No OneTemporary

File Metadata

Created
Mon, May 6, 1:05 PM
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/3rdparty/ext_qt/CMakeLists.txt b/3rdparty/ext_qt/CMakeLists.txt
index 2c1aef418e..194bcc7297 100644
--- a/3rdparty/ext_qt/CMakeLists.txt
+++ b/3rdparty/ext_qt/CMakeLists.txt
@@ -1,301 +1,302 @@
SET(EXTPREFIX_qt "${EXTPREFIX}")
if (WIN32)
list(APPEND _QT_conf -skip qt3d -skip qtactiveqt -skip qtcanvas3d
-skip qtconnectivity -skip qtdoc -skip qtgraphicaleffects
-skip qtlocation -skip qtsensors -skip qtserialport -skip qtwayland
-skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview
- -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools
- -no-compile-examples -no-dbus -no-iconv -no-qml-debug
+ -skip qtxmlpatterns -nomake examples -nomake tools
+ -no-compile-examples -no-dbus -no-iconv -no-qml-debug
-no-libproxy -no-system-proxies -no-icu -no-mtdev
-skip qtcharts -skip qtdatavis3d -skip qtgamepad -skip qtnetworkauth
-skip qtpurchasing -skip qtremoteobjects -skip qtscxml -skip qtserialbus
-skip qtspeech -skip qtvirtualkeyboard
#
-qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -openssl-linked
-I ${EXTPREFIX_qt}/include
-L ${EXTPREFIX_qt}/lib
#
-opensource -confirm-license
#
-release -platform win32-g++ -prefix ${EXTPREFIX_qt}
QMAKE_LFLAGS_APP+=${SECURITY_EXE_LINKER_FLAGS}
QMAKE_LFLAGS_SHLIB+=${SECURITY_SHARED_LINKER_FLAGS}
QMAKE_LFLAGS_SONAME+=${SECURITY_SHARED_LINKER_FLAGS}
)
if (QT_ENABLE_DEBUG_INFO)
# Set the option to build Qt with debugging info enabled
list(APPEND _QT_conf -force-debug-info)
endif(QT_ENABLE_DEBUG_INFO)
if (QT_ENABLE_DYNAMIC_OPENGL)
list(APPEND _QT_conf -opengl dynamic -angle)
else (QT_ENABLE_DYNAMIC_OPENGL)
list(APPEND _QT_conf -opengl desktop -no-angle)
endif (QT_ENABLE_DYNAMIC_OPENGL)
# MIME-type optimization patches
set(ext_qt_PATCH_COMMAND
${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0001-Use-fast-path-for-unsupported-mime-types.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0002-Hack-always-return-we-support-DIBV5.patch
)
# Tablet support patches
if (NOT USE_QT_TABLET_WINDOWS)
set(ext_qt_PATCH_COMMAND ${ext_qt_PATCH_COMMAND}
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0001-disable-wintab.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/disable-winink.patch
)
else()
set(ext_qt_PATCH_COMMAND ${ext_qt_PATCH_COMMAND}
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0020-Synthesize-Enter-LeaveEvent-for-accepted-QTabletEven.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0023-Implement-a-switch-for-tablet-API-on-Windows.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0024-Fetch-stylus-button-remapping-from-WinTab-driver.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0025-Disable-tablet-relative-mode-in-Qt.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0026-Fetch-mapped-screen-size-from-the-Wintab-driver.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0027-Switch-stylus-pointer-type-when-the-tablet-is-in-the.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0028-Fix-updating-tablet-pressure-resolution-on-every-pro.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0051-Add-workaround-for-handling-table-press-correctly-in.patch
)
endif()
# HDR patches
set(ext_qt_PATCH_COMMAND ${ext_qt_PATCH_COMMAND}
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0003-Implement-openGL-surface-color-space-selection-in-An.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0004-Implement-color-space-selection-for-QSurfaceFormat.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0005-Implement-color-conversion-for-the-backing-store-tex.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0006-Return-QScreen-s-HMONITOR-handle-via-QPlatformNative.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0007-Implement-a-manual-test-for-checking-is-HDR-features.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0008-Fix-notification-of-QDockWidget-when-it-gets-undocke.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0009-Fix-Rec2020-display-format.patch
)
# Other patches
set(ext_qt_PATCH_COMMAND ${ext_qt_PATCH_COMMAND}
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0060-Windows-Add-a-default-setting-for-hasBorderInFullScr.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0061-Hack-to-hide-1px-border-with-OpenGL-fullscreen-hack.patch
COMMAND ${PATCH_COMMAND} -p1 -d qttools -i ${CMAKE_CURRENT_SOURCE_DIR}/windeployqt-force-allow-debug-info.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0080-Sync-buffers-of-the-destination-file-after-QFile-cop.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0081-Fix-no-warning-for-overwriting-files-in-non-native-d.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0082-Make-jp-e-g-default-extensions-context-aware.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0100-Fix-qt5_make_output_file-macro-for-paths-containing-.patch
# COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0031-Compute-logical-DPI-on-a-per-screen-basis.patch
# COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0032-Update-Dpi-and-scale-factor-computation.patch
# COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0033-Move-QT_FONT_DPI-to-cross-platform-code.patch
# COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0034-Update-QT_SCREEN_SCALE_FACTORS.patch
# COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0035-Deprecate-QT_AUTO_SCREEN_SCALE_FACTOR.patch
# COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0036-Add-high-DPI-scale-factor-rounding-policy-C-API.patch
)
ExternalProject_Add(
ext_qt
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
URL https://download.qt.io/official_releases/qt/5.12/5.12.7/single/qt-everywhere-src-5.12.7.zip
URL_MD5 753e0147d5e32d61c30d6a1b498bed3e
PATCH_COMMAND ${ext_qt_PATCH_COMMAND}
INSTALL_DIR ${EXTPREFIX_qt}
CONFIGURE_COMMAND <SOURCE_DIR>/configure.bat ${_QT_conf}
BUILD_COMMAND mingw32-make -j${SUBMAKE_JOBS}
INSTALL_COMMAND mingw32-make -j${SUBMAKE_JOBS} install
UPDATE_COMMAND ""
# Use a short name to reduce the chance of exceeding path length limit
SOURCE_DIR s
BINARY_DIR b
DEPENDS ext_patch ext_openssl
)
elseif (ANDROID)
ExternalProject_Add(
ext_qt
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
URL https://download.qt.io/archive/qt/5.12/5.12.7/single/qt-everywhere-src-5.12.7.tar.xz
URL_MD5 ce2c5661c028b9de6183245982d7c120
PATCH_COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0091-Add-support-for-pen-tilt-rotation-for-Android.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0092-Bugfix-fix-the-offset-bug-when-using-Stylus-with-And.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/fix-locale-android.patch
CONFIGURE_COMMAND <SOURCE_DIR>/configure -prefix ${EXTPREFIX_qt} -opensource -confirm-license -verbose -nomake examples -nomake tests -nomake tools -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtgraphicaleffects -skip qtlocation -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtserialport -skip qtdatavis3d -skip qtvirtualkeyboard -skip qtspeech -skip qtsensors -skip qtgamepad -skip qtscxml -skip qtremoteobjects -skip qtxmlpatterns -skip qtnetworkauth -skip qtcharts -skip qtdatavis3d -skip qtgamepad -skip qtpurchasing -skip qtscxml -skip qtserialbus -skip qtspeech -skip qtvirtualkeyboard -skip qtmultimedia -android-sdk ${ANDROID_SDK_ROOT} -android-ndk ${CMAKE_ANDROID_NDK} -android-arch ${ANDROID_ABI} -xplatform android-clang -android-ndk-platform android-21 -make libs
INSTALL_DIR ${EXTPREFIX_qt}
BUILD_COMMAND $(MAKE)
INSTALL_COMMAND $(MAKE) install
UPDATE_COMMAND ""
BUILD_IN_SOURCE 1
)
elseif (NOT APPLE)
ExternalProject_Add(
ext_qt
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
URL https://download.qt.io/official_releases/qt/5.12/5.12.7/single/qt-everywhere-src-5.12.7.tar.xz
URL_MD5 ce2c5661c028b9de6183245982d7c120
PATCH_COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0012-Synthesize-Enter-LeaveEvent-for-accepted-QTabletEven.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0013-Poison-Qt-s-headers-with-a-mark-about-presence-of-En.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0081-Fix-no-warning-for-overwriting-files-in-non-native-d.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0082-Make-jp-e-g-default-extensions-context-aware.patch
CMAKE_ARGS -DOPENSSL_LIBS='-L${EXTPREFIX_qt}/lib -lssl -lcrypto'
CONFIGURE_COMMAND <SOURCE_DIR>/configure -prefix ${EXTPREFIX_qt} -opensource -confirm-license -openssl-linked -verbose -nomake examples -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtgraphicaleffects -skip qtlocation -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtandroidextras -skip qtserialport -skip qtdatavis3d -skip qtvirtualkeyboard -skip qtspeech -skip qtsensors -skip qtgamepad -skip qtscxml -skip qtremoteobjects -skip qtxmlpatterns -skip qtnetworkauth -skip qtcharts -skip qtdatavis3d -skip qtgamepad -skip qtpurchasing -skip qtscxml -skip qtserialbus -skip qtspeech -skip qtvirtualkeyboard -skip qtmultimedia
INSTALL_DIR ${EXTPREFIX_qt}
BUILD_COMMAND $(MAKE)
INSTALL_COMMAND $(MAKE) install
UPDATE_COMMAND ""
BUILD_IN_SOURCE 1
)
else( APPLE )
# XCODE_VERSION is set by CMake when using the Xcode generator, otherwise we need
# to detect it manually here.
if (NOT XCODE_VERSION)
execute_process(
COMMAND xcodebuild -version
OUTPUT_VARIABLE xcodebuild_version
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_FILE /dev/null
)
string(REGEX MATCH "Xcode ([0-9]+([.][0-9]+)*)" version_match ${xcodebuild_version})
if (version_match)
message(STATUS "${EXTPREFIX_qt}:Identified Xcode Version: ${CMAKE_MATCH_1}")
set(XCODE_VERSION ${CMAKE_MATCH_1})
else()
# If detecting Xcode version failed, set a crazy high version so we default
# to the newest.
set(XCODE_VERSION 99)
message(WARNING "${EXTPREFIX_qt}:Failed to detect the version of an installed copy of Xcode, falling back to highest supported version. Set XCODE_VERSION to override.")
endif(version_match)
endif(NOT XCODE_VERSION)
# -------------------------------------------------------------------------------
# Verify the Xcode installation on Mac OS like Qt5.7 does/will
# If not stop now, the system isn't configured correctly for Qt.
# No reason to even proceed.
# -------------------------------------------------------------------------------
set(XCSELECT_OUTPUT)
find_program(XCSELECT_PROGRAM "xcode-select")
if(XCSELECT_PROGRAM)
message(STATUS "${EXTPREFIX_qt}:Found XCSELECT_PROGRAM as ${XCSELECT_PROGRAM}")
set(XCSELECT_COMMAND ${XCSELECT_PROGRAM}
"--print-path")
execute_process(
COMMAND ${XCSELECT_COMMAND}
RESULT_VARIABLE XCSELECT_COMMAND_RESULT
OUTPUT_VARIABLE XCSELECT_COMMAND_OUTPUT
ERROR_FILE /dev/null
)
if(NOT XCSELECT_COMMAND_RESULT)
# returned 0, we're ok.
string(REGEX REPLACE
"[ \t]*[\r\n]+[ \t]*" ";"
XCSELECT_COMMAND_OUTPUT ${XCSELECT_COMMAND_OUTPUT})
else()
string(REPLACE ";" " " XCSELECT_COMMAND_STR "${XCSELECT_COMMAND}")
# message(STATUS "${XCSELECT_COMMAND_STR}")
message(FATAL_ERROR "${EXTPREFIX_qt}:${XCSELECT_PROGRAM} test failed with status ${XCSELECT_COMMAND_RESULT}")
endif()
else()
message(FATAL_ERROR "${EXTPREFIX_qt}:${XCSELECT_PROGRAM} not found. No Xcode is selected. Use xcode-select -switch to choose an Xcode version")
endif()
# Belts and suspenders
# Beyond all the Xcode and Qt version checking, the proof of the pudding
# lies in the success/failure of this command: xcrun --find xcrun.
# On failure a patch is necessary, otherwise we're ok
# So hard check xcrun now...
set(XCRUN_OUTPUT)
find_program(XCRUN_PROGRAM "xcrun")
if(XCRUN_PROGRAM)
message(STATUS "${EXTPREFIX_qt}:Found XCRUN_PROGRAM as ${XCRUN_PROGRAM}")
set(XCRUN_COMMAND ${XCRUN_PROGRAM}
"--find xcrun")
execute_process(
COMMAND ${XCRUN_COMMAND}
RESULT_VARIABLE XCRUN_COMMAND_RESULT
OUTPUT_VARIABLE XCRUN_COMMAND_OUTPUT
ERROR_FILE /dev/null
)
if(NOT XCRUN_COMMAND_RESULT)
# returned 0, we're ok.
string(REGEX REPLACE
"[ \t]*[\r\n]+[ \t]*" ";"
XCRUN_COMMAND_OUTPUT ${XCRUN_COMMAND_OUTPUT})
else()
string(REPLACE ";" " " XCRUN_COMMAND_STR "${XCRUN_COMMAND}")
# message(STATUS "${XCRUN_COMMAND_STR}")
message(STATUS "${EXTPREFIX_qt}:xcrun test failed with status ${XCRUN_COMMAND_RESULT}")
endif()
else()
message(STATUS "${EXTPREFIX_qt}:xcrun not found -- ${XCRUN_PROGRAM}")
endif()
#
# Now configure ext_qt accordingly
#
if ((XCRUN_COMMAND_RESULT) AND (NOT (XCODE_VERSION VERSION_LESS 8.0.0)))
# Fix Xcode xcrun related issue.
# NOTE: This should be fixed by Qt 5.7.1 see here: http://code.qt.io/cgit/qt/qtbase.git/commit/?h=dev&id=77a71c32c9d19b87f79b208929e71282e8d8b5d9
# NOTE: but no one's holding their breath.
set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND}} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0012-Synthesize-Enter-LeaveEvent-for-accepted-QTabletEven.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0013-Poison-Qt-s-headers-with-a-mark-about-presence-of-En.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0081-Fix-no-warning-for-overwriting-files-in-non-native-d.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0082-Make-jp-e-g-default-extensions-context-aware.patch
#COMMAND ${PATCH_COMMAND} -p1 -b -d <SOURCE_DIR>/qtbase/mkspecs/features/mac -i ${CMAKE_CURRENT_SOURCE_DIR}/mac-default.patch
)
message(STATUS "${EXTPREFIX_qt}:Additional patches injected.")
else()
# No extra patches will be applied
# NOTE: defaults for some untested scenarios like xcrun fails and xcode_version < 8.
# NOTE: that is uncharted territory and (hopefully) a very unlikely scenario...
set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0012-Synthesize-Enter-LeaveEvent-for-accepted-QTabletEven.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0081-Fix-no-warning-for-overwriting-files-in-non-native-d.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/0082-Make-jp-e-g-default-extensions-context-aware.patch
)
endif()
# Qt is big - try and parallelize if at all possible
include(ProcessorCount)
ProcessorCount(NUM_CORES)
if(NOT NUM_CORES EQUAL 0)
if (NUM_CORES GREATER 2)
# be nice...
MATH( EXPR NUM_CORES "${NUM_CORES} - 2" )
endif()
set(PARALLEL_MAKE "make;-j${NUM_CORES}")
message(STATUS "${EXTPREFIX_qt}:Parallelized make: ${PARALLEL_MAKE}")
else()
set(PARALLEL_MAKE "make")
endif()
ExternalProject_Add(
ext_qt
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
LOG_DOWNLOAD ON
LOG_UPDATE ON
LOG_CONFIGURE ON
LOG_BUILD ON
LOG_TEST ON
LOG_INSTALL ON
BUILD_IN_SOURCE ON
URL https://download.qt.io/official_releases/qt/5.12/5.12.7/single/qt-everywhere-src-5.12.7.tar.xz
URL_MD5 ce2c5661c028b9de6183245982d7c120
CMAKE_ARGS -DOPENSSL_LIBS='-L${EXTPREFIX_qt}/lib -lssl -lcrypto'
INSTALL_DIR ${EXTPREFIX_qt}
CONFIGURE_COMMAND <SOURCE_DIR>/configure
-skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtgraphicaleffects -skip qtlocation -skip qtsensors -skip qtserialport -skip qtwayland
- -skip qtwebchannel -skip qtwebsockets -skip qtwebview -skip qtwebengine -skip qtxmlpatterns -no-sql-sqlite -skip qtcharts -skip qtdatavis3d -skip qtgamepad -skip qtnetworkauth
+ -skip qtwebchannel -skip qtwebsockets -skip qtwebview -skip qtwebengine -skip qtxmlpatterns
+ -skip qtcharts -skip qtdatavis3d -skip qtgamepad -skip qtnetworkauth
-skip qtpurchasing -skip qtremoteobjects -skip qtscxml -skip qtserialbus -skip qtspeech -skip qtvirtualkeyboard -nomake examples -nomake tools -no-compile-examples
-no-dbus -no-iconv -no-qml-debug -no-libproxy -no-system-proxies -no-icu -no-mtdev -system-zlib -qt-pcre
-opensource -confirm-license -openssl-linked -prefix ${EXTPREFIX_qt}
BUILD_COMMAND ${PARALLEL_MAKE}
INSTALL_COMMAND make install
UPDATE_COMMAND ""
BUILD_IN_SOURCE 1
)
endif()
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 040d4c1a78..276c9394ae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,925 +1,926 @@
project(krita)
message(STATUS "Using CMake version: ${CMAKE_VERSION}")
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
set(MIN_QT_VERSION 5.9.0)
set(MIN_FRAMEWORKS_VERSION 5.44.0)
set( CMAKE_CXX_STANDARD 11 )
set( CMAKE_CXX_STANDARD_REQUIRED ON )
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.12 -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)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wno-suggest-override> -Wextra -Wno-class-memaccess)
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.3.0-prealpha")
# 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 3)
# 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
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 ()
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(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.8 EXACT)
find_package(PythonLibs 3.8 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.8)
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
Config
WidgetsAddons
Completion
CoreAddons
GuiAddons
I18n
ItemModels
ItemViews
WindowSystem
Archive
)
find_package(Qt5 ${MIN_QT_VERSION}
REQUIRED COMPONENTS
Core
Gui
Widgets
Xml
Network
PrintSupport
Svg
Test
Concurrent
+ Sql
)
if (ANDROID)
find_package(Qt5 ${MIN_QT_VERSION}
REQUIRED COMPONENTS
AndroidExtras
)
endif()
if (WIN32)
set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS})
set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES})
CHECK_CXX_SOURCE_COMPILES("
#include <QCoreApplication>
int main(int argc, char *argv[]) {
QCoreApplication::setAttribute(Qt::AA_MSWindowsUseWinTabAPI);
}
"
QT_HAS_WINTAB_SWITCH
)
unset(CMAKE_REQUIRED_INCLUDES)
unset(CMAKE_REQUIRED_LIBRARIES)
option(USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt." ON)
add_feature_info("Use Qt's Windows Tablet Support" USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt.")
configure_file(config_use_qt_tablet_windows.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_use_qt_tablet_windows.h)
endif ()
set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS})
set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Gui_LIBRARIES})
CHECK_CXX_SOURCE_COMPILES("
#include <QSurfaceFormat>
int main(int argc, char *argv[]) {
QSurfaceFormat fmt;
fmt.setColorSpace(QSurfaceFormat::scRGBColorSpace);
fmt.setColorSpace(QSurfaceFormat::bt2020PQColorSpace);
}
"
HAVE_HDR
)
configure_file(config-hdr.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hdr.h)
CHECK_CXX_SOURCE_COMPILES("
#include <QGuiApplication>
int main(int argc, char *argv[]) {
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor);
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
}
"
HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY
)
configure_file(config-high-dpi-scale-factor-rounding-policy.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-high-dpi-scale-factor-rounding-policy.h)
if (WIN32)
CHECK_CXX_SOURCE_COMPILES("
#include <QtPlatformHeaders/QWindowsWindowFunctions>
int main(int argc, char *argv[]) {
QWindowsWindowFunctions::setHasBorderInFullScreenDefault(true);
}
"
HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT
)
configure_file(config-set-has-border-in-full-screen-default.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-set-has-border-in-full-screen-default.h)
endif (WIN32)
unset(CMAKE_REQUIRED_INCLUDES)
unset(CMAKE_REQUIRED_LIBRARIES)
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 "https://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 "https://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 "https://www.qt.io/"
TYPE OPTIONAL
PURPOSE "Optionally used for the touch gui for Krita")
endif()
if (NOT WIN32 AND NOT APPLE AND NOT ANDROID)
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 "https://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 "https://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)
else()
set(HAVE_DBUS FALSE)
set(HAVE_X11 FALSE)
endif()
add_definitions(
-DQT_USE_QSTRINGBUILDER
-DQT_STRICT_ITERATORS
-DQT_NO_SIGNALS_SLOTS_KEYWORDS
-DQT_NO_URL_CAST_FROM_STRING
-DQT_USE_FAST_CONCATENATION
-DQT_USE_FAST_OPERATOR_PLUS
)
#if (${Qt5_VERSION} VERSION_GREATER "5.14.0" )
# add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50F00)
#elseif (${Qt5_VERSION} VERSION_GREATER "5.13.0" )
# add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50E00)
#elseif (${Qt5_VERSION} VERSION_GREATER "5.12.0" )
# add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50D00)
#elseif (${Qt5_VERSION} VERSION_GREATER "5.11.0" )
# add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50C00)
#if(${Qt5_VERSION} VERSION_GREATER "5.10.0" )
# add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50B00)
#if(${Qt5_VERSION} VERSION_GREATER "5.9.0" )
# add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50A00)
#else()
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50900)
#endif()
add_definitions(-DQT_DEPRECATED_WARNINGS)
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)
if(MINGW)
# Hack CMake's variables to tell AR to create thin archives to reduce unnecessary writes.
# Source of definition: https://github.com/Kitware/CMake/blob/v3.14.1/Modules/Platform/Windows-GNU.cmake#L128
# Thin archives: https://sourceware.org/binutils/docs/binutils/ar.html#index-thin-archives
macro(mingw_use_thin_archive lang)
foreach(rule CREATE_SHARED_MODULE CREATE_SHARED_LIBRARY LINK_EXECUTABLE)
string(REGEX REPLACE "(<CMAKE_AR> [^ T]+) " "\\1T " CMAKE_${lang}_${rule} "${CMAKE_${lang}_${rule}}")
endforeach()
endmacro()
mingw_use_thin_archive(CXX)
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
if (ANDROID)
# use default ABI
if (NOT ANDROID_ABI)
set (ANDROID_ABI armeabi-v7a)
endif()
set (ANDROID_SDK_ROOT $ENV{ANDROID_SDK_ROOT})
set (KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR})
# set (DATA_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/assets)
else()
set (KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins)
endif()
###########################
############################
## Required dependencies ##
############################
###########################
# FIXME: Still hardcoded
if (ANDROID)
set (Boost_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/i/${ANDROID_ABI}/include/boost-1_69)
set (Boost_LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/i/${ANDROID_ABI}/lib)
set (KF5_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/kf5/kde/install/lib)
endif()
find_package(PNG REQUIRED)
list (APPEND ANDROID_EXTRA_LIBS ${PNG_LIBRARY})
if (APPLE)
# this is not added correctly on OSX -- see https://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 "https://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 )
if (GSL_FOUND)
list (APPEND ANDROID_EXTRA_LIBS ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES})
endif()
###########################
############################
## Optional dependencies ##
############################
###########################
find_package(ZLIB)
set_package_properties(ZLIB PROPERTIES
DESCRIPTION "Compression library"
URL "https://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 "https://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_DIRS})
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.libtiff.org"
TYPE OPTIONAL
PURPOSE "Required by the Krita TIFF filter")
if (TIFF_FOUND)
list (APPEND ANDROID_EXTRA_LIBS ${TIFF_LIBRARY})
endif()
find_package(JPEG)
set_package_properties(JPEG PROPERTIES
DESCRIPTION "Free library for JPEG image compression. Note: libjpeg8 is NOT supported."
URL "https://www.libjpeg-turbo.org"
TYPE OPTIONAL
PURPOSE "Required by the Krita JPEG filter")
if (JPEG_FOUND)
list (APPEND ANDROID_EXTRA_LIBS ${JPEG_LIBRARY})
endif()
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")
if (GIF_FOUND)
list (APPEND ANDROID_EXTRA_LIBS ${GIF_LIBRARY})
endif()
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")
find_package(OpenJPEG "2.3.0")
set_package_properties(OpenJPEG PROPERTIES
DESCRIPTION "Library for loading and saving jp2000 files."
URL "https://www.openjpeg.org/"
TYPE OPTIONAL
PURPOSE "Required by the Krita JP2000 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 "https://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)
if (FFTW3_FOUND)
list (APPEND ANDROID_EXTRA_LIBS ${FFTW3_LIBRARY})
endif()
find_package(OCIO)
set_package_properties(OCIO PROPERTIES
DESCRIPTION "The OpenColorIO Library"
URL "https://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 "https://www.python.org"
TYPE OPTIONAL
PURPOSE "Required by the Krita PyQt plugin")
macro_bool_to_01(PYTHONLIBS_FOUND HAVE_PYTHONLIBS)
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(LibExiv2 0.16 REQUIRED)
if (ANDROID)
list (APPEND ANDROID_EXTRA_LIBS ${LibExiv2_LIBRARIES})
# because libexiv2 depends on libexpat and it is installed in the same folder
get_filename_component (_base_dir ${LibExiv2_LIBRARIES} DIRECTORY)
list (APPEND ANDROID_EXTRA_LIBS ${_base_dir}/libexpat.so)
endif()
##
## 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()
list (APPEND ANDROID_EXTRA_LIBS ${LCMS2_LIBRARIES})
##
## 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 "-ffp-contract=fast")
if(NOT WIN32)
set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC")
endif()
elseif (NOT MSVC)
set(ADDITIONAL_VC_FLAGS "-fabi-version=0 -ffp-contract=fast")
if(NOT WIN32)
set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC")
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 "https://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"
)
# FIXME: better way to do this?
list (APPEND ANDROID_EXTRA_LIBS ${QUAZIP_LIBRARIES}
${EXPAT_LIBRARY}
${KF5_LIBRARIES}/libKF5Completion.so
${KF5_LIBRARIES}/libKF5WindowSystem.so
${KF5_LIBRARIES}/libKF5WidgetsAddons.so
${KF5_LIBRARIES}/libKF5ItemViews.so
${KF5_LIBRARIES}/libKF5ItemModels.so
${KF5_LIBRARIES}/libKF5GuiAddons.so
${KF5_LIBRARIES}/libKF5I18n.so
${KF5_LIBRARIES}/libKF5CoreAddons.so
${KF5_LIBRARIES}/libKF5ConfigGui.so
${KF5_LIBRARIES}/libKF5ConfigCore.so
${KF5_LIBRARIES}/libKF5Archive.so)
##
## 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)
if (BUILD_TESTING)
add_subdirectory(benchmarks)
endif()
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()
if(DEFINED QTANDROID_EXPORTED_TARGET AND NOT TARGET "create-apk")
set (_CMAKE_ANDROID_DIR "${ECM_DIR}/../toolchain")
list(LENGTH QTANDROID_EXPORTED_TARGET targetsCount)
include(${_CMAKE_ANDROID_DIR}/ECMAndroidDeployQt.cmake)
math(EXPR last "${targetsCount}-1")
foreach(idx RANGE 0 ${last})
list(GET QTANDROID_EXPORTED_TARGET ${idx} exportedTarget)
list(GET ANDROID_APK_DIR ${idx} APK_DIR)
if(APK_DIR AND NOT EXISTS "${ANDROID_APK_DIR}/AndroidManifest.xml" AND IS_ABSOLUTE ANDROID_APK_DIR)
message(FATAL_ERROR "Cannot find ${APK_DIR}/AndroidManifest.xml according to ANDROID_APK_DIR. ${ANDROID_APK_DIR} ${exportedTarget}")
elseif(NOT APK_DIR)
get_filename_component(_qt5Core_install_prefix "${Qt5Core_DIR}/../../../" ABSOLUTE)
set(APK_DIR "${_qt5Core_install_prefix}/src/android/templates/")
endif()
ecm_androiddeployqt("${exportedTarget}" "${ECM_ADDITIONAL_FIND_ROOT_PATH}")
set_target_properties(create-apk-${exportedTarget} PROPERTIES ANDROID_APK_DIR "${APK_DIR}")
endforeach()
elseif(ANDROID)
message(STATUS "You can export a target by specifying -DQTANDROID_EXPORTED_TARGET=<targetname> and -DANDROID_APK_DIR=<paths>")
endif()
diff --git a/KoConfig.h.cmake b/KoConfig.h.cmake
index 41eca731bd..b56cd39356 100644
--- a/KoConfig.h.cmake
+++ b/KoConfig.h.cmake
@@ -1,62 +1,65 @@
// Check windows
#ifdef Q_OS_WIN
#ifdef _WIN64
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
// Check GCC
#if __GNUC__
#if defined (__x86_64__) || defined (__ppc64__)
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
#ifdef __APPLE__
# ifdef __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# else
# undef WORDS_BIGENDIAN
# endif
#else
/* Define to 1 if your processor stores words with the most significant byte
first (like Motorola and SPARC, unlike Intel and VAX). */
#cmakedefine WORDS_BIGENDIAN ${CMAKE_WORDS_BIGENDIAN}
#endif
/* Defines if the Dr. Mingw crash handler should be used */
#cmakedefine USE_DRMINGW 1
/* Number of bits in a file offset, on hosts where this is settable. */
#define _FILE_OFFSET_BITS 64
/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
/* #undef _LARGEFILE_SOURCE */
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */
/* Defines if your system has the OpenEXR library */
#cmakedefine HAVE_OPENEXR 1
/* Defines if we use lcms2 */
#cmakedefine HAVE_LCMS2 1
/* Defines if we use lcms2.4 */
#cmakedefine HAVE_LCMS24 1
/* Defines if DBUS is present */
#cmakedefine HAVE_DBUS 1
/* Defines if KCrash is present */
#cmakedefine HAVE_KCRASH 1
-/* This variable contains the path to the current build directory */
+/* This variable contains the path to the root of the build directory */
#define KRITA_BUILD_DIR "${CMAKE_BINARY_DIR}"
+/* This variable contains the path to the root of the source directory */
+#define KRITA_SOURCE_DIR "${CMAKE_SOURCE_DIR}"
+
/* This variable contains the path to the data install dir */
#define KRITA_EXTRA_RESOURCE_DIRS "${CMAKE_INSTALL_PREFIX}/${DATA_INSTALL_DIR}:${CMAKE_SOURCE_DIR}/krita/data"
diff --git a/benchmarks/kis_bcontrast_benchmark.cpp b/benchmarks/kis_bcontrast_benchmark.cpp
index 889855f4de..ee68e18547 100644
--- a/benchmarks/kis_bcontrast_benchmark.cpp
+++ b/benchmarks/kis_bcontrast_benchmark.cpp
@@ -1,100 +1,101 @@
/*
* 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.
*/
#include <QTest>
#include "kis_bcontrast_benchmark.h"
#include "kis_benchmark_values.h"
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColor.h>
#include <kis_image.h>
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter.h"
#include "kis_processing_information.h"
#include "kis_selection.h"
#include <kis_iterator_ng.h>
#include "krita_utils.h"
+#include <KisGlobalResourcesInterface.h>
void KisBContrastBenchmark::initTestCase()
{
m_colorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_device = new KisPaintDevice(m_colorSpace);
m_color = KoColor(m_colorSpace);
srand(31524744);
int r,g,b;
KisSequentialIterator it(m_device, QRect(0, 0, GMP_IMAGE_WIDTH, GMP_IMAGE_HEIGHT));
while (it.nextPixel()) {
r = rand() % 255;
g = rand() % 255;
b = rand() % 255;
m_color.fromQColor(QColor(r,g,b));
memcpy(it.rawData(), m_color.data(), m_colorSpace->pixelSize());
}
}
void KisBContrastBenchmark::cleanupTestCase()
{
}
void KisBContrastBenchmark::benchmarkFilter()
{
KisFilterSP filter = KisFilterRegistry::instance()->value("brightnesscontrast");
- KisFilterConfigurationSP kfc = filter->defaultConfiguration();
+ KisFilterConfigurationSP kfc = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
// Get the predefined configuration from a file
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + filter->id() + ".cfg");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out.setCodec("UTF-8");
out << kfc->toXML();
} else {
QString s;
QTextStream in(&file);
in.setCodec("UTF-8");
s = in.readAll();
kfc->fromXML(s);
}
QSize size = KritaUtils::optimalPatchSize();
QVector<QRect> rects = KritaUtils::splitRectIntoPatches(QRect(0, 0, GMP_IMAGE_WIDTH,GMP_IMAGE_HEIGHT), size);
QBENCHMARK{
Q_FOREACH (const QRect &rc, rects) {
filter->process(m_device, rc, kfc);
}
}
}
QTEST_MAIN(KisBContrastBenchmark)
diff --git a/benchmarks/kis_blur_benchmark.cpp b/benchmarks/kis_blur_benchmark.cpp
index 70fe441b52..71ad1b78cb 100644
--- a/benchmarks/kis_blur_benchmark.cpp
+++ b/benchmarks/kis_blur_benchmark.cpp
@@ -1,92 +1,93 @@
/*
* 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.
*/
#include <QTest>
#include "kis_blur_benchmark.h"
#include "kis_benchmark_values.h"
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColor.h>
#include <kis_image.h>
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter.h"
#include "kis_processing_information.h"
#include "kis_selection.h"
#include <kis_iterator_ng.h>
+#include <KisGlobalResourcesInterface.h>
void KisBlurBenchmark::initTestCase()
{
m_colorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_device = new KisPaintDevice(m_colorSpace);
m_color = KoColor(m_colorSpace);
QColor qcolor(Qt::red);
srand(31524744);
int r,g,b;
KisSequentialIterator it(m_device, QRect(0,0,GMP_IMAGE_WIDTH, GMP_IMAGE_HEIGHT));
while (it.nextPixel()) {
r = rand() % 255;
g = rand() % 255;
b = rand() % 255;
m_color.fromQColor(QColor(r,g,b));
memcpy(it.rawData(), m_color.data(), m_colorSpace->pixelSize());
}
}
void KisBlurBenchmark::cleanupTestCase()
{
}
void KisBlurBenchmark::benchmarkFilter()
{
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
- KisFilterConfigurationSP kfc = filter->defaultConfiguration();
+ KisFilterConfigurationSP kfc = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
// Get the predefined configuration from a file
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + filter->id() + ".cfg");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out.setCodec("UTF-8");
out << kfc->toXML();
} else {
QString s;
QTextStream in(&file);
in.setCodec("UTF-8");
s = in.readAll();
kfc->fromXML(s);
}
QBENCHMARK{
filter->process(m_device, QRect(0, 0, GMP_IMAGE_WIDTH,GMP_IMAGE_HEIGHT), kfc);
}
}
QTEST_MAIN(KisBlurBenchmark)
diff --git a/benchmarks/kis_composition_benchmark.cpp b/benchmarks/kis_composition_benchmark.cpp
index 4065a1adfb..2a38b6c88a 100644
--- a/benchmarks/kis_composition_benchmark.cpp
+++ b/benchmarks/kis_composition_benchmark.cpp
@@ -1,949 +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 <QElapsedTimer>
#include <KoColorSpace.h>
#include <KoCompositeOp.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceTraits.h>
#include <KoCompositeOpAlphaDarken.h>
#include <KoCompositeOpOver.h>
#include <KoOptimizedCompositeOpFactory.h>
#include <KoAlphaDarkenParamsWrapper.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
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++) {
qDebug() << "Src: "
<< srcPixels[i*4] << "\t"
<< srcPixels[i*4+1] << "\t"
<< srcPixels[i*4+2] << "\t"
<< srcPixels[i*4+3] << "\t"
<< "Msk:" << mask[i];
qDebug() << "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)) {
qDebug() << "Wrong result:" << i;
qDebug() << "Act: " << dst1[0] << dst1[1] << dst1[2] << dst1[3];
qDebug() << "Exp: " << dst2[0] << dst2[1] << dst2[2] << dst2[3];
qDebug() << "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;
qDebug() << "SrcA:" << s1[0] << s1[1] << s1[2] << s1[3];
qDebug() << "SrcE:" << s2[0] << s2[1] << s2[2] << s2[3];
qDebug() << "MskA:" << tiles[0].mask[i];
qDebug() << "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;
+ QElapsedTimer 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);
}
qDebug() << testName << "RESULT:" << timer.elapsed() << "msec";
freeTiles(tiles, srcAlignmentShift, dstAlignmentShift);
}
void benchmarkCompositeOp(const KoCompositeOp *op, const QString &postfix)
{
qDebug() << "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::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, paramsWrapper);
for (int j = 0; j < vecSize; j++) {
//if (8 * i + j == 7080) {
// qDebug() << "src: " << src2[0] << src2[1] << src2[2] << src2[3];
// qDebug() << "dst: " << dst2[0] << dst2[1] << dst2[2] << dst2[3];
// qDebug() << "msk:" << msk2[0];
//}
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) {
qDebug() << "Wrong rounding in pixel:" << 8 * i + j;
qDebug() << "Vector version: " << dst1[0] << dst1[1] << dst1[2] << dst1[3];
qDebug() << "Scalar version: " << dst2[0] << dst2[1] << dst2[2] << dst2[3];
qDebug() << "src:" << src1[0] << src1[1] << src1[2] << src1[3];
qDebug() << "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, KoAlphaDarkenParamsWrapperCreamy> >(0.5,0.3);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_05()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32, KoAlphaDarkenParamsWrapperCreamy> >(0.5,0.5);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_07()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32, KoAlphaDarkenParamsWrapperCreamy> >(0.5,0.7);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_10()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32, KoAlphaDarkenParamsWrapperCreamy> >(0.5,1.0);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_10_08()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32, KoAlphaDarkenParamsWrapperCreamy> >(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::createAlphaDarkenOpCreamy32(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::createAlphaDarkenOpCreamy128(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::createAlphaDarkenOpCreamy32(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, KoAlphaDarkenParamsWrapperCreamy>(cs);
benchmarkCompositeOp(op, "Legacy");
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenOptimized()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createAlphaDarkenOpCreamy32(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, KoAlphaDarkenParamsWrapperCreamy>(cs);
benchmarkCompositeOp(op, "Legacy");
delete op;
}
void KisCompositionBenchmark::testRgbF32CompositeAlphaDarkenOptimized()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", "");
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createAlphaDarkenOpCreamy128(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/benchmarks/kis_filter_selections_benchmark.cpp b/benchmarks/kis_filter_selections_benchmark.cpp
index 0ddf499615..6e6c16d2d5 100644
--- a/benchmarks/kis_filter_selections_benchmark.cpp
+++ b/benchmarks/kis_filter_selections_benchmark.cpp
@@ -1,293 +1,293 @@
/*
* 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_filter_selections_benchmark.h"
#include "kis_painter.h"
#include <QTest>
#include "testutil.h"
#include "kis_transaction.h"
#include <KoCompositeOpRegistry.h>
#include "kis_datamanager.h"
-
+#include <KisGlobalResourcesInterface.h>
#define NUM_CYCLES 50
#define WARMUP_CYCLES 2
#define SHOW_WARMUPS 0
/**
* Our filters don't know anything about applyAlphaU8Mask
* That's why they treat semy-selected pixels badly
*
* If you have a hack in KisFilter - processSpecial(..) method
* that takes into account this alpha mask then activate
* the following define
*/
#define USE_GOOD_SELECTIONS 0
#define USE_UTIME 0
#if(USE_UTIME==1)
#include <sys/times.h>
class KisTimeCounter
{
public:
KisTimeCounter() {
m_factor = double(sysconf(_SC_CLK_TCK)) / 1000.0;
restart();
}
void restart() {
times(&m_startTime);
}
double elapsed() {
struct tms endTime;
times(&endTime);
return double(endTime.tms_utime - m_startTime.tms_utime) / m_factor;
}
private:
struct tms m_startTime;
double m_factor;
};
#else /* if(USE_UTIME==0) */
-typedef QTime KisTimeCounter;
+typedef QElapsedTimer KisTimeCounter;
#endif
void KisFilterSelectionsBenchmark::initSelection()
{
m_selection = new KisSelection();
KisPixelSelectionSP pixelSelection = m_selection->pixelSelection();
//67.2% deselected
dbgKrita << "Deselected: 67.2%";
pixelSelection->dataManager()->clear(75, 75, 500, 320, 255);
pixelSelection->dataManager()->clear(100, 100, 50, 50, quint8(0));
pixelSelection->dataManager()->clear(150, 150, 50, 50, quint8(0));
pixelSelection->dataManager()->clear(200, 200, 50, 50, quint8(0));
pixelSelection->dataManager()->clear(375, 195, 200, 200, quint8(0));
pixelSelection->dataManager()->clear(75, 195, 200, 200, quint8(0));
pixelSelection->dataManager()->clear(375, 75, 150, 150, quint8(0));
pixelSelection->dataManager()->clear(205, 105, 50, 50, quint8(128));
// 94.9% deselected
// dbgKrita << "Deselected: 94.9%";
// pixelSelection->dataManager()->clear(75,75,500,320,255);
// pixelSelection->dataManager()->clear(80,80,490,310,quint8(0));
pixelSelection->convertToQImage(0).save("TEST_FILTER_SELECTION.png");
}
void KisFilterSelectionsBenchmark::initFilter(const QString &name)
{
m_filter = KisFilterRegistry::instance()->value(name);
Q_ASSERT(m_filter);
- m_configuration = m_filter->defaultConfiguration();
+ m_configuration = m_filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
dbgKrita << "Filter initialized:" << name;
}
void KisFilterSelectionsBenchmark::testFilter(const QString &name)
{
blockSignals(true);
initFilter(name);
testUsualSelections(WARMUP_CYCLES);
testUsualSelections(NUM_CYCLES);
testGoodSelections(WARMUP_CYCLES);
testGoodSelections(NUM_CYCLES);
testNoSelections(WARMUP_CYCLES);
testNoSelections(NUM_CYCLES);
testBitBltWOSelections(WARMUP_CYCLES);
testBitBltWOSelections(NUM_CYCLES);
testBitBltSelections(WARMUP_CYCLES);
testBitBltSelections(NUM_CYCLES);
blockSignals(false);
}
void KisFilterSelectionsBenchmark::testAll()
{
initSelection();
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
m_device = new KisPaintDevice(cs);
m_device->convertFromQImage(image, 0, 0, 0);
testFilter("brightnesscontrast");
testFilter("invert");
// testFilter("levels");
}
void KisFilterSelectionsBenchmark::testUsualSelections(int num)
{
KisPaintDeviceSP projection =
new KisPaintDevice(m_device->colorSpace());
double avTime;
KisTimeCounter timer;
QRect filterRect = m_selection->selectedExactRect();
timer.restart();
for (int i = 0; i < num; i++) {
KisTransaction transac(projection, 0);
m_filter->process(m_device, projection, m_selection, filterRect, m_configuration, 0);
}
avTime = double(timer.elapsed()) / num;
projection->convertToQImage(0).save("TFS__USUAL_SELECTIONS.png");
if (num > WARMUP_CYCLES || SHOW_WARMUPS)
dbgKrita << "Selections inside filter:\t\t" << avTime;
}
void KisFilterSelectionsBenchmark::testNoSelections(int num)
{
KisPaintDeviceSP projection =
new KisPaintDevice(m_device->colorSpace());
double avTime;
KisTimeCounter timer;
QRect filterRect = m_selection->selectedExactRect();
timer.restart();
for (int i = 0; i < num; i++) {
KisTransaction transac(projection, 0);
m_filter->process(m_device, projection, 0, filterRect, m_configuration, 0);
}
avTime = double(timer.elapsed()) / num;
projection->convertToQImage(0).save("TFS__NO_SELECTIONS.png");
if (num > WARMUP_CYCLES || SHOW_WARMUPS)
dbgKrita << "No Selections:\t\t\t\t" << avTime;
}
void KisFilterSelectionsBenchmark::testGoodSelections(int num)
{
#if(USE_GOOD_SELECTIONS==1)
KisPaintDeviceSP projection =
new KisPaintDevice(m_device->colorSpace());
double avTime;
KisTimeCounter timer;
QRect filterRect = m_selection->selectedExactRect();
KisConstProcessingInformation src(m_device, filterRect.topLeft(), m_selection);
KisProcessingInformation dst(projection, filterRect.topLeft(), 0);
timer.restart();
for (int i = 0; i < num; i++) {
KisTransaction transac(0, projection, 0);
m_filter->processSpecial(src, dst, filterRect.size(), m_configuration, 0);
}
avTime = double(timer.elapsed()) / num;
projection->convertToQImage(0).save("TFS__GOOD_SELECTIONS.png");
if (num > WARMUP_CYCLES || SHOW_WARMUPS)
dbgKrita << "Selections with alpha (filter):\t" << avTime;
#else /* if (USE_GOOD_SELECTIONS!=1) */
if (num > WARMUP_CYCLES || SHOW_WARMUPS)
dbgKrita << "Selections with alpha (filter):\t [Disabled]";
#endif
}
void KisFilterSelectionsBenchmark::testBitBltWOSelections(int num)
{
KisPaintDeviceSP projection =
new KisPaintDevice(m_device->colorSpace());
double avTime;
KisTimeCounter timer;
QRect filterRect = m_selection->selectedExactRect();
timer.restart();
for (int i = 0; i < num; i++) {
KisPaintDeviceSP cacheDevice = new KisPaintDevice(projection->colorSpace());
KisTransaction transac(cacheDevice, 0);
m_filter->process(m_device, projection, 0, filterRect, m_configuration, 0);
KisPainter painter(projection);
painter.beginTransaction();
painter.setCompositeOp(projection->colorSpace()->compositeOp(COMPOSITE_ALPHA_DARKEN));
painter.bitBlt(filterRect.topLeft(), cacheDevice, filterRect);
painter.deleteTransaction();
}
avTime = double(timer.elapsed()) / num;
projection->convertToQImage(0).save("TFS__BITBLT_WO_SELECTIONS.png");
if (num > WARMUP_CYCLES || SHOW_WARMUPS)
dbgKrita << "bitBlt w/o sel:\t\t\t" << avTime;
}
void KisFilterSelectionsBenchmark::testBitBltSelections(int num)
{
KisPaintDeviceSP projection =
new KisPaintDevice(m_device->colorSpace());
double avTime;
KisTimeCounter timer;
QRect filterRect = m_selection->selectedExactRect();
timer.restart();
for (int i = 0; i < num; i++) {
KisPaintDeviceSP cacheDevice = new KisPaintDevice(projection->colorSpace());
KisTransaction transac(cacheDevice, 0);
m_filter->process(m_device, cacheDevice, 0, filterRect, m_configuration, 0);
KisPainter gc(projection);
gc.beginTransaction();
gc.setCompositeOp(projection->colorSpace()->compositeOp(COMPOSITE_ALPHA_DARKEN));
gc.setSelection(m_selection);
gc.bitBlt(filterRect.topLeft(), cacheDevice, filterRect);
gc.deleteTransaction();
}
avTime = double(timer.elapsed()) / num;
projection->convertToQImage(0).save("TFS__BITBLT_WITH_SELECTIONS.png");
if (num > WARMUP_CYCLES || SHOW_WARMUPS)
dbgKrita << "bitBlt with sel:\t\t\t" << avTime;
}
QTEST_MAIN(KisFilterSelectionsBenchmark)
diff --git a/benchmarks/kis_gradient_benchmark.cpp b/benchmarks/kis_gradient_benchmark.cpp
index 8ab24e977f..da04402ff7 100644
--- a/benchmarks/kis_gradient_benchmark.cpp
+++ b/benchmarks/kis_gradient_benchmark.cpp
@@ -1,91 +1,91 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.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 <QTest>
#include <kundo2command.h>
#include "kis_benchmark_values.h"
#include "kis_random_accessor_ng.h"
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColor.h>
#include <KoCompositeOpRegistry.h>
#include <kis_image.h>
#include "kis_gradient_benchmark.h"
#include <kis_gradient_painter.h>
#include <resources/KoStopGradient.h>
void KisGradientBenchmark::initTestCase()
{
m_colorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_device = new KisPaintDevice(m_colorSpace);
m_color = KoColor(m_colorSpace);
m_color.fromQColor(QColor(0,0,0,0)); // default pixel
m_device->fill( 0,0,GMP_IMAGE_WIDTH, GMP_IMAGE_HEIGHT,m_color.data() );
}
void KisGradientBenchmark::benchmarkGradient()
{
KoColor fg(m_colorSpace);
KoColor bg(m_colorSpace);
fg.fromQColor(Qt::blue);
bg.fromQColor(Qt::black);
QBENCHMARK
{
QLinearGradient grad;
grad.setColorAt(0, Qt::white);
grad.setColorAt(1.0, Qt::red);
- KoAbstractGradient* kograd = KoStopGradient::fromQGradient(&grad);
+ KoAbstractGradientSP kograd(KoStopGradient::fromQGradient(&grad));
Q_ASSERT(kograd);
KisGradientPainter fillPainter(m_device);
//setupPainter(&fillPainter);
fillPainter.setGradient(kograd);
fillPainter.beginTransaction(kundo2_noi18n("Gradient Fill"));
//fillPainter.setProgress(updater->startSubtask());
fillPainter.setOpacity(OPACITY_OPAQUE_U8);
// default
fillPainter.setCompositeOp(COMPOSITE_OVER);
fillPainter.setGradientShape(KisGradientPainter::GradientShapeBiLinear);
fillPainter.paintGradient(QPointF(0,0), QPointF(3000,3000), KisGradientPainter::GradientRepeatNone, 1.0, false, 0, 0, GMP_IMAGE_WIDTH,GMP_IMAGE_HEIGHT);
fillPainter.deleteTransaction();
}
// uncomment this to see the output
QImage out = m_device->convertToQImage(m_colorSpace->profile(),0,0,GMP_IMAGE_WIDTH,GMP_IMAGE_HEIGHT);
out.save("fill_output.png");
}
void KisGradientBenchmark::cleanupTestCase()
{
}
QTEST_MAIN(KisGradientBenchmark)
diff --git a/benchmarks/kis_level_filter_benchmark.cpp b/benchmarks/kis_level_filter_benchmark.cpp
index 63a35a10a3..299dbfc2c9 100644
--- a/benchmarks/kis_level_filter_benchmark.cpp
+++ b/benchmarks/kis_level_filter_benchmark.cpp
@@ -1,106 +1,107 @@
/*
* 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.
*/
#include <QTest>
#include "kis_level_filter_benchmark.h"
#include "kis_benchmark_values.h"
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColor.h>
#include <kis_image.h>
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_color_transformation_configuration.h"
#include "filter/kis_filter.h"
#include "kis_processing_information.h"
#include "kis_selection.h"
#include <kis_iterator_ng.h>
#include "krita_utils.h"
+#include <KisGlobalResourcesInterface.h>
void KisLevelFilterBenchmark::initTestCase()
{
m_colorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_device = new KisPaintDevice(m_colorSpace);
m_color = KoColor(m_colorSpace);
QColor qcolor(Qt::red);
srand(31524744);
int r,g,b;
KisSequentialIterator it(m_device, QRect(0,0,GMP_IMAGE_WIDTH, GMP_IMAGE_HEIGHT));
while (it.nextPixel()) {
r = rand() % 255;
g = rand() % 255;
b = rand() % 255;
m_color.fromQColor(QColor(r,g,b));
memcpy(it.rawData(), m_color.data(), m_colorSpace->pixelSize());
}
}
void KisLevelFilterBenchmark::cleanupTestCase()
{
}
void KisLevelFilterBenchmark::benchmarkFilter()
{
KisFilterSP filter = KisFilterRegistry::instance()->value("levels");
//KisFilterConfigurationSP kfc = filter->defaultConfiguration(m_device);
- KisColorTransformationConfiguration * kfc= new KisColorTransformationConfiguration("levels", 1);
+ KisColorTransformationConfiguration * kfc= new KisColorTransformationConfiguration("levels", 1, KisGlobalResourcesInterface::instance());
kfc->setProperty("blackvalue", 75);
kfc->setProperty("whitevalue", 231);
kfc->setProperty("gammavalue", 1.0);
kfc->setProperty("outblackvalue", 0);
kfc->setProperty("outwhitevalue", 255);
// Get the predefined configuration from a file
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + filter->id() + ".cfg");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out.setCodec("UTF-8");
out << kfc->toXML();
} else {
QString s;
QTextStream in(&file);
in.setCodec("UTF-8");
s = in.readAll();
kfc->fromXML(s);
}
QSize size = KritaUtils::optimalPatchSize();
QVector<QRect> rects = KritaUtils::splitRectIntoPatches(QRect(0, 0, GMP_IMAGE_WIDTH,GMP_IMAGE_HEIGHT), size);
QBENCHMARK{
Q_FOREACH (const QRect &rc, rects) {
filter->process(m_device, rc, kfc);
}
}
}
QTEST_MAIN(KisLevelFilterBenchmark)
diff --git a/benchmarks/kis_low_memory_benchmark.cpp b/benchmarks/kis_low_memory_benchmark.cpp
index affbd3e6d5..4ea4bec604 100644
--- a/benchmarks/kis_low_memory_benchmark.cpp
+++ b/benchmarks/kis_low_memory_benchmark.cpp
@@ -1,231 +1,233 @@
/*
* 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.
*/
#include "kis_low_memory_benchmark.h"
#include <QTest>
#include "kis_benchmark_values.h"
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_paint_layer.h>
#include "kis_paint_device.h"
#include "kis_painter.h"
#include <brushengine/kis_paint_information.h>
#include <brushengine/kis_paintop_registry.h>
#include <brushengine/kis_paintop_preset.h>
+#include "KisGlobalResourcesInterface.h"
+
#include "tiles3/kis_tile_data_store.h"
#include "kis_surrogate_undo_adapter.h"
#include "kis_image_config.h"
#define LOAD_PRESET_OR_RETURN(preset, fileName) \
- if(!preset->load()) { dbgKrita << "Preset" << fileName << "was NOT loaded properly. Done."; return; } \
+ if(!preset->load(KisGlobalResourcesInterface::instance())) { dbgKrita << "Preset" << fileName << "was NOT loaded properly. Done."; return; } \
else dbgKrita << "Loaded preset:" << fileName
#define HUGE_IMAGE_SIZE 8000
/**
* This benchmark runs a series of huge strokes on a canvas with a
* particular configuration of the swapper/pooler and history
* management. After the test is done you can visualize the results
* with the GNU Octave. Please use kis_low_memory_show_report.m file
* for that.
*/
void KisLowMemoryBenchmark::benchmarkWideArea(const QString presetFileName,
const QRectF &rect, qreal vstep,
int numCycles,
bool createTransaction,
int hardLimitMiB,
int softLimitMiB,
int poolLimitMiB,
int index)
{
- KisPaintOpPresetSP preset = new KisPaintOpPreset(QString(FILES_DATA_DIR) + QDir::separator() + presetFileName);
+ KisPaintOpPresetSP preset(new KisPaintOpPreset(QString(FILES_DATA_DIR) + QDir::separator() + presetFileName));
LOAD_PRESET_OR_RETURN(preset, presetFileName);
/**
* Initialize image and painter
*/
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, HUGE_IMAGE_SIZE, HUGE_IMAGE_SIZE, colorSpace, "stroke sample image");
KisLayerSP layer = new KisPaintLayer(image, "temporary for stroke sample", OPACITY_OPAQUE_U8, colorSpace);
KisLayerSP layerExtra = new KisPaintLayer(image, "temporary for threading", OPACITY_OPAQUE_U8, colorSpace);
image->addNode(layer, image->root());
image->addNode(layerExtra, image->root());
KisPainter *painter = new KisPainter(layer->paintDevice());
painter->setPaintColor(KoColor(Qt::black, colorSpace));
painter->setPaintOpPreset(preset, layer, image);
/**
* A simple adapter that will store all the transactions for us
*/
KisSurrogateUndoAdapter undoAdapter;
/**
* Reset configuration to the desired settings
*/
KisImageConfig config(false);
qreal oldHardLimit = config.memoryHardLimitPercent();
qreal oldSoftLimit = config.memorySoftLimitPercent();
qreal oldPoolLimit = config.memoryPoolLimitPercent();
const qreal _MiB = 100.0 / KisImageConfig::totalRAM();
config.setMemoryHardLimitPercent(hardLimitMiB * _MiB);
config.setMemorySoftLimitPercent(softLimitMiB * _MiB);
config.setMemoryPoolLimitPercent(poolLimitMiB * _MiB);
KisTileDataStore::instance()->testingRereadConfig();
/**
* Create an empty the log file
*/
QString fileName;
fileName = QString("log_%1_%2_%3_%4_%5.txt")
.arg(createTransaction)
.arg(hardLimitMiB)
.arg(softLimitMiB)
.arg(poolLimitMiB)
.arg(index);
QFile logFile(fileName);
logFile.open(QFile::WriteOnly | QFile::Truncate);
QTextStream logStream(&logFile);
logStream.setFieldWidth(10);
logStream.setFieldAlignment(QTextStream::AlignRight);
/**
* Start painting on the image
*/
- QTime cycleTime;
- QTime lineTime;
+ QElapsedTimer cycleTime;
+ QElapsedTimer lineTime;
cycleTime.start();
lineTime.start();
qreal rectBottom = rect.y() + rect.height();
for (int i = 0; i < numCycles; i++) {
cycleTime.restart();
QLineF line(rect.topLeft(), rect.topLeft() + QPointF(rect.width(), 0));
if (createTransaction) {
painter->beginTransaction();
}
KisDistanceInformation currentDistance;
while(line.y1() < rectBottom) {
lineTime.restart();
KisPaintInformation pi1(line.p1(), 0.0);
KisPaintInformation pi2(line.p2(), 1.0);
painter->paintLine(pi1, pi2, &currentDistance);
painter->device()->setDirty(painter->takeDirtyRegion());
logStream << "L 1" << i << lineTime.elapsed()
<< KisTileDataStore::instance()->numTilesInMemory() * 16
<< KisTileDataStore::instance()->numTiles() * 16
<< createTransaction << endl;
line.translate(0, vstep);
}
painter->device()->setDirty(painter->takeDirtyRegion());
if (createTransaction) {
painter->endTransaction(&undoAdapter);
}
// comment/uncomment to emulate user waiting after the stroke
QTest::qSleep(1000);
logStream << "C 2" << i << cycleTime.elapsed()
<< KisTileDataStore::instance()->numTilesInMemory() * 16
<< KisTileDataStore::instance()->numTiles() * 16
<< createTransaction
<< config.memoryHardLimitPercent() / _MiB
<< config.memorySoftLimitPercent() / _MiB
<< config.memoryPoolLimitPercent() / _MiB << endl;
}
config.setMemoryHardLimitPercent(oldHardLimit * _MiB);
config.setMemorySoftLimitPercent(oldSoftLimit * _MiB);
config.setMemoryPoolLimitPercent(oldPoolLimit * _MiB);
delete painter;
}
void KisLowMemoryBenchmark::unlimitedMemoryNoHistoryNoPool()
{
QString presetFileName = "autobrush_300px.kpp";
// one cycle takes about 48 MiB of memory (total 960 MiB)
QRectF rect(150,150,4000,4000);
qreal step = 250;
int numCycles = 20;
benchmarkWideArea(presetFileName, rect, step, numCycles, false,
3000, 3000, 0, 0);
}
void KisLowMemoryBenchmark::unlimitedMemoryHistoryNoPool()
{
QString presetFileName = "autobrush_300px.kpp";
// one cycle takes about 48 MiB of memory (total 960 MiB)
QRectF rect(150,150,4000,4000);
qreal step = 250;
int numCycles = 20;
benchmarkWideArea(presetFileName, rect, step, numCycles, true,
3000, 3000, 0, 0);
}
void KisLowMemoryBenchmark::unlimitedMemoryHistoryPool50()
{
QString presetFileName = "autobrush_300px.kpp";
// one cycle takes about 48 MiB of memory (total 960 MiB)
QRectF rect(150,150,4000,4000);
qreal step = 250;
int numCycles = 20;
benchmarkWideArea(presetFileName, rect, step, numCycles, true,
3000, 3000, 50, 0);
}
void KisLowMemoryBenchmark::memory2000History100Pool500HugeBrush()
{
QString presetFileName = "BIG_TESTING.kpp";
// one cycle takes about 316 MiB of memory (total 3+ GiB)
QRectF rect(150,150,7850,7850);
qreal step = 250;
int numCycles = 10;
benchmarkWideArea(presetFileName, rect, step, numCycles, true,
2000, 600, 500, 0);
}
QTEST_MAIN(KisLowMemoryBenchmark)
diff --git a/benchmarks/kis_stroke_benchmark.cpp b/benchmarks/kis_stroke_benchmark.cpp
index 93296b2dbe..ac2e34239d 100644
--- a/benchmarks/kis_stroke_benchmark.cpp
+++ b/benchmarks/kis_stroke_benchmark.cpp
@@ -1,634 +1,636 @@
/*
* 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.
*/
#include <stdlib.h>
#if defined(_WIN32) || defined(_WIN64)
#define srand48 srand
inline double drand48()
{
return double(rand()) / RAND_MAX;
}
#endif
#include <QTest>
#include "kis_stroke_benchmark.h"
#include "kis_benchmark_values.h"
#include "kis_paint_device.h"
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColor.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_paint_layer.h>
#include <brushengine/kis_paint_information.h>
#include <brushengine/kis_paintop_preset.h>
#define GMP_IMAGE_WIDTH 3274
#define GMP_IMAGE_HEIGHT 2067
#include <kis_painter.h>
#include <brushengine/kis_paintop_registry.h>
+#include <KisGlobalResourcesInterface.h>
+
//#define SAVE_OUTPUT
static const int LINES = 20;
static const int RECTANGLES = 20;
const QString OUTPUT_FORMAT = ".png";
void KisStrokeBenchmark::initTestCase()
{
m_dataPath = QString(FILES_DATA_DIR) + QDir::separator();
m_outputPath = QString(FILES_OUTPUT_DIR) + QDir::separator();
m_colorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_color = KoColor(m_colorSpace);
int width = TEST_IMAGE_WIDTH;
int height = TEST_IMAGE_HEIGHT;
m_image = new KisImage(0, width, height, m_colorSpace, "stroke sample image");
m_layer = new KisPaintLayer(m_image, "temporary for stroke sample", OPACITY_OPAQUE_U8, m_colorSpace);
m_painter = new KisPainter(m_layer->paintDevice());
m_painter->setPaintColor(KoColor(Qt::black, m_colorSpace));
// for bezier curve test
initCurvePoints(width, height);
// for the lines test
initLines(width,height);
// for the rectangles test
initRectangles(width, height);
}
void KisStrokeBenchmark::init()
{
KoColor white(m_colorSpace);
white.fromQColor(Qt::white);
m_layer->paintDevice()->fill(0,0, m_image->width(), m_image->height(),white.data());
}
void KisStrokeBenchmark::initCurvePoints(int width, int height)
{
QPointF p1(0 , 7.0 / 12.0 * height);
QPointF p2(1.0 / 2.0 * width , 7.0 / 12.0 * height);
QPointF p3(width - 4.0, height - 4.0);
m_c1 = QPointF(1.0 / 4.0 * width, height - 2.0);
m_c2 = QPointF(3.0 / 4.0 * width, 0);
m_pi1 = KisPaintInformation(p1, 0.0);
m_pi2 = KisPaintInformation(p2, 0.95);
m_pi3 = KisPaintInformation(p3, 0.0);
}
void KisStrokeBenchmark::initLines(int width, int height)
{
srand(12345678);
for (int i = 0; i < LINES; i++){
qreal sx = rand() / qreal(RAND_MAX - 1);
qreal sy = rand() / qreal(RAND_MAX - 1);
m_startPoints.append(QPointF(sx * width,sy * height));
qreal ex = rand() / qreal(RAND_MAX - 1);
qreal ey = rand() / qreal(RAND_MAX - 1);
m_endPoints.append(QPointF(ex * width,ey * height));
}
}
void KisStrokeBenchmark::initRectangles(int width, int height)
{
qreal margin = 0.5;
qreal skip = 0.01;
qreal marginWidth = margin*width;
qreal marginHeight = margin*height;
qreal skipWidth = skip*width >= 1 ? skip*width : 1;
qreal skipHeight = skip*width >= 1 ? skip*width : 1;
// "concentric" rectangles
for (int i = 0; i < RECTANGLES; i++){
QPoint corner1 = QPoint(marginWidth + i*skipWidth, marginHeight + i*skipHeight);
QPoint corner2 = QPoint(width - marginWidth - i*skipWidth, height - marginHeight - i*skipHeight);
if(corner1.x() < corner2.x() && corner1.y() < corner2.y()) {
// if the rectangle is not empty
m_rectangleLeftLowerCorners.append(corner1);
m_rectangleRightUpperCorners.append(corner2);
}
}
}
void KisStrokeBenchmark::cleanupTestCase()
{
delete m_painter;
}
void KisStrokeBenchmark::deformBrush()
{
QString presetFileName = "deform-default.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::deformBrushRL()
{
QString presetFileName = "deform-default.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::pixelbrush300px()
{
QString presetFileName = "autobrush_300px.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::pixelbrush300pxRL()
{
QString presetFileName = "autobrush_300px.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::sprayPixels()
{
QString presetFileName = "spray_wu_pixels1.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::sprayPixelsRL()
{
QString presetFileName = "spray_wu_pixels1.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::sprayTexture()
{
QString presetFileName = "spray_21_textures1.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::sprayTextureRL()
{
QString presetFileName = "spray_21_textures1.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::spray30px21particles()
{
QString presetFileName = "spray_30px21rasterParticles.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::spray30px21particlesRL()
{
QString presetFileName = "spray_30px21rasterParticles.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::sprayPencil()
{
QString presetFileName = "spray_scaled2rasterParticles.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::sprayPencilRL()
{
QString presetFileName = "spray_scaled2rasterParticles.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::softbrushDefault30()
{
QString presetFileName = "softbrush_30px.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::softbrushCircle30()
{
QString presetFileName = "softbrush_30px.kpp";
benchmarkCircle(presetFileName);
}
void KisStrokeBenchmark::softbrushDefault30RL()
{
QString presetFileName = "softbrush_30px.kpp";
benchmarkRandomLines(presetFileName);}
void KisStrokeBenchmark::softbrushFullFeatures30()
{
QString presetFileName = "softbrush_30px_full.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::softbrushFullFeatures30RL()
{
QString presetFileName = "softbrush_30px_full.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::hairy30pxDefault()
{
QString presetFileName = "hairybrush_thesis30px1.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::hairy30pxDefaultRL()
{
QString presetFileName = "hairybrush_thesis30px1.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::hairy30pxAntiAlias()
{
QString presetFileName = "hairybrush_thesis30px_antialiasing1.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::hairy30pxAntiAliasRL()
{
QString presetFileName = "hairybrush_thesis30px_antialiasing1.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::hairy30px30density()
{
QString presetFileName = "hairybrush_thesis30px_density301.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::hairy30px30densityRL()
{
QString presetFileName = "hairybrush_thesis30px_density301.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::hairy30InkDepletion()
{
QString presetFileName = "hairy30inkDepletion1.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::hairy30InkDepletionRL()
{
QString presetFileName = "hairy30inkDepletion1.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::softbrushOpacity()
{
QString presetFileName = "softbrush_opacity1.kpp";
benchmarkLine(presetFileName);
}
void KisStrokeBenchmark::softbrushSoftness()
{
QString presetFileName = "softbrush_softness1.kpp";
benchmarkLine(presetFileName);
}
void KisStrokeBenchmark::dynabrush()
{
QString presetFileName = "dyna301.kpp";
benchmarkLine(presetFileName);
}
void KisStrokeBenchmark::dynabrushRL()
{
QString presetFileName = "dyna301.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::experimental()
{
QString presetFileName = "experimental.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::experimentalCircle()
{
QString presetFileName = "experimental.kpp";
benchmarkCircle(presetFileName);
}
void KisStrokeBenchmark::colorsmudge()
{
QString presetFileName = "colorsmudge.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::colorsmudgeRL()
{
QString presetFileName = "colorsmudge.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::roundMarker()
{
// Quick Brush engine ( b) Basic - 1 brush, size 40px)
QString presetFileName = "roundmarker40px.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::roundMarkerRandomLines()
{
// Quick Brush engine ( b) Basic - 1 brush, size 40px)
QString presetFileName = "roundmarker40px.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::roundMarkerRectangle()
{
// Quick Brush engine ( b) Basic - 1 brush, size 40px)
QString presetFileName = "roundmarker40px.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::roundMarkerHalfPixel()
{
// Quick Brush engine ( b) Basic - 1 brush, size 0.5px)
QString presetFileName = "roundmarker05px.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::roundMarkerRandomLinesHalfPixel()
{
// Quick Brush engine ( b) Basic - 1 brush, size 0.5px)
QString presetFileName = "roundmarker05px.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::roundMarkerRectangleHalfPixel()
{
// Quick Brush engine ( b) Basic - 1 brush, size 0.5px)
QString presetFileName = "roundmarker05px.kpp";
benchmarkStroke(presetFileName);
}
/*
void KisStrokeBenchmark::predefinedBrush()
{
QString presetFileName = "deevad-slow-brush1.kpp";
benchmarkLine(presetFileName);
}
void KisStrokeBenchmark::predefinedBrushRL()
{
QString presetFileName = "deevad-slow-brush1.kpp";
KisPaintOpPresetSP preset = new KisPaintOpPreset(m_dataPath + presetFileName);
preset->load();
preset->settings()->setNode(m_layer);
m_painter->setPaintOpPreset(preset, m_image);
sleep(3);
QBENCHMARK{
for (int i = 0; i < LINES; i++){
KisPaintInformation pi1(m_startPoints[i], 0.0);
KisPaintInformation pi2(m_endPoints[i], 1.0);
m_painter->paintLine(pi1, pi2);
}
}
//m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_randomLines" + OUTPUT_FORMAT);
}
*/
inline void KisStrokeBenchmark::benchmarkLine(QString presetFileName)
{
- KisPaintOpPresetSP preset = new KisPaintOpPreset(m_dataPath + presetFileName);
- preset->load();
+ KisPaintOpPresetSP preset(new KisPaintOpPreset(m_dataPath + presetFileName));
+ preset->load(KisGlobalResourcesInterface::instance());
m_painter->setPaintOpPreset(preset, m_layer, m_image);
QPointF startPoint(0.10 * TEST_IMAGE_WIDTH, 0.5 * TEST_IMAGE_HEIGHT);
QPointF endPoint(0.90 * TEST_IMAGE_WIDTH, 0.5 * TEST_IMAGE_HEIGHT);
KisDistanceInformation currentDistance;
KisPaintInformation pi1(startPoint, 0.0);
KisPaintInformation pi2(endPoint, 1.0);
QBENCHMARK{
m_painter->paintLine(pi1, pi2, &currentDistance);
}
#ifdef SAVE_OUTPUT
m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_line" + OUTPUT_FORMAT);
#endif
}
void KisStrokeBenchmark::benchmarkCircle(QString presetFileName)
{
dbgKrita << "(circle)preset : " << presetFileName;
- KisPaintOpPresetSP preset = new KisPaintOpPreset(m_dataPath + presetFileName);
- if (!preset->load()){
+ KisPaintOpPresetSP preset(new KisPaintOpPreset(m_dataPath + presetFileName));
+ if (!preset->load(KisGlobalResourcesInterface::instance())){
dbgKrita << "Preset was not loaded";
return;
}
m_painter->setPaintOpPreset(preset, m_layer, m_image);
QBENCHMARK{
qreal radius = 300;
qreal randomOffset = 300 * 0.4;
int rounds = 20;
int steps = 20;
qreal step = 1.0 / steps;
QPointF center(m_image->width() * 0.5, m_image->height() * 0.5);
QPointF first(center.x()+radius,center.y());
srand48(0);
for (int k = 0; k < rounds; k++){
KisDistanceInformation currentDistance;
m_painter->paintLine(center, first, &currentDistance);
QPointF prev = first;
for (int i = 1; i < steps; i++) {
qreal cx = cos(i * step * 2 * M_PI);
qreal cy = sin(i * step * 2 * M_PI);
cx *= (radius + drand48() * randomOffset);
cy *= (radius + drand48() * randomOffset);
cx += center.x();
cy += center.y();
m_painter->paintLine(prev, QPointF(cx,cy), &currentDistance);
prev = QPointF(cx,cy);
}
m_painter->paintLine(prev, first, &currentDistance);
}
}
#ifdef SAVE_OUTPUT
m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_circle" + OUTPUT_FORMAT);
#endif
}
void KisStrokeBenchmark::benchmarkRandomLines(QString presetFileName)
{
- KisPaintOpPresetSP preset = new KisPaintOpPreset(m_dataPath + presetFileName);
- bool loadedOk = preset->load();
+ KisPaintOpPresetSP preset(new KisPaintOpPreset(m_dataPath + presetFileName));
+ bool loadedOk = preset->load(KisGlobalResourcesInterface::instance());
if (!loadedOk){
dbgKrita << "The preset was not loaded correctly. Done.";
return;
}else{
dbgKrita << "preset : " << presetFileName;
}
m_painter->setPaintOpPreset(preset, m_layer, m_image);
QBENCHMARK{
KisDistanceInformation currentDistance;
for (int i = 0; i < LINES; i++){
KisPaintInformation pi1(m_startPoints[i], 0.0);
KisPaintInformation pi2(m_endPoints[i], 1.0);
m_painter->paintLine(pi1, pi2, &currentDistance);
}
}
#ifdef SAVE_OUTPUT
m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_randomLines" + OUTPUT_FORMAT);
#endif
}
void KisStrokeBenchmark::benchmarkRectangle(QString presetFileName)
{
- KisPaintOpPresetSP preset = new KisPaintOpPreset(m_dataPath + presetFileName);
- bool loadedOk = preset->load();
+ KisPaintOpPresetSP preset(new KisPaintOpPreset(m_dataPath + presetFileName));
+ bool loadedOk = preset->load(KisGlobalResourcesInterface::instance());
if (!loadedOk){
dbgKrita << "The preset was not loaded correctly. Done.";
return;
}else{
dbgKrita << "preset : " << presetFileName;
}
m_painter->setPaintOpPreset(preset, m_layer, m_image);
int rectangleNumber = m_rectangleLeftLowerCorners.size(); // see initRectangles
QBENCHMARK{
KisDistanceInformation currentDistance;
for (int i = 0; i < rectangleNumber; i++){
QPainterPath path;
QRect rect = QRect(m_rectangleLeftLowerCorners[i], m_rectangleRightUpperCorners[i]);
path.addRect(rect);
m_painter->paintPainterPath(path);
}
}
#ifdef SAVE_OUTPUT
m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_rectangle" + OUTPUT_FORMAT);
#endif
}
void KisStrokeBenchmark::benchmarkStroke(QString presetFileName)
{
- KisPaintOpPresetSP preset = new KisPaintOpPreset(m_dataPath + presetFileName);
- bool loadedOk = preset->load();
+ KisPaintOpPresetSP preset(new KisPaintOpPreset(m_dataPath + presetFileName));
+ bool loadedOk = preset->load(KisGlobalResourcesInterface::instance());
if (!loadedOk){
dbgKrita << "The preset was not loaded correctly. Done.";
return;
} else {
dbgKrita << "preset : " << presetFileName;
}
m_painter->setPaintOpPreset(preset, m_layer, m_image);
QBENCHMARK{
KisDistanceInformation currentDistance;
m_painter->paintBezierCurve(m_pi1, m_c1, m_c1, m_pi2, &currentDistance);
m_painter->paintBezierCurve(m_pi2, m_c2, m_c2, m_pi3, &currentDistance);
}
#ifdef SAVE_OUTPUT
dbgKrita << "Saving output " << m_outputPath + presetFileName + ".png";
m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + OUTPUT_FORMAT);
#endif
}
static const int COUNT = 1000000;
void KisStrokeBenchmark::benchmarkRand48()
{
QBENCHMARK
{
for (int i = 0 ; i < COUNT; i++){
double result = drand48();
Q_UNUSED(result);
}
}
}
void KisStrokeBenchmark::benchmarkRand()
{
float j;
QBENCHMARK{
for (int i = 0 ; i < COUNT; i++){
j = rand() / (float)RAND_MAX;
}
}
Q_UNUSED(j);
}
void KisStrokeBenchmark::becnhmarkPresetCloning()
{
QString presetFileName = "spray_21_textures1.kpp";
- KisPaintOpPresetSP preset = new KisPaintOpPreset(m_dataPath + presetFileName);
- bool loadedOk = preset->load();
+ KisPaintOpPresetSP preset(new KisPaintOpPreset(m_dataPath + presetFileName));
+ bool loadedOk = preset->load(KisGlobalResourcesInterface::instance());
KIS_ASSERT_RECOVER_RETURN(loadedOk);
KIS_ASSERT_RECOVER_RETURN(preset->settings());
QBENCHMARK {
- KisPaintOpPresetSP other = preset->clone();
+ KisPaintOpPresetSP other = preset->clone().dynamicCast<KisPaintOpPreset>();
other->settings()->setPaintOpOpacity(0.3);
}
}
QTEST_MAIN(KisStrokeBenchmark)
diff --git a/krita/data/paintoppresets/CMakeLists.txt b/krita/data/paintoppresets/CMakeLists.txt
index c382cd9442..877f7cafe0 100644
--- a/krita/data/paintoppresets/CMakeLists.txt
+++ b/krita/data/paintoppresets/CMakeLists.txt
@@ -1,5 +1,17 @@
install( FILES
a\)_Eraser_Circle.kpp
b\)_Basic-5_Size_default.kpp
DESTINATION ${DATA_INSTALL_DIR}/krita/paintoppresets)
+
+install( FILES
+ "Favorites.tag"
+ "Pixel Art.tag"
+ Digital.tag
+ Erasers.tag
+ FX.tag
+ Ink.tag
+ Paint.tag
+ Sketch.tag
+ Textures.tag
+DESTINATION ${DATA_INSTALL_DIR}/krita/paintoppresets)
diff --git a/krita/data/paintoppresets/Digital.tag b/krita/data/paintoppresets/Digital.tag
new file mode 100644
index 0000000000..a1797de120
--- /dev/null
+++ b/krita/data/paintoppresets/Digital.tag
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Tag
+Name=Digital
+Comment=Brush presets with a digital look and feel.
+URL=Digital
+Default Resources=a)_Eraser_Circle,b)_Basic-5_Size
diff --git a/krita/data/paintoppresets/Erasers.tag b/krita/data/paintoppresets/Erasers.tag
new file mode 100644
index 0000000000..7bc34e87b9
--- /dev/null
+++ b/krita/data/paintoppresets/Erasers.tag
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Tag
+Name=Erasers
+Comment=Eraser Brush Presets
+URL=Erasers
+Default Resources=a)_Eraser_Circle
diff --git a/krita/data/paintoppresets/FX.tag b/krita/data/paintoppresets/FX.tag
new file mode 100644
index 0000000000..f95e390c2f
--- /dev/null
+++ b/krita/data/paintoppresets/FX.tag
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Tag
+Name=FX
+Comment=Special Effects
+URL=FX
diff --git a/krita/data/paintoppresets/Favorites.tag b/krita/data/paintoppresets/Favorites.tag
new file mode 100644
index 0000000000..20dc0315c3
--- /dev/null
+++ b/krita/data/paintoppresets/Favorites.tag
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Tag
+Name=★ My Favorites
+Comment=Your favorite brush presets
+URL=★ My Favorites
diff --git a/krita/data/paintoppresets/Ink.tag b/krita/data/paintoppresets/Ink.tag
new file mode 100644
index 0000000000..6c6c2d80a7
--- /dev/null
+++ b/krita/data/paintoppresets/Ink.tag
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Tag
+Name=Ink
+Comment=Inking Brush Presets
+URL=Ink
diff --git a/krita/data/paintoppresets/Paint.tag b/krita/data/paintoppresets/Paint.tag
new file mode 100644
index 0000000000..2f9d6fe3b4
--- /dev/null
+++ b/krita/data/paintoppresets/Paint.tag
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Tag
+Name=Paint
+Comment=Paint Brushes
+URL=Paint
diff --git a/krita/data/paintoppresets/Pixel Art.tag b/krita/data/paintoppresets/Pixel Art.tag
new file mode 100644
index 0000000000..0d057f1f23
--- /dev/null
+++ b/krita/data/paintoppresets/Pixel Art.tag
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Tag
+Name=Pixel Art
+Comment=Presets for working with individual pixels
+URL=Pixel Art
diff --git a/krita/data/paintoppresets/Sketch.tag b/krita/data/paintoppresets/Sketch.tag
new file mode 100644
index 0000000000..2e252fe0ef
--- /dev/null
+++ b/krita/data/paintoppresets/Sketch.tag
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Tag
+Name=Sketch
+Comment=Sketching brush presets
+URL=Sketch
diff --git a/krita/data/paintoppresets/Textures.tag b/krita/data/paintoppresets/Textures.tag
new file mode 100644
index 0000000000..c8ff245b73
--- /dev/null
+++ b/krita/data/paintoppresets/Textures.tag
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Tag
+Name=Textures
+Comment=Textured Brush Presets
+URL=Textures
diff --git a/krita/data/shortcuts/krita_default.shortcuts b/krita/data/shortcuts/krita_default.shortcuts
index e7ca0597dd..0c7463f8ec 100644
--- a/krita/data/shortcuts/krita_default.shortcuts
+++ b/krita/data/shortcuts/krita_default.shortcuts
@@ -1,286 +1,285 @@
[Shortcuts]
activateNextLayer=PgUp
activatePreviousLayer=PgDown
add_blank_frame=none
add_duplicate_frame=none
add_new_adjustment_layer=none
add_new_clone_layer=none
add_new_file_layer=none
add_new_fill_layer=none
add_new_filter_mask=none
add_new_group_layer=none
add_new_paint_layer=none
add_new_selection_mask=none
add_new_shape_layer=none
add_new_transform_mask=none
add_new_transparency_mask=none
borderselection=none
BrushesAndStuff=none
brushslider2=none
brushslider3=none
canvassize=Ctrl+Alt+C
clear=Del
clones_array=none
colorrange=none
composite_actions=none
convert_selection_to_shape=none
convert_shapes_to_vector_selection=none
convert_to_filter_mask=none
convert_to_paint_layer=none
convert_to_selection_mask=none
convert_to_transparency_mask=none
convert_to_vector_selection=none
copy_merged=Ctrl+Shift+C
copy_selection_to_new_layer=Ctrl+Alt+J
copy_sharp=none
create_bundle=none
create_copy=none
create_template=none
cut_selection_to_new_layer=Ctrl+Shift+J
cut_sharp=none
decrease_brush_size=[
decrease_opacity=I
delete_keyframe=none
deselect=Ctrl+Shift+A
dual=none
duplicatelayer=Ctrl+J
-edit_blacklist_cleanup=none
edit_copy=Ctrl+C
edit_cut=Ctrl+X
EditLayerMetaData=none
edit_paste=Ctrl+V
edit_redo=Ctrl+Shift+Z
edit_undo=Ctrl+Z
erase_action=E
featherselection=Shift+F6
file_close_all=Ctrl+Shift+W
file_close=Ctrl+W
file_documentinfo=none
file_export_animation=none
file_export_file=none
file_export_pdf=none
file_import_file=none
file_new=Ctrl+N
file_open=Ctrl+O
file_open_recent=none
file_print=Ctrl+P
file_print_preview=none
file_quit=Ctrl+Q
file_save_as=Ctrl+Shift+S
file_save=Ctrl+S
fill_selection_background_color=Backspace
fill_selection_background_color_opacity=Ctrl+Backspace
fill_selection_foreground_color_opacity=Ctrl+Shift+Backspace
fill_selection_foreground_color=Shift+Backspace
fill_selection_pattern=none
fill_selection_pattern_opacity=none
filter_apply_again=Ctrl+F
first_frame=none
flatten_image=Ctrl+Shift+E
flatten_layer=none
fullscreen=Ctrl+Shift+F
gmic=none
gradients=none
growselection=none
help_about_app=none
help_about_kde=none
help_contents=F1
help_report_bug=none
histogram=none
hmirror_action=none
image_color=none
imagecolorspaceconversion=none
image_properties=none
imagesize=Ctrl+Alt+I
imagesplit=none
import_layer_as_filter_mask=none
import_layer_as_paint_layer=none
import_layer_as_selection_mask=none
import_layer_as_transparency_mask=none
import_layer_from_file=none
import_resources=none
increase_brush_size=]
increase_opacity=O
invert=Ctrl+Shift+I
isolate_layer=none
krita_filter_autocontrast=none
krita_filter_blur=none
krita_filter_bottom edge detections=none
krita_filter_brightnesscontrast=none
krita_filter_burn=none
krita_filter_colorbalance=Ctrl+B
krita_filter_colortoalpha=none
krita_filter_colortransfer=none
krita_filter_desaturate=Ctrl+Shift+U
krita_filter_dodge=none
krita_filter_emboss all directions=none
krita_filter_emboss horizontal and vertical=none
krita_filter_emboss horizontal only=none
krita_filter_emboss laplascian=none
krita_filter_emboss=none
krita_filter_emboss vertical only=none
krita_filter_gaussian blur=none
krita_filter_gaussiannoisereducer=none
krita_filter_hsvadjustment=Ctrl+U
krita_filter_indexcolors=none
krita_filter_invert=Ctrl+I
krita_filter_left edge detections=none
krita_filter_lens blur=none
krita_filter_levels=Ctrl+L
krita_filter_maximize=none
krita_filter_mean removal=none
krita_filter_minimize=none
krita_filter_motion blur=none
krita_filter_noise=none
krita_filter_oilpaint=none
krita_filter_perchannel=Ctrl+M
krita_filter_phongbumpmap=none
krita_filter_pixelize=none
krita_filter_posterize=none
krita_filter_raindrops=none
krita_filter_randompick=none
krita_filter_right edge detections=none
krita_filter_roundcorners=none
krita_filter_sharpen=none
krita_filter_smalltiles=none
krita_filter_sobel=none
krita_filter_top edge detections=none
krita_filter_unsharp=none
krita_filter_waveletnoisereducer=none
krita_filter_wave=none
last_frame=none
layercolorspaceconversion=none
LayerGroupSwitcher/next=none
LayerGroupSwitcher/previous=none
layer_properties=none
layersize=none
layersplit=none
layer_style=none
lazy_frame=none
level_of_detail_mode=Shift+L
Macro_Open_Edit=none
Macro_Open_Play=none
mainToolBar=none
make_brush_color_darker=K
make_brush_color_lighter=L
manage_bundles=none
merge_layer=Ctrl+E
mirror_actions=none
mirror_canvas=M
mirrorImageHorizontal=none
mirrorImageVertical=none
mirrorNodeX=none
mirrorNodeY=none
move_layer_down=none
move_layer_up=none
next_favorite_preset=,
next_frame=none
next_keyframe=none
offsetimage=none
offsetlayer=none
open_resources_directory=none
options_configure_keybinding=none
options_configure=none
options_configure_toolbars=none
paintops=none
paste_at=none
paste_new=Ctrl+Shift+N
paste_as_reference=Ctrl+Shift+R
patterns=none
preserve_alpha=none
previous_favorite_preset=.
previous_frame=none
previous_keyframe=none
previous_preset=/
rasterize_layer=none
Recording_Start_Recording_Macro=none
Recording_Stop_Recording_Macro=none
reload_preset_action=none
remove_layer=Shift+Delete
RenameCurrentLayer=F2
reselect=Ctrl+Shift+D
resizeimagetolayer=none
resizeimagetoselection=none
rotate_canvas_left=Ctrl+[
rotate_canvas_right=Ctrl+]
rotateImage180=none
rotateImageCCW90=none
rotateImageCW90=none
rotateimage=none
rotateLayer180=none
rotateLayerCCW90=none
rotateLayerCW90=none
rotatelayer=none
save_groups_as_images=none
save_incremental_backup=F4
save_incremental_version=Ctrl+Alt+S
save_node_as_image=none
select_all=Ctrl+A
Select Behind Blending Mode=Alt+Shift+Q
Select Clear Blending Mode=Alt+Shift+R
Select Color Blending Mode=Alt+Shift+C
Select Color Burn Blending Mode=Alt+Shift+B
Select Color Dodge Blending Mode=Alt+Shift+D
Select Darken Blending Mode=Alt+Shift+K
Select Difference Blending Mode=Alt+Shift+E
Select Dissolve Blending Mode=Alt+Shift+I
Select Exclusion Blending Mode=Alt+Shift+X
Select Hard Light Blending Mode=Alt+Shift+H
Select Hard Mix Blending Mode=Alt+Shift+L
Select Hue Blending Mode=Alt+Shift+U
selectionscale=none
Select Lighten Blending Mode=Alt+Shift+G
Select Linear Burn Blending Mode=Alt+Shift+A
Select Linear Dodge Blending Mode=Alt+Shift+W
Select Linear Light Blending Mode=Alt+Shift+J
Select Luminosity Blending Mode=Alt+Shift+Y
Select Multiply Blending Mode=Alt+Shift+M
Select Normal Blending Mode=Alt+Shift+N
select_opaque=none
selectopaque=none
Select Overlay Blending Mode=Alt+Shift+O
Select Pin Light Blending Mode=Alt+Shift+Z
Select Saturation Blending Mode=Alt+Shift+T
Select Screen Blending Mode=Alt+Shift+S
Select Soft Light Blending Mode=Alt+Shift+F
Select Vivid Light Blending Mode=Alt+Shift+V
separate=none
settings_active_author=none
shearimage=none
shearlayer=none
show_brush_presets=F6
show-global-selection-mask=none
show_in_timeline=none
showStatusBar=none
shrinkselection=none
smoothselection=none
split_alpha_into_mask=none
split_alpha_save_merged=none
split_alpha_write=none
stroke_shapes=none
tablet_debugger=Ctrl+Shift+T
toggle_display_selection=Ctrl+H
toggle_playback=none
toggle-selection-overlay-mode=none
trim_to_image=none
view_clear_perspective_grid=none
view_grid=Ctrl+Shift+'
view_newwindow=none
view_ruler=none
view_show_canvas_only=Tab
view_show_guides=none
view_snap_to_grid=Ctrl+Shift+;
view_toggle_assistant_previews=none
view_toggledockers=none
view_toggledockertitlebars=none
view_toggle_painting_assistants=none
view_toggle_perspective_grid=none
view_zoom_in=Ctrl++
view_zoom_out=Ctrl+-
vmirror_action=none
windows_cascade=none
windows_next=Ctrl+Tab
windows_previous=none
windows_tile=none
workspaces=none
zoom_to_100pct=Ctrl+0
diff --git a/krita/data/shortcuts/paint_tool_sai_compatible.shortcuts b/krita/data/shortcuts/paint_tool_sai_compatible.shortcuts
index 1e56d50f16..1e82c2d476 100644
--- a/krita/data/shortcuts/paint_tool_sai_compatible.shortcuts
+++ b/krita/data/shortcuts/paint_tool_sai_compatible.shortcuts
@@ -1,429 +1,428 @@
[Shortcuts]
activateNextLayer=PgUp
activatePreviousLayer=PgDown
add_blank_frame=none
add_duplicate_frame=none
add_new_adjustment_layer=none
add_new_clone_layer=none
add_new_file_layer=none
add_new_fill_layer=none
add_new_filter_mask=none
add_new_group_layer=none
add_new_paint_layer=none
add_new_selection_mask=none
add_new_shape_layer=none
add_new_transform_mask=none
add_new_transparency_mask=none
artistictext_anchor_end=none
artistictext_anchor_middle=none
artistictext_anchor_start=none
artistictext_convert_to_path=none
artistictext_detach_from_path=none
artistictext_font_bold=none
artistictext_font_italic=none
artistictext_subscript=none
artistictext_superscript=none
ArtisticTextTool=none
borderselection=none
BrushesAndStuff=none
brushslider2=none
brushslider3=none
canvassize=Ctrl+Alt+C
change_text_direction=Ctrl+Shift+D
clear=D
clones_array=none
colorrange=none
composite_actions=none
configure_bibliography=none
configure_section=none
convert_selection_to_shape=none
convert_shapes_to_vector_selection=none
convert_to_filter_mask=none
convert_to_paint_layer=none
convert-to-path=none
convert_to_selection_mask=none
convert_to_transparency_mask=none
convert_to_vector_selection=none
copy_merged=Ctrl+Shift+C
copy_selection_to_new_layer=Ctrl+Alt+J
copy_sharp=none
create_bundle=none
create_copy=none
CreateShapesTool=none
create_template=none
cut_selection_to_new_layer=Ctrl+Shift+J
cut_sharp=none
decrease_brush_size=[
decrease_opacity=I
delete_keyframe=none
deselect=Ctrl+Shift+A
dual=none
duplicatelayer=Ctrl+J
-edit_blacklist_cleanup=none
edit_copy=Ctrl+C
edit_cut=Ctrl+X
edit_deselect_all=Ctrl+Shift+A
EditLayerMetaData=none
edit_paste=Ctrl+V
edit_paste_text=none
edit_redo=Ctrl+Y
edit_select_all=Ctrl+A
edit_undo=Ctrl+Z
erase_action=E
featherselection=Shift+F6
file_close_all=Ctrl+Shift+W
file_close=none
file_documentinfo=none
file_export_animation=none
file_export_file=none
file_export_pdf=none
file_import_file=none
file_new=Ctrl+N
file_open=Ctrl+O
file_open_recent=F4
file_print=Ctrl+P
file_print_preview=none
file_quit=Ctrl+Q
file_save_as=Ctrl+Shift+S
file_save=Ctrl+S
fill_selection_background_color=Backspace
fill_selection_background_color_opacity=Ctrl+Backspace
fill_selection_foreground_color_opacity=Ctrl+Shift+Backspace
fill_selection_foreground_color=Shift+Backspace
fill_selection_pattern=none
fill_selection_pattern_opacity=none
filter_apply_again=Ctrl+F
first_frame=none
flatten_image=Ctrl+Shift+E
flatten_layer=none
fontsizedown=Ctrl+<
fontsizeup=Ctrl+>
format_alignblock=Ctrl+Alt+R
format_aligncenter=Ctrl+Alt+C
format_alignleft=none
format_alignright=Ctrl+Alt+R
format_backgroundcolor=none
format_bold=Ctrl+B
format_bulletlist=none
format_decreaseindent=none
format_endnotes=none
format_font=Ctrl+Alt+F
format_fontfamily=none
format_fontsize=none
format_footnotes=none
format_increaseindent=none
format_italic=Ctrl+I
format_numberlist=none
format_paragraph=Ctrl+Alt+P
format_strike=none
format_stylist=Ctrl+Alt+S
format_sub=Ctrl+Shift+B
format_super=Ctrl+Shift+P
format_tableofcontents=none
format_textcolor=none
format_underline=Ctrl+U
fullscreen=Ctrl+Shift+F
gmic=none
gradients=none
growselection=none
grow_to_fit_height=none
grow_to_fit_width=none
help_about_app=none
help_about_kde=none
help_contents=F1
help_report_bug=none
histogram=none
hmirror_action=none
image_color=none
imagecolorspaceconversion=none
image_properties=none
imagesize=Ctrl+Alt+I
imagesplit=none
import_layer_as_filter_mask=none
import_layer_as_paint_layer=none
import_layer_as_selection_mask=none
import_layer_as_transparency_mask=none
import_layer_from_file=none
import_resources=none
increase_brush_size=]
increase_opacity=O
insert_annotation=Ctrl+Shift+C
insert_autoendnote=none
insert_autofootnote=none
insert_bibliography=none
insert_bookmark=none
insert_citation=none
insert_configure_tableofcontents=none
insert_custom_bibliography=none
insert_index=Ctrl+T
insert_labeledendnote=none
insert_labeledfootnote=none
insert_link=none
insert_section=none
insert_specialchar=Alt+Shift+C
insert_tableofcontents=none
InteractionTool=none
invert=Ctrl+Shift+I
invoke_bookmark_handler=none
isolate_layer=none
KarbonCalligraphyTool=none
KarbonGradientTool=none
KarbonPatternTool=none
KisRulerAssistantTool=none
KisToolCrop=C
KisToolPath=P
KisToolPencil=none
KisToolPerspectiveGrid=none
KisToolPolygon=none
KisToolPolyline=none
KisToolSelectContiguous=W
KisToolSelectElliptical=J
KisToolSelectOutline=none
KisToolSelectPath=none
KisToolSelectPolygonal=none
KisToolSelectRectangular=M
KisToolSelectSimilar=none
KisToolTransform=Ctrl+T
KritaFill/KisToolFill=G
KritaFill/KisToolGradient=none
krita_filter_autocontrast=none
krita_filter_blur=none
krita_filter_bottom edge detections=none
krita_filter_brightnesscontrast=none
krita_filter_burn=none
krita_filter_colorbalance=Ctrl+B
krita_filter_colortoalpha=none
krita_filter_colortransfer=none
krita_filter_desaturate=Ctrl+Shift+U
krita_filter_dodge=none
krita_filter_emboss all directions=none
krita_filter_emboss horizontal and vertical=none
krita_filter_emboss horizontal only=none
krita_filter_emboss laplascian=none
krita_filter_emboss=none
krita_filter_emboss vertical only=none
krita_filter_gaussian blur=none
krita_filter_gaussiannoisereducer=none
krita_filter_hsvadjustment=Ctrl+U
krita_filter_indexcolors=none
krita_filter_invert=Ctrl+I
krita_filter_left edge detections=none
krita_filter_lens blur=none
krita_filter_levels=Ctrl+L
krita_filter_maximize=none
krita_filter_mean removal=none
krita_filter_minimize=none
krita_filter_motion blur=none
krita_filter_noise=none
krita_filter_oilpaint=none
krita_filter_perchannel=Ctrl+M
krita_filter_phongbumpmap=none
krita_filter_pixelize=none
krita_filter_posterize=none
krita_filter_raindrops=none
krita_filter_randompick=none
krita_filter_right edge detections=none
krita_filter_roundcorners=none
krita_filter_sharpen=none
krita_filter_smalltiles=none
krita_filter_sobel=none
krita_filter_top edge detections=none
krita_filter_unsharp=none
krita_filter_waveletnoisereducer=none
krita_filter_wave=none
KritaSelected/KisToolColorPicker=none
KritaShape/KisToolBrush=B
KritaShape/KisToolDyna=none
KritaShape/KisToolEllipse=none
KritaShape/KisToolLine=none
KritaShape/KisToolMeasure=none
KritaShape/KisToolMultiBrush=Q
KritaShape/KisToolRectangle=none
KritaShape/KisToolText=none
KritaTransform/KisToolMove=T
last_frame=none
layercolorspaceconversion=none
LayerGroupSwitcher/next=none
LayerGroupSwitcher/previous=none
layer_properties=none
layersize=none
layersplit=none
layer_style=none
lazy_frame=none
level_of_detail_mode=Shift+L
Macro_Open_Edit=none
Macro_Open_Play=none
mainToolBar=none
make_brush_color_darker=K
make_brush_color_lighter=L
manage_bookmarks=none
manage_bundles=none
merge_layer=Ctrl+E
mirror_actions=none
mirror_canvas=Shift+H
mirrorImageHorizontal=none
mirrorImageVertical=none
mirrorNodeX=none
mirrorNodeY=none
move_layer_down=Ctrl+PgDown
move_layer_up=Ctrl+PgDown
next_favorite_preset=,
next_frame=none
next_keyframe=none
nonbreaking_hyphen=Ctrl+Shift+-
nonbreaking_space=Ctrl+Space
object_align_horizontal_center=none
object_align_horizontal_left=none
object_align_horizontal_right=none
object_align_vertical_bottom=none
object_align_vertical_center=none
object_align_vertical_top=none
object_group=none
object_order_back=Ctrl+Shift+[
object_order_front=Ctrl+Shift+]
object_order_lower=Ctrl+[
object_order_raise=Ctrl+]
object_ungroup=none
offsetimage=none
offsetlayer=none
open_resources_directory=none
options_configure_keybinding=none
options_configure=none
options_configure_toolbars=none
paintops=none
paste_at=none
paste_new=Ctrl+Shift+N
path-break-point=none
path-break-segment=none
pathpoint-corner=none
pathpoint-curve=none
pathpoint-insert=Ins
pathpoint-join=J
pathpoint-line=none
pathpoint-merge=none
pathpoint-remove=Backspace
pathpoint-smooth=none
pathpoint-symmetric=none
pathsegment-curve=Shift+C
pathsegment-line=F
PathTool=none
patterns=none
preserve_alpha=none
previous_favorite_preset=.
previous_frame=none
previous_keyframe=none
previous_preset=/
rasterize_layer=none
Recording_Start_Recording_Macro=none
Recording_Stop_Recording_Macro=none
ReferencesTool=none
reload_preset_action=none
remove_layer=none
rename_composition=none
RenameCurrentLayer=F2
repaint=none
reselect=Ctrl+Shift+D
reset_fg_bg=D
resizeimagetolayer=none
resizeimagetoselection=none
ReviewTool=none
rotate_canvas_left=Ctrl+[
rotate_canvas_right=Ctrl+]
rotateImage180=none
rotateImageCCW90=none
rotateImageCW90=none
rotateimage=none
rotateLayer180=none
rotateLayerCCW90=none
rotateLayerCW90=none
rotatelayer=none
save_groups_as_images=none
save_incremental_backup=F4
save_incremental_version=Ctrl+Alt+S
save_node_as_image=none
select_all=Ctrl+A
Select Behind Blending Mode=Alt+Shift+Q
Select Clear Blending Mode=Alt+Shift+R
Select Color Blending Mode=Alt+Shift+C
Select Color Burn Blending Mode=Alt+Shift+B
Select Color Dodge Blending Mode=Alt+Shift+D
Select Darken Blending Mode=Alt+Shift+K
Select Difference Blending Mode=Alt+Shift+E
Select Dissolve Blending Mode=Alt+Shift+I
Select Exclusion Blending Mode=Alt+Shift+X
Select Hard Light Blending Mode=Alt+Shift+H
Select Hard Mix Blending Mode=Alt+Shift+L
Select Hue Blending Mode=Alt+Shift+U
selectionscale=none
Select Lighten Blending Mode=Alt+Shift+G
Select Linear Burn Blending Mode=Alt+Shift+A
Select Linear Dodge Blending Mode=Alt+Shift+W
Select Linear Light Blending Mode=Alt+Shift+J
Select Luminosity Blending Mode=Alt+Shift+Y
Select Multiply Blending Mode=Alt+Shift+M
Select Normal Blending Mode=Alt+Shift+N
select_opaque=none
selectopaque=none
Select Overlay Blending Mode=Alt+Shift+O
Select Pin Light Blending Mode=Alt+Shift+Z
Select Saturation Blending Mode=Alt+Shift+T
Select Screen Blending Mode=Alt+Shift+S
Select Soft Light Blending Mode=Alt+Shift+F
Select Vivid Light Blending Mode=Alt+Shift+V
separate=none
set_no_brush_smoothing=none
set_simple_brush_smoothing=none
set_stabilizer_brush_smoothing=none
settings_active_author=none
set_weighted_brush_smoothing=none
shearimage=none
shearlayer=none
show_brush_presets=none
show_color_history=H
show_color_selector=Shift+I
show_common_colors=U
show-global-selection-mask=none
show_in_timeline=none
show_minimal_shade_selector=Shift+N
show_mypaint_shade_selector=Shift+M
showStatusBar=none
shrinkselection=none
shrink_to_fit=none
smoothselection=none
soft_hyphen=none
split_alpha_into_mask=none
split_alpha_save_merged=none
split_alpha_write=none
split_sections=none
stroke_shapes=none
tablet_debugger=Ctrl+Shift+T
TextTool=none
toggle_assistant=Ctrl+Shift+L
toggle_display_selection=Ctrl+H
toggle_fg_bg=X
toggle_playback=none
toggle-selection-overlay-mode=none
trim_to_image=none
undo_polygon_selection=Shift+Z
VectorTool=none
view_clear_perspective_grid=none
view_grid=Ctrl+Shift+'
view_newwindow=none
view_ruler=none
view_show_canvas_only=none
view_show_guides=none
view_snap_to_grid=Ctrl+Shift+;
view_toggle_assistant_previews=none
view_toggledockers=Tab
view_toggledockertitlebars=none
view_toggle_painting_assistants=none
view_toggle_perspective_grid=none
view_zoom_in=none
view_zoom_out=none
vmirror_action=none
windows_cascade=none
windows_next=Ctrl+Tab
windows_previous=Ctrl+Shift+Tab
windows_tile=none
workspaces=none
zoom_to_100pct=Home
diff --git a/krita/data/shortcuts/photoshop_compatible.shortcuts b/krita/data/shortcuts/photoshop_compatible.shortcuts
index b731b25779..2c44a8f84a 100644
--- a/krita/data/shortcuts/photoshop_compatible.shortcuts
+++ b/krita/data/shortcuts/photoshop_compatible.shortcuts
@@ -1,429 +1,428 @@
-[Shortcuts]
+black[Shortcuts]
activateNextLayer=Alt+]
activatePreviousLayer=Alt+[
add_blank_frame=none
add_duplicate_frame=none
add_new_adjustment_layer=none
add_new_clone_layer=none
add_new_file_layer=none
add_new_fill_layer=none
add_new_filter_mask=none
add_new_group_layer=Ctrl+G
add_new_paint_layer=Ctrl+Shift+N
add_new_selection_mask=none
add_new_shape_layer=none
add_new_transform_mask=none
add_new_transparency_mask=none
artistictext_anchor_end=none
artistictext_anchor_middle=none
artistictext_anchor_start=none
artistictext_convert_to_path=none
artistictext_detach_from_path=none
artistictext_font_bold=none
artistictext_font_italic=none
artistictext_subscript=none
artistictext_superscript=none
ArtisticTextTool=none
borderselection=none
BrushesAndStuff=none
brushslider2=none
brushslider3=none
canvassize=Ctrl+Alt+C
change_text_direction=Ctrl+Shift+D
clear=Del
clones_array=none
colorrange=none
composite_actions=none
configure_bibliography=none
configure_section=none
convert_selection_to_shape=none
convert_shapes_to_vector_selection=none
convert_to_filter_mask=none
convert_to_paint_layer=none
convert-to-path=P
convert_to_selection_mask=none
convert_to_transparency_mask=none
convert_to_vector_selection=none
copy_merged=Ctrl+Shift+C
copy_selection_to_new_layer=Ctrl+Alt+J
copy_sharp=none
create_bundle=none
create_copy=none
CreateShapesTool=none
create_template=none
cut_selection_to_new_layer=Ctrl+Shift+J
cut_sharp=none
decrease_brush_size=[
decrease_opacity=;\s
delete_keyframe=none
deselect=Ctrl+D
dual=none
duplicatelayer=Ctrl+J
-edit_blacklist_cleanup=none
edit_copy=Ctrl+C
edit_cut=Ctrl+X
edit_deselect_all=Ctrl+Shift+A
EditLayerMetaData=none
edit_paste=Ctrl+V
edit_paste_text=none
edit_redo=Ctrl+Shift+Z
edit_select_all=Ctrl+A
edit_undo=Ctrl+Z
erase_action=E
featherselection=Shift+F6
file_close_all=Ctrl+Shift+W
file_close=Ctrl+W
file_documentinfo=Ctrl+Alt+Shift+I
file_export_animation=none
file_export_file=none
file_export_pdf=none
file_import_file=none
file_new=Ctrl+N
file_open=Ctrl+O
file_open_recent=F4
file_print=Ctrl+P
file_print_preview=none
file_quit=Ctrl+Q
file_save_as=Ctrl+Shift+S
file_save=Ctrl+S
fill_selection_background_color=Backspace
fill_selection_background_color_opacity=Ctrl+Backspace
fill_selection_foreground_color_opacity=Ctrl+Shift+Backspace
fill_selection_foreground_color=Shift+Backspace
fill_selection_pattern=none
fill_selection_pattern_opacity=none
filter_apply_again=Ctrl+F
first_frame=none
flatten_image=Ctrl+Shift+E
flatten_layer=none
fontsizedown=Ctrl+<
fontsizeup=Ctrl+>
format_alignblock=Ctrl+Alt+R
format_aligncenter=Ctrl+Alt+C
format_alignleft=none
format_alignright=Ctrl+Alt+R
format_backgroundcolor=none
format_bold=Ctrl+B
format_bulletlist=none
format_decreaseindent=none
format_endnotes=none
format_font=Ctrl+Alt+F
format_fontfamily=none
format_fontsize=none
format_footnotes=none
format_increaseindent=none
format_italic=Ctrl+I
format_numberlist=none
format_paragraph=Ctrl+Alt+P
format_strike=none
format_stylist=Ctrl+Alt+S
format_sub=Ctrl+Shift+B
format_super=Ctrl+Shift+P
format_tableofcontents=none
format_textcolor=none
format_underline=Ctrl+U
fullscreen=Ctrl+Shift+F
gmic=none
gradients=none
growselection=none
grow_to_fit_height=none
grow_to_fit_width=none
help_about_app=none
help_about_kde=none
help_contents=F1
help_report_bug=none
histogram=none
hmirror_action=none
image_color=none
imagecolorspaceconversion=none
image_properties=none
imagesize=Ctrl+Alt+I
imagesplit=none
import_layer_as_filter_mask=none
import_layer_as_paint_layer=none
import_layer_as_selection_mask=none
import_layer_as_transparency_mask=none
import_layer_from_file=none
import_resources=none
increase_brush_size=]
increase_opacity=O
insert_annotation=Ctrl+Shift+C
insert_autoendnote=none
insert_autofootnote=none
insert_bibliography=none
insert_bookmark=none
insert_citation=none
insert_configure_tableofcontents=none
insert_custom_bibliography=none
insert_index=Ctrl+T
insert_labeledendnote=none
insert_labeledfootnote=none
insert_link=none
insert_section=none
insert_specialchar=Alt+Shift+C
insert_tableofcontents=none
InteractionTool=none
invert=Ctrl+Shift+I
invoke_bookmark_handler=none
isolate_layer=none
KarbonCalligraphyTool=none
KarbonGradientTool=none
KarbonPatternTool=none
KisRulerAssistantTool=none
KisToolCrop=C
KisToolPath=none
KisToolPencil=none
KisToolPerspectiveGrid=none
KisToolPolygon=none
KisToolPolyline=none
KisToolSelectContiguous=none
KisToolSelectElliptical=Shift+M
KisToolSelectOutline=none
KisToolSelectPath=none
KisToolSelectPolygonal=none
KisToolSelectRectangular=M
KisToolSelectSimilar=none
KisToolTransform=Ctrl+T
KritaFill/KisToolFill=Shift+G
KritaFill/KisToolGradient=G
krita_filter_autocontrast=none
krita_filter_blur=none
krita_filter_bottom edge detections=none
krita_filter_brightnesscontrast=none
krita_filter_burn=none
krita_filter_colorbalance=Ctrl+B
krita_filter_colortoalpha=none
krita_filter_colortransfer=none
krita_filter_desaturate=Ctrl+Shift+U
krita_filter_dodge=none
krita_filter_emboss all directions=none
krita_filter_emboss horizontal and vertical=none
krita_filter_emboss horizontal only=none
krita_filter_emboss laplascian=none
krita_filter_emboss=none
krita_filter_emboss vertical only=none
krita_filter_gaussian blur=none
krita_filter_gaussiannoisereducer=none
krita_filter_hsvadjustment=Ctrl+U
krita_filter_indexcolors=none
krita_filter_invert=Ctrl+I
krita_filter_left edge detections=none
krita_filter_lens blur=none
krita_filter_levels=Ctrl+L
krita_filter_maximize=none
krita_filter_mean removal=none
krita_filter_minimize=none
krita_filter_motion blur=none
krita_filter_noise=none
krita_filter_oilpaint=none
krita_filter_perchannel=Ctrl+M
krita_filter_phongbumpmap=none
krita_filter_pixelize=none
krita_filter_posterize=none
krita_filter_raindrops=none
krita_filter_randompick=none
krita_filter_right edge detections=none
krita_filter_roundcorners=none
krita_filter_sharpen=none
krita_filter_smalltiles=none
krita_filter_sobel=none
krita_filter_top edge detections=none
krita_filter_unsharp=none
krita_filter_waveletnoisereducer=none
krita_filter_wave=none
KritaSelected/KisToolColorPicker=I
KritaShape/KisToolBrush=B
KritaShape/KisToolDyna=none
KritaShape/KisToolEllipse=none
KritaShape/KisToolLine=none
KritaShape/KisToolMeasure=none
KritaShape/KisToolMultiBrush=Q
KritaShape/KisToolRectangle=none
KritaShape/KisToolText=T
KritaTransform/KisToolMove=V
last_frame=none
layercolorspaceconversion=none
LayerGroupSwitcher/next=none
LayerGroupSwitcher/previous=none
layer_properties=none
layersize=none
layersplit=none
layer_style=none
lazy_frame=none
level_of_detail_mode=Shift+L
Macro_Open_Edit=none
Macro_Open_Play=none
mainToolBar=none
make_brush_color_darker=K
make_brush_color_lighter=L
manage_bookmarks=none
manage_bundles=none
merge_layer=Ctrl+E
mirror_actions=none
mirror_canvas=;\s
mirrorImageHorizontal=none
mirrorImageVertical=none
mirrorNodeX=none
mirrorNodeY=none
move_layer_down=Ctrl+[
move_layer_up=Ctrl+]
next_favorite_preset=,
next_frame=none
next_keyframe=none
nonbreaking_hyphen=Ctrl+Shift+-
nonbreaking_space=Ctrl+Space
object_align_horizontal_center=none
object_align_horizontal_left=none
object_align_horizontal_right=none
object_align_vertical_bottom=none
object_align_vertical_center=none
object_align_vertical_top=none
object_group=none
object_order_back=Ctrl+Shift+[
object_order_front=Ctrl+Shift+]
object_order_lower=none
object_order_raise=none
object_ungroup=none
offsetimage=none
offsetlayer=none
open_resources_directory=none
options_configure_keybinding=none
options_configure=none
options_configure_toolbars=none
paintops=none
paste_at=none
paste_new=none
path-break-point=none
path-break-segment=none
pathpoint-corner=none
pathpoint-curve=none
pathpoint-insert=Ins
pathpoint-join=J
pathpoint-line=none
pathpoint-merge=none
pathpoint-remove=Backspace
pathpoint-smooth=none
pathpoint-symmetric=none
pathsegment-curve=Shift+C
pathsegment-line=F
PathTool=none
patterns=none
preserve_alpha=none
previous_favorite_preset=.
previous_frame=none
previous_keyframe=none
previous_preset=/
rasterize_layer=none
Recording_Start_Recording_Macro=none
Recording_Stop_Recording_Macro=none
ReferencesTool=none
reload_preset_action=none
remove_layer=none
rename_composition=none
RenameCurrentLayer=F2
repaint=none
reselect=Ctrl+Shift+D
reset_fg_bg=D
resizeimagetolayer=none
resizeimagetoselection=none
ReviewTool=none
rotate_canvas_left=none
rotate_canvas_right=none
rotateImage180=none
rotateImageCCW90=none
rotateImageCW90=none
rotateimage=none
rotateLayer180=none
rotateLayerCCW90=none
rotateLayerCW90=none
rotatelayer=none
save_groups_as_images=none
save_incremental_backup=F4
save_incremental_version=Ctrl+Alt+S
save_node_as_image=none
select_all=Ctrl+A
Select Behind Blending Mode=Alt+Shift+Q
Select Clear Blending Mode=Alt+Shift+R
Select Color Blending Mode=Alt+Shift+C
Select Color Burn Blending Mode=Alt+Shift+B
Select Color Dodge Blending Mode=Alt+Shift+D
Select Darken Blending Mode=Alt+Shift+K
Select Difference Blending Mode=Alt+Shift+E
Select Dissolve Blending Mode=Alt+Shift+I
Select Exclusion Blending Mode=Alt+Shift+X
Select Hard Light Blending Mode=Alt+Shift+H
Select Hard Mix Blending Mode=Alt+Shift+L
Select Hue Blending Mode=Alt+Shift+U
selectionscale=none
Select Lighten Blending Mode=Alt+Shift+G
Select Linear Burn Blending Mode=Alt+Shift+A
Select Linear Dodge Blending Mode=Alt+Shift+W
Select Linear Light Blending Mode=Alt+Shift+J
Select Luminosity Blending Mode=Alt+Shift+Y
Select Multiply Blending Mode=Alt+Shift+M
Select Normal Blending Mode=Alt+Shift+N
select_opaque=none
selectopaque=none
Select Overlay Blending Mode=Alt+Shift+O
Select Pin Light Blending Mode=Alt+Shift+Z
Select Saturation Blending Mode=Alt+Shift+T
Select Screen Blending Mode=Alt+Shift+S
Select Soft Light Blending Mode=Alt+Shift+F
Select Vivid Light Blending Mode=Alt+Shift+V
separate=none
set_no_brush_smoothing=none
set_simple_brush_smoothing=none
set_stabilizer_brush_smoothing=none
settings_active_author=none
set_weighted_brush_smoothing=none
shearimage=none
shearlayer=none
show_brush_presets=F6
show_color_history=H
show_color_selector=Shift+I
show_common_colors=U
show-global-selection-mask=none
show_in_timeline=none
show_minimal_shade_selector=Shift+N
show_mypaint_shade_selector=;\s
showStatusBar=none
shrinkselection=none
shrink_to_fit=none
smoothselection=none
soft_hyphen=none
split_alpha_into_mask=none
split_alpha_save_merged=none
split_alpha_write=none
split_sections=none
stroke_shapes=none
tablet_debugger=Ctrl+Shift+T
TextTool=none
toggle_assistant=Ctrl+Shift+L
toggle_display_selection=Ctrl+H
toggle_fg_bg=X
toggle_playback=none
toggle-selection-overlay-mode=none
trim_to_image=none
undo_polygon_selection=Shift+Z
VectorTool=none
view_clear_perspective_grid=none
view_grid=Ctrl+Shift+'
view_newwindow=none
view_ruler=none
view_show_canvas_only=Tab
view_show_guides=none
view_snap_to_grid=Ctrl+Shift+;
view_toggle_assistant_previews=none
view_toggledockers=none
view_toggledockertitlebars=none
view_toggle_painting_assistants=none
view_toggle_perspective_grid=none
view_zoom_in=Ctrl++
view_zoom_out=Ctrl+-
vmirror_action=none
windows_cascade=none
windows_next=none
windows_previous=none
windows_tile=none
workspaces=none
zoom_to_100pct=Ctrl+0
diff --git a/krita/data/shortcuts/tablet_pro.shortcuts b/krita/data/shortcuts/tablet_pro.shortcuts
index f590f223ff..e5947d28c5 100644
--- a/krita/data/shortcuts/tablet_pro.shortcuts
+++ b/krita/data/shortcuts/tablet_pro.shortcuts
@@ -1,467 +1,466 @@
[Shortcuts]
activateNextLayer=PgUp
activatePreviousLayer=PgDown
add_blank_frame=none
add_duplicate_frame=none
add_new_adjustment_layer=none
add_new_clone_layer=none
add_new_file_layer=none
add_new_fill_layer=none
add_new_filter_mask=none
add_new_group_layer=none
add_new_paint_layer=Ins
add_new_selection_mask=none
add_new_shape_layer=none
add_new_transform_mask=none
add_new_transparency_mask=none
artistictext_anchor_end=none
artistictext_anchor_middle=none
artistictext_anchor_start=none
artistictext_convert_to_path=none
artistictext_detach_from_path=none
artistictext_font_bold=none
artistictext_font_italic=none
artistictext_subscript=none
artistictext_superscript=none
ArtisticTextTool=none
borderselection=none
BrushesAndStuff=none
brushslider1=none
brushslider2=none
brushslider3=none
canvassize=Ctrl+Alt+C
change_text_direction=Ctrl+Shift+D
clear=Del
clones_array=none
colorrange=none
composite_actions=none
configure_bibliography=none
configure_section=none
convert_selection_to_shape=none
convert_shapes_to_vector_selection=none
convert_to_filter_mask=none
convert_to_paint_layer=none
convert-to-path=P
convert_to_selection_mask=none
convert_to_transparency_mask=none
convert_to_vector_selection=none
copy_layer_clipboard=none
copy_merged=Ctrl+Shift+C
copy_selection_to_new_layer=Ctrl+Alt+J
copy_sharp=none
create_bundle=none
create_copy=none
create_quick_clipping_group=Ctrl+Shift+G
create_quick_group=Ctrl+G
CreateShapesTool=none
create_template=none
cut_layer_clipboard=none
cut_selection_to_new_layer=Ctrl+Shift+J
cut_sharp=none
decrease_brush_size=[
decrease_opacity=none
delete_keyframe=none
deselect=Ctrl+Shift+A
drop_frames=none
dual=none
duplicatelayer=Ctrl+J
-edit_blacklist_cleanup=none
edit_copy=Ctrl+C
edit_cut=Ctrl+X
edit_deselect_all=Ctrl+Shift+A
EditLayerMetaData=none
edit_paste=Ctrl+V
edit_paste_text=none
edit_redo=Ctrl+Shift+Z
edit_select_all=Ctrl+A
edit_undo=Ctrl+Z
erase_action=E
featherselection=Shift+F6
file_close_all=Ctrl+Shift+W
file_close=Ctrl+W
file_documentinfo=none
file_export_animation=none
file_export_file=none
file_export_pdf=none
file_import_animation=none
file_import_file=none
file_new=Ctrl+N
file_open=Ctrl+O
file_open_recent=none
file_quit=Ctrl+Q
file_save_as=Ctrl+Shift+S
file_save=Ctrl+S
fill_selection_background_color=Backspace
fill_selection_background_color_opacity=Ctrl+Backspace
fill_selection_foreground_color_opacity=Ctrl+Shift+Backspace
fill_selection_foreground_color=Shift+Backspace
fill_selection_pattern=none
fill_selection_pattern_opacity=none
filter_apply_again=Ctrl+F
first_frame=none
flatten_image=Ctrl+Shift+E
flatten_layer=none
fontsizedown=Ctrl+<
fontsizeup=Ctrl+>
format_alignblock=Ctrl+Alt+R
format_aligncenter=Ctrl+Alt+C
format_alignleft=none
format_alignright=Ctrl+Alt+R
format_backgroundcolor=none
format_bold=Ctrl+B
format_bulletlist=none
format_decreaseindent=none
format_endnotes=none
format_font=Ctrl+Alt+F
format_fontfamily=none
format_fontsize=none
format_footnotes=none
format_increaseindent=none
format_italic=Ctrl+I
format_numberlist=none
format_paragraph=Ctrl+Alt+P
format_strike=none
format_stylist=Ctrl+Alt+S
format_sub=Ctrl+Shift+B
format_super=Ctrl+Shift+P
format_tableofcontents=none
format_textcolor=none
format_underline=Ctrl+U
gmic=none
gradients=none
growselection=none
grow_to_fit_height=none
grow_to_fit_width=none
help_about_app=none
help_about_kde=none
help_contents=F1
help_report_bug=none
histogram=none
hmirror_action=none
image_color=none
imagecolorspaceconversion=none
image_properties=none
imagesize=Ctrl+Alt+I
imagesplit=none
import_layer_as_filter_mask=none
import_layer_as_paint_layer=none
import_layer_as_selection_mask=none
import_layer_as_transparency_mask=none
import_layer_from_file=none
increase_brush_size=]
increase_opacity=O
insert_annotation=Ctrl+Shift+C
insert_autoendnote=none
insert_autofootnote=none
insert_bibliography=none
insert_bookmark=none
insert_citation=none
insert_configure_tableofcontents=none
insert_custom_bibliography=none
insert_index=Ctrl+T
insert_labeledendnote=none
insert_labeledfootnote=none
insert_link=none
insert_section=none
insert_specialchar=Alt+Shift+C
insert_tableofcontents=none
InteractionTool=none
invert=Ctrl+Shift+I
invoke_bookmark_handler=none
isolate_layer=none
KarbonCalligraphyTool=none
KarbonGradientTool=none
KarbonPatternTool=none
KisRulerAssistantTool=none
KisToolCrop=C
KisToolPath=none
KisToolPencil=none
KisToolPolygon=none
KisToolPolyline=none
KisToolSelectContiguous=none
KisToolSelectElliptical=Shift+M
KisToolSelectOutline=L
KisToolSelectPath=none
KisToolSelectPolygonal=none
KisToolSelectRectangular=M
KisToolSelectSimilar=none
KisToolTransform=Ctrl+T
KritaFill/KisToolFill=Shift+G
KritaFill/KisToolGradient=G
krita_filter_autocontrast=none
krita_filter_blur=none
krita_filter_bottom edge detections=none
krita_filter_brightnesscontrast=none
krita_filter_burn=none
krita_filter_colorbalance=Ctrl+B
krita_filter_colortoalpha=none
krita_filter_colortransfer=none
krita_filter_desaturate=Ctrl+Shift+U
krita_filter_dodge=none
krita_filter_emboss all directions=none
krita_filter_emboss horizontal and vertical=none
krita_filter_emboss horizontal only=none
krita_filter_emboss laplascian=none
krita_filter_emboss=none
krita_filter_emboss vertical only=none
krita_filter_gaussian blur=none
krita_filter_gaussiannoisereducer=none
krita_filter_hsvadjustment=Ctrl+U
krita_filter_indexcolors=none
krita_filter_invert=Ctrl+I
krita_filter_left edge detections=none
krita_filter_lens blur=none
krita_filter_levels=Ctrl+L
krita_filter_maximize=none
krita_filter_mean removal=none
krita_filter_minimize=none
krita_filter_motion blur=none
krita_filter_noise=none
krita_filter_oilpaint=none
krita_filter_perchannel=Ctrl+M
krita_filter_phongbumpmap=none
krita_filter_pixelize=none
krita_filter_posterize=none
krita_filter_raindrops=none
krita_filter_randompick=none
krita_filter_right edge detections=none
krita_filter_roundcorners=none
krita_filter_sharpen=none
krita_filter_smalltiles=none
krita_filter_sobel=none
krita_filter_top edge detections=none
krita_filter_unsharp=none
krita_filter_waveletnoisereducer=none
krita_filter_wave=none
KritaSelected/KisToolColorPicker=I
KritaShape/KisToolBrush=B
KritaShape/KisToolDyna=none
KritaShape/KisToolEllipse=none
KritaShape/KisToolLine=none
KritaShape/KisToolMeasure=none
KritaShape/KisToolMultiBrush=Q
KritaShape/KisToolRectangle=none
KritaShape/KisToolText=T
KritaTransform/KisToolMove=V
last_frame=none
layercolorspaceconversion=none
LayerGroupSwitcher/next=none
LayerGroupSwitcher/previous=none
layer_properties=none
layersize=none
layersplit=none
layer_style=none
lazy_frame=none
level_of_detail_mode=Shift+L
Macro_Open_Edit=none
Macro_Open_Play=none
mainToolBar=none
make_brush_color_bluer=none
make_brush_color_darker=K
make_brush_color_desaturated=none
make_brush_color_greener=none
make_brush_color_lighter=none
make_brush_color_redder=none
make_brush_color_saturated=none
make_brush_color_yellower=none
manage_bookmarks=none
manage_bundles=none
merge_layer=Ctrl+E
mirror_actions=none
mirror_canvas=M
mirrorImageHorizontal=none
mirrorImageVertical=none
mirrorNodeX=none
mirrorNodeY=none
move_layer_down=none
move_layer_up=none
movetool-move-down=Down
movetool-move-down-more=Shift+Down
movetool-move-left=Left
movetool-move-left-more=Shift+Left
movetool-move-right-more=Shift+Right
movetool-move-right=Right
movetool-move-up-more=Shift+Up
movetool-move-up=Up
next_favorite_preset=,
next_frame=none
next_keyframe=none
nonbreaking_hyphen=Ctrl+Shift+-
nonbreaking_space=Ctrl+Space
object_align_horizontal_center=none
object_align_horizontal_left=none
object_align_horizontal_right=none
object_align_vertical_bottom=none
object_align_vertical_center=none
object_align_vertical_top=none
object_group=none
object_order_back=Ctrl+Shift+[
object_order_front=Ctrl+Shift+]
object_order_lower=none
object_order_raise=none
object_ungroup=none
offsetimage=none
offsetlayer=none
open_resources_directory=none
options_configure=none
options_configure_toolbars=none
paintops=none
paste_at=none
paste_layer_from_clipboard=none
paste_new=Ctrl+Shift+N
path-break-point=none
path-break-segment=none
pathpoint-corner=none
pathpoint-curve=none
pathpoint-insert=none
pathpoint-join=J
pathpoint-line=none
pathpoint-merge=none
pathpoint-remove=Backspace
pathpoint-smooth=none
pathpoint-symmetric=none
pathsegment-curve=Shift+C
pathsegment-line=F
PathTool=none
patterns=none
preserve_alpha=none
previous_favorite_preset=.
previous_frame=none
previous_keyframe=none
previous_preset=/
quick_ungroup=Ctrl+Alt+G
rasterize_layer=none
Recording_Start_Recording_Macro=none
Recording_Stop_Recording_Macro=none
ReferencesTool=none
reload_preset_action=none
remove_layer=Shift+Del
rename_composition=none
RenameCurrentLayer=F2
repaint=none
reselect=Ctrl+Shift+D
reset_canvas_rotation=none
reset_fg_bg=D
resizeimagetolayer=none
resizeimagetoselection=none
ReviewTool=none
rotate_canvas_left=Ctrl+[
rotate_canvas_right=Ctrl+]
rotateImage180=none
rotateImageCCW90=none
rotateImageCW90=none
rotateimage=none
rotateLayer180=none
rotateLayerCCW90=none
rotateLayerCW90=none
rotatelayer=none
rulers_track_mouse=none
save_groups_as_images=none
save_incremental_backup=F4
save_incremental_version=Ctrl+Alt+S
save_node_as_image=none
select_all=Ctrl+A
select_all_layers=none
Select Behind Blending Mode=Alt+Shift+Q
Select Clear Blending Mode=Alt+Shift+R
Select Color Blending Mode=Alt+Shift+C
Select Color Burn Blending Mode=Alt+Shift+B
Select Color Dodge Blending Mode=Alt+Shift+D
Select Darken Blending Mode=Alt+Shift+K
Select Difference Blending Mode=Alt+Shift+E
Select Dissolve Blending Mode=Alt+Shift+I
Select Exclusion Blending Mode=Alt+Shift+X
Select Hard Light Blending Mode=Alt+Shift+H
Select Hard Mix Blending Mode=Alt+Shift+L
Select Hue Blending Mode=Alt+Shift+U
select_invisible_layers=none
selectionscale=none
Select Lighten Blending Mode=Alt+Shift+G
Select Linear Burn Blending Mode=Alt+Shift+A
Select Linear Dodge Blending Mode=Alt+Shift+W
Select Linear Light Blending Mode=Alt+Shift+J
select_locked_layers=none
Select Luminosity Blending Mode=Alt+Shift+Y
Select Multiply Blending Mode=Alt+Shift+M
Select Normal Blending Mode=Alt+Shift+N
select_opaque=none
selectopaque=none
Select Overlay Blending Mode=Alt+Shift+O
Select Pin Light Blending Mode=Alt+Shift+Z
Select Saturation Blending Mode=Alt+Shift+T
Select Screen Blending Mode=Alt+Shift+S
Select Soft Light Blending Mode=Alt+Shift+F
select_unlocked_layers=none
select_visible_layers=none
Select Vivid Light Blending Mode=Alt+Shift+V
separate=none
set_no_brush_smoothing=none
set_simple_brush_smoothing=none
set_stabilizer_brush_smoothing=none
settings_active_author=none
set_weighted_brush_smoothing=none
shearimage=none
shearlayer=none
shift_brush_color_clockwise=none
shift_brush_color_counter_clockwise=none
show_brush_editor=F5
show_brush_presets=F6
show_color_history=H
show_color_selector=Shift+I
show_common_colors=U
show-global-selection-mask=none
show_in_timeline=none
show_minimal_shade_selector=Shift+N
show_mypaint_shade_selector=none
show_snap_options_popup=Shift+S
showStatusBar=none
show_tool_options=\\
shrinkselection=none
shrink_to_fit=none
smoothselection=none
soft_hyphen=none
split_alpha_into_mask=none
split_alpha_save_merged=none
split_alpha_write=none
split_sections=none
stroke_shapes=none
switch_application_language=none
tablet_debugger=Ctrl+Shift+T
TextTool=none
toggle_assistant=Ctrl+Shift+L
toggle_display_selection=Ctrl+H
toggle_fg_bg=X
toggle_onion_skin=none
toggle_playback=none
toggle-selection-overlay-mode=none
trim_to_image=none
undo_polygon_selection=Shift+Z
VectorTool=none
view_grid=Ctrl+Shift+'
view_lock_guides=none
view_newwindow=none
view_ruler=none
view_show_canvas_only=Tab
view_show_guides=none
view_snap_bounding_box=none
view_snap_extension=none
view_snap_image_bounds=none
view_snap_image_center=none
view_snap_intersection=none
view_snap_node=none
view_snap_orthogonal=none
view_snap_to_grid=Ctrl+Shift+;
view_snap_to_guides=none
view_toggle_assistant_previews=none
view_toggledockers=none
view_toggledockertitlebars=none
view_toggle_painting_assistants=none
view_zoom_in=Ctrl++
view_zoom_out=Ctrl+-
vmirror_action=none
windows_cascade=none
windows_next=Ctrl+Tab
windows_previous=none
windows_tile=none
workspaces=none
zoom_to_100pct=Ctrl+0
diff --git a/krita/krita.action b/krita/krita.action
index 1e61343a76..73247b578d 100644
--- a/krita/krita.action
+++ b/krita/krita.action
@@ -1,3694 +1,3682 @@
<?xml version="1.0" encoding="UTF-8"?>
<ActionCollection version="2" name="Krita">
<Actions category="General">
<text>General</text>
<Action name="open_resources_directory">
<icon></icon>
<text>Open Resources Folder</text>
<whatsThis>Opens a file browser at the location Krita saves resources such as brushes to.</whatsThis>
<toolTip>Opens a file browser at the location Krita saves resources such as brushes to.</toolTip>
<iconText>Open Resources Folder</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
- <Action name="edit_blacklist_cleanup">
- <icon></icon>
- <text>Cleanup removed files...</text>
- <whatsThis></whatsThis>
- <toolTip>Cleanup removed files</toolTip>
- <iconText>Cleanup removed files</iconText>
- <activationFlags>0</activationFlags>
- <activationConditions>0</activationConditions>
- <shortcut></shortcut>
- <isCheckable>false</isCheckable>
- <statusTip></statusTip>
- </Action>
<Action name="windows_cascade">
<icon></icon>
<text>C&amp;ascade</text>
<whatsThis></whatsThis>
<toolTip>Cascade</toolTip>
<iconText>Cascade</iconText>
<activationFlags>10</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="windows_tile">
<icon></icon>
<text>&amp;Tile</text>
<whatsThis></whatsThis>
<toolTip>Tile</toolTip>
<iconText>Tile</iconText>
<activationFlags>10</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="create_bundle">
<icon></icon>
<text>Create Resource Bundle...</text>
<whatsThis></whatsThis>
<toolTip>Create Resource Bundle</toolTip>
<iconText>Create Resource Bundle</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mainToolBar">
<icon></icon>
<text>Show File Toolbar</text>
<whatsThis></whatsThis>
<toolTip>Show File Toolbar</toolTip>
<iconText>Show File Toolbar</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="show_color_selector">
<icon></icon>
<text>Show color selector</text>
<whatsThis></whatsThis>
<toolTip>Show color selector</toolTip>
<iconText>Show color selector</iconText>
<shortcut>Shift+I</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="show_mypaint_shade_selector">
<icon></icon>
<text>Show MyPaint shade selector</text>
<whatsThis></whatsThis>
<toolTip>Show MyPaint shade selector</toolTip>
<iconText>Show MyPaint shade selector</iconText>
<shortcut>Shift+M</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="show_minimal_shade_selector">
<icon></icon>
<text>Show minimal shade selector</text>
<whatsThis></whatsThis>
<toolTip>Show minimal shade selector</toolTip>
<iconText>Show minimal shade selector</iconText>
<shortcut>Shift+N</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="show_color_history">
<icon></icon>
<text>Show color history</text>
<whatsThis></whatsThis>
<toolTip>Show color history</toolTip>
<iconText>Show color history</iconText>
<shortcut>H</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="show_common_colors">
<icon></icon>
<text>Show common colors</text>
<whatsThis></whatsThis>
<toolTip>Show common colors</toolTip>
<iconText>Show common colors</iconText>
<shortcut>U</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="show_tool_options">
<icon></icon>
<text>Show Tool Options</text>
<whatsThis></whatsThis>
<toolTip>Show Tool Options</toolTip>
<iconText>Show Tool Options</iconText>
<shortcut>\</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="show_brush_editor">
<icon></icon>
<text>Show Brush Editor</text>
<whatsThis></whatsThis>
<toolTip>Show Brush Editor</toolTip>
<iconText>Show Brush Editor</iconText>
<shortcut>F5</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="show_brush_presets">
<icon></icon>
<text>Show Brush Presets</text>
<whatsThis></whatsThis>
<toolTip>Show Brush Presets</toolTip>
<iconText>Show Brush Presets</iconText>
<shortcut>F6</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="tablet_debugger">
<icon></icon>
<text>Toggle Tablet Debugger</text>
<whatsThis></whatsThis>
<toolTip>Toggle Tablet Debugger</toolTip>
<iconText>Toggle Tablet Debugger</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+Shift+T</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="buginfo">
<icon></icon>
<text>Show Krita log for bug reports.</text>
<whatsThis></whatsThis>
<toolTip>Show Krita log for bug reports.</toolTip>
<iconText>Show Krita log for bug reports.</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="sysinfo">
<icon></icon>
<text>Show system information for bug reports.</text>
<whatsThis></whatsThis>
<toolTip>Show system information for bug reports.</toolTip>
<iconText>Show system information for bug reports.</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="rename_composition">
<icon></icon>
<text>Rename Composition...</text>
<whatsThis></whatsThis>
<toolTip>Rename Composition</toolTip>
<iconText>Rename Composition</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="update_composition">
<icon></icon>
<text>Update Composition</text>
<whatsThis></whatsThis>
<toolTip>Update Composition</toolTip>
<iconText>Update Composition</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="ruler_pixel_multiple2">
<icon></icon>
<text>Use multiple of 2 for pixel scale</text>
<whatsThis>Use multiple of 2 for pixel scale</whatsThis>
<toolTip>Use multiple of 2 for pixel scale</toolTip>
<iconText>Use multiple of 2 for pixel scale</iconText>
<activationFlags>1</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="invert_selection">
<icon></icon>
<text>&amp;Invert Selection</text>
<whatsThis></whatsThis>
<toolTip>Invert current selection</toolTip>
<iconText>Invert Selection</iconText>
<activationFlags>10000000000</activationFlags>
<activationConditions>100</activationConditions>
<shortcut>Ctrl+Shift+I</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="create_snapshot">
<icon></icon>
<text>Create Snapshot</text>
<whatsThis></whatsThis>
<toolTip>Create Snapshot</toolTip>
<iconText></iconText>
<activationFlags>1</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="switchto_snapshot">
<icon></icon>
<text>Switch to Selected Snapshot</text>
<whatsThis></whatsThis>
<toolTip>Switch to selected snapshot</toolTip>
<iconText></iconText>
<activationFlags>1</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="remove_snapshot">
<icon></icon>
<text>Remove Selected Snapshot</text>
<whatsThis></whatsThis>
<toolTip>Remove Selected Snapshot</toolTip>
<iconText></iconText>
<activationFlags>1</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
</Actions>
<Actions category="Painting">
<text>Painting</text>
<Action name="make_brush_color_lighter">
<icon>lightness-increase</icon>
<text>Make brush color lighter</text>
<whatsThis></whatsThis>
<toolTip>Make brush color lighter</toolTip>
<iconText>Make brush color lighter</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>L</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="make_brush_color_darker">
<icon>lightness-decrease</icon>
<text>Make brush color darker</text>
<whatsThis></whatsThis>
<toolTip>Make brush color darker</toolTip>
<iconText>Make brush color darker</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>K</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="make_brush_color_saturated">
<icon></icon>
<text>Make brush color more saturated</text>
<whatsThis></whatsThis>
<toolTip>Make brush color more saturated</toolTip>
<iconText>Make brush color more saturated</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="make_brush_color_desaturated">
<icon></icon>
<text>Make brush color more desaturated</text>
<whatsThis></whatsThis>
<toolTip>Make brush color more desaturated</toolTip>
<iconText>Make brush color more desaturated</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="shift_brush_color_clockwise">
<icon></icon>
<text>Shift brush color hue clockwise</text>
<whatsThis></whatsThis>
<toolTip>Shift brush color hue clockwise</toolTip>
<iconText>Shift brush color hue clockwise</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="shift_brush_color_counter_clockwise">
<icon></icon>
<text>Shift brush color hue counter-clockwise</text>
<whatsThis></whatsThis>
<toolTip>Shift brush color hue counter-clockwise</toolTip>
<iconText>Shift brush color hue counter-clockwise</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="make_brush_color_redder">
<icon></icon>
<text>Make brush color more red</text>
<whatsThis></whatsThis>
<toolTip>Make brush color more red</toolTip>
<iconText>Make brush color more red</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="make_brush_color_greener">
<icon></icon>
<text>Make brush color more green</text>
<whatsThis></whatsThis>
<toolTip>Make brush color more green</toolTip>
<iconText>Make brush color more green</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="make_brush_color_bluer">
<icon></icon>
<text>Make brush color more blue</text>
<whatsThis></whatsThis>
<toolTip>Make brush color more blue</toolTip>
<iconText>Make brush color more blue</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="make_brush_color_yellower">
<icon></icon>
<text>Make brush color more yellow</text>
<whatsThis></whatsThis>
<toolTip>Make brush color more yellow</toolTip>
<iconText>Make brush color more yellow</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="increase_opacity">
<icon>opacity-increase</icon>
<text>Increase opacity</text>
<whatsThis></whatsThis>
<toolTip>Increase opacity</toolTip>
<iconText>Increase opacity</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>O</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="decrease_opacity">
<icon>opacity-decrease</icon>
<text>Decrease opacity</text>
<whatsThis></whatsThis>
<toolTip>Decrease opacity</toolTip>
<iconText>Decrease opacity</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>I</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="erase_action">
<icon>draw-eraser</icon>
<text>Set eraser mode</text>
<whatsThis></whatsThis>
<toolTip>Set eraser mode</toolTip>
<iconText>Set eraser mode</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>E</shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="reload_preset_action">
<icon>view-refresh</icon>
<text>Reload Original Preset</text>
<whatsThis></whatsThis>
<toolTip>Reload Original Preset</toolTip>
<iconText>Reload Original Preset</iconText>
<activationFlags>10000</activationFlags>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="preserve_alpha">
<icon>transparency-unlocked</icon>
<text>Preserve Alpha</text>
<whatsThis></whatsThis>
<toolTip>Preserve Alpha</toolTip>
<iconText>Preserve Alpha</iconText>
<activationFlags>10000</activationFlags>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="disable_pressure">
<icon>transform_icons_penPressure</icon>
<text>Use Pen Pressure</text>
<whatsThis></whatsThis>
<toolTip>Use Pen Pressure</toolTip>
<iconText>Use Pen Pressure</iconText>
<activationFlags>10000</activationFlags>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="hmirror_action">
<icon>symmetry-horizontal</icon>
<text>Horizontal Mirror Tool</text>
<whatsThis></whatsThis>
<toolTip>Horizontal Mirror Tool</toolTip>
<iconText>Horizontal Mirror Tool</iconText>
<activationFlags>0</activationFlags>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="vmirror_action">
<icon>symmetry-vertical</icon>
<text>Vertical Mirror Tool</text>
<whatsThis></whatsThis>
<toolTip>Vertical Mirror Tool</toolTip>
<iconText>Vertical Mirror Tool</iconText>
<activationFlags>0</activationFlags>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mirrorX-hideDecorations">
<icon></icon>
<text>Hide Mirror X Line</text>
<whatsThis></whatsThis>
<toolTip>Hide Mirror X Line</toolTip>
<iconText>Hide Mirror X Line</iconText>
<activationFlags>10000</activationFlags>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mirrorY-hideDecorations">
<icon></icon>
<text>Hide Mirror Y Line</text>
<whatsThis></whatsThis>
<toolTip>Hide Mirror Y Line</toolTip>
<iconText>Hide Mirror Y Line</iconText>
<activationFlags>10000</activationFlags>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mirrorX-lock">
<icon></icon>
<text>Lock</text>
<whatsThis></whatsThis>
<toolTip>Lock X Line</toolTip>
<iconText>Lock X Line</iconText>
<activationFlags>10000</activationFlags>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mirrorY-lock">
<icon></icon>
<text>Lock Y Line</text>
<whatsThis></whatsThis>
<toolTip>Lock Y Line</toolTip>
<iconText>Lock Y Line</iconText>
<activationFlags>10000</activationFlags>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mirrorX-moveToCenter">
<icon></icon>
<text>Move to Canvas Center X</text>
<whatsThis></whatsThis>
<toolTip>Move to Canvas Center X</toolTip>
<iconText>Move to Canvas Center X</iconText>
<activationFlags>10000</activationFlags>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mirrorY-moveToCenter">
<icon></icon>
<text>Move to Canvas Center Y</text>
<whatsThis></whatsThis>
<toolTip>Move to Canvas Center Y</toolTip>
<iconText>Move to Canvas Center Y</iconText>
<activationFlags>10000</activationFlags>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="toggle-selection-overlay-mode">
<icon></icon>
<text>&amp;Toggle Selection Display Mode</text>
<whatsThis></whatsThis>
<toolTip>Toggle Selection Display Mode</toolTip>
<iconText>Toggle Selection Display Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="next_favorite_preset">
<icon></icon>
<text>Next Favourite Preset</text>
<whatsThis></whatsThis>
<toolTip>Next Favourite Preset</toolTip>
<iconText>Next Favourite Preset</iconText>
<shortcut>,</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="previous_favorite_preset">
<icon></icon>
<text>Previous Favourite Preset</text>
<whatsThis></whatsThis>
<toolTip>Previous Favourite Preset</toolTip>
<iconText>Previous Favourite Preset</iconText>
<shortcut>.</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="previous_preset">
<icon>preset-switcher</icon>
<text>Switch to Previous Preset</text>
<whatsThis></whatsThis>
<toolTip>Switch to Previous Preset</toolTip>
<iconText>Switch to Previous Preset</iconText>
<shortcut>/</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="BrushesAndStuff">
<icon></icon>
<text>Hide Brushes and Stuff Toolbar</text>
<whatsThis></whatsThis>
<toolTip>Hide Brushes and Stuff Toolbar</toolTip>
<iconText>Hide Brushes and Stuff Toolbar</iconText>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="reset_fg_bg">
<icon></icon>
<text>Reset Foreground and Background Color</text>
<whatsThis></whatsThis>
<toolTip>Reset Foreground and Background Color</toolTip>
<iconText>Reset Foreground and Background Color</iconText>
<shortcut>D</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="toggle_fg_bg">
<icon></icon>
<text>Swap Foreground and Background Color</text>
<whatsThis></whatsThis>
<toolTip>Swap Foreground and Background Color</toolTip>
<iconText>Swap Foreground and Background Color</iconText>
<shortcut>X</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="selection_tool_mode_add">
<icon></icon>
<text>Selection Mode: Add</text>
<whatsThis></whatsThis>
<toolTip>Selection Mode: Add</toolTip>
<iconText>Selection Mode: Add</iconText>
<shortcut>A</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="selection_tool_mode_subtract">
<icon></icon>
<text>Selection Mode: Subtract</text>
<whatsThis></whatsThis>
<toolTip>Selection Mode: Subtract</toolTip>
<iconText>Selection Mode: Subtract</iconText>
<shortcut>S</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="selection_tool_mode_intersect">
<icon></icon>
<text>Selection Mode: Intersect</text>
<whatsThis></whatsThis>
<toolTip>Selection Mode: Intersect</toolTip>
<iconText>Selection Mode: Intersect</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="selection_tool_mode_replace">
<icon></icon>
<text>Selection Mode: Replace</text>
<whatsThis></whatsThis>
<toolTip>Selection Mode: Replace</toolTip>
<iconText>Selection Mode: Replace</iconText>
<shortcut>R</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="set_weighted_brush_smoothing">
<icon>smoothing-weighted</icon>
<text>Brush Smoothing: Weighted</text>
<whatsThis></whatsThis>
<toolTip>Brush Smoothing: Weighted</toolTip>
<iconText>Brush Smoothing: Weighted</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="set_no_brush_smoothing">
<icon>smoothing-no</icon>
<text>Brush Smoothing: Disabled</text>
<whatsThis></whatsThis>
<toolTip>Brush Smoothing: Disabled</toolTip>
<iconText>Brush Smoothing: Disabled</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="set_stabilizer_brush_smoothing">
<icon>smoothing-stabilizer</icon>
<text>Brush Smoothing: Stabilizer</text>
<whatsThis></whatsThis>
<toolTip>Brush Smoothing: Stabilizer</toolTip>
<iconText>Brush Smoothing: Stabilizer</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="decrease_brush_size">
<icon>brushsize-decrease</icon>
<text>Decrease Brush Size</text>
<whatsThis></whatsThis>
<toolTip>Decrease Brush Size</toolTip>
<iconText>Decrease Brush Size</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>[</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="set_simple_brush_smoothing">
<icon>smoothing-basic</icon>
<text>Brush Smoothing: Basic</text>
<whatsThis></whatsThis>
<toolTip>Brush Smoothing: Basic</toolTip>
<iconText>Brush Smoothing: Basic</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="increase_brush_size">
<icon>brushsize-increase</icon>
<text>Increase Brush Size</text>
<whatsThis></whatsThis>
<toolTip>Increase Brush Size</toolTip>
<iconText>Increase Brush Size</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>]</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="toggle_assistant">
<icon></icon>
<text>Toggle Assistant</text>
<whatsThis></whatsThis>
<toolTip>Toggle Assistant</toolTip>
<iconText>ToggleAssistant</iconText>
<shortcut>Ctrl+Shift+L</shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="undo_polygon_selection">
<icon></icon>
<text>Undo Polygon Selection Points</text>
<whatsThis></whatsThis>
<toolTip>Undo Polygon Selection Points</toolTip>
<iconText>Undo Polygon Selection Points</iconText>
<shortcut>Shift+Z</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="fill_selection_foreground_color_opacity">
<icon></icon>
<text>Fill with Foreground Color (Opacity)</text>
<whatsThis></whatsThis>
<toolTip>Fill with Foreground Color (Opacity)</toolTip>
<iconText>Fill with Foreground Color (Opacity)</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut>Ctrl+Shift+Backspace</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="fill_selection_background_color_opacity">
<icon></icon>
<text>Fill with Background Color (Opacity)</text>
<whatsThis></whatsThis>
<toolTip>Fill with Background Color (Opacity)</toolTip>
<iconText>Fill with Background Color (Opacity)</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut>Ctrl+Backspace</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="fill_selection_pattern_opacity">
<icon></icon>
<text>Fill with Pattern (Opacity)</text>
<whatsThis></whatsThis>
<toolTip>Fill with Pattern (Opacity)</toolTip>
<iconText>Fill with Pattern (Opacity)</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="convert_selection_to_shape">
<icon></icon>
<text>Convert &amp;to Shape</text>
<whatsThis></whatsThis>
<toolTip>Convert to Shape</toolTip>
<iconText>Convert to Shape</iconText>
<activationFlags>10000000000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="show-global-selection-mask">
<icon></icon>
<text>&amp;Show Global Selection Mask</text>
<whatsThis></whatsThis>
<toolTip>Shows global selection as a usual selection mask in &lt;interface>Layers&lt;/interface> docker</toolTip>
<iconText>Show Global Selection Mask</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>100</activationConditions>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
</Actions>
<Actions category="Filters">
<text>Filters</text>
<Action name="krita_filter_colortoalpha">
<icon>color-to-alpha</icon>
<text>&amp;Color to Alpha...</text>
<whatsThis></whatsThis>
<toolTip>Color to Alpha</toolTip>
<iconText>Color to Alpha</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_top edge detections">
<icon></icon>
<text>&amp;Top Edge Detection</text>
<whatsThis></whatsThis>
<toolTip>Top Edge Detection</toolTip>
<iconText>Top Edge Detection</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_indexcolors">
<icon></icon>
<text>&amp;Index Colors...</text>
<whatsThis></whatsThis>
<toolTip>Index Colors</toolTip>
<iconText>Index Colors</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_emboss horizontal only">
<icon></icon>
<text>Emboss Horizontal &amp;Only</text>
<whatsThis></whatsThis>
<toolTip>Emboss Horizontal Only</toolTip>
<iconText>Emboss Horizontal Only</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_dodge">
<icon></icon>
<text>D&amp;odge</text>
<whatsThis></whatsThis>
<toolTip>Dodge</toolTip>
<iconText>Dodge</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_sharpen">
<icon></icon>
<text>&amp;Sharpen</text>
<whatsThis></whatsThis>
<toolTip>Sharpen</toolTip>
<iconText>Sharpen</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_burn">
<icon></icon>
<text>B&amp;urn</text>
<whatsThis></whatsThis>
<toolTip>Burn</toolTip>
<iconText>Burn</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_mean removal">
<icon></icon>
<text>&amp;Mean Removal</text>
<whatsThis></whatsThis>
<toolTip>Mean Removal</toolTip>
<iconText>Mean Removal</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_gaussian blur">
<icon></icon>
<text>&amp;Gaussian Blur...</text>
<whatsThis></whatsThis>
<toolTip>Gaussian Blur</toolTip>
<iconText>Gaussian Blur</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_emboss all directions">
<icon></icon>
<text>Emboss &amp;in All Directions</text>
<whatsThis></whatsThis>
<toolTip>Emboss in All Directions</toolTip>
<iconText>Emboss in All Directions</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_smalltiles">
<icon></icon>
<text>&amp;Small Tiles...</text>
<whatsThis></whatsThis>
<toolTip>Small Tiles</toolTip>
<iconText>Small Tiles</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_levels">
<icon></icon>
<text>&amp;Levels...</text>
<whatsThis></whatsThis>
<toolTip>Levels</toolTip>
<iconText>Levels</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+L</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_sobel">
<icon></icon>
<text>&amp;Sobel...</text>
<whatsThis></whatsThis>
<toolTip>Sobel</toolTip>
<iconText>Sobel</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_wave">
<icon></icon>
<text>&amp;Wave...</text>
<whatsThis></whatsThis>
<toolTip>Wave</toolTip>
<iconText>Wave</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_motion blur">
<icon></icon>
<text>&amp;Motion Blur...</text>
<whatsThis></whatsThis>
<toolTip>Motion Blur</toolTip>
<iconText>Motion Blur</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_invert">
<icon></icon>
<text>&amp;Invert</text>
<whatsThis></whatsThis>
<toolTip>Invert</toolTip>
<iconText>Invert</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+I</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_perchannel">
<icon></icon>
<text>&amp;Color Adjustment curves...</text>
<whatsThis></whatsThis>
<toolTip>Color Adjustment curves</toolTip>
<iconText>Color Adjustment curves</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+M</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_pixelize">
<icon></icon>
<text>Pi&amp;xelize...</text>
<whatsThis></whatsThis>
<toolTip>Pixelize</toolTip>
<iconText>Pixelize</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_emboss laplascian">
<icon></icon>
<text>Emboss (&amp;Laplacian)</text>
<whatsThis></whatsThis>
<toolTip>Emboss (Laplacian)</toolTip>
<iconText>Emboss (Laplacian)</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_left edge detections">
<icon></icon>
<text>&amp;Left Edge Detection</text>
<whatsThis></whatsThis>
<toolTip>Left Edge Detection</toolTip>
<iconText>Left Edge Detection</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_blur">
<icon></icon>
<text>&amp;Blur...</text>
<whatsThis></whatsThis>
<toolTip>Blur</toolTip>
<iconText>Blur</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_raindrops">
<icon></icon>
<text>&amp;Raindrops...</text>
<whatsThis></whatsThis>
<toolTip>Raindrops</toolTip>
<iconText>Raindrops</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_bottom edge detections">
<icon></icon>
<text>&amp;Bottom Edge Detection</text>
<whatsThis></whatsThis>
<toolTip>Bottom Edge Detection</toolTip>
<iconText>Bottom Edge Detection</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_noise">
<icon></icon>
<text>&amp;Random Noise...</text>
<whatsThis></whatsThis>
<toolTip>Random Noise</toolTip>
<iconText>Random Noise</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_brightnesscontrast">
<icon></icon>
<text>&amp;Brightness/Contrast curve...</text>
<whatsThis></whatsThis>
<toolTip>Brightness/Contrast curve</toolTip>
<iconText>Brightness/Contrast curve</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_colorbalance">
<icon></icon>
<text>Colo&amp;r Balance...</text>
<whatsThis></whatsThis>
<toolTip>Color Balance</toolTip>
<iconText>Color Balance</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+B</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_phongbumpmap">
<icon></icon>
<text>&amp;Phong Bumpmap...</text>
<whatsThis></whatsThis>
<toolTip>Phong Bumpmap</toolTip>
<iconText>Phong Bumpmap</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_desaturate">
<icon></icon>
<text>&amp;Desaturate</text>
<whatsThis></whatsThis>
<toolTip>Desaturate</toolTip>
<iconText>Desaturate</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+Shift+U</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_colortransfer">
<icon></icon>
<text>Color &amp;Transfer...</text>
<whatsThis></whatsThis>
<toolTip>Color Transfer</toolTip>
<iconText>Color Transfer</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_emboss vertical only">
<icon></icon>
<text>Emboss &amp;Vertical Only</text>
<whatsThis></whatsThis>
<toolTip>Emboss Vertical Only</toolTip>
<iconText>Emboss Vertical Only</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_lens blur">
<icon></icon>
<text>&amp;Lens Blur...</text>
<whatsThis></whatsThis>
<toolTip>Lens Blur</toolTip>
<iconText>Lens Blur</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_minimize">
<icon></icon>
<text>M&amp;inimize Channel</text>
<whatsThis></whatsThis>
<toolTip>Minimize Channel</toolTip>
<iconText>Minimize Channel</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_maximize">
<icon></icon>
<text>M&amp;aximize Channel</text>
<whatsThis></whatsThis>
<toolTip>Maximize Channel</toolTip>
<iconText>Maximize Channel</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_oilpaint">
<icon></icon>
<text>&amp;Oilpaint...</text>
<whatsThis></whatsThis>
<toolTip>Oilpaint</toolTip>
<iconText>Oilpaint</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_right edge detections">
<icon></icon>
<text>&amp;Right Edge Detection</text>
<whatsThis></whatsThis>
<toolTip>Right Edge Detection</toolTip>
<iconText>Right Edge Detection</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_autocontrast">
<icon></icon>
<text>&amp;Auto Contrast</text>
<whatsThis></whatsThis>
<toolTip>Auto Contrast</toolTip>
<iconText>Auto Contrast</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_roundcorners">
<icon></icon>
<text>&amp;Round Corners...</text>
<whatsThis></whatsThis>
<toolTip>Round Corners</toolTip>
<iconText>Round Corners</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_unsharp">
<icon></icon>
<text>&amp;Unsharp Mask...</text>
<whatsThis></whatsThis>
<toolTip>Unsharp Mask</toolTip>
<iconText>Unsharp Mask</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_emboss">
<icon></icon>
<text>&amp;Emboss with Variable Depth...</text>
<whatsThis></whatsThis>
<toolTip>Emboss with Variable Depth</toolTip>
<iconText>Emboss with Variable Depth</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_emboss horizontal and vertical">
<icon></icon>
<text>Emboss &amp;Horizontal &amp;&amp; Vertical</text>
<whatsThis></whatsThis>
<toolTip>Emboss Horizontal &amp; Vertical</toolTip>
<iconText>Emboss Horizontal &amp; Vertical</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_randompick">
<icon></icon>
<text>Random &amp;Pick...</text>
<whatsThis></whatsThis>
<toolTip>Random Pick</toolTip>
<iconText>Random Pick</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_gaussiannoisereducer">
<icon></icon>
<text>&amp;Gaussian Noise Reduction...</text>
<whatsThis></whatsThis>
<toolTip>Gaussian Noise Reduction</toolTip>
<iconText>Gaussian Noise Reduction</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_posterize">
<icon></icon>
<text>&amp;Posterize...</text>
<whatsThis></whatsThis>
<toolTip>Posterize</toolTip>
<iconText>Posterize</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_waveletnoisereducer">
<icon></icon>
<text>&amp;Wavelet Noise Reducer...</text>
<whatsThis></whatsThis>
<toolTip>Wavelet Noise Reducer</toolTip>
<iconText>Wavelet Noise Reducer</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="krita_filter_hsvadjustment">
<icon></icon>
<text>&amp;HSV Adjustment...</text>
<whatsThis></whatsThis>
<toolTip>HSV Adjustment</toolTip>
<iconText>HSV Adjustment</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+U</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
</Actions>
<Actions category="tool-shortcuts">
<text>Tool Shortcuts</text>
<Action name="KritaShape/KisToolDyna">
<icon></icon>
<text>Dynamic Brush Tool</text>
<whatsThis></whatsThis>
<toolTip>Dynamic Brush Tool</toolTip>
<iconText>Dynamic Brush Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisToolCrop">
<icon></icon>
<text>Crop Tool</text>
<whatsThis></whatsThis>
<toolTip>Crop the image to an area</toolTip>
<iconText>Crop the image to an area</iconText>
<shortcut>C</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisToolPolygon">
<icon></icon>
<text>Polygon Tool</text>
<whatsThis></whatsThis>
<toolTip>Polygon Tool. Shift-mouseclick ends the polygon.</toolTip>
<iconText>Polygon Tool. Shift-mouseclick ends the polygon.</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KritaShape/KisToolRectangle">
<icon></icon>
<text>Rectangle Tool</text>
<whatsThis></whatsThis>
<toolTip>Rectangle Tool</toolTip>
<iconText>Rectangle Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KritaShape/KisToolMultiBrush">
<icon></icon>
<text>Multibrush Tool</text>
<whatsThis></whatsThis>
<toolTip>Multibrush Tool</toolTip>
<iconText>Multibrush Tool</iconText>
<shortcut>Q</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KritaShape/KisToolLazyBrush">
<icon></icon>
<text>Colorize Mask Tool</text>
<whatsThis></whatsThis>
<toolTip>Colorize Mask Tool</toolTip>
<iconText>Colorize Mask Tool</iconText>
<shortcut></shortcut>
<statusTip></statusTip>
</Action>
<Action name="KritaShape/KisToolSmartPatch">
<icon></icon>
<text>Smart Patch Tool</text>
<whatsThis></whatsThis>
<toolTip>Smart Patch Tool</toolTip>
<iconText>Smart Patch Tool</iconText>
<shortcut></shortcut>
<statusTip></statusTip>
</Action>
<Action name="PanTool">
<icon></icon>
<text>Pan Tool</text>
<whatsThis></whatsThis>
<toolTip>Pan Tool</toolTip>
<iconText>Pan Tool</iconText>
<shortcut></shortcut>
<statusTip></statusTip>
</Action>
<Action name="InteractionTool">
<icon></icon>
<text>Select Shapes Tool</text>
<whatsThis></whatsThis>
<toolTip>Select Shapes Tool</toolTip>
<iconText>Select Shapes Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KritaSelected/KisToolColorPicker">
<icon></icon>
<text>Color Picker</text>
<whatsThis></whatsThis>
<toolTip>Select a color from the image or current layer</toolTip>
<iconText>Select a color from the image or current layer</iconText>
<shortcut>P</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisToolSelectOutline">
<icon></icon>
<text>Outline Selection Tool</text>
<whatsThis></whatsThis>
<toolTip>Outline Selection Tool</toolTip>
<iconText>Outline Selection Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisToolSelectPath">
<icon></icon>
<text>Bezier Curve Selection Tool</text>
<whatsThis></whatsThis>
<toolTip>Bezier Curve Selection Tool</toolTip>
<iconText>Bezier Curve Selection Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisToolSelectSimilar">
<icon></icon>
<text>Similar Color Selection Tool</text>
<whatsThis></whatsThis>
<toolTip>Similar Color Selection Tool</toolTip>
<iconText>Similar Color Selection Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KritaFill/KisToolFill">
<icon></icon>
<text>Fill Tool</text>
<whatsThis></whatsThis>
<toolTip>Fill a contiguous area of color with a color, or fill a selection.</toolTip>
<iconText>Fill a contiguous area of color with a color, or fill a selection.</iconText>
<shortcut>F</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KritaShape/KisToolLine">
<icon></icon>
<text>Line Tool</text>
<whatsThis></whatsThis>
<toolTip>Line Tool</toolTip>
<iconText>Line Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisToolPencil">
<icon></icon>
<text>Freehand Path Tool</text>
<whatsThis></whatsThis>
<toolTip>Freehand Path Tool</toolTip>
<iconText>Freehand Path Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisToolPath">
<icon></icon>
<text>Bezier Curve Tool</text>
<whatsThis></whatsThis>
<toolTip>Bezier Curve Tool. Shift-mouseclick or double-click ends the curve.</toolTip>
<iconText>Bezier Curve Tool. Shift-mouseclick or double-click ends the curve.</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KritaShape/KisToolEllipse">
<icon></icon>
<text>Ellipse Tool</text>
<whatsThis></whatsThis>
<toolTip>Ellipse Tool</toolTip>
<iconText>Ellipse Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KritaShape/KisToolBrush">
<icon></icon>
<text>Freehand Brush Tool</text>
<whatsThis></whatsThis>
<toolTip>Freehand Brush Tool</toolTip>
<iconText>Freehand Brush Tool</iconText>
<shortcut>B</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="CreateShapesTool">
<icon></icon>
<text>Create object</text>
<whatsThis></whatsThis>
<toolTip>Create object</toolTip>
<iconText>Create object</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisToolSelectElliptical">
<icon></icon>
<text>Elliptical Selection Tool</text>
<whatsThis></whatsThis>
<toolTip>Elliptical Selection Tool</toolTip>
<iconText>Elliptical Selection Tool</iconText>
<shortcut>J</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisToolSelectContiguous">
<icon></icon>
<text>Contiguous Selection Tool</text>
<whatsThis></whatsThis>
<toolTip>Contiguous Selection Tool</toolTip>
<iconText>Contiguous Selection Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KarbonPatternTool">
<icon></icon>
<text>Pattern editing</text>
<whatsThis></whatsThis>
<toolTip>Pattern editing</toolTip>
<iconText>Pattern editing</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="ReviewTool">
<icon></icon>
<text>Review</text>
<whatsThis></whatsThis>
<toolTip>Review</toolTip>
<iconText>Review</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KritaFill/KisToolGradient">
<icon></icon>
<text>Draw a gradient.</text>
<whatsThis></whatsThis>
<toolTip>Draw a gradient.</toolTip>
<iconText>Draw a gradient.</iconText>
<shortcut>G</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisToolSelectPolygonal">
<icon></icon>
<text>Polygonal Selection Tool</text>
<whatsThis></whatsThis>
<toolTip>Polygonal Selection Tool</toolTip>
<iconText>Polygonal Selection Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KritaShape/KisToolMeasure">
<icon></icon>
<text>Measurement Tool</text>
<whatsThis></whatsThis>
<toolTip>Measure the distance between two points</toolTip>
<iconText>Measure the distance between two points</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisToolSelectRectangular">
<icon></icon>
<text>Rectangular Selection Tool</text>
<whatsThis></whatsThis>
<toolTip>Rectangular Selection Tool</toolTip>
<iconText>Rectangular Selection Tool</iconText>
<shortcut>Ctrl+R</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KritaTransform/KisToolMove">
<icon></icon>
<text>Move Tool</text>
<whatsThis></whatsThis>
<toolTip>Move a layer</toolTip>
<iconText>Move a layer</iconText>
<shortcut>T</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="VectorTool">
<icon></icon>
<text>Vector Image Tool</text>
<whatsThis></whatsThis>
<toolTip>Vector Image (EMF/WMF/SVM/SVG) tool</toolTip>
<iconText>Vector Image (EMF/WMF/SVM/SVG) tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KarbonCalligraphyTool">
<icon></icon>
<text>Calligraphy</text>
<whatsThis></whatsThis>
<toolTip>Calligraphy</toolTip>
<iconText>Calligraphy</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="PathTool">
<icon></icon>
<text>Path editing</text>
<whatsThis></whatsThis>
<toolTip>Path editing</toolTip>
<iconText>Path editing</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="ZoomTool">
<icon></icon>
<text>Zoom Tool</text>
<whatsThis></whatsThis>
<toolTip>Zoom Tool</toolTip>
<iconText>Zoom Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisToolPolyline">
<icon></icon>
<text>Polyline Tool</text>
<whatsThis></whatsThis>
<toolTip>Polyline Tool. Shift-mouseclick ends the polyline.</toolTip>
<iconText>Polyline Tool. Shift-mouseclick ends the polyline.</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisToolTransform">
<icon></icon>
<text>Transform Tool</text>
<whatsThis></whatsThis>
<toolTip>Transform a layer or a selection</toolTip>
<iconText>Transform a layer or a selection</iconText>
<shortcut>Ctrl+T</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KisAssistantTool">
<icon></icon>
<text>Assistant Tool</text>
<whatsThis></whatsThis>
<toolTip>Assistant Tool</toolTip>
<iconText>Assistant Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="KarbonGradientTool">
<icon></icon>
<text>Gradient Editing Tool</text>
<whatsThis></whatsThis>
<toolTip>Gradient editing</toolTip>
<iconText>Gradient editing</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="ToolReferenceImages">
<icon></icon>
<text>Reference Images Tool</text>
<whatsThis></whatsThis>
<toolTip>Reference Images Tool</toolTip>
<iconText>Reference Images Tool</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
</Actions>
<Actions category="Blending Modes">
<text>Blending Modes</text>
<!-- commented out in the code right now
<Action name="Next Blending Mode">
<icon></icon>
<text>Next Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Next Blending Mode</toolTip>
<iconText>Next Blending Mode</iconText>
<shortcut>Alt+Shift++</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Previous Blending Mode">
<icon></icon>
<text>Previous Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Previous Blending Mode</toolTip>
<iconText>Previous Blending Mode</iconText>
<shortcut>Alt+Shift+-</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
-->
<Action name="Select Normal Blending Mode">
<icon></icon>
<text>Select Normal Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Normal Blending Mode</toolTip>
<iconText>Select Normal Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+N</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Dissolve Blending Mode">
<icon></icon>
<text>Select Dissolve Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Dissolve Blending Mode</toolTip>
<iconText>Select Dissolve Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+I</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Behind Blending Mode">
<icon></icon>
<text>Select Behind Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Behind Blending Mode</toolTip>
<iconText>Select Behind Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+Q</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Clear Blending Mode">
<icon></icon>
<text>Select Clear Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Clear Blending Mode</toolTip>
<iconText>Select Clear Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+R</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Darken Blending Mode">
<icon></icon>
<text>Select Darken Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Darken Blending Mode</toolTip>
<iconText>Select Darken Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+K</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Multiply Blending Mode">
<icon></icon>
<text>Select Multiply Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Multiply Blending Mode</toolTip>
<iconText>Select Multiply Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+M</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Color Burn Blending Mode">
<icon></icon>
<text>Select Color Burn Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Color Burn Blending Mode</toolTip>
<iconText>Select Color Burn Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+B</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Linear Burn Blending Mode">
<icon></icon>
<text>Select Linear Burn Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Linear Burn Blending Mode</toolTip>
<iconText>Select Linear Burn Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+A</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Lighten Blending Mode">
<icon></icon>
<text>Select Lighten Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Lighten Blending Mode</toolTip>
<iconText>Select Lighten Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+G</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Screen Blending Mode">
<icon></icon>
<text>Select Screen Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Screen Blending Mode</toolTip>
<iconText>Select Screen Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+S</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Color Dodge Blending Mode">
<icon></icon>
<text>Select Color Dodge Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Color Dodge Blending Mode</toolTip>
<iconText>Select Color Dodge Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+D</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Linear Dodge Blending Mode">
<icon></icon>
<text>Select Linear Dodge Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Linear Dodge Blending Mode</toolTip>
<iconText>Select Linear Dodge Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+W</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Overlay Blending Mode">
<icon></icon>
<text>Select Overlay Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Overlay Blending Mode</toolTip>
<iconText>Select Overlay Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+O</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Hard Overlay Blending Mode">
<icon></icon>
<text>Select Hard Overlay Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Hard Overlay Blending Mode</toolTip>
<iconText>Select Hard Overlay Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+P</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Soft Light Blending Mode">
<icon></icon>
<text>Select Soft Light Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Soft Light Blending Mode</toolTip>
<iconText>Select Soft Light Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+F</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Hard Light Blending Mode">
<icon></icon>
<text>Select Hard Light Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Hard Light Blending Mode</toolTip>
<iconText>Select Hard Light Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+H</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Vivid Light Blending Mode">
<icon></icon>
<text>Select Vivid Light Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Vivid Light Blending Mode</toolTip>
<iconText>Select Vivid Light Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+V</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Linear Light Blending Mode">
<icon></icon>
<text>Select Linear Light Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Linear Light Blending Mode</toolTip>
<iconText>Select Linear Light Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+J</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Pin Light Blending Mode">
<icon></icon>
<text>Select Pin Light Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Pin Light Blending Mode</toolTip>
<iconText>Select Pin Light Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+Z</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Hard Mix Blending Mode">
<icon></icon>
<text>Select Hard Mix Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Hard Mix Blending Mode</toolTip>
<iconText>Select Hard Mix Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+L</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Difference Blending Mode">
<icon></icon>
<text>Select Difference Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Difference Blending Mode</toolTip>
<iconText>Select Difference Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+E</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Exclusion Blending Mode">
<icon></icon>
<text>Select Exclusion Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Exclusion Blending Mode</toolTip>
<iconText>Select Exclusion Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+X</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Hue Blending Mode">
<icon></icon>
<text>Select Hue Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Hue Blending Mode</toolTip>
<iconText>Select Hue Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+U</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Saturation Blending Mode">
<icon></icon>
<text>Select Saturation Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Saturation Blending Mode</toolTip>
<iconText>Select Saturation Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+T</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Color Blending Mode">
<icon></icon>
<text>Select Color Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Color Blending Mode</toolTip>
<iconText>Select Color Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+C</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="Select Luminosity Blending Mode">
<icon></icon>
<text>Select Luminosity Blending Mode</text>
<whatsThis></whatsThis>
<toolTip>Select Luminosity Blending Mode</toolTip>
<iconText>Select Luminosity Blending Mode</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Alt+Shift+Y</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
</Actions>
<Actions category="Animation">
<text>Animation</text>
<Action name="previous_frame">
<icon></icon>
<text>Previous frame</text>
<whatsThis></whatsThis>
<toolTip>Move to previous frame</toolTip>
<iconText>Move to previous frame</iconText>
<activationFlags>1</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="next_frame">
<icon></icon>
<text>Next frame</text>
<whatsThis></whatsThis>
<toolTip>Move to next frame</toolTip>
<iconText>Move to next frame</iconText>
<activationFlags>1</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="toggle_playback">
<icon></icon>
<text>Play / pause animation</text>
<whatsThis></whatsThis>
<toolTip>Play / pause animation</toolTip>
<iconText>Play / pause animation</iconText>
<activationFlags>1</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_blank_frame">
<icon>addblankframe</icon>
<text>Create Blank Frame</text>
<whatsThis></whatsThis>
<toolTip>Add blank frame</toolTip>
<iconText>Add blank frame</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_duplicate_frame">
<icon>addduplicateframe</icon>
<text>Create Duplicate Frame</text>
<whatsThis></whatsThis>
<toolTip>Add duplicate frame</toolTip>
<iconText>Add duplicate frame</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="toggle_onion_skin">
<icon></icon>
<text>Toggle onion skin</text>
<whatsThis></whatsThis>
<toolTip>Toggle onion skin</toolTip>
<iconText>Toggle onion skin</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="previous_keyframe">
<icon></icon>
<text>Previous Keyframe</text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="next_keyframe">
<icon></icon>
<text>Next Keyframe</text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="first_frame">
<icon></icon>
<text>First Frame</text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="last_frame">
<icon></icon>
<text>Last Frame</text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="lazy_frame">
<icon></icon>
<text>Auto Frame Mode</text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="drop_frames">
<icon></icon>
<text></text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="show_in_timeline">
<icon></icon>
<text>Show in Timeline</text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="insert_keyframe_left">
<icon></icon>
<text>Insert Keyframe Left</text>
<whatsThis></whatsThis>
<toolTip>Insert keyframes to the left of selection, moving the tail of animation to the right.</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="insert_keyframe_right">
<icon></icon>
<text>Insert Keyframe Right</text>
<whatsThis></whatsThis>
<toolTip>Insert keyframes to the right of selection, moving the tail of animation to the right.</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="insert_multiple_keyframes">
<icon></icon>
<text>Insert Multiple Keyframes</text>
<whatsThis></whatsThis>
<toolTip>Insert several keyframes based on user parameters.</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="remove_frames_and_pull">
<icon></icon>
<text>Remove Frame and Pull</text>
<whatsThis></whatsThis>
<toolTip>Remove keyframes moving the tail of animation to the left</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="remove_frames">
<icon>deletekeyframe</icon>
<text>Remove Keyframe</text>
<whatsThis></whatsThis>
<toolTip>Remove keyframes without moving anything around</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="insert_column_left">
<icon></icon>
<text>Insert Column Left</text>
<whatsThis></whatsThis>
<toolTip>Insert column to the left of selection, moving the tail of animation to the right</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="insert_column_right">
<icon></icon>
<text>Insert Column Right</text>
<whatsThis></whatsThis>
<toolTip>Insert column to the right of selection, moving the tail of animation to the right</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="insert_multiple_columns">
<icon></icon>
<text>Insert Multiple Columns</text>
<whatsThis></whatsThis>
<toolTip>Insert several columns based on user parameters.</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="remove_columns_and_pull">
<icon></icon>
<text>Remove Column and Pull</text>
<whatsThis></whatsThis>
<toolTip>Remove columns moving the tail of animation to the left</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="remove_columns">
<icon></icon>
<text>Remove Column</text>
<whatsThis></whatsThis>
<toolTip>Remove columns without moving anything around</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="insert_hold_frame">
<icon></icon>
<text>Insert Hold Frame</text>
<whatsThis></whatsThis>
<toolTip>Insert a hold frame after every keyframe</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="insert_multiple_hold_frames">
<icon></icon>
<text>Insert Multiple Hold Frames</text>
<whatsThis></whatsThis>
<toolTip>Insert N hold frames after every keyframe</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="remove_hold_frame">
<icon></icon>
<text>Remove Hold Frame</text>
<whatsThis></whatsThis>
<toolTip>Remove a hold frame after every keyframe</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="remove_multiple_hold_frames">
<icon></icon>
<text>Remove Multiple Hold Frames</text>
<whatsThis></whatsThis>
<toolTip>Remove N hold frames after every keyframe</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="insert_hold_column">
<icon></icon>
<text>Insert Hold Column</text>
<whatsThis></whatsThis>
<toolTip>Insert a hold column into the frame at the current position</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="insert_multiple_hold_columns">
<icon></icon>
<text>Insert Multiple Hold Columns</text>
<whatsThis></whatsThis>
<toolTip>Insert N hold columns into the frame at the current position</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="remove_hold_column">
<icon></icon>
<text>Remove Hold Column</text>
<whatsThis></whatsThis>
<toolTip>Remove a hold column from the frame at the current position</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="remove_multiple_hold_columns">
<icon></icon>
<text>Remove Multiple Hold Columns</text>
<whatsThis></whatsThis>
<toolTip>Remove N hold columns from the frame at the current position</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="insert_opacity_keyframe">
<icon></icon>
<text>Add opacity keyframe</text>
<whatsThis></whatsThis>
<toolTip>Adds keyframe to control layer opacity</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="remove_opacity_keyframe">
<icon></icon>
<text>Remove opacity keyframe</text>
<whatsThis></whatsThis>
<toolTip>Removes keyframe to control layer opacity</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mirror_frames">
<icon></icon>
<text>Mirror Frames</text>
<whatsThis></whatsThis>
<toolTip>Mirror frames' position</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mirror_columns">
<icon></icon>
<text>Mirror Columns</text>
<whatsThis></whatsThis>
<toolTip>Mirror columns' position</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="copy_frames_to_clipboard">
<icon></icon>
<text>Copy to Clipboard</text>
<whatsThis></whatsThis>
<toolTip>Copy frames to clipboard</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="cut_frames_to_clipboard">
<icon></icon>
<text>Cut to Clipboard</text>
<whatsThis></whatsThis>
<toolTip>Cut frames to clipboard</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="paste_frames_from_clipboard">
<icon></icon>
<text>Paste from Clipboard</text>
<whatsThis></whatsThis>
<toolTip>Paste frames from clipboard</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="copy_columns_to_clipboard">
<icon></icon>
<text>Copy Columns to Clipboard</text>
<whatsThis></whatsThis>
<toolTip>Copy columns to clipboard</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="cut_columns_to_clipboard">
<icon></icon>
<text>Cut Columns to Clipboard</text>
<whatsThis></whatsThis>
<toolTip>Cut columns to clipboard</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="paste_columns_from_clipboard">
<icon></icon>
<text>Paste Columns from Clipboard</text>
<whatsThis></whatsThis>
<toolTip>Paste columns from clipboard</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="set_start_time">
<icon></icon>
<text>Set Start Time</text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="set_end_time">
<icon></icon>
<text>Set End Time</text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="update_playback_range">
<icon></icon>
<text>Update Playback Range</text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
</Actions>
<Actions category="Layers">
<text>Layers</text>
<Action name="activateNextLayer">
<icon></icon>
<text>Activate next layer</text>
<whatsThis></whatsThis>
<toolTip>Activate next layer</toolTip>
<iconText>Activate next layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>PgUp</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="activatePreviousLayer">
<icon></icon>
<text>Activate previous layer</text>
<whatsThis></whatsThis>
<toolTip>Activate previous layer</toolTip>
<iconText>Activate previous layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>PgDown</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="switchToPreviouslyActiveNode">
<icon></icon>
<text>Activate previously selected layer</text>
<whatsThis></whatsThis>
<toolTip>Activate previously selected layer</toolTip>
<iconText>Activate previously selected layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>;</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_new_group_layer">
<icon>groupLayer</icon>
<text>&amp;Group Layer</text>
<whatsThis></whatsThis>
<toolTip>Group Layer</toolTip>
<iconText>Group Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_new_clone_layer">
<icon>cloneLayer</icon>
<text>&amp;Clone Layer</text>
<whatsThis></whatsThis>
<toolTip>Clone Layer</toolTip>
<iconText>Clone Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_new_shape_layer">
<icon>vectorLayer</icon>
<text>&amp;Vector Layer</text>
<whatsThis></whatsThis>
<toolTip>Vector Layer</toolTip>
<iconText>Vector Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_new_adjustment_layer">
<icon>filterLayer</icon>
<text>&amp;Filter Layer...</text>
<whatsThis></whatsThis>
<toolTip>Filter Layer</toolTip>
<iconText>Filter Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_new_fill_layer">
<icon>fillLayer</icon>
<text>&amp;Fill Layer...</text>
<whatsThis></whatsThis>
<toolTip>Fill Layer</toolTip>
<iconText>Fill Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_new_file_layer">
<icon>fileLayer</icon>
<text>&amp;File Layer...</text>
<whatsThis></whatsThis>
<toolTip>File Layer</toolTip>
<iconText>File Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_new_transparency_mask">
<icon>transparencyMask</icon>
<text>&amp;Transparency Mask</text>
<whatsThis></whatsThis>
<toolTip>Transparency Mask</toolTip>
<iconText>Transparency Mask</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_new_filter_mask">
<icon>filterMask</icon>
<text>&amp;Filter Mask...</text>
<whatsThis></whatsThis>
<toolTip>Filter Mask</toolTip>
<iconText>Filter Mask</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_new_colorize_mask">
<icon>filterMask</icon>
<text>&amp;Colorize Mask</text>
<whatsThis></whatsThis>
<toolTip>Colorize Mask</toolTip>
<iconText>Colorize Mask</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_new_transform_mask">
<icon>transformMask</icon>
<text>&amp;Transform Mask...</text>
<whatsThis></whatsThis>
<toolTip>Transform Mask</toolTip>
<iconText>Transform Mask</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_new_selection_mask">
<icon>selectionMask</icon>
<text>&amp;Local Selection</text>
<whatsThis></whatsThis>
<toolTip>Local Selection</toolTip>
<iconText>Local Selection</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="isolate_layer">
<icon>view-filter</icon>
<text>&amp;Isolate Layer</text>
<whatsThis></whatsThis>
<toolTip>Isolate Layer</toolTip>
<iconText>Isolate Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="toggle_layer_lock">
<icon>layer-locked</icon>
<text>&amp;Toggle layer lock</text>
<whatsThis></whatsThis>
<toolTip>Toggle layer lock</toolTip>
<iconText>Toggle layer lock</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="toggle_layer_visibility">
<icon>visible</icon>
<text>Toggle layer &amp;visibility</text>
<whatsThis></whatsThis>
<toolTip>Toggle layer visibility</toolTip>
<iconText>Toggle layer visibility</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="toggle_layer_alpha_lock">
<icon>transparency-locked</icon>
<text>Toggle layer &amp;alpha</text>
<whatsThis></whatsThis>
<toolTip>Toggle layer alpha</toolTip>
<iconText>Toggle layer alpha</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="toggle_layer_inherit_alpha">
<icon>transparency-enabled</icon>
<text>Toggle layer alpha &amp;inheritance</text>
<whatsThis></whatsThis>
<toolTip>Toggle layer alpha inheritance</toolTip>
<iconText>Toggle layer alpha inheritance</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_new_paint_layer">
<icon>paintLayer</icon>
<text>&amp;Paint Layer</text>
<whatsThis></whatsThis>
<toolTip>Paint Layer</toolTip>
<iconText>Paint Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Insert</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="new_from_visible">
<icon></icon>
<text>&amp;New Layer From Visible</text>
<whatsThis></whatsThis>
<toolTip>New layer from visible</toolTip>
<iconText>New layer from visible</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="duplicatelayer">
<icon>duplicatelayer</icon>
<text>&amp;Duplicate Layer or Mask</text>
<whatsThis></whatsThis>
<toolTip>Duplicate Layer or Mask</toolTip>
<iconText>Duplicate Layer or Mask</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+J</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="cut_selection_to_new_layer">
<icon></icon>
<text>&amp;Cut Selection to New Layer</text>
<whatsThis></whatsThis>
<toolTip>Cut Selection to New Layer</toolTip>
<iconText>Cut Selection to New Layer</iconText>
<activationFlags>100000000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut>Ctrl+Shift+J</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="copy_selection_to_new_layer">
<icon></icon>
<text>Copy &amp;Selection to New Layer</text>
<whatsThis></whatsThis>
<toolTip>Copy Selection to New Layer</toolTip>
<iconText>Copy Selection to New Layer</iconText>
<activationFlags>100000000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+Alt+J</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="copy_layer_clipboard">
<icon></icon>
<text>Copy Layer</text>
<whatsThis></whatsThis>
<toolTip>Copy layer to clipboard</toolTip>
<iconText>Copy layer to clipboard</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="cut_layer_clipboard">
<icon></icon>
<text>Cut Layer</text>
<whatsThis></whatsThis>
<toolTip>Cut layer to clipboard</toolTip>
<iconText>Cut layer to clipboard</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="paste_layer_from_clipboard">
<icon></icon>
<text>Paste Layer</text>
<whatsThis></whatsThis>
<toolTip>Paste layer from clipboard</toolTip>
<iconText>Paste layer from clipboard</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="create_quick_group">
<icon></icon>
<text>Quick Group</text>
<whatsThis></whatsThis>
<toolTip>Create a group layer containing selected layers</toolTip>
<iconText>Quick Group</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+G</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="quick_ungroup">
<icon></icon>
<text>Quick Ungroup</text>
<whatsThis></whatsThis>
<toolTip>Remove grouping of the layers or remove one layer out of the group</toolTip>
<iconText>Quick Ungroup</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+Alt+G</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="create_quick_clipping_group">
<icon></icon>
<text>Quick Clipping Group</text>
<whatsThis></whatsThis>
<toolTip>Group selected layers and add a layer with clipped alpha channel</toolTip>
<iconText>Quick Clipping Group</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+Shift+G</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="select_all_layers">
<icon></icon>
<text>All Layers</text>
<whatsThis></whatsThis>
<toolTip>Select all layers</toolTip>
<iconText>Select all layers</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="select_visible_layers">
<icon></icon>
<text>Visible Layers</text>
<whatsThis></whatsThis>
<toolTip>Select all visible layers</toolTip>
<iconText>Select all visible layers</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="select_locked_layers">
<icon></icon>
<text>Locked Layers</text>
<whatsThis></whatsThis>
<toolTip>Select all locked layers</toolTip>
<iconText>Select all locked layers</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="select_invisible_layers">
<icon></icon>
<text>Invisible Layers</text>
<whatsThis></whatsThis>
<toolTip>Select all invisible layers</toolTip>
<iconText>Select all invisible layers</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="select_unlocked_layers">
<icon></icon>
<text>Unlocked Layers</text>
<whatsThis></whatsThis>
<toolTip>Select all unlocked layers</toolTip>
<iconText>Select all unlocked layers</iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="save_node_as_image">
<icon>document-save</icon>
<text>&amp;Save Layer/Mask...</text>
<whatsThis></whatsThis>
<toolTip>Save Layer/Mask</toolTip>
<iconText>Save Layer/Mask</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="save_vector_node_to_svg">
<icon>document-save</icon>
<text>Save Vector Layer as SVG...</text>
<whatsThis></whatsThis>
<toolTip>Save Vector Layer as SVG</toolTip>
<iconText>Save Vector Layer as SVG</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="save_groups_as_images">
<icon>document-save</icon>
<text>Save &amp;Group Layers...</text>
<whatsThis></whatsThis>
<toolTip>Save Group Layers</toolTip>
<iconText>Save Group Layers</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="convert_group_to_animated">
<icon></icon>
<text>Convert group to &amp;animated layer</text>
<whatsThis></whatsThis>
<toolTip>Convert child layers into animation frames</toolTip>
<iconText>Convert child layers into animation frames</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="convert_to_animated">
<icon></icon>
<text>Convert to &amp;animated layer</text>
<whatsThis></whatsThis>
<toolTip>Convert layer into animation frames</toolTip>
<iconText>Convert layer into animation frames</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="convert_to_file_layer">
<icon>fileLayer</icon>
<text>to &amp;File Layer</text>
<whatsThis></whatsThis>
<toolTip>Saves out the layers into a new image and then references that image.</toolTip>
<iconText>Convert to File Layer</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="import_layer_from_file">
<icon></icon>
<text>I&amp;mport Layer...</text>
<whatsThis></whatsThis>
<toolTip>Import Layer</toolTip>
<iconText>Import Layer</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="import_layer_as_paint_layer">
<icon>paintLayer</icon>
<text>&amp;as Paint Layer...</text>
<whatsThis></whatsThis>
<toolTip>as Paint Layer</toolTip>
<iconText>as Paint Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="import_layer_as_transparency_mask">
<icon>transparencyMask</icon>
<text>as &amp;Transparency Mask...</text>
<whatsThis></whatsThis>
<toolTip>as Transparency Mask</toolTip>
<iconText>as Transparency Mask</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="import_layer_as_filter_mask">
<icon>filterMask</icon>
<text>as &amp;Filter Mask...</text>
<whatsThis></whatsThis>
<toolTip>as Filter Mask</toolTip>
<iconText>as Filter Mask</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="import_layer_as_selection_mask">
<icon>selectionMask</icon>
<text>as &amp;Selection Mask...</text>
<whatsThis></whatsThis>
<toolTip>as Selection Mask</toolTip>
<iconText>as Selection Mask</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="convert_to_paint_layer">
<icon>paintLayer</icon>
<text>to &amp;Paint Layer</text>
<whatsThis></whatsThis>
<toolTip>to Paint Layer</toolTip>
<iconText>to Paint Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="convert_to_transparency_mask">
<icon>transparencyMask</icon>
<text>to &amp;Transparency Mask</text>
<whatsThis></whatsThis>
<toolTip>to Transparency Mask</toolTip>
<iconText>to Transparency Mask</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="convert_to_filter_mask">
<icon>filterMask</icon>
<text>to &amp;Filter Mask...</text>
<whatsThis></whatsThis>
<toolTip>to Filter Mask</toolTip>
<iconText>to Filter Mask</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="convert_to_selection_mask">
<icon>selectionMask</icon>
<text>to &amp;Selection Mask</text>
<whatsThis></whatsThis>
<toolTip>to Selection Mask</toolTip>
<iconText>to Selection Mask</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="split_alpha_into_mask">
<icon>transparencyMask</icon>
<text>&amp;Alpha into Mask</text>
<whatsThis></whatsThis>
<toolTip>Alpha into Mask</toolTip>
<iconText>Alpha into Mask</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>10</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="split_alpha_write">
<icon>transparency-enabled</icon>
<text>&amp;Write as Alpha</text>
<whatsThis></whatsThis>
<toolTip>Write as Alpha</toolTip>
<iconText>Write as Alpha</iconText>
<activationFlags>1000000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="split_alpha_save_merged">
<icon>document-save</icon>
<text>&amp;Save Merged...</text>
<whatsThis></whatsThis>
<toolTip>Save Merged</toolTip>
<iconText>Save Merged</iconText>
<activationFlags>1000000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="layersplit">
<icon>split-layer</icon>
<text>Split Layer...</text>
<whatsThis></whatsThis>
<toolTip>Split Layer</toolTip>
<iconText>Split Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="waveletdecompose">
<icon></icon>
<text>Wavelet Decompose ...</text>
<whatsThis></whatsThis>
<toolTip>Wavelet Decompose</toolTip>
<iconText>Wavelet Decompose</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mirrorNodeX">
<icon>symmetry-horizontal</icon>
<text>Mirror Layer Hori&amp;zontally</text>
<whatsThis></whatsThis>
<toolTip>Mirror Layer Horizontally</toolTip>
<iconText>Mirror Layer Horizontally</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mirrorNodeY">
<icon>symmetry-vertical</icon>
<text>Mirror Layer &amp;Vertically</text>
<whatsThis></whatsThis>
<toolTip>Mirror Layer Vertically</toolTip>
<iconText>Mirror Layer Vertically</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="rotatelayer">
<icon></icon>
<text>&amp;Rotate Layer...</text>
<whatsThis></whatsThis>
<toolTip>Rotate Layer</toolTip>
<iconText>Rotate Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="rotateLayerCW90">
<icon>object-rotate-right</icon>
<text>Rotate &amp;Layer 90° to the Right</text>
<whatsThis></whatsThis>
<toolTip>Rotate Layer 90° to the Right</toolTip>
<iconText>Rotate Layer 90° to the Right</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="rotateLayerCCW90">
<icon>object-rotate-left</icon>
<text>Rotate Layer &amp;90° to the Left</text>
<whatsThis></whatsThis>
<toolTip>Rotate Layer 90° to the Left</toolTip>
<iconText>Rotate Layer 90° to the Left</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="rotateLayer180">
<icon></icon>
<text>Rotate Layer &amp;180°</text>
<whatsThis></whatsThis>
<toolTip>Rotate Layer 180°</toolTip>
<iconText>Rotate Layer 180°</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="layersize">
<icon></icon>
<text>Scale &amp;Layer to new Size...</text>
<whatsThis></whatsThis>
<toolTip>Scale Layer to new Size</toolTip>
<iconText>Scale Layer to new Size</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="shearlayer">
<icon></icon>
<text>&amp;Shear Layer...</text>
<whatsThis></whatsThis>
<toolTip>Shear Layer</toolTip>
<iconText>Shear Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mirrorAllNodesX">
<icon>symmetry-horizontal</icon>
<text>Mirror All Layers Hori&amp;zontally</text>
<whatsThis></whatsThis>
<toolTip>Mirror All Layers Horizontally</toolTip>
<iconText>Mirror All Layers Horizontally</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mirrorAllNodesY">
<icon>symmetry-vertical</icon>
<text>Mirror All Layers &amp;Vertically</text>
<whatsThis></whatsThis>
<toolTip>Mirror All Layers Vertically</toolTip>
<iconText>Mirror All Layers Vertically</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="rotateAllLayers">
<icon></icon>
<text>&amp;Rotate All Layers...</text>
<whatsThis></whatsThis>
<toolTip>Rotate All Layers</toolTip>
<iconText>Rotate All Layers</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="rotateAllLayersCW90">
<icon>object-rotate-right</icon>
<text>Rotate All &amp;Layers 90° to the Right</text>
<whatsThis></whatsThis>
<toolTip>Rotate All Layers 90° to the Right</toolTip>
<iconText>Rotate All Layers 90° to the Right</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="rotateAllLayersCCW90">
<icon>object-rotate-left</icon>
<text>Rotate All Layers &amp;90° to the Left</text>
<whatsThis></whatsThis>
<toolTip>Rotate All Layers 90° to the Left</toolTip>
<iconText>Rotate All Layers 90° to the Left</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="rotateAllLayers180">
<icon></icon>
<text>Rotate All Layers &amp;180°</text>
<whatsThis></whatsThis>
<toolTip>Rotate All Layers 180°</toolTip>
<iconText>Rotate All Layers 180°</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="scaleAllLayers">
<icon></icon>
<text>Scale All &amp;Layers to new Size...</text>
<whatsThis></whatsThis>
<toolTip>Scale All Layers to new Size</toolTip>
<iconText>Scale All Layers to new Size</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="shearAllLayers">
<icon></icon>
<text>&amp;Shear All Layers...</text>
<whatsThis></whatsThis>
<toolTip>Shear All Layers</toolTip>
<iconText>Shear All Layers</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="offsetlayer">
<icon></icon>
<text>&amp;Offset Layer...</text>
<whatsThis></whatsThis>
<toolTip>Offset Layer</toolTip>
<iconText>Offset Layer</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="clones_array">
<icon></icon>
<text>Clones &amp;Array...</text>
<whatsThis></whatsThis>
<toolTip>Clones Array</toolTip>
<iconText>Clones Array</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="EditLayerMetaData">
<icon></icon>
<text>&amp;Edit metadata...</text>
<whatsThis></whatsThis>
<toolTip>Edit metadata</toolTip>
<iconText>Edit metadata</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="histogram">
<icon></icon>
<text>&amp;Histogram...</text>
<whatsThis></whatsThis>
<toolTip>Histogram</toolTip>
<iconText>Histogram</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="layercolorspaceconversion">
<icon></icon>
<text>&amp;Convert Layer Color Space...</text>
<whatsThis></whatsThis>
<toolTip>Convert Layer Color Space</toolTip>
<iconText>Convert Layer Color Space</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="merge_layer">
<icon>merge-layer-below</icon>
<text>&amp;Merge with Layer Below</text>
<whatsThis></whatsThis>
<toolTip>Merge with Layer Below</toolTip>
<iconText>Merge with Layer Below</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+E</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="flatten_layer">
<icon></icon>
<text>&amp;Flatten Layer</text>
<whatsThis></whatsThis>
<toolTip>Flatten Layer</toolTip>
<iconText>Flatten Layer</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="rasterize_layer">
<icon></icon>
<text>Ras&amp;terize Layer</text>
<whatsThis></whatsThis>
<toolTip>Rasterize Layer</toolTip>
<iconText>Rasterize Layer</iconText>
<activationFlags>10000000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="flatten_image">
<icon></icon>
<text>Flatten ima&amp;ge</text>
<whatsThis></whatsThis>
<toolTip>Flatten image</toolTip>
<iconText>Flatten image</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>Ctrl+Shift+E</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="layer_style">
<icon></icon>
<text>La&amp;yer Style...</text>
<whatsThis></whatsThis>
<toolTip>Layer Style</toolTip>
<iconText>Layer Style</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="LayerGroupSwitcher/previous">
<icon></icon>
<text>Move into previous group</text>
<whatsThis></whatsThis>
<toolTip>Move into previous group</toolTip>
<iconText>Move into previous group</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="LayerGroupSwitcher/next">
<icon></icon>
<text>Move into next group</text>
<whatsThis></whatsThis>
<toolTip>Move into next group</toolTip>
<iconText>Move into next group</iconText>
<activationFlags>0</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="RenameCurrentLayer">
<icon></icon>
<text>Rename current layer</text>
<whatsThis></whatsThis>
<toolTip>Rename current layer</toolTip>
<iconText>Rename current layer</iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>F2</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="remove_layer">
<icon>deletelayer</icon>
<text>&amp;Remove Layer</text>
<whatsThis></whatsThis>
<toolTip>Remove Layer</toolTip>
<iconText>Remove Layer</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut>Shift+Delete</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="move_layer_up">
<icon>arrowupblr</icon>
<text>Move Layer or Mask Up</text>
<whatsThis></whatsThis>
<toolTip>Move Layer or Mask Up</toolTip>
<iconText></iconText>
<shortcut>Ctrl+PgUp</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="move_layer_down">
<icon>arrowdown</icon>
<text>Move Layer or Mask Down</text>
<whatsThis></whatsThis>
<toolTip>Move Layer or Mask Down</toolTip>
<iconText></iconText>
<shortcut>Ctrl+PgDown</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="layer_properties">
<icon>properties</icon>
<text>&amp;Properties...</text>
<whatsThis></whatsThis>
<toolTip>Properties</toolTip>
<iconText>Properties</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut>F3</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="set-copy-from">
<icon></icon>
<text>Set Copy F&amp;rom...</text>
<whatsThis></whatsThis>
<toolTip>Set the source for the selected clone layer(s).</toolTip>
<iconText>Set Copy From</iconText>
<activationFlags>1000</activationFlags>
<activationConditions>1</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
</Actions>
</ActionCollection>
diff --git a/krita/krita4.xmlgui b/krita/krita4.xmlgui
index 531eee5e33..49c16a698c 100644
--- a/krita/krita4.xmlgui
+++ b/krita/krita4.xmlgui
@@ -1,409 +1,410 @@
<?xml version="1.1"?>
<kpartgui xmlns="http://www.kde.org/standards/kxmlgui/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
name="Krita"
version="435"
xsi:schemaLocation="http://www.kde.org/standards/kxmlgui/1.0 http://www.kde.org/standards/kxmlgui/1.0/kxmlgui.xsd">
<MenuBar>
<Menu name="file">
<text>&amp;File</text>
<Action name="file_new"/>
<Action name="file_open"/>
<Action name="file_open_recent"/>
<Separator/>
<Action name="file_save"/>
<Action name="file_save_as"/>
<Action name="file_reload_file"/>
<Separator/>
<Action name="file_sessions"/>
<Separator/>
<Action name="file_import_file"/>
<Action name="file_export_file"/>
<Separator/>
<Action name="file_export_pdf"/>
<Separator/>
<Action name="file_import_animation"/>
<Action name="render_animation"/>
<Separator/>
<Action name="save_incremental_version"/>
<Action name="save_incremental_backup"/>
<Separator/>
<Action name="create_template"/>
<Action name="create_copy"/>
<!--Separator/>
<Action name="file_print"/>
<Action name="file_print_preview"/-->
<Separator/>
<Action name="file_documentinfo"/>
<Separator/>
<Action name="file_close"/>
<Action name="file_close_all"/>
<Action name="file_quit"/>
</Menu>
<Menu name="edit">
<text>&amp;Edit</text>
<Action name="edit_undo"/>
<Action name="edit_redo"/>
<Separator/>
<Action name="edit_cut"/>
<Action name="edit_copy"/>
<Action name="cut_sharp"/>
<Action name="copy_sharp"/>
<Action name="copy_merged"/>
<Action name="edit_paste"/>
<Action name="paste_at"/>
<Action name="paste_new"/>
<Action name="paste_as_reference"/>
<Action name="clear"/>
<Action name="fill_selection_foreground_color"/>
<Action name="fill_selection_background_color"/>
<Action name="fill_selection_pattern"/>
<Menu name="fill_special">
<text>Fill Special</text>
<Action name="fill_selection_foreground_color_opacity"/>
<Action name="fill_selection_background_color_opacity"/>
<Action name="fill_selection_pattern_opacity"/>
</Menu>
<Action name="stroke_shapes"/>
<Action name="stroke_selection"/>
<Action name="delete"/>
<Separator/>
<Action name="revert"/>
</Menu>
<Menu name="view">
<text>&amp;View</text>
<Action name="view_show_canvas_only"/>
<Action name="fullscreen"/>
<Action name="view_detached_canvas"/>
<Action name="wrap_around_mode"/>
<Action name="level_of_detail_mode"/>
<Action name="softProof"/>
<Action name="gamutCheck"/>
<Separator/>
<Menu name="Canvas">
<text>&amp;Canvas</text>
<Action name="mirror_canvas"/>
<Separator/>
<Action name="zoom_to_100pct" />
<Action name="rotate_canvas_right" />
<Action name="rotate_canvas_left" />
<Action name="reset_canvas_rotation" />
<!-- TODO: Something is not right with the way zoom actions are hooked up. These are in the KoZoomController.
It seems they are not being properly placed in the view manager since the MDI changes were implemented
-->
<Action name="view_zoom_in"/>
<Action name="view_zoom_out"/>
</Menu>
<!-- TODO: None of these actions are showing. There names must have been changed to something with the MDI changes?...
<Action name="actual_pixels"/>
<Action name="actual_size"/>
<Action name="fit_to_canvas"/>
-->
<Separator/>
<Action name="view_ruler"/>
<Action name="rulers_track_mouse"/>
<Action name="view_show_guides"/>
<Action name="view_lock_guides"/>
<Action name="showStatusBar" />
<Separator/>
<Action name="view_grid"/>
<Action name="view_pixel_grid"/>
<Separator/>
<Menu name="SnapTo">
<text>&amp;Snap To</text>
<Action name="view_snap_to_guides"/>
<Action name="view_snap_to_grid"/>
<Action name="view_snap_to_pixel"/>
<Action name="view_snap_orthogonal" />
<Action name="view_snap_node" />
<Action name="view_snap_extension" />
<Action name="view_snap_intersection" />
<Action name="view_snap_bounding_box" />
<Action name="view_snap_image_bounds" />
<Action name="view_snap_image_center" />
</Menu>
<Separator/>
<Action name="view_toggle_painting_assistants"/>
<Action name="view_toggle_assistant_previews"/>
<Action name="view_toggle_reference_images"/>
<Separator/>
<Action name="view_palette_action_menu"/>
<Separator/>
<Action name="refresh_canvas"/>
</Menu>
<Menu name="Image">
<text>&amp;Image</text>
<Action name="image_properties"/>
<Action name="image_color"/>
<Action name="imagecolorspaceconversion"/>
<Action name="duplicate_image"/>
<Separator/>
<Action name="trim_to_image"/>
<Action name="resizeimagetolayer"/>
<Action name="resizeimagetoselection"/>
<Separator/>
<Menu name="Rotate">
<text>&amp;Rotate</text>
<Action name="rotateimage"/>
<Separator/>
<Action name="rotateImageCW90"/>
<Action name="rotateImageCCW90"/>
<Action name="rotateImage180"/>
</Menu>
<Action name="shearimage"/>
<Separator/>
<Action name="mirrorImageHorizontal"/>
<Action name="mirrorImageVertical"/>
<Separator/>
<Action name="imagesize"/>
<Action name="offsetimage"/>
<Action name="imageresolution"/>
<Action name="canvassize"/>
<Separator/>
<Action name="imagesplit"/>
<Action name="waveletdecompose"/>
<Action name="separate"/>
</Menu>
<Menu name="Layer">
<text>&amp;Layer</text>
<Action name="cut_layer_clipboard"/>
<Action name="copy_layer_clipboard"/>
<Action name="paste_layer_from_clipboard"/>
<Separator/>
<Menu name="LayerNew">
<text>New</text>
<Action name="add_new_paint_layer"/>
<Action name="add_new_clone_layer"/>
<Action name="new_from_visible"/>
<Action name="duplicatelayer"/>
<Separator/>
<Action name="cut_selection_to_new_layer"/>
<Action name="copy_selection_to_new_layer"/>
</Menu>
<Menu name="LayerImportExport">
<text>&amp;Import/Export</text>
<Action name="save_node_as_image"/>
<Action name="save_vector_node_to_svg"/>
<Action name="save_groups_as_images"/>
<Separator/>
<Action name="import_layer_from_file"/>
<Menu name="LayerImportAs">
<text>Import</text>
<Action name="import_layer_as_paint_layer"/>
<Action name="import_layer_as_transparency_mask"/>
<Action name="import_layer_as_filter_mask"/>
<Action name="import_layer_as_selection_mask"/>
</Menu>
</Menu>
<Menu name="LayerConvert">
<text>&amp;Convert</text>
<Action name="convert_to_paint_layer"/>
<Action name="convert_to_transparency_mask"/>
<Action name="convert_to_filter_mask"/>
<Action name="convert_to_selection_mask"/>
<Action name="convert_to_file_layer"/>
<Action name="convert_group_to_animated"/>
<Action name="layercolorspaceconversion"/>
</Menu>
<Separator/>
<Menu name="LayerSelect">
<text>&amp;Select</text>
<Action name="select_all_layers"/>
<Action name="select_visible_layers"/>
<Action name="select_invisible_layers"/>
<Action name="select_locked_layers"/>
<Action name="select_unlocked_layers"/>
</Menu>
<Menu name="LayerGroup">
<text>&amp;Group</text>
<Action name="create_quick_group"/>
<Action name="create_quick_clipping_group"/>
<Action name="quick_ungroup"/>
</Menu>
<Menu name="LayerTransform">
<text>&amp;Transform</text>
<Action name="mirrorNodeX"/>
<Action name="mirrorNodeY"/>
<Action name="layersize"/>
<Menu name="Rotate">
<text>&amp;Rotate</text>
<Action name="rotatelayer"/>
<Separator/>
<Action name="rotateLayerCW90"/>
<Action name="rotateLayerCCW90"/>
<Action name="rotateLayer180"/>
</Menu>
<Action name="shearlayer"/>
<Action name="offsetlayer"/>
</Menu>
<Menu name="LayerTransformAll">
<text>Transform &amp;All Layers</text>
<Action name="mirrorAllNodesX"/>
<Action name="mirrorAllNodesY"/>
<Action name="scaleAllLayers"/>
<Menu name="Rotate">
<text>&amp;Rotate</text>
<Action name="rotateAllLayers"/>
<Separator/>
<Action name="rotateAllLayersCW90"/>
<Action name="rotateAllLayersCCW90"/>
<Action name="rotateAllLayers180"/>
</Menu>
<Action name="shearAllLayers"/>
</Menu>
<Menu name="LayerSplitAlpha">
<text>S&amp;plit</text>
<Menu name="LayerSplitAlpha">
<text>S&amp;plit Alpha</text>
<Action name="split_alpha_into_mask"/>
<Action name="split_alpha_write"/>
<Action name="split_alpha_save_merged"/>
</Menu>
<Action name="layersplit"/>
<Action name="clones_array"/>
</Menu>
<Separator/>
<Action name="EditLayerMetaData"/>
<Action name="histogram"/>
<Separator/>
<Action name="merge_layer"/>
<Action name="flatten_layer"/>
<Action name="rasterize_layer"/>
<Action name="merge_all_shape_layers"/>
<Action name="flatten_image"/>
<Action name="merge_selected_layers"/>
<Separator/>
<Action name="layer_style"/>
</Menu>
<Menu name="Select">
<text>&amp;Select</text>
<Action name="select_all"/>
<Action name="deselect"/>
<Action name="reselect"/>
<Action name="invert_selection"/>
<Separator/>
<Action name="edit_selection"/>
<Action name="convert_to_vector_selection"/>
<Action name="convert_to_raster_selection"/>
<Action name="convert_shapes_to_vector_selection"/>
<Action name="convert_selection_to_shape"/>
<Separator/>
<Action name="feather"/>
<Action name="similar"/>
<Separator/>
<Action name="toggle_display_selection"/>
<Action name="show-global-selection-mask"/>
<Action name="selectionscale"/>
<Separator/>
<Action name="colorrange"/>
<Menu name="selectopaquemenu">
<text>Select &amp;Opaque</text>
<Action name="selectopaque"/>
<Separator/>
<Action name="selectopaque_add"/>
<Action name="selectopaque_subtract"/>
<Action name="selectopaque_intersect"/>
</Menu>
<Separator/>
<Action name="featherselection"/>
<Action name="growselection"/>
<Action name="shrinkselection"/>
<Action name="borderselection"/>
<Action name="smoothselection"/>
</Menu>
<Menu name="Filter">
<text>Filte&amp;r</text>
<Action name="filter_apply_again"/>
<Action name="filter_gallery"/>
<Separator/>
<Action name="adjust_filters"/>
<Action name="artistic_filters"/>
<Action name="blur_filters"/>
<Action name="color_filters"/>
<Action name="decor_filters"/>
<Action name="edge_filters"/>
<Action name="emboss_filters"/>
<Action name="enhance_filters"/>
<Action name="map_filters"/>
<Action name="nonphotorealistic_filters"/>
<Action name="other_filters"/>
<Separator/>
<Action name="QMic"/>
<Action name="QMicAgain"/>
</Menu>
<Menu name="tools">
<text>&amp;Tools</text>
<Menu name="scripts"><text>Scripts</text></Menu>
</Menu>
<Menu name="settings">
<text>Setti&amp;ngs</text>
<Action name="options_configure"/>
<Action name="manage_bundles"/>
+ <Action name="dbexplorer"/>
<Separator/>
<Action name="options_configure_toolbars"/>
<Merge name="StandardToolBarMenuHandler" />
<Separator/>
<Action name="view_toggledockers"/>
<Action name="settings_dockers_menu"/>
<Separator/>
<Action name="theme_menu"/>
<Separator/>
<!-- `Configure Shortcuts` was moved into main configuration menu -->
<!-- <Action name="options_configure_keybinding"/> -->
<Separator/>
<Action name="switch_application_language"/>
<Action name="settings_active_author"/>
<Separator/>
</Menu>
<Action name="window"/>
<Separator/>
<Menu name="help">
<text>&amp;Help</text>
<Action name="help_contents"/>
<Action name="help_whats_this"/>
<Separator/>
<MergeLocal/>
<Action name="help_show_tip"/>
<Separator/>
<Action name="help_report_bug"/>
<Action name="buginfo"/>
<Action name="sysinfo"/>
<Separator/>
<Action name="help_about_app"/>
<Action name="help_about_kde"/>
</Menu>
</MenuBar>
<ToolBar name="mainToolBar" fullWidth="false" noMerge="1">
<Text>File</Text>
<Action name="file_new"/>
<Action name="file_open"/>
<Action name="file_save"/>
<Separator/>
<Action name="edit_undo"/>
<Action name="edit_redo"/>
</ToolBar>
<ToolBar name="BrushesAndStuff" position="top">
<Text>Brushes and Stuff</Text>
<Action name="gradients"/>
<Action name="patterns"/>
<Separator/>
<Action name="dual"/>
<Separator/>
<Action name="paintops"/>
<Action name="paintop_options"/>
<Action name="composite_actions"/>
<Action name="brushslider1"/>
<Action name="brushslider2"/>
<Separator/>
<Action name="mirror_actions"/>
<Action name="expanding_spacer_1"/>
<Action name="select_layout"/>
<Action name="workspaces"/>
</ToolBar>
</kpartgui>
diff --git a/krita/pics/svg/dark_bundle_archive.svg b/krita/pics/svg/dark_bundle_archive.svg
new file mode 100644
index 0000000000..2088e0b500
--- /dev/null
+++ b/krita/pics/svg/dark_bundle_archive.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ viewBox="0 0 22 22"
+ version="1.1"
+ id="svg9"
+ sodipodi:docname="dark_bundle_archive.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <style
+ type="text/css"
+ id="current-color-scheme">.ColorScheme-Text{color:#373737;}</style>
+ <g
+ style="color:#373737;fill:currentColor;fill-opacity:1;stroke:none"
+ class="ColorScheme-Text"
+ id="g7">
+ <Title>Adapted from Breeze's Archive Insert</Title>
+ <path
+ d="M 19,3 H 3 V 19 H 19 Z M 4,18 V 4 h 7 v 1 h 1 V 4 h 6 V 18 Z M 11,5 h -1 v 1 h 1 z m 0,1 v 1 h 1 V 6 Z m 0,1 h -1 v 1 h 1 z m 0,1 v 1 h 1 V 8 Z m 0,1 h -1 v 1 1 h 2 v -1 h -1 z"
+ id="path3"/>
+ </g>
+</svg>
diff --git a/krita/pics/svg/light_bundle_archive.svg b/krita/pics/svg/light_bundle_archive.svg
new file mode 100644
index 0000000000..b903187d70
--- /dev/null
+++ b/krita/pics/svg/light_bundle_archive.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ viewBox="0 0 22 22"
+ version="1.1"
+ id="svg9"
+ sodipodi:docname="dark_bundle_archive.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <style
+ type="text/css"
+ id="current-color-scheme">.ColorScheme-Text{color:#cacaca;}</style>
+ <g
+ style="color:#cacaca;fill:currentColor;fill-opacity:1;stroke:none"
+ class="ColorScheme-Text"
+ id="g7">
+ <Title>Adapted from Breeze's Archive Insert</Title>
+ <path
+ d="M 19,3 H 3 V 19 H 19 Z M 4,18 V 4 h 7 v 1 h 1 V 4 h 6 V 18 Z M 11,5 h -1 v 1 h 1 z m 0,1 v 1 h 1 V 6 Z m 0,1 h -1 v 1 h 1 z m 0,1 v 1 h 1 V 8 Z m 0,1 h -1 v 1 1 h 2 v -1 h -1 z"
+ id="path3"/>
+ </g>
+</svg>
diff --git a/krita/pics/svg/svg-icons.qrc b/krita/pics/svg/svg-icons.qrc
index 5d6418703d..13f02593e8 100644
--- a/krita/pics/svg/svg-icons.qrc
+++ b/krita/pics/svg/svg-icons.qrc
@@ -1,157 +1,159 @@
<RCC>
<qresource prefix="/">
<file>broken-preset.svgz</file>
<file>dark_addblankframe.svg</file>
<file>dark_addcolor.svg</file>
<file>dark_addduplicateframe.svg</file>
<file>dark_deletekeyframe.svg</file>
<file>dark_docker_lock_a.svg</file>
<file>dark_docker_lock_b.svg</file>
<file>dark_layer-locked.svg</file>
<file>dark_layer-unlocked.svg</file>
<file>dark_nextframe.svg</file>
<file>dark_nextkeyframe.svg</file>
<file>dark_lastframe.svg</file>
<file>dark_prevkeyframe.svg</file>
<file>dark_firstframe.svg</file>
<file>dark_pallete_librarysvg.svg</file>
<file>dark_passthrough-disabled.svg</file>
<file>dark_passthrough-enabled.svg</file>
<file>dark_prevframe.svg</file>
<file>dark_selection-mode_ants.svg</file>
<file>dark_selection-mode_invisible.svg</file>
<file>dark_selection-mode_mask.svg</file>
<file>dark_transparency-disabled.svg</file>
<file>dark_transparency-enabled.svg</file>
<file>dark_trim-to-image.svg</file>
<file>dark_warning.svg</file>
<file>delete.svgz</file>
<file>layer-style-disabled.svg</file>
<file>layer-style-enabled.svg</file>
<file>light_addblankframe.svg</file>
<file>light_addcolor.svg</file>
<file>light_addduplicateframe.svg</file>
<file>light_deletekeyframe.svg</file>
<file>light_docker_lock_a.svg</file>
<file>light_docker_lock_b.svg</file>
<file>light_layer-locked.svg</file>
<file>light_layer-unlocked.svg</file>
<file>light_nextframe.svg</file>
<file>light_pallete_library.svg</file>
<file>light_passthrough-disabled.svgz</file>
<file>light_passthrough-enabled.svgz</file>
<file>light_prevframe.svg</file>
<file>light_nextkeyframe.svg</file>
<file>light_lastframe.svg</file>
<file>light_prevkeyframe.svg</file>
<file>light_firstframe.svg</file>
<file>light_selection-mode_ants.svg</file>
<file>light_selection-mode_invisible.svg</file>
<file>light_selection-mode_mask.svg</file>
<file>light_timeline_keyframe.svg</file>
<file>light_transparency-disabled.svg</file>
<file>light_transparency-enabled.svg</file>
<file>light_trim-to-image.svg</file>
<file>light_warning.svg</file>
<file>paintop_presets_disabled.svgz</file>
<file>paintop_settings_01.svgz</file>
<file>selection-info.svg</file>
<file>selection-mode_invisible.svg</file>
<file>svg-icons.qrc</file>
<file>transparency-locked.svg</file>
<file>transparency-unlocked.svg</file>
<file>workspace-chooser.svg</file>
<file>light_lazyframeOn.svg</file>
<file>light_lazyframeOff.svg</file>
<file>dark_lazyframeOn.svg</file>
<file>dark_lazyframeOff.svg</file>
<file>dark_mirror-view.svg</file>
<file>light_mirror-view.svg</file>
<file>dark_rotation-reset.svg</file>
<file>light_rotation-reset.svg</file>
<file>light_smoothing-basic.svg</file>
<file>light_smoothing-no.svg</file>
<file>light_smoothing-stabilizer.svg</file>
<file>light_smoothing-weighted.svg</file>
<file>dark_smoothing-basic.svg</file>
<file>dark_smoothing-no.svg</file>
<file>dark_smoothing-stabilizer.svg</file>
<file>dark_smoothing-weighted.svg</file>
<file>light_merge-layer-below.svg</file>
<file>dark_merge-layer-below.svg</file>
<file>light_rotate-canvas-left.svg</file>
<file>light_rotate-canvas-right.svg</file>
<file>dark_rotate-canvas-left.svg</file>
<file>dark_rotate-canvas-right.svg</file>
<file>light_gmic.svg</file>
<file>dark_gmic.svg</file>
<file>light_split-layer.svg</file>
<file>dark_split-layer.svg</file>
<file>light_color-to-alpha.svg</file>
<file>dark_color-to-alpha.svg</file>
<file>light_preset-switcher.svg</file>
<file>dark_preset-switcher.svg</file>
<file>dark_animation_play.svg</file>
<file>dark_animation_stop.svg</file>
<file>dark_dropframe.svg</file>
<file>dark_droppedframes.svg</file>
<file>light_animation_play.svg</file>
<file>light_animation_stop.svg</file>
<file>light_dropframe.svg</file>
<file>light_droppedframes.svg</file>
<file>dark_landscape.svg</file>
<file>dark_portrait.svg</file>
<file>light_landscape.svg</file>
<file>light_portrait.svg</file>
<file>dark_interpolation_constant.svg</file>
<file>dark_interpolation_linear.svg</file>
<file>dark_interpolation_bezier.svg</file>
<file>dark_interpolation_sharp.svg</file>
<file>dark_interpolation_smooth.svg</file>
<file>light_interpolation_bezier.svg</file>
<file>light_interpolation_constant.svg</file>
<file>light_interpolation_linear.svg</file>
<file>light_interpolation_sharp.svg</file>
<file>light_interpolation_smooth.svg</file>
<file>dark_audio-none.svg</file>
<file>dark_audio-volume-high.svg</file>
<file>dark_audio-volume-mute.svg</file>
<file>dark_keyframe-add.svg</file>
<file>dark_keyframe-remove.svg</file>
<file>dark_zoom-fit.svg</file>
<file>dark_zoom-horizontal.svg</file>
<file>dark_zoom-vertical.svg</file>
<file>light_audio-none.svg</file>
<file>light_audio-volume-high.svg</file>
<file>light_audio-volume-mute.svg</file>
<file>light_keyframe-add.svg</file>
<file>light_keyframe-remove.svg</file>
<file>light_zoom-fit.svg</file>
<file>light_zoom-horizontal.svg</file>
<file>light_zoom-vertical.svg</file>
<file>dark_showColoring.svg</file>
<file>dark_showMarks.svg</file>
<file>dark_showColoringOff.svg</file>
<file>dark_showMarksOff.svg</file>
<file>dark_updateColorize.svg</file>
<file>light_showColoring.svg</file>
<file>light_showMarks.svg</file>
<file>light_showColoringOff.svg</file>
<file>light_showMarksOff.svg</file>
<file>light_updateColorize.svg</file>
<file>light_wheel-light.svg</file>
<file>light_wheel-rings.svg</file>
<file>light_wheel-sectors.svg</file>
<file>dark_wheel-light.svg</file>
<file>dark_wheel-rings.svg</file>
<file>dark_wheel-sectors.svg</file>
<file>dark_infinity.svg</file>
<file>light_infinity.svg</file>
<file>dark_gamut-mask-on.svg</file>
<file>dark_gamut-mask-off.svg</file>
<file>light_gamut-mask-off.svg</file>
<file>light_gamut-mask-on.svg</file>
<file>dark_ratio.svg</file>
<file>light_ratio.svg</file>
+ <file>dark_bundle_archive.svg</file>
+ <file>light_bundle_archive.svg</file>
</qresource>
</RCC>
diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt
index 9bd57a43be..b9bb8ac7ef 100644
--- a/libs/CMakeLists.txt
+++ b/libs/CMakeLists.txt
@@ -1,23 +1,24 @@
add_subdirectory( version )
add_subdirectory( global )
add_subdirectory( koplugin )
add_subdirectory( widgetutils )
add_subdirectory( widgets )
add_subdirectory( store )
add_subdirectory( odf )
add_subdirectory( flake )
add_subdirectory( basicflakes )
add_subdirectory( pigment )
add_subdirectory( command )
add_subdirectory( brush )
add_subdirectory( psd )
add_subdirectory( color )
add_subdirectory( image )
add_subdirectory( ui )
add_subdirectory( impex )
add_subdirectory( libkis )
if (NOT APPLE AND HAVE_QT_QUICK)
add_subdirectory( libqml )
endif()
+add_subdirectory( resources )
add_subdirectory( metadata )
-
+add_subdirectory( resourcewidgets )
diff --git a/libs/brush/CMakeLists.txt b/libs/brush/CMakeLists.txt
index e2ca732025..a6f851515d 100644
--- a/libs/brush/CMakeLists.txt
+++ b/libs/brush/CMakeLists.txt
@@ -1,50 +1,51 @@
add_subdirectory( tests )
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
)
set(kritalibbrush_LIB_SRCS
kis_predefined_brush_factory.cpp
kis_auto_brush.cpp
kis_boundary.cc
kis_brush.cpp
kis_scaling_size_brush.cpp
kis_brush_registry.cpp
- kis_brush_server.cpp
+ KisBrushServerProvider.cpp
kis_gbr_brush.cpp
kis_abr_brush.cpp
kis_abr_brush_collection.cpp
kis_imagepipe_brush.cpp
kis_pipebrush_parasite.cpp
kis_png_brush.cpp
kis_svg_brush.cpp
kis_qimage_pyramid.cpp
KisSharedQImagePyramid.cpp
kis_text_brush.cpp
kis_auto_brush_factory.cpp
kis_text_brush_factory.cpp
+ KisAbrStorage.cpp
)
add_library(kritalibbrush SHARED ${kritalibbrush_LIB_SRCS} )
generate_export_header(kritalibbrush BASE_NAME kritabrush EXPORT_MACRO_NAME BRUSH_EXPORT)
if (WIN32)
target_link_libraries(kritalibbrush kritaimage Qt5::Svg ${WIN32_PLATFORM_NET_LIBS})
else ()
target_link_libraries(kritalibbrush kritaimage Qt5::Svg)
endif ()
if(HAVE_VC)
include_directories(SYSTEM ${Vc_INCLUDE_DIR})
target_link_libraries(kritalibbrush ${Vc_LIBRARIES})
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Vc_DEFINITIONS}")
endif()
set_target_properties(kritalibbrush PROPERTIES
VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritalibbrush ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/libs/brush/KisAbrStorage.cpp b/libs/brush/KisAbrStorage.cpp
new file mode 100644
index 0000000000..eecdb0d7e6
--- /dev/null
+++ b/libs/brush/KisAbrStorage.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2018 Boudewijn Rempt <boud@valdyas.org>
+ * Copyright (C) 2019 Agata Cacko <cacko.azh@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 "KisAbrStorage.h"
+#include "KisResourceStorage.h"
+
+#include <QFileInfo>
+
+struct KisAbrStorageStaticRegistrar {
+ KisAbrStorageStaticRegistrar() {
+ KisStoragePluginRegistry::instance()->addStoragePluginFactory(KisResourceStorage::StorageType::AdobeBrushLibrary, new KisStoragePluginFactory<KisAbrStorage>());
+ }
+};
+static KisAbrStorageStaticRegistrar s_registrar;
+
+
+class AbrTagIterator : public KisResourceStorage::TagIterator
+{
+public:
+ AbrTagIterator(const QString &location, const QString &resourceType)
+ : m_location(location)
+ , m_resourceType(resourceType)
+ {}
+
+ bool hasNext() const override {return false; }
+ void next() override {}
+
+ QString url() const override { return QString(); }
+ QString name() const override { return QString(); }
+ QString comment() const override {return QString(); }
+ KisTagSP tag() const override { return 0; }
+private:
+
+ QString m_location;
+ QString m_resourceType;
+};
+
+class AbrIterator : public KisResourceStorage::ResourceIterator
+{
+public:
+ KisAbrBrushCollectionSP m_brushCollection;
+ QSharedPointer<QMap<QString, KisAbrBrushSP>> m_brushesMap;
+ QMap<QString, KisAbrBrushSP>::const_key_value_iterator m_brushCollectionIterator;
+ KisAbrBrushSP m_currentResource;
+ bool isLoaded;
+ QString m_currentUrl;
+
+
+ AbrIterator(KisAbrBrushCollectionSP brushCollection)
+ : m_brushCollection(brushCollection)
+ , isLoaded(false)
+ {
+ }
+
+ bool hasNext() const override
+ {
+ if (!isLoaded) {
+ bool success = m_brushCollection->load();
+ Q_UNUSED(success); // brush collection will be empty
+ const_cast<AbrIterator*>(this)->m_brushesMap = m_brushCollection->brushesMap();
+ const_cast<AbrIterator*>(this)->m_brushCollectionIterator = m_brushesMap->constKeyValueBegin();
+ const_cast<AbrIterator*>(this)->isLoaded = true;
+ }
+
+ if (m_brushCollectionIterator == m_brushesMap->constKeyValueEnd()) {
+ return false;
+ }
+
+ const_cast<AbrIterator*>(this)->m_brushCollectionIterator++;
+ bool hasNext = (m_brushCollectionIterator != m_brushesMap->constKeyValueEnd());
+ const_cast<AbrIterator*>(this)->m_brushCollectionIterator--;
+
+ return hasNext;
+ }
+
+ void next() override
+ {
+ m_brushCollectionIterator++;
+ std::pair<QString, KisAbrBrushSP> resourcePair = *m_brushCollectionIterator;
+ m_currentResource = resourcePair.second;
+ m_currentUrl = resourcePair.first;
+ }
+
+ QString url() const override { return m_currentUrl; }
+ QString type() const override { return ResourceType::Brushes; }
+ QDateTime lastModified() const override { return m_brushCollection->lastModified(); }
+
+ KoResourceSP resource() const override
+ {
+ return m_currentResource;
+ }
+};
+
+KisAbrStorage::KisAbrStorage(const QString &location)
+ : KisStoragePlugin(location)
+ , m_brushCollection(new KisAbrBrushCollection(location))
+{
+
+}
+
+KisAbrStorage::~KisAbrStorage()
+{
+
+}
+
+KisResourceStorage::ResourceItem KisAbrStorage::resourceItem(const QString &url)
+{
+ KisResourceStorage::ResourceItem item;
+ item.url = url;
+ // last "_" with index is the suffix added by abr_collection
+ int indexOfUnderscore = url.lastIndexOf("_");
+ QString filenameUrl = url;
+ // filenameUrl contains the name of the collection (filename without .abr, brush name without index)
+ filenameUrl.remove(indexOfUnderscore, url.length() - indexOfUnderscore);
+ item.folder = filenameUrl;
+ item.resourceType = ResourceType::Brushes;
+ item.lastModified = QFileInfo(m_brushCollection->filename()).lastModified();
+ return item;
+}
+
+
+KoResourceSP KisAbrStorage::resource(const QString &url)
+{
+ if (!m_brushCollection->isLoaded()) {
+ m_brushCollection->load();
+ }
+ return m_brushCollection->brushByName(url);
+}
+
+QSharedPointer<KisResourceStorage::ResourceIterator> KisAbrStorage::resources(const QString &/*resourceType*/)
+{
+ return QSharedPointer<KisResourceStorage::ResourceIterator>(new AbrIterator(m_brushCollection));
+}
+
+QSharedPointer<KisResourceStorage::TagIterator> KisAbrStorage::tags(const QString &resourceType)
+{
+ return QSharedPointer<KisResourceStorage::TagIterator>(new AbrTagIterator(location(), resourceType));
+}
diff --git a/libs/brush/KisAbrStorage.h b/libs/brush/KisAbrStorage.h
new file mode 100644
index 0000000000..78477ea12d
--- /dev/null
+++ b/libs/brush/KisAbrStorage.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 Boudewijn Rempt <boud@valdyas.org>
+ * Copyright (C) 2019 Agata Cacko <cacko.azh@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 KISABRSTORAGE_H
+#define KISABRSTORAGE_H
+
+#include <KisStoragePlugin.h>
+
+#include <kritabrush_export.h>
+#include <kis_abr_brush_collection.h>
+
+class BRUSH_EXPORT KisAbrStorage : public KisStoragePlugin
+{
+public:
+ KisAbrStorage(const QString &location);
+ virtual ~KisAbrStorage();
+
+ KisResourceStorage::ResourceItem resourceItem(const QString &url) override;
+
+ KoResourceSP resource(const QString &url) override;
+ QSharedPointer<KisResourceStorage::ResourceIterator> resources(const QString &resourceType) override;
+ QSharedPointer<KisResourceStorage::TagIterator> tags(const QString &resourceType) override;
+
+ KisAbrBrushCollectionSP m_brushCollection;
+};
+
+#endif // KISABRSTORAGE_H
diff --git a/libs/brush/KisBrushServerProvider.cpp b/libs/brush/KisBrushServerProvider.cpp
new file mode 100644
index 0000000000..29f0c39e13
--- /dev/null
+++ b/libs/brush/KisBrushServerProvider.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+#include "KisBrushServerProvider.h"
+
+#include <QDir>
+#include <QApplication>
+
+#include <QGlobalStatic>
+#include <KoResourcePaths.h>
+
+#include <KoResource.h>
+
+#include <kis_debug.h>
+
+Q_GLOBAL_STATIC(KisBrushServerProvider, s_instance)
+
+
+KisBrushServerProvider::KisBrushServerProvider()
+{
+ m_brushServer = new KoResourceServer<KisBrush>(ResourceType::Brushes);
+}
+
+KisBrushServerProvider::~KisBrushServerProvider()
+{
+ delete m_brushServer;
+}
+
+KisBrushServerProvider* KisBrushServerProvider::instance()
+{
+ return s_instance;
+}
+
+KoResourceServer<KisBrush>* KisBrushServerProvider::brushServer()
+{
+ return m_brushServer;
+}
diff --git a/libs/brush/KisBrushServerProvider.h b/libs/brush/KisBrushServerProvider.h
new file mode 100644
index 0000000000..6601c6e79a
--- /dev/null
+++ b/libs/brush/KisBrushServerProvider.h
@@ -0,0 +1,54 @@
+/*
+ * 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_BRUSH_SERVER_PROVIDER_H
+#define KIS_BRUSH_SERVER_PROVIDER_H
+
+#include <QString>
+#include <QStringList>
+#include <QList>
+
+#include <KoResourceServer.h>
+
+#include "kritabrush_export.h"
+#include "kis_brush.h"
+
+/**
+ *
+ */
+class BRUSH_EXPORT KisBrushServerProvider : public QObject
+{
+
+ Q_OBJECT
+
+public:
+ KisBrushServerProvider();
+ ~KisBrushServerProvider() override;
+
+ KoResourceServer<KisBrush>* brushServer();
+
+ static KisBrushServerProvider* instance();
+
+private:
+
+ KisBrushServerProvider(const KisBrushServerProvider&);
+ KisBrushServerProvider operator=(const KisBrushServerProvider&);
+
+ KoResourceServer<KisBrush>* m_brushServer;
+};
+
+#endif
diff --git a/libs/brush/kis_abr_brush.cpp b/libs/brush/kis_abr_brush.cpp
index aeb5c83fb5..00c6c3b31c 100644
--- a/libs/brush/kis_abr_brush.cpp
+++ b/libs/brush/kis_abr_brush.cpp
@@ -1,118 +1,96 @@
/*
* 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 "kis_abr_brush.h"
#include "kis_abr_brush_collection.h"
#include <QDomElement>
#include <QFile>
#include <QImage>
#include <QPoint>
#include <QByteArray>
#include <QBuffer>
#include <QCryptographicHash>
#include <klocalizedstring.h>
#include <KoColor.h>
#include "kis_datamanager.h"
#include "kis_paint_device.h"
#include "kis_global.h"
#include "kis_image.h"
#define DEFAULT_SPACING 0.25
KisAbrBrush::KisAbrBrush(const QString& filename, KisAbrBrushCollection *parent)
- : KisScalingSizeBrush(filename)
+ : KoEphemeralResource<KisScalingSizeBrush>(filename)
, m_parent(parent)
{
setBrushType(INVALID);
setHasColor(false);
setSpacing(DEFAULT_SPACING);
}
KisAbrBrush::KisAbrBrush(const KisAbrBrush& rhs)
- : KisScalingSizeBrush(rhs),
- m_parent(0)
+ : KoEphemeralResource<KisScalingSizeBrush>(rhs)
+ , m_parent(0)
{
- // Warning! The brush became detached from the parent!
+ // Warning! The brush became detached from the parent collection!
}
KisAbrBrush::KisAbrBrush(const KisAbrBrush& rhs, KisAbrBrushCollection *parent)
- : KisScalingSizeBrush(rhs),
- m_parent(parent)
-{
-}
-
-KisBrush* KisAbrBrush::clone() const
-{
- return new KisAbrBrush(*this);
-}
-
-bool KisAbrBrush::load()
-{
- return true;
-}
-
-bool KisAbrBrush::loadFromDevice(QIODevice */*dev*/)
-{
- return true;
-}
-
-bool KisAbrBrush::save()
+ : KoEphemeralResource<KisScalingSizeBrush>(rhs)
+ , m_parent(parent)
{
- //Return true, otherwise the brush won't be added to the
- //resource server if the brush is loaded via import
- return true;
}
-bool KisAbrBrush::saveToDevice(QIODevice* /*dev*/) const
+KoResourceSP KisAbrBrush::clone() const
{
- return true;
+ return KoResourceSP(new KisAbrBrush(*this));
}
void KisAbrBrush::setBrushTipImage(const QImage& image)
{
setValid(true);
setBrushType(MASK);
setHasColor(false);
KisBrush::setBrushTipImage(image);
}
void KisAbrBrush::toXML(QDomDocument& d, QDomElement& e) const
{
e.setAttribute("name", name()); // legacy
predefinedBrushToXML("abr_brush", e);
KisBrush::toXML(d, e);
}
QString KisAbrBrush::defaultFileExtension() const
{
return QString();
}
QImage KisAbrBrush::brushTipImage() const
{
if (KisBrush::brushTipImage().isNull() && m_parent) {
m_parent->load();
}
return KisBrush::brushTipImage();
}
diff --git a/libs/brush/kis_abr_brush.h b/libs/brush/kis_abr_brush.h
index 5625814f23..31aa516ced 100644
--- a/libs/brush/kis_abr_brush.h
+++ b/libs/brush/kis_abr_brush.h
@@ -1,77 +1,77 @@
/*
* 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.
*/
#ifndef KIS_ABR_BRUSH_
#define KIS_ABR_BRUSH_
#include <QImage>
#include <QVector>
#include <kis_scaling_size_brush.h>
#include <kis_types.h>
#include <kis_shared.h>
+#include <KoEphemeralResource.h>
#include "kritabrush_export.h"
class KisQImagemask;
class KisAbrBrushCollection;
typedef KisSharedPtr<KisQImagemask> KisQImagemaskSP;
class QString;
class QIODevice;
-class BRUSH_EXPORT KisAbrBrush : public KisScalingSizeBrush
+class BRUSH_EXPORT KisAbrBrush : public KoEphemeralResource<KisScalingSizeBrush>
{
public:
/// Construct brush to load filename later as brush
KisAbrBrush(const QString& filename, KisAbrBrushCollection *parent);
KisAbrBrush(const KisAbrBrush& rhs);
KisAbrBrush(const KisAbrBrush& rhs, KisAbrBrushCollection *parent);
- KisBrush* clone() const override;
+ KisAbrBrush &operator=(const KisAbrBrush &rhs) = delete;
+ KoResourceSP clone() const override;
- bool load() override;
-
- bool loadFromDevice(QIODevice *dev) override;
-
- bool save() override;
-
- bool saveToDevice(QIODevice* dev) const override;
+ QPair<QString, QString> resourceType() const override {
+ return QPair<QString, QString>(ResourceType::Brushes, ResourceSubType::AbrBrushes);
+ }
/**
* @return default file extension for saving the brush
*/
QString defaultFileExtension() const override;
QImage brushTipImage() const override;
friend class KisAbrBrushCollection;
void setBrushTipImage(const QImage& image) override;
void toXML(QDomDocument& d, QDomElement& e) const override;
private:
KisAbrBrushCollection *m_parent;
};
+typedef QSharedPointer<KisAbrBrush> KisAbrBrushSP;
+
#endif // KIS_ABR_BRUSH_
diff --git a/libs/brush/kis_abr_brush_collection.cpp b/libs/brush/kis_abr_brush_collection.cpp
index e0d08671aa..cc21f5ab42 100644
--- a/libs/brush/kis_abr_brush_collection.cpp
+++ b/libs/brush/kis_abr_brush_collection.cpp
@@ -1,628 +1,641 @@
/*
* 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 <QFileInfo>
+#include <KoMD5Generator.h>
+#include <KoMD5Generator.h>
#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];
+ KisAbrBrushSP abrBrush;
+ if (m_abrBrushes->contains(name)) {
+ abrBrush = m_abrBrushes.data()->operator[](name);
}
else {
- abrBrush = new KisAbrBrush(name, this);
+ abrBrush = KisAbrBrushSP(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;
+ m_abrBrushes.data()->operator[](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];
+ KisAbrBrushSP abrBrush;
+ if (m_abrBrushes->contains(name)) {
+ abrBrush = m_abrBrushes.data()->operator[](name);
}
else {
- abrBrush = new KisAbrBrush(name, this);
+ abrBrush = KisAbrBrushSP(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;
+ m_abrBrushes.data()->operator[](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)
+ : m_isLoaded(false)
+ , m_lastModified()
+ , m_filename(filename)
+ , m_abrBrushes(new QMap<QString, KisAbrBrushSP>())
{
}
KisAbrBrushCollection::KisAbrBrushCollection(const KisAbrBrushCollection& rhs)
- : KisScalingSizeBrush(rhs)
+ : m_isLoaded(rhs.m_isLoaded)
+ , m_lastModified(rhs.m_lastModified)
{
- for (auto it = rhs.m_abrBrushes.begin();
- it != rhs.m_abrBrushes.end();
+ m_abrBrushes.reset(new QMap<QString, KisAbrBrushSP>());
+ for (auto it = rhs.m_abrBrushes->begin();
+ it != rhs.m_abrBrushes->end();
++it) {
- m_abrBrushes.insert(it.key(), new KisAbrBrush(*it.value(), this));
+ m_abrBrushes->insert(it.key(), KisAbrBrushSP(new KisAbrBrush(*it.value(), this)));
}
}
-KisBrush* KisAbrBrushCollection::clone() const
-{
- return new KisAbrBrushCollection(*this);
-}
-
bool KisAbrBrushCollection::load()
{
+ m_isLoaded = true;
QFile file(filename());
+ QFileInfo info(file);
+ m_lastModified = info.lastModified();
// 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();
+
+ m_md5 = KoMD5Generator::generateHash(ba);
+
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);
+ layer_ID = abr_brush_load(abr, &abr_hdr, QFileInfo(filename()).fileName(), 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;
}
+bool KisAbrBrushCollection::isLoaded() const
+{
+ return m_isLoaded;
+}
+
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_abr_brush_collection.h b/libs/brush/kis_abr_brush_collection.h
index 4b18d19ba4..f103bff2aa 100644
--- a/libs/brush/kis_abr_brush_collection.h
+++ b/libs/brush/kis_abr_brush_collection.h
@@ -1,93 +1,126 @@
/*
* 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.
*/
#ifndef KIS_ABR_BRUSH_COLLECTION_H
#define KIS_ABR_BRUSH_COLLECTION_H
#include <QImage>
#include <QVector>
#include <QDataStream>
#include <QString>
#include <kis_debug.h>
#include <kis_scaling_size_brush.h>
#include <kis_types.h>
#include <kis_shared.h>
#include <brushengine/kis_paint_information.h>
+#include <kis_abr_brush.h>
+
class QString;
class QIODevice;
-class KisAbrBrush;
+
struct AbrInfo;
/**
* load a collection of brushes from an abr file
*/
-class BRUSH_EXPORT KisAbrBrushCollection : public KisScalingSizeBrush
+class BRUSH_EXPORT KisAbrBrushCollection
{
protected:
public:
/// Construct brush to load filename later as brush
KisAbrBrushCollection(const QString& filename);
- KisBrush* clone() const override;
+ ~KisAbrBrushCollection() {}
- ~KisAbrBrushCollection() override {}
+ bool load();
- bool load() override;
+ bool loadFromDevice(QIODevice *dev);
- bool loadFromDevice(QIODevice *dev) override;
+ bool save();
- bool save() override;
+ bool saveToDevice(QIODevice* dev) const;
- bool saveToDevice(QIODevice* dev) const override;
+ bool isLoaded() const;
/**
* @return a preview of the brush
*/
- virtual QImage image() const;
+ QImage image() const;
/**
* @return default file extension for saving the brush
*/
- QString defaultFileExtension() const override;
+ QString defaultFileExtension() const;
+
+ QList<KisAbrBrushSP> brushes() const {
+ return m_abrBrushes->values();
+ }
+
+ QSharedPointer<QMap<QString, KisAbrBrushSP>> brushesMap() const {
+ return m_abrBrushes;
+ }
+
+ KisAbrBrushSP brushByName(QString name) const {
+ if (m_abrBrushes->contains(name)) {
+ return m_abrBrushes.data()->operator[](name);
+ }
+ return KisAbrBrushSP();
+ }
- QList<KisAbrBrush*> brushes() {
- return m_abrBrushes.values();
+ QDateTime lastModified() const {
+ return m_lastModified;
+ }
+
+ QString filename() const {
+ return m_filename;
+ }
+
+ QByteArray md5() const {
+ return m_md5;
}
protected:
KisAbrBrushCollection(const KisAbrBrushCollection& rhs);
- void toXML(QDomDocument& d, QDomElement& e) const override;
+ void toXML(QDomDocument& d, QDomElement& e) const;
private:
qint32 abr_brush_load(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id);
qint32 abr_brush_load_v12(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id);
quint32 abr_brush_load_v6(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id);
- QMap<QString, KisAbrBrush*> m_abrBrushes;
+
+ bool m_isLoaded;
+ QDateTime m_lastModified;
+ QString m_filename;
+ QSharedPointer<QMap<QString, KisAbrBrushSP>> m_abrBrushes;
+ QByteArray m_md5;
+
};
+typedef QSharedPointer<KisAbrBrushCollection> KisAbrBrushCollectionSP;
+
#endif
diff --git a/libs/brush/kis_auto_brush.cpp b/libs/brush/kis_auto_brush.cpp
index 3ce5f9963f..6df3140548 100644
--- a/libs/brush/kis_auto_brush.cpp
+++ b/libs/brush/kis_auto_brush.cpp
@@ -1,398 +1,402 @@
/*
* Copyright (c) 2004,2007-2009 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2012 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 <compositeops/KoVcMultiArchBuildSupport.h> //MSVC requires that Vc come first
#include "kis_auto_brush.h"
#include <kis_debug.h>
#include <math.h>
#include <QRect>
#include <QDomElement>
#include <QtConcurrentMap>
#include <QByteArray>
#include <QBuffer>
#include <QFile>
#include <QFileInfo>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <kis_datamanager.h>
#include <kis_fixed_paint_device.h>
#include <kis_paint_device.h>
#include <brushengine/kis_paint_information.h>
#include <kis_mask_generator.h>
#include <kis_boundary.h>
#include <brushengine/kis_paintop_lod_limitations.h>
#include <kis_brush_mask_applicator_base.h>
#if defined(_WIN32) || defined(_WIN64)
#include <stdlib.h>
#define srand48 srand
inline double drand48()
{
return double(rand()) / RAND_MAX;
}
#endif
struct KisAutoBrush::Private {
Private()
- : randomness(0), density(1.0), idealThreadCountCached(1) {}
+ : randomness(0)
+ , density(1.0)
+ , idealThreadCountCached(1)
+ {}
Private(const Private &rhs)
- : shape(rhs.shape->clone()),
- randomness(rhs.randomness),
- density(rhs.density),
- idealThreadCountCached(rhs.idealThreadCountCached)
+ : shape(rhs.shape->clone())
+ , randomness(rhs.randomness)
+ , density(rhs.density)
+ , idealThreadCountCached(rhs.idealThreadCountCached)
{
}
+
QScopedPointer<KisMaskGenerator> shape;
qreal randomness;
qreal density;
int idealThreadCountCached;
};
KisAutoBrush::KisAutoBrush(KisMaskGenerator* as, qreal angle, qreal randomness, qreal density)
- : KisBrush(),
+ : KoEphemeralResource<KisBrush>(),
d(new Private)
{
d->shape.reset(as);
d->randomness = randomness;
d->density = density;
d->idealThreadCountCached = QThread::idealThreadCount();
setBrushType(MASK);
setWidth(qMax(qreal(1.0), d->shape->width()));
setHeight(qMax(qreal(1.0), d->shape->height()));
QImage image = createBrushPreview();
setBrushTipImage(image);
// Set angle here so brush tip image is generated unrotated
setAngle(angle);
image = createBrushPreview();
setImage(image);
}
KisAutoBrush::~KisAutoBrush()
{
}
qreal KisAutoBrush::userEffectiveSize() const
{
return d->shape->diameter();
}
void KisAutoBrush::setUserEffectiveSize(qreal value)
{
d->shape->setDiameter(value);
}
KisAutoBrush::KisAutoBrush(const KisAutoBrush& rhs)
- : KisBrush(rhs),
- d(new Private(*rhs.d))
+ : KoEphemeralResource<KisBrush>(rhs)
+ , d(new Private(*rhs.d))
{
}
-KisBrush* KisAutoBrush::clone() const
+KoResourceSP KisAutoBrush::clone() const
{
- return new KisAutoBrush(*this);
+ return KoResourceSP(new KisAutoBrush(*this));
}
/* It's difficult to predict the mask height exactly when there are
* more than 2 spikes, so we return an upperbound instead. */
static KisDabShape lieAboutDabShape(KisDabShape const& shape, int spikes)
{
return spikes > 2 ? KisDabShape(shape.scale(), 1.0, shape.rotation()) : shape;
}
qint32 KisAutoBrush::maskHeight(KisDabShape const& shape,
qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const
{
return KisBrush::maskHeight(
lieAboutDabShape(shape, maskGenerator()->spikes()), subPixelX, subPixelY, info);
}
qint32 KisAutoBrush::maskWidth(KisDabShape const& shape,
qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const
{
return KisBrush::maskWidth(
lieAboutDabShape(shape, maskGenerator()->spikes()), subPixelX, subPixelY, info);
}
QSizeF KisAutoBrush::characteristicSize(KisDabShape const& shape) const
{
return KisBrush::characteristicSize(lieAboutDabShape(shape, maskGenerator()->spikes()));
}
inline void fillPixelOptimized_4bytes(quint8 *color, quint8 *buf, int size)
{
/**
* This version of filling uses low granularity of data transfers
* (32-bit chunks) and internal processor's parallelism. It reaches
* 25% better performance in KisStrokeBenchmark in comparison to
* per-pixel memcpy version (tested on Sandy Bridge).
*/
int block1 = size / 8;
int block2 = size % 8;
quint32 *src = reinterpret_cast<quint32*>(color);
quint32 *dst = reinterpret_cast<quint32*>(buf);
// check whether all buffers are 4 bytes aligned
// (uncomment if experience some problems)
// Q_ASSERT(((qint64)src & 3) == 0);
// Q_ASSERT(((qint64)dst & 3) == 0);
for (int i = 0; i < block1; i++) {
*dst = *src;
*(dst + 1) = *src;
*(dst + 2) = *src;
*(dst + 3) = *src;
*(dst + 4) = *src;
*(dst + 5) = *src;
*(dst + 6) = *src;
*(dst + 7) = *src;
dst += 8;
}
for (int i = 0; i < block2; i++) {
*dst = *src;
dst++;
}
}
inline void fillPixelOptimized_general(quint8 *color, quint8 *buf, int size, int pixelSize)
{
/**
* This version uses internal processor's parallelism and gives
* 20% better performance in KisStrokeBenchmark in comparison to
* per-pixel memcpy version (tested on Sandy Bridge (+20%) and
* on Merom (+10%)).
*/
int block1 = size / 8;
int block2 = size % 8;
for (int i = 0; i < block1; i++) {
quint8 *d1 = buf;
quint8 *d2 = buf + pixelSize;
quint8 *d3 = buf + 2 * pixelSize;
quint8 *d4 = buf + 3 * pixelSize;
quint8 *d5 = buf + 4 * pixelSize;
quint8 *d6 = buf + 5 * pixelSize;
quint8 *d7 = buf + 6 * pixelSize;
quint8 *d8 = buf + 7 * pixelSize;
for (int j = 0; j < pixelSize; j++) {
*(d1 + j) = color[j];
*(d2 + j) = color[j];
*(d3 + j) = color[j];
*(d4 + j) = color[j];
*(d5 + j) = color[j];
*(d6 + j) = color[j];
*(d7 + j) = color[j];
*(d8 + j) = color[j];
}
buf += 8 * pixelSize;
}
for (int i = 0; i < block2; i++) {
memcpy(buf, color, pixelSize);
buf += pixelSize;
}
}
void KisAutoBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst,
KisBrush::ColoringInformation* coloringInformation,
KisDabShape const& shape,
const KisPaintInformation& info,
double subPixelX , double subPixelY, qreal softnessFactor) const
{
Q_UNUSED(info);
// Generate the paint device from the mask
const KoColorSpace* cs = dst->colorSpace();
quint32 pixelSize = cs->pixelSize();
// mask dimension methods already includes KisBrush::angle()
int dstWidth = maskWidth(shape, subPixelX, subPixelY, info);
int dstHeight = maskHeight(shape, subPixelX, subPixelY, info);
QPointF hotSpot = this->hotSpot(shape, info);
// mask size and hotSpot function take the KisBrush rotation into account
qreal angle = shape.rotation() + KisBrush::angle();
// if there's coloring information, we merely change the alpha: in that case,
// the dab should be big enough!
if (coloringInformation) {
// new bounds. we don't care if there is some extra memory occcupied.
dst->setRect(QRect(0, 0, dstWidth, dstHeight));
dst->lazyGrowBufferWithoutInitialization();
}
else {
KIS_SAFE_ASSERT_RECOVER_RETURN(dst->bounds().width() >= dstWidth &&
dst->bounds().height() >= dstHeight);
}
quint8* dabPointer = dst->data();
quint8* color = 0;
if (coloringInformation) {
if (dynamic_cast<PlainColoringInformation*>(coloringInformation)) {
color = const_cast<quint8*>(coloringInformation->color());
}
}
double centerX = hotSpot.x() - 0.5 + subPixelX;
double centerY = hotSpot.y() - 0.5 + subPixelY;
d->shape->setSoftness(softnessFactor); // softness must be set first
d->shape->setScale(shape.scaleX(), shape.scaleY());
if (coloringInformation) {
if (color && pixelSize == 4) {
fillPixelOptimized_4bytes(color, dabPointer, dstWidth * dstHeight);
}
else if (color) {
fillPixelOptimized_general(color, dabPointer, dstWidth * dstHeight, pixelSize);
}
else {
for (int y = 0; y < dstHeight; y++) {
for (int x = 0; x < dstWidth; x++) {
memcpy(dabPointer, coloringInformation->color(), pixelSize);
coloringInformation->nextColumn();
dabPointer += pixelSize;
}
coloringInformation->nextRow();
}
}
}
MaskProcessingData data(dst, cs, d->randomness, d->density,
centerX, centerY,
angle);
KisBrushMaskApplicatorBase *applicator = d->shape->applicator();
applicator->initializeData(&data);
int jobs = d->idealThreadCountCached;
if (threadingAllowed() && dstHeight > 100 && jobs >= 4) {
int splitter = dstHeight / jobs;
QVector<QRect> rects;
for (int i = 0; i < jobs - 1; i++) {
rects << QRect(0, i * splitter, dstWidth, splitter);
}
rects << QRect(0, (jobs - 1)*splitter, dstWidth, dstHeight - (jobs - 1)*splitter);
OperatorWrapper wrapper(applicator);
QtConcurrent::blockingMap(rects, wrapper);
}
else {
QRect rect(0, 0, dstWidth, dstHeight);
applicator->process(rect);
}
}
void KisAutoBrush::toXML(QDomDocument& doc, QDomElement& e) const
{
QDomElement shapeElt = doc.createElement("MaskGenerator");
d->shape->toXML(doc, shapeElt);
e.appendChild(shapeElt);
e.setAttribute("type", "auto_brush");
e.setAttribute("spacing", QString::number(spacing()));
e.setAttribute("useAutoSpacing", QString::number(autoSpacingActive()));
e.setAttribute("autoSpacingCoeff", QString::number(autoSpacingCoeff()));
e.setAttribute("angle", QString::number(KisBrush::angle()));
e.setAttribute("randomness", QString::number(d->randomness));
e.setAttribute("density", QString::number(d->density));
KisBrush::toXML(doc, e);
}
QImage KisAutoBrush::createBrushPreview()
{
int width = maskWidth(KisDabShape(), 0.0, 0.0, KisPaintInformation());
int height = maskHeight(KisDabShape(), 0.0, 0.0, KisPaintInformation());
KisPaintInformation info(QPointF(width * 0.5, height * 0.5), 0.5, 0, 0, angle(), 0, 0, 0, 0);
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
fdev->setRect(QRect(0, 0, width, height));
fdev->initialize();
mask(fdev, KoColor(Qt::black, fdev->colorSpace()), KisDabShape(), info);
return fdev->convertToQImage(0);
}
const KisMaskGenerator* KisAutoBrush::maskGenerator() const
{
return d->shape.data();
}
qreal KisAutoBrush::density() const
{
return d->density;
}
qreal KisAutoBrush::randomness() const
{
return d->randomness;
}
QPainterPath KisAutoBrush::outline() const
{
bool simpleOutline = (d->density < 1.0);
if (simpleOutline) {
QPainterPath path;
QRectF brushBoundingbox(0, 0, width(), height());
if (maskGenerator()->type() == KisMaskGenerator::CIRCLE) {
path.addEllipse(brushBoundingbox);
}
else { // if (maskGenerator()->type() == KisMaskGenerator::RECTANGLE)
path.addRect(brushBoundingbox);
}
return path;
}
return KisBrush::boundary()->path();
}
void KisAutoBrush::lodLimitations(KisPaintopLodLimitations *l) const
{
KisBrush::lodLimitations(l);
if (!qFuzzyCompare(density(), 1.0)) {
l->limitations << KoID("auto-brush-density", i18nc("PaintOp instant preview limitation", "Brush Density recommended value 100.0"));
}
if (!qFuzzyCompare(randomness(), 0.0)) {
l->limitations << KoID("auto-brush-randomness", i18nc("PaintOp instant preview limitation", "Brush Randomness recommended value 0.0"));
}
}
diff --git a/libs/brush/kis_auto_brush.h b/libs/brush/kis_auto_brush.h
index 0966f6eaa8..6e7bbddb7b 100644
--- a/libs/brush/kis_auto_brush.h
+++ b/libs/brush/kis_auto_brush.h
@@ -1,103 +1,92 @@
/*
* Copyright (c) 2004 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_AUTOBRUSH_RESOURCE_H_
#define _KIS_AUTOBRUSH_RESOURCE_H_
#include "kritabrush_export.h"
+
+#include <KoResource.h>
+#include <KoEphemeralResource.h>
+
#include "kis_brush.h"
#include <QScopedPointer>
class KisMaskGenerator;
/**
* XXX: docs!
*/
-class BRUSH_EXPORT KisAutoBrush : public KisBrush
+class BRUSH_EXPORT KisAutoBrush : public KoEphemeralResource<KisBrush>
{
public:
- KisAutoBrush(KisMaskGenerator* as, qreal angle, qreal randomness, qreal density = 1.0);
- KisAutoBrush(const KisAutoBrush& rhs);
- KisBrush* clone() const override;
+ KisAutoBrush(KisMaskGenerator *as, qreal angle, qreal randomness, qreal density = 1.0);
+ KisAutoBrush(const KisAutoBrush &rhs);
+ KisAutoBrush &operator=(const KisAutoBrush &rhs) = delete;
+ KoResourceSP clone() const override;
~KisAutoBrush() override;
public:
qreal userEffectiveSize() const override;
void setUserEffectiveSize(qreal value) override;
qint32 maskWidth(KisDabShape const& shape, qreal subPixelX, qreal subPixelY,
const KisPaintInformation& info) const override;
qint32 maskHeight(KisDabShape const& shape, qreal subPixelX, qreal subPixelY,
const KisPaintInformation& info) const override;
QSizeF characteristicSize(KisDabShape const&) const override;
KisFixedPaintDeviceSP paintDevice(const KoColorSpace*,
KisDabShape const&,
const KisPaintInformation&,
double = 0, double = 0) const override {
return 0; // The autobrush does NOT support images!
}
void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst,
KisBrush::ColoringInformation* src,
KisDabShape const&,
const KisPaintInformation& info,
double subPixelX = 0, double subPixelY = 0,
qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const override;
QPainterPath outline() const override;
public:
- bool load() override {
- return false;
- }
-
- bool loadFromDevice(QIODevice *) override {
- return false;
- }
-
- bool save() override {
- return false;
- }
-
- bool saveToDevice(QIODevice*) const override {
- return false;
- }
-
void toXML(QDomDocument& , QDomElement&) const override;
const KisMaskGenerator* maskGenerator() const;
qreal randomness() const;
qreal density() const;
void lodLimitations(KisPaintopLodLimitations *l) const override;
private:
QImage createBrushPreview();
private:
struct Private;
const QScopedPointer<Private> d;
};
#endif // _KIS_AUTOBRUSH_RESOURCE_H_
diff --git a/libs/brush/kis_auto_brush_factory.cpp b/libs/brush/kis_auto_brush_factory.cpp
index ec0a697a13..77c8aea521 100644
--- a/libs/brush/kis_auto_brush_factory.cpp
+++ b/libs/brush/kis_auto_brush_factory.cpp
@@ -1,43 +1,45 @@
/*
* Copyright (c) 2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2010-2011 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.
*/
#include <compositeops/KoVcMultiArchBuildSupport.h> //MSVC requires that Vc come first
#include "kis_auto_brush_factory.h"
#include <QDomDocument>
#include "kis_auto_brush.h"
#include "kis_mask_generator.h"
#include <kis_dom_utils.h>
-KisBrushSP KisAutoBrushFactory::createBrush(const QDomElement &brushDefinition)
+KisBrushSP KisAutoBrushFactory::createBrush(const QDomElement &brushDefinition, KisResourcesInterfaceSP resourcesInterface)
{
+ Q_UNUSED(resourcesInterface);
+
KisMaskGenerator* mask = KisMaskGenerator::fromXML(brushDefinition.firstChildElement("MaskGenerator"));
double angle = KisDomUtils::toDouble(brushDefinition.attribute("angle", "0.0"));
double randomness = KisDomUtils::toDouble(brushDefinition.attribute("randomness", "0.0"));
qreal density = KisDomUtils::toDouble(brushDefinition.attribute("density", "1.0"));
double spacing = KisDomUtils::toDouble(brushDefinition.attribute("spacing", "1.0"));
bool useAutoSpacing = KisDomUtils::toInt(brushDefinition.attribute("useAutoSpacing", "0"));
qreal autoSpacingCoeff = KisDomUtils::toDouble(brushDefinition.attribute("autoSpacingCoeff", "1.0"));
- KisBrushSP brush = new KisAutoBrush(mask, angle, randomness, density);
+ KisBrushSP brush = KisBrushSP(new KisAutoBrush(mask, angle, randomness, density));
brush->setSpacing(spacing);
brush->setAutoSpacing(useAutoSpacing, autoSpacingCoeff);
return brush;
}
diff --git a/libs/brush/kis_auto_brush_factory.h b/libs/brush/kis_auto_brush_factory.h
index 1d00b102eb..aed0fa40f6 100644
--- a/libs/brush/kis_auto_brush_factory.h
+++ b/libs/brush/kis_auto_brush_factory.h
@@ -1,58 +1,58 @@
/*
* 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_AUTO_BRUSH_FACTORY
#define KIS_AUTO_BRUSH_FACTORY
#include <QString>
#include <QDomElement>
#include <QHash>
#include <KoID.h>
#include "kis_brush.h"
#include "kis_brush_factory.h"
#include "kis_fixed_paint_device.h"
/**
* A brush factory can create a new brush instance based
* on a properties object that contains a serialized representation
* of the object.
*/
class BRUSH_EXPORT KisAutoBrushFactory : public KisBrushFactory
{
public:
KisAutoBrushFactory() {}
~KisAutoBrushFactory() override {}
QString id() const override {
return "auto_brush";
}
/**
* Create a new brush from the given data or return an existing KisBrush
* object. If this call leads to the creation of a resource, it should be
* added to the resource provider, too.
*/
- KisBrushSP createBrush(const QDomElement& brushDefinition) override;
+ KisBrushSP createBrush(const QDomElement& brushDefinition, KisResourcesInterfaceSP resourcesInterface) override;
};
#endif
diff --git a/libs/brush/kis_brush.cpp b/libs/brush/kis_brush.cpp
index 8a0f787edd..54fca2daa2 100644
--- a/libs/brush/kis_brush.cpp
+++ b/libs/brush/kis_brush.cpp
@@ -1,643 +1,636 @@
/*
* Copyright (c) 1999 Matthias Elter <me@kde.org>
* Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004-2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
* Copyright (c) 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.
*/
#include "kis_brush.h"
#include <QDomElement>
#include <QFile>
#include <QPoint>
#include <QFileInfo>
#include <QBuffer>
#include <kis_debug.h>
#include <klocalizedstring.h>
#include <KoColor.h>
#include <KoColorSpaceMaths.h>
#include <KoColorSpaceRegistry.h>
#include "kis_datamanager.h"
#include "kis_paint_device.h"
#include "kis_global.h"
#include "kis_boundary.h"
#include "kis_image.h"
#include "kis_iterator_ng.h"
#include "kis_brush_registry.h"
#include <brushengine/kis_paint_information.h>
#include <kis_fixed_paint_device.h>
#include <kis_qimage_pyramid.h>
#include <KisSharedQImagePyramid.h>
#include <brushengine/kis_paintop_lod_limitations.h>
KisBrush::ColoringInformation::~ColoringInformation()
{
}
KisBrush::PlainColoringInformation::PlainColoringInformation(const quint8* color) : m_color(color)
{
}
KisBrush::PlainColoringInformation::~PlainColoringInformation()
{
}
const quint8* KisBrush::PlainColoringInformation::color() const
{
return m_color;
}
void KisBrush::PlainColoringInformation::nextColumn()
{
}
void KisBrush::PlainColoringInformation::nextRow()
{
}
KisBrush::PaintDeviceColoringInformation::PaintDeviceColoringInformation(const KisPaintDeviceSP source, int width)
: m_source(source)
, m_iterator(m_source->createHLineConstIteratorNG(0, 0, width))
{
}
KisBrush::PaintDeviceColoringInformation::~PaintDeviceColoringInformation()
{
}
const quint8* KisBrush::PaintDeviceColoringInformation::color() const
{
return m_iterator->oldRawData();
}
void KisBrush::PaintDeviceColoringInformation::nextColumn()
{
m_iterator->nextPixel();
}
void KisBrush::PaintDeviceColoringInformation::nextRow()
{
m_iterator->nextRow();
}
struct KisBrush::Private {
Private()
- : boundary(0)
+ : brushType(INVALID)
+ , hasColor(false)
, angle(0)
, scale(1.0)
- , hasColor(false)
- , brushType(INVALID)
, autoSpacingActive(false)
, autoSpacingCoeff(1.0)
, threadingAllowed(true)
{}
+ Private(const Private &rhs)
+ : brushType(rhs.brushType),
+ width(rhs.width),
+ height(rhs.height),
+ spacing(rhs.spacing),
+ hotSpot(rhs.hotSpot),
+ hasColor(rhs.hasColor),
+ angle(rhs.angle),
+ scale(rhs.scale),
+ autoSpacingActive(rhs.autoSpacingActive),
+ autoSpacingCoeff(rhs.autoSpacingCoeff),
+ threadingAllowed(rhs.threadingAllowed),
+ brushTipImage(rhs.brushTipImage),
+ /**
+ * Be careful! The pyramid is shared between two brush objects,
+ * therefore you cannot change it, only recreate! That is the
+ * reason why it is defined as const!
+ */
+ brushPyramid(rhs.brushPyramid)
+
+ {
+ // don't copy the boundary, it will be regenerated -- see bug 291910
+ }
+
~Private() {
- delete boundary;
}
- mutable KisBoundary* boundary;
- qreal angle;
- qreal scale;
- bool hasColor;
+ mutable QScopedPointer<KisBoundary> boundary;
enumBrushType brushType;
qint32 width;
qint32 height;
double spacing;
QPointF hotSpot;
-
- mutable QSharedPointer<KisSharedQImagePyramid> brushPyramid;
-
- QImage brushTipImage;
-
+ bool hasColor;
+ qreal angle;
+ qreal scale;
bool autoSpacingActive;
qreal autoSpacingCoeff;
-
bool threadingAllowed;
+
+ QImage brushTipImage;
+ mutable QSharedPointer<KisSharedQImagePyramid> brushPyramid;
};
KisBrush::KisBrush()
: KoResource(QString())
, d(new Private)
{
}
KisBrush::KisBrush(const QString& filename)
: KoResource(filename)
, d(new Private)
{
}
KisBrush::KisBrush(const KisBrush& rhs)
- : KoResource(QString())
- , KisShared()
- , d(new Private)
+ : KoResource(rhs)
+ , d(new Private(*rhs.d))
{
- setBrushTipImage(rhs.brushTipImage());
- d->brushType = rhs.d->brushType;
- d->width = rhs.d->width;
- d->height = rhs.d->height;
- d->spacing = rhs.d->spacing;
- d->hotSpot = rhs.d->hotSpot;
- d->hasColor = rhs.d->hasColor;
- d->angle = rhs.d->angle;
- d->scale = rhs.d->scale;
- d->autoSpacingActive = rhs.d->autoSpacingActive;
- d->autoSpacingCoeff = rhs.d->autoSpacingCoeff;
- d->threadingAllowed = rhs.d->threadingAllowed;
- setFilename(rhs.filename());
-
- /**
- * Be careful! The pyramid is shared between two brush objects,
- * therefore you cannot change it, only recreate! That i sthe
- * reason why it is defined as const!
- */
- d->brushPyramid = rhs.d->brushPyramid;
-
- // don't copy the boundary, it will be regenerated -- see bug 291910
}
KisBrush::~KisBrush()
{
delete d;
}
QImage KisBrush::brushTipImage() const
{
- if (d->brushTipImage.isNull()) {
- const_cast<KisBrush*>(this)->load();
- }
+ KIS_SAFE_ASSERT_RECOVER_NOOP(!d->brushTipImage.isNull());
return d->brushTipImage;
}
qint32 KisBrush::width() const
{
return d->width;
}
void KisBrush::setWidth(qint32 width)
{
d->width = width;
}
qint32 KisBrush::height() const
{
return d->height;
}
void KisBrush::setHeight(qint32 height)
{
d->height = height;
}
void KisBrush::setHotSpot(QPointF pt)
{
double x = pt.x();
double y = pt.y();
if (x < 0)
x = 0;
else if (x >= width())
x = width() - 1;
if (y < 0)
y = 0;
else if (y >= height())
y = height() - 1;
d->hotSpot = QPointF(x, y);
}
QPointF KisBrush::hotSpot(KisDabShape const& shape, const KisPaintInformation& info) const
{
Q_UNUSED(info);
QSizeF metric = characteristicSize(shape);
qreal w = metric.width();
qreal h = metric.height();
// The smallest brush we can produce is a single pixel.
if (w < 1) {
w = 1;
}
if (h < 1) {
h = 1;
}
// XXX: This should take d->hotSpot into account, though it
// isn't specified by gimp brushes so it would default to the center
// anyway.
QPointF p(w / 2, h / 2);
return p;
}
bool KisBrush::hasColor() const
{
return d->hasColor;
}
void KisBrush::setHasColor(bool hasColor)
{
d->hasColor = hasColor;
}
bool KisBrush::isPiercedApprox() const
{
QImage image = brushTipImage();
qreal w = image.width();
qreal h = image.height();
qreal xPortion = qMin(0.1, 5.0 / w);
qreal yPortion = qMin(0.1, 5.0 / h);
int x0 = std::floor((0.5 - xPortion) * w);
int x1 = std::ceil((0.5 + xPortion) * w);
int y0 = std::floor((0.5 - yPortion) * h);
int y1 = std::ceil((0.5 + yPortion) * h);
const int maxNumSamples = (x1 - x0 + 1) * (y1 - y0 + 1);
const int failedPixelsThreshold = 0.1 * maxNumSamples;
const int thresholdValue = 0.95 * 255;
int failedPixels = 0;
for (int y = y0; y <= y1; y++) {
for (int x = x0; x <= x1; x++) {
QRgb pixel = image.pixel(x,y);
if (qRed(pixel) > thresholdValue) {
failedPixels++;
}
}
}
return failedPixels > failedPixelsThreshold;
}
bool KisBrush::canPaintFor(const KisPaintInformation& /*info*/)
{
return true;
}
void KisBrush::setBrushTipImage(const QImage& image)
{
d->brushTipImage = image;
if (!image.isNull()) {
if (image.width() > 128 || image.height() > 128) {
KoResource::setImage(image.scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation));
}
else {
KoResource::setImage(image);
}
setWidth(image.width());
setHeight(image.height());
}
clearBrushPyramid();
}
void KisBrush::setBrushType(enumBrushType type)
{
d->brushType = type;
}
enumBrushType KisBrush::brushType() const
{
return d->brushType;
}
void KisBrush::predefinedBrushToXML(const QString &type, QDomElement& e) const
{
e.setAttribute("type", type);
- e.setAttribute("filename", shortFilename());
+ e.setAttribute("filename", filename());
e.setAttribute("spacing", QString::number(spacing()));
e.setAttribute("useAutoSpacing", QString::number(autoSpacingActive()));
e.setAttribute("autoSpacingCoeff", QString::number(autoSpacingCoeff()));
e.setAttribute("angle", QString::number(angle()));
e.setAttribute("scale", QString::number(scale()));
}
void KisBrush::toXML(QDomDocument& /*document*/ , QDomElement& element) const
{
element.setAttribute("BrushVersion", "2");
}
-KisBrushSP KisBrush::fromXML(const QDomElement& element)
+KisBrushSP KisBrush::fromXML(const QDomElement& element, KisResourcesInterfaceSP resourcesInterface)
{
- KisBrushSP brush = KisBrushRegistry::instance()->createBrush(element);
+ KisBrushSP brush = KisBrushRegistry::instance()->createBrush(element, resourcesInterface);
if (brush && element.attribute("BrushVersion", "1") == "1") {
brush->setScale(brush->scale() * 2.0);
}
return brush;
}
QSizeF KisBrush::characteristicSize(KisDabShape const& shape) const
{
KisDabShape normalizedShape(
- shape.scale() * d->scale,
- shape.ratio(),
- normalizeAngle(shape.rotation() + d->angle));
+ shape.scale() * d->scale,
+ shape.ratio(),
+ normalizeAngle(shape.rotation() + d->angle));
return KisQImagePyramid::characteristicSize(
- QSize(width(), height()), normalizedShape);
+ QSize(width(), height()), normalizedShape);
}
qint32 KisBrush::maskWidth(KisDabShape const& shape, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const
{
Q_UNUSED(info);
qreal angle = normalizeAngle(shape.rotation() + d->angle);
qreal scale = shape.scale() * d->scale;
return KisQImagePyramid::imageSize(QSize(width(), height()),
KisDabShape(scale, shape.ratio(), angle),
subPixelX, subPixelY).width();
}
qint32 KisBrush::maskHeight(KisDabShape const& shape, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const
{
Q_UNUSED(info);
qreal angle = normalizeAngle(shape.rotation() + d->angle);
qreal scale = shape.scale() * d->scale;
return KisQImagePyramid::imageSize(QSize(width(), height()),
KisDabShape(scale, shape.ratio(), angle),
subPixelX, subPixelY).height();
}
double KisBrush::maskAngle(double angle) const
{
return normalizeAngle(angle + d->angle);
}
quint32 KisBrush::brushIndex(const KisPaintInformation& info) const
{
Q_UNUSED(info);
return 0;
}
void KisBrush::setSpacing(double s)
{
if (s < 0.02) s = 0.02;
d->spacing = s;
}
double KisBrush::spacing() const
{
return d->spacing;
}
void KisBrush::setAutoSpacing(bool active, qreal coeff)
{
d->autoSpacingCoeff = coeff;
d->autoSpacingActive = active;
}
bool KisBrush::autoSpacingActive() const
{
return d->autoSpacingActive;
}
qreal KisBrush::autoSpacingCoeff() const
{
return d->autoSpacingCoeff;
}
void KisBrush::notifyStrokeStarted()
{
}
void KisBrush::notifyCachedDabPainted(const KisPaintInformation& info)
{
Q_UNUSED(info);
}
void KisBrush::prepareForSeqNo(const KisPaintInformation &info, int seqNo)
{
Q_UNUSED(info);
Q_UNUSED(seqNo);
}
void KisBrush::setThreadingAllowed(bool value)
{
d->threadingAllowed = value;
}
bool KisBrush::threadingAllowed() const
{
return d->threadingAllowed;
}
void KisBrush::clearBrushPyramid()
{
d->brushPyramid.reset(new KisSharedQImagePyramid());
}
void KisBrush::mask(KisFixedPaintDeviceSP dst, const KoColor& color, KisDabShape const& shape, const KisPaintInformation& info, double subPixelX, double subPixelY, qreal softnessFactor) const
{
PlainColoringInformation pci(color.data());
generateMaskAndApplyMaskOrCreateDab(dst, &pci, shape, info, subPixelX, subPixelY, softnessFactor);
}
void KisBrush::mask(KisFixedPaintDeviceSP dst, const KisPaintDeviceSP src, KisDabShape const& shape, const KisPaintInformation& info, double subPixelX, double subPixelY, qreal softnessFactor) const
{
PaintDeviceColoringInformation pdci(src, maskWidth(shape, subPixelX, subPixelY, info));
generateMaskAndApplyMaskOrCreateDab(dst, &pdci, shape, info, subPixelX, subPixelY, softnessFactor);
}
void KisBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst,
- ColoringInformation* coloringInformation,
- KisDabShape const& shape,
- const KisPaintInformation& info_,
- double subPixelX, double subPixelY, qreal softnessFactor) const
+ ColoringInformation* coloringInformation,
+ KisDabShape const& shape,
+ const KisPaintInformation& info_,
+ double subPixelX, double subPixelY, qreal softnessFactor) const
{
KIS_SAFE_ASSERT_RECOVER_RETURN(valid());
Q_UNUSED(info_);
Q_UNUSED(softnessFactor);
QImage outputImage = d->brushPyramid->pyramid(this)->createImage(KisDabShape(
- shape.scale() * d->scale, shape.ratio(),
- -normalizeAngle(shape.rotation() + d->angle)),
- subPixelX, subPixelY);
+ shape.scale() * d->scale, shape.ratio(),
+ -normalizeAngle(shape.rotation() + d->angle)),
+ subPixelX, subPixelY);
qint32 maskWidth = outputImage.width();
qint32 maskHeight = outputImage.height();
dst->setRect(QRect(0, 0, maskWidth, maskHeight));
dst->lazyGrowBufferWithoutInitialization();
quint8* color = 0;
if (coloringInformation) {
if (dynamic_cast<PlainColoringInformation*>(coloringInformation)) {
color = const_cast<quint8*>(coloringInformation->color());
}
}
const KoColorSpace *cs = dst->colorSpace();
qint32 pixelSize = cs->pixelSize();
quint8 *dabPointer = dst->data();
quint8 *rowPointer = dabPointer;
quint8 *alphaArray = new quint8[maskWidth];
bool hasColor = this->hasColor();
for (int y = 0; y < maskHeight; y++) {
const quint8* maskPointer = outputImage.constScanLine(y);
if (coloringInformation) {
for (int x = 0; x < maskWidth; x++) {
if (color) {
memcpy(dabPointer, color, pixelSize);
}
else {
memcpy(dabPointer, coloringInformation->color(), pixelSize);
coloringInformation->nextColumn();
}
dabPointer += pixelSize;
}
}
if (hasColor) {
const quint8 *src = maskPointer;
quint8 *dst = alphaArray;
for (int x = 0; x < maskWidth; x++) {
const QRgb *c = reinterpret_cast<const QRgb*>(src);
*dst = KoColorSpaceMaths<quint8>::multiply(255 - qGray(*c), qAlpha(*c));
src += 4;
dst++;
}
}
else {
const quint8 *src = maskPointer;
quint8 *dst = alphaArray;
for (int x = 0; x < maskWidth; x++) {
const QRgb *c = reinterpret_cast<const QRgb*>(src);
*dst = KoColorSpaceMaths<quint8>::multiply(255 - *src, qAlpha(*c));
src += 4;
dst++;
}
}
cs->applyAlphaU8Mask(rowPointer, alphaArray, maskWidth);
rowPointer += maskWidth * pixelSize;
dabPointer = rowPointer;
if (!color && coloringInformation) {
coloringInformation->nextRow();
}
}
delete[] alphaArray;
}
KisFixedPaintDeviceSP KisBrush::paintDevice(const KoColorSpace * colorSpace,
- KisDabShape const& shape,
- const KisPaintInformation& info,
- double subPixelX, double subPixelY) const
+ KisDabShape const& shape,
+ const KisPaintInformation& info,
+ double subPixelX, double subPixelY) const
{
Q_ASSERT(valid());
Q_UNUSED(info);
double angle = normalizeAngle(shape.rotation() + d->angle);
double scale = shape.scale() * d->scale;
QImage outputImage = d->brushPyramid->pyramid(this)->createImage(
- KisDabShape(scale, shape.ratio(), -angle), subPixelX, subPixelY);
+ KisDabShape(scale, shape.ratio(), -angle), subPixelX, subPixelY);
KisFixedPaintDeviceSP dab = new KisFixedPaintDevice(colorSpace);
Q_CHECK_PTR(dab);
dab->convertFromQImage(outputImage, "");
return dab;
}
void KisBrush::resetBoundary()
{
- delete d->boundary;
- d->boundary = 0;
+ d->boundary.reset();
}
void KisBrush::generateBoundary() const
{
KisFixedPaintDeviceSP dev;
KisDabShape inverseTransform(1.0 / scale(), 1.0, -angle());
if (brushType() == IMAGE || brushType() == PIPE_IMAGE) {
dev = paintDevice(KoColorSpaceRegistry::instance()->rgb8(),
- inverseTransform, KisPaintInformation());
+ inverseTransform, KisPaintInformation());
}
else {
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
dev = new KisFixedPaintDevice(cs);
mask(dev, KoColor(Qt::black, cs), inverseTransform, KisPaintInformation());
}
- d->boundary = new KisBoundary(dev);
+ d->boundary.reset(new KisBoundary(dev));
d->boundary->generateBoundary();
}
const KisBoundary* KisBrush::boundary() const
{
if (!d->boundary)
generateBoundary();
- return d->boundary;
+ return d->boundary.data();
}
void KisBrush::setScale(qreal _scale)
{
d->scale = _scale;
}
qreal KisBrush::scale() const
{
return d->scale;
}
void KisBrush::setAngle(qreal _rotation)
{
d->angle = _rotation;
}
qreal KisBrush::angle() const
{
return d->angle;
}
QPainterPath KisBrush::outline() const
{
return boundary()->path();
}
void KisBrush::lodLimitations(KisPaintopLodLimitations *l) const
{
if (spacing() > 0.5) {
l->limitations << KoID("huge-spacing", i18nc("PaintOp instant preview limitation", "Spacing > 0.5, consider disabling Instant Preview"));
}
}
diff --git a/libs/brush/kis_brush.h b/libs/brush/kis_brush.h
index b7c8af4a68..2f914c3f16 100644
--- a/libs/brush/kis_brush.h
+++ b/libs/brush/kis_brush.h
@@ -1,397 +1,380 @@
/*
* 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 <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;
+typedef QSharedPointer<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
+class BRUSH_EXPORT KisBrush : public KoResource
{
-
-
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;
+ KisBrush(const KisBrush &rhs);
+ KisBrush &operator=(const KisBrush &rhs) = delete;
+
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;
+ QPair<QString, QString> resourceType() const override {
+ return QPair<QString, QString>(ResourceType::Brushes, "");
}
/**
* @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 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);
+ static KisBrushSP fromXML(const QDomElement& element, KisResourcesInterfaceSP resourcesInterface);
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/brush/kis_brush_factory.h b/libs/brush/kis_brush_factory.h
index 1201e12674..d409d9e0b8 100644
--- a/libs/brush/kis_brush_factory.h
+++ b/libs/brush/kis_brush_factory.h
@@ -1,54 +1,54 @@
/*
* 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_BRUSH_FACTORY
#define KIS_BRUSH_FACTORY
#include "kis_brush.h"
class QDomElement;
/**
* A brush factory can create a new brush instance based
* on a properties object that contains a serialized representation
* of the object.
*/
class BRUSH_EXPORT KisBrushFactory
{
public:
KisBrushFactory() {}
virtual ~KisBrushFactory() {}
virtual QString id() const = 0;
virtual QString name() const {
return QString();
}
/**
* Create a new brush from the given data or return an existing KisBrush
* object. If this call leads to the creation of a resource, it should be
* added to the resource provider, too.
*/
- virtual KisBrushSP createBrush(const QDomElement& element) = 0;
+ virtual KisBrushSP createBrush(const QDomElement& element, KisResourcesInterfaceSP resourcesInterface) = 0;
};
#endif
diff --git a/libs/brush/kis_brush_registry.cpp b/libs/brush/kis_brush_registry.cpp
index 811de1a003..9d5cf90ce3 100644
--- a/libs/brush/kis_brush_registry.cpp
+++ b/libs/brush/kis_brush_registry.cpp
@@ -1,75 +1,74 @@
/*
* 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.
*/
#include "kis_brush_registry.h"
#include <QString>
#include <QGlobalStatic>
#include <klocalizedstring.h>
#include <KoPluginLoader.h>
#include <kis_debug.h>
-#include "kis_brush_server.h"
+#include "KoResourceServer.h"
#include "kis_auto_brush_factory.h"
#include "kis_text_brush_factory.h"
#include "kis_predefined_brush_factory.h"
Q_GLOBAL_STATIC(KisBrushRegistry, s_instance)
KisBrushRegistry::KisBrushRegistry()
{
- KisBrushServer::instance();
}
KisBrushRegistry::~KisBrushRegistry()
{
Q_FOREACH (const QString & id, keys()) {
delete get(id);
}
dbgRegistry << "deleting KisBrushRegistry";
}
KisBrushRegistry* KisBrushRegistry::instance()
{
if (!s_instance.exists()) {
s_instance->add(new KisAutoBrushFactory());
s_instance->add(new KisPredefinedBrushFactory("gbr_brush"));
s_instance->add(new KisPredefinedBrushFactory("abr_brush"));
s_instance->add(new KisTextBrushFactory());
s_instance->add(new KisPredefinedBrushFactory("png_brush"));
s_instance->add(new KisPredefinedBrushFactory("svg_brush"));
}
return s_instance;
}
-KisBrushSP KisBrushRegistry::createBrush(const QDomElement& element)
+KisBrushSP KisBrushRegistry::createBrush(const QDomElement& element, KisResourcesInterfaceSP resourcesInterface)
{
QString brushType = element.attribute("type");
if (brushType.isEmpty()) return 0;
KisBrushFactory* factory = get(brushType);
if (!factory) return 0;
- return factory->createBrush(element);
+ return factory->createBrush(element, resourcesInterface);
}
diff --git a/libs/brush/kis_brush_registry.h b/libs/brush/kis_brush_registry.h
index 1713cc067b..43226c04c0 100644
--- a/libs/brush/kis_brush_registry.h
+++ b/libs/brush/kis_brush_registry.h
@@ -1,52 +1,52 @@
/*
* 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_BRUSH_REGISTRY_H_
#define KIS_BRUSH_REGISTRY_H_
#include <QObject>
#include "kis_types.h"
#include "KoGenericRegistry.h"
#include <kritabrush_export.h>
#include "kis_brush.h"
#include "kis_brush_factory.h"
class QDomElement;
class BRUSH_EXPORT KisBrushRegistry : public QObject, public KoGenericRegistry<KisBrushFactory*>
{
Q_OBJECT
public:
KisBrushRegistry();
~KisBrushRegistry() override;
static KisBrushRegistry* instance();
- KisBrushSP createBrush(const QDomElement& element);
+ KisBrushSP createBrush(const QDomElement& element, KisResourcesInterfaceSP resourcesInterface);
private:
KisBrushRegistry(const KisBrushRegistry&);
KisBrushRegistry operator=(const KisBrushRegistry&);
};
#endif // KIS_GENERATOR_REGISTRY_H_
diff --git a/libs/brush/kis_brush_server.cpp b/libs/brush/kis_brush_server.cpp
deleted file mode 100644
index 414980a2f0..0000000000
--- a/libs/brush/kis_brush_server.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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.
- */
-#include "kis_brush_server.h"
-
-#include <QDir>
-#include <QApplication>
-
-#include <QGlobalStatic>
-#include <KoResourcePaths.h>
-
-#include <resources/KoResource.h>
-#include <KoResourceServerProvider.h>
-
-#include <kis_debug.h>
-
-#include "kis_abr_brush.h"
-#include "kis_abr_brush_collection.h"
-#include "kis_gbr_brush.h"
-#include "kis_imagepipe_brush.h"
-#include "kis_png_brush.h"
-#include "kis_svg_brush.h"
-
-Q_GLOBAL_STATIC(KisBrushServer, s_instance)
-
-
-class BrushResourceServer : public KisBrushResourceServer
-{
-
-public:
-
- BrushResourceServer()
- : KisBrushResourceServer("kis_brushes", "*.gbr:*.gih:*.abr:*.png:*.svg")
- {
- }
-
- ///Reimplemented
- bool importResourceFile(const QString& filename, bool fileCreation = true) override {
- QFileInfo fi(filename);
- if (fi.exists() == false)
- return false;
-
- if (fi.size() == 0) return false;
-
- if (fi.suffix().toLower() == "abr") {
- if (fileCreation) {
- QFile::copy(filename, saveLocation() + fi.fileName());
- }
- QList<KisBrushSP> collectionResources = createResources(filename);
- Q_FOREACH (KisBrushSP brush, collectionResources) {
- addResource(brush);
- }
- }
- else {
-
- return KisBrushResourceServer::importResourceFile(filename, fileCreation);
-
- }
- qApp->processEvents(QEventLoop::AllEvents);
- return true;
- }
-
-private:
-
- ///Reimplemented
- QList<KisBrushSP> createResources(const QString & filename) override {
- QList<KisBrushSP> brushes;
-
- QString fileExtension = QFileInfo(filename).suffix().toLower();
- if (fileExtension == "abr") {
- KisAbrBrushCollection collection(filename);
- collection.load();
- Q_FOREACH (KisAbrBrush * abrBrush, collection.brushes()) {
-// abrBrush->setBrushTipImage(QImage());
- brushes.append(abrBrush);
- addTag(abrBrush, collection.filename());
- }
- }
- else {
- brushes.append(createResource(filename));
- }
- return brushes;
- }
-
- ///Reimplemented
- KisBrushSP createResource(const QString & filename) override {
-
- QString fileExtension = QFileInfo(filename).suffix().toLower();
-
- KisBrushSP brush;
-
- if (fileExtension == "gbr") {
- brush = new KisGbrBrush(filename);
- }
- else if (fileExtension == "gih") {
- brush = new KisImagePipeBrush(filename);
- }
- else if (fileExtension == "png") {
- brush = new KisPngBrush(filename);
- }
- else if (fileExtension == "svg") {
- brush = new KisSvgBrush(filename);
- }
- return brush;
- }
-};
-
-KisBrushServer::KisBrushServer()
-{
- m_brushServer = new BrushResourceServer();
- m_brushServer->loadResources(KoResourceServerProvider::blacklistFileNames(m_brushServer->fileNames(), m_brushServer->blackListedFiles()));
-
- Q_FOREACH (KisBrushSP brush, m_brushServer->resources()) {
- if (!dynamic_cast<KisAbrBrush*>(brush.data())) {
- brush->setBrushTipImage(QImage());
- }
- }
-}
-
-KisBrushServer::~KisBrushServer()
-{
- delete m_brushServer;
-}
-
-KisBrushServer* KisBrushServer::instance()
-{
- return s_instance;
-}
-
-KisBrushResourceServer* KisBrushServer::brushServer()
-{
- return m_brushServer;
-}
-
-void KisBrushServer::slotRemoveBlacklistedResources()
-{
- m_brushServer->removeBlackListedFiles();
-}
-
diff --git a/libs/brush/kis_brush_server.h b/libs/brush/kis_brush_server.h
deleted file mode 100644
index 4ce7b92092..0000000000
--- a/libs/brush/kis_brush_server.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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_BRUSH_SERVER_H
-#define KIS_BRUSH_SERVER_H
-
-#include <QString>
-#include <QStringList>
-#include <QList>
-
-#include <KoResourceServer.h>
-#include <KoResourceServerAdapter.h>
-
-#include "kritabrush_export.h"
-#include "kis_brush.h"
-
-typedef KoResourceServer<KisBrush, SharedPointerStoragePolicy<KisBrushSP> > KisBrushResourceServer;
-typedef KoResourceServerAdapter<KisBrush, SharedPointerStoragePolicy<KisBrushSP> > KisBrushResourceServerAdapter;
-
-/**
- *
- */
-class BRUSH_EXPORT KisBrushServer : public QObject
-{
-
- Q_OBJECT
-
-public:
- KisBrushServer();
- ~KisBrushServer() override;
- KisBrushResourceServer* brushServer();
-
- static KisBrushServer* instance();
-
-public Q_SLOTS:
- void slotRemoveBlacklistedResources();
-
-private:
-
- KisBrushServer(const KisBrushServer&);
- KisBrushServer operator=(const KisBrushServer&);
-
- KisBrushResourceServer* m_brushServer;
-};
-
-#endif
diff --git a/libs/brush/kis_brushes_pipe.h b/libs/brush/kis_brushes_pipe.h
index 4bf4c90f0a..804fef40e6 100644
--- a/libs/brush/kis_brushes_pipe.h
+++ b/libs/brush/kis_brushes_pipe.h
@@ -1,195 +1,194 @@
/*
* 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_BRUSHES_PIPE_H
#define __KIS_BRUSHES_PIPE_H
#include <kis_fixed_paint_device.h>
+#include <kis_brush.h>
template<class BrushType>
class KisBrushesPipe
{
public:
KisBrushesPipe() {
}
KisBrushesPipe(const KisBrushesPipe &rhs) {
- qDeleteAll(m_brushes);
m_brushes.clear();
- Q_FOREACH (BrushType * brush, rhs.m_brushes) {
- BrushType *clonedBrush = dynamic_cast<BrushType*>(brush->clone());
+ Q_FOREACH (QSharedPointer<BrushType> brush, rhs.m_brushes) {
+ KoResourceSP clonedBrush = brush->clone();
+ QSharedPointer<BrushType> actualClonedBrush = clonedBrush.dynamicCast<BrushType>();
+ m_brushes.append(actualClonedBrush );
KIS_ASSERT_RECOVER(clonedBrush) {continue;}
-
- m_brushes.append(clonedBrush);
}
}
virtual ~KisBrushesPipe() {
- qDeleteAll(m_brushes);
}
virtual void clear() {
- qDeleteAll(m_brushes);
m_brushes.clear();
}
- BrushType* firstBrush() const {
+ QSharedPointer<BrushType> firstBrush() const {
return m_brushes.first();
}
- BrushType* lastBrush() const {
+ QSharedPointer<BrushType> lastBrush() const {
return m_brushes.last();
}
- BrushType* currentBrush(const KisPaintInformation& info) {
+
+ QSharedPointer<BrushType> currentBrush(const KisPaintInformation& info) {
Q_UNUSED(info);
return !m_brushes.isEmpty() ? m_brushes.at(currentBrushIndex()) : 0;
}
int brushIndex(const KisPaintInformation& info) {
return chooseNextBrush(info);
}
qint32 maskWidth(KisDabShape const& shape, double subPixelX, double subPixelY, const KisPaintInformation& info) {
- BrushType *brush = currentBrush(info);
+ QSharedPointer<BrushType> brush = currentBrush(info);
return brush ? brush->maskWidth(shape, subPixelX, subPixelY, info) : 0;
}
qint32 maskHeight(KisDabShape const& shape, double subPixelX, double subPixelY, const KisPaintInformation& info) {
- BrushType *brush = currentBrush(info);
+ QSharedPointer<BrushType> brush = currentBrush(info);
return brush ? brush->maskHeight(shape, subPixelX, subPixelY, info) : 0;
}
void setAngle(qreal angle) {
- Q_FOREACH (BrushType * brush, m_brushes) {
+ Q_FOREACH (QSharedPointer<BrushType> brush, m_brushes) {
brush->setAngle(angle);
}
}
void setScale(qreal scale) {
- Q_FOREACH (BrushType * brush, m_brushes) {
+ Q_FOREACH (QSharedPointer<BrushType> brush, m_brushes) {
brush->setScale(scale);
}
}
void setSpacing(double spacing) {
- Q_FOREACH (BrushType * brush, m_brushes) {
+ Q_FOREACH (QSharedPointer<BrushType> brush, m_brushes) {
brush->setSpacing(spacing);
}
}
bool hasColor() const {
- Q_FOREACH (BrushType * brush, m_brushes) {
+ Q_FOREACH (QSharedPointer<BrushType> brush, m_brushes) {
if (brush->hasColor()) return true;
}
return false;
}
void notifyCachedDabPainted(const KisPaintInformation& info) {
updateBrushIndexes(info, -1);
}
void prepareForSeqNo(const KisPaintInformation& info, int seqNo) {
updateBrushIndexes(info, seqNo);
}
void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation,
KisDabShape const& shape,
const KisPaintInformation& info,
double subPixelX , double subPixelY,
qreal softnessFactor) {
- BrushType *brush = currentBrush(info);
+ QSharedPointer<BrushType> brush = currentBrush(info);
if (!brush) return;
brush->generateMaskAndApplyMaskOrCreateDab(dst, coloringInformation, shape, info, subPixelX, subPixelY, softnessFactor);
notifyCachedDabPainted(info);
}
KisFixedPaintDeviceSP paintDevice(const KoColorSpace * colorSpace,
KisDabShape const& shape,
const KisPaintInformation& info,
double subPixelX, double subPixelY) {
- BrushType *brush = currentBrush(info);
+ QSharedPointer<BrushType> brush = currentBrush(info);
if (!brush) return 0;
KisFixedPaintDeviceSP device = brush->paintDevice(colorSpace, shape, info, subPixelX, subPixelY);
notifyCachedDabPainted(info);
return device;
}
- QVector<BrushType*> brushes() {
+ QVector<QSharedPointer<BrushType>> brushes() {
return m_brushes;
}
void testingSelectNextBrush(const KisPaintInformation& info) {
(void) chooseNextBrush(info);
notifyCachedDabPainted(info);
}
/**
* Is called by the paint op when a paintop starts a stroke. The
* brushes are shared among different strokes, so sometimes the
* brush should be reset.
*/
virtual void notifyStrokeStarted() = 0;
protected:
- void addBrush(BrushType *brush) {
+ void addBrush(QSharedPointer<BrushType> brush) {
m_brushes.append(brush);
}
int sizeBrush() {
return m_brushes.size();
}
/**
* Returns the index of the next brush that corresponds to the current
* values of \p info. This method is called *before* the dab is
* actually painted.
*
*/
virtual int chooseNextBrush(const KisPaintInformation& info) = 0;
/**
* Returns the current index of the brush
* This method is called *before* the dab is
* actually painted.
*
* The method is const, so no internal counters of the brush should
* change during its execution
*/
virtual int currentBrushIndex() = 0;
/**
* Updates internal counters of the brush *after* a dab has been
* painted on the canvas. Some incremental switching of the brushes
* may me implemented in this method.
*
* If \p seqNo is equal or greater than zero, then incremental iteration is
* overridden by this seqNo value
*/
virtual void updateBrushIndexes(const KisPaintInformation& info, int seqNo) = 0;
protected:
- QVector<BrushType*> m_brushes;
+ QVector<QSharedPointer<BrushType>> m_brushes;
};
#endif /* __KIS_BRUSHES_PIPE_H */
diff --git a/libs/brush/kis_gbr_brush.cpp b/libs/brush/kis_gbr_brush.cpp
index a8514ce8d6..5ed0763684 100644
--- a/libs/brush/kis_gbr_brush.cpp
+++ b/libs/brush/kis_gbr_brush.cpp
@@ -1,506 +1,494 @@
/*
* Copyright (c) 1999 Matthias Elter <me@kde.org>
* Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
* Copyright (c) 2007 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.
*/
#include <sys/types.h>
#include <QtEndian>
#include "kis_gbr_brush.h"
#include <QDomElement>
#include <QFile>
#include <QImage>
#include <QPoint>
#include <kis_debug.h>
#include <klocalizedstring.h>
#include <KoColor.h>
#include <KoColorSpaceRegistry.h>
#include "kis_datamanager.h"
#include "kis_paint_device.h"
#include "kis_global.h"
#include "kis_image.h"
struct GimpBrushV1Header {
quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */
quint32 version; /* brush file version # */
quint32 width; /* width of brush */
quint32 height; /* height of brush */
quint32 bytes; /* depth of brush in bytes */
};
/// All fields are in MSB on disk!
struct GimpBrushHeader {
quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */
quint32 version; /* brush file version # */
quint32 width; /* width of brush */
quint32 height; /* height of brush */
quint32 bytes; /* depth of brush in bytes */
/* The following are only defined in version 2 */
quint32 magic_number; /* GIMP brush magic number */
quint32 spacing; /* brush spacing as % of width & height, 0 - 1000 */
};
// Needed, or the GIMP won't open it!
quint32 const GimpV2BrushMagic = ('G' << 24) + ('I' << 16) + ('M' << 8) + ('P' << 0);
struct KisGbrBrush::Private {
QByteArray data;
- bool ownData; /* seems to indicate that @ref data is owned by the brush, but in Qt4.x this is already guaranteed... so in reality it seems more to indicate whether the data is loaded from file (ownData = true) or memory (ownData = false) */
-
bool useColorAsMask;
quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */
quint32 version; /* brush file version # */
quint32 bytes; /* depth of brush in bytes */
quint32 magic_number; /* GIMP brush magic number */
};
#define DEFAULT_SPACING 0.25
KisGbrBrush::KisGbrBrush(const QString& filename)
: KisScalingSizeBrush(filename)
, d(new Private)
{
- d->ownData = true;
d->useColorAsMask = false;
setHasColor(false);
setSpacing(DEFAULT_SPACING);
}
-KisGbrBrush::KisGbrBrush(const QString& filename,
- const QByteArray& data,
- qint32 & dataPos)
+KisGbrBrush::KisGbrBrush(const QString &filename,
+ const QByteArray &data,
+ qint32 &dataPos)
: KisScalingSizeBrush(filename)
, d(new Private)
{
- d->ownData = false;
d->useColorAsMask = false;
setHasColor(false);
setSpacing(DEFAULT_SPACING);
d->data = QByteArray::fromRawData(data.data() + dataPos, data.size() - dataPos);
init();
d->data.clear();
dataPos += d->header_size + (width() * height() * d->bytes);
}
KisGbrBrush::KisGbrBrush(KisPaintDeviceSP image, int x, int y, int w, int h)
: KisScalingSizeBrush()
, d(new Private)
{
- d->ownData = true;
d->useColorAsMask = false;
setHasColor(false);
setSpacing(DEFAULT_SPACING);
initFromPaintDev(image, x, y, w, h);
}
KisGbrBrush::KisGbrBrush(const QImage& image, const QString& name)
: KisScalingSizeBrush()
, d(new Private)
{
- d->ownData = false;
d->useColorAsMask = false;
setHasColor(false);
setSpacing(DEFAULT_SPACING);
setBrushTipImage(image);
setName(name);
}
KisGbrBrush::KisGbrBrush(const KisGbrBrush& rhs)
: KisScalingSizeBrush(rhs)
, d(new Private(*rhs.d))
{
d->data = QByteArray();
}
-KisGbrBrush::~KisGbrBrush()
+KoResourceSP KisGbrBrush::clone() const
{
- delete d;
+ return KoResourceSP(new KisGbrBrush(*this));
}
-bool KisGbrBrush::load()
+KisGbrBrush::~KisGbrBrush()
{
- QFile file(filename());
- if (file.size() == 0) return false;
- file.open(QIODevice::ReadOnly);
- bool res = loadFromDevice(&file);
- file.close();
-
- return res;
+ delete d;
}
-bool KisGbrBrush::loadFromDevice(QIODevice *dev)
+bool KisGbrBrush::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
{
- if (d->ownData) {
- d->data = dev->readAll();
- }
+ Q_UNUSED(resourcesInterface);
+ d->data = dev->readAll();
return init();
}
bool KisGbrBrush::init()
{
GimpBrushHeader bh;
if (sizeof(GimpBrushHeader) > (uint)d->data.size()) {
+ qWarning() << filename() << "GBR could not be loaded: expected header size larger than bytearray size. Header Size:" << sizeof(GimpBrushHeader) << "Byte array size" << d->data.size();
return false;
}
memcpy(&bh, d->data, sizeof(GimpBrushHeader));
bh.header_size = qFromBigEndian(bh.header_size);
d->header_size = bh.header_size;
bh.version = qFromBigEndian(bh.version);
d->version = bh.version;
bh.width = qFromBigEndian(bh.width);
bh.height = qFromBigEndian(bh.height);
bh.bytes = qFromBigEndian(bh.bytes);
d->bytes = bh.bytes;
bh.magic_number = qFromBigEndian(bh.magic_number);
d->magic_number = bh.magic_number;
if (bh.version == 1) {
// No spacing in version 1 files so use Gimp default
bh.spacing = static_cast<int>(DEFAULT_SPACING * 100);
}
else {
bh.spacing = qFromBigEndian(bh.spacing);
if (bh.spacing > 1000) {
+ qWarning() << filename() << "GBR could not be loaded, spacing above 1000. Spacing:" << bh.spacing;
return false;
}
}
setSpacing(bh.spacing / 100.0);
if (bh.header_size > (uint)d->data.size() || bh.header_size == 0) {
+ qWarning() << "GBR could not be loaded: header size larger than bytearray size. Header Size:" << bh.header_size << "Byte array size" << d->data.size();
return false;
}
QString name;
if (bh.version == 1) {
// Version 1 has no magic number or spacing, so the name
// is at a different offset. Character encoding is undefined.
const char *text = d->data.constData() + sizeof(GimpBrushV1Header);
name = QString::fromLatin1(text, bh.header_size - sizeof(GimpBrushV1Header) - 1);
}
else {
// ### Version = 3->cinepaint; may be float16 data!
// Version >=2: UTF-8 encoding is used
name = QString::fromUtf8(d->data.constData() + sizeof(GimpBrushHeader),
bh.header_size - sizeof(GimpBrushHeader) - 1);
}
setName(name);
if (bh.width == 0 || bh.height == 0) {
+ qWarning() << filename() << "GBR loading failed: width or height is 0" << bh.width << bh.height;
return false;
}
QImage::Format imageFormat;
if (bh.bytes == 1) {
imageFormat = QImage::Format_Indexed8;
} else {
imageFormat = QImage::Format_ARGB32;
}
QImage image(QImage(bh.width, bh.height, imageFormat));
if (image.isNull()) {
+ qWarning() << filename() << "GBR loading failed; image could not be created from following dimensions" << bh.width << bh.height
+ << "QImage::Format" << imageFormat;
return false;
}
qint32 k = bh.header_size;
if (bh.bytes == 1) {
QVector<QRgb> table;
for (int i = 0; i < 256; ++i) table.append(qRgb(i, i, i));
image.setColorTable(table);
// Grayscale
if (static_cast<qint32>(k + bh.width * bh.height) > d->data.size()) {
+ qWarning() << filename() << "GBR file dimensions bigger than bytearray size. Header:"<< k << "Width:" << bh.width << "height" << bh.height
+ << "expected byte array size:" << (k + (bh.width * bh.height)) << "actual byte array size" << d->data.size();
return false;
}
setHasColor(false);
for (quint32 y = 0; y < bh.height; y++) {
uchar *pixel = reinterpret_cast<uchar *>(image.scanLine(y));
for (quint32 x = 0; x < bh.width; x++, k++) {
qint32 val = 255 - static_cast<uchar>(d->data[k]);
*pixel = val;
++pixel;
}
}
} else if (bh.bytes == 4) {
// RGBA
if (static_cast<qint32>(k + (bh.width * bh.height * 4)) > d->data.size()) {
+ qWarning() << filename() << "GBR file dimensions bigger than bytearray size. Header:"<< k << "Width:" << bh.width << "height" << bh.height
+ << "expected byte array size:" << (k + (bh.width * bh.height * 4)) << "actual byte array size" << d->data.size();
return false;
}
setHasColor(true);
for (quint32 y = 0; y < bh.height; y++) {
QRgb *pixel = reinterpret_cast<QRgb *>(image.scanLine(y));
for (quint32 x = 0; x < bh.width; x++, k += 4) {
*pixel = qRgba(d->data[k], d->data[k + 1], d->data[k + 2], d->data[k + 3]);
++pixel;
}
}
}
else {
- warnKrita << "WARNING: loading of GBR brushes with" << bh.bytes << "bytes per pixel is not supported";
+ warnKrita << filename() << "WARNING: loading of GBR brushes with" << bh.bytes << "bytes per pixel is not supported";
return false;
}
setWidth(image.width());
setHeight(image.height());
- if (d->ownData) {
+ if (!d->data.isEmpty()) {
d->data.resize(0); // Save some memory, we're using enough of it as it is.
}
setValid(image.width() != 0 && image.height() != 0);
setBrushTipImage(image);
return true;
}
bool KisGbrBrush::initFromPaintDev(KisPaintDeviceSP image, int x, int y, int w, int h)
{
// Forcefully convert to RGBA8
// XXX profile and exposure?
setBrushTipImage(image->convertToQImage(0, x, y, w, h, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()));
setName(image->objectName());
setHasColor(true);
return true;
}
-bool KisGbrBrush::save()
-{
- QFile file(filename());
- file.open(QIODevice::WriteOnly | QIODevice::Truncate);
- bool ok = saveToDevice(&file);
- file.close();
- return ok;
-}
-
bool KisGbrBrush::saveToDevice(QIODevice* dev) const
{
+ if (!valid() || brushTipImage().isNull()) {
+ qWarning() << "this brush is not valid, set a brush tip image" << filename();
+ return false;
+ }
GimpBrushHeader bh;
QByteArray utf8Name = name().toUtf8(); // Names in v2 brushes are in UTF-8
char const* name = utf8Name.data();
int nameLength = qstrlen(name);
int wrote;
bh.header_size = qToBigEndian((quint32)sizeof(GimpBrushHeader) + nameLength + 1);
bh.version = qToBigEndian((quint32)2); // Only RGBA8 data needed atm, no cinepaint stuff
bh.width = qToBigEndian((quint32)width());
bh.height = qToBigEndian((quint32)height());
// Hardcoded, 4 bytes RGBA or 1 byte GREY
if (!hasColor()) {
bh.bytes = qToBigEndian((quint32)1);
}
else {
bh.bytes = qToBigEndian((quint32)4);
}
bh.magic_number = qToBigEndian((quint32)GimpV2BrushMagic);
bh.spacing = qToBigEndian(static_cast<quint32>(spacing() * 100.0));
// Write header: first bh, then the name
QByteArray bytes = QByteArray::fromRawData(reinterpret_cast<char*>(&bh), sizeof(GimpBrushHeader));
wrote = dev->write(bytes);
bytes.clear();
if (wrote == -1) {
return false;
}
wrote = dev->write(name, nameLength + 1);
if (wrote == -1) {
return false;
}
int k = 0;
QImage image = brushTipImage();
if (!hasColor()) {
bytes.resize(width() * height());
for (qint32 y = 0; y < height(); y++) {
for (qint32 x = 0; x < width(); x++) {
QRgb c = image.pixel(x, y);
bytes[k++] = static_cast<char>(255 - qRed(c)); // red == blue == green
}
}
} else {
bytes.resize(width() * height() * 4);
for (qint32 y = 0; y < height(); y++) {
for (qint32 x = 0; x < width(); x++) {
// order for gimp brushes, v2 is: RGBA
QRgb pixel = image.pixel(x, y);
bytes[k++] = static_cast<char>(qRed(pixel));
bytes[k++] = static_cast<char>(qGreen(pixel));
bytes[k++] = static_cast<char>(qBlue(pixel));
bytes[k++] = static_cast<char>(qAlpha(pixel));
}
}
}
wrote = dev->write(bytes);
if (wrote == -1) {
return false;
}
KoResource::saveToDevice(dev);
return true;
}
QImage KisGbrBrush::brushTipImage() const
{
QImage image = KisBrush::brushTipImage();
- if (hasColor() && useColorAsMask()) {
+ if (hasColor() && useColorAsMask() && !image.isNull()) {
for (int y = 0; y < image.height(); y++) {
QRgb *pixel = reinterpret_cast<QRgb *>(image.scanLine(y));
for (int x = 0; x < image.width(); x++) {
QRgb c = pixel[x];
- int a = qGray(c);
- pixel[x] = qRgba(a, a, a, qAlpha(c));
+ float alpha = qAlpha(c) / 255.0f;
+ int a = 255 + int(alpha * (qGray(c) - 255));
+ pixel[x] = qRgba(a, a, a, 255);
}
}
}
return image;
}
enumBrushType KisGbrBrush::brushType() const
{
return !hasColor() || useColorAsMask() ? MASK : IMAGE;
}
void KisGbrBrush::setBrushType(enumBrushType type)
{
Q_UNUSED(type);
qFatal("FATAL: protected member setBrushType has no meaning for KisGbrBrush");
}
void KisGbrBrush::setBrushTipImage(const QImage& image)
{
KisBrush::setBrushTipImage(image);
setValid(true);
}
void KisGbrBrush::makeMaskImage()
{
if (!hasColor()) {
return;
}
QImage brushTip = brushTipImage();
if (brushTip.width() == width() && brushTip.height() == height()) {
- int imageWidth = width();
- int imageHeight = height();
+ int imageWidth = brushTip.width();
+ int imageHeight = brushTip.height();
QImage image(imageWidth, imageHeight, QImage::Format_Indexed8);
QVector<QRgb> table;
for (int i = 0; i < 256; ++i) {
table.append(qRgb(i, i, i));
}
image.setColorTable(table);
for (int y = 0; y < imageHeight; y++) {
QRgb *pixel = reinterpret_cast<QRgb *>(brushTip.scanLine(y));
uchar * dstPixel = image.scanLine(y);
for (int x = 0; x < imageWidth; x++) {
QRgb c = pixel[x];
float alpha = qAlpha(c) / 255.0f;
// linear interpolation with maximum gray value which is transparent in the mask
//int a = (qGray(c) * alpha) + ((1.0 - alpha) * 255);
// single multiplication version
- int a = 255 + alpha * (qGray(c) - 255);
+ int a = 255 + int(alpha * (qGray(c) - 255));
dstPixel[x] = (uchar)a;
}
}
setBrushTipImage(image);
}
setHasColor(false);
setUseColorAsMask(false);
resetBoundary();
clearBrushPyramid();
}
-KisBrush* KisGbrBrush::clone() const
-{
- return new KisGbrBrush(*this);
-}
-
void KisGbrBrush::toXML(QDomDocument& d, QDomElement& e) const
{
predefinedBrushToXML("gbr_brush", e);
e.setAttribute("ColorAsMask", QString::number((int)useColorAsMask()));
KisBrush::toXML(d, e);
}
void KisGbrBrush::setUseColorAsMask(bool useColorAsMask)
{
/**
* WARNING: There is a problem in the brush server, since it
* returns not copies of brushes, but direct pointers to them. It
* means that the brushes are shared among all the currently
* present paintops, which might be a problem for e.g. Multihand
* Brush Tool.
*
* Right now, all the instances of Multihand Brush Tool share the
* same brush, so there is no problem in this sharing, unless we
* reset the internal state of the brush on our way.
*/
if (useColorAsMask != d->useColorAsMask) {
d->useColorAsMask = useColorAsMask;
resetBoundary();
clearBrushPyramid();
}
}
bool KisGbrBrush::useColorAsMask() const
{
return d->useColorAsMask;
}
QString KisGbrBrush::defaultFileExtension() const
{
return QString(".gbr");
}
diff --git a/libs/brush/kis_gbr_brush.h b/libs/brush/kis_gbr_brush.h
index ae14a722f4..e2bab1975a 100644
--- a/libs/brush/kis_gbr_brush.h
+++ b/libs/brush/kis_gbr_brush.h
@@ -1,122 +1,126 @@
/*
* 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_GBR_BRUSH_
#define KIS_GBR_BRUSH_
#include <QImage>
#include <QVector>
#include "kis_scaling_size_brush.h"
#include <kis_types.h>
#include <kis_shared.h>
#include <brushengine/kis_paint_information.h>
#include "kritabrush_export.h"
class KisQImagemask;
typedef KisSharedPtr<KisQImagemask> KisQImagemaskSP;
class QString;
class QIODevice;
class BRUSH_EXPORT KisGbrBrush : public KisScalingSizeBrush
{
protected:
public:
/// Construct brush to load filename later as brush
KisGbrBrush(const QString& filename);
/// Load brush from the specified data, at position dataPos, and set the filename
KisGbrBrush(const QString& filename,
const QByteArray & data,
qint32 & dataPos);
/// Load brush from the specified paint device, in the specified region
KisGbrBrush(KisPaintDeviceSP image, int x, int y, int w, int h);
/// Load brush as a copy from the specified QImage (handy when you need to copy a brush!)
KisGbrBrush(const QImage& image, const QString& name = QString());
+ ~KisGbrBrush() override;
+
KisGbrBrush(const KisGbrBrush& rhs);
- ~KisGbrBrush() override;
+ KoResourceSP clone() const override;
+
+ KisGbrBrush &operator=(const KisGbrBrush &rhs);
- bool load() override;
- bool loadFromDevice(QIODevice *dev) override;
- bool save() override;
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
bool saveToDevice(QIODevice* dev) const override;
+ QPair<QString, QString> resourceType() const override {
+ return QPair<QString, QString>(ResourceType::Brushes, ResourceSubType::GbrBrushes);
+ }
+
/**
* @return a preview of the brush
*/
QImage brushTipImage() const override;
/**
* If the brush image data are colorful (e.g. you created the brush from the canvas with custom brush)
* and you want to paint with it as with masks, set to true.
*/
virtual void setUseColorAsMask(bool useColorAsMask);
virtual bool useColorAsMask() const;
/**
* Convert the mask to inverted gray scale, so it is alpha mask.
* It can be used as MASK brush type. This operates on the date of the brush,
* so it destruct the original brush data
*/
virtual void makeMaskImage();
enumBrushType brushType() const override;
- /**
- * Makes a copy of this brush.
- */
- KisBrush* clone() const override;
/**
* @return default file extension for saving the brush
*/
QString defaultFileExtension() const override;
protected:
/**
* save the content of this brush to an IO device
*/
friend class KisImageBrushesPipe;
friend class KisBrushExport;
void setBrushType(enumBrushType type) override;
void setBrushTipImage(const QImage& image) override;
void toXML(QDomDocument& d, QDomElement& e) const override;
private:
bool init();
bool initFromPaintDev(KisPaintDeviceSP image, int x, int y, int w, int h);
struct Private;
Private* const d;
};
+typedef QSharedPointer<KisGbrBrush> KisGbrBrushSP;
+
#endif // KIS_GBR_BRUSH_
diff --git a/libs/brush/kis_imagepipe_brush.cpp b/libs/brush/kis_imagepipe_brush.cpp
index 626c365419..2c3c919b02 100644
--- a/libs/brush/kis_imagepipe_brush.cpp
+++ b/libs/brush/kis_imagepipe_brush.cpp
@@ -1,539 +1,522 @@
/*
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2005 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.
*/
#include "kis_imagepipe_brush.h"
#include "kis_pipebrush_parasite.h"
#include "kis_brushes_pipe.h"
class KisImageBrushesPipe : public KisBrushesPipe<KisGbrBrush>
{
public:
KisImageBrushesPipe()
: m_currentBrushIndex(0)
, m_isInitialized(false)
{
}
/*
pre and post are split because:
21:12:20 < dmitryK> boud: i guess it was somehow related to the fact that the maskWidth/maskHeight should
correspond to the size of the mask returned by paintDevice()
21:13:33 < dmitryK> boud: the random stuff is called once per brush->paintDevice() call, after the device is
returned to the paint op, that is "preparing the randomness for the next call"
21:14:16 < dmitryK> boud: and brushesPipe->currentBrush() always returning the same brush for any particular
paintInfo.
*/
protected:
static int selectPre(KisParasite::SelectionMode mode,
int index, int rank,
const KisPaintInformation& info) {
qreal angle;
qreal velocity;
qreal capSpeed = 3;
switch (mode) {
case KisParasite::Constant:
case KisParasite::Incremental:
case KisParasite::Random:
break;
case KisParasite::Pressure:
index = static_cast<int>(info.pressure() * (rank - 1) + 0.5);
break;
case KisParasite::Angular:
// + M_PI_2 + M_PI_4 to be compatible with the gimp
angle = info.drawingAngle() + M_PI_2 + M_PI_4;
angle = normalizeAngle(angle);
index = static_cast<int>(angle / (2.0 * M_PI) * rank);
break;
case KisParasite::TiltX:
index = qRound(info.xTilt() / 2.0 * rank) + rank / 2;
break;
case KisParasite::TiltY:
index = qRound(info.yTilt() / 2.0 * rank) + rank / 2;
break;
case KisParasite::Velocity:
// log is slow, but allows for nicer dab transition
velocity = log(info.drawingSpeed() + 1);
if (velocity > capSpeed) {
velocity = capSpeed;
}
velocity /= capSpeed;
velocity *= (rank - 1) + 0.5;
index = qRound(velocity);
break;
default:
warnImage << "Parasite" << mode << "is not implemented";
index = 0;
}
return index;
}
static int selectPost(KisParasite::SelectionMode mode,
int index, int rank,
const KisPaintInformation& info,
int seqNo) {
switch (mode) {
case KisParasite::Constant: break;
case KisParasite::Incremental:
index = (seqNo >= 0 ? seqNo : (index + 1)) % rank;
break;
case KisParasite::Random:
index = info.randomSource()->generate(0, rank-1);
break;
case KisParasite::Pressure:
case KisParasite::Angular:
break;
case KisParasite::TiltX:
case KisParasite::TiltY:
case KisParasite::Velocity:
break;
default:
warnImage << "Parasite" << mode << "is not implemented";
index = 0;
}
return index;
}
int chooseNextBrush(const KisPaintInformation& info) override {
quint32 brushIndex = 0;
if (!m_isInitialized) {
/**
* Reset all the indexes to the initial values and do the
* generation based on parameters.
*/
for (int i = 0; i < m_parasite.dim; i++) {
m_parasite.index[i] = 0;
}
updateBrushIndexes(info, 0);
m_isInitialized = true;
}
for (int i = 0; i < m_parasite.dim; i++) {
int index = selectPre(m_parasite.selection[i],
m_parasite.index[i],
m_parasite.rank[i], info);
brushIndex += m_parasite.brushesCount[i] * index;
}
brushIndex %= (quint32)m_brushes.size();
m_currentBrushIndex = brushIndex;
return brushIndex;
}
int currentBrushIndex() override {
return m_currentBrushIndex;
}
void updateBrushIndexes(const KisPaintInformation& info, int seqNo) override {
for (int i = 0; i < m_parasite.dim; i++) {
m_parasite.index[i] = selectPost(m_parasite.selection[i],
m_parasite.index[i],
m_parasite.rank[i],
info,
seqNo);
}
}
public:
using KisBrushesPipe<KisGbrBrush>::addBrush;
using KisBrushesPipe<KisGbrBrush>::sizeBrush;
void setParasite(const KisPipeBrushParasite& parasite) {
m_parasite = parasite;
}
const KisPipeBrushParasite& parasite() const {
return m_parasite;
}
void setUseColorAsMask(bool useColorAsMask) {
- Q_FOREACH (KisGbrBrush * brush, m_brushes) {
+ Q_FOREACH (KisGbrBrushSP brush, m_brushes) {
brush->setUseColorAsMask(useColorAsMask);
}
}
void makeMaskImage() {
- Q_FOREACH (KisGbrBrush * brush, m_brushes) {
+ Q_FOREACH (KisGbrBrushSP brush, m_brushes) {
brush->makeMaskImage();
}
}
bool saveToDevice(QIODevice* dev) const {
- Q_FOREACH (KisGbrBrush * brush, m_brushes) {
+ Q_FOREACH (KisGbrBrushSP brush, m_brushes) {
if (!brush->saveToDevice(dev)) {
return false;
}
}
return true;
}
void notifyStrokeStarted() override {
m_isInitialized = false;
}
private:
KisPipeBrushParasite m_parasite;
int m_currentBrushIndex;
bool m_isInitialized;
};
struct KisImagePipeBrush::Private {
public:
KisImageBrushesPipe brushesPipe;
};
KisImagePipeBrush::KisImagePipeBrush(const QString& filename)
: KisGbrBrush(filename)
- , m_d(new Private())
+ , d(new Private())
{
}
KisImagePipeBrush::KisImagePipeBrush(const QString& name, int w, int h,
QVector< QVector<KisPaintDevice*> > devices,
QVector<KisParasite::SelectionMode > modes)
: KisGbrBrush(QString())
- , m_d(new Private())
+ , d(new Private())
{
Q_ASSERT(devices.count() == modes.count());
Q_ASSERT(devices.count() > 0);
Q_ASSERT(devices.count() < 2); // XXX Multidimensionals not supported yet, change to MaxDim!
setName(name);
KisPipeBrushParasite parasite;
parasite.dim = devices.count();
// XXX Change for multidim! :
parasite.ncells = devices.at(0).count();
parasite.rank[0] = parasite.ncells; // ### This can masquerade some bugs, be careful here in the future
parasite.selection[0] = modes.at(0);
// XXX needsmovement!
parasite.setBrushesCount();
setParasite(parasite);
setDevices(devices, w, h);
- setBrushTipImage(m_d->brushesPipe.firstBrush()->brushTipImage());
+ setBrushTipImage(d->brushesPipe.firstBrush()->brushTipImage());
}
KisImagePipeBrush::KisImagePipeBrush(const KisImagePipeBrush& rhs)
: KisGbrBrush(rhs),
- m_d(new Private(*rhs.m_d))
+ d(new Private(*rhs.d))
{
}
-
-KisImagePipeBrush::~KisImagePipeBrush()
+KoResourceSP KisImagePipeBrush::clone() const
{
- delete m_d;
+ return KoResourceSP(new KisImagePipeBrush(*this));
}
-bool KisImagePipeBrush::load()
+KisImagePipeBrush::~KisImagePipeBrush()
{
- QFile file(filename());
- file.open(QIODevice::ReadOnly);
- bool res = loadFromDevice(&file);
- file.close();
- return res;
+ delete d;
}
-bool KisImagePipeBrush::loadFromDevice(QIODevice *dev)
+bool KisImagePipeBrush::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
{
+ Q_UNUSED(resourcesInterface);
+
QByteArray data = dev->readAll();
return initFromData(data);
}
bool KisImagePipeBrush::initFromData(const QByteArray &data)
{
if (data.size() == 0) return false;
// XXX: this doesn't correctly load the image pipe brushes yet.
// XXX: This stuff is in utf-8, too.
// The first line contains the name -- this means we look until we arrive at the first newline
QByteArray line1;
qint32 i = 0;
while (data[i] != '\n' && i < data.size()) {
line1.append(data[i]);
i++;
}
setName(QString::fromUtf8(line1, line1.size()));
i++; // Skip past the first newline
// The second line contains the number of brushes, separated by a space from the parasite
// XXX: This stuff is in utf-8, too.
QByteArray line2;
while (data[i] != '\n' && i < data.size()) {
line2.append(data[i]);
i++;
}
QString paramline = QString::fromUtf8(line2, line2.size());
qint32 numOfBrushes = paramline.left(paramline.indexOf(' ')).toUInt();
QString parasiteString = paramline.mid(paramline.indexOf(' ') + 1);
KisPipeBrushParasite parasite = KisPipeBrushParasite(parasiteString);
parasite.sanitize();
parasiteSelectionString = parasite.selectionMode; // selection mode to return to UI
- m_d->brushesPipe.setParasite(parasite);
+ d->brushesPipe.setParasite(parasite);
i++; // Skip past the second newline
- for (int brushIndex = m_d->brushesPipe.sizeBrush();
+ for (int brushIndex = d->brushesPipe.sizeBrush();
brushIndex < numOfBrushes && i < data.size(); brushIndex++) {
- KisGbrBrush* brush = new KisGbrBrush(name() + '_' + QString().setNum(brushIndex),
+ KisGbrBrushSP brush = KisGbrBrushSP(new KisGbrBrush(name() + '_' + QString().setNum(brushIndex),
data,
- i);
+ i));
- m_d->brushesPipe.addBrush(brush);
+ d->brushesPipe.addBrush(brush);
}
if (numOfBrushes > 0) {
setValid(true);
- setSpacing(m_d->brushesPipe.lastBrush()->spacing());
- setWidth(m_d->brushesPipe.firstBrush()->width());
- setHeight(m_d->brushesPipe.firstBrush()->height());
- setBrushTipImage(m_d->brushesPipe.firstBrush()->brushTipImage());
+ setSpacing(d->brushesPipe.lastBrush()->spacing());
+ setWidth(d->brushesPipe.firstBrush()->width());
+ setHeight(d->brushesPipe.firstBrush()->height());
+ setBrushTipImage(d->brushesPipe.firstBrush()->brushTipImage());
}
return true;
}
-bool KisImagePipeBrush::save()
-{
- QFile file(filename());
- file.open(QIODevice::WriteOnly | QIODevice::Truncate);
- bool ok = saveToDevice(&file);
- file.close();
- return ok;
-}
-
bool KisImagePipeBrush::saveToDevice(QIODevice* dev) const
{
QByteArray utf8Name = name().toUtf8(); // Names in v2 brushes are in UTF-8
char const* name = utf8Name.data();
int len = qstrlen(name);
- if (m_d->brushesPipe.parasite().dim >= KisPipeBrushParasite::MaxDim) {
+ if (d->brushesPipe.parasite().dim >= KisPipeBrushParasite::MaxDim) {
warnImage << "Save to file for pipe brushes with dim != not yet supported!";
return false;
}
// Save this pipe brush: first the header, and then all individual brushes consecutively
// XXX: this needs some care for when we have > 1 dimension)
// Gimp Pipe Brush header format: Name\n<number of brushes> <parasite>\n
// The name\n
if (dev->write(name, len) == -1)
return false;
if (!dev->putChar('\n'))
return false;
// Write the parasite (also writes number of brushes)
- if (!m_d->brushesPipe.parasite().saveToDevice(dev))
+ if (!d->brushesPipe.parasite().saveToDevice(dev))
return false;
if (!dev->putChar('\n'))
return false;
KoResource::saveToDevice(dev);
// <gbr brushes>
- return m_d->brushesPipe.saveToDevice(dev);
+ return d->brushesPipe.saveToDevice(dev);
}
void KisImagePipeBrush::notifyStrokeStarted()
{
- m_d->brushesPipe.notifyStrokeStarted();
+ d->brushesPipe.notifyStrokeStarted();
}
void KisImagePipeBrush::notifyCachedDabPainted(const KisPaintInformation& info)
{
- m_d->brushesPipe.notifyCachedDabPainted(info);
+ d->brushesPipe.notifyCachedDabPainted(info);
}
void KisImagePipeBrush::prepareForSeqNo(const KisPaintInformation &info, int seqNo)
{
- m_d->brushesPipe.prepareForSeqNo(info, seqNo);
+ d->brushesPipe.prepareForSeqNo(info, seqNo);
}
void KisImagePipeBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation,
KisDabShape const& shape,
const KisPaintInformation& info,
double subPixelX , double subPixelY,
qreal softnessFactor) const
{
- m_d->brushesPipe.generateMaskAndApplyMaskOrCreateDab(dst, coloringInformation, shape, info, subPixelX, subPixelY, softnessFactor);
+ d->brushesPipe.generateMaskAndApplyMaskOrCreateDab(dst, coloringInformation, shape, info, subPixelX, subPixelY, softnessFactor);
}
-QVector<KisGbrBrush *> KisImagePipeBrush::brushes() const
+QVector<KisGbrBrushSP> KisImagePipeBrush::brushes() const
{
- return m_d->brushesPipe.brushes();
+ return d->brushesPipe.brushes();
}
KisFixedPaintDeviceSP KisImagePipeBrush::paintDevice(
const KoColorSpace * colorSpace,
KisDabShape const& shape,
const KisPaintInformation& info, double subPixelX, double subPixelY) const
{
- return m_d->brushesPipe.paintDevice(colorSpace, shape, info, subPixelX, subPixelY);
+ return d->brushesPipe.paintDevice(colorSpace, shape, info, subPixelX, subPixelY);
}
enumBrushType KisImagePipeBrush::brushType() const
{
return !hasColor() || useColorAsMask() ? PIPE_MASK : PIPE_IMAGE;
}
QString KisImagePipeBrush::parasiteSelection()
{
return parasiteSelectionString;
}
bool KisImagePipeBrush::hasColor() const
{
- return m_d->brushesPipe.hasColor();
+ return d->brushesPipe.hasColor();
}
void KisImagePipeBrush::makeMaskImage()
{
- m_d->brushesPipe.makeMaskImage();
+ d->brushesPipe.makeMaskImage();
setUseColorAsMask(false);
}
void KisImagePipeBrush::setUseColorAsMask(bool useColorAsMask)
{
KisGbrBrush::setUseColorAsMask(useColorAsMask);
- m_d->brushesPipe.setUseColorAsMask(useColorAsMask);
+ d->brushesPipe.setUseColorAsMask(useColorAsMask);
}
const KisBoundary* KisImagePipeBrush::boundary() const
{
- KisGbrBrush *brush = m_d->brushesPipe.firstBrush();
+ KisGbrBrushSP brush = d->brushesPipe.firstBrush();
Q_ASSERT(brush);
return brush->boundary();
}
bool KisImagePipeBrush::canPaintFor(const KisPaintInformation& info)
{
- return (!m_d->brushesPipe.parasite().needsMovement || info.drawingDistance() >= 0.5);
-}
-
-KisBrush* KisImagePipeBrush::clone() const
-{
- return new KisImagePipeBrush(*this);
+ return (!d->brushesPipe.parasite().needsMovement || info.drawingDistance() >= 0.5);
}
QString KisImagePipeBrush::defaultFileExtension() const
{
return QString(".gih");
}
quint32 KisImagePipeBrush::brushIndex(const KisPaintInformation& info) const
{
- return m_d->brushesPipe.brushIndex(info);
+ return d->brushesPipe.brushIndex(info);
}
qint32 KisImagePipeBrush::maskWidth(KisDabShape const& shape, double subPixelX, double subPixelY, const KisPaintInformation& info) const
{
- return m_d->brushesPipe.maskWidth(shape, subPixelX, subPixelY, info);
+ return d->brushesPipe.maskWidth(shape, subPixelX, subPixelY, info);
}
qint32 KisImagePipeBrush::maskHeight(KisDabShape const& shape, double subPixelX, double subPixelY, const KisPaintInformation& info) const
{
- return m_d->brushesPipe.maskHeight(shape, subPixelX, subPixelY, info);
+ return d->brushesPipe.maskHeight(shape, subPixelX, subPixelY, info);
}
void KisImagePipeBrush::setAngle(qreal _angle)
{
KisGbrBrush::setAngle(_angle);
- m_d->brushesPipe.setAngle(_angle);
+ d->brushesPipe.setAngle(_angle);
}
void KisImagePipeBrush::setScale(qreal _scale)
{
KisGbrBrush::setScale(_scale);
- m_d->brushesPipe.setScale(_scale);
+ d->brushesPipe.setScale(_scale);
}
void KisImagePipeBrush::setSpacing(double _spacing)
{
KisGbrBrush::setSpacing(_spacing);
- m_d->brushesPipe.setSpacing(_spacing);
+ d->brushesPipe.setSpacing(_spacing);
}
void KisImagePipeBrush::setBrushType(enumBrushType type)
{
Q_UNUSED(type);
qFatal("FATAL: protected member setBrushType has no meaning for KisImagePipeBrush");
// brushType() is a function of hasColor() and useColorAsMask()
}
void KisImagePipeBrush::setHasColor(bool hasColor)
{
Q_UNUSED(hasColor);
qFatal("FATAL: protected member setHasColor has no meaning for KisImagePipeBrush");
// hasColor() is a function of the underlying brushes
}
-KisGbrBrush* KisImagePipeBrush::testingGetCurrentBrush(const KisPaintInformation& info) const
+KisGbrBrushSP KisImagePipeBrush::testingGetCurrentBrush(const KisPaintInformation& info) const
{
- return m_d->brushesPipe.currentBrush(info);
+ return d->brushesPipe.currentBrush(info);
}
void KisImagePipeBrush::testingSelectNextBrush(const KisPaintInformation& info) const
{
- return m_d->brushesPipe.testingSelectNextBrush(info);
+ return d->brushesPipe.testingSelectNextBrush(info);
}
const KisPipeBrushParasite& KisImagePipeBrush::parasite() const {
- return m_d->brushesPipe.parasite();
+ return d->brushesPipe.parasite();
}
void KisImagePipeBrush::setParasite(const KisPipeBrushParasite &parasite)
{
- m_d->brushesPipe.setParasite(parasite);
+ d->brushesPipe.setParasite(parasite);
}
void KisImagePipeBrush::setDevices(QVector<QVector<KisPaintDevice *> > devices, int w, int h)
{
for (int i = 0; i < devices.at(0).count(); i++) {
- m_d->brushesPipe.addBrush(new KisGbrBrush(devices.at(0).at(i), 0, 0, w, h));
+ d->brushesPipe.addBrush(KisGbrBrushSP(new KisGbrBrush(devices.at(0).at(i), 0, 0, w, h)));
}
}
diff --git a/libs/brush/kis_imagepipe_brush.h b/libs/brush/kis_imagepipe_brush.h
index 591c7cd5a5..7b257ddc54 100644
--- a/libs/brush/kis_imagepipe_brush.h
+++ b/libs/brush/kis_imagepipe_brush.h
@@ -1,144 +1,143 @@
/*
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2005 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_IMAGEPIPE_BRUSH_
#define KIS_IMAGEPIPE_BRUSH_
#include <QList>
#include <QMap>
#include <QString>
-#include <resources/KoResource.h>
+#include <KoResource.h>
#include "kis_gbr_brush.h"
#include "kis_global.h"
class KisPipeBrushParasite;
/**
* Velocity won't be supported, atm Tilt isn't either,
* but have chances of implementation
*/
namespace KisParasite
{
enum SelectionMode {
Constant,
Incremental,
Angular,
Velocity,
Random,
Pressure,
TiltX,
TiltY
};
}
class BRUSH_EXPORT KisImagePipeBrush : public KisGbrBrush
{
public:
KisImagePipeBrush(const QString& filename);
/**
* Specialized constructor that makes a new pipe brush from a sequence of samesize
* devices. The fact that it's a vector of a vector, is to support multidimensional
* brushes (not yet supported!) */
KisImagePipeBrush(const QString& name, int w, int h,
QVector< QVector<KisPaintDevice*> > devices,
QVector<KisParasite::SelectionMode> modes);
~KisImagePipeBrush() override;
- bool load() override;
- bool loadFromDevice(QIODevice *dev) override;
- bool save() override;
+ /// Will call KisBrush's saveToDevice as well
+ KisImagePipeBrush(const KisImagePipeBrush& rhs);
+
+ KisImagePipeBrush &operator=(const KisImagePipeBrush &rhs) = delete;
+
+ KoResourceSP clone() const override;
+
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
bool saveToDevice(QIODevice* dev) const override;
/**
* @return the next image in the pipe.
*/
KisFixedPaintDeviceSP paintDevice(const KoColorSpace * colorSpace,
KisDabShape const&,
const KisPaintInformation& info,
double subPixelX = 0, double subPixelY = 0) const override;
void setUseColorAsMask(bool useColorAsMask) override;
bool hasColor() const override;
enumBrushType brushType() const override;
QString parasiteSelection(); // returns random, constant, etc
const KisBoundary* boundary() const override;
bool canPaintFor(const KisPaintInformation& info) override;
void makeMaskImage() override;
- KisBrush* clone() const override;
QString defaultFileExtension() const override;
void setAngle(qreal _angle) override;
void setScale(qreal _scale) override;
void setSpacing(double _spacing) override;
quint32 brushIndex(const KisPaintInformation& info) const override;
qint32 maskWidth(KisDabShape const&, double subPixelX, double subPixelY, const KisPaintInformation& info) const override;
qint32 maskHeight(KisDabShape const&, double subPixelX, double subPixelY, const KisPaintInformation& info) const override;
void notifyStrokeStarted() override;
void notifyCachedDabPainted(const KisPaintInformation& info) override;
void prepareForSeqNo(const KisPaintInformation& info, int seqNo) override;
void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation,
KisDabShape const&,
const KisPaintInformation& info,
double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const override;
- QVector<KisGbrBrush*> brushes() const;
+ QVector<KisGbrBrushSP> brushes() const;
const KisPipeBrushParasite &parasite() const;
void setParasite(const KisPipeBrushParasite& parasite);
void setDevices(QVector< QVector<KisPaintDevice*> > devices, int w, int h);
protected:
void setBrushType(enumBrushType type) override;
void setHasColor(bool hasColor) override;
- /// Will call KisBrush's saveToDevice as well
-
- KisImagePipeBrush(const KisImagePipeBrush& rhs);
-
private:
friend class KisImagePipeBrushTest;
- KisGbrBrush* testingGetCurrentBrush(const KisPaintInformation& info) const;
+ KisGbrBrushSP testingGetCurrentBrush(const KisPaintInformation& info) const;
void testingSelectNextBrush(const KisPaintInformation& info) const;
bool initFromData(const QByteArray &data);
QString parasiteSelectionString; // incremental, random, etc.
private:
struct Private;
- Private * const m_d;
-
-
-
+ Private * const d;
};
+typedef QSharedPointer<KisImagePipeBrush> KisImagePipeBrushSP;
+
#endif // KIS_IMAGEPIPE_BRUSH_
diff --git a/libs/brush/kis_png_brush.cpp b/libs/brush/kis_png_brush.cpp
index e587b3b230..8e484e1c47 100644
--- a/libs/brush/kis_png_brush.cpp
+++ b/libs/brush/kis_png_brush.cpp
@@ -1,164 +1,142 @@
/*
* 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 "kis_png_brush.h"
#include <QDomElement>
#include <QFileInfo>
#include <QImageReader>
#include <QByteArray>
#include <QBuffer>
#include <QPainter>
#include <kis_dom_utils.h>
KisPngBrush::KisPngBrush(const QString& filename)
: KisScalingSizeBrush(filename)
{
setBrushType(INVALID);
setSpacing(0.25);
setHasColor(false);
}
KisPngBrush::KisPngBrush(const KisPngBrush &rhs)
: KisScalingSizeBrush(rhs)
{
setSpacing(rhs.spacing());
if (brushTipImage().isGrayscale()) {
setBrushType(MASK);
setHasColor(false);
}
else {
setBrushType(IMAGE);
setHasColor(true);
}
}
-KisBrush* KisPngBrush::clone() const
+KoResourceSP KisPngBrush::clone() const
{
- return new KisPngBrush(*this);
+ return KoResourceSP(new KisPngBrush(*this));
}
-bool KisPngBrush::load()
-{
- QFile f(filename());
- if (f.size() == 0) return false;
- if (!f.exists()) return false;
- if (!f.open(QIODevice::ReadOnly)) {
- warnKrita << "Can't open file " << filename();
- return false;
- }
- bool res = loadFromDevice(&f);
- f.close();
- return res;
-}
-
-bool KisPngBrush::loadFromDevice(QIODevice *dev)
+bool KisPngBrush::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
{
+ Q_UNUSED(resourcesInterface);
// Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice
// fails with "libpng error: IDAT: CRC error"
QByteArray data = dev->readAll();
QBuffer buf(&data);
buf.open(QIODevice::ReadOnly);
QImageReader reader(&buf, "PNG");
if (!reader.canRead()) {
dbgKrita << "Could not read brush" << filename() << ". Error:" << reader.errorString();
setValid(false);
return false;
}
if (reader.textKeys().contains("brush_spacing")) {
setSpacing(KisDomUtils::toDouble(reader.text("brush_spacing")));
}
if (reader.textKeys().contains("brush_name")) {
setName(reader.text("brush_name"));
}
else {
QFileInfo info(filename());
setName(info.completeBaseName());
}
QImage image = reader.read();
if (image.isNull()) {
dbgKrita << "Could not create image for" << filename() << ". Error:" << reader.errorString();
setValid(false);
return false;
}
setValid(true);
if (image.allGray()) {
// Make sure brush tips all have a white background
QImage base(image.size(), image.format());
if ((int)base.format() < (int)QImage::Format_RGB32) {
base = base.convertToFormat(QImage::Format_ARGB32);
}
QPainter gc(&base);
gc.fillRect(base.rect(), Qt::white);
gc.drawImage(0, 0, image);
gc.end();
QImage converted = base.convertToFormat(QImage::Format_Grayscale8);
setBrushTipImage(converted);
setBrushType(MASK);
setHasColor(false);
}
else {
setBrushTipImage(image);
setBrushType(IMAGE);
setHasColor(true);
}
setWidth(brushTipImage().width());
setHeight(brushTipImage().height());
return valid();
}
-bool KisPngBrush::save()
-{
- QFile f(filename());
- if (!f.open(QFile::WriteOnly)) return false;
- bool res = saveToDevice(&f);
- f.close();
- return res;
-}
-
bool KisPngBrush::saveToDevice(QIODevice *dev) const
{
if(brushTipImage().save(dev, "PNG")) {
KoResource::saveToDevice(dev);
return true;
}
return false;
}
QString KisPngBrush::defaultFileExtension() const
{
return QString(".png");
}
void KisPngBrush::toXML(QDomDocument& d, QDomElement& e) const
{
predefinedBrushToXML("png_brush", e);
KisBrush::toXML(d, e);
}
diff --git a/libs/brush/kis_png_brush.h b/libs/brush/kis_png_brush.h
index d8ceb9cc20..9476ba306e 100644
--- a/libs/brush/kis_png_brush.h
+++ b/libs/brush/kis_png_brush.h
@@ -1,41 +1,44 @@
/*
* 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.
*/
#ifndef KIS_PNG_BRUSH_
#define KIS_PNG_BRUSH_
#include "kis_scaling_size_brush.h"
class BRUSH_EXPORT KisPngBrush : public KisScalingSizeBrush
{
public:
/// Construct brush to load filename later as brush
KisPngBrush(const QString& filename);
KisPngBrush(const KisPngBrush &rhs);
- KisBrush* clone() const override;
+ KoResourceSP clone() const override;
+ KisPngBrush &operator=(const KisPngBrush &rhs) = delete;
- bool load() override;
- bool loadFromDevice(QIODevice *dev) override;
- bool save() override;
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
bool saveToDevice(QIODevice *dev) const override;
QString defaultFileExtension() const override;
void toXML(QDomDocument& d, QDomElement& e) const override;
+ QPair<QString, QString> resourceType() const override {
+ return QPair<QString, QString>(ResourceType::Brushes, ResourceSubType::PngBrushes);
+ }
+
};
#endif
diff --git a/libs/brush/kis_predefined_brush_factory.cpp b/libs/brush/kis_predefined_brush_factory.cpp
index e232c3e595..263ea07356 100644
--- a/libs/brush/kis_predefined_brush_factory.cpp
+++ b/libs/brush/kis_predefined_brush_factory.cpp
@@ -1,81 +1,83 @@
/*
* 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_predefined_brush_factory.h"
#include <QDomDocument>
-#include "kis_brush_server.h"
+#include "KisBrushServerProvider.h"
#include "kis_gbr_brush.h"
#include <kis_dom_utils.h>
+#include <KisResourcesInterface.h>
KisPredefinedBrushFactory::KisPredefinedBrushFactory(const QString &brushType)
: m_id(brushType)
{
}
QString KisPredefinedBrushFactory::id() const
{
return m_id;
}
-KisBrushSP KisPredefinedBrushFactory::createBrush(const QDomElement& brushDefinition)
+KisBrushSP KisPredefinedBrushFactory::createBrush(const QDomElement& brushDefinition, KisResourcesInterfaceSP resourcesInterface)
{
- KisBrushResourceServer *rServer = KisBrushServer::instance()->brushServer();
- QString brushFileName = brushDefinition.attribute("filename", "");
- KisBrushSP brush = rServer->resourceByFilename(brushFileName);
+ auto resourceSourceAdapter = resourcesInterface->source<KisBrush>(ResourceType::Brushes);
+
+ const QString brushFileName = brushDefinition.attribute("filename", "");
+ KisBrushSP brush = resourceSourceAdapter.resourceForFilename(brushFileName);
//Fallback for files that still use the old format
if (!brush) {
QFileInfo info(brushFileName);
- brush = rServer->resourceByFilename(info.fileName());
+ brush = resourceSourceAdapter.resourceForFilename(info.fileName());
}
if (!brush) {
- brush = rServer->resources().first();
+ brush = resourceSourceAdapter.fallbackResource();
}
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(brush, 0);
// we always return a copy of the brush!
- brush = brush->clone();
+ brush = brush->clone().dynamicCast<KisBrush>();
double spacing = KisDomUtils::toDouble(brushDefinition.attribute("spacing", "0.25"));
brush->setSpacing(spacing);
bool useAutoSpacing = KisDomUtils::toInt(brushDefinition.attribute("useAutoSpacing", "0"));
qreal autoSpacingCoeff = KisDomUtils::toDouble(brushDefinition.attribute("autoSpacingCoeff", "1.0"));
brush->setAutoSpacing(useAutoSpacing, autoSpacingCoeff);
double angle = KisDomUtils::toDouble(brushDefinition.attribute("angle", "0.0"));
brush->setAngle(angle);
double scale = KisDomUtils::toDouble(brushDefinition.attribute("scale", "1.0"));
brush->setScale(scale);
if (m_id == "gbr_brush") {
KisGbrBrush *gbrbrush = dynamic_cast<KisGbrBrush*>(brush.data());
if (gbrbrush) {
/**
* WARNING: see comment in KisGbrBrush::setUseColorAsMask()
*/
gbrbrush->setUseColorAsMask((bool)brushDefinition.attribute("ColorAsMask").toInt());
}
}
return brush;
}
diff --git a/libs/brush/kis_predefined_brush_factory.h b/libs/brush/kis_predefined_brush_factory.h
index 5c3cc18899..f23873bf1e 100644
--- a/libs/brush/kis_predefined_brush_factory.h
+++ b/libs/brush/kis_predefined_brush_factory.h
@@ -1,41 +1,41 @@
/*
* 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_PREDEFINED_BRUSH_FACTORY_H
#define __KIS_PREDEFINED_BRUSH_FACTORY_H
#include <QString>
#include <QDomElement>
#include "kis_brush_factory.h"
#include "kis_brush.h"
class KisPredefinedBrushFactory : public KisBrushFactory
{
public:
KisPredefinedBrushFactory(const QString &brushType);
QString id() const override;
- KisBrushSP createBrush(const QDomElement& brushDefinition) override;
+ KisBrushSP createBrush(const QDomElement& brushDefinition, KisResourcesInterfaceSP resourcesInterface) override;
private:
const QString m_id;
};
#endif /* __KIS_PREDEFINED_BRUSH_FACTORY_H */
diff --git a/libs/brush/kis_svg_brush.cpp b/libs/brush/kis_svg_brush.cpp
index 8e76f32f6e..3e37fb4f9c 100644
--- a/libs/brush/kis_svg_brush.cpp
+++ b/libs/brush/kis_svg_brush.cpp
@@ -1,132 +1,109 @@
/*
* 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 "kis_svg_brush.h"
#include <QDomElement>
#include <QFileInfo>
#include <QPainter>
#include <QImageReader>
#include <QSvgRenderer>
KisSvgBrush::KisSvgBrush(const QString& filename)
: KisScalingSizeBrush(filename)
{
setBrushType(INVALID);
setSpacing(0.25);
setHasColor(false);
}
KisSvgBrush::KisSvgBrush(const KisSvgBrush& rhs)
: KisScalingSizeBrush(rhs)
, m_svg(rhs.m_svg)
{
}
-KisBrush* KisSvgBrush::clone() const
+KoResourceSP KisSvgBrush::clone() const
{
- return new KisSvgBrush(*this);
+ return KoResourceSP(new KisSvgBrush(*this));
}
-bool KisSvgBrush::load()
-{
- QFile f(filename());
- if (f.size() == 0) return false;
- if (!f.exists()) return false;
- if (!f.open(QIODevice::ReadOnly)) {
- warnKrita << "Can't open file " << filename();
- return false;
- }
- bool res = loadFromDevice(&f);
- f.close();
-
- return res;
-}
-
-bool KisSvgBrush::loadFromDevice(QIODevice *dev)
+bool KisSvgBrush::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
{
+ Q_UNUSED(resourcesInterface);
m_svg = dev->readAll();
QSvgRenderer renderer(m_svg);
QRect box = renderer.viewBox();
if (box.isEmpty()) return false;
QImage image_(1000, (1000 * box.height()) / box.width(), QImage::Format_ARGB32);
{
QPainter p(&image_);
p.fillRect(0, 0, image_.width(), image_.height(), Qt::white);
renderer.render(&p);
}
QVector<QRgb> table;
for (int i = 0; i < 256; ++i) table.push_back(qRgb(i, i, i));
image_ = image_.convertToFormat(QImage::Format_Indexed8, table);
setBrushTipImage(image_);
setValid(true);
// Well for now, always true
if (brushTipImage().isGrayscale()) {
setBrushType(MASK);
setHasColor(false);
}
else {
setBrushType(IMAGE);
setHasColor(true);
}
setWidth(brushTipImage().width());
setHeight(brushTipImage().height());
QFileInfo fi(filename());
setName(fi.completeBaseName());
return !brushTipImage().isNull() && valid();
}
-bool KisSvgBrush::save()
-{
- QFile f(filename());
- if (!f.open(QFile::WriteOnly)) return false;
- bool res = saveToDevice(&f);
- f.close();
- return res;
-}
-
bool KisSvgBrush::saveToDevice(QIODevice *dev) const
{
if((dev->write(m_svg.constData(), m_svg.size()) == m_svg.size())) {
KoResource::saveToDevice(dev);
return true;
}
return false;
}
QString KisSvgBrush::defaultFileExtension() const
{
return QString(".svg");
}
void KisSvgBrush::toXML(QDomDocument& d, QDomElement& e) const
{
predefinedBrushToXML("svg_brush", e);
KisBrush::toXML(d, e);
}
diff --git a/libs/brush/kis_svg_brush.h b/libs/brush/kis_svg_brush.h
index 16f0d47d14..ff0fae2c2a 100644
--- a/libs/brush/kis_svg_brush.h
+++ b/libs/brush/kis_svg_brush.h
@@ -1,43 +1,47 @@
/*
* 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.
*/
#ifndef KIS_SVG_BRUSH_
#define KIS_SVG_BRUSH_
#include "kis_scaling_size_brush.h"
class BRUSH_EXPORT KisSvgBrush : public KisScalingSizeBrush
{
public:
/// Construct brush to load filename later as brush
- KisSvgBrush(const QString& filename);
- KisSvgBrush(const KisSvgBrush& rhs);
- KisBrush* clone() const override;
+ KisSvgBrush(const QString &filename);
+ KisSvgBrush(const KisSvgBrush &rhs);
+ KisSvgBrush &operator=(const KisSvgBrush &rhs) = delete;
- bool load() override;
- bool loadFromDevice(QIODevice *dev) override;
- bool save() override;
+ KoResourceSP clone() const override;
+
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
bool saveToDevice(QIODevice *dev) const override;
+ QPair<QString, QString> resourceType() const override {
+ return QPair<QString, QString>(ResourceType::Brushes, ResourceSubType::SvgBrushes);
+ }
+
QString defaultFileExtension() const override;
void toXML(QDomDocument& d, QDomElement& e) const override;
private:
QByteArray m_svg;
};
#endif
diff --git a/libs/brush/kis_text_brush.cpp b/libs/brush/kis_text_brush.cpp
index a0812d6223..69778bbe86 100644
--- a/libs/brush/kis_text_brush.cpp
+++ b/libs/brush/kis_text_brush.cpp
@@ -1,339 +1,341 @@
/*
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2011 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.
*/
#include "kis_text_brush.h"
#include <QDomDocument>
#include <QDomElement>
#include <QFontMetrics>
#include <QPainter>
#include "kis_gbr_brush.h"
#include "kis_brushes_pipe.h"
#include <kis_dom_utils.h>
#include <kis_threaded_text_rendering_workaround.h>
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
#include <QApplication>
#include <QWidget>
#include <QThread>
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
class KisTextBrushesPipe : public KisBrushesPipe<KisGbrBrush>
{
public:
KisTextBrushesPipe() {
m_charIndex = 0;
m_currentBrushIndex = 0;
}
KisTextBrushesPipe(const KisTextBrushesPipe &rhs)
: KisBrushesPipe<KisGbrBrush>(), // no copy here!
m_text(rhs.m_text),
m_charIndex(rhs.m_charIndex),
m_currentBrushIndex(rhs.m_currentBrushIndex)
{
m_brushesMap.clear();
- QMapIterator<QChar, KisGbrBrush*> iter(rhs.m_brushesMap);
+ QMapIterator<QChar, KisGbrBrushSP> iter(rhs.m_brushesMap);
while (iter.hasNext()) {
iter.next();
- KisGbrBrush *brush = new KisGbrBrush(*iter.value());
+ KisGbrBrushSP brush(new KisGbrBrush(*iter.value()));
m_brushesMap.insert(iter.key(), brush);
KisBrushesPipe<KisGbrBrush>::addBrush(brush);
}
}
void setText(const QString &text, const QFont &font) {
m_text = text;
m_charIndex = 0;
clear();
for (int i = 0; i < m_text.length(); i++) {
const QChar letter = m_text.at(i);
// skip letters that are already present in the brushes pipe
if (m_brushesMap.contains(letter)) continue;
QImage image = renderChar(letter, font);
- KisGbrBrush *brush = new KisGbrBrush(image, letter);
+ KisGbrBrushSP brush(new KisGbrBrush(image, letter));
brush->setSpacing(0.1); // support for letter spacing?
brush->makeMaskImage();
m_brushesMap.insert(letter, brush);
KisBrushesPipe<KisGbrBrush>::addBrush(brush);
}
}
static QImage renderChar(const QString& text, const QFont &font) {
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
QWidget *focusWidget = qApp->focusWidget();
if (focusWidget) {
QThread *guiThread = focusWidget->thread();
if (guiThread != QThread::currentThread()) {
warnKrita << "WARNING: Rendering text in non-GUI thread!"
<< "That may lead to hangups and crashes on some"
<< "versions of X11/Qt!";
}
}
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
QFontMetrics metric(font);
QRect rect = metric.boundingRect(text);
if (rect.isEmpty()) {
rect = QRect(0, 0, 1, 1); // paint at least something
}
QRect paintingRect = rect.translated(-rect.x(), -rect.y());
QImage renderedChar(paintingRect.size(), QImage::Format_ARGB32);
QPainter p;
p.begin(&renderedChar);
p.setFont(font);
p.fillRect(paintingRect, Qt::white);
p.setPen(Qt::black);
p.drawText(-rect.x(), -rect.y(), text);
p.end();
return renderedChar;
}
void clear() override {
m_brushesMap.clear();
KisBrushesPipe<KisGbrBrush>::clear();
}
- KisGbrBrush* firstBrush() const {
+ KisGbrBrushSP firstBrush() const {
Q_ASSERT(m_text.size() > 0);
Q_ASSERT(m_brushesMap.size() > 0);
return m_brushesMap.value(m_text.at(0));
}
void notifyStrokeStarted() override {
m_charIndex = 0;
updateBrushIndexesImpl();
}
protected:
int chooseNextBrush(const KisPaintInformation& info) override {
Q_UNUSED(info);
return m_currentBrushIndex;
}
int currentBrushIndex() override {
return m_currentBrushIndex;
}
void updateBrushIndexes(const KisPaintInformation& info, int seqNo) override {
Q_UNUSED(info);
if (m_text.size()) {
m_charIndex = (seqNo >= 0 ? seqNo : (m_charIndex + 1)) % m_text.size();
} else {
m_charIndex = 0;
}
updateBrushIndexesImpl();
}
private:
void updateBrushIndexesImpl() {
if (m_text.isEmpty()) return;
if (m_charIndex >= m_text.size()) {
m_charIndex = 0;
}
QChar letter = m_text.at(m_charIndex);
Q_ASSERT(m_brushesMap.contains(letter));
m_currentBrushIndex = m_brushes.indexOf(m_brushesMap.value(letter));
}
private:
- QMap<QChar, KisGbrBrush*> m_brushesMap;
+ QMap<QChar, KisGbrBrushSP> m_brushesMap;
QString m_text;
int m_charIndex;
int m_currentBrushIndex;
};
KisTextBrush::KisTextBrush()
: m_brushesPipe(new KisTextBrushesPipe())
{
setPipeMode(false);
}
KisTextBrush::KisTextBrush(const KisTextBrush &rhs)
- : KisScalingSizeBrush(rhs),
+ : KoEphemeralResource<KisScalingSizeBrush>(rhs),
m_font(rhs.m_font),
m_text(rhs.m_text),
m_brushesPipe(new KisTextBrushesPipe(*rhs.m_brushesPipe))
{
}
KisTextBrush::~KisTextBrush()
{
delete m_brushesPipe;
}
+KoResourceSP KisTextBrush::clone() const
+{
+ return KisBrushSP(new KisTextBrush(*this));
+}
+
+
void KisTextBrush::setPipeMode(bool pipe)
{
setBrushType(pipe ? PIPE_MASK : MASK);
}
bool KisTextBrush::pipeMode() const
{
return brushType() == PIPE_MASK;
}
void KisTextBrush::setText(const QString& txt)
{
m_text = txt;
}
QString KisTextBrush::text(void) const
{
return m_text;
}
void KisTextBrush::setFont(const QFont& font)
{
m_font = font;
}
QFont KisTextBrush::font()
{
return m_font;
}
void KisTextBrush::notifyStrokeStarted()
{
m_brushesPipe->notifyStrokeStarted();
}
void KisTextBrush::notifyCachedDabPainted(const KisPaintInformation& info)
{
m_brushesPipe->notifyCachedDabPainted(info);
}
void KisTextBrush::prepareForSeqNo(const KisPaintInformation &info, int seqNo)
{
m_brushesPipe->prepareForSeqNo(info, seqNo);
}
void KisTextBrush::generateMaskAndApplyMaskOrCreateDab(
KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation,
KisDabShape const& shape,
const KisPaintInformation& info, double subPixelX, double subPixelY, qreal softnessFactor) const
{
if (brushType() == MASK) {
KisBrush::generateMaskAndApplyMaskOrCreateDab(dst, coloringInformation, shape, info, subPixelX, subPixelY, softnessFactor);
}
else { /* if (brushType() == PIPE_MASK)*/
m_brushesPipe->generateMaskAndApplyMaskOrCreateDab(dst, coloringInformation, shape, info, subPixelX, subPixelY, softnessFactor);
}
}
KisFixedPaintDeviceSP KisTextBrush::paintDevice(const KoColorSpace * colorSpace,
KisDabShape const& shape,
const KisPaintInformation& info, double subPixelX, double subPixelY) const
{
if (brushType() == MASK) {
return KisBrush::paintDevice(colorSpace, shape, info, subPixelX, subPixelY);
}
else { /* if (brushType() == PIPE_MASK)*/
return m_brushesPipe->paintDevice(colorSpace, shape, info, subPixelX, subPixelY);
}
}
void KisTextBrush::toXML(QDomDocument& doc, QDomElement& e) const
{
Q_UNUSED(doc);
e.setAttribute("type", "kis_text_brush");
e.setAttribute("spacing", KisDomUtils::toString(spacing()));
e.setAttribute("text", m_text);
e.setAttribute("font", m_font.toString());
e.setAttribute("pipe", (brushType() == PIPE_MASK) ? "true" : "false");
KisBrush::toXML(doc, e);
}
void KisTextBrush::updateBrush()
{
Q_ASSERT((brushType() == PIPE_MASK) || (brushType() == MASK));
if (brushType() == PIPE_MASK) {
m_brushesPipe->setText(m_text, m_font);
setBrushTipImage(m_brushesPipe->firstBrush()->brushTipImage());
}
else { /* if (brushType() == MASK)*/
setBrushTipImage(KisTextBrushesPipe::renderChar(m_text, m_font));
}
resetBoundary();
setValid(true);
}
quint32 KisTextBrush::brushIndex(const KisPaintInformation& info) const
{
return brushType() == MASK ? 0 : 1 + m_brushesPipe->brushIndex(info);
}
qint32 KisTextBrush::maskWidth(KisDabShape const& shape, double subPixelX, double subPixelY, const KisPaintInformation& info) const
{
return brushType() == MASK ?
KisBrush::maskWidth(shape, subPixelX, subPixelY, info) :
m_brushesPipe->maskWidth(shape, subPixelX, subPixelY, info);
}
qint32 KisTextBrush::maskHeight(KisDabShape const& shape, double subPixelX, double subPixelY, const KisPaintInformation& info) const
{
return brushType() == MASK ?
KisBrush::maskHeight(shape, subPixelX, subPixelY, info) :
m_brushesPipe->maskHeight(shape, subPixelX, subPixelY, info);
}
void KisTextBrush::setAngle(qreal _angle)
{
KisBrush::setAngle(_angle);
m_brushesPipe->setAngle(_angle);
}
void KisTextBrush::setScale(qreal _scale)
{
KisBrush::setScale(_scale);
m_brushesPipe->setScale(_scale);
}
void KisTextBrush::setSpacing(double _spacing)
{
KisBrush::setSpacing(_spacing);
m_brushesPipe->setSpacing(_spacing);
}
-KisBrush* KisTextBrush::clone() const
-{
- return new KisTextBrush(*this);
-}
diff --git a/libs/brush/kis_text_brush.h b/libs/brush/kis_text_brush.h
index 01dfed2854..26f7bc809c 100644
--- a/libs/brush/kis_text_brush.h
+++ b/libs/brush/kis_text_brush.h
@@ -1,96 +1,87 @@
/*
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2011 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_TEXT_BRUSH_H_
#define _KIS_TEXT_BRUSH_H_
#include <QFont>
+#include <KoEphemeralResource.h>
+
#include "kis_scaling_size_brush.h"
#include "kritabrush_export.h"
class KisTextBrushesPipe;
-class BRUSH_EXPORT KisTextBrush : public KisScalingSizeBrush
+class BRUSH_EXPORT KisTextBrush : public KoEphemeralResource<KisScalingSizeBrush>
{
public:
KisTextBrush();
KisTextBrush(const KisTextBrush &rhs);
~KisTextBrush() override;
+ KisTextBrush &operator=(const KisTextBrush &rhs) = delete;
+
+ KoResourceSP clone() const override;
+
+
void notifyStrokeStarted() override;
void notifyCachedDabPainted(const KisPaintInformation& info) override;
void prepareForSeqNo(const KisPaintInformation& info, int seqNo) override;
void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation,
KisDabShape const&,
const KisPaintInformation& info,
double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const override;
KisFixedPaintDeviceSP paintDevice(const KoColorSpace * colorSpace,
KisDabShape const&, const KisPaintInformation& info, double subPixelX, double subPixelY) const override;
- bool load() override {
- return false;
- }
-
- bool loadFromDevice(QIODevice *) override {
- return false;
- }
-
- bool save() override {
- return false;
- }
-
- bool saveToDevice(QIODevice* ) const override {
- return false;
- }
-
void setText(const QString& txt);
QString text(void) const;
QFont font();
void setFont(const QFont& font);
void setPipeMode(bool pipe);
bool pipeMode() const;
void updateBrush();
void toXML(QDomDocument& , QDomElement&) const override;
quint32 brushIndex(const KisPaintInformation& info) const override;
qint32 maskWidth(KisDabShape const&, double subPixelX, double subPixelY, const KisPaintInformation& info) const override;
qint32 maskHeight(KisDabShape const&, double subPixelX, double subPixelY, const KisPaintInformation& info) const override;
void setAngle(qreal _angle) override;
void setScale(qreal _scale) override;
void setSpacing(double _spacing) override;
- KisBrush* clone() const override;
-
private:
QFont m_font;
QString m_text;
private:
KisTextBrushesPipe *m_brushesPipe;
};
+typedef QSharedPointer<KisTextBrush> KisTextBrushSP;
+
#endif
diff --git a/libs/brush/kis_text_brush_factory.cpp b/libs/brush/kis_text_brush_factory.cpp
index 1d7fad257f..76559ee047 100644
--- a/libs/brush/kis_text_brush_factory.cpp
+++ b/libs/brush/kis_text_brush_factory.cpp
@@ -1,46 +1,45 @@
/*
* 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.
*/
#include "kis_text_brush_factory.h"
#include <QString>
#include <QFont>
#include <kis_dom_utils.h>
#include "kis_text_brush.h"
-KisBrushSP KisTextBrushFactory::createBrush(const QDomElement& brushDefinition)
+KisBrushSP KisTextBrushFactory::createBrush(const QDomElement& brushDefinition, KisResourcesInterfaceSP resourcesInterface)
{
+ Q_UNUSED(resourcesInterface);
+
QString text = brushDefinition.attribute("text", "The quick brown fox ate your text");
QFont font;
font.fromString(brushDefinition.attribute("font"));
double spacing = KisDomUtils::toDouble(brushDefinition.attribute("spacing", "1.0"));
QString pipeMode = brushDefinition.attribute("pipe", "false");
bool pipe = (pipeMode == "true") ? true : false;
- KisBrushSP b = new KisTextBrush();
-
- KisTextBrush *brush = dynamic_cast<KisTextBrush*>(b.data());
+ KisTextBrushSP brush = KisTextBrushSP(new KisTextBrush());
brush->setText(text);
brush->setFont(font);
brush->setPipeMode(pipe);
brush->setSpacing(spacing);
brush->updateBrush();
- return b;
-
+ return brush;
}
diff --git a/libs/brush/kis_text_brush_factory.h b/libs/brush/kis_text_brush_factory.h
index c7f8af0c83..e3c435872f 100644
--- a/libs/brush/kis_text_brush_factory.h
+++ b/libs/brush/kis_text_brush_factory.h
@@ -1,54 +1,54 @@
/*
* 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_TEXT_BRUSH_FACTORY
#define KIS_TEXT_BRUSH_FACTORY
#include <QString>
#include <QDomElement>
#include "kis_brush_factory.h"
#include "kis_brush.h"
/**
* A brush factory can create a new brush instance based
* on a properties object that contains a serialized representation
* of the object.
*/
class BRUSH_EXPORT KisTextBrushFactory : public KisBrushFactory
{
public:
KisTextBrushFactory() {}
~KisTextBrushFactory() override {}
QString id() const override {
return "kis_text_brush";
}
/**
* Create a new brush from the given data or return an existing KisBrush
* object. If this call leads to the creation of a resource, it should be
* added to the resource provider, too.
*/
- KisBrushSP createBrush(const QDomElement& brushDefinition) override;
+ KisBrushSP createBrush(const QDomElement& brushDefinition, KisResourcesInterfaceSP resourcesInterface) override;
};
#endif
diff --git a/libs/brush/tests/CMakeLists.txt b/libs/brush/tests/CMakeLists.txt
index c89ea12baa..7e777ac926 100644
--- a/libs/brush/tests/CMakeLists.txt
+++ b/libs/brush/tests/CMakeLists.txt
@@ -1,30 +1,31 @@
########### next target ###############
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
include_directories(
${CMAKE_SOURCE_DIR}/libs/image/metadata
${CMAKE_SOURCE_DIR}/sdk/tests
)
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
)
if(HAVE_VC)
include_directories(SYSTEM
${Vc_INCLUDE_DIR}
)
endif()
macro_add_unittest_definitions()
include(ECMAddTests)
ecm_add_tests(
kis_auto_brush_test.cpp
kis_auto_brush_factory_test.cpp
kis_gbr_brush_test.cpp
kis_boundary_test.cpp
kis_imagepipe_brush_test.cpp
+ TestAbrStorage.cpp
NAME_PREFIX "libs-brush-"
LINK_LIBRARIES kritaimage kritalibbrush Qt5::Test
)
diff --git a/libs/brush/tests/TestAbrStorage.cpp b/libs/brush/tests/TestAbrStorage.cpp
new file mode 100644
index 0000000000..58937b07ce
--- /dev/null
+++ b/libs/brush/tests/TestAbrStorage.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2018 boud <boud@valdyas.org>
+ * Copyright (c) 2019 Agata Cacko <cacko.azh@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 "TestAbrStorage.h"
+
+
+#include <QTest>
+#include <QImageReader>
+
+#include <KoConfig.h>
+
+#include <KisAbrStorage.h>
+#include <KisBundleStorage.h>
+#include <KisResourceLoader.h>
+#include <KoResource.h>
+#include <KisResourceLoaderRegistry.h>
+#ifndef FILES_DATA_DIR
+#error "FILES_DATA_DIR not set. A directory with the data used for testing the importing of files in krita"
+#endif
+
+void TestAbrStorage::initTestCase()
+{
+ // nothing to do
+}
+
+void TestAbrStorage::testResourceIterator()
+{
+ QString filename = "brushes_by_mar_ka_d338ela.abr";
+ KisAbrStorage storage(FILES_DATA_DIR + QDir::separator() + filename);
+
+ QSharedPointer<KisResourceStorage::ResourceIterator> iter(storage.resources(ResourceType::Brushes));
+ QVERIFY(iter->hasNext());
+ int count = 0;
+ while (iter->hasNext()) {
+ iter->next();
+ KoResourceSP res(iter->resource());
+ QVERIFY(res);
+ count++;
+ }
+ QVERIFY(count > 0);
+}
+
+void TestAbrStorage::testTagIterator()
+{
+ QString filename = "brushes_by_mar_ka_d338ela.abr";
+ KisAbrStorage storage(FILES_DATA_DIR + QDir::separator() + filename);
+
+ QSharedPointer<KisResourceStorage::TagIterator> iter = storage.tags(ResourceType::Brushes);
+ int count = 0;
+ while (iter->hasNext()) {
+ iter->next();
+ count++;
+ }
+ QVERIFY(count == 0);
+}
+
+void TestAbrStorage::testResourceItem()
+{
+ QString name = "brushes_by_mar_ka_d338ela";
+ QString filename = name + ".abr";
+ QString resourceName = name + "_2"; // "1" seem to be invalid or something; it isn't not loaded in any case.
+ KisAbrStorage storage(FILES_DATA_DIR + QDir::separator() + filename);
+
+ KisResourceStorage::ResourceItem item = storage.resourceItem(resourceName);
+ QVERIFY(!item.url.isEmpty());
+}
+
+void TestAbrStorage::testResource()
+{
+ QString name = "brushes_by_mar_ka_d338ela";
+ QString filename = name + ".abr";
+ QString resourceName = name + "_2"; // "1" seem to be invalid or something; it isn't not loaded in any case.
+ KisAbrStorage storage(FILES_DATA_DIR + QDir::separator() + filename);
+ KoResourceSP res = storage.resource(resourceName);
+ QVERIFY(res);
+ QVERIFY(res->filename() == resourceName);
+}
+
+
+QTEST_MAIN(TestAbrStorage)
+
diff --git a/libs/brush/tests/TestAbrStorage.h b/libs/brush/tests/TestAbrStorage.h
new file mode 100644
index 0000000000..aba5534434
--- /dev/null
+++ b/libs/brush/tests/TestAbrStorage.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018 boud <boud@valdyas.org>
+ * Copyright (c) 2019 Agata Cacko <cacko.azh@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 TESTABRSTORAGE_H
+#define TESTABRSTORAGE_H
+
+
+#include <QObject>
+
+class TestAbrStorage : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+ void testResourceIterator();
+ void testTagIterator();
+ void testResourceItem();
+ void testResource();
+};
+
+#endif // TESTABRSTORAGE_H
diff --git a/libs/brush/tests/data/brushes_by_mar_ka_d338ela.abr b/libs/brush/tests/data/brushes_by_mar_ka_d338ela.abr
new file mode 100644
index 0000000000..3f2058d7a0
Binary files /dev/null and b/libs/brush/tests/data/brushes_by_mar_ka_d338ela.abr differ
diff --git a/libs/brush/tests/kis_auto_brush_factory_test.cpp b/libs/brush/tests/kis_auto_brush_factory_test.cpp
index 724d027f3f..9520469cef 100644
--- a/libs/brush/tests/kis_auto_brush_factory_test.cpp
+++ b/libs/brush/tests/kis_auto_brush_factory_test.cpp
@@ -1,51 +1,51 @@
#include "kis_auto_brush_factory_test.h"
#include <QTest>
#include <testutil.h>
#include "kis_auto_brush.h"
#include "kis_auto_brush_factory.h"
#include "kis_mask_generator.h"
#include <KoColor.h>
#include <brushengine/kis_paint_information.h>
+#include <KisGlobalResourcesInterface.h>
void KisAutoBrushFactoryTest::testXMLClone()
{
// Set up an autobrush.
- KisBrushSP brush = new KisAutoBrush(new KisCircleMaskGenerator(20, 0.6, 0.8, 0.4, 3, true), 1.0,
- 0.0);
+ KisBrushSP brush(new KisAutoBrush(new KisCircleMaskGenerator(20, 0.6, 0.8, 0.4, 3, true), 1.0, 0.0));
brush->setSpacing(0.15);
brush->setAutoSpacing(true, 0.1);
// Try to clone the brush by converting to XML and back.
QDomDocument d;
QDomElement e = d.createElement("Brush");
brush->toXML(d, e);
- KisBrushSP clone = KisAutoBrushFactory().createBrush(e);
+ KisBrushSP clone = KisAutoBrushFactory().createBrush(e, KisGlobalResourcesInterface::instance());
// Test that the clone has the same settings as the original brush.
QCOMPARE(brush->width(), clone->width());
QCOMPARE(brush->height(), clone->height());
QCOMPARE(brush->angle(), clone->angle());
QCOMPARE(brush->spacing(), clone->spacing());
QCOMPARE(brush->autoSpacingActive(), clone->autoSpacingActive());
QCOMPARE(brush->autoSpacingCoeff(), clone->autoSpacingCoeff());
// Test that the clone draws the same as the original brush.
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintInformation info(QPointF(100.0, 100.0), 0.5);
KisDabShape shape(0.9, 0.7, 1.0);
KoColor color(Qt::yellow, cs);
KisFixedPaintDeviceSP fdev1 = new KisFixedPaintDevice(cs);
brush->mask(fdev1, color, shape, info);
QImage res1 = fdev1->convertToQImage(0);
KisFixedPaintDeviceSP fdev2 = new KisFixedPaintDevice(cs);
clone->mask(fdev2, color, shape, info);
QImage res2 = fdev2->convertToQImage(0);
QCOMPARE(res1, res2);
}
QTEST_MAIN(KisAutoBrushFactoryTest)
diff --git a/libs/brush/tests/kis_auto_brush_test.cpp b/libs/brush/tests/kis_auto_brush_test.cpp
index 3d8144c675..dd471cf7d6 100644
--- a/libs/brush/tests/kis_auto_brush_test.cpp
+++ b/libs/brush/tests/kis_auto_brush_test.cpp
@@ -1,196 +1,196 @@
/*
* Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org
* Copyright (c) 2009 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 <compositeops/KoVcMultiArchBuildSupport.h> //MSVC requires that Vc come first
#include "kis_auto_brush_test.h"
#include <QTest>
#include <testutil.h>
#include "../kis_auto_brush.h"
#include "kis_mask_generator.h"
#include "kis_paint_device.h"
#include "kis_fill_painter.h"
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOpRegistry.h>
#include <kis_fixed_paint_device.h>
#include <brushengine/kis_paint_information.h>
void KisAutoBrushTest::testCreation()
{
KisCircleMaskGenerator circle(10, 1.0, 1.0, 1.0, 2, true);
KisRectangleMaskGenerator rect(10, 1.0, 1.0, 1.0, 2, true);
}
void KisAutoBrushTest::testMaskGeneration()
{
KisCircleMaskGenerator* circle = new KisCircleMaskGenerator(10, 1.0, 1.0, 1.0, 2, false);
- KisBrushSP a = new KisAutoBrush(circle, 0.0, 0.0);
+ KisBrushSP a(new KisAutoBrush(circle, 0.0, 0.0));
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintInformation info(QPointF(100.0, 100.0), 0.5);
// check masking an existing paint device
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs);
fdev->setRect(QRect(0, 0, 100, 100));
fdev->initialize();
cs->setOpacity(fdev->data(), OPACITY_OPAQUE_U8, 100 * 100);
QPoint errpoint;
QImage result(QString(FILES_DATA_DIR) + QDir::separator() + "result_autobrush_1.png");
QImage image = fdev->convertToQImage(0);
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("kis_autobrush_test_1.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
// Check creating a mask dab with a single color
fdev = new KisFixedPaintDevice(cs);
a->mask(fdev, KoColor(Qt::black, cs), KisDabShape(), info);
result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_autobrush_3.png");
image = fdev->convertToQImage(0);
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("kis_autobrush_test_3.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
// Check creating a mask dab with a color taken from a paint device
KoColor red(Qt::red, cs);
cs->setOpacity(red.data(), quint8(128), 1);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(0, 0, 100, 100, red.data());
fdev = new KisFixedPaintDevice(cs);
a->mask(fdev, dev, KisDabShape(), info);
result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_autobrush_4.png");
image = fdev->convertToQImage(0);
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("kis_autobrush_test_4.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
static void dabSizeHelper(KisBrushSP const& brush,
QString const& name, KisDabShape const& shape, int expectedWidth, int expectedHeight)
{
qDebug() << name;
QCOMPARE(brush->maskWidth(shape, 0.0, 0.0, KisPaintInformation()), expectedWidth);
QCOMPARE(brush->maskHeight(shape, 0.0, 0.0, KisPaintInformation()), expectedHeight);
}
void KisAutoBrushTest::testDabSize()
{
KisCircleMaskGenerator* circle = new KisCircleMaskGenerator(10, 0.5, 1.0, 1.0, 2, false);
- KisBrushSP a = new KisAutoBrush(circle, 0.0, 0.0);
+ KisBrushSP a(new KisAutoBrush(circle, 0.0, 0.0));
QCOMPARE(a->width(), 10);
QCOMPARE(a->height(), 5);
dabSizeHelper(a, "Identity", KisDabShape(), 10, 5);
dabSizeHelper(a, "Double", KisDabShape(2.0, 1.0, 0.0), 20, 10);
dabSizeHelper(a, "Halve", KisDabShape(0.5, 1.0, 0.0), 5, 3);
dabSizeHelper(a, "180 deg", KisDabShape(1.0, 1.0, M_PI), 10, 5);
dabSizeHelper(a, "90 deg", KisDabShape(1.0, 1.0, M_PI_2), 6, 10); // ceil rule
dabSizeHelper(a, "-90 deg", KisDabShape(1.0, 1.0, -M_PI_2), 6, 11); // ceil rule
dabSizeHelper(a, "45 deg", KisDabShape(1.0, 1.0, 0.25 * M_PI), 11, 11);
dabSizeHelper(a, "2x, 45d", KisDabShape(2.0, 1.0, 0.25 * M_PI), 22, 22);
dabSizeHelper(a, "0.5x, 45d", KisDabShape(0.5, 1.0, 0.25 * M_PI), 6, 6);
dabSizeHelper(a, "0.5x, 45d", KisDabShape(0.5, 1.0, 0.25 * M_PI), 6, 6);
dabSizeHelper(a, "0.5y", KisDabShape(1.0, 0.5, 0.0), 10, 5);
}
//#define SAVE_OUTPUT_IMAGES
void KisAutoBrushTest::testCopyMasking()
{
int w = 64;
int h = 64;
int x = 0;
int y = 0;
QRect rc(x, y, w, h);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KoColor black(Qt::black, cs);
KoColor red(Qt::red, cs);
KisPaintDeviceSP tempDev = new KisPaintDevice(cs);
tempDev->fill(0, 0, w, h, red.data());
#ifdef SAVE_OUTPUT_IMAGES
tempDev->convertToQImage(0).save("tempDev.png");
#endif
KisCircleMaskGenerator * mask = new KisCircleMaskGenerator(w, 1.0, 0.5, 0.5, 2, true);
KisAutoBrush brush(mask, 0, 0);
KisFixedPaintDeviceSP maskDab = new KisFixedPaintDevice(cs);
brush.mask(maskDab, black, KisDabShape(), KisPaintInformation());
maskDab->convertTo(KoColorSpaceRegistry::instance()->alpha8());
#ifdef SAVE_OUTPUT_IMAGES
maskDab->convertToQImage(0, 0, 0, 64, 64).save("maskDab.png");
#endif
QCOMPARE(tempDev->exactBounds(), rc);
QCOMPARE(maskDab->bounds(), rc);
KisFixedPaintDeviceSP dev2fixed = new KisFixedPaintDevice(cs);
dev2fixed->setRect(rc);
dev2fixed->initialize();
tempDev->readBytes(dev2fixed->data(), rc);
dev2fixed->convertToQImage(0).save("converted-tempDev-to-fixed.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KisPainter painter(dev);
painter.setCompositeOp(COMPOSITE_COPY);
painter.bltFixedWithFixedSelection(x, y, dev2fixed, maskDab, 0, 0, 0, 0, rc.width(), rc.height());
//painter.bitBltWithFixedSelection(x, y, tempDev, maskDab, 0, 0, 0, 0, rc.width(), rc.height());
#ifdef SAVE_OUTPUT_IMAGES
dev->convertToQImage(0).save("final.png");
#endif
}
void KisAutoBrushTest::testClone()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisCircleMaskGenerator* circle = new KisCircleMaskGenerator(10, 0.7, 0.85, 0.5, 2, true);
- KisBrushSP brush = new KisAutoBrush(circle, 0.5, 0.0);
+ KisBrushSP brush(new KisAutoBrush(circle, 0.5, 0.0));
KisPaintInformation info(QPointF(100.0, 100.0), 0.5);
KisFixedPaintDeviceSP fdev1 = new KisFixedPaintDevice(cs);
brush->mask(fdev1, KoColor(Qt::black, cs), KisDabShape(0.8, 1.0, 8.0), info);
QImage res1 = fdev1->convertToQImage(0);
- KisBrushSP clone = brush->clone();
+ KisBrushSP clone = brush->clone().dynamicCast<KisBrush>();
KisFixedPaintDeviceSP fdev2 = new KisFixedPaintDevice(cs);
clone->mask(fdev2, KoColor(Qt::black, cs), KisDabShape(0.8, 1.0, 8.0), info);
QImage res2 = fdev2->convertToQImage(0);
QCOMPARE(res1, res2);
}
QTEST_MAIN(KisAutoBrushTest)
diff --git a/libs/brush/tests/kis_gbr_brush_test.cpp b/libs/brush/tests/kis_gbr_brush_test.cpp
index e9873b023a..9f62ae753c 100644
--- a/libs/brush/tests/kis_gbr_brush_test.cpp
+++ b/libs/brush/tests/kis_gbr_brush_test.cpp
@@ -1,302 +1,302 @@
/*
* 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 "kis_gbr_brush_test.h"
#include <QTest>
#include <QString>
#include <QDir>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include "testutil.h"
#include "../kis_gbr_brush.h"
#include "kis_types.h"
#include "kis_paint_device.h"
#include "brushengine/kis_paint_information.h"
#include <kis_fixed_paint_device.h>
#include "kis_qimage_pyramid.h"
-
+#include <KisGlobalResourcesInterface.h>
void KisGbrBrushTest::testMaskGenerationSingleColor()
{
QScopedPointer<KisGbrBrush> brush(new KisGbrBrush(QString(FILES_DATA_DIR) + QDir::separator() + "brush.gbr"));
- brush->load();
+ brush->load(KisGlobalResourcesInterface::instance());
Q_ASSERT(brush->valid());
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintInformation info(QPointF(100.0, 100.0), 0.5);
// check masking an existing paint device
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs);
fdev->setRect(QRect(0, 0, 100, 100));
fdev->initialize();
cs->setOpacity(fdev->data(), OPACITY_OPAQUE_U8, 100 * 100);
// Check creating a mask dab with a single color
fdev = new KisFixedPaintDevice(cs);
brush->mask(fdev, KoColor(Qt::black, cs), KisDabShape(), info);
QPoint errpoint;
QImage result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_brush_3.png");
QImage image = fdev->convertToQImage(0);
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("kis_gbr_brush_test_3.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisGbrBrushTest::testMaskGenerationDevColor()
{
QScopedPointer<KisGbrBrush> brush(new KisGbrBrush(QString(FILES_DATA_DIR) + QDir::separator() + "brush.gbr"));
- brush->load();
+ brush->load(KisGlobalResourcesInterface::instance());
Q_ASSERT(brush->valid());
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintInformation info(QPointF(100.0, 100.0), 0.5);
// check masking an existing paint device
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs);
fdev->setRect(QRect(0, 0, 100, 100));
fdev->initialize();
cs->setOpacity(fdev->data(), OPACITY_OPAQUE_U8, 100 * 100);
// Check creating a mask dab with a color taken from a paint device
KoColor red(Qt::red, cs);
cs->setOpacity(red.data(), quint8(128), 1);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(0, 0, 100, 100, red.data());
fdev = new KisFixedPaintDevice(cs);
brush->mask(fdev, dev, KisDabShape(), info);
QPoint errpoint;
QImage result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_brush_4.png");
QImage image = fdev->convertToQImage(0);
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("kis_gbr_brush_test_4.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisGbrBrushTest::testImageGeneration()
{
QScopedPointer<KisGbrBrush> brush(new KisGbrBrush(QString(FILES_DATA_DIR) + QDir::separator() + "testing_brush_512_bars.gbr"));
- bool res = brush->load();
+ bool res = brush->load(KisGlobalResourcesInterface::instance());
Q_UNUSED(res);
Q_ASSERT(res);
QVERIFY(!brush->brushTipImage().isNull());
qsrand(1);
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintInformation info(QPointF(100.0, 100.0), 0.5);
KisFixedPaintDeviceSP dab;
for (int i = 0; i < 200; i++) {
qreal scale = qreal(qrand()) / RAND_MAX * 2.0;
qreal rotation = qreal(qrand()) / RAND_MAX * 2 * M_PI;
qreal subPixelX = qreal(qrand()) / RAND_MAX * 0.5;
QString testName =
QString("brush_%1_sc_%2_rot_%3_sub_%4")
.arg(i).arg(scale).arg(rotation).arg(subPixelX);
dab = brush->paintDevice(cs, KisDabShape(scale, 1.0, rotation), info, subPixelX);
/**
* Compare first 10 images. Others are tested for asserts only
*/
if (i < 10) {
QImage result = dab->convertToQImage(0);
TestUtil::checkQImage(result, "brush_masks", "", testName);
}
}
}
#include "KisSharedQImagePyramid.h"
void KisGbrBrushTest::benchmarkPyramidCreation()
{
QScopedPointer<KisGbrBrush> brush(new KisGbrBrush(QString(FILES_DATA_DIR) + QDir::separator() + "testing_brush_512_bars.gbr"));
- brush->load();
+ brush->load(KisGlobalResourcesInterface::instance());
QVERIFY(!brush->brushTipImage().isNull());
QBENCHMARK {
KisSharedQImagePyramid sharedPyramid;
QVERIFY(sharedPyramid.pyramid(brush.data())); // avoid compiler elimination of unused code!
}
}
void KisGbrBrushTest::benchmarkScaling()
{
QScopedPointer<KisGbrBrush> brush(new KisGbrBrush(QString(FILES_DATA_DIR) + QDir::separator() + "testing_brush_512_bars.gbr"));
- brush->load();
+ brush->load(KisGlobalResourcesInterface::instance());
QVERIFY(!brush->brushTipImage().isNull());
qsrand(1);
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintInformation info(QPointF(100.0, 100.0), 0.5);
KisFixedPaintDeviceSP dab;
{
// warm up the pyramid!
dab = brush->paintDevice(cs, KisDabShape(qreal(qrand()) / RAND_MAX * 2.0, 1.0, 0.0), info);
QVERIFY(dab); // avoid compiler elimination of unused code!
dab.clear();
}
QBENCHMARK {
dab = brush->paintDevice(cs, KisDabShape(qreal(qrand()) / RAND_MAX * 2.0, 1.0, 0.0), info);
//dab->convertToQImage(0).save(QString("dab_%1_new_smooth.png").arg(i++));
}
}
void KisGbrBrushTest::benchmarkRotation()
{
QScopedPointer<KisGbrBrush> brush(new KisGbrBrush(QString(FILES_DATA_DIR) + QDir::separator() + "testing_brush_512_bars.gbr"));
- brush->load();
+ brush->load(KisGlobalResourcesInterface::instance());
QVERIFY(!brush->brushTipImage().isNull());
qsrand(1);
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintInformation info(QPointF(100.0, 100.0), 0.5);
KisFixedPaintDeviceSP dab;
QBENCHMARK {
dab = brush->paintDevice(cs, KisDabShape(1.0, 1.0, qreal(qrand()) / RAND_MAX * 2 * M_PI), info);
}
}
void KisGbrBrushTest::benchmarkMaskScaling()
{
QScopedPointer<KisGbrBrush> brush(new KisGbrBrush(QString(FILES_DATA_DIR) + QDir::separator() + "testing_brush_512_bars.gbr"));
- brush->load();
+ brush->load(KisGlobalResourcesInterface::instance());
QVERIFY(!brush->brushTipImage().isNull());
qsrand(1);
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintInformation info(QPointF(100.0, 100.0), 0.5);
KisFixedPaintDeviceSP dab = new KisFixedPaintDevice(cs);
QBENCHMARK {
KoColor c(Qt::black, cs);
qreal scale = qreal(qrand()) / RAND_MAX * 2.0;
brush->mask(dab, c, KisDabShape(scale, 1.0, 0.0), info, 0.0, 0.0, 1.0);
}
}
void KisGbrBrushTest::testPyramidLevelRounding()
{
QSize imageSize(41, 41);
QImage image(imageSize, QImage::Format_ARGB32);
image.fill(0);
KisQImagePyramid pyramid(image);
qreal baseScale;
int baseLevel;
baseLevel = pyramid.findNearestLevel(1.0, &baseScale);
QCOMPARE(baseScale, 1.0);
QCOMPARE(baseLevel, 3);
baseLevel = pyramid.findNearestLevel(2.0, &baseScale);
QCOMPARE(baseScale, 2.0);
QCOMPARE(baseLevel, 2);
baseLevel = pyramid.findNearestLevel(4.0, &baseScale);
QCOMPARE(baseScale, 4.0);
QCOMPARE(baseLevel, 1);
baseLevel = pyramid.findNearestLevel(0.5, &baseScale);
QCOMPARE(baseScale, 0.5);
QCOMPARE(baseLevel, 4);
baseLevel = pyramid.findNearestLevel(0.25, &baseScale);
QCOMPARE(baseScale, 0.25);
QCOMPARE(baseLevel, 5);
baseLevel = pyramid.findNearestLevel(0.25 + 1e-7, &baseScale);
QCOMPARE(baseScale, 0.25);
QCOMPARE(baseLevel, 5);
}
static QSize dabTransformHelper(KisDabShape const& shape)
{
QSize const testSize(150, 150);
qreal const subPixelX = 0.0,
subPixelY = 0.0;
return KisQImagePyramid::imageSize(testSize, shape, subPixelX, subPixelY);
}
void KisGbrBrushTest::testPyramidDabTransform()
{
QCOMPARE(dabTransformHelper(KisDabShape(1.0, 1.0, 0.0)), QSize(150, 150));
QCOMPARE(dabTransformHelper(KisDabShape(1.0, 0.5, 0.0)), QSize(150, 75));
QCOMPARE(dabTransformHelper(KisDabShape(1.0, 1.0, M_PI / 4)), QSize(213, 213));
QCOMPARE(dabTransformHelper(KisDabShape(1.0, 0.5, M_PI / 4)), QSize(160, 160));
}
// see comment in KisQImagePyramid::appendPyramidLevel
void KisGbrBrushTest::testQPainterTransformationBorder()
{
QImage image1(10, 10, QImage::Format_ARGB32);
QImage image2(12, 12, QImage::Format_ARGB32);
image1.fill(0);
image2.fill(0);
{
QPainter gc(&image1);
gc.fillRect(QRect(0, 0, 10, 10), Qt::black);
}
{
QPainter gc(&image2);
gc.fillRect(QRect(1, 1, 10, 10), Qt::black);
}
image1.save("src1.png");
image2.save("src2.png");
{
QImage canvas(100, 100, QImage::Format_ARGB32);
canvas.fill(0);
QPainter gc(&canvas);
QTransform transform;
transform.rotate(15);
gc.setTransform(transform);
gc.setRenderHints(QPainter::SmoothPixmapTransform);
gc.drawImage(QPointF(50, 50), image1);
gc.end();
canvas.save("canvas1.png");
}
{
QImage canvas(100, 100, QImage::Format_ARGB32);
canvas.fill(0);
QPainter gc(&canvas);
QTransform transform;
transform.rotate(15);
gc.setTransform(transform);
gc.setRenderHints(QPainter::SmoothPixmapTransform);
gc.drawImage(QPointF(50, 50), image2);
gc.end();
canvas.save("canvas2.png");
}
}
QTEST_MAIN(KisGbrBrushTest)
diff --git a/libs/brush/tests/kis_imagepipe_brush_test.cpp b/libs/brush/tests/kis_imagepipe_brush_test.cpp
index 6fabb72275..9dc0499b4f 100644
--- a/libs/brush/tests/kis_imagepipe_brush_test.cpp
+++ b/libs/brush/tests/kis_imagepipe_brush_test.cpp
@@ -1,264 +1,265 @@
/*
* 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.
*/
#include "kis_imagepipe_brush_test.h"
#include <QTest>
#include <QPainter>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOpRegistry.h>
#include <kis_fixed_paint_device.h>
#include <brushengine/kis_paint_information.h>
#include "kis_imagepipe_brush.h"
#include <kis_paint_device.h>
#include <kis_painter.h>
+#include <KisGlobalResourcesInterface.h>
#define COMPARE_ALL(brush, method) \
- Q_FOREACH (KisGbrBrush *child, brush->brushes()) { \
+ Q_FOREACH (KisGbrBrushSP child, brush->brushes()) { \
if(brush->method() != child->method()) { \
dbgKrita << "Failing method:" << #method \
<< "brush index:" \
<< brush->brushes().indexOf(child); \
QCOMPARE(brush->method(), child->method()); \
} \
}
-inline void KisImagePipeBrushTest::checkConsistency(KisImagePipeBrush *brush)
+inline void KisImagePipeBrushTest::checkConsistency(KisImagePipeBrushSP brush)
{
qreal scale = 0.5; Q_UNUSED(scale);
- KisGbrBrush *firstBrush = brush->brushes().first();
+ KisGbrBrushSP firstBrush = brush->brushes().first();
/**
* This set of values is supposed to be constant, so
* it is just set to the corresponding values of the
* first brush
*/
QCOMPARE(brush->width(), firstBrush->width());
QCOMPARE(brush->height(), firstBrush->height());
QCOMPARE(brush->boundary(), firstBrush->boundary());
/**
* These values should be spread over the children brushes
*/
COMPARE_ALL(brush, maskAngle);
COMPARE_ALL(brush, scale);
COMPARE_ALL(brush, angle);
COMPARE_ALL(brush, spacing);
/**
* Check mask size values, they depend on current brush
*/
KisPaintInformation info;
- KisBrush *oldBrush = brush->testingGetCurrentBrush(info);
+ KisBrushSP oldBrush = brush->testingGetCurrentBrush(info);
QVERIFY(oldBrush);
qreal realScale = 1;
qreal realAngle = 0;
qreal subPixelX = 0;
qreal subPixelY = 0;
int maskWidth = brush->maskWidth(KisDabShape(realScale, 1.0, realAngle), subPixelX, subPixelY, info);
int maskHeight = brush->maskHeight(KisDabShape(realScale, 1.0, realAngle), subPixelX, subPixelY, info);
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP dev = brush->testingGetCurrentBrush(info)->paintDevice(
cs, KisDabShape(realScale, 1.0, realAngle), info, subPixelX, subPixelY);
QCOMPARE(maskWidth, dev->bounds().width());
QCOMPARE(maskHeight, dev->bounds().height());
- KisBrush *newBrush = brush->testingGetCurrentBrush(info);
+ KisBrushSP newBrush = brush->testingGetCurrentBrush(info);
QCOMPARE(oldBrush, newBrush);
}
void KisImagePipeBrushTest::testLoading()
{
- QScopedPointer<KisImagePipeBrush> brush(new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "C_Dirty_Spot.gih"));
- brush->load();
+ QSharedPointer<KisImagePipeBrush> brush(new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "C_Dirty_Spot.gih"));
+ brush->load(KisGlobalResourcesInterface::instance());
QVERIFY(brush->valid());
- checkConsistency(brush.data());
+ checkConsistency(brush);
}
void KisImagePipeBrushTest::testChangingBrushes()
{
- QScopedPointer<KisImagePipeBrush> brush(new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "C_Dirty_Spot.gih"));
- brush->load();
+ QSharedPointer<KisImagePipeBrush> brush(new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "C_Dirty_Spot.gih"));
+ brush->load(KisGlobalResourcesInterface::instance());
QVERIFY(brush->valid());
qreal rotation = 0;
KisPaintInformation info(QPointF(100.0, 100.0), 0.5, 0, 0, rotation);
for (int i = 0; i < 100; i++) {
- checkConsistency(brush.data());
+ checkConsistency(brush);
brush->testingSelectNextBrush(info);
}
}
-void checkIncrementalPainting(KisBrush *brush, const QString &prefix)
+void checkIncrementalPainting(KisBrushSP brush, const QString &prefix)
{
qreal realScale = 1;
qreal realAngle = 0;
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KoColor fillColor(Qt::red, cs);
KisFixedPaintDeviceSP fixedDab = new KisFixedPaintDevice(cs);
qreal rotation = 0;
qreal subPixelX = 0.0;
qreal subPixelY = 0.0;
KisPaintInformation info(QPointF(100.0, 100.0), 0.5, 0, 0, rotation);
for (int i = 0; i < 20; i++) {
int maskWidth = brush->maskWidth(KisDabShape(realScale, 1.0, realAngle), subPixelX, subPixelY, info);
int maskHeight = brush->maskHeight(KisDabShape(realScale, 1.0, realAngle), subPixelX, subPixelY, info);
QRect fillRect(0, 0, maskWidth, maskHeight);
fixedDab->setRect(fillRect);
fixedDab->lazyGrowBufferWithoutInitialization();
brush->mask(fixedDab, fillColor, KisDabShape(realScale, 1.0, realAngle), info);
QCOMPARE(fixedDab->bounds(), fillRect);
QImage result = fixedDab->convertToQImage(0);
result.save(QString("fixed_dab_%1_%2.png").arg(prefix).arg(i));
}
}
void KisImagePipeBrushTest::testSimpleDabApplication()
{
- QScopedPointer<KisImagePipeBrush> brush(new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "C_Dirty_Spot.gih"));
- brush->load();
+ QSharedPointer<KisImagePipeBrush> brush(new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "C_Dirty_Spot.gih"));
+ brush->load(KisGlobalResourcesInterface::instance());
QVERIFY(brush->valid());
- checkConsistency(brush.data());
- checkIncrementalPainting(brush.data(), "simple");
+ checkConsistency(brush);
+ checkIncrementalPainting(brush, "simple");
}
void KisImagePipeBrushTest::testColoredDab()
{
- QScopedPointer<KisImagePipeBrush> brush(new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "G_Sparks.gih"));
- brush->load();
+ QSharedPointer<KisImagePipeBrush> brush(new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "G_Sparks.gih"));
+ brush->load(KisGlobalResourcesInterface::instance());
QVERIFY(brush->valid());
- checkConsistency(brush.data());
+ checkConsistency(brush);
QCOMPARE(brush->useColorAsMask(), false);
QCOMPARE(brush->hasColor(), true);
QCOMPARE(brush->brushType(), PIPE_IMAGE);
// let it be the mask (should be revertible)
brush->setUseColorAsMask(true);
QCOMPARE(brush->useColorAsMask(), true);
QCOMPARE(brush->hasColor(), true);
QCOMPARE(brush->brushType(), PIPE_MASK);
// revert back
brush->setUseColorAsMask(false);
QCOMPARE(brush->useColorAsMask(), false);
QCOMPARE(brush->hasColor(), true);
QCOMPARE(brush->brushType(), PIPE_IMAGE);
// convert to the mask (irreversible)
brush->makeMaskImage();
QCOMPARE(brush->useColorAsMask(), false);
QCOMPARE(brush->hasColor(), false);
QCOMPARE(brush->brushType(), PIPE_MASK);
- checkConsistency(brush.data());
+ checkConsistency(brush);
}
void KisImagePipeBrushTest::testColoredDabWash()
{
- QScopedPointer<KisImagePipeBrush> brush(new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "G_Sparks.gih"));
- brush->load();
+ QSharedPointer<KisImagePipeBrush> brush(new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "G_Sparks.gih"));
+ brush->load(KisGlobalResourcesInterface::instance());
QVERIFY(brush->valid());
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
qreal rotation = 0;
KisPaintInformation info(QPointF(100.0, 100.0), 0.5, 0, 0, rotation);
KisPaintDeviceSP layer = new KisPaintDevice(cs);
KisPainter painter(layer);
painter.setCompositeOp(COMPOSITE_ALPHA_DARKEN);
- const QVector<KisGbrBrush*> gbrs = brush->brushes();
+ const QVector<KisGbrBrushSP> gbrs = brush->brushes();
KisFixedPaintDeviceSP dab = gbrs.at(0)->paintDevice(cs, KisDabShape(2.0, 1.0, 0.0), info);
painter.bltFixed(0, 0, dab, 0, 0, dab->bounds().width(), dab->bounds().height());
painter.bltFixed(80, 60, dab, 0, 0, dab->bounds().width(), dab->bounds().height());
painter.end();
QRect rc = layer->exactBounds();
QImage result = layer->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
#if 0
// if you want to see the result on white background, set #if 1
QImage bg(result.size(), result.format());
bg.fill(Qt::white);
QPainter qPainter(&bg);
qPainter.drawImage(0, 0, result);
result = bg;
#endif
result.save("z_spark_alpha_darken.png");
}
#include "kis_text_brush.h"
void KisImagePipeBrushTest::testTextBrushNoPipes()
{
- QScopedPointer<KisTextBrush> brush(new KisTextBrush());
+ QSharedPointer<KisTextBrush> brush(new KisTextBrush());
brush->setPipeMode(false);
brush->setFont(QApplication::font());
brush->setText("The_Quick_Brown_Fox_Jumps_Over_The_Lazy_Dog");
brush->updateBrush();
- checkIncrementalPainting(brush.data(), "text_no_incremental");
+ checkIncrementalPainting(brush, "text_no_incremental");
}
void KisImagePipeBrushTest::testTextBrushPiped()
{
- QScopedPointer<KisTextBrush> brush(new KisTextBrush());
+ QSharedPointer<KisTextBrush> brush(new KisTextBrush());
brush->setPipeMode(true);
brush->setFont(QApplication::font());
brush->setText("The_Quick_Brown_Fox_Jumps_Over_The_Lazy_Dog");
brush->updateBrush();
- checkIncrementalPainting(brush.data(), "text_incremental");
+ checkIncrementalPainting(brush, "text_incremental");
}
QTEST_MAIN(KisImagePipeBrushTest)
diff --git a/libs/brush/tests/kis_imagepipe_brush_test.h b/libs/brush/tests/kis_imagepipe_brush_test.h
index e5da39636c..850bd2d23d 100644
--- a/libs/brush/tests/kis_imagepipe_brush_test.h
+++ b/libs/brush/tests/kis_imagepipe_brush_test.h
@@ -1,42 +1,42 @@
/*
* 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_IMAGEPIPE_BRUSH_TEST_H
#define __KIS_IMAGEPIPE_BRUSH_TEST_H
#include <QtTest>
-class KisImagePipeBrush;
+#include <kis_imagepipe_brush.h>
class KisImagePipeBrushTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testLoading();
void testChangingBrushes();
void testSimpleDabApplication();
void testColoredDab();
void testColoredDabWash();
void testTextBrushNoPipes();
void testTextBrushPiped();
private:
- void checkConsistency(KisImagePipeBrush *brush);
+ void checkConsistency(KisImagePipeBrushSP brush);
};
#endif /* __KIS_IMAGEPIPE_BRUSH_TEST_H */
diff --git a/libs/flake/KoDerivedResourceConverter.h b/libs/flake/KoDerivedResourceConverter.h
index f4e9ae8e85..7a0d45b15d 100644
--- a/libs/flake/KoDerivedResourceConverter.h
+++ b/libs/flake/KoDerivedResourceConverter.h
@@ -1,79 +1,102 @@
/*
* 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 __KO_DERIVED_RESOURCE_CONVERTER_H
#define __KO_DERIVED_RESOURCE_CONVERTER_H
#include <QScopedPointer>
#include <QSharedPointer>
#include "kritaflake_export.h"
class QVariant;
/**
* \class KoDerivedResourceConverter
*
* Defines the abstraction of a derived resource. It should be
* uploaded to the KoResourceManager during the loading phase.
* The manager will use it to convert values to/from the source
* resource.
+ *
+ * "Derived" resources are the resources that do not exist temselves.
+ * Instead they are contained in some other resources and are updated
+ * synchronously with the parent resources as well.
+ *
+ * E.g. we store opacity and composite op and opacity in the current
+ * paintop preset, which is also a resource. So composite op and opacity
+ * are "derived" resources.
+ *
+ * The main goal of this class is to make our resources comply with
+ * a general Model-View-Controller architecture:
+ *
+ * Model: KisPaintOpPreset. It stores opacity, composite op, eraser mode
+ * and other "global" properties.
+ *
+ * Controller: KoCanvasResourceManager. It controls access to the resources
+ * and emits notification signals when they are changed.
+ *
+ * View: KisPaintOpBox and other classes that show the resources on screen
+ *
+ * Please take into account that according to the MVC design all the access
+ * to the model resources should be performed through the controller.
+ *
*/
-class KRITAFLAKE_EXPORT KoDerivedResourceConverter
+ class KRITAFLAKE_EXPORT KoDerivedResourceConverter
{
public:
/**
* \param key the unique id of the resource defined by this
* converter
*
* \param sourceKey the id of the parent resource, i.e. where the
* values are really loaded/saved.
*/
KoDerivedResourceConverter(int key, int sourceKey);
virtual ~KoDerivedResourceConverter();
int key() const;
int sourceKey() const;
QVariant readFromSource(const QVariant &value);
QVariant writeToSource(const QVariant &value,
const QVariant &sourceValue,
bool *changed);
virtual bool notifySourceChanged(const QVariant &sourceValue);
protected:
/**
* Converts the \p value of the source resource into the space of
* the "derived" resource. E.g. preset -> opacity.
*/
virtual QVariant fromSource(const QVariant &value) = 0;
/**
* Converts the value of the "derived" resource into the space of the
* original ("source") resource. E.g. opacity -> preset.
*/
virtual QVariant toSource(const QVariant &value, const QVariant &sourceValue) = 0;
private:
struct Private;
const QScopedPointer<Private> m_d;
};
typedef QSharedPointer<KoDerivedResourceConverter> KoDerivedResourceConverterSP;
#endif /* __KO_DERIVED_RESOURCE_CONVERTER_H */
diff --git a/libs/flake/KoPathShapeFactory.cpp b/libs/flake/KoPathShapeFactory.cpp
index 2219151bea..befccf52e9 100644
--- a/libs/flake/KoPathShapeFactory.cpp
+++ b/libs/flake/KoPathShapeFactory.cpp
@@ -1,91 +1,91 @@
/* This file is part of the KDE project
* Copyright (C) 2006 Thomas Zander <zander@kde.org>
* Copyright (C) 2011 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 "KoPathShapeFactory.h"
#include "KoPathShape.h"
#include "KoShapeStroke.h"
#include "KoImageCollection.h"
#include "KoMarkerCollection.h"
#include "KoDocumentResourceManager.h"
#include "KoShapeLoadingContext.h"
#include <KoIcon.h>
#include "KoInsets.h"
#include <klocalizedstring.h>
#include <KoXmlReader.h>
#include <KoXmlNS.h>
#include "kis_pointer_utils.h"
KoPathShapeFactory::KoPathShapeFactory(const QStringList&)
: KoShapeFactoryBase(KoPathShapeId, i18n("Simple path shape"))
{
setToolTip(i18n("A simple path shape"));
setIconName(koIconNameCStr("pathshape"));
QStringList elementNames;
elementNames << "path" << "line" << "polyline" << "polygon";
setXmlElementNames(KoXmlNS::draw, elementNames);
setLoadingPriority(0);
}
KoShape *KoPathShapeFactory::createDefaultShape(KoDocumentResourceManager *) const
{
KoPathShape* path = new KoPathShape();
path->moveTo(QPointF(0, 50));
path->curveTo(QPointF(0, 120), QPointF(50, 120), QPointF(50, 50));
path->curveTo(QPointF(50, -20), QPointF(100, -20), QPointF(100, 50));
path->normalize();
path->setStroke(toQShared(new KoShapeStroke(1.0)));
return path;
}
bool KoPathShapeFactory::supports(const KoXmlElement & e, KoShapeLoadingContext &context) const
{
Q_UNUSED(context);
if (e.namespaceURI() == KoXmlNS::draw) {
if (e.localName() == "path")
return true;
if (e.localName() == "line")
return true;
if (e.localName() == "polyline")
return true;
if (e.localName() == "polygon")
return true;
}
return false;
}
void KoPathShapeFactory::newDocumentResourceManager(KoDocumentResourceManager *manager) const
{
// as we need an image collection for the pattern background
// we want to make sure that there is always an image collection
// added to the data center map, in case the picture shape plugin
// is not loaded
if (manager->imageCollection() == 0) {
KoImageCollection *imgCol = new KoImageCollection(manager);
manager->setImageCollection(imgCol);
}
// we also need a MarkerCollection so add if it is not there yet
if (!manager->hasResource(KoDocumentResourceManager::MarkerCollection)) {
KoMarkerCollection *markerCollection = new KoMarkerCollection(manager);
- manager->setResource(KoDocumentResourceManager::MarkerCollection, qVariantFromValue(markerCollection));
+ manager->setResource(KoDocumentResourceManager::MarkerCollection, QVariant::fromValue(markerCollection));
}
}
diff --git a/libs/flake/KoResourceManager_p.h b/libs/flake/KoResourceManager_p.h
index e4d2140baa..c6f37da5f2 100644
--- a/libs/flake/KoResourceManager_p.h
+++ b/libs/flake/KoResourceManager_p.h
@@ -1,220 +1,226 @@
/*
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;
+/**
+ * @brief The KoResourceManager class provides access to the currently
+ * active resources for a given canvas. It has nearly zilch to do with
+ * the system that provides resources like brushes or palettes to the
+ * application.
+ */
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 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 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/KoShape.h b/libs/flake/KoShape.h
index 338cadf35c..e9fefbdf97 100644
--- a/libs/flake/KoShape.h
+++ b/libs/flake/KoShape.h
@@ -1,1273 +1,1273 @@
/* 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 <QSharedDataPointer>
#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 KoFilterEffectStack;
class KoSnapData;
class KoClipPath;
class KoClipMask;
class KoShapePaintingContext;
class KoShapeAnchor;
class KoBorder;
struct KoInsets;
class KoShapeBackground;
class KisHandlePainterHelper;
class KoShapeManager;
/**
* 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()
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
ClipMaskChanged, ///< 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. \p painter is expected
* to be preconfigured to work in "document" pixels.
*
* @param painter used for painting the shape
* @param paintcontext the painting context.
*/
virtual void paint(QPainter &painter, KoShapePaintingContext &paintcontext) const = 0;
/**
* @brief paintStroke paints the shape's stroked outline
* @param painter used for painting the shape
* @see applyConversion()
* @param paintcontext the painting context.
*/
virtual void paintStroke(QPainter &painter, KoShapePaintingContext &paintcontext) const;
/**
* 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() 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);
/**
* @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 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 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() const;
/**
* 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.
*/
QTransform absoluteTransformation() 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);
/**
* 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 createHandlePainterHelperView(QPainter *painter, KoShape *shape, const KoViewConverter &converter, qreal handleRadius = 0.0);
static KisHandlePainterHelper createHandlePainterHelperDocument(QPainter *painter, KoShape *shape, qreal handleRadius);
/**
* @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 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(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);
/**
* 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);
public:
struct KRITAFLAKE_EXPORT ShapeChangeListener {
virtual ~ShapeChangeListener();
virtual void notifyShapeChanged(ChangeType type, KoShape *shape) = 0;
private:
friend class KoShape;
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);
protected:
QList<ShapeChangeListener *> listeners() const;
void setSizeImpl(const QSizeF &size) const;
public:
static QList<KoShape*> linearizeSubtree(const QList<KoShape*> &shapes);
static QList<KoShape *> linearizeSubtreeSorted(const QList<KoShape *> &shapes);
protected:
KoShape(const KoShape &rhs);
/* ** 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;
private:
- struct Private;
+ class Private;
QScopedPointer<Private> d;
class SharedData;
QSharedDataPointer<SharedData> s;
protected:
/**
* Notify the shape that a change was done. To be used by inheriting shapes.
* @param type the change type
*/
void shapeChangedPriv(KoShape::ChangeType type);
private:
void addShapeManager(KoShapeManager *manager);
void removeShapeManager(KoShapeManager *manager);
friend class KoShapeManager;
};
Q_DECLARE_METATYPE(KoShape*)
#endif
diff --git a/libs/flake/KoShapeManager.cpp b/libs/flake/KoShapeManager.cpp
index 52180bd9f0..be13450751 100644
--- a/libs/flake/KoShapeManager.cpp
+++ b/libs/flake/KoShapeManager.cpp
@@ -1,781 +1,781 @@
/* 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) 2009-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 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 "KoShapeManager.h"
#include "KoShapeManager_p.h"
#include "KoSelection.h"
#include "KoToolManager.h"
#include "KoPointerEvent.h"
#include "KoShape.h"
#include "KoShape_p.h"
#include "KoCanvasBase.h"
#include "KoShapeContainer.h"
#include "KoShapeStrokeModel.h"
#include "KoShapeGroup.h"
#include "KoToolProxy.h"
#include "KoShapeShadow.h"
#include "KoShapeLayer.h"
#include "KoFilterEffect.h"
#include "KoFilterEffectStack.h"
#include "KoFilterEffectRenderContext.h"
#include "KoShapeBackground.h"
#include <KoRTree.h>
#include "KoClipPath.h"
#include "KoClipMaskPainter.h"
#include "KoShapePaintingContext.h"
#include "KoViewConverter.h"
#include "KisQPainterStateSaver.h"
#include "KoSvgTextChunkShape.h"
#include "KoSvgTextShape.h"
#include <QApplication>
#include <QPainter>
#include <QTimer>
#include <FlakeDebug.h>
#include "kis_painting_tweaks.h"
#include "kis_debug.h"
#include "KisForest.h"
#include <unordered_set>
namespace {
/**
* Returns whether the shape should be added to the RTree for collision and ROI
* detection.
*/
inline bool shapeUsedInRenderingTree(KoShape *shape)
{
// FIXME: make more general!
return !dynamic_cast<KoShapeGroup*>(shape) &&
!dynamic_cast<KoShapeLayer*>(shape) &&
!(dynamic_cast<KoSvgTextChunkShape*>(shape) && !dynamic_cast<KoSvgTextShape*>(shape));
}
/**
* Returns whether a shape should be added to the rendering tree because of
* its clip mask/path or effects.
*/
inline bool shapeHasGroupEffects(KoShape *shape) {
return shape->clipPath() ||
(shape->filterEffectStack() && !shape->filterEffectStack()->isEmpty()) ||
shape->clipMask();
}
/**
* Returns true if the shape is not fully transparent
*/
inline bool shapeIsVisible(KoShape *shape) {
return shape->isVisible(false) && shape->transparency() < 1.0;
}
/**
* Populate \p tree with the subtree of shapes pointed by a shape \p parentShape.
* All new shapes are added as children of \p parentIt. Please take it into account
* that \c *parentIt might be not the same as \c parentShape, because \c parentShape
* may be hidden from rendering.
*/
void populateRenderSubtree(KoShape *parentShape,
KisForest<KoShape*>::child_iterator parentIt,
KisForest<KoShape*> &tree,
std::function<bool(KoShape*)> shouldIncludeNode,
std::function<bool(KoShape*)> shouldEnterSubtree)
{
KoShapeContainer *parentContainer = dynamic_cast<KoShapeContainer*>(parentShape);
if (!parentContainer) return;
QList<KoShape*> children = parentContainer->shapes();
std::sort(children.begin(), children.end(), KoShape::compareShapeZIndex);
for (auto it = children.constBegin(); it != children.constEnd(); ++it) {
auto newParentIt = parentIt;
if (shouldIncludeNode(*it)) {
newParentIt = tree.insert(childEnd(parentIt), *it);
}
if (shouldEnterSubtree(*it)) {
populateRenderSubtree(*it, newParentIt, tree, shouldIncludeNode, shouldEnterSubtree);
}
}
}
/**
* Build a rendering tree for **leaf** nodes defined by \p leafNodes
*
* Sometimes we should render only a part of the layer (e.g. when we render
* in patches). So we shouldn't render the whole graph. The problem is that
* some of the shapes may have parents with clip paths/masks and/or effects.
* In such a case, these parents should be also included into the rendering
* process.
*
* \c buildRenderTree() builds a graph for such rendering. It includes the
* leaf shapes themselves, and all parent shapes that have some effects affecting
* these shapes.
*/
void buildRenderTree(QList<KoShape*> leafShapes,
KisForest<KoShape*> &tree)
{
QList<KoShape*> sortedShapes = leafShapes;
std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
std::unordered_set<KoShape*> includedShapes;
Q_FOREACH (KoShape *shape, sortedShapes) {
bool shouldSkipShape = !shapeIsVisible(shape);
if (shouldSkipShape) continue;
bool shapeIsPartOfIncludedSubtree = false;
QVector<KoShape*> hierarchy = {shape};
while ((shape = shape->parent())) {
if (!shapeIsVisible(shape)) {
shouldSkipShape = true;
break;
}
if (includedShapes.find(shape) != end(includedShapes)) {
shapeIsPartOfIncludedSubtree = true;
break;
}
if (shapeHasGroupEffects(shape)) {
hierarchy << shape;
}
}
if (shouldSkipShape) continue;
if (!shapeIsPartOfIncludedSubtree &&
includedShapes.find(hierarchy.last()) == end(includedShapes)) {
tree.insert(childEnd(tree), hierarchy.last());
}
std::copy(hierarchy.begin(), hierarchy.end(),
std::inserter(includedShapes, end(includedShapes)));
}
auto shouldIncludeShape =
[includedShapes] (KoShape *shape) {
// included shapes are guaranteed to be visible
return includedShapes.find(shape) != end(includedShapes);
};
for (auto it = childBegin(tree); it != childEnd(tree); ++it) {
populateRenderSubtree(*it, it, tree, shouldIncludeShape, &shapeIsVisible);
}
}
/**
* Render the prebuilt rendering tree on \p painter
*/
void renderShapes(typename KisForest<KoShape*>::child_iterator beginIt,
typename KisForest<KoShape*>::child_iterator endIt,
QPainter &painter,
KoShapePaintingContext &paintContext)
{
for (auto it = beginIt; it != endIt; ++it) {
KoShape *shape = *it;
KisQPainterStateSaver saver(&painter);
if (!isEnd(parent(it))) {
painter.setTransform(shape->transformation() * painter.transform());
} else {
painter.setTransform(shape->absoluteTransformation() * painter.transform());
}
KoClipPath::applyClipping(shape, painter);
qreal transparency = shape->transparency(true);
if (transparency > 0.0) {
painter.setOpacity(1.0-transparency);
}
if (shape->shadow()) {
KisQPainterStateSaver saver(&painter);
shape->shadow()->paint(shape, painter);
}
QScopedPointer<KoClipMaskPainter> clipMaskPainter;
QPainter *shapePainter = &painter;
KoClipMask *clipMask = shape->clipMask();
if (clipMask) {
const QRectF bounds = painter.transform().mapRect(shape->outlineRect());
clipMaskPainter.reset(new KoClipMaskPainter(&painter, bounds/*shape->boundingRect())*/));
shapePainter = clipMaskPainter->shapePainter();
}
/**
* We expect the shape to save/restore the painter's state itself. Such design was not
* not always here, so we need a period of sanity checks to ensure all the shapes are
* ported correctly.
*/
const QTransform sanityCheckTransformSaved = shapePainter->transform();
renderShapes(childBegin(it), childEnd(it), *shapePainter, paintContext);
shape->paint(*shapePainter, paintContext);
shape->paintStroke(*shapePainter, paintContext);
KIS_SAFE_ASSERT_RECOVER(shapePainter->transform() == sanityCheckTransformSaved) {
shapePainter->setTransform(sanityCheckTransformSaved);
}
if (clipMask) {
clipMaskPainter->maskPainter()->save();
shape->clipMask()->drawMask(clipMaskPainter->maskPainter(), shape);
clipMaskPainter->renderOnGlobalPainter();
clipMaskPainter->maskPainter()->restore();
}
}
}
}
void KoShapeManager::Private::updateTree()
{
bool selectionModified = false;
bool anyModified = false;
{
QMutexLocker l(&this->treeMutex);
Q_FOREACH (KoShape *shape, aggregate4update) {
selectionModified = selectionModified || selection->isSelected(shape);
anyModified = true;
}
foreach (KoShape *shape, aggregate4update) {
if (!shapeUsedInRenderingTree(shape)) continue;
tree.remove(shape);
QRectF br(shape->boundingRect());
tree.insert(br, shape);
}
aggregate4update.clear();
shapeIndexesBeforeUpdate.clear();
}
if (selectionModified) {
emit q->selectionContentChanged();
}
if (anyModified) {
emit q->contentChanged();
}
}
void KoShapeManager::Private::forwardCompressedUdpate()
{
bool shouldUpdateDecorations = false;
QRectF scheduledUpdate;
{
QMutexLocker l(&shapesMutex);
if (!compressedUpdate.isEmpty()) {
scheduledUpdate = compressedUpdate;
compressedUpdate = QRect();
}
Q_FOREACH (const KoShape *shape, compressedUpdatedShapes) {
if (selection->isSelected(shape)) {
shouldUpdateDecorations = true;
break;
}
}
compressedUpdatedShapes.clear();
}
if (shouldUpdateDecorations && canvas->toolProxy()) {
canvas->toolProxy()->repaintDecorations();
}
canvas->updateCanvas(scheduledUpdate);
}
KoShapeManager::KoShapeManager(KoCanvasBase *canvas, const QList<KoShape *> &shapes)
: d(new Private(this, canvas))
{
Q_ASSERT(d->canvas); // not optional.
connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
setShapes(shapes);
/**
* Shape manager uses signal compressors with timers, therefore
* it might handle queued signals, therefore it should belong
* to the GUI thread.
*/
this->moveToThread(qApp->thread());
connect(&d->updateCompressor, SIGNAL(timeout()), this, SLOT(forwardCompressedUdpate()));
}
KoShapeManager::KoShapeManager(KoCanvasBase *canvas)
: d(new Private(this, canvas))
{
Q_ASSERT(d->canvas); // not optional.
connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
// see a comment in another constructor
this->moveToThread(qApp->thread());
connect(&d->updateCompressor, SIGNAL(timeout()), this, SLOT(forwardCompressedUdpate()));
}
void KoShapeManager::Private::unlinkFromShapesRecursively(const QList<KoShape*> &shapes)
{
Q_FOREACH (KoShape *shape, shapes) {
shape->removeShapeManager(q);
KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
if (container) {
unlinkFromShapesRecursively(container->shapes());
}
}
}
KoShapeManager::~KoShapeManager()
{
d->unlinkFromShapesRecursively(d->shapes);
d->shapes.clear();
delete d;
}
void KoShapeManager::setShapes(const QList<KoShape *> &shapes, Repaint repaint)
{
{
QMutexLocker l1(&d->shapesMutex);
QMutexLocker l2(&d->treeMutex);
//clear selection
d->selection->deselectAll();
d->unlinkFromShapesRecursively(d->shapes);
d->compressedUpdate = QRect();
d->compressedUpdatedShapes.clear();
d->aggregate4update.clear();
d->shapeIndexesBeforeUpdate.clear();
d->tree.clear();
d->shapes.clear();
}
Q_FOREACH (KoShape *shape, shapes) {
addShape(shape, repaint);
}
}
void KoShapeManager::addShape(KoShape *shape, Repaint repaint)
{
{
QMutexLocker l1(&d->shapesMutex);
if (d->shapes.contains(shape))
return;
shape->addShapeManager(this);
d->shapes.append(shape);
if (shapeUsedInRenderingTree(shape)) {
QMutexLocker l2(&d->treeMutex);
QRectF br(shape->boundingRect());
d->tree.insert(br, shape);
}
}
if (repaint == PaintShapeOnAdd) {
shape->update();
}
// add the children of a KoShapeContainer
KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
if (container) {
foreach (KoShape *containerShape, container->shapes()) {
addShape(containerShape, repaint);
}
}
}
void KoShapeManager::remove(KoShape *shape)
{
QRectF dirtyRect;
{
QMutexLocker l1(&d->shapesMutex);
QMutexLocker l2(&d->treeMutex);
dirtyRect = shape->absoluteOutlineRect();
shape->removeShapeManager(this);
d->selection->deselect(shape);
d->aggregate4update.remove(shape);
d->compressedUpdatedShapes.remove(shape);
if (shapeUsedInRenderingTree(shape)) {
d->tree.remove(shape);
}
d->shapes.removeAll(shape);
}
if (!dirtyRect.isEmpty()) {
d->canvas->updateCanvas(dirtyRect);
}
// remove the children of a KoShapeContainer
KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
if (container) {
foreach (KoShape *containerShape, container->shapes()) {
remove(containerShape);
}
}
}
KoShapeManager::ShapeInterface::ShapeInterface(KoShapeManager *_q)
: q(_q)
{
}
void KoShapeManager::ShapeInterface::notifyShapeDestructed(KoShape *shape)
{
QMutexLocker l1(&q->d->shapesMutex);
QMutexLocker l2(&q->d->treeMutex);
q->d->selection->deselect(shape);
q->d->aggregate4update.remove(shape);
q->d->compressedUpdatedShapes.remove(shape);
// we cannot access RTTI of the semi-destructed shape, so just
// unlink it lazily
if (q->d->tree.contains(shape)) {
q->d->tree.remove(shape);
}
q->d->shapes.removeAll(shape);
}
KoShapeManager::ShapeInterface *KoShapeManager::shapeInterface()
{
return &d->shapeInterface;
}
void KoShapeManager::preparePaintJobs(PaintJobsList &jobs,
KoShape *excludeRoot)
{
d->updateTree();
QMutexLocker l1(&d->shapesMutex);
QSet<KoShape*> rootShapesSet;
Q_FOREACH (KoShape *shape, d->shapes) {
while (shape->parent() && shape->parent() != excludeRoot) {
shape = shape->parent();
}
if (!rootShapesSet.contains(shape) && shape != excludeRoot) {
rootShapesSet.insert(shape);
}
}
- const QList<KoShape*> rootShapes = rootShapesSet.toList();
+ const QList<KoShape*> rootShapes(rootShapesSet.begin(), rootShapesSet.end());
QList<KoShape*> newRootShapes;
Q_FOREACH (KoShape *srcShape, rootShapes) {
KIS_SAFE_ASSERT_RECOVER(srcShape->parent() == excludeRoot) { continue; }
KoShape *clonedShape = srcShape->cloneShape();
KoShapeContainer *parentShape = srcShape->parent();
if (parentShape && !parentShape->transformation().isIdentity()) {
clonedShape->applyAbsoluteTransformation(parentShape->transformation());
}
newRootShapes << clonedShape;
}
PaintJobsList result;
PaintJob::SharedSafeStorage shapesStorage = std::make_shared<PaintJob::ShapesStorage>();
Q_FOREACH (KoShape *shape, newRootShapes) {
shapesStorage->emplace_back(std::unique_ptr<KoShape>(shape));
}
const QList<KoShape*> originalShapes = KoShape::linearizeSubtreeSorted(rootShapes);
const QList<KoShape*> clonedShapes = KoShape::linearizeSubtreeSorted(newRootShapes);
KIS_SAFE_ASSERT_RECOVER_RETURN(clonedShapes.size() == originalShapes.size());
QHash<KoShape*, KoShape*> clonedFromOriginal;
for (int i = 0; i < originalShapes.size(); i++) {
clonedFromOriginal[originalShapes[i]] = clonedShapes[i];
}
for (auto it = std::begin(jobs); it != std::end(jobs); ++it) {
QMutexLocker l(&d->treeMutex);
QList<KoShape*> unsortedOriginalShapes = d->tree.intersects(it->docUpdateRect);
it->allClonedShapes = shapesStorage;
Q_FOREACH (KoShape *shape, unsortedOriginalShapes) {
KIS_SAFE_ASSERT_RECOVER(shapeUsedInRenderingTree(shape)) { continue; }
it->shapes << clonedFromOriginal[shape];
}
}
}
void KoShapeManager::paintJob(QPainter &painter, const KoShapeManager::PaintJob &job, bool forPrint)
{
painter.setPen(Qt::NoPen); // painters by default have a black stroke, lets turn that off.
painter.setBrush(Qt::NoBrush);
KisForest<KoShape*> renderTree;
buildRenderTree(job.shapes, renderTree);
KoShapePaintingContext paintContext(d->canvas, forPrint); //FIXME
renderShapes(childBegin(renderTree), childEnd(renderTree), painter, paintContext);
}
void KoShapeManager::paint(QPainter &painter, bool forPrint)
{
d->updateTree();
QMutexLocker l1(&d->shapesMutex);
painter.setPen(Qt::NoPen); // painters by default have a black stroke, lets turn that off.
painter.setBrush(Qt::NoBrush);
QList<KoShape*> unsortedShapes;
if (painter.hasClipping()) {
QMutexLocker l(&d->treeMutex);
QRectF rect = KisPaintingTweaks::safeClipBoundingRect(painter);
unsortedShapes = d->tree.intersects(rect);
} else {
unsortedShapes = d->shapes;
warnFlake << "KoShapeManager::paint Painting with a painter that has no clipping will lead to too much being painted!";
}
KoShapePaintingContext paintContext(d->canvas, forPrint); //FIXME
KisForest<KoShape*> renderTree;
buildRenderTree(unsortedShapes, renderTree);
renderShapes(childBegin(renderTree), childEnd(renderTree), painter, paintContext);
}
void KoShapeManager::renderSingleShape(KoShape *shape, QPainter &painter, KoShapePaintingContext &paintContext)
{
KisForest<KoShape*> renderTree;
KoViewConverter converter;
auto root = renderTree.insert(childBegin(renderTree), shape);
populateRenderSubtree(shape, root, renderTree, &shapeIsVisible, &shapeIsVisible);
renderShapes(childBegin(renderTree), childEnd(renderTree), painter, paintContext);
}
KoShape *KoShapeManager::shapeAt(const QPointF &position, KoFlake::ShapeSelection selection, bool omitHiddenShapes)
{
d->updateTree();
QMutexLocker l(&d->shapesMutex);
QList<KoShape*> sortedShapes;
{
QMutexLocker l(&d->treeMutex);
sortedShapes = d->tree.contains(position);
}
std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
KoShape *firstUnselectedShape = 0;
for (int count = sortedShapes.count() - 1; count >= 0; count--) {
KoShape *shape = sortedShapes.at(count);
if (omitHiddenShapes && ! shape->isVisible())
continue;
if (! shape->hitTest(position))
continue;
switch (selection) {
case KoFlake::ShapeOnTop:
if (shape->isSelectable())
return shape;
break;
case KoFlake::Selected:
if (d->selection->isSelected(shape))
return shape;
break;
case KoFlake::Unselected:
if (! d->selection->isSelected(shape))
return shape;
break;
case KoFlake::NextUnselected:
// we want an unselected shape
if (d->selection->isSelected(shape))
continue;
// memorize the first unselected shape
if (! firstUnselectedShape)
firstUnselectedShape = shape;
// check if the shape above is selected
if (count + 1 < sortedShapes.count() && d->selection->isSelected(sortedShapes.at(count + 1)))
return shape;
break;
}
}
// if we want the next unselected below a selected but there was none selected,
// return the first found unselected shape
if (selection == KoFlake::NextUnselected && firstUnselectedShape)
return firstUnselectedShape;
if (d->selection->hitTest(position))
return d->selection;
return 0; // missed everything
}
QList<KoShape *> KoShapeManager::shapesAt(const QRectF &rect, bool omitHiddenShapes, bool containedMode)
{
QMutexLocker l(&d->shapesMutex);
d->updateTree();
QList<KoShape*> shapes;
{
QMutexLocker l(&d->treeMutex);
shapes = containedMode ? d->tree.contained(rect) : d->tree.intersects(rect);
}
for (int count = shapes.count() - 1; count >= 0; count--) {
KoShape *shape = shapes.at(count);
if (omitHiddenShapes && !shape->isVisible()) {
shapes.removeAt(count);
} else {
const QPainterPath outline = shape->absoluteTransformation().map(shape->outline());
if (!containedMode && !outline.intersects(rect) && !outline.contains(rect)) {
shapes.removeAt(count);
} else if (containedMode) {
QPainterPath containingPath;
containingPath.addRect(rect);
if (!containingPath.contains(outline)) {
shapes.removeAt(count);
}
}
}
}
return shapes;
}
void KoShapeManager::update(const QRectF &rect, const KoShape *shape, bool selectionHandles)
{
if (d->updatesBlocked) return;
{
QMutexLocker l(&d->shapesMutex);
d->compressedUpdate |= rect;
if (selectionHandles) {
d->compressedUpdatedShapes.insert(shape);
}
}
d->updateCompressor.start();
}
void KoShapeManager::setUpdatesBlocked(bool value)
{
d->updatesBlocked = value;
}
bool KoShapeManager::updatesBlocked() const
{
return d->updatesBlocked;
}
void KoShapeManager::notifyShapeChanged(KoShape *shape)
{
{
QMutexLocker l(&d->treeMutex);
Q_ASSERT(shape);
if (d->aggregate4update.contains(shape)) {
return;
}
d->aggregate4update.insert(shape);
d->shapeIndexesBeforeUpdate.insert(shape, shape->zIndex());
}
KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
if (container) {
Q_FOREACH (KoShape *child, container->shapes())
notifyShapeChanged(child);
}
}
QList<KoShape*> KoShapeManager::shapes() const
{
QMutexLocker l(&d->shapesMutex);
return d->shapes;
}
QList<KoShape*> KoShapeManager::topLevelShapes() const
{
QMutexLocker l(&d->shapesMutex);
QList<KoShape*> shapes;
// get all toplevel shapes
Q_FOREACH (KoShape *shape, d->shapes) {
if (!shape->parent() || dynamic_cast<KoShapeLayer*>(shape->parent())) {
shapes.append(shape);
}
}
return shapes;
}
KoSelection *KoShapeManager::selection() const
{
return d->selection;
}
KoCanvasBase *KoShapeManager::canvas()
{
return d->canvas;
}
//have to include this because of Q_PRIVATE_SLOT
#include "moc_KoShapeManager.cpp"
diff --git a/libs/flake/KoToolManager.cpp b/libs/flake/KoToolManager.cpp
index 429fa978f3..8d0cd4e94e 100644
--- a/libs/flake/KoToolManager.cpp
+++ b/libs/flake/KoToolManager.cpp
@@ -1,958 +1,958 @@
/* This file is part of the KDE project
*
* Copyright (c) 2005-2010 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2006-2008 Thomas Zander <zander@kde.org>
* Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
* Copyright (C) 2008 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.
*/
// flake
#include "KoToolManager.h"
#include "KoToolManager_p.h"
#include "KoToolRegistry.h"
#include "KoToolProxy.h"
#include "KoToolProxy_p.h"
#include "KoSelection.h"
#include "KoCanvasController.h"
#include "KoCanvasControllerWidget.h"
#include "KoShape.h"
#include "KoShapeLayer.h"
#include "KoShapeRegistry.h"
#include "KoShapeManager.h"
#include "KoSelectedShapesProxy.h"
#include "KoCanvasBase.h"
#include "KoInputDeviceHandlerRegistry.h"
#include "KoInputDeviceHandlerEvent.h"
#include "KoPointerEvent.h"
#include "tools/KoZoomTool.h"
#include "kis_action_registry.h"
#include "KoToolFactoryBase.h"
#include "kis_assert.h"
#include <krita_container_utils.h>
// Qt + kde
#include <QWidget>
#include <QEvent>
#include <QWheelEvent>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QTabletEvent>
#include <QKeyEvent>
#include <QVBoxLayout>
#include <QStringList>
#include <QApplication>
#include <kactioncollection.h>
#include <kactioncategory.h>
#include <FlakeDebug.h>
#include <QAction>
#include <klocalizedstring.h>
#include <QKeySequence>
#include <QStack>
#include <QLabel>
#include <QGlobalStatic>
Q_GLOBAL_STATIC(KoToolManager, s_instance)
class CanvasData
{
public:
CanvasData(KoCanvasController *cc, const KoInputDevice &id)
: activeTool(0),
canvas(cc),
inputDevice(id),
dummyToolWidget(0),
dummyToolLabel(0)
{
}
~CanvasData()
{
// the dummy tool widget does not necessarily have a parent and we create it, so we delete it.
delete dummyToolWidget;
}
void activateToolActions()
{
toolActions.clear();
disabledGlobalActions.clear();
KActionCollection *windowActionCollection = canvas->actionCollection();
if (!windowActionCollection) {
qWarning() << "We haven't got an action collection";
return;
}
QStringList globalActions;
QMap<QKeySequence, QStringList> shortcutMap;
//qDebug() << "................... activating tool" << activeToolId;
Q_FOREACH(QAction *action, windowActionCollection->actions()) {
//qDebug() << "Action" << action->objectName() << "shortcuts" << action->shortcuts();
if (action->property("tool_action").isValid()) {
QStringList tools = action->property("tool_action").toStringList();
//qDebug() << "\tassociated with" << tools;
if (tools.contains(activeToolId)) {
//qDebug() << "\t\tenabling";
action->setEnabled(true);
toolActions << action->objectName();
}
else {
action->setDisabled(true);
}
}
else {
globalActions << action->objectName();
}
Q_FOREACH(QKeySequence keySequence, action->shortcuts()) {
// After loading a custom shortcut profile, shortcuts can be defined as an empty string, which is not an empty shortcut
if (keySequence.toString() != "") {
if (shortcutMap.contains(keySequence)) {
shortcutMap[keySequence].append(action->objectName());
}
else {
shortcutMap[keySequence] = QStringList() << action->objectName();
}
}
}
}
// Make sure the tool's actions override the global actions that aren't associated with the tool.
Q_FOREACH(const QKeySequence &k, shortcutMap.keys()) {
if (shortcutMap[k].size() > 1) {
QStringList actions = shortcutMap[k];
//qDebug() << k << actions;
bool toolActionFound = false;
Q_FOREACH(const QString &action, actions) {
if (toolActions.contains(action)) {
toolActionFound = true;
}
}
Q_FOREACH(const QString &action, actions) {
if (toolActionFound && globalActions.contains(action)) {
//qDebug() << "\tdisabling global action" << action;
windowActionCollection->action(action)->setEnabled(false);
disabledGlobalActions << action;
}
}
//qDebug() << k << shortcutMap[k];
}
}
windowActionCollection->readSettings(); // The shortcuts might have been configured in the meantime.
}
void deactivateToolActions()
{
if (!activeTool)
return;
//qDebug() << "............... deactivating previous tool because activating" << activeToolId;
KActionCollection *windowActionCollection = canvas->actionCollection();
Q_FOREACH(const QString &action, toolActions) {
//qDebug() << "disabling" << action;
windowActionCollection->action(action)->setDisabled(true);
}
Q_FOREACH(const QString &action, disabledGlobalActions) {
//qDebug() << "enabling" << action;
windowActionCollection->action(action)->setEnabled(true);
}
}
KoToolBase *activeTool; // active Tool
QString activeToolId; // the id of the active Tool
QString activationShapeId; // the shape-type (KoShape::shapeId()) the activeTool 'belongs' to.
QHash<QString, KoToolBase*> allTools; // all the tools that are created for this canvas.
QStack<QString> stack; // stack of temporary tools
KoCanvasController *const canvas;
const KoInputDevice inputDevice;
QWidget *dummyToolWidget; // the widget shown in the toolDocker.
QLabel *dummyToolLabel;
QStringList toolActions;
QStringList disabledGlobalActions;
};
// ******** KoToolManager **********
KoToolManager::KoToolManager()
: QObject(),
d(new Private(this))
{
connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*,QWidget*)),
this, SLOT(movedFocus(QWidget*,QWidget*)));
}
KoToolManager::~KoToolManager()
{
delete d;
}
QList<KoToolAction*> KoToolManager::toolActionList() const
{
QList<KoToolAction*> answer;
answer.reserve(d->tools.count());
Q_FOREACH (ToolHelper *tool, d->tools) {
answer.append(tool->toolAction());
}
return answer;
}
void KoToolManager::requestToolActivation(KoCanvasController * controller)
{
if (d->canvasses.contains(controller)) {
QString activeToolId = d->canvasses.value(controller).first()->activeToolId;
Q_FOREACH (ToolHelper * th, d->tools) {
if (th->id() == activeToolId) {
d->toolActivated(th);
break;
}
}
}
}
KoInputDevice KoToolManager::currentInputDevice() const
{
return d->inputDevice;
}
void KoToolManager::registerToolActions(KActionCollection *ac, KoCanvasController *controller)
{
Q_ASSERT(controller);
Q_ASSERT(ac);
d->setup();
if (!d->canvasses.contains(controller)) {
return;
}
// Actions used to switch tools via shortcuts
Q_FOREACH (ToolHelper * th, d->tools) {
if (ac->action(th->id())) {
continue;
}
ShortcutToolAction* action = th->createShortcutToolAction(ac);
ac->addCategorizedAction(th->id(), action, "tool-shortcuts");
}
}
void KoToolManager::addController(KoCanvasController *controller)
{
Q_ASSERT(controller);
if (d->canvasses.contains(controller))
return;
d->setup();
d->attachCanvas(controller);
connect(controller->proxyObject, SIGNAL(destroyed(QObject*)), this, SLOT(attemptCanvasControllerRemoval(QObject*)));
connect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*)));
connect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*)));
}
void KoToolManager::removeCanvasController(KoCanvasController *controller)
{
Q_ASSERT(controller);
disconnect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*)));
disconnect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*)));
d->detachCanvas(controller);
}
void KoToolManager::attemptCanvasControllerRemoval(QObject* controller)
{
KoCanvasControllerProxyObject* controllerActual = qobject_cast<KoCanvasControllerProxyObject*>(controller);
if (controllerActual) {
removeCanvasController(controllerActual->canvasController());
}
}
void KoToolManager::switchToolRequested(const QString & id)
{
Q_ASSERT(d->canvasData);
if (!d->canvasData) return;
while (!d->canvasData->stack.isEmpty()) // switching means to flush the stack
d->canvasData->stack.pop();
d->switchTool(id, false);
}
void KoToolManager::switchInputDeviceRequested(const KoInputDevice &id)
{
if (!d->canvasData) return;
d->switchInputDevice(id);
}
void KoToolManager::switchToolTemporaryRequested(const QString &id)
{
d->switchTool(id, true);
}
void KoToolManager::switchBackRequested()
{
if (!d->canvasData) return;
if (d->canvasData->stack.isEmpty()) {
// default to changing to the interactionTool
d->switchTool(KoInteractionTool_ID, false);
return;
}
d->switchTool(d->canvasData->stack.pop(), false);
}
KoToolBase *KoToolManager::toolById(KoCanvasBase *canvas, const QString &id) const
{
Q_ASSERT(canvas);
Q_FOREACH (KoCanvasController *controller, d->canvasses.keys()) {
if (controller->canvas() == canvas)
return d->canvasData->allTools.value(id);
}
return 0;
}
KoCanvasController *KoToolManager::activeCanvasController() const
{
if (! d->canvasData) return 0;
return d->canvasData->canvas;
}
QString KoToolManager::preferredToolForSelection(const QList<KoShape*> &shapes)
{
QSet<QString> shapeTypes;
Q_FOREACH (KoShape *shape, shapes) {
shapeTypes << shape->shapeId();
}
//KritaUtils::makeContainerUnique(types);
QString toolType = KoInteractionTool_ID;
int prio = INT_MAX;
Q_FOREACH (ToolHelper *helper, d->tools) {
if (helper->priority() >= prio)
continue;
bool toolWillWork = false;
foreach (const QString &type, shapeTypes) {
if (helper->activationShapeId().split(',').contains(type)) {
toolWillWork = true;
break;
}
}
if (toolWillWork) {
toolType = helper->id();
prio = helper->priority();
}
}
return toolType;
}
QPair<QString, KoToolBase*> KoToolManager::createTools(KoCanvasController *controller, ToolHelper *tool)
{
// XXX: maybe this method should go into the private class?
QHash<QString, KoToolBase*> origHash;
if (d->canvasses.contains(controller)) {
origHash = d->canvasses.value(controller).first()->allTools;
}
if (origHash.contains(tool->id())) {
return QPair<QString, KoToolBase*>(tool->id(), origHash.value(tool->id()));
}
debugFlake << "Creating tool" << tool->id() << ". Activated on:" << tool->activationShapeId() << ", prio:" << tool->priority();
KoToolBase *tl = tool->createTool(controller->canvas());
if (tl) {
d->uniqueToolIds.insert(tl, tool->uniqueId());
tl->setObjectName(tool->id());
}
KoZoomTool *zoomTool = dynamic_cast<KoZoomTool*>(tl);
if (zoomTool) {
zoomTool->setCanvasController(controller);
}
return QPair<QString, KoToolBase*>(tool->id(), tl);
}
void KoToolManager::initializeCurrentToolForCanvas()
{
KIS_ASSERT_RECOVER_RETURN(d->canvasData);
// make a full reconnect cycle for the currently active tool
d->disconnectActiveTool();
d->connectActiveTool();
d->postSwitchTool(false);
}
KoToolManager* KoToolManager::instance()
{
return s_instance;
}
QString KoToolManager::activeToolId() const
{
if (!d->canvasData) return QString();
return d->canvasData->activeToolId;
}
KoToolManager::Private *KoToolManager::priv()
{
return d;
}
/**** KoToolManager::Private ****/
KoToolManager::Private::Private(KoToolManager *qq)
: q(qq),
canvasData(0),
layerExplicitlyDisabled(false)
{
}
KoToolManager::Private::~Private()
{
qDeleteAll(tools);
}
// helper method.
CanvasData *KoToolManager::Private::createCanvasData(KoCanvasController *controller, const KoInputDevice &device)
{
QHash<QString, KoToolBase*> toolsHash;
Q_FOREACH (ToolHelper *tool, tools) {
QPair<QString, KoToolBase*> toolPair = q->createTools(controller, tool);
if (toolPair.second) { // only if a real tool was created
toolsHash.insert(toolPair.first, toolPair.second);
}
}
CanvasData *cd = new CanvasData(controller, device);
cd->allTools = toolsHash;
return cd;
}
void KoToolManager::Private::setup()
{
if (tools.size() > 0)
return;
KoShapeRegistry::instance();
KoToolRegistry *registry = KoToolRegistry::instance();
Q_FOREACH (const QString & id, registry->keys()) {
ToolHelper *t = new ToolHelper(registry->value(id));
tools.append(t);
}
// connect to all tools so we can hear their button-clicks
Q_FOREACH (ToolHelper *tool, tools)
connect(tool, SIGNAL(toolActivated(ToolHelper*)), q, SLOT(toolActivated(ToolHelper*)));
// load pluggable input devices
KoInputDeviceHandlerRegistry::instance();
}
void KoToolManager::Private::connectActiveTool()
{
if (canvasData->activeTool) {
connect(canvasData->activeTool, SIGNAL(cursorChanged(QCursor)),
q, SLOT(updateCursor(QCursor)));
connect(canvasData->activeTool, SIGNAL(activateTool(QString)),
q, SLOT(switchToolRequested(QString)));
connect(canvasData->activeTool, SIGNAL(activateTemporary(QString)),
q, SLOT(switchToolTemporaryRequested(QString)));
connect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested()));
connect(canvasData->activeTool, SIGNAL(statusTextChanged(QString)),
q, SIGNAL(changedStatusText(QString)));
}
// we expect the tool to emit a cursor on activation.
updateCursor(Qt::ForbiddenCursor);
}
void KoToolManager::Private::disconnectActiveTool()
{
if (canvasData->activeTool) {
canvasData->deactivateToolActions();
// repaint the decorations before we deactivate the tool as it might deleted
// data needed for the repaint
emit q->aboutToChangeTool(canvasData->canvas);
canvasData->activeTool->deactivate();
disconnect(canvasData->activeTool, SIGNAL(cursorChanged(QCursor)),
q, SLOT(updateCursor(QCursor)));
disconnect(canvasData->activeTool, SIGNAL(activateTool(QString)),
q, SLOT(switchToolRequested(QString)));
disconnect(canvasData->activeTool, SIGNAL(activateTemporary(QString)),
q, SLOT(switchToolTemporaryRequested(QString)));
disconnect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested()));
disconnect(canvasData->activeTool, SIGNAL(statusTextChanged(QString)),
q, SIGNAL(changedStatusText(QString)));
}
// emit a empty status text to clear status text from last active tool
emit q->changedStatusText(QString());
}
void KoToolManager::Private::switchTool(KoToolBase *tool, bool temporary)
{
Q_ASSERT(tool);
if (canvasData == 0)
return;
if (canvasData->activeTool == tool && tool->toolId() != KoInteractionTool_ID)
return;
disconnectActiveTool();
canvasData->activeTool = tool;
connectActiveTool();
postSwitchTool(temporary);
}
void KoToolManager::Private::switchTool(const QString &id, bool temporary)
{
Q_ASSERT(canvasData);
if (!canvasData) return;
if (canvasData->activeTool && temporary)
canvasData->stack.push(canvasData->activeToolId);
canvasData->activeToolId = id;
KoToolBase *tool = canvasData->allTools.value(id);
if (! tool) {
return;
}
Q_FOREACH (ToolHelper *th, tools) {
if (th->id() == id) {
canvasData->activationShapeId = th->activationShapeId();
break;
}
}
switchTool(tool, temporary);
}
void KoToolManager::Private::postSwitchTool(bool temporary)
{
#ifndef NDEBUG
int canvasCount = 1;
Q_FOREACH (QList<CanvasData*> list, canvasses) {
bool first = true;
Q_FOREACH (CanvasData *data, list) {
if (first) {
debugFlake << "Canvas" << canvasCount++;
}
debugFlake << " +- Tool:" << data->activeToolId << (data == canvasData ? " *" : "");
first = false;
}
}
#endif
Q_ASSERT(canvasData);
if (!canvasData) return;
KoToolBase::ToolActivation toolActivation;
if (temporary)
toolActivation = KoToolBase::TemporaryActivation;
else
toolActivation = KoToolBase::DefaultActivation;
QSet<KoShape*> shapesToOperateOn;
if (canvasData->activeTool
&& canvasData->activeTool->canvas()
&& canvasData->activeTool->canvas()->shapeManager()) {
KoSelection *selection = canvasData->activeTool->canvas()->shapeManager()->selection();
Q_ASSERT(selection);
- shapesToOperateOn = QSet<KoShape*>::fromList(selection->selectedEditableShapesAndDelegates());
+ shapesToOperateOn = QSet<KoShape*>(selection->selectedEditableShapesAndDelegates().begin(), selection->selectedEditableShapesAndDelegates().end());
}
if (canvasData->canvas->canvas()) {
// Caller of postSwitchTool expect this to be called to update the selected tool
updateToolForProxy();
canvasData->activeTool->activate(toolActivation, shapesToOperateOn);
KoCanvasBase *canvas = canvasData->canvas->canvas();
canvas->updateInputMethodInfo();
} else {
canvasData->activeTool->activate(toolActivation, shapesToOperateOn);
}
QList<QPointer<QWidget> > optionWidgetList = canvasData->activeTool->optionWidgets();
if (optionWidgetList.empty()) { // no option widget.
QWidget *toolWidget;
QString title;
Q_FOREACH (ToolHelper *tool, tools) {
if (tool->id() == canvasData->activeTool->toolId()) {
title = tool->toolTip();
break;
}
}
toolWidget = canvasData->dummyToolWidget;
if (toolWidget == 0) {
toolWidget = new QWidget();
toolWidget->setObjectName("DummyToolWidget");
QVBoxLayout *layout = new QVBoxLayout(toolWidget);
layout->setMargin(3);
canvasData->dummyToolLabel = new QLabel(toolWidget);
layout->addWidget(canvasData->dummyToolLabel);
layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding));
toolWidget->setLayout(layout);
canvasData->dummyToolWidget = toolWidget;
}
canvasData->dummyToolLabel->setText(i18n("Active tool: %1", title));
optionWidgetList.append(toolWidget);
}
// Activate the actions for the currently active tool
canvasData->activateToolActions();
emit q->changedTool(canvasData->canvas, uniqueToolIds.value(canvasData->activeTool));
emit q->toolOptionWidgetsChanged(canvasData->canvas, optionWidgetList);
}
void KoToolManager::Private::switchCanvasData(CanvasData *cd)
{
Q_ASSERT(cd);
KoCanvasBase *oldCanvas = 0;
KoInputDevice oldInputDevice;
if (canvasData) {
oldCanvas = canvasData->canvas->canvas();
oldInputDevice = canvasData->inputDevice;
if (canvasData->activeTool) {
disconnectActiveTool();
}
KoToolProxy *proxy = proxies.value(oldCanvas);
Q_ASSERT(proxy);
proxy->setActiveTool(0);
}
canvasData = cd;
inputDevice = canvasData->inputDevice;
if (canvasData->activeTool) {
connectActiveTool();
postSwitchTool(false);
}
if (oldInputDevice != canvasData->inputDevice) {
emit q->inputDeviceChanged(canvasData->inputDevice);
}
if (oldCanvas != canvasData->canvas->canvas()) {
emit q->changedCanvas(canvasData->canvas->canvas());
}
}
void KoToolManager::Private::toolActivated(ToolHelper *tool)
{
Q_ASSERT(tool);
Q_ASSERT(canvasData);
if (!canvasData) return;
KoToolBase *t = canvasData->allTools.value(tool->id());
Q_ASSERT(t);
canvasData->activeToolId = tool->id();
canvasData->activationShapeId = tool->activationShapeId();
switchTool(t, false);
}
void KoToolManager::Private::detachCanvas(KoCanvasController *controller)
{
Q_ASSERT(controller);
// check if we are removing the active canvas controller
if (canvasData && canvasData->canvas == controller) {
KoCanvasController *newCanvas = 0;
// try to find another canvas controller beside the one we are removing
Q_FOREACH (KoCanvasController* canvas, canvasses.keys()) {
if (canvas != controller) {
// yay found one
newCanvas = canvas;
break;
}
}
if (newCanvas) {
switchCanvasData(canvasses.value(newCanvas).first());
} else {
disconnectActiveTool();
emit q->toolOptionWidgetsChanged(controller, QList<QPointer<QWidget> >());
// as a last resort just set a blank one
canvasData = 0;
}
}
KoToolProxy *proxy = proxies.value(controller->canvas());
if (proxy)
proxy->setActiveTool(0);
QList<KoToolBase *> tools;
Q_FOREACH (CanvasData *canvasData, canvasses.value(controller)) {
Q_FOREACH (KoToolBase *tool, canvasData->allTools) {
if (! tools.contains(tool)) {
tools.append(tool);
}
}
delete canvasData;
}
Q_FOREACH (KoToolBase *tool, tools) {
uniqueToolIds.remove(tool);
delete tool;
}
canvasses.remove(controller);
emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0);
}
void KoToolManager::Private::attachCanvas(KoCanvasController *controller)
{
Q_ASSERT(controller);
CanvasData *cd = createCanvasData(controller, KoInputDevice::mouse());
// switch to new canvas as the active one.
switchCanvasData(cd);
inputDevice = cd->inputDevice;
QList<CanvasData*> canvasses_;
canvasses_.append(cd);
canvasses[controller] = canvasses_;
KoToolProxy *tp = proxies[controller->canvas()];
if (tp)
tp->priv()->setCanvasController(controller);
if (cd->activeTool == 0) {
// no active tool, so we activate the highest priority main tool
int highestPriority = INT_MAX;
ToolHelper * helper = 0;
Q_FOREACH (ToolHelper * th, tools) {
if (th->section() == KoToolFactoryBase::mainToolType()) {
if (th->priority() < highestPriority) {
highestPriority = qMin(highestPriority, th->priority());
helper = th;
}
}
}
if (helper)
toolActivated(helper);
}
Connector *connector = new Connector(controller->canvas()->shapeManager());
connect(connector, SIGNAL(selectionChanged(QList<KoShape*>)), q,
SLOT(selectionChanged(QList<KoShape*>)));
connect(controller->canvas()->selectedShapesProxy(),
SIGNAL(currentLayerChanged(const KoShapeLayer*)),
q, SLOT(currentLayerChanged(const KoShapeLayer*)));
emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0);
}
void KoToolManager::Private::movedFocus(QWidget *from, QWidget *to)
{
Q_UNUSED(from);
// no canvas anyway or no focus set anyway?
if (!canvasData || to == 0) {
return;
}
// Check if this app is about QWidget-based KoCanvasControllerWidget canvasses
// XXX: Focus handling for non-qwidget based canvases!
KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast<KoCanvasControllerWidget*>(canvasData->canvas);
if (!canvasControllerWidget) {
return;
}
// canvasWidget is set as focusproxy for KoCanvasControllerWidget,
// so all focus checks are to be done against canvasWidget objects
// focus returned to current canvas?
if (to == canvasData->canvas->canvas()->canvasWidget()) {
// nothing to do
return;
}
// if the 'to' is one of our canvasWidgets, then switch.
// for code simplicity the current canvas will be checked again,
// but would have been caught already in the lines above, so no issue
KoCanvasController *newCanvas = 0;
Q_FOREACH (KoCanvasController* canvas, canvasses.keys()) {
if (canvas->canvas()->canvasWidget() == to) {
newCanvas = canvas;
break;
}
}
// none of our canvasWidgets got focus?
if (newCanvas == 0) {
return;
}
// switch to canvasdata matching inputdevice used last with this app instance
Q_FOREACH (CanvasData *data, canvasses.value(newCanvas)) {
if (data->inputDevice == inputDevice) {
switchCanvasData(data);
return;
}
}
// if no such inputDevice for this canvas, then simply fallback to first one
switchCanvasData(canvasses.value(newCanvas).first());
}
void KoToolManager::Private::updateCursor(const QCursor &cursor)
{
Q_ASSERT(canvasData);
Q_ASSERT(canvasData->canvas);
Q_ASSERT(canvasData->canvas->canvas());
canvasData->canvas->canvas()->setCursor(cursor);
}
void KoToolManager::Private::selectionChanged(const QList<KoShape*> &shapes)
{
QList<QString> types;
Q_FOREACH (KoShape *shape, shapes) {
QSet<KoShape*> delegates = shape->toolDelegates();
if (delegates.isEmpty()) { // no delegates, just the orig shape
delegates << shape;
}
foreach (KoShape *shape2, delegates) {
Q_ASSERT(shape2);
if (! types.contains(shape2->shapeId())) {
types.append(shape2->shapeId());
}
}
}
// check if there is still a shape selected the active tool can work on
// there needs to be at least one shape for a tool without an activationShapeId
// to work
// if not change the current tool to the default tool
const QStringList activationShapeIds = canvasData->activationShapeId.split(',');
if (!(canvasData->activationShapeId.isNull() && shapes.size() > 0)
&& !activationShapeIds.contains("flake/always")
&& !activationShapeIds.contains("flake/edit")) {
bool currentToolWorks = false;
foreach (const QString &type, types) {
if (activationShapeIds.contains(type)) {
currentToolWorks = true;
break;
}
}
if (!currentToolWorks) {
switchTool(KoInteractionTool_ID, false);
}
}
emit q->toolCodesSelected(types);
}
void KoToolManager::Private::currentLayerChanged(const KoShapeLayer *layer)
{
emit q->currentLayerChanged(canvasData->canvas, layer);
layerExplicitlyDisabled = layer && !layer->isShapeEditable();
updateToolForProxy();
debugFlake << "Layer changed to" << layer << "explicitly disabled:" << layerExplicitlyDisabled;
}
void KoToolManager::Private::updateToolForProxy()
{
KoToolProxy *proxy = proxies.value(canvasData->canvas->canvas());
if(!proxy) return;
bool canUseTool = !layerExplicitlyDisabled || canvasData->activationShapeId.endsWith(QLatin1String("/always"));
proxy->setActiveTool(canUseTool ? canvasData->activeTool : 0);
}
void KoToolManager::Private::switchInputDevice(const KoInputDevice &device)
{
Q_ASSERT(canvasData);
if (!canvasData) return;
if (inputDevice == device) return;
if (inputDevice.isMouse() && device.isMouse()) return;
if (device.isMouse() && !inputDevice.isMouse()) {
// we never switch back to mouse from a tablet input device, so the user can use the
// mouse to edit the settings for a tool activated by a tablet. See bugs
// https://bugs.kde.org/show_bug.cgi?id=283130 and https://bugs.kde.org/show_bug.cgi?id=285501.
// We do continue to switch between tablet devices, thought.
return;
}
QList<CanvasData*> items = canvasses[canvasData->canvas];
// search for a canvasdata object for the current input device
Q_FOREACH (CanvasData *cd, items) {
if (cd->inputDevice == device) {
switchCanvasData(cd);
if (!canvasData->activeTool) {
switchTool(KoInteractionTool_ID, false);
}
return;
}
}
// still here? That means we need to create a new CanvasData instance with the current InputDevice.
CanvasData *cd = createCanvasData(canvasData->canvas, device);
// switch to new canvas as the active one.
QString oldTool = canvasData->activeToolId;
items.append(cd);
canvasses[cd->canvas] = items;
switchCanvasData(cd);
q->switchToolRequested(oldTool);
}
void KoToolManager::Private::registerToolProxy(KoToolProxy *proxy, KoCanvasBase *canvas)
{
proxies.insert(canvas, proxy);
Q_FOREACH (KoCanvasController *controller, canvasses.keys()) {
if (controller->canvas() == canvas) {
proxy->priv()->setCanvasController(controller);
break;
}
}
}
//have to include this because of Q_PRIVATE_SLOT
#include "moc_KoToolManager.cpp"
diff --git a/libs/flake/KoToolProxy_p.h b/libs/flake/KoToolProxy_p.h
index 0bb0a204e6..e347bf20aa 100644
--- a/libs/flake/KoToolProxy_p.h
+++ b/libs/flake/KoToolProxy_p.h
@@ -1,70 +1,70 @@
/* 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 KOTOOLPROXYPRIVATE_P
#define KOTOOLPROXYPRIVATE_P
#include <QTimer>
-#include <QTime>
+#include <QElapsedTimer>
#include <QPointF>
class KoPointerEvent;
class KoToolBase;
class KoCanvasController;
class KoToolProxy;
class KoToolProxyPrivate
{
public:
explicit KoToolProxyPrivate(KoToolProxy *p);
void timeout(); // Auto scroll the canvas
void checkAutoScroll(const KoPointerEvent &event);
void selectionChanged(bool newSelection);
bool isActiveLayerEditable();
/// the toolManager tells us which KoCanvasController this toolProxy is working for.
void setCanvasController(KoCanvasController *controller);
KoToolBase *activeTool {0};
bool tabletPressed {false};
bool hasSelection {false};
QTimer scrollTimer;
QPoint widgetScrollPoint;
KoCanvasController *controller {0};
KoToolProxy *parent {0};
// used to determine if the mouse-release is after a drag or a simple click
QPoint mouseDownPoint;
// up until at least 4.3.0 we get a mouse move event when the tablet leaves the canvas.
bool mouseLeaveWorkaround {false};
bool isToolPressed {false};
// for multi clicking (double click or triple click) we need the following
int multiClickCount {0};
QPointF multiClickGlobalPoint;
- QTime multiClickTimeStamp;
+ QElapsedTimer multiClickTimeStamp;
};
#endif
diff --git a/libs/flake/resources/KoGamutMask.cpp b/libs/flake/resources/KoGamutMask.cpp
index b22342c558..328ffd3db3 100644
--- a/libs/flake/resources/KoGamutMask.cpp
+++ b/libs/flake/resources/KoGamutMask.cpp
@@ -1,439 +1,426 @@
/*
* Copyright (c) 2018 Anna Medonosova <anna.medonosova@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 "KoGamutMask.h"
#include <cstring>
#include <QVector>
#include <QString>
#include <QFile>
#include <QList>
#include <QDomDocument>
#include <QDomElement>
#include <QByteArray>
#include <QBuffer>
#include <QScopedPointer>
#include <FlakeDebug.h>
#include <KoStore.h>
#include <KoStoreDevice.h>
#include <KoDocumentResourceManager.h>
#include <SvgParser.h>
#include <SvgWriter.h>
#include <KoShape.h>
#include <kis_assert.h>
#include <QTransform>
//#include <kis_debug.h>
KoGamutMaskShape::KoGamutMaskShape(KoShape* shape)
: m_maskShape(shape)
, m_shapePaintingContext(KoShapePaintingContext())
{
}
KoGamutMaskShape::KoGamutMaskShape() {};
KoGamutMaskShape::~KoGamutMaskShape() {};
KoShape* KoGamutMaskShape::koShape()
{
return m_maskShape;
}
bool KoGamutMaskShape::coordIsClear(const QPointF& coord) const
{
bool isClear = m_maskShape->hitTest(coord);
return isClear;
}
void KoGamutMaskShape::paint(QPainter &painter)
{
painter.save();
painter.setTransform(m_maskShape->absoluteTransformation(), true);
m_maskShape->paint(painter, m_shapePaintingContext);
painter.restore();
}
void KoGamutMaskShape::paintStroke(QPainter &painter)
{
painter.save();
painter.setTransform(m_maskShape->absoluteTransformation(), true);
m_maskShape->paintStroke(painter, m_shapePaintingContext);
painter.restore();
}
struct KoGamutMask::Private {
QString name;
QString title;
- QString description;
QByteArray data;
QVector<KoGamutMaskShape*> maskShapes;
QVector<KoGamutMaskShape*> previewShapes;
QSizeF maskSize;
int rotation {0};
};
KoGamutMask::KoGamutMask(const QString& filename)
: KoResource(filename)
, d(new Private)
{
d->maskSize = QSizeF(144.0,144.0);
setRotation(0);
}
KoGamutMask::KoGamutMask()
: KoResource(QString())
, d(new Private)
{
d->maskSize = QSizeF(144.0,144.0);
setRotation(0);
}
KoGamutMask::KoGamutMask(KoGamutMask* rhs)
+ : KoGamutMask(*rhs)
+{
+}
+
+KoGamutMask::KoGamutMask(const KoGamutMask &rhs)
: QObject(0)
- , KoResource(QString())
+ , KoResource(rhs)
, d(new Private)
{
- setFilename(rhs->filename());
- setTitle(rhs->title());
- setDescription(rhs->description());
- d->maskSize = rhs->d->maskSize;
+ setTitle(rhs.title());
+ setDescription(rhs.description());
+ d->maskSize = rhs.d->maskSize;
QList<KoShape*> newShapes;
- for(KoShape* sh: rhs->koShapes()) {
+ for(KoShape* sh: rhs.koShapes()) {
newShapes.append(sh->cloneShape());
}
-
setMaskShapes(newShapes);
-
- setValid(true);
}
+KoResourceSP KoGamutMask::clone() const
+{
+ return KoResourceSP(new KoGamutMask(*this));
+}
KoGamutMask::~KoGamutMask()
{
delete d;
}
bool KoGamutMask::coordIsClear(const QPointF& coord, bool preview)
{
QVector<KoGamutMaskShape*>* shapeVector;
if (preview && !d->previewShapes.isEmpty()) {
shapeVector = &d->previewShapes;
} else {
shapeVector = &d->maskShapes;
}
for(KoGamutMaskShape* shape: *shapeVector) {
if (shape->coordIsClear(coord) == true) {
return true;
}
}
return false;
}
void KoGamutMask::paint(QPainter &painter, bool preview)
{
QVector<KoGamutMaskShape*>* shapeVector;
if (preview && !d->previewShapes.isEmpty()) {
shapeVector = &d->previewShapes;
} else {
shapeVector = &d->maskShapes;
}
for(KoGamutMaskShape* shape: *shapeVector) {
shape->paint(painter);
}
}
void KoGamutMask::paintStroke(QPainter &painter, bool preview)
{
QVector<KoGamutMaskShape*>* shapeVector;
if (preview && !d->previewShapes.isEmpty()) {
shapeVector = &d->previewShapes;
} else {
shapeVector = &d->maskShapes;
}
for(KoGamutMaskShape* shape: *shapeVector) {
shape->paintStroke(painter);
}
}
QTransform KoGamutMask::maskToViewTransform(quint8 viewSize)
{
// apply mask rotation before drawing
QPointF centerPoint(viewSize*0.5, viewSize*0.5);
QTransform transform;
transform.translate(centerPoint.x(), centerPoint.y());
transform.rotate(rotation());
transform.translate(-centerPoint.x(), -centerPoint.y());
qreal scale = viewSize/(maskSize().width());
transform.scale(scale, scale);
return transform;
}
QTransform KoGamutMask::viewToMaskTransform(quint8 viewSize)
{
QPointF centerPoint(viewSize*0.5, viewSize*0.5);
QTransform transform;
qreal scale = viewSize/(maskSize().width());
transform.scale(1/scale, 1/scale);
transform.translate(centerPoint.x(), centerPoint.y());
transform.rotate(-rotation());
transform.translate(-centerPoint.x(), -centerPoint.y());
return transform;
}
-bool KoGamutMask::load()
+bool KoGamutMask::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
{
- QFile file(filename());
- if (file.size() == 0) return false;
- if (!file.open(QIODevice::ReadOnly)) {
- warnFlake << "Can't open file " << filename();
- return false;
- }
- bool res = loadFromDevice(&file);
- file.close();
- return res;
-}
+ Q_UNUSED(resourcesInterface);
-bool KoGamutMask::loadFromDevice(QIODevice *dev)
-{
if (!dev->isOpen()) dev->open(QIODevice::ReadOnly);
d->data = dev->readAll();
// TODO: test
KIS_ASSERT_RECOVER_RETURN_VALUE(d->data.size() != 0, false);
if (filename().isNull()) {
warnFlake << "Cannot load gamut mask" << name() << "there is no filename set";
return false;
}
if (d->data.isNull()) {
QFile file(filename());
if (file.size() == 0) {
warnFlake << "Cannot load gamut mask" << name() << "there is no data available";
return false;
}
file.open(QIODevice::ReadOnly);
d->data = file.readAll();
file.close();
}
QBuffer buf(&d->data);
buf.open(QBuffer::ReadOnly);
QScopedPointer<KoStore> store(KoStore::createStore(&buf, KoStore::Read, "application/x-krita-gamutmask", KoStore::Zip));
if (!store || store->bad()) return false;
bool storeOpened = store->open("gamutmask.svg");
if (!storeOpened) { return false; }
QByteArray data;
data.resize(store->size());
QByteArray ba = store->read(store->size());
store->close();
QString errorMsg;
int errorLine = 0;
int errorColumn = 0;
KoXmlDocument xmlDocument = SvgParser::createDocumentFromSvg(ba, &errorMsg, &errorLine, &errorColumn);
if (xmlDocument.isNull()) {
errorFlake << "Parsing error in " << filename() << "! Aborting!" << endl
<< " In line: " << errorLine << ", column: " << errorColumn << endl
<< " Error message: " << errorMsg << endl;
errorFlake << "Parsing error in the main document at line" << errorLine
<< ", column" << errorColumn << endl
<< "Error message: " << errorMsg;
return false;
}
KoDocumentResourceManager manager;
SvgParser parser(&manager);
parser.setResolution(QRectF(0,0,100,100), 72); // initialize with default values
QSizeF fragmentSize;
QList<KoShape*> shapes = parser.parseSvg(xmlDocument.documentElement(), &fragmentSize);
d->maskSize = fragmentSize;
d->title = parser.documentTitle();
setName(d->title);
- d->description = parser.documentDescription();
+ setDescription(parser.documentDescription());
setMaskShapes(shapes);
if (store->open("preview.png")) {
KoStoreDevice previewDev(store.data());
previewDev.open(QIODevice::ReadOnly);
QImage preview = QImage();
preview.load(&previewDev, "PNG");
setImage(preview);
(void)store->close();
}
buf.close();
setValid(true);
return true;
}
void KoGamutMask::setMaskShapes(QList<KoShape*> shapes)
{
setMaskShapesToVector(shapes, d->maskShapes);
}
-bool KoGamutMask::save()
-{
- QFile file(filename());
- if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- return false;
- }
- saveToDevice(&file);
- file.close();
-
- return true;
-}
-
QList<KoShape*> KoGamutMask::koShapes() const
{
QList<KoShape*> shapes;
for(KoGamutMaskShape* maskShape: d->maskShapes) {
shapes.append(maskShape->koShape());
}
return shapes;
}
bool KoGamutMask::saveToDevice(QIODevice *dev) const
{
KoStore* store(KoStore::createStore(dev, KoStore::Write, "application/x-krita-gamutmask", KoStore::Zip));
if (!store || store->bad()) return false;
QList<KoShape*> shapes = koShapes();
std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
if (!store->open("gamutmask.svg")) {
return false;
}
KoStoreDevice storeDev(store);
storeDev.open(QIODevice::WriteOnly);
SvgWriter writer(shapes);
writer.setDocumentTitle(d->title);
- writer.setDocumentDescription(d->description);
+ writer.setDocumentDescription(description());
writer.save(storeDev, d->maskSize);
if (!store->close()) { return false; }
if (!store->open("preview.png")) {
return false;
}
KoStoreDevice previewDev(store);
previewDev.open(QIODevice::WriteOnly);
image().save(&previewDev, "PNG");
if (!store->close()) { return false; }
- return store->finalize();
+ return store->finalize() && KoResource::saveToDevice(dev);
}
-QString KoGamutMask::title()
+QString KoGamutMask::title() const
{
return d->title;
}
void KoGamutMask::setTitle(QString title)
{
d->title = title;
setName(title);
}
-QString KoGamutMask::description()
+QString KoGamutMask::description() const
{
- return d->description;
+ QMap<QString, QVariant> m = metadata();
+ return m["description"].toString();
}
void KoGamutMask::setDescription(QString description)
{
- d->description = description;
+ addMetaData("description", description);
+}
+
+QString KoGamutMask::defaultFileExtension() const
+{
+ return ".kgm";
}
int KoGamutMask::rotation()
{
return d->rotation;
}
void KoGamutMask::setRotation(int rotation)
{
d->rotation = rotation;
}
QSizeF KoGamutMask::maskSize()
{
return d->maskSize;
}
void KoGamutMask::setPreviewMaskShapes(QList<KoShape*> shapes)
{
setMaskShapesToVector(shapes, d->previewShapes);
}
void KoGamutMask::setMaskShapesToVector(QList<KoShape *> shapes, QVector<KoGamutMaskShape *> &targetVector)
{
targetVector.clear();
for(KoShape* sh: shapes) {
KoGamutMaskShape* maskShape = new KoGamutMaskShape(sh);
targetVector.append(maskShape);
}
}
// clean up when ending mask preview
void KoGamutMask::clearPreview()
{
d->previewShapes.clear();
}
diff --git a/libs/flake/resources/KoGamutMask.h b/libs/flake/resources/KoGamutMask.h
index d2372842e9..2b41e9bd5b 100644
--- a/libs/flake/resources/KoGamutMask.h
+++ b/libs/flake/resources/KoGamutMask.h
@@ -1,104 +1,114 @@
/*
* Copyright (c) 2018 Anna Medonosova <anna.medonosova@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 KOGAMUTMASK_H
#define KOGAMUTMASK_H
#include <QPainter>
#include <QString>
#include <QVector>
#include <cmath>
#include <FlakeDebug.h>
-#include <resources/KoResource.h>
+#include <KoResource.h>
#include <KoShape.h>
#include <KoShapePaintingContext.h>
//class KoViewConverter;
class QTransform;
class KoGamutMaskShape
{
public:
KoGamutMaskShape(KoShape* shape);
KoGamutMaskShape();
~KoGamutMaskShape();
bool coordIsClear(const QPointF& coord) const;
QPainterPath outline();
void paint(QPainter &painter);
void paintStroke(QPainter &painter);
KoShape* koShape();
private:
KoShape* m_maskShape;
KoShapePaintingContext m_shapePaintingContext;
};
/**
* @brief The resource type for gamut masks used by the artistic color selector
*/
class KRITAFLAKE_EXPORT KoGamutMask : public QObject, public KoResource
{
Q_OBJECT
public:
KoGamutMask(const QString &filename);
KoGamutMask();
KoGamutMask(KoGamutMask *rhs);
+ KoGamutMask(const KoGamutMask &rhs);
+ KoGamutMask &operator=(const KoGamutMask &rhs) = delete;
+ KoResourceSP clone() const override;
~KoGamutMask() override;
bool coordIsClear(const QPointF& coord, bool preview);
- bool load() override;
- bool loadFromDevice(QIODevice *dev) override;
- bool save() override;
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
bool saveToDevice(QIODevice* dev) const override;
+ QPair<QString, QString> resourceType() const override
+ {
+ return QPair<QString, QString>(ResourceType::GamutMasks, "");
+ }
+
void paint(QPainter &painter, bool preview);
void paintStroke(QPainter &painter, bool preview);
QTransform maskToViewTransform(quint8 viewSize);
QTransform viewToMaskTransform(quint8 viewSize);
- QString title();
+ QString title() const;
void setTitle(QString title);
- QString description();
+ QString description() const;
void setDescription(QString description);
+ QString defaultFileExtension() const override;
+
int rotation();
void setRotation(int rotation);
QSizeF maskSize();
void setMaskShapes(QList<KoShape*> shapes);
void setPreviewMaskShapes(QList<KoShape*> shapes);
QList<KoShape*> koShapes() const;
void clearPreview();
private:
void setMaskShapesToVector(QList<KoShape*> shapes, QVector<KoGamutMaskShape*>& targetVector);
struct Private;
Private* const d;
};
+typedef QSharedPointer<KoGamutMask> KoGamutMaskSP;
+
#endif // KOGAMUTMASK_H
diff --git a/libs/flake/resources/KoSvgSymbolCollectionResource.cpp b/libs/flake/resources/KoSvgSymbolCollectionResource.cpp
index f2acf5d46e..4953e1bd17 100644
--- a/libs/flake/resources/KoSvgSymbolCollectionResource.cpp
+++ b/libs/flake/resources/KoSvgSymbolCollectionResource.cpp
@@ -1,233 +1,219 @@
/* This file is part of the KDE project
Copyright (c) 2017 L. E. Segovia <amy@amyspark.me>
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/KoSvgSymbolCollectionResource.h>
#include <QDebug>
#include <QVector>
#include <QFile>
#include <QFileInfo>
#include <QBuffer>
#include <QByteArray>
#include <QImage>
#include <QPainter>
#include <klocalizedstring.h>
#include <KoStore.h>
#include <KoDocumentResourceManager.h>
#include "kis_debug.h"
#include <KoShape.h>
#include <KoShapeGroup.h>
#include <KoShapeManager.h>
#include <KoShapePaintingContext.h>
#include <SvgParser.h>
+#include <KoMD5Generator.h>
#include <FlakeDebug.h>
QImage KoSvgSymbol::icon()
{
KoShapeGroup *group = dynamic_cast<KoShapeGroup*>(shape);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(group, QImage());
QRectF rc = group->boundingRect().normalized();
QImage image(rc.width(), rc.height(), QImage::Format_ARGB32_Premultiplied);
QPainter gc(&image);
image.fill(Qt::gray);
KoShapePaintingContext ctx;
// debugFlake << "Going to render. Original bounding rect:" << group->boundingRect()
// << "Normalized: " << rc
// << "Scale W" << 256 / rc.width() << "Scale H" << 256 / rc.height();
gc.translate(-rc.x(), -rc.y());
KoShapeManager::renderSingleShape(group, gc, ctx);
gc.end();
image = image.scaled(128, 128, Qt::KeepAspectRatio);
return image;
}
struct KoSvgSymbolCollectionResource::Private {
QVector<KoSvgSymbol*> symbols;
QString title;
QString description;
};
KoSvgSymbolCollectionResource::KoSvgSymbolCollectionResource(const QString& filename)
: KoResource(filename)
, d(new Private())
{
}
KoSvgSymbolCollectionResource::KoSvgSymbolCollectionResource()
: KoResource(QString())
, d(new Private())
{
}
KoSvgSymbolCollectionResource::KoSvgSymbolCollectionResource(const KoSvgSymbolCollectionResource& rhs)
- : QObject(0)
- , KoResource(QString())
- , d(new Private())
+ : KoResource(QString())
+ , d(new Private(*rhs.d))
{
- setFilename(rhs.filename());
- d->symbols = rhs.d->symbols;
- setValid(true);
}
-KoSvgSymbolCollectionResource::~KoSvgSymbolCollectionResource()
+KoResourceSP KoSvgSymbolCollectionResource::clone() const
{
+ return KoResourceSP(new KoSvgSymbolCollectionResource(*this));
}
-bool KoSvgSymbolCollectionResource::load()
+KoSvgSymbolCollectionResource::~KoSvgSymbolCollectionResource()
{
- QFile file(filename());
- if (file.size() == 0) return false;
- if (!file.open(QIODevice::ReadOnly)) {
- return false;
- }
- bool res = loadFromDevice(&file);
- file.close();
- return res;
}
+bool KoSvgSymbolCollectionResource::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
+{
+ Q_UNUSED(resourcesInterface);
+
+ if (!dev->isOpen()) {
+ dev->open(QIODevice::ReadOnly);
+ }
+ QByteArray ba = dev->readAll();
+ setMD5(KoMD5Generator::generateHash(ba));
-bool KoSvgSymbolCollectionResource::loadFromDevice(QIODevice *dev)
-{
- if (!dev->isOpen()) dev->open(QIODevice::ReadOnly);
+ dev->seek(0);
QString errorMsg;
int errorLine = 0;
int errorColumn;
KoXmlDocument doc = SvgParser::createDocumentFromSvg(dev, &errorMsg, &errorLine, &errorColumn);
if (doc.isNull()) {
errKrita << "Parsing error in " << filename() << "! Aborting!" << endl
<< " In line: " << errorLine << ", column: " << errorColumn << endl
<< " Error message: " << errorMsg << endl;
errKrita << i18n("Parsing error in the main document at line %1, column %2\nError message: %3"
, errorLine , errorColumn , errorMsg);
return false;
}
KoDocumentResourceManager manager;
SvgParser parser(&manager);
parser.setResolution(QRectF(0,0,100,100), 72); // initialize with default values
QSizeF fragmentSize;
// We're not interested in the shapes themselves
qDeleteAll(parser.parseSvg(doc.documentElement(), &fragmentSize));
d->symbols = parser.takeSymbols();
// debugFlake << "Loaded" << filename() << "\n\t"
// << "Title" << parser.documentTitle() << "\n\t"
// << "Description" << parser.documentDescription()
-// << "\n\tgot" << d->symbols.size() << "symbols"
+// << "\n\tgot" << d->symbols.size() << ResourceType::Symbols
// << d->symbols[0]->shape->outlineRect()
// << d->symbols[0]->shape->size();
d->title = parser.documentTitle();
setName(d->title);
d->description = parser.documentDescription();
if (d->symbols.size() < 1) {
setValid(false);
return false;
}
setValid(true);
setImage(d->symbols[0]->icon());
return true;
}
-bool KoSvgSymbolCollectionResource::save()
-{
- QFile file(filename());
- if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- return false;
- }
- saveToDevice(&file);
- file.close();
- return true;
-}
-
bool KoSvgSymbolCollectionResource::saveToDevice(QIODevice *dev) const
{
bool res = false;
// XXX
if (res) {
KoResource::saveToDevice(dev);
}
return res;
}
QString KoSvgSymbolCollectionResource::defaultFileExtension() const
{
return QString(".svg");
}
QString KoSvgSymbolCollectionResource::title() const
{
return d->title;
}
QString KoSvgSymbolCollectionResource::description() const
{
return d->description;
}
QString KoSvgSymbolCollectionResource::creator() const
{
return "";
}
QString KoSvgSymbolCollectionResource::rights() const
{
return "";
}
QString KoSvgSymbolCollectionResource::language() const
{
return "";
}
QStringList KoSvgSymbolCollectionResource::subjects() const
{
return QStringList();
}
QString KoSvgSymbolCollectionResource::license() const
{
return "";
}
QStringList KoSvgSymbolCollectionResource::permits() const
{
return QStringList();
}
QVector<KoSvgSymbol *> KoSvgSymbolCollectionResource::symbols() const
{
return d->symbols;
}
diff --git a/libs/flake/resources/KoSvgSymbolCollectionResource.h b/libs/flake/resources/KoSvgSymbolCollectionResource.h
index fab2275371..45085ad025 100644
--- a/libs/flake/resources/KoSvgSymbolCollectionResource.h
+++ b/libs/flake/resources/KoSvgSymbolCollectionResource.h
@@ -1,104 +1,106 @@
/* This file is part of the KDE project
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; 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 KOSVGSYMBOLCOLLECTIONRESOURCE
#define KOSVGSYMBOLCOLLECTIONRESOURCE
#include <QObject>
#include <QColor>
#include <QVector>
#include <QScopedPointer>
#include <QImage>
#include <QPainter>
-#include <resources/KoResource.h>
+#include <KoResource.h>
#include <KoShape.h>
#include <KoShapeGroup.h>
#include <KoShapeManager.h>
#include <KoShapePaintingContext.h>
#include "kritaflake_export.h"
struct KRITAFLAKE_EXPORT KoSvgSymbol {
KoSvgSymbol() {}
KoSvgSymbol(const QString &_title)
: title(_title) {}
~KoSvgSymbol()
{
delete shape;
}
QString id;
QString title;
KoShape *shape;
QImage icon();
bool operator==(const KoSvgSymbol& rhs) const {
return title == rhs.title;
}
};
/**
* Loads an svg file that contains "symbol" objects and creates a collection of those objects.
*/
-class KRITAFLAKE_EXPORT KoSvgSymbolCollectionResource : public QObject, public KoResource
+class KRITAFLAKE_EXPORT KoSvgSymbolCollectionResource : public KoResource
{
- Q_OBJECT
public:
/**
*/
explicit KoSvgSymbolCollectionResource(const QString &filename);
/// Create an empty color set
KoSvgSymbolCollectionResource();
-
- /// Explicit copy constructor (KoResource copy constructor is private)
- KoSvgSymbolCollectionResource(const KoSvgSymbolCollectionResource& rhs);
-
~KoSvgSymbolCollectionResource() override;
- bool load() override;
- bool loadFromDevice(QIODevice *dev) override;
- bool save() override;
+ KoSvgSymbolCollectionResource(const KoSvgSymbolCollectionResource &rhs);
+ KoSvgSymbolCollectionResource &operator=(const KoSvgSymbolCollectionResource &rhs) = delete;
+ KoResourceSP clone() const override;
+
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
bool saveToDevice(QIODevice* dev) const override;
QString defaultFileExtension() const override;
+ QPair<QString, QString> resourceType() const override
+ {
+ return QPair<QString, QString>(ResourceType::Symbols, "");
+ }
+
QString title() const;
QString description() const;
QString creator() const;
QString rights() const;
QString language() const;
QStringList subjects() const;
QString license() const;
QStringList permits() const;
QVector<KoSvgSymbol *> symbols() const;
private:
struct Private;
const QScopedPointer<Private> d;
};
#endif // KoSvgSymbolCollectionResource
diff --git a/libs/flake/resources/tests/KoGamutMaskTest.cpp b/libs/flake/resources/tests/KoGamutMaskTest.cpp
index ff275ed606..8a9f831f54 100644
--- a/libs/flake/resources/tests/KoGamutMaskTest.cpp
+++ b/libs/flake/resources/tests/KoGamutMaskTest.cpp
@@ -1,133 +1,134 @@
/*
* Copyright (c) 2019 Anna Medonosova <anna.medonosova@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 <QTest>
#include <resources/KoGamutMask.h>
#include <testutil.h>
#include "KoGamutMaskTest.h"
+#include <KisGlobalResourcesInterface.h>
KoGamutMaskTest::KoGamutMaskTest(QObject *parent) : QObject(parent)
{
}
void KoGamutMaskTest::testCoordIsClear()
{
QFETCH(QString, maskFile);
QFETCH(QPointF, coord);
QFETCH(int, maskRotation);
QFETCH(bool, expectedOutput);
QScopedPointer<KoGamutMask> mask(new KoGamutMask(TestUtil::fetchDataFileLazy(maskFile)));
- mask->load();
+ mask->load(KisGlobalResourcesInterface::instance());
Q_ASSERT(mask->valid());
mask->setRotation(maskRotation);
// for this test we have a hardcoded view size of 100
QPointF translatedPoint = mask->viewToMaskTransform(100).map(coord);
bool maskOutput = mask->coordIsClear(translatedPoint, false);
QCOMPARE(maskOutput, expectedOutput);
}
void KoGamutMaskTest::testCoordIsClear_data()
{
QTest::addColumn<QString>("maskFile");
QTest::addColumn<QPointF>("coord");
QTest::addColumn<int>("maskRotation");
QTest::addColumn<bool>("expectedOutput");
// single shape mask
QTest::addRow("Atmospheric_Triad.kgm: disallowed coordinate, no rotation") << "Atmospheric_Triad.kgm"
<< QPointF(0.0, 0.0) << 0
<< false;
QTest::addRow("Atmospheric_Triad.kgm: allowed coordinate, no rotation") << "Atmospheric_Triad.kgm"
<< QPointF(33.0, 71.0) << 0
<< true;
QTest::addRow("Atmospheric_Triad.kgm: disallowed coordinate, with rotation") << "Atmospheric_Triad.kgm"
<< QPointF(33.0, 71.0) << 180
<< false;
QTest::addRow("Atmospheric_Triad.kgm: allowed coordinate, with rotation") << "Atmospheric_Triad.kgm"
<< QPointF(76.4,60.9) << 180
<< true;
// multiple shapes mask
QTest::addRow("Dominant_Hue_With_Accent.kgm: allowed coordinate, shape 1, no rotation")
<< "Dominant_Hue_With_Accent.kgm"
<< QPointF(71.0, 49.0) << 0
<< true;
QTest::addRow("Dominant_Hue_With_Accent.kgm: allowed coordinate, shape 2, no rotation")
<< "Dominant_Hue_With_Accent.kgm"
<< QPointF(11.0, 51.0) << 0
<< true;
QTest::addRow("Dominant_Hue_With_Accent.kgm: allowed coordinate, shape 1, with rotation")
<< "Dominant_Hue_With_Accent.kgm"
<< QPointF(40.0, 21.0) << 256
<< true;
QTest::addRow("Dominant_Hue_With_Accent.kgm: allowed coordinate, shape 2, with rotation")
<< "Dominant_Hue_With_Accent.kgm"
<< QPointF(57.0, 82.0) << 256
<< true;
}
void KoGamutMaskTest::testLoad()
{
QFETCH(QString, maskFile);
QFETCH(QString, expectedTitle);
QFETCH(QString, expectedDescription);
QFETCH(int, expectedShapeCount);
QScopedPointer<KoGamutMask> mask(new KoGamutMask(TestUtil::fetchDataFileLazy(maskFile)));
- mask->load();
+ mask->load(KisGlobalResourcesInterface::instance());
Q_ASSERT(mask->valid());
QCOMPARE(mask->title(), expectedTitle);
QCOMPARE(mask->description(), expectedDescription);
QCOMPARE(mask->koShapes().size(), expectedShapeCount);
}
void KoGamutMaskTest::testLoad_data()
{
QTest::addColumn<QString>("maskFile");
QTest::addColumn<QString>("expectedTitle");
QTest::addColumn<QString>("expectedDescription");
QTest::addColumn<int>("expectedShapeCount");
QTest::addRow("single shape mask")
<< "Atmospheric_Triad.kgm"
<< "Atmospheric Triad" << "test gamut mask description"
<< 1;
QTest::addRow("multiple shape mask")
<< "Dominant_Hue_With_Accent.kgm"
<< "Dominant Hue With Accent" << ""
<< 2;
}
QTEST_MAIN(KoGamutMaskTest);
diff --git a/libs/flake/text/KoSvgTextShapeMarkupConverter.cpp b/libs/flake/text/KoSvgTextShapeMarkupConverter.cpp
index 446112092d..08f3f45f76 100644
--- a/libs/flake/text/KoSvgTextShapeMarkupConverter.cpp
+++ b/libs/flake/text/KoSvgTextShapeMarkupConverter.cpp
@@ -1,1255 +1,1256 @@
/*
* 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 "KoSvgTextShapeMarkupConverter.h"
#include "klocalizedstring.h"
#include "kis_assert.h"
#include "kis_debug.h"
#include <QBuffer>
#include <QStringList>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QTextBlock>
#include <QTextLayout>
#include <QTextLine>
#include <QStack>
#include <KoSvgTextShape.h>
#include <KoXmlWriter.h>
#include <KoXmlReader.h>
#include <KoDocumentResourceManager.h>
#include <SvgParser.h>
#include <SvgWriter.h>
#include <SvgUtil.h>
#include <SvgSavingContext.h>
#include <SvgGraphicContext.h>
#include <html/HtmlSavingContext.h>
#include <html/HtmlWriter.h>
#include "kis_dom_utils.h"
#include <boost/optional.hpp>
#include <FlakeDebug.h>
struct KoSvgTextShapeMarkupConverter::Private {
Private(KoSvgTextShape *_shape) : shape(_shape) {}
KoSvgTextShape *shape;
QStringList errors;
QStringList warnings;
void clearErrors() {
errors.clear();
warnings.clear();
}
};
KoSvgTextShapeMarkupConverter::KoSvgTextShapeMarkupConverter(KoSvgTextShape *shape)
: d(new Private(shape))
{
}
KoSvgTextShapeMarkupConverter::~KoSvgTextShapeMarkupConverter()
{
}
bool KoSvgTextShapeMarkupConverter::convertToSvg(QString *svgText, QString *stylesText)
{
d->clearErrors();
QBuffer shapesBuffer;
QBuffer stylesBuffer;
shapesBuffer.open(QIODevice::WriteOnly);
stylesBuffer.open(QIODevice::WriteOnly);
{
SvgSavingContext savingContext(shapesBuffer, stylesBuffer);
savingContext.setStrippedTextMode(true);
SvgWriter writer({d->shape});
writer.saveDetached(savingContext);
}
shapesBuffer.close();
stylesBuffer.close();
*svgText = QString::fromUtf8(shapesBuffer.data());
*stylesText = QString::fromUtf8(stylesBuffer.data());
return true;
}
bool KoSvgTextShapeMarkupConverter::convertFromSvg(const QString &svgText, const QString &stylesText,
const QRectF &boundsInPixels, qreal pixelsPerInch)
{
debugFlake << "convertFromSvg. text:" << svgText << "styles:" << stylesText << "bounds:" << boundsInPixels << "ppi:" << pixelsPerInch;
d->clearErrors();
QString errorMessage;
int errorLine = 0;
int errorColumn = 0;
const QString fullText = QString("<svg>\n%1\n%2\n</svg>\n").arg(stylesText).arg(svgText);
KoXmlDocument doc = SvgParser::createDocumentFromSvg(fullText, &errorMessage, &errorLine, &errorColumn);
if (doc.isNull()) {
d->errors << QString("line %1, col %2: %3").arg(errorLine).arg(errorColumn).arg(errorMessage);
return false;
}
d->shape->resetTextShape();
KoDocumentResourceManager resourceManager;
SvgParser parser(&resourceManager);
parser.setResolution(boundsInPixels, pixelsPerInch);
KoXmlElement root = doc.documentElement();
KoXmlNode node = root.firstChild();
bool textNodeFound = false;
for (; !node.isNull(); node = node.nextSibling()) {
KoXmlElement el = node.toElement();
if (el.isNull()) continue;
if (el.tagName() == "defs") {
parser.parseDefsElement(el);
}
else if (el.tagName() == "text") {
if (textNodeFound) {
d->errors << i18n("More than one 'text' node found!");
return false;
}
KoShape *shape = parser.parseTextElement(el, d->shape);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape == d->shape, false);
textNodeFound = true;
break;
} else {
d->errors << i18n("Unknown node of type \'%1\' found!", el.tagName());
return false;
}
}
if (!textNodeFound) {
d->errors << i18n("No \'text\' node found!");
return false;
}
return true;
}
bool KoSvgTextShapeMarkupConverter::convertToHtml(QString *htmlText)
{
d->clearErrors();
QBuffer shapesBuffer;
shapesBuffer.open(QIODevice::WriteOnly);
{
HtmlWriter writer({d->shape});
if (!writer.save(shapesBuffer)) {
d->errors = writer.errors();
d->warnings = writer.warnings();
return false;
}
}
shapesBuffer.close();
*htmlText = QString(shapesBuffer.data());
debugFlake << "\t\t" << *htmlText;
return true;
}
bool KoSvgTextShapeMarkupConverter::convertFromHtml(const QString &htmlText, QString *svgText, QString *styles)
{
debugFlake << ">>>>>>>>>>>" << htmlText;
QBuffer svgBuffer;
svgBuffer.open(QIODevice::WriteOnly);
QXmlStreamReader htmlReader(htmlText);
QXmlStreamWriter svgWriter(&svgBuffer);
svgWriter.setAutoFormatting(true);
QStringRef elementName;
bool newLine = false;
int lineCount = 0;
QString bodyEm = "1em";
QString em;
QString p("p");
//previous style string is for keeping formatting proper on linebreaks and appendstyle is for specific tags
QString previousStyleString;
QString appendStyle;
while (!htmlReader.atEnd()) {
QXmlStreamReader::TokenType token = htmlReader.readNext();
switch (token) {
case QXmlStreamReader::StartElement:
{
newLine = false;
if (htmlReader.name() == "br") {
debugFlake << "\tdoing br";
svgWriter.writeEndElement();
elementName = QStringRef(&p);
em = bodyEm;
appendStyle = previousStyleString;
}
else {
elementName = htmlReader.name();
em = "";
}
if (elementName == "body") {
debugFlake << "\tstart Element" << elementName;
svgWriter.writeStartElement("text");
appendStyle = QString();
}
else if (elementName == "p") {
// new line
debugFlake << "\t\tstart Element" << elementName;
svgWriter.writeStartElement("tspan");
newLine = true;
if (em.isEmpty()) {
em = bodyEm;
appendStyle = QString();
}
lineCount++;
}
else if (elementName == "span") {
debugFlake << "\tstart Element" << elementName;
svgWriter.writeStartElement("tspan");
appendStyle = QString();
}
else if (elementName == "b" || elementName == "strong") {
debugFlake << "\tstart Element" << elementName;
svgWriter.writeStartElement("tspan");
appendStyle = "font-weight:700;";
}
else if (elementName == "i" || elementName == "em") {
debugFlake << "\tstart Element" << elementName;
svgWriter.writeStartElement("tspan");
appendStyle = "font-style:italic;";
}
else if (elementName == "u") {
debugFlake << "\tstart Element" << elementName;
svgWriter.writeStartElement("tspan");
appendStyle = "text-decoration:underline";
}
QXmlStreamAttributes attributes = htmlReader.attributes();
QString textAlign;
if (attributes.hasAttribute("align")) {
textAlign = attributes.value("align").toString();
}
if (attributes.hasAttribute("style")) {
QString filteredStyles;
QStringList svgStyles = QString("font-family font-size font-weight font-variant word-spacing text-decoration font-style font-size-adjust font-stretch direction letter-spacing").split(" ");
QStringList styles = attributes.value("style").toString().split(";");
for(int i=0; i<styles.size(); i++) {
QStringList style = QString(styles.at(i)).split(":");
debugFlake<<style.at(0);
if (svgStyles.contains(QString(style.at(0)).trimmed())) {
filteredStyles.append(styles.at(i)+";");
}
if (QString(style.at(0)).trimmed() == "color") {
filteredStyles.append(" fill:"+style.at(1)+";");
}
if (QString(style.at(0)).trimmed() == "text-align") {
textAlign = QString(style.at(1)).trimmed();
}
if (QString(style.at(0)).trimmed() == "line-height"){
if (style.at(1).contains("%")) {
double percentage = QString(style.at(1)).remove("%").toDouble();
em = QString::number(percentage/100.0)+"em";
} else if(style.at(1).contains("em")) {
em = style.at(1);
} else if(style.at(1).contains("px")) {
em = style.at(1);
}
if (elementName == "body") {
bodyEm = em;
}
}
}
if (textAlign == "center") {
filteredStyles.append(" text-anchor:middle;");
} else if (textAlign == "right") {
filteredStyles.append(" text-anchor:end;");
} else if (textAlign == "left"){
filteredStyles.append(" text-anchor:start;");
}
filteredStyles.append(appendStyle);
if (!filteredStyles.isEmpty()) {
svgWriter.writeAttribute("style", filteredStyles);
previousStyleString = filteredStyles;
}
}
if (newLine && lineCount > 1) {
debugFlake << "\t\tAdvancing to the next line";
svgWriter.writeAttribute("x", "0");
svgWriter.writeAttribute("dy", em);
}
break;
}
case QXmlStreamReader::EndElement:
{
if (htmlReader.name() == "br") break;
if (elementName == "p" || elementName == "span" || elementName == "body") {
debugFlake << "\tEndElement" << htmlReader.name() << "(" << elementName << ")";
svgWriter.writeEndElement();
}
break;
}
case QXmlStreamReader::Characters:
{
if (elementName == "style") {
*styles = htmlReader.text().toString();
}
else {
if (!htmlReader.isWhitespace()) {
debugFlake << "\tCharacters:" << htmlReader.text();
svgWriter.writeCharacters(htmlReader.text().toString());
}
}
break;
}
default:
;
}
}
if (htmlReader.hasError()) {
d->errors << htmlReader.errorString();
return false;
}
if (svgWriter.hasError()) {
d->errors << i18n("Unknown error writing SVG text element");
return false;
}
*svgText = QString::fromUtf8(svgBuffer.data());
return true;
}
void postCorrectBlockHeight(QTextDocument *doc,
qreal currLineAscent,
qreal prevLineAscent,
qreal prevLineDescent,
int prevBlockCursorPosition,
qreal currentBlockAbsoluteLineOffset)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(prevBlockCursorPosition >= 0);
QTextCursor postCorrectionCursor(doc);
postCorrectionCursor.setPosition(prevBlockCursorPosition);
if (!postCorrectionCursor.isNull()) {
const qreal relativeLineHeight =
((currentBlockAbsoluteLineOffset - currLineAscent + prevLineAscent) /
(prevLineAscent + prevLineDescent)) * 100.0;
QTextBlockFormat format = postCorrectionCursor.blockFormat();
format.setLineHeight(relativeLineHeight, QTextBlockFormat::ProportionalHeight);
postCorrectionCursor.setBlockFormat(format);
postCorrectionCursor = QTextCursor();
}
}
QTextFormat findMostCommonFormat(const QList<QTextFormat> &allFormats)
{
QTextCharFormat mostCommonFormat;
QSet<int> propertyIds;
/**
* Get all existing property ids
*/
Q_FOREACH (const QTextFormat &format, allFormats) {
const QMap<int, QVariant> formatProperties = format.properties();
Q_FOREACH (int id, formatProperties.keys()) {
propertyIds.insert(id);
}
}
/**
* Filter out properties that do not exist in some formats. Otherwise, the
* global format may override the default value used in these formats
* (and yes, we do not have access to the default values to use them
* in difference calculation algorithm
*/
Q_FOREACH (const QTextFormat &format, allFormats) {
for (auto it = propertyIds.begin(); it != propertyIds.end();) {
if (!format.hasProperty(*it)) {
it = propertyIds.erase(it);
} else {
++it;
}
}
if (propertyIds.isEmpty()) break;
}
if (!propertyIds.isEmpty()) {
QMap<int, QMap<QVariant, int>> propertyFrequency;
/**
* Calculate the frequency of values used in *all* the formats
*/
Q_FOREACH (const QTextFormat &format, allFormats) {
const QMap<int, QVariant> formatProperties = format.properties();
Q_FOREACH (int id, propertyIds) {
KIS_SAFE_ASSERT_RECOVER_BREAK(formatProperties.contains(id));
propertyFrequency[id][formatProperties.value(id)]++;
}
}
/**
* Add the most popular property value to the set of most common properties
*/
for (auto it = propertyFrequency.constBegin(); it != propertyFrequency.constEnd(); ++it) {
const int id = it.key();
const QMap<QVariant, int> allValues = it.value();
int maxCount = 0;
QVariant maxValue;
for (auto valIt = allValues.constBegin(); valIt != allValues.constEnd(); ++valIt) {
if (valIt.value() > maxCount) {
maxCount = valIt.value();
maxValue = valIt.key();
}
}
KIS_SAFE_ASSERT_RECOVER_BREAK(maxCount > 0);
mostCommonFormat.setProperty(id, maxValue);
}
}
return mostCommonFormat;
}
qreal calcLineWidth(const QTextBlock &block)
{
const QString blockText = block.text();
QTextLayout lineLayout;
lineLayout.setText(blockText);
lineLayout.setFont(block.charFormat().font());
lineLayout.setFormats(block.textFormats());
lineLayout.setTextOption(block.layout()->textOption());
lineLayout.beginLayout();
QTextLine fullLine = lineLayout.createLine();
if (!fullLine.isValid()) {
fullLine.setNumColumns(blockText.size());
}
lineLayout.endLayout();
return lineLayout.boundingRect().width();
}
bool KoSvgTextShapeMarkupConverter::convertDocumentToSvg(const QTextDocument *doc, QString *svgText)
{
QBuffer svgBuffer;
svgBuffer.open(QIODevice::WriteOnly);
QXmlStreamWriter svgWriter(&svgBuffer);
// disable auto-formatting to avoid axtra spaces appearing here and there
svgWriter.setAutoFormatting(false);
qreal maxParagraphWidth = 0.0;
QTextCharFormat mostCommonCharFormat;
QTextBlockFormat mostCommonBlockFormat;
struct LineInfo {
LineInfo() {}
LineInfo(QTextBlock _block, int _numSkippedLines)
: block(_block), numSkippedLines(_numSkippedLines)
{}
QTextBlock block;
int numSkippedLines = 0;
};
QVector<LineInfo> lineInfoList;
{
QTextBlock block = doc->begin();
QList<QTextFormat> allCharFormats;
QList<QTextFormat> allBlockFormats;
int numSequentialEmptyLines = 0;
while (block.isValid()) {
if (!block.text().trimmed().isEmpty()) {
lineInfoList.append(LineInfo(block, numSequentialEmptyLines));
numSequentialEmptyLines = 0;
maxParagraphWidth = qMax(maxParagraphWidth, calcLineWidth(block));
allBlockFormats.append(block.blockFormat());
Q_FOREACH (const QTextLayout::FormatRange &range, block.textFormats()) {
QTextFormat format = range.format;
allCharFormats.append(format);
}
} else {
numSequentialEmptyLines++;
}
block = block.next();
}
mostCommonCharFormat = findMostCommonFormat(allCharFormats).toCharFormat();
mostCommonBlockFormat = findMostCommonFormat(allBlockFormats).toBlockFormat();
}
//Okay, now the actual writing.
QTextBlock block = doc->begin();
Q_UNUSED(block);
svgWriter.writeStartElement("text");
{
const QString commonTextStyle = style(mostCommonCharFormat, mostCommonBlockFormat);
if (!commonTextStyle.isEmpty()) {
svgWriter.writeAttribute("style", commonTextStyle);
}
}
+ // TODO: check if we should change into to float
int prevBlockRelativeLineSpacing = mostCommonBlockFormat.lineHeight();
int prevBlockLineType = mostCommonBlockFormat.lineHeightType();
qreal prevBlockAscent = 0.0;
qreal prevBlockDescent= 0.0;
Q_FOREACH (const LineInfo &info, lineInfoList) {
QTextBlock block = info.block;
const QTextBlockFormat blockFormatDiff = formatDifference(block.blockFormat(), mostCommonBlockFormat).toBlockFormat();
QTextCharFormat blockCharFormatDiff = QTextCharFormat();
const QVector<QTextLayout::FormatRange> formats = block.textFormats();
if (formats.size()==1) {
blockCharFormatDiff = formatDifference(formats.at(0).format, mostCommonCharFormat).toCharFormat();
}
const QTextLayout *layout = block.layout();
const QTextLine line = layout->lineAt(0);
svgWriter.writeStartElement("tspan");
const QString text = block.text();
/**
* Mindblowing part: QTextEdit uses a hi-end algorithm for auto-estimation for the text
* directionality, so the user expects his text being saved to SVG with the same
* directionality. Just emulate behavior of direction="auto", which is not supported by
* SVG 1.1
*
* BUG: 392064
*/
bool isRightToLeft = false;
for (int i = 0; i < text.size(); i++) {
const QChar ch = text[i];
if (ch.direction() == QChar::DirR || ch.direction() == QChar::DirAL) {
isRightToLeft = true;
break;
} else if (ch.direction() == QChar::DirL) {
break;
}
}
if (isRightToLeft) {
svgWriter.writeAttribute("direction", "rtl");
svgWriter.writeAttribute("unicode-bidi", "embed");
}
{
const QString blockStyleString = style(blockCharFormatDiff, blockFormatDiff);
if (!blockStyleString.isEmpty()) {
svgWriter.writeAttribute("style", blockStyleString);
}
}
/**
* The alignment rule will be inverted while rendering the text in the text shape
* (according to the standard the alignment is defined not by "left" or "right",
* but by "start" and "end", which inverts for rtl text)
*/
Qt::Alignment blockAlignment = block.blockFormat().alignment();
if (isRightToLeft) {
if (blockAlignment & Qt::AlignLeft) {
blockAlignment &= ~Qt::AlignLeft;
blockAlignment |= Qt::AlignRight;
} else if (blockAlignment & Qt::AlignRight) {
blockAlignment &= ~Qt::AlignRight;
blockAlignment |= Qt::AlignLeft;
}
}
if (blockAlignment & Qt::AlignHCenter) {
svgWriter.writeAttribute("x", KisDomUtils::toString(0.5 * maxParagraphWidth) + "pt");
} else if (blockAlignment & Qt::AlignRight) {
svgWriter.writeAttribute("x", KisDomUtils::toString(maxParagraphWidth) + "pt");
} else {
svgWriter.writeAttribute("x", "0");
}
if (block.blockNumber() > 0) {
const qreal lineHeightPt =
line.ascent() - prevBlockAscent +
(prevBlockAscent + prevBlockDescent) * qreal(prevBlockRelativeLineSpacing) / 100.0;
const qreal currentLineSpacing = (info.numSkippedLines + 1) * lineHeightPt;
svgWriter.writeAttribute("dy", KisDomUtils::toString(currentLineSpacing) + "pt");
}
prevBlockRelativeLineSpacing =
blockFormatDiff.hasProperty(QTextFormat::LineHeight) ?
blockFormatDiff.lineHeight() :
mostCommonBlockFormat.lineHeight();
prevBlockLineType =
blockFormatDiff.hasProperty(QTextFormat::LineHeightType) ?
blockFormatDiff.lineHeightType() :
mostCommonBlockFormat.lineHeightType();
if (prevBlockLineType == QTextBlockFormat::SingleHeight) {
//single line will set lineHeight to 100%
prevBlockRelativeLineSpacing = 100;
}
prevBlockAscent = line.ascent();
prevBlockDescent = line.descent();
if (formats.size()>1) {
QStringList texts;
QVector<QTextCharFormat> charFormats;
for (int f=0; f<formats.size(); f++) {
QString chunk;
for (int c = 0; c<formats.at(f).length; c++) {
chunk.append(text.at(formats.at(f).start+c));
}
texts.append(chunk);
charFormats.append(formats.at(f).format);
}
for (int c = 0; c<texts.size(); c++) {
QTextCharFormat diff = formatDifference(charFormats.at(c), mostCommonCharFormat).toCharFormat();
const QString subStyle = style(diff, QTextBlockFormat(), mostCommonCharFormat);
if (!subStyle.isEmpty()) {
svgWriter.writeStartElement("tspan");
svgWriter.writeAttribute("style", subStyle);
svgWriter.writeCharacters(texts.at(c));
svgWriter.writeEndElement();
} else {
svgWriter.writeCharacters(texts.at(c));
}
}
} else {
svgWriter.writeCharacters(text);
//check format against
}
svgWriter.writeEndElement();
}
svgWriter.writeEndElement();//text root element.
if (svgWriter.hasError()) {
d->errors << i18n("Unknown error writing SVG text element");
return false;
}
*svgText = QString::fromUtf8(svgBuffer.data()).trimmed();
return true;
}
void parseTextAttributes(const QXmlStreamAttributes &elementAttributes,
QTextCharFormat &charFormat,
QTextBlockFormat &blockFormat)
{
QString styleString;
// we convert all the presentation attributes into styles
QString presentationAttributes;
for (int a = 0; a < elementAttributes.size(); a++) {
if (elementAttributes.at(a).name() != "style") {
presentationAttributes
.append(elementAttributes.at(a).name().toString())
.append(":")
.append(elementAttributes.at(a).value().toString())
.append(";");
}
}
if (presentationAttributes.endsWith(";")) {
presentationAttributes.chop(1);
}
if (elementAttributes.hasAttribute("style")) {
styleString = elementAttributes.value("style").toString();
if (styleString.endsWith(";")) {
styleString.chop(1);
}
}
if (!styleString.isEmpty() || !presentationAttributes.isEmpty()) {
//add attributes to parse them as part of the style.
styleString.append(";")
.append(presentationAttributes);
QStringList styles = styleString.split(";");
QVector<QTextFormat> formats = KoSvgTextShapeMarkupConverter::stylesFromString(styles, charFormat, blockFormat);
charFormat = formats.at(0).toCharFormat();
blockFormat = formats.at(1).toBlockFormat();
}
}
bool KoSvgTextShapeMarkupConverter::convertSvgToDocument(const QString &svgText, QTextDocument *doc)
{
QXmlStreamReader svgReader(svgText.trimmed());
doc->clear();
QTextCursor cursor(doc);
struct BlockFormatRecord {
BlockFormatRecord() {}
BlockFormatRecord(QTextBlockFormat _blockFormat,
QTextCharFormat _charFormat)
: blockFormat(_blockFormat),
charFormat(_charFormat)
{}
QTextBlockFormat blockFormat;
QTextCharFormat charFormat;
};
QStack<BlockFormatRecord> formatStack;
formatStack.push(BlockFormatRecord(QTextBlockFormat(), QTextCharFormat()));
qreal currBlockAbsoluteLineOffset = 0.0;
int prevBlockCursorPosition = -1;
qreal prevLineDescent = 0.0;
qreal prevLineAscent = 0.0;
boost::optional<qreal> previousBlockAbsoluteXOffset = boost::none;
while (!svgReader.atEnd()) {
QXmlStreamReader::TokenType token = svgReader.readNext();
switch (token) {
case QXmlStreamReader::StartElement:
{
bool newBlock = false;
QTextBlockFormat newBlockFormat;
QTextCharFormat newCharFormat;
qreal absoluteLineOffset = 1.0;
// fetch format of the parent block and make it default
if (formatStack.size() >= 2) {
newBlockFormat = formatStack[formatStack.size() - 2].blockFormat;
newCharFormat = formatStack[formatStack.size() - 2].charFormat;
}
{
const QXmlStreamAttributes elementAttributes = svgReader.attributes();
parseTextAttributes(elementAttributes, newCharFormat, newBlockFormat);
// mnemonic for a newline is (dy != 0 && x == 0)
boost::optional<qreal> blockAbsoluteXOffset = boost::none;
if (elementAttributes.hasAttribute("x")) {
QString xString = elementAttributes.value("x").toString();
if (xString.contains("pt")) {
xString = xString.remove("pt").trimmed();
}
blockAbsoluteXOffset = KisDomUtils::toDouble(xString);
}
if (previousBlockAbsoluteXOffset &&
blockAbsoluteXOffset &&
qFuzzyCompare(*previousBlockAbsoluteXOffset, *blockAbsoluteXOffset) &&
svgReader.name() != "text" &&
elementAttributes.hasAttribute("dy")) {
QString dyString = elementAttributes.value("dy").toString();
if (dyString.contains("pt")) {
dyString = dyString.remove("pt").trimmed();
}
KIS_SAFE_ASSERT_RECOVER_NOOP(formatStack.isEmpty() == (svgReader.name() == "text"));
absoluteLineOffset = KisDomUtils::toDouble(dyString);
newBlock = absoluteLineOffset > 0;
}
if (elementAttributes.hasAttribute("x")) {
previousBlockAbsoluteXOffset = blockAbsoluteXOffset;
}
}
//hack
doc->setTextWidth(100);
doc->setTextWidth(-1);
if (newBlock && absoluteLineOffset > 0) {
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!formatStack.isEmpty(), false);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cursor.block().layout()->lineCount() > 0, false);
QTextLine line = cursor.block().layout()->lineAt(0);
if (prevBlockCursorPosition >= 0) {
postCorrectBlockHeight(doc, line.ascent(), prevLineAscent, prevLineDescent,
prevBlockCursorPosition, currBlockAbsoluteLineOffset);
}
prevBlockCursorPosition = cursor.position();
prevLineAscent = line.ascent();
prevLineDescent = line.descent();
currBlockAbsoluteLineOffset = absoluteLineOffset;
cursor.insertBlock();
cursor.setCharFormat(formatStack.top().charFormat);
cursor.setBlockFormat(formatStack.top().blockFormat);
}
cursor.mergeCharFormat(newCharFormat);
cursor.mergeBlockFormat(newBlockFormat);
formatStack.push(BlockFormatRecord(cursor.blockFormat(), cursor.charFormat()));
break;
}
case QXmlStreamReader::EndElement:
{
if (svgReader.name() != "text") {
formatStack.pop();
KIS_SAFE_ASSERT_RECOVER(!formatStack.isEmpty()) { break; }
cursor.setCharFormat(formatStack.top().charFormat);
cursor.setBlockFormat(formatStack.top().blockFormat);
}
break;
}
case QXmlStreamReader::Characters:
{
if (!svgReader.isWhitespace()) {
cursor.insertText(svgReader.text().toString());
}
break;
}
default:
break;
}
}
if (prevBlockCursorPosition >= 0) {
QTextLine line = cursor.block().layout()->lineAt(0);
postCorrectBlockHeight(doc, line.ascent(), prevLineAscent, prevLineDescent,
prevBlockCursorPosition, currBlockAbsoluteLineOffset);
}
if (svgReader.hasError()) {
d->errors << svgReader.errorString();
return false;
}
doc->setModified(false);
return true;
}
QStringList KoSvgTextShapeMarkupConverter::errors() const
{
return d->errors;
}
QStringList KoSvgTextShapeMarkupConverter::warnings() const
{
return d->warnings;
}
QString KoSvgTextShapeMarkupConverter::style(QTextCharFormat format,
QTextBlockFormat blockFormat,
QTextCharFormat mostCommon)
{
QStringList style;
for(int i=0; i<format.properties().size(); i++) {
QString c;
int propertyId = format.properties().keys().at(i);
if (propertyId == QTextCharFormat::FontFamily) {
c.append("font-family").append(":")
.append(format.properties()[propertyId].toString());
}
if (propertyId == QTextCharFormat::FontPointSize) {
c.append("font-size").append(":")
.append(format.properties()[propertyId].toString()+"pt");
style.append(c);
c.clear();
QFontMetricsF metrics(format.fontFamily());
qreal xRatio = metrics.xHeight()/metrics.height();
c.append("font-size-adjust").append(":").append(QString::number(xRatio));
}
if (propertyId == QTextCharFormat::FontPixelSize) {
c.append("font-size").append(":")
.append(format.properties()[propertyId].toString()+"px");
}
if (propertyId == QTextCharFormat::FontWeight) {
//8 comes from QTextDocument...
c.append("font-weight").append(":")
.append(QString::number(format.properties()[propertyId].toInt()*8));
}
if (propertyId == QTextCharFormat::FontItalic) {
QString val = "italic";
if (!format.fontItalic()) {
val = "normal";
}
c.append("font-style").append(":")
.append(val);
}
if (propertyId == QTextCharFormat::FontCapitalization) {
QString val;
if (format.fontCapitalization() == QFont::SmallCaps){
c.append("font-variant").append(":")
.append("small-caps");
} else if (format.fontCapitalization() == QFont::AllUppercase) {
c.append("text-transform").append(":")
.append("uppercase");
} else if (format.fontCapitalization() == QFont::AllLowercase) {
c.append("text-transform").append(":")
.append("lowercase");
} else if (format.fontCapitalization() == QFont::Capitalize) {
c.append("text-transform").append(":")
.append("capitalize");
}
}
if (propertyId == QTextCharFormat::FontStretch) {
c.append("font-stretch").append(":")
.append(format.properties()[propertyId].toString());
}
if (propertyId == QTextCharFormat::FontKerning) {
QString val = "normal";
if(!format.fontKerning()) {
val = "0";
}
c.append("kerning").append(":")
.append(val);
}
if (propertyId == QTextCharFormat::FontWordSpacing) {
c.append("word-spacing").append(":")
.append(QString::number(format.fontWordSpacing()));
}
if (propertyId == QTextCharFormat::FontLetterSpacing) {
QString val;
if (format.fontLetterSpacingType()==QFont::AbsoluteSpacing) {
val = QString::number(format.fontLetterSpacing());
} else {
val = QString::number(((format.fontLetterSpacing()/100)*format.fontPointSize()));
}
c.append("letter-spacing").append(":")
.append(val);
}
if (propertyId == QTextCharFormat::TextOutline) {
if (format.textOutline().color() != mostCommon.textOutline().color()) {
c.append("stroke").append(":")
.append(format.textOutline().color().name());
style.append(c);
c.clear();
}
if (format.textOutline().width() != mostCommon.textOutline().width()) {
c.append("stroke-width").append(":")
.append(QString::number(format.textOutline().width()));
}
}
if (propertyId == QTextCharFormat::TextVerticalAlignment) {
QString val = "baseline";
if (format.verticalAlignment() == QTextCharFormat::AlignSubScript) {
val = QLatin1String("sub");
}
else if (format.verticalAlignment() == QTextCharFormat::AlignSuperScript) {
val = QLatin1String("super");
}
c.append("baseline-shift").append(":").append(val);
}
//we might need a better check than 'isn't black'
if (propertyId == QTextCharFormat::ForegroundBrush) {
QString c;
c.append("fill").append(":")
.append(format.foreground().color().name());
if (!c.isEmpty()) {
style.append(c);
}
}
if (!c.isEmpty()) {
style.append(c);
}
}
if (format.underlineStyle() != QTextCharFormat::SpellCheckUnderline) {
if(format.underlineStyle() != mostCommon.underlineStyle()){
QStringList values;
QString c;
if (format.fontUnderline()) {
values.append("underline");
}
if (format.fontOverline()) {
values.append("overline");
}
if (format.fontStrikeOut()) {
values.append("line-through");
}
if (values.isEmpty()) {
values.append("none");
}
c.append("text-decoration").append(":")
.append(values.join(" "));
if (!values.isEmpty()) {
style.append(c);
}
}
}
if (blockFormat.hasProperty(QTextBlockFormat::BlockAlignment)) {
// TODO: Alignment works incorrectly! The offsets should be calculated
// according to the shape width/height!
QString c;
QString val;
if (blockFormat.alignment()==Qt::AlignRight) {
val = "end";
} else if (blockFormat.alignment()==Qt::AlignCenter) {
val = "middle";
} else {
val = "start";
}
c.append("text-anchor").append(":")
.append(val);
if (!c.isEmpty()) {
style.append(c);
}
}
return style.join("; ");
}
QVector<QTextFormat> KoSvgTextShapeMarkupConverter::stylesFromString(QStringList styles, QTextCharFormat currentCharFormat, QTextBlockFormat currentBlockFormat)
{
Q_UNUSED(currentBlockFormat);
QVector<QTextFormat> formats;
QTextCharFormat charFormat;
charFormat.setTextOutline(currentCharFormat.textOutline());
QTextBlockFormat blockFormat;
QScopedPointer<SvgGraphicsContext> context(new SvgGraphicsContext());
for (int i=0; i<styles.size(); i++) {
if (!styles.at(i).isEmpty()){
QStringList style = styles.at(i).split(":");
QString property = style.at(0).trimmed();
QString value = style.at(1).trimmed();
if (property == "font-family") {
charFormat.setFontFamily(value);
}
if (property == "font-size") {
qreal val = SvgUtil::parseUnitX(context.data(), value);
charFormat.setFontPointSize(val);
}
if (property == "font-variant") {
if (value=="small-caps") {
charFormat.setFontCapitalization(QFont::SmallCaps);
} else {
charFormat.setFontCapitalization(QFont::MixedCase);
}
}
if (property == "font-style") {
if (value=="italic" || value=="oblique") {
charFormat.setFontItalic(true);
} else {
charFormat.setFontItalic(false);
}
}
if (property == "font-stretch") {
charFormat.setFontStretch(value.toInt());
}
if (property == "font-weight") {
charFormat.setFontWeight(value.toInt()/8);
}
if (property == "text-decoration") {
charFormat.setFontUnderline(false);
charFormat.setFontOverline(false);
charFormat.setFontStrikeOut(false);
QStringList values = value.split(" ");
if (values.contains("line-through")) {
charFormat.setFontStrikeOut(true);
}
if (values.contains("overline")) {
charFormat.setFontOverline(true);
}
if(values.contains("underline")){
charFormat.setFontUnderline(true);
}
}
if (property == "text-transform") {
if (value == "uppercase") {
charFormat.setFontCapitalization(QFont::AllUppercase);
} else if (value == "lowercase") {
charFormat.setFontCapitalization(QFont::AllLowercase);
} else if (value == "capitalize") {
charFormat.setFontCapitalization(QFont::Capitalize);
} else{
charFormat.setFontCapitalization(QFont::MixedCase);
}
}
if (property == "letter-spacing") {
qreal val = SvgUtil::parseUnitX(context.data(), value);
charFormat.setFontLetterSpacingType(QFont::AbsoluteSpacing);
charFormat.setFontLetterSpacing(val);
}
if (property == "word-spacing") {
qreal val = SvgUtil::parseUnitX(context.data(), value);
charFormat.setFontWordSpacing(val);
}
if (property == "kerning") {
if (value=="normal") {
charFormat.setFontKerning(true);
} else {
qreal val = SvgUtil::parseUnitX(context.data(), value);
charFormat.setFontKerning(false);
charFormat.setFontLetterSpacingType(QFont::AbsoluteSpacing);
charFormat.setFontLetterSpacing(charFormat.fontLetterSpacing() + val);
}
}
if (property == "stroke") {
QPen pen = charFormat.textOutline();
QColor color;
color.setNamedColor(value);
pen.setColor(color);
charFormat.setTextOutline(pen);
}
if (property == "stroke-width") {
QPen pen = charFormat.textOutline();
pen.setWidth(value.toInt());
charFormat.setTextOutline(pen);
}
if (property == "fill") {
QColor color;
color.setNamedColor(value);
charFormat.setForeground(color);
}
if (property == "text-anchor") {
if (value == "end") {
blockFormat.setAlignment(Qt::AlignRight);
} else if (value == "middle") {
blockFormat.setAlignment(Qt::AlignCenter);
} else {
blockFormat.setAlignment(Qt::AlignLeft);
}
}
if (property == "baseline-shift") {
if (value == "super") {
charFormat.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
} else if (value == "sub") {
charFormat.setVerticalAlignment(QTextCharFormat::AlignSubScript);
} else {
charFormat.setVerticalAlignment(QTextCharFormat::AlignNormal);
}
}
}
}
formats.append(charFormat);
formats.append(blockFormat);
return formats;
}
QTextFormat KoSvgTextShapeMarkupConverter::formatDifference(QTextFormat test, QTextFormat reference)
{
//copied from QTextDocument.cpp
QTextFormat diff = test;
//props should proly compare itself to the main text format...
const QMap<int, QVariant> props = reference.properties();
for (QMap<int, QVariant>::ConstIterator it = props.begin(), end = props.end();
it != end; ++it)
if (it.value() == test.property(it.key())) {
// Some props must not be removed as default state gets in the way.
if (it.key() == 0x2023) { // TextUnderlineStyle
continue;
} else if (it.key() == 0x2033) { // FontLetterSpacingType
continue;
}
diff.clearProperty(it.key());
}
return diff;
}
diff --git a/libs/flake/tools/KoPathTool.cpp b/libs/flake/tools/KoPathTool.cpp
index 405597b0e9..d6390b5dac 100644
--- a/libs/flake/tools/KoPathTool.cpp
+++ b/libs/flake/tools/KoPathTool.cpp
@@ -1,1308 +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)
{
m_points = new QActionGroup(this);
// m_pointTypeGroup->setExclusive(true);
m_actionPathPointCorner = action("pathpoint-corner");
if (m_actionPathPointCorner) {
m_actionPathPointCorner->setData(KoPathPointTypeCommand::Corner);
m_points->addAction(m_actionPathPointCorner);
}
m_actionPathPointSmooth = action("pathpoint-smooth");
if (m_actionPathPointSmooth) {
m_actionPathPointSmooth->setData(KoPathPointTypeCommand::Smooth);
m_points->addAction(m_actionPathPointSmooth);
}
m_actionPathPointSymmetric = action("pathpoint-symmetric");
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()));
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::createHandlePainterHelperView(&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::createHandlePainterHelperView(&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();
painter.setTransform(converter.documentToView(), true);
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)));
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());
+ initializeWithShapes(QList<KoShape*>(shapes.begin(), shapes.end()));
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();
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()));
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;
}
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);
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;
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())) {
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().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());
+ d->canvas->snapGuide()->setIgnoredPathPoints(QList<KoPathPoint*>(m_pointSelection.selectedPoints().begin(), m_pointSelection.selectedPoints().end()));
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/KisCppQuirks.h b/libs/global/KisCppQuirks.h
index 4283839cd2..0bde25d96b 100644
--- a/libs/global/KisCppQuirks.h
+++ b/libs/global/KisCppQuirks.h
@@ -1,45 +1,61 @@
/*
* 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 KISCPPQUIRKS_H
#define KISCPPQUIRKS_H
+#include <type_traits>
+
namespace std {
+// from C++14
+
#if __cplusplus < 201402L
template <typename Cont>
inline auto rbegin(Cont &cont) -> decltype(declval<Cont>().rbegin()) {
return cont.rbegin();
}
template <typename Cont>
inline auto rend(Cont &cont) -> decltype(declval<Cont>().rend()) {
return cont.rend();
}
template <class BidirectionalIterator>
inline reverse_iterator<BidirectionalIterator> make_reverse_iterator(BidirectionalIterator x)
{
return reverse_iterator<BidirectionalIterator>(x);
}
+template< bool B, class T = void >
+using enable_if_t = typename enable_if<B,T>::type;
+
+#endif
+
+// from C++17
+
+#if __cplusplus < 201703L
+
+template<typename...>
+using void_t = void;
+
#endif
}
#endif // KISCPPQUIRKS_H
diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt
index 11cae2d1db..7d9c7381c7 100644
--- a/libs/image/CMakeLists.txt
+++ b/libs/image/CMakeLists.txt
@@ -1,385 +1,389 @@
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_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/KisNodeRenameCommand.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
commands_new/KisHoldUIUpdatesCommand.cpp
commands_new/KisChangeChannelFlagsCommand.cpp
commands_new/KisChangeChannelLockFlagsCommand.cpp
processing/kis_do_nothing_processing_visitor.cpp
processing/kis_simple_processing_visitor.cpp
processing/kis_convert_color_space_processing_visitor.cpp
processing/kis_assign_profile_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
KisCroppedOriginalLayerInterface.cpp
KisDecoratedNodeInterface.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
KisBusyWaitBroker.cpp
KisSafeBlockingQueueConnectionProxy.cpp
kis_node_uuid_info.cpp
kis_clone_layer.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
KisSafeNodeProjectionStore.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
KisRecycleProjectionsJob.cpp
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
layerstyles/KisLayerStyleKnockoutBlower.cpp
KisProofingConfiguration.cpp
kis_node_query_path.cc
+
+
+ kis_asl_layer_style_serializer.cpp
+ KisAslStorage.cpp
+ kis_psd_layer_style.cpp
)
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/KisAslStorage.cpp b/libs/image/KisAslStorage.cpp
new file mode 100644
index 0000000000..af1b9ee5c4
--- /dev/null
+++ b/libs/image/KisAslStorage.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2018 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 "KisAslStorage.h"
+#include <KisResourceStorage.h>
+#include <KisResourceLoaderRegistry.h>
+#include <KisResourceLoader.h>
+#include <kis_psd_layer_style.h>
+
+#include <QFileInfo>
+
+struct KisAslStorageStaticRegistrar {
+ KisAslStorageStaticRegistrar() {
+ KisStoragePluginRegistry::instance()->addStoragePluginFactory(KisResourceStorage::StorageType::AdobeStyleLibrary, new KisStoragePluginFactory<KisAslStorage>());
+ }
+};
+static KisAslStorageStaticRegistrar s_registrar;
+
+
+class AslTagIterator : public KisResourceStorage::TagIterator
+{
+public:
+
+ AslTagIterator(const QString &location, const QString &resourceType)
+ : m_location(location)
+ , m_resourceType(resourceType)
+ {}
+
+ bool hasNext() const override {return false; }
+ void next() override {}
+
+ QString url() const override { return QString(); }
+ QString name() const override { return QString(); }
+ QString comment() const override {return QString(); }
+ KisTagSP tag() const override { return 0; }
+
+private:
+
+ QString m_location;
+ QString m_resourceType;
+
+};
+
+class AslIterator : public KisResourceStorage::ResourceIterator
+{
+
+private:
+
+ QString m_filename;
+ QSharedPointer<KisAslLayerStyleSerializer> m_aslSerializer;
+ bool m_isLoaded;
+ QHash<QString, KoPatternSP> m_patterns;
+ QVector<KisPSDLayerStyleSP> m_styles;
+ QScopedPointer<QHashIterator<QString, KoPatternSP>> m_patternsIterator;
+ QScopedPointer<QVectorIterator<KisPSDLayerStyleSP>> m_stylesIterator;
+ QString m_currentType;
+ KoResourceSP m_currentResource;
+ QString m_currentUuid;
+
+public:
+
+ AslIterator(QSharedPointer<KisAslLayerStyleSerializer> aslSerializer, const QString& filename)
+ : m_filename(filename)
+ , m_aslSerializer(aslSerializer)
+ , m_isLoaded(false)
+ {
+ }
+
+ bool hasNext() const override
+ {
+ if (!m_isLoaded) {
+ if (!m_aslSerializer->isInitialized()) {
+ m_aslSerializer->readFromFile(m_filename);
+ }
+
+ const_cast<AslIterator*>(this)->m_isLoaded = true;
+ const_cast<AslIterator*>(this)->m_patterns = m_aslSerializer->patterns();
+ const_cast<AslIterator*>(this)->m_styles = m_aslSerializer->styles();
+
+ const_cast<AslIterator*>(this)->m_patternsIterator.reset(new QHashIterator<QString, KoPatternSP>(m_patterns));
+ const_cast<AslIterator*>(this)->m_stylesIterator.reset(new QVectorIterator<KisPSDLayerStyleSP>(m_styles));
+
+ QHash<QString, KisPSDLayerStyleSP> layerStyles = const_cast<AslIterator*>(this)->m_aslSerializer->stylesHash();
+ }
+ if (!m_aslSerializer->isValid()) {
+ return false;
+ }
+ return m_patternsIterator->hasNext() ? true : m_stylesIterator->hasNext();
+ }
+ void next() override
+ {
+ if (m_patternsIterator->hasNext()) {
+ m_currentType = ResourceType::Patterns;
+ m_patternsIterator->next();
+ KoPatternSP currentPattern = m_patternsIterator->value();
+ m_currentResource = currentPattern;
+ KIS_ASSERT(currentPattern);
+ m_currentUuid = currentPattern->filename();
+ }
+ else if (m_stylesIterator->hasNext()) {
+ m_currentType = ResourceType::LayerStyles;
+ KisPSDLayerStyleSP currentLayerStyle = m_stylesIterator->next();
+ m_currentResource = currentLayerStyle;
+ KIS_ASSERT(currentLayerStyle);
+ m_currentUuid = currentLayerStyle->filename();
+ }
+ }
+
+ QString url() const override
+ {
+ if (m_currentResource.isNull()) {
+ return QString();
+ }
+ return m_currentUuid;
+ }
+
+ QString type() const override
+ {
+ return m_currentResource.isNull() ? QString() : m_currentType;
+ }
+
+ QDateTime lastModified() const override { return QDateTime(); }
+
+
+ /// This only loads the resource when called
+ KoResourceSP resource() const override
+ {
+ return m_currentResource;
+ }
+};
+
+KisAslStorage::KisAslStorage(const QString &location)
+ : KisStoragePlugin(location)
+ , m_aslSerializer(new KisAslLayerStyleSerializer())
+{
+}
+
+KisAslStorage::~KisAslStorage()
+{
+
+}
+
+KisResourceStorage::ResourceItem KisAslStorage::resourceItem(const QString &url)
+{
+ KisResourceStorage::ResourceItem item;
+ item.url = url;
+ item.folder = location();
+ item.resourceType = url.contains("pattern") ? ResourceType::Patterns : ResourceType::LayerStyles;
+ item.lastModified = QFileInfo(location()).lastModified();
+ return item;
+}
+
+KoResourceSP KisAslStorage::resource(const QString &url)
+{
+ if (!m_aslSerializer->isInitialized()) {
+ m_aslSerializer->readFromFile(location());
+ }
+ int indexOfUnderscore = url.lastIndexOf("_");
+ QString realUuid = url;
+ realUuid.remove(indexOfUnderscore, url.length() - indexOfUnderscore); // remove _pattern or _style added in iterator
+ // TODO: RESOURCES: Since we do get a resource type at the beginning of the path now
+ // maybe we could skip adding the _[resourcetype] at the end of the path as well?
+ realUuid = QFileInfo(realUuid).fileName(); // remove patterns/ at the beginning, if there are any
+ if (url.contains("pattern")) {
+ QHash<QString, KoPatternSP> patterns = m_aslSerializer->patterns();
+
+ if (patterns.contains(realUuid)) {
+ return patterns[realUuid];
+ }
+ }
+ else {
+ QHash<QString, KisPSDLayerStyleSP> styles = m_aslSerializer->stylesHash();
+ if (styles.contains(realUuid)) {
+ return styles[realUuid];
+ }
+ }
+ return 0;
+}
+
+QSharedPointer<KisResourceStorage::ResourceIterator> KisAslStorage::resources(const QString &/*resourceType*/)
+{
+ return QSharedPointer<KisResourceStorage::ResourceIterator>(new AslIterator(m_aslSerializer, location()));
+}
+
+QSharedPointer<KisResourceStorage::TagIterator> KisAslStorage::tags(const QString &resourceType)
+{
+ return QSharedPointer<KisResourceStorage::TagIterator>(new AslTagIterator(location(), resourceType));
+}
diff --git a/libs/image/KisAslStorage.h b/libs/image/KisAslStorage.h
new file mode 100644
index 0000000000..49760a4210
--- /dev/null
+++ b/libs/image/KisAslStorage.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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 KISASLSTORAGE_H
+#define KISASLSTORAGE_H
+
+#include <kritaimage_export.h>
+
+#include <KisStoragePlugin.h>
+#include <kis_asl_layer_style_serializer.h>
+
+
+
+
+class KRITAIMAGE_EXPORT KisAslStorage : public KisStoragePlugin
+{
+public:
+ KisAslStorage(const QString &location);
+ virtual ~KisAslStorage();
+
+ KisResourceStorage::ResourceItem resourceItem(const QString &url) override;
+ KoResourceSP resource(const QString &url) override;
+ QSharedPointer<KisResourceStorage::ResourceIterator> resources(const QString &resourceType) override;
+ QSharedPointer<KisResourceStorage::TagIterator> tags(const QString &resourceType) override;
+
+ QSharedPointer<KisAslLayerStyleSerializer> m_aslSerializer;
+};
+
+#endif // KISASLSTORAGE_H
diff --git a/libs/image/brushengine/kis_locked_properties_proxy.cpp b/libs/image/brushengine/kis_locked_properties_proxy.cpp
index c25abff634..a88ef74714 100644
--- a/libs/image/brushengine/kis_locked_properties_proxy.cpp
+++ b/libs/image/brushengine/kis_locked_properties_proxy.cpp
@@ -1,115 +1,123 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
* 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.
*/
#include <brushengine/kis_locked_properties_proxy.h>
+
+#include <KoResource.h>
+#include <KisResourceDirtyStateSaver.h>
+
#include <brushengine/kis_locked_properties.h>
#include <brushengine/kis_locked_properties_server.h>
#include <brushengine/kis_paintop_settings.h>
#include <brushengine/kis_paintop_preset.h>
+
KisLockedPropertiesProxy::KisLockedPropertiesProxy(KisPropertiesConfiguration *p, KisLockedPropertiesSP l)
{
m_parent = p;
m_lockedProperties = l;
}
KisLockedPropertiesProxy::~KisLockedPropertiesProxy()
{
}
QVariant KisLockedPropertiesProxy::getProperty(const QString &name) const
{
KisPaintOpSettings *t = dynamic_cast<KisPaintOpSettings*>(m_parent);
- if (!t->preset()) return m_parent->getProperty(name);
+ if (!t->updateProxy()) return m_parent->getProperty(name);
// restores the dirty state on returns automagically
- KisPaintOpPreset::DirtyStateSaver dirtyStateSaver(t->preset().data());
+ // XXX: This is extremely dirty
+ KisResourceDirtyStateSaver dirtyStateSaver(qobject_cast<ProxyParent*>(t->updateProxy()->parent())->m_preset);
+ Q_UNUSED(dirtyStateSaver);
if (m_lockedProperties->lockedProperties()) {
if (m_lockedProperties->lockedProperties()->hasProperty(name)) {
KisLockedPropertiesServer::instance()->setPropertiesFromLocked(true);
if (!m_parent->hasProperty(name + "_previous")) {
m_parent->setProperty(name + "_previous", m_parent->getProperty(name));
}
m_parent->setProperty(name, m_lockedProperties->lockedProperties()->getProperty(name));
return m_lockedProperties->lockedProperties()->getProperty(name);
} else {
if (m_parent->hasProperty(name + "_previous")) {
m_parent->setProperty(name, m_parent->getProperty(name + "_previous"));
m_parent->removeProperty(name + "_previous");
}
}
}
return m_parent->getProperty(name);
}
void KisLockedPropertiesProxy::setProperty(const QString & name, const QVariant & value)
{
KisPaintOpSettings *t = dynamic_cast<KisPaintOpSettings*>(m_parent);
- if (!t->preset()) return;
+ if (!t->updateProxy()) return;
if (m_lockedProperties->lockedProperties()) {
if (m_lockedProperties->lockedProperties()->hasProperty(name)) {
m_lockedProperties->lockedProperties()->setProperty(name, value);
m_parent->setProperty(name, value);
if (!m_parent->hasProperty(name + "_previous")) {
// restores the dirty state on returns automagically
- KisPaintOpPreset::DirtyStateSaver dirtyStateSaver(t->preset().data());
-
+ // XXX: This is extremely dirty
+ KisResourceDirtyStateSaver dirtyStateSaver(qobject_cast<ProxyParent*>(t->updateProxy()->parent())->m_preset);
m_parent->setProperty(name + "_previous", m_parent->getProperty(name));
}
return;
}
}
m_parent->setProperty(name, value);
}
bool KisLockedPropertiesProxy::hasProperty(const QString &name) const
{
KisPaintOpSettings *t = dynamic_cast<KisPaintOpSettings*>(m_parent);
- if (!t->preset()) return m_parent->hasProperty(name);
+ if (!t->updateProxy()) return m_parent->hasProperty(name);
return (m_lockedProperties->lockedProperties() &&
m_lockedProperties->lockedProperties()->hasProperty(name)) ||
m_parent->hasProperty(name);
}
QList<QString> KisLockedPropertiesProxy::getPropertiesKeys() const
{
KisPaintOpSettings *t = dynamic_cast<KisPaintOpSettings*>(m_parent);
- if (!t->preset()) return m_parent->getPropertiesKeys();
+ if (!t->updateProxy()) return m_parent->getPropertiesKeys();
QList<QString> result = m_parent->getPropertiesKeys();
if (m_lockedProperties->lockedProperties()) {
- QSet<QString> properties = QSet<QString>::fromList(result);
- properties += QSet<QString>::fromList(m_lockedProperties->lockedProperties()->getPropertiesKeys());
+ QSet<QString> properties = QSet<QString>(result.begin(), result.end());
+ properties += QSet<QString>(m_lockedProperties->lockedProperties()->getPropertiesKeys().begin(),
+ m_lockedProperties->lockedProperties()->getPropertiesKeys().end());
- result = properties.toList();
+ result = QList<QString>(properties.begin(), properties.end());
}
return result;
}
diff --git a/libs/image/brushengine/kis_no_size_paintop_settings.cpp b/libs/image/brushengine/kis_no_size_paintop_settings.cpp
index 1efdf16d88..66a68e8212 100644
--- a/libs/image/brushengine/kis_no_size_paintop_settings.cpp
+++ b/libs/image/brushengine/kis_no_size_paintop_settings.cpp
@@ -1,34 +1,35 @@
/*
* 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_no_size_paintop_settings.h"
-KisNoSizePaintOpSettings::KisNoSizePaintOpSettings()
+KisNoSizePaintOpSettings::KisNoSizePaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
+ : KisPaintOpSettings(resourcesInterface)
{
}
void KisNoSizePaintOpSettings::setPaintOpSize(qreal value)
{
Q_UNUSED(value);
}
qreal KisNoSizePaintOpSettings::paintOpSize() const
{
return 1.0;
}
diff --git a/libs/image/brushengine/kis_no_size_paintop_settings.h b/libs/image/brushengine/kis_no_size_paintop_settings.h
index 8cc29ca7a8..0cff0b7d2a 100644
--- a/libs/image/brushengine/kis_no_size_paintop_settings.h
+++ b/libs/image/brushengine/kis_no_size_paintop_settings.h
@@ -1,35 +1,35 @@
/*
* 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 KISNOSIZEPAINTOPSETTINGS_H
#define KISNOSIZEPAINTOPSETTINGS_H
#include "kis_paintop_settings.h"
#include "kritaimage_export.h"
class KRITAIMAGE_EXPORT KisNoSizePaintOpSettings : public KisPaintOpSettings
{
public:
- KisNoSizePaintOpSettings();
+ KisNoSizePaintOpSettings(KisResourcesInterfaceSP resourcesInterface);
void setPaintOpSize(qreal value) override;
qreal paintOpSize() const override;
};
#endif // KISNOSIZEPAINTOPSETTINGS_H
diff --git a/libs/image/brushengine/kis_paintop_factory.h b/libs/image/brushengine/kis_paintop_factory.h
index 7c62545621..c39dc50135 100644
--- a/libs/image/brushengine/kis_paintop_factory.h
+++ b/libs/image/brushengine/kis_paintop_factory.h
@@ -1,118 +1,129 @@
/*
* 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;
+class KoResource;
+using KoResourceSP = QSharedPointer<KoResource>;
+
+class KisResourcesInterface;
+using KisResourcesInterfaceSP = QSharedPointer<KisResourcesInterface>;
+
/**
* 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;
+ /**
+ * @return all the resources linked to \p settings.
+ */
+ virtual QList<KoResourceSP> prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface) = 0;
+
+ /**
+ * @return all the resources embedded into \p settings. The resources are first tried to be loaded
+ * from \p resourcesInterface, and, if it fails, loaded from the embedded data.
+ */
+ virtual QList<KoResourceSP> prepareEmbeddedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface) = 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;
+ virtual KisPaintOpSettingsSP createSettings(KisResourcesInterfaceSP resourcesInterface) = 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/brushengine/kis_paintop_preset.cpp b/libs/image/brushengine/kis_paintop_preset.cpp
index 59d3f0aa2c..896e75b7e5 100644
--- a/libs/image/brushengine/kis_paintop_preset.cpp
+++ b/libs/image/brushengine/kis_paintop_preset.cpp
@@ -1,425 +1,495 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2008
* Copyright (C) Sven Langkamp <sven.langkamp@gmail.com>, (C) 2009
*
* 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 <brushengine/kis_paintop_preset.h>
#include <QFile>
#include <QSize>
#include <QImage>
#include <QImageWriter>
#include <QImageReader>
#include <QDomDocument>
#include <QBuffer>
+#include <KisResourceDirtyStateSaver.h>
+
#include <brushengine/kis_paintop_settings.h>
#include "kis_paintop_registry.h"
#include "kis_painter.h"
#include <brushengine/kis_paint_information.h>
#include "kis_paint_device.h"
#include "kis_image.h"
#include "kis_paintop_settings_update_proxy.h"
#include <brushengine/kis_paintop_config_widget.h>
+#include <KisRequiredResourcesOperators.h>
#include <KoStore.h>
struct Q_DECL_HIDDEN KisPaintOpPreset::Private {
- Private()
- : settings(0),
- dirtyPreset(false)
+ Private(KisPaintOpPreset *q)
{
+ proxyParent = new ProxyParent(q);
}
- KisPaintOpSettingsSP settings;
- bool dirtyPreset;
- QScopedPointer<KisPaintopSettingsUpdateProxy> updateProxy;
+ QPointer<ProxyParent> proxyParent{0};
+ KisPaintOpSettingsSP settings {0};
+ QPointer<KisPaintopSettingsUpdateProxy> updateProxy {0};
};
KisPaintOpPreset::KisPaintOpPreset()
: KoResource(QString())
- , m_d(new Private)
+ , d(new Private(this))
{
}
KisPaintOpPreset::KisPaintOpPreset(const QString & fileName)
: KoResource(fileName)
- , m_d(new Private)
+ , d(new Private(this))
{
}
KisPaintOpPreset::~KisPaintOpPreset()
{
- delete m_d;
+ delete d;
}
-KisPaintOpPresetSP KisPaintOpPreset::clone() const
+KisPaintOpPreset::KisPaintOpPreset(const KisPaintOpPreset &rhs)
+ : KoResource(rhs)
+ , d(new Private(this))
{
- KisPaintOpPresetSP preset(new KisPaintOpPreset());
-
- if (settings()) {
- preset->setSettings(settings()); // the settings are cloned inside!
+ if (rhs.settings()) {
+ setSettings(rhs.settings()); // the settings are cloned inside!
}
- preset->setDirty(isDirty());
+ setDirty(isDirty());
// only valid if we could clone the settings
- preset->setValid(settings());
-
- preset->setPaintOp(paintOp());
- preset->setName(name());
- preset->setImage(image());
- preset->settings()->setPreset(KisPaintOpPresetWSP(preset));
-
- Q_ASSERT(preset->valid());
+ setValid(rhs.settings());
- return preset;
-}
-void KisPaintOpPreset::setDirty(bool value)
-{
- m_d->dirtyPreset = value;
+ setPaintOp(rhs.paintOp());
+ setName(rhs.name());
+ setImage(rhs.image());
+ settings()->setUpdateProxy(rhs.updateProxy());
}
-bool KisPaintOpPreset::isDirty() const
+KoResourceSP KisPaintOpPreset::clone() const
{
- return m_d->dirtyPreset;
+ return KoResourceSP(new KisPaintOpPreset(*this));
}
void KisPaintOpPreset::setPaintOp(const KoID & paintOp)
{
- Q_ASSERT(m_d->settings);
- m_d->settings->setProperty("paintop", paintOp.id());
+ Q_ASSERT(d->settings);
+ d->settings->setProperty("paintop", paintOp.id());
}
KoID KisPaintOpPreset::paintOp() const
{
- Q_ASSERT(m_d->settings);
- return KoID(m_d->settings->getString("paintop"));
+ Q_ASSERT(d->settings);
+ return KoID(d->settings->getString("paintop"));
}
void KisPaintOpPreset::setOptionsWidget(KisPaintOpConfigWidget* widget)
{
- if (m_d->settings) {
- m_d->settings->setOptionsWidget(widget);
+ if (d->settings) {
+ d->settings->setOptionsWidget(widget);
if (widget) {
- widget->setConfigurationSafe(m_d->settings);
+ widget->setConfigurationSafe(d->settings);
}
}
}
void KisPaintOpPreset::setSettings(KisPaintOpSettingsSP settings)
{
Q_ASSERT(settings);
Q_ASSERT(!settings->getString("paintop", QString()).isEmpty());
- DirtyStateSaver dirtyStateSaver(this);
+ KisResourceDirtyStateSaver dirtyStateSaver(this);
+ Q_UNUSED(dirtyStateSaver);
KisPaintOpConfigWidget *oldOptionsWidget = 0;
- if (m_d->settings) {
- oldOptionsWidget = m_d->settings->optionsWidget();
- m_d->settings->setOptionsWidget(0);
- m_d->settings->setPreset(0);
- m_d->settings = 0;
+ if (d->settings) {
+ oldOptionsWidget = d->settings->optionsWidget();
+ d->settings->setOptionsWidget(0);
+ d->settings->setUpdateProxy(updateProxy());
+ d->settings = 0;
}
if (settings) {
- m_d->settings = settings->clone();
- m_d->settings->setPreset(KisPaintOpPresetWSP(this));
+ d->settings = settings->clone();
+ d->settings->setUpdateProxy(updateProxy());
if (oldOptionsWidget) {
- oldOptionsWidget->setConfigurationSafe(m_d->settings);
- m_d->settings->setOptionsWidget(oldOptionsWidget);
+ oldOptionsWidget->setConfigurationSafe(d->settings);
+ d->settings->setOptionsWidget(oldOptionsWidget);
}
}
- setValid(m_d->settings);
+ setValid(d->settings);
- if (m_d->updateProxy) {
- m_d->updateProxy->notifyUniformPropertiesChanged();
- m_d->updateProxy->notifySettingsChanged();
+ if (d->updateProxy) {
+ d->updateProxy->notifyUniformPropertiesChanged();
+ d->updateProxy->notifySettingsChanged();
}
}
KisPaintOpSettingsSP KisPaintOpPreset::settings() const
{
- Q_ASSERT(m_d->settings);
- Q_ASSERT(!m_d->settings->getString("paintop", QString()).isEmpty());
+ Q_ASSERT(d->settings);
+ Q_ASSERT(!d->settings->getString("paintop", QString()).isEmpty());
- return m_d->settings;
+ return d->settings;
}
-bool KisPaintOpPreset::load()
+bool KisPaintOpPreset::load(KisResourcesInterfaceSP resourcesInterface)
{
- dbgImage << "Load preset " << filename();
+ qDebug() << "Load preset " << filename();
setValid(false);
if (filename().isEmpty()) {
return false;
}
QIODevice *dev = 0;
QByteArray ba;
if (filename().startsWith("bundle://")) {
QString bn = filename().mid(9);
int pos = bn.lastIndexOf(":");
QString fn = bn.right(bn.size() - pos - 1);
bn = bn.left(pos);
QScopedPointer<KoStore> resourceStore(KoStore::createStore(bn, KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip));
if (!resourceStore || resourceStore->bad()) {
warnKrita << "Could not open store on bundle" << bn;
return false;
}
if (resourceStore->isOpen()) resourceStore->close();
if (!resourceStore->open(fn)) {
warnKrita << "Could not open preset" << fn << "in bundle" << bn;
return false;
}
ba = resourceStore->device()->readAll();
dev = new QBuffer(&ba);
resourceStore->close();
}
else {
dev = new QFile(filename());
if (dev->size() == 0)
{
delete dev;
return false;
}
if (!dev->open(QIODevice::ReadOnly)) {
warnKrita << "Can't open file " << filename();
delete dev;
return false;
}
}
- bool res = loadFromDevice(dev);
+ bool res = loadFromDevice(dev, resourcesInterface);
delete dev;
setValid(res);
setDirty(false);
return res;
}
-bool KisPaintOpPreset::loadFromDevice(QIODevice *dev)
+bool KisPaintOpPreset::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
{
QImageReader reader(dev, "PNG");
QString version = reader.text("version");
QString preset = reader.text("preset");
dbgImage << version;
if (version != "2.2") {
return false;
}
QImage img;
if (!reader.read(&img)) {
dbgImage << "Fail to decode PNG";
return false;
}
//Workaround for broken presets
//Presets was saved with nested cdata section
preset.replace("<curve><![CDATA[", "<curve>");
preset.replace("]]></curve>", "</curve>");
QDomDocument doc;
if (!doc.setContent(preset)) {
return false;
}
- fromXML(doc.documentElement());
+ fromXML(doc.documentElement(), resourcesInterface);
- if (!m_d->settings) {
+ if (!d->settings) {
return false;
}
setValid(true);
setImage(img);
return true;
}
bool KisPaintOpPreset::save()
{
-
- if (filename().isEmpty())
- return false;
-
- QString paintopid = m_d->settings->getString("paintop", QString());
-
+ const QString paintopid = d->settings->getString("paintop", QString());
if (paintopid.isEmpty())
return false;
- QFile f(filename());
- f.open(QFile::WriteOnly);
-
- return saveToDevice(&f);
+ return KoResource::save();
}
void KisPaintOpPreset::toXML(QDomDocument& doc, QDomElement& elt) const
{
- QString paintopid = m_d->settings->getString("paintop", QString());
+ QString paintopid = d->settings->getString("paintop", QString());
elt.setAttribute("paintopid", paintopid);
elt.setAttribute("name", name());
// sanitize the settings
- bool hasTexture = m_d->settings->getBool("Texture/Pattern/Enabled");
+ bool hasTexture = d->settings->getBool("Texture/Pattern/Enabled");
if (!hasTexture) {
- Q_FOREACH (const QString & key, m_d->settings->getProperties().keys()) {
+ Q_FOREACH (const QString & key, d->settings->getProperties().keys()) {
if (key.startsWith("Texture") && key != "Texture/Pattern/Enabled") {
- m_d->settings->removeProperty(key);
+ d->settings->removeProperty(key);
}
}
}
- m_d->settings->toXML(doc, elt);
+ d->settings->toXML(doc, elt);
}
-void KisPaintOpPreset::fromXML(const QDomElement& presetElt)
+void KisPaintOpPreset::fromXML(const QDomElement& presetElt, KisResourcesInterfaceSP resourcesInterface)
{
setName(presetElt.attribute("name"));
QString paintopid = presetElt.attribute("paintopid");
if (paintopid.isEmpty()) {
dbgImage << "No paintopid attribute";
setValid(false);
return;
}
if (KisPaintOpRegistry::instance()->get(paintopid) == 0) {
dbgImage << "No paintop " << paintopid;
setValid(false);
return;
}
KoID id(paintopid, QString());
- KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->settings(id);
+ KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->createSettings(id, resourcesInterface);
if (!settings) {
setValid(false);
warnKrita << "Could not load settings for preset" << paintopid;
return;
}
settings->fromXML(presetElt);
// sanitize the settings
bool hasTexture = settings->getBool("Texture/Pattern/Enabled");
if (!hasTexture) {
Q_FOREACH (const QString & key, settings->getProperties().keys()) {
if (key.startsWith("Texture") && key != "Texture/Pattern/Enabled") {
settings->removeProperty(key);
}
}
}
setSettings(settings);
}
bool KisPaintOpPreset::saveToDevice(QIODevice *dev) const
{
QImageWriter writer(dev, "PNG");
QDomDocument doc;
QDomElement root = doc.createElement("Preset");
toXML(doc, root);
doc.appendChild(root);
writer.setText("version", "2.2");
writer.setText("preset", doc.toString());
QImage img;
if (image().isNull()) {
img = QImage(1, 1, QImage::Format_RGB32);
} else {
img = image();
}
- m_d->dirtyPreset = false;
+ const_cast<KisPaintOpPreset*>(this)->setDirty(false);
KoResource::saveToDevice(dev);
return writer.write(img);
}
-KisPaintopSettingsUpdateProxy* KisPaintOpPreset::updateProxy() const
+QPointer<KisPaintopSettingsUpdateProxy> KisPaintOpPreset::updateProxy() const
{
- if (!m_d->updateProxy) {
- m_d->updateProxy.reset(new KisPaintopSettingsUpdateProxy());
+ if (!d->updateProxy) {
+ d->updateProxy = new KisPaintopSettingsUpdateProxy(d->proxyParent);
}
- return m_d->updateProxy.data();
+ return d->updateProxy;
}
-KisPaintopSettingsUpdateProxy* KisPaintOpPreset::updateProxyNoCreate() const
+QPointer<KisPaintopSettingsUpdateProxy> KisPaintOpPreset::updateProxyNoCreate() const
{
- return m_d->updateProxy.data();
+ return d->updateProxy;
}
QList<KisUniformPaintOpPropertySP> KisPaintOpPreset::uniformProperties()
{
- return m_d->settings->uniformProperties(m_d->settings);
+ return d->settings->uniformProperties(d->settings);
}
bool KisPaintOpPreset::hasMaskingPreset() const
{
- return m_d->settings && m_d->settings->hasMaskingSettings();
+ return d->settings && d->settings->hasMaskingSettings();
}
KisPaintOpPresetSP KisPaintOpPreset::createMaskingPreset() const
{
KisPaintOpPresetSP result;
- if (m_d->settings && m_d->settings->hasMaskingSettings()) {
- result = new KisPaintOpPreset();
- result->setSettings(m_d->settings->createMaskingSettings());
+ if (d->settings && d->settings->hasMaskingSettings()) {
+ result.reset(new KisPaintOpPreset());
+ result->setSettings(d->settings->createMaskingSettings());
if (!result->valid()) {
result.clear();
}
}
return result;
}
-KisPaintOpPreset::UpdatedPostponer::UpdatedPostponer(KisPaintOpPreset *preset)
+KisResourcesInterfaceSP KisPaintOpPreset::resourcesInterface() const
+{
+ return d->settings ? d->settings->resourcesInterface() : nullptr;
+}
+
+void KisPaintOpPreset::setResourcesInterface(KisResourcesInterfaceSP resourcesInterface)
+{
+ KIS_SAFE_ASSERT_RECOVER_RETURN(d->settings);
+ d->settings->setResourcesInterface(resourcesInterface);
+}
+
+namespace KisRequiredResourcesOperators
+{
+template <>
+struct ResourceTraits<KisPaintOpPreset>
+{
+ template <typename T>
+ using SharedPointerType = QSharedPointer<T>;
+
+ template <typename D, typename S>
+ static inline SharedPointerType<D> dynamicCastSP(SharedPointerType<S> src) {
+ return src.template dynamicCast<D>();
+ }
+};
+}
+
+void KisPaintOpPreset::createLocalResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface)
+{
+ KisRequiredResourcesOperators::createLocalResourcesSnapshot(this, globalResourcesInterface);
+}
+
+bool KisPaintOpPreset::hasLocalResourcesSnapshot() const
+{
+ return KisRequiredResourcesOperators::hasLocalResourcesSnapshot(this);
+}
+
+KisPaintOpPresetSP KisPaintOpPreset::cloneWithResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface) const
+{
+ return KisRequiredResourcesOperators::cloneWithResourcesSnapshot(this, globalResourcesInterface);
+}
+
+QList<KoResourceSP> KisPaintOpPreset::linkedResources(KisResourcesInterfaceSP globalResourcesInterface) const
+{
+ QList<KoResourceSP> resources;
+
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(d->settings, resources);
+
+ KisPaintOpFactory* f = KisPaintOpRegistry::instance()->value(paintOp().id());
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(f, resources);
+ resources << f->prepareLinkedResources(d->settings, globalResourcesInterface);
+
+ if (hasMaskingPreset()) {
+ KisPaintOpPresetSP maskingPreset = createMaskingPreset();
+
+ KisPaintOpFactory* f = KisPaintOpRegistry::instance()->value(maskingPreset->paintOp().id());
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(f, resources);
+ resources << f->prepareLinkedResources(maskingPreset->settings(), globalResourcesInterface);
+ }
+
+ return resources;
+}
+
+QList<KoResourceSP> KisPaintOpPreset::embeddedResources(KisResourcesInterfaceSP globalResourcesInterface) const
+{
+ QList<KoResourceSP> resources;
+
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(d->settings, resources);
+
+ KisPaintOpFactory* f = KisPaintOpRegistry::instance()->value(paintOp().id());
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(f, resources);
+ resources << f->prepareEmbeddedResources(d->settings, globalResourcesInterface);
+
+ if (hasMaskingPreset()) {
+ KisPaintOpPresetSP maskingPreset = createMaskingPreset();
+
+ KisPaintOpFactory* f = KisPaintOpRegistry::instance()->value(maskingPreset->paintOp().id());
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(f, resources);
+ resources << f->prepareEmbeddedResources(maskingPreset->settings(), globalResourcesInterface);
+ }
+
+ return resources;
+}
+
+KisPaintOpPreset::UpdatedPostponer::UpdatedPostponer(KisPaintOpPresetSP preset)
: m_updateProxy(preset->updateProxyNoCreate())
{
if (m_updateProxy) {
m_updateProxy->postponeSettingsChanges();
}
}
KisPaintOpPreset::UpdatedPostponer::~UpdatedPostponer()
{
if (m_updateProxy) {
m_updateProxy->unpostponeSettingsChanges();
}
}
diff --git a/libs/image/brushengine/kis_paintop_preset.h b/libs/image/brushengine/kis_paintop_preset.h
index 3cfbd9c40a..74e2ea367a 100644
--- a/libs/image/brushengine/kis_paintop_preset.h
+++ b/libs/image/brushengine/kis_paintop_preset.h
@@ -1,154 +1,181 @@
/* 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_PRESET_H
#define KIS_PAINTOP_PRESET_H
-#include <resources/KoResource.h>
+#include <QPointer>
+
+#include <KoResource.h>
#include "KoID.h"
#include "kis_types.h"
#include "kis_shared.h"
#include "kritaimage_export.h"
#include <brushengine/kis_uniform_paintop_property.h>
+#include <kis_paintop_settings_update_proxy.h>
-class KisPaintopSettingsUpdateProxy;
class KisPaintOpConfigWidget;
+
+class ProxyParent : public QObject
+{
+ Q_OBJECT
+public:
+ ProxyParent(KisPaintOpPreset *preset)
+ {
+ m_preset = preset;
+ }
+
+ KisPaintOpPreset *m_preset {0};
+};
+
/**
* A KisPaintOpPreset contains a particular set of settings
* associated with a paintop, like brush, paintopsettings.
* A new property in this class is to make it dirty. That means the
* user can now temporarily save any tweaks in the Preset throughout
* the session. The Dirty Preset setting/unsetting is handled by KisPaintOpPresetSettings
*/
-class KRITAIMAGE_EXPORT KisPaintOpPreset : public KoResource, public KisShared
+class KRITAIMAGE_EXPORT KisPaintOpPreset : public KoResource
{
+public:
+
+ /**
+ * @brief The UpdatedPostponer class
+ * @see KisPaintopSettingsUpdateProxy::postponeSettingsChanges()
+ */
+ class KRITAIMAGE_EXPORT UpdatedPostponer{
+ public:
+ UpdatedPostponer(KisPaintOpPresetSP preset);
+
+ ~UpdatedPostponer();
+
+ private:
+ QPointer<KisPaintopSettingsUpdateProxy> m_updateProxy;
+ };
+
public:
KisPaintOpPreset();
KisPaintOpPreset(const QString& filename);
~KisPaintOpPreset() override;
- KisPaintOpPresetSP clone() const;
+ KisPaintOpPreset(const KisPaintOpPreset &rhs);
+ KisPaintOpPreset &operator=(const KisPaintOpPreset &rhs) = delete;
+ KoResourceSP clone() const override;
/// set the id of the paintop plugin
void setPaintOp(const KoID & paintOp);
/// return the id of the paintop plugin
KoID paintOp() const;
/// replace the current settings object with the specified settings
void setSettings(KisPaintOpSettingsSP settings);
void setOriginalSettings(KisPaintOpSettingsSP originalSettings);
/// return the settings that define this paintop preset
KisPaintOpSettingsSP settings() const;
KisPaintOpSettingsSP originalSettings() const;
- bool load() override;
- bool loadFromDevice(QIODevice *dev) override;
+ bool load(KisResourcesInterfaceSP resourcesInterface) override;
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
bool save() override;
bool saveToDevice(QIODevice* dev) const override;
+ QPair<QString, QString> resourceType() const override
+ {
+ return QPair<QString, QString>(ResourceType::PaintOpPresets, "");
+ }
+
void toXML(QDomDocument& doc, QDomElement& elt) const;
- void fromXML(const QDomElement& elt);
+ void fromXML(const QDomElement& elt, KisResourcesInterfaceSP resourcesInterface);
bool removable() const {
return true;
}
QString defaultFileExtension() const override {
return ".kpp";
}
- /// Mark the preset as modified but not saved
- void setDirty(bool value);
-
- /// @return true if the preset has been modified, but not saved
- bool isDirty() const;
-
- /**
- * Never use manual save/restore calls to
- * isPresetDirty()/setPresetDirty()! They will lead to
- * hard-to-tack-down bugs when the dirty state will not be
- * restored on jumps like 'return', 'break' or exception.
- */
- class KRITAIMAGE_EXPORT DirtyStateSaver {
- public:
- DirtyStateSaver(KisPaintOpPreset *preset)
- : m_preset(preset), m_isDirty(preset->isDirty())
- {
- }
-
- ~DirtyStateSaver() {
- m_preset->setDirty(m_isDirty);
- }
-
- private:
- KisPaintOpPreset *m_preset;
- bool m_isDirty;
- };
-
- /**
- * @brief The UpdatedPostponer class
- * @see KisPaintopSettingsUpdateProxy::postponeSettingsChanges()
- */
- class KRITAIMAGE_EXPORT UpdatedPostponer{
- public:
- UpdatedPostponer(KisPaintOpPreset *preset);
-
- ~UpdatedPostponer();
-
- private:
- KisPaintopSettingsUpdateProxy *m_updateProxy;
- };
-
void setOptionsWidget(KisPaintOpConfigWidget *widget);
- KisPaintopSettingsUpdateProxy* updateProxy() const;
- KisPaintopSettingsUpdateProxy* updateProxyNoCreate() const;
+ QPointer<KisPaintopSettingsUpdateProxy> updateProxy() const;
+ QPointer<KisPaintopSettingsUpdateProxy> updateProxyNoCreate() const;
QList<KisUniformPaintOpPropertySP> uniformProperties();
/**
* @return true if this preset demands a secondary masked brush running
* alongside it
*/
bool hasMaskingPreset() const;
/**
* @return a newly created preset of the masked brush that should be run
* alongside the current brush
*/
KisPaintOpPresetSP createMaskingPreset() const;
+ /**
+ * @return resource interface that is used by KisPaintOpSettings object for
+ * loading linked resources
+ */
+ KisResourcesInterfaceSP resourcesInterface() const;
+
+ /**
+ * Set resource interface that will be used by KisPaintOpSettings object for
+ * loading linked resources
+ */
+ void setResourcesInterface(KisResourcesInterfaceSP resourcesInterface);
+
+ /**
+ * \see KisRequiredResourcesOperators::createLocalResourcesSnapshot
+ */
+ void createLocalResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface = nullptr);
+
+ /**
+ * \see KisRequiredResourcesOperators::hasLocalResourcesSnapshot
+ */
+ bool hasLocalResourcesSnapshot() const;
+
+ /**
+ * \see KisRequiredResourcesOperators::cloneWithResourcesSnapshot
+ */
+ KisPaintOpPresetSP cloneWithResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface = nullptr) const;
+
+
+ QList<KoResourceSP> linkedResources(KisResourcesInterfaceSP globalResourcesInterface) const;
+
+ QList<KoResourceSP> embeddedResources(KisResourcesInterfaceSP globalResourcesInterface) const;
private:
struct Private;
- Private * const m_d;
+ Private * const d;
};
Q_DECLARE_METATYPE(KisPaintOpPresetSP)
#endif
diff --git a/libs/image/brushengine/kis_paintop_registry.cc b/libs/image/brushengine/kis_paintop_registry.cc
index 8cb8643a64..c1a74c45fa 100644
--- a/libs/image/brushengine/kis_paintop_registry.cc
+++ b/libs/image/brushengine/kis_paintop_registry.cc
@@ -1,175 +1,161 @@
/*
* 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 "kis_paintop_registry.h"
#include <QGlobalStatic>
#include <klocalizedstring.h>
#include <KoGenericRegistry.h>
#include <KoPluginLoader.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOp.h>
#include <KoID.h>
#include "kis_paint_device.h"
#include "kis_painter.h"
#include "kis_debug.h"
#include "kis_layer.h"
#include "kis_image.h"
#include "kis_paintop_config_widget.h"
Q_GLOBAL_STATIC(KisPaintOpRegistry, s_registryInstance)
KisPaintOpRegistry::KisPaintOpRegistry()
{
}
KisPaintOpRegistry::~KisPaintOpRegistry()
{
Q_FOREACH (const QString & id, keys()) {
delete get(id);
}
dbgRegistry << "Deleting KisPaintOpRegistry";
}
void KisPaintOpRegistry::initRegistry()
{
KoPluginLoader::instance()->load("Krita/Paintop", "(Type == 'Service') and ([X-Krita-Version] == 28)");
-
- QStringList toBeRemoved;
-
- Q_FOREACH (const QString & id, keys()) {
- KisPaintOpFactory *factory = get(id);
- if (!factory->settings()) {
- toBeRemoved << id;
- } else {
- factory->processAfterLoading();
- }
- }
- Q_FOREACH (const QString & id, toBeRemoved) {
- remove(id);
- }
}
KisPaintOpRegistry* KisPaintOpRegistry::instance()
{
if (!s_registryInstance.exists()) {
dbgRegistry << "initializing KisPaintOpRegistry";
s_registryInstance->initRegistry();
}
return s_registryInstance;
}
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
void KisPaintOpRegistry::preinitializePaintOpIfNeeded(const KisPaintOpPresetSP preset)
{
if (!preset) return;
KisPaintOpFactory *f = value(preset->paintOp().id());
f->preinitializePaintOpIfNeeded(preset->settings());
}
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
KisPaintOp * KisPaintOpRegistry::paintOp(const QString & id, const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) const
{
if (painter == 0) {
warnKrita << " KisPaintOpRegistry::paintOp painter is null";
return 0;
}
Q_ASSERT(settings);
KisPaintOpFactory* f = value(id);
if (f) {
KisPaintOp * op = f->createOp(settings, painter, node, image);
if (op) {
return op;
}
}
warnKrita << "Could not create paintop for factory" << id << "with settings" << settings;
return 0;
}
KisPaintOp * KisPaintOpRegistry::paintOp(const KisPaintOpPresetSP preset, KisPainter * painter, KisNodeSP node, KisImageSP image) const
{
Q_ASSERT(preset);
Q_ASSERT(painter);
if (!preset) return 0;
return paintOp(preset->paintOp().id(), preset->settings(), painter, node, image);
}
-KisPaintOpSettingsSP KisPaintOpRegistry::settings(const KoID& id) const
+KisPaintOpSettingsSP KisPaintOpRegistry::createSettings(const KoID& id, KisResourcesInterfaceSP resourcesInterface) const
{
KisPaintOpFactory *f = value(id.id());
Q_ASSERT(f);
if (f) {
- KisPaintOpSettingsSP settings = f->settings();
+ KisPaintOpSettingsSP settings = f->createSettings(resourcesInterface);
settings->setProperty("paintop", id.id());
return settings;
}
return 0;
}
-KisPaintOpPresetSP KisPaintOpRegistry::defaultPreset(const KoID& id) const
+KisPaintOpPresetSP KisPaintOpRegistry::defaultPreset(const KoID& id, KisResourcesInterfaceSP resourcesInterface) const
{
- KisPaintOpSettingsSP s = settings(id);
+ KisPaintOpSettingsSP s = createSettings(id, resourcesInterface);
if (s.isNull()) {
return KisPaintOpPresetSP();
}
KisPaintOpPresetSP preset(new KisPaintOpPreset());
preset->setName(i18n("default"));
preset->setSettings(s);
preset->setPaintOp(id);
Q_ASSERT(!preset->paintOp().id().isEmpty());
preset->setValid(true);
return preset;
}
QIcon KisPaintOpRegistry::icon(const KoID &id) const
{
KisPaintOpFactory* f = value(id.id());
if (!f) {
dbgRegistry << "No paintop" << id.id() << "";
QPixmap p = QPixmap(22, 22);
p.fill(Qt::transparent);
return QIcon(p);
}
return f->icon();
}
QList<KoID> KisPaintOpRegistry::listKeys() const
{
QList<KoID> answer;
Q_FOREACH (const QString & key, keys()) {
answer.append(KoID(key, get(key)->name()));
}
return answer;
}
diff --git a/libs/image/brushengine/kis_paintop_registry.h b/libs/image/brushengine/kis_paintop_registry.h
index 435a0c2205..e4591d0570 100644
--- a/libs/image/brushengine/kis_paintop_registry.h
+++ b/libs/image/brushengine/kis_paintop_registry.h
@@ -1,105 +1,105 @@
/*
* 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_PAINTOP_REGISTRY_H_
#define KIS_PAINTOP_REGISTRY_H_
#include <QObject>
#include "KoGenericRegistry.h"
#include "kis_paintop.h"
#include <brushengine/kis_paintop_factory.h>
#include "kis_types.h"
#include <brushengine/kis_paintop_settings.h>
#include <brushengine/kis_paintop_preset.h>
#include <kis_threaded_text_rendering_workaround.h>
#include <kritaimage_export.h>
class KisPaintOp;
class KisPainter;
/**
* Manages the loading and creating of all paintop plugins.
*/
class KRITAIMAGE_EXPORT KisPaintOpRegistry : public QObject, public KoGenericRegistry<KisPaintOpFactory*>
{
Q_OBJECT
public:
KisPaintOpRegistry();
~KisPaintOpRegistry() override;
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
void preinitializePaintOpIfNeeded(const KisPaintOpPresetSP preset);
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
/**
* Create and return a paintop based on the given preset. A preset defines
* a paintop, a settings object and possible a brush tip.
*/
KisPaintOp* paintOp(const KisPaintOpPresetSP preset, KisPainter * painter, KisNodeSP node, KisImageSP image) const;
/**
* Create and return an (abstracted) configuration widget
* for using the specified paintop with the specified input device,
* with the specified parent as widget parent. Returns 0 if there
* are no settings available for the given device.
*/
- KisPaintOpSettingsSP settings(const KoID& id) const;
+ KisPaintOpSettingsSP createSettings(const KoID& id, KisResourcesInterfaceSP resourcesInterface) const;
/**
* @return a default preset for the given paintop.
*/
- KisPaintOpPresetSP defaultPreset(const KoID& id) const;
+ KisPaintOpPresetSP defaultPreset(const KoID& id, KisResourcesInterfaceSP resourcesInterface) const;
// Get the icon to show in the user interface
QIcon icon(const KoID & id) const;
/**
* 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;
public:
static KisPaintOpRegistry* instance();
private:
KisPaintOpRegistry(const KisPaintOpRegistry&);
KisPaintOpRegistry operator=(const KisPaintOpRegistry&);
void initRegistry();
// So the settings can get a paintop to render their sample image
friend class KisPaintOpSettings;
/**
* Return a newly created paintop. You are responsible for deleting
*/
KisPaintOp * paintOp(const QString& id, const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) const;
};
#endif // KIS_PAINTOP_REGISTRY_H_
diff --git a/libs/image/brushengine/kis_paintop_settings.cpp b/libs/image/brushengine/kis_paintop_settings.cpp
index 87f5c53a25..3b13bec1e4 100644
--- a/libs/image/brushengine/kis_paintop_settings.cpp
+++ b/libs/image/brushengine/kis_paintop_settings.cpp
@@ -1,513 +1,510 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2008 Lukáš Tvrdý <lukast.dev@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 <brushengine/kis_paintop_settings.h>
#include <QImage>
#include <QColor>
#include <QPointer>
#include <KoPointerEvent.h>
#include <KoColor.h>
#include <KoCompositeOpRegistry.h>
#include <KoViewConverter.h>
+#include "kis_paintop_preset.h"
#include "kis_paint_layer.h"
#include "kis_image.h"
#include "kis_painter.h"
#include "kis_paint_device.h"
#include "kis_paintop_registry.h"
#include "kis_timing_information.h"
#include <brushengine/kis_paint_information.h>
#include "kis_paintop_config_widget.h"
#include <brushengine/kis_paintop_preset.h>
#include "kis_paintop_settings_update_proxy.h"
#include <time.h>
#include <kis_types.h>
#include <kis_signals_blocker.h>
#include <brushengine/kis_locked_properties_server.h>
#include <brushengine/kis_locked_properties_proxy.h>
#include "KisPaintopSettingsIds.h"
#include "kis_algebra_2d.h"
struct Q_DECL_HIDDEN KisPaintOpSettings::Private {
Private()
: disableDirtyNotifications(false)
{}
QPointer<KisPaintOpConfigWidget> settingsWidget;
QString modelName;
- KisPaintOpPresetWSP preset;
+ QPointer<KisPaintopSettingsUpdateProxy> updateProxy;
QList<KisUniformPaintOpPropertyWSP> uniformProperties;
+ KisResourcesInterfaceSP resourcesInterface = 0;
bool disableDirtyNotifications;
class DirtyNotificationsLocker {
public:
DirtyNotificationsLocker(KisPaintOpSettings::Private *d)
: m_d(d),
m_oldNotificationsState(d->disableDirtyNotifications)
{
m_d->disableDirtyNotifications = true;
}
~DirtyNotificationsLocker() {
m_d->disableDirtyNotifications = m_oldNotificationsState;
}
private:
KisPaintOpSettings::Private *m_d;
bool m_oldNotificationsState;
Q_DISABLE_COPY(DirtyNotificationsLocker)
};
-
- KisPaintopSettingsUpdateProxy* updateProxyNoCreate() const {
- auto presetSP = preset.toStrongRef();
- return presetSP ? presetSP->updateProxyNoCreate() : 0;
- }
-
- KisPaintopSettingsUpdateProxy* updateProxyCreate() const {
- auto presetSP = preset.toStrongRef();
- return presetSP ? presetSP->updateProxy() : 0;
- }
};
-KisPaintOpSettings::KisPaintOpSettings()
+KisPaintOpSettings::KisPaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
: d(new Private)
{
- d->preset = 0;
+ d->updateProxy = 0;
+ d->resourcesInterface = resourcesInterface;
}
KisPaintOpSettings::~KisPaintOpSettings()
{
}
KisPaintOpSettings::KisPaintOpSettings(const KisPaintOpSettings &rhs)
: KisPropertiesConfiguration(rhs)
, d(new Private)
{
d->settingsWidget = 0;
- d->preset = rhs.preset();
+ d->updateProxy = rhs.updateProxy();
d->modelName = rhs.modelName();
+ d->resourcesInterface = rhs.d->resourcesInterface;
}
void KisPaintOpSettings::setOptionsWidget(KisPaintOpConfigWidget* widget)
{
d->settingsWidget = widget;
}
-void KisPaintOpSettings::setPreset(KisPaintOpPresetWSP preset)
+
+void KisPaintOpSettings::setUpdateProxy(const QPointer<KisPaintopSettingsUpdateProxy> proxy)
{
- d->preset = preset;
+ d->updateProxy = proxy;
}
-KisPaintOpPresetWSP KisPaintOpSettings::preset() const
+
+QPointer<KisPaintopSettingsUpdateProxy> KisPaintOpSettings::updateProxy() const
{
- return d->preset;
+ return d->updateProxy;
}
bool KisPaintOpSettings::mousePressEvent(const KisPaintInformation &paintInformation, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode)
{
Q_UNUSED(modifiers);
Q_UNUSED(currentNode);
setRandomOffset(paintInformation);
return true; // ignore the event by default
}
bool KisPaintOpSettings::mouseReleaseEvent()
{
return true; // ignore the event by default
}
void KisPaintOpSettings::setRandomOffset(const KisPaintInformation &paintInformation)
{
bool disableDirtyBefore = d->disableDirtyNotifications;
d->disableDirtyNotifications = true;
if (getBool("Texture/Pattern/Enabled")) {
if (getBool("Texture/Pattern/isRandomOffsetX")) {
setProperty("Texture/Pattern/OffsetX",
paintInformation.randomSource()->generate(0, KisPropertiesConfiguration::getInt("Texture/Pattern/MaximumOffsetX")));
}
if (getBool("Texture/Pattern/isRandomOffsetY")) {
setProperty("Texture/Pattern/OffsetY",
paintInformation.randomSource()->generate(0, KisPropertiesConfiguration::getInt("Texture/Pattern/MaximumOffsetY")));
}
}
d->disableDirtyNotifications = disableDirtyBefore;
}
bool KisPaintOpSettings::hasMaskingSettings() const
{
return getBool(KisPaintOpUtils::MaskingBrushEnabledTag, false);
}
KisPaintOpSettingsSP KisPaintOpSettings::createMaskingSettings() const
{
if (!hasMaskingSettings()) return KisPaintOpSettingsSP();
const KoID pixelBrushId(KisPaintOpUtils::MaskingBrushPaintOpId, QString());
- KisPaintOpSettingsSP maskingSettings = KisPaintOpRegistry::instance()->settings(pixelBrushId);
+ KisPaintOpSettingsSP maskingSettings = KisPaintOpRegistry::instance()->createSettings(pixelBrushId, resourcesInterface());
this->getPrefixedProperties(KisPaintOpUtils::MaskingBrushPresetPrefix, maskingSettings);
const bool useMasterSize = this->getBool(KisPaintOpUtils::MaskingBrushUseMasterSizeTag, true);
if (useMasterSize) {
const qreal masterSizeCoeff = getDouble(KisPaintOpUtils::MaskingBrushMasterSizeCoeffTag, 1.0);
maskingSettings->setPaintOpSize(masterSizeCoeff * paintOpSize());
}
return maskingSettings;
}
QString KisPaintOpSettings::maskingBrushCompositeOp() const
{
return getString(KisPaintOpUtils::MaskingBrushCompositeOpTag, COMPOSITE_MULT);
}
+KisResourcesInterfaceSP KisPaintOpSettings::resourcesInterface() const
+{
+ return d->resourcesInterface;
+}
+
+void KisPaintOpSettings::setResourcesInterface(KisResourcesInterfaceSP resourcesInterface)
+{
+ d->resourcesInterface = resourcesInterface;
+}
+
KisPaintOpSettingsSP KisPaintOpSettings::clone() const
{
QString paintopID = getString("paintop");
if (paintopID.isEmpty())
return 0;
- KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->settings(KoID(paintopID));
+ KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->createSettings(KoID(paintopID), resourcesInterface());
QMapIterator<QString, QVariant> i(getProperties());
while (i.hasNext()) {
i.next();
settings->setProperty(i.key(), QVariant(i.value()));
}
- settings->setPreset(this->preset());
+ settings->setUpdateProxy(this->updateProxy());
return settings;
}
void KisPaintOpSettings::resetSettings(const QStringList &preserveProperties)
{
QStringList allKeys = preserveProperties;
allKeys << "paintop";
QHash<QString, QVariant> preserved;
Q_FOREACH (const QString &key, allKeys) {
if (hasProperty(key)) {
preserved[key] = getProperty(key);
}
}
clearProperties();
for (auto it = preserved.constBegin(); it != preserved.constEnd(); ++it) {
setProperty(it.key(), it.value());
}
}
void KisPaintOpSettings::activate()
{
}
void KisPaintOpSettings::setPaintOpOpacity(qreal value)
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
proxy->setProperty("OpacityValue", value);
}
void KisPaintOpSettings::setPaintOpFlow(qreal value)
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
proxy->setProperty("FlowValue", value);
}
void KisPaintOpSettings::setPaintOpCompositeOp(const QString &value)
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
proxy->setProperty("CompositeOp", value);
}
qreal KisPaintOpSettings::paintOpOpacity()
{
KisLockedPropertiesProxySP proxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this);
return proxy->getDouble("OpacityValue", 1.0);
}
qreal KisPaintOpSettings::paintOpFlow()
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
return proxy->getDouble("FlowValue", 1.0);
}
QString KisPaintOpSettings::paintOpCompositeOp()
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
return proxy->getString("CompositeOp", COMPOSITE_OVER);
}
void KisPaintOpSettings::setEraserMode(bool value)
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
proxy->setProperty("EraserMode", value);
}
bool KisPaintOpSettings::eraserMode()
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
return proxy->getBool("EraserMode", false);
}
QString KisPaintOpSettings::effectivePaintOpCompositeOp()
{
return !eraserMode() ? paintOpCompositeOp() : COMPOSITE_ERASE;
}
qreal KisPaintOpSettings::savedEraserSize() const
{
return getDouble("SavedEraserSize", 0.0);
}
void KisPaintOpSettings::setSavedEraserSize(qreal value)
{
setProperty("SavedEraserSize", value);
setPropertyNotSaved("SavedEraserSize");
}
qreal KisPaintOpSettings::savedBrushSize() const
{
return getDouble("SavedBrushSize", 0.0);
}
void KisPaintOpSettings::setSavedBrushSize(qreal value)
{
setProperty("SavedBrushSize", value);
setPropertyNotSaved("SavedBrushSize");
}
qreal KisPaintOpSettings::savedEraserOpacity() const
{
return getDouble("SavedEraserOpacity", 0.0);
}
void KisPaintOpSettings::setSavedEraserOpacity(qreal value)
{
setProperty("SavedEraserOpacity", value);
setPropertyNotSaved("SavedEraserOpacity");
}
qreal KisPaintOpSettings::savedBrushOpacity() const
{
return getDouble("SavedBrushOpacity", 0.0);
}
void KisPaintOpSettings::setSavedBrushOpacity(qreal value)
{
setProperty("SavedBrushOpacity", value);
setPropertyNotSaved("SavedBrushOpacity");
}
QString KisPaintOpSettings::modelName() const
{
return d->modelName;
}
void KisPaintOpSettings::setModelName(const QString & modelName)
{
d->modelName = modelName;
}
KisPaintOpConfigWidget* KisPaintOpSettings::optionsWidget() const
{
if (d->settingsWidget.isNull())
return 0;
return d->settingsWidget.data();
}
bool KisPaintOpSettings::isValid() const
{
return true;
}
-bool KisPaintOpSettings::isLoadable()
-{
- return isValid();
-}
-
QString KisPaintOpSettings::indirectPaintingCompositeOp() const
{
return COMPOSITE_ALPHA_DARKEN;
}
bool KisPaintOpSettings::isAirbrushing() const
{
return getBool(AIRBRUSH_ENABLED, false);
}
qreal KisPaintOpSettings::airbrushInterval() const
{
qreal rate = getDouble(AIRBRUSH_RATE, 1.0);
if (rate == 0.0) {
return LONG_TIME;
}
else {
return 1000.0 / rate;
}
}
bool KisPaintOpSettings::useSpacingUpdates() const
{
return getBool(SPACING_USE_UPDATES, false);
}
bool KisPaintOpSettings::needsAsynchronousUpdates() const
{
return false;
}
QPainterPath KisPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom)
{
QPainterPath path;
if (mode.isVisible) {
path = ellipseOutline(10, 10, 1.0, 0);
if (mode.showTiltDecoration) {
path.addPath(makeTiltIndicator(info, QPointF(0.0, 0.0), 0.0, 2.0));
}
path.translate(KisAlgebra2D::alignForZoom(info.pos(), alignForZoom));
}
return path;
}
QPainterPath KisPaintOpSettings::ellipseOutline(qreal width, qreal height, qreal scale, qreal rotation)
{
QPainterPath path;
QRectF ellipse(0, 0, width * scale, height * scale);
ellipse.translate(-ellipse.center());
path.addEllipse(ellipse);
QTransform m;
m.reset();
m.rotate(rotation);
path = m.map(path);
return path;
}
QPainterPath KisPaintOpSettings::makeTiltIndicator(KisPaintInformation const& info,
QPointF const& start, qreal maxLength, qreal angle)
{
if (maxLength == 0.0) maxLength = 50.0;
maxLength = qMax(maxLength, 50.0);
qreal const length = maxLength * (1 - info.tiltElevation(info, 60.0, 60.0, true));
qreal const baseAngle = 360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0);
QLineF guideLine = QLineF::fromPolar(length, baseAngle + angle);
guideLine.translate(start);
QPainterPath ret;
ret.moveTo(guideLine.p1());
ret.lineTo(guideLine.p2());
guideLine.setAngle(baseAngle - angle);
ret.lineTo(guideLine.p2());
ret.lineTo(guideLine.p1());
return ret;
}
void KisPaintOpSettings::setProperty(const QString & name, const QVariant & value)
{
- if (value != KisPropertiesConfiguration::getProperty(name) &&
- !d->disableDirtyNotifications) {
- KisPaintOpPresetSP presetSP = preset().toStrongRef();
- if (presetSP) {
- presetSP->setDirty(true);
+ if (value != KisPropertiesConfiguration::getProperty(name) && !d->disableDirtyNotifications) {
+ if (d->updateProxy) {
+ d->updateProxy->setDirty(true);
}
}
KisPropertiesConfiguration::setProperty(name, value);
onPropertyChanged();
}
void KisPaintOpSettings::onPropertyChanged()
{
- KisPaintopSettingsUpdateProxy *proxy = d->updateProxyNoCreate();
-
- if (proxy) {
- proxy->notifySettingsChanged();
+ if (d->updateProxy) {
+ d->updateProxy->notifySettingsChanged();
}
}
bool KisPaintOpSettings::isLodUserAllowed(const KisPropertiesConfigurationSP config)
{
return config->getBool("lodUserAllowed", true);
}
void KisPaintOpSettings::setLodUserAllowed(KisPropertiesConfigurationSP config, bool value)
{
config->setProperty("lodUserAllowed", value);
}
bool KisPaintOpSettings::lodSizeThresholdSupported() const
{
return true;
}
qreal KisPaintOpSettings::lodSizeThreshold() const
{
return getDouble("lodSizeThreshold", 100.0);
}
void KisPaintOpSettings::setLodSizeThreshold(qreal value)
{
setProperty("lodSizeThreshold", value);
}
#include "kis_standard_uniform_properties_factory.h"
QList<KisUniformPaintOpPropertySP> KisPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings)
{
QList<KisUniformPaintOpPropertySP> props =
listWeakToStrong(d->uniformProperties);
if (props.isEmpty()) {
using namespace KisStandardUniformPropertiesFactory;
- props.append(createProperty(opacity, settings, d->updateProxyCreate()));
- props.append(createProperty(size, settings, d->updateProxyCreate()));
- props.append(createProperty(flow, settings, d->updateProxyCreate()));
+ props.append(createProperty(opacity, settings, d->updateProxy));
+ props.append(createProperty(size, settings, d->updateProxy));
+ props.append(createProperty(flow, settings, d->updateProxy));
d->uniformProperties = listStrongToWeak(props);
}
return props;
}
diff --git a/libs/image/brushengine/kis_paintop_settings.h b/libs/image/brushengine/kis_paintop_settings.h
index e47b8781bf..5b77316ce4 100644
--- a/libs/image/brushengine/kis_paintop_settings.h
+++ b/libs/image/brushengine/kis_paintop_settings.h
@@ -1,349 +1,356 @@
/*
* 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_PAINTOP_SETTINGS_H_
#define KIS_PAINTOP_SETTINGS_H_
#include "kis_types.h"
#include "kritaimage_export.h"
#include <QImage>
#include <QScopedPointer>
#include "kis_properties_configuration.h"
#include <brushengine/kis_paint_information.h>
#include <brushengine/kis_uniform_paintop_property.h>
-
-
class KisPaintOpConfigWidget;
class KisPaintopSettingsUpdateProxy;
+class KisResourcesInterface;
+using KisResourcesInterfaceSP = QSharedPointer<KisResourcesInterface>;
+
+
/**
* Configuration property used to control whether airbrushing is enabled.
*/
const QString AIRBRUSH_ENABLED = "PaintOpSettings/isAirbrushing";
/**
* Configuration property used to control airbrushing rate. The value should be in dabs per second.
*/
const QString AIRBRUSH_RATE = "PaintOpSettings/rate";
/**
* Configuration property used to control whether airbrushing is configured to ignore distance-based
* spacing.
*/
const QString AIRBRUSH_IGNORE_SPACING = "PaintOpSettings/ignoreSpacing";
/**
* Configuration property used to control whether the spacing settings can be updated between
* painted dabs.
*/
const QString SPACING_USE_UPDATES = "PaintOpSettings/updateSpacingBetweenDabs";
/**
* This class is used to cache the settings for a paintop
* between two creations. There is one KisPaintOpSettings per input device (mouse, tablet,
* etc...).
*
* The settings may be stored in a preset. Note that if your
* paintop's settings subclass has data that is not stored as a property, that data is not
* saved and restored.
*
* The object also contains a pointer to its parent KisPaintOpPreset object.This is to control the DirtyPreset
* property of KisPaintOpPreset. Whenever the settings are changed/modified from the original -- the preset is
* set to dirty.
*/
class KRITAIMAGE_EXPORT KisPaintOpSettings : public KisPropertiesConfiguration
{
public:
- KisPaintOpSettings();
+ KisPaintOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisPaintOpSettings() override;
KisPaintOpSettings(const KisPaintOpSettings &rhs);
/**
*
*/
void setOptionsWidget(KisPaintOpConfigWidget* widget);
/**
* This function is called by a tool when the mouse is pressed. It's useful if
* the paintop needs mouse interaction for instance in the case of the clone op.
* If the tool is supposed to ignore the event, the paint op should return true
* and if the tool is supposed to use the event, return false.
* See kis_tool_freehand:tryPickByPaintOp()
*/
virtual bool mousePressEvent(const KisPaintInformation &paintInformation, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode);
/**
* This function is called by a tool when the mouse is released. It's useful if
* the paintop needs mouse interaction for instance in the case of the clone op.
* If the tool is supposed to ignore the event, the paint op should return true
* and if the tool is supposed to use the event, return false.
*/
virtual bool mouseReleaseEvent();
/**
* Clone the current settings object. Override this if your settings instance doesn't
* store everything as properties.
*/
virtual KisPaintOpSettingsSP clone() const;
/**
* Removes all the settings from the object while keeping the paintop id,
* which is loaded to the object by the factory
*/
void resetSettings(const QStringList &preserveProperties = QStringList());
/**
* @return the node the paintop is working on.
*/
KisNodeSP node() const;
/**
* Call this function when the paint op is selected or the tool is activated
*/
virtual void activate();
/**
* XXX: Remove this after 2.0, when the paint operation (incremental/non incremental) will
* be completely handled in the paintop, not in the tool. This is a filthy hack to move
* the option to the right place, at least.
* @return true if we paint incrementally, false if we paint like Photoshop. By default, paintops
* do not support non-incremental.
*/
virtual bool paintIncremental() {
return true;
}
/**
* @return the composite op it to which the indirect painting device
* should be initialized to. This is used by clone op to reset
* the composite op to COMPOSITE_COPY
*/
virtual QString indirectPaintingCompositeOp() const;
/**
* Whether this paintop wants to deposit paint even when not moving, i.e. the tool needs to
* activate its timer. If this is true, painting updates need to be generated at regular
* intervals even in the absence of input device events, e.g. when the cursor is not moving.
*
* The default implementation checks the property AIRBRUSH_ENABLED, defaulting to false if the
* property is not found. This should be suitable for most paintops.
*/
virtual bool isAirbrushing() const;
/**
* Indicates the minimum time interval that might be needed between airbrush dabs, in
* milliseconds. A lower value means painting updates need to happen more frequently. This value
* should be ignored if isAirbrushing() is false.
*
* The default implementation uses the property AIRBRUSH_RATE, defaulting to an interval of
* one second if the property is not found. This should be suitable for most paintops.
*/
virtual qreal airbrushInterval() const;
/**
* Indicates whether this configuration allows spacing information to be updated between painted
* dabs during a stroke.
*/
virtual bool useSpacingUpdates() const;
/**
* Indicates if the tool should call paintOp->doAsynchronousUpdate() inbetween
* paintAt() calls to do the asynchronous rendering
*/
virtual bool needsAsynchronousUpdates() const;
/**
* This structure defines the current mode for painting an outline.
*/
struct OutlineMode {
bool isVisible = false;
bool forceCircle = false;
bool showTiltDecoration = false;
bool forceFullSize = false;
};
/**
* Returns the brush outline in pixel coordinates. Tool is responsible for conversion into view coordinates.
* Outline mode has to be passed to the paintop which builds the outline as some paintops have to paint outline
* always like clone paintop indicating the duplicate position
*/
virtual QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom);
/**
* Helpers for drawing the brush outline
*/
static QPainterPath ellipseOutline(qreal width, qreal height, qreal scale, qreal rotation);
/**
* Helper for drawing a triangle representing the tilt of the stylus.
*
* @param start is the offset from the brush's outline's bounding box
* @param lengthScale is used for deciding the size of the triangle.
* Brush diameter or width are common choices for this.
* @param angle is the angle between the two sides of the triangle.
*/
static QPainterPath makeTiltIndicator(KisPaintInformation const& info,
QPointF const& start, qreal lengthScale, qreal angle);
/**
* Set paintop opacity directly in the properties
*/
void setPaintOpOpacity(qreal value);
/**
* Set paintop flow directly in the properties
*/
void setPaintOpFlow(qreal value);
/**
* Set paintop composite mode directly in the properties
*/
void setPaintOpCompositeOp(const QString &value);
/**
* @return opacity saved in the properties
*/
qreal paintOpOpacity();
/**
* @return flow saved in the properties
*/
qreal paintOpFlow();
/**
* @return composite mode saved in the properties
*/
QString paintOpCompositeOp();
/**
* Set paintop size directly in the properties
*/
virtual void setPaintOpSize(qreal value) = 0;
/**
* @return size saved in the properties
*/
virtual qreal paintOpSize() const = 0;
void setEraserMode(bool value);
bool eraserMode();
qreal savedEraserSize() const;
void setSavedEraserSize(qreal value);
qreal savedBrushSize() const;
void setSavedBrushSize(qreal value);
qreal savedEraserOpacity() const;
void setSavedEraserOpacity(qreal value);
qreal savedBrushOpacity() const;
void setSavedBrushOpacity(qreal value);
QString effectivePaintOpCompositeOp();
- void setPreset(KisPaintOpPresetWSP preset);
+ void setUpdateProxy(const QPointer<KisPaintopSettingsUpdateProxy> proxy);
- KisPaintOpPresetWSP preset() const;
+ QPointer<KisPaintopSettingsUpdateProxy> updateProxy() const;
/**
* @return filename of the 3D brush model, empty if no brush is set
*/
virtual QString modelName() const;
/**
* Set filename of 3D brush model. By default no brush is set
*/
void setModelName(const QString & modelName);
/// Check if the settings are valid, setting might be invalid through missing brushes etc
/// Overwrite if the settings of a paintop can be invalid
/// @return state of the settings, default implementation is true
virtual bool isValid() const;
- /// Check if the settings are loadable, that might the case if we can fallback to something
- /// Overwrite if the settings can do some kind of fallback
- /// @return loadable state of the settings, by default implementation return the same as isValid()
- virtual bool isLoadable();
-
/**
* Overrides the method in KisPropertiesCofiguration to allow
* onPropertyChanged() callback
*/
void setProperty(const QString & name, const QVariant & value) override;
virtual QList<KisUniformPaintOpPropertySP> uniformProperties(KisPaintOpSettingsSP settings);
static bool isLodUserAllowed(const KisPropertiesConfigurationSP config);
static void setLodUserAllowed(KisPropertiesConfigurationSP config, bool value);
virtual bool lodSizeThresholdSupported() const;
qreal lodSizeThreshold() const;
void setLodSizeThreshold(qreal value);
/**
* @return the option widget of the paintop (can be 0 is no option widgets is set)
*/
KisPaintOpConfigWidget* optionsWidget() const;
/**
* This function is called to set random offsets to the brush whenever the mouse is clicked. It is
* specific to when the pattern option is set.
*
*/
virtual void setRandomOffset(const KisPaintInformation &paintInformation);
/**
* @return true if this preset demands a secondary masked brush running
* alongside it
*/
bool hasMaskingSettings() const;
/**
* @return a newly created settings object representing a preset of the masking
* brush that should be run alongside the current brush
*/
KisPaintOpSettingsSP createMaskingSettings() const;
/**
* @return a composite op id of the masked brush rendering algorithm.
*
* Please take into account that the brush itself always paints in alpha-
* darken mode, but the final result is combined with this composite op.
*/
QString maskingBrushCompositeOp() const;
+ /**
+ * @return resource interface that is used for loading linked resources
+ */
+ KisResourcesInterfaceSP resourcesInterface() const;
+
+ /**
+ * Set resource interface that will be used for loading linked resources
+ */
+ void setResourcesInterface(KisResourcesInterfaceSP resourcesInterface);
+
protected:
/**
* The callback is called every time when a property changes
*/
virtual void onPropertyChanged();
private:
struct Private;
const QScopedPointer<Private> d;
};
#endif
diff --git a/libs/image/brushengine/kis_paintop_settings_update_proxy.cpp b/libs/image/brushengine/kis_paintop_settings_update_proxy.cpp
index 532e6886a7..ed1df61c37 100644
--- a/libs/image/brushengine/kis_paintop_settings_update_proxy.cpp
+++ b/libs/image/brushengine/kis_paintop_settings_update_proxy.cpp
@@ -1,81 +1,93 @@
/*
* 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_paintop_settings_update_proxy.h"
#include "kis_signal_compressor.h"
+#include <kis_paintop_preset.h>
struct KisPaintopSettingsUpdateProxy::Private
{
Private()
: updatesCompressor(100, KisSignalCompressor::FIRST_ACTIVE),
updatesBlocked(0),
numUpdatesWhileBlocked(0)
{
}
KisSignalCompressor updatesCompressor;
int updatesBlocked;
int numUpdatesWhileBlocked;
};
KisPaintopSettingsUpdateProxy::KisPaintopSettingsUpdateProxy(QObject *parent)
: QObject(parent),
m_d(new Private)
{
connect(&m_d->updatesCompressor, SIGNAL(timeout()), SLOT(slotDeliverSettingsChanged()));
}
KisPaintopSettingsUpdateProxy::~KisPaintopSettingsUpdateProxy()
{
}
+void KisPaintopSettingsUpdateProxy::setDirty(bool dirty)
+{
+ ProxyParent *proxyParent = qobject_cast<ProxyParent*>(parent());
+ if (proxyParent){
+ KisPaintOpPreset *preset = proxyParent->m_preset;
+ if (preset) {
+ preset->setDirty(dirty);
+ }
+ }
+}
+
void KisPaintopSettingsUpdateProxy::notifySettingsChanged()
{
m_d->updatesCompressor.start();
}
void KisPaintopSettingsUpdateProxy::notifyUniformPropertiesChanged()
{
emit sigUniformPropertiesChanged();
}
void KisPaintopSettingsUpdateProxy::postponeSettingsChanges()
{
m_d->updatesBlocked++;
}
void KisPaintopSettingsUpdateProxy::unpostponeSettingsChanges()
{
m_d->updatesBlocked--;
if (!m_d->updatesBlocked && m_d->numUpdatesWhileBlocked) {
m_d->numUpdatesWhileBlocked = 0;
emit sigSettingsChanged();
}
}
void KisPaintopSettingsUpdateProxy::slotDeliverSettingsChanged()
{
if (m_d->updatesBlocked) {
m_d->numUpdatesWhileBlocked++;
} else {
emit sigSettingsChanged();
}
}
diff --git a/libs/image/brushengine/kis_paintop_settings_update_proxy.h b/libs/image/brushengine/kis_paintop_settings_update_proxy.h
index d44ec5933b..e5a0eb1d45 100644
--- a/libs/image/brushengine/kis_paintop_settings_update_proxy.h
+++ b/libs/image/brushengine/kis_paintop_settings_update_proxy.h
@@ -1,63 +1,67 @@
/*
* 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_PAINTOP_SETTINGS_UPDATE_PROXY_H
#define __KIS_PAINTOP_SETTINGS_UPDATE_PROXY_H
#include <QScopedPointer>
#include <QObject>
-
+/**
+ * @brief The KisPaintopSettingsUpdateProxy class
+ */
class KisPaintopSettingsUpdateProxy : public QObject
{
Q_OBJECT
public:
KisPaintopSettingsUpdateProxy(QObject *parent = 0);
~KisPaintopSettingsUpdateProxy() override;
+ void setDirty(bool dirty);
+
void notifySettingsChanged();
void notifyUniformPropertiesChanged();
/**
* Blocks all sigSettingsChanged() signals until unpostponeSettingsChanges()
* is called. Used to perform "atomic" writing operations.
*
* @see unpostponeSettingsChanges()
*/
void postponeSettingsChanges();
/**
* Unblocks sigSettingsChanged() and emits one signal if there were at least one
* dropped signal while the block was held.
*/
void unpostponeSettingsChanges();
Q_SIGNALS:
void sigSettingsChanged();
void sigUniformPropertiesChanged();
private Q_SLOTS:
void slotDeliverSettingsChanged();
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif /* __KIS_PAINTOP_SETTINGS_UPDATE_PROXY_H */
diff --git a/libs/image/commands/kis_change_filter_command.h b/libs/image/commands/kis_change_filter_command.h
index 0222792c8f..87bfb09141 100644
--- a/libs/image/commands/kis_change_filter_command.h
+++ b/libs/image/commands/kis_change_filter_command.h
@@ -1,96 +1,76 @@
/*
* Copyright (c) 2008 Boudewijn Rempt <boud@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_CHANGE_FILTER_COMMAND_H
#define KIS_CHANGE_FILTER_COMMAND_H
#include <kundo2command.h>
#include <QRect>
#include "kis_types.h"
#include <klocalizedstring.h>
#include "filter/kis_filter_configuration.h"
#include "kis_node.h"
#include "kis_node_filter_interface.h"
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter.h"
#include "generator/kis_generator_registry.h"
#include "generator/kis_generator.h"
class KisChangeFilterCmd : public KUndo2Command
{
public:
KisChangeFilterCmd(KisNodeSP node,
- const QString &filterNameBefore,
- const QString &xmlBefore,
- const QString &filterNameAfter,
- const QString &xmlAfter,
- bool useGeneratorRegistry)
+ KisFilterConfigurationSP configBefore,
+ KisFilterConfigurationSP configAfter)
: KUndo2Command(kundo2_i18n("Change Filter")) {
m_node = node;
m_filterInterface = dynamic_cast<KisNodeFilterInterface*>(node.data());
Q_ASSERT(m_filterInterface);
- m_useGeneratorRegistry = useGeneratorRegistry;
+ KIS_SAFE_ASSERT_RECOVER(configBefore->hasLocalResourcesSnapshot()) {
+ configBefore->createLocalResourcesSnapshot();
+ }
+
+ KIS_SAFE_ASSERT_RECOVER(configAfter->hasLocalResourcesSnapshot()) {
+ configAfter->createLocalResourcesSnapshot();
+ }
- m_xmlBefore = xmlBefore;
- m_xmlAfter = xmlAfter;
- m_filterNameBefore = filterNameBefore;
- m_filterNameAfter = filterNameAfter;
+ m_configBefore = configBefore;
+ m_configAfter = configAfter;
}
public:
void redo() override {
- m_filterInterface->setFilter(createConfiguration(m_filterNameAfter, m_xmlAfter));
+ m_filterInterface->setFilter(m_configAfter);
m_node->setDirty();
}
void undo() override {
- m_filterInterface->setFilter(createConfiguration(m_filterNameBefore, m_xmlBefore));
+ m_filterInterface->setFilter(m_configBefore);
m_node->setDirty();
}
-private:
- KisFilterConfigurationSP createConfiguration(const QString &name, const QString &data)
- {
- KisFilterConfigurationSP config;
-
- if (m_useGeneratorRegistry) {
- KisGeneratorSP generator = KisGeneratorRegistry::instance()->value(name);
- config = generator->factoryConfiguration();
- } else {
- KisFilterSP filter = KisFilterRegistry::instance()->value(name);
- config = filter->factoryConfiguration();
- }
-
- config->fromXML(data);
- return config;
-}
-
private:
KisNodeSP m_node;
KisNodeFilterInterface *m_filterInterface;
- bool m_useGeneratorRegistry;
-
- QString m_xmlBefore;
- QString m_xmlAfter;
- QString m_filterNameBefore;
- QString m_filterNameAfter;
+ KisFilterConfigurationSP m_configBefore;
+ KisFilterConfigurationSP m_configAfter;
};
#endif
diff --git a/libs/image/filter/kis_color_transformation_configuration.cc b/libs/image/filter/kis_color_transformation_configuration.cc
index 4c99ab9e89..e8a141b473 100644
--- a/libs/image/filter/kis_color_transformation_configuration.cc
+++ b/libs/image/filter/kis_color_transformation_configuration.cc
@@ -1,68 +1,73 @@
/*
* 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.
*/
#include "filter/kis_color_transformation_configuration.h"
#include <QMutexLocker>
#include <QMap>
#include <QThread>
#include "filter/kis_color_transformation_filter.h"
struct Q_DECL_HIDDEN KisColorTransformationConfiguration::Private {
Private()
{}
~Private()
{
qDeleteAll(colorTransformation);
}
// XXX: Threadlocal storage!!!
QMap<QThread*, KoColorTransformation*> colorTransformation;
QMutex mutex;
};
-KisColorTransformationConfiguration::KisColorTransformationConfiguration(const QString & name, qint32 version)
- : KisFilterConfiguration(name, version)
+KisColorTransformationConfiguration::KisColorTransformationConfiguration(const QString & name, qint32 version, KisResourcesInterfaceSP resourcesInterface)
+ : KisFilterConfiguration(name, version, resourcesInterface)
, d(new Private())
{
}
KisColorTransformationConfiguration::KisColorTransformationConfiguration(const KisColorTransformationConfiguration &rhs)
: KisFilterConfiguration(rhs)
, d(new Private())
{
}
KisColorTransformationConfiguration::~KisColorTransformationConfiguration()
{
delete d;
}
+KisFilterConfigurationSP KisColorTransformationConfiguration::clone() const
+{
+ return new KisColorTransformationConfiguration(*this);
+}
+
KoColorTransformation* KisColorTransformationConfiguration::colorTransformation(const KoColorSpace *cs, const KisColorTransformationFilter *filter) const
{
QMutexLocker locker(&d->mutex);
KoColorTransformation *transformation = d->colorTransformation.value(QThread::currentThread(), 0);
if (!transformation) {
KisFilterConfigurationSP config(const_cast<KisColorTransformationConfiguration*>(this));
transformation = filter->createTransformation(cs, config);
d->colorTransformation.insert(QThread::currentThread(), transformation);
}
locker.unlock();
return transformation;
}
diff --git a/libs/image/filter/kis_color_transformation_configuration.h b/libs/image/filter/kis_color_transformation_configuration.h
index ea10984ae1..2d2b96646a 100644
--- a/libs/image/filter/kis_color_transformation_configuration.h
+++ b/libs/image/filter/kis_color_transformation_configuration.h
@@ -1,45 +1,47 @@
/*
* 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.
*/
#ifndef _KIS_COLOR_TRANSFORMATION_CONFIGURATION_H_
#define _KIS_COLOR_TRANSFORMATION_CONFIGURATION_H_
#include "kis_filter_configuration.h"
#include "kritaimage_export.h"
class KoColorSpace;
class KisColorTransformationFilter;
class KisColorTransformationConfiguration;
typedef KisSharedPtr<KisColorTransformationConfiguration> KisColorTransformationConfigurationSP;
class KRITAIMAGE_EXPORT KisColorTransformationConfiguration : public KisFilterConfiguration
{
public:
- KisColorTransformationConfiguration(const QString & name, qint32 version);
+ KisColorTransformationConfiguration(const QString & name, qint32 version, KisResourcesInterfaceSP resourcesInterface);
KisColorTransformationConfiguration(const KisColorTransformationConfiguration &rhs);
~KisColorTransformationConfiguration() override;
+ KisFilterConfigurationSP clone() const override;
+
KoColorTransformation *colorTransformation(const KoColorSpace *cs, const KisColorTransformationFilter *filter) const;
private:
struct Private;
Private* const d;
};
#endif /* _KIS_COLOR_TRANSFORMATION_CONFIGURATION_H_ */
diff --git a/libs/image/filter/kis_color_transformation_filter.cc b/libs/image/filter/kis_color_transformation_filter.cc
index afe08dbe2a..8e0877b769 100644
--- a/libs/image/filter/kis_color_transformation_filter.cc
+++ b/libs/image/filter/kis_color_transformation_filter.cc
@@ -1,81 +1,81 @@
/*
* Copyright (c) 2004, 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 "kis_color_transformation_filter.h"
#include <KoColorTransformation.h>
#include <KoUpdater.h>
#include <kis_processing_information.h>
#include <kis_paint_device.h>
#include <kis_selection.h>
#ifndef NDEBUG
#include <QTime>
#endif
#include <KisSequentialIteratorProgress.h>
#include "kis_color_transformation_configuration.h"
KisColorTransformationFilter::KisColorTransformationFilter(const KoID& id, const KoID & category, const QString & entry) : KisFilter(id, category, entry)
{
setSupportsLevelOfDetail(true);
}
KisColorTransformationFilter::~KisColorTransformationFilter()
{
}
void KisColorTransformationFilter::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
Q_ASSERT(!device.isNull());
const KoColorSpace * cs = device->colorSpace();
KoColorTransformation * colorTransformation = 0;
// Ew, casting
KisColorTransformationConfigurationSP colorTransformationConfiguration(dynamic_cast<KisColorTransformationConfiguration*>(const_cast<KisFilterConfiguration*>(config.data())));
if (colorTransformationConfiguration) {
colorTransformation = colorTransformationConfiguration->colorTransformation(cs, this);
}
else {
colorTransformation = createTransformation(cs, config);
}
if (!colorTransformation) return;
KisSequentialIteratorProgress it(device, applyRect, progressUpdater);
int conseq = it.nConseqPixels();
while (it.nextPixels(conseq)) {
conseq = it.nConseqPixels();
colorTransformation->transform(it.oldRawData(), it.rawData(), conseq);
}
if (!colorTransformationConfiguration) {
delete colorTransformation;
}
}
-KisFilterConfigurationSP KisColorTransformationFilter::factoryConfiguration() const
+KisFilterConfigurationSP KisColorTransformationFilter::factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- return new KisColorTransformationConfiguration(id(), 0);
+ return new KisColorTransformationConfiguration(id(), 0, resourcesInterface);
}
diff --git a/libs/image/filter/kis_color_transformation_filter.h b/libs/image/filter/kis_color_transformation_filter.h
index d495c004cd..09dcf7f1de 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
* 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;
+ KisFilterConfigurationSP factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
};
#endif
diff --git a/libs/image/filter/kis_filter.cc b/libs/image/filter/kis_filter.cc
index 266001ddd9..64a97277dc 100644
--- a/libs/image/filter/kis_filter.cc
+++ b/libs/image/filter/kis_filter.cc
@@ -1,144 +1,146 @@
/*
* 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.
*/
#include "filter/kis_filter.h"
#include <QString>
#include <KoCompositeOpRegistry.h>
#include "kis_bookmarked_configuration_manager.h"
#include "filter/kis_filter_configuration.h"
#include "kis_processing_information.h"
#include "kis_transaction.h"
#include "kis_paint_device.h"
#include "kis_selection.h"
#include "kis_types.h"
#include <kis_painter.h>
#include <KoUpdater.h>
KisFilter::KisFilter(const KoID& _id, const KoID & category, const QString & entry)
: KisBaseProcessor(_id, category, entry),
m_supportsLevelOfDetail(false)
{
init(id() + "_filter_bookmarks");
}
KisFilter::~KisFilter()
{
}
void KisFilter::process(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater) const
{
process(device, device, KisSelectionSP(), applyRect, config, progressUpdater);
}
void KisFilter::process(const KisPaintDeviceSP src,
KisPaintDeviceSP dst,
KisSelectionSP selection,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater ) const
{
+ KIS_SAFE_ASSERT_RECOVER_NOOP(config->hasLocalResourcesSnapshot());
+
if (applyRect.isEmpty()) return;
QRect needRect = neededRect(applyRect, config, src->defaultBounds()->currentLevelOfDetail());
KisPaintDeviceSP temporary;
KisTransaction *transaction = 0;
bool weirdDstColorSpace =
dst->colorSpace() != dst->compositionSourceColorSpace() &&
*dst->colorSpace() != *dst->compositionSourceColorSpace();
if(src == dst && !selection && !weirdDstColorSpace) {
temporary = src;
}
else {
temporary = dst->createCompositionSourceDevice(src, needRect);
transaction = new KisTransaction(temporary);
}
try {
QScopedPointer<KoUpdater> fakeUpdater;
if (!progressUpdater) {
// TODO: remove dependency on KoUpdater, depend on KoProgressProxy,
// it is more lightweight
fakeUpdater.reset(new KoDummyUpdater());
progressUpdater = fakeUpdater.data();
}
processImpl(temporary, applyRect, config, progressUpdater);
}
catch (const std::bad_alloc&) {
warnKrita << "Filter" << name() << "failed to allocate enough memory to run.";
}
if(transaction) {
delete transaction;
KisPainter::copyAreaOptimized(applyRect.topLeft(), temporary, dst, applyRect, selection);
}
}
QRect KisFilter::neededRect(const QRect & rect, const KisFilterConfigurationSP c, int lod) const
{
Q_UNUSED(c);
Q_UNUSED(lod);
return rect;
}
QRect KisFilter::changedRect(const QRect & rect, const KisFilterConfigurationSP c, int lod) const
{
Q_UNUSED(c);
Q_UNUSED(lod);
return rect;
}
bool KisFilter::supportsLevelOfDetail(const KisFilterConfigurationSP config, int lod) const
{
Q_UNUSED(config);
Q_UNUSED(lod);
return m_supportsLevelOfDetail;
}
void KisFilter::setSupportsLevelOfDetail(bool value)
{
m_supportsLevelOfDetail = value;
}
bool KisFilter::needsTransparentPixels(const KisFilterConfigurationSP config, const KoColorSpace *cs) const
{
Q_UNUSED(config);
Q_UNUSED(cs);
return false;
}
bool KisFilter::configurationAllowedForMask(KisFilterConfigurationSP config) const
{
Q_UNUSED(config);
return supportsAdjustmentLayers();
}
void KisFilter::fixLoadedFilterConfigurationForMasks(KisFilterConfigurationSP config) const
{
Q_UNUSED(config);
}
diff --git a/libs/image/filter/kis_filter_configuration.cc b/libs/image/filter/kis_filter_configuration.cc
index e9e4d1204f..5d9bb2832f 100644
--- a/libs/image/filter/kis_filter_configuration.cc
+++ b/libs/image/filter/kis_filter_configuration.cc
@@ -1,173 +1,249 @@
/*
* 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 "filter/kis_filter_configuration.h"
#include "filter/kis_filter.h"
#include <kis_debug.h>
#include <QDomDocument>
#include <QString>
#include "filter/kis_filter_registry.h"
#include "kis_transaction.h"
#include "kis_undo_adapter.h"
#include "kis_painter.h"
#include "kis_selection.h"
#include "KoID.h"
#include "kis_types.h"
+#include <KisRequiredResourcesOperators.h>
#include "kis_config_widget.h"
struct Q_DECL_HIDDEN KisFilterConfiguration::Private {
QString name;
qint32 version;
QBitArray channelFlags;
KisCubicCurve curve;
QList< KisCubicCurve > curves;
+ KisResourcesInterfaceSP resourcesInterface = 0;
+
+ Private(const QString & _name, qint32 _version, KisResourcesInterfaceSP _resourcesInterface)
+ : name(_name),
+ version(_version),
+ resourcesInterface(_resourcesInterface)
+ {
+ }
+
+ Private(const Private &rhs)
+ : name(rhs.name),
+ version(rhs.version),
+ channelFlags(rhs.channelFlags),
+ curve(rhs.curve),
+ curves(rhs.curves),
+ resourcesInterface(rhs.resourcesInterface)
+ {
+ }
#ifdef SANITY_CHECK_FILTER_CONFIGURATION_OWNER
QAtomicInt sanityUsageCounter;
#endif /* SANITY_CHECK_FILTER_CONFIGURATION_OWNER */
};
-KisFilterConfiguration::KisFilterConfiguration(const QString & name, qint32 version)
- : d(new Private)
+KisFilterConfiguration::KisFilterConfiguration(const QString & name, qint32 version, KisResourcesInterfaceSP resourcesInterface)
+ : d(new Private(name, version, resourcesInterface))
{
- d->name = name;
- d->version = version;
- d->channelFlags = QBitArray();
+}
+
+KisFilterConfigurationSP KisFilterConfiguration::clone() const
+{
+ return new KisFilterConfiguration(*this);
}
KisFilterConfiguration::KisFilterConfiguration(const KisFilterConfiguration & rhs)
: KisPropertiesConfiguration(rhs)
- , d(new Private)
+ , d(new Private(*rhs.d))
{
- d->name = rhs.d->name;
- d->version = rhs.d->version;
}
KisFilterConfiguration::~KisFilterConfiguration()
{
delete d;
}
void KisFilterConfiguration::fromLegacyXML(const QDomElement& e)
{
clearProperties();
d->name = e.attribute("name");
d->version = e.attribute("version").toInt();
QDomNode n = e.firstChild();
while (!n.isNull()) {
// We don't nest elements in filter configuration. For now...
QDomElement e = n.toElement();
QString name;
QString type;
QString value;
if (!e.isNull()) {
if (e.tagName() == "property") {
name = e.attribute("name");
type = e.attribute("type");
value = e.text();
// XXX Convert the variant pro-actively to the right type?
setProperty(name, QVariant(value));
}
}
n = n.nextSibling();
}
}
void KisFilterConfiguration::fromXML(const QDomElement& elt)
{
d->version = elt.attribute("version").toInt();
KisPropertiesConfiguration::fromXML(elt);
}
void KisFilterConfiguration::toXML(QDomDocument& doc, QDomElement& elt) const
{
elt.setAttribute("version", d->version);
KisPropertiesConfiguration::toXML(doc, elt);
}
const QString & KisFilterConfiguration::name() const
{
return d->name;
}
qint32 KisFilterConfiguration::version() const
{
return d->version;
}
void KisFilterConfiguration::setVersion(qint32 version)
{
d->version = version;
}
const KisCubicCurve& KisFilterConfiguration::curve() const
{
return d->curve;
}
void KisFilterConfiguration::setCurve(const KisCubicCurve& curve)
{
d->curve = curve;
}
const QList< KisCubicCurve >& KisFilterConfiguration::curves() const
{
return d->curves;
}
+KisResourcesInterfaceSP KisFilterConfiguration::resourcesInterface() const
+{
+ return d->resourcesInterface;
+}
+
+void KisFilterConfiguration::setResourcesInterface(KisResourcesInterfaceSP resourcesInterface)
+{
+ d->resourcesInterface = resourcesInterface;
+}
+
+namespace KisRequiredResourcesOperators
+{
+template <>
+struct ResourceTraits<KisFilterConfiguration>
+{
+ template <typename T>
+ using SharedPointerType = KisPinnedSharedPtr<T>;
+
+ template <typename D, typename S>
+ static inline SharedPointerType<D> dynamicCastSP(SharedPointerType<S> src) {
+ return SharedPointerType<D>(dynamic_cast<D*>(src.data()));
+ }
+};
+}
+
+void KisFilterConfiguration::createLocalResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface)
+{
+ KisRequiredResourcesOperators::createLocalResourcesSnapshot(this, globalResourcesInterface);
+}
+
+bool KisFilterConfiguration::hasLocalResourcesSnapshot() const
+{
+ return KisRequiredResourcesOperators::hasLocalResourcesSnapshot(this);
+}
+
+KisFilterConfigurationSP KisFilterConfiguration::cloneWithResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface) const
+{
+ return KisRequiredResourcesOperators::cloneWithResourcesSnapshot(this, globalResourcesInterface);
+}
+
+QList<KoResourceSP> KisFilterConfiguration::requiredResources(KisResourcesInterfaceSP globalResourcesInterface) const
+{
+ return linkedResources(globalResourcesInterface) + embeddedResources(globalResourcesInterface);
+}
+
+QList<KoResourceSP> KisFilterConfiguration::linkedResources(KisResourcesInterfaceSP globalResourcesInterface) const
+{
+ Q_UNUSED(globalResourcesInterface);
+ return {};
+}
+
+QList<KoResourceSP> KisFilterConfiguration::embeddedResources(KisResourcesInterfaceSP globalResourcesInterface) const
+{
+ Q_UNUSED(globalResourcesInterface);
+ return {};
+}
+
void KisFilterConfiguration::setCurves(QList< KisCubicCurve >& curves)
{
d->curves = curves;
}
bool KisFilterConfiguration::isCompatible(const KisPaintDeviceSP) const
{
return true;
}
QBitArray KisFilterConfiguration::channelFlags() const
{
return d->channelFlags;
}
void KisFilterConfiguration::setChannelFlags(QBitArray channelFlags)
{
d->channelFlags = channelFlags;
}
#ifdef SANITY_CHECK_FILTER_CONFIGURATION_OWNER
int KisFilterConfiguration::sanityRefUsageCounter()
{
return d->sanityUsageCounter.ref();
}
int KisFilterConfiguration::sanityDerefUsageCounter()
{
return d->sanityUsageCounter.deref();
}
#endif /* SANITY_CHECK_FILTER_CONFIGURATION_OWNER */
diff --git a/libs/image/filter/kis_filter_configuration.h b/libs/image/filter/kis_filter_configuration.h
index 3b4196826f..bfb41281d5 100644
--- a/libs/image/filter/kis_filter_configuration.h
+++ b/libs/image/filter/kis_filter_configuration.h
@@ -1,139 +1,204 @@
/*
* 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_FILTER_CONFIGURATION_H_
#define _KIS_FILTER_CONFIGURATION_H_
#include <QMetaType>
#include "kis_properties_configuration.h"
#include "kis_types.h"
#include "kritaimage_export.h"
+
+class KoResource;
+typedef QSharedPointer<KoResource> KoResourceSP;
+
+class KisResourcesInterface;
+typedef QSharedPointer<KisResourcesInterface> KisResourcesInterfaceSP;
+
/**
* KisFilterConfiguration does inherit neither KisShared or QSharedData
* so sometimes there might be problem with broken QSharedPointer counters.
* This macro activates debugging routines for such stuff.
*
* In the future, please either port the entire KisNodeFilterInterface
* into KisFilterConfigurationSP or derive filter configuration
* interface from QSharedData to handle these cases.
*/
#define SANITY_CHECK_FILTER_CONFIGURATION_OWNER
/**
* A KisFilterConfiguration is the serializable representation of
* the filter parameters. Filters can subclass this class to implement
* direct accessors to properties, but properties not in the map will
* not be serialized.
*
* XXX: Use KoProperties here!
*/
class KRITAIMAGE_EXPORT KisFilterConfiguration : public KisPropertiesConfiguration
{
-
public:
/**
* Create a new filter config.
*/
- KisFilterConfiguration(const QString & name, qint32 version);
+ KisFilterConfiguration(const QString & name, qint32 version, KisResourcesInterfaceSP resourcesInterface);
+
+ /**
+ * @return an exact copy of the filter configuration. Resources interface is
+ * becomes shared between two configuration objects.
+ */
+ virtual KisFilterConfigurationSP clone() const;
protected:
/**
* Deep copy the filter configFile
*/
KisFilterConfiguration(const KisFilterConfiguration & rhs);
public:
~KisFilterConfiguration() override;
public:
/**
* This function is use to convert from legacy XML as used in .kra file.
*/
virtual void fromLegacyXML(const QDomElement&);
using KisPropertiesConfiguration::fromXML;
using KisPropertiesConfiguration::toXML;
void fromXML(const QDomElement&) override;
void toXML(QDomDocument&, QDomElement&) const override;
/**
* Get the unique, language independent name of the filter.
*/
const QString & name() const;
/**
* Get the version of the filter that has created this config
*/
qint32 version() const;
/**
* Check if that configuration is compatible with this paint device.
* The default implementation always return true.
*/
virtual bool isCompatible(const KisPaintDeviceSP) const;
/**
* @return an array with each colorspace channel a true/false bit
* that indicates whether the channel should be filtered or left
* alone. It is up to the filter to decide whether channels that
* are to be left alone are copied to the dest file or not.
*/
QBitArray channelFlags() const;
/**
* Set the channel flags. An empty array is allowed; that means
* that all channels are to be filtered. Filters can optimize on
* that. The array must be in the order of the pixel layout.
*/
void setChannelFlags(QBitArray channelFlags);
/**
* These functions exist solely to allow plugins to reimplement them as
* needed, while allowing consumers to implement support for them without
* linking directly to the plugin. In particular, the filter management
* in Sketch requires this.
*/
virtual void setCurve(const KisCubicCurve &curve);
virtual const KisCubicCurve& curve() const;
virtual void setCurves(QList<KisCubicCurve> &curves);
virtual const QList<KisCubicCurve>& curves() const;
+ /**
+ * @return resource interface that is used by KisFilterConfiguration object for
+ * loading linked resources
+ */
+ KisResourcesInterfaceSP resourcesInterface() const;
+
+ /**
+ * Set resource interface that will be used by KisFilterConfiguration object for
+ * loading linked resources
+ */
+ void setResourcesInterface(KisResourcesInterfaceSP resourcesInterface);
+
+ /**
+ * \see KisRequiredResourcesOperators::createLocalResourcesSnapshot
+ */
+ void createLocalResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface = nullptr);
+
+ /**
+ * \see KisRequiredResourcesOperators::hasLocalResourcesSnapshot
+ */
+ bool hasLocalResourcesSnapshot() const;
+
+ /**
+ * \see KisRequiredResourcesOperators::cloneWithResourcesSnapshot
+ */
+ KisFilterConfigurationSP cloneWithResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface = nullptr) const;
+
+ /**
+ * Loads all the required resources either from \p globalResourcesInterface or
+ * from embedded data. The filter first tries to fetch the required resource
+ * from the global source, and only if it fails, tries to load it from the
+ * embedded data. One can check if the loaded resource is embedded by checking
+ * its resourceId().
+ *
+ * The set of resources returned is basically: linkedResources() + embeddedResources()
+ */
+ QList<KoResourceSP> requiredResources(KisResourcesInterfaceSP globalResourcesInterface) const;
+
+ /**
+ * @return all the resources that are needed but (*this) filter and
+ * are not embedded into it. The resources are fetched from
+ * \p globalResourcesInterface. If fetching of some resources is failed,
+ * then (*this) filter is invalid.
+ */
+ virtual QList<KoResourceSP> linkedResources(KisResourcesInterfaceSP globalResourcesInterface) const;
+
+ /**
+ * @return all the resources that were embedded into (*this) filter.
+ * If the resources were already added to the global database, then they
+ * are fetched from \p globalResourcesInterface to save time/memory.
+ */
+ virtual QList<KoResourceSP> embeddedResources(KisResourcesInterfaceSP globalResourcesInterface) const;
+
+
#ifdef SANITY_CHECK_FILTER_CONFIGURATION_OWNER
private:
friend class KisNodeFilterInterface;
int sanityRefUsageCounter();
int sanityDerefUsageCounter();
#endif /* SANITY_CHECK_FILTER_CONFIGURATION_OWNER */
protected:
void setVersion(qint32 version);
private:
struct Private;
Private* const d;
};
Q_DECLARE_METATYPE(KisFilterConfiguration*)
-
#endif // _KIS_FILTER_CONFIGURATION_H_
diff --git a/libs/image/filter/kis_filter_registry.cc b/libs/image/filter/kis_filter_registry.cc
index 96c98f3562..7ade8493c0 100644
--- a/libs/image/filter/kis_filter_registry.cc
+++ b/libs/image/filter/kis_filter_registry.cc
@@ -1,82 +1,74 @@
/*
* Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2004-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.
*/
#include "filter/kis_filter_registry.h"
#include <math.h>
#include <QString>
#include <QApplication>
#include <klocalizedstring.h>
#include <KoPluginLoader.h>
#include "kis_debug.h"
#include "kis_types.h"
#include "kis_paint_device.h"
#include "filter/kis_filter.h"
#include "kis_filter_configuration.h"
KisFilterRegistry::KisFilterRegistry(QObject *parent)
: QObject(parent)
{
}
KisFilterRegistry::~KisFilterRegistry()
{
dbgRegistry << "deleting KisFilterRegistry";
Q_FOREACH (KisFilterSP filter, values()) {
remove(filter->id());
filter.clear();
}
}
KisFilterRegistry* KisFilterRegistry::instance()
{
KisFilterRegistry *reg = qApp->findChild<KisFilterRegistry *>(QString());
if (!reg) {
dbgRegistry << "initializing KisFilterRegistry";
reg = new KisFilterRegistry(qApp);
KoPluginLoader::instance()->load("Krita/Filter", "Type == 'Service' and ([X-Krita-Version] == 28)");
}
return reg;
}
void KisFilterRegistry::add(KisFilterSP item)
{
add(item->id(), item);
}
void KisFilterRegistry::add(const QString &id, KisFilterSP item)
{
KoGenericRegistry<KisFilterSP>::add(id, item);
emit(filterAdded(id));
}
-KisFilterConfigurationSP KisFilterRegistry::cloneConfiguration(const KisFilterConfigurationSP kfc)
-{
- Q_ASSERT(kfc);
- KisFilterSP filter = value(kfc->name());
- KisFilterConfigurationSP newkfc(filter->factoryConfiguration());
- newkfc->fromXML(kfc->toXML());
- return newkfc;
-}
diff --git a/libs/image/filter/kis_filter_registry.h b/libs/image/filter/kis_filter_registry.h
index ed95db5720..df4b62c932 100644
--- a/libs/image/filter/kis_filter_registry.h
+++ b/libs/image/filter/kis_filter_registry.h
@@ -1,60 +1,59 @@
/*
* Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 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_REGISTRY_H_
#define KIS_FILTER_REGISTRY_H_
#include <QObject>
#include "kis_filter.h"
#include "kis_types.h"
#include "KoGenericRegistry.h"
#include <kritaimage_export.h>
class QString;
class KisFilterConfiguration;
class KRITAIMAGE_EXPORT KisFilterRegistry : public QObject, public KoGenericRegistry<KisFilterSP>
{
Q_OBJECT
public:
~KisFilterRegistry() override;
static KisFilterRegistry* instance();
void add(KisFilterSP item);
void add(const QString &id, KisFilterSP item);
- KisFilterConfigurationSP cloneConfiguration(const KisFilterConfigurationSP);
Q_SIGNALS:
void filterAdded(QString id);
private:
KisFilterRegistry(QObject *parent);
KisFilterRegistry(const KisFilterRegistry&);
KisFilterRegistry operator=(const KisFilterRegistry&);
};
#endif // KIS_FILTERSPACE_REGISTRY_H_
diff --git a/libs/image/generator/kis_generator_layer.cpp b/libs/image/generator/kis_generator_layer.cpp
index 48b821b351..d4d248552f 100644
--- a/libs/image/generator/kis_generator_layer.cpp
+++ b/libs/image/generator/kis_generator_layer.cpp
@@ -1,189 +1,189 @@
/*
* 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.
*/
#include "kis_generator_layer.h"
#include <klocalizedstring.h>
#include "kis_debug.h"
#include <KoIcon.h>
#include <kis_icon.h>
#include "kis_selection.h"
#include "filter/kis_filter_configuration.h"
#include "kis_processing_information.h"
#include "generator/kis_generator_registry.h"
#include "generator/kis_generator.h"
#include "kis_node_visitor.h"
#include "kis_processing_visitor.h"
#include "kis_thread_safe_signal_compressor.h"
#include "kis_recalculate_generator_layer_job.h"
#define UPDATE_DELAY 100 /*ms */
struct Q_DECL_HIDDEN KisGeneratorLayer::Private
{
Private()
: updateSignalCompressor(UPDATE_DELAY, KisSignalCompressor::FIRST_INACTIVE)
{
}
KisThreadSafeSignalCompressor updateSignalCompressor;
QRect preparedRect;
KisFilterConfigurationSP preparedForFilter;
};
KisGeneratorLayer::KisGeneratorLayer(KisImageWSP image,
const QString &name,
KisFilterConfigurationSP kfc,
KisSelectionSP selection)
- : KisSelectionBasedLayer(image, name, selection, kfc, true),
+ : KisSelectionBasedLayer(image, name, selection, kfc),
m_d(new Private)
{
connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate()));
}
KisGeneratorLayer::KisGeneratorLayer(const KisGeneratorLayer& rhs)
: KisSelectionBasedLayer(rhs),
m_d(new Private)
{
connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate()));
}
KisGeneratorLayer::~KisGeneratorLayer()
{
}
void KisGeneratorLayer::setFilter(KisFilterConfigurationSP filterConfig)
{
KisSelectionBasedLayer::setFilter(filterConfig);
m_d->preparedRect = QRect();
update();
}
void KisGeneratorLayer::slotDelayedStaticUpdate()
{
/**
* The mask might have been deleted from the layers stack in the
* meanwhile. Just ignore the updates in the case.
*/
KisLayerSP parentLayer(qobject_cast<KisLayer*>(parent().data()));
if (!parentLayer) return;
KisImageSP image = parentLayer->image();
if (image) {
image->addSpontaneousJob(new KisRecalculateGeneratorLayerJob(KisGeneratorLayerSP(this)));
}
}
void KisGeneratorLayer::update()
{
KisImageSP image = this->image().toStrongRef();
const QRect updateRect = extent() | image->bounds();
KisFilterConfigurationSP filterConfig = filter();
KIS_SAFE_ASSERT_RECOVER_RETURN(filterConfig);
if (filterConfig != m_d->preparedForFilter) {
resetCache();
}
const QRegion processRegion(QRegion(updateRect) - m_d->preparedRect);
if (processRegion.isEmpty()) return;
KisGeneratorSP f = KisGeneratorRegistry::instance()->value(filterConfig->name());
KIS_SAFE_ASSERT_RECOVER_RETURN(f);
KisPaintDeviceSP originalDevice = original();
QVector<QRect> dirtyRegion;
auto rc = processRegion.begin();
while (rc != processRegion.end()) {
KisProcessingInformation dstCfg(originalDevice,
rc->topLeft(),
KisSelectionSP());
f->generate(dstCfg, rc->size(), filterConfig.data());
dirtyRegion << *rc;
rc++;
}
m_d->preparedRect = updateRect;
m_d->preparedForFilter = filterConfig;
// HACK ALERT!!!
// this avoids cyclic loop with KisRecalculateGeneratorLayerJob::run()
KisSelectionBasedLayer::setDirty(dirtyRegion);
}
bool KisGeneratorLayer::accept(KisNodeVisitor & v)
{
return v.visit(this);
}
void KisGeneratorLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
{
return visitor.visit(this, undoAdapter);
}
QIcon KisGeneratorLayer::icon() const
{
return KisIconUtils::loadIcon("fillLayer");
}
KisBaseNode::PropertyList KisGeneratorLayer::sectionModelProperties() const
{
KisFilterConfigurationSP filterConfig = filter();
KisBaseNode::PropertyList l = KisLayer::sectionModelProperties();
l << KisBaseNode::Property(KoID("generator", i18n("Generator")),
KisGeneratorRegistry::instance()->value(filterConfig->name())->name());
return l;
}
void KisGeneratorLayer::setX(qint32 x)
{
KisSelectionBasedLayer::setX(x);
m_d->preparedRect = QRect();
m_d->updateSignalCompressor.start();
}
void KisGeneratorLayer::setY(qint32 y)
{
KisSelectionBasedLayer::setY(y);
m_d->preparedRect = QRect();
m_d->updateSignalCompressor.start();
}
void KisGeneratorLayer::resetCache()
{
KisSelectionBasedLayer::resetCache();
m_d->preparedRect = QRect();
m_d->updateSignalCompressor.start();
}
void KisGeneratorLayer::setDirty(const QVector<QRect> &rects)
{
KisSelectionBasedLayer::setDirty(rects);
m_d->updateSignalCompressor.start();
}
diff --git a/libs/image/generator/kis_generator_registry.cpp b/libs/image/generator/kis_generator_registry.cpp
index dc48bf8b71..14f9d5211e 100644
--- a/libs/image/generator/kis_generator_registry.cpp
+++ b/libs/image/generator/kis_generator_registry.cpp
@@ -1,81 +1,73 @@
/*
* 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.
*/
#include "generator/kis_generator_registry.h"
#include <math.h>
#include <QString>
#include <QApplication>
#include <klocalizedstring.h>
#include <KoPluginLoader.h>
#include "filter/kis_filter_configuration.h"
#include "kis_debug.h"
#include "kis_types.h"
#include "kis_paint_device.h"
#include "generator/kis_generator.h"
KisGeneratorRegistry::KisGeneratorRegistry(QObject *parent)
: QObject(parent)
{
}
KisGeneratorRegistry::~KisGeneratorRegistry()
{
Q_FOREACH (KisGeneratorSP generator, values()) {
remove(generator->id());
generator.clear();
}
dbgRegistry << "deleting KisGeneratorRegistry";
}
KisGeneratorRegistry* KisGeneratorRegistry::instance()
{
KisGeneratorRegistry *reg = qApp->findChild<KisGeneratorRegistry *>(QString());
if (!reg) {
dbgRegistry << "initializing KisGeneratorRegistry";
reg = new KisGeneratorRegistry(qApp);
KoPluginLoader::instance()->load("Krita/Generator", "Type == 'Service' and ([X-Krita-Version] == 28)");
}
return reg;
}
void KisGeneratorRegistry::add(KisGeneratorSP item)
{
dbgPlugins << "adding " << item->name();
add(item->id(), item);
}
void KisGeneratorRegistry::add(const QString &id, KisGeneratorSP item)
{
dbgPlugins << "adding " << item->name() << " with id " << id;
KoGenericRegistry<KisGeneratorSP>::add(id, item);
emit(generatorAdded(id));
}
-KisFilterConfigurationSP KisGeneratorRegistry::cloneConfiguration(const KisFilterConfigurationSP kfc)
-{
- KisGeneratorSP filter = value(kfc->name());
- KisFilterConfigurationSP newkfc(filter->factoryConfiguration());
- newkfc->fromXML(kfc->toXML());
- return newkfc;
-}
-
diff --git a/libs/image/generator/kis_generator_registry.h b/libs/image/generator/kis_generator_registry.h
index 6d92841bce..5209ce25d6 100644
--- a/libs/image/generator/kis_generator_registry.h
+++ b/libs/image/generator/kis_generator_registry.h
@@ -1,60 +1,59 @@
/*
* 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_REGISTRY_H_
#define KIS_GENERATOR_REGISTRY_H_
#include <QObject>
#include "kis_generator.h"
#include "kis_types.h"
#include "KoGenericRegistry.h"
#include <kritaimage_export.h>
class QString;
class KisFilterConfiguration;
/**
* XXX_DOCS
*/
class KRITAIMAGE_EXPORT KisGeneratorRegistry : public QObject, public KoGenericRegistry<KisGeneratorSP>
{
Q_OBJECT
public:
~KisGeneratorRegistry() override;
static KisGeneratorRegistry* instance();
void add(KisGeneratorSP item);
void add(const QString &id, KisGeneratorSP item);
- KisFilterConfigurationSP cloneConfiguration(const KisFilterConfigurationSP kfc);
Q_SIGNALS:
void generatorAdded(QString id);
private:
KisGeneratorRegistry(QObject *parent);
KisGeneratorRegistry(const KisGeneratorRegistry&);
KisGeneratorRegistry operator=(const KisGeneratorRegistry&);
};
#endif // KIS_GENERATOR_REGISTRY_H_
diff --git a/libs/image/kis_asl_layer_style_serializer.cpp b/libs/image/kis_asl_layer_style_serializer.cpp
new file mode 100644
index 0000000000..a02df3955a
--- /dev/null
+++ b/libs/image/kis_asl_layer_style_serializer.cpp
@@ -0,0 +1,1273 @@
+/*
+ * 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_asl_layer_style_serializer.h"
+
+#include <QDomDocument>
+
+#include <KoResourceServerProvider.h>
+#include <resources/KoAbstractGradient.h>
+#include <resources/KoSegmentGradient.h>
+#include <resources/KoStopGradient.h>
+#include <resources/KoPattern.h>
+
+#include "kis_dom_utils.h"
+
+
+#include "psd.h"
+#include "kis_global.h"
+
+#include "asl/kis_asl_reader.h"
+#include "asl/kis_asl_xml_parser.h"
+#include "asl/kis_asl_writer_utils.h"
+
+#include "asl/kis_asl_xml_writer.h"
+#include "asl/kis_asl_writer.h"
+
+#include <functional>
+using namespace std::placeholders;
+
+KisAslLayerStyleSerializer::KisAslLayerStyleSerializer()
+{
+}
+
+KisAslLayerStyleSerializer::~KisAslLayerStyleSerializer()
+{
+}
+
+QVector<KisPSDLayerStyleSP> KisAslLayerStyleSerializer::styles() const
+{
+ return m_stylesVector;
+}
+
+void KisAslLayerStyleSerializer::setStyles(const QVector<KisPSDLayerStyleSP> &styles)
+{
+ m_stylesVector = styles;
+ Q_FOREACH(const KisPSDLayerStyleSP style, styles) {
+ m_stylesHash.insert(style->psdUuid(), style);
+ }
+ m_initialized = true;
+}
+
+QHash<QString, KoPatternSP> KisAslLayerStyleSerializer::patterns() const
+{
+ return m_patternsStore;
+}
+
+QHash<QString, KisPSDLayerStyleSP> KisAslLayerStyleSerializer::stylesHash()
+{
+ if (m_stylesHash.count() == 0 && m_stylesVector.count() != 0) {
+ // build the hash
+ Q_FOREACH(KisPSDLayerStyleSP style, m_stylesVector) {
+ m_stylesHash.insert(style->psdUuid(), style);
+ }
+ }
+ return m_stylesHash;
+}
+
+QString compositeOpToBlendMode(const QString &compositeOp)
+{
+ QString mode = "Nrml";
+
+ if (compositeOp == COMPOSITE_OVER) {
+ mode = "Nrml";
+ } else if (compositeOp == COMPOSITE_DISSOLVE) {
+ mode = "Dslv";
+ } else if (compositeOp == COMPOSITE_DARKEN) {
+ mode = "Drkn";
+ } else if (compositeOp == COMPOSITE_MULT) {
+ mode = "Mltp";
+ } else if (compositeOp == COMPOSITE_BURN) {
+ mode = "CBrn";
+ } else if (compositeOp == COMPOSITE_LINEAR_BURN) {
+ mode = "linearBurn";
+ } else if (compositeOp == COMPOSITE_DARKER_COLOR) {
+ mode = "darkerColor";
+ } else if (compositeOp == COMPOSITE_LIGHTEN) {
+ mode = "Lghn";
+ } else if (compositeOp == COMPOSITE_SCREEN) {
+ mode = "Scrn";
+ } else if (compositeOp == COMPOSITE_DODGE) {
+ mode = "CDdg";
+ } else if (compositeOp == COMPOSITE_LINEAR_DODGE) {
+ mode = "linearDodge";
+ } else if (compositeOp == COMPOSITE_LIGHTER_COLOR) {
+ mode = "lighterColor";
+ } else if (compositeOp == COMPOSITE_OVERLAY) {
+ mode = "Ovrl";
+ } else if (compositeOp == COMPOSITE_SOFT_LIGHT_PHOTOSHOP) {
+ mode = "SftL";
+ } else if (compositeOp == COMPOSITE_HARD_LIGHT) {
+ mode = "HrdL";
+ } else if (compositeOp == COMPOSITE_VIVID_LIGHT) {
+ mode = "vividLight";
+ } else if (compositeOp == COMPOSITE_LINEAR_LIGHT) {
+ mode = "linearLight";
+ } else if (compositeOp == COMPOSITE_PIN_LIGHT) {
+ mode = "pinLight";
+ } else if (compositeOp == COMPOSITE_HARD_MIX_PHOTOSHOP) {
+ mode = "hardMix";
+ } else if (compositeOp == COMPOSITE_DIFF) {
+ mode = "Dfrn";
+ } else if (compositeOp == COMPOSITE_EXCLUSION) {
+ mode = "Xclu";
+ } else if (compositeOp == COMPOSITE_SUBTRACT) {
+ mode = "Sbtr";
+ } else if (compositeOp == COMPOSITE_DIVIDE) {
+ mode = "divide";
+ } else if (compositeOp == COMPOSITE_HUE) {
+ mode = "H ";
+ } else if (compositeOp == COMPOSITE_SATURATION) {
+ mode = "Strt";
+ } else if (compositeOp == COMPOSITE_COLOR) {
+ mode = "Clr ";
+ } else if (compositeOp == COMPOSITE_LUMINIZE) {
+ mode = "Lmns";
+ } else {
+ dbgKrita << "Unknown composite op:" << mode << "Returning \"Nrml\"!";
+ }
+
+ return mode;
+}
+
+QString techniqueToString(psd_technique_type technique, const QString &typeId)
+{
+ QString result = "SfBL";
+
+ switch (technique) {
+ case psd_technique_softer:
+ result = "SfBL";
+ break;
+ case psd_technique_precise:
+ result = "PrBL";
+ break;
+ case psd_technique_slope_limit:
+ result = "Slmt";
+ break;
+ }
+
+ if (typeId == "BETE" && technique == psd_technique_slope_limit) {
+ warnKrita << "WARNING: techniqueToString: invalid technique type!" << ppVar(technique) << ppVar(typeId);
+ }
+
+ return result;
+}
+
+QString bevelStyleToString(psd_bevel_style style)
+{
+ QString result = "OtrB";
+
+ switch (style) {
+ case psd_bevel_outer_bevel:
+ result = "OtrB";
+ break;
+ case psd_bevel_inner_bevel:
+ result = "InrB";
+ break;
+ case psd_bevel_emboss:
+ result = "Embs";
+ break;
+ case psd_bevel_pillow_emboss:
+ result = "PlEb";
+ break;
+ case psd_bevel_stroke_emboss:
+ result = "strokeEmboss";
+ break;
+ }
+
+ return result;
+}
+
+QString gradientTypeToString(psd_gradient_style style)
+{
+ QString result = "Lnr ";
+
+ switch (style) {
+ case psd_gradient_style_linear:
+ result = "Lnr ";
+ break;
+ case psd_gradient_style_radial:
+ result = "Rdl ";
+ break;
+ case psd_gradient_style_angle:
+ result = "Angl";
+ break;
+ case psd_gradient_style_reflected:
+ result = "Rflc";
+ break;
+ case psd_gradient_style_diamond:
+ result = "Dmnd";
+ break;
+ }
+
+ return result;
+}
+
+QString strokePositionToString(psd_stroke_position position)
+{
+ QString result = "OutF";
+
+ switch (position) {
+ case psd_stroke_outside:
+ result = "OutF";
+ break;
+ case psd_stroke_inside:
+ result = "InsF";
+ break;
+ case psd_stroke_center:
+ result = "CtrF";
+ break;
+ }
+
+ return result;
+}
+
+QString strokeFillTypeToString(psd_fill_type position)
+{
+ QString result = "SClr";
+
+ switch (position) {
+ case psd_fill_solid_color:
+ result = "SClr";
+ break;
+ case psd_fill_gradient:
+ result = "GrFl";
+ break;
+ case psd_fill_pattern:
+ result = "Ptrn";
+ break;
+ }
+
+ return result;
+}
+
+QVector<KoPatternSP> KisAslLayerStyleSerializer::fetchAllPatterns(KisPSDLayerStyle *style) const
+{
+ QVector <KoPatternSP> allPatterns;
+
+ if (style->patternOverlay()->effectEnabled()) {
+ allPatterns << style->patternOverlay()->pattern();
+ }
+
+ if (style->stroke()->effectEnabled() &&
+ style->stroke()->fillType() == psd_fill_pattern) {
+
+ allPatterns << style->stroke()->pattern();
+ }
+
+ if(style->bevelAndEmboss()->effectEnabled() &&
+ style->bevelAndEmboss()->textureEnabled()) {
+
+ allPatterns << style->bevelAndEmboss()->texturePattern();
+ }
+
+ return allPatterns;
+}
+
+QString fetchPatternUuidSafe(KoPatternSP pattern, QHash<KoPatternSP, QString> patternToUuid)
+{
+ if (patternToUuid.contains(pattern)) {
+ return patternToUuid[pattern];
+ } else {
+ warnKrita << "WARNING: the pattern is not present in the Uuid map!";
+ return "invalid-uuid";
+ }
+}
+
+QDomDocument KisAslLayerStyleSerializer::formXmlDocument() const
+{
+ KIS_ASSERT_RECOVER(!m_stylesVector.isEmpty()) { return QDomDocument(); }
+
+ QVector<KoPatternSP> allPatterns;
+
+ Q_FOREACH (KisPSDLayerStyleSP style, m_stylesVector) {
+ allPatterns += fetchAllPatterns(style.data());
+ }
+
+ QHash<KoPatternSP, QString> patternToUuidMap;
+
+ KisAslXmlWriter w;
+
+ if (!allPatterns.isEmpty()) {
+ w.enterList(ResourceType::Patterns);
+
+ Q_FOREACH (KoPatternSP pattern, allPatterns) {
+ if (pattern) {
+ if (!patternToUuidMap.contains(pattern)) {
+ QString uuid = w.writePattern("", pattern);
+ patternToUuidMap.insert(pattern, uuid);
+ }
+ } else {
+ warnKrita << "WARNING: KisAslLayerStyleSerializer::saveToDevice: saved pattern is null!";
+ }
+ }
+
+ w.leaveList();
+ }
+
+ Q_FOREACH (KisPSDLayerStyleSP style, m_stylesVector) {
+
+ w.enterDescriptor("", "", "null");
+ w.writeText("Nm ", style->name());
+ w.writeText("Idnt", style->psdUuid());
+ w.leaveDescriptor();
+
+ w.enterDescriptor("", "", "Styl");
+
+ w.enterDescriptor("documentMode", "", "documentMode");
+ w.leaveDescriptor();
+
+ w.enterDescriptor("Lefx", "", "Lefx");
+
+ w.writeUnitFloat("Scl ", "#Prc", 100);
+ w.writeBoolean("masterFXSwitch", style->isEnabled());
+
+
+ // Drop Shadow
+ const psd_layer_effects_drop_shadow *dropShadow = style->dropShadow();
+ if (dropShadow->effectEnabled()) {
+ w.enterDescriptor("DrSh", "", "DrSh");
+
+ w.writeBoolean("enab", dropShadow->effectEnabled());
+ w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(dropShadow->blendMode()));
+ w.writeColor("Clr ", dropShadow->color());
+
+ w.writeUnitFloat("Opct", "#Prc", dropShadow->opacity());
+ w.writeBoolean("uglg", dropShadow->useGlobalLight());
+ w.writeUnitFloat("lagl", "#Ang", dropShadow->angle());
+ w.writeUnitFloat("Dstn", "#Pxl", dropShadow->distance());
+ w.writeUnitFloat("Ckmt", "#Pxl", dropShadow->spread());
+ w.writeUnitFloat("blur", "#Pxl", dropShadow->size());
+ w.writeUnitFloat("Nose", "#Prc", dropShadow->noise());
+
+ w.writeBoolean("AntA", dropShadow->antiAliased());
+
+ // FIXME: save curves
+ w.writeCurve("TrnS",
+ "Linear",
+ QVector<QPointF>() << QPointF() << QPointF(255, 255));
+
+ w.writeBoolean("layerConceals", dropShadow->knocksOut());
+ w.leaveDescriptor();
+ }
+
+ // Inner Shadow
+ const psd_layer_effects_inner_shadow *innerShadow = style->innerShadow();
+ if (innerShadow->effectEnabled()) {
+ w.enterDescriptor("IrSh", "", "IrSh");
+
+ w.writeBoolean("enab", innerShadow->effectEnabled());
+ w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(innerShadow->blendMode()));
+ w.writeColor("Clr ", innerShadow->color());
+
+ w.writeUnitFloat("Opct", "#Prc", innerShadow->opacity());
+ w.writeBoolean("uglg", innerShadow->useGlobalLight());
+ w.writeUnitFloat("lagl", "#Ang", innerShadow->angle());
+ w.writeUnitFloat("Dstn", "#Pxl", innerShadow->distance());
+ w.writeUnitFloat("Ckmt", "#Pxl", innerShadow->spread());
+ w.writeUnitFloat("blur", "#Pxl", innerShadow->size());
+ w.writeUnitFloat("Nose", "#Prc", innerShadow->noise());
+
+ w.writeBoolean("AntA", innerShadow->antiAliased());
+
+ // FIXME: save curves
+ w.writeCurve("TrnS",
+ "Linear",
+ QVector<QPointF>() << QPointF() << QPointF(255, 255));
+
+ w.leaveDescriptor();
+ }
+
+ // Outer Glow
+ const psd_layer_effects_outer_glow *outerGlow = style->outerGlow();
+ if (outerGlow->effectEnabled()) {
+ w.enterDescriptor("OrGl", "", "OrGl");
+
+ w.writeBoolean("enab", outerGlow->effectEnabled());
+ w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(outerGlow->blendMode()));
+
+
+ if (outerGlow->fillType() == psd_fill_gradient && outerGlow->gradient()) {
+ KoSegmentGradient *segmentGradient = dynamic_cast<KoSegmentGradient*>(outerGlow->gradient().data());
+ KoStopGradient *stopGradient = dynamic_cast<KoStopGradient*>(outerGlow->gradient().data());
+
+ if (segmentGradient) {
+ w.writeSegmentGradient("Grad", segmentGradient);
+ } else if (stopGradient) {
+ w.writeStopGradient("Grad", stopGradient);
+ } else {
+ warnKrita << "WARNING: OG: Unknown gradient type!";
+ w.writeColor("Clr ", outerGlow->color());
+ }
+
+ } else {
+ w.writeColor("Clr ", outerGlow->color());
+ }
+
+ w.writeUnitFloat("Opct", "#Prc", outerGlow->opacity());
+
+ w.writeEnum("GlwT", "BETE", techniqueToString(outerGlow->technique(), "BETE"));
+
+ w.writeUnitFloat("Ckmt", "#Pxl", outerGlow->spread());
+ w.writeUnitFloat("blur", "#Pxl", outerGlow->size());
+ w.writeUnitFloat("Nose", "#Prc", outerGlow->noise());
+
+ w.writeUnitFloat("ShdN", "#Prc", outerGlow->jitter());
+
+ w.writeBoolean("AntA", outerGlow->antiAliased());
+
+ // FIXME: save curves
+ w.writeCurve("TrnS",
+ "Linear",
+ QVector<QPointF>() << QPointF() << QPointF(255, 255));
+
+ w.writeUnitFloat("Inpr", "#Prc", outerGlow->range());
+
+ w.leaveDescriptor();
+ }
+
+ // Inner Glow
+ const psd_layer_effects_inner_glow *innerGlow = style->innerGlow();
+ if (innerGlow->effectEnabled()) {
+ w.enterDescriptor("IrGl", "", "IrGl");
+
+ w.writeBoolean("enab", innerGlow->effectEnabled());
+ w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(innerGlow->blendMode()));
+
+
+ if (innerGlow->fillType() == psd_fill_gradient && innerGlow->gradient()) {
+ KoSegmentGradient *segmentGradient = dynamic_cast<KoSegmentGradient*>(innerGlow->gradient().data());
+ KoStopGradient *stopGradient = dynamic_cast<KoStopGradient*>(innerGlow->gradient().data());
+
+ if (segmentGradient) {
+ w.writeSegmentGradient("Grad", segmentGradient);
+ } else if (stopGradient) {
+ w.writeStopGradient("Grad", stopGradient);
+ } else {
+ warnKrita << "WARNING: IG: Unknown gradient type!";
+ w.writeColor("Clr ", innerGlow->color());
+ }
+
+ } else {
+ w.writeColor("Clr ", innerGlow->color());
+ }
+
+ w.writeUnitFloat("Opct", "#Prc", innerGlow->opacity());
+
+ w.writeEnum("GlwT", "BETE", techniqueToString(innerGlow->technique(), "BETE"));
+
+ w.writeUnitFloat("Ckmt", "#Pxl", innerGlow->spread());
+ w.writeUnitFloat("blur", "#Pxl", innerGlow->size());
+
+ // NOTE: order is swapped in ASL!
+ w.writeUnitFloat("ShdN", "#Prc", innerGlow->jitter());
+ w.writeUnitFloat("Nose", "#Prc", innerGlow->noise());
+
+ w.writeBoolean("AntA", innerGlow->antiAliased());
+
+ w.writeEnum("glwS", "IGSr", innerGlow->source() == psd_glow_center ? "SrcC" : "SrcE");
+
+ // FIXME: save curves
+ w.writeCurve("TrnS",
+ "Linear",
+ QVector<QPointF>() << QPointF() << QPointF(255, 255));
+
+ w.writeUnitFloat("Inpr", "#Prc", innerGlow->range());
+
+ w.leaveDescriptor();
+ }
+
+ // Bevel and Emboss
+ const psd_layer_effects_bevel_emboss *bevelAndEmboss = style->bevelAndEmboss();
+ if (bevelAndEmboss->effectEnabled()) {
+ w.enterDescriptor("ebbl", "", "ebbl");
+
+ w.writeBoolean("enab", bevelAndEmboss->effectEnabled());
+
+ w.writeEnum("hglM", "BlnM", compositeOpToBlendMode(bevelAndEmboss->highlightBlendMode()));
+ w.writeColor("hglC", bevelAndEmboss->highlightColor());
+ w.writeUnitFloat("hglO", "#Prc", bevelAndEmboss->highlightOpacity());
+
+ w.writeEnum("sdwM", "BlnM", compositeOpToBlendMode(bevelAndEmboss->shadowBlendMode()));
+ w.writeColor("sdwC", bevelAndEmboss->shadowColor());
+ w.writeUnitFloat("sdwO", "#Prc", bevelAndEmboss->shadowOpacity());
+
+ w.writeEnum("bvlT", "bvlT", techniqueToString(bevelAndEmboss->technique(), "bvlT"));
+ w.writeEnum("bvlS", "BESl", bevelStyleToString(bevelAndEmboss->style()));
+
+ w.writeBoolean("uglg", bevelAndEmboss->useGlobalLight());
+ w.writeUnitFloat("lagl", "#Ang", bevelAndEmboss->angle());
+ w.writeUnitFloat("Lald", "#Ang", bevelAndEmboss->altitude());
+
+ w.writeUnitFloat("srgR", "#Prc", bevelAndEmboss->depth());
+ w.writeUnitFloat("blur", "#Pxl", bevelAndEmboss->size());
+ w.writeEnum("bvlD", "BESs", bevelAndEmboss->direction() == psd_direction_up ? "In " : "Out ");
+
+ // FIXME: save curves
+ w.writeCurve("TrnS",
+ "Linear",
+ QVector<QPointF>() << QPointF() << QPointF(255, 255));
+
+ w.writeBoolean("antialiasGloss", bevelAndEmboss->glossAntiAliased());
+
+ w.writeUnitFloat("Sftn", "#Pxl", bevelAndEmboss->soften());
+
+ if (bevelAndEmboss->contourEnabled()) {
+ w.writeBoolean("useShape", bevelAndEmboss->contourEnabled());
+
+ // FIXME: save curves
+ w.writeCurve("MpgS",
+ "Linear",
+ QVector<QPointF>() << QPointF() << QPointF(255, 255));
+
+ w.writeBoolean("AntA", bevelAndEmboss->antiAliased());
+ w.writeUnitFloat("Inpr", "#Prc", bevelAndEmboss->contourRange());
+ }
+
+ w.writeBoolean("useTexture", bevelAndEmboss->textureEnabled());
+
+ if (bevelAndEmboss->textureEnabled()) {
+ w.writeBoolean("InvT", bevelAndEmboss->textureInvert());
+ w.writeBoolean("Algn", bevelAndEmboss->textureAlignWithLayer());
+ w.writeUnitFloat("Scl ", "#Prc", bevelAndEmboss->textureScale());
+ w.writeUnitFloat("textureDepth ", "#Prc", bevelAndEmboss->textureDepth());
+ w.writePatternRef("Ptrn", bevelAndEmboss->texturePattern(), fetchPatternUuidSafe(bevelAndEmboss->texturePattern(), patternToUuidMap));
+ w.writePhasePoint("phase", bevelAndEmboss->texturePhase());
+ }
+
+ w.leaveDescriptor();
+ }
+
+ // Satin
+ const psd_layer_effects_satin *satin = style->satin();
+ if (satin->effectEnabled()) {
+ w.enterDescriptor("ChFX", "", "ChFX");
+
+ w.writeBoolean("enab", satin->effectEnabled());
+ w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(satin->blendMode()));
+ w.writeColor("Clr ", satin->color());
+
+ w.writeBoolean("AntA", satin->antiAliased());
+ w.writeBoolean("Invr", satin->invert());
+ w.writeUnitFloat("Opct", "#Prc", satin->opacity());
+ w.writeUnitFloat("lagl", "#Ang", satin->angle());
+ w.writeUnitFloat("Dstn", "#Pxl", satin->distance());
+ w.writeUnitFloat("blur", "#Pxl", satin->size());
+
+ // FIXME: save curves
+ w.writeCurve("MpgS",
+ "Linear",
+ QVector<QPointF>() << QPointF() << QPointF(255, 255));
+
+ w.leaveDescriptor();
+ }
+
+ const psd_layer_effects_color_overlay *colorOverlay = style->colorOverlay();
+ if (colorOverlay->effectEnabled()) {
+ w.enterDescriptor("SoFi", "", "SoFi");
+
+ w.writeBoolean("enab", colorOverlay->effectEnabled());
+ w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(colorOverlay->blendMode()));
+ w.writeUnitFloat("Opct", "#Prc", colorOverlay->opacity());
+ w.writeColor("Clr ", colorOverlay->color());
+
+ w.leaveDescriptor();
+ }
+
+ // Gradient Overlay
+ const psd_layer_effects_gradient_overlay *gradientOverlay = style->gradientOverlay();
+ KoSegmentGradient *segmentGradient = dynamic_cast<KoSegmentGradient*>(gradientOverlay->gradient().data());
+ KoStopGradient *stopGradient = dynamic_cast<KoStopGradient*>(gradientOverlay->gradient().data());
+
+ if (gradientOverlay->effectEnabled() && (segmentGradient || stopGradient)) {
+ w.enterDescriptor("GrFl", "", "GrFl");
+
+ w.writeBoolean("enab", gradientOverlay->effectEnabled());
+ w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(gradientOverlay->blendMode()));
+ w.writeUnitFloat("Opct", "#Prc", gradientOverlay->opacity());
+
+ if (segmentGradient) {
+ w.writeSegmentGradient("Grad", segmentGradient);
+ } else if (stopGradient) {
+ w.writeStopGradient("Grad", stopGradient);
+ }
+
+ w.writeUnitFloat("Angl", "#Ang", gradientOverlay->angle());
+
+ w.writeEnum("Type", "GrdT", gradientTypeToString(gradientOverlay->style()));
+
+ w.writeBoolean("Rvrs", gradientOverlay->reverse());
+ w.writeBoolean("Algn", gradientOverlay->alignWithLayer());
+ w.writeUnitFloat("Scl ", "#Prc", gradientOverlay->scale());
+
+ w.writeOffsetPoint("Ofst", gradientOverlay->gradientOffset());
+
+ // FIXME: Krita doesn't support dithering
+ w.writeBoolean("Dthr", true/*gradientOverlay->dither()*/);
+
+ w.leaveDescriptor();
+ }
+
+ // Pattern Overlay
+ const psd_layer_effects_pattern_overlay *patternOverlay = style->patternOverlay();
+ if (patternOverlay->effectEnabled()) {
+ w.enterDescriptor("patternFill", "", "patternFill");
+
+ w.writeBoolean("enab", patternOverlay->effectEnabled());
+ w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(patternOverlay->blendMode()));
+ w.writeUnitFloat("Opct", "#Prc", patternOverlay->opacity());
+
+ w.writePatternRef("Ptrn", patternOverlay->pattern(), fetchPatternUuidSafe(patternOverlay->pattern(), patternToUuidMap));
+
+ w.writeUnitFloat("Scl ", "#Prc", patternOverlay->scale());
+ w.writeBoolean("Algn", patternOverlay->alignWithLayer());
+ w.writePhasePoint("phase", patternOverlay->patternPhase());
+
+ w.leaveDescriptor();
+ }
+
+ const psd_layer_effects_stroke *stroke = style->stroke();
+ if (stroke->effectEnabled()) {
+ w.enterDescriptor("FrFX", "", "FrFX");
+
+ w.writeBoolean("enab", stroke->effectEnabled());
+
+ w.writeEnum("Styl", "FStl", strokePositionToString(stroke->position()));
+ w.writeEnum("PntT", "FrFl", strokeFillTypeToString(stroke->fillType()));
+
+ w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(stroke->blendMode()));
+ w.writeUnitFloat("Opct", "#Prc", stroke->opacity());
+
+ w.writeUnitFloat("Sz ", "#Pxl", stroke->size());
+
+ if (stroke->fillType() == psd_fill_solid_color) {
+ w.writeColor("Clr ", stroke->color());
+ } else if (stroke->fillType() == psd_fill_gradient) {
+ KoSegmentGradient *segmentGradient = dynamic_cast<KoSegmentGradient*>(stroke->gradient().data());
+ KoStopGradient *stopGradient = dynamic_cast<KoStopGradient*>(stroke->gradient().data());
+
+ if (segmentGradient) {
+ w.writeSegmentGradient("Grad", segmentGradient);
+ } else if (stopGradient) {
+ w.writeStopGradient("Grad", stopGradient);
+ } else {
+ warnKrita << "WARNING: Stroke: Unknown gradient type!";
+ w.writeColor("Clr ", stroke->color());
+ }
+
+ w.writeUnitFloat("Angl", "#Ang", stroke->angle());
+ w.writeEnum("Type", "GrdT", gradientTypeToString(stroke->style()));
+
+ w.writeBoolean("Rvrs", stroke->reverse());
+ w.writeUnitFloat("Scl ", "#Prc", stroke->scale());
+ w.writeBoolean("Algn", stroke->alignWithLayer());
+ w.writeOffsetPoint("Ofst", stroke->gradientOffset());
+
+ // FIXME: Krita doesn't support dithering
+ w.writeBoolean("Dthr", true/*stroke->dither()*/);
+
+ } else if (stroke->fillType() == psd_fill_pattern) {
+ w.writePatternRef("Ptrn", stroke->pattern(), fetchPatternUuidSafe(stroke->pattern(), patternToUuidMap));
+ w.writeUnitFloat("Scl ", "#Prc", stroke->scale());
+ w.writeBoolean("Lnkd", stroke->alignWithLayer());
+ w.writePhasePoint("phase", stroke->patternPhase());
+ }
+
+ w.leaveDescriptor();
+ }
+
+ w.leaveDescriptor();
+ w.leaveDescriptor();
+ }
+
+ return w.document();
+}
+
+inline QDomNode findNodeByClassId(const QString &classId, QDomNode parent) {
+ return KisDomUtils::findElementByAttibute(parent, "node", "classId", classId);
+}
+
+void replaceAllChildren(QDomNode src, QDomNode dst)
+{
+ QDomNode node;
+
+ do {
+ node = dst.lastChild();
+ dst.removeChild(node);
+
+ } while(!node.isNull());
+
+
+ node = src.firstChild();
+ while(!node.isNull()) {
+ dst.appendChild(node);
+ node = src.firstChild();
+ }
+
+ src.parentNode().removeChild(src);
+}
+
+QDomDocument KisAslLayerStyleSerializer::formPsdXmlDocument() const
+{
+ QDomDocument doc = formXmlDocument();
+
+ QDomNode nullNode = findNodeByClassId("null", doc.documentElement());
+ QDomNode stylNode = findNodeByClassId("Styl", doc.documentElement());
+ QDomNode lefxNode = findNodeByClassId("Lefx", stylNode);
+
+ replaceAllChildren(lefxNode, nullNode);
+
+ return doc;
+}
+
+void KisAslLayerStyleSerializer::saveToDevice(QIODevice *device)
+{
+ QDomDocument doc = formXmlDocument();
+ if (doc.isNull()) return ;
+
+ KisAslWriter writer;
+ writer.writeFile(device, doc);
+}
+
+void convertAndSetBlendMode(const QString &mode,
+ boost::function<void (const QString &)> setBlendMode)
+{
+ QString compositeOp = COMPOSITE_OVER;
+
+ if (mode == "Nrml") {
+ compositeOp = COMPOSITE_OVER;
+ } else if (mode == "Dslv") {
+ compositeOp = COMPOSITE_DISSOLVE;
+ } else if (mode == "Drkn") {
+ compositeOp = COMPOSITE_DARKEN;
+ } else if (mode == "Mltp") {
+ compositeOp = COMPOSITE_MULT;
+ } else if (mode == "CBrn") {
+ compositeOp = COMPOSITE_BURN;
+ } else if (mode == "linearBurn") {
+ compositeOp = COMPOSITE_LINEAR_BURN;
+ } else if (mode == "darkerColor") {
+ compositeOp = COMPOSITE_DARKER_COLOR;
+ } else if (mode == "Lghn") {
+ compositeOp = COMPOSITE_LIGHTEN;
+ } else if (mode == "Scrn") {
+ compositeOp = COMPOSITE_SCREEN;
+ } else if (mode == "CDdg") {
+ compositeOp = COMPOSITE_DODGE;
+ } else if (mode == "linearDodge") {
+ compositeOp = COMPOSITE_LINEAR_DODGE;
+ } else if (mode == "lighterColor") {
+ compositeOp = COMPOSITE_LIGHTER_COLOR;
+ } else if (mode == "Ovrl") {
+ compositeOp = COMPOSITE_OVERLAY;
+ } else if (mode == "SftL") {
+ compositeOp = COMPOSITE_SOFT_LIGHT_PHOTOSHOP;
+ } else if (mode == "HrdL") {
+ compositeOp = COMPOSITE_HARD_LIGHT;
+ } else if (mode == "vividLight") {
+ compositeOp = COMPOSITE_VIVID_LIGHT;
+ } else if (mode == "linearLight") {
+ compositeOp = COMPOSITE_LINEAR_LIGHT;
+ } else if (mode == "pinLight") {
+ compositeOp = COMPOSITE_PIN_LIGHT;
+ } else if (mode == "hardMix") {
+ compositeOp = COMPOSITE_HARD_MIX_PHOTOSHOP;
+ } else if (mode == "Dfrn") {
+ compositeOp = COMPOSITE_DIFF;
+ } else if (mode == "Xclu") {
+ compositeOp = COMPOSITE_EXCLUSION;
+ } else if (mode == "Sbtr") {
+ compositeOp = COMPOSITE_SUBTRACT;
+ } else if (mode == "divide") {
+ compositeOp = COMPOSITE_DIVIDE;
+ } else if (mode == "H ") {
+ compositeOp = COMPOSITE_HUE;
+ } else if (mode == "Strt") {
+ compositeOp = COMPOSITE_SATURATION;
+ } else if (mode == "Clr ") {
+ compositeOp = COMPOSITE_COLOR;
+ } else if (mode == "Lmns") {
+ compositeOp = COMPOSITE_LUMINIZE;
+ } else {
+ dbgKrita << "Unknown blending mode:" << mode << "Returning COMPOSITE_OVER!";
+ }
+
+ setBlendMode(compositeOp);
+}
+
+void convertAndSetCurve(const QString &name,
+ const QVector<QPointF> &points,
+ boost::function<void (const quint8*)> setCurveLookupTable)
+{
+ Q_UNUSED(name);
+ Q_UNUSED(points);
+ Q_UNUSED(setCurveLookupTable);
+
+ warnKrita << "convertAndSetBlendMode:" << "Curve conversion is not implemented yet";
+}
+
+template <typename T>
+void convertAndSetEnum(const QString &value,
+ const QMap<QString, T> map,
+ boost::function<void (T)> setMappedValue)
+{
+ setMappedValue(map[value]);
+}
+
+inline QString _prepaddr(const QString &pref, const QString &addr) {
+ return pref + addr;
+}
+
+#define CONN_TEXT_RADDR(addr, method, object, type) m_catcher.subscribeText(addr, std::bind(&type::method, object, _1))
+#define CONN_COLOR(addr, method, object, type, prefix) m_catcher.subscribeColor(_prepaddr(prefix, addr), std::bind(&type::method, object, _1))
+#define CONN_UNITF(addr, unit, method, object, type, prefix) m_catcher.subscribeUnitFloat(_prepaddr(prefix, addr), unit, std::bind(&type::method, object, _1))
+#define CONN_BOOL(addr, method, object, type, prefix) m_catcher.subscribeBoolean(_prepaddr(prefix, addr), std::bind(&type::method, object, _1))
+
+#define CONN_POINT(addr, method, object, type, prefix) m_catcher.subscribePoint(_prepaddr(prefix, addr), std::bind(&type::method, object, _1))
+
+#define CONN_COMPOSITE_OP(addr, method, object, type, prefix) \
+ { \
+ boost::function<void (const QString&)> setter = \
+ std::bind(&type::method, object, _1); \
+ m_catcher.subscribeEnum(_prepaddr(prefix, addr), "BlnM", std::bind(convertAndSetBlendMode, _1, setter)); \
+ }
+
+#define CONN_CURVE(addr, method, object, type, prefix) \
+ { \
+ boost::function<void (const quint8*)> setter = \
+ std::bind(&type::method, object, _1); \
+ m_catcher.subscribeCurve(_prepaddr(prefix, addr), std::bind(convertAndSetCurve, _1, _2, setter)); \
+ }
+
+#define CONN_ENUM(addr, tag, method, map, mapped_type, object, type, prefix) \
+ { \
+ boost::function<void (mapped_type)> setter = \
+ std::bind(&type::method, object, _1); \
+ m_catcher.subscribeEnum(_prepaddr(prefix, addr), tag, std::bind(convertAndSetEnum<mapped_type>, _1, map, setter)); \
+ }
+
+#define CONN_GRADIENT(addr, method, object, type, prefix) \
+ { \
+ m_catcher.subscribeGradient(_prepaddr(prefix, addr), std::bind(&type::method, object, _1)); \
+ }
+
+#define CONN_PATTERN(addr, method, object, type, prefix) \
+ { \
+ boost::function<void (KoPatternSP)> setter = \
+ std::bind(&type::method, object, _1); \
+ m_catcher.subscribePatternRef(_prepaddr(prefix, addr), std::bind(&KisAslLayerStyleSerializer::assignPatternObject, this, _1, _2, setter)); \
+ }
+
+void KisAslLayerStyleSerializer::registerPatternObject(const KoPatternSP pattern, const QString& patternUuid) {
+
+ if (m_patternsStore.contains(patternUuid)) {
+ warnKrita << "WARNING: ASL style contains a duplicated pattern!" << ppVar(pattern->name()) << ppVar(m_patternsStore[patternUuid]->name());
+ } else {
+ pattern->setFilename(patternUuid + QString("_pattern"));
+ m_patternsStore.insert(patternUuid, pattern);
+ }
+}
+
+void KisAslLayerStyleSerializer::assignPatternObject(const QString &patternUuid,
+ const QString &patternName,
+ boost::function<void (KoPatternSP )> setPattern)
+{
+ Q_UNUSED(patternName);
+
+ KoPatternSP pattern = m_patternsStore[patternUuid];
+
+ if (!pattern) {
+ warnKrita << "WARNING: ASL style contains non-existent pattern reference! Searching for uuid: "
+ << patternUuid << " (name: " << patternName << ")";
+
+ QImage dumbImage(32, 32, QImage::Format_ARGB32);
+ dumbImage.fill(Qt::red);
+ KoPatternSP dumbPattern(new KoPattern(dumbImage, "invalid", ""));
+ registerPatternObject(dumbPattern, patternUuid + QString("_invalid"));
+ pattern = dumbPattern;
+ m_isValid = false;
+ }
+
+ setPattern(pattern);
+}
+
+class FillStylesCorrector {
+public:
+
+ static void correct(KisPSDLayerStyle *style) {
+ correctWithoutPattern(style->outerGlow());
+ correctWithoutPattern(style->innerGlow());
+ correctWithPattern(style->stroke());
+ }
+
+private:
+
+ template <class T>
+ static void correctWithPattern(T *config) {
+ if (config->pattern()) {
+ config->setFillType(psd_fill_pattern);
+ } else if (config->gradient()) {
+ config->setFillType(psd_fill_gradient);
+ } else {
+ config->setFillType(psd_fill_solid_color);
+ }
+ }
+
+ template <class T>
+ static void correctWithoutPattern(T *config) {
+ if (config->gradient()) {
+ config->setFillType(psd_fill_gradient);
+ } else {
+ config->setFillType(psd_fill_solid_color);
+ }
+ }
+};
+
+void KisAslLayerStyleSerializer::connectCatcherToStyle(KisPSDLayerStyle *style, const QString &prefix)
+{
+ CONN_TEXT_RADDR("/null/Nm ", setName, style, KisPSDLayerStyle);
+ CONN_TEXT_RADDR("/null/Idnt", setPsdUuid, style, KisPSDLayerStyle);
+
+ CONN_BOOL("/masterFXSwitch", setEnabled, style, KisPSDLayerStyle, prefix);
+
+ psd_layer_effects_drop_shadow *dropShadow = style->dropShadow();
+
+ CONN_COMPOSITE_OP("/DrSh/Md ", setBlendMode, dropShadow, psd_layer_effects_drop_shadow, prefix);
+ CONN_COLOR("/DrSh/Clr ", setColor, dropShadow, psd_layer_effects_drop_shadow, prefix);
+ CONN_UNITF("/DrSh/Opct", "#Prc", setOpacity, dropShadow, psd_layer_effects_drop_shadow, prefix);
+ CONN_UNITF("/DrSh/lagl", "#Ang", setAngle, dropShadow, psd_layer_effects_drop_shadow, prefix);
+ CONN_UNITF("/DrSh/Dstn", "#Pxl", setDistance, dropShadow, psd_layer_effects_drop_shadow, prefix);
+ CONN_UNITF("/DrSh/Ckmt", "#Pxl", setSpread, dropShadow, psd_layer_effects_drop_shadow, prefix);
+ CONN_UNITF("/DrSh/blur", "#Pxl", setSize, dropShadow, psd_layer_effects_drop_shadow, prefix);
+ CONN_UNITF("/DrSh/Nose", "#Prc", setNoise, dropShadow, psd_layer_effects_drop_shadow, prefix);
+ CONN_BOOL("/DrSh/enab", setEffectEnabled, dropShadow, psd_layer_effects_drop_shadow, prefix);
+ CONN_BOOL("/DrSh/uglg", setUseGlobalLight, dropShadow, psd_layer_effects_drop_shadow, prefix);
+ CONN_BOOL("/DrSh/AntA", setAntiAliased, dropShadow, psd_layer_effects_drop_shadow, prefix);
+ CONN_BOOL("/DrSh/layerConceals", setKnocksOut, dropShadow, psd_layer_effects_drop_shadow, prefix);
+ CONN_CURVE("/DrSh/TrnS", setContourLookupTable, dropShadow, psd_layer_effects_drop_shadow, prefix);
+
+ psd_layer_effects_inner_shadow *innerShadow = style->innerShadow();
+
+ CONN_COMPOSITE_OP("/IrSh/Md ", setBlendMode, innerShadow, psd_layer_effects_inner_shadow, prefix);
+ CONN_COLOR("/IrSh/Clr ", setColor, innerShadow, psd_layer_effects_inner_shadow, prefix);
+ CONN_UNITF("/IrSh/Opct", "#Prc", setOpacity, innerShadow, psd_layer_effects_inner_shadow, prefix);
+ CONN_UNITF("/IrSh/lagl", "#Ang", setAngle, innerShadow, psd_layer_effects_inner_shadow, prefix);
+ CONN_UNITF("/IrSh/Dstn", "#Pxl", setDistance, innerShadow, psd_layer_effects_inner_shadow, prefix);
+ CONN_UNITF("/IrSh/Ckmt", "#Pxl", setSpread, innerShadow, psd_layer_effects_inner_shadow, prefix);
+ CONN_UNITF("/IrSh/blur", "#Pxl", setSize, innerShadow, psd_layer_effects_inner_shadow, prefix);
+ CONN_UNITF("/IrSh/Nose", "#Prc", setNoise, innerShadow, psd_layer_effects_inner_shadow, prefix);
+ CONN_BOOL("/IrSh/enab", setEffectEnabled, innerShadow, psd_layer_effects_inner_shadow, prefix);
+ CONN_BOOL("/IrSh/uglg", setUseGlobalLight, innerShadow, psd_layer_effects_inner_shadow, prefix);
+ CONN_BOOL("/IrSh/AntA", setAntiAliased, innerShadow, psd_layer_effects_inner_shadow, prefix);
+ CONN_CURVE("/IrSh/TrnS", setContourLookupTable, innerShadow, psd_layer_effects_inner_shadow, prefix);
+
+ psd_layer_effects_outer_glow *outerGlow = style->outerGlow();
+
+ CONN_COMPOSITE_OP("/OrGl/Md ", setBlendMode, outerGlow, psd_layer_effects_outer_glow, prefix);
+ CONN_COLOR("/OrGl/Clr ", setColor, outerGlow, psd_layer_effects_outer_glow, prefix);
+ CONN_UNITF("/OrGl/Opct", "#Prc", setOpacity, outerGlow, psd_layer_effects_outer_glow, prefix);
+ CONN_UNITF("/OrGl/Ckmt", "#Pxl", setSpread, outerGlow, psd_layer_effects_outer_glow, prefix);
+ CONN_UNITF("/OrGl/blur", "#Pxl", setSize, outerGlow, psd_layer_effects_outer_glow, prefix);
+ CONN_UNITF("/OrGl/Nose", "#Prc", setNoise, outerGlow, psd_layer_effects_outer_glow, prefix);
+ CONN_BOOL("/OrGl/enab", setEffectEnabled, outerGlow, psd_layer_effects_outer_glow, prefix);
+ CONN_BOOL("/OrGl/AntA", setAntiAliased, outerGlow, psd_layer_effects_outer_glow, prefix);
+ CONN_CURVE("/OrGl/TrnS", setContourLookupTable, outerGlow, psd_layer_effects_outer_glow, prefix);
+
+ QMap<QString, psd_technique_type> fillTechniqueMap;
+ fillTechniqueMap.insert("PrBL", psd_technique_precise);
+ fillTechniqueMap.insert("SfBL", psd_technique_softer);
+ CONN_ENUM("/OrGl/GlwT", "BETE", setTechnique, fillTechniqueMap, psd_technique_type, outerGlow, psd_layer_effects_outer_glow, prefix);
+
+ CONN_GRADIENT("/OrGl/Grad", setGradient, outerGlow, psd_layer_effects_outer_glow, prefix);
+
+ CONN_UNITF("/OrGl/Inpr", "#Prc", setRange, outerGlow, psd_layer_effects_outer_glow, prefix);
+ CONN_UNITF("/OrGl/ShdN", "#Prc", setJitter, outerGlow, psd_layer_effects_outer_glow, prefix);
+
+
+ psd_layer_effects_inner_glow *innerGlow = style->innerGlow();
+
+ CONN_COMPOSITE_OP("/IrGl/Md ", setBlendMode, innerGlow, psd_layer_effects_inner_glow, prefix);
+ CONN_COLOR("/IrGl/Clr ", setColor, innerGlow, psd_layer_effects_inner_glow, prefix);
+ CONN_UNITF("/IrGl/Opct", "#Prc", setOpacity, innerGlow, psd_layer_effects_inner_glow, prefix);
+ CONN_UNITF("/IrGl/Ckmt", "#Pxl", setSpread, innerGlow, psd_layer_effects_inner_glow, prefix);
+ CONN_UNITF("/IrGl/blur", "#Pxl", setSize, innerGlow, psd_layer_effects_inner_glow, prefix);
+ CONN_UNITF("/IrGl/Nose", "#Prc", setNoise, innerGlow, psd_layer_effects_inner_glow, prefix);
+ CONN_BOOL("/IrGl/enab", setEffectEnabled, innerGlow, psd_layer_effects_inner_glow, prefix);
+ CONN_BOOL("/IrGl/AntA", setAntiAliased, innerGlow, psd_layer_effects_inner_glow, prefix);
+ CONN_CURVE("/IrGl/TrnS", setContourLookupTable, innerGlow, psd_layer_effects_inner_glow, prefix);
+
+ CONN_ENUM("/IrGl/GlwT", "BETE", setTechnique, fillTechniqueMap, psd_technique_type, innerGlow, psd_layer_effects_inner_glow, prefix);
+
+ CONN_GRADIENT("/IrGl/Grad", setGradient, innerGlow, psd_layer_effects_inner_glow, prefix);
+
+ CONN_UNITF("/IrGl/Inpr", "#Prc", setRange, innerGlow, psd_layer_effects_inner_glow, prefix);
+ CONN_UNITF("/IrGl/ShdN", "#Prc", setJitter, innerGlow, psd_layer_effects_inner_glow, prefix);
+
+ QMap<QString, psd_glow_source> glowSourceMap;
+ glowSourceMap.insert("SrcC", psd_glow_center);
+ glowSourceMap.insert("SrcE", psd_glow_edge);
+ CONN_ENUM("/IrGl/glwS", "IGSr", setSource, glowSourceMap, psd_glow_source, innerGlow, psd_layer_effects_inner_glow, prefix);
+
+
+ psd_layer_effects_satin *satin = style->satin();
+
+ CONN_COMPOSITE_OP("/ChFX/Md ", setBlendMode, satin, psd_layer_effects_satin, prefix);
+ CONN_COLOR("/ChFX/Clr ", setColor, satin, psd_layer_effects_satin, prefix);
+
+ CONN_UNITF("/ChFX/Opct", "#Prc", setOpacity, satin, psd_layer_effects_satin, prefix);
+ CONN_UNITF("/ChFX/lagl", "#Ang", setAngle, satin, psd_layer_effects_satin, prefix);
+ CONN_UNITF("/ChFX/Dstn", "#Pxl", setDistance, satin, psd_layer_effects_satin, prefix);
+ CONN_UNITF("/ChFX/blur", "#Pxl", setSize, satin, psd_layer_effects_satin, prefix);
+
+ CONN_BOOL("/ChFX/enab", setEffectEnabled, satin, psd_layer_effects_satin, prefix);
+ CONN_BOOL("/ChFX/AntA", setAntiAliased, satin, psd_layer_effects_satin, prefix);
+ CONN_BOOL("/ChFX/Invr", setInvert, satin, psd_layer_effects_satin, prefix);
+ CONN_CURVE("/ChFX/MpgS", setContourLookupTable, satin, psd_layer_effects_satin, prefix);
+
+ psd_layer_effects_color_overlay *colorOverlay = style->colorOverlay();
+
+ CONN_COMPOSITE_OP("/SoFi/Md ", setBlendMode, colorOverlay, psd_layer_effects_color_overlay, prefix);
+ CONN_COLOR("/SoFi/Clr ", setColor, colorOverlay, psd_layer_effects_color_overlay, prefix);
+ CONN_UNITF("/SoFi/Opct", "#Prc", setOpacity, colorOverlay, psd_layer_effects_color_overlay, prefix);
+ CONN_BOOL("/SoFi/enab", setEffectEnabled, colorOverlay, psd_layer_effects_color_overlay, prefix);
+
+ psd_layer_effects_gradient_overlay *gradientOverlay = style->gradientOverlay();
+
+ CONN_COMPOSITE_OP("/GrFl/Md ", setBlendMode, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
+ CONN_UNITF("/GrFl/Opct", "#Prc", setOpacity, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
+ CONN_UNITF("/GrFl/Scl ", "#Prc", setScale, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
+ CONN_UNITF("/GrFl/Angl", "#Ang", setAngle, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
+ CONN_BOOL("/GrFl/enab", setEffectEnabled, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
+ // CONN_BOOL("/GrFl/Dthr", setDitherNotImplemented, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
+ CONN_BOOL("/GrFl/Rvrs", setReverse, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
+ CONN_BOOL("/GrFl/Algn", setAlignWithLayer, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
+ CONN_POINT("/GrFl/Ofst", setGradientOffset, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
+ CONN_GRADIENT("/GrFl/Grad", setGradient, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
+
+
+ QMap<QString, psd_gradient_style> gradientStyleMap;
+ gradientStyleMap.insert("Lnr ", psd_gradient_style_linear);
+ gradientStyleMap.insert("Rdl ", psd_gradient_style_radial);
+ gradientStyleMap.insert("Angl", psd_gradient_style_angle);
+ gradientStyleMap.insert("Rflc", psd_gradient_style_reflected);
+ gradientStyleMap.insert("Dmnd", psd_gradient_style_diamond);
+ CONN_ENUM("/GrFl/Type", "GrdT", setStyle, gradientStyleMap, psd_gradient_style, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
+
+ psd_layer_effects_pattern_overlay *patternOverlay = style->patternOverlay();
+
+ CONN_BOOL("/patternFill/enab", setEffectEnabled, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
+ CONN_COMPOSITE_OP("/patternFill/Md ", setBlendMode, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
+ CONN_UNITF("/patternFill/Opct", "#Prc", setOpacity, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
+ CONN_PATTERN("/patternFill/Ptrn", setPattern, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
+ CONN_UNITF("/patternFill/Scl ", "#Prc", setScale, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
+ CONN_BOOL("/patternFill/Algn", setAlignWithLayer, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
+ CONN_POINT("/patternFill/phase", setPatternPhase, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
+
+ psd_layer_effects_stroke *stroke = style->stroke();
+
+ CONN_COMPOSITE_OP("/FrFX/Md ", setBlendMode, stroke, psd_layer_effects_stroke, prefix);
+ CONN_BOOL("/FrFX/enab", setEffectEnabled, stroke, psd_layer_effects_stroke, prefix);
+ CONN_UNITF("/FrFX/Opct", "#Prc", setOpacity, stroke, psd_layer_effects_stroke, prefix);
+ CONN_UNITF("/FrFX/Sz ", "#Pxl", setSize, stroke, psd_layer_effects_stroke, prefix);
+
+ QMap<QString, psd_stroke_position> strokeStyleMap;
+ strokeStyleMap.insert("OutF", psd_stroke_outside);
+ strokeStyleMap.insert("InsF", psd_stroke_inside);
+ strokeStyleMap.insert("CtrF", psd_stroke_center);
+ CONN_ENUM("/FrFX/Styl", "FStl", setPosition, strokeStyleMap, psd_stroke_position, stroke, psd_layer_effects_stroke, prefix);
+
+ QMap<QString, psd_fill_type> strokeFillType;
+ strokeFillType.insert("SClr", psd_fill_solid_color);
+ strokeFillType.insert("GrFl", psd_fill_gradient);
+ strokeFillType.insert("Ptrn", psd_fill_pattern);
+ CONN_ENUM("/FrFX/PntT", "FrFl", setFillType, strokeFillType, psd_fill_type, stroke, psd_layer_effects_stroke, prefix);
+
+ // Color type
+ CONN_COLOR("/FrFX/Clr ", setColor, stroke, psd_layer_effects_stroke, prefix);
+
+ // Gradient Type
+ CONN_GRADIENT("/FrFX/Grad", setGradient, stroke, psd_layer_effects_stroke, prefix);
+ CONN_UNITF("/FrFX/Angl", "#Ang", setAngle, stroke, psd_layer_effects_stroke, prefix);
+ CONN_UNITF("/FrFX/Scl ", "#Prc", setScale, stroke, psd_layer_effects_stroke, prefix);
+ CONN_ENUM("/FrFX/Type", "GrdT", setStyle, gradientStyleMap, psd_gradient_style, stroke, psd_layer_effects_stroke, prefix);
+ CONN_BOOL("/FrFX/Rvrs", setReverse, stroke, psd_layer_effects_stroke, prefix);
+ CONN_BOOL("/FrFX/Algn", setAlignWithLayer, stroke, psd_layer_effects_stroke, prefix);
+ CONN_POINT("/FrFX/Ofst", setGradientOffset, stroke, psd_layer_effects_stroke, prefix);
+ // CONN_BOOL("/FrFX/Dthr", setDitherNotImplemented, stroke, psd_layer_effects_stroke, prefix);
+
+ // Pattern type
+
+ CONN_PATTERN("/FrFX/Ptrn", setPattern, stroke, psd_layer_effects_stroke, prefix);
+ CONN_BOOL("/FrFX/Lnkd", setAlignWithLayer, stroke, psd_layer_effects_stroke, prefix); // yes, we share the params...
+ CONN_POINT("/FrFX/phase", setPatternPhase, stroke, psd_layer_effects_stroke, prefix);
+
+
+ psd_layer_effects_bevel_emboss *bevelAndEmboss = style->bevelAndEmboss();
+
+ CONN_BOOL("/ebbl/enab", setEffectEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+ CONN_COMPOSITE_OP("/ebbl/hglM", setHighlightBlendMode, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_COLOR("/ebbl/hglC", setHighlightColor, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_UNITF("/ebbl/hglO", "#Prc", setHighlightOpacity, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+ CONN_COMPOSITE_OP("/ebbl/sdwM", setShadowBlendMode, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_COLOR("/ebbl/sdwC", setShadowColor, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_UNITF("/ebbl/sdwO", "#Prc", setShadowOpacity, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+ QMap<QString, psd_technique_type> bevelTechniqueMap;
+ bevelTechniqueMap.insert("PrBL", psd_technique_precise);
+ bevelTechniqueMap.insert("SfBL", psd_technique_softer);
+ bevelTechniqueMap.insert("Slmt", psd_technique_slope_limit);
+ CONN_ENUM("/ebbl/bvlT", "bvlT", setTechnique, bevelTechniqueMap, psd_technique_type, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+ QMap<QString, psd_bevel_style> bevelStyleMap;
+ bevelStyleMap.insert("OtrB", psd_bevel_outer_bevel);
+ bevelStyleMap.insert("InrB", psd_bevel_inner_bevel);
+ bevelStyleMap.insert("Embs", psd_bevel_emboss);
+ bevelStyleMap.insert("PlEb", psd_bevel_pillow_emboss);
+ bevelStyleMap.insert("strokeEmboss", psd_bevel_stroke_emboss);
+ CONN_ENUM("/ebbl/bvlS", "BESl", setStyle, bevelStyleMap, psd_bevel_style, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+ CONN_BOOL("/ebbl/uglg", setUseGlobalLight, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+ CONN_UNITF("/ebbl/lagl", "#Ang", setAngle, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_UNITF("/ebbl/Lald", "#Ang", setAltitude, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+ CONN_UNITF("/ebbl/srgR", "#Prc", setDepth, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+ CONN_UNITF("/ebbl/blur", "#Pxl", setSize, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+
+ QMap<QString, psd_direction> bevelDirectionMap;
+ bevelDirectionMap.insert("In ", psd_direction_up);
+ bevelDirectionMap.insert("Out ", psd_direction_down);
+ CONN_ENUM("/ebbl/bvlD", "BESs", setDirection, bevelDirectionMap, psd_direction, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+ CONN_CURVE("/ebbl/TrnS", setContourLookupTable, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+ CONN_BOOL("/ebbl/antialiasGloss", setGlossAntiAliased, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+ CONN_UNITF("/ebbl/Sftn", "#Pxl", setSoften, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+ // Use shape mode
+
+ CONN_BOOL("/ebbl/useShape", setContourEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_CURVE("/ebbl/MpgS", setGlossContourLookupTable, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_BOOL("/ebbl/AntA", setAntiAliased, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_UNITF("/ebbl/Inpr", "#Prc", setContourRange, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+
+ // Use texture mode
+
+ CONN_BOOL("/ebbl/useTexture", setTextureEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_BOOL("/ebbl/InvT", setTextureInvert, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_BOOL("/ebbl/Algn", setTextureAlignWithLayer, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_UNITF("/ebbl/Scl ", "#Prc", setTextureScale, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_UNITF("/ebbl/textureDepth", "#Prc", setTextureDepth, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_PATTERN("/ebbl/Ptrn", setTexturePattern, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+ CONN_POINT("/ebbl/phase", setTexturePhase, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
+}
+
+void KisAslLayerStyleSerializer::newStyleStarted(bool isPsdStructure)
+{
+ m_stylesVector.append(toQShared(new KisPSDLayerStyle()));
+ KisPSDLayerStyleSP currentStyleSP = m_stylesVector.last();
+ KisPSDLayerStyle *currentStyle = currentStyleSP.data();
+
+ psd_layer_effects_context *context = currentStyleSP->context();
+ context->keep_original = 0;
+
+ QString prefix = isPsdStructure ? "/null" : "/Styl/Lefx";
+ connectCatcherToStyle(currentStyle, prefix);
+}
+
+bool KisAslLayerStyleSerializer::readFromFile(const QString& filename)
+{
+ QFile file(filename);
+ if (file.size() == 0) return false;
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ dbgKrita << "Can't open file " << filename;
+ return false;
+ }
+ readFromDevice(&file);
+ m_initialized = true;
+ file.close();
+
+ return true;
+}
+
+void KisAslLayerStyleSerializer::readFromDevice(QIODevice *device)
+{
+ m_stylesVector.clear();
+
+ m_catcher.subscribePattern("/patterns/KisPattern", std::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1, _2));
+ m_catcher.subscribePattern("/Patterns/KisPattern", std::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1, _2));
+ m_catcher.subscribeNewStyleStarted(std::bind(&KisAslLayerStyleSerializer::newStyleStarted, this, false));
+
+ KisAslReader reader;
+ QDomDocument doc = reader.readFile(device);
+
+ //dbgKrita << ppVar(doc.toString());
+
+ //KisAslObjectCatcher c2;
+ KisAslXmlParser parser;
+ parser.parseXML(doc, m_catcher);
+
+ // correct all the layer styles
+ Q_FOREACH (KisPSDLayerStyleSP style, m_stylesVector) {
+ FillStylesCorrector::correct(style.data());
+ style->setValid(!style->isEmpty());
+
+ style->setFilename(style->psdUuid() + QString("_style"));
+ }
+
+ m_initialized = true;
+}
+
+void KisAslLayerStyleSerializer::registerPSDPattern(const QDomDocument &doc)
+{
+ KisAslCallbackObjectCatcher catcher;
+ catcher.subscribePattern("/Patterns/KisPattern", std::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1, _2));
+ catcher.subscribePattern("/patterns/KisPattern", std::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1, _2));
+
+ //KisAslObjectCatcher c2;
+ KisAslXmlParser parser;
+ parser.parseXML(doc, catcher);
+}
+
+void KisAslLayerStyleSerializer::readFromPSDXML(const QDomDocument &doc)
+{
+ // The caller prepares the document using th efollowing code
+ //
+ // KisAslReader reader;
+ // QDomDocument doc = reader.readLfx2PsdSection(device);
+
+ m_stylesVector.clear();
+
+ //m_catcher.subscribePattern("/Patterns/KisPattern", std::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1));
+ m_catcher.subscribeNewStyleStarted(std::bind(&KisAslLayerStyleSerializer::newStyleStarted, this, true));
+
+ //KisAslObjectCatcher c2;
+ KisAslXmlParser parser;
+ parser.parseXML(doc, m_catcher);
+
+ // correct all the layer styles
+ Q_FOREACH (KisPSDLayerStyleSP style, m_stylesVector) {
+ FillStylesCorrector::correct(style.data());
+ }
+}
diff --git a/libs/image/kis_asl_layer_style_serializer.h b/libs/image/kis_asl_layer_style_serializer.h
new file mode 100644
index 0000000000..25b89c10f0
--- /dev/null
+++ b/libs/image/kis_asl_layer_style_serializer.h
@@ -0,0 +1,85 @@
+/*
+ * 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_ASL_LAYER_STYLE_SERIALIZER_H
+#define __KIS_ASL_LAYER_STYLE_SERIALIZER_H
+
+#include "kritapsd_export.h"
+
+class QIODevice;
+class KoPattern;
+
+#include "kis_psd_layer_style.h"
+#include "asl/kis_asl_callback_object_catcher.h"
+
+
+class KRITAPSD_EXPORT KisAslLayerStyleSerializer
+{
+public:
+ KisAslLayerStyleSerializer();
+ ~KisAslLayerStyleSerializer();
+
+ void saveToDevice(QIODevice *device);
+ void readFromDevice(QIODevice *device);
+ bool readFromFile(const QString& filename);
+
+ QVector<KisPSDLayerStyleSP> styles() const;
+ void setStyles(const QVector<KisPSDLayerStyleSP> &styles);
+
+ QHash<QString, KoPatternSP> patterns() const;
+ QHash<QString, KisPSDLayerStyleSP> stylesHash();
+
+
+ void registerPSDPattern(const QDomDocument &doc);
+ void readFromPSDXML(const QDomDocument &doc);
+
+ QDomDocument formXmlDocument() const;
+ QDomDocument formPsdXmlDocument() const;
+
+ bool isInitialized() {
+ return m_initialized;
+ }
+
+ bool isValid() {
+ return isInitialized() && m_isValid;
+ }
+
+
+private:
+ void registerPatternObject(const KoPatternSP pattern, const QString& patternUuid);
+
+ void assignPatternObject(const QString &patternUuid,
+ const QString &patternName,
+ boost::function<void (KoPatternSP )> setPattern);
+
+ QVector<KoPatternSP> fetchAllPatterns(KisPSDLayerStyle *style) const;
+
+ void newStyleStarted(bool isPsdStructure);
+ void connectCatcherToStyle(KisPSDLayerStyle *style, const QString &prefix);
+
+private:
+ QHash<QString, KoPatternSP> m_patternsStore;
+
+ KisAslCallbackObjectCatcher m_catcher;
+ QVector<KisPSDLayerStyleSP> m_stylesVector;
+ QHash<QString, KisPSDLayerStyleSP> m_stylesHash;
+ bool m_initialized {false};
+ bool m_isValid {true};
+};
+
+#endif /* __KIS_ASL_LAYER_STYLE_SERIALIZER_H */
diff --git a/libs/image/kis_base_processor.cpp b/libs/image/kis_base_processor.cpp
index 02d1514e41..2dec301342 100644
--- a/libs/image/kis_base_processor.cpp
+++ b/libs/image/kis_base_processor.cpp
@@ -1,191 +1,193 @@
/*
* 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.
*/
#include "kis_base_processor.h"
#include <QString>
#include "kis_bookmarked_configuration_manager.h"
#include "filter/kis_filter_configuration.h"
#include "kis_paint_device.h"
#include "kis_selection.h"
+#include <KisGlobalResourcesInterface.h>
class KisBaseProcessorConfigurationFactory : public KisSerializableConfigurationFactory
{
public:
KisBaseProcessorConfigurationFactory(KisBaseProcessor* _generator) : m_generator(_generator) {}
~KisBaseProcessorConfigurationFactory() override {}
+
KisSerializableConfigurationSP createDefault() override {
- return m_generator->defaultConfiguration();
+ return m_generator->defaultConfiguration(KisGlobalResourcesInterface::instance());
}
KisSerializableConfigurationSP create(const QDomElement& e) override {
- KisSerializableConfigurationSP config = m_generator->factoryConfiguration();
+ KisSerializableConfigurationSP config = m_generator->factoryConfiguration(KisGlobalResourcesInterface::instance());
config->fromXML(e);
return config;
}
private:
KisBaseProcessor* m_generator;
};
struct Q_DECL_HIDDEN KisBaseProcessor::Private {
Private()
: bookmarkManager(0)
, supportsPainting(false)
, supportsAdjustmentLayers(true)
, supportsThreading(true)
, showConfigurationWidget(true)
, colorSpaceIndependence(FULLY_INDEPENDENT) {
}
KisBookmarkedConfigurationManager* bookmarkManager;
KoID id;
KoID category; // The category in the filter menu this filter fits
QString entry; // the i18n'ed accelerated menu text
QKeySequence shortcut;
bool supportsPainting;
bool supportsAdjustmentLayers;
bool supportsThreading;
bool showConfigurationWidget;
ColorSpaceIndependence colorSpaceIndependence;
};
KisBaseProcessor::KisBaseProcessor(const KoID& id, const KoID & category, const QString & entry)
: d(new Private)
{
d->id = id;
d->category = category;
d->entry = entry;
}
void KisBaseProcessor::init(const QString& configEntryGroup)
{
d->bookmarkManager = new KisBookmarkedConfigurationManager(configEntryGroup, new KisBaseProcessorConfigurationFactory(this));
}
KisBaseProcessor::~KisBaseProcessor()
{
delete d->bookmarkManager;
delete d;
}
-KisFilterConfigurationSP KisBaseProcessor::factoryConfiguration() const
+KisFilterConfigurationSP KisBaseProcessor::factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- return new KisFilterConfiguration(id(), 1);
+ return new KisFilterConfiguration(id(), 1, resourcesInterface);
}
-KisFilterConfigurationSP KisBaseProcessor::defaultConfiguration() const
+KisFilterConfigurationSP KisBaseProcessor::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- return factoryConfiguration();
+ return factoryConfiguration(resourcesInterface);
}
KisConfigWidget * KisBaseProcessor::createConfigurationWidget(QWidget *, const KisPaintDeviceSP, bool) const
{
return 0;
}
KisBookmarkedConfigurationManager* KisBaseProcessor::bookmarkManager()
{
return d->bookmarkManager;
}
const KisBookmarkedConfigurationManager* KisBaseProcessor::bookmarkManager() const
{
return d->bookmarkManager;
}
QString KisBaseProcessor::id() const
{
return d->id.id();
}
QString KisBaseProcessor::name() const
{
return d->id.name();
}
KoID KisBaseProcessor::menuCategory() const
{
return d->category;
}
QString KisBaseProcessor::menuEntry() const
{
return d->entry;
}
QKeySequence KisBaseProcessor::shortcut() const
{
return d->shortcut;
}
void KisBaseProcessor::setShortcut(const QKeySequence & shortcut)
{
d->shortcut = shortcut;
}
bool KisBaseProcessor::supportsPainting() const
{
return d->supportsPainting;
}
bool KisBaseProcessor::supportsAdjustmentLayers() const
{
return d->supportsAdjustmentLayers;
}
bool KisBaseProcessor::supportsThreading() const
{
return d->supportsThreading;
}
ColorSpaceIndependence KisBaseProcessor::colorSpaceIndependence() const
{
return d->colorSpaceIndependence;
}
void KisBaseProcessor::setSupportsPainting(bool v)
{
d->supportsPainting = v;
}
void KisBaseProcessor::setSupportsAdjustmentLayers(bool v)
{
d->supportsAdjustmentLayers = v;
}
void KisBaseProcessor::setSupportsThreading(bool v)
{
d->supportsThreading = v;
}
void KisBaseProcessor::setColorSpaceIndependence(ColorSpaceIndependence v)
{
d->colorSpaceIndependence = v;
}
bool KisBaseProcessor::showConfigurationWidget()
{
return d->showConfigurationWidget;
}
void KisBaseProcessor::setShowConfigurationWidget(bool v)
{
d->showConfigurationWidget = v;
}
diff --git a/libs/image/kis_base_processor.h b/libs/image/kis_base_processor.h
index e2062ecc21..671a8c4abf 100644
--- a/libs/image/kis_base_processor.h
+++ b/libs/image/kis_base_processor.h
@@ -1,170 +1,176 @@
/*
* 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_BASE_PROCESSOR_H_
#define _KIS_BASE_PROCESSOR_H_
#include <list>
#include <QString>
#include <klocalizedstring.h>
#include <QKeySequence>
#include "KoID.h"
#include "KoColorSpace.h"
#include "kis_types.h"
#include "kis_shared.h"
#include "kis_image.h"
#include "kritaimage_export.h"
class QWidget;
class KisBookmarkedConfigurationManager;
class KisFilterConfiguration;
class KisConfigWidget;
+class KoResource;
+typedef QSharedPointer<KoResource> KoResourceSP;
+
+class KisResourcesInterface;
+typedef QSharedPointer<KisResourcesInterface> KisResourcesInterfaceSP;
+
/**
* Base class for classes that process areas of pixels.
* Processors can either read in pixels and write out pixels
* or just write out pixels, using a certain set of configuration
* pixels.
*
* in-out processing is typically filtering: @see KisFilter.
* out-only processing is typically generating: @see KisGenerator.
*
* Information about the area that needs to be processed is contained
* @see KisProcessingInformation and @see KisConstProcessingInformation.
*/
class KRITAIMAGE_EXPORT KisBaseProcessor : public KisShared
{
friend class KisBaseProcessorConfigurationFactory;
public:
KisBaseProcessor(const KoID& id, const KoID & category, const QString & entry);
virtual ~KisBaseProcessor();
/**
* Return the configuration set as the default by the user or the default
* configuration from the filter writer as returned by factoryConfiguration.
*
* This configuration is used by default for the configuration widget and
* given to the process function if there is no configuration widget.
*
* @return the default configuration of this widget
*/
- virtual KisFilterConfigurationSP defaultConfiguration() const;
+ virtual KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const;
/**
* @return the bookmark manager for this processor
*/
KisBookmarkedConfigurationManager* bookmarkManager();
/**
* @return the bookmark manager for this processor
*/
const KisBookmarkedConfigurationManager* bookmarkManager() const;
/// @return Unique identification for this processor
QString id() const;
/// @return User-visible identification for this processor
QString name() const;
/// @return the submenu in the filters menu does processor want to go?
KoID menuCategory() const;
/// @return the i18n'ed string this filter wants to show itself in the menu
QString menuEntry() const;
/**
* Return the default keyboard shortcut for activation of this filter
*
* @return the shortcut
*/
QKeySequence shortcut() const;
/**
* Create the configuration widget for this processor.
*
* @param parent the Qt owner widget of this widget
* @param dev the paintdevice this filter will act on
* @param useForMasks shown if the filer is going to be used in a mask. Some filters
* may provide limited options when applied as a mask (e.g. Gaussian Blur)
*/
virtual KisConfigWidget * createConfigurationWidget(QWidget * parent, const KisPaintDeviceSP dev, bool useForMasks) const;
// "Support" functions
public:
/**
* If true, this filter can be used in painting tools as a paint operation
*/
bool supportsPainting() const;
/// This filter can be used in adjustment layers
bool supportsAdjustmentLayers() const;
/**
* This filter supports cutting up the work area and filtering
* each chunk in a separate thread. Filters that need access to the
* whole area for correct computations should return false.
*/
bool supportsThreading() const;
/// If true, the filter wants to show a configuration widget
bool showConfigurationWidget();
/**
* Determine the colorspace independence of this filter.
* @see ColorSpaceIndependence
*
* @return the degree of independence
*/
ColorSpaceIndependence colorSpaceIndependence() const;
/// @return the default configuration object as defined by whoever wrote the plugin.
/// This object must be filled in with fromXML after that.
- virtual KisFilterConfigurationSP factoryConfiguration() const;
+ virtual KisFilterConfigurationSP factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const;
protected:
void setSupportsPainting(bool v);
void setSupportsAdjustmentLayers(bool v);
void setSupportsThreading(bool v);
void setColorSpaceIndependence(ColorSpaceIndependence v);
void setShowConfigurationWidget(bool v);
/**
* Set the default shortcut for activation of this filter.
*/
void setShortcut(const QKeySequence & shortcut);
protected:
void init(const QString& configEntryGroup);
private:
struct Private;
Private* const d;
};
#endif
diff --git a/libs/image/kis_fill_painter.cc b/libs/image/kis_fill_painter.cc
index b38058d5cf..ba8c1db769 100644
--- a/libs/image/kis_fill_painter.cc
+++ b/libs/image/kis_fill_painter.cc
@@ -1,324 +1,324 @@
/*
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2004 Bart Coppens <kde@bartcoppens.be>
* 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.
*/
#include "kis_fill_painter.h"
#include <stdlib.h>
#include <string.h>
#include <cfloat>
#include <stack>
#include <QFontInfo>
#include <QFontMetrics>
#include <QPen>
#include <QMatrix>
#include <QImage>
#include <QMap>
#include <QPainter>
#include <QRect>
#include <QString>
#include <klocalizedstring.h>
#include <KoUpdater.h>
#include "generator/kis_generator.h"
#include "filter/kis_filter_configuration.h"
#include "generator/kis_generator_registry.h"
#include "kis_processing_information.h"
#include "kis_debug.h"
#include "kis_image.h"
#include "kis_layer.h"
#include "kis_paint_device.h"
#include <resources/KoPattern.h>
#include "KoColorSpace.h"
#include "kis_transaction.h"
#include "kis_pixel_selection.h"
#include <KoCompositeOpRegistry.h>
#include <floodfill/kis_scanline_fill.h>
#include "kis_selection_filters.h"
KisFillPainter::KisFillPainter()
: KisPainter()
{
initFillPainter();
}
KisFillPainter::KisFillPainter(KisPaintDeviceSP device)
: KisPainter(device)
{
initFillPainter();
}
KisFillPainter::KisFillPainter(KisPaintDeviceSP device, KisSelectionSP selection)
: KisPainter(device, selection)
{
initFillPainter();
}
void KisFillPainter::initFillPainter()
{
m_width = m_height = -1;
m_careForSelection = false;
m_sizemod = 0;
m_feather = 0;
m_useCompositioning = false;
m_threshold = 0;
}
void KisFillPainter::fillSelection(const QRect &rc, const KoColor &color)
{
KisPaintDeviceSP fillDevice = new KisPaintDevice(device()->colorSpace());
fillDevice->setDefaultPixel(color);
bitBlt(rc.topLeft(), fillDevice, rc);
}
// 'regular' filling
// XXX: This also needs renaming, since filling ought to keep the opacity and the composite op in mind,
// this is more eraseToColor.
void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoColor& kc, quint8 opacity)
{
if (w > 0 && h > 0) {
// Make sure we're in the right colorspace
KoColor kc2(kc); // get rid of const
kc2.convertTo(device()->colorSpace());
quint8 * data = kc2.data();
device()->colorSpace()->setOpacity(data, opacity, 1);
device()->fill(x1, y1, w, h, data);
addDirtyRect(QRect(x1, y1, w, h));
}
}
-void KisFillPainter::fillRect(const QRect &rc, const KoPattern *pattern, const QPoint &offset)
+void KisFillPainter::fillRect(const QRect &rc, const KoPatternSP pattern, const QPoint &offset)
{
fillRect(rc.x(), rc.y(), rc.width(), rc.height(), pattern, offset);
}
-void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoPattern * pattern, const QPoint &offset)
+void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoPatternSP pattern, const QPoint &offset)
{
if (!pattern) return;
if (!pattern->valid()) return;
if (!device()) return;
if (w < 1) return;
if (h < 1) return;
KisPaintDeviceSP patternLayer = new KisPaintDevice(device()->compositionSourceColorSpace(), pattern->name());
patternLayer->convertFromQImage(pattern->pattern(), 0);
if (!offset.isNull()) {
patternLayer->moveTo(offset);
}
fillRect(x1, y1, w, h, patternLayer, QRect(offset.x(), offset.y(), pattern->width(), pattern->height()));
}
void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisPaintDeviceSP device, const QRect& deviceRect)
{
const QRect &patternRect = deviceRect;
const QRect fillRect(x1, y1, w, h);
auto toPatternLocal = [](int value, int offset, int width) {
const int normalizedValue = value - offset;
return offset + (normalizedValue >= 0 ?
normalizedValue % width :
width - (-normalizedValue - 1) % width - 1);
};
int dstY = fillRect.y();
while (dstY <= fillRect.bottom()) {
const int dstRowsRemaining = fillRect.bottom() - dstY + 1;
const int srcY = toPatternLocal(dstY, patternRect.y(), patternRect.height());
const int height = qMin(patternRect.height() - srcY + patternRect.y(), dstRowsRemaining);
int dstX = fillRect.x();
while (dstX <= fillRect.right()) {
const int dstColumnsRemaining = fillRect.right() - dstX + 1;
const int srcX = toPatternLocal(dstX, patternRect.x(), patternRect.width());
const int width = qMin(patternRect.width() - srcX + patternRect.x(), dstColumnsRemaining);
bitBlt(dstX, dstY, device, srcX, srcY, width, height);
dstX += width;
}
dstY += height;
}
addDirtyRect(QRect(x1, y1, w, h));
}
void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisFilterConfigurationSP generator)
{
if (!generator) return;
KisGeneratorSP g = KisGeneratorRegistry::instance()->value(generator->name());
if (!device()) return;
if (w < 1) return;
if (h < 1) return;
QRect tmpRc(x1, y1, w, h);
KisProcessingInformation dstCfg(device(), tmpRc.topLeft(), 0);
g->generate(dstCfg, tmpRc.size(), generator);
addDirtyRect(tmpRc);
}
// flood filling
void KisFillPainter::fillColor(int startX, int startY, KisPaintDeviceSP sourceDevice)
{
if (!m_useCompositioning) {
if (m_sizemod || m_feather ||
compositeOp()->id() != COMPOSITE_OVER ||
opacity() != MAX_SELECTED ||
sourceDevice != device()) {
warnKrita << "WARNING: Fast Flood Fill (no compositioning mode)"
<< "does not support compositeOps, opacity, "
<< "selection enhancements and separate source "
<< "devices";
}
QRect fillBoundsRect(0, 0, m_width, m_height);
QPoint startPoint(startX, startY);
if (!fillBoundsRect.contains(startPoint)) return;
KisScanlineFill gc(device(), startPoint, fillBoundsRect);
gc.setThreshold(m_threshold);
gc.fillColor(paintColor());
} else {
genericFillStart(startX, startY, sourceDevice);
// Now create a layer and fill it
KisPaintDeviceSP filled = device()->createCompositionSourceDevice();
Q_CHECK_PTR(filled);
KisFillPainter painter(filled);
painter.fillRect(0, 0, m_width, m_height, paintColor());
painter.end();
genericFillEnd(filled);
}
}
void KisFillPainter::fillPattern(int startX, int startY, KisPaintDeviceSP sourceDevice)
{
genericFillStart(startX, startY, sourceDevice);
// Now create a layer and fill it
KisPaintDeviceSP filled = device()->createCompositionSourceDevice();
Q_CHECK_PTR(filled);
KisFillPainter painter(filled);
painter.fillRect(0, 0, m_width, m_height, pattern());
painter.end();
genericFillEnd(filled);
}
void KisFillPainter::genericFillStart(int startX, int startY, KisPaintDeviceSP sourceDevice)
{
Q_ASSERT(m_width > 0);
Q_ASSERT(m_height > 0);
// Create a selection from the surrounding area
m_fillSelection = createFloodSelection(startX, startY, sourceDevice);
}
void KisFillPainter::genericFillEnd(KisPaintDeviceSP filled)
{
if (progressUpdater() && progressUpdater()->interrupted()) {
m_width = m_height = -1;
return;
}
// TODO: filling using the correct bound of the selection would be better, *but*
// the selection is limited to the exact bound of a layer, while in reality, we don't
// want that, since we want a transparent layer to be completely filled
// QRect rc = m_fillSelection->selectedExactRect();
/**
* Apply the real selection to a filled one
*/
KisSelectionSP realSelection = selection();
if (realSelection) {
m_fillSelection->pixelSelection()->applySelection(
realSelection->projection(), SELECTION_INTERSECT);
}
setSelection(m_fillSelection);
bitBlt(0, 0, filled, 0, 0, m_width, m_height);
setSelection(realSelection);
if (progressUpdater()) progressUpdater()->setProgress(100);
m_width = m_height = -1;
}
KisSelectionSP KisFillPainter::createFloodSelection(int startX, int startY, KisPaintDeviceSP sourceDevice)
{
if (m_width < 0 || m_height < 0) {
if (selection() && m_careForSelection) {
QRect rc = selection()->selectedExactRect();
m_width = rc.width() - (startX - rc.x());
m_height = rc.height() - (startY - rc.y());
}
}
dbgImage << "Width: " << m_width << " Height: " << m_height;
// Otherwise the width and height should have been set
Q_ASSERT(m_width > 0 && m_height > 0);
QRect fillBoundsRect(0, 0, m_width, m_height);
QPoint startPoint(startX, startY);
KisSelectionSP selection = new KisSelection(new KisSelectionDefaultBounds(device()));
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
if (!fillBoundsRect.contains(startPoint)) {
return selection;
}
KisScanlineFill gc(sourceDevice, startPoint, fillBoundsRect);
gc.setThreshold(m_threshold);
gc.fillSelection(pixelSelection);
if (m_sizemod > 0) {
KisGrowSelectionFilter biggy(m_sizemod, m_sizemod);
biggy.process(pixelSelection, selection->selectedRect().adjusted(-m_sizemod, -m_sizemod, m_sizemod, m_sizemod));
}
else if (m_sizemod < 0) {
KisShrinkSelectionFilter tiny(-m_sizemod, -m_sizemod, false);
tiny.process(pixelSelection, selection->selectedRect());
}
if (m_feather > 0) {
KisFeatherSelectionFilter feathery(m_feather);
feathery.process(pixelSelection, selection->selectedRect().adjusted(-m_feather, -m_feather, m_feather, m_feather));
}
return selection;
}
diff --git a/libs/image/kis_fill_painter.h b/libs/image/kis_fill_painter.h
index 79107704c3..8e04c41caa 100644
--- a/libs/image/kis_fill_painter.h
+++ b/libs/image/kis_fill_painter.h
@@ -1,281 +1,282 @@
/*
* 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 <KoColor.h>
+#include <KoColorSpaceRegistry.h>
+#include <KoPattern.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());
+ void fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoPatternSP 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());
+ void fillRect(const QRect& rc, const KoPatternSP 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 @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_mask.cpp b/libs/image/kis_filter_mask.cpp
index c74d5cae8d..07600f7339 100644
--- a/libs/image/kis_filter_mask.cpp
+++ b/libs/image/kis_filter_mask.cpp
@@ -1,179 +1,179 @@
/*
* 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 <KoIcon.h>
#include <kis_icon.h>
#include <KoCompositeOpRegistry.h>
#include "kis_layer.h"
#include "kis_filter_mask.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_selection.h"
#include "kis_processing_information.h"
#include "kis_node.h"
#include "kis_node_visitor.h"
#include "kis_processing_visitor.h"
#include "kis_busy_progress_indicator.h"
#include "kis_transaction.h"
#include "kis_painter.h"
KisFilterMask::KisFilterMask()
: KisEffectMask(),
- KisNodeFilterInterface(0, false)
+ KisNodeFilterInterface(0)
{
setCompositeOpId(COMPOSITE_COPY);
}
KisFilterMask::~KisFilterMask()
{
}
KisFilterMask::KisFilterMask(const KisFilterMask& rhs)
: KisEffectMask(rhs)
, KisNodeFilterInterface(rhs)
{
}
QIcon KisFilterMask::icon() const
{
return KisIconUtils::loadIcon("filterMask");
}
void KisFilterMask::setFilter(KisFilterConfigurationSP filterConfig)
{
KisNodeFilterInterface::setFilter(filterConfig);
}
QRect KisFilterMask::decorateRect(KisPaintDeviceSP &src,
KisPaintDeviceSP &dst,
const QRect & rc,
PositionToFilthy maskPos) const
{
Q_UNUSED(maskPos);
KisFilterConfigurationSP filterConfig = filter();
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(nodeProgressProxy(), rc);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(
src != dst &&
"KisFilterMask::decorateRect: "
"src must be != dst, because we can't create transactions "
"during merge, as it breaks reentrancy",
rc);
if (!filterConfig) {
return QRect();
}
KisFilterSP filter =
KisFilterRegistry::instance()->value(filterConfig->name());
if (!filter) {
warnKrita << "Could not retrieve filter \"" << filterConfig->name() << "\"";
return QRect();
}
KIS_ASSERT_RECOVER_NOOP(this->busyProgressIndicator());
this->busyProgressIndicator()->update();
filter->process(src, dst, 0, rc, filterConfig.data(), 0);
QRect r = filter->changedRect(rc, filterConfig.data(), dst->defaultBounds()->currentLevelOfDetail());
return r;
}
bool KisFilterMask::accept(KisNodeVisitor &v)
{
return v.visit(this);
}
void KisFilterMask::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
{
return visitor.visit(this, undoAdapter);
}
/**
* FIXME: try to cache filter pointer inside a Private block
*/
QRect KisFilterMask::changeRect(const QRect &rect, PositionToFilthy pos) const
{
/**
* FIXME: This check of the emptiness should be done
* on the higher/lower level
*/
if(rect.isEmpty()) return rect;
QRect filteredRect = rect;
KisFilterConfigurationSP filterConfig = filter();
if (filterConfig) {
KisNodeSP parent = this->parent();
const int lod = parent && parent->projection() ?
parent->projection()->defaultBounds()->currentLevelOfDetail() : 0;
KisFilterSP filter = KisFilterRegistry::instance()->value(filterConfig->name());
filteredRect = filter->changedRect(rect, filterConfig.data(), lod);
}
/**
* We can't paint outside a selection, that is why we call
* KisMask::changeRect to crop actual change area in the end
*/
filteredRect = KisMask::changeRect(filteredRect, pos);
/**
* FIXME: Think over this solution
* Union of rects means that changeRect returns NOT the rect
* changed by this very layer, but an accumulated rect changed
* by all underlying layers. Just take into account and change
* documentation accordingly
*/
return rect | filteredRect;
}
QRect KisFilterMask::needRect(const QRect& rect, PositionToFilthy pos) const
{
Q_UNUSED(pos);
/**
* FIXME: This check of the emptiness should be done
* on the higher/lower level
*/
if(rect.isEmpty()) return rect;
KisFilterConfigurationSP filterConfig = filter();
if (!filterConfig) return rect;
KisNodeSP parent = this->parent();
const int lod = parent && parent->projection() ?
parent->projection()->defaultBounds()->currentLevelOfDetail() : 0;
KisFilterSP filter = KisFilterRegistry::instance()->value(filterConfig->name());
/**
* If we need some additional pixels even outside of a selection
* for accurate layer filtering, we'll get them!
* And no KisMask::needRect will prevent us from doing this! ;)
* That's why simply we do not call KisMask::needRect here :)
*/
return filter->neededRect(rect, filterConfig.data(), lod);
}
diff --git a/libs/image/kis_gradient_painter.cc b/libs/image/kis_gradient_painter.cc
index a1486d01fe..fd2ff9e584 100644
--- a/libs/image/kis_gradient_painter.cc
+++ b/libs/image/kis_gradient_painter.cc
@@ -1,891 +1,893 @@
/*
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2019 Miguel Lopez <reptillia39@live.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_gradient_painter.h"
#include <cfloat>
#include <KoColorSpace.h>
#include <resources/KoAbstractGradient.h>
#include <KoUpdater.h>
+#include <KoEphemeralResource.h>
#include "kis_global.h"
#include "kis_paint_device.h"
#include <resources/KoPattern.h>
#include "kis_selection.h"
#include <KisSequentialIteratorProgress.h>
#include "kis_image.h"
#include "kis_random_accessor_ng.h"
#include "kis_gradient_shape_strategy.h"
#include "kis_polygonal_gradient_shape_strategy.h"
#include "kis_cached_gradient_shape_strategy.h"
#include "krita_utils.h"
-class CachedGradient : public KoAbstractGradient
+class CachedGradient : public KoEphemeralResource<KoAbstractGradient>
{
public:
- explicit CachedGradient(const KoAbstractGradient *gradient, qint32 steps, const KoColorSpace *cs)
- : KoAbstractGradient(gradient->filename())
+ explicit CachedGradient(const KoAbstractGradientSP gradient, qint32 steps, const KoColorSpace *cs)
+ : KoEphemeralResource<KoAbstractGradient>(gradient->filename())
+ , m_subject(gradient)
+ , m_max(steps - 1)
+ , m_colorSpace(cs)
+ , m_black(KoColor(cs))
{
- m_subject = gradient;
- m_max = steps - 1;
- m_colorSpace = cs;
-
- m_black = KoColor(cs);
-
KoColor tmpColor(m_colorSpace);
for(qint32 i = 0; i < steps; i++) {
m_subject->colorAt(tmpColor, qreal(i) / m_max);
m_colors << tmpColor;
}
}
~CachedGradient() override {}
- KoAbstractGradient* clone() const override {
- return new CachedGradient(m_subject, m_max + 1, m_colorSpace);
+ KoResourceSP clone() const override {
+ return KoResourceSP(new CachedGradient(m_subject, m_max + 1, m_colorSpace));
}
/**
* Creates a QGradient from the gradient.
* The resulting QGradient might differ from original gradient
*/
QGradient* toQGradient() const override
{
return m_subject->toQGradient();
}
+ QPair<QString, QString> resourceType() const override {
+ return m_subject->resourceType();
+ }
/// gets the color data at position 0 <= t <= 1
const quint8 *cachedAt(qreal t) const
{
qint32 tInt = t * m_max + 0.5;
if (m_colors.size() > tInt) {
return m_colors[tInt].data();
}
else {
return m_black.data();
}
}
void setColorSpace(KoColorSpace* colorSpace) { m_colorSpace = colorSpace; }
const KoColorSpace * colorSpace() const { return m_colorSpace; }
QByteArray generateMD5() const override { return QByteArray(); }
private:
- const KoAbstractGradient *m_subject;
- const KoColorSpace *m_colorSpace;
+ const KoAbstractGradientSP m_subject;
qint32 m_max;
+ const KoColorSpace *m_colorSpace;
QVector<KoColor> m_colors;
KoColor m_black;
};
namespace
{
class LinearGradientStrategy : public KisGradientShapeStrategy
{
public:
LinearGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
double valueAt(double x, double y) const override;
protected:
double m_normalisedVectorX;
double m_normalisedVectorY;
double m_vectorLength;
};
LinearGradientStrategy::LinearGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
: KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
{
double dx = gradientVectorEnd.x() - gradientVectorStart.x();
double dy = gradientVectorEnd.y() - gradientVectorStart.y();
m_vectorLength = sqrt((dx * dx) + (dy * dy));
if (m_vectorLength < DBL_EPSILON) {
m_normalisedVectorX = 0;
m_normalisedVectorY = 0;
} else {
m_normalisedVectorX = dx / m_vectorLength;
m_normalisedVectorY = dy / m_vectorLength;
}
}
double LinearGradientStrategy::valueAt(double x, double y) const
{
double vx = x - m_gradientVectorStart.x();
double vy = y - m_gradientVectorStart.y();
// Project the vector onto the normalised gradient vector.
double t = vx * m_normalisedVectorX + vy * m_normalisedVectorY;
if (m_vectorLength < DBL_EPSILON) {
t = 0;
} else {
// Scale to 0 to 1 over the gradient vector length.
t /= m_vectorLength;
}
return t;
}
class BiLinearGradientStrategy : public LinearGradientStrategy
{
public:
BiLinearGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
double valueAt(double x, double y) const override;
};
BiLinearGradientStrategy::BiLinearGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
: LinearGradientStrategy(gradientVectorStart, gradientVectorEnd)
{
}
double BiLinearGradientStrategy::valueAt(double x, double y) const
{
double t = LinearGradientStrategy::valueAt(x, y);
// Reflect
if (t < -DBL_EPSILON) {
t = -t;
}
return t;
}
class RadialGradientStrategy : public KisGradientShapeStrategy
{
public:
RadialGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
double valueAt(double x, double y) const override;
protected:
double m_radius;
};
RadialGradientStrategy::RadialGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
: KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
{
double dx = gradientVectorEnd.x() - gradientVectorStart.x();
double dy = gradientVectorEnd.y() - gradientVectorStart.y();
m_radius = sqrt((dx * dx) + (dy * dy));
}
double RadialGradientStrategy::valueAt(double x, double y) const
{
double dx = x - m_gradientVectorStart.x();
double dy = y - m_gradientVectorStart.y();
double distance = sqrt((dx * dx) + (dy * dy));
double t;
if (m_radius < DBL_EPSILON) {
t = 0;
} else {
t = distance / m_radius;
}
return t;
}
class SquareGradientStrategy : public KisGradientShapeStrategy
{
public:
SquareGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
double valueAt(double x, double y) const override;
protected:
double m_normalisedVectorX;
double m_normalisedVectorY;
double m_vectorLength;
};
SquareGradientStrategy::SquareGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
: KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
{
double dx = gradientVectorEnd.x() - gradientVectorStart.x();
double dy = gradientVectorEnd.y() - gradientVectorStart.y();
m_vectorLength = sqrt((dx * dx) + (dy * dy));
if (m_vectorLength < DBL_EPSILON) {
m_normalisedVectorX = 0;
m_normalisedVectorY = 0;
} else {
m_normalisedVectorX = dx / m_vectorLength;
m_normalisedVectorY = dy / m_vectorLength;
}
}
double SquareGradientStrategy::valueAt(double x, double y) const
{
double px = x - m_gradientVectorStart.x();
double py = y - m_gradientVectorStart.y();
double distance1 = 0;
double distance2 = 0;
if (m_vectorLength > DBL_EPSILON) {
// Point to line distance is:
// distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / m_vectorLength;
//
// Here l0 = (0, 0) and |l1 - l0| = 1
distance1 = -m_normalisedVectorY * px + m_normalisedVectorX * py;
distance1 = fabs(distance1);
// Rotate point by 90 degrees and get the distance to the perpendicular
distance2 = -m_normalisedVectorY * -py + m_normalisedVectorX * px;
distance2 = fabs(distance2);
}
double t = qMax(distance1, distance2) / m_vectorLength;
return t;
}
class ConicalGradientStrategy : public KisGradientShapeStrategy
{
public:
ConicalGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
double valueAt(double x, double y) const override;
protected:
double m_vectorAngle;
};
ConicalGradientStrategy::ConicalGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
: KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
{
double dx = gradientVectorEnd.x() - gradientVectorStart.x();
double dy = gradientVectorEnd.y() - gradientVectorStart.y();
// Get angle from 0 to 2 PI.
m_vectorAngle = atan2(dy, dx) + M_PI;
}
double ConicalGradientStrategy::valueAt(double x, double y) const
{
double px = x - m_gradientVectorStart.x();
double py = y - m_gradientVectorStart.y();
double angle = atan2(py, px) + M_PI;
angle -= m_vectorAngle;
if (angle < 0) {
angle += 2 * M_PI;
}
double t = angle / (2 * M_PI);
return t;
}
class ConicalSymetricGradientStrategy : public KisGradientShapeStrategy
{
public:
ConicalSymetricGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
double valueAt(double x, double y) const override;
protected:
double m_vectorAngle;
};
ConicalSymetricGradientStrategy::ConicalSymetricGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
: KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
{
double dx = gradientVectorEnd.x() - gradientVectorStart.x();
double dy = gradientVectorEnd.y() - gradientVectorStart.y();
// Get angle from 0 to 2 PI.
m_vectorAngle = atan2(dy, dx) + M_PI;
}
double ConicalSymetricGradientStrategy::valueAt(double x, double y) const
{
double px = x - m_gradientVectorStart.x();
double py = y - m_gradientVectorStart.y();
double angle = atan2(py, px) + M_PI;
angle -= m_vectorAngle;
if (angle < 0) {
angle += 2 * M_PI;
}
double t;
if (angle < M_PI) {
t = angle / M_PI;
} else {
t = 1 - ((angle - M_PI) / M_PI);
}
return t;
}
class SpiralGradientStrategy : public KisGradientShapeStrategy
{
public:
SpiralGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
double valueAt(double x, double y) const override;
protected:
double m_vectorAngle;
double m_radius;
};
SpiralGradientStrategy::SpiralGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
: KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
{
double dx = gradientVectorEnd.x() - gradientVectorStart.x();
double dy = gradientVectorEnd.y() - gradientVectorStart.y();
// Get angle from 0 to 2 PI.
m_vectorAngle = atan2(dy, dx) + M_PI;
m_radius = sqrt((dx * dx) + (dy * dy));
};
double SpiralGradientStrategy::valueAt(double x, double y) const
{
double dx = x - m_gradientVectorStart.x();
double dy = y - m_gradientVectorStart.y();
double distance = sqrt((dx * dx) + (dy * dy));
double angle = atan2(dy, dx) + M_PI;
double t;
angle -= m_vectorAngle;
if (m_radius < DBL_EPSILON) {
t = 0;
} else {
t = distance / m_radius;
}
if (angle < 0) {
angle += 2 * M_PI;
}
t += angle / (2 * M_PI);
return t;
};
class ReverseSpiralGradientStrategy : public KisGradientShapeStrategy
{
public:
ReverseSpiralGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd);
double valueAt(double x, double y) const override;
protected:
double m_vectorAngle;
double m_radius;
};
ReverseSpiralGradientStrategy::ReverseSpiralGradientStrategy(const QPointF& gradientVectorStart, const QPointF& gradientVectorEnd)
: KisGradientShapeStrategy(gradientVectorStart, gradientVectorEnd)
{
double dx = gradientVectorEnd.x() - gradientVectorStart.x();
double dy = gradientVectorEnd.y() - gradientVectorStart.y();
// Get angle from 0 to 2 PI.
m_vectorAngle = atan2(dy, dx) + M_PI;
m_radius = sqrt((dx * dx) + (dy * dy));
};
double ReverseSpiralGradientStrategy::valueAt(double x, double y) const
{
double dx = x - m_gradientVectorStart.x();
double dy = y - m_gradientVectorStart.y();
double distance = sqrt((dx * dx) + (dy * dy));
double angle = atan2(dy, dx) + M_PI;
double t;
angle -= m_vectorAngle;
if (m_radius < DBL_EPSILON) {
t = 0;
} else {
t = distance / m_radius;
}
if (angle < 0) {
angle += 2 * M_PI;
}
//Reverse direction of spiral gradient
t += 1 - (angle / (2 * M_PI));
return t;
};
class GradientRepeatStrategy
{
public:
GradientRepeatStrategy() {}
virtual ~GradientRepeatStrategy() {}
virtual double valueAt(double t) const = 0;
};
class GradientRepeatNoneStrategy : public GradientRepeatStrategy
{
public:
static GradientRepeatNoneStrategy *instance();
double valueAt(double t) const override;
private:
GradientRepeatNoneStrategy() {}
static GradientRepeatNoneStrategy *m_instance;
};
GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::m_instance = 0;
GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::instance()
{
if (m_instance == 0) {
m_instance = new GradientRepeatNoneStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
// Output is clamped to 0 to 1.
double GradientRepeatNoneStrategy::valueAt(double t) const
{
double value = t;
if (t < DBL_EPSILON) {
value = 0;
} else if (t > 1 - DBL_EPSILON) {
value = 1;
}
return value;
}
class GradientRepeatForwardsStrategy : public GradientRepeatStrategy
{
public:
static GradientRepeatForwardsStrategy *instance();
double valueAt(double t) const override;
private:
GradientRepeatForwardsStrategy() {}
static GradientRepeatForwardsStrategy *m_instance;
};
GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::m_instance = 0;
GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::instance()
{
if (m_instance == 0) {
m_instance = new GradientRepeatForwardsStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
// Output is 0 to 1, 0 to 1, 0 to 1...
double GradientRepeatForwardsStrategy::valueAt(double t) const
{
int i = static_cast<int>(t);
if (t < DBL_EPSILON) {
i--;
}
double value = t - i;
return value;
}
class GradientRepeatAlternateStrategy : public GradientRepeatStrategy
{
public:
static GradientRepeatAlternateStrategy *instance();
double valueAt(double t) const override;
private:
GradientRepeatAlternateStrategy() {}
static GradientRepeatAlternateStrategy *m_instance;
};
GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::m_instance = 0;
GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::instance()
{
if (m_instance == 0) {
m_instance = new GradientRepeatAlternateStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
// Output is 0 to 1, 1 to 0, 0 to 1, 1 to 0...
double GradientRepeatAlternateStrategy::valueAt(double t) const
{
if (t < 0) {
t = -t;
}
int i = static_cast<int>(t);
double value = t - i;
if (i % 2 == 1) {
value = 1 - value;
}
return value;
}
//Had to create this class to solve alternating mode for cases where values should be repeated for every HalfValues like for example, spirals...
class GradientRepeatModuloDivisiveContinuousHalfStrategy : public GradientRepeatStrategy
{
public:
static GradientRepeatModuloDivisiveContinuousHalfStrategy *instance();
double valueAt(double t) const override;
private:
GradientRepeatModuloDivisiveContinuousHalfStrategy() {}
static GradientRepeatModuloDivisiveContinuousHalfStrategy *m_instance;
};
GradientRepeatModuloDivisiveContinuousHalfStrategy *GradientRepeatModuloDivisiveContinuousHalfStrategy::m_instance = 0;
GradientRepeatModuloDivisiveContinuousHalfStrategy *GradientRepeatModuloDivisiveContinuousHalfStrategy::instance()
{
if (m_instance == 0) {
m_instance = new GradientRepeatModuloDivisiveContinuousHalfStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
// Output is 0 to 1, 1 to 0, 0 to 1, 1 to 0 per HalfValues
double GradientRepeatModuloDivisiveContinuousHalfStrategy::valueAt(double t) const
{
if (t < 0) {
t = -t;
}
int i = static_cast<int>(t*2);
int ti = static_cast<int>(t);
double value = t - ti;
if (i % 2 == 1) {
value = 1 - value;
}
return value*2;
}
}
struct Q_DECL_HIDDEN KisGradientPainter::Private
{
enumGradientShape shape;
struct ProcessRegion {
ProcessRegion() {}
ProcessRegion(QSharedPointer<KisGradientShapeStrategy> _precalculatedShapeStrategy,
const QRect &_processRect)
: precalculatedShapeStrategy(_precalculatedShapeStrategy),
processRect(_processRect) {}
QSharedPointer<KisGradientShapeStrategy> precalculatedShapeStrategy;
QRect processRect;
};
QVector<ProcessRegion> processRegions;
};
KisGradientPainter::KisGradientPainter()
: m_d(new Private())
{
}
KisGradientPainter::KisGradientPainter(KisPaintDeviceSP device)
: KisPainter(device),
m_d(new Private())
{
}
KisGradientPainter::KisGradientPainter(KisPaintDeviceSP device, KisSelectionSP selection)
: KisPainter(device, selection),
m_d(new Private())
{
}
KisGradientPainter::~KisGradientPainter()
{
}
void KisGradientPainter::setGradientShape(enumGradientShape shape)
{
m_d->shape = shape;
}
KisGradientShapeStrategy* createPolygonShapeStrategy(const QPainterPath &path, const QRect &boundingRect)
{
// TODO: implement UI for exponent option
const qreal exponent = 2.0;
KisGradientShapeStrategy *strategy =
new KisPolygonalGradientShapeStrategy(path, exponent);
KIS_ASSERT_RECOVER_NOOP(boundingRect.width() >= 3 &&
boundingRect.height() >= 3);
const qreal step =
qMin(qreal(8.0), KritaUtils::maxDimensionPortion(boundingRect, 0.01, 2));
return new KisCachedGradientShapeStrategy(boundingRect, step, step, strategy);
}
/**
* TODO: make this call happen asynchronously when the user does nothing
*/
void KisGradientPainter::precalculateShape()
{
if (!m_d->processRegions.isEmpty()) return;
QPainterPath path;
if (selection()) {
if (!selection()->outlineCacheValid()) {
selection()->recalculateOutlineCache();
}
KIS_ASSERT_RECOVER_RETURN(selection()->outlineCacheValid());
KIS_ASSERT_RECOVER_RETURN(!selection()->outlineCache().isEmpty());
path = selection()->outlineCache();
} else {
path.addRect(device()->defaultBounds()->bounds());
}
QList<QPainterPath> splitPaths = KritaUtils::splitDisjointPaths(path);
Q_FOREACH (const QPainterPath &subpath, splitPaths) {
QRect boundingRect = subpath.boundingRect().toAlignedRect();
if (boundingRect.width() < 3 || boundingRect.height() < 3) {
boundingRect = kisGrowRect(boundingRect, 2);
}
Private::ProcessRegion r(toQShared(createPolygonShapeStrategy(subpath, boundingRect)),
boundingRect);
m_d->processRegions << r;
}
}
bool KisGradientPainter::paintGradient(const QPointF& gradientVectorStart,
const QPointF& gradientVectorEnd,
enumGradientRepeat repeat,
double antiAliasThreshold,
bool reverseGradient,
qint32 startx,
qint32 starty,
qint32 width,
qint32 height)
{
return paintGradient(gradientVectorStart,
gradientVectorEnd,
repeat,
antiAliasThreshold,
reverseGradient,
QRect(startx, starty, width, height));
}
bool KisGradientPainter::paintGradient(const QPointF& gradientVectorStart,
const QPointF& gradientVectorEnd,
enumGradientRepeat repeat,
double antiAliasThreshold,
bool reverseGradient,
const QRect &applyRect)
{
Q_UNUSED(antiAliasThreshold);
if (!gradient()) return false;
QRect requestedRect = applyRect;
//If the device has a selection only iterate over that selection united with our area of interest
if (selection()) {
requestedRect &= selection()->selectedExactRect();
}
QSharedPointer<KisGradientShapeStrategy> shapeStrategy;
switch (m_d->shape) {
case GradientShapeLinear: {
Private::ProcessRegion r(toQShared(new LinearGradientStrategy(gradientVectorStart, gradientVectorEnd)),
requestedRect);
m_d->processRegions.clear();
m_d->processRegions << r;
break;
}
case GradientShapeBiLinear: {
Private::ProcessRegion r(toQShared(new BiLinearGradientStrategy(gradientVectorStart, gradientVectorEnd)),
requestedRect);
m_d->processRegions.clear();
m_d->processRegions << r;
break;
}
case GradientShapeRadial: {
Private::ProcessRegion r(toQShared(new RadialGradientStrategy(gradientVectorStart, gradientVectorEnd)),
requestedRect);
m_d->processRegions.clear();
m_d->processRegions << r;
break;
}
case GradientShapeSquare: {
Private::ProcessRegion r(toQShared(new SquareGradientStrategy(gradientVectorStart, gradientVectorEnd)),
requestedRect);
m_d->processRegions.clear();
m_d->processRegions << r;
break;
}
case GradientShapeConical: {
Private::ProcessRegion r(toQShared(new ConicalGradientStrategy(gradientVectorStart, gradientVectorEnd)),
requestedRect);
m_d->processRegions.clear();
m_d->processRegions << r;
break;
}
case GradientShapeConicalSymetric: {
Private::ProcessRegion r(toQShared(new ConicalSymetricGradientStrategy(gradientVectorStart, gradientVectorEnd)),
requestedRect);
m_d->processRegions.clear();
m_d->processRegions << r;
break;
}
case GradientShapeSpiral: {
Private::ProcessRegion r(toQShared(new SpiralGradientStrategy(gradientVectorStart, gradientVectorEnd)),
requestedRect);
m_d->processRegions.clear();
m_d->processRegions << r;
break;
}
case GradientShapeReverseSpiral: {
Private::ProcessRegion r(toQShared(new ReverseSpiralGradientStrategy(gradientVectorStart, gradientVectorEnd)),
requestedRect);
m_d->processRegions.clear();
m_d->processRegions << r;
break;
}
case GradientShapePolygonal:
precalculateShape();
repeat = GradientRepeatNone;
break;
}
GradientRepeatStrategy *repeatStrategy = 0;
switch (repeat) {
case GradientRepeatNone:
repeatStrategy = GradientRepeatNoneStrategy::instance();
break;
case GradientRepeatForwards:
repeatStrategy = GradientRepeatForwardsStrategy::instance();
break;
case GradientRepeatAlternate:
if (m_d->shape == GradientShapeSpiral || m_d->shape == GradientShapeReverseSpiral) {repeatStrategy = GradientRepeatModuloDivisiveContinuousHalfStrategy::instance();}
else {repeatStrategy = GradientRepeatAlternateStrategy::instance();}
break;
}
Q_ASSERT(repeatStrategy != 0);
KisPaintDeviceSP dev = device()->createCompositionSourceDevice();
const KoColorSpace * colorSpace = dev->colorSpace();
const qint32 pixelSize = colorSpace->pixelSize();
Q_FOREACH (const Private::ProcessRegion &r, m_d->processRegions) {
QRect processRect = r.processRect;
QSharedPointer<KisGradientShapeStrategy> shapeStrategy = r.precalculatedShapeStrategy;
CachedGradient cachedGradient(gradient(), qMax(processRect.width(), processRect.height()), colorSpace);
KisSequentialIteratorProgress it(dev, processRect, progressUpdater());
while (it.nextPixel()) {
double t = shapeStrategy->valueAt(it.x(), it.y());
t = repeatStrategy->valueAt(t);
if (reverseGradient) {
t = 1 - t;
}
memcpy(it.rawData(), cachedGradient.cachedAt(t), pixelSize);
}
bitBlt(processRect.topLeft(), dev, processRect);
}
return true;
}
diff --git a/libs/image/kis_layer.cc b/libs/image/kis_layer.cc
index 609afb26f5..a283371fbd 100644
--- a/libs/image/kis_layer.cc
+++ b/libs/image/kis_layer.cc
@@ -1,1006 +1,1006 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* 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.
*/
#include "kis_layer.h"
#include <klocalizedstring.h>
#include <QImage>
#include <QBitArray>
#include <QStack>
#include <QMutex>
#include <QMutexLocker>
#include <QReadWriteLock>
#include <QReadLocker>
#include <QWriteLocker>
#include <KoIcon.h>
#include <kis_icon.h>
#include <KoProperties.h>
#include <KoCompositeOpRegistry.h>
#include <KoColorSpace.h>
#include "kis_debug.h"
#include "kis_image.h"
#include "kis_painter.h"
#include "kis_mask.h"
#include "kis_effect_mask.h"
#include "kis_selection_mask.h"
#include "kis_meta_data_store.h"
#include "kis_selection.h"
#include "kis_paint_layer.h"
#include "kis_raster_keyframe_channel.h"
#include "kis_clone_layer.h"
#include "kis_psd_layer_style.h"
#include "kis_layer_projection_plane.h"
#include "layerstyles/kis_layer_style_projection_plane.h"
#include "krita_utils.h"
#include "kis_layer_properties_icons.h"
#include "kis_layer_utils.h"
#include "kis_projection_leaf.h"
#include "KisSafeNodeProjectionStore.h"
class KisCloneLayersList {
public:
void addClone(KisCloneLayerWSP cloneLayer) {
m_clonesList.append(cloneLayer);
}
void removeClone(KisCloneLayerWSP cloneLayer) {
m_clonesList.removeOne(cloneLayer);
}
void setDirty(const QRect &rect) {
Q_FOREACH (KisCloneLayerSP clone, m_clonesList) {
if (clone) {
clone->setDirtyOriginal(rect);
}
}
}
const QList<KisCloneLayerWSP> registeredClones() const {
return m_clonesList;
}
bool hasClones() const {
return !m_clonesList.isEmpty();
}
private:
QList<KisCloneLayerWSP> m_clonesList;
};
class KisLayerMasksCache {
public:
KisLayerMasksCache(KisLayer *parent)
: m_parent(parent)
{
}
KisSelectionMaskSP selectionMask() {
QReadLocker readLock(&m_lock);
if (!m_isSelectionMaskValid) {
readLock.unlock();
QWriteLocker writeLock(&m_lock);
if (!m_isSelectionMaskValid) {
KoProperties properties;
properties.setProperty("active", true);
properties.setProperty("visible", true);
QList<KisNodeSP> masks = m_parent->childNodes(QStringList("KisSelectionMask"), properties);
// return the first visible mask
Q_FOREACH (KisNodeSP mask, masks) {
if (mask) {
m_selectionMask = dynamic_cast<KisSelectionMask*>(mask.data());
break;
}
}
m_isSelectionMaskValid = true;
}
// return under write lock
return m_selectionMask;
}
// return under read lock
return m_selectionMask;
}
QList<KisEffectMaskSP> effectMasks() {
QReadLocker readLock(&m_lock);
if (!m_isEffectMasksValid) {
readLock.unlock();
QWriteLocker writeLock(&m_lock);
if (!m_isEffectMasksValid) {
m_effectMasks = m_parent->searchEffectMasks(0);
m_isEffectMasksValid = true;
}
// return under write lock
return m_effectMasks;
}
// return under read lock
return m_effectMasks;
}
void setDirty()
{
QWriteLocker l(&m_lock);
m_isSelectionMaskValid = false;
m_isEffectMasksValid = false;
m_selectionMask = 0;
m_effectMasks.clear();
}
private:
KisLayer *m_parent;
QReadWriteLock m_lock;
bool m_isSelectionMaskValid = false;
bool m_isEffectMasksValid = false;
KisSelectionMaskSP m_selectionMask;
QList<KisEffectMaskSP> m_effectMasks;
};
struct Q_DECL_HIDDEN KisLayer::Private
{
Private(KisLayer *q)
: masksCache(q)
{
}
QBitArray channelFlags;
KisMetaData::Store* metaDataStore;
KisCloneLayersList clonesList;
KisPSDLayerStyleSP layerStyle;
KisLayerStyleProjectionPlaneSP layerStyleProjectionPlane;
KisLayerProjectionPlaneSP projectionPlane;
KisSafeNodeProjectionStoreSP safeProjection;
KisLayerMasksCache masksCache;
};
KisLayer::KisLayer(KisImageWSP image, const QString &name, quint8 opacity)
: KisNode(image)
, m_d(new Private(this))
{
setName(name);
setOpacity(opacity);
m_d->metaDataStore = new KisMetaData::Store();
m_d->projectionPlane = toQShared(new KisLayerProjectionPlane(this));
m_d->safeProjection = new KisSafeNodeProjectionStore();
m_d->safeProjection->setImage(image);
}
KisLayer::KisLayer(const KisLayer& rhs)
: KisNode(rhs)
, m_d(new Private(this))
{
if (this != &rhs) {
m_d->metaDataStore = new KisMetaData::Store(*rhs.m_d->metaDataStore);
m_d->channelFlags = rhs.m_d->channelFlags;
setName(rhs.name());
m_d->projectionPlane = toQShared(new KisLayerProjectionPlane(this));
m_d->safeProjection = new KisSafeNodeProjectionStore(*rhs.m_d->safeProjection);
m_d->safeProjection->setImage(image());
if (rhs.m_d->layerStyle) {
- m_d->layerStyle = rhs.m_d->layerStyle->clone();
+ m_d->layerStyle = rhs.m_d->layerStyle->clone().dynamicCast<KisPSDLayerStyle>();
if (rhs.m_d->layerStyleProjectionPlane) {
m_d->layerStyleProjectionPlane = toQShared(
new KisLayerStyleProjectionPlane(*rhs.m_d->layerStyleProjectionPlane,
this,
m_d->layerStyle));
}
}
}
}
KisLayer::~KisLayer()
{
delete m_d->metaDataStore;
delete m_d;
}
const KoColorSpace * KisLayer::colorSpace() const
{
KisImageSP image = this->image();
if (!image) {
return nullptr;
}
return image->colorSpace();
}
const KoCompositeOp * KisLayer::compositeOp() const
{
/**
* FIXME: This function duplicates the same function from
* KisMask. We can't move it to KisBaseNode as it doesn't
* know anything about parent() method of KisNode
* Please think it over...
*/
KisNodeSP parentNode = parent();
if (!parentNode) return 0;
if (!parentNode->colorSpace()) return 0;
const KoCompositeOp* op = parentNode->colorSpace()->compositeOp(compositeOpId());
return op ? op : parentNode->colorSpace()->compositeOp(COMPOSITE_OVER);
}
KisPSDLayerStyleSP KisLayer::layerStyle() const
{
return m_d->layerStyle;
}
void KisLayer::setLayerStyle(KisPSDLayerStyleSP layerStyle)
{
if (layerStyle) {
m_d->layerStyle = layerStyle;
KisLayerStyleProjectionPlaneSP plane = !layerStyle->isEmpty() ?
KisLayerStyleProjectionPlaneSP(new KisLayerStyleProjectionPlane(this)) :
KisLayerStyleProjectionPlaneSP(0);
m_d->layerStyleProjectionPlane = plane;
} else {
m_d->layerStyleProjectionPlane.clear();
m_d->layerStyle.clear();
}
}
KisBaseNode::PropertyList KisLayer::sectionModelProperties() const
{
KisBaseNode::PropertyList l = KisBaseNode::sectionModelProperties();
l << KisBaseNode::Property(KoID("opacity", i18n("Opacity")), i18n("%1%", percentOpacity()));
const KoCompositeOp * compositeOp = this->compositeOp();
if (compositeOp) {
l << KisBaseNode::Property(KoID("compositeop", i18n("Blending Mode")), compositeOp->description());
}
if (m_d->layerStyle && !m_d->layerStyle->isEmpty()) {
l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::layerStyle, m_d->layerStyle->isEnabled());
}
l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::inheritAlpha, alphaChannelDisabled());
return l;
}
void KisLayer::setSectionModelProperties(const KisBaseNode::PropertyList &properties)
{
KisBaseNode::setSectionModelProperties(properties);
Q_FOREACH (const KisBaseNode::Property &property, properties) {
if (property.id == KisLayerPropertiesIcons::inheritAlpha.id()) {
disableAlphaChannel(property.state.toBool());
}
if (property.id == KisLayerPropertiesIcons::layerStyle.id()) {
if (m_d->layerStyle &&
m_d->layerStyle->isEnabled() != property.state.toBool()) {
m_d->layerStyle->setEnabled(property.state.toBool());
baseNodeChangedCallback();
baseNodeInvalidateAllFramesCallback();
}
}
}
}
void KisLayer::disableAlphaChannel(bool disable)
{
QBitArray newChannelFlags = m_d->channelFlags;
if(newChannelFlags.isEmpty())
newChannelFlags = colorSpace()->channelFlags(true, true);
if(disable)
newChannelFlags &= colorSpace()->channelFlags(true, false);
else
newChannelFlags |= colorSpace()->channelFlags(false, true);
setChannelFlags(newChannelFlags);
}
bool KisLayer::alphaChannelDisabled() const
{
QBitArray flags = colorSpace()->channelFlags(false, true) & m_d->channelFlags;
return flags.count(true) == 0 && !m_d->channelFlags.isEmpty();
}
void KisLayer::setChannelFlags(const QBitArray & channelFlags)
{
Q_ASSERT(channelFlags.isEmpty() ||((quint32)channelFlags.count() == colorSpace()->channelCount()));
if (KritaUtils::compareChannelFlags(channelFlags,
this->channelFlags())) {
return;
}
if (!channelFlags.isEmpty() &&
channelFlags == QBitArray(channelFlags.size(), true)) {
m_d->channelFlags.clear();
} else {
m_d->channelFlags = channelFlags;
}
baseNodeChangedCallback();
baseNodeInvalidateAllFramesCallback();
}
QBitArray & KisLayer::channelFlags() const
{
return m_d->channelFlags;
}
bool KisLayer::temporary() const
{
return nodeProperties().boolProperty("temporary", false);
}
void KisLayer::setTemporary(bool t)
{
setNodeProperty("temporary", t);
}
void KisLayer::setImage(KisImageWSP image)
{
// we own the projection device, so we should take care about it
KisPaintDeviceSP projection = this->projection();
if (projection && projection != original()) {
projection->setDefaultBounds(new KisDefaultBounds(image));
}
m_d->safeProjection->setImage(image);
KisNode::setImage(image);
}
bool KisLayer::canMergeAndKeepBlendOptions(KisLayerSP otherLayer)
{
return
this->compositeOpId() == otherLayer->compositeOpId() &&
this->opacity() == otherLayer->opacity() &&
this->channelFlags() == otherLayer->channelFlags() &&
!this->layerStyle() && !otherLayer->layerStyle() &&
(this->colorSpace() == otherLayer->colorSpace() ||
*this->colorSpace() == *otherLayer->colorSpace());
}
KisLayerSP KisLayer::createMergedLayerTemplate(KisLayerSP prevLayer)
{
const bool keepBlendingOptions = canMergeAndKeepBlendOptions(prevLayer);
KisLayerSP newLayer = new KisPaintLayer(image(), prevLayer->name(), OPACITY_OPAQUE_U8);
if (keepBlendingOptions) {
newLayer->setCompositeOpId(compositeOpId());
newLayer->setOpacity(opacity());
newLayer->setChannelFlags(channelFlags());
}
return newLayer;
}
void KisLayer::fillMergedLayerTemplate(KisLayerSP dstLayer, KisLayerSP prevLayer)
{
const bool keepBlendingOptions = canMergeAndKeepBlendOptions(prevLayer);
QRect layerProjectionExtent = this->projection()->extent();
QRect prevLayerProjectionExtent = prevLayer->projection()->extent();
bool alphaDisabled = this->alphaChannelDisabled();
bool prevAlphaDisabled = prevLayer->alphaChannelDisabled();
KisPaintDeviceSP mergedDevice = dstLayer->paintDevice();
if (!keepBlendingOptions) {
KisPainter gc(mergedDevice);
KisImageSP imageSP = image().toStrongRef();
if (!imageSP) {
return;
}
//Copy the pixels of previous layer with their actual alpha value
prevLayer->disableAlphaChannel(false);
prevLayer->projectionPlane()->apply(&gc, prevLayerProjectionExtent | imageSP->bounds());
//Restore the previous prevLayer disableAlpha status for correct undo/redo
prevLayer->disableAlphaChannel(prevAlphaDisabled);
//Paint the pixels of the current layer, using their actual alpha value
if (alphaDisabled == prevAlphaDisabled) {
this->disableAlphaChannel(false);
}
this->projectionPlane()->apply(&gc, layerProjectionExtent | imageSP->bounds());
//Restore the layer disableAlpha status for correct undo/redo
this->disableAlphaChannel(alphaDisabled);
}
else {
//Copy prevLayer
KisPaintDeviceSP srcDev = prevLayer->projection();
mergedDevice->makeCloneFrom(srcDev, srcDev->extent());
//Paint layer on the copy
KisPainter gc(mergedDevice);
gc.bitBlt(layerProjectionExtent.topLeft(), this->projection(), layerProjectionExtent);
}
}
void KisLayer::registerClone(KisCloneLayerWSP clone)
{
m_d->clonesList.addClone(clone);
}
void KisLayer::unregisterClone(KisCloneLayerWSP clone)
{
m_d->clonesList.removeClone(clone);
}
const QList<KisCloneLayerWSP> KisLayer::registeredClones() const
{
return m_d->clonesList.registeredClones();
}
bool KisLayer::hasClones() const
{
return m_d->clonesList.hasClones();
}
void KisLayer::updateClones(const QRect &rect)
{
m_d->clonesList.setDirty(rect);
}
void KisLayer::notifyChildMaskChanged()
{
m_d->masksCache.setDirty();
}
KisSelectionMaskSP KisLayer::selectionMask() const
{
return m_d->masksCache.selectionMask();
}
KisSelectionSP KisLayer::selection() const
{
KisSelectionMaskSP mask = selectionMask();
if (mask) {
return mask->selection();
}
KisImageSP image = this->image();
if (image) {
return image->globalSelection();
}
return KisSelectionSP();
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
QList<KisEffectMaskSP> KisLayer::effectMasks() const
{
return m_d->masksCache.effectMasks();
}
QList<KisEffectMaskSP> KisLayer::effectMasks(KisNodeSP lastNode) const
{
if (lastNode.isNull()) {
return effectMasks();
} else {
// happens rarely.
return searchEffectMasks(lastNode);
}
}
QList<KisEffectMaskSP> KisLayer::searchEffectMasks(KisNodeSP lastNode) const
{
QList<KisEffectMaskSP> masks;
KIS_SAFE_ASSERT_RECOVER_NOOP(projectionLeaf());
KisProjectionLeafSP child = projectionLeaf()->firstChild();
while (child) {
if (child->node() == lastNode) break;
KIS_SAFE_ASSERT_RECOVER_NOOP(child);
KIS_SAFE_ASSERT_RECOVER_NOOP(child->node());
if (child->visible()) {
KisEffectMaskSP mask = dynamic_cast<KisEffectMask*>(const_cast<KisNode*>(child->node().data()));
if (mask) {
masks.append(mask);
}
}
child = child->nextSibling();
}
return masks;
}
bool KisLayer::hasEffectMasks() const
{
return !m_d->masksCache.effectMasks().isEmpty();
}
QRect KisLayer::masksChangeRect(const QList<KisEffectMaskSP> &masks,
const QRect &requestedRect,
bool &rectVariesFlag) const
{
rectVariesFlag = false;
QRect prevChangeRect = requestedRect;
/**
* We set default value of the change rect for the case
* when there is no mask at all
*/
QRect changeRect = requestedRect;
Q_FOREACH (const KisEffectMaskSP& mask, masks) {
changeRect = mask->changeRect(prevChangeRect);
if (changeRect != prevChangeRect)
rectVariesFlag = true;
prevChangeRect = changeRect;
}
return changeRect;
}
QRect KisLayer::masksNeedRect(const QList<KisEffectMaskSP> &masks,
const QRect &changeRect,
QStack<QRect> &applyRects,
bool &rectVariesFlag) const
{
rectVariesFlag = false;
QRect prevNeedRect = changeRect;
QRect needRect;
for (qint32 i = masks.size() - 1; i >= 0; i--) {
applyRects.push(prevNeedRect);
needRect = masks[i]->needRect(prevNeedRect);
if (prevNeedRect != needRect)
rectVariesFlag = true;
prevNeedRect = needRect;
}
return needRect;
}
KisNode::PositionToFilthy calculatePositionToFilthy(KisNodeSP nodeInQuestion,
KisNodeSP filthy,
KisNodeSP parent)
{
if (parent == filthy || parent != filthy->parent()) {
return KisNode::N_ABOVE_FILTHY;
}
if (nodeInQuestion == filthy) {
return KisNode::N_FILTHY;
}
KisNodeSP node = nodeInQuestion->prevSibling();
while (node) {
if (node == filthy) {
return KisNode::N_ABOVE_FILTHY;
}
node = node->prevSibling();
}
return KisNode::N_BELOW_FILTHY;
}
QRect KisLayer::applyMasks(const KisPaintDeviceSP source,
KisPaintDeviceSP destination,
const QRect &requestedRect,
KisNodeSP filthyNode,
KisNodeSP lastNode) const
{
Q_ASSERT(source);
Q_ASSERT(destination);
QList<KisEffectMaskSP> masks = effectMasks(lastNode);
QRect changeRect;
QRect needRect;
if (masks.isEmpty()) {
changeRect = requestedRect;
if (source != destination) {
copyOriginalToProjection(source, destination, requestedRect);
}
} else {
QStack<QRect> applyRects;
bool changeRectVaries;
bool needRectVaries;
/**
* FIXME: Assume that varying of the changeRect has already
* been taken into account while preparing walkers
*/
changeRectVaries = false;
changeRect = requestedRect;
//changeRect = masksChangeRect(masks, requestedRect,
// changeRectVaries);
needRect = masksNeedRect(masks, changeRect,
applyRects, needRectVaries);
if (!changeRectVaries && !needRectVaries) {
/**
* A bit of optimization:
* All filters will read/write exactly from/to the requested
* rect so we needn't create temporary paint device,
* just apply it onto destination
*/
Q_ASSERT(needRect == requestedRect);
if (source != destination) {
copyOriginalToProjection(source, destination, needRect);
}
Q_FOREACH (const KisEffectMaskSP& mask, masks) {
const QRect maskApplyRect = applyRects.pop();
const QRect maskNeedRect =
applyRects.isEmpty() ? needRect : applyRects.top();
PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast<KisLayer*>(this));
mask->apply(destination, maskApplyRect, maskNeedRect, maskPosition);
}
Q_ASSERT(applyRects.isEmpty());
} else {
/**
* We can't eliminate additional copy-op
* as filters' behaviour may be quite insane here,
* so let them work on their own paintDevice =)
*/
KisPaintDeviceSP tempDevice = new KisPaintDevice(colorSpace());
tempDevice->prepareClone(source);
copyOriginalToProjection(source, tempDevice, needRect);
QRect maskApplyRect = applyRects.pop();
QRect maskNeedRect = needRect;
Q_FOREACH (const KisEffectMaskSP& mask, masks) {
PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast<KisLayer*>(this));
mask->apply(tempDevice, maskApplyRect, maskNeedRect, maskPosition);
if (!applyRects.isEmpty()) {
maskNeedRect = maskApplyRect;
maskApplyRect = applyRects.pop();
}
}
Q_ASSERT(applyRects.isEmpty());
KisPainter::copyAreaOptimized(changeRect.topLeft(), tempDevice, destination, changeRect);
}
}
return changeRect;
}
QRect KisLayer::updateProjection(const QRect& rect, KisNodeSP filthyNode)
{
QRect updatedRect = rect;
KisPaintDeviceSP originalDevice = original();
if (!rect.isValid() ||
(!visible() && !hasClones()) ||
!originalDevice) return QRect();
if (!needProjection() && !hasEffectMasks()) {
m_d->safeProjection->releaseDevice();
} else {
if (!updatedRect.isEmpty()) {
KisPaintDeviceSP projection = m_d->safeProjection->getDeviceLazy(originalDevice);
updatedRect = applyMasks(originalDevice, projection,
updatedRect, filthyNode, 0);
}
}
return updatedRect;
}
QRect KisLayer::partialChangeRect(KisNodeSP lastNode, const QRect& rect)
{
bool changeRectVaries = false;
QRect changeRect = outgoingChangeRect(rect);
changeRect = masksChangeRect(effectMasks(lastNode), changeRect,
changeRectVaries);
return changeRect;
}
/**
* \p rect is a dirty rect in layer's original() coordinates!
*/
void KisLayer::buildProjectionUpToNode(KisPaintDeviceSP projection, KisNodeSP lastNode, const QRect& rect)
{
QRect changeRect = partialChangeRect(lastNode, rect);
KisPaintDeviceSP originalDevice = original();
KIS_ASSERT_RECOVER_RETURN(needProjection() || hasEffectMasks());
if (!changeRect.isEmpty()) {
applyMasks(originalDevice, projection,
changeRect, this, lastNode);
}
}
bool KisLayer::needProjection() const
{
return false;
}
void KisLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
KisPaintDeviceSP projection,
const QRect& rect) const
{
KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect);
}
KisAbstractProjectionPlaneSP KisLayer::projectionPlane() const
{
return m_d->layerStyleProjectionPlane ?
KisAbstractProjectionPlaneSP(m_d->layerStyleProjectionPlane) :
KisAbstractProjectionPlaneSP(m_d->projectionPlane);
}
KisLayerProjectionPlaneSP KisLayer::internalProjectionPlane() const
{
return m_d->projectionPlane;
}
KisPaintDeviceSP KisLayer::projection() const
{
KisPaintDeviceSP originalDevice = original();
return needProjection() || hasEffectMasks() ?
m_d->safeProjection->getDeviceLazy(originalDevice) : originalDevice;
}
QRect KisLayer::tightUserVisibleBounds() const
{
QRect changeRect = exactBounds();
/// we do not use incomingChangeRect() here, because
/// exactBounds() already takes it into account (it
/// was used while preparing original())
bool changeRectVaries;
changeRect = outgoingChangeRect(changeRect);
changeRect = masksChangeRect(effectMasks(), changeRect, changeRectVaries);
return changeRect;
}
QRect KisLayer::changeRect(const QRect &rect, PositionToFilthy pos) const
{
QRect changeRect = rect;
changeRect = incomingChangeRect(changeRect);
if(pos == KisNode::N_FILTHY) {
QRect projectionToBeUpdated = projection()->exactBoundsAmortized() & changeRect;
bool changeRectVaries;
changeRect = outgoingChangeRect(changeRect);
changeRect = masksChangeRect(effectMasks(), changeRect, changeRectVaries);
/**
* If the projection contains some dirty areas we should also
* add them to the change rect, because they might have
* changed. E.g. when a visibility of the mask has chnaged
* while the parent layer was invinisble.
*/
if (!projectionToBeUpdated.isEmpty() &&
!changeRect.contains(projectionToBeUpdated)) {
changeRect |= projectionToBeUpdated;
}
}
// TODO: string comparizon: optimize!
if (pos != KisNode::N_FILTHY &&
pos != KisNode::N_FILTHY_PROJECTION &&
compositeOpId() != COMPOSITE_COPY) {
changeRect |= rect;
}
return changeRect;
}
void KisLayer::childNodeChanged(KisNodeSP changedChildNode)
{
if (dynamic_cast<KisMask*>(changedChildNode.data())) {
notifyChildMaskChanged();
}
}
QRect KisLayer::incomingChangeRect(const QRect &rect) const
{
return rect;
}
QRect KisLayer::outgoingChangeRect(const QRect &rect) const
{
return rect;
}
QRect KisLayer::needRectForOriginal(const QRect &rect) const
{
QRect needRect = rect;
const QList<KisEffectMaskSP> masks = effectMasks();
if (!masks.isEmpty()) {
QStack<QRect> applyRects;
bool needRectVaries;
needRect = masksNeedRect(masks, rect,
applyRects, needRectVaries);
}
return needRect;
}
QImage KisLayer::createThumbnail(qint32 w, qint32 h)
{
if (w == 0 || h == 0) {
return QImage();
}
KisPaintDeviceSP originalDevice = original();
return originalDevice ?
originalDevice->createThumbnail(w, h, 1,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags()) : QImage();
}
QImage KisLayer::createThumbnailForFrame(qint32 w, qint32 h, int time)
{
if (w == 0 || h == 0) {
return QImage();
}
KisPaintDeviceSP originalDevice = original();
if (originalDevice ) {
KisRasterKeyframeChannel *channel = originalDevice->keyframeChannel();
if (channel) {
KisPaintDeviceSP targetDevice = new KisPaintDevice(colorSpace());
KisKeyframeSP keyframe = channel->activeKeyframeAt(time);
channel->fetchFrame(keyframe, targetDevice);
return targetDevice->createThumbnail(w, h, 1,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
}
}
return createThumbnail(w, h);
}
qint32 KisLayer::x() const
{
KisPaintDeviceSP originalDevice = original();
return originalDevice ? originalDevice->x() : 0;
}
qint32 KisLayer::y() const
{
KisPaintDeviceSP originalDevice = original();
return originalDevice ? originalDevice->y() : 0;
}
void KisLayer::setX(qint32 x)
{
KisPaintDeviceSP originalDevice = original();
if (originalDevice)
originalDevice->setX(x);
}
void KisLayer::setY(qint32 y)
{
KisPaintDeviceSP originalDevice = original();
if (originalDevice)
originalDevice->setY(y);
}
QRect KisLayer::layerExtentImpl(bool needExactBounds) const
{
QRect additionalMaskExtent = QRect();
QList<KisEffectMaskSP> effectMasks = this->effectMasks();
Q_FOREACH(KisEffectMaskSP mask, effectMasks) {
additionalMaskExtent |= mask->nonDependentExtent();
}
KisPaintDeviceSP originalDevice = original();
QRect layerExtent;
if (originalDevice) {
layerExtent = needExactBounds ?
originalDevice->exactBounds() :
originalDevice->extent();
}
QRect additionalCompositeOpExtent;
if (compositeOpId() == COMPOSITE_DESTINATION_IN ||
compositeOpId() == COMPOSITE_DESTINATION_ATOP) {
additionalCompositeOpExtent = originalDevice->defaultBounds()->bounds();
}
return layerExtent | additionalMaskExtent | additionalCompositeOpExtent;
}
QRect KisLayer::extent() const
{
return layerExtentImpl(false);
}
QRect KisLayer::exactBounds() const
{
return layerExtentImpl(true);
}
KisLayerSP KisLayer::parentLayer() const
{
return qobject_cast<KisLayer*>(parent().data());
}
KisMetaData::Store* KisLayer::metaData()
{
return m_d->metaDataStore;
}
diff --git a/libs/image/kis_node_filter_interface.cpp b/libs/image/kis_node_filter_interface.cpp
index 091579f235..3dee307468 100644
--- a/libs/image/kis_node_filter_interface.cpp
+++ b/libs/image/kis_node_filter_interface.cpp
@@ -1,92 +1,88 @@
/*
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
* 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.
*/
#include "kis_node_filter_interface.h"
#include "filter/kis_filter.h"
#include "generator/kis_generator.h"
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter_configuration.h"
#include "generator/kis_generator_registry.h"
#ifdef SANITY_CHECK_FILTER_CONFIGURATION_OWNER
#define SANITY_ACQUIRE_FILTER(filter) \
do { \
if ((filter)) { \
(filter)->sanityRefUsageCounter(); \
} \
} while (0)
#define SANITY_RELEASE_FILTER(filter) \
do { \
if (m_filter && m_filter->sanityDerefUsageCounter()) { \
warnKrita; \
warnKrita << "WARNING: filter configuration has more than one user! Krita will probably crash soon!"; \
warnKrita << "WARNING:" << ppVar(this); \
warnKrita << "WARNING:" << ppVar(filter.data()); \
warnKrita; \
} \
} while (0)
#else /* SANITY_CHECK_FILTER_CONFIGURATION_OWNER */
#define SANITY_ACQUIRE_FILTER(filter)
#define SANITY_RELEASE_FILTER(filter)
#endif /* SANITY_CHECK_FILTER_CONFIGURATION_OWNER*/
-KisNodeFilterInterface::KisNodeFilterInterface(KisFilterConfigurationSP filterConfig, bool useGeneratorRegistry)
- : m_filter(filterConfig),
- m_useGeneratorRegistry(useGeneratorRegistry)
+KisNodeFilterInterface::KisNodeFilterInterface(KisFilterConfigurationSP filterConfig)
+ : m_filter(filterConfig)
{
SANITY_ACQUIRE_FILTER(m_filter);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(!filterConfig || filterConfig->hasLocalResourcesSnapshot());
}
KisNodeFilterInterface::KisNodeFilterInterface(const KisNodeFilterInterface &rhs)
- : m_useGeneratorRegistry(rhs.m_useGeneratorRegistry)
-{
- if (m_useGeneratorRegistry) {
- m_filter = KisGeneratorRegistry::instance()->cloneConfiguration(const_cast<KisFilterConfiguration*>(rhs.m_filter.data()));
- } else {
- m_filter = KisFilterRegistry::instance()->cloneConfiguration(const_cast<KisFilterConfiguration*>(rhs.m_filter.data()));
- }
+ : m_filter(rhs.m_filter->clone())
+{
SANITY_ACQUIRE_FILTER(m_filter);
}
KisNodeFilterInterface::~KisNodeFilterInterface()
{
SANITY_RELEASE_FILTER(m_filter);
}
KisFilterConfigurationSP KisNodeFilterInterface::filter() const
{
return m_filter;
}
void KisNodeFilterInterface::setFilter(KisFilterConfigurationSP filterConfig)
{
SANITY_RELEASE_FILTER(m_filter);
KIS_SAFE_ASSERT_RECOVER_RETURN(filterConfig);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(filterConfig->hasLocalResourcesSnapshot());
m_filter = filterConfig;
SANITY_ACQUIRE_FILTER(m_filter);
}
diff --git a/libs/image/kis_node_filter_interface.h b/libs/image/kis_node_filter_interface.h
index cee5e1c1f4..cda11a879f 100644
--- a/libs/image/kis_node_filter_interface.h
+++ b/libs/image/kis_node_filter_interface.h
@@ -1,59 +1,58 @@
/*
* 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_NODE_FILTER_INTERFACE_H_
#define _KIS_NODE_FILTER_INTERFACE_H_
#include <kritaimage_export.h>
#include <kis_types.h>
/**
* Define an interface for nodes that are associated with a filter.
*/
class KRITAIMAGE_EXPORT KisNodeFilterInterface
{
public:
- KisNodeFilterInterface(KisFilterConfigurationSP filterConfig, bool useGeneratorRegistry);
+ KisNodeFilterInterface(KisFilterConfigurationSP filterConfig);
KisNodeFilterInterface(const KisNodeFilterInterface &rhs);
virtual ~KisNodeFilterInterface();
/**
* @return safe shared pointer to the filter configuration
* associated with this node
*/
virtual KisFilterConfigurationSP filter() const;
/**
* Sets the filter configuration for this node. The filter might
* differ from the filter that is currently set up on this node.
*
* WARNING: the filterConfig becomes *owned* by the node right
* after you've set it. Don't try to access the configuration
* after you've associated it with the node.
*/
virtual void setFilter(KisFilterConfigurationSP filterConfig);
// the child classes should access the filter with the filter() method
private:
KisNodeFilterInterface& operator=(const KisNodeFilterInterface &other);
KisFilterConfigurationSP m_filter;
- bool m_useGeneratorRegistry;
};
#endif
diff --git a/libs/image/kis_painter.cc b/libs/image/kis_painter.cc
index 42cb1a490c..dd8393442b 100644
--- a/libs/image/kis_painter.cc
+++ b/libs/image/kis_painter.cc
@@ -1,3043 +1,3041 @@
/*
* 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;
}
KisPaintDeviceSP KisPainter::convertToAlphaAsPureAlpha(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->opacityU8(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 (d->strokeStyle == StrokeStyleNone) return;
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:
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()
KIS_SAFE_ASSERT_RECOVER_NOOP(_pen.color() == Qt::white);
QPen pen(_pen);
pen.setColor(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 = 0;
bool lockAxis = true;
if (xd == 0) {
m = 2.0;
} else if ( yd != 0) {
lockAxis = false;
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 = (lockAxis)? 0 : 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)
+void KisPainter::setPattern(const KoPatternSP pattern)
{
d->pattern = pattern;
}
-const KoPattern * KisPainter::pattern() const
+const KoPatternSP 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)
+void KisPainter::setGradient(const KoAbstractGradientSP gradient)
{
d->gradient = gradient;
}
-const KoAbstractGradient* KisPainter::gradient() const
+const KoAbstractGradientSP 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);
}
namespace {
inline void mirrorOneObject(Qt::Orientation dir, const QPoint &center, QRect *rc) {
KritaUtils::mirrorRect(dir, center, rc);
}
inline void mirrorOneObject(Qt::Orientation dir, const QPoint &center, QPointF *pt) {
KritaUtils::mirrorPoint(dir, center, pt);
}
inline void mirrorOneObject(Qt::Orientation dir, const QPoint &center, QPair<QPointF, QPointF> *pair) {
KritaUtils::mirrorPoint(dir, center, &pair->first);
KritaUtils::mirrorPoint(dir, center, &pair->second);
}
}
template<class T> QVector<T> KisPainter::Private::calculateMirroredObjects(const T &object)
{
QVector<T> result;
KisLodTransform t(this->device);
const QPoint effectiveAxesCenter = t.map(this->axesCenter).toPoint();
T baseObject = object;
result << baseObject;
if (this->mirrorHorizontally && this->mirrorVertically){
mirrorOneObject(Qt::Horizontal, effectiveAxesCenter, &baseObject);
result << baseObject;
mirrorOneObject(Qt::Vertical, effectiveAxesCenter, &baseObject);
result << baseObject;
mirrorOneObject(Qt::Horizontal, effectiveAxesCenter, &baseObject);
result << baseObject;
} else if (this->mirrorHorizontally) {
mirrorOneObject(Qt::Horizontal, effectiveAxesCenter, &baseObject);
result << baseObject;
} else if (this->mirrorVertically) {
mirrorOneObject(Qt::Vertical, effectiveAxesCenter, &baseObject);
result << baseObject;
}
return result;
}
const QVector<QRect> KisPainter::calculateAllMirroredRects(const QRect &rc)
{
return d->calculateMirroredObjects(rc);
}
const QVector<QPointF> KisPainter::calculateAllMirroredPoints(const QPointF &pos)
{
return d->calculateMirroredObjects(pos);
}
const QVector<QPair<QPointF, QPointF>> KisPainter::calculateAllMirroredPoints(const QPair<QPointF, QPointF> &pair)
{
return d->calculateMirroredObjects(pair);
}
diff --git a/libs/image/kis_painter.h b/libs/image/kis_painter.h
index a89c7d9ff4..50ed42716d 100644
--- a/libs/image/kis_painter.h
+++ b/libs/image/kis_painter.h
@@ -1,898 +1,899 @@
/*
* 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 <KoPattern.h>
+#include <KoAbstractGradient.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);
/**
* creates a paint device with only alpha values from src
*/
static KisPaintDeviceSP convertToAlphaAsPureAlpha(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 @p dstX and @p dstY.
* @param srcDev the source device.
* @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 @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.
* @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 @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 @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 @p dstX and @p dstY.
* @param srcDev the source device.
* @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 @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.
* @p srcDev is a @ref KisFixedPaintDevice : this means that @p srcDev
* must have the same colorspace as the destination device.
* @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 @p srcWidth and @p srcHeight must not go beyond their limits, and
* they must be different from zero.
* @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 @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 @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 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 @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 @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
* @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 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 @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 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 @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);
/**
* Calculate the list of the mirrored points according to the current
* mirroring configuration.
*/
const QVector<QPointF> calculateAllMirroredPoints(const QPointF &pos);
/**
* Calculate the list of the mirrored point pairs according to the current
* mirroring configuration.
*/
const QVector<QPair<QPointF, QPointF>> calculateAllMirroredPoints(const QPair<QPointF, QPointF> &pair);
/// Set the current pattern
- void setPattern(const KoPattern * pattern);
+ void setPattern(const KoPatternSP pattern);
/// Returns the currently set pattern
- const KoPattern * pattern() const;
+ const KoPatternSP 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,
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;
+ void setGradient(const KoAbstractGradientSP gradient);
+ const KoAbstractGradientSP 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_painter_p.h b/libs/image/kis_painter_p.h
index 2ced134de2..27b95019af 100644
--- a/libs/image/kis_painter_p.h
+++ b/libs/image/kis_painter_p.h
@@ -1,107 +1,107 @@
/*
* 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 KISPAINTERPRIVATE_H
#define KISPAINTERPRIVATE_H
#include <KoColorSpace.h>
#include <KoColor.h>
#include <KoCompositeOpRegistry.h>
#include <KoUpdater.h>
#include "kis_paintop.h"
#include "kis_selection.h"
#include "kis_fill_painter.h"
#include "kis_painter.h"
#include "kis_paintop_preset.h"
#include <KisFakeRunnableStrokeJobsExecutor.h>
struct Q_DECL_HIDDEN KisPainter::Private {
Private(KisPainter *_q) : q(_q) {}
Private(KisPainter *_q, const KoColorSpace *cs)
: q(_q), paintColor(cs), backgroundColor(cs) {}
KisPainter *q;
KisPaintDeviceSP device;
KisSelectionSP selection;
KisTransaction* transaction;
KoUpdater* progressUpdater;
QVector<QRect> dirtyRects;
KisPaintOp* paintOp;
KoColor paintColor;
KoColor backgroundColor;
KoColor customColor;
KisFilterConfigurationSP generator;
KisPaintLayer* sourceLayer;
FillStyle fillStyle;
StrokeStyle strokeStyle;
bool antiAliasPolygonFill;
- const KoPattern* pattern;
+ KoPatternSP pattern;
QPointF duplicateOffset;
quint32 pixelSize;
const KoColorSpace* colorSpace;
KoColorProfile* profile;
const KoCompositeOp* compositeOp;
- const KoAbstractGradient* gradient;
+ KoAbstractGradientSP gradient;
KisPaintOpPresetSP paintOpPreset;
QImage polygonMaskImage;
QPainter* maskPainter;
KisFillPainter* fillPainter;
KisPaintDeviceSP polygon;
qint32 maskImageWidth;
qint32 maskImageHeight;
QPointF axesCenter;
bool mirrorHorizontally;
bool mirrorVertically;
bool isOpacityUnit; // TODO: move into ParameterInfo
KoCompositeOp::ParameterInfo paramInfo;
KoColorConversionTransformation::Intent renderingIntent;
KoColorConversionTransformation::ConversionFlags conversionFlags;
KisRunnableStrokeJobsInterface *runnableStrokeJobsInterface = 0;
QScopedPointer<KisRunnableStrokeJobsInterface> fakeRunnableStrokeJobsInterface;
bool tryReduceSourceRect(const KisPaintDevice *srcDev,
QRect *srcRect,
qint32 *srcX,
qint32 *srcY,
qint32 *srcWidth,
qint32 *srcHeight,
qint32 *dstX,
qint32 *dstY);
void fillPainterPathImpl(const QPainterPath& path, const QRect &requestedRect);
void applyDevice(const QRect &applyRect,
const KisRenderedDab &dab,
KisRandomAccessorSP dstIt,
const KoColorSpace *srcColorSpace,
KoCompositeOp::ParameterInfo &localParamInfo);
void applyDeviceWithSelection(const QRect &applyRect,
const KisRenderedDab &dab,
KisRandomAccessorSP dstIt,
KisRandomConstAccessorSP maskIt,
const KoColorSpace *srcColorSpace,
KoCompositeOp::ParameterInfo &localParamInfo);
template<class T> QVector<T> calculateMirroredObjects(const T &object);
};
#endif // KISPAINTERPRIVATE_H
diff --git a/libs/image/kis_psd_layer_style.cpp b/libs/image/kis_psd_layer_style.cpp
index 152d88e573..83cb22a4a3 100644
--- a/libs/image/kis_psd_layer_style.cpp
+++ b/libs/image/kis_psd_layer_style.cpp
@@ -1,297 +1,293 @@
/*
* 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_psd_layer_style.h"
#include <QIODevice>
#include <QUuid>
#include <psd.h>
#include <psd_utils.h>
#include <klocalizedstring.h>
#include "kis_global.h"
struct Q_DECL_HIDDEN KisPSDLayerStyle::Private
{
Private()
: version(-1)
, effectEnabled(true)
{}
Private(const Private &rhs)
: name(rhs.name),
uuid(rhs.uuid),
version(rhs.version),
effectEnabled(rhs.effectEnabled),
context(rhs.context),
drop_shadow(rhs.drop_shadow),
inner_shadow(rhs.inner_shadow),
outer_glow(rhs.outer_glow),
inner_glow(rhs.inner_glow),
bevel_emboss(rhs.bevel_emboss),
satin(rhs.satin),
color_overlay(rhs.color_overlay),
gradient_overlay(rhs.gradient_overlay),
pattern_overlay(rhs.pattern_overlay),
stroke(rhs.stroke)
{}
Private operator=(const Private &rhs)
{
if (this != &rhs) {
name = rhs.name;
uuid = rhs.uuid;
version = rhs.version;
effectEnabled = rhs.effectEnabled;
context = rhs.context;
drop_shadow = rhs.drop_shadow;
inner_shadow = rhs.inner_shadow;
outer_glow = rhs.outer_glow;
inner_glow = rhs.inner_glow;
bevel_emboss = rhs.bevel_emboss;
satin = rhs.satin;
color_overlay = rhs.color_overlay;
gradient_overlay = rhs.gradient_overlay;
pattern_overlay = rhs.pattern_overlay;
stroke = rhs.stroke;
}
return *this;
}
QString name;
QUuid uuid;
quint16 version;
bool effectEnabled;
psd_layer_effects_context context;
psd_layer_effects_drop_shadow drop_shadow;
psd_layer_effects_inner_shadow inner_shadow;
psd_layer_effects_outer_glow outer_glow;
psd_layer_effects_inner_glow inner_glow;
psd_layer_effects_bevel_emboss bevel_emboss;
psd_layer_effects_satin satin;
psd_layer_effects_color_overlay color_overlay;
psd_layer_effects_gradient_overlay gradient_overlay;
psd_layer_effects_pattern_overlay pattern_overlay;
psd_layer_effects_stroke stroke;
};
-KisPSDLayerStyle::KisPSDLayerStyle()
- : d(new Private())
+KisPSDLayerStyle::KisPSDLayerStyle(const QString &name)
+ : KoEphemeralResource<KoResource>(name)
+ , d(new Private())
{
d->name = i18n("Unnamed");
d->version = 7;
}
KisPSDLayerStyle::~KisPSDLayerStyle()
{
delete d;
}
KisPSDLayerStyle::KisPSDLayerStyle(const KisPSDLayerStyle &rhs)
- : d(new Private(*rhs.d))
+ : KoEphemeralResource<KoResource>(rhs)
+ , d(new Private(*rhs.d))
{
-}
-
-KisPSDLayerStyle KisPSDLayerStyle::operator=(const KisPSDLayerStyle &rhs)
-{
- if (this != &rhs) {
- *d = *rhs.d;
- }
- return *this;
+ setValid(valid());
}
bool KisPSDLayerStyle::isEnabled() const
{
return d->effectEnabled;
}
void KisPSDLayerStyle::setEnabled(bool value)
{
d->effectEnabled = value;
}
-KisPSDLayerStyleSP KisPSDLayerStyle::clone() const
+KoResourceSP KisPSDLayerStyle::clone() const
{
- return toQShared(new KisPSDLayerStyle(*this));
+ return toQShared(new KisPSDLayerStyle(*this)).dynamicCast<KoResource>();
}
void KisPSDLayerStyle::clear()
{
*d = Private();
}
bool KisPSDLayerStyle::isEmpty() const
{
return !(d->drop_shadow.effectEnabled() ||
d->inner_shadow.effectEnabled() ||
d->outer_glow.effectEnabled() ||
d->inner_glow.effectEnabled() ||
d->bevel_emboss.effectEnabled() ||
d->satin.effectEnabled() ||
d->color_overlay.effectEnabled() ||
d->gradient_overlay.effectEnabled() ||
d->pattern_overlay.effectEnabled() ||
d->stroke.effectEnabled());
}
QString KisPSDLayerStyle::name() const
{
return d->name;
}
void KisPSDLayerStyle::setName(const QString &value)
{
d->name = value;
+ dynamic_cast<KoResource*>(this)->setName(value);
}
QUuid KisPSDLayerStyle::uuid() const
{
if (d->uuid.isNull()) {
d->uuid = QUuid::createUuid();
}
return d->uuid;
}
void KisPSDLayerStyle::setUuid(const QUuid &value) const
{
d->uuid = value;
}
QString KisPSDLayerStyle::psdUuid() const
{
return uuid().toString().mid(1, 36);
}
void KisPSDLayerStyle::setPsdUuid(const QString &value) const
{
setUuid(QUuid(QString("{%1}").arg(value)));
}
const psd_layer_effects_context* KisPSDLayerStyle::context() const
{
return &d->context;
}
const psd_layer_effects_drop_shadow* KisPSDLayerStyle::dropShadow() const
{
return &d->drop_shadow;
}
const psd_layer_effects_inner_shadow* KisPSDLayerStyle::innerShadow() const
{
return &d->inner_shadow;
}
const psd_layer_effects_outer_glow* KisPSDLayerStyle::outerGlow() const
{
return &d->outer_glow;
}
const psd_layer_effects_inner_glow* KisPSDLayerStyle::innerGlow() const
{
return &d->inner_glow;
}
const psd_layer_effects_satin* KisPSDLayerStyle::satin() const
{
return &d->satin;
}
const psd_layer_effects_color_overlay* KisPSDLayerStyle::colorOverlay() const
{
return &d->color_overlay;
}
const psd_layer_effects_gradient_overlay* KisPSDLayerStyle::gradientOverlay() const
{
return &d->gradient_overlay;
}
const psd_layer_effects_pattern_overlay* KisPSDLayerStyle::patternOverlay() const
{
return &d->pattern_overlay;
}
const psd_layer_effects_stroke* KisPSDLayerStyle::stroke() const
{
return &d->stroke;
}
const psd_layer_effects_bevel_emboss* KisPSDLayerStyle::bevelAndEmboss() const
{
return &d->bevel_emboss;
}
psd_layer_effects_context* KisPSDLayerStyle::context()
{
return &d->context;
}
psd_layer_effects_drop_shadow* KisPSDLayerStyle::dropShadow()
{
return &d->drop_shadow;
}
psd_layer_effects_inner_shadow* KisPSDLayerStyle::innerShadow()
{
return &d->inner_shadow;
}
psd_layer_effects_outer_glow* KisPSDLayerStyle::outerGlow()
{
return &d->outer_glow;
}
psd_layer_effects_inner_glow* KisPSDLayerStyle::innerGlow()
{
return &d->inner_glow;
}
psd_layer_effects_satin* KisPSDLayerStyle::satin()
{
return &d->satin;
}
psd_layer_effects_color_overlay* KisPSDLayerStyle::colorOverlay()
{
return &d->color_overlay;
}
psd_layer_effects_gradient_overlay* KisPSDLayerStyle::gradientOverlay()
{
return &d->gradient_overlay;
}
psd_layer_effects_pattern_overlay* KisPSDLayerStyle::patternOverlay()
{
return &d->pattern_overlay;
}
psd_layer_effects_stroke* KisPSDLayerStyle::stroke()
{
return &d->stroke;
}
psd_layer_effects_bevel_emboss* KisPSDLayerStyle::bevelAndEmboss()
{
return &d->bevel_emboss;
}
diff --git a/libs/image/kis_psd_layer_style.h b/libs/image/kis_psd_layer_style.h
index 2711f296ff..4711baf1b2 100644
--- a/libs/image/kis_psd_layer_style.h
+++ b/libs/image/kis_psd_layer_style.h
@@ -1,101 +1,109 @@
/*
* 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_PSD_LAYER_STYLE_H
#define KIS_PSD_LAYER_STYLE_H
class QIODevice;
class QUuid;
#include <QVector>
+#include <KoEphemeralResource.h>
#include <psd.h>
#include "kis_types.h"
-#include "kritaimage_export.h"
+#include "kritapsd_export.h"
class KisPSDLayerStyle;
typedef QSharedPointer<KisPSDLayerStyle> KisPSDLayerStyleSP;
/**
* @brief The KisPSDLayerStyle class implements loading, saving and applying
* the PSD layer effects.
*
* See https://www.tonton-pixel.com/Photoshop%20Additional%20File%20Formats/styles-file-format.html
*
*/
-class KRITAIMAGE_EXPORT KisPSDLayerStyle
+class KRITAPSD_EXPORT KisPSDLayerStyle : public KoEphemeralResource<KoResource>
{
public:
- explicit KisPSDLayerStyle();
+ KisPSDLayerStyle(const QString& name = QString());
virtual ~KisPSDLayerStyle();
KisPSDLayerStyle(const KisPSDLayerStyle& rhs);
- KisPSDLayerStyle operator=(const KisPSDLayerStyle& rhs);
+ KisPSDLayerStyle operator=(const KisPSDLayerStyle& rhs) = delete;
- KisPSDLayerStyleSP clone() const;
+ KoResourceSP clone() const override;
void clear();
QString name() const;
void setName(const QString &value);
+
+
QUuid uuid() const;
void setUuid(const QUuid &value) const;
QString psdUuid() const;
void setPsdUuid(const QString &value) const;
+ QPair<QString, QString> resourceType() const override
+ {
+ return QPair<QString, QString>(ResourceType::LayerStyles, "");
+ }
+
/**
* \return true if all the styles are disabled
*/
bool isEmpty() const;
bool isEnabled() const;
void setEnabled(bool value);
const psd_layer_effects_context* context() const;
const psd_layer_effects_drop_shadow* dropShadow() const;
const psd_layer_effects_inner_shadow* innerShadow() const;
const psd_layer_effects_outer_glow* outerGlow() const;
const psd_layer_effects_inner_glow* innerGlow() const;
const psd_layer_effects_satin* satin() const;
const psd_layer_effects_color_overlay* colorOverlay() const;
const psd_layer_effects_gradient_overlay* gradientOverlay() const;
const psd_layer_effects_pattern_overlay* patternOverlay() const;
const psd_layer_effects_stroke* stroke() const;
const psd_layer_effects_bevel_emboss* bevelAndEmboss() const;
psd_layer_effects_context* context();
psd_layer_effects_drop_shadow* dropShadow();
psd_layer_effects_inner_shadow* innerShadow();
psd_layer_effects_outer_glow* outerGlow();
psd_layer_effects_inner_glow* innerGlow();
psd_layer_effects_satin* satin();
psd_layer_effects_color_overlay* colorOverlay();
psd_layer_effects_gradient_overlay* gradientOverlay();
psd_layer_effects_pattern_overlay* patternOverlay();
psd_layer_effects_stroke* stroke();
psd_layer_effects_bevel_emboss* bevelAndEmboss();
private:
struct Private;
Private * const d;
};
#endif // KIS_PSD_LAYER_STYLE_H
diff --git a/libs/image/kis_selection_based_layer.cpp b/libs/image/kis_selection_based_layer.cpp
index 73ae0469fb..3bad0c4d41 100644
--- a/libs/image/kis_selection_based_layer.cpp
+++ b/libs/image/kis_selection_based_layer.cpp
@@ -1,359 +1,358 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* 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.
*/
#include "kis_selection_based_layer.h"
#include <klocalizedstring.h>
#include "kis_debug.h"
#include <KoCompositeOpRegistry.h>
#include "kis_image.h"
#include "kis_painter.h"
#include "kis_default_bounds.h"
#include "kis_selection.h"
#include "kis_pixel_selection.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter.h"
#include "kis_raster_keyframe_channel.h"
struct Q_DECL_HIDDEN KisSelectionBasedLayer::Private
{
public:
Private() : useSelectionInProjection(true) {}
Private(const Private &rhs) : useSelectionInProjection(rhs.useSelectionInProjection) {}
KisSelectionSP selection;
KisPaintDeviceSP paintDevice;
bool useSelectionInProjection;
};
KisSelectionBasedLayer::KisSelectionBasedLayer(KisImageWSP image,
const QString &name,
KisSelectionSP selection,
- KisFilterConfigurationSP filterConfig,
- bool useGeneratorRegistry)
+ KisFilterConfigurationSP filterConfig)
: KisLayer(image.data(), name, OPACITY_OPAQUE_U8),
- KisNodeFilterInterface(filterConfig, useGeneratorRegistry),
+ KisNodeFilterInterface(filterConfig),
m_d(new Private())
{
if (!selection) {
initSelection();
} else {
setInternalSelection(selection);
}
KisImageSP imageSP = image.toStrongRef();
if (!imageSP) {
return;
}
m_d->paintDevice = KisPaintDeviceSP(new KisPaintDevice(this, imageSP->colorSpace(), KisDefaultBoundsSP(new KisDefaultBounds(image))));
connect(imageSP.data(), SIGNAL(sigSizeChanged(QPointF,QPointF)), SLOT(slotImageSizeChanged()));
}
KisSelectionBasedLayer::KisSelectionBasedLayer(const KisSelectionBasedLayer& rhs)
: KisLayer(rhs)
, KisIndirectPaintingSupport()
, KisNodeFilterInterface(rhs)
, m_d(new Private(*rhs.m_d))
{
setInternalSelection(rhs.m_d->selection);
m_d->paintDevice = new KisPaintDevice(*rhs.m_d->paintDevice.data());
}
KisSelectionBasedLayer::~KisSelectionBasedLayer()
{
delete m_d;
}
void KisSelectionBasedLayer::initSelection()
{
m_d->selection = KisSelectionSP(new KisSelection(KisDefaultBoundsSP(new KisDefaultBounds(image()))));
m_d->selection->pixelSelection()->setDefaultPixel(KoColor(Qt::white, m_d->selection->pixelSelection()->colorSpace()));
m_d->selection->setParentNode(this);
m_d->selection->updateProjection();
}
void KisSelectionBasedLayer::slotImageSizeChanged()
{
if (m_d->selection) {
/**
* Make sure exactBounds() of the selection got recalculated after
* the image changed
*/
m_d->selection->pixelSelection()->setDirty();
setDirty();
}
}
void KisSelectionBasedLayer::setImage(KisImageWSP image)
{
m_d->paintDevice->setDefaultBounds(KisDefaultBoundsSP(new KisDefaultBounds(image)));
m_d->selection->pixelSelection()->setDefaultBounds(KisDefaultBoundsSP(new KisDefaultBounds(image)));
KisLayer::setImage(image);
connect(image.data(), SIGNAL(sigSizeChanged(QPointF,QPointF)), SLOT(slotImageSizeChanged()));
}
bool KisSelectionBasedLayer::allowAsChild(KisNodeSP node) const
{
return node->inherits("KisMask");
}
KisPaintDeviceSP KisSelectionBasedLayer::original() const
{
return m_d->paintDevice;
}
KisPaintDeviceSP KisSelectionBasedLayer::paintDevice() const
{
return m_d->selection->pixelSelection();
}
bool KisSelectionBasedLayer::needProjection() const
{
return m_d->selection;
}
void KisSelectionBasedLayer::setUseSelectionInProjection(bool value) const
{
m_d->useSelectionInProjection = value;
}
KisSelectionSP KisSelectionBasedLayer::fetchComposedInternalSelection(const QRect &rect) const
{
if (!m_d->selection) return KisSelectionSP();
m_d->selection->updateProjection(rect);
KisSelectionSP tempSelection = m_d->selection;
KisIndirectPaintingSupport::ReadLocker l(this);
if (hasTemporaryTarget()) {
/**
* WARNING: we don't try to clone the selection entirely, because
* it might be unsafe for shape selections.
*
* TODO: make cloning of vector selections safe! See a comment in
* KisShapeSelection::clone().
*/
tempSelection = new KisSelection();
KisPainter::copyAreaOptimized(rect.topLeft(), m_d->selection->pixelSelection(), tempSelection->pixelSelection(), rect);
KisPainter gc2(tempSelection->pixelSelection());
setupTemporaryPainter(&gc2);
gc2.bitBlt(rect.topLeft(), temporaryTarget(), rect);
}
return tempSelection;
}
void KisSelectionBasedLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
KisPaintDeviceSP projection,
const QRect& rect) const
{
KisSelectionSP tempSelection;
if (m_d->useSelectionInProjection) {
tempSelection = fetchComposedInternalSelection(rect);
/**
* When we paint with a selection, the deselected areas will *not* be
* overwritten by copyAreaOptimized(), so we need to clear them beforehand
*/
projection->clear(rect);
}
KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect, tempSelection);
}
QRect KisSelectionBasedLayer::cropChangeRectBySelection(const QRect &rect) const
{
return m_d->selection ?
rect & m_d->selection->selectedRect() :
rect;
}
QRect KisSelectionBasedLayer::needRect(const QRect &rect, PositionToFilthy pos) const
{
Q_UNUSED(pos);
return rect;
}
void KisSelectionBasedLayer::resetCache()
{
KisImageSP imageSP = image().toStrongRef();
if (!imageSP) {
return;
}
if (!m_d->paintDevice || *m_d->paintDevice->colorSpace() != *imageSP->colorSpace()) {
m_d->paintDevice = KisPaintDeviceSP(new KisPaintDevice(KisNodeWSP(this), imageSP->colorSpace(), new KisDefaultBounds(image())));
} else {
m_d->paintDevice->clear();
}
}
KisSelectionSP KisSelectionBasedLayer::internalSelection() const
{
return m_d->selection;
}
void KisSelectionBasedLayer::setInternalSelection(KisSelectionSP selection)
{
if (selection) {
m_d->selection = new KisSelection(*selection.data());
m_d->selection->setParentNode(this);
m_d->selection->setDefaultBounds(new KisDefaultBounds(image()));
m_d->selection->updateProjection();
KisPixelSelectionSP pixelSelection = m_d->selection->pixelSelection();
if (pixelSelection->framesInterface()) {
addKeyframeChannel(pixelSelection->keyframeChannel());
enableAnimation();
}
KisImageSP imageSP = image().toStrongRef();
KIS_SAFE_ASSERT_RECOVER_RETURN(imageSP);
if (m_d->selection->pixelSelection()->defaultBounds()->bounds() != imageSP->bounds()) {
qWarning() << "WARNING: KisSelectionBasedLayer::setInternalSelection"
<< "New selection has suspicious default bounds";
qWarning() << "WARNING:" << ppVar(m_d->selection->pixelSelection()->defaultBounds()->bounds());
qWarning() << "WARNING:" << ppVar(imageSP->bounds());
}
} else {
m_d->selection = 0;
}
}
qint32 KisSelectionBasedLayer::x() const
{
return m_d->selection ? m_d->selection->x() : 0;
}
qint32 KisSelectionBasedLayer::y() const
{
return m_d->selection ? m_d->selection->y() : 0;
}
void KisSelectionBasedLayer::setX(qint32 x)
{
if (m_d->selection) {
m_d->selection->setX(x);
}
}
void KisSelectionBasedLayer::setY(qint32 y)
{
if (m_d->selection) {
m_d->selection->setY(y);
}
}
KisKeyframeChannel *KisSelectionBasedLayer::requestKeyframeChannel(const QString &id)
{
if (id == KisKeyframeChannel::Content.id()) {
KisRasterKeyframeChannel *contentChannel = m_d->selection->pixelSelection()->createKeyframeChannel(KisKeyframeChannel::Content);
contentChannel->setFilenameSuffix(".pixelselection");
return contentChannel;
}
return KisLayer::requestKeyframeChannel(id);
}
void KisSelectionBasedLayer::setDirty()
{
Q_ASSERT(image());
KisImageSP imageSP = image().toStrongRef();
if (!imageSP) {
return;
}
setDirty(imageSP->bounds());
}
QRect KisSelectionBasedLayer::extent() const
{
QRect resultRect;
if (m_d->selection) {
resultRect = m_d->selection->selectedRect();
// copy for thread safety!
KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
if (temporaryTarget) {
resultRect |= temporaryTarget->extent();
}
} else {
KisImageSP image = this->image().toStrongRef();
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(image, QRect());
resultRect = image->bounds();
}
return resultRect;
}
QRect KisSelectionBasedLayer::exactBounds() const
{
QRect resultRect;
if (m_d->selection) {
resultRect = m_d->selection->selectedExactRect();
// copy for thread safety!
KisPaintDeviceSP temporaryTarget = this->temporaryTarget();
if (temporaryTarget) {
resultRect |= temporaryTarget->exactBounds();
}
} else {
KisImageSP image = this->image().toStrongRef();
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(image, QRect());
resultRect = image->bounds();
}
return resultRect;
}
QImage KisSelectionBasedLayer::createThumbnail(qint32 w, qint32 h)
{
KisSelectionSP originalSelection = internalSelection();
KisPaintDeviceSP originalDevice = original();
return originalDevice && originalSelection ?
originalDevice->createThumbnail(w, h, 1,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags()) :
QImage();
}
diff --git a/libs/image/kis_selection_based_layer.h b/libs/image/kis_selection_based_layer.h
index 90fe72cff0..80a8eefdef 100644
--- a/libs/image/kis_selection_based_layer.h
+++ b/libs/image/kis_selection_based_layer.h
@@ -1,211 +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
* @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(KisImageWSP image, const QString &name, KisSelectionSP selection, KisFilterConfigurationSP filterConfig);
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 KisRegion&)
*/
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_types.h b/libs/image/kis_types.h
index f055bb0671..66e9011878 100644
--- a/libs/image/kis_types.h
+++ b/libs/image/kis_types.h
@@ -1,314 +1,314 @@
/*
* 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 KISTYPES_H_
#define KISTYPES_H_
#include <QVector>
#include <QPoint>
#include <QList>
#include "kritaimage_export.h"
template<class T>
class KisWeakSharedPtr;
template<class T>
class KisSharedPtr;
template<class T> class QSharedPointer;
template<class T> class QWeakPointer;
template <class T>
uint qHash(KisSharedPtr<T> ptr) {
return qHash(ptr.data());
}
template <class T>
uint qHash(KisWeakSharedPtr<T> ptr) {
return qHash(ptr.data());
}
/**
* Define lots of shared pointer versions of Krita classes.
* Shared pointer classes have the advantage of near automatic
* memory management (but beware of circular references)
* These types should never be passed by reference,
* because that will mess up their reference counter.
*
* An example of the naming pattern used:
*
* KisPaintDeviceSP is a KisSharedPtr of KisPaintDevice
* KisPaintDeviceWSP is a KisWeakSharedPtr of KisPaintDevice
* vKisPaintDeviceSP is a QVector of KisPaintDeviceSP
* vKisPaintDeviceSP_it is an iterator of vKisPaintDeviceSP
*
*/
class KisImage;
typedef KisSharedPtr<KisImage> KisImageSP;
typedef KisWeakSharedPtr<KisImage> KisImageWSP;
class KisPaintDevice;
typedef KisSharedPtr<KisPaintDevice> KisPaintDeviceSP;
typedef KisWeakSharedPtr<KisPaintDevice> KisPaintDeviceWSP;
typedef QVector<KisPaintDeviceSP> vKisPaintDeviceSP;
typedef vKisPaintDeviceSP::iterator vKisPaintDeviceSP_it;
class KisFixedPaintDevice;
typedef KisSharedPtr<KisFixedPaintDevice> KisFixedPaintDeviceSP;
class KisMask;
typedef KisSharedPtr<KisMask> KisMaskSP;
typedef KisWeakSharedPtr<KisMask> KisMaskWSP;
class KisNode;
typedef KisSharedPtr<KisNode> KisNodeSP;
typedef KisWeakSharedPtr<KisNode> KisNodeWSP;
typedef QVector<KisNodeSP> vKisNodeSP;
typedef vKisNodeSP::iterator vKisNodeSP_it;
typedef vKisNodeSP::const_iterator vKisNodeSP_cit;
class KisBaseNode;
typedef KisSharedPtr<KisBaseNode> KisBaseNodeSP;
typedef KisWeakSharedPtr<KisBaseNode> KisBaseNodeWSP;
class KisEffectMask;
typedef KisSharedPtr<KisEffectMask> KisEffectMaskSP;
typedef KisWeakSharedPtr<KisEffectMask> KisEffectMaskWSP;
class KisFilterMask;
typedef KisSharedPtr<KisFilterMask> KisFilterMaskSP;
typedef KisWeakSharedPtr<KisFilterMask> KisFilterMaskWSP;
class KisTransformMask;
typedef KisSharedPtr<KisTransformMask> KisTransformMaskSP;
typedef KisWeakSharedPtr<KisTransformMask> KisTransformMaskWSP;
class KisTransformMaskParamsInterface;
typedef QSharedPointer<KisTransformMaskParamsInterface> KisTransformMaskParamsInterfaceSP;
typedef QWeakPointer<KisTransformMaskParamsInterface> KisTransformMaskParamsInterfaceWSP;
class KisTransparencyMask;
typedef KisSharedPtr<KisTransparencyMask> KisTransparencyMaskSP;
typedef KisWeakSharedPtr<KisTransparencyMask> KisTransparencyMaskWSP;
class KisColorizeMask;
typedef KisSharedPtr<KisColorizeMask> KisColorizeMaskSP;
typedef KisWeakSharedPtr<KisColorizeMask> KisColorizeMaskWSP;
class KisLayer;
typedef KisSharedPtr<KisLayer> KisLayerSP;
typedef KisWeakSharedPtr<KisLayer> KisLayerWSP;
class KisShapeLayer;
typedef KisSharedPtr<KisShapeLayer> KisShapeLayerSP;
class KisPaintLayer;
typedef KisSharedPtr<KisPaintLayer> KisPaintLayerSP;
class KisAdjustmentLayer;
typedef KisSharedPtr<KisAdjustmentLayer> KisAdjustmentLayerSP;
class KisGeneratorLayer;
typedef KisSharedPtr<KisGeneratorLayer> KisGeneratorLayerSP;
class KisCloneLayer;
typedef KisSharedPtr<KisCloneLayer> KisCloneLayerSP;
typedef KisWeakSharedPtr<KisCloneLayer> KisCloneLayerWSP;
class KisGroupLayer;
typedef KisSharedPtr<KisGroupLayer> KisGroupLayerSP;
typedef KisWeakSharedPtr<KisGroupLayer> KisGroupLayerWSP;
class KisFileLayer;
typedef KisSharedPtr<KisFileLayer> KisFileLayerSP;
typedef KisWeakSharedPtr<KisFileLayer> KisFileLayerWSP;
class KisSelection;
typedef KisSharedPtr<KisSelection> KisSelectionSP;
typedef KisWeakSharedPtr<KisSelection> KisSelectionWSP;
class KisSelectionComponent;
typedef KisSharedPtr<KisSelectionComponent> KisSelectionComponentSP;
class KisSelectionMask;
typedef KisSharedPtr<KisSelectionMask> KisSelectionMaskSP;
class KisPixelSelection;
typedef KisSharedPtr<KisPixelSelection> KisPixelSelectionSP;
class KisHistogram;
typedef KisSharedPtr<KisHistogram> KisHistogramSP;
typedef QVector<QPoint> vKisSegments;
class KisFilter;
typedef KisSharedPtr<KisFilter> KisFilterSP;
class KisLayerStyleFilter;
typedef KisSharedPtr<KisLayerStyleFilter> KisLayerStyleFilterSP;
class KisGenerator;
typedef KisSharedPtr<KisGenerator> KisGeneratorSP;
class KisConvolutionKernel;
typedef KisSharedPtr<KisConvolutionKernel> KisConvolutionKernelSP;
class KisAnnotation;
typedef KisSharedPtr<KisAnnotation> KisAnnotationSP;
typedef QVector<KisAnnotationSP> vKisAnnotationSP;
typedef vKisAnnotationSP::iterator vKisAnnotationSP_it;
typedef vKisAnnotationSP::const_iterator vKisAnnotationSP_cit;
class KisAnimationFrameCache;
typedef KisSharedPtr<KisAnimationFrameCache> KisAnimationFrameCacheSP;
typedef KisWeakSharedPtr<KisAnimationFrameCache> KisAnimationFrameCacheWSP;
class KisPaintingAssistant;
typedef QSharedPointer<KisPaintingAssistant> KisPaintingAssistantSP;
typedef QWeakPointer<KisPaintingAssistant> KisPaintingAssistantWSP;
class KisReferenceImage;
typedef QSharedPointer<KisReferenceImage> KisReferenceImageSP;
typedef QWeakPointer<KisReferenceImage> KisReferenceImageWSP;
// Repeat iterators
class KisHLineIterator2;
template<class T> class KisRepeatHLineIteratorPixelBase;
typedef KisRepeatHLineIteratorPixelBase< KisHLineIterator2 > KisRepeatHLineConstIteratorNG;
typedef KisSharedPtr<KisRepeatHLineConstIteratorNG> KisRepeatHLineConstIteratorSP;
class KisVLineIterator2;
template<class T> class KisRepeatVLineIteratorPixelBase;
typedef KisRepeatVLineIteratorPixelBase< KisVLineIterator2 > KisRepeatVLineConstIteratorNG;
typedef KisSharedPtr<KisRepeatVLineConstIteratorNG> KisRepeatVLineConstIteratorSP;
// NG Iterators
class KisHLineIteratorNG;
typedef KisSharedPtr<KisHLineIteratorNG> KisHLineIteratorSP;
class KisHLineConstIteratorNG;
typedef KisSharedPtr<KisHLineConstIteratorNG> KisHLineConstIteratorSP;
class KisVLineIteratorNG;
typedef KisSharedPtr<KisVLineIteratorNG> KisVLineIteratorSP;
class KisVLineConstIteratorNG;
typedef KisSharedPtr<KisVLineConstIteratorNG> KisVLineConstIteratorSP;
class KisRandomConstAccessorNG;
typedef KisSharedPtr<KisRandomConstAccessorNG> KisRandomConstAccessorSP;
class KisRandomAccessorNG;
typedef KisSharedPtr<KisRandomAccessorNG> KisRandomAccessorSP;
class KisRandomSubAccessor;
typedef KisSharedPtr<KisRandomSubAccessor> KisRandomSubAccessorSP;
// Things
typedef QVector<QPointF> vQPointF;
class KisPaintOpPreset;
-typedef KisSharedPtr<KisPaintOpPreset> KisPaintOpPresetSP;
-typedef KisWeakSharedPtr<KisPaintOpPreset> KisPaintOpPresetWSP;
+typedef QSharedPointer<KisPaintOpPreset> KisPaintOpPresetSP;
+typedef QWeakPointer<KisPaintOpPreset> KisPaintOpPresetWSP;
template <typename T>
class KisPinnedSharedPtr;
class KisPaintOpSettings;
typedef KisPinnedSharedPtr<KisPaintOpSettings> KisPaintOpSettingsSP;
template <typename T>
class KisRestrictedSharedPtr;
typedef KisRestrictedSharedPtr<KisPaintOpSettings> KisPaintOpSettingsRestrictedSP;
class KisPaintOp;
typedef KisSharedPtr<KisPaintOp> KisPaintOpSP;
class KoID;
typedef QList<KoID> KoIDList;
class KoUpdater;
template<class T> class QPointer;
typedef QPointer<KoUpdater> KoUpdaterPtr;
class KisProcessingVisitor;
typedef KisSharedPtr<KisProcessingVisitor> KisProcessingVisitorSP;
class KUndo2Command;
typedef QSharedPointer<KUndo2Command> KUndo2CommandSP;
typedef QList<KisNodeSP> KisNodeList;
typedef QSharedPointer<KisNodeList> KisNodeListSP;
typedef QList<KisPaintDeviceSP> KisPaintDeviceList;
class KisStroke;
typedef QSharedPointer<KisStroke> KisStrokeSP;
typedef QWeakPointer<KisStroke> KisStrokeWSP;
typedef KisStrokeWSP KisStrokeId;
class KisFilterConfiguration;
typedef KisPinnedSharedPtr<KisFilterConfiguration> KisFilterConfigurationSP;
class KisPropertiesConfiguration;
typedef KisPinnedSharedPtr<KisPropertiesConfiguration> KisPropertiesConfigurationSP;
class KisLockedProperties;
typedef KisSharedPtr<KisLockedProperties> KisLockedPropertiesSP;
class KisProjectionUpdatesFilter;
typedef QSharedPointer<KisProjectionUpdatesFilter> KisProjectionUpdatesFilterSP;
using KisProjectionUpdatesFilterCookie = void*;
class KisAbstractProjectionPlane;
typedef QSharedPointer<KisAbstractProjectionPlane> KisAbstractProjectionPlaneSP;
typedef QWeakPointer<KisAbstractProjectionPlane> KisAbstractProjectionPlaneWSP;
class KisProjectionLeaf;
typedef QSharedPointer<KisProjectionLeaf> KisProjectionLeafSP;
typedef QWeakPointer<KisProjectionLeaf> KisProjectionLeafWSP;
class KisKeyframe;
typedef QSharedPointer<KisKeyframe> KisKeyframeSP;
typedef QWeakPointer<KisKeyframe> KisKeyframeWSP;
class KisFilterChain;
typedef KisSharedPtr<KisFilterChain> KisFilterChainSP;
class KisProofingConfiguration;
typedef QSharedPointer<KisProofingConfiguration> KisProofingConfigurationSP;
typedef QWeakPointer<KisProofingConfiguration> KisProofingConfigurationWSP;
class KisLayerComposition;
typedef QSharedPointer<KisLayerComposition> KisLayerCompositionSP;
typedef QWeakPointer<KisLayerComposition> KisLayerCompositionWSP;
class KisMirrorAxis;
typedef KisSharedPtr<KisMirrorAxis> KisMirrorAxisSP;
typedef KisWeakSharedPtr<KisMirrorAxis> KisMirrorAxisWSP;
#include <QSharedPointer>
#include <QWeakPointer>
#include <kis_shared_ptr.h>
#include <kis_restricted_shared_ptr.h>
#include <kis_pinned_shared_ptr.h>
#endif // KISTYPES_H_
diff --git a/libs/image/kis_update_time_monitor.cpp b/libs/image/kis_update_time_monitor.cpp
index 05232f8969..d35da037a0 100644
--- a/libs/image/kis_update_time_monitor.cpp
+++ b/libs/image/kis_update_time_monitor.cpp
@@ -1,258 +1,258 @@
/*
* 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_update_time_monitor.h"
#include <QGlobalStatic>
#include <QHash>
#include <QSet>
#include <QMutex>
#include <QMutexLocker>
#include <QPointF>
#include <QRect>
#include <QRegion>
#include <QFile>
#include <QDir>
#include <QElapsedTimer>
#include <QFileInfo>
#include "kis_debug.h"
#include "kis_global.h"
#include "kis_image_config.h"
#include <brushengine/kis_paintop_preset.h>
Q_GLOBAL_STATIC(KisUpdateTimeMonitor, s_instance)
struct StrokeTicket
{
StrokeTicket()
: m_jobTime(0)
, m_updateTime(0) {}
QRegion dirtyRegion;
void start() {
m_timer.start();
}
void jobCompleted() {
m_jobTime = m_timer.restart();
}
void updateCompleted() {
m_updateTime = m_timer.restart();
}
qint64 jobTime() const {
return m_jobTime;
}
qint64 updateTime() const {
return m_updateTime;
}
private:
QElapsedTimer m_timer;
qint64 m_jobTime;
qint64 m_updateTime;
};
struct Q_DECL_HIDDEN KisUpdateTimeMonitor::Private
{
Private()
: jobsTime(0),
responseTime(0),
numTickets(0),
numUpdates(0),
mousePath(0.0),
loggingEnabled(false)
{
loggingEnabled = KisImageConfig(true).enablePerfLog();
}
QHash<void*, StrokeTicket*> preliminaryTickets;
QSet<StrokeTicket*> finishedTickets;
qint64 jobsTime;
qint64 responseTime;
qint32 numTickets;
qint32 numUpdates;
QMutex mutex;
qreal mousePath;
QPointF lastMousePos;
QElapsedTimer strokeTime;
KisPaintOpPresetSP preset;
bool loggingEnabled;
};
KisUpdateTimeMonitor::KisUpdateTimeMonitor()
: m_d(new Private)
{
if (m_d->loggingEnabled) {
QDir dir;
if (dir.exists("log")) {
dir.remove("log");
}
dir.mkdir("log");
}
}
KisUpdateTimeMonitor::~KisUpdateTimeMonitor()
{
delete m_d;
}
KisUpdateTimeMonitor* KisUpdateTimeMonitor::instance()
{
return s_instance;
}
void KisUpdateTimeMonitor::startStrokeMeasure()
{
if (!m_d->loggingEnabled) return;
QMutexLocker locker(&m_d->mutex);
m_d->jobsTime = 0;
m_d->responseTime = 0;
m_d->numTickets = 0;
m_d->numUpdates = 0;
m_d->mousePath = 0;
m_d->lastMousePos = QPointF();
m_d->preset = 0;
m_d->strokeTime.start();
}
void KisUpdateTimeMonitor::endStrokeMeasure()
{
if (!m_d->loggingEnabled) return;
QMutexLocker locker(&m_d->mutex);
if(m_d->numTickets) {
printValues();
}
}
void KisUpdateTimeMonitor::reportPaintOpPreset(KisPaintOpPresetSP preset)
{
if (!m_d->loggingEnabled) return;
m_d->preset = preset;
}
void KisUpdateTimeMonitor::reportMouseMove(const QPointF &pos)
{
if (!m_d->loggingEnabled) return;
QMutexLocker locker(&m_d->mutex);
if (!m_d->lastMousePos.isNull()) {
qreal distance = kisDistance(m_d->lastMousePos, pos);
m_d->mousePath += distance;
}
m_d->lastMousePos = pos;
}
void KisUpdateTimeMonitor::printValues()
{
qint64 strokeTime = m_d->strokeTime.elapsed();
qreal responseTime = qreal(m_d->responseTime) / m_d->numTickets;
qreal nonUpdateTime = qreal(m_d->jobsTime) / m_d->numTickets;
qreal jobsPerUpdate = qreal(m_d->numTickets) / m_d->numUpdates;
qreal mouseSpeed = qreal(m_d->mousePath) / strokeTime;
QString prefix;
if (m_d->preset) {
- KisPaintOpPresetSP preset = m_d->preset->clone();
+ KoResourceSP preset = m_d->preset->clone();
prefix = QString("%1.").arg(preset->name());
preset->setFilename(QString("log/%1.kpp").arg(preset->name()));
preset->save();
}
QFile logFile(QString("log/%1stroke.rdata").arg(prefix));
logFile.open(QIODevice::Append);
QTextStream stream(&logFile);
stream << i18n("Stroke Time:") << strokeTime << "\t"
<< i18n("Mouse Speed:") << QString::number( mouseSpeed, 'f', 3 ) << "\t"
<< i18n("Jobs/Update:") << QString::number( jobsPerUpdate, 'f', 3 ) << "\t"
<< i18n("Non Update Time:") << QString::number( nonUpdateTime, 'f', 3 ) << "\t"
<< i18n("Response Time:") << responseTime << endl; // 'endl' will use the correct OS line ending
logFile.close();
}
void KisUpdateTimeMonitor::reportJobStarted(void *key)
{
if (!m_d->loggingEnabled) return;
QMutexLocker locker(&m_d->mutex);
StrokeTicket *ticket = new StrokeTicket();
ticket->start();
m_d->preliminaryTickets.insert(key, ticket);
}
void KisUpdateTimeMonitor::reportJobFinished(void *key, const QVector<QRect> &rects)
{
if (!m_d->loggingEnabled) return;
QMutexLocker locker(&m_d->mutex);
StrokeTicket *ticket = m_d->preliminaryTickets.take(key);
if( ticket ){
ticket->jobCompleted();
Q_FOREACH (const QRect &rect, rects) {
ticket->dirtyRegion += rect;
}
m_d->finishedTickets.insert(ticket);
}
}
void KisUpdateTimeMonitor::reportUpdateFinished(const QRect &rect)
{
if (!m_d->loggingEnabled) return;
QMutexLocker locker(&m_d->mutex);
Q_FOREACH (StrokeTicket *ticket, m_d->finishedTickets) {
ticket->dirtyRegion -= rect;
if(ticket->dirtyRegion.isEmpty()) {
ticket->updateCompleted();
m_d->jobsTime += ticket->jobTime();
m_d->responseTime += ticket->jobTime() + ticket->updateTime();
m_d->numTickets++;
m_d->finishedTickets.remove(ticket);
delete ticket;
}
}
m_d->numUpdates++;
}
diff --git a/libs/image/layerstyles/kis_ls_stroke_filter.cpp b/libs/image/layerstyles/kis_ls_stroke_filter.cpp
index db94fec07c..548e593f08 100644
--- a/libs/image/layerstyles/kis_ls_stroke_filter.cpp
+++ b/libs/image/layerstyles/kis_ls_stroke_filter.cpp
@@ -1,171 +1,170 @@
/*
* 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_ls_stroke_filter.h"
#include <cstdlib>
#include <QBitArray>
#include <resources/KoPattern.h>
#include <resources/KoAbstractGradient.h>
#include "psd.h"
#include "kis_convolution_kernel.h"
#include "kis_convolution_painter.h"
#include "kis_gaussian_kernel.h"
#include "kis_pixel_selection.h"
#include "kis_fill_painter.h"
#include "kis_gradient_painter.h"
#include "kis_iterator_ng.h"
#include "kis_random_accessor_ng.h"
#include "kis_psd_layer_style.h"
#include "kis_layer_style_filter_environment.h"
#include "kis_ls_utils.h"
#include "kis_multiple_projection.h"
#include "kis_cached_paint_device.h"
#include "krita_utils.h"
#include "KisLayerStyleKnockoutBlower.h"
namespace {
int borderSize(psd_stroke_position position, int size)
{
int border = 0;
switch (position) {
case psd_stroke_outside:
border = size + 1;
break;
case psd_stroke_center:
border = qCeil(0.5 * size) + 1;
break;
case psd_stroke_inside:
border = 1;
break;
}
return border;
}
}
KisLsStrokeFilter::KisLsStrokeFilter()
: KisLayerStyleFilter(KoID("lsstroke", i18n("Stroke (style)")))
{
}
KisLsStrokeFilter::KisLsStrokeFilter(const KisLsStrokeFilter &rhs)
: KisLayerStyleFilter(rhs)
{
}
KisLayerStyleFilter *KisLsStrokeFilter::clone() const
{
return new KisLsStrokeFilter(*this);
}
void KisLsStrokeFilter::applyStroke(KisPaintDeviceSP srcDevice,
KisMultipleProjection *dst,
KisLayerStyleKnockoutBlower *blower,
const QRect &applyRect,
const psd_layer_effects_stroke *config,
KisLayerStyleFilterEnvironment *env) const
{
if (applyRect.isEmpty()) return;
const QRect needRect = kisGrowRect(applyRect, borderSize(config->position(), config->size()));
- KisSelectionSP baseSelection = blower->knockoutSelectionLazy();
+ KisSelectionSP finalBlowerSelection = blower->knockoutSelectionLazy();
+
+ KisCachedSelection::Guard s1(*env->cachedSelection());
+ KisSelectionSP baseSelection = s1.selection();
KisLsUtils::selectionFromAlphaChannel(srcDevice, baseSelection, needRect);
KisPixelSelectionSP selection = baseSelection->pixelSelection();
-// KritaUtils::filterAlpha8Device(selection, needRect,
-// [](quint8 pixel) {
-// return pixel > 0 ? 255 : 0;
-// });
-// KisGaussianKernel::applyGaussian(selection, needRect, 1.0, 1.0, QBitArray(), 0);
-
{
KisCachedSelection::Guard s2(*env->cachedSelection());
KisPixelSelectionSP knockOutSelection = s2.selection()->pixelSelection();
knockOutSelection->makeCloneFromRough(selection, needRect);
if (config->position() == psd_stroke_outside) {
KisGaussianKernel::applyDilate(selection, needRect, config->size(), QBitArray(), 0, true);
} else if (config->position() == psd_stroke_inside) {
KisGaussianKernel::applyErodeU8(knockOutSelection, needRect, config->size(), QBitArray(), 0, true);
} else if (config->position() == psd_stroke_center) {
KisGaussianKernel::applyDilate(selection, needRect, 0.5 * config->size(), QBitArray(), 0, true);
KisGaussianKernel::applyErodeU8(knockOutSelection, needRect, 0.5 * config->size(), QBitArray(), 0, true);
}
KisPainter gc(selection);
gc.setCompositeOp(COMPOSITE_ERASE);
- gc.bitBlt(needRect.topLeft(), knockOutSelection, needRect);
+ gc.bitBlt(applyRect.topLeft(), knockOutSelection, applyRect);
gc.end();
+
+ KisPainter::copyAreaOptimized(applyRect.topLeft(), selection, finalBlowerSelection->pixelSelection(), applyRect);
}
const QString compositeOp = config->blendMode();
const quint8 opacityU8 = quint8(qRound(255.0 / 100.0 * config->opacity()));
KisPaintDeviceSP dstDevice = dst->getProjection(KisMultipleProjection::defaultProjectionId(),
compositeOp,
opacityU8,
QBitArray(),
srcDevice);
KisLsUtils::fillOverlayDevice(dstDevice, applyRect, config, env);
}
void KisLsStrokeFilter::processDirectly(KisPaintDeviceSP src,
KisMultipleProjection *dst,
KisLayerStyleKnockoutBlower *blower,
const QRect &applyRect,
KisPSDLayerStyleSP style,
KisLayerStyleFilterEnvironment *env) const
{
Q_UNUSED(env);
KIS_ASSERT_RECOVER_RETURN(style);
const psd_layer_effects_stroke *config = style->stroke();
if (!KisLsUtils::checkEffectEnabled(config, dst)) return;
KisLsUtils::LodWrapper<psd_layer_effects_stroke> w(env->currentLevelOfDetail(), config);
applyStroke(src, dst, blower, applyRect, w.config, env);
}
QRect KisLsStrokeFilter::neededRect(const QRect &rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const
{
const psd_layer_effects_stroke *config = style->stroke();
if (!config->effectEnabled()) return rect;
KisLsUtils::LodWrapper<psd_layer_effects_stroke> w(env->currentLevelOfDetail(), config);
return kisGrowRect(rect, borderSize(w.config->position(), w.config->size()));
}
QRect KisLsStrokeFilter::changedRect(const QRect &rect, KisPSDLayerStyleSP style, KisLayerStyleFilterEnvironment *env) const
{
return neededRect(rect, style, env);
}
diff --git a/libs/image/layerstyles/kis_ls_utils.cpp b/libs/image/layerstyles/kis_ls_utils.cpp
index 4422016f11..12044ec6e5 100644
--- a/libs/image/layerstyles/kis_ls_utils.cpp
+++ b/libs/image/layerstyles/kis_ls_utils.cpp
@@ -1,592 +1,592 @@
/*
* 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_ls_utils.h"
#include <resources/KoAbstractGradient.h>
#include <KoColorSpace.h>
#include <resources/KoPattern.h>
#include "psd.h"
#include "kis_default_bounds.h"
#include "kis_pixel_selection.h"
#include "kis_random_accessor_ng.h"
#include "kis_iterator_ng.h"
#include "kis_convolution_kernel.h"
#include "kis_convolution_painter.h"
#include "kis_gaussian_kernel.h"
#include "kis_fill_painter.h"
#include "kis_gradient_painter.h"
#include "kis_layer_style_filter_environment.h"
#include "kis_selection_filters.h"
#include "kis_multiple_projection.h"
#include "kis_default_bounds_base.h"
#include "kis_cached_paint_device.h"
namespace KisLsUtils
{
QRect growSelectionUniform(KisPixelSelectionSP selection, int growSize, const QRect &applyRect)
{
QRect changeRect = applyRect;
if (growSize > 0) {
KisGrowSelectionFilter filter(growSize, growSize);
changeRect = filter.changeRect(applyRect, selection->defaultBounds());
filter.process(selection, applyRect);
} else if (growSize < 0) {
KisShrinkSelectionFilter filter(qAbs(growSize), qAbs(growSize), false);
changeRect = filter.changeRect(applyRect, selection->defaultBounds());
filter.process(selection, applyRect);
}
return changeRect;
}
void selectionFromAlphaChannel(KisPaintDeviceSP srcDevice,
KisSelectionSP dstSelection,
const QRect &srcRect)
{
const KoColorSpace *cs = srcDevice->colorSpace();
KisPixelSelectionSP selection = dstSelection->pixelSelection();
KisSequentialConstIterator srcIt(srcDevice, srcRect);
KisSequentialIterator dstIt(selection, srcRect);
while (srcIt.nextPixel() && dstIt.nextPixel()) {
quint8 *dstPtr = dstIt.rawData();
const quint8* srcPtr = srcIt.rawDataConst();
*dstPtr = cs->opacityU8(srcPtr);
}
}
void findEdge(KisPixelSelectionSP selection, const QRect &applyRect, const bool edgeHidden)
{
KisSequentialIterator dstIt(selection, applyRect);
if (edgeHidden) {
while(dstIt.nextPixel()) {
quint8 *pixelPtr = dstIt.rawData();
*pixelPtr =
(*pixelPtr < 24) ?
*pixelPtr * 10 : 0xFF;
}
} else {
while(dstIt.nextPixel()) {
quint8 *pixelPtr = dstIt.rawData();
*pixelPtr = 0xFF;
}
}
}
QRect growRectFromRadius(const QRect &rc, int radius)
{
int halfSize = KisGaussianKernel::kernelSizeFromRadius(radius) / 2;
return rc.adjusted(-halfSize, -halfSize, halfSize, halfSize);
}
void applyGaussianWithTransaction(KisPixelSelectionSP selection,
const QRect &applyRect,
qreal radius)
{
KisGaussianKernel::applyGaussian(selection, applyRect,
radius, radius,
QBitArray(), 0, true,
BORDER_IGNORE);
}
namespace Private {
void getGradientTable(const KoAbstractGradient *gradient,
QVector<KoColor> *table,
const KoColorSpace *colorSpace)
{
KIS_ASSERT_RECOVER_RETURN(table->size() == 256);
for (int i = 0; i < 256; i++) {
gradient->colorAt(((*table)[i]), qreal(i) / 255.0);
(*table)[i].convertTo(colorSpace);
}
}
struct LinearGradientIndex
{
int popOneIndex(int selectionAlpha) {
return 255 - selectionAlpha;
}
bool nextPixel() {
return true;
}
};
struct JitterGradientIndex
{
JitterGradientIndex(const QRect &applyRect,
int jitter,
const KisLayerStyleFilterEnvironment *env)
: randomSelection(env->cachedRandomSelection(applyRect)),
noiseIt(randomSelection, applyRect),
m_jitterCoeff(jitter * 255 / 100)
{
}
int popOneIndex(int selectionAlpha) {
int gradientIndex = 255 - selectionAlpha;
gradientIndex += m_jitterCoeff * *noiseIt.rawDataConst() >> 8;
gradientIndex &= 0xFF;
return gradientIndex;
}
bool nextPixel() {
return noiseIt.nextPixel();
}
private:
KisPixelSelectionSP randomSelection;
KisSequentialConstIterator noiseIt;
int m_jitterCoeff;
};
template <class IndexFetcher>
void applyGradientImpl(KisPaintDeviceSP device,
KisPixelSelectionSP selection,
const QRect &applyRect,
const QVector<KoColor> &table,
bool edgeHidden,
IndexFetcher &indexFetcher)
{
KIS_ASSERT_RECOVER_RETURN(
*table.first().colorSpace() == *device->colorSpace());
const KoColorSpace *cs = device->colorSpace();
const int pixelSize = cs->pixelSize();
KisSequentialConstIterator selIt(selection, applyRect);
KisSequentialIterator dstIt(device, applyRect);
if (edgeHidden) {
while (selIt.nextPixel() &&
dstIt.nextPixel() &&
indexFetcher.nextPixel()) {
quint8 selAlpha = *selIt.rawDataConst();
int gradientIndex = indexFetcher.popOneIndex(selAlpha);
const KoColor &color = table[gradientIndex];
quint8 tableAlpha = color.opacityU8();
memcpy(dstIt.rawData(), color.data(), pixelSize);
if (selAlpha < 24 && tableAlpha == 255) {
tableAlpha = int(selAlpha) * 10 * tableAlpha >> 8;
cs->setOpacity(dstIt.rawData(), tableAlpha, 1);
}
}
} else {
while (selIt.nextPixel() &&
dstIt.nextPixel() &&
indexFetcher.nextPixel()) {
int gradientIndex = indexFetcher.popOneIndex(*selIt.rawDataConst());
const KoColor &color = table[gradientIndex];
memcpy(dstIt.rawData(), color.data(), pixelSize);
}
}
}
void applyGradient(KisPaintDeviceSP device,
KisPixelSelectionSP selection,
const QRect &applyRect,
const QVector<KoColor> &table,
bool edgeHidden,
int jitter,
const KisLayerStyleFilterEnvironment *env)
{
if (!jitter) {
LinearGradientIndex fetcher;
applyGradientImpl(device, selection, applyRect, table, edgeHidden, fetcher);
} else {
JitterGradientIndex fetcher(applyRect, jitter, env);
applyGradientImpl(device, selection, applyRect, table, edgeHidden, fetcher);
}
}
}
const int noiseNeedBorder = 8;
void applyNoise(KisPixelSelectionSP selection,
const QRect &applyRect,
int noise,
const psd_layer_effects_context *context,
KisLayerStyleFilterEnvironment *env)
{
Q_UNUSED(context);
const QRect overlayRect = kisGrowRect(applyRect, noiseNeedBorder);
KisPixelSelectionSP randomSelection = env->cachedRandomSelection(overlayRect);
KisCachedSelection::Guard s1(*env->cachedSelection());
KisPixelSelectionSP randomOverlay = s1.selection()->pixelSelection();
KisSequentialConstIterator noiseIt(randomSelection, overlayRect);
KisSequentialConstIterator srcIt(selection, overlayRect);
KisRandomAccessorSP dstIt = randomOverlay->createRandomAccessorNG(overlayRect.x(), overlayRect.y());
while (noiseIt.nextPixel() && srcIt.nextPixel()) {
int itX = noiseIt.x();
int itY = noiseIt.y();
int x = itX + (*noiseIt.rawDataConst() >> 4) - 8;
int y = itY + (*noiseIt.rawDataConst() & 0x0F) - 8;
x = (x + itX) >> 1;
y = (y + itY) >> 1;
dstIt->moveTo(x, y);
quint8 dstAlpha = *dstIt->rawData();
quint8 srcAlpha = *srcIt.rawDataConst();
int value = qMin(255, dstAlpha + srcAlpha);
*dstIt->rawData() = value;
}
noise = noise * 255 / 100;
KisPainter gc(selection);
gc.setOpacity(noise);
gc.setCompositeOp(COMPOSITE_COPY);
gc.bitBlt(applyRect.topLeft(), randomOverlay, applyRect);
}
//const int FULL_PERCENT_RANGE = 100;
void adjustRange(KisPixelSelectionSP selection, const QRect &applyRect, const int range)
{
KIS_ASSERT_RECOVER_RETURN(range >= 1 && range <= 100);
quint8 rangeTable[256];
for(int i = 0; i < 256; i ++) {
quint8 value = i * 100 / range;
rangeTable[i] = qMin(value, quint8(255));
}
KisSequentialIterator dstIt(selection, applyRect);
while (dstIt.nextPixel()) {
quint8 *pixelPtr = dstIt.rawData();
*pixelPtr = rangeTable[*pixelPtr];
}
}
void applyContourCorrection(KisPixelSelectionSP selection,
const QRect &applyRect,
const quint8 *lookup_table,
bool antiAliased,
bool edgeHidden)
{
quint8 contour[PSD_LOOKUP_TABLE_SIZE] = {
0x00, 0x0b, 0x16, 0x21, 0x2c, 0x37, 0x42, 0x4d, 0x58, 0x63, 0x6e, 0x79, 0x84, 0x8f, 0x9a, 0xa5,
0xb0, 0xbb, 0xc6, 0xd1, 0xdc, 0xf2, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
if (edgeHidden) {
if (antiAliased) {
for (int i = 0; i < PSD_LOOKUP_TABLE_SIZE; i++) {
contour[i] = contour[i] * lookup_table[i] >> 8;
}
} else {
for (int i = 0; i < PSD_LOOKUP_TABLE_SIZE; i++) {
contour[i] = contour[i] * lookup_table[(int)((int)(i / 2.55) * 2.55 + 0.5)] >> 8;
}
}
} else {
if (antiAliased) {
for (int i = 0; i < PSD_LOOKUP_TABLE_SIZE; i++) {
contour[i] = lookup_table[i];
}
} else {
for (int i = 0; i < PSD_LOOKUP_TABLE_SIZE; i++) {
contour[i] = lookup_table[(int)((int)(i / 2.55) * 2.55 + 0.5)];
}
}
}
KisSequentialIterator dstIt(selection, applyRect);
while (dstIt.nextPixel()) {
quint8 *pixelPtr = dstIt.rawData();
*pixelPtr = contour[*pixelPtr];
}
}
void knockOutSelection(KisPixelSelectionSP selection,
KisPixelSelectionSP knockOutSelection,
const QRect &srcRect,
const QRect &dstRect,
const QRect &totalNeedRect,
const bool knockOutInverted)
{
KIS_ASSERT_RECOVER_RETURN(knockOutSelection);
QRect knockOutRect = !knockOutInverted ? srcRect : totalNeedRect;
knockOutRect &= dstRect;
KisPainter gc(selection);
gc.setCompositeOp(COMPOSITE_ERASE);
gc.bitBlt(knockOutRect.topLeft(), knockOutSelection, knockOutRect);
}
void fillPattern(KisPaintDeviceSP fillDevice,
const QRect &applyRect,
KisLayerStyleFilterEnvironment *env,
int scale,
- KoPattern *pattern,
+ KoPatternSP pattern,
int horizontalPhase,
int verticalPhase,
bool alignWithLayer)
{
if (scale != 100) {
warnKrita << "KisLsOverlayFilter::applyOverlay(): Pattern scaling is NOT implemented!";
}
KIS_SAFE_ASSERT_RECOVER_RETURN(pattern);
QSize psize(pattern->width(), pattern->height());
QPoint patternOffset(qreal(psize.width()) * horizontalPhase / 100,
qreal(psize.height()) * verticalPhase / 100);
const QRect boundsRect = alignWithLayer ?
env->layerBounds() : env->defaultBounds();
patternOffset += boundsRect.topLeft();
patternOffset.rx() %= psize.width();
patternOffset.ry() %= psize.height();
QRect fillRect = applyRect | applyRect.translated(patternOffset);
KisFillPainter gc(fillDevice);
gc.fillRect(fillRect.x(), fillRect.y(),
fillRect.width(), fillRect.height(), pattern, -patternOffset);
gc.end();
}
void fillOverlayDevice(KisPaintDeviceSP fillDevice,
const QRect &applyRect,
const psd_layer_effects_overlay_base *config,
KisLayerStyleFilterEnvironment *env)
{
if (config->fillType() == psd_fill_solid_color) {
KoColor color(config->color(), fillDevice->colorSpace());
fillDevice->setDefaultPixel(color);
} else if (config->fillType() == psd_fill_pattern) {
fillPattern(fillDevice, applyRect, env,
config->scale(), config->pattern(),
config->horizontalPhase(),
config->verticalPhase(),
config->alignWithLayer());
} else if (config->fillType() == psd_fill_gradient) {
const QRect boundsRect = config->alignWithLayer() ?
env->layerBounds() : env->defaultBounds();
QPoint center = boundsRect.center();
center += QPoint(boundsRect.width() * config->gradientXOffset() / 100,
boundsRect.height() * config->gradientYOffset() / 100);
int width = (boundsRect.width() * config->scale() + 100) / 200;
int height = (boundsRect.height() * config->scale() + 100) / 200;
/* copy paste from libpsd */
int angle = config->angle();
int corner_angle = (int)(atan((qreal)boundsRect.height() / boundsRect.width()) * 180 / M_PI + 0.5);
int sign_x = 1;
int sign_y = 1;
if(angle < 0) {
angle += 360;
}
if (angle >= 90 && angle < 180) {
angle = 180 - angle;
sign_x = -1;
} else if (angle >= 180 && angle < 270) {
angle = angle - 180;
sign_x = -1;
sign_y = -1;
} else if (angle >= 270 && angle <= 360) {
angle = 360 - angle;
sign_y = -1;
}
int radius_x = 0;
int radius_y = 0;
if (angle <= corner_angle) {
radius_x = width;
radius_y = (int)(radius_x * tan(kisDegreesToRadians(qreal(angle))) + 0.5);
} else {
radius_y = height;
radius_x = (int)(radius_y / tan(kisDegreesToRadians(qreal(angle))) + 0.5);
}
int radius_corner = (int)(std::sqrt((qreal)(radius_x * radius_x + radius_y * radius_y)) + 0.5);
/* end of copy paste from libpsd */
KisGradientPainter gc(fillDevice);
- gc.setGradient(config->gradient().data());
+ gc.setGradient(config->gradient());
QPointF gradStart;
QPointF gradEnd;
KisGradientPainter::enumGradientRepeat repeat =
KisGradientPainter::GradientRepeatNone;
QPoint rectangularOffset(sign_x * radius_x, -sign_y * radius_y);
switch(config->style())
{
case psd_gradient_style_linear:
gc.setGradientShape(KisGradientPainter::GradientShapeLinear);
repeat = KisGradientPainter::GradientRepeatNone;
gradStart = center - rectangularOffset;
gradEnd = center + rectangularOffset;
break;
case psd_gradient_style_radial:
gc.setGradientShape(KisGradientPainter::GradientShapeRadial);
repeat = KisGradientPainter::GradientRepeatNone;
gradStart = center;
gradEnd = center + QPointF(radius_corner, 0);
break;
case psd_gradient_style_angle:
gc.setGradientShape(KisGradientPainter::GradientShapeConical);
repeat = KisGradientPainter::GradientRepeatNone;
gradStart = center;
gradEnd = center + rectangularOffset;
break;
case psd_gradient_style_reflected:
gc.setGradientShape(KisGradientPainter::GradientShapeLinear);
repeat = KisGradientPainter::GradientRepeatAlternate;
gradStart = center - rectangularOffset;
gradEnd = center;
break;
case psd_gradient_style_diamond:
gc.setGradientShape(KisGradientPainter::GradientShapeBiLinear);
repeat = KisGradientPainter::GradientRepeatNone;
gradStart = center - rectangularOffset;
gradEnd = center + rectangularOffset;
break;
default:
qFatal("Gradient Overlay: unknown switch case!");
break;
}
gc.paintGradient(gradStart, gradEnd,
repeat, 0.0,
config->reverse(),
applyRect);
}
}
void applyFinalSelection(const QString &projectionId,
KisSelectionSP baseSelection,
KisPaintDeviceSP srcDevice,
KisMultipleProjection *dst,
const QRect &/*srcRect*/,
const QRect &dstRect,
const psd_layer_effects_context */*context*/,
const psd_layer_effects_shadow_base *config,
const KisLayerStyleFilterEnvironment *env)
{
const KoColor effectColor(config->color(), srcDevice->colorSpace());
const QRect effectRect(dstRect);
const QString compositeOp = config->blendMode();
const quint8 opacityU8 = quint8(qRound(255.0 / 100.0 * config->opacity()));
KisPaintDeviceSP dstDevice = dst->getProjection(projectionId, compositeOp, opacityU8, QBitArray(), srcDevice);
if (config->fillType() == psd_fill_solid_color) {
KisFillPainter gc(dstDevice);
gc.setCompositeOp(COMPOSITE_COPY);
gc.setSelection(baseSelection);
gc.fillSelection(effectRect, effectColor);
gc.end();
} else if (config->fillType() == psd_fill_gradient) {
if (!config->gradient()) {
warnKrita << "KisLsUtils::applyFinalSelection: Gradient object is null! Skipping...";
return;
}
QVector<KoColor> table(256);
Private::getGradientTable(config->gradient().data(), &table, dstDevice->colorSpace());
Private::applyGradient(dstDevice, baseSelection->pixelSelection(),
effectRect, table,
true, config->jitter(), env);
}
//dstDevice->convertToQImage(0, QRect(0,0,300,300)).save("6_device_shadow.png");
}
bool checkEffectEnabled(const psd_layer_effects_shadow_base *config, KisMultipleProjection *dst)
{
bool result = config->effectEnabled();
if (!result) {
dst->freeAllProjections();
}
return result;
}
}
diff --git a/libs/image/layerstyles/kis_ls_utils.h b/libs/image/layerstyles/kis_ls_utils.h
index bd6c7e5ac7..addb30fd2e 100644
--- a/libs/image/layerstyles/kis_ls_utils.h
+++ b/libs/image/layerstyles/kis_ls_utils.h
@@ -1,129 +1,129 @@
/*
* 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_LS_UTILS_H
#define __KIS_LS_UTILS_H
#include "kis_types.h"
#include "kritaimage_export.h"
#include "kis_lod_transform.h"
-
+#include <KoPattern.h>
struct psd_layer_effects_context;
class psd_layer_effects_shadow_base;
struct psd_layer_effects_overlay_base;
class KisLayerStyleFilterEnvironment;
-class KoPattern;
+
class KisMultipleProjection;
class KisCachedSelection;
namespace KisLsUtils
{
QRect growSelectionUniform(KisPixelSelectionSP selection, int growSize, const QRect &applyRect);
KRITAIMAGE_EXPORT void selectionFromAlphaChannel(KisPaintDeviceSP srcDevice,
KisSelectionSP dstSelection,
const QRect &srcRect);
void findEdge(KisPixelSelectionSP selection, const QRect &applyRect, const bool edgeHidden);
QRect growRectFromRadius(const QRect &rc, int radius);
void applyGaussianWithTransaction(KisPixelSelectionSP selection,
const QRect &applyRect,
qreal radius);
static const int FULL_PERCENT_RANGE = 100;
void adjustRange(KisPixelSelectionSP selection, const QRect &applyRect, const int range);
void applyContourCorrection(KisPixelSelectionSP selection,
const QRect &applyRect,
const quint8 *lookup_table,
bool antiAliased,
bool edgeHidden);
extern const int noiseNeedBorder;
void applyNoise(KisPixelSelectionSP selection,
const QRect &applyRect,
int noise,
const psd_layer_effects_context *context,
KisLayerStyleFilterEnvironment *env);
void knockOutSelection(KisPixelSelectionSP selection,
KisPixelSelectionSP knockOutSelection,
const QRect &srcRect,
const QRect &dstRect,
const QRect &totalNeedRect,
const bool knockOutInverted);
void fillPattern(KisPaintDeviceSP fillDevice,
const QRect &applyRect,
KisLayerStyleFilterEnvironment *env,
int scale,
- KoPattern *pattern,
+ KoPatternSP pattern,
int horizontalPhase,
int verticalPhase,
bool alignWithLayer);
void fillOverlayDevice(KisPaintDeviceSP fillDevice,
const QRect &applyRect,
const psd_layer_effects_overlay_base *config,
KisLayerStyleFilterEnvironment *env);
void applyFinalSelection(const QString &projectionId,
KisSelectionSP baseSelection,
KisPaintDeviceSP srcDevice,
KisMultipleProjection *dst,
const QRect &srcRect,
const QRect &dstRect,
const psd_layer_effects_context *context,
const psd_layer_effects_shadow_base *config,
const KisLayerStyleFilterEnvironment *env);
bool checkEffectEnabled(const psd_layer_effects_shadow_base *config, KisMultipleProjection *dst);
template<class ConfigStruct>
struct LodWrapper
{
LodWrapper(int lod,
const ConfigStruct *srcStruct)
{
if (lod > 0) {
storage.reset(new ConfigStruct(*srcStruct));
const qreal lodScale = KisLodTransform::lodToScale(lod);
storage->scaleLinearSizes(lodScale);
config = storage.data();
} else {
config = srcStruct;
}
}
const ConfigStruct *config;
private:
QScopedPointer<ConfigStruct> storage;
};
}
#endif /* __KIS_LS_UTILS_H */
diff --git a/libs/image/tests/CMakeLists.txt b/libs/image/tests/CMakeLists.txt
index 4914680b14..fddafbc361 100644
--- a/libs/image/tests/CMakeLists.txt
+++ b/libs/image/tests/CMakeLists.txt
@@ -1,190 +1,192 @@
# cmake in some versions for some not yet known reasons fails to run automoc
# on random targets (changing target names already has an effect)
# As temporary workaround skipping build of tests on these versions for now
# See https://mail.kde.org/pipermail/kde-buildsystem/2015-June/010819.html
# extend range of affected cmake versions as needed
if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1.3 AND
NOT ${CMAKE_VERSION} VERSION_GREATER 3.2.3)
message(WARNING "Skipping krita/image/tests, CMake in at least versions 3.1.3 - 3.2.3 seems to have a problem with automoc. \n(FRIENDLY REMINDER: PLEASE DON'T BREAK THE TESTS!)")
set (HAVE_FAILING_CMAKE TRUE)
else()
set (HAVE_FAILING_CMAKE FALSE)
endif()
include_directories(
${CMAKE_BINARY_DIR}/libs/image/
${CMAKE_SOURCE_DIR}/libs/image/
${CMAKE_SOURCE_DIR}/libs/image/brushengine
${CMAKE_SOURCE_DIR}/libs/image/tiles3
${CMAKE_SOURCE_DIR}/libs/image/tiles3/swap
${CMAKE_SOURCE_DIR}/sdk/tests
)
include_Directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
)
if(HAVE_VC)
include_directories(${Vc_INCLUDE_DIR})
endif()
include(ECMAddTests)
macro_add_unittest_definitions()
set(KisRandomGeneratorDemoSources kis_random_generator_demo.cpp kimageframe.cpp)
ki18n_wrap_ui(KisRandomGeneratorDemoSources kis_random_generator_demo.ui)
add_executable(KisRandomGeneratorDemo ${KisRandomGeneratorDemoSources})
target_link_libraries(KisRandomGeneratorDemo kritaimage)
ecm_mark_as_test(KisRandomGeneratorDemo)
ecm_add_tests(
kis_base_node_test.cpp
kis_fast_math_test.cpp
kis_node_test.cpp
kis_node_facade_test.cpp
kis_fixed_paint_device_test.cpp
kis_layer_test.cpp
kis_effect_mask_test.cpp
kis_iterator_test.cpp
kis_painter_test.cpp
kis_count_visitor_test.cpp
kis_projection_test.cpp
kis_properties_configuration_test.cpp
kis_transaction_test.cpp
kis_pixel_selection_test.cpp
kis_group_layer_test.cpp
kis_paint_layer_test.cpp
kis_adjustment_layer_test.cpp
kis_annotation_test.cpp
kis_clone_layer_test.cpp
kis_convolution_painter_test.cpp
kis_crop_processing_visitor_test.cpp
kis_processing_applicator_test.cpp
kis_datamanager_test.cpp
kis_fill_painter_test.cpp
kis_filter_configuration_test.cpp
kis_filter_test.cpp
kis_filter_processing_information_test.cpp
kis_filter_registry_test.cpp
kis_filter_strategy_test.cpp
kis_gradient_painter_test.cpp
kis_image_commands_test.cpp
kis_image_test.cpp
kis_image_signal_router_test.cpp
kis_iterators_ng_test.cpp
kis_iterator_benchmark.cpp
kis_updater_context_test.cpp
kis_simple_update_queue_test.cpp
kis_stroke_test.cpp
kis_simple_stroke_strategy_test.cpp
kis_stroke_strategy_undo_command_based_test.cpp
kis_strokes_queue_test.cpp
kis_mask_test.cpp
kis_math_toolbox_test.cpp
kis_name_server_test.cpp
kis_node_commands_test.cpp
kis_node_graph_listener_test.cpp
kis_node_visitor_test.cpp
kis_paint_information_test.cpp
kis_distance_information_test.cpp
kis_paintop_test.cpp
kis_pattern_test.cpp
kis_selection_mask_test.cpp
kis_shared_ptr_test.cpp
kis_bsplines_test.cpp
kis_warp_transform_worker_test.cpp
kis_liquify_transform_worker_test.cpp
kis_transparency_mask_test.cpp
kis_types_test.cpp
kis_vec_test.cpp
kis_filter_config_widget_test.cpp
kis_mask_generator_test.cpp
kis_cubic_curve_test.cpp
kis_fixed_point_maths_test.cpp
kis_node_query_path_test.cpp
kis_filter_weights_buffer_test.cpp
kis_filter_weights_applicator_test.cpp
kis_fill_interval_test.cpp
kis_fill_interval_map_test.cpp
kis_scanline_fill_test.cpp
kis_psd_layer_style_test.cpp
kis_layer_style_projection_plane_test.cpp
kis_lod_capable_layer_offset_test.cpp
kis_algebra_2d_test.cpp
kis_marker_painter_test.cpp
kis_lazy_brush_test.cpp
kis_mask_similarity_test.cpp
KisMaskGeneratorTest.cpp
kis_layer_style_filter_environment_test.cpp
kis_asl_parser_test.cpp
KisPerStrokeRandomSourceTest.cpp
KisWatershedWorkerTest.cpp
kis_dom_utils_test.cpp
kis_transform_worker_test.cpp
kis_cs_conversion_test.cpp
kis_projection_leaf_test.cpp
kis_histogram_test.cpp
kis_onion_skin_compositor_test.cpp
kis_queues_progress_updater_test.cpp
kis_image_animation_interface_test.cpp
kis_walkers_test.cpp
kis_cage_transform_worker_test.cpp
kis_random_generator_test.cpp
kis_keyframing_test.cpp
kis_filter_mask_test.cpp
+ kis_asl_layer_style_serializer_test.cpp
+ TestAslStorage.cpp
kis_async_merger_test.cpp
LINK_LIBRARIES kritaimage Qt5::Test
NAME_PREFIX "libs-image-"
)
include(KritaAddBrokenUnitTest)
krita_add_broken_unit_test( kis_transform_mask_test.cpp
TEST_NAME kis_transform_mask_test
LINK_LIBRARIES kritaimage Qt5::Test
NAME_PREFIX "libs-image-"
)
krita_add_broken_unit_test( kis_layer_styles_test.cpp
TEST_NAME kis_layer_styles_test
LINK_LIBRARIES kritaimage Qt5::Test
NAME_PREFIX "libs-image-"
)
krita_add_broken_unit_test( kis_update_scheduler_test.cpp
TEST_NAME kis_update_scheduler_test
LINK_LIBRARIES kritaimage Qt5::Test
NAME_PREFIX "libs-image-"
)
krita_add_broken_unit_test( kis_paint_device_test.cpp
TEST_NAME kis_paint_device_test
LINK_LIBRARIES kritaimage Qt5::Test
NAME_PREFIX "libs-image-"
)
krita_add_broken_unit_test( kis_colorize_mask_test.cpp
TEST_NAME kis_colorize_mask_test
LINK_LIBRARIES kritaimage Qt5::Test
NAME_PREFIX "libs-image-"
)
krita_add_broken_unit_test( kis_selection_test.cpp
TEST_NAME kis_selection_test
LINK_LIBRARIES kritaimage Qt5::Test
NAME_PREFIX "libs-image-"
)
krita_add_broken_unit_test( kis_processings_test.cpp
TEST_NAME kis_processings_test
LINK_LIBRARIES kritaimage Qt5::Test
NAME_PREFIX "libs-image-"
)
krita_add_broken_unit_test( kis_perspective_transform_worker_test.cpp
TEST_NAME kis_perspective_transform_worker_test
LINK_LIBRARIES kritaimage Qt5::Test
NAME_PREFIX "libs-image-"
)
diff --git a/libs/image/tests/TestAslStorage.cpp b/libs/image/tests/TestAslStorage.cpp
new file mode 100644
index 0000000000..7cc4355055
--- /dev/null
+++ b/libs/image/tests/TestAslStorage.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2018 boud <boud@valdyas.org>
+ * Copyright (c) 2019 Agata Cacko <cacko.azh@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 "TestAslStorage.h"
+
+
+#include <QTest>
+#include <QImageReader>
+
+#include <KoConfig.h>
+
+#include <KisAslStorage.h>
+
+#include <testutil.h>
+
+#ifndef FILES_DATA_DIR
+#error "FILES_DATA_DIR not set. A directory with the data used for testing the importing of files in krita"
+#endif
+
+void TestAslStorage::initTestCase()
+{
+ // nothing to do
+}
+
+
+void TestAslStorage::testResourceIterator_data()
+{
+ QTest::addColumn<QString>("filename");
+ QTest::addColumn<int>("patternsCount");
+ QTest::addColumn<int>("stylesCount");
+
+ QTest::newRow("asl/test_all_style.asl") << TestUtil::fetchDataFileLazy("asl/test_all_style.asl") << 0 << 1;
+ //QTest::newRow("asl/test_all_with_pattern.asl") << TestUtil::fetchDataFileLazy("asl/test_all_with_pattern.asl") << 1 << 1;
+ QTest::newRow("asl/multiple_styles.asl") << TestUtil::fetchDataFileLazy("asl/multiple_styles.asl") << 1 << 4;
+ QTest::newRow("asl/freebie_with_pattern.asl") << TestUtil::fetchDataFileLazy("asl/testset/freebie_with_pattern.asl") << 1 << 1;
+
+
+}
+
+void TestAslStorage::testResourceIterator()
+{
+ QFETCH(QString, filename);
+ QFETCH(int, patternsCount);
+ QFETCH(int, stylesCount);
+
+ KisAslStorage storage(filename);
+
+ QSharedPointer<KisResourceStorage::ResourceIterator> iter(storage.resources(ResourceType::Patterns));
+
+ QVERIFY(iter->hasNext());
+ int count = 0;
+
+ while (iter->hasNext()) {
+ iter->next();
+ KoResourceSP res(iter->resource());
+ QVERIFY(res);
+ count++;
+ }
+ QCOMPARE(count, patternsCount + stylesCount);
+
+}
+
+void TestAslStorage::testTagIterator_data()
+{
+ QTest::addColumn<QString>("filename");
+
+ QTest::newRow("asl/test_all_style.asl") << TestUtil::fetchDataFileLazy("asl/test_all_style.asl");
+ //QTest::newRow("asl/test_all_with_pattern.asl") << TestUtil::fetchDataFileLazy("asl/test_all_with_pattern.asl") << 1 << 1;
+ QTest::newRow("asl/multiple_styles.asl") << TestUtil::fetchDataFileLazy("asl/multiple_styles.asl");
+ QTest::newRow("asl/freebie_with_pattern.asl") << TestUtil::fetchDataFileLazy("asl/testset/freebie_with_pattern.asl");
+
+
+}
+
+void TestAslStorage::testTagIterator()
+{
+ QFETCH(QString, filename);
+
+ KisAslStorage storage(filename);
+
+ QSharedPointer<KisResourceStorage::TagIterator> iter = storage.tags(ResourceType::LayerStyles);
+ int count = 0;
+ while (iter->hasNext()) {
+ iter->next();
+ count++;
+ }
+ QVERIFY(count == 0);
+
+}
+
+void TestAslStorage::testResourceItem_data()
+{
+ QTest::addColumn<QString>("filename");
+ QTest::addColumn<QString>("resource");
+
+ QTest::newRow("asl/test_all_style.asl") << TestUtil::fetchDataFileLazy("asl/test_all_style.asl") << "0701cdb9-df8a-11e4-adaf-ce8e6f81a66e_style";
+
+ //QTest::newRow("asl/test_all_with_pattern.asl") << TestUtil::fetchDataFileLazy("asl/test_all_with_pattern.asl") << 1 << 1;
+
+ QTest::newRow("asl/multiple_styles.asl") << TestUtil::fetchDataFileLazy("asl/multiple_styles.asl") << "8122fc0c-58b9-11d4-b895-a898787104c1_pattern";
+ QTest::newRow("asl/multiple_styles.asl") << TestUtil::fetchDataFileLazy("asl/multiple_styles.asl") << "81a5a778-bd9f-11d5-b8ba-b73f8571793d_style";
+ QTest::newRow("asl/multiple_styles.asl") << TestUtil::fetchDataFileLazy("asl/multiple_styles.asl") << "81a5a779-bd9f-11d5-b8ba-b73f8571793d_style";
+ QTest::newRow("asl/multiple_styles.asl") << TestUtil::fetchDataFileLazy("asl/multiple_styles.asl") << "81a5a77a-bd9f-11d5-b8ba-b73f8571793d_style";
+ QTest::newRow("asl/multiple_styles.asl") << TestUtil::fetchDataFileLazy("asl/multiple_styles.asl") << "4ae237f2-bda0-11d5-b8ba-b73f8571793d_style";
+
+
+ QTest::newRow("asl/freebie_with_pattern.asl") << TestUtil::fetchDataFileLazy("asl/testset/freebie_with_pattern.asl") << "47c8b792-b27f-11e1-a082-d6e8ee17595d_style";
+ QTest::newRow("asl/freebie_with_pattern.asl") << TestUtil::fetchDataFileLazy("asl/testset/freebie_with_pattern.asl") << "47c8b78c-b27f-11e1-a082-d6e8ee17595d_pattern";
+
+
+}
+
+void TestAslStorage::testResourceItem()
+{
+
+ QFETCH(QString, filename);
+ QFETCH(QString, resource);
+ KisAslStorage storage(filename);
+
+ KisResourceStorage::ResourceItem item = storage.resourceItem(resource);
+ QVERIFY(!item.url.isEmpty());
+
+}
+
+void TestAslStorage::testResource_data()
+{
+ QTest::addColumn<QString>("filename");
+ QTest::addColumn<QString>("resource");
+
+ QTest::newRow("asl/test_all_style.asl") << TestUtil::fetchDataFileLazy("asl/test_all_style.asl") << "0701cdb9-df8a-11e4-adaf-ce8e6f81a66e_style";
+
+ //QTest::newRow("asl/test_all_with_pattern.asl") << TestUtil::fetchDataFileLazy("asl/test_all_with_pattern.asl") << 1 << 1;
+
+ QTest::newRow("asl/multiple_styles.asl") << TestUtil::fetchDataFileLazy("asl/multiple_styles.asl") << "8122fc0c-58b9-11d4-b895-a898787104c1_pattern";
+ QTest::newRow("asl/multiple_styles.asl") << TestUtil::fetchDataFileLazy("asl/multiple_styles.asl") << "81a5a778-bd9f-11d5-b8ba-b73f8571793d_style";
+ QTest::newRow("asl/multiple_styles.asl") << TestUtil::fetchDataFileLazy("asl/multiple_styles.asl") << "81a5a779-bd9f-11d5-b8ba-b73f8571793d_style";
+ QTest::newRow("asl/multiple_styles.asl") << TestUtil::fetchDataFileLazy("asl/multiple_styles.asl") << "81a5a77a-bd9f-11d5-b8ba-b73f8571793d_style";
+ QTest::newRow("asl/multiple_styles.asl") << TestUtil::fetchDataFileLazy("asl/multiple_styles.asl") << "4ae237f2-bda0-11d5-b8ba-b73f8571793d_style";
+
+
+ QTest::newRow("asl/freebie_with_pattern.asl") << TestUtil::fetchDataFileLazy("asl/testset/freebie_with_pattern.asl") << "47c8b792-b27f-11e1-a082-d6e8ee17595d_style";
+ QTest::newRow("asl/freebie_with_pattern.asl") << TestUtil::fetchDataFileLazy("asl/testset/freebie_with_pattern.asl") << "47c8b78c-b27f-11e1-a082-d6e8ee17595d_pattern";
+
+
+}
+
+void TestAslStorage::testResource()
+{
+ QFETCH(QString, filename);
+ QFETCH(QString, resource);
+
+
+ KisAslStorage storage(filename);
+ KoResourceSP res = storage.resource(resource);
+ QVERIFY(res);
+ QCOMPARE(res->filename(), resource);
+}
+
+
+QTEST_MAIN(TestAslStorage)
+
diff --git a/libs/image/tests/TestAslStorage.h b/libs/image/tests/TestAslStorage.h
new file mode 100644
index 0000000000..664e1c2083
--- /dev/null
+++ b/libs/image/tests/TestAslStorage.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018 boud <boud@valdyas.org>
+ * Copyright (c) 2019 Agata Cacko <cacko.azh@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 TESTASLSTORAGE_H
+#define TESTASLSTORAGE_H
+
+
+#include <QObject>
+
+class TestAslStorage : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+ void testResourceIterator_data();
+ void testResourceIterator();
+ void testTagIterator_data();
+ void testTagIterator();
+ void testResourceItem_data();
+ void testResourceItem();
+ void testResource_data();
+ void testResource();
+};
+
+#endif // TESTASLSTORAGE_H
diff --git a/libs/image/tests/data/test.asl b/libs/image/tests/data/test.asl
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/libs/image/tests/kis_adjustment_layer_test.cpp b/libs/image/tests/kis_adjustment_layer_test.cpp
index 1a3add9126..92fd9eea4a 100644
--- a/libs/image/tests/kis_adjustment_layer_test.cpp
+++ b/libs/image/tests/kis_adjustment_layer_test.cpp
@@ -1,117 +1,118 @@
/*
* 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 "kis_adjustment_layer_test.h"
#include <QTest>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include "kis_adjustment_layer.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_registry.h"
#include "kis_image.h"
#include "kis_selection.h"
#include "kis_types.h"
#include "kis_datamanager.h"
#include "kis_pixel_selection.h"
#include "testutil.h"
+#include <KisGlobalResourcesInterface.h>
void KisAdjustmentLayerTest::testCreation()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "adj layer test");
KisFilterSP f = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(f);
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(kfc);
KisAdjustmentLayerSP test = new KisAdjustmentLayer(image, "test", kfc, 0);
}
void KisAdjustmentLayerTest::testSetSelection()
{
KisSelectionSP sel = new KisSelection();
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "adj layer test");
KisFilterSP f = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(f);
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(kfc);
sel->pixelSelection()->select(QRect(10, 10, 200, 200), 128);
- KisAdjustmentLayerSP l1 = new KisAdjustmentLayer(image, "bla", kfc, sel);
+ KisAdjustmentLayerSP l1 = new KisAdjustmentLayer(image, "bla", kfc->cloneWithResourcesSnapshot(), sel);
QCOMPARE(sel->selectedExactRect(), l1->internalSelection()->selectedExactRect());
}
void KisAdjustmentLayerTest::testInverted()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "adj layer test");
KisFilterSP f = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(f);
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(kfc);
KisSelectionSP sel2 = new KisSelection();
sel2->pixelSelection()->invert();
- KisAdjustmentLayerSP l2 = new KisAdjustmentLayer(image, "bla", kfc, sel2);
+ KisAdjustmentLayerSP l2 = new KisAdjustmentLayer(image, "bla", kfc->cloneWithResourcesSnapshot(), sel2);
QCOMPARE(l2->internalSelection()->selectedExactRect(), image->bounds());
KisSelectionSP sel3 = new KisSelection();
sel3->pixelSelection()->select(QRect(50, -10, 800, 30), 128);
l2->setInternalSelection(sel3);
}
void KisAdjustmentLayerTest::testSelectionParent()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "adj layer test");
KisFilterSP f = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(f);
{
KisAdjustmentLayerSP adjLayer =
- new KisAdjustmentLayer(image, "bla", f->defaultConfiguration(), 0);
+ new KisAdjustmentLayer(image, "bla", f->defaultConfiguration(KisGlobalResourcesInterface::instance())->cloneWithResourcesSnapshot(), 0);
QCOMPARE(adjLayer->internalSelection()->parentNode(), KisNodeWSP(adjLayer));
}
{
KisSelectionSP selection = new KisSelection();
KisAdjustmentLayerSP adjLayer =
- new KisAdjustmentLayer(image, "bla", f->defaultConfiguration(), selection);
+ new KisAdjustmentLayer(image, "bla", f->defaultConfiguration(KisGlobalResourcesInterface::instance())->cloneWithResourcesSnapshot(), selection);
QCOMPARE(adjLayer->internalSelection()->parentNode(), KisNodeWSP(adjLayer));
}
{
KisAdjustmentLayerSP adjLayer =
- new KisAdjustmentLayer(image, "bla", f->defaultConfiguration(), 0);
+ new KisAdjustmentLayer(image, "bla", f->defaultConfiguration(KisGlobalResourcesInterface::instance())->cloneWithResourcesSnapshot(), 0);
KisSelectionSP selection = new KisSelection();
adjLayer->setInternalSelection(selection);
QCOMPARE(adjLayer->internalSelection()->parentNode(), KisNodeWSP(adjLayer));
}
}
QTEST_MAIN(KisAdjustmentLayerTest)
diff --git a/libs/image/tests/kis_asl_layer_style_serializer_test.cpp b/libs/image/tests/kis_asl_layer_style_serializer_test.cpp
new file mode 100644
index 0000000000..80984fa7f5
--- /dev/null
+++ b/libs/image/tests/kis_asl_layer_style_serializer_test.cpp
@@ -0,0 +1,423 @@
+/*
+ * 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_asl_layer_style_serializer_test.h"
+
+#include <QTest>
+
+#include <QDomDocument>
+
+#include <KoCompositeOpRegistry.h>
+#include <resources/KoAbstractGradient.h>
+#include <resources/KoStopGradient.h>
+
+#include <resources/KoPattern.h>
+
+#include "kis_global.h"
+
+#include "testutil.h"
+#include "kis_psd_layer_style.h"
+#include "kis_asl_layer_style_serializer.h"
+#include <asl/kis_asl_reader.h>
+
+
+#define CMP(object, method, value) QCOMPARE(style->object()->method(), value)
+
+void KisAslLayerStyleSerializerTest::testReading()
+{
+ KisAslLayerStyleSerializer s;
+
+// QString srcFileName(TestUtil::fetchDataFileLazy("asl/test_all_style.asl"));
+ QString srcFileName(TestUtil::fetchDataFileLazy("asl/test_all_and_pattern.asl"));
+ QFile aslFile(srcFileName);
+ aslFile.open(QIODevice::ReadOnly);
+ s.readFromDevice(&aslFile);
+
+ QVector<KisPSDLayerStyleSP> styles = s.styles();
+
+ QVERIFY(styles.size() == 1);
+ KisPSDLayerStyleSP style = styles.first();
+
+ CMP(dropShadow, effectEnabled, true);
+ CMP(dropShadow, blendMode, COMPOSITE_MULT);
+ CMP(dropShadow, color, QColor(Qt::black));
+ CMP(dropShadow, opacity, 15);
+ CMP(dropShadow, angle, -120);
+ CMP(dropShadow, useGlobalLight, false);
+ CMP(dropShadow, distance, 2);
+ CMP(dropShadow, spread, 1);
+ CMP(dropShadow, size, 7);
+ CMP(dropShadow, antiAliased, true);
+ CMP(dropShadow, noise, 3);
+ // CMP(dropShadow, contourLookupTable,);
+
+ CMP(innerShadow, effectEnabled, true);
+ CMP(innerShadow, blendMode, COMPOSITE_DARKEN);
+ CMP(innerShadow, color, QColor(Qt::black));
+ CMP(innerShadow, opacity, 28);
+ CMP(innerShadow, angle, 120);
+ CMP(innerShadow, useGlobalLight, true);
+ CMP(innerShadow, distance, 8);
+ CMP(innerShadow, spread, 15);
+ CMP(innerShadow, size, 27);
+ CMP(innerShadow, antiAliased, false);
+ CMP(innerShadow, noise, 10);
+ // CMP(innerShadow, contourLookupTable,);
+
+ CMP(outerGlow, effectEnabled, true);
+ CMP(outerGlow, blendMode, COMPOSITE_SCREEN);
+ CMP(outerGlow, color, QColor(255, 255, 189));
+ CMP(outerGlow, opacity, 43);
+ CMP(outerGlow, spread, 23);
+ CMP(outerGlow, size, 109);
+ CMP(outerGlow, antiAliased, true);
+ CMP(outerGlow, noise, 29);
+ // CMP(outerGlow, contourLookupTable,);
+ // CMP(outerGlow, gradient,);
+ CMP(outerGlow, fillType, psd_fill_solid_color);
+ CMP(outerGlow, technique, psd_technique_precise);
+ CMP(outerGlow, range, 69);
+ CMP(outerGlow, jitter, 18);
+
+ CMP(innerGlow, effectEnabled, true);
+ CMP(innerGlow, blendMode, COMPOSITE_SCREEN);
+ CMP(innerGlow, color, QColor(255, 255, 189));
+ CMP(innerGlow, opacity, 55);
+ CMP(innerGlow, spread, 21);
+ CMP(innerGlow, size, 128);
+ CMP(innerGlow, antiAliased, true);
+ CMP(innerGlow, noise, 33);
+ // CMP(innerGlow, contourLookupTable,);
+ // CMP(innerGlow, gradient,);
+ CMP(innerGlow, fillType, psd_fill_solid_color);
+ CMP(innerGlow, technique, psd_technique_softer);
+ CMP(innerGlow, range, 32);
+ CMP(innerGlow, jitter, 22);
+ CMP(innerGlow, source, psd_glow_edge);
+
+ CMP(satin, effectEnabled, true);
+ CMP(satin, blendMode, COMPOSITE_MULT);
+ CMP(satin, color, QColor(Qt::black));
+ CMP(satin, opacity, 68);
+ CMP(satin, angle, 19);
+ CMP(satin, distance, 11);
+ CMP(satin, size, 14);
+ CMP(satin, antiAliased, false);
+ CMP(satin, invert, true);
+ // CMP(satin, contourLookupTable,);
+
+ CMP(colorOverlay, effectEnabled, true);
+ CMP(colorOverlay, blendMode, COMPOSITE_OVER);
+ CMP(colorOverlay, color, QColor(Qt::red));
+ CMP(colorOverlay, opacity, 63);
+
+ CMP(gradientOverlay, effectEnabled, true);
+ CMP(gradientOverlay, blendMode, COMPOSITE_OVER);
+ CMP(gradientOverlay, opacity, 100);
+ CMP(gradientOverlay, angle, 90);
+ CMP(gradientOverlay, style, psd_gradient_style_linear);
+ CMP(gradientOverlay, reverse, false);
+ CMP(gradientOverlay, alignWithLayer, true);
+ CMP(gradientOverlay, scale, 100);
+ CMP(gradientOverlay, gradientXOffset, 0);
+ CMP(gradientOverlay, gradientYOffset, 0);
+ //CMP(gradientOverlay, dither, );
+ CMP(gradientOverlay, gradient()->name, QString("Two Color"));
+
+ CMP(stroke, effectEnabled, true);
+ CMP(stroke, blendMode, COMPOSITE_OVER);
+ CMP(stroke, opacity, 67);
+ CMP(stroke, size, 13);
+ CMP(stroke, fillType, psd_fill_solid_color);
+ CMP(stroke, position, psd_stroke_outside);
+ CMP(stroke, color, QColor(210, 33, 87));
+
+ CMP(bevelAndEmboss, effectEnabled, true);
+
+ CMP(bevelAndEmboss, highlightBlendMode, COMPOSITE_SCREEN);
+ CMP(bevelAndEmboss, highlightOpacity, 75);
+ CMP(bevelAndEmboss, highlightColor, QColor(255, 255, 255));
+
+ CMP(bevelAndEmboss, shadowBlendMode, COMPOSITE_MULT);
+ CMP(bevelAndEmboss, shadowOpacity, 75);
+ CMP(bevelAndEmboss, shadowColor, QColor(Qt::black));
+
+ CMP(bevelAndEmboss, technique, psd_technique_softer);
+ CMP(bevelAndEmboss, style, psd_bevel_inner_bevel);
+
+ CMP(bevelAndEmboss, useGlobalLight, true);
+ CMP(bevelAndEmboss, angle, 120);
+ CMP(bevelAndEmboss, altitude, 30);
+
+ CMP(bevelAndEmboss, depth, 83);
+ CMP(bevelAndEmboss, size, 49);
+
+ CMP(bevelAndEmboss, direction, psd_direction_up);
+
+ // FIXME: contour
+ CMP(bevelAndEmboss, glossAntiAliased, false);
+ CMP(bevelAndEmboss, soften, 2);
+ CMP(bevelAndEmboss, contourEnabled, true);
+ // FIXME: contour curve
+
+ CMP(bevelAndEmboss, antiAliased, true);
+ CMP(bevelAndEmboss, contourRange, 60);
+ CMP(bevelAndEmboss, textureEnabled, false);
+
+ CMP(patternOverlay, effectEnabled, true);
+ CMP(patternOverlay, blendMode, COMPOSITE_OVER);
+ CMP(patternOverlay, opacity, 100);
+ CMP(patternOverlay, alignWithLayer, true);
+ CMP(patternOverlay, scale, 100);
+ CMP(patternOverlay, horizontalPhase, 201);
+ CMP(patternOverlay, verticalPhase, 162);
+
+ CMP(patternOverlay, pattern()->name, QString("$$$/Presets/Patterns/Patterns_pat/Bubbles=Bubbles"));
+ CMP(patternOverlay, pattern()->filename, QString("b7334da0-122f-11d4-8bb5-e27e45023b5f.pat"));
+
+}
+
+void KisAslLayerStyleSerializerTest::testWriting()
+{
+ QVector<KisPSDLayerStyleSP> styles;
+
+ QByteArray refXMLDoc;
+
+ {
+ KisAslLayerStyleSerializer s;
+
+ QString srcFileName(TestUtil::fetchDataFileLazy("asl/test_all_and_pattern.asl"));
+ QFile aslFile(srcFileName);
+ aslFile.open(QIODevice::ReadOnly);
+ s.readFromDevice(&aslFile);
+
+ styles = s.styles();
+
+ {
+ aslFile.seek(0);
+
+ KisAslReader reader;
+ QDomDocument doc = reader.readFile(&aslFile);
+ refXMLDoc = doc.toByteArray();
+ }
+ }
+
+ // now we have an initialized KisPSDLayerStyle object
+ {
+ KisAslLayerStyleSerializer s;
+
+ s.setStyles(styles);
+
+ QFile dstFile("test_written.asl");
+ dstFile.open(QIODevice::WriteOnly);
+ s.saveToDevice(&dstFile);
+ dstFile.close();
+ }
+
+ QByteArray resultXMLDoc;
+
+ {
+ QFile resultFile("test_written.asl");
+ resultFile.open(QIODevice::ReadOnly);
+
+ KisAslReader reader;
+ QDomDocument doc = reader.readFile(&resultFile);
+ resultXMLDoc = doc.toByteArray();
+ }
+
+ QFile refXMLFile("save_round_trip_src.xml");
+ refXMLFile.open(QIODevice::WriteOnly);
+ refXMLFile.write(refXMLDoc);
+ refXMLFile.close();
+
+ QFile resultXMLFile("save_round_trip_dst.xml");
+ resultXMLFile.open(QIODevice::WriteOnly);
+ resultXMLFile.write(resultXMLDoc);
+ resultXMLFile.close();
+
+ QEXPECT_FAIL("", "Tried to compare two xml files, which are not the same. The order of attributes when serializing is undefined", Continue);
+ QCOMPARE(resultXMLDoc, refXMLDoc);
+}
+
+#include <KoResourceServerProvider.h>
+
+
+void KisAslLayerStyleSerializerTest::testWritingGlobalPatterns()
+{
+ KisPSDLayerStyleSP style(new KisPSDLayerStyle());
+
+ KoResourceServer<KoPattern> *server = KoResourceServerProvider::instance()->patternServer();
+ KoPatternSP pattern = server->firstResource();
+ Q_ASSERT(pattern);
+
+ dbgKrita << ppVar(pattern->name());
+ dbgKrita << ppVar(pattern->filename());
+
+ style->patternOverlay()->setEffectEnabled(true);
+ style->patternOverlay()->setPattern(pattern);
+
+ {
+ KisAslLayerStyleSerializer s;
+
+ s.setStyles(QVector<KisPSDLayerStyleSP>() << style);
+
+ QFile dstFile("test_written_pattern_only.asl");
+ dstFile.open(QIODevice::WriteOnly);
+ s.saveToDevice(&dstFile);
+ dstFile.close();
+ }
+/*
+ QByteArray resultXMLDoc;
+
+ {
+ QFile resultFile("test_written.asl");
+ resultFile.open(QIODevice::ReadOnly);
+
+ KisAslReader reader;
+ QDomDocument doc = reader.readFile(&resultFile);
+ resultXMLDoc = doc.toByteArray();
+ }
+*/
+
+}
+
+void KisAslLayerStyleSerializerTest::testReadMultipleStyles()
+{
+ KisPSDLayerStyleSP style(new KisPSDLayerStyle());
+
+ QVector<KisPSDLayerStyleSP> styles;
+
+ {
+ KisAslLayerStyleSerializer s;
+
+ QString srcFileName(TestUtil::fetchDataFileLazy("asl/multiple_styles.asl"));
+ QFile aslFile(srcFileName);
+ aslFile.open(QIODevice::ReadOnly);
+ s.readFromDevice(&aslFile);
+
+ styles = s.styles();
+ }
+
+
+ {
+ KisAslLayerStyleSerializer s;
+
+ QString dstFileName("multiple_styles_out.asl");
+ QFile aslFile(dstFileName);
+ aslFile.open(QIODevice::WriteOnly);
+
+ s.setStyles(styles);
+ s.saveToDevice(&aslFile);
+ }
+
+ {
+ KisAslLayerStyleSerializer s;
+
+ QString srcFileName("multiple_styles_out.asl");
+ QFile aslFile(srcFileName);
+ aslFile.open(QIODevice::ReadOnly);
+ s.readFromDevice(&aslFile);
+
+ styles = s.styles();
+
+ dbgKrita << ppVar(styles.size());
+ }
+}
+
+void KisAslLayerStyleSerializerTest::testWritingGradients()
+{
+ KoStopGradient stopGradient("");
+
+ {
+ const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
+ QList<KoGradientStop> stops;
+ stops << KoGradientStop(0.0, KoColor(Qt::black, cs));
+ stops << KoGradientStop(0.3, KoColor(Qt::red, cs));
+ stops << KoGradientStop(0.6, KoColor(Qt::green, cs));
+ stops << KoGradientStop(1.0, KoColor(Qt::white, cs));
+ stopGradient.setStops(stops);
+ }
+
+ KisPSDLayerStyleSP style(new KisPSDLayerStyle());
+
+ style->outerGlow()->setEffectEnabled(true);
+ style->outerGlow()->setFillType(psd_fill_gradient);
+ style->outerGlow()->setGradient(toQShared(new KoStopGradient(stopGradient)));
+
+ style->innerGlow()->setEffectEnabled(true);
+ style->innerGlow()->setFillType(psd_fill_gradient);
+ style->innerGlow()->setGradient(toQShared(new KoStopGradient(stopGradient)));
+
+ style->gradientOverlay()->setEffectEnabled(true);
+ style->gradientOverlay()->setGradient(toQShared(new KoStopGradient(stopGradient)));
+
+ style->stroke()->setEffectEnabled(true);
+ style->stroke()->setFillType(psd_fill_gradient);
+ style->stroke()->setGradient(toQShared(new KoStopGradient(stopGradient)));
+
+ {
+ KisAslLayerStyleSerializer s;
+
+ s.setStyles(QVector<KisPSDLayerStyleSP>() << style);
+
+ QFile dstFile("test_written_stop_gradient.asl");
+ dstFile.open(QIODevice::WriteOnly);
+ s.saveToDevice(&dstFile);
+ dstFile.close();
+ }
+
+ QString xmlDoc;
+
+ {
+ QFile resultFile("test_written_stop_gradient.asl");
+ resultFile.open(QIODevice::ReadOnly);
+
+ KisAslReader reader;
+ QDomDocument doc = reader.readFile(&resultFile);
+ xmlDoc = doc.toString();
+ }
+
+ {
+ // the reference document has stripped "Idnt" field which is random
+
+ QRegExp rx("<node key=\"Idnt\" type=\"Text\" value=\".+\"/>");
+ rx.setMinimal(true);
+
+ int pos = 0;
+ while ((pos = rx.indexIn(xmlDoc, pos)) != -1) {
+ xmlDoc.remove(pos, rx.matchedLength());
+ }
+
+ {
+ //QFile xmlFile("reference_gradients.asl.xml");
+ //xmlFile.open(QIODevice::WriteOnly);
+ //xmlFile.write(xmlDoc.toLatin1());
+ //xmlFile.close();
+ }
+
+ QString refFileName(TestUtil::fetchDataFileLazy("reference_gradients.asl.xml"));
+ QFile refFile(refFileName);
+ refFile.open(QIODevice::ReadOnly);
+ QString refDoc = QString(refFile.readAll());
+
+ QEXPECT_FAIL("", "Tried to compare two gradient files, which are not the same. The order of attributes when serializing is undefined.", Continue);
+ QCOMPARE(xmlDoc, refDoc);
+ }
+}
+
+QTEST_MAIN(KisAslLayerStyleSerializerTest)
diff --git a/libs/ui/tests/kis_asl_layer_style_serializer_test.h b/libs/image/tests/kis_asl_layer_style_serializer_test.h
similarity index 100%
rename from libs/ui/tests/kis_asl_layer_style_serializer_test.h
rename to libs/image/tests/kis_asl_layer_style_serializer_test.h
diff --git a/libs/image/tests/kis_asl_parser_test.cpp b/libs/image/tests/kis_asl_parser_test.cpp
index 9f3f6e8857..584bbdb55d 100644
--- a/libs/image/tests/kis_asl_parser_test.cpp
+++ b/libs/image/tests/kis_asl_parser_test.cpp
@@ -1,331 +1,332 @@
/*
* 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_asl_parser_test.h"
#include <QTest>
#include "testutil.h"
#include <QDomDocument>
#include <resources/KoPattern.h>
#include <asl/kis_asl_reader.h>
#include <asl/kis_asl_xml_parser.h>
#include <asl/kis_asl_object_catcher.h>
#include <asl/kis_asl_callback_object_catcher.h>
#include <kis_debug.h>
void KisAslParserTest::test()
{
QString fileName(TestUtil::fetchDataFileLazy("asl/freebie.asl"));
QFile aslFile(fileName);
aslFile.open(QIODevice::ReadOnly);
KisAslReader reader;
QDomDocument doc = reader.readFile(&aslFile);
dbgKrita << ppVar(doc.toString());
KisAslObjectCatcher trivialCatcher;
KisAslXmlParser parser;
parser.parseXML(doc, trivialCatcher);
}
struct CallbackVerifier {
CallbackVerifier() : m_numCallsHappened(0) {}
void setColor(const QColor &color) {
QVERIFY(color == QColor(Qt::white));
m_numCallsHappened++;
}
void setOpacity(double opacity) {
QVERIFY(qFuzzyCompare(opacity, 75));
m_numCallsHappened++;
}
void setBlendingMode(const QString &mode) {
QVERIFY(mode == "Scrn");
m_numCallsHappened++;
}
void setEnabled(bool value) {
QVERIFY(value);
m_numCallsHappened++;
}
void setCurve(const QString &name, const QVector<QPointF> &points) {
QCOMPARE(name, QString("Linear"));
QCOMPARE(points[0], QPointF());
QCOMPARE(points[1], QPointF(255.0, 255.0));
m_numCallsHappened++;
}
void setText(const QString &text) {
QCOMPARE(text, QString("11adf7a2-a120-11e1-957c-d1ee226781a4"));
m_numCallsHappened++;
}
- void setPattern(const KoPattern *pattern) {
+ void setPattern(const KoPatternSP pattern) {
dbgKrita << ppVar(pattern->name());
dbgKrita << ppVar(pattern->filename());
//QCOMPARE(text, QString("11adf7a2-a120-11e1-957c-d1ee226781a4"));
m_numCallsHappened++;
}
int m_numCallsHappened;
};
void KisAslParserTest::testWithCallbacks()
{
using namespace std::placeholders;
QString fileName(TestUtil::fetchDataFileLazy("asl/freebie.asl"));
QFile aslFile(fileName);
aslFile.open(QIODevice::ReadOnly);
KisAslReader reader;
QDomDocument doc = reader.readFile(&aslFile);
KisAslCallbackObjectCatcher c;
CallbackVerifier verifier;
c.subscribeColor("/Styl/Lefx/IrGl/Clr ", std::bind(&CallbackVerifier::setColor, &verifier, _1));
c.subscribeUnitFloat("/Styl/Lefx/IrGl/Opct", "#Prc", std::bind(&CallbackVerifier::setOpacity, &verifier, _1));
c.subscribeEnum("/Styl/Lefx/IrGl/Md ", "BlnM", std::bind(&CallbackVerifier::setBlendingMode, &verifier, _1));
c.subscribeBoolean("/Styl/Lefx/IrGl/enab", std::bind(&CallbackVerifier::setEnabled, &verifier, _1));
c.subscribeCurve("/Styl/Lefx/OrGl/TrnS", std::bind(&CallbackVerifier::setCurve, &verifier, _1, _2));
c.subscribeText("/null/Idnt", std::bind(&CallbackVerifier::setText, &verifier, _1));
KisAslXmlParser parser;
parser.parseXML(doc, c);
QCOMPARE(verifier.m_numCallsHappened, 6);
}
#include <asl/kis_asl_xml_writer.h>
void KisAslParserTest::testASLXMLWriter()
{
KisAslXmlWriter w;
QImage testImage(QSize(16, 16), QImage::Format_ARGB32);
- KoPattern testPattern1(testImage, "Some very nice name ;)", "");
- KoPattern testPattern2(testImage, "Another very nice name ;P", "");
+ KoPatternSP testPattern1(new KoPattern(testImage, "Some very nice name ;)", ""));
+ KoPatternSP testPattern2(new KoPattern(testImage, "Another very nice name ;P", ""));
- w.enterList("Patterns");
- w.writePattern("", &testPattern1);
- w.writePattern("", &testPattern2);
+ w.enterList(ResourceType::Patterns);
+ w.writePattern("", testPattern1);
+ w.writePattern("", testPattern2);
w.leaveList();
w.enterDescriptor("", "", "null");
w.writeText("Nm ", "www.designpanoply.com - Freebie 5");
w.writeText("Idnt", "11adf7a2-a120-11e1-957c-d1ee226781a4");
w.leaveDescriptor();
w.enterDescriptor("", "", "Styl");
w.enterDescriptor("documentMode", "", "documentMode");
w.leaveDescriptor();
w.enterDescriptor("Lefx", "", "Lefx");
w.writeUnitFloat("Scl ", "#Prc", 100);
w.writeBoolean("masterFxSwitch", true);
w.enterDescriptor("DrSh", "", "DrSh");
w.writeBoolean("enab", true);
w.writeEnum("Md ", "BlnM", "Mltp");
w.writeColor("Clr ", Qt::green);
w.writeUnitFloat("Opct", "#Prc", 16);
w.writeBoolean("uglg", false);
w.writeUnitFloat("lagl", "#Prc", 100);
w.writeUnitFloat("Dstn", "#Pxl", 100);
w.writeUnitFloat("Ckmt", "#Pxl", 100);
w.writeUnitFloat("blur", "#Pxl", 100);
w.writeUnitFloat("Nose", "#Prc", 100);
w.writeBoolean("anta", true);
w.writeCurve("TrnS",
"Linear",
QVector<QPointF>() << QPointF() << QPointF(255, 255));
w.writeBoolean("layerConceals", true);
w.leaveDescriptor();
w.leaveDescriptor();
w.leaveDescriptor();
dbgKrita << ppVar(w.document().toString());
}
#include <resources/KoStopGradient.h>
#include <resources/KoSegmentGradient.h>
void KisAslParserTest::testWritingGradients()
{
KisAslXmlWriter w1;
KoSegmentGradient segmentGradient;
segmentGradient.createSegment(INTERP_LINEAR, COLOR_INTERP_RGB,
0.0, 0.3, 0.15,
Qt::black, Qt::red);
segmentGradient.createSegment(INTERP_LINEAR, COLOR_INTERP_RGB,
0.3, 0.6, 0.45,
Qt::red, Qt::green);
segmentGradient.createSegment(INTERP_LINEAR, COLOR_INTERP_RGB,
0.6, 1.0, 0.8,
Qt::green, Qt::white);
w1.writeSegmentGradient("tstG", &segmentGradient);
//dbgKrita << "===";
//dbgKrita << ppVar(w1.document().toString());
KisAslXmlWriter w2;
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QList<KoGradientStop> stops;
stops << KoGradientStop(0.0, KoColor(Qt::black, cs));
stops << KoGradientStop(0.3, KoColor(Qt::red, cs));
stops << KoGradientStop(0.6, KoColor(Qt::green, cs));
stops << KoGradientStop(1.0, KoColor(Qt::white, cs));
KoStopGradient stopGradient;
stopGradient.setStops(stops);
w2.writeStopGradient("tstG", &stopGradient);
//dbgKrita << "===";
//dbgKrita << ppVar(w2.document().toString());
QCOMPARE(w1.document().toString(),
w2.document().toString());
}
#include <asl/kis_asl_writer.h>
void KisAslParserTest::testASLWriter()
{
//QString srcFileName(TestUtil::fetchDataFileLazy("asl/testset/freebie_with_pattern.asl"));
QString srcFileName(TestUtil::fetchDataFileLazy("asl/freebie.asl"));
QDomDocument srcDoc;
{
QFile srcAslFile(srcFileName);
srcAslFile.open(QIODevice::ReadOnly);
KisAslReader reader;
srcDoc = reader.readFile(&srcAslFile);
QFile tfile("src_parsed.xml");
tfile.open(QIODevice::WriteOnly);
tfile.write(srcDoc.toByteArray());
tfile.close();
}
QString dstFileName("test.asl");
{
QFile dstAslFile(dstFileName);
dstAslFile.open(QIODevice::WriteOnly);
KisAslWriter writer;
writer.writeFile(&dstAslFile, srcDoc);
dstAslFile.flush();
dstAslFile.close();
}
QDomDocument dstDoc;
{
QFile roundTripAslFile(dstFileName);
roundTripAslFile.open(QIODevice::ReadOnly);
KisAslReader reader;
dstDoc = reader.readFile(&roundTripAslFile);
QFile tfile("dst_parsed.xml");
tfile.open(QIODevice::WriteOnly);
tfile.write(dstDoc.toByteArray());
tfile.close();
}
QCOMPARE(srcDoc.toByteArray(), dstDoc.toByteArray());
}
void KisAslParserTest::testParserWithPatterns()
{
QDir dir(QString(FILES_DATA_DIR) + QDir::separator() + "testset");
QFileInfoList files = dir.entryInfoList(QStringList() << "*.asl", QDir::Files);
int index = 0;
Q_FOREACH (const QFileInfo &fileInfo, files) {
//if (index != 12) {index++; continue;}
dbgKrita << "===" << index << "===";
dbgKrita << ppVar(fileInfo.fileName());
QFile aslFile(fileInfo.absoluteFilePath());
aslFile.open(QIODevice::ReadOnly);
KisAslReader reader;
QDomDocument doc = reader.readFile(&aslFile);
QFile xmlFile("mydata.xml");
xmlFile.open(QIODevice::WriteOnly);
xmlFile.write(doc.toByteArray());
//dbgKrita << ppVar(doc.toString());
CallbackVerifier verifier;
KisAslCallbackObjectCatcher c;
c.subscribePattern("/Patterns/KisPattern", std::bind(&CallbackVerifier::setPattern, &verifier, std::placeholders::_1));
+ c.subscribePattern("/patterns/KisPattern", std::bind(&CallbackVerifier::setPattern, &verifier, std::placeholders::_1));
KisAslXmlParser parser;
parser.parseXML(doc, c);
//QCOMPARE(verifier.m_numCallsHappened, 7);
index++;
//break;
}
}
QTEST_MAIN(KisAslParserTest)
diff --git a/libs/image/tests/kis_async_merger_test.cpp b/libs/image/tests/kis_async_merger_test.cpp
index cdb17cf040..1338d63a46 100644
--- a/libs/image/tests/kis_async_merger_test.cpp
+++ b/libs/image/tests/kis_async_merger_test.cpp
@@ -1,512 +1,513 @@
/*
* 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_async_merger_test.h"
#include "kis_merge_walker.h"
#include "kis_full_refresh_walker.h"
#include "kis_async_merger.h"
#include <QTest>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpace.h>
#include "kis_image.h"
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include "kis_clone_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_filter_mask.h"
#include "kis_selection.h"
#include "kis_paint_device_debug_utils.h"
+#include <KisGlobalResourcesInterface.h>
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "../../sdk/tests/testutil.h"
#include "kis_image_config.h"
#include "KisImageConfigNotifier.h"
void KisAsyncMergerTest::init()
{
KisImageConfig::resetConfig();
}
/*
+-----------+
|root |
| group |
| blur 1 |
| paint 2 |
| paint 1 |
+-----------+
*/
void KisAsyncMergerTest::testMerger()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 640, 441, colorSpace, "merger test");
QImage sourceImage1(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
QImage sourceImage2(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png");
QImage referenceProjection(QString(FILES_DATA_DIR) + QDir::separator() + "merged_hakonepa.png");
KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
KisPaintDeviceSP device2 = new KisPaintDevice(colorSpace);
device1->convertFromQImage(sourceImage1, 0, 0, 0);
device2->convertFromQImage(sourceImage2, 0, 0, 0);
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
- KisFilterConfigurationSP configuration = filter->defaultConfiguration();
+ KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(configuration);
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8, device2);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", 200/*OPACITY_OPAQUE*/);
- KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration, 0);
+ KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration->cloneWithResourcesSnapshot(), 0);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(blur1, groupLayer);
QRect testRect1(0,0,100,441);
QRect testRect2(100,0,400,441);
QRect testRect3(500,0,140,441);
QRect testRect4(580,381,40,40);
QRect cropRect(image->bounds());
KisMergeWalker walker(cropRect);
KisAsyncMerger merger;
walker.collectRects(paintLayer2, testRect1);
merger.startMerge(walker);
walker.collectRects(paintLayer2, testRect2);
merger.startMerge(walker);
walker.collectRects(paintLayer2, testRect3);
merger.startMerge(walker);
walker.collectRects(paintLayer2, testRect4);
merger.startMerge(walker);
// Old style merging: has artifacts at x=100 and x=500
// And should be turned on inside KisLayer
/* paintLayer2->setDirty(testRect1);
QTest::qSleep(3000);
paintLayer2->setDirty(testRect2);
QTest::qSleep(3000);
paintLayer2->setDirty(testRect3);
QTest::qSleep(3000);
paintLayer2->setDirty(testRect4);
QTest::qSleep(3000);
*/
KisLayerSP rootLayer = image->rootLayer();
QVERIFY(rootLayer->exactBounds() == image->bounds());
QImage resultProjection = rootLayer->projection()->convertToQImage(0);
resultProjection.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + "actual_merge_result.png");
QPoint pt;
QVERIFY(TestUtil::compareQImages(pt, resultProjection, referenceProjection, 5, 0, 0));
}
/**
* This in not fully automated test for child obliging in KisAsyncMerger.
* It just checks whether devices are shared. To check if the merger
* touches originals you can add a debug message to the merger
* and take a look.
*/
/*
+-----------+
|root |
| group |
| paint 1 |
+-----------+
*/
void KisAsyncMergerTest::debugObligeChild()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 640, 441, colorSpace, "merger test");
QImage sourceImage1(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
device1->convertFromQImage(sourceImage1, 0, 0, 0);
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
image->addNode(groupLayer, image->rootLayer());
image->addNode(paintLayer1, groupLayer);
QRect testRect1(0,0,640,441);
QRect cropRect(image->bounds());
KisMergeWalker walker(cropRect);
KisAsyncMerger merger;
walker.collectRects(paintLayer1, testRect1);
merger.startMerge(walker);
KisLayerSP rootLayer = image->rootLayer();
QVERIFY(rootLayer->original() == groupLayer->projection());
QVERIFY(groupLayer->original() == paintLayer1->projection());
}
/*
+--------------+
|root |
| paint 1 |
| invert_mask |
| clone_of_1 |
+--------------+
*/
void KisAsyncMergerTest::testFullRefreshWithClones()
{
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 128, 128, colorSpace, "clones test");
KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
device1->fill(image->bounds(), KoColor( Qt::white, colorSpace));
KisFilterSP filter = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(filter);
- KisFilterConfigurationSP configuration = filter->defaultConfiguration();
+ KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(configuration);
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
KisFilterMaskSP invertMask1 = new KisFilterMask();
invertMask1->initSelection(paintLayer1);
- invertMask1->setFilter(configuration);
+ invertMask1->setFilter(configuration->cloneWithResourcesSnapshot());
KisLayerSP cloneLayer1 = new KisCloneLayer(paintLayer1, image, "clone_of_1", OPACITY_OPAQUE_U8);
/**
* The clone layer must have a projection to allow us
* to read what it got from its source. Just shift it.
*/
cloneLayer1->setX(10);
cloneLayer1->setY(10);
image->addNode(cloneLayer1, image->rootLayer());
image->addNode(paintLayer1, image->rootLayer());
image->addNode(invertMask1, paintLayer1);
QRect cropRect(image->bounds());
KisFullRefreshWalker walker(cropRect);
KisAsyncMerger merger;
walker.collectRects(image->rootLayer(), image->bounds());
merger.startMerge(walker);
// Wait for additional jobs generated by the clone are finished
image->waitForDone();
QRect filledRect(10, 10,
image->width() - cloneLayer1->x(),
image->height() - cloneLayer1->y());
const int pixelSize = device1->pixelSize();
const int numPixels = filledRect.width() * filledRect.height();
QByteArray bytes(numPixels * pixelSize, 13);
cloneLayer1->projection()->readBytes((quint8*)bytes.data(), filledRect);
KoColor desiredPixel(Qt::black, colorSpace);
quint8 *srcPtr = (quint8*)bytes.data();
quint8 *dstPtr = desiredPixel.data();
for(int i = 0; i < numPixels; i++) {
if(memcmp(srcPtr, dstPtr, pixelSize)) {
dbgKrita << "expected:" << dstPtr[0] << dstPtr[1] << dstPtr[2] << dstPtr[3];
dbgKrita << "result: " << srcPtr[0] << srcPtr[1] << srcPtr[2] << srcPtr[3];
QFAIL("Failed to compare pixels");
}
srcPtr += pixelSize;
}
}
/*
+--------------+
|root |
| paint 2 |
| paint 1 |
+--------------+
*/
void KisAsyncMergerTest::testSubgraphingWithoutUpdatingParent()
{
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 128, 128, colorSpace, "clones test");
KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
device1->fill(image->bounds(), KoColor(Qt::white, colorSpace));
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
KisPaintDeviceSP device2 = new KisPaintDevice(colorSpace);
device2->fill(image->bounds(), KoColor(Qt::black, colorSpace));
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", 128, device2);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(paintLayer2, image->rootLayer());
image->initialRefreshGraph();
QImage refImage(QString(FILES_DATA_DIR) + QDir::separator() + "subgraphing_without_updating.png");
{
QImage resultImage = image->projection()->convertToQImage(0);
QCOMPARE(resultImage, refImage);
}
QRect cropRect(image->bounds());
KisRefreshSubtreeWalker walker(cropRect);
KisAsyncMerger merger;
walker.collectRects(paintLayer2, image->bounds());
merger.startMerge(walker);
{
QImage resultImage = image->projection()->convertToQImage(0);
QCOMPARE(resultImage, refImage);
}
}
#include <KoCompositeOpRegistry.h>
enum DependentNodeType {
GROUP_LAYER,
ADJUSTMENT_LAYER
};
/*
+--------------+
|root |
| (blur1) |
| group |
| blur_mask |
| paint 2 |
| paint 3 |
| paint 1 |
+--------------+
*/
void testFullRefreshForDependentNodes(const DependentNodeType dependentNode,
const bool useFilterMask,
const bool useLayerStyle)
{
const QRect imageRect = QRect(0, 0, 128, 128);
const QRect fillRect = QRect(32, 32, 64, 64);
const QRect smallRect = QRect(60, 60, 8, 8);
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), colorSpace, "blur test");
KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
if (dependentNode == GROUP_LAYER && !useLayerStyle) {
device1->fill(imageRect, KoColor(Qt::black, colorSpace));
}
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
KisPaintDeviceSP device2 = new KisPaintDevice(colorSpace);
device2->fill(fillRect, KoColor(Qt::white, colorSpace));
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8, device2);
device2->fill(QRect(63, 0, 1, 64), KoColor(Qt::green, colorSpace));
KisPaintDeviceSP device3 = new KisPaintDevice(colorSpace);
device3->fill(smallRect, KoColor(Qt::red, colorSpace));
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8, device3);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(paintLayer3, groupLayer);
KisLayerSP testingLayer = groupLayer;
if (dependentNode == ADJUSTMENT_LAYER) {
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
KIS_ASSERT(filter);
- KisFilterConfigurationSP configuration = filter->defaultConfiguration();
+ KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
KIS_ASSERT(configuration);
- KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration, 0);
+ KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration->cloneWithResourcesSnapshot(), 0);
blur1->setCompositeOpId(COMPOSITE_OVER);
image->addNode(blur1, image->rootLayer());
}
if (useFilterMask) {
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
KIS_ASSERT(filter);
- KisFilterConfigurationSP configuration = filter->defaultConfiguration();
+ KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
KIS_ASSERT(configuration);
KisFilterMaskSP blurMask1 = new KisFilterMask();
blurMask1->initSelection(groupLayer);
- blurMask1->setFilter(configuration);
+ blurMask1->setFilter(configuration->cloneWithResourcesSnapshot());
image->addNode(blurMask1, testingLayer);
}
if (useLayerStyle) {
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->context()->keep_original = true;
style->dropShadow()->setEffectEnabled(true);
style->dropShadow()->setDistance(9);
style->dropShadow()->setSpread(100);
style->dropShadow()->setSize(9);
style->dropShadow()->setNoise(0);
style->dropShadow()->setKnocksOut(false);
style->dropShadow()->setOpacity(90);
style->dropShadow()->setAngle(0);
testingLayer->setLayerStyle(style);
}
QRect cropRect(image->bounds());
KisFullRefreshWalker walker(cropRect);
KisAsyncMerger merger;
QVector<QRect> patchRects;
patchRects << QRect(0,0,64,64);
patchRects << QRect(64,0,64,64);
patchRects << QRect(0,64,64,64);
patchRects << QRect(64,64,64,64);
Q_FOREACH(const QRect &rc, patchRects) {
walker.collectRects(image->rootLayer(), rc);
merger.startMerge(walker);
}
// Wait for additional jobs generated by the clone are finished
image->waitForDone();
QString testName = dependentNode == GROUP_LAYER ? "group" : "adj";
if (useFilterMask) {
testName += "_mask";
}
if (useLayerStyle) {
testName += "_style";
}
TestUtil::checkQImage(image->projection()->convertToQImage(0, imageRect),
"async_merger_test", "dependent", testName, 3);
}
void KisAsyncMergerTest::testFullRefreshGroupWithMask()
{
testFullRefreshForDependentNodes(GROUP_LAYER, true, false);
}
void KisAsyncMergerTest::testFullRefreshGroupWithStyle()
{
testFullRefreshForDependentNodes(GROUP_LAYER, false, true);
}
void KisAsyncMergerTest::testFullRefreshGroupWithMaskAndStyle()
{
testFullRefreshForDependentNodes(GROUP_LAYER, true, true);
}
void KisAsyncMergerTest::testFullRefreshAdjustmentWithMask()
{
testFullRefreshForDependentNodes(ADJUSTMENT_LAYER, true, false);
}
void KisAsyncMergerTest::testFullRefreshAdjustmentWithStyle()
{
testFullRefreshForDependentNodes(ADJUSTMENT_LAYER, false, true);
}
/*
+---------------------------------+
|root |
| adj 2 (filter 2, color balance) |
| mask 3 (filter 3, blur) |
| paint 1 |
+---------------------------------+
*/
void KisAsyncMergerTest::testFilterMaskOnFilterLayer()
{
{
KisImageConfig cfg(false);
cfg.setUpdatePatchWidth(64);
cfg.setUpdatePatchHeight(64);
cfg.setMaxNumberOfThreads(1);
}
KisImageConfigNotifier::instance()->notifyConfigChanged();
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 128, 128, colorSpace, "masks test");
KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
device1->fill(image->bounds(), KoColor(Qt::yellow, colorSpace));
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
image->addNode(paintLayer1, image->rootLayer());
KisFilterSP filter2 = KisFilterRegistry::instance()->value("colorbalance");
KIS_ASSERT(filter2);
- KisFilterConfigurationSP configuration2 = filter2->defaultConfiguration();
+ KisFilterConfigurationSP configuration2 = filter2->defaultConfiguration(KisGlobalResourcesInterface::instance());
KIS_ASSERT(configuration2);
KisLayerSP adjLayer2 = new KisAdjustmentLayer(image, "adj2", configuration2, 0);
image->addNode(adjLayer2, image->rootLayer());
KisFilterSP filter3 = KisFilterRegistry::instance()->value("blur");
KIS_ASSERT(filter3);
- KisFilterConfigurationSP configuration3 = filter3->defaultConfiguration();
+ KisFilterConfigurationSP configuration3 = filter3->defaultConfiguration(KisGlobalResourcesInterface::instance());
KIS_ASSERT(configuration3);
KisFilterMaskSP mask3 = new KisFilterMask();
mask3->initSelection(adjLayer2);
mask3->setFilter(configuration3);
image->addNode(mask3, adjLayer2);
image->initialRefreshGraph();
QVERIFY(TestUtil::checkQImage(image->projection()->convertToQImage(0),
"async_merger_test", "mask_on_adj", "initial", 3));
}
QTEST_MAIN(KisAsyncMergerTest)
diff --git a/libs/image/tests/kis_convolution_painter_test.cpp b/libs/image/tests/kis_convolution_painter_test.cpp
index 0bcfade595..14eafc4c2e 100644
--- a/libs/image/tests/kis_convolution_painter_test.cpp
+++ b/libs/image/tests/kis_convolution_painter_test.cpp
@@ -1,474 +1,475 @@
/*
* 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 <compositeops/KoVcMultiArchBuildSupport.h> //MSVC requires that Vc come first
#include "kis_convolution_painter_test.h"
#include <QTest>
#include <QBitArray>
+#include <QElapsedTimer>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceTraits.h>
#include "kis_paint_device.h"
#include "kis_convolution_painter.h"
#include "kis_convolution_kernel.h"
#include <kis_gaussian_kernel.h>
#include <kis_mask_generator.h>
#include "testutil.h"
KisPaintDeviceSP initAsymTestDevice(QRect &imageRect, int &pixelSize, QByteArray &initialData)
{
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
pixelSize = dev->pixelSize();
imageRect = QRect(0,0,5,5);
initialData.resize(25 * pixelSize);
quint8 *ptr = (quint8*) initialData.data();
for(int i = 0; i < 25; i++) {
KoColor pixel(QColor(i,i,i,255), dev->colorSpace());
memcpy(ptr, pixel.data(), pixelSize);
ptr += pixelSize;
}
dev->writeBytes((const quint8*)initialData.constData(), imageRect);
return dev;
}
Eigen::Matrix<qreal, 3, 3> initSymmFilter(qreal &offset, qreal &factor)
{
Eigen::Matrix<qreal, 3, 3> filter;
filter(0,0) = 1.0 / 21;
filter(0,1) = 3.0 / 21;
filter(0,2) = 1.0 / 21;
filter(1,0) = 3.0 / 21;
filter(1,1) = 5.0 / 21;
filter(1,2) = 3.0 / 21;
filter(2,0) = 1.0 / 21;
filter(2,1) = 3.0 / 21;
filter(2,2) = 1.0 / 21;
offset = 0.0;
factor = 1.0;
return filter;
}
Eigen::Matrix<qreal, 3, 3> initAsymmFilter(qreal &offset, qreal &factor)
{
Eigen::Matrix<qreal, 3, 3> filter;
filter(0,0) = 1.0;
filter(1,0) = 2.0;
filter(2,0) = 1.0;
filter(0,1) = 0.0;
filter(1,1) = 1.0;
filter(2,1) = 0.0;
filter(0,2) =-1.0;
filter(1,2) =-2.0;
filter(2,2) =-1.0;
offset = 0.0;
factor = 1.0;
return filter;
}
void printPixel(QString prefix, int pixelSize, quint8 *data) {
QString str = prefix;
for(int i = 0; i < pixelSize; i++) {
str += ' ';
str += QString::number(data[i]);
}
dbgKrita << str;
}
void KisConvolutionPainterTest::testIdentityConvolution()
{
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
dev->convertFromQImage(qimage, 0, 0, 0);
KisConvolutionKernelSP kernel = new KisConvolutionKernel(3, 3, 0, 0);
kernel->data()(0) = 0;
kernel->data()(1) = 0;
kernel->data()(2) = 0;
kernel->data()(3) = 0;
kernel->data()(4) = 1;
kernel->data()(5) = 0;
kernel->data()(6) = 0;
kernel->data()(7) = 0;
kernel->data()(8) = 0;
KisConvolutionPainter gc(dev);
gc.beginTransaction();
gc.applyMatrix(kernel, dev, QPoint(0, 0), QPoint(0, 0), QSize(qimage.width(), qimage.height()));
gc.deleteTransaction();
QImage resultImage = dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height());
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, qimage, resultImage)) {
resultImage.save("identity_convolution.png");
QFAIL(QString("Identity kernel did change image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisConvolutionPainterTest::testSymmConvolution()
{
qreal offset = 0.0;
qreal factor = 1.0;
Eigen::Matrix<qreal, 3, 3> filter = initSymmFilter(offset, factor);
QRect imageRect;
int pixelSize = 0;
QByteArray initialData;
KisPaintDeviceSP dev = initAsymTestDevice(imageRect, pixelSize, initialData);
KisConvolutionKernelSP kernel =
KisConvolutionKernel::fromMatrix(filter, offset, factor);
KisConvolutionPainter gc(dev);
gc.beginTransaction();
QRect filterRect = imageRect.adjusted(1,1,-1,-1);
gc.applyMatrix(kernel, dev, filterRect.topLeft(), filterRect.topLeft(),
filterRect.size());
gc.deleteTransaction();
QByteArray resultData(initialData.size(), 0);
dev->readBytes((quint8*)resultData.data(), imageRect);
QCOMPARE(resultData, initialData);
}
void KisConvolutionPainterTest::testAsymmConvolutionImp(QBitArray channelFlags)
{
qreal offset = 0.0;
qreal factor = 1.0;
Eigen::Matrix<qreal, 3, 3> filter = initAsymmFilter(offset, factor);
QRect imageRect;
int pixelSize = -1;
QByteArray initialData;
KisPaintDeviceSP dev = initAsymTestDevice(imageRect, pixelSize, initialData);
KisConvolutionKernelSP kernel =
KisConvolutionKernel::fromMatrix(filter, offset, factor);
KisConvolutionPainter gc(dev);
gc.beginTransaction();
gc.setChannelFlags(channelFlags);
QRect filterRect = imageRect.adjusted(1,1,-1,-1);
gc.applyMatrix(kernel, dev, filterRect.topLeft(), filterRect.topLeft(),
filterRect.size());
gc.deleteTransaction();
QByteArray resultData(initialData.size(), 0);
dev->readBytes((quint8*)resultData.data(), imageRect);
QRect filteredRect = imageRect.adjusted(1, 1, -1, -1);
quint8 *srcPtr = (quint8*) initialData.data();
quint8 *resPtr = (quint8*) resultData.data();
for(int row = 0; row < imageRect.height(); row++) {
for(int col = 0; col < imageRect.width(); col++) {
bool isFiltered = filteredRect.contains(col, row);
int pixelValue = 8 + row * imageRect.width() + col;
KoColor filteredPixel(QColor(pixelValue, pixelValue, pixelValue, 255), dev->colorSpace());
KoColor resultPixel(dev->colorSpace());
for(int j = 0; j < pixelSize; j++) {
resultPixel.data()[j] = isFiltered && channelFlags[j] ?
filteredPixel.data()[j] : srcPtr[j];
}
if(memcmp(resPtr, resultPixel.data(), pixelSize)) {
printPixel("Actual: ", pixelSize, resPtr);
printPixel("Expected:", pixelSize, resultPixel.data());
QFAIL("Failed to filter area");
}
srcPtr += pixelSize;
resPtr += pixelSize;
}
}
}
void KisConvolutionPainterTest::testAsymmAllChannels()
{
QBitArray channelFlags =
KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true);
testAsymmConvolutionImp(channelFlags);
}
void KisConvolutionPainterTest::testAsymmSkipRed()
{
QBitArray channelFlags =
KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true);
channelFlags[2] = false;
testAsymmConvolutionImp(channelFlags);
}
void KisConvolutionPainterTest::testAsymmSkipGreen()
{
QBitArray channelFlags =
KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true);
channelFlags[1] = false;
testAsymmConvolutionImp(channelFlags);
}
void KisConvolutionPainterTest::testAsymmSkipBlue()
{
QBitArray channelFlags =
KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true);
channelFlags[0] = false;
testAsymmConvolutionImp(channelFlags);
}
void KisConvolutionPainterTest::testAsymmSkipAlpha()
{
QBitArray channelFlags =
KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true);
channelFlags[3] = false;
testAsymmConvolutionImp(channelFlags);
}
// #include <valgrind/callgrind.h>
void KisConvolutionPainterTest::benchmarkConvolution()
{
QImage referenceImage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
QRect imageRect(QPoint(), referenceImage.size());
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
dev->convertFromQImage(referenceImage, 0, 0, 0);
int diameter = 1;
for (int i = 0; i < 4; i++) {
KisCircleMaskGenerator* kas = new KisCircleMaskGenerator(diameter, 1.0, 5, 5, 2, false);
KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMaskGenerator(kas);
KisConvolutionPainter gc(dev);
- QTime timer; timer.start();
+ QElapsedTimer timer; timer.start();
// CALLGRIND_START_INSTRUMENTATION;
gc.beginTransaction();
gc.applyMatrix(kernel, dev, imageRect.topLeft(), imageRect.topLeft(),
imageRect.size());
gc.deleteTransaction();
// CALLGRIND_STOP_INSTRUMENTATION;
dbgKrita << "Diameter:" << diameter << "time:" << timer.elapsed();
if(diameter < 4) {
diameter += 2;
} else {
diameter += 8;
}
}
}
void KisConvolutionPainterTest::testGaussianBase(KisPaintDeviceSP dev, bool useFftw, const QString &prefix)
{
QBitArray channelFlags =
KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true);
KisPainter gc(dev);
qreal horizontalRadius = 5, verticalRadius = 5;
for(int i = 0; i < 3 ; i++, horizontalRadius+=5, verticalRadius+=5)
{
- QTime timer;
+ QElapsedTimer timer;
timer.start();
gc.beginTransaction();
if (( horizontalRadius > 0 ) && ( verticalRadius > 0 )) {
KisPaintDeviceSP interm = new KisPaintDevice(dev->colorSpace());
KisConvolutionKernelSP kernelHoriz = KisGaussianKernel::createHorizontalKernel(horizontalRadius);
KisConvolutionKernelSP kernelVertical = KisGaussianKernel::createVerticalKernel(verticalRadius);
const QRect applyRect = dev->exactBounds();
KisConvolutionPainter::TestingEnginePreference enginePreference =
useFftw ?
KisConvolutionPainter::FFTW :
KisConvolutionPainter::SPATIAL;
KisConvolutionPainter horizPainter(interm, enginePreference);
horizPainter.setChannelFlags(channelFlags);
horizPainter.applyMatrix(kernelHoriz, dev,
applyRect.topLeft() - QPoint(0, verticalRadius),
applyRect.topLeft() - QPoint(0, verticalRadius),
applyRect.size() + QSize(0, 2 * verticalRadius),
BORDER_REPEAT);
KisConvolutionPainter verticalPainter(dev, enginePreference);
verticalPainter.setChannelFlags(channelFlags);
verticalPainter.applyMatrix(kernelVertical, interm,
applyRect.topLeft(),
applyRect.topLeft(),
applyRect.size(), BORDER_REPEAT);
QImage result = dev->convertToQImage(0, applyRect.x(), applyRect.y(), applyRect.width(), applyRect.height());
QString engine = useFftw ? "fftw" : "spatial";
QString testCaseName = QString("test_gaussian_%1_%2_%3.png").arg(horizontalRadius).arg(verticalRadius).arg(engine);
TestUtil::checkQImage(result,
"convolution_painter_test",
QString("gaussian_") + prefix,
testCaseName);
gc.revertTransaction();
}
dbgKrita << "Elapsed time:" << timer.elapsed() << "ms";
}
}
void KisConvolutionPainterTest::testGaussian(bool useFftw)
{
QImage referenceImage(TestUtil::fetchDataFileLazy("kritaTransparent.png"));
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
dev->convertFromQImage(referenceImage, 0, 0, 0);
testGaussianBase(dev, useFftw, "");
}
void KisConvolutionPainterTest::testGaussianSpatial()
{
testGaussian(false);
}
void KisConvolutionPainterTest::testGaussianFFTW()
{
testGaussian(true);
}
void KisConvolutionPainterTest::testGaussianSmall(bool useFftw)
{
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
KoColor c(Qt::yellow, dev->colorSpace());
for (int i = 0; i < 50; i++) {
quint8 baseOpacity = 75;
KoColor c(Qt::magenta, dev->colorSpace());
for (int j = 0; j <= 6; j++) {
c.setOpacity(static_cast<quint8>(baseOpacity + 30 * j));
dev->setPixel(i + j, i, c);
}
}
testGaussianBase(dev, useFftw, "reduced");
}
void KisConvolutionPainterTest::testGaussianSmallSpatial()
{
testGaussianSmall(false);
}
void KisConvolutionPainterTest::testGaussianSmallFFTW()
{
testGaussianSmall(true);
}
void KisConvolutionPainterTest::testGaussianDetails(bool useFftw)
{
QImage referenceImage(TestUtil::fetchDataFileLazy("resolution_test.png"));
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
dev->convertFromQImage(referenceImage, 0, 0, 0);
testGaussianBase(dev, useFftw, "details");
}
void KisConvolutionPainterTest::testGaussianDetailsSpatial()
{
testGaussianDetails(false);
}
void KisConvolutionPainterTest::testGaussianDetailsFFTW()
{
testGaussianDetails(true);
}
#include "kis_transaction.h"
void KisConvolutionPainterTest::testDilate()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
const QRect imageRect(0,0,256,256);
dev->fill(QRect(50,50,100,20), KoColor(Qt::white, cs));
dev->fill(QRect(150,50,20,100), KoColor(Qt::white, cs));
TestUtil::checkQImage(dev->convertToQImage(0, imageRect), "convolution_painter_test", "dilate", "initial");
KisGaussianKernel::applyDilate(dev, imageRect, 10, QBitArray(), 0);
TestUtil::checkQImage(dev->convertToQImage(0, imageRect), "convolution_painter_test", "dilate", "dilate10");
}
void KisConvolutionPainterTest::testErode()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
const QRect imageRect(0,0,256,256);
dev->fill(QRect(50,50,100,20), KoColor(Qt::white, cs));
dev->fill(QRect(150,50,20,100), KoColor(Qt::white, cs));
TestUtil::checkQImage(dev->convertToQImage(0, imageRect), "convolution_painter_test", "dilate", "initial");
KisGaussianKernel::applyErodeU8(dev, imageRect, 5, QBitArray(), 0);
TestUtil::checkQImage(dev->convertToQImage(0, imageRect), "convolution_painter_test", "dilate", "erode5");
}
QTEST_MAIN(KisConvolutionPainterTest)
diff --git a/libs/image/tests/kis_cs_conversion_test.cpp b/libs/image/tests/kis_cs_conversion_test.cpp
index cf056ecb09..10ff337b73 100644
--- a/libs/image/tests/kis_cs_conversion_test.cpp
+++ b/libs/image/tests/kis_cs_conversion_test.cpp
@@ -1,104 +1,104 @@
/*
* 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 "kis_cs_conversion_test.h"
#include <QTest>
#include <QTime>
#include <KoColor.h>
#include <KoColorSpace.h>
#include "kis_painter.h"
#include "kis_types.h"
#include "kis_paint_device.h"
#include "kis_layer.h"
#include "kis_paint_layer.h"
#include "kis_selection.h"
#include "kis_datamanager.h"
#include "kis_global.h"
#include "testutil.h"
#include "kis_transaction.h"
#include "kis_image.h"
#include "testing_timed_default_bounds.h"
void logFailure(const QString & reason, const KoColorSpace * srcCs, const KoColorSpace * dstCs)
{
QString profile1("no profile");
QString profile2("no profile");
if (srcCs->profile())
profile1 = srcCs->profile()->name();
if (dstCs->profile())
profile2 = dstCs->profile()->name();
QWARN(QString("Failed %1 %2 -> %3 %4 %5")
.arg(srcCs->name())
.arg(profile1)
.arg(dstCs->name())
.arg(profile2)
.arg(reason)
.toLatin1());
}
void KisCsConversionTest::testColorSpaceConversion()
{
- QTime t;
+ QElapsedTimer t;
t.start();
QList<const KoColorSpace*> colorSpaces = KoColorSpaceRegistry::instance()->allColorSpaces(KoColorSpaceRegistry::AllColorSpaces, KoColorSpaceRegistry::OnlyDefaultProfile);
int failedColorSpaces = 0;
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png");
Q_FOREACH (const KoColorSpace * srcCs, colorSpaces) {
Q_FOREACH (const KoColorSpace * dstCs, colorSpaces) {
KisPaintDeviceSP dev = new KisPaintDevice(srcCs);
dev->convertFromQImage(image, 0);
dev->moveTo(10, 10); // Unalign with tile boundaries
dev->setDefaultBounds(new TestUtil::TestingTimedDefaultBounds(dev->exactBounds()));
dev->convertTo(dstCs);
if (dev->exactBounds() != QRect(10, 10, image.width(), image.height())) {
logFailure("bounds", srcCs, dstCs);
failedColorSpaces++;
}
if (dev->pixelSize() != dstCs->pixelSize()) {
logFailure("pixelsize", srcCs, dstCs);
failedColorSpaces++;
}
if (*dev->colorSpace() != *dstCs) {
logFailure("dest cs", srcCs, dstCs);
failedColorSpaces++;
}
}
}
qDebug() << colorSpaces.size() * colorSpaces.size()
<< "conversions"
<< " done in "
<< t.elapsed()
<< "ms";
if (failedColorSpaces > 0) {
QFAIL(QString("Failed conversions %1, see log for details.").arg(failedColorSpaces).toLatin1());
}
}
KISTEST_MAIN(KisCsConversionTest)
diff --git a/libs/image/tests/kis_filter_configuration_test.cpp b/libs/image/tests/kis_filter_configuration_test.cpp
index 71134c7613..4c4439440e 100644
--- a/libs/image/tests/kis_filter_configuration_test.cpp
+++ b/libs/image/tests/kis_filter_configuration_test.cpp
@@ -1,68 +1,70 @@
/*
* 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_filter_configuration_test.h"
#include <QApplication>
#include <QTest>
#include <kis_debug.h>
#include <KoID.h>
#include <kis_paint_device.h>
#include "../filter/kis_filter_configuration.h"
#include "../filter/kis_filter_registry.h"
#include "../filter/kis_filter.h"
+#include <KisGlobalResourcesInterface.h>
+
void KisFilterConfigurationTest::testCreation()
{
- KisFilterConfigurationSP kfc = new KisFilterConfiguration("test", 1);
+ KisFilterConfigurationSP kfc = new KisFilterConfiguration("test", 1, KisGlobalResourcesInterface::instance());
QVERIFY2(kfc != 0, "Could not create test filter configuration");
QCOMPARE(kfc->version(), 1);
QCOMPARE(kfc->name(), QString("test"));
}
void KisFilterConfigurationTest::testRoundTrip()
{
- KisFilterConfigurationSP kfc = new KisFilterConfiguration("test", 1);
+ KisFilterConfigurationSP kfc = new KisFilterConfiguration("test", 1, KisGlobalResourcesInterface::instance());
QCOMPARE(kfc->version(), 1);
QCOMPARE(kfc->name(), QString("test"));
QString s = kfc->toXML();
- kfc = new KisFilterConfiguration("test2", 2);
+ kfc = new KisFilterConfiguration("test2", 2, KisGlobalResourcesInterface::instance());
kfc->fromXML(s);
QCOMPARE(kfc->version(), 1);
}
void KisFilterConfigurationTest::testSetGetProperty()
{
- KisFilterConfigurationSP kfc = new KisFilterConfiguration("test", 1);
+ KisFilterConfigurationSP kfc = new KisFilterConfiguration("test", 1, KisGlobalResourcesInterface::instance());
kfc->setProperty("value1", 10);
kfc->setProperty("value2", "foo");
QCOMPARE(kfc->getInt("value1"), 10);
QCOMPARE(kfc->getString("value2"), QString("foo"));
QString s = kfc->toXML();
- kfc = new KisFilterConfiguration("test2", 2);
+ kfc = new KisFilterConfiguration("test2", 2, KisGlobalResourcesInterface::instance());
kfc->fromXML(s);
QCOMPARE(kfc->getInt("value1"), 10);
QCOMPARE(kfc->getString("value2"), QString("foo"));
}
QTEST_MAIN(KisFilterConfigurationTest)
diff --git a/libs/image/tests/kis_filter_mask_test.cpp b/libs/image/tests/kis_filter_mask_test.cpp
index 6f16a2f4bb..ee9e6d3196 100644
--- a/libs/image/tests/kis_filter_mask_test.cpp
+++ b/libs/image/tests/kis_filter_mask_test.cpp
@@ -1,125 +1,126 @@
/*
* 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 "kis_filter_mask_test.h"
#include <QTest>
#include <KoColorSpaceRegistry.h>
#include "kis_selection.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "kis_filter_mask.h"
#include "filter/kis_filter_registry.h"
#include "kis_group_layer.h"
#include "kis_paint_device.h"
#include "kis_paint_layer.h"
#include "kis_types.h"
#include "kis_image.h"
+#include <KisGlobalResourcesInterface.h>
#include "testutil.h"
#define IMAGE_WIDTH 1000
#define IMAGE_HEIGHT 1000
void KisFilterMaskTest::testCreation()
{
KisFilterMaskSP mask = new KisFilterMask();
}
#define initImage(image, layer, device, mask) do { \
image = new KisImage(0, IMAGE_WIDTH, IMAGE_HEIGHT, 0, "tests"); \
layer = new KisPaintLayer(image, 0, 100, device); \
image->addNode(layer); \
image->addNode(mask, layer); \
} while(0)
void KisFilterMaskTest::testProjectionNotSelected()
{
KisImageSP image;
KisPaintLayerSP layer;
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
QImage inverted(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png");
KisFilterSP f = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(f);
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(kfc);
KisFilterMaskSP mask = new KisFilterMask();
- mask->setFilter(kfc);
+ mask->setFilter(kfc->cloneWithResourcesSnapshot());
// Check basic apply(). Shouldn't do anything, since nothing is selected yet.
KisPaintDeviceSP projection = new KisPaintDevice(cs);
initImage(image, layer, projection, mask);
projection->convertFromQImage(qimage, 0, 0, 0);
mask->initSelection(layer);
mask->createNodeProgressProxy();
mask->select(qimage.rect(), MIN_SELECTED);
mask->apply(projection, qimage.rect(), qimage.rect(), KisNode::N_FILTHY);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, qimage, projection->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) {
projection->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save("filtermasktest1.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisFilterMaskTest::testProjectionSelected()
{
KisImageSP image;
KisPaintLayerSP layer;
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
QImage inverted(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png");
KisFilterSP f = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(f);
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(kfc);
KisFilterMaskSP mask = new KisFilterMask();
- mask->setFilter(kfc);
+ mask->setFilter(kfc->cloneWithResourcesSnapshot());
mask->createNodeProgressProxy();
KisPaintDeviceSP projection = new KisPaintDevice(cs);
initImage(image, layer, projection, mask);
projection->convertFromQImage(qimage, 0, 0, 0);
mask->initSelection(layer);
mask->select(qimage.rect(), MAX_SELECTED);
mask->apply(projection, qimage.rect(), qimage.rect(), KisNode::N_FILTHY);
QCOMPARE(mask->exactBounds(), QRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT));
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, inverted, projection->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) {
projection->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save("filtermasktest2.png");
QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
QTEST_MAIN(KisFilterMaskTest)
diff --git a/libs/image/tests/kis_filter_test.cpp b/libs/image/tests/kis_filter_test.cpp
index 7f081fc4ab..77ba56c591 100644
--- a/libs/image/tests/kis_filter_test.cpp
+++ b/libs/image/tests/kis_filter_test.cpp
@@ -1,240 +1,241 @@
/*
* 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 "kis_filter_test.h"
#include <QTest>
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_selection.h"
#include "kis_processing_information.h"
#include "filter/kis_filter.h"
#include "testutil.h"
#include "kis_pixel_selection.h"
+#include <KisGlobalResourcesInterface.h>
#include <KoProgressUpdater.h>
#include <KoUpdater.h>
class TestFilter : public KisFilter
{
public:
TestFilter()
: KisFilter(KoID("test", "test"), KoID("test", "test"), "TestFilter") {
}
void processImpl(KisPaintDeviceSP src,
const QRect& size,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater) const override {
Q_UNUSED(src);
Q_UNUSED(size);
Q_UNUSED(config);
Q_UNUSED(progressUpdater);
}
};
void KisFilterTest::testCreation()
{
TestFilter test;
}
void KisFilterTest::testWithProgressUpdater()
{
TestUtil::TestProgressBar * bar = new TestUtil::TestProgressBar();
KoProgressUpdater* pu = new KoProgressUpdater(bar);
KoUpdaterPtr updater = pu->startSubtask();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
QImage inverted(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(qimage, 0, 0, 0);
KisFilterSP f = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(f);
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(kfc);
f->process(dev, QRect(QPoint(0,0), qimage.size()), kfc, updater);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, inverted, dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) {
dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save("filtertest.png");
QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
delete pu;
delete bar;
}
void KisFilterTest::testSingleThreaded()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
QImage inverted(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(qimage, 0, 0, 0);
KisFilterSP f = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(f);
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(kfc);
f->process(dev, QRect(QPoint(0,0), qimage.size()), kfc);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, inverted, dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) {
dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save("filtertest.png");
QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisFilterTest::testDifferentSrcAndDst()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
QImage inverted(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png");
KisPaintDeviceSP src = new KisPaintDevice(cs);
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KisSelectionSP sel = new KisSelection(new KisSelectionDefaultBounds(src));
sel->pixelSelection()->invert(); // select everything
sel->updateProjection();
src->convertFromQImage(qimage, 0, 0, 0);
KisFilterSP f = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(f);
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(kfc);
f->process(src, dst, sel, QRect(QPoint(0,0), qimage.size()), kfc);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, inverted, dst->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) {
dst->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save("filtertest.png");
QFAIL(QString("Failed to create inverted image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisFilterTest::testOldDataApiAfterCopy()
{
QRect updateRect(0,0,63,63);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
quint8 *whitePixel = new quint8[cs->pixelSize()];
cs->fromQColor(Qt::white, whitePixel);
cs->setOpacity(whitePixel, OPACITY_OPAQUE_U8, 1);
KisPaintDeviceSP tmp = new KisPaintDevice(cs);
KisPaintDeviceSP src = new KisPaintDevice(cs);
src->fill(0, 0, 50, 50, whitePixel);
/**
* Make a full copy here to catch the bug.
* Buggy memento manager would make a commit
* that is not good.
*/
KisPaintDeviceSP dst = new KisPaintDevice(*src);
/**
* This would write go to a new revision in a buggy
* memento manager
*/
dst->clear(updateRect);
KisFilterSP f = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(f);
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(kfc);
/**
* This filter reads from oldRawData, so if we have some
* weirdness with transactions it will read from old and non-cleared
* version of the device and we will see a black square instead
* of empty device in tmp
*/
f->process(dst, tmp, 0, updateRect, kfc);
/**
* In theory, both devices: dst and tmp must be empty by now
*/
KisPaintDeviceSP reference = new KisPaintDevice(cs);
QImage refImage = reference->convertToQImage(0,0,0,63,63);
QImage dstImage = dst->convertToQImage(0,0,0,63,63);
QImage tmpImage = tmp->convertToQImage(0,0,0,63,63);
QPoint pt;
QVERIFY(TestUtil::compareQImages(pt, refImage, dstImage));
QVERIFY(TestUtil::compareQImages(pt, refImage, tmpImage));
}
void KisFilterTest::testBlurFilterApplicationRect()
{
QRect filterRect(10,10,40,40);
QRect src1Rect(5,5,50,50);
QRect src2Rect(0,0,60,60);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
quint8 *whitePixel = new quint8[cs->pixelSize()];
cs->fromQColor(Qt::white, whitePixel);
cs->setOpacity(whitePixel, OPACITY_OPAQUE_U8, 1);
KisPaintDeviceSP src1 = new KisPaintDevice(cs);
src1->fill(src1Rect.left(),src1Rect.top(),src1Rect.width(),src1Rect.height(), whitePixel);
KisPaintDeviceSP src2 = new KisPaintDevice(cs);
src2->fill(src2Rect.left(),src2Rect.top(),src2Rect.width(),src2Rect.height(), whitePixel);
KisPaintDeviceSP dst1 = new KisPaintDevice(cs);
KisPaintDeviceSP dst2 = new KisPaintDevice(cs);
KisFilterSP f = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(f);
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(kfc);
f->process(src1, dst1, 0, filterRect, kfc);
f->process(src2, dst2, 0, filterRect, kfc);
KisPaintDeviceSP reference = new KisPaintDevice(cs);
reference->fill(filterRect.left(),filterRect.top(),filterRect.width(),filterRect.height(), whitePixel);
QImage refImage = reference->convertToQImage(0,10,10,40,40);
QImage dst1Image = dst1->convertToQImage(0,10,10,40,40);
QImage dst2Image = dst2->convertToQImage(0,10,10,40,40);
//dst1Image.save("DST1.png");
//dst2Image.save("DST2.png");
QPoint pt;
QVERIFY(TestUtil::compareQImages(pt, refImage, dst1Image));
QVERIFY(TestUtil::compareQImages(pt, refImage, dst2Image));
}
QTEST_MAIN(KisFilterTest)
diff --git a/libs/image/tests/kis_fixed_paint_device_test.cpp b/libs/image/tests/kis_fixed_paint_device_test.cpp
index caad6a0f73..53ca57bb0b 100644
--- a/libs/image/tests/kis_fixed_paint_device_test.cpp
+++ b/libs/image/tests/kis_fixed_paint_device_test.cpp
@@ -1,370 +1,370 @@
/*
* 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 "kis_fixed_paint_device_test.h"
#include <QTest>
#include <QTime>
#include <KoColorSpace.h>
#include <KoColor.h>
#include <KoColorSpaceRegistry.h>
#include "kis_painter.h"
#include "kis_types.h"
#include "kis_paint_device.h"
#include "kis_fixed_paint_device.h"
#include "kis_layer.h"
#include "kis_paint_layer.h"
#include "kis_selection.h"
#include "kis_datamanager.h"
#include "kis_global.h"
#include "testutil.h"
#include "kis_transaction.h"
#include "kis_image.h"
void KisFixedPaintDeviceTest::testCreation()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev = new KisFixedPaintDevice(cs);
QVERIFY(dev->bounds() == QRect());
QVERIFY(*dev->colorSpace() == *cs);
QVERIFY(dev->pixelSize() == cs->pixelSize());
dev->setRect(QRect(0, 0, 100, 100));
QVERIFY(dev->bounds() == QRect(0, 0, 100, 100));
dev->initialize();
QVERIFY(dev->data() != 0);
quint8* data = dev->data();
for (uint i = 0; i < 100 * 100 * cs->pixelSize(); ++i) {
QVERIFY(data[i] == 0);
}
}
void logFailure(const QString & reason, const KoColorSpace * srcCs, const KoColorSpace * dstCs)
{
QString profile1("no profile");
QString profile2("no profile");
if (srcCs->profile())
profile1 = srcCs->profile()->name();
if (dstCs->profile())
profile2 = dstCs->profile()->name();
QWARN(QString("Failed %1 %2 -> %3 %4 %5")
.arg(srcCs->name())
.arg(profile1)
.arg(dstCs->name())
.arg(profile2)
.arg(reason)
.toLatin1());
}
void KisFixedPaintDeviceTest::testColorSpaceConversion()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png");
const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->rgb8();
const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->lab16();
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(srcCs);
dev->convertFromQImage(image, 0);
dev->convertTo(dstCs);
QVERIFY(dev->bounds() == QRect(0, 0, image.width(), image.height()));
QVERIFY(dev->pixelSize() == dstCs->pixelSize());
QVERIFY(*dev->colorSpace() == *dstCs);
}
void KisFixedPaintDeviceTest::testRoundtripQImageConversion()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev->convertFromQImage(image, 0);
QImage result = dev->convertToQImage(0, 0, 0, 640, 441);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("kis_fixed_paint_device_test_test_roundtrip_qimage.png");
result.save("kis_fixed_paint_device_test_test_roundtrip_result.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisFixedPaintDeviceTest::testBltFixed()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs);
fdev->convertFromQImage(image, 0);
// Without opacity
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KisPainter gc(dev);
gc.bltFixed(QPoint(0, 0), fdev, image.rect());
QImage result = dev->convertToQImage(0, 0, 0, 640, 441);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, image, result, 1)) {
fdev->convertToQImage(0).save("kis_fixed_paint_device_test_test_blt_fixed_expected.png");
result.save("kis_fixed_paint_device_test_test_blt_fixed_result.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisFixedPaintDeviceTest::testBltFixedOpacity()
{
// blt a semi-transparent image on a white paint device
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs);
fdev->convertFromQImage(image, 0);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data());
KisPainter gc(dev);
gc.bltFixed(QPoint(0, 0), fdev, image.rect());
QImage result = dev->convertToQImage(0, 0, 0, 640, 441);
QImage checkResult(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent_result.png");
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, checkResult, result, 1)) {
checkResult.save("kis_fixed_paint_device_test_test_blt_fixed_opactiy_expected.png");
result.save("kis_fixed_paint_device_test_test_blt_fixed_opacity_result.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisFixedPaintDeviceTest::testSilly()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev->initialize();
dev->initialize();
}
void KisFixedPaintDeviceTest::testClear()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev->clear(QRect(0, 0, 100, 100));
QVERIFY(dev->bounds() == QRect(0, 0, 100, 100));
QVERIFY(cs->opacityU8(dev->data() + (50 * 50 * cs->pixelSize())) == OPACITY_TRANSPARENT_U8);
}
void KisFixedPaintDeviceTest::testFill()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
quint8* red = new quint8[cs->pixelSize()];
memcpy(red, KoColor(Qt::red, cs).data(), cs->pixelSize());
cs->setOpacity(red, quint8(128), 1);
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev->fill(0, 0, 100, 100, red);
QVERIFY(dev->bounds() == QRect(0, 0, 100, 100));
QVERIFY(cs->opacityU8(dev->data()) == 128);
QVERIFY(memcmp(dev->data(), red, cs->pixelSize()) == 0);
//Compare fill will normal paint device
dev = new KisFixedPaintDevice(cs);
dev->setRect(QRect(0, 0, 150, 150));
dev->initialize();
dev->fill(50, 50, 50, 50, red);
KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
dev2->fill(50, 50, 50, 50, red);
QImage image = dev->convertToQImage(0);
QImage checkImage = dev2->convertToQImage(0, 0, 0, 150, 150);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, image, checkImage)) {
image.save("kis_fixed_paint_device_filled_result.png");
checkImage.save("kis_fixed_paint_device_filled_result_expected.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
delete[] red;
}
void KisFixedPaintDeviceTest::testBltFixedSmall()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "fixed_blit_small.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs);
fdev->convertFromQImage(image, 0);
// Without opacity
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KisPainter gc(dev);
gc.bltFixed(QPoint(0, 0), fdev, image.rect());
QImage result = dev->convertToQImage(0, 0, 0, 51, 51);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("kis_fixed_paint_device_test_blt_small_image.png");
result.save("kis_fixed_paint_device_test_blt_small_result.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisFixedPaintDeviceTest::testBltPerformance()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs);
fdev->convertFromQImage(image, 0);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data());
- QTime t;
+ QElapsedTimer t;
t.start();
int x;
for (x = 0; x < 100; ++x) {
KisPainter gc(dev);
gc.bltFixed(QPoint(0, 0), fdev, image.rect());
}
dbgKrita << x
<< "blits"
<< " done in "
<< t.elapsed()
<< "ms";
}
inline void setPixel(KisFixedPaintDeviceSP dev, int x, int y, quint8 alpha)
{
KoColor c(Qt::black, dev->colorSpace());
c.setOpacity(alpha);
dev->fill(x, y, 1, 1, c.data());
}
inline quint8 pixel(KisFixedPaintDeviceSP dev, int x, int y)
{
KoColor c(Qt::black, dev->colorSpace());
dev->readBytes(c.data(), x, y, 1, 1);
return c.opacityU8();
}
void KisFixedPaintDeviceTest::testMirroring_data()
{
QTest::addColumn<QRect>("rc");
QTest::addColumn<bool>("mirrorHorizontally");
QTest::addColumn<bool>("mirrorVertically");
QTest::newRow("4, false, false") << (QRect(99,99,4,4)) << false << false;
QTest::newRow("4, false, true") << (QRect(99,99,4,4)) << false << true;
QTest::newRow("4, true, false") << (QRect(99,99,4,4)) << true << false;
QTest::newRow("4, true, true") << (QRect(99,99,4,4)) << true << true;
QTest::newRow("5, false, false") << (QRect(99,99,5,5)) << false << false;
QTest::newRow("5, false, true") << (QRect(99,99,5,5)) << false << true;
QTest::newRow("5, true, false") << (QRect(99,99,5,5)) << true << false;
QTest::newRow("5, true, true") << (QRect(99,99,5,5)) << true << true;
}
void KisFixedPaintDeviceTest::testMirroring()
{
QFETCH(QRect, rc);
QFETCH(bool, mirrorHorizontally);
QFETCH(bool, mirrorVertically);
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev->setRect(rc);
dev->initialize();
KoColor c(Qt::black, cs);
qsrand(1);
int value = 0;
for (int i = rc.x(); i < rc.x() + rc.width(); i++) {
for (int j = rc.y(); j < rc.y() + rc.height(); j++) {
setPixel(dev, i, j, value);
value = qrand() % 255;
}
value = qrand() % 255;
}
//dev->convertToQImage(0).save("0_a.png");
dev->mirror(mirrorHorizontally, mirrorVertically);
//dev->convertToQImage(0).save("0_b.png");
int startX;
int endX;
int incX;
int startY;
int endY;
int incY;
if (mirrorHorizontally) {
startX = rc.x() + rc.width() - 1;
endX = rc.x() - 1;
incX = -1;
} else {
startX = rc.x();
endX = rc.x() + rc.width();
incX = 1;
}
if (mirrorVertically) {
startY = rc.y() + rc.height() - 1;
endY = rc.y() - 1;
incY = -1;
} else {
startY = rc.y();
endY = rc.y() + rc.height();
incY = 1;
}
qsrand(1);
value = 0;
for (int i = startX; i != endX ; i += incX) {
for (int j = startY; j != endY; j += incY) {
QCOMPARE(pixel(dev, i, j), (quint8)value);
value = qrand() % 255;
}
value = qrand() % 255;
}
}
QTEST_MAIN(KisFixedPaintDeviceTest)
diff --git a/libs/image/tests/kis_gradient_painter_test.cpp b/libs/image/tests/kis_gradient_painter_test.cpp
index cd2a2fa0ae..7bf6b9a1a8 100644
--- a/libs/image/tests/kis_gradient_painter_test.cpp
+++ b/libs/image/tests/kis_gradient_painter_test.cpp
@@ -1,334 +1,333 @@
/*
* 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 "kis_gradient_painter_test.h"
#include <QTest>
#include "kis_gradient_painter.h"
#include "kis_paint_device.h"
#include "kis_selection.h"
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOpRegistry.h>
#include <resources/KoStopGradient.h>
#include "krita_utils.h"
#include "testutil.h"
void KisGradientPainterTest::testSimplifyPath()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 100);
selectionPolygon << QPointF(202, 100);
selectionPolygon << QPointF(200, 200);
selectionPolygon << QPointF(100, 200);
selectionPolygon << QPointF(100, 102);
QPainterPath path;
path.addPolygon(selectionPolygon);
QPainterPath simplifiedPath;
simplifiedPath = KritaUtils::trySimplifyPath(path, 10.0);
QPainterPath ref;
ref.moveTo(100,100);
ref.lineTo(200,100);
ref.lineTo(200,200);
ref.lineTo(100,200);
QCOMPARE(simplifiedPath, ref);
}
void testShapedGradientPainterImpl(const QPolygonF &selectionPolygon,
const QString &testName,
const QPolygonF &selectionErasePolygon = QPolygonF())
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QRect imageRect(0,0,300,300);
KisSelectionSP selection = new KisSelection();
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
KisPainter selPainter(pixelSelection);
selPainter.setFillStyle(KisPainter::FillStyleForegroundColor);
selPainter.setPaintColor(KoColor(Qt::white, pixelSelection->colorSpace()));
selPainter.paintPolygon(selectionPolygon);
if (!selectionErasePolygon.isEmpty()) {
selPainter.setCompositeOp(COMPOSITE_ERASE);
selPainter.setPaintColor(KoColor(Qt::white, pixelSelection->colorSpace()));
selPainter.paintPolygon(selectionErasePolygon);
}
selPainter.end();
pixelSelection->invalidateOutlineCache();
pixelSelection->convertToQImage(0, imageRect).save("sgt_selection.png");
QLinearGradient testGradient;
testGradient.setColorAt(0.0, Qt::white);
testGradient.setColorAt(0.5, Qt::green);
testGradient.setColorAt(1.0, Qt::black);
testGradient.setSpread(QGradient::ReflectSpread);
- QScopedPointer<KoStopGradient> gradient(
- KoStopGradient::fromQGradient(&testGradient));
+ QSharedPointer<KoStopGradient> gradient(KoStopGradient::fromQGradient(&testGradient));
KisGradientPainter gc(dev, selection);
- gc.setGradient(gradient.data());
+ gc.setGradient(gradient);
gc.setGradientShape(KisGradientPainter::GradientShapePolygonal);
gc.paintGradient(selectionPolygon.boundingRect().topLeft(),
selectionPolygon.boundingRect().bottomRight(),
KisGradientPainter::GradientRepeatNone,
0,
false,
imageRect.x(),
imageRect.y(),
imageRect.width(),
imageRect.height());
QVERIFY(TestUtil::checkQImageExternal(dev->convertToQImage(0, imageRect),
"shaped_gradient",
"fill",
testName, 1, 1, 0));
}
void KisGradientPainterTest::testShapedGradientPainterRect()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 100);
selectionPolygon << QPointF(202, 100);
selectionPolygon << QPointF(200, 200);
selectionPolygon << QPointF(100, 200);
testShapedGradientPainterImpl(selectionPolygon, "rect_shape");
}
void KisGradientPainterTest::testShapedGradientPainterRectPierced()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 100);
selectionPolygon << QPointF(200, 200);
selectionPolygon << QPointF(100, 200);
QPolygonF selectionErasePolygon;
selectionErasePolygon << QPointF(150, 150);
selectionErasePolygon << QPointF(155, 150);
selectionErasePolygon << QPointF(155, 155);
selectionErasePolygon << QPointF(150, 155);
testShapedGradientPainterImpl(selectionPolygon, "rect_shape_pierced", selectionErasePolygon);
}
void KisGradientPainterTest::testShapedGradientPainterNonRegular()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 120);
selectionPolygon << QPointF(170, 140);
selectionPolygon << QPointF(200, 180);
selectionPolygon << QPointF(30, 220);
testShapedGradientPainterImpl(selectionPolygon, "nonregular_shape");
}
void KisGradientPainterTest::testShapedGradientPainterNonRegularPierced()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 120);
selectionPolygon << QPointF(170, 140);
selectionPolygon << QPointF(200, 180);
selectionPolygon << QPointF(30, 220);
QPolygonF selectionErasePolygon;
selectionErasePolygon << QPointF(150, 150);
selectionErasePolygon << QPointF(155, 150);
selectionErasePolygon << QPointF(155, 155);
selectionErasePolygon << QPointF(150, 155);
testShapedGradientPainterImpl(selectionPolygon, "nonregular_shape_pierced", selectionErasePolygon);
}
#include "kis_polygonal_gradient_shape_strategy.h"
void KisGradientPainterTest::testFindShapedExtremums()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 120);
selectionPolygon << QPointF(170, 140);
selectionPolygon << QPointF(200, 180);
selectionPolygon << QPointF(30, 220);
QPolygonF selectionErasePolygon;
selectionErasePolygon << QPointF(101, 101);
selectionErasePolygon << QPointF(190, 120);
selectionErasePolygon << QPointF(160, 140);
selectionErasePolygon << QPointF(200, 180);
selectionErasePolygon << QPointF(30, 220);
QPainterPath path;
path.addPolygon(selectionPolygon);
path.closeSubpath();
path.addPolygon(selectionErasePolygon);
path.closeSubpath();
QPointF center =
KisPolygonalGradientShapeStrategy::testingCalculatePathCenter(
4, path, 2.0, true);
dbgKrita << ppVar(center);
QVERIFY(path.contains(center));
}
void KisGradientPainterTest::testSplitDisjointPaths()
{
QPainterPath path;
// small bug: the smaller rect is also merged
path.addRect(QRectF(323, 123, 4, 4));
path.addRect(QRectF(300, 100, 50, 50));
path.addRect(QRectF(320, 120, 10, 10));
path.addRect(QRectF(200, 100, 50, 50));
path.addRect(QRectF(240, 120, 70, 10));
path.addRect(QRectF(100, 100, 50, 50));
path.addRect(QRectF(120, 120, 10, 10));
path = path.simplified();
{
QImage srcImage(450, 250, QImage::Format_ARGB32);
srcImage.fill(0);
QPainter gc(&srcImage);
gc.fillPath(path, Qt::red);
//srcImage.save("src_disjoint_paths.png");
}
QList<QPainterPath> result = KritaUtils::splitDisjointPaths(path);
{
QImage dstImage(450, 250, QImage::Format_ARGB32);
dstImage.fill(0);
QPainter gc(&dstImage);
QVector<QBrush> brushes;
brushes << Qt::red;
brushes << Qt::green;
brushes << Qt::blue;
brushes << Qt::cyan;
brushes << Qt::magenta;
brushes << Qt::yellow;
brushes << Qt::black;
brushes << Qt::white;
int index = 0;
Q_FOREACH (const QPainterPath &p, result) {
gc.fillPath(p, brushes[index]);
index = (index + 1) % brushes.size();
}
TestUtil::checkQImageExternal(dstImage,
"shaped_gradient",
"test",
"disjoint_paths");
}
}
#include "kis_cached_gradient_shape_strategy.h"
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>
using namespace boost::accumulators;
void KisGradientPainterTest::testCachedStrategy()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 120);
selectionPolygon << QPointF(170, 140);
selectionPolygon << QPointF(200, 180);
selectionPolygon << QPointF(30, 220);
QPainterPath selectionPath;
selectionPath.addPolygon(selectionPolygon);
QRect rc = selectionPolygon.boundingRect().toAlignedRect();
KisGradientShapeStrategy *strategy =
new KisPolygonalGradientShapeStrategy(selectionPath, 2.0);
KisCachedGradientShapeStrategy cached(rc, 4, 4, strategy);
accumulator_set<qreal, stats<tag::variance, tag::max, tag::min> > accum;
const qreal maxRelError = 5.0 / 256;
for (int y = rc.y(); y <= rc.bottom(); y++) {
for (int x = rc.x(); x <= rc.right(); x++) {
if (!selectionPolygon.containsPoint(QPointF(x, y), Qt::OddEvenFill)) continue;
qreal ref = strategy->valueAt(x, y);
qreal value = cached.valueAt(x, y);
if (ref == 0.0) continue;
qreal relError = (ref - value)/* / ref*/;
accum(relError);
if (relError > maxRelError) {
//dbgKrita << ppVar(x) << ppVar(y) << ppVar(value) << ppVar(ref) << ppVar(relError);
}
}
}
dbgKrita << ppVar(count(accum));
dbgKrita << ppVar(mean(accum));
dbgKrita << ppVar(variance(accum));
dbgKrita << ppVar((min)(accum));
dbgKrita << ppVar((max)(accum));
qreal varError = variance(accum);
QVERIFY(varError < maxRelError);
qreal maxError = qMax(qAbs((min)(accum)), qAbs((max)(accum)));
QVERIFY(maxError < 2 * maxRelError);
}
QTEST_MAIN(KisGradientPainterTest)
diff --git a/libs/image/tests/kis_image_test.cpp b/libs/image/tests/kis_image_test.cpp
index e6e70feb8b..57d8ba1e24 100644
--- a/libs/image/tests/kis_image_test.cpp
+++ b/libs/image/tests/kis_image_test.cpp
@@ -1,1293 +1,1294 @@
/*
* Copyright (c) 2005 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 "kis_image_test.h"
#include <QApplication>
#include <QTest>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpace.h>
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_image.h"
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_selection.h"
#include <kis_debug.h>
#include <kis_layer_composition.h>
#include "kis_keyframe_channel.h"
#include "kis_selection_mask.h"
#include "kis_layer_utils.h"
#include "kis_annotation.h"
#include "KisProofingConfiguration.h"
+#include <KisGlobalResourcesInterface.h>
#include "kis_undo_stores.h"
#define IMAGE_WIDTH 128
#define IMAGE_HEIGHT 128
void KisImageTest::layerTests()
{
KisImageSP image = new KisImage(0, IMAGE_WIDTH, IMAGE_WIDTH, 0, "layer tests");
QVERIFY(image->rootLayer() != 0);
QVERIFY(image->rootLayer()->firstChild() == 0);
KisLayerSP layer = new KisPaintLayer(image, "layer 1", OPACITY_OPAQUE_U8);
image->addNode(layer);
QVERIFY(image->rootLayer()->firstChild()->objectName() == layer->objectName());
}
void KisImageTest::benchmarkCreation()
{
const QRect imageRect(0,0,3000,2000);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QList<KisImageSP> images;
QList<KisSurrogateUndoStore*> stores;
QBENCHMARK {
for (int i = 0; i < 10; i++) {
stores << new KisSurrogateUndoStore();
}
for (int i = 0; i < 10; i++) {
KisImageSP image = new KisImage(stores.takeLast(), imageRect.width(), imageRect.height(), cs, "test image");
images << image;
}
}
}
#include "testutil.h"
#include "kis_stroke_strategy.h"
#include <functional>
class ForbiddenLodStrokeStrategy : public KisStrokeStrategy
{
public:
ForbiddenLodStrokeStrategy(std::function<void()> lodCallback)
: KisStrokeStrategy(QLatin1String("ForbiddenLodStrokeStrategy")),
m_lodCallback(lodCallback)
{
}
KisStrokeStrategy* createLodClone(int levelOfDetail) override {
Q_UNUSED(levelOfDetail);
m_lodCallback();
return 0;
}
private:
std::function<void()> m_lodCallback;
};
void notifyVar(bool *value) {
*value = true;
}
void KisImageTest::testBlockLevelOfDetail()
{
TestUtil::MaskParent p;
QCOMPARE(p.image->currentLevelOfDetail(), 0);
p.image->setDesiredLevelOfDetail(1);
p.image->waitForDone();
QCOMPARE(p.image->currentLevelOfDetail(), 0);
{
bool lodCreated = false;
KisStrokeId id = p.image->startStroke(
new ForbiddenLodStrokeStrategy(
std::bind(&notifyVar, &lodCreated)));
p.image->endStroke(id);
p.image->waitForDone();
QVERIFY(lodCreated);
}
p.image->setLevelOfDetailBlocked(true);
{
bool lodCreated = false;
KisStrokeId id = p.image->startStroke(
new ForbiddenLodStrokeStrategy(
std::bind(&notifyVar, &lodCreated)));
p.image->endStroke(id);
p.image->waitForDone();
QVERIFY(!lodCreated);
}
p.image->setLevelOfDetailBlocked(false);
p.image->setDesiredLevelOfDetail(1);
{
bool lodCreated = false;
KisStrokeId id = p.image->startStroke(
new ForbiddenLodStrokeStrategy(
std::bind(&notifyVar, &lodCreated)));
p.image->endStroke(id);
p.image->waitForDone();
QVERIFY(lodCreated);
}
}
void KisImageTest::testConvertImageColorSpace()
{
const KoColorSpace *cs8 = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 1000, 1000, cs8, "stest");
KisPaintDeviceSP device1 = new KisPaintDevice(cs8);
KisLayerSP paint1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
- KisFilterConfigurationSP configuration = filter->defaultConfiguration();
+ KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(configuration);
- KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration, 0);
+ KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration->cloneWithResourcesSnapshot(), 0);
image->addNode(paint1, image->root());
image->addNode(blur1, image->root());
image->refreshGraph();
const KoColorSpace *cs16 = KoColorSpaceRegistry::instance()->rgb16();
image->convertImageColorSpace(cs16,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
image->waitForDone();
QVERIFY(*cs16 == *image->colorSpace());
QVERIFY(*cs16 == *image->root()->colorSpace());
QVERIFY(*cs16 == *paint1->colorSpace());
QVERIFY(*cs16 == *blur1->colorSpace());
QVERIFY(!image->root()->compositeOp());
QVERIFY(*cs16 == *paint1->compositeOp()->colorSpace());
QVERIFY(*cs16 == *blur1->compositeOp()->colorSpace());
image->refreshGraph();
}
void KisImageTest::testAssignImageProfile()
{
const KoColorSpace *rgb8 = KoColorSpaceRegistry::instance()->rgb8();
const KoColorSpace *gray8 = KoColorSpaceRegistry::instance()->graya8();
KisImageSP image = new KisImage(0, 1000, 1000, rgb8, "stest");
KisPaintDeviceSP device1 = new KisPaintDevice(rgb8);
KisLayerSP paint1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
KisPaintDeviceSP device2 = new KisPaintDevice(gray8);
KisLayerSP paint2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8, device2);
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
- KisFilterConfigurationSP configuration = filter->defaultConfiguration();
+ KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(configuration);
- KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration, 0);
+ KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration->cloneWithResourcesSnapshot(), 0);
image->addNode(paint1, image->root());
image->addNode(paint2, image->root());
image->addNode(blur1, image->root());
QCOMPARE(*image->colorSpace(), *rgb8);
QCOMPARE(*image->colorSpace()->profile(), *KoColorSpaceRegistry::instance()->p709SRGBProfile());
QCOMPARE(*paint1->colorSpace(), *rgb8);
QCOMPARE(*paint1->colorSpace()->profile(), *KoColorSpaceRegistry::instance()->p709SRGBProfile());
QCOMPARE(*paint2->colorSpace(), *gray8);
QCOMPARE(*blur1->colorSpace(), *rgb8);
QCOMPARE(*blur1->colorSpace()->profile(), *KoColorSpaceRegistry::instance()->p709SRGBProfile());
image->assignImageProfile(KoColorSpaceRegistry::instance()->p2020G10Profile());
image->waitForDone();
QVERIFY(*image->colorSpace() != *rgb8);
QCOMPARE(*image->colorSpace()->profile(), *KoColorSpaceRegistry::instance()->p2020G10Profile());
QVERIFY(*paint1->colorSpace() != *rgb8);
QCOMPARE(*paint1->colorSpace()->profile(), *KoColorSpaceRegistry::instance()->p2020G10Profile());
QCOMPARE(*paint2->colorSpace(), *gray8);
QVERIFY(*blur1->colorSpace() != *rgb8);
QCOMPARE(*blur1->colorSpace()->profile(), *KoColorSpaceRegistry::instance()->p2020G10Profile());
}
void KisImageTest::testGlobalSelection()
{
const KoColorSpace *cs8 = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 1000, 1000, cs8, "stest");
QCOMPARE(image->globalSelection(), KisSelectionSP(0));
QCOMPARE(image->canReselectGlobalSelection(), false);
QCOMPARE(image->root()->childCount(), 0U);
KisSelectionSP selection1 = new KisSelection(new KisDefaultBounds(image));
KisSelectionSP selection2 = new KisSelection(new KisDefaultBounds(image));
image->setGlobalSelection(selection1);
QCOMPARE(image->globalSelection(), selection1);
QCOMPARE(image->canReselectGlobalSelection(), false);
QCOMPARE(image->root()->childCount(), 1U);
image->setGlobalSelection(selection2);
QCOMPARE(image->globalSelection(), selection2);
QCOMPARE(image->canReselectGlobalSelection(), false);
QCOMPARE(image->root()->childCount(), 1U);
image->deselectGlobalSelection();
QCOMPARE(image->globalSelection(), KisSelectionSP(0));
QCOMPARE(image->canReselectGlobalSelection(), true);
QCOMPARE(image->root()->childCount(), 0U);
image->reselectGlobalSelection();
QCOMPARE(image->globalSelection(), selection2);
QCOMPARE(image->canReselectGlobalSelection(), false);
QCOMPARE(image->root()->childCount(), 1U);
// mixed deselecting/setting/reselecting
image->deselectGlobalSelection();
QCOMPARE(image->globalSelection(), KisSelectionSP(0));
QCOMPARE(image->canReselectGlobalSelection(), true);
QCOMPARE(image->root()->childCount(), 0U);
image->setGlobalSelection(selection1);
QCOMPARE(image->globalSelection(), selection1);
QCOMPARE(image->canReselectGlobalSelection(), false);
QCOMPARE(image->root()->childCount(), 1U);
}
void KisImageTest::testCloneImage()
{
KisImageSP image = new KisImage(0, IMAGE_WIDTH, IMAGE_WIDTH, 0, "layer tests");
QVERIFY(image->rootLayer() != 0);
QVERIFY(image->rootLayer()->firstChild() == 0);
KisAnnotationSP annotation = new KisAnnotation("mytype", "mydescription", QByteArray());
image->addAnnotation(annotation);
QVERIFY(image->annotation("mytype"));
KisProofingConfigurationSP proofing = toQShared(new KisProofingConfiguration());
image->setProofingConfiguration(proofing);
QVERIFY(image->proofingConfiguration());
const KoColor defaultColor(Qt::green, image->colorSpace());
image->setDefaultProjectionColor(defaultColor);
QCOMPARE(image->defaultProjectionColor(), defaultColor);
KisLayerSP layer = new KisPaintLayer(image, "layer1", OPACITY_OPAQUE_U8);
image->addNode(layer);
KisLayerSP layer2 = new KisPaintLayer(image, "layer2", OPACITY_OPAQUE_U8);
image->addNode(layer2);
QVERIFY(layer->visible());
QVERIFY(layer2->visible());
QVERIFY(TestUtil::findNode(image->root(), "layer1"));
QVERIFY(TestUtil::findNode(image->root(), "layer2"));
QUuid uuid1 = layer->uuid();
QUuid uuid2 = layer2->uuid();
{
KisImageSP newImage = image->clone();
KisNodeSP newLayer1 = TestUtil::findNode(newImage->root(), "layer1");
KisNodeSP newLayer2 = TestUtil::findNode(newImage->root(), "layer2");
QVERIFY(newLayer1);
QVERIFY(newLayer2);
QVERIFY(newLayer1->uuid() != uuid1);
QVERIFY(newLayer2->uuid() != uuid2);
KisAnnotationSP newAnnotation = newImage->annotation("mytype");
QVERIFY(newAnnotation);
QVERIFY(newAnnotation != annotation);
KisProofingConfigurationSP newProofing = newImage->proofingConfiguration();
QVERIFY(newProofing);
QVERIFY(newProofing != proofing);
QCOMPARE(newImage->defaultProjectionColor(), defaultColor);
}
{
KisImageSP newImage = image->clone(true);
KisNodeSP newLayer1 = TestUtil::findNode(newImage->root(), "layer1");
KisNodeSP newLayer2 = TestUtil::findNode(newImage->root(), "layer2");
QVERIFY(newLayer1);
QVERIFY(newLayer2);
QVERIFY(newLayer1->uuid() == uuid1);
QVERIFY(newLayer2->uuid() == uuid2);
}
}
void KisImageTest::testLayerComposition()
{
KisImageSP image = new KisImage(0, IMAGE_WIDTH, IMAGE_WIDTH, 0, "layer tests");
QVERIFY(image->rootLayer() != 0);
QVERIFY(image->rootLayer()->firstChild() == 0);
KisLayerSP layer = new KisPaintLayer(image, "layer1", OPACITY_OPAQUE_U8);
image->addNode(layer);
KisLayerSP layer2 = new KisPaintLayer(image, "layer2", OPACITY_OPAQUE_U8);
image->addNode(layer2);
QVERIFY(layer->visible());
QVERIFY(layer2->visible());
KisLayerComposition comp(image, "comp 1");
comp.store();
layer2->setVisible(false);
QVERIFY(layer->visible());
QVERIFY(!layer2->visible());
KisLayerComposition comp2(image, "comp 2");
comp2.store();
KisLayerCompositionSP comp3 = toQShared(new KisLayerComposition(image, "comp 3"));
comp3->store();
image->addComposition(comp3);
comp.apply();
QVERIFY(layer->visible());
QVERIFY(layer2->visible());
comp2.apply();
QVERIFY(layer->visible());
QVERIFY(!layer2->visible());
comp.apply();
QVERIFY(layer->visible());
QVERIFY(layer2->visible());
KisImageSP newImage = image->clone();
KisNodeSP newLayer1 = TestUtil::findNode(newImage->root(), "layer1");
KisNodeSP newLayer2 = TestUtil::findNode(newImage->root(), "layer2");
QVERIFY(newLayer1);
QVERIFY(newLayer2);
QVERIFY(newLayer1->visible());
QVERIFY(newLayer2->visible());
KisLayerComposition newComp1(comp, newImage);
newComp1.apply();
QVERIFY(newLayer1->visible());
QVERIFY(newLayer2->visible());
KisLayerComposition newComp2(comp2, newImage);
newComp2.apply();
QVERIFY(newLayer1->visible());
QVERIFY(!newLayer2->visible());
newComp1.apply();
QVERIFY(newLayer1->visible());
QVERIFY(newLayer2->visible());
QVERIFY(!newImage->compositions().isEmpty());
KisLayerCompositionSP newComp3 = newImage->compositions().first();
newComp3->apply();
QVERIFY(newLayer1->visible());
QVERIFY(!newLayer2->visible());
}
#include "kis_transparency_mask.h"
#include "kis_psd_layer_style.h"
struct FlattenTestImage
{
FlattenTestImage()
: refRect(0,0,512,512)
, p(refRect)
{
image = p.image;
undoStore = p.undoStore;
layer1 = p.layer;
layer5 = new KisPaintLayer(p.image, "paint5", 0.4 * OPACITY_OPAQUE_U8);
layer5->disableAlphaChannel(true);
layer2 = new KisPaintLayer(p.image, "paint2", OPACITY_OPAQUE_U8);
tmask = new KisTransparencyMask();
// check channel flags
// make addition composite op
group1 = new KisGroupLayer(p.image, "group1", OPACITY_OPAQUE_U8);
layer3 = new KisPaintLayer(p.image, "paint3", OPACITY_OPAQUE_U8);
layer4 = new KisPaintLayer(p.image, "paint4", OPACITY_OPAQUE_U8);
layer6 = new KisPaintLayer(p.image, "paint6", OPACITY_OPAQUE_U8);
layer7 = new KisPaintLayer(p.image, "paint7", OPACITY_OPAQUE_U8);
layer8 = new KisPaintLayer(p.image, "paint8", OPACITY_OPAQUE_U8);
layer7->setCompositeOpId(COMPOSITE_ADD);
layer8->setCompositeOpId(COMPOSITE_ADD);
QRect rect1(100, 100, 100, 100);
QRect rect2(150, 150, 150, 150);
QRect tmaskRect(200,200,100,100);
QRect rect3(400, 100, 100, 100);
QRect rect4(500, 100, 100, 100);
QRect rect5(50, 50, 100, 100);
QRect rect6(50, 250, 100, 100);
QRect rect7(50, 350, 50, 50);
QRect rect8(50, 400, 50, 50);
layer1->paintDevice()->fill(rect1, KoColor(Qt::red, p.image->colorSpace()));
layer2->paintDevice()->fill(rect2, KoColor(Qt::green, p.image->colorSpace()));
tmask->testingInitSelection(tmaskRect, layer2);
layer3->paintDevice()->fill(rect3, KoColor(Qt::blue, p.image->colorSpace()));
layer4->paintDevice()->fill(rect4, KoColor(Qt::yellow, p.image->colorSpace()));
layer5->paintDevice()->fill(rect5, KoColor(Qt::green, p.image->colorSpace()));
layer6->paintDevice()->fill(rect6, KoColor(Qt::cyan, p.image->colorSpace()));
layer7->paintDevice()->fill(rect7, KoColor(Qt::red, p.image->colorSpace()));
layer8->paintDevice()->fill(rect8, KoColor(Qt::green, p.image->colorSpace()));
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->dropShadow()->setEffectEnabled(true);
style->dropShadow()->setDistance(10.0);
style->dropShadow()->setSpread(80.0);
style->dropShadow()->setSize(10);
style->dropShadow()->setNoise(0);
style->dropShadow()->setKnocksOut(false);
style->dropShadow()->setOpacity(80.0);
layer2->setLayerStyle(style);
layer2->setCompositeOpId(COMPOSITE_ADD);
group1->setCompositeOpId(COMPOSITE_ADD);
p.image->addNode(layer5);
p.image->addNode(layer2);
p.image->addNode(tmask, layer2);
p.image->addNode(group1);
p.image->addNode(layer3, group1);
p.image->addNode(layer4, group1);
p.image->addNode(layer6);
p.image->addNode(layer7);
p.image->addNode(layer8);
p.image->initialRefreshGraph();
// dbgKrita << ppVar(layer1->exactBounds());
// dbgKrita << ppVar(layer5->exactBounds());
// dbgKrita << ppVar(layer2->exactBounds());
// dbgKrita << ppVar(group1->exactBounds());
// dbgKrita << ppVar(layer3->exactBounds());
// dbgKrita << ppVar(layer4->exactBounds());
TestUtil::ReferenceImageChecker chk("flatten", "imagetest");
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
}
QRect refRect;
TestUtil::MaskParent p;
KisImageSP image;
KisSurrogateUndoStore *undoStore;
KisPaintLayerSP layer1;
KisPaintLayerSP layer2;
KisTransparencyMaskSP tmask;
KisGroupLayerSP group1;
KisPaintLayerSP layer3;
KisPaintLayerSP layer4;
KisPaintLayerSP layer5;
KisPaintLayerSP layer6;
KisPaintLayerSP layer7;
KisPaintLayerSP layer8;
};
template<class ContainerTest>
KisLayerSP flattenLayerHelper(ContainerTest &p, KisLayerSP layer, bool nothingHappens = false)
{
QSignalSpy spy(p.image.data(), SIGNAL(sigNodeAddedAsync(KisNodeSP)));
//p.image->flattenLayer(layer);
KisLayerUtils::flattenLayer(p.image, layer);
p.image->waitForDone();
if (nothingHappens) {
Q_ASSERT(!spy.count());
return layer;
}
Q_ASSERT(spy.count() == 1);
QList<QVariant> arguments = spy.takeFirst();
KisNodeSP newNode = arguments.first().value<KisNodeSP>();
KisLayerSP newLayer = qobject_cast<KisLayer*>(newNode.data());
return newLayer;
}
void KisImageTest::testFlattenLayer()
{
FlattenTestImage p;
TestUtil::ReferenceImageChecker chk("flatten", "imagetest");
{
QCOMPARE(p.layer2->compositeOpId(), COMPOSITE_ADD);
KisLayerSP newLayer = flattenLayerHelper(p, p.layer2);
//KisLayerSP newLayer = p.image->flattenLayer(p.layer2);
//p.image->waitForDone();
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer2_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
}
{
QCOMPARE(p.group1->compositeOpId(), COMPOSITE_ADD);
KisLayerSP newLayer = flattenLayerHelper(p, p.group1);
//KisLayerSP newLayer = p.image->flattenLayer(p.group1);
//p.image->waitForDone();
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "02_group1_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(newLayer->exactBounds(), QRect(400, 100, 200, 100));
}
{
QCOMPARE(p.layer5->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(p.layer5->alphaChannelDisabled(), true);
KisLayerSP newLayer = flattenLayerHelper(p, p.layer5, true);
//KisLayerSP newLayer = p.image->flattenLayer(p.layer5);
//p.image->waitForDone();
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "03_layer5_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(50, 50, 100, 100));
QCOMPARE(newLayer->alphaChannelDisabled(), true);
}
}
#include <kis_meta_data_merge_strategy_registry.h>
template<class ContainerTest>
KisLayerSP mergeHelper(ContainerTest &p, KisLayerSP layer)
{
KisNodeSP parent = layer->parent();
const int newIndex = parent->index(layer) - 1;
p.image->mergeDown(layer, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
//KisLayerUtils::mergeDown(p.image, layer, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
p.image->waitForDone();
KisLayerSP newLayer = qobject_cast<KisLayer*>(parent->at(newIndex).data());
return newLayer;
}
void KisImageTest::testMergeDown()
{
FlattenTestImage p;
TestUtil::ReferenceImageChecker img("flatten", "imagetest");
TestUtil::ReferenceImageChecker chk("mergedown_simple", "imagetest");
{
QCOMPARE(p.layer5->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(p.layer5->alphaChannelDisabled(), true);
KisLayerSP newLayer = mergeHelper(p, p.layer5);
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer5_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
{
QCOMPARE(p.layer2->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.layer2->alphaChannelDisabled(), false);
KisLayerSP newLayer = mergeHelper(p, p.layer2);
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "02_layer2_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(100, 100, 213, 217));
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
{
QCOMPARE(p.group1->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.group1->alphaChannelDisabled(), false);
KisLayerSP newLayer = mergeHelper(p, p.group1);
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "03_group1_mergedown_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(100, 100, 500, 217));
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
}
void KisImageTest::testMergeDownDestinationInheritsAlpha()
{
FlattenTestImage p;
TestUtil::ReferenceImageChecker img("flatten", "imagetest");
TestUtil::ReferenceImageChecker chk("mergedown_dst_inheritsalpha", "imagetest");
{
QCOMPARE(p.layer2->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.layer2->alphaChannelDisabled(), false);
KisLayerSP newLayer = mergeHelper(p, p.layer2);
// WARN: this check is suspicious!
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_proj_merged_layer2_over_layer5_IA"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer2_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(50,50, 263, 267));
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
}
void KisImageTest::testMergeDownDestinationCustomCompositeOp()
{
FlattenTestImage p;
TestUtil::ReferenceImageChecker img("flatten", "imagetest");
TestUtil::ReferenceImageChecker chk("mergedown_dst_customop", "imagetest");
{
QCOMPARE(p.layer6->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(p.layer6->alphaChannelDisabled(), false);
QCOMPARE(p.group1->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.group1->alphaChannelDisabled(), false);
KisLayerSP newLayer = mergeHelper(p, p.layer6);
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer6_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(50, 100, 550, 250));
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
}
void KisImageTest::testMergeDownDestinationSameCompositeOpLayerStyle()
{
FlattenTestImage p;
TestUtil::ReferenceImageChecker img("flatten", "imagetest");
TestUtil::ReferenceImageChecker chk("mergedown_sameop_ls", "imagetest");
{
QCOMPARE(p.group1->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.group1->alphaChannelDisabled(), false);
QCOMPARE(p.layer2->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.layer2->alphaChannelDisabled(), false);
KisLayerSP newLayer = mergeHelper(p, p.group1);
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_group1_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(197, 100, 403, 217));
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
}
void KisImageTest::testMergeDownDestinationSameCompositeOp()
{
FlattenTestImage p;
TestUtil::ReferenceImageChecker img("flatten", "imagetest");
TestUtil::ReferenceImageChecker chk("mergedown_sameop_fastpath", "imagetest");
{
QCOMPARE(p.layer8->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.layer8->alphaChannelDisabled(), false);
QCOMPARE(p.layer7->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.layer7->alphaChannelDisabled(), false);
KisLayerSP newLayer = mergeHelper(p, p.layer8);
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer8_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(newLayer->exactBounds(), QRect(50, 350, 50, 100));
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
}
#include "kis_image_animation_interface.h"
void KisImageTest::testMergeDownMultipleFrames()
{
FlattenTestImage p;
TestUtil::ReferenceImageChecker img("flatten", "imagetest");
TestUtil::ReferenceImageChecker chk("mergedown_simple", "imagetest");
QSet<int> initialFrames;
{
KisLayerSP l = p.layer5;
l->enableAnimation();
KisKeyframeChannel *channel = l->getKeyframeChannel(KisKeyframeChannel::Content.id(), true);
channel->addKeyframe(10);
channel->addKeyframe(20);
channel->addKeyframe(30);
QCOMPARE(channel->keyframeCount(), 4);
initialFrames = KisLayerUtils::fetchLayerFramesRecursive(l);
QCOMPARE(initialFrames.size(), 4);
}
{
QCOMPARE(p.layer5->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(p.layer5->alphaChannelDisabled(), true);
KisLayerSP newLayer = mergeHelper(p, p.layer5);
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer5_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->alphaChannelDisabled(), false);
QVERIFY(newLayer->isAnimated());
QSet<int> newFrames = KisLayerUtils::fetchLayerFramesRecursive(newLayer);
QCOMPARE(newFrames, initialFrames);
foreach (int frame, newFrames) {
KisImageAnimationInterface *interface = p.image->animationInterface();
int savedSwitchedTime = 0;
interface->saveAndResetCurrentTime(frame, &savedSwitchedTime);
QCOMPARE(newLayer->exactBounds(), QRect(100,100,100,100));
interface->restoreCurrentTime(&savedSwitchedTime);
}
p.undoStore->undo();
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
}
}
template<class ContainerTest>
KisNodeSP mergeMultipleHelper(ContainerTest &p, QList<KisNodeSP> selectedNodes, KisNodeSP putAfter)
{
QSignalSpy spy(p.image.data(), SIGNAL(sigNodeAddedAsync(KisNodeSP)));
p.image->mergeMultipleLayers(selectedNodes, putAfter);
//KisLayerUtils::mergeMultipleLayers(p.image, selectedNodes, putAfter);
p.image->waitForDone();
Q_ASSERT(spy.count() == 1);
QList<QVariant> arguments = spy.takeFirst();
KisNodeSP newNode = arguments.first().value<KisNodeSP>();
return newNode;
}
void KisImageTest::testMergeMultiple()
{
FlattenTestImage p;
TestUtil::ReferenceImageChecker img("flatten", "imagetest");
TestUtil::ReferenceImageChecker chk("mergemultiple", "imagetest");
{
QList<KisNodeSP> selectedNodes;
selectedNodes << p.layer2
<< p.group1
<< p.layer6;
{
KisNodeSP newLayer = mergeMultipleHelper(p, selectedNodes, 0);
//KisNodeSP newLayer = p.image->mergeMultipleLayers(selectedNodes, 0);
//p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer8_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(50, 100, 550, 250));
}
}
p.p.undoStore->undo();
p.image->waitForDone();
// Test reversed order, the result must be the same
{
QList<KisNodeSP> selectedNodes;
selectedNodes << p.layer6
<< p.group1
<< p.layer2;
{
KisNodeSP newLayer = mergeMultipleHelper(p, selectedNodes, 0);
//KisNodeSP newLayer = p.image->mergeMultipleLayers(selectedNodes, 0);
//p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer8_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(50, 100, 550, 250));
}
}
}
void testMergeCrossColorSpaceImpl(bool useProjectionColorSpace, bool swapSpaces)
{
TestUtil::MaskParent p;
KisPaintLayerSP layer1;
KisPaintLayerSP layer2;
KisPaintLayerSP layer3;
const KoColorSpace *cs2 = useProjectionColorSpace ?
p.image->colorSpace() : KoColorSpaceRegistry::instance()->lab16();
const KoColorSpace *cs3 = KoColorSpaceRegistry::instance()->rgb16();
if (swapSpaces) {
std::swap(cs2, cs3);
}
dbgKrita << "Testing testMergeCrossColorSpaceImpl:";
dbgKrita << " " << ppVar(cs2);
dbgKrita << " " << ppVar(cs3);
layer1 = p.layer;
layer2 = new KisPaintLayer(p.image, "paint2", OPACITY_OPAQUE_U8, cs2);
layer3 = new KisPaintLayer(p.image, "paint3", OPACITY_OPAQUE_U8, cs3);
QRect rect1(100, 100, 100, 100);
QRect rect2(150, 150, 150, 150);
QRect rect3(250, 250, 200, 200);
layer1->paintDevice()->fill(rect1, KoColor(Qt::red, layer1->colorSpace()));
layer2->paintDevice()->fill(rect2, KoColor(Qt::green, layer2->colorSpace()));
layer3->paintDevice()->fill(rect3, KoColor(Qt::blue, layer3->colorSpace()));
p.image->addNode(layer2);
p.image->addNode(layer3);
p.image->initialRefreshGraph();
{
KisLayerSP newLayer = mergeHelper(p, layer3);
QCOMPARE(newLayer->colorSpace(), p.image->colorSpace());
p.undoStore->undo();
p.image->waitForDone();
}
{
layer2->disableAlphaChannel(true);
KisLayerSP newLayer = mergeHelper(p, layer3);
QCOMPARE(newLayer->colorSpace(), p.image->colorSpace());
p.undoStore->undo();
p.image->waitForDone();
}
}
void KisImageTest::testMergeCrossColorSpace()
{
testMergeCrossColorSpaceImpl(true, false);
testMergeCrossColorSpaceImpl(true, true);
testMergeCrossColorSpaceImpl(false, false);
testMergeCrossColorSpaceImpl(false, true);
}
void KisImageTest::testMergeSelectionMasks()
{
TestUtil::MaskParent p;
QRect rect1(100, 100, 100, 100);
QRect rect2(150, 150, 150, 150);
QRect rect3(50, 50, 100, 100);
KisPaintLayerSP layer1 = p.layer;
layer1->paintDevice()->fill(rect1, KoColor(Qt::red, layer1->colorSpace()));
p.image->initialRefreshGraph();
KisSelectionSP sel = new KisSelection(layer1->paintDevice()->defaultBounds());
sel->pixelSelection()->select(rect2, MAX_SELECTED);
KisSelectionMaskSP mask1 = new KisSelectionMask(p.image);
mask1->initSelection(sel, layer1);
p.image->addNode(mask1, layer1);
QVERIFY(!layer1->selection());
mask1->setActive(true);
QCOMPARE(layer1->selection()->selectedExactRect(), QRect(150,150,150,150));
sel->pixelSelection()->select(rect3, MAX_SELECTED);
KisSelectionMaskSP mask2 = new KisSelectionMask(p.image);
mask2->initSelection(sel, layer1);
p.image->addNode(mask2, layer1);
QCOMPARE(layer1->selection()->selectedExactRect(), QRect(150,150,150,150));
mask2->setActive(true);
QCOMPARE(layer1->selection()->selectedExactRect(), QRect(50,50,250,250));
QList<KisNodeSP> selectedNodes;
selectedNodes << mask2
<< mask1;
{
KisNodeSP newLayer = mergeMultipleHelper(p, selectedNodes, 0);
QCOMPARE(newLayer->parent(), KisNodeSP(layer1));
QCOMPARE((int)layer1->childCount(), 1);
QCOMPARE(layer1->selection()->selectedExactRect(), QRect(50,50,250,250));
}
}
void KisImageTest::testFlattenImage()
{
FlattenTestImage p;
KisImageSP image = p.image;
TestUtil::ReferenceImageChecker img("flatten", "imagetest");
{
KisLayerUtils::flattenImage(p.image, 0);
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
p.undoStore->undo();
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
}
{
KisLayerUtils::flattenImage(p.image, p.layer5); // flatten with active layer just under the root (not inside any group)
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
p.undoStore->undo();
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
}
{
KisLayerUtils::flattenImage(p.image, p.layer2); // flatten with active layer just under the root (not inside any group), but with a mask
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
p.undoStore->undo();
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
}
{
KisLayerUtils::flattenImage(p.image, p.layer3); // flatten with active layer inside of a group
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
p.undoStore->undo();
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
}
}
struct FlattenPassThroughTestImage
{
FlattenPassThroughTestImage()
: refRect(0,0,512,512)
, p(refRect)
{
image = p.image;
undoStore = p.undoStore;
group1 = new KisGroupLayer(p.image, "group1", OPACITY_OPAQUE_U8);
layer2 = new KisPaintLayer(p.image, "paint2", OPACITY_OPAQUE_U8);
layer3 = new KisPaintLayer(p.image, "paint3", OPACITY_OPAQUE_U8);
group4 = new KisGroupLayer(p.image, "group4", OPACITY_OPAQUE_U8);
layer5 = new KisPaintLayer(p.image, "paint5", OPACITY_OPAQUE_U8);
layer6 = new KisPaintLayer(p.image, "paint6", OPACITY_OPAQUE_U8);
QRect rect2(100, 100, 100, 100);
QRect rect3(150, 150, 100, 100);
QRect rect5(200, 200, 100, 100);
QRect rect6(250, 250, 100, 100);
group1->setPassThroughMode(true);
layer2->paintDevice()->fill(rect2, KoColor(Qt::red, p.image->colorSpace()));
layer3->paintDevice()->fill(rect3, KoColor(Qt::green, p.image->colorSpace()));
group4->setPassThroughMode(true);
layer5->paintDevice()->fill(rect5, KoColor(Qt::blue, p.image->colorSpace()));
layer6->paintDevice()->fill(rect6, KoColor(Qt::yellow, p.image->colorSpace()));
p.image->addNode(group1);
p.image->addNode(layer2, group1);
p.image->addNode(layer3, group1);
p.image->addNode(group4);
p.image->addNode(layer5, group4);
p.image->addNode(layer6, group4);
p.image->initialRefreshGraph();
TestUtil::ReferenceImageChecker chk("passthrough", "imagetest");
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
}
QRect refRect;
TestUtil::MaskParent p;
KisImageSP image;
KisSurrogateUndoStore *undoStore;
KisGroupLayerSP group1;
KisPaintLayerSP layer2;
KisPaintLayerSP layer3;
KisGroupLayerSP group4;
KisPaintLayerSP layer5;
KisPaintLayerSP layer6;
};
void KisImageTest::testFlattenPassThroughLayer()
{
FlattenPassThroughTestImage p;
TestUtil::ReferenceImageChecker chk("passthrough", "imagetest");
{
QCOMPARE(p.group1->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(p.group1->passThroughMode(), true);
KisLayerSP newLayer = flattenLayerHelper(p, p.group1);
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_group1_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QVERIFY(newLayer->inherits("KisPaintLayer"));
}
}
void KisImageTest::testMergeTwoPassThroughLayers()
{
FlattenPassThroughTestImage p;
TestUtil::ReferenceImageChecker chk("passthrough", "imagetest");
{
QCOMPARE(p.group1->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(p.group1->passThroughMode(), true);
KisLayerSP newLayer = mergeHelper(p, p.group4);
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QVERIFY(newLayer->inherits("KisGroupLayer"));
}
}
void KisImageTest::testMergePaintOverPassThroughLayer()
{
FlattenPassThroughTestImage p;
TestUtil::ReferenceImageChecker chk("passthrough", "imagetest");
{
QCOMPARE(p.group1->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(p.group1->passThroughMode(), true);
KisLayerSP newLayer = flattenLayerHelper(p, p.group4);
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(newLayer->inherits("KisPaintLayer"));
newLayer = mergeHelper(p, newLayer);
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(newLayer->inherits("KisPaintLayer"));
}
}
void KisImageTest::testMergePassThroughOverPaintLayer()
{
FlattenPassThroughTestImage p;
TestUtil::ReferenceImageChecker chk("passthrough", "imagetest");
{
QCOMPARE(p.group1->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(p.group1->passThroughMode(), true);
KisLayerSP newLayer = flattenLayerHelper(p, p.group1);
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(newLayer->inherits("KisPaintLayer"));
newLayer = mergeHelper(p, p.group4);
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(newLayer->inherits("KisPaintLayer"));
}
}
#include "kis_paint_device_debug_utils.h"
#include "kis_algebra_2d.h"
void KisImageTest::testPaintOverlayMask()
{
QRect refRect(0, 0, 512, 512);
TestUtil::MaskParent p(refRect);
QRect fillRect(50, 50, 412, 412);
QRect selectionRect(200, 200, 100, 50);
KisPaintLayerSP layer1 = p.layer;
layer1->paintDevice()->fill(fillRect, KoColor(Qt::yellow, layer1->colorSpace()));
KisSelectionMaskSP mask = new KisSelectionMask(p.image);
KisSelectionSP selection = new KisSelection(new KisSelectionDefaultBounds(layer1->paintDevice()));
selection->pixelSelection()->select(selectionRect, 128);
selection->pixelSelection()->select(KisAlgebra2D::blowRect(selectionRect,-0.3), 255);
mask->setSelection(selection);
//mask->setVisible(false);
//mask->setActive(false);
p.image->addNode(mask, layer1);
// a simple layer to disable oblidge child mechanism
KisPaintLayerSP layer2 = new KisPaintLayer(p.image, "layer2", OPACITY_OPAQUE_U8);
p.image->addNode(layer2);
p.image->initialRefreshGraph();
KIS_DUMP_DEVICE_2(p.image->projection(), refRect, "00_initial", "dd");
p.image->setOverlaySelectionMask(mask);
p.image->waitForDone();
KIS_DUMP_DEVICE_2(p.image->projection(), refRect, "01_activated_00_image", "dd");
KIS_DUMP_DEVICE_2(p.image->root()->original(), refRect, "01_activated_01_root_original", "dd");
KIS_DUMP_DEVICE_2(p.image->root()->projection(), refRect, "01_activated_02_root_projection", "dd");
KisImageSP clonedImage = p.image->clone();
clonedImage->waitForDone();
KIS_DUMP_DEVICE_2(clonedImage->projection(), refRect, "02_cloned_when_activated_00_image", "dd");
KIS_DUMP_DEVICE_2(clonedImage->root()->original(), refRect, "02_cloned_when_activated_01_root_original", "dd");
KIS_DUMP_DEVICE_2(clonedImage->root()->projection(), refRect, "02_cloned_when_activated_02_root_projection", "dd");
p.image->setOverlaySelectionMask(0);
p.image->waitForDone();
KIS_DUMP_DEVICE_2(p.image->projection(), refRect, "03_deactivated", "dd");
}
KISTEST_MAIN(KisImageTest)
diff --git a/libs/image/tests/kis_iterator_benchmark.cpp b/libs/image/tests/kis_iterator_benchmark.cpp
index 963f2dd1e1..59ac7ecd5a 100644
--- a/libs/image/tests/kis_iterator_benchmark.cpp
+++ b/libs/image/tests/kis_iterator_benchmark.cpp
@@ -1,248 +1,248 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.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.
*/
#include "kis_iterator_benchmark.h"
#include <QApplication>
#include <QTest>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include "kis_random_accessor_ng.h"
#include "kis_random_sub_accessor.h"
#include <kis_iterator_ng.h>
#include <kis_repeat_iterators_pixel.h>
#include "kis_paint_device.h"
#define TEST_WIDTH 3000
#define TEST_HEIGHT 3000
template <bool useXY>
void KisIteratorBenchmark::sequentialIter(const KoColorSpace * colorSpace)
{
KisPaintDeviceSP dev = new KisPaintDevice(colorSpace);
quint8 * bytes = new quint8[colorSpace->pixelSize() * 64*64];
memset(bytes, 128, 64 * 64 * colorSpace->pixelSize());
- QTime t;
+ QElapsedTimer t;
t.start();
for (int i = 0; i < 3; i++) {
KisSequentialIterator it(dev, QRect(0, 0, TEST_WIDTH, TEST_HEIGHT));
int sum = 0;
while (it.nextPixel()) {
if (useXY) {
sum = it.x() + it.y();
}
memcpy(it.rawData(), bytes, colorSpace->pixelSize());
}
qDebug() << ppVar(useXY) << "SequentialIterator run " << i << "took" << t.elapsed();
Q_UNUSED(sum);
t.restart();
}
t.restart();
for (int i = 0; i < 3; i++) {
KisSequentialConstIterator it(dev, QRect(0, 0, TEST_WIDTH, TEST_HEIGHT));
int sum = 0;
while (it.nextPixel()) {
if (useXY) {
sum = it.x() + it.y();
}
//memcpy(it.rawData(), bytes, colorSpace->pixelSize());
}
qDebug() << ppVar(useXY) << "SequentialConstIterator run " << i << "took" << t.elapsed();
Q_UNUSED(sum);
t.restart();
}
delete[] bytes;
}
void KisIteratorBenchmark::hLineIterNG(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
quint8 * bytes = new quint8[colorSpace->pixelSize() * 128];
memset(bytes, 128, 128 * colorSpace->pixelSize());
- QTime t;
+ QElapsedTimer t;
t.start();
for (int i = 0; i < 3; i++) {
KisHLineIteratorSP it = dev.createHLineIteratorNG(0, 0, TEST_WIDTH);
for (int j = 0; j < TEST_HEIGHT; j++) {
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while (it->nextPixel());
it->nextRow();
}
qDebug() << "HLineIteratorNG run " << i << "took" << t.elapsed();
t.restart();
}
for (int i = 0; i < 3; i++) {
KisHLineIteratorSP it = dev.createHLineIteratorNG(0, 0, TEST_WIDTH);
for (int j = 0; j < TEST_HEIGHT; j++) {
int pixels;
do {
pixels = it->nConseqPixels();
memcpy(it->rawData(), bytes, pixels * colorSpace->pixelSize());
} while (it->nextPixels(pixels));
it->nextRow();
}
qDebug() << "HLineIteratorNG with nConseqHPixels run " << i << "took" << t.elapsed();
t.restart();
}
KisHLineConstIteratorSP cit = dev.createHLineConstIteratorNG(0, 0, TEST_WIDTH);
for (int i = 0; i < TEST_HEIGHT; i++) {
do {
//do stuff
} while (cit->nextPixel());
cit->nextRow();
}
qDebug() << "const HLineIteratorNG took" << t.elapsed();
delete[] bytes;
}
void KisIteratorBenchmark::vLineIterNG(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
quint8 * bytes = new quint8[colorSpace->pixelSize()];
memset(bytes, 128, colorSpace->pixelSize());
- QTime t;
+ QElapsedTimer t;
t.start();
for (int i = 0; i < 3; i++) {
KisVLineIteratorSP it = dev.createVLineIteratorNG(0, 0, TEST_HEIGHT);
for (int j = 0; j < TEST_WIDTH; j++) {
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while(it->nextPixel());
it->nextColumn();
}
qDebug() << "VLineIteratorNG run " << i << " took" << t.elapsed();
t.restart();
}
KisVLineConstIteratorSP cit = dev.createVLineConstIteratorNG(0, 0, TEST_HEIGHT);
for (int i = 0; i < TEST_WIDTH; i++) {
do {} while(cit->nextPixel());
cit->nextColumn();
}
qDebug() << "const VLineIteratorNG took" << t.elapsed();
delete[] bytes;
}
void KisIteratorBenchmark::randomAccessor(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
quint8 * bytes = new quint8[colorSpace->pixelSize() * 128];
memset(bytes, 128, 128 * colorSpace->pixelSize());
- QTime t;
+ QElapsedTimer t;
t.start();
for (int i = 0; i < 3; i++) {
KisRandomAccessorSP ac = dev.createRandomAccessorNG(0, 0);
for (int y = 0; y < TEST_HEIGHT; ++y) {
for (int x = 0; x < TEST_WIDTH; ++x) {
ac->moveTo(x, y);
memcpy(ac->rawData(), bytes, colorSpace->pixelSize());
}
}
qDebug() << "RandomIterator run " << i << " took" << t.elapsed();
t.restart();
}
for (int i = 0; i < 3; i++) {
KisRandomAccessorSP ac = dev.createRandomAccessorNG(0, 0);
for (int y = 0; y < TEST_HEIGHT; ) {
int numContiguousRows = qMin(ac->numContiguousRows(y), TEST_HEIGHT - y);
for (int x = 0; x < TEST_WIDTH; ) {
int numContiguousColumns = qMin(ac->numContiguousColumns(x), TEST_WIDTH - x);
ac->moveTo(x, y);
int rowStride = ac->rowStride(x, y);
quint8 *data = ac->rawData();
for (int i = 0; i < numContiguousRows; i++) {
memcpy(data, bytes, numContiguousColumns * colorSpace->pixelSize());
data += rowStride;
}
x += numContiguousColumns;
}
y += numContiguousRows;
}
qDebug() << "RandomIterator run (with strides)" << i << " took" << t.elapsed();
t.restart();
}
KisRandomConstAccessorSP cac = dev.createRandomConstAccessorNG(0, 0);
for (int y = 0; y < TEST_HEIGHT; ++y) {
for (int x = 0; x < TEST_WIDTH; ++x) {
cac->moveTo(x, y);
}
}
qDebug() << "const RandomIterator took" << t.elapsed();
delete[] bytes;
}
void KisIteratorBenchmark::runBenchmark()
{
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
hLineIterNG(cs);
vLineIterNG(cs);
sequentialIter<false>(cs);
sequentialIter<true>(cs);
randomAccessor(cs);
}
QTEST_MAIN(KisIteratorBenchmark)
diff --git a/libs/image/tests/kis_layer_style_projection_plane_test.cpp b/libs/image/tests/kis_layer_style_projection_plane_test.cpp
index 095c1f56f7..13009ef7bf 100644
--- a/libs/image/tests/kis_layer_style_projection_plane_test.cpp
+++ b/libs/image/tests/kis_layer_style_projection_plane_test.cpp
@@ -1,617 +1,617 @@
/*
* 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_style_projection_plane_test.h"
#include <QTest>
#include "testutil.h"
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <resources/KoPattern.h>
#include "kis_transparency_mask.h"
#include "kis_paint_layer.h"
#include "kis_image.h"
#include "kis_painter.h"
#include "kis_selection.h"
#include "kis_pixel_selection.h"
#include "layerstyles/kis_layer_style_projection_plane.h"
#include "kis_psd_layer_style.h"
#include "kis_paint_device_debug_utils.h"
-
+#include <KisGlobalResourcesInterface.h>
void KisLayerStyleProjectionPlaneTest::test(KisPSDLayerStyleSP style, const QString testName)
{
const QRect imageRect(0, 0, 200, 200);
const QRect rFillRect(10, 10, 100, 100);
const QRect tMaskRect(50, 50, 20, 20);
const QRect partialSelectionRect(90, 50, 20, 20);
const QRect updateRect1(10, 10, 50, 100);
const QRect updateRect2(60, 10, 50, 100);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "styles test");
KisPaintLayerSP layer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8);
image->addNode(layer);
KisLayerStyleProjectionPlane plane(layer.data(), style);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "00L_initial", testName);
//layer->paintDevice()->fill(rFillRect, KoColor(Qt::red, cs));
{
KisPainter gc(layer->paintDevice());
gc.setPaintColor(KoColor(Qt::red, cs));
gc.setFillStyle(KisPainter::FillStyleForegroundColor);
gc.paintEllipse(rFillRect);
}
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "01L_fill", testName);
KisPaintDeviceSP projection = new KisPaintDevice(cs);
{
const QRect changeRect = plane.changeRect(rFillRect, KisLayer::N_FILTHY);
dbgKrita << ppVar(rFillRect) << ppVar(changeRect);
plane.recalculate(changeRect, layer);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "02L_recalculate_fill", testName);
KisPainter painter(projection);
plane.apply(&painter, changeRect);
KIS_DUMP_DEVICE_2(projection, imageRect, "03P_apply_on_fill", testName);
}
//return;
KisTransparencyMaskSP transparencyMask = new KisTransparencyMask();
KisSelectionSP selection = new KisSelection();
selection->pixelSelection()->select(tMaskRect, OPACITY_OPAQUE_U8);
transparencyMask->setSelection(selection);
image->addNode(transparencyMask, layer);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "04L_mask_added", testName);
plane.recalculate(imageRect, layer);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "05L_mask_added_recalculated", testName);
{
projection->clear();
KisPainter painter(projection);
plane.apply(&painter, imageRect);
KIS_DUMP_DEVICE_2(projection, imageRect, "06P_apply_on_mask", testName);
}
selection->pixelSelection()->select(partialSelectionRect, OPACITY_OPAQUE_U8);
{
const QRect changeRect = plane.changeRect(partialSelectionRect, KisLayer::N_FILTHY);
projection->clear(changeRect);
dbgKrita << ppVar(partialSelectionRect) << ppVar(changeRect);
plane.recalculate(changeRect, layer);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "07L_recalculate_partial", testName);
KisPainter painter(projection);
plane.apply(&painter, changeRect);
KIS_DUMP_DEVICE_2(projection, imageRect, "08P_apply_partial", testName);
}
// half updates
transparencyMask->setVisible(false);
{
const QRect changeRect = plane.changeRect(updateRect1, KisLayer::N_FILTHY);
projection->clear(changeRect);
dbgKrita << ppVar(updateRect1) << ppVar(changeRect);
plane.recalculate(changeRect, layer);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "09L_recalculate_half1", testName);
KisPainter painter(projection);
plane.apply(&painter, changeRect);
KIS_DUMP_DEVICE_2(projection, imageRect, "10P_apply_half1", testName);
}
{
const QRect changeRect = plane.changeRect(updateRect2, KisLayer::N_FILTHY);
projection->clear(changeRect);
dbgKrita << ppVar(updateRect2) << ppVar(changeRect);
plane.recalculate(changeRect, layer);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "09L_recalculate_half1", testName);
KisPainter painter(projection);
plane.apply(&painter, changeRect);
KIS_DUMP_DEVICE_2(projection, imageRect, "10P_apply_half2", testName);
}
}
void KisLayerStyleProjectionPlaneTest::testShadow()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->dropShadow()->setSize(15);
style->dropShadow()->setDistance(15);
style->dropShadow()->setOpacity(70);
style->dropShadow()->setNoise(30);
style->dropShadow()->setEffectEnabled(true);
style->innerShadow()->setSize(10);
style->innerShadow()->setSpread(10);
style->innerShadow()->setDistance(5);
style->innerShadow()->setOpacity(70);
style->innerShadow()->setNoise(30);
style->innerShadow()->setEffectEnabled(true);
test(style, "shadow");
}
void KisLayerStyleProjectionPlaneTest::testGlow()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->outerGlow()->setSize(15);
style->outerGlow()->setSpread(10);
style->outerGlow()->setOpacity(70);
style->outerGlow()->setNoise(30);
style->outerGlow()->setEffectEnabled(true);
style->outerGlow()->setColor(Qt::green);
test(style, "glow_outer");
}
#include <resources/KoStopGradient.h>
void KisLayerStyleProjectionPlaneTest::testGlowGradient()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->outerGlow()->setSize(15);
style->outerGlow()->setSpread(10);
style->outerGlow()->setOpacity(70);
style->outerGlow()->setNoise(10);
style->outerGlow()->setEffectEnabled(true);
style->outerGlow()->setColor(Qt::green);
QLinearGradient testGradient;
testGradient.setColorAt(0.0, Qt::white);
testGradient.setColorAt(0.5, Qt::green);
testGradient.setColorAt(1.0, Qt::black);
testGradient.setSpread(QGradient::ReflectSpread);
QSharedPointer<KoStopGradient> gradient(
KoStopGradient::fromQGradient(&testGradient));
style->outerGlow()->setGradient(gradient);
style->outerGlow()->setFillType(psd_fill_gradient);
test(style, "glow_outer_grad");
}
void KisLayerStyleProjectionPlaneTest::testGlowGradientJitter()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->outerGlow()->setSize(15);
style->outerGlow()->setSpread(10);
style->outerGlow()->setOpacity(70);
style->outerGlow()->setNoise(0);
style->outerGlow()->setEffectEnabled(true);
style->outerGlow()->setColor(Qt::green);
QLinearGradient testGradient;
testGradient.setColorAt(0.0, Qt::white);
testGradient.setColorAt(0.5, Qt::green);
testGradient.setColorAt(1.0, Qt::black);
testGradient.setSpread(QGradient::ReflectSpread);
QSharedPointer<KoStopGradient> gradient(
KoStopGradient::fromQGradient(&testGradient));
style->outerGlow()->setGradient(gradient);
style->outerGlow()->setFillType(psd_fill_gradient);
style->outerGlow()->setJitter(20);
test(style, "glow_outer_grad_jit");
}
void KisLayerStyleProjectionPlaneTest::testGlowInnerGradient()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->innerGlow()->setSize(15);
style->innerGlow()->setSpread(10);
style->innerGlow()->setOpacity(80);
style->innerGlow()->setNoise(10);
style->innerGlow()->setEffectEnabled(true);
style->innerGlow()->setColor(Qt::white);
QLinearGradient testGradient;
testGradient.setColorAt(0.0, Qt::white);
testGradient.setColorAt(0.5, Qt::green);
testGradient.setColorAt(1.0, Qt::black);
testGradient.setSpread(QGradient::ReflectSpread);
QSharedPointer<KoStopGradient> gradient(
KoStopGradient::fromQGradient(&testGradient));
style->innerGlow()->setGradient(gradient);
style->innerGlow()->setFillType(psd_fill_gradient);
test(style, "glow_inner_grad");
style->innerGlow()->setFillType(psd_fill_solid_color);
style->innerGlow()->setSource(psd_glow_center);
test(style, "glow_inner_grad_center");
}
#include <KoCompositeOpRegistry.h>
void KisLayerStyleProjectionPlaneTest::testSatin()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->satin()->setSize(15);
style->satin()->setOpacity(80);
style->satin()->setAngle(180);
style->satin()->setEffectEnabled(true);
style->satin()->setColor(Qt::white);
style->satin()->setBlendMode(COMPOSITE_LINEAR_DODGE);
test(style, "satin");
}
void KisLayerStyleProjectionPlaneTest::testColorOverlay()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->colorOverlay()->setOpacity(80);
style->colorOverlay()->setEffectEnabled(true);
style->colorOverlay()->setColor(Qt::white);
style->colorOverlay()->setBlendMode(COMPOSITE_LINEAR_DODGE);
test(style, "color_overlay");
}
void KisLayerStyleProjectionPlaneTest::testGradientOverlay()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->gradientOverlay()->setAngle(90);
style->gradientOverlay()->setOpacity(80);
style->gradientOverlay()->setEffectEnabled(true);
style->gradientOverlay()->setBlendMode(COMPOSITE_LINEAR_DODGE);
style->gradientOverlay()->setAlignWithLayer(true);
style->gradientOverlay()->setScale(100);
style->gradientOverlay()->setStyle(psd_gradient_style_diamond);
QLinearGradient testGradient;
testGradient.setColorAt(0.0, Qt::white);
testGradient.setColorAt(0.5, Qt::green);
testGradient.setColorAt(1.0, Qt::black);
testGradient.setSpread(QGradient::ReflectSpread);
QSharedPointer<KoStopGradient> gradient(
KoStopGradient::fromQGradient(&testGradient));
style->gradientOverlay()->setGradient(gradient);
test(style, "grad_overlay");
}
void KisLayerStyleProjectionPlaneTest::testPatternOverlay()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->patternOverlay()->setOpacity(80);
style->patternOverlay()->setEffectEnabled(true);
style->patternOverlay()->setBlendMode(COMPOSITE_LINEAR_DODGE);
style->patternOverlay()->setScale(100);
style->patternOverlay()->setAlignWithLayer(false);
QString fileName(TestUtil::fetchDataFileLazy("pattern.pat"));
- KoPattern pattern(fileName);
- QVERIFY(pattern.load());
+ KoPatternSP pattern(new KoPattern(fileName));
+ QVERIFY(pattern->load(KisGlobalResourcesInterface::instance()));
- style->patternOverlay()->setPattern(&pattern);
+ style->patternOverlay()->setPattern(pattern);
test(style, "pat_overlay");
}
void KisLayerStyleProjectionPlaneTest::testStroke()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->stroke()->setColor(Qt::blue);
style->stroke()->setOpacity(80);
style->stroke()->setEffectEnabled(true);
style->stroke()->setBlendMode(COMPOSITE_OVER);
style->stroke()->setSize(3);
style->stroke()->setPosition(psd_stroke_center);
test(style, "stroke_col_ctr");
style->stroke()->setPosition(psd_stroke_outside);
test(style, "stroke_col_out");
style->stroke()->setPosition(psd_stroke_inside);
test(style, "stroke_col_in");
QString fileName(TestUtil::fetchDataFileLazy("pattern.pat"));
- KoPattern pattern(fileName);
- QVERIFY(pattern.load());
- style->stroke()->setPattern(&pattern);
+ KoPatternSP pattern(new KoPattern(fileName));
+ QVERIFY(pattern->load(KisGlobalResourcesInterface::instance()));
+ style->stroke()->setPattern(pattern);
style->stroke()->setFillType(psd_fill_pattern);
test(style, "stroke_pat");
QLinearGradient testGradient;
testGradient.setColorAt(0.0, Qt::white);
testGradient.setColorAt(0.5, Qt::green);
testGradient.setColorAt(1.0, Qt::black);
testGradient.setSpread(QGradient::ReflectSpread);
QSharedPointer<KoStopGradient> gradient(
KoStopGradient::fromQGradient(&testGradient));
style->stroke()->setGradient(gradient);
style->stroke()->setFillType(psd_fill_gradient);
test(style, "stroke_grad");
}
#include "layerstyles/gimp_bump_map.h"
void KisLayerStyleProjectionPlaneTest::testBumpmap()
{
KisPixelSelectionSP device = new KisPixelSelection();
const int numCycles = 30;
const int step = 5;
QRect applyRect(200, 100, 100, 100);
QRect fillRect(210, 110, 80, 80);
quint8 selectedness = 256 - numCycles * step;
for (int i = 0; i < numCycles; i++) {
device->select(fillRect, selectedness);
fillRect = kisGrowRect(fillRect, -1);
selectedness += step;
}
KIS_DUMP_DEVICE_2(device, applyRect, "00_initial", "bumpmap");
bumpmap_vals_t bmvals;
bmvals.azimuth = 240;
bmvals.elevation = 30;
bmvals.depth = 50;
bmvals.ambient = 128;
bmvals.compensate = false;
bmvals.invert = false;
bmvals.type = 0;
bumpmap(device, applyRect, bmvals);
KIS_DUMP_DEVICE_2(device, applyRect, "01_bumpmapped", "bumpmap");
}
void KisLayerStyleProjectionPlaneTest::testBevel()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->bevelAndEmboss()->setEffectEnabled(true);
style->bevelAndEmboss()->setAngle(135);
style->bevelAndEmboss()->setAltitude(45);
style->bevelAndEmboss()->setDepth(100);
style->bevelAndEmboss()->setHighlightColor(Qt::white);
style->bevelAndEmboss()->setHighlightBlendMode(COMPOSITE_OVER);
style->bevelAndEmboss()->setHighlightOpacity(100);
style->bevelAndEmboss()->setShadowColor(Qt::black);
style->bevelAndEmboss()->setShadowBlendMode(COMPOSITE_OVER);
style->bevelAndEmboss()->setShadowOpacity(100);
QString fileName(TestUtil::fetchDataFileLazy("pattern.pat"));
- KoPattern pattern(fileName);
- QVERIFY(pattern.load());
+ KoPatternSP pattern(new KoPattern(fileName));
+ QVERIFY(pattern->load(KisGlobalResourcesInterface::instance()));
- style->bevelAndEmboss()->setTexturePattern(&pattern);
+ style->bevelAndEmboss()->setTexturePattern(pattern);
style->bevelAndEmboss()->setTextureEnabled(true);
style->bevelAndEmboss()->setTextureDepth(-10);
style->bevelAndEmboss()->setTextureInvert(false);
style->bevelAndEmboss()->setStyle(psd_bevel_outer_bevel);
style->bevelAndEmboss()->setDirection(psd_direction_up);
style->bevelAndEmboss()->setSoften(0);
test(style, "bevel_outer_up");
style->bevelAndEmboss()->setTextureInvert(true);
style->bevelAndEmboss()->setStyle(psd_bevel_outer_bevel);
style->bevelAndEmboss()->setDirection(psd_direction_up);
style->bevelAndEmboss()->setSoften(0);
test(style, "bevel_outer_up_invert_texture");
style->bevelAndEmboss()->setTextureInvert(false);
style->bevelAndEmboss()->setStyle(psd_bevel_outer_bevel);
style->bevelAndEmboss()->setDirection(psd_direction_down);
style->bevelAndEmboss()->setSoften(0);
test(style, "bevel_outer_down");
style->bevelAndEmboss()->setStyle(psd_bevel_emboss);
style->bevelAndEmboss()->setDirection(psd_direction_up);
style->bevelAndEmboss()->setSoften(0);
test(style, "bevel_emboss_up");
style->bevelAndEmboss()->setStyle(psd_bevel_pillow_emboss);
style->bevelAndEmboss()->setDirection(psd_direction_up);
style->bevelAndEmboss()->setSoften(0);
test(style, "bevel_pillow_up");
style->bevelAndEmboss()->setStyle(psd_bevel_pillow_emboss);
style->bevelAndEmboss()->setDirection(psd_direction_down);
style->bevelAndEmboss()->setSoften(0);
test(style, "bevel_pillow_down");
style->bevelAndEmboss()->setStyle(psd_bevel_pillow_emboss);
style->bevelAndEmboss()->setDirection(psd_direction_up);
style->bevelAndEmboss()->setSoften(3);
test(style, "bevel_pillow_up_soft");
}
#include "kis_ls_utils.h"
void KisLayerStyleProjectionPlaneTest::testBlending()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP layer = new KisPaintDevice(cs);
KisPaintDeviceSP overlay = new KisPaintDevice(cs);
KisPaintDeviceSP bg = new KisPaintDevice(cs);
KisPaintDeviceSP result = new KisPaintDevice(cs);
const int width = 20;
KoColor color(Qt::transparent, cs);
QVector<QColor> layerColors;
QVector<QColor> overlayColors;
QVector<QColor> bgColors;
layerColors << QColor(0, 255, 0);
layerColors << QColor(128, 255, 64);
overlayColors << QColor(255, 0, 0);
overlayColors << QColor(255, 128, 64);
bgColors << QColor(0, 0, 0, 0);
bgColors << QColor(0, 0, 0, 255);
bgColors << QColor(255, 255, 255, 255);
bgColors << QColor(64, 128, 255, 255);
bgColors << QColor(0, 0, 0, 128);
bgColors << QColor(255, 255, 255, 128);
bgColors << QColor(64, 128, 255, 128);
const int overlayOpacity = 255;
const int layerOpacity = 255;
int y = 1;
Q_FOREACH(const QColor &layerColor, layerColors) {
Q_FOREACH(const QColor &overlayColor, overlayColors) {
Q_FOREACH(const QColor &bgColor, bgColors) {
bg->setPixel(0, y, layerColor);
bg->setPixel(1, y, overlayColor);
bg->setPixel(2, y, bgColor);
bg->setPixel(3, y, QColor(layerOpacity, layerOpacity, layerOpacity, 255));
bg->setPixel(4, y, QColor(overlayOpacity, overlayOpacity, overlayOpacity, 255));
for (int i = 5; i < width; i++) {
bg->setPixel(i, y, bgColor);
}
for (int i = 0; i <= 10; i++) {
const quint8 alpha = i == 0 ? 71 : qRound(255 * qreal(i) / 10);
{
QColor c(layerColor);
c.setAlpha(alpha);
layer->setPixel(7 + i, y, c);
}
{
QColor c(overlayColor);
c.setAlpha(alpha);
overlay->setPixel(7 + i, y, c);
}
}
y++;
}
}
}
const QRect rc = bg->exactBounds() | layer->exactBounds();
KIS_DUMP_DEVICE_2(layer, rc, "00_layer", "dd");
KIS_DUMP_DEVICE_2(overlay, rc, "01_overlay", "dd");
KIS_DUMP_DEVICE_2(bg, rc, "02_bg", "dd");
KisPaintDeviceSP originalBg = new KisPaintDevice(*bg);
KisSelectionSP selection = new KisSelection();
KisLsUtils::selectionFromAlphaChannel(layer, selection, rc);
{
KisSequentialIterator it(layer, rc);
while (it.nextPixel()) {
cs->setOpacity(it.rawData(), quint8(255), 1);
}
}
{
KisSequentialIterator it(overlay, rc);
while (it.nextPixel()) {
cs->setOpacity(it.rawData(), quint8(255), 1);
}
}
KisPainter painter(bg);
painter.setOpacity(layerOpacity);
painter.setCompositeOp(COMPOSITE_OVER);
painter.bitBlt(rc.topLeft(), layer, rc);
painter.setOpacity(overlayOpacity);
painter.setCompositeOp(COMPOSITE_ADD);
painter.bitBlt(rc.topLeft(), overlay, rc);
KIS_DUMP_DEVICE_2(bg, rc, "03_result", "dd");
KisPainter bgPainter(originalBg);
bgPainter.setCompositeOp(COMPOSITE_COPY);
bgPainter.setSelection(selection);
bgPainter.bitBlt(rc.topLeft(), bg, rc);
KIS_DUMP_DEVICE_2(originalBg, rc, "04_knockout", "dd");
}
QTEST_MAIN(KisLayerStyleProjectionPlaneTest)
diff --git a/libs/image/tests/kis_layer_test.cpp b/libs/image/tests/kis_layer_test.cpp
index 2c5cf8f97e..7a743d7647 100644
--- a/libs/image/tests/kis_layer_test.cpp
+++ b/libs/image/tests/kis_layer_test.cpp
@@ -1,315 +1,316 @@
/*
* 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 "kis_layer_test.h"
#include <QTest>
#include <QRect>
#include <QIcon>
#include <QBitArray>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include "kis_paint_device.h"
#include "kis_selection.h"
#include "kis_filter_mask.h"
#include "kis_transparency_mask.h"
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
+#include <KisGlobalResourcesInterface.h>
void KisLayerTest::testCreation()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "layer test");
image->waitForDone();
KisLayerSP layer = new TestLayer(image, "test", OPACITY_OPAQUE_U8);
QCOMPARE(layer->name(), QString("test"));
QCOMPARE(layer->opacity(), OPACITY_OPAQUE_U8);
QCOMPARE(layer->image().data(), image.data());
QCOMPARE(layer->colorSpace(), image->colorSpace());
QCOMPARE(layer->visible(), true);
QCOMPARE(layer->userLocked(), false);
QCOMPARE(layer->temporary(), false);
image->addNode(layer, image->rootLayer());
QBitArray channels(4);
channels.fill(true);
channels.setBit(1, false);
layer->setChannelFlags(channels);
QVERIFY(layer->channelFlags().count() == 4);
QCOMPARE(layer->channelFlags().at(0), true);
QCOMPARE(layer->channelFlags().at(1), false);
QCOMPARE(layer->channelFlags().at(2), true);
QCOMPARE(layer->channelFlags().at(3), true);
layer->setOpacity(OPACITY_TRANSPARENT_U8);
QCOMPARE(layer->opacity(), OPACITY_TRANSPARENT_U8);
layer->setPercentOpacity(100);
QCOMPARE(layer->opacity(), OPACITY_OPAQUE_U8);
layer->setPercentOpacity(0);
QCOMPARE(layer->opacity(), OPACITY_TRANSPARENT_U8);
}
void KisLayerTest::testOrdering()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "layer test");
image->waitForDone();
KisLayerSP layer1 = new TestLayer(image, "layer1", OPACITY_OPAQUE_U8);
KisLayerSP layer2 = new TestLayer(image, "layer2", OPACITY_OPAQUE_U8);
KisLayerSP layer3 = new TestLayer(image, "layer3", OPACITY_OPAQUE_U8);
QVERIFY(layer1->name() == "layer1");
QVERIFY(layer2->name() == "layer2");
QVERIFY(layer3->name() == "layer3");
/*
+---------+
| layer 2 |
| layer 3 |
| layer 1 |
|root |
+---------+
*/
QVERIFY(image->addNode(layer1, image->rootLayer()));
QVERIFY(image->addNode(layer2, image->rootLayer()));
QVERIFY(image->addNode(layer3, image->rootLayer(), layer1));
QCOMPARE((int) image->nlayers(), 4);
QVERIFY(layer1->parent() == image->root());
QVERIFY(layer2->parent() == image->root());
QVERIFY(layer3->parent() == image->root());
QVERIFY(image->rootLayer()->firstChild() == layer1.data());
QVERIFY(image->rootLayer()->lastChild() == layer2.data());
QVERIFY(image->rootLayer()->at(0) == layer1.data());
QVERIFY(image->rootLayer()->at(1) == layer3.data());
QVERIFY(image->rootLayer()->at(2) == layer2.data());
QVERIFY(image->rootLayer()->index(layer1) == 0);
QVERIFY(image->rootLayer()->index(layer3) == 1);
QVERIFY(image->rootLayer()->index(layer2) == 2);
QVERIFY(layer3->prevSibling() == layer1.data());
QVERIFY(layer2->prevSibling() == layer3.data());
QVERIFY(layer1->prevSibling() == 0);
QVERIFY(layer3->nextSibling() == layer2.data());
QVERIFY(layer2->nextSibling() == 0);
QVERIFY(layer1->nextSibling() == layer3.data());
/*
+---------+
| layer 3 |
| layer 2 |
| layer 1 |
|root |
+---------+
*/
QVERIFY(image->moveNode(layer2, image->rootLayer(), layer1));
QVERIFY(image->rootLayer()->at(0) == layer1.data());
QVERIFY(image->rootLayer()->at(1) == layer2.data());
QVERIFY(image->rootLayer()->at(2) == layer3.data());
QVERIFY(image->rootLayer()->firstChild() == layer1.data());
QVERIFY(image->rootLayer()->lastChild() == layer3.data());
QVERIFY(image->rootLayer()->index(layer1) == 0);
QVERIFY(image->rootLayer()->index(layer2) == 1);
QVERIFY(image->rootLayer()->index(layer3) == 2);
QVERIFY(layer3->prevSibling() == layer2.data());
QVERIFY(layer2->prevSibling() == layer1.data());
QVERIFY(layer1->prevSibling() == 0);
QVERIFY(layer3->nextSibling() == 0);
QVERIFY(layer2->nextSibling() == layer3.data());
QVERIFY(layer1->nextSibling() == layer2.data());
}
void KisLayerTest::testMoveNode()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "layer test");
image->waitForDone();
KisLayerSP node1 = new TestLayer(image, "layer1", OPACITY_OPAQUE_U8);
KisLayerSP node2 = new TestLayer(image, "layer2", OPACITY_OPAQUE_U8);
KisLayerSP node3 = new TestLayer(image, "layer3", OPACITY_OPAQUE_U8);
node1->setName("node1");
node2->setName("node2");
node3->setName("node3");
QVERIFY(image->addNode(node1));
QVERIFY(image->addNode(node2));
QVERIFY(image->addNode(node3));
QVERIFY(image->root()->at(0) == node1.data());
QVERIFY(image->root()->at(1) == node2.data());
QVERIFY(image->root()->at(2) == node3.data());
QVERIFY(image->moveNode(node3, image->root(), node1));
QVERIFY(image->root()->at(0) == node1.data());
QVERIFY(image->root()->at(1) == node3.data());
QVERIFY(image->root()->at(2) == node2.data());
}
void KisLayerTest::testMoveLayer()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "layer test");
image->waitForDone();
KisLayerSP node1 = new TestLayer(image, "layer1", OPACITY_OPAQUE_U8);
KisLayerSP node2 = new TestLayer(image, "layer2", OPACITY_OPAQUE_U8);
KisLayerSP node3 = new TestLayer(image, "layer3", OPACITY_OPAQUE_U8);
node1->setName("node1");
node2->setName("node2");
node3->setName("node3");
QVERIFY(image->addNode(node1));
QVERIFY(image->addNode(node2));
QVERIFY(image->addNode(node3));
QVERIFY(image->root()->at(0) == node1.data());
QVERIFY(image->root()->at(1) == node2.data());
QVERIFY(image->root()->at(2) == node3.data());
QVERIFY(image->moveNode(node3, image->rootLayer(), node1));
QVERIFY(image->root()->at(0) == node1.data());
QVERIFY(image->root()->at(1) == node3.data());
QVERIFY(image->root()->at(2) == node2.data());
}
/*
+----------+
|root |
| paint 1 |
| fmask2 |
| fmask1 |
+----------+
*/
void KisLayerTest::testMasksChangeRect()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
KisFilterMaskSP filterMask1 = new KisFilterMask();
KisFilterMaskSP filterMask2 = new KisFilterMask();
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
- KisFilterConfigurationSP configuration1 = filter->defaultConfiguration();
- KisFilterConfigurationSP configuration2 = filter->defaultConfiguration();
+ KisFilterConfigurationSP configuration1 = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
+ KisFilterConfigurationSP configuration2 = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
- filterMask1->setFilter(configuration1);
- filterMask2->setFilter(configuration2);
+ filterMask1->setFilter(configuration1->cloneWithResourcesSnapshot());
+ filterMask2->setFilter(configuration2->cloneWithResourcesSnapshot());
image->addNode(filterMask1, paintLayer1);
image->addNode(filterMask2, paintLayer1);
QVERIFY(paintLayer1->hasEffectMasks());
QRect testRect(10, 10, 100, 100);
QRect resultRect;
resultRect = paintLayer1->changeRect(testRect, KisNode::N_FILTHY);
QVERIFY2(resultRect == QRect(0, 0, 120, 120),
"KisNode::N_FILTHY node should take masks into account");
resultRect = paintLayer1->changeRect(testRect, KisNode::N_ABOVE_FILTHY);
QVERIFY2(resultRect == testRect,
"KisNode::N_ABOVE_FILTHY node should NOT take "
"masks into account");
/**
* KisNode::N_BELOW_FILTHY, KisNode::N_FILTHY_PROJECTION
* should not be use by the caller, because the walker
* should not visit these node on a forward way.
* So the behavior here is undefined.
*
* resultRect = paintLayer1->changeRect(testRect, KisNode::N_BELOW_FILTHY);
* resultRect = paintLayer1->changeRect(testRect, KisNode::N_FILTHY_PROJECTION);
*/
}
void KisLayerTest::testMoveLayerWithMaskThreaded()
{
/**
* This test ensures that the layer's original() can be moved
* while its projection is still being updated
*/
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 2000, 2000, colorSpace, "walker test");
KisLayerSP paintLayer = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
image->addNode(paintLayer, image->rootLayer());
paintLayer->paintDevice()->fill(image->bounds(), KoColor(Qt::black, colorSpace));
KisTransparencyMaskSP transpMask = new KisTransparencyMask();
transpMask->initSelection(paintLayer);
image->addNode(transpMask, paintLayer);
for(int i = 0; i < 100; i++) {
paintLayer->setDirty();
QTest::qSleep(1 + (qrand() & 63));
paintLayer->setX((i*67) % 1873);
paintLayer->setY((i*23) % 1873);
}
}
QTEST_MAIN(KisLayerTest)
diff --git a/libs/image/tests/kis_paint_device_test.cpp b/libs/image/tests/kis_paint_device_test.cpp
index 7372e2f59d..ff35b3f3cc 100644
--- a/libs/image/tests/kis_paint_device_test.cpp
+++ b/libs/image/tests/kis_paint_device_test.cpp
@@ -1,2376 +1,2376 @@
/*
* 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 "kis_paint_device_test.h"
#include <QTest>
-#include <QTime>
+#include <QElapsedTimer>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoStore.h>
#include "kis_paint_device_writer.h"
#include "kis_painter.h"
#include "kis_types.h"
#include "kis_paint_device.h"
#include "kis_layer.h"
#include "kis_paint_layer.h"
#include "kis_selection.h"
#include "kis_datamanager.h"
#include "kis_global.h"
#include "testutil.h"
#include "kis_transaction.h"
#include "kis_image.h"
#include "config-limit-long-tests.h"
#include "kistest.h"
class KisFakePaintDeviceWriter : public KisPaintDeviceWriter {
public:
KisFakePaintDeviceWriter(KoStore *store)
: m_store(store)
{
}
bool write(const QByteArray &data) override {
return (m_store->write(data) == data.size());
}
bool write(const char* data, qint64 length) override {
return (m_store->write(data, length) == length);
}
KoStore *m_store;
};
void KisPaintDeviceTest::testCreation()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QVERIFY(dev->objectName().isEmpty());
dev = new KisPaintDevice(cs);
QVERIFY(*dev->colorSpace() == *cs);
QVERIFY(dev->x() == 0);
QVERIFY(dev->y() == 0);
QVERIFY(dev->pixelSize() == cs->pixelSize());
QVERIFY(dev->channelCount() == cs->channelCount());
QVERIFY(dev->dataManager() != 0);
KisImageSP image = new KisImage(0, 1000, 1000, cs, "merge test");
KisPaintLayerSP layer = new KisPaintLayer(image, "bla", 125);
dev = new KisPaintDevice(layer.data(), cs);
QVERIFY(*dev->colorSpace() == *cs);
QVERIFY(dev->x() == 0);
QVERIFY(dev->y() == 0);
QVERIFY(dev->pixelSize() == cs->pixelSize());
QVERIFY(dev->channelCount() == cs->channelCount());
QVERIFY(dev->dataManager() != 0);
// Let the layer go out of scope and see what happens
{
KisPaintLayerSP l2 = new KisPaintLayer(image, "blabla", 250);
dev = new KisPaintDevice(l2.data(), cs);
}
}
void KisPaintDeviceTest::testStore()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KoStore * readStore =
KoStore::createStore(QString(FILES_DATA_DIR) + QDir::separator() + "store_test.kra", KoStore::Read);
readStore->open("built image/layers/layer0");
QVERIFY(dev->read(readStore->device()));
readStore->close();
delete readStore;
QVERIFY(dev->exactBounds() == QRect(0, 0, 100, 100));
KoStore * writeStore =
KoStore::createStore(QString(FILES_OUTPUT_DIR) + QDir::separator() + "store_test_out.kra", KoStore::Write);
KisFakePaintDeviceWriter fakeWriter(writeStore);
writeStore->open("built image/layers/layer0");
QVERIFY(dev->write(fakeWriter));
writeStore->close();
delete writeStore;
KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
readStore =
KoStore::createStore(QString(FILES_OUTPUT_DIR) + QDir::separator() + "store_test_out.kra", KoStore::Read);
readStore->open("built image/layers/layer0");
QVERIFY(dev2->read(readStore->device()));
readStore->close();
delete readStore;
QVERIFY(dev2->exactBounds() == QRect(0, 0, 100, 100));
QPoint pt;
if (!TestUtil::comparePaintDevices(pt, dev, dev2)) {
QFAIL(QString("Loading a saved image is not pixel perfect, first different pixel: %1,%2 ").arg(pt.x()).arg(pt.y()).toLatin1());
}
}
void KisPaintDeviceTest::testGeometry()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
quint8* pixel = new quint8[cs->pixelSize()];
cs->fromQColor(Qt::white, pixel);
dev->fill(0, 0, 512, 512, pixel);
QCOMPARE(dev->exactBounds(), QRect(0, 0, 512, 512));
QCOMPARE(dev->extent(), QRect(0, 0, 512, 512));
dev->moveTo(10, 10);
QCOMPARE(dev->exactBounds(), QRect(10, 10, 512, 512));
QCOMPARE(dev->extent(), QRect(10, 10, 512, 512));
dev->crop(50, 50, 50, 50);
QCOMPARE(dev->exactBounds(), QRect(50, 50, 50, 50));
QCOMPARE(dev->extent(), QRect(10, 10, 128, 128));
QColor c;
dev->clear(QRect(50, 50, 50, 50));
dev->pixel(80, 80, &c);
QVERIFY(c.alpha() == OPACITY_TRANSPARENT_U8);
dev->fill(0, 0, 512, 512, pixel);
dev->pixel(80, 80, &c);
QVERIFY(c == Qt::white);
QVERIFY(c.alpha() == OPACITY_OPAQUE_U8);
dev->clear();
dev->pixel(80, 80, &c);
QVERIFY(c.alpha() == OPACITY_TRANSPARENT_U8);
QVERIFY(dev->extent().isEmpty());
QVERIFY(dev->exactBounds().isEmpty());
}
void KisPaintDeviceTest::testClear()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QVERIFY(!dev->extent().isValid());
QVERIFY(!dev->exactBounds().isValid());
dev->clear();
QVERIFY(!dev->extent().isValid());
QVERIFY(!dev->exactBounds().isValid());
QRect fillRect1(50, 100, 150, 100);
dev->fill(fillRect1, KoColor(Qt::red, cs));
QCOMPARE(dev->extent(), QRect(0, 64, 256, 192));
QCOMPARE(dev->exactBounds(), fillRect1);
dev->clear(QRect(100, 100, 100, 100));
QCOMPARE(dev->extent(), QRect(0, 64, 256, 192));
QCOMPARE(dev->exactBounds(), QRect(50, 100, 50, 100));
dev->clear();
QVERIFY(!dev->extent().isValid());
QVERIFY(!dev->exactBounds().isValid());
}
void KisPaintDeviceTest::testCrop()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
quint8* pixel = new quint8[cs->pixelSize()];
cs->fromQColor(Qt::white, pixel);
dev->fill(-14, 8, 433, 512, pixel);
QVERIFY(dev->exactBounds() == QRect(-14, 8, 433, 512));
// Crop inside
dev->crop(50, 50, 150, 150);
QVERIFY(dev->exactBounds() == QRect(50, 50, 150, 150));
// Crop outside, pd should not grow
dev->crop(0, 0, 1000, 1000);
QVERIFY(dev->exactBounds() == QRect(50, 50, 150, 150));
}
void KisPaintDeviceTest::testRoundtripReadWrite()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png");
dev->convertFromQImage(image, 0);
quint8* bytes = new quint8[cs->pixelSize() * image.width() * image.height()];
memset(bytes, 0, image.width() * image.height() * dev->pixelSize());
dev->readBytes(bytes, image.rect());
KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
dev2->writeBytes(bytes, image.rect());
QVERIFY(dev2->exactBounds() == image.rect());
dev2->convertToQImage(0, 0, 0, image.width(), image.height()).save("readwrite.png");
QPoint pt;
if (!TestUtil::comparePaintDevices(pt, dev, dev2)) {
QFAIL(QString("Failed round trip using readBytes and writeBytes, first different pixel: %1,%2 ").arg(pt.x()).arg(pt.y()).toLatin1());
}
}
void logFailure(const QString & reason, const KoColorSpace * srcCs, const KoColorSpace * dstCs)
{
QString profile1("no profile");
QString profile2("no profile");
if (srcCs->profile())
profile1 = srcCs->profile()->name();
if (dstCs->profile())
profile2 = dstCs->profile()->name();
QWARN(QString("Failed %1 %2 -> %3 %4 %5")
.arg(srcCs->name())
.arg(profile1)
.arg(dstCs->name())
.arg(profile2)
.arg(reason)
.toLatin1());
}
void KisPaintDeviceTest::testColorSpaceConversion()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png");
const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->rgb8();
const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->lab16();
KisPaintDeviceSP dev = new KisPaintDevice(srcCs);
dev->convertFromQImage(image, 0);
dev->moveTo(10, 10); // Unalign with tile boundaries
KUndo2Command* cmd = new KUndo2Command();
dev->convertTo(dstCs,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags(),
cmd);
QCOMPARE(dev->exactBounds(), QRect(10, 10, image.width(), image.height()));
QCOMPARE(dev->pixelSize(), dstCs->pixelSize());
QVERIFY(*dev->colorSpace() == *dstCs);
cmd->redo();
cmd->undo();
QCOMPARE(dev->exactBounds(), QRect(10, 10, image.width(), image.height()));
QCOMPARE(dev->pixelSize(), srcCs->pixelSize());
QVERIFY(*dev->colorSpace() == *srcCs);
delete cmd;
}
void KisPaintDeviceTest::testRoundtripConversion()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
QImage result = dev->convertToQImage(0, 0, 0, 640, 441);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("kis_paint_device_test_test_roundtrip_qimage.png");
result.save("kis_paint_device_test_test_roundtrip_result.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisPaintDeviceTest::testFastBitBlt()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dstDev = new KisPaintDevice(cs);
KisPaintDeviceSP srcDev = new KisPaintDevice(cs);
srcDev->convertFromQImage(image, 0);
QRect cloneRect(100,100,200,200);
QPoint errpoint;
QVERIFY(dstDev->fastBitBltPossible(srcDev));
dstDev->fastBitBlt(srcDev, cloneRect);
QImage srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
cloneRect.width(), cloneRect.height());
QImage dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
cloneRect.width(), cloneRect.height());
if (!TestUtil::compareQImages(errpoint, srcImage, dstImage)) {
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
// Test Rough version
dstDev->clear();
dstDev->fastBitBltRough(srcDev, cloneRect);
srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
cloneRect.width(), cloneRect.height());
dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
cloneRect.width(), cloneRect.height());
if (!TestUtil::compareQImages(errpoint, srcImage, dstImage)) {
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
srcDev->moveTo(10,10);
QVERIFY(!dstDev->fastBitBltPossible(srcDev));
}
void KisPaintDeviceTest::testMakeClone()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP srcDev = new KisPaintDevice(cs);
srcDev->convertFromQImage(image, 0);
srcDev->moveTo(10,10);
const KoColorSpace * weirdCS = KoColorSpaceRegistry::instance()->lab16();
KisPaintDeviceSP dstDev = new KisPaintDevice(weirdCS);
dstDev->moveTo(1000,1000);
QVERIFY(!dstDev->fastBitBltPossible(srcDev));
QRect cloneRect(100,100,200,200);
QPoint errpoint;
dstDev->makeCloneFrom(srcDev, cloneRect);
QVERIFY(*dstDev->colorSpace() == *srcDev->colorSpace());
QCOMPARE(dstDev->pixelSize(), srcDev->pixelSize());
QCOMPARE(dstDev->x(), srcDev->x());
QCOMPARE(dstDev->y(), srcDev->y());
QCOMPARE(dstDev->exactBounds(), cloneRect);
QImage srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
cloneRect.width(), cloneRect.height());
QImage dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
cloneRect.width(), cloneRect.height());
if (!TestUtil::compareQImages(errpoint, dstImage, srcImage)) {
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisPaintDeviceTest::testThumbnail()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
{
KisPaintDeviceSP thumb = dev->createThumbnailDevice(50, 50);
QRect rc = thumb->exactBounds();
QVERIFY(rc.width() <= 50);
QVERIFY(rc.height() <= 50);
}
{
QImage thumb = dev->createThumbnail(50, 50);
QVERIFY(!thumb.isNull());
QVERIFY(thumb.width() <= 50);
QVERIFY(thumb.height() <= 50);
image.save("kis_paint_device_test_test_thumbnail.png");
}
}
void KisPaintDeviceTest::testThumbnailDeviceWithOffset()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
dev->setX(10);
dev->setY(10);
QImage thumb = dev->createThumbnail(640,441,QRect(10,10,640,441));
image.save("oring.png");
thumb.save("thumb.png");
QPoint pt;
QVERIFY(TestUtil::compareQImages(pt, thumb, image));
}
void KisPaintDeviceTest::testCaching()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
quint8* whitePixel = new quint8[cs->pixelSize()];
cs->fromQColor(Qt::white, whitePixel);
quint8* blackPixel = new quint8[cs->pixelSize()];
cs->fromQColor(Qt::black, blackPixel);
dev->fill(0, 0, 512, 512, whitePixel);
QImage thumb1 = dev->createThumbnail(50, 50);
QRect exactBounds1 = dev->exactBounds();
dev->fill(0, 0, 768, 768, blackPixel);
QImage thumb2 = dev->createThumbnail(50, 50);
QRect exactBounds2 = dev->exactBounds();
dev->moveTo(10, 10);
QImage thumb3 = dev->createThumbnail(50, 50);
QRect exactBounds3 = dev->exactBounds();
dev->crop(50, 50, 50, 50);
QImage thumb4 = dev->createThumbnail(50, 50);
QRect exactBounds4 = dev->exactBounds();
QVERIFY(thumb1 != thumb2);
QVERIFY(thumb2 == thumb3); // Cache miss, but image is the same
QVERIFY(thumb3 != thumb4);
QVERIFY(thumb4 != thumb1);
QCOMPARE(exactBounds1, QRect(0,0,512,512));
QCOMPARE(exactBounds2, QRect(0,0,768,768));
QCOMPARE(exactBounds3, QRect(10,10,768,768));
QCOMPARE(exactBounds4, QRect(50,50,50,50));
}
void KisPaintDeviceTest::testRegion()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
quint8* whitePixel = new quint8[cs->pixelSize()];
cs->fromQColor(Qt::white, whitePixel);
dev->fill(0, 0, 10, 10, whitePixel);
dev->fill(70, 70, 10, 10, whitePixel);
dev->fill(129, 0, 10, 10, whitePixel);
dev->fill(0, 1030, 10, 10, whitePixel);
QVector<QRect> referenceRegion;
referenceRegion += QRect(0,0,64,64);
referenceRegion += QRect(64,64,64,64);
referenceRegion += QRect(128,0,64,64);
referenceRegion += QRect(0,1024,64,64);
QCOMPARE(dev->exactBounds(), QRect(0,0,139,1040));
QCOMPARE(dev->region(), KisRegion(referenceRegion));
}
void KisPaintDeviceTest::testPixel()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QColor c = Qt::red;
quint8 opacity = 125;
c.setAlpha(opacity);
dev->setPixel(5, 5, c);
QColor c2;
dev->pixel(5, 5, &c2);
QVERIFY(c == c2);
QVERIFY(opacity == c2.alpha());
}
void KisPaintDeviceTest::testPlanarReadWrite()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
quint8* pixel = new quint8[cs->pixelSize()];
cs->fromQColor(QColor(255, 200, 155, 100), pixel);
dev->fill(0, 0, 5000, 5000, pixel);
delete[] pixel;
QColor c1;
dev->pixel(5, 5, &c1);
QVector<quint8*> planes = dev->readPlanarBytes(500, 500, 100, 100);
QVector<quint8*> swappedPlanes;
QCOMPARE((int)planes.size(), (int)dev->channelCount());
for (int i = 0; i < 100*100; i++) {
// BGRA encoded
QVERIFY(planes.at(2)[i] == 255);
QVERIFY(planes.at(1)[i] == 200);
QVERIFY(planes.at(0)[i] == 155);
QVERIFY(planes.at(3)[i] == 100);
}
for (uint i = 1; i < dev->channelCount() + 1; ++i) {
swappedPlanes.append(planes[dev->channelCount() - i]);
}
dev->writePlanarBytes(swappedPlanes, 0, 0, 100, 100);
dev->convertToQImage(0, 0, 0, 1000, 1000).save("planar.png");
dev->pixel(5, 5, &c1);
QVERIFY(c1.red() == 200);
QVERIFY(c1.green() == 255);
QVERIFY(c1.blue() == 100);
QVERIFY(c1.alpha() == 155);
dev->pixel(75, 50, &c1);
QVERIFY(c1.red() == 200);
QVERIFY(c1.green() == 255);
QVERIFY(c1.blue() == 100);
QVERIFY(c1.alpha() == 155);
// check if one of the planes is Null.
Q_ASSERT(planes.size() == 4);
delete [] planes[2];
planes[2] = 0;
dev->writePlanarBytes(planes, 0, 0, 100, 100);
dev->convertToQImage(0, 0, 0, 1000, 1000).save("planar_noR.png");
dev->pixel(75, 50, &c1);
QCOMPARE(c1.red(), 200);
QCOMPARE(c1.green(), 200);
QCOMPARE(c1.blue(), 155);
QCOMPARE(c1.alpha(), 100);
QVector<quint8*>::iterator i;
for (i = planes.begin(); i != planes.end(); ++i)
{
delete [] *i;
}
swappedPlanes.clear();
}
void KisPaintDeviceTest::testBltPerformance()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP fdev = new KisPaintDevice(cs);
fdev->convertFromQImage(image, 0);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data());
- QTime t;
+ QElapsedTimer t;
t.start();
int x;
#ifdef LIMIT_LONG_TESTS
int steps = 10;
#else
int steps = 1000;
#endif
for (x = 0; x < steps; ++x) {
KisPainter gc(dev);
gc.bitBlt(QPoint(0, 0), fdev, image.rect());
}
dbgKrita << x
<< "blits"
<< " done in "
<< t.elapsed()
<< "ms";
}
void KisPaintDeviceTest::testDeviceDuplication()
{
QRect fillRect(0,0,64,64);
quint8 fillPixel[4]={255,255,255,255};
QRect clearRect(10,10,20,20);
QImage referenceImage;
QImage resultImage;
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP device = new KisPaintDevice(cs);
// dbgKrita<<"FILLING";
device->fill(fillRect.left(), fillRect.top(),
fillRect.width(), fillRect.height(),fillPixel);
referenceImage = device->convertToQImage(0);
KisTransaction transaction1(device);
// dbgKrita<<"CLEARING";
device->clear(clearRect);
transaction1.revert();
resultImage = device->convertToQImage(0);
QVERIFY(resultImage == referenceImage);
KisPaintDeviceSP clone = new KisPaintDevice(*device);
KisTransaction transaction(clone);
// dbgKrita<<"CLEARING";
clone->clear(clearRect);
transaction.revert();
resultImage = clone->convertToQImage(0);
QVERIFY(resultImage == referenceImage);
}
void KisPaintDeviceTest::testTranslate()
{
QRect fillRect(0,0,64,64);
quint8 fillPixel[4]={255,255,255,255};
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP device = new KisPaintDevice(cs);
device->fill(fillRect.left(), fillRect.top(),
fillRect.width(), fillRect.height(),fillPixel);
device->setX(-10);
device->setY(10);
QCOMPARE(device->exactBounds(), QRect(-10,10,64,64));
QCOMPARE(device->extent(), QRect(-10,10,64,64));
}
void KisPaintDeviceTest::testOpacity()
{
// blt a semi-transparent image on a white paint device
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP fdev = new KisPaintDevice(cs);
fdev->convertFromQImage(image, 0);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data());
KisPainter gc(dev);
gc.bitBlt(QPoint(0, 0), fdev, image.rect());
QImage result = dev->convertToQImage(0, 0, 0, 640, 441);
QImage checkResult(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent_result.png");
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, checkResult, result, 1)) {
checkResult.save("kis_paint_device_test_test_blt_fixed_opactiy_expected.png");
result.save("kis_paint_device_test_test_blt_fixed_opacity_result.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisPaintDeviceTest::testExactBoundsWeirdNullAlphaCase()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QVERIFY(dev->exactBounds().isEmpty());
dev->fill(QRect(10,10,10,10), KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), QRect(10,10,10,10));
const quint8 weirdPixelData[4] = {0,10,0,0};
KoColor weirdColor(weirdPixelData, cs);
dev->setPixel(6,6,weirdColor);
// such weird pixels should not change our opinion about
// device's size
QCOMPARE(dev->exactBounds(), QRect(10,10,10,10));
}
void KisPaintDeviceTest::benchmarkExactBoundsNullDefaultPixel()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QVERIFY(dev->exactBounds().isEmpty());
QRect fillRect(60,60, 1930, 1930);
dev->fill(fillRect, KoColor(Qt::white, cs));
QRect measuredRect;
QBENCHMARK {
// invalidate the cache
dev->setDirty();
measuredRect = dev->exactBounds();
}
QCOMPARE(measuredRect, fillRect);
}
void KisPaintDeviceTest::testAmortizedExactBounds()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QVERIFY(dev->exactBounds().isEmpty());
QRect fillRect(60,60, 833, 833);
QRect extent(0,0,896,896);
dev->fill(fillRect, KoColor(Qt::white, cs));
QEXPECT_FAIL("", "Expecting the extent, we somehow get the fillrect", Continue);
QCOMPARE(dev->exactBounds(), extent);
QCOMPARE(dev->extent(), extent);
QCOMPARE(dev->exactBoundsAmortized(), fillRect);
dev->setDirty();
QEXPECT_FAIL("", "Expecting the fillRect, we somehow get the extent", Continue);
QCOMPARE(dev->exactBoundsAmortized(), fillRect);
dev->setDirty();
QCOMPARE(dev->exactBoundsAmortized(), extent);
QTest::qSleep(1100);
QEXPECT_FAIL("", "Expecting the fillRect, we somehow get the extent", Continue);
QCOMPARE(dev->exactBoundsAmortized(), fillRect);
}
void KisPaintDeviceTest::testNonDefaultPixelArea()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QVERIFY(dev->exactBounds().isEmpty());
QVERIFY(dev->nonDefaultPixelArea().isEmpty());
KoColor defPixel(Qt::red, cs);
dev->setDefaultPixel(defPixel);
QCOMPARE(dev->exactBounds(), KisDefaultBounds::infiniteRect);
QVERIFY(dev->nonDefaultPixelArea().isEmpty());
QRect fillRect(10,11,18,14);
dev->fill(fillRect, KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), KisDefaultBounds::infiniteRect);
QCOMPARE(dev->nonDefaultPixelArea(), fillRect);
// non-default pixel variant should also handle weird pixels
const quint8 weirdPixelData[4] = {0,10,0,0};
KoColor weirdColor(weirdPixelData, cs);
dev->setPixel(100,100,weirdColor);
// such weird pixels should not change our opinion about
// device's size
QCOMPARE(dev->exactBounds(), KisDefaultBounds::infiniteRect);
QCOMPARE(dev->nonDefaultPixelArea(), fillRect | QRect(100,100,1,1));
}
void KisPaintDeviceTest::testExactBoundsNonTransparent()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 1000, 1000, cs, "merge test");
KisPaintLayerSP layer = new KisPaintLayer(image, "bla", 125);
KisPaintDeviceSP dev = layer->paintDevice();
QVERIFY(dev);
QRect imageRect(0,0,1000,1000);
KoColor defPixel(Qt::red, cs);
dev->setDefaultPixel(defPixel);
QCOMPARE(dev->exactBounds(), imageRect);
QVERIFY(dev->nonDefaultPixelArea().isEmpty());
KoColor fillPixel(Qt::white, cs);
dev->fill(imageRect, KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), imageRect);
QCOMPARE(dev->nonDefaultPixelArea(), imageRect);
dev->fill(QRect(1000,0, 1, 1000), KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), QRect(0,0,1001,1000));
QCOMPARE(dev->nonDefaultPixelArea(), QRect(0,0,1001,1000));
dev->fill(QRect(0,1000, 1000, 1), KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), QRect(0,0,1001,1001));
QCOMPARE(dev->nonDefaultPixelArea(), QRect(0,0,1001,1001));
dev->fill(QRect(0,-1, 1000, 1), KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), QRect(0,-1,1001,1002));
QCOMPARE(dev->nonDefaultPixelArea(), QRect(0,-1,1001,1002));
dev->fill(QRect(-1,0, 1, 1000), KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), QRect(-1,-1,1002,1002));
QCOMPARE(dev->nonDefaultPixelArea(), QRect(-1,-1,1002,1002));
}
KisPaintDeviceSP createWrapAroundPaintDevice(const KoColorSpace *cs)
{
struct TestingDefaultBounds : public KisDefaultBoundsBase {
QRect bounds() const override {
return QRect(0,0,20,20);
}
bool wrapAroundMode() const override {
return true;
}
int currentLevelOfDetail() const override {
return 0;
}
int currentTime() const override {
return 0;
}
bool externalFrameActive() const override {
return false;
}
void * sourceCookie() const override {
return 0;
}
};
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KisDefaultBoundsBaseSP bounds = new TestingDefaultBounds();
dev->setDefaultBounds(bounds);
return dev;
}
void checkReadWriteRoundTrip(KisPaintDeviceSP dev,
const QRect &rc)
{
KisPaintDeviceSP deviceCopy = new KisPaintDevice(*dev.data());
int bufSize = rc.width() * rc.height() * dev->pixelSize();
QScopedArrayPointer<quint8> buf1(new quint8[bufSize]);
deviceCopy->readBytes(buf1.data(), rc);
deviceCopy->clear();
QVERIFY(deviceCopy->extent().isEmpty());
QScopedArrayPointer<quint8> buf2(new quint8[bufSize]);
deviceCopy->writeBytes(buf1.data(), rc);
deviceCopy->readBytes(buf2.data(), rc);
QVERIFY(!memcmp(buf1.data(), buf2.data(), bufSize));
}
void KisPaintDeviceTest::testReadBytesWrapAround()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
KoColor c1(Qt::red, cs);
KoColor c2(Qt::green, cs);
dev->setPixel(3, 3, c1);
dev->setPixel(18, 18, c2);
const int pixelSize = dev->pixelSize();
{
QRect readRect(10, 10, 20, 20);
QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
readRect.height() *
pixelSize]);
dev->readBytes(buf.data(), readRect);
//dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final1.png");
QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize));
checkReadWriteRoundTrip(dev, readRect);
}
{
// check weird case when the read rect is larger than wrap rect
QRect readRect(10, 10, 30, 30);
QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
readRect.height() *
pixelSize]);
dev->readBytes(buf.data(), readRect);
//dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final2.png");
QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize));
checkReadWriteRoundTrip(dev, readRect);
}
{
// even more large
QRect readRect(10, 10, 40, 40);
QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
readRect.height() *
pixelSize]);
dev->readBytes(buf.data(), readRect);
//dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final3.png");
QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (32 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (33 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 32) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 33) * pixelSize, c1.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (32 + readRect.width() * 32) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (33 + readRect.width() * 33) * pixelSize, c1.data(), pixelSize));
checkReadWriteRoundTrip(dev, readRect);
}
{
// check if the wrap rect contains the read rect entirely
QRect readRect(1, 1, 10, 10);
QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
readRect.height() *
pixelSize]);
dev->readBytes(buf.data(), readRect);
//dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final4.png");
QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize));
checkReadWriteRoundTrip(dev, readRect);
}
{
// check if the wrap happens only on vertical side of the rect
QRect readRect(1, 1, 29, 10);
QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
readRect.height() *
pixelSize]);
dev->readBytes(buf.data(), readRect);
//dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final5.png");
QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (21 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (22 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize));
checkReadWriteRoundTrip(dev, readRect);
}
{
// check if the wrap happens only on horizontal side of the rect
QRect readRect(1, 1, 10, 29);
QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
readRect.height() *
pixelSize]);
dev->readBytes(buf.data(), readRect);
//dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final6.png");
QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 21) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 22) * pixelSize, c1.data(), pixelSize));
checkReadWriteRoundTrip(dev, readRect);
}
}
#include "kis_random_accessor_ng.h"
void KisPaintDeviceTest::testWrappedRandomAccessor()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
KoColor c1(Qt::red, cs);
KoColor c2(Qt::green, cs);
dev->setPixel(3, 3, c1);
dev->setPixel(18, 18, c2);
const int pixelSize = dev->pixelSize();
int x;
int y;
x = 3;
y = 3;
KisRandomAccessorSP dstIt = dev->createRandomAccessorNG(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
x = 23;
y = 23;
dstIt->moveTo(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
x = 3;
y = 23;
dstIt->moveTo(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
x = 23;
y = 3;
dstIt->moveTo(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
x = -17;
y = 3;
dstIt->moveTo(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
x = 3;
y = -17;
dstIt->moveTo(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
x = -17;
y = -17;
dstIt->moveTo(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
}
#include "kis_iterator_ng.h"
static bool nextRowGeneral(KisHLineIteratorSP it, int y, const QRect &rc) {
it->nextRow();
return y < rc.height();
}
static bool nextRowGeneral(KisVLineIteratorSP it, int y, const QRect &rc) {
it->nextColumn();
return y < rc.width();
}
template <class T>
bool checkXY(const QPoint &pt, const QPoint &realPt) {
Q_UNUSED(pt);
Q_UNUSED(realPt);
return false;
}
template <>
bool checkXY<KisHLineIteratorSP>(const QPoint &pt, const QPoint &realPt) {
return pt == realPt;
}
template <>
bool checkXY<KisVLineIteratorSP>(const QPoint &pt, const QPoint &realPt) {
return pt.x() == realPt.y() && pt.y() == realPt.x();
}
#include <kis_wrapped_rect.h>
template <class T>
bool checkConseqPixels(int value, const QPoint &pt, const KisWrappedRect &wrappedRect) {
Q_UNUSED(value);
Q_UNUSED(pt);
Q_UNUSED(wrappedRect);
return false;
}
template <>
bool checkConseqPixels<KisHLineIteratorSP>(int value, const QPoint &pt, const KisWrappedRect &wrappedRect) {
int x = KisWrappedRect::xToWrappedX(pt.x(), wrappedRect.wrapRect());
int borderX = wrappedRect.originalRect().x() + wrappedRect.wrapRect().width();
int conseq = x >= borderX ? wrappedRect.wrapRect().right() - x + 1 : borderX - x;
conseq = qMin(conseq, wrappedRect.originalRect().right() - pt.x() + 1);
return value == conseq;
}
template <>
bool checkConseqPixels<KisVLineIteratorSP>(int value, const QPoint &pt, const KisWrappedRect &wrappedRect) {
Q_UNUSED(pt);
Q_UNUSED(wrappedRect);
return value == 1;
}
template <class IteratorSP>
IteratorSP createIterator(KisPaintDeviceSP dev, const QRect &rc) {
Q_UNUSED(dev);
Q_UNUSED(rc);
return 0;
}
template <>
KisHLineIteratorSP createIterator(KisPaintDeviceSP dev,
const QRect &rc) {
return dev->createHLineIteratorNG(rc.x(), rc.y(), rc.width());
}
template <>
KisVLineIteratorSP createIterator(KisPaintDeviceSP dev,
const QRect &rc) {
return dev->createVLineIteratorNG(rc.x(), rc.y(), rc.height());
}
template <class IteratorSP>
void testWrappedLineIterator(QString testName, const QRect &rect)
{
testName = QString("%1_%2_%3_%4_%5")
.arg(testName)
.arg(rect.x())
.arg(rect.y())
.arg(rect.width())
.arg(rect.height());
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
// test rect fits the wrap rect in both dimensions
IteratorSP it = createIterator<IteratorSP>(dev, rect);
int y = 0;
do {
int x = 0;
do {
quint8 *data = it->rawData();
data[0] = 10 * x;
data[1] = 10 * y;
data[2] = 0;
data[3] = 255;
x++;
} while (it->nextPixel());
} while (nextRowGeneral(it, ++y, rect));
QRect rc = dev->defaultBounds()->bounds() | dev->exactBounds();
QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QVERIFY(TestUtil::checkQImage(result, "paint_device_test", "wrapped_iterators", testName));
}
template <class IteratorSP>
void testWrappedLineIterator(const QString &testName)
{
testWrappedLineIterator<IteratorSP>(testName, QRect(10,10,20,20));
testWrappedLineIterator<IteratorSP>(testName, QRect(10,10,10,20));
testWrappedLineIterator<IteratorSP>(testName, QRect(10,10,20,10));
testWrappedLineIterator<IteratorSP>(testName, QRect(10,10,10,10));
testWrappedLineIterator<IteratorSP>(testName, QRect(0,0,20,20));
}
void KisPaintDeviceTest::testWrappedHLineIterator()
{
testWrappedLineIterator<KisHLineIteratorSP>("hline_iterator");
}
void KisPaintDeviceTest::testWrappedVLineIterator()
{
testWrappedLineIterator<KisVLineIteratorSP>("vline_iterator");
}
template <class IteratorSP>
void testWrappedLineIteratorReadMoreThanBounds(QString testName)
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
KisPaintDeviceSP dst = new KisPaintDevice(cs);
// fill device with a gradient
QRect bounds = dev->defaultBounds()->bounds();
for (int y = bounds.y(); y < bounds.y() + bounds.height(); y++) {
for (int x = bounds.x(); x < bounds.x() + bounds.width(); x++) {
QColor c((10 * x) % 255, (10 * y) % 255, 0, 255);
dev->setPixel(x, y, c);
}
}
// test rect doesn't fit the wrap rect in both dimensions
const QRect &rect(bounds.adjusted(-6,-6,8,8));
KisRandomAccessorSP dstIt = dst->createRandomAccessorNG(rect.x(), rect.y());
IteratorSP it = createIterator<IteratorSP>(dev, rect);
for (int y = rect.y(); y < rect.y() + rect.height(); y++) {
for (int x = rect.x(); x < rect.x() + rect.width(); x++) {
quint8 *data = it->rawData();
QVERIFY(checkConseqPixels<IteratorSP>(it->nConseqPixels(), QPoint(x, y), KisWrappedRect(rect, bounds)));
dstIt->moveTo(x, y);
memcpy(dstIt->rawData(), data, cs->pixelSize());
QVERIFY(checkXY<IteratorSP>(QPoint(it->x(), it->y()), QPoint(x,y)));
bool stepDone = it->nextPixel();
QCOMPARE(stepDone, x < rect.x() + rect.width() - 1);
}
if (!nextRowGeneral(it, y, rect)) break;
}
testName = QString("%1_%2_%3_%4_%5")
.arg(testName)
.arg(rect.x())
.arg(rect.y())
.arg(rect.width())
.arg(rect.height());
QRect rc = rect;
QImage result = dst->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QImage ref = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QVERIFY(TestUtil::checkQImage(result, "paint_device_test", "wrapped_iterators_huge", testName, 1));
}
void KisPaintDeviceTest::testWrappedHLineIteratorReadMoreThanBounds()
{
testWrappedLineIteratorReadMoreThanBounds<KisHLineIteratorSP>("hline_iterator");
}
void KisPaintDeviceTest::testWrappedVLineIteratorReadMoreThanBounds()
{
testWrappedLineIteratorReadMoreThanBounds<KisVLineIteratorSP>("vline_iterator");
}
void KisPaintDeviceTest::testMoveWrapAround()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
KoColor c1(Qt::red, cs);
KoColor c2(Qt::green, cs);
dev->setPixel(3, 3, c1);
dev->setPixel(18, 18, c2);
// QRect rc = dev->defaultBounds()->bounds();
//dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()).save("move0.png");
QCOMPARE(dev->exactBounds(), QRect(3,3,16,16));
dev->moveTo(QPoint(10,10));
QCOMPARE(dev->exactBounds(), QRect(8,8,6,6));
//dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()).save("move1.png");
}
#include "kis_lock_free_cache.h"
#define NUM_TYPES 3
// high-concurrency
#define NUM_CYCLES 500000
#define NUM_THREADS 4
struct TestingCache : KisLockFreeCache<int> {
int calculateNewValue() const override {
return m_realValue;
}
QAtomicInt m_realValue;
};
class CacheStressJob : public QRunnable
{
public:
CacheStressJob(TestingCache &cache)
: m_cache(cache),
m_oldValue(0)
{
}
void run() override {
for(qint32 i = 0; i < NUM_CYCLES; i++) {
qint32 type = i % NUM_TYPES;
switch(type) {
case 0:
m_cache.m_realValue.ref();
m_oldValue = m_cache.m_realValue;
m_cache.invalidate();
break;
case 1:
{
int newValue = m_cache.getValue();
Q_ASSERT(newValue >= m_oldValue);
Q_UNUSED(newValue);
}
break;
case 3:
QTest::qSleep(3);
break;
}
}
}
private:
TestingCache &m_cache;
int m_oldValue;
};
void KisPaintDeviceTest::testCacheState()
{
TestingCache cache;
QList<CacheStressJob*> jobsList;
CacheStressJob *job;
for(qint32 i = 0; i < NUM_THREADS; i++) {
//job = new CacheStressJob(value, cacheValue, cacheState);
job = new CacheStressJob(cache);
job->setAutoDelete(true);
jobsList.append(job);
}
QThreadPool pool;
pool.setMaxThreadCount(NUM_THREADS);
Q_FOREACH (job, jobsList) {
pool.start(job);
}
pool.waitForDone();
}
struct TestingLodDefaultBounds : public KisDefaultBoundsBase {
TestingLodDefaultBounds(const QRect &bounds = QRect(0,0,100,100))
: m_lod(0), m_bounds(bounds) {}
QRect bounds() const override {
return m_bounds;
}
bool wrapAroundMode() const override {
return false;
}
int currentLevelOfDetail() const override {
return m_lod;
}
int currentTime() const override {
return 0;
}
bool externalFrameActive() const override {
return false;
}
void testingSetLevelOfDetail(int lod) {
m_lod = lod;
}
void * sourceCookie() const override {
return 0;
}
private:
int m_lod;
QRect m_bounds;
};
void fillGradientDevice(KisPaintDeviceSP dev, const QRect &rect, bool flat = false)
{
if (flat) {
dev->fill(rect, KoColor(Qt::red, dev->colorSpace()));
} else {
// fill device with a gradient
KisSequentialIterator it(dev, rect);
while (it.nextPixel()) {
QColor c((10 * it.x()) & 0xFF, (10 * it.y()) & 0xFF, 0, 255);
KoColor color(c, dev->colorSpace());
memcpy(it.rawData(), color.data(), dev->pixelSize());
}
}
}
#include "kis_lod_transform.h"
void KisPaintDeviceTest::testLodTransform()
{
const int lod = 2; // round to 4
KisLodTransform t(lod);
QRect rc1(-16, -16, 8, 8);
QRect rc2(-16, -16, 7, 7);
QRect rc3(-15, -15, 7, 7);
QCOMPARE(t.alignedRect(rc1, lod), rc1);
QCOMPARE(t.alignedRect(rc2, lod), rc1);
QCOMPARE(t.alignedRect(rc3, lod), rc1);
}
#include "krita_utils.h"
void syncLodCache(KisPaintDeviceSP dev, int levelOfDetail)
{
KisPaintDevice::LodDataStruct* s = dev->createLodDataStruct(levelOfDetail);
KisRegion region = dev->regionForLodSyncing();
Q_FOREACH(QRect rect2, KritaUtils::splitRegionIntoPatches(region, KritaUtils::optimalPatchSize())) {
dev->updateLodDataStruct(s, rect2);
}
dev->uploadLodDataStruct(s);
}
void KisPaintDeviceTest::testLodDevice()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds();
dev->setDefaultBounds(bounds);
// fill device with a gradient
// QRect rect = dev->defaultBounds()->bounds();
// fillGradientDevice(dev, rect);
fillGradientDevice(dev, QRect(50,50,30,30));
QCOMPARE(dev->exactBounds(), QRect(50,50,30,30));
QImage result;
qDebug() << ppVar(dev->exactBounds());
result = dev->convertToQImage(0,0,0,100,100);
/*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test",
"lod", "initial"));
bounds->testingSetLevelOfDetail(1);
syncLodCache(dev, 1);
QCOMPARE(dev->exactBounds(), QRect(25,25,15,15));
qDebug() << ppVar(dev->exactBounds());
result = dev->convertToQImage(0,0,0,100,100);
/*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test",
"lod", "lod1"));
bounds->testingSetLevelOfDetail(2);
QCOMPARE(dev->exactBounds(), QRect(25,25,15,15));
qDebug() << ppVar(dev->exactBounds());
result = dev->convertToQImage(0,0,0,100,100);
/*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test",
"lod", "lod1"));
syncLodCache(dev, 2);
QCOMPARE(dev->exactBounds(), QRect(12,12,8,8));
qDebug() << ppVar(dev->exactBounds());
result = dev->convertToQImage(0,0,0,100,100);
/*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test",
"lod", "lod2"));
bounds->testingSetLevelOfDetail(0);
dev->setX(20);
dev->setY(10);
bounds->testingSetLevelOfDetail(1);
syncLodCache(dev, 1);
QCOMPARE(dev->exactBounds(), QRect(35,30,15,15));
qDebug() << ppVar(dev->exactBounds()) << ppVar(dev->x()) << ppVar(dev->y());
result = dev->convertToQImage(0,0,0,100,100);
/*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test",
"lod", "lod1-offset-6-14"));
}
void KisPaintDeviceTest::benchmarkLod1Generation()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds(QRect(0,0,6000,4000));
dev->setDefaultBounds(bounds);
// fill device with a gradient
QRect rect = dev->defaultBounds()->bounds();
fillGradientDevice(dev, rect, true);
QBENCHMARK {
bounds->testingSetLevelOfDetail(1);
syncLodCache(dev, 1);
}
}
void KisPaintDeviceTest::benchmarkLod2Generation()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds(QRect(0,0,6000,4000));
dev->setDefaultBounds(bounds);
// fill device with a gradient
QRect rect = dev->defaultBounds()->bounds();
fillGradientDevice(dev, rect, true);
QBENCHMARK {
bounds->testingSetLevelOfDetail(2);
syncLodCache(dev, 2);
}
}
void KisPaintDeviceTest::benchmarkLod3Generation()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds(QRect(0,0,3000,2000));
dev->setDefaultBounds(bounds);
// fill device with a gradient
QRect rect = dev->defaultBounds()->bounds();
fillGradientDevice(dev, rect, true);
QBENCHMARK {
bounds->testingSetLevelOfDetail(3);
syncLodCache(dev, 3);
}
}
void KisPaintDeviceTest::benchmarkLod4Generation()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds(QRect(0,0,3000,2000));
dev->setDefaultBounds(bounds);
// fill device with a gradient
QRect rect = dev->defaultBounds()->bounds();
fillGradientDevice(dev, rect, true);
QBENCHMARK {
bounds->testingSetLevelOfDetail(4);
syncLodCache(dev, 4);
}
}
#include "kis_keyframe_channel.h"
#include "kis_raster_keyframe_channel.h"
#include "kis_paint_device_frames_interface.h"
#include "testing_timed_default_bounds.h"
void KisPaintDeviceTest::testFramesLeaking()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
dev->setDefaultBounds(bounds);
KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content);
QVERIFY(channel);
KisPaintDeviceFramesInterface *i = dev->framesInterface();
QVERIFY(i);
QCOMPARE(i->frames().size(), 1);
KisPaintDeviceFramesInterface::TestingDataObjects o;
// Itinial state: one frame, m_data is kept separate
o = i->testingGetDataObjects();
QVERIFY(o.m_data);
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 1);
QVERIFY(o.m_currentData == o.m_frames[0]);
// add keyframe at position 10
channel->addKeyframe(10);
// two frames, m_data has a default empty value
o = i->testingGetDataObjects();
QVERIFY(o.m_data);
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 2);
QVERIFY(o.m_currentData == o.m_frames[0]);
// add keyframe at position 20
channel->addKeyframe(20);
// three frames, m_data is default, current frame is 0
o = i->testingGetDataObjects();
QVERIFY(o.m_data);
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 3);
QVERIFY(o.m_currentData == o.m_frames[0]);
// switch to frame 10
bounds->testingSetTime(10);
// three frames, m_data is default, current frame is 10
o = i->testingGetDataObjects();
QVERIFY(o.m_data);
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QVERIFY(o.m_currentData == o.m_frames[1]);
QCOMPARE(o.m_frames.size(), 3);
// switch to frame 20
bounds->testingSetTime(20);
// three frames, m_data is default, current frame is 20
o = i->testingGetDataObjects();
QVERIFY(o.m_data);
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QVERIFY(o.m_currentData == o.m_frames[2]);
QCOMPARE(o.m_frames.size(), 3);
// switch to frame 15
bounds->testingSetTime(15);
// three frames, m_data is default, current frame is 10
o = i->testingGetDataObjects();
QVERIFY(o.m_data);
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QVERIFY(o.m_currentData == o.m_frames[1]);
QCOMPARE(o.m_frames.size(), 3);
KisKeyframeSP key;
// deletion of frame 0 is forbidden
key = channel->keyframeAt(0);
QVERIFY(key);
QVERIFY(channel->deleteKeyframe(key));
// delete keyframe at position 11
key = channel->activeKeyframeAt(11);
QVERIFY(key);
QCOMPARE(key->time(), 10);
QVERIFY(channel->deleteKeyframe(key));
// two frames, m_data is default, current frame is 0
o = i->testingGetDataObjects();
QVERIFY(o.m_data);
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
//QVERIFY(o.m_currentData == o.m_frames[0]);
QCOMPARE(o.m_frames.size(), 2);
// deletion of frame 0 is forbidden
key = channel->activeKeyframeAt(11);
QVERIFY(key);
QCOMPARE(key->time(), 0);
QVERIFY(channel->deleteKeyframe(key));
// nothing changed
o = i->testingGetDataObjects();
QVERIFY(o.m_data);
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
//QVERIFY(o.m_currentData == o.m_frames[0]);
QCOMPARE(o.m_frames.size(), 2);
// delete keyframe at position 20
key = channel->activeKeyframeAt(20);
QVERIFY(key);
QCOMPARE(key->time(), 20);
QVERIFY(channel->deleteKeyframe(key));
// one keyframe is left at position 0, m_data is default
o = i->testingGetDataObjects();
QVERIFY(o.m_data);
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
//QVERIFY(o.m_currentData == o.m_frames[0]);
QCOMPARE(o.m_frames.size(), 1);
// ensure all the objects in the list of all objects are unique
QList<KisPaintDeviceData*> allObjects = i->testingGetDataObjectsList();
QSet<KisPaintDeviceData*> uniqueObjects;
Q_FOREACH (KisPaintDeviceData *obj, allObjects) {
if (!obj) continue;
QVERIFY(!uniqueObjects.contains(obj));
uniqueObjects.insert(obj);
}
}
void KisPaintDeviceTest::testFramesUndoRedo()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
dev->setDefaultBounds(bounds);
KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content);
QVERIFY(channel);
KisPaintDeviceFramesInterface *i = dev->framesInterface();
QVERIFY(i);
QCOMPARE(i->frames().size(), 1);
KisPaintDeviceFramesInterface::TestingDataObjects o;
// Itinial state: one frame, m_data shared
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 1);
QVERIFY(o.m_currentData == o.m_frames[0]);
// add a keyframe
KUndo2Command cmdAdd;
int frameId = -1;
const int time = 1;
channel->addKeyframe(time, &cmdAdd);
frameId = channel->frameIdAt(time);
//int frameId = i->createFrame(false, 0, QPoint(), &cmdAdd);
QCOMPARE(frameId, 1);
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 2);
QVERIFY(o.m_currentData == o.m_frames[0]);
cmdAdd.undo();
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 1);
QVERIFY(o.m_currentData == o.m_frames[0]);
cmdAdd.redo();
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 2);
QVERIFY(o.m_currentData == o.m_frames[0]);
KUndo2Command cmdRemove;
KisKeyframeSP keyframe = channel->keyframeAt(time);
QVERIFY(keyframe);
channel->deleteKeyframe(keyframe, &cmdRemove);
//i->deleteFrame(1, &cmdRemove);
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 1);
QVERIFY(o.m_currentData == o.m_frames[0]);
cmdRemove.undo();
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 2);
QVERIFY(o.m_currentData == o.m_frames[0]);
cmdRemove.redo();
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 1);
QVERIFY(o.m_currentData == o.m_frames[0]);
}
void KisPaintDeviceTest::testFramesUndoRedoWithChannel()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
dev->setDefaultBounds(bounds);
KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content);
QVERIFY(channel);
KisPaintDeviceFramesInterface *i = dev->framesInterface();
QVERIFY(i);
QCOMPARE(i->frames().size(), 1);
KisPaintDeviceFramesInterface::TestingDataObjects o;
// Itinial state: one frame, m_data shared
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 1);
QVERIFY(o.m_currentData == o.m_frames[0]);
// add a keyframe
KUndo2Command cmdAdd;
KisKeyframeSP frame = channel->addKeyframe(10, &cmdAdd);
QVERIFY(channel->keyframeAt(10));
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 2);
QVERIFY(o.m_currentData == o.m_frames[0]);
cmdAdd.undo();
QVERIFY(!channel->keyframeAt(10));
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 1);
QVERIFY(o.m_currentData == o.m_frames[0]);
cmdAdd.redo();
QVERIFY(channel->keyframeAt(10));
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 2);
QVERIFY(o.m_currentData == o.m_frames[0]);
KUndo2Command cmdRemove;
channel->deleteKeyframe(frame, &cmdRemove);
QVERIFY(!channel->keyframeAt(10));
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 1);
QVERIFY(o.m_currentData == o.m_frames[0]);
cmdRemove.undo();
QVERIFY(channel->keyframeAt(10));
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 2);
QVERIFY(o.m_currentData == o.m_frames[0]);
cmdRemove.redo();
QVERIFY(!channel->keyframeAt(10));
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 1);
QVERIFY(o.m_currentData == o.m_frames[0]);
cmdRemove.undo();
QVERIFY(channel->keyframeAt(10));
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 2);
QVERIFY(o.m_currentData == o.m_frames[0]);
KUndo2Command cmdMove;
channel->moveKeyframe(frame, 12, &cmdMove);
QVERIFY(!channel->keyframeAt(10));
QVERIFY(channel->keyframeAt(12));
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 2);
QVERIFY(o.m_currentData == o.m_frames[0]);
cmdMove.undo();
QVERIFY(channel->keyframeAt(10));
QVERIFY(!channel->keyframeAt(12));
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 2);
QVERIFY(o.m_currentData == o.m_frames[0]);
cmdMove.redo();
QVERIFY(!channel->keyframeAt(10));
QVERIFY(channel->keyframeAt(12));
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // default m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 2);
QVERIFY(o.m_currentData == o.m_frames[0]);
}
void fillRect(KisPaintDeviceSP dev, int time, const QRect &rc, TestUtil::TestingTimedDefaultBounds *bounds)
{
KUndo2Command parentCommand;
KisRasterKeyframeChannel *channel = dev->keyframeChannel();
KisKeyframeSP frame = channel->addKeyframe(time, &parentCommand);
const int oldTime = bounds->currentTime();
bounds->testingSetTime(time);
KoColor color(Qt::red, dev->colorSpace());
dev->fill(rc, color);
bounds->testingSetTime(oldTime);
}
bool checkRect(KisPaintDeviceSP dev, int time, const QRect &rc, TestUtil::TestingTimedDefaultBounds *bounds)
{
const int oldTime = bounds->currentTime();
bounds->testingSetTime(time);
bool result = dev->exactBounds() == rc;
if (!result) {
qDebug() << "Failed to check frame:" << ppVar(time) << ppVar(rc) << ppVar(dev->exactBounds());
}
bounds->testingSetTime(oldTime);
return result;
}
void testCrossDeviceFrameCopyImpl(bool useChannel)
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev1 = new KisPaintDevice(cs);
KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
const KoColorSpace *cs3 = KoColorSpaceRegistry::instance()->rgb16();
KisPaintDeviceSP dev3 = new KisPaintDevice(cs3);
TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
dev1->setDefaultBounds(bounds);
dev2->setDefaultBounds(bounds);
dev3->setDefaultBounds(bounds);
KisRasterKeyframeChannel *channel1 = dev1->createKeyframeChannel(KisKeyframeChannel::Content);
KisPaintDeviceFramesInterface *i1 = dev1->framesInterface();
QVERIFY(channel1);
QVERIFY(i1);
KisRasterKeyframeChannel *channel2 = dev2->createKeyframeChannel(KisKeyframeChannel::Content);
KisPaintDeviceFramesInterface *i2 = dev2->framesInterface();
QVERIFY(channel2);
QVERIFY(i2);
KisRasterKeyframeChannel *channel3 = dev3->createKeyframeChannel(KisKeyframeChannel::Content);
KisPaintDeviceFramesInterface *i3 = dev3->framesInterface();
QVERIFY(channel3);
QVERIFY(i3);
fillRect(dev1, 10, QRect(100,100,100,100), bounds);
fillRect(dev2, 20, QRect(200,200,100,100), bounds);
fillRect(dev3, 30, QRect(300,300,100,100), bounds);
QCOMPARE(dev1->exactBounds(), QRect());
const int dstFrameId1 = channel1->frameIdAt(10);
const int srcFrameId2 = channel2->frameIdAt(20);
const int srcFrameId3 = channel3->frameIdAt(30);
KUndo2Command cmd1;
if (!useChannel) {
dev1->framesInterface()->uploadFrame(srcFrameId2, dstFrameId1, dev2);
} else {
KisKeyframeSP k = channel1->copyExternalKeyframe(channel2, 20, 10, &cmd1);
}
QCOMPARE(dev1->exactBounds(), QRect());
QVERIFY(checkRect(dev1, 10, QRect(200,200,100,100), bounds));
if (useChannel) {
cmd1.undo();
QVERIFY(checkRect(dev1, 10, QRect(100,100,100,100), bounds));
}
KUndo2Command cmd2;
if (!useChannel) {
dev1->framesInterface()->uploadFrame(srcFrameId3, dstFrameId1, dev3);
} else {
KisKeyframeSP k = channel1->copyExternalKeyframe(channel3, 30, 10, &cmd2);
}
QCOMPARE(dev1->exactBounds(), QRect());
QVERIFY(checkRect(dev1, 10, QRect(300,300,100,100), bounds));
if (useChannel) {
cmd2.undo();
QVERIFY(checkRect(dev1, 10, QRect(100,100,100,100), bounds));
}
}
void KisPaintDeviceTest::testCrossDeviceFrameCopyDirect()
{
testCrossDeviceFrameCopyImpl(false);
}
void KisPaintDeviceTest::testCrossDeviceFrameCopyChannel()
{
testCrossDeviceFrameCopyImpl(true);
}
#include "kis_surrogate_undo_adapter.h"
void KisPaintDeviceTest::testLazyFrameCreation()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
dev->setDefaultBounds(bounds);
KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content);
QVERIFY(channel);
KisPaintDeviceFramesInterface *i = dev->framesInterface();
QVERIFY(i);
QCOMPARE(i->frames().size(), 1);
bounds->testingSetTime(10);
QCOMPARE(i->frames().size(), 1);
KisSurrogateUndoAdapter undoAdapter;
{
KisTransaction transaction1(dev);
transaction1.commit(&undoAdapter);
}
QCOMPARE(i->frames().size(), 2);
undoAdapter.undoAll();
QCOMPARE(i->frames().size(), 1);
undoAdapter.redoAll();
QCOMPARE(i->frames().size(), 2);
}
void KisPaintDeviceTest::testCopyPaintDeviceWithFrames()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
dev->setDefaultBounds(bounds);
KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content);
QVERIFY(channel);
KisPaintDeviceFramesInterface *i = dev->framesInterface();
QVERIFY(i);
QCOMPARE(i->frames().size(), 1);
KisPaintDeviceFramesInterface::TestingDataObjects o;
// Itinial state: one frame, m_data shared
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 1);
QVERIFY(o.m_currentData == o.m_frames[0]);
// add a keyframe
KUndo2Command cmdAdd;
KisKeyframeSP frame = channel->addKeyframe(10, &cmdAdd);
QVERIFY(channel->keyframeAt(10));
o = i->testingGetDataObjects();
QVERIFY(o.m_data); // m_data should always be present
QVERIFY(!o.m_lodData);
QVERIFY(!o.m_externalFrameData);
QCOMPARE(o.m_frames.size(), 2);
//QVERIFY(o.m_currentData == o.m_frames[0]);
KisPaintDeviceSP newDev = new KisPaintDevice(*dev, KritaUtils::CopyAllFrames);
QVERIFY(channel->keyframeAt(0));
QVERIFY(channel->keyframeAt(10));
}
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_smallint.hpp>
#include "KoCompositeOpRegistry.h"
using namespace boost::accumulators;
accumulator_set<qreal, stats<tag::variance, tag::max, tag::min> > accum;
void KisPaintDeviceTest::testCompositionAssociativity()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
qsrand(500);
boost::mt11213b _rnd0(qrand());
boost::mt11213b _rnd1(qrand());
boost::mt11213b _rnd2(qrand());
boost::mt11213b _rnd3(qrand());
boost::uniform_smallint<int> rnd0(0, 255);
boost::uniform_smallint<int> rnd1(0, 255);
boost::uniform_smallint<int> rnd2(0, 255);
boost::uniform_smallint<int> rnd3(0, 255);
QList<KoCompositeOp*> allCompositeOps = cs->compositeOps();
Q_FOREACH (const KoCompositeOp *op, allCompositeOps) {
accumulator_set<qreal, stats<tag::variance, tag::max, tag::min> > accum;
const int numIterations = 10000;
for (int j = 0; j < numIterations; j++) {
KoColor c1(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs);
KoColor c2(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs);
KoColor c3(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs);
//KoColor c4(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs);
//KoColor c5(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs);
KoColor r1(QColor(Qt::transparent), cs);
KoColor r2(QColor(Qt::transparent), cs);
KoColor r3(QColor(Qt::transparent), cs);
op->composite(r1.data(), 0, c1.data(), 0, 0,0, 1,1, 255);
op->composite(r1.data(), 0, c2.data(), 0, 0,0, 1,1, 255);
op->composite(r1.data(), 0, c3.data(), 0, 0,0, 1,1, 255);
//op->composite(r1.data(), 0, c4.data(), 0, 0,0, 1,1, 255);
//op->composite(r1.data(), 0, c5.data(), 0, 0,0, 1,1, 255);
op->composite(r3.data(), 0, c2.data(), 0, 0,0, 1,1, 255);
op->composite(r3.data(), 0, c3.data(), 0, 0,0, 1,1, 255);
//op->composite(r3.data(), 0, c4.data(), 0, 0,0, 1,1, 255);
//op->composite(r3.data(), 0, c5.data(), 0, 0,0, 1,1, 255);
op->composite(r2.data(), 0, c1.data(), 0, 0,0, 1,1, 255);
op->composite(r2.data(), 0, r3.data(), 0, 0,0, 1,1, 255);
const quint8 *p1 = r1.data();
const quint8 *p2 = r2.data();
if (memcmp(p1, p2, 4) != 0) {
for (int i = 0; i < 4; i++) {
accum(qAbs(p1[i] - p2[i]));
}
}
}
qDebug("Errors for op %25s err rate %7.2f var %7.2f max %7.2f",
op->id().toLatin1().data(),
(qreal(count(accum)) / (4 * numIterations)),
variance(accum),
count(accum) > 0 ? (max)(accum) : 0);
}
}
#include <kundo2stack.h>
struct FillWorker : public QRunnable
{
FillWorker(KisPaintDeviceSP dev, const QRect &fillRect, bool clear)
: m_dev(dev), m_fillRect(fillRect), m_clear(clear)
{
setAutoDelete(true);
}
void run() {
if (m_clear) {
m_dev->clear(m_fillRect);
} else {
const KoColor fillColor(Qt::red, m_dev->colorSpace());
const int pixelSize = m_dev->colorSpace()->pixelSize();
KisSequentialIterator it(m_dev, m_fillRect);
while (it.nextPixel()) {
memcpy(it.rawData(), fillColor.data(), pixelSize);
}
}
}
private:
KisPaintDeviceSP m_dev;
QRect m_fillRect;
bool m_clear;
};
#ifdef Q_OS_LINUX
#include <malloc.h>
#endif
void KisPaintDeviceTest::stressTestMemoryFragmentation()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KUndo2Stack undoStack;
#ifdef LIMIT_LONG_TESTS
const int numCycles = 3;
undoStack.setUndoLimit(1);
#else
const int numCycles = 200;
undoStack.setUndoLimit(10);
#endif
const int numThreads = 16;
const int desiredWidth = 10000;
const int patchSize = 81;
const int numSidePatches = desiredWidth / patchSize;
QThreadPool pool;
pool.setMaxThreadCount(numThreads);
for (int i = 0; i < numCycles; i++) {
qDebug() << "iteration"<< i;
// KisTransaction t(dev);
for (int y = 0; y < numSidePatches; y++) {
for (int x = 0; x < numSidePatches; x++) {
const QRect workerRect(x * patchSize, y * patchSize, patchSize, patchSize);
pool.start(new FillWorker(dev, workerRect, (i + x + y) & 0x1));
}
}
pool.waitForDone();
// undoStack.push(t.endAndTake());
qDebug() << "Iteration:" << i;
#ifdef Q_OS_LINUX
struct mallinfo info = mallinfo();
qDebug() << "Allocated on heap:" << (info.arena >> 20) << "MiB";
qDebug() << "Mmaped regions:" << info.hblks << (info.hblkhd >> 20) << "MiB";
qDebug() << "Free fastbin chunks:" << info.smblks << (info.fsmblks >> 10) << "KiB";
qDebug() << "Allocated in ordinary blocks" << (info.uordblks >> 20) << "MiB";
qDebug() << "Free in ordinary blockes" << info.ordblks << (info.fordblks >> 20) << "MiB";
#endif
qDebug() << "========================================";
}
undoStack.clear();
}
KISTEST_MAIN(KisPaintDeviceTest)
diff --git a/libs/image/tests/kis_painter_test.cpp b/libs/image/tests/kis_painter_test.cpp
index f5b8ae264b..28c5ebfddd 100644
--- a/libs/image/tests/kis_painter_test.cpp
+++ b/libs/image/tests/kis_painter_test.cpp
@@ -1,688 +1,687 @@
/*
* 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.
*/
#include "kis_painter_test.h"
#include <QTest>
-
#include <kis_debug.h>
#include <QRect>
-#include <QTime>
+#include <QElapsedTimer>
#include <QtXml>
#include <KoChannelInfo.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOpRegistry.h>
#include "kis_datamanager.h"
#include "kis_types.h"
#include "kis_paint_device.h"
#include "kis_painter.h"
#include "kis_pixel_selection.h"
#include "kis_fill_painter.h"
#include <kis_fixed_paint_device.h>
#include "testutil.h"
#include <kis_iterator_ng.h>
void KisPainterTest::allCsApplicator(void (KisPainterTest::* funcPtr)(const KoColorSpace*cs))
{
qDebug() << qAppName();
QList<const KoColorSpace*> colorspaces = KoColorSpaceRegistry::instance()->allColorSpaces(KoColorSpaceRegistry::AllColorSpaces, KoColorSpaceRegistry::OnlyDefaultProfile);
Q_FOREACH (const KoColorSpace* cs, colorspaces) {
QString csId = cs->id();
// ALL THESE COLORSPACES ARE BROKEN: WE NEED UNITTESTS FOR COLORSPACES!
if (csId.startsWith("KS")) continue;
if (csId.startsWith("Xyz")) continue;
if (csId.startsWith('Y')) continue;
if (csId.contains("AF")) continue;
if (csId == "GRAYU16") continue; // No point in testing bounds with a cs without alpha
if (csId == "GRAYU8") continue; // No point in testing bounds with a cs without alpha
dbgKrita << "Testing with cs" << csId;
if (cs && cs->compositeOp(COMPOSITE_OVER) != 0) {
(this->*funcPtr)(cs);
} else {
dbgKrita << "Cannot bitBlt for cs" << csId;
}
}
}
void KisPainterTest::testSimpleBlt(const KoColorSpace * cs)
{
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KisPaintDeviceSP src = new KisPaintDevice(cs);
KoColor c(Qt::red, cs);
c.setOpacity(quint8(128));
src->fill(20, 20, 20, 20, c.data());
QCOMPARE(src->exactBounds(), QRect(20, 20, 20, 20));
const KoCompositeOp* op;
{
op = cs->compositeOp(COMPOSITE_OVER);
KisPainter painter(dst);
painter.setCompositeOp(op);
painter.bitBlt(50, 50, src, 20, 20, 20, 20);
painter.end();
QCOMPARE(dst->exactBounds(), QRect(50,50,20,20));
}
dst->clear();
{
op = cs->compositeOp(COMPOSITE_COPY);
KisPainter painter(dst);
painter.setCompositeOp(op);
painter.bitBlt(50, 50, src, 20, 20, 20, 20);
painter.end();
QCOMPARE(dst->exactBounds(), QRect(50,50,20,20));
}
}
void KisPainterTest::testSimpleBlt()
{
allCsApplicator(&KisPainterTest::testSimpleBlt);
}
/*
Note: the bltSelection tests assume the following geometry:
0,0 0,30
+---------+------+
| 10,10 | |
| +----+ |
| |####| |
| |####| |
+----+----+ |
| 20,20 |
| |
| |
+----------------+
30,30
*/
void KisPainterTest::testPaintDeviceBltSelection(const KoColorSpace * cs)
{
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KisPaintDeviceSP src = new KisPaintDevice(cs);
KoColor c(Qt::red, cs);
c.setOpacity(quint8(128));
src->fill(0, 0, 20, 20, c.data());
QCOMPARE(src->exactBounds(), QRect(0, 0, 20, 20));
KisSelectionSP selection = new KisSelection();
selection->pixelSelection()->select(QRect(10, 10, 20, 20));
selection->updateProjection();
QCOMPARE(selection->selectedExactRect(), QRect(10, 10, 20, 20));
KisPainter painter(dst);
painter.setSelection(selection);
painter.bitBlt(0, 0, src, 0, 0, 30, 30);
painter.end();
QImage image = dst->convertToQImage(0);
image.save("blt_Selection_" + cs->name() + ".png");
QCOMPARE(dst->exactBounds(), QRect(10, 10, 10, 10));
const KoCompositeOp* op = cs->compositeOp(COMPOSITE_SUBTRACT);
if (op->id() == COMPOSITE_SUBTRACT) {
KisPaintDeviceSP dst2 = new KisPaintDevice(cs);
KisPainter painter2(dst2);
painter2.setSelection(selection);
painter2.setCompositeOp(op);
painter2.bitBlt(0, 0, src, 0, 0, 30, 30);
painter2.end();
QCOMPARE(dst2->exactBounds(), QRect(10, 10, 10, 10));
}
}
void KisPainterTest::testPaintDeviceBltSelection()
{
allCsApplicator(&KisPainterTest::testPaintDeviceBltSelection);
}
void KisPainterTest::testPaintDeviceBltSelectionIrregular(const KoColorSpace * cs)
{
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KisPaintDeviceSP src = new KisPaintDevice(cs);
KisFillPainter gc(src);
gc.fillRect(0, 0, 20, 20, KoColor(Qt::red, cs));
gc.end();
QCOMPARE(src->exactBounds(), QRect(0, 0, 20, 20));
KisSelectionSP sel = new KisSelection();
KisPixelSelectionSP psel = sel->pixelSelection();
psel->select(QRect(10, 15, 20, 15));
psel->select(QRect(15, 10, 15, 5));
QCOMPARE(psel->selectedExactRect(), QRect(10, 10, 20, 20));
QCOMPARE(TestUtil::alphaDevicePixel(psel, 13, 13), MIN_SELECTED);
KisPainter painter(dst);
painter.setSelection(sel);
painter.bitBlt(0, 0, src, 0, 0, 30, 30);
painter.end();
QImage image = dst->convertToQImage(0);
image.save("blt_Selection_irregular" + cs->name() + ".png");
QCOMPARE(dst->exactBounds(), QRect(10, 10, 10, 10));
Q_FOREACH (KoChannelInfo * channel, cs->channels()) {
// Only compare alpha if there actually is an alpha channel in
// this colorspace
if (channel->channelType() == KoChannelInfo::ALPHA) {
QColor c;
dst->pixel(13, 13, &c);
QCOMPARE((int) c.alpha(), (int) OPACITY_TRANSPARENT_U8);
}
}
}
void KisPainterTest::testPaintDeviceBltSelectionIrregular()
{
allCsApplicator(&KisPainterTest::testPaintDeviceBltSelectionIrregular);
}
void KisPainterTest::testPaintDeviceBltSelectionInverted(const KoColorSpace * cs)
{
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KisPaintDeviceSP src = new KisPaintDevice(cs);
KisFillPainter gc(src);
gc.fillRect(0, 0, 30, 30, KoColor(Qt::red, cs));
gc.end();
QCOMPARE(src->exactBounds(), QRect(0, 0, 30, 30));
KisSelectionSP sel = new KisSelection();
KisPixelSelectionSP psel = sel->pixelSelection();
psel->select(QRect(10, 10, 20, 20));
psel->invert();
sel->updateProjection();
KisPainter painter(dst);
painter.setSelection(sel);
painter.bitBlt(0, 0, src, 0, 0, 30, 30);
painter.end();
QCOMPARE(dst->exactBounds(), QRect(0, 0, 30, 30));
}
void KisPainterTest::testPaintDeviceBltSelectionInverted()
{
allCsApplicator(&KisPainterTest::testPaintDeviceBltSelectionInverted);
}
void KisPainterTest::testSelectionBltSelection()
{
KisPixelSelectionSP src = new KisPixelSelection();
src->select(QRect(0, 0, 20, 20));
QCOMPARE(src->selectedExactRect(), QRect(0, 0, 20, 20));
KisSelectionSP sel = new KisSelection();
KisPixelSelectionSP Selection = sel->pixelSelection();
Selection->select(QRect(10, 10, 20, 20));
QCOMPARE(Selection->selectedExactRect(), QRect(10, 10, 20, 20));
sel->updateProjection();
KisPixelSelectionSP dst = new KisPixelSelection();
KisPainter painter(dst);
painter.setSelection(sel);
painter.bitBlt(0, 0, src, 0, 0, 30, 30);
painter.end();
QCOMPARE(dst->selectedExactRect(), QRect(10, 10, 10, 10));
KisSequentialConstIterator it(dst, QRect(10, 10, 10, 10));
while (it.nextPixel()) {
// These are selections, so only one channel and it should
// be totally selected
QCOMPARE(it.oldRawData()[0], MAX_SELECTED);
}
}
/*
Test with non-square selection
0,0 0,30
+-----------+------+
| 13,13 | |
| x +--+ |
| +--+##| |
| |#####| |
+-----+-----+ |
| 20,20 |
| |
| |
+------------------+
30,30
*/
void KisPainterTest::testSelectionBltSelectionIrregular()
{
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
KisPixelSelectionSP src = new KisPixelSelection();
src->select(QRect(0, 0, 20, 20));
QCOMPARE(src->selectedExactRect(), QRect(0, 0, 20, 20));
KisSelectionSP sel = new KisSelection();
KisPixelSelectionSP Selection = sel->pixelSelection();
Selection->select(QRect(10, 15, 20, 15));
Selection->select(QRect(15, 10, 15, 5));
QCOMPARE(Selection->selectedExactRect(), QRect(10, 10, 20, 20));
QCOMPARE(TestUtil::alphaDevicePixel(Selection, 13, 13), MIN_SELECTED);
sel->updateProjection();
KisPixelSelectionSP dst = new KisPixelSelection();
KisPainter painter(dst);
painter.setSelection(sel);
painter.bitBlt(0, 0, src, 0, 0, 30, 30);
painter.end();
QCOMPARE(dst->selectedExactRect(), QRect(10, 10, 10, 10));
QCOMPARE(TestUtil::alphaDevicePixel(dst, 13, 13), MIN_SELECTED);
}
void KisPainterTest::testSelectionBitBltFixedSelection()
{
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KisPaintDeviceSP src = new KisPaintDevice(cs);
KoColor c(Qt::red, cs);
c.setOpacity(quint8(128));
src->fill(0, 0, 20, 20, c.data());
QCOMPARE(src->exactBounds(), QRect(0, 0, 20, 20));
KisFixedPaintDeviceSP fixedSelection = new KisFixedPaintDevice(cs);
fixedSelection->setRect(QRect(0, 0, 20, 20));
fixedSelection->initialize();
KoColor fill(Qt::white, cs);
fixedSelection->fill(5, 5, 10, 10, fill.data());
fixedSelection->convertTo(KoColorSpaceRegistry::instance()->alpha8());
KisPainter painter(dst);
painter.bitBltWithFixedSelection(0, 0, src, fixedSelection, 20, 20);
painter.end();
QCOMPARE(dst->exactBounds(), QRect(5, 5, 10, 10));
/*
dbgKrita << "canary1.5";
dst->clear();
painter.begin(dst);
painter.bitBltWithFixedSelection(0, 0, src, fixedSelection, 10, 20);
painter.end();
dbgKrita << "canary2";
QCOMPARE(dst->exactBounds(), QRect(5, 5, 5, 10));
dst->clear();
painter.begin(dst);
painter.bitBltWithFixedSelection(0, 0, src, fixedSelection, 5, 5, 5, 5, 10, 20);
painter.end();
dbgKrita << "canary3";
QCOMPARE(dst->exactBounds(), QRect(5, 5, 5, 10));
dst->clear();
painter.begin(dst);
painter.bitBltWithFixedSelection(5, 5, src, fixedSelection, 10, 20);
painter.end();
dbgKrita << "canary4";
QCOMPARE(dst->exactBounds(), QRect(10, 10, 5, 10));
*/
}
void KisPainterTest::testSelectionBitBltEraseCompositeOp()
{
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KoColor c(Qt::red, cs);
dst->fill(0, 0, 150, 150, c.data());
KisPaintDeviceSP src = new KisPaintDevice(cs);
KoColor c2(Qt::black, cs);
src->fill(50, 50, 50, 50, c2.data());
KisSelectionSP sel = new KisSelection();
KisPixelSelectionSP selection = sel->pixelSelection();
selection->select(QRect(25, 25, 100, 100));
sel->updateProjection();
const KoCompositeOp* op = cs->compositeOp(COMPOSITE_ERASE);
KisPainter painter(dst);
painter.setSelection(sel);
painter.setCompositeOp(op);
painter.bitBlt(0, 0, src, 0, 0, 150, 150);
painter.end();
//dst->convertToQImage(0).save("result.png");
QRect erasedRect(50, 50, 50, 50);
KisSequentialConstIterator it(dst, QRect(0, 0, 150, 150));
while (it.nextPixel()) {
if(!erasedRect.contains(it.x(), it.y())) {
QVERIFY(memcmp(it.oldRawData(), c.data(), cs->pixelSize()) == 0);
}
}
}
void KisPainterTest::testSimpleAlphaCopy()
{
KisPaintDeviceSP src = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
KisPaintDeviceSP dst = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
quint8 p = 128;
src->fill(0, 0, 100, 100, &p);
QVERIFY(src->exactBounds() == QRect(0, 0, 100, 100));
KisPainter gc(dst);
gc.setCompositeOp(KoColorSpaceRegistry::instance()->alpha8()->compositeOp(COMPOSITE_COPY));
gc.bitBlt(QPoint(0, 0), src, src->exactBounds());
gc.end();
QCOMPARE(dst->exactBounds(), QRect(0, 0, 100, 100));
}
void KisPainterTest::checkPerformance()
{
KisPaintDeviceSP src = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
KisPaintDeviceSP dst = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
quint8 p = 128;
src->fill(0, 0, 10000, 5000, &p);
KisSelectionSP sel = new KisSelection();
sel->pixelSelection()->select(QRect(0, 0, 10000, 5000), 128);
sel->updateProjection();
- QTime t;
+ QElapsedTimer t;
t.start();
for (int i = 0; i < 10; ++i) {
KisPainter gc(dst);
gc.bitBlt(0, 0, src, 0, 0, 10000, 5000);
}
t.restart();
for (int i = 0; i < 10; ++i) {
KisPainter gc(dst, sel);
gc.bitBlt(0, 0, src, 0, 0, 10000, 5000);
}
}
void KisPainterTest::testBitBltOldData()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8();
KisPaintDeviceSP src = new KisPaintDevice(cs);
KisPaintDeviceSP dst = new KisPaintDevice(cs);
quint8 defaultPixel = 0;
quint8 p1 = 128;
quint8 p2 = 129;
quint8 p3 = 130;
KoColor defaultColor(&defaultPixel, cs);
KoColor color1(&p1, cs);
KoColor color2(&p2, cs);
KoColor color3(&p3, cs);
QRect fillRect(0,0,5000,5000);
src->fill(fillRect, color1);
KisPainter srcGc(src);
srcGc.beginTransaction();
src->fill(fillRect, color2);
KisPainter dstGc(dst);
dstGc.bitBltOldData(QPoint(), src, fillRect);
QVERIFY(TestUtil::checkAlphaDeviceFilledWithPixel(dst, fillRect, p1));
dstGc.end();
srcGc.deleteTransaction();
}
#include "kis_paint_device_debug_utils.h"
#include "KisRenderedDab.h"
void testMassiveBltFixedImpl(int numRects, bool varyOpacity = false, bool useSelection = false)
{
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dst = new KisPaintDevice(cs);
QList<QColor> colors;
colors << Qt::red;
colors << Qt::green;
colors << Qt::blue;
QRect devicesRect;
QList<KisRenderedDab> devices;
for (int i = 0; i < numRects; i++) {
const QRect rc(10 + i * 10, 10 + i * 10, 30, 30);
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev->setRect(rc);
dev->initialize();
dev->fill(rc, KoColor(colors[i % 3], cs));
dev->fill(kisGrowRect(rc, -5), KoColor(Qt::white, cs));
KisRenderedDab dab;
dab.device = dev;
dab.offset = dev->bounds().topLeft();
dab.opacity = varyOpacity ? qreal(1 + i) / numRects : 1.0;
dab.flow = 1.0;
devices << dab;
devicesRect |= rc;
}
KisSelectionSP selection;
if (useSelection) {
selection = new KisSelection();
selection->pixelSelection()->select(kisGrowRect(devicesRect, -7));
}
const QString opacityPostfix = varyOpacity ? "_varyop" : "";
const QString selectionPostfix = useSelection ? "_sel" : "";
const QRect fullRect = kisGrowRect(devicesRect, 10);
{
KisPainter painter(dst);
painter.setSelection(selection);
painter.bltFixed(fullRect, devices);
painter.end();
QVERIFY(TestUtil::checkQImage(dst->convertToQImage(0, fullRect),
"kispainter_test",
"massive_bitblt",
QString("full_update_%1%2%3")
.arg(numRects)
.arg(opacityPostfix)
.arg(selectionPostfix), 1, 1));
}
dst->clear();
{
KisPainter painter(dst);
painter.setSelection(selection);
for (int i = fullRect.x(); i <= fullRect.center().x(); i += 10) {
const QRect rc(i, fullRect.y(), 10, fullRect.height());
painter.bltFixed(rc, devices);
}
painter.end();
QVERIFY(TestUtil::checkQImage(dst->convertToQImage(0, fullRect),
"kispainter_test",
"massive_bitblt",
QString("partial_update_%1%2%3")
.arg(numRects)
.arg(opacityPostfix)
.arg(selectionPostfix), 1, 1));
}
}
void KisPainterTest::testMassiveBltFixedSingleTile()
{
testMassiveBltFixedImpl(3);
}
void KisPainterTest::testMassiveBltFixedMultiTile()
{
testMassiveBltFixedImpl(6);
}
void KisPainterTest::testMassiveBltFixedMultiTileWithOpacity()
{
testMassiveBltFixedImpl(6, true);
}
void KisPainterTest::testMassiveBltFixedMultiTileWithSelection()
{
testMassiveBltFixedImpl(6, false, true);
}
void KisPainterTest::testMassiveBltFixedCornerCases()
{
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dst = new KisPaintDevice(cs);
QList<KisRenderedDab> devices;
QVERIFY(dst->extent().isEmpty());
{
// empty devices, shouldn't crash
KisPainter painter(dst);
painter.bltFixed(QRect(60,60,20,20), devices);
painter.end();
}
QVERIFY(dst->extent().isEmpty());
const QRect rc(10,10,20,20);
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev->setRect(rc);
dev->initialize();
dev->fill(rc, KoColor(Qt::white, cs));
devices.append(KisRenderedDab(dev));
{
// rect outside the devices bounds, shouldn't crash
KisPainter painter(dst);
painter.bltFixed(QRect(60,60,20,20), devices);
painter.end();
}
QVERIFY(dst->extent().isEmpty());
}
#include "kis_lod_transform.h"
inline QRect extentifyRect(const QRect &rc)
{
return KisLodTransform::alignedRect(rc, 6);
}
void testOptimizedCopyingImpl(const QRect &srcRect,
const QRect &dstRect,
const QRect &srcCopyRect,
const QPoint &dstPt,
const QRect &expectedDstBounds)
{
const QRect expectedDstExtent = extentifyRect(expectedDstBounds);
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP src = new KisPaintDevice(cs);
KisPaintDeviceSP dst = new KisPaintDevice(cs);
const KoColor color1(Qt::red, cs);
const KoColor color2(Qt::blue, cs);
src->fill(srcRect, color1);
dst->fill(dstRect, color2);
KisPainter::copyAreaOptimized(dstPt, src, dst, srcCopyRect);
//KIS_DUMP_DEVICE_2(dst, QRect(0,0,5000,5000), "dst", "dd");
QCOMPARE(dst->exactBounds(), expectedDstBounds);
QCOMPARE(dst->extent(), expectedDstExtent);
}
void KisPainterTest::testOptimizedCopying()
{
const QRect srcRect(1000, 1000, 1000, 1000);
const QRect srcCopyRect(0, 0, 5000, 5000);
testOptimizedCopyingImpl(srcRect, QRect(6000, 500, 1000,1000),
srcCopyRect, srcCopyRect.topLeft(),
QRect(1000, 500, 6000, 1500));
testOptimizedCopyingImpl(srcRect, QRect(4500, 1500, 1000, 1000),
srcCopyRect, srcCopyRect.topLeft(),
QRect(1000, 1000, 4500, 1500));
testOptimizedCopyingImpl(srcRect, QRect(2500, 2500, 1000, 1000),
srcCopyRect, srcCopyRect.topLeft(),
srcRect);
testOptimizedCopyingImpl(srcRect, QRect(1200, 1200, 600, 1600),
srcCopyRect, srcCopyRect.topLeft(),
srcRect);
testOptimizedCopyingImpl(srcRect, QRect(1200, 1200, 600, 600),
srcCopyRect, srcCopyRect.topLeft(),
srcRect);
}
KISTEST_MAIN(KisPainterTest)
diff --git a/libs/image/tests/kis_pattern_test.cpp b/libs/image/tests/kis_pattern_test.cpp
index ee14d32c5b..7ff9992a50 100644
--- a/libs/image/tests/kis_pattern_test.cpp
+++ b/libs/image/tests/kis_pattern_test.cpp
@@ -1,80 +1,81 @@
/*
* 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 "kis_pattern_test.h"
#include <QTest>
#include <resources/KoPattern.h>
#include <QCryptographicHash>
#include <QByteArray>
#include <kis_debug.h>
+#include <KisGlobalResourcesInterface.h>
void KoPatternTest::testCreation()
{
KoPattern test(QString(FILES_DATA_DIR) + QDir::separator() + "pattern.pat");
}
void KoPatternTest::testRoundTripMd5()
{
QString filename(QString(FILES_DATA_DIR) + QDir::separator() + "test_pattern.png");
QString patFilename("test_pattern.pat");
KoPattern pngPattern(filename);
- QVERIFY(pngPattern.load());
+ QVERIFY(pngPattern.load(KisGlobalResourcesInterface::instance()));
dbgKrita << "PNG Name:" << pngPattern.name();
dbgKrita << "PNG Filename:" << pngPattern.filename();
pngPattern.setFilename(patFilename);
pngPattern.save();
KoPattern patPattern(patFilename);
- QVERIFY(patPattern.load());
+ QVERIFY(patPattern.load(KisGlobalResourcesInterface::instance()));
dbgKrita << "PAT Name:" << patPattern.name();
dbgKrita << "PAT Filename:" << patPattern.filename();
dbgKrita << pngPattern.pattern().format();
dbgKrita << patPattern.pattern().format();
QCOMPARE(pngPattern.pattern().convertToFormat(QImage::Format_ARGB32), patPattern.pattern().convertToFormat(QImage::Format_ARGB32));
QImage im1 = pngPattern.pattern().convertToFormat(QImage::Format_ARGB32);
QImage im2 = patPattern.pattern().convertToFormat(QImage::Format_ARGB32);
QCryptographicHash h1(QCryptographicHash::Md5);
#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)
h1.addData(QByteArray::fromRawData((const char*)im1.constBits(), im1.sizeInBytes()));
#else
h1.addData(QByteArray::fromRawData((const char*)im1.constBits(), im1.byteCount()));
#endif
QCryptographicHash h2(QCryptographicHash::Md5);
#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)
h2.addData(QByteArray::fromRawData((const char*)im2.constBits(), im2.sizeInBytes()));
#else
h2.addData(QByteArray::fromRawData((const char*)im2.constBits(), im2.byteCount()));
#endif
QCOMPARE(h1.result(), h2.result());
QCOMPARE(im1, im2);
QCOMPARE(pngPattern.md5(), patPattern.md5());
}
QTEST_MAIN(KoPatternTest)
diff --git a/libs/image/tests/kis_properties_configuration_test.cpp b/libs/image/tests/kis_properties_configuration_test.cpp
index 421db530f1..1b3f5aeb9e 100644
--- a/libs/image/tests/kis_properties_configuration_test.cpp
+++ b/libs/image/tests/kis_properties_configuration_test.cpp
@@ -1,196 +1,196 @@
/*
* Copyright (c) 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.
*/
#include "kis_properties_configuration_test.h"
#include <QTest>
#include "kis_properties_configuration.h"
KisPropertiesConfigurationTest::KisPropertiesConfigurationTest() :
v1(10), v2("hello"), v3(1242.0), v4(true)
{
QList<QPointF> pts; pts.push_back(QPointF(0.2, 0.3)); pts.push_back(QPointF(0.5, 0.7));
v5.setPoints(pts);
}
void KisPropertiesConfigurationTest::testSerialization()
{
KisPropertiesConfigurationSP config = createConfig();
QString xml = config->toXML();
KisPropertiesConfigurationSP decodedConfig = new KisPropertiesConfiguration();
decodedConfig->fromXML(xml);
testConfig(decodedConfig);
}
void KisPropertiesConfigurationTest::testSetGet()
{
KisPropertiesConfigurationSP config = createConfig();
testConfig(config);
}
void KisPropertiesConfigurationTest::testDefaultValues()
{
KisPropertiesConfigurationSP config = new KisPropertiesConfiguration();
QVERIFY(config->getInt("bouh", v1) == v1);
QVERIFY(config->getString("bouh", v2) == v2);
QVERIFY(config->getDouble("bouh", v3) == v3);
QVERIFY(config->getBool("bouh", v4) == v4);
QVERIFY(config->getCubicCurve("bouh", v5) == v5);
}
KisPropertiesConfigurationSP KisPropertiesConfigurationTest::createConfig()
{
KisPropertiesConfigurationSP config = new KisPropertiesConfiguration();
config->setProperty("v1", v1);
config->setProperty("v2", v2);
config->setProperty("v3", v3);
config->setProperty("v4", v4);
- config->setProperty("v5", qVariantFromValue(v5));
+ config->setProperty("v5", QVariant::fromValue(v5));
return config;
}
void KisPropertiesConfigurationTest::testConfig(KisPropertiesConfigurationSP config)
{
QVERIFY(config->getInt("v1", 0) == v1);
QVERIFY(config->getString("v2", QString()) == v2);
QVERIFY(config->getDouble("v3", 0.0) == v3);
QVERIFY(config->getBool("v4", !v4) == v4);
QVERIFY(config->getCubicCurve("v5") == v5);
}
void KisPropertiesConfigurationTest::testNotSavedValues()
{
KisPropertiesConfigurationSP config = createConfig();
config->setPropertyNotSaved("v3");
testConfig(config);
QString s = config->toXML();
config = new KisPropertiesConfiguration();
config->fromXML(s);
QVERIFY(config->getInt("v1", 0) == v1);
QVERIFY(config->getString("v2", QString()) == v2);
QVERIFY(config->hasProperty("v3") == false);
QVERIFY(config->getBool("v4", !v4) == v4);
QVERIFY(config->getCubicCurve("v5") == v5);
}
void KisPropertiesConfigurationTest::testCopy()
{
KisPropertiesConfigurationSP p1 = createConfig();
p1->setProperty("v6", "bla");
p1->setPropertyNotSaved("v6");
KisPropertiesConfiguration config = KisPropertiesConfiguration(*p1.data());
QVERIFY(config.getInt("v1", 0) == v1);
QVERIFY(config.getString("v2", QString()) == v2);
QVERIFY(config.getDouble("v3", 0.0) == v3);
QVERIFY(config.getBool("v4", !v4) == v4);
QVERIFY(config.getCubicCurve("v5") == v5);
QVERIFY(config.hasProperty("v6") == true); // copying works!
p1->setProperty("testBool1", true);
p1->setProperty("testBool2", false);
QString string = p1->toXML();
KisPropertiesConfiguration p2;
p2.fromXML(string);
QVERIFY(p2.getInt("v1", 0) == v1);
QVERIFY(p2.getString("v2", QString()) == v2);
QVERIFY(p2.getDouble("v3", 0.0) == v3);
QVERIFY(p2.getBool("v4", !v4) == v4);
QVERIFY(p2.hasProperty("v6") == false); // round-tripping -- no
QCOMPARE(p2.getBool("testBool1", false), true);
QCOMPARE(p2.getBool("testBool2", true), false);
}
void KisPropertiesConfigurationTest::testGetColor()
{
KisPropertiesConfiguration pc;
KoColor kc = KoColor(QColor(Qt::red), KoColorSpaceRegistry::instance()->rgb8());
QVariant c = QVariant::fromValue<KoColor>(kc);
pc.setProperty("colorAsKoColor", c);
pc.setProperty("colorAsQColor", QColor(Qt::red));
pc.setProperty("colorAsString", "#FF0000");
pc.setProperty("colorAsXML", "<!DOCTYPE color><color><RGB space=\"sRGB-elle-V2-g10.icc\" g=\"0\" b=\"0\" r=\"1\"/></color>");
kc = pc.getColor("colorAsKoColor");
QVERIFY(kc.toQColor() == QColor(Qt::red));
kc = pc.getColor("colorAsQColor");
QVERIFY(kc.toQColor() == QColor(Qt::red));
kc = pc.getColor("colorAsString");
QVERIFY(kc.toQColor() == QColor(Qt::red));
kc = pc.getColor("colorAsXML");
QVERIFY(kc.toQColor() == QColor(Qt::red));
}
void roundTripStringList(const QStringList &refList)
{
KisPropertiesConfiguration config1;
config1.setProperty("testProp", refList);
const QString xmlData = config1.toXML();
KisPropertiesConfiguration config2;
config2.fromXML(xmlData);
const QStringList results = config2.getStringList("testProp");
QCOMPARE(results, refList);
}
void KisPropertiesConfigurationTest::testLists()
{
const QString str1("str1 str2\\ str3/ str4; str5% str6& str7;; str8\\; str9]]> str10");
const QString str2("str1 str2\\ str3/ str4; str5% str6& str7;; str8\\; str9]]> str10;");
{
const QStringList refList({str1, str1});
roundTripStringList(refList);
}
{
const QStringList refList({str2, str2});
roundTripStringList(refList);
}
{
const QStringList refList({"", str2, str2});
roundTripStringList(refList);
}
{
const QStringList refList({str2, str2, ""});
roundTripStringList(refList);
}
{
const QStringList refList({str2, str2, ";"});
roundTripStringList(refList);
}
{
const QStringList refList({";", str2, str2});
roundTripStringList(refList);
}
}
QTEST_MAIN(KisPropertiesConfigurationTest)
diff --git a/libs/image/tests/kis_simple_update_queue_test.cpp b/libs/image/tests/kis_simple_update_queue_test.cpp
index 2b522c88ae..a7a48f11a4 100644
--- a/libs/image/tests/kis_simple_update_queue_test.cpp
+++ b/libs/image/tests/kis_simple_update_queue_test.cpp
@@ -1,280 +1,281 @@
/*
* 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.
*/
#include "kis_simple_update_queue_test.h"
#include <QTest>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_adjustment_layer.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_selection.h"
#include "kis_update_job_item.h"
#include "kis_simple_update_queue.h"
#include "scheduler_utils.h"
+#include <KisGlobalResourcesInterface.h>
#include "lod_override.h"
void KisSimpleUpdateQueueTest::testJobProcessing()
{
KisTestableUpdaterContext context(2);
QRect imageRect(0,0,200,200);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "merge test");
KisPaintLayerSP paintLayer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8);
image->barrierLock();
image->addNode(paintLayer);
image->unlock();
QRect dirtyRect1(0,0,50,100);
QRect dirtyRect2(0,0,100,100);
QRect dirtyRect3(50,0,50,100);
QRect dirtyRect4(150,150,50,50);
QRect dirtyRect5(dirtyRect4); // theoretically, should be merged with 4
QVector<KisUpdateJobItem*> jobs;
KisWalkersList walkersList;
/**
* Process the queue and look what has been added into
* the updater context
*/
KisTestableSimpleUpdateQueue queue;
queue.addUpdateJob(paintLayer, dirtyRect1, imageRect, 0);
queue.addUpdateJob(paintLayer, dirtyRect2, imageRect, 0);
queue.addUpdateJob(paintLayer, dirtyRect3, imageRect, 0);
queue.addUpdateJob(paintLayer, dirtyRect4, imageRect, 0);
{
TestUtil::LodOverride l(1, image);
queue.addUpdateJob(paintLayer, dirtyRect5, imageRect, 1);
}
queue.processQueue(context);
jobs = context.getJobs();
QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect2));
QVERIFY(checkWalker(jobs[1]->walker(), dirtyRect4));
QCOMPARE(jobs.size(), 2);
QCOMPARE(context.currentLevelOfDetail(), 0);
walkersList = queue.getWalkersList();
QCOMPARE(walkersList.size(), 1);
QVERIFY(checkWalker(walkersList[0], dirtyRect5, 1));
}
void KisSimpleUpdateQueueTest::testSplitUpdate()
{
testSplit(false);
}
void KisSimpleUpdateQueueTest::testSplitFullRefresh()
{
testSplit(true);
}
void KisSimpleUpdateQueueTest::testSplit(bool useFullRefresh)
{
QRect imageRect(0,0,1024,1024);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "merge test");
KisPaintLayerSP paintLayer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8);
image->barrierLock();
image->addNode(paintLayer);
image->unlock();
QRect dirtyRect1(0,0,1000,1000);
KisTestableSimpleUpdateQueue queue;
KisWalkersList& walkersList = queue.getWalkersList();
if(!useFullRefresh) {
queue.addUpdateJob(paintLayer, dirtyRect1, imageRect, 0);
}
else {
queue.addFullRefreshJob(paintLayer, dirtyRect1, imageRect, 0);
}
QCOMPARE(walkersList.size(), 4);
QVERIFY(checkWalker(walkersList[0], QRect(0,0,512,512)));
QVERIFY(checkWalker(walkersList[1], QRect(512,0,488,512)));
QVERIFY(checkWalker(walkersList[2], QRect(0,512,512,488)));
QVERIFY(checkWalker(walkersList[3], QRect(512,512,488,488)));
queue.optimize();
//must change nothing
QCOMPARE(walkersList.size(), 4);
QVERIFY(checkWalker(walkersList[0], QRect(0,0,512,512)));
QVERIFY(checkWalker(walkersList[1], QRect(512,0,488,512)));
QVERIFY(checkWalker(walkersList[2], QRect(0,512,512,488)));
QVERIFY(checkWalker(walkersList[3], QRect(512,512,488,488)));
}
void KisSimpleUpdateQueueTest::testChecksum()
{
QRect imageRect(0,0,512,512);
QRect dirtyRect(100,100,100,100);
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), colorSpace, "test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisAdjustmentLayerSP adjustmentLayer = new KisAdjustmentLayer(image, "adj", 0, 0);
image->barrierLock();
image->addNode(paintLayer1, image->rootLayer());
image->addNode(adjustmentLayer, image->rootLayer());
image->unlock();
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
- KisFilterConfigurationSP configuration = filter->defaultConfiguration();
+ KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
KisTestableSimpleUpdateQueue queue;
KisWalkersList& walkersList = queue.getWalkersList();
{
TestUtil::LodOverride l(1, image);
queue.addUpdateJob(adjustmentLayer, dirtyRect, imageRect, 1);
QCOMPARE(walkersList[0]->checksumValid(), true);
QCOMPARE(walkersList[0]->levelOfDetail(), 1);
}
- adjustmentLayer->setFilter(configuration);
+ adjustmentLayer->setFilter(configuration->cloneWithResourcesSnapshot());
{
TestUtil::LodOverride l(1, image);
QCOMPARE(walkersList[0]->checksumValid(), false);
}
QVector<KisUpdateJobItem*> jobs;
KisTestableUpdaterContext context(2);
{
TestUtil::LodOverride l(1, image);
queue.processQueue(context);
}
jobs = context.getJobs();
{
TestUtil::LodOverride l(1, image);
QCOMPARE(jobs[0]->walker()->checksumValid(), true);
}
}
void KisSimpleUpdateQueueTest::testMixingTypes()
{
QRect imageRect(0,0,1024,1024);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "merge test");
KisPaintLayerSP paintLayer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8);
image->barrierLock();
image->addNode(paintLayer);
image->unlock();
QRect dirtyRect1(0,0,200,200);
QRect dirtyRect2(0,0,200,200);
QRect dirtyRect3(20,20,200,200);
KisTestableSimpleUpdateQueue queue;
KisWalkersList& walkersList = queue.getWalkersList();
queue.addUpdateJob(paintLayer, dirtyRect1, imageRect, 0);
queue.addFullRefreshJob(paintLayer, dirtyRect2, imageRect, 0);
queue.addFullRefreshJob(paintLayer, dirtyRect3, imageRect, 0);
queue.addUpdateNoFilthyJob(paintLayer, dirtyRect1, imageRect, 0);
QCOMPARE(walkersList.size(), 3);
QVERIFY(checkWalker(walkersList[0], QRect(0,0,200,200)));
QVERIFY(checkWalker(walkersList[1], QRect(0,0,220,220)));
QVERIFY(checkWalker(walkersList[2], QRect(0,0,200,200)));
QCOMPARE(walkersList[0]->type(), KisBaseRectsWalker::UPDATE);
QCOMPARE(walkersList[1]->type(), KisBaseRectsWalker::FULL_REFRESH);
QCOMPARE(walkersList[2]->type(), KisBaseRectsWalker::UPDATE_NO_FILTHY);
}
void KisSimpleUpdateQueueTest::testSpontaneousJobsCompression()
{
KisTestableSimpleUpdateQueue queue;
KisSpontaneousJobsList &jobsList = queue.getSpontaneousJobsList();
QVERIFY(queue.isEmpty());
QCOMPARE(queue.sizeMetric(), 0);
QVERIFY(jobsList.isEmpty());
// we cannot use scoped pointers for the jobs, since they become owned by the queue
KisSpontaneousJob *job1 = new KisNoopSpontaneousJob(false);
queue.addSpontaneousJob(job1);
QVERIFY(!queue.isEmpty());
QCOMPARE(queue.sizeMetric(), 1);
QCOMPARE(jobsList.size(), 1);
QCOMPARE(jobsList[0], job1);
KisSpontaneousJob *job2 = new KisNoopSpontaneousJob(false);
queue.addSpontaneousJob(job2);
QVERIFY(!queue.isEmpty());
QCOMPARE(queue.sizeMetric(), 2);
QCOMPARE(jobsList.size(), 2);
QCOMPARE(jobsList[0], job1);
QCOMPARE(jobsList[1], job2);
KisSpontaneousJob *job3 = new KisNoopSpontaneousJob(true);
queue.addSpontaneousJob(job3);
QVERIFY(!queue.isEmpty());
QCOMPARE(queue.sizeMetric(), 1);
QCOMPARE(jobsList.size(), 1);
QCOMPARE(jobsList[0], job3);
}
QTEST_MAIN(KisSimpleUpdateQueueTest)
diff --git a/libs/image/tests/kis_update_scheduler_test.cpp b/libs/image/tests/kis_update_scheduler_test.cpp
index da7456918a..62f2bbfd87 100644
--- a/libs/image/tests/kis_update_scheduler_test.cpp
+++ b/libs/image/tests/kis_update_scheduler_test.cpp
@@ -1,431 +1,432 @@
/*
* 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.
*/
#include "kis_update_scheduler_test.h"
#include <QTest>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_adjustment_layer.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_selection.h"
#include "scheduler_utils.h"
#include "kis_update_scheduler.h"
#include "kis_updater_context.h"
#include "kis_update_job_item.h"
#include "kis_simple_update_queue.h"
+#include <KisGlobalResourcesInterface.h>
#include "../../sdk/tests/testutil.h"
KisImageSP KisUpdateSchedulerTest::buildTestingImage()
{
QImage sourceImage1(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
QImage sourceImage2(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png");
QRect imageRect = QRect(QPoint(0,0), sourceImage1.size());
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "merge test");
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
- KisFilterConfigurationSP configuration = filter->defaultConfiguration();
+ KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(configuration);
KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisPaintLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8 / 3);
- KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration, 0);
+ KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration->cloneWithResourcesSnapshot(), 0);
paintLayer1->paintDevice()->convertFromQImage(sourceImage1, 0, 0, 0);
paintLayer2->paintDevice()->convertFromQImage(sourceImage2, 0, 0, 0);
image->barrierLock();
image->addNode(paintLayer1);
image->addNode(paintLayer2);
image->addNode(blur1);
image->unlock();
return image;
}
void KisUpdateSchedulerTest::testMerge()
{
KisImageSP image = buildTestingImage();
QRect imageRect = image->bounds();
KisNodeSP rootLayer = image->rootLayer();
KisNodeSP paintLayer1 = rootLayer->firstChild();
QCOMPARE(paintLayer1->name(), QString("paint1"));
KisUpdateScheduler scheduler(image.data());
/**
* Test synchronous Full Refresh
*/
scheduler.fullRefresh(rootLayer, image->bounds(), image->bounds());
QCOMPARE(rootLayer->exactBounds(), image->bounds());
QImage resultFRProjection = rootLayer->projection()->convertToQImage(0);
resultFRProjection.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + "scheduler_fr_merge_result.png");
/**
* Test incremental updates
*/
rootLayer->projection()->clear();
const qint32 num = 4;
qint32 width = imageRect.width() / num;
qint32 lastWidth = imageRect.width() - width;
QVector<QRect> dirtyRects(num);
for(qint32 i = 0; i < num-1; i++) {
dirtyRects[i] = QRect(width*i, 0, width, imageRect.height());
}
dirtyRects[num-1] = QRect(width*(num-1), 0, lastWidth, imageRect.height());
for(qint32 i = 0; i < num; i+=2) {
scheduler.updateProjection(paintLayer1, dirtyRects[i], image->bounds());
}
for(qint32 i = 1; i < num; i+=2) {
scheduler.updateProjection(paintLayer1, dirtyRects[i], image->bounds());
}
scheduler.waitForDone();
QCOMPARE(rootLayer->exactBounds(), image->bounds());
QImage resultDirtyProjection = rootLayer->projection()->convertToQImage(0);
resultDirtyProjection.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + "scheduler_dp_merge_result.png");
QPoint pt;
QVERIFY(TestUtil::compareQImages(pt, resultFRProjection, resultDirtyProjection));
}
void KisUpdateSchedulerTest::benchmarkOverlappedMerge()
{
KisImageSP image = buildTestingImage();
KisNodeSP rootLayer = image->rootLayer();
KisNodeSP paintLayer1 = rootLayer->firstChild();
QRect imageRect = image->bounds();
QCOMPARE(paintLayer1->name(), QString("paint1"));
QCOMPARE(imageRect, QRect(0,0,640,441));
KisUpdateScheduler scheduler(image.data());
const qint32 xShift = 10;
const qint32 yShift = 0;
const qint32 numShifts = 64;
QBENCHMARK{
QRect dirtyRect(0, 0, 200, imageRect.height());
for(int i = 0; i < numShifts; i++) {
// dbgKrita << dirtyRect;
scheduler.updateProjection(paintLayer1, dirtyRect, image->bounds());
dirtyRect.translate(xShift, yShift);
}
scheduler.waitForDone();
}
}
void KisUpdateSchedulerTest::testLocking()
{
KisImageSP image = buildTestingImage();
KisNodeSP rootLayer = image->rootLayer();
KisNodeSP paintLayer1 = rootLayer->firstChild();
QRect imageRect = image->bounds();
QCOMPARE(paintLayer1->name(), QString("paint1"));
QCOMPARE(imageRect, QRect(0,0,640,441));
KisTestableUpdateScheduler scheduler(image.data(), 2);
KisUpdaterContext *context = scheduler.updaterContext();
QVERIFY(context);
QVector<KisUpdateJobItem*> jobs;
QRect dirtyRect1(0,0,50,100);
QRect dirtyRect2(0,0,100,100);
QRect dirtyRect3(50,0,50,100);
QRect dirtyRect4(150,150,50,50);
scheduler.updateProjection(paintLayer1, imageRect, imageRect);
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
QVERIFY(checkWalker(jobs[0]->walker(), imageRect));
context->clear();
scheduler.lock();
scheduler.updateProjection(paintLayer1, dirtyRect1, imageRect);
scheduler.updateProjection(paintLayer1, dirtyRect2, imageRect);
scheduler.updateProjection(paintLayer1, dirtyRect3, imageRect);
scheduler.updateProjection(paintLayer1, dirtyRect4, imageRect);
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), false);
QCOMPARE(jobs[1]->isRunning(), false);
scheduler.unlock();
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), true);
QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect2));
QVERIFY(checkWalker(jobs[1]->walker(), dirtyRect4));
}
void KisUpdateSchedulerTest::testExclusiveStrokes()
{
KisImageSP image = buildTestingImage();
KisNodeSP rootLayer = image->rootLayer();
KisNodeSP paintLayer1 = rootLayer->firstChild();
QRect imageRect = image->bounds();
QCOMPARE(paintLayer1->name(), QString("paint1"));
QCOMPARE(imageRect, QRect(0,0,640,441));
QRect dirtyRect1(0,0,50,100);
KisTestableUpdateScheduler scheduler(image.data(), 2);
KisUpdaterContext *context = scheduler.updaterContext();
QVector<KisUpdateJobItem*> jobs;
scheduler.updateProjection(paintLayer1, dirtyRect1, imageRect);
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect1));
KisStrokeId id = scheduler.startStroke(new KisTestingStrokeStrategy(QLatin1String("excl_"), true, false));
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect1));
context->clear();
scheduler.endStroke(id);
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
COMPARE_NAME(jobs[0], "excl_init");
scheduler.updateProjection(paintLayer1, dirtyRect1, imageRect);
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
COMPARE_NAME(jobs[0], "excl_init");
context->clear();
scheduler.processQueues();
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
COMPARE_NAME(jobs[0], "excl_finish");
context->clear();
scheduler.processQueues();
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect1));
}
void KisUpdateSchedulerTest::testEmptyStroke()
{
KisImageSP image = buildTestingImage();
KisStrokeId id = image->startStroke(new KisStrokeStrategy(QLatin1String()));
image->addJob(id, 0);
image->endStroke(id);
image->waitForDone();
}
#include "kis_lazy_wait_condition.h"
void KisUpdateSchedulerTest::testLazyWaitCondition()
{
{
dbgKrita << "Not initialized";
KisLazyWaitCondition condition;
QVERIFY(!condition.wait(50));
}
{
dbgKrita << "Initialized, not awake";
KisLazyWaitCondition condition;
condition.initWaiting();
QVERIFY(!condition.wait(50));
condition.endWaiting();
}
{
dbgKrita << "Initialized, awake";
KisLazyWaitCondition condition;
condition.initWaiting();
condition.wakeAll();
QVERIFY(condition.wait(50));
condition.endWaiting();
}
{
dbgKrita << "Initialized, not awake, then awake";
KisLazyWaitCondition condition;
condition.initWaiting();
QVERIFY(!condition.wait(50));
condition.wakeAll();
QVERIFY(condition.wait(50));
condition.endWaiting();
}
{
dbgKrita << "Doublewait";
KisLazyWaitCondition condition;
condition.initWaiting();
condition.initWaiting();
QVERIFY(!condition.wait(50));
condition.wakeAll();
QVERIFY(condition.wait(50));
QVERIFY(condition.wait(50));
condition.endWaiting();
}
}
#define NUM_THREADS 10
#define NUM_CYCLES 500
#define NTH_CHECK 3
class UpdatesBlockTester : public QRunnable
{
public:
UpdatesBlockTester(KisUpdateScheduler *scheduler, KisNodeSP node)
: m_scheduler(scheduler), m_node(node)
{
}
void run() override {
for (int i = 0; i < NUM_CYCLES; i++) {
if(i % NTH_CHECK == 0) {
m_scheduler->blockUpdates();
QTest::qSleep(1); // a bit of salt for crashiness ;)
Q_ASSERT(!m_scheduler->haveUpdatesRunning());
m_scheduler->unblockUpdates();
}
else {
QRect updateRect(0,0,100,100);
updateRect.moveTopLeft(QPoint((i%10)*100, (i%10)*100));
m_scheduler->updateProjection(m_node, updateRect, QRect(0,0,1100,1100));
}
}
}
private:
KisUpdateScheduler *m_scheduler;
KisNodeSP m_node;
};
void KisUpdateSchedulerTest::testBlockUpdates()
{
KisImageSP image = buildTestingImage();
KisNodeSP rootLayer = image->rootLayer();
KisNodeSP paintLayer1 = rootLayer->firstChild();
KisUpdateScheduler scheduler(image.data());
QThreadPool threadPool;
threadPool.setMaxThreadCount(NUM_THREADS);
for(int i = 0; i< NUM_THREADS; i++) {
UpdatesBlockTester *tester =
new UpdatesBlockTester(&scheduler, paintLayer1);
threadPool.start(tester);
}
threadPool.waitForDone();
}
#include "kis_update_time_monitor.h"
void KisUpdateSchedulerTest::testTimeMonitor()
{
QVector<QRect> dirtyRects;
KisUpdateTimeMonitor::instance()->startStrokeMeasure();
KisUpdateTimeMonitor::instance()->reportMouseMove(QPointF(100, 0));
KisUpdateTimeMonitor::instance()->reportJobStarted((void*) 10);
QTest::qSleep(300);
KisUpdateTimeMonitor::instance()->reportJobStarted((void*) 20);
QTest::qSleep(100);
dirtyRects << QRect(10,10,10,10);
KisUpdateTimeMonitor::instance()->reportJobFinished((void*) 10, dirtyRects);
QTest::qSleep(100);
dirtyRects.clear();
dirtyRects << QRect(30,30,10,10);
KisUpdateTimeMonitor::instance()->reportJobFinished((void*) 20, dirtyRects);
QTest::qSleep(500);
KisUpdateTimeMonitor::instance()->reportUpdateFinished(QRect(10,10,10,10));
QTest::qSleep(300);
KisUpdateTimeMonitor::instance()->reportUpdateFinished(QRect(30,30,10,10));
KisUpdateTimeMonitor::instance()->reportMouseMove(QPointF(130, 0));
KisUpdateTimeMonitor::instance()->endStrokeMeasure();
}
void KisUpdateSchedulerTest::testLodSync()
{
KisImageSP image = buildTestingImage();
KisNodeSP rootLayer = image->root();
KisNodeSP paintLayer1 = rootLayer->firstChild();
QCOMPARE(paintLayer1->name(), QString("paint1"));
image->setLevelOfDetailBlocked(false);
image->setDesiredLevelOfDetail(2);
image->explicitRegenerateLevelOfDetail();
image->waitForDone();
}
QTEST_MAIN(KisUpdateSchedulerTest)
diff --git a/libs/image/tests/kis_walkers_test.cpp b/libs/image/tests/kis_walkers_test.cpp
index 83aef57ae7..20a9c61108 100644
--- a/libs/image/tests/kis_walkers_test.cpp
+++ b/libs/image/tests/kis_walkers_test.cpp
@@ -1,1294 +1,1295 @@
/*
* 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_walkers_test.h"
#include "kis_base_rects_walker.h"
#include "kis_refresh_subtree_walker.h"
#include "kis_full_refresh_walker.h"
#include <QTest>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpace.h>
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include "kis_clone_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_selection.h"
+#include <KisGlobalResourcesInterface.h>
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_filter_mask.h"
#include "kis_transparency_mask.h"
#include <sdk/tests/kistest.h>
//#define DEBUG_VISITORS
QString nodeTypeString(KisMergeWalker::NodePosition position);
QString nodeTypePostfix(KisMergeWalker::NodePosition position);
/************** Test Implementation Of A Walker *********************/
class KisTestWalker : public KisMergeWalker
{
public:
KisTestWalker()
:KisMergeWalker(QRect())
{
}
QStringList popResult() {
QStringList order(m_order);
m_order.clear();
return order;
}
using KisMergeWalker::startTrip;
protected:
void registerChangeRect(KisProjectionLeafSP node, NodePosition position) override {
Q_UNUSED(position);
QString postfix;
if(!node->isLayer()) {
postfix = "[skipped as not-a-layer]";
}
#ifdef DEBUG_VISITORS
qDebug()<< "FW:"<< node->node()->name() <<'\t'<< nodeTypeString(position) << postfix;
#endif
if(postfix.isEmpty()) {
m_order.append(node->node()->name());
}
}
void registerNeedRect(KisProjectionLeafSP node, NodePosition position) override {
QString postfix;
if(!node->isLayer()) {
postfix = "[skipped as not-a-layer]";
}
#ifdef DEBUG_VISITORS
qDebug()<< "BW:"<< node->node()->name() <<'\t'<< nodeTypeString(position) << postfix;
#endif
if(postfix.isEmpty()) {
m_order.append(node->node()->name() + nodeTypePostfix(position));
}
}
protected:
QStringList m_order;
};
/************** Debug And Verify Code *******************************/
struct UpdateTestJob {
QString updateAreaName;
KisNodeSP startNode;
QRect updateRect;
QString referenceString;
QRect accessRect;
bool changeRectVaries;
bool needRectVaries;
};
void reportStartWith(QString nodeName, QRect rect = QRect())
{
qDebug();
if(!rect.isEmpty())
qDebug() << "Start with:" << nodeName << rect;
else
qDebug() << "Start with:" << nodeName;
}
QString nodeTypeString(KisMergeWalker::NodePosition position)
{
QString string;
if(position & KisMergeWalker::N_TOPMOST)
string="TOP";
else if(position & KisMergeWalker::N_BOTTOMMOST)
string="BOT";
else
string="NOR";
if(position & KisMergeWalker::N_ABOVE_FILTHY)
string+="_ABOVE ";
else if(position & KisMergeWalker::N_FILTHY)
string+="_FILTH*";
else if(position & KisMergeWalker::N_FILTHY_PROJECTION)
string+="_PROJE*";
else if(position & KisMergeWalker::N_FILTHY_ORIGINAL)
string+="_ORIGI*_WARNINIG!!!: NOT USED";
else if(position & KisMergeWalker::N_BELOW_FILTHY)
string+="_BELOW ";
else if(position & KisMergeWalker::N_EXTRA)
string+="_EXTRA*";
else
qFatal("Impossible happened");
return string;
}
QString nodeTypePostfix(KisMergeWalker::NodePosition position)
{
QString string('_');
if(position & KisMergeWalker::N_TOPMOST)
string += 'T';
else if(position & KisMergeWalker::N_BOTTOMMOST)
string += 'B';
else
string += 'N';
if(position & KisMergeWalker::N_ABOVE_FILTHY)
string += 'A';
else if(position & KisMergeWalker::N_FILTHY)
string += 'F';
else if(position & KisMergeWalker::N_FILTHY_PROJECTION)
string += 'P';
else if(position & KisMergeWalker::N_FILTHY_ORIGINAL)
string += 'O';
else if(position & KisMergeWalker::N_BELOW_FILTHY)
string += 'B';
else if(position & KisMergeWalker::N_EXTRA)
string += 'E';
else
qFatal("Impossible happened");
return string;
}
void KisWalkersTest::verifyResult(KisBaseRectsWalker &walker, struct UpdateTestJob &job)
{
QStringList list;
if(!job.referenceString.isEmpty()) {
list = job.referenceString.split(',');
}
verifyResult(walker, list, job.accessRect,
job.changeRectVaries, job.needRectVaries);
}
void KisWalkersTest::verifyResult(KisBaseRectsWalker &walker, QStringList reference,
QRect accessRect, bool changeRectVaries,
bool needRectVaries)
{
KisMergeWalker::LeafStack &list = walker.leafStack();
QStringList::const_iterator iter = reference.constBegin();
if(reference.size() != list.size()) {
qDebug() << "*** Seems like the walker returned stack of wrong size"
<< "( ref:" << reference.size() << "act:" << list.size() << ")";
qDebug() << "*** We are going to crash soon... just wait...";
}
Q_FOREACH (const KisMergeWalker::JobItem &item, list) {
#ifdef DEBUG_VISITORS
qDebug() << item.m_leaf->node()->name() << '\t'
<< item.m_applyRect << '\t'
<< nodeTypeString(item.m_position);
#endif
QCOMPARE(item.m_leaf->node()->name(), *iter);
iter++;
}
#ifdef DEBUG_VISITORS
qDebug() << "Result AR:\t" << walker.accessRect();
#endif
QCOMPARE(walker.accessRect(), accessRect);
QCOMPARE(walker.changeRectVaries(), changeRectVaries);
QCOMPARE(walker.needRectVaries(), needRectVaries);
}
/************** Actual Testing **************************************/
/*
+----------+
|root |
| layer 5 |
| group |
| paint 4 |
| paint 3 |
| adj |
| paint 2 |
| paint 1 |
+----------+
*/
void KisWalkersTest::testUsualVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP adjustmentLayer = new KisAdjustmentLayer(image, "adj", 0, 0);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(adjustmentLayer, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(paintLayer4, groupLayer);
KisTestWalker walker;
{
QString order("paint3,paint4,group,paint5,root,"
"root_TF,paint5_TA,group_NF,paint1_BB,"
"paint4_TA,paint3_NF,adj_NB,paint2_BB");
QStringList orderList = order.split(',');
reportStartWith("paint3");
walker.startTrip(paintLayer3->projectionLeaf());
QVERIFY(walker.popResult() == orderList);
}
{
QString order("adj,paint3,paint4,group,paint5,root,"
"root_TF,paint5_TA,group_NF,paint1_BB,"
"paint4_TA,paint3_NA,adj_NF,paint2_BB");
QStringList orderList = order.split(',');
reportStartWith("adj");
walker.startTrip(adjustmentLayer->projectionLeaf());
QVERIFY(walker.popResult() == orderList);
}
{
QString order("group,paint5,root,"
"root_TF,paint5_TA,group_NF,paint1_BB");
QStringList orderList = order.split(',');
reportStartWith("group");
walker.startTrip(groupLayer->projectionLeaf());
QVERIFY(walker.popResult() == orderList);
}
}
/*
+----------+
|root |
| layer 5 |
| group |
| mask 1 |
| paint 4 |
| paint 3 |
| adj |
| paint 2 |
| paint 1 |
+----------+
*/
void KisWalkersTest::testVisitingWithTopmostMask()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP adjustmentLayer = new KisAdjustmentLayer(image, "adj", 0, 0);
KisFilterMaskSP filterMask1 = new KisFilterMask();
filterMask1->initSelection(groupLayer);
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
KIS_ASSERT(filter);
- KisFilterConfigurationSP configuration1 = filter->defaultConfiguration();
- filterMask1->setFilter(configuration1);
+ KisFilterConfigurationSP configuration1 = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
+ filterMask1->setFilter(configuration1->cloneWithResourcesSnapshot());
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(adjustmentLayer, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(paintLayer4, groupLayer);
// nasty mask!
image->addNode(filterMask1, groupLayer);
/**
* The results must be the same as for testUsualVisiting
*/
KisTestWalker walker;
{
QString order("paint3,paint4,group,paint5,root,"
"root_TF,paint5_TA,group_NF,paint1_BB,"
"paint4_TA,paint3_NF,adj_NB,paint2_BB");
QStringList orderList = order.split(',');
reportStartWith("paint3");
walker.startTrip(paintLayer3->projectionLeaf());
QVERIFY(walker.popResult() == orderList);
}
{
QString order("adj,paint3,paint4,group,paint5,root,"
"root_TF,paint5_TA,group_NF,paint1_BB,"
"paint4_TA,paint3_NA,adj_NF,paint2_BB");
QStringList orderList = order.split(',');
reportStartWith("adj");
walker.startTrip(adjustmentLayer->projectionLeaf());
QVERIFY(walker.popResult() == orderList);
}
{
QString order("group,paint5,root,"
"root_TF,paint5_TA,group_NF,paint1_BB");
QStringList orderList = order.split(',');
reportStartWith("group");
walker.startTrip(groupLayer->projectionLeaf());
QVERIFY(walker.popResult() == orderList);
}
}
/*
+----------+
|root |
| layer 5 |
| cplx 2 |
| group |
| paint 4 |
| paint 3 |
| cplx 1 |
| paint 2 |
| paint 1 |
+----------+
*/
void KisWalkersTest::testMergeVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer1 = new ComplexRectsLayer(image, "cplx1", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer2 = new ComplexRectsLayer(image, "cplx2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(complexRectsLayer2, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(complexRectsLayer1, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(paintLayer4, groupLayer);
QRect testRect(10,10,10,10);
// Empty rect to show we don't need any cropping
QRect cropRect;
KisMergeWalker walker(cropRect);
{
QString order("root,paint5,cplx2,group,paint1,"
"paint4,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect(-7,-7,44,44);
reportStartWith("paint3");
walker.collectRects(paintLayer3, testRect);
verifyResult(walker, orderList, accessRect, true, true);
}
{
QString order("root,paint5,cplx2,group,paint1,"
"paint4,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect(-10,-10,50,50);
reportStartWith("paint2");
walker.collectRects(paintLayer2, testRect);
verifyResult(walker, orderList, accessRect, true, true);
}
{
QString order("root,paint5,cplx2,group,paint1");
QStringList orderList = order.split(',');
QRect accessRect(3,3,24,24);
reportStartWith("paint5");
walker.collectRects(paintLayer5, testRect);
verifyResult(walker, orderList, accessRect, false, true);
}
{
/**
* Test cropping
*/
QString order("root,paint5,cplx2,group,paint1,"
"paint4,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect(0,0,40,40);
reportStartWith("paint2 (with cropping)");
walker.setCropRect(image->bounds());
walker.collectRects(paintLayer2, testRect);
walker.setCropRect(cropRect);
verifyResult(walker, orderList, accessRect, true, true);
}
{
/**
* Test uncropped values
*/
QString order("root,paint5,cplx2,group,paint1,"
"paint4,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect cropRect(9,9,12,12);
QRect accessRect(cropRect);
reportStartWith("paint2 (testing uncropped)");
walker.setCropRect(cropRect);
walker.collectRects(paintLayer2, testRect);
walker.setCropRect(cropRect);
verifyResult(walker, orderList, accessRect, true, false);
QCOMPARE(walker.uncroppedChangeRect(), QRect(4,4,22,22));
}
}
#include "kis_psd_layer_style.h"
/*
+----------+
|root |
| group ls |
| paint 3 |
| paint 2 |
| paint 1 |
+----------+
*/
void KisWalkersTest::testComplexGroupVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
{
style->context()->keep_original = true;
style->dropShadow()->setEffectEnabled(true);
style->dropShadow()->setDistance(3);
style->dropShadow()->setSpread(1);
style->dropShadow()->setSize(7);
style->dropShadow()->setNoise(0);
style->dropShadow()->setKnocksOut(false);
style->dropShadow()->setOpacity(50);
style->dropShadow()->setAngle(0);
}
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "groupls", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(paintLayer3, groupLayer);
groupLayer->setLayerStyle(style);
QRect testRect(10,10,10,10);
// Empty rect to show we don't need any cropping
QRect cropRect;
KisMergeWalker walker(cropRect);
{
QString order("root,groupls,paint1,"
"paint3,paint2");
QStringList orderList = order.split(',');
QRect accessRect(-8,-8,46,46);
reportStartWith("paint3");
walker.collectRects(paintLayer3, testRect);
verifyResult(walker, orderList, accessRect, true, true);
}
}
/*
+------------+
|root |
| layer 5 |
| cplx 2 |
| group |
| paint 4 |
| cplxacc 1 |
| paint 3 |
| cplx 1 |
| paint 2 |
| paint 1 |
+------------+
*/
void KisWalkersTest::testComplexAccessVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer1 = new ComplexRectsLayer(image, "cplx1", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer2 = new ComplexRectsLayer(image, "cplx2", OPACITY_OPAQUE_U8);
KisLayerSP complexAccess = new ComplexAccessLayer(image, "cplxacc1", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(complexRectsLayer2, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(complexRectsLayer1, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(complexAccess, groupLayer);
image->addNode(paintLayer4, groupLayer);
QRect testRect(10,10,10,10);
// Empty rect to show we don't need any cropping
QRect cropRect;
KisMergeWalker walker(cropRect);
{
QString order("root,paint5,cplx2,group,paint1,"
"paint4,cplxacc1,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect = QRect(-7,-7,44,44) | QRect(0,0,30,30).translated(70,0);
reportStartWith("paint3");
walker.collectRects(paintLayer3, testRect);
verifyResult(walker, orderList, accessRect, true, true);
}
}
void KisWalkersTest::checkNotification(const KisMergeWalker::CloneNotification &notification,
const QString &name,
const QRect &rect)
{
QCOMPARE(notification.m_layer->name(), name);
QCOMPARE(notification.m_dirtyRect, rect);
}
/*
+--------------+
|root |
| paint 3 <--+ |
| cplx 2 | |
| group <--+ | |
| cplx 1 | | |
| paint 2 | | |
| clone 2 -+ | |
| clone 1 ---+ |
| paint 1 |
+--------------+
*/
void KisWalkersTest::testCloneNotificationsVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer1 = new ComplexRectsLayer(image, "cplx1", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer2 = new ComplexRectsLayer(image, "cplx2", OPACITY_OPAQUE_U8);
KisLayerSP cloneLayer1 = new KisCloneLayer(paintLayer3, image, "clone1", OPACITY_OPAQUE_U8);
KisLayerSP cloneLayer2 = new KisCloneLayer(groupLayer, image, "clone2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(cloneLayer1, image->rootLayer());
image->addNode(cloneLayer2, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(complexRectsLayer2, image->rootLayer());
image->addNode(paintLayer3, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(complexRectsLayer1, groupLayer);
QRect testRect(10,10,10,10);
QRect cropRect(5,5,507,507);
KisMergeWalker walker(cropRect);
{
QString order("root,paint3,cplx2,group,clone2,clone1,paint1,"
"cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect = QRect(5,5,35,35);
reportStartWith("paint2");
walker.collectRects(paintLayer2, testRect);
verifyResult(walker, orderList, accessRect, true, true);
const KisMergeWalker::CloneNotificationsVector vector = walker.cloneNotifications();
QCOMPARE(vector.size(), 1);
checkNotification(vector[0], "group", QRect(7,7,16,16));
}
}
class TestingRefreshSubtreeWalker : public KisRefreshSubtreeWalker
{
public:
TestingRefreshSubtreeWalker(QRect cropRect) : KisRefreshSubtreeWalker(cropRect) {}
UpdateType type() const override { return FULL_REFRESH; }
};
/*
+----------+
|root |
| layer 5 |
| cplx 2 |
| group |
| paint 4 |
| paint 3 |
| cplx 1 |
| paint 2 |
| paint 1 |
+----------+
*/
void KisWalkersTest::testRefreshSubtreeVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer1 = new ComplexRectsLayer(image, "cplx1", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer2 = new ComplexRectsLayer(image, "cplx2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(complexRectsLayer2, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(complexRectsLayer1, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(paintLayer4, groupLayer);
QRect testRect(10,10,10,10);
// Empty rect to show we don't need any cropping
QRect cropRect;
TestingRefreshSubtreeWalker walker(cropRect);
{
QString order("root,paint5,cplx2,group,paint1,"
"paint4,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect(-4,-4,38,38);
reportStartWith("root");
walker.collectRects(image->rootLayer(), testRect);
verifyResult(walker, orderList, accessRect, false, true);
}
}
/*
+----------+
|root |
| layer 5 |
| cplx 2 |
| group |
| paint 4 |
| paint 3 |
| cplx 1 |
| paint 2 |
| paint 1 |
+----------+
*/
void KisWalkersTest::testFullRefreshVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer1 = new ComplexRectsLayer(image, "cplx1", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer2 = new ComplexRectsLayer(image, "cplx2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(complexRectsLayer2, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(complexRectsLayer1, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(paintLayer4, groupLayer);
QRect testRect(10,10,10,10);
// Empty rect to show we don't need any cropping
QRect cropRect;
KisFullRefreshWalker walker(cropRect);
{
QString order("root,paint5,cplx2,group,paint1,"
"group,paint4,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect(-10,-10,50,50);
reportStartWith("root");
walker.collectRects(groupLayer, testRect);
verifyResult(walker, orderList, accessRect, false, true);
}
}
/*
+----------+
|root |
| layer 5 |
| cache1 |
| group |
| paint 4 |
| paint 3 |
| paint 2 |
| paint 1 |
+----------+
*/
void KisWalkersTest::testCachedVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP cacheLayer1 = new CacheLayer(image, "cache1", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(cacheLayer1, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(paintLayer4, groupLayer);
QRect testRect(10,10,10,10);
// Empty rect to show we don't need any cropping
QRect cropRect;
KisMergeWalker walker(cropRect);
{
QString order("root,paint5,cache1,group,paint1,"
"paint4,paint3,paint2");
QStringList orderList = order.split(',');
QRect accessRect(0,0,30,30);
reportStartWith("paint3");
walker.collectRects(paintLayer3, testRect);
verifyResult(walker, orderList, accessRect, true, true);
}
{
QString order("root,paint5,cache1");
QStringList orderList = order.split(',');
QRect accessRect(10,10,10,10);
reportStartWith("paint5");
walker.collectRects(paintLayer5, testRect);
verifyResult(walker, orderList, accessRect, false, true);
}
}
/*
+----------+
|root |
| paint 2 |
| paint 1 |
| fmask2 |
| tmask |
| fmask1 |
+----------+
*/
void KisWalkersTest::testMasksVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(paintLayer2, image->rootLayer());
KisFilterMaskSP filterMask1 = new KisFilterMask();
KisFilterMaskSP filterMask2 = new KisFilterMask();
KisTransparencyMaskSP transparencyMask = new KisTransparencyMask();
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
- KisFilterConfigurationSP configuration1 = filter->defaultConfiguration();
- KisFilterConfigurationSP configuration2 = filter->defaultConfiguration();
+ KisFilterConfigurationSP configuration1 = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
+ KisFilterConfigurationSP configuration2 = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
- filterMask1->setFilter(configuration1);
- filterMask2->setFilter(configuration2);
+ filterMask1->setFilter(configuration1->cloneWithResourcesSnapshot());
+ filterMask2->setFilter(configuration2->cloneWithResourcesSnapshot());
QRect selection1(10, 10, 20, 10);
QRect selection2(30, 15, 10, 10);
QRect selection3(20, 10, 20, 10);
filterMask1->testingInitSelection(selection1, paintLayer1);
transparencyMask->testingInitSelection(selection2, paintLayer1);
filterMask2->testingInitSelection(selection3, paintLayer1);
image->addNode(filterMask1, paintLayer1);
image->addNode(transparencyMask, paintLayer1);
image->addNode(filterMask2, paintLayer1);
QRect testRect(5,5,30,30);
// Empty rect to show we don't need any cropping
QRect cropRect;
KisMergeWalker walker(cropRect);
{
QString order("root,paint2,paint1");
QStringList orderList = order.split(',');
QRect accessRect(0,0,40,40);
reportStartWith("tmask");
walker.collectRects(transparencyMask, testRect);
verifyResult(walker, orderList, accessRect, true, false);
}
KisTestWalker twalker;
{
QString order("paint2,root,"
"root_TF,paint2_TA,paint1_BP");
QStringList orderList = order.split(',');
reportStartWith("tmask");
twalker.startTrip(transparencyMask->projectionLeaf());
QCOMPARE(twalker.popResult(), orderList);
}
}
/*
+----------+
|root |
| paint 2 |
| paint 1 |
| fmask2 |
| tmask |
| fmask1 |
+----------+
*/
void KisWalkersTest::testMasksVisitingNoFilthy()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(paintLayer2, image->rootLayer());
KisFilterMaskSP filterMask1 = new KisFilterMask();
KisFilterMaskSP filterMask2 = new KisFilterMask();
KisTransparencyMaskSP transparencyMask = new KisTransparencyMask();
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
- KisFilterConfigurationSP configuration1 = filter->defaultConfiguration();
- KisFilterConfigurationSP configuration2 = filter->defaultConfiguration();
+ KisFilterConfigurationSP configuration1 = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
+ KisFilterConfigurationSP configuration2 = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
- filterMask1->setFilter(configuration1);
- filterMask2->setFilter(configuration2);
+ filterMask1->setFilter(configuration1->cloneWithResourcesSnapshot());
+ filterMask2->setFilter(configuration2->cloneWithResourcesSnapshot());
QRect selection1(10, 10, 20, 10);
QRect selection2(30, 15, 10, 10);
QRect selection3(20, 10, 20, 10);
filterMask1->testingInitSelection(selection1, paintLayer1);
transparencyMask->testingInitSelection(selection2, paintLayer1);
filterMask2->testingInitSelection(selection3, paintLayer1);
image->addNode(filterMask1, paintLayer1);
image->addNode(transparencyMask, paintLayer1);
image->addNode(filterMask2, paintLayer1);
QRect testRect(5,5,30,30);
// Empty rect to show we don't need any cropping
QRect cropRect;
{
KisMergeWalker walker(cropRect, KisMergeWalker::NO_FILTHY);
QString order("root,paint2,paint1");
QStringList orderList = order.split(',');
QRect accessRect(0,0,40,40);
reportStartWith("tmask");
walker.collectRects(transparencyMask, testRect);
verifyResult(walker, orderList, accessRect, true, false);
}
{
KisMergeWalker walker(cropRect, KisMergeWalker::NO_FILTHY);
QString order("root,paint2,paint1");
QStringList orderList = order.split(',');
QRect accessRect(5,5,30,30);
reportStartWith("paint1");
walker.collectRects(paintLayer1, testRect);
verifyResult(walker, orderList, accessRect, false, false);
}
}
/*
+----------+
|root |
| paint 2 |
| paint 1 |
| fmask2 |
| tmask |
| fmask1 |
+----------+
*/
void KisWalkersTest::testMasksOverlapping()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(paintLayer2, image->rootLayer());
KisFilterMaskSP filterMask1 = new KisFilterMask();
KisFilterMaskSP filterMask2 = new KisFilterMask();
KisTransparencyMaskSP transparencyMask = new KisTransparencyMask();
KisFilterSP blurFilter = KisFilterRegistry::instance()->value("blur");
KisFilterSP invertFilter = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(blurFilter);
Q_ASSERT(invertFilter);
- KisFilterConfigurationSP blurConfiguration = blurFilter->defaultConfiguration();
- KisFilterConfigurationSP invertConfiguration = invertFilter->defaultConfiguration();
+ KisFilterConfigurationSP blurConfiguration = blurFilter->defaultConfiguration(KisGlobalResourcesInterface::instance());
+ KisFilterConfigurationSP invertConfiguration = invertFilter->defaultConfiguration(KisGlobalResourcesInterface::instance());
- filterMask1->setFilter(invertConfiguration);
- filterMask2->setFilter(blurConfiguration);
+ filterMask1->setFilter(invertConfiguration->cloneWithResourcesSnapshot());
+ filterMask2->setFilter(blurConfiguration->cloneWithResourcesSnapshot());
QRect selection1(0, 0, 128, 128);
QRect selection2(128, 0, 128, 128);
QRect selection3(0, 64, 256, 128);
filterMask1->testingInitSelection(selection1, paintLayer1);
transparencyMask->testingInitSelection(selection2, paintLayer1);
filterMask2->testingInitSelection(selection3, paintLayer1);
image->addNode(filterMask1, paintLayer1);
image->addNode(transparencyMask, paintLayer1);
image->addNode(filterMask2, paintLayer1);
// Empty rect to show we don't need any cropping
QRect cropRect;
QRect IMRect(10,10,50,50);
QRect TMRect(135,10,40,40);
QRect IMTMRect(10,10,256,40);
QList<UpdateTestJob> updateList;
{
// FIXME: now we do not crop change rect if COMPOSITE_OVER is used!
UpdateTestJob job = {"IM", paintLayer1, IMRect,
"",
QRect(0,0,0,0), true, false};
updateList.append(job);
}
{
UpdateTestJob job = {"IM", filterMask1, IMRect,
"",
QRect(0,0,0,0), true, false};
updateList.append(job);
}
{
UpdateTestJob job = {"IM", transparencyMask, IMRect,
"root,paint2,paint1",
QRect(5,10,60,55), true, false};
updateList.append(job);
}
{
UpdateTestJob job = {"IM", filterMask2, IMRect,
"root,paint2,paint1",
IMRect, false, false};
updateList.append(job);
}
/******* Dirty rect: transparency mask *********/
{
UpdateTestJob job = {"TM", paintLayer1, TMRect,
"root,paint2,paint1",
TMRect, false, false};
updateList.append(job);
}
{
UpdateTestJob job = {"TM", filterMask1, TMRect,
"root,paint2,paint1",
TMRect, false, false};
updateList.append(job);
}
{
UpdateTestJob job = {"TM", transparencyMask, TMRect,
"root,paint2,paint1",
TMRect, false, false};
updateList.append(job);
}
{
UpdateTestJob job = {"TM", filterMask2, TMRect,
"root,paint2,paint1",
TMRect, false, false};
updateList.append(job);
}
/******* Dirty rect: invert + transparency mask *********/
{
UpdateTestJob job = {"IMTM", paintLayer1, IMTMRect,
"root,paint2,paint1",
IMTMRect & selection2, true, false};
updateList.append(job);
}
{
UpdateTestJob job = {"IMTM", filterMask1, IMTMRect,
"root,paint2,paint1",
IMTMRect & selection2, true, false};
updateList.append(job);
}
{
UpdateTestJob job = {"IMTM", transparencyMask, IMTMRect,
"root,paint2,paint1",
IMTMRect, false, false};
updateList.append(job);
}
{
UpdateTestJob job = {"IMTM", filterMask2, IMTMRect,
"root,paint2,paint1",
IMTMRect, false, false};
updateList.append(job);
}
Q_FOREACH (UpdateTestJob job, updateList) {
KisMergeWalker walker(cropRect);
reportStartWith(job.startNode->name(), job.updateRect);
qDebug() << "Area:" << job.updateAreaName;
walker.collectRects(job.startNode, job.updateRect);
verifyResult(walker, job);
}
}
/*
+----------+
|root |
| adj |
| paint 1 |
+----------+
*/
void KisWalkersTest::testRectsChecksum()
{
QRect imageRect(0,0,512,512);
QRect dirtyRect(100,100,100,100);
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisAdjustmentLayerSP adjustmentLayer = new KisAdjustmentLayer(image, "adj", 0, 0);
image->barrierLock();
image->addNode(paintLayer1, image->rootLayer());
image->addNode(adjustmentLayer, image->rootLayer());
image->unlock();
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
KisFilterConfigurationSP configuration;
KisMergeWalker walker(imageRect);
walker.collectRects(adjustmentLayer, dirtyRect);
QCOMPARE(walker.checksumValid(), true);
- configuration = filter->defaultConfiguration();
- adjustmentLayer->setFilter(configuration);
+ configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
+ adjustmentLayer->setFilter(configuration->cloneWithResourcesSnapshot());
QCOMPARE(walker.checksumValid(), false);
walker.recalculate(dirtyRect);
QCOMPARE(walker.checksumValid(), true);
- configuration = filter->defaultConfiguration();
+ configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
configuration->setProperty("halfWidth", 20);
configuration->setProperty("halfHeight", 20);
- adjustmentLayer->setFilter(configuration);
+ adjustmentLayer->setFilter(configuration->cloneWithResourcesSnapshot());
QCOMPARE(walker.checksumValid(), false);
walker.recalculate(dirtyRect);
QCOMPARE(walker.checksumValid(), true);
- configuration = filter->defaultConfiguration();
+ configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
configuration->setProperty("halfWidth", 21);
configuration->setProperty("halfHeight", 21);
- adjustmentLayer->setFilter(configuration);
+ adjustmentLayer->setFilter(configuration->cloneWithResourcesSnapshot());
QCOMPARE(walker.checksumValid(), false);
walker.recalculate(dirtyRect);
QCOMPARE(walker.checksumValid(), true);
}
void KisWalkersTest::testGraphStructureChecksum()
{
QRect imageRect(0,0,512,512);
QRect dirtyRect(100,100,100,100);
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
image->barrierLock();
image->addNode(paintLayer1, image->rootLayer());
image->unlock();
KisMergeWalker walker(imageRect);
walker.collectRects(paintLayer1, dirtyRect);
QCOMPARE(walker.checksumValid(), true);
image->barrierLock();
image->addNode(paintLayer2, image->rootLayer());
image->unlock();
QCOMPARE(walker.checksumValid(), false);
walker.recalculate(dirtyRect);
QCOMPARE(walker.checksumValid(), true);
image->barrierLock();
image->moveNode(paintLayer1, image->rootLayer(), paintLayer2);
image->unlock();
QCOMPARE(walker.checksumValid(), false);
walker.recalculate(dirtyRect);
QCOMPARE(walker.checksumValid(), true);
image->barrierLock();
image->removeNode(paintLayer1);
image->unlock();
QCOMPARE(walker.checksumValid(), false);
walker.recalculate(dirtyRect);
QCOMPARE(walker.checksumValid(), true);
}
KISTEST_MAIN(KisWalkersTest)
diff --git a/libs/image/tiles3/kis_memento_manager.cc b/libs/image/tiles3/kis_memento_manager.cc
index 2805cfdbbd..1e86e2ddab 100644
--- a/libs/image/tiles3/kis_memento_manager.cc
+++ b/libs/image/tiles3/kis_memento_manager.cc
@@ -1,425 +1,425 @@
/*
* 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 <QtGlobal>
#include "kis_memento_manager.h"
#include "kis_memento.h"
//#define DEBUG_MM
#ifdef DEBUG_MM
#define DEBUG_LOG_TILE_ACTION(action, tile, col, row) \
printf("### MementoManager (0x%X): %s " \
"\ttile:\t0x%X (%d, %d) ###\n", (quintptr)this, action, \
(quintptr)tile, col, row)
#define DEBUG_LOG_SIMPLE_ACTION(action) \
printf("### MementoManager (0x%X): %s\n", (quintptr)this, action)
#define DEBUG_DUMP_MESSAGE(action) do { \
printf("\n### MementoManager (0x%X): %s \t\t##########\n", \
(quintptr)this, action); \
debugPrintInfo(); \
printf("##################################################################\n\n"); \
} while(0)
#else
#define DEBUG_LOG_TILE_ACTION(action, tile, col, row)
#define DEBUG_LOG_SIMPLE_ACTION(action)
#define DEBUG_DUMP_MESSAGE(action)
#endif
/**
* The class is supposed to store the changes of the paint device
* it is associated with. The history of changes is presented in form
* of transactions (revisions). If you purge the history of one
* transaction (revision) with purgeHistory() we won't be able to undo
* the changes made by this transactions.
*
* The Memento Manager can be in two states:
* - Named Transaction is in progress - it means the caller
* has explicitly requested creation of a new transaction.
* The handle for the transaction is stored on a side of
* the caller. And the history will be automatically purged
* when the handler dies.
* - Anonymous Transaction is in progress - the caller isn't
* bothered about transactions at all. We pretend as we do
* not support any versioning and do not have any historical
* information. The history of such transactions is not purged
* automatically, but it is free'd when younger named transaction
* is purged.
*/
#define blockRegistration() (m_registrationBlocked = true)
#define unblockRegistration() (m_registrationBlocked = false)
#define registrationBlocked() (m_registrationBlocked)
#define namedTransactionInProgress() ((bool)m_currentMemento)
KisMementoManager::KisMementoManager()
: m_index(0),
m_headsHashTable(0),
m_registrationBlocked(false)
{
/**
* Tile change/delete registration is enabled for all
* devices by default. It can't be delayed.
*/
}
KisMementoManager::KisMementoManager(const KisMementoManager& rhs)
: m_index(rhs.m_index, 0),
m_revisions(rhs.m_revisions),
m_cancelledRevisions(rhs.m_cancelledRevisions),
m_headsHashTable(rhs.m_headsHashTable, 0),
m_currentMemento(rhs.m_currentMemento),
m_registrationBlocked(rhs.m_registrationBlocked)
{
Q_ASSERT_X(!m_registrationBlocked,
"KisMementoManager", "(impossible happened) "
"The device has been copied while registration was blocked");
}
KisMementoManager::~KisMementoManager()
{
// Nothing to be done here. Happily...
// Everything is done by QList and KisSharedPtr...
DEBUG_LOG_SIMPLE_ACTION("died\n");
}
/**
* NOTE: We don't assume that the registerTileChange/Delete
* can be called once a commit only. Reverse can happen when we
* do sequential clears of the device. In such a case the tiles
* will be removed and added several times during a commit.
*
* TODO: There is an 'uncomfortable' state for the tile possible
* 1) Imagine we have a clear device
* 2) Then we painted something in a tile
* 3) It registered itself using registerTileChange()
* 4) Then we called clear() and getMemento() [==commit()]
* 5) The tile will be registered as deleted and successfully
* committed to a revision. That means the states of the memento
* manager at stages 1 and 5 do not coincide.
* This will not lead to any memory leaks or bugs seen, it just
* not good from a theoretical perspective.
*/
void KisMementoManager::registerTileChange(KisTile *tile)
{
if (registrationBlocked()) return;
DEBUG_LOG_TILE_ACTION("reg. [C]", tile, tile->col(), tile->row());
KisMementoItemSP mi = m_index.getExistingTile(tile->col(), tile->row());
if(!mi) {
mi = new KisMementoItem();
mi->changeTile(tile);
m_index.addTile(mi);
if(namedTransactionInProgress())
m_currentMemento->updateExtent(mi->col(), mi->row());
}
else {
mi->reset();
mi->changeTile(tile);
}
}
void KisMementoManager::registerTileDeleted(KisTile *tile)
{
if (registrationBlocked()) return;
DEBUG_LOG_TILE_ACTION("reg. [D]", tile, tile->col(), tile->row());
KisMementoItemSP mi = m_index.getExistingTile(tile->col(), tile->row());
if(!mi) {
mi = new KisMementoItem();
mi->deleteTile(tile, m_headsHashTable.defaultTileData());
m_index.addTile(mi);
if(namedTransactionInProgress())
m_currentMemento->updateExtent(mi->col(), mi->row());
}
else {
mi->reset();
mi->deleteTile(tile, m_headsHashTable.defaultTileData());
}
}
void KisMementoManager::commit()
{
if (m_index.isEmpty()) {
if(namedTransactionInProgress()) {
//warnTiles << "Named Transaction is empty";
/**
* We still need to continue commit, because
* a named transaction may be reverted by the user
*/
}
else {
m_currentMemento = 0;
return;
}
}
KisMementoItemList revisionList;
KisMementoItemSP mi;
KisMementoItemSP parentMI;
bool newTile;
KisMementoItemHashTableIterator iter(&m_index);
while ((mi = iter.tile())) {
parentMI = m_headsHashTable.getTileLazy(mi->col(), mi->row(), newTile);
mi->setParent(parentMI);
mi->commit();
revisionList.append(mi);
m_headsHashTable.deleteTile(mi->col(), mi->row());
iter.moveCurrentToHashTable(&m_headsHashTable);
//iter.next(); // previous line does this for us
}
KisHistoryItem hItem;
hItem.itemList = revisionList;
hItem.memento = m_currentMemento.data();
m_revisions.append(hItem);
m_currentMemento = 0;
Q_ASSERT(m_index.isEmpty());
DEBUG_DUMP_MESSAGE("COMMIT_DONE");
// Waking up pooler to prepare copies for us
KisTileDataStore::instance()->kickPooler();
}
KisTileSP KisMementoManager::getCommitedTile(qint32 col, qint32 row, bool &existingTile)
{
/**
* Our getOldTile mechanism is supposed to return current
* tile, if the history is disabled. So we return zero if
* no named transaction is in progress.
*/
if(!namedTransactionInProgress())
return KisTileSP();
KisMementoItemSP mi = m_headsHashTable.getReadOnlyTileLazy(col, row, existingTile);
Q_ASSERT(mi);
return mi->tile(0);
}
KisMementoSP KisMementoManager::getMemento()
{
/**
* We do not allow nested transactions
*/
- Q_ASSERT(!namedTransactionInProgress());
+ KIS_SAFE_ASSERT_RECOVER_NOOP(!namedTransactionInProgress());
// Clear redo() information
m_cancelledRevisions.clear();
commit();
m_currentMemento = new KisMemento(this);
DEBUG_LOG_SIMPLE_ACTION("GET_MEMENTO_DONE");
return m_currentMemento;
}
KisMementoSP KisMementoManager::currentMemento() {
return m_currentMemento;
}
#define forEachReversed(iter, list) \
for(iter=list.end(); iter-- != list.begin();)
void KisMementoManager::rollback(KisTileHashTable *ht)
{
commit();
if (! m_revisions.size()) return;
KisHistoryItem changeList = m_revisions.takeLast();
KisMementoItemSP mi;
KisMementoItemSP parentMI;
KisMementoItemList::iterator iter;
blockRegistration();
forEachReversed(iter, changeList.itemList) {
mi=*iter;
parentMI = mi->parent();
if (mi->type() == KisMementoItem::CHANGED)
ht->deleteTile(mi->col(), mi->row());
if (parentMI->type() == KisMementoItem::CHANGED)
ht->addTile(parentMI->tile(this));
m_headsHashTable.deleteTile(parentMI->col(), parentMI->row());
m_headsHashTable.addTile(parentMI);
// This is not necessary
//mi->setParent(0);
}
/**
* NOTE: tricky hack alert.
* We have just deleted some tiles from the original hash table.
* And they accurately reported to us about their death. Should
* have reported... But we have prevented their registration with
* explicitly blocking the process. So all the dead tiles are
* going to /dev/null :)
*
* PS: It could cause some race condition... But we insist on
* serialization of rollback()/rollforward() requests. There is
* not much sense in calling rollback() concurrently.
*/
unblockRegistration();
// We have just emulated a commit so:
m_currentMemento = 0;
Q_ASSERT(!namedTransactionInProgress());
m_cancelledRevisions.prepend(changeList);
DEBUG_DUMP_MESSAGE("UNDONE");
// Waking up pooler to prepare copies for us
KisTileDataStore::instance()->kickPooler();
}
void KisMementoManager::rollforward(KisTileHashTable *ht)
{
Q_ASSERT(m_index.isEmpty());
if (!m_cancelledRevisions.size()) return;
KisHistoryItem changeList = m_cancelledRevisions.takeFirst();
KisMementoItemSP mi;
blockRegistration();
Q_FOREACH (mi, changeList.itemList) {
if (mi->parent()->type() == KisMementoItem::CHANGED)
ht->deleteTile(mi->col(), mi->row());
if (mi->type() == KisMementoItem::CHANGED)
ht->addTile(mi->tile(this));
m_index.addTile(mi);
}
// see comment in rollback()
m_currentMemento = changeList.memento;
commit();
unblockRegistration();
DEBUG_DUMP_MESSAGE("REDONE");
}
void KisMementoManager::purgeHistory(KisMementoSP oldestMemento)
{
if (m_currentMemento == oldestMemento) {
commit();
}
qint32 revisionIndex = findRevisionByMemento(oldestMemento);
if (revisionIndex < 0) return;
for(; revisionIndex > 0; revisionIndex--) {
resetRevisionHistory(m_revisions.first().itemList);
m_revisions.removeFirst();
}
Q_ASSERT(m_revisions.first().memento == oldestMemento);
resetRevisionHistory(m_revisions.first().itemList);
DEBUG_DUMP_MESSAGE("PURGE_HISTORY");
}
qint32 KisMementoManager::findRevisionByMemento(KisMementoSP memento) const
{
qint32 index = -1;
for(qint32 i = 0; i < m_revisions.size(); i++) {
if (m_revisions[i].memento == memento) {
index = i;
break;
}
}
return index;
}
void KisMementoManager::resetRevisionHistory(KisMementoItemList list)
{
KisMementoItemSP parentMI;
KisMementoItemSP mi;
Q_FOREACH (mi, list) {
parentMI = mi->parent();
if(!parentMI) continue;
while (parentMI->parent()) {
parentMI = parentMI->parent();
}
mi->setParent(parentMI);
}
}
void KisMementoManager::setDefaultTileData(KisTileData *defaultTileData)
{
m_headsHashTable.setDefaultTileData(defaultTileData);
m_index.setDefaultTileData(defaultTileData);
}
void KisMementoManager::debugPrintInfo()
{
printf("KisMementoManager stats:\n");
printf("Index list\n");
KisMementoItemSP mi;
KisMementoItemHashTableIteratorConst iter(&m_index);
while ((mi = iter.tile())) {
mi->debugPrintInfo();
iter.next();
}
printf("Revisions list:\n");
qint32 i = 0;
Q_FOREACH (const KisHistoryItem &changeList, m_revisions) {
printf("--- revision #%d ---\n", i++);
Q_FOREACH (mi, changeList.itemList) {
mi->debugPrintInfo();
}
}
printf("\nCancelled revisions list:\n");
i = 0;
Q_FOREACH (const KisHistoryItem &changeList, m_cancelledRevisions) {
printf("--- revision #%d ---\n", m_revisions.size() + i++);
Q_FOREACH (mi, changeList.itemList) {
mi->debugPrintInfo();
}
}
printf("----------------\n");
m_headsHashTable.debugPrintInfo();
}
diff --git a/libs/koplugin/KisMimeDatabase.cpp b/libs/koplugin/KisMimeDatabase.cpp
index 9ca4ab82a8..895e995bf1 100644
--- a/libs/koplugin/KisMimeDatabase.cpp
+++ b/libs/koplugin/KisMimeDatabase.cpp
@@ -1,297 +1,312 @@
/*
* 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-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-windowlayout";
+ mimeType.description = i18nc("description of a file type", "Krita Window Layout");
+ mimeType.suffixes = QStringList() << "kwl";
+ s_mimeDatabase << mimeType;
+
+ mimeType.mimeType = "application/x-krita-session";
+ mimeType.description = i18nc("description of a file type", "Krita Session");
+ mimeType.suffixes = QStringList() << "ksn";
+ 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 = "application/x-krita-gamutmasks";
+ mimeType.description = i18nc("description of a file type", "Krita Gamut Mask");
+ mimeType.suffixes = QStringList() << "kgm";
+ s_mimeDatabase << mimeType;
+
mimeType.mimeType = "application/x-krita-shortcuts";
mimeType.description = i18nc("description of a file type", "Krita Shortcut Scheme");
mimeType.suffixes = QStringList() << "shortcuts";
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;
mimeType.mimeType = "image/jp2";
mimeType.description = i18nc("description of a file type", "JP2 Image");
mimeType.suffixes = QStringList() << "jp2" << "j2k";
s_mimeDatabase << mimeType;
debugPlugin << "Filled mimedatabase with" << s_mimeDatabase.count() << "special mimetypes";
}
}
diff --git a/libs/libkis/Document.cpp b/libs/libkis/Document.cpp
index 7b627a8a40..7700a71c99 100644
--- a/libs/libkis/Document.cpp
+++ b/libs/libkis/Document.cpp
@@ -1,998 +1,999 @@
/*
* 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 "Document.h"
#include <QPointer>
#include <QUrl>
#include <QDomDocument>
#include <KoColorSpaceConstants.h>
#include <KoXmlReader.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <KisPart.h>
#include <kis_paint_device.h>
#include <KisMainWindow.h>
#include <kis_node_manager.h>
#include <kis_node_selection_adapter.h>
#include <KisViewManager.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_filter_mask.h>
#include <kis_transform_mask.h>
#include <kis_transparency_mask.h>
#include <kis_selection_mask.h>
#include <kis_effect_mask.h>
#include <kis_paint_layer.h>
#include <kis_generator_layer.h>
#include <kis_generator_registry.h>
#include <kis_shape_layer.h>
#include <kis_filter_configuration.h>
#include <kis_filter_registry.h>
#include <kis_selection.h>
#include <KisMimeDatabase.h>
#include <kis_filter_strategy.h>
#include <kis_guides_config.h>
#include <kis_coordinates_converter.h>
#include <kis_time_range.h>
#include <KisImportExportErrorCode.h>
#include <KisPart.h>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorProfile.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorConversionTransformation.h>
#include <KoDocumentInfo.h>
+#include <KisGlobalResourcesInterface.h>
#include <InfoObject.h>
#include <Node.h>
#include <Selection.h>
#include <LibKisUtils.h>
#include "kis_animation_importer.h"
#include <kis_canvas2.h>
#include <KoUpdater.h>
#include <QMessageBox>
#include <kis_image_animation_interface.h>
struct Document::Private {
Private() {}
QPointer<KisDocument> document;
bool ownsDocument {false};
};
Document::Document(KisDocument *document, bool ownsDocument, QObject *parent)
: QObject(parent)
, d(new Private)
{
d->document = document;
d->ownsDocument = ownsDocument;
}
Document::~Document()
{
if (d->ownsDocument) {
KisPart::instance()->removeDocument(d->document);
}
delete d;
}
bool Document::operator==(const Document &other) const
{
return (d->document == other.d->document);
}
bool Document::operator!=(const Document &other) const
{
return !(operator==(other));
}
bool Document::batchmode() const
{
if (!d->document) return false;
return d->document->fileBatchMode();
}
void Document::setBatchmode(bool value)
{
if (!d->document) return;
d->document->setFileBatchMode(value);
}
Node *Document::activeNode() const
{
QList<KisNodeSP> activeNodes;
Q_FOREACH(QPointer<KisView> view, KisPart::instance()->views()) {
if (view && view->document() == d->document) {
activeNodes << view->currentNode();
}
}
if (activeNodes.size() > 0) {
QList<Node*> nodes = LibKisUtils::createNodeList(activeNodes, d->document->image());
return nodes.first();
}
return 0;
}
void Document::setActiveNode(Node* value)
{
if (!value->node()) return;
KisMainWindow *mainWin = KisPart::instance()->currentMainwindow();
if (!mainWin) return;
KisViewManager *viewManager = mainWin->viewManager();
if (!viewManager) return;
if (viewManager->document() != d->document) return;
KisNodeManager *nodeManager = viewManager->nodeManager();
if (!nodeManager) return;
KisNodeSelectionAdapter *selectionAdapter = nodeManager->nodeSelectionAdapter();
if (!selectionAdapter) return;
selectionAdapter->setActiveNode(value->node());
}
QList<Node *> Document::topLevelNodes() const
{
if (!d->document) return QList<Node *>();
Node n(d->document->image(), d->document->image()->rootLayer());
return n.childNodes();
}
Node *Document::nodeByName(const QString &name) const
{
if (!d->document) return 0;
KisNodeSP node = d->document->image()->rootLayer()->findChildByName(name);
if (node.isNull()) return 0;
return Node::createNode(d->document->image(), node);
}
QString Document::colorDepth() const
{
if (!d->document) return "";
return d->document->image()->colorSpace()->colorDepthId().id();
}
QString Document::colorModel() const
{
if (!d->document) return "";
return d->document->image()->colorSpace()->colorModelId().id();
}
QString Document::colorProfile() const
{
if (!d->document) return "";
return d->document->image()->colorSpace()->profile()->name();
}
bool Document::setColorProfile(const QString &value)
{
if (!d->document) return false;
if (!d->document->image()) return false;
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileByName(value);
if (!profile) return false;
bool retval = d->document->image()->assignImageProfile(profile);
d->document->image()->waitForDone();
return retval;
}
bool Document::setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile)
{
if (!d->document) return false;
if (!d->document->image()) return false;
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, colorProfile);
if (!colorSpace) return false;
d->document->image()->convertImageColorSpace(colorSpace,
KoColorConversionTransformation::IntentPerceptual,
KoColorConversionTransformation::HighQuality | KoColorConversionTransformation::NoOptimization);
d->document->image()->waitForDone();
return true;
}
QColor Document::backgroundColor()
{
if (!d->document) return QColor();
if (!d->document->image()) return QColor();
const KoColor color = d->document->image()->defaultProjectionColor();
return color.toQColor();
}
bool Document::setBackgroundColor(const QColor &color)
{
if (!d->document) return false;
if (!d->document->image()) return false;
KoColor background = KoColor(color, d->document->image()->colorSpace());
d->document->image()->setDefaultProjectionColor(background);
d->document->image()->setModified();
d->document->image()->initialRefreshGraph();
return true;
}
QString Document::documentInfo() const
{
QDomDocument doc = KisDocument::createDomDocument("document-info"
/*DTD name*/, "document-info" /*tag name*/, "1.1");
doc = d->document->documentInfo()->save(doc);
return doc.toString();
}
void Document::setDocumentInfo(const QString &document)
{
KoXmlDocument doc;
QString errorMsg;
int errorLine, errorColumn;
doc.setContent(document, &errorMsg, &errorLine, &errorColumn);
d->document->documentInfo()->load(doc);
}
QString Document::fileName() const
{
if (!d->document) return QString();
return d->document->url().toLocalFile();
}
void Document::setFileName(QString value)
{
if (!d->document) return;
QString mimeType = KisMimeDatabase::mimeTypeForFile(value, false);
d->document->setMimeType(mimeType.toLatin1());
d->document->setUrl(QUrl::fromLocalFile(value));
}
int Document::height() const
{
if (!d->document) return 0;
KisImageSP image = d->document->image();
if (!image) return 0;
return image->height();
}
void Document::setHeight(int value)
{
if (!d->document) return;
if (!d->document->image()) return;
resizeImage(d->document->image()->bounds().x(),
d->document->image()->bounds().y(),
d->document->image()->width(),
value);
}
QString Document::name() const
{
if (!d->document) return "";
return d->document->documentInfo()->aboutInfo("title");
}
void Document::setName(QString value)
{
if (!d->document) return;
d->document->documentInfo()->setAboutInfo("title", value);
}
int Document::resolution() const
{
if (!d->document) return 0;
KisImageSP image = d->document->image();
if (!image) return 0;
return qRound(d->document->image()->xRes() * 72);
}
void Document::setResolution(int value)
{
if (!d->document) return;
KisImageSP image = d->document->image();
if (!image) return;
d->document->image()->setResolution(value / 72.0, value / 72.0);
}
Node *Document::rootNode() const
{
if (!d->document) return 0;
KisImageSP image = d->document->image();
if (!image) return 0;
return Node::createNode(image, image->root());
}
Selection *Document::selection() const
{
if (!d->document) return 0;
if (!d->document->image()) return 0;
if (!d->document->image()->globalSelection()) return 0;
return new Selection(d->document->image()->globalSelection());
}
void Document::setSelection(Selection* value)
{
if (!d->document) return;
if (!d->document->image()) return;
if (value) {
d->document->image()->setGlobalSelection(value->selection());
}
else {
d->document->image()->setGlobalSelection(0);
}
}
int Document::width() const
{
if (!d->document) return 0;
KisImageSP image = d->document->image();
if (!image) return 0;
return image->width();
}
void Document::setWidth(int value)
{
if (!d->document) return;
if (!d->document->image()) return;
resizeImage(d->document->image()->bounds().x(),
d->document->image()->bounds().y(),
value,
d->document->image()->height());
}
int Document::xOffset() const
{
if (!d->document) return 0;
KisImageSP image = d->document->image();
if (!image) return 0;
return image->bounds().x();
}
void Document::setXOffset(int x)
{
if (!d->document) return;
if (!d->document->image()) return;
resizeImage(x,
d->document->image()->bounds().y(),
d->document->image()->width(),
d->document->image()->height());
}
int Document::yOffset() const
{
if (!d->document) return 0;
KisImageSP image = d->document->image();
if (!image) return 0;
return image->bounds().y();
}
void Document::setYOffset(int y)
{
if (!d->document) return;
if (!d->document->image()) return;
resizeImage(d->document->image()->bounds().x(),
y,
d->document->image()->width(),
d->document->image()->height());
}
double Document::xRes() const
{
if (!d->document) return 0.0;
if (!d->document->image()) return 0.0;
return d->document->image()->xRes()*72.0;
}
void Document::setXRes(double xRes) const
{
if (!d->document) return;
if (!d->document->image()) return;
d->document->image()->setResolution(xRes/72.0, d->document->image()->yRes());
}
double Document::yRes() const
{
if (!d->document) return 0.0;
if (!d->document->image()) return 0.0;
return d->document->image()->yRes()*72.0;
}
void Document::setYRes(double yRes) const
{
if (!d->document) return;
if (!d->document->image()) return;
d->document->image()->setResolution(d->document->image()->xRes(), yRes/72.0);
}
QByteArray Document::pixelData(int x, int y, int w, int h) const
{
QByteArray ba;
if (!d->document) return ba;
KisImageSP image = d->document->image();
if (!image) return ba;
KisPaintDeviceSP dev = image->projection();
ba.resize(w * h * dev->pixelSize());
dev->readBytes(reinterpret_cast<quint8*>(ba.data()), x, y, w, h);
return ba;
}
bool Document::close()
{
bool retval = d->document->closeUrl(false);
Q_FOREACH(KisView *view, KisPart::instance()->views()) {
if (view->document() == d->document) {
view->close();
view->closeView();
view->deleteLater();
}
}
KisPart::instance()->removeDocument(d->document);
d->document = 0;
return retval;
}
void Document::crop(int x, int y, int w, int h)
{
if (!d->document) return;
KisImageSP image = d->document->image();
if (!image) return;
QRect rc(x, y, w, h);
image->cropImage(rc);
}
bool Document::exportImage(const QString &filename, const InfoObject &exportConfiguration)
{
if (!d->document) return false;
const QString outputFormatString = KisMimeDatabase::mimeTypeForFile(filename, false);
const QByteArray outputFormat = outputFormatString.toLatin1();
return d->document->exportDocumentSync(QUrl::fromLocalFile(filename), outputFormat, exportConfiguration.configuration());
}
void Document::flatten()
{
if (!d->document) return;
if (!d->document->image()) return;
d->document->image()->flatten(0);
}
void Document::resizeImage(int x, int y, int w, int h)
{
if (!d->document) return;
KisImageSP image = d->document->image();
if (!image) return;
QRect rc;
rc.setX(x);
rc.setY(y);
rc.setWidth(w);
rc.setHeight(h);
image->resizeImage(rc);
}
void Document::scaleImage(int w, int h, int xres, int yres, QString strategy)
{
if (!d->document) return;
KisImageSP image = d->document->image();
if (!image) return;
QRect rc = image->bounds();
rc.setWidth(w);
rc.setHeight(h);
KisFilterStrategy *actualStrategy = KisFilterStrategyRegistry::instance()->get(strategy);
if (!actualStrategy) actualStrategy = KisFilterStrategyRegistry::instance()->get("Bicubic");
image->scaleImage(rc.size(), xres/72, yres/72, actualStrategy);
}
void Document::rotateImage(double radians)
{
if (!d->document) return;
KisImageSP image = d->document->image();
if (!image) return;
image->rotateImage(radians);
}
void Document::shearImage(double angleX, double angleY)
{
if (!d->document) return;
KisImageSP image = d->document->image();
if (!image) return;
image->shear(angleX, angleY);
}
bool Document::save()
{
if (!d->document) return false;
if (d->document->url().isEmpty()) return false;
bool retval = d->document->save(true, 0);
d->document->waitForSavingToComplete();
return retval;
}
bool Document::saveAs(const QString &filename)
{
if (!d->document) return false;
const QString outputFormatString = KisMimeDatabase::mimeTypeForFile(filename, false);
const QByteArray outputFormat = outputFormatString.toLatin1();
QUrl oldUrl = d->document->url();
d->document->setUrl(QUrl::fromLocalFile(filename));
bool retval = d->document->saveAs(QUrl::fromLocalFile(filename), outputFormat, true);
d->document->waitForSavingToComplete();
d->document->setUrl(oldUrl);
return retval;
}
Node* Document::createNode(const QString &name, const QString &nodeType)
{
if (!d->document) return 0;
if (!d->document->image()) return 0;
KisImageSP image = d->document->image();
Node *node = 0;
if (nodeType.toLower()== "paintlayer") {
node = new Node(image, new KisPaintLayer(image, name, OPACITY_OPAQUE_U8));
}
else if (nodeType.toLower() == "grouplayer") {
node = new Node(image, new KisGroupLayer(image, name, OPACITY_OPAQUE_U8));
}
else if (nodeType.toLower() == "filelayer") {
node = new Node(image, new KisFileLayer(image, name, OPACITY_OPAQUE_U8));
}
else if (nodeType.toLower() == "filterlayer") {
node = new Node(image, new KisAdjustmentLayer(image, name, 0, 0));
}
else if (nodeType.toLower() == "filllayer") {
node = new Node(image, new KisGeneratorLayer(image, name, 0, 0));
}
else if (nodeType.toLower() == "clonelayer") {
node = new Node(image, new KisCloneLayer(0, image, name, OPACITY_OPAQUE_U8));
}
else if (nodeType.toLower() == "vectorlayer") {
node = new Node(image, new KisShapeLayer(d->document->shapeController(), image, name, OPACITY_OPAQUE_U8));
}
else if (nodeType.toLower() == "transparencymask") {
node = new Node(image, new KisTransparencyMask());
}
else if (nodeType.toLower() == "filtermask") {
node = new Node(image, new KisFilterMask());
}
else if (nodeType.toLower() == "transformmask") {
node = new Node(image, new KisTransformMask());
}
else if (nodeType.toLower() == "selectionmask") {
node = new Node(image, new KisSelectionMask(image));
}
return node;
}
GroupLayer *Document::createGroupLayer(const QString &name)
{
if (!d->document) return 0;
if (!d->document->image()) return 0;
KisImageSP image = d->document->image();
return new GroupLayer(image, name);
}
FileLayer *Document::createFileLayer(const QString &name, const QString fileName, const QString scalingMethod)
{
if (!d->document) return 0;
if (!d->document->image()) return 0;
KisImageSP image = d->document->image();
return new FileLayer(image, name, this->fileName(), fileName, scalingMethod);
}
FilterLayer *Document::createFilterLayer(const QString &name, Filter &filter, Selection &selection)
{
if (!d->document) return 0;
if (!d->document->image()) return 0;
KisImageSP image = d->document->image();
return new FilterLayer(image, name, filter, selection);
}
FillLayer *Document::createFillLayer(const QString &name, const QString generatorName, InfoObject &configuration, Selection &selection)
{
if (!d->document) return 0;
if (!d->document->image()) return 0;
KisImageSP image = d->document->image();
KisGeneratorSP generator = KisGeneratorRegistry::instance()->value(generatorName);
if (generator) {
- KisFilterConfigurationSP config = generator->factoryConfiguration();
+ KisFilterConfigurationSP config = generator->factoryConfiguration(KisGlobalResourcesInterface::instance());
Q_FOREACH(const QString property, configuration.properties().keys()) {
config->setProperty(property, configuration.property(property));
}
return new FillLayer(image, name, config, selection);
}
return 0;
}
CloneLayer *Document::createCloneLayer(const QString &name, const Node *source)
{
if (!d->document) return 0;
if (!d->document->image()) return 0;
KisImageSP image = d->document->image();
KisLayerSP layer = qobject_cast<KisLayer*>(source->node().data());
return new CloneLayer(image, name, layer);
}
VectorLayer *Document::createVectorLayer(const QString &name)
{
if (!d->document) return 0;
if (!d->document->image()) return 0;
KisImageSP image = d->document->image();
return new VectorLayer(d->document->shapeController(), image, name);
}
FilterMask *Document::createFilterMask(const QString &name, Filter &filter, const Node *selection_source)
{
if (!d->document)
return 0;
if (!d->document->image())
return 0;
if(!selection_source)
return 0;
KisLayerSP layer = qobject_cast<KisLayer*>(selection_source->node().data());
if(layer.isNull())
return 0;
KisImageSP image = d->document->image();
FilterMask* mask = new FilterMask(image, name, filter);
qobject_cast<KisMask*>(mask->node().data())->initSelection(layer);
return mask;
}
FilterMask *Document::createFilterMask(const QString &name, Filter &filter, Selection &selection)
{
if (!d->document)
return 0;
if (!d->document->image())
return 0;
KisImageSP image = d->document->image();
FilterMask* mask = new FilterMask(image, name, filter);
qobject_cast<KisMask*>(mask->node().data())->setSelection(selection.selection());
return mask;
}
SelectionMask *Document::createSelectionMask(const QString &name)
{
if (!d->document) return 0;
if (!d->document->image()) return 0;
KisImageSP image = d->document->image();
return new SelectionMask(image, name);
}
QImage Document::projection(int x, int y, int w, int h) const
{
if (!d->document || !d->document->image()) return QImage();
return d->document->image()->convertToQImage(x, y, w, h, 0);
}
QImage Document::thumbnail(int w, int h) const
{
if (!d->document || !d->document->image()) return QImage();
return d->document->generatePreview(QSize(w, h)).toImage();
}
void Document::lock()
{
if (!d->document || !d->document->image()) return;
d->document->image()->barrierLock();
}
void Document::unlock()
{
if (!d->document || !d->document->image()) return;
d->document->image()->unlock();
}
void Document::waitForDone()
{
if (!d->document || !d->document->image()) return;
d->document->image()->waitForDone();
}
bool Document::tryBarrierLock()
{
if (!d->document || !d->document->image()) return false;
return d->document->image()->tryBarrierLock();
}
bool Document::isIdle()
{
if (!d->document || !d->document->image()) return false;
return d->document->image()->isIdle();
}
void Document::refreshProjection()
{
if (!d->document || !d->document->image()) return;
d->document->image()->refreshGraph();
}
QList<qreal> Document::horizontalGuides() const
{
QList<qreal> lines;
if (!d->document || !d->document->image()) return lines;
KisCoordinatesConverter converter;
converter.setImage(d->document->image());
QTransform transform = converter.imageToDocumentTransform().inverted();
QList<qreal> untransformedLines = d->document->guidesConfig().horizontalGuideLines();
for (int i = 0; i< untransformedLines.size(); i++) {
qreal line = untransformedLines[i];
lines.append(transform.map(QPointF(line, line)).x());
}
return lines;
}
QList<qreal> Document::verticalGuides() const
{
QList<qreal> lines;
if (!d->document || !d->document->image()) return lines;
KisCoordinatesConverter converter;
converter.setImage(d->document->image());
QTransform transform = converter.imageToDocumentTransform().inverted();
QList<qreal> untransformedLines = d->document->guidesConfig().verticalGuideLines();
for (int i = 0; i< untransformedLines.size(); i++) {
qreal line = untransformedLines[i];
lines.append(transform.map(QPointF(line, line)).y());
}
return lines;
}
bool Document::guidesVisible() const
{
return d->document->guidesConfig().showGuides();
}
bool Document::guidesLocked() const
{
return d->document->guidesConfig().lockGuides();
}
Document *Document::clone() const
{
if (!d->document) return 0;
QPointer<KisDocument> clone = d->document->clone();
Document * newDocument = new Document(clone, d->ownsDocument);
clone->setParent(newDocument); // It's owned by the document, not KisPart
return newDocument;
}
void Document::setHorizontalGuides(const QList<qreal> &lines)
{
if (!d->document) return;
KisGuidesConfig config = d->document->guidesConfig();
KisCoordinatesConverter converter;
converter.setImage(d->document->image());
QTransform transform = converter.imageToDocumentTransform();
QList<qreal> transformedLines;
for (int i = 0; i< lines.size(); i++) {
qreal line = lines[i];
transformedLines.append(transform.map(QPointF(line, line)).x());
}
config.setHorizontalGuideLines(transformedLines);
d->document->setGuidesConfig(config);
}
void Document::setVerticalGuides(const QList<qreal> &lines)
{
if (!d->document) return;
KisGuidesConfig config = d->document->guidesConfig();
KisCoordinatesConverter converter;
converter.setImage(d->document->image());
QTransform transform = converter.imageToDocumentTransform();
QList<qreal> transformedLines;
for (int i = 0; i< lines.size(); i++) {
qreal line = lines[i];
transformedLines.append(transform.map(QPointF(line, line)).y());
}
config.setVerticalGuideLines(transformedLines);
d->document->setGuidesConfig(config);
}
void Document::setGuidesVisible(bool visible)
{
if (!d->document) return;
KisGuidesConfig config = d->document->guidesConfig();
config.setShowGuides(visible);
d->document->setGuidesConfig(config);
}
void Document::setGuidesLocked(bool locked)
{
if (!d->document) return;
KisGuidesConfig config = d->document->guidesConfig();
config.setLockGuides(locked);
d->document->setGuidesConfig(config);
}
bool Document::modified() const
{
if (!d->document) return false;
return d->document->isModified();
}
QRect Document::bounds() const
{
if (!d->document) return QRect();
return d->document->image()->bounds();
}
QPointer<KisDocument> Document::document() const
{
return d->document;
}
/* Animation related function */
bool Document::importAnimation(const QList<QString> &files, int firstFrame, int step)
{
KisView *activeView = KisPart::instance()->currentMainwindow()->activeView();
KoUpdaterPtr updater = 0;
if (activeView && d->document->fileBatchMode()) {
updater = activeView->viewManager()->createUnthreadedUpdater(i18n("Import frames"));
}
KisAnimationImporter importer(d->document->image(), updater);
KisImportExportErrorCode status = importer.import(files, firstFrame, step);
return status.isOk();
}
int Document::framesPerSecond()
{
if (!d->document) return false;
if (!d->document->image()) return false;
return d->document->image()->animationInterface()->framerate();
}
void Document::setFramesPerSecond(int fps)
{
if (!d->document) return;
if (!d->document->image()) return;
d->document->image()->animationInterface()->setFramerate(fps);
}
void Document::setFullClipRangeStartTime(int startTime)
{
if (!d->document) return;
if (!d->document->image()) return;
d->document->image()->animationInterface()->setFullClipRangeStartTime(startTime);
}
int Document::fullClipRangeStartTime()
{
if (!d->document) return false;
if (!d->document->image()) return false;
return d->document->image()->animationInterface()->fullClipRange().start();
}
void Document::setFullClipRangeEndTime(int endTime)
{
if (!d->document) return;
if (!d->document->image()) return;
d->document->image()->animationInterface()->setFullClipRangeEndTime(endTime);
}
int Document::fullClipRangeEndTime()
{
if (!d->document) return false;
if (!d->document->image()) return false;
return d->document->image()->animationInterface()->fullClipRange().end();
}
int Document::animationLength()
{
if (!d->document) return false;
if (!d->document->image()) return false;
return d->document->image()->animationInterface()->totalLength();
}
void Document::setPlayBackRange(int start, int stop)
{
if (!d->document) return;
if (!d->document->image()) return;
const KisTimeRange newTimeRange = KisTimeRange(start, (stop-start));
d->document->image()->animationInterface()->setPlaybackRange(newTimeRange);
}
int Document::playBackStartTime()
{
if (!d->document) return false;
if (!d->document->image()) return false;
return d->document->image()->animationInterface()->playbackRange().start();
}
int Document::playBackEndTime()
{
if (!d->document) return false;
if (!d->document->image()) return false;
return d->document->image()->animationInterface()->playbackRange().end();
}
int Document::currentTime()
{
if (!d->document) return false;
if (!d->document->image()) return false;
return d->document->image()->animationInterface()->currentTime();
}
void Document::setCurrentTime(int time)
{
if (!d->document) return;
if (!d->document->image()) return;
return d->document->image()->animationInterface()->requestTimeSwitchWithUndo(time);
}
diff --git a/libs/libkis/FillLayer.cpp b/libs/libkis/FillLayer.cpp
index a4efb92518..1184182771 100644
--- a/libs/libkis/FillLayer.cpp
+++ b/libs/libkis/FillLayer.cpp
@@ -1,74 +1,75 @@
/*
* 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.
*/
#include "FillLayer.h"
#include <kis_generator_layer.h>
#include <kis_image.h>
#include <kis_filter_configuration.h>
#include <kis_generator_registry.h>
#include <InfoObject.h>
#include <kis_selection.h>
+#include <KisGlobalResourcesInterface.h>
FillLayer::FillLayer(KisImageSP image, QString name, KisFilterConfigurationSP filterConfig, Selection &selection, QObject *parent) :
- Node(image, new KisGeneratorLayer(image, name, filterConfig, selection.selection()), parent)
+ Node(image, new KisGeneratorLayer(image, name, filterConfig->cloneWithResourcesSnapshot(), selection.selection()), parent)
{
}
FillLayer::FillLayer(KisGeneratorLayerSP layer, QObject *parent):
Node(layer->image(), layer, parent)
{
}
FillLayer::~FillLayer()
{
}
QString FillLayer::generatorName()
{
const KisGeneratorLayer *layer = qobject_cast<const KisGeneratorLayer*>(this->node());
return layer->filter()->name();
}
InfoObject * FillLayer::filterConfig()
{
const KisGeneratorLayer *layer = qobject_cast<const KisGeneratorLayer*>(this->node());
return new InfoObject(layer->filter());
}
QString FillLayer::type() const
{
return "filllayer";
}
bool FillLayer::setGenerator(const QString &generatorName, InfoObject *config)
{
KisGeneratorLayer *layer = dynamic_cast<KisGeneratorLayer*>(this->node().data());
//getting the default configuration here avoids trouble with versioning.
KisGeneratorSP generator = KisGeneratorRegistry::instance()->value(generatorName);
if (generator) {
- KisFilterConfigurationSP cfg = generator->factoryConfiguration();
+ KisFilterConfigurationSP cfg = generator->factoryConfiguration(KisGlobalResourcesInterface::instance());
Q_FOREACH(const QString property, config->properties().keys()) {
cfg->setProperty(property, config->property(property));
}
- layer->setFilter(cfg);
+ layer->setFilter(cfg->cloneWithResourcesSnapshot());
return true;
}
return false;
}
diff --git a/libs/libkis/Filter.cpp b/libs/libkis/Filter.cpp
index f20a5e613a..69b8537b99 100644
--- a/libs/libkis/Filter.cpp
+++ b/libs/libkis/Filter.cpp
@@ -1,174 +1,175 @@
/*
* 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 "Filter.h"
#include <KoCanvasResourceProvider.h>
#include <kis_canvas_resource_provider.h>
#include <kis_filter.h>
#include <kis_properties_configuration.h>
#include <kis_filter_configuration.h>
#include <kis_filter_manager.h>
#include <kis_filter_registry.h>
#include <KisPart.h>
#include <KisView.h>
#include <strokes/kis_filter_stroke_strategy.h>
#include <krita_utils.h>
+#include <KisGlobalResourcesInterface.h>
#include "Krita.h"
#include "Document.h"
#include "InfoObject.h"
#include "Node.h"
struct Filter::Private {
Private() {}
QString name;
InfoObject *configuration {0};
};
Filter::Filter()
: QObject(0)
, d(new Private)
{
}
Filter::~Filter()
{
delete d->configuration;
delete d;
}
bool Filter::operator==(const Filter &other) const
{
return (d->name == other.d->name
&& d->configuration == other.d->configuration);
}
bool Filter::operator!=(const Filter &other) const
{
return !(operator==(other));
}
QString Filter::name() const
{
return d->name;
}
void Filter::setName(const QString &name)
{
d->name = name;
delete d->configuration;
KisFilterSP filter = KisFilterRegistry::instance()->value(d->name);
- d->configuration = new InfoObject(filter->defaultConfiguration());
+ d->configuration = new InfoObject(filter->defaultConfiguration(KisGlobalResourcesInterface::instance()));
}
InfoObject* Filter::configuration() const
{
return d->configuration;
}
void Filter::setConfiguration(InfoObject* value)
{
d->configuration = value;
}
bool Filter::apply(Node *node, int x, int y, int w, int h)
{
if (node->locked()) return false;
KisFilterSP filter = KisFilterRegistry::instance()->value(d->name);
if (!filter) return false;
KisPaintDeviceSP dev = node->paintDevice();
if (!dev) return false;
QRect applyRect = QRect(x, y, w, h);
KisFilterConfigurationSP config = static_cast<KisFilterConfiguration*>(d->configuration->configuration().data());
filter->process(dev, applyRect, config);
return true;
}
bool Filter::startFilter(Node *node, int x, int y, int w, int h)
{
if (node->locked()) return false;
KisFilterSP filter = KisFilterRegistry::instance()->value(d->name);
if (!filter) return false;
KisImageWSP image = node->image();
if (!image) return false;
KisFilterConfigurationSP filterConfig = static_cast<KisFilterConfiguration*>(d->configuration->configuration().data());
image->waitForDone();
QRect initialApplyRect = QRect(x, y, w, h);
QRect applyRect = initialApplyRect;
KisPaintDeviceSP paintDevice = node->paintDevice();
if (paintDevice && filter->needsTransparentPixels(filterConfig.data(), paintDevice->colorSpace())) {
applyRect |= image->bounds();
}
KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image, node->node());
Document *document = Krita::instance()->activeDocument();
if (document && KisPart::instance()->viewCount(document->document()) > 0) {
Q_FOREACH (QPointer<KisView> view, KisPart::instance()->views()) {
if (view && view->document() == document->document()) {
resources = new KisResourcesSnapshot(image, node->node(), view->resourceProvider()->resourceManager());
break;
}
}
}
delete document;
KisStrokeId currentStrokeId = image->startStroke(new KisFilterStrokeStrategy(filter,
KisFilterConfigurationSP(filterConfig),
resources));
QRect processRect = filter->changedRect(applyRect, filterConfig.data(), 0);
processRect &= image->bounds();
if (filter->supportsThreading()) {
QSize size = KritaUtils::optimalPatchSize();
QVector<QRect> rects = KritaUtils::splitRectIntoPatches(processRect, size);
Q_FOREACH (const QRect &rc, rects) {
image->addJob(currentStrokeId, new KisFilterStrokeStrategy::Data(rc, true));
}
} else {
image->addJob(currentStrokeId, new KisFilterStrokeStrategy::Data(processRect, false));
}
image->endStroke(currentStrokeId);
image->waitForDone();
return true;
}
KisFilterConfigurationSP Filter::filterConfig()
{
- KisFilterConfigurationSP config = KisFilterRegistry::instance()->get(d->name)->factoryConfiguration();
+ KisFilterConfigurationSP config = KisFilterRegistry::instance()->get(d->name)->factoryConfiguration(KisGlobalResourcesInterface::instance());
Q_FOREACH(const QString property, d->configuration->properties().keys()) {
config->setProperty(property, d->configuration->property(property));
}
return config;
}
diff --git a/libs/libkis/FilterLayer.cpp b/libs/libkis/FilterLayer.cpp
index 46373d49ec..51b51ba316 100644
--- a/libs/libkis/FilterLayer.cpp
+++ b/libs/libkis/FilterLayer.cpp
@@ -1,66 +1,66 @@
/*
* 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.
*/
#include "FilterLayer.h"
#include <kis_adjustment_layer.h>
#include <kis_image.h>
#include <kis_filter_configuration.h>
#include <kis_filter_registry.h>
#include <InfoObject.h>
#include <kis_selection.h>
FilterLayer::FilterLayer(KisImageSP image, QString name, Filter &filter, Selection &selection, QObject *parent) :
- Node(image, new KisAdjustmentLayer(image, name, filter.filterConfig(), selection.selection()), parent)
+ Node(image, new KisAdjustmentLayer(image, name, filter.filterConfig()->cloneWithResourcesSnapshot(), selection.selection()), parent)
{
}
FilterLayer::FilterLayer(KisAdjustmentLayerSP layer, QObject *parent):
Node(layer->image(), layer, parent)
{
}
FilterLayer::~FilterLayer()
{
}
QString FilterLayer::type() const
{
return "filterlayer";
}
void FilterLayer::setFilter(Filter &filter)
{
if (!this->node()) return;
KisAdjustmentLayer *layer = dynamic_cast<KisAdjustmentLayer*>(this->node().data());
//getting the default configuration here avoids trouble with versioning.
if (layer) {
- layer->setFilter(filter.filterConfig());
+ layer->setFilter(filter.filterConfig()->cloneWithResourcesSnapshot());
}
}
Filter * FilterLayer::filter()
{
Filter* filter = new Filter();
const KisAdjustmentLayer *layer = qobject_cast<const KisAdjustmentLayer*>(this->node());
filter->setName(layer->filter()->name());
filter->setConfiguration(new InfoObject(layer->filter()));
return filter;
}
diff --git a/libs/libkis/FilterMask.cpp b/libs/libkis/FilterMask.cpp
index 70b151fad2..b027073194 100644
--- a/libs/libkis/FilterMask.cpp
+++ b/libs/libkis/FilterMask.cpp
@@ -1,63 +1,63 @@
/*
* 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.
*/
#include "FilterMask.h"
#include <kis_filter_mask.h>
#include <kis_image.h>
#include <kis_filter_configuration.h>
#include <kis_filter_registry.h>
#include <InfoObject.h>
FilterMask::FilterMask(KisImageSP image, QString name, Filter &filter, QObject *parent) :
Node(image, new KisFilterMask(), parent)
{
this->node()->setName(name);
KisFilterMask *mask = dynamic_cast<KisFilterMask*>(this->node().data());
- mask->setFilter(filter.filterConfig());
+ mask->setFilter(filter.filterConfig()->cloneWithResourcesSnapshot());
}
FilterMask::FilterMask(KisImageSP image, KisFilterMaskSP mask, QObject *parent):
Node(image, mask, parent)
{
}
FilterMask::~FilterMask()
{
}
QString FilterMask::type() const
{
return "filtermask";
}
void FilterMask::setFilter(Filter &filter)
{
KisFilterMask *mask = dynamic_cast<KisFilterMask*>(this->node().data());
- mask->setFilter(filter.filterConfig());
+ mask->setFilter(filter.filterConfig()->cloneWithResourcesSnapshot());
}
Filter * FilterMask::filter()
{
Filter* filter = new Filter();
const KisFilterMask *mask = qobject_cast<const KisFilterMask*>(this->node());
filter->setName(mask->filter()->name());
filter->setConfiguration(new InfoObject(mask->filter()));
return filter;
}
diff --git a/libs/libkis/Krita.cpp b/libs/libkis/Krita.cpp
index a0d9bc9534..8e6b57bcb3 100644
--- a/libs/libkis/Krita.cpp
+++ b/libs/libkis/Krita.cpp
@@ -1,423 +1,400 @@
/*
* 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 <KisBrushServerProvider.h>
#include <kis_action_registry.h>
#include <kis_icon_utils.h>
+#include <KisResourceModel.h>
+#include <KisResourceModelProvider.h>
+#include <KisGlobalResourcesInterface.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(sigMainWindowIsBeingCreated(KisMainWindow*)), SLOT(mainWindowIsBeingCreated(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, false);
}
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, false);
}
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();
+ KisFilterConfigurationSP fc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
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();
+ return QStringList(colorModelsIds.begin(), colorModelsIds.end());
}
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();
+ return QStringList(colorDepthsIds.begin(), colorDepthsIds.end());
}
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();
+ QStringList r(profileNames.begin(), profileNames.end());
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*> Krita::resources(const QString &type) const
{
- QMap<QString, Resource *> resources = QMap<QString, Resource *> ();
+ QMap<QString, Resource*> resources;
+ KisResourceModel *resourceModel = KisResourceModelProvider::resourceModel(type);
+ for (int i = 0; i < resourceModel->rowCount(); ++i) {
- 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);
- }
+ QModelIndex idx = resourceModel->index(i, 0);
+ int id = resourceModel->data(idx, Qt::UserRole + KisResourceModel::Id).toInt();
+ QString name = resourceModel->data(idx, Qt::UserRole + KisResourceModel::Name).toString();
+ QString filename = resourceModel->data(idx, Qt::UserRole + KisResourceModel::Filename).toString();
+ QImage image = resourceModel->data(idx, Qt::UserRole + KisResourceModel::Thumbnail).value<QImage>();
+
+ resources[name] = new Resource(id, type, name, filename, image, 0);
}
+
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, KisConfig::RASTER_LAYER, 1, "", double(resolution / 72) )) {
return 0;
}
Q_ASSERT(document->image());
return new Document(document, true);
}
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, true);
}
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::mainWindowIsBeingCreated(KisMainWindow *kisWindow)
{
Q_FOREACH(Extension *extension, d->extensions) {
Window window(kisWindow);
extension->createActions(&window);
}
}
diff --git a/libs/libkis/Palette.cpp b/libs/libkis/Palette.cpp
index 568331976c..0b5bf8b3ea 100644
--- a/libs/libkis/Palette.cpp
+++ b/libs/libkis/Palette.cpp
@@ -1,155 +1,155 @@
/*
* 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.
*/
#include "Palette.h"
#include <KoColorSet.h>
#include <KisSwatch.h>
#include <KisSwatchGroup.h>
#include <ManagedColor.h>
#include <KisPaletteModel.h>
struct Palette::Private {
- KoColorSet *palette {0};
+ KoColorSetSP palette {0};
};
Palette::Palette(Resource *resource): d(new Private()) {
- d->palette = dynamic_cast<KoColorSet*>(resource->resource());
+ d->palette = resource->resource().dynamicCast<KoColorSet>();
}
Palette::~Palette()
{
delete d;
}
int Palette::numberOfEntries() const
{
if (!d->palette) return 0;
return d->palette->colorCount();
}
int Palette::columnCount()
{
if (!d->palette) return 0;
return d->palette->columnCount();
}
void Palette::setColumnCount(int columns)
{
if (d->palette)
d->palette->setColumnCount(columns);
}
QString Palette::comment()
{
if (!d->palette) return "";
return d->palette->comment();
}
void Palette::setComment(QString comment)
{
if (!d->palette) return;
return d->palette->setComment(comment);
}
QStringList Palette::groupNames() const
{
if (!d->palette) return QStringList();
return d->palette->getGroupNames();
}
bool Palette::addGroup(QString name)
{
if (!d->palette) return false;
return d->palette->addGroup(name);
}
bool Palette::removeGroup(QString name, bool keepColors)
{
if (!d->palette) return false;
return d->palette->removeGroup(name, keepColors);
}
int Palette::colorsCountTotal()
{
if (!d->palette) return 0;
return d->palette->colorCount();
}
Swatch *Palette::colorSetEntryByIndex(int index)
{
if (!d->palette) return new Swatch();
int col = index % columnCount();
int row = (index - col) / columnCount();
return new Swatch(d->palette->getColorGlobal(col, row));
}
Swatch *Palette::colorSetEntryFromGroup(int index, const QString &groupName)
{
if (!d->palette) return new Swatch();
int row = index % columnCount();
return new Swatch(d->palette->getColorGroup((index - row) / columnCount(), row, groupName));
}
void Palette::addEntry(Swatch entry, QString groupName)
{
d->palette->add(entry.kisSwatch(), groupName);
}
void Palette::removeEntry(int index, const QString &/*groupName*/)
{
int col = index % columnCount();
int tmp = index;
int row = (index - col) / columnCount();
KisSwatchGroup *groupFoundIn = 0;
Q_FOREACH(const QString &name, groupNames()) {
KisSwatchGroup *g = d->palette->getGroup(name);
tmp -= g->rowCount() * columnCount();
if (tmp < 0) {
groupFoundIn = g;
break;
}
row -= g->rowCount();
}
if (!groupFoundIn) { return; }
groupFoundIn->removeEntry(col, row);
}
bool Palette::changeGroupName(QString oldGroupName, QString newGroupName)
{
return d->palette->changeGroupName(oldGroupName, newGroupName);
}
bool Palette::moveGroup(const QString &groupName, const QString &groupNameInsertBefore)
{
return d->palette->moveGroup(groupName, groupNameInsertBefore);
}
bool Palette::save()
{
if (d->palette->filename().size()>0) {
return d->palette->save();
}
//if there's no filename the palette proly doesn't even exist...
return false;
}
-KoColorSet *Palette::colorSet()
+KoColorSetSP Palette::colorSet()
{
return d->palette;
}
diff --git a/libs/libkis/Palette.h b/libs/libkis/Palette.h
index 2d853240b5..19124eb612 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 @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();
+ KoColorSetSP colorSet();
};
#endif // LIBKIS_PALETTE_H
diff --git a/libs/libkis/PresetChooser.cpp b/libs/libkis/PresetChooser.cpp
index d865215b96..4c932e6272 100644
--- a/libs/libkis/PresetChooser.cpp
+++ b/libs/libkis/PresetChooser.cpp
@@ -1,55 +1,53 @@
/*
* 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 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 "PresetChooser.h"
#include <KoResource.h>
#include <kis_config.h>
#include "Resource.h"
PresetChooser::PresetChooser(QWidget *parent)
: KisPresetChooser(parent)
{
- connect(this, SIGNAL(resourceSelected(KoResource*)), SLOT(slotResourceSelected(KoResource*)));
- connect(this, SIGNAL(resourceClicked(KoResource*)), SLOT(slotResourceClicked(KoResource*)));
+ connect(this, SIGNAL(resourceSelected(KoResourceSP )), SLOT(slotResourceSelected(KoResourceSP )));
+ connect(this, SIGNAL(resourceClicked(KoResourceSP )), SLOT(slotResourceClicked(KoResourceSP )));
showTaggingBar(true);
}
void PresetChooser::setCurrentPreset(Resource *resource)
{
- KoResource *r = resource->resource();
+ KoResourceSP r = resource->resource();
setCurrentResource(r);
}
Resource *PresetChooser::currentPreset() const
{
- KoResource *r = currentResource();
- return new Resource(r);
+ KoResourceSP r = currentResource();
+ return new Resource(r->resourceId(), "paintoppreset", r->name(), r->filename(), r->image());
}
-void PresetChooser::slotResourceSelected(KoResource *resource)
+void PresetChooser::slotResourceSelected(KoResourceSP r)
{
- Resource *r = new Resource(resource);
- emit presetSelected(r);
+ emit presetSelected(Resource(r->resourceId(), "paintoppreset", r->name(), r->filename(), r->image()));
}
-void PresetChooser::slotResourceClicked(KoResource *resource)
+void PresetChooser::slotResourceClicked(KoResourceSP r)
{
- Resource *r = new Resource(resource);
- emit presetClicked(r);
+ emit presetClicked(Resource(r->resourceId(), "paintoppreset", r->name(), r->filename(), r->image()));
}
diff --git a/libs/libkis/PresetChooser.h b/libs/libkis/PresetChooser.h
index 4e835ffa55..ed1ff8676b 100644
--- a/libs/libkis/PresetChooser.h
+++ b/libs/libkis/PresetChooser.h
@@ -1,75 +1,75 @@
/*
* 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 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 PRESETCHOOSER_H
#define PRESETCHOOSER_H
#include <QObject>
#include <QWidget>
#include <kis_preset_chooser.h>
#include "kritalibkis_export.h"
#include "libkis.h"
class Resource;
/**
* @brief The PresetChooser widget wraps the KisPresetChooser widget.
* The widget provides for selecting brush presets. It has a tagging
* bar and a filter field. It is not automatically synchronized with
* the currently selected preset in the current Windows.
*/
class KRITALIBKIS_EXPORT PresetChooser : public KisPresetChooser
{
Q_OBJECT
public:
PresetChooser(QWidget *parent = 0);
~PresetChooser() override {}
public Q_SLOTS:
/**
* Make the given preset active.
*/
void setCurrentPreset(Resource *resource);
/**
* @return a Resource wrapper around the currently selected
* preset.
*/
Resource *currentPreset() const;
Q_SIGNALS:
/**
* Emitted whenever a user selects the given preset.
*/
- void presetSelected(Resource *resource);
+ void presetSelected(Resource resource);
/**
* Emitted whenever a user clicks on the given preset.
*/
- void presetClicked(Resource *resource);
+ void presetClicked(Resource resource);
private Q_SLOTS:
- void slotResourceSelected(KoResource *resource);
- void slotResourceClicked(KoResource *resource);
+ void slotResourceSelected(KoResourceSP resource);
+ void slotResourceClicked(KoResourceSP resource);
};
#endif // PRESETCHOOSER_H
diff --git a/libs/libkis/Resource.cpp b/libs/libkis/Resource.cpp
index d773f367c7..28b7458c5c 100644
--- a/libs/libkis/Resource.cpp
+++ b/libs/libkis/Resource.cpp
@@ -1,130 +1,138 @@
/*
* 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 "Resource.h"
#include <KoResource.h>
#include <QByteArray>
#include <QBuffer>
#include <KoPattern.h>
#include <KoAbstractGradient.h>
#include <kis_brush.h>
#include <kis_paintop_preset.h>
#include <KoColorSet.h>
#include <kis_workspace_resource.h>
+#include <KisResourceLocator.h>
struct Resource::Private {
- Private(KoResource *_resource)
- : resource(_resource)
- {}
+ Private() {}
- KoResource *resource {0};
+ int id {-1};
+ QString type;
+ QString name;
+ QString filename;
+ QImage image;
};
-Resource::Resource(KoResource *resource, QObject *parent)
+Resource::Resource(int resourceId, const QString &type, const QString &name, const QString &filename, const QImage &image, QObject *parent)
: QObject(parent)
- , d(new Private(resource))
+ , d(new Private())
{
+ d->id = resourceId;
+ d->type = type;
+ d->name = name;
+ d->filename = filename;
+ d->image = image;
+}
+
+Resource::Resource(KoResourceSP resource, const QString &type, QObject *parent)
+ : QObject(parent)
+ , d(new Private())
+{
+ d->id = resource->resourceId();
+ d->type = type;
+ d->name = resource->name();
+ d->filename = resource->filename();
+ d->image = resource->image();
}
Resource::~Resource()
{
- delete d;
+}
+
+Resource::Resource(const Resource &rhs)
+ : QObject()
+ , d(new Private())
+{
+ d->id = rhs.d->id;
+ d->type = rhs.d->type;
+ d->name = rhs.d->name;
+ d->filename = rhs.d->filename;
+ d->image = rhs.d->image;
}
bool Resource::operator==(const Resource &other) const
{
- return (d->resource == other.d->resource);
+ return (d->id == other.d->id);
}
bool Resource::operator!=(const Resource &other) const
{
return !(operator==(other));
}
+Resource Resource::operator=(const Resource &rhs)
+{
+ Resource res(rhs.d->id,
+ rhs.d->type,
+ rhs.d->name,
+ rhs.d->filename,
+ rhs.d->image);
+ return res;
+}
+
QString Resource::type() const
{
- if (!d->resource) return QString();
- if (dynamic_cast<KoPattern*>(d->resource)) return "pattern";
- else if (dynamic_cast<KoAbstractGradient*>(d->resource)) return "gradient";
- else if (dynamic_cast<KisBrush*>(d->resource)) return "brush";
- else if (dynamic_cast<KisPaintOpPreset*>(d->resource)) return "preset";
- else if (dynamic_cast<KoColorSet*>(d->resource)) return "palette";
- else if (dynamic_cast<KisWorkspaceResource*>(d->resource)) return "workspace";
- else return "";
+ return d->type;
}
QString Resource::name() const
{
- if (!d->resource) return QString();
- return d->resource->name();
+ return d->name;
}
void Resource::setName(QString value)
{
- if (!d->resource) return;
- d->resource->setName(value);
+ d->name = value;
}
-
QString Resource::filename() const
{
- if (!d->resource) return QString();
- return d->resource->filename();
+ return d->filename;
}
QImage Resource::image() const
{
- if (!d->resource) return QImage();
- return d->resource->image();
+ return d->image;
}
void Resource::setImage(QImage image)
{
- if (!d->resource) return;
- d->resource->setImage(image);
-}
-
-QByteArray Resource::data() const
-{
- QByteArray ba;
-
- if (!d->resource) return ba;
-
- QBuffer buf(&ba);
- d->resource->saveToDevice(&buf);
- return ba;
-}
-
-bool Resource::setData(QByteArray data)
-{
- if (!d->resource) return false;
- QBuffer buf(&data);
- return d->resource->loadFromDevice(&buf);
+ d->image = image;
}
-KoResource *Resource::resource() const
+KoResourceSP Resource::resource() const
{
- return d->resource;
+ return KisResourceLocator::instance()->resourceForId(d->id);
}
diff --git a/libs/libkis/Resource.h b/libs/libkis/Resource.h
index 87a6c0f05b..108dccb2a8 100644
--- a/libs/libkis/Resource.h
+++ b/libs/libkis/Resource.h
@@ -1,121 +1,113 @@
/*
* 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_RESOURCE_H
#define LIBKIS_RESOURCE_H
#include <QObject>
+#include <QScopedPointer>
#include <kis_types.h>
#include "kritalibkis_export.h"
#include "libkis.h"
-
-class KoResource;
+#include <KoResource.h>
/**
* A Resource represents a gradient, pattern, brush tip, brush preset, palette or
* workspace definition.
*
* @code
* allPresets = Application.resources("preset")
* for preset in allPresets:
* print(preset.name())
* @endcode
*
* Resources are identified by their type, name and filename. If you want to change
* the contents of a resource, you should read its data using data(), parse it and
* write the changed contents back.
*/
class KRITALIBKIS_EXPORT Resource : public QObject
{
Q_OBJECT
public:
- explicit Resource(KoResource *resource, QObject *parent = 0);
+ Resource(int resourceId, const QString &type, const QString &name, const QString &filename, const QImage &image, QObject *parent = 0);
+ Resource(KoResourceSP resource, const QString &type, QObject *parent = 0);
~Resource() override;
+ Resource(const Resource &rhs);
+
bool operator==(const Resource &other) const;
bool operator!=(const Resource &other) const;
+ Resource operator=(const Resource &rhs);
public Q_SLOTS:
/**
* Return the type of this resource. Valid types are:
* <ul>
* <li>pattern: a raster image representing a pattern
* <li>gradient: a gradient
* <li>brush: a brush tip
* <li>preset: a brush preset
* <li>palette: a color set
* <li>workspace: a workspace definition.
* </ul>
*/
QString type() const;
/**
* The user-visible name of the resource.
*/
QString name() const;
/**
* setName changes the user-visible name of the current resource.
*/
void setName(QString value);
/**
* The filename of the resource, if present. Not all resources
* are loaded from files.
*/
QString filename() const;
/**
* An image that can be used to represent the resource in the
* user interface. For some resources, like patterns, the
* image is identical to the resource, for others it's a mere
* icon.
*/
QImage image() const;
/**
* Change the image for this resource.
*/
void setImage(QImage image);
- /**
- * Return the resource as a byte array.
- */
- QByteArray data() const;
-
- /**
- * Change the internal data of the resource to the given byte
- * array. If the byte array is not valid, setData returns
- * false, otherwise true.
- */
- bool setData(QByteArray data);
-
private:
friend class PresetChooser;
friend class View;
friend class Palette;
- KoResource *resource() const;
+ KoResourceSP resource() const;
struct Private;
- const Private *const d;
+ QScopedPointer<Private> d;
};
#endif // LIBKIS_RESOURCE_H
diff --git a/libs/libkis/View.cpp b/libs/libkis/View.cpp
index 70b8b44b41..c72221585e 100644
--- a/libs/libkis/View.cpp
+++ b/libs/libkis/View.cpp
@@ -1,279 +1,279 @@
/*
* 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 "View.h"
#include <QPointer>
#include <KoPattern.h>
#include <KoAbstractGradient.h>
#include <kis_paintop_preset.h>
#include <KisView.h>
#include <KisViewManager.h>
#include <kis_node_manager.h>
#include <kis_selection_manager.h>
#include <kis_canvas_resource_provider.h>
#include <kis_paintop_box.h>
#include <KisMainWindow.h>
#include <KoCanvasBase.h>
#include <kis_canvas2.h>
+#include <KisResourceTypes.h>
#include <KisDocument.h>
-
#include "Document.h"
#include "Canvas.h"
#include "Window.h"
#include "Resource.h"
#include "ManagedColor.h"
#include "LibKisUtils.h"
struct View::Private {
Private() {}
QPointer<KisView> view;
};
View::View(KisView* view, QObject *parent)
: QObject(parent)
, d(new Private)
{
d->view = view;
}
View::~View()
{
delete d;
}
bool View::operator==(const View &other) const
{
return (d->view == other.d->view);
}
bool View::operator!=(const View &other) const
{
return !(operator==(other));
}
Window* View::window() const
{
if (!d->view) return 0;
KisMainWindow *mainwin = d->view->mainWindow();
Window *win = new Window(mainwin);
return win;
}
Document* View::document() const
{
if (!d->view) return 0;
Document *doc = new Document(d->view->document(), false);
return doc;
}
void View::setDocument(Document *document)
{
if (!d->view || !document || !document->document()) return;
d->view = d->view->replaceBy(document->document());
}
bool View::visible() const
{
if (!d->view) return false;
return d->view->isVisible();
}
void View::setVisible()
{
if (!d->view) return;
KisMainWindow *mainwin = d->view->mainWindow();
mainwin->setActiveView(d->view);
mainwin->subWindowActivated();
}
Canvas* View::canvas() const
{
if (!d->view) return 0;
Canvas *c = new Canvas(d->view->canvasBase());
return c;
}
KisView *View::view()
{
return d->view;
}
void View::activateResource(Resource *resource)
{
if (!d->view) return;
if (!resource) return;
- KoResource *r= resource->resource();
+ KoResourceSP r = resource->resource();
if (!r) return;
- if (dynamic_cast<KoPattern*>(r)) {
+ if (r.dynamicCast<KoPattern>()) {
QVariant v;
- v.setValue(static_cast<void*>(r));
+ v.setValue<KoResourceSP>(r);
d->view->canvasBase()->resourceManager()->setResource(KisCanvasResourceProvider::CurrentPattern, v);
}
- else if (dynamic_cast<KoAbstractGradient*>(r)) {
+ else if (r.dynamicCast<KoAbstractGradient>()) {
QVariant v;
- v.setValue(static_cast<void*>(r));
+ v.setValue<KoResourceSP>(r);
d->view->canvasBase()->resourceManager()->setResource(KisCanvasResourceProvider::CurrentGradient, v);
}
- else if (dynamic_cast<KisPaintOpPreset*>(r)) {
+ else if (r.dynamicCast<KisPaintOpPreset>()) {
d->view->viewManager()->paintOpBox()->resourceSelected(r);
}
}
ManagedColor *View::foregroundColor() const
{
if (!d->view) return 0;
return new ManagedColor(d->view->resourceProvider()->fgColor());
}
void View::setForeGroundColor(ManagedColor *color)
{
if (!d->view) return;
d->view->resourceProvider()->setFGColor(color->color());
}
ManagedColor *View::backgroundColor() const
{
if (!d->view) return 0;
return new ManagedColor(d->view->resourceProvider()->bgColor());
}
void View::setBackGroundColor(ManagedColor *color)
{
if (!d->view) return;
d->view->resourceProvider()->setBGColor(color->color());
}
Resource *View::currentBrushPreset() const
{
if (!d->view) return 0;
- return new Resource(d->view->resourceProvider()->currentPreset().data());
+ return new Resource(d->view->resourceProvider()->currentPreset(), ResourceType::PaintOpPresets);
}
void View::setCurrentBrushPreset(Resource *resource)
{
activateResource(resource);
}
Resource *View::currentPattern() const
{
if (!d->view) return 0;
- return new Resource(d->view->resourceProvider()->currentPattern());
+ return new Resource(d->view->resourceProvider()->currentPattern(), ResourceType::Patterns);
}
void View::setCurrentPattern(Resource *resource)
{
activateResource(resource);
}
Resource *View::currentGradient() const
{
if (!d->view) return 0;
- return new Resource(d->view->resourceProvider()->currentGradient());
+ return new Resource(d->view->resourceProvider()->currentGradient(), ResourceType::Gradients);
}
void View::setCurrentGradient(Resource *resource)
{
activateResource(resource);
}
QString View::currentBlendingMode() const
{
if (!d->view) return "";
return d->view->resourceProvider()->currentCompositeOp();
}
void View::setCurrentBlendingMode(const QString &blendingMode)
{
if (!d->view) return;
d->view->resourceProvider()->setCurrentCompositeOp(blendingMode);
}
float View::HDRExposure() const
{
if (!d->view) return 0.0;
return d->view->resourceProvider()->HDRExposure();
}
void View::setHDRExposure(float exposure)
{
if (!d->view) return;
d->view->resourceProvider()->setHDRExposure(exposure);
}
float View::HDRGamma() const
{
if (!d->view) return 0.0;
return d->view->resourceProvider()->HDRGamma();
}
void View::setHDRGamma(float gamma)
{
if (!d->view) return;
d->view->resourceProvider()->setHDRGamma(gamma);
}
qreal View::paintingOpacity() const
{
if (!d->view) return 0.0;
return d->view->resourceProvider()->opacity();
}
void View::setPaintingOpacity(qreal opacity)
{
if (!d->view) return;
d->view->resourceProvider()->setOpacity(opacity);
}
qreal View::brushSize() const
{
if (!d->view) return 0.0;
return d->view->resourceProvider()->size();
}
void View::setBrushSize(qreal brushSize)
{
if (!d->view) return;
d->view->resourceProvider()->setSize(brushSize);
}
qreal View::paintingFlow() const
{
if (!d->view) return 0.0;
return d->view->resourceProvider()->flow();
}
void View::setPaintingFlow(qreal flow)
{
if (!d->view) return;
d->view->resourceProvider()->setFlow(flow);
}
QList<Node *> View::selectedNodes() const
{
if (!d->view) return QList<Node *>();
if (!d->view->viewManager()) return QList<Node *>();
if (!d->view->viewManager()->nodeManager()) return QList<Node *>();
KisNodeList selectedNodes = d->view->viewManager()->nodeManager()->selectedNodes();
return LibKisUtils::createNodeList(selectedNodes, d->view->image());
}
diff --git a/libs/libqml/Settings.cpp b/libs/libqml/Settings.cpp
index 7fbb803ee1..b08c5bd98e 100644
--- a/libs/libqml/Settings.cpp
+++ b/libs/libqml/Settings.cpp
@@ -1,164 +1,144 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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 "Settings.h"
#include <QApplication>
#include <ksharedconfig.h>
#include <kconfiggroup.h>
#include <kis_paintop_preset.h>
#include "KisResourceServerProvider.h"
#include "Theme.h"
#include "PropertyContainer.h"
#include <kis_config.h>
class Settings::Private
{
public:
Private() : temporaryFile(false), focusItem(0), theme(0){ }
QString currentFile;
bool temporaryFile;
QQuickItem *focusItem;
Theme* theme {0};
};
Settings::Settings( QObject* parent )
: QObject( parent )
, d( new Private )
{
}
Settings::~Settings()
{
delete d;
}
void Settings::setTheme(Theme *theme)
{
d->theme = theme;
d->theme->setParent(this);
connect(d->theme, SIGNAL(fontCacheRebuilt()), SIGNAL(themeChanged()));
}
QString Settings::currentFile() const
{
return d->currentFile;
}
void Settings::setCurrentFile(const QString& fileName)
{
qApp->processEvents();
if (fileName != d->currentFile) {
d->currentFile = fileName;
emit currentFileChanged();
}
}
bool Settings::isTemporaryFile() const
{
return d->temporaryFile;
}
void Settings::setTemporaryFile(bool temp)
{
if (temp != d->temporaryFile) {
d->temporaryFile = temp;
emit temporaryFileChanged();
}
}
QQuickItem* Settings::focusItem()
{
return d->focusItem;
}
void Settings::setFocusItem(QQuickItem* item)
{
if (item != d->focusItem) {
d->focusItem = item;
emit focusItemChanged();
}
}
QObject* Settings::theme() const
{
return d->theme;
}
QString Settings::themeID() const
{
if(d->theme)
return d->theme->id();
return QString();
}
void Settings::setThemeID(const QString& /*id*/)
{
// if(!d->theme || id != d->theme->id()) {
// if(d->theme) {
// delete d->theme;
// d->theme = 0;
// }
// d->theme = Theme::load(id, this);
// KSharedConfig::openConfig()->group("General").writeEntry<QString>("theme", id);
// emit themeChanged();
// }
}
QObject* Settings::customImageSettings() const
{
QObject* settings = new PropertyContainer("customImageSettings", qApp);
KisConfig cfg(false);
settings->setProperty("Width", cfg.defImageWidth());
settings->setProperty("Height", cfg.defImageHeight());
settings->setProperty("Resolution", qRound(cfg.defImageResolution() * 72)); // otherwise we end up with silly floating point numbers
settings->setProperty("ColorModel", cfg.defColorModel());
settings->setProperty("ColorDepth", cfg.defaultColorDepth());
settings->setProperty("ColorProfile", cfg.defColorProfile());
return settings;
}
-QString Settings::lastPreset() const
-{
- KisConfig cfg(true);
- 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;
- }
- }
- return cfg.readEntry("LastPreset", defaultPresetName);
-}
-
diff --git a/libs/libqml/Settings.h b/libs/libqml/Settings.h
index 2694c55ba9..fb442c4551 100644
--- a/libs/libqml/Settings.h
+++ b/libs/libqml/Settings.h
@@ -1,81 +1,78 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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 SETTINGS_H
#define SETTINGS_H
#include <QObject>
#include <QString>
#include "krita_sketch_export.h"
class QQuickItem;
class Theme;
class KRITA_SKETCH_EXPORT Settings : public QObject
{
Q_OBJECT
Q_PROPERTY(QString currentFile READ currentFile WRITE setCurrentFile NOTIFY currentFileChanged)
Q_PROPERTY(bool temporaryFile READ isTemporaryFile WRITE setTemporaryFile NOTIFY temporaryFileChanged)
Q_PROPERTY(QQuickItem* focusItem READ focusItem WRITE setFocusItem NOTIFY focusItemChanged)
Q_PROPERTY(QObject* theme READ theme NOTIFY themeChanged)
Q_PROPERTY(QString themeID READ themeID WRITE setThemeID NOTIFY themeChanged)
Q_PROPERTY(QObject* customImageSettings READ customImageSettings NOTIFY customImageSettingsChanged)
- Q_PROPERTY(QString lastPreset READ lastPreset NOTIFY lastPresetChanged)
public:
explicit Settings( QObject* parent = 0);
virtual ~Settings();
void setTheme(Theme *theme);
public Q_SLOTS:
QString currentFile() const;
void setCurrentFile(const QString &fileName);
bool isTemporaryFile() const;
void setTemporaryFile(bool temp);
QQuickItem *focusItem();
void setFocusItem(QQuickItem *item);
QObject* theme() const;
QString themeID() const;
void setThemeID(const QString& id);
QObject* customImageSettings() const;
- QString lastPreset() const;
Q_SIGNALS:
void currentFileChanged();
void temporaryFileChanged();
void focusItemChanged();
void themeChanged();
void customImageSettingsChanged();
- void lastPresetChanged();
private:
class Private;
Private* const d;
};
#endif // SETTINGS_H
diff --git a/libs/libqml/plugins/kritasketchplugin/CMakeLists.txt b/libs/libqml/plugins/kritasketchplugin/CMakeLists.txt
index fae87272d4..b0e38b9dd4 100644
--- a/libs/libqml/plugins/kritasketchplugin/CMakeLists.txt
+++ b/libs/libqml/plugins/kritasketchplugin/CMakeLists.txt
@@ -1,89 +1,86 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/models
)
set(kritasketchplugin_SRCS
kritasketchplugin.cpp
ColorSelectorItem.cpp
CurveEditorItem.cpp
Constants.cpp
ToolManager.cpp
SimpleTouchArea.cpp
ImageBuilder.cpp
KritaNamespace.cpp
MouseTracker.cpp
PanelConfiguration.cpp
PropertyContainer.cpp
)
set(kritasketch_kritaintegrationmodels_SRCS
models/TemplatesModel.cpp
models/KeyboardModel.cpp
models/ColorImageProvider.cpp
models/CompositeOpModel.cpp
models/FiltersCategoryModel.cpp
models/FiltersModel.cpp
models/LayerCompositeDetails.cpp
models/LayerModel.cpp
models/LayerThumbProvider.cpp
models/PaletteColorsModel.cpp
- models/PaletteModel.cpp
- models/PresetImageProvider.cpp
- models/PresetModel.cpp
models/RecentImageImageProvider.cpp
models/RecentImagesModel.cpp
models/FileSystemModel.cpp
models/ColorModelModel.cpp
models/ColorDepthModel.cpp
models/ColorProfileModel.cpp
models/IconImageProvider.cpp
)
set(KRITA_COLORSELECTORNG_SOURCE_DIR "${CMAKE_SOURCE_DIR}/plugins/dockers/advancedcolorselector")
set(KRITA_COLORSELECTORNG_SOURCES
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_my_paint_shade_selector.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_color_patches.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_color_history.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_color_selector.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_common_colors.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_common_colors_recalculation_runner.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_minimal_shade_selector.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_color_selector_base_proxy.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_shade_selector_line.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_shade_selector_line_editor.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_shade_selector_line_combo_box.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_shade_selector_line_combo_box_popup.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_shade_selector_lines_settings.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_color_selector_container.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_color_selector_base.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_color_selector_component.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_color_selector_ring.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_color_selector_triangle.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_color_selector_simple.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_color_selector_wheel.cpp
${KRITA_COLORSELECTORNG_SOURCE_DIR}/kis_color_selector_combo_box.cpp
)
if (WIN32)
add_library(kritasketchplugin MODULE ${kritasketchplugin_SRCS} ${kritasketch_kritaintegrationmodels_SRCS} ${KRITA_COLORSELECTORNG_SOURCES})
else()
add_library(kritasketchplugin SHARED ${kritasketchplugin_SRCS} ${kritasketch_kritaintegrationmodels_SRCS} ${KRITA_COLORSELECTORNG_SOURCES})
endif()
target_link_libraries(kritasketchplugin
Qt5::Quick
Qt5::Gui
Qt5::Network
Qt5::Core
kritawidgets
kritaui
kritaqml
)
install(TARGETS kritasketchplugin DESTINATION ${QML_INSTALL_DIR}/org/krita/sketch)
install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/krita/sketch)
diff --git a/libs/libqml/plugins/kritasketchplugin/kritasketchplugin.cpp b/libs/libqml/plugins/kritasketchplugin/kritasketchplugin.cpp
index 9090ac0dc1..2119cbf964 100644
--- a/libs/libqml/plugins/kritasketchplugin/kritasketchplugin.cpp
+++ b/libs/libqml/plugins/kritasketchplugin/kritasketchplugin.cpp
@@ -1,143 +1,137 @@
/*
* <one line to give the library's name and an idea of what it does.>
* Copyright 2013 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) 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 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/>.
*
*/
#include "kritasketchplugin.h"
#include "ColorSelectorItem.h"
#include "CurveEditorItem.h"
#include "DocumentListModel.h"
#include "ColorImageProvider.h"
#include "FiltersCategoryModel.h"
#include "LayerModel.h"
#include "LayerCompositeDetails.h"
#include "PaletteColorsModel.h"
#include "RecentImagesModel.h"
-#include "PaletteModel.h"
-#include "PresetModel.h"
-#include "PresetImageProvider.h"
#include "RecentImageImageProvider.h"
#include "RecentFileManager.h"
#include "IconImageProvider.h"
#include "KisMultiFeedRSSModel.h"
#include "FileSystemModel.h"
#include "CompositeOpModel.h"
#include "KeyboardModel.h"
#include "ColorModelModel.h"
#include "ColorDepthModel.h"
#include "ColorProfileModel.h"
#include <TemplatesModel.h>
#include "Theme.h"
#include "Constants.h"
#include "Settings.h"
#include "SimpleTouchArea.h"
#include "ToolManager.h"
#include "KritaNamespace.h"
#include "PanelConfiguration.h"
#include "DocumentManager.h"
#include "kis_clipboard.h"
#include "KisSketchView.h"
#include <QQmlEngine>
#include <QQmlContext>
static QObject *provideConstantsObject(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return new Constants;
}
static QObject *provideKritaNamespaceObject(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return new KritaNamespace;
}
static QObject *provideKritaRssModelObject(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
MultiFeedRssModel *rssModel = new MultiFeedRssModel;
rssModel->addFeed(QLatin1String("https://krita.org/en/feed/"));
return rssModel;
}
void KritaSketchPlugin::registerTypes(const char* uri)
{
Q_UNUSED(uri)
Q_ASSERT(uri == QLatin1String("org.krita.sketch"));
qmlRegisterType<SimpleTouchArea>("org.krita.sketch", 1, 0, "SimpleTouchArea");
qmlRegisterType<ColorSelectorItem>("org.krita.sketch", 1, 0, "ColorSelectorItem");
qmlRegisterType<CurveEditorItem>("org.krita.sketch", 1, 0, "CurveEditorItem");
qmlRegisterType<DocumentListModel>("org.krita.sketch", 1, 0, "DocumentListModel");
- qmlRegisterType<PaletteModel>("org.krita.sketch", 1, 0, "PaletteModel");
qmlRegisterType<PaletteColorsModel>("org.krita.sketch", 1, 0, "PaletteColorsModel");
- qmlRegisterType<PresetModel>("org.krita.sketch", 1, 0, "PresetModel");
qmlRegisterType<KisSketchView>("org.krita.sketch", 1, 0, "SketchView");
qmlRegisterType<LayerModel>("org.krita.sketch", 1, 0, "LayerModel");
qmlRegisterType<FiltersCategoryModel>("org.krita.sketch", 1, 0, "FiltersCategoryModel");
qmlRegisterType<RecentImagesModel>("org.krita.sketch", 1, 0, "RecentImagesModel");
qmlRegisterType<FileSystemModel>("org.krita.sketch", 1, 0, "FileSystemModel");
qmlRegisterType<ToolManager>("org.krita.sketch", 1, 0, "ToolManager");
qmlRegisterType<CompositeOpModel>("org.krita.sketch", 1, 0, "CompositeOpModel");
qmlRegisterType<PanelConfiguration>("org.krita.sketch", 1, 0, "PanelConfiguration");
qmlRegisterType<KeyboardModel>("org.krita.sketch", 1, 0, "KeyboardModel");
qmlRegisterType<ColorModelModel>("org.krita.sketch", 1, 0, "ColorModelModel");
qmlRegisterType<ColorDepthModel>("org.krita.sketch", 1, 0, "ColorDepthModel");
qmlRegisterType<ColorProfileModel>("org.krita.sketch", 1, 0, "ColorProfileModel");
qmlRegisterType<Theme>("org.krita.sketch", 1, 0, "Theme");
qmlRegisterType<TemplatesModel>("org.krita.sketch", 1, 0, "TemplatesModel");
qmlRegisterSingletonType<Constants>("org.krita.sketch", 1, 0, "Constants", provideConstantsObject);
qmlRegisterSingletonType<KritaNamespace>("org.krita.sketch", 1, 0, "Krita", provideKritaNamespaceObject);
qmlRegisterSingletonType<MultiFeedRssModel>("org.krita.sketch", 1, 0, "KritaFeedRssModel", provideKritaRssModelObject);
qmlRegisterUncreatableType<LayerCompositeDetails>("org.krita.sketch", 1, 0, "LayerCompositeDetails", "This type is returned by the LayerModel class");
}
void KritaSketchPlugin::initializeEngine(QQmlEngine* engine, const char* uri)
{
Q_UNUSED(uri)
Q_ASSERT(uri == QLatin1String("org.krita.sketch"));
- engine->addImageProvider(QLatin1String("presetthumb"), new PresetImageProvider);
engine->addImageProvider(QLatin1String("color"), new ColorImageProvider);
engine->addImageProvider(QLatin1String("recentimage"), new RecentImageImageProvider);
engine->addImageProvider(QLatin1String("icon"), new IconImageProvider);
RecentFileManager *recentFileManager = DocumentManager::instance()->recentFileManager();
engine->rootContext()->setContextProperty("RecentFileManager", recentFileManager);
engine->rootContext()->setContextProperty("KisClipBoard", KisClipboard::instance());
engine->rootContext()->setContextProperty("QMLEngine", engine);
// This would be a problem, but doesn't seem to be used...
// engine->rootContext()->setContextProperty("View", d->view);
}
diff --git a/libs/libqml/plugins/kritasketchplugin/models/FiltersCategoryModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/FiltersCategoryModel.cpp
index 99d7f57333..361542fd52 100644
--- a/libs/libqml/plugins/kritasketchplugin/models/FiltersCategoryModel.cpp
+++ b/libs/libqml/plugins/kritasketchplugin/models/FiltersCategoryModel.cpp
@@ -1,316 +1,317 @@
/* This file is part of the KDE project
* 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 "FiltersCategoryModel.h"
#include "FiltersModel.h"
#include <PropertyContainer.h>
#include <filter/kis_filter_registry.h>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_filter_mask.h>
#include <kis_layer.h>
#include <kis_selection.h>
#include <kis_config_widget.h>
#include <KisViewManager.h>
#include <kis_node_manager.h>
#include <kis_selection_manager.h>
#include <kis_canvas2.h>
#include <kis_filter_manager.h>
+#include <KisGlobalResourcesInterface.h>
#include <QApplication>
#include <algorithm>
bool categoryLessThan(const FiltersModel* s1, const FiltersModel* s2)
{
return s1->categoryName.toLower() < s2->categoryName.toLower();
}
class FiltersCategoryModel::Private
{
public:
Private(FiltersCategoryModel* qq)
: q(qq)
, currentCategory(-1)
, view(0)
, previewEnabled(false)
, previewFilterID(-1)
, previewTimer(new QTimer())
{
previewTimer->setInterval(150);
previewTimer->setSingleShot(true);
connect(previewTimer, SIGNAL(timeout()), q, SLOT(updatePreview()));
}
FiltersCategoryModel* q;
int currentCategory;
KisViewManager* view;
QList<FiltersModel*> categories;
FiltersModel* categoryByName(const QString& name)
{
FiltersModel* category = 0;
for(int i = 0; i < categories.count(); ++i)
{
if (categories.at(i)->categoryId == name)
{
category = categories[i];
break;
}
}
return category;
}
void refreshContents()
{
q->beginResetModel();
qDeleteAll(categories);
categories.clear();
QList<KisFilterSP> filters = KisFilterRegistry::instance()->values();
QList<QString> tmpCategoryIDs;
Q_FOREACH (const KisFilterSP filter, filters) {
Q_ASSERT(filter);
FiltersModel* cat = 0;
if (!tmpCategoryIDs.contains(filter->menuCategory().id())) {
cat = new FiltersModel(q);
cat->categoryId = filter->menuCategory().id();
cat->categoryName = filter->menuCategory().name();
cat->setView(view);
categories << cat;
tmpCategoryIDs << filter->menuCategory().id();
connect(cat, SIGNAL(configurationChanged(int)), q, SLOT(filterConfigurationChanged(int)));
connect(cat, SIGNAL(filterActivated(int)), q, SLOT(filterActivated(int)));
}
else
cat = categoryByName(filter->menuCategory().id());
cat->addFilter(filter);
qApp->processEvents();
}
std::sort(categories.begin(), categories.end(), categoryLessThan);
q->endResetModel();
}
bool previewEnabled;
KisFilterMaskSP mask;
KisNodeSP node;
int previewFilterID;
KisFilterConfigurationSP newConfig;
QTimer* previewTimer;
};
FiltersCategoryModel::FiltersCategoryModel(QObject* parent)
: QAbstractListModel(parent)
, d(new Private(this))
{
}
FiltersCategoryModel::~FiltersCategoryModel()
{
delete d;
}
QHash<int, QByteArray> FiltersCategoryModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[TextRole] = "text";
return roles;
}
QVariant FiltersCategoryModel::data(const QModelIndex& index, int role) const
{
QVariant data;
if (index.isValid())
{
switch(role)
{
case TextRole:
data = d->categories[index.row()]->categoryName;
break;
default:
break;
}
}
return data;
}
int FiltersCategoryModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid())
return 0;
return d->categories.count();
}
QObject* FiltersCategoryModel::filterModel() const
{
if (d->currentCategory == -1)
return 0;
return d->categories[d->currentCategory];
}
void FiltersCategoryModel::activateItem(int index)
{
if (index > -1 && index < d->categories.count())
{
d->currentCategory = index;
emit filterModelChanged();
}
}
QObject* FiltersCategoryModel::view() const
{
return d->view;
}
void FiltersCategoryModel::setView(QObject* newView)
{
if (d->view)
{
setPreviewEnabled(false);
d->view->nodeManager()->disconnect(this);
d->view->selectionManager()->disconnect(this);
}
d->view = qobject_cast<KisViewManager*>( newView );
if (d->view)
{
d->refreshContents();
// connect(d->view->nodeManager(), SIGNAL(sigLayerActivated(KisLayerSP)), this, SLOT(activeLayerChanged(KisLayerSP)));
connect(d->view->selectionManager(), SIGNAL(currentSelectionChanged()), this, SLOT(activeSelectionChanged()));
}
emit viewChanged();
}
void FiltersCategoryModel::activeLayerChanged(KisLayerSP layer)
{
Q_UNUSED(layer);
setPreviewEnabled(false);
}
void FiltersCategoryModel::activeSelectionChanged()
{
setPreviewEnabled(false);
}
void FiltersCategoryModel::filterActivated(int index)
{
Q_UNUSED(index);
setPreviewEnabled(false);
}
void FiltersCategoryModel::filterConfigurationChanged(int index, FiltersModel* model)
{
d->previewFilterID = index;
if (d->previewEnabled && index > -1)
{
if (!model) {
model = qobject_cast<FiltersModel*>(sender());
}
if (!model) {
return;
}
KisFilterConfigurationSP config;
KisFilter* filter = model->filter(index);
if (filter->showConfigurationWidget() && filter->id() != QLatin1String("colortransfer")) {
KisConfigWidget* wdg = filter->createConfigurationWidget(0, d->view->activeNode()->original(), false);
wdg->deleteLater();
- config = KisFilterConfigurationSP(KisFilterRegistry::instance()->cloneConfiguration(dynamic_cast<KisFilterConfiguration*>(wdg->configuration().data())));
+ config = dynamic_cast<KisFilterConfiguration*>(wdg->configuration().data())->clone();
}
else {
- config = KisFilterConfigurationSP(KisFilterRegistry::instance()->cloneConfiguration(filter->defaultConfiguration()));
+ config = filter->defaultConfiguration(KisGlobalResourcesInterface::instance())->clone();
}
QObject* configuration = d->categories[d->currentCategory]->configuration(index);
Q_FOREACH (const QByteArray& propName, configuration->dynamicPropertyNames()) {
config->setProperty(QString(propName), configuration->property(propName));
}
config->setCurve(qobject_cast<PropertyContainer*>(configuration)->curve());
config->setCurves(qobject_cast<PropertyContainer*>(configuration)->curves());
configuration->deleteLater();
d->newConfig = config;
d->previewTimer->start();
}
}
void FiltersCategoryModel::updatePreview()
{
d->view->filterManager()->apply(d->newConfig);
}
bool FiltersCategoryModel::previewEnabled() const
{
return d->previewEnabled;
}
void FiltersCategoryModel::filterSelected(int index)
{
if (d->previewEnabled)
filterConfigurationChanged(index, d->categories[d->currentCategory]);
}
void FiltersCategoryModel::setPreviewEnabled(bool enabled)
{
if (d->previewEnabled != enabled)
{
d->previewEnabled = enabled;
emit previewEnabledChanged();
if (enabled)
filterConfigurationChanged(d->previewFilterID, d->categories[d->currentCategory]);
else
d->view->filterManager()->cancel();
}
}
int FiltersCategoryModel::categoryIndexForConfig(QObject* config)
{
PropertyContainer* configuration = qobject_cast<PropertyContainer*>(config);
if (!configuration)
return -1;
FiltersModel* model = 0;
int i = 0;
while(model == 0 && i < d->categories.count())
{
FiltersModel* cat = d->categories.at(i);
// i know there's no check here - but a category is not created unless there
// is something to put in it
for(int j = 0; j < cat->rowCount(); ++j)
{
if (cat->filter(j)->id() == configuration->name())
return i;
}
++i;
}
return -1;
}
int FiltersCategoryModel::filterIndexForConfig(int categoryIndex, QObject* filterConfig)
{
PropertyContainer* configuration = qobject_cast<PropertyContainer*>(filterConfig);
if (!configuration)
return -1;
if (categoryIndex < 0 || categoryIndex > d->categories.count() - 1)
return -1;
FiltersModel* cat = d->categories.at(categoryIndex);
// i know there's no check here - but a category is not created unless there
// is something to put in it
for(int j = 0; j < cat->rowCount(); ++j)
{
if (cat->filter(j)->id() == configuration->name())
return j;
}
return -1;
}
diff --git a/libs/libqml/plugins/kritasketchplugin/models/FiltersModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/FiltersModel.cpp
index b12eba8730..f0551c513d 100644
--- a/libs/libqml/plugins/kritasketchplugin/models/FiltersModel.cpp
+++ b/libs/libqml/plugins/kritasketchplugin/models/FiltersModel.cpp
@@ -1,207 +1,208 @@
/* This file is part of the KDE project
* 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 "FiltersModel.h"
#include <PropertyContainer.h>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_config_widget.h>
#include <KisViewManager.h>
#include <kis_filter_manager.h>
+#include <KisGlobalResourcesInterface.h>
class FiltersModel::Private
{
public:
Private()
: view(0)
{};
KisViewManager* view;
QList<KisFilterSP> filters;
QList<KisFilterConfigurationSP> configurations;
};
FiltersModel::FiltersModel(QObject* parent)
: QAbstractListModel(parent)
, d(new Private)
{
}
FiltersModel::~FiltersModel()
{
delete d;
}
QHash<int, QByteArray> FiltersModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[TextRole] = "text";
return roles;
}
QVariant FiltersModel::data(const QModelIndex& index, int role) const
{
QVariant data;
if (index.isValid())
{
switch(role)
{
case TextRole:
data = d->filters[index.row()]->name();
break;
default:
break;
}
}
return data;
}
int FiltersModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid())
return 0;
return d->filters.count();
}
bool FiltersModel::filterRequiresConfiguration(int index)
{
if (index > -1 && index < d->filters.count())
{
return d->filters[index]->showConfigurationWidget();
}
return false;
}
QString FiltersModel::filterID(int index)
{
if (index > -1 && index < d->filters.count())
{
return d->filters[index]->id();
}
return QLatin1String("");
}
void FiltersModel::activateFilter(int index)
{
if (index > -1 && index < d->filters.count())
{
if (d->configurations[index])
{
d->view->filterManager()->apply(d->configurations[index]);
}
else
{
- d->view->filterManager()->apply(KisFilterConfigurationSP(d->filters[index]->defaultConfiguration()));
+ d->view->filterManager()->apply(KisFilterConfigurationSP(d->filters[index]->defaultConfiguration(KisGlobalResourcesInterface::instance())));
}
d->view->filterManager()->finish();
emit filterActivated(index);
}
}
KisFilter* FiltersModel::filter(int index)
{
if (index > -1 && index < d->filters.count())
{
return d->filters[index].data();
}
return 0;
}
void FiltersModel::addFilter(KisFilterSP filter)
{
if (!d->view || !d->view->activeNode()) return;
if (!filter.isNull())
{
int newRow = d->filters.count();
beginInsertRows(QModelIndex(), newRow, newRow);
d->filters << filter;
// We're not asking for the config widget config for color transfer
// The reason for this is that the completion widget is VERY slow to destruct on
// Windows. This can be removed once that bug has been alleviated at some later
// point in time, but for now it has no side effects, as this filter's default
// config is fine anyway.
if (filter->showConfigurationWidget() && filter->id() != QLatin1String("colortransfer")) {
KisConfigWidget* wdg = filter->createConfigurationWidget(0, d->view->activeNode()->original(), false);
wdg->deleteLater();
d->configurations << KisFilterConfigurationSP(dynamic_cast<KisFilterConfiguration*>(wdg->configuration().data()));
}
else {
- d->configurations << KisFilterConfigurationSP(filter->defaultConfiguration());
+ d->configurations << KisFilterConfigurationSP(filter->defaultConfiguration(KisGlobalResourcesInterface::instance()));
}
endInsertRows();
}
}
QObject* FiltersModel::view() const
{
return d->view;
}
void FiltersModel::setView(QObject* newView)
{
d->view = qobject_cast<KisViewManager*>( newView );
emit viewChanged();
}
QObject* FiltersModel::configuration(int index)
{
// If index is out of bounds, return /something/ for the object work on at least.
if (index < 0 || index > d->configurations.count() - 1)
return new PropertyContainer("", this);
PropertyContainer* config = new PropertyContainer(d->filters[index]->id(), this);
if (!d->configurations[index]) {
// if we have a config widget to show, reinitialise the configuration, just in case
if(d->filters[index]->showConfigurationWidget() && d->filters[index]->id() != QLatin1String("colortransfer")) {
KisConfigWidget* wdg = d->filters[index]->createConfigurationWidget(0, d->view->activeNode()->original(), false);
wdg->deleteLater();
d->configurations[index] = KisFilterConfigurationSP(dynamic_cast<KisFilterConfiguration*>(wdg->configuration().data()));
}
// If we've not got one already, assign the default configuration to the cache
else {
- d->configurations[index] = KisFilterConfigurationSP(d->filters[index]->defaultConfiguration());
+ d->configurations[index] = KisFilterConfigurationSP(d->filters[index]->defaultConfiguration(KisGlobalResourcesInterface::instance()));
}
}
QMap<QString, QVariant> props = d->configurations[index]->getProperties();
QMap<QString, QVariant>::const_iterator i;
for(i = props.constBegin(); i != props.constEnd(); ++i)
{
config->setProperty(i.key().toLatin1(), i.value());
}
config->setCurve(d->configurations[index]->curve());
config->setCurves(d->configurations[index]->curves());
return config;
}
void FiltersModel::setConfiguration(int index, QObject* configuration)
{
if (qobject_cast< PropertyContainer* >(configuration) && index > -1 && index < d->configurations.count() - 1)
{
KisFilterConfigurationSP config = d->configurations[index];
Q_FOREACH (const QByteArray& propName, configuration->dynamicPropertyNames())
{
config->setProperty(QString(propName), configuration->property(propName));
}
config->setCurve(qobject_cast< PropertyContainer* >(configuration)->curve());
config->setCurves(qobject_cast< PropertyContainer* >(configuration)->curves());
d->configurations[index] = config;
emit configurationChanged(index);
}
}
diff --git a/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp
index 9dbef76382..c9fe7bc192 100644
--- a/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp
+++ b/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp
@@ -1,982 +1,977 @@
/* This file is part of the KDE project
* 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 "LayerModel.h"
#include "LayerThumbProvider.h"
#include <PropertyContainer.h>
#include <kis_node_model.h>
#include <KisViewManager.h>
#include <kis_canvas2.h>
#include <kis_node_manager.h>
#include <kis_dummies_facade_base.h>
#include <KisDocument.h>
#include <kis_composite_ops_model.h>
#include <kis_node.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_group_layer.h>
#include <kis_paint_layer.h>
#include <kis_filter_mask.h>
#include <kis_shape_controller.h>
#include <kis_adjustment_layer.h>
#include <kis_selection_manager.h>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <filter/kis_filter_registry.h>
#include <KoShapeControllerBase.h>
#include <KoProperties.h>
#include <QQmlEngine>
#include <kis_base_node.h>
#include "KisSelectionActionsAdapter.h"
+#include <KisGlobalResourcesInterface.h>
struct LayerModelMetaInfo {
LayerModelMetaInfo()
: canMoveUp(false)
, canMoveRight(false)
, canMoveDown(false)
, canMoveLeft(false)
, depth(-1)
{}
bool canMoveUp;
bool canMoveRight;
bool canMoveDown;
bool canMoveLeft;
int depth;
};
class LayerModel::Private {
public:
Private(LayerModel* qq)
: q(qq)
, nodeModel(new KisNodeModel(qq))
, aboutToRemoveRoots(false)
, canvas(0)
, nodeManager(0)
, image(0)
, activeNode(0)
, declarativeEngine(0)
, thumbProvider(0)
, updateActiveLayerWithNewFilterConfigTimer(new QTimer(qq))
, imageChangedTimer(new QTimer(qq))
{
QList<KisFilterSP> tmpFilters = KisFilterRegistry::instance()->values();
Q_FOREACH (const KisFilterSP& filter, tmpFilters)
{
filters[filter.data()->id()] = filter.data();
}
updateActiveLayerWithNewFilterConfigTimer->setInterval(0);
updateActiveLayerWithNewFilterConfigTimer->setSingleShot(true);
connect(updateActiveLayerWithNewFilterConfigTimer, SIGNAL(timeout()), qq, SLOT(updateActiveLayerWithNewFilterConfig()));
imageChangedTimer->setInterval(250);
imageChangedTimer->setSingleShot(true);
connect(imageChangedTimer, SIGNAL(timeout()), qq, SLOT(imageHasChanged()));
}
LayerModel* q;
QList<KisNodeSP> layers;
QHash<const KisNode*, LayerModelMetaInfo> layerMeta;
KisNodeModel* nodeModel;
bool aboutToRemoveRoots;
KisViewManager* view;
KisCanvas2* canvas;
QScopedPointer<KisSelectionActionsAdapter> selectionActionsAdapter;
QPointer<KisNodeManager> nodeManager;
KisImageWSP image;
KisNodeSP activeNode;
QQmlEngine* declarativeEngine;
LayerThumbProvider* thumbProvider;
QHash<QString, const KisFilter*> filters;
KisFilterConfigurationSP newConfig;
QTimer* updateActiveLayerWithNewFilterConfigTimer;
QTimer* imageChangedTimer;
static int counter()
{
static int count = 0;
return count++;
}
static QStringList layerClassNames()
{
QStringList list;
list << "KisGroupLayer";
list << "KisPaintLayer";
list << "KisFilterMask";
list << "KisAdjustmentLayer";
return list;
}
int deepChildCount(KisNodeSP layer)
{
quint32 childCount = layer->childCount();
QList<KisNodeSP> children = layer->childNodes(layerClassNames(), KoProperties());
for(quint32 i = 0; i < childCount; ++i)
childCount += deepChildCount(children.at(i));
return childCount;
}
void rebuildLayerList(KisNodeSP layer = 0)
{
bool refreshingFromRoot = false;
if (!image)
{
layers.clear();
return;
}
if (layer == 0)
{
refreshingFromRoot = true;
layers.clear();
layer = image->rootLayer();
}
// implementation node: The root node is not a visible node, and so
// is never added to the list of layers
QList<KisNodeSP> children = layer->childNodes(layerClassNames(), KoProperties());
if (children.count() == 0)
return;
for(quint32 i = children.count(); i > 0; --i)
{
layers << children.at(i-1);
rebuildLayerList(children.at(i-1));
}
if (refreshingFromRoot)
refreshLayerMovementAbilities();
}
void refreshLayerMovementAbilities()
{
layerMeta.clear();
if (layers.count() == 0)
return;
for(int i = 0; i < layers.count(); ++i)
{
const KisNodeSP layer = layers.at(i);
LayerModelMetaInfo ability;
if (i > 0)
ability.canMoveUp = true;
if (i < layers.count() - 1)
ability.canMoveDown = true;
KisNodeSP parent = layer;
while(parent)
{
++ability.depth;
parent = parent->parent();
}
if (ability.depth > 1)
ability.canMoveLeft = true;
if (i < layers.count() - 1 && qobject_cast<const KisGroupLayer*>(layers.at(i + 1).constData()))
ability.canMoveRight = true;
layerMeta[layer] = ability;
}
}
};
LayerModel::LayerModel(QObject* parent)
: QAbstractListModel(parent)
, d(new Private(this))
{
connect(d->nodeModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(source_rowsAboutToBeInserted(QModelIndex,int,int)));
connect(d->nodeModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(source_rowsInserted(QModelIndex,int,int)));
connect(d->nodeModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(source_rowsAboutToBeRemoved(QModelIndex,int,int)));
connect(d->nodeModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(source_rowsRemoved(QModelIndex,int,int)));
connect(d->nodeModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(source_dataChanged(QModelIndex,QModelIndex)));
connect(d->nodeModel, SIGNAL(modelReset()),
this, SLOT(source_modelReset()));
connect(d->nodeModel, SIGNAL(layoutAboutToBeChanged()), this, SIGNAL(layoutAboutToBeChanged()));
connect(d->nodeModel, SIGNAL(layoutChanged()), this, SIGNAL(layoutChanged()));
}
LayerModel::~LayerModel()
{
delete d;
}
QHash<int, QByteArray> LayerModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[IconRole] = "icon";
roles[NameRole] = "name";
roles[ActiveLayerRole] = "activeLayer";
roles[OpacityRole] = "opacity";
roles[PercentOpacityRole] = "percentOpacity";
roles[VisibleRole] = "visible";
roles[CompositeDetailsRole] = "compositeDetails";
roles[FilterRole] = "filter";
roles[ChildCountRole] = "childCount";
roles[DeepChildCountRole] = "deepChildCount";
roles[DepthRole] = "depth";
roles[PreviousItemDepthRole] = "previousItemDepth";
roles[NextItemDepthRole] = "nextItemDepth";
roles[CanMoveDownRole] = "canMoveDown";
roles[CanMoveLeftRole] = "canMoveLeft";
roles[CanMoveRightRole] = "canMoveRight";
roles[CanMoveUpRole] = "canMoveUp";
return roles;
}
QObject* LayerModel::view() const
{
return d->view;
}
void LayerModel::setView(QObject *newView)
{
KisViewManager* view = qobject_cast<KisViewManager*>(newView);
// This has to happen very early, and we will be filling it back up again soon anyway...
if (d->canvas) {
d->canvas->disconnectCanvasObserver(this);
disconnect(d->image, 0, this, 0);
disconnect(d->nodeManager, 0, this, 0);
disconnect(d->nodeModel, 0, d->nodeManager, 0);
disconnect(d->nodeModel, SIGNAL(nodeActivated(KisNodeSP)), this, SLOT(currentNodeChanged(KisNodeSP)));
d->image = 0;
d->nodeManager = 0;
d->layers.clear();
d->activeNode.clear();
d->canvas = 0;
d->nodeModel->setDummiesFacade(0, 0, 0, 0, 0);
d->selectionActionsAdapter.reset();
}
d->view = view;
if (!d->view) {
return;
}
d->canvas = view->canvasBase();
d->thumbProvider = new LayerThumbProvider();
d->thumbProvider->setLayerModel(this);
d->thumbProvider->setLayerID(Private::counter());
// QT5TODO: results in a crash
d->declarativeEngine->addImageProvider(QString("layerthumb%1").arg(d->thumbProvider->layerID()), d->thumbProvider);
if (d->canvas) {
d->image = d->canvas->imageView()->image();
d->nodeManager = d->canvas->viewManager()->nodeManager();
KisDummiesFacadeBase *kritaDummiesFacade = dynamic_cast<KisDummiesFacadeBase*>(d->canvas->imageView()->document()->shapeController());
KisShapeController *shapeController = dynamic_cast<KisShapeController*>(d->canvas->imageView()->document()->shapeController());
d->selectionActionsAdapter.reset(new KisSelectionActionsAdapter(d->canvas->viewManager()->selectionManager()));
d->nodeModel->setDummiesFacade(kritaDummiesFacade,
d->image,
shapeController,
d->selectionActionsAdapter.data(),
d->nodeManager);
connect(d->image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted()));
connect(d->image, SIGNAL(sigNodeChanged(KisNodeSP)), SLOT(nodeChanged(KisNodeSP)));
connect(d->image, SIGNAL(sigImageUpdated(QRect)), SLOT(imageChanged()));
connect(d->image, SIGNAL(sigRemoveNodeAsync(KisNodeSP)), SLOT(aboutToRemoveNode(KisNodeSP)));
// cold start
currentNodeChanged(d->nodeManager->activeNode());
// Connection KisNodeManager -> KisLayerBox
connect(d->nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)), this, SLOT(currentNodeChanged(KisNodeSP)));
d->rebuildLayerList();
beginResetModel();
endResetModel();
}
}
QObject* LayerModel::engine() const
{
return d->declarativeEngine;
}
void LayerModel::setEngine(QObject* newEngine)
{
d->declarativeEngine = qobject_cast<QQmlEngine*>(newEngine);
emit engineChanged();
}
QString LayerModel::fullImageThumbUrl() const
{
return QString("image://layerthumb%1/fullimage/%2").arg(d->thumbProvider->layerID()).arg(QDateTime::currentMSecsSinceEpoch());
}
void LayerModel::currentNodeChanged(KisNodeSP newActiveNode)
{
if (!d->activeNode.isNull()) {
QModelIndex oldIndex = d->nodeModel->indexFromNode(d->activeNode);
source_dataChanged(oldIndex, oldIndex);
}
d->activeNode = newActiveNode;
emitActiveChanges();
if (!d->activeNode.isNull()) {
QModelIndex oldIndex = d->nodeModel->indexFromNode(d->activeNode);
source_dataChanged(oldIndex, oldIndex);
}
}
QVariant LayerModel::data(const QModelIndex& index, int role) const
{
QVariant data;
if (index.isValid()) {
index.internalPointer();
KisNodeSP node = d->layers.at(index.row());
if (node.isNull())
return data;
KisNodeSP parent;
switch(role)
{
case IconRole:
if (dynamic_cast<const KisGroupLayer*>(node.constData()))
data = QLatin1String("../images/svg/icon-layer_group-black.svg");
else if (dynamic_cast<const KisFilterMask*>(node.constData()))
data = QLatin1String("../images/svg/icon-layer_filter-black.svg");
else if (dynamic_cast<const KisAdjustmentLayer*>(node.constData()))
data = QLatin1String("../images/svg/icon-layer_filter-black.svg");
else
// We add the currentMSecsSinceEpoch to ensure we force an update (even with cache turned
// off, we still apparently get some caching behaviour on delegates in QML)
data = QString("image://layerthumb%1/%2/%3").arg(d->thumbProvider->layerID()).arg(index.row()).arg(QDateTime::currentMSecsSinceEpoch());
break;
case NameRole:
data = node->name();
break;
case ActiveLayerRole:
data = (node == d->activeNode);
break;
case OpacityRole:
data = node->opacity();
break;
case PercentOpacityRole:
data = node->percentOpacity();
break;
case VisibleRole:
data = node->visible();
break;
case CompositeDetailsRole:
// composite op goes here...
if (node->compositeOp())
data = node->compositeOp()->description();
break;
case FilterRole:
break;
case ChildCountRole:
data = node->childNodes(d->layerClassNames(), KoProperties()).count();
break;
case DeepChildCountRole:
data = d->deepChildCount(d->layers.at(index.row()));
break;
case DepthRole:
data = d->layerMeta[node.data()].depth;
break;
case PreviousItemDepthRole:
if (index.row() == 0)
data = -1;
else
data = d->layerMeta[d->layers[index.row() - 1].data()].depth;
break;
case NextItemDepthRole:
if (index.row() == d->layers.count() - 1)
data = -1;
else
data = d->layerMeta[d->layers[index.row() + 1].data()].depth;
break;
case CanMoveDownRole:
data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveDown;
break;
case CanMoveLeftRole:
data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveLeft;
break;
case CanMoveRightRole:
data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveRight;
break;
case CanMoveUpRole:
data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveUp;
break;
default:
break;
}
}
return data;
}
int LayerModel::rowCount(const QModelIndex& parent) const
{
if ( parent.isValid() ) {
return 0;
}
return d->layers.count();
}
QVariant LayerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
return QAbstractItemModel::headerData(section, orientation, role);
}
void LayerModel::setActive(int index)
{
if (index > -1 && index < d->layers.count()) {
KisNodeSP newNode = d->layers.at(index);
d->nodeManager->slotUiActivatedNode(newNode);
currentNodeChanged(newNode);
}
}
void LayerModel::moveUp()
{
KisNodeSP node = d->nodeManager->activeNode();
KisNodeSP parent = node->parent();
KisNodeSP grandParent = parent->parent();
if (!d->nodeManager->activeNode()->nextSibling()) {
//dbgKrita << "Active node apparently has no next sibling, however that has happened...";
if (!grandParent)
return;
//dbgKrita << "Node has grandparent";
if (!grandParent->parent() && node->inherits("KisMask"))
return;
//dbgKrita << "Node isn't a mask";
d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1);
}
else {
//dbgKrita << "Move node directly";
d->nodeManager->lowerNode();
}
}
void LayerModel::moveDown()
{
KisNodeSP node = d->nodeManager->activeNode();
KisNodeSP parent = node->parent();
KisNodeSP grandParent = parent->parent();
if (!d->nodeManager->activeNode()->prevSibling()) {
//dbgKrita << "Active node apparently has no previous sibling, however that has happened...";
if (!grandParent)
return;
//dbgKrita << "Node has grandparent";
if (!grandParent->parent() && node->inherits("KisMask"))
return;
//dbgKrita << "Node isn't a mask";
d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent));
} else
{
//dbgKrita << "Move node directly";
d->nodeManager->raiseNode();
}
}
void LayerModel::moveLeft()
{
KisNodeSP node = d->nodeManager->activeNode();
KisNodeSP parent = node->parent();
KisNodeSP grandParent = parent->parent();
quint16 nodeIndex = parent->index(node);
if (!grandParent)
return;
if (!grandParent->parent() && node->inherits("KisMask"))
return;
if (nodeIndex <= parent->childCount() / 2) {
d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent));
}
else {
d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1);
}
}
void LayerModel::moveRight()
{
KisNodeSP node = d->nodeManager->activeNode();
KisNodeSP parent = d->nodeManager->activeNode()->parent();
KisNodeSP newParent;
int nodeIndex = parent->index(node);
int indexAbove = nodeIndex + 1;
int indexBelow = nodeIndex - 1;
if (parent->at(indexBelow) && parent->at(indexBelow)->allowAsChild(node)) {
newParent = parent->at(indexBelow);
d->nodeManager->moveNodeAt(node, newParent, newParent->childCount());
}
else if (parent->at(indexAbove) && parent->at(indexAbove)->allowAsChild(node)) {
newParent = parent->at(indexAbove);
d->nodeManager->moveNodeAt(node, newParent, 0);
}
else {
return;
}
}
void LayerModel::clear()
{
d->canvas->viewManager()->selectionManager()->clear();
}
void LayerModel::clone()
{
d->nodeManager->duplicateActiveNode();
}
void LayerModel::setLocked(int index, bool newLocked)
{
if (index > -1 && index < d->layers.count()) {
if(d->layers[index]->userLocked() == newLocked)
return;
d->layers[index]->setUserLocked(newLocked);
QModelIndex idx = createIndex(index, 0);
dataChanged(idx, idx);
}
}
void LayerModel::setOpacity(int index, float newOpacity)
{
if (index > -1 && index < d->layers.count()) {
if(qFuzzyCompare(d->layers[index]->opacity() + 1, newOpacity + 1))
return;
d->layers[index]->setOpacity(newOpacity);
d->layers[index]->setDirty();
QModelIndex idx = createIndex(index, 0);
dataChanged(idx, idx);
}
}
void LayerModel::setVisible(int index, bool newVisible)
{
if (index > -1 && index < d->layers.count()) {
KisBaseNode::PropertyList props = d->layers[index]->sectionModelProperties();
if(props[0].state == newVisible)
return;
KisBaseNode::Property prop = props[0];
prop.state = newVisible;
props[0] = prop;
d->nodeModel->setData( d->nodeModel->indexFromNode(d->layers[index]), QVariant::fromValue<KisBaseNode::PropertyList>(props), KisNodeModel::PropertiesRole );
d->layers[index]->setDirty(d->layers[index]->extent());
QModelIndex idx = createIndex(index, 0);
dataChanged(idx, idx);
}
}
QImage LayerModel::layerThumbnail(QString layerID) const
{
// So, yeah, this is a complete cheatery hack. However, it ensures
// we actually get updates when we want them (every time the image is supposed
// to be changed). Had hoped we could avoid it, but apparently not.
int index = layerID.section(QChar('/'), 0, 0).toInt();
QImage thumb;
if (index > -1 && index < d->layers.count()) {
if (d->thumbProvider)
thumb = d->layers[index]->createThumbnail(120, 120);
}
return thumb;
}
void LayerModel::deleteCurrentLayer()
{
d->activeNode.clear();
d->nodeManager->removeNode();
}
void LayerModel::deleteLayer(int index)
{
if (index > -1 && index < d->layers.count()) {
if (d->activeNode == d->layers.at(index))
d->activeNode.clear();
d->nodeManager->slotUiActivatedNode(d->layers.at(index));
d->nodeManager->removeNode();
d->rebuildLayerList();
beginResetModel();
endResetModel();
}
}
void LayerModel::addLayer(int layerType)
{
switch(layerType) {
case 0:
d->nodeManager->createNode("KisPaintLayer");
break;
case 1:
d->nodeManager->createNode("KisGroupLayer");
break;
case 2:
d->nodeManager->createNode("KisFilterMask", true);
break;
default:
break;
}
}
void LayerModel::source_rowsAboutToBeInserted(QModelIndex /*p*/, int /*from*/, int /*to*/)
{
beginResetModel();
}
void LayerModel::source_rowsInserted(QModelIndex /*p*/, int, int)
{
d->rebuildLayerList();
emit countChanged();
endResetModel();
}
void LayerModel::source_rowsAboutToBeRemoved(QModelIndex /*p*/, int /*from*/, int /*to*/)
{
beginResetModel();
}
void LayerModel::source_rowsRemoved(QModelIndex, int, int)
{
d->rebuildLayerList();
emit countChanged();
endResetModel();
}
void LayerModel::source_dataChanged(QModelIndex /*tl*/, QModelIndex /*br*/)
{
QModelIndex top = createIndex(0, 0);
QModelIndex bottom = createIndex(d->layers.count() - 1, 0);
dataChanged(top, bottom);
}
void LayerModel::source_modelReset()
{
beginResetModel();
d->rebuildLayerList();
d->activeNode.clear();
if (d->layers.count() > 0)
{
d->nodeManager->slotUiActivatedNode(d->layers.at(0));
currentNodeChanged(d->layers.at(0));
}
emit countChanged();
endResetModel();
}
void LayerModel::notifyImageDeleted()
{
}
void LayerModel::nodeChanged(KisNodeSP node)
{
QModelIndex index = createIndex(d->layers.indexOf(node), 0);
dataChanged(index, index);
}
void LayerModel::imageChanged()
{
d->imageChangedTimer->start();
}
void LayerModel::imageHasChanged()
{
QModelIndex top = createIndex(0, 0);
QModelIndex bottom = createIndex(d->layers.count() - 1, 0);
dataChanged(top, bottom);
}
void LayerModel::aboutToRemoveNode(KisNodeSP node)
{
Q_UNUSED(node)
QTimer::singleShot(0, this, SLOT(source_modelReset()));
}
void LayerModel::emitActiveChanges()
{
emit activeFilterConfigChanged();
emit activeNameChanged();
emit activeTypeChanged();
emit activeCompositeOpChanged();
emit activeOpacityChanged();
emit activeVisibleChanged();
emit activeLockedChanged();
emit activeRChannelActiveChanged();
emit activeGChannelActiveChanged();
emit activeBChannelActiveChanged();
emit activeAChannelActiveChanged();
}
QString LayerModel::activeName() const
{
if (d->activeNode.isNull())
return QString();
return d->activeNode->name();
}
void LayerModel::setActiveName(QString newName)
{
if (d->activeNode.isNull())
return;
d->activeNode->setName(newName);
emit activeNameChanged();
}
QString LayerModel::activeType() const
{
return d->activeNode->metaObject()->className();
}
int LayerModel::activeCompositeOp() const
{
if (d->activeNode.isNull())
return 0;
KoID entry(d->activeNode->compositeOp()->id());
QModelIndex idx = KisCompositeOpListModel::sharedInstance()->indexOf(entry);
if (idx.isValid())
return idx.row();
return 0;
}
void LayerModel::setActiveCompositeOp(int newOp)
{
if (d->activeNode.isNull())
return;
KoID entry;
if (KisCompositeOpListModel::sharedInstance()->entryAt(entry, KisCompositeOpListModel::sharedInstance()->index(newOp)))
{
d->activeNode->setCompositeOpId(entry.id());
d->activeNode->setDirty();
emit activeCompositeOpChanged();
}
}
int LayerModel::activeOpacity() const
{
if (d->activeNode.isNull())
return 0;
return d->activeNode->opacity();
}
void LayerModel::setActiveOpacity(int newOpacity)
{
d->activeNode->setOpacity(newOpacity);
d->activeNode->setDirty();
emit activeOpacityChanged();
}
bool LayerModel::activeVisible() const
{ if (d->activeNode.isNull())
return false;
return d->activeNode->visible();
}
void LayerModel::setActiveVisible(bool newVisible)
{
if (d->activeNode.isNull())
return;
setVisible(d->layers.indexOf(d->activeNode), newVisible);
emit activeVisibleChanged();
}
bool LayerModel::activeLocked() const
{
if (d->activeNode.isNull())
return false;
return d->activeNode->userLocked();
}
void LayerModel::setActiveLocked(bool newLocked)
{
if (d->activeNode.isNull())
return;
d->activeNode->setUserLocked(newLocked);
emit activeLockedChanged();
}
bool LayerModel::activeAChannelActive() const
{
KisLayer* layer = qobject_cast<KisLayer*>(d->activeNode.data());
bool state = false;
if (layer)
state = !layer->alphaChannelDisabled();
return state;
}
void LayerModel::setActiveAChannelActive(bool newActive)
{
KisLayer* layer = qobject_cast<KisLayer*>(d->activeNode.data());
if (layer)
{
layer->disableAlphaChannel(!newActive);
layer->setDirty();
emit activeAChannelActiveChanged();
}
}
bool getActiveChannel(KisNodeSP node, int channelIndex)
{
KisLayer* layer = qobject_cast<KisLayer*>(node.data());
bool flag = false;
if (layer)
{
QBitArray flags = layer->channelFlags();
if (channelIndex < flags.size()) {
flag = flags[channelIndex];
}
}
return flag;
}
void setChannelActive(KisNodeSP node, int channelIndex, bool newActive)
{
KisLayer* layer = qobject_cast<KisLayer*>(node.data());
if (layer) {
QBitArray flags = layer->channelFlags();
flags.setBit(channelIndex, newActive);
layer->setChannelFlags(flags);
layer->setDirty();
}
}
bool LayerModel::activeBChannelActive() const
{
return getActiveChannel(d->activeNode, 2);
}
void LayerModel::setActiveBChannelActive(bool newActive)
{
setChannelActive(d->activeNode, 2, newActive);
emit activeBChannelActiveChanged();
}
bool LayerModel::activeGChannelActive() const
{
return getActiveChannel(d->activeNode, 1);
}
void LayerModel::setActiveGChannelActive(bool newActive)
{
setChannelActive(d->activeNode, 1, newActive);
emit activeGChannelActiveChanged();
}
bool LayerModel::activeRChannelActive() const
{
return getActiveChannel(d->activeNode, 0);
}
void LayerModel::setActiveRChannelActive(bool newActive)
{
setChannelActive(d->activeNode, 0, newActive);
emit activeRChannelActiveChanged();
}
QObject* LayerModel::activeFilterConfig() const
{
QMap<QString, QVariant> props;
QString filterId;
KisFilterMask* filterMask = qobject_cast<KisFilterMask*>(d->activeNode.data());
if (filterMask) {
props = filterMask->filter()->getProperties();
filterId = filterMask->filter()->name();
}
else {
KisAdjustmentLayer* adjustmentLayer = qobject_cast<KisAdjustmentLayer*>(d->activeNode.data());
if (adjustmentLayer)
{
props = adjustmentLayer->filter()->getProperties();
filterId = adjustmentLayer->filter()->name();
}
}
PropertyContainer* config = new PropertyContainer(filterId, 0);
QMap<QString, QVariant>::const_iterator i;
for(i = props.constBegin(); i != props.constEnd(); ++i) {
config->setProperty(i.key().toLatin1(), i.value());
//dbgKrita << "Getting active config..." << i.key() << i.value();
}
return config;
}
void LayerModel::setActiveFilterConfig(QObject* newConfig)
{
if (d->activeNode.isNull())
return;
PropertyContainer* config = qobject_cast<PropertyContainer*>(newConfig);
if (!config)
return;
//dbgKrita << "Attempting to set new config" << config->name();
- KisFilterConfigurationSP realConfig = d->filters.value(config->name())->factoryConfiguration();
+ KisFilterConfigurationSP realConfig = d->filters.value(config->name())->factoryConfiguration(KisGlobalResourcesInterface::instance());
QMap<QString, QVariant>::const_iterator i;
for(i = realConfig->getProperties().constBegin(); i != realConfig->getProperties().constEnd(); ++i)
{
realConfig->setProperty(i.key(), config->property(i.key().toLatin1()));
//dbgKrita << "Creating config..." << i.key() << i.value();
}
// The following code causes sporadic crashes, and disabling causes leaks. So, leaks it must be, for now.
// The cause is the lack of a smart pointer interface for passing filter configs around
// Must be remedied, but for now...
// if (d->newConfig)
// delete(d->newConfig);
d->newConfig = realConfig;
//d->updateActiveLayerWithNewFilterConfigTimer->start();
updateActiveLayerWithNewFilterConfig();
}
void LayerModel::updateActiveLayerWithNewFilterConfig()
{
if (!d->newConfig)
return;
//dbgKrita << "Setting new config..." << d->newConfig->name();
KisFilterMask* filterMask = qobject_cast<KisFilterMask*>(d->activeNode.data());
if (filterMask)
{
- //dbgKrita << "Filter mask";
- if (filterMask->filter() == d->newConfig)
- return;
//dbgKrita << "Setting filter mask";
- filterMask->setFilter(d->newConfig);
+ filterMask->setFilter(d->newConfig->cloneWithResourcesSnapshot());
}
else
{
KisAdjustmentLayer* adjustmentLayer = qobject_cast<KisAdjustmentLayer*>(d->activeNode.data());
if (adjustmentLayer)
{
- //dbgKrita << "Adjustment layer";
- if (adjustmentLayer->filter() == d->newConfig)
- return;
//dbgKrita << "Setting filter on adjustment layer";
- adjustmentLayer->setFilter(d->newConfig);
+ adjustmentLayer->setFilter(d->newConfig->cloneWithResourcesSnapshot());
}
else
{
//dbgKrita << "UNKNOWN, BAIL OUT!";
}
}
d->newConfig = 0;
d->activeNode->setDirty(d->activeNode->extent());
d->image->setModified();
QTimer::singleShot(100, this, SIGNAL(activeFilterConfigChanged()));
}
diff --git a/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.cpp
index eb0d1f08e1..797219bd4b 100644
--- a/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.cpp
+++ b/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.cpp
@@ -1,151 +1,155 @@
/* This file is part of the KDE project
* 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 "PaletteColorsModel.h"
#include <KisViewManager.h>
#include <kis_canvas_resource_provider.h>
#include <resources/KoColorSet.h>
class PaletteColorsModel::Private {
public:
Private()
: colorSet(0)
, view(0)
{}
- KoColorSet* colorSet;
+ KoColorSetSP colorSet;
KisViewManager* view;
};
PaletteColorsModel::PaletteColorsModel(QObject *parent)
: QAbstractListModel(parent)
, d(new Private)
{
}
PaletteColorsModel::~PaletteColorsModel()
{
delete d;
}
QHash<int, QByteArray> PaletteColorsModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[ImageRole] = "image";
roles[TextRole] = "text";
return roles;
}
int PaletteColorsModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
if (!d->colorSet)
return 0;
return d->colorSet->colorCount();
}
QVariant PaletteColorsModel::data(const QModelIndex &/*index*/, int /*role*/) const
{
QVariant result;
/*
QColor color;
if (index.isValid() && d->colorSet)
{
switch(role)
{
case ImageRole:
color = d->colorSet->getColorGlobal(index.row(), index.column()).color().toQColor();
result = QString("image://color/%1,%2,%3,%4").arg(color.redF()).arg(color.greenF()).arg(color.blueF()).arg(color.alphaF());
break;
case TextRole:
result = d->colorSet->getColorGlobal(index.row(), index.column()).name();
break;
default:
break;
}
}
*/
return result;
}
QVariant PaletteColorsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(orientation);
QVariant result;
if (section == 0)
{
switch(role)
{
case ImageRole:
result = QString("Thumbnail");
break;
case TextRole:
result = QString("Name");
break;
default:
break;
}
}
return result;
}
void PaletteColorsModel::setColorSet(QObject *newColorSet)
{
- d->colorSet = qobject_cast<KoColorSet*>(newColorSet);
+ // XXX SharedPtr We need to wrap KoColorSet
+
+ //d->colorSet = qobject_cast<KoColorSet*>(newColorSet);
beginResetModel();
endResetModel();
emit colorSetChanged();
}
QObject* PaletteColorsModel::colorSet() const
{
- return d->colorSet;
+ // XXX SharedPtr We need to wrap KoColorSet
+ // return d->colorSet;
+ return 0;
}
QObject* PaletteColorsModel::view() const
{
return d->view;
}
void PaletteColorsModel::setView(QObject* newView)
{
d->view = qobject_cast<KisViewManager*>( newView );
emit viewChanged();
}
void PaletteColorsModel::activateColor(int index, bool /*setBackgroundColor*/)
{
if ( !d->view )
return;
if (index >= 0 && index < (int)d->colorSet->colorCount()) {
/*
if (setBackgroundColor)
d->view->resourceProvider()->setBGColor(d->colorSet->getColorGlobal(index).color());
else
d->view->resourceProvider()->setFGColor( d->colorSet->getColorGlobal(index).color());
emit colorChanged(d->colorSet->getColorGlobal(index).color().toQColor(), setBackgroundColor);
*/
}
}
diff --git a/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.h b/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.h
index cfd53e5504..71603be0cf 100644
--- a/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.h
+++ b/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.h
@@ -1,61 +1,62 @@
/* This file is part of the KDE project
* 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.
*/
#ifndef PALETTECOLORSMODEL_H
#define PALETTECOLORSMODEL_H
#include <QAbstractListModel>
#include <QColor>
+#include <KoColorSet.h>
class PaletteColorsModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QObject* colorSet READ colorSet WRITE setColorSet NOTIFY colorSetChanged)
Q_PROPERTY(QObject* view READ view WRITE setView NOTIFY viewChanged)
public:
enum PaletteColorsRoles {
ImageRole = Qt::UserRole + 1,
TextRole
};
explicit PaletteColorsModel(QObject *parent = 0);
virtual ~PaletteColorsModel();
QHash<int, QByteArray> roleNames() const;
virtual int rowCount(const QModelIndex &parent) const;
virtual QVariant data(const QModelIndex &index, int role) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
QObject* view() const;
void setView(QObject* newView);
QObject* colorSet() const;
void setColorSet(QObject* newColorSet);
Q_SIGNALS:
void colorChanged(QColor newColor, bool backgroundChanged);
void colorSetChanged();
void viewChanged();
public Q_SLOTS:
void activateColor(int index, bool setBackgroundColor);
private:
class Private;
Private* d;
};
#endif // PALETTECOLORSMODEL_H
diff --git a/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.cpp
deleted file mode 100644
index 1c8ee8e3de..0000000000
--- a/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/* This file is part of the KDE project
- * 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 "PaletteModel.h"
-
-#include <resources/KoColorSet.h>
-#include <KoResourceServerAdapter.h>
-#include <KoResourceServerProvider.h>
-#include <KisResourceServerProvider.h>
-
-class PaletteModel::Private {
-public:
- Private(QObject* q)
- : currentSet(0)
- {
- KoResourceServer<KoColorSet>* rServer = KoResourceServerProvider::instance()->paletteServer();
- serverAdaptor = new KoResourceServerAdapter<KoColorSet>(rServer, q);
- serverAdaptor->connectToResourceServer();
- }
- KoResourceServerAdapter<KoColorSet>* serverAdaptor;
- KoColorSet* currentSet;
-};
-
-PaletteModel::PaletteModel(QObject *parent)
- : QAbstractListModel(parent)
- , d(new Private(this))
-{
-}
-
-PaletteModel::~PaletteModel()
-{
- delete d;
-}
-
-QHash<int, QByteArray> PaletteModel::roleNames() const
-{
- QHash<int, QByteArray> roles;
- roles[ImageRole] = "image";
- roles[TextRole] = "text";
-
- return roles;
-}
-
-int PaletteModel::rowCount(const QModelIndex &parent) const
-{
- if (parent.isValid())
- return 0;
- return d->serverAdaptor->resources().count();
-}
-
-QVariant PaletteModel::data(const QModelIndex &index, int role) const
-{
- QVariant result;
- if (index.isValid())
- {
- switch(role)
- {
- case ImageRole:
- result = "../images/help-about.png";
- break;
- case TextRole:
- result = d->serverAdaptor->resources().at(index.row())->name();
- break;
- default:
- break;
- }
- }
- return result;
-}
-
-QVariant PaletteModel::headerData(int section, Qt::Orientation orientation, int role) const
-{
- Q_UNUSED(orientation);
- QVariant result;
- if (section == 0)
- {
- switch(role)
- {
- case ImageRole:
- result = QString("Thumbnail");
- break;
- case TextRole:
- result = QString("Name");
- break;
- default:
- break;
- }
- }
- return result;
-}
-
-void PaletteModel::itemActivated(int index)
-{
- QList<KoResource*> resources = d->serverAdaptor->resources();
- if (index >= 0 && index < resources.count())
- {
- d->currentSet = dynamic_cast<KoColorSet*>(resources.at(index));
- emit colorSetChanged();
- }
-}
-
-QObject* PaletteModel::colorSet() const
-{
- return d->currentSet;
-}
-
diff --git a/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.h b/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.h
deleted file mode 100644
index db342afcd9..0000000000
--- a/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* This file is part of the KDE project
- * 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.
- */
-
-#ifndef PALETTEMODEL_H
-#define PALETTEMODEL_H
-
-#include <QAbstractListModel>
-
-class PaletteModel : public QAbstractListModel
-{
- Q_OBJECT
- Q_PROPERTY(QObject* colorSet READ colorSet NOTIFY colorSetChanged)
-public:
- enum PaletteRoles {
- ImageRole = Qt::UserRole + 1,
- TextRole
- };
- explicit PaletteModel(QObject *parent = 0);
- virtual ~PaletteModel();
- QHash<int, QByteArray> roleNames() const;
- virtual int rowCount(const QModelIndex &parent) const;
- virtual QVariant data(const QModelIndex &index, int role) const;
- virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
-
- QObject* colorSet() const;
-
-public Q_SLOTS:
- void itemActivated(int index);
-
-Q_SIGNALS:
- void colorSetChanged();
-
-private:
- class Private;
- Private* d;
-};
-
-#endif // PALETTEMODEL_H
diff --git a/libs/libqml/plugins/kritasketchplugin/models/PresetImageProvider.cpp b/libs/libqml/plugins/kritasketchplugin/models/PresetImageProvider.cpp
deleted file mode 100644
index 6cfcfdc20d..0000000000
--- a/libs/libqml/plugins/kritasketchplugin/models/PresetImageProvider.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/* This file is part of the KDE project
- * 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 "PresetImageProvider.h"
-
-#include <KoResourceServerAdapter.h>
-#include <KisResourceServerProvider.h>
-#include <brushengine/kis_paintop_preset.h>
-
-class PresetImageProvider::Private {
-public:
- Private()
- {
- rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
- }
-
- KisPaintOpPresetResourceServer * rserver ;
-};
-
-PresetImageProvider::PresetImageProvider()
- : QQuickImageProvider(QQuickImageProvider::Image)
- , d(new Private)
-{
-}
-
-QImage PresetImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
-{
- Q_UNUSED(size);
- QImage image(requestedSize, QImage::Format_ARGB32);
- QList<KisPaintOpPresetSP> resources = d->rserver->resources();
- int theID = id.toInt();
- if (theID >= 0 && theID < resources.count())
- {
- image = resources.at(theID)->image();
- }
- return image;
-}
diff --git a/libs/libqml/plugins/kritasketchplugin/models/PresetImageProvider.h b/libs/libqml/plugins/kritasketchplugin/models/PresetImageProvider.h
deleted file mode 100644
index d8c45aad7d..0000000000
--- a/libs/libqml/plugins/kritasketchplugin/models/PresetImageProvider.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* This file is part of the KDE project
- * 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.
- */
-
-#ifndef PRESETIMAGEPROVIDER_H
-#define PRESETIMAGEPROVIDER_H
-
-#include <QQuickImageProvider>
-
-class PresetImageProvider : public QQuickImageProvider
-{
-public:
- explicit PresetImageProvider();
- QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
-
-private:
- class Private;
- Private* d;
-};
-
-#endif // PRESETIMAGEPROVIDER_H
diff --git a/libs/libqml/plugins/kritasketchplugin/models/PresetModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/PresetModel.cpp
deleted file mode 100644
index af81f5411b..0000000000
--- a/libs/libqml/plugins/kritasketchplugin/models/PresetModel.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
-/* This file is part of the KDE project
- * 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 "PresetModel.h"
-
-#include <KoResourceServerAdapter.h>
-#include <KisResourceServerProvider.h>
-#include <KisViewManager.h>
-#include <kis_canvas_resource_provider.h>
-#include <kis_canvas2.h>
-#include <kis_paintop_box.h>
-#include <brushengine/kis_paintop_preset.h>
-#include <brushengine/kis_paintop_factory.h>
-#include <brushengine/kis_paintop_registry.h>
-#include <kis_node.h>
-#include <kis_image.h>
-
-class PresetModel::Private {
-public:
- Private()
- : view(0)
- {
- rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
- }
-
- KisPaintOpPresetResourceServer * rserver;
- QString currentPreset;
- KisViewManager* view;
-
- KisPaintOpPresetSP 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;
- }
-
- void setCurrentPaintop(const KoID& paintop, KisPaintOpPresetSP preset)
- {
- preset = (!preset) ? defaultPreset(paintop) : preset;
-
- Q_ASSERT(preset && preset->settings());
-
- // handle the settings and expose it through a a simple QObject property
- //m_optionWidget->setConfiguration(preset->settings());
-#if 0
- preset->settings()->setNode(view->resourceProvider()->currentNode());
-#endif
-
- view->canvasResourceProvider()->setPaintOpPreset(preset);
- }
-};
-
-PresetModel::PresetModel(QObject *parent)
- : QAbstractListModel(parent)
- , d(new Private)
-{
-}
-
-PresetModel::~PresetModel()
-{
- delete d;
-}
-
-QHash<int, QByteArray> PresetModel::roleNames() const
-{
- QHash<int, QByteArray> roles;
- roles[ImageRole] = "image";
- roles[TextRole] = "text";
- roles[NameRole] = "name";
-
- return roles;
-}
-
-int PresetModel::rowCount(const QModelIndex &parent) const
-{
- if (parent.isValid())
- return 0;
- return d->rserver->resources().count();
-}
-
-QVariant PresetModel::data(const QModelIndex &index, int role) const
-{
- QVariant result;
- if (index.isValid())
- {
- switch(role)
- {
- case ImageRole:
- result = QString("image://presetthumb/%1").arg(index.row());
- break;
- case TextRole:
- result = d->rserver->resources().at(index.row())->name().replace(QLatin1String("_"), QLatin1String(" "));
- break;
- case NameRole:
- result = d->rserver->resources().at(index.row())->name();
- break;
- default:
- result = "";
- break;
- }
- }
- return result;
-}
-
-QVariant PresetModel::headerData(int section, Qt::Orientation orientation, int role) const
-{
- Q_UNUSED(orientation);
- QVariant result;
- if (section == 0)
- {
- switch(role)
- {
- case ImageRole:
- result = QString("Thumbnail");
- break;
- case TextRole:
- result = QString("Name");
- break;
- default:
- result = "";
- break;
- }
- }
- return result;
-}
-
-QObject* PresetModel::view() const
-{
- return d->view;
-}
-
-void PresetModel::setView(QObject* newView)
-{
- d->view = qobject_cast<KisViewManager*>( newView );
- if (d->view && d->view->canvasBase()) {
- connect(d->view->canvasBase()->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
- this, SLOT(resourceChanged(int,QVariant)));
- }
- emit viewChanged();
-}
-
-QString PresetModel::currentPreset() const
-{
- return d->currentPreset;
-}
-
-void PresetModel::setCurrentPreset(QString presetName)
-{
- activatePreset(nameToIndex(presetName));
- // not emitting here, as that happens when the resource changes... this is more
- // a polite request than an actual setter, due to the nature of the resource system.
-}
-
-int PresetModel::nameToIndex(QString presetName) const
-{
- int index = 0;
- QList<KisPaintOpPresetSP> resources = d->rserver->resources();
- for(int i = 0; i < resources.count(); ++i)
- {
- if (resources.at(i)->name() == presetName || resources.at(i)->name().replace(QLatin1String("_"), QLatin1String(" ")) == presetName)
- {
- index = i;
- break;
- }
- }
- return index;
-}
-
-void PresetModel::activatePreset(int index)
-{
- if ( !d->view )
- return;
-
- QList<KisPaintOpPresetSP> resources = d->rserver->resources();
- if (index >= 0 && index < resources.count()) {
- KisPaintOpPresetSP preset = resources.at( index );
- d->setCurrentPaintop(preset->paintOp(), preset);
- }
-}
-
-void PresetModel::resourceChanged(int /*key*/, const QVariant& /*v*/)
-{
- if (d->view)
- {
- KisPaintOpPresetSP preset = d->view->canvasBase()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
- if (preset && d->currentPreset != preset->name())
- {
- d->currentPreset = preset->name();
- emit currentPresetChanged();
- }
- }
-}
-
diff --git a/libs/libqml/plugins/kritasketchplugin/models/PresetModel.h b/libs/libqml/plugins/kritasketchplugin/models/PresetModel.h
deleted file mode 100644
index 34dd4320c5..0000000000
--- a/libs/libqml/plugins/kritasketchplugin/models/PresetModel.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* This file is part of the KDE project
- * 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.
- */
-
-#ifndef PRESETMODEL_H
-#define PRESETMODEL_H
-
-#include <QAbstractListModel>
-
-class PresetModel : public QAbstractListModel
-{
- Q_OBJECT
- Q_PROPERTY(QObject* view READ view WRITE setView NOTIFY viewChanged)
- Q_PROPERTY(QString currentPreset READ currentPreset WRITE setCurrentPreset NOTIFY currentPresetChanged)
-public:
- enum PresetRoles {
- ImageRole = Qt::UserRole + 1,
- TextRole,
- NameRole
- };
-
- explicit PresetModel(QObject *parent = 0);
- virtual ~PresetModel();
- QHash<int, QByteArray> roleNames() const;
- virtual int rowCount(const QModelIndex &parent) const;
- virtual QVariant data(const QModelIndex &index, int role) const;
- virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
-
- QObject* view() const;
- void setView(QObject* newView);
-
- QString currentPreset() const;
- void setCurrentPreset(QString presetName);
-
- Q_INVOKABLE int nameToIndex(QString presetName) const;
-
-Q_SIGNALS:
- void viewChanged();
- void currentPresetChanged();
-
-public Q_SLOTS:
- void activatePreset(int index);
- void resourceChanged(int key, const QVariant& v);
-
-private:
- class Private;
- Private* d;
-};
-
-#endif // PRESETMODEL_H
diff --git a/libs/odf/tests/TestXmlWriter.cpp b/libs/odf/tests/TestXmlWriter.cpp
index caeca47df5..2d25fde0bb 100644
--- a/libs/odf/tests/TestXmlWriter.cpp
+++ b/libs/odf/tests/TestXmlWriter.cpp
@@ -1,253 +1,254 @@
/* This file is part of the KDE project
* Copyright (C) 2004 David Faure <faure@kde.org>
* Copyright 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.
*/
#include <KoXmlWriter.h>
#include <QString>
#include <QBuffer>
#include <QTest>
+#include <QElapsedTimer>
class TestXmlWriter : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testDocytype();
void testEmtpyElement();
void testAttributes();
void testIndent();
void testTextNode();
void testTextSpan();
void testTextSpanWithTabCache();
void testProcessingInstruction();
void testAddManifestEntry();
void testEscapingLongString();
void testEscalingLongString2();
void testConfig();
void speedTest();
private:
void setup(const char *publicId = 0, const char *systemId = 0);
QString content();
KoXmlWriter *writer;
QBuffer *buffer;
};
void TestXmlWriter::setup(const char *publicId, const char *systemId)
{
buffer = new QBuffer();
buffer->open( QIODevice::WriteOnly );
writer = new KoXmlWriter( buffer );
writer->startDocument( "dummy", publicId, systemId );
writer->startElement( "dummy" );
}
QString TestXmlWriter::content()
{
writer->endElement();
writer->endDocument();
buffer->putChar( '\0' ); /*null-terminate*/
buffer->close();
QString stringContent = QString::fromUtf8(buffer->data());
int index = stringContent.indexOf("<dummy");
Q_ASSERT(index);
index = stringContent.indexOf('>', index);
stringContent = stringContent.mid(index+1, stringContent.length() - index - 11).trimmed();
return stringContent;
}
void TestXmlWriter::testDocytype()
{
setup("foo", "bar");
QCOMPARE(content(), QString());
QString stringContent = QString::fromUtf8(buffer->data());
QCOMPARE(stringContent, QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE dummy PUBLIC \"foo\" \"bar\">\n<dummy/>\n"));
}
void TestXmlWriter::testAttributes()
{
setup();
writer->startElement("test");
writer->addAttribute("a", "val");
writer->addAttribute("b", "<\">");
writer->addAttribute("c", -42);
writer->addAttribute("d", 1234.56789012345);
writer->addAttribute("f", false);
writer->addAttribute("g", true);
writer->endElement();
QCOMPARE(content(), QString("<test a=\"val\" b=\"&lt;&quot;&gt;\" c=\"-42\" d=\"1234.56789012345\" f=\"false\" g=\"true\"/>"));
}
void TestXmlWriter::testEmtpyElement()
{
setup();
writer->startElement("m");
writer->endElement();
QCOMPARE(content(), QString("<m/>"));
}
void TestXmlWriter::testIndent()
{
setup();
writer->startElement("a");
writer->startElement("b");
writer->startElement("c");
writer->endElement();
writer->endElement();
writer->endElement();
QCOMPARE(content(), QString("<a>\n <b>\n <c/>\n </b>\n </a>"));
}
void TestXmlWriter::testTextNode()
{
setup();
writer->startElement("a");
writer->startElement("b", false /*no indent*/);
writer->startElement("c");
writer->endElement();
writer->addTextNode("te");
writer->addTextNode("xt");
writer->endElement();
writer->endElement();
QCOMPARE(content(), QString("<a><b><c/>text</b>\n </a>"));
}
void TestXmlWriter::testTextSpan()
{
setup();
writer->startElement("p", false /*no indent*/);
writer->addTextSpan(QString::fromLatin1(" \t\n foo "));
writer->endElement();
QCOMPARE(content(), QString("<p><text:s text:c=\"3\"/><text:tab/><text:line-break/> foo<text:s text:c=\"2\"/></p>"));
}
void TestXmlWriter::testTextSpanWithTabCache()
{
setup();
writer->startElement("p", false /*no indent*/);
QMap<int, int> tabCache;
tabCache.insert(3, 0);
writer->addTextSpan(QString::fromUtf8(" \t\n foö "), tabCache);
writer->endElement();
QCOMPARE(content(), QString::fromUtf8("<p><text:s text:c=\"3\"/><text:tab text:tab-ref=\"1\"/>"
"<text:line-break/> foö<text:s text:c=\"2\"/></p>"));
}
void TestXmlWriter::testProcessingInstruction()
{
setup();
writer->startElement("p", false /*no indent*/);
writer->addProcessingInstruction("opendocument foobar");
writer->addTextSpan(QString::fromLatin1("foo"));
writer->endElement();
QCOMPARE(content(), QString("<p><?opendocument foobar?>foo</p>"));
}
void TestXmlWriter::testAddManifestEntry()
{
setup();
writer->addManifestEntry(QString::fromLatin1("foo/bar/blah"), QString::fromLatin1("mime/type"));
QCOMPARE(content(), QString("<manifest:file-entry manifest:media-type=\"mime/type\" "
"manifest:full-path=\"foo/bar/blah\"/>"));
}
void TestXmlWriter::testEscapingLongString()
{
int sz = 15000; // must be more than KoXmlWriter::s_escapeBufferLen
QString x(sz);
x.fill('x', sz);
x += '&';
setup();
writer->startElement("test");
writer->addAttribute("a", x);
writer->endElement();
QString expected = "<test a=\"";
expected += x + "amp;\"/>";
QCOMPARE(content(), QString(expected));
}
void TestXmlWriter::testEscalingLongString2()
{
QString longPath;
for (uint i = 0 ; i < 1000 ; ++i)
longPath += QString::fromLatin1("M10 10L20 20 ");
setup();
writer->startElement("test");
writer->addAttribute("a", longPath);
writer->endElement();
QString expected = "<test a=\"";
expected += longPath.toUtf8() + "\"/>";
QCOMPARE(content(), expected);
}
void TestXmlWriter::testConfig()
{
setup();
const bool val = true;
const int num = 1;
const qreal numdouble = 5.0;
writer->addConfigItem(QString::fromLatin1("TestConfigBool"), val);
writer->addConfigItem(QString::fromLatin1("TestConfigInt"), num);
writer->addConfigItem(QString::fromLatin1("TestConfigDouble"), numdouble);
QCOMPARE(content(), QString("<config:config-item config:name=\"TestConfigBool\""
" config:type=\"boolean\">true</config:config-item>\n"
" <config:config-item config:name=\"TestConfigInt\" config:type=\"int\">1</config:config-item>\n"
" <config:config-item config:name=\"TestConfigDouble\" config:type=\"double\">5</config:config-item>"));
}
static const int NumParagraphs = 30000;
void TestXmlWriter::speedTest()
{
- QTime time;
+ QElapsedTimer time;
time.start();
QString paragText = QString::fromUtf8("This is the text of the paragraph. I'm including a euro sign to test encoding issues: €");
QString styleName = "Heading 1";
QFile out(QString::fromLatin1("out5.xml"));
if (out.open(QIODevice::WriteOnly)) {
KoXmlWriter writer(&out);
writer.startDocument("rootelem");
writer.startElement("rootelem");
for (int i = 0 ; i < NumParagraphs ; ++i) {
writer.startElement("paragraph");
writer.addAttribute("text:style-name", styleName);
writer.addTextNode(paragText);
writer.endElement();
}
writer.endElement();
writer.endDocument();
}
out.close();
out.remove();
- qDebug("writing %i XML elements using KoXmlWriter: %i ms", NumParagraphs, time.elapsed());
+ qDebug("writing %i XML elements using KoXmlWriter: %i ms", NumParagraphs, (int)time.elapsed());
// TODO we might want to convert this into a QBenchmark test
}
QTEST_GUILESS_MAIN(TestXmlWriter)
#include <TestXmlWriter.moc>
diff --git a/libs/pigment/CMakeLists.txt b/libs/pigment/CMakeLists.txt
index 2a574a6c77..54ad3d1b4c 100644
--- a/libs/pigment/CMakeLists.txt
+++ b/libs/pigment/CMakeLists.txt
@@ -1,126 +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_DIRS})
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
KoCmykColorSpaceMaths.cpp
KoLabColorSpaceMaths.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
+ kritaresources
${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/resources/KoAbstractGradient.h b/libs/pigment/resources/KoAbstractGradient.h
index 435ce2b64d..066ead0a30 100644
--- a/libs/pigment/resources/KoAbstractGradient.h
+++ b/libs/pigment/resources/KoAbstractGradient.h
@@ -1,93 +1,77 @@
/*
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 KOABSTRACTGRADIENT_H
#define KOABSTRACTGRADIENT_H
#include <QGradient>
#include <QMetaType>
#include "KoColorSpace.h"
-#include <resources/KoResource.h>
+#include <KoResource.h>
#include <kritapigment_export.h>
class KoAbstractGradient;
typedef QSharedPointer<KoAbstractGradient> KoAbstractGradientSP;
class KoColor;
/**
* KoAbstractGradient is the base class of all gradient resources
*/
class KRITAPIGMENT_EXPORT KoAbstractGradient : public KoResource
{
public:
explicit KoAbstractGradient(const QString &filename);
~KoAbstractGradient() override;
- virtual KoAbstractGradient* clone() const = 0;
-
- bool load() override {
- return false;
- }
-
- bool loadFromDevice(QIODevice *) override {
- return false;
- }
-
- bool save() override {
- return false;
- }
-
- bool saveToDevice(QIODevice*) const override {
- return false;
- }
-
/**
* Creates a QGradient from the gradient.
* The resulting QGradient might differ from original gradient
*/
virtual QGradient* toQGradient() const {
return new QGradient();
}
/// gets the color at position 0 <= t <= 1
virtual void colorAt(KoColor&, qreal t) const;
void setColorSpace(KoColorSpace* colorSpace);
const KoColorSpace * colorSpace() const;
void setSpread(QGradient::Spread spreadMethod);
QGradient::Spread spread() const;
void setType(QGradient::Type repeatType);
QGradient::Type type() const;
void updatePreview();
QImage generatePreview(int width, int height) const;
KoAbstractGradient(const KoAbstractGradient &rhs);
private:
struct Private;
Private* const d;
};
Q_DECLARE_METATYPE(KoAbstractGradient*)
+Q_DECLARE_METATYPE(QSharedPointer<KoAbstractGradient>)
+
#endif // KOABSTRACTGRADIENT_H
diff --git a/libs/pigment/resources/KoColorSet.cpp b/libs/pigment/resources/KoColorSet.cpp
index da4dffde8d..a704def676 100644
--- a/libs/pigment/resources/KoColorSet.cpp
+++ b/libs/pigment/resources/KoColorSet.cpp
@@ -1,1646 +1,1630 @@
/* This file is part of the KDE project
Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
Copyright (c) 2016 L. E. Segovia <amy@amyspark.me>
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/KoColorSet.h>
#include <sys/types.h>
#include <QFile>
#include <QFileInfo>
#include <QBuffer>
#include <QVector>
#include <QTextStream>
#include <QTextCodec>
#include <QHash>
#include <QList>
#include <QByteArray>
#include <QDomDocument>
#include <QDomElement>
#include <QDomNodeList>
#include <QString>
#include <QStringList>
#include <QImage>
#include <QPainter>
#include <QXmlStreamReader>
#include <QXmlStreamAttributes>
#include <QtEndian> // qFromLittleEndian
#include <DebugPigment.h>
#include <klocalizedstring.h>
#include <KoStore.h>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include <KoColorModelStandardIds.h>
#include "KisSwatch.h"
#include "KoColorSet.h"
#include "KoColorSet_p.h"
namespace {
/**
* readAllLinesSafe() reads all the lines in the byte array
* using the automated UTF8 and CR/LF transformations. That
* might be necessary for opening GPL palettes created on Linux
* in Windows environment.
*/
QStringList readAllLinesSafe(QByteArray *data)
{
QStringList lines;
QBuffer buffer(data);
buffer.open(QBuffer::ReadOnly);
QTextStream stream(&buffer);
QString line;
while (stream.readLineInto(&line)) {
lines << line;
}
return lines;
}
}
const QString KoColorSet::GLOBAL_GROUP_NAME = QString();
const QString KoColorSet::KPL_VERSION_ATTR = "version";
const QString KoColorSet::KPL_GROUP_ROW_COUNT_ATTR = "rows";
const QString KoColorSet::KPL_PALETTE_COLUMN_COUNT_ATTR = "columns";
const QString KoColorSet::KPL_PALETTE_NAME_ATTR = "name";
const QString KoColorSet::KPL_PALETTE_COMMENT_ATTR = "comment";
const QString KoColorSet::KPL_PALETTE_FILENAME_ATTR = "filename";
const QString KoColorSet::KPL_PALETTE_READONLY_ATTR = "readonly";
const QString KoColorSet::KPL_COLOR_MODEL_ID_ATTR = "colorModelId";
const QString KoColorSet::KPL_COLOR_DEPTH_ID_ATTR = "colorDepthId";
const QString KoColorSet::KPL_GROUP_NAME_ATTR = "name";
const QString KoColorSet::KPL_SWATCH_ROW_ATTR = "row";
const QString KoColorSet::KPL_SWATCH_COL_ATTR = "column";
const QString KoColorSet::KPL_SWATCH_NAME_ATTR = "name";
const QString KoColorSet::KPL_SWATCH_ID_ATTR = "id";
const QString KoColorSet::KPL_SWATCH_SPOT_ATTR = "spot";
const QString KoColorSet::KPL_SWATCH_BITDEPTH_ATTR = "bitdepth";
const QString KoColorSet::KPL_PALETTE_PROFILE_TAG = "Profile";
const QString KoColorSet::KPL_SWATCH_POS_TAG = "Position";
const QString KoColorSet::KPL_SWATCH_TAG = "ColorSetEntry";
const QString KoColorSet::KPL_GROUP_TAG = "Group";
const QString KoColorSet::KPL_PALETTE_TAG = "ColorSet";
const int MAXIMUM_ALLOWED_COLUMNS = 4096;
KoColorSet::KoColorSet(const QString& filename)
: KoResource(filename)
, d(new Private(this))
{
if (!filename.isEmpty()) {
QFileInfo f(filename);
setIsEditable(f.isWritable());
}
}
/// Create an copied palette
KoColorSet::KoColorSet(const KoColorSet& rhs)
- : QObject(0)
- , KoResource(rhs)
+ : KoResource(rhs)
, d(new Private(this))
{
d->paletteType = rhs.d->paletteType;
d->data = rhs.d->data;
d->comment = rhs.d->comment;
d->groupNames = rhs.d->groupNames;
d->groups = rhs.d->groups;
- d->isGlobal = rhs.d->isGlobal;
d->isEditable = rhs.d->isEditable;
}
KoColorSet::~KoColorSet()
-{ }
+{
+}
-bool KoColorSet::load()
+KoResourceSP KoColorSet::clone() const
{
- QFile file(filename());
- if (file.size() == 0) return false;
- if (!file.open(QIODevice::ReadOnly)) {
- warnPigment << "Can't open file " << filename();
- return false;
- }
- bool res = loadFromDevice(&file);
- file.close();
- if (!QFileInfo(filename()).isWritable()) {
- setIsEditable(false);
- }
- return res;
+ return KoResourceSP(new KoColorSet(*this));
}
-bool KoColorSet::loadFromDevice(QIODevice *dev)
+bool KoColorSet::load(KisResourcesInterfaceSP resourcesInterface)
{
+ const bool result = KoResource::load(resourcesInterface);
+ setIsEditable(result && QFileInfo(filename()).isWritable());
+ return result;
+}
+
+bool KoColorSet::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
+{
+ Q_UNUSED(resourcesInterface);
+
if (!dev->isOpen()) dev->open(QIODevice::ReadOnly);
d->data = dev->readAll();
Q_ASSERT(d->data.size() != 0);
return d->init();
}
-
-bool KoColorSet::save()
-{
- if (d->isGlobal) {
- // save to resource dir
- QFile file(filename());
- if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- return false;
- }
- saveToDevice(&file);
- file.close();
- return true;
- } else {
- return true; // palette is not global, but still indicate that it's saved
- }
-}
-
bool KoColorSet::saveToDevice(QIODevice *dev) const
{
bool res;
switch(d->paletteType) {
case GPL:
res = d->saveGpl(dev);
break;
default:
res = d->saveKpl(dev);
}
if (res) {
KoResource::saveToDevice(dev);
}
return res;
}
QByteArray KoColorSet::toByteArray() const
{
- QBuffer s;
+ QByteArray ba;
+ QBuffer s(&ba);
s.open(QIODevice::WriteOnly);
if (!saveToDevice(&s)) {
warnPigment << "saving palette failed:" << name();
return QByteArray();
}
s.close();
- s.open(QIODevice::ReadOnly);
- QByteArray res = s.readAll();
- s.close();
- return res;
+ return ba;
}
-bool KoColorSet::fromByteArray(QByteArray &data)
+bool KoColorSet::fromByteArray(QByteArray &data, KisResourcesInterfaceSP resourcesInterface)
{
QBuffer buf(&data);
buf.open(QIODevice::ReadOnly);
- return loadFromDevice(&buf);
+ return loadFromDevice(&buf, resourcesInterface);
}
KoColorSet::PaletteType KoColorSet::paletteType() const
{
return d->paletteType;
}
void KoColorSet::setPaletteType(PaletteType paletteType)
{
d->paletteType = paletteType;
QString suffix;
switch(d->paletteType) {
case GPL:
suffix = ".gpl";
break;
case ACT:
suffix = ".act";
break;
case RIFF_PAL:
case PSP_PAL:
suffix = ".pal";
break;
case ACO:
suffix = ".aco";
break;
case XML:
suffix = ".xml";
break;
case KPL:
suffix = ".kpl";
break;
case SBZ:
suffix = ".sbz";
break;
default:
suffix = defaultFileExtension();
}
QStringList fileName = filename().split(".");
fileName.last() = suffix.replace(".", "");
setFilename(fileName.join("."));
}
quint32 KoColorSet::colorCount() const
{
int colorCount = 0;
for (KisSwatchGroup &g : d->groups.values()) {
colorCount += g.colorCount();
}
return colorCount;
}
void KoColorSet::add(const KisSwatch &c, const QString &groupName)
{
KisSwatchGroup &modifiedGroup = d->groups.contains(groupName)
? d->groups[groupName] : d->global();
modifiedGroup.addEntry(c);
}
void KoColorSet::setEntry(const KisSwatch &e, int x, int y, const QString &groupName)
{
KisSwatchGroup &modifiedGroup = d->groups.contains(groupName)
? d->groups[groupName] : d->global();
modifiedGroup.setEntry(e, x, y);
}
void KoColorSet::clear()
{
d->groups.clear();
d->groupNames.clear();
d->groups[GLOBAL_GROUP_NAME] = KisSwatchGroup();
d->groupNames.append(GLOBAL_GROUP_NAME);
}
KisSwatch KoColorSet::getColorGlobal(quint32 x, quint32 y) const
{
for (const QString &groupName : getGroupNames()) {
if (d->groups.contains(groupName)) {
if ((int)y < d->groups[groupName].rowCount()) {
return d->groups[groupName].getEntry(x, y);
} else {
y -= d->groups[groupName].rowCount();
}
}
}
return KisSwatch();
}
KisSwatch KoColorSet::getColorGroup(quint32 x, quint32 y, QString groupName)
{
KisSwatch e;
const KisSwatchGroup &sourceGroup = groupName == QString()
? d->global() : d->groups[groupName];
if (sourceGroup.checkEntry(x, y)) {
e = sourceGroup.getEntry(x, y);
}
return e;
}
QStringList KoColorSet::getGroupNames() const
{
if (d->groupNames.size() != d->groups.size()) {
warnPigment << "mismatch between groups and the groupnames list.";
return QStringList(d->groups.keys());
}
return d->groupNames;
}
bool KoColorSet::changeGroupName(const QString &oldGroupName, const QString &newGroupName)
{
if (!d->groups.contains(oldGroupName)) {
return false;
}
if (oldGroupName == newGroupName) {
return true;
}
d->groups[newGroupName] = d->groups[oldGroupName];
d->groups.remove(oldGroupName);
d->groups[newGroupName].setName(newGroupName);
//rename the string in the stringlist;
int index = d->groupNames.indexOf(oldGroupName);
d->groupNames.replace(index, newGroupName);
return true;
}
void KoColorSet::setColumnCount(int columns)
{
d->groups[GLOBAL_GROUP_NAME].setColumnCount(columns);
for (KisSwatchGroup &g : d->groups.values()) {
g.setColumnCount(columns);
}
}
int KoColorSet::columnCount() const
{
return d->groups[GLOBAL_GROUP_NAME].columnCount();
}
QString KoColorSet::comment()
{
return d->comment;
}
void KoColorSet::setComment(QString comment)
{
d->comment = comment;
}
bool KoColorSet::addGroup(const QString &groupName)
{
if (d->groups.contains(groupName) || getGroupNames().contains(groupName)) {
return false;
}
d->groupNames.append(groupName);
d->groups[groupName] = KisSwatchGroup();
d->groups[groupName].setName(groupName);
return true;
}
bool KoColorSet::moveGroup(const QString &groupName, const QString &groupNameInsertBefore)
{
if (!d->groupNames.contains(groupName) || d->groupNames.contains(groupNameInsertBefore)==false) {
return false;
}
if (groupNameInsertBefore != GLOBAL_GROUP_NAME && groupName != GLOBAL_GROUP_NAME) {
d->groupNames.removeAt(d->groupNames.indexOf(groupName));
int index = d->groupNames.indexOf(groupNameInsertBefore);
d->groupNames.insert(index, groupName);
}
return true;
}
bool KoColorSet::removeGroup(const QString &groupName, bool keepColors)
{
if (!d->groups.contains(groupName)) {
return false;
}
if (groupName == GLOBAL_GROUP_NAME) {
return false;
}
if (keepColors) {
// put all colors directly below global
int startingRow = d->groups[GLOBAL_GROUP_NAME].rowCount();
for (const KisSwatchGroup::SwatchInfo &info : d->groups[groupName].infoList()) {
d->groups[GLOBAL_GROUP_NAME].setEntry(info.swatch,
info.column,
info.row + startingRow);
}
}
d->groupNames.removeAt(d->groupNames.indexOf(groupName));
d->groups.remove(groupName);
return true;
}
QString KoColorSet::defaultFileExtension() const
{
return QString(".kpl");
}
int KoColorSet::rowCount() const
{
int res = 0;
for (const QString &name : getGroupNames()) {
res += d->groups[name].rowCount();
}
return res;
}
KisSwatchGroup *KoColorSet::getGroup(const QString &name)
{
if (!d->groups.contains(name)) {
return 0;
}
return &(d->groups[name]);
}
KisSwatchGroup *KoColorSet::getGlobalGroup()
{
return getGroup(GLOBAL_GROUP_NAME);
}
-bool KoColorSet::isGlobal() const
-{
- return d->isGlobal;
-}
-
-void KoColorSet::setIsGlobal(bool isGlobal)
-{
- d->isGlobal = isGlobal;
-}
bool KoColorSet::isEditable() const
{
return d->isEditable;
}
void KoColorSet::setIsEditable(bool isEditable)
{
d->isEditable = isEditable;
}
KisSwatchGroup::SwatchInfo KoColorSet::getClosestColorInfo(KoColor compare, bool useGivenColorSpace)
{
KisSwatchGroup::SwatchInfo res;
quint8 highestPercentage = 0;
quint8 testPercentage = 0;
for (const QString &groupName : getGroupNames()) {
KisSwatchGroup *group = getGroup(groupName);
for (const KisSwatchGroup::SwatchInfo &currInfo : group->infoList()) {
KoColor color = currInfo.swatch.color();
if (useGivenColorSpace == true && compare.colorSpace() != color.colorSpace()) {
color.convertTo(compare.colorSpace());
} else if (compare.colorSpace() != color.colorSpace()) {
compare.convertTo(color.colorSpace());
}
testPercentage = (255 - compare.colorSpace()->difference(compare.data(), color.data()));
if (testPercentage > highestPercentage)
{
highestPercentage = testPercentage;
res = currInfo;
}
}
}
return res;
}
/********************************KoColorSet::Private**************************/
KoColorSet::Private::Private(KoColorSet *a_colorSet)
: colorSet(a_colorSet)
{
groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup();
groupNames.append(KoColorSet::GLOBAL_GROUP_NAME);
}
KoColorSet::PaletteType KoColorSet::Private::detectFormat(const QString &fileName, const QByteArray &ba)
{
QFileInfo fi(fileName);
// .pal
if (ba.startsWith("RIFF") && ba.indexOf("PAL data", 8)) {
return KoColorSet::RIFF_PAL;
}
// .gpl
else if (ba.startsWith("GIMP Palette")) {
return KoColorSet::GPL;
}
// .pal
else if (ba.startsWith("JASC-PAL")) {
return KoColorSet::PSP_PAL;
}
else if (fi.suffix().toLower() == "aco") {
return KoColorSet::ACO;
}
else if (fi.suffix().toLower() == "act") {
return KoColorSet::ACT;
}
else if (fi.suffix().toLower() == "xml") {
return KoColorSet::XML;
}
else if (fi.suffix().toLower() == "kpl") {
return KoColorSet::KPL;
}
else if (fi.suffix().toLower() == "sbz") {
return KoColorSet::SBZ;
}
return KoColorSet::UNKNOWN;
}
void KoColorSet::Private::scribusParseColor(KoColorSet *set, QXmlStreamReader *xml)
{
KisSwatch colorEntry;
// It's a color, retrieve it
QXmlStreamAttributes colorProperties = xml->attributes();
QStringRef colorName = colorProperties.value("NAME");
colorEntry.setName(colorName.isEmpty() || colorName.isNull() ? i18n("Untitled") : colorName.toString());
// RGB or CMYK?
if (colorProperties.hasAttribute("RGB")) {
dbgPigment << "Color " << colorProperties.value("NAME") << ", RGB " << colorProperties.value("RGB");
KoColor currentColor(KoColorSpaceRegistry::instance()->rgb8());
QStringRef colorValue = colorProperties.value("RGB");
if (colorValue.length() != 7 && colorValue.at(0) != '#') { // Color is a hexadecimal number
xml->raiseError("Invalid rgb8 color (malformed): " + colorValue);
return;
} else {
bool rgbOk;
quint32 rgb = colorValue.mid(1).toUInt(&rgbOk, 16);
if (!rgbOk) {
xml->raiseError("Invalid rgb8 color (unable to convert): " + colorValue);
return;
}
quint8 r = rgb >> 16 & 0xff;
quint8 g = rgb >> 8 & 0xff;
quint8 b = rgb & 0xff;
dbgPigment << "Color parsed: "<< r << g << b;
currentColor.data()[0] = r;
currentColor.data()[1] = g;
currentColor.data()[2] = b;
currentColor.setOpacity(OPACITY_OPAQUE_U8);
colorEntry.setColor(currentColor);
set->add(colorEntry);
while(xml->readNextStartElement()) {
//ignore - these are all unknown or the /> element tag
xml->skipCurrentElement();
}
return;
}
}
else if (colorProperties.hasAttribute("CMYK")) {
dbgPigment << "Color " << colorProperties.value("NAME") << ", CMYK " << colorProperties.value("CMYK");
KoColor currentColor(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer8BitsColorDepthID.id(), QString()));
QStringRef colorValue = colorProperties.value("CMYK");
if (colorValue.length() != 9 && colorValue.at(0) != '#') { // Color is a hexadecimal number
xml->raiseError("Invalid cmyk color (malformed): " % colorValue);
return;
}
else {
bool cmykOk;
quint32 cmyk = colorValue.mid(1).toUInt(&cmykOk, 16); // cmyk uses the full 32 bits
if (!cmykOk) {
xml->raiseError("Invalid cmyk color (unable to convert): " % colorValue);
return;
}
quint8 c = cmyk >> 24 & 0xff;
quint8 m = cmyk >> 16 & 0xff;
quint8 y = cmyk >> 8 & 0xff;
quint8 k = cmyk & 0xff;
dbgPigment << "Color parsed: "<< c << m << y << k;
currentColor.data()[0] = c;
currentColor.data()[1] = m;
currentColor.data()[2] = y;
currentColor.data()[3] = k;
currentColor.setOpacity(OPACITY_OPAQUE_U8);
colorEntry.setColor(currentColor);
set->add(colorEntry);
while(xml->readNextStartElement()) {
//ignore - these are all unknown or the /> element tag
xml->skipCurrentElement();
}
return;
}
}
else {
xml->raiseError("Unknown color space for color " + colorEntry.name());
}
}
bool KoColorSet::Private::loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml)
{
//1. Get name
QXmlStreamAttributes paletteProperties = xml->attributes();
QStringRef paletteName = paletteProperties.value("Name");
dbgPigment << "Processed name of palette:" << paletteName;
set->setName(paletteName.toString());
//2. Inside the SCRIBUSCOLORS, there are lots of colors. Retrieve them
while(xml->readNextStartElement()) {
QStringRef currentElement = xml->name();
if(QStringRef::compare(currentElement, "COLOR", Qt::CaseInsensitive) == 0) {
scribusParseColor(set, xml);
}
else {
xml->skipCurrentElement();
}
}
if(xml->hasError()) {
return false;
}
return true;
}
quint16 KoColorSet::Private::readShort(QIODevice *io) {
quint16 val;
quint64 read = io->read((char*)&val, 2);
if (read != 2) return false;
return qFromBigEndian(val);
}
bool KoColorSet::Private::init()
{
// just in case this is a reload (eg by KoEditColorSetDialog),
groupNames.clear();
groups.clear();
groupNames.append(KoColorSet::GLOBAL_GROUP_NAME);
groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup();
if (colorSet->filename().isNull()) {
warnPigment << "Cannot load palette" << colorSet->name() << "there is no filename set";
return false;
}
if (data.isNull()) {
QFile file(colorSet->filename());
if (file.size() == 0) {
warnPigment << "Cannot load palette" << colorSet->name() << "there is no data available";
return false;
}
file.open(QIODevice::ReadOnly);
data = file.readAll();
file.close();
}
bool res = false;
paletteType = detectFormat(colorSet->filename(), data);
switch(paletteType) {
case GPL:
res = loadGpl();
break;
case ACT:
res = loadAct();
break;
case RIFF_PAL:
res = loadRiff();
break;
case PSP_PAL:
res = loadPsp();
break;
case ACO:
res = loadAco();
break;
case XML:
res = loadXml();
break;
case KPL:
res = loadKpl();
break;
case SBZ:
res = loadSbz();
break;
default:
res = false;
}
colorSet->setValid(res);
- QImage img(global().columnCount() * 4, global().rowCount() * 4, QImage::Format_ARGB32);
+ int rows = 0;
+ for (QString groupName : groupNames) {
+ int lastRowGroup = 0;
+ for (const KisSwatchGroup::SwatchInfo &info : groups[groupName].infoList()) {
+ lastRowGroup = qMax(lastRowGroup, info.row);
+ }
+ rows += (lastRowGroup + 1);
+ }
+
+ QImage img(global().columnCount() * 4, rows*4, QImage::Format_ARGB32);
QPainter gc(&img);
+ int lastRow = 0;
gc.fillRect(img.rect(), Qt::darkGray);
- for (const KisSwatchGroup::SwatchInfo &info : global().infoList()) {
- QColor c = info.swatch.color().toQColor();
- gc.fillRect(info.column * 4, info.row * 4, 4, 4, c);
+ for (QString groupName : groupNames) {
+ int lastRowGroup = 0;
+ for (const KisSwatchGroup::SwatchInfo &info : groups[groupName].infoList()) {
+ QColor c = info.swatch.color().toQColor();
+ gc.fillRect(info.column * 4, (lastRow + info.row) * 4, 4, 4, c);
+ lastRowGroup = qMax(lastRowGroup, info.row);
+ }
+ lastRow += (lastRowGroup + 1);
}
colorSet->setImage(img);
colorSet->setValid(res);
data.clear();
return res;
}
bool KoColorSet::Private::saveGpl(QIODevice *dev) const
{
Q_ASSERT(dev->isOpen());
Q_ASSERT(dev->isWritable());
QTextStream stream(dev);
stream << "GIMP Palette\nName: " << colorSet->name() << "\nColumns: " << colorSet->columnCount() << "\n#\n";
/*
* Qt doesn't provide an interface to get a const reference to a QHash, that is
* the underlying data structure of groups. Therefore, directly use
* groups[KoColorSet::GLOBAL_GROUP_NAME] so that saveGpl can stay const
*/
for (int y = 0; y < groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount(); y++) {
for (int x = 0; x < colorSet->columnCount(); x++) {
if (!groups[KoColorSet::GLOBAL_GROUP_NAME].checkEntry(x, y)) {
continue;
}
const KisSwatch& entry = groups[KoColorSet::GLOBAL_GROUP_NAME].getEntry(x, y);
QColor c = entry.color().toQColor();
stream << c.red() << " " << c.green() << " " << c.blue() << "\t";
if (entry.name().isEmpty())
stream << "Untitled\n";
else
stream << entry.name() << "\n";
}
}
return true;
}
bool KoColorSet::Private::loadGpl()
{
if (data.isEmpty() || data.isNull() || data.length() < 50) {
warnPigment << "Illegal Gimp palette file: " << colorSet->filename();
return false;
}
quint32 index = 0;
QStringList lines = readAllLinesSafe(&data);
if (lines.size() < 3) {
warnPigment << "Not enough lines in palette file: " << colorSet->filename();
return false;
}
QString columnsText;
qint32 r, g, b;
KisSwatch e;
// Read name
if (!lines[0].startsWith("GIMP") || !lines[1].toLower().contains("name")) {
warnPigment << "Illegal Gimp palette file: " << colorSet->filename();
return false;
}
colorSet->setName(i18n(lines[1].split(":")[1].trimmed().toLatin1()));
index = 2;
// Read columns
int columns = 0;
if (lines[index].toLower().contains("columns")) {
columnsText = lines[index].split(":")[1].trimmed();
columns = columnsText.toInt();
if (columns > MAXIMUM_ALLOWED_COLUMNS) {
warnPigment << "Refusing to set unreasonable number of columns (" << columns << ") in GIMP Palette file " << colorSet->filename() << " - using maximum number of allowed columns instead";
global().setColumnCount(MAXIMUM_ALLOWED_COLUMNS);
}
else {
global().setColumnCount(columns);
}
index = 3;
}
for (qint32 i = index; i < lines.size(); i++) {
if (lines[i].startsWith('#')) {
comment += lines[i].mid(1).trimmed() + ' ';
} else if (!lines[i].isEmpty()) {
QStringList a = lines[i].replace('\t', ' ').split(' ', QString::SkipEmptyParts);
if (a.count() < 3) {
continue;
}
r = qBound(0, a[0].toInt(), 255);
g = qBound(0, a[1].toInt(), 255);
b = qBound(0, a[2].toInt(), 255);
e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8()));
for (int i = 0; i != 3; i++) {
a.pop_front();
}
QString name = a.join(" ");
e.setName(name.isEmpty() || name == "Untitled" ? i18n("Untitled") : name);
global().addEntry(e);
}
}
int rowCount = global().colorCount()/ global().columnCount();
if (global().colorCount() % global().columnCount()>0) {
rowCount ++;
}
global().setRowCount(rowCount);
return true;
}
bool KoColorSet::Private::loadAct()
{
QFileInfo info(colorSet->filename());
colorSet->setName(info.completeBaseName());
KisSwatch e;
for (int i = 0; i < data.size(); i += 3) {
quint8 r = data[i];
quint8 g = data[i+1];
quint8 b = data[i+2];
e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8()));
global().addEntry(e);
}
return true;
}
bool KoColorSet::Private::loadRiff()
{
// https://worms2d.info/Palette_file
QFileInfo info(colorSet->filename());
colorSet->setName(info.completeBaseName());
KisSwatch e;
RiffHeader header;
memcpy(&header, data.constData(), sizeof(RiffHeader));
header.colorcount = qFromBigEndian(header.colorcount);
for (int i = sizeof(RiffHeader);
(i < (int)(sizeof(RiffHeader) + header.colorcount) && i < data.size());
i += 4) {
quint8 r = data[i];
quint8 g = data[i+1];
quint8 b = data[i+2];
e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8()));
groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e);
}
return true;
}
bool KoColorSet::Private::loadPsp()
{
QFileInfo info(colorSet->filename());
colorSet->setName(info.completeBaseName());
KisSwatch e;
qint32 r, g, b;
QStringList l = readAllLinesSafe(&data);
if (l.size() < 4) return false;
if (l[0] != "JASC-PAL") return false;
if (l[1] != "0100") return false;
int entries = l[2].toInt();
for (int i = 0; i < entries; ++i) {
QStringList a = l[i + 3].replace('\t', ' ').split(' ', QString::SkipEmptyParts);
if (a.count() != 3) {
continue;
}
r = qBound(0, a[0].toInt(), 255);
g = qBound(0, a[1].toInt(), 255);
b = qBound(0, a[2].toInt(), 255);
e.setColor(KoColor(QColor(r, g, b),
KoColorSpaceRegistry::instance()->rgb8()));
QString name = a.join(" ");
e.setName(name.isEmpty() ? i18n("Untitled") : name);
groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e);
}
return true;
}
bool KoColorSet::Private::loadKpl()
{
QBuffer buf(&data);
buf.open(QBuffer::ReadOnly);
QScopedPointer<KoStore> store(KoStore::createStore(&buf, KoStore::Read, "krita/x-colorset", KoStore::Zip));
if (!store || store->bad()) { return false; }
if (store->hasFile("profiles.xml")) {
if (!store->open("profiles.xml")) { return false; }
QByteArray data;
data.resize(store->size());
QByteArray ba = store->read(store->size());
store->close();
QDomDocument doc;
doc.setContent(ba);
QDomElement e = doc.documentElement();
QDomElement c = e.firstChildElement(KPL_PALETTE_PROFILE_TAG);
while (!c.isNull()) {
QString name = c.attribute(KPL_PALETTE_NAME_ATTR);
QString filename = c.attribute(KPL_PALETTE_FILENAME_ATTR);
QString colorModelId = c.attribute(KPL_COLOR_MODEL_ID_ATTR);
QString colorDepthId = c.attribute(KPL_COLOR_DEPTH_ID_ATTR);
if (!KoColorSpaceRegistry::instance()->profileByName(name)) {
store->open(filename);
QByteArray data;
data.resize(store->size());
data = store->read(store->size());
store->close();
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(colorModelId, colorDepthId, data);
if (profile && profile->valid()) {
KoColorSpaceRegistry::instance()->addProfile(profile);
}
}
c = c.nextSiblingElement();
}
}
{
if (!store->open("colorset.xml")) { return false; }
QByteArray data;
data.resize(store->size());
QByteArray ba = store->read(store->size());
store->close();
int desiredColumnCount;
QDomDocument doc;
doc.setContent(ba);
QDomElement e = doc.documentElement();
colorSet->setName(e.attribute(KPL_PALETTE_NAME_ATTR));
colorSet->setIsEditable(e.attribute(KPL_PALETTE_READONLY_ATTR) != "true");
comment = e.attribute(KPL_PALETTE_COMMENT_ATTR);
desiredColumnCount = e.attribute(KPL_PALETTE_COLUMN_COUNT_ATTR).toInt();
if (desiredColumnCount > MAXIMUM_ALLOWED_COLUMNS) {
warnPigment << "Refusing to set unreasonable number of columns (" << desiredColumnCount << ") in KPL palette file " << colorSet->filename() << " - setting maximum allowed column count instead.";
colorSet->setColumnCount(MAXIMUM_ALLOWED_COLUMNS);
}
else {
colorSet->setColumnCount(desiredColumnCount);
}
loadKplGroup(doc, e, colorSet->getGlobalGroup());
QDomElement g = e.firstChildElement(KPL_GROUP_TAG);
while (!g.isNull()) {
QString groupName = g.attribute(KPL_GROUP_NAME_ATTR);
colorSet->addGroup(groupName);
loadKplGroup(doc, g, colorSet->getGroup(groupName));
g = g.nextSiblingElement(KPL_GROUP_TAG);
}
}
buf.close();
return true;
}
bool KoColorSet::Private::loadAco()
{
QFileInfo info(colorSet->filename());
colorSet->setName(info.completeBaseName());
QBuffer buf(&data);
buf.open(QBuffer::ReadOnly);
quint16 version = readShort(&buf);
quint16 numColors = readShort(&buf);
KisSwatch e;
if (version == 1 && buf.size() > 4+numColors*10) {
buf.seek(4+numColors*10);
version = readShort(&buf);
numColors = readShort(&buf);
}
const quint16 quint16_MAX = 65535;
for (int i = 0; i < numColors && !buf.atEnd(); ++i) {
quint16 colorSpace = readShort(&buf);
quint16 ch1 = readShort(&buf);
quint16 ch2 = readShort(&buf);
quint16 ch3 = readShort(&buf);
quint16 ch4 = readShort(&buf);
bool skip = false;
if (colorSpace == 0) { // RGB
const KoColorProfile *srgb = KoColorSpaceRegistry::instance()->rgb8()->profile();
KoColor c(KoColorSpaceRegistry::instance()->rgb16(srgb));
reinterpret_cast<quint16*>(c.data())[0] = ch3;
reinterpret_cast<quint16*>(c.data())[1] = ch2;
reinterpret_cast<quint16*>(c.data())[2] = ch1;
c.setOpacity(OPACITY_OPAQUE_U8);
e.setColor(c);
}
else if (colorSpace == 1) { // HSB
QColor qc;
qc.setHsvF(ch1 / 65536.0, ch2 / 65536.0, ch3 / 65536.0);
KoColor c(qc, KoColorSpaceRegistry::instance()->rgb16());
c.setOpacity(OPACITY_OPAQUE_U8);
e.setColor(c);
}
else if (colorSpace == 2) { // CMYK
KoColor c(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer16BitsColorDepthID.id(), QString()));
reinterpret_cast<quint16*>(c.data())[0] = quint16_MAX - ch1;
reinterpret_cast<quint16*>(c.data())[1] = quint16_MAX - ch2;
reinterpret_cast<quint16*>(c.data())[2] = quint16_MAX - ch3;
reinterpret_cast<quint16*>(c.data())[3] = quint16_MAX - ch4;
c.setOpacity(OPACITY_OPAQUE_U8);
e.setColor(c);
}
else if (colorSpace == 7) { // LAB
KoColor c = KoColor(KoColorSpaceRegistry::instance()->lab16());
reinterpret_cast<quint16*>(c.data())[0] = ch3;
reinterpret_cast<quint16*>(c.data())[1] = ch2;
reinterpret_cast<quint16*>(c.data())[2] = ch1;
c.setOpacity(OPACITY_OPAQUE_U8);
e.setColor(c);
}
else if (colorSpace == 8) { // GRAY
KoColor c(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), QString()));
reinterpret_cast<quint16*>(c.data())[0] = ch1 * (quint16_MAX / 10000);
c.setOpacity(OPACITY_OPAQUE_U8);
e.setColor(c);
}
else {
warnPigment << "Unsupported colorspace in palette" << colorSet->filename() << "(" << colorSpace << ")";
skip = true;
}
if (version == 2) {
quint16 v2 = readShort(&buf); //this isn't a version, it's a marker and needs to be skipped.
Q_UNUSED(v2);
quint16 size = readShort(&buf) -1; //then comes the length
if (size>0) {
QByteArray ba = buf.read(size*2);
if (ba.size() == size*2) {
QTextCodec *Utf16Codec = QTextCodec::codecForName("UTF-16BE");
e.setName(Utf16Codec->toUnicode(ba));
} else {
warnPigment << "Version 2 name block is the wrong size" << colorSet->filename();
}
}
v2 = readShort(&buf); //end marker also needs to be skipped.
Q_UNUSED(v2);
}
if (!skip) {
groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e);
}
}
return true;
}
bool KoColorSet::Private::loadSbz() {
QBuffer buf(&data);
buf.open(QBuffer::ReadOnly);
// &buf is a subclass of QIODevice
QScopedPointer<KoStore> store(KoStore::createStore(&buf, KoStore::Read, "application/x-swatchbook", KoStore::Zip));
if (!store || store->bad()) return false;
if (store->hasFile("swatchbook.xml")) { // Try opening...
if (!store->open("swatchbook.xml")) { return false; }
QByteArray data;
data.resize(store->size());
QByteArray ba = store->read(store->size());
store->close();
dbgPigment << "XML palette: " << colorSet->filename() << ", SwatchBooker format";
QDomDocument doc;
int errorLine, errorColumn;
QString errorMessage;
bool status = doc.setContent(ba, &errorMessage, &errorLine, &errorColumn);
if (!status) {
warnPigment << "Illegal XML palette:" << colorSet->filename();
warnPigment << "Error (line" << errorLine << ", column" << errorColumn << "):" << errorMessage;
return false;
}
QDomElement e = doc.documentElement(); // SwatchBook
// Start reading properties...
QDomElement metadata = e.firstChildElement("metadata");
if (e.isNull()) {
warnPigment << "Palette metadata not found";
return false;
}
QDomElement title = metadata.firstChildElement("dc:title");
QString colorName = title.text();
colorName = colorName.isEmpty() ? i18n("Untitled") : colorName;
colorSet->setName(colorName);
dbgPigment << "Processed name of palette:" << colorSet->name();
// End reading properties
// Now read colors...
QDomElement materials = e.firstChildElement("materials");
if (materials.isNull()) {
warnPigment << "Materials (color definitions) not found";
return false;
}
// This one has lots of "color" elements
QDomElement colorElement = materials.firstChildElement("color");
if (colorElement.isNull()) {
warnPigment << "Color definitions not found (line" << materials.lineNumber() << ", column" << materials.columnNumber() << ")";
return false;
}
// Also read the swatch book...
QDomElement book = e.firstChildElement("book");
if (book.isNull()) {
warnPigment << "Palette book (swatch composition) not found (line" << e.lineNumber() << ", column" << e.columnNumber() << ")";
return false;
}
// Which has lots of "swatch"es (todo: support groups)
QDomElement swatch = book.firstChildElement();
if (swatch.isNull()) {
warnPigment << "Swatches/groups definition not found (line" << book.lineNumber() << ", column" << book.columnNumber() << ")";
return false;
}
// We'll store colors here, and as we process swatches
// we'll add them to the palette
QHash<QString, KisSwatch> materialsBook;
QHash<QString, const KoColorSpace*> fileColorSpaces;
// Color processing
for(; !colorElement.isNull(); colorElement = colorElement.nextSiblingElement("color"))
{
KisSwatch currentEntry;
// Set if color is spot
currentEntry.setSpotColor(colorElement.attribute("usage") == "spot");
// <metadata> inside contains id and name
// one or more <values> define the color
QDomElement currentColorMetadata = colorElement.firstChildElement("metadata");
QDomNodeList currentColorValues = colorElement.elementsByTagName("values");
// Get color name
QDomElement colorTitle = currentColorMetadata.firstChildElement("dc:title");
QDomElement colorId = currentColorMetadata.firstChildElement("dc:identifier");
// Is there an id? (we need that at the very least for identifying a color)
if (colorId.text().isEmpty()) {
warnPigment << "Unidentified color (line" << colorId.lineNumber()<< ", column" << colorId.columnNumber() << ")";
return false;
}
if (materialsBook.contains(colorId.text())) {
warnPigment << "Duplicated color definition (line" << colorId.lineNumber()<< ", column" << colorId.columnNumber() << ")";
return false;
}
// Get a valid color name
currentEntry.setId(colorId.text());
currentEntry.setName(colorTitle.text().isEmpty() ? colorId.text() : colorTitle.text());
// Get a valid color definition
if (currentColorValues.isEmpty()) {
warnPigment << "Color definitions not found (line" << colorElement.lineNumber() << ", column" << colorElement.columnNumber() << ")";
return false;
}
bool firstDefinition = false;
const KoColorProfile *srgb = KoColorSpaceRegistry::instance()->rgb8()->profile();
// Priority: Lab, otherwise the first definition found
for(int j = 0; j < currentColorValues.size(); j++) {
QDomNode colorValue = currentColorValues.at(j);
QDomElement colorValueE = colorValue.toElement();
QString model = colorValueE.attribute("model", QString());
// sRGB,RGB,HSV,HSL,CMY,CMYK,nCLR: 0 -> 1
// YIQ: Y 0 -> 1 : IQ -0.5 -> 0.5
// Lab: L 0 -> 100 : ab -128 -> 127
// XYZ: 0 -> ~100
if (model == "Lab") {
QStringList lab = colorValueE.text().split(" ");
if (lab.length() != 3) {
warnPigment << "Invalid Lab color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")";
}
float l = lab.at(0).toFloat(&status);
float a = lab.at(1).toFloat(&status);
float b = lab.at(2).toFloat(&status);
if (!status) {
warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")";
}
KoColor c(KoColorSpaceRegistry::instance()->colorSpace(LABAColorModelID.id(), Float32BitsColorDepthID.id(), QString()));
reinterpret_cast<float*>(c.data())[0] = l;
reinterpret_cast<float*>(c.data())[1] = a;
reinterpret_cast<float*>(c.data())[2] = b;
c.setOpacity(OPACITY_OPAQUE_F);
firstDefinition = true;
currentEntry.setColor(c);
break; // Immediately add this one
}
else if (model == "sRGB" && !firstDefinition) {
QStringList rgb = colorValueE.text().split(" ");
if (rgb.length() != 3) {
warnPigment << "Invalid sRGB color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")";
}
float r = rgb.at(0).toFloat(&status);
float g = rgb.at(1).toFloat(&status);
float b = rgb.at(2).toFloat(&status);
if (!status) {
warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")";
}
KoColor c(KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), srgb));
reinterpret_cast<float*>(c.data())[0] = r;
reinterpret_cast<float*>(c.data())[1] = g;
reinterpret_cast<float*>(c.data())[2] = b;
c.setOpacity(OPACITY_OPAQUE_F);
currentEntry.setColor(c);
firstDefinition = true;
}
else if (model == "XYZ" && !firstDefinition) {
QStringList xyz = colorValueE.text().split(" ");
if (xyz.length() != 3) {
warnPigment << "Invalid XYZ color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")";
}
float x = xyz.at(0).toFloat(&status);
float y = xyz.at(1).toFloat(&status);
float z = xyz.at(2).toFloat(&status);
if (!status) {
warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")";
}
KoColor c(KoColorSpaceRegistry::instance()->colorSpace(XYZAColorModelID.id(), Float32BitsColorDepthID.id(), QString()));
reinterpret_cast<float*>(c.data())[0] = x;
reinterpret_cast<float*>(c.data())[1] = y;
reinterpret_cast<float*>(c.data())[2] = z;
c.setOpacity(OPACITY_OPAQUE_F);
currentEntry.setColor(c);
firstDefinition = true;
}
// The following color spaces admit an ICC profile (in SwatchBooker)
else if (model == "CMYK" && !firstDefinition) {
QStringList cmyk = colorValueE.text().split(" ");
if (cmyk.length() != 4) {
warnPigment << "Invalid CMYK color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")";
}
float c = cmyk.at(0).toFloat(&status);
float m = cmyk.at(1).toFloat(&status);
float y = cmyk.at(2).toFloat(&status);
float k = cmyk.at(3).toFloat(&status);
if (!status) {
warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")";
}
QString space = colorValueE.attribute("space");
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), QString());
if (!space.isEmpty()) {
// Try loading the profile and add it to the registry
if (!fileColorSpaces.contains(space)) {
store->enterDirectory("profiles");
store->open(space);
QByteArray data;
data.resize(store->size());
data = store->read(store->size());
store->close();
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), data);
if (profile && profile->valid()) {
KoColorSpaceRegistry::instance()->addProfile(profile);
colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), profile);
fileColorSpaces.insert(space, colorSpace);
}
}
else {
colorSpace = fileColorSpaces.value(space);
}
}
KoColor color(colorSpace);
reinterpret_cast<float*>(color.data())[0] = c;
reinterpret_cast<float*>(color.data())[1] = m;
reinterpret_cast<float*>(color.data())[2] = y;
reinterpret_cast<float*>(color.data())[3] = k;
color.setOpacity(OPACITY_OPAQUE_F);
currentEntry.setColor(color);
firstDefinition = true;
}
else if (model == "GRAY" && !firstDefinition) {
QString gray = colorValueE.text();
float g = gray.toFloat(&status);
if (!status) {
warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")";
}
QString space = colorValueE.attribute("space");
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Float32BitsColorDepthID.id(), QString());
if (!space.isEmpty()) {
// Try loading the profile and add it to the registry
if (!fileColorSpaces.contains(space)) {
store->enterDirectory("profiles");
store->open(space);
QByteArray data;
data.resize(store->size());
data = store->read(store->size());
store->close();
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), data);
if (profile && profile->valid()) {
KoColorSpaceRegistry::instance()->addProfile(profile);
colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), profile);
fileColorSpaces.insert(space, colorSpace);
}
}
else {
colorSpace = fileColorSpaces.value(space);
}
}
KoColor c(colorSpace);
reinterpret_cast<float*>(c.data())[0] = g;
c.setOpacity(OPACITY_OPAQUE_F);
currentEntry.setColor(c);
firstDefinition = true;
}
else if (model == "RGB" && !firstDefinition) {
QStringList rgb = colorValueE.text().split(" ");
if (rgb.length() != 3) {
warnPigment << "Invalid RGB color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")";
}
float r = rgb.at(0).toFloat(&status);
float g = rgb.at(1).toFloat(&status);
float b = rgb.at(2).toFloat(&status);
if (!status) {
warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")";
}
QString space = colorValueE.attribute("space");
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), srgb);
if (!space.isEmpty()) {
// Try loading the profile and add it to the registry
if (!fileColorSpaces.contains(space)) {
store->enterDirectory("profiles");
store->open(space);
QByteArray data;
data.resize(store->size());
data = store->read(store->size());
store->close();
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), data);
if (profile && profile->valid()) {
KoColorSpaceRegistry::instance()->addProfile(profile);
colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), profile);
fileColorSpaces.insert(space, colorSpace);
}
}
else {
colorSpace = fileColorSpaces.value(space);
}
}
KoColor c(colorSpace);
reinterpret_cast<float*>(c.data())[0] = r;
reinterpret_cast<float*>(c.data())[1] = g;
reinterpret_cast<float*>(c.data())[2] = b;
c.setOpacity(OPACITY_OPAQUE_F);
currentEntry.setColor(c);
firstDefinition = true;
}
else {
warnPigment << "Color space not implemented:" << model << "(line" << colorValueE.lineNumber() << ", column "<< colorValueE.columnNumber() << ")";
}
}
if (firstDefinition) {
materialsBook.insert(currentEntry.id(), currentEntry);
}
else {
warnPigment << "No supported color spaces for the current color (line" << colorElement.lineNumber() << ", column "<< colorElement.columnNumber() << ")";
return false;
}
}
// End colors
// Now decide which ones will go into the palette
for(;!swatch.isNull(); swatch = swatch.nextSiblingElement()) {
QString type = swatch.tagName();
if (type.isEmpty() || type.isNull()) {
warnPigment << "Invalid swatch/group definition (no id) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")";
return false;
}
else if (type == "swatch") {
QString id = swatch.attribute("material");
if (id.isEmpty() || id.isNull()) {
warnPigment << "Invalid swatch definition (no material id) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")";
return false;
}
if (materialsBook.contains(id)) {
groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(materialsBook.value(id));
}
else {
warnPigment << "Invalid swatch definition (material not found) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")";
return false;
}
}
else if (type == "group") {
QDomElement groupMetadata = swatch.firstChildElement("metadata");
if (groupMetadata.isNull()) {
warnPigment << "Invalid group definition (missing metadata) (line" << groupMetadata.lineNumber() << ", column" << groupMetadata.columnNumber() << ")";
return false;
}
QDomElement groupTitle = metadata.firstChildElement("dc:title");
if (groupTitle.isNull()) {
warnPigment << "Invalid group definition (missing title) (line" << groupTitle.lineNumber() << ", column" << groupTitle.columnNumber() << ")";
return false;
}
QString currentGroupName = groupTitle.text();
QDomElement groupSwatch = swatch.firstChildElement("swatch");
while(!groupSwatch.isNull()) {
QString id = groupSwatch.attribute("material");
if (id.isEmpty() || id.isNull()) {
warnPigment << "Invalid swatch definition (no material id) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")";
return false;
}
if (materialsBook.contains(id)) {
groups[currentGroupName].addEntry(materialsBook.value(id));
}
else {
warnPigment << "Invalid swatch definition (material not found) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")";
return false;
}
groupSwatch = groupSwatch.nextSiblingElement("swatch");
}
}
}
// End palette
}
buf.close();
return true;
}
bool KoColorSet::Private::loadXml() {
bool res = false;
QXmlStreamReader *xml = new QXmlStreamReader(data);
if (xml->readNextStartElement()) {
QStringRef paletteId = xml->name();
if (QStringRef::compare(paletteId, "SCRIBUSCOLORS", Qt::CaseInsensitive) == 0) { // Scribus
dbgPigment << "XML palette: " << colorSet->filename() << ", Scribus format";
res = loadScribusXmlPalette(colorSet, xml);
}
else {
// Unknown XML format
xml->raiseError("Unknown XML palette format. Expected SCRIBUSCOLORS, found " + paletteId);
}
}
// If there is any error (it should be returned through the stream)
if (xml->hasError() || !res) {
warnPigment << "Illegal XML palette:" << colorSet->filename();
warnPigment << "Error (line"<< xml->lineNumber() << ", column" << xml->columnNumber() << "):" << xml->errorString();
return false;
}
else {
dbgPigment << "XML palette parsed successfully:" << colorSet->filename();
return true;
}
}
bool KoColorSet::Private::saveKpl(QIODevice *dev) const
{
QScopedPointer<KoStore> store(KoStore::createStore(dev, KoStore::Write, "krita/x-colorset", KoStore::Zip));
if (!store || store->bad()) return false;
QSet<const KoColorSpace *> colorSpaces;
{
QDomDocument doc;
QDomElement root = doc.createElement(KPL_PALETTE_TAG);
root.setAttribute(KPL_VERSION_ATTR, "1.0");
root.setAttribute(KPL_PALETTE_NAME_ATTR, colorSet->name());
root.setAttribute(KPL_PALETTE_COMMENT_ATTR, comment);
root.setAttribute(KPL_PALETTE_READONLY_ATTR,
- (colorSet->isEditable() || !colorSet->isGlobal()) ? "false" : "true");
+ (colorSet->isEditable()) ? "false" : "true");
root.setAttribute(KPL_PALETTE_COLUMN_COUNT_ATTR, colorSet->columnCount());
root.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount());
saveKplGroup(doc, root, colorSet->getGroup(KoColorSet::GLOBAL_GROUP_NAME), colorSpaces);
for (const QString &groupName : groupNames) {
if (groupName == KoColorSet::GLOBAL_GROUP_NAME) { continue; }
QDomElement gl = doc.createElement(KPL_GROUP_TAG);
gl.setAttribute(KPL_GROUP_NAME_ATTR, groupName);
root.appendChild(gl);
saveKplGroup(doc, gl, colorSet->getGroup(groupName), colorSpaces);
}
doc.appendChild(root);
if (!store->open("colorset.xml")) { return false; }
QByteArray ba = doc.toByteArray();
if (store->write(ba) != ba.size()) { return false; }
if (!store->close()) { return false; }
}
QDomDocument doc;
QDomElement profileElement = doc.createElement("Profiles");
for (const KoColorSpace *colorSpace : colorSpaces) {
QString fn = QFileInfo(colorSpace->profile()->fileName()).fileName();
if (!store->open(fn)) { return false; }
QByteArray profileRawData = colorSpace->profile()->rawData();
if (!store->write(profileRawData)) { return false; }
if (!store->close()) { return false; }
QDomElement el = doc.createElement(KPL_PALETTE_PROFILE_TAG);
el.setAttribute(KPL_PALETTE_FILENAME_ATTR, fn);
el.setAttribute(KPL_PALETTE_NAME_ATTR, colorSpace->profile()->name());
el.setAttribute(KPL_COLOR_MODEL_ID_ATTR, colorSpace->colorModelId().id());
el.setAttribute(KPL_COLOR_DEPTH_ID_ATTR, colorSpace->colorDepthId().id());
profileElement.appendChild(el);
}
doc.appendChild(profileElement);
if (!store->open("profiles.xml")) { return false; }
QByteArray ba = doc.toByteArray();
if (store->write(ba) != ba.size()) { return false; }
if (!store->close()) { return false; }
return store->finalize();
}
void KoColorSet::Private::saveKplGroup(QDomDocument &doc,
QDomElement &groupEle,
const KisSwatchGroup *group,
QSet<const KoColorSpace *> &colorSetSet) const
{
groupEle.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, QString::number(group->rowCount()));
for (const SwatchInfoType &info : group->infoList()) {
const KoColorProfile *profile = info.swatch.color().colorSpace()->profile();
// Only save non-builtin profiles.=
if (!profile->fileName().isEmpty()) {
colorSetSet.insert(info.swatch.color().colorSpace());
}
QDomElement swatchEle = doc.createElement(KPL_SWATCH_TAG);
swatchEle.setAttribute(KPL_SWATCH_NAME_ATTR, info.swatch.name());
swatchEle.setAttribute(KPL_SWATCH_ID_ATTR, info.swatch.id());
swatchEle.setAttribute(KPL_SWATCH_SPOT_ATTR, info.swatch.spotColor() ? "true" : "false");
swatchEle.setAttribute(KPL_SWATCH_BITDEPTH_ATTR, info.swatch.color().colorSpace()->colorDepthId().id());
info.swatch.color().toXML(doc, swatchEle);
QDomElement positionEle = doc.createElement(KPL_SWATCH_POS_TAG);
positionEle.setAttribute(KPL_SWATCH_ROW_ATTR, info.row);
positionEle.setAttribute(KPL_SWATCH_COL_ATTR, info.column);
swatchEle.appendChild(positionEle);
groupEle.appendChild(swatchEle);
}
}
void KoColorSet::Private::loadKplGroup(const QDomDocument &doc, const QDomElement &parentEle, KisSwatchGroup *group)
{
Q_UNUSED(doc);
if (!parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).isNull()) {
group->setRowCount(parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).toInt());
}
group->setColumnCount(colorSet->columnCount());
for (QDomElement swatchEle = parentEle.firstChildElement(KPL_SWATCH_TAG);
!swatchEle.isNull();
swatchEle = swatchEle.nextSiblingElement(KPL_SWATCH_TAG)) {
QString colorDepthId = swatchEle.attribute(KPL_SWATCH_BITDEPTH_ATTR, Integer8BitsColorDepthID.id());
KisSwatch entry;
entry.setColor(KoColor::fromXML(swatchEle.firstChildElement(), colorDepthId));
entry.setName(swatchEle.attribute(KPL_SWATCH_NAME_ATTR));
entry.setId(swatchEle.attribute(KPL_SWATCH_ID_ATTR));
entry.setSpotColor(swatchEle.attribute(KPL_SWATCH_SPOT_ATTR, "false") == "true" ? true : false);
QDomElement positionEle = swatchEle.firstChildElement(KPL_SWATCH_POS_TAG);
if (!positionEle.isNull()) {
int rowNumber = positionEle.attribute(KPL_SWATCH_ROW_ATTR).toInt();
int columnNumber = positionEle.attribute(KPL_SWATCH_COL_ATTR).toInt();
if (columnNumber < 0 ||
columnNumber >= colorSet->columnCount() ||
rowNumber < 0
) {
warnPigment << "Swatch" << entry.name()
<< "of palette" << colorSet->name()
<< "has invalid position.";
continue;
}
group->setEntry(entry, columnNumber, rowNumber);
} else {
group->addEntry(entry);
}
}
if (parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).isNull()
&& group->colorCount() > 0
&& group->columnCount() > 0
&& (group->colorCount() / (group->columnCount()) + 1) < 20) {
group->setRowCount((group->colorCount() / group->columnCount()) + 1);
}
}
diff --git a/libs/pigment/resources/KoColorSet.h b/libs/pigment/resources/KoColorSet.h
index 9364040fe5..5a47d6ef11 100644
--- a/libs/pigment/resources/KoColorSet.h
+++ b/libs/pigment/resources/KoColorSet.h
@@ -1,219 +1,213 @@
/* This file is part of the KDE project
Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
Copyright (c) 2016 L. E. Segovia <amy@amyspark.me>
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 <QSharedPointer>
-#include <resources/KoResource.h>
+#include <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
+class KRITAPIGMENT_EXPORT KoColorSet :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;
+ KoColorSet &operator=(const KoColorSet &rhs) = delete;
+
+ KoResourceSP clone() const override;
+
+ bool load(KisResourcesInterfaceSP resourcesInterface) override;
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
bool saveToDevice(QIODevice* dev) const override;
QString defaultFileExtension() const override;
+ QPair<QString, QString> resourceType() const override
+ {
+ return QPair<QString, QString>(ResourceType::Palettes, "");
+ }
-
-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 @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);
+ bool fromByteArray(QByteArray &data, KisResourcesInterfaceSP resourcesInterface);
/**
* @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 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 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() const;
/**
* @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 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;
};
+
+typedef QSharedPointer<KoColorSet> KoColorSetSP;
+
#endif // KOCOLORSET
diff --git a/libs/pigment/resources/KoColorSet_p.h b/libs/pigment/resources/KoColorSet_p.h
index 625cc38b17..a9dcf0015c 100644
--- a/libs/pigment/resources/KoColorSet_p.h
+++ b/libs/pigment/resources/KoColorSet_p.h
@@ -1,74 +1,73 @@
#ifndef KOCOLORSET_P_H
#define KOCOLORSET_P_H
#include <QHash>
#include <QXmlStreamReader>
#include <QDomElement>
#include <QPointer>
#include <KisSwatch.h>
#include <KisSwatchGroup.h>
#include "KoColorSet.h"
struct RiffHeader {
quint32 riff;
quint32 size;
quint32 signature;
quint32 data;
quint32 datasize;
quint16 version;
quint16 colorcount;
};
class KoColorSet::Private
{
private:
typedef KisSwatchGroup::SwatchInfo SwatchInfoType;
public:
Private(KoColorSet *a_colorSet);
public:
KisSwatchGroup &global() {
Q_ASSERT(groups.contains(GLOBAL_GROUP_NAME));
return groups[GLOBAL_GROUP_NAME];
}
public:
bool init();
bool saveGpl(QIODevice *dev) const;
bool loadGpl();
bool loadAct();
bool loadRiff();
bool loadPsp();
bool loadAco();
bool loadXml();
bool loadSbz();
bool saveKpl(QIODevice *dev) const;
bool loadKpl();
public:
KoColorSet *colorSet {0};
KoColorSet::PaletteType paletteType;
QByteArray data;
QString comment;
QStringList groupNames; //names of the groups, this is used to determine the order they are in.
QHash<QString, KisSwatchGroup> groups; //grouped colors.
- bool isGlobal {true};
bool isEditable {false};
private:
KoColorSet::PaletteType detectFormat(const QString &fileName, const QByteArray &ba);
void scribusParseColor(KoColorSet *set, QXmlStreamReader *xml);
bool loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml);
quint16 readShort(QIODevice *io);
void saveKplGroup(QDomDocument &doc, QDomElement &groupEle,
const KisSwatchGroup *group, QSet<const KoColorSpace *> &colorSetSet) const;
void loadKplGroup(const QDomDocument &doc, const QDomElement &parentElement, KisSwatchGroup *group);
};
#endif // KOCOLORSET_P_H
diff --git a/libs/pigment/resources/KoHashGenerator.h b/libs/pigment/resources/KoHashGenerator.h
deleted file mode 100644
index 383ff9bc15..0000000000
--- a/libs/pigment/resources/KoHashGenerator.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2015 Stefano Bonicatti <smjert@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 KOHASHGENERATOR_H
-#define KOHASHGENERATOR_H
-
-#include <QByteArray>
-#include <QString>
-
-class KoHashGenerator
-{
-public:
- virtual QByteArray generateHash(const QString &filename) = 0;
- virtual QByteArray generateHash(const QByteArray &array) = 0;
- virtual ~KoHashGenerator(){}
-};
-#endif
diff --git a/libs/pigment/resources/KoHashGeneratorProvider.cpp b/libs/pigment/resources/KoHashGeneratorProvider.cpp
deleted file mode 100644
index 8dbfcad073..0000000000
--- a/libs/pigment/resources/KoHashGeneratorProvider.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2015 Stefano Bonicatti <smjert@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 "KoHashGeneratorProvider.h"
-
-#include <QMutexLocker>
-#include <QGlobalStatic>
-
-#include "KoMD5Generator.h"
-
-KoHashGeneratorProvider *KoHashGeneratorProvider::instance_var = 0;
-Q_GLOBAL_STATIC(KoHashGeneratorProvider, s_instance)
-
-KoHashGeneratorProvider::KoHashGeneratorProvider()
-{
- // Initialize default generators
- hashGenerators.insert("MD5", new KoMD5Generator());
-}
-
-KoHashGeneratorProvider::~KoHashGeneratorProvider()
-{
- qDeleteAll(hashGenerators);
-}
-
-KoHashGenerator *KoHashGeneratorProvider::getGenerator(const QString &algorithm)
-{
- QMutexLocker locker(&mutex);
- return hashGenerators.value(algorithm);
-}
-
-void KoHashGeneratorProvider::setGenerator(const QString &algorithm, KoHashGenerator *generator)
-{
- if (hashGenerators.contains(algorithm)) {
- delete hashGenerators.take(algorithm);
- hashGenerators[algorithm] = generator;
- }
- else
- hashGenerators.insert(algorithm, generator);
-}
-
-KoHashGeneratorProvider *KoHashGeneratorProvider::instance()
-{
- return s_instance;
-}
diff --git a/libs/pigment/resources/KoHashGeneratorProvider.h b/libs/pigment/resources/KoHashGeneratorProvider.h
deleted file mode 100644
index 5be1781341..0000000000
--- a/libs/pigment/resources/KoHashGeneratorProvider.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2015 Stefano Bonicatti <smjert@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 KOHASHGENERATORPROVIDER_H
-#define KOHASHGENERATORPROVIDER_H
-
-#include <QHash>
-#include <QMutex>
-
-#include <kritapigment_export.h>
-
-class KoHashGenerator;
-
-class KRITAPIGMENT_EXPORT KoHashGeneratorProvider
-{
-public:
- KoHashGeneratorProvider();
- ~KoHashGeneratorProvider();
-
- KoHashGenerator *getGenerator(const QString &algorithm);
- void setGenerator(const QString &algorithm, KoHashGenerator *generator);
- static KoHashGeneratorProvider *instance();
-private:
- static KoHashGeneratorProvider *instance_var;
- QHash<QString, KoHashGenerator *> hashGenerators;
- QMutex mutex;
-};
-
-#endif
diff --git a/libs/pigment/resources/KoMD5Generator.cpp b/libs/pigment/resources/KoMD5Generator.cpp
deleted file mode 100644
index 3c65fadde9..0000000000
--- a/libs/pigment/resources/KoMD5Generator.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2015 Stefano Bonicatti <smjert@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 "KoMD5Generator.h"
-
-#include <QIODevice>
-#include <QFile>
-#include <QCryptographicHash>
-
-KoMD5Generator::KoMD5Generator()
-{
-
-}
-
-KoMD5Generator::~KoMD5Generator()
-{
-
-}
-
-QByteArray KoMD5Generator::generateHash(const QByteArray &array)
-{
- if (!array.isEmpty()) {
- QCryptographicHash md5(QCryptographicHash::Md5);
- md5.addData(array);
- return md5.result();
- }
-
- return array;
-}
-
-QByteArray KoMD5Generator::generateHash(const QString &filename)
-{
- QByteArray result;
-
- QFile f(filename);
- if (f.exists() && f.open(QIODevice::ReadOnly)) {
- QByteArray ba = f.readAll();
- result = generateHash(ba);
- }
-
- return result;
-}
diff --git a/libs/pigment/resources/KoMD5Generator.h b/libs/pigment/resources/KoMD5Generator.h
deleted file mode 100644
index 26304d5314..0000000000
--- a/libs/pigment/resources/KoMD5Generator.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2015 Stefano Bonicatti <smjert@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 KOMD5GENERATOR_H
-#define KOMD5GENERATOR_H
-
-#include "KoHashGenerator.h"
-
-#include <kritapigment_export.h>
-
-class KRITAPIGMENT_EXPORT KoMD5Generator : public KoHashGenerator
-{
-public:
- KoMD5Generator();
- ~KoMD5Generator() override;
- QByteArray generateHash(const QString &filename) override;
- QByteArray generateHash(const QByteArray &array) override;
-};
-
-#endif
diff --git a/libs/pigment/resources/KoPattern.cpp b/libs/pigment/resources/KoPattern.cpp
index e87cd51591..86703a2693 100644
--- a/libs/pigment/resources/KoPattern.cpp
+++ b/libs/pigment/resources/KoPattern.cpp
@@ -1,399 +1,375 @@
/* This file is part of the KDE project
Copyright (c) 2000 Matthias Elter <elter@kde.org>
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 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/KoPattern.h>
#include <sys/types.h>
#include <QtEndian>
#include <limits.h>
#include <stdlib.h>
#include <QFileInfo>
#include <QDir>
#include <QPoint>
#include <QSize>
#include <QImage>
#include <QMap>
#include <QFile>
#include <QBuffer>
#include <QTextStream>
#include <DebugPigment.h>
#include <klocalizedstring.h>
namespace
{
struct GimpPatternHeader {
quint32 header_size; /* header_size = sizeof (PatternHeader) + brush name */
quint32 version; /* pattern file version # */
quint32 width; /* width of pattern */
quint32 height; /* height of pattern */
quint32 bytes; /* depth of pattern in bytes : 1, 2, 3 or 4*/
quint32 magic_number; /* GIMP brush magic number */
};
// Yes! This is _NOT_ what my pat.txt file says. It's really not 'GIMP', but 'GPAT'
quint32 const GimpPatternMagic = (('G' << 24) + ('P' << 16) + ('A' << 8) + ('T' << 0));
}
KoPattern::KoPattern(const QString& file)
: KoResource(file)
{
}
KoPattern::KoPattern(const QImage &image, const QString &name, const QString &folderName)
: KoResource(QString())
{
setPatternImage(image);
setName(name);
QFileInfo fileInfo(folderName + QDir::separator() + name + defaultFileExtension());
int i = 1;
while (fileInfo.exists()) {
fileInfo.setFile(folderName + QDir::separator() +
name + QString::number(i) + defaultFileExtension());
i++;
}
setFilename(fileInfo.filePath());
}
KoPattern::~KoPattern()
{
}
-bool KoPattern::load()
+KoPattern::KoPattern(const KoPattern &rhs)
+ : KoResource(rhs),
+ m_pattern(rhs.m_pattern),
+ m_md5(rhs.m_md5)
{
- QFile file(filename());
- if (file.size() == 0) return false;
-
- bool result;
- if (!file.open(QIODevice::ReadOnly)) {
- qWarning() << "Can't open file " << filename();
- return false;
- }
- result = loadFromDevice(&file);
- file.close();
+}
- return result;
+KoResourceSP KoPattern::clone() const
+{
+ return KoResourceSP(new KoPattern(*this));
}
bool KoPattern::loadPatFromDevice(QIODevice *dev)
{
QByteArray data = dev->readAll();
return init(data);
}
bool KoPattern::savePatToDevice(QIODevice* dev) const
{
// Header: header_size (24+name length),version,width,height,colordepth of brush,magic,name
// depth: 1 = greyscale, 2 = greyscale + A, 3 = RGB, 4 = RGBA
// magic = "GPAT", as a single uint32, the docs are wrong here!
// name is UTF-8 (\0-terminated! The docs say nothing about this!)
// _All_ data in network order, it seems! (not mentioned in gimp-2.2.8/devel-docs/pat.txt!!)
// We only save RGBA at the moment
// Version is 1 for now...
GimpPatternHeader ph;
QByteArray utf8Name = name().toUtf8();
char const* name = utf8Name.data();
int nameLength = qstrlen(name);
ph.header_size = qToBigEndian((quint32)sizeof(GimpPatternHeader) + nameLength + 1); // trailing 0
ph.version = qToBigEndian((quint32)1);
ph.width = qToBigEndian((quint32)width());
ph.height = qToBigEndian((quint32)height());
ph.bytes = qToBigEndian((quint32)4);
ph.magic_number = qToBigEndian((quint32)GimpPatternMagic);
QByteArray bytes = QByteArray::fromRawData(reinterpret_cast<char*>(&ph), sizeof(GimpPatternHeader));
int wrote = dev->write(bytes);
bytes.clear();
if (wrote == -1)
return false;
wrote = dev->write(name, nameLength + 1); // Trailing 0 apparently!
if (wrote == -1)
return false;
int k = 0;
bytes.resize(width() * height() * 4);
for (qint32 y = 0; y < height(); ++y) {
for (qint32 x = 0; x < width(); ++x) {
// RGBA only
QRgb pixel = m_pattern.pixel(x, y);
bytes[k++] = static_cast<char>(qRed(pixel));
bytes[k++] = static_cast<char>(qGreen(pixel));
bytes[k++] = static_cast<char>(qBlue(pixel));
bytes[k++] = static_cast<char>(qAlpha(pixel));
}
}
wrote = dev->write(bytes);
if (wrote == -1)
return false;
KoResource::saveToDevice(dev);
return true;
}
-bool KoPattern::loadFromDevice(QIODevice *dev)
+bool KoPattern::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
{
+ Q_UNUSED(resourcesInterface);
+
QString fileExtension;
int index = filename().lastIndexOf('.');
if (index != -1)
fileExtension = filename().mid(index + 1).toLower();
bool result;
if (fileExtension == "pat") {
result = loadPatFromDevice(dev);
}
else {
QImage image;
// Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice
// fails with "libpng error: IDAT: CRC error"
QByteArray data = dev->readAll();
QBuffer buffer(&data);
result = image.load(&buffer, fileExtension.toUpper().toLatin1());
setPatternImage(image);
}
return result;
}
-bool KoPattern::save()
-{
- QFile file(filename());
- file.open(QIODevice::WriteOnly | QIODevice::Truncate);
- bool res = saveToDevice(&file);
- file.close();
- return res;
-}
-
bool KoPattern::saveToDevice(QIODevice *dev) const
{
QString fileExtension;
int index = filename().lastIndexOf('.');
if (index != -1)
fileExtension = filename().mid(index + 1).toLower();
+ bool result = false;
+
if (fileExtension == "pat") {
- return savePatToDevice(dev);
+ result = savePatToDevice(dev);
}
else {
- return m_pattern.save(dev, fileExtension.toUpper().toLatin1());
+ result = m_pattern.save(dev, fileExtension.toUpper().toLatin1());
}
- return true;
-
+ return result && KoResource::saveToDevice(dev);
}
bool KoPattern::init(QByteArray& bytes)
{
int dataSize = bytes.size();
const char* data = bytes.constData();
// load Gimp patterns
GimpPatternHeader bh;
qint32 k;
char* name;
if ((int)sizeof(GimpPatternHeader) > dataSize) {
return false;
}
memcpy(&bh, data, sizeof(GimpPatternHeader));
bh.header_size = qFromBigEndian(bh.header_size);
bh.version = qFromBigEndian(bh.version);
bh.width = qFromBigEndian(bh.width);
bh.height = qFromBigEndian(bh.height);
bh.bytes = qFromBigEndian(bh.bytes);
bh.magic_number = qFromBigEndian(bh.magic_number);
if ((int)bh.header_size > dataSize || bh.header_size == 0) {
return false;
}
int size = bh.header_size - sizeof(GimpPatternHeader);
name = new char[size];
memcpy(name, data + sizeof(GimpPatternHeader), size);
if (name[size - 1]) {
delete[] name;
return false;
}
// size -1 so we don't add the end 0 to the QString...
setName(QString::fromLatin1(name, size -1));
delete[] name;
if (bh.width == 0 || bh.height == 0) {
return false;
}
QImage::Format imageFormat;
if (bh.bytes == 1 || bh.bytes == 3) {
imageFormat = QImage::Format_RGB32;
} else {
imageFormat = QImage::Format_ARGB32;
}
QImage pattern = QImage(bh.width, bh.height, imageFormat);
if (pattern.isNull()) {
return false;
}
k = bh.header_size;
if (bh.bytes == 1) {
// Grayscale
qint32 val;
for (quint32 y = 0; y < bh.height; ++y) {
QRgb* pixels = reinterpret_cast<QRgb*>( pattern.scanLine(y) );
for (quint32 x = 0; x < bh.width; ++x, ++k) {
if (k > dataSize) {
qWarning() << "failed to load grayscale pattern" << filename();
return false;
}
val = data[k];
pixels[x] = qRgb(val, val, val);
}
}
// It was grayscale, so make the pattern as small as possible
// by converting it to Indexed8
pattern = pattern.convertToFormat(QImage::Format_Indexed8);
}
else if (bh.bytes == 2) {
// Grayscale + A
qint32 val;
qint32 alpha;
for (quint32 y = 0; y < bh.height; ++y) {
QRgb* pixels = reinterpret_cast<QRgb*>( pattern.scanLine(y) );
for (quint32 x = 0; x < bh.width; ++x, ++k) {
if (k + 2 > dataSize) {
qWarning() << "failed to load grayscale +_ alpha pattern" << filename();
return false;
}
val = data[k];
alpha = data[k++];
pixels[x] = qRgba(val, val, val, alpha);
}
}
}
else if (bh.bytes == 3) {
// RGB without alpha
for (quint32 y = 0; y < bh.height; ++y) {
QRgb* pixels = reinterpret_cast<QRgb*>( pattern.scanLine(y) );
for (quint32 x = 0; x < bh.width; ++x) {
if (k + 3 > dataSize) {
qWarning() << "failed to load RGB pattern" << filename();
return false;
}
pixels[x] = qRgb(data[k],
data[k + 1],
data[k + 2]);
k += 3;
}
}
} else if (bh.bytes == 4) {
// Has alpha
for (quint32 y = 0; y < bh.height; ++y) {
QRgb* pixels = reinterpret_cast<QRgb*>( pattern.scanLine(y) );
for (quint32 x = 0; x < bh.width; ++x) {
if (k + 4 > dataSize) {
qWarning() << "failed to load RGB + Alpha pattern" << filename();
return false;
}
pixels[x] = qRgba(data[k],
data[k + 1],
data[k + 2],
data[k + 3]);
k += 4;
}
}
} else {
return false;
}
if (pattern.isNull()) {
return false;
}
setPatternImage(pattern);
setValid(true);
return true;
}
qint32 KoPattern::width() const
{
return m_pattern.width();
}
qint32 KoPattern::height() const
{
return m_pattern.height();
}
void KoPattern::setPatternImage(const QImage& image)
{
m_pattern = image;
setImage(image);
setValid(true);
}
-KoPattern& KoPattern::operator=(const KoPattern & pattern)
-{
- setFilename(pattern.filename());
- setPatternImage(pattern.pattern());
- setValid(true);
- return *this;
-}
QString KoPattern::defaultFileExtension() const
{
return QString(".pat");
}
-KoPattern* KoPattern::clone() const
-{
- KoPattern* pat = new KoPattern(filename());
- pat->setPatternImage(pattern());
- pat->setName(name());
- return pat;
-}
QImage KoPattern::pattern() const
{
return m_pattern;
}
diff --git a/libs/pigment/resources/KoPattern.h b/libs/pigment/resources/KoPattern.h
index cd89cbaa17..16ae4d5976 100644
--- a/libs/pigment/resources/KoPattern.h
+++ b/libs/pigment/resources/KoPattern.h
@@ -1,80 +1,89 @@
/*
Copyright (c) 2000 Matthias Elter <elter@kde.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 KOPATTERN_H
#define KOPATTERN_H
-#include <resources/KoResource.h>
+#include <KoResource.h>
#include <kritapigment_export.h>
#include <QMetaType>
+#include <QSharedPointer>
+
+class KoPattern;
+typedef QSharedPointer<KoPattern> KoPatternSP;
+
/// Write API docs here
class KRITAPIGMENT_EXPORT KoPattern : public KoResource
{
-
public:
/**
* Creates a new KoPattern object using @p filename. No file is opened
* in the constructor, you have to call load.
*
* @param filename the file name to save and load from.
*/
explicit KoPattern(const QString &filename);
KoPattern(const QImage &image, const QString &name, const QString &folderName);
~KoPattern() override;
+ KoPattern(const KoPattern &rhs);
+ KoPattern& operator=(const KoPattern& rhs) = delete;
+ KoResourceSP clone() const override;
+
+
public:
- bool load() override;
- bool loadFromDevice(QIODevice *dev) override;
- bool save() override;
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
bool saveToDevice(QIODevice* dev) const override;
bool loadPatFromDevice(QIODevice *dev);
bool savePatToDevice(QIODevice* dev) const;
qint32 width() const;
qint32 height() const;
QString defaultFileExtension() const override;
- KoPattern& operator=(const KoPattern& pattern);
-
- KoPattern* clone() const;
+ QPair<QString, QString> resourceType() const override {
+ return QPair<QString, QString>(ResourceType::Patterns, "");
+ }
/**
* @brief pattern the actual pattern image
* @return a valid QImage. There are no guarantees to the image format.
*/
QImage pattern() const;
private:
bool init(QByteArray& data);
void setPatternImage(const QImage& image);
private:
QImage m_pattern;
mutable QByteArray m_md5;
};
Q_DECLARE_METATYPE(KoPattern*)
+Q_DECLARE_METATYPE(QSharedPointer<KoPattern>)
+
#endif // KOPATTERN_H
diff --git a/libs/pigment/resources/KoResource.cpp b/libs/pigment/resources/KoResource.cpp
deleted file mode 100644
index 4e1aa15e4b..0000000000
--- a/libs/pigment/resources/KoResource.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-/* This file is part of the KDE project
- Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
- Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.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 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/KoResource.h>
-
-#include <QDomElement>
-#include <QFileInfo>
-#include <QDebug>
-#include <QImage>
-#include <QBuffer>
-
-#include "KoHashGenerator.h"
-#include "KoHashGeneratorProvider.h"
-
-struct Q_DECL_HIDDEN KoResource::Private {
- QString name;
- QString filename;
- bool valid;
- bool removable;
- QByteArray md5;
- QImage image;
- bool permanent;
-};
-
-KoResource::KoResource(const QString& filename)
- : d(new Private)
-{
- d->filename = filename;
- d->valid = false;
- QFileInfo fileInfo(filename);
- d->removable = fileInfo.isWritable();
- d->permanent = false;
-}
-
-KoResource::~KoResource()
-{
- delete d;
-}
-
-KoResource::KoResource(const KoResource &rhs)
- : d(new Private(*rhs.d))
-{ }
-
-bool KoResource::saveToDevice(QIODevice *dev) const
-{
- Q_UNUSED(dev)
- d->md5 = QByteArray();
-
- return true;
-}
-
-QImage KoResource::image() const
-{
- return d->image;
-}
-
-void KoResource::setImage(const QImage &image)
-{
- d->image = image;
-}
-
-QByteArray KoResource::md5() const
-{
- if (d->md5.isEmpty()) {
- const_cast<KoResource*>(this)->setMD5(generateMD5());
- }
- return d->md5;
-}
-
-void KoResource::setMD5(const QByteArray &md5)
-{
- d->md5 = md5;
-}
-
-QByteArray KoResource::generateMD5() const
-{
- KoHashGenerator *hashGenerator = KoHashGeneratorProvider::instance()->getGenerator("MD5");
- QByteArray hash = hashGenerator->generateHash(d->filename);
- if (hash.isEmpty()) {
- QByteArray ba;
- QBuffer buf(&ba);
- buf.open(QBuffer::WriteOnly);
- if (saveToDevice(&buf)) {
- buf.close();
- hash = hashGenerator->generateHash(ba);
- }
- }
- return hash;
-}
-
-QString KoResource::filename() const
-{
- return d->filename;
-}
-
-void KoResource::setFilename(const QString& filename)
-{
- d->filename = filename;
- QFileInfo fileInfo(filename);
- d->removable = ! fileInfo.exists() || fileInfo.isWritable();
-}
-
-QString KoResource::shortFilename() const
-{
- QFileInfo fileInfo(d->filename);
- return fileInfo.fileName();
-}
-
-QString KoResource::name() const
-{
- return d->name;
-}
-
-void KoResource::setName(const QString& name)
-{
- d->name = name;
-}
-
-bool KoResource::valid() const
-{
- return d->valid;
-}
-
-void KoResource::setValid(bool valid)
-{
- d->valid = valid;
-}
-
-bool KoResource::removable() const
-{
- return d->removable;
-}
-
-QString KoResource::defaultFileExtension() const
-{
- return QString();
-}
-
-bool KoResource::permanent() const
-{
- return d->permanent;
-}
-
-void KoResource::setPermanent(bool permanent)
-{
- d->permanent = permanent;
-}
-
diff --git a/libs/pigment/resources/KoResource.h b/libs/pigment/resources/KoResource.h
deleted file mode 100644
index be20bed321..0000000000
--- a/libs/pigment/resources/KoResource.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/* This file is part of the KDE project
- Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
- Copyright (c) 2005 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; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#ifndef KORESOURCE_H
-#define KORESOURCE_H
-
-#include <QImage>
-#include <QString>
-#include <QHash>
-
-#include <kritapigment_export.h>
-
-class QDomDocument;
-class QDomElement;
-
-/**
- * The KoResource class provides a representation of resources. This
- * includes, but not limited to, brushes and patterns.
- */
-class KRITAPIGMENT_EXPORT KoResource
-{
-public:
-
- /**
- * Creates a new KoResource object using @p filename. No file is opened
- * in the constructor, you have to call load.
- *
- * @param filename the file name to save and load from.
- */
- explicit KoResource(const QString &filename);
- virtual ~KoResource();
-
- bool operator ==(const KoResource &other) const
- {
- return other.md5() == md5();
- }
-
-public:
- /**
- * Load this resource.
- * @return true if loading the resource succeeded.
- */
- virtual bool load() = 0;
- virtual bool loadFromDevice(QIODevice *dev) = 0;
-
- /**
- * Save this resource.
- *@return true if saving the resource succeeded.
- */
- virtual bool save() = 0;
- virtual bool saveToDevice(QIODevice* dev) const;
-
- /**
- * @returns a QImage thumbnail image representing this resource.
- *
- * This image could be null. The image can be in any valid format.
- */
- QImage image() const;
- void setImage(const QImage &image);
-
- /// @return the md5sum calculated over the contents of the resource.
- QByteArray md5() const;
-
- /// @returns true if resource can be removed by the user
- bool removable() const;
-
- /// @return the full path to this resource
- QString filename() const;
- void setFilename(const QString& filename);
-
- /// @return the name of the file without the path
- QString shortFilename() const;
-
- /// @return the user-visible name of the resource
- QString name() const;
- void setName(const QString& name);
-
- /// @return true if the resource is ready for use
- bool valid() const;
- void setValid(bool valid);
-
- /// @return the default file extension which should be used when saving the resource
- virtual QString defaultFileExtension() const;
-
- /// @return true if the resource is permanent and can't be removed by the user
- bool permanent() const;
- void setPermanent(bool permanent);
-
-protected:
-
- /// override generateMD5 and in your resource subclass
- virtual QByteArray generateMD5() const;
-
- /// call this when the contents of the resource change so the md5 needs to be recalculated
- void setMD5(const QByteArray &md5);
-
-protected:
- KoResource(const KoResource &rhs);
-
-private:
- struct Private;
- Private* const d;
-};
-
-static inline bool operator==(const KoResource &resource1, const KoResource &resource2)
-{
- return (resource1.md5() == resource2.md5());
-}
-
-static inline uint qHash(const KoResource &resource)
-{
- return qHash(resource.md5());
-}
-
-#endif // KORESOURCE_H_
-
diff --git a/libs/pigment/resources/KoSegmentGradient.cpp b/libs/pigment/resources/KoSegmentGradient.cpp
index 9a063b52c3..b9772f10be 100644
--- a/libs/pigment/resources/KoSegmentGradient.cpp
+++ b/libs/pigment/resources/KoSegmentGradient.cpp
@@ -1,984 +1,960 @@
/*
Copyright (c) 2000 Matthias Elter <elter@kde.org>
2001 John Califf
2004 Boudewijn Rempt <boud@valdyas.org>
2004 Adrian Page <adrian@pagenet.plus.com>
2004, 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/KoSegmentGradient.h>
#include <cfloat>
#include <cmath>
#include <QImage>
#include <QTextStream>
#include <QFile>
#include <QByteArray>
#include <QDomDocument>
#include <QDomElement>
#include <QBuffer>
#include <kis_dom_utils.h>
#include "KoColorSpaceRegistry.h"
#include "KoColorSpace.h"
#include "KoMixColorsOp.h"
#include <KoColorModelStandardIds.h>
#include <DebugPigment.h>
#include <klocalizedstring.h>
KoGradientSegment::RGBColorInterpolationStrategy *KoGradientSegment::RGBColorInterpolationStrategy::m_instance = 0;
KoGradientSegment::HSVCWColorInterpolationStrategy *KoGradientSegment::HSVCWColorInterpolationStrategy::m_instance = 0;
KoGradientSegment::HSVCCWColorInterpolationStrategy *KoGradientSegment::HSVCCWColorInterpolationStrategy::m_instance = 0;
KoGradientSegment::LinearInterpolationStrategy *KoGradientSegment::LinearInterpolationStrategy::m_instance = 0;
KoGradientSegment::CurvedInterpolationStrategy *KoGradientSegment::CurvedInterpolationStrategy::m_instance = 0;
KoGradientSegment::SineInterpolationStrategy *KoGradientSegment::SineInterpolationStrategy::m_instance = 0;
KoGradientSegment::SphereIncreasingInterpolationStrategy *KoGradientSegment::SphereIncreasingInterpolationStrategy::m_instance = 0;
KoGradientSegment::SphereDecreasingInterpolationStrategy *KoGradientSegment::SphereDecreasingInterpolationStrategy::m_instance = 0;
KoSegmentGradient::KoSegmentGradient(const QString& file)
: KoAbstractGradient(file)
{
}
KoSegmentGradient::~KoSegmentGradient()
{
for (int i = 0; i < m_segments.count(); i++) {
delete m_segments[i];
m_segments[i] = 0;
}
}
KoSegmentGradient::KoSegmentGradient(const KoSegmentGradient &rhs)
: KoAbstractGradient(rhs)
{
Q_FOREACH (KoGradientSegment *segment, rhs.m_segments) {
pushSegment(new KoGradientSegment(*segment));
}
}
-KoAbstractGradient* KoSegmentGradient::clone() const
+KoResourceSP KoSegmentGradient::clone() const
{
- return new KoSegmentGradient(*this);
+ return KoResourceSP(new KoSegmentGradient(*this));
}
-bool KoSegmentGradient::load()
+bool KoSegmentGradient::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
{
- QFile file(filename());
- if (!file.open(QIODevice::ReadOnly)) {
- warnPigment << "Can't open file " << filename();
- return false;
- }
- bool res = loadFromDevice(&file);
- file.close();
- return res;
-}
+ Q_UNUSED(resourcesInterface);
-bool KoSegmentGradient::loadFromDevice(QIODevice *dev)
-{
QByteArray data = dev->readAll();
QTextStream fileContent(data, QIODevice::ReadOnly);
fileContent.setAutoDetectUnicode(true);
QString header = fileContent.readLine();
if (header != "GIMP Gradient") {
return false;
}
QString nameDefinition = fileContent.readLine();
QString numSegmentsText;
if (nameDefinition.startsWith("Name: ")) {
QString nameText = nameDefinition.right(nameDefinition.length() - 6);
setName(nameText);
numSegmentsText = fileContent.readLine();
} else {
// Older format without name.
numSegmentsText = nameDefinition;
}
dbgPigment << "Loading gradient: " << name();
int numSegments;
bool ok;
numSegments = numSegmentsText.toInt(&ok);
if (!ok || numSegments < 1) {
return false;
}
dbgPigment << "Number of segments = " << numSegments;
const KoColorSpace* rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8();
for (int i = 0; i < numSegments; i++) {
QString segmentText = fileContent.readLine();
QTextStream segmentFields(&segmentText);
QStringList values = segmentText.split(' ');
qreal leftOffset = values[0].toDouble();
qreal middleOffset = values[1].toDouble();
qreal rightOffset = values[2].toDouble();
qreal leftRed = values[3].toDouble();
qreal leftGreen = values[4].toDouble();
qreal leftBlue = values[5].toDouble();
qreal leftAlpha = values[6].toDouble();
qreal rightRed = values[7].toDouble();
qreal rightGreen = values[8].toDouble();
qreal rightBlue = values[9].toDouble();
qreal rightAlpha = values[10].toDouble();
int interpolationType = values[11].toInt();
int colorInterpolationType = values[12].toInt();
quint8 data[4];
data[2] = static_cast<quint8>(leftRed * 255 + 0.5);
data[1] = static_cast<quint8>(leftGreen * 255 + 0.5);
data[0] = static_cast<quint8>(leftBlue * 255 + 0.5);
data[3] = static_cast<quint8>(leftAlpha * OPACITY_OPAQUE_U8 + 0.5);
KoColor leftColor(data, rgbColorSpace);
data[2] = static_cast<quint8>(rightRed * 255 + 0.5);
data[1] = static_cast<quint8>(rightGreen * 255 + 0.5);
data[0] = static_cast<quint8>(rightBlue * 255 + 0.5);
data[3] = static_cast<quint8>(rightAlpha * OPACITY_OPAQUE_U8 + 0.5);
KoColor rightColor(data, rgbColorSpace);
KoGradientSegment *segment = new KoGradientSegment(interpolationType, colorInterpolationType, leftOffset, middleOffset, rightOffset, leftColor, rightColor);
Q_CHECK_PTR(segment);
if (!segment -> isValid()) {
delete segment;
return false;
}
m_segments.push_back(segment);
}
if (!m_segments.isEmpty()) {
updatePreview();
setValid(true);
return true;
} else {
return false;
}
}
-bool KoSegmentGradient::save()
-{
- QFile file(filename());
-
- if (!file.open(QIODevice::WriteOnly)) {
- return false;
- }
-
- saveToDevice(&file);
- file.close();
-
- return true;
-}
-
bool KoSegmentGradient::saveToDevice(QIODevice *dev) const
{
QTextStream fileContent(dev);
fileContent << "GIMP Gradient\n";
fileContent << "Name: " << name() << "\n";
fileContent << m_segments.count() << "\n";
Q_FOREACH (KoGradientSegment* segment, m_segments) {
fileContent << QString::number(segment->startOffset(), 'f') << " " << QString::number(segment->middleOffset(), 'f') << " "
<< QString::number(segment->endOffset(), 'f') << " ";
QColor startColor = segment->startColor().toQColor();
QColor endColor = segment->endColor().toQColor();
fileContent << QString::number(startColor.redF(), 'f') << " " << QString::number(startColor.greenF(), 'f') << " "
<< QString::number(startColor.blueF(), 'f') << " " << QString::number(startColor.alphaF(), 'f') << " ";
fileContent << QString::number(endColor.redF(), 'f') << " " << QString::number(endColor.greenF(), 'f') << " "
<< QString::number(endColor.blueF(), 'f') << " " << QString::number(endColor.alphaF(), 'f') << " ";
fileContent << (int)segment->interpolation() << " " << (int)segment->colorInterpolation() << "\n";
}
KoResource::saveToDevice(dev);
return true;
}
KoGradientSegment *KoSegmentGradient::segmentAt(qreal t) const
{
if (t < 0.0) return 0;
if (t > 1.0) return 0;
if (m_segments.isEmpty()) return 0;
for (QList<KoGradientSegment *>::const_iterator it = m_segments.begin(); it != m_segments.end(); ++it) {
if (t > (*it)->startOffset() - DBL_EPSILON && t < (*it)->endOffset() + DBL_EPSILON) {
return *it;
}
}
return 0;
}
void KoSegmentGradient::colorAt(KoColor& dst, qreal t) const
{
const KoGradientSegment *segment = segmentAt(t);
Q_ASSERT(segment != 0);
if (segment) {
segment->colorAt(dst, t);
}
}
QGradient* KoSegmentGradient::toQGradient() const
{
QGradient* gradient = new QLinearGradient();
QColor color;
Q_FOREACH (KoGradientSegment* segment, m_segments) {
segment->startColor().toQColor(&color);
gradient->setColorAt(segment->startOffset() , color);
segment->endColor().toQColor(&color);
gradient->setColorAt(segment->endOffset() , color);
}
return gradient;
}
QString KoSegmentGradient::defaultFileExtension() const
{
return QString(".ggr");
}
void KoSegmentGradient::toXML(QDomDocument &doc, QDomElement &gradientElt) const
{
gradientElt.setAttribute("type", "segment");
Q_FOREACH(KoGradientSegment *segment, this->segments()) {
QDomElement segmentElt = doc.createElement("segment");
QDomElement start = doc.createElement("start");
QDomElement end = doc.createElement("end");
segmentElt.setAttribute("start-offset", KisDomUtils::toString(segment->startOffset()));
const KoColor startColor = segment->startColor();
segmentElt.setAttribute("start-bitdepth", startColor.colorSpace()->colorDepthId().id());
segmentElt.setAttribute("start-alpha", KisDomUtils::toString(startColor.opacityF()));
startColor.toXML(doc, start);
segmentElt.setAttribute("middle-offset", KisDomUtils::toString(segment->middleOffset()));
segmentElt.setAttribute("end-offset", KisDomUtils::toString(segment->endOffset()));
const KoColor endColor = segment->endColor();
segmentElt.setAttribute("end-bitdepth", endColor.colorSpace()->colorDepthId().id());
segmentElt.setAttribute("end-alpha", KisDomUtils::toString(endColor.opacityF()));
endColor.toXML(doc, end);
segmentElt.setAttribute("interpolation", KisDomUtils::toString(segment->interpolation()));
segmentElt.setAttribute("color-interpolation", KisDomUtils::toString(segment->colorInterpolation()));
segmentElt.appendChild(start);
segmentElt.appendChild(end);
gradientElt.appendChild(segmentElt);
}
}
KoSegmentGradient KoSegmentGradient::fromXML(const QDomElement &elt)
{
KoSegmentGradient gradient;
QDomElement segmentElt = elt.firstChildElement("segment");
while (!segmentElt.isNull()) {
int interpolation = KisDomUtils::toInt(segmentElt.attribute("interpolation", "0.0"));
int colorInterpolation = KisDomUtils::toInt(segmentElt.attribute("color-interpolation", "0.0"));
double startOffset = KisDomUtils::toDouble(segmentElt.attribute("start-offset", "0.0"));
qreal middleOffset = KisDomUtils::toDouble(segmentElt.attribute("middle-offset", "0.0"));
qreal endOffset = KisDomUtils::toDouble(segmentElt.attribute("end-offset", "0.0"));
QDomElement start = segmentElt.firstChildElement("start");
QString startBitdepth = segmentElt.attribute("start-bitdepth", Integer8BitsColorDepthID.id());
QColor left = KoColor::fromXML(start.firstChildElement(), startBitdepth).toQColor();
left.setAlphaF(KisDomUtils::toDouble(segmentElt.attribute("start-alpha", "1.0")));
QString endBitdepth = segmentElt.attribute("end-bitdepth", Integer8BitsColorDepthID.id());
QDomElement end = segmentElt.firstChildElement("end");
QColor right = KoColor::fromXML(end.firstChildElement(), endBitdepth).toQColor();
right.setAlphaF(KisDomUtils::toDouble(segmentElt.attribute("end-alpha", "1.0")));
gradient.createSegment(interpolation, colorInterpolation, startOffset, endOffset, middleOffset, left, right);
segmentElt = segmentElt.nextSiblingElement("segment");
}
return gradient;
}
KoGradientSegment::KoGradientSegment(int interpolationType, int colorInterpolationType, qreal startOffset, qreal middleOffset, qreal endOffset, const KoColor& startColor, const KoColor& endColor)
{
m_interpolator = 0;
switch (interpolationType) {
case INTERP_LINEAR:
m_interpolator = LinearInterpolationStrategy::instance();
break;
case INTERP_CURVED:
m_interpolator = CurvedInterpolationStrategy::instance();
break;
case INTERP_SINE:
m_interpolator = SineInterpolationStrategy::instance();
break;
case INTERP_SPHERE_INCREASING:
m_interpolator = SphereIncreasingInterpolationStrategy::instance();
break;
case INTERP_SPHERE_DECREASING:
m_interpolator = SphereDecreasingInterpolationStrategy::instance();
break;
}
m_colorInterpolator = 0;
switch (colorInterpolationType) {
case COLOR_INTERP_RGB:
m_colorInterpolator = RGBColorInterpolationStrategy::instance();
break;
case COLOR_INTERP_HSV_CCW:
m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance();
break;
case COLOR_INTERP_HSV_CW:
m_colorInterpolator = HSVCWColorInterpolationStrategy::instance();
break;
}
if (startOffset < DBL_EPSILON) {
m_startOffset = 0;
} else if (startOffset > 1 - DBL_EPSILON) {
m_startOffset = 1;
} else {
m_startOffset = startOffset;
}
if (middleOffset < m_startOffset + DBL_EPSILON) {
m_middleOffset = m_startOffset;
} else if (middleOffset > 1 - DBL_EPSILON) {
m_middleOffset = 1;
} else {
m_middleOffset = middleOffset;
}
if (endOffset < m_middleOffset + DBL_EPSILON) {
m_endOffset = m_middleOffset;
} else if (endOffset > 1 - DBL_EPSILON) {
m_endOffset = 1;
} else {
m_endOffset = endOffset;
}
m_length = m_endOffset - m_startOffset;
if (m_length < DBL_EPSILON) {
m_middleT = 0.5;
} else {
m_middleT = (m_middleOffset - m_startOffset) / m_length;
}
m_startColor = startColor;
m_endColor = endColor;
}
const KoColor& KoGradientSegment::startColor() const
{
return m_startColor;
}
const KoColor& KoGradientSegment::endColor() const
{
return m_endColor;
}
qreal KoGradientSegment::startOffset() const
{
return m_startOffset;
}
qreal KoGradientSegment::middleOffset() const
{
return m_middleOffset;
}
qreal KoGradientSegment::endOffset() const
{
return m_endOffset;
}
void KoGradientSegment::setStartOffset(qreal t)
{
m_startOffset = t;
m_length = m_endOffset - m_startOffset;
if (m_length < DBL_EPSILON) {
m_middleT = 0.5;
} else {
m_middleT = (m_middleOffset - m_startOffset) / m_length;
}
}
void KoGradientSegment::setMiddleOffset(qreal t)
{
m_middleOffset = t;
if (m_length < DBL_EPSILON) {
m_middleT = 0.5;
} else {
m_middleT = (m_middleOffset - m_startOffset) / m_length;
}
}
void KoGradientSegment::setEndOffset(qreal t)
{
m_endOffset = t;
m_length = m_endOffset - m_startOffset;
if (m_length < DBL_EPSILON) {
m_middleT = 0.5;
} else {
m_middleT = (m_middleOffset - m_startOffset) / m_length;
}
}
int KoGradientSegment::interpolation() const
{
return m_interpolator->type();
}
void KoGradientSegment::setInterpolation(int interpolationType)
{
switch (interpolationType) {
case INTERP_LINEAR:
m_interpolator = LinearInterpolationStrategy::instance();
break;
case INTERP_CURVED:
m_interpolator = CurvedInterpolationStrategy::instance();
break;
case INTERP_SINE:
m_interpolator = SineInterpolationStrategy::instance();
break;
case INTERP_SPHERE_INCREASING:
m_interpolator = SphereIncreasingInterpolationStrategy::instance();
break;
case INTERP_SPHERE_DECREASING:
m_interpolator = SphereDecreasingInterpolationStrategy::instance();
break;
}
}
int KoGradientSegment::colorInterpolation() const
{
return m_colorInterpolator->type();
}
void KoGradientSegment::setColorInterpolation(int colorInterpolationType)
{
switch (colorInterpolationType) {
case COLOR_INTERP_RGB:
m_colorInterpolator = RGBColorInterpolationStrategy::instance();
break;
case COLOR_INTERP_HSV_CCW:
m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance();
break;
case COLOR_INTERP_HSV_CW:
m_colorInterpolator = HSVCWColorInterpolationStrategy::instance();
break;
}
}
void KoGradientSegment::colorAt(KoColor& dst, qreal t) const
{
Q_ASSERT(t > m_startOffset - DBL_EPSILON && t < m_endOffset + DBL_EPSILON);
qreal segmentT;
if (m_length < DBL_EPSILON) {
segmentT = 0.5;
} else {
segmentT = (t - m_startOffset) / m_length;
}
qreal colorT = m_interpolator->valueAt(segmentT, m_middleT);
m_colorInterpolator->colorAt(dst, colorT, m_startColor, m_endColor);
}
bool KoGradientSegment::isValid() const
{
if (m_interpolator == 0 || m_colorInterpolator == 0)
return false;
return true;
}
KoGradientSegment::RGBColorInterpolationStrategy::RGBColorInterpolationStrategy()
: m_colorSpace(KoColorSpaceRegistry::instance()->rgb8())
{
}
KoGradientSegment::RGBColorInterpolationStrategy *KoGradientSegment::RGBColorInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new RGBColorInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
void KoGradientSegment::RGBColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& _start, const KoColor& _end) const
{
KoColor buffer(m_colorSpace);
KoColor start(m_colorSpace);
KoColor end(m_colorSpace);
KoColor startDummy, endDummy;
//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());
//convert to the right colorspace for the start and end if we have our mixSpace.
if (mixSpace){
startDummy = KoColor(_start, mixSpace);
endDummy = KoColor(_end, mixSpace);
} else {
startDummy = _start;
endDummy = _end;
}
start.fromKoColor(_start);
end.fromKoColor(_end);
const quint8 *colors[2];
colors[0] = startDummy.data();
colors[1] = endDummy.data();
qint16 colorWeights[2];
colorWeights[0] = static_cast<quint8>((1.0 - t) * 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(m_colorSpace);
m_colorSpace->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data());
}
dst.fromKoColor(buffer);
}
KoGradientSegment::HSVCWColorInterpolationStrategy::HSVCWColorInterpolationStrategy()
: m_colorSpace(KoColorSpaceRegistry::instance()->rgb8())
{
}
KoGradientSegment::HSVCWColorInterpolationStrategy *KoGradientSegment::HSVCWColorInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new HSVCWColorInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
void KoGradientSegment::HSVCWColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const
{
QColor sc;
QColor ec;
start.toQColor(&sc);
end.toQColor(&ec);
int s = static_cast<int>(sc.saturation() + t * (ec.saturation() - sc.saturation()) + 0.5);
int v = static_cast<int>(sc.value() + t * (ec.value() - sc.value()) + 0.5);
int h;
if (ec.hue() < sc.hue()) {
h = static_cast<int>(ec.hue() + (1 - t) * (sc.hue() - ec.hue()) + 0.5);
} else {
h = static_cast<int>(ec.hue() + (1 - t) * (360 - ec.hue() + sc.hue()) + 0.5);
if (h > 359) {
h -= 360;
}
}
// XXX: added an explicit cast. Is this correct?
quint8 opacity = static_cast<quint8>(sc.alpha() + t * (ec.alpha() - sc.alpha()));
QColor result;
result.setHsv(h, s, v);
result.setAlpha(opacity);
dst.fromQColor(result);
}
KoGradientSegment::HSVCCWColorInterpolationStrategy::HSVCCWColorInterpolationStrategy() :
m_colorSpace(KoColorSpaceRegistry::instance()->rgb8())
{
}
KoGradientSegment::HSVCCWColorInterpolationStrategy *KoGradientSegment::HSVCCWColorInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new HSVCCWColorInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
void KoGradientSegment::HSVCCWColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const
{
QColor sc;
QColor se;
start.toQColor(&sc);
end.toQColor(&se);
int s = static_cast<int>(sc.saturation() + t * (se.saturation() - sc.saturation()) + 0.5);
int v = static_cast<int>(sc.value() + t * (se.value() - sc.value()) + 0.5);
int h;
if (sc.hue() < se.hue()) {
h = static_cast<int>(sc.hue() + t * (se.hue() - sc.hue()) + 0.5);
} else {
h = static_cast<int>(sc.hue() + t * (360 - sc.hue() + se.hue()) + 0.5);
if (h > 359) {
h -= 360;
}
}
// XXX: Added an explicit static cast
quint8 opacity = static_cast<quint8>(sc.alpha() + t * (se.alpha() - sc.alpha()));
QColor result;
result.setHsv(h, s, v);
result.setAlpha(opacity);
dst.fromQColor(result);
}
KoGradientSegment::LinearInterpolationStrategy *KoGradientSegment::LinearInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new LinearInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
qreal KoGradientSegment::LinearInterpolationStrategy::calcValueAt(qreal t, qreal middle)
{
Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON);
qreal value = 0;
if (t <= middle) {
if (middle < DBL_EPSILON) {
value = 0;
} else {
value = (t / middle) * 0.5;
}
} else {
if (middle > 1 - DBL_EPSILON) {
value = 1;
} else {
value = ((t - middle) / (1 - middle)) * 0.5 + 0.5;
}
}
return value;
}
qreal KoGradientSegment::LinearInterpolationStrategy::valueAt(qreal t, qreal middle) const
{
return calcValueAt(t, middle);
}
KoGradientSegment::CurvedInterpolationStrategy::CurvedInterpolationStrategy()
{
m_logHalf = log(0.5);
}
KoGradientSegment::CurvedInterpolationStrategy *KoGradientSegment::CurvedInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new CurvedInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
qreal KoGradientSegment::CurvedInterpolationStrategy::valueAt(qreal t, qreal middle) const
{
Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON);
qreal value = 0;
if (middle < DBL_EPSILON) {
middle = DBL_EPSILON;
}
value = pow(t, m_logHalf / log(middle));
return value;
}
KoGradientSegment::SineInterpolationStrategy *KoGradientSegment::SineInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new SineInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
qreal KoGradientSegment::SineInterpolationStrategy::valueAt(qreal t, qreal middle) const
{
qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle);
qreal value = (sin(-M_PI_2 + M_PI * lt) + 1.0) / 2.0;
return value;
}
KoGradientSegment::SphereIncreasingInterpolationStrategy *KoGradientSegment::SphereIncreasingInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new SphereIncreasingInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
qreal KoGradientSegment::SphereIncreasingInterpolationStrategy::valueAt(qreal t, qreal middle) const
{
qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle) - 1;
qreal value = sqrt(1 - lt * lt);
return value;
}
KoGradientSegment::SphereDecreasingInterpolationStrategy *KoGradientSegment::SphereDecreasingInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new SphereDecreasingInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
qreal KoGradientSegment::SphereDecreasingInterpolationStrategy::valueAt(qreal t, qreal middle) const
{
qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle);
qreal value = 1 - sqrt(1 - lt * lt);
return value;
}
void KoSegmentGradient::createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, const QColor & left, const QColor & right)
{
pushSegment(new KoGradientSegment(interpolation, colorInterpolation, startOffset, middleOffset, endOffset, KoColor(left, colorSpace()), KoColor(right, colorSpace())));
}
const QList<double> KoSegmentGradient::getHandlePositions() const
{
QList<double> handlePositions;
handlePositions.push_back(m_segments[0]->startOffset());
for (int i = 0; i < m_segments.count(); i++) {
handlePositions.push_back(m_segments[i]->endOffset());
}
return handlePositions;
}
const QList<double> KoSegmentGradient::getMiddleHandlePositions() const
{
QList<double> middleHandlePositions;
for (int i = 0; i < m_segments.count(); i++) {
middleHandlePositions.push_back(m_segments[i]->middleOffset());
}
return middleHandlePositions;
}
void KoSegmentGradient::moveSegmentStartOffset(KoGradientSegment* segment, double t)
{
QList<KoGradientSegment*>::iterator it = std::find(m_segments.begin(), m_segments.end(), segment);
if (it != m_segments.end()) {
if (it == m_segments.begin()) {
segment->setStartOffset(0.0);
return;
}
KoGradientSegment* previousSegment = (*(it - 1));
if (t > segment->startOffset()) {
if (t > segment->middleOffset())
t = segment->middleOffset();
} else {
if (t < previousSegment->middleOffset())
t = previousSegment->middleOffset();
}
previousSegment->setEndOffset(t);
segment->setStartOffset(t);
}
}
void KoSegmentGradient::moveSegmentEndOffset(KoGradientSegment* segment, double t)
{
QList<KoGradientSegment*>::iterator it = std::find(m_segments.begin(), m_segments.end(), segment);
if (it != m_segments.end()) {
if (it + 1 == m_segments.end()) {
segment->setEndOffset(1.0);
return;
}
KoGradientSegment* followingSegment = (*(it + 1));
if (t < segment->endOffset()) {
if (t < segment->middleOffset())
t = segment->middleOffset();
} else {
if (t > followingSegment->middleOffset())
t = followingSegment->middleOffset();
}
followingSegment->setStartOffset(t);
segment->setEndOffset(t);
}
}
void KoSegmentGradient::moveSegmentMiddleOffset(KoGradientSegment* segment, double t)
{
if (segment) {
if (t > segment->endOffset())
segment->setMiddleOffset(segment->endOffset());
else if (t < segment->startOffset())
segment->setMiddleOffset(segment->startOffset());
else
segment->setMiddleOffset(t);
}
}
void KoSegmentGradient::splitSegment(KoGradientSegment* segment)
{
Q_ASSERT(segment != 0);
QList<KoGradientSegment*>::iterator it = std::find(m_segments.begin(), m_segments.end(), segment);
if (it != m_segments.end()) {
KoColor midleoffsetColor(segment->endColor().colorSpace());
segment->colorAt(midleoffsetColor, segment->middleOffset());
KoGradientSegment* newSegment = new KoGradientSegment(
segment->interpolation(), segment->colorInterpolation(),
segment ->startOffset(),
(segment->middleOffset() - segment->startOffset()) / 2 + segment->startOffset(),
segment->middleOffset(),
segment->startColor(),
midleoffsetColor);
m_segments.insert(it, newSegment);
segment->setStartColor(midleoffsetColor);
segment->setStartOffset(segment->middleOffset());
segment->setMiddleOffset((segment->endOffset() - segment->startOffset()) / 2 + segment->startOffset());
}
}
void KoSegmentGradient::duplicateSegment(KoGradientSegment* segment)
{
Q_ASSERT(segment != 0);
QList<KoGradientSegment*>::iterator it = std::find(m_segments.begin(), m_segments.end(), segment);
if (it != m_segments.end()) {
double middlePostionPercentage = (segment->middleOffset() - segment->startOffset()) / segment->length();
double center = segment->startOffset() + segment->length() / 2;
KoGradientSegment* newSegment = new KoGradientSegment(
segment->interpolation(), segment->colorInterpolation(),
segment ->startOffset(),
segment->length() / 2 * middlePostionPercentage + segment->startOffset(),
center, segment->startColor(),
segment->endColor());
m_segments.insert(it, newSegment);
segment->setStartOffset(center);
segment->setMiddleOffset(segment->length() * middlePostionPercentage + segment->startOffset());
}
}
void KoSegmentGradient::mirrorSegment(KoGradientSegment* segment)
{
Q_ASSERT(segment != 0);
KoColor tmpColor = segment->startColor();
segment->setStartColor(segment->endColor());
segment->setEndColor(tmpColor);
segment->setMiddleOffset(segment->endOffset() - (segment->middleOffset() - segment->startOffset()));
if (segment->interpolation() == INTERP_SPHERE_INCREASING)
segment->setInterpolation(INTERP_SPHERE_DECREASING);
else if (segment->interpolation() == INTERP_SPHERE_DECREASING)
segment->setInterpolation(INTERP_SPHERE_INCREASING);
if (segment->colorInterpolation() == COLOR_INTERP_HSV_CW)
segment->setColorInterpolation(COLOR_INTERP_HSV_CCW);
else if (segment->colorInterpolation() == COLOR_INTERP_HSV_CCW)
segment->setColorInterpolation(COLOR_INTERP_HSV_CW);
}
KoGradientSegment* KoSegmentGradient::removeSegment(KoGradientSegment* segment)
{
Q_ASSERT(segment != 0);
if (m_segments.count() < 2)
return 0;
QList<KoGradientSegment*>::iterator it = std::find(m_segments.begin(), m_segments.end(), segment);
if (it != m_segments.end()) {
double middlePostionPercentage;
KoGradientSegment* nextSegment;
if (it == m_segments.begin()) {
nextSegment = (*(it + 1));
middlePostionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length();
nextSegment->setStartOffset(segment->startOffset());
nextSegment->setMiddleOffset(middlePostionPercentage * nextSegment->length() + nextSegment->startOffset());
} else {
nextSegment = (*(it - 1));
middlePostionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length();
nextSegment->setEndOffset(segment->endOffset());
nextSegment->setMiddleOffset(middlePostionPercentage * nextSegment->length() + nextSegment->startOffset());
}
delete segment;
m_segments.erase(it);
return nextSegment;
}
return 0;
}
bool KoSegmentGradient::removeSegmentPossible() const
{
if (m_segments.count() < 2)
return false;
return true;
}
const QList<KoGradientSegment *>& KoSegmentGradient::segments() const
{
return m_segments;
}
diff --git a/libs/pigment/resources/KoSegmentGradient.h b/libs/pigment/resources/KoSegmentGradient.h
index 3bd9bb5019..efc7702f97 100644
--- a/libs/pigment/resources/KoSegmentGradient.h
+++ b/libs/pigment/resources/KoSegmentGradient.h
@@ -1,431 +1,433 @@
/*
Copyright (c) 2000 Matthias Elter <elter@kde.org>
2004 Boudewijn Rempt <boud@valdyas.org>
2004 Adrian Page <adrian@pagenet.plus.com>
2004, 2007 Sven Langkamp <sven.langkamp@gmail.com>
2017 Wolthera van Hövell tot Westerflier <griffinvalley@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 KOSEGMENTGRADIENT_H
#define KOSEGMENTGRADIENT_H
#include <QList>
#include <QColor>
-#include <resources/KoResource.h>
+#include <KoResource.h>
#include <resources/KoAbstractGradient.h>
#include "KoColor.h"
#include <kritapigment_export.h>
enum {
INTERP_LINEAR = 0,
INTERP_CURVED,
INTERP_SINE,
INTERP_SPHERE_INCREASING,
INTERP_SPHERE_DECREASING
};
enum {
COLOR_INTERP_RGB,
COLOR_INTERP_HSV_CCW,
COLOR_INTERP_HSV_CW
};
/// Write API docs here
class KRITAPIGMENT_EXPORT KoGradientSegment
{
public:
KoGradientSegment(int interpolationType, int colorInterpolationType, qreal startOffset, qreal middleOffset, qreal endOffset, const KoColor& startColor, const KoColor& endColor);
// startOffset <= t <= endOffset
void colorAt(KoColor&, qreal t) const;
const KoColor& startColor() const;
const KoColor& endColor() const;
void setStartColor(const KoColor& color) {
m_startColor = color;
}
void setEndColor(const KoColor& color) {
m_endColor = color;
}
qreal startOffset() const;
qreal middleOffset() const;
qreal endOffset() const;
void setStartOffset(qreal t);
void setMiddleOffset(qreal t);
void setEndOffset(qreal t);
qreal length() {
return m_length;
}
int interpolation() const;
int colorInterpolation() const;
void setInterpolation(int interpolationType);
void setColorInterpolation(int colorInterpolationType);
bool isValid() const;
+
protected:
class ColorInterpolationStrategy
{
public:
ColorInterpolationStrategy() {}
virtual ~ColorInterpolationStrategy() {}
virtual void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const = 0;
virtual int type() const = 0;
};
class RGBColorInterpolationStrategy : public ColorInterpolationStrategy
{
public:
static RGBColorInterpolationStrategy *instance();
void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const override;
int type() const override {
return COLOR_INTERP_RGB;
}
private:
RGBColorInterpolationStrategy();
static RGBColorInterpolationStrategy *m_instance;
const KoColorSpace * const m_colorSpace;
};
class HSVCWColorInterpolationStrategy : public ColorInterpolationStrategy
{
public:
static HSVCWColorInterpolationStrategy *instance();
void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const override;
int type() const override {
return COLOR_INTERP_HSV_CW;
}
private:
HSVCWColorInterpolationStrategy();
static HSVCWColorInterpolationStrategy *m_instance;
const KoColorSpace * const m_colorSpace;
};
class HSVCCWColorInterpolationStrategy : public ColorInterpolationStrategy
{
public:
static HSVCCWColorInterpolationStrategy *instance();
void colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const override;
int type() const override {
return COLOR_INTERP_HSV_CCW;
}
private:
HSVCCWColorInterpolationStrategy();
static HSVCCWColorInterpolationStrategy *m_instance;
const KoColorSpace * const m_colorSpace;
};
class InterpolationStrategy
{
public:
InterpolationStrategy() {}
virtual ~InterpolationStrategy() {}
virtual qreal valueAt(qreal t, qreal middle) const = 0;
virtual int type() const = 0;
};
class LinearInterpolationStrategy : public InterpolationStrategy
{
public:
static LinearInterpolationStrategy *instance();
qreal valueAt(qreal t, qreal middle) const override;
int type() const override {
return INTERP_LINEAR;
}
// This does the actual calculation and is made
// static as an optimization for the other
// strategies that need this for their own calculation.
static qreal calcValueAt(qreal t, qreal middle);
private:
LinearInterpolationStrategy() {}
static LinearInterpolationStrategy *m_instance;
};
class CurvedInterpolationStrategy : public InterpolationStrategy
{
public:
static CurvedInterpolationStrategy *instance();
qreal valueAt(qreal t, qreal middle) const override;
int type() const override {
return INTERP_CURVED;
}
private:
CurvedInterpolationStrategy();
static CurvedInterpolationStrategy *m_instance;
qreal m_logHalf;
};
class SphereIncreasingInterpolationStrategy : public InterpolationStrategy
{
public:
static SphereIncreasingInterpolationStrategy *instance();
qreal valueAt(qreal t, qreal middle) const override;
int type() const override {
return INTERP_SPHERE_INCREASING;
}
private:
SphereIncreasingInterpolationStrategy() {}
static SphereIncreasingInterpolationStrategy *m_instance;
};
class SphereDecreasingInterpolationStrategy : public InterpolationStrategy
{
public:
static SphereDecreasingInterpolationStrategy *instance();
qreal valueAt(qreal t, qreal middle) const override;
int type() const override {
return INTERP_SPHERE_DECREASING;
}
private:
SphereDecreasingInterpolationStrategy() {}
static SphereDecreasingInterpolationStrategy *m_instance;
};
class SineInterpolationStrategy : public InterpolationStrategy
{
public:
static SineInterpolationStrategy *instance();
qreal valueAt(qreal t, qreal middle) const override;
int type() const override {
return INTERP_SINE;
}
private:
SineInterpolationStrategy() {}
static SineInterpolationStrategy *m_instance;
};
private:
InterpolationStrategy *m_interpolator;
ColorInterpolationStrategy *m_colorInterpolator;
qreal m_startOffset;
qreal m_middleOffset;
qreal m_endOffset;
qreal m_length;
qreal m_middleT;
KoColor m_startColor;
KoColor m_endColor;
};
/**
* KoSegmentGradient stores a segment based gradients like Gimp gradients
*/
class KRITAPIGMENT_EXPORT KoSegmentGradient : public KoAbstractGradient
{
public:
explicit KoSegmentGradient(const QString &file = QString());
~KoSegmentGradient() override;
+ KoSegmentGradient(const KoSegmentGradient &rhs);
+ KoSegmentGradient &operator=(const KoSegmentGradient &rhs) = delete;
+ KoResourceSP clone() const override;
- KoAbstractGradient* clone() const override;
-
- /// reimplemented
- bool load() override;
- bool loadFromDevice(QIODevice *dev) override;
-
- /// not implemented
- bool save() override;
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
bool saveToDevice(QIODevice* dev) const override;
+ QPair<QString, QString> resourceType() const override {
+ return QPair<QString, QString>(ResourceType::Gradients, ResourceSubType::SegmentedGradients);
+ }
+
/// reimplemented
void colorAt(KoColor& dst, qreal t) const override;
/**
* Returns the segment at a given position
* @param t position inside the gradient, with 0 <= t <= 1
* @return the segment the position, 0 if no segment is found
*/
KoGradientSegment *segmentAt(qreal t) const;
/// reimplemented
QGradient* toQGradient() const override;
/// reimplemented
QString defaultFileExtension() const override;
/**
* @brief toXML
* convert the gradient to xml.
*/
void toXML(QDomDocument& doc, QDomElement& gradientElt) const;
/**
* @brief fromXML
* get a segment gradient from xml.
* @return gradient
*/
static KoSegmentGradient fromXML(const QDomElement& elt);
/**
* a gradient colour picker can consist of one or more segments.
* A segment has two end points - each colour in the gradient
* colour picker represents a segment end point.
* @param interpolation
* @param colorInterpolation
* @param startOffset
* @param endOffset
* @param middleOffset
* @param left
* @param right
* @return void
*/
void createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, const QColor & left, const QColor & right);
/**
* gets a list of end points of the segments in the gradient
* colour picker. If two colours, one segment then two end
* points, and if three colours, then two segments with four
* endpoints.
* @return a list of double values
*/
const QList<double> getHandlePositions() const;
/**
* gets a list of middle points of the segments in the gradient
* colour picker.
* @return a list of double values
*/
const QList<double> getMiddleHandlePositions() const;
/**
* Moves the StartOffset of the specified segment to the
* specified value and corrects the endoffset of the previous
* segment. If the segment is the first Segment the startoffset
* will be set to 0.0 . The offset will maximally be moved till
* the middle of the current or the previous segment. This is
* useful if someone clicks to move the handler for a segment,
* to set the half the segment to the right and half the segment
* to the left of the handler.
* @param segment the segment for which to move the relative
* offset within the gradient colour picker.
* @param t the new startoff position for the segment
* @return void
*/
void moveSegmentStartOffset(KoGradientSegment* segment, double t);
/**
* Moves the endoffset of the specified segment to the specified
* value and corrects the startoffset of the following segment.
* If the segment is the last segment the endoffset will be set
* to 1.0 . The offset will maximally be moved till the middle
* of the current or the following segment. This is useful if
* someone moves the segment handler in the gradient colour
* picker, and needs the segment to move with it. Sets the end
* position of the segment to the correct new position.
* @param segment the segment for which to move the relative
* end position within the gradient colour picker.
* @param t the new end position for the segment
* @return void
*/
void moveSegmentEndOffset(KoGradientSegment* segment, double t);
/**
* moves the Middle of the specified segment to the specified
* value. The offset will maximally be moved till the endoffset
* or startoffset of the segment. This sets the middle of the
* segment to the same position as the handler of the gradient
* colour picker.
* @param segment the segment for which to move the relative
* middle position within the gradient colour picker.
* @param t the new middle position for the segment
* @return void
*/
void moveSegmentMiddleOffset(KoGradientSegment* segment, double t);
/**
* splits the specified segment into two equal parts
* @param segment the segment to split
* @return void
*/
void splitSegment(KoGradientSegment* segment);
/**
* duplicate the specified segment
* @param segment the segment to duplicate
* @return void
*/
void duplicateSegment(KoGradientSegment* segment);
/**
* create a segment horizontally reversed to the specified one.
* @param segment the segment to reverse
* @return void
*/
void mirrorSegment(KoGradientSegment* segment);
/**
* removes the specific segment from the gradient colour picker.
* @param segment the segment to remove
* @return the segment which will be at the place of the old
* segment. 0 if the segment is not in the gradient or it is
* not possible to remove the segment.
*/
KoGradientSegment* removeSegment(KoGradientSegment* segment);
/**
* checks if it's possible to remove a segment (at least two
* segments in the gradient)
* @return true if it's possible to remove an segment
*/
bool removeSegmentPossible() const;
const QList<KoGradientSegment *>& segments() const;
protected:
- KoSegmentGradient(const KoSegmentGradient &rhs);
inline void pushSegment(KoGradientSegment* segment) {
m_segments.push_back(segment);
}
QList<KoGradientSegment *> m_segments;
- private:
+private:
bool init();
};
+typedef QSharedPointer<KoSegmentGradient> KoSegmentGradientSP;
+
#endif // KOSEGMENTGRADIENT_H
diff --git a/libs/pigment/resources/KoStopGradient.cpp b/libs/pigment/resources/KoStopGradient.cpp
index ac583e733e..5da8b2f7fc 100644
--- a/libs/pigment/resources/KoStopGradient.cpp
+++ b/libs/pigment/resources/KoStopGradient.cpp
@@ -1,604 +1,590 @@
/*
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 "kis_dom_utils.h"
#include <math.h>
#include <KoColorModelStandardIds.h>
KoStopGradient::KoStopGradient(const QString& filename)
: KoAbstractGradient(filename)
{
}
KoStopGradient::~KoStopGradient()
{
}
+KoStopGradient::KoStopGradient(const KoStopGradient &rhs)
+ : KoAbstractGradient(rhs),
+ m_stops(rhs.m_stops),
+ m_start(rhs.m_start),
+ m_stop(rhs.m_stop),
+ m_focalPoint(rhs.m_focalPoint)
+{
+}
+
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;
+ m_stops == rhs.m_stops;
}
-KoAbstractGradient* KoStopGradient::clone() const
+KoResourceSP KoStopGradient::clone() const
{
- return new KoStopGradient(*this);
+ return KoResourceSP(new KoStopGradient(*this));
}
-bool KoStopGradient::load()
+bool KoStopGradient::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
{
- QFile f(filename());
- if (!f.open(QIODevice::ReadOnly)) {
- warnPigment << "Can't open file " << filename();
- return false;
- }
- bool res = loadFromDevice(&f);
- f.close();
- return res;
-}
+ Q_UNUSED(resourcesInterface);
-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);
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;
}
bool KoStopGradient::stopsAt(KoGradientStop &leftStop, KoGradientStop &rightStop, qreal t) const
{
if (! m_stops.count())
return false;
if (t <= m_stops.first().first || m_stops.count() == 1) {
// we have only one stop or t is before the first stop
leftStop = m_stops.first();
rightStop = KoGradientStop(-std::numeric_limits<double>::infinity(), leftStop.second);
return true;
} else if (t >= m_stops.last().first) {
// t is after the last stop
rightStop = m_stops.last();
leftStop = KoGradientStop(std::numeric_limits<double>::infinity(), rightStop.second);
return true;
} else {
// we have at least two color stops
// -> find the two stops which frame our t
auto it = std::lower_bound(m_stops.begin(), m_stops.end(), KoGradientStop(t, KoColor()), [](const KoGradientStop &a, const KoGradientStop &b){
return a.first < b.first;
});
leftStop = *(it - 1);
rightStop = *(it);
return true;
}
}
void KoStopGradient::colorAt(KoColor& dst, qreal t) const
{
KoColor buffer;
KoGradientStop leftStop, rightStop;
if (!stopsAt(leftStop, rightStop, t)) return;
const KoColorSpace* mixSpace = KoColorSpaceRegistry::instance()->rgb8(dst.colorSpace()->profile());
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)
+QSharedPointer<KoStopGradient> KoStopGradient::fromQGradient(const QGradient *gradient)
{
if (! gradient)
- return 0;
+ return QSharedPointer<KoStopGradient>(0);
- KoStopGradient * newGradient = new KoStopGradient(QString());
+ QSharedPointer<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;
+ return QSharedPointer<KoStopGradient>(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::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::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", KisDomUtils::toString(stop.first));
stopElt.setAttribute("bitdepth", stop.second.colorSpace()->colorDepthId().id());
stopElt.setAttribute("alpha", KisDomUtils::toString(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 = KisDomUtils::toDouble(stopElt.attribute("offset", "0.0"));
QString bitDepth = stopElt.attribute("bitdepth", Integer8BitsColorDepthID.id());
KoColor color = KoColor::fromXML(stopElt.firstChildElement(), bitDepth);
color.setOpacity(KisDomUtils::toDouble(stopElt.attribute("alpha", "1.0")));
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 4c4d653809..587586d1d8 100644
--- a/libs/pigment/resources/KoStopGradient.h
+++ b/libs/pigment/resources/KoStopGradient.h
@@ -1,104 +1,108 @@
/*
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 <KoResource.h>
#include <kritapigment_export.h>
#include <boost/operators.hpp>
typedef QPair<qreal, KoColor> KoGradientStop;
struct KoGradientStopValueSort
{
inline bool operator() (const KoGradientStop& a, const KoGradientStop& b) {
return (a.second.toQColor().valueF() < b.second.toQColor().valueF());
}
};
/**
* 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;
-
+ KoStopGradient(const KoStopGradient &rhs);
bool operator==(const KoStopGradient &rhs) const;
+ KoStopGradient &operator=(const KoStopGradient &rhs) = delete;
+ KoResourceSP clone() const override;
- KoAbstractGradient* clone() const override;
-
- bool load() override;
- bool loadFromDevice(QIODevice *dev) override;
- bool save() override;
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
bool saveToDevice(QIODevice* dev) const override;
+ QPair<QString, QString> resourceType() const override {
+ return QPair<QString, QString>(ResourceType::Gradients, ResourceSubType::StopGradients);
+ }
+
/// reimplemented
QGradient* toQGradient() const override;
/// Find stops surrounding position, returns false if position outside gradient
bool stopsAt(KoGradientStop& leftStop, KoGradientStop& rightStop, qreal t) const;
/// reimplemented
void colorAt(KoColor&, qreal t) const override;
/// Creates KoStopGradient from a QGradient
- static KoStopGradient * fromQGradient(const QGradient * gradient);
+ static QSharedPointer<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 loadSvgGradient(QIODevice *file);
void parseSvgGradient(const QDomElement& element);
void parseSvgColor(QColor &color, const QString &s);
};
+typedef QSharedPointer<KoStopGradient> KoStopGradientSP;
+
#endif // KOSTOPGRADIENT_H
diff --git a/libs/psd/asl/kis_asl_callback_object_catcher.cpp b/libs/psd/asl/kis_asl_callback_object_catcher.cpp
index 3a7f7057cb..e11b33bc41 100644
--- a/libs/psd/asl/kis_asl_callback_object_catcher.cpp
+++ b/libs/psd/asl/kis_asl_callback_object_catcher.cpp
@@ -1,258 +1,270 @@
/*
* 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_asl_callback_object_catcher.h"
#include <QHash>
#include <QString>
#include <QPointF>
#include <QColor>
#include "kis_debug.h"
typedef QHash<QString, ASLCallbackDouble> MapHashDouble;
typedef QHash<QString, ASLCallbackInteger> MapHashInt;
struct EnumMapping {
EnumMapping(const QString &_typeId, ASLCallbackString _map)
: typeId(_typeId),
map(_map)
{
}
QString typeId;
ASLCallbackString map;
};
typedef QHash<QString, EnumMapping> MapHashEnum;
struct UnitFloatMapping {
UnitFloatMapping(const QString &_unit, ASLCallbackDouble _map)
: unit(_unit),
map(_map)
{
}
QString unit;
ASLCallbackDouble map;
};
typedef QHash<QString, UnitFloatMapping> MapHashUnitFloat;
typedef QHash<QString, ASLCallbackString> MapHashText;
typedef QHash<QString, ASLCallbackBoolean> MapHashBoolean;
typedef QHash<QString, ASLCallbackColor> MapHashColor;
typedef QHash<QString, ASLCallbackPoint> MapHashPoint;
typedef QHash<QString, ASLCallbackCurve> MapHashCurve;
typedef QHash<QString, ASLCallbackPattern> MapHashPattern;
typedef QHash<QString, ASLCallbackPatternRef> MapHashPatternRef;
typedef QHash<QString, ASLCallbackGradient> MapHashGradient;
struct KisAslCallbackObjectCatcher::Private
{
MapHashDouble mapDouble;
MapHashInt mapInteger;
MapHashEnum mapEnum;
MapHashUnitFloat mapUnitFloat;
MapHashText mapText;
MapHashBoolean mapBoolean;
MapHashColor mapColor;
MapHashPoint mapPoint;
MapHashCurve mapCurve;
MapHashPattern mapPattern;
MapHashPatternRef mapPatternRef;
MapHashGradient mapGradient;
ASLCallbackNewStyle newStyleCallback;
};
KisAslCallbackObjectCatcher::KisAslCallbackObjectCatcher()
: m_d(new Private)
{
}
KisAslCallbackObjectCatcher::~KisAslCallbackObjectCatcher()
{
}
template <class HashType, typename T>
inline void passToCallback(const QString &path, const HashType &hash, const T &value)
{
typename HashType::const_iterator it = hash.constFind(path);
if (it != hash.constEnd()) {
(*it)(value);
}
}
+template <class HashType, typename T1, typename T2>
+inline void passToCallback(const QString &path, const HashType &hash, const T1 &value1, const T2 &value2)
+{
+ typename HashType::const_iterator it = hash.constFind(path);
+ if (it != hash.constEnd()) {
+ (*it)(value1, value2);
+ }
+ else {
+ warnKrita << "Couldn't find a callback, even though the non-empty catcher is used";
+ }
+}
+
void KisAslCallbackObjectCatcher::addDouble(const QString &path, double value)
{
passToCallback(path, m_d->mapDouble, value);
}
void KisAslCallbackObjectCatcher::addInteger(const QString &path, int value)
{
passToCallback(path, m_d->mapInteger, value);
}
void KisAslCallbackObjectCatcher::addEnum(const QString &path, const QString &typeId, const QString &value)
{
MapHashEnum::const_iterator it = m_d->mapEnum.constFind(path);
if (it != m_d->mapEnum.constEnd()) {
if (it->typeId == typeId) {
it->map(value);
} else {
warnKrita << "KisAslCallbackObjectCatcher::addEnum: inconsistent typeId" << ppVar(typeId) << ppVar(it->typeId);
}
}
}
void KisAslCallbackObjectCatcher::addUnitFloat(const QString &path, const QString &unit, double value)
{
MapHashUnitFloat::const_iterator it = m_d->mapUnitFloat.constFind(path);
if (it != m_d->mapUnitFloat.constEnd()) {
if (it->unit == unit) {
it->map(value);
} else {
warnKrita << "KisAslCallbackObjectCatcher::addUnitFloat: inconsistent unit" << ppVar(unit) << ppVar(it->unit);
}
}
}
void KisAslCallbackObjectCatcher::addText(const QString &path, const QString &value)
{
passToCallback(path, m_d->mapText, value);
}
void KisAslCallbackObjectCatcher::addBoolean(const QString &path, bool value)
{
passToCallback(path, m_d->mapBoolean, value);
}
void KisAslCallbackObjectCatcher::addColor(const QString &path, const QColor &value)
{
passToCallback(path, m_d->mapColor, value);
}
void KisAslCallbackObjectCatcher::addPoint(const QString &path, const QPointF &value)
{
passToCallback(path, m_d->mapPoint, value);
}
void KisAslCallbackObjectCatcher::addCurve(const QString &path, const QString &name, const QVector<QPointF> &points)
{
MapHashCurve::const_iterator it = m_d->mapCurve.constFind(path);
if (it != m_d->mapCurve.constEnd()) {
(*it)(name, points);
}
}
-void KisAslCallbackObjectCatcher::addPattern(const QString &path, const KoPattern *value)
+void KisAslCallbackObjectCatcher::addPattern(const QString &path, const KoPatternSP value, const QString& patternUuid)
{
- passToCallback(path, m_d->mapPattern, value);
+ passToCallback(path, m_d->mapPattern, value, patternUuid);
}
void KisAslCallbackObjectCatcher::addPatternRef(const QString &path, const QString &patternUuid, const QString &patternName)
{
MapHashPatternRef::const_iterator it = m_d->mapPatternRef.constFind(path);
if (it != m_d->mapPatternRef.constEnd()) {
(*it)(patternUuid, patternName);
}
}
void KisAslCallbackObjectCatcher::addGradient(const QString &path, KoAbstractGradientSP value)
{
passToCallback(path, m_d->mapGradient, value);
}
void KisAslCallbackObjectCatcher::newStyleStarted()
{
if (m_d->newStyleCallback) {
m_d->newStyleCallback();
}
}
/*****************************************************************/
/* Subscription methods */
/*****************************************************************/
void KisAslCallbackObjectCatcher::subscribeDouble(const QString &path, ASLCallbackDouble callback)
{
m_d->mapDouble.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeInteger(const QString &path, ASLCallbackInteger callback)
{
m_d->mapInteger.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeEnum(const QString &path, const QString &typeId, ASLCallbackString callback)
{
m_d->mapEnum.insert(path, EnumMapping(typeId, callback));
}
void KisAslCallbackObjectCatcher::subscribeUnitFloat(const QString &path, const QString &unit, ASLCallbackDouble callback)
{
m_d->mapUnitFloat.insert(path, UnitFloatMapping(unit, callback));
}
void KisAslCallbackObjectCatcher::subscribeText(const QString &path, ASLCallbackString callback)
{
m_d->mapText.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeBoolean(const QString &path, ASLCallbackBoolean callback)
{
m_d->mapBoolean.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeColor(const QString &path, ASLCallbackColor callback)
{
m_d->mapColor.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribePoint(const QString &path, ASLCallbackPoint callback)
{
m_d->mapPoint.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeCurve(const QString &path, ASLCallbackCurve callback)
{
m_d->mapCurve.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribePattern(const QString &path, ASLCallbackPattern callback)
{
m_d->mapPattern.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribePatternRef(const QString &path, ASLCallbackPatternRef callback)
{
m_d->mapPatternRef.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeGradient(const QString &path, ASLCallbackGradient callback)
{
m_d->mapGradient.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeNewStyleStarted(ASLCallbackNewStyle callback)
{
m_d->newStyleCallback = callback;
}
diff --git a/libs/psd/asl/kis_asl_callback_object_catcher.h b/libs/psd/asl/kis_asl_callback_object_catcher.h
index 0e9a6f2839..17e757fec5 100644
--- a/libs/psd/asl/kis_asl_callback_object_catcher.h
+++ b/libs/psd/asl/kis_asl_callback_object_catcher.h
@@ -1,85 +1,85 @@
/*
* 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_ASL_CALLBACK_OBJECT_CATCHER_H
#define __KIS_ASL_CALLBACK_OBJECT_CATCHER_H
#include "kis_asl_object_catcher.h"
#include <boost/function.hpp>
#include <QScopedPointer>
#include <resources/KoAbstractGradient.h>
#include "kritapsd_export.h"
class KoPattern;
typedef boost::function<void (double)> ASLCallbackDouble;
typedef boost::function<void (int)> ASLCallbackInteger;
typedef boost::function<void (const QString &)> ASLCallbackString;
typedef boost::function<void (bool)> ASLCallbackBoolean;
typedef boost::function<void (const QColor &)> ASLCallbackColor;
typedef boost::function<void (const QPointF &)> ASLCallbackPoint;
typedef boost::function<void (const QString &, const QVector<QPointF> &)> ASLCallbackCurve;
-typedef boost::function<void (const KoPattern *)> ASLCallbackPattern;
+typedef boost::function<void (const KoPatternSP, const QString& )> ASLCallbackPattern;
typedef boost::function<void (const QString &, const QString &)> ASLCallbackPatternRef;
typedef boost::function<void (KoAbstractGradientSP)> ASLCallbackGradient;
typedef boost::function<void ()> ASLCallbackNewStyle;
class KRITAPSD_EXPORT KisAslCallbackObjectCatcher : public KisAslObjectCatcher
{
public:
KisAslCallbackObjectCatcher();
~KisAslCallbackObjectCatcher() override;
void addDouble(const QString &path, double value) override;
void addInteger(const QString &path, int value) override;
void addEnum(const QString &path, const QString &typeId, const QString &value) override;
void addUnitFloat(const QString &path, const QString &unit, double value) override;
void addText(const QString &path, const QString &value) override;
void addBoolean(const QString &path, bool value) override;
void addColor(const QString &path, const QColor &value) override;
void addPoint(const QString &path, const QPointF &value) override;
void addCurve(const QString &path, const QString &name, const QVector<QPointF> &points) override;
- void addPattern(const QString &path, const KoPattern *pattern) override;
+ void addPattern(const QString &path, const KoPatternSP pattern, const QString& patternUuid) override;
void addPatternRef(const QString &path, const QString &patternUuid, const QString &patternName) override;
void addGradient(const QString &path, KoAbstractGradientSP gradient) override;
void newStyleStarted() override;
void subscribeDouble(const QString &path, ASLCallbackDouble callback);
void subscribeInteger(const QString &path, ASLCallbackInteger callback);
void subscribeEnum(const QString &path, const QString &typeId, ASLCallbackString callback);
void subscribeUnitFloat(const QString &path, const QString &unit, ASLCallbackDouble callback);
void subscribeText(const QString &path, ASLCallbackString callback);
void subscribeBoolean(const QString &path, ASLCallbackBoolean callback);
void subscribeColor(const QString &path, ASLCallbackColor callback);
void subscribePoint(const QString &path, ASLCallbackPoint callback);
void subscribeCurve(const QString &path, ASLCallbackCurve callback);
void subscribePattern(const QString &path, ASLCallbackPattern callback);
void subscribePatternRef(const QString &path, ASLCallbackPatternRef callback);
void subscribeGradient(const QString &path, ASLCallbackGradient callback);
void subscribeNewStyleStarted(ASLCallbackNewStyle callback);
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif /* __KIS_ASL_CALLBACK_OBJECT_CATCHER_H */
diff --git a/libs/psd/asl/kis_asl_object_catcher.cpp b/libs/psd/asl/kis_asl_object_catcher.cpp
index d2585f04ff..1b7ef4ecc2 100644
--- a/libs/psd/asl/kis_asl_object_catcher.cpp
+++ b/libs/psd/asl/kis_asl_object_catcher.cpp
@@ -1,97 +1,97 @@
/*
* 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_asl_object_catcher.h"
#include <QString>
#include <QPointF>
#include <QColor>
#include <resources/KoAbstractGradient.h>
#include <kis_debug.h>
KisAslObjectCatcher::KisAslObjectCatcher()
: m_arrayMode(false)
{
}
KisAslObjectCatcher::~KisAslObjectCatcher()
{
}
void KisAslObjectCatcher::addDouble(const QString &path, double value) {
dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "double" << value;
}
void KisAslObjectCatcher::addInteger(const QString &path, int value) {
dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "int" << value;
}
void KisAslObjectCatcher::addEnum(const QString &path, const QString &typeId, const QString &value) {
dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "enum" << ppVar(typeId) << ppVar(value);
}
void KisAslObjectCatcher::addUnitFloat(const QString &path, const QString &unit, double value) {
dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "unitfloat" << ppVar(unit) << ppVar(value);
}
void KisAslObjectCatcher::addText(const QString &path, const QString &value) {
dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "text" << value;
}
void KisAslObjectCatcher::addBoolean(const QString &path, bool value) {
dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "bool" << value;
}
void KisAslObjectCatcher::addColor(const QString &path, const QColor &value) {
dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "color" << value;
}
void KisAslObjectCatcher::addPoint(const QString &path, const QPointF &value) {
dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "point" << value;
}
void KisAslObjectCatcher::addCurve(const QString &path, const QString &name, const QVector<QPointF> &points) {
dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "curve" << name << ppVar(points.size());
}
-void KisAslObjectCatcher::addPattern(const QString &path, const KoPattern *value)
+void KisAslObjectCatcher::addPattern(const QString &path, const KoPatternSP value, const QString& patternUuid)
{
- dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "pattern" << value;
+ dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "pattern" << value << " uuid " << patternUuid;
}
void KisAslObjectCatcher::addPatternRef(const QString &path, const QString &patternUuid, const QString &patternName)
{
dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "pattern-ref" << ppVar(patternUuid) << ppVar(patternName);
}
void KisAslObjectCatcher::addGradient(const QString &path, KoAbstractGradientSP value)
{
dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "gradient" << value;
}
void KisAslObjectCatcher::newStyleStarted()
{
dbgKrita << "Unhandled:" << "new style started";
}
void KisAslObjectCatcher::setArrayMode(bool value) {
m_arrayMode = value;
}
diff --git a/libs/psd/asl/kis_asl_object_catcher.h b/libs/psd/asl/kis_asl_object_catcher.h
index a674c987d7..4353529bc2 100644
--- a/libs/psd/asl/kis_asl_object_catcher.h
+++ b/libs/psd/asl/kis_asl_object_catcher.h
@@ -1,60 +1,62 @@
/*
* 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_ASL_OBJECT_CATCHER_H
#define __KIS_ASL_OBJECT_CATCHER_H
#include <QVector>
-#include "kritapsd_export.h"
+
+#include <KoPattern.h>
class QString;
class QColor;
class QPointF;
-class KoPattern;
class KoAbstractGradient;
+#include "kritapsd_export.h"
+
template<class T> class QSharedPointer;
typedef QSharedPointer<KoAbstractGradient> KoAbstractGradientSP;
class KRITAPSD_EXPORT KisAslObjectCatcher
{
public:
KisAslObjectCatcher();
virtual ~KisAslObjectCatcher();
virtual void addDouble(const QString &path, double value);
virtual void addInteger(const QString &path, int value);
virtual void addEnum(const QString &path, const QString &typeId, const QString &value);
virtual void addUnitFloat(const QString &path, const QString &unit, double value);
virtual void addText(const QString &path, const QString &value);
virtual void addBoolean(const QString &path, bool value);
virtual void addColor(const QString &path, const QColor &value);
virtual void addPoint(const QString &path, const QPointF &value);
virtual void addCurve(const QString &path, const QString &name, const QVector<QPointF> &points);
- virtual void addPattern(const QString &path, const KoPattern *pattern);
+ virtual void addPattern(const QString &path, const KoPatternSP pattern, const QString &patternUuid);
virtual void addPatternRef(const QString &path, const QString &patternUuid, const QString &patternName);
virtual void addGradient(const QString &path, KoAbstractGradientSP gradient);
virtual void newStyleStarted();
void setArrayMode(bool value);
protected:
bool m_arrayMode;
};
#endif /* __KIS_ASL_OBJECT_CATCHER_H */
diff --git a/libs/psd/asl/kis_asl_patterns_writer.cpp b/libs/psd/asl/kis_asl_patterns_writer.cpp
index 4c9a0f8c8a..1c3453a136 100644
--- a/libs/psd/asl/kis_asl_patterns_writer.cpp
+++ b/libs/psd/asl/kis_asl_patterns_writer.cpp
@@ -1,206 +1,207 @@
/*
* 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_asl_patterns_writer.h"
#include <resources/KoPattern.h>
#include <functional>
#include "kis_asl_callback_object_catcher.h"
#include "kis_asl_xml_parser.h"
#include "kis_debug.h"
#include "compression.h"
#include "kis_asl_writer_utils.h"
KisAslPatternsWriter::KisAslPatternsWriter(const QDomDocument &doc, QIODevice *device)
: m_doc(doc),
m_device(device),
m_numPatternsWritten(0)
{
}
void KisAslPatternsWriter::writePatterns()
{
KisAslCallbackObjectCatcher c;
c.subscribePattern("/Patterns/KisPattern", std::bind(&KisAslPatternsWriter::addPattern, this, std::placeholders::_1));
+ c.subscribePattern("/patterns/KisPattern", std::bind(&KisAslPatternsWriter::addPattern, this, std::placeholders::_1));
KisAslXmlParser parser;
parser.parseXML(m_doc, c);
}
void sliceQImage(const QImage &image, QVector<QVector<QByteArray> > *dstPlanes, bool *isCompressed)
{
KIS_ASSERT_RECOVER_NOOP(image.format() == QImage::Format_ARGB32);
QVector<QVector<QByteArray> > uncompressedRows;
QVector<QVector<QByteArray> > compressedRows;
uncompressedRows.resize(3);
compressedRows.resize(3);
int compressedSize = 0;
for (int i = 0; i < 3; i++) {
const int srcRowOffset = 2 - i;
const int srcStep = 4;
const int dstStep = 1;
for (int row = 0; row < image.height(); row++) {
uncompressedRows[i].append(QByteArray(image.width(), '\0'));
quint8 *dstPtr = (quint8*)uncompressedRows[i].last().data();
const quint8 *srcPtr = image.constScanLine(row) + srcRowOffset;
for (int col = 0; col < image.width(); col++) {
*dstPtr = *srcPtr;
srcPtr += srcStep;
dstPtr += dstStep;
}
compressedRows[i].append(Compression::compress(uncompressedRows[i].last(), Compression::RLE));
if (compressedRows[i].last().isEmpty()) {
throw KisAslWriterUtils::ASLWriteException("Failed to compress pattern plane");
}
compressedSize += compressedRows[i].last().size() + 2; // two bytes for offset tag
}
}
if (compressedSize < image.width() * image.height() * 3) {
*dstPlanes = compressedRows;
*isCompressed = true;
} else {
*dstPlanes = uncompressedRows;
*isCompressed = false;
}
}
-void KisAslPatternsWriter::addPattern(const KoPattern *pattern)
+void KisAslPatternsWriter::addPattern(const KoPatternSP pattern)
{
{
KisAslWriterUtils::OffsetStreamPusher<quint32> patternSizeField(m_device);
{
const quint32 patternVersion = 1;
SAFE_WRITE_EX(m_device, patternVersion);
}
{
const quint32 patternImageMode = 3;
SAFE_WRITE_EX(m_device, patternImageMode);
}
{
const quint16 patternHeight = pattern->height();
SAFE_WRITE_EX(m_device, patternHeight);
}
{
const quint16 patternWidth = pattern->width();
SAFE_WRITE_EX(m_device, patternWidth);
}
KisAslWriterUtils::writeUnicodeString(pattern->name(), m_device);
KisAslWriterUtils::writePascalString(KisAslWriterUtils::getPatternUuidLazy(pattern), m_device);
// Write "Virtual Memory Array List"
const QRect patternRect(0, 0, pattern->width(), pattern->height());
{
{
const quint32 arrayVersion = 3;
SAFE_WRITE_EX(m_device, arrayVersion);
}
KisAslWriterUtils::OffsetStreamPusher<quint32> arraySizeField(m_device);
KisAslWriterUtils::writeRect(patternRect, m_device);
{
// don't ask me why it is called this way...
const quint32 numberOfChannels = 24;
SAFE_WRITE_EX(m_device, numberOfChannels);
}
KIS_ASSERT_RECOVER_RETURN(patternRect.size() == pattern->pattern().size());
QVector<QVector<QByteArray> > imagePlanes;
bool isCompressed;
sliceQImage(pattern->pattern(), &imagePlanes, &isCompressed);
for (int i = 0; i < 3; i++) {
{
const quint32 planeIsWritten = 1;
SAFE_WRITE_EX(m_device, planeIsWritten);
}
KisAslWriterUtils::OffsetStreamPusher<quint32> planeSizeField(m_device);
{
const quint32 pixelDepth1 = 8;
SAFE_WRITE_EX(m_device, pixelDepth1);
}
KisAslWriterUtils::writeRect(patternRect, m_device);
{
// why twice? who knows...
const quint16 pixelDepth2 = 8;
SAFE_WRITE_EX(m_device, pixelDepth2);
}
{
// compress with RLE
const quint8 compressionMethod = isCompressed;
SAFE_WRITE_EX(m_device, compressionMethod);
}
KIS_ASSERT_RECOVER_RETURN(imagePlanes[i].size() == pattern->pattern().height());
if (isCompressed) {
Q_FOREACH (const QByteArray &compressedRow, imagePlanes[i]) {
const quint16 compressionRowSize = compressedRow.size();
SAFE_WRITE_EX(m_device, compressionRowSize);
}
}
Q_FOREACH (const QByteArray &rowData, imagePlanes[i]) {
int bytesWritten = m_device->write(rowData);
if (bytesWritten != rowData.size()) {
throw KisAslWriterUtils::ASLWriteException("Failed to write a compressed pattern plane");
}
}
}
}
}
const qint64 currentPos = m_device->pos();
const qint64 alignedPos = KisAslWriterUtils::alignOffsetCeil(currentPos, 4);
if (currentPos != alignedPos) {
m_device->seek(alignedPos);
}
m_numPatternsWritten++;
}
diff --git a/libs/psd/asl/kis_asl_patterns_writer.h b/libs/psd/asl/kis_asl_patterns_writer.h
index ddeda08293..de7b7e3010 100644
--- a/libs/psd/asl/kis_asl_patterns_writer.h
+++ b/libs/psd/asl/kis_asl_patterns_writer.h
@@ -1,47 +1,46 @@
/*
* 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_ASL_PATTERNS_WRITER_H
#define __KIS_ASL_PATTERNS_WRITER_H
#include "kritapsd_export.h"
class QDomDocument;
class QIODevice;
-class KoPattern;
-
+#include <KoPattern.h>
class KRITAPSD_EXPORT KisAslPatternsWriter
{
public:
KisAslPatternsWriter(const QDomDocument &doc, QIODevice *device);
void writePatterns();
private:
- void addPattern(const KoPattern *pattern);
+ void addPattern(const KoPatternSP pattern);
private:
const QDomDocument &m_doc;
QIODevice *m_device;
int m_numPatternsWritten;
};
#endif /* __KIS_ASL_PATTERNS_WRITER_H */
diff --git a/libs/psd/asl/kis_asl_reader.cpp b/libs/psd/asl/kis_asl_reader.cpp
index cfe4d37ff3..8d801f5655 100644
--- a/libs/psd/asl/kis_asl_reader.cpp
+++ b/libs/psd/asl/kis_asl_reader.cpp
@@ -1,658 +1,658 @@
/*
* 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_asl_reader.h"
#include "kis_dom_utils.h"
#include <stdexcept>
#include <string>
#include <QDomDocument>
#include <QIODevice>
#include <QBuffer>
#include "psd_utils.h"
#include "psd.h"
#include "compression.h"
#include "kis_offset_on_exit_verifier.h"
#include "kis_asl_writer_utils.h"
#include "kis_asl_reader_utils.h"
namespace Private {
/**
* Numerical fetch functions
*
* We read numbers and convert them to strings to be able to store
* them in XML.
*/
QString readDoubleAsString(QIODevice *device) {
double value = 0.0;
SAFE_READ_EX(device, value);
return KisDomUtils::toString(value);
}
QString readIntAsString(QIODevice *device) {
quint32 value = 0.0;
SAFE_READ_EX(device, value);
return KisDomUtils::toString(value);
}
QString readBoolAsString(QIODevice *device) {
quint8 value = 0.0;
SAFE_READ_EX(device, value);
return KisDomUtils::toString(value);
}
/**
* XML generation functions
*
* Add a node and fill the corresponding attributes
*/
QDomElement appendXMLNodeCommon(const QString &key, const QString &value, const QString &type, QDomElement *parent, QDomDocument *doc)
{
QDomElement el = doc->createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", type);
el.setAttribute("value", value);
parent->appendChild(el);
return el;
}
QDomElement appendXMLNodeCommonNoValue(const QString &key, const QString &type, QDomElement *parent, QDomDocument *doc)
{
QDomElement el = doc->createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", type);
parent->appendChild(el);
return el;
}
void appendIntegerXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
{
appendXMLNodeCommon(key, value, "Integer", parent, doc);
}
void appendDoubleXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
{
appendXMLNodeCommon(key, value, "Double", parent, doc);
}
void appendTextXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
{
appendXMLNodeCommon(key, value, "Text", parent, doc);
}
void appendPointXMLNode(const QString &key, const QPointF &pt, QDomElement *parent, QDomDocument *doc)
{
QDomElement el = appendXMLNodeCommonNoValue(key, "Descriptor", parent, doc);
el.setAttribute("classId", "CrPt");
el.setAttribute("name", "");
appendDoubleXMLNode("Hrzn", KisDomUtils::toString(pt.x()), &el, doc);
appendDoubleXMLNode("Vrtc", KisDomUtils::toString(pt.x()), &el, doc);
}
/**
* ASL -> XML parsing functions
*/
void readDescriptor(QIODevice *device,
const QString &key,
QDomElement *parent,
QDomDocument *doc);
void readChildObject(QIODevice *device,
QDomElement *parent,
QDomDocument *doc,
bool skipKey = false)
{
using namespace KisAslReaderUtils;
QString key;
if (!skipKey) {
key = readVarString(device);
}
QString OSType = readFixedString(device);
//dbgKrita << "Child" << ppVar(key) << ppVar(OSType);
if (OSType == "obj ") {
throw KisAslReaderUtils::ASLParseException("OSType 'obj' not implemented");
} else if (OSType == "Objc" || OSType == "GlbO") {
readDescriptor(device, key, parent, doc);
} else if (OSType == "VlLs") {
quint32 numItems = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, numItems);
QDomElement el = appendXMLNodeCommonNoValue(key, "List", parent, doc);
for (quint32 i = 0; i < numItems; i++) {
readChildObject(device, &el, doc, true);
}
} else if (OSType == "doub") {
appendDoubleXMLNode(key, readDoubleAsString(device), parent, doc);
} else if (OSType == "UntF") {
const QString unit = readFixedString(device);
const QString value = readDoubleAsString(device);
QDomElement el = appendXMLNodeCommon(key, value, "UnitFloat", parent, doc);
el.setAttribute("unit", unit);
} else if (OSType == "TEXT") {
QString unicodeString = readUnicodeString(device);
appendTextXMLNode(key, unicodeString, parent, doc);
} else if (OSType == "enum") {
const QString typeId = readVarString(device);
const QString value = readVarString(device);
QDomElement el = appendXMLNodeCommon(key, value, "Enum", parent, doc);
el.setAttribute("typeId", typeId);
} else if (OSType == "long") {
appendIntegerXMLNode(key, readIntAsString(device), parent, doc);
} else if (OSType == "bool") {
const QString value = readBoolAsString(device);
appendXMLNodeCommon(key, value, "Boolean", parent, doc);
} else if (OSType == "type") {
throw KisAslReaderUtils::ASLParseException("OSType 'type' not implemented");
} else if (OSType == "GlbC") {
throw KisAslReaderUtils::ASLParseException("OSType 'GlbC' not implemented");
} else if (OSType == "alis") {
throw KisAslReaderUtils::ASLParseException("OSType 'alis' not implemented");
} else if (OSType == "tdta") {
throw KisAslReaderUtils::ASLParseException("OSType 'tdta' not implemented");
}
}
void readDescriptor(QIODevice *device,
const QString &key,
QDomElement *parent,
QDomDocument *doc)
{
using namespace KisAslReaderUtils;
QString name = readUnicodeString(device);
QString classId = readVarString(device);
quint32 numChildren = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, numChildren);
QDomElement el = appendXMLNodeCommonNoValue(key, "Descriptor", parent, doc);
el.setAttribute("classId", classId);
el.setAttribute("name", name);
//dbgKrita << "Descriptor" << ppVar(key) << ppVar(classId) << ppVar(numChildren);
for (quint32 i = 0; i < numChildren; i++) {
readChildObject(device, &el, doc);
}
}
QImage readVirtualArrayList(QIODevice *device,
int numPlanes)
{
using namespace KisAslReaderUtils;
quint32 arrayVersion = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, arrayVersion);
if (arrayVersion != 3) {
throw ASLParseException("VAList version is not '3'!");
}
quint32 arrayLength = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, arrayLength);
SETUP_OFFSET_VERIFIER(vaEndVerifier, device, arrayLength, 100);
quint32 x0, y0, x1, y1;
SAFE_READ_EX(device, y0);
SAFE_READ_EX(device, x0);
SAFE_READ_EX(device, y1);
SAFE_READ_EX(device, x1);
QRect arrayRect(x0, y0, x1 - x0, y1 - y0);
quint32 numberOfChannels = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, numberOfChannels);
if (numberOfChannels != 24) {
throw ASLParseException("VAList: Krita doesn't support ASL files with 'numberOfChannels' flag not equal to 24 (it is not documented)!");
}
// dbgKrita << ppVar(arrayVersion);
// dbgKrita << ppVar(arrayLength);
// dbgKrita << ppVar(arrayRect);
// dbgKrita << ppVar(numberOfChannels);
if (numPlanes != 1 && numPlanes != 3) {
throw ASLParseException("VAList: unsupported number of planes!");
}
QVector<QByteArray> dataPlanes;
dataPlanes.resize(3);
for (int i = 0; i < numPlanes; i++) {
quint32 arrayWritten = GARBAGE_VALUE_MARK;
if (!psdread(device, &arrayWritten) || !arrayWritten) {
throw ASLParseException("VAList plane has not-written flag set!");
}
quint32 arrayPlaneLength = GARBAGE_VALUE_MARK;
if (!psdread(device, &arrayPlaneLength) || !arrayPlaneLength) {
throw ASLParseException("VAList has plane length set to zero!");
}
SETUP_OFFSET_VERIFIER(planeEndVerifier, device, arrayPlaneLength, 0);
qint64 nextPos = device->pos() + arrayPlaneLength;
quint32 pixelDepth1 = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, pixelDepth1);
quint32 x0, y0, x1, y1;
SAFE_READ_EX(device, y0);
SAFE_READ_EX(device, x0);
SAFE_READ_EX(device, y1);
SAFE_READ_EX(device, x1);
QRect planeRect(x0, y0, x1 - x0, y1 - y0);
if (planeRect != arrayRect) {
throw ASLParseException("VAList: planes are not uniform. Not supported yet!");
}
quint16 pixelDepth2 = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, pixelDepth2);
quint8 useCompression = 9;
SAFE_READ_EX(device, useCompression);
// dbgKrita << "plane index:" << ppVar(i);
// dbgKrita << ppVar(arrayWritten);
// dbgKrita << ppVar(arrayPlaneLength);
// dbgKrita << ppVar(pixelDepth1);
// dbgKrita << ppVar(planeRect);
// dbgKrita << ppVar(pixelDepth2);
// dbgKrita << ppVar(useCompression);
if (pixelDepth1 != pixelDepth2) {
throw ASLParseException("VAList: two pixel depths of the plane are not equal (it is not documented)!");
}
if (pixelDepth1 != 8) {
throw ASLParseException("VAList: supported pixel depth of the plane in 8 only!");
}
const int dataLength = planeRect.width() * planeRect.height();
if (useCompression == Compression::Uncompressed) {
dataPlanes[i] = device->read(dataLength);
} else if (useCompression == Compression::RLE) {
const int numRows = planeRect.height();
QVector<quint16> rowSizes;
rowSizes.resize(numRows);
for (int row = 0; row < numRows; row++) {
quint16 rowSize = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, rowSize);
rowSizes[row] = rowSize;
}
for (int row = 0; row < numRows; row++) {
const quint16 rowSize = rowSizes[row];
QByteArray compressedData = device->read(rowSize);
if (compressedData.size() != rowSize) {
throw ASLParseException("VAList: failed to read compressed data!");
}
QByteArray uncompressedData = Compression::uncompress(planeRect.width(), compressedData, Compression::RLE);
if (uncompressedData.size() != planeRect.width()) {
throw ASLParseException("VAList: failed to decompress data!");
}
dataPlanes[i].append(uncompressedData);
}
} else {
throw ASLParseException("VAList: ZIP compression is not implemented yet!");
}
if (dataPlanes[i].size() != dataLength) {
throw ASLParseException("VAList: failed to read/uncompress data plane!");
}
device->seek(nextPos);
}
QImage image(arrayRect.size(), QImage::Format_ARGB32);
const int dataLength = arrayRect.width() * arrayRect.height();
quint8 *dstPtr = image.bits();
for (int i = 0; i < dataLength; i++) {
for (int j = 2; j >= 0; j--) {
int plane = qMin(numPlanes, j);
*dstPtr++ = dataPlanes[plane][i];
}
*dstPtr++ = 0xFF;
}
#if 0
static int i = -1; i++;
QString filename = QString("pattern_image_%1.png").arg(i);
dbgKrita << "### dumping pattern image" << ppVar(filename);
image.save(filename);
#endif
return image;
}
qint64 readPattern(QIODevice *device,
QDomElement *parent,
QDomDocument *doc)
{
using namespace KisAslReaderUtils;
quint32 patternSize = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, patternSize);
// patterns are always aligned by 4 bytes
patternSize = KisAslWriterUtils::alignOffsetCeil(patternSize, 4);
SETUP_OFFSET_VERIFIER(patternEndVerifier, device, patternSize, 0);
quint32 patternVersion = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, patternVersion);
if (patternVersion != 1) {
throw ASLParseException("Pattern version is not \'1\'");
}
quint32 patternImageMode = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, patternImageMode);
quint16 patternHeight = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, patternHeight);
quint16 patternWidth = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, patternWidth);
QString patternName;
psdread_unicodestring(device, patternName);
QString patternUuid = readPascalString(device);
// dbgKrita << "--";
// dbgKrita << ppVar(patternSize);
// dbgKrita << ppVar(patternImageMode);
// dbgKrita << ppVar(patternHeight);
// dbgKrita << ppVar(patternWidth);
// dbgKrita << ppVar(patternName);
// dbgKrita << ppVar(patternUuid);
int numPlanes = 0;
psd_color_mode mode = static_cast<psd_color_mode>(patternImageMode);
switch (mode) {
case MultiChannel:
case Grayscale:
numPlanes = 1;
break;
case RGB:
numPlanes = 3;
break;
default: {
QString msg = QString("Unsupported image mode: %1!").arg(mode);
throw ASLParseException(msg);
}
}
/**
* Create XML data
*/
QDomElement pat = doc->createElement("node");
pat.setAttribute("classId", "KisPattern");
pat.setAttribute("type", "Descriptor");
pat.setAttribute("name", "");
QBuffer patternBuf;
patternBuf.open(QIODevice::WriteOnly);
{ // ensure we don't keep resources for too long
QString fileName = QString("%1.pat").arg(patternUuid);
QImage patternImage = readVirtualArrayList(device, numPlanes);
KoPattern realPattern(patternImage, patternName, fileName);
realPattern.savePatToDevice(&patternBuf);
}
/**
* We are loading the pattern and convert it into ARGB right away,
* so we need not store real image mode and size of the pattern
* externally.
*/
appendTextXMLNode("Nm ", patternName, &pat, doc);
appendTextXMLNode("Idnt", patternUuid, &pat, doc);
QDomCDATASection dataSection = doc->createCDATASection(qCompress(patternBuf.buffer()).toBase64());
QDomElement dataElement = doc->createElement("node");
dataElement.setAttribute("type", "KisPatternData");
dataElement.setAttribute("key", "Data");
dataElement.appendChild(dataSection);
pat.appendChild(dataElement);
parent->appendChild(pat);
return sizeof(patternSize) + patternSize;
}
QDomDocument readFileImpl(QIODevice *device)
{
using namespace KisAslReaderUtils;
QDomDocument doc;
QDomElement root = doc.createElement("asl");
doc.appendChild(root);
{
quint16 stylesVersion = GARBAGE_VALUE_MARK;
SAFE_READ_SIGNATURE_EX(device, stylesVersion, 2);
}
{
quint32 aslSignature = GARBAGE_VALUE_MARK;
const quint32 refSignature = 0x3842534c; // '8BSL' in little-endian
SAFE_READ_SIGNATURE_EX(device, aslSignature, refSignature);
}
{
quint16 patternsVersion = GARBAGE_VALUE_MARK;
SAFE_READ_SIGNATURE_EX(device, patternsVersion, 3);
}
// Patterns
{
quint32 patternsSize = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, patternsSize);
if (patternsSize > 0) {
SETUP_OFFSET_VERIFIER(patternsSectionVerifier, device, patternsSize, 0);
QDomElement patternsRoot = doc.createElement("node");
patternsRoot.setAttribute("type", "List");
- patternsRoot.setAttribute("key", "Patterns");
+ patternsRoot.setAttribute("key", ResourceType::Patterns);
root.appendChild(patternsRoot);
try {
qint64 bytesRead = 0;
while (bytesRead < patternsSize) {
qint64 chunk = readPattern(device, &patternsRoot, &doc);
bytesRead += chunk;
}
} catch (ASLParseException &e) {
warnKrita << "WARNING: ASL (emb. pattern):" << e.what();
}
}
}
// Styles
quint32 numStyles = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, numStyles);
for (int i = 0; i < (int)numStyles; i++) {
quint32 bytesToRead = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, bytesToRead);
SETUP_OFFSET_VERIFIER(singleStyleSectionVerifier, device, bytesToRead, 0);
{
quint32 stylesFormatVersion = GARBAGE_VALUE_MARK;
SAFE_READ_SIGNATURE_EX(device, stylesFormatVersion, 16);
}
readDescriptor(device, "", &root, &doc);
{
quint32 stylesFormatVersion = GARBAGE_VALUE_MARK;
SAFE_READ_SIGNATURE_EX(device, stylesFormatVersion, 16);
}
readDescriptor(device, "", &root, &doc);
}
return doc;
}
} // namespace
QDomDocument KisAslReader::readFile(QIODevice *device)
{
QDomDocument doc;
if (device->isSequential()) {
warnKrita << "WARNING: *** KisAslReader::readFile: the supplied"
<< "IO device is sequential. Chances are that"
<< "the layer style will *not* be loaded correctly!";
}
try {
doc = Private::readFileImpl(device);
} catch (KisAslReaderUtils::ASLParseException &e) {
warnKrita << "WARNING: ASL:" << e.what();
}
return doc;
}
QDomDocument KisAslReader::readLfx2PsdSection(QIODevice *device)
{
QDomDocument doc;
if (device->isSequential()) {
warnKrita << "WARNING: *** KisAslReader::readLfx2PsdSection: the supplied"
<< "IO device is sequential. Chances are that"
<< "the layer style will *not* be loaded correctly!";
}
try {
{
quint32 objectEffectsVersion = GARBAGE_VALUE_MARK;
const quint32 ref = 0x00;
SAFE_READ_SIGNATURE_EX(device, objectEffectsVersion, ref);
}
{
quint32 descriptorVersion = GARBAGE_VALUE_MARK;
const quint32 ref = 0x10;
SAFE_READ_SIGNATURE_EX(device, descriptorVersion, ref);
}
QDomElement root = doc.createElement("asl");
doc.appendChild(root);
Private::readDescriptor(device, "", &root, &doc);
} catch (KisAslReaderUtils::ASLParseException &e) {
warnKrita << "WARNING: PSD: lfx2 section:" << e.what();
}
return doc;
}
QDomDocument KisAslReader::readPsdSectionPattern(QIODevice *device, qint64 bytesLeft)
{
QDomDocument doc;
QDomElement root = doc.createElement("asl");
doc.appendChild(root);
QDomElement pat = doc.createElement("node");
root.appendChild(pat);
- pat.setAttribute("classId", "Patterns");
+ pat.setAttribute("classId", ResourceType::Patterns);
pat.setAttribute("type", "Descriptor");
pat.setAttribute("name", "");
try {
qint64 bytesRead = 0;
while (bytesRead < bytesLeft) {
qint64 chunk = Private::readPattern(device, &pat, &doc);
bytesRead += chunk;
}
} catch (KisAslReaderUtils::ASLParseException &e) {
warnKrita << "WARNING: PSD (emb. pattern):" << e.what();
}
return doc;
}
diff --git a/libs/psd/asl/kis_asl_writer.cpp b/libs/psd/asl/kis_asl_writer.cpp
index 3975c8154e..19ec199d79 100644
--- a/libs/psd/asl/kis_asl_writer.cpp
+++ b/libs/psd/asl/kis_asl_writer.cpp
@@ -1,278 +1,278 @@
/*
* 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_asl_writer.h"
#include <QDomDocument>
#include <QIODevice>
#include "kis_dom_utils.h"
#include "kis_debug.h"
#include "psd_utils.h"
#include "kis_asl_patterns_writer.h"
#include "kis_asl_writer_utils.h"
namespace Private {
using namespace KisAslWriterUtils;
void parseElement(const QDomElement &el, QIODevice *device, bool forceTypeInfo = false)
{
KIS_ASSERT_RECOVER_RETURN(el.tagName() == "node");
QString type = el.attribute("type", "<unknown>");
QString key = el.attribute("key", "");
// should be filtered on a higher level
- KIS_ASSERT_RECOVER_RETURN(key != "Patterns");
+ KIS_ASSERT_RECOVER_RETURN(key != ResourceType::Patterns);
if (type == "Descriptor") {
if (!key.isEmpty()) {
writeVarString(key, device);
}
if (!key.isEmpty() || forceTypeInfo) {
writeFixedString("Objc", device);
}
QString classId = el.attribute("classId", "");
QString name = el.attribute("name", "");
writeUnicodeString(name, device);
writeVarString(classId, device);
quint32 numChildren = el.childNodes().size();
SAFE_WRITE_EX(device, numChildren);
QDomNode child = el.firstChild();
while (!child.isNull()) {
parseElement(child.toElement(), device);
child = child.nextSibling();
}
} else if (type == "List") {
writeVarString(key, device);
writeFixedString("VlLs", device);
quint32 numChildren = el.childNodes().size();
SAFE_WRITE_EX(device, numChildren);
QDomNode child = el.firstChild();
while (!child.isNull()) {
parseElement(child.toElement(), device, true);
child = child.nextSibling();
}
} else if (type == "Double") {
double v = KisDomUtils::toDouble(el.attribute("value", "0"));
writeVarString(key, device);
writeFixedString("doub", device);
SAFE_WRITE_EX(device, v);
} else if (type == "UnitFloat") {
double v = KisDomUtils::toDouble(el.attribute("value", "0"));
QString unit = el.attribute("unit", "#Pxl");
writeVarString(key, device);
writeFixedString("UntF", device);
writeFixedString(unit, device);
SAFE_WRITE_EX(device, v);
} else if (type == "Text") {
QString v = el.attribute("value", "");
writeVarString(key, device);
writeFixedString("TEXT", device);
writeUnicodeString(v, device);
} else if (type == "Enum") {
QString v = el.attribute("value", "");
QString typeId = el.attribute("typeId", "DEAD");
writeVarString(key, device);
writeFixedString("enum", device);
writeVarString(typeId, device);
writeVarString(v, device);
} else if (type == "Integer") {
quint32 v = KisDomUtils::toInt(el.attribute("value", "0"));
writeVarString(key, device);
writeFixedString("long", device);
SAFE_WRITE_EX(device, v);
} else if (type == "Boolean") {
quint8 v = KisDomUtils::toInt(el.attribute("value", "0"));
writeVarString(key, device);
writeFixedString("bool", device);
SAFE_WRITE_EX(device, v);
} else {
warnKrita << "WARNING: XML (ASL) Unknown element type:" << type << ppVar(key);
}
}
int calculateNumStyles(const QDomElement &root)
{
int numStyles = 0;
QDomNode child = root.firstChild();
while (!child.isNull()) {
QDomElement el = child.toElement();
QString classId = el.attribute("classId", "");
if (classId == "null") {
numStyles++;
}
child = child.nextSibling();
}
return numStyles;
}
void writeFileImpl(QIODevice *device, const QDomDocument &doc)
{
{
quint16 stylesVersion = 2;
SAFE_WRITE_EX(device, stylesVersion);
}
{
QString signature("8BSL");
if (!device->write(signature.toLatin1().data(), 4)) {
throw ASLWriteException("Failed to write ASL signature");
}
}
{
quint16 patternsVersion = 3;
SAFE_WRITE_EX(device, patternsVersion);
}
{
KisAslWriterUtils::OffsetStreamPusher<quint32> patternsSizeField(device);
KisAslPatternsWriter patternsWriter(doc, device);
patternsWriter.writePatterns();
}
QDomElement root = doc.documentElement();
KIS_ASSERT_RECOVER_RETURN(root.tagName() == "asl");
int numStyles = calculateNumStyles(root);
KIS_ASSERT_RECOVER_RETURN(numStyles > 0);
{
quint32 numStylesTag = numStyles;
SAFE_WRITE_EX(device, numStylesTag);
}
QDomNode child = root.firstChild();
for (int styleIndex = 0; styleIndex < numStyles; styleIndex++) {
KisAslWriterUtils::OffsetStreamPusher<quint32> theOnlyStyleSizeField(device);
KIS_ASSERT_RECOVER_RETURN(!child.isNull());
{
quint32 stylesFormatVersion = 16;
SAFE_WRITE_EX(device, stylesFormatVersion);
}
while (!child.isNull()) {
QDomElement el = child.toElement();
QString key = el.attribute("key", "");
- if (key != "Patterns") break;
+ if (key != ResourceType::Patterns) break;
child = child.nextSibling();
}
parseElement(child.toElement(), device);
child = child.nextSibling();
{
quint32 stylesFormatVersion = 16;
SAFE_WRITE_EX(device, stylesFormatVersion);
}
parseElement(child.toElement(), device);
child = child.nextSibling();
// ASL files' size should be 4-bytes aligned
const qint64 paddingSize = 4 - (device->pos() & 0x3);
if (paddingSize != 4) {
QByteArray padding(paddingSize, '\0');
device->write(padding);
}
}
}
void writePsdLfx2SectionImpl(QIODevice *device, const QDomDocument &doc)
{
QDomElement root = doc.documentElement();
KIS_ASSERT_RECOVER_RETURN(root.tagName() == "asl");
int numStyles = calculateNumStyles(root);
KIS_ASSERT_RECOVER_RETURN(numStyles == 1);
{
quint32 objectEffectsVersion = 0;
SAFE_WRITE_EX(device, objectEffectsVersion);
}
{
quint32 descriptorVersion = 16;
SAFE_WRITE_EX(device, descriptorVersion);
}
QDomNode child = root.firstChild();
while (!child.isNull()) {
QDomElement el = child.toElement();
QString key = el.attribute("key", "");
- if (key != "Patterns") break;
+ if (key != ResourceType::Patterns) break;
child = child.nextSibling();
}
parseElement(child.toElement(), device);
child = child.nextSibling();
// ASL files' size should be 4-bytes aligned
const qint64 paddingSize = 4 - (device->pos() & 0x3);
if (paddingSize != 4) {
QByteArray padding(paddingSize, '\0');
device->write(padding);
}
}
} // namespace
void KisAslWriter::writeFile(QIODevice *device, const QDomDocument &doc)
{
try {
Private::writeFileImpl(device, doc);
} catch (Private::ASLWriteException &e) {
warnKrita << "WARNING: ASL:" << e.what();
}
}
void KisAslWriter::writePsdLfx2SectionEx(QIODevice *device, const QDomDocument &doc)
{
Private::writePsdLfx2SectionImpl(device, doc);
}
diff --git a/libs/psd/asl/kis_asl_writer_utils.cpp b/libs/psd/asl/kis_asl_writer_utils.cpp
index 806314586f..82e99f861c 100644
--- a/libs/psd/asl/kis_asl_writer_utils.cpp
+++ b/libs/psd/asl/kis_asl_writer_utils.cpp
@@ -1,111 +1,111 @@
/*
* 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_asl_writer_utils.h"
#include <QUuid>
#include <resources/KoPattern.h>
namespace KisAslWriterUtils {
void writeRect(const QRect &rect, QIODevice *device)
{
{
const quint32 rectY0 = rect.y();
SAFE_WRITE_EX(device, rectY0);
}
{
const quint32 rectX0 = rect.x();
SAFE_WRITE_EX(device, rectX0);
}
{
const quint32 rectY1 = rect.y() + rect.height();
SAFE_WRITE_EX(device, rectY1);
}
{
const quint32 rectX1 = rect.x() + rect.width();
SAFE_WRITE_EX(device, rectX1);
}
}
void writeUnicodeString(const QString &value, QIODevice *device)
{
quint32 len = value.length() + 1;
SAFE_WRITE_EX(device, len);
const quint16 *ptr = value.utf16();
for (quint32 i = 0; i < len; i++) {
SAFE_WRITE_EX(device, ptr[i]);
}
}
void writeVarString(const QString &value, QIODevice *device)
{
quint32 lenTag = value.length() != 4 ? value.length() : 0;
SAFE_WRITE_EX(device, lenTag);
if (!device->write(value.toLatin1().data(), value.length())) {
warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
return;
}
}
void writePascalString(const QString &value, QIODevice *device)
{
quint8 lenTag = value.length();
SAFE_WRITE_EX(device, lenTag);
if (!device->write(value.toLatin1().data(), value.length())) {
warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
return;
}
}
void writeFixedString(const QString &value, QIODevice *device)
{
KIS_ASSERT_RECOVER_RETURN(value.length() == 4);
if (!device->write(value.toLatin1().data(), value.length())) {
warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
return;
}
}
// Write UUID fetched from the file name or generate
-QString getPatternUuidLazy(const KoPattern *pattern)
+QString getPatternUuidLazy(const KoPatternSP pattern)
{
QUuid uuid;
QString patternFileName = pattern->filename();
if (patternFileName.endsWith(".pat", Qt::CaseInsensitive)) {
QString strUuid = patternFileName.left(patternFileName.size() - 4);
uuid = QUuid(strUuid);
}
if (uuid.isNull()) {
warnKrita << "WARNING: Saved pattern doesn't have a UUID, generating...";
warnKrita << ppVar(patternFileName) << ppVar(pattern->name());
uuid = QUuid::createUuid();
}
return uuid.toString().mid(1, 36);
}
}
diff --git a/libs/psd/asl/kis_asl_writer_utils.h b/libs/psd/asl/kis_asl_writer_utils.h
index bd1c68dab7..2a975ee3f8 100644
--- a/libs/psd/asl/kis_asl_writer_utils.h
+++ b/libs/psd/asl/kis_asl_writer_utils.h
@@ -1,137 +1,137 @@
/*
* 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_ASL_WRITER_UTILS_H
#define __KIS_ASL_WRITER_UTILS_H
#include <stdexcept>
#include <string>
#include <QIODevice>
#include "psd_utils.h"
#include "kis_debug.h"
#include "kritapsd_export.h"
namespace KisAslWriterUtils {
/**
* Exception that is emitted when any write error appear.
*/
struct KRITAPSD_EXPORT ASLWriteException : public std::runtime_error
{
ASLWriteException(const QString &msg)
: std::runtime_error(msg.toLatin1().data())
{
}
};
}
#define SAFE_WRITE_EX(device, varname) \
if (!psdwrite(device, varname)) { \
QString msg = QString("Failed to write \'%1\' tag!").arg(#varname); \
throw KisAslWriterUtils::ASLWriteException(msg); \
}
namespace KisAslWriterUtils {
KRITAPSD_EXPORT void writeRect(const QRect &rect, QIODevice *device);
KRITAPSD_EXPORT void writeUnicodeString(const QString &value, QIODevice *device);
KRITAPSD_EXPORT void writeVarString(const QString &value, QIODevice *device);
KRITAPSD_EXPORT void writePascalString(const QString &value, QIODevice *device);
KRITAPSD_EXPORT void writeFixedString(const QString &value, QIODevice *device);
-KRITAPSD_EXPORT QString getPatternUuidLazy(const KoPattern *pattern);
+KRITAPSD_EXPORT QString getPatternUuidLazy(const KoPatternSP pattern);
/**
* Align the pointer \p pos by alignment. Grow the pointer
* if needed.
*
* \return the lowest integer not smaller than \p pos that divides by
* alignment
*/
inline qint64 alignOffsetCeil(qint64 pos, qint64 alignment)
{
qint64 mask = alignment - 1;
return (pos + mask) & ~mask;
}
template <class OffsetType>
class OffsetStreamPusher
{
public:
OffsetStreamPusher(QIODevice *device, qint64 alignOnExit = 0, qint64 externalSizeTagOffset = -1)
: m_device(device),
m_alignOnExit(alignOnExit),
m_externalSizeTagOffset(externalSizeTagOffset)
{
m_chunkStartPos = m_device->pos();
if (externalSizeTagOffset < 0) {
const OffsetType fakeObjectSize = OffsetType(0xdeadbeef);
SAFE_WRITE_EX(m_device, fakeObjectSize);
}
}
~OffsetStreamPusher() {
try {
if (m_alignOnExit) {
qint64 currentPos = m_device->pos();
const qint64 alignedPos = alignOffsetCeil(currentPos, m_alignOnExit);
for (; currentPos < alignedPos; currentPos++) {
quint8 padding = 0;
SAFE_WRITE_EX(m_device, padding);
}
}
const qint64 currentPos = m_device->pos();
qint64 writtenDataSize = 0;
qint64 sizeFiledOffset = 0;
if (m_externalSizeTagOffset >= 0) {
writtenDataSize = currentPos - m_chunkStartPos;
sizeFiledOffset = m_externalSizeTagOffset;
} else {
writtenDataSize = currentPos - m_chunkStartPos - sizeof(OffsetType);
sizeFiledOffset = m_chunkStartPos;
}
m_device->seek(sizeFiledOffset);
const OffsetType realObjectSize = writtenDataSize;
SAFE_WRITE_EX(m_device, realObjectSize);
m_device->seek(currentPos);
}
catch(ASLWriteException &e) {
warnKrita << PREPEND_METHOD(e.what());
}
}
private:
qint64 m_chunkStartPos;
QIODevice *m_device;
qint64 m_alignOnExit;
qint64 m_externalSizeTagOffset;
};
}
#endif /* __KIS_ASL_WRITER_UTILS_H */
diff --git a/libs/psd/asl/kis_asl_xml_parser.cpp b/libs/psd/asl/kis_asl_xml_parser.cpp
index 9b208b3672..47c35575e5 100644
--- a/libs/psd/asl/kis_asl_xml_parser.cpp
+++ b/libs/psd/asl/kis_asl_xml_parser.cpp
@@ -1,556 +1,558 @@
/*
* 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_asl_xml_parser.h"
#include <boost/function.hpp>
#include <stdexcept>
#include <string>
#include <QDomDocument>
#include <QIODevice>
#include <QBuffer>
#include <QColor>
#include <QHash>
#include <KoColorSpaceRegistry.h>
#include <resources/KoSegmentGradient.h>
#include "kis_dom_utils.h"
#include "kis_debug.h"
#include "psd_utils.h"
#include "psd.h"
#include "compression.h"
#include "kis_asl_object_catcher.h"
namespace Private {
void parseElement(const QDomElement &el,
const QString &parentPath,
KisAslObjectCatcher &catcher);
class CurveObjectCatcher : public KisAslObjectCatcher
{
public:
void addText(const QString &path, const QString &value) override {
if (path == "/Nm ") {
m_name = value;
} else {
warnKrita << "XML (ASL): failed to parse curve object" << path << value;
}
}
void addPoint(const QString &path, const QPointF &value) override {
if (!m_arrayMode) {
warnKrita << "XML (ASL): failed to parse curve object (array fault)" << path << value << ppVar(m_arrayMode);
}
m_points.append(value);
}
public:
QVector<QPointF> m_points;
QString m_name;
};
QColor parseRGBColorObject(QDomElement parent)
{
QColor color(Qt::black);
QDomNode child = parent.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
if (type != "Double") {
warnKrita << "Unknown color component type:" << ppVar(type) << ppVar(key);
return Qt::red;
}
double value = KisDomUtils::toDouble(childEl.attribute("value", "0"));
if (key == "Rd ") {
color.setRed(value);
} else if (key == "Grn ") {
color.setGreen(value);
} else if (key == "Bl ") {
color.setBlue(value);
} else {
warnKrita << "Unknown color key value:" << ppVar(key);
return Qt::red;
}
child = child.nextSibling();
}
return color;
}
void parseColorStopsList(QDomElement parent,
QVector<qreal> &startLocations,
QVector<qreal> &middleOffsets,
QVector<QColor> &colors)
{
QDomNode child = parent.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
QString classId = childEl.attribute("classId", "");
if (type == "Descriptor" && classId == "Clrt") {
// sorry for naming...
QDomNode child = childEl.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
QString classId = childEl.attribute("classId", "");
if (type == "Integer" && key == "Lctn") {
int value = KisDomUtils::toInt(childEl.attribute("value", "0"));
startLocations.append(qreal(value) / 4096.0);
} else if (type == "Integer" && key == "Mdpn") {
int value = KisDomUtils::toInt(childEl.attribute("value", "0"));
middleOffsets.append(qreal(value) / 100.0);
} else if (type == "Descriptor" && key == "Clr ") {
colors.append(parseRGBColorObject(childEl));
} else if (type == "Enum" && key == "Type") {
QString typeId = childEl.attribute("typeId", "");
if (typeId != "Clry") {
warnKrita << "WARNING: Invalid typeId of a gradient stop type" << typeId;
}
QString value = childEl.attribute("value", "");
if (value == "BckC" || value == "FrgC") {
warnKrita << "WARNING: Using foreground/background colors in ASL gradients is not yet supported";
}
}
child = child.nextSibling();
}
} else {
warnKrita << "WARNING: Unrecognized object in color stops list" << ppVar(type) << ppVar(key) << ppVar(classId);
}
child = child.nextSibling();
}
}
void parseTransparencyStopsList(QDomElement parent,
QVector<qreal> &startLocations,
QVector<qreal> &middleOffsets,
QVector<qreal> &transparencies)
{
QDomNode child = parent.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
QString classId = childEl.attribute("classId", "");
if (type == "Descriptor" && classId == "TrnS") {
// sorry for naming again...
QDomNode child = childEl.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
if (type == "Integer" && key == "Lctn") {
int value = KisDomUtils::toInt(childEl.attribute("value", "0"));
startLocations.append(qreal(value) / 4096.0);
} else if (type == "Integer" && key == "Mdpn") {
int value = KisDomUtils::toInt(childEl.attribute("value", "0"));
middleOffsets.append(qreal(value) / 100.0);
} else if (type == "UnitFloat" && key == "Opct") {
QString unit = childEl.attribute("unit", "");
if (unit != "#Prc") {
warnKrita << "WARNING: Invalid unit of a gradient stop transparency" << unit;
}
qreal value = KisDomUtils::toDouble(childEl.attribute("value", "100"));
transparencies.append(value / 100.0);
}
child = child.nextSibling();
}
} else {
warnKrita << "WARNING: Unrecognized object in transparency stops list" << ppVar(type) << ppVar(key) << ppVar(classId);
}
child = child.nextSibling();
}
}
inline QString buildPath(const QString &parent, const QString &key) {
return parent + "/" + key;
}
bool tryParseDescriptor(const QDomElement &el,
const QString &path,
const QString &classId,
KisAslObjectCatcher &catcher)
{
bool retval = true;
if (classId == "null") {
catcher.newStyleStarted();
// here we just notify that a new style is started, we haven't
// processed the whole block yet, so return false.
retval = false;
} else if (classId == "RGBC") {
catcher.addColor(path, parseRGBColorObject(el));
} else if (classId == "ShpC") {
CurveObjectCatcher curveCatcher;
QDomNode child = el.firstChild();
while (!child.isNull()) {
parseElement(child.toElement(), "", curveCatcher);
child = child.nextSibling();
}
catcher.addCurve(path, curveCatcher.m_name, curveCatcher.m_points);
} else if (classId == "CrPt") {
QPointF point;
QDomNode child = el.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
if (type == "Boolean" && key == "Cnty") {
warnKrita << "WARNING: tryParseDescriptor: The points of the curve object contain \'Cnty\' flag which is unsupported by Krita";
warnKrita << " " << ppVar(type) << ppVar(key) << ppVar(path);
child = child.nextSibling();
continue;
}
if (type != "Double") {
warnKrita << "Unknown point component type:" << ppVar(type) << ppVar(key) << ppVar(path);
return false;
}
double value = KisDomUtils::toDouble(childEl.attribute("value", "0"));
if (key == "Hrzn") {
point.setX(value);
} else if (key == "Vrtc") {
point.setY(value);
} else {
warnKrita << "Unknown point key value:" << ppVar(key) << ppVar(path);
return false;
}
child = child.nextSibling();
}
catcher.addPoint(path, point);
} else if (classId == "Pnt ") {
QPointF point;
QDomNode child = el.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
QString unit = childEl.attribute("unit", "");
if (type != "Double" && !(type == "UnitFloat" && unit == "#Prc")) {
warnKrita << "Unknown point component type:" << ppVar(unit) << ppVar(type) << ppVar(key) << ppVar(path);
return false;
}
double value = KisDomUtils::toDouble(childEl.attribute("value", "0"));
if (key == "Hrzn") {
point.setX(value);
} else if (key == "Vrtc") {
point.setY(value);
} else {
warnKrita << "Unknown point key value:" << ppVar(key) << ppVar(path);
return false;
}
child = child.nextSibling();
}
catcher.addPoint(path, point);
} else if (classId == "KisPattern") {
QByteArray patternData;
QString patternUuid;
QDomNode child = el.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
if (type == "Text" && key == "Idnt") {
patternUuid = childEl.attribute("value", "");
}
if (type == "KisPatternData" && key == "Data") {
QDomNode dataNode = child.firstChild();
if (!dataNode.isCDATASection()) {
warnKrita << "WARNING: failed to parse KisPatternData XML section!";
continue;
}
QDomCDATASection dataSection = dataNode.toCDATASection();
QByteArray data = dataSection.data().toLatin1();
data = QByteArray::fromBase64(data);
data = qUncompress(data);
if (data.isEmpty()) {
warnKrita << "WARNING: failed to parse KisPatternData XML section!";
continue;
}
patternData = data;
}
child = child.nextSibling();
}
if (!patternUuid.isEmpty() && !patternData.isEmpty()) {
+
QString fileName = QString("%1.pat").arg(patternUuid);
- QScopedPointer<KoPattern> pattern(new KoPattern(fileName));
+ QSharedPointer<KoPattern> pattern(new KoPattern(fileName));
QBuffer buffer(&patternData);
buffer.open(QIODevice::ReadOnly);
pattern->loadPatFromDevice(&buffer);
- catcher.addPattern(path, pattern.data());
+ catcher.addPattern(path, pattern, patternUuid);
} else {
warnKrita << "WARNING: failed to load KisPattern XML section!" << ppVar(patternUuid);
}
} else if (classId == "Ptrn") { // reference to an existing pattern
QString patternUuid;
QString patternName;
+
QDomNode child = el.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
if (type == "Text" && key == "Idnt") {
patternUuid = childEl.attribute("value", "");
} else if (type == "Text" && key == "Nm ") {
patternName = childEl.attribute("value", "");
} else {
warnKrita << "WARNING: unrecognized pattern-ref section key:" << ppVar(type) << ppVar(key);
}
child = child.nextSibling();
}
catcher.addPatternRef(path, patternUuid, patternName);
} else if (classId == "Grdn") {
QString gradientName;
qreal gradientSmoothness = 100.0;
QVector<qreal> startLocations;
QVector<qreal> middleOffsets;
QVector<QColor> colors;
QVector<qreal> transpStartLocations;
QVector<qreal> transpMiddleOffsets;
QVector<qreal> transparencies;
QDomNode child = el.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
if (type == "Text" && key == "Nm ") {
gradientName = childEl.attribute("value", "");
} else if (type == "Enum" && key == "GrdF") {
QString typeId = childEl.attribute("typeId", "");
QString value = childEl.attribute("value", "");
if (typeId != "GrdF" || value != "CstS") {
warnKrita << "WARNING: Unsupported gradient type (porbably, noise-based):" << value;
return true;
}
} else if (type == "Double" && key == "Intr") {
double value = KisDomUtils::toDouble(childEl.attribute("value", "4096"));
gradientSmoothness = 100.0 * value / 4096.0;
} else if (type == "List" && key == "Clrs") {
parseColorStopsList(childEl, startLocations, middleOffsets, colors);
} else if (type == "List" && key == "Trns") {
parseTransparencyStopsList(childEl, transpStartLocations, transpMiddleOffsets, transparencies);
}
child = child.nextSibling();
}
if (colors.size() < 2) {
warnKrita << "WARNING: ASL gradient has too few stops" << ppVar(colors.size());
}
if (colors.size() != transparencies.size()) {
warnKrita << "WARNING: ASL gradient has inconsistent number of transparency stops. Dropping transparency..." << ppVar(colors.size()) << ppVar(transparencies.size());
transparencies.resize(colors.size());
for (int i = 0; i < colors.size(); i++) {
transparencies[i] = 1.0;
}
}
QString fileName = gradientName + ".ggr";
QSharedPointer<KoSegmentGradient> gradient(new KoSegmentGradient(fileName));
Q_UNUSED(gradientSmoothness);
gradient->setName(gradientName);
for (int i = 1; i < colors.size(); i++) {
QColor startColor = colors[i-1];
QColor endColor = colors[i];
startColor.setAlphaF(transparencies[i-1]);
endColor.setAlphaF(transparencies[i]);
qreal start = startLocations[i-1];
qreal end = startLocations[i];
qreal middle = start + middleOffsets[i-1] * (end - start);
gradient->createSegment(INTERP_LINEAR, COLOR_INTERP_RGB,
start, end, middle,
startColor,
endColor);
}
gradient->setValid(true);
catcher.addGradient(path, gradient);
} else {
retval = false;
}
return retval;
}
void parseElement(const QDomElement &el, const QString &parentPath, KisAslObjectCatcher &catcher)
{
KIS_ASSERT_RECOVER_RETURN(el.tagName() == "node");
QString type = el.attribute("type", "<unknown>");
QString key = el.attribute("key", "");
if (type == "Descriptor") {
QString classId = el.attribute("classId", "<noClassId>");
QString containerName = key.isEmpty() ? classId : key;
QString containerPath = buildPath(parentPath, containerName);
if (!tryParseDescriptor(el, containerPath, classId, catcher)) {
QDomNode child = el.firstChild();
while (!child.isNull()) {
parseElement(child.toElement(), containerPath, catcher);
child = child.nextSibling();
}
}
} else if (type == "List") {
catcher.setArrayMode(true);
QString containerName = key;
QString containerPath = buildPath(parentPath, containerName);
QDomNode child = el.firstChild();
while (!child.isNull()) {
parseElement(child.toElement(), containerPath, catcher);
child = child.nextSibling();
}
catcher.setArrayMode(false);
} else if (type == "Double") {
double v = KisDomUtils::toDouble(el.attribute("value", "0"));
catcher.addDouble(buildPath(parentPath, key), v);
} else if (type == "UnitFloat") {
QString unit = el.attribute("unit", "<unknown>");
double v = KisDomUtils::toDouble(el.attribute("value", "0"));
catcher.addUnitFloat(buildPath(parentPath, key), unit, v);
} else if (type == "Text") {
QString v = el.attribute("value", "");
catcher.addText(buildPath(parentPath, key), v);
} else if (type == "Enum") {
QString v = el.attribute("value", "");
QString typeId = el.attribute("typeId", "<unknown>");
catcher.addEnum(buildPath(parentPath, key), typeId, v);
} else if (type == "Integer") {
int v = KisDomUtils::toInt(el.attribute("value", "0"));
catcher.addInteger(buildPath(parentPath, key), v);
} else if (type == "Boolean") {
int v = KisDomUtils::toInt(el.attribute("value", "0"));
catcher.addBoolean(buildPath(parentPath, key), v);
} else {
warnKrita << "WARNING: XML (ASL) Unknown element type:" << type << ppVar(parentPath) << ppVar(key);
}
}
} // namespace
void KisAslXmlParser::parseXML(const QDomDocument &doc, KisAslObjectCatcher &catcher)
{
QDomElement root = doc.documentElement();
if (root.tagName() != "asl") {
return;
}
QDomNode child = root.firstChild();
while (!child.isNull()) {
Private::parseElement(child.toElement(), "", catcher);
child = child.nextSibling();
}
}
diff --git a/libs/psd/asl/kis_asl_xml_writer.cpp b/libs/psd/asl/kis_asl_xml_writer.cpp
index 3250119321..e147061ad1 100644
--- a/libs/psd/asl/kis_asl_xml_writer.cpp
+++ b/libs/psd/asl/kis_asl_xml_writer.cpp
@@ -1,397 +1,397 @@
/*
* 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_asl_xml_writer.h"
#include <QDomDocument>
#include <QColor>
#include <QPointF>
#include <QUuid>
#include <QBuffer>
#include <resources/KoPattern.h>
#include <resources/KoSegmentGradient.h>
#include <resources/KoStopGradient.h>
#include <cfloat>
#include "kis_dom_utils.h"
#include "kis_asl_writer_utils.h"
struct KisAslXmlWriter::Private
{
QDomDocument document;
QDomElement currentElement;
};
KisAslXmlWriter::KisAslXmlWriter()
: m_d(new Private)
{
QDomElement el = m_d->document.createElement("asl");
m_d->document.appendChild(el);
m_d->currentElement = el;
}
KisAslXmlWriter::~KisAslXmlWriter()
{
}
QDomDocument KisAslXmlWriter::document() const
{
if (m_d->document.documentElement() != m_d->currentElement) {
warnKrita << "KisAslXmlWriter::document(): unbalanced enter/leave descriptor/array";
}
return m_d->document;
}
void KisAslXmlWriter::enterDescriptor(const QString &key, const QString &name, const QString &classId)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "Descriptor");
el.setAttribute("name", name);
el.setAttribute("classId", classId);
m_d->currentElement.appendChild(el);
m_d->currentElement = el;
}
void KisAslXmlWriter::leaveDescriptor()
{
if (!m_d->currentElement.parentNode().toElement().isNull()) {
m_d->currentElement = m_d->currentElement.parentNode().toElement();
} else {
warnKrita << "KisAslXmlWriter::leaveDescriptor(): unbalanced enter/leave descriptor";
}
}
void KisAslXmlWriter::enterList(const QString &key)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "List");
m_d->currentElement.appendChild(el);
m_d->currentElement = el;
}
void KisAslXmlWriter::leaveList()
{
if (!m_d->currentElement.parentNode().toElement().isNull()) {
m_d->currentElement = m_d->currentElement.parentNode().toElement();
} else {
warnKrita << "KisAslXmlWriter::leaveList(): unbalanced enter/leave list";
}
}
void KisAslXmlWriter::writeDouble(const QString &key, double value)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "Double");
el.setAttribute("value", KisDomUtils::toString(value));
m_d->currentElement.appendChild(el);
}
void KisAslXmlWriter::writeInteger(const QString &key, int value)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "Integer");
el.setAttribute("value", KisDomUtils::toString(value));
m_d->currentElement.appendChild(el);
}
void KisAslXmlWriter::writeEnum(const QString &key, const QString &typeId, const QString &value)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "Enum");
el.setAttribute("typeId", typeId);
el.setAttribute("value", value);
m_d->currentElement.appendChild(el);
}
void KisAslXmlWriter::writeUnitFloat(const QString &key, const QString &unit, double value)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "UnitFloat");
el.setAttribute("unit", unit);
el.setAttribute("value", KisDomUtils::toString(value));
m_d->currentElement.appendChild(el);
}
void KisAslXmlWriter::writeText(const QString &key, const QString &value)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "Text");
el.setAttribute("value", value);
m_d->currentElement.appendChild(el);
}
void KisAslXmlWriter::writeBoolean(const QString &key, bool value)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "Boolean");
el.setAttribute("value", KisDomUtils::toString(value));
m_d->currentElement.appendChild(el);
}
void KisAslXmlWriter::writeColor(const QString &key, const QColor &value)
{
enterDescriptor(key, "", "RGBC");
writeDouble("Rd ", value.red());
writeDouble("Grn ", value.green());
writeDouble("Bl ", value.blue());
leaveDescriptor();
}
void KisAslXmlWriter::writePoint(const QString &key, const QPointF &value)
{
enterDescriptor(key, "", "CrPt");
writeDouble("Hrzn", value.x());
writeDouble("Vrtc", value.y());
leaveDescriptor();
}
void KisAslXmlWriter::writePhasePoint(const QString &key, const QPointF &value)
{
enterDescriptor(key, "", "Pnt ");
writeDouble("Hrzn", value.x());
writeDouble("Vrtc", value.y());
leaveDescriptor();
}
void KisAslXmlWriter::writeOffsetPoint(const QString &key, const QPointF &value)
{
enterDescriptor(key, "", "Pnt ");
writeUnitFloat("Hrzn", "#Prc", value.x());
writeUnitFloat("Vrtc", "#Prc", value.y());
leaveDescriptor();
}
void KisAslXmlWriter::writeCurve(const QString &key, const QString &name, const QVector<QPointF> &points)
{
enterDescriptor(key, "", "ShpC");
writeText("Nm ", name);
enterList("Crv ");
Q_FOREACH (const QPointF &pt, points) {
writePoint("", pt);
}
leaveList();
leaveDescriptor();
}
-QString KisAslXmlWriter::writePattern(const QString &key, const KoPattern *pattern)
+QString KisAslXmlWriter::writePattern(const QString &key, const KoPatternSP pattern)
{
enterDescriptor(key, "", "KisPattern");
writeText("Nm ", pattern->name());
QString uuid = KisAslWriterUtils::getPatternUuidLazy(pattern);
writeText("Idnt", uuid);
// Write pattern data
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
pattern->savePatToDevice(&buffer);
QDomCDATASection dataSection = m_d->document.createCDATASection(qCompress(buffer.buffer()).toBase64());
QDomElement dataElement = m_d->document.createElement("node");
dataElement.setAttribute("type", "KisPatternData");
dataElement.setAttribute("key", "Data");
dataElement.appendChild(dataSection);
m_d->currentElement.appendChild(dataElement);
leaveDescriptor();
return uuid;
}
-void KisAslXmlWriter::writePatternRef(const QString &key, const KoPattern *pattern, const QString &uuid)
+void KisAslXmlWriter::writePatternRef(const QString &key, const KoPatternSP pattern, const QString &uuid)
{
enterDescriptor(key, "", "Ptrn");
writeText("Nm ", pattern->name());
writeText("Idnt", uuid);
leaveDescriptor();
}
void KisAslXmlWriter::writeGradientImpl(const QString &key,
const QString &name,
QVector<QColor> colors,
QVector<qreal> transparencies,
QVector<qreal> positions,
QVector<qreal> middleOffsets)
{
enterDescriptor(key, "Gradient", "Grdn");
writeText("Nm ", name);
writeEnum("GrdF", "GrdF", "CstS");
writeDouble("Intr", 4096);
enterList("Clrs");
for (int i = 0; i < colors.size(); i++) {
enterDescriptor("", "", "Clrt");
writeColor("Clr ", colors[i]);
writeEnum("Type", "Clry", "UsrS"); // NOTE: we do not support BG/FG color tags
writeInteger("Lctn", positions[i] * 4096.0);
writeInteger("Mdpn", middleOffsets[i] * 100.0);
leaveDescriptor();
};
leaveList();
enterList("Trns");
for (int i = 0; i < colors.size(); i++) {
enterDescriptor("", "", "TrnS");
writeUnitFloat("Opct", "#Prc", transparencies[i] * 100.0);
writeInteger("Lctn", positions[i] * 4096.0);
writeInteger("Mdpn", middleOffsets[i] * 100.0);
leaveDescriptor();
};
leaveList();
leaveDescriptor();
}
void KisAslXmlWriter::writeSegmentGradient(const QString &key, const KoSegmentGradient *gradient)
{
const QList<KoGradientSegment *>&segments = gradient->segments();
QVector<QColor> colors;
QVector<qreal> transparencies;
QVector<qreal> positions;
QVector<qreal> middleOffsets;
Q_FOREACH (const KoGradientSegment *seg, segments) {
const qreal start = seg->startOffset();
const qreal end = seg->endOffset();
const qreal mid = (end - start) > DBL_EPSILON ? (seg->middleOffset() - start) / (end - start) : 0.5;
QColor color = seg->startColor().toQColor();
qreal transparency = color.alphaF();
color.setAlphaF(1.0);
colors << color;
transparencies << transparency;
positions << start;
middleOffsets << mid;
}
// last segment
{
const KoGradientSegment *lastSeg = segments.last();
QColor color = lastSeg->endColor().toQColor();
qreal transparency = color.alphaF();
color.setAlphaF(1.0);
colors << color;
transparencies << transparency;
positions << lastSeg->endOffset();
middleOffsets << 0.5;
}
writeGradientImpl(key, gradient->name(), colors, transparencies, positions, middleOffsets);
}
void KisAslXmlWriter::writeStopGradient(const QString &key, const KoStopGradient *gradient)
{
QVector<QColor> colors;
QVector<qreal> transparencies;
QVector<qreal> positions;
QVector<qreal> middleOffsets;
Q_FOREACH (const KoGradientStop &stop, gradient->stops()) {
QColor color = stop.second.toQColor();
qreal transparency = color.alphaF();
color.setAlphaF(1.0);
colors << color;
transparencies << transparency;
positions << stop.first;
middleOffsets << 0.5;
}
writeGradientImpl(key, gradient->name(), colors, transparencies, positions, middleOffsets);
}
diff --git a/libs/psd/asl/kis_asl_xml_writer.h b/libs/psd/asl/kis_asl_xml_writer.h
index 46b8746d06..aaf11be2f8 100644
--- a/libs/psd/asl/kis_asl_xml_writer.h
+++ b/libs/psd/asl/kis_asl_xml_writer.h
@@ -1,79 +1,81 @@
/*
* 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_ASL_XML_WRITER_H
#define __KIS_ASL_XML_WRITER_H
#include <QScopedPointer>
#include <QVector>
+#include <KoPattern.h>
+
#include "kritapsd_export.h"
class QString;
class QColor;
class QPointF;
class QDomDocument;
-class KoPattern;
+
class KoStopGradient;
class KoSegmentGradient;
class KRITAPSD_EXPORT KisAslXmlWriter
{
public:
KisAslXmlWriter();
~KisAslXmlWriter();
QDomDocument document() const;
void enterDescriptor(const QString &key, const QString &name, const QString &classId);
void leaveDescriptor();
void enterList(const QString &key);
void leaveList();
void writeDouble(const QString &key, double value);
void writeInteger(const QString &key, int value);
void writeEnum(const QString &key, const QString &typeId, const QString &value);
void writeUnitFloat(const QString &key, const QString &unit, double value);
void writeText(const QString &key, const QString &value);
void writeBoolean(const QString &key, bool value);
void writeColor(const QString &key, const QColor &value);
void writePoint(const QString &key, const QPointF &value);
void writePhasePoint(const QString &key, const QPointF &value);
void writeOffsetPoint(const QString &key, const QPointF &value);
void writeCurve(const QString &key, const QString &name, const QVector<QPointF> &points);
- QString writePattern(const QString &key, const KoPattern *pattern);
- void writePatternRef(const QString &key, const KoPattern *pattern, const QString &uuid);
+ QString writePattern(const QString &key, const KoPatternSP pattern);
+ void writePatternRef(const QString &key, const KoPatternSP pattern, const QString &uuid);
void writeSegmentGradient(const QString &key, const KoSegmentGradient *gradient);
void writeStopGradient(const QString &key, const KoStopGradient *gradient);
private:
void writeGradientImpl(const QString &key,
const QString &name,
QVector<QColor> colors,
QVector<qreal> transparencies,
QVector<qreal> positions,
QVector<qreal> middleOffsets);
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif /* __KIS_ASL_XML_WRITER_H */
diff --git a/libs/psd/psd.h b/libs/psd/psd.h
index ff7efc1d09..87d90fb822 100644
--- a/libs/psd/psd.h
+++ b/libs/psd/psd.h
@@ -1,1169 +1,1169 @@
/*
* Copyright (c) 2010 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.
*/
/*
* Constants and defines taken from gimp and psdparse
*/
#ifndef PSD_H
#define PSD_H
#include <QPair>
#include <QString>
#include <QColor>
#include <QVector>
#include <KoColorModelStandardIds.h>
#include <KoCompositeOpRegistry.h>
#include <resources/KoAbstractGradient.h>
-
+#include <KoPattern.h>
#include "kritapsd_export.h"
-class KoPattern;
+
const int MAX_CHANNELS = 56;
typedef qint32 Fixed; /* Represents a fixed point implied decimal */
/**
* Image color/depth modes
*/
enum psd_color_mode {
Bitmap = 0,
Grayscale=1,
Indexed=2,
RGB=3,
CMYK=4,
MultiChannel=7,
DuoTone=8,
Lab=9,
Gray16,
RGB48,
Lab48,
CMYK64,
DeepMultichannel,
Duotone16,
COLORMODE_UNKNOWN = 9000
};
/**
* Color samplers, apparently distict from PSDColormode
*/
namespace psd_color_sampler {
enum PSDColorSamplers {
RGB,
HSB,
CMYK,
PANTONE, // LAB
FOCOLTONE, // CMYK
TRUMATCH, // CMYK
TOYO, // LAB
LAB,
GRAYSCALE,
HKS, // CMYK
DIC, // LAB
TOTAL_INK,
MONITOR_RGB,
DUOTONE,
OPACITY,
ANPA = 3000 // LAB
};
}
// EFFECTS
enum psd_gradient_style {
psd_gradient_style_linear, // 'Lnr '
psd_gradient_style_radial, // 'Rdl '
psd_gradient_style_angle, // 'Angl'
psd_gradient_style_reflected, // 'Rflc'
psd_gradient_style_diamond // 'Dmnd'
};
enum psd_color_stop_type {
psd_color_stop_type_foreground_color, // 'FrgC'
psd_color_stop_type_background_Color, // 'BckC'
psd_color_stop_type_user_stop // 'UsrS'
};
enum psd_technique_type {
psd_technique_softer,
psd_technique_precise,
psd_technique_slope_limit,
};
enum psd_stroke_position {
psd_stroke_outside,
psd_stroke_inside,
psd_stroke_center
};
enum psd_fill_type {
psd_fill_solid_color,
psd_fill_gradient,
psd_fill_pattern,
};
enum psd_glow_source {
psd_glow_center,
psd_glow_edge,
};
enum psd_bevel_style {
psd_bevel_outer_bevel,
psd_bevel_inner_bevel,
psd_bevel_emboss,
psd_bevel_pillow_emboss,
psd_bevel_stroke_emboss,
};
enum psd_direction {
psd_direction_up,
psd_direction_down
};
enum psd_section_type {
psd_other = 0,
psd_open_folder,
psd_closed_folder,
psd_bounding_divider
};
// GRADIENT MAP
// Each color stop
struct psd_gradient_color_stop
{
qint32 location; // Location of color stop
qint32 midpoint; // Midpoint of color stop
QColor actual_color;
psd_color_stop_type color_stop_type;
};
// Each transparency stop
struct psd_gradient_transparency_stop
{
qint32 location; // Location of transparency stop
qint32 midpoint; // Midpoint of transparency stop
qint8 opacity; // Opacity of transparency stop
};
// Gradient settings (Photoshop 6.0)
struct psd_layer_gradient_map
{
bool reverse; // Is gradient reverse
bool dithered; // Is gradient dithered
qint32 name_length;
quint16 *name; // Name of the gradient: Unicode string, padded
qint8 number_color_stops; // Number of color stops to follow
psd_gradient_color_stop * color_stop;
qint8 number_transparency_stops;// Number of transparency stops to follow
psd_gradient_transparency_stop * transparency_stop;
qint8 expansion_count; // Expansion count ( = 2 for Photoshop 6.0)
qint8 interpolation; // Interpolation if length above is non-zero
qint8 length; // Length (= 32 for Photoshop 6.0)
qint8 mode; // Mode for this gradient
qint32 random_number_seed; // Random number seed
qint8 showing_transparency_flag;// Flag for showing transparency
qint8 using_vector_color_flag;// Flag for using vector color
qint32 roughness_factor; // Roughness factor
QColor min_color;
QColor max_color;
QColor lookup_table[256];
};
struct psd_gradient_color {
qint32 smoothness;
qint32 name_length;
quint16 * name; // Name of the gradient: Unicode string, padded
qint8 number_color_stops; // Number of color stops to follow
psd_gradient_color_stop * color_stop;
qint8 number_transparency_stops;// Number of transparency stops to follow
psd_gradient_transparency_stop *transparency_stop;
};
struct psd_pattern {
psd_color_mode color_mode = Bitmap; // The image mode of the file.
quint8 height = 0; // Point: vertical, 2 bytes and horizontal, 2 bytes
quint8 width = 0;
QString name;
QString uuid;
qint32 version = 0;
quint8 top = 0; // Rectangle: top, left, bottom, right
quint8 left = 0;
quint8 bottom = 0;
quint8 right = 0;
qint32 max_channel = 0; // Max channels
qint32 channel_number = 0;
QVector<QRgb> color_table;
};
struct psd_layer_effects_context {
psd_layer_effects_context()
: keep_original(false)
{
}
bool keep_original;
};
#define PSD_LOOKUP_TABLE_SIZE 256
// dsdw, isdw: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_22203
class KRITAPSD_EXPORT psd_layer_effects_shadow_base {
public:
psd_layer_effects_shadow_base()
: m_invertsSelection(false)
, m_edgeHidden(true)
, m_effectEnabled(false)
, m_blendMode(COMPOSITE_MULT)
, m_color(Qt::black)
, m_nativeColor(Qt::black)
, m_opacity(75)
, m_angle(120)
, m_useGlobalLight(true)
, m_distance(21)
, m_spread(0)
, m_size(21)
, m_antiAliased(0)
, m_noise(0)
, m_knocksOut(false)
, m_fillType(psd_fill_solid_color)
, m_technique(psd_technique_softer)
, m_range(100)
, m_jitter(0)
, m_gradient(0)
{
for(int i = 0; i < PSD_LOOKUP_TABLE_SIZE; ++i) {
m_contourLookupTable[i] = i;
}
}
virtual ~psd_layer_effects_shadow_base() {
}
QPoint calculateOffset(const psd_layer_effects_context *context) const;
void setEffectEnabled(bool value) {
m_effectEnabled = value;
}
bool effectEnabled() const {
return m_effectEnabled;
}
QString blendMode() const {
return m_blendMode;
}
QColor color() const {
return m_color;
}
QColor nativeColor() const {
return m_nativeColor;
}
qint32 opacity() const {
return m_opacity;
}
qint32 angle() const {
return m_angle;
}
bool useGlobalLight() const {
return m_useGlobalLight;
}
qint32 distance() const {
return m_distance;
}
qint32 spread() const {
return m_spread;
}
qint32 size() const {
return m_size;
}
const quint8* contourLookupTable() const {
return m_contourLookupTable;
}
bool antiAliased() const {
return m_antiAliased;
}
qint32 noise() const {
return m_noise;
}
bool knocksOut() const {
return m_knocksOut;
}
bool invertsSelection() const {
return m_invertsSelection;
}
bool edgeHidden() const {
return m_edgeHidden;
}
psd_fill_type fillType() const {
return m_fillType;
}
psd_technique_type technique() const {
return m_technique;
}
qint32 range() const {
return m_range;
}
qint32 jitter() const {
return m_jitter;
}
KoAbstractGradientSP gradient() const {
return m_gradient;
}
public:
void setBlendMode(QString value) {
m_blendMode = value;
}
void setColor(QColor value) {
m_color = value;
}
void setNativeColor(QColor value) {
m_nativeColor = value;
}
void setOpacity(qint32 value) {
m_opacity = value;
}
void setAngle(qint32 value) {
m_angle = value;
}
void setUseGlobalLight(bool value) {
m_useGlobalLight = value;
}
void setDistance(qint32 value) {
m_distance = value;
}
void setSpread(qint32 value) {
m_spread = value;
}
void setSize(qint32 value) {
m_size = value;
}
void setContourLookupTable(const quint8* value) {
memcpy(m_contourLookupTable, value, PSD_LOOKUP_TABLE_SIZE * sizeof(quint8));
}
void setAntiAliased(bool value) {
m_antiAliased = value;
}
void setNoise(qint32 value) {
m_noise = value;
}
void setKnocksOut(bool value) {
m_knocksOut = value;
}
void setInvertsSelection(bool value) {
m_invertsSelection = value;
}
void setEdgeHidden(bool value) {
m_edgeHidden = value;
}
void setFillType(psd_fill_type value) {
m_fillType = value;
}
void setTechnique(psd_technique_type value) {
m_technique = value;
}
void setRange(qint32 value) {
m_range = value;
}
void setJitter(qint32 value) {
m_jitter = value;
}
void setGradient(KoAbstractGradientSP value) {
m_gradient = value;
}
virtual void scaleLinearSizes(qreal scale) {
m_distance *= scale;
m_size *= scale;
}
private:
// internal
bool m_invertsSelection;
bool m_edgeHidden;
private:
bool m_effectEnabled; // Effect enabled
QString m_blendMode; // already in Krita format!
QColor m_color;
QColor m_nativeColor;
qint32 m_opacity; // Opacity as a percent (0...100)
qint32 m_angle; // Angle in degrees
bool m_useGlobalLight; // Use this angle in all of the layer effects
qint32 m_distance; // Distance in pixels
qint32 m_spread; // Intensity as a percent
qint32 m_size; // Blur value in pixels
quint8 m_contourLookupTable[PSD_LOOKUP_TABLE_SIZE];
bool m_antiAliased;
qint32 m_noise;
bool m_knocksOut;
// for Outer/Inner Glow
psd_fill_type m_fillType;
psd_technique_type m_technique;
qint32 m_range;
qint32 m_jitter;
KoAbstractGradientSP m_gradient;
};
class KRITAPSD_EXPORT psd_layer_effects_shadow_common : public psd_layer_effects_shadow_base
{
public:
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setBlendMode;
// using psd_layer_effects_shadow_base::setColor;
// using psd_layer_effects_shadow_base::setOpacity;
// using psd_layer_effects_shadow_base::setAngle;
// using psd_layer_effects_shadow_base::setUseGlobalLight;
// using psd_layer_effects_shadow_base::setDistance;
// using psd_layer_effects_shadow_base::setSpread;
// using psd_layer_effects_shadow_base::setSize;
// using psd_layer_effects_shadow_base::setContourLookupTable;
// using psd_layer_effects_shadow_base::setAntiAliased;
// using psd_layer_effects_shadow_base::setNoise;
};
class KRITAPSD_EXPORT psd_layer_effects_drop_shadow : public psd_layer_effects_shadow_common
{
public:
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
//using psd_layer_effects_shadow_base::setKnocksOut;
};
// isdw: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_22203
class KRITAPSD_EXPORT psd_layer_effects_inner_shadow : public psd_layer_effects_shadow_common
{
public:
psd_layer_effects_inner_shadow() {
setKnocksOut(false);
setInvertsSelection(true);
setEdgeHidden(false);
}
};
class KRITAPSD_EXPORT psd_layer_effects_glow_common : public psd_layer_effects_shadow_base
{
public:
psd_layer_effects_glow_common() {
setKnocksOut(true);
setDistance(0);
setBlendMode(COMPOSITE_LINEAR_DODGE);
setColor(Qt::white);
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setBlendMode;
// using psd_layer_effects_shadow_base::setColor;
// using psd_layer_effects_shadow_base::setOpacity;
// using psd_layer_effects_shadow_base::setSpread;
// using psd_layer_effects_shadow_base::setSize;
// using psd_layer_effects_shadow_base::setContourLookupTable;
// using psd_layer_effects_shadow_base::setAntiAliased;
// using psd_layer_effects_shadow_base::setNoise;
// using psd_layer_effects_shadow_base::setFillType;
// using psd_layer_effects_shadow_base::setTechnique;
// using psd_layer_effects_shadow_base::setRange;
// using psd_layer_effects_shadow_base::setJitter;
// using psd_layer_effects_shadow_base::setGradient;
};
// oglw: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_25738
class KRITAPSD_EXPORT psd_layer_effects_outer_glow : public psd_layer_effects_glow_common
{
};
// iglw: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_27692
class KRITAPSD_EXPORT psd_layer_effects_inner_glow : public psd_layer_effects_glow_common
{
public:
psd_layer_effects_inner_glow()
: m_source(psd_glow_edge) {
setInvertsSelection(true);
setEdgeHidden(false);
setKnocksOut(false);
}
psd_glow_source source() const {
return m_source;
}
void setSource(psd_glow_source value) {
m_source = value;
}
private:
psd_glow_source m_source;
};
struct psd_layer_effects_satin : public psd_layer_effects_shadow_base
{
psd_layer_effects_satin() {
setInvert(false);
setUseGlobalLight(false);
setDistance(8);
setSize(7);
setSpread(0);
setKnocksOut(true);
setEdgeHidden(false);
setBlendMode(COMPOSITE_LINEAR_BURN);
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setBlendMode;
// using psd_layer_effects_shadow_base::setColor;
// using psd_layer_effects_shadow_base::setOpacity;
// // NOTE: no global light setting explicitly!
// using psd_layer_effects_shadow_base::setAngle;
// using psd_layer_effects_shadow_base::setDistance;
// using psd_layer_effects_shadow_base::setSize;
// using psd_layer_effects_shadow_base::setContourLookupTable;
// using psd_layer_effects_shadow_base::setAntiAliased;
bool invert() const {
return m_invert;
}
void setInvert(bool value) {
m_invert = value;
}
private:
bool m_invert;
};
struct psd_pattern_info {
qint32 name_length;
quint16 * name;
quint8 identifier[256];
};
// bevl: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_31889
struct psd_layer_effects_bevel_emboss : public psd_layer_effects_shadow_base
{
psd_layer_effects_bevel_emboss()
: m_style(psd_bevel_inner_bevel),
m_technique(psd_technique_softer),
m_depth(100),
m_direction(psd_direction_up),
m_soften(0),
m_altitude(30),
m_glossAntiAliased(false),
m_highlightBlendMode(COMPOSITE_SCREEN),
m_highlightColor(Qt::white),
m_highlightOpacity(75),
m_shadowBlendMode(COMPOSITE_MULT),
m_shadowColor(Qt::black),
m_shadowOpacity(75),
m_contourEnabled(false),
m_contourRange(100),
m_textureEnabled(false),
m_texturePattern(0),
m_textureScale(100),
m_textureDepth(100),
m_textureInvert(false),
m_textureAlignWithLayer(true),
m_textureHorizontalPhase(0),
m_textureVerticalPhase(0)
{
for(int i = 0; i < PSD_LOOKUP_TABLE_SIZE; ++i) {
m_glossContourLookupTable[i] = i;
}
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setSize;
// using psd_layer_effects_shadow_base::setAngle;
// using psd_layer_effects_shadow_base::setUseGlobalLight;
// using psd_layer_effects_shadow_base::setContourLookupTable;
// using psd_layer_effects_shadow_base::setAntiAliased;
psd_bevel_style style() const {
return m_style;
}
void setStyle(psd_bevel_style value) {
m_style = value;
}
psd_technique_type technique() const {
return m_technique;
}
void setTechnique(psd_technique_type value) {
m_technique = value;
}
int depth() const {
return m_depth;
}
void setDepth(int value) {
m_depth = value;
}
psd_direction direction() const {
return m_direction;
}
void setDirection(psd_direction value) {
m_direction = value;
}
int soften() const {
return m_soften;
}
void setSoften(int value) {
m_soften = value;
}
int altitude() const {
return m_altitude;
}
void setAltitude(int value) {
m_altitude = value;
}
const quint8* glossContourLookupTable() const {
return m_glossContourLookupTable;
}
void setGlossContourLookupTable(const quint8 *value) {
memcpy(m_glossContourLookupTable, value, PSD_LOOKUP_TABLE_SIZE * sizeof(quint8));
}
bool glossAntiAliased() const {
return m_glossAntiAliased;
}
void setGlossAntiAliased(bool value) {
m_glossAntiAliased = value;
}
QString highlightBlendMode() const {
return m_highlightBlendMode;
}
void setHighlightBlendMode(QString value) {
m_highlightBlendMode = value;
}
QColor highlightColor() const {
return m_highlightColor;
}
void setHighlightColor(QColor value) {
m_highlightColor = value;
}
qint32 highlightOpacity() const {
return m_highlightOpacity;
}
void setHighlightOpacity(qint32 value) {
m_highlightOpacity = value;
}
QString shadowBlendMode() const {
return m_shadowBlendMode;
}
void setShadowBlendMode(QString value) {
m_shadowBlendMode = value;
}
QColor shadowColor() const {
return m_shadowColor;
}
void setShadowColor(QColor value) {
m_shadowColor = value;
}
qint32 shadowOpacity() const {
return m_shadowOpacity;
}
void setShadowOpacity(qint32 value) {
m_shadowOpacity = value;
}
bool contourEnabled() const {
return m_contourEnabled;
}
void setContourEnabled(bool value) {
m_contourEnabled = value;
}
int contourRange() const {
return m_contourRange;
}
void setContourRange(int value) {
m_contourRange = value;
}
bool textureEnabled() const {
return m_textureEnabled;
}
void setTextureEnabled(bool value) {
m_textureEnabled = value;
}
- KoPattern* texturePattern() const {
+ KoPatternSP texturePattern() const {
return m_texturePattern;
}
- void setTexturePattern(KoPattern *value) {
+ void setTexturePattern(KoPatternSP value) {
m_texturePattern = value;
}
int textureScale() const {
return m_textureScale;
}
void setTextureScale(int value) {
m_textureScale = value;
}
int textureDepth() const {
return m_textureDepth;
}
void setTextureDepth(int value) {
m_textureDepth = value;
}
bool textureInvert() const {
return m_textureInvert;
}
void setTextureInvert(bool value) {
m_textureInvert = value;
}
bool textureAlignWithLayer() const {
return m_textureAlignWithLayer;
}
void setTextureAlignWithLayer(bool value) {
m_textureAlignWithLayer = value;
}
void setTexturePhase(const QPointF &phase) {
m_textureHorizontalPhase = phase.x();
m_textureVerticalPhase = phase.y();
}
QPointF texturePhase() const {
return QPointF(m_textureHorizontalPhase, m_textureVerticalPhase);
}
int textureHorizontalPhase() const {
return m_textureHorizontalPhase;
}
void setTextureHorizontalPhase(int value) {
m_textureHorizontalPhase = value;
}
int textureVerticalPhase() const {
return m_textureVerticalPhase;
}
void setTextureVerticalPhase(int value) {
m_textureVerticalPhase = value;
}
void scaleLinearSizes(qreal scale) override {
psd_layer_effects_shadow_base::scaleLinearSizes(scale);
m_soften *= scale;
m_textureScale *= scale;
}
private:
psd_bevel_style m_style;
psd_technique_type m_technique;
int m_depth;
psd_direction m_direction; // Up or down
int m_soften; // Blur value in pixels.
int m_altitude;
quint8 m_glossContourLookupTable[256];
bool m_glossAntiAliased;
QString m_highlightBlendMode; // already in Krita format
QColor m_highlightColor;
qint32 m_highlightOpacity; // Highlight opacity as a percent
QString m_shadowBlendMode; // already in Krita format
QColor m_shadowColor;
qint32 m_shadowOpacity; // Shadow opacity as a percent
bool m_contourEnabled;
int m_contourRange;
bool m_textureEnabled;
- KoPattern *m_texturePattern;
+ KoPatternSP m_texturePattern;
int m_textureScale;
int m_textureDepth;
bool m_textureInvert;
bool m_textureAlignWithLayer;
int m_textureHorizontalPhase; // 0..100%
int m_textureVerticalPhase; // 0..100%
};
struct psd_layer_effects_overlay_base : public psd_layer_effects_shadow_base
{
psd_layer_effects_overlay_base()
: m_scale(100),
m_alignWithLayer(true),
m_reverse(false),
m_style(psd_gradient_style_linear),
m_gradientXOffset(0),
m_gradientYOffset(0),
m_pattern(0),
m_horizontalPhase(0),
m_verticalPhase(0)
{
setUseGlobalLight(false);
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setBlendMode;
// using psd_layer_effects_shadow_base::setOpacity;
int scale() const {
return m_scale;
}
bool alignWithLayer() const {
return m_alignWithLayer;
}
bool reverse() const {
return m_reverse;
}
psd_gradient_style style() const {
return m_style;
}
int gradientXOffset() const {
return m_gradientXOffset;
}
int gradientYOffset() const {
return m_gradientYOffset;
}
- KoPattern* pattern() const {
+ KoPatternSP pattern() const {
return m_pattern;
}
int horizontalPhase() const {
return m_horizontalPhase;
}
int verticalPhase() const {
return m_verticalPhase;
}
// refactor that
public:
void setScale(int value) {
m_scale = value;
}
void setAlignWithLayer(bool value) {
m_alignWithLayer = value;
}
void setReverse(bool value) {
m_reverse = value;
}
void setStyle(psd_gradient_style value) {
m_style = value;
}
void setGradientOffset(const QPointF &pt) {
m_gradientXOffset = qRound(pt.x());
m_gradientYOffset = qRound(pt.y());
}
QPointF gradientOffset() const {
return QPointF(m_gradientXOffset, m_gradientYOffset);
}
- void setPattern(KoPattern *value) {
+ void setPattern(KoPatternSP value) {
m_pattern = value;
}
void setPatternPhase(const QPointF &phase) {
m_horizontalPhase = phase.x();
m_verticalPhase = phase.y();
}
QPointF patternPhase() const {
return QPointF(m_horizontalPhase, m_verticalPhase);
}
void scaleLinearSizes(qreal scale) override {
psd_layer_effects_shadow_base::scaleLinearSizes(scale);
m_scale *= scale;
}
private:
// Gradient+Pattern
int m_scale;
bool m_alignWithLayer;
// Gradient
bool m_reverse;
psd_gradient_style m_style;
int m_gradientXOffset; // 0..100%
int m_gradientYOffset; // 0..100%
// Pattern
- KoPattern *m_pattern;
+ KoPatternSP m_pattern;
int m_horizontalPhase; // 0..100%
int m_verticalPhase; // 0..100%
protected:
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// must be called in the derived classes' c-tor
// using psd_layer_effects_shadow_base::setFillType;
};
// sofi: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_70055
struct psd_layer_effects_color_overlay : public psd_layer_effects_overlay_base
{
psd_layer_effects_color_overlay() {
setFillType(psd_fill_solid_color);
setColor(Qt::white);
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setColor;
};
struct psd_layer_effects_gradient_overlay : public psd_layer_effects_overlay_base
{
psd_layer_effects_gradient_overlay()
{
setFillType(psd_fill_gradient);
setAngle(90);
setReverse(false);
setScale(100);
setAlignWithLayer(true);
setStyle(psd_gradient_style_linear);
}
public:
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setGradient;
// using psd_layer_effects_shadow_base::setAngle;
// using psd_layer_effects_overlay_base::setReverse;
// using psd_layer_effects_overlay_base::setScale;
// using psd_layer_effects_overlay_base::setAlignWithLayer;
// using psd_layer_effects_overlay_base::setStyle;
// using psd_layer_effects_overlay_base::setGradientOffset;
// using psd_layer_effects_overlay_base::gradientOffset;
};
struct psd_layer_effects_pattern_overlay : public psd_layer_effects_overlay_base
{
psd_layer_effects_pattern_overlay()
{
setFillType(psd_fill_pattern);
setScale(100);
setAlignWithLayer(true);
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_overlay_base::setScale;
// using psd_layer_effects_overlay_base::setAlignWithLayer;
// using psd_layer_effects_overlay_base::setPattern;
// using psd_layer_effects_overlay_base::setPatternPhase;
// using psd_layer_effects_overlay_base::patternPhase;
private:
// These are unused
/*int m_scale;
bool m_alignWithLayer;
- KoPattern *m_pattern;
+ KoPatternSP m_pattern;
int m_horizontalPhase;
int m_verticalPhase;*/
};
struct psd_layer_effects_stroke : public psd_layer_effects_overlay_base
{
psd_layer_effects_stroke()
: m_position(psd_stroke_outside)
{
setFillType(psd_fill_solid_color);
setColor(Qt::black);
setAngle(90);
setReverse(false);
setScale(100);
setAlignWithLayer(true);
setStyle(psd_gradient_style_linear);
setScale(100);
setAlignWithLayer(true);
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setFillType;
// using psd_layer_effects_shadow_base::setSize;
// using psd_layer_effects_shadow_base::setColor;
// using psd_layer_effects_shadow_base::setGradient;
// using psd_layer_effects_shadow_base::setAngle;
// using psd_layer_effects_overlay_base::setReverse;
// using psd_layer_effects_overlay_base::setScale;
// using psd_layer_effects_overlay_base::setAlignWithLayer;
// using psd_layer_effects_overlay_base::setStyle;
// using psd_layer_effects_overlay_base::setGradientOffset;
// using psd_layer_effects_overlay_base::gradientOffset;
// using psd_layer_effects_overlay_base::setPattern;
// using psd_layer_effects_overlay_base::setPatternPhase;
// using psd_layer_effects_overlay_base::patternPhase;
psd_stroke_position position() const {
return m_position;
}
void setPosition(psd_stroke_position value) {
m_position = value;
}
private:
psd_stroke_position m_position;
};
/**
* Convert PsdColorMode to pigment colormodelid and colordepthid.
* @see KoColorModelStandardIds
*
* @return a QPair containing ColorModelId and ColorDepthID
*/
QPair<QString, QString> KRITAPSD_EXPORT psd_colormode_to_colormodelid(psd_color_mode colormode, quint16 channelDepth);
/**
* Convert the Photoshop blend mode strings to Pigment compositeop id's
*/
QString KRITAPSD_EXPORT psd_blendmode_to_composite_op(const QString& blendmode);
QString KRITAPSD_EXPORT composite_op_to_psd_blendmode(const QString& compositeOp);
#endif // PSD_H
diff --git a/libs/psd/psd_pattern.cpp b/libs/psd/psd_pattern.cpp
index e2941bb5ab..2bcf98feb0 100644
--- a/libs/psd/psd_pattern.cpp
+++ b/libs/psd/psd_pattern.cpp
@@ -1,212 +1,212 @@
/*
* 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 "psd_pattern.h"
#include "psd_utils.h"
#include "compression.h"
#include <QImage>
#include <kis_debug.h>
struct PsdPattern::Private
{
- KoPattern *patternResource;
+ KoPatternSP patternResource;
};
PsdPattern::PsdPattern()
: d(new Private())
{
d->patternResource = 0;
}
PsdPattern::~PsdPattern()
{
delete d;
}
-void PsdPattern::setPattern(KoPattern *pattern)
+void PsdPattern::setPattern(KoPatternSP pattern)
{
d->patternResource = pattern;
}
-KoPattern *PsdPattern::pattern() const
+KoPatternSP PsdPattern::pattern() const
{
return d->patternResource;
}
bool psd_write_pattern(QIODevice *io)
{
Q_UNUSED(io);
return false;
}
bool psd_read_pattern(QIODevice *io)
{
quint32 pattern_length;
psd_pattern pattern;
psdread(io, &pattern_length);
pattern_length = (pattern_length + 3) & ~3;
psdread(io, &pattern.version);
if (pattern.version != 1) return false;
psdread(io, (quint32*)&pattern.color_mode);
psdread(io, &pattern.height);
psdread(io, &pattern.width);
psdread_unicodestring(io, pattern.name);
psdread_pascalstring(io, pattern.uuid, 2);
if (pattern.color_mode == Indexed) {
pattern.color_table.reserve(256);
quint8 r;
quint8 g;
quint8 b;
for (int i = 0; i < 256; ++i) {
psdread(io, &r);
psdread(io, &g);
psdread(io, &b);
pattern.color_table.append(qRgb(r, g, b));
}
}
// Now load the virtual memory array
psdread(io, &pattern.version);
if (pattern.version != 3) return false;
quint32 vm_array_length;
psdread(io, &vm_array_length);
psdread(io, &pattern.top);
psdread(io, &pattern.left);
psdread(io, &pattern.bottom);
psdread(io, &pattern.right);
QImage img;
if (pattern.color_mode == Indexed) {
img = QImage(pattern.width, pattern.height, QImage::Format_Indexed8);
img.setColorTable(pattern.color_table);
}
else {
img = QImage(pattern.width, pattern.height, QImage::Format_ARGB32);
}
qint32 max_channels;
psdread(io, &max_channels);
QVector<QByteArray> channelData;
for (int i = 0; i < max_channels; ++i) {
quint32 written;
qint32 len;
quint32 pixel_depth1;
quint32 top;
quint32 left;
quint32 bottom;
quint32 right;
quint16 pixel_depth2;
quint8 compression_mode;
psdread(io, &written);
psdread(io, &len);
len -= 4 + 4 * 4 + 2 + 1;
if (len < 0) {
continue;
}
if (written > 0) {
// Note: channel_number is not read from the file, so always equals 0.
// Other behavior may be implemented later.
if (pattern.channel_number == 0) {
psdread(io, &pixel_depth1);
}
else {
quint32 d;
psdread(io, &d);
}
}
else {
quint32 d;
psdread(io, &d);
}
psdread(io, &top);
psdread(io, &left);
psdread(io, &bottom);
psdread(io, &right);
psdread(io, &pixel_depth2);
psdread(io, &compression_mode);
quint32 per_channel_length = 0;
if (written > 0) {
if (pattern.channel_number == 0) {
qint32 pixels;
qint32 length;
pixels = length = pattern.width * pattern.height;
switch(pixel_depth1) {
case 1:
length = (pattern.width + 7) / 8 * pattern.height;
break;
case 8:
break;
case 16:
length *= 2;
pixels *= 2;
break;
default:
dbgKrita << "Wrong depth for pattern";
return false;
}
per_channel_length = length;
Q_UNUSED(per_channel_length); // wtf!?
switch(pattern.color_mode) {
case Bitmap:
case Indexed:
break;
case Grayscale:
case DuoTone:
length *= 2;
break;
case RGB:
length *= 4;
break;
case CMYK:
length *= 5;
break;
case Lab:
length *= 4;
break;
case MultiChannel:
length *= 4;
break;
default:
dbgKrita << "Impossible color mode" << pattern.color_mode;
return false;
}
QByteArray ba = io->read(len);
channelData << Compression::uncompress(length, ba, (Compression::CompressionType)compression_mode);
}
}
}
return true;
}
diff --git a/libs/psd/psd_pattern.h b/libs/psd/psd_pattern.h
index d9aed40628..e7ab0b6afe 100644
--- a/libs/psd/psd_pattern.h
+++ b/libs/psd/psd_pattern.h
@@ -1,41 +1,41 @@
/*
* 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 PSD_PATTERN_H
#define PSD_PATTERN_H
#include "psd.h"
#include <QIODevice>
#include <resources/KoPattern.h>
class KRITAPSD_EXPORT PsdPattern
{
public:
PsdPattern();
~PsdPattern();
- void setPattern(KoPattern *pattern);
- KoPattern *pattern() const;
+ void setPattern(KoPatternSP pattern);
+ KoPatternSP pattern() const;
bool psd_write_pattern(QIODevice* io);
bool psd_read_pattern(QIODevice* io);
private:
struct Private;
Private * const d;
};
#endif // PSD_PATTERN_H
diff --git a/libs/resources/CMakeLists.txt b/libs/resources/CMakeLists.txt
new file mode 100644
index 0000000000..9044d89a26
--- /dev/null
+++ b/libs/resources/CMakeLists.txt
@@ -0,0 +1,74 @@
+include_directories(${QUAZIP_INCLUDE_DIRS})
+
+add_subdirectory(tests)
+
+set(kritaresources_LIB_SRCS
+ KisResourceCacheDb.cpp
+ KisResourceLoader.cpp
+ KisResourceLoaderRegistry.cpp
+ KisResourceLocator.cpp
+ KisResourceStorage.cpp
+ KisResourceModel.cpp
+ KisTagFilterResourceProxyModel.cpp
+ KisResourceModelProvider.cpp
+ KisResourceTypeModel.cpp
+ KisStorageModel.cpp
+ KisStorageFilterProxyModel.cpp
+ KisResourceIterator.cpp
+ KisResourceSearchBoxFilter.cpp
+
+ KisStoragePlugin.cpp
+ KisBundleStorage.cpp
+ KisFolderStorage.cpp
+ KisMemoryStorage.cpp
+
+ KisTag.cpp
+ KisTagModel.cpp
+ KisTagModelProvider.cpp
+ KisActiveFilterTagProxyModel.cpp
+
+ KoResource.cpp
+ KoResourceBundle.cpp
+ KoResourceBundleManifest.cpp
+ KoMD5Generator.cpp
+ KoResourcePaths.cpp
+
+ ResourceDebug.cpp
+
+ kconfigini.cpp
+ kconfigdata.cpp
+ kconfigbackend.cpp
+
+ KisRequiredResourcesOperators.cpp
+ KisResourcesInterface.cpp
+ KisLocalStrokeResources.cpp
+ KisGlobalResourcesInterface.cpp
+)
+
+qt5_add_resources(kritaresources_LIB_SRCS sql.qrc)
+
+add_library(kritaresources SHARED ${kritaresources_LIB_SRCS})
+
+generate_export_header(kritaresources BASE_NAME kritaresources)
+
+target_link_libraries(kritaresources
+ PUBLIC
+ Qt5::Core
+ Qt5::Widgets
+ PRIVATE
+ Qt5::Sql
+ kritaversion
+ kritaglobal
+ kritaplugin
+ kritastore
+ KF5::ConfigCore
+ KF5::CoreAddons
+ KF5::I18n
+ ${QUAZIP_LIBRARIES}
+)
+
+set_target_properties(kritaresources PROPERTIES
+ VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
+)
+install(TARGETS kritaresources ${INSTALL_TARGETS_DEFAULT_ARGS} )
+
diff --git a/libs/resources/KisActiveFilterTagProxyModel.cpp b/libs/resources/KisActiveFilterTagProxyModel.cpp
new file mode 100644
index 0000000000..0803c35d5f
--- /dev/null
+++ b/libs/resources/KisActiveFilterTagProxyModel.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 Agata Cacko <cacko.azh@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 "KisActiveFilterTagProxyModel.h"
+
+#include <QDebug>
+#include <kis_debug.h>
+#include <KisTagModel.h>
+
+
+
+struct KisActiveFilterTagProxyModel::Private
+{
+ QList<KisTagSP> tags;
+};
+
+
+
+
+KisActiveFilterTagProxyModel::KisActiveFilterTagProxyModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+ , d(new Private)
+{
+
+}
+
+KisActiveFilterTagProxyModel::~KisActiveFilterTagProxyModel()
+{
+ delete d;
+}
+
+bool KisActiveFilterTagProxyModel::filterAcceptsColumn(int /*source_column*/, const QModelIndex &/*source_parent*/) const
+{
+ return true;
+}
+
+bool KisActiveFilterTagProxyModel::filterAcceptsRow(int /*source_row*/, const QModelIndex &/*source_parent*/) const
+{
+ return true;
+}
+
+bool KisActiveFilterTagProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
+{
+ KisTagModel* model = qobject_cast<KisTagModel*>(sourceModel());
+ if (model != 0) {
+ KisTagSP left = model->tagForIndex(source_left);
+ KisTagSP right = model->tagForIndex(source_right);
+ return KisTag::compareNamesAndUrls(left, right);
+ }
+ return source_left.row() < source_right.row();
+}
diff --git a/libs/resources/KisActiveFilterTagProxyModel.h b/libs/resources/KisActiveFilterTagProxyModel.h
new file mode 100644
index 0000000000..45ea438a4a
--- /dev/null
+++ b/libs/resources/KisActiveFilterTagProxyModel.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 Agata Cacko <cacko.azh@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_ACTIVE_FILTER_TAG_PROXY_MODEL_H
+#define KIS_ACTIVE_FILTER_TAG_PROXY_MODEL_H
+
+
+#include <QSortFilterProxyModel>
+#include <QObject>
+#include <KoResource.h>
+#include <KisTag.h>
+
+#include "kritaresources_export.h"
+
+/**
+ * KisActiveFilterTagProxyModel is a sort/filter proxy model that does no filtering,
+ * but sorts tags by name and url.
+ */
+class KRITARESOURCES_EXPORT KisActiveFilterTagProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+public:
+ KisActiveFilterTagProxyModel(QObject *parent);
+ ~KisActiveFilterTagProxyModel() override;
+
+protected:
+
+ bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override;
+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
+ bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
+private:
+ struct Private;
+ Private *const d;
+
+ Q_DISABLE_COPY(KisActiveFilterTagProxyModel)
+};
+
+#endif // KIS_ACTIVE_FILTER_TAG_PROXY_MODEL_H
diff --git a/libs/resources/KisBundleStorage.cpp b/libs/resources/KisBundleStorage.cpp
new file mode 100644
index 0000000000..d7f45b64b4
--- /dev/null
+++ b/libs/resources/KisBundleStorage.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2018 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 "KisBundleStorage.h"
+
+#include <QDebug>
+#include <QFileInfo>
+#include <QDir>
+
+#include <KisTag.h>
+#include "KisResourceStorage.h"
+#include "KoResourceBundle.h"
+#include "KoResourceBundleManifest.h"
+
+class BundleTagIterator : public KisResourceStorage::TagIterator
+{
+public:
+
+ BundleTagIterator(KoResourceBundle *bundle, const QString &resourceType)
+ : m_bundle(bundle)
+ , m_resourceType(resourceType)
+ {
+ QList<KoResourceBundleManifest::ResourceReference> resources = m_bundle->manifest().files(resourceType);
+ Q_FOREACH(const KoResourceBundleManifest::ResourceReference &resourceReference, resources) {
+ Q_FOREACH(const QString &tagname, resourceReference.tagList) {
+ if (!m_tags.contains(tagname)){
+ KisTagSP tag = QSharedPointer<KisTag>(new KisTag());
+ tag->setName(tagname);
+ tag->setComment(tagname);
+ tag->setUrl(tagname);
+ m_tags[tagname] = tag;
+ }
+ KoResourceSP resource = m_bundle->resource(resourceType, resourceReference.resourcePath);
+ if (resource) {
+ m_tags[tagname]->setDefaultResources(m_tags[tagname]->defaultResources() << resource->name());
+ } else {
+ qWarning() << tagname << "The following resource could not be tagged:" << resourceReference.resourcePath << "from" << bundle->filename();
+ }
+ }
+ }
+ m_tagIterator.reset(new QListIterator<KisTagSP>(m_tags.values()));
+ }
+
+ bool hasNext() const override
+ {
+ return m_tagIterator->hasNext();
+ }
+
+ void next() override
+ {
+ const_cast<BundleTagIterator*>(this)->m_tag = m_tagIterator->next();
+ }
+
+ QString url() const override { return m_tag ? m_tag->url() : QString(); }
+ QString name() const override { return m_tag ? m_tag->name() : QString(); }
+ QString comment() const override {return m_tag ? m_tag->comment() : QString(); }
+ KisTagSP tag() const override { return m_tag; }
+
+private:
+ QHash<QString, KisTagSP> m_tags;
+ KoResourceBundle *m_bundle {0};
+ QString m_resourceType;
+ QScopedPointer<QListIterator<KisTagSP> > m_tagIterator;
+ KisTagSP m_tag;
+};
+
+
+class BundleIterator : public KisResourceStorage::ResourceIterator
+{
+public:
+ BundleIterator(KoResourceBundle *bundle, const QString &resourceType)
+ : m_bundle(bundle)
+ , m_resourceType(resourceType)
+ {
+ m_entriesIterator.reset(new QListIterator<KoResourceBundleManifest::ResourceReference>(m_bundle->manifest().files(resourceType)));
+ }
+
+ bool hasNext() const override
+ {
+ return m_entriesIterator->hasNext();
+ }
+
+ void next() override
+ {
+ KoResourceBundleManifest::ResourceReference ref = m_entriesIterator->next();
+ const_cast<BundleIterator*>(this)->m_resourceReference = ref;
+ }
+
+ QString url() const override
+ {
+ return m_resourceReference.resourcePath;
+ }
+
+ QString type() const override
+ {
+ return m_resourceType;
+ }
+
+ QDateTime lastModified() const override
+ {
+ return QFileInfo(m_bundle->filename()).lastModified();
+ }
+
+ /// This only loads the resource when called
+ KoResourceSP resource() const override
+ {
+ return m_bundle->resource(m_resourceType, m_resourceReference.resourcePath);
+ }
+
+private:
+
+ KoResourceBundle *m_bundle {0};
+ QString m_resourceType;
+ QScopedPointer<QListIterator<KoResourceBundleManifest::ResourceReference> > m_entriesIterator;
+ KoResourceBundleManifest::ResourceReference m_resourceReference;
+
+};
+
+
+class KisBundleStorage::Private {
+public:
+ QScopedPointer<KoResourceBundle> bundle;
+};
+
+
+KisBundleStorage::KisBundleStorage(const QString &location)
+ : KisStoragePlugin(location)
+ , d(new Private())
+{
+ d->bundle.reset(new KoResourceBundle(location));
+ if (!d->bundle->load()) {
+ qWarning() << "Could not load bundle" << location;
+ }
+}
+
+KisBundleStorage::~KisBundleStorage()
+{
+}
+
+KisResourceStorage::ResourceItem KisBundleStorage::resourceItem(const QString &url)
+{
+ KisResourceStorage::ResourceItem item;
+ item.url = url;
+ QStringList parts = url.split('/', QString::SkipEmptyParts);
+ Q_ASSERT(parts.size() == 2);
+ item.folder = parts[0];
+ item.resourceType = parts[0];
+ item.lastModified = QFileInfo(d->bundle->filename()).lastModified();
+ return item;
+}
+
+KoResourceSP KisBundleStorage::resource(const QString &url)
+{
+ QStringList parts = url.split('/', QString::SkipEmptyParts);
+ Q_ASSERT(parts.size() == 2);
+ return d->bundle->resource(parts[0], url);
+}
+
+QSharedPointer<KisResourceStorage::ResourceIterator> KisBundleStorage::resources(const QString &resourceType)
+{
+ return QSharedPointer<KisResourceStorage::ResourceIterator>(new BundleIterator(d->bundle.data(), resourceType));
+}
+
+QSharedPointer<KisResourceStorage::TagIterator> KisBundleStorage::tags(const QString &resourceType)
+{
+ return QSharedPointer<KisResourceStorage::TagIterator>(new BundleTagIterator(d->bundle.data(), resourceType));
+}
+
+QImage KisBundleStorage::thumbnail() const
+{
+ return d->bundle->image();
+}
+
+QStringList KisBundleStorage::metaDataKeys() const
+{
+
+ return QStringList() << KisResourceStorage::s_meta_generator
+ << KisResourceStorage::s_meta_author
+ << KisResourceStorage::s_meta_title
+ << KisResourceStorage::s_meta_description
+ << KisResourceStorage::s_meta_initial_creator
+ << KisResourceStorage::s_meta_creator
+ << KisResourceStorage::s_meta_creation_date
+ << KisResourceStorage::s_meta_dc_date
+ << KisResourceStorage::s_meta_user_defined
+ << KisResourceStorage::s_meta_name
+ << KisResourceStorage::s_meta_value
+ << KisResourceStorage::s_meta_version;
+
+}
+
+QVariant KisBundleStorage::metaData(const QString &key) const
+{
+ return d->bundle->metaData(key);
+}
+
+bool KisBundleStorage::addResource(const QString &resourceType, KoResourceSP resource)
+{
+ bool addVersion = true;
+ QString bloc = location() + "_modified" + "/" + resourceType;
+
+ if (!QDir(bloc).exists()) {
+ QDir().mkpath(bloc);
+ }
+
+ QString fn = bloc + "/" + resource->filename();
+ if (!QFileInfo(fn).exists()) {
+ addVersion = false;
+ resource->setFilename(fn);
+ }
+
+ bool result = KisStorageVersioningHelper::addVersionedResource(fn, bloc, resource);
+
+ if (addVersion) {
+ resource->setVersion(resource->version() + 1);
+ }
+
+ return result;
+}
+
diff --git a/libs/resources/KisBundleStorage.h b/libs/resources/KisBundleStorage.h
new file mode 100644
index 0000000000..ab0309cb2a
--- /dev/null
+++ b/libs/resources/KisBundleStorage.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 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 KISBUNDLESTORAGE_H
+#define KISBUNDLESTORAGE_H
+
+#include <KisStoragePlugin.h>
+#include "kritaresources_export.h"
+
+/**
+ * KisBundleStorage is KisStoragePlugin that can load resources
+ * from bundles. It can also manage overrridden resources from bundles,
+ * which are not stored in the bundles themselves.
+ */
+class KRITARESOURCES_EXPORT KisBundleStorage : public KisStoragePlugin
+{
+public:
+ KisBundleStorage(const QString &location);
+ virtual ~KisBundleStorage();
+
+ KisResourceStorage::ResourceItem resourceItem(const QString &url) override;
+
+ /// Note: this should find resources in a folder that override a resource in the bundle first
+ KoResourceSP resource(const QString &url) override;
+ QSharedPointer<KisResourceStorage::ResourceIterator> resources(const QString &resourceType) override;
+ QSharedPointer<KisResourceStorage::TagIterator> tags(const QString &resourceType) override;
+ QImage thumbnail() const override;
+ QStringList metaDataKeys() const override;
+ QVariant metaData(const QString &key) const override;
+
+ /// Add a tag to this bundle: note, the bundle itself should NOT be rewritten, but we need to
+ /// put these tags in a place in the file system
+ bool addTag(const QString &resourceType, KisTagSP tag) override {Q_UNUSED(resourceType); Q_UNUSED(tag); return false;}
+
+ /// Add a resource to this bundle: note, the bundle itself should NOT be rewritten, but we need to
+ /// put these tags in a place in the file system
+ bool addResource(const QString &resourceType, KoResourceSP resource) override;
+
+
+private:
+ class Private;
+ QScopedPointer<Private> d;
+};
+
+#endif // KISBUNDLESTORAGE_H
diff --git a/libs/resources/KisFolderStorage.cpp b/libs/resources/KisFolderStorage.cpp
new file mode 100644
index 0000000000..204342aae9
--- /dev/null
+++ b/libs/resources/KisFolderStorage.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2018 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 "KisFolderStorage.h"
+
+#include <QDirIterator>
+#include <KisMimeDatabase.h>
+#include <kis_debug.h>
+#include <KisTag.h>
+#include <KisResourceLoaderRegistry.h>
+#include <kbackup.h>
+#include <KisGlobalResourcesInterface.h>
+
+class FolderTagIterator : public KisResourceStorage::TagIterator
+{
+public:
+
+ FolderTagIterator(const QString &location, const QString &resourceType)
+ : m_location(location)
+ , m_resourceType(resourceType)
+ {
+ m_dirIterator.reset(new QDirIterator(location + '/' + resourceType,
+ QStringList() << "*.tag",
+ QDir::Files | QDir::Readable,
+ QDirIterator::Subdirectories));
+ }
+
+ bool hasNext() const override
+ {
+ return m_dirIterator->hasNext();
+ }
+
+ void next() override
+ {
+ m_dirIterator->next();
+ const_cast<FolderTagIterator*>(this)->m_tag.reset(new KisTag);
+ load(m_tag);
+ }
+
+ QString url() const override { return m_tag ? m_tag->url() : QString(); }
+ QString name() const override { return m_tag ? m_tag->name() : QString(); }
+ QString comment() const override {return m_tag ? m_tag->comment() : QString(); }
+
+ KisTagSP tag() const override
+ {
+ return m_tag;
+ }
+
+private:
+
+ bool load(KisTagSP tag) const
+ {
+ QFile f(m_dirIterator->filePath());
+ if (f.exists()) {
+ f.open(QFile::ReadOnly);
+ if (!tag->load(f)) {
+ qWarning() << m_dirIterator << "is not a valid tag desktop file";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ QScopedPointer<QDirIterator> m_dirIterator;
+ QString m_location;
+ QString m_resourceType;
+ KisTagSP m_tag;
+};
+
+
+class FolderItem : public KisResourceStorage::ResourceItem
+{
+public:
+ ~FolderItem() override {}
+};
+
+class FolderIterator : public KisResourceStorage::ResourceIterator
+{
+public:
+ FolderIterator(const QString &location, const QString &resourceType)
+ : m_location(location)
+ , m_resourceType(resourceType)
+ {
+ m_dirIterator.reset(new QDirIterator(location + '/' + resourceType,
+ KisResourceLoaderRegistry::instance()->filters(resourceType),
+ QDir::Files | QDir::Readable,
+ QDirIterator::Subdirectories));
+ }
+
+ ~FolderIterator() override {}
+
+ bool hasNext() const override
+ {
+ return m_dirIterator->hasNext();
+ }
+
+ void next() override
+ {
+ m_dirIterator->next();
+ }
+
+ QString url() const override
+ {
+ return m_dirIterator->filePath();
+ }
+
+ QString type() const override
+ {
+ return m_resourceType;
+ }
+
+ QDateTime lastModified() const override
+ {
+ return m_dirIterator->fileInfo().lastModified();
+ }
+
+ KoResourceSP resource() const override
+ {
+ if (!loadResourceInternal()) {
+ qWarning() << "Could not load resource" << m_dirIterator->filePath();
+ }
+ return m_resource;
+ }
+
+protected:
+
+ bool loadResourceInternal() const {
+ if (!m_resource || (m_resource && m_resource->filename() != m_dirIterator->filePath())) {
+ QFile f(m_dirIterator->filePath());
+ f.open(QFile::ReadOnly);
+ if (!m_resourceLoader) {
+ const_cast<FolderIterator*>(this)->m_resourceLoader = KisResourceLoaderRegistry::instance()->loader(m_resourceType, KisMimeDatabase::mimeTypeForFile(m_dirIterator->filePath()));
+ }
+ if (m_resourceLoader) {
+ const_cast<FolderIterator*>(this)->m_resource = m_resourceLoader->load(m_dirIterator->fileName(), f, KisGlobalResourcesInterface::instance());
+ }
+ else {
+ qWarning() << "Could not get resource loader for type" << m_resourceType;
+ }
+ f.close();
+ }
+ return !m_resource.isNull();
+ }
+
+ KisResourceLoaderBase *m_resourceLoader {0};
+ KoResourceSP m_resource;
+ QScopedPointer<QDirIterator> m_dirIterator;
+ const QString m_location;
+ const QString m_resourceType;
+};
+
+
+KisFolderStorage::KisFolderStorage(const QString &location)
+ : KisStoragePlugin(location)
+{
+}
+
+KisFolderStorage::~KisFolderStorage()
+{
+}
+
+bool KisFolderStorage::addTag(const QString &/*resourceType*/, KisTagSP /*tag*/)
+{
+ return false;
+}
+
+bool KisFolderStorage::addResource(const QString &resourceType, KoResourceSP _resource)
+{
+ QString fn = location() + "/" + resourceType + "/" + _resource->filename();
+ return KisStorageVersioningHelper::addVersionedResource(fn, location() + "/" + resourceType, _resource);
+}
+
+KisResourceStorage::ResourceItem KisFolderStorage::resourceItem(const QString &url)
+{
+ QFileInfo fi(url);
+ FolderItem item;
+ item.url = url;
+ item.folder = fi.path().split("/").last();
+ item.lastModified = fi.lastModified();
+ return item;
+}
+
+KoResourceSP KisFolderStorage::resource(const QString &url)
+{
+ QFileInfo fi(location() + '/' + url);
+ const QString resourceType = fi.path().split("/").last();
+ KisResourceLoaderBase *loader = KisResourceLoaderRegistry::instance()->loader(resourceType, KisMimeDatabase::mimeTypeForFile(fi.absoluteFilePath(), false));
+ Q_ASSERT(loader);
+ QFile f(fi.absoluteFilePath());
+ if (!f.open(QFile::ReadOnly)) {
+ qWarning() << "Could not open" << fi.absoluteFilePath() << "for reading";
+ return 0;
+ }
+
+ KoResourceSP res = loader->load(fi.fileName(), f, KisGlobalResourcesInterface::instance());
+ f.close();
+ return res;
+}
+
+QSharedPointer<KisResourceStorage::ResourceIterator> KisFolderStorage::resources(const QString &resourceType)
+{
+ return QSharedPointer<KisResourceStorage::ResourceIterator>(new FolderIterator(location(), resourceType));
+}
+
+QSharedPointer<KisResourceStorage::TagIterator> KisFolderStorage::tags(const QString &resourceType)
+{
+ return QSharedPointer<KisResourceStorage::TagIterator>(new FolderTagIterator(location(), resourceType));
+}
+
+QStringList KisFolderStorage::metaDataKeys() const
+{
+ return QStringList() << KisResourceStorage::s_meta_name;
+}
+
+QVariant KisFolderStorage::metaData(const QString &key) const
+{
+ if (key == KisResourceStorage::s_meta_name) {
+ return i18n("Local Resources");
+ }
+ return QVariant();
+
+}
diff --git a/libs/resources/KisFolderStorage.h b/libs/resources/KisFolderStorage.h
new file mode 100644
index 0000000000..afb893e3aa
--- /dev/null
+++ b/libs/resources/KisFolderStorage.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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 KISFOLDERSTORAGE_H
+#define KISFOLDERSTORAGE_H
+
+#include <KisStoragePlugin.h>
+
+#include <kritaresources_export.h>
+
+/**
+ * KisFolderStorage is a KisStoragePlugin which handles resources
+ * stored in the user's resource folder. On initial startup, every
+ * resource that comes as a folder resource is copied to the user's
+ * resource folder. This is also the default location where the
+ * resources the user creates are stored.
+ */
+class KRITARESOURCES_EXPORT KisFolderStorage : public KisStoragePlugin
+{
+public:
+ KisFolderStorage(const QString &location);
+ virtual ~KisFolderStorage();
+
+ /// Adds or updates this tag to the storage
+ bool addTag(const QString &resourceType, KisTagSP tag) override;
+
+ /// Adds or updates this resource to the storage
+ bool addResource(const QString &resourceType, KoResourceSP resource) override;
+
+ KisResourceStorage::ResourceItem resourceItem(const QString &url) override;
+ KoResourceSP resource(const QString &url) override;
+ QSharedPointer<KisResourceStorage::ResourceIterator> resources(const QString &resourceType) override;
+ QSharedPointer<KisResourceStorage::TagIterator> tags(const QString &resourceType) override;
+
+ QStringList metaDataKeys() const override;
+ QVariant metaData(const QString &key) const override;
+};
+
+#endif // KISFOLDERSTORAGE_H
diff --git a/libs/resources/KisGlobalResourcesInterface.cpp b/libs/resources/KisGlobalResourcesInterface.cpp
new file mode 100644
index 0000000000..5ea0675bb9
--- /dev/null
+++ b/libs/resources/KisGlobalResourcesInterface.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2020 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 "KisGlobalResourcesInterface.h"
+
+#include <QGlobalStatic>
+#include <KisResourceModelProvider.h>
+#include <KisResourceModel.h>
+
+#include <kis_debug.h>
+
+#include <QBasicMutex>
+#include <QGlobalStatic>
+
+
+namespace {
+class GlobalResourcesSource : public KisResourcesInterface::ResourceSourceAdapter
+{
+public:
+ GlobalResourcesSource(KisResourceModel *model) : m_model(model) {}
+
+ KoResourceSP resourceForFilename(const QString& filename) const override {
+ return m_model->resourceForFilename(filename);
+ }
+ KoResourceSP resourceForName(const QString& name) const override {
+ return m_model->resourceForName(name);
+ }
+ KoResourceSP resourceForMD5(const QByteArray& md5) const override {
+ return m_model->resourceForMD5(md5);
+ }
+ KoResourceSP fallbackResource() const override {
+ return m_model->rowCount() > 0 ? m_model->resourceForIndex(m_model->index(0, 0)) : KoResourceSP();
+ }
+
+private:
+ KisResourceModel *m_model;
+};
+}
+
+KisResourcesInterfaceSP KisGlobalResourcesInterface::instance()
+{
+ // TODO: implement general macro like Q_GLOBAL_STATIC()
+
+ static QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized);
+ static KisResourcesInterfaceSP d;
+ static QBasicMutex mutex;
+ int x = guard.loadAcquire();
+ if (Q_UNLIKELY(x >= QtGlobalStatic::Uninitialized)) {
+ QMutexLocker locker(&mutex);
+ if (guard.load() == QtGlobalStatic::Uninitialized) {
+ d.reset(new KisGlobalResourcesInterface());
+ static struct Cleanup {
+ ~Cleanup() {
+ d.reset();
+ guard.store(QtGlobalStatic::Destroyed);
+ }
+ } cleanup;
+ guard.storeRelease(QtGlobalStatic::Initialized);
+ }
+ }
+
+ return d;
+}
+
+KisResourcesInterface::ResourceSourceAdapter *KisGlobalResourcesInterface::createSourceImpl(const QString &type) const
+{
+ KisResourcesInterface::ResourceSourceAdapter *source =
+ new GlobalResourcesSource(KisResourceModelProvider::resourceModel(type));
+
+ KIS_ASSERT(source);
+ return source;
+}
diff --git a/libs/resources/KisGlobalResourcesInterface.h b/libs/resources/KisGlobalResourcesInterface.h
new file mode 100644
index 0000000000..c15f407b7e
--- /dev/null
+++ b/libs/resources/KisGlobalResourcesInterface.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 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 KISGLOBALRESOURCESINTERFACE_H
+#define KISGLOBALRESOURCESINTERFACE_H
+
+#include "kritaresources_export.h"
+#include "KisResourcesInterface.h"
+
+/**
+ * @brief the main resource source in Krita
+ *
+ * This class wraps KisResourceModel into a KisResourcesInterface and provides
+ * all Krita resources to consumers.
+ *
+ * WARNING: this class should never be accessed in non-GUI thread
+ */
+class KRITARESOURCES_EXPORT KisGlobalResourcesInterface : public KisResourcesInterface
+{
+public:
+ static KisResourcesInterfaceSP instance();
+
+protected:
+ ResourceSourceAdapter* createSourceImpl(const QString &type) const override;
+};
+
+#endif // KISGLOBALRESOURCESINTERFACE_H
diff --git a/libs/resources/KisLocalStrokeResources.cpp b/libs/resources/KisLocalStrokeResources.cpp
new file mode 100644
index 0000000000..19daf1ceaa
--- /dev/null
+++ b/libs/resources/KisLocalStrokeResources.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020 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 "KisLocalStrokeResources.h"
+#include "KisResourcesInterface_p.h"
+
+#include <QFileInfo>
+#include "kis_assert.h"
+
+namespace {
+class LocalResourcesSource : public KisResourcesInterface::ResourceSourceAdapter
+{
+public:
+ LocalResourcesSource(const QString &resourceType, const QList<KoResourceSP> &cachedResources)
+ : m_resourceType(resourceType),
+ m_cachedResources(cachedResources)
+ {
+ }
+
+ KoResourceSP resourceForFilename(const QString& filename) const {
+ auto it = std::find_if(m_cachedResources.begin(),
+ m_cachedResources.end(),
+ [this, filename] (KoResourceSP res) {
+ return res->resourceType().first == this->m_resourceType &&
+ (res->filename() == filename ||
+ QFileInfo(res->filename()).fileName() == filename);
+ });
+ return it != m_cachedResources.end() ? *it : KoResourceSP();
+ }
+ KoResourceSP resourceForName(const QString& name) const {
+ auto it = std::find_if(m_cachedResources.begin(),
+ m_cachedResources.end(),
+ [this, name] (KoResourceSP res) {
+ return res->resourceType().first == this->m_resourceType &&
+ res->name() == name;
+ });
+ return it != m_cachedResources.end() ? *it : KoResourceSP();
+ }
+ KoResourceSP resourceForMD5(const QByteArray& md5) const {
+ auto it = std::find_if(m_cachedResources.begin(),
+ m_cachedResources.end(),
+ [this, md5] (KoResourceSP res) {
+ return res->resourceType().first == this->m_resourceType &&
+ res->md5() == md5;
+ });
+ return it != m_cachedResources.end() ? *it : KoResourceSP();
+ }
+ KoResourceSP fallbackResource() const {
+ auto it = std::find_if(m_cachedResources.begin(),
+ m_cachedResources.end(),
+ [this] (KoResourceSP res) {
+ return res->resourceType().first == this->m_resourceType;
+ });
+ return it != m_cachedResources.end() ? *it : KoResourceSP();
+ }
+
+private:
+ const QString m_resourceType;
+ const QList<KoResourceSP> &m_cachedResources;
+};
+}
+
+class KisLocalStrokeResourcesPrivate : public KisResourcesInterfacePrivate
+{
+public:
+ KisLocalStrokeResourcesPrivate(const QList<KoResourceSP> &_localResources)
+ : localResources(_localResources)
+ {
+ // sanity check that we don't have any null resources
+ KIS_SAFE_ASSERT_RECOVER(!localResources.contains(KoResourceSP())) {
+ localResources.removeAll(KoResourceSP());
+ }
+ }
+
+ QList<KoResourceSP> localResources;
+};
+
+
+KisLocalStrokeResources::KisLocalStrokeResources(const QList<KoResourceSP> &localResources)
+ : KisResourcesInterface(new KisLocalStrokeResourcesPrivate(localResources))
+{
+}
+
+KisResourcesInterface::ResourceSourceAdapter *KisLocalStrokeResources::createSourceImpl(const QString &type) const
+{
+ Q_D(const KisLocalStrokeResources);
+ return new LocalResourcesSource(type, d->localResources);
+}
diff --git a/libs/resources/KisLocalStrokeResources.h b/libs/resources/KisLocalStrokeResources.h
new file mode 100644
index 0000000000..a3b1536237
--- /dev/null
+++ b/libs/resources/KisLocalStrokeResources.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 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 KISLOCALSTROKERESOURCES_H
+#define KISLOCALSTROKERESOURCES_H
+
+#include <KisResourcesInterface.h>
+
+class KisLocalStrokeResourcesPrivate;
+
+
+/**
+ * @brief a KisResourcesInterface-like resources storage for preloaded resources
+ *
+ * KisLocalStrokeResources stores preloaded resources and dispatches them
+ * to the consumers as a resources source.
+ *
+ * It is used by the strokes to avoid accessing global resource storage
+ * from non-gui threads.
+ */
+class KRITARESOURCES_EXPORT KisLocalStrokeResources : public KisResourcesInterface
+{
+public:
+ KisLocalStrokeResources(const QList<KoResourceSP> &localResources);
+
+protected:
+ ResourceSourceAdapter* createSourceImpl(const QString &type) const;
+
+private:
+ Q_DECLARE_PRIVATE(KisLocalStrokeResources);
+};
+
+#endif // KISLOCALSTROKERESOURCES_H
diff --git a/libs/resources/KisMemoryStorage.cpp b/libs/resources/KisMemoryStorage.cpp
new file mode 100644
index 0000000000..7c31ff38f0
--- /dev/null
+++ b/libs/resources/KisMemoryStorage.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2018 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 "KisMemoryStorage.h"
+
+#include <QVector>
+#include <QFileInfo>
+
+#include <KisMimeDatabase.h>
+#include <kis_debug.h>
+#include <KisTag.h>
+#include <KisResourceLoaderRegistry.h>
+#include <KisResourceStorage.h>
+
+
+class MemoryTagIterator : public KisResourceStorage::TagIterator
+{
+public:
+
+ MemoryTagIterator(QVector<KisTagSP> tags, const QString &resourceType)
+ : m_tags(tags)
+ , m_resourceType(resourceType)
+ {
+ }
+
+ bool hasNext() const override
+ {
+ return m_currentPosition < m_tags.size();
+ }
+
+ void next() override
+ {
+ const_cast<MemoryTagIterator*>(this)->m_currentPosition += 1;
+ }
+
+ QString url() const override { return tag() ? tag()->url() : QString(); }
+ QString name() const override { return tag() ? tag()->name() : QString(); }
+ QString comment() const override {return tag() ? tag()->comment() : QString(); }
+
+ KisTagSP tag() const override
+ {
+ if (m_currentPosition >= m_tags.size()) return 0;
+ return m_tags.at(m_currentPosition);
+
+ }
+
+private:
+
+ int m_currentPosition {0};
+ QVector<KisTagSP> m_tags;
+ QString m_resourceType;
+};
+
+
+class MemoryItem : public KisResourceStorage::ResourceItem
+{
+public:
+ ~MemoryItem() override {}
+};
+
+class MemoryIterator : public KisResourceStorage::ResourceIterator
+{
+public:
+ MemoryIterator(QVector<KoResourceSP> resources, const QString &resourceType)
+ : m_resources(resources)
+ , m_resourceType(resourceType)
+ {
+ }
+
+ ~MemoryIterator() override {}
+
+ bool hasNext() const override
+ {
+ return m_currentPosition < m_resources.size();
+ }
+
+ void next() override
+ {
+ const_cast<MemoryIterator*>(this)->m_currentPosition++;
+ }
+
+ QString url() const override
+ {
+ if (resource()) {
+ return resource()->filename();
+ }
+ return QString();
+ }
+
+ QString type() const override
+ {
+ return m_resourceType;
+ }
+
+ QDateTime lastModified() const override
+ {
+ return QDateTime::fromMSecsSinceEpoch(0);
+ }
+
+ KoResourceSP resource() const override
+ {
+ if (m_currentPosition > m_resources.size()) return 0;
+ return m_resources.at(m_currentPosition - 1);
+ }
+
+private:
+
+ int m_currentPosition {0};
+ QVector<KoResourceSP> m_resources;
+ QString m_resourceType;
+
+};
+
+class KisMemoryStorage::Private {
+public:
+ QHash<QString, QVector<KoResourceSP> > resources;
+ QHash<QString, QVector<KisTagSP> > tags;
+ QMap<QString, QVariant> metadata;
+};
+
+
+KisMemoryStorage::KisMemoryStorage(const QString &location)
+ : KisStoragePlugin(location)
+ , d(new Private)
+{
+}
+
+KisMemoryStorage::~KisMemoryStorage()
+{
+}
+
+KisMemoryStorage::KisMemoryStorage(const KisMemoryStorage &rhs)
+ : KisStoragePlugin(rhs.location())
+ , d(new Private)
+{
+ *this = rhs;
+}
+
+KisMemoryStorage &KisMemoryStorage::operator=(const KisMemoryStorage &rhs)
+{
+ if (this != &rhs) {
+ Q_FOREACH(const QString &key, rhs.d->resources.keys()) {
+ Q_FOREACH(const KoResourceSP resource, rhs.d->resources[key]) {
+ if (!d->resources.contains(key)) {
+ d->resources[key] = QVector<KoResourceSP>();
+ }
+ d->resources[key] << resource->clone();
+ }
+ }
+ Q_FOREACH(const QString &key, rhs.d->tags.keys()) {
+ Q_FOREACH(const KisTagSP tag, rhs.d->tags[key]) {
+ if (!d->tags.contains(key)) {
+ d->tags[key] = QVector<KisTagSP>();
+ }
+ d->tags[key] << tag->clone();
+ }
+ }
+ }
+ return *this;
+}
+
+bool KisMemoryStorage::addTag(const QString &resourceType, KisTagSP tag)
+{
+ if (!d->tags.contains(resourceType)) {
+ d->tags[resourceType] = QVector<KisTagSP>();
+ }
+ if (!d->tags[resourceType].contains(tag)) {
+ d->tags[resourceType].append(tag);
+ }
+ return true;
+}
+
+bool KisMemoryStorage::addResource(const QString &resourceType, KoResourceSP resource)
+{
+ if (!d->resources.contains(resourceType)) {
+ d->resources[resourceType] = QVector<KoResourceSP>();
+ }
+ if (!d->resources[resourceType].contains(resource)) {
+ d->resources[resourceType].append(resource);
+ }
+ return true;
+}
+
+KisResourceStorage::ResourceItem KisMemoryStorage::resourceItem(const QString &url)
+{
+ MemoryItem item;
+ item.url = url;
+ item.folder = QString();
+ item.lastModified = QDateTime::fromMSecsSinceEpoch(0);
+ return item;
+}
+
+KoResourceSP KisMemoryStorage::resource(const QString &url)
+{
+ KoResourceSP resource;
+ QFileInfo fi(location() + '/' + url);
+ const QString resourceType = fi.path().split("/").last();
+ Q_FOREACH(resource, d->resources[resourceType]) {
+ if (resource->filename() == url) {
+ break;
+ }
+ }
+ return resource;
+}
+
+QSharedPointer<KisResourceStorage::ResourceIterator> KisMemoryStorage::resources(const QString &resourceType)
+{
+ return QSharedPointer<KisResourceStorage::ResourceIterator>(new MemoryIterator(d->resources[resourceType], resourceType));
+}
+
+QSharedPointer<KisResourceStorage::TagIterator> KisMemoryStorage::tags(const QString &resourceType)
+{
+ return QSharedPointer<KisResourceStorage::TagIterator>(new MemoryTagIterator(d->tags[resourceType], resourceType));
+}
+
+void KisMemoryStorage::setMetaData(const QString &key, const QVariant &value)
+{
+ d->metadata[key] = value;
+}
+
+QStringList KisMemoryStorage::metaDataKeys() const
+{
+ return QStringList() << KisResourceStorage::s_meta_name;
+}
+
+QVariant KisMemoryStorage::metaData(const QString &key) const
+{
+ QVariant r;
+ if (d->metadata.contains(key)) {
+ r = d->metadata[key];
+ }
+ return r;
+}
diff --git a/libs/resources/KisMemoryStorage.h b/libs/resources/KisMemoryStorage.h
new file mode 100644
index 0000000000..fe820ea203
--- /dev/null
+++ b/libs/resources/KisMemoryStorage.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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 KISMEMORYSTORAGE_H
+#define KISMEMORYSTORAGE_H
+
+#include <KisStoragePlugin.h>
+
+#include <kritaresources_export.h>
+
+/**
+ * @brief The KisMemoryStorage class stores the temporary resources
+ * that are not saved to disk or bundle. It is also used to stores
+ * transient per-document resources, such as the document-local palette
+ * list.
+ */
+class KRITARESOURCES_EXPORT KisMemoryStorage : public KisStoragePlugin
+{
+public:
+ KisMemoryStorage(const QString &location = QString("memory"));
+ virtual ~KisMemoryStorage();
+
+ /// Copying the memory storage clones all contained resources and tags
+ KisMemoryStorage(const KisMemoryStorage &rhs);
+
+ /// This clones all contained resources and tags from rhs
+ KisMemoryStorage &operator=(const KisMemoryStorage &rhs);
+
+ bool addTag(const QString &resourceType, KisTagSP tag) override;
+ bool addResource(const QString &resourceType, KoResourceSP resource) override;
+
+ KisResourceStorage::ResourceItem resourceItem(const QString &url) override;
+ KoResourceSP resource(const QString &url) override;
+ QSharedPointer<KisResourceStorage::ResourceIterator> resources(const QString &resourceType) override;
+ QSharedPointer<KisResourceStorage::TagIterator> tags(const QString &resourceType) override;
+
+ void setMetaData(const QString &key, const QVariant &value) override;
+ QStringList metaDataKeys() const override;
+ QVariant metaData(const QString &key) const override;
+
+
+private:
+ class Private;
+ QScopedPointer<Private> d;
+
+};
+
+
+#endif // KISMEMORYSTORAGE_H
diff --git a/libs/resources/KisRequiredResourcesOperators.cpp b/libs/resources/KisRequiredResourcesOperators.cpp
new file mode 100644
index 0000000000..1d00d7ce6e
--- /dev/null
+++ b/libs/resources/KisRequiredResourcesOperators.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 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 "KisRequiredResourcesOperators.h"
+
+#include <KisLocalStrokeResources.h>
+#include <QApplication>
+#include <QThread>
+
+
+bool KisRequiredResourcesOperators::detail::isLocalResourcesStorage(KisResourcesInterfaceSP resourcesInterface)
+{
+ return resourcesInterface.dynamicCast<KisLocalStrokeResources>();
+}
+
+void KisRequiredResourcesOperators::detail::assertInGuiThread()
+{
+ KIS_SAFE_ASSERT_RECOVER_RETURN(QThread::currentThread() == qApp->thread());
+}
+
+KisResourcesInterfaceSP KisRequiredResourcesOperators::detail::createLocalResourcesStorage(const QList<KoResourceSP> &resources)
+{
+ return QSharedPointer<KisLocalStrokeResources>::create(resources);
+}
diff --git a/libs/resources/KisRequiredResourcesOperators.h b/libs/resources/KisRequiredResourcesOperators.h
new file mode 100644
index 0000000000..c08c3a0f50
--- /dev/null
+++ b/libs/resources/KisRequiredResourcesOperators.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2020 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 KISREQUIREDRESOURCESOPERATORS_H
+#define KISREQUIREDRESOURCESOPERATORS_H
+
+#include "kritaresources_export.h"
+
+#include <KisResourcesInterface.h>
+#include "kis_assert.h"
+
+namespace KisRequiredResourcesOperators
+{
+
+namespace detail {
+bool KRITARESOURCES_EXPORT isLocalResourcesStorage(KisResourcesInterfaceSP resourcesInterface);
+void KRITARESOURCES_EXPORT assertInGuiThread();
+KisResourcesInterfaceSP KRITARESOURCES_EXPORT createLocalResourcesStorage(const QList<KoResourceSP> &resources);
+}
+
+template <typename T>
+struct ResourceTraits
+{
+};
+
+/**
+ * @return true if the configuration has all the necessary resources in
+ * local storage. It mean it can be used in a threaded environment.
+ *
+ * @see createLocalResourcesSnapshot()
+ */
+template <typename T>
+bool hasLocalResourcesSnapshot(const T *object)
+{
+ return detail::isLocalResourcesStorage(object->resourcesInterface());
+}
+
+/**
+ * Loads all the required resources either from the current resource interface
+ * or from the embedded data. The object first tries to fetch the required
+ * resource from the current source, and only if it fails, tries to load
+ * it from the embedded data.
+ *
+ * @param globalResourcesInterface if \p globalResourcesInterface is not null,
+ * the resources are fetched from there, not from the internally stored resources
+ * interface
+ */
+template <typename T>
+void createLocalResourcesSnapshot(T *object, KisResourcesInterfaceSP globalResourcesInterface = nullptr)
+{
+ detail::assertInGuiThread();
+ QList<KoResourceSP> resources =
+ object->requiredResources(globalResourcesInterface ?
+ globalResourcesInterface :
+ object->resourcesInterface());
+ object->setResourcesInterface(detail::createLocalResourcesStorage(resources));
+}
+
+/**
+ * @brief creates an exact copy of the object and loads all the linked
+ * resources into the local storage.
+ * @param globalResourcesInterface is an optional override for the
+ * resources interface used for fetching linked resources. If
+ * \p globalResourcesInterface is null, then object->resourcesInterface()
+ * is used.
+ *
+ * If a filter configuration object already has a resources snapshot, then
+ * the function just clones the object without reloading anything.
+ */
+template <typename T, typename TypeSP = typename ResourceTraits<T>::template SharedPointerType<T>>
+TypeSP cloneWithResourcesSnapshot(const T* object,
+ KisResourcesInterfaceSP globalResourcesInterface = nullptr)
+{
+ auto clonedStorage = object->clone();
+ TypeSP cloned = ResourceTraits<T>::template dynamicCastSP<T>(clonedStorage);
+
+ if (!hasLocalResourcesSnapshot(cloned.data())) {
+ createLocalResourcesSnapshot(cloned.data(), globalResourcesInterface);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(hasLocalResourcesSnapshot(cloned.data()));
+ }
+
+ return cloned;
+}
+
+}
+
+#endif // KISREQUIREDRESOURCESOPERATORS_H
diff --git a/libs/resources/KisResourceCacheDb.cpp b/libs/resources/KisResourceCacheDb.cpp
new file mode 100644
index 0000000000..eb443960fe
--- /dev/null
+++ b/libs/resources/KisResourceCacheDb.cpp
@@ -0,0 +1,1287 @@
+/*
+ * Copyright (C) 2018 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 "KisResourceCacheDb.h"
+
+#include <QtSql>
+#include <QStandardPaths>
+#include <QDir>
+#include <QDirIterator>
+#include <QStringList>
+#include <QElapsedTimer>
+#include <QDataStream>
+#include <QByteArray>
+
+#include <KritaVersionWrapper.h>
+#include <klocalizedstring.h>
+#include <kis_debug.h>
+#include <KisUsageLogger.h>
+
+#include "KisResourceLocator.h"
+#include "KisResourceCacheDb.h"
+#include "KisResourceLoaderRegistry.h"
+
+const QString dbDriver = "QSQLITE";
+
+const QString KisResourceCacheDb::dbLocationKey { "ResourceCacheDbDirectory" };
+const QString KisResourceCacheDb::resourceCacheDbFilename { "resourcecache.sqlite" };
+const QString KisResourceCacheDb::databaseVersion { "0.0.2" };
+QStringList KisResourceCacheDb::storageTypes { QStringList() };
+QStringList KisResourceCacheDb::disabledBundles { QStringList() << "Krita_3_Default_Resources.bundle" };
+
+bool KisResourceCacheDb::s_valid {false};
+QString KisResourceCacheDb::s_lastError {QString()};
+
+bool KisResourceCacheDb::isValid()
+{
+ return s_valid;
+}
+
+QString KisResourceCacheDb::lastError()
+{
+ return s_lastError;
+}
+
+QSqlError createDatabase(const QString &location)
+{
+ // NOTE: if the id's of Unknown and Memory in the database
+ // will change, and that will break the queries that
+ // remove Unknown and Memory storages on start-up.
+ KisResourceCacheDb::storageTypes << KisResourceStorage::storageTypeToString(KisResourceStorage::StorageType(1))
+ << KisResourceStorage::storageTypeToString(KisResourceStorage::StorageType(2))
+ << KisResourceStorage::storageTypeToString(KisResourceStorage::StorageType(3))
+ << KisResourceStorage::storageTypeToString(KisResourceStorage::StorageType(4))
+ << KisResourceStorage::storageTypeToString(KisResourceStorage::StorageType(5))
+ << KisResourceStorage::storageTypeToString(KisResourceStorage::StorageType(6))
+ ;
+
+ if (!QSqlDatabase::connectionNames().isEmpty()) {
+ infoResources << "Already connected to resource cache database";
+ return QSqlError();
+ }
+
+ QDir dbLocation(location);
+ if (!dbLocation.exists()) {
+ dbLocation.mkpath(dbLocation.path());
+ }
+
+ QSqlDatabase db = QSqlDatabase::addDatabase(dbDriver);
+ db.setDatabaseName(location + "/" + KisResourceCacheDb::resourceCacheDbFilename);
+
+ //qDebug() << "QuerySize supported" << db.driver()->hasFeature(QSqlDriver::QuerySize);
+
+ if (!db.open()) {
+ qWarning() << "Could not connect to resource cache database";
+ return db.lastError();
+ }
+
+ QStringList tables = QStringList() << "version_information"
+ << "storage_types"
+ << "resource_types"
+ << "storages"
+ << "tags"
+ << "resources"
+ << "versioned_resources"
+ << "resource_tags"
+ << "metadata"
+ << "tags_storages";
+
+ QStringList dbTables;
+ // Verify whether we should recreate the database
+ {
+ bool allTablesPresent = true;
+ dbTables = db.tables();
+ Q_FOREACH(const QString &table, tables) {
+ if (!dbTables.contains(table)) {
+ allTablesPresent = false;
+ }
+ }
+
+ bool schemaIsOutDated = false;
+ QString schemaVersion = "Unknown";
+ QString kritaVersion = "Unknown";
+ int creationDate = 0;
+
+
+ if (dbTables.contains("version_information")) {
+ // Verify the version number
+ QFile f(":/get_version_information.sql");
+ if (f.open(QFile::ReadOnly)) {
+ QSqlQuery q(f.readAll());
+ if (q.size() > 0) {
+ q.first();
+ schemaVersion = q.value(0).toString();
+ kritaVersion = q.value(1).toString();
+ creationDate = q.value(2).toInt();
+
+ if (schemaVersion != KisResourceCacheDb::databaseVersion) {
+ // XXX: Implement migration
+ schemaIsOutDated = true;
+ qFatal("Database schema is outdated, migration is needed. Database migration has NOT been implemented yet.");
+ }
+ }
+ }
+ else {
+ return QSqlError("Error executing SQL", "Could not open get_version_information.sql", QSqlError::StatementError);
+ }
+ }
+
+ if (allTablesPresent && !schemaIsOutDated) {
+ KisUsageLogger::log(QString("Database is up to date. Version: %1, created by Krita %2, at %3")
+ .arg(schemaVersion)
+ .arg(kritaVersion)
+ .arg(QDateTime::fromSecsSinceEpoch(creationDate).toString()));
+ return QSqlError();
+ }
+ }
+
+ // Create tables
+ Q_FOREACH(const QString &table, tables) {
+ QFile f(":/create_" + table + ".sql");
+ if (f.open(QFile::ReadOnly)) {
+ QSqlQuery q;
+ if (!q.exec(f.readAll())) {
+ qWarning() << "Could not create table" << table << q.lastError();
+ return db.lastError();
+ }
+ infoResources << "Created table" << table;
+ }
+ else {
+ return QSqlError("Error executing SQL", QString("Could not find SQL file %1").arg(table), QSqlError::StatementError);
+ }
+ }
+
+ // Create indexes
+ QStringList indexes = QStringList() << "storages" << "versioned_resources";
+
+ Q_FOREACH(const QString &index, indexes) {
+ QFile f(":/create_index_" + index + ".sql");
+ if (f.open(QFile::ReadOnly)) {
+ QSqlQuery q;
+ if (!q.exec(f.readAll())) {
+ qWarning() << "Could not create index" << index;
+ return db.lastError();
+ }
+ infoResources << "Created table" << index;
+ }
+ else {
+ return QSqlError("Error executing SQL", QString("Could not find SQL file %1").arg(index), QSqlError::StatementError);
+ }
+ }
+
+ // Fill lookup tables
+ {
+ if (dbTables.contains("storage_types")) {
+ QSqlQuery q;
+ if (!q.exec("DELETE * FROM storage_types;")) {
+ qWarning() << "Could not clear table storage_types" << db.lastError();
+ }
+ }
+
+ QFile f(":/fill_storage_types.sql");
+ if (f.open(QFile::ReadOnly)) {
+ QString sql = f.readAll();
+ Q_FOREACH(const QString &originType, KisResourceCacheDb::storageTypes) {
+ QSqlQuery q(sql);
+ q.addBindValue(originType);
+ if (!q.exec()) {
+ qWarning() << "Could not insert" << originType << db.lastError() << q.executedQuery();
+ return db.lastError();
+ }
+ }
+ infoResources << "Filled lookup table storage_types";
+ }
+ else {
+ return QSqlError("Error executing SQL", QString("Could not find SQL fill_storage_types.sql."), QSqlError::StatementError);
+ }
+ }
+
+ {
+ if (dbTables.contains("resource_types")) {
+ QSqlQuery q;
+ if (!q.exec("DELETE * FROM resource_types;")) {
+ qWarning() << "Could not clear table resource_types" << db.lastError();
+ }
+ }
+ QFile f(":/fill_resource_types.sql");
+ if (f.open(QFile::ReadOnly)) {
+ QString sql = f.readAll();
+ Q_FOREACH(const QString &resourceType, KisResourceLoaderRegistry::instance()->resourceTypes()) {
+ QSqlQuery q(sql);
+ q.addBindValue(resourceType);
+ if (!q.exec()) {
+ qWarning() << "Could not insert" << resourceType << db.lastError() << q.executedQuery();
+ return db.lastError();
+ }
+ }
+ infoResources << "Filled lookup table resource_types";
+ }
+ else {
+ return QSqlError("Error executing SQL", QString("Could not find SQL fill_resource_types.sql."), QSqlError::StatementError);
+ }
+ }
+
+ {
+ QFile f(":/fill_version_information.sql");
+ if (f.open(QFile::ReadOnly)) {
+ QString sql = f.readAll();
+ QSqlQuery q;
+ q.prepare(sql);
+ q.addBindValue(KisResourceCacheDb::databaseVersion);
+ q.addBindValue(KritaVersionWrapper::versionString());
+ q.addBindValue(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
+ if (!q.exec()) {
+ qWarning() << "Could not insert the current version" << db.lastError() << q.executedQuery() << q.boundValues();
+ return db.lastError();
+ }
+ infoResources << "Filled version table";
+ }
+ else {
+ return QSqlError("Error executing SQL", QString("Could not find SQL fill_version_information.sql."), QSqlError::StatementError);
+ }
+ }
+
+ return QSqlError();
+}
+
+bool KisResourceCacheDb::initialize(const QString &location)
+{
+ QSqlError err = createDatabase(location);
+
+ s_valid = !err.isValid();
+ switch (err.type()) {
+ case QSqlError::NoError:
+ s_lastError = QString();
+ break;
+ case QSqlError::ConnectionError:
+ s_lastError = QString("Could not initialize the resource cache database. Connection error: %1").arg(err.text());
+ break;
+ case QSqlError::StatementError:
+ s_lastError = QString("Could not initialize the resource cache database. Statement error: %1").arg(err.text());
+ break;
+ case QSqlError::TransactionError:
+ s_lastError = QString("Could not initialize the resource cache database. Transaction error: %1").arg(err.text());
+ break;
+ case QSqlError::UnknownError:
+ s_lastError = QString("Could not initialize the resource cache database. Unknown error: %1").arg(err.text());
+ break;
+ }
+
+ // Delete all storages that are no longer known to the resource locator (including the memory storages)
+ deleteTemporaryResources();
+
+ return s_valid;
+}
+
+int KisResourceCacheDb::resourceIdForResource(const QString &resourceName, const QString &resourceType, const QString &storageLocation)
+{
+ QFile f(":/select_resource_id.sql");
+ f.open(QFile::ReadOnly);
+ QSqlQuery q;
+ if (!q.prepare(f.readAll())) {
+ qWarning() << "Could not read and prepare resourceIdForResource" << q.lastError();
+ return -1;
+ }
+
+ q.bindValue(":name", resourceName);
+ q.bindValue(":resource_type", resourceType);
+ q.bindValue(":storage_location", storageLocation);
+ if (!q.exec()) {
+ qWarning() << "Could not query resourceIdForResource" << q.boundValues() << q.lastError();
+ return -1;
+ }
+ if (!q.first()) {
+ return -1;
+ }
+ return q.value(0).toInt();
+
+}
+
+bool KisResourceCacheDb::resourceNeedsUpdating(int resourceId, QDateTime timestamp)
+{
+ QSqlQuery q;
+ if (!q.prepare("SELECT timestamp\n"
+ "FROM versioned_resources\n"
+ "WHERE resource_id = :resource_id\n"
+ "AND version = (SELECT MAX(version)\n"
+ " FROM versioned_resources\n"
+ " WHERE resource_id = :resource_id);")) {
+ qWarning() << "Could not prepare resourceNeedsUpdating statement" << q.lastError();
+ return false;
+ }
+
+ q.bindValue(":resource_id", resourceId);
+
+ if (!q.exec()) {
+ qWarning() << "Could not query for the most recent timestamp" << q.boundValues() << q.lastError();
+ return false;
+ }
+
+ if (!q.first()) {
+ qWarning() << "Inconsistent database: could not find a version for resource with Id" << resourceId;
+ return false;
+ }
+
+ QVariant resourceTimeStamp = q.value(0);
+
+ if (!resourceTimeStamp.isValid()) {
+ qWarning() << "Could not retrieve timestamp from versioned_resources" << resourceId;
+ return false;
+ }
+
+ return (timestamp.toSecsSinceEpoch() > resourceTimeStamp.toInt());
+}
+
+bool KisResourceCacheDb::addResourceVersion(int resourceId, QDateTime timestamp, KisResourceStorageSP storage, KoResourceSP resource)
+{
+ bool r = false;
+
+ // Create the new version. The resource is expected to have an updated version number, or
+ // this will fail on the unique index on resource_id, storage_id and version.
+ {
+ QSqlQuery q;
+ r = q.prepare("INSERT INTO versioned_resources \n"
+ "(resource_id, storage_id, version, location, timestamp, md5sum)\n"
+ "VALUES\n"
+ "( :resource_id\n"
+ ", (SELECT id \n"
+ " FROM storages \n"
+ " WHERE location = :storage_location)\n"
+ ", :version\n"
+ ", :location\n"
+ ", :timestamp\n"
+ ", :md5sum\n"
+ ");");
+
+ if (!r) {
+ qWarning() << "Could not prepare addResourceVersion statement" << q.lastError();
+ return r;
+ }
+
+ q.bindValue(":resource_id", resourceId);
+ q.bindValue(":storage_location", KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
+ q.bindValue(":version", resource->version());
+ q.bindValue(":location", QFileInfo(resource->filename()).fileName());
+ q.bindValue(":timestamp", timestamp.toSecsSinceEpoch());
+ Q_ASSERT(!resource->md5().isEmpty());
+ q.bindValue(":md5sum", resource->md5().toHex());
+ r = q.exec();
+ if (!r) {
+ qWarning() << "Could not execute addResourceVersion statement" << q.boundValues() << q.lastError();
+ return r;
+ }
+ }
+ // Update the resource itself. The resource gets a new filename when it's updated
+ {
+ QSqlQuery q;
+ r = q.prepare("UPDATE resources\n"
+ "SET name = :name\n"
+ ", filename = :filename\n"
+ ", tooltip = :tooltip\n"
+ ", thumbnail = :thumbnail\n"
+ ", version = :version\n"
+ "WHERE id = :id");
+ if (!r) {
+ qWarning() << "Could not prepare updateResource statement" << q.lastError();
+ return r;
+ }
+
+ qDebug() << resource->name() << resource->filename() << resource->version();
+
+ q.bindValue(":name", resource->name());
+ q.bindValue(":filename", QFileInfo(resource->filename()).fileName());
+ q.bindValue(":tooltip", i18n(resource->name().toUtf8()));
+ q.bindValue(":version", resource->version());
+
+ QByteArray ba;
+ QBuffer buf(&ba);
+ buf.open(QBuffer::WriteOnly);
+ resource->thumbnail().save(&buf, "PNG");
+ buf.close();
+ q.bindValue(":thumbnail", ba);
+
+ q.bindValue(":id", resourceId);
+
+ r = q.exec();
+ if (!r) {
+ qWarning() << "Could not update resource" << q.boundValues() << q.lastError();
+ }
+ }
+ return r;
+}
+
+bool KisResourceCacheDb::addResource(KisResourceStorageSP storage, QDateTime timestamp, KoResourceSP resource, const QString &resourceType)
+{
+ bool r = false;
+
+ if (!s_valid) {
+ qWarning() << "KisResourceCacheDb::addResource: The database is not valid";
+ return false;
+ }
+
+ if (!resource || !resource->valid()) {
+ qWarning() << "KisResourceCacheDb::addResource: The resource is not valid";
+ // We don't care about invalid resources and will just ignore them.
+ return true;
+ }
+ bool temporary = (storage->type() == KisResourceStorage::StorageType::Memory);
+
+ // Check whether it already exists
+ int resourceId = resourceIdForResource(resource->name(), resourceType, KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
+ if (resourceId > -1) {
+ if (resourceNeedsUpdating(resourceId, timestamp)) {
+ r = addResourceVersion(resourceId, timestamp, storage, resource);
+ }
+ return true;
+ }
+
+ QSqlQuery q;
+ r = q.prepare("INSERT INTO resources \n"
+ "(storage_id, resource_type_id, name, filename, tooltip, thumbnail, status, temporary) \n"
+ "VALUES \n"
+ "((SELECT id "
+ " FROM storages "
+ " WHERE location = :storage_location)\n"
+ ", (SELECT id\n"
+ " FROM resource_types\n"
+ " WHERE name = :resource_type)\n"
+ ", :name\n"
+ ", :filename\n"
+ ", :tooltip\n"
+ ", :thumbnail\n"
+ ", :status\n"
+ ", :temporary);");
+
+ if (!r) {
+ qWarning() << "Could not prepare addResource statement" << q.lastError();
+ return r;
+ }
+
+ q.bindValue(":storage_location", KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
+ q.bindValue(":resource_type", resourceType);
+ q.bindValue(":name", resource->name());
+ q.bindValue(":filename", QFileInfo(resource->filename()).fileName());
+ q.bindValue(":tooltip", i18n(resource->name().toUtf8()));
+
+ QByteArray ba;
+ QBuffer buf(&ba);
+ buf.open(QBuffer::WriteOnly);
+ resource->image().save(&buf, "PNG");
+ buf.close();
+ q.bindValue(":thumbnail", ba);
+
+ q.bindValue(":status", 1);
+ q.bindValue(":temporary", (temporary ? 1 : 0));
+
+ r = q.exec();
+ if (!r) {
+ qWarning() << "Could not execute addResource statement" << q.boundValues() << q.lastError();
+ return r;
+ }
+
+ resourceId = resourceIdForResource(resource->name(), resourceType, KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
+
+ // Then add a new version
+ r = q.prepare("INSERT INTO versioned_resources\n"
+ "(resource_id, storage_id, version, location, timestamp, md5sum)\n"
+ "VALUES\n"
+ "(:resource_id\n"
+ ", (SELECT id FROM storages\n"
+ " WHERE location = :storage_location)\n"
+ ", :version\n"
+ ", :location\n"
+ ", :timestamp\n"
+ ", :md5sum\n"
+ ");");
+
+ if (!r) {
+ qWarning() << "Could not prepare intitial addResourceVersion statement" << q.lastError();
+ return r;
+ }
+
+ q.bindValue(":resource_id", resourceId);
+ q.bindValue(":storage_location", KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
+ q.bindValue(":version", resource->version());
+ q.bindValue(":location", QFileInfo(resource->filename()).fileName());
+ q.bindValue(":timestamp", timestamp.toSecsSinceEpoch());
+ //Q_ASSERT(!resource->md5().isEmpty());
+ if (resource->md5().isEmpty()) {
+ qWarning() << "No md5 for resource" << resource->name() << resourceType << storage->location();
+ }
+ q.bindValue(":md5sum", resource->md5().toHex());
+
+ r = q.exec();
+ if (!r) {
+ qWarning() << "Could not execute initial addResourceVersion statement" << q.boundValues() << q.lastError();
+ }
+
+ return r;
+}
+
+bool KisResourceCacheDb::addResources(KisResourceStorageSP storage, QString resourceType)
+{
+ QSqlDatabase::database().transaction();
+ QSharedPointer<KisResourceStorage::ResourceIterator> iter = storage->resources(resourceType);
+ while (iter->hasNext()) {
+ iter->next();
+ KoResourceSP resource = iter->resource();
+ if (resource && resource->valid()) {
+ if (!addResource(storage, iter->lastModified(), resource, iter->type())) {
+ qWarning() << "Could not add resource" << QFileInfo(resource->filename()).fileName() << "to the database";
+ }
+ }
+ }
+ QSqlDatabase::database().commit();
+ return true;
+}
+
+bool KisResourceCacheDb::removeResource(int resourceId)
+{
+ if (resourceId < 0) {
+ qWarning() << "Invalid resource id; cannot remove resource";
+ return false;
+ }
+ QSqlQuery q;
+ bool r = q.prepare("UPDATE resources\n"
+ "SET status = 0\n"
+ "WHERE id = :resource_id");
+ if (!r) {
+ qWarning() << "Could not prepare removeResource query" << q.lastError();
+ }
+ q.bindValue(":resource_id", resourceId);
+ if (!q.exec()) {
+ qWarning() << "Could not update resource" << resourceId << "to inactive" << q.lastError();
+ return false;
+ }
+
+ return true;
+}
+
+bool KisResourceCacheDb::tagResource(KisResourceStorageSP storage, const QString resourceName, KisTagSP tag, const QString &resourceType)
+{
+ // Get resource id
+ int resourceId = resourceIdForResource(resourceName, resourceType, KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
+
+ if (resourceId < 0) {
+ qWarning() << "Could not find resource to tag" << KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()) << resourceName << resourceType;
+ return false;
+ }
+
+ // Get tag id
+ int tagId {-1};
+ {
+ QFile f(":/select_tag.sql");
+ if (f.open(QFile::ReadOnly)) {
+ QSqlQuery q;
+ if (!q.prepare(f.readAll())) {
+ qWarning() << "Could not read and prepare select_tag.sql" << q.lastError();
+ return false;
+ }
+ q.bindValue(":url", tag->url());
+ q.bindValue(":resource_type", resourceType);
+
+ if (!q.exec()) {
+ qWarning() << "Could not query tags" << q.boundValues() << q.lastError();
+ return false;
+ }
+
+ if (!q.first()) {
+ qWarning() << "Could not find tag" << q.boundValues() << q.lastError();
+ return false;
+ }
+
+ tagId = q.value(0).toInt();
+ }
+ }
+
+ QSqlQuery q;
+ if (!q.prepare("INSERT INTO resource_tags\n"
+ "(resource_id, tag_id)\n"
+ "VALUES\n"
+ "(:resource_id, :tag_id);")) {
+ qWarning() << "Could not prepare tagResource statement" << q.lastError();
+ return false;
+ }
+ q.bindValue(":resource_id", resourceId);
+ q.bindValue(":tag_id", tagId);
+ if (!q.exec()) {
+ qWarning() << "Could not execute tagResource stagement" << q.boundValues() << q.lastError();
+ return false;
+ }
+ return true;
+}
+
+bool KisResourceCacheDb::hasTag(const QString &url, const QString &resourceType)
+{
+ QFile f(":/select_tag.sql");
+ if (f.open(QFile::ReadOnly)) {
+ QSqlQuery q;
+ if (!q.prepare(f.readAll())) {
+ qWarning() << "Could not read and prepare select_tag.sql" << q.lastError();
+ return false;
+ }
+ q.bindValue(":url", url);
+ q.bindValue(":resource_type", resourceType);
+ if (!q.exec()) {
+ qWarning() << "Could not query tags" << q.boundValues() << q.lastError();
+ }
+ return q.first();
+ }
+ qWarning() << "Could not open select_tag.sql";
+ return false;
+}
+
+bool KisResourceCacheDb::linkTagToStorage(const QString &url, const QString &resourceType, const QString &storageLocation)
+{
+ QSqlQuery q;
+ if (!q.prepare("INSERT INTO tags_storages\n"
+ "(tag_id, storage_id)\n"
+ "VALUES\n"
+ "(\n"
+ " ( SELECT id\n"
+ " FROM tags\n"
+ " WHERE url = :url\n"
+ " AND resource_type_id = (SELECT id \n"
+ " FROM resource_types\n"
+ " WHERE name = :resource_type)"
+ " )\n"
+ ",( SELECT id\n"
+ " FROM storages\n"
+ " WHERE location = :storage_location\n"
+ " )\n"
+ ");")) {
+ qWarning() << "Could not prepare add tag/storage statement" << q.lastError();
+ return false;
+ }
+
+ q.bindValue(":url", url);
+ q.bindValue(":resource_type", resourceType);
+ q.bindValue(":storage_location", KisResourceLocator::instance()->makeStorageLocationRelative(storageLocation));
+
+ if (!q.exec()) {
+ qWarning() << "Could not insert tag/storage link" << q.boundValues() << q.lastError();
+ return false;
+ }
+ return true;
+}
+
+
+bool KisResourceCacheDb::addTag(const QString &resourceType, const QString storageLocation, const QString url, const QString name, const QString comment)
+{
+ if (hasTag(url, resourceType)) {
+ // Check whether this storage is already registered for this tag
+ QSqlQuery q;
+ if (!q.prepare("SELECT storages.location\n"
+ "FROM tags_storages\n"
+ ", tags\n"
+ ", storages\n"
+ "WHERE tags.id = tags_storages.tag_id\n"
+ "AND storages.id = tags_storages.storage_id\n"
+ "AND tags.resource_type_id = (SELECT id\n"
+ " FROM resource_types\n"
+ " WHERE name = :resource_type)\n"
+ "AND tags.url = :url"))
+ {
+ qWarning() << "Could not prepare select tags from tags_storages query" << q.lastError();
+ }
+
+ q.bindValue(":url", url);
+ q.bindValue(":resource_type", resourceType);
+
+ if (!q.exec()) {
+ qWarning() << "Could not execute tags_storages query" << q.boundValues() << q.lastError();
+ }
+
+ // If this tag is not yet linked to the storage, link it
+ if (!q.first()) {
+ return linkTagToStorage(url, resourceType, storageLocation);
+ }
+
+ return true;
+ }
+
+ // Insert the tag
+ {
+ QSqlQuery q;
+ if (!q.prepare("INSERT INTO tags\n"
+ "(url, name, comment, resource_type_id, active)\n"
+ "VALUES\n"
+ "( :url\n"
+ ", :name\n"
+ ", :comment\n"
+ ", (SELECT id\n"
+ " FROM resource_types\n"
+ " WHERE name = :resource_type)\n"
+ ", 1"
+ ");")) {
+ qWarning() << "Could not prepare insert tag statement" << q.lastError();
+ return false;
+ }
+
+ q.bindValue(":url", url);
+ q.bindValue(":name", name);
+ q.bindValue(":comment", comment);
+ q.bindValue(":resource_type", resourceType);
+
+
+ if (!q.exec()) {
+ qWarning() << "Could not insert tag" << q.boundValues() << q.lastError();
+ }
+ }
+
+ linkTagToStorage(url, resourceType, storageLocation);
+
+ return true;
+}
+
+bool KisResourceCacheDb::addTags(KisResourceStorageSP storage, QString resourceType)
+{
+ QSqlDatabase::database().transaction();
+ QSharedPointer<KisResourceStorage::TagIterator> iter = storage->tags(resourceType);
+ while(iter->hasNext()) {
+ iter->next();
+ if (!addTag(resourceType, storage->location(), iter->url(), iter->name(), iter->comment())) {
+ qWarning() << "Could not add tag" << iter->url() << "to the database";
+ }
+ if (!iter->tag()->defaultResources().isEmpty()) {
+ Q_FOREACH(const QString &resourceName, iter->tag()->defaultResources()) {
+ if (!tagResource(storage, resourceName, iter->tag(), resourceType)) {
+ qWarning() << "Could not tag resource" << resourceName << "with tag" << iter->url();
+ }
+ }
+ }
+ }
+ QSqlDatabase::database().commit();
+ return true;
+}
+
+bool KisResourceCacheDb::addStorage(KisResourceStorageSP storage, bool preinstalled)
+{
+ bool r = true;
+
+ if (!s_valid) {
+ qWarning() << "The database is not valid";
+ return false;
+ }
+
+ {
+ QSqlQuery q;
+ r = q.prepare("SELECT * FROM storages WHERE location = :location");
+ q.bindValue(":location", KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
+ r = q.exec();
+ if (!r) {
+ qWarning() << "Could not select from storages";
+ return r;
+ }
+ if (q.first()) {
+ qDebug() << "Storage already exists" << storage;
+ return true;
+ }
+ }
+
+ // Insert the storage;
+ {
+ QSqlQuery q;
+
+ r = q.prepare("INSERT INTO storages\n "
+ "(storage_type_id, location, timestamp, pre_installed, active, thumbnail)\n"
+ "VALUES\n"
+ "(:storage_type_id, :location, :timestamp, :pre_installed, :active, :thumbnail);");
+
+ if (!r) {
+ qWarning() << "Could not prepare query" << q.lastError();
+ return r;
+ }
+
+ q.bindValue(":storage_type_id", static_cast<int>(storage->type()));
+ q.bindValue(":location", KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
+ q.bindValue(":timestamp", storage->timestamp().toSecsSinceEpoch());
+ q.bindValue(":pre_installed", preinstalled ? 1 : 0);
+ q.bindValue(":active", !disabledBundles.contains(storage->name()));
+ QByteArray ba;
+ QBuffer buf(&ba);
+ buf.open(QBuffer::WriteOnly);
+ storage->thumbnail().save(&buf, "PNG");
+ buf.close();
+ q.bindValue(":thumbnail", ba);
+
+ r = q.exec();
+
+ if (!r) qWarning() << "Could not execute query" << q.lastError();
+
+ }
+
+ // Insert the metadata
+ {
+ QStringList keys = storage->metaDataKeys();
+ if (keys.size() > 0) {
+
+ QSqlQuery q;
+ if (!q.prepare("SELECT MAX(id)\n"
+ "FROM storages\n")) {
+ qWarning() << "Could not create select storages query for metadata" << q.lastError();
+ }
+
+ if (!q.exec()) {
+ qWarning() << "Could not execute select storages query for metadata" << q.lastError();
+ }
+
+ q.first();
+ int id = q.value(0).toInt();
+
+ QMap<QString, QVariant> metadata;
+
+ Q_FOREACH(const QString &key, storage->metaDataKeys()) {
+ metadata[key] = storage->metaData(key);
+ }
+
+ addMetaDataForId(metadata, id, "storages");
+ }
+ }
+
+ Q_FOREACH(const QString &resourceType, KisResourceLoaderRegistry::instance()->resourceTypes()) {
+ if (!KisResourceCacheDb::addResources(storage, resourceType)) {
+ qWarning() << "Failed to add all resources for storage" << storage;
+ r = false;
+ }
+ if (!KisResourceCacheDb::addTags(storage, resourceType)) {
+ qWarning() << "Failed to add all tags for storage" << storage;
+ }
+ }
+
+ return r;
+}
+
+bool KisResourceCacheDb::deleteStorage(KisResourceStorageSP storage)
+{
+ {
+ QSqlQuery q;
+ if (!q.prepare("DELETE FROM resources\n"
+ "WHERE id IN (SELECT versioned_resources.resource_id\n"
+ " FROM versioned_resources\n"
+ " WHERE versioned_resources.storage_id = (SELECT storages.id\n"
+ " FROM storages\n"
+ " WHERE storages.location = :location)\n"
+ " );")) {
+ qWarning() << "Could not prepare delete resources query in deleteStorage" << q.lastError();
+ return false;
+ }
+ q.bindValue(":location", KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
+ if (!q.exec()) {
+ qWarning() << "Could not execute delete resources query in deleteStorage" << q.lastError();
+ return false;
+ }
+ }
+
+ {
+ QSqlQuery q;
+ if (!q.prepare("DELETE FROM versioned_resources\n"
+ "WHERE storage_id = (SELECT storages.id\n"
+ " FROM storages\n"
+ " WHERE storages.location = :location);")) {
+ qWarning() << "Could not prepare delete versioned_resources query" << q.lastError();
+ return false;
+ }
+ q.bindValue(":location", KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
+ if (!q.exec()) {
+ qWarning() << "Could not execute delete versioned_resources query" << q.lastError();
+ return false;
+ }
+ }
+
+ {
+ QSqlQuery q;
+ if (!q.prepare("DELETE FROM storages\n"
+ "WHERE location = :location;")) {
+ qWarning() << "Could not prepare delete storages query" << q.lastError();
+ return false;
+ }
+ q.bindValue(":location", KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
+ if (!q.exec()) {
+ qWarning() << "Could not execute delete storages query" << q.lastError();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool KisResourceCacheDb::synchronizeStorage(KisResourceStorageSP storage)
+{
+ qDebug() << "Going to synchronize" << storage->location();
+
+ QElapsedTimer t;
+ t.start();
+
+ QSqlDatabase::database().transaction();
+
+ if (!s_valid) {
+ qWarning() << "KisResourceCacheDb::addResource: The database is not valid";
+ return false;
+ }
+
+ bool success = true;
+
+ // Find the storage in the database
+ QSqlQuery q;
+ if (!q.prepare("SELECT id\n"
+ ", timestamp\n"
+ ", pre_installed\n"
+ "FROM storages\n"
+ "WHERE location = :location\n")) {
+ qWarning() << "Could not prepare storage timestamp statement" << q.lastError();
+ }
+
+ q.bindValue(":location", KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
+ if (!q.exec()) {
+ qWarning() << "Could not execute storage timestamp statement" << q.boundValues() << q.lastError();
+ }
+
+ if (!q.first()) {
+ // This is a new storage, the user must have dropped it in the path before restarting Krita, so add it.
+ qDebug() << "Adding storage to the database:" << storage;
+ if (!addStorage(storage, false)) {
+ qWarning() << "Could not add new storage" << storage->name() << "to the database";
+ success = false;
+ }
+ return true;
+ }
+
+
+ // Only check the time stamp for container storages, not the contents
+ if (storage->type() != KisResourceStorage::StorageType::Folder) {
+
+ qDebug() << storage->location() << "is not a folder, going to check timestamps. Database:"
+ << q.value(1).toInt() << ", File:" << storage->timestamp().toSecsSinceEpoch();
+
+ if (!q.value(0).isValid()) {
+ qWarning() << "Could not retrieve timestamp for storage" << KisResourceLocator::instance()->makeStorageLocationRelative(storage->location());
+ success = false;
+ }
+ if (storage->timestamp().toSecsSinceEpoch() > q.value(1).toInt()) {
+ qDebug() << "Deleting" << storage->location() << "because the one on disk is newer.";
+ if (!deleteStorage(storage)) {
+ qWarning() << "Could not delete storage" << KisResourceLocator::instance()->makeStorageLocationRelative(storage->location());
+ success = false;
+ }
+ qDebug() << "Inserting" << storage->location();
+ if (!addStorage(storage, q.value(2).toBool())) {
+ qWarning() << "Could not add storage" << KisResourceLocator::instance()->makeStorageLocationRelative(storage->location());
+ success = false;
+ }
+ }
+ }
+ else {
+ // This is a folder, we need to check what's on disk and what's in the database
+
+ // Check whether everything in the storage is in the database
+ QList<int> resourcesToBeDeleted;
+
+ Q_FOREACH(const QString &resourceType, KisResourceLoaderRegistry::instance()->resourceTypes()) {
+ QStringList resourcesOnDisk;
+
+ // Check the folder
+ QSharedPointer<KisResourceStorage::ResourceIterator> iter = storage->resources(resourceType);
+ while (iter->hasNext()) {
+ iter->next();
+ qDebug() << "\tadding resources" << iter->url();
+ KoResourceSP resource = iter->resource();
+ resourcesOnDisk << QFileInfo(iter->url()).fileName();
+ if (resource) {
+ if (!addResource(storage, iter->lastModified(), resource, iter->type())) {
+ qWarning() << "Could not add/update resource" << QFileInfo(resource->filename()).fileName() << "to the database";
+ success = false;
+ }
+ }
+ }
+
+ qDebug() << "Checking for" << resourceType << ":" << resourcesOnDisk;
+
+ QSqlQuery q;
+ q.setForwardOnly(true);
+ if (!q.prepare("SELECT resources.id, resources.filename\n"
+ "FROM resources\n"
+ ", resource_types\n"
+ "WHERE resources.resource_type_id = resource_types.id\n"
+ "AND resource_types.name = :resource_type\n"
+ "AND storage_id in (SELECT id\n"
+ " FROM storages\n"
+ " WHERE storage_type_id == :storage_type)")) {
+ qWarning() << "Could not prepare resource by type query" << q.lastError();
+ success = false;
+ continue;
+ }
+
+ q.bindValue(":resource_type", resourceType);
+ q.bindValue(":storage_type", (int)KisResourceStorage::StorageType::Folder);
+
+ if (!q.exec()) {
+ qWarning() << "Could not exec resource by type query" << q.boundValues() << q.lastError();
+ success = false;
+ continue;
+ }
+
+ while (q.next()) {
+ if (!resourcesOnDisk.contains(q.value(1).toString())) {
+ resourcesToBeDeleted << q.value(0).toInt();
+ }
+ }
+ }
+
+ QSqlQuery deleteResources;
+ if (!deleteResources.prepare("DELETE FROM resources WHERE id = :id")) {
+ success = false;
+ qWarning() << "Could not prepare delete Resources query";
+ }
+
+ QSqlQuery deleteResourceVersions;
+ if (!deleteResourceVersions.prepare("DELETE FROM versioned_resources WHERE resource_id = :id")) {
+ success = false;
+ qWarning() << "Could not prepare delete Resources query";
+ }
+
+ Q_FOREACH(int id, resourcesToBeDeleted) {
+ deleteResourceVersions.bindValue(":id", id);
+ if (!deleteResourceVersions.exec()) {
+ success = false;
+ qWarning() << "Could not delete resource version" << deleteResourceVersions.boundValues() << deleteResourceVersions.lastError();
+ }
+
+ deleteResources.bindValue(":id", id);
+ if (!deleteResources.exec()) {
+ success = false;
+ qWarning() << "Could not delete resource" << deleteResources.boundValues() << deleteResources.lastError();
+ }
+ }
+ }
+ QSqlDatabase::database().commit();
+ qDebug() << "Synchronizing the storages took" << t.elapsed() << "milliseconds for" << storage->location();
+
+ return success;
+}
+
+void KisResourceCacheDb::deleteTemporaryResources()
+{
+ QSqlDatabase::database().transaction();
+
+ QSqlQuery q;
+
+ if (!q.prepare("DELETE FROM versioned_resources\n"
+ "WHERE storage_id in (SELECT id\n"
+ " FROM storages\n"
+ " WHERE storage_type_id == :storage_type)"))
+ {
+ qWarning() << "Could not prepare delete versioned resources from Unknown or Memory storages query." << q.lastError();
+ }
+
+ q.bindValue(":storage_type", (int)KisResourceStorage::StorageType::Memory);
+
+ if (!q.exec()) {
+ qWarning() << "Could not execute delete versioned resources from Unknown or Memory storages query." << q.lastError();
+ }
+
+ if (!q.prepare("DELETE FROM resources\n"
+ "WHERE storage_id in (SELECT id\n"
+ " FROM storages\n"
+ " WHERE storage_type_id == :storage_type)"))
+ {
+ qWarning() << "Could not prepare delete resources from Unknown or Memory storages query." << q.lastError();
+ }
+
+ q.bindValue(":storage_type", (int)KisResourceStorage::StorageType::Memory);
+
+ if (!q.exec()) {
+ qWarning() << "Could not execute delete resources from Unknown or Memory storages query." << q.lastError();
+ }
+
+
+ if (!q.prepare("DELETE FROM versioned_resources\n"
+ "WHERE resource_id IN (SELECT id FROM resources\n"
+ " WHERE temporary = 1)")) {
+ qWarning() << "Could not prepare delete temporary versioned resources query." << q.lastError();
+ }
+
+ if (!q.exec()) {
+ qWarning() << "Could not execute delete temporary versioned resources query." << q.lastError();
+ }
+
+ if (!q.prepare("DELETE FROM resources\n"
+ "WHERE temporary = 1")) {
+ qWarning() << "Could not prepare delete temporary resources query." << q.lastError();
+ return;
+ }
+
+ if (!q.exec()) {
+ qWarning() << "Could not execute delete temporary resources query." << q.lastError();
+ }
+
+ if (!q.prepare("DELETE FROM storages\n"
+ "WHERE storage_type_id == :storage_type\n"))
+ {
+ qWarning() << "Could not prepare delete Unknown or Memory storages query." << q.lastError();
+ }
+
+ q.bindValue(":storage_type", (int)KisResourceStorage::StorageType::Memory);
+
+ if (!q.exec()) {
+ qWarning() << "Could not execute delete Unknown or Memory storages query." << q.lastError();
+ }
+
+ QSqlDatabase::database().commit();
+}
+
+bool KisResourceCacheDb::registerResourceType(const QString &resourceType)
+{
+ // Check whether the type already exists
+ {
+ QSqlQuery q;
+ if (!q.prepare("SELECT count(*)\n"
+ "FROM resource_types\n"
+ "WHERE name = :resource_type\n")) {
+ qWarning() << "Could not prepare select from resource_types query" << q.lastError();
+ return false;
+ }
+ q.bindValue(":resource_type", resourceType);
+ if (!q.exec()) {
+ qWarning() << "Could not execute select from resource_types query" << q.lastError();
+ return false;
+ }
+ q.first();
+ int rowCount = q.value(0).toInt();
+ if (rowCount > 0) {
+ return true;
+ }
+ }
+ // if not, add it
+ QFile f(":/fill_resource_types.sql");
+ if (f.open(QFile::ReadOnly)) {
+ QString sql = f.readAll();
+ QSqlQuery q(sql);
+ q.addBindValue(resourceType);
+ if (!q.exec()) {
+ qWarning() << "Could not insert" << resourceType << q.lastError();
+ return false;
+ }
+ return true;
+ }
+ qWarning() << "Could not open fill_resource_types.sql";
+ return false;
+}
+
+QMap<QString, QVariant> KisResourceCacheDb::metaDataForId(int id, const QString &tableName)
+{
+ QMap<QString, QVariant> map;
+
+ QSqlQuery q;
+ q.setForwardOnly(true);
+ if (!q.prepare("SELECT key\n"
+ ", value\n"
+ "FROM metadata\n"
+ "WHERE foreign_id = :id\n"
+ "AND table_name = :table")) {
+ qWarning() << "Could not prepare metadata query" << q.lastError();
+ return map;
+ }
+
+ q.bindValue(":id", id);
+ q.bindValue(":table", tableName);
+
+ if (!q.exec()) {
+ qWarning() << "Could not execute metadata query" << q.lastError();
+ return map;
+ }
+
+ while (q.next()) {
+ QString key = q.value(0).toString();
+ QByteArray ba = q.value(1).toByteArray();
+ QDataStream ds(QByteArray::fromBase64(ba));
+ QVariant value;
+ ds >> value;
+ map[key] = value;
+ }
+
+ return map;
+}
+
+bool KisResourceCacheDb::updateMetaDataForId(const QMap<QString, QVariant> map, int id, const QString &tableName)
+{
+ QSqlDatabase::database().transaction();
+
+ {
+ QSqlQuery q;
+ if (!q.prepare("DELETE FROM metadata\n"
+ "WHERE foreign_id = :id\n"
+ "AND table_name = :table\n")) {
+ qWarning() << "Could not prepare delete metadata query" << q.lastError();
+ return false;
+ }
+
+ q.bindValue(":id", id);
+ q.bindValue(":table", tableName);
+
+ if (!q.exec()) {
+ QSqlDatabase::database().rollback();
+ qWarning() << "Could not execute delete metadata query" << q.lastError();
+ return false;
+
+ }
+ }
+
+ if (addMetaDataForId(map, id, tableName)) {
+ QSqlDatabase::database().commit();
+ }
+ else {
+ QSqlDatabase::database().rollback();
+ }
+ return true;
+}
+
+bool KisResourceCacheDb::addMetaDataForId(const QMap<QString, QVariant> map, int id, const QString &tableName)
+{
+
+ QSqlQuery q;
+ if (!q.prepare("INSERT INTO metadata\n"
+ "(foreign_id, table_name, key, value)\n"
+ "VALUES\n"
+ "(:id, :table, :key, :value)")) {
+ QSqlDatabase::database().rollback();
+ qWarning() << "Could not create insert metadata query" << q.lastError();
+ return false;
+ }
+
+ QMap<QString, QVariant>::const_iterator iter = map.cbegin();
+ while (iter != map.cend()) {
+ q.bindValue(":id", id);
+ q.bindValue(":table", tableName);
+ q.bindValue(":key", iter.key());
+
+ QVariant v = iter.value();
+ QByteArray ba;
+ QDataStream ds(&ba, QIODevice::WriteOnly);
+ ds << v;
+ ba = ba.toBase64();
+ q.bindValue(":value", QString::fromLatin1(ba));
+
+ if (!q.exec()) {
+ qWarning() << "Could not insert metadata" << q.lastError();
+ return false;
+ }
+
+ ++iter;
+ }
+ return true;
+}
diff --git a/libs/resources/KisResourceCacheDb.h b/libs/resources/KisResourceCacheDb.h
new file mode 100644
index 0000000000..e40d0ee416
--- /dev/null
+++ b/libs/resources/KisResourceCacheDb.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 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 KISRESOURCECACHEDB_H
+#define KISRESOURCECACHEDB_H
+
+#include <QObject>
+
+#include <kritaresources_export.h>
+
+#include <KisResourceStorage.h>
+
+/**
+ * @brief The KisResourceCacheDb class encapsulates the database that
+ * caches information about the resources available to the user.
+ *
+ * KisApplication creates and initializes the database. All other methods
+ * are static and can be used from anywhere.
+ */
+class KRITARESOURCES_EXPORT KisResourceCacheDb
+{
+public:
+
+ static const QString dbLocationKey; ///< configuration key for the location of the database
+ static const QString resourceCacheDbFilename; ///< filename of the database
+ static const QString databaseVersion; ///< current schema version
+ static QStringList storageTypes; ///< kinds of places where resources can be stored
+ static QStringList disabledBundles; ///< the list of compatibility bundles that need to inactive by default
+
+ /**
+ * @brief isValid
+ * @return true if the database has been correctly created, false if the database cannot be used
+ */
+ static bool isValid();
+
+ /**
+ * @brief lastError returns the last SQL error.
+ */
+ static QString lastError();
+
+ /**
+ * @brief initializes the database and updates the scheme if necessary. Does not actually
+ * fill the database with pointers to resources.
+ *
+ * @param location the location of the database
+ * @return true if the database has been initialized correctly
+ */
+ static bool initialize(const QString &location);
+
+ /// Delete all storages that are Unknown or Memory and all resources that are marked temporary or belong to Unknown or Memory storages
+ static void deleteTemporaryResources();
+
+private:
+
+ friend class KisResourceLocator;
+ friend class TestResourceLocator;
+ friend class TestResourceCacheDb;
+ friend class KisTagModel;
+ friend class KisResourceLoaderRegistry;
+
+ explicit KisResourceCacheDb(); // Deleted
+ ~KisResourceCacheDb(); // Deleted
+ KisResourceCacheDb operator=(const KisResourceCacheDb&); // Deleted
+
+ /**
+ * @brief registerResourceType registers this resource type in the database
+ * @param resourceType the string that represents the type
+ * @return true if the type was registered or had already been registered
+ */
+ static bool registerResourceType(const QString &resourceType);
+
+ static int resourceIdForResource(const QString &resourceName, const QString &resourceType, const QString &storageLocation);
+ static bool resourceNeedsUpdating(int resourceId, QDateTime timestamp);
+
+ /**
+ * @brief addResourceVersion addes a new version of the resource to the database.
+ * The resource itself already should be updated with the updated filename and version.
+ * @param resourceId unique identifier for the resource
+ * @param timestamp
+ * @param storage
+ * @param resource
+ * @return true if the database was succesfully updated
+ */
+ static bool addResourceVersion(int resourceId, QDateTime timestamp, KisResourceStorageSP storage, KoResourceSP resource);
+ static bool addResource(KisResourceStorageSP storage, QDateTime timestamp, KoResourceSP resource, const QString &resourceType);
+ static bool addResources(KisResourceStorageSP storage, QString resourceType);
+
+ /// Make this resource inactive; this does not remove the resource from disk or from the database
+ static bool removeResource(int resourceId);
+
+ static bool tagResource(KisResourceStorageSP storage, const QString resourceName, KisTagSP tag, const QString &resourceType);
+ static bool hasTag(const QString &url, const QString &resourceType);
+ static bool linkTagToStorage(const QString &url, const QString &resourceType, const QString &storageLocation);
+ static bool addTag(const QString &resourceType, const QString storageLocation, const QString url, const QString name, const QString comment);
+ static bool addTags(KisResourceStorageSP storage, QString resourceType);
+
+ static bool addStorage(KisResourceStorageSP storage, bool preinstalled);
+ static bool deleteStorage(KisResourceStorageSP storage);
+ static bool synchronizeStorage(KisResourceStorageSP storage);
+
+ /**
+ * @brief metaDataForId
+ * @param id
+ * @param tableName
+ * @return
+ */
+ static QMap<QString, QVariant> metaDataForId(int id, const QString &tableName);
+
+ /**
+ * @brief setMetaDataForId removes all metadata for the given id and table name,
+ * and inserts the metadata in the metadata table.
+ * @param id
+ * @param tableName
+ * @return true if succesful, false if not
+ */
+ static bool updateMetaDataForId(const QMap<QString, QVariant> map, int id, const QString &tableName);
+ static bool addMetaDataForId(const QMap<QString, QVariant> map, int id, const QString &tableName);
+
+ static bool s_valid;
+ static QString s_lastError;
+};
+
+#endif // KISRESOURCECACHEDB_H
diff --git a/libs/resources/KisResourceDirtyStateSaver.h b/libs/resources/KisResourceDirtyStateSaver.h
new file mode 100644
index 0000000000..43eb21e901
--- /dev/null
+++ b/libs/resources/KisResourceDirtyStateSaver.h
@@ -0,0 +1,65 @@
+/*
+ * 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 KISRESOURCEDIRTYSTATESAVER_H
+#define KISRESOURCEDIRTYSTATESAVER_H
+
+#include <KoResource.h>
+
+#include "kritaresources_export.h"
+/**
+ * Never use manual save/restore calls to
+ * KoResource::isDirty()/KoResource::setDirty()! They will lead to
+ * hard-to-tack-down bugs when the dirty state will not be
+ * restored on jumps like 'return', 'break' or exception.
+ */
+class KRITARESOURCES_EXPORT KisResourceDirtyStateSaver
+{
+public:
+ KisResourceDirtyStateSaver(KoResourceSP resource)
+ : m_resource(resource)
+ , m_isDirty(resource->isDirty())
+
+ {
+ }
+
+ /// Extra constructor to be called from KoResource itself
+ KisResourceDirtyStateSaver(KoResource *resource)
+ : m_parentResource(resource)
+ , m_isDirty(resource->isDirty())
+ {
+ }
+
+ ~KisResourceDirtyStateSaver() {
+ if (m_resource) {
+ m_resource->setDirty(m_isDirty);
+ }
+ else if (m_parentResource) {
+ m_parentResource->setDirty(m_isDirty);
+ }
+ }
+
+private:
+
+ KoResourceSP m_resource;
+ KoResource *m_parentResource {0};
+ bool m_isDirty;
+
+};
+
+#endif // KISRESOURCEDIRTYSTATESAVER_H
diff --git a/libs/resources/KisResourceIterator.cpp b/libs/resources/KisResourceIterator.cpp
new file mode 100644
index 0000000000..e8aa99734f
--- /dev/null
+++ b/libs/resources/KisResourceIterator.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2018 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 "KisResourceIterator.h"
+
+#include <KisResourceModel.h>
+#include <QModelIndex>
+
+KisResourceItem::KisResourceItem(KisResourceModel *resourceModel, const QModelIndex &index)
+ : m_resourceModel(resourceModel)
+ , m_index(index)
+{
+
+}
+
+int KisResourceItem::id()
+{
+ if (m_index.isValid()) {
+ return m_index.data(Qt::UserRole + KisResourceModel::Id).toInt();
+ }
+ return -1;
+}
+
+QString KisResourceItem::resourceType()
+{
+ if (m_index.isValid()) {
+ return m_index.data(Qt::UserRole + KisResourceModel::ResourceType).toString();
+ }
+ return QString();
+}
+
+QString KisResourceItem::name()
+{
+ if (m_index.isValid()) {
+ return m_index.data(Qt::UserRole + KisResourceModel::Name).toString();
+ }
+ return QString();
+}
+
+QString KisResourceItem::filename()
+{
+ if (m_index.isValid()) {
+ return m_index.data(Qt::UserRole + KisResourceModel::Filename).toString();
+ }
+ return QString();
+}
+
+QString KisResourceItem::tooltip()
+{
+ if (m_index.isValid()) {
+ return m_index.data(Qt::UserRole + KisResourceModel::Tooltip).toString();
+ }
+ return QString();
+}
+
+QImage KisResourceItem::thumbnail()
+{
+ if (m_index.isValid()) {
+ return m_index.data(Qt::UserRole + KisResourceModel::Thumbnail).value<QImage>();
+ }
+ return QImage();
+}
+
+KoResourceSP KisResourceItem::resource()
+{
+ if (m_index.isValid() && m_resourceModel) {
+ return m_resourceModel->resourceForIndex(m_index);
+ }
+ return 0;
+}
+
+struct KisResourceIterator::Private
+{
+ Private(KisResourceModel *_resourceModel)
+ : resourceModel(_resourceModel)
+ {}
+
+ KisResourceModel *resourceModel {0};
+ int currentRow {0};
+};
+
+KisResourceIterator::KisResourceIterator(KisResourceModel *resourceModel)
+ : d(new Private(resourceModel))
+{
+}
+
+KisResourceIterator::~KisResourceIterator()
+{
+}
+
+
+bool KisResourceIterator::hasNext() const
+{
+ return d->currentRow < d->resourceModel->rowCount() - 1;
+}
+
+bool KisResourceIterator::hasPrevious() const
+{
+ return d->currentRow > 0 && d->resourceModel->rowCount() > 1 && d->currentRow < d->resourceModel->rowCount();
+}
+
+const KisResourceItemSP KisResourceIterator::next()
+{
+ if (d->currentRow < d->resourceModel->rowCount() - 1) {
+ d->currentRow++;
+ QModelIndex idx = d->resourceModel->index(d->currentRow, 0);
+ return KisResourceItemSP(new KisResourceItem(d->resourceModel, idx));
+ }
+ return KisResourceItemSP(new KisResourceItem(0, QModelIndex()));
+}
+
+const KisResourceItemSP KisResourceIterator::peekNext() const
+{
+ if (d->currentRow < d->resourceModel->rowCount() - 2) {
+ QModelIndex idx = d->resourceModel->index(d->currentRow + 1, 0);
+ return KisResourceItemSP(new KisResourceItem(d->resourceModel, idx));
+ }
+ return KisResourceItemSP(new KisResourceItem(0, QModelIndex()));
+}
+
+const KisResourceItemSP KisResourceIterator::peekPrevious() const
+{
+ if (d->currentRow > 1 && d->resourceModel->rowCount() > 2) {
+ QModelIndex idx = d->resourceModel->index(d->currentRow -1, 0);
+ return KisResourceItemSP(new KisResourceItem(d->resourceModel, idx));
+ }
+ return KisResourceItemSP(new KisResourceItem(0, QModelIndex()));
+}
+
+const KisResourceItemSP KisResourceIterator::previous()
+{
+ if (d->currentRow > 1 && d->resourceModel->rowCount() > 2) {
+ d->currentRow--;
+ QModelIndex idx = d->resourceModel->index(d->currentRow, 0);
+ return KisResourceItemSP(new KisResourceItem(d->resourceModel, idx));
+ }
+ return KisResourceItemSP(new KisResourceItem(0, QModelIndex()));
+
+}
+
+void KisResourceIterator::toBack()
+{
+ d->currentRow = 0;
+}
+
+void KisResourceIterator::toEnd()
+{
+ d->currentRow = d->resourceModel->rowCount() -1;
+}
diff --git a/libs/resources/KisResourceIterator.h b/libs/resources/KisResourceIterator.h
new file mode 100644
index 0000000000..4983abec6b
--- /dev/null
+++ b/libs/resources/KisResourceIterator.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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 KISRESOURCEITERATOR_H
+#define KISRESOURCEITERATOR_H
+
+#include <QImage>
+#include <QString>
+#include <QScopedPointer>
+#include <QModelIndex>
+
+#include <KoResource.h>
+
+class KisResourceModel;
+
+#include <kritaresources_export.h>
+
+/**
+ * @brief The KisResourceItem class represents a resource, but until resource() is called,
+ * the resource is not loaded; the rest of the information comes from the cache database.
+ *
+ * KisResourceItem is used in the KisStoragePlugin's resource iterators to populate the
+ * database.
+ */
+class KRITARESOURCES_EXPORT KisResourceItem {
+private:
+ friend class KisResourceIterator;
+ KisResourceItem(KisResourceModel *resourceModel, const QModelIndex &index);
+public:
+ int id();
+ QString resourceType();
+ QString name();
+ QString filename();
+ QString tooltip();
+ QImage thumbnail();
+ KoResourceSP resource();
+private:
+ KisResourceModel *m_resourceModel;
+ QModelIndex m_index;
+};
+
+typedef QSharedPointer<KisResourceItem> KisResourceItemSP;
+
+/**
+ * @brief The KisResourceIterator class provides an iterator
+ * for a KisResourceModel.
+ */
+class KRITARESOURCES_EXPORT KisResourceIterator
+{
+public:
+ KisResourceIterator(KisResourceModel *resourceModel);
+ ~KisResourceIterator();
+
+ bool hasNext() const;
+ bool hasPrevious() const;
+ const KisResourceItemSP next();
+ const KisResourceItemSP peekNext() const;
+ const KisResourceItemSP peekPrevious() const;
+ const KisResourceItemSP previous();
+ void toBack();
+ void toEnd();
+
+private:
+ struct Private;
+ QScopedPointer<Private> d;
+};
+
+#endif // KisResourceITERATOR_H
diff --git a/libs/resources/KisResourceLoader.cpp b/libs/resources/KisResourceLoader.cpp
new file mode 100644
index 0000000000..271fcdafd9
--- /dev/null
+++ b/libs/resources/KisResourceLoader.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 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 <KisResourceLoader.h>
+#include <QDebug>
+#include <KisMimeDatabase.h>
+
+/**
+ * @return a set of filters ("*.bla,*.foo") that is suitable for filtering
+ * the contents of a directory.
+ */
+QStringList KisResourceLoaderBase::filters() const
+{
+ QStringList filters;
+ Q_FOREACH(const QString &mimeType, mimetypes()) {
+ QStringList suffixes = KisMimeDatabase::suffixesForMimeType(mimeType);
+ Q_FOREACH(const QString &suffix, suffixes) {
+ filters << "*." + suffix;
+ }
+ }
+
+ return filters;
+}
+
+#include "KisResourceLoader.h"
+
diff --git a/libs/resources/KisResourceLoader.h b/libs/resources/KisResourceLoader.h
new file mode 100644
index 0000000000..1301d51beb
--- /dev/null
+++ b/libs/resources/KisResourceLoader.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 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 KISRESOURCELOADER_H
+#define KISRESOURCELOADER_H
+
+#include <QString>
+#include <QStringList>
+#include <QImage>
+#include <QScopedPointer>
+#include <QSharedPointer>
+
+#include <KoResource.h>
+#include <KoID.h>
+
+#include <kritaresources_export.h>
+
+
+/**
+ * @brief The KisResourceLoader class is an abstract interface
+ * class that must be implemented by actual resource classes and
+ * registered with the KisResourceLoaderRegistry.
+ */
+class KRITARESOURCES_EXPORT KisResourceLoaderBase
+{
+public:
+
+ KisResourceLoaderBase(const QString &resourceSubType, const QString &resourceType, const QString &name, const QStringList &mimetypes)
+ {
+ m_resourceSubType = resourceSubType;
+ m_resourceType = resourceType;
+ m_mimetypes = mimetypes;
+ m_name = name;
+ }
+
+ virtual ~KisResourceLoaderBase()
+ {
+ }
+
+ /**
+ * @return a set of filters ("*.bla,*.foo") that is suitable for filtering
+ * the contents of a directory.
+ */
+ QStringList filters() const;
+
+ /**
+ * @return the mimetypes this resource can load
+ */
+ QStringList mimetypes() const
+ {
+ return m_mimetypes;
+ }
+
+ /**
+ * @return the folder in the resource storage where resources
+ * of this type are located
+ */
+ QString resourceType() const
+ {
+ return m_resourceType;
+ }
+
+ QString resourceSubType() const
+ {
+ return id();
+ }
+
+ /// For registration in KisResourceLoaderRegistry
+ QString id() const
+ {
+ return m_resourceSubType;
+ }
+
+ /// The user-friendly name of the category
+ QString name() const
+ {
+ return m_name;
+ }
+
+ /**
+ * Load this resource.
+ * @return a resource if loading the resource succeeded, 0 otherwise
+ */
+ virtual KoResourceSP load(const QString &name, QIODevice &dev, KisResourcesInterfaceSP resourcesInterface) { Q_UNUSED(name); Q_UNUSED(dev); Q_UNUSED(resourcesInterface); return 0; }
+
+private:
+ QString m_resourceSubType;
+ QString m_resourceType;
+ QStringList m_mimetypes;
+ QString m_name;
+
+};
+
+template<typename T>
+class KisResourceLoader : public KisResourceLoaderBase {
+public:
+ KisResourceLoader(const QString &id, const QString &folder, const QString &name, const QStringList &mimetypes)
+ : KisResourceLoaderBase(id, folder, name, mimetypes)
+ {
+ }
+
+ KoResourceSP load(const QString &name, QIODevice &dev, KisResourcesInterfaceSP resourcesInterface) override
+ {
+ QSharedPointer<T> resource = QSharedPointer<T>::create(name);
+ Q_ASSERT(dev.isOpen() && dev.isReadable());
+ if (resource->loadFromDevice(&dev, resourcesInterface)) {
+ return resource;
+ }
+ return 0;
+ }
+};
+
+
+
+#endif // KISRESOURCELOADER_H
diff --git a/libs/resources/KisResourceLoaderRegistry.cpp b/libs/resources/KisResourceLoaderRegistry.cpp
new file mode 100644
index 0000000000..2b9018fe31
--- /dev/null
+++ b/libs/resources/KisResourceLoaderRegistry.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 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 "KisResourceLoaderRegistry.h"
+
+#include <QApplication>
+#include <QString>
+#include <QDebug>
+
+#include <KisResourceCacheDb.h>
+#include <KisMimeDatabase.h>
+
+KisResourceLoaderRegistry::KisResourceLoaderRegistry(QObject *parent)
+ : QObject(parent)
+{
+}
+
+KisResourceLoaderRegistry::~KisResourceLoaderRegistry()
+{
+ qDeleteAll(values());
+}
+
+KisResourceLoaderRegistry* KisResourceLoaderRegistry::instance()
+{
+ KisResourceLoaderRegistry *reg = qApp->findChild<KisResourceLoaderRegistry *>(QString());
+ if (!reg) {
+ reg = new KisResourceLoaderRegistry(qApp);
+ }
+ return reg;
+}
+
+bool KisResourceLoaderRegistry::registerLoader(KisResourceLoaderBase *loader)
+{
+ add(loader);
+ return KisResourceCacheDb::registerResourceType(loader->resourceType());
+}
+
+KisResourceLoaderBase *KisResourceLoaderRegistry::loader(const QString &resourceType, const QString &mimetype) const
+{
+ Q_FOREACH(KisResourceLoaderBase *loader, resourceTypeLoaders(resourceType)) {
+ if (loader->mimetypes().contains(mimetype)) return loader;
+ }
+ return 0;
+}
+
+QVector<KisResourceLoaderBase *> KisResourceLoaderRegistry::resourceTypeLoaders(const QString &resourceType) const
+{
+ QVector<KisResourceLoaderBase *> r;
+ Q_FOREACH(KisResourceLoaderBase *loader, values()) {
+ if (loader->resourceType() == resourceType) {
+ r << loader;
+ }
+ }
+ return r;
+}
+
+QStringList KisResourceLoaderRegistry::filters(const QString &resourceType) const
+{
+ QStringList r;
+ Q_FOREACH(KisResourceLoaderBase *loader, resourceTypeLoaders(resourceType)) {
+ r.append(loader->filters());
+ }
+ r.removeDuplicates();
+ r.sort();
+ return r;
+}
+
+QStringList KisResourceLoaderRegistry::mimeTypes(const QString &resourceType) const
+{
+ QStringList extensions = KisResourceLoaderRegistry::instance()->filters(resourceType);
+ QStringList mimeTypes;
+ Q_FOREACH(const QString &extension, extensions) {
+ mimeTypes << KisMimeDatabase::mimeTypeForSuffix(extension);
+ }
+ mimeTypes.removeDuplicates();
+ mimeTypes.sort();
+ return mimeTypes;
+}
+
+
+
+QStringList KisResourceLoaderRegistry::resourceTypes() const
+{
+ QStringList r;
+ Q_FOREACH(KisResourceLoaderBase *loader, values()) {
+ r << loader->resourceType();
+ }
+ r.removeDuplicates();
+ r.sort();
+
+ return r;
+}
diff --git a/libs/resources/KisResourceLoaderRegistry.h b/libs/resources/KisResourceLoaderRegistry.h
new file mode 100644
index 0000000000..fefaedd15a
--- /dev/null
+++ b/libs/resources/KisResourceLoaderRegistry.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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 KISRESOURCELOADERREGISTRY_H
+#define KISRESOURCELOADERREGISTRY_H
+
+#include <QObject>
+#include <QSet>
+#include <QStringList>
+
+#include <KoGenericRegistry.h>
+#include "KisResourceLoader.h"
+
+#include <kritaresources_export.h>
+
+/**
+ * @brief The KisResourceLoaderRegistry class manages the loader plugins for resources. Every resource can be loaded
+ * by a KisResourceLoader instance. A loader corresponds to a particular file type. Resources are organized in
+ * folders that represent the main type of a certain resource (brushes) and subtypes, that identify a particular
+ * resource format (gbr, gih, png, svg).
+ *
+ * KisResourceLoaderRegistry has full knowlege of all resource types that are defined for Krita.
+ */
+class KRITARESOURCES_EXPORT KisResourceLoaderRegistry : public QObject, public KoGenericRegistry<KisResourceLoaderBase*>
+{
+ Q_OBJECT
+public:
+ ~KisResourceLoaderRegistry() override;
+
+ static KisResourceLoaderRegistry *instance();
+
+ /**
+ * Adds the given loader and registers its type in the database, if it hasn't been registered yet.
+ */
+ bool registerLoader(KisResourceLoaderBase* loader);
+
+ /// @return the first loader for the given resource type and mimetype
+ KisResourceLoaderBase *loader(const QString &resourceType, const QString &mimetype) const;
+
+ /**
+ * @return a list of filename extensions that can be present for the given resource type
+ */
+ QStringList filters(const QString &resourceType) const;
+
+ /**
+ * @return a list of mimetypes that can be loaded for the given resourde type
+ */
+ QStringList mimeTypes(const QString &resourceType) const;
+
+ /**
+ * @return the list of folders for which resource loaders have been registered
+ */
+ QStringList resourceTypes() const;
+
+ /**
+ * @return a list of loader plugins that can handle the resources stored in the folder. A folder can contain multiple subtypes.
+ */
+ QVector<KisResourceLoaderBase*> resourceTypeLoaders(const QString &resourceType) const;
+
+private:
+
+ KisResourceLoaderRegistry(QObject *parent);
+ KisResourceLoaderRegistry(const KisResourceLoaderRegistry&);
+ KisResourceLoaderRegistry operator=(const KisResourceLoaderRegistry&);
+};
+
+#endif // KISRESOURCELOADERREGISTRY_H
diff --git a/libs/resources/KisResourceLocator.cpp b/libs/resources/KisResourceLocator.cpp
new file mode 100644
index 0000000000..c8cef10ffc
--- /dev/null
+++ b/libs/resources/KisResourceLocator.cpp
@@ -0,0 +1,686 @@
+/*
+ * Copyright (C) 2018 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 "KisResourceLocator.h"
+
+#include <QApplication>
+#include <QDebug>
+#include <QList>
+#include <QDir>
+#include <QDirIterator>
+#include <QFileInfo>
+#include <QMessageBox>
+#include <QVersionNumber>
+#include <QElapsedTimer>
+#include <QSqlQuery>
+#include <QSqlError>
+
+#include <kconfig.h>
+#include <kconfiggroup.h>
+#include <ksharedconfig.h>
+#include <klocalizedstring.h>
+
+#include <KritaVersionWrapper.h>
+#include <KisMimeDatabase.h>
+#include <kis_assert.h>
+
+#include "KoResourcePaths.h"
+#include "KisResourceStorage.h"
+#include "KisResourceCacheDb.h"
+#include "KisResourceLoaderRegistry.h"
+#include "KisMemoryStorage.h"
+#include "KisResourceModelProvider.h"
+#include <KisGlobalResourcesInterface.h>
+
+
+const QString KisResourceLocator::resourceLocationKey {"ResourceDirectory"};
+
+
+class KisResourceLocator::Private {
+public:
+ QString resourceLocation;
+ QMap<QString, KisResourceStorageSP> storages;
+ QHash<QPair<QString, QString>, KoResourceSP> resourceCache;
+ QStringList errorMessages;
+};
+
+KisResourceLocator::KisResourceLocator(QObject *parent)
+ : QObject(parent)
+ , d(new Private())
+{
+}
+
+KisResourceLocator *KisResourceLocator::instance()
+{
+ // Not a regular Q_GLOBAL_STATIC, because we want this deleted as
+ // part of the app destructor.
+ KisResourceLocator *locator = qApp->findChild<KisResourceLocator *>(QString());
+ if (!locator) {
+ locator = new KisResourceLocator(qApp);
+ }
+ return locator;
+}
+
+KisResourceLocator::~KisResourceLocator()
+{
+}
+
+KisResourceLocator::LocatorError KisResourceLocator::initialize(const QString &installationResourcesLocation)
+{
+ InitalizationStatus initalizationStatus = InitalizationStatus::Unknown;
+
+ KConfigGroup cfg(KSharedConfig::openConfig(), "");
+ d->resourceLocation = cfg.readEntry(resourceLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+ if (!d->resourceLocation.endsWith('/')) d->resourceLocation += '/';
+
+ QFileInfo fi(d->resourceLocation);
+
+ if (!fi.exists()) {
+ if (!QDir().mkpath(d->resourceLocation)) {
+ d->errorMessages << i18n("1. Could not create the resource location at %1.", d->resourceLocation);
+ return LocatorError::CannotCreateLocation;
+ }
+ initalizationStatus = InitalizationStatus::FirstRun;
+ }
+
+ if (!fi.isWritable()) {
+ d->errorMessages << i18n("2. The resource location at %1 is not writable.", d->resourceLocation);
+ return LocatorError::LocationReadOnly;
+ }
+
+ // Check whether we're updating from an older version
+ if (initalizationStatus != InitalizationStatus::FirstRun) {
+ QFile fi(d->resourceLocation + '/' + "KRITA_RESOURCE_VERSION");
+ if (!fi.exists()) {
+ initalizationStatus = InitalizationStatus::FirstUpdate;
+ }
+ else {
+ fi.open(QFile::ReadOnly);
+ QVersionNumber resource_version = QVersionNumber::fromString(QString::fromUtf8(fi.readAll()));
+ QVersionNumber krita_version = QVersionNumber::fromString(KritaVersionWrapper::versionString());
+ if (krita_version > resource_version) {
+ initalizationStatus = InitalizationStatus::Updating;
+ }
+ else {
+ initalizationStatus = InitalizationStatus::Initialized;
+ }
+ }
+ }
+
+ if (initalizationStatus != InitalizationStatus::Initialized) {
+ KisResourceLocator::LocatorError res = firstTimeInstallation(initalizationStatus, installationResourcesLocation);
+ if (res != LocatorError::Ok) {
+ return res;
+ }
+ initalizationStatus = InitalizationStatus::Initialized;
+ }
+ else {
+ if (!synchronizeDb()) {
+ return LocatorError::CannotSynchronizeDb;
+ }
+ }
+ return LocatorError::Ok;
+}
+
+QStringList KisResourceLocator::errorMessages() const
+{
+ return d->errorMessages;
+}
+
+QString KisResourceLocator::resourceLocationBase() const
+{
+ return d->resourceLocation;
+}
+
+bool KisResourceLocator::resourceCached(QString storageLocation, const QString &resourceType, const QString &filename) const
+{
+ storageLocation = makeStorageLocationAbsolute(storageLocation);
+ QPair<QString, QString> key = QPair<QString, QString> (storageLocation, resourceType + "/" + filename);
+
+ return d->resourceCache.contains(key);
+}
+
+void KisResourceLocator::loadRequiredResources(KoResourceSP resource)
+{
+ QList<KoResourceSP> requiredResources = resource->requiredResources(KisGlobalResourcesInterface::instance());
+ Q_FOREACH (KoResourceSP res, requiredResources) {
+ if (res->resourceId() < 0) {
+ // we put all the embedded resources into the global shared "memory" storage
+ this->addResource(res->resourceType().first, res, "memory");
+ }
+ }
+}
+
+KoResourceSP KisResourceLocator::resource(QString storageLocation, const QString &resourceType, const QString &filename)
+{
+ storageLocation = makeStorageLocationAbsolute(storageLocation);
+
+ QPair<QString, QString> key = QPair<QString, QString> (storageLocation, resourceType + "/" + filename);
+
+ KoResourceSP resource;
+ if (d->resourceCache.contains(key)) {
+ resource = d->resourceCache[key];
+ }
+ else {
+ KisResourceStorageSP storage = d->storages[storageLocation];
+ if (!storage) {
+ qWarning() << "Could not find storage" << storageLocation;
+ return 0;
+ }
+
+ resource = storage->resource(resourceType + "/" + filename);
+ // Try to locate bundle in bundle modificated resources location.
+ if (QFileInfo(storage->location() + "_modified" + "/" + resourceType + "/" + filename).exists()) {
+ QFileInfo bundleLoc(storage->location());
+ storage = d->storages[bundleLoc.path() + "/"];
+ QString bundleFolderLocation(bundleLoc.fileName() + "_modified" + "/" + resourceType + "/" + filename);
+ resource = storage->resource(bundleFolderLocation);
+ key = QPair<QString, QString> (storageLocation, bundleFolderLocation);
+ } else {
+ resource = storage->resource(resourceType + "/" + filename);
+ }
+ if (resource) {
+ KIS_SAFE_ASSERT_RECOVER(!resource->filename().startsWith(resourceType)) {};
+ d->resourceCache[key] = resource;
+
+ // load all the embedded resources into temporary "memory" storage
+ loadRequiredResources(resource);
+ }
+ }
+
+ if (!resource) {
+ qDebug() << "KoResourceSP KisResourceLocator::resource" << storageLocation << resourceType << filename;
+ }
+ Q_ASSERT(resource);
+
+ resource->setStorageLocation(storageLocation);
+ Q_ASSERT(!resource->storageLocation().isEmpty());
+
+ if (resource->resourceId() < 0 || resource->version() < 0) {
+ QSqlQuery q;
+ if (!q.prepare("SELECT resources.id\n"
+ ", resources.version\n"
+ "FROM resources\n"
+ ", storages\n"
+ ", resource_types\n"
+ "WHERE storages.id = resources.storage_id\n"
+ "AND storages.location = :storage_location\n"
+ "AND resource_types.id = resources.resource_type_id\n"
+ "AND resource_types.name = :resource_type\n"
+ "AND resources.filename = :filename")) {
+ qWarning() << "Could not prepare id/version query" << q.lastError();
+
+ }
+
+ q.bindValue(":storage_location", makeStorageLocationRelative(storageLocation));
+ q.bindValue(":resource_type", resourceType);
+ q.bindValue(":filename", filename);
+
+ if (!q.exec()) {
+ qWarning() << "Could not execute id/version quert" << q.lastError() << q.boundValues();
+ }
+
+ if (!q.first()) {
+ qWarning() << "Could not find the resource in the database" << storageLocation << resourceType << filename;
+ }
+
+ resource->setResourceId(q.value(0).toInt());
+ Q_ASSERT(resource->resourceId() >= 0);
+
+ resource->setVersion(q.value(1).toInt());
+ Q_ASSERT(resource->version() >= 0);
+ }
+
+ if (!resource) {
+ qWarning() << "Could not find resource" << resourceType + "/" + filename;
+ return 0;
+ }
+
+ return resource;
+}
+
+KoResourceSP KisResourceLocator::resourceForId(int resourceId)
+{
+ ResourceStorage rs = getResourceStorage(resourceId);
+ KoResourceSP r = resource(rs.storageLocation, rs.resourceType, rs.resourceFileName);
+ return r;
+}
+
+bool KisResourceLocator::removeResource(int resourceId, const QString &/*storageLocation*/)
+{
+ // First remove the resource from the cache
+ ResourceStorage rs = getResourceStorage(resourceId);
+ QPair<QString, QString> key = QPair<QString, QString> (rs.storageLocation, rs.resourceType + "/" + rs.resourceFileName);
+
+ d->resourceCache.remove(key);
+
+ return KisResourceCacheDb::removeResource(resourceId);
+}
+
+bool KisResourceLocator::importResourceFromFile(const QString &resourceType, const QString &fileName, const QString &storageLocation)
+{
+ KisResourceLoaderBase *loader = KisResourceLoaderRegistry::instance()->loader(resourceType, KisMimeDatabase::mimeTypeForFile(fileName));
+ QFile f(fileName);
+ if (!f.open(QFile::ReadOnly)) {
+ qWarning() << "Could not open" << fileName << "for loading";
+ return false;
+ }
+
+ KoResourceSP resource = loader->load(QFileInfo(fileName).fileName(), f, KisGlobalResourcesInterface::instance());
+ if (!resource) {
+ qWarning() << "Could not import" << fileName << ": resource doesn't load.";
+ return false;
+ }
+ KisResourceStorageSP storage = d->storages[makeStorageLocationAbsolute(storageLocation)];
+ Q_ASSERT(storage);
+ if (!storage->addResource(resource)) {
+ qWarning() << "Could not add resource" << resource->filename() << "to the folder storage";
+ return false;
+ }
+
+ return KisResourceCacheDb::addResource(folderStorage(), QFileInfo(resource->filename()).lastModified(), resource, resourceType);
+}
+
+bool KisResourceLocator::addResource(const QString &resourceType, const KoResourceSP resource, const QString &storageLocation)
+{
+ if (!resource || !resource->valid()) return false;
+
+ KisResourceStorageSP storage = d->storages[makeStorageLocationAbsolute(storageLocation)];
+ Q_ASSERT(storage);
+
+ //If we have gotten this far and the resource still doesn't have a filename to save to, we should generate one.
+ if (resource->filename().isEmpty()) {
+ if (storageLocation == "memory") {
+ resource->setFilename("memory/" + resourceType + "/" + resource->name());
+ }
+ else {
+ resource->setFilename(resource->name().split(" ").join("_") + resource->defaultFileExtension());
+ }
+ }
+
+ // Save the resource to the storage storage
+ if (!storage->addResource(resource)) {
+ qWarning() << "Could not add resource" << resource->filename() << "to the folder storage";
+ return false;
+ }
+
+ // And the database
+ return KisResourceCacheDb::addResource(storage,
+ storage->timeStampForResource(resourceType, resource->filename()),
+ resource,
+ resourceType);
+
+}
+
+bool KisResourceLocator::updateResource(const QString &resourceType, const KoResourceSP resource)
+{
+ QString storageLocation = makeStorageLocationAbsolute(resource->storageLocation());
+
+ qDebug() << ">>>>>>>>>>>>>>>> storageLocation"<< storageLocation << "resource storage location" << resource->storageLocation();
+
+ Q_ASSERT(d->storages.contains(storageLocation));
+ Q_ASSERT(resource->resourceId() > -1);
+
+ KisResourceStorageSP storage = d->storages[storageLocation];
+ resource->updateThumbnail();
+ int version = resource->version();
+
+ // This increments the version in the resource
+ if (!storage->addResource(resource)) {
+ qWarning() << "Failed to save the new version of " << resource->name() << "to storage" << storageLocation;
+ return false;
+ }
+
+ // Memory storages don't store versioned resources
+ if (storage->type() == KisResourceStorage::StorageType::Memory) {
+ return true;
+ }
+
+ // It's the storages that keep track of the version
+ Q_ASSERT(resource->version() == version + 1);
+
+ // The version needs already to have been incremented
+ if (!KisResourceCacheDb::addResourceVersion(resource->resourceId(), QDateTime::currentDateTime(), storage, resource)) {
+ qWarning() << "Failed to add a new version of the resource to the database" << resource->name();
+ return false;
+ }
+
+ // Update the resource in the cache
+ QPair<QString, QString> key = QPair<QString, QString> (storageLocation, resourceType + "/" + QFileInfo(resource->filename()).fileName());
+ d->resourceCache[key] = resource;
+
+ return true;
+}
+
+QMap<QString, QVariant> KisResourceLocator::metaDataForResource(int id) const
+{
+ return KisResourceCacheDb::metaDataForId(id, "resources");
+}
+
+bool KisResourceLocator::setMetaDataForResource(int id, QMap<QString, QVariant> map) const
+{
+ return KisResourceCacheDb::updateMetaDataForId(map, id, "resources");
+}
+
+QMap<QString, QVariant> KisResourceLocator::metaDataForStorage(const QString &storageLocation) const
+{
+ QMap<QString, QVariant> metadata;
+ if (!d->storages.contains(makeStorageLocationAbsolute(storageLocation))) {
+ qWarning() << storageLocation << "not in" << d->storages.keys();
+ return metadata;
+ }
+
+ KisResourceStorageSP st = d->storages[makeStorageLocationAbsolute(storageLocation)];
+
+ if (d->storages[makeStorageLocationAbsolute(storageLocation)].isNull()) {
+ return metadata;
+ }
+
+ Q_FOREACH(const QString key, st->metaDataKeys()) {
+ metadata[key] = st->metaData(key);
+ }
+ return metadata;
+}
+
+void KisResourceLocator::setMetaDataForStorage(const QString &storageLocation, QMap<QString, QVariant> map) const
+{
+ Q_ASSERT(d->storages.contains(storageLocation));
+ Q_FOREACH(const QString &key, map.keys()) {
+ d->storages[storageLocation]->setMetaData(key, map[key]);
+ }
+}
+
+bool KisResourceLocator::storageContainsResourceByFile(const QString &storageLocation, const QString &resourceType, const QString &filename) const
+{
+ QSqlQuery q;
+ if (!q.prepare("SELECT *\n"
+ "FROM storages\n"
+ ", resources\n"
+ ", resource_types\n"
+ "WHERE resources.filename = :filename\n"
+ "AND resources.storage_id = storages.id\n"
+ "AND storages.location = :storage_location\n"
+ "AND resources.resource_type_id = resource_types.id\n"
+ "AND resource_types.name = :resource_type"))
+ {
+ qWarning() << "Could not prepare storageCOntainsResourceByFile query" << q.lastError();
+ return false;
+ }
+
+ q.bindValue(":filename", filename);
+ q.bindValue(":storage_location", storageLocation);
+ q.bindValue(":resource_type", resourceType);
+
+ if (!q.exec()) {
+ qWarning() << "Could not execute storageCOntainsResourceByFile query" << q.lastError() << q.boundValues();
+ return false;
+ }
+
+ return q.first();
+}
+
+void KisResourceLocator::purge()
+{
+ d->resourceCache.clear();
+}
+
+bool KisResourceLocator::addStorage(const QString &storageLocation, KisResourceStorageSP storage)
+{
+ Q_ASSERT(!d->storages.contains(storageLocation));
+
+ d->storages[storageLocation] = storage;
+
+ if (!KisResourceCacheDb::addStorage(storage, false)) {
+ d->errorMessages.append(i18n("Could not add %1 to the database", storage->location()));
+ return false;
+ }
+
+ KisResourceModelProvider::resetAllModels();
+ emit storageAdded();
+
+ return true;
+}
+
+bool KisResourceLocator::removeStorage(const QString &document)
+{
+ // Cloned documents have a document storage, but that isn't in the locator.
+ if (!d->storages.contains(document)) return true;
+
+ purge();
+ KisResourceStorageSP storage = d->storages. take(document);
+ if (!KisResourceCacheDb::deleteStorage(storage)) {
+ d->errorMessages.append(i18n("Could not remove storage %1 from the database", storage->location()));
+ return false;
+ }
+ KisResourceModelProvider::resetAllModels();
+
+ emit storageRemoved();
+ return true;
+}
+
+bool KisResourceLocator::hasStorage(const QString &document)
+{
+ return d->storages.contains(document);
+}
+
+KisResourceLocator::LocatorError KisResourceLocator::firstTimeInstallation(InitalizationStatus initalizationStatus, const QString &installationResourcesLocation)
+{
+ emit progressMessage(i18n("Krita is running for the first time. Intialization will take some time."));
+ Q_UNUSED(initalizationStatus);
+
+ Q_FOREACH(const QString &folder, KisResourceLoaderRegistry::instance()->resourceTypes()) {
+ QDir dir(d->resourceLocation + '/' + folder + '/');
+ if (!dir.exists()) {
+ if (!QDir().mkpath(d->resourceLocation + '/' + folder + '/')) {
+ d->errorMessages << i18n("3. Could not create the resource location at %1.", dir.path());
+ return LocatorError::CannotCreateLocation;
+ }
+ }
+ }
+
+ Q_FOREACH(const QString &folder, KisResourceLoaderRegistry::instance()->resourceTypes()) {
+ QDir dir(installationResourcesLocation + '/' + folder + '/');
+ if (dir.exists()) {
+ Q_FOREACH(const QString &entry, dir.entryList(QDir::Files | QDir::Readable)) {
+ QFile f(dir.canonicalPath() + '/'+ entry);
+ if (!QFileInfo(d->resourceLocation + '/' + folder + '/' + entry).exists()) {
+ if (!f.copy(d->resourceLocation + '/' + folder + '/' + entry)) {
+ d->errorMessages << i18n("Could not copy resource %1 to %2", f.fileName(), d->resourceLocation + '/' + folder + '/' + entry);
+ }
+ }
+ }
+ }
+ }
+
+ // And add bundles and adobe libraries
+ QStringList filters = QStringList() << "*.bundle" << "*.abr" << "*.asl";
+ QDirIterator iter(installationResourcesLocation, filters, QDir::Files, QDirIterator::Subdirectories);
+ while (iter.hasNext()) {
+ iter.next();
+ emit progressMessage(i18n("Installing the resources from bundle %1.", iter.filePath()));
+ QFile f(iter.filePath());
+ Q_ASSERT(f.exists());
+ if (!f.copy(d->resourceLocation + '/' + iter.fileName())) {
+ d->errorMessages << i18n("Could not copy resource %1 to %2", f.fileName(), d->resourceLocation);
+ }
+ }
+
+ QFile f(d->resourceLocation + '/' + "KRITA_RESOURCE_VERSION");
+ f.open(QFile::WriteOnly);
+ f.write(KritaVersionWrapper::versionString().toUtf8());
+ f.close();
+
+ if (!initializeDb()) {
+ return LocatorError::CannotInitializeDb;
+ }
+
+ return LocatorError::Ok;
+}
+
+bool KisResourceLocator::initializeDb()
+{
+ emit progressMessage(i18n("Initalizing the resources."));
+ d->errorMessages.clear();
+ findStorages();
+
+ Q_FOREACH(KisResourceStorageSP storage, d->storages) {
+
+ QElapsedTimer t;
+ t.start();
+
+ if (!KisResourceCacheDb::addStorage(storage, (storage->type() == KisResourceStorage::StorageType::Folder ? false : true))) {
+ d->errorMessages.append(i18n("Could not add storage %1 to the cache database", storage->location()));
+ }
+
+ qDebug() << "Adding storage" << storage->location() << "to the database took" << t.elapsed() << "ms";
+ }
+
+ return (d->errorMessages.isEmpty());
+}
+
+void KisResourceLocator::findStorages()
+{
+ d->storages.clear();
+
+ // Add the folder
+ KisResourceStorageSP storage = QSharedPointer<KisResourceStorage>::create(d->resourceLocation);
+ Q_ASSERT(storage->location() == d->resourceLocation);
+ d->storages[d->resourceLocation] = storage;
+
+ // Add the memory storage
+ d->storages["memory"] = QSharedPointer<KisResourceStorage>::create("memory");
+ d->storages["memory"]->setMetaData(KisResourceStorage::s_meta_name, i18n("Temporary Resources"));
+
+ // And add bundles and adobe libraries
+ QStringList filters = QStringList() << "*.bundle" << "*.abr" << "*.asl";
+ QDirIterator iter(d->resourceLocation, filters, QDir::Files, QDirIterator::Subdirectories);
+ while (iter.hasNext()) {
+ iter.next();
+ KisResourceStorageSP storage = QSharedPointer<KisResourceStorage>::create(iter.filePath());
+ d->storages[storage->location()] = storage;
+ }
+}
+
+QList<KisResourceStorageSP> KisResourceLocator::storages() const
+{
+ return d->storages.values();
+}
+
+KisResourceStorageSP KisResourceLocator::storageByLocation(const QString &location) const
+{
+ if (!d->storages.contains(location)) {
+ qWarning() << "No" << location << "storage defined:" << d->storages.keys();
+ return 0;
+ }
+ KisResourceStorageSP storage = d->storages[location];
+ if (!storage || !storage->valid()) {
+ qWarning() << "Could not retrieve the" << location << "storage object or the object is not valid";
+ return 0;
+ }
+
+ return storage;
+}
+
+KisResourceStorageSP KisResourceLocator::folderStorage() const
+{
+ return storageByLocation(d->resourceLocation);
+}
+
+KisResourceStorageSP KisResourceLocator::memoryStorage() const
+{
+ return storageByLocation("memory");
+}
+
+KisResourceLocator::ResourceStorage KisResourceLocator::getResourceStorage(int resourceId) const
+{
+ ResourceStorage rs;
+
+ QSqlQuery q;
+ bool r = q.prepare("SELECT storages.location\n"
+ ", resource_types.name as resource_type\n"
+ ", resources.filename\n"
+ "FROM resources\n"
+ ", storages\n"
+ ", resource_types\n"
+ "WHERE resources.id = :resource_id\n"
+ "AND resources.storage_id = storages.id\n"
+ "AND resource_types.id = resources.resource_type_id");
+ if (!r) {
+ qWarning() << "KisResourceLocator::removeResource: could not prepare query." << q.lastError();
+ return rs;
+ }
+
+
+ q.bindValue(":resource_id", resourceId);
+
+ r = q.exec();
+ if (!r) {
+ qWarning() << "KisResourceLocator::removeResource: could not execute query." << q.lastError();
+ return rs;
+ }
+
+ q.first();
+
+ QString storageLocation = q.value("location").toString();
+ QString resourceType= q.value("resource_type").toString();
+ QString resourceFilename = q.value("filename").toString();
+
+ rs.storageLocation = makeStorageLocationAbsolute(storageLocation);
+ rs.resourceType = resourceType;
+ rs.resourceFileName = resourceFilename;
+
+ return rs;
+}
+
+QString KisResourceLocator::makeStorageLocationAbsolute(QString storageLocation) const
+{
+ if (storageLocation.isEmpty()) {
+ return resourceLocationBase();
+ }
+
+ if (!storageLocation.startsWith('/') && (storageLocation.endsWith("bundle")
+ || storageLocation.endsWith("asl")
+ || storageLocation.endsWith("abr"))) {
+ if (resourceLocationBase().endsWith('/')) {
+ storageLocation = resourceLocationBase() + storageLocation;
+ }
+ else {
+ storageLocation = resourceLocationBase() + '/' + storageLocation;
+ }
+ }
+ return storageLocation;
+}
+
+bool KisResourceLocator::synchronizeDb()
+{
+ d->errorMessages.clear();
+ findStorages();
+ Q_FOREACH(const KisResourceStorageSP storage, d->storages) {
+ if (!KisResourceCacheDb::synchronizeStorage(storage)) {
+ d->errorMessages.append(i18n("Could not synchronize %1 with the database", storage->location()));
+ }
+ }
+ return d->errorMessages.isEmpty();
+}
+
+
+QString KisResourceLocator::makeStorageLocationRelative(QString location) const
+{
+ return location.remove(resourceLocationBase());
+}
diff --git a/libs/resources/KisResourceLocator.h b/libs/resources/KisResourceLocator.h
new file mode 100644
index 0000000000..55a04224c8
--- /dev/null
+++ b/libs/resources/KisResourceLocator.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2018 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 KISRESOURCELOCATOR_H
+#define KISRESOURCELOCATOR_H
+
+#include <QObject>
+#include <QScopedPointer>
+#include <QStringList>
+#include <QString>
+
+#include "kritaresources_export.h"
+
+#include <KisResourceStorage.h>
+
+
+/**
+ * The KisResourceLocator class locates all resource storages (folders,
+ * bundles, various adobe resource libraries) in the resource location.
+ *
+ * The resource location is always a writable folder.
+ *
+ * There is one resource locator which is owned by the QApplication
+ * object.
+ *
+ * The resource location is configurable, but there is only one location
+ * where Krita will look for resources.
+ */
+class KRITARESOURCES_EXPORT KisResourceLocator : public QObject
+{
+ Q_OBJECT
+public:
+
+ // The configuration key that holds the resource location
+ // for this installation of Krita. The location is
+ // QStandardPaths::AppDataLocation by default, but that
+ // can be changed.
+ static const QString resourceLocationKey;
+
+ static KisResourceLocator *instance();
+
+ ~KisResourceLocator();
+
+ enum class LocatorError {
+ Ok,
+ LocationReadOnly,
+ CannotCreateLocation,
+ CannotInitializeDb,
+ CannotSynchronizeDb
+ };
+
+ /**
+ * @brief initialize Setup the resource locator for use.
+ *
+ * @param installationResourcesLocation the place where the resources
+ * that come packaged with Krita reside.
+ */
+ LocatorError initialize(const QString &installationResourcesLocation);
+
+ /**
+ * @brief errorMessages
+ * @return
+ */
+ QStringList errorMessages() const;
+
+ /**
+ * @brief resourceLocationBase is the place where all resource storages (folder,
+ * bundles etc. are located. This is a writable place.
+ * @return the base location for all storages.
+ */
+ QString resourceLocationBase() const;
+
+ /**
+ * @brief purge purges the local resource cache
+ */
+ void purge();
+
+ /**
+ * @brief addStorage Adds a new resource storage to the database. The storage is
+ * will be marked as not pre-installed.
+ * @param storageLocation a unique name for the given storage
+ * @param storage a storage object
+ * @return true if the storage has been added succesfully
+ */
+ bool addStorage(const QString &storageLocation, KisResourceStorageSP storage);
+
+ /**
+ * @brief removeStorage removes the temporary storage from the database
+ * @param document the unique name of the document
+ * @return true is succesful.
+ */
+ bool removeStorage(const QString &storageLocation);
+
+ /**
+ * @brief hasStorage can be used to check whether the given storage already exists
+ * @param storageLocation the name of the storage
+ * @return true if the storage is known
+ */
+ bool hasStorage(const QString &storageLocation);
+
+Q_SIGNALS:
+
+ void progressMessage(const QString&);
+
+ /// Emitted whenever a storage is added
+ void storageAdded();
+
+ /// Emitted whenever a storage is removed
+ void storageRemoved();
+
+private:
+
+ friend class KisResourceModel;
+ friend class KisTagModel;
+ friend class KisStorageModel;
+ friend class TestResourceLocator;
+ friend class TestResourceModel;
+ friend class Resource;
+ friend class KisResourceCacheDb;
+ friend class KisStorageFilterProxyModel;
+
+ /// @return true if the resource is present in the cache, false if it hasn't been loaded
+ bool resourceCached(QString storageLocation, const QString &resourceType, const QString &filename) const;
+
+ /**
+ * @brief resource finds a physical resource in one of the storages
+ * @param storageLocation the storage containing the resource. If empty,
+ * this is the folder storage.
+ *
+ * Note that the resource does not have the version or id field set, so this cannot be used directly,
+ * but only through KisResourceModel.
+ *
+ * @param resourceType the type of the resource
+ * @param filename the filename of the resource including extension, but withou
+ * any paths
+ * @return A resource if found, or 0
+ */
+ KoResourceSP resource(QString storageLocation, const QString &resourceType, const QString &filename);
+
+ /**
+ * @brief resourceForId returns the resource with the given id, or 0 if no such resource exists.
+ * The resource object will have its id set but not its version.
+ * @param resourceId the id
+ */
+ KoResourceSP resourceForId(int resourceId);
+
+ /**
+ * @brief removeResource
+ * @param resourceId
+ * @param optional: the storage that contains the given resource
+ * @return
+ */
+ bool removeResource(int resourceId, const QString &storageLocation = QString());
+
+ /**
+ * @brief importResourceFromFile
+ * @param resourceType
+ * @param fileName
+ * @param storageLocation: optional, the storage where the resource will be stored. Empty means in the default Folder storage.
+ * @return
+ */
+ bool importResourceFromFile(const QString &resourceType, const QString &fileName, const QString &storageLocation = QString());
+
+ /**
+ * @brief addResource adds the given resource to the database and potentially a storage
+ * @param resourceType the type of the resource
+ * @param resource the actual resource object
+ * @param storageLocation the storage where the resource will be saved. By default this is the the default folder storage.
+ * @return true if succesfull
+ */
+ bool addResource(const QString &resourceType, const KoResourceSP resource, const QString &storageLocation = QString());
+
+ /**
+ * @brief updateResource
+ * @param resourceType
+ * @param resource
+ * @return
+ */
+ bool updateResource(const QString &resourceType, const KoResourceSP resource);
+
+ /**
+ * @brief metaDataForResource
+ * @param id
+ * @return
+ */
+ QMap<QString, QVariant> metaDataForResource(int id) const;
+
+ /**
+ * @brief setMetaDataForResource
+ * @param id
+ * @param map
+ * @return
+ */
+ bool setMetaDataForResource(int id, QMap<QString, QVariant> map) const;
+
+ /**
+ * @brief metaDataForStorage
+ * @param storage
+ * @return
+ */
+ QMap<QString, QVariant> metaDataForStorage(const QString &storageLocation) const;
+
+ /**
+ * @brief setMetaDataForStorage
+ * @param storage
+ * @param map
+ */
+ void setMetaDataForStorage(const QString &storageLocation, QMap<QString, QVariant> map) const;
+
+ /**
+ * @brief storageContainsResourceByFile
+ * @param storageLocation
+ * @param filename
+ * @return
+ */
+ bool storageContainsResourceByFile(const QString &storageLocation, const QString &resourceType, const QString &filename) const;
+
+ /**
+ * Loads all the resources required by \p resource into the cache
+ *
+ * loadRequiredResources() also loads embedded resources and adds them
+ * into the database.
+ */
+ void loadRequiredResources(KoResourceSP resource);
+
+ KisResourceLocator(QObject *parent);
+ KisResourceLocator(const KisResourceLocator&);
+ KisResourceLocator operator=(const KisResourceLocator&);
+
+ enum class InitalizationStatus {
+ Unknown, // We don't know whether Krita has run on this system for this resource location yet
+ Initialized, // Everything is ready to start synchronizing the database
+ FirstRun, // Krita hasn't run for this resource location yet
+ FirstUpdate, // Krita was installed, but it's a version from before the resource locator existed, only user-defined resources are present
+ Updating // Krita is updating from an older version with resource locator
+ };
+
+ LocatorError firstTimeInstallation(InitalizationStatus initalizationStatus, const QString &installationResourcesLocation);
+
+ // First time installation
+ bool initializeDb();
+
+ // Synchronize on restarting Krita to see whether the user has added any storages or resources to the resources location
+ bool synchronizeDb();
+
+ void findStorages();
+ QList<KisResourceStorageSP> storages() const;
+
+ KisResourceStorageSP storageByLocation(const QString &location) const;
+ KisResourceStorageSP folderStorage() const;
+ KisResourceStorageSP memoryStorage() const;
+
+ struct ResourceStorage {
+ QString storageLocation;
+ QString resourceType;
+ QString resourceFileName;
+ };
+
+ ResourceStorage getResourceStorage(int resourceId) const;
+ QString makeStorageLocationAbsolute(QString storageLocation) const;
+ QString makeStorageLocationRelative(QString location) const;
+
+
+ class Private;
+ QScopedPointer<Private> d;
+};
+
+#endif // KISRESOURCELOCATOR_H
diff --git a/libs/resources/KisResourceModel.cpp b/libs/resources/KisResourceModel.cpp
new file mode 100644
index 0000000000..439432c468
--- /dev/null
+++ b/libs/resources/KisResourceModel.cpp
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2018 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 "KisResourceModel.h"
+
+#include <QElapsedTimer>
+#include <QBuffer>
+#include <QImage>
+#include <QtSql>
+#include <QStringList>
+
+#include <KisResourceLocator.h>
+#include <KisResourceCacheDb.h>
+
+#include <KisTagModelProvider.h>
+
+
+
+struct KisResourceModel::Private {
+ QSqlQuery resourcesQuery;
+ QSqlQuery tagQuery;
+ QString resourceType;
+ int columnCount {9};
+ int cachedRowCount {-1};
+};
+
+
+//static int s_i = 0;
+
+KisResourceModel::KisResourceModel(const QString &resourceType, QObject *parent)
+ : QAbstractTableModel(parent)
+ , d(new Private)
+{
+ //qDebug() << "ResourceModel" << s_i << resourceType; s_i++;
+
+ d->resourceType = resourceType;
+
+ bool r = d->resourcesQuery.prepare("SELECT resources.id\n"
+ ", resources.storage_id\n"
+ ", resources.name\n"
+ ", resources.filename\n"
+ ", resources.tooltip\n"
+ ", resources.thumbnail\n"
+ ", resources.status\n"
+ ", storages.location\n"
+ ", resources.version\n"
+ ", resource_types.name as resource_type\n"
+ "FROM resources\n"
+ ", resource_types\n"
+ ", storages\n"
+ "WHERE resources.resource_type_id = resource_types.id\n"
+ "AND resources.storage_id = storages.id\n"
+ "AND resource_types.name = :resource_type\n"
+ "AND resources.status = 1\n"
+ "AND storages.active = 1");
+ if (!r) {
+ qWarning() << "Could not prepare KisResourceModel query" << d->resourcesQuery.lastError();
+ }
+ d->resourcesQuery.bindValue(":resource_type", d->resourceType);
+
+ resetQuery();
+
+ r = d->tagQuery.prepare("SELECT tags.id\n"
+ ", tags.url\n"
+ ", tags.name\n"
+ ", tags.comment\n"
+ "FROM tags\n"
+ ", resource_tags\n"
+ "WHERE tags.active > 0\n" // make sure the tag is active
+ "AND tags.id = resource_tags.tag_id\n" // join tags + resource_tags by tag_id
+ "AND resource_tags.resource_id = :resource_id\n"); // make sure we're looking for tags for a specific resource
+ if (!r) {
+ qWarning() << "Could not prepare TagsForResource query" << d->tagQuery.lastError();
+ }
+
+}
+
+KisResourceModel::~KisResourceModel()
+{
+ delete d;
+}
+
+int KisResourceModel::columnCount(const QModelIndex &/*parent*/) const
+{
+ return d->columnCount;
+}
+
+QVariant KisResourceModel::data(const QModelIndex &index, int role) const
+{
+
+ QVariant v;
+ if (!index.isValid()) return v;
+
+ if (index.row() > rowCount()) return v;
+ if (index.column() > d->columnCount) return v;
+
+ bool pos = const_cast<KisResourceModel*>(this)->d->resourcesQuery.seek(index.row());
+
+ if (pos) {
+ switch(role) {
+ case Qt::DisplayRole:
+ {
+ switch(index.column()) {
+ case Id:
+ return d->resourcesQuery.value("id");
+ case StorageId:
+ return d->resourcesQuery.value("storage_id");
+ case Name:
+ return d->resourcesQuery.value("name");
+ case Filename:
+ return d->resourcesQuery.value("filename");
+ case Tooltip:
+ return d->resourcesQuery.value("tooltip");
+ case Thumbnail:
+ {
+ QByteArray ba = d->resourcesQuery.value("thumbnail").toByteArray();
+ QBuffer buf(&ba);
+ buf.open(QBuffer::ReadOnly);
+ QImage img;
+ img.load(&buf, "PNG");
+ return QVariant::fromValue<QImage>(img);
+ }
+ case Status:
+ return d->resourcesQuery.value("status");
+ case Location:
+ return d->resourcesQuery.value("location");
+ case ResourceType:
+ return d->resourcesQuery.value("resource_type");
+ default:
+ ;
+ };
+ Q_FALLTHROUGH();
+ }
+ case Qt::DecorationRole:
+ {
+ if (index.column() == Thumbnail) {
+ QByteArray ba = d->resourcesQuery.value("thumbnail").toByteArray();
+ QBuffer buf(&ba);
+ buf.open(QBuffer::ReadOnly);
+ QImage img;
+ img.load(&buf, "PNG");
+ return QVariant::fromValue<QImage>(img);
+ }
+ return QVariant();
+ }
+ case Qt::ToolTipRole:
+ Q_FALLTHROUGH();
+ case Qt::StatusTipRole:
+ Q_FALLTHROUGH();
+ case Qt::WhatsThisRole:
+ return d->resourcesQuery.value("tooltip");
+ case Qt::UserRole + Id:
+ return d->resourcesQuery.value("id");
+ case Qt::UserRole + StorageId:
+ return d->resourcesQuery.value("storage_id");
+ case Qt::UserRole + Name:
+ return d->resourcesQuery.value("name");
+ case Qt::UserRole + Filename:
+ return d->resourcesQuery.value("filename");
+ case Qt::UserRole + Tooltip:
+ return d->resourcesQuery.value("tooltip");
+ case Qt::UserRole + Thumbnail:
+ {
+ QByteArray ba = d->resourcesQuery.value("thumbnail").toByteArray();
+ QBuffer buf(&ba);
+ buf.open(QBuffer::ReadOnly);
+ QImage img;
+ img.load(&buf, "PNG");
+ return QVariant::fromValue<QImage>(img);
+ }
+ case Qt::UserRole + Status:
+ return d->resourcesQuery.value("status");
+ case Qt::UserRole + Location:
+ return d->resourcesQuery.value("location");
+ case Qt::UserRole + ResourceType:
+ return d->resourcesQuery.value("resource_type");
+ case Qt::UserRole + Tags:
+ {
+ QVector<KisTagSP> tags = tagsForResource(d->resourcesQuery.value("id").toInt());
+ QStringList tagNames;
+ Q_FOREACH(const KisTagSP tag, tags) {
+ tagNames << tag->name();
+ }
+ return tagNames;
+ }
+ case Qt::UserRole + Dirty:
+ {
+ QString storageLocation = d->resourcesQuery.value("location").toString();
+ QString filename = d->resourcesQuery.value("filename").toString();
+
+ // An uncached resource has not been loaded, so it cannot be dirty
+ if (!KisResourceLocator::instance()->resourceCached(storageLocation, d->resourceType, filename)) {
+ return false;
+ }
+ else {
+ // Now we have to check the resource, but that's cheap since it's been loaded in any case
+ KoResourceSP resource = resourceForIndex(index);
+ return resource->isDirty();
+ }
+ }
+ case Qt::UserRole + MetaData:
+ {
+ QMap<QString, QVariant> r = KisResourceLocator::instance()->metaDataForResource(d->resourcesQuery.value("id").toInt());
+ return r;
+ }
+ case Qt::UserRole + KoResourceRole:
+ {
+ KoResourceSP tag = resourceForIndex(index);
+ QVariant response;
+ response.setValue(tag);
+ return response;
+ }
+
+ default:
+ ;
+ }
+ }
+ return v;
+}
+
+QVariant KisResourceModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ QVariant v = QVariant();
+ if (role != Qt::DisplayRole) {
+ return v;
+ }
+ if (orientation == Qt::Horizontal) {
+ switch(section) {
+ case Id:
+ v = i18n("Id");
+ break;
+ case StorageId:
+ v = i18n("Storage ID");
+ break;
+ case Name:
+ v = i18n("Name");
+ break;
+ case Filename:
+ v = i18n("File Name");
+ break;
+ case Tooltip:
+ v = i18n("Tooltip");
+ break;
+ case Thumbnail:
+ v = i18n("Image");
+ break;
+ case Status:
+ v = i18n("Status");
+ break;
+ case Location:
+ v = i18n("Location");
+ break;
+ case ResourceType:
+ v = i18n("Resource Type");
+ break;
+ default:
+ v = QString::number(section);
+ }
+ return v;
+ }
+ return QAbstractItemModel::headerData(section, orientation, role);
+}
+
+//static int s_i2 {0};
+
+KoResourceSP KisResourceModel::resourceForIndex(QModelIndex index) const
+{
+ KoResourceSP resource = 0;
+
+ if (!index.isValid()) return resource;
+ if (index.row() > rowCount()) return resource;
+ if (index.column() > d->columnCount) return resource;
+
+ //qDebug() << "KisResourceModel::resourceForIndex" << s_i2 << d->resourceType; s_i2++;
+
+ bool pos = const_cast<KisResourceModel*>(this)->d->resourcesQuery.seek(index.row());
+ if (pos) {
+ int id = d->resourcesQuery.value("id").toInt();
+ resource = resourceForId(id);
+ }
+ return resource;
+}
+
+KoResourceSP KisResourceModel::resourceForId(int id) const
+{
+ return KisResourceLocator::instance()->resourceForId(id);
+}
+
+KoResourceSP KisResourceModel::resourceForFilename(QString filename) const
+{
+ KoResourceSP resource = 0;
+
+ QSqlQuery q;
+ bool r = q.prepare("SELECT resources.id AS id\n"
+ "FROM resources\n"
+ ", resource_types\n"
+ ", storages\n"
+ "WHERE resources.resource_type_id = resource_types.id\n"
+ "AND resources.storage_id = storages.id\n"
+ "AND resources.filename = :resource_filename\n"
+ "AND resource_types.name = :resource_type\n"
+ "AND resources.status = 1\n"
+ "AND storages.active = 1");
+ if (!r) {
+ qWarning() << "Could not prepare KisResourceModel query for resource name" << q.lastError();
+ }
+ q.bindValue(":resource_filename", filename);
+ q.bindValue(":resource_type", d->resourceType);
+
+ r = q.exec();
+ if (!r) {
+ qWarning() << "Could not select" << d->resourceType << "resources by filename" << q.lastError() << q.boundValues();
+ }
+
+ if (q.first()) {
+ int id = q.value("id").toInt();
+ resource = KisResourceLocator::instance()->resourceForId(id);
+ }
+ return resource;
+}
+
+KoResourceSP KisResourceModel::resourceForName(QString name) const
+{
+ KoResourceSP resource = 0;
+
+ QSqlQuery q;
+ bool r = q.prepare("SELECT resources.id AS id\n"
+ "FROM resources\n"
+ ", resource_types\n"
+ ", storages\n"
+ "WHERE resources.resource_type_id = resource_types.id\n"
+ "AND resources.storage_id = storages.id\n"
+ "AND resources.name = :resource_name\n"
+ "AND resource_types.name = :resource_type\n"
+ "AND resources.status = 1\n"
+ "AND storages.active = 1");
+ if (!r) {
+ qWarning() << "Could not prepare KisResourceModel query for resource name" << q.lastError();
+ }
+ q.bindValue(":resource_type", d->resourceType);
+ q.bindValue(":resource_name", name);
+
+ r = q.exec();
+ if (!r) {
+ qWarning() << "Could not select" << d->resourceType << "resources by name" << q.lastError() << q.boundValues();
+ }
+
+ if (q.first()) {
+ int id = q.value("id").toInt();
+ resource = KisResourceLocator::instance()->resourceForId(id);
+ }
+ return resource;
+}
+
+
+KoResourceSP KisResourceModel::resourceForMD5(const QByteArray md5sum) const
+{
+ KoResourceSP resource = 0;
+
+ QSqlQuery q;
+ bool r = q.prepare("SELECT resource_id AS id\n"
+ "FROM versioned_resources\n"
+ "WHERE md5sum = :md5sum");
+ if (!r) {
+ qWarning() << "Could not prepare KisResourceModel query for resource md5" << q.lastError();
+ }
+ q.bindValue(":md5sum", md5sum.toHex());
+
+ r = q.exec();
+ if (!r) {
+ qWarning() << "Could not select" << d->resourceType << "resources by md5" << q.lastError() << q.boundValues();
+ }
+
+ if (q.first()) {
+ int id = q.value("id").toInt();
+ resource = KisResourceLocator::instance()->resourceForId(id);
+ }
+ return resource;
+}
+
+//static int s_i3 {0};
+
+QModelIndex KisResourceModel::indexFromResource(KoResourceSP resource) const
+{
+ if (!resource || !resource->valid()) return QModelIndex();
+
+ //qDebug() << "KisResourceModel::indexFromResource" << s_i3 << d->resourceType; s_i3++;
+
+ // For now a linear seek to find the first resource with the right id
+ d->resourcesQuery.first();
+ do {
+ if (d->resourcesQuery.value("id").toInt() == resource->resourceId()) {
+ return createIndex(d->resourcesQuery.at(), 0);
+ }
+ } while (d->resourcesQuery.next());
+
+ return QModelIndex();
+}
+
+//static int s_i4 {0};
+
+bool KisResourceModel::removeResource(const QModelIndex &index)
+{
+ if (index.row() > rowCount()) return false;
+ if (index.column() > d->columnCount) return false;
+
+ //qDebug() << "KisResourceModel::removeResource" << s_i4 << d->resourceType; s_i4++;
+
+ bool pos = d->resourcesQuery.seek(index.row());
+ if (!pos) return false;
+
+ int resourceId = d->resourcesQuery.value("id").toInt();
+ if (!KisResourceLocator::instance()->removeResource(resourceId)) {
+ qWarning() << "Failed to remove resource" << resourceId;
+ return false;
+ }
+ return resetQuery();
+}
+
+//static int s_i5 {0};
+
+bool KisResourceModel::removeResource(KoResourceSP resource)
+{
+ if (!resource || !resource->valid()) return false;
+
+ //qDebug() << "KisResourceModel::remvoeResource 2" << s_i5 << d->resourceType; s_i5++;
+
+ if (!KisResourceLocator::instance()->removeResource(resource->resourceId())) {
+ qWarning() << "Failed to remove resource" << resource->resourceId();
+ return false;
+ }
+ return resetQuery();
+}
+
+//static int s_i6 {0};
+
+bool KisResourceModel::importResourceFile(const QString &filename)
+{
+ //qDebug() << "KisResourceModel::importResource" << s_i6 << d->resourceType; s_i6++;
+
+ if (!KisResourceLocator::instance()->importResourceFromFile(d->resourceType, filename)) {
+ qWarning() << "Failed to import resource" << filename;
+ return false;
+ }
+ return resetQuery();
+}
+
+//static int s_i7 {0};
+
+bool KisResourceModel::addResource(KoResourceSP resource, const QString &storageId)
+{
+ if (!resource || !resource->valid()) {
+ qWarning() << "Cannot add resource. Resource is null or not valid";
+ return false;
+ }
+
+ //qDebug() << "KisResourceModel::addResource" << s_i7 << d->resourceType; s_i7++;
+
+ if (!KisResourceLocator::instance()->addResource(d->resourceType, resource, storageId)) {
+ qWarning() << "Failed to add resource" << resource->name();
+ return false;
+ }
+ return resetQuery();
+}
+
+//static int s_i8 {0};
+
+bool KisResourceModel::updateResource(KoResourceSP resource)
+{
+ if (!resource || !resource->valid()) {
+ qWarning() << "Cannot update resource. Resource is null or not valid";
+ return false;
+ }
+
+ //qDebug() << "KisResourceModel::updateResource" << s_i8 << d->resourceType; s_i8++;
+
+ if (!KisResourceLocator::instance()->updateResource(d->resourceType, resource)) {
+ qWarning() << "Failed to update resource" << resource;
+ return false;
+ }
+ return resetQuery();
+}
+
+bool KisResourceModel::renameResource(KoResourceSP resource, const QString &name)
+{
+ if (!resource || !resource->valid() || name.isEmpty()) {
+ qWarning() << "Cannot rename resources. Resource is NULL or not valid or name is empty";
+ return false;
+ }
+ resource->setName(name);
+ if (!KisResourceLocator::instance()->updateResource(d->resourceType, resource)) {
+ qWarning() << "Failed to rename resource" << resource << name;
+ return false;
+ }
+ return resetQuery();
+}
+
+//static int s_i9 {0};
+
+bool KisResourceModel::setResourceMetaData(KoResourceSP resource, QMap<QString, QVariant> metadata)
+{
+ //qDebug() << "KisResourceModel::setResourceMetaData" << s_i9 << d->resourceType; s_i9++;
+ Q_ASSERT(resource->resourceId() > -1);
+ return KisResourceLocator::instance()->setMetaDataForResource(resource->resourceId(), metadata);
+}
+
+bool KisResourceModel::resetQuery()
+{
+ QElapsedTimer t;
+ t.start();
+
+ emit beforeResourcesLayoutReset(QModelIndex());
+
+ beginResetModel();
+ bool r = d->resourcesQuery.exec();
+ if (!r) {
+ qWarning() << "Could not select" << d->resourceType << "resources" << d->resourcesQuery.lastError() << d->resourcesQuery.boundValues();
+ }
+ d->cachedRowCount = -1;
+
+ endResetModel();
+ emit afterResourcesLayoutReset();
+
+ qDebug() << "KisResourceModel::resetQuery for" << d->resourceType << "took" << t.elapsed() << "ms";
+
+ return r;
+}
+
+QVector<KisTagSP> KisResourceModel::tagsForResource(int resourceId) const
+{
+ return KisTagModelProvider::tagModel(d->resourceType)->tagsForResource(resourceId);
+}
+
+int KisResourceModel::rowCount(const QModelIndex &) const
+{
+ if (d->cachedRowCount < 0) {
+ QSqlQuery q;
+ q.prepare("SELECT count(*)\n"
+ "FROM resources\n"
+ ", resource_types\n"
+ ", storages\n"
+ "WHERE resources.resource_type_id = resource_types.id\n"
+ "AND resource_types.name = :resource_type\n"
+ "AND resources.storage_id = storages.id\n"
+ "AND resources.status = 1\n"
+ "AND storages.active = 1");
+ q.bindValue(":resource_type", d->resourceType);
+ q.exec();
+ q.first();
+
+ const_cast<KisResourceModel*>(this)->d->cachedRowCount = q.value(0).toInt();
+ }
+
+ return d->cachedRowCount;
+}
diff --git a/libs/resources/KisResourceModel.h b/libs/resources/KisResourceModel.h
new file mode 100644
index 0000000000..7e15f38087
--- /dev/null
+++ b/libs/resources/KisResourceModel.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2018 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 KISRESOURCEMODEL_H
+#define KISRESOURCEMODEL_H
+
+#include <QAbstractTableModel>
+
+#include <kritaresources_export.h>
+
+#include <KoResource.h>
+#include <KisTag.h>
+
+/**
+ * KisAbstractResourceModel defines the interface for accessing resources
+ * that is used in KisResourceModel and the various filter/proxy models
+ */
+class KRITARESOURCES_EXPORT KisAbstractResourceModel {
+
+public:
+
+ virtual ~KisAbstractResourceModel(){}
+
+ /**
+ * @brief resourceForIndex
+ * @param index
+ * @return
+ */
+ virtual KoResourceSP resourceForIndex(QModelIndex index = QModelIndex()) const = 0;
+
+ /**
+ * @brief indexFromResource
+ * @param resource
+ * @return
+ */
+ virtual QModelIndex indexFromResource(KoResourceSP resource) const = 0;
+
+ /**
+ * @brief removeResource
+ * @param index
+ * @return
+ */
+ virtual bool removeResource(const QModelIndex &index) = 0;
+
+ /**
+ * @brief importResourceFile
+ * @param filename
+ * @return
+ */
+ virtual bool importResourceFile(const QString &filename) = 0;
+
+ /**
+ * @brief addResource adds the given resource to the database and storage
+ * @param resource the resource itself
+ * @param storageId the id of the storage (could be "memory" for temporary
+ * resources, the document's storage id for document storages or empty to save
+ * to the default resources folder
+ * @return true if adding the resoruce succeded.
+ */
+ virtual bool addResource(KoResourceSP resource, const QString &storageId = QString()) = 0;
+
+ /**
+ * @brief updateResource
+ * @param resource
+ * @return
+ */
+ virtual bool updateResource(KoResourceSP resource) = 0;
+
+ /**
+ * @brief renameResource name the given resource. The resource will have its
+ * name field reset, will be saved to the storage and there will be a new
+ * version created in the database.
+ * @param resource The resource to rename
+ * @param name The new name
+ * @return true if the operation succeeded.
+ */
+ virtual bool renameResource(KoResourceSP resource, const QString &name) = 0;
+
+ /**
+ * @brief removeResource
+ * @param resource
+ * @return
+ */
+ virtual bool removeResource(KoResourceSP resource) = 0;
+
+ /**
+ * @brief setResourceMetaData
+ * @param metadata
+ * @return
+ */
+ virtual bool setResourceMetaData(KoResourceSP resource, QMap<QString, QVariant> metadata) = 0;
+
+
+};
+
+/**
+ * @brief The KisResourceModel class provides access to the cache database
+ * for a particular resource type. Instances should be retrieved using
+ * KisResourceModelProvider.
+ */
+class KRITARESOURCES_EXPORT KisResourceModel : public QAbstractTableModel, public KisAbstractResourceModel
+{
+ Q_OBJECT
+public:
+
+ /**
+ * @brief The Columns enum indexes the columns in the model. To get
+ * the thumbnail for a particular resource, create the index with
+ * QModelIndex(row, Thumbnail).
+ */
+ enum Columns {
+ Id = 0,
+ StorageId,
+ Name,
+ Filename,
+ Tooltip,
+ Thumbnail,
+ Status,
+ Location,
+ ResourceType,
+ Tags,
+ /// A larger thumbnail for displaying in a tooltip. 200x200 or so.
+ LargeThumbnail,
+ /// A dirty resource is one that has been modified locally but not saved
+ Dirty,
+ /// MetaData is a map of key, value pairs that is associated with this resource
+ MetaData,
+ KoResourceRole
+ };
+
+private:
+ friend class KisResourceModelProvider;
+ friend class TestResourceModel;
+ KisResourceModel(const QString &resourceType, QObject *parent = 0);
+
+public:
+
+ ~KisResourceModel() override;
+
+// QAbstractItemModel API
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+
+// Resources API
+ /**
+ * @brief resourceForIndex returns a properly versioned and id's resource object
+ */
+ KoResourceSP resourceForIndex(QModelIndex index = QModelIndex()) const override;
+ KoResourceSP resourceForId(int id) const;
+
+ /**
+ * resourceForFilename returns the first resource with the given filename that
+ * is active and is in an active store. Note that the filename does not include
+ * a path to the storage, and if there are resources with the same filename
+ * in several active storages, only one resource is returned.
+ *
+ * @return a resource if one is found, or 0 if none are found
+ */
+ KoResourceSP resourceForFilename(QString fileName) const;
+
+ /**
+ * resourceForName returns the first resource with the given name that
+ * is active and is in an active store. Note that if there are resources
+ * with the same name in several active storages, only one resource
+ * is returned.
+ *
+ * @return a resource if one is found, or 0 if none are found
+ */
+ KoResourceSP resourceForName(QString name) const;
+ KoResourceSP resourceForMD5(const QByteArray md5sum) const;
+
+ QModelIndex indexFromResource(KoResourceSP resource) const override;
+ bool removeResource(const QModelIndex &index) override;
+ bool removeResource(KoResourceSP resource) override;
+ bool importResourceFile(const QString &filename) override;
+ bool addResource(KoResourceSP resource, const QString &storageId = QString()) override;
+ bool updateResource(KoResourceSP resource) override;
+ bool renameResource(KoResourceSP resource, const QString &name) override;
+ bool setResourceMetaData(KoResourceSP resource, QMap<QString, QVariant> metadata) override;
+ QVector<KisTagSP> tagsForResource(int resourceId) const;
+
+Q_SIGNALS:
+
+ // XXX: emit these signals
+ void beforeResourcesLayoutReset(QModelIndex activateAfterReformat);
+ void afterResourcesLayoutReset();
+
+private:
+
+ bool resetQuery();
+
+ struct Private;
+ Private *const d;
+
+};
+
+#endif // KISRESOURCEMODEL_H
diff --git a/libs/resources/KisResourceModelProvider.cpp b/libs/resources/KisResourceModelProvider.cpp
new file mode 100644
index 0000000000..0c79333506
--- /dev/null
+++ b/libs/resources/KisResourceModelProvider.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include "KisResourceModelProvider.h"
+
+#include "KisResourceModel.h"
+#include "KoResource.h"
+
+#include <memory>
+
+#include <QGlobalStatic>
+
+Q_GLOBAL_STATIC(KisResourceModelProvider, s_instance)
+
+struct KisResourceModelProvider::Private
+{
+ QMap<QString, KisResourceModel*> resourceModels;
+};
+
+KisResourceModelProvider::KisResourceModelProvider()
+ : d(new Private())
+{
+}
+
+KisResourceModelProvider::~KisResourceModelProvider()
+{
+ qDeleteAll(d->resourceModels);
+ delete d;
+}
+
+KisResourceModel *KisResourceModelProvider::resourceModel(const QString &resourceType)
+{
+ if (!s_instance->d->resourceModels.contains(resourceType)) {
+ s_instance->d->resourceModels[resourceType] = new KisResourceModel(resourceType);
+ }
+ return s_instance->d->resourceModels[resourceType];
+}
+
+void KisResourceModelProvider::resetAllModels()
+{
+ Q_FOREACH(KisResourceModel *model, s_instance->d->resourceModels.values()) {
+ model->resetQuery();
+ }
+}
+
+void KisResourceModelProvider::resetModel(const QString& resourceType)
+{
+ QMap<QString, KisResourceModel*>::iterator found
+ = s_instance->d->resourceModels.find(resourceType);
+
+ if (found != s_instance->d->resourceModels.end())
+ {
+ found.value()->resetQuery();
+ }
+}
diff --git a/libs/resources/KisResourceModelProvider.h b/libs/resources/KisResourceModelProvider.h
new file mode 100644
index 0000000000..12d99e5083
--- /dev/null
+++ b/libs/resources/KisResourceModelProvider.h
@@ -0,0 +1,50 @@
+/*
+ * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef KISRESOURCEMODELPROVIDER_H
+#define KISRESOURCEMODELPROVIDER_H
+
+#include <qglobal.h>
+
+#include "kritaresources_export.h"
+
+class KisResourceModel;
+
+/**
+ * KisResourceModelProvider should be used to retrieve resource models.
+ * For every resource type, there is only one instance of the resource model,
+ * so all views on these models show the same state.
+ */
+class KRITARESOURCES_EXPORT KisResourceModelProvider
+{
+public:
+ KisResourceModelProvider();
+ ~KisResourceModelProvider();
+
+ static KisResourceModel *resourceModel(const QString &resourceType);
+ static void resetAllModels();
+ static void resetModel(const QString& resourceType);
+
+private:
+
+ struct Private;
+ Private *const d;
+
+ Q_DISABLE_COPY(KisResourceModelProvider)
+};
+
+#endif // KISRESOURCEMODELPROVIDER_H
diff --git a/libs/resources/KisResourceSearchBoxFilter.cpp b/libs/resources/KisResourceSearchBoxFilter.cpp
new file mode 100644
index 0000000000..4a46a9782f
--- /dev/null
+++ b/libs/resources/KisResourceSearchBoxFilter.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2019 Agata Cacko <cacko.azh@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 "KisResourceSearchBoxFilter.h"
+
+
+#include <QRegExp>
+#include <QRegularExpression>
+#include <QList>
+#include <QSet>
+
+
+class Q_DECL_HIDDEN KisResourceSearchBoxFilter::Private
+{
+public:
+ Private()
+ : searchTokenizer("\\s*,+\\s*")
+ {}
+
+ QRegularExpression searchTokenizer;
+
+ QChar tagBegin {'['};
+ QChar tagEnd {']'};
+ QChar exactMatchBeginEnd {'"'};
+
+ QSet<QString> tagNamesIncluded;
+ QSet<QString> tagNamesExcluded;
+
+ QList<QString> resourceNamesPartsIncluded;
+ QList<QString> resourceNamesPartsExcluded;
+ QSet<QString> resourceExactMatchesIncluded;
+ QSet<QString> resourceExactMatchesExcluded;
+
+ QString filter;
+};
+
+
+KisResourceSearchBoxFilter::KisResourceSearchBoxFilter()
+ : d(new Private())
+{
+
+}
+
+QString cutOutDelimeters(QString text)
+{
+ QString response(text);
+ response = response.remove(0, 1);
+ response = response.left(response.length() - 1);
+ return response;
+}
+
+void KisResourceSearchBoxFilter::setFilter(const QString& filter)
+{
+ d->filter = QString(filter);
+ initializeFilterData();
+}
+
+
+bool KisResourceSearchBoxFilter::matchesResource(const QString &resourceName)
+{
+ // exact matches
+ if (d->resourceExactMatchesIncluded.count() > 0
+ && !d->resourceExactMatchesIncluded.contains(resourceName)) {
+ return false;
+ }
+ if (d->resourceExactMatchesExcluded.contains(resourceName)) {
+ return false;
+ }
+ // partial name matches
+ if (d->resourceNamesPartsIncluded.count() > 0) {
+ Q_FOREACH(const QString& partialName, d->resourceNamesPartsIncluded) {
+ if (!resourceName.contains(partialName)) {
+ return false;
+ }
+ }
+ }
+ Q_FOREACH(const QString& partialName, d->resourceNamesPartsExcluded) {
+ if (resourceName.contains(partialName)) {
+ return false;
+ }
+ }
+ // tags matches
+
+ return true;
+}
+
+bool KisResourceSearchBoxFilter::isEmpty()
+{
+ return d->filter.isEmpty();
+}
+
+void KisResourceSearchBoxFilter::clearFilterData()
+{
+
+ d->tagNamesIncluded.clear();
+ d->tagNamesExcluded.clear();
+ d->resourceNamesPartsIncluded.clear();
+ d->resourceNamesPartsExcluded.clear();
+ d->resourceExactMatchesIncluded.clear();
+ d->resourceExactMatchesExcluded.clear();
+
+}
+
+void KisResourceSearchBoxFilter::initializeFilterData()
+{
+ clearFilterData();
+
+ QString tempFilter(d->filter);
+
+ QStringList parts = tempFilter.split(d->searchTokenizer, QString::SkipEmptyParts);
+ Q_FOREACH(const QString& partFor, parts) {
+ QString part(partFor);
+
+ bool included = true;
+ if (part.startsWith('!')) {
+ part.remove(0, 1);
+ included = false;
+ }
+
+ if (part.startsWith(d->tagBegin) && part.endsWith(d->tagEnd)) {
+ QString tagMatchCaptured = cutOutDelimeters(part);
+ if (included) {
+ d->tagNamesIncluded.insert(tagMatchCaptured);
+ } else {
+ d->tagNamesExcluded.insert(tagMatchCaptured);
+ }
+ } else if (part.startsWith(d->exactMatchBeginEnd) && part.endsWith(d->exactMatchBeginEnd)) {
+ QString exactMatchCaptured = cutOutDelimeters(part);
+ if (included) {
+ d->resourceExactMatchesIncluded.insert(exactMatchCaptured);
+ } else {
+ d->resourceExactMatchesExcluded.insert(exactMatchCaptured);
+ }
+ } else {
+ if (included) {
+ d->resourceNamesPartsIncluded.append(part);
+ } else {
+ d->resourceNamesPartsExcluded.append(part);
+ }
+ }
+ }
+
+}
diff --git a/libs/resources/KisResourceSearchBoxFilter.h b/libs/resources/KisResourceSearchBoxFilter.h
new file mode 100644
index 0000000000..8e7c6a3486
--- /dev/null
+++ b/libs/resources/KisResourceSearchBoxFilter.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019 Agata Cacko <cacko.azh@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_RESOURCE_SEARCH_BOX_FILTER_H
+#define KIS_RESOURCE_SEARCH_BOX_FILTER_H
+
+
+#include "kritaresources_export.h"
+
+#include <QString>
+
+/**
+ * XXX: Apidox
+ *
+ */
+class KRITARESOURCES_EXPORT KisResourceSearchBoxFilter
+{
+
+public:
+
+ KisResourceSearchBoxFilter();
+ void setFilter(const QString& filter);
+ bool matchesResource(const QString& resourceName);
+ bool isEmpty();
+
+private:
+
+ void initializeFilterData();
+ void clearFilterData();
+
+ class Private;
+ Private * d;
+
+};
+
+
+#endif
diff --git a/libs/resources/KisResourceStorage.cpp b/libs/resources/KisResourceStorage.cpp
new file mode 100644
index 0000000000..262ea9e12b
--- /dev/null
+++ b/libs/resources/KisResourceStorage.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2018 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 "KisResourceStorage.h"
+
+#include <QFileInfo>
+#include <QDebug>
+
+#include <quazip.h>
+
+#include "KisFolderStorage.h"
+#include "KisBundleStorage.h"
+#include "KisMemoryStorage.h"
+
+const QString KisResourceStorage::s_meta_generator("meta:generator");
+const QString KisResourceStorage::s_meta_author("dc:author");
+const QString KisResourceStorage::s_meta_title("dc:title");
+const QString KisResourceStorage::s_meta_description("dc:description");
+const QString KisResourceStorage::s_meta_initial_creator("meta:initial-creator");
+const QString KisResourceStorage::s_meta_creator("cd:creator");
+const QString KisResourceStorage::s_meta_creation_date("meta:creation-data");
+const QString KisResourceStorage::s_meta_dc_date("meta:dc-date");
+const QString KisResourceStorage::s_meta_user_defined("meta:meta-userdefined");
+const QString KisResourceStorage::s_meta_name("meta:name");
+const QString KisResourceStorage::s_meta_value("meta:value");
+const QString KisResourceStorage::s_meta_version("meta:bundle-version");
+
+Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance);
+
+KisStoragePluginRegistry::KisStoragePluginRegistry()
+{
+ m_storageFactoryMap[KisResourceStorage::StorageType::Folder] = new KisStoragePluginFactory<KisFolderStorage>();
+ m_storageFactoryMap[KisResourceStorage::StorageType::Memory] = new KisStoragePluginFactory<KisMemoryStorage>();
+ m_storageFactoryMap[KisResourceStorage::StorageType::Bundle] = new KisStoragePluginFactory<KisBundleStorage>();
+}
+
+void KisStoragePluginRegistry::addStoragePluginFactory(KisResourceStorage::StorageType storageType, KisStoragePluginFactoryBase *factory)
+{
+ m_storageFactoryMap[storageType] = factory;
+}
+
+KisStoragePluginRegistry *KisStoragePluginRegistry::instance()
+{
+ return s_instance;
+}
+
+class KisResourceStorage::Private {
+public:
+ QString name;
+ QString location;
+ bool valid {false};
+ KisResourceStorage::StorageType storageType {KisResourceStorage::StorageType::Unknown};
+ QSharedPointer<KisStoragePlugin> storagePlugin;
+};
+
+KisResourceStorage::KisResourceStorage(const QString &location)
+ : d(new Private())
+{
+ d->location = location;
+ d->name = QFileInfo(d->location).fileName();
+ QFileInfo fi(d->location);
+ if (fi.isDir()) {
+ d->storagePlugin.reset(KisStoragePluginRegistry::instance()->m_storageFactoryMap[StorageType::Folder]->create(location));
+ d->storageType = StorageType::Folder;
+ d->valid = fi.isWritable();
+ }
+ else if (d->location.endsWith(".bundle")) {
+ d->storagePlugin.reset(KisStoragePluginRegistry::instance()->m_storageFactoryMap[StorageType::Bundle]->create(location));
+ d->storageType = StorageType::Bundle;
+ // XXX: should we also check whether there's a valid metadata entry? Or is this enough?
+ d->valid = (fi.isReadable() && QuaZip(d->location).open(QuaZip::mdUnzip));
+ } else if (d->location.endsWith(".abr")) {
+ d->storagePlugin.reset(KisStoragePluginRegistry::instance()->m_storageFactoryMap[StorageType::AdobeBrushLibrary]->create(location));
+ d->storageType = StorageType::AdobeBrushLibrary;
+ d->valid = fi.isReadable();
+ } else if (d->location.endsWith(".asl")) {
+ d->storagePlugin.reset(KisStoragePluginRegistry::instance()->m_storageFactoryMap[StorageType::AdobeStyleLibrary]->create(location));
+ d->storageType = StorageType::AdobeStyleLibrary;
+ d->valid = fi.isReadable();
+ }
+ else if (!d->location.isEmpty()) {
+ d->storagePlugin.reset(KisStoragePluginRegistry::instance()->m_storageFactoryMap[StorageType::Memory]->create(location));
+ d->name = location;
+ d->storageType = StorageType::Memory;
+ d->valid = true;
+ }
+ else {
+ d->valid = false;
+ }
+}
+
+KisResourceStorage::~KisResourceStorage()
+{
+}
+
+KisResourceStorage::KisResourceStorage(const KisResourceStorage &rhs)
+ : d(new Private)
+{
+ *this = rhs;
+}
+
+KisResourceStorage &KisResourceStorage::operator=(const KisResourceStorage &rhs)
+{
+ if (this != &rhs) {
+ d->name = rhs.d->name;
+ d->location = rhs.d->location;
+ d->storageType = rhs.d->storageType;
+ if (d->storageType == StorageType::Memory) {
+ d->storagePlugin = QSharedPointer<KisMemoryStorage>(new KisMemoryStorage(*dynamic_cast<KisMemoryStorage*>(rhs.d->storagePlugin.data())));
+ }
+ else {
+ d->storagePlugin = rhs.d->storagePlugin;
+ }
+ d->valid = false;
+ }
+ return *this;
+}
+
+KisResourceStorageSP KisResourceStorage::clone() const
+{
+ return KisResourceStorageSP(new KisResourceStorage(*this));
+}
+
+QString KisResourceStorage::name() const
+{
+ return d->name;
+}
+
+QString KisResourceStorage::location() const
+{
+ return d->location;
+}
+
+KisResourceStorage::StorageType KisResourceStorage::type() const
+{
+ return d->storageType;
+}
+
+QImage KisResourceStorage::thumbnail() const
+{
+ return d->storagePlugin->thumbnail();
+}
+
+QDateTime KisResourceStorage::timestamp() const
+{
+ return d->storagePlugin->timestamp();
+}
+
+QDateTime KisResourceStorage::timeStampForResource(const QString &resourceType, const QString &filename) const
+{
+ QFileInfo li(d->location);
+ if (li.suffix().toLower() == "bundle") {
+ QFileInfo bf(d->location + "_modified/" + resourceType + "/" + filename);
+ if (bf.exists()) {
+ return bf.lastModified();
+ }
+ } else if (QFileInfo(d->location + "/" + resourceType + "/" + filename).exists()) {
+ return QFileInfo(d->location + "/" + resourceType + "/" + filename).lastModified();
+ }
+ return this->timestamp();
+}
+
+KisResourceStorage::ResourceItem KisResourceStorage::resourceItem(const QString &url)
+{
+ return d->storagePlugin->resourceItem(url);
+}
+
+KoResourceSP KisResourceStorage::resource(const QString &url)
+{
+ return d->storagePlugin->resource(url);
+}
+
+QSharedPointer<KisResourceStorage::ResourceIterator> KisResourceStorage::resources(const QString &resourceType) const
+{
+ return d->storagePlugin->resources(resourceType);
+}
+
+QSharedPointer<KisResourceStorage::TagIterator> KisResourceStorage::tags(const QString &resourceType) const
+{
+ return d->storagePlugin->tags(resourceType);
+}
+
+bool KisResourceStorage::addTag(const QString &resourceType, KisTagSP tag)
+{
+ return d->storagePlugin->addTag(resourceType, tag);
+}
+
+bool KisResourceStorage::addResource(KoResourceSP resource)
+{
+ return d->storagePlugin->addResource(resource->resourceType().first, resource);
+}
+
+void KisResourceStorage::setMetaData(const QString &key, const QVariant &value)
+{
+ d->storagePlugin->setMetaData(key, value);
+}
+
+bool KisResourceStorage::valid() const
+{
+ return d->valid;
+}
+
+QStringList KisResourceStorage::metaDataKeys() const
+{
+ return d->storagePlugin->metaDataKeys();
+}
+
+QVariant KisResourceStorage::metaData(const QString &key) const
+{
+ return d->storagePlugin->metaData(key);
+}
+
+bool KisStorageVersioningHelper::addVersionedResource(const QString &filename, const QString &saveLocation, KoResourceSP resource)
+{
+ // Find a new filename for the resource if it already exists: we do not rename old resources, but rename updated resources
+ if (!QFileInfo(filename).exists()) {
+ // Simply save it
+ QFile f(filename);
+ if (!f.open(QFile::WriteOnly)) {
+ qWarning() << "Could not open resource file for writing" << filename;
+ return false;
+ }
+ if (!resource->saveToDevice(&f)) {
+ qWarning() << "Could not save resource file" << filename;
+ return false;
+ }
+ f.close();
+ }
+ else {
+ QFileInfo fi(filename);
+
+ // Save the new resource
+ QString newFilename = fi.baseName() +
+ "."
+ + QString("%1").arg(resource->version() + 1, 4, 10, QChar('0'))
+ + "."
+ + fi.suffix();
+ QFile f(saveLocation + "/" + newFilename);
+
+ if (!f.open(QFile::WriteOnly)) {
+ qWarning() << "Could not open resource file for writing" << newFilename;
+ return false;
+ }
+ if (!resource->saveToDevice(&f)) {
+ qWarning() << "Could not save resource file" << newFilename;
+ return false;
+ }
+ resource->setFilename(newFilename);
+ f.close();
+ }
+ return true;
+};
+
diff --git a/libs/resources/KisResourceStorage.h b/libs/resources/KisResourceStorage.h
new file mode 100644
index 0000000000..90dea338d1
--- /dev/null
+++ b/libs/resources/KisResourceStorage.h
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2018 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 KISRESOURCESTORAGE_H
+#define KISRESOURCESTORAGE_H
+
+#include <QSharedPointer>
+#include <QScopedPointer>
+#include <QString>
+#include <QDateTime>
+#include <QMap>
+
+#include <KoResource.h>
+#include <KisTag.h>
+
+#include <klocalizedstring.h>
+
+#include <kritaresources_export.h>
+
+class KisStoragePlugin;
+
+class KRITARESOURCES_EXPORT KisStoragePluginFactoryBase
+{
+public:
+ virtual ~KisStoragePluginFactoryBase(){}
+ virtual KisStoragePlugin *create(const QString &/*location*/) { return 0; }
+};
+
+template<typename T>
+class KRITARESOURCES_EXPORT KisStoragePluginFactory : public KisStoragePluginFactoryBase
+{
+public:
+ KisStoragePlugin *create(const QString &location) override {
+ return new T(location);
+ }
+};
+
+class KisResourceStorage;
+typedef QSharedPointer<KisResourceStorage> KisResourceStorageSP;
+
+
+/**
+ * The KisResourceStorage class is the base class for
+ * places where resources can be stored. Examples are
+ * folders, bundles or Adobe resource libraries like
+ * ABR files.
+ */
+class KRITARESOURCES_EXPORT KisResourceStorage
+{
+public:
+
+ /// A resource item is simply an entry in the storage,
+ struct ResourceItem {
+
+ virtual ~ResourceItem() {}
+ QString url;
+ QString folder;
+ QString resourceType;
+ QDateTime lastModified;
+ };
+
+ class TagIterator
+ {
+ public:
+ virtual ~TagIterator() {}
+ virtual bool hasNext() const = 0;
+ /// The iterator is only valid if next() has been called at least once.
+ virtual void next() = 0;
+
+ /// The untranslated name of the tag, to be used for making connections to resources
+ virtual QString url() const = 0;
+ /// The translated name of the tag, to be shown in the GUI
+ virtual QString name() const = 0;
+ /// An extra, optional comment for the tag
+ virtual QString comment() const = 0;
+
+ /// A tag object on which we can set properties and which we can save
+ virtual KisTagSP tag() const = 0;
+ };
+
+ class ResourceIterator
+ {
+ public:
+
+ virtual ~ResourceIterator() {}
+
+ virtual bool hasNext() const = 0;
+ /// The iterator is only valid if next() has been called at least once.
+ virtual void next() = 0;
+
+ virtual QString url() const = 0;
+ virtual QString type() const = 0;
+ virtual QDateTime lastModified() const = 0;
+ /// This only loads the resource when called
+ virtual KoResourceSP resource() const = 0;
+ };
+
+ enum class StorageType : int {
+ Unknown = 1,
+ Folder = 2,
+ Bundle = 3,
+ AdobeBrushLibrary = 4,
+ AdobeStyleLibrary = 5,
+ Memory = 6
+ };
+
+ static QString storageTypeToString(StorageType storageType) {
+ switch (storageType) {
+ case StorageType::Unknown:
+ return i18n("Unknown");
+ case StorageType::Folder:
+ return i18n("Folder");
+ case StorageType::Bundle:
+ return i18n("Bundle");
+ case StorageType::AdobeBrushLibrary:
+ return i18n("Adobe Brush Library");
+ case StorageType::AdobeStyleLibrary:
+ return i18n("Adobe Style Library");
+ case StorageType::Memory:
+ return i18n("Memory");
+ default:
+ return i18n("Invalid");
+ }
+ }
+
+
+ static QString storageTypeToUntranslatedString(StorageType storageType) {
+ switch (storageType) {
+ case StorageType::Unknown:
+ return ("Unknown");
+ case StorageType::Folder:
+ return ("Folder");
+ case StorageType::Bundle:
+ return ("Bundle");
+ case StorageType::AdobeBrushLibrary:
+ return ("Adobe Brush Library");
+ case StorageType::AdobeStyleLibrary:
+ return ("Adobe Style Library");
+ case StorageType::Memory:
+ return ("Memory");
+ default:
+ return ("Invalid");
+ }
+ }
+
+
+ KisResourceStorage(const QString &location);
+ ~KisResourceStorage();
+ KisResourceStorage(const KisResourceStorage &rhs);
+ KisResourceStorage &operator=(const KisResourceStorage &rhs);
+ KisResourceStorageSP clone() const;
+
+ /// The filename of the storage if it's a bundle or Adobe Library. This can
+ /// also be empty (for the folder storage) or "memory" for the storage for
+ /// temporary resources, a UUID for storages associated with documents.
+ QString name() const;
+
+ /// The absolute location of the storage
+ QString location() const;
+
+ /// true if the storage exists and can be used
+ bool valid() const;
+
+ /// The type of the storage
+ StorageType type() const;
+
+ /// The icond for the storage
+ QImage thumbnail() const;
+
+ /// The time and date when the storage was last modified, or created
+ /// for memory storages.
+ QDateTime timestamp() const;
+
+ /// The time and date when the resource was last modified
+ /// For filestorage
+ QDateTime timeStampForResource(const QString &resourceType, const QString &filename) const;
+
+ /// And entry in the storage; this is not the loaded resource
+ ResourceItem resourceItem(const QString &url);
+
+ /// The loaded resource for an entry in the storage
+ KoResourceSP resource(const QString &url);
+
+ /// An iterator over all the resources in the storage
+ QSharedPointer<ResourceIterator> resources(const QString &resourceType) const;
+
+ /// An iterator over all the tags in the resource
+ QSharedPointer<TagIterator> tags(const QString &resourceType) const;
+
+ /// Adds a tag to the storage, however, it does not store the links between
+ /// tags and resources.
+ bool addTag(const QString &resourceType, KisTagSP tag);
+
+ /// Adds the given resource to the storage.
+ bool addResource(KoResourceSP resource);
+
+ static const QString s_meta_generator;
+ static const QString s_meta_author;
+ static const QString s_meta_title;
+ static const QString s_meta_description;
+ static const QString s_meta_initial_creator;
+ static const QString s_meta_creator;
+ static const QString s_meta_creation_date;
+ static const QString s_meta_dc_date;
+ static const QString s_meta_user_defined;
+ static const QString s_meta_name;
+ static const QString s_meta_value;
+ static const QString s_meta_version;
+
+ void setMetaData(const QString &key, const QVariant &value);
+ QStringList metaDataKeys() const;
+ QVariant metaData(const QString &key) const;
+
+private:
+
+ class Private;
+ QScopedPointer<Private> d;
+};
+
+
+
+inline QDebug operator<<(QDebug dbg, const KisResourceStorageSP storage)
+{
+ if (storage.isNull()) {
+ dbg.nospace() << "[RESOURCESTORAGE] NULL";
+ }
+ else {
+ dbg.nospace() << "[RESOURCESTORAGE] Name: " << storage->name()
+ << " Version: " << storage->location()
+ << " Valid: " << storage->valid()
+ << " Storage: " << KisResourceStorage::storageTypeToString(storage->type())
+ << " Timestamp: " << storage->timestamp();
+ }
+ return dbg.space();
+}
+
+class KRITARESOURCES_EXPORT KisStoragePluginRegistry {
+public:
+ KisStoragePluginRegistry();
+ void addStoragePluginFactory(KisResourceStorage::StorageType storageType, KisStoragePluginFactoryBase *factory);
+ static KisStoragePluginRegistry *instance();
+private:
+ friend class KisResourceStorage;
+ QMap<KisResourceStorage::StorageType, KisStoragePluginFactoryBase*> m_storageFactoryMap;
+
+};
+
+class KisStorageVersioningHelper {
+public:
+ static bool addVersionedResource(const QString &filename, const QString &saveLocation, KoResourceSP resource);
+
+};
+
+
+#endif // KISRESOURCESTORAGE_H
diff --git a/libs/resources/KisResourceTypeModel.cpp b/libs/resources/KisResourceTypeModel.cpp
new file mode 100644
index 0000000000..0121d4d2a6
--- /dev/null
+++ b/libs/resources/KisResourceTypeModel.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2018 boud <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 "KisResourceTypeModel.h"
+#include <QtSql>
+
+#include "KisResourceLoader.h"
+#include "KisResourceLoaderRegistry.h"
+
+struct KisResourceTypeModel::Private {
+ int cachedRowCount {-1};
+ QSqlQuery query;
+};
+
+
+KisResourceTypeModel::KisResourceTypeModel(QObject *parent)
+ : QAbstractTableModel(parent)
+ , d(new Private)
+{
+ prepareQuery();
+}
+
+KisResourceTypeModel::~KisResourceTypeModel()
+{
+ delete d;
+}
+
+int KisResourceTypeModel::rowCount(const QModelIndex &/*parent*/) const
+{
+ if (d->cachedRowCount < 0) {
+ QSqlQuery q;
+ q.prepare("SELECT count(*)\n"
+ "FROM resource_types\n");
+ q.exec();
+ q.first();
+
+ const_cast<KisResourceTypeModel*>(this)->d->cachedRowCount = q.value(0).toInt();
+ }
+ return d->cachedRowCount;
+}
+
+int KisResourceTypeModel::columnCount(const QModelIndex &/*parent*/) const
+{
+ return 3;
+}
+
+QVariant KisResourceTypeModel::data(const QModelIndex &index, int role) const
+{
+ QVariant v;
+ if (!index.isValid()) return v;
+
+ if (index.row() > rowCount()) return v;
+ if (index.column() > (int)Name) return v;
+
+ bool pos = d->query.seek(index.row());
+
+ if (pos) {
+ QString id = d->query.value("id").toString();
+ QString resourceType = d->query.value("name").toString();
+ QString name = resourceType;
+ QVector<KisResourceLoaderBase *> loaders = KisResourceLoaderRegistry::instance()->resourceTypeLoaders(resourceType);
+ Q_ASSERT(loaders.size() > 0);
+ name = loaders.first()->name();
+
+ switch(role) {
+ case Qt::DisplayRole:
+ {
+ switch(index.column()) {
+ case Id:
+ return id;
+ case ResourceType:
+ return resourceType;
+ case Name:
+ default:
+ return name;
+ }
+ }
+ case Qt::UserRole + Id:
+ return id;
+ case Qt::UserRole + ResourceType:
+ return resourceType;
+ case Qt::UserRole + Name:
+ return name;
+ default:
+ ;
+ }
+ }
+ return v;
+}
+
+bool KisResourceTypeModel::prepareQuery()
+{
+ beginResetModel();
+ bool r = d->query.prepare("SELECT id\n"
+ ", name\n"
+ "FROM resource_types\n");
+ if (!r) {
+ qWarning() << "Could not prepare KisResourceTypeModel query" << d->query.lastError();
+ }
+ r = d->query.exec();
+ if (!r) {
+ qWarning() << "Could not execute KisResourceTypeModel query" << d->query.lastError();
+ }
+ d->cachedRowCount = -1;
+ endResetModel();
+ return r;
+}
diff --git a/libs/resources/KisResourceTypeModel.h b/libs/resources/KisResourceTypeModel.h
new file mode 100644
index 0000000000..d3be6a5cce
--- /dev/null
+++ b/libs/resources/KisResourceTypeModel.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018 boud <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 KISRESOURCETYPEMODEL_H
+#define KISRESOURCETYPEMODEL_H
+
+#include <QAbstractTableModel>
+#include <QStyledItemDelegate>
+#include <QObject>
+
+#include "kritaresources_export.h"
+
+/**
+ * KisResourceTypeModel provides a view on the various resource types
+ * defined in the database. This should be the same list as available
+ * from KisResourceLoaderRegistry.
+ */
+class KRITARESOURCES_EXPORT KisResourceTypeModel : public QAbstractTableModel
+{
+ Q_OBJECT
+public:
+
+ enum Columns {
+ Id = 0,
+ ResourceType,
+ Name,
+ };
+
+ KisResourceTypeModel(QObject *parent = 0);
+ ~KisResourceTypeModel() override;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+private:
+
+ bool prepareQuery();
+
+ struct Private;
+ Private* const d;
+
+};
+
+#endif // KISRESOURCETYPEMODEL_H
diff --git a/libs/resources/KisResourceTypes.h b/libs/resources/KisResourceTypes.h
new file mode 100644
index 0000000000..286580d9d6
--- /dev/null
+++ b/libs/resources/KisResourceTypes.h
@@ -0,0 +1,55 @@
+/*
+ * 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 KISRESOURCETYPES_H
+#define KISRESOURCETYPES_H
+
+#include <QString>
+
+/**
+ * These namespaces define the type keys and sub-type keys for resource types.
+ * The type keys correspond to folders in the resource folder, the sub-type
+ * keys to different types that have their own resource loader instance.
+ */
+namespace ResourceType {
+ static const QString PaintOpPresets {"paintoppresets"};
+ static const QString Brushes {"brushes"};
+ static const QString Gradients {"gradients"};
+ static const QString Palettes {"palettes"};
+ static const QString Patterns {"patterns"};
+ static const QString Workspaces {"workspaces"};
+ static const QString Symbols {"symbols"};
+ static const QString WindowLayouts {"windowlayouts"};
+ static const QString Sessions {"sessions"};
+ static const QString GamutMasks {"gamutmasks"};
+ static const QString FilterEffects {"ko_effects"};
+ static const QString TaskSets {"tasksets"};
+ static const QString LayerStyles {"layerstyles"};
+}
+
+namespace ResourceSubType {
+ static const QString AbrBrushes {"abr_brushes"};
+ static const QString GbrBrushes {"gbr_brushes"};
+ static const QString GihBrushes {"gih_brushes"};
+ static const QString SvgBrushes {"svg_brushes"};
+ static const QString PngBrushes {"png_brushes"};
+ static const QString SegmentedGradients {"segmented_gradients"};
+ static const QString StopGradients {"stop_gradients"};
+}
+
+#endif // KISRESOURCETYPES_H
diff --git a/libs/resources/KisResourcesInterface.cpp b/libs/resources/KisResourcesInterface.cpp
new file mode 100644
index 0000000000..0419812fd7
--- /dev/null
+++ b/libs/resources/KisResourcesInterface.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020 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 "KisResourcesInterface.h"
+
+
+#include <QString>
+#include "kis_assert.h"
+#include "KisResourcesInterface_p.h"
+
+#include "kis_debug.h"
+
+KisResourcesInterface::KisResourcesInterface()
+ : d_ptr(new KisResourcesInterfacePrivate)
+{
+}
+
+KisResourcesInterface::KisResourcesInterface(KisResourcesInterfacePrivate *dd)
+ : d_ptr(dd)
+{
+}
+
+KisResourcesInterface::~KisResourcesInterface()
+{
+
+}
+
+KisResourcesInterface::ResourceSourceAdapter &KisResourcesInterface::source(const QString &type) const
+{
+ Q_D(const KisResourcesInterface);
+
+ // use double-locking for fetching the source
+
+ ResourceSourceAdapter *source = 0;
+
+ {
+ QReadLocker l(&d->lock);
+ source = d->findExistingSource(type);
+ if (source) return *source;
+ }
+
+ {
+ QWriteLocker l(&d->lock);
+ source = d->findExistingSource(type);
+ if (source) return *source;
+
+ source = createSourceImpl(type);
+
+ std::unique_ptr<ResourceSourceAdapter> sourcePtr(source);
+ d->sourceAdapters.emplace(type, std::move(sourcePtr));
+ }
+
+ KIS_ASSERT(source);
+ return *source;
+}
+
+KisResourcesInterface::ResourceSourceAdapter::ResourceSourceAdapter()
+{
+}
+
+KisResourcesInterface::ResourceSourceAdapter::~ResourceSourceAdapter()
+{
+}
diff --git a/libs/resources/KisResourcesInterface.h b/libs/resources/KisResourcesInterface.h
new file mode 100644
index 0000000000..313405407b
--- /dev/null
+++ b/libs/resources/KisResourcesInterface.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2020 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 KISRESOURCESINTERFACE_H
+#define KISRESOURCESINTERFACE_H
+
+#include "kritaresources_export.h"
+
+#include <QScopedPointer>
+#include <KoResource.h>
+#include <QHash>
+
+class QString;
+class QByteArray;
+class KisResourcesInterfacePrivate;
+
+/**
+ * @brief a provider-like interface class for accessing resource sources in Krita.
+ *
+ * Main differences to KoResourceServer and KisResourceModel:
+ *
+ * 1) It is apolymorphic class. Therefore, we are not obliged to pass
+ a pointer to the global gui-only resource storage everywhere. Instead,
+ we can create temporary storages and pass them to the strokes, when needed.
+
+ 2) The class doesn't depend on any specific resource types. Its baseline
+ implementation of resourceInterface->source(type) returns a source
+ working with KoResourceSP only. But when needed, the caller may request
+ a typed version via resourcesInterface->source<KisBrush>(type). It
+ will instantiate a templated wrapper **in the caller's** object file,
+ not in kritaresources library. It solves linking problem:
+ we have a source for KisBrush objects in kritaresources library,
+ even though this library doesn't link kritabrush.
+
+ 3) Since strokes may have local storages for the resources, we operate
+ with resources sources using shared pointers, not raw pointers.
+ */
+class KRITARESOURCES_EXPORT KisResourcesInterface
+{
+public:
+ class KRITARESOURCES_EXPORT ResourceSourceAdapter
+ {
+ public:
+ ResourceSourceAdapter();
+ virtual ~ResourceSourceAdapter();
+
+ virtual KoResourceSP resourceForFilename(const QString& filename) const = 0;
+ virtual KoResourceSP resourceForName(const QString& name) const = 0;
+ virtual KoResourceSP resourceForMD5(const QByteArray& md5) const = 0;
+ virtual KoResourceSP fallbackResource() const = 0;
+
+ private:
+ Q_DISABLE_COPY(ResourceSourceAdapter);
+ };
+
+ template <typename T>
+ class TypedResourceSourceAdapter
+ {
+ public:
+ TypedResourceSourceAdapter(ResourceSourceAdapter *adapter)
+ : m_source(adapter)
+ {
+ }
+
+ QSharedPointer<T> resourceForFilename(const QString& filename) const
+ {
+ return m_source->resourceForFilename(filename).dynamicCast<T>();
+ }
+
+ QSharedPointer<T> resourceForName(const QString& name) const
+ {
+ return m_source->resourceForName(name).dynamicCast<T>();
+ }
+
+ QSharedPointer<T> resourceForMD5(const QByteArray& md5) const
+ {
+ return m_source->resourceForMD5(md5).dynamicCast<T>();
+ }
+
+ QSharedPointer<T> fallbackResource() const
+ {
+ return m_source->fallbackResource().dynamicCast<T>();
+ }
+
+ protected:
+ ResourceSourceAdapter *m_source;
+ };
+
+public:
+ KisResourcesInterface();
+ virtual ~KisResourcesInterface();
+
+ /**
+ * A basic implementation that returns a source for a specific type
+ * of the resource. Please take into account that this source object will
+ * return un-casted resources of type KoResourceSP. If you want to have a
+ * proper resource (in most of the cases), use a `server<T>(type)` instead.
+ */
+ ResourceSourceAdapter& source(const QString &type) const;
+
+ /**
+ * The main fetcher of resource source for resources of a specific type.
+ *
+ * Usage:
+ *
+ * \code{.cpp}
+ *
+ * auto source = resourceInterface->source<KisBrush>(ResourceType::Brushes);
+ * KisBrushSP brush = source.resourceByMd5(md5)
+ *
+ * \endcode
+ *
+ */
+ template<typename T>
+ TypedResourceSourceAdapter<T> source(const QString &type) const {
+ return TypedResourceSourceAdapter<T>(&this->source(type));
+ }
+
+protected:
+ KisResourcesInterface(KisResourcesInterfacePrivate *dd);
+ virtual ResourceSourceAdapter* createSourceImpl(const QString &type) const = 0;
+
+protected:
+ QScopedPointer<KisResourcesInterfacePrivate> d_ptr;
+
+private:
+ Q_DECLARE_PRIVATE(KisResourcesInterface)
+};
+
+using KisResourcesInterfaceSP = QSharedPointer<KisResourcesInterface>;
+
+#endif // KISRESOURCESINTERFACE_H
diff --git a/libs/resources/KisResourcesInterface_p.h b/libs/resources/KisResourcesInterface_p.h
new file mode 100644
index 0000000000..d91c3b1efd
--- /dev/null
+++ b/libs/resources/KisResourcesInterface_p.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 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 KisResourcesInterface_P_H
+#define KisResourcesInterface_P_H
+
+#include "kritaresources_export.h"
+#include "KisResourcesInterface.h"
+#include <unordered_map>
+#include <memory>
+
+#include <QReadWriteLock>
+#include <QReadLocker>
+#include <QWriteLocker>
+
+#include "kis_assert.h"
+
+/// added to Qt in 5.14.0
+/// https://codereview.qt-project.org/c/qt/qtbase/+/261819
+
+#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
+namespace std
+{
+ template<> struct hash<QString>
+ {
+ std::size_t operator()(const QString &s) const noexcept {
+ return qHash(s);
+ }
+ };
+}
+#endif
+
+class KRITARESOURCES_EXPORT KisResourcesInterfacePrivate
+{
+public:
+ mutable std::unordered_map<QString,
+ std::unique_ptr<
+ KisResourcesInterface::ResourceSourceAdapter>> sourceAdapters;
+ mutable QReadWriteLock lock;
+
+ KisResourcesInterface::ResourceSourceAdapter* findExistingSource(const QString &type) const {
+ auto it = this->sourceAdapters.find(type);
+ if (it != this->sourceAdapters.end()) {
+ KIS_ASSERT(bool(it->second));
+
+ return it->second.get();
+ }
+
+ return nullptr;
+ }
+};
+
+#endif // KisResourcesInterface_P_H
diff --git a/libs/resources/KisStorageFilterProxyModel.cpp b/libs/resources/KisStorageFilterProxyModel.cpp
new file mode 100644
index 0000000000..82210f9b11
--- /dev/null
+++ b/libs/resources/KisStorageFilterProxyModel.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 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 "KisStorageFilterProxyModel.h"
+
+#include <QDebug>
+#include <KisResourceModel.h>
+#include <kis_debug.h>
+#include <KisResourceSearchBoxFilter.h>
+#include <KisResourceLocator.h>
+
+struct KisStorageFilterProxyModel::Private
+{
+ FilterType filterType {KisStorageFilterProxyModel::ByStorageType};
+ QVariant filter;
+
+};
+
+KisStorageFilterProxyModel::KisStorageFilterProxyModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+ , d(new Private)
+{
+}
+
+KisStorageFilterProxyModel::~KisStorageFilterProxyModel()
+{
+ delete d;
+}
+
+KisResourceStorageSP KisStorageFilterProxyModel::storageForIndex(QModelIndex index) const
+{
+ KisStorageModel *source = dynamic_cast<KisStorageModel*>(sourceModel());
+ if (source) {
+ return source->storageForIndex(mapToSource(index));
+ }
+ return 0;
+}
+
+void KisStorageFilterProxyModel::setFilter(KisStorageFilterProxyModel::FilterType filterType, QVariant filter)
+{
+ d->filter = filter;
+ d->filterType = filterType;
+}
+
+
+bool KisStorageFilterProxyModel::filterAcceptsColumn(int /*source_column*/, const QModelIndex &/*source_parent*/) const
+{
+ return true;
+}
+
+bool KisStorageFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+ if (d->filter.isNull()) return true;
+
+ QModelIndex idx = sourceModel()->index(source_row, KisResourceModel::Name, source_parent);
+
+ switch (d->filterType) {
+ case ByFileName:
+ {
+ QMap<QString, QVariant> v = d->filter.toMap();
+ return KisResourceLocator::instance()->storageContainsResourceByFile(sourceModel()->data(idx, Qt::UserRole + KisStorageModel::Location).toString()
+ , v["resourcetype"].toString()
+ , v["filename"].toString());
+ }
+ case ByStorageType:
+ {
+ QString storageType = sourceModel()->data(idx, Qt::UserRole + KisStorageModel::StorageType).toString();
+ return (d->filter.toStringList().contains(storageType));
+ }
+ default:
+ ;
+ }
+
+ return false;
+}
+
+bool KisStorageFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
+{
+ QString nameLeft = sourceModel()->data(source_left, Qt::UserRole + KisResourceModel::Name).toString();
+ QString nameRight = sourceModel()->data(source_right, Qt::UserRole + KisResourceModel::Name).toString();
+ return nameLeft < nameRight;
+}
+
+void KisStorageFilterProxyModel::slotModelReset()
+{
+ invalidateFilter();
+}
diff --git a/libs/resources/KisStorageFilterProxyModel.h b/libs/resources/KisStorageFilterProxyModel.h
new file mode 100644
index 0000000000..80ce1e2006
--- /dev/null
+++ b/libs/resources/KisStorageFilterProxyModel.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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 KISSTORAGEFILTERPROXYMODEL_H
+#define KISSTORAGEFILTERPROXYMODEL_H
+
+#include <QSortFilterProxyModel>
+#include <QObject>
+#include <KisResourceStorage.h>
+#include <KisStorageModel.h>
+#include "kritaresources_export.h"
+
+/**
+ * KisStorageFilterProxyModel provides a filtered view on the available storages.
+ * It can be used to find the storages that have resource with a particular file
+ * name, or storages of particular types.
+ *
+ * Filtering by file name takes a string, filtering by storage type a list
+ * of untranslated strings (there is a method in KisResourceStorage for retrieving
+ * those strings from the ResourceType).
+ */
+class KRITARESOURCES_EXPORT KisStorageFilterProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+public:
+ KisStorageFilterProxyModel(QObject *parent = 0);
+ ~KisStorageFilterProxyModel() override;
+
+ enum FilterType {
+ ByFileName = 0,
+ ByStorageType
+ };
+
+ KisResourceStorageSP storageForIndex(QModelIndex index = QModelIndex()) const;
+
+ void setFilter(FilterType filterType, QVariant filter);
+
+protected:
+
+ bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override;
+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
+ bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
+
+private Q_SLOTS:
+ void slotModelReset();
+
+
+private:
+ struct Private;
+ Private *const d;
+
+ Q_DISABLE_COPY(KisStorageFilterProxyModel)
+};
+
+#endif
diff --git a/libs/resources/KisStorageModel.cpp b/libs/resources/KisStorageModel.cpp
new file mode 100644
index 0000000000..28304da627
--- /dev/null
+++ b/libs/resources/KisStorageModel.cpp
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2019 boud <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 "KisStorageModel.h"
+
+#include <QtSql>
+#include <QElapsedTimer>
+#include <KisResourceLocator.h>
+#include <KisResourceModelProvider.h>
+
+Q_GLOBAL_STATIC(KisStorageModel, s_instance)
+
+struct KisStorageModel::Private {
+ int cachedRowCount {-1};
+ QSqlQuery query;
+};
+
+KisStorageModel::KisStorageModel(QObject *parent)
+ : QAbstractTableModel(parent)
+ , d(new Private())
+{
+ prepareQuery();
+ connect(KisResourceLocator::instance(), SIGNAL(storageAdded()), this, SLOT(resetQuery()));
+ connect(KisResourceLocator::instance(), SIGNAL(storageRemoved()), this, SLOT(resetQuery()));
+}
+
+KisStorageModel *KisStorageModel::instance()
+{
+ return s_instance;
+}
+
+KisStorageModel::~KisStorageModel()
+{
+}
+
+int KisStorageModel::rowCount(const QModelIndex & /*parent*/) const
+{
+ if (d->cachedRowCount < 0) {
+ QSqlQuery q;
+ q.prepare("SELECT count(*)\n"
+ "FROM storages\n");
+ q.exec();
+ q.first();
+
+ const_cast<KisStorageModel*>(this)->d->cachedRowCount = q.value(0).toInt();
+ }
+ return d->cachedRowCount;
+}
+
+int KisStorageModel::columnCount(const QModelIndex &/*parent*/) const
+{
+ return 8;
+}
+
+QVariant KisStorageModel::data(const QModelIndex &index, int role) const
+{
+ QVariant v;
+
+ if (!index.isValid()) return v;
+ if (index.row() > rowCount()) return v;
+ if (index.column() > (int)MetaData) return v;
+
+ bool pos = d->query.seek(index.row());
+
+ if (pos) {
+ switch(role) {
+ case Qt::DisplayRole:
+ {
+ switch(index.column()) {
+ case Id:
+ return d->query.value("id");
+ case StorageType:
+ return d->query.value("storage_type");
+ case Location:
+ return d->query.value("location");
+ case TimeStamp:
+ return d->query.value("timestamp");
+ case PreInstalled:
+ return d->query.value("pre_installed");
+ case Active:
+ return d->query.value("active");
+ case Thumbnail:
+ {
+ QByteArray ba = d->query.value("thumbnail").toByteArray();
+ QBuffer buf(&ba);
+ buf.open(QBuffer::ReadOnly);
+ QImage img;
+ img.load(&buf, "PNG");
+ return QVariant::fromValue<QImage>(img);
+ }
+ case DisplayName:
+ {
+ QMap<QString, QVariant> r = KisResourceLocator::instance()->metaDataForStorage(d->query.value("location").toString());
+ QVariant name = d->query.value("location");
+ if (r.contains(KisResourceStorage::s_meta_name) && !r[KisResourceStorage::s_meta_name].isNull()) {
+ name = r[KisResourceStorage::s_meta_name];
+ }
+ else if (r.contains(KisResourceStorage::s_meta_title) && !r[KisResourceStorage::s_meta_title].isNull()) {
+ name = r[KisResourceStorage::s_meta_title];
+ }
+ return name;
+ }
+ case Qt::UserRole + MetaData:
+ {
+ QMap<QString, QVariant> r = KisResourceLocator::instance()->metaDataForStorage(d->query.value("location").toString());
+ return r;
+ }
+ default:
+ return v;
+ }
+ }
+ case Qt::UserRole + Id:
+ return d->query.value("id");
+ case Qt::UserRole + StorageType:
+ return d->query.value("storage_type");
+ case Qt::UserRole + Location:
+ return d->query.value("location");
+ case Qt::UserRole + TimeStamp:
+ return d->query.value("timestamp");
+ case Qt::UserRole + PreInstalled:
+ return d->query.value("pre_installed");
+ case Qt::UserRole + Active:
+ return d->query.value("active");
+ case Qt::UserRole + Thumbnail:
+ {
+ QByteArray ba = d->query.value("thumbnail").toByteArray();
+ QBuffer buf(&ba);
+ buf.open(QBuffer::ReadOnly);
+ QImage img;
+ img.load(&buf, "PNG");
+ return QVariant::fromValue<QImage>(img);
+ }
+ case Qt::UserRole + MetaData:
+ {
+ QMap<QString, QVariant> r = KisResourceLocator::instance()->metaDataForStorage(d->query.value("location").toString());
+ return r;
+ }
+
+ default:
+ ;
+ }
+ }
+ return v;
+
+}
+
+bool KisStorageModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (index.isValid()) {
+ if (role == Qt::CheckStateRole) {
+ QSqlQuery q;
+ bool r = q.prepare("UPDATE storages\n"
+ "SET active = :active\n"
+ "WHERE id = :id\n");
+ q.bindValue(":active", value);
+ q.bindValue(":id", index.data(Qt::UserRole + Id));
+ if (!r) {
+ qWarning() << "Could not prepare KisStorageModel update query" << d->query.lastError();
+ return false;
+ }
+ r = q.exec();
+ if (!r) {
+ qWarning() << "Could not execute KisStorageModel update query" << d->query.lastError();
+ return false;
+ }
+
+ }
+ }
+ QAbstractTableModel::setData(index, value, role);
+ KisResourceModelProvider::resetAllModels();
+ return prepareQuery();
+}
+
+Qt::ItemFlags KisStorageModel::flags(const QModelIndex &index) const
+{
+ return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
+}
+
+KisResourceStorageSP KisStorageModel::storageForIndex(const QModelIndex &index) const
+{
+ return KisResourceLocator::instance()->storageByLocation(KisResourceLocator::instance()->makeStorageLocationAbsolute(index.data(Qt::UserRole + Location).toString()));
+}
+
+QVariant KisStorageModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ QVariant v = QVariant();
+ if (role != Qt::DisplayRole) {
+ return v;
+ }
+ if (orientation == Qt::Horizontal) {
+ switch(section) {
+ case 0:
+ v = i18n("Id");
+ break;
+ case 1:
+ v = i18n("Type");
+ break;
+ case 2:
+ v = i18n("Location");
+ break;
+ case 3:
+ v = i18n("Creation Date");
+ break;
+ case 4:
+ v = i18n("Preinstalled");
+ break;
+ case 5:
+ v = i18n("Active");
+ break;
+ case 6:
+ v = i18n("Thumbnail");
+ break;
+ case 7:
+ v = i18n("Name");
+ break;
+ default:
+ v = QString::number(section);
+ }
+ return v;
+ }
+ return QAbstractTableModel::headerData(section, orientation, role);
+}
+
+bool KisStorageModel::resetQuery()
+{
+ QElapsedTimer t;
+ t.start();
+
+ beginResetModel();
+ bool r = d->query.exec();
+ if (!r) {
+ qWarning() << "Could not select storages" << d->query.lastError() << d->query.boundValues();
+ }
+ d->cachedRowCount = -1;
+
+ endResetModel();
+ qDebug() << "KisStorageModel::resetQuery took" << t.elapsed() << "ms";
+
+ return r;
+}
+
+bool KisStorageModel::prepareQuery()
+{
+ beginResetModel();
+ bool r = d->query.prepare("SELECT storages.id as id\n"
+ ", storage_types.name as storage_type\n"
+ ", location\n"
+ ", timestamp\n"
+ ", pre_installed\n"
+ ", active\n"
+ ", thumbnail\n"
+ "FROM storages\n"
+ ", storage_types\n"
+ "WHERE storages.storage_type_id = storage_types.id\n");
+ if (!r) {
+ qWarning() << "Could not prepare KisStorageModel query" << d->query.lastError();
+ }
+ r = d->query.exec();
+ if (!r) {
+ qWarning() << "Could not execute KisStorageModel query" << d->query.lastError();
+ }
+ d->cachedRowCount = -1;
+ endResetModel();
+ return r;
+}
diff --git a/libs/resources/KisStorageModel.h b/libs/resources/KisStorageModel.h
new file mode 100644
index 0000000000..3d13c4f92f
--- /dev/null
+++ b/libs/resources/KisStorageModel.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019 boud <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 KISSTORAGEMODEL_H
+#define KISSTORAGEMODEL_H
+
+#include <QAbstractTableModel>
+#include <QObject>
+#include <QScopedPointer>
+
+#include "KisResourceStorage.h"
+#include "kritaresources_export.h"
+
+/**
+ * KisStorageModel provides a model of all registered storages, like
+ * the folder storages, the bundle storages or the memory storages. Note
+ * that inactive storages are also part of this model.
+ */
+class KRITARESOURCES_EXPORT KisStorageModel : public QAbstractTableModel
+{
+ Q_OBJECT
+public:
+
+ enum Columns {
+ Id = 0,
+ StorageType,
+ Location,
+ TimeStamp,
+ PreInstalled,
+ Active,
+ Thumbnail,
+ DisplayName,
+ MetaData
+ };
+
+ KisStorageModel(QObject *parent = 0);
+ ~KisStorageModel() override;
+
+
+ static KisStorageModel * instance();
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ bool setData(const QModelIndex &index, const QVariant &value, int role) override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+
+ KisResourceStorageSP storageForIndex(const QModelIndex &index) const;
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+
+public Q_SLOTS:
+
+ bool resetQuery();
+
+private:
+
+ KisStorageModel(const KisStorageModel&);
+ KisStorageModel operator=(const KisStorageModel&);
+
+ bool prepareQuery();
+
+ struct Private;
+ QScopedPointer<Private> d;
+
+
+};
+
+#endif // KISSTORAGEMODEL_H
diff --git a/libs/resources/KisStoragePlugin.cpp b/libs/resources/KisStoragePlugin.cpp
new file mode 100644
index 0000000000..e83f6bedf4
--- /dev/null
+++ b/libs/resources/KisStoragePlugin.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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 "KisStoragePlugin.h"
+#include <QFileInfo>
+
+class KisStoragePlugin::Private
+{
+public:
+ QString location;
+ QDateTime timestamp;
+};
+
+KisStoragePlugin::KisStoragePlugin(const QString &location)
+ : d(new Private())
+{
+ d->location = location;
+
+ if (!QFileInfo(d->location).exists()) {
+ d->timestamp = QDateTime::currentDateTime();
+ }
+}
+
+KisStoragePlugin::~KisStoragePlugin()
+{
+}
+
+QDateTime KisStoragePlugin::timestamp()
+{
+ if (d->timestamp.isNull()) {
+ return QFileInfo(d->location).lastModified();
+ }
+ return d->timestamp;
+}
+
+QString KisStoragePlugin::location() const
+{
+ return d->location;
+}
diff --git a/libs/resources/KisStoragePlugin.h b/libs/resources/KisStoragePlugin.h
new file mode 100644
index 0000000000..dbe166f8a9
--- /dev/null
+++ b/libs/resources/KisStoragePlugin.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 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 KISSTORAGEPLUGIN_H
+#define KISSTORAGEPLUGIN_H
+
+#include <QScopedPointer>
+#include <QString>
+
+#include <KisResourceStorage.h>
+#include "kritaresources_export.h"
+
+/**
+ * The KisStoragePlugin class is the base class
+ * for storage plugins. A storage plugin is used by
+ * KisResourceStorage to locate resources and tags in
+ * a kind of storage, like a folder, a bundle or an adobe
+ * resource library.
+ */
+class KRITARESOURCES_EXPORT KisStoragePlugin
+{
+public:
+ KisStoragePlugin(const QString &location);
+ virtual ~KisStoragePlugin();
+
+ virtual KisResourceStorage::ResourceItem resourceItem(const QString &url) = 0;
+
+ /// Retrieve the given resource. The url is the unique identifier of the resource,
+ /// for instance resourcetype plus filename.
+ virtual KoResourceSP resource(const QString &url) = 0;
+ virtual QSharedPointer<KisResourceStorage::ResourceIterator> resources(const QString &resourceType) = 0;
+ virtual QSharedPointer<KisResourceStorage::TagIterator> tags(const QString &resourceType) = 0;
+
+ virtual bool addTag(const QString &resourceType, KisTagSP tag) {Q_UNUSED(resourceType); Q_UNUSED(tag); return false;}
+ virtual bool addResource(const QString &resourceType, KoResourceSP resource) {Q_UNUSED(resourceType); Q_UNUSED(resource); return false;}
+
+ virtual QImage thumbnail() const { return QImage(); }
+
+ virtual void setMetaData(const QString &key, const QVariant &value) {Q_UNUSED(key); Q_UNUSED(value);}
+ virtual QStringList metaDataKeys() const { return QStringList(); }
+ virtual QVariant metaData(const QString &key) const { Q_UNUSED(key); return QString(); }
+
+ QDateTime timestamp();
+
+protected:
+ friend class TestBundleStorage;
+ QString location() const;
+private:
+ class Private;
+ QScopedPointer<Private> d;
+};
+
+#endif // KISSTORAGEPLUGIN_H
diff --git a/libs/resources/KisTag.cpp b/libs/resources/KisTag.cpp
new file mode 100644
index 0000000000..d5bbc09c32
--- /dev/null
+++ b/libs/resources/KisTag.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2018 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 "KisTag.h"
+
+#include <QIODevice>
+#include <QLocale>
+#include <QBuffer>
+#include <QByteArray>
+#include <QStandardPaths>
+#include <QFile>
+
+#include "kconfigini_p.h"
+#include "kconfigbackend_p.h"
+#include "kconfigdata.h"
+
+#include <kis_debug.h>
+
+const QByteArray KisTag::s_group {"Desktop Entry"};
+const QByteArray KisTag::s_type {"Type"};
+const QByteArray KisTag::s_tag {"Tag"};
+const QByteArray KisTag::s_name {"Name"};
+const QByteArray KisTag::s_url {"URL"};
+const QByteArray KisTag::s_comment {"Comment"};
+const QByteArray KisTag::s_defaultResources {"Default Resources"};
+
+class KisTag::Private {
+public:
+ bool valid {false};
+ QString url; // This is the actual tag
+ QString name; // The translated tag name
+ QString comment; // The translated tag comment
+ QStringList defaultResources; // The list of resources as defined in the tag file
+ KEntryMap map;
+ int id {-1};
+ bool active{true};
+};
+
+KisTag::KisTag()
+ : d(new Private)
+{
+}
+
+KisTag::~KisTag()
+{
+}
+
+KisTag::KisTag(const KisTag &rhs)
+ : d(new Private)
+{
+ *this = rhs;
+}
+
+KisTag &KisTag::operator=(const KisTag &rhs)
+{
+ if (this != &rhs) {
+ d->valid = rhs.d->valid;
+ d->url = rhs.d->url;
+ d->name = rhs.d->name;
+ d->comment = rhs.d->comment;
+ d->defaultResources = rhs.d->defaultResources;
+ d->map = rhs.d->map;
+ }
+ return *this;
+}
+
+KisTagSP KisTag::clone() const
+{
+ return KisTagSP(new KisTag(*this));
+}
+
+bool KisTag::valid() const
+{
+ return d->valid;
+}
+
+int KisTag::id() const
+{
+ return d->id;
+}
+
+bool KisTag::active() const
+{
+ return d->active;
+}
+
+QString KisTag::name() const
+{
+ return d->name;
+}
+
+void KisTag::setName(const QString &name)
+{
+ d->map.setEntry(s_group, s_name, name, KEntryMap::EntryDirty);
+ d->name = name;
+}
+
+QString KisTag::url() const
+{
+ return d->url;
+}
+
+void KisTag::setUrl(const QString &url)
+{
+ d->map.setEntry(s_group, s_url, url, KEntryMap::EntryDirty);
+ d->url = url;
+}
+
+QString KisTag::comment() const
+{
+ return d->comment;
+}
+
+void KisTag::setComment(const QString &comment)
+{
+ d->map.setEntry(s_group, s_comment, comment, KEntryMap::EntryDirty);
+ d->comment = comment;
+}
+
+QStringList KisTag::defaultResources() const
+{
+ return d->defaultResources;
+}
+
+void KisTag::setDefaultResources(const QStringList &defaultResources)
+{
+ d->defaultResources = defaultResources;
+}
+
+bool KisTag::compareNamesAndUrls(KisTagSP left, KisTagSP right)
+{
+ if (left.isNull() && !right.isNull()) {
+ return true;
+ }
+ if (right.isNull()) {
+ return false;
+ }
+ if (left->name() != right->name()) {
+ return left->name().compare(right->name()) > 0;
+ }
+
+ return left->url().compare(right->url()) > 0;
+}
+
+bool KisTag::load(QIODevice &io)
+{
+ if (!io.isOpen()) {
+ io.open(QIODevice::ReadOnly);
+ }
+ KIS_ASSERT(io.isOpen());
+
+ KConfigIniBackend ini;
+ KConfigBackend::ParseInfo r = ini.parseConfigIO(io, QLocale().name().toUtf8(), d->map, KConfigBackend::ParseOption::ParseGlobal, false);
+ if (r != KConfigBackend::ParseInfo::ParseOk) {
+ qWarning() << "Could not load this tag file" << r;
+ return false;
+ }
+
+ QString t = d->map.getEntry(s_group, s_type);
+ if (t != s_tag) {
+ qWarning() << "Not a tag desktop file" << t;
+ return false;
+ }
+
+ d->url = d->map.getEntry(s_group, s_url);
+ d->name = d->map.getEntry(s_group, s_name, QString(), KEntryMap::SearchLocalized);
+ d->comment = d->map.getEntry(s_group, s_comment, QString(), KEntryMap::SearchLocalized);
+ d->defaultResources = d->map.getEntry(s_group, s_defaultResources, QString()).split(',', QString::SkipEmptyParts);
+ d->valid = true;
+
+ return true;
+}
+
+bool KisTag::save(QIODevice &io)
+{
+ KConfigIniBackend ini;
+
+ d->map.setEntry(s_group, s_url, d->url, KEntryMap::EntryDirty);
+ d->map.setEntry(s_group, s_name, d->name, KEntryMap::EntryDirty);
+ d->map.setEntry(s_group, s_comment, d->comment, KEntryMap::EntryDirty);
+ d->map.setEntry(s_group, s_defaultResources, d->defaultResources.join(','), KEntryMap::EntryDirty);
+
+ ini.writeEntries(QLocale().name().toUtf8(), io, d->map);
+ return true;
+}
+
+void KisTag::setId(int id)
+{
+ d->id = id;
+}
+
+void KisTag::setActive(bool active)
+{
+ d->active = active;
+}
+
+void KisTag::setValid(bool valid)
+{
+ d->valid = valid;
+}
+
diff --git a/libs/resources/KisTag.h b/libs/resources/KisTag.h
new file mode 100644
index 0000000000..39da92383a
--- /dev/null
+++ b/libs/resources/KisTag.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 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 KISTAGLOADER_H
+#define KISTAGLOADER_H
+
+#include <QDebug>
+#include <QString>
+#include <QScopedPointer>
+#include <QSharedPointer>
+
+class QIODevice;
+
+#include "kritaresources_export.h"
+
+class KisTag;
+typedef QSharedPointer<KisTag> KisTagSP;
+
+
+/**
+ * @brief The KisTag loads a tag from a .tag file.
+ * A .tag file is a .desktop file. The following fields
+ * are important:
+ *
+ * name: the name of the tag, which can be translated
+ * comment: a tooltip for the tag, which can be translagted
+ * url: the untranslated name of the tag
+ *
+ */
+class KRITARESOURCES_EXPORT KisTag
+{
+public:
+ KisTag();
+ virtual ~KisTag();
+ KisTag(const KisTag &rhs);
+ KisTag &operator=(const KisTag &rhs);
+ KisTagSP clone() const;
+
+ bool valid() const;
+
+ int id() const;
+ bool active() const;
+
+ /// The unique identifier for the tag. Since tag urls are compared COLLATE NOCASE, tag urls must be ASCII only.
+ QString url() const;
+ void setUrl(const QString &url);
+
+ /// The translated name of the tag
+ QString name() const;
+ void setName(const QString &name);
+
+ /// a translated tooltip for the tag
+ QString comment() const;
+ void setComment(const QString &comment);
+
+ QStringList defaultResources() const;
+ void setDefaultResources(const QStringList &defaultResources);
+
+ bool load(QIODevice &io);
+ bool save(QIODevice &io);
+
+ static bool compareNamesAndUrls(KisTagSP left, KisTagSP right);
+
+private:
+
+ friend class KisTagModel;
+ friend class KisResourceModel;
+ friend class KisTagChooserWidget;
+ void setId(int id);
+ void setActive(bool active);
+ void setValid(bool valid);
+ static const QByteArray s_group;
+ static const QByteArray s_type;
+ static const QByteArray s_tag;
+ static const QByteArray s_name;
+ static const QByteArray s_url;
+ static const QByteArray s_comment;
+ static const QByteArray s_defaultResources;
+ class Private;
+ QScopedPointer<Private> d;
+};
+
+
+inline QDebug operator<<(QDebug dbg, const KisTagSP tag)
+{
+ dbg.space() << "[TAG] Name" << tag->name()
+ << "Url" << tag->url()
+ << "Comment" << tag->comment()
+ << "Default resources" << tag->defaultResources().join(", ");
+ return dbg.space();
+}
+
+#endif // KISTAGLOADER_H
diff --git a/libs/resources/KisTagFilterResourceProxyModel.cpp b/libs/resources/KisTagFilterResourceProxyModel.cpp
new file mode 100644
index 0000000000..1be4fe2731
--- /dev/null
+++ b/libs/resources/KisTagFilterResourceProxyModel.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2018 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 "KisTagFilterResourceProxyModel.h"
+
+#include <QDebug>
+#include <KisResourceModel.h>
+#include <kis_debug.h>
+#include <KisResourceSearchBoxFilter.h>
+
+struct KisTagFilterResourceProxyModel::Private
+{
+ Private()
+ : filter(new KisResourceSearchBoxFilter())
+ {
+ }
+
+ QList<KisTagSP> tags;
+ KisTagModel* tagModel;
+ QScopedPointer<KisResourceSearchBoxFilter> filter;
+ bool filterInCurrentTag;
+
+};
+
+KisTagFilterResourceProxyModel::KisTagFilterResourceProxyModel(KisTagModel* model, QObject *parent)
+ : QSortFilterProxyModel(parent)
+ , d(new Private)
+{
+ d->tagModel = model;
+ //connect(model, SIGNAL(modelReset()), this, SLOT(slotModelReset()));
+}
+
+KisTagFilterResourceProxyModel::~KisTagFilterResourceProxyModel()
+{
+ delete d;
+}
+
+KoResourceSP KisTagFilterResourceProxyModel::resourceForIndex(QModelIndex index) const
+{
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->resourceForIndex(mapToSource(index));
+ }
+ return 0;
+}
+
+QModelIndex KisTagFilterResourceProxyModel::indexFromResource(KoResourceSP resource) const
+{
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return mapFromSource(source->indexFromResource(resource));
+ }
+ return QModelIndex();
+}
+
+bool KisTagFilterResourceProxyModel::removeResource(const QModelIndex &index)
+{
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->removeResource(mapToSource(index));
+ }
+ return false;
+}
+
+bool KisTagFilterResourceProxyModel::importResourceFile(const QString &filename)
+{
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->importResourceFile(filename);
+ }
+ return false;
+}
+
+bool KisTagFilterResourceProxyModel::addResource(KoResourceSP resource, const QString &storageId)
+{
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->addResource(resource, storageId);
+ }
+ return false;
+}
+
+bool KisTagFilterResourceProxyModel::updateResource(KoResourceSP resource)
+{
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->updateResource(resource);
+ }
+ return false;
+}
+
+bool KisTagFilterResourceProxyModel::renameResource(KoResourceSP resource, const QString &name)
+{
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->renameResource(resource, name);
+ }
+ return false;
+}
+
+bool KisTagFilterResourceProxyModel::removeResource(KoResourceSP resource)
+{
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->removeResource(resource);
+ }
+ return false;
+}
+
+bool KisTagFilterResourceProxyModel::setResourceMetaData(KoResourceSP resource, QMap<QString, QVariant> metadata)
+{
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->setResourceMetaData(resource, metadata);
+ }
+ return false;
+}
+
+void KisTagFilterResourceProxyModel::setTag(const KisTagSP tag)
+{
+ d->tags.clear();
+ if (!tag.isNull()) {
+ d->tags << tag;
+ }
+ invalidateFilter();
+}
+
+void KisTagFilterResourceProxyModel::setSearchBoxText(const QString& seatchBoxText)
+{
+ d->filter->setFilter(seatchBoxText);
+ invalidateFilter();
+}
+
+void KisTagFilterResourceProxyModel::setFilterByCurrentTag(const bool filterInCurrentTag)
+{
+ d->filterInCurrentTag = filterInCurrentTag;
+ invalidateFilter();
+}
+
+bool KisTagFilterResourceProxyModel::filterAcceptsColumn(int /*source_column*/, const QModelIndex &/*source_parent*/) const
+{
+ return true;
+}
+
+bool KisTagFilterResourceProxyModel::resourceHasCurrentTag(KisTagSP currentTag, QVector<KisTagSP> tagsForResource) const
+{
+
+ if (!d->filterInCurrentTag && !d->filter->isEmpty()) {
+ // we don't need to check anything else because the user wants to search in all resources
+ // but if the filter text is empty, we do need to filter by the current tag
+ return true;
+ }
+
+ if (currentTag.isNull()) {
+ // no tag set; all resources are allowed
+ return true;
+ } else {
+ if (currentTag->id() == KisTagModel::All) {
+ // current tag is "All", all resources are allowed
+ return true;
+ } else if (currentTag->id() == KisTagModel::AllUntagged) {
+ // current tag is "All untagged", all resources without any tags are allowed
+ return tagsForResource.size() == 0;
+ } else {
+ // checking whether the current tag is on the list of tags assigned to the resource
+ Q_FOREACH(KisTagSP temp, tagsForResource) {
+ if (temp->id() == currentTag->id()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool KisTagFilterResourceProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+ if (d->tagModel == 0) {
+ return true;
+ }
+
+ QModelIndex idx = sourceModel()->index(source_row, KisResourceModel::Name, source_parent);
+ int resourceId = sourceModel()->data(idx, Qt::UserRole + KisResourceModel::Id).toInt();
+ QString resourceName = sourceModel()->data(idx, Qt::UserRole + KisResourceModel::Name).toString();
+
+ QVector<KisTagSP> tagsForResource = d->tagModel->tagsForResource(resourceId);
+ KisTagSP tag = d->tags.isEmpty() ? KisTagSP() : d->tags.first();
+
+ bool hasCurrentTag = resourceHasCurrentTag(tag, tagsForResource);
+ if (!hasCurrentTag) {
+ return false;
+ }
+
+ bool currentFilterMatches = d->filter->matchesResource(resourceName);
+
+ return currentFilterMatches;
+}
+
+bool KisTagFilterResourceProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
+{
+ QString nameLeft = sourceModel()->data(source_left, Qt::UserRole + KisResourceModel::Name).toString();
+ QString nameRight = sourceModel()->data(source_right, Qt::UserRole + KisResourceModel::Name).toString();
+ return nameLeft < nameRight;
+}
+
+void KisTagFilterResourceProxyModel::slotModelReset()
+{
+ invalidateFilter();
+}
diff --git a/libs/resources/KisTagFilterResourceProxyModel.h b/libs/resources/KisTagFilterResourceProxyModel.h
new file mode 100644
index 0000000000..7585c7e608
--- /dev/null
+++ b/libs/resources/KisTagFilterResourceProxyModel.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 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 KISTAGFILTERRESOURCEPROXYMODEL_H
+#define KISTAGFILTERRESOURCEPROXYMODEL_H
+
+#include <QSortFilterProxyModel>
+#include <QObject>
+#include <KoResource.h>
+#include <KisResourceModel.h>
+#include <KisTag.h>
+#include <KisTagModel.h>
+
+#include "kritaresources_export.h"
+
+class KRITARESOURCES_EXPORT KisTagFilterResourceProxyModel : public QSortFilterProxyModel, public KisAbstractResourceModel
+{
+ Q_OBJECT
+public:
+ KisTagFilterResourceProxyModel(KisTagModel* model = 0, QObject *parent = 0);
+ ~KisTagFilterResourceProxyModel() override;
+
+ // KisAbstractResourceModel interface
+public:
+ KoResourceSP resourceForIndex(QModelIndex index = QModelIndex()) const override;
+ QModelIndex indexFromResource(KoResourceSP resource) const override;
+ bool removeResource(const QModelIndex &index) override;
+ bool importResourceFile(const QString &filename) override;
+ bool addResource(KoResourceSP resource, const QString &storageId = QString()) override;
+ bool updateResource(KoResourceSP resource) override;
+ bool renameResource(KoResourceSP resource, const QString &name) override;
+ bool removeResource(KoResourceSP resource) override;
+ bool setResourceMetaData(KoResourceSP resource, QMap<QString, QVariant> metadata) override;
+
+ /**
+ * @brief setTag
+ * @param tag
+ */
+ void setTag(const KisTagSP tag);
+ void setSearchBoxText(const QString& seatchBoxText);
+ void setFilterByCurrentTag(bool filterInCurrentTag);
+
+protected:
+
+ bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override;
+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
+ bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
+
+ bool resourceHasCurrentTag(KisTagSP currentTag, QVector<KisTagSP> tagsForResource) const;
+
+private Q_SLOTS:
+ void slotModelReset();
+
+
+private:
+ struct Private;
+ Private *const d;
+
+ Q_DISABLE_COPY(KisTagFilterResourceProxyModel)
+};
+
+#endif // KISTAGFILTERRESOURCEPROXYMODEL_H
diff --git a/libs/resources/KisTagList.cpp b/libs/resources/KisTagList.cpp
new file mode 100644
index 0000000000..aa7270447d
--- /dev/null
+++ b/libs/resources/KisTagList.cpp
@@ -0,0 +1,10 @@
+
+
+
+#include <KisTagList.h>
+
+KisTagList::KisTagList()
+{
+
+
+}
diff --git a/libs/resources/KisTagList.h b/libs/resources/KisTagList.h
new file mode 100644
index 0000000000..cc19d9b712
--- /dev/null
+++ b/libs/resources/KisTagList.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 Agata Cacko <cacko.azh@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 <QList>
+
+#include <KisTag.h>
+
+class KisTagList : public QList<KisTagSP>
+{
+ KisTagList();
+
+
+
+}
+
diff --git a/libs/resources/KisTagModel.cpp b/libs/resources/KisTagModel.cpp
new file mode 100644
index 0000000000..a1337dcca3
--- /dev/null
+++ b/libs/resources/KisTagModel.cpp
@@ -0,0 +1,611 @@
+/*
+ * Copyright (c) 2018 boud <boud@valdyas.org>
+ * Copyright (c) 2020 Agata Cacko <cacko.azh@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 "KisTagModel.h"
+
+#include <QtSql>
+#include <QStringList>
+#include <QElapsedTimer>
+
+#include <klocalizedstring.h>
+
+#include <KisResourceLocator.h>
+#include <KisResourceCacheDb.h>
+#include <KisTag.h>
+
+#include <KisResourceModelProvider.h>
+#include <QVector>
+
+#include <kis_assert.h>
+
+
+Q_DECLARE_METATYPE(QSharedPointer<KisTag>)
+
+
+struct KisTagModel::Private {
+ QSqlQuery query;
+
+ QSqlQuery tagsForResourceQuery;
+ QSqlQuery resourcesForTagQuery;
+
+ QString resourceType;
+ int columnCount {5};
+ int cachedRowCount {-1};
+ int fakeRowsCount {2};
+};
+
+
+KisTagModel::KisTagModel(const QString &resourceType, QObject *parent)
+ : QAbstractTableModel(parent)
+ , d(new Private())
+{
+ d->resourceType = resourceType;
+ if (!d->resourceType.isEmpty()) {
+ prepareQuery();
+ }
+}
+
+KisTagModel::~KisTagModel()
+{
+ delete d;
+}
+
+int KisTagModel::rowCount(const QModelIndex &/*parent*/) const
+{
+ if (d->cachedRowCount < 0) {
+ QSqlQuery q;
+ q.prepare("SELECT count(*)\n"
+ "FROM tags\n"
+ ", resource_types\n"
+ "WHERE active = 1\n"
+ "AND tags.resource_type_id = resource_types.id\n"
+ "AND resource_types.name = :resource_type\n");
+ q.bindValue(":resource_type", d->resourceType);
+ q.exec();
+ q.first();
+
+ const_cast<KisTagModel*>(this)->d->cachedRowCount = q.value(0).toInt() + d->fakeRowsCount;
+ }
+
+ return d->cachedRowCount;
+}
+
+int KisTagModel::columnCount(const QModelIndex &/*parent*/) const
+{
+ return d->columnCount;
+}
+
+QVariant KisTagModel::data(const QModelIndex &index, int role) const
+{
+ QVariant v;
+
+ if (!index.isValid()) return v;
+ if (index.row() > rowCount()) return v;
+ if (index.column() > d->columnCount) return v;
+
+ // The first row is All
+ // XXX: Should we also add an "All Untagged"?
+ if (index.row() < d->fakeRowsCount) {
+ if (index.row() == KisTagModel::All + d->fakeRowsCount) {
+ switch(role) {
+ case Qt::DisplayRole: // fallthrough
+ case Qt::ToolTipRole: // fallthrough
+ case Qt::StatusTipRole: // fallthrough
+ case Qt::WhatsThisRole:
+ case Qt::UserRole + Name:
+ return i18n("All");
+ case Qt::UserRole + Id:
+ return QString::number(KisTagModel::All);
+ case Qt::UserRole + Url: {
+ return "All";
+ }
+ case Qt::UserRole + ResourceType:
+ return d->resourceType;
+ case Qt::UserRole + Active:
+ return true;
+ case Qt::UserRole + KisTagRole:
+ {
+ KisTagSP tag = tagForIndex(index);
+ QVariant response;
+ response.setValue(tag);
+ return response;
+ }
+ default:
+ ;
+ }
+ } else if (index.row() == KisTagModel::AllUntagged + d->fakeRowsCount) {
+ switch(role) {
+ case Qt::DisplayRole: // fallthrough
+ case Qt::ToolTipRole: // fallthrough
+ case Qt::StatusTipRole: // fallthrough
+ case Qt::WhatsThisRole:
+ case Qt::UserRole + Name:
+ return i18n("All untagged");
+ case Qt::UserRole + Id:
+ return QString::number(KisTagModel::AllUntagged);
+ case Qt::UserRole + Url: {
+ return "All untagged";
+ }
+ case Qt::UserRole + ResourceType:
+ return d->resourceType;
+ case Qt::UserRole + Active:
+ return true;
+ case Qt::UserRole + KisTagRole:
+ {
+ KisTagSP tag = tagForIndex(index);
+ QVariant response;
+ response.setValue(tag);
+ return response;
+ }
+ default:
+ ;
+ }
+ }
+ }
+ else {
+ bool pos = const_cast<KisTagModel*>(this)->d->query.seek(index.row() - d->fakeRowsCount);
+ if (pos) {
+ switch(role) {
+ case Qt::DisplayRole:
+ return d->query.value("name");
+ case Qt::ToolTipRole: // fallthrough
+ case Qt::StatusTipRole: // fallthrough
+ case Qt::WhatsThisRole:
+ return d->query.value("comment");
+ case Qt::UserRole + Id:
+ return d->query.value("id");
+ case Qt::UserRole + Name:
+ return d->query.value("name");
+ case Qt::UserRole + Url:
+ return d->query.value("url");
+ case Qt::UserRole + ResourceType:
+ return d->query.value("resource_type");
+ case Qt::UserRole + Active:
+ return d->query.value("active");
+ case Qt::UserRole + KisTagRole:
+ {
+ KisTagSP tag = tagForIndex(index);
+ QVariant response;
+ response.setValue(tag);
+ return response;
+ }
+ default:
+ ;
+ }
+ }
+ }
+ return v;
+}
+
+void KisTagModel::setResourceType(const QString &resourceType)
+{
+ d->resourceType = resourceType;
+ prepareQuery();
+}
+
+KisTagSP KisTagModel::tagForIndex(QModelIndex index) const
+{
+ KisTagSP tag = 0;
+ if (!index.isValid()) return tag;
+ if (index.row() > rowCount()) return tag;
+ if (index.column() > columnCount()) return tag;
+
+
+ if (index.row() < d->fakeRowsCount) {
+ if (index.row() == KisTagModel::All + d->fakeRowsCount) {
+ tag.reset(new KisTag());
+ tag->setName(i18n("All"));
+ tag->setUrl("All");
+ tag->setComment(i18n("All"));
+ tag->setId(KisTagModel::All);
+ tag->setActive(true);
+ tag->setValid(true);
+ } else if (index.row() == KisTagModel::AllUntagged + d->fakeRowsCount) {
+ tag.reset(new KisTag());
+ tag->setName(i18n("All untagged"));
+ tag->setUrl("All untagged");
+ tag->setComment(i18n("All untagged"));
+ tag->setId(KisTagModel::AllUntagged);
+ tag->setActive(true);
+ tag->setValid(true);
+ }
+ }
+ else {
+ bool pos = const_cast<KisTagModel*>(this)->d->query.seek(index.row() - d->fakeRowsCount);
+ if (pos) {
+
+ tag.reset(new KisTag());
+ tag->setUrl(d->query.value("url").toString());
+ tag->setName(d->query.value("name").toString());
+ tag->setComment(d->query.value("comment").toString());
+ tag->setId(d->query.value("id").toInt());
+ tag->setActive(d->query.value("active").toBool());
+ tag->setValid(true);
+ }
+ }
+
+ return tag;
+}
+
+bool KisTagModel::addEmptyTag(const QString& tagName, QVector<KoResourceSP> taggedResouces)
+{
+ qDebug() << "bool KisTagModel::addEmptyTag(const QString& tagName, QVector<KoResourceSP> taggedResouces) ### " << tagName;
+ KisTagSP tag = KisTagSP(new KisTag());
+ tag->setName(tagName);
+ tag->setUrl(tagName);
+ return addEmptyTag(tag, taggedResouces);
+}
+
+bool KisTagModel::addEmptyTag(const KisTagSP tag, QVector<KoResourceSP> taggedResouces)
+{
+ qDebug() << "bool KisTagModel::addEmptyTag(const KisTagSP tag, QVector<KoResourceSP> taggedResouces) ### " << tag;
+ tag->setValid(true);
+ tag->setActive(true);
+ return addTag(tag, taggedResouces);
+}
+
+bool KisTagModel::addTag(const KisTagSP tag, QVector<KoResourceSP> taggedResouces)
+{
+ qDebug() << "######################";
+ qDebug() << "bool KisTagModel::addTag(const KisTagSP tag, QVector<KoResourceSP> taggedResouces) " << tag;
+
+ if (tag.isNull()) return false;
+ if (!tag) return false;
+ if (!tag->valid()) return false;
+ // A new tag doesn't have an ID yet, that comes from the database
+ if (tag->id() >= 0) return false;
+
+
+ if (!KisResourceCacheDb::hasTag(tag->url(), d->resourceType)) {
+ qDebug() << "it doesn't have the tag!" << tag->url() << tag->name() << tag->comment();
+ if (!KisResourceCacheDb::addTag(d->resourceType, "", tag->url(), tag->name(), tag->comment())) {
+ qWarning() << "Could not add tag" << tag;
+ return false;
+ }
+ } else {
+ QSqlQuery q;
+ if (!q.prepare("UPDATE tags\n"
+ "SET active = 1\n"
+ "WHERE url = :url\n"
+ "AND resource_type_id = (SELECT id\n"
+ " FROM resource_types\n"
+ " WHERE name = :resource_type\n)")) {
+ qWarning() << "Couild not prepare make existing tag active query" << tag << q.lastError();
+ return false;
+ }
+ q.bindValue(":url", tag->url());
+ q.bindValue(":resource_type", d->resourceType);
+
+ if (!q.exec()) {
+ qWarning() << "Couild not execute make existing tag active query" << q.boundValues(), q.lastError();
+ return false;
+ }
+ }
+
+ qDebug() << "tag = " << tag;
+ if (!taggedResouces.isEmpty()) {
+
+
+ qDebug() << "********************";
+
+ qDebug() << "tag = " << tag;
+ qDebug() << "tag url = " << tag->url();
+
+ KisTagSP tagFromDb = tagByUrl(tag->url());
+ qDebug() << "tag from db: " << tagFromDb << tag->id();
+ Q_FOREACH(const KoResourceSP resource, taggedResouces) {
+
+ if (!resource) continue;
+ if (!resource->valid()) continue;
+ if (resource->resourceId() < 0) continue;
+
+ tagResource(tagFromDb, resource);
+ }
+ }
+
+ qDebug() << "^^^^^^^^^^^^^^^^^^^^^^^^^";
+ return prepareQuery();
+}
+
+bool KisTagModel::removeTag(const KisTagSP tag)
+{
+ if (!tag) return false;
+ if (!tag->valid()) return false;
+ if (tag->id() < 0) return false;
+
+ QSqlQuery q;
+ if (!q.prepare("UPDATE tags\n"
+ "SET active = 0\n"
+ "WHERE id = :id")) {
+ qWarning() << "Could not prepare remove tag query" << q.lastError();
+ return false;
+ }
+
+ q.bindValue(":id", tag->id());
+
+ if (!q.exec()) {
+ qWarning() << "Could not execute remove tag query" << q.lastError();
+ return false;
+ }
+
+ return prepareQuery();
+}
+
+bool KisTagModel::tagResource(const KisTagSP tag, const KoResourceSP resource)
+{
+ if (!tag) return false;
+ if (!tag->valid()) return false;
+ if (tag->id() < 0) return false;
+
+ qDebug() << tag << " tag id " << tag->id();
+
+ if (!resource) return false;
+ if (!resource->valid()) return false;
+ if (resource->resourceId() < 0) return false;
+
+ QSqlQuery q;
+ bool r = q.prepare("INSERT INTO resource_tags\n"
+ "(resource_id, tag_id)\n"
+ "VALUES\n"
+ "( (SELECT id\n"
+ " FROM resources\n"
+ " WHERE id = :resource_id)\n"
+ ", (SELECT id\n"
+ " FROM tags\n"
+ " WHERE id = :tag_id\n"
+ " AND resource_type_id = (SELECT id\n"
+ " FROM resource_types\n"
+ " WHERE name = :resource_type"
+ " \n)"
+ " )\n"
+ ")\n");
+ if (!r) {
+ qWarning() << "Could not prepare insert into resource tags statement" << q.lastError();
+ return false;
+ }
+
+ q.bindValue(":resource_id", resource->resourceId());
+ q.bindValue(":tag_id", tag->id());
+ q.bindValue(":resource_type", d->resourceType);
+
+ if (!q.exec()) {
+ qWarning() << "Could not execute insert into resource tags statement" << q.boundValues() << q.lastError();
+ return false;
+ }
+
+ KisResourceModelProvider::resetModel(d->resourceType);
+ return true;
+}
+
+bool KisTagModel::untagResource(const KisTagSP tag, const KoResourceSP resource)
+{
+ if (!tag) return false;
+ if (!tag->valid()) return false;
+ if (!tag->id()) return false;
+
+ if (!resource) return false;
+ if (!resource->valid()) return false;
+ if (resource->resourceId() < 0) return false;
+
+ // we need to delete an entry in resource_tags
+ QSqlQuery query;
+ bool r = query.prepare("DELETE FROM resource_tags\n"
+ "WHERE resource_id = :resource_id\n"
+ "AND tag_id = :tag_id");
+
+ if (!r) {
+ qWarning() << "Could not prepare KisTagModel query untagResource " << query.lastError();
+ }
+
+ query.bindValue(":resource_id", resource->resourceId());
+ query.bindValue(":tag_id", tag->id());
+
+
+ r = query.exec();
+
+ if (!r) {
+ qWarning() << "Could not select tags" << query.lastError();
+ }
+
+ KisResourceModelProvider::resetModel(d->resourceType);
+ return true;
+}
+
+bool KisTagModel::renameTag(const KisTagSP tag, const QString &name)
+{
+ if (!tag) return false;
+ if (!tag->valid()) return false;
+
+ if (name.isEmpty()) return false;
+
+ QSqlQuery q;
+ if (!q.prepare("UPDATE tags\n"
+ "SET name = :name\n"
+ "WHERE url = :url\n"
+ "AND resource_type_id = (SELECT id\n"
+ " FROM resource_types\n"
+ " WHERE name = :resource_type\n)")) {
+ qWarning() << "Couild not prepare make existing tag active query" << tag << q.lastError();
+ return false;
+ }
+
+ q.bindValue(":name", name);
+ q.bindValue(":url", tag->url());
+ q.bindValue(":resource_type", d->resourceType);
+
+ if (!q.exec()) {
+ qWarning() << "Couild not execute make existing tag active query" << q.boundValues(), q.lastError();
+ return false;
+ }
+
+ return prepareQuery();
+
+}
+
+bool KisTagModel::changeTagActive(const KisTagSP tag, bool active)
+{
+ if (!tag) return false;
+ if (!tag->valid()) return false;
+
+ QSqlQuery q;
+ if (!q.prepare("UPDATE tags\n"
+ "SET active = :active\n"
+ "WHERE url = :url\n"
+ "AND resource_type_id = (SELECT id\n"
+ " FROM resource_types\n"
+ " WHERE name = :resource_type\n)")) {
+ qWarning() << "Couild not prepare make existing tag active query" << tag << q.lastError();
+ return false;
+ }
+
+ q.bindValue(":active", active);
+ q.bindValue(":url", tag->url());
+ q.bindValue(":resource_type", d->resourceType);
+
+ if (!q.exec()) {
+ qWarning() << "Couild not execute make existing tag active query" << q.boundValues(), q.lastError();
+ return false;
+ }
+
+ return prepareQuery();
+
+}
+
+QVector<KisTagSP> KisTagModel::tagsForResource(int resourceId) const
+{
+ bool r = d->tagsForResourceQuery.prepare("SELECT tags.id\n"
+ ", tags.url\n"
+ ", tags.name\n"
+ ", tags.comment\n"
+ "FROM tags\n"
+ ", resource_tags\n"
+ "WHERE tags.active > 0\n" // make sure the tag is active
+ "AND tags.id = resource_tags.tag_id\n" // join tags + resource_tags by tag_id
+ "AND resource_tags.resource_id = :resource_id\n"); // make sure we're looking for tags for a specific resource
+ if (!r) {
+ qWarning() << "Could not prepare TagsForResource query" << d->tagsForResourceQuery.lastError();
+ }
+
+ d->tagsForResourceQuery.bindValue(":resource_id", resourceId);
+ r = d->tagsForResourceQuery.exec();
+ if (!r) {
+ qWarning() << "Could not select tags for" << resourceId << d->tagsForResourceQuery.lastError() << d->tagsForResourceQuery.boundValues();
+ }
+
+ QVector<KisTagSP> tags;
+ while (d->tagsForResourceQuery.next()) {
+ //qDebug() << d->tagQuery.value(0).toString() << d->tagQuery.value(1).toString() << d->tagQuery.value(2).toString();
+ KisTagSP tag(new KisTag());
+ tag->setId(d->tagsForResourceQuery.value("id").toInt());
+ tag->setUrl(d->tagsForResourceQuery.value("url").toString());
+ tag->setName(d->tagsForResourceQuery.value("name").toString());
+ tag->setComment(d->tagsForResourceQuery.value("comment").toString());
+ tag->setValid(true);
+ tag->setActive(true);
+ tags << tag;
+ }
+ return tags;
+}
+
+KisTagSP KisTagModel::tagByUrl(const QString& tagUrl) const
+{
+ if (tagUrl.isEmpty()) {
+ return KisTagSP();
+ }
+
+ QSqlQuery query;
+ bool r = query.prepare("SELECT tags.id\n"
+ ", tags.url\n"
+ ", tags.name\n"
+ ", tags.comment\n"
+ ", tags.active\n"
+ ", resource_types.name as resource_type\n"
+ "FROM tags\n"
+ ", resource_types\n"
+ "WHERE tags.resource_type_id = resource_types.id\n"
+ "AND resource_types.name = :resource_type\n"
+ "AND tags.active = 1\n"
+ "AND tags.url = :tag_url\n"
+ "AND tags.storage_id in (SELECT id\n"
+ " FROM storages\n"
+ " WHERE storages.active == 1)");
+
+ if (!r) {
+ qWarning() << "Could not prepare KisTagModel::tagByUrl query" << query.lastError();
+ }
+
+ query.bindValue(":resource_type", d->resourceType);
+ QString tagUrlForSql = tagUrl;
+ query.bindValue(":tag_url", tagUrlForSql);
+
+ r = query.exec();
+ if (!r) {
+ qWarning() << "Could not execute KisTagModel::tagByUrl query" << query.lastError();
+ }
+ KisTagSP tag(new KisTag());
+ query.next();
+ tag->setUrl(query.value("url").toString());
+ tag->setName(query.value("name").toString());
+ tag->setComment(query.value("comment").toString());
+ tag->setId(query.value("id").toInt());
+ tag->setActive(query.value("active").toBool());
+ tag->setValid(true);
+
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(tagUrl == tag->url(), KisTagSP());
+
+ return tag;
+
+}
+
+bool KisTagModel::prepareQuery()
+{
+ QElapsedTimer t;
+ t.start();
+
+ beginResetModel();
+ bool r = d->query.prepare("SELECT tags.id\n"
+ ", tags.url\n"
+ ", tags.name\n"
+ ", tags.comment\n"
+ ", tags.active\n"
+ ", resource_types.name as resource_type\n"
+ "FROM tags\n"
+ ", resource_types\n"
+ "WHERE tags.resource_type_id = resource_types.id\n"
+ "AND resource_types.name = :resource_type\n"
+ "AND tags.active = 1\n");
+
+ if (!r) {
+ qWarning() << "Could not prepare KisTagModel query" << d->query.lastError();
+ }
+
+ d->query.bindValue(":resource_type", d->resourceType);
+
+ r = d->query.exec();
+
+ if (!r) {
+ qWarning() << "Could not select tags" << d->query.lastError();
+ }
+
+ d->cachedRowCount = -1;
+ endResetModel();
+
+ qDebug() << "bool KisTagModel::prepareQuery() ### RESET TAG MODEL ### for " << d->resourceType << " took " << t.elapsed() << " ms.";
+
+ return r;
+}
diff --git a/libs/resources/KisTagModel.h b/libs/resources/KisTagModel.h
new file mode 100644
index 0000000000..a663329137
--- /dev/null
+++ b/libs/resources/KisTagModel.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2018 boud <boud@valdyas.org>
+ * Copyright (c) 2020 Agata Cacko <cacko.azh@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 KISTAGMODEL_H
+#define KISTAGMODEL_H
+
+#include <QObject>
+#include <QAbstractTableModel>
+
+#include <KisTag.h>
+#include <KoResource.h>
+
+#include "kritaresources_export.h"
+
+
+class KRITARESOURCES_EXPORT KisTagModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+private:
+
+ KisTagModel(const QString &resourceType, QObject *parent = 0);
+
+public:
+
+ enum Columns {
+ Id = 0,
+ Url,
+ Name,
+ Comment,
+ ResourceType,
+ Active,
+ KisTagRole,
+ };
+
+ enum Ids {
+ All = -2, // so it gets on top in the combobox
+ AllUntagged = -1,
+ };
+
+
+ ~KisTagModel() override;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+// KisTagModel API
+
+ KisTagSP tagForIndex(QModelIndex index = QModelIndex()) const;
+ QList<KisTagSP> allTags() const;
+
+ bool addEmptyTag(const QString &tagName, QVector<KoResourceSP> taggedResouces);
+ bool addEmptyTag(const KisTagSP tag, QVector<KoResourceSP> taggedResouces);
+ bool addTag(const KisTagSP tag, QVector<KoResourceSP> taggedResouces = QVector<KoResourceSP>());
+ bool removeTag(const KisTagSP tag);
+ bool tagResource(const KisTagSP tag, const KoResourceSP resource);
+ bool untagResource(const KisTagSP tag, const KoResourceSP resource);
+ bool renameTag(const KisTagSP tag, const QString &name);
+ bool changeTagActive(const KisTagSP tag, bool active);
+ QVector<KisTagSP> tagsForResource(int resourceId) const;
+
+ KisTagSP tagByUrl(const QString& tagUrl) const;
+
+
+
+private:
+
+ friend class DlgDbExplorer;
+ friend class KisTagModelProvider;
+ friend class TestTagModel;
+
+ void setResourceType(const QString &resourceType);
+
+ bool tagResourceByUrl(const QString& tagUrl, const int resourceId);
+ bool tagResourceById(const int tagId, const int resource);
+
+
+
+ bool prepareQuery();
+
+ struct Private;
+ Private* const d;
+
+};
+
+typedef QSharedPointer<KisTagModel> KisTagModelSP;
+
+#endif // KISTAGMODEL_H
diff --git a/libs/resources/KisTagModelProvider.cpp b/libs/resources/KisTagModelProvider.cpp
new file mode 100644
index 0000000000..84b9e52135
--- /dev/null
+++ b/libs/resources/KisTagModelProvider.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2019 Agata Cacko <cacko.azh@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 "KisTagModelProvider.h"
+
+#include <QMap>
+#include <QString>
+#include <memory>
+
+Q_GLOBAL_STATIC(KisTagModelProvider, s_instance)
+
+
+struct KisTagModelProvider::Private {
+
+ std::map<QString, std::unique_ptr<KisTagModel>> tagModelsMap;
+
+};
+
+
+
+KisTagModelProvider::KisTagModelProvider()
+ : d(new Private())
+{
+}
+
+
+KisTagModelProvider::~KisTagModelProvider()
+{
+ delete d;
+}
+
+KisTagModel* KisTagModelProvider::tagModel(const QString& resourceType)
+{
+
+ std::map<QString, std::unique_ptr<KisTagModel> >::const_iterator found = s_instance->d->tagModelsMap.find(resourceType);
+
+ if (found == s_instance->d->tagModelsMap.end())
+ {
+ std::unique_ptr<KisTagModel> modelStorage(new KisTagModel(resourceType));
+ KisTagModel *model = modelStorage.get();
+ s_instance->d->tagModelsMap.insert(std::make_pair(resourceType, std::move(modelStorage)));
+ return model;
+ }
+ return found->second.get();
+}
+
+
+void KisTagModelProvider::resetModels()
+{
+ typedef std::map<QString, std::unique_ptr<KisTagModel>>::iterator mapIterator;
+
+ mapIterator begin = s_instance->d->tagModelsMap.begin();
+ mapIterator end = s_instance->d->tagModelsMap.end();
+
+ for (mapIterator iter = begin; iter!=end; iter++) {
+ iter->second->prepareQuery();
+ }
+}
+
+void KisTagModelProvider::resetModel(const QString& resourceType)
+{
+ std::map<QString, std::unique_ptr<KisTagModel> >::const_iterator found
+ = s_instance->d->tagModelsMap.find(resourceType);
+
+ if (found != s_instance->d->tagModelsMap.end())
+ {
+ found->second->prepareQuery();
+ }
+}
diff --git a/libs/resources/KisTagModelProvider.h b/libs/resources/KisTagModelProvider.h
new file mode 100644
index 0000000000..b9f18d881b
--- /dev/null
+++ b/libs/resources/KisTagModelProvider.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 Agata Cacko <cacko.azh@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_TAG_MODEL_PROVIDER_H
+#define KIS_TAG_MODEL_PROVIDER_H
+
+
+#include <QObject>
+#include <QAbstractTableModel>
+
+#include <KisTag.h>
+#include <KoResource.h>
+
+#include "kritaresources_export.h"
+#include "KisTagModel.h"
+
+
+
+class KRITARESOURCES_EXPORT KisTagModelProvider : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ KisTagModelProvider();
+ ~KisTagModelProvider();
+
+ static KisTagModel* tagModel(const QString& resourceType);
+ static void resetModels();
+ static void resetModel(const QString& resourceType);
+
+private:
+
+ struct Private;
+ Private* const d;
+
+};
+
+#endif // KIS_TAG_MODEL_PROVIDER_H
diff --git a/libs/resources/KoEphemeralResource.h b/libs/resources/KoEphemeralResource.h
new file mode 100644
index 0000000000..74efcd9127
--- /dev/null
+++ b/libs/resources/KoEphemeralResource.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 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 KOEPHEMERALRESOURCE_H
+#define KOEPHEMERALRESOURCE_H
+
+#include <KoResource.h>
+
+
+/**
+ * KoEphemeralResource is a type of resource that has no physical
+ * representation on disk. Therefore, its load()/save() calls do
+ * nothing.
+ *
+ * This type of resources is created directly by the corresponding
+ * factory or other object (e.g. KisAutoBrushFactory).
+ */
+template<class ParentClass>
+class KoEphemeralResource : public ParentClass
+{
+public:
+ KoEphemeralResource()
+ : ParentClass()
+ {
+ }
+
+ KoEphemeralResource(const QString &arg)
+ : ParentClass(arg)
+ {
+ }
+
+ KoEphemeralResource(const KoEphemeralResource &rhs)
+ : ParentClass(rhs)
+ {
+ }
+
+ bool load(KisResourcesInterfaceSP resourcesInterface) override
+ {
+ Q_UNUSED(resourcesInterface);
+ return false;
+ }
+
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override
+ {
+ Q_UNUSED(dev);
+ Q_UNUSED(resourcesInterface);
+ return false;
+ }
+
+ bool save() override
+ {
+ return false;
+ }
+
+ bool saveToDevice(QIODevice *dev) const override
+ {
+ Q_UNUSED(dev);
+ return false;
+ }
+};
+
+#endif // KOEPHEMERALRESOURCE_H
diff --git a/libs/resources/KoMD5Generator.cpp b/libs/resources/KoMD5Generator.cpp
new file mode 100644
index 0000000000..4cab50aa35
--- /dev/null
+++ b/libs/resources/KoMD5Generator.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015 Stefano Bonicatti <smjert@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 "KoMD5Generator.h"
+
+#include <QIODevice>
+#include <QFile>
+#include <QCryptographicHash>
+
+QByteArray KoMD5Generator::generateHash(const QByteArray &array)
+{
+ if (!array.isEmpty()) {
+ QCryptographicHash md5(QCryptographicHash::Md5);
+ md5.addData(array);
+ return md5.result();
+ }
+
+ return array;
+}
+
+QByteArray KoMD5Generator::generateHash(const QString &filename)
+{
+ QByteArray result;
+
+ QFile f(filename);
+ if (f.exists() && f.open(QIODevice::ReadOnly)) {
+ QByteArray ba = f.readAll();
+ result = generateHash(ba);
+ }
+
+ return result;
+}
diff --git a/libs/resources/KoMD5Generator.h b/libs/resources/KoMD5Generator.h
new file mode 100644
index 0000000000..1125eaf0e0
--- /dev/null
+++ b/libs/resources/KoMD5Generator.h
@@ -0,0 +1,34 @@
+#/*
+ * Copyright (c) 2015 Stefano Bonicatti <smjert@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 KOMD5GENERATOR_H
+#define KOMD5GENERATOR_H
+
+#include <QByteArray>
+#include <QString>
+
+#include <kritaresources_export.h>
+
+class KRITARESOURCES_EXPORT KoMD5Generator
+{
+public:
+ static QByteArray generateHash(const QString &filename);
+ static QByteArray generateHash(const QByteArray &array);
+};
+
+#endif
diff --git a/libs/resources/KoResource.cpp b/libs/resources/KoResource.cpp
new file mode 100644
index 0000000000..80e40c4080
--- /dev/null
+++ b/libs/resources/KoResource.cpp
@@ -0,0 +1,277 @@
+/* This file is part of the KDE project
+ Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
+ Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.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 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 <KoResource.h>
+
+#include <QDomElement>
+#include <QFileInfo>
+#include <QDebug>
+#include <QImage>
+#include <QBuffer>
+
+#include <kis_debug.h>
+#include "KoMD5Generator.h"
+
+
+struct Q_DECL_HIDDEN KoResource::Private {
+ int version {-1};
+ int resourceId {-1};
+ bool valid {false};
+ bool permanent {false};
+ bool dirty {false};
+ QString name;
+ QString filename;
+ QString storageLocation;
+ QByteArray md5;
+ QImage image;
+ QMap<QString, QVariant> metadata;
+};
+
+KoResource::KoResource(const QString& filename)
+ : d(new Private)
+{
+ d->filename = filename;
+}
+
+KoResource::~KoResource()
+{
+ delete d;
+}
+
+KoResource::KoResource(const KoResource &rhs)
+ : d(new Private(*rhs.d))
+{
+}
+
+bool KoResource::operator==(const KoResource &other) const
+{
+ return other.md5() == md5();
+}
+
+bool KoResource::load(KisResourcesInterfaceSP resourcesInterface)
+{
+ QFile file(filename());
+
+ if (!file.exists()) {
+ warnKrita << "File doesn't exist: " << filename();
+ return false;
+ }
+
+ if (file.size() == 0) {
+ warnKrita << "File is empty: " << filename();
+ return false;
+ }
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ warnKrita << "Can't open file for reading" << filename();
+ return false;
+ }
+
+ const bool res = loadFromDevice(&file, resourcesInterface);
+ file.close();
+
+ return res;
+}
+
+bool KoResource::save()
+{
+ if (filename().isEmpty()) return false;
+
+ QFile file(filename());
+
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ warnKrita << "Can't open file for writing" << filename();
+ return false;
+ }
+
+ saveToDevice(&file);
+
+ file.close();
+ return true;
+}
+
+bool KoResource::saveToDevice(QIODevice *dev) const
+{
+ Q_UNUSED(dev)
+ d->md5 = QByteArray();
+
+ return true;
+}
+
+QImage KoResource::image() const
+{
+ return d->image;
+}
+
+void KoResource::updateThumbnail()
+{
+}
+
+QImage KoResource::thumbnail() const
+{
+ return image();
+}
+
+void KoResource::setImage(const QImage &image)
+{
+ d->image = image;
+}
+
+QByteArray KoResource::md5() const
+{
+ if (d->md5.isEmpty()) {
+ const_cast<KoResource*>(this)->setMD5(generateMD5());
+ }
+ return d->md5;
+}
+
+void KoResource::setMD5(const QByteArray &md5)
+{
+ d->md5 = md5;
+}
+
+QByteArray KoResource::generateMD5() const
+{
+ QByteArray hash;
+ QByteArray ba;
+ QBuffer buf(&ba);
+ buf.open(QBuffer::WriteOnly);
+ if (saveToDevice(&buf)) {
+ buf.close();
+ hash = KoMD5Generator::generateHash(ba);
+ }
+ else {
+ qWarning() << "Could not create md5sum for resource" << d->filename;
+ }
+ return hash;
+}
+
+QString KoResource::filename() const
+{
+ return d->filename;
+}
+
+void KoResource::setFilename(const QString& filename)
+{
+ d->filename = filename;
+}
+
+QString KoResource::name() const
+{
+ return (!d->name.isEmpty() ? d->name : QFileInfo(filename()).fileName());
+}
+
+void KoResource::setName(const QString& name)
+{
+ d->name = name;
+}
+
+bool KoResource::valid() const
+{
+ return d->valid;
+}
+
+void KoResource::setValid(bool valid)
+{
+ d->valid = valid;
+}
+
+
+QString KoResource::defaultFileExtension() const
+{
+ return QString();
+}
+
+bool KoResource::permanent() const
+{
+ return d->permanent;
+}
+
+void KoResource::setPermanent(bool permanent)
+{
+ d->permanent = permanent;
+}
+
+int KoResource::resourceId() const
+{
+ return d->resourceId;
+}
+
+QList<KoResourceSP> KoResource::requiredResources(KisResourcesInterfaceSP globalResourcesInterface) const
+{
+ return linkedResources(globalResourcesInterface) + embeddedResources(globalResourcesInterface);
+}
+
+QList<KoResourceSP> KoResource::linkedResources(KisResourcesInterfaceSP globalResourcesInterface) const
+{
+ Q_UNUSED(globalResourcesInterface);
+ return {};
+}
+
+QList<KoResourceSP> KoResource::embeddedResources(KisResourcesInterfaceSP globalResourcesInterface) const
+{
+ Q_UNUSED(globalResourcesInterface);
+ return {};
+}
+
+QString KoResource::storageLocation() const
+{
+ return d->storageLocation;
+}
+
+void KoResource::setDirty(bool value)
+{
+ d->dirty = value;
+}
+
+bool KoResource::isDirty() const
+{
+ return d->dirty;
+}
+
+void KoResource::addMetaData(QString key, QVariant value)
+{
+ d->metadata.insert(key, value);
+}
+
+QMap<QString, QVariant> KoResource::metadata() const
+{
+ return d->metadata;
+}
+
+int KoResource::version() const
+{
+ return d->version;
+}
+
+void KoResource::setVersion(int version)
+{
+ d->version = version;
+}
+
+void KoResource::setResourceId(int id)
+{
+ d->resourceId = id;
+}
+
+void KoResource::setStorageLocation(const QString &location)
+{
+ d->storageLocation = location;
+}
+
diff --git a/libs/resources/KoResource.h b/libs/resources/KoResource.h
new file mode 100644
index 0000000000..b40ee137e9
--- /dev/null
+++ b/libs/resources/KoResource.h
@@ -0,0 +1,240 @@
+/* This file is part of the KDE project
+ Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
+ Copyright (c) 2005 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; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef KORESOURCE_H
+#define KORESOURCE_H
+
+#include <QImage>
+#include <QString>
+#include <QHash>
+#include <QSharedPointer>
+#include <QDebug>
+
+#include "KisResourceTypes.h"
+#include <boost/operators.hpp>
+
+#include <kritaresources_export.h>
+
+class QDomDocument;
+class QDomElement;
+
+class KoResource;
+typedef QSharedPointer<KoResource> KoResourceSP;
+
+class KisResourcesInterface;
+typedef QSharedPointer<KisResourcesInterface> KisResourcesInterfaceSP;
+
+/**
+ * The KoResource class provides a representation of resources. This
+ * includes, but not limited to, brushes and patterns.
+ *
+ * A resource knows its filename, but not the location where it's stored.
+ * A new version of a resource is stored with an updated filename, the old
+ * version isn't renamed.
+ *
+ */
+class KRITARESOURCES_EXPORT KoResource : public boost::equality_comparable<KoResource>
+{
+public:
+
+ /**
+ * Creates a new KoResource object using @p filename. No file is opened
+ * in the constructor, you have to call load.
+ *
+ * @param filename the file name to save and load from.
+ */
+ explicit KoResource(const QString &filename);
+ virtual ~KoResource();
+ KoResource(const KoResource &rhs);
+ KoResource &operator=(const KoResource &rhs) = delete;
+
+ virtual KoResourceSP clone() const = 0;
+
+ bool operator==(const KoResource &other) const;
+
+public:
+ /**
+ * Load this resource.
+ * @return true if loading the resource succeeded.
+ */
+ virtual bool load(KisResourcesInterfaceSP resourcesInterface);
+ virtual bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) = 0;
+
+ /**
+ * Save this resource.
+ *@return true if saving the resource succeeded.
+ */
+ virtual bool save();
+ virtual bool saveToDevice(QIODevice* dev) const;
+
+ /**
+ * @returns a QImage image representing this resource: in the case
+ * of some resources, it is the actual resource.
+ *
+ * This image could be null. The image can be in any valid format.
+ */
+ QImage image() const;
+ void setImage(const QImage &image);
+
+ /**
+ * @brief updateThumbnail updates the thumbnail for this resource.
+ * Reimplement if your thumbnail is something else than the image
+ * set with setImage.
+ */
+ virtual void updateThumbnail();
+
+ /**
+ * @brief thumbnail the thumbnail image to use in resource selectors
+ * @return a valid qimage. All thumbnails for a given resource have the
+ * same size (which is not true for image(), but that size need not
+ * be square. By default it's the same as image(), but that is not guaranteed.
+ */
+ virtual QImage thumbnail() const;
+
+ /// @return the md5sum calculated over the contents of the resource.
+ QByteArray md5() const;
+
+ /// @return the unique identifier of this resource within the container (folder, bundle, ...)
+ QString filename() const;
+ void setFilename(const QString& filename);
+
+ /// @return the user-visible name of the resource
+ QString name() const;
+ void setName(const QString& name);
+
+ /// @return true if the resource is ready for use
+ bool valid() const;
+ void setValid(bool valid);
+
+ /// @return the default file extension which should be used when saving the resource
+ virtual QString defaultFileExtension() const;
+
+ /// @return true if the resource is permanent and can't be removed by the user
+ bool permanent() const;
+ void setPermanent(bool permanent);
+
+ /// @return the name of the storage location of the resource
+ QString storageLocation() const;
+
+ /// Mark the preset as modified but not saved
+ void setDirty(bool value);
+
+ /// @return true if the preset has been modified, but not saved
+ bool isDirty() const;
+
+ /// store the given key, value pair in the resource
+ void addMetaData(QString key, QVariant value);
+
+ /// get a map with all the metadata
+ QMap<QString, QVariant> metadata() const;
+
+ /// Get the version of the resource
+ int version() const;
+
+ /// @return the unique id of the resource in the resource database
+ int resourceId() const;
+
+ /// @return the resource type
+ virtual QPair<QString, QString> resourceType() const = 0;
+
+ /**
+ * Loads all the required resources either from \p globalResourcesInterface or
+ * from embedded data. The preset first tries to fetch the required resource
+ * from the global source, and only if it fails, tries to load it from the
+ * embedded data. One can check if the loaded resource is embedded by checking
+ * its resourceId().
+ *
+ * The set of resources returned is basically: linkedResources() + embeddedResources()
+ */
+ QList<KoResourceSP> requiredResources(KisResourcesInterfaceSP globalResourcesInterface) const;
+
+ /**
+ * @return all the resources that are needed but (*this) resource and
+ * are not embedded into it. The resources are fetched from
+ * \p globalResourcesInterface. If fetching of some resources is failed,
+ * then (*this) resource is invalid.
+ */
+ virtual QList<KoResourceSP> linkedResources(KisResourcesInterfaceSP globalResourcesInterface) const;
+
+ /**
+ * @return all the resources that were embedded into (*this) resource.
+ * If the resources were already added to the global database, then they
+ * are fetched from \p globalResourcesInterface to save time/memory.
+ */
+ virtual QList<KoResourceSP> embeddedResources(KisResourcesInterfaceSP globalResourcesInterface) const;
+
+
+private:
+
+ friend class KisResourceModel;
+ friend class KisResourceLocator;
+ friend class TestResourceModel;
+ friend class TestResourceLocator;
+ friend class TestFolderStorage;
+ friend class KisFolderStorage;
+ friend class KisBundleStorage;
+ friend class KisStorageVersioningHelper;
+ friend class KisMemoryStorage;
+
+ void setVersion(int version);
+ void setResourceId(int id);
+ void setStorageLocation(const QString &location);
+
+protected:
+
+ /// override generateMD5 and in your resource subclass
+ virtual QByteArray generateMD5() const;
+
+ /// call this when the contents of the resource change so the md5 needs to be recalculated
+ void setMD5(const QByteArray &md5);
+
+
+private:
+ struct Private;
+ Private* const d;
+};
+
+static inline bool operator==(const KoResource &resource1, const KoResource &resource2)
+{
+ return (resource1.md5() == resource2.md5());
+}
+
+static inline uint qHash(const KoResource &resource)
+{
+ return qHash(resource.md5());
+}
+
+Q_DECLARE_METATYPE(QSharedPointer<KoResource>)
+
+inline QDebug operator<<(QDebug dbg, const KoResourceSP res)
+{
+ if (!res) {
+ dbg.noquote() << "NULL Resource";
+ }
+ else {
+ dbg.nospace() << "[RESOURCE] Name: " << res->name()
+ << " Version: " << res->version()
+ << " Filename: " << res->filename()
+ << " Valid: " << res->valid()
+ << " Storage: " << res->storageLocation();
+ }
+ return dbg.space();
+}
+
+#endif // KORESOURCE_H_
+
diff --git a/libs/resources/KoResourceBundle.cpp b/libs/resources/KoResourceBundle.cpp
new file mode 100644
index 0000000000..d13e250c77
--- /dev/null
+++ b/libs/resources/KoResourceBundle.cpp
@@ -0,0 +1,453 @@
+/*
+ * 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.
+ */
+
+#include "KoResourceBundle.h"
+
+#include <QBuffer>
+#include <QByteArray>
+#include <QCryptographicHash>
+#include <QDate>
+#include <QDir>
+#include <QMessageBox>
+#include <QPainter>
+#include <QProcessEnvironment>
+#include <QScopedPointer>
+#include <QStringList>
+
+#include <klocalizedstring.h>
+
+#include <KisMimeDatabase.h>
+#include "KoResourceBundleManifest.h"
+#include <KoMD5Generator.h>
+#include <KoResourcePaths.h>
+#include <KoStore.h>
+#include <KoXmlReader.h>
+#include <KoXmlWriter.h>
+#include "KisStoragePlugin.h"
+#include "KisResourceLoaderRegistry.h"
+#include <KisResourceModelProvider.h>
+#include <KisResourceModel.h>
+
+
+#include <KritaVersionWrapper.h>
+
+#include <kis_debug.h>
+#include <KisGlobalResourcesInterface.h>
+
+
+KoResourceBundle::KoResourceBundle(QString const& fileName)
+ : m_filename(fileName),
+ m_bundleVersion("1")
+{
+ m_metadata[KisResourceStorage::s_meta_generator] = "Krita (" + KritaVersionWrapper::versionString(true) + ")";
+}
+
+KoResourceBundle::~KoResourceBundle()
+{
+}
+
+QString KoResourceBundle::defaultFileExtension() const
+{
+ return QString(".bundle");
+}
+
+bool KoResourceBundle::load()
+{
+ if (m_filename.isEmpty()) return false;
+ QScopedPointer<KoStore> resourceStore(KoStore::createStore(m_filename, KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip));
+
+ if (!resourceStore || resourceStore->bad()) {
+ qWarning() << "Could not open store on bundle" << m_filename;
+ return false;
+
+ }
+ else {
+
+ m_metadata.clear();
+
+ if (resourceStore->open("META-INF/manifest.xml")) {
+ if (!m_manifest.load(resourceStore->device())) {
+ qWarning() << "Could not open manifest for bundle" << m_filename;
+ return false;
+ }
+ resourceStore->close();
+
+ Q_FOREACH (KoResourceBundleManifest::ResourceReference ref, m_manifest.files()) {
+ if (!resourceStore->open(ref.resourcePath)) {
+ qWarning() << "Bundle is broken. File" << ref.resourcePath << "is missing";
+ }
+ else {
+ resourceStore->close();
+ }
+ }
+
+ } else {
+ qWarning() << "Could not load META-INF/manifest.xml";
+ return false;
+ }
+
+ bool versionFound = false;
+ if (!readMetaData(resourceStore.data())) {
+ qWarning() << "Could not load meta.xml";
+ return false;
+ }
+
+ if (resourceStore->open("preview.png")) {
+ // Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice
+ // fails with "libpng error: IDAT: CRC error"
+ QByteArray data = resourceStore->device()->readAll();
+ QBuffer buffer(&data);
+ m_thumbnail.load(&buffer, "PNG");
+ resourceStore->close();
+ }
+ else {
+ qWarning() << "Could not open preview.png";
+ }
+
+ /*
+ * If no version is found it's an old bundle with md5 hashes to fix, or if some manifest resource entry
+ * doesn't not correspond to a file the bundle is "broken", in both cases we need to recreate the bundle.
+ */
+ if (!versionFound) {
+ m_metadata.insert(KisResourceStorage::s_meta_version, "1");
+ }
+
+ }
+
+ return true;
+}
+
+bool KoResourceBundle::loadFromDevice(QIODevice *)
+{
+ return false;
+}
+
+bool saveResourceToStore(KoResourceSP resource, KoStore *store, const QString &resType)
+{
+ if (!resource) {
+ qWarning() << "No Resource";
+ return false;
+ }
+
+ if (!resource->valid()) {
+ qWarning() << "Resource is not valid";
+ return false;
+ }
+ if (!store || store->bad()) {
+ qWarning() << "No Store or Store is Bad";
+ return false;
+ }
+
+ QByteArray ba;
+ QBuffer buf;
+ buf.open(QFile::ReadWrite);
+
+ bool response = resource->saveToDevice(&buf);
+ if (!response) {
+ ENTER_FUNCTION() << "Cannot save to device";
+ return false;
+ }
+
+ if (!store->open(resType + "/" + resource->filename())) {
+ qWarning() << "Could not open file in store for resource";
+ return false;
+ }
+
+ qint64 size = store->write(buf.data());
+ store->close();
+ buf.close();
+ if (size != buf.size()) {
+ ENTER_FUNCTION() << "Cannot save to the store" << size << buf.size();
+ }
+ return size == buf.size();
+}
+
+bool KoResourceBundle::save()
+{
+ if (m_filename.isEmpty()) return false;
+
+ setMetaData(KisResourceStorage::s_meta_dc_date, QDate::currentDate().toString("dd/MM/yyyy"));
+
+ QDir bundleDir = KoResourcePaths::saveLocation("data", "bundles");
+ bundleDir.cdUp();
+
+ QScopedPointer<KoStore> store(KoStore::createStore(m_filename, KoStore::Write, "application/x-krita-resourcebundle", KoStore::Zip));
+
+ if (!store || store->bad()) return false;
+
+ Q_FOREACH (const QString &resType, m_manifest.types()) {
+ KisResourceModel* model = KisResourceModelProvider::resourceModel(resType);
+ Q_FOREACH (const KoResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
+ KoResourceSP res = model->resourceForMD5(ref.md5sum);
+ if (!res) res = model->resourceForFilename(QFileInfo(ref.resourcePath).fileName());
+ qDebug() << "res is or isn't found: " << (res.isNull() ? "(null)" : res->name());
+ if (!saveResourceToStore(res, store.data(), resType)) {
+ if (res) {
+ qWarning() << "Could not save resource" << resType << res->name();
+ }
+ else {
+ qWarning() << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
+ }
+ }
+ }
+ }
+
+ if (!m_thumbnail.isNull()) {
+ QByteArray byteArray;
+ QBuffer buffer(&byteArray);
+ m_thumbnail.save(&buffer, "PNG");
+ if (!store->open("preview.png")) qWarning() << "Could not open preview.png";
+ if (store->write(byteArray) != buffer.size()) qWarning() << "Could not write preview.png";
+ store->close();
+ }
+
+ saveManifest(store);
+
+ saveMetadata(store);
+
+ store->finalize();
+
+ return true;
+}
+
+bool KoResourceBundle::saveToDevice(QIODevice */*dev*/) const
+{
+ return false;
+}
+
+void KoResourceBundle::setMetaData(const QString &key, const QString &value)
+{
+ m_metadata.insert(key, value);
+}
+
+const QString KoResourceBundle::metaData(const QString &key, const QString &defaultValue) const
+{
+ if (m_metadata.contains(key)) {
+ return m_metadata[key];
+ }
+ else {
+ return defaultValue;
+ }
+}
+
+void KoResourceBundle::addResource(QString resourceType, QString filePath, QVector<KisTagSP> fileTagList, const QByteArray md5sum)
+{
+ QStringList tags;
+ Q_FOREACH(KisTagSP tag, fileTagList) {
+ tags << tag->url();
+ }
+ m_manifest.addResource(resourceType, filePath, tags, md5sum);
+}
+
+QList<QString> KoResourceBundle::getTagsList()
+{
+ return QList<QString>(m_bundletags.begin(), m_bundletags.end());
+}
+
+QStringList KoResourceBundle::resourceTypes() const
+{
+ return m_manifest.types();
+}
+
+void KoResourceBundle::setThumbnail(QString filename)
+{
+ if (QFileInfo(filename).exists()) {
+ m_thumbnail = QImage(filename);
+ m_thumbnail = m_thumbnail.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ }
+ else {
+ m_thumbnail = QImage(256, 256, QImage::Format_ARGB32);
+ QPainter gc(&m_thumbnail);
+ gc.fillRect(0, 0, 256, 256, Qt::red);
+ gc.end();
+ }
+}
+
+void KoResourceBundle::writeMeta(const QString &metaTag, KoXmlWriter *writer)
+{
+ if (m_metadata.contains(metaTag)) {
+ writer->startElement(metaTag.toUtf8());
+ writer->addTextNode(m_metadata[metaTag].toUtf8());
+ writer->endElement();
+ }
+}
+
+void KoResourceBundle::writeUserDefinedMeta(const QString &metaTag, KoXmlWriter *writer)
+{
+ if (m_metadata.contains(metaTag)) {
+ writer->startElement("meta:meta-userdefined");
+ writer->addAttribute("meta:name", metaTag);
+ writer->addAttribute("meta:value", m_metadata[metaTag]);
+ writer->endElement();
+ }
+}
+
+bool KoResourceBundle::readMetaData(KoStore *resourceStore)
+{
+ if (resourceStore->open("meta.xml")) {
+ KoXmlDocument doc;
+ if (!doc.setContent(resourceStore->device())) {
+ qWarning() << "Could not parse meta.xml for" << m_filename;
+ return false;
+ }
+ // First find the manifest:manifest node.
+ KoXmlNode n = doc.firstChild();
+ for (; !n.isNull(); n = n.nextSibling()) {
+ if (!n.isElement()) {
+ continue;
+ }
+ if (n.toElement().tagName() == "meta:meta") {
+ break;
+ }
+ }
+
+ if (n.isNull()) {
+ qWarning() << "Could not find manifest node for bundle" << m_filename;
+ return false;
+ }
+
+ const KoXmlElement metaElement = n.toElement();
+ for (n = metaElement.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ if (n.isElement()) {
+ KoXmlElement e = n.toElement();
+ if (e.tagName() == "meta:meta-userdefined") {
+ if (e.attribute("meta:name") == "tag") {
+ m_bundletags << e.attribute("meta:value");
+ }
+ else {
+ m_metadata.insert(e.attribute("meta:name"), e.attribute("meta:value"));
+ }
+ }
+ else {
+ m_metadata.insert(e.tagName(), e.firstChild().toText().data());
+ }
+ }
+ }
+ resourceStore->close();
+ return true;
+ }
+ return false;
+}
+
+void KoResourceBundle::saveMetadata(QScopedPointer<KoStore> &store)
+{
+ QBuffer buf;
+
+ store->open("meta.xml");
+ buf.open(QBuffer::WriteOnly);
+
+ KoXmlWriter metaWriter(&buf);
+ metaWriter.startDocument("office:document-meta");
+ metaWriter.startElement("meta:meta");
+
+ writeMeta(KisResourceStorage::s_meta_generator, &metaWriter);
+
+ metaWriter.startElement(KisResourceStorage::s_meta_version.toUtf8());
+ metaWriter.addTextNode(m_bundleVersion.toUtf8());
+ metaWriter.endElement();
+
+ writeMeta(KisResourceStorage::s_meta_author, &metaWriter);
+ writeMeta(KisResourceStorage::s_meta_title, &metaWriter);
+ writeMeta(KisResourceStorage::s_meta_description, &metaWriter);
+ writeMeta(KisResourceStorage::s_meta_initial_creator, &metaWriter);
+ writeMeta(KisResourceStorage::s_meta_creator, &metaWriter);
+ writeMeta(KisResourceStorage::s_meta_creation_date, &metaWriter);
+ writeMeta(KisResourceStorage::s_meta_dc_date, &metaWriter);
+ writeUserDefinedMeta("email", &metaWriter);
+ writeUserDefinedMeta("license", &metaWriter);
+ writeUserDefinedMeta("website", &metaWriter);
+ Q_FOREACH (const QString &tag, m_bundletags) {
+ metaWriter.startElement(KisResourceStorage::s_meta_user_defined.toUtf8());
+ metaWriter.addAttribute(KisResourceStorage::s_meta_name.toUtf8(), "tag");
+ metaWriter.addAttribute(KisResourceStorage::s_meta_value.toUtf8(), tag);
+ metaWriter.endElement();
+ }
+
+ metaWriter.endElement(); // meta:meta
+ metaWriter.endDocument();
+
+ buf.close();
+ store->write(buf.data());
+ store->close();
+}
+
+void KoResourceBundle::saveManifest(QScopedPointer<KoStore> &store)
+{
+ store->open("META-INF/manifest.xml");
+ QBuffer buf;
+ buf.open(QBuffer::WriteOnly);
+ m_manifest.save(&buf);
+ buf.close();
+ store->write(buf.data());
+ store->close();
+}
+
+int KoResourceBundle::resourceCount() const
+{
+ return m_manifest.files().count();
+}
+
+KoResourceBundleManifest &KoResourceBundle::manifest()
+{
+ return m_manifest;
+}
+
+KoResourceSP KoResourceBundle::resource(const QString &resourceType, const QString &filepath)
+{
+ if (m_filename.isEmpty()) return 0;
+
+
+ QScopedPointer<KoStore> resourceStore(KoStore::createStore(m_filename, KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip));
+
+ if (!resourceStore || resourceStore->bad()) {
+ qWarning() << "Could not open store on bundle" << m_filename;
+ return 0;
+ }
+
+ if (!resourceStore->open(filepath)) {
+ qWarning() << "Could not open file in bundle" << filepath;
+ return 0;
+ }
+
+ QString mime = KisMimeDatabase::mimeTypeForSuffix(filepath);
+ KisResourceLoaderBase *loader = KisResourceLoaderRegistry::instance()->loader(resourceType, mime);
+ if (!loader) {
+ qWarning() << "Could not create loader for" << resourceType << filepath << mime;
+ return 0;
+ }
+ KoResourceSP res = loader->load(filepath, *resourceStore->device(), KisGlobalResourcesInterface::instance());
+ QString filename = QFileInfo(filepath).fileName();
+ if (!res.isNull()) {
+ // Note that res will be null for Special_dyna_dots.kpp which is a brush preset based on a deleted engine
+ res->setFilename(filename);
+ }
+ resourceStore->close();
+
+ return res;
+}
+
+QImage KoResourceBundle::image() const
+{
+ return m_thumbnail;
+}
+
+QString KoResourceBundle::filename() const
+{
+ return m_filename;
+}
diff --git a/libs/resources/KoResourceBundle.h b/libs/resources/KoResourceBundle.h
new file mode 100644
index 0000000000..d190867254
--- /dev/null
+++ b/libs/resources/KoResourceBundle.h
@@ -0,0 +1,144 @@
+/*
+ * 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 <QSharedPointer>
+
+#include <KoXmlWriter.h>
+
+#include <KoResource.h>
+#include "KoResourceBundleManifest.h"
+
+#include "kritaresources_export.h"
+
+#include <KisTag.h>
+
+class KoStore;
+
+/**
+ * @brief A KoResourceBundle is a zip file that contains resources,
+ * some metadata about the creator of the bundle and a manifest file
+ * that lists the contained resources.
+ */
+class KRITARESOURCES_EXPORT KoResourceBundle
+{
+
+public:
+ /**
+ * @brief ResourceBundle : Ctor * @param bundlePath the path of the bundle
+ */
+ KoResourceBundle(QString const& fileName);
+
+ /**
+ * @brief ~ResourceBundle : Dtor
+ */
+ virtual ~KoResourceBundle();
+
+ /**
+ * @brief defaultFileExtension
+ * @return the default file extension which should be when saving the resource
+ */
+ QString defaultFileExtension() const;
+
+ /**
+ * @brief load : Load this resource.
+ * @return true if succeed, false otherwise.
+ */
+ bool load();
+ bool loadFromDevice(QIODevice *dev);
+
+ /**
+ * @brief save : Save this resource.
+ * @return true if succeed, false otherwise.
+ */
+ bool save();
+
+ bool saveToDevice(QIODevice* dev) const;
+
+ /**
+ * @brief addMeta : Add a Metadata to the resource
+ * @param type type of the metadata
+ * @param value value of the metadata
+ */
+ void setMetaData(const QString &key, const QString &value);
+ const QString metaData(const QString &key, const QString &defaultValue = QString()) const;
+
+ /**
+ * @brief addFile : Add a file to the bundle
+ * @param fileType type of the resource file
+ * @param filePath path of the resource file
+ */
+ void addResource(QString fileType, QString filePath, QVector<KisTagSP> fileTagList, const QByteArray md5sum);
+
+ QList<QString> getTagsList();
+
+ 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);
+
+ QStringList resourceTypes() const;
+ int resourceCount() const;
+
+ KoResourceBundleManifest &manifest();
+
+ KoResourceSP resource(const QString &resourceType, const QString &filepath);
+
+ QImage image() const;
+
+ QString filename() const;
+
+private:
+
+ void writeMeta(const QString &metaTag, KoXmlWriter *writer);
+ void writeUserDefinedMeta(const QString &metaTag, KoXmlWriter *writer);
+ bool readMetaData(KoStore *resourceStore);
+
+private:
+ QImage m_thumbnail;
+ KoResourceBundleManifest m_manifest;
+ QMap<QString, QString> m_metadata;
+ QSet<QString> m_bundletags;
+ QList<QByteArray> m_gradientsMd5Installed;
+ QList<QByteArray> m_patternsMd5Installed;
+ QList<QByteArray> m_brushesMd5Installed;
+ QList<QByteArray> m_palettesMd5Installed;
+ QList<QByteArray> m_workspacesMd5Installed;
+ QList<QByteArray> m_presetsMd5Installed;
+ QString m_filename;
+ QString m_bundleVersion;
+
+};
+
+typedef QSharedPointer<KoResourceBundle> KoResourceBundleSP;
+
+#endif // KORESOURCEBUNDLE_H
diff --git a/libs/resources/KoResourceBundleManifest.cpp b/libs/resources/KoResourceBundleManifest.cpp
new file mode 100644
index 0000000000..16020f91e5
--- /dev/null
+++ b/libs/resources/KoResourceBundleManifest.cpp
@@ -0,0 +1,231 @@
+/* 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/>.
+*/
+#include "KoResourceBundleManifest.h"
+
+#include <QList>
+#include <QSet>
+#include <QString>
+#include <QDomDocument>
+#include <QDomElement>
+#include <QDomNode>
+#include <QDomNodeList>
+#include <QFileInfo>
+
+#include <KoXmlNS.h>
+#include <KoXmlReader.h>
+#include <KoXmlWriter.h>
+
+#include <kis_debug.h>
+
+#include "KisResourceTypes.h"
+
+QString resourceTypeToManifestType(const QString &type) {
+ if (type.startsWith("ko_")) {
+ return type.mid(3);
+ }
+ else if (type.startsWith("kis_")) {
+ return type.mid(4);
+ }
+ else {
+ return type;
+ }
+}
+
+QString manifestTypeToResourceType(const QString &type) {
+ if (type == ResourceType::Patterns || type == ResourceType::Gradients || type == ResourceType::Palettes) {
+ return "ko_" + type;
+ }
+ else {
+ return "kis_" + type;
+ }
+}
+
+KoResourceBundleManifest::KoResourceBundleManifest()
+{
+}
+
+KoResourceBundleManifest::~KoResourceBundleManifest()
+{
+}
+
+bool KoResourceBundleManifest::load(QIODevice *device)
+{
+ m_resources.clear();
+ if (!device->isOpen()) {
+ if (!device->open(QIODevice::ReadOnly)) {
+ return false;
+ }
+ }
+
+ KoXmlDocument manifestDocument;
+ QString errorMessage;
+ int errorLine;
+ int errorColumn;
+ if (!manifestDocument.setContent(device, true, &errorMessage, &errorLine, &errorColumn)) {
+ return false;
+ }
+
+ if (!errorMessage.isEmpty()) {
+ warnKrita << "Error parsing manifest" << errorMessage << "line" << errorLine << "column" << errorColumn;
+ return false;
+ }
+
+ // First find the manifest:manifest node.
+ KoXmlNode n = manifestDocument.firstChild();
+ for (; !n.isNull(); n = n.nextSibling()) {
+ if (!n.isElement()) {
+ continue;
+ }
+ if (n.toElement().localName() == "manifest" && n.toElement().namespaceURI() == KoXmlNS::manifest) {
+ break;
+ }
+ }
+
+ if (n.isNull()) {
+ // "Could not find manifest:manifest";
+ return false;
+ }
+
+ // Now loop through the children of the manifest:manifest and
+ // store all the manifest:file-entry elements.
+ const KoXmlElement manifestElement = n.toElement();
+ for (n = manifestElement.firstChild(); !n.isNull(); n = n.nextSibling()) {
+
+ if (!n.isElement())
+ continue;
+
+ KoXmlElement el = n.toElement();
+ if (!(el.localName() == "file-entry" && el.namespaceURI() == KoXmlNS::manifest))
+ continue;
+
+ QString fullPath = el.attributeNS(KoXmlNS::manifest, "full-path", QString());
+ QString mediaType = el.attributeNS(KoXmlNS::manifest, "media-type", QString());
+ QString md5sum = el.attributeNS(KoXmlNS::manifest, "md5sum", QString());
+ QString version = el.attributeNS(KoXmlNS::manifest, "version", QString());
+
+ QStringList tagList;
+ KoXmlNode tagNode = n.firstChildElement().firstChildElement();
+ while (!tagNode.isNull()) {
+ if (tagNode.firstChild().isText()) {
+ tagList.append(tagNode.firstChild().toText().data());
+ }
+ tagNode = tagNode.nextSibling();
+ }
+
+ // Only if fullPath is valid, should we store this entry.
+ // If not, we don't bother to find out exactly what is wrong, we just skip it.
+ if (!fullPath.isNull() && !mediaType.isEmpty() && !md5sum.isEmpty()) {
+ addResource(mediaType, fullPath, tagList, QByteArray::fromHex(md5sum.toLatin1()));
+ }
+ }
+
+ return true;
+}
+
+bool KoResourceBundleManifest::save(QIODevice *device)
+{
+ if (!device->isOpen()) {
+ if (!device->open(QIODevice::WriteOnly)) {
+ return false;
+ }
+ }
+ KoXmlWriter manifestWriter(device);
+ manifestWriter.startDocument("manifest:manifest");
+ manifestWriter.startElement("manifest:manifest");
+ manifestWriter.addAttribute("xmlns:manifest", KoXmlNS::manifest);
+ manifestWriter.addAttribute("manifest:version", "1.2");
+ manifestWriter.addManifestEntry("/", "application/x-krita-resourcebundle");
+
+ Q_FOREACH (QString resourceType, m_resources.uniqueKeys()) {
+ Q_FOREACH (const ResourceReference &resource, m_resources[resourceType].values()) {
+ manifestWriter.startElement("manifest:file-entry");
+ manifestWriter.addAttribute("manifest:media-type", resourceTypeToManifestType(resourceType));
+ manifestWriter.addAttribute("manifest:full-path", resourceTypeToManifestType(resourceType) + "/" + QFileInfo(resource.resourcePath).fileName());
+ manifestWriter.addAttribute("manifest:md5sum", QString(resource.md5sum.toHex()));
+ if (!resource.tagList.isEmpty()) {
+ manifestWriter.startElement("manifest:tags");
+ Q_FOREACH (const QString tag, resource.tagList) {
+ manifestWriter.startElement("manifest:tag");
+ manifestWriter.addTextNode(tag);
+ manifestWriter.endElement();
+ }
+ manifestWriter.endElement();
+ }
+ manifestWriter.endElement();
+ }
+ }
+
+ manifestWriter.endElement();
+ manifestWriter.endDocument();
+
+ return true;
+}
+
+void KoResourceBundleManifest::addResource(const QString &fileTypeName, const QString &fileName, const QStringList &fileTagList, const QByteArray &md5)
+{
+ ResourceReference ref(fileName, fileTagList, fileTypeName, md5);
+ if (!m_resources.contains(fileTypeName)) {
+ m_resources[fileTypeName] = QMap<QString, ResourceReference>();
+ }
+ m_resources[fileTypeName].insert(fileName, ref);
+}
+
+QStringList KoResourceBundleManifest::types() const
+{
+ return m_resources.keys();
+}
+
+QStringList KoResourceBundleManifest::tags() const
+{
+ QSet<QString> tags;
+ Q_FOREACH (const QString &type, m_resources.keys()) {
+ Q_FOREACH (const ResourceReference &ref, m_resources[type].values()) {
+ tags += QSet<QString>(ref.tagList.begin(), ref.tagList.end());
+ }
+ }
+ return QStringList(tags.begin(), tags.end());
+}
+
+QList<KoResourceBundleManifest::ResourceReference> KoResourceBundleManifest::files(const QString &type) const
+{
+ // If no type is specified we return all the resources
+ if(type.isEmpty()) {
+ QList<ResourceReference> resources;
+ QList<QMap<QString, ResourceReference> >::iterator i;
+ QList<QMap<QString, ResourceReference> > values = m_resources.values();
+ for(i = values.begin(); i != values.end(); ++i) {
+ resources.append(i->values());
+ }
+
+ return resources;
+ }
+ else if (!m_resources.contains(type)) {
+ return QList<KoResourceBundleManifest::ResourceReference>();
+ }
+ return m_resources[type].values();
+}
+
+void KoResourceBundleManifest::removeFile(QString fileName)
+{
+ QList<QString> tags;
+ Q_FOREACH (const QString &type, m_resources.keys()) {
+ if (m_resources[type].contains(fileName)) {
+ m_resources[type].remove(fileName);
+ }
+ }
+}
+
diff --git a/libs/resources/KoResourceBundleManifest.h b/libs/resources/KoResourceBundleManifest.h
new file mode 100644
index 0000000000..8b627f9b9e
--- /dev/null
+++ b/libs/resources/KoResourceBundleManifest.h
@@ -0,0 +1,101 @@
+/* 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 KORESOURCEBUNDLEMANIFEST_H
+#define KORESOURCEBUNDLEMANIFEST_H
+
+#include <QString>
+#include <QPair>
+#include <QMap>
+#include <QMultiMap>
+
+#include <kritaresources_export.h>
+
+class QIODevice;
+
+class KRITARESOURCES_EXPORT KoResourceBundleManifest
+{
+public:
+
+ struct ResourceReference {
+
+ 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
+ */
+ KoResourceBundleManifest();
+
+ /**
+ * @brief ~ResourceBundleManifest : Dtor
+ */
+ virtual ~KoResourceBundleManifest();
+
+
+ /**
+ * @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
+ * @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
diff --git a/libs/resources/KoResourcePaths.cpp b/libs/resources/KoResourcePaths.cpp
new file mode 100644
index 0000000000..1edb9d9f66
--- /dev/null
+++ b/libs/resources/KoResourcePaths.cpp
@@ -0,0 +1,592 @@
+/*
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "KoResourcePaths.h"
+
+#include <QGlobalStatic>
+#include <QString>
+#include <QStringList>
+#include <QMap>
+#include <QStandardPaths>
+#include <QDir>
+#include <QFileInfo>
+#include <QDebug>
+#include <QCoreApplication>
+#include <QMutex>
+#include "kis_debug.h"
+
+Q_GLOBAL_STATIC(KoResourcePaths, s_instance)
+
+
+static QString cleanup(const QString &path)
+{
+ return QDir::cleanPath(path);
+}
+
+
+static QStringList cleanup(const QStringList &pathList)
+{
+ QStringList cleanedPathList;
+ Q_FOREACH(const QString &path, pathList) {
+ cleanedPathList << cleanup(path);
+ }
+ return cleanedPathList;
+}
+
+
+static QString cleanupDirs(const QString &path)
+{
+ return QDir::cleanPath(path) + QDir::separator();
+}
+
+static QStringList cleanupDirs(const QStringList &pathList)
+{
+ QStringList cleanedPathList;
+ Q_FOREACH(const QString &path, pathList) {
+ cleanedPathList << cleanupDirs(path);
+ }
+ return cleanedPathList;
+}
+
+void appendResources(QStringList *dst, const QStringList &src, bool eliminateDuplicates)
+{
+ Q_FOREACH (const QString &resource, src) {
+ QString realPath = QDir::cleanPath(resource);
+ if (!eliminateDuplicates || !dst->contains(realPath)) {
+ *dst << realPath;
+ }
+ }
+}
+
+
+#ifdef Q_OS_WIN
+static const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
+#else
+static const Qt::CaseSensitivity cs = Qt::CaseSensitive;
+#endif
+
+#ifdef Q_OS_MACOS
+#include <ApplicationServices/ApplicationServices.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+#endif
+
+QString getInstallationPrefix() {
+#ifdef Q_OS_MACOS
+ QString appPath = qApp->applicationDirPath();
+
+ dbgResources << "1" << appPath;
+ appPath.chop(QString("MacOS/").length());
+ dbgResources << "2" << appPath;
+
+ bool makeInstall = QDir(appPath + "/../../../share/kritaplugins").exists();
+ bool inBundle = QDir(appPath + "/Resources/kritaplugins").exists();
+
+ QString bundlePath;
+
+ if (inBundle) {
+ bundlePath = appPath + "/";
+ }
+ else if (makeInstall) {
+ appPath.chop(QString("Contents/").length());
+ bundlePath = appPath + "/../../";
+ }
+ else {
+ qFatal("Cannot calculate the bundle path from the app path");
+ }
+
+ return bundlePath;
+#else
+#ifdef Q_OS_QWIN
+ QDir appdir(qApp->applicationDirPath());
+
+ // Corrects for mismatched case errors in path (qtdeclarative fails to load)
+ wchar_t buffer[1024];
+ QString absolute = appdir.absolutePath();
+ DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024);
+ rv = ::GetLongPathName(buffer, buffer, 1024);
+ QString correctedPath((QChar *)buffer);
+ appdir.setPath(correctedPath);
+ appdir.cdUp();
+ return appdir.canonicalPath();
+#else
+#ifdef Q_OS_ANDROID
+ // qApp->applicationDirPath() isn't writable and android system won't allow
+ // any files other than libraries
+ return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/";
+#else
+ return qApp->applicationDirPath() + "/../";
+#endif
+#endif
+#endif
+}
+
+class Q_DECL_HIDDEN KoResourcePaths::Private {
+public:
+ QMap<QString, QStringList> absolutes; // For each resource type, the list of absolute paths, from most local (most priority) to most global
+ QMap<QString, QStringList> relatives; // Same with relative paths
+
+ QMutex relativesMutex;
+ QMutex absolutesMutex;
+
+ QStringList aliases(const QString &type)
+ {
+ QStringList r;
+ QStringList a;
+ relativesMutex.lock();
+ if (relatives.contains(type)) {
+ r += relatives[type];
+ }
+ relativesMutex.unlock();
+ absolutesMutex.lock();
+ if (absolutes.contains(type)) {
+ a += absolutes[type];
+ }
+ absolutesMutex.unlock();
+
+ return r + a;
+ }
+
+ QStandardPaths::StandardLocation mapTypeToQStandardPaths(const QString &type)
+ {
+ if (type == "tmp") {
+ return QStandardPaths::TempLocation;
+ }
+ else if (type == "appdata") {
+ return QStandardPaths::AppDataLocation;
+ }
+ else if (type == "data") {
+ return QStandardPaths::AppDataLocation;
+ }
+ else if (type == "cache") {
+ return QStandardPaths::CacheLocation;
+ }
+ else if (type == "locale") {
+ return QStandardPaths::AppDataLocation;
+ }
+ else {
+ return QStandardPaths::AppDataLocation;
+ }
+ }
+};
+
+KoResourcePaths::KoResourcePaths()
+ : d(new Private)
+{
+}
+
+KoResourcePaths::~KoResourcePaths()
+{
+}
+
+QString KoResourcePaths::getApplicationRoot()
+{
+ return getInstallationPrefix();
+}
+
+void KoResourcePaths::addResourceType(const QString &type, const char *basetype,
+ const QString &relativeName, bool priority)
+{
+ s_instance->addResourceTypeInternal(type, QString::fromLatin1(basetype), relativeName, priority);
+}
+
+void KoResourcePaths::addResourceDir(const QString &type, const QString &dir, bool priority)
+{
+ s_instance->addResourceDirInternal(type, dir, priority);
+}
+
+QString KoResourcePaths::findResource(const QString &type, const QString &fileName)
+{
+ return cleanup(s_instance->findResourceInternal(type, fileName));
+}
+
+QStringList KoResourcePaths::findDirs(const QString &type)
+{
+ return cleanupDirs(s_instance->findDirsInternal(type));
+}
+
+QStringList KoResourcePaths::findAllResources(const QString &type,
+ const QString &filter,
+ SearchOptions options)
+{
+ return cleanup(s_instance->findAllResourcesInternal(type, filter, options));
+}
+
+QStringList KoResourcePaths::resourceDirs(const QString &type)
+{
+ return cleanupDirs(s_instance->resourceDirsInternal(type));
+}
+
+QString KoResourcePaths::saveLocation(const QString &type, const QString &suffix, bool create)
+{
+ return cleanupDirs(s_instance->saveLocationInternal(type, suffix, create));
+}
+
+QString KoResourcePaths::locate(const QString &type, const QString &filename)
+{
+ return cleanup(s_instance->locateInternal(type, filename));
+}
+
+QString KoResourcePaths::locateLocal(const QString &type, const QString &filename, bool createDir)
+{
+ return cleanup(s_instance->locateLocalInternal(type, filename, createDir));
+}
+
+void KoResourcePaths::addResourceTypeInternal(const QString &type, const QString &basetype,
+ const QString &relativename,
+ bool priority)
+{
+ Q_UNUSED(basetype);
+ if (relativename.isEmpty()) return;
+
+ QString copy = relativename;
+
+ Q_ASSERT(basetype == "data");
+
+ if (!copy.endsWith(QLatin1Char('/'))) {
+ copy += QLatin1Char('/');
+ }
+
+ d->relativesMutex.lock();
+ QStringList &rels = d->relatives[type]; // find or insert
+
+ if (!rels.contains(copy, cs)) {
+ if (priority) {
+ rels.prepend(copy);
+ } else {
+ rels.append(copy);
+ }
+ }
+ d->relativesMutex.unlock();
+
+ dbgResources << "addResourceType: type" << type << "basetype" << basetype << "relativename" << relativename << "priority" << priority << d->relatives[type];
+}
+
+void KoResourcePaths::addResourceDirInternal(const QString &type, const QString &absdir, bool priority)
+{
+ if (absdir.isEmpty() || type.isEmpty()) return;
+
+ // find or insert entry in the map
+ QString copy = absdir;
+ if (copy.at(copy.length() - 1) != QLatin1Char('/')) {
+ copy += QLatin1Char('/');
+ }
+
+ d->absolutesMutex.lock();
+ QStringList &paths = d->absolutes[type];
+ if (!paths.contains(copy, cs)) {
+ if (priority) {
+ paths.prepend(copy);
+ } else {
+ paths.append(copy);
+ }
+ }
+ d->absolutesMutex.unlock();
+
+ dbgResources << "addResourceDir: type" << type << "absdir" << absdir << "priority" << priority << d->absolutes[type];
+}
+
+QString KoResourcePaths::findResourceInternal(const QString &type, const QString &fileName)
+{
+ QStringList aliases = d->aliases(type);
+ dbgResources<< "aliases" << aliases << getApplicationRoot();
+ QString resource = QStandardPaths::locate(QStandardPaths::AppDataLocation, fileName, QStandardPaths::LocateFile);
+
+ if (resource.isEmpty()) {
+ Q_FOREACH (const QString &alias, aliases) {
+ resource = QStandardPaths::locate(d->mapTypeToQStandardPaths(type), alias + '/' + fileName, QStandardPaths::LocateFile);
+ if (QFile::exists(resource)) {
+ continue;
+ }
+ }
+ }
+ if (resource.isEmpty() || !QFile::exists(resource)) {
+ QString approot = getApplicationRoot();
+ Q_FOREACH (const QString &alias, aliases) {
+ resource = approot + "/share/" + alias + '/' + fileName;
+ if (QFile::exists(resource)) {
+ continue;
+ }
+ }
+ }
+ if (resource.isEmpty() || !QFile::exists(resource)) {
+ QString approot = getApplicationRoot();
+ Q_FOREACH (const QString &alias, aliases) {
+ resource = approot + "/share/krita/" + alias + '/' + fileName;
+ if (QFile::exists(resource)) {
+ continue;
+ }
+ }
+ }
+
+ if (resource.isEmpty() || !QFile::exists(resource)) {
+ QString extraResourceDirs = qgetenv("EXTRA_RESOURCE_DIRS");
+ if (!extraResourceDirs.isEmpty()) {
+ Q_FOREACH(const QString &extraResourceDir, extraResourceDirs.split(':', QString::SkipEmptyParts)) {
+ if (aliases.isEmpty()) {
+ resource = extraResourceDir + '/' + fileName;
+ dbgResources<< "\t4" << resource;
+ if (QFile::exists(resource)) {
+ continue;
+ }
+ }
+ else {
+ Q_FOREACH (const QString &alias, aliases) {
+ resource = extraResourceDir + '/' + alias + '/' + fileName;
+ dbgResources<< "\t4" << resource;
+ if (QFile::exists(resource)) {
+ continue;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ dbgResources<< "findResource: type" << type << "filename" << fileName << "resource" << resource;
+ Q_ASSERT(!resource.isEmpty());
+ return resource;
+}
+
+
+QStringList filesInDir(const QString &startdir, const QString & filter, bool recursive)
+{
+ dbgResources << "filesInDir: startdir" << startdir << "filter" << filter << "recursive" << recursive;
+ QStringList result;
+
+ // First the entries in this path
+ QStringList nameFilters;
+ nameFilters << filter;
+ const QStringList fileNames = QDir(startdir).entryList(nameFilters, QDir::Files | QDir::CaseSensitive, QDir::Name);
+ dbgResources << "\tFound:" << fileNames.size() << ":" << fileNames;
+ Q_FOREACH (const QString &fileName, fileNames) {
+ QString file = startdir + '/' + fileName;
+ result << file;
+ }
+
+ // And then everything underneath, if recursive is specified
+ if (recursive) {
+ const QStringList entries = QDir(startdir).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ Q_FOREACH (const QString &subdir, entries) {
+ dbgResources << "\tGoing to look in subdir" << subdir << "of" << startdir;
+ result << filesInDir(startdir + '/' + subdir, filter, recursive);
+ }
+ }
+ return result;
+}
+
+QStringList KoResourcePaths::findDirsInternal(const QString &type)
+{
+ QStringList aliases = d->aliases(type);
+ dbgResources << type << aliases << d->mapTypeToQStandardPaths(type);
+
+ QStringList dirs;
+ QStringList standardDirs =
+ QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), "", QStandardPaths::LocateDirectory);
+ appendResources(&dirs, standardDirs, true);
+
+ Q_FOREACH (const QString &alias, aliases) {
+ QStringList aliasDirs =
+ QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias + '/', QStandardPaths::LocateDirectory);
+ appendResources(&dirs, aliasDirs, true);
+
+#ifdef Q_OS_MACOS
+ dbgResources << "MAC:" << getApplicationRoot();
+ QStringList bundlePaths;
+ bundlePaths << getApplicationRoot() + "/share/krita/" + alias;
+ bundlePaths << getApplicationRoot() + "/../share/krita/" + alias;
+ dbgResources << "bundlePaths" << bundlePaths;
+ appendResources(&dirs, bundlePaths, true);
+ Q_ASSERT(!dirs.isEmpty());
+#endif
+
+ QStringList fallbackPaths;
+ fallbackPaths << getApplicationRoot() + "/share/" + alias;
+ fallbackPaths << getApplicationRoot() + "/share/krita/" + alias;
+ appendResources(&dirs, fallbackPaths, true);
+
+ }
+ dbgResources << "findDirs: type" << type << "resource" << dirs;
+ return dirs;
+}
+
+
+QStringList KoResourcePaths::findAllResourcesInternal(const QString &type,
+ const QString &_filter,
+ SearchOptions options) const
+{
+ dbgResources << "=====================================================";
+ dbgResources << type << _filter << QStandardPaths::standardLocations(d->mapTypeToQStandardPaths(type));
+
+ bool recursive = options & KoResourcePaths::Recursive;
+
+ dbgResources << "findAllResources: type" << type << "filter" << _filter << "recursive" << recursive;
+
+ QStringList aliases = d->aliases(type);
+ QString filter = _filter;
+
+ // In cases where the filter is like "color-schemes/*.colors" instead of "*.kpp", used with unregistered resource types
+ if (filter.indexOf('*') > 0) {
+ aliases << filter.split('*').first();
+ filter = '*' + filter.split('*')[1];
+ dbgResources << "Split up alias" << aliases << "filter" << filter;
+ }
+
+ QStringList resources;
+ if (aliases.isEmpty()) {
+ QStringList standardResources =
+ QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type),
+ filter, QStandardPaths::LocateFile);
+ dbgResources << "standardResources" << standardResources;
+ appendResources(&resources, standardResources, true);
+ dbgResources << "1" << resources;
+ }
+
+
+ QString extraResourceDirs = qgetenv("EXTRA_RESOURCE_DIRS");
+ dbgResources << "extraResourceDirs" << extraResourceDirs;
+ if (!extraResourceDirs.isEmpty()) {
+ Q_FOREACH(const QString &extraResourceDir, extraResourceDirs.split(':', QString::SkipEmptyParts)) {
+ if (aliases.isEmpty()) {
+ appendResources(&resources, filesInDir(extraResourceDir + '/' + type, filter, recursive), true);
+ }
+ else {
+ Q_FOREACH (const QString &alias, aliases) {
+ appendResources(&resources, filesInDir(extraResourceDir + '/' + alias + '/', filter, recursive), true);
+ }
+ }
+ }
+
+ }
+
+ dbgResources << "\tresources from qstandardpaths:" << resources.size();
+
+ Q_FOREACH (const QString &alias, aliases) {
+ dbgResources << "\t\talias:" << alias;
+ QStringList dirs;
+
+ QFileInfo dirInfo(alias);
+ if (dirInfo.exists() && dirInfo.isDir() && dirInfo.isAbsolute()) {
+ dirs << alias;
+ } else {
+ dirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory)
+ << getInstallationPrefix() + "share/" + alias + "/"
+ << getInstallationPrefix() + "share/krita/" + alias + "/";
+ }
+
+ Q_FOREACH (const QString &dir, dirs) {
+ appendResources(&resources,
+ filesInDir(dir, filter, recursive),
+ true);
+ }
+ }
+
+ dbgResources << "\tresources also from aliases:" << resources.size();
+
+ // if the original filter is "input/*", we only want share/input/* and share/krita/input/* here, but not
+ // share/*. therefore, use _filter here instead of filter which was split into alias and "*".
+ QFileInfo fi(_filter);
+
+ QStringList prefixResources;
+ prefixResources << filesInDir(getInstallationPrefix() + "share/" + fi.path(), fi.fileName(), false);
+ prefixResources << filesInDir(getInstallationPrefix() + "share/krita/" + fi.path(), fi.fileName(), false);
+ appendResources(&resources, prefixResources, true);
+
+ dbgResources << "\tresources from installation:" << resources.size();
+ dbgResources << "=====================================================";
+
+ return resources;
+}
+
+QStringList KoResourcePaths::resourceDirsInternal(const QString &type)
+{
+ QStringList resourceDirs;
+ QStringList aliases = d->aliases(type);
+
+ Q_FOREACH (const QString &alias, aliases) {
+ QStringList aliasDirs;
+
+ aliasDirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory);
+
+ aliasDirs << getInstallationPrefix() + "share/" + alias + "/"
+ << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory);
+ aliasDirs << getInstallationPrefix() + "share/krita/" + alias + "/"
+ << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory);
+
+ appendResources(&resourceDirs, aliasDirs, true);
+ }
+
+ dbgResources << "resourceDirs: type" << type << resourceDirs;
+
+ return resourceDirs;
+}
+
+QString KoResourcePaths::saveLocationInternal(const QString &type, const QString &suffix, bool create)
+{
+ QStringList aliases = d->aliases(type);
+ QString path;
+ if (aliases.size() > 0) {
+ path = QStandardPaths::writableLocation(d->mapTypeToQStandardPaths(type)) + '/' + aliases.first();
+ }
+ else {
+ path = QStandardPaths::writableLocation(d->mapTypeToQStandardPaths(type));
+ if (!path.endsWith("krita")) {
+ path += "/krita";
+ }
+ if (!suffix.isEmpty()) {
+ path += "/" + suffix;
+ }
+ }
+
+ QDir d(path);
+
+ if (!d.exists() && create) {
+ d.mkpath(path);
+ }
+ dbgResources << "saveLocation: type" << type << "suffix" << suffix << "create" << create << "path" << path;
+
+ return path;
+}
+
+QString KoResourcePaths::locateInternal(const QString &type, const QString &filename)
+{
+ QStringList aliases = d->aliases(type);
+
+ QStringList locations;
+ if (aliases.isEmpty()) {
+ locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type), filename, QStandardPaths::LocateFile);
+ }
+
+ Q_FOREACH (const QString &alias, aliases) {
+ locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type),
+ (alias.endsWith('/') ? alias : alias + '/') + filename, QStandardPaths::LocateFile);
+ }
+ dbgResources << "locate: type" << type << "filename" << filename << "locations" << locations;
+ if (locations.size() > 0) {
+ return locations.first();
+ }
+ else {
+ return "";
+ }
+}
+
+QString KoResourcePaths::locateLocalInternal(const QString &type, const QString &filename, bool createDir)
+{
+ QString path = saveLocationInternal(type, "", createDir);
+ dbgResources << "locateLocal: type" << type << "filename" << filename << "CreateDir" << createDir << "path" << path;
+ return path + '/' + filename;
+}
diff --git a/libs/resources/KoResourcePaths.h b/libs/resources/KoResourcePaths.h
new file mode 100644
index 0000000000..f64b1a6737
--- /dev/null
+++ b/libs/resources/KoResourcePaths.h
@@ -0,0 +1,250 @@
+/*
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef KORESOURCEPATHS_H
+#define KORESOURCEPATHS_H
+
+#include <QScopedPointer>
+#include <QString>
+#include <QStringList>
+
+#include <kritaresources_export.h>
+
+
+/**
+ * DEBUGGING KoResourcePaths:
+ *
+ * The usual place to look for resources is Qt's AppDataLocation.
+ * This corresponds to XDG_DATA_DIRS on Linux. To ensure your installation and
+ * path are configured correctly, ensure your files are located in the directories
+ * contained in this variable:
+ *
+ * QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
+ *
+ * There are many debug lines that can be uncommented for more specific installation
+ * checks. In the future these should be converted to qloggingcategory to enable
+ * convenient enable/disable functionality.
+ */
+class KRITARESOURCES_EXPORT KoResourcePaths
+{
+public:
+
+ KoResourcePaths();
+ virtual ~KoResourcePaths();
+
+ enum SearchOption { NoSearchOptions = 0,
+ Recursive = 1,
+ IgnoreExecBit = 4
+ };
+ Q_DECLARE_FLAGS(SearchOptions, SearchOption)
+
+ static QString getApplicationRoot();
+
+ /**
+ * Adds suffixes for types.
+ *
+ * You may add as many as you need, but it is advised that there
+ * is exactly one to make writing definite.
+ *
+ * The later a suffix is added, the higher its priority. Note, that the
+ * suffix should end with / but doesn't have to start with one (as prefixes
+ * should end with one). So adding a suffix for app_pics would look
+ * like KoStandardPaths::addResourceType("app_pics", "data", "app/pics");
+ *
+ * @param type Specifies a short descriptive string to access
+ * files of this type.
+ * @param basetype Specifies an already known type, or 0 if none
+ * @param relativename Specifies a directory relative to the basetype
+ * @param priority if true, the directory is added before any other,
+ * otherwise after
+ */
+ static void addResourceType(const QString &type, const char *basetype,
+ const QString &relativeName, bool priority = true);
+
+
+ /**
+ * Adds absolute path at the beginning of the search path for
+ * particular types (for example in case of icons where
+ * the user specifies extra paths).
+ *
+ * You shouldn't need this function in 99% of all cases besides
+ * adding user-given paths.
+ *
+ * @param type Specifies a short descriptive string to access files
+ * of this type.
+ * @param absdir Points to directory where to look for this specific
+ * type. Non-existent directories may be saved but pruned.
+ * @param priority if true, the directory is added before any other,
+ * otherwise after
+ */
+ static void addResourceDir(const QString &type, const QString &dir, bool priority = true);
+
+ /**
+ * Tries to find a resource in the following order:
+ * @li All PREFIX/\<relativename> paths (most recent first).
+ * @li All absolute paths (most recent first).
+ *
+ * The filename should be a filename relative to the base dir
+ * for resources. So is a way to get the path to libkdecore.la
+ * to findResource("lib", "libkdecore.la"). KStandardDirs will
+ * then look into the subdir lib of all elements of all prefixes
+ * ($KDEDIRS) for a file libkdecore.la and return the path to
+ * the first one it finds (e.g. /opt/kde/lib/libkdecore.la).
+ *
+ * Example:
+ * @code
+ * QString iconfilename = KStandardPaths::findResource("icon",QString("oxygen/22x22/apps/ktip.png"));
+ * @endcode
+ *
+ * @param type The type of the wanted resource
+ * @param filename A relative filename of the resource.
+ *
+ * @return A full path to the filename specified in the second
+ * argument, or QString() if not found.
+ */
+
+ static QString findResource(const QString &type, const QString &fileName);
+
+ /**
+ * Tries to find all directories whose names consist of the
+ * specified type and a relative path. So
+ * findDirs("xdgdata-apps", "Settings") would return
+ * @li /home/joe/.local/share/applications/Settings/
+ * @li /usr/share/applications/Settings/
+ *
+ * (from the most local to the most global)
+ *
+ * Note that it appends @c / to the end of the directories,
+ * so you can use this right away as directory names.
+ *
+ * @param type The type of the base directory.
+ * @param reldir Relative directory.
+ *
+ * @return A list of matching directories, or an empty
+ * list if the resource specified is not found.
+ */
+ static QStringList findDirs(const QString &type);
+
+ /**
+ * Tries to find all resources with the specified type.
+ *
+ * The function will look into all specified directories
+ * and return all filenames in these directories.
+ *
+ * The "most local" files are returned before the "more global" files.
+ *
+ * @param type The type of resource to locate directories for.
+ * @param filter Only accept filenames that fit to filter. The filter
+ * may consist of an optional directory and a QRegExp
+ * wildcard expression. E.g. <tt>"images\*.jpg"</tt>.
+ * Use QString() if you do not want a filter.
+ * @param options if the flags passed include Recursive, subdirectories
+ * will also be search.
+ *
+ * @return List of all the files whose filename matches the
+ * specified filter.
+ */
+ static QStringList findAllResources(const QString &type,
+ const QString &filter = QString(),
+ SearchOptions options = NoSearchOptions);
+
+ /**
+ * @param type The type of resource
+ * @return The list of possible directories for the specified @p type.
+ * The function updates the cache if possible. If the resource
+ * type specified is unknown, it will return an empty list.
+ * Note, that the directories are assured to exist beside the save
+ * location, which may not exist, but is returned anyway.
+ */
+ static QStringList resourceDirs(const QString &type);
+
+ /**
+ * Finds a location to save files into for the given type
+ * in the user's home directory.
+ *
+ * @param type The type of location to return.
+ * @param suffix A subdirectory name.
+ * Makes it easier for you to create subdirectories.
+ * You can't pass filenames here, you _have_ to pass
+ * directory names only and add possible filename in
+ * that directory yourself. A directory name always has a
+ * trailing slash ('/').
+ * @param create If set, saveLocation() will create the directories
+ * needed (including those given by @p suffix).
+ *
+ * @return A path where resources of the specified type should be
+ * saved, or QString() if the resource type is unknown.
+ */
+ static QString saveLocation(const QString &type, const QString &suffix = QString(), bool create = true);
+
+ /**
+ * This function is just for convenience. It simply calls
+ * KoResourcePaths::findResource((type, filename).
+ *
+ * @param type The type of the wanted resource, see KStandardDirs
+ * @param filename A relative filename of the resource
+ *
+ * @return A full path to the filename specified in the second
+ * argument, or QString() if not found
+ **/
+ static QString locate(const QString &type, const QString &filename);
+
+ /**
+ * This function is much like locate. However it returns a
+ * filename suitable for writing to. No check is made if the
+ * specified @p filename actually exists. Missing directories
+ * are created. If @p filename is only a directory, without a
+ * specific file, @p filename must have a trailing slash.
+ *
+ * @param type The type of the wanted resource, see KStandardDirs
+ * @param filename A relative filename of the resource
+ *
+ * @return A full path to the filename specified in the second
+ * argument, or QString() if not found
+ **/
+ static QString locateLocal(const QString &type, const QString &filename, bool createDir = false);
+
+private:
+
+ void addResourceTypeInternal(const QString &type, const QString &basetype,
+ const QString &relativeName, bool priority);
+
+ void addResourceDirInternal(const QString &type, const QString &absdir, bool priority);
+
+ QString findResourceInternal(const QString &type, const QString &fileName);
+
+ QStringList findDirsInternal(const QString &type);
+
+ QStringList findAllResourcesInternal(const QString &type,
+ const QString &filter = QString(),
+ SearchOptions options = NoSearchOptions) const;
+
+ QStringList resourceDirsInternal(const QString &type);
+
+ QString saveLocationInternal(const QString &type, const QString &suffix = QString(), bool create = true);
+
+ QString locateInternal(const QString &type, const QString &filename);
+
+ QString locateLocalInternal(const QString &type, const QString &filename, bool createDir = false);
+
+ class Private;
+ QScopedPointer<Private> d;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(KoResourcePaths::SearchOptions)
+
+#endif // KORESOURCEPATHS_H
diff --git a/libs/resources/KoResourceServer.h b/libs/resources/KoResourceServer.h
new file mode 100644
index 0000000000..c4a5be28a9
--- /dev/null
+++ b/libs/resources/KoResourceServer.h
@@ -0,0 +1,359 @@
+/* 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>
+ Copyright (c) 2003-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 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 <QString>
+#include <QStringList>
+#include <QList>
+#include <QFileInfo>
+#include <QApplication>
+#include <QThread>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QDir>
+#include <QTemporaryFile>
+
+#include "KoResource.h"
+#include "KoResourceServerObserver.h"
+#include "KoResourcePaths.h"
+#include "ksharedconfig.h"
+
+#include <KisResourceModel.h>
+#include <KisResourceModelProvider.h>
+#include <KisTagModelProvider.h>
+#include <kis_assert.h>
+#include <kis_debug.h>
+
+#include <ResourceDebug.h>
+
+class KoResource;
+
+/**
+ * KoResourceServer is a shim around KisResourceModel. It knows
+ * nothing by its own, and does nothing on its own. It can only
+ * be used in the gui thread.
+ */
+template <class T>
+class KoResourceServer
+{
+public:
+
+ typedef KoResourceServerObserver<T> ObserverType;
+
+ KoResourceServer(const QString& type)
+ : m_resourceModel(KisResourceModelProvider::resourceModel(type))
+ , m_tagModel(KisTagModelProvider::tagModel(type))
+ , m_type(type)
+ {
+ KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
+ if (QThread::currentThread() != qApp->thread()) {
+ Q_FOREACH(const QString &s, kisBacktrace().split('\n')) {
+ qDebug() << s;
+ }
+ }
+
+ }
+
+ virtual ~KoResourceServer()
+ {
+ Q_FOREACH (ObserverType* observer, m_observers) {
+ observer->unsetResourceServer();
+ }
+ }
+
+ /// @return the active resource model
+ KisResourceModel *resourceModel() const
+ {
+ QMutexLocker l(&m_mutex);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
+ if (QThread::currentThread() != qApp->thread()) {
+ Q_FOREACH(const QString &s, kisBacktrace().split('\n')) {
+ qDebug() << s;
+ }
+ }
+
+ return m_resourceModel;
+ }
+
+ /// Return the first resource available
+ QSharedPointer<T> firstResource() const
+ {
+ QMutexLocker l(&m_mutex);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
+ if (QThread::currentThread() != qApp->thread()) {
+ Q_FOREACH(const QString &s, kisBacktrace().split('\n')) {
+ qDebug() << s;
+ }
+ }
+
+ if (QThread::currentThread() != qApp->thread()) {
+ Q_FOREACH(const QString &s, kisBacktrace().split('\n')) {
+ qDebug() << s;
+ }
+ }
+ return m_resourceModel->resourceForIndex(m_resourceModel->index(0, 0)).dynamicCast<T>();
+ }
+
+ int resourceCount() const {
+ QMutexLocker l(&m_mutex);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
+ return m_resourceModel->rowCount();
+ }
+
+ /// Adds an already loaded resource to the server
+ bool addResource(QSharedPointer<T> resource, bool save = true) {
+ QMutexLocker l(&m_mutex);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
+ if (QThread::currentThread() != qApp->thread()) {
+ Q_FOREACH(const QString &s, kisBacktrace().split('\n')) {
+ qDebug() << s;
+ }
+ }
+
+ if (!resource->valid()) {
+ warnResource << "Tried to add an invalid resource!";
+ return false;
+ }
+
+ if (m_resourceModel->addResource(resource, save ? resource->storageLocation() : "memory")) {
+ notifyResourceAdded(resource);
+ return true;
+ }
+
+ return false;
+ }
+
+ /// Remove a resource from Resource Server but not from a file
+ bool removeResourceFromServer(QSharedPointer<T> resource){
+ QMutexLocker l(&m_mutex);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
+ if (QThread::currentThread() != qApp->thread()) {
+ Q_FOREACH(const QString &s, kisBacktrace().split('\n')) {
+ qDebug() << s;
+ }
+ }
+
+ if (m_resourceModel->removeResource(resource)) {
+ notifyRemovingResource(resource);
+ return true;
+ }
+ return false;
+ }
+
+ QList<QSharedPointer<T>> resources() {
+ QMutexLocker l(&m_mutex);
+ qDebug() << "KoResourceServer::resources()" << m_type;
+
+ KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
+ if (QThread::currentThread() != qApp->thread()) {
+ Q_FOREACH(const QString &s, kisBacktrace().split('\n')) {
+ qDebug() << s;
+ }
+ }
+
+ KIS_SAFE_ASSERT_RECOVER_NOOP(m_type != "paintoppresets");
+ QList<QSharedPointer<T>> resourceList;
+ for (int row = 0; row < m_resourceModel->rowCount(); ++row) {
+ resourceList << m_resourceModel->resourceForIndex(m_resourceModel->index(row, 0)).dynamicCast<T>();
+ }
+ return resourceList;
+ }
+
+ /// Returns path where to save user defined and imported resources to
+ QString saveLocation() {
+ return KoResourcePaths::saveLocation(m_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
+ */
+ bool importResourceFile(const QString &filename)
+ {
+ QMutexLocker l(&m_mutex);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
+ if (QThread::currentThread() != qApp->thread()) {
+ Q_FOREACH(const QString &s, kisBacktrace().split('\n')) {
+ qDebug() << s;
+ }
+ }
+
+ return m_resourceModel->importResourceFile(filename);
+ }
+
+ /// Removes the resource file from the resource server
+ void removeResourceFile(const QString & filename)
+ {
+ QFileInfo fi(filename);
+
+ QSharedPointer<T> resource = resourceByFilename(fi.fileName());
+ if (!resource) {
+ warnResource << "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)
+ {
+ if (observer && !m_observers.contains(observer)) {
+ m_observers.append(observer);
+ }
+ }
+
+ /**
+ * 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 );
+ }
+
+ QSharedPointer<T> resourceByFilename(const QString& filename) const
+ {
+ KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
+ if (QThread::currentThread() != qApp->thread()) {
+ Q_FOREACH(const QString &s, kisBacktrace().split('\n')) {
+ qDebug() << s;
+ }
+ }
+
+ QMutexLocker l(&m_mutex);
+ qDebug() << "resourceByFilename" << filename;
+ if (filename.isEmpty() || filename.isNull()) {
+ return 0;
+ }
+ return m_resourceModel->resourceForFilename(filename).dynamicCast<T>();
+ }
+
+
+ QSharedPointer<T> resourceByName(const QString& name) const
+ {
+ KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
+ if (QThread::currentThread() != qApp->thread()) {
+ Q_FOREACH(const QString &s, kisBacktrace().split('\n')) {
+ qDebug() << s;
+ }
+ }
+
+ QMutexLocker l(&m_mutex);
+ qDebug() << "resourceByName" << name;
+ if (name.isEmpty() || name.isNull()) {
+ return 0;
+ }
+ return m_resourceModel->resourceForName(name).dynamicCast<T>();
+
+ }
+
+ QSharedPointer<T> resourceByMD5(const QByteArray& md5) const
+ {
+ KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
+ if (QThread::currentThread() != qApp->thread()) {
+ Q_FOREACH(const QString &s, kisBacktrace().split('\n')) {
+ qDebug() << s;
+ }
+ }
+
+ QMutexLocker l(&m_mutex);
+ qDebug() << "resourceByMD5" << md5.toHex();
+ if (md5.isEmpty() || md5.isNull()) {
+ return 0;
+ }
+ return m_resourceModel->resourceForMD5(md5).dynamicCast<T>();
+ }
+
+ /**
+ * Call after changing the content of a resource;
+ * Notifies the connected views.
+ */
+ void updateResource(QSharedPointer<T> resource)
+ {
+ QMutexLocker l(&m_mutex);
+
+ KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread());
+ if (QThread::currentThread() != qApp->thread()) {
+ Q_FOREACH(const QString &s, kisBacktrace().split('\n')) {
+ qDebug() << s;
+ }
+ }
+ m_resourceModel->updateResource(resource);
+ notifyResourceChanged(resource);
+ }
+
+ QVector<KisTagSP> assignedTagsList(KoResourceSP resource) const
+ {
+ if (resource.isNull()) {
+ return QVector<KisTagSP>();
+ }
+ return m_resourceModel->tagsForResource(resource->resourceId());
+ }
+
+protected:
+
+ void notifyResourceAdded(QSharedPointer<T> resource)
+ {
+ Q_FOREACH (ObserverType* observer, m_observers) {
+ observer->resourceAdded(resource);
+ }
+ }
+
+ void notifyRemovingResource(QSharedPointer<T> resource)
+ {
+ Q_FOREACH (ObserverType* observer, m_observers) {
+ observer->removingResource(resource);
+ }
+ }
+
+ void notifyResourceChanged(QSharedPointer<T> resource)
+ {
+ Q_FOREACH (ObserverType* observer, m_observers) {
+ observer->resourceChanged(resource);
+ }
+ }
+
+private:
+
+ QList<ObserverType*> m_observers;
+ KisResourceModel *m_resourceModel {0};
+ KisTagModel *m_tagModel {0};
+ QString m_type;
+ mutable QMutex m_mutex;
+};
+
+#endif // KORESOURCESERVER_H
diff --git a/libs/resources/Mainpage.dox b/libs/resources/Mainpage.dox
new file mode 100644
index 0000000000..658f33ce06
--- /dev/null
+++ b/libs/resources/Mainpage.dox
@@ -0,0 +1,33 @@
+/**
+ \mainpage Resources
+
+ The Resources library provides cached access to resources. Resources are things
+ like brush presets, brush tips or gradients. The following components play an
+ important role in the library:
+
+ - KisResourceModel: the public entry point to the library. This is a QAbstractItemModel
+ and can be used to represent resources in the user interface without loading the
+ actual resource until it is needed. KisResourceModel is available through KisResourceModelProvider.
+
+ There is only one instance of a KisResourceModel for a given resource type, and KisResourceModel
+ can only be used from the gui thread. Accessing the data() method will in general not load the
+ actual resource.
+
+ - KisResourceLocator: KisResourceLocator should not be used outside of the resources library.
+ Its task is to load the resources from the resource storages: resource storages are the
+ resource folder, bundles, ASL or ABR files that contain a collection of resources. Once a
+ resource is loaded, it's cached. Loading resources is handled through the KisResourceLoader
+ classes, while finding resources in the storages is handled through the KisStoragePlugin
+ implementations.
+
+ All resources are captured in a QSharedPointer and should not be used naked.
+
+ KisResourceLocator is also repsonsible for initializing the cache database.
+
+ - KisResourceCacheDB: this class creates the sqlite database and handles synchronizing resources
+ with he contents of storages.
+
+ */
+
+// DOXYGEN_SET_PROJECT_NAME = KritaResources
+// DOXYGEN_SET_IGNORE_PREFIX = Kis Ko K
diff --git a/libs/resources/NOTES.md b/libs/resources/NOTES.md
new file mode 100644
index 0000000000..e97241a355
--- /dev/null
+++ b/libs/resources/NOTES.md
@@ -0,0 +1,31 @@
+DONE
+
+ * copy over resources to the user's resources folder
+ * initial creation of the database schema
+ * initial filling of the database
+ * folder storage class
+ * tag loading from desktop files`
+ * updating the cache db with changes done directly on the resources folder
+ * bundle storage class
+ * Database explorer ui
+ * models for integrating with the ui
+ * implement dirty resources mechanism
+ * Remove all hints that bundles are resources.
+ * implement metadata mechanism for resources --> the KisResourceModel class has many unimplemented methods atm
+ * Implement in-memory resources, that are deleted from the db when Krita starts (add to locator cache, set id on resource, add to database, update model)
+
+DOING
+
+ * Implement KoResourceServer as a shim for KoResourceModel
+ * Make KisFavoriteManager a model-view class for presets and tags
+ * fix loading the actual preset in the preset delegate
+
+TODO
+
+ * asl storage class
+ * abr storage class
+ * adding/updating/removing resources from the ui
+ * adding/removing tags from the ui
+ * database migration and versioning (loop over version, find update_from_version, execute and so on until version is up to date)
+ * fetch gradients from resource server by md5 or name when handling layer styles: fix how layer styles store gradients
+ * fix reloading the preset if dirty presets is switched off (KisPaintopBox::resourceSelected). The problem is that the preset itself no longer knows where it comes from or how it should be loaded
diff --git a/libs/resources/ResourceDebug.cpp b/libs/resources/ResourceDebug.cpp
new file mode 100644
index 0000000000..674155587c
--- /dev/null
+++ b/libs/resources/ResourceDebug.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020 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 "ResourceDebug.h"
+
+const QLoggingCategory &RESOURCE_LOG() \
+{
+ static const QLoggingCategory category("krita.lib.resource", QtInfoMsg);
+ return category;
+}
+
+
diff --git a/libs/resources/ResourceDebug.h b/libs/resources/ResourceDebug.h
new file mode 100644
index 0000000000..d5cf113b1d
--- /dev/null
+++ b/libs/resources/ResourceDebug.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 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 RESOURCE_DEBUG_H_
+#define RESOURCE_DEBUG_H_
+
+#include <QDebug>
+#include <QLoggingCategory>
+#include <kritaresources_export.h>
+
+extern const KRITARESOURCES_EXPORT QLoggingCategory &RESOURCE_LOG();
+
+#define debugResource qCDebug(RESOURCE_LOG)
+#define warnResource qCWarning(RESOURCE_LOG)
+#define errorResource qCCritical(RESOURCE_LOG)
+
+#endif
diff --git a/libs/resources/bufferfragment_p.h b/libs/resources/bufferfragment_p.h
new file mode 100644
index 0000000000..bbc09a2bf7
--- /dev/null
+++ b/libs/resources/bufferfragment_p.h
@@ -0,0 +1,215 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 2008 Jakub Stachowski <qbast@go2.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 BUFFERFRAGMENT_H
+#define BUFFERFRAGMENT_H
+
+#include "kconfigini_p.h"
+
+#define bf_isspace(str) ((str == ' ') || (str == '\t') || (str == '\r'))
+
+// This class provides wrapper around fragment of existing buffer (array of bytes).
+// If underlying buffer gets deleted, all BufferFragment objects referencing it become invalid.
+// Use toByteArray() to make deep copy of the buffer fragment.
+//
+// API is designed to subset of QByteArray methods with some changes:
+// - trim() is like QByteArray.trimmed(), but it modifies current object
+// - truncateLeft() provides way to cut off beginning of the buffer
+// - split() works more like strtok_r than QByteArray.split()
+// - truncateLeft() and mid() require position argument to be valid
+
+class KConfigIniBackend::BufferFragment
+{
+
+public:
+
+ BufferFragment() : d(nullptr), len(0)
+ {
+ }
+
+ BufferFragment(char *buf, int size) : d(buf), len(size)
+ {
+ }
+
+ int length() const
+ {
+ return len;
+ }
+
+ char at(unsigned int i) const
+ {
+ Q_ASSERT(i < len);
+ return d[i];
+ }
+
+ void clear()
+ {
+ len = 0;
+ }
+
+ const char *constData() const
+ {
+ return d;
+ }
+
+ char *data() const
+ {
+ return d;
+ }
+
+ void trim()
+ {
+ while (bf_isspace(*d) && len > 0) {
+ d++;
+ len--;
+ }
+ while (len > 0 && bf_isspace(d[len - 1])) {
+ len--;
+ }
+ }
+
+ // similar to strtok_r . On first call variable pointed by start should be set to 0.
+ // Each call will update *start to new starting position.
+ BufferFragment split(char c, unsigned int *start)
+ {
+ while (*start < len) {
+ int end = indexOf(c, *start);
+ if (end == -1) {
+ end = len;
+ }
+ BufferFragment line(d + (*start), end - (*start));
+ *start = end + 1;
+ return line;
+ }
+ return BufferFragment();
+ }
+
+ bool isEmpty() const
+ {
+ return !len;
+ }
+
+ BufferFragment left(unsigned int size) const
+ {
+ return BufferFragment(d, qMin(size, len));
+ }
+
+ void truncateLeft(unsigned int size)
+ {
+ Q_ASSERT(size <= len);
+ d += size;
+ len -= size;
+ }
+
+ void truncate(unsigned int pos)
+ {
+ if (pos < len) {
+ len = pos;
+ }
+ }
+
+ bool isNull() const
+ {
+ return !d;
+ }
+
+ BufferFragment mid(unsigned int pos, int length = -1) const
+ {
+ Q_ASSERT(pos < len);
+ int size = length;
+ if (length == -1 || (pos + length) > len) {
+ size = len - pos;
+ }
+ return BufferFragment(d + pos, size);
+ }
+
+ bool operator==(const QByteArray &other) const
+ {
+ return (other.size() == (int)len && memcmp(d, other.constData(), len) == 0);
+ }
+
+ bool operator!=(const QByteArray &other) const
+ {
+ return (other.size() != (int)len || memcmp(d, other.constData(), len) != 0);
+ }
+
+ bool operator==(const BufferFragment &other) const
+ {
+ return other.len == len && !memcmp(d, other.d, len);
+ }
+
+ int indexOf(char c, unsigned int from = 0) const
+ {
+ const char *cursor = d + from - 1;
+ const char *end = d + len;
+ while (++cursor < end)
+ if (*cursor == c) {
+ return cursor - d;
+ }
+ return -1;
+ }
+
+ int lastIndexOf(char c) const
+ {
+ int from = len - 1;
+ while (from >= 0)
+ if (d[from] == c) {
+ return from;
+ } else {
+ from--;
+ }
+ return -1;
+ }
+
+ QByteArray toByteArray() const
+ {
+ return QByteArray(d, len);
+ }
+
+ // this is faster than toByteArray, but returned QByteArray becomes invalid
+ // when buffer for this BufferFragment disappears
+ QByteArray toVolatileByteArray() const
+ {
+ return QByteArray::fromRawData(d, len);
+ }
+
+private:
+ char *d;
+ unsigned int len;
+};
+
+uint qHash(const KConfigIniBackend::BufferFragment &fragment)
+{
+ const uchar *p = reinterpret_cast<const uchar*>(fragment.constData());
+ const int len = fragment.length();
+
+ // This algorithm is copied from qhash.cpp (Qt5 version).
+ // Sadly this code is not accessible from the outside without going through abstraction
+ // layers. Even QByteArray::fromRawData would do an allocation internally...
+ uint h = 0;
+
+ for (int i = 0; i < len; ++i) {
+ h = 31 * h + p[i];
+ }
+
+ return h;
+}
+
+#endif
diff --git a/libs/resources/kconfigbackend.cpp b/libs/resources/kconfigbackend.cpp
new file mode 100644
index 0000000000..6063f9b34d
--- /dev/null
+++ b/libs/resources/kconfigbackend.cpp
@@ -0,0 +1,82 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 2006 Thomas Braxton <brax108@cox.net>
+ Copyright (c) 1999 Preston Brown <pbrown@kde.org>
+ Copyright (c) 1997-1999 Matthias Kalle Dalheimer <kalle@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 "kconfigbackend_p.h"
+
+#include <QDateTime>
+#include <QStringList>
+#include <QDir>
+#include <QFileInfo>
+#include <QHash>
+#include <QDebug>
+
+#include "kconfigini_p.h"
+#include "kconfigdata.h"
+
+typedef QExplicitlySharedDataPointer<KConfigBackend> BackendPtr;
+
+class KConfigBackendPrivate
+{
+public:
+ QString localFileName;
+
+ static QString whatSystem(const QString & /*fileName*/)
+ {
+ return QStringLiteral("INI");
+ }
+};
+
+void KConfigBackend::registerMappings(const KEntryMap & /*entryMap*/)
+{
+}
+
+BackendPtr KConfigBackend::create(const QString &file)
+{
+ //qDebug() << "creating a backend for file" << file << "with system" << sys;
+ KConfigBackend *backend = nullptr;
+
+ //qDebug() << "default creation of the Ini backend";
+ backend = new KConfigIniBackend;
+ backend->setFilePath(file);
+ return BackendPtr(backend);
+}
+
+KConfigBackend::KConfigBackend()
+ : d(new KConfigBackendPrivate)
+{
+}
+
+KConfigBackend::~KConfigBackend()
+{
+ delete d;
+}
+
+QString KConfigBackend::filePath() const
+{
+ return d->localFileName;
+}
+
+void KConfigBackend::setLocalFilePath(const QString &file)
+{
+ d->localFileName = file;
+}
+
diff --git a/libs/resources/kconfigbackend_p.h b/libs/resources/kconfigbackend_p.h
new file mode 100644
index 0000000000..32721cbc6a
--- /dev/null
+++ b/libs/resources/kconfigbackend_p.h
@@ -0,0 +1,205 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
+ Copyright (c) 1999 Preston Brown <pbrown@kde.org>
+ Portions copyright (c) 1997 Matthias Kalle Dalheimer <kalle@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 KCONFIGBACKEND_H
+#define KCONFIGBACKEND_H
+
+#include <QObject>
+#include <QString>
+#include <QExplicitlySharedDataPointer>
+
+#include <kconfigbase.h>
+class KConfigBackendPrivate;
+
+class KEntryMap;
+class QFile;
+class QByteArray;
+class QIODevice;
+
+/**
+ * \class KConfigBackend kconfigbackend_p.h <KConfigBackend>
+ *
+ * Provides the implementation for accessing configuration sources.
+ *
+ * KConfig only provides an INI backend, but this class can be used
+ * to create plugins that allow access to other file formats and
+ * configuration systems.
+ *
+ * \internal
+ */
+class KConfigBackend : public QObject, public QSharedData
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Creates a new KConfig backend.
+ *
+ * If no @p system is given, or the given @p system is unknown, this method tries
+ * to determine the correct backend to use.
+ *
+ * @param fileName the absolute file name of the configuration file
+ * @param system the configuration system to use
+ * @return a KConfigBackend object to be used with KConfig
+ */
+ static QExplicitlySharedDataPointer<KConfigBackend> create(const QString &fileName = QString());
+
+ /**
+ * Registers mappings from directories/files to configuration systems
+ *
+ * Allows you to tell KConfigBackend that create() should use a particular
+ * backend for a particular file or directory.
+ *
+ * @warning currently does nothing
+ *
+ * @param entryMap the KEntryMap to build the mappings from
+ */
+ static void registerMappings(const KEntryMap &entryMap);
+
+ /** Destroys the backend */
+ virtual ~KConfigBackend();
+
+ /** Allows the behaviour of parseConfig() to be tuned */
+ enum ParseOption {
+ ParseGlobal = 1, /// entries should be marked as @em global
+ ParseDefaults = 2, /// entries should be marked as @em default
+ ParseExpansions = 4 /// entries are allowed to be marked as @em expandable
+ };
+ Q_FLAG(ParseOption)
+ /// @typedef typedef QFlags<ParseOption> ParseOptions
+ Q_DECLARE_FLAGS(ParseOptions, ParseOption)
+
+ /** Allows the behaviour of writeConfig() to be tuned */
+ enum WriteOption {
+ WriteGlobal = 1 /// only write entries marked as "global"
+ };
+ Q_FLAG(WriteOption)
+ /// @typedef typedef QFlags<WriteOption> WriteOptions
+ Q_DECLARE_FLAGS(WriteOptions, WriteOption)
+
+ /** Return value from parseConfig() */
+ enum ParseInfo {
+ ParseOk, /// the configuration was opened read/write
+ ParseImmutable, /// the configuration is @em immutable
+ ParseOpenError /// the configuration could not be opened
+ };
+
+ /**
+ * Read persistent storage
+ *
+ * @param locale the locale to read entries for (if the backend supports localized entries)
+ * @param pWriteBackMap the KEntryMap where the entries are placed
+ * @param options See ParseOptions
+ * @return See ParseInfo
+ */
+ virtual ParseInfo parseConfig(const QByteArray &locale,
+ KEntryMap &pWriteBackMap,
+ ParseOptions options = ParseOptions()) = 0;
+
+ /**
+ * Write the @em dirty entries to permanent storage
+ *
+ * @param locale the locale to write entries for (if the backend supports localized entries)
+ * @param entryMap the KEntryMap containing the config object's entries.
+ * @param options See WriteOptions
+ *
+ * @return @c true if the write was successful, @c false if writing the configuration failed
+ */
+ virtual bool writeConfig(const QByteArray &locale, KEntryMap &entryMap,
+ WriteOptions options) = 0;
+
+ /**
+ * If isWritable() returns false, writeConfig() will always fail.
+ *
+ * @return @c true if the configuration is writable, @c false if it is immutable
+ */
+ virtual bool isWritable() const = 0;
+ /**
+ * When isWritable() returns @c false, return an error message to
+ * explain to the user why saving configuration will not work.
+ *
+ * The return value when isWritable() returns @c true is undefined.
+ *
+ * @returns a translated user-visible explanation for the configuration
+ * object not being writable
+ */
+ virtual QString nonWritableErrorMessage() const = 0;
+ /**
+ * @return the read/write status of the configuration object
+ *
+ * @see KConfigBase::AccessMode
+ */
+ virtual KConfigBase::AccessMode accessMode() const = 0;
+ /**
+ * Create the enclosing object of the configuration object
+ *
+ * For example, if the configuration object is a file, this should create
+ * the parent directory.
+ */
+ virtual void createEnclosing() = 0;
+
+ /**
+ * Set the file path.
+ *
+ * @note @p path @b MUST be @em absolute.
+ *
+ * @param path the absolute file path
+ */
+ virtual void setFilePath(const QString &path) = 0;
+
+ /**
+ * Lock the file
+ */
+ virtual bool lock() = 0;
+ /**
+ * Release the lock on the file
+ */
+ virtual void unlock() = 0;
+ /**
+ * @return @c true if the file is locked, @c false if it is not locked
+ */
+ virtual bool isLocked() const = 0;
+
+ /** @return the absolute path to the object */
+ QString filePath() const;
+
+protected:
+ KConfigBackend();
+ void setLocalFilePath(const QString &file);
+ void setLocalIODevice(QIODevice &io);
+
+private:
+ KConfigBackendPrivate *const d;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(KConfigBackend::ParseOptions)
+Q_DECLARE_OPERATORS_FOR_FLAGS(KConfigBackend::WriteOptions)
+
+#if 0 // TODO re-enable if the plugin loading code is re-enabled
+/**
+ * Register a KConfig backend when it is contained in a loadable module
+ */
+#define K_EXPORT_KCONFIGBACKEND(libname, classname) \
+ K_PLUGIN_FACTORY(factory, registerPlugin<classname>();)
+#endif
+
+#endif // KCONFIGBACKEND_H
diff --git a/libs/resources/kconfigdata.cpp b/libs/resources/kconfigdata.cpp
new file mode 100644
index 0000000000..6ef6af076e
--- /dev/null
+++ b/libs/resources/kconfigdata.cpp
@@ -0,0 +1,333 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
+ Copyright (c) 1999-2000 Preston Brown <pbrown@kde.org>
+ Copyright (C) 1996-2000 Matthias Kalle Dalheimer <kalle@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 <kconfigdata.h>
+
+QDebug operator<<(QDebug dbg, const KEntryKey &key)
+{
+ dbg.nospace() << "[" << key.mGroup << ", " << key.mKey << (key.bLocal ? " localized" : "") <<
+ (key.bDefault ? " default" : "") << (key.bRaw ? " raw" : "") << "]";
+ return dbg.space();
+}
+
+QDebug operator<<(QDebug dbg, const KEntry &entry)
+{
+ dbg.nospace() << "[" << entry.mValue << (entry.bDirty ? " dirty" : "") <<
+ (entry.bGlobal ? " global" : "") << (entry.bImmutable ? " immutable" : "") <<
+ (entry.bDeleted ? " deleted" : "") << (entry.bReverted ? " reverted" : "") <<
+ (entry.bExpand ? " expand" : "") << "]";
+
+ return dbg.space();
+}
+
+QMap< KEntryKey, KEntry >::Iterator KEntryMap::findExactEntry(const QByteArray &group, const QByteArray &key, KEntryMap::SearchFlags flags)
+{
+ KEntryKey theKey(group, key, bool(flags & SearchLocalized), bool(flags & SearchDefaults));
+ return find(theKey);
+}
+
+QMap< KEntryKey, KEntry >::Iterator KEntryMap::findEntry(const QByteArray &group, const QByteArray &key, KEntryMap::SearchFlags flags)
+{
+ KEntryKey theKey(group, key, false, bool(flags & SearchDefaults));
+
+ // try the localized key first
+ if (flags & SearchLocalized) {
+ theKey.bLocal = true;
+
+ Iterator it = find(theKey);
+ if (it != end()) {
+ return it;
+ }
+
+ theKey.bLocal = false;
+ }
+ return find(theKey);
+}
+
+QMap< KEntryKey, KEntry >::ConstIterator KEntryMap::findEntry(const QByteArray &group, const QByteArray &key, KEntryMap::SearchFlags flags) const
+{
+ KEntryKey theKey(group, key, false, bool(flags & SearchDefaults));
+
+ // try the localized key first
+ if (flags & SearchLocalized) {
+ theKey.bLocal = true;
+
+ ConstIterator it = find(theKey);
+ if (it != constEnd()) {
+ return it;
+ }
+
+ theKey.bLocal = false;
+ }
+ return find(theKey);
+}
+
+bool KEntryMap::setEntry(const QByteArray &group, const QByteArray &key, const QByteArray &value, KEntryMap::EntryOptions options)
+{
+ KEntryKey k;
+ KEntry e;
+ bool newKey = false;
+
+ const Iterator it = findExactEntry(group, key, SearchFlags(options >> 16));
+
+ if (key.isEmpty()) { // inserting a group marker
+ k.mGroup = group;
+ e.bImmutable = (options & EntryImmutable);
+ if (options & EntryDeleted) {
+ qWarning("Internal KConfig error: cannot mark groups as deleted");
+ }
+ if (it == end()) {
+ insert(k, e);
+ return true;
+ } else if (it.value() == e) {
+ return false;
+ }
+
+ it.value() = e;
+ return true;
+ }
+
+ if (it != end()) {
+ if (it->bImmutable) {
+ return false; // we cannot change this entry. Inherits group immutability.
+ }
+ k = it.key();
+ e = *it;
+ //qDebug() << "found existing entry for key" << k;
+ } else {
+ // make sure the group marker is in the map
+ KEntryMap const *that = this;
+ ConstIterator cit = that->findEntry(group);
+ if (cit == constEnd()) {
+ insert(KEntryKey(group), KEntry());
+ } else if (cit->bImmutable) {
+ return false; // this group is immutable, so we cannot change this entry.
+ }
+
+ k = KEntryKey(group, key);
+ newKey = true;
+ }
+
+ // set these here, since we may be changing the type of key from the one we found
+ k.bLocal = (options & EntryLocalized);
+ k.bDefault = (options & EntryDefault);
+ k.bRaw = (options & EntryRawKey);
+
+ e.mValue = value;
+ e.bDirty = e.bDirty || (options & EntryDirty);
+ e.bGlobal = (options & EntryGlobal); //we can't use || here, because changes to entries in
+ //kdeglobals would be written to kdeglobals instead
+ //of the local config file, regardless of the globals flag
+ e.bImmutable = e.bImmutable || (options & EntryImmutable);
+ if (value.isNull()) {
+ e.bDeleted = e.bDeleted || (options & EntryDeleted);
+ } else {
+ e.bDeleted = false; // setting a value to a previously deleted entry
+ }
+ e.bExpand = (options & EntryExpansion);
+ e.bReverted = false;
+ if (options & EntryLocalized) {
+ e.bLocalizedCountry = (options & EntryLocalizedCountry);
+ } else {
+ e.bLocalizedCountry = false;
+ }
+
+ if (newKey) {
+ //qDebug() << "inserting" << k << "=" << value;
+ insert(k, e);
+ if (k.bDefault) {
+ k.bDefault = false;
+ //qDebug() << "also inserting" << k << "=" << value;
+ insert(k, e);
+ }
+ // TODO check for presence of unlocalized key
+ return true;
+ } else {
+// KEntry e2 = it.value();
+ if (options & EntryLocalized) {
+ // fast exit checks for cases where the existing entry is more specific
+ const KEntry &e2 = it.value();
+ if (e2.bLocalizedCountry && !e.bLocalizedCountry) {
+ // lang_COUNTRY > lang
+ return false;
+ }
+ }
+ if (it.value() != e) {
+ //qDebug() << "changing" << k << "from" << e.mValue << "to" << value;
+ it.value() = e;
+ if (k.bDefault) {
+ KEntryKey nonDefaultKey(k);
+ nonDefaultKey.bDefault = false;
+ insert(nonDefaultKey, e);
+ }
+ if (!(options & EntryLocalized)) {
+ KEntryKey theKey(group, key, true, false);
+ //qDebug() << "non-localized entry, remove localized one:" << theKey;
+ remove(theKey);
+ if (k.bDefault) {
+ theKey.bDefault = true;
+ remove(theKey);
+ }
+ }
+ return true;
+ } else {
+ //qDebug() << k << "was already set to" << e.mValue;
+ if (!(options & EntryLocalized)) {
+ //qDebug() << "unchanged non-localized entry, remove localized one.";
+ KEntryKey theKey(group, key, true, false);
+ bool ret = false;
+ Iterator cit = find(theKey);
+ if (cit != end()) {
+ erase(cit);
+ ret = true;
+ }
+ if (k.bDefault) {
+ theKey.bDefault = true;
+ Iterator cit = find(theKey);
+ if (cit != end()) {
+ erase(cit);
+ return true;
+ }
+ }
+ return ret;
+ }
+ //qDebug() << "localized entry, unchanged, return false";
+ // When we are writing a default, we know that the non-
+ // default is the same as the default, so we can simply
+ // use the same branch.
+ return false;
+ }
+ }
+}
+
+QString KEntryMap::getEntry(const QByteArray &group, const QByteArray &key, const QString &defaultValue, KEntryMap::SearchFlags flags, bool *expand) const
+{
+ const ConstIterator it = findEntry(group, key, flags);
+ QString theValue = defaultValue;
+
+ if (it != constEnd() && !it->bDeleted) {
+ if (!it->mValue.isNull()) {
+ const QByteArray data = it->mValue;
+ theValue = QString::fromUtf8(data.constData(), data.length());
+ if (expand) {
+ *expand = it->bExpand;
+ }
+ }
+ }
+
+ return theValue;
+}
+
+bool KEntryMap::hasEntry(const QByteArray &group, const QByteArray &key, KEntryMap::SearchFlags flags) const
+{
+ const ConstIterator it = findEntry(group, key, flags);
+ if (it == constEnd()) {
+ return false;
+ }
+ if (it->bDeleted) {
+ return false;
+ }
+ if (key.isNull()) { // looking for group marker
+ return it->mValue.isNull();
+ }
+ // if it->bReverted, we'll just return true; the real answer depends on lookup up with SearchDefaults, though.
+ return true;
+}
+
+bool KEntryMap::getEntryOption(const QMap< KEntryKey, KEntry >::ConstIterator &it, KEntryMap::EntryOption option) const
+{
+ if (it != constEnd()) {
+ switch (option) {
+ case EntryDirty:
+ return it->bDirty;
+ case EntryLocalized:
+ return it.key().bLocal;
+ case EntryGlobal:
+ return it->bGlobal;
+ case EntryImmutable:
+ return it->bImmutable;
+ case EntryDeleted:
+ return it->bDeleted;
+ case EntryExpansion:
+ return it->bExpand;
+ default:
+ break; // fall through
+ }
+ }
+
+ return false;
+}
+
+void KEntryMap::setEntryOption(QMap< KEntryKey, KEntry >::Iterator it, KEntryMap::EntryOption option, bool bf)
+{
+ if (it != end()) {
+ switch (option) {
+ case EntryDirty:
+ it->bDirty = bf;
+ break;
+ case EntryGlobal:
+ it->bGlobal = bf;
+ break;
+ case EntryImmutable:
+ it->bImmutable = bf;
+ break;
+ case EntryDeleted:
+ it->bDeleted = bf;
+ break;
+ case EntryExpansion:
+ it->bExpand = bf;
+ break;
+ default:
+ break; // fall through
+ }
+ }
+}
+
+bool KEntryMap::revertEntry(const QByteArray &group, const QByteArray &key, KEntryMap::SearchFlags flags)
+{
+ Q_ASSERT((flags & KEntryMap::SearchDefaults) == 0);
+ Iterator entry = findEntry(group, key, flags);
+ if (entry != end()) {
+ //qDebug() << "reverting" << entry.key() << " = " << entry->mValue;
+ if (entry->bReverted) { // already done before
+ return false;
+ }
+
+ KEntryKey defaultKey(entry.key());
+ defaultKey.bDefault = true;
+ //qDebug() << "looking up default entry with key=" << defaultKey;
+ const ConstIterator defaultEntry = constFind(defaultKey);
+ if (defaultEntry != constEnd()) {
+ Q_ASSERT(defaultEntry.key().bDefault);
+ //qDebug() << "found, update entry";
+ *entry = *defaultEntry; // copy default value, for subsequent lookups
+ } else {
+ entry->mValue = QByteArray();
+ }
+ entry->bDirty = true;
+ entry->bReverted = true; // skip it when writing out to disk
+
+ //qDebug() << "Here's what we have now:" << *this;
+ return true;
+ }
+ return false;
+}
diff --git a/libs/resources/kconfigdata.h b/libs/resources/kconfigdata.h
new file mode 100644
index 0000000000..f84e51e37a
--- /dev/null
+++ b/libs/resources/kconfigdata.h
@@ -0,0 +1,244 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
+ Copyright (c) 1999-2000 Preston Brown <pbrown@kde.org>
+ Copyright (C) 1996-2000 Matthias Kalle Dalheimer <kalle@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 KCONFIGDATA_H
+#define KCONFIGDATA_H
+
+#include <QByteArray>
+#include <QString>
+#include <QMap>
+#include <QDebug>
+
+/**
+ * map/dict/list config node entry.
+ * @internal
+ */
+struct KEntry {
+ /** Constructor. @internal */
+ KEntry()
+ : mValue(), bDirty(false),
+ bGlobal(false), bImmutable(false), bDeleted(false), bExpand(false), bReverted(false),
+ bLocalizedCountry(false) {}
+ /** @internal */
+ QByteArray mValue;
+ /**
+ * Must the entry be written back to disk?
+ */
+ bool bDirty : 1;
+ /**
+ * Entry should be written to the global config file
+ */
+ bool bGlobal: 1;
+ /**
+ * Entry can not be modified.
+ */
+ bool bImmutable: 1;
+ /**
+ * Entry has been deleted.
+ */
+ bool bDeleted: 1;
+ /**
+ * Whether to apply dollar expansion or not.
+ */
+ bool bExpand: 1;
+ /**
+ * Entry has been reverted to its default value (from a more global file).
+ */
+ bool bReverted: 1;
+ /**
+ * Entry is for a localized key. If @c false the value references just language e.g. "de",
+ * if @c true the value references language and country, e.g. "de_DE".
+ **/
+ bool bLocalizedCountry: 1;
+};
+
+// These operators are used to check whether an entry which is about
+// to be written equals the previous value. As such, this intentionally
+// omits the dirty flag from the comparison.
+inline bool operator ==(const KEntry &k1, const KEntry &k2)
+{
+ return k1.bGlobal == k2.bGlobal && k1.bImmutable == k2.bImmutable
+ && k1.bDeleted == k2.bDeleted && k1.bExpand == k2.bExpand
+ && k1.mValue == k2.mValue;
+}
+
+inline bool operator !=(const KEntry &k1, const KEntry &k2)
+{
+ return !(k1 == k2);
+}
+
+/**
+ * key structure holding both the actual key and the group
+ * to which it belongs.
+ * @internal
+ */
+struct KEntryKey {
+ /** Constructor. @internal */
+ KEntryKey(const QByteArray &_group = QByteArray(),
+ const QByteArray &_key = QByteArray(), bool isLocalized = false, bool isDefault = false)
+ : mGroup(_group), mKey(_key), bLocal(isLocalized), bDefault(isDefault), bRaw(false)
+ {
+ ;
+ }
+ /**
+ * The "group" to which this EntryKey belongs
+ */
+ QByteArray mGroup;
+ /**
+ * The _actual_ key of the entry in question
+ */
+ QByteArray mKey;
+ /**
+ * Entry is localised or not
+ */
+ bool bLocal : 1;
+ /**
+ * Entry indicates if this is a default value.
+ */
+ bool bDefault: 1;
+ /** @internal
+ * Key is a raw unprocessed key.
+ * @warning this should only be set during merging, never for normal use.
+ */
+ bool bRaw: 1;
+};
+
+/**
+ * Compares two KEntryKeys (needed for QMap). The order is localized, localized-default,
+ * non-localized, non-localized-default
+ * @internal
+ */
+inline bool operator <(const KEntryKey &k1, const KEntryKey &k2)
+{
+ int result = qstrcmp(k1.mGroup, k2.mGroup);
+ if (result != 0) {
+ return result < 0;
+ }
+
+ result = qstrcmp(k1.mKey, k2.mKey);
+ if (result != 0) {
+ return result < 0;
+ }
+
+ if (k1.bLocal != k2.bLocal) {
+ return k1.bLocal;
+ }
+ return (!k1.bDefault && k2.bDefault);
+}
+
+QDebug operator<<(QDebug dbg, const KEntryKey &key);
+QDebug operator<<(QDebug dbg, const KEntry &entry);
+
+/**
+ * \relates KEntry
+ * type specifying a map of entries (key,value pairs).
+ * The keys are actually a key in a particular config file group together
+ * with the group name.
+ * @internal
+ */
+class KEntryMap : public QMap<KEntryKey, KEntry>
+{
+public:
+ enum SearchFlag {
+ SearchDefaults = 1,
+ SearchLocalized = 2
+ };
+ Q_DECLARE_FLAGS(SearchFlags, SearchFlag)
+
+ enum EntryOption {
+ EntryDirty = 1,
+ EntryGlobal = 2,
+ EntryImmutable = 4,
+ EntryDeleted = 8,
+ EntryExpansion = 16,
+ EntryRawKey = 32,
+ EntryLocalizedCountry = 64,
+ EntryDefault = (SearchDefaults << 16),
+ EntryLocalized = (SearchLocalized << 16)
+ };
+ Q_DECLARE_FLAGS(EntryOptions, EntryOption)
+
+ Iterator findExactEntry(const QByteArray &group, const QByteArray &key = QByteArray(),
+ SearchFlags flags = SearchFlags());
+
+ Iterator findEntry(const QByteArray &group, const QByteArray &key = QByteArray(),
+ SearchFlags flags = SearchFlags());
+
+ ConstIterator findEntry(const QByteArray &group, const QByteArray &key = QByteArray(),
+ SearchFlags flags = SearchFlags()) const;
+
+ /**
+ * Returns true if the entry gets dirtied or false in other case
+ */
+ bool setEntry(const QByteArray &group, const QByteArray &key,
+ const QByteArray &value, EntryOptions options);
+
+ void setEntry(const QByteArray &group, const QByteArray &key,
+ const QString &value, EntryOptions options)
+ {
+ setEntry(group, key, value.toUtf8(), options);
+ }
+
+ QString getEntry(const QByteArray &group, const QByteArray &key,
+ const QString &defaultValue = QString(),
+ SearchFlags flags = SearchFlags(),
+ bool *expand = nullptr) const;
+
+ bool hasEntry(const QByteArray &group, const QByteArray &key = QByteArray(),
+ SearchFlags flags = SearchFlags()) const;
+
+ bool getEntryOption(const ConstIterator &it, EntryOption option) const;
+ bool getEntryOption(const QByteArray &group, const QByteArray &key,
+ SearchFlags flags, EntryOption option) const
+ {
+ return getEntryOption(findEntry(group, key, flags), option);
+ }
+
+ void setEntryOption(Iterator it, EntryOption option, bool bf);
+ void setEntryOption(const QByteArray &group, const QByteArray &key, SearchFlags flags,
+ EntryOption option, bool bf)
+ {
+ setEntryOption(findEntry(group, key, flags), option, bf);
+ }
+
+ bool revertEntry(const QByteArray &group, const QByteArray &key, SearchFlags flags = SearchFlags());
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(KEntryMap::SearchFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(KEntryMap::EntryOptions)
+
+/**
+ * \relates KEntry
+ * type for iterating over keys in a KEntryMap in sorted order.
+ * @internal
+ */
+typedef QMap<KEntryKey, KEntry>::Iterator KEntryMapIterator;
+
+/**
+ * \relates KEntry
+ * type for iterating over keys in a KEntryMap in sorted order.
+ * It is const, thus you cannot change the entries in the iterator,
+ * only examine them.
+ * @internal
+ */
+typedef QMap<KEntryKey, KEntry>::ConstIterator KEntryMapConstIterator;
+
+#endif
diff --git a/libs/resources/kconfigini.cpp b/libs/resources/kconfigini.cpp
new file mode 100644
index 0000000000..4caacd7ef4
--- /dev/null
+++ b/libs/resources/kconfigini.cpp
@@ -0,0 +1,823 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
+ Copyright (c) 1999 Preston Brown <pbrown@kde.org>
+ Copyright (C) 1997-1999 Matthias Kalle Dalheimer (kalle@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 "kconfigini_p.h"
+
+#include "kconfig.h"
+#include "kconfigbackend_p.h"
+#include "bufferfragment_p.h"
+#include "kconfigdata.h"
+
+#include <qsavefile.h>
+#include <qlockfile.h>
+#include <qdatetime.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qdebug.h>
+#include <qplatformdefs.h>
+
+#ifndef Q_OS_WIN
+#include <unistd.h> // getuid, close
+#endif
+#include <sys/types.h> // uid_t
+#include <fcntl.h> // open
+
+bool kde_kiosk_exception = false; // flag to disable kiosk restrictions
+
+static QByteArray lookup(const KConfigIniBackend::BufferFragment &fragment, QHash<KConfigIniBackend::BufferFragment, QByteArray> *cache)
+{
+ auto it = cache->constFind(fragment);
+ if (it != cache->constEnd()) {
+ return it.value();
+ }
+
+ return cache->insert(fragment, fragment.toByteArray()).value();
+}
+
+QString KConfigIniBackend::warningProlog(const QIODevice &file, int line)
+{
+ const QFile *f = dynamic_cast<const QFile*>(&file);
+ if (f) {
+ return QStringLiteral("KConfigIni: In file %2, line %1: ").arg(line).arg(f->fileName());
+ }
+ else {
+ return QStringLiteral("KConfigIni: line %1: ").arg(line);
+ }
+}
+
+KConfigIniBackend::KConfigIniBackend()
+ : KConfigBackend(), lockFile(nullptr)
+{
+}
+
+KConfigIniBackend::~KConfigIniBackend()
+{
+}
+
+KConfigBackend::ParseInfo KConfigIniBackend::parseConfig(const QByteArray &currentLocale, KEntryMap &entryMap,
+ ParseOptions options)
+{
+ return parseConfig(currentLocale, entryMap, options, false);
+}
+
+// merging==true is the merging that happens at the beginning of writeConfig:
+// merge changes in the on-disk file with the changes in the KConfig object.
+KConfigBackend::ParseInfo KConfigIniBackend::parseConfig(const QByteArray &currentLocale, KEntryMap &entryMap, ParseOptions options, bool merging)
+{
+ if (filePath().isEmpty() || !QFile::exists(filePath())) {
+ return ParseOk;
+ }
+
+ QFile file(filePath());
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ return ParseOpenError;
+ }
+
+ return parseConfigIO(file, currentLocale, entryMap, options, merging);
+}
+
+
+// merging==true is the merging that happens at the beginning of writeConfig:
+// merge changes in the on-disk file with the changes in the KConfig object.
+KConfigBackend::ParseInfo KConfigIniBackend::parseConfigIO(QIODevice &file, const QByteArray &currentLocale, KEntryMap &entryMap, ParseOptions options, bool merging)
+{
+ const QByteArray currentLanguage = currentLocale.split('_').first();
+
+ bool bDefault = options & ParseDefaults;
+ bool allowExecutableValues = options & ParseExpansions;
+
+ QByteArray currentGroup("<default>");
+
+ QList<QByteArray> immutableGroups;
+
+ bool fileOptionImmutable = false;
+ bool groupOptionImmutable = false;
+ bool groupSkip = false;
+
+ int lineNo = 0;
+ // on systems using \r\n as end of line, \r will be taken care of by
+ // trim() below
+ QByteArray buffer = file.readAll();
+ BufferFragment contents(buffer.data(), buffer.size());
+ unsigned int len = contents.length();
+ unsigned int startOfLine = 0;
+
+ // Reduce memory overhead by making use of implicit sharing
+ // This assumes that config files contain only a small amount of
+ // different fragments which are repeated often.
+ // This is often the case, especially sub groups will all have
+ // the same list of keys and similar values as well.
+ QHash<BufferFragment, QByteArray> cache;
+ cache.reserve(4096);
+
+ while (startOfLine < len) {
+ BufferFragment line = contents.split('\n', &startOfLine);
+ line.trim();
+ lineNo++;
+
+ // skip empty lines and lines beginning with '#'
+ if (line.isEmpty() || line.at(0) == '#') {
+ continue;
+ }
+
+ if (line.at(0) == '[') { // found a group
+ groupOptionImmutable = fileOptionImmutable;
+
+ QByteArray newGroup;
+ int start = 1, end;
+ do {
+ end = start;
+ for (;;) {
+ if (end == line.length()) {
+ qWarning() << warningProlog(file, lineNo) << "Invalid group header.";
+ // XXX maybe reset the current group here?
+ goto next_line;
+ }
+ if (line.at(end) == ']') {
+ break;
+ }
+ end++;
+ }
+ if (end + 1 == line.length() && start + 2 == end &&
+ line.at(start) == '$' && line.at(start + 1) == 'i') {
+ if (newGroup.isEmpty()) {
+ fileOptionImmutable = !kde_kiosk_exception;
+ } else {
+ groupOptionImmutable = !kde_kiosk_exception;
+ }
+ } else {
+ if (!newGroup.isEmpty()) {
+ newGroup += '\x1d';
+ }
+ BufferFragment namePart = line.mid(start, end - start);
+ printableToString(&namePart, file, lineNo);
+ newGroup += namePart.toByteArray();
+ }
+ } while ((start = end + 2) <= line.length() && line.at(end + 1) == '[');
+ currentGroup = newGroup;
+
+ groupSkip = entryMap.getEntryOption(currentGroup, nullptr, nullptr, KEntryMap::EntryImmutable);
+
+ if (groupSkip && !bDefault) {
+ continue;
+ }
+
+ if (groupOptionImmutable)
+ // Do not make the groups immutable until the entries from
+ // this file have been added.
+ {
+ immutableGroups.append(currentGroup);
+ }
+ } else {
+ if (groupSkip && !bDefault) {
+ continue; // skip entry
+ }
+
+ BufferFragment aKey;
+ int eqpos = line.indexOf('=');
+ if (eqpos < 0) {
+ aKey = line;
+ line.clear();
+ } else {
+ BufferFragment temp = line.left(eqpos);
+ temp.trim();
+ aKey = temp;
+ line.truncateLeft(eqpos + 1);
+ line.trim();
+ }
+ if (aKey.isEmpty()) {
+ qWarning() << warningProlog(file, lineNo) << "Invalid entry (empty key)";
+ continue;
+ }
+
+ KEntryMap::EntryOptions entryOptions = nullptr;
+ if (groupOptionImmutable) {
+ entryOptions |= KEntryMap::EntryImmutable;
+ }
+
+ BufferFragment locale;
+ int start;
+ while ((start = aKey.lastIndexOf('[')) >= 0) {
+ int end = aKey.indexOf(']', start);
+ if (end < 0) {
+ qWarning() << warningProlog(file, lineNo)
+ << "Invalid entry (missing ']')";
+ goto next_line;
+ } else if (end > start + 1 && aKey.at(start + 1) == '$') { // found option(s)
+ int i = start + 2;
+ while (i < end) {
+ switch (aKey.at(i)) {
+ case 'i':
+ if (!kde_kiosk_exception) {
+ entryOptions |= KEntryMap::EntryImmutable;
+ }
+ break;
+ case 'e':
+ if (allowExecutableValues) {
+ entryOptions |= KEntryMap::EntryExpansion;
+ }
+ break;
+ case 'd':
+ entryOptions |= KEntryMap::EntryDeleted;
+ aKey = aKey.left(start);
+ printableToString(&aKey, file, lineNo);
+ entryMap.setEntry(currentGroup, aKey.toByteArray(), QByteArray(), entryOptions);
+ goto next_line;
+ default:
+ break;
+ }
+ i++;
+ }
+ } else { // found a locale
+ if (!locale.isNull()) {
+ qWarning() << warningProlog(file, lineNo)
+ << "Invalid entry (second locale!?)";
+ goto next_line;
+ }
+
+ locale = aKey.mid(start + 1, end - start - 1);
+ }
+ aKey.truncate(start);
+ }
+ if (eqpos < 0) { // Do this here after [$d] was checked
+ qWarning() << warningProlog(file, lineNo) << "Invalid entry (missing '=')";
+ continue;
+ }
+ printableToString(&aKey, file, lineNo);
+ if (!locale.isEmpty()) {
+ if (locale != currentLocale && locale != currentLanguage) {
+ // backward compatibility. C == en_US
+ if (locale.at(0) != 'C' || currentLocale != "en_US") {
+ if (merging) {
+ entryOptions |= KEntryMap::EntryRawKey;
+ } else {
+ goto next_line; // skip this entry if we're not merging
+ }
+ }
+ }
+ }
+
+ if (!(entryOptions & KEntryMap::EntryRawKey)) {
+ printableToString(&aKey, file, lineNo);
+ }
+
+ if (options & ParseGlobal) {
+ entryOptions |= KEntryMap::EntryGlobal;
+ }
+ if (bDefault) {
+ entryOptions |= KEntryMap::EntryDefault;
+ }
+ if (!locale.isNull()) {
+ entryOptions |= KEntryMap::EntryLocalized;
+ if (locale.indexOf('_') != -1) {
+ entryOptions |= KEntryMap::EntryLocalizedCountry;
+ }
+ }
+ printableToString(&line, file, lineNo);
+ if (entryOptions & KEntryMap::EntryRawKey) {
+ QByteArray rawKey;
+ rawKey.reserve(aKey.length() + locale.length() + 2);
+ rawKey.append(aKey.toVolatileByteArray());
+ rawKey.append('[').append(locale.toVolatileByteArray()).append(']');
+ entryMap.setEntry(currentGroup, rawKey, lookup(line, &cache), entryOptions);
+ } else {
+ entryMap.setEntry(currentGroup, lookup(aKey, &cache), lookup(line, &cache), entryOptions);
+ }
+ }
+ next_line:
+ continue;
+ }
+
+ // now make sure immutable groups are marked immutable
+ Q_FOREACH (const QByteArray &group, immutableGroups) {
+ entryMap.setEntry(group, QByteArray(), QByteArray(), KEntryMap::EntryImmutable);
+ }
+
+ return fileOptionImmutable ? ParseImmutable : ParseOk;
+}
+
+void KConfigIniBackend::writeEntries(const QByteArray &locale, QIODevice &file,
+ const KEntryMap &map, bool defaultGroup, bool &firstEntry)
+{
+ QByteArray currentGroup;
+ bool groupIsImmutable = false;
+ const KEntryMapConstIterator end = map.constEnd();
+ for (KEntryMapConstIterator it = map.constBegin(); it != end; ++it) {
+ const KEntryKey &key = it.key();
+
+ // Either process the default group or all others
+ if ((key.mGroup != "<default>") == defaultGroup) {
+ continue; // skip
+ }
+
+ // the only thing we care about groups is, is it immutable?
+ if (key.mKey.isNull()) {
+ groupIsImmutable = it->bImmutable;
+ continue; // skip
+ }
+
+ const KEntry &currentEntry = *it;
+ if (!defaultGroup && currentGroup != key.mGroup) {
+ if (!firstEntry) {
+ file.putChar('\n');
+ }
+ currentGroup = key.mGroup;
+ for (int start = 0, end;; start = end + 1) {
+ file.putChar('[');
+ end = currentGroup.indexOf('\x1d', start);
+ if (end < 0) {
+ int cgl = currentGroup.length();
+ if (currentGroup.at(start) == '$' && cgl - start <= 10) {
+ for (int i = start + 1; i < cgl; i++) {
+ char c = currentGroup.at(i);
+ if (c < 'a' || c > 'z') {
+ goto nope;
+ }
+ }
+ file.write("\\x24");
+ start++;
+ }
+ nope:
+ file.write(stringToPrintable(currentGroup.mid(start), GroupString));
+ file.putChar(']');
+ if (groupIsImmutable) {
+ file.write("[$i]", 4);
+ }
+ file.putChar('\n');
+ break;
+ } else {
+ file.write(stringToPrintable(currentGroup.mid(start, end - start), GroupString));
+ file.putChar(']');
+ }
+ }
+ }
+
+ firstEntry = false;
+ // it is data for a group
+
+ if (key.bRaw) { // unprocessed key with attached locale from merge
+ file.write(key.mKey);
+ } else {
+ file.write(stringToPrintable(key.mKey, KeyString)); // Key
+ if (key.bLocal && locale != "C") { // 'C' locale == untranslated
+ file.putChar('[');
+ file.write(locale); // locale tag
+ file.putChar(']');
+ }
+ }
+ if (currentEntry.bDeleted) {
+ if (currentEntry.bImmutable) {
+ file.write("[$di]", 5); // Deleted + immutable
+ } else {
+ file.write("[$d]", 4); // Deleted
+ }
+ } else {
+ if (currentEntry.bImmutable || currentEntry.bExpand) {
+ file.write("[$", 2);
+ if (currentEntry.bImmutable) {
+ file.putChar('i');
+ }
+ if (currentEntry.bExpand) {
+ file.putChar('e');
+ }
+ file.putChar(']');
+ }
+ file.putChar('=');
+ file.write(stringToPrintable(currentEntry.mValue, ValueString));
+ }
+ file.putChar('\n');
+ }
+}
+
+void KConfigIniBackend::writeEntries(const QByteArray &locale, QIODevice &file, const KEntryMap &map)
+{
+ bool firstEntry = true;
+
+ // write default group
+ writeEntries(locale, file, map, true, firstEntry);
+
+ // write all other groups
+ writeEntries(locale, file, map, false, firstEntry);
+}
+
+bool KConfigIniBackend::writeConfig(const QByteArray &locale, KEntryMap &entryMap,
+ WriteOptions options)
+{
+ Q_ASSERT(!filePath().isEmpty());
+
+ KEntryMap writeMap;
+ const bool bGlobal = options & WriteGlobal;
+
+ // First, reparse the file on disk, to merge our changes with the ones done by other apps
+ // Store the result into writeMap.
+ {
+ ParseOptions opts = ParseExpansions;
+ if (bGlobal) {
+ opts |= ParseGlobal;
+ }
+ ParseInfo info = parseConfig(locale, writeMap, opts, true);
+ if (info != ParseOk) { // either there was an error or the file became immutable
+ return false;
+ }
+ }
+
+ const KEntryMapIterator end = entryMap.end();
+ for (KEntryMapIterator it = entryMap.begin(); it != end; ++it) {
+ if (!it.key().mKey.isEmpty() && !it->bDirty) { // not dirty, doesn't overwrite entry in writeMap. skips default entries, too.
+ continue;
+ }
+
+ const KEntryKey &key = it.key();
+
+ // only write entries that have the same "globality" as the file
+ if (it->bGlobal == bGlobal) {
+ if (it->bReverted) {
+ writeMap.remove(key);
+ } else if (!it->bDeleted) {
+ writeMap[key] = *it;
+ } else {
+ KEntryKey defaultKey = key;
+ defaultKey.bDefault = true;
+ if (!entryMap.contains(defaultKey)) {
+ writeMap.remove(key); // remove the deleted entry if there is no default
+ //qDebug() << "Detected as deleted=>removed:" << key.mGroup << key.mKey << "global=" << bGlobal;
+ } else {
+ writeMap[key] = *it; // otherwise write an explicitly deleted entry
+ //qDebug() << "Detected as deleted=>[$d]:" << key.mGroup << key.mKey << "global=" << bGlobal;
+ }
+ }
+ it->bDirty = false;
+ }
+ }
+
+ // now writeMap should contain only entries to be written
+ // so write it out to disk
+
+ // check if file exists
+ QFile::Permissions fileMode = QFile::ReadUser | QFile::WriteUser;
+ bool createNew = true;
+
+ QFileInfo fi(filePath());
+ if (fi.exists()) {
+#ifdef Q_OS_WIN
+ //TODO: getuid does not exist on windows, use GetSecurityInfo and GetTokenInformation instead
+ createNew = false;
+#else
+ if (fi.ownerId() == ::getuid()) {
+ // Preserve file mode if file exists and is owned by user.
+ fileMode = fi.permissions();
+ } else {
+ // File is not owned by user:
+ // Don't create new file but write to existing file instead.
+ createNew = false;
+ }
+#endif
+ }
+
+ if (createNew) {
+ QSaveFile file(filePath());
+ if (!file.open(QIODevice::WriteOnly)) {
+ return false;
+ }
+
+ file.setTextModeEnabled(true); // to get eol translation
+ writeEntries(locale, file, writeMap);
+
+ if (!file.size() && (fileMode == (QFile::ReadUser | QFile::WriteUser))) {
+ // File is empty and doesn't have special permissions: delete it.
+ file.cancelWriting();
+
+ if (fi.exists()) {
+ // also remove the old file in case it existed. this can happen
+ // when we delete all the entries in an existing config file.
+ // if we don't do this, then deletions and revertToDefault's
+ // will mysteriously fail
+ QFile::remove(filePath());
+ }
+ } else {
+ // Normal case: Close the file
+ if (file.commit()) {
+ QFile::setPermissions(filePath(), fileMode);
+ return true;
+ }
+ // Couldn't write. Disk full?
+ qWarning() << "Couldn't write" << filePath() << ". Disk full?";
+ return false;
+ }
+ } else {
+ // Open existing file. *DON'T* create it if it suddenly does not exist!
+#ifdef Q_OS_UNIX
+ int fd = QT_OPEN(QFile::encodeName(filePath()).constData(), O_WRONLY | O_TRUNC);
+ if (fd < 0) {
+ return false;
+ }
+ FILE *fp = ::fdopen(fd, "w");
+ if (!fp) {
+ QT_CLOSE(fd);
+ return false;
+ }
+ QFile f;
+ if (!f.open(fp, QIODevice::WriteOnly)) {
+ fclose(fp);
+ return false;
+ }
+ writeEntries(locale, f, writeMap);
+ f.close();
+ fclose(fp);
+#else
+ QFile f(filePath());
+ // XXX This is broken - it DOES create the file if it is suddenly gone.
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ return false;
+ }
+ f.setTextModeEnabled(true);
+ writeEntries(locale, f, writeMap);
+#endif
+ }
+ return true;
+}
+
+bool KConfigIniBackend::isWritable() const
+{
+ const QString filePath = this->filePath();
+ if (!filePath.isEmpty()) {
+ QFileInfo file(filePath);
+ if (!file.exists()) {
+ // If the file does not exist, check if the deepest
+ // existing dir is writable.
+ QFileInfo dir(file.absolutePath());
+ while (!dir.exists()) {
+ QString parent = dir.absolutePath(); // Go up. Can't use cdUp() on non-existing dirs.
+ if (parent == dir.filePath()) {
+ // no parent
+ return false;
+ }
+ dir.setFile(parent);
+ }
+ return dir.isDir() && dir.isWritable();
+ } else {
+ return file.isWritable();
+ }
+ }
+
+ return false;
+}
+
+QString KConfigIniBackend::nonWritableErrorMessage() const
+{
+ return tr("Configuration file \"%1\" not writable.\n").arg(filePath());
+}
+
+void KConfigIniBackend::createEnclosing()
+{
+ const QString file = filePath();
+ if (file.isEmpty()) {
+ return; // nothing to do
+ }
+
+ // Create the containing dir, maybe it wasn't there
+ QDir dir;
+ dir.mkpath(QFileInfo(file).absolutePath());
+}
+
+void KConfigIniBackend::setFilePath(const QString &file)
+{
+ if (file.isEmpty()) {
+ return;
+ }
+
+ Q_ASSERT(QDir::isAbsolutePath(file));
+
+ const QFileInfo info(file);
+ if (info.exists()) {
+ setLocalFilePath(info.canonicalFilePath());
+ } else {
+ setLocalFilePath(file);
+ }
+}
+
+KConfigBase::AccessMode KConfigIniBackend::accessMode() const
+{
+ if (filePath().isEmpty()) {
+ return KConfigBase::NoAccess;
+ }
+
+ if (isWritable()) {
+ return KConfigBase::ReadWrite;
+ }
+
+ return KConfigBase::ReadOnly;
+}
+
+bool KConfigIniBackend::lock()
+{
+ Q_ASSERT(!filePath().isEmpty());
+
+ if (!lockFile) {
+ lockFile = new QLockFile(filePath() + QLatin1String(".lock"));
+ }
+
+ lockFile->lock();
+ return lockFile->isLocked();
+}
+
+void KConfigIniBackend::unlock()
+{
+ lockFile->unlock();
+ delete lockFile;
+ lockFile = nullptr;
+}
+
+bool KConfigIniBackend::isLocked() const
+{
+ return lockFile && lockFile->isLocked();
+}
+
+QByteArray KConfigIniBackend::stringToPrintable(const QByteArray &aString, StringType type)
+{
+ static const char nibbleLookup[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+ };
+
+ if (aString.isEmpty()) {
+ return aString;
+ }
+ const int l = aString.length();
+
+ QByteArray result; // Guesstimated that it's good to avoid data() initialization for a length of l*4
+ result.resize(l * 4); // Maximum 4x as long as source string due to \x<ab> escape sequences
+ const char *s = aString.constData();
+ int i = 0;
+ char *data = result.data();
+ char *start = data;
+
+ // Protect leading space
+ if (s[0] == ' ' && type != GroupString) {
+ *data++ = '\\';
+ *data++ = 's';
+ i++;
+ }
+
+ for (; i < l; ++i/*, r++*/) {
+ switch (s[i]) {
+ default:
+ // The \n, \t, \r cases (all < 32) are handled below; we can ignore them here
+ if (((unsigned char)s[i]) < 32) {
+ goto doEscape;
+ }
+ *data++ = s[i];
+ break;
+ case '\n':
+ *data++ = '\\';
+ *data++ = 'n';
+ break;
+ case '\t':
+ *data++ = '\\';
+ *data++ = 't';
+ break;
+ case '\r':
+ *data++ = '\\';
+ *data++ = 'r';
+ break;
+ case '\\':
+ *data++ = '\\';
+ *data++ = '\\';
+ break;
+ case '=':
+ if (type != KeyString) {
+ *data++ = s[i];
+ break;
+ }
+ goto doEscape;
+ case '[':
+ case ']':
+ // Above chars are OK to put in *value* strings as plaintext
+ if (type == ValueString) {
+ *data++ = s[i];
+ break;
+ }
+ doEscape:
+ *data++ = '\\';
+ *data++ = 'x';
+ *data++ = nibbleLookup[((unsigned char)s[i]) >> 4];
+ *data++ = nibbleLookup[((unsigned char)s[i]) & 0x0f];
+ break;
+ }
+ }
+ *data = 0;
+ result.resize(data - start);
+
+ // Protect trailing space
+ if (result.endsWith(' ') && type != GroupString) {
+ result.replace(result.length() - 1, 1, "\\s");
+ }
+
+ return result;
+}
+
+char KConfigIniBackend::charFromHex(const char *str, const QIODevice &file, int line)
+{
+ unsigned char ret = 0;
+ for (int i = 0; i < 2; i++) {
+ ret <<= 4;
+ quint8 c = quint8(str[i]);
+
+ if (c >= '0' && c <= '9') {
+ ret |= c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ ret |= c - 'a' + 0x0a;
+ } else if (c >= 'A' && c <= 'F') {
+ ret |= c - 'A' + 0x0a;
+ } else {
+ QByteArray e(str, 2);
+ e.prepend("\\x");
+ qWarning() << warningProlog(file, line) << "Invalid hex character " << c
+ << " in \\x<nn>-type escape sequence \"" << e.constData() << "\".";
+ return 'x';
+ }
+ }
+ return char(ret);
+}
+
+void KConfigIniBackend::printableToString(BufferFragment *aString, const QIODevice &file, int line)
+{
+ if (aString->isEmpty() || aString->indexOf('\\') == -1) {
+ return;
+ }
+ aString->trim();
+ int l = aString->length();
+ char *r = aString->data();
+ char *str = r;
+
+ for (int i = 0; i < l; i++, r++) {
+ if (str[i] != '\\') {
+ *r = str[i];
+ } else {
+ // Probable escape sequence
+ i++;
+ if (i >= l) { // Line ends after backslash - stop.
+ *r = '\\';
+ break;
+ }
+
+ switch (str[i]) {
+ case 's':
+ *r = ' ';
+ break;
+ case 't':
+ *r = '\t';
+ break;
+ case 'n':
+ *r = '\n';
+ break;
+ case 'r':
+ *r = '\r';
+ break;
+ case '\\':
+ *r = '\\';
+ break;
+ case ';':
+ // not really an escape sequence, but allowed in .desktop files, don't strip '\;' from the string
+ *r = '\\';
+ r++;
+ *r = ';';
+ break;
+ case 'x':
+ if (i + 2 < l) {
+ *r = charFromHex(str + i + 1, file, line);
+ i += 2;
+ } else {
+ *r = 'x';
+ i = l - 1;
+ }
+ break;
+ default:
+ *r = '\\';
+ qWarning() << warningProlog(file, line)
+ << QStringLiteral("Invalid escape sequence \"\\%1\".").arg(str[i]);
+ }
+ }
+ }
+ aString->truncate(r - aString->constData());
+}
diff --git a/libs/resources/kconfigini_p.h b/libs/resources/kconfigini_p.h
new file mode 100644
index 0000000000..313c3c8bd5
--- /dev/null
+++ b/libs/resources/kconfigini_p.h
@@ -0,0 +1,89 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
+ Copyright (c) 1999 Preston Brown <pbrown@kde.org>
+ Portions copyright (c) 1997 Matthias Kalle Dalheimer <kalle@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 KCONFIGINI_P_H
+#define KCONFIGINI_P_H
+
+#include <kconfigbackend_p.h>
+
+class QLockFile;
+class QIODevice;
+
+class KConfigIniBackend : public KConfigBackend
+{
+ Q_OBJECT
+private:
+ QLockFile *lockFile;
+
+public:
+ class BufferFragment;
+
+ KConfigIniBackend();
+ ~KConfigIniBackend() override;
+
+ ParseInfo parseConfig(const QByteArray &locale,
+ KEntryMap &entryMap,
+ ParseOptions options) override;
+
+ ParseInfo parseConfig(const QByteArray &locale,
+ KEntryMap &entryMap,
+ ParseOptions options,
+ bool merging);
+ ParseInfo parseConfigIO(QIODevice &file,
+ const QByteArray &locale,
+ KEntryMap &entryMap,
+ ParseOptions options,
+ bool merging);
+ bool writeConfig(const QByteArray &locale, KEntryMap &entryMap,
+ WriteOptions options) override;
+
+ bool isWritable() const override;
+ QString nonWritableErrorMessage() const override;
+ KConfigBase::AccessMode accessMode() const override;
+ void createEnclosing() override;
+ void setFilePath(const QString &path) override;
+ bool lock() override;
+ void unlock() override;
+ bool isLocked() const override;
+
+ void writeEntries(const QByteArray &locale, QIODevice &file, const KEntryMap &map);
+ void writeEntries(const QByteArray &locale, QIODevice &file, const KEntryMap &map,
+ bool defaultGroup, bool &firstEntry);
+
+
+protected:
+
+ enum StringType {
+ GroupString = 0,
+ KeyString = 1,
+ ValueString = 2
+ };
+ // Warning: this modifies data in-place. Other BufferFragment objects referencing the same buffer
+ // fragment will get their data modified too.
+ static void printableToString(BufferFragment *aString, const QIODevice &file, int line);
+ static QByteArray stringToPrintable(const QByteArray &aString, StringType type);
+ static char charFromHex(const char *str, const QIODevice &file, int line);
+ static QString warningProlog(const QIODevice &file, int line);
+
+};
+
+#endif // KCONFIGINI_P_H
diff --git a/libs/resources/sql.qrc b/libs/resources/sql.qrc
new file mode 100644
index 0000000000..e14c35cac1
--- /dev/null
+++ b/libs/resources/sql.qrc
@@ -0,0 +1,23 @@
+<RCC>
+ <qresource prefix="/">
+ <file alias="create_version_information.sql">sql/create_version_information.sql</file>
+ <file alias="fill_version_information.sql">sql/fill_version_information.sql</file>
+ <file alias="create_storage_types.sql">sql/create_storage_types.sql</file>
+ <file alias="fill_storage_types.sql">sql/fill_storage_types.sql</file>
+ <file alias="create_storages.sql">sql/create_storages.sql</file>
+ <file alias="create_tags.sql">sql/create_tags.sql</file>
+ <file alias="create_resource_types.sql">sql/create_resource_types.sql</file>
+ <file alias="fill_resource_types.sql">sql/fill_resource_types.sql</file>
+ <file alias="create_resources.sql">sql/create_resources.sql</file>
+ <file alias="create_versioned_resources.sql">sql/create_versioned_resources.sql</file>
+ <file alias="create_resource_tags.sql">sql/create_resource_tags.sql</file>
+ <file alias="get_version_information.sql">sql/get_version_information.sql</file>
+ <file alias="create_index_storages.sql">sql/create_index_storages.sql</file>
+ <file alias="create_index_versioned_resources.sql">sql/create_index_versioned_resources.sql</file>
+ <file alias="select_resource_id.sql">sql/select_resource_id.sql</file>
+ <file alias="select_tag.sql">sql/select_tag.sql</file>
+ <file alias="update_from_001.sql">sql/update_from_001.sql</file>
+ <file alias="create_metadata.sql">sql/create_metadata.sql</file>
+ <file alias="create_tags_storages.sql">sql/create_tags_storages.sql</file>
+ </qresource>
+</RCC>
diff --git a/libs/resources/sql/create_index_storages.sql b/libs/resources/sql/create_index_storages.sql
new file mode 100644
index 0000000000..a8c4fb3003
--- /dev/null
+++ b/libs/resources/sql/create_index_storages.sql
@@ -0,0 +1 @@
+CREATE UNIQUE INDEX storages_location_index ON storages(location);
diff --git a/libs/resources/sql/create_index_versioned_resources.sql b/libs/resources/sql/create_index_versioned_resources.sql
new file mode 100644
index 0000000000..5ac528d613
--- /dev/null
+++ b/libs/resources/sql/create_index_versioned_resources.sql
@@ -0,0 +1 @@
+CREATE UNIQUE INDEX versioned_resources_index on versioned_resources(resource_id, storage_id, version);
diff --git a/libs/resources/sql/create_metadata.sql b/libs/resources/sql/create_metadata.sql
new file mode 100644
index 0000000000..573e8bfc9a
--- /dev/null
+++ b/libs/resources/sql/create_metadata.sql
@@ -0,0 +1,7 @@
+CREATE TABLE IF NOT EXISTS metadata (
+ id INTEGER PRIMARY KEY
+, foreign_id INTEGER
+, key TEXT
+, value TEXT
+, table_name TEXT
+);
diff --git a/libs/resources/sql/create_resource_tags.sql b/libs/resources/sql/create_resource_tags.sql
new file mode 100644
index 0000000000..65b8acf966
--- /dev/null
+++ b/libs/resources/sql/create_resource_tags.sql
@@ -0,0 +1,7 @@
+CREATE TABLE IF NOT EXISTS resource_tags (
+ id INTEGER PRIMARY KEY
+, resource_id INTEGER NOT NULL
+, tag_id INTEGER NOT NULL
+, FOREIGN KEY(resource_id) REFERENCES resources(id)
+, FOREIGN KEY(tag_id) REFERENCES tags(id)
+);
diff --git a/libs/resources/sql/create_resource_types.sql b/libs/resources/sql/create_resource_types.sql
new file mode 100644
index 0000000000..bd3342b0b4
--- /dev/null
+++ b/libs/resources/sql/create_resource_types.sql
@@ -0,0 +1,4 @@
+CREATE TABLE IF NOT EXISTS resource_types (
+ id INTEGER PRIMARY KEY
+, name TEXT NOT NULL UNIQUE
+);
diff --git a/libs/resources/sql/create_resources.sql b/libs/resources/sql/create_resources.sql
new file mode 100644
index 0000000000..8637356999
--- /dev/null
+++ b/libs/resources/sql/create_resources.sql
@@ -0,0 +1,15 @@
+CREATE TABLE IF NOT EXISTS resources (
+ id INTEGER PRIMARY KEY /* within this database, a unique and stable id for this resource */
+, resource_type_id INTEGER /* points to the type of this resource */
+, storage_id INTEGER /* points to the storage object that contains the actual resource */
+, name TEXT NOT NULL /* the untranslatable name of the resource */
+, filename TEXT NOT NULL /* the filename of the resource RELATIVE to the storage path and resource type */
+, tooltip TEXT /* a translated text that can be shown in the UI */
+, thumbnail BLOB /* the image representing the resource visually*/
+, status INTEGER /* active resources are visible in the UI, inactive resources are considered "deleted" */
+, temporary INTEGER /* temporary resources are removed from the database on startup */
+, version INTEGER /* the current version number of the resource (cached for performance reasons */
+, FOREIGN KEY(resource_type_id) REFERENCES resource_types(id)
+, UNIQUE(storage_id, resource_type_id, name)
+, UNIQUE(storage_id, filename)
+);
diff --git a/libs/resources/sql/create_storage_types.sql b/libs/resources/sql/create_storage_types.sql
new file mode 100644
index 0000000000..00a6a423cf
--- /dev/null
+++ b/libs/resources/sql/create_storage_types.sql
@@ -0,0 +1,4 @@
+CREATE TABLE IF NOT EXISTS storage_types (
+ id INTEGER PRIMARY KEY
+, name TEXT
+);
diff --git a/libs/resources/sql/create_storages.sql b/libs/resources/sql/create_storages.sql
new file mode 100644
index 0000000000..54186a18b3
--- /dev/null
+++ b/libs/resources/sql/create_storages.sql
@@ -0,0 +1,11 @@
+CREATE TABLE IF NOT EXISTS storages (
+ id INTEGER PRIMARY KEY AUTOINCREMENT
+, storage_type_id INTEGER
+, location TEXT
+, timestamp INTEGER
+, pre_installed INTEGER
+, active INTEGER
+, thumbnail BLOB /* the image representing the storage visually*/
+, FOREIGN KEY(storage_type_id) REFERENCES storage_types(id)
+, UNIQUE(location)
+);
diff --git a/libs/resources/sql/create_tags.sql b/libs/resources/sql/create_tags.sql
new file mode 100644
index 0000000000..e71112d52f
--- /dev/null
+++ b/libs/resources/sql/create_tags.sql
@@ -0,0 +1,10 @@
+CREATE TABLE IF NOT EXISTS tags (
+ id INTEGER PRIMARY KEY
+, resource_type_id INTEGER
+, url TEXT COLLATE NOCASE /* the unique untranslated name of the tag */
+, name TEXT /* the translated name of the tag */
+, comment TEXT /* a translated comment on the tag */
+, active INTEGER
+, FOREIGN KEY(resource_type_id) REFERENCES resource_types(id)
+, UNIQUE (url, resource_type_id)
+);
diff --git a/libs/resources/sql/create_tags_storages.sql b/libs/resources/sql/create_tags_storages.sql
new file mode 100644
index 0000000000..c36d94deb9
--- /dev/null
+++ b/libs/resources/sql/create_tags_storages.sql
@@ -0,0 +1,7 @@
+CREATE TABLE IF NOT EXISTS tags_storages (
+ tag_id INTEGER
+, storage_id INTEGER
+, FOREIGN KEY(storage_id) REFERENCES storages(id)
+, FOREIGN KEY(tag_id) REFERENCES tags (id)
+, UNIQUE (tag_id, storage_id)
+);
diff --git a/libs/resources/sql/create_version_information.sql b/libs/resources/sql/create_version_information.sql
new file mode 100644
index 0000000000..a2830dccb0
--- /dev/null
+++ b/libs/resources/sql/create_version_information.sql
@@ -0,0 +1,6 @@
+CREATE TABLE IF NOT EXISTS version_information (
+ id INTEGER PRIMARY KEY AUTOINCREMENT
+, database_version TEXT
+, krita_version TEXT
+, creation_date INTEGER
+);
diff --git a/libs/resources/sql/create_versioned_resources.sql b/libs/resources/sql/create_versioned_resources.sql
new file mode 100644
index 0000000000..1a5691dfce
--- /dev/null
+++ b/libs/resources/sql/create_versioned_resources.sql
@@ -0,0 +1,11 @@
+CREATE TABLE IF NOT EXISTS versioned_resources (
+ id INTEGER PRIMARY KEY
+, resource_id INTEGER
+, storage_id INTEGER
+, version INTEGER
+, location TEXT NOT NULL
+, md5sum TEXT NOT NULL
+, timestamp INTEGER
+, FOREIGN KEY(resource_id) REFERENCES resources(id)
+, FOREIGN KEY(storage_id) REFERENCES storages(id)
+);
diff --git a/libs/resources/sql/fill_resource_types.sql b/libs/resources/sql/fill_resource_types.sql
new file mode 100644
index 0000000000..b782d00428
--- /dev/null
+++ b/libs/resources/sql/fill_resource_types.sql
@@ -0,0 +1 @@
+INSERT INTO resource_types (name) VALUES (?);
diff --git a/libs/resources/sql/fill_storage_types.sql b/libs/resources/sql/fill_storage_types.sql
new file mode 100644
index 0000000000..7d83d0b8a0
--- /dev/null
+++ b/libs/resources/sql/fill_storage_types.sql
@@ -0,0 +1 @@
+INSERT into storage_types (name) VALUES (?);
diff --git a/libs/resources/sql/fill_version_information.sql b/libs/resources/sql/fill_version_information.sql
new file mode 100644
index 0000000000..b425cd6a58
--- /dev/null
+++ b/libs/resources/sql/fill_version_information.sql
@@ -0,0 +1,9 @@
+INSERT INTO version_information (
+ database_version
+, krita_version
+, creation_date )
+VALUES (
+ ?
+, ?
+, ?
+);
diff --git a/libs/resources/sql/get_version_information.sql b/libs/resources/sql/get_version_information.sql
new file mode 100644
index 0000000000..619540f78c
--- /dev/null
+++ b/libs/resources/sql/get_version_information.sql
@@ -0,0 +1,8 @@
+SELECT (
+ database_version
+, krita_version
+, creation_date (
+FROM version_information
+ORDER BY id
+DESC
+LIMIT 1;
diff --git a/libs/resources/sql/select_resource_id.sql b/libs/resources/sql/select_resource_id.sql
new file mode 100644
index 0000000000..8e12795bfa
--- /dev/null
+++ b/libs/resources/sql/select_resource_id.sql
@@ -0,0 +1,9 @@
+SELECT resources.id
+FROM resources
+, resource_types
+, storages
+WHERE resources.resource_type_id = resource_types.id
+AND storages.id = resources.storage_id
+AND storages.location = :storage_location
+AND resource_types.name = :resource_type
+AND resources.name = :name
diff --git a/libs/resources/sql/select_tag.sql b/libs/resources/sql/select_tag.sql
new file mode 100644
index 0000000000..e1990e5ddd
--- /dev/null
+++ b/libs/resources/sql/select_tag.sql
@@ -0,0 +1,6 @@
+SELECT tags.id
+FROM tags
+, resource_types
+WHERE tags.url = :url
+AND resource_types.id = tags.resource_type_id
+AND resource_types.name = :resource_type;
diff --git a/libs/resources/sql/update_from_001.sql b/libs/resources/sql/update_from_001.sql
new file mode 100644
index 0000000000..bd1bcf82e5
--- /dev/null
+++ b/libs/resources/sql/update_from_001.sql
@@ -0,0 +1,15 @@
+CREATE TABLE IF NOT EXISTS resource_metadata (
+ id INTEGER PRIMARY KEY
+, resource_id INTEGER
+, key TEXT
+, value TEXT
+, FOREIGN KEY(resource_id) REFERENCES resources(id)
+);
+
+
+INSERT INTO storage_types
+VALUES name = "Memory";
+
+
+UPDATE TABLE version_information
+SET database_version = "0.0.3"
diff --git a/libs/resources/tests/CMakeLists.txt b/libs/resources/tests/CMakeLists.txt
new file mode 100644
index 0000000000..fd18530727
--- /dev/null
+++ b/libs/resources/tests/CMakeLists.txt
@@ -0,0 +1,26 @@
+add_definitions(-DFILES_DEST_DIR="${CMAKE_CURRENT_BINARY_DIR}/data/")
+
+set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
+
+include(ECMAddTests)
+include(KritaAddBrokenUnitTest)
+
+macro_add_unittest_definitions()
+
+ecm_add_tests(
+ TestResourceCacheDb
+ TestResourceLoaderRegistry
+ TestResourceLocator
+ TestResourceStorage
+ TestFolderStorage
+ TestMemoryStorage
+ TestTag
+ TestBundleStorage
+ TestResourceModel
+ TestTagFilterResourceProxyModel
+ TestTagModel
+ TestResourceTypeModel
+ TestStorageModel
+ TestResourceSearchBoxFilter
+ NAME_PREFIX "libs-kritaresources-"
+ LINK_LIBRARIES kritaglobal kritaplugin kritaresources kritaversion KF5::ConfigCore Qt5::Sql Qt5::Test)
diff --git a/libs/resources/tests/DummyResource.h b/libs/resources/tests/DummyResource.h
new file mode 100644
index 0000000000..ea79612a28
--- /dev/null
+++ b/libs/resources/tests/DummyResource.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2018 boud <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 DUMMYRESOURCE_H
+#define DUMMYRESOURCE_H
+
+#include "KoResource.h"
+#include <QDebug>
+#include <QRandomGenerator64>
+#include <KoMD5Generator.h>
+#include <KoMD5Generator.h>
+#include <KisResourceTypes.h>
+
+class DummyResource : public KoResource {
+public:
+ DummyResource(const QString &f, const QString &resourceType = ResourceType::PaintOpPresets)
+ : KoResource(f)
+ , m_resourceType(resourceType)
+ {
+ QRandomGenerator64 qrg;
+ QByteArray ba(1024, '0');
+ for (int i = 0; i < 1024 / 8; i+=8) {
+ quint64 v = qrg.generate64();
+ ba[i] = v;
+ }
+ QByteArray hash = KoMD5Generator::generateHash(ba);
+ setMD5(hash);
+ setValid(true);
+ }
+
+ DummyResource(const DummyResource &rhs)
+ : KoResource(rhs),
+ m_something(rhs.m_something)
+ {
+ }
+
+ KoResourceSP clone() const override
+ {
+ return KoResourceSP(new DummyResource(*this));
+ }
+
+ bool load(KisResourcesInterfaceSP resourcesInterface) override
+ {
+ Q_UNUSED(resourcesInterface);
+
+ Q_ASSERT(false);
+ setValid(true);
+ return true;
+ }
+
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override
+ {
+ Q_UNUSED(resourcesInterface);
+
+ if (!dev->isOpen()) {
+ dev->open(QIODevice::ReadOnly);
+ }
+ m_something = QString::fromUtf8(dev->readAll());
+ setValid(true);
+ return true;
+ }
+
+ bool save() override
+ {
+ Q_ASSERT(false);
+ return true;
+ }
+
+ bool saveToDevice(QIODevice *dev) const
+ {
+ if (!dev->isOpen()) {
+ dev->open(QIODevice::WriteOnly);
+ }
+ dev->write(m_something.toUtf8());
+ return true;
+ }
+
+ void setSomething(const QString &something)
+ {
+ m_something = something;
+ }
+
+ QString something() const
+ {
+ return m_something;
+ }
+
+ QPair<QString, QString> resourceType() const override {
+ return QPair<QString, QString>(m_resourceType, "");
+ }
+
+private:
+
+ QString m_something;
+ QString m_resourceType;
+};
+
+#endif // DUMMYRESOURCE_H
diff --git a/libs/resources/tests/ResourceTestHelper.h b/libs/resources/tests/ResourceTestHelper.h
new file mode 100644
index 0000000000..6fb9db57b1
--- /dev/null
+++ b/libs/resources/tests/ResourceTestHelper.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2018 boud <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 RESOURCETESTHELPER_H
+#define RESOURCETESTHELPER_H
+
+#include <QImageReader>
+#include <QDir>
+#include <QStandardPaths>
+#include <QDirIterator>
+
+#include <KisMimeDatabase.h>
+#include <KisResourceLoaderRegistry.h>
+
+#include <KisResourceCacheDb.h>
+#include "KisResourceTypes.h"
+#include <DummyResource.h>
+
+
+#ifndef FILES_DATA_DIR
+#error "FILES_DATA_DIR not set. A directory with the data used for testing installing resources"
+#endif
+
+#ifndef FILES_DEST_DIR
+#error "FILES_DEST_DIR not set. A directory where data will be written to for testing installing resources"
+#endif
+
+namespace ResourceTestHelper {
+
+void rmTestDb() {
+ QDir dbLocation(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+ QFile(dbLocation.path() + "/" + KisResourceCacheDb::resourceCacheDbFilename).remove();
+ dbLocation.rmpath(dbLocation.path());
+}
+
+void createDummyLoaderRegistry() {
+
+ KisResourceLoaderRegistry *reg = KisResourceLoaderRegistry::instance();
+ reg->add(new KisResourceLoader<DummyResource>(ResourceType::PaintOpPresets, ResourceType::PaintOpPresets, i18n("Brush presets"), QStringList() << "application/x-krita-paintoppreset"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceSubType::GbrBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/x-gimp-brush"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceSubType::GihBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/x-gimp-brush-animated"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceSubType::SvgBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/svg+xml"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceSubType::PngBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/png"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceSubType::SegmentedGradients, ResourceType::Gradients, i18n("Gradients"), QStringList() << "application/x-gimp-gradient"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceSubType::StopGradients, ResourceType::Gradients, i18n("Gradients"), QStringList() << "application/x-karbon-gradient" << "image/svg+xml"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceType::Palettes, ResourceType::Palettes, i18n("Palettes"),
+ QStringList() << KisMimeDatabase::mimeTypeForSuffix("kpl")
+ << KisMimeDatabase::mimeTypeForSuffix("gpl")
+ << KisMimeDatabase::mimeTypeForSuffix("pal")
+ << KisMimeDatabase::mimeTypeForSuffix("act")
+ << KisMimeDatabase::mimeTypeForSuffix("aco")
+ << KisMimeDatabase::mimeTypeForSuffix("css")
+ << KisMimeDatabase::mimeTypeForSuffix("colors")
+ << KisMimeDatabase::mimeTypeForSuffix("xml")
+ << KisMimeDatabase::mimeTypeForSuffix("sbz")));
+
+ QList<QByteArray> src = QImageReader::supportedMimeTypes();
+ QStringList allImageMimes;
+ Q_FOREACH(const QByteArray ba, src) {
+ allImageMimes << QString::fromUtf8(ba);
+ }
+ allImageMimes << KisMimeDatabase::mimeTypeForSuffix("pat");
+
+ reg->add(new KisResourceLoader<DummyResource>(ResourceType::Patterns, ResourceType::Patterns, i18n("Patterns"), allImageMimes));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceType::Workspaces, ResourceType::Workspaces, i18n("Workspaces"), QStringList() << "application/x-krita-workspace"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceType::Symbols, ResourceType::Symbols, i18n("SVG symbol libraries"), QStringList() << "image/svg+xml"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceType::WindowLayouts, ResourceType::WindowLayouts, i18n("Window layouts"), QStringList() << "application/x-krita-windowlayout"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceType::Sessions, ResourceType::Sessions, i18n("Sessions"), QStringList() << "application/x-krita-session"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceType::GamutMasks, ResourceType::GamutMasks, i18n("Gamut masks"), QStringList() << "application/x-krita-gamutmask"));
+
+}
+
+bool cleanDstLocation(const QString &dstLocation)
+{
+ if (QDir(dstLocation).exists()) {
+ {
+ QDirIterator iter(dstLocation, QStringList() << "*", QDir::Files, QDirIterator::Subdirectories);
+ while (iter.hasNext()) {
+ iter.next();
+ QFile f(iter.filePath());
+ f.remove();
+ //qDebug() << (r ? "Removed" : "Failed to remove") << iter.filePath();
+ }
+ }
+ {
+ QDirIterator iter(dstLocation, QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+ while (iter.hasNext()) {
+ iter.next();
+ QDir(iter.filePath()).rmpath(iter.filePath());
+ //qDebug() << (r ? "Removed" : "Failed to remove") << iter.filePath();
+ }
+ }
+
+ return QDir().rmpath(dstLocation);
+ }
+ return true;
+}
+
+void initTestDb()
+{
+ rmTestDb();
+ cleanDstLocation(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+}
+
+
+}
+
+#endif // RESOURCETESTHELPER_H
diff --git a/libs/resources/tests/TestBundleStorage.cpp b/libs/resources/tests/TestBundleStorage.cpp
new file mode 100644
index 0000000000..b66d0b39e6
--- /dev/null
+++ b/libs/resources/tests/TestBundleStorage.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2018 boud <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 "TestBundleStorage.h"
+
+#include <QTest>
+#include <QImageReader>
+
+#include <KoConfig.h>
+
+#include <KisBundleStorage.h>
+#include <KisResourceLoader.h>
+#include <KoResource.h>
+#include <KisResourceLoaderRegistry.h>
+#include "DummyResource.h"
+
+void TestBundleStorage::initTestCase()
+{
+ KisResourceLoaderRegistry *reg = KisResourceLoaderRegistry::instance();
+ reg->add(new KisResourceLoader<DummyResource>(ResourceSubType::GbrBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/x-gimp-brush"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceSubType::GihBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/x-gimp-brush-animated"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceSubType::SvgBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/svg+xml"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceSubType::PngBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/png"));
+ reg->add(new KisResourceLoader<DummyResource>(ResourceType::PaintOpPresets, ResourceType::PaintOpPresets, i18n("Brush presets"), QStringList() << "application/x-krita-paintoppreset"));
+ QList<QByteArray> src = QImageReader::supportedMimeTypes();
+ QStringList allImageMimes;
+ Q_FOREACH(const QByteArray ba, src) {
+ allImageMimes << QString::fromUtf8(ba);
+ }
+ reg->add(new KisResourceLoader<DummyResource>(ResourceType::Patterns, ResourceType::Patterns, i18n("Patterns"), allImageMimes));
+}
+
+void TestBundleStorage::testMetaData()
+{
+ KisBundleStorage storage(KRITA_SOURCE_DIR + QString("/krita/data/bundles/Krita_4_Default_Resources.bundle"));
+ QVERIFY(storage.location() == KRITA_SOURCE_DIR + QString("/krita/data/bundles/Krita_4_Default_Resources.bundle"));
+ QVERIFY(!storage.metaData(KisResourceStorage::s_meta_generator).isNull());
+ QVERIFY(!storage.metaData(KisResourceStorage::s_meta_author).isNull());
+ QVERIFY(!storage.metaData(KisResourceStorage::s_meta_description).isNull());
+ QVERIFY(!storage.metaData(KisResourceStorage::s_meta_initial_creator).isNull());
+ QVERIFY(!storage.metaData(KisResourceStorage::s_meta_dc_date).isNull());
+ QVERIFY(!storage.metaData(KisResourceStorage::s_meta_version).isNull());
+}
+
+void TestBundleStorage::testResourceIterator()
+{
+ KisBundleStorage storage(KRITA_SOURCE_DIR + QString("/krita/data/bundles/Krita_4_Default_Resources.bundle"));
+ QSharedPointer<KisResourceStorage::ResourceIterator> iter = storage.resources(ResourceType::Brushes);
+ QVERIFY(iter->hasNext());
+ int count = 0;
+ while (iter->hasNext()) {
+ iter->next();
+ KoResourceSP res = iter->resource();
+ QVERIFY(res);
+ count++;
+ }
+ QVERIFY(count > 0);
+}
+
+void TestBundleStorage::testTagIterator()
+{
+ KisBundleStorage storage(KRITA_SOURCE_DIR + QString("/krita/data/bundles/Krita_4_Default_Resources.bundle"));
+ QSharedPointer<KisResourceStorage::TagIterator> iter = storage.tags(ResourceType::PaintOpPresets);
+ QVERIFY(iter->hasNext());
+ int count = 0;
+ while (iter->hasNext()) {
+ //qDebug() << iter->url() << iter->name() << iter->tag();
+ iter->next();
+ count++;
+ }
+ QVERIFY(count > 0);
+}
+
+void TestBundleStorage::testResourceItem()
+{
+ KisBundleStorage storage(KRITA_SOURCE_DIR + QString("/krita/data/bundles/Krita_4_Default_Resources.bundle"));
+ KisResourceStorage::ResourceItem item = storage.resourceItem("paintoppresets/g)_Dry_Brushing.kpp");
+ QVERIFY(!item.url.isEmpty());
+}
+
+void TestBundleStorage::testResource()
+{
+ KisBundleStorage storage(KRITA_SOURCE_DIR + QString("/krita/data/bundles/Krita_4_Default_Resources.bundle"));
+ KoResourceSP res = storage.resource("paintoppresets/g)_Dry_Brushing.kpp");
+ QVERIFY(res);
+ QVERIFY(res->filename() == "g)_Dry_Brushing.kpp");
+}
+
+
+QTEST_MAIN(TestBundleStorage)
+
diff --git a/libs/resources/tests/TestBundleStorage.h b/libs/resources/tests/TestBundleStorage.h
new file mode 100644
index 0000000000..8637b2bab5
--- /dev/null
+++ b/libs/resources/tests/TestBundleStorage.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018 boud <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 TESTBUNDLESTORAGE_H
+#define TESTBUNDLESTORAGE_H
+
+#include <QObject>
+
+class TestBundleStorage : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+ void testMetaData();
+ void testResourceIterator();
+ void testTagIterator();
+ void testResourceItem();
+ void testResource();
+};
+
+#endif // TESTBUNDLESTORAGE_H
diff --git a/libs/resources/tests/TestFolderStorage.cpp b/libs/resources/tests/TestFolderStorage.cpp
new file mode 100644
index 0000000000..b6ee5c4100
--- /dev/null
+++ b/libs/resources/tests/TestFolderStorage.cpp
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+#include "TestFolderStorage.h"
+#include <QTest>
+
+#include <kconfiggroup.h>
+#include <ksharedconfig.h>
+
+#include <KritaVersionWrapper.h>
+
+#include <KisFolderStorage.h>
+#include <KisResourceLoader.h>
+#include <KoResource.h>
+#include <KisResourceLoaderRegistry.h>
+#include <KisResourceCacheDb.h>
+
+#include <KisResourceLocator.h>
+#include <KisResourceLoaderRegistry.h>
+
+#include "DummyResource.h"
+#include "ResourceTestHelper.h"
+
+#ifndef FILES_DATA_DIR
+#error "FILES_DATA_DIR not set. A directory with the data used for testing installing resources"
+#endif
+
+#ifndef FILES_DEST_DIR
+#error "FILES_DEST_DIR not set. A directory where data will be written to for testing installing resources"
+#endif
+
+
+void TestFolderStorage::initTestCase()
+{
+ ResourceTestHelper::initTestDb();
+
+ m_srcLocation = QString(FILES_DATA_DIR);
+ QVERIFY2(QDir(m_srcLocation).exists(), m_srcLocation.toUtf8());
+
+ m_dstLocation = QString(FILES_DEST_DIR);
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+
+ KConfigGroup cfg(KSharedConfig::openConfig(), "");
+ cfg.writeEntry(KisResourceLocator::resourceLocationKey, m_dstLocation);
+
+ m_locator = KisResourceLocator::instance();
+
+ ResourceTestHelper::createDummyLoaderRegistry();
+
+ KisResourceCacheDb::initialize(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+ m_locator->initialize(m_srcLocation);
+
+ if (!m_locator->errorMessages().isEmpty()) qDebug() << m_locator->errorMessages();
+}
+
+void TestFolderStorage ::testStorage()
+{
+
+ KisFolderStorage folderStorage(m_dstLocation);
+
+ KisResourceLoaderRegistry::instance()->add(ResourceType::Brushes, new KisResourceLoader<DummyResource>("dummy", ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/x-gimp-brush"));
+ QSharedPointer<KisResourceStorage::ResourceIterator> iter = folderStorage.resources(ResourceType::Brushes);
+ QVERIFY(iter->hasNext());
+ int count = 0;
+ while (iter->hasNext()) {
+ iter->next();
+ qDebug() << iter->url() << iter->type() << iter->lastModified();
+ count++;
+ }
+ QVERIFY(count == 1);
+}
+
+void TestFolderStorage::testTagIterator()
+{
+ KisFolderStorage folderStorage(m_dstLocation);
+ QSharedPointer<KisResourceStorage::TagIterator> iter = folderStorage.tags(ResourceType::PaintOpPresets);
+ QVERIFY(iter->hasNext());
+ int count = 0;
+ while (iter->hasNext()) {
+ iter->next();
+ qDebug() << iter->url() << iter->name() << iter->tag();
+ count++;
+ }
+ QVERIFY(count == 1);
+}
+
+void TestFolderStorage::testAddResource()
+{
+ KoResourceSP resource(new DummyResource("anewresource.kpp"));
+ resource->setValid(true);
+ resource->setVersion(0);
+
+ KisFolderStorage folderStorage(QString(FILES_DEST_DIR));
+ bool r = folderStorage.addResource("paintoppresets", resource);
+ QVERIFY(r);
+ QString fileName = resource->filename();
+
+ resource.dynamicCast<DummyResource>()->setSomething("It's changed");
+ r = folderStorage.addResource("paintoppresets", resource);
+ QVERIFY(r);
+ QVERIFY(resource->filename() != fileName);
+ QVERIFY(resource->version() == 1);
+
+ QDir d(m_dstLocation + "/" + "paintoppresets");
+ QVERIFY(d.entryList().contains("anewresource.0001.kpp"));
+}
+
+void TestFolderStorage::cleanupTestCase()
+{
+ ResourceTestHelper::rmTestDb();
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+}
+
+QTEST_MAIN(TestFolderStorage)
+
diff --git a/libs/resources/tests/TestFolderStorage.h b/libs/resources/tests/TestFolderStorage.h
new file mode 100644
index 0000000000..ca79ca3984
--- /dev/null
+++ b/libs/resources/tests/TestFolderStorage.h
@@ -0,0 +1,43 @@
+/*
+ * 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 TESTFOLDERSTORAGE_H
+#define TESTFOLDERSTORAGE_H
+
+#include <QObject>
+
+class KisResourceLocator;
+
+class TestFolderStorage : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+ void testStorage();
+ void testTagIterator();
+ void testAddResource();
+ void cleanupTestCase();
+private:
+ QString m_srcLocation;
+ QString m_dstLocation;
+ KisResourceLocator *m_locator;
+
+};
+
+#endif
diff --git a/libs/resources/tests/TestMemoryStorage.cpp b/libs/resources/tests/TestMemoryStorage.cpp
new file mode 100644
index 0000000000..d44b836f2a
--- /dev/null
+++ b/libs/resources/tests/TestMemoryStorage.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "TestMemoryStorage.h"
+#include <QTest>
+
+#include <KisMemoryStorage.h>
+#include <KisResourceLoader.h>
+#include <KoResource.h>
+#include <KisResourceLoaderRegistry.h>
+
+#include "DummyResource.h"
+
+#ifndef FILES_DATA_DIR
+#error "FILES_DATA_DIR not set. A directory with the data used for testing installing resources"
+#endif
+
+void TestMemoryStorage ::testStorage()
+{
+ KisMemoryStorage memoryStorage;
+ KoResourceSP resource(new DummyResource("test"));
+ memoryStorage.addResource("brushes", resource);
+
+ KisResourceLoaderRegistry::instance()->add(ResourceType::Brushes, new KisResourceLoader<DummyResource>("dummy", ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/x-gimp-brush"));
+
+ QSharedPointer<KisResourceStorage::ResourceIterator> iter = memoryStorage.resources(ResourceType::Brushes);
+ QVERIFY(iter->hasNext());
+ int count = 0;
+ while (iter->hasNext()) {
+ iter->next();
+ QVERIFY(iter->resource());
+ count++;
+ }
+ QCOMPARE(count, 1);
+}
+
+void TestMemoryStorage::testTagIterator()
+{
+ KisMemoryStorage memoryStorage;
+ KisTagSP tag(new KisTag());
+ tag->setComment("comment");
+ tag->setUrl("url");
+ tag->setName("name");
+ memoryStorage.addTag("paintoppresets", tag);
+ QSharedPointer<KisResourceStorage::TagIterator> iter = memoryStorage.tags(ResourceType::PaintOpPresets);
+ QVERIFY(iter->hasNext());
+ int count = 0;
+ while (iter->hasNext()) {
+ iter->next();
+ count++;
+ }
+ QVERIFY(count == 1);
+}
+
+QTEST_MAIN(TestMemoryStorage)
+
diff --git a/libs/resources/tests/TestMemoryStorage.h b/libs/resources/tests/TestMemoryStorage.h
new file mode 100644
index 0000000000..f0830a881c
--- /dev/null
+++ b/libs/resources/tests/TestMemoryStorage.h
@@ -0,0 +1,34 @@
+/*
+ * 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 TESTMEMORYSTORAGE_H
+#define TESTMEMORYSTORAGE_H
+
+#include <QObject>
+
+class TestMemoryStorage : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void testStorage();
+ void testTagIterator();
+private:
+};
+
+#endif
diff --git a/libs/resources/tests/TestResource.cpp b/libs/resources/tests/TestResource.cpp
new file mode 100644
index 0000000000..e20e0fa8e5
--- /dev/null
+++ b/libs/resources/tests/TestResource.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 boud <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 "TestResource.h"
+
+#include <QTest>
+
+#include "DummyResource.h"
+
+void TestResource::testCopyResource()
+{
+ DummyResource r1("filename");
+ DummyResource r2(r1);
+ QCOMPARE(r1.filename(), r2.filename());
+ r2.setFilename("other");
+ QVERIFY(r1.filename() == "filename");
+ QVERIFY(r2.filename() == "other");
+}
+
+QTEST_MAIN(TestResource)
diff --git a/libs/resources/tests/TestResource.h b/libs/resources/tests/TestResource.h
new file mode 100644
index 0000000000..c966892200
--- /dev/null
+++ b/libs/resources/tests/TestResource.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 boud <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 TESTRESOURCE_H
+#define TESTRESOURCE_H
+
+#include <QObject>
+
+class TestResource : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void testCopyResource();
+};
+
+#endif // TESTRESOURCE_H
diff --git a/libs/resources/tests/TestResourceCacheDb.cpp b/libs/resources/tests/TestResourceCacheDb.cpp
new file mode 100644
index 0000000000..1247c2b6ae
--- /dev/null
+++ b/libs/resources/tests/TestResourceCacheDb.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#include "TestResourceCacheDb.h"
+#include <QTest>
+#include <QtSql>
+#include <QStandardPaths>
+#include <QDir>
+#include <QImage>
+#include <QPainter>
+
+#include <KisResourceCacheDb.h>
+#include <KisResourceLoaderRegistry.h>
+
+void TestResourceCacheDb::initTestCase()
+{
+ QDir dbLocation(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+ if (dbLocation.exists()) {
+ QFile(dbLocation.path() + "/" + KisResourceCacheDb::resourceCacheDbFilename).remove();
+ dbLocation.rmpath(dbLocation.path());
+ }
+}
+
+void TestResourceCacheDb::testCreateDatabase()
+{
+ bool res = KisResourceCacheDb::initialize(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+ QVERIFY(res);
+ QVERIFY(KisResourceCacheDb::isValid());
+
+ QSqlDatabase sqlDb = QSqlDatabase::database();
+
+ QStringList tables = QStringList() << "version_information"
+ << "storage_types"
+ << "resource_types"
+ << "storages"
+ << "tags"
+ << "resources"
+ << "versioned_resources"
+ << "resource_tags";
+ QStringList dbTables = sqlDb.tables();
+
+ Q_FOREACH(const QString &table, tables) {
+ QVERIFY2(dbTables.contains(table), table.toLatin1());
+ }
+
+ res = KisResourceCacheDb::initialize(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+ QVERIFY(res);
+ QVERIFY(KisResourceCacheDb::isValid());
+}
+
+void TestResourceCacheDb::testLookupTables()
+{
+ QSqlQuery query;
+ bool r = query.exec("SELECT COUNT(*) FROM storage_types");
+ QVERIFY(r);
+ QVERIFY(query.lastError() == QSqlError());
+ query.first();
+ QCOMPARE(query.value(0).toInt(), 6);
+
+ r = query.exec("SELECT COUNT(*) FROM resource_types");
+ QVERIFY(r);
+ QVERIFY(query.lastError() == QSqlError());
+ query.first();
+ QVERIFY(query.value(0).toInt() == KisResourceLoaderRegistry::instance()->resourceTypes().count());
+}
+
+void TestResourceCacheDb::testMetaData()
+{
+ // Test adding metadata
+ QMap<QString, QVariant> m1;
+ m1["string"] = QString("bla");
+ m1["bool"] = QVariant::fromValue<bool>(true);
+ m1["int"] = QVariant::fromValue<int>(10);
+ QImage img(50, 50, QImage::Format_RGB32);
+ QPainter gc(&img);
+ gc.fillRect(QRect(0, 0, 50, 50), QBrush(Qt::red));
+ gc.end();
+ m1["image"] = QVariant::fromValue<QImage>(img);
+
+ bool r = KisResourceCacheDb::updateMetaDataForId(m1, 1, "test");
+ QVERIFY(r);
+
+ // Test retrieving metadata
+ QMap<QString, QVariant> m2 = KisResourceCacheDb::metaDataForId(1, "test");
+ QVERIFY(m1 == m2);
+
+ // Test deleting metadata
+ r = KisResourceCacheDb::updateMetaDataForId(QMap<QString, QVariant>(), 1, "test");
+ QMap<QString, QVariant> m3 = KisResourceCacheDb::metaDataForId(1, "test");
+ QVERIFY(m3.size() == 0);
+}
+
+void TestResourceCacheDb::cleanupTestCase()
+{
+ QDir dbLocation(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+ bool res = QFile(dbLocation.path() + "/" + KisResourceCacheDb::resourceCacheDbFilename).remove();
+ Q_ASSERT(res);
+ res = dbLocation.rmpath(dbLocation.path());
+ Q_ASSERT(res);
+}
+
+QTEST_MAIN(TestResourceCacheDb)
+
diff --git a/libs/resources/tests/TestResourceCacheDb.h b/libs/resources/tests/TestResourceCacheDb.h
new file mode 100644
index 0000000000..12377437af
--- /dev/null
+++ b/libs/resources/tests/TestResourceCacheDb.h
@@ -0,0 +1,37 @@
+/*
+ * 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 TESTRESOURCECACHEDB_H
+#define TESTRESOURCECACHEDB_H
+
+#include <QObject>
+
+class TestResourceCacheDb : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+ void testCreateDatabase();
+ void testLookupTables();
+ void testMetaData();
+ void cleanupTestCase();
+private:
+};
+
+#endif
diff --git a/libs/resources/tests/TestResourceLoaderRegistry.cpp b/libs/resources/tests/TestResourceLoaderRegistry.cpp
new file mode 100644
index 0000000000..de64c0902b
--- /dev/null
+++ b/libs/resources/tests/TestResourceLoaderRegistry.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#include "TestResourceLoaderRegistry.h"
+#include <QTest>
+#include <QtSql>
+#include <QStandardPaths>
+#include <QDir>
+
+#include <KisResourceLoaderRegistry.h>
+#include <KisResourceLoader.h>
+#include <KoResource.h>
+#include <KisGlobalResourcesInterface.h>
+
+#include "DummyResource.h"
+
+void TestResourceLoaderRegistry::testRegistry()
+{
+ KisResourceLoaderRegistry *reg = KisResourceLoaderRegistry::instance();
+
+ KisResourceLoader<DummyResource> *loader = new KisResourceLoader<DummyResource>("dummy", "dummy", i18n("Dummy"), QStringList() << "x-dummy");
+ reg->add(loader);
+ QVERIFY(reg->count() == 1);
+
+ KisResourceLoaderBase *l2 = reg->get("dummy");
+ QByteArray ba;
+ QBuffer f(&ba);
+ f.open(QFile::ReadOnly);
+ KoResourceSP res = l2->load("test", f, KisGlobalResourcesInterface::instance());
+ QVERIFY(res.data());
+ QVERIFY(dynamic_cast<DummyResource*>(res.data()));
+}
+
+QTEST_MAIN(TestResourceLoaderRegistry)
+
diff --git a/libs/resources/tests/TestResourceLoaderRegistry.h b/libs/resources/tests/TestResourceLoaderRegistry.h
new file mode 100644
index 0000000000..927c3773d1
--- /dev/null
+++ b/libs/resources/tests/TestResourceLoaderRegistry.h
@@ -0,0 +1,33 @@
+/*
+ * 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 TESTRESOURCELOADERREGISTRY_H
+#define TESTRESOURCELOADERREGISTRY_H
+
+#include <QObject>
+
+class TestResourceLoaderRegistry : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void testRegistry();
+private:
+};
+
+#endif
diff --git a/libs/resources/tests/TestResourceLocator.cpp b/libs/resources/tests/TestResourceLocator.cpp
new file mode 100644
index 0000000000..3953201b39
--- /dev/null
+++ b/libs/resources/tests/TestResourceLocator.cpp
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+
+#include "TestResourceLocator.h"
+
+#include <QTest>
+#include <QVersionNumber>
+#include <QDirIterator>
+#include <QSqlError>
+#include <QSqlQuery>
+
+#include <kconfig.h>
+#include <kconfiggroup.h>
+#include <ksharedconfig.h>
+
+#include <KritaVersionWrapper.h>
+
+#include <KisResourceCacheDb.h>
+#include <KisResourceLocator.h>
+#include <KisResourceLoaderRegistry.h>
+#include <KisMemoryStorage.h>
+#include <KisResourceModelProvider.h>
+#include <KisResourceModel.h>
+#include <KisResourceTypes.h>
+
+#include <DummyResource.h>
+#include <ResourceTestHelper.h>
+
+
+#ifndef FILES_DATA_DIR
+#error "FILES_DATA_DIR not set. A directory with the data used for testing installing resources"
+#endif
+
+#ifndef FILES_DEST_DIR
+#error "FILES_DEST_DIR not set. A directory where data will be written to for testing installing resources"
+#endif
+
+void TestResourceLocator::initTestCase()
+{
+ ResourceTestHelper::initTestDb();
+
+ m_srcLocation = QString(FILES_DATA_DIR);
+ QVERIFY2(QDir(m_srcLocation).exists(), m_srcLocation.toUtf8());
+
+ m_dstLocation = QString(FILES_DEST_DIR);
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+
+ KConfigGroup cfg(KSharedConfig::openConfig(), "");
+ cfg.writeEntry(KisResourceLocator::resourceLocationKey, m_dstLocation);
+
+ m_locator = KisResourceLocator::instance();
+
+ ResourceTestHelper::createDummyLoaderRegistry();
+}
+
+void TestResourceLocator::testLocatorInitalization()
+{
+ KisResourceCacheDb::initialize(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+ KisResourceLocator::LocatorError r = m_locator->initialize(m_srcLocation);
+ if (!m_locator->errorMessages().isEmpty()) qDebug() << m_locator->errorMessages();
+ QVERIFY(r == KisResourceLocator::LocatorError::Ok);
+ QVERIFY(QDir(m_dstLocation).exists());
+ Q_FOREACH(const QString &folder, KisResourceLoaderRegistry::instance()->resourceTypes()) {
+ QDir dstDir(m_dstLocation + '/' + folder + '/');
+ QDir srcDir(m_srcLocation + '/' + folder + '/');
+
+ QVERIFY(dstDir.exists());
+ QVERIFY(dstDir.entryList(QDir::Files | QDir::NoDotAndDotDot) == srcDir.entryList(QDir::Files | QDir::NoDotAndDotDot));
+ }
+
+ QFile f(m_dstLocation + '/' + "KRITA_RESOURCE_VERSION");
+ QVERIFY(f.exists());
+ f.open(QFile::ReadOnly);
+ QVersionNumber version = QVersionNumber::fromString(QString::fromUtf8(f.readAll()));
+ QVERIFY(version == QVersionNumber::fromString(KritaVersionWrapper::versionString()));
+
+}
+
+void TestResourceLocator::testStorageInitialization()
+{
+ Q_FOREACH(KisResourceStorageSP storage, m_locator->storages()) {
+ QVERIFY(KisResourceCacheDb::addStorage(storage, true));
+ }
+ QSqlQuery query;
+ bool r = query.exec("SELECT COUNT(*) FROM storages");
+ QVERIFY(r);
+ QVERIFY(query.lastError() == QSqlError());
+ query.first();
+ QCOMPARE(query.value(0).toInt(), m_locator->storages().count());
+}
+
+void TestResourceLocator::testLocatorSynchronization()
+{
+ QVERIFY(m_locator->synchronizeDb());
+ {
+ QSqlQuery query;
+ bool r = query.exec("SELECT COUNT(*) FROM resources");
+ QVERIFY(r);
+ QVERIFY(query.lastError() == QSqlError());
+ query.first();
+ QCOMPARE(query.value(0).toInt(), 7);
+ }
+
+ {
+ QSqlQuery query;
+ bool r = query.exec("SELECT COUNT(*) FROM tags");
+ QVERIFY(r);
+ QVERIFY(query.lastError() == QSqlError());
+ query.first();
+ QCOMPARE(query.value(0).toInt(), 1);
+ }
+}
+
+void TestResourceLocator::testResourceLocationBase()
+{
+ QCOMPARE(m_locator->resourceLocationBase(), QString(FILES_DEST_DIR));
+}
+
+void TestResourceLocator::testResource()
+{
+ KoResourceSP res = m_locator->resource("", ResourceType::PaintOpPresets, "test0.kpp");
+ QVERIFY(res);
+}
+
+void TestResourceLocator::testResourceForId()
+{
+ KoResourceSP res = m_locator->resource("", ResourceType::PaintOpPresets, "test0.kpp");
+ int resourceId = KisResourceCacheDb::resourceIdForResource("test0.kpp", ResourceType::PaintOpPresets, "");
+ QVERIFY(resourceId > -1);
+ KoResourceSP res2 = m_locator->resourceForId(resourceId);
+ QCOMPARE(res, res2);
+}
+
+void TestResourceLocator::testStorageContainsResourceByFile()
+{
+ QVERIFY(m_locator->storageContainsResourceByFile("", "paintoppresets", "test0.kpp") > 0);
+ QVERIFY(m_locator->storageContainsResourceByFile("", "paintoppresets", "XSLKDJSADLKSAJDA") == 0);
+}
+
+
+void TestResourceLocator::testDocumentStorage()
+{
+ const QString &documentName("document");
+
+ KisResourceModel *model = KisResourceModelProvider::resourceModel(ResourceType::PaintOpPresets);
+ int rowcount = model->rowCount();
+
+ KisResourceStorageSP documentStorage = QSharedPointer<KisResourceStorage>::create(documentName);
+ KoResourceSP resource(new DummyResource("test"));
+ documentStorage->addResource(resource);
+
+ m_locator->addStorage(documentName, documentStorage);
+
+ QVERIFY(model->rowCount() > rowcount);
+ QVERIFY(m_locator->hasStorage(documentName));
+ m_locator->removeStorage(documentName);
+ QVERIFY(!m_locator->hasStorage(documentName));
+ QVERIFY(model->rowCount() == rowcount);
+}
+
+
+
+void TestResourceLocator::cleanupTestCase()
+{
+ //ResourceTestHelper::rmTestDb();
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+}
+
+QTEST_MAIN(TestResourceLocator)
+
diff --git a/libs/resources/tests/TestResourceLocator.h b/libs/resources/tests/TestResourceLocator.h
new file mode 100644
index 0000000000..3720888c56
--- /dev/null
+++ b/libs/resources/tests/TestResourceLocator.h
@@ -0,0 +1,53 @@
+/*
+ * 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 TESTRESOURCELOCATOR_H
+#define TESTRESOURCELOCATOR_H
+
+#include <QObject>
+
+class KisResourceLocator;
+
+
+class TestResourceLocator : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+ void testLocatorInitalization();
+ void testStorageInitialization();
+ void testLocatorSynchronization();
+
+ void testResourceLocationBase();
+ void testResource();
+ void testResourceForId();
+ void testStorageContainsResourceByFile();
+ void testDocumentStorage();
+
+ void cleanupTestCase();
+
+private:
+
+ QString m_srcLocation;
+ QString m_dstLocation;
+
+ KisResourceLocator *m_locator;
+};
+
+#endif
diff --git a/libs/resources/tests/TestResourceModel.cpp b/libs/resources/tests/TestResourceModel.cpp
new file mode 100644
index 0000000000..ad462f6554
--- /dev/null
+++ b/libs/resources/tests/TestResourceModel.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2018 boud <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 "TestResourceModel.h"
+
+#include <QTest>
+#include <QStandardPaths>
+#include <QDir>
+#include <QVersionNumber>
+#include <QDirIterator>
+#include <QSqlError>
+#include <QSqlQuery>
+#include <QTemporaryFile>
+#include <QDir>
+#include <QtSql>
+
+#include <kconfig.h>
+#include <kconfiggroup.h>
+#include <ksharedconfig.h>
+
+#include <KisResourceCacheDb.h>
+#include <KisResourceLocator.h>
+#include <KisResourceLoaderRegistry.h>
+#include <KisResourceModel.h>
+
+#include <DummyResource.h>
+#include <ResourceTestHelper.h>
+
+#ifndef FILES_DATA_DIR
+#error "FILES_DATA_DIR not set. A directory with the data used for testing installing resources"
+#endif
+
+#ifndef FILES_DEST_DIR
+#error "FILES_DEST_DIR not set. A directory where data will be written to for testing installing resources"
+#endif
+
+
+void TestResourceModel::initTestCase()
+{
+ ResourceTestHelper::initTestDb();
+ ResourceTestHelper::createDummyLoaderRegistry();
+
+ m_srcLocation = QString(FILES_DATA_DIR);
+ QVERIFY2(QDir(m_srcLocation).exists(), m_srcLocation.toUtf8());
+
+ m_dstLocation = QString(FILES_DEST_DIR);
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+
+ KConfigGroup cfg(KSharedConfig::openConfig(), "");
+ cfg.writeEntry(KisResourceLocator::resourceLocationKey, m_dstLocation);
+
+ m_locator = KisResourceLocator::instance();
+
+ if (!KisResourceCacheDb::initialize(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))) {
+ qWarning() << "Could not initialize KisResourceCacheDb on" << QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+ }
+ QVERIFY(KisResourceCacheDb::isValid());
+
+ KisResourceLocator::LocatorError r = m_locator->initialize(m_srcLocation);
+ if (!m_locator->errorMessages().isEmpty()) {
+ qDebug() << m_locator->errorMessages();
+ }
+
+ QVERIFY(r == KisResourceLocator::LocatorError::Ok);
+ QVERIFY(QDir(m_dstLocation).exists());
+}
+
+
+void TestResourceModel::testRowCount()
+{
+ QSqlQuery q;
+ QVERIFY(q.prepare("SELECT count(*)\n"
+ "FROM resources\n"
+ ", resource_types\n"
+ "WHERE resources.resource_type_id = resource_types.id\n"
+ "AND resource_types.name = :resource_type"));
+ q.bindValue(":resource_type", m_resourceType);
+ QVERIFY(q.exec());
+ q.first();
+ int rowCount = q.value(0).toInt();
+ QVERIFY(rowCount == 3);
+ KisResourceModel resourceModel(m_resourceType);
+ QCOMPARE(resourceModel.rowCount(), rowCount);
+}
+
+void TestResourceModel::testData()
+{
+ KisResourceModel resourceModel(m_resourceType);
+
+ QStringList resourceNames;
+
+ for (int i = 0; i < resourceModel.rowCount(); ++i) {
+ QVariant v = resourceModel.data(resourceModel.index(i, KisResourceModel::Name), Qt::DisplayRole);
+ resourceNames << v.toString();
+ }
+ QVERIFY(resourceNames.contains("test0.kpp"));
+ QVERIFY(resourceNames.contains("test1.kpp"));
+ QVERIFY(resourceNames.contains("test2.kpp"));
+}
+
+
+void TestResourceModel::testResourceForIndex()
+{
+ KisResourceModel resourceModel(m_resourceType);
+ KoResourceSP resource = resourceModel.resourceForIndex(resourceModel.index(0, 0));
+ QVERIFY(resource);
+ QVERIFY(resource->resourceId() > -1);
+}
+
+void TestResourceModel::testIndexFromResource()
+{
+ KisResourceModel resourceModel(m_resourceType);
+ KoResourceSP resource = resourceModel.resourceForIndex(resourceModel.index(1, 0));
+ QModelIndex idx = resourceModel.indexFromResource(resource);
+ QVERIFY(idx.row() == 1);
+ QVERIFY(idx.column() == 0);
+}
+
+void TestResourceModel::testRemoveResourceByIndex()
+{
+ KisResourceModel resourceModel(m_resourceType);
+ int resourceCount = resourceModel.rowCount();
+ KoResourceSP resource = resourceModel.resourceForIndex(resourceModel.index(1, 0));
+ bool r = resourceModel.removeResource(resourceModel.index(1, 0));
+ QVERIFY(r);
+ QCOMPARE(resourceCount - 1, resourceModel.rowCount());
+ QVERIFY(!resourceModel.indexFromResource(resource).isValid());
+
+}
+
+void TestResourceModel::testRemoveResource()
+{
+ KisResourceModel resourceModel(m_resourceType);
+ int resourceCount = resourceModel.rowCount();
+ KoResourceSP resource = resourceModel.resourceForIndex(resourceModel.index(1, 0));
+ QVERIFY(resource);
+
+ bool r = resourceModel.removeResource(resource);
+ QVERIFY(r);
+ QCOMPARE(resourceCount - 1, resourceModel.rowCount());
+
+ QVERIFY(!resourceModel.indexFromResource(resource).isValid());
+}
+
+void TestResourceModel::testImportResourceFile()
+{
+ KisResourceModel resourceModel(m_resourceType);
+
+ QTemporaryFile f(QDir::tempPath() + "/testresourcemodel-testimportresourcefile-XXXXXX.kpp");
+ f.open();
+ f.write("0");
+ f.close();
+
+ int resourceCount = resourceModel.rowCount();
+ bool r = resourceModel.importResourceFile(f.fileName());
+ QVERIFY(r);
+ QCOMPARE(resourceCount + 1, resourceModel.rowCount());
+}
+
+void TestResourceModel::testAddResource()
+{
+ KisResourceModel resourceModel(m_resourceType);
+ int resourceCount = resourceModel.rowCount();
+ KoResourceSP resource(new DummyResource("dummy.kpp"));
+ resource->setValid(true);
+ bool r = resourceModel.addResource(resource);
+ QVERIFY(r);
+ QCOMPARE(resourceCount + 1, resourceModel.rowCount());
+}
+
+void TestResourceModel::testAddTemporaryResource()
+{
+ KisResourceModel resourceModel(m_resourceType);
+ int resourceCount = resourceModel.rowCount();
+ KoResourceSP resource(new DummyResource("dummy.kpp"));
+ resource->setValid(true);
+ bool r = resourceModel.addResource(resource, "memory");
+ QVERIFY(r);
+ QCOMPARE(resourceCount + 1, resourceModel.rowCount());
+}
+
+void TestResourceModel::testUpdateResource()
+{
+ int resourceId;
+ {
+ KisResourceModel resourceModel(m_resourceType);
+ KoResourceSP resource = resourceModel.resourceForIndex(resourceModel.index(0, 0));
+ QVERIFY(resource);
+ QVERIFY(resource.dynamicCast<DummyResource>()->something().isEmpty());
+ resource.dynamicCast<DummyResource>()->setSomething("It's changed");
+ resourceId = resource->resourceId();
+ bool r = resourceModel.updateResource(resource);
+ QVERIFY(r);
+ }
+
+ {
+ // Check the resource itself
+ KisResourceLocator::instance()->purge();
+ KoResourceSP resource = KisResourceLocator::instance()->resourceForId(resourceId);
+
+ QVERIFY(resource);
+ QVERIFY(!resource.dynamicCast<DummyResource>()->something().isEmpty());
+ QVERIFY(resource->resourceId() == resourceId);
+
+ // Check the versions in the database
+ QSqlQuery q;
+ QVERIFY(q.prepare("SELECT count(*)\n"
+ "FROM versioned_resources\n"
+ "WHERE resource_id = :resource_id\n"));
+ q.bindValue(":resource_id", resourceId);
+ QVERIFY(q.exec());
+ q.first();
+ int rowCount = q.value(0).toInt();
+ QCOMPARE(rowCount, 2);
+ }
+}
+
+void TestResourceModel::testResourceForId()
+{
+ KisResourceModel resourceModel(m_resourceType);
+ KoResourceSP resource = resourceModel.resourceForIndex(resourceModel.index(0, 0));
+ QVERIFY(!resource.isNull());
+ KoResourceSP resource2 = resourceModel.resourceForId(resource->resourceId());
+ QVERIFY(!resource2.isNull());
+ QCOMPARE(resource, resource2);
+}
+
+void TestResourceModel::testResourceForName()
+{
+ KisResourceModel resourceModel(m_resourceType);
+ KoResourceSP resource = resourceModel.resourceForIndex(resourceModel.index(0, 0));
+ QVERIFY(!resource.isNull());
+ KoResourceSP resource2 = resourceModel.resourceForName(resource->name());
+ QVERIFY(!resource2.isNull());
+ QCOMPARE(resource, resource2);
+}
+
+void TestResourceModel::testResourceForFileName()
+{
+ KisResourceModel resourceModel(m_resourceType);
+ KoResourceSP resource = resourceModel.resourceForIndex(resourceModel.index(0, 0));
+ QVERIFY(!resource.isNull());
+ KoResourceSP resource2 = resourceModel.resourceForFilename(resource->filename());
+ QVERIFY(!resource2.isNull());
+ QCOMPARE(resource, resource2);
+}
+
+void TestResourceModel::testResourceForMD5()
+{
+ KisResourceModel resourceModel(m_resourceType);
+ KoResourceSP resource = resourceModel.resourceForIndex(resourceModel.index(0, 0));
+ QVERIFY(!resource.isNull());
+ KoResourceSP resource2 = resourceModel.resourceForMD5(resource->md5());
+ QVERIFY(!resource2.isNull());
+ QCOMPARE(resource->md5(), resource2->md5());
+}
+
+void TestResourceModel::testRenameResource()
+{
+ KisResourceModel resourceModel(m_resourceType);
+
+ KoResourceSP resource = resourceModel.resourceForIndex(resourceModel.index(1, 0));
+ QVERIFY(!resource.isNull());
+ const QString name = resource->name();
+ bool r = resourceModel.renameResource(resource, "A New Name");
+ QVERIFY(r);
+ QSqlQuery q;
+ if (!q.prepare("SELECT name\n"
+ "FROM resources\n"
+ "WHERE id = :resource_id\n")) {
+ qWarning() << "Could not prepare testRenameResource Query" << q.lastError();
+ }
+
+ q.bindValue(":resource_id", resource->resourceId());
+
+ if (!q.exec()) {
+ qWarning() << "Could not execute testRenameResource Query" << q.lastError();
+ }
+
+ q.first();
+ QString newName = q.value(0).toString();
+ QVERIFY(name != newName);
+ QCOMPARE("A New Name", newName);
+}
+
+void TestResourceModel::cleanupTestCase()
+{
+ ResourceTestHelper::rmTestDb();
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+}
+
+
+QTEST_MAIN(TestResourceModel)
+
diff --git a/libs/resources/tests/TestResourceModel.h b/libs/resources/tests/TestResourceModel.h
new file mode 100644
index 0000000000..b899fab411
--- /dev/null
+++ b/libs/resources/tests/TestResourceModel.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018 boud <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 TESTRESOURCEMODEL_H
+#define TESTRESOURCEMODEL_H
+
+#include <QObject>
+#include <QtSql>
+
+#include "KisResourceTypes.h"
+
+class KisResourceLocator;
+class TestResourceModel : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+ void testRowCount();
+ void testData();
+ void testResourceForIndex();
+ void testIndexFromResource();
+ void testRemoveResourceByIndex();
+ void testRemoveResource();
+ void testImportResourceFile();
+ void testAddResource();
+ void testAddTemporaryResource();
+ void testUpdateResource();
+ void testResourceForId();
+ void testResourceForName();
+ void testResourceForFileName();
+ void testResourceForMD5();
+ void testRenameResource();
+ void cleanupTestCase();
+private:
+
+ QString m_srcLocation;
+ QString m_dstLocation;
+
+ KisResourceLocator *m_locator;
+ const QString m_resourceType = ResourceType::PaintOpPresets;
+
+};
+
+#endif
diff --git a/libs/resources/tests/TestResourceSearchBoxFilter.cpp b/libs/resources/tests/TestResourceSearchBoxFilter.cpp
new file mode 100644
index 0000000000..caf2f5b820
--- /dev/null
+++ b/libs/resources/tests/TestResourceSearchBoxFilter.cpp
@@ -0,0 +1,165 @@
+/* This file is part of the Krita project
+
+ Copyright (c) 2019 Agata Cacko <cacko.azh@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 "TestResourceSearchBoxFilter.h"
+
+
+#include <QTest>
+
+
+TestResourceSearchBoxFilter::TestResourceSearchBoxFilter()
+{
+}
+
+
+bool TestResourceSearchBoxFilter::filterMatches(QString resourceName, QString filterText)
+{
+ KisResourceSearchBoxFilter filter;
+ filter.setFilter(filterText);
+ return filter.matchesResource(resourceName);
+}
+
+void addNameDataColumns()
+{
+ QTest::addColumn<QString>("resourceName");
+ QTest::addColumn<QString>("filter");
+ QTest::addColumn<bool>("matches");
+}
+
+void addNameData(QString name, QString resourceName, QString filter, bool matches)
+{
+ QTest::newRow(name.toStdString().c_str()) << resourceName << filter << matches;
+}
+
+void TestResourceSearchBoxFilter::runNameTest()
+{
+ QFETCH(QString, resourceName);
+ QFETCH(QString, filter);
+ QFETCH(bool, matches);
+ QCOMPARE(filterMatches(resourceName, filter), matches);
+}
+
+void TestResourceSearchBoxFilter::testOnePartialName_data()
+{
+ addNameDataColumns();
+ addNameData("1", "Nanana", "na", true);
+ addNameData("2", "Nanana", "nam", false);
+ addNameData("3", "Nanana", "Nanana", true);
+ addNameData("4", "Nanana", "Nananam", false);
+ addNameData("5", "f)_Bristle-4_Glaze", "Bristle", true);
+
+ addNameData("6", "Nanana", "!na", false);
+ addNameData("7", "Nanana", "!nam", true);
+ addNameData("8", "Nanana", "!Nanana", false);
+ addNameData("9", "Nanana", "!Nananam", true);
+ addNameData("10", "f)_Bristle-4_Glaze", "!Bristle", false);
+
+}
+
+void TestResourceSearchBoxFilter::testOnePartialName()
+{
+ runNameTest();
+
+}
+
+void TestResourceSearchBoxFilter::testMultiplePartialNames_data()
+{
+ addNameDataColumns();
+ addNameData("1", "Nanana", "na,nan", true);
+ addNameData("2", "Bristle", "Bristl,Na", false);
+ addNameData("3", "Bristle", "Bri,stle", true);
+ addNameData("4", "Bristle", "Glaze,tle", false);
+ addNameData("5", "f)_Bristle-4_Glaze", "Bristle,Glaze", true);
+
+ addNameData("6", "Nanana", "!na,!nan", false);
+ addNameData("7", "Bristle", "!Bristl,!Na", false);
+ addNameData("8", "Bristle", "!Bri,!stle", false);
+ addNameData("9", "Bristle", "!Glaze,!tle", false);
+ addNameData("10", "f)_Bristle-4_Glaze", "!Bristle,!Glaze", false);
+ addNameData("11", "Bristle", "!na,!nan", true);
+}
+
+void TestResourceSearchBoxFilter::testMultiplePartialNames()
+{
+ runNameTest();
+}
+
+void TestResourceSearchBoxFilter::testOneExactMatch_data()
+{
+ addNameDataColumns();
+ addNameData("1", "Resource1", "\"Resource1\"", true);
+ addNameData("2", "Brush2", "\"nam\"", false);
+ addNameData("3", "Preset3", "\"Preset3\"", true);
+ addNameData("4", "Nanana", "\"Nananam\"", false);
+ addNameData("5", "f)_Bristle-4_Glaze", "\"f)_Bristle-4_Glaze\"", true);
+
+ addNameData("6", "Resource1", "!\"Resource1\"", false);
+ addNameData("7", "Brush2", "!\"nam\"", true);
+ addNameData("8", "Preset3", "!\"Preset3\"", false);
+ addNameData("9", "Nanana", "!\"Nananam\"", true);
+ addNameData("10", "f)_Bristle-4_Glaze", "!\"f)_Bristle-4_Glaze\"", false);
+}
+
+void TestResourceSearchBoxFilter::testOneExactMatch()
+{
+ runNameTest();
+}
+
+void TestResourceSearchBoxFilter::testMultipleExactMatches_data()
+{
+ addNameDataColumns();
+ addNameData("1", "Nanana", "\"Nanana\",\"Nanana\"", true);
+ // needs to ask for the expected behaviour first
+ /* addNameData("2", "Nanana", "\"Nanana\",\"Glaze\"", false); */
+
+ addNameData("3", "Nanana", "!\"Nanana\",!\"Nanana\"", false);
+ addNameData("4", "Nanana", "!\"Nanana\",\"Nanana\"", false);
+ addNameData("5", "Nanana", "!\"Nanana\",!\"Glaze\"", false);
+ addNameData("6", "Bristle", "!\"Nanana\",!\"Glaze\"", true);
+
+}
+
+void TestResourceSearchBoxFilter::testMultipleExactMatches()
+{
+ runNameTest();
+}
+
+void TestResourceSearchBoxFilter::testOneTag_data()
+{
+ // tag filtering not implemented yet
+}
+
+void TestResourceSearchBoxFilter::testOneTag()
+{
+ // tag filtering not implemented yet
+}
+
+void TestResourceSearchBoxFilter::testMultipleTags_data()
+{
+ // tag filtering not implemented yet
+}
+
+void TestResourceSearchBoxFilter::testMultipleTags()
+{
+ // tag filtering not implemented yet
+}
+
+
+
+
+QTEST_MAIN(TestResourceSearchBoxFilter)
diff --git a/libs/resources/tests/TestResourceSearchBoxFilter.h b/libs/resources/tests/TestResourceSearchBoxFilter.h
new file mode 100644
index 0000000000..fdb2124d35
--- /dev/null
+++ b/libs/resources/tests/TestResourceSearchBoxFilter.h
@@ -0,0 +1,60 @@
+/* This file is part of the Krita project
+
+ Copyright (c) 2019 Agata Cacko <cacko.azh@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 TEST_RESOURCE_SEARCH_BOX_FILTER_H
+#define TEST_RESOURCE_SEARCH_BOX_FILTER_H
+
+#include <QObject>
+#include <QScopedPointer>
+
+#include <KisResourceSearchBoxFilter.h>
+
+class TestResourceSearchBoxFilter : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ TestResourceSearchBoxFilter();
+
+private Q_SLOTS:
+ void testOnePartialName_data();
+ void testOnePartialName();
+
+ void testMultiplePartialNames_data();
+ void testMultiplePartialNames();
+
+ void testOneExactMatch_data();
+ void testOneExactMatch();
+
+ void testMultipleExactMatches_data();
+ void testMultipleExactMatches();
+
+ void testOneTag_data();
+ void testOneTag();
+
+ void testMultipleTags_data();
+ void testMultipleTags();
+
+private:
+ bool filterMatches(QString resourceName, QString filter);
+ void runNameTest();
+
+};
+
+#endif // TESTRESOURCESEARCHBOXFILTER_H
diff --git a/libs/resources/tests/TestResourceStorage.cpp b/libs/resources/tests/TestResourceStorage.cpp
new file mode 100644
index 0000000000..2d3b3d1b60
--- /dev/null
+++ b/libs/resources/tests/TestResourceStorage.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#include "TestResourceStorage.h"
+#include <QTest>
+
+#include <KisResourceStorage.h>
+
+#ifndef FILES_DATA_DIR
+#error "FILES_DATA_DIR not set. A directory with the data used for testing installing resources"
+#endif
+
+
+void TestResourceStorage ::testStorage()
+{
+ {
+ KisResourceStorage storage(QString(FILES_DATA_DIR));
+ QVERIFY(storage.type() == KisResourceStorage::StorageType::Folder);
+ QVERIFY(storage.valid());
+ }
+
+ {
+ KisResourceStorage storage(QString(FILES_DATA_DIR) + "/bundles/test1.bundle");
+ QVERIFY(storage.type() == KisResourceStorage::StorageType::Bundle);
+ QVERIFY(storage.valid());
+ }
+
+ {
+ KisResourceStorage storage(QString(FILES_DATA_DIR) + "/bundles/test2.bundle");
+ QVERIFY(storage.type() == KisResourceStorage::StorageType::Bundle);
+ QVERIFY(storage.valid());
+ }
+
+ {
+ KisResourceStorage storage("");
+ QVERIFY(storage.type() == KisResourceStorage::StorageType::Unknown);
+ QVERIFY(!storage.valid());
+ }
+
+
+}
+
+QTEST_MAIN(TestResourceStorage)
+
diff --git a/libs/resources/tests/TestResourceStorage.h b/libs/resources/tests/TestResourceStorage.h
new file mode 100644
index 0000000000..b1d4aef20d
--- /dev/null
+++ b/libs/resources/tests/TestResourceStorage.h
@@ -0,0 +1,33 @@
+/*
+ * 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 TESTRESOURCESTORAGE_H
+#define TESTRESOURCESTORAGE_H
+
+#include <QObject>
+
+class TestResourceStorage : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void testStorage();
+private:
+};
+
+#endif
diff --git a/libs/resources/tests/TestResourceTypeModel.cpp b/libs/resources/tests/TestResourceTypeModel.cpp
new file mode 100644
index 0000000000..28c6ba8b00
--- /dev/null
+++ b/libs/resources/tests/TestResourceTypeModel.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2018 boud <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 "TestResourceTypeModel.h"
+
+#include <QTest>
+#include <QStandardPaths>
+#include <QDir>
+#include <QVersionNumber>
+#include <QDirIterator>
+#include <QSqlError>
+#include <QSqlQuery>
+#include <QtSql>
+#include <QModelIndex>
+
+#include <kconfig.h>
+#include <kconfiggroup.h>
+#include <ksharedconfig.h>
+
+#include <KisResourceCacheDb.h>
+#include <KisResourceLocator.h>
+#include <KisResourceLoaderRegistry.h>
+#include <KisResourceTypeModel.h>
+
+#include <DummyResource.h>
+#include <ResourceTestHelper.h>
+
+
+#ifndef FILES_DATA_DIR
+#error "FILES_DATA_DIR not set. A directory with the data used for testing installing resources"
+#endif
+
+#ifndef FILES_DEST_DIR
+#error "FILES_DEST_DIR not set. A directory where data will be written to for testing installing resources"
+#endif
+
+
+void TestResourceTypeModel::initTestCase()
+{
+ ResourceTestHelper::initTestDb();
+ ResourceTestHelper::createDummyLoaderRegistry();
+
+ m_srcLocation = QString(FILES_DATA_DIR);
+ QVERIFY2(QDir(m_srcLocation).exists(), m_srcLocation.toUtf8());
+
+ m_dstLocation = QString(FILES_DEST_DIR);
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+
+ KConfigGroup cfg(KSharedConfig::openConfig(), "");
+ cfg.writeEntry(KisResourceLocator::resourceLocationKey, m_dstLocation);
+
+ m_locator = KisResourceLocator::instance();
+
+ if (!KisResourceCacheDb::initialize(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))) {
+ qDebug() << "Could not initialize KisResourceCacheDb on" << QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+ }
+ QVERIFY(KisResourceCacheDb::isValid());
+
+ KisResourceLocator::LocatorError r = m_locator->initialize(m_srcLocation);
+ if (!m_locator->errorMessages().isEmpty()) qDebug() << m_locator->errorMessages();
+
+ QVERIFY(r == KisResourceLocator::LocatorError::Ok);
+ QVERIFY(QDir(m_dstLocation).exists());
+}
+
+
+void TestResourceTypeModel::testRowCount()
+{
+ QSqlQuery q;
+ QVERIFY(q.prepare("SELECT count(*)\n"
+ "FROM resource_types"));
+ QVERIFY(q.exec());
+ q.first();
+ int rowCount = q.value(0).toInt();
+ QCOMPARE(rowCount, KisResourceLoaderRegistry::instance()->resourceTypes().count());
+
+ KisResourceTypeModel typeModel;
+ QCOMPARE(typeModel.rowCount(), rowCount);
+}
+
+void TestResourceTypeModel::testData()
+{
+ KisResourceTypeModel typeModel;
+ for(int i = 0; i < typeModel.rowCount(); ++i) {
+
+ QModelIndex idx = typeModel.index(0, KisResourceTypeModel::ResourceType);
+// qDebug() << "test" << idx.data(Qt::DisplayRole)
+// << idx.data(Qt::UserRole + KisResourceTypeModel::Id)
+// << idx.data(Qt::UserRole + KisResourceTypeModel::ResourceType)
+// << idx.data(Qt::UserRole + KisResourceTypeModel::Name);
+
+ QVERIFY(KisResourceLoaderRegistry::instance()->resourceTypeLoaders(idx.data(Qt::DisplayRole).toString()).size() > 0);
+
+ QVector<KisResourceLoaderBase *> loaders = KisResourceLoaderRegistry::instance()->resourceTypeLoaders(idx.data(Qt::UserRole + KisResourceTypeModel::ResourceType).toString());
+
+ QVERIFY(loaders.size() > 0);
+
+ auto loader = loaders.first();
+ QCOMPARE(loader->name(), idx.data(Qt::UserRole + KisResourceTypeModel::Name).toString());
+ }
+}
+
+
+void TestResourceTypeModel::cleanupTestCase()
+{
+ ResourceTestHelper::rmTestDb();
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+}
+
+
+
+
+QTEST_MAIN(TestResourceTypeModel)
+
diff --git a/libs/resources/tests/TestResourceTypeModel.h b/libs/resources/tests/TestResourceTypeModel.h
new file mode 100644
index 0000000000..1538c771e9
--- /dev/null
+++ b/libs/resources/tests/TestResourceTypeModel.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018 boud <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 TESTRESOURCETYPEMODEL_H
+#define TESTRESOURCETYPEMODEL_H
+
+#include <QObject>
+class KisResourceLocator;
+
+class TestResourceTypeModel : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+ void testRowCount();
+ void testData();
+ void cleanupTestCase();
+private:
+
+ QString m_srcLocation;
+ QString m_dstLocation;
+ KisResourceLocator *m_locator;
+};
+
+#endif
diff --git a/libs/resources/tests/TestStorageModel.cpp b/libs/resources/tests/TestStorageModel.cpp
new file mode 100644
index 0000000000..da12267ea3
--- /dev/null
+++ b/libs/resources/tests/TestStorageModel.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2018 boud <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 "TestStorageModel.h"
+
+#include <QTest>
+#include <QStandardPaths>
+#include <QDir>
+#include <QVersionNumber>
+#include <QDirIterator>
+#include <QSqlError>
+#include <QSqlQuery>
+#include <QtSql>
+#include <QModelIndex>
+
+#include <kconfig.h>
+#include <kconfiggroup.h>
+#include <ksharedconfig.h>
+
+#include <KisResourceCacheDb.h>
+#include <KisResourceLocator.h>
+#include <KisResourceLoaderRegistry.h>
+#include <KisStorageModel.h>
+
+#include <DummyResource.h>
+#include <ResourceTestHelper.h>
+
+
+#ifndef FILES_DATA_DIR
+#error "FILES_DATA_DIR not set. A directory with the data used for testing installing resources"
+#endif
+
+#ifndef FILES_DEST_DIR
+#error "FILES_DEST_DIR not set. A directory where data will be written to for testing installing resources"
+#endif
+
+
+void TestStorageModel::initTestCase()
+{
+ ResourceTestHelper::initTestDb();
+ ResourceTestHelper::createDummyLoaderRegistry();
+
+ m_srcLocation = QString(FILES_DATA_DIR);
+ QVERIFY2(QDir(m_srcLocation).exists(), m_srcLocation.toUtf8());
+
+ m_dstLocation = QString(FILES_DEST_DIR);
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+
+ KConfigGroup cfg(KSharedConfig::openConfig(), "");
+ cfg.writeEntry(KisResourceLocator::resourceLocationKey, m_dstLocation);
+
+ m_locator = KisResourceLocator::instance();
+
+ if (!KisResourceCacheDb::initialize(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))) {
+ qDebug() << "Could not initialize KisResourceCacheDb on" << QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+ }
+ QVERIFY(KisResourceCacheDb::isValid());
+
+ KisResourceLocator::LocatorError r = m_locator->initialize(m_srcLocation);
+ if (!m_locator->errorMessages().isEmpty()) qDebug() << m_locator->errorMessages();
+
+ QVERIFY(r == KisResourceLocator::LocatorError::Ok);
+ QVERIFY(QDir(m_dstLocation).exists());
+}
+
+
+void TestStorageModel::testRowCount()
+{
+ QSqlQuery q;
+ QVERIFY(q.prepare("SELECT count(*)\n"
+ "FROM storages"));
+ QVERIFY(q.exec());
+ q.first();
+ int rowCount = q.value(0).toInt();
+
+ KisStorageModel storageModel;
+ QCOMPARE(storageModel.rowCount(), rowCount);
+}
+
+void TestStorageModel::testSetActive()
+{
+ KisStorageModel storageModel;
+ for (int i = 0; i < storageModel.rowCount(); ++i) {
+ QModelIndex idx = storageModel.index(i, 0);
+
+ storageModel.setData(idx, QVariant(true), Qt::CheckStateRole);
+ QVERIFY(idx.data(Qt::UserRole + KisStorageModel::Active).toBool() == true);
+
+ storageModel.setData(idx, QVariant(false), Qt::CheckStateRole);
+
+ idx = storageModel.index(i, 0);
+ QVERIFY(idx.data(Qt::UserRole + KisStorageModel::Active).toBool() == false);
+
+ }
+}
+
+
+void TestStorageModel::cleanupTestCase()
+{
+ ResourceTestHelper::rmTestDb();
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+}
+
+void TestStorageModel::testMetaData()
+{
+ KisStorageModel storageModel;
+ int rowCount = storageModel.rowCount();
+
+ KisResourceStorageSP storage {new KisResourceStorage("My Named Memory Storage")};
+ KisResourceLocator::instance()->addStorage("My Named Memory Storage", storage);
+ storage->setMetaData(KisResourceStorage::s_meta_name, "My Named Memory Storage");
+ storageModel.resetQuery();
+
+ QVERIFY(storageModel.rowCount() > rowCount);
+
+ QModelIndex idx;
+ for (int row = 0; row < storageModel.rowCount(); ++row) {
+ idx = storageModel.index(row, 7);
+ KisResourceStorageSP st = storageModel.storageForIndex(idx);
+ QVERIFY(st);
+ if (st == storage) {
+ break;
+ }
+ }
+
+ QVERIFY(idx.isValid());
+
+ QString displayName = storageModel.data(idx, Qt::DisplayRole).toString();
+ QCOMPARE("My Named Memory Storage", displayName);
+
+ idx = storageModel.index(idx.row(), 0);
+ QMap<QString, QVariant> metadata = storageModel.data(idx, Qt::UserRole + KisStorageModel::MetaData).toMap();
+ QVERIFY(metadata.contains(KisResourceStorage::s_meta_name));
+ QVERIFY(metadata[KisResourceStorage::s_meta_name] == "My Named Memory Storage");
+}
+
+
+
+
+QTEST_MAIN(TestStorageModel)
+
diff --git a/libs/resources/tests/TestStorageModel.h b/libs/resources/tests/TestStorageModel.h
new file mode 100644
index 0000000000..65ec9ca5b2
--- /dev/null
+++ b/libs/resources/tests/TestStorageModel.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018 boud <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 TESTRESOURCETYPEMODEL_H
+#define TESTRESOURCETYPEMODEL_H
+
+#include <QObject>
+class KisResourceLocator;
+
+class TestStorageModel : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+ void testRowCount();
+ void testSetActive();
+ void cleanupTestCase();
+ void testMetaData();
+
+private:
+
+ QString m_srcLocation;
+ QString m_dstLocation;
+ KisResourceLocator *m_locator;
+};
+
+#endif
diff --git a/libs/resources/tests/TestTag.cpp b/libs/resources/tests/TestTag.cpp
new file mode 100644
index 0000000000..03cf49153f
--- /dev/null
+++ b/libs/resources/tests/TestTag.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+#include "TestTag.h"
+#include <QTest>
+#include <QBuffer>
+
+#include <KisTag.h>
+#include <KisResourceLoader.h>
+#include <KoResource.h>
+#include <KisResourceLoaderRegistry.h>
+
+#ifndef FILES_DATA_DIR
+#error "FILES_DATA_DIR not set. A directory with the data used for testing installing resources"
+#endif
+
+void TestTag::testLoadTag()
+{
+ KisTag tagLoader;
+ QFile f(QString(FILES_DATA_DIR) + "paintoppresets/test.tag");
+
+ QVERIFY(f.exists());
+
+ f.open(QFile::ReadOnly);
+ QVERIFY(f.isOpen());
+
+ bool r = tagLoader.load(f);
+
+ f.close();
+
+ QVERIFY(r);
+ QVERIFY(tagLoader.name() == "* Favorites");
+ QVERIFY(tagLoader.comment() == "Your favorite brush presets");
+ QVERIFY(tagLoader.url() == "* Favorites");
+
+ QLocale nl(QLocale::Dutch, QLocale::Netherlands);
+ QLocale::setDefault(nl);
+
+ f.open(QFile::ReadOnly);
+ QVERIFY(f.isOpen());
+ r = tagLoader.load(f);
+ f.close();
+
+ QVERIFY(r);
+ QVERIFY(tagLoader.name() == "* Favorieten");
+ QVERIFY(tagLoader.comment() == "Jouw favoriete penseel presets");
+ QVERIFY(tagLoader.url() == "* Favorites");
+
+}
+
+void TestTag::testSaveTag()
+{
+ KisTag tag1;
+ QFile f(QString(FILES_DATA_DIR) + "paintoppresets/test.tag");
+
+ QVERIFY(f.exists());
+
+ f.open(QFile::ReadOnly);
+ QVERIFY(f.isOpen());
+
+ bool r = tag1.load(f);
+ QVERIFY(r);
+
+ tag1.setName(QString("Test"));
+
+ QByteArray ba;
+ QBuffer buf(&ba);
+ buf.open(QBuffer::WriteOnly);
+
+ tag1.save(buf);
+
+ buf.close();
+ buf.open(QBuffer::ReadOnly);
+ KisTag tag2;
+ tag2.load(buf);
+ QVERIFY(tag2.url() == tag1.url());
+ QVERIFY(tag2.name() == tag1.name());
+ QVERIFY(tag2.comment() == tag1.comment());
+ QVERIFY(tag2.defaultResources() == tag1.defaultResources());
+
+}
+
+QTEST_MAIN(TestTag)
+
diff --git a/libs/resources/tests/TestTag.h b/libs/resources/tests/TestTag.h
new file mode 100644
index 0000000000..afe0d4bf79
--- /dev/null
+++ b/libs/resources/tests/TestTag.h
@@ -0,0 +1,34 @@
+/*
+ * 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 TESTTAG_H
+#define TESTTAG_H
+
+#include <QObject>
+
+class TestTag: public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void testLoadTag();
+ void testSaveTag();
+private:
+};
+
+#endif
diff --git a/libs/resources/tests/TestTagFilterResourceProxyModel.cpp b/libs/resources/tests/TestTagFilterResourceProxyModel.cpp
new file mode 100644
index 0000000000..8cbb7bebcd
--- /dev/null
+++ b/libs/resources/tests/TestTagFilterResourceProxyModel.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2019 boud <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 "TestTagFilterResourceProxyModel.h"
+
+#include <QTest>
+#include <QStandardPaths>
+#include <QDir>
+#include <QVersionNumber>
+#include <QDirIterator>
+#include <QSqlError>
+#include <QSqlQuery>
+
+#include <kconfig.h>
+#include <kconfiggroup.h>
+#include <ksharedconfig.h>
+
+#include <KisResourceCacheDb.h>
+#include <KisResourceLocator.h>
+#include <KisResourceLoaderRegistry.h>
+#include <KisResourceModel.h>
+#include <KisTagFilterResourceProxyModel.h>
+#include <KisResourceModelProvider.h>
+
+#include <DummyResource.h>
+#include <ResourceTestHelper.h>
+
+#ifndef FILES_DATA_DIR
+#error "FILES_DATA_DIR not set. A directory with the data used for testing installing resources"
+#endif
+
+#ifndef FILES_DEST_DIR
+#error "FILES_DEST_DIR not set. A directory where data will be written to for testing installing resources"
+#endif
+
+
+void TestTagFilterResourceProxyModel::initTestCase()
+{
+ ResourceTestHelper::initTestDb();
+ ResourceTestHelper::createDummyLoaderRegistry();
+
+ m_srcLocation = QString(FILES_DATA_DIR);
+ QVERIFY2(QDir(m_srcLocation).exists(), m_srcLocation.toUtf8());
+
+ m_dstLocation = QString(FILES_DEST_DIR);
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+
+ KConfigGroup cfg(KSharedConfig::openConfig(), "");
+ cfg.writeEntry(KisResourceLocator::resourceLocationKey, m_dstLocation);
+
+ m_locator = KisResourceLocator::instance();
+
+ if (!KisResourceCacheDb::initialize(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))) {
+ qDebug() << "Could not initialize KisResourceCacheDb on" << QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+ }
+ QVERIFY(KisResourceCacheDb::isValid());
+
+ KisResourceLocator::LocatorError r = m_locator->initialize(m_srcLocation);
+ if (!m_locator->errorMessages().isEmpty()) qDebug() << m_locator->errorMessages();
+
+ QVERIFY(r == KisResourceLocator::LocatorError::Ok);
+ QVERIFY(QDir(m_dstLocation).exists());
+}
+
+
+void TestTagFilterResourceProxyModel::testRowCount()
+{
+ QSqlQuery q;
+ QVERIFY(q.prepare("SELECT count(*)\n"
+ "FROM resources\n"
+ ", resource_types\n"
+ "WHERE resources.resource_type_id = resource_types.id\n"
+ "AND resource_types.name = :resource_type"));
+ q.bindValue(":resource_type", resourceType);
+ QVERIFY(q.exec());
+ q.first();
+ int rowCount = q.value(0).toInt();
+ QVERIFY(rowCount == 3);
+
+ KisResourceModel *resourceModel = KisResourceModelProvider::resourceModel(resourceType);
+ KisTagFilterResourceProxyModel proxyModel;
+ proxyModel.setSourceModel(resourceModel);
+
+ QCOMPARE(proxyModel.rowCount(), rowCount);
+}
+
+void TestTagFilterResourceProxyModel::testData()
+{
+ KisResourceModel *resourceModel = KisResourceModelProvider::resourceModel(resourceType);
+ KisTagFilterResourceProxyModel proxyModel;
+ proxyModel.setSourceModel(resourceModel);
+
+ QStringList names = QStringList() << "test0.kpp"
+ << "test1.kpp"
+ << "test2.kpp";
+ for (int i = 0; i < proxyModel.rowCount(); ++i) {
+ QVariant v = resourceModel->data(proxyModel.mapToSource(proxyModel.index(i, 0)), Qt::UserRole + KisResourceModel::Name);
+ QVERIFY(names.contains(v.toString()));
+ }
+}
+
+
+void TestTagFilterResourceProxyModel::testResource()
+{
+ KisResourceModel *resourceModel = KisResourceModelProvider::resourceModel(resourceType);
+ KisTagFilterResourceProxyModel proxyModel;
+ proxyModel.setSourceModel(resourceModel);
+
+ KoResourceSP resource = resourceModel->resourceForIndex(proxyModel.mapToSource(proxyModel.index(0, 0)));
+ QVERIFY(resource);
+}
+
+
+void TestTagFilterResourceProxyModel::cleanupTestCase()
+{
+ ResourceTestHelper::rmTestDb();
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+}
+
+
+QTEST_MAIN(TestTagFilterResourceProxyModel)
+
diff --git a/libs/resources/tests/TestTagFilterResourceProxyModel.h b/libs/resources/tests/TestTagFilterResourceProxyModel.h
new file mode 100644
index 0000000000..6cf9e4f2b8
--- /dev/null
+++ b/libs/resources/tests/TestTagFilterResourceProxyModel.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 boud <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 TESTAGFILTERTRESOURCEPROXYMODEL_H
+#define TESTAGFILTERTRESOURCEPROXYMODEL_H
+
+#include <QObject>
+#include <QtSql>
+#include "KisResourceTypes.h"
+class KisResourceLocator;
+class TestTagFilterResourceProxyModel : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+ void testRowCount();
+ void testData();
+ void testResource();
+ void cleanupTestCase();
+private:
+
+ QString m_srcLocation;
+ QString m_dstLocation;
+
+ KisResourceLocator *m_locator;
+ const QString resourceType = ResourceType::PaintOpPresets;
+
+};
+
+#endif
diff --git a/libs/resources/tests/TestTagModel.cpp b/libs/resources/tests/TestTagModel.cpp
new file mode 100644
index 0000000000..cd14edc0ce
--- /dev/null
+++ b/libs/resources/tests/TestTagModel.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2018 boud <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 "TestTagModel.h"
+
+#include <QTest>
+#include <QStandardPaths>
+#include <QDir>
+#include <QVersionNumber>
+#include <QDirIterator>
+#include <QSqlError>
+#include <QSqlQuery>
+
+#include <kconfig.h>
+#include <kconfiggroup.h>
+#include <ksharedconfig.h>
+
+#include <KisResourceCacheDb.h>
+#include <KisResourceLocator.h>
+#include <KisResourceLoaderRegistry.h>
+#include <KisTagModel.h>
+
+#include <DummyResource.h>
+#include <ResourceTestHelper.h>
+
+
+#ifndef FILES_DATA_DIR
+#error "FILES_DATA_DIR not set. A directory with the data used for testing installing resources"
+#endif
+
+#ifndef FILES_DEST_DIR
+#error "FILES_DEST_DIR not set. A directory where data will be written to for testing installing resources"
+#endif
+
+
+void TestTagModel::initTestCase()
+{
+ ResourceTestHelper::initTestDb();
+ ResourceTestHelper::createDummyLoaderRegistry();
+
+ m_srcLocation = QString(FILES_DATA_DIR);
+ QVERIFY2(QDir(m_srcLocation).exists(), m_srcLocation.toUtf8());
+
+ m_dstLocation = QString(FILES_DEST_DIR);
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+
+ KConfigGroup cfg(KSharedConfig::openConfig(), "");
+ cfg.writeEntry(KisResourceLocator::resourceLocationKey, m_dstLocation);
+
+ m_locator = KisResourceLocator::instance();
+
+ if (!KisResourceCacheDb::initialize(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))) {
+ qDebug() << "Could not initialize KisResourceCacheDb on" << QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+ }
+ QVERIFY(KisResourceCacheDb::isValid());
+
+ KisResourceLocator::LocatorError r = m_locator->initialize(m_srcLocation);
+ if (!m_locator->errorMessages().isEmpty()) qDebug() << m_locator->errorMessages();
+
+ QVERIFY(r == KisResourceLocator::LocatorError::Ok);
+ QVERIFY(QDir(m_dstLocation).exists());
+}
+
+
+void TestTagModel::testRowCount()
+{
+ QSqlQuery q;
+ QVERIFY(q.prepare("SELECT count(*)\n"
+ "FROM tags\n"
+ ", resource_types\n"
+ "WHERE tags.resource_type_id = resource_types.id\n"
+ "AND resource_types.name = :resource_type"));
+ q.bindValue(":resource_type", resourceType);
+ QVERIFY(q.exec());
+ q.first();
+ int rowCount = q.value(0).toInt();
+ QCOMPARE(rowCount, 1);
+
+ KisTagModel tagModel(resourceType);
+ // There is always an "All" tag in the first row
+ QCOMPARE(tagModel.rowCount(), rowCount + 2);
+}
+
+void TestTagModel::testData()
+{
+ KisTagModel tagModel(resourceType);
+
+ QVariant v = tagModel.data(tagModel.index(0, 0), Qt::DisplayRole);
+ QCOMPARE(v.toString(), "All");
+
+ v = tagModel.data(tagModel.index(0, 0), Qt::UserRole + KisTagModel::Url);
+ QCOMPARE(v.toString(), "All");
+
+ v = tagModel.data(tagModel.index(2, 0), Qt::DisplayRole);
+ QCOMPARE(v.toString(), "* Favorites");
+
+ v = tagModel.data(tagModel.index(2, 0), Qt::UserRole + KisTagModel::Url);
+ QCOMPARE(v.toString(), "* Favorites");
+
+}
+
+
+void TestTagModel::cleanupTestCase()
+{
+ ResourceTestHelper::rmTestDb();
+ ResourceTestHelper::cleanDstLocation(m_dstLocation);
+}
+
+
+
+
+QTEST_MAIN(TestTagModel)
+
diff --git a/libs/resources/tests/TestTagModel.h b/libs/resources/tests/TestTagModel.h
new file mode 100644
index 0000000000..ac2111ba62
--- /dev/null
+++ b/libs/resources/tests/TestTagModel.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018 boud <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 TESTTAGMODEL_H
+#define TESTTAGMODEL_H
+
+#include <QObject>
+#include <QtSql>
+
+#include "KisResourceTypes.h"
+
+class KisResourceLocator;
+class TestTagModel : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+ void testRowCount();
+ void testData();
+ void cleanupTestCase();
+private:
+
+ QString m_srcLocation;
+ QString m_dstLocation;
+
+ KisResourceLocator *m_locator;
+ const QString resourceType = ResourceType::PaintOpPresets;
+
+};
+
+#endif
diff --git a/libs/resources/tests/data/brushes/test.gbr b/libs/resources/tests/data/brushes/test.gbr
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/libs/resources/tests/data/bundles/test1.bundle b/libs/resources/tests/data/bundles/test1.bundle
new file mode 100644
index 0000000000..14e33bc615
Binary files /dev/null and b/libs/resources/tests/data/bundles/test1.bundle differ
diff --git a/libs/resources/tests/data/bundles/test2.bundle b/libs/resources/tests/data/bundles/test2.bundle
new file mode 100644
index 0000000000..14e33bc615
Binary files /dev/null and b/libs/resources/tests/data/bundles/test2.bundle differ
diff --git a/libs/resources/tests/data/gradients/test.ggr b/libs/resources/tests/data/gradients/test.ggr
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/libs/resources/tests/data/paintoppresets/test.tag b/libs/resources/tests/data/paintoppresets/test.tag
new file mode 100644
index 0000000000..127a70539c
--- /dev/null
+++ b/libs/resources/tests/data/paintoppresets/test.tag
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Type=Tag
+Name=* Favorites
+Name[nl]=* Favorieten
+Comment=Your favorite brush presets
+Comment[nl]=Jouw favoriete penseel presets
+URL=* Favorites
+Default Resources=test0.kpp,test1.kpp
diff --git a/libs/resources/tests/data/paintoppresets/test0.kpp b/libs/resources/tests/data/paintoppresets/test0.kpp
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/libs/resources/tests/data/paintoppresets/test1.kpp b/libs/resources/tests/data/paintoppresets/test1.kpp
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/libs/resources/tests/data/paintoppresets/test2.kpp b/libs/resources/tests/data/paintoppresets/test2.kpp
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/libs/resources/tests/data/palettes/test.gpl b/libs/resources/tests/data/palettes/test.gpl
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/libs/resources/tests/data/symbols/test.svg b/libs/resources/tests/data/symbols/test.svg
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/libs/resources/tests/data/workspaces/test.workspace b/libs/resources/tests/data/workspaces/test.workspace
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/libs/resourcewidgets/CMakeLists.txt b/libs/resourcewidgets/CMakeLists.txt
new file mode 100644
index 0000000000..6f1628c123
--- /dev/null
+++ b/libs/resourcewidgets/CMakeLists.txt
@@ -0,0 +1,44 @@
+add_subdirectory(dbexplorer)
+
+set(kritaresourcewidgets_LIB_SRCS
+ KisIconToolTip.cpp
+ KisResourceItemChooserContextMenu.cpp
+ KisResourceItemChooser.cpp
+ KisResourceItemChooserSync.cpp
+ KisResourceItemDelegate.cpp
+ KisResourceItemListView.cpp
+ KisResourceItemView.cpp
+ KisTagChooserWidget.cpp
+ KisTagFilterWidget.cpp
+ KisTagToolButton.cpp
+ KisResourceTaggingManager.cpp
+ KisStorageChooserWidget.cpp
+)
+
+add_library(kritaresourcewidgets SHARED ${kritaresourcewidgets_LIB_SRCS})
+
+generate_export_header(kritaresourcewidgets BASE_NAME kritaresourcewidgets)
+
+target_link_libraries(kritaresourcewidgets
+ PUBLIC
+ Qt5::Core
+ Qt5::Widgets
+ PRIVATE
+ Qt5::Sql
+ kritaversion
+ kritaglobal
+ kritaplugin
+ kritastore
+ kritaresources
+ kritawidgetutils
+ KF5::ConfigCore
+ KF5::WidgetsAddons
+ KF5::CoreAddons
+ KF5::I18n
+)
+
+set_target_properties(kritaresourcewidgets PROPERTIES
+ VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
+)
+install(TARGETS kritaresourcewidgets ${INSTALL_TARGETS_DEFAULT_ARGS} )
+
diff --git a/libs/resourcewidgets/KisIconToolTip.cpp b/libs/resourcewidgets/KisIconToolTip.cpp
new file mode 100644
index 0000000000..f3d319ee24
--- /dev/null
+++ b/libs/resourcewidgets/KisIconToolTip.cpp
@@ -0,0 +1,76 @@
+/* This file is part of the KDE project
+ * Copyright (c) 1999 Carsten Pfeiffer (pfeiffer@kde.org)
+ * Copyright (c) 2002 Igor Jansen (rm@kde.org)
+ * Copyright (c) 2018 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 "KisIconToolTip.h"
+
+#include <QTextDocument>
+#include <QUrl>
+#include <QModelIndex>
+
+#include <KisResourceModel.h>
+
+#include <klocalizedstring.h>
+
+KisIconToolTip::KisIconToolTip()
+{
+}
+
+KisIconToolTip::~KisIconToolTip()
+{
+}
+
+QTextDocument *KisIconToolTip::createDocument(const QModelIndex &index)
+{
+ QTextDocument *doc = new QTextDocument(this);
+
+ QImage thumb = index.data(Qt::DecorationRole).value<QImage>();
+ if (thumb.isNull()) {
+ thumb = index.data(Qt::UserRole + KisResourceModel::Thumbnail).value<QImage>();
+ }
+ doc->addResource(QTextDocument::ImageResource, QUrl("data:thumbnail"), thumb);
+
+ QString name = index.data(Qt::DisplayRole).toString();
+ QString presetDisplayName = index.data(Qt::UserRole + KisResourceModel::Name).toString().replace("_", " ");
+ //This is to ensure that the other uses of this class don't get an empty string, while resource management should get a nice string.
+ if (!presetDisplayName.isEmpty()) {
+ name = presetDisplayName;
+ }
+
+ QString tags;
+ QString tagsData = index.data(Qt::UserRole + KisResourceModel::Tags).toStringList().join(", ");
+ if (tagsData.length() > 0) {
+ const QString list = QString("<ul style=\"list-style-type: none; margin: 0px;\">%1</ul> ").arg(tagsData);
+ tags = QString("<p><table><tr><td>%1:</td><td>%2</td></tr></table></p>").arg(i18n("Tags"), list);
+ }
+
+ const QString image = QString("<center><img src=\"data:thumbnail\"></center>");
+ const QString body = QString("<h3 align=\"center\">%1</h3>%2%3").arg(name, image, tags);
+ const QString html = QString("<html><body>%1</body></html>").arg(body);
+
+ doc->setHtml(html);
+
+ const int margin = 16;
+ doc->setTextWidth(qMin(doc->size().width() + 2 * margin, qreal(500.0)));
+ doc->setDocumentMargin(margin);
+ doc->setUseDesignMetrics(true);
+
+ return doc;
+}
diff --git a/libs/resourcewidgets/KisIconToolTip.h b/libs/resourcewidgets/KisIconToolTip.h
new file mode 100644
index 0000000000..63e13616d7
--- /dev/null
+++ b/libs/resourcewidgets/KisIconToolTip.h
@@ -0,0 +1,40 @@
+/* This file is part of the KDE project
+ * Copyright (c) 1999 Carsten Pfeiffer (pfeiffer@kde.org)
+ * Copyright (c) 2002 Igor Jansen (rm@kde.org)
+ * Copyright (c) 2018 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 KISICONTOOLTIP_H
+#define KISICONTOOLTIP_H
+
+#include "KoItemToolTip.h"
+
+#include "kritaresourcewidgets_export.h"
+
+class KRITARESOURCEWIDGETS_EXPORT KisIconToolTip: public KoItemToolTip
+{
+
+public:
+ KisIconToolTip();
+ ~KisIconToolTip() override;
+
+protected:
+ QTextDocument *createDocument( const QModelIndex &index ) override;
+};
+
+#endif // KOICONTOOLTIP_H
diff --git a/libs/resourcewidgets/KisResourceItemChooser.cpp b/libs/resourcewidgets/KisResourceItemChooser.cpp
new file mode 100644
index 0000000000..7c7ec5920a
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceItemChooser.cpp
@@ -0,0 +1,548 @@
+/* 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 "KisResourceItemChooser.h"
+
+#include <math.h>
+
+#include <QGridLayout>
+#include <QButtonGroup>
+#include <QHeaderView>
+#include <QAbstractProxyModel>
+#include <QLabel>
+#include <QScrollArea>
+#include <QImage>
+#include <QPixmap>
+#include <QPainter>
+#include <QSplitter>
+#include <QToolButton>
+#include <QWheelEvent>
+#include <QLineEdit>
+#include <QComboBox>
+
+#include <klocalizedstring.h>
+
+#include <KoIcon.h>
+#include <KoFileDialog.h>
+#include <KisKineticScroller.h>
+#include <KisMimeDatabase.h>
+
+#include <KisResourceModelProvider.h>
+#include <KisResourceModel.h>
+#include <KisTagFilterResourceProxyModel.h>
+#include <KisResourceLoaderRegistry.h>
+
+#include "KisResourceItemListView.h"
+#include "KisResourceItemDelegate.h"
+#include "KisTagFilterWidget.h"
+#include "KisTagChooserWidget.h"
+#include "KisResourceItemChooserSync.h"
+#include "KisResourceTaggingManager.h"
+#include "KisTagModelProvider.h"
+
+
+
+#include "KisStorageChooserWidget.h"
+#include "kis_assert.h"
+
+
+class Q_DECL_HIDDEN KisResourceItemChooser::Private
+{
+public:
+ Private(QString _resourceType)
+ : resourceType(_resourceType)
+ {}
+
+ QString resourceType;
+
+ KisResourceModel *resourceModel {0};
+ KisTagFilterResourceProxyModel *tagFilterProxyModel {0};
+ QSortFilterProxyModel *extraFilterModel {0};
+
+ KisResourceTaggingManager *tagManager {0};
+ KisResourceItemListView *view {0};
+ QButtonGroup *buttonGroup {0};
+ QToolButton *viewModeButton {0};
+ KisStorageChooserWidget *storagePopupButton {0};
+
+ QScrollArea *previewScroller {0};
+ QLabel *previewLabel {0};
+ QSplitter *splitter {0};
+ QGridLayout *buttonLayout {0};
+
+ QPushButton *importButton {0};
+ QPushButton *deleteButton {0};
+
+ bool usePreview {false};
+ bool tiledPreview {false};
+ bool grayscalePreview {false};
+ bool synced {false};
+ bool updatesBlocked {false};
+
+ QModelIndex savedResourceWhileReset; // Indexes on the proxyModel, not the source resource model
+
+ QList<QAbstractButton*> customButtons;
+
+};
+
+KisResourceItemChooser::KisResourceItemChooser(const QString &resourceType, bool usePreview, QWidget *parent, QSortFilterProxyModel *extraFilterProxy)
+ : QWidget(parent)
+ , d(new Private(resourceType))
+{
+ d->splitter = new QSplitter(this);
+
+ d->resourceModel = KisResourceModelProvider::resourceModel(resourceType);
+
+ d->tagFilterProxyModel = new KisTagFilterResourceProxyModel(KisTagModelProvider::tagModel(resourceType), this);
+
+ d->extraFilterModel = extraFilterProxy;
+ if (d->extraFilterModel) {
+ d->extraFilterModel->setParent(this);
+ d->extraFilterModel->setSourceModel(d->resourceModel);
+ d->tagFilterProxyModel->setSourceModel(d->extraFilterModel);
+ } else {
+ d->tagFilterProxyModel->setSourceModel(d->resourceModel);
+ }
+
+ connect(d->resourceModel, SIGNAL(beforeResourcesLayoutReset(QModelIndex)), SLOT(slotBeforeResourcesLayoutReset(QModelIndex)));
+ connect(d->resourceModel, SIGNAL(afterResourcesLayoutReset()), SLOT(slotAfterResourcesLayoutReset()));
+
+ d->view = new KisResourceItemListView(this);
+ d->view->setObjectName("ResourceItemview");
+
+ d->view->setModel(d->tagFilterProxyModel);
+ d->view->setItemDelegate(new KisResourceItemDelegate(this));
+ d->view->setSelectionMode(QAbstractItemView::SingleSelection);
+ d->view->viewport()->installEventFilter(this);
+
+ connect(d->view, SIGNAL(currentResourceChanged(QModelIndex)), this, SLOT(activated(QModelIndex)));
+ connect(d->view, SIGNAL(currentResourceClicked(QModelIndex)), this, SLOT(clicked(QModelIndex)));
+ connect(d->view, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint)));
+ connect(d->view, SIGNAL(sigSizeChanged()), this, SLOT(updateView()));
+
+ d->splitter->addWidget(d->view);
+ d->splitter->setStretchFactor(0, 2);
+
+ d->usePreview = usePreview;
+ if (d->usePreview) {
+ d->previewScroller = new QScrollArea(this);
+ d->previewScroller->setWidgetResizable(true);
+ d->previewScroller->setBackgroundRole(QPalette::Dark);
+ d->previewScroller->setVisible(true);
+ d->previewScroller->setAlignment(Qt::AlignCenter);
+ d->previewLabel = new QLabel(this);
+ d->previewScroller->setWidget(d->previewLabel);
+ d->splitter->addWidget(d->previewScroller);
+
+ if (d->splitter->count() == 2) {
+ d->splitter->setSizes(QList<int>() << 280 << 160);
+ }
+
+ QScroller* scroller = KisKineticScroller::createPreconfiguredScroller(d->previewScroller);
+ if (scroller) {
+ connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChanged(QScroller::State)));
+ }
+ }
+
+ d->splitter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+ connect(d->splitter, SIGNAL(splitterMoved(int,int)), SIGNAL(splitterMoved()));
+
+ d->buttonGroup = new QButtonGroup(this);
+ d->buttonGroup->setExclusive(false);
+
+ QGridLayout *layout = new QGridLayout(this);
+
+ d->buttonLayout = new QGridLayout();
+
+ d->importButton = new QPushButton(this);
+
+ d->importButton->setToolTip(i18nc("@info:tooltip", "Import resource"));
+ d->importButton->setEnabled(true);
+ d->buttonGroup->addButton(d->importButton, Button_Import);
+ d->buttonLayout->addWidget(d->importButton, 0, 0);
+
+ d->deleteButton = new QPushButton(this);
+ d->deleteButton->setToolTip(i18nc("@info:tooltip", "Delete resource"));
+ d->deleteButton->setEnabled(false);
+ d->buttonGroup->addButton(d->deleteButton, Button_Remove);
+ d->buttonLayout->addWidget(d->deleteButton, 0, 1);
+
+ connect(d->buttonGroup, SIGNAL(buttonClicked(int)), this, SLOT(slotButtonClicked(int)));
+
+ d->buttonLayout->setColumnStretch(0, 1);
+ d->buttonLayout->setColumnStretch(1, 1);
+ d->buttonLayout->setColumnStretch(2, 2);
+ d->buttonLayout->setSpacing(0);
+ d->buttonLayout->setMargin(0);
+
+ d->viewModeButton = new QToolButton(this);
+ d->viewModeButton->setPopupMode(QToolButton::InstantPopup);
+ d->viewModeButton->setVisible(false);
+ d->tagManager = new KisResourceTaggingManager(resourceType, d->tagFilterProxyModel, this);
+
+ d->storagePopupButton = new KisStorageChooserWidget(this);
+
+ layout->addWidget(d->tagManager->tagChooserWidget(), 0, 0);
+ layout->addWidget(d->viewModeButton, 0, 1);
+ layout->addWidget(d->storagePopupButton, 0, 2);
+ layout->addWidget(d->splitter, 1, 0, 1, 3);
+ layout->addWidget(d->tagManager->tagFilterWidget(), 2, 0, 1, 3);
+ layout->addLayout(d->buttonLayout, 3, 0, 1, 3);
+ layout->setMargin(0);
+ layout->setSpacing(0);
+
+ updateView();
+
+ updateButtonState();
+ showTaggingBar(false);
+ activated(d->view->model()->index(0, 0));
+}
+
+KisResourceItemChooser::~KisResourceItemChooser()
+{
+ disconnect();
+ delete d;
+}
+
+void KisResourceItemChooser::slotButtonClicked(int button)
+{
+ if (button == Button_Import) {
+ QStringList mimeTypes = KisResourceLoaderRegistry::instance()->mimeTypes(d->resourceType);
+ KoFileDialog dialog(0, KoFileDialog::OpenFiles, "OpenDocument");
+ dialog.setMimeTypeFilters(mimeTypes);
+ dialog.setCaption(i18nc("@title:window", "Choose File to Add"));
+ Q_FOREACH(const QString &filename, dialog.filenames()) {
+ if (QFileInfo(filename).exists() && QFileInfo(filename).isReadable()) {
+ d->tagFilterProxyModel->importResourceFile(filename);
+ }
+ }
+ }
+ else if (button == Button_Remove) {
+ QModelIndex index = d->view->currentIndex();
+ if (index.isValid()) {
+ d->tagFilterProxyModel->removeResource(index);
+ }
+ int row = index.row();
+ int rowMin = --row;
+ row = qBound(0, rowMin, row);
+ setCurrentItem(row);
+ activated(d->tagFilterProxyModel->index(row, index.column()));
+ }
+ updateButtonState();
+}
+
+void KisResourceItemChooser::showButtons(bool show)
+{
+ foreach (QAbstractButton * button, d->buttonGroup->buttons()) {
+ show ? button->show() : button->hide();
+ }
+
+ Q_FOREACH (QAbstractButton *button, d->customButtons) {
+ show ? button->show() : button->hide();
+ }
+}
+
+void KisResourceItemChooser::addCustomButton(QAbstractButton *button, int cell)
+{
+ d->buttonLayout->addWidget(button, 0, cell);
+ d->buttonLayout->setColumnStretch(2, 1);
+ d->buttonLayout->setColumnStretch(3, 1);
+}
+
+void KisResourceItemChooser::showTaggingBar(bool show)
+{
+ d->tagManager->showTaggingBar(show);
+
+}
+
+int KisResourceItemChooser::rowCount() const
+{
+ return d->view->model()->rowCount();
+}
+
+void KisResourceItemChooser::setRowHeight(int rowHeight)
+{
+ d->view->setItemSize(QSize(d->view->gridSize().width(), rowHeight));
+}
+
+void KisResourceItemChooser::setColumnWidth(int columnWidth)
+{
+ d->view->setItemSize(QSize(columnWidth, d->view->gridSize().height()));
+}
+
+void KisResourceItemChooser::setItemDelegate(QAbstractItemDelegate *delegate)
+{
+ d->view->setItemDelegate(delegate);
+}
+
+KoResourceSP KisResourceItemChooser::currentResource() const
+{
+ QModelIndex index = d->view->currentIndex();
+ if (index.isValid()) {
+ return resourceFromModelIndex(index);
+ }
+ return 0;
+}
+
+void KisResourceItemChooser::setCurrentResource(KoResourceSP resource)
+{
+ // don't update if the change came from the same chooser
+ if (d->updatesBlocked) {
+ return;
+ }
+ QModelIndex index = d->resourceModel->indexFromResource(resource);
+ d->view->setCurrentIndex(index);
+ updatePreview(index);
+}
+
+void KisResourceItemChooser::slotBeforeResourcesLayoutReset(QModelIndex activateAfterReset)
+{
+ QModelIndex proxyIndex = d->tagFilterProxyModel->mapFromSource(d->tagFilterProxyModel->mapFromSource(activateAfterReset));
+ d->savedResourceWhileReset = proxyIndex.isValid() ? proxyIndex : d->view->currentIndex();
+}
+
+void KisResourceItemChooser::slotAfterResourcesLayoutReset()
+{
+ if (d->savedResourceWhileReset.isValid()) {
+ this->blockSignals(true);
+ setCurrentItem(d->savedResourceWhileReset.row());
+ this->blockSignals(false);
+ }
+ d->savedResourceWhileReset = QModelIndex();
+}
+
+void KisResourceItemChooser::setPreviewOrientation(Qt::Orientation orientation)
+{
+ d->splitter->setOrientation(orientation);
+}
+
+void KisResourceItemChooser::setPreviewTiled(bool tiled)
+{
+ d->tiledPreview = tiled;
+}
+
+void KisResourceItemChooser::setGrayscalePreview(bool grayscale)
+{
+ d->grayscalePreview = grayscale;
+}
+
+void KisResourceItemChooser::setCurrentItem(int row)
+{
+ QModelIndex index = d->view->model()->index(row, 0);
+ if (!index.isValid())
+ return;
+
+ d->view->setCurrentIndex(index);
+ if (index.isValid()) {
+ updatePreview(index);
+ }
+}
+
+void KisResourceItemChooser::activated(const QModelIndex &index)
+{
+ if (!index.isValid()) return;
+
+ KoResourceSP resource = 0;
+
+ if (index.isValid()) {
+ resource = resourceFromModelIndex(index);
+ }
+
+ if (resource && resource->valid()) {
+ d->updatesBlocked = true;
+ emit resourceSelected(resource);
+ d->updatesBlocked = false;
+ updatePreview(index);
+ updateButtonState();
+ }
+}
+
+void KisResourceItemChooser::clicked(const QModelIndex &index)
+{
+ Q_UNUSED(index);
+
+ KoResourceSP resource = currentResource();
+ if (resource) {
+ emit resourceClicked(resource);
+ }
+}
+
+void KisResourceItemChooser::updateButtonState()
+{
+ QAbstractButton *removeButton = d->buttonGroup->button(Button_Remove);
+ if (! removeButton)
+ return;
+
+ KoResourceSP resource = currentResource();
+ if (resource) {
+ removeButton->setEnabled(!resource->permanent());
+ return;
+ }
+ removeButton->setEnabled(false);
+}
+
+void KisResourceItemChooser::updatePreview(const QModelIndex &idx)
+{
+ if (!d->usePreview) return;
+
+ if (!idx.isValid()) {
+ d->previewLabel->setPixmap(QPixmap());
+ return;
+ }
+
+ QImage image = idx.data(Qt::UserRole + KisResourceModel::Thumbnail).value<QImage>();
+
+ if (image.format() != QImage::Format_RGB32 &&
+ image.format() != QImage::Format_ARGB32 &&
+ image.format() != QImage::Format_ARGB32_Premultiplied) {
+
+ image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ }
+
+ if (d->tiledPreview) {
+ int width = d->previewScroller->width() * 4;
+ int height = d->previewScroller->height() * 4;
+ QImage img(width, height, image.format());
+ QPainter gc(&img);
+ gc.fillRect(img.rect(), Qt::white);
+ gc.setPen(Qt::NoPen);
+ gc.setBrush(QBrush(image));
+ gc.drawRect(img.rect());
+ image = img;
+ }
+
+ // Only convert to grayscale if it is rgb. Otherwise, it's gray already.
+ if (d->grayscalePreview && !image.isGrayscale()) {
+ QRgb *pixel = reinterpret_cast<QRgb *>(image.bits());
+ for (int row = 0; row < image.height(); ++row) {
+ for (int col = 0; col < image.width(); ++col) {
+ const QRgb currentPixel = pixel[row * image.width() + col];
+ const int red = qRed(currentPixel);
+ const int green = qGreen(currentPixel);
+ const int blue = qBlue(currentPixel);
+ const int grayValue = (red * 11 + green * 16 + blue * 5) / 32;
+ pixel[row * image.width() + col] = qRgb(grayValue, grayValue, grayValue);
+ }
+ }
+ }
+ d->previewLabel->setPixmap(QPixmap::fromImage(image));
+}
+
+KoResourceSP KisResourceItemChooser::resourceFromModelIndex(const QModelIndex &index) const
+{
+ if (!index.isValid()) {
+ return 0;
+ }
+ KoResourceSP r = d->tagFilterProxyModel->resourceForIndex(index);
+ return r;
+}
+
+QSize KisResourceItemChooser::viewSize() const
+{
+ return d->view->size();
+}
+
+KisResourceItemListView *KisResourceItemChooser::itemView() const
+{
+ return d->view;
+}
+
+void KisResourceItemChooser::contextMenuRequested(const QPoint &pos)
+{
+ d->tagManager->contextMenuRequested(currentResource(), pos);
+}
+
+void KisResourceItemChooser::setViewModeButtonVisible(bool visible)
+{
+ d->viewModeButton->setVisible(visible);
+}
+
+QToolButton *KisResourceItemChooser::viewModeButton() const
+{
+ return d->viewModeButton;
+}
+
+void KisResourceItemChooser::setSynced(bool sync)
+{
+ if (d->synced == sync)
+ return;
+
+ d->synced = sync;
+ KisResourceItemChooserSync *chooserSync = KisResourceItemChooserSync::instance();
+ if (sync) {
+ connect(chooserSync, SIGNAL(baseLengthChanged(int)), SLOT(baseLengthChanged(int)));
+ baseLengthChanged(chooserSync->baseLength());
+ } else {
+ chooserSync->disconnect(this);
+ }
+}
+
+void KisResourceItemChooser::baseLengthChanged(int length)
+{
+ if (d->synced) {
+ d->view->setItemSize(QSize(length, length));
+ }
+}
+
+bool KisResourceItemChooser::eventFilter(QObject *object, QEvent *event)
+{
+ if (d->synced && event->type() == QEvent::Wheel) {
+ KisResourceItemChooserSync *chooserSync = KisResourceItemChooserSync::instance();
+ QWheelEvent *qwheel = static_cast<QWheelEvent *>(event);
+ if (qwheel->modifiers() & Qt::ControlModifier) {
+
+ int degrees = qwheel->delta() / 8;
+ int newBaseLength = chooserSync->baseLength() + degrees / 15 * 10;
+ chooserSync->setBaseLength(newBaseLength);
+ return true;
+ }
+ }
+ return QObject::eventFilter(object, event);
+}
+
+
+void KisResourceItemChooser::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+ updateView();
+}
+
+void KisResourceItemChooser::showEvent(QShowEvent *event)
+{
+ QWidget::showEvent(event);
+ updateView();
+}
+
+void KisResourceItemChooser::updateView()
+{
+ if (d->synced) {
+ KisResourceItemChooserSync *chooserSync = KisResourceItemChooserSync::instance();
+ baseLengthChanged(chooserSync->baseLength());
+ }
+
+ /// helps to set icons here in case the theme is changed
+ d->viewModeButton->setIcon(koIcon("view-choose"));
+ d->importButton->setIcon(koIcon("document-open"));
+ d->deleteButton->setIcon(koIcon("trash-empty"));
+ d->storagePopupButton->setIcon(koIcon("bundle_archive"));
+}
diff --git a/libs/resourcewidgets/KisResourceItemChooser.h b/libs/resourcewidgets/KisResourceItemChooser.h
new file mode 100644
index 0000000000..61bd21d4a9
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceItemChooser.h
@@ -0,0 +1,153 @@
+/* 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) 2010 Boudewijn Rempt <boud@valdyas.org>
+ 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>
+ 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 KIS_RESOURCE_ITEM_CHOOSER
+#define KIS_RESOURCE_ITEM_CHOOSER
+
+#include <QWidget>
+
+#include <QPushButton>
+#include <QModelIndex>
+
+#include <KoResource.h>
+#include <KisKineticScroller.h>
+
+class QAbstractProxyModel;
+class QAbstractItemDelegate;
+class QAbstractButton;
+class QToolButton;
+class QSortFilterProxyModel;
+class KisResourceItemListView;
+
+#include "kritaresourcewidgets_export.h"
+
+
+/**
+ * A widget that contains a KoResourceChooser as well
+ * as an import/export button
+ */
+class KRITARESOURCEWIDGETS_EXPORT KisResourceItemChooser : public QWidget
+{
+ Q_OBJECT
+public:
+ enum Buttons { Button_Import, Button_Remove };
+
+ /// \p usePreview shows the aside preview with the resource's image
+ /// \p extraFilterProxy is an extra filter proxy model for additional filtering. KisResourceItemChooser will take over ownership
+ explicit KisResourceItemChooser(const QString &resourceType, bool usePreview = false, QWidget *parent = 0, QSortFilterProxyModel *extraFilterProxy = 0);
+ ~KisResourceItemChooser() override;
+
+ /// return the number of rows in the view
+ int rowCount() const;
+
+ /// Sets the height of the view rows
+ void setRowHeight(int rowHeight);
+
+ /// Sets the width of the view columns
+ void setColumnWidth(int columnWidth);
+
+ /// Sets a custom delegate for the view
+ void setItemDelegate(QAbstractItemDelegate *delegate);
+
+ /// Gets the currently selected resource
+ /// @returns the selected resource, 0 is no resource is selected
+ KoResourceSP currentResource() const;
+
+ /// Sets the item representing the resource as selected
+ void setCurrentResource(KoResourceSP resource);
+
+ /**
+ * Sets the selected resource, does nothing if there is no valid item
+ * @param row row of the item
+ * @param column column of the item
+ */
+ void setCurrentItem(int row);
+
+ void showButtons(bool show);
+
+ void addCustomButton(QAbstractButton *button, int cell);
+
+ /// determines whether the preview right or below the splitter
+ void setPreviewOrientation(Qt::Orientation orientation);
+ /// determines whether the preview should tile the resource's image or not
+ void setPreviewTiled(bool tiled);
+ /// shows the preview converted to grayscale
+ void setGrayscalePreview(bool grayscale);
+
+ /// sets the visibility of tagging KlineEdits.
+ void showTaggingBar(bool show);
+
+ QSize viewSize() const;
+
+ KisResourceItemListView *itemView() const;
+
+ void setViewModeButtonVisible(bool visible);
+ QToolButton *viewModeButton() const;
+
+ void setSynced(bool sync);
+
+ bool eventFilter(QObject *object, QEvent *event) override;
+
+Q_SIGNALS:
+ /// Emitted when a resource was selected
+ void resourceSelected(KoResourceSP resource);
+ /// Emitted when an *already selected* resource is clicked
+ /// again
+ void resourceClicked(KoResourceSP resource);
+ void splitterMoved();
+
+public Q_SLOTS:
+ void slotButtonClicked(int button);
+ void slotScrollerStateChanged(QScroller::State state){ KisKineticScroller::updateCursor(this, state); }
+
+private Q_SLOTS:
+ void activated(const QModelIndex &index);
+ void clicked(const QModelIndex &index);
+ void contextMenuRequested(const QPoint &pos);
+ void baseLengthChanged(int length);
+ void updateView();
+ void slotBeforeResourcesLayoutReset(QModelIndex activateAfterReset);
+ void slotAfterResourcesLayoutReset();
+
+protected:
+ void showEvent(QShowEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+
+private:
+ void updateButtonState();
+ void updatePreview(const QModelIndex &idx);
+
+ /// Resource for a given model index
+ /// @returns the resource pointer, 0 is index not valid
+ KoResourceSP resourceFromModelIndex(const QModelIndex &index) const;
+
+ class Private;
+ Private *const d;
+
+
+};
+
+#endif // KO_RESOURCE_ITEM_CHOOSER
diff --git a/libs/resourcewidgets/KisResourceItemChooserContextMenu.cpp b/libs/resourcewidgets/KisResourceItemChooserContextMenu.cpp
new file mode 100644
index 0000000000..1db01caaab
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceItemChooserContextMenu.cpp
@@ -0,0 +1,346 @@
+
+/* This file is part of the KDE project
+ * Copyright (c) 2013 Sascha Suelzer <s.suelzer@gmail.com>
+ * Copyright (c) 2020 Agata Cacko <cacko.azh@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 "KisResourceItemChooserContextMenu.h"
+
+#include <QDebug>
+#include <QLabel>
+#include <QGridLayout>
+
+#include <KoIcon.h>
+#include <klocalizedstring.h>
+#include <KoResource.h>
+#include <KisTagModelProvider.h>
+
+
+#include <KisTag.h>
+
+#include "kis_debug.h"
+
+KoLineEditAction::KoLineEditAction(QObject* parent)
+ : QWidgetAction(parent)
+ , m_closeParentOnTrigger(false)
+{
+ QWidget* pWidget = new QWidget (0);
+ QHBoxLayout* pLayout = new QHBoxLayout();
+ m_label = new QLabel(0);
+ m_editBox = new QLineEdit(0);
+ m_editBox->setClearButtonEnabled(true);
+ m_AddButton = new QPushButton();
+ m_AddButton->setIcon(koIcon("list-add"));
+ pLayout->addWidget(m_label);
+ pLayout->addWidget(m_editBox);
+ pLayout->addWidget(m_AddButton);
+ pWidget->setLayout(pLayout);
+ setDefaultWidget(pWidget);
+
+ connect (m_editBox, &QLineEdit::returnPressed, this, &KoLineEditAction::onTriggered);
+ connect (m_AddButton, &QPushButton::clicked, this, &KoLineEditAction::onTriggered);
+}
+
+KoLineEditAction::~KoLineEditAction()
+{
+
+}
+
+void KoLineEditAction::setIcon(const QIcon &icon)
+{
+ QPixmap pixmap = QPixmap(icon.pixmap(16,16));
+ m_label->setPixmap(pixmap);
+}
+
+void KoLineEditAction::closeParentOnTrigger(bool closeParent)
+{
+ m_closeParentOnTrigger = closeParent;
+}
+
+bool KoLineEditAction::closeParentOnTrigger()
+{
+ return m_closeParentOnTrigger;
+}
+
+void KoLineEditAction::onTriggered()
+{
+ if (! m_editBox->text().isEmpty()) {
+ KisTagSP tag(new KisTag());
+ tag->setName(m_editBox->text());
+ tag->setUrl(m_editBox->text());
+ emit triggered(tag);
+ m_editBox->text().clear();
+
+ if (m_closeParentOnTrigger) {
+ this->parentWidget()->close();
+ m_editBox->clearFocus();
+ }
+ }
+}
+
+void KoLineEditAction::setPlaceholderText(const QString& clickMessage)
+{
+ m_editBox->setPlaceholderText(clickMessage);
+}
+
+void KoLineEditAction::setText(const QString& text)
+{
+ ENTER_FUNCTION();
+ m_editBox->setText(text);
+}
+
+void KoLineEditAction::setVisible(bool showAction)
+{
+ QLayout* currentLayout = defaultWidget()->layout();
+
+ this->QAction::setVisible(showAction);
+
+ for(int i=0;i<currentLayout->count();i++) {
+ currentLayout->itemAt(i)->widget()->setVisible(showAction);
+ }
+ defaultWidget()->setVisible(showAction);
+}
+
+ContextMenuExistingTagAction::ContextMenuExistingTagAction(KoResourceSP resource, KisTagSP tag, QObject* parent)
+ : QAction(parent)
+ , m_resource(resource)
+ , m_tag(tag)
+{
+ setText(tag->name());
+ connect (this, SIGNAL(triggered()),
+ this, SLOT(onTriggered()));
+}
+
+ContextMenuExistingTagAction::~ContextMenuExistingTagAction()
+{
+}
+
+void ContextMenuExistingTagAction::onTriggered()
+{
+ ENTER_FUNCTION();
+ emit triggered(m_resource, m_tag);
+}
+
+NewTagAction::~NewTagAction()
+{
+}
+
+NewTagAction::NewTagAction(KoResourceSP resource, QMenu* parent)
+ :KoLineEditAction (parent)
+{
+ m_resource = resource;
+ setIcon(koIcon("document-new"));
+ setPlaceholderText(i18n("New tag"));
+ closeParentOnTrigger(true);
+
+ connect (this, SIGNAL(triggered(KisTagSP)),
+ this, SLOT(onTriggered(KisTagSP)));
+}
+
+void NewTagAction::onTriggered(const KisTagSP tag)
+{
+ emit triggered(m_resource,tag);
+}
+
+class CompareWithOtherTagFunctor
+{
+ KisTagSP m_referenceTag;
+
+public:
+ CompareWithOtherTagFunctor(KisTagSP referenceTag)
+ {
+ m_referenceTag = referenceTag;
+ }
+
+ bool operator()(KisTagSP otherTag)
+ {
+ ENTER_FUNCTION() << "refTag: " << (m_referenceTag.isNull() ? "null" : m_referenceTag->name())
+ << " other: " << (otherTag.isNull() ? "null" : otherTag->name())
+ << " result = " << (!otherTag.isNull() && !m_referenceTag.isNull() && otherTag->url() == m_referenceTag->url());
+ return !otherTag.isNull() && !m_referenceTag.isNull() && otherTag->url() == m_referenceTag->url();
+ }
+
+ void setReferenceTag(KisTagSP referenceTag) {
+ m_referenceTag = referenceTag;
+ }
+
+ KisTagSP referenceTag() {
+ return m_referenceTag;
+ }
+
+};
+
+bool compareWithSpecialTags(KisTagSP tag) {
+ // TODO: RESOURCES: id < 0? For now, "All" fits
+ return !tag.isNull() && tag->id() < 0;
+}
+
+
+
+KisResourceItemChooserContextMenu::KisResourceItemChooserContextMenu(KoResourceSP resource,
+ const KisTagSP currentlySelectedTag)
+{
+
+ QImage image = resource->image();
+ QIcon icon(QPixmap::fromImage(image));
+ QAction * label = new QAction(resource->name(), this);
+ label->setIcon(icon);
+
+ addAction(label);
+
+ QMenu * removableTagsMenu;
+ QMenu * assignableTagsMenu;
+
+ m_tagModel = KisTagModelProvider::tagModel(resource->resourceType().first);
+
+
+ QList<KisTagSP> removables = m_tagModel->tagsForResource(resource->resourceId()).toList();
+
+ QList<KisTagSP> list;
+ for (int i = 0; i < m_tagModel->rowCount(); i++) {
+ QModelIndex index = m_tagModel->index(i, 0);
+ KisTagSP tag = m_tagModel->tagForIndex(index);
+ if (!tag.isNull()) {
+ list << tag;
+ }
+ }
+
+ QList<KisTagSP> assignables2 = list;
+
+ CompareWithOtherTagFunctor comparer(currentlySelectedTag);
+
+ bool currentTagInRemovables = !currentlySelectedTag.isNull();
+ currentTagInRemovables = currentTagInRemovables
+ && (std::find_if(removables.begin(), removables.end(), comparer) != removables.end());
+
+ ENTER_FUNCTION() << "current tag in removeables: " <<currentTagInRemovables;
+
+
+ // remove "All" tag from both "Remove from tag: " and "Assign to tag: " lists
+ std::remove_if(removables.begin(), removables.end(), compareWithSpecialTags);
+ std::remove_if(assignables2.begin(), assignables2.end(), compareWithSpecialTags);
+
+
+ assignableTagsMenu = addMenu(koIcon("list-add"),i18n("Assign to tag"));
+
+
+ if (!removables.isEmpty()) {
+ addSeparator();
+ KisTagSP currentTag = currentlySelectedTag;
+
+ if (!currentTag.isNull() && currentTagInRemovables) {
+ // remove the current tag from both "Remove from tag: " and "Assign to tag: " lists
+ ENTER_FUNCTION() << "# remove the current tag from both lists";
+
+ ENTER_FUNCTION() << "now just removeables";
+ ENTER_FUNCTION() << "comparer's tag: " << comparer.referenceTag();
+ QList<QSharedPointer<KisTag> >::iterator b = std::remove_if(removables.begin(), removables.end(), comparer);
+ if (b != removables.end()) {
+ removables.removeAll(*b);
+ }
+ QList<QSharedPointer<KisTag> >::iterator b2 = std::remove_if(assignables2.begin(), assignables2.end(), comparer);
+ if (b2 != assignables2.end()) {
+ assignables2.removeAll(*b2);
+ }
+ ENTER_FUNCTION() << "done. The list now consists of: ";
+ Q_FOREACH(KisTagSP tag, removables) {
+ ENTER_FUNCTION() << tag;
+ }
+ ENTER_FUNCTION() << "end";
+
+ ContextMenuExistingTagAction * removeTagAction = new ContextMenuExistingTagAction(resource, currentTag, this);
+ removeTagAction->setText(i18n("Remove from this tag"));
+ removeTagAction->setIcon(koIcon("list-remove"));
+
+ connect(removeTagAction, SIGNAL(triggered(KoResourceSP, const KisTagSP)),
+ this, SLOT(removeResourceExistingTag(KoResourceSP, const KisTagSP)));
+ addAction(removeTagAction);
+ }
+
+ if (!removables.isEmpty()) {
+ removableTagsMenu = addMenu(koIcon("list-remove"),i18n("Remove from other tag"));
+
+ KisTagSP empty;
+ CompareWithOtherTagFunctor compareWithRemovable(empty);
+ foreach (const KisTagSP tag, removables) {
+
+ if (tag.isNull()) {
+ continue;
+ }
+
+ compareWithRemovable.setReferenceTag(tag);
+ std::remove_if(assignables2.begin(), assignables2.end(), compareWithRemovable);
+
+
+ ContextMenuExistingTagAction * removeTagAction = new ContextMenuExistingTagAction(resource, tag, this);
+
+ connect(removeTagAction, SIGNAL(triggered(KoResourceSP, const KisTagSP)),
+ this, SLOT(removeResourceExistingTag(KoResourceSP, const KisTagSP)));
+ removableTagsMenu->addAction(removeTagAction);
+ }
+ }
+
+ }
+
+
+ foreach (const KisTagSP &tag, assignables2) {
+ if (tag.isNull()) {
+ continue;
+ }
+
+ ContextMenuExistingTagAction * addTagAction = new ContextMenuExistingTagAction(resource, tag, this);
+
+ connect(addTagAction, SIGNAL(triggered(KoResourceSP, const KisTagSP)),
+ this, SLOT(addResourceTag(KoResourceSP, const KisTagSP)));
+
+
+ assignableTagsMenu->addAction(addTagAction);
+ }
+
+ assignableTagsMenu->addSeparator();
+
+ NewTagAction * addTagAction = new NewTagAction(resource, this);
+ connect(addTagAction, SIGNAL(triggered(KoResourceSP, const KisTagSP)),
+ this, SLOT(addResourceNewTag(KoResourceSP, const KisTagSP)));
+ assignableTagsMenu->addAction(addTagAction);
+
+}
+
+KisResourceItemChooserContextMenu::~KisResourceItemChooserContextMenu()
+{
+
+}
+
+
+void KisResourceItemChooserContextMenu::addResourceTag(KoResourceSP resource, const KisTagSP tag)
+{
+ m_tagModel->tagResource(tag, resource);
+}
+
+void KisResourceItemChooserContextMenu::removeResourceExistingTag(KoResourceSP resource, const KisTagSP tag)
+{
+ m_tagModel->untagResource(tag, resource);
+}
+
+void KisResourceItemChooserContextMenu::addResourceNewTag(KoResourceSP resource, const KisTagSP tag)
+{
+ QString name = tag->name().isEmpty() ? tag->url() : tag->name();
+ QVector<KoResourceSP> resourceList;
+ resourceList << resource;
+ m_tagModel->addEmptyTag(tag, resourceList);
+}
diff --git a/libs/resourcewidgets/KisResourceItemChooserContextMenu.h b/libs/resourcewidgets/KisResourceItemChooserContextMenu.h
new file mode 100644
index 0000000000..80bd1ed096
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceItemChooserContextMenu.h
@@ -0,0 +1,163 @@
+/*
+ * This file is part of the KDE project
+ * Copyright (c) 2013 Sascha Suelzer <s.suelzer@gmail.com>
+ * Copyright (c) 2019 Boudewijn Rempt <boud@valdyas.org>
+ * Copyright (c) 2020 Agata Cacko <cacko.azh@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 KISRESOURCEITEMCHOOSERCONTEXTMENU_H
+#define KISRESOURCEITEMCHOOSERCONTEXTMENU_H
+
+#include <QMenu>
+#include <QWidgetAction>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+
+#include <KoResource.h>
+
+#include <KisTag.h>
+#include <KisTagModel.h>
+
+class ContextMenuExistingTagAction : public QAction
+{
+ Q_OBJECT
+public:
+ explicit ContextMenuExistingTagAction( KoResourceSP resource, KisTagSP tag, QObject* parent = 0);
+ ~ContextMenuExistingTagAction() override;
+
+Q_SIGNALS:
+ void triggered(KoResourceSP resource, KisTagSP tag);
+
+protected Q_SLOTS:
+ void onTriggered();
+
+private:
+ KoResourceSP m_resource;
+ KisTagSP m_tag;
+};
+
+/*!
+ * A line edit QWidgetAction.
+ * Default behavior: Closes its parent upon triggering.
+ */
+class KoLineEditAction : public QWidgetAction
+{
+ Q_OBJECT
+public:
+ explicit KoLineEditAction(QObject* parent);
+ ~KoLineEditAction() override;
+ void setIcon(const QIcon &icon);
+ void closeParentOnTrigger(bool closeParent);
+ bool closeParentOnTrigger();
+ void setPlaceholderText(const QString& clickMessage);
+ void setText(const QString& text);
+ void setVisible(bool showAction);
+
+ Q_SIGNALS:
+ void triggered(const KisTagSP tag);
+
+protected Q_SLOTS:
+ void onTriggered();
+
+private:
+ bool m_closeParentOnTrigger;
+ QLabel * m_label;
+ QLineEdit * m_editBox;
+ QPushButton * m_AddButton;
+};
+
+class NewTagAction : public KoLineEditAction
+{
+ Q_OBJECT
+public:
+ explicit NewTagAction (KoResourceSP resource, QMenu* parent);
+ ~NewTagAction() override;
+
+ Q_SIGNALS:
+ void triggered(KoResourceSP resource, const KisTagSP tag);
+
+protected Q_SLOTS:
+ void onTriggered(const KisTagSP tagName);
+
+private:
+ KoResourceSP m_resource;
+};
+
+
+///
+/// \brief The KisResourceItemChooserContextMenu class is responsible for the context menu in ResourceItemChooser
+///
+/// The context menu for the resource item in the resource item chooser (see: main area in the Brush Presets docker)
+/// contains actions to tag and untag the selected resource.
+/// In case of tagging the user can choose to create a new tag or select one of the existing ones.
+/// In case of untagging the user can untag from the current selected tag (in the combobox) or from some other tags.
+/// This class needs to provide correct lists of tags and take into account that "All" and "All untagged" (and possibly other
+/// generated tags) are special and the user cannot untage the resource from it.
+///
+class KisResourceItemChooserContextMenu : public QMenu
+{
+ Q_OBJECT
+public:
+ ///
+ /// \brief KisResourceItemChooserContextMenu the constructor for the KisResourceItemChooserContextMenu class
+ /// \param resource the resource that the context menu is called for
+ /// \param currentlySelectedTag the currently selected tag in the combobox over the resource item chooser
+ ///
+ explicit KisResourceItemChooserContextMenu(KoResourceSP resource, const KisTagSP currentlySelectedTag);
+ /// \brief the destructor
+ ~KisResourceItemChooserContextMenu() override;
+
+Q_SIGNALS:
+ /// Emitted when a resource should be added to an existing tag.
+ void resourceTagAdditionRequested(KoResourceSP resource, const KisTagSP tag);
+ /// Emitted when a resource should be removed from an existing tag.
+ void resourceTagRemovalRequested(KoResourceSP resource, const KisTagSP tag);
+ /// Emitted when a resource should be added to a new tag, which will need to be created.
+ void resourceAssignmentToNewTagRequested(KoResourceSP resource, const KisTagSP tag);
+
+
+public Q_SLOTS:
+ ///
+ /// \brief addResourceTag slot for a signal from the action to add the tag to the resource
+ /// \param resource resource that needs to be tagged
+ /// \param tag tag to add to the resource
+ ///
+ void addResourceTag(KoResourceSP resource, const KisTagSP tag);
+ ///
+ /// \brief removeResourceExistingTag slot for a signal from the action to remove the tag from the resource
+ /// \param resource resource that the tag needs to be removed from
+ /// \param tag tag that needs to be removed from the resource
+ ///
+ void removeResourceExistingTag(KoResourceSP resource, const KisTagSP tag);
+ ///
+ /// \brief addResourceNewTag slot for the signal from the action to add the tag to the resource
+ /// \param resource resource that the tag needs to be added to
+ /// \param tag tag (more precisely, tag name encapsulated in a tag class) that needs to be added to the resource
+ ///
+ void addResourceNewTag(KoResourceSP resource, const KisTagSP tag);
+
+private:
+ ///
+ /// \brief m_tagModel data model for tags (for tagging and untagging resources and create lists of tags)
+ ///
+ KisTagModel* m_tagModel;
+
+};
+
+#endif // KORESOURCEITEMCHOOSERCONTEXTMENU_H
diff --git a/libs/resourcewidgets/KisResourceItemChooserSync.cpp b/libs/resourcewidgets/KisResourceItemChooserSync.cpp
new file mode 100644
index 0000000000..e387dc33bd
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceItemChooserSync.cpp
@@ -0,0 +1,64 @@
+/* This file is part of the KDE project
+
+ 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 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 "KisResourceItemChooserSync.h"
+
+#include <QGlobalStatic>
+
+Q_GLOBAL_STATIC(KisResourceItemChooserSync, s_instance)
+
+struct Q_DECL_HIDDEN KisResourceItemChooserSync::Private
+{
+ int baseLength;
+};
+
+
+KisResourceItemChooserSync::KisResourceItemChooserSync()
+ : d(new Private)
+{
+ d->baseLength = 50;
+}
+
+KisResourceItemChooserSync::~KisResourceItemChooserSync()
+{
+}
+
+KisResourceItemChooserSync* KisResourceItemChooserSync::instance()
+{
+ return s_instance;
+}
+
+int KisResourceItemChooserSync::baseLength()
+{
+ return d->baseLength;
+}
+
+void KisResourceItemChooserSync::setBaseLength(int length)
+{
+ d->baseLength = qBound(25, length, 100);
+ emit baseLengthChanged(d->baseLength);
+}
+
+
+
+
+
+
+
diff --git a/libs/resourcewidgets/KisResourceItemChooserSync.h b/libs/resourcewidgets/KisResourceItemChooserSync.h
new file mode 100644
index 0000000000..59f5fd1f71
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceItemChooserSync.h
@@ -0,0 +1,66 @@
+/* This file is part of the KDE project
+
+ Copyright (c) 2014 Sven Langkamp <sven.langkamp@gmail.com>
+ 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 KISRESOURCEITEMCHOOSERSYNC_H
+#define KISRESOURCEITEMCHOOSERSYNC_H
+
+#include <QObject>
+#include <QScopedPointer>
+
+#include "kritaresourcewidgets_export.h"
+
+/**
+ * KisResourceItemChooserSync is a singleton that syncs the size of entries in the
+ * resource item choosers between different choosers
+ * To use the syncing it has to be turned on in the KisResourceItemChooser
+ */
+class KRITARESOURCEWIDGETS_EXPORT KisResourceItemChooserSync : public QObject
+{
+ Q_OBJECT
+public:
+ KisResourceItemChooserSync();
+ ~KisResourceItemChooserSync() override;
+ static KisResourceItemChooserSync* instance();
+
+ /// Gets the base length
+ /// @returns the base length of items
+ int baseLength();
+
+ /// Set the base length
+ /// @param length base length for the items, will be clamped if outside range
+ void setBaseLength(int length);
+
+Q_SIGNALS:
+ /// Signal is emitted when the base length is changed and will trigger and update in
+ /// the resource item choosers
+ void baseLengthChanged(int length);
+
+private:
+
+ KisResourceItemChooserSync(const KisResourceItemChooserSync&);
+ KisResourceItemChooserSync operator=(const KisResourceItemChooserSync&);
+
+private:
+ struct Private;
+ const QScopedPointer<Private> d;
+};
+
+#endif // KORESOURCEITEMCHOOSERSYNC_H
diff --git a/libs/resourcewidgets/KisResourceItemDelegate.cpp b/libs/resourcewidgets/KisResourceItemDelegate.cpp
new file mode 100644
index 0000000000..66f62be6b2
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceItemDelegate.cpp
@@ -0,0 +1,89 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 Jan Hambrecht <jaham@gmx.net>
+ * Copyright (C) 2018 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 "KisResourceItemDelegate.h"
+
+#include <QPainter>
+#include <QDebug>
+
+#include "KisResourceModel.h"
+
+KisResourceItemDelegate::KisResourceItemDelegate(QObject *parent)
+ : QAbstractItemDelegate(parent)
+ , m_checkerPainter(4)
+{
+}
+
+void KisResourceItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem & option, const QModelIndex &index) const
+{
+ if (!index.isValid()) return;
+
+ painter->save();
+
+ if (option.state & QStyle::State_Selected) {
+ painter->fillRect(option.rect, option.palette.highlight());
+ }
+
+ QRect innerRect = option.rect.adjusted(2, 2, -2, -2);
+
+ QImage thumbnail = index.data(Qt::UserRole + KisResourceModel::Thumbnail).value<QImage>();
+
+ QSize imageSize = thumbnail.size();
+
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
+
+ QString resourceType = index.data(Qt::UserRole + KisResourceModel::ResourceType).toString();
+ // XXX: don't use a hardcoded string here to identify the resource type
+ if (resourceType == ResourceType::Gradients) {
+ m_checkerPainter.paint(*painter, innerRect);
+ thumbnail = thumbnail.scaled(innerRect.width(), innerRect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ painter->drawImage(innerRect.topLeft(), thumbnail);
+ }
+ else if (resourceType == ResourceType::Patterns) {
+ painter->fillRect(innerRect, Qt::white); // no checkers, they are confusing with patterns.
+ if (imageSize.height() > innerRect.height() || imageSize.width() > innerRect.width()) {
+ thumbnail = thumbnail.scaled(innerRect.width(), innerRect.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ }
+ painter->fillRect(innerRect, QBrush(thumbnail));
+ }
+ else {
+ painter->fillRect(innerRect, Qt::white); // no checkers, they are confusing with patterns.
+ if (imageSize.height() > innerRect.height() || imageSize.width() > innerRect.width()) {
+ thumbnail = thumbnail.scaled(innerRect.width(), innerRect.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ }
+ QPoint topleft(innerRect.topLeft());
+
+ if (thumbnail.width() < innerRect.width()) {
+ topleft.setX(topleft.x() + (innerRect.width() - thumbnail.width()) / 2);
+ }
+ if (thumbnail.height() < innerRect.height()) {
+ topleft.setY(topleft.y() + (innerRect.height() - thumbnail.height()) / 2);
+ }
+ painter->drawImage(topleft, thumbnail);
+ }
+
+
+ painter->restore();
+}
+
+QSize KisResourceItemDelegate::sizeHint(const QStyleOptionViewItem &optionItem, const QModelIndex &) const
+{
+ return optionItem.decorationSize;
+}
diff --git a/libs/resourcewidgets/KisResourceItemDelegate.h b/libs/resourcewidgets/KisResourceItemDelegate.h
new file mode 100644
index 0000000000..7e0c96ebc7
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceItemDelegate.h
@@ -0,0 +1,46 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 Jan Hambrecht <jaham@gmx.net>
+ * Copyright (C) 2018 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 KISRESOURCEITEMDELEGATE_H
+#define KISRESOURCEITEMDELEGATE_H
+
+#include <QAbstractItemDelegate>
+
+#include "KoCheckerBoardPainter.h"
+
+#include "kritaresourcewidgets_export.h"
+
+/// The resource item delegate for rendering the resource preview
+class KRITARESOURCEWIDGETS_EXPORT KisResourceItemDelegate : public QAbstractItemDelegate
+{
+ Q_OBJECT
+public:
+ explicit KisResourceItemDelegate(QObject *parent = 0);
+ ~KisResourceItemDelegate() override {}
+
+ void paint( QPainter *, const QStyleOptionViewItem &, const QModelIndex & ) const override;
+
+ QSize sizeHint ( const QStyleOptionViewItem &, const QModelIndex & ) const override;
+
+private:
+ KoCheckerBoardPainter m_checkerPainter;
+};
+
+#endif
diff --git a/libs/resourcewidgets/KisResourceItemListView.cpp b/libs/resourcewidgets/KisResourceItemListView.cpp
new file mode 100644
index 0000000000..123ba517be
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceItemListView.cpp
@@ -0,0 +1,84 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2019 Wolthera van Hövell tot Westerflier<griffinvalley@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 "KisResourceItemListView.h"
+
+#include <QEvent>
+#include <QHeaderView>
+#include <QScroller>
+#include <QHelpEvent>
+#include <QDebug>
+
+KisResourceItemListView::KisResourceItemListView(QWidget *parent): QListView(parent)
+{
+ setSelectionMode(QAbstractItemView::SingleSelection);
+ setContextMenuPolicy(Qt::DefaultContextMenu);
+ setViewMode(QListView::IconMode);
+ setGridSize(QSize(64, 64));
+ setIconSize(QSize(64, 64));
+ setResizeMode(QListView::Adjust);
+ setUniformItemSizes(true);
+
+ QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(this);
+ if (scroller) {
+ connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChange(QScroller::State)));
+ }
+
+ connect(this, SIGNAL(clicked(QModelIndex)), SIGNAL(currentResourceClicked(const QModelIndex &)));
+}
+
+void KisResourceItemListView::setItemSize(QSize size)
+{
+ setGridSize(size);
+ setIconSize(size);
+}
+
+void KisResourceItemListView::selectionChanged(const QItemSelection &selected, const QItemSelection &/*deselected*/)
+{
+ if (selected.isEmpty()) {
+ emit currentResourceChanged(QModelIndex());
+ }
+ else {
+ emit currentResourceChanged(selected.indexes().first());
+ }
+}
+
+void KisResourceItemListView::contextMenuEvent(QContextMenuEvent *event)
+{
+ QListView::contextMenuEvent(event);
+ emit contextMenuRequested(event->globalPos());
+}
+
+bool KisResourceItemListView::viewportEvent(QEvent *event)
+{
+ if (!model()) return true;
+
+ if (event->type() == QEvent::ToolTip) {
+ QHelpEvent *he = static_cast<QHelpEvent *>(event);
+ QStyleOptionViewItem option = viewOptions();
+ QModelIndex index = model()->buddy(indexAt(he->pos()));
+ if (index.isValid()) {
+ option.rect = visualRect(index);
+ m_tip.showTip(this, he->pos(), option, index);
+ return true;
+ }
+ }
+
+ return QListView::viewportEvent(event);
+}
diff --git a/libs/resourcewidgets/KisResourceItemListView.h b/libs/resourcewidgets/KisResourceItemListView.h
new file mode 100644
index 0000000000..2794844e48
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceItemListView.h
@@ -0,0 +1,71 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2019 Wolthera van Hövell tot Westerflier<griffinvalley@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 KISRESOURCEITEMLISTVIEW_H
+#define KISRESOURCEITEMLISTVIEW_H
+
+#include <QWidget>
+#include <QListView>
+
+#include <KisKineticScroller.h>
+
+#include "KisIconToolTip.h"
+
+#include "kritaresourcewidgets_export.h"
+
+class KRITARESOURCEWIDGETS_EXPORT KisResourceItemListView : public QListView
+{
+ Q_OBJECT
+
+public:
+ KisResourceItemListView(QWidget *parent = nullptr);
+
+ /**
+ * @brief setItemSize
+ * convenience function which sets both the icon and the grid size
+ * to the same value.
+ * @param size - the size you wish either to be.
+ */
+ void setItemSize(QSize size);
+
+public Q_SLOTS:
+ void slotScrollerStateChange(QScroller::State state){ KisKineticScroller::updateCursor(this, state); }
+
+Q_SIGNALS:
+
+ void sigSizeChanged();
+
+ void currentResourceChanged(const QModelIndex &);
+ void currentResourceClicked(const QModelIndex &);
+
+ void contextMenuRequested(const QPoint &);
+
+protected:
+ friend class KisResourceItemChooser;
+
+ void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
+ void contextMenuEvent(QContextMenuEvent *event) override;
+
+ bool viewportEvent(QEvent *event) override;
+
+private:
+ KisIconToolTip m_tip;
+};
+
+#endif // KISRESOURCEITEMLISTVIEW_H
diff --git a/libs/resourcewidgets/KisResourceItemView.cpp b/libs/resourcewidgets/KisResourceItemView.cpp
new file mode 100644
index 0000000000..edf7392c0b
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceItemView.cpp
@@ -0,0 +1,154 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 Jan Hambrecht <jaham@gmx.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 "KisResourceItemView.h"
+
+#include <QEvent>
+#include <QHeaderView>
+#include <QScroller>
+#include <QHelpEvent>
+#include <QDebug>
+
+#include <KisKineticScroller.h>
+
+#include <QtMath>
+
+KisResourceItemView::KisResourceItemView(QWidget *parent)
+ : QTableView(parent)
+{
+ setSelectionMode(QAbstractItemView::SingleSelection);
+ verticalHeader()->hide();
+ horizontalHeader()->hide();
+ verticalHeader()->setDefaultSectionSize(20);
+ setContextMenuPolicy(Qt::DefaultContextMenu);
+ setViewMode(FIXED_COLUMNS);
+
+ QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(this);
+ if (scroller) {
+ connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChange(QScroller::State)));
+ }
+
+ connect(this, SIGNAL(clicked(QModelIndex)), SLOT(slotItemClicked(QModelIndex)));
+}
+
+bool KisResourceItemView::viewportEvent(QEvent *event)
+{
+ if (!model()) return true;
+
+ if (event->type() == QEvent::ToolTip) {
+ QHelpEvent *he = static_cast<QHelpEvent *>(event);
+ QStyleOptionViewItem option = viewOptions();
+ QModelIndex index = model()->buddy(indexAt(he->pos()));
+ if (index.isValid()) {
+ option.rect = visualRect(index);
+ m_tip.showTip(this, he->pos(), option, index);
+ return true;
+ }
+ }
+
+ return QTableView::viewportEvent(event);
+}
+
+void KisResourceItemView::selectionChanged(const QItemSelection &selected, const QItemSelection &/*deselected*/)
+{
+ if (selected.isEmpty()) {
+ emit currentResourceChanged(QModelIndex());
+ }
+ else {
+ emit currentResourceChanged(selected.indexes().first());
+ }
+}
+
+void KisResourceItemView::mousePressEvent(QMouseEvent *event)
+{
+ m_beforeClickIndex = currentIndex();
+ QTableView::mousePressEvent(event);
+}
+
+void KisResourceItemView::slotItemClicked(const QModelIndex &index)
+{
+ if (m_beforeClickIndex == index) {
+ emit currentResourceClicked(index);
+ }
+ m_beforeClickIndex = QModelIndex();
+}
+
+void KisResourceItemView::contextMenuEvent(QContextMenuEvent *event)
+{
+ QTableView::contextMenuEvent(event);
+ emit contextMenuRequested(event->globalPos());
+}
+
+void KisResourceItemView::resizeEvent(QResizeEvent *event)
+{
+ QTableView::resizeEvent(event);
+ updateView();
+ emit sigSizeChanged();
+}
+
+void KisResourceItemView::setViewMode(ViewMode mode)
+{
+ m_viewMode = mode;
+
+ switch (m_viewMode) {
+ case FIXED_COLUMNS:
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Horizontal scrollbar is never needed
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ break;
+ case FIXED_ROWS:
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Vertical scrollbar is never needed
+ break;
+ default:
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ }
+
+}
+
+void KisResourceItemView::updateView()
+{
+ if (!model()) return;
+
+ int columnCount = model()->columnCount(QModelIndex());
+ int rowCount = model()->rowCount(QModelIndex());
+ int rowHeight, columnWidth;
+
+ if (m_viewMode == FIXED_COLUMNS) {
+ columnWidth = qFloor(viewport()->size().width() / static_cast<double>(columnCount));
+
+ for (int i = 0; i < columnCount; ++i) {
+ setColumnWidth(i, columnWidth);
+ }
+ // keep aspect ratio always square.
+ if (columnCount > 1) {
+ for (int i = 0; i < rowCount; ++i) {
+ setRowHeight(i, columnWidth);
+ }
+ }
+ } else if (m_viewMode == FIXED_ROWS) {
+ if (rowCount == 0) return; // Don't divide by zero
+ rowHeight = qFloor(viewport()->size().height() / static_cast<double>(rowCount));
+
+ for (int i = 0; i < rowCount; ++i) {
+ setRowHeight(i, rowHeight);
+ }
+ }
+}
diff --git a/libs/resourcewidgets/KisResourceItemView.h b/libs/resourcewidgets/KisResourceItemView.h
new file mode 100644
index 0000000000..f57ed60da9
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceItemView.h
@@ -0,0 +1,96 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 Jan Hambrecht <jaham@gmx.net>
+ * Copyright (c) 2011 José Luis Vergara <pentalis@gmail.com>
+ * Copyright (c) 2018 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 KISRESOURCEITEMVIEW_H
+#define KISRESOURCEITEMVIEW_H
+
+#include <QTableView>
+#include <QScroller>
+
+#include <KisKineticScroller.h>
+
+#include "KisIconToolTip.h"
+
+class QEvent;
+class QModelIndex;
+
+#include "kritaresourcewidgets_export.h"
+
+/// The resource view
+class KRITARESOURCEWIDGETS_EXPORT KisResourceItemView : public QTableView
+{
+ Q_OBJECT
+
+public:
+
+ enum ViewMode {
+ FIXED_COLUMNS, /// The number of columns is fixed
+ FIXED_ROWS /// The number of rows is fixed
+ };
+
+ explicit KisResourceItemView(QWidget *parent = 0);
+ ~KisResourceItemView() override { disconnect(); }
+
+public Q_SLOTS:
+ void slotScrollerStateChange(QScroller::State state){ KisKineticScroller::updateCursor(this, state); }
+
+Q_SIGNALS:
+
+ void sigSizeChanged();
+
+Q_SIGNALS:
+
+ void currentResourceChanged(const QModelIndex &);
+ void currentResourceClicked(const QModelIndex &);
+
+ void contextMenuRequested(const QPoint &);
+
+protected:
+
+ friend class KisResourceItemChooser;
+
+ void contextMenuEvent(QContextMenuEvent *event) override;
+ void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
+
+ void mousePressEvent(QMouseEvent *event) override;
+ bool viewportEvent(QEvent *event) override;
+
+ /**
+ * This will draw a number of rows based on the number of columns if m_viewMode is FIXED_COLUMNS
+ * And it will draw a number of columns based on the number of rows if m_viewMode is FIXED_ROWS
+ */
+ void resizeEvent(QResizeEvent *event) override;
+
+ void setViewMode(ViewMode mode);
+
+ void updateView();
+
+
+private Q_SLOTS:
+ void slotItemClicked(const QModelIndex &index);
+
+private:
+ KisIconToolTip m_tip;
+ QModelIndex m_beforeClickIndex;
+ ViewMode m_viewMode;
+};
+
+#endif // KORESOURCEITEMVIEW_H
diff --git a/libs/resourcewidgets/KisResourceTaggingManager.cpp b/libs/resourcewidgets/KisResourceTaggingManager.cpp
new file mode 100644
index 0000000000..8b8a4fa636
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceTaggingManager.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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>
+ * Copyright (c) 2020 Agata Cacko <cacko.azh@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 "KisResourceTaggingManager.h"
+
+#include <QInputDialog>
+#include <QMessageBox>
+#include <QPointer>
+#include <QStringList>
+
+#include <klocalizedstring.h>
+#include <ksharedconfig.h>
+#include <kconfiggroup.h>
+
+#include <KoResource.h>
+
+#include <KisResourceModel.h>
+#include <KisResourceModelProvider.h>
+#include <KisTagFilterResourceProxyModel.h>
+#include <KisTagModel.h>
+#include <KisTagModelProvider.h>
+
+#include "KisTagFilterWidget.h"
+#include "KisTagChooserWidget.h"
+#include "KisResourceItemChooserContextMenu.h"
+#include "kis_debug.h"
+#include "KisTag.h"
+
+class KisResourceTaggingManager::Private
+{
+public:
+ ///
+ /// \brief tagChooser tag chooser widget (tags combobox + tag tool button with the popup)
+ ///
+ KisTagChooserWidget *tagChooser;
+ ///
+ /// \brief tagFilter resources filter widget (resources filter box + "filter by tag" checkbox)
+ ///
+ KisTagFilterWidget *tagFilter;
+
+ ///
+ /// \brief model main data model for resources in the item chooser that the Tagging Manager is taking care of
+ ///
+ QPointer<KisTagFilterResourceProxyModel> model;
+
+ ///
+ /// \brief tagModel main tag model for tags in the tags combobox
+ ///
+ KisTagModel* tagModel;
+};
+
+
+KisResourceTaggingManager::KisResourceTaggingManager(QString resourceType, KisTagFilterResourceProxyModel *model, QWidget *parent)
+
+ : QObject(parent)
+ , d(new Private())
+{
+ d->model = model;
+
+ d->tagModel = KisTagModelProvider::tagModel(resourceType);
+ d->tagChooser = new KisTagChooserWidget(d->tagModel, parent);
+ d->tagFilter = new KisTagFilterWidget(d->tagModel, parent);
+
+ connect(d->tagChooser, SIGNAL(sigTagChosen(KisTagSP)), this, SLOT(tagChooserIndexChanged(KisTagSP)));
+
+ connect(d->tagFilter, SIGNAL(filterByTagChanged(bool)), this, SLOT(slotFilterByTagChanged(bool)));
+ connect(d->tagFilter, SIGNAL(filterTextChanged(QString)), this, SLOT(tagSearchLineEditTextChanged(QString)));
+}
+
+KisResourceTaggingManager::~KisResourceTaggingManager()
+{
+ delete d;
+}
+
+void KisResourceTaggingManager::showTaggingBar(bool show)
+{
+ show ? d->tagFilter->show() : d->tagFilter->hide();
+ show ? d->tagChooser->show() : d->tagChooser->hide();
+}
+
+void KisResourceTaggingManager::tagChooserIndexChanged(const KisTagSP tag)
+{
+ d->model->setTag(tag);
+}
+
+void KisResourceTaggingManager::tagSearchLineEditTextChanged(const QString& lineEditText)
+{
+ d->model->setSearchBoxText(lineEditText);
+}
+
+void KisResourceTaggingManager::slotFilterByTagChanged(const bool filterByTag)
+{
+ d->model->setFilterByCurrentTag(filterByTag);
+}
+
+void KisResourceTaggingManager::contextMenuRequested(KoResourceSP resource, QPoint pos)
+{
+ // No visible tag chooser usually means no intended tag interaction,
+ // context menu makes no sense then either
+ if (!resource || !d->tagChooser->isVisible())
+ return;
+
+ KisResourceItemChooserContextMenu menu(resource, d->tagChooser->currentlySelectedTag());
+ menu.exec(pos);
+}
+
+KisTagChooserWidget *KisResourceTaggingManager::tagChooserWidget()
+{
+ return d->tagChooser;
+}
+
+KisTagFilterWidget *KisResourceTaggingManager::tagFilterWidget()
+{
+ return d->tagFilter;
+}
+
diff --git a/libs/resourcewidgets/KisResourceTaggingManager.h b/libs/resourcewidgets/KisResourceTaggingManager.h
new file mode 100644
index 0000000000..fdbabe7727
--- /dev/null
+++ b/libs/resourcewidgets/KisResourceTaggingManager.h
@@ -0,0 +1,127 @@
+/*
+ * 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>
+ * Copyright (c) 2019 Boudewijn Rempt <boud@valdyas.org>
+ * Copyright (c) 2020 Agata Cacko <cacko.azh@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 KISRESOURCETAGGINGMANAGER_H
+#define KISRESOURCETAGGINGMANAGER_H
+
+#include <QObject>
+
+#include <KoResource.h>
+#include <KisTag.h>
+#include <KisTagModel.h>
+
+#include <kis_debug.h>
+#include <KisTag.h>
+
+class QWidget;
+class QStringList;
+class QString;
+class QPoint;
+
+class KisTagFilterWidget;
+class KisTagChooserWidget;
+class KisTagFilterResourceProxyModel;
+
+
+/**
+ * @brief The KisResourceTaggingManager class is a helper class for KisResourceItemChooser for tagChooser and tagFilter widgets.
+ *
+ * It takes care of exchanging information about tags between KisTagChooserWidget and KisTagFilterWidget.
+ * It makes sure that the correct tag is put in the resource model proxy that is used in the KisResourceItemChooser.
+ * Historically it also managed a list of tags; now KisTagModel is taking care of it.
+ *
+ */
+class KisResourceTaggingManager : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ ///
+ /// \brief KisResourceTaggingManager standard constructor of the KisResourceTaggingManager class
+ /// \param resourceType resource type of the resources that will be dealt with
+ /// \param model proxy model that is used to show only the resources tagged with a specific tag
+ /// \param parent parent widget
+ ///
+ explicit KisResourceTaggingManager(QString resourceType, KisTagFilterResourceProxyModel *model, QWidget *parent);
+
+ /// \brief ~KisResourceTaggingManager destructor
+ ~KisResourceTaggingManager() override;
+
+ ///
+ /// \brief showTaggingBar method to show or hide the tag chooser bar and the tag filter
+ /// \param show if true, the bars should be shown; if false, they should be hidden
+ ///
+ void showTaggingBar(bool show);
+ ///
+ /// \brief contextMenuRequested method to get the context menu
+ /// \param currentResource current selected resource
+ /// \param pos position of the mouse cursor where the context menu should be created
+ ///
+ void contextMenuRequested(KoResourceSP currentResource, QPoint pos);
+ ///
+ /// \brief tagFilterWidget method to get the tag filter widget
+ /// \return tag filter widget
+ ///
+ KisTagFilterWidget *tagFilterWidget();
+ ///
+ /// \brief tagChooserWidget method to get the tag chooser widget
+ /// \return tag chooser widget
+ ///
+ KisTagChooserWidget *tagChooserWidget();
+
+private Q_SLOTS:
+ ///
+ /// \brief tagChooserIndexChanged slot for the signal that the tag chosen in the tags combobox changed
+ /// \param tag the currently chosen tag
+ ///
+ /// It puts the current tag in the filter proxy model to get only the resources filtered out by the tag.
+ ///
+ void tagChooserIndexChanged(const KisTagSP tag);
+ ///
+ /// \brief tagSearchLineEditTextChanged slot for the signal that the text in the filter changed
+ /// \param lineEditText the current text in the filter box
+ ///
+ /// It updates the filter in the filter proxy model.
+ ///
+ void tagSearchLineEditTextChanged(const QString &lineEditText);
+ ///
+ /// \brief slotFilterByTagChanged slot for the "filter by tag" checkbox being checked or unchecked by the user
+ /// \param filterByTag current state of the checkbox
+ ///
+ /// It updates the filter in the filter proxy model to honor the "filter by tag" setting correctly.
+ ///
+ void slotFilterByTagChanged(const bool filterByTag);
+
+private:
+
+ class Private;
+ Private* const d;
+};
+
+
+#endif // KORESOURCETAGGINGINTERFACE_H
diff --git a/libs/resourcewidgets/KisStorageChooserWidget.cpp b/libs/resourcewidgets/KisStorageChooserWidget.cpp
new file mode 100644
index 0000000000..f3b8d89e89
--- /dev/null
+++ b/libs/resourcewidgets/KisStorageChooserWidget.cpp
@@ -0,0 +1,132 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2019 Wolthera van Hövell tot Westerflier<griffinvalley@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 <QAbstractItemView>
+#include <QPainter>
+#include <QApplication>
+#include <QStyle>
+#include <QDebug>
+
+#include <QListView>
+
+#include "KisStorageChooserWidget.h"
+#include "KisStorageModel.h"
+#include <KoIcon.h>
+
+
+KisStorageChooserDelegate::KisStorageChooserDelegate(QObject *parent)
+ : QAbstractItemDelegate(parent)
+{
+}
+
+void KisStorageChooserDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ if (!index.isValid()) return;
+
+ painter->save();
+
+ QString name = index.siblingAtColumn(KisStorageModel::DisplayName).data(Qt::DisplayRole).value<QString>();
+ QString location = index.siblingAtColumn(KisStorageModel::Location).data(Qt::DisplayRole).value<QString>();
+ bool active = index.data(Qt::UserRole + KisStorageModel::Active).value<bool>();
+ QString storageType = index.data(Qt::UserRole + KisStorageModel::StorageType).value<QString>();
+
+ QImage thumbnail = index.data(Qt::UserRole + + KisStorageModel::Thumbnail).value<QImage>();
+
+ if (thumbnail.isNull()) {
+ //fallback on cute icons.
+ thumbnail = koIcon("warning").pixmap(option.decorationSize).toImage();
+ if (storageType == "Folder") {
+ thumbnail = koIcon("document-open").pixmap(option.decorationSize).toImage();
+ }
+ else if (storageType == "Adobe Style Library") {
+ thumbnail = koIcon("layer-style-enabled").pixmap(option.decorationSize).toImage();
+ thumbnail = thumbnail.scaled(option.decorationSize, Qt::KeepAspectRatio, Qt::FastTransformation);
+ }
+ else if (storageType == "Adobe Brush Library") {
+ thumbnail = koIcon("select-all").pixmap(option.decorationSize).toImage();
+ }
+ else if (storageType == "Memory") {
+ if (location != "memory") {
+ thumbnail = koIcon("document-new").pixmap(option.decorationSize).toImage();
+ } else {
+ thumbnail = koIcon("drive-harddisk").pixmap(option.decorationSize).toImage();
+ }
+
+ }
+ else if (storageType == "Bundle") {
+ thumbnail = koIcon("bundle_archive").pixmap(option.decorationSize).toImage();
+ }
+
+ } else {
+ thumbnail = thumbnail.scaled(option.decorationSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ }
+
+ QColor penColor(option.palette.text().color());
+
+ QStyleOptionViewItem opt = option;
+
+ if (active) {
+ opt.state = QStyle::State_Sunken;
+ }
+
+ QApplication::style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, painter);
+
+ painter->setPen(penColor);
+ painter->drawImage(option.rect.topLeft()+QPoint(4, 4), thumbnail, thumbnail.rect());
+ QRect text = option.rect;
+ text.setLeft(text.left()+option.decorationSize.width()+8);
+ text.setTop(text.top()+4);
+ painter->drawText(text, Qt::TextWordWrap, name.split("_").join(" "));
+
+ painter->restore();
+}
+
+QSize KisStorageChooserDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ Q_UNUSED(index);
+ int w = 200;
+ int h = option.decorationSize.height()+8;
+ return QSize(w, h);
+}
+
+KisStorageChooserWidget::KisStorageChooserWidget(QWidget *parent) : KisPopupButton(parent)
+{
+ QListView *view = new QListView(this);
+ view->setModel(KisStorageModel::instance());
+ view->setIconSize(QSize(64, 64));
+ view->setItemDelegate(new KisStorageChooserDelegate(this));
+ view->setSelectionMode(QAbstractItemView::SingleSelection);
+ connect(view, SIGNAL(clicked(QModelIndex)), this, SLOT(activated(QModelIndex)));
+ this->setPopupWidget(view);
+}
+
+void KisStorageChooserWidget::activated(const QModelIndex &index)
+{
+ if (!index.isValid()) return;
+
+ bool active = index.data(Qt::UserRole + KisStorageModel::Active).value<bool>();
+ KisStorageModel::instance()->setData(index, !active, Qt::CheckStateRole);
+}
+
+KisStorageChooserWidget::~KisStorageChooserWidget()
+{
+
+}
+
+
diff --git a/libs/resourcewidgets/KisStorageChooserWidget.h b/libs/resourcewidgets/KisStorageChooserWidget.h
new file mode 100644
index 0000000000..5c22f50eb4
--- /dev/null
+++ b/libs/resourcewidgets/KisStorageChooserWidget.h
@@ -0,0 +1,57 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2019 Wolthera van Hövell tot Westerflier<griffinvalley@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 KISSTORAGECHOOSERWIDGET_H
+#define KISSTORAGECHOOSERWIDGET_H
+
+#include <QWidget>
+#include <QAbstractItemDelegate>
+#include <KisPopupButton.h>
+
+#include "kritaresourcewidgets_export.h"
+
+class KRITARESOURCEWIDGETS_EXPORT KisStorageChooserDelegate : public QAbstractItemDelegate
+{
+ Q_OBJECT
+public:
+ explicit KisStorageChooserDelegate(QObject *parent = 0);
+ ~KisStorageChooserDelegate() override {}
+
+ void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
+
+ QSize sizeHint ( const QStyleOptionViewItem &, const QModelIndex & ) const override;
+
+};
+
+
+class KRITARESOURCEWIDGETS_EXPORT KisStorageChooserWidget : public KisPopupButton
+{
+ Q_OBJECT
+public:
+ KisStorageChooserWidget(QWidget *parent = 0);
+
+ ~KisStorageChooserWidget();
+
+private Q_SLOTS:
+ void activated(const QModelIndex &index);
+
+};
+
+
+#endif // KISSTORAGECHOOSERWIDGET_H
diff --git a/libs/resourcewidgets/KisTagChooserWidget.cpp b/libs/resourcewidgets/KisTagChooserWidget.cpp
new file mode 100644
index 0000000000..62d92d3008
--- /dev/null
+++ b/libs/resourcewidgets/KisTagChooserWidget.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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>
+ * Copyright (c) 2020 Agata Cacko <cacko.azh@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 "KisTagChooserWidget.h"
+
+#include <QDebug>
+#include <QToolButton>
+#include <QGridLayout>
+#include <QComboBox>
+
+#include <klocalizedstring.h>
+#include <KisSqueezedComboBox.h>
+
+#include <KoIcon.h>
+
+#include "KisResourceItemChooserContextMenu.h"
+#include "KisTagToolButton.h"
+#include "kis_debug.h"
+#include <KisActiveFilterTagProxyModel.h>
+
+class Q_DECL_HIDDEN KisTagChooserWidget::Private
+{
+public:
+ QComboBox *comboBox;
+ KisTagToolButton *tagToolButton;
+ KisTagModel* model;
+ QScopedPointer<KisActiveFilterTagProxyModel> activeFilterModel;
+ KisTagSP rememberedTag;
+
+ Private(KisTagModel* model)
+ : activeFilterModel(new KisActiveFilterTagProxyModel(0))
+ {
+ activeFilterModel->setSourceModel(model);
+ }
+};
+
+KisTagChooserWidget::KisTagChooserWidget(KisTagModel* model, QWidget* parent)
+ : QWidget(parent)
+ , d(new Private(model))
+{
+ d->comboBox = new QComboBox(this);
+
+ d->comboBox->setToolTip(i18n("Tag"));
+ d->comboBox->setSizePolicy(QSizePolicy::MinimumExpanding , QSizePolicy::Fixed );
+
+ d->comboBox->setModel(d->activeFilterModel.get());
+
+ d->model = model;
+
+ QGridLayout* comboLayout = new QGridLayout(this);
+
+ comboLayout->addWidget(d->comboBox, 0, 0);
+
+ d->tagToolButton = new KisTagToolButton(this);
+ comboLayout->addWidget(d->tagToolButton, 0, 1);
+
+ comboLayout->setSpacing(0);
+ comboLayout->setMargin(0);
+ comboLayout->setColumnStretch(0, 3);
+ this->setEnabled(true);
+
+ connect(d->comboBox, SIGNAL(currentIndexChanged(int)),
+ this, SLOT(tagChanged(int)));
+
+ connect(d->tagToolButton, SIGNAL(popupMenuAboutToShow()),
+ this, SLOT (tagToolContextMenuAboutToShow()));
+
+ connect(d->tagToolButton, SIGNAL(newTagRequested(KisTagSP)),
+ this, SLOT(tagToolCreateNewTag(KisTagSP)));
+
+ connect(d->tagToolButton, SIGNAL(deletionOfCurrentTagRequested()),
+ this, SLOT(tagToolDeleteCurrentTag()));
+
+ connect(d->tagToolButton, SIGNAL(renamingOfCurrentTagRequested(KisTagSP)),
+ this, SLOT(tagToolRenameCurrentTag(KisTagSP)));
+ connect(d->tagToolButton, SIGNAL(undeletionOfTagRequested(KisTagSP)),
+ this, SLOT(tagToolUndeleteLastTag(KisTagSP)));
+
+ connect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(slotModelAboutToBeReset()));
+ connect(d->model, SIGNAL(modelReset()), this, SLOT(slotModelReset()));
+
+
+}
+
+KisTagChooserWidget::~KisTagChooserWidget()
+{
+ delete d;
+}
+
+void KisTagChooserWidget::tagToolDeleteCurrentTag()
+{
+ KisTagSP currentTag = currentlySelectedTag();
+ if (!currentTag.isNull() && currentTag->id() >= 0) {
+ d->model->removeTag(currentTag);
+ setCurrentIndex(0);
+ d->tagToolButton->setUndeletionCandidate(currentTag);
+ }
+}
+
+void KisTagChooserWidget::tagChanged(int tagIndex)
+{
+ if (tagIndex >= 0) {
+ emit sigTagChosen(currentlySelectedTag());
+ }
+}
+
+void KisTagChooserWidget::tagToolRenameCurrentTag(const KisTagSP newName)
+{
+ // TODO: RESOURCES: it should use QString, not KisTagSP
+ KisTagSP currentTag = currentlySelectedTag();
+ QString name = newName.isNull() ? "" : newName->name();
+ bool canRenameCurrentTag = !currentTag.isNull() && currentTag->id() < 0;
+ if (canRenameCurrentTag && !name.isEmpty()) {
+ d->model->renameTag(currentTag, newName->name());
+ }
+}
+
+void KisTagChooserWidget::tagToolUndeleteLastTag(const KisTagSP tag)
+{
+ int previousIndex = d->comboBox->currentIndex();
+ bool success = d->model->changeTagActive(tag, true);
+ setCurrentIndex(previousIndex);
+ if (success) {
+ d->tagToolButton->setUndeletionCandidate(KisTagSP());
+ setCurrentItem(tag);
+ }
+}
+
+void KisTagChooserWidget::setCurrentIndex(int index)
+{
+ d->comboBox->setCurrentIndex(index);
+}
+
+int KisTagChooserWidget::currentIndex() const
+{
+ return d->comboBox->currentIndex();
+}
+
+bool KisTagChooserWidget::setCurrentItem(KisTagSP tag)
+{
+ for (int i = 0; i < d->model->rowCount(); i++) {
+ QModelIndex index = d->model->index(i, 0);
+ KisTagSP temp = d->model->tagForIndex(index);
+ if (!temp.isNull() && temp->url() == tag->url()) {
+ setCurrentIndex(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+KisTagSP KisTagChooserWidget::tagToolCreateNewTag(KisTagSP tag)
+{
+ // TODO: RESOURCES: this function should use QString, not KisTagSP
+ int previous = d->comboBox->currentIndex();
+
+ if(tag.isNull() || tag->name().isNull() || tag->name().isEmpty()) {
+ return KisTagSP();
+ }
+
+ tag->setUrl(tag->name());
+ tag->setComment(tag->name());
+ tag->setActive(true);
+ tag->setValid(true);
+ bool added = d->model->addTag(tag);
+
+ if (added) {
+ bool found = setCurrentItem(tag);
+ if (found) {
+ return currentlySelectedTag();
+ } else {
+ return KisTagSP();
+ }
+ }
+
+ setCurrentIndex(previous);
+ return KisTagSP();
+}
+
+KisTagSP KisTagChooserWidget::currentlySelectedTag()
+{
+ int row = d->comboBox->currentIndex();
+ if (row < 0) {
+ return KisTagSP();
+ }
+
+ QModelIndex index = d->model->index(row, 0);
+ KisTagSP tag = d->model->tagForIndex(index);
+ return tag;
+}
+
+bool KisTagChooserWidget::selectedTagIsReadOnly()
+{
+ return currentlySelectedTag()->id() < 0;
+}
+
+void KisTagChooserWidget::tagToolContextMenuAboutToShow()
+{
+ /* only enable the save button if the selected tag set is editable */
+ d->tagToolButton->readOnlyMode(selectedTagIsReadOnly());
+}
+
+void KisTagChooserWidget::slotModelAboutToBeReset()
+{
+ d->rememberedTag = currentlySelectedTag();
+}
+
+void KisTagChooserWidget::slotModelReset()
+{
+ bool selected = setCurrentItem(d->rememberedTag);
+ if (!selected) {
+ setCurrentIndex(0); // last used tag was most probably removed
+ }
+}
diff --git a/libs/resourcewidgets/KisTagChooserWidget.h b/libs/resourcewidgets/KisTagChooserWidget.h
new file mode 100644
index 0000000000..45b1139b57
--- /dev/null
+++ b/libs/resourcewidgets/KisTagChooserWidget.h
@@ -0,0 +1,158 @@
+/*
+ * 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>
+ * Copyright (c) 2019 Boudewijn Rempt <boud@valdyas.org>
+ * Copyright (c) 2020 Agata Cacko <cacko.azh@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 KISTAGCHOOSERWIDGET_H
+#define KISTAGCHOOSERWIDGET_H
+
+#include <QWidget>
+#include "kritaresourcewidgets_export.h"
+
+#include <KisTag.h>
+#include <KisTagModel.h>
+
+///
+/// \brief The KisTagChooserWidget class is responsible for all the logic that tags combobox has in various resource choosers.
+///
+/// (Example of usage: tag combobox in Brushes docker).
+/// It uses KisTagModel as a model for items in the combobox.
+/// It is also responsible for the popup for tag removal, renaming and creation
+/// that appears on the right side of the tag combobox (via KisTagToolButton)
+/// All the logic for adding and removing tags is done through KisTagModel.
+///
+/// For logic related to tagging and untagging resources, check KisTaggingManager
+/// and KisItemChooserContextMenu.
+///
+class KRITARESOURCEWIDGETS_EXPORT KisTagChooserWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit KisTagChooserWidget(KisTagModel* model, QWidget* parent);
+ ~KisTagChooserWidget() override;
+
+
+ /// \brief setCurrentIndex sets the current index in the combobox
+ /// \param index index is the index of the tag in the combobox
+ ///
+ void setCurrentIndex(int index);
+
+ ///
+ /// \brief currentIndex returns the current index in tags combobox
+ /// \return the index of the current item in tags combobox
+ ///
+ int currentIndex() const;
+
+ /// \brief currentlySelectedTag returns the current tag from combobox
+ /// \see currentIndex
+ /// \return the tag that is currently selected in the tag combobox
+ ///
+ KisTagSP currentlySelectedTag();
+ ///
+ /// \brief selectedTagIsReadOnly checks whether the tag is readonly (generated by Krita)
+ /// \return true if the tag was generated by Krita, false if it's just a normal tag
+ ///
+ bool selectedTagIsReadOnly();
+
+Q_SIGNALS:
+ ///
+ /// \brief sigTagChosen is emitted when the selected tag in the combobox changes due to user interaction or by other means
+ /// \param tag current tag
+ ///
+ void sigTagChosen(const KisTagSP tag);
+
+public Q_SLOTS:
+
+ ///
+ /// \brief tagChanged slot for the signal from the combobox that the index changed
+ /// \param index new index
+ ///
+ /// When the index in the combobox changes, for example because of user's interaction,
+ /// combobox emits a signal; this method is called when it happens.
+ void tagChanged(int index);
+
+private Q_SLOTS:
+ ///
+ /// \brief tagToolCreateNewTag slot for the signal from KisTagToolButton that a new tag needs to be created
+ /// \param tag tag with the name to be created
+ /// \return created tag taken from the model, with a valid id
+ ///
+ KisTagSP tagToolCreateNewTag(KisTagSP tag);
+ ///
+ /// \brief tagToolRenameCurrentTag slot for the signal from KisTagToolButton that the current tag needs to be renamed
+ /// \param newName new name for the tag
+ ///
+ void tagToolRenameCurrentTag(const KisTagSP newName);
+ ///
+ /// \brief tagToolDeleteCurrentTag slot for the signal from the KisTagToolButton that the current tag needs to be deleted
+ ///
+ /// Note that tags are not deleted but just marked inactive in the database.
+ ///
+ void tagToolDeleteCurrentTag();
+
+ ///
+ /// \brief tagToolUndeleteLastTag slot for the signal from the KisTagToolButton that the last deleted tag needs to be undeleted
+ /// \param tag tag to be undeleted (marked active)
+ ///
+ void tagToolUndeleteLastTag(const KisTagSP tag);
+
+ ///
+ /// \brief tagToolContextMenuAboutToShow slot for the signal from the KisTagToolButton that the popup will be shown soon
+ ///
+ /// Based on the current tag (if it's readonly or not), the popup looks different, so this function
+ /// sets the correct mode on the KisTagToolButton popup.
+ ///
+ void tagToolContextMenuAboutToShow();
+
+ ///
+ /// \brief slotModelAboutToBeReset is called before the tag model is being reset.
+ ///
+ /// It remembers the last selected tag to select it again after the reset of the model.
+ ///
+ void slotModelAboutToBeReset();
+ ///
+ /// \brief slotModelReset is called after the tag model is reset.
+ ///
+ /// It restores the last selected tag.
+ ///
+ void slotModelReset();
+
+
+private:
+ ///
+ /// \brief setCurrentItem sets the tag from the param as the current tag in the combobox
+ /// \param tag tag to be set as current in the combobox
+ /// \return true if successful, false if not successful
+ ///
+ bool setCurrentItem(KisTagSP tag);
+
+private:
+ class Private;
+ Private* const d;
+
+};
+
+#endif // KOTAGCHOOSERWIDGET_H
diff --git a/libs/resourcewidgets/KisTagFilterWidget.cpp b/libs/resourcewidgets/KisTagFilterWidget.cpp
new file mode 100644
index 0000000000..a49fcccf1b
--- /dev/null
+++ b/libs/resourcewidgets/KisTagFilterWidget.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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 "KisTagFilterWidget.h"
+
+#include <QPushButton>
+#include <QAction>
+#include <QGridLayout>
+#include <QLineEdit>
+#include <QCompleter>
+#include <QCheckBox>
+
+#include <klocalizedstring.h>
+
+#include <KoIcon.h>
+
+#include <kis_debug.h>
+#include <kconfig.h>
+#include <ksharedconfig.h>
+#include <kconfiggroup.h>
+
+class KisTagFilterWidget::Private
+{
+public:
+ QString tagSearchBarTooltip_saving_disabled;
+ QString tagSearchBarTooltip_saving_enabled;
+ QLineEdit* tagSearchLineEdit;
+ QGridLayout* filterBarLayout;
+ QCompleter* completer;
+ QCheckBox* filterByTagCheckbox;
+
+ QString configGroup {"resources"};
+ QString configName {"filterByTagChecked"};
+
+};
+
+KisTagFilterWidget::KisTagFilterWidget(KisTagModel* model, QWidget* parent)
+ : QWidget(parent)
+ , d(new Private())
+{
+ QString searchTooltipMaintext = i18nc(
+ "@info:tooltip",
+ "<p>Enter search terms here to add resources to, or remove them from, the current tag view.</p>"
+ "<p>To filter based on the partial, case insensitive name of a resource:<br/>"
+ "<tt>partialname</tt> or <tt>!partialname</tt></p>"
+ "<p>To include or exclude other tag sets:<br/>"
+ "<tt>[Tagname]</tt> or <tt>![Tagname]</tt></p>"
+ "<p>For case sensitive and full name matching in-/exclusion:<br/>"
+ "<tt>\"ExactMatch\"</tt> or <tt>!\"ExactMatch\"</tt></p>");
+
+ d->tagSearchBarTooltip_saving_disabled = searchTooltipMaintext + i18nc(
+ "@info:tooltip",
+ "<p>Filter results cannot be saved for the <b>All Presets</b> view. "
+ "In this view, pressing <b>Enter</b> or clearing the filter box will restore all items. "
+ "Create and/or switch to a different tag if you want to save filtered resources into named sets.</p>");
+
+ d->tagSearchBarTooltip_saving_enabled = searchTooltipMaintext + i18nc(
+ "@info:tooltip",
+ "<p>Pressing <b>Enter</b> or clicking the <b>Save</b> button will save the changes.</p>");
+
+ QGridLayout* filterBarLayout = new QGridLayout;
+
+
+ d->tagSearchLineEdit = new QLineEdit(this);
+ d->tagSearchLineEdit->setClearButtonEnabled(true);
+ d->tagSearchLineEdit->setPlaceholderText(i18n("Search"));
+ d->tagSearchLineEdit->setToolTip(d->tagSearchBarTooltip_saving_disabled);
+ d->tagSearchLineEdit->setEnabled(true);
+
+ d->completer = new QCompleter(model, this);
+ d->completer->setCompletionRole(Qt::DisplayRole);
+ d->completer->setCaseSensitivity(Qt::CaseInsensitive);
+ d->tagSearchLineEdit->setCompleter(d->completer);
+
+ filterBarLayout->setMargin(0);
+ filterBarLayout->setColumnStretch(0, 1);
+ filterBarLayout->addWidget(d->tagSearchLineEdit, 0, 0);
+
+ d->filterByTagCheckbox = new QCheckBox(this);
+ d->filterByTagCheckbox->setText(i18nc("It appears in the checkbox next to the filter box "
+ "in resources dockers; must be short.", "filter by tag"));
+
+ KConfigGroup cfg = KSharedConfig::openConfig()->group(d->configGroup);
+ bool filterByTagCheckboxChecked = cfg.readEntry(d->configName, true);
+ d->filterByTagCheckbox->setChecked(filterByTagCheckboxChecked);
+
+
+ filterBarLayout->addWidget(d->filterByTagCheckbox, 0, 1);
+ connect(d->tagSearchLineEdit, SIGNAL(textChanged(QString)),
+ this, SLOT(onTextChanged(QString)));
+ connect(d->filterByTagCheckbox, SIGNAL(stateChanged(int)), this, SLOT(slotFilterByTagChanged(int)));
+ this->setLayout(filterBarLayout);
+
+}
+
+KisTagFilterWidget::~KisTagFilterWidget()
+{
+ delete d;
+}
+
+void KisTagFilterWidget::clear()
+{
+ d->tagSearchLineEdit->clear();
+}
+
+
+void KisTagFilterWidget::onTextChanged(const QString& lineEditText)
+{
+ emit filterTextChanged(lineEditText);
+}
+
+void KisTagFilterWidget::slotFilterByTagChanged(int filterByTag)
+{
+ emit filterByTagChanged(filterByTag == Qt::Checked);
+ KConfigGroup cfg = KSharedConfig::openConfig()->group(d->configGroup);
+ cfg.writeEntry(d->configName, filterByTag == Qt::Checked);
+}
diff --git a/libs/resourcewidgets/KisTagFilterWidget.h b/libs/resourcewidgets/KisTagFilterWidget.h
new file mode 100644
index 0000000000..f3e000a219
--- /dev/null
+++ b/libs/resourcewidgets/KisTagFilterWidget.h
@@ -0,0 +1,53 @@
+/*
+ * 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>
+ * 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 KISTAGFILTERWIDGET_H
+#define KISTAGFILTERWIDGET_H
+
+#include <QWidget>
+#include <KisTagModel.h>
+
+class KisTagFilterWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit KisTagFilterWidget(KisTagModel* model, QWidget* parent);
+ ~KisTagFilterWidget() override;
+ void clear();
+
+Q_SIGNALS:
+ void filterTextChanged(const QString &filterText);
+ void filterByTagChanged(const bool filterByTag);
+private Q_SLOTS:
+ void onTextChanged(const QString &lineEditText);
+ void slotFilterByTagChanged(int filterByTag);
+private:
+ class Private;
+ Private* const d;
+};
+
+#endif // KOTAGFILTERWIDGET_H
diff --git a/libs/resourcewidgets/KisTagToolButton.cpp b/libs/resourcewidgets/KisTagToolButton.cpp
new file mode 100644
index 0000000000..12c14b1955
--- /dev/null
+++ b/libs/resourcewidgets/KisTagToolButton.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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>
+ * Copyright (c) 2020 Agata Cacko <cacko.azh@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 "KisTagToolButton.h"
+
+#include <QToolButton>
+#include <QGridLayout>
+
+#include <klocalizedstring.h>
+
+#include <KoIcon.h>
+
+#include "KisResourceItemChooserContextMenu.h"
+#include "kis_debug.h"
+
+class KisTagToolButton::Private
+{
+public:
+ QToolButton* tagToolButton;
+ QAction* action_undeleteTag;
+ QAction* action_deleteTag;
+ KoLineEditAction* action_renameTag;
+ KisTagSP undeleteCandidate;
+};
+
+KisTagToolButton::KisTagToolButton(QWidget* parent)
+ :QWidget(parent), d(new Private())
+{
+
+ QGridLayout* buttonLayout = new QGridLayout(this);
+ buttonLayout->setMargin(0);
+ buttonLayout->setSpacing(0);
+
+ d->tagToolButton = new QToolButton(this);
+ d->tagToolButton->setIcon(koIcon("bookmarks"));
+ d->tagToolButton->setText(i18n("Tag"));
+ d->tagToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+ d->tagToolButton->setToolTip(i18nc("@info:tooltip", "<qt>Show the tag box options.</qt>"));
+ d->tagToolButton->setPopupMode(QToolButton::InstantPopup);
+ d->tagToolButton->setEnabled(true);
+
+ QMenu* popup = new QMenu(this);
+
+ KoLineEditAction* addTagAction = new KoLineEditAction(popup);
+ addTagAction->setPlaceholderText(i18n("New tag"));
+ addTagAction->setIcon(koIcon("document-new"));
+ addTagAction->closeParentOnTrigger(true);
+ popup->addAction(addTagAction);
+
+ connect(addTagAction, SIGNAL(triggered(KisTagSP)),
+ this, SIGNAL(newTagRequested(KisTagSP)));
+
+ d->action_renameTag = new KoLineEditAction(popup);
+ d->action_renameTag->setPlaceholderText(i18n("Rename tag"));
+ d->action_renameTag->setIcon(koIcon("edit-rename"));
+ d->action_renameTag->closeParentOnTrigger(true);
+ popup->addAction(d->action_renameTag);
+
+ connect(d->action_renameTag, SIGNAL(triggered(KisTagSP)),
+ this, SIGNAL(renamingOfCurrentTagRequested(KisTagSP)));
+
+ popup->addSeparator();
+
+ d->action_deleteTag = new QAction(popup);
+ d->action_deleteTag->setText(i18n("Delete this tag"));
+ d->action_deleteTag->setIcon(koIcon("edit-delete"));
+ popup->addAction(d->action_deleteTag);
+
+ connect(d->action_deleteTag, SIGNAL(triggered()),
+ this, SIGNAL(deletionOfCurrentTagRequested()));
+
+ popup->addSeparator();
+
+ d->action_undeleteTag = new QAction(popup);
+ d->action_undeleteTag->setIcon(koIcon("edit-redo"));
+ d->action_undeleteTag->setVisible(false);
+ popup->addAction(d->action_undeleteTag);
+
+ connect(d->action_undeleteTag, SIGNAL(triggered()),
+ this, SLOT(onTagUndeleteClicked()));
+
+ connect(popup, SIGNAL(aboutToShow()),
+ this, SIGNAL(popupMenuAboutToShow()));
+
+ d->tagToolButton->setMenu(popup);
+ buttonLayout->addWidget(d->tagToolButton);
+}
+
+KisTagToolButton::~KisTagToolButton()
+{
+ delete d;
+}
+
+void KisTagToolButton::readOnlyMode(bool activate)
+{
+ activate = !activate;
+ d->action_renameTag->setVisible(activate);
+ d->action_deleteTag->setVisible(activate);
+}
+
+void KisTagToolButton::setUndeletionCandidate(const KisTagSP deletedTag)
+{
+ if (deletedTag.isNull() || deletedTag->name().isEmpty()) {
+ d->action_undeleteTag->setVisible(false);
+ return;
+ } else {
+ d->undeleteCandidate = deletedTag;
+ d->action_undeleteTag->setText(i18n("Undelete") +" "+ deletedTag->name());
+ d->action_undeleteTag->setVisible(true);
+ }
+}
+
+void KisTagToolButton::onTagUndeleteClicked()
+{
+ emit undeletionOfTagRequested(d->undeleteCandidate);
+}
+
diff --git a/libs/resourcewidgets/KisTagToolButton.h b/libs/resourcewidgets/KisTagToolButton.h
new file mode 100644
index 0000000000..d665dab3a6
--- /dev/null
+++ b/libs/resourcewidgets/KisTagToolButton.h
@@ -0,0 +1,116 @@
+/*
+ * 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>
+ * Copyright (c) 2020 Agata Cacko <cacko.azh@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 KISTAGTOOLBUTTON_H
+#define KISTAGTOOLBUTTON_H
+
+#include <QWidget>
+#include <KisTag.h>
+
+///
+/// \brief The KisTagToolButton class manages the logic of the tag management popup.
+///
+/// This class is responsible for the GUI for creating, renaming and removing tags.
+/// Since both renaming and removing is context-dependant (it depends on which tag
+/// is currently selected in the combobox), all actions emit signals to the TagChooserWidget
+/// for it to handle actual creationg, renaming and removal of tags in the KisTagModel.
+///
+class KisTagToolButton : public QWidget
+{
+ Q_OBJECT
+
+private:
+ explicit KisTagToolButton(QWidget* parent = 0);
+ ~KisTagToolButton() override;
+
+ ///
+ /// \brief readOnlyMode sets the mode of the popup
+ ///
+ /// If the mode is read-only, then renaming and removal of the tag
+ /// is not accessible (the textbox and the buttons are hidden).
+ /// \param activate if true, then the popup is in the read-only mode.
+ ///
+ void readOnlyMode(bool activate);
+ ///
+ /// \brief setUndeletionCandidate sets a new item in the deleted tags list
+ ///
+ /// Tags are never deleted fully, they are only marked inactive.
+ /// Undeletion means marking them as active again. This function
+ /// adds new tags for the user to be able to undelete them (mark active in the database).
+ /// \param deletedTag tag that can be undeleted (activated again)
+ ///
+ void setUndeletionCandidate(const KisTagSP deletedTag);
+
+Q_SIGNALS:
+ ///
+ /// \brief newTagRequested signals to the KisTagChooserWidget to create a new tag
+ /// \param tag tag name written by the user (other fields are not used)
+ ///
+ /// Since KisTagToolButton doesn't know which KisTagModel it should be using (because it doesn't
+ /// know the resourceType) and for the consistency, it signals KisTagChooserWidget to create
+ /// a new tag with the name written by the user.
+ void newTagRequested(const KisTagSP tag);
+ ///
+ /// \brief renamingOfCurrentTagRequested signals to KisTagChooserWidget to rename the current tag
+ /// \param tag tag name written by the user (other fields are not used)
+ ///
+ /// Since KisTagToolButton doesn't know which tag is current or which KisTagModel it should be using,
+ /// it signals KisTagChooserWidget to do rename the current tag to the name written by the user.
+ void renamingOfCurrentTagRequested(const KisTagSP tag);
+ ///
+ /// \brief deletionOfCurrentTagRequested signals to KisTagChooserWidget to delete the current tag
+ ///
+ /// Since KisTagToolButton doesn't know which tag is current or which KisTagModel it should be using,
+ /// it signals KisTagChooserWidget to do remove the current tag.
+ void deletionOfCurrentTagRequested();
+ ///
+ /// \brief undeletionOfTagRequested signals to KisTagChooserWidget to undelete the mentioned tag
+ /// \param tag tag to be undeleted (marked active again)
+ ///
+ /// Tags are never deleted fully, they are only marked inactive.
+ /// Undeletion means marking them as active again. This function signals to KisTagChooserWidget
+ /// that a tag mentioned in the argument should be activated.
+ void undeletionOfTagRequested(const KisTagSP tag);
+ ///
+ /// \brief popupMenuAboutToShow signals that the tags popup will be shown soon.
+ ///
+ /// It is used by \see KisTagChooserWidget
+ ///
+ void popupMenuAboutToShow();
+
+private Q_SLOTS:
+ ///
+ /// \brief onTagUndeleteClicked is called when the user
+ ///
+ void onTagUndeleteClicked();
+
+private:
+ class Private;
+ Private* const d;
+ friend class KisTagChooserWidget;
+};
+
+#endif // KOTAGTOOLBUTTON_H
diff --git a/libs/resourcewidgets/dbexplorer/CMakeLists.txt b/libs/resourcewidgets/dbexplorer/CMakeLists.txt
new file mode 100644
index 0000000000..1c09bc3a81
--- /dev/null
+++ b/libs/resourcewidgets/dbexplorer/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(kritadbexplorer_SOURCES
+ DbExplorer.cpp
+ DlgDbExplorer.cpp
+ TableModel.cpp
+)
+
+ki18n_wrap_ui(kritadbexplorer_SOURCES WdgDbExplorer.ui )
+
+add_library(kritadbexplorer MODULE ${kritadbexplorer_SOURCES})
+
+target_link_libraries(kritadbexplorer
+ PRIVATE
+ kritaresources
+ kritaresourcewidgets
+ kritaui
+ Qt5::Core
+ Qt5::Widgets
+ Qt5::Sql
+)
+install(TARGETS kritadbexplorer DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
+install(FILES dbexplorer.xmlgui DESTINATION ${DATA_INSTALL_DIR}/kritaplugins)
+install(FILES dbexplorer.action DESTINATION ${DATA_INSTALL_DIR}/krita/actions)
diff --git a/libs/resourcewidgets/dbexplorer/DbExplorer.cpp b/libs/resourcewidgets/dbexplorer/DbExplorer.cpp
new file mode 100644
index 0000000000..514c54abde
--- /dev/null
+++ b/libs/resourcewidgets/dbexplorer/DbExplorer.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DbExplorer.h"
+
+#include <cmath>
+
+#include <klocalizedstring.h>
+#include <kis_debug.h>
+#include <kpluginfactory.h>
+#include <kis_icon.h>
+#include <KisViewManager.h>
+#include <kis_action.h>
+#include "DlgDbExplorer.h"
+
+K_PLUGIN_FACTORY_WITH_JSON(DbExplorerFactory, "kritadbexplorer.json", registerPlugin<DbExplorer>();)
+
+DbExplorer::DbExplorer(QObject *parent, const QVariantList &)
+ : KisActionPlugin(parent)
+{
+ KisAction *action = createAction("dbexplorer");
+ connect(action, SIGNAL(triggered()), this, SLOT(slotDbExplorer()));
+}
+
+
+DbExplorer::~DbExplorer()
+{
+}
+
+void DbExplorer::slotDbExplorer()
+{
+ DlgDbExplorer dlgDbExplorer(viewManager()->mainWindow());
+ dlgDbExplorer.exec();
+}
+
+#include "DbExplorer.moc"
diff --git a/libs/resourcewidgets/dbexplorer/DbExplorer.h b/libs/resourcewidgets/dbexplorer/DbExplorer.h
new file mode 100644
index 0000000000..8eb8491ef0
--- /dev/null
+++ b/libs/resourcewidgets/dbexplorer/DbExplorer.h
@@ -0,0 +1,40 @@
+/*
+ * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef DBEXPLORER_H
+#define DBEXPLORER_H
+
+#include <QVariant>
+#include <KisActionPlugin.h>
+
+class KUndo2MagicString;
+
+class DbExplorer : public KisActionPlugin
+{
+ Q_OBJECT
+public:
+ DbExplorer(QObject *parent, const QVariantList &);
+ ~DbExplorer() override;
+
+public Q_SLOTS:
+
+ void slotDbExplorer();
+
+};
+
+#endif // DBEXPLORER_H
diff --git a/libs/resourcewidgets/dbexplorer/DlgDbExplorer.cpp b/libs/resourcewidgets/dbexplorer/DlgDbExplorer.cpp
new file mode 100644
index 0000000000..3033579b0a
--- /dev/null
+++ b/libs/resourcewidgets/dbexplorer/DlgDbExplorer.cpp
@@ -0,0 +1,211 @@
+/*
+ * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DlgDbExplorer.h"
+
+#include <klocalizedstring.h>
+#include <kis_debug.h>
+
+#include <QDataWidgetMapper>
+#include <QTableView>
+#include <QtSql>
+#include <QStyledItemDelegate>
+
+#include <TableModel.h>
+#include <KisResourceModel.h>
+#include <KisTagFilterResourceProxyModel.h>
+#include <KisResourceModelProvider.h>
+#include <KisTagModelProvider.h>
+#include <KisResourceTypeModel.h>
+#include <KisTagModel.h>
+#include <KisStorageModel.h>
+#include <KisTagModelProvider.h>
+#include <KisResourceItemDelegate.h>
+#include <KisResourceItemListView.h>
+
+
+DlgDbExplorer::DlgDbExplorer(QWidget *parent)
+ : KoDialog(parent)
+{
+ setCaption(i18n("Resources Cache Database Explorer"));
+
+ setButtons(Ok);
+
+ m_page = new WdgDbExplorer(this);
+ Q_CHECK_PTR(m_page);
+
+ setMainWidget(m_page);
+
+ m_resourceTypeModel = new KisResourceTypeModel(this);
+ m_tagModel = new KisTagModel("", this);
+
+ {
+ m_page->tableStorages->setModel(new KisStorageModel(this));
+ m_page->tableStorages->hideColumn(0);
+ m_page->tableStorages->setSelectionMode(QAbstractItemView::SingleSelection);
+ m_page->tableStorages->resizeColumnsToContents();
+ }
+
+ {
+ KisResourceModel *resourcesModel = KisResourceModelProvider::resourceModel(ResourceType::Brushes);
+ m_page->tableResources->setModel(resourcesModel);
+ m_page->tableResources->hideColumn(0);
+ m_page->tableResources->setSelectionMode(QAbstractItemView::SingleSelection);
+
+ m_page->cmbResourceTypes->setModel(m_resourceTypeModel);
+ m_page->cmbResourceTypes->setModelColumn(KisResourceTypeModel::Name);
+
+ connect(m_page->cmbResourceTypes, SIGNAL(activated(int)), SLOT(slotTbResourceTypeSelected(int)));
+ connect(m_page->tableResources, SIGNAL(clicked(QModelIndex)), SLOT(slotTbResourceItemSelected()));
+ }
+
+ {
+ TableModel *tagsModel = new TableModel(this, QSqlDatabase::database());
+ TableDelegate *tagsDelegate = new TableDelegate(m_page->tableStorages);
+ tagsDelegate->setEditable(true);
+ tagsModel->setTable("tags");
+ tagsModel->setHeaderData(0, Qt::Horizontal, i18n("Id"));
+ tagsModel->setHeaderData(1, Qt::Horizontal, i18n("Type"));
+ tagsModel->setRelation(1, QSqlRelation("resource_types", "id", "name"));
+ tagsModel->setHeaderData(2, Qt::Horizontal, i18n("Tag"));
+ tagsModel->setHeaderData(3, Qt::Horizontal, i18n("Name"));
+ tagsModel->setHeaderData(4, Qt::Horizontal, i18n("Comment"));
+ tagsModel->setHeaderData(5, Qt::Horizontal, i18n("Active"));
+ tagsModel->setHeaderData(6, Qt::Horizontal, i18n("Thumbnail"));
+ tagsModel->setHeaderData(7, Qt::Horizontal, i18n("Display Name"));
+ tagsModel->addBooleanColumn(5);
+ tagsDelegate->addBooleanColumn(5);
+ tagsModel->select();
+
+ connect(tagsModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(slotResetTagModel(QModelIndex, QModelIndex)));
+
+ m_page->tableTags->setModel(tagsModel);
+ m_page->tableTags->hideColumn(0);
+ m_page->tableTags->setItemDelegate(tagsDelegate);
+ m_page->tableTags->setSelectionMode(QAbstractItemView::SingleSelection);
+ m_page->tableTags->resizeColumnsToContents();
+ }
+
+ {
+ QSqlTableModel *versionModel = new QSqlTableModel(this, QSqlDatabase::database());
+ versionModel->setTable("version_information");
+ versionModel->setHeaderData(0, Qt::Horizontal, "id");
+ versionModel->setHeaderData(1, Qt::Horizontal, "database_version");
+ versionModel->setHeaderData(2, Qt::Horizontal, "krita_version");
+ versionModel->setHeaderData(3, Qt::Horizontal, "creation_date");
+ versionModel->select();
+ QSqlRecord r = versionModel->record(0);
+
+ m_page->lblDatabaseVersion->setText(r.value("database_version").toString());
+ m_page->lblKritaVersion->setText(r.value("krita_version").toString());
+ m_page->lblCreationDate->setText(r.value("creation_date").toString());
+ }
+
+
+ {
+ m_page->cmbRvResourceTypes->setModel(m_resourceTypeModel);
+ m_page->cmbRvResourceTypes->setModelColumn(KisResourceTypeModel::Name);
+ connect(m_page->cmbRvResourceTypes, SIGNAL(activated(int)), SLOT(slotRvResourceTypeSelected(int)));
+
+ m_page->cmbRvTags->setModelColumn(KisTagModel::Name);
+ m_page->cmbRvTags->setModel(m_tagModel);
+ connect(m_page->cmbRvTags, SIGNAL(activated(int)), SLOT(slotRvTagSelected(int)));
+
+ m_page->cmbRvResourceTypes->setCurrentIndex(0);
+ slotRvResourceTypeSelected(0);
+
+ m_page->resourceItemView->setItemDelegate(new KisResourceItemDelegate(this));
+ m_page->resourceItemView->setSelectionMode(QAbstractItemView::SingleSelection);
+ }
+
+}
+
+DlgDbExplorer::~DlgDbExplorer()
+{
+}
+
+void DlgDbExplorer::updateTagModel(const QString& resourceType)
+{
+ m_tagModel = KisTagModelProvider::tagModel(resourceType);
+ m_page->cmbRvTags->setModelColumn(KisTagModel::Name);
+ m_page->cmbRvTags->setModel(m_tagModel);
+ m_page->cmbRvTags->update();
+ qDebug() << "number of tags in " << resourceType << " tag model: " << m_tagModel->rowCount();
+}
+
+void DlgDbExplorer::slotResetTagModel(QModelIndex topLeft, QModelIndex bottomRight)
+{
+ KisTagModelProvider::resetModels();
+}
+
+void DlgDbExplorer::slotRvResourceTypeSelected(int index)
+{
+ QModelIndex idx = m_page->cmbResourceTypes->model()->index(index, KisResourceTypeModel::ResourceType);
+ QString resourceType = idx.data(Qt::DisplayRole).toString();
+ qDebug() << resourceType;
+
+ updateTagModel(resourceType);
+
+ KisResourceModel *resourceModel = KisResourceModelProvider::resourceModel(resourceType);
+
+ KisTagFilterResourceProxyModel *tagFilterModel = new KisTagFilterResourceProxyModel(KisTagModelProvider::tagModel(resourceType), this);
+ tagFilterModel->setSourceModel(resourceModel);
+ m_filterProxyModel = tagFilterModel;
+
+ m_page->resourceItemView->setModel(tagFilterModel);
+}
+
+void DlgDbExplorer::slotTbResourceTypeSelected(int index)
+{
+ QModelIndex idx = m_page->cmbRvResourceTypes->model()->index(index, KisResourceTypeModel::ResourceType);
+ QString resourceType = idx.data(Qt::DisplayRole).toString();
+ qDebug() << resourceType;
+
+ m_tagModel = KisTagModelProvider::tagModel(resourceType);
+
+ KisResourceModel *resourceModel = KisResourceModelProvider::resourceModel(resourceType);
+ m_page->tableResources->setModel(resourceModel);
+ m_page->tableResources->setCurrentIndex(m_page->tableResources->model()->index(0, 0));
+ slotTbResourceItemSelected();
+ m_page->tableResources->resizeColumnsToContents();
+}
+
+void DlgDbExplorer::slotTbResourceItemSelected()
+{
+ QModelIndex idx = m_page->tableResources->selectionModel()->selectedIndexes().first();
+
+ QImage thumb = idx.data(Qt::UserRole + KisResourceModel::Thumbnail).value<QImage>();
+ Qt::TransformationMode mode = Qt::SmoothTransformation;
+ if (thumb.size().width() < 100 && thumb.size().height() < 100) {
+ mode = Qt::FastTransformation;
+ }
+
+ m_page->lblThumbnail->setPixmap(QPixmap::fromImage(thumb.scaled(100, 100, Qt::KeepAspectRatio, mode)));
+ //If we could get a list of versions for a given resource, this would be the moment to add them...
+}
+
+void DlgDbExplorer::slotRvTagSelected(int index)
+{
+ qDebug() << "selected tag" << index;
+ QModelIndex idx = m_tagModel->index(index, 0);
+ KisTagSP tag = m_tagModel->tagForIndex(idx);
+
+ if (m_filterProxyModel && !tag.isNull() && tag->valid()) {
+ m_filterProxyModel->setTag(tag);
+ }
+}
diff --git a/libs/resourcewidgets/dbexplorer/DlgDbExplorer.h b/libs/resourcewidgets/dbexplorer/DlgDbExplorer.h
new file mode 100644
index 0000000000..dda9aa0a4a
--- /dev/null
+++ b/libs/resourcewidgets/dbexplorer/DlgDbExplorer.h
@@ -0,0 +1,70 @@
+/*
+ * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef DLG_DBEXPLORER
+#define DLG_DBEXPLORER
+
+#include <KoDialog.h>
+
+#include "ui_WdgDbExplorer.h"
+//#include <KisTagFilterResourceProxyModel.h>
+
+class KisResourceModel;
+class KisTagModel;
+class KisResourceTypeModel;
+class KisTagFilterResourceProxyModel;
+
+
+class WdgDbExplorer : public QWidget, public Ui::WdgDbExplorer
+{
+ Q_OBJECT
+
+public:
+ WdgDbExplorer(QWidget *parent) : QWidget(parent) {
+ setupUi(this);
+ }
+};
+
+class DlgDbExplorer: public KoDialog
+{
+ Q_OBJECT
+public:
+ DlgDbExplorer(QWidget * parent = 0);
+ ~DlgDbExplorer() override;
+
+private Q_SLOTS:
+
+ void slotTbResourceTypeSelected(int index);
+ void slotTbResourceItemSelected();
+
+ void slotRvResourceTypeSelected(int index);
+ void slotRvTagSelected(int index);
+
+ void slotResetTagModel(QModelIndex topLeft, QModelIndex bottomRight);
+
+private:
+ void updateTagModel(const QString& resourceType);
+
+ WdgDbExplorer *m_page {0};
+
+ KisTagModel *m_tagModel {0};
+ KisResourceTypeModel *m_resourceTypeModel {0};
+ KisTagFilterResourceProxyModel* m_filterProxyModel {0};
+};
+
+#endif // DLG_DBEXPLORER
diff --git a/libs/resourcewidgets/dbexplorer/TableModel.cpp b/libs/resourcewidgets/dbexplorer/TableModel.cpp
new file mode 100644
index 0000000000..9df2094ab9
--- /dev/null
+++ b/libs/resourcewidgets/dbexplorer/TableModel.cpp
@@ -0,0 +1,175 @@
+/*
+ * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include "TableModel.h"
+
+#include <QApplication>
+
+TableDelegate::TableDelegate(QObject *parent)
+ : QSqlRelationalDelegate(parent)
+{}
+
+QRect getNewRect(const QStyleOptionViewItem &option)
+{
+ // get the rectangle in the middle of the field
+ const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
+ QRect newRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
+ QSize(option.decorationSize.width() +
+ 5,option.decorationSize.height()),
+ QRect(option.rect.x() + textMargin, option.rect.y(),
+ option.rect.width() -
+ (2 * textMargin), option.rect.height()));
+ return newRect;
+}
+
+void TableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ QStyleOptionViewItem viewItemOption(option);
+ // Only do this if we are accessing the column with boolean variables.
+ if (m_booleanColumns.contains(index.column())) {
+ // This basically changes the rectangle in which the check box is drawn.
+ viewItemOption.rect = getNewRect(option);
+ }
+ // Draw the check box using the new rectangle.
+ QSqlRelationalDelegate::paint(painter, viewItemOption, index);
+}
+
+QSize TableDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ return QSqlRelationalDelegate::sizeHint(option, index);
+}
+
+bool TableDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
+{
+ if (m_editable) {
+ if (m_booleanColumns.contains(index.column())) {
+ QStyleOptionViewItem optionCheckable = option;
+ optionCheckable.rect = getNewRect(option);
+ optionCheckable.features |= QStyleOptionViewItem::HasCheckIndicator;
+ return QSqlRelationalDelegate::editorEvent(event, model, optionCheckable, index);
+ } else {
+ return QSqlRelationalDelegate::editorEvent(event, model, option, index);
+ }
+ }
+ return false;
+}
+
+QWidget *TableDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ if (m_editable) {
+ if (m_booleanColumns.contains(index.column())) {
+ QStyleOptionViewItem optionCheckable = option;
+ optionCheckable.features |= QStyleOptionViewItem::HasCheckIndicator;
+ optionCheckable.rect = getNewRect(option);
+ return QSqlRelationalDelegate::createEditor(parent, optionCheckable, index);
+ } else {
+ return QSqlRelationalDelegate::createEditor(parent, option, index);
+ }
+ }
+ return 0;
+}
+
+void TableDelegate::addDateTimeColumn(int column)
+{
+ m_dateTimeColumns << column;
+}
+
+void TableDelegate::addBooleanColumn(int column)
+{
+ m_booleanColumns << column;
+}
+
+void TableDelegate::setEditable(bool editable)
+{
+ m_editable = editable;
+}
+
+
+TableModel::TableModel(QObject *parent, QSqlDatabase db)
+ : QSqlRelationalTableModel(parent, db)
+{
+ this->setEditStrategy(QSqlTableModel::OnFieldChange);
+}
+
+TableModel::~TableModel()
+{
+
+}
+
+QVariant TableModel::data(const QModelIndex &index, int role) const
+{
+ QVariant d = QSqlRelationalTableModel::data(index, Qt::DisplayRole);
+ if (role == Qt::DisplayRole) {
+ if (m_dateTimeColumns.contains(index.column())) {
+ d = QVariant::fromValue<QString>(QDateTime::fromSecsSinceEpoch(d.toInt()).toString());
+ }
+ if (m_booleanColumns.contains(index.column())) {
+ return QVariant();
+ }
+ }
+ else if (role == Qt::CheckStateRole) {
+ if (m_booleanColumns.contains(index.column())) {
+ if (d.toInt() == 0) {
+ return Qt::Unchecked;
+ }
+ else {
+ return Qt::Checked;
+ }
+ }
+ return QVariant();
+ }
+ return d;
+
+}
+
+bool TableModel::setData(const QModelIndex & index, const QVariant & value, int role)
+{
+ if (m_booleanColumns.contains(index.column()) && role == Qt::CheckStateRole) {
+ // Writing the data when the check box is set to checked.
+ if (value == Qt::Checked) {
+ // Let's write the new value
+ return QSqlTableModel::setData(index, 1, Qt::EditRole);
+ // Writing the data when the check box is set to unchecked
+ } else if (value == Qt::Unchecked) {
+ // Let's write the new value
+ return QSqlTableModel::setData(index, 0, Qt::EditRole);
+ }
+ }
+
+ bool response = QSqlTableModel::setData(index, value, role);
+ return response;
+}
+
+Qt::ItemFlags TableModel::flags(const QModelIndex &index) const
+{
+ Qt::ItemFlags f = QSqlRelationalTableModel::flags((index));
+ if (m_booleanColumns.contains(index.column())) {
+ f |= Qt::ItemIsUserCheckable;
+ }
+ return f;
+
+}
+
+void TableModel::addDateTimeColumn(int column)
+{
+ m_dateTimeColumns << column;
+}
+
+void TableModel::addBooleanColumn(int column)
+{
+ m_booleanColumns << column;
+}
diff --git a/libs/resourcewidgets/dbexplorer/TableModel.h b/libs/resourcewidgets/dbexplorer/TableModel.h
new file mode 100644
index 0000000000..4ad1a906ae
--- /dev/null
+++ b/libs/resourcewidgets/dbexplorer/TableModel.h
@@ -0,0 +1,71 @@
+/*
+ * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef TABLEMODEL_H
+#define TABLEMODEL_H
+
+#include <QObject>
+#include <QtSql>
+
+class TableDelegate : public QSqlRelationalDelegate
+{
+ Q_OBJECT
+public:
+
+ TableDelegate(QObject *parent);
+
+ 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 addDateTimeColumn(int column);
+ void addBooleanColumn(int column);
+ void setEditable(bool editable);
+private:
+
+ bool m_editable{false};
+ QVector<int> m_booleanColumns;
+ QVector<int> m_dateTimeColumns;
+};
+
+/**
+ * @brief The TableModel class handles boolean
+ * and datatime columns in a custom way.
+ */
+class TableModel : public QSqlRelationalTableModel
+{
+ Q_OBJECT
+public:
+
+ TableModel(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase());
+ ~TableModel() override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ bool setData(const QModelIndex & index, const QVariant & value, int role) override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+
+ void addDateTimeColumn(int column);
+ void addBooleanColumn(int column);
+
+private:
+
+ QVector<int> m_booleanColumns;
+ QVector<int> m_dateTimeColumns;
+
+};
+
+#endif // TABLEMODEL_H
diff --git a/libs/resourcewidgets/dbexplorer/WdgDbExplorer.ui b/libs/resourcewidgets/dbexplorer/WdgDbExplorer.ui
new file mode 100644
index 0000000000..9c998a8a4a
--- /dev/null
+++ b/libs/resourcewidgets/dbexplorer/WdgDbExplorer.ui
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WdgDbExplorer</class>
+ <widget class="QWidget" name="WdgDbExplorer">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>975</width>
+ <height>545</height>
+ </rect>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="1">
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>4</number>
+ </property>
+ <widget class="QWidget" name="tabStorages">
+ <attribute name="title">
+ <string>Storages</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTableView" name="tableStorages">
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabResources">
+ <attribute name="title">
+ <string>Resources</string>
+ </attribute>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Resource Type</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="cmbResourceTypes"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QTableView" name="tableResources">
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QLabel" name="lblThumbnail">
+ <property name="text">
+ <string>lblThumbnail</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Versions</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTableView" name="tableVersions"/>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabTags">
+ <attribute name="title">
+ <string>Tags</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QTableView" name="tableTags">
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTableView" name="tableTaggedResources">
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabSchema">
+ <attribute name="title">
+ <string>Schema Information</string>
+ </attribute>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Database Version</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="lblDatabaseVersion">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Krita Version</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="lblKritaVersion">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Creation Date</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="lblCreationDate">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabResourceView">
+ <attribute name="title">
+ <string>Resource View</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Resource Type:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="KComboBox" name="cmbRvResourceTypes"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Tag:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="cmbRvTags">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="KisResourceItemListView" name="resourceItemView"/>
+ </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>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>KComboBox</class>
+ <extends>QComboBox</extends>
+ <header location="global">kcombobox.h</header>
+ </customwidget>
+ <customwidget>
+ <class>KisResourceItemListView</class>
+ <extends>QListView</extends>
+ <header location="global">KisResourceItemListView.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/libs/resourcewidgets/dbexplorer/dbexplorer.action b/libs/resourcewidgets/dbexplorer/dbexplorer.action
new file mode 100644
index 0000000000..772321dcb7
--- /dev/null
+++ b/libs/resourcewidgets/dbexplorer/dbexplorer.action
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ActionCollection version="2" name="DbExplorer">
+ <Actions category="DbExplorer">
+ <text>Database Explorer</text>
+ <Action name="dbexplorer">
+ <icon></icon>
+ <text>&amp;Explore Resources Cache Database...</text>
+ <whatsThis>Resources Cache Database</whatsThis>
+ <toolTip>Resources Cache Database</toolTip>
+ <iconText>Resources Cache Database</iconText>
+ <activationFlags>0</activationFlags>
+ <activationConditions>0</activationConditions>
+ <shortcut></shortcut>
+ <isCheckable>false</isCheckable>
+ <statusTip></statusTip>
+ </Action>
+ </Actions>
+</ActionCollection>
diff --git a/libs/resourcewidgets/dbexplorer/dbexplorer.xmlgui b/libs/resourcewidgets/dbexplorer/dbexplorer.xmlgui
new file mode 100644
index 0000000000..b06239b823
--- /dev/null
+++ b/libs/resourcewidgets/dbexplorer/dbexplorer.xmlgui
@@ -0,0 +1,8 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui library="kritadbexplorer" version="8" translationDomain="krita">
+ <MenuBar>
+ <Menu name="Tools">
+ <Action name="dbexplorer"/>
+ </Menu>
+ </MenuBar>
+</kpartgui>
diff --git a/libs/resourcewidgets/dbexplorer/kritadbexplorer.json b/libs/resourcewidgets/dbexplorer/kritadbexplorer.json
new file mode 100644
index 0000000000..45c083e82a
--- /dev/null
+++ b/libs/resourcewidgets/dbexplorer/kritadbexplorer.json
@@ -0,0 +1,9 @@
+{
+ "Id": "Cache Db Explorer Plugin",
+ "Type": "Service",
+ "X-KDE-Library": "kritadbexplorer",
+ "X-KDE-ServiceTypes": [
+ "Krita/ViewPlugin"
+ ],
+ "X-Krita-Version": "28"
+}
diff --git a/libs/store/KoQuaZipStore.cpp b/libs/store/KoQuaZipStore.cpp
index 6e09f75921..f95a98eeb9 100644
--- a/libs/store/KoQuaZipStore.cpp
+++ b/libs/store/KoQuaZipStore.cpp
@@ -1,291 +1,295 @@
/*
* 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);
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())
{
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)
{
+ qDebug() << "QUAZIP! qint64 KoQuaZipStore::write(const char *_data, qint64 _len)" << _data << _len;
Q_D(KoStore);
if (_len == 0) return 0;
if (!d->isOpen) {
+ qDebug() << "qint64 KoQuaZipStore::write (1)";
errorStore << "KoStore: You must open before writing" << endl;
return 0;
}
if (d->mode != Write) {
+ qDebug() << "qint64 KoQuaZipStore::write (2)";
errorStore << "KoStore: Can not write to store that is opened for reading" << endl;
return 0;
}
d->size += _len;
+ qDebug() << "qint64 KoQuaZipStore::write (3)";
if (dd->buffer.write(_data, _len)) { // writeData returns a bool!
return _len;
}
return 0;
}
QStringList KoQuaZipStore::directoryList() const
{
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;
}
if (!d->substituteThis.isEmpty()) {
fixedPath = fixedPath.replace(d->substituteThis, d->substituteWith);
}
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*/)
{
return true;
}
bool KoQuaZipStore::enterAbsoluteDirectory(const QString &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
{
Q_D(const KoStore);
QString fixedPath = absPath;
fixedPath.replace("//", "/");
if (!d->substituteThis.isEmpty()) {
fixedPath = fixedPath.replace(d->substituteThis, d->substituteWith);
}
return dd->archive->getFileNameList().contains(fixedPath);
}
diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index 5b2859f77b..4c30c763c1 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -1,631 +1,622 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile
)
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
${OCIO_INCLUDE_DIR}
)
if (ANDROID)
add_definitions(-DQT_OPENGL_ES_3)
add_definitions(-DHAS_ONLY_OPENGL_ES)
include_directories (${Qt5AndroidExtras_INCLUDE_DIRS})
endif()
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_display_color_converter.cpp
canvas/kis_display_filter.cpp
canvas/kis_exposure_gamma_correction_interface.cpp
canvas/kis_tool_proxy.cpp
canvas/kis_canvas_decoration.cc
canvas/kis_coordinates_converter.cpp
canvas/kis_grid_manager.cpp
canvas/kis_grid_decoration.cpp
canvas/kis_grid_config.cpp
canvas/kis_prescaled_projection.cpp
canvas/kis_qpainter_canvas.cpp
canvas/kis_projection_backend.cpp
canvas/kis_update_info.cpp
canvas/kis_image_patch.cpp
canvas/kis_image_pyramid.cpp
canvas/kis_infinity_manager.cpp
canvas/kis_change_guides_command.cpp
canvas/kis_guides_decoration.cpp
canvas/kis_guides_manager.cpp
canvas/kis_guides_config.cpp
canvas/kis_snap_config.cpp
canvas/kis_snap_line_strategy.cpp
canvas/KisSnapPointStrategy.cpp
canvas/KisSnapPixelStrategy.cpp
canvas/KisMirrorAxisConfig.cpp
dialogs/kis_about_application.cpp
dialogs/kis_dlg_adj_layer_props.cc
dialogs/kis_dlg_adjustment_layer.cc
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_generator_layer.cpp
dialogs/kis_dlg_file_layer.cpp
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_stroke_selection_properties.cpp
dialogs/kis_dlg_image_properties.cc
dialogs/kis_dlg_layer_properties.cc
dialogs/kis_dlg_preferences.cc
dialogs/slider_and_spin_box_sync.cpp
- dialogs/kis_dlg_blacklist_cleanup.cpp
dialogs/kis_dlg_layer_style.cpp
dialogs/kis_dlg_png_import.cpp
dialogs/kis_dlg_import_image_sequence.cpp
dialogs/kis_delayed_save_dialog.cpp
dialogs/KisSessionManagerDialog.cpp
dialogs/KisNewWindowLayoutDialog.cpp
dialogs/KisDlgChangeCloneSource.cpp
flake/kis_node_dummies_graph.cpp
flake/kis_dummies_facade_base.cpp
flake/kis_dummies_facade.cpp
flake/kis_node_shapes_graph.cpp
flake/kis_node_shape.cpp
flake/kis_shape_controller.cpp
flake/kis_shape_layer.cc
flake/kis_shape_layer_canvas.cpp
flake/kis_shape_selection.cpp
flake/kis_shape_selection_canvas.cpp
flake/kis_shape_selection_model.cpp
flake/kis_take_all_shapes_command.cpp
brushhud/kis_uniform_paintop_property_widget.cpp
brushhud/kis_brush_hud.cpp
brushhud/kis_round_hud_button.cpp
brushhud/kis_dlg_brush_hud_config.cpp
brushhud/kis_brush_hud_properties_list.cpp
brushhud/kis_brush_hud_properties_config.cpp
kis_aspect_ratio_locker.cpp
kis_autogradient.cc
kis_bookmarked_configurations_editor.cc
kis_bookmarked_configurations_model.cc
kis_bookmarked_filter_configurations_model.cc
KisPaintopPropertiesBase.cpp
kis_canvas_resource_provider.cpp
kis_derived_resources.cpp
kis_categories_mapper.cpp
kis_categorized_list_model.cpp
kis_categorized_item_delegate.cpp
kis_clipboard.cc
kis_config.cc
KisOcioConfiguration.cpp
kis_control_frame.cpp
kis_composite_ops_model.cc
kis_paint_ops_model.cpp
kis_custom_pattern.cc
kis_file_layer.cpp
kis_change_file_layer_command.h
kis_safe_document_loader.cpp
kis_splash_screen.cpp
kis_filter_manager.cc
kis_filters_model.cc
KisImageBarrierLockerWithFeedback.cpp
kis_image_manager.cc
kis_image_view_converter.cpp
kis_import_catcher.cc
kis_layer_manager.cc
kis_mask_manager.cc
kis_mimedata.cpp
kis_node_commands_adapter.cpp
kis_node_manager.cpp
kis_node_juggler_compressed.cpp
kis_node_selection_adapter.cpp
kis_node_insertion_adapter.cpp
KisNodeDisplayModeAdapter.cpp
kis_node_model.cpp
kis_node_filter_proxy_model.cpp
kis_model_index_converter_base.cpp
kis_model_index_converter.cpp
kis_model_index_converter_show_all.cpp
kis_painting_assistant.cc
kis_painting_assistants_decoration.cpp
KisDecorationsManager.cpp
kis_paintop_box.cc
kis_paintop_option.cpp
kis_paintop_options_model.cpp
kis_paintop_settings_widget.cpp
kis_popup_palette.cpp
kis_png_converter.cpp
kis_preference_set_registry.cpp
KisResourceServerProvider.cpp
- KisResourceBundleServerProvider.cpp
KisSelectedShapesProxy.cpp
kis_selection_decoration.cc
kis_selection_manager.cc
KisSelectionActionsAdapter.cpp
kis_statusbar.cc
kis_zoom_manager.cc
kis_favorite_resource_manager.cpp
kis_workspace_resource.cpp
kis_action.cpp
kis_action_manager.cpp
KisActionPlugin.cpp
kis_canvas_controls_manager.cpp
kis_tooltip_manager.cpp
kis_multinode_property.cpp
kis_stopgradient_editor.cpp
KisWelcomePageWidget.cpp
KisChangeCloneLayersCommand.cpp
kisexiv2/kis_exif_io.cpp
kisexiv2/kis_exiv2.cpp
kisexiv2/kis_iptc_io.cpp
kisexiv2/kis_xmp_io.cpp
opengl/kis_opengl.cpp
opengl/kis_opengl_canvas2.cpp
opengl/kis_opengl_canvas_debugger.cpp
opengl/kis_opengl_image_textures.cpp
opengl/kis_texture_tile.cpp
opengl/kis_opengl_shader_loader.cpp
opengl/kis_texture_tile_info_pool.cpp
opengl/KisOpenGLUpdateInfoBuilder.cpp
opengl/KisOpenGLModeProber.cpp
opengl/KisScreenInformationAdapter.cpp
kis_fps_decoration.cpp
tool/KisToolChangesTracker.cpp
tool/KisToolChangesTrackerData.cpp
tool/kis_selection_tool_helper.cpp
tool/kis_selection_tool_config_widget_helper.cpp
tool/kis_rectangle_constraint_widget.cpp
tool/kis_shape_tool_helper.cpp
tool/kis_tool.cc
tool/kis_delegated_tool_policies.cpp
tool/kis_tool_freehand.cc
tool/kis_speed_smoother.cpp
tool/kis_painting_information_builder.cpp
tool/kis_stabilized_events_sampler.cpp
tool/kis_tool_freehand_helper.cpp
tool/kis_tool_multihand_helper.cpp
tool/kis_figure_painting_tool_helper.cpp
tool/KisAsyncronousStrokeUpdateHelper.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/KisSelectionPropertySlider.cpp
widgets/kis_size_group.cpp
widgets/kis_size_group_p.cpp
widgets/kis_wdg_generator.cpp
widgets/kis_workspace_chooser.cpp
widgets/kis_categorized_list_view.cpp
widgets/kis_widget_chooser.cpp
widgets/kis_tool_button.cpp
widgets/kis_floating_message.cpp
widgets/kis_lod_availability_widget.cpp
widgets/kis_color_label_selector_widget.cpp
widgets/kis_color_filter_combo.cpp
widgets/kis_elided_label.cpp
widgets/kis_stopgradient_slider_widget.cpp
widgets/kis_preset_live_preview_view.cpp
widgets/KisScreenColorPicker.cpp
widgets/KoDualColorButton.cpp
widgets/KoStrokeConfigWidget.cpp
widgets/KoFillConfigWidget.cpp
widgets/KisLayerStyleAngleSelector.cpp
widgets/KisMemoryReportButton.cpp
widgets/KisDitherWidget.cpp
KisPaletteEditor.cpp
dialogs/KisDlgPaletteEditor.cpp
widgets/KisNewsWidget.cpp
widgets/KisGamutMaskToolbar.cpp
utils/kis_document_aware_spin_box_unit_manager.cpp
utils/KisSpinBoxSplineUnitConverter.cpp
utils/KisClipboardUtil.cpp
utils/KisDitherUtil.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
input/kis_zoom_and_rotate_action.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/KisPasteActionFactories.cpp
actions/KisTransformToolActivationCommand.cpp
input/kis_touch_shortcut.cpp
kis_document_undo_store.cpp
kis_gui_context_command.cpp
kis_gui_context_command_p.cpp
input/kis_tablet_debugger.cpp
input/kis_input_profile_manager.cpp
input/kis_input_profile.cpp
input/kis_shortcut_configuration.cpp
input/config/kis_input_configuration_page.cpp
input/config/kis_edit_profiles_dialog.cpp
input/config/kis_input_profile_model.cpp
input/config/kis_input_configuration_page_item.cpp
input/config/kis_action_shortcuts_model.cpp
input/config/kis_input_type_delegate.cpp
input/config/kis_input_mode_delegate.cpp
input/config/kis_input_button.cpp
input/config/kis_input_editor_delegate.cpp
input/config/kis_mouse_input_editor.cpp
input/config/kis_wheel_input_editor.cpp
input/config/kis_key_input_editor.cpp
processing/fill_processing_visitor.cpp
- kis_asl_layer_style_serializer.cpp
- kis_psd_layer_style_resource.cpp
canvas/kis_mirror_axis.cpp
kis_abstract_perspective_grid.cpp
KisApplication.cpp
KisAutoSaveRecoveryDialog.cpp
KisDetailsPane.cpp
KisDocument.cpp
KisCloneDocumentStroke.cpp
kis_node_view_color_scheme.cpp
KisImportExportFilter.cpp
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
KisCanvasWindow.cpp
KisImportExportErrorCode.cpp
KisImportExportAdditionalChecks.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
KisMouseClickEater.cpp
KisDecorationsWrapperLayer.cpp
)
if(WIN32)
# Private headers are needed for:
# * KisDlgCustomTabletResolution
# * KisScreenInformationAdapter
include_directories(SYSTEM ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
qtlockedfile/qtlockedfile_win.cpp
)
if (NOT USE_QT_TABLET_WINDOWS)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/wintab/kis_tablet_support_win.cpp
input/wintab/kis_screen_size_choice_dialog.cpp
input/wintab/kis_tablet_support_win8.cpp
)
else()
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
dialogs/KisDlgCustomTabletResolution.cpp
)
endif()
endif()
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
kis_animation_frame_cache.cpp
kis_animation_cache_populator.cpp
KisAsyncAnimationRendererBase.cpp
KisAsyncAnimationCacheRenderer.cpp
KisAsyncAnimationFramesSavingRenderer.cpp
dialogs/KisAsyncAnimationRenderDialogBase.cpp
dialogs/KisAsyncAnimationCacheRenderDialog.cpp
dialogs/KisAsyncAnimationFramesSaveDialog.cpp
canvas/kis_animation_player.cpp
kis_animation_importer.cpp
KisSyncedAudioPlayback.cpp
KisFrameDataSerializer.cpp
KisFrameCacheStore.cpp
KisFrameCacheSwapper.cpp
KisAbstractFrameCacheSwapper.cpp
KisInMemoryFrameCacheSwapper.cpp
input/wintab/drawpile_tablettester/tablettester.cpp
input/wintab/drawpile_tablettester/tablettest.cpp
)
if (UNIX)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
qtlockedfile/qtlockedfile_unix.cpp
)
endif()
if(APPLE)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
osx.mm
)
endif()
if (ANDROID)
set (kritaui_LIB_SRCS ${kritaui_LIB_SRCS} KisAndroidFileManager.cpp)
endif()
ki18n_wrap_ui(kritaui_LIB_SRCS
widgets/KoFillConfigWidget.ui
widgets/KoStrokeConfigWidget.ui
widgets/KisDitherWidget.ui
forms/wdgdlgpngimport.ui
forms/wdgfullscreensettings.ui
forms/wdgautogradient.ui
forms/wdggeneralsettings.ui
forms/wdgperformancesettings.ui
forms/wdggenerators.ui
forms/wdgbookmarkedconfigurationseditor.ui
forms/wdgapplyprofile.ui
forms/wdgcustompattern.ui
forms/wdglayerproperties.ui
forms/wdgcolorsettings.ui
forms/wdgtabletsettings.ui
forms/wdgcolorspaceselector.ui
forms/wdgcolorspaceselectoradvanced.ui
forms/wdgdisplaysettings.ui
forms/kis_previewwidgetbase.ui
forms/kis_matrix_widget.ui
forms/wdgselectionoptions.ui
forms/wdggeometryoptions.ui
forms/wdgnewimage.ui
forms/wdgimageproperties.ui
forms/wdgmaskfromselection.ui
forms/wdgmasksource.ui
forms/wdgfilterdialog.ui
forms/wdgmetadatamergestrategychooser.ui
forms/wdgpaintoppresets.ui
forms/wdgpaintopsettings.ui
forms/wdgdlggeneratorlayer.ui
forms/wdgdlgfilelayer.ui
forms/wdgfilterselector.ui
forms/wdgfilternodecreation.ui
forms/wdgmultipliersdoublesliderspinbox.ui
forms/wdgnodequerypatheditor.ui
forms/wdgpresetselectorstrip.ui
forms/wdgsavebrushpreset.ui
forms/wdgpreseticonlibrary.ui
- forms/wdgdlgblacklistcleanup.ui
forms/wdgrectangleconstraints.ui
forms/wdgimportimagesequence.ui
forms/wdgstrokeselectionproperties.ui
forms/KisDetailsPaneBase.ui
forms/KisOpenPaneBase.ui
forms/wdgstopgradienteditor.ui
forms/wdgsessionmanager.ui
forms/wdgnewwindowlayout.ui
forms/KisWelcomePage.ui
forms/WdgDlgPaletteEditor.ui
forms/KisNewsPage.ui
forms/wdgGamutMaskToolbar.ui
forms/wdgchangeclonesource.ui
brushhud/kis_dlg_brush_hud_config.ui
dialogs/kis_delayed_save_dialog.ui
input/config/kis_input_configuration_page.ui
input/config/kis_edit_profiles_dialog.ui
input/config/kis_input_configuration_page_item.ui
input/config/kis_mouse_input_editor.ui
input/config/kis_wheel_input_editor.ui
input/config/kis_key_input_editor.ui
layerstyles/wdgBevelAndEmboss.ui
layerstyles/wdgblendingoptions.ui
layerstyles/WdgColorOverlay.ui
layerstyles/wdgContour.ui
layerstyles/wdgdropshadow.ui
layerstyles/WdgGradientOverlay.ui
layerstyles/wdgInnerGlow.ui
layerstyles/wdglayerstyles.ui
layerstyles/WdgPatternOverlay.ui
layerstyles/WdgSatin.ui
layerstyles/WdgStroke.ui
layerstyles/wdgstylesselector.ui
layerstyles/wdgTexture.ui
layerstyles/wdgKisLayerStyleAngleSelector.ui
wdgsplash.ui
input/wintab/kis_screen_size_choice_dialog.ui
input/wintab/drawpile_tablettester/tablettest.ui
)
if(WIN32)
if(USE_QT_TABLET_WINDOWS)
ki18n_wrap_ui(kritaui_LIB_SRCS
dialogs/KisDlgCustomTabletResolution.ui
)
else()
ki18n_wrap_ui(kritaui_LIB_SRCS
input/wintab/kis_screen_size_choice_dialog.ui
)
endif()
endif()
add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} )
generate_export_header(kritaui BASE_NAME kritaui)
target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network
- kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} LibExiv2::LibExiv2
+ kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils kritaresources ${PNG_LIBRARIES} LibExiv2::LibExiv2
)
if (ANDROID)
target_link_libraries(kritaui GLESv3)
target_link_libraries(kritaui Qt5::Gui)
target_link_libraries(kritaui Qt5::AndroidExtras)
endif()
if (HAVE_QT_MULTIMEDIA)
target_link_libraries(kritaui Qt5::Multimedia)
endif()
if (NOT WIN32 AND NOT APPLE AND NOT ANDROID)
target_link_libraries(kritaui ${X11_X11_LIB}
${X11_Xinput_LIB})
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 AND NOT ANDROID)
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/KisActionPlugin.cpp b/libs/ui/KisActionPlugin.cpp
index dba3a391c0..fd25d2830f 100644
--- a/libs/ui/KisActionPlugin.cpp
+++ b/libs/ui/KisActionPlugin.cpp
@@ -1,68 +1,68 @@
/*
* 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 "KisActionPlugin.h"
#include "KisViewManager.h"
#include "kis_action_manager.h"
#include "operations/kis_operation.h"
KisActionPlugin::KisActionPlugin(QObject* parent)
: QObject(parent)
{
m_viewManager = qobject_cast<KisViewManager*>(parent);
KIS_ASSERT_RECOVER_NOOP(m_viewManager);
}
KisActionPlugin::~KisActionPlugin()
{
}
void KisActionPlugin::addAction(const QString& name, KisAction* action)
{
if (m_viewManager) {
m_viewManager->actionManager()->addAction(name, action);
}
}
KisAction* KisActionPlugin::createAction(const QString& name)
{
- if (m_viewManager) {
- return m_viewManager->actionManager()->createAction(name);
- }
- return 0;
+ if (m_viewManager) {
+ return m_viewManager->actionManager()->createAction(name);
+ }
+ return 0;
}
void KisActionPlugin::addUIFactory(KisOperationUIFactory* factory)
{
if (m_viewManager) {
m_viewManager->actionManager()->registerOperationUIFactory(factory);
}
}
void KisActionPlugin::addOperation(KisOperation* operation)
{
if (m_viewManager) {
m_viewManager->actionManager()->registerOperation(operation);
}
}
QPointer<KisViewManager> KisActionPlugin::viewManager() const
{
return m_viewManager;
}
diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp
index e2d9bd5412..e37bffdabe 100644
--- a/libs/ui/KisApplication.cpp
+++ b/libs/ui/KisApplication.cpp
@@ -1,888 +1,933 @@
/*
* 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_MACOS
#include "osx.h"
#endif
#include <QStandardPaths>
#include <QDesktopWidget>
#include <QDir>
#include <QFile>
#include <QLocale>
#include <QMessageBox>
#include <QProcessEnvironment>
#include <QStringList>
#include <QStyle>
#include <QStyleFactory>
#include <QSysInfo>
#include <QTimer>
#include <QWidget>
+#include <QImageReader>
+#include <QImageWriter>
+#include <QThread>
#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 <KoResourceServer.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 <KisUsageLogger.h>
#include <KritaVersionWrapper.h>
#include <dialogs/KisSessionManagerDialog.h>
+#include <KisResourceCacheDb.h>
+#include <KisResourceLocator.h>
+#include <KisResourceLoader.h>
+#include <KisResourceLoaderRegistry.h>
+
+#include <kis_gbr_brush.h>
+#include <kis_png_brush.h>
+#include <kis_svg_brush.h>
+#include <kis_imagepipe_brush.h>
+#include <KoColorSet.h>
+#include <KoSegmentGradient.h>
+#include <KoStopGradient.h>
+#include <KoPattern.h>
+#include <kis_workspace_resource.h>
+#include <KisSessionResource.h>
+#include <resources/KoSvgSymbolCollectionResource.h>
+
#include "widgets/KisScreenColorPicker.h"
#include "KisDlgInternalColorSelector.h"
#include <dialogs/KisAsyncAnimationFramesSaveDialog.h>
#include <kis_image_animation_interface.h>
+#include <kis_psd_layer_style.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};
QVector<QByteArray> earlyRemoteArguments;
-
};
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_MACOS
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("krita"));
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();
}
}
#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(ResourceType::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(ResourceType::PaintOpPresets, "data", "/paintoppresets/");
+ KoResourcePaths::addResourceType(ResourceType::Workspaces, "data", "/workspaces/");
+ KoResourcePaths::addResourceType(ResourceType::WindowLayouts, "data", "/windowlayouts/");
+ KoResourcePaths::addResourceType(ResourceType::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(ResourceType::Patterns, "data", "/patterns/", true);
+ KoResourcePaths::addResourceType(ResourceType::Gradients, "data", "/gradients/");
+ KoResourcePaths::addResourceType(ResourceType::Gradients, "data", "/gradients/", true);
+ KoResourcePaths::addResourceType(ResourceType::Palettes, "data", "/palettes/", true);
KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/");
KoResourcePaths::addResourceType("kis_actions", "data", "/actions");
KoResourcePaths::addResourceType("kis_actions", "data", "/pykrita");
KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc");
KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/");
- KoResourcePaths::addResourceType("ko_effects", "data", "/effects/");
+ KoResourcePaths::addResourceType(ResourceType::FilterEffects, "data", "/effects/");
KoResourcePaths::addResourceType("tags", "data", "/tags/");
KoResourcePaths::addResourceType("templates", "data", "/templates");
KoResourcePaths::addResourceType("pythonscripts", "data", "/pykrita");
- KoResourcePaths::addResourceType("symbols", "data", "/symbols");
+ KoResourcePaths::addResourceType(ResourceType::Symbols, "data", "/symbols");
KoResourcePaths::addResourceType("preset_icons", "data", "/preset_icons");
- KoResourcePaths::addResourceType("ko_gamutmasks", "data", "/gamutmasks/", true);
+ KoResourcePaths::addResourceType(ResourceType::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"));
+ // KoResourcePaths::addResourceDir(ResourceType::Gradients, "/usr/share/create/gradients/gimp");
+ // KoResourcePaths::addResourceDir(ResourceType::Gradients, QDir::homePath() + QString("/.create/gradients/gimp"));
+ // KoResourcePaths::addResourceDir(ResourceType::Patterns, "/usr/share/create/patterns/gimp");
+ // KoResourcePaths::addResourceDir(ResourceType::Patterns, QDir::homePath() + QString("/.create/patterns/gimp"));
+ // KoResourcePaths::addResourceDir(ResourceType::Brushes, "/usr/share/create/brushes/gimp");
+ // KoResourcePaths::addResourceDir(ResourceType::Brushes, QDir::homePath() + QString("/.create/brushes/gimp"));
+ // KoResourcePaths::addResourceDir(ResourceType::Palettes, "/usr/share/create/swatches");
+ // KoResourcePaths::addResourceDir(ResourceType::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/");
+ // between 4.2.x and 4.3.0 there was a change from 'taskset' to 'tasksets'
+ // so to make older resource folders compatible with the new version, let's rename the folder
+ // so no tasksets are lost.
+ if (d.exists(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/")) {
+ d.rename(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/",
+ QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tasksets/");
+ }
+ d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tasksets/");
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()
+bool KisApplication::registerResources()
{
- // 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();
+ KisResourceLoaderRegistry *reg = KisResourceLoaderRegistry::instance();
+
+ reg->add(new KisResourceLoader<KisPaintOpPreset>(ResourceType::PaintOpPresets, ResourceType::PaintOpPresets, i18n("Brush presets"), QStringList() << "application/x-krita-paintoppreset"));
+
+ reg->add(new KisResourceLoader<KisGbrBrush>(ResourceSubType::GbrBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/x-gimp-brush"));
+ reg->add(new KisResourceLoader<KisImagePipeBrush>(ResourceSubType::GihBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/x-gimp-brush-animated"));
+ reg->add(new KisResourceLoader<KisSvgBrush>(ResourceSubType::SvgBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/svg+xml"));
+ reg->add(new KisResourceLoader<KisPngBrush>(ResourceSubType::PngBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/png"));
+
+ reg->add(new KisResourceLoader<KoSegmentGradient>(ResourceSubType::SegmentedGradients, ResourceType::Gradients, i18n("Gradients"), QStringList() << "application/x-gimp-gradient"));
+ reg->add(new KisResourceLoader<KoStopGradient>(ResourceSubType::StopGradients, ResourceType::Gradients, i18n("Gradients"), QStringList() << "application/x-karbon-gradient" << "image/svg+xml"));
+
+ reg->add(new KisResourceLoader<KoColorSet>(ResourceType::Palettes, ResourceType::Palettes, i18n("Palettes"),
+ QStringList() << KisMimeDatabase::mimeTypeForSuffix("kpl")
+ << KisMimeDatabase::mimeTypeForSuffix("gpl")
+ << KisMimeDatabase::mimeTypeForSuffix("pal")
+ << KisMimeDatabase::mimeTypeForSuffix("act")
+ << KisMimeDatabase::mimeTypeForSuffix("aco")
+ << KisMimeDatabase::mimeTypeForSuffix("css")
+ << KisMimeDatabase::mimeTypeForSuffix("colors")
+ << KisMimeDatabase::mimeTypeForSuffix("xml")
+ << KisMimeDatabase::mimeTypeForSuffix("sbz")));
+
+ QList<QByteArray> src = QImageReader::supportedMimeTypes();
+ QStringList allImageMimes;
+ Q_FOREACH(const QByteArray ba, src) {
+ if (QImageWriter::supportedMimeTypes().contains(ba)) {
+ allImageMimes << QString::fromUtf8(ba);
+ }
+ }
+ allImageMimes << KisMimeDatabase::mimeTypeForSuffix("pat");
+
+ reg->add(new KisResourceLoader<KoPattern>(ResourceType::Patterns, ResourceType::Patterns, i18n("Patterns"), allImageMimes));
+ reg->add(new KisResourceLoader<KisWorkspaceResource>(ResourceType::Workspaces, ResourceType::Workspaces, i18n("Workspaces"), QStringList() << "application/x-krita-workspace"));
+ reg->add(new KisResourceLoader<KoSvgSymbolCollectionResource>(ResourceType::Symbols, ResourceType::Symbols, i18n("SVG symbol libraries"), QStringList() << "image/svg+xml"));
+ reg->add(new KisResourceLoader<KisWindowLayoutResource>(ResourceType::WindowLayouts, ResourceType::WindowLayouts, i18n("Window layouts"), QStringList() << "application/x-krita-windowlayout"));
+ reg->add(new KisResourceLoader<KisSessionResource>(ResourceType::Sessions, ResourceType::Sessions, i18n("Sessions"), QStringList() << "application/x-krita-session"));
+ reg->add(new KisResourceLoader<KoGamutMask>(ResourceType::GamutMasks, ResourceType::GamutMasks, i18n("Gamut masks"), QStringList() << "application/x-krita-gamutmasks"));
+
+ reg->add(new KisResourceLoader<KisPSDLayerStyle>(ResourceType::LayerStyles,
+ ResourceType::LayerStyles,
+ ResourceType::LayerStyles,
+ QStringList() << "application/x-photoshop-style"));
+
+ if (!KisResourceCacheDb::initialize(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))) {
+ QMessageBox::critical(0, i18nc("@title:window", "Krita: Fatal error"), i18n("%1\n\nKrita will quit now.").arg(KisResourceCacheDb::lastError()));
+ //return false;
+ }
- setSplashScreenLoadingText(i18n("Loading Bundles..."));
- processEvents();
- KisResourceBundleServerProvider::instance();
-}
+ KisResourceLocator::LocatorError r = KisResourceLocator::instance()->initialize(KoResourcePaths::getApplicationRoot() + "/share/krita");
+ connect(KisResourceLocator::instance(), SIGNAL(progressMessage(const QString&)), this, SLOT(setSplashScreenLoadingText(const QString&)));
+ if (r != KisResourceLocator::LocatorError::Ok ) {
+ QMessageBox::critical(0, i18nc("@title:window", "Krita: Fatal error"), KisResourceLocator::instance()->errorMessages().join('\n') + i18n("\n\nKrita will quit now."));
+ //return false;
+ }
-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();
+ return true;
}
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" && opengl != "TRY_OPENGL") {
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 bool exportSequence = args.exportSequence();
const QString exportFileName = args.exportFileName();
d->batchRun = (exportAs || exportSequence || !exportFileName.isEmpty());
const bool needsMainWindow = (!exportAs && !exportSequence);
// only show the mainWindow when no command-line mode option is passed
bool showmainWindow = (!exportAs && !exportSequence); // 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();
+ if (!registerResources()) {
+ return false;
+ }
// 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());
+ KisWindowLayoutResourceSP 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());
+ KisWorkspaceResourceSP workspace = rserver->resourceByName(args.workspace());
if (workspace) {
- d->mainWindow->restoreWorkspace(workspace);
+ d->mainWindow->restoreWorkspace(workspace->resourceId());
}
}
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 false;
}
KisDocument *doc = kisPart->createDocument();
doc->setFileBatchMode(d->batchRun);
bool result = doc->openUrl(QUrl::fromLocalFile(fileName));
if (!result) {
errKrita << "Could not load " << fileName << ":" << doc->errorMessage();
QTimer::singleShot(0, this, SLOT(quit()));
return false;
}
if (exportFileName.isEmpty()) {
errKrita << "Export destination is not specified for" << fileName << "Please specify export destination with --export-filename option";
QTimer::singleShot(0, this, SLOT(quit()));
return false;
}
qApp->processEvents(); // For vector layers to be updated
doc->setFileBatchMode(true);
if (!doc->exportDocumentSync(QUrl::fromLocalFile(exportFileName), outputMimetype.toLatin1())) {
errKrita << "Could not export " << fileName << "to" << exportFileName << ":" << doc->errorMessage();
}
QTimer::singleShot(0, this, SLOT(quit()));
return true;
}
else if (exportSequence) {
KisDocument *doc = kisPart->createDocument();
doc->setFileBatchMode(d->batchRun);
doc->openUrl(QUrl::fromLocalFile(fileName));
qApp->processEvents(); // For vector layers to be updated
if (!doc->image()->animationInterface()->hasAnimation()) {
errKrita << "This file has no animation." << endl;
QTimer::singleShot(0, this, SLOT(quit()));
return false;
}
doc->setFileBatchMode(true);
int sequenceStart = 0;
KisAsyncAnimationFramesSaveDialog exporter(doc->image(),
doc->image()->animationInterface()->fullClipRange(),
exportFileName,
sequenceStart,
0);
exporter.setBatchMode(d->batchRun);
KisAsyncAnimationFramesSaveDialog::Result result =
exporter.regenerateRange(0);
if (result == KisAsyncAnimationFramesSaveDialog::RenderFailed) {
errKrita << i18n("Failed to render animation frames!") << endl;
}
QTimer::singleShot(0, this, SLOT(quit()));
return true;
}
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);
}
Q_FOREACH(const QByteArray &message, d->earlyRemoteArguments) {
executeRemoteArguments(message, d->mainWindow);
}
KisUsageLogger::writeSysInfo(KisUsageLogger::screenInformation());
// not calling this before since the program will quit there.
return true;
}
KisApplication::~KisApplication()
{
+ KisResourceCacheDb::deleteTemporaryResources();
}
void KisApplication::setSplashScreen(QWidget *splashScreen)
{
d->splashScreen = qobject_cast<KisSplashScreen*>(splashScreen);
}
-void KisApplication::setSplashScreenLoadingText(QString textToLoad)
+void KisApplication::setSplashScreenLoadingText(const 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::executeRemoteArguments(QByteArray message, KisMainWindow *mainWindow)
{
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, mainWindow);
}
else if (QFile(filename).exists()) {
KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
mainWindow->openDocument(QUrl::fromLocalFile(filename), flags);
}
}
}
}
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 && KisPart::instance()->mainWindows().size() > 0) {
mw = KisPart::instance()->mainWindows().first();
}
if (!mw) {
d->earlyRemoteArguments << message;
return;
}
executeRemoteArguments(message, mw);
}
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;
#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);
// Visible 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)) {
KisUsageLogger::log(QString("Removing autosave file %1").arg(dir.absolutePath() + "/" + 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/KisApplication.h b/libs/ui/KisApplication.h
index 0b6a7c4dd0..f07c6817fa 100644
--- a/libs/ui/KisApplication.h
+++ b/libs/ui/KisApplication.h
@@ -1,125 +1,122 @@
/*
* 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.
*/
#ifndef KIS_APPLICATION_H
#define KIS_APPLICATION_H
#include <QPointer>
#include <QScopedPointer>
#include <qtsingleapplication/qtsingleapplication.h>
#include "kritaui_export.h"
class KisMainWindow;
class KisApplicationPrivate;
class QWidget;
class KisApplicationArguments;
class KisAutoSaveRecoveryDialog;
#include <KisImportExportManager.h>
/**
* @brief Base class for the %Krita app
*
* This class handles arguments given on the command line and
* shows a generic about dialog for the Krita app.
*
* In addition it adds the standard directories where Krita
* can find its images etc.
*
* If the last mainwindow becomes closed, KisApplication automatically
* calls QApplication::quit.
*/
class KRITAUI_EXPORT KisApplication : public QtSingleApplication
{
Q_OBJECT
public:
/**
* Creates an application object, adds some standard directories and
* initializes kimgio.
*/
explicit KisApplication(const QString &key, int &argc, char **argv);
/**
* Destructor.
*/
~KisApplication() override;
/**
* Call this to start the application.
*
* Parses command line arguments and creates the initial main windowss and docs
* from them (or an empty doc if no cmd-line argument is specified ).
*
* You must call this method directly before calling QApplication::exec.
*
* It is valid behaviour not to call this method at all. In this case you
* have to process your command line parameters by yourself.
*/
virtual bool start(const KisApplicationArguments &args);
/**
* Checks if user is holding ctrl+alt+shift keys and asks if the settings file should be cleared.
*
* Typically called during startup before reading the config.
*/
void askClearConfig();
/**
* Tell KisApplication to show this splashscreen when you call start();
* when start returns, the splashscreen is hidden. Use KSplashScreen
* to have the splash show correctly on Xinerama displays.
*/
void setSplashScreen(QWidget *splash);
-
- void setSplashScreenLoadingText(QString);
-
void hideSplashScreen();
/// Overridden to handle exceptions from event handlers.
bool notify(QObject *receiver, QEvent *event) override;
void addResourceTypes();
- void loadResources();
- void loadResourceTags();
+ bool registerResources();
void loadPlugins();
void loadGuiPlugins();
void initializeGlobals(const KisApplicationArguments &args);
public Q_SLOTS:
void executeRemoteArguments(QByteArray message, KisMainWindow *mainWindow);
void remoteArguments(QByteArray message, QObject*socket);
void fileOpenRequested(const QString & url);
+ void setSplashScreenLoadingText(const QString&);
private:
/// @return the number of autosavefiles opened
void checkAutosaveFiles();
bool createNewDocFromTemplate(const QString &fileName, KisMainWindow *m_mainWindow);
void clearConfig();
private:
class Private;
QScopedPointer<Private> d;
class ResetStarting;
friend class ResetStarting;
};
#endif
diff --git a/libs/ui/KisDocument.cpp b/libs/ui/KisDocument.cpp
index 20cd0b4bcb..02f582d65d 100644
--- a/libs/ui/KisDocument.cpp
+++ b/libs/ui/KisDocument.cpp
@@ -1,2268 +1,2326 @@
/* 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 <KisImportExportErrorCode.h>
#include <KoDocumentResourceManager.h>
#include <KoMD5Generator.h>
+#include <KisResourceStorage.h>
+#include <KisResourceLocator.h>
+#include <KisResourceTypes.h>
+#include <KisGlobalResourcesInterface.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>
+#include <QUuid>
// 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"
#include "kis_selection_mask.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"
#include <KisMirrorAxisConfig.h>
#include <KisDecorationsWrapperLayer.h>
#include "kis_simple_stroke_strategy.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)
: q(_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)
: q(_q)
, docInfo(new KoDocumentInfo(*rhs.docInfo, _q))
, importExportManager(new KisImportExportManager(_q))
, autoSaveTimer(new QTimer(_q))
, undoStack(new UndoStack(_q))
, nserver(new KisNameServer(*rhs.nserver))
, preActivatedNode(0) // the node is from another hierarchy!
, imageIdleWatcher(2000 /*ms*/)
, savingLock(&savingMutex)
{
copyFromImpl(rhs, _q, CONSTRUCT);
}
~Private() {
// Don't delete m_d->shapeController because it's in a QObject hierarchy.
delete nserver;
}
KisDocument *q = 0;
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;
KisMirrorAxisConfig mirrorAxisConfig;
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;
- QList<KoColorSet*> paletteList;
- bool ownsPaletteList = false;
-
+ KisSharedPtr<KisReferenceImagesLayer> referenceImagesLayer;
KisGridConfig gridConfig;
StdLockableWrapper<QMutex> savingLock;
bool modifiedWhileSaving = false;
QScopedPointer<KisDocument> backgroundSaveDocument;
QPointer<KoUpdater> savingUpdater;
QFuture<KisImportExportErrorCode> childSavingFuture;
KritaUtils::ExportFileJob backgroundSaveJob;
bool isRecovered = false;
bool batchMode { false };
+ QString documentStorageID {QUuid::createUuid().toString()};
+ KisResourceStorageSP documentResourceStorage;
+
void syncDecorationsWrapperLayerState();
void setImageAndInitIdleWatcher(KisImageSP _image) {
image = _image;
imageIdleWatcher.setTrackedImage(image);
if (image) {
imageIdleConnection.reset(
new KisSignalAutoConnection(
&imageIdleWatcher, SIGNAL(startedIdleMode()),
image.data(), SLOT(explicitRegenerateLevelOfDetail())));
}
}
void copyFrom(const Private &rhs, KisDocument *q);
void copyFromImpl(const Private &rhs, KisDocument *q, KisDocument::CopyPolicy policy);
/// clones the palette list oldList
/// the ownership of the returned KoColorSet * belongs to the caller
- QList<KoColorSet *> clonePaletteList(const QList<KoColorSet *> &oldList);
-
class StrippedSafeSavingLocker;
};
void KisDocument::Private::syncDecorationsWrapperLayerState()
{
if (!this->image) return;
KisImageSP image = this->image;
KisDecorationsWrapperLayerSP decorationsLayer =
KisLayerUtils::findNodeByType<KisDecorationsWrapperLayer>(image->root());
const bool needsDecorationsWrapper =
gridConfig.showGrid() || (guidesConfig.showGuides() && guidesConfig.hasGuides()) || !assistants.isEmpty();
struct SyncDecorationsWrapperStroke : public KisSimpleStrokeStrategy {
SyncDecorationsWrapperStroke(KisDocument *document, bool needsDecorationsWrapper)
: KisSimpleStrokeStrategy(QLatin1String("sync-decorations-wrapper"),
kundo2_noi18n("start-isolated-mode")),
m_document(document),
m_needsDecorationsWrapper(needsDecorationsWrapper)
{
this->enableJob(JOB_INIT, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
setClearsRedoOnStart(false);
}
void initStrokeCallback() {
KisDecorationsWrapperLayerSP decorationsLayer =
KisLayerUtils::findNodeByType<KisDecorationsWrapperLayer>(m_document->image()->root());
if (m_needsDecorationsWrapper && !decorationsLayer) {
m_document->image()->addNode(new KisDecorationsWrapperLayer(m_document));
} else if (!m_needsDecorationsWrapper && decorationsLayer) {
m_document->image()->removeNode(decorationsLayer);
}
}
private:
KisDocument *m_document = 0;
bool m_needsDecorationsWrapper = false;
};
KisStrokeId id = image->startStroke(new SyncDecorationsWrapperStroke(q, needsDecorationsWrapper));
image->endStroke(id);
}
void KisDocument::Private::copyFrom(const Private &rhs, KisDocument *q)
{
copyFromImpl(rhs, q, KisDocument::REPLACE);
}
void KisDocument::Private::copyFromImpl(const Private &rhs, KisDocument *q, KisDocument::CopyPolicy policy)
{
if (policy == REPLACE) {
delete docInfo;
}
docInfo = (new KoDocumentInfo(*rhs.docInfo, q));
unit = rhs.unit;
mimeType = rhs.mimeType;
outputMimeType = rhs.outputMimeType;
if (policy == REPLACE) {
q->setGuidesConfig(rhs.guidesConfig);
q->setMirrorAxisConfig(rhs.mirrorAxisConfig);
q->setModified(rhs.modified);
q->setAssistants(KisPaintingAssistant::cloneAssistantList(rhs.assistants));
q->setGridConfig(rhs.gridConfig);
} else {
// in CONSTRUCT mode, we cannot use the functions of KisDocument
// because KisDocument does not yet have a pointer to us.
guidesConfig = rhs.guidesConfig;
mirrorAxisConfig = rhs.mirrorAxisConfig;
modified = rhs.modified;
assistants = KisPaintingAssistant::cloneAssistantList(rhs.assistants);
gridConfig = rhs.gridConfig;
}
m_bAutoDetectedMime = rhs.m_bAutoDetectedMime;
m_url = rhs.m_url;
m_file = rhs.m_file;
readwrite = rhs.readwrite;
firstMod = rhs.firstMod;
lastMod = rhs.lastMod;
// XXX: the display properties will be shared between different snapshots
globalAssistantsColor = rhs.globalAssistantsColor;
-
- if (policy == REPLACE) {
- QList<KoColorSet *> newPaletteList = clonePaletteList(rhs.paletteList);
- q->setPaletteList(newPaletteList, /* emitSignal = */ true);
- // we still do not own palettes if we did not
- } else {
- paletteList = rhs.paletteList;
- }
-
batchMode = rhs.batchMode;
-}
-QList<KoColorSet *> KisDocument::Private::clonePaletteList(const QList<KoColorSet *> &oldList)
-{
- QList<KoColorSet *> newList;
- Q_FOREACH (KoColorSet *palette, oldList) {
- newList << new KoColorSet(*palette);
+ // CHECK THIS! This is what happened to the palette list -- but is it correct here as well? Ask Dmitry!!!
+ // if (policy == REPLACE) {
+ // QList<KoColorSetSP> newPaletteList = clonePaletteList(rhs.paletteList);
+ // q->setPaletteList(newPaletteList, /* emitSignal = */ true);
+ // // we still do not own palettes if we did not
+ // } else {
+ // paletteList = rhs.paletteList;
+ // }
+
+ if (rhs.documentResourceStorage) {
+ if (policy == REPLACE) {
+ // Clone the resources, but don't add them to the database, only the editable
+ // version of the document should have those resources in the database.
+ documentResourceStorage = rhs.documentResourceStorage->clone();
+ }
+ else {
+ documentResourceStorage = rhs.documentResourceStorage;
+ }
}
- return newList;
+
}
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(QEventLoop::ExcludeUserInputEvents);
// one more try...
m_locked = std::try_lock(m_imageLock, m_savingLock) < 0;
}
}
~StrippedSafeSavingLocker() {
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()
+KisDocument::KisDocument(bool addStorage)
: 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());
+
+ if (addStorage) {
+ d->documentResourceStorage.reset(new KisResourceStorage(d->documentStorageID));
+ KisResourceLocator::instance()->addStorage(d->documentStorageID, d->documentResourceStorage);
+ }
+
// preload the krita resources
KisResourceServerProvider::instance();
d->shapeController = new KisShapeController(this, d->nserver);
d->koShapeController = new KoShapeController(0, d->shapeController);
d->shapeController->resourceManager()->setGlobalShapeController(d->koShapeController);
slotConfigChanged();
}
KisDocument::KisDocument(const KisDocument &rhs)
: QObject(),
d(new Private(*rhs.d, this))
{
copyFromDocumentImpl(rhs, CONSTRUCT);
}
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());
}
-
- if (d->ownsPaletteList) {
- qDeleteAll(d->paletteList);
+ if (KisResourceLocator::instance()->hasStorage(d->documentStorageID)) {
+ KisResourceLocator::instance()->removeStorage(d->documentStorageID);
}
delete d;
}
+QString KisDocument::uniqueID() const
+{
+ return d->documentStorageID;
+}
+
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, ImportExportCodes::NoAccessToWrite,
i18n("%1 cannot be written to. Please save under a different name.", job.filePath));
//return ImportExportCodes::NoAccessToWrite;
return false;
}
KisConfig cfg(true);
if (cfg.backupFile() && filePathInfo.exists()) {
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) {
if (!KBackup::simpleBackupFile(job.filePath, backupDir, suffix)) {
qWarning() << "Failed to create simple backup file!" << job.filePath << backupDir << suffix;
KisUsageLogger::log(QString("Failed to create a simple backup for %1 in %2.").arg(job.filePath).arg(backupDir.isEmpty() ? "the same location as the file" : backupDir));
return false;
}
else {
KisUsageLogger::log(QString("Create a simple backup for %1 in %2.").arg(job.filePath).arg(backupDir.isEmpty() ? "the same location as the file" : backupDir));
}
}
else if (numOfBackupsKept > 1) {
if (!KBackup::numberedBackupFile(job.filePath, backupDir, suffix, numOfBackupsKept)) {
qWarning() << "Failed to create numbered backup file!" << job.filePath << backupDir << suffix;
KisUsageLogger::log(QString("Failed to create a numbered backup for %2.").arg(job.filePath).arg(backupDir.isEmpty() ? "the same location as the file" : backupDir));
return false;
}
else {
KisUsageLogger::log(QString("Create a simple backup for %1 in %2.").arg(job.filePath).arg(backupDir.isEmpty() ? "the same location as the file" : backupDir));
}
}
}
//KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!job.mimeType.isEmpty(), false);
if (job.mimeType.isEmpty()) {
KisImportExportErrorCode error = ImportExportCodes::FileFormatIncorrect;
slotCompleteSavingDocument(job, error, error.errorMessage());
return false;
}
const QString actionName =
job.flags & KritaUtils::SaveIsExporting ?
i18n("Exporting Document...") :
i18n("Saving Document...");
bool started =
initiateSavingInBackground(actionName,
this, SLOT(slotCompleteSavingDocument(KritaUtils::ExportFileJob, KisImportExportErrorCode ,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)
{
using namespace KritaUtils;
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).isOk()) {
qWarning() << "serializeToByteArray():: Could not export to our native format";
}
return byteArray;
}
void KisDocument::slotCompleteSavingDocument(const KritaUtils::ExportFileJob &job, KisImportExportErrorCode status, const QString &errorMessage)
{
if (status.isCancelled())
return;
const QString fileName = QFileInfo(job.filePath).fileName();
if (!status.isOk()) {
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(QEventLoop::ExcludeUserInputEvents);
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);
}
KisDocument *KisDocument::lockAndCreateSnapshot()
{
KisDocument *doc = lockAndCloneForSaving();
if (doc) {
- // clone palette list
- doc->d->paletteList = doc->d->clonePaletteList(doc->d->paletteList);
- doc->d->ownsPaletteList = true;
+ // clone the local resource storage and its contents -- that is, the old palette list
+ if (doc->d->documentResourceStorage) {
+ doc->d->documentResourceStorage = doc->d->documentResourceStorage->clone();
+ }
}
return doc;
}
void KisDocument::copyFromDocument(const KisDocument &rhs)
{
copyFromDocumentImpl(rhs, REPLACE);
}
void KisDocument::copyFromDocumentImpl(const KisDocument &rhs, CopyPolicy policy)
{
if (policy == REPLACE) {
d->copyFrom(*(rhs.d), this);
d->undoStack->clear();
} else {
// in CONSTRUCT mode, d should be already initialized
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()));
d->shapeController = new KisShapeController(this, d->nserver);
d->koShapeController = new KoShapeController(0, d->shapeController);
d->shapeController->resourceManager()->setGlobalShapeController(d->koShapeController);
}
setObjectName(rhs.objectName());
slotConfigChanged();
if (rhs.d->image) {
if (policy == REPLACE) {
d->image->barrierLock(/* readOnly = */ false);
rhs.d->image->barrierLock(/* readOnly = */ true);
d->image->copyFromImage(*(rhs.d->image));
d->image->unlock();
rhs.d->image->unlock();
setCurrentImage(d->image, /* forceInitialUpdate = */ true);
} else {
// clone the image with keeping the GUIDs of the layers intact
// NOTE: we expect the image to be locked!
setCurrentImage(rhs.image()->clone(/* exactCopy = */ true), /* forceInitialUpdate = */ false);
}
}
if (rhs.d->preActivatedNode) {
QQueue<KisNodeSP> linearizedNodes;
KisLayerUtils::recursiveApplyNodes(rhs.d->image->root(),
[&linearizedNodes](KisNodeSP node) {
linearizedNodes.enqueue(node);
});
KisLayerUtils::recursiveApplyNodes(d->image->root(),
[&linearizedNodes, &rhs, this](KisNodeSP node) {
KisNodeSP refNode = linearizedNodes.dequeue();
if (rhs.d->preActivatedNode.data() == refNode.data()) {
d->preActivatedNode = node;
}
});
}
// reinitialize references' signal connection
KisReferenceImagesLayerSP referencesLayer = this->referenceImagesLayer();
setReferenceImagesLayer(referencesLayer, false);
KisDecorationsWrapperLayerSP decorationsLayer =
KisLayerUtils::findNodeByType<KisDecorationsWrapperLayer>(d->image->root());
if (decorationsLayer) {
decorationsLayer->setDocument(this);
}
if (policy == REPLACE) {
setModified(true);
}
}
bool KisDocument::exportDocumentSync(const QUrl &url, const QByteArray &mimeType, KisPropertiesConfigurationSP exportConfiguration)
{
{
/**
* The caller guarantees that no one else uses the document (usually,
* it is a temporary document created specifically for exporting), so
* we don't need to copy or lock the document. Instead we should just
* ensure the barrier lock is synced and then released.
*/
Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image);
if (!locker.successfullyLocked()) {
return false;
}
}
d->savingImage = d->image;
const QString fileName = url.toLocalFile();
KisImportExportErrorCode status =
d->importExportManager->
exportDocument(fileName, fileName, mimeType, false, exportConfiguration);
d->savingImage = 0;
return status.isOk();
}
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;
}
auto waitForImage = [] (KisImageSP image) {
KisMainWindow *window = KisPart::instance()->currentMainwindow();
if (window) {
if (window->viewManager()) {
window->viewManager()->blockUntilOperationsFinishedForced(image);
}
}
};
{
KisNodeSP newRoot = clonedDocument->image()->root();
KIS_SAFE_ASSERT_RECOVER(!KisLayerUtils::hasDelayedNodeWithUpdates(newRoot)) {
KisLayerUtils::forceAllDelayedNodesUpdate(newRoot);
waitForImage(clonedDocument->image());
}
}
if (clonedDocument->image()->hasOverlaySelectionMask()) {
clonedDocument->image()->setOverlaySelectionMask(0);
waitForImage(clonedDocument->image());
}
KIS_SAFE_ASSERT_RECOVER(clonedDocument->image()->isIdle()) {
waitForImage(clonedDocument->image());
}
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(KisImportExportErrorCode, QString)),
this,
SLOT(slotChildCompletedSavingInBackground(KisImportExportErrorCode, QString)));
connect(this, SIGNAL(sigCompleteBackgroundSaving(KritaUtils::ExportFileJob, KisImportExportErrorCode, QString)),
receiverObject, receiverMethod, Qt::UniqueConnection);
bool started =
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(KisImportExportErrorCode status, const QString &errorMessage)
{
KIS_ASSERT_RECOVER_RETURN(isSaving());
KIS_ASSERT_RECOVER(d->backgroundSaveDocument) {
d->savingMutex.unlock();
return;
}
if (d->backgroundSaveJob.flags & KritaUtils::SaveInAutosaveMode) {
d->backgroundSaveDocument->d->isAutosaving = false;
}
d->backgroundSaveDocument.take()->deleteLater();
KIS_ASSERT_RECOVER(d->backgroundSaveJob.isValid()) {
d->savingMutex.unlock();
return;
}
const KritaUtils::ExportFileJob job = d->backgroundSaveJob;
d->backgroundSaveJob = KritaUtils::ExportFileJob();
// unlock at the very end
d->savingMutex.unlock();
QFileInfo fi(job.filePath);
KisUsageLogger::log(QString("Completed saving %1 (mime: %2). Result: %3. Size: %4. MD5 Hash: %5")
.arg(job.filePath)
.arg(QString::fromLatin1(job.mimeType))
.arg(!status.isOk() ? exportErrorToUserMessage(status, errorMessage) : "OK")
.arg(fi.size())
.arg(QString::fromLatin1(KoMD5Generator().generateHash(job.filePath).toHex())));
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);
KisUsageLogger::log(QString("Autosaving: %1").arg(autoSaveFileName));
const bool hadClonedDocument = bool(optionalClonedDocument);
bool started = false;
if (d->image->isIdle() || hadClonedDocument) {
started = initiateSavingInBackground(i18n("Autosaving..."),
this, SLOT(slotCompleteAutoSaving(KritaUtils::ExportFileJob, KisImportExportErrorCode, 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, KisImportExportErrorCode status, const QString &errorMessage)
{
Q_UNUSED(job);
const QString fileName = QFileInfo(job.filePath).fileName();
if (!status.isOk()) {
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);
}
}
KisImportExportErrorCode initializationStatus(ImportExportCodes::OK);
d->childSavingFuture =
d->importExportManager->exportDocumentAsyc(location,
realLocation,
mimeType,
initializationStatus,
showWarnings,
exportConfiguration);
if (!initializationStatus.isOk()) {
if (d->savingUpdater) {
d->savingUpdater->cancel();
}
d->savingImage.clear();
emit sigBackgroundSavingFinished(initializationStatus, initializationStatus.errorMessage());
return false;
}
typedef QFutureWatcher<KisImportExportErrorCode> 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(ImportExportCodes::InternalError, "");
return;
}
KisImportExportErrorCode status =
d->childSavingFuture.result();
const QString errorMessage = status.errorMessage();
d->savingImage.clear();
d->childSavingFuture = QFuture<KisImportExportErrorCode>();
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");
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() || autosavePattern1.match(filename).hasMatch() || autosavePattern2.match(filename).hasMatch() || !fi.isWritable()) {
// 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%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%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%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();
//qDebug() <<"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 :
KisUsageLogger::log(QString("Removing autosave file: %1").arg(asf));
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 (ret) {
if (!(flags & DontAddToRecent)) {
KisPart::instance()->addRecentURLToAllMainWindows(_url);
}
// 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()) && !fileBatchMode()) {
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();
KoUpdaterPtr updater;
if (window && window->viewManager()) {
updater = window->viewManager()->createUnthreadedUpdater(i18n("Opening document"));
d->importExportManager->setUpdater(updater);
}
KisImportExportErrorCode status = d->importExportManager->importDocument(localFilePath(), typeName);
if (!status.isOk()) {
if (window && window->viewManager()) {
updater->cancel();
}
QString msg = status.errorMessage();
if (!msg.isEmpty() && !fileBatchMode()) {
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() && !fileBatchMode()) {
DlgLoadMessages dlg(i18nc("@title:window", "Krita"),
i18n("There were problems opening %1.", prettyPathOrUrl()),
warningMessage().split("\n"));
dlg.exec();
setUrl(QUrl());
}
setMimeTypeAfterLoading(typeName);
d->syncDecorationsWrapperLayerState();
emit sigLoadingFinished();
undoStack()->clear();
return true;
}
void KisDocument::autoSaveOnPause()
{
if (!d->modified || !d->modifiedAfterAutosave)
return;
const QString autoSaveFileName = generateAutoSaveFileName(localFilePath());
QUrl url("file:/" + autoSaveFileName);
bool started = exportDocumentSync(url, nativeFormatMimeType());
if (started)
{
d->modifiedAfterAutosave = false;
dbgAndroid << "autoSaveOnPause successful";
}
else
{
qWarning() << "Could not auto-save when paused";
}
}
// 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)
{
// Eliminate any auto-save file
QString asf = generateAutoSaveFileName(autosaveBaseName); // the one in the current dir
if (QFile::exists(asf)) {
KisUsageLogger::log(QString("Removing autosave file: %1").arg(asf));
QFile::remove(asf);
}
asf = generateAutoSaveFileName(QString()); // and the one in $HOME
if (QFile::exists(asf)) {
KisUsageLogger::log(QString("Removing autosave file: %1").arg(asf));
QFile::remove(asf);
}
QList<QRegularExpression> expressions;
expressions << QRegularExpression("^\\..+-autosave.kra$")
<< QRegularExpression("^.+-autosave.kra$");
Q_FOREACH(const QRegularExpression &rex, expressions) {
if (wasRecovered &&
!autosaveBaseName.isEmpty() &&
rex.match(QFileInfo(autosaveBaseName).fileName()).hasMatch() &&
QFile::exists(autosaveBaseName)) {
KisUsageLogger::log(QString("Removing autosave file: %1").arg(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);
if (d->undoStack->undoLimit() != cfg.undoStackLimit()) {
if (!d->undoStack->isClean()) {
d->undoStack->clear();
}
d->undoStack->setUndoLimit(cfg.undoStackLimit());
}
d->autoSaveDelay = cfg.autoSaveInterval();
setNormalAutoSaveInterval();
}
void KisDocument::slotImageRootChanged()
{
d->syncDecorationsWrapperLayerState();
}
void KisDocument::clearUndoHistory()
{
d->undoStack->clear();
}
KisGridConfig KisDocument::gridConfig() const
{
return d->gridConfig;
}
void KisDocument::setGridConfig(const KisGridConfig &config)
{
if (d->gridConfig != config) {
d->gridConfig = config;
d->syncDecorationsWrapperLayerState();
emit sigGridConfigChanged(config);
}
}
-QList<KoColorSet *> &KisDocument::paletteList()
+QList<KoColorSetSP > KisDocument::paletteList()
{
- return d->paletteList;
+ qDebug() << "PALETTELIST storage" << d->documentResourceStorage;
+
+ QList<KoColorSetSP> _paletteList;
+ if (d->documentResourceStorage.isNull()) {
+ qWarning() << "No documentstorage for palettes";
+ return _paletteList;
+ }
+
+ QSharedPointer<KisResourceStorage::ResourceIterator> iter = d->documentResourceStorage->resources(ResourceType::Palettes);
+ while (iter->hasNext()) {
+ iter->next();
+ KoResourceSP resource = iter->resource();
+ if (resource && resource->valid()) {
+ _paletteList << resource.dynamicCast<KoColorSet>();
+ }
+ }
+ return _paletteList;
}
-void KisDocument::setPaletteList(const QList<KoColorSet *> &paletteList, bool emitSignal)
+void KisDocument::setPaletteList(const QList<KoColorSetSP > &paletteList, bool emitSignal)
{
- if (d->paletteList != paletteList) {
- QList<KoColorSet *> oldPaletteList = d->paletteList;
- d->paletteList = paletteList;
- if (emitSignal) {
- emit sigPaletteListChanged(oldPaletteList, paletteList);
+ qDebug() << "SET PALETTE LIST" << paletteList.size() << "storage" << d->documentResourceStorage;
+
+ QList<KoColorSetSP> oldPaletteList;
+ if (d->documentResourceStorage) {
+ QSharedPointer<KisResourceStorage::ResourceIterator> iter = d->documentResourceStorage->resources(ResourceType::Palettes);
+ while (iter->hasNext()) {
+ iter->next();
+ KoResourceSP resource = iter->resource();
+ if (resource && resource->valid()) {
+ oldPaletteList << resource.dynamicCast<KoColorSet>();
+ }
+ }
+ if (oldPaletteList != paletteList) {
+ KisResourceModel *resourceModel = KisResourceModelProvider::resourceModel(ResourceType::Palettes);
+ Q_FOREACH(KoColorSetSP palette, oldPaletteList) {
+ resourceModel->removeResource(palette);
+ }
+ Q_FOREACH(KoColorSetSP palette, paletteList) {
+ qDebug()<< "loading palette into document" << palette->filename();
+ resourceModel->addResource(palette, d->documentStorageID);
+ }
+ if (emitSignal) {
+ emit sigPaletteListChanged(oldPaletteList, paletteList);
+ }
}
}
}
const KisGuidesConfig& KisDocument::guidesConfig() const
{
return d->guidesConfig;
}
void KisDocument::setGuidesConfig(const KisGuidesConfig &data)
{
if (d->guidesConfig == data) return;
d->guidesConfig = data;
d->syncDecorationsWrapperLayerState();
emit sigGuidesConfigChanged(d->guidesConfig);
}
const KisMirrorAxisConfig& KisDocument::mirrorAxisConfig() const
{
return d->mirrorAxisConfig;
}
void KisDocument::setMirrorAxisConfig(const KisMirrorAxisConfig &config)
{
if (d->mirrorAxisConfig == config) {
return;
}
d->mirrorAxisConfig = config;
setModified(true);
emit sigMirrorAxisConfigChanged();
}
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, KisConfig::BackgroundStyle bgStyle,
int numberOfLayers,
const QString &description, const double imageResolution)
{
Q_ASSERT(cs);
KisImageSP image;
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());
image->waitForDone();
documentInfo()->setAboutInfo("title", name);
documentInfo()->setAboutInfo("abstract", description);
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();
+ KisFilterConfigurationSP filter_config = KisGeneratorRegistry::instance()->get("color")->defaultConfiguration(KisGlobalResourcesInterface::instance());
filter_config->setProperty("color", strippedAlpha.toQColor());
+ filter_config->createLocalResourcesSnapshot();
layer = new KisGeneratorLayer(image.data(), "Background Fill", filter_config, image->globalSelection());
}
layer->setOpacity(bgColor.opacityU8());
if (numberOfLayers > 1) {
//Lock bg layer if others are present.
layer->setUserLocked(true);
}
}
else { // KisConfig::CANVAS_COLOR (needs an unlocked starting layer).
image->setDefaultProjectionColor(bgColor);
layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, cs);
}
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(QString("Created image \"%1\", %2 * %3 pixels, %4 dpi. Color model: %6 %5 (%7). Layers: %8")
.arg(name)
.arg(width).arg(height)
.arg(imageResolution * 72.0)
.arg(image->colorSpace()->colorModelId().name())
.arg(image->colorSpace()->colorDepthId().name())
.arg(image->colorSpace()->profile()->name())
.arg(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)
{
if (d->assistants != value) {
d->assistants = value;
d->syncDecorationsWrapperLayerState();
emit sigAssistantsChanged();
}
}
KisReferenceImagesLayerSP KisDocument::referenceImagesLayer() const
{
if (!d->image) return KisReferenceImagesLayerSP();
KisReferenceImagesLayerSP referencesLayer =
KisLayerUtils::findNodeByType<KisReferenceImagesLayer>(d->image->root());
return referencesLayer;
}
void KisDocument::setReferenceImagesLayer(KisSharedPtr<KisReferenceImagesLayer> layer, bool updateImage)
{
KisReferenceImagesLayerSP currentReferenceLayer = referenceImagesLayer();
if (currentReferenceLayer == layer) {
return;
}
if (currentReferenceLayer) {
currentReferenceLayer->disconnect(this);
}
if (updateImage) {
if (currentReferenceLayer) {
d->image->removeNode(currentReferenceLayer);
}
if (layer) {
d->image->addNode(layer);
}
}
currentReferenceLayer = layer;
if (currentReferenceLayer) {
connect(currentReferenceLayer, SIGNAL(sigUpdateCanvas(QRectF)),
this, SIGNAL(sigReferenceImagesChanged()));
}
emit sigReferenceImagesLayerChanged(layer);
}
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;
+ if (d->documentResourceStorage){
+ d->documentResourceStorage->setMetaData(KisResourceStorage::s_meta_name, image->objectName());
+ }
+
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);
connect(d->image, SIGNAL(sigLayersChangedAsync()), this, SLOT(slotImageRootChanged()));
if (forceInitialUpdate) {
d->image->initialRefreshGraph();
}
}
void KisDocument::hackPreliminarySetImage(KisImageSP image)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(!d->image);
// we set image without connecting idle-watcher, because loading
// hasn't been finished yet
d->image = image;
d->shapeController->setImage(image);
}
void KisDocument::setImageModified()
{
// we only set as modified if undo stack is not at clean state
setModified(!d->undoStack->isClean());
}
KisUndoStore* KisDocument::createUndoStore()
{
return new KisDocumentUndoStore(this);
}
bool KisDocument::isAutosaving() const
{
return d->isAutosaving;
}
QString KisDocument::exportErrorToUserMessage(KisImportExportErrorCode status, const QString &errorMessage)
{
return errorMessage.isEmpty() ? status.errorMessage() : errorMessage;
}
void KisDocument::setAssistantsGlobalColor(QColor color)
{
d->globalAssistantsColor = color;
}
QColor KisDocument::assistantsGlobalColor()
{
return d->globalAssistantsColor;
}
QRectF KisDocument::documentBounds() const
{
QRectF bounds = d->image->bounds();
KisReferenceImagesLayerSP referenceImagesLayer = this->referenceImagesLayer();
if (referenceImagesLayer) {
bounds |= referenceImagesLayer->boundingImageRect();
}
return bounds;
}
diff --git a/libs/ui/KisDocument.h b/libs/ui/KisDocument.h
index a6df324e76..cf24b4bf65 100644
--- a/libs/ui/KisDocument.h
+++ b/libs/ui/KisDocument.h
@@ -1,698 +1,715 @@
/* 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 KisMirrorAxisConfig;
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();
+ explicit KisDocument(bool addStorage = true);
/**
* @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 uniqueID is a temporary unique ID that identifies the document. It is
+ * generated on creation and can be used to uniquely associated temporary objects
+ * with this document.
+ *
+ * @return the temporary unique id for this document.
+ */
+ QString uniqueID() const;
+
/**
* @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);
/**
* Exports he document is a synchronous way. The caller must ensure that the
* image is not accessed by any other actors, because the exporting happens
* without holding the image lock.
*/
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);
+ /**
+ * @brief paletteList returns all the palettes found in the document's local resource storage
+ */
+ QList<KoColorSetSP> paletteList();
+
+ /**
+ * @brief setPaletteList replaces the palettes in the document's local resource storage with the list
+ * of palettes passed to this function. It will then emitsigPaletteListChanged with both the old and
+ * the new list, if emitsignal is true.
+ */
+ void setPaletteList(const QList<KoColorSetSP> &paletteList, bool emitSignal = false);
+
const KisMirrorAxisConfig& mirrorAxisConfig() const;
void setMirrorAxisConfig(const KisMirrorAxisConfig& config);
- QList<KoColorSet *> &paletteList();
- void setPaletteList(const QList<KoColorSet *> &paletteList, bool emitSignal = false);
-
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(KisImportExportErrorCode status, const QString &errorMessage);
void sigCompleteBackgroundSaving(const KritaUtils::ExportFileJob &job, KisImportExportErrorCode status, const QString &errorMessage);
void sigReferenceImagesChanged();
void sigMirrorAxisConfigChanged();
void sigGridConfigChanged(const KisGridConfig &config);
void sigReferenceImagesLayerChanged(KisSharedPtr<KisReferenceImagesLayer> layer);
/**
* Emitted when the palette list has changed.
* The pointers in oldPaletteList are to be deleted by the resource server.
**/
- void sigPaletteListChanged(const QList<KoColorSet *> &oldPaletteList, const QList<KoColorSet *> &newPaletteList);
+ void sigPaletteListChanged(const QList<KoColorSetSP> &oldPaletteList, const QList<KoColorSetSP> &newPaletteList);
void sigAssistantsChanged();
private Q_SLOTS:
void finishExportInBackground();
void slotChildCompletedSavingInBackground(KisImportExportErrorCode status, const QString &errorMessage);
void slotCompleteAutoSaving(const KritaUtils::ExportFileJob &job, KisImportExportErrorCode status, const QString &errorMessage);
void slotCompleteSavingDocument(const KritaUtils::ExportFileJob &job, KisImportExportErrorCode 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, 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;
/**
* @brief Start saving when android activity is pushed to the background
*/
void autoSaveOnPause();
Q_SIGNALS:
void completed();
void canceled(const QString &);
private Q_SLOTS:
void setImageModified();
void slotAutoSave();
void slotUndoStackCleanChanged(bool value);
void slotConfigChanged();
void slotImageRootChanged();
/**
* @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();
public:
KisDocument *lockAndCreateSnapshot();
void copyFromDocument(const KisDocument &rhs);
private:
enum CopyPolicy {
CONSTRUCT = 0, ///< we are copy-constructing a new KisDocument
REPLACE ///< we are replacing the current KisDocument with another
};
void copyFromDocumentImpl(const KisDocument &rhs, CopyPolicy policy);
QString exportErrorToUserMessage(KisImportExportErrorCode 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 2e5414446c..c595e807a3 100644
--- a/libs/ui/KisImportExportManager.cpp
+++ b/libs/ui/KisImportExportManager.cpp
@@ -1,730 +1,730 @@
/*
* 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 <QDir>
#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 "KisPopupButton.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<KisImportExportErrorCode> &futureStatus)
: m_isAsync(true),
m_futureStatus(futureStatus)
{
}
ConversionResult(KisImportExportErrorCode status)
: m_isAsync(false),
m_status(status)
{
}
bool isAsync() const {
return m_isAsync;
}
QFuture<KisImportExportErrorCode> 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.isOk());
return m_futureStatus;
}
KisImportExportErrorCode status() const {
return m_status;
}
void setStatus(KisImportExportErrorCode value) {
m_status = value;
}
private:
bool m_isAsync = false;
QFuture<KisImportExportErrorCode> m_futureStatus;
KisImportExportErrorCode m_status = ImportExportCodes::InternalError;
};
KisImportExportManager::KisImportExportManager(KisDocument* document)
: m_document(document)
, d(new Private)
{
}
KisImportExportManager::~KisImportExportManager()
{
delete d;
}
KisImportExportErrorCode 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(), ImportExportCodes::InternalError);
return result.status();
}
KisImportExportErrorCode 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(), ImportExportCodes::InternalError);
return result.status();
}
QFuture<KisImportExportErrorCode> KisImportExportManager::exportDocumentAsyc(const QString &location, const QString &realLocation, const QByteArray &mimeType,
KisImportExportErrorCode &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().isOk(), QFuture<KisImportExportErrorCode>());
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();
+ m_importMimeTypes = QList<QString>(mimeTypes.begin(), mimeTypes.end());
}
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();
+ m_exportMimeTypes = QList<QString>(mimeTypes.begin(), mimeTypes.end());
}
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("@title: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 KisImportExportErrorCode(ImportExportCodes::FileFormatIncorrect);
}
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,
KisImportExportErrorCode(ImportExportCodes::InternalError)); // "bad conversion graph"
ConversionResult result = KisImportExportErrorCode(ImportExportCodes::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);
// FIXME: Dmitry says "this progress reporting code never worked. Initial idea was to implement it his way, but I stopped and didn't finish it"
if (0 && !batchMode()) {
KisAsyncActionFeedback f(i18n("Opening document..."), 0);
result = f.runAction(std::bind(&KisImportExportManager::doImport, this, location, filter));
} else {
result = doImport(location, filter);
}
if (result.status().isOk()) {
KisImageSP image = m_document->image().toStrongRef();
if (image) {
KisUsageLogger::log(QString("Loaded image from %1. Size: %2 * %3 pixels, %4 dpi. Color model: %6 %5 (%7). Layers: %8")
.arg(QString::fromLatin1(from))
.arg(image->width())
.arg(image->height())
.arg(image->xRes())
.arg(image->colorSpace()->colorModelId().name())
.arg(image->colorSpace()->colorDepthId().name())
.arg(image->colorSpace()->profile()->name())
.arg(image->nlayers()));
}
else {
qWarning() << "The filter returned OK, but there is no image";
}
}
else {
KisUsageLogger::log(QString("Failed to load image from %1").arg(QString::fromLatin1(from)));
}
}
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 KisImportExportErrorCode(ImportExportCodes::Cancelled);
}
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(ImportExportCodes::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()) {
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);
KisMainWindow *kisMain = KisPart::instance()->currentMainwindow();
if (wdg && kisMain) {
KisViewManager *manager = kisMain->viewManager();
wdg->setView(manager);
}
}
// 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> " + i18n("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> " + i18n("Reason:") + "</p>";
}
else {
warning += "</b> " + i18n("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;
}
KisImportExportErrorCode KisImportExportManager::doImport(const QString &location, QSharedPointer<KisImportExportFilter> filter)
{
QFile file(location);
if (!file.exists()) {
return ImportExportCodes::FileNotExist;
}
if (filter->supportsIO() && !file.open(QFile::ReadOnly)) {
return KisImportExportErrorCode(KisImportExportErrorCannotRead(file.error()));
}
KisImportExportErrorCode status = filter->convert(m_document, &file, KisPropertiesConfigurationSP());
if (file.isOpen()) {
file.close();
}
return status;
}
KisImportExportErrorCode KisImportExportManager::doExport(const QString &location, QSharedPointer<KisImportExportFilter> filter, KisPropertiesConfigurationSP exportConfiguration, bool alsoAsKra)
{
KisImportExportErrorCode status =
doExportImpl(location, filter, exportConfiguration);
if (alsoAsKra && status.isOk()) {
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 = ImportExportCodes::FileFormatIncorrect;
}
}
return status;
}
// Temporary workaround until QTBUG-57299 is fixed.
// 02-10-2019 update: the bug is closed, but we've still seen this issue.
// and without using QSaveFile the issue can still occur
// when QFile::copy fails because Dropbox/Google/OneDrive
// locks the target file.
#ifndef Q_OS_WIN
#define USE_QSAVEFILE
#endif
KisImportExportErrorCode 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(QDir::tempPath() + "/.XXXXXX.kra");
if (filter->supportsIO() && !file.open()) {
#endif
KisImportExportErrorCannotWrite result(file.error());
#ifdef USE_QSAVEFILE
file.cancelWriting();
#endif
return result;
}
KisImportExportErrorCode status = filter->convert(m_document, &file, exportConfiguration);
if (filter->supportsIO()) {
if (!status.isOk()) {
#ifdef USE_QSAVEFILE
file.cancelWriting();
#endif
} else {
#ifdef USE_QSAVEFILE
if (!file.commit()) {
qWarning() << "Could not commit QSaveFile";
status = KisImportExportErrorCannotWrite(file.error());
}
#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);
return KisImportExportErrorCannotWrite(file.error());
}
#endif
}
}
// Do some minimal verification
QString verificationResult = filter->verify(location);
if (!verificationResult.isEmpty()) {
status = KisImportExportErrorCode(ImportExportCodes::ErrorWhileWriting);
m_document->setErrorMessage(verificationResult);
}
return status;
}
#include <KisMimeDatabase.h>
diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp
index bd04b9995d..3d90c94a63 100644
--- a/libs/ui/KisMainWindow.cpp
+++ b/libs/ui/KisMainWindow.cpp
@@ -1,2813 +1,2860 @@
/* 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 <KisSignalMapper.h>
#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 <QWindow>
#include <QScrollArea>
#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 <KisResourceModel.h>
+#include <KisResourceModelProvider.h>
+#include <KisResourceLoaderRegistry.h>
+#include <KisResourceIterator.h>
+#include <KisResourceTypes.h>
+#include <KisResourceCacheDb.h>
#ifdef Q_OS_ANDROID
#include <KisAndroidFileManager.h>
#endif
#include <KisUsageLogger.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_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 "KisCanvasWindow.h"
#include "kis_action.h"
#include <mutex>
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 KisSignalMapper(parent))
, documentMapper(new KisSignalMapper(parent))
#ifdef Q_OS_ANDROID
, fileManager(new KisAndroidFileManager(parent))
#endif
{
if (id.isNull()) this->id = QUuid::createUuid();
welcomeScroller = new QScrollArea();
welcomeScroller->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
welcomeScroller->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
welcomeScroller->setWidget(welcomePage);
welcomeScroller->setWidgetResizable(true);
widgetStack->addWidget(welcomeScroller);
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 *toggleDetachCanvas {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};
+ KisResourceModel *workspacemodel {0};
QScopedPointer<KisUndoActionsUpdateManager> undoActionsUpdateManager;
QString lastExportLocation;
QMap<QString, QDockWidget *> dockWidgetsMap;
QByteArray dockerStateBeforeHiding;
KoToolDocker *toolOptionsDocker {0};
QCloseEvent *deferredClosingEvent {0};
Digikam::ThemeManager *themeManager {0};
QScrollArea *welcomeScroller {0};
KisWelcomePageWidget *welcomePage {0};
QStackedWidget *widgetStack {0};
QMdiArea *mdiArea;
QMdiSubWindow *activeSubWindow {0};
KisSignalMapper *windowMapper;
KisSignalMapper *documentMapper;
KisCanvasWindow *canvasWindow {0};
QByteArray lastExportedFormat;
QScopedPointer<KisSignalCompressorWithParam<int> > tabSwitchCompressor;
QMutex savingEntryMutex;
KConfigGroup windowStateConfig;
QUuid workspaceBorrowedBy;
KisSignalAutoConnectionsStore screenConnectionsStore;
#ifdef Q_OS_ANDROID
KisAndroidFileManager *fileManager;
#endif
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->workspacemodel = KisResourceModelProvider::resourceModel(ResourceType::Workspaces);
+ connect(d->workspacemodel, SIGNAL(afterResourcesLayoutReset()), this, SLOT(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");
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_MACOS
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*)));
d->canvasWindow = new KisCanvasWindow(this);
actionCollection()->addAssociatedWidget(d->canvasWindow);
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();
// Make sure the python plugins create their actions in time
KisPart::instance()->notifyMainWindowIsBeingCreated(this);
// If we have customized the toolbars, load that first
setLocalXMLFile(KoResourcePaths::locateLocal("data", "krita4.xmlgui"));
setXMLFile(":/kxmlgui5/krita4.xmlgui");
guiFactory()->addClient(this);
connect(guiFactory(), SIGNAL(makingChanges(bool)), SLOT(slotXmlGuiMakingChanges(bool)));
// 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();
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);
+ KisWorkspaceResourceSP workspace = rserver->resourceByName(currentWorkspace);
if (workspace) {
- restoreWorkspace(workspace);
+ restoreWorkspace(workspace->resourceId());
}
cfg.writeEntry("CanvasOnlyActive", false);
menuBar()->setVisible(true);
}
this->winId(); // Ensures the native window has been created.
QWindow *window = this->windowHandle();
connect(window, SIGNAL(screenChanged(QScreen *)), this, SLOT(windowScreenChanged(QScreen *)));
}
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, QMdiSubWindow *subWindow)
{
if (d->activeView == view && !subWindow) return;
if (d->activeView) {
d->activeView->disconnect(this);
}
// register the newly created view in the input manager
viewManager()->inputManager()->addTrackedCanvas(view->canvasBase());
showView(view, subWindow);
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)
{
/**
* If we are the last view of the window, Qt will not activate another tab
* before destroying tab/window. In this case we should clear all the dangling
* pointers manually by setting the current view to null
*/
viewManager()->inputManager()->removeTrackedCanvas(view->canvasBase());
if (view->canvasBase() == viewManager()->canvasBase()) {
viewManager()->setCurrentView(0);
}
}
void KisMainWindow::showView(KisView *imageView, QMdiSubWindow *subwin)
{
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();
if (!subwin) {
subwin = d->mdiArea->addSubWindow(imageView);
} else {
subwin->setWidget(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());
#ifdef Q_OS_MACOS
connect(subwin, SIGNAL(destroyed()), SLOT(updateSubwindowFlags()));
updateSubwindowFlags();
#endif
if (d->mdiArea->subWindowList().size() == 1) {
imageView->showMaximized();
}
else {
imageView->show();
}
/**
* 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();
// 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()
{
QScopedPointer<KisDlgPreferences> dlgPreferences(new KisDlgPreferences(this));
if (dlgPreferences->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();
}
}
updateWindowMenu();
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);
}
if (d->mdiArea) {
d->mdiArea->setPalette(qApp->palette());
for (int i=0; i<d->mdiArea->subWindowList().size(); i++) {
QMdiSubWindow *window = d->mdiArea->subWindowList().at(i);
if (window) {
window->setPalette(qApp->palette());
KisView *view = qobject_cast<KisView*>(window->widget());
if (view) {
view->slotThemeChanged(qApp->palette());
}
}
}
}
emit themeChanged();
}
bool KisMainWindow::canvasDetached() const
{
return centralWidget() != d->widgetStack;
}
void KisMainWindow::setCanvasDetached(bool detach)
{
if (detach == canvasDetached()) return;
QWidget *outgoingWidget = centralWidget() ? takeCentralWidget() : nullptr;
QWidget *incomingWidget = d->canvasWindow->swapMainWidget(outgoingWidget);
if (incomingWidget) {
setCentralWidget(incomingWidget);
}
if (detach) {
d->canvasWindow->show();
} else {
d->canvasWindow->hide();
}
}
void KisMainWindow::slotFileSelected(QString path)
{
QString url = path;
if (!url.isEmpty()) {
bool res = openDocument(QUrl::fromLocalFile(url), Import);
if (!res) {
warnKrita << "Loading" << url << "failed";
}
}
}
void KisMainWindow::slotEmptyFilePath()
{
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("The chosen file's location could not be found. Does it exist?"));
}
QWidget * KisMainWindow::canvasWindow() const
{
return d->canvasWindow;
}
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, const QUrl &oldUrl)
{
// 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) {
if (!oldUrl.isEmpty()) {
d->recentFiles->removeUrl(oldUrl);
}
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();
d->welcomePage->populateRecentDocuments();
}
void KisMainWindow::removeRecentUrl(const QUrl &url)
{
d->recentFiles->removeUrl(url);
KSharedConfigPtr config = KSharedConfig::openConfig();
d->recentFiles->saveEntries(config->group("RecentFiles"));
config->sync();
}
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());
+
+ caption = "RESOURCES REWRITE GOING ON " + 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( ")");
}
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 modified)
{
QString versionString = KritaVersionWrapper::versionString(true);
QString title = caption;
if (!title.contains(QStringLiteral("[*]"))) { // append the placeholder so that the modified mechanism works
title.append(QStringLiteral(" [*]"));
}
if (d->mdiArea->activeSubWindow()) {
#if defined(KRITA_ALPHA) || defined (KRITA_BETA) || defined (KRITA_RC)
d->mdiArea->activeSubWindow()->setWindowTitle(QString("%1: %2").arg(versionString).arg(title));
#else
d->mdiArea->activeSubWindow()->setWindowTitle(title);
#endif
d->mdiArea->activeSubWindow()->setWindowModified(modified);
}
else {
#if defined(KRITA_ALPHA) || defined (KRITA_BETA) || defined (KRITA_RC)
setWindowTitle(QString("%1: %2").arg(versionString).arg(title));
#else
setWindowTitle(title);
#endif
}
setWindowModified(modified);
}
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;
// XXX: Why this duplication of of OpenFlags...
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);
}
if (flags & RecoveryFile &&
( url.toLocalFile().startsWith(QDir::tempPath())
|| url.toLocalFile().startsWith(QDir::homePath()))
) {
newdoc->setUrl(QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation) + "/" + QFileInfo(url.toLocalFile()).fileName()));
newdoc->save(false, 0);
}
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,
QMdiSubWindow *subWindow)
{
showWelcomeScreen(false); // see workaround in function header
KisView *view = KisPart::instance()->createView(document, d->viewManager, this);
addView(view, subWindow);
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)
{
KisUsageLogger::log(QString("Loading canceled. Error:").arg(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* 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)
{
KisUsageLogger::log(QString("Saving canceled. Error:").arg(errMsg));
if (!errMsg.isEmpty()) { // empty when canceled by user
QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg);
}
slotSaveCompleted();
}
void KisMainWindow::slotSaveCompleted()
{
KisUsageLogger::log(QString("Saving Completed"));
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());
}
+QImage KisMainWindow::layoutThumbnail()
+{
+ int size = 256;
+ qreal scale = qreal(size)/qreal(qMax(geometry().width(), geometry().height()));
+ QImage layoutThumbnail = QImage(qRound(geometry().width()*scale), qRound(geometry().height()*scale), QImage::Format_ARGB32);
+ QPainter gc(&layoutThumbnail);
+ gc.fillRect(0, 0, layoutThumbnail.width(), layoutThumbnail.height(), this->palette().dark());
+
+ for (int childW = 0; childW< children().size(); childW++) {
+ if (children().at(childW)->isWidgetType()) {
+ QWidget *w = dynamic_cast<QWidget*>(children().at(childW));
+
+ if (w->isVisible()) {
+ QRect wRect = QRectF(w->geometry().x()*scale
+ , w->geometry().y()*scale
+ , w->geometry().width()*scale
+ , w->geometry().height()*scale
+ ).toRect();
+
+ wRect = wRect.intersected(layoutThumbnail.rect().adjusted(-1, -1, -1, -1));
+
+ gc.setBrush(this->palette().window());
+ if (w == d->widgetStack) {
+ gc.setBrush(d->mdiArea->background());
+ }
+ gc.setPen(this->palette().windowText().color());
+ gc.drawRect(wRect);
+ }
+ }
+ }
+ gc.end();
+ return layoutThumbnail;
+}
+
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()).completeBaseName();
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() && !d->lastExportLocation.contains(QDir::tempPath())) {
// 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()).completeBaseName();
// 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.completeBaseName());
}
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()).completeBaseName();
justChangingFilterOptions = (QFileInfo(newURL.toLocalFile()).absolutePath() == path)
&& (QFileInfo(newURL.toLocalFile()).completeBaseName() == 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 (hackIsSaving()) {
e->setAccepted(false);
return;
}
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);
d->canvasWindow->close();
} 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::dragMove(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::dragLeave()
{
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";
item.title = i18n("Custom Document");
startupWidget->addCustomDocumentWidget(item.widget, item.title, "Custom Document", 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, "Create from ClipBoard", 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)
{
#ifndef Q_OS_ANDROID
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";
}
}
}
#else
Q_UNUSED(isImporting)
d->fileManager->openImportFile();
connect(d->fileManager, SIGNAL(sigFileSelected(QString)), this, SLOT(slotFileSelected(QString)));
connect(d->fileManager, SIGNAL(sigEmptyFilePath()), this, SLOT(slotEmptyFilePath()));
#endif
}
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->canvasResourceProvider()->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 KisMainWindow::restoreWorkspace(int workspaceId)
{
+
+ KisWorkspaceResourceSP workspace =
+ KisResourceModelProvider::resourceModel(ResourceType::Workspaces)
+ ->resourceForId(workspaceId).dynamicCast<KisWorkspaceResource>();
+
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()
{
// Do not close while KisMainWindow has the savingEntryMutex locked, bug409395.
// After the background saving job is initiated, KisDocument blocks closing
// while it saves itself.
if (hackIsSaving()) {
return;
}
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);
KisImportExportErrorCode status = importer.import(files, firstFrame, step);
if (!status.isOk() && !status.isInternalError()) {
QString msg = status.errorMessage();
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
}
d->fullScreenMode->setChecked(isFullScreen());
}
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_MACOS
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();
QFontMetrics fontMetrics = docMenu->fontMetrics();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
QScreen *screen = qApp->screenAt(this->geometry().topLeft());
int fileStringWidth = 300;
if (screen) {
fileStringWidth = int(screen->availableGeometry().width() * .40f);
}
#else
int fileStringWidth = int(QApplication::desktop()->screenGeometry(this).width() * .40f);
#endif
Q_FOREACH (QPointer<KisDocument> doc, KisPart::instance()->documents()) {
if (doc) {
QString title = fontMetrics.elidedText(doc->url().toDisplayString(QUrl::PreferLocalFile), Qt::ElideMiddle, fileStringWidth);
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());
+ KisResourceIterator resourceIterator(KisResourceModelProvider::resourceModel(ResourceType::Workspaces));
+ KisMainWindow *m_this = this;
+
+ while (resourceIterator.hasNext()) {
+ KisResourceItemSP resource = resourceIterator.next();
+ QAction *action = workspaceMenu->addAction(resource->name());
connect(action, &QAction::triggered, this, [=]() {
- m_this->restoreWorkspace(w);
+ m_this->restoreWorkspace(resource->id());
});
}
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);
- }
-
+ [&]()
+ {
+ QStringList mimeTypes = KisResourceLoaderRegistry::instance()->mimeTypes(ResourceType::Workspaces);
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;
+ QString name;
auto rserver = KisResourceServerProvider::instance()->workspaceServer();
- KisWorkspaceResource* workspace = new KisWorkspaceResource("");
+ KisWorkspaceResourceSP workspace(new KisWorkspaceResource(""));
workspace->setDockerState(m_this->saveState());
d->viewManager->canvasResourceProvider()->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());
+ bool fileOverWriteAccepted = false;
- 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);
+ while(!fileOverWriteAccepted) {
+ name = QInputDialog::getText(this, i18nc("@title:window", "New Workspace..."),
+ i18nc("@label:textbox", "Name:"));
+ if (name.isNull() || name.isEmpty()) {
+ return;
+ } else {
+ fileInfo = QFileInfo(saveLocation + name.split(" ").join("_") + workspace->defaultFileExtension());
+ if (fileInfo.exists()) {
+ int res = QMessageBox::warning(this, i18nc("@title:window", "Name Already Exists")
+ , i18n("The name '%1' already exists, do you wish to overwrite it?", name)
+ , QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ if (res == QMessageBox::Yes) fileOverWriteAccepted = true;
+ } else {
+ fileOverWriteAccepted = true;
+ }
+ }
}
+
+ workspace->setFilename(fileInfo.fileName());
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, fontMetrics.elidedText(child->document()->url().toDisplayString(QUrl::PreferLocalFile), Qt::ElideMiddle, fileStringWidth));
}
else {
text = i18n("%1 %2", i + 1, fontMetrics.elidedText(child->document()->url().toDisplayString(QUrl::PreferLocalFile), Qt::ElideMiddle, fileStringWidth));
}
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::updateSubwindowFlags()
{
bool onlyOne = false;
if (d->mdiArea->subWindowList().size() == 1 && d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
onlyOne = true;
}
Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
if (onlyOne) {
subwin->setWindowFlags(subwin->windowFlags() | Qt::FramelessWindowHint);
subwin->showMaximized();
} else {
subwin->setWindowFlags((subwin->windowFlags() | Qt::FramelessWindowHint) ^ Qt::FramelessWindowHint);
}
}
}
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 continue 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();
}
}
}
#ifdef Q_OS_MACOS
updateSubwindowFlags();
#endif
KConfigGroup group( KSharedConfig::openConfig(), "theme");
d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark"));
d->actionManager()->updateGUI();
QString s = cfg.getMDIBackgroundColor();
KoColor c = KoColor::fromXML(s);
QBrush brush(c.toQColor());
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, QMdiSubWindow *subWindow)
{
KisDocument *doc = qobject_cast<KisDocument*>(document);
KisView *view = addViewAndNotifyLoadingCompleted(doc, subWindow);
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()) {
+ if (rserver->resourceCount() == 0) {
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_MACOS
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.completeBaseName();
}
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)));
d->toggleDetachCanvas = actionManager->createAction("view_detached_canvas");
d->toggleDetachCanvas->setChecked(false);
connect(d->toggleDetachCanvas, SIGNAL(toggled(bool)), SLOT(setCanvasDetached(bool)));
setCanvasDetached(false);
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 = QGuiApplication::screens().at(scnum)->availableVirtualGeometry();
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::windowScreenChanged(QScreen *screen)
{
emit screenChanged();
d->screenConnectionsStore.clear();
d->screenConnectionsStore.addConnection(screen, SIGNAL(physicalDotsPerInchChanged(qreal)),
this, SIGNAL(screenChanged()));
}
void KisMainWindow::slotXmlGuiMakingChanges(bool finished)
{
if (finished) {
subWindowActivated();
}
}
#include <moc_KisMainWindow.cpp>
diff --git a/libs/ui/KisMainWindow.h b/libs/ui/KisMainWindow.h
index ad6b47981f..bcc858b29d 100644
--- a/libs/ui/KisMainWindow.h
+++ b/libs/ui/KisMainWindow.h
@@ -1,523 +1,530 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
Copyright (C) 2000-2004 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 KIS_MAIN_WINDOW_H
#define KIS_MAIN_WINDOW_H
#include "kritaui_export.h"
#include <QPointer>
#include <QPrinter>
#include <QUuid>
#include <QUrl>
#include <xmlgui/kxmlguiwindow.h>
#include <KoCanvasObserverBase.h>
#include <KoCanvasSupervisor.h>
#include "KisView.h"
+#include <kis_workspace_resource.h>
class QCloseEvent;
class QMoveEvent;
struct KoPageLayout;
class KoCanvasResourceProvider;
class KisDocument;
class KisPrintJob;
class KoDockFactoryBase;
class QDockWidget;
class KisView;
class KisViewManager;
class KoCanvasController;
-class KisWorkspaceResource;
+
/**
* @brief Main window for Krita
*
* This class is used to represent a main window within a Krita session. Each
* main window contains a menubar and some toolbars, and potentially several
* views of several canvases.
*
*/
class KRITAUI_EXPORT KisMainWindow : public KXmlGuiWindow, public KoCanvasSupervisor
{
Q_OBJECT
public:
enum OpenFlag {
None = 0,
Import = 0x1,
BatchMode = 0x2,
RecoveryFile = 0x4
};
Q_DECLARE_FLAGS(OpenFlags, OpenFlag)
public:
/**
* Constructor.
*
* Initializes a Calligra main window (with its basic GUI etc.).
*/
explicit KisMainWindow(QUuid id = QUuid());
/**
* Destructor.
*/
~KisMainWindow() override;
QUuid id() const;
/**
* @brief showView shows the given view, in @p subWindow if not
* null, in a new tab otherwise.
*/
virtual void showView(KisView *view, QMdiSubWindow *subWindow = 0);
/**
* @returns the currently active view
*/
KisView *activeView() const;
/**
* Sets the maximum number of recent documents entries.
*/
void setMaxRecentItems(uint _number);
/**
* The document opened a URL -> store into recent documents list.
* @param oldUrl if not empty, @p url will replace @p oldUrl if present
*/
void addRecentURL(const QUrl &url, const QUrl &oldUrl = QUrl());
/**
* get list of URL strings for recent files
*/
QList<QUrl> recentFilesUrls();
/**
* removes the given url from the list of recent files
*/
void removeRecentUrl(const QUrl &url);
/**
* Load the desired document and show it.
* @param url the URL to open
*
* @return TRUE on success.
*/
bool openDocument(const QUrl &url, OpenFlags flags);
/**
* Activate a view containing the document in this window, creating one if needed.
*/
void showDocument(KisDocument *document);
/**
* Toggles between showing the welcome screen and the MDI area
*
* hack: There seems to be a bug that prevents events happening to the MDI area if it
* isn't actively displayed (set in the widgetStack). This can cause things like the title bar
* not to update correctly Before doing any actions related to opening or creating documents,
* make sure to switch this first to make sure everything can communicate to the MDI area correctly
*/
void showWelcomeScreen(bool show);
/**
* Saves the document, asking for a filename if necessary.
*
* @param saveas if set to TRUE the user is always prompted for a filename
* @param silent if set to TRUE rootDocument()->setTitleModified will not be called.
*
* @return TRUE on success, false on error or cancel
* (don't display anything in this case, the error dialog box is also implemented here
* but restore the original URL in slotFileSaveAs)
*/
bool saveDocument(KisDocument *document, bool saveas, bool isExporting);
void setReadWrite(bool readwrite);
/// Return the list of dock widgets belonging to this main window.
QList<QDockWidget*> dockWidgets() const;
QDockWidget* dockWidget(const QString &id);
QList<KoCanvasObserverBase*> canvasObservers() const override;
KoCanvasResourceProvider *resourceManager() const;
int viewCount() const;
void saveWindowState(bool restoreNormalState =false);
const KConfigGroup &windowStateConfig() const;
/**
* A wrapper around restoreState
* @param state the saved state
* @return TRUE on success
*/
- bool restoreWorkspace(KisWorkspaceResource *workspace);
+ bool restoreWorkspace(int workspaceId);
bool restoreWorkspaceState(const QByteArray &state);
static void swapWorkspaces(KisMainWindow *a, KisMainWindow *b);
KisViewManager *viewManager() const;
KisView *addViewAndNotifyLoadingCompleted(KisDocument *document,
QMdiSubWindow *subWindow = 0);
QStringList showOpenFileDialog(bool isImporting);
/**
* The top-level window used for a detached canvas.
*/
QWidget *canvasWindow() const;
bool canvasDetached() const;
/**
* Shows if the main window is saving anything right now. If the
* user presses Ctrl+W too fast, then the document can be close
* before the saving is completed. I'm not sure if it is fixable
* in any way without avoiding using porcessEvents()
* everywhere (DK)
*
* Don't use it unless you have no option.
*/
bool hackIsSaving() const;
/// Copy the given file into the bundle directory.
bool installBundle(const QString &fileName) const;
+ /**
+ * @brief layoutThumbnail
+ * @return image for the workspaces.
+ */
+ QImage layoutThumbnail();
+
Q_SIGNALS:
/**
* This signal is emitted if the document has been saved successfully.
*/
void documentSaved();
/// This signal is emitted when this windows has finished loading of a
/// document. The document may be opened in another window in the end.
/// In this case, the signal means there is no link between the window
/// and the document anymore.
void loadCompleted();
/// This signal is emitted right after the docker states have been succefully restored from config
void restoringDone();
/// This signal is emitted when the color theme changes
void themeChanged();
/// This signal is emitted when the shortcut key configuration has changed
void keyBindingsChanged();
void guiLoadingFinished();
/// emitted when the window is migrated among different screens
void screenChanged();
public Q_SLOTS:
/**
* clears the list of the recent files
*/
void clearRecentFiles();
/**
* Slot for opening a new document.
*
* If the current document is empty, the new document replaces it.
* If not, a new mainwindow will be opened for showing the document.
*/
void slotFileNew();
/**
* Slot for opening a saved file.
*
* If the current document is empty, the opened document replaces it.
* If not a new mainwindow will be opened for showing the opened file.
*/
void slotFileOpen(bool isImporting = false);
/**
* Slot for opening a file among the recently opened files.
*
* If the current document is empty, the opened document replaces it.
* If not a new mainwindow will be opened for showing the opened file.
*/
void slotFileOpenRecent(const QUrl &);
/**
* @brief slotPreferences open the preferences dialog
*/
void slotPreferences();
/**
* Update caption from document info - call when document info
* (title in the about page) changes.
*/
void updateCaption();
/**
* Saves the current document with the current name.
*/
void slotFileSave();
void slotShowSessionManager();
// XXX: disabled
KisPrintJob* exportToPdf(QString pdfFileName = QString());
/**
* Update the option widgets to the argument ones, removing the currently set widgets.
*/
void newOptionWidgets(KoCanvasController *controller, const QList<QPointer<QWidget> > & optionWidgetList);
KisView *newView(QObject *document, QMdiSubWindow *subWindow = 0);
void notifyChildViewDestroyed(KisView *view);
/// Set the active view, this will update the undo/redo actions
void setActiveView(KisView *view);
void subWindowActivated();
void windowFocused();
/**
* Reloads the recent documents list.
*/
void reloadRecentFileList();
/**
* Detach canvas onto a separate window, or restore it back to to main window.
*/
void setCanvasDetached(bool detached);
void slotFileSelected(QString path);
void slotEmptyFilePath();
/**
* Toggle full screen on/off.
*/
void viewFullscreen(bool fullScreen);
private Q_SLOTS:
/**
* Save the list of recent files.
*/
void saveRecentFiles();
void slotLoadCompleted();
void slotLoadCanceled(const QString &);
void slotSaveCompleted();
void slotSaveCanceled(const QString &);
void forceDockTabFonts();
/**
* @internal
*/
void slotDocumentTitleModified();
/**
* Prints the actual document.
*/
void slotFilePrint();
/**
* Saves the current document with a new name.
*/
void slotFileSaveAs();
void slotFilePrintPreview();
void importAnimation();
/**
* Show a dialog with author and document information.
*/
void slotDocumentInfo();
/**
* Closes all open documents.
*/
bool slotFileCloseAll();
/**
* @brief showAboutApplication show the about box
*/
virtual void showAboutApplication();
/**
* Closes the mainwindow.
*/
void slotFileQuit();
/**
* Configure toolbars.
*/
void slotConfigureToolbars();
/**
* Post toolbar config.
* (Plug action lists back in, etc.)
*/
void slotNewToolbarConfig();
/**
* Shows or hides a toolbar
*/
void slotToolbarToggled(bool toggle);
/**
* Reload file
*/
void slotReloadFile();
/**
* File --> Import
*
* This will call slotFileOpen().
*/
void slotImportFile();
/**
* File --> Export
*
* This will call slotFileSaveAs().
*/
void slotExportFile();
/**
* Hide the dockers
*/
void toggleDockersVisibility(bool visible);
/**
* Handle theme changes from theme manager
*/
void slotThemeChanged();
void undo();
void redo();
void updateWindowMenu();
void updateSubwindowFlags();
void setActiveSubWindow(QWidget *window);
void configChanged();
void newWindow();
void closeCurrentWindow();
void checkSanity();
/// Quits Krita with error message from m_errorMessage.
void showErrorAndDie();
void initializeGeometry();
void showManual();
void switchTab(int index);
void windowScreenChanged(QScreen *screen);
void slotXmlGuiMakingChanges(bool finished);
protected:
void closeEvent(QCloseEvent * e) override;
void resizeEvent(QResizeEvent * e) override;
// QWidget overrides
private:
friend class KisWelcomePageWidget;
void dragMove(QDragMoveEvent *event);
void dragLeave();
private:
/**
* Add a the given view to the list of views of this mainwindow.
* This is a private implementation. For public usage please use
* newView() and addViewAndNotifyLoadingCompleted().
*/
void addView(KisView *view, QMdiSubWindow *subWindow = 0);
friend class KisPart;
/**
* Returns the dockwidget specified by the @p factory. If the dock widget doesn't exist yet it's created.
* Add a "view_palette_action_menu" action to your view menu if you want to use closable dock widgets.
* @param factory the factory used to create the dock widget if needed
* @return the dock widget specified by @p factory (may be 0)
*/
QDockWidget* createDockWidget(KoDockFactoryBase* factory);
bool openDocumentInternal(const QUrl &url, KisMainWindow::OpenFlags flags = 0);
/**
* Updates the window caption based on the document info and path.
*/
void updateCaption(const QString & caption, bool modified);
void updateReloadFileAction(KisDocument *doc);
void saveWindowSettings();
QPointer<KisView> activeKisView();
void applyDefaultSettings(QPrinter &printer);
void createActions();
void applyToolBarLayout();
QByteArray borrowWorkspace(KisMainWindow *borrower);
private:
/**
* Struct used in the list created by createCustomDocumentWidgets()
*/
struct CustomDocumentWidgetItem {
/// Pointer to the custom document widget
QWidget *widget;
/// title used in the sidebar. If left empty it will be displayed as "Custom Document"
QString title;
/// icon used in the sidebar. If left empty it will use the unknown icon
QString icon;
};
class Private;
Private * const d;
QString m_errorMessage;
bool m_dieOnError;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KisMainWindow::OpenFlags)
#endif
diff --git a/libs/ui/KisPaintopPropertiesBase.cpp b/libs/ui/KisPaintopPropertiesBase.cpp
index b31df4386c..3e16c79aa6 100644
--- a/libs/ui/KisPaintopPropertiesBase.cpp
+++ b/libs/ui/KisPaintopPropertiesBase.cpp
@@ -1,45 +1,97 @@
/*
* 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 "KisPaintopPropertiesBase.h"
#include "kis_properties_configuration.h"
+#include <KisResourcesInterface.h>
-KisPaintopPropertiesBase::~KisPaintopPropertiesBase()
+
+KisPaintopPropertiesResourcesBase::~KisPaintopPropertiesResourcesBase()
{
+
}
-void KisPaintopPropertiesBase::readOptionSetting(KisPropertiesConfigurationSP settings)
+void KisPaintopPropertiesResourcesBase::readOptionSetting(KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface)
{
- readOptionSettingImpl(settings.data());
+ readOptionSettingResourceImpl(settings.data(), resourcesInterface);
}
-void KisPaintopPropertiesBase::writeOptionSetting(KisPropertiesConfigurationSP settings) const
+void KisPaintopPropertiesResourcesBase::writeOptionSetting(KisPropertiesConfigurationSP settings) const
{
writeOptionSettingImpl(settings.data());
}
+void KisPaintopPropertiesResourcesBase::readOptionSetting(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface)
+{
+ readOptionSettingResourceImpl(settings, resourcesInterface);
+}
+
+void KisPaintopPropertiesResourcesBase::writeOptionSetting(KisPropertiesConfiguration *settings) const
+{
+ writeOptionSettingImpl(settings);
+}
+
+QList<KoResourceSP> KisPaintopPropertiesResourcesBase::prepareLinkedResources(const KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface) const
+{
+ return prepareLinkedResourcesImpl(settings.data(), resourcesInterface);
+}
+
+QList<KoResourceSP> KisPaintopPropertiesResourcesBase::prepareLinkedResources(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const
+{
+ return prepareLinkedResourcesImpl(settings, resourcesInterface);
+}
+
+QList<KoResourceSP> KisPaintopPropertiesResourcesBase::prepareEmbeddedResources(const KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface) const
+{
+ return prepareEmbeddedResources(settings.data(), resourcesInterface);
+}
+
+QList<KoResourceSP> KisPaintopPropertiesResourcesBase::prepareEmbeddedResources(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const
+{
+ return prepareEmbeddedResources(settings, resourcesInterface);
+}
+
+void KisPaintopPropertiesBase::readOptionSetting(KisPropertiesConfigurationSP settings)
+{
+ readOptionSettingImpl(settings.data());
+}
+
void KisPaintopPropertiesBase::readOptionSetting(const KisPropertiesConfiguration *settings)
{
readOptionSettingImpl(settings);
}
-void KisPaintopPropertiesBase::writeOptionSetting(KisPropertiesConfiguration *settings) const
+void KisPaintopPropertiesBase::readOptionSettingResourceImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface)
{
- writeOptionSettingImpl(settings);
+ Q_UNUSED(resourcesInterface);
+ readOptionSettingImpl(settings);
+}
+
+QList<KoResourceSP> KisPaintopPropertiesBase::prepareLinkedResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const
+{
+ Q_UNUSED(settings);
+ Q_UNUSED(resourcesInterface);
+ return {};
}
+QList<KoResourceSP> KisPaintopPropertiesBase::prepareEmbeddedResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const
+{
+ Q_UNUSED(settings);
+ Q_UNUSED(resourcesInterface);
+ return {};
+}
diff --git a/libs/ui/KisPaintopPropertiesBase.h b/libs/ui/KisPaintopPropertiesBase.h
index 93a305a557..8419c4243b 100644
--- a/libs/ui/KisPaintopPropertiesBase.h
+++ b/libs/ui/KisPaintopPropertiesBase.h
@@ -1,59 +1,89 @@
/*
* 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 KISBASEOPTION_H
#define KISBASEOPTION_H
#include <kis_types.h>
#include <kritaui_export.h>
+class KoResource;
+using KoResourceSP = QSharedPointer<KoResource>;
+
+class KisResourcesInterface;
+using KisResourcesInterfaceSP = QSharedPointer<KisResourcesInterface>;
/**
* This is a special base class for all the options that load/save
* settings into a properties objects and do *not* store the properties
* themselves. A KisPaintOpOption derived class generates a QWidget for
* its configuration page. This cannot be created from a KisPaintO[
*
* This class adapts your option to allow its easy use with
* both raw pointers and shared pointers.
*
* Motivation:
* In quite a lot of places we call some options from the KisPaintOpSettings
* class itself with patting 'this' as a parameter into
* read/writeOptionSetting(). Conversion of 'this' into a shared pointer is
* extremely dangerous, and, ideally, should be prohibited. We cannot prohibit
* it atm, but we still can create a special interface for accepting raw pointers,
* which will be used automatically, when 'this' is passed.
*/
-class KRITAUI_EXPORT KisPaintopPropertiesBase
+
+class KRITAUI_EXPORT KisPaintopPropertiesResourcesBase
{
public:
- virtual ~KisPaintopPropertiesBase();
+ virtual ~KisPaintopPropertiesResourcesBase();
- void readOptionSetting(KisPropertiesConfigurationSP settings);
+ void readOptionSetting(KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface);
void writeOptionSetting(KisPropertiesConfigurationSP setting) const;
- void readOptionSetting(const KisPropertiesConfiguration *settings);
+ void readOptionSetting(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface);
void writeOptionSetting(KisPropertiesConfiguration *settings) const;
+ QList<KoResourceSP> prepareLinkedResources(const KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface) const;
+ QList<KoResourceSP> prepareLinkedResources(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const;
+
+ QList<KoResourceSP> prepareEmbeddedResources(const KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface) const;
+ QList<KoResourceSP> prepareEmbeddedResources(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const;
+
+
protected:
- virtual void readOptionSettingImpl(const KisPropertiesConfiguration *settings) = 0;
+ virtual void readOptionSettingResourceImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) = 0;
virtual void writeOptionSettingImpl(KisPropertiesConfiguration *settings) const = 0;
+ virtual QList<KoResourceSP> prepareLinkedResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const = 0;
+ virtual QList<KoResourceSP> prepareEmbeddedResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const = 0;
+};
+
+class KRITAUI_EXPORT KisPaintopPropertiesBase : public KisPaintopPropertiesResourcesBase
+{
+public:
+ void readOptionSetting(KisPropertiesConfigurationSP settings);
+ void readOptionSetting(const KisPropertiesConfiguration *settings);
+
+private:
+ void readOptionSettingResourceImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) override final;
+ QList<KoResourceSP> prepareLinkedResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const override final;
+ QList<KoResourceSP> prepareEmbeddedResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const override final;
+
+protected:
+ virtual void readOptionSettingImpl(const KisPropertiesConfiguration *settings) = 0;
};
#endif // KISBASEOPTION_H
diff --git a/libs/ui/KisPaletteEditor.cpp b/libs/ui/KisPaletteEditor.cpp
index 161ccfd099..1f37e36e38 100644
--- a/libs/ui/KisPaletteEditor.cpp
+++ b/libs/ui/KisPaletteEditor.cpp
@@ -1,689 +1,531 @@
/*
* 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 <QHash>
#include <QString>
#include <QScopedPointer>
#include <QPointer>
#include <QFormLayout>
#include <QCheckBox>
#include <QLineEdit>
#include <QPushButton>
#include <QLabel>
#include <QSpinBox>
#include <QComboBox>
#include <QMessageBox>
#include <KoDialog.h>
#include <KoFileDialog.h>
#include <KoColorSet.h>
#include <KisSwatchGroup.h>
#include <kis_signal_compressor.h>
#include <KisViewManager.h>
#include <KisDocument.h>
#include <KoResourceServer.h>
#include <KoResourceServerProvider.h>
#include <KisPaletteModel.h>
#include <kis_color_button.h>
#include "KisPaletteEditor.h"
+#include <KisGlobalResourcesInterface.h>
struct KisPaletteEditor::PaletteInfo {
QString name;
QString filename;
int columnCount;
- bool isGlobal;
bool isReadOnly;
+ bool isGlobal;
QHash<QString, KisSwatchGroup> groups;
};
struct KisPaletteEditor::Private
{
- bool isGlobalModified {false};
bool isNameModified {false};
- bool isFilenameModified {false};
bool isColumnCountModified {false};
QSet<QString> modifiedGroupNames; // key is original group name
QSet<QString> newGroupNames;
QSet<QString> keepColorGroups;
QSet<QString> pathsToRemove;
QString groupBeingRenamed;
QPointer<KisPaletteModel> model;
QPointer<KisViewManager> view;
PaletteInfo modified;
QPointer<KoDialog> query;
KoResourceServer<KoColorSet> *rServer {0};
QPalette normalPalette;
QPalette warnPalette;
};
KisPaletteEditor::KisPaletteEditor(QObject *parent)
: QObject(parent)
, m_d(new Private)
{
m_d->rServer = KoResourceServerProvider::instance()->paletteServer();
m_d->warnPalette.setColor(QPalette::Text, Qt::red);
}
KisPaletteEditor::~KisPaletteEditor()
{ }
void KisPaletteEditor::setPaletteModel(KisPaletteModel *model)
{
if (!model) { return; }
m_d->model = model;
slotPaletteChanged();
connect(model, SIGNAL(sigPaletteChanged()), SLOT(slotPaletteChanged()));
connect(model, SIGNAL(sigPaletteModified()), SLOT(slotSetDocumentModified()));
}
void KisPaletteEditor::setView(KisViewManager *view)
{
m_d->view = view;
}
void KisPaletteEditor::addPalette()
{
if (!m_d->view) { return; }
if (!m_d->view->document()) { return; }
+ KoColorSetSP colorSet(new KoColorSet());
+
KoDialog dlg;
QFormLayout layout;
dlg.mainWidget()->setLayout(&layout);
QLabel lbl(i18nc("Label for line edit to set a palette name.","Name"));
QLineEdit le(i18nc("Default name for a new palette","New Palette"));
layout.addRow(&lbl, &le);
- QLabel lbl2(i18nc("Label for line edit to set a palette filename.","File Name"));
- QLineEdit le2(i18nc("Default file name for a new palette", "New Palette"));
- layout.addRow(&lbl2, &le2);
+ QString saveLocation = m_d->rServer->saveLocation();
QCheckBox chkSaveInDocument(i18n("Save Palette in the Current Document"));
chkSaveInDocument.setChecked(false);
layout.addRow(&chkSaveInDocument);
if (dlg.exec() != QDialog::Accepted) { return; }
- KoColorSet *newColorSet = new KoColorSet(newPaletteFileName(!chkSaveInDocument.isChecked(), le2.text()));
- newColorSet->setPaletteType(KoColorSet::KPL);
- newColorSet->setIsGlobal(!chkSaveInDocument.isChecked());
- newColorSet->setIsEditable(true);
- newColorSet->setValid(true);
- newColorSet->setName(le.text());
-
- m_d->rServer->addResource(newColorSet, !chkSaveInDocument.isChecked());
- m_d->rServer->removeFromBlacklist(newColorSet);
+ QString name = le.text();
+ colorSet->setPaletteType(KoColorSet::KPL);
+ colorSet->setIsEditable(true);
+ colorSet->setValid(true);
+ colorSet->setName(name);
+ colorSet->setFilename(name.split(" ").join("_")+colorSet->defaultFileExtension());
- uploadPaletteList();
+ QString resourceLocation = "";
+ if (chkSaveInDocument.isChecked()) {
+ resourceLocation = m_d->view->document()->uniqueID();
+ }
+ m_d->rServer->resourceModel()->addResource(colorSet, resourceLocation);
}
void KisPaletteEditor::importPalette()
{
KoFileDialog dialog(0, KoFileDialog::OpenFile, "Open Palette");
dialog.setDefaultDir(QDir::homePath());
dialog.setMimeTypeFilters(QStringList() << "krita/x-colorset" << "application/x-gimp-color-palette");
QString filename = dialog.filename();
if (filename.isEmpty()) { return; }
- if (duplicateExistsFilename(filename, false)) {
- QMessageBox message;
- message.setWindowTitle(i18n("Can't Import Palette"));
- message.setText(i18n("Can't import palette: there's already imported with the same filename"));
- message.exec();
- return;
- }
QMessageBox messageBox;
messageBox.setText(i18n("Do you want to store this palette in your current image?"));
messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
- bool global = (messageBox.exec() == QMessageBox::Yes);
-
- KoColorSet *colorSet = new KoColorSet(filename);
- colorSet->load();
- QString name = filenameFromPath(colorSet->filename());
-
- if (duplicateExistsFilename(name, false)) {
- colorSet->setFilename(newPaletteFileName(global));
- } else {
- colorSet->setFilename(name);
+ QString storageLocation = "";
+ if (messageBox.exec() == QMessageBox::Yes) {
+ storageLocation = m_d->view->document()->uniqueID();
}
- colorSet->setIsGlobal(global);
- m_d->rServer->addResource(colorSet, global);
- m_d->rServer->removeFromBlacklist(colorSet);
-
- uploadPaletteList();
+ KoColorSetSP colorSet(new KoColorSet(filename));
+ colorSet->load(KisGlobalResourcesInterface::instance());
+ m_d->rServer->resourceModel()->addResource(colorSet, storageLocation);
}
-void KisPaletteEditor::removePalette(KoColorSet *cs)
+void KisPaletteEditor::removePalette(KoColorSetSP cs)
{
if (!m_d->view) { return; }
if (!m_d->view->document()) { return; }
if (!cs || !cs->isEditable()) {
return;
}
-
- if (cs->isGlobal()) {
- QFile::remove(cs->filename());
- m_d->rServer->removeResourceAndBlacklist(cs);
- return;
- }
m_d->rServer->removeResourceFromServer(cs);
- uploadPaletteList();
}
int KisPaletteEditor::rowNumberOfGroup(const QString &oriName) const
{
if (!m_d->modified.groups.contains(oriName)) { return 0; }
return m_d->modified.groups[oriName].rowCount();
}
bool KisPaletteEditor::duplicateExistsGroupName(const QString &name) const
{
if (name == m_d->groupBeingRenamed) { return false; }
Q_FOREACH (const KisSwatchGroup &g, m_d->modified.groups.values()) {
if (name == g.name()) { return true; }
}
return false;
}
bool KisPaletteEditor::duplicateExistsOriginalGroupName(const QString &name) const
{
return m_d->modified.groups.contains(name);
}
QString KisPaletteEditor::oldNameFromNewName(const QString &newName) const
{
Q_FOREACH (const QString &oldGroupName, m_d->modified.groups.keys()) {
if (m_d->modified.groups[oldGroupName].name() == newName) {
return oldGroupName;
}
}
return QString();
}
void KisPaletteEditor::rename(const QString &newName)
{
if (newName.isEmpty()) { return; }
m_d->isNameModified = true;
m_d->modified.name = newName;
}
-void KisPaletteEditor::changeFilename(const QString &newName)
-{
- if (newName.isEmpty()) { return; }
- m_d->isFilenameModified = true;
- m_d->pathsToRemove.insert(m_d->modified.filename);
- if (m_d->modified.isGlobal) {
- m_d->modified.filename = m_d->rServer->saveLocation() + newName;
- } else {
- m_d->modified.filename = newName;
- }
-}
-
void KisPaletteEditor::changeColCount(int newCount)
{
m_d->isColumnCountModified = true;
m_d->modified.columnCount = newCount;
}
QString KisPaletteEditor::addGroup()
{
KoDialog dlg;
m_d->query = &dlg;
QVBoxLayout layout(&dlg);
dlg.mainWidget()->setLayout(&layout);
QLabel lblName(i18n("Name"), &dlg);
layout.addWidget(&lblName);
QLineEdit leName(&dlg);
leName.setText(newGroupName());
connect(&leName, SIGNAL(textChanged(QString)), SLOT(slotGroupNameChanged(QString)));
layout.addWidget(&leName);
QLabel lblRowCount(i18n("Row count"), &dlg);
layout.addWidget(&lblRowCount);
QSpinBox spxRow(&dlg);
spxRow.setValue(20);
layout.addWidget(&spxRow);
if (dlg.exec() != QDialog::Accepted) { return QString(); }
if (duplicateExistsGroupName(leName.text())) { return QString(); }
QString realName = leName.text();
QString name = realName;
if (duplicateExistsOriginalGroupName(name)) {
name = newGroupName();
}
m_d->modified.groups[name] = KisSwatchGroup();
KisSwatchGroup &newGroup = m_d->modified.groups[name];
newGroup.setName(realName);
m_d->newGroupNames.insert(name);
newGroup.setRowCount(spxRow.value());
return realName;
}
bool KisPaletteEditor::removeGroup(const QString &name)
{
KoDialog window;
window.setWindowTitle(i18nc("@title:window", "Removing Group"));
QFormLayout editableItems(&window);
QCheckBox chkKeep(&window);
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; }
m_d->modified.groups.remove(name);
m_d->newGroupNames.remove(name);
if (chkKeep.isChecked()) {
m_d->keepColorGroups.insert(name);
}
return true;
}
QString KisPaletteEditor::renameGroup(const QString &oldName)
{
if (oldName.isEmpty() || oldName == KoColorSet::GLOBAL_GROUP_NAME) { return QString(); }
KoDialog dlg;
m_d->query = &dlg;
m_d->groupBeingRenamed = m_d->modified.groups[oldName].name();
QFormLayout form(&dlg);
dlg.mainWidget()->setLayout(&form);
QLineEdit leNewName;
connect(&leNewName, SIGNAL(textChanged(QString)), SLOT(slotGroupNameChanged(QString)));
leNewName.setText(m_d->modified.groups[oldName].name());
form.addRow(i18nc("Renaming swatch group", "New name"), &leNewName);
if (dlg.exec() != KoDialog::Accepted) { return QString(); }
if (leNewName.text().isEmpty()) { return QString(); }
if (duplicateExistsGroupName(leNewName.text())) { return QString(); }
m_d->modified.groups[oldName].setName(leNewName.text());
m_d->modifiedGroupNames.insert(oldName);
return leNewName.text();
}
void KisPaletteEditor::slotGroupNameChanged(const QString &newName)
{
QLineEdit *leGroupName = qobject_cast<QLineEdit*>(sender());
if (duplicateExistsGroupName(newName) || newName == QString()) {
leGroupName->setPalette(m_d->warnPalette);
if (m_d->query->button(KoDialog::Ok)) {
m_d->query->button(KoDialog::Ok)->setEnabled(false);
}
return;
}
leGroupName->setPalette(m_d->normalPalette);
if (m_d->query->button(KoDialog::Ok)) {
m_d->query->button(KoDialog::Ok)->setEnabled(true);
}
}
void KisPaletteEditor::changeGroupRowCount(const QString &name, int newRowCount)
{
if (!m_d->modified.groups.contains(name)) { return; }
m_d->modified.groups[name].setRowCount(newRowCount);
m_d->modifiedGroupNames.insert(name);
}
void KisPaletteEditor::setGlobal(bool isGlobal)
{
- m_d->isGlobalModified = true;
m_d->modified.isGlobal = isGlobal;
}
void KisPaletteEditor::setEntry(const KoColor &color, const QModelIndex &index)
{
Q_ASSERT(m_d->model);
if (!m_d->model->colorSet()->isEditable()) { return; }
if (!m_d->view) { return; }
if (!m_d->view->document()) { return; }
KisSwatch c = KisSwatch(color);
c.setId(QString::number(m_d->model->colorSet()->colorCount() + 1));
c.setName(i18nc("Default name for a color swatch","Color %1", QString::number(m_d->model->colorSet()->colorCount()+1)));
m_d->model->setEntry(c, index);
}
void KisPaletteEditor::slotSetDocumentModified()
{
+ m_d->rServer->resourceModel()->addResource(m_d->model->colorSet(), m_d->model->colorSet()->storageLocation());
m_d->view->document()->setModified(true);
}
void KisPaletteEditor::removeEntry(const QModelIndex &index)
{
Q_ASSERT(m_d->model);
if (!m_d->model->colorSet()->isEditable()) { return; }
if (!m_d->view) { return; }
if (!m_d->view->document()) { return; }
if (qvariant_cast<bool>(index.data(KisPaletteModel::IsGroupNameRole))) {
removeGroup(qvariant_cast<QString>(index.data(KisPaletteModel::GroupNameRole)));
- updatePalette();
} else {
m_d->model->removeEntry(index, false);
}
- if (m_d->model->colorSet()->isGlobal()) {
- m_d->model->colorSet()->save();
- return;
- }
+ updatePalette();
}
void KisPaletteEditor::modifyEntry(const QModelIndex &index)
{
if (!m_d->model->colorSet()->isEditable()) { return; }
if (!m_d->view) { return; }
if (!m_d->view->document()) { return; }
KoDialog dlg;
dlg.setCaption(i18nc("@title:window", "Add a Color"));
QFormLayout *editableItems = new QFormLayout(&dlg);
dlg.mainWidget()->setLayout(editableItems);
QString groupName = qvariant_cast<QString>(index.data(Qt::DisplayRole));
if (qvariant_cast<bool>(index.data(KisPaletteModel::IsGroupNameRole))) {
renameGroup(groupName);
updatePalette();
}
else {
QLineEdit *lnIDName = new QLineEdit(&dlg);
QLineEdit *lnGroupName = new QLineEdit(&dlg);
KisColorButton *bnColor = new KisColorButton(&dlg);
QCheckBox *chkSpot = new QCheckBox(&dlg);
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."));
KisSwatch entry = m_d->model->getEntry(index);
editableItems->addRow(i18n("ID"), lnIDName);
editableItems->addRow(i18nc("Name for a swatch group", "Swatch group name"), lnGroupName);
editableItems->addRow(i18n("Color"), bnColor);
editableItems->addRow(i18n("Spot color"), chkSpot);
lnGroupName->setText(entry.name());
lnIDName->setText(entry.id());
bnColor->setColor(entry.color());
chkSpot->setChecked(entry.spotColor());
if (dlg.exec() == KoDialog::Accepted) {
entry.setName(lnGroupName->text());
entry.setId(lnIDName->text());
entry.setColor(bnColor->color());
entry.setSpotColor(chkSpot->isChecked());
m_d->model->setEntry(entry, index);
}
}
}
void KisPaletteEditor::addEntry(const KoColor &color)
{
Q_ASSERT(m_d->model);
if (!m_d->view) { return; }
if (!m_d->view->document()) { return; }
if (!m_d->model->colorSet()->isEditable()) { return; }
KoDialog window;
window.setWindowTitle(i18nc("@title:window", "Add a new Colorset Entry"));
QFormLayout editableItems(&window);
window.mainWidget()->setLayout(&editableItems);
QComboBox cmbGroups(&window);
cmbGroups.addItems(m_d->model->colorSet()->getGroupNames());
QLineEdit lnIDName(&window);
QLineEdit lnName(&window);
KisColorButton bnColor(&window);
QCheckBox chkSpot(&window);
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("Default name for a color swatch","Color %1", 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) { return; }
QString groupName = cmbGroups.currentText();
KisSwatch newEntry;
newEntry.setColor(bnColor.color());
newEntry.setName(lnName.text());
newEntry.setId(lnIDName.text());
newEntry.setSpotColor(chkSpot.isChecked());
m_d->model->addEntry(newEntry, groupName);
-
- if (m_d->model->colorSet()->isGlobal()) {
- m_d->model->colorSet()->save();
- return;
- }
+ m_d->rServer->resourceModel()->updateResource(m_d->model->colorSet());
m_d->modifiedGroupNames.insert(groupName);
m_d->modified.groups[groupName].addEntry(newEntry);
}
void KisPaletteEditor::updatePalette()
{
Q_ASSERT(m_d->model);
Q_ASSERT(m_d->model->colorSet());
if (!m_d->model->colorSet()->isEditable()) { return; }
if (!m_d->view) { return; }
if (!m_d->view->document()) { return; }
- KoColorSet *palette = m_d->model->colorSet();
+ KoColorSetSP palette = m_d->model->colorSet();
PaletteInfo &modified = m_d->modified;
if (m_d->isColumnCountModified) {
palette->setColumnCount(modified.columnCount);
}
if (m_d->isNameModified) {
- palette->setName(modified.name);
+ m_d->rServer->resourceModel()->renameResource(palette, modified.name);
}
- if (m_d->isFilenameModified) {
- QString originalPath = palette->filename();
- palette->setFilename(modified.filename);
- if (palette->isGlobal()) {
- if (!palette->save()) {
- palette->setFilename(newPaletteFileName(true));
- palette->save();
- }
- QFile::remove(originalPath);
- }
- }
- if (m_d->isGlobalModified) {
- palette->setIsGlobal(modified.isGlobal);
- if (modified.isGlobal) {
- setGlobal();
- } else {
- setNonGlobal();
- }
+ QString resourceLocation = m_d->model->colorSet()->storageLocation();
+ if (m_d->modified.isGlobal) {
+ resourceLocation = QString();
+ } else {
+ resourceLocation = m_d->view->document()->uniqueID();
}
Q_FOREACH (const QString &groupName, palette->getGroupNames()) {
if (!modified.groups.contains(groupName)) {
m_d->model->removeGroup(groupName, m_d->keepColorGroups.contains(groupName));
}
}
m_d->keepColorGroups.clear();
Q_FOREACH (const QString &groupName, palette->getGroupNames()) {
if (m_d->modifiedGroupNames.contains(groupName)) {
m_d->model->setRowNumber(groupName, modified.groups[groupName].rowCount());
if (groupName != modified.groups[groupName].name()) {
m_d->model->renameGroup(groupName, modified.groups[groupName].name());
modified.groups[modified.groups[groupName].name()] = modified.groups[groupName];
modified.groups.remove(groupName);
}
}
}
m_d->modifiedGroupNames.clear();
Q_FOREACH (const QString &newGroupName, m_d->newGroupNames) {
m_d->model->addGroup(modified.groups[newGroupName]);
}
m_d->newGroupNames.clear();
-
- if (m_d->model->colorSet()->isGlobal()) {
- m_d->model->colorSet()->save();
- }
+ qDebug() << "updating palette from updatePalette" << m_d->model->colorSet()->filename() << resourceLocation;
+ m_d->rServer->resourceModel()->addResource(m_d->model->colorSet(), resourceLocation);
}
void KisPaletteEditor::slotPaletteChanged()
{
Q_ASSERT(m_d->model);
if (!m_d->model->colorSet()) { return; }
- KoColorSet *palette = m_d->model->colorSet();
+ KoColorSetSP palette = m_d->model->colorSet();
m_d->modified.groups.clear();
m_d->keepColorGroups.clear();
m_d->newGroupNames.clear();
m_d->modifiedGroupNames.clear();
m_d->modified.name = palette->name();
- m_d->modified.filename = palette->filename();
+ //hack alert! needs better solution.
+ m_d->modified.isGlobal = !palette->storageLocation().contains("/");
m_d->modified.columnCount = palette->columnCount();
- m_d->modified.isGlobal = palette->isGlobal();
m_d->modified.isReadOnly = !palette->isEditable();
Q_FOREACH (const QString &groupName, palette->getGroupNames()) {
KisSwatchGroup *cs = palette->getGroup(groupName);
m_d->modified.groups[groupName] = KisSwatchGroup(*cs);
}
}
-void KisPaletteEditor::setGlobal()
-{
- Q_ASSERT(m_d->model);
- if (!m_d->view) { return; }
- if (!m_d->view->document()) { return; }
- if (!m_d->model->colorSet()) { return; }
-
- KoColorSet *colorSet = m_d->model->colorSet();
- QString saveLocation = m_d->rServer->saveLocation();
- QString name = filenameFromPath(colorSet->filename());
-
- QFileInfo fileInfo(saveLocation + name);
-
- colorSet->setFilename(fileInfo.filePath());
- colorSet->setIsGlobal(true);
- m_d->rServer->removeFromBlacklist(colorSet);
- if (!colorSet->save()) {
- QMessageBox message;
- message.setWindowTitle(i18n("Saving palette failed"));
- message.setText(i18n("Failed to save global palette file. Please set it to non-global, or you will lose the file when you close Krita"));
- message.exec();
- }
-
- uploadPaletteList();
-}
-
-bool KisPaletteEditor::duplicateExistsFilename(const QString &filename, bool global) const
-{
- QString prefix;
- if (global) {
- prefix = m_d->rServer->saveLocation();
- }
-
- Q_FOREACH (const KoResource *r, KoResourceServerProvider::instance()->paletteServer()->resources()) {
- if (r->filename() == prefix + filename && r != m_d->model->colorSet()) {
- return true;
- }
- }
-
- return false;
-}
-
QString KisPaletteEditor::relativePathFromSaveLocation() const
{
return filenameFromPath(m_d->modified.filename);
}
-void KisPaletteEditor::setNonGlobal()
-{
- Q_ASSERT(m_d->model);
- if (!m_d->view) { return; }
- if (!m_d->view->document()) { return; }
- if (!m_d->model->colorSet()) { return; }
-
- KoColorSet *colorSet = m_d->model->colorSet();
- QString name = filenameFromPath(colorSet->filename());
- QFile::remove(colorSet->filename());
-
- if (duplicateExistsFilename(name, false)) {
- colorSet->setFilename(newPaletteFileName(false));
- } else {
- colorSet->setFilename(name);
- }
-
- colorSet->setIsGlobal(false);
-
- uploadPaletteList();
-}
-
-QString KisPaletteEditor::newPaletteFileName(bool isGlobal, const QString &filename)
-{
- QSet<QString> nameSet;
-
- Q_FOREACH (const KoResource *r, m_d->rServer->resources()) {
- nameSet.insert(r->filename());
- }
-
- KoColorSet tmpColorSet;
- QString result = (filename.isEmpty() ? "new_palette" : filename);
-
- if (isGlobal) {
- result = m_d->rServer->saveLocation() + result;
- }
-
- int i = 0;
- while (nameSet.contains(result + QString::number(i) + tmpColorSet.defaultFileExtension())) {
- i++;
- }
- result = result + (i > 0 ? QString::number(i) : "") + tmpColorSet.defaultFileExtension();
- return result;
-}
-
QString KisPaletteEditor::newGroupName() const
{
int i = 1;
QString groupname = i18nc("Default new group name", "New Group %1", QString::number(i));
while (m_d->modified.groups.contains(groupname)) {
i++;
groupname = i18nc("Default new group name", "New Group %1", QString::number(i));
}
return groupname;
}
-void KisPaletteEditor::uploadPaletteList() const
-{
- QList<KoColorSet *> list;
- Q_FOREACH (KoResource * paletteResource, m_d->rServer->resources()) {
- KoColorSet *palette = static_cast<KoColorSet*>(paletteResource);
- Q_ASSERT(palette);
- if (!palette->isGlobal()) {
- list.append(palette);
- }
- }
- m_d->view->document()->setPaletteList(list);
-}
-
QString KisPaletteEditor::filenameFromPath(const QString &path) const
{
return QDir::fromNativeSeparators(path).section('/', -1, -1);
}
diff --git a/libs/ui/KisPaletteEditor.h b/libs/ui/KisPaletteEditor.h
index b11c05a977..41327304e5 100644
--- a/libs/ui/KisPaletteEditor.h
+++ b/libs/ui/KisPaletteEditor.h
@@ -1,146 +1,142 @@
/*
* 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 = 0);
~KisPaletteEditor();
void setPaletteModel(KisPaletteModel *model);
void setView(KisViewManager *view);
void addPalette();
void importPalette();
- void removePalette(KoColorSet *);
+ void removePalette(KoColorSetSP );
/**
* @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 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
* @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 accepted, 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, const QString &filename = QString());
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/KisPart.cpp b/libs/ui/KisPart.cpp
index f2636f23a1..cdcce4511a 100644
--- a/libs/ui/KisPart.cpp
+++ b/libs/ui/KisPart.cpp
@@ -1,624 +1,634 @@
/* This file is part of the KDE project
* Copyright (C) 1998-1999 Torben Weis <weis@kde.org>
* Copyright (C) 2000-2005 David Faure <faure@kde.org>
* Copyright (C) 2007-2008 Thorsten Zachmann <zachmann@kde.org>
* Copyright (C) 2010-2012 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2011 Inge Wallin <ingwa@kogmbh.com>
* 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.
*/
#include "KisPart.h"
#include "KoProgressProxy.h"
#include <KoCanvasController.h>
#include <KoCanvasControllerWidget.h>
#include <KoColorSpaceEngine.h>
#include <KoCanvasBase.h>
#include <KoToolManager.h>
#include <KoShapeControllerBase.h>
#include <KoResourceServerProvider.h>
#include <kis_icon.h>
#include "KisApplication.h"
#include "KisMainWindow.h"
#include "KisDocument.h"
#include "KisView.h"
#include "KisViewManager.h"
#include "KisImportExportManager.h"
#include <kis_debug.h>
#include <KoResourcePaths.h>
#include <KoDialog.h>
-#include <kdesktopfile.h>
#include <QMessageBox>
#include <QMenu>
#include <QMenuBar>
#include <klocalizedstring.h>
#include <kactioncollection.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <QKeySequence>
#include <QDialog>
#include <QApplication>
#include <QDomDocument>
#include <QDomElement>
#include <QGlobalStatic>
#include <KisMimeDatabase.h>
#include <dialogs/KisSessionManagerDialog.h>
#include "kis_config.h"
#include "kis_shape_controller.h"
#include "KisResourceServerProvider.h"
#include "kis_animation_cache_populator.h"
#include "kis_image_animation_interface.h"
#include "kis_time_range.h"
#include "kis_idle_watcher.h"
#include "kis_image.h"
#include "KisOpenPane.h"
#include "kis_color_manager.h"
#include "kis_action.h"
#include "kis_action_registry.h"
#include "KisSessionResource.h"
#include "KisBusyWaitBroker.h"
#include "dialogs/kis_delayed_save_dialog.h"
Q_GLOBAL_STATIC(KisPart, s_instance)
class Q_DECL_HIDDEN KisPart::Private
{
public:
Private(KisPart *_part)
: part(_part)
, idleWatcher(2500)
, animationCachePopulator(_part)
{
}
~Private()
{
}
KisPart *part;
QList<QPointer<KisView> > views;
QList<QPointer<KisMainWindow> > mainWindows;
QList<QPointer<KisDocument> > documents;
KActionCollection *actionCollection{0};
KisIdleWatcher idleWatcher;
KisAnimationCachePopulator animationCachePopulator;
- KisSessionResource *currentSession = nullptr;
+ KisSessionResourceSP currentSession;
bool closingSession{false};
QScopedPointer<KisSessionManagerDialog> sessionManager;
bool queryCloseDocument(KisDocument *document) {
Q_FOREACH(auto view, views) {
if (view && view->isVisible() && view->document() == document) {
return view->queryClose();
}
}
return true;
}
};
KisPart* KisPart::instance()
{
return s_instance;
}
namespace {
void busyWaitWithFeedback(KisImageSP image)
{
const int busyWaitDelay = 1000;
KisDelayedSaveDialog dialog(image, KisDelayedSaveDialog::ForcedDialog, busyWaitDelay, KisPart::instance()->currentMainwindow());
dialog.blockIfImageIsBusy();
}
}
KisPart::KisPart()
: d(new Private(this))
{
// Preload all the resources in the background
Q_UNUSED(KoResourceServerProvider::instance());
Q_UNUSED(KisResourceServerProvider::instance());
Q_UNUSED(KisColorManager::instance());
connect(this, SIGNAL(documentOpened(QString)),
this, SLOT(updateIdleWatcherConnections()));
connect(this, SIGNAL(documentClosed(QString)),
this, SLOT(updateIdleWatcherConnections()));
connect(KisActionRegistry::instance(), SIGNAL(shortcutsUpdated()),
this, SLOT(updateShortcuts()));
connect(&d->idleWatcher, SIGNAL(startedIdleMode()),
&d->animationCachePopulator, SLOT(slotRequestRegeneration()));
d->animationCachePopulator.slotRequestRegeneration();
KisBusyWaitBroker::instance()->setFeedbackCallback(&busyWaitWithFeedback);
}
KisPart::~KisPart()
{
while (!d->documents.isEmpty()) {
delete d->documents.takeFirst();
}
while (!d->views.isEmpty()) {
delete d->views.takeFirst();
}
while (!d->mainWindows.isEmpty()) {
delete d->mainWindows.takeFirst();
}
delete d;
}
void KisPart::updateIdleWatcherConnections()
{
QVector<KisImageSP> images;
Q_FOREACH (QPointer<KisDocument> document, documents()) {
if (document->image()) {
images << document->image();
}
}
d->idleWatcher.setTrackedImages(images);
}
void KisPart::addDocument(KisDocument *document)
{
//dbgUI << "Adding document to part list" << document;
Q_ASSERT(document);
if (!d->documents.contains(document)) {
d->documents.append(document);
emit documentOpened('/'+objectName());
emit sigDocumentAdded(document);
connect(document, SIGNAL(sigSavingFinished()), SLOT(slotDocumentSaved()));
}
}
QList<QPointer<KisDocument> > KisPart::documents() const
{
return d->documents;
}
KisDocument *KisPart::createDocument() const
{
KisDocument *doc = new KisDocument();
return doc;
}
+KisDocument *KisPart::createTemporaryDocument() const
+{
+ KisDocument *doc = new KisDocument(false);
+ return doc;
+}
+
int KisPart::documentCount() const
{
return d->documents.size();
}
void KisPart::removeDocument(KisDocument *document)
{
d->documents.removeAll(document);
emit documentClosed('/' + objectName());
emit sigDocumentRemoved(document->url().toLocalFile());
document->deleteLater();
}
KisMainWindow *KisPart::createMainWindow(QUuid id)
{
KisMainWindow *mw = new KisMainWindow(id);
dbgUI <<"mainWindow" << (void*)mw << "added to view" << this;
d->mainWindows.append(mw);
// Add all actions with a menu property to the main window
Q_FOREACH(QAction *action, mw->actionCollection()->actions()) {
QString menuLocation = action->property("menulocation").toString();
if (!menuLocation.isEmpty()) {
QAction *found = 0;
QList<QAction *> candidates = mw->menuBar()->actions();
Q_FOREACH(const QString &name, menuLocation.split("/")) {
Q_FOREACH(QAction *candidate, candidates) {
if (candidate->objectName().toLower() == name.toLower()) {
found = candidate;
candidates = candidate->menu()->actions();
break;
}
}
if (candidates.isEmpty()) {
break;
}
}
if (found && found->menu()) {
found->menu()->addAction(action);
}
}
}
return mw;
}
void KisPart::notifyMainWindowIsBeingCreated(KisMainWindow *mainWindow)
{
emit sigMainWindowIsBeingCreated(mainWindow);
}
KisView *KisPart::createView(KisDocument *document,
KisViewManager *viewManager,
QWidget *parent)
{
// If creating the canvas fails, record this and disable OpenGL next time
KisConfig cfg(false);
KConfigGroup grp( KSharedConfig::openConfig(), "crashprevention");
if (grp.readEntry("CreatingCanvas", false)) {
cfg.disableOpenGL();
}
if (cfg.canvasState() == "OPENGL_FAILED") {
cfg.disableOpenGL();
}
grp.writeEntry("CreatingCanvas", true);
grp.sync();
QApplication::setOverrideCursor(Qt::WaitCursor);
KisView *view = new KisView(document, viewManager, parent);
QApplication::restoreOverrideCursor();
// Record successful canvas creation
grp.writeEntry("CreatingCanvas", false);
grp.sync();
addView(view);
return view;
}
void KisPart::addView(KisView *view)
{
if (!view)
return;
if (!d->views.contains(view)) {
d->views.append(view);
}
emit sigViewAdded(view);
}
void KisPart::removeView(KisView *view)
{
if (!view) return;
/**
* HACK ALERT: we check here explicitly if the document (or main
* window), is saving the stuff. If we close the
* document *before* the saving is completed, a crash
* will happen.
*/
KIS_ASSERT_RECOVER_RETURN(!view->mainWindow()->hackIsSaving());
emit sigViewRemoved(view);
QPointer<KisDocument> doc = view->document();
d->views.removeAll(view);
if (doc) {
bool found = false;
Q_FOREACH (QPointer<KisView> view, d->views) {
if (view && view->document() == doc) {
found = true;
break;
}
}
if (!found) {
removeDocument(doc);
}
}
}
QList<QPointer<KisView> > KisPart::views() const
{
return d->views;
}
int KisPart::viewCount(KisDocument *doc) const
{
if (!doc) {
return d->views.count();
}
else {
int count = 0;
Q_FOREACH (QPointer<KisView> view, d->views) {
if (view && view->isVisible() && view->document() == doc) {
count++;
}
}
return count;
}
}
bool KisPart::closingSession() const
{
return d->closingSession;
}
bool KisPart::exists()
{
return s_instance.exists();
}
bool KisPart::closeSession(bool keepWindows)
{
d->closingSession = true;
Q_FOREACH(auto document, d->documents) {
if (!d->queryCloseDocument(document.data())) {
d->closingSession = false;
return false;
}
}
if (d->currentSession) {
KisConfig kisCfg(false);
if (kisCfg.saveSessionOnQuit(false)) {
d->currentSession->storeCurrentWindows();
d->currentSession->save();
KConfigGroup cfg = KSharedConfig::openConfig()->group("session");
cfg.writeEntry("previousSession", d->currentSession->name());
}
d->currentSession = nullptr;
}
if (!keepWindows) {
Q_FOREACH (auto window, d->mainWindows) {
window->close();
}
if (d->sessionManager) {
d->sessionManager->close();
}
}
d->closingSession = false;
return true;
}
void KisPart::slotDocumentSaved()
{
KisDocument *doc = qobject_cast<KisDocument*>(sender());
emit sigDocumentSaved(doc->url().toLocalFile());
}
void KisPart::removeMainWindow(KisMainWindow *mainWindow)
{
dbgUI <<"mainWindow" << (void*)mainWindow <<"removed from doc" << this;
if (mainWindow) {
d->mainWindows.removeAll(mainWindow);
}
}
const QList<QPointer<KisMainWindow> > &KisPart::mainWindows() const
{
return d->mainWindows;
}
int KisPart::mainwindowCount() const
{
return d->mainWindows.count();
}
KisMainWindow *KisPart::currentMainwindow() const
{
QWidget *widget = qApp->activeWindow();
KisMainWindow *mainWindow = qobject_cast<KisMainWindow*>(widget);
while (!mainWindow && widget) {
widget = widget->parentWidget();
mainWindow = qobject_cast<KisMainWindow*>(widget);
}
if (!mainWindow && mainWindows().size() > 0) {
mainWindow = mainWindows().first();
}
return mainWindow;
}
KisMainWindow * KisPart::windowById(QUuid id) const
{
Q_FOREACH(QPointer<KisMainWindow> mainWindow, d->mainWindows) {
if (mainWindow->id() == id) {
return mainWindow;
}
}
return nullptr;
}
KisIdleWatcher* KisPart::idleWatcher() const
{
return &d->idleWatcher;
}
KisAnimationCachePopulator* KisPart::cachePopulator() const
{
return &d->animationCachePopulator;
}
void KisPart::prioritizeFrameForCache(KisImageSP image, int frame) {
KisImageAnimationInterface* animInterface = image->animationInterface();
KIS_SAFE_ASSERT_RECOVER_RETURN(animInterface->fullClipRange().contains(frame));
d->animationCachePopulator.requestRegenerationWithPriorityFrame(image, frame);
}
void KisPart::openExistingFile(const QUrl &url)
{
// TODO: refactor out this method!
KisMainWindow *mw = currentMainwindow();
KIS_SAFE_ASSERT_RECOVER_RETURN(mw);
mw->openDocument(url, KisMainWindow::None);
}
void KisPart::updateShortcuts()
{
// Update any non-UI actionCollections. That includes:
// Now update the UI actions.
Q_FOREACH (KisMainWindow *mainWindow, d->mainWindows) {
KActionCollection *ac = mainWindow->actionCollection();
ac->updateShortcuts();
// Loop through mainWindow->actionCollections() to modify tooltips
// so that they list shortcuts at the end in parentheses
Q_FOREACH ( QAction* action, ac->actions())
{
// Remove any existing suffixes from the tooltips.
// Note this regexp starts with a space, e.g. " (Ctrl-a)"
QString strippedTooltip = action->toolTip().remove(QRegExp("\\s\\(.*\\)"));
// Now update the tooltips with the new shortcut info.
if (action->shortcut() == QKeySequence(0))
action->setToolTip(strippedTooltip);
else
action->setToolTip( strippedTooltip + " (" + action->shortcut().toString() + ")");
}
}
}
void KisPart::openTemplate(const QUrl &url)
{
qApp->setOverrideCursor(Qt::BusyCursor);
KisDocument *document = createDocument();
bool ok = document->loadNativeFormat(url.toLocalFile());
document->setModified(false);
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$" ) );
document->setMimeTypeAfterLoading(mimeType);
document->resetURL();
document->setReadWrite(true);
}
else {
if (document->errorMessage().isEmpty()) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not create document from template\n%1", document->localFilePath()));
}
else {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not create document from template\n%1\nReason: %2", document->localFilePath(), document->errorMessage()));
}
delete document;
return;
}
addDocument(document);
KisMainWindow *mw = currentMainwindow();
mw->addViewAndNotifyLoadingCompleted(document);
KisOpenPane *pane = qobject_cast<KisOpenPane*>(sender());
if (pane) {
pane->hide();
pane->deleteLater();
}
qApp->restoreOverrideCursor();
}
void KisPart::addRecentURLToAllMainWindows(QUrl url, QUrl oldUrl)
{
// Add to recent actions list in our mainWindows
Q_FOREACH (KisMainWindow *mainWindow, d->mainWindows) {
mainWindow->addRecentURL(url, oldUrl);
}
}
void KisPart::startCustomDocument(KisDocument* doc)
{
addDocument(doc);
KisMainWindow *mw = currentMainwindow();
KisOpenPane *pane = qobject_cast<KisOpenPane*>(sender());
if (pane) {
pane->hide();
pane->deleteLater();
}
mw->addViewAndNotifyLoadingCompleted(doc);
}
KisInputManager* KisPart::currentInputManager()
{
KisMainWindow *mw = currentMainwindow();
KisViewManager *manager = mw ? mw->viewManager() : 0;
return manager ? manager->inputManager() : 0;
}
void KisPart::showSessionManager()
{
if (d->sessionManager.isNull()) {
d->sessionManager.reset(new KisSessionManagerDialog());
}
d->sessionManager->show();
d->sessionManager->activateWindow();
}
void KisPart::startBlankSession()
{
KisMainWindow *window = createMainWindow();
window->initializeGeometry();
window->show();
}
bool KisPart::restoreSession(const QString &sessionName)
{
if (sessionName.isNull()) return false;
- KoResourceServer<KisSessionResource> * rserver = KisResourceServerProvider::instance()->sessionServer();
- auto *session = rserver->resourceByName(sessionName);
+ KoResourceServer<KisSessionResource> *rserver = KisResourceServerProvider::instance()->sessionServer();
+ KisSessionResourceSP session = rserver->resourceByName(sessionName);
if (!session || !session->valid()) return false;
- session->restore();
+ return restoreSession(session);
+}
+bool KisPart::restoreSession(KisSessionResourceSP session)
+{
+ session->restore();
+ d->currentSession = session;
return true;
}
-void KisPart::setCurrentSession(KisSessionResource *session)
+void KisPart::setCurrentSession(KisSessionResourceSP session)
{
d->currentSession = session;
}
diff --git a/libs/ui/KisPart.h b/libs/ui/KisPart.h
index e712c22323..1d91fdf5b2 100644
--- a/libs/ui/KisPart.h
+++ b/libs/ui/KisPart.h
@@ -1,297 +1,305 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
Copyright (C) 2000-2005 David Faure <faure@kde.org>
Copyright (C) 2007 Thorsten Zachmann <zachmann@kde.org>
Copyright (C) 2010 Boudewijn Rempt <boud@valdyas.org>
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 KIS_PART_H
#define KIS_PART_H
#include <QList>
#include <QPointer>
#include <QUrl>
#include <QUuid>
+#include <KisSessionResource.h>
+
#include "kritaui_export.h"
#include <KConfigCore/kconfiggroup.h>
#include <KoConfig.h>
#include <KisMainWindow.h>
namespace KIO {
}
class KisAction;
class KisDocument;
class KisView;
class KisDocument;
class KisIdleWatcher;
class KisAnimationCachePopulator;
-class KisSessionResource;
+
/**
* KisPart a singleton class which provides the main entry point to the application.
* Krita supports multiple documents, multiple main windows, and multiple
* components. KisPart manages these resources and provides them to the rest of
* Krita. It manages lists of Actions and shortcuts as well.
*
* The terminology comes from KParts, which is a system allowing one KDE app
* to be run from inside another, like pressing F4 inside dophin to run konsole.
*
* Needless to say, KisPart hasn't got much to do with KParts anymore.
*/
class KRITAUI_EXPORT KisPart : public QObject
{
Q_OBJECT
public:
static KisPart *instance();
/**
* Constructor.
*
* @param parent may be another KisDocument, or anything else.
* Usually passed by KPluginFactory::create.
*/
explicit KisPart();
/**
* Destructor.
*
* The destructor does not delete any attached KisView objects and it does not
* delete the attached widget as returned by widget().
*/
~KisPart() override;
// ----------------- Document management -----------------
/**
* create an empty document. The document is not automatically registered with the part.
*/
KisDocument *createDocument() const;
+ /**
+ * create a throwaway empty document. The document does not register a resource storage
+ */
+ KisDocument *createTemporaryDocument() const;
+
/**
* Add the specified document to the list of documents this KisPart manages.
*/
void addDocument(KisDocument *document);
/**
* @return a list of all documents this part manages
*/
QList<QPointer<KisDocument> > documents() const;
/**
* @return number of documents this part manages.
*/
int documentCount() const;
void removeDocument(KisDocument *document);
// ----------------- MainWindow management -----------------
/**
* Create a new main window.
*/
KisMainWindow *createMainWindow(QUuid id = QUuid());
/**
* @brief notifyMainWindowIsBeingCreated emits the sigMainWindowCreated signal
* @param mainWindow
*/
void notifyMainWindowIsBeingCreated(KisMainWindow *mainWindow);
/**
* Removes a main window from the list of managed windows.
*
* This is called by the MainWindow after it finishes its shutdown routine.
*/
void removeMainWindow(KisMainWindow *mainWindow);
/**
* @return the list of main windows.
*/
const QList<QPointer<KisMainWindow> >& mainWindows() const;
/**
* @return the number of shells for the main window
*/
int mainwindowCount() const;
void addRecentURLToAllMainWindows(QUrl url, QUrl oldUrl = QUrl());
/**
* @return the currently active main window.
*/
KisMainWindow *currentMainwindow() const;
KisMainWindow *windowById(QUuid id) const;
/**
* @return the application-wide KisIdleWatcher.
*/
KisIdleWatcher *idleWatcher() const;
// ----------------- Cache Populator Management -----------------
/**
* @return the application-wide AnimationCachePopulator.
*/
KisAnimationCachePopulator *cachePopulator() const;
/**
* Adds a frame time index to a priority stack, which should be
* cached immediately and irregardless of whether it is the
* the currently occupied frame. The process of regeneration is
* started immediately.
*/
void prioritizeFrameForCache(KisImageSP image, int frame);
public Q_SLOTS:
/**
* This slot loads an existing file.
* @param url the file to load
*/
void openExistingFile(const QUrl &url);
/**
* This slot loads a template and deletes the sender.
* @param url the template to load
*/
void openTemplate(const QUrl &url);
/**
* @brief startCustomDocument adds the given document to the document list and deletes the sender()
* @param doc
*/
void startCustomDocument(KisDocument *doc);
private Q_SLOTS:
void updateIdleWatcherConnections();
void updateShortcuts();
Q_SIGNALS:
/**
* emitted when a new document is opened. (for the idle watcher)
*/
void documentOpened(const QString &ref);
/**
* emitted when an old document is closed. (for the idle watcher)
*/
void documentClosed(const QString &ref);
// These signals are for libkis or sketch
void sigViewAdded(KisView *view);
void sigViewRemoved(KisView *view);
void sigDocumentAdded(KisDocument *document);
void sigDocumentSaved(const QString &url);
void sigDocumentRemoved(const QString &filename);
void sigMainWindowIsBeingCreated(KisMainWindow *window);
public:
KisInputManager *currentInputManager();
//------------------ View management ------------------
/**
* Create a new view for the document. The view is added to the list of
* views, and if the document wasn't known yet, it's registered as well.
*/
KisView *createView(KisDocument *document,
KisViewManager *viewManager,
QWidget *parent);
/**
* Adds a view to the document. If the part doesn't know yet about
* the document, it is registered.
*
* This calls KisView::updateReadWrite to tell the new view
* whether the document is readonly or not.
*/
void addView(KisView *view);
/**
* Removes a view of the document.
*/
void removeView(KisView *view);
/**
* @return a list of views this document is displayed in
*/
QList<QPointer<KisView> > views() const;
/**
* @return number of views this document is displayed in
*/
int viewCount(KisDocument *doc) const;
//------------------ Session management ------------------
void showSessionManager();
void startBlankSession();
/**
* Restores a saved session by name
*/
bool restoreSession(const QString &sessionName);
+ bool restoreSession(KisSessionResourceSP session);
- void setCurrentSession(KisSessionResource *session);
+ void setCurrentSession(KisSessionResourceSP session);
/**
* Attempts to save the session and close all windows.
* This may involve asking the user to save open files.
* @return false, if closing was cancelled by the user
*/
bool closeSession(bool keepWindows = false);
/**
* Are we in the process of closing the application through closeSession().
*/
bool closingSession() const;
/**
* This function returns true if the instance has already been initialized,
* false otherwise. This to prevent premature initialization that causes crash
* on android see `1fbb25506a`
* @see QGlobalStatic::exists()
*/
static bool exists();
private Q_SLOTS:
void slotDocumentSaved();
private:
Q_DISABLE_COPY(KisPart)
class Private;
Private *const d;
};
#endif
diff --git a/libs/ui/KisResourceBundle.cpp b/libs/ui/KisResourceBundle.cpp
deleted file mode 100644
index fb4bc40cc3..0000000000
--- a/libs/ui/KisResourceBundle.cpp
+++ /dev/null
@@ -1,1098 +0,0 @@
-/*
- * 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.
- */
-
-#include "KisResourceBundle.h"
-#include "KisResourceBundleManifest.h"
-
-#include <KoXmlReader.h>
-#include <KoXmlWriter.h>
-#include <KoStore.h>
-#include <KoResourceServerProvider.h>
-#include <KoResourcePaths.h>
-
-#include <QScopedPointer>
-#include <QProcessEnvironment>
-#include <QDate>
-#include <QDir>
-#include <kis_debug.h>
-#include <QBuffer>
-#include <QCryptographicHash>
-#include <QByteArray>
-#include <QPainter>
-#include <QStringList>
-#include <QMessageBox>
-
-#include <resources/KoHashGeneratorProvider.h>
-#include <resources/KoHashGenerator.h>
-#include <KisResourceServerProvider.h>
-#include <kis_workspace_resource.h>
-#include <brushengine/kis_paintop_preset.h>
-#include <kis_brush_server.h>
-
-#include <KritaVersionWrapper.h>
-
-KisResourceBundle::KisResourceBundle(QString const& fileName)
- : KoResource(fileName),
- m_bundleVersion("1")
-{
- setName(QFileInfo(fileName).completeBaseName());
- m_metadata["generator"] = "Krita (" + KritaVersionWrapper::versionString(true) + ")";
-
-}
-
-KisResourceBundle::~KisResourceBundle()
-{
-}
-
-QString KisResourceBundle::defaultFileExtension() const
-{
- return QString(".bundle");
-}
-
-bool KisResourceBundle::load()
-{
- if (filename().isEmpty()) return false;
- QScopedPointer<KoStore> resourceStore(KoStore::createStore(filename(), KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip));
-
- if (!resourceStore || resourceStore->bad()) {
- warnKrita << "Could not open store on bundle" << filename();
- m_installed = false;
- setValid(false);
- return false;
-
- } else {
-
- m_metadata.clear();
-
- bool toRecreate = false;
- if (resourceStore->open("META-INF/manifest.xml")) {
- if (!m_manifest.load(resourceStore->device())) {
- warnKrita << "Could not open manifest for bundle" << filename();
- return false;
- }
- resourceStore->close();
-
- Q_FOREACH (KisResourceBundleManifest::ResourceReference ref, m_manifest.files()) {
- if (!resourceStore->open(ref.resourcePath)) {
- warnKrita << "Bundle is broken. File" << ref.resourcePath << "is missing";
- toRecreate = true;
- }
- else {
- resourceStore->close();
- }
- }
-
-
- if(toRecreate) {
- warnKrita << "Due to missing files and wrong entries in the manifest, " << filename() << " will be recreated.";
- }
-
- } else {
- warnKrita << "Could not load META-INF/manifest.xml";
- return false;
- }
-
- bool versionFound = false;
- if (resourceStore->open("meta.xml")) {
- KoXmlDocument doc;
- if (!doc.setContent(resourceStore->device())) {
- warnKrita << "Could not parse meta.xml for" << filename();
- return false;
- }
- // First find the manifest:manifest node.
- KoXmlNode n = doc.firstChild();
- for (; !n.isNull(); n = n.nextSibling()) {
- if (!n.isElement()) {
- continue;
- }
- if (n.toElement().tagName() == "meta:meta") {
- break;
- }
- }
-
- if (n.isNull()) {
- warnKrita << "Could not find manifest node for bundle" << filename();
- return false;
- }
-
- const KoXmlElement metaElement = n.toElement();
- for (n = metaElement.firstChild(); !n.isNull(); n = n.nextSibling()) {
- if (n.isElement()) {
- KoXmlElement e = n.toElement();
- if (e.tagName() == "meta:generator") {
- m_metadata.insert("generator", e.firstChild().toText().data());
- }
- else if (e.tagName() == "dc:author") {
- m_metadata.insert("author", e.firstChild().toText().data());
- }
- else if (e.tagName() == "dc:title") {
- m_metadata.insert("title", e.firstChild().toText().data());
- }
- else if (e.tagName() == "dc:description") {
- m_metadata.insert("description", e.firstChild().toText().data());
- }
- else if (e.tagName() == "meta:initial-creator") {
- m_metadata.insert("author", e.firstChild().toText().data());
- }
- else if (e.tagName() == "dc:creator") {
- m_metadata.insert("author", e.firstChild().toText().data());
- }
- else if (e.tagName() == "meta:creation-date") {
- m_metadata.insert("created", e.firstChild().toText().data());
- }
- else if (e.tagName() == "meta:dc-date") {
- m_metadata.insert("updated", e.firstChild().toText().data());
- }
- else if (e.tagName() == "meta:meta-userdefined") {
- if (e.attribute("meta:name") == "tag") {
- m_bundletags << e.attribute("meta:value");
- }
- else {
- m_metadata.insert(e.attribute("meta:name"), e.attribute("meta:value"));
- }
- }
- else if(e.tagName() == "meta:bundle-version") {
- m_metadata.insert("bundle-version", e.firstChild().toText().data());
- versionFound = true;
- }
- }
- }
- resourceStore->close();
- }
- else {
- warnKrita << "Could not load meta.xml";
- return false;
- }
-
- if (resourceStore->open("preview.png")) {
- // Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice
- // fails with "libpng error: IDAT: CRC error"
- QByteArray data = resourceStore->device()->readAll();
- QBuffer buffer(&data);
- m_thumbnail.load(&buffer, "PNG");
- resourceStore->close();
- }
- else {
- warnKrita << "Could not open preview.png";
- }
-
- /*
- * If no version is found it's an old bundle with md5 hashes to fix, or if some manifest resource entry
- * doesn't not correspond to a file the bundle is "broken", in both cases we need to recreate the bundle.
- */
- if (!versionFound) {
- m_metadata.insert("bundle-version", "1");
- warnKrita << filename() << " has an old version and possibly wrong resources md5, so it will be recreated.";
- toRecreate = true;
- }
-
- if (toRecreate) {
- recreateBundle(resourceStore);
- }
-
-
- m_installed = true;
- setValid(true);
- setImage(m_thumbnail);
- }
-
- return true;
-}
-
-bool KisResourceBundle::loadFromDevice(QIODevice *)
-{
- return false;
-}
-
-bool saveResourceToStore(KoResource *resource, KoStore *store, const QString &resType)
-{
- if (!resource) {
- warnKrita << "No Resource";
- return false;
- }
-
- if (!resource->valid()) {
- warnKrita << "Resource is not valid";
- return false;
- }
- if (!store || store->bad()) {
- warnKrita << "No Store or Store is Bad";
- return false;
- }
-
- QByteArray ba;
- QBuffer buf;
-
- QFileInfo fi(resource->filename());
- if (fi.exists() && fi.isReadable()) {
-
- QFile f(resource->filename());
- if (!f.open(QFile::ReadOnly)) {
- warnKrita << "Could not open resource" << resource->filename();
- return false;
- }
- ba = f.readAll();
- if (ba.size() == 0) {
- warnKrita << "Resource is empty" << resource->filename();
- return false;
- }
- f.close();
- buf.setBuffer(&ba);
- }
- else {
- warnKrita << "Could not find the resource " << resource->filename() << " or it isn't readable";
- return false;
- }
-
- if (!buf.open(QBuffer::ReadOnly)) {
- warnKrita << "Could not open buffer";
- return false;
- }
- Q_ASSERT(!store->hasFile(resType + "/" + resource->shortFilename()));
- if (!store->open(resType + "/" + resource->shortFilename())) {
- warnKrita << "Could not open file in store for resource";
- return false;
- }
-
- bool res = (store->write(buf.data()) == buf.size());
- store->close();
- return res;
-
-}
-
-bool KisResourceBundle::save()
-{
- if (filename().isEmpty()) return false;
-
- addMeta("updated", QDateTime::currentDateTime().toOffsetFromUtc(0).toString(Qt::ISODate));
-
- QDir bundleDir = KoResourcePaths::saveLocation("data", "bundles");
- bundleDir.cdUp();
-
- QScopedPointer<KoStore> store(KoStore::createStore(filename(), KoStore::Write, "application/x-krita-resourcebundle", KoStore::Zip));
-
- if (!store || store->bad()) return false;
-
- Q_FOREACH (const QString &resType, m_manifest.types()) {
-
- if (resType == "ko_gradients") {
- KoResourceServer<KoAbstractGradient>* gradientServer = KoResourceServerProvider::instance()->gradientServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
- KoResource *res = gradientServer->resourceByMD5(ref.md5sum);
- if (!res) res = gradientServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName());
- if (!saveResourceToStore(res, store.data(), "gradients")) {
- if (res) {
- warnKrita << "Could not save resource" << resType << res->name();
- }
- else {
- warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
- }
- }
- }
- }
- else if (resType == "ko_patterns") {
- KoResourceServer<KoPattern>* patternServer = KoResourceServerProvider::instance()->patternServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
- KoResource *res = patternServer->resourceByMD5(ref.md5sum);
- if (!res) res = patternServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName());
- if (!saveResourceToStore(res, store.data(), "patterns")) {
- if (res) {
- warnKrita << "Could not save resource" << resType << res->name();
- }
- else {
- warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
- }
- }
- }
- }
- else if (resType == "kis_brushes") {
- KisBrushResourceServer* brushServer = KisBrushServer::instance()->brushServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
- KisBrushSP brush = brushServer->resourceByMD5(ref.md5sum);
- if (!brush) brush = brushServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName());
- KoResource *res = brush.data();
- if (!saveResourceToStore(res, store.data(), "brushes")) {
- if (res) {
- warnKrita << "Could not save resource" << resType << res->name();
- }
- else {
- warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
- }
- }
- }
- }
- else if (resType == "ko_palettes") {
- KoResourceServer<KoColorSet>* paletteServer = KoResourceServerProvider::instance()->paletteServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
- KoResource *res = paletteServer->resourceByMD5(ref.md5sum);
- if (!res) res = paletteServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName());
- if (!saveResourceToStore(res, store.data(), "palettes")) {
- if (res) {
- warnKrita << "Could not save resource" << resType << res->name();
- }
- else {
- warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
- }
- }
- }
- }
- else if (resType == "kis_workspaces") {
- KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
- KoResource *res = workspaceServer->resourceByMD5(ref.md5sum);
- if (!res) res = workspaceServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName());
- if (!saveResourceToStore(res, store.data(), "workspaces")) {
- if (res) {
- warnKrita << "Could not save resource" << resType << res->name();
- }
- else {
- warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
- }
- }
- }
- }
- else if (resType == "kis_paintoppresets") {
- KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
- KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(ref.md5sum);
- if (!res) res = paintoppresetServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName());
- if (!saveResourceToStore(res.data(), store.data(), "paintoppresets")) {
- if (res) {
- warnKrita << "Could not save resource" << resType << res->name();
- }
- else {
- warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
- }
- }
- }
- }
- else if (resType == "ko_gamutmasks") {
- KoResourceServer<KoGamutMask>* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
- KoResource *res = gamutMaskServer->resourceByMD5(ref.md5sum);
- if (!res) res = gamutMaskServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName());
- if (!saveResourceToStore(res, store.data(), "gamutmasks")) {
- if (res) {
- warnKrita << "Could not save resource" << resType << res->name();
- }
- else {
- warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
- }
- }
- }
- }
- }
-
- if (!m_thumbnail.isNull()) {
- QByteArray byteArray;
- QBuffer buffer(&byteArray);
- m_thumbnail.save(&buffer, "PNG");
- if (!store->open("preview.png")) warnKrita << "Could not open preview.png";
- if (store->write(byteArray) != buffer.size()) warnKrita << "Could not write preview.png";
- store->close();
- }
-
- saveManifest(store);
-
- saveMetadata(store);
-
- store->finalize();
-
- return true;
-}
-
-bool KisResourceBundle::saveToDevice(QIODevice */*dev*/) const
-{
- return false;
-}
-
-bool KisResourceBundle::install()
-{
- QStringList md5Mismatch;
- if (filename().isEmpty()) {
- warnKrita << "Cannot install bundle: no file name" << this;
- return false;
- }
- QScopedPointer<KoStore> resourceStore(KoStore::createStore(filename(), KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip));
-
- if (!resourceStore || resourceStore->bad()) {
- warnKrita << "Cannot open the resource bundle: invalid zip file?";
- return false;
- }
-
- Q_FOREACH (const QString &resType, m_manifest.types()) {
- dbgResources << "Installing resource type" << resType;
- if (resType == "gradients") {
- KoResourceServer<KoAbstractGradient>* gradientServer = KoResourceServerProvider::instance()->gradientServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
-
- if (resourceStore->isOpen()) resourceStore->close();
-
- dbgResources << "\tInstalling" << ref.resourcePath;
- KoAbstractGradient *res = gradientServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath));
- if (!res) {
- warnKrita << "Could not create resource for" << ref.resourcePath;
- continue;
- }
- if (!resourceStore->open(ref.resourcePath)) {
- warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- if (!res->loadFromDevice(resourceStore->device())) {
- warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- dbgResources << "\t\tresource:" << res->name();
-
- KoAbstractGradient *res2 = gradientServer->resourceByName(res->name());
- if (!res2) {//if it doesn't exist...
- gradientServer->addResource(res, false);//add it!
-
- if (!m_gradientsMd5Installed.contains(res->md5())) {
- m_gradientsMd5Installed.append(res->md5());
- }
- if (ref.md5sum!=res->md5()) {
- md5Mismatch.append(res->name());
- }
-
- Q_FOREACH (const QString &tag, ref.tagList) {
- gradientServer->addTag(res, tag);
- }
- //gradientServer->addTag(res, name());
- }
- else {
- //warnKrita << "Didn't install" << res->name()<<"It already exists on the server";
- }
-
- }
- }
- else if (resType == "patterns") {
- KoResourceServer<KoPattern>* patternServer = KoResourceServerProvider::instance()->patternServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
-
- if (resourceStore->isOpen()) resourceStore->close();
-
- dbgResources << "\tInstalling" << ref.resourcePath;
- KoPattern *res = patternServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath));
- if (!res) {
- warnKrita << "Could not create resource for" << ref.resourcePath;
- continue;
- }
- if (!resourceStore->open(ref.resourcePath)) {
- warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- if (!res->loadFromDevice(resourceStore->device())) {
- warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- dbgResources << "\t\tresource:" << res->name();
-
- KoPattern *res2 = patternServer->resourceByName(res->name());
- if (!res2) {//if it doesn't exist...
- patternServer->addResource(res, false);//add it!
-
- if (!m_patternsMd5Installed.contains(res->md5())) {
- m_patternsMd5Installed.append(res->md5());
- }
- if (ref.md5sum!=res->md5()) {
- md5Mismatch.append(res->name());
- }
-
- Q_FOREACH (const QString &tag, ref.tagList) {
- patternServer->addTag(res, tag);
- }
- //patternServer->addTag(res, name());
- }
-
- }
- }
- else if (resType == "brushes") {
- KisBrushResourceServer *brushServer = KisBrushServer::instance()->brushServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
-
- if (resourceStore->isOpen()) resourceStore->close();
-
- dbgResources << "\tInstalling" << ref.resourcePath;
- KisBrushSP res = brushServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath));
- if (!res) {
- warnKrita << "Could not create resource for" << ref.resourcePath;
- continue;
- }
- if (!resourceStore->open(ref.resourcePath)) {
- warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- if (!res->loadFromDevice(resourceStore->device())) {
- warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- dbgResources << "\t\tresource:" << res->name();
-
- //find the resource on the server
- KisBrushSP res2 = brushServer->resourceByName(res->name());
- if (res2) {
- res->setName(res->name()+"("+res->shortFilename()+")");
- }
- // file name is more important than the regular name because the
- // it is the way how it is called up from the brushpreset settings.
- // Therefore just adjust the resource name and only refuse to load
- // when the filename is different.
- res2 = brushServer->resourceByFilename(res->shortFilename());
- if (!res2) {//if it doesn't exist...
- brushServer->addResource(res, false);//add it!
-
- if (!m_brushesMd5Installed.contains(res->md5())) {
- m_brushesMd5Installed.append(res->md5());
- }
- if (ref.md5sum!=res->md5()) {
- md5Mismatch.append(res->name());
- }
-
- Q_FOREACH (const QString &tag, ref.tagList) {
- brushServer->addTag(res.data(), tag);
- }
- //brushServer->addTag(res.data(), name());
- }
- else {
- //warnKrita << "Didn't install" << res->name()<<"It already exists on the server";
- }
- }
- }
- else if (resType == "palettes") {
- KoResourceServer<KoColorSet>* paletteServer = KoResourceServerProvider::instance()->paletteServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
-
- if (resourceStore->isOpen()) resourceStore->close();
-
- dbgResources << "\tInstalling" << ref.resourcePath;
- KoColorSet *res = paletteServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath));
-
- if (!res) {
- warnKrita << "Could not create resource for" << ref.resourcePath;
- continue;
- }
- if (!resourceStore->open(ref.resourcePath)) {
- warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- if (!res->loadFromDevice(resourceStore->device())) {
- warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- dbgResources << "\t\tresource:" << res->name();
-
- //find the resource on the server
- KoColorSet *res2 = paletteServer->resourceByName(res->name());
- if (!res2) {//if it doesn't exist...
- paletteServer->addResource(res, false);//add it!
-
- if (!m_palettesMd5Installed.contains(res->md5())) {
- m_palettesMd5Installed.append(res->md5());
- }
- if (ref.md5sum!=res->md5()) {
- md5Mismatch.append(res->name());
- }
-
- Q_FOREACH (const QString &tag, ref.tagList) {
- paletteServer->addTag(res, tag);
- }
- //paletteServer->addTag(res, name());
- }
- else {
- //warnKrita << "Didn't install" << res->name()<<"It already exists on the server";
- }
- }
- }
- else if (resType == "workspaces") {
- KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
-
- if (resourceStore->isOpen()) resourceStore->close();
-
- dbgResources << "\tInstalling" << ref.resourcePath;
- KisWorkspaceResource *res = workspaceServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath));
- if (!res) {
- warnKrita << "Could not create resource for" << ref.resourcePath;
- continue;
- }
- if (!resourceStore->open(ref.resourcePath)) {
- warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- if (!res->loadFromDevice(resourceStore->device())) {
- warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- dbgResources << "\t\tresource:" << res->name();
-
- //the following tries to find the resource by name.
- KisWorkspaceResource *res2 = workspaceServer->resourceByName(res->name());
- if (!res2) {//if it doesn't exist...
- workspaceServer->addResource(res, false);//add it!
-
- if (!m_workspacesMd5Installed.contains(res->md5())) {
- m_workspacesMd5Installed.append(res->md5());
- }
- if (ref.md5sum!=res->md5()) {
- md5Mismatch.append(res->name());
- }
-
- Q_FOREACH (const QString &tag, ref.tagList) {
- workspaceServer->addTag(res, tag);
- }
- //workspaceServer->addTag(res, name());
- }
- else {
- //warnKrita << "Didn't install" << res->name()<<"It already exists on the server";
- }
-
- }
- }
- else if (resType == "paintoppresets") {
- KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
-
- if (resourceStore->isOpen()) resourceStore->close();
-
- dbgResources << "\tInstalling" << ref.resourcePath;
- KisPaintOpPresetSP res = paintoppresetServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath));
-
- if (!res) {
- warnKrita << "Could not create resource for" << ref.resourcePath;
- continue;
- }
- if (!resourceStore->open(ref.resourcePath)) {
- warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- // Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice
- // fails with "libpng error: IDAT: CRC error"
- QByteArray data = resourceStore->device()->readAll();
- QBuffer buffer(&data);
- if (!res->loadFromDevice(&buffer)) {
- warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- dbgResources << "\t\tresource:" << res->name() << "File:" << res->filename();
-
- //the following tries to find the resource by name.
- KisPaintOpPresetSP res2 = paintoppresetServer->resourceByName(res->name());
- if (!res2) {//if it doesn't exist...
- paintoppresetServer->addResource(res, false);//add it!
- if (!m_presetsMd5Installed.contains(res->md5())){
- m_presetsMd5Installed.append(res->md5());
- }
- if (ref.md5sum!=res->md5()) {
- md5Mismatch.append(res->name());
- }
-
- Q_FOREACH (const QString &tag, ref.tagList) {
- paintoppresetServer->addTag(res.data(), tag);
- }
- //paintoppresetServer->addTag(res.data(), name());
- }
- else {
- //warnKrita << "Didn't install" << res->name()<<"It already exists on the server";
- }
-
- }
- }
- else if (resType == "gamutmasks") {
- KoResourceServer<KoGamutMask>* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer();
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
-
- if (resourceStore->isOpen()) resourceStore->close();
-
- dbgResources << "\tInstalling" << ref.resourcePath;
- KoGamutMask *res = gamutMaskServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath));
-
- if (!res) {
- warnKrita << "Could not create resource for" << ref.resourcePath;
- continue;
- }
- if (!resourceStore->open(ref.resourcePath)) {
- warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- if (!res->loadFromDevice(resourceStore->device())) {
- warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename();
- continue;
- }
- dbgResources << "\t\tresource:" << res->name();
-
- //find the resource on the server
- KoGamutMask *res2 = gamutMaskServer->resourceByName(res->name());
- if (!res2) {//if it doesn't exist...
- gamutMaskServer->addResource(res, false);//add it!
-
- if (!m_gamutMasksMd5Installed.contains(res->md5())) {
- m_gamutMasksMd5Installed.append(res->md5());
- }
- if (ref.md5sum!=res->md5()) {
- md5Mismatch.append(res->name());
- }
-
- Q_FOREACH (const QString &tag, ref.tagList) {
- gamutMaskServer->addTag(res, tag);
- }
- //gamutMaskServer->addTag(res, name());
- }
- else {
- //warnKrita << "Didn't install" << res->name()<<"It already exists on the server";
- }
- }
- }
- }
- m_installed = true;
- if(!md5Mismatch.isEmpty()){
- QString message = i18n("The following resources had mismatching MD5 sums. They may have gotten corrupted, for example, during download.");
- QMessageBox bundleFeedback;
- bundleFeedback.setIcon(QMessageBox::Warning);
- Q_FOREACH (QString name, md5Mismatch) {
- message.append("\n");
- message.append(name);
- }
- bundleFeedback.setText(message);
- bundleFeedback.exec();
- }
- return true;
-}
-
-bool KisResourceBundle::uninstall()
-{
-
- m_installed = false;
- QStringList tags = getTagsList();
- tags << m_manifest.tags();
- //tags << name();
-
- KoResourceServer<KoAbstractGradient>* gradientServer = KoResourceServerProvider::instance()->gradientServer();
- //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("gradients")) {
- Q_FOREACH (const QByteArray md5, m_gradientsMd5Installed) {
- KoAbstractGradient *res = gradientServer->resourceByMD5(md5);
- if (res) {
- gradientServer->removeResourceFromServer(res);
- }
- }
-
- KoResourceServer<KoPattern>* patternServer = KoResourceServerProvider::instance()->patternServer();
- //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("patterns")) {
- Q_FOREACH (const QByteArray md5, m_patternsMd5Installed) {
- KoPattern *res = patternServer->resourceByMD5(md5);
- if (res) {
- patternServer->removeResourceFromServer(res);
- }
- }
-
- KisBrushResourceServer *brushServer = KisBrushServer::instance()->brushServer();
- //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("brushes")) {
- Q_FOREACH (const QByteArray md5, m_brushesMd5Installed) {
- KisBrushSP res = brushServer->resourceByMD5(md5);
- if (res) {
- brushServer->removeResourceFromServer(res);
- }
- }
-
- KoResourceServer<KoColorSet>* paletteServer = KoResourceServerProvider::instance()->paletteServer();
- //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("palettes")) {
- Q_FOREACH (const QByteArray md5, m_palettesMd5Installed) {
- KoColorSet *res = paletteServer->resourceByMD5(md5);
- if (res) {
- paletteServer->removeResourceFromServer(res);
- }
- }
-
- KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer();
- //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("workspaces")) {
- Q_FOREACH (const QByteArray md5, m_workspacesMd5Installed) {
- KisWorkspaceResource *res = workspaceServer->resourceByMD5(md5);
- if (res) {
- workspaceServer->removeResourceFromServer(res);
- }
- }
- KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer();
- //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("paintoppresets")) {
- Q_FOREACH (const QByteArray md5, m_presetsMd5Installed) {
- KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(md5);
- if (res) {
- paintoppresetServer->removeResourceFromServer(res);
- }
- }
-
- KoResourceServer<KoGamutMask>* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer();
- //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("gamutmasks")) {
- Q_FOREACH (const QByteArray md5, m_gamutMasksMd5Installed) {
- KoGamutMask *res = gamutMaskServer->resourceByMD5(md5);
- if (res) {
- gamutMaskServer->removeResourceFromServer(res);
- }
- }
-
- Q_FOREACH(const QString &tag, tags) {
- paintoppresetServer->tagCategoryRemoved(tag);
- workspaceServer->tagCategoryRemoved(tag);
- paletteServer->tagCategoryRemoved(tag);
- brushServer->tagCategoryRemoved(tag);
- patternServer->tagCategoryRemoved(tag);
- gradientServer->tagCategoryRemoved(tag);
- gamutMaskServer->tagCategoryRemoved(tag);
- }
-
-
- return true;
-}
-
-void KisResourceBundle::addMeta(const QString &type, const QString &value)
-{
- m_metadata.insert(type, value);
-}
-
-const QString KisResourceBundle::getMeta(const QString &type, const QString &defaultValue) const
-{
- if (m_metadata.contains(type)) {
- return m_metadata[type];
- }
- else {
- return defaultValue;
- }
-}
-
-void KisResourceBundle::addResource(QString fileType, QString filePath, QStringList fileTagList, const QByteArray md5sum)
-{
- m_manifest.addResource(fileType, filePath, fileTagList, md5sum);
-}
-
-QList<QString> KisResourceBundle::getTagsList()
-{
- return QList<QString>::fromSet(m_bundletags);
-}
-
-
-bool KisResourceBundle::isInstalled()
-{
- return m_installed;
-}
-
-
-QStringList KisResourceBundle::resourceTypes() const
-{
- return m_manifest.types();
-}
-
-QList<KoResource*> KisResourceBundle::resources(const QString &resType) const
-{
- QList<KisResourceBundleManifest::ResourceReference> references = m_manifest.files(resType);
-
- QList<KoResource*> ret;
- Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, references) {
- if (resType == "gradients") {
- KoResourceServer<KoAbstractGradient>* gradientServer = KoResourceServerProvider::instance()->gradientServer();
- KoResource *res = gradientServer->resourceByMD5(ref.md5sum);
- if (res) ret << res;
- }
- else if (resType == "patterns") {
- KoResourceServer<KoPattern>* patternServer = KoResourceServerProvider::instance()->patternServer();
- KoResource *res = patternServer->resourceByMD5(ref.md5sum);
- if (res) ret << res;
- }
- else if (resType == "brushes") {
- KisBrushResourceServer *brushServer = KisBrushServer::instance()->brushServer();
- KoResource *res = brushServer->resourceByMD5(ref.md5sum).data();
- if (res) ret << res;
- }
- else if (resType == "palettes") {
- KoResourceServer<KoColorSet>* paletteServer = KoResourceServerProvider::instance()->paletteServer();
- KoResource *res = paletteServer->resourceByMD5(ref.md5sum);
- if (res) ret << res;
- }
- else if (resType == "workspaces") {
- KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer();
- KoResource *res = workspaceServer->resourceByMD5(ref.md5sum);
- if (res) ret << res;
- }
- else if (resType == "paintoppresets") {
- KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer();
- KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(ref.md5sum);
- if (res) ret << res.data();
- }
- else if (resType == "gamutmasks") {
- KoResourceServer<KoGamutMask>* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer();
- KoResource *res = gamutMaskServer->resourceByMD5(ref.md5sum);
- if (res) ret << res;
- }
- }
- return ret;
-}
-
-void KisResourceBundle::setThumbnail(QString filename)
-{
- if (QFileInfo(filename).exists()) {
- m_thumbnail = QImage(filename);
- m_thumbnail = m_thumbnail.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation);
- }
- else {
- m_thumbnail = QImage(256, 256, QImage::Format_ARGB32);
- QPainter gc(&m_thumbnail);
- gc.fillRect(0, 0, 256, 256, Qt::red);
- gc.end();
- }
-
- setImage(m_thumbnail);
-}
-
-void KisResourceBundle::writeMeta(const char *metaTag, const QString &metaKey, KoXmlWriter *writer)
-{
- if (m_metadata.contains(metaKey)) {
- writer->startElement(metaTag);
- writer->addTextNode(m_metadata[metaKey].toUtf8());
- writer->endElement();
- }
-}
-
-void KisResourceBundle::writeUserDefinedMeta(const QString &metaKey, KoXmlWriter *writer)
-{
- if (m_metadata.contains(metaKey)) {
- writer->startElement("meta:meta-userdefined");
- writer->addAttribute("meta:name", metaKey);
- writer->addAttribute("meta:value", m_metadata[metaKey]);
- writer->endElement();
- }
-}
-
-void KisResourceBundle::setInstalled(bool install)
-{
- m_installed = install;
-}
-
-void KisResourceBundle::saveMetadata(QScopedPointer<KoStore> &store)
-{
- QBuffer buf;
-
- store->open("meta.xml");
- buf.open(QBuffer::WriteOnly);
-
- KoXmlWriter metaWriter(&buf);
- metaWriter.startDocument("office:document-meta");
- metaWriter.startElement("meta:meta");
-
- writeMeta("meta:generator", "generator", &metaWriter);
-
- metaWriter.startElement("meta:bundle-version");
- metaWriter.addTextNode(m_bundleVersion.toUtf8());
- metaWriter.endElement();
-
- writeMeta("dc:author", "author", &metaWriter);
- writeMeta("dc:title", "filename", &metaWriter);
- writeMeta("dc:description", "description", &metaWriter);
- writeMeta("meta:initial-creator", "author", &metaWriter);
- writeMeta("dc:creator", "author", &metaWriter);
- writeMeta("meta:creation-date", "created", &metaWriter);
- writeMeta("meta:dc-date", "updated", &metaWriter);
- writeUserDefinedMeta("email", &metaWriter);
- writeUserDefinedMeta("license", &metaWriter);
- writeUserDefinedMeta("website", &metaWriter);
- Q_FOREACH (const QString &tag, m_bundletags) {
- metaWriter.startElement("meta:meta-userdefined");
- metaWriter.addAttribute("meta:name", "tag");
- metaWriter.addAttribute("meta:value", tag);
- metaWriter.endElement();
- }
-
- metaWriter.endElement(); // meta:meta
- metaWriter.endDocument();
-
- buf.close();
- store->write(buf.data());
- store->close();
-}
-
-void KisResourceBundle::saveManifest(QScopedPointer<KoStore> &store)
-{
- store->open("META-INF/manifest.xml");
- QBuffer buf;
- buf.open(QBuffer::WriteOnly);
- m_manifest.save(&buf);
- buf.close();
- store->write(buf.data());
- store->close();
-}
-
-void KisResourceBundle::recreateBundle(QScopedPointer<KoStore> &oldStore)
-{
- // Save a copy of the unmodified bundle, so that if anything goes bad the user doesn't lose it
- QFile file(filename());
- file.copy(filename() + ".old");
-
- QString newStoreName = filename() + ".tmp";
- {
- QScopedPointer<KoStore> store(KoStore::createStore(newStoreName, KoStore::Write, "application/x-krita-resourcebundle", KoStore::Zip));
- KoHashGenerator *generator = KoHashGeneratorProvider::instance()->getGenerator("MD5");
- KisResourceBundleManifest newManifest;
-
- addMeta("updated", QDateTime::currentDateTime().toString(Qt::ISODate));
-
- Q_FOREACH (KisResourceBundleManifest::ResourceReference ref, m_manifest.files()) {
- // Wrong manifest entry found, skip it
- if(!oldStore->open(ref.resourcePath))
- continue;
-
- store->open(ref.resourcePath);
-
- QByteArray data = oldStore->device()->readAll();
- oldStore->close();
- store->write(data);
- store->close();
- QByteArray result = generator->generateHash(data);
- newManifest.addResource(ref.fileTypeName, ref.resourcePath, ref.tagList, result);
- }
-
- m_manifest = newManifest;
-
- if (!m_thumbnail.isNull()) {
- QByteArray byteArray;
- QBuffer buffer(&byteArray);
- m_thumbnail.save(&buffer, "PNG");
- if (!store->open("preview.png")) warnKrita << "Could not open preview.png";
- if (store->write(byteArray) != buffer.size()) warnKrita << "Could not write preview.png";
- store->close();
- }
-
- saveManifest(store);
- saveMetadata(store);
-
- store->finalize();
- }
- // Remove the current bundle and then move the tmp one to be the correct one
- file.setFileName(filename());
- if (!file.remove()) {
- qWarning() << "Could not remove" << filename() << file.errorString();
- }
- QFile f(newStoreName);
- Q_ASSERT(f.exists());
- if (!f.copy(filename())) {
- qWarning() << "Could not copy the tmp file to the store" << filename() << newStoreName << QFile(newStoreName).exists() << f.errorString();
- }
-}
-
-
-int KisResourceBundle::resourceCount() const
-{
- return m_manifest.files().count();
-}
diff --git a/libs/ui/KisResourceBundle.h b/libs/ui/KisResourceBundle.h
deleted file mode 100644
index bd167bf4e7..0000000000
--- a/libs/ui/KisResourceBundle.h
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * 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 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 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.cpp b/libs/ui/KisResourceBundleManifest.cpp
deleted file mode 100644
index 0b14fc4f77..0000000000
--- a/libs/ui/KisResourceBundleManifest.cpp
+++ /dev/null
@@ -1,232 +0,0 @@
-/* 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/>.
-*/
-#include "KisResourceBundleManifest.h"
-
-#include <QList>
-#include <QString>
-#include <QDomDocument>
-#include <QDomElement>
-#include <QDomNode>
-#include <QDomNodeList>
-
-#include <KoXmlNS.h>
-#include <KoXmlReader.h>
-#include <KoXmlWriter.h>
-#include <resources/KoPattern.h>
-#include <resources/KoAbstractGradient.h>
-
-#include "kis_brush_server.h"
-#include "KisResourceServerProvider.h"
-#include <brushengine/kis_paintop_preset.h>
-#include "kis_workspace_resource.h"
-
-QString resourceTypeToManifestType(const QString &type) {
- if (type.startsWith("ko_")) {
- return type.mid(3);
- }
- else if (type.startsWith("kis_")) {
- return type.mid(4);
- }
- else {
- return type;
- }
-}
-
-QString manifestTypeToResourceType(const QString &type) {
- if (type == "patterns" || type == "gradients" || type == "palettes" || type == "gamutmasks") {
- return "ko_" + type;
- }
- else {
- return "kis_" + type;
- }
-}
-
-KisResourceBundleManifest::KisResourceBundleManifest()
-{
-}
-
-KisResourceBundleManifest::~KisResourceBundleManifest()
-{
-}
-
-bool KisResourceBundleManifest::load(QIODevice *device)
-{
- m_resources.clear();
- if (!device->isOpen()) {
- if (!device->open(QIODevice::ReadOnly)) {
- return false;
- }
- }
-
- KoXmlDocument manifestDocument;
- QString errorMessage;
- int errorLine;
- int errorColumn;
- if (!manifestDocument.setContent(device, true, &errorMessage, &errorLine, &errorColumn)) {
- return false;
- }
-
- if (!errorMessage.isEmpty()) {
- warnKrita << "Error parsing manifest" << errorMessage << "line" << errorLine << "column" << errorColumn;
- return false;
- }
-
- // First find the manifest:manifest node.
- KoXmlNode n = manifestDocument.firstChild();
- for (; !n.isNull(); n = n.nextSibling()) {
- if (!n.isElement()) {
- continue;
- }
- if (n.toElement().localName() == "manifest" && n.toElement().namespaceURI() == KoXmlNS::manifest) {
- break;
- }
- }
-
- if (n.isNull()) {
- // "Could not find manifest:manifest";
- return false;
- }
-
- // Now loop through the children of the manifest:manifest and
- // store all the manifest:file-entry elements.
- const KoXmlElement manifestElement = n.toElement();
- for (n = manifestElement.firstChild(); !n.isNull(); n = n.nextSibling()) {
-
- if (!n.isElement())
- continue;
-
- KoXmlElement el = n.toElement();
- if (!(el.localName() == "file-entry" && el.namespaceURI() == KoXmlNS::manifest))
- continue;
-
- QString fullPath = el.attributeNS(KoXmlNS::manifest, "full-path", QString());
- QString mediaType = el.attributeNS(KoXmlNS::manifest, "media-type", QString());
- QString md5sum = el.attributeNS(KoXmlNS::manifest, "md5sum", QString());
- QString version = el.attributeNS(KoXmlNS::manifest, "version", QString());
-
- QStringList tagList;
- KoXmlNode tagNode = n.firstChildElement().firstChildElement();
- while (!tagNode.isNull()) {
- if (tagNode.firstChild().isText()) {
- tagList.append(tagNode.firstChild().toText().data());
- }
- tagNode = tagNode.nextSibling();
- }
-
- // Only if fullPath is valid, should we store this entry.
- // If not, we don't bother to find out exactly what is wrong, we just skip it.
- if (!fullPath.isNull() && !mediaType.isEmpty() && !md5sum.isEmpty()) {
- addResource(mediaType, fullPath, tagList, QByteArray::fromHex(md5sum.toLatin1()));
- }
- }
-
- return true;
-}
-
-bool KisResourceBundleManifest::save(QIODevice *device)
-{
- if (!device->isOpen()) {
- if (!device->open(QIODevice::WriteOnly)) {
- return false;
- }
- }
- KoXmlWriter manifestWriter(device);
- manifestWriter.startDocument("manifest:manifest");
- manifestWriter.startElement("manifest:manifest");
- manifestWriter.addAttribute("xmlns:manifest", KoXmlNS::manifest);
- manifestWriter.addAttribute("manifest:version", "1.2");
- manifestWriter.addManifestEntry("/", "application/x-krita-resourcebundle");
-
- Q_FOREACH (QString resourceType, m_resources.uniqueKeys()) {
- Q_FOREACH (const ResourceReference &resource, m_resources[resourceType].values()) {
- manifestWriter.startElement("manifest:file-entry");
- manifestWriter.addAttribute("manifest:media-type", resourceTypeToManifestType(resourceType));
- manifestWriter.addAttribute("manifest:full-path", resourceTypeToManifestType(resourceType) + "/" + QFileInfo(resource.resourcePath).fileName());
- manifestWriter.addAttribute("manifest:md5sum", QString(resource.md5sum.toHex()));
- if (!resource.tagList.isEmpty()) {
- manifestWriter.startElement("manifest:tags");
- Q_FOREACH (const QString tag, resource.tagList) {
- manifestWriter.startElement("manifest:tag");
- manifestWriter.addTextNode(tag);
- manifestWriter.endElement();
- }
- manifestWriter.endElement();
- }
- manifestWriter.endElement();
- }
- }
-
- manifestWriter.endElement();
- manifestWriter.endDocument();
-
- return true;
-}
-
-void KisResourceBundleManifest::addResource(const QString &fileTypeName, const QString &fileName, const QStringList &fileTagList, const QByteArray &md5)
-{
- ResourceReference ref(fileName, fileTagList, fileTypeName, md5);
- if (!m_resources.contains(fileTypeName)) {
- m_resources[fileTypeName] = QMap<QString, ResourceReference>();
- }
- m_resources[fileTypeName].insert(fileName, ref);
-}
-
-QStringList KisResourceBundleManifest::types() const
-{
- return m_resources.keys();
-}
-
-QStringList KisResourceBundleManifest::tags() const
-{
- QSet<QString> tags;
- Q_FOREACH (const QString &type, m_resources.keys()) {
- Q_FOREACH (const ResourceReference &ref, m_resources[type].values()) {
- tags += ref.tagList.toSet();
- }
- }
- return QStringList::fromSet(tags);
-}
-
-QList<KisResourceBundleManifest::ResourceReference> KisResourceBundleManifest::files(const QString &type) const
-{
- // If no type is specified we return all the resources
- if(type.isEmpty()) {
- QList<ResourceReference> resources;
- QList<QMap<QString, ResourceReference> >::iterator i;
- QList<QMap<QString, ResourceReference> > values = m_resources.values();
- for(i = values.begin(); i != values.end(); ++i) {
- resources.append(i->values());
- }
-
- return resources;
- }
- else if (!m_resources.contains(type)) {
- return QList<KisResourceBundleManifest::ResourceReference>();
- }
- return m_resources[type].values();
-}
-
-void KisResourceBundleManifest::removeFile(QString fileName)
-{
- QList<QString> tags;
- Q_FOREACH (const QString &type, m_resources.keys()) {
- if (m_resources[type].contains(fileName)) {
- m_resources[type].remove(fileName);
- }
- }
-}
-
diff --git a/libs/ui/KisResourceBundleManifest.h b/libs/ui/KisResourceBundleManifest.h
deleted file mode 100644
index c854f88d51..0000000000
--- a/libs/ui/KisResourceBundleManifest.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* 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
- */
- 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 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/KisResourceBundleServerProvider.cpp b/libs/ui/KisResourceBundleServerProvider.cpp
deleted file mode 100644
index 8724dac768..0000000000
--- a/libs/ui/KisResourceBundleServerProvider.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * kis_resourceserver.cc - part of KImageShop
- *
- * 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>
- *
- * 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 "KisResourceBundleServerProvider.h"
-#include "KisResourceServerProvider.h"
-
-#include <QDir>
-#include <QApplication>
-#include <QGlobalStatic>
-
-#include <kis_debug.h>
-
-#include <KoResourcePaths.h>
-#include <resources/KoResource.h>
-#include <KoResourceServer.h>
-#include <KoResourceServerProvider.h>
-#include <KoResourceServerAdapter.h>
-
-Q_GLOBAL_STATIC(KisResourceBundleServerProvider, s_instance)
-
-KisResourceBundleServerProvider::KisResourceBundleServerProvider()
-{
- m_resourceBundleServer = new KoResourceServerSimpleConstruction<KisResourceBundle>("kis_resourcebundles", "*.bundle");
- QStringList files = KoResourceServerProvider::blacklistFileNames(m_resourceBundleServer->fileNames(), m_resourceBundleServer->blackListedFiles());
-// qDebug() << "Bundle files to load" << files;
- m_resourceBundleServer->loadResources(files);
-
- Q_FOREACH (KisResourceBundle *bundle, m_resourceBundleServer->resources()) {
- if (!bundle->install()) {
- warnKrita << "Could not install resources for bundle" << bundle->name();
- }
- }
-}
-
-KisResourceBundleServerProvider::~KisResourceBundleServerProvider()
-{
- delete m_resourceBundleServer;
-}
-
-KisResourceBundleServerProvider* KisResourceBundleServerProvider::instance()
-{
- return s_instance;
-}
-
-KoResourceServer<KisResourceBundle> *KisResourceBundleServerProvider::resourceBundleServer()
-{
- return m_resourceBundleServer;
-}
-
diff --git a/libs/ui/KisResourceBundleServerProvider.h b/libs/ui/KisResourceBundleServerProvider.h
deleted file mode 100644
index a578718fca..0000000000
--- a/libs/ui/KisResourceBundleServerProvider.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * kis_resource_server_provider.h - part of KImageShop
- *
- * 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) 2003-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_RESOURCEBUNDLESERVERPROVIDER_H_
-#define KIS_RESOURCEBUNDLESERVERPROVIDER_H_
-
-#include <KoResourceServer.h>
-#include <KoResourceServerAdapter.h>
-
-#include <brushengine/kis_paintop_preset.h>
-
-#include "KisResourceBundle.h"
-#include "kritaui_export.h"
-
-class KRITAUI_EXPORT KisResourceBundleServerProvider : public QObject
-{
- Q_OBJECT
-
-public:
- KisResourceBundleServerProvider();
- ~KisResourceBundleServerProvider() override;
-
- static KisResourceBundleServerProvider* instance();
-
- KoResourceServer<KisResourceBundle> *resourceBundleServer();
-
-private:
-
- KisResourceBundleServerProvider(const KisResourceBundleServerProvider&);
- KisResourceBundleServerProvider operator=(const KisResourceBundleServerProvider&);
- KoResourceServer<KisResourceBundle> *m_resourceBundleServer;
-};
-
-#endif // KIS_RESOURCESERVERPROVIDER_H_
diff --git a/libs/ui/KisResourceServerProvider.cpp b/libs/ui/KisResourceServerProvider.cpp
index 5360352ce8..2f07faf401 100644
--- a/libs/ui/KisResourceServerProvider.cpp
+++ b/libs/ui/KisResourceServerProvider.cpp
@@ -1,120 +1,100 @@
/*
* kis_resourceserver.cc - part of KImageShop
*
* 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>
*
* 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 "KisResourceServerProvider.h"
#include <QDir>
#include <QApplication>
#include <QGlobalStatic>
#include <kis_debug.h>
#include <KoResourcePaths.h>
-#include <resources/KoResource.h>
+#include <KoResource.h>
#include <KoResourceServer.h>
#include <KoResourceServerProvider.h>
-#include <KoResourceServerAdapter.h>
#include <resources/KoPattern.h>
#include <brushengine/kis_paintop_preset.h>
#include <kis_workspace_resource.h>
#include <KisWindowLayoutResource.h>
#include <KisSessionResource.h>
-#include <kis_psd_layer_style_resource.h>
+#include <kis_psd_layer_style.h>
-#include <kis_brush_server.h>
+#include <KoResourceServer.h>
Q_GLOBAL_STATIC(KisResourceServerProvider, s_instance)
-typedef KoResourceServerSimpleConstruction<KisPaintOpPreset, SharedPointerStoragePolicy<KisPaintOpPresetSP> > KisPaintOpPresetResourceServer;
-typedef KoResourceServerAdapter<KisPaintOpPreset, SharedPointerStoragePolicy<KisPaintOpPresetSP> > KisPaintOpPresetResourceServerAdapter;
+typedef KoResourceServer<KisPaintOpPreset> KisPaintOpPresetResourceServer;
+typedef KoResourceServer<KisPSDLayerStyle> KisPSDLayerStyleServer;
KisResourceServerProvider::KisResourceServerProvider()
{
- KisBrushServer *brushServer = KisBrushServer::instance();
-
- m_paintOpPresetServer = new KisPaintOpPresetResourceServer("kis_paintoppresets", "*.kpp");
- m_paintOpPresetServer->loadResources(KoResourceServerProvider::blacklistFileNames(m_paintOpPresetServer->fileNames(), m_paintOpPresetServer->blackListedFiles()));
-
- m_workspaceServer = new KoResourceServerSimpleConstruction<KisWorkspaceResource>("kis_workspaces", "*.kws");
- m_workspaceServer->loadResources(KoResourceServerProvider::blacklistFileNames(m_workspaceServer->fileNames(), m_workspaceServer->blackListedFiles()));
-
- m_windowLayoutServer = new KoResourceServerSimpleConstruction<KisWindowLayoutResource>("kis_windowlayouts", "*.kwl");
- m_windowLayoutServer->loadResources(KoResourceServerProvider::blacklistFileNames(m_windowLayoutServer->fileNames(), m_windowLayoutServer->blackListedFiles()));
-
- m_sessionServer = new KoResourceServerSimpleConstruction<KisSessionResource>("kis_sessions", "*.ksn");
- m_sessionServer->loadResources(KoResourceServerProvider::blacklistFileNames(m_sessionServer->fileNames(), m_sessionServer->blackListedFiles()));
-
- m_layerStyleCollectionServer = new KoResourceServerSimpleConstruction<KisPSDLayerStyleCollectionResource>("psd_layer_style_collections", "*.asl");
- m_layerStyleCollectionServer->loadResources(KoResourceServerProvider::blacklistFileNames(m_layerStyleCollectionServer->fileNames(), m_layerStyleCollectionServer->blackListedFiles()));
-
- connect(this, SIGNAL(notifyBrushBlacklistCleanup()),
- brushServer, SLOT(slotRemoveBlacklistedResources()));
-
+ m_paintOpPresetServer = new KisPaintOpPresetResourceServer(ResourceType::PaintOpPresets);
+ m_workspaceServer = new KoResourceServer<KisWorkspaceResource>(ResourceType::Workspaces);
+ m_windowLayoutServer = new KoResourceServer<KisWindowLayoutResource>(ResourceType::WindowLayouts);
+ m_sessionServer = new KoResourceServer<KisSessionResource>(ResourceType::Sessions);
+ m_layerStyleServer = new KisPSDLayerStyleServer(ResourceType::LayerStyles);
}
KisResourceServerProvider::~KisResourceServerProvider()
{
delete m_paintOpPresetServer;
delete m_workspaceServer;
delete m_sessionServer;
delete m_windowLayoutServer;
- delete m_layerStyleCollectionServer;
+ delete m_layerStyleServer;
}
KisResourceServerProvider* KisResourceServerProvider::instance()
{
return s_instance;
}
KisPaintOpPresetResourceServer* KisResourceServerProvider::paintOpPresetServer()
{
return m_paintOpPresetServer;
}
KoResourceServer< KisWorkspaceResource >* KisResourceServerProvider::workspaceServer()
{
return m_workspaceServer;
}
KoResourceServer< KisWindowLayoutResource >* KisResourceServerProvider::windowLayoutServer()
{
return m_windowLayoutServer;
}
KoResourceServer< KisSessionResource >* KisResourceServerProvider::sessionServer()
{
return m_sessionServer;
}
-KoResourceServer<KisPSDLayerStyleCollectionResource> *KisResourceServerProvider::layerStyleCollectionServer()
+KoResourceServer<KisPSDLayerStyle> *KisResourceServerProvider::layerStyleServer()
{
- return m_layerStyleCollectionServer;
+ return m_layerStyleServer;
}
-void KisResourceServerProvider::brushBlacklistCleanup()
-{
- emit notifyBrushBlacklistCleanup();
-}
diff --git a/libs/ui/KisResourceServerProvider.h b/libs/ui/KisResourceServerProvider.h
index 8bf6eed962..8a6b23cc24 100644
--- a/libs/ui/KisResourceServerProvider.h
+++ b/libs/ui/KisResourceServerProvider.h
@@ -1,75 +1,67 @@
/*
* kis_resource_server_provider.h - part of KImageShop
*
* 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) 2003-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_RESOURCESERVERPROVIDER_H_
#define KIS_RESOURCESERVERPROVIDER_H_
#include <KoResourceServer.h>
-#include <KoResourceServerAdapter.h>
-#include <KisResourceBundleServerProvider.h>
#include <brushengine/kis_paintop_preset.h>
#include "kritaui_export.h"
#include "KisWindowLayoutResource.h"
class KisWorkspaceResource;
class KisSessionResource;
-class KisPSDLayerStyleCollectionResource;
+class KisPSDLayerStyle;
-typedef KoResourceServerSimpleConstruction<KisPaintOpPreset, SharedPointerStoragePolicy<KisPaintOpPresetSP> > KisPaintOpPresetResourceServer;
-typedef KoResourceServerAdapter<KisPaintOpPreset, SharedPointerStoragePolicy<KisPaintOpPresetSP> > KisPaintOpPresetResourceServerAdapter;
+typedef KoResourceServer<KisPaintOpPreset> KisPaintOpPresetResourceServer;
class KRITAUI_EXPORT KisResourceServerProvider : public QObject
{
Q_OBJECT
public:
KisResourceServerProvider();
~KisResourceServerProvider() override;
static KisResourceServerProvider* instance();
KisPaintOpPresetResourceServer* paintOpPresetServer();
KoResourceServer<KisWorkspaceResource>* workspaceServer();
KoResourceServer<KisWindowLayoutResource>* windowLayoutServer();
KoResourceServer<KisSessionResource>* sessionServer();
- KoResourceServer<KisPSDLayerStyleCollectionResource>* layerStyleCollectionServer();
-
- void brushBlacklistCleanup();
-
-Q_SIGNALS:
- void notifyBrushBlacklistCleanup();
+ KoResourceServer<KisPSDLayerStyle>* layerStyleServer();
private:
KisResourceServerProvider(const KisResourceServerProvider&);
KisResourceServerProvider operator=(const KisResourceServerProvider&);
KisPaintOpPresetResourceServer *m_paintOpPresetServer;
KoResourceServer<KisWorkspaceResource> *m_workspaceServer;
KoResourceServer<KisWindowLayoutResource> *m_windowLayoutServer;
KoResourceServer<KisSessionResource> *m_sessionServer;
- KoResourceServer<KisPSDLayerStyleCollectionResource> *m_layerStyleCollectionServer;
+ KoResourceServer<KisPSDLayerStyle> *m_layerStyleServer;
};
#endif // KIS_RESOURCESERVERPROVIDER_H_
diff --git a/libs/ui/KisSessionResource.cpp b/libs/ui/KisSessionResource.cpp
index 0ac281b58c..c1feff8a21 100644
--- a/libs/ui/KisSessionResource.cpp
+++ b/libs/ui/KisSessionResource.cpp
@@ -1,188 +1,198 @@
/*
* 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 "KisSessionResource.h"
#include <QDomElement>
#include <KisPart.h>
#include <kis_properties_configuration.h>
#include <KisDocument.h>
#include <ksharedconfig.h>
#include <KisViewManager.h>
struct KisSessionResource::Private
{
struct View
{
QUuid windowId;
QUrl file;
KisPropertiesConfiguration viewConfig;
KisMainWindow *getWindow() const {
Q_FOREACH(KisMainWindow *window, KisPart::instance()->mainWindows()) {
if (window->id() == this->windowId) return window;
}
return nullptr;
}
};
QString profileName;
QVector<View> views;
};
KisSessionResource::KisSessionResource(const QString &filename)
: KisWindowLayoutResource(filename)
, d(new Private)
{}
KisSessionResource::~KisSessionResource()
{}
+KisSessionResource::KisSessionResource(const KisSessionResource &rhs)
+ : KisWindowLayoutResource(rhs)
+ , d(new Private(*rhs.d))
+{
+}
+
+KoResourceSP KisSessionResource::clone() const
+{
+ return KoResourceSP(new KisSessionResource(*this));
+}
+
void KisSessionResource::restore()
{
auto *kisPart = KisPart::instance();
applyLayout();
QMap<QUrl, KisDocument *> documents;
// Find documents which are already open so we don't need to reload them
QList<QPointer<KisView>> oldViews = kisPart->views();
Q_FOREACH(const QPointer<KisView> view, oldViews) {
KisDocument *document = view->document();
const QUrl url = document->url();
documents.insert(url, document);
}
Q_FOREACH(auto &viewData, d->views) {
QUrl url = viewData.file;
KisMainWindow *window = viewData.getWindow();
if (!window) {
qDebug() << "Warning: session file contains inconsistent data.";
} else {
KisDocument *document = documents.value(url);
if (!document) {
document = kisPart->createDocument();
bool ok = document->openUrl(url);
if (!ok) {
delete document;
continue;
}
kisPart->addDocument(document);
documents.insert(url, document);
}
//update profile
QString profileName;
profileName = d->profileName;
window->viewManager()->changeAuthorProfile(profileName);
window->viewManager()->slotUpdateAuthorProfileActions();
KisView *view = window->newView(document);
view->restoreViewState(viewData.viewConfig);
}
}
Q_FOREACH(QPointer<KisView> view, oldViews) {
view->closeView();
}
- kisPart->setCurrentSession(this);
}
QString KisSessionResource::defaultFileExtension() const
{
return ".ksn";
}
void KisSessionResource::storeCurrentWindows()
{
KisPart *kisPart = KisPart::instance();
const auto &windows = kisPart->mainWindows();
setWindows(windows);
d->views.clear();
Q_FOREACH(const KisView *view, kisPart->views()) {
if (view->document()->url().isEmpty()) continue;
auto viewData = Private::View();
viewData.windowId = view->mainWindow()->id();
viewData.file = view->document()->url();
view->saveViewState(viewData.viewConfig);
d->views.append(viewData);
}
setValid(true);
}
void KisSessionResource::saveXml(QDomDocument &doc, QDomElement &root) const
{
KisWindowLayoutResource::saveXml(doc, root);
Q_FOREACH(const auto view, d->views) {
QDomElement elem = doc.createElement("view");
elem.setAttribute("window", view.windowId.toString());
elem.setAttribute("src", view.file.toString());
view.viewConfig.toXML(doc, elem);
root.appendChild(elem);
// Save profile
KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
QString profileName = appAuthorGroup.readEntry("active-profile", "");
QDomElement session = doc.createElement("session");
session.setAttribute("profile", profileName);
root.appendChild(session);
}
}
void KisSessionResource::loadXml(const QDomElement &root) const
{
KisWindowLayoutResource::loadXml(root);
d->views.clear();
for (auto viewElement = root.firstChildElement("view");
!viewElement.isNull();
viewElement = viewElement.nextSiblingElement("view")) {
Private::View view;
view.file = QUrl(viewElement.attribute("src"));
view.windowId = QUuid(viewElement.attribute("window"));
view.viewConfig.fromXML(viewElement);
d->views.append(view);
}
//Load session
d->profileName.clear();
auto sessionElement = root.firstChildElement("session");
d->profileName = QString(sessionElement.attribute("profile"));
}
diff --git a/libs/ui/KisSessionResource.h b/libs/ui/KisSessionResource.h
index 6ae173bcc7..161b58b363 100644
--- a/libs/ui/KisSessionResource.h
+++ b/libs/ui/KisSessionResource.h
@@ -1,46 +1,61 @@
/*
* 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.
*/
#ifndef KISSESSIONRESOURCE_H
#define KISSESSIONRESOURCE_H
#include "KisWindowLayoutResource.h"
class KisSessionResource : public KisWindowLayoutResource
{
public:
KisSessionResource(const QString &filename);
~KisSessionResource();
+ KisSessionResource(const KisSessionResource &rhs);
+ KisSessionResource &operator=(const KisSessionResource &rhs) = delete;
+ KoResourceSP clone() const override;
void storeCurrentWindows();
- void restore();
+
QString defaultFileExtension() const override;
protected:
void saveXml(QDomDocument &doc, QDomElement &root) const override;
void loadXml(const QDomElement &root) const override;
+ QPair<QString, QString> resourceType() const override
+ {
+ return QPair<QString, QString>(ResourceType::Sessions, "");
+ }
+
private:
+
+ // Only KisPart should be able to call restore() to make sure it contains the pointer to it
+ void restore();
+ friend class KisPart;
+
struct Private;
QScopedPointer<Private> d;
};
+typedef QSharedPointer<KisSessionResource> KisSessionResourceSP;
+
#endif
diff --git a/libs/ui/KisSyncedAudioPlayback.cpp b/libs/ui/KisSyncedAudioPlayback.cpp
index 6f311269e9..684e2622cd 100644
--- a/libs/ui/KisSyncedAudioPlayback.cpp
+++ b/libs/ui/KisSyncedAudioPlayback.cpp
@@ -1,148 +1,148 @@
#include "KisSyncedAudioPlayback.h"
#include "config-qtmultimedia.h"
#ifdef HAVE_QT_MULTIMEDIA
#include <QtMultimedia/QMediaPlayer>
#else
class QIODevice;
#include <QUrl>
namespace {
class QMediaPlayer : public QObject {
Q_OBJECT
public:
enum Error
{
NoError,
ResourceError,
FormatError,
NetworkError,
AccessDeniedError,
ServiceMissingError,
MediaIsPlaylist
};
enum State
{
StoppedState,
PlayingState,
PausedState
};
State state() const { return StoppedState; }
void play() {}
void stop() {}
qint64 position() const { return 0; }
qreal playbackRate() const { return 1.0; }
void setPosition(qint64) {}
void setPlaybackRate(qreal) {}
void setVolume(int) {}
void setMedia(const QUrl&, QIODevice * device = 0) { Q_UNUSED(device);}
QString errorString() const { return QString(); }
Q_SIGNALS:
void error(Error value);
};
}
#endif
#include <QFileInfo>
struct KisSyncedAudioPlayback::Private
{
QMediaPlayer player;
qint64 tolerance = 200;
};
KisSyncedAudioPlayback::KisSyncedAudioPlayback(const QString &fileName)
: QObject(0),
m_d(new Private)
{
QFileInfo fileInfo(fileName);
Q_ASSERT(fileInfo.exists());
m_d->player.setMedia(QUrl::fromLocalFile(fileInfo.absoluteFilePath()));
m_d->player.setVolume(50);
connect(&m_d->player, SIGNAL(error(QMediaPlayer::Error)), SLOT(slotOnError()));
}
KisSyncedAudioPlayback::~KisSyncedAudioPlayback()
{
}
void KisSyncedAudioPlayback::setSoundOffsetTolerance(qint64 value)
{
m_d->tolerance = value;
}
void KisSyncedAudioPlayback::syncWithVideo(qint64 position)
{
if (qAbs(position - m_d->player.position()) > m_d->tolerance) {
m_d->player.setPosition(position);
}
}
bool KisSyncedAudioPlayback::isPlaying() const
{
return m_d->player.state() == QMediaPlayer::PlayingState;
}
qint64 KisSyncedAudioPlayback::position() const
{
return m_d->player.position();
}
void KisSyncedAudioPlayback::setVolume(qreal value)
{
m_d->player.setVolume(qRound(100.0 * value));
}
void KisSyncedAudioPlayback::setSpeed(qreal value)
{
if (qFuzzyCompare(value, m_d->player.playbackRate())) return;
if (m_d->player.state() == QMediaPlayer::PlayingState) {
const qint64 oldPosition = m_d->player.position();
m_d->player.stop();
m_d->player.setPlaybackRate(value);
m_d->player.setPosition(oldPosition);
m_d->player.play();
} else {
m_d->player.setPlaybackRate(value);
}
}
void KisSyncedAudioPlayback::play(qint64 startPosition)
{
m_d->player.setPosition(startPosition);
m_d->player.play();
}
void KisSyncedAudioPlayback::stop()
{
m_d->player.stop();
}
void KisSyncedAudioPlayback::slotOnError()
{
#ifdef HAVE_QT_MULTIMEDIA
- emit error(m_d->player.media().canonicalUrl().toLocalFile(), m_d->player.errorString());
+ emit error(m_d->player.media().request().url().toLocalFile(), m_d->player.errorString());
#endif
}
#ifndef HAVE_QT_MULTIMEDIA
#include "KisSyncedAudioPlayback.moc"
#endif
diff --git a/libs/ui/KisViewManager.cpp b/libs/ui/KisViewManager.cpp
index 081d41507e..279093646f 100644
--- a/libs/ui/KisViewManager.cpp
+++ b/libs/ui/KisViewManager.cpp
@@ -1,1457 +1,1451 @@
/*
* 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 <amy@amyspark.me>
*
* 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 <KisResourceItemChooserSync.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)),
canvasResourceProvider(), 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 (canvasResourceProvider() && canvasResourceProvider()->currentPreset()) {
cfg.writeKoColor("LastForeGroundColor",canvasResourceProvider()->fgColor());
cfg.writeKoColor("LastBackGroundColor",canvasResourceProvider()->bgColor());
}
- cfg.writeEntry("baseLength", KoResourceItemChooserSync::instance()->baseLength());
+ cfg.writeEntry("baseLength", KisResourceItemChooserSync::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 (canvasResourceProvider() && canvasResourceProvider()->currentPreset()) {
cfg.writeEntry("LastPreset", canvasResourceProvider()->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 = imageView->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();
+ KisResourceModel *resourceModel = rserver->resourceModel();
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;
+ for (int i = 0; i < resourceModel->rowCount(); i++) {
+
+ QModelIndex idx = resourceModel->index(i, 0);
+
+ QString resourceName = idx.data(Qt::UserRole + KisResourceModel::Name).toString().toLower();
+ QString fileName = idx.data(Qt::UserRole + KisResourceModel::Filename).toString().toLower();
+
+ if (resourceName.contains("basic_tip_default")) {
+ defaultPresetName = resourceName;
+ }
+ else if (resourceName.contains("default") || fileName.contains("default")) {
+ defaultPresetName = resourceName;
}
}
+
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 && rserver->resourceCount() > 0) {
+ preset = rserver->firstResource();
}
if (preset) {
- paintOpBox()->restoreResource(preset.data());
+ paintOpBox()->restoreResource(preset);
canvasResourceProvider()->setCurrentCompositeOp(preset->settings()->paintOpCompositeOp());
}
}
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)),
canvasResourceProvider(), SLOT(slotImageSizeChanged()));
d->viewConnections.addUniqueConnection(
image(), SIGNAL(sigResolutionChanged(double,double)),
canvasResourceProvider(), SLOT(slotOnScreenResolutionChanged()));
d->viewConnections.addUniqueConnection(
image(), SIGNAL(sigNodeChanged(KisNodeSP)),
this, SLOT(updateGUI()));
d->viewConnections.addUniqueConnection(
d->currentImageView->zoomManager()->zoomController(),
SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)),
canvasResourceProvider(), SLOT(slotOnScreenResolutionChanged()));
}
d->actionManager.updateGUI();
canvasResourceProvider()->slotImageSizeChanged();
canvasResourceProvider()->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::canvasResourceProvider()
{
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;
}
QUrl newUrl = QUrl::fromUserInput(fileName);
document()->setFileBatchMode(true);
document()->saveAs(newUrl, document()->mimeType(), true);
document()->setFileBatchMode(false);
KisPart::instance()->addRecentURLToAllMainWindows(newUrl, document()->url());
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();
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()));
}
void KisViewManager::slotResetRotation()
{
KisCanvasController *canvasController = d->currentImageView->canvasController();
canvasController->resetCanvasRotation();
}
void KisViewManager::slotToggleFullscreen()
{
KisConfig cfg(false);
KisMainWindow *main = mainWindow();
main->viewFullscreen(!main->isFullScreen());
cfg.fullscreenMode(main->isFullScreen());
}
diff --git a/libs/ui/KisViewManager.h b/libs/ui/KisViewManager.h
index 2b33401753..0e0536bb8f 100644
--- a/libs/ui/KisViewManager.h
+++ b/libs/ui/KisViewManager.h
@@ -1,263 +1,262 @@
/*
* 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 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 *canvasResourceProvider();
/// 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();
void slotResetRotation();
void slotToggleFullscreen();
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 2a96e0bbad..652bdb46bf 100644
--- a/libs/ui/KisWelcomePageWidget.cpp
+++ b/libs/ui/KisWelcomePageWidget.cpp
@@ -1,460 +1,460 @@
/* 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 <QMimeData>
#include <QPixmap>
#include <QImage>
#include <KisMimeDatabase.h>
#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"
#include "KisDocument.h"
#include <kis_image.h>
#include <kis_paint_device.h>
#include <KisPart.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->setTextFormat(Qt::RichText);
manualLink->setTextInteractionFlags(Qt::TextBrowserInteraction);
manualLink->setOpenExternalLinks(true);
gettingStartedLink->setTextFormat(Qt::RichText);
gettingStartedLink->setTextInteractionFlags(Qt::TextBrowserInteraction);
gettingStartedLink->setOpenExternalLinks(true);
supportKritaLink->setTextFormat(Qt::RichText);
supportKritaLink->setTextInteractionFlags(Qt::TextBrowserInteraction);
supportKritaLink->setOpenExternalLinks(true);
userCommunityLink->setTextFormat(Qt::RichText);
userCommunityLink->setTextInteractionFlags(Qt::TextBrowserInteraction);
userCommunityLink->setOpenExternalLinks(true);
kritaWebsiteLink->setTextFormat(Qt::RichText);
kritaWebsiteLink->setTextInteractionFlags(Qt::TextBrowserInteraction);
kritaWebsiteLink->setOpenExternalLinks(true);
sourceCodeLink->setTextFormat(Qt::RichText);
sourceCodeLink->setTextInteractionFlags(Qt::TextBrowserInteraction);
sourceCodeLink->setOpenExternalLinks(true);
poweredByKDELink->setTextFormat(Qt::RichText);
poweredByKDELink->setTextInteractionFlags(Qt::TextBrowserInteraction);
poweredByKDELink->setOpenExternalLinks(true);
kdeIcon->setIconSize(QSize(20, 20));
kdeIcon->setIcon(KisIconUtils::loadIcon(QStringLiteral("kde")).pixmap(20));
versionNotificationLabel->setTextFormat(Qt::RichText);
versionNotificationLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
versionNotificationLabel->setOpenExternalLinks(true);
connect(chkShowNews, SIGNAL(toggled(bool)), newsWidget, SLOT(toggleNews(bool)));
connect(newsWidget, SIGNAL(newsDataChanged()), this, SLOT(slotUpdateVersionMessage()));
#ifdef Q_OS_ANDROID
// checking this widgets crashes the app, so it is better for it to be hidden for now
newsWidget->hide();
helpTitleLabel_2->hide();
chkShowNews->hide();
#endif
// configure the News area
KisConfig cfg(true);
bool m_getNews = cfg.readEntry<bool>("FetchNews", false);
chkShowNews->setChecked(m_getNews);
setAcceptDrops(true);
}
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)), mainWin, SLOT(clearRecentFiles()));
slotUpdateThemeColors();
// allows RSS news items to apply analytics tracking.
newsWidget->setAnalyticsTracking("?" + analyticsString);
}
}
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()
{
textColor = qApp->palette().color(QPalette::Text);
backgroundColor = qApp->palette().color(QPalette::Background);
// make the welcome screen labels a subtle color so it doesn't clash with the main UI elements
blendedColor = KritaUtils::blendColors(textColor, backgroundColor, 0.8);
blendedStyle = QString("color: ").append(blendedColor.name());
// what labels to change the color...
startTitleLabel->setStyleSheet(blendedStyle);
recentDocumentsLabel->setStyleSheet(blendedStyle);
helpTitleLabel->setStyleSheet(blendedStyle);
newFileLinkShortcut->setStyleSheet(blendedStyle);
openFileShortcut->setStyleSheet(blendedStyle);
clearRecentFilesLink->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"));
kdeIcon->setIcon(KisIconUtils::loadIcon(QStringLiteral("kde")).pixmap(20));
// HTML links seem to be a bit more stubborn with theme changes... setting inline styles to help with color change
userCommunityLink->setText(QString("<a style=\"color: " + blendedColor.name() + " \" href=\"https://forum.kde.org/viewforum.php?f=136&" + analyticsString + "user-community" + "\">")
.append(i18n("User Community")).append("</a>"));
gettingStartedLink->setText(QString("<a style=\"color: " + blendedColor.name() + " \" href=\"https://docs.krita.org/en/user_manual/getting_started.html?" + analyticsString + "getting-started" + "\">")
.append(i18n("Getting Started")).append("</a>"));
manualLink->setText(QString("<a style=\"color: " + blendedColor.name() + " \" href=\"https://docs.krita.org?" + analyticsString + "documentation-site" + "\">")
.append(i18n("User Manual")).append("</a>"));
supportKritaLink->setText(QString("<a style=\"color: " + blendedColor.name() + " \" href=\"https://krita.org/en/support-us/donations?" + analyticsString + "donations" + "\">")
.append(i18n("Support Krita")).append("</a>"));
kritaWebsiteLink->setText(QString("<a style=\"color: " + blendedColor.name() + " \" href=\"https://www.krita.org?" + analyticsString + "marketing-site" + "\">")
.append(i18n("Krita Website")).append("</a>"));
sourceCodeLink->setText(QString("<a style=\"color: " + blendedColor.name() + " \" href=\"https://invent.kde.org/kde/krita?" + analyticsString + "source-code" + "\">")
.append(i18n("Source Code")).append("</a>"));
poweredByKDELink->setText(QString("<a style=\"color: " + blendedColor.name() + " \" href=\"https://userbase.kde.org/What_is_KDE?" + analyticsString + "what-is-kde" + "\">")
.append(i18n("Powered by KDE")).append("</a>"));
slotUpdateVersionMessage(); // text set from RSS feed
// re-populate recent files since they might have themed icons
populateRecentDocuments();
}
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).toLocalFile();
QString fileName = QFileInfo(recentFileUrlPath).fileName();
QList<QUrl> brokenUrls;
if (m_thumbnailMap.contains(recentFileUrlPath)) {
recentItem->setIcon(m_thumbnailMap[recentFileUrlPath]);
}
else {
QFileInfo fi(recentFileUrlPath);
if (fi.exists()) {
QString mimeType = KisMimeDatabase::mimeTypeForFile(recentFileUrlPath);
if (mimeType == KisDocument::nativeFormatMimeType()
|| mimeType == "image/openraster") {
QScopedPointer<KoStore> store(KoStore::createStore(recentFileUrlPath, KoStore::Read));
if (store) {
QString thumbnailpath;
if (store->hasFile(QString("Thumbnails/thumbnail.png"))){
thumbnailpath = QString("Thumbnails/thumbnail.png");
} else if (store->hasFile(QString("preview.png"))) {
thumbnailpath = QString("preview.png");
}
if (!thumbnailpath.isEmpty()) {
if (store->open(thumbnailpath)) {
QByteArray bytes = store->read(store->size());
store->close();
QImage img;
img.loadFromData(bytes);
img.setDevicePixelRatio(devicePixelRatioF());
recentItem->setIcon(QIcon(QPixmap::fromImage(img)));
}
}
}
else {
brokenUrls << m_mainWindow->recentFilesUrls().at(i);
}
}
else if (mimeType == "image/tiff" || mimeType == "image/x-tiff") {
// Workaround for a bug in Qt tiff QImageIO plugin
QScopedPointer<KisDocument> doc;
- doc.reset(KisPart::instance()->createDocument());
+ doc.reset(KisPart::instance()->createTemporaryDocument());
doc->setFileBatchMode(true);
bool r = doc->openUrl(QUrl::fromLocalFile(recentFileUrlPath), KisDocument::DontAddToRecent);
if (r) {
KisPaintDeviceSP projection = doc->image()->projection();
recentItem->setIcon(QIcon(QPixmap::fromImage(projection->createThumbnail(48, 48, projection->exactBounds()))));
}
else {
brokenUrls << m_mainWindow->recentFilesUrls().at(i);
}
}
else {
QImage img;
img.setDevicePixelRatio(devicePixelRatioF());
img.load(recentFileUrlPath);
if (!img.isNull()) {
recentItem->setIcon(QIcon(QPixmap::fromImage(img.scaledToWidth(48))));
}
else {
brokenUrls << m_mainWindow->recentFilesUrls().at(i);
}
}
if (brokenUrls.size() == 0 || brokenUrls.last().toLocalFile() != recentFileUrlPath) {
m_thumbnailMap[recentFileUrlPath] = recentItem->icon();
}
}
}
Q_FOREACH(const QUrl &url, brokenUrls) {
m_mainWindow->removeRecentUrl(url);
}
// set the recent object with the data
if (brokenUrls.isEmpty() || brokenUrls.last().toLocalFile() != recentFileUrlPath) {
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::slotUpdateVersionMessage()
{
alertIcon->setIcon(KisIconUtils::loadIcon("warning"));
alertIcon->setVisible(false);
// find out if we need an update...or if this is a development version:
// dev builds contain GIT hash in it and the word git
// stable versions do not contain this.
if (qApp->applicationVersion().contains("git")) {
// Development build
QString versionLabelText = QString("<a style=\"color: " +
blendedColor.name() +
" \" href=\"https://docs.krita.org/en/untranslatable_pages/triaging_bugs.html?"
+ analyticsString + "dev-build" + "\">")
.append(i18n("DEV BUILD")).append("</a>");
versionNotificationLabel->setText(versionLabelText);
alertIcon->setVisible(true);
versionNotificationLabel->setVisible(true);
} else if (newsWidget->hasUpdateAvailable()) {
// build URL for label
QString versionLabelText = QString("<a style=\"color: " +
blendedColor.name() +
" \" href=\"" +
newsWidget->versionLink() + "?" +
analyticsString + "version-update" + "\">")
.append(i18n("New Version Available!")).append("</a>");
versionNotificationLabel->setVisible(true);
versionNotificationLabel->setText(versionLabelText);
alertIcon->setVisible(true);
} else {
// no message needed... exit
versionNotificationLabel->setVisible(false);
return;
}
if (!blendedStyle.isNull()) {
versionNotificationLabel->setStyleSheet(blendedStyle);
}
}
void KisWelcomePageWidget::dragEnterEvent(QDragEnterEvent *event)
{
//qDebug() << "dragEnterEvent formats" << event->mimeData()->formats() << "urls" << event->mimeData()->urls() << "has images" << event->mimeData()->hasImage();
showDropAreaIndicator(true);
if (event->mimeData()->hasUrls() ||
event->mimeData()->hasFormat("application/x-krita-node") ||
event->mimeData()->hasFormat("application/x-qt-image")) {
event->accept();
}
}
void KisWelcomePageWidget::dropEvent(QDropEvent *event)
{
//qDebug() << "KisWelcomePageWidget::dropEvent() formats" << event->mimeData()->formats() << "urls" << event->mimeData()->urls() << "has images" << event->mimeData()->hasImage();
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 = m_mainWindow->installBundle(url.toLocalFile());
if (!r) {
qWarning() << "Could not install bundle" << url.toLocalFile();
}
}
else {
m_mainWindow->openDocument(url, KisMainWindow::None);
}
}
}
}
void KisWelcomePageWidget::dragMoveEvent(QDragMoveEvent *event)
{
//qDebug() << "dragMoveEvent";
m_mainWindow->dragMoveEvent(event);
if (event->mimeData()->hasUrls() ||
event->mimeData()->hasFormat("application/x-krita-node") ||
event->mimeData()->hasFormat("application/x-qt-image")) {
event->accept();
}
}
void KisWelcomePageWidget::dragLeaveEvent(QDragLeaveEvent */*event*/)
{
//qDebug() << "dragLeaveEvent";
showDropAreaIndicator(false);
m_mainWindow->dragLeave();
}
void KisWelcomePageWidget::recentDocumentClicked(QModelIndex index)
{
QString fileUrl = index.data(Qt::ToolTipRole).toString();
m_mainWindow->openDocument(QUrl::fromLocalFile(fileUrl), KisMainWindow::None );
}
void KisWelcomePageWidget::slotNewFileClicked()
{
m_mainWindow->slotFileNew();
}
void KisWelcomePageWidget::slotOpenFileClicked()
{
m_mainWindow->slotFileOpen();
}
diff --git a/libs/ui/KisWindowLayoutManager.cpp b/libs/ui/KisWindowLayoutManager.cpp
index 26e7ccdc58..0114e74def 100644
--- a/libs/ui/KisWindowLayoutManager.cpp
+++ b/libs/ui/KisWindowLayoutManager.cpp
@@ -1,243 +1,243 @@
/*
* 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 "KisWindowLayoutManager.h"
#include <QWidget>
#include <QDesktopWidget>
#include <QScreen>
#include <kconfiggroup.h>
#include <ksharedconfig.h>
#include <KisApplication.h>
#include <KisMainWindow.h>
#include <KisPart.h>
#include <kis_dom_utils.h>
#include <KisResourceServerProvider.h>
#include <KisSessionResource.h>
Q_GLOBAL_STATIC(KisWindowLayoutManager, s_instance)
struct KisWindowLayoutManager::Private {
bool showImageInAllWindows{false};
bool primaryWorkspaceFollowsFocus{false};
QUuid primaryWindow;
QVector<DisplayLayout*> displayLayouts;
void loadDisplayLayouts() {
KConfigGroup layoutsCfg(KSharedConfig::openConfig(), "DisplayLayouts");
QStringList groups = layoutsCfg.groupList();
Q_FOREACH(QString name, groups) {
loadDisplayLayout(name, layoutsCfg.group(name));
}
}
void loadDisplayLayout(const QString &name, KConfigGroup layoutCfg) {
DisplayLayout *layout = new DisplayLayout();
layout->name = name;
int displayNumber = 1;
while (true) {
const QString displayDefinition = layoutCfg.readEntry(QString("Display%1").arg(displayNumber++), QString());
if (displayDefinition.isEmpty()) break;
// Just the resolution for now. Later we might want to split by a separator and include things like serial number, etc.
const QString &resolutionStr = displayDefinition;
QStringList dimensions = resolutionStr.split('x');
if (dimensions.size() != 2) {
qWarning() << "Invalid display definition: " << displayDefinition;
break;
}
QSize resolution = QSize(
KisDomUtils::toInt(dimensions[0]),
KisDomUtils::toInt(dimensions[1])
);
layout->displays.append(Display{resolution});
}
layout->preferredWindowLayout = layoutCfg.readEntry("PreferredLayout", "");
displayLayouts.append(layout);
}
void saveDisplayLayout(const DisplayLayout &layout) {
KConfigGroup layoutsCfg(KSharedConfig::openConfig(), "DisplayLayouts");
KConfigGroup layoutCfg = layoutsCfg.group(layout.name);
layoutCfg.writeEntry("PreferredLayout", layout.preferredWindowLayout);
}
};
bool KisWindowLayoutManager::Display::matches(QScreen* screen) const
{
return resolution == screen->geometry().size();
}
bool KisWindowLayoutManager::DisplayLayout::matches(QList<QScreen*> screens) const
{
if (screens.size() != displays.size()) return false;
QVector<bool> matchedScreens(screens.size());
Q_FOREACH(auto &expectedDisplay, displays) {
int i;
for (i = 0; i < screens.size(); i++) {
if (matchedScreens[i]) continue;
if (expectedDisplay.matches(screens[i])) {
matchedScreens[i] = true;
break;
}
}
if (i == screens.size()) {
return false;
}
}
return true;
}
KisWindowLayoutManager * KisWindowLayoutManager::instance()
{
return s_instance;
}
KisWindowLayoutManager::KisWindowLayoutManager()
: d(new Private)
{
d->loadDisplayLayouts();
connect(qobject_cast<KisApplication*>(KisApplication::instance()),
SIGNAL(focusChanged(QWidget*,QWidget*)),
this, SLOT(slotFocusChanged(QWidget*,QWidget*)));
connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(slotScreensChanged()));
connect(QApplication::desktop(), SIGNAL(screenCountChanged(int)), this, SLOT(slotScreensChanged()));
}
KisWindowLayoutManager::~KisWindowLayoutManager() {
Q_FOREACH(DisplayLayout *layout, d->displayLayouts) {
delete layout;
}
}
void KisWindowLayoutManager::setShowImageInAllWindowsEnabled(bool showInAll)
{
bool wasEnabled = d->showImageInAllWindows;
d->showImageInAllWindows = showInAll;
if (!wasEnabled && showInAll) {
KisMainWindow *currentMainWindow = KisPart::instance()->currentMainwindow();
if (currentMainWindow) {
KisView *activeView = currentMainWindow->activeView();
if (activeView) {
KisDocument *document = activeView->document();
if (document) {
activeDocumentChanged(document);
}
}
}
}
}
bool KisWindowLayoutManager::isShowImageInAllWindowsEnabled() const
{
return d->showImageInAllWindows;
}
bool KisWindowLayoutManager::primaryWorkspaceFollowsFocus() const
{
return d->primaryWorkspaceFollowsFocus;
}
void KisWindowLayoutManager::setPrimaryWorkspaceFollowsFocus(bool enabled, QUuid primaryWindow)
{
d->primaryWorkspaceFollowsFocus = enabled;
d->primaryWindow = primaryWindow;
}
QUuid KisWindowLayoutManager::primaryWindowId() const
{
return d->primaryWindow;
}
void KisWindowLayoutManager::activeDocumentChanged(KisDocument *document)
{
if (d->showImageInAllWindows) {
Q_FOREACH(QPointer<KisMainWindow> window, KisPart::instance()->mainWindows()) {
if (window->isHidden()) continue;
const auto view = window->activeView();
if (!view || view->document() != document) {
window->showDocument(document);
}
}
}
}
void KisWindowLayoutManager::slotFocusChanged(QWidget *old, QWidget *now)
{
Q_UNUSED(old);
if (!now) return;
KisMainWindow *newMainWindow = qobject_cast<KisMainWindow*>(now->window());
if (!newMainWindow) return;
newMainWindow->windowFocused();
}
-void KisWindowLayoutManager::setLastUsedLayout(const KisWindowLayoutResource *layout)
+void KisWindowLayoutManager::setLastUsedLayout(KisWindowLayoutResource *layout)
{
// For automatic switching, only allow a window layout proper
- auto *session = dynamic_cast<const KisSessionResource*>(layout);
+ KisSessionResource *session = dynamic_cast<KisSessionResource*>(layout);
if (session) return;
QList<QScreen*> screens = QGuiApplication::screens();
Q_FOREACH(DisplayLayout *displayLayout, d->displayLayouts) {
if (displayLayout->matches(screens)) {
displayLayout->preferredWindowLayout = layout->name();
d->saveDisplayLayout(*displayLayout);
break;
}
}
}
void KisWindowLayoutManager::slotScreensChanged()
{
QList<QScreen*> screens = QGuiApplication::screens();
Q_FOREACH(const DisplayLayout *displayLayout, d->displayLayouts) {
if (displayLayout->matches(screens)) {
KoResourceServer<KisWindowLayoutResource> *windowLayoutServer = KisResourceServerProvider::instance()->windowLayoutServer();
- KisWindowLayoutResource *layout = windowLayoutServer->resourceByName(displayLayout->preferredWindowLayout);
+ KisWindowLayoutResourceSP layout = windowLayoutServer->resourceByName(displayLayout->preferredWindowLayout);
if (layout) {
- setLastUsedLayout(layout);
+ setLastUsedLayout(layout.data());
layout->applyLayout();
return;
}
}
}
}
diff --git a/libs/ui/KisWindowLayoutManager.h b/libs/ui/KisWindowLayoutManager.h
index eb7ae94029..a38586f873 100644
--- a/libs/ui/KisWindowLayoutManager.h
+++ b/libs/ui/KisWindowLayoutManager.h
@@ -1,85 +1,86 @@
/*
* 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.
*/
#ifndef KISWINDOWLAYOUTMANAGER_H
#define KISWINDOWLAYOUTMANAGER_H
#include <QObject>
#include <QUuid>
#include <QVector>
#include <QSize>
+#include <KisWindowLayoutResource.h>
+
class QScreen;
-class KisWindowLayoutResource;
class KisDocument;
class KisWindowLayoutManager : public QObject
{
Q_OBJECT
public:
struct Display
{
QSize resolution;
bool matches(QScreen* screen) const;
};
struct DisplayLayout
{
QString name;
QVector<Display> displays;
QString preferredWindowLayout;
bool matches(QList<QScreen*> screens) const;
};
explicit KisWindowLayoutManager();
~KisWindowLayoutManager();
static KisWindowLayoutManager *instance();
/**
* When enabled, a workspace dedicated as primary is used for any main window which receives focus.
* Meanwhile, the workspace of that window is used for the window which originally had the primary workspace.
*/
bool primaryWorkspaceFollowsFocus() const;
void setPrimaryWorkspaceFollowsFocus(bool enabled, QUuid primaryWindow);
QUuid primaryWindowId() const;
/**
* When enabled, main windows will synchronize to keep the same document active
*/
bool isShowImageInAllWindowsEnabled() const;
void setShowImageInAllWindowsEnabled(bool showInAll);
void activeDocumentChanged(KisDocument *document);
- void setLastUsedLayout(const KisWindowLayoutResource *layout);
+ void setLastUsedLayout(KisWindowLayoutResource *layout);
private Q_SLOTS:
void slotFocusChanged(QWidget*, QWidget*);
void slotScreensChanged();
private:
struct Private;
QScopedPointer<Private> d;
};
#endif
diff --git a/libs/ui/KisWindowLayoutResource.cpp b/libs/ui/KisWindowLayoutResource.cpp
index bcb6d4b4fc..4df140c5f5 100644
--- a/libs/ui/KisWindowLayoutResource.cpp
+++ b/libs/ui/KisWindowLayoutResource.cpp
@@ -1,450 +1,436 @@
/*
* 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 "KisWindowLayoutResource.h"
#include "KisWindowLayoutManager.h"
#include <QVector>
#include <QList>
#include <QFile>
#include <QDomDocument>
#include <QApplication>
#include <QEventLoop>
#include <QWindow>
#include <QScreen>
#include <KisPart.h>
#include <KisDocument.h>
#include <kis_dom_utils.h>
static const int WINDOW_LAYOUT_VERSION = 1;
struct KisWindowLayoutResource::Private
{
struct WindowGeometry{
int screen = -1;
Qt::WindowStates stateFlags = Qt::WindowNoState;
QByteArray data;
static WindowGeometry fromWindow(const QWidget *window, QList<QScreen*> screens)
{
WindowGeometry geometry;
QWindow *windowHandle = window->windowHandle();
geometry.data = window->saveGeometry();
geometry.stateFlags = windowHandle->windowState();
int index = screens.indexOf(windowHandle->screen());
if (index >= 0) {
geometry.screen = index;
}
return geometry;
}
void forceOntoCorrectScreen(QWidget *window, QList<QScreen*> screens)
{
QWindow *windowHandle = window->windowHandle();
if (screens.indexOf(windowHandle->screen()) != screen) {
QScreen *qScreen = screens[screen];
windowHandle->setScreen(qScreen);
windowHandle->setPosition(qScreen->availableGeometry().topLeft());
}
if (stateFlags) {
window->setWindowState(stateFlags);
}
}
void save(QDomDocument &doc, QDomElement &elem) const
{
if (screen >= 0) {
elem.setAttribute("screen", screen);
}
if (stateFlags & Qt::WindowMaximized) {
elem.setAttribute("maximized", "1");
}
QDomElement geometry = doc.createElement("geometry");
geometry.appendChild(doc.createCDATASection(data.toBase64()));
elem.appendChild(geometry);
}
static WindowGeometry load(const QDomElement &element)
{
WindowGeometry geometry;
geometry.screen = element.attribute("screen", "-1").toInt();
if (element.attribute("maximized", "0") != "0") {
geometry.stateFlags |= Qt::WindowMaximized;
}
QDomElement dataElement = element.firstChildElement("geometry");
geometry.data = QByteArray::fromBase64(dataElement.text().toLatin1());
return geometry;
}
};
struct Window {
QUuid windowId;
QByteArray windowState;
WindowGeometry geometry;
bool canvasDetached = false;
WindowGeometry canvasWindowGeometry;
};
QVector<Window> windows;
bool showImageInAllWindows {false};
bool primaryWorkspaceFollowsFocus {false};
QUuid primaryWindow;
Private() = default;
+ Private(const Private &rhs) = default;
+
explicit Private(QVector<Window> windows)
: windows(std::move(windows))
{}
void openNecessaryWindows(QList<QPointer<KisMainWindow>> &currentWindows) {
auto *kisPart = KisPart::instance();
Q_FOREACH(const Window &window, windows) {
QPointer<KisMainWindow> mainWindow = kisPart->windowById(window.windowId);
if (mainWindow.isNull()) {
mainWindow = kisPart->createMainWindow(window.windowId);
currentWindows.append(mainWindow);
mainWindow->show();
}
}
}
void closeUnneededWindows(QList<QPointer<KisMainWindow>> &currentWindows) {
QVector<QPointer<KisMainWindow>> windowsToClose;
Q_FOREACH(KisMainWindow *mainWindow, currentWindows) {
bool keep = false;
Q_FOREACH(const Window &window, windows) {
if (window.windowId == mainWindow->id()) {
keep = true;
break;
}
}
if (!keep) {
windowsToClose.append(mainWindow);
// Set the window hidden to prevent "show image in all windows" feature from opening new views on it
// while we migrate views onto the remaining windows
mainWindow->hide();
}
}
migrateViewsFromClosingWindows(windowsToClose);
Q_FOREACH(QPointer<KisMainWindow> mainWindow, windowsToClose) {
mainWindow->close();
}
}
void migrateViewsFromClosingWindows(QVector<QPointer<KisMainWindow>> &closingWindows) const
{
auto *kisPart = KisPart::instance();
KisMainWindow *migrationTarget = nullptr;
Q_FOREACH(KisMainWindow *mainWindow, kisPart->mainWindows()) {
if (!closingWindows.contains(mainWindow)) {
migrationTarget = mainWindow;
break;
}
}
if (!migrationTarget) {
qWarning() << "Problem: window layout with no windows would leave user with zero main windows.";
migrationTarget = closingWindows.takeLast();
migrationTarget->show();
}
QVector<KisDocument*> visibleDocuments;
Q_FOREACH(KisView *view, kisPart->views()) {
KisMainWindow *window = view->mainWindow();
if (!closingWindows.contains(window)) {
visibleDocuments.append(view->document());
}
}
Q_FOREACH(KisDocument *document, kisPart->documents()) {
if (!visibleDocuments.contains(document)) {
visibleDocuments.append(document);
migrationTarget->newView(document);
}
}
}
QList<QScreen*> getScreensInConsistentOrder() {
QList<QScreen*> screens = QGuiApplication::screens();
std::sort(screens.begin(), screens.end(), [](const QScreen *a, const QScreen *b) {
QRect aRect = a->geometry();
QRect bRect = b->geometry();
if (aRect.y() == bRect.y()) return aRect.x() < bRect.x();
return (aRect.y() < aRect.y());
});
return screens;
}
};
KisWindowLayoutResource::KisWindowLayoutResource(const QString &filename)
: KoResource(filename)
, d(new Private)
{}
KisWindowLayoutResource::~KisWindowLayoutResource()
{}
-KisWindowLayoutResource * KisWindowLayoutResource::fromCurrentWindows(
+KisWindowLayoutResource::KisWindowLayoutResource(const KisWindowLayoutResource &rhs)
+ : KoResource(rhs)
+ , d(new Private(*rhs.d))
+{
+}
+
+KoResourceSP KisWindowLayoutResource::clone() const
+{
+ return KoResourceSP(new KisWindowLayoutResource(*this));
+}
+
+KisWindowLayoutResourceSP KisWindowLayoutResource::fromCurrentWindows(
const QString &filename, const QList<QPointer<KisMainWindow>> &mainWindows, bool showImageInAllWindows,
bool primaryWorkspaceFollowsFocus, KisMainWindow *primaryWindow
)
{
- auto resource = new KisWindowLayoutResource(filename);
+ KisWindowLayoutResourceSP resource(new KisWindowLayoutResource(filename));
resource->setWindows(mainWindows);
resource->d->showImageInAllWindows = showImageInAllWindows;
resource->d->primaryWorkspaceFollowsFocus = primaryWorkspaceFollowsFocus;
resource->d->primaryWindow = primaryWindow->id();
return resource;
}
void KisWindowLayoutResource::applyLayout()
{
auto *kisPart = KisPart::instance();
auto *layoutManager= KisWindowLayoutManager::instance();
layoutManager->setLastUsedLayout(this);
QList<QPointer<KisMainWindow>> currentWindows = kisPart->mainWindows();
if (d->windows.isEmpty()) {
// No windows defined (e.g. fresh new session). Leave things as they are, but make sure there's at least one visible main window
if (kisPart->mainwindowCount() == 0) {
kisPart->createMainWindow();
} else {
kisPart->mainWindows().first()->show();
}
} else {
d->openNecessaryWindows(currentWindows);
d->closeUnneededWindows(currentWindows);
}
// Wait for the windows to finish opening / closing before applying saved geometry.
// If we don't, the geometry may get reset after we apply it.
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
Q_FOREACH(const auto &window, d->windows) {
QPointer<KisMainWindow> mainWindow = kisPart->windowById(window.windowId);
KIS_SAFE_ASSERT_RECOVER_BREAK(mainWindow);
mainWindow->restoreGeometry(window.geometry.data);
mainWindow->restoreWorkspaceState(window.windowState);
mainWindow->setCanvasDetached(window.canvasDetached);
if (window.canvasDetached) {
QWidget *canvasWindow = mainWindow->canvasWindow();
canvasWindow->restoreGeometry(window.canvasWindowGeometry.data);
}
}
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
QList<QScreen*> screens = d->getScreensInConsistentOrder();
Q_FOREACH(const auto &window, d->windows) {
Private::WindowGeometry geometry = window.geometry;
QPointer<KisMainWindow> mainWindow = kisPart->windowById(window.windowId);
KIS_SAFE_ASSERT_RECOVER_BREAK(mainWindow);
if (geometry.screen >= 0 && geometry.screen < screens.size()) {
geometry.forceOntoCorrectScreen(mainWindow, screens);
}
if (window.canvasDetached) {
Private::WindowGeometry canvasWindowGeometry = window.canvasWindowGeometry;
if (canvasWindowGeometry.screen >= 0 && canvasWindowGeometry.screen < screens.size()) {
canvasWindowGeometry.forceOntoCorrectScreen(mainWindow->canvasWindow(), screens);
}
}
}
layoutManager->setShowImageInAllWindowsEnabled(d->showImageInAllWindows);
layoutManager->setPrimaryWorkspaceFollowsFocus(d->primaryWorkspaceFollowsFocus, d->primaryWindow);
}
-bool KisWindowLayoutResource::save()
-{
- if (filename().isEmpty())
- return false;
-
- QFile file(filename());
- file.open(QIODevice::WriteOnly);
- bool res = saveToDevice(&file);
- file.close();
- return res;
-}
-
-bool KisWindowLayoutResource::load()
-{
- if (filename().isEmpty())
- return false;
-
- QFile file(filename());
- if (file.size() == 0) return false;
- if (!file.open(QIODevice::ReadOnly)) {
- warnKrita << "Can't open file " << filename();
- return false;
- }
-
- bool res = loadFromDevice(&file);
- file.close();
- return res;
-}
-
bool KisWindowLayoutResource::saveToDevice(QIODevice *dev) const
{
QDomDocument doc;
QDomElement root = doc.createElement("WindowLayout");
root.setAttribute("name", name());
root.setAttribute("version", WINDOW_LAYOUT_VERSION);
saveXml(doc, root);
doc.appendChild(root);
QTextStream textStream(dev);
textStream.setCodec("UTF-8");
doc.save(textStream, 4);
KoResource::saveToDevice(dev);
return true;
}
-bool KisWindowLayoutResource::loadFromDevice(QIODevice *dev)
+bool KisWindowLayoutResource::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
{
+ Q_UNUSED(resourcesInterface);
+
QDomDocument doc;
if (!doc.setContent(dev)) {
return false;
}
QDomElement element = doc.documentElement();
setName(element.attribute("name"));
d->windows.clear();
loadXml(element);
setValid(true);
return true;
}
void KisWindowLayoutResource::saveXml(QDomDocument &doc, QDomElement &root) const
{
root.setAttribute("showImageInAllWindows", (int)d->showImageInAllWindows);
root.setAttribute("primaryWorkspaceFollowsFocus", (int)d->primaryWorkspaceFollowsFocus);
root.setAttribute("primaryWindow", d->primaryWindow.toString());
Q_FOREACH(const auto &window, d->windows) {
QDomElement elem = doc.createElement("window");
elem.setAttribute("id", window.windowId.toString());
window.geometry.save(doc, elem);
if (window.canvasDetached) {
QDomElement canvasWindowElement = doc.createElement("canvasWindow");
window.canvasWindowGeometry.save(doc, canvasWindowElement);
elem.appendChild(canvasWindowElement);
}
QDomElement state = doc.createElement("windowState");
state.appendChild(doc.createCDATASection(window.windowState.toBase64()));
elem.appendChild(state);
root.appendChild(elem);
}
}
void KisWindowLayoutResource::loadXml(const QDomElement &element) const
{
d->showImageInAllWindows = KisDomUtils::toInt(element.attribute("showImageInAllWindows", "0"));
d->primaryWorkspaceFollowsFocus = KisDomUtils::toInt(element.attribute("primaryWorkspaceFollowsFocus", "0"));
d->primaryWindow = element.attribute("primaryWindow");
for (auto windowElement = element.firstChildElement("window");
!windowElement.isNull();
windowElement = windowElement.nextSiblingElement("window")) {
Private::Window window;
window.windowId = QUuid(windowElement.attribute("id", QUuid().toString()));
if (window.windowId.isNull()) {
window.windowId = QUuid::createUuid();
}
window.geometry = Private::WindowGeometry::load(windowElement);
QDomElement canvasWindowElement = windowElement.firstChildElement("canvasWindow");
if (!canvasWindowElement.isNull()) {
window.canvasDetached = true;
window.canvasWindowGeometry = Private::WindowGeometry::load(canvasWindowElement);
}
QDomElement state = windowElement.firstChildElement("windowState");
window.windowState = QByteArray::fromBase64(state.text().toLatin1());
d->windows.append(window);
}
}
QString KisWindowLayoutResource::defaultFileExtension() const
{
return QString(".kwl");
}
void KisWindowLayoutResource::setWindows(const QList<QPointer<KisMainWindow>> &mainWindows)
{
d->windows.clear();
QList<QScreen*> screens = d->getScreensInConsistentOrder();
Q_FOREACH(auto window, mainWindows) {
if (!window->isVisible()) continue;
Private::Window state;
state.windowId = window->id();
state.windowState = window->saveState();
state.geometry = Private::WindowGeometry::fromWindow(window, screens);
state.canvasDetached = window->canvasDetached();
if (state.canvasDetached) {
state.canvasWindowGeometry = Private::WindowGeometry::fromWindow(window->canvasWindow(), screens);
}
d->windows.append(state);
}
}
diff --git a/libs/ui/KisWindowLayoutResource.h b/libs/ui/KisWindowLayoutResource.h
index 7ca38ccc8e..45c74ca57b 100644
--- a/libs/ui/KisWindowLayoutResource.h
+++ b/libs/ui/KisWindowLayoutResource.h
@@ -1,61 +1,71 @@
/*
* 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.
*/
#ifndef KISWINDOWLAYOUTRESOURCE_H
#define KISWINDOWLAYOUTRESOURCE_H
#include <KoResource.h>
#include <KisMainWindow.h>
+class KisWindowLayoutResource;
+typedef QSharedPointer<KisWindowLayoutResource> KisWindowLayoutResourceSP;
+
class KisWindowLayoutResource : public KoResource
{
public:
explicit KisWindowLayoutResource(const QString &filename);
~KisWindowLayoutResource() override;
+ KisWindowLayoutResource(const KisWindowLayoutResource &rhs);
+ KisWindowLayoutResource &operator=(const KisWindowLayoutResource &rhs) = delete;
+ KoResourceSP clone() const override;
- static KisWindowLayoutResource * fromCurrentWindows(
- const QString &filename, const QList<QPointer<KisMainWindow>> &mainWindows,
- bool showImageInAllWindows,
- bool primaryWorkspaceFollowsFocus,
- KisMainWindow *primaryWindow
- );
+ static KisWindowLayoutResourceSP fromCurrentWindows (
+ const QString &filename, const QList<QPointer<KisMainWindow>> &mainWindows,
+ bool showImageInAllWindows,
+ bool primaryWorkspaceFollowsFocus,
+ KisMainWindow *primaryWindow
+ );
void applyLayout();
- bool save() override;
- bool load() override;
-
bool saveToDevice(QIODevice *dev) const override;
- bool loadFromDevice(QIODevice *dev) override;
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
+
+
+ QPair<QString, QString> resourceType() const override
+ {
+ return QPair<QString, QString>(ResourceType::WindowLayouts, "");
+ }
QString defaultFileExtension() const override;
protected:
void setWindows(const QList<QPointer<KisMainWindow>> &mainWindows);
virtual void saveXml(QDomDocument &doc, QDomElement &root) const;
virtual void loadXml(const QDomElement &root) const;
private:
struct Private;
QScopedPointer<Private> d;
};
+
#endif
diff --git a/libs/ui/dialogs/KisDlgPaletteEditor.cpp b/libs/ui/dialogs/KisDlgPaletteEditor.cpp
index 7d7cf09f6f..302e6b244a 100644
--- a/libs/ui/dialogs/KisDlgPaletteEditor.cpp
+++ b/libs/ui/dialogs/KisDlgPaletteEditor.cpp
@@ -1,235 +1,208 @@
/*
* 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 <QAction>
#include <QFileInfo>
#include <QSpinBox>
#include <QLabel>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QMessageBox>
#include <QComboBox>
#include <QInputDialog>
#include <QFormLayout>
#include <QPicture>
#include <QPushButton>
#include <QSignalBlocker>
#include <KoResourceServerProvider.h>
#include <KoResourceServer.h>
#include <KoDialog.h>
#include <KoColorSet.h>
#include <kis_global.h>
#include <KisPaletteModel.h>
#include "KisPaletteEditor.h"
#include "ui_WdgDlgPaletteEditor.h"
#include "KisDlgPaletteEditor.h"
KisDlgPaletteEditor::KisDlgPaletteEditor()
: m_ui(new Ui_WdgDlgPaletteEditor)
, m_actAddGroup(new QAction(i18n("Add a group")))
, m_actDelGroup(new QAction(i18n("Delete this group")))
, m_actRenGroup(new QAction(i18n("Rename this group")))
, m_globalButtons(new QButtonGroup(this))
, m_paletteEditor(new KisPaletteEditor(this))
, m_currentGroupOriginalName(KoColorSet::GLOBAL_GROUP_NAME)
{
setWindowTitle(i18n("Palette Editor"));
m_ui->setupUi(this);
m_ui->gbxPalette->setTitle(i18n("Palette settings"));
- m_ui->labelFilename->setText(i18n("Filename"));
m_ui->labelName->setText(i18n("Palette Name"));
m_ui->bnAddGroup->setDefaultAction(m_actAddGroup.data());
m_ui->gbxGroup->setTitle(i18n("Group settings"));
m_ui->labelColCount->setText(i18n("Column count"));
m_ui->labelRowCount->setText(i18n("Row count"));
m_ui->bnDelGroup->setDefaultAction(m_actDelGroup.data());
m_ui->bnRenGroup->setDefaultAction(m_actRenGroup.data());
connect(m_actAddGroup.data(), SIGNAL(triggered(bool)), SLOT(slotAddGroup()));
connect(m_actDelGroup.data(), SIGNAL(triggered(bool)), SLOT(slotDelGroup()));
connect(m_actRenGroup.data(), SIGNAL(triggered(bool)), SLOT(slotRenGroup()));
connect(m_ui->spinBoxRow, SIGNAL(valueChanged(int)), SLOT(slotRowCountChanged(int)));
connect(m_ui->spinBoxCol, SIGNAL(valueChanged(int)), SLOT(slotColCountChanged(int)));
connect(m_ui->lineEditName, SIGNAL(editingFinished()), SLOT(slotNameChanged()));
- connect(m_ui->lineEditFilename, SIGNAL(textEdited(QString)), SLOT(slotFilenameChanged(QString)));
- connect(m_ui->lineEditFilename, SIGNAL(editingFinished()), SLOT(slotFilenameInputFinished()));
m_globalButtons->addButton(m_ui->rb_global, 0);
m_globalButtons->addButton(m_ui->rb_document, 1);
connect(m_globalButtons.data(), SIGNAL(buttonClicked(int)), SLOT(slotSetGlobal()));
connect(this, SIGNAL(accepted()), SLOT(slotAccepted()));
m_warnPalette.setColor(QPalette::Text, Qt::red);
}
KisDlgPaletteEditor::~KisDlgPaletteEditor()
{ }
void KisDlgPaletteEditor::setPaletteModel(KisPaletteModel *model)
{
m_colorSet = model->colorSet();
if (m_colorSet.isNull()) {
return;
}
m_paletteEditor->setPaletteModel(model);
// don't let editor make changes when initializing
- const QSignalBlocker blocker1(m_ui->lineEditFilename);
const QSignalBlocker blocker2(m_ui->lineEditName);
const QSignalBlocker blocker3(m_ui->spinBoxCol);
const QSignalBlocker blocker4(m_ui->spinBoxRow);
const QSignalBlocker blocker5(m_ui->rb_global);
const QSignalBlocker blocker6(m_ui->rb_document);
const QSignalBlocker blocker7(m_ui->cbxGroup);
m_ui->lineEditName->setText(m_colorSet->name());
- m_ui->lineEditFilename->setText(m_paletteEditor->relativePathFromSaveLocation());
m_ui->spinBoxCol->setValue(m_colorSet->columnCount());
- if (m_colorSet->isGlobal()) {
+ //Hack alert!!!
+ //TODO: replace with a storage selector.
+ if (m_colorSet->storageLocation().contains("/")) {
m_globalButtons->button(0)->setChecked(true);
} else {
m_globalButtons->button(1)->setChecked(true);
}
Q_FOREACH (const QString & groupName, m_colorSet->getGroupNames()) {
m_ui->cbxGroup->addItem(groupName);
}
connect(m_ui->cbxGroup, SIGNAL(currentTextChanged(QString)), SLOT(slotGroupChosen(QString)));
m_ui->cbxGroup->setCurrentText(KoColorSet::GLOBAL_GROUP_NAME);
m_ui->bnDelGroup->setEnabled(false);
m_ui->bnRenGroup->setEnabled(false);
m_ui->spinBoxRow->setValue(m_paletteEditor->rowNumberOfGroup(KoColorSet::GLOBAL_GROUP_NAME));
bool canWrite = m_colorSet->isEditable();
m_ui->lineEditName->setEnabled(canWrite);
- m_ui->lineEditFilename->setEnabled(canWrite);
m_ui->spinBoxCol->setEnabled(canWrite);
m_ui->spinBoxRow->setEnabled(canWrite);
m_ui->rb_global->setEnabled(canWrite);
m_ui->rb_document->setEnabled(canWrite);
m_ui->bnAddGroup->setEnabled(canWrite);
}
void KisDlgPaletteEditor::setView(KisViewManager *view)
{
m_paletteEditor->setView(view);
}
void KisDlgPaletteEditor::slotAddGroup()
{
QString newGroupName = m_paletteEditor->addGroup();
if (!newGroupName.isEmpty()) {
m_ui->cbxGroup->addItem(newGroupName);
m_ui->cbxGroup->setCurrentIndex(m_ui->cbxGroup->count() - 1);
};
}
void KisDlgPaletteEditor::slotRenGroup()
{
QString newName = m_paletteEditor->renameGroup(m_currentGroupOriginalName);
if (!newName.isEmpty()) {
int idx = m_ui->cbxGroup->currentIndex();
m_ui->cbxGroup->removeItem(idx);
m_ui->cbxGroup->insertItem(idx, newName);
m_ui->cbxGroup->setCurrentIndex(idx);
}
}
void KisDlgPaletteEditor::slotDelGroup()
{
int deletedIdx = m_ui->cbxGroup->currentIndex();
if (m_paletteEditor->removeGroup(m_currentGroupOriginalName)) {
m_ui->cbxGroup->setCurrentIndex(0);
m_ui->cbxGroup->removeItem(deletedIdx);
}
}
void KisDlgPaletteEditor::slotGroupChosen(const QString &groupName)
{
if (groupName == KoColorSet::GLOBAL_GROUP_NAME) {
m_ui->bnDelGroup->setEnabled(false);
m_ui->bnRenGroup->setEnabled(false);
} else {
m_ui->bnDelGroup->setEnabled(true);
m_ui->bnRenGroup->setEnabled(true);
}
m_currentGroupOriginalName = m_paletteEditor->oldNameFromNewName(groupName);
m_ui->spinBoxRow->setValue(m_paletteEditor->rowNumberOfGroup(m_currentGroupOriginalName));
}
void KisDlgPaletteEditor::slotRowCountChanged(int newCount)
{
m_paletteEditor->changeGroupRowCount(m_currentGroupOriginalName, newCount);
}
void KisDlgPaletteEditor::slotSetGlobal()
{
bool toGlobal = false;
if (m_ui->rb_global->isChecked()) {
toGlobal = true;
}
m_paletteEditor->setGlobal(toGlobal);
}
void KisDlgPaletteEditor::slotNameChanged()
{
m_paletteEditor->rename(qobject_cast<QLineEdit*>(sender())->text());
}
-void KisDlgPaletteEditor::slotFilenameChanged(const QString &newFilename)
-{
- bool global = m_colorSet->isGlobal();
- if (m_paletteEditor->duplicateExistsFilename(newFilename, global)) {
- m_ui->lineEditFilename->setPalette(m_warnPalette);
- m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
- return;
- }
- m_ui->lineEditFilename->setPalette(m_normalPalette);
- m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
- m_paletteEditor->changeFilename(newFilename);
-}
-
-void KisDlgPaletteEditor::slotFilenameInputFinished()
-{
- QString newName = m_ui->lineEditFilename->text();
- bool global = m_colorSet->isGlobal();
- if (m_paletteEditor->duplicateExistsFilename(newName, global)) {
- return;
- }
- m_paletteEditor->changeFilename(newName);
-}
-
void KisDlgPaletteEditor::slotColCountChanged(int newCount)
{
m_paletteEditor->changeColCount(newCount);
}
void KisDlgPaletteEditor::slotAccepted()
{
m_paletteEditor->updatePalette();
}
diff --git a/libs/ui/dialogs/KisDlgPaletteEditor.h b/libs/ui/dialogs/KisDlgPaletteEditor.h
index dedd193f08..a14b7aa70a 100644
--- a/libs/ui/dialogs/KisDlgPaletteEditor.h
+++ b/libs/ui/dialogs/KisDlgPaletteEditor.h
@@ -1,95 +1,95 @@
/*
* 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 KISDLGPALETTEEDITOR_H
#define KISDLGPALETTEEDITOR_H
#include <QDialog>
#include <QPointer>
#include <QPair>
#include <QScopedPointer>
#include <QHash>
#include <QSet>
#include <QButtonGroup>
+#include <KoColorSet.h>
+
#include "kritaui_export.h"
class QAction;
class KoColorSet;
class KisPaletteModel;
class KisSwatchGroup;
class KoDialog;
class KisViewManager;
class KisPaletteEditor;
class Ui_WdgDlgPaletteEditor;
/**
* @brief The KisDlgPaletteEditor class
* a dialog used by the palette docker to make modifications to a palette.
* it automatically uploads all changes into the resource server when
* the change is accepted
*/
class KRITAUI_EXPORT KisDlgPaletteEditor : public QDialog
{
Q_OBJECT
public:
explicit KisDlgPaletteEditor();
~KisDlgPaletteEditor();
public:
void setPaletteModel(KisPaletteModel *);
- KoColorSet* palette() const { return m_colorSet; }
+ KoColorSetSP palette() const { return m_colorSet; }
void setView(KisViewManager *);
private Q_SLOTS:
void slotDelGroup();
void slotAddGroup();
void slotRenGroup();
void slotGroupChosen(const QString &groupName);
void slotRowCountChanged(int);
void slotSetGlobal();
void slotNameChanged();
- void slotFilenameChanged(const QString &newFilename);
- void slotFilenameInputFinished();
void slotColCountChanged(int);
void slotAccepted();
private:
QString oldNameFromNewName(const QString &newName) const;
private:
QScopedPointer<Ui_WdgDlgPaletteEditor> m_ui;
QScopedPointer<QAction> m_actAddGroup;
QScopedPointer<QAction> m_actDelGroup;
QScopedPointer<QAction> m_actRenGroup;
QScopedPointer<QButtonGroup> m_globalButtons;
QScopedPointer<KisPaletteEditor> m_paletteEditor;
- QPointer<KoColorSet> m_colorSet;
+ QSharedPointer<KoColorSet> m_colorSet;
QString m_currentGroupOriginalName;
QPalette m_normalPalette;
QPalette m_warnPalette;
};
#endif // KISKisDlgPaletteEditor_H
diff --git a/libs/ui/dialogs/KisSessionManagerDialog.cpp b/libs/ui/dialogs/KisSessionManagerDialog.cpp
index 0412c01417..75ddb0064a 100644
--- a/libs/ui/dialogs/KisSessionManagerDialog.cpp
+++ b/libs/ui/dialogs/KisSessionManagerDialog.cpp
@@ -1,151 +1,164 @@
/*
* 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 <KisSessionResource.h>
#include <KisResourceServerProvider.h>
#include <QInputDialog>
#include <QMessageBox>
#include <KisPart.h>
#include "KisSessionManagerDialog.h"
KisSessionManagerDialog::KisSessionManagerDialog(QWidget *parent)
: QDialog(parent)
{
setupUi(this);
- updateSessionList();
-
connect(btnNew, SIGNAL(clicked()), this, SLOT(slotNewSession()));
connect(btnRename, SIGNAL(clicked()), this, SLOT(slotRenameSession()));
connect(btnSwitchTo, SIGNAL(clicked()), this, SLOT(slotSwitchSession()));
connect(btnDelete, SIGNAL(clicked()), this, SLOT(slotDeleteSession()));
connect(btnClose, SIGNAL(clicked()), this, SLOT(slotClose()));
- connect(lstSessions, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(slotSessionDoubleClicked(QListWidgetItem*)));
-}
-void KisSessionManagerDialog::updateSessionList() {
- KoResourceServer<KisSessionResource> *server = KisResourceServerProvider::instance()->sessionServer();
+ m_model = KisResourceModelProvider::resourceModel(ResourceType::Sessions);
+ lstSessions->setModel(m_model);
+ lstSessions->setModelColumn(KisResourceModel::Name);
+
+
+ connect(m_model, SIGNAL(beforeResourcesLayoutReset(QModelIndex)), this, SLOT(slotModelAboutToBeReset(QModelIndex)));
+ connect(m_model, SIGNAL(afterResourcesLayoutReset()), this, SLOT(slotModelReset()));
+
+ connect(lstSessions, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotSessionDoubleClicked(QModelIndex)));
+
- lstSessions->clear();
- Q_FOREACH(KisSessionResource *session, server->resources()) {
- lstSessions->addItem(session->name());
- }
}
void KisSessionManagerDialog::slotNewSession()
{
- QString name = QInputDialog::getText(this,
- i18n("Create session"),
- i18n("Session name:"), QLineEdit::Normal
- );
- if (name.isNull() || name.isEmpty()) return;
+ QString name;
- auto *session = new KisSessionResource(QString());
+ KisSessionResourceSP session(new KisSessionResource(QString()));
KoResourceServer<KisSessionResource> *server = KisResourceServerProvider::instance()->sessionServer();
QString saveLocation = server->saveLocation();
- QFileInfo fileInfo(saveLocation + name + session->defaultFileExtension());
- int i = 1;
- while (fileInfo.exists()) {
- fileInfo.setFile(saveLocation + name + QString("%1").arg(i) + session->defaultFileExtension());
- i++;
+ QFileInfo fileInfo(saveLocation + name.split(" ").join("_") + session->defaultFileExtension());
+
+ bool fileOverwriteAccepted = false;
+
+ while(!fileOverwriteAccepted) {
+ name = QInputDialog::getText(this,
+ i18n("Create session"),
+ i18n("Session name:"), QLineEdit::Normal,
+ name);
+ if (name.isNull() || name.isEmpty()) {
+ return;
+ } else {
+ fileInfo = QFileInfo(saveLocation + name.split(" ").join("_") + session->defaultFileExtension());
+ if (fileInfo.exists()) {
+ int res = QMessageBox::warning(this, i18nc("@title:window", "Name Already Exists")
+ , i18n("The name '%1' already exists, do you wish to overwrite it?", name)
+ , QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ if (res == QMessageBox::Yes) fileOverwriteAccepted = true;
+ } else {
+ fileOverwriteAccepted = true;
+ }
+ }
}
- session->setFilename(fileInfo.filePath());
+ session->setFilename(fileInfo.fileName());
session->setName(name);
session->storeCurrentWindows();
server->addResource(session);
KisPart::instance()->setCurrentSession(session);
-
- updateSessionList();
}
void KisSessionManagerDialog::slotRenameSession()
{
QString name = QInputDialog::getText(this,
i18n("Rename session"),
i18n("New name:"), QLineEdit::Normal
);
if (name.isNull() || name.isEmpty()) return;
- KisSessionResource *session = getSelectedSession();
+ KisSessionResourceSP session = getSelectedSession();
if (!session) return;
- session->setName(name);
- session->save();
-
- updateSessionList();
+ m_model->renameResource(session, name);
}
-void KisSessionManagerDialog::slotSessionDoubleClicked(QListWidgetItem* /*item*/)
+void KisSessionManagerDialog::slotSessionDoubleClicked(QModelIndex /*item*/)
{
slotSwitchSession();
slotClose();
}
void KisSessionManagerDialog::slotSwitchSession()
{
- KisSessionResource *session = getSelectedSession();
+ KisSessionResourceSP session = getSelectedSession();
if (session) {
bool closed = KisPart::instance()->closeSession(true);
if (closed) {
- session->restore();
+ KisPart::instance()->restoreSession(session);
}
}
}
-KisSessionResource *KisSessionManagerDialog::getSelectedSession() const
+KisSessionResourceSP KisSessionManagerDialog::getSelectedSession() const
{
- QListWidgetItem *item = lstSessions->currentItem();
- if (item) {
+ QModelIndex idx = lstSessions->currentIndex();
+ if (idx.isValid()) {
KoResourceServer<KisSessionResource> *server = KisResourceServerProvider::instance()->sessionServer();
- return server->resourceByName(item->text());
+ QString name = m_model->data(idx, Qt::UserRole + KisResourceModel::Name).toString();
+ return server->resourceByName(name);
}
return nullptr;
}
void KisSessionManagerDialog::slotDeleteSession()
{
- KisSessionResource *session = getSelectedSession();
- if (!session) return;
-
- if (QMessageBox::warning(this,
- i18nc("@title:window", "Krita"),
- QString(i18n("Permanently delete session %1?", session->name())),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
-
- KisPart::instance()->setCurrentSession(0);
- const QString filename = session->filename();
-
- KoResourceServer<KisSessionResource> *server = KisResourceServerProvider::instance()->sessionServer();
- server->removeResourceFromServer(session);
-
- QFile file(filename);
- file.remove();
-
- updateSessionList();
+ QModelIndex idx = lstSessions->currentIndex();
+ if (idx.isValid()) {
+ m_model->removeResource(lstSessions->currentIndex());
}
}
void KisSessionManagerDialog::slotClose()
{
hide();
}
+
+void KisSessionManagerDialog::slotModelAboutToBeReset(QModelIndex)
+{
+ QModelIndex idx = lstSessions->currentIndex();
+ if (idx.isValid()) {
+ m_lastSessionId = m_model->data(idx, Qt::UserRole + KisResourceModel::Id).toInt();
+ }
+}
+
+void KisSessionManagerDialog::slotModelReset()
+{
+ for (int i = 0; i < m_model->rowCount(); i++) {
+ QModelIndex idx = m_model->index(i, 0);
+ int id = m_model->data(idx, Qt::UserRole + KisResourceModel::Id).toInt();
+ if (id == m_lastSessionId) {
+ lstSessions->setCurrentIndex(idx);
+ }
+ }
+}
diff --git a/libs/ui/dialogs/KisSessionManagerDialog.h b/libs/ui/dialogs/KisSessionManagerDialog.h
index 5ba4566f92..6e5f0a34f0 100644
--- a/libs/ui/dialogs/KisSessionManagerDialog.h
+++ b/libs/ui/dialogs/KisSessionManagerDialog.h
@@ -1,50 +1,59 @@
/*
* 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.
*/
#ifndef KISSESSIONMANAGERDIALOG_H
#define KISSESSIONMANAGERDIALOG_H
#include <QDialog>
#include "ui_wdgsessionmanager.h"
-class KisSessionResource;
+#include <KisSessionResource.h>
+
+class KisResourceModel;
class KisSessionManagerDialog : public QDialog, Ui::DlgSessionManager
{
Q_OBJECT
public:
explicit KisSessionManagerDialog(QWidget *parent = nullptr);
private Q_SLOTS:
void slotNewSession();
void slotRenameSession();
void slotSwitchSession();
void slotDeleteSession();
- void slotSessionDoubleClicked(QListWidgetItem* item);
+ void slotSessionDoubleClicked(QModelIndex item);
void slotClose();
+ void slotModelAboutToBeReset(QModelIndex);
+ void slotModelReset();
+
+
private:
- void updateSessionList();
- KisSessionResource *getSelectedSession() const;
+ KisSessionResourceSP getSelectedSession() const;
+
+ int m_lastSessionId;
+
+ KisResourceModel* m_model;
};
#endif
diff --git a/libs/ui/dialogs/kis_dlg_adj_layer_props.cc b/libs/ui/dialogs/kis_dlg_adj_layer_props.cc
index 4446f4fc2b..74b480c521 100644
--- a/libs/ui/dialogs/kis_dlg_adj_layer_props.cc
+++ b/libs/ui/dialogs/kis_dlg_adj_layer_props.cc
@@ -1,143 +1,144 @@
/*
* 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_dlg_adj_layer_props.h"
#include <klocalizedstring.h>
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLineEdit>
#include "kis_config_widget.h"
#include "kis_transaction.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_paint_device.h"
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include "kis_node_filter_interface.h"
+#include <KisGlobalResourcesInterface.h>
KisDlgAdjLayerProps::KisDlgAdjLayerProps(KisNodeSP node,
KisNodeFilterInterface* nfi,
KisPaintDeviceSP paintDevice,
KisViewManager *view,
KisFilterConfigurationSP configuration,
const QString & layerName,
const QString & caption,
QWidget *parent,
const char *name)
: KoDialog(parent)
, m_node(node)
, m_paintDevice(paintDevice)
, m_currentConfigWidget(0)
, m_currentFilter(0)
, m_currentConfiguration(0)
, m_nodeFilterInterface(nfi)
{
setButtons(Ok | Cancel);
setDefaultButton(Ok);
setObjectName(name);
- m_currentConfiguration = configuration;
+ m_currentConfiguration = configuration->clone();
if (m_currentConfiguration) {
m_currentFilter = KisFilterRegistry::instance()->get(m_currentConfiguration->name()).data();
}
setCaption(caption);
QWidget * page = new QWidget(this);
page->setObjectName("page widget");
QHBoxLayout * layout = new QHBoxLayout(page);
layout->setMargin(0);
setMainWidget(page);
QVBoxLayout *v1 = new QVBoxLayout();
layout->addLayout(v1);
QHBoxLayout *hl = new QHBoxLayout();
v1->addLayout(hl);
QLabel * lblName = new QLabel(i18n("Layer name:"), page);
lblName->setObjectName("lblName");
hl->addWidget(lblName, 0);
m_layerName = new QLineEdit(page);
m_layerName->setObjectName("m_layerName");
m_layerName->setText(layerName);
m_layerName->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
hl->addWidget(m_layerName, 10);
connect(m_layerName, SIGNAL(textChanged(QString)), this, SLOT(slotNameChanged(QString)));
if (m_currentFilter) {
m_currentConfigWidget = m_currentFilter->createConfigurationWidget(page, paintDevice, true);
if (m_currentConfigWidget) {
m_currentConfigWidget->setView(view);
m_currentConfigWidget->setConfiguration(m_currentConfiguration);
}
}
if (m_currentFilter == 0 || m_currentConfigWidget == 0) {
QLabel * labelNoConfigWidget = new QLabel(i18n("No configuration options are available for this filter"), page);
v1->addWidget(labelNoConfigWidget);
}
else {
v1->addWidget(m_currentConfigWidget);
connect(m_currentConfigWidget, SIGNAL(sigConfigurationUpdated()), SLOT(slotConfigChanged()));
}
enableButtonOk(!m_layerName->text().isEmpty());
}
void KisDlgAdjLayerProps::slotNameChanged(const QString & text)
{
enableButtonOk(!text.isEmpty());
}
KisFilterConfigurationSP KisDlgAdjLayerProps::filterConfiguration() const
{
if (m_currentConfigWidget) {
KisFilterConfigurationSP config = dynamic_cast<KisFilterConfiguration*>(m_currentConfigWidget->configuration().data());
if (config) {
return config;
}
}
- return m_currentFilter->defaultConfiguration();
+ return m_currentFilter->defaultConfiguration(KisGlobalResourcesInterface::instance());
}
QString KisDlgAdjLayerProps::layerName() const
{
return m_layerName->text();
}
void KisDlgAdjLayerProps::slotConfigChanged()
{
enableButtonOk(true);
KisFilterConfigurationSP config = filterConfiguration();
if (config) {
- m_nodeFilterInterface->setFilter(config);
+ m_nodeFilterInterface->setFilter(config->cloneWithResourcesSnapshot());
}
m_node->setDirty();
}
diff --git a/libs/ui/dialogs/kis_dlg_adjustment_layer.cc b/libs/ui/dialogs/kis_dlg_adjustment_layer.cc
index 564c2efac2..0f98494dce 100644
--- a/libs/ui/dialogs/kis_dlg_adjustment_layer.cc
+++ b/libs/ui/dialogs/kis_dlg_adjustment_layer.cc
@@ -1,140 +1,140 @@
/*
* 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.
*/
#include "kis_dlg_adjustment_layer.h"
#include <klocalizedstring.h>
#include <QGroupBox>
#include <QLabel>
#include <QLayout>
#include <QGridLayout>
#include <QTimer>
#include <QIcon>
#include <QImage>
#include <QPixmap>
#include <QPushButton>
#include <QDialogButtonBox>
#include "filter/kis_filter.h"
#include "kis_config_widget.h"
#include "filter/kis_filter_configuration.h"
#include "kis_paint_device.h"
#include "kis_transaction.h"
#include "kis_node.h"
#include "kis_node_filter_interface.h"
#include <kis_config.h>
#include "KisViewManager.h"
KisDlgAdjustmentLayer::KisDlgAdjustmentLayer(KisNodeSP node,
KisNodeFilterInterface* nfi,
KisPaintDeviceSP paintDevice,
const QString &layerName,
const QString &caption,
KisViewManager *view, QWidget *parent)
: KoDialog(parent, Qt::Dialog)
, m_node(node)
, m_nodeFilterInterface(nfi)
, m_currentFilter(0)
, m_customName(false)
, m_layerName(layerName)
{
setCaption(caption);
setButtons(None);
QWidget * page = new QWidget(this);
wdgFilterNodeCreation.setupUi(page);
setMainWidget(page);
wdgFilterNodeCreation.filterGalleryToggle->setChecked(wdgFilterNodeCreation.filterSelector->isFilterGalleryVisible());
wdgFilterNodeCreation.filterGalleryToggle->setIcon(QPixmap(":/pics/sidebaricon.png"));
wdgFilterNodeCreation.filterGalleryToggle->setMaximumWidth(wdgFilterNodeCreation.filterGalleryToggle->height());
connect(wdgFilterNodeCreation.filterSelector, SIGNAL(sigFilterGalleryToggled(bool)), wdgFilterNodeCreation.filterGalleryToggle, SLOT(setChecked(bool)));
connect(wdgFilterNodeCreation.filterGalleryToggle, SIGNAL(toggled(bool)), wdgFilterNodeCreation.filterSelector, SLOT(showFilterGallery(bool)));
connect(wdgFilterNodeCreation.filterSelector, SIGNAL(sigSizeChanged()), this, SLOT(slotFilterWidgetSizeChanged()));
connect(wdgFilterNodeCreation.buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(wdgFilterNodeCreation.buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
wdgFilterNodeCreation.filterSelector->setView(view);
wdgFilterNodeCreation.filterSelector->showFilterGallery(KisConfig(true).showFilterGalleryLayerMaskDialog());
wdgFilterNodeCreation.filterSelector->setPaintDevice(false, paintDevice);
wdgFilterNodeCreation.layerName->setText(layerName);
connect(wdgFilterNodeCreation.filterSelector, SIGNAL(configurationChanged()), SLOT(slotConfigChanged()));
connect(wdgFilterNodeCreation.layerName, SIGNAL(textChanged(QString)), SLOT(slotNameChanged(QString)));
slotConfigChanged();
}
KisDlgAdjustmentLayer::~KisDlgAdjustmentLayer()
{
KisConfig(true).setShowFilterGalleryLayerMaskDialog(wdgFilterNodeCreation.filterSelector->isFilterGalleryVisible());
}
void KisDlgAdjustmentLayer::slotNameChanged(const QString &text)
{
Q_UNUSED(text);
m_customName = !text.isEmpty();
enableButtonOk(m_currentFilter);
}
KisFilterConfigurationSP KisDlgAdjustmentLayer::filterConfiguration() const
{
KisFilterConfigurationSP config = wdgFilterNodeCreation.filterSelector->configuration();
Q_ASSERT(config);
return config;
}
QString KisDlgAdjustmentLayer::layerName() const
{
return wdgFilterNodeCreation.layerName->text();
}
void KisDlgAdjustmentLayer::slotConfigChanged()
{
m_currentFilter = filterConfiguration();
enableButtonOk(m_currentFilter);
if (m_currentFilter) {
- m_nodeFilterInterface->setFilter(m_currentFilter);
+ m_nodeFilterInterface->setFilter(m_currentFilter->cloneWithResourcesSnapshot());
if (!m_customName) {
wdgFilterNodeCreation.layerName->blockSignals(true);
wdgFilterNodeCreation.layerName->setText(m_layerName + " (" + wdgFilterNodeCreation.filterSelector->currentFilter()->name() + ")");
wdgFilterNodeCreation.layerName->blockSignals(false);
}
}
m_node->setDirty();
}
void KisDlgAdjustmentLayer::adjustSize()
{
QWidget::adjustSize();
}
void KisDlgAdjustmentLayer::slotFilterWidgetSizeChanged()
{
QMetaObject::invokeMethod(this, "adjustSize", Qt::QueuedConnection);
}
diff --git a/libs/ui/dialogs/kis_dlg_blacklist_cleanup.cpp b/libs/ui/dialogs/kis_dlg_blacklist_cleanup.cpp
deleted file mode 100644
index 5e69de6d95..0000000000
--- a/libs/ui/dialogs/kis_dlg_blacklist_cleanup.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *
- * Copyright (c) 2012 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_dlg_blacklist_cleanup.h"
-#include <KisResourceServerProvider.h>
-
-#include <kis_icon.h>
-#include <KoResourceServerProvider.h>
-
-#include <brushengine/kis_paintop_preset.h>
-#include <kis_workspace_resource.h>
-#include <resources/KoColorSet.h>
-#include <resources/KoAbstractGradient.h>
-#include <resources/KoPattern.h>
-
-KisDlgBlacklistCleanup::KisDlgBlacklistCleanup()
-{
- setCaption(i18n("Cleanup resource files"));
- setButtons(Ok | Cancel);
- setDefaultButton(Ok);
-
- QWidget* page = new QWidget(this);
- setupUi(page);
- setMainWidget(page);
- labelWarning->setPixmap(KisIconUtils::loadIcon("warning").pixmap(32, 32));
-}
-
-void KisDlgBlacklistCleanup::accept()
-{
- QDialog::accept();
- if (cbRemovePresets->isChecked()) {
- KisResourceServerProvider::instance()->paintOpPresetServer()->removeBlackListedFiles();
- }
- if (cbRemoveBrushes->isChecked()) {
- KisResourceServerProvider::instance()->brushBlacklistCleanup();
- }
- if (cbRemoveWorkspaces->isChecked()) {
- KisResourceServerProvider::instance()->workspaceServer()->removeBlackListedFiles();
- }
- if (cbRemoveColorsets->isChecked()) {
- KoResourceServerProvider::instance()->paletteServer()->removeBlackListedFiles();
- }
- if (cbRemoveGradients->isChecked()) {
- KoResourceServerProvider::instance()->gradientServer()->removeBlackListedFiles();
- }
- if (cbRemovePattern->isChecked()) {
- KoResourceServerProvider::instance()->patternServer()->removeBlackListedFiles();
- }
- if (cbRemoveGamutMasks->isChecked()) {
- KoResourceServerProvider::instance()->gamutMaskServer()->removeBlackListedFiles();
- }
-}
-
diff --git a/libs/ui/dialogs/kis_dlg_blacklist_cleanup.h b/libs/ui/dialogs/kis_dlg_blacklist_cleanup.h
deleted file mode 100644
index 8b66731778..0000000000
--- a/libs/ui/dialogs/kis_dlg_blacklist_cleanup.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- *
- * Copyright (c) 2012 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_DLG_BLACKLIST_CLEANUP_H
-#define KIS_DLG_BLACKLIST_CLEANUP_H
-
-#include <KoDialog.h>
-#include <ui_wdgdlgblacklistcleanup.h>
-
-class KisDlgBlacklistCleanup : public KoDialog, public Ui_WdgDisplayBlacklist
-{
-
-public:
- KisDlgBlacklistCleanup();
- void accept() override;
-};
-
-#endif // KIS_DLG_BLACKLIST_CLEANUP_H
diff --git a/libs/ui/dialogs/kis_dlg_filter.cpp b/libs/ui/dialogs/kis_dlg_filter.cpp
index f01e7b93ed..e327804e14 100644
--- a/libs/ui/dialogs/kis_dlg_filter.cpp
+++ b/libs/ui/dialogs/kis_dlg_filter.cpp
@@ -1,251 +1,251 @@
/*
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2008 Boudewijn Rempt <boud@valdysa.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_filter.h"
#include <KoResourcePaths.h>
#include <QPushButton>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_filter_mask.h>
#include <kis_node.h>
#include <kis_layer.h>
#include <kis_paint_layer.h>
#include <KisViewManager.h>
#include <kis_config.h>
#include "kis_selection.h"
#include "kis_node_commands_adapter.h"
#include "kis_filter_manager.h"
#include "ui_wdgfilterdialog.h"
#include "kis_canvas2.h"
struct KisDlgFilter::Private {
Private(KisFilterManager *_filterManager, KisViewManager *_view)
: currentFilter(0)
, resizeCount(0)
, view(_view)
, filterManager(_filterManager)
, blockModifyingActionsGuard(new KisInputActionGroupsMaskGuard(view->canvasBase(), ViewTransformActionGroup))
{
}
KisFilterSP currentFilter;
Ui_FilterDialog uiFilterDialog;
KisNodeSP node;
int resizeCount;
KisViewManager *view;
KisFilterManager *filterManager;
// a special guard object that blocks all the painting input actions while the
// dialog is open
QScopedPointer<KisInputActionGroupsMaskGuard> blockModifyingActionsGuard;
};
KisDlgFilter::KisDlgFilter(KisViewManager *view, KisNodeSP node, KisFilterManager *filterManager, QWidget *parent) :
QDialog(parent),
d(new Private(filterManager, view))
{
setModal(false);
d->uiFilterDialog.setupUi(this);
d->node = node;
d->uiFilterDialog.filterSelection->setView(view);
d->uiFilterDialog.filterSelection->showFilterGallery(KisConfig(true).showFilterGallery());
d->uiFilterDialog.pushButtonCreateMaskEffect->show();
connect(d->uiFilterDialog.pushButtonCreateMaskEffect, SIGNAL(pressed()), SLOT(createMask()));
connect(d->uiFilterDialog.pushButtonCreateMaskEffect,SIGNAL(pressed()),SLOT(close()));
d->uiFilterDialog.filterGalleryToggle->setChecked(d->uiFilterDialog.filterSelection->isFilterGalleryVisible());
d->uiFilterDialog.filterGalleryToggle->setIcon(QPixmap(":/pics/sidebaricon.png"));
d->uiFilterDialog.filterGalleryToggle->setMaximumWidth(d->uiFilterDialog.filterGalleryToggle->height());
connect(d->uiFilterDialog.filterSelection, SIGNAL(sigFilterGalleryToggled(bool)), d->uiFilterDialog.filterGalleryToggle, SLOT(setChecked(bool)));
connect(d->uiFilterDialog.filterGalleryToggle, SIGNAL(toggled(bool)), d->uiFilterDialog.filterSelection, SLOT(showFilterGallery(bool)));
connect(d->uiFilterDialog.filterSelection, SIGNAL(sigSizeChanged()), this, SLOT(slotFilterWidgetSizeChanged()));
if (node->inherits("KisMask")) {
d->uiFilterDialog.pushButtonCreateMaskEffect->setVisible(false);
}
d->uiFilterDialog.filterSelection->setPaintDevice(true, d->node->original());
connect(d->uiFilterDialog.buttonBox, SIGNAL(accepted()), SLOT(accept()));
connect(d->uiFilterDialog.buttonBox, SIGNAL(rejected()), SLOT(reject()));
connect(d->uiFilterDialog.checkBoxPreview, SIGNAL(toggled(bool)), SLOT(enablePreviewToggled(bool)));
connect(d->uiFilterDialog.filterSelection, SIGNAL(configurationChanged()), SLOT(filterSelectionChanged()));
connect(this, SIGNAL(accepted()), SLOT(slotOnAccept()));
connect(this, SIGNAL(accepted()), d->uiFilterDialog.filterSelection, SLOT(slotBookMarkCurrentFilter()));
connect(this, SIGNAL(rejected()), SLOT(slotOnReject()));
KConfigGroup group( KSharedConfig::openConfig(), "filterdialog");
d->uiFilterDialog.checkBoxPreview->setChecked(group.readEntry("showPreview", true));
restoreGeometry(KisConfig(true).readEntry("filterdialog/geometry", QByteArray()));
}
KisDlgFilter::~KisDlgFilter()
{
KisConfig(false).writeEntry("filterdialog/geometry", saveGeometry());
delete d;
}
void KisDlgFilter::setFilter(KisFilterSP f)
{
Q_ASSERT(f);
setDialogTitle(f);
d->uiFilterDialog.filterSelection->setFilter(f);
d->uiFilterDialog.pushButtonCreateMaskEffect->setEnabled(f->supportsAdjustmentLayers());
d->currentFilter = f;
updatePreview();
}
void KisDlgFilter::setDialogTitle(KisFilterSP filter)
{
setWindowTitle(filter.isNull() ? i18nc("@title:window", "Filter") : i18nc("@title:window", "Filter: %1", filter->name()));
}
void KisDlgFilter::startApplyingFilter(KisFilterConfigurationSP config)
{
if (!d->uiFilterDialog.filterSelection->configuration()) return;
if (d->node->inherits("KisPaintLayer")) {
config->setChannelFlags(qobject_cast<KisPaintLayer*>(d->node.data())->channelLockFlags());
}
d->filterManager->apply(config);
}
void KisDlgFilter::updatePreview()
{
KisFilterConfigurationSP config = d->uiFilterDialog.filterSelection->configuration();
if (!config) return;
bool maskCreationAllowed = !d->currentFilter || d->currentFilter->configurationAllowedForMask(config);
d->uiFilterDialog.pushButtonCreateMaskEffect->setEnabled(maskCreationAllowed);
if (d->uiFilterDialog.checkBoxPreview->isChecked()) {
KisFilterConfigurationSP config(d->uiFilterDialog.filterSelection->configuration());
startApplyingFilter(config);
}
d->uiFilterDialog.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
}
void KisDlgFilter::adjustSize()
{
QWidget::adjustSize();
}
void KisDlgFilter::slotFilterWidgetSizeChanged()
{
QMetaObject::invokeMethod(this, "adjustSize", Qt::QueuedConnection);
}
void KisDlgFilter::slotOnAccept()
{
if (!d->filterManager->isStrokeRunning()) {
KisFilterConfigurationSP config(d->uiFilterDialog.filterSelection->configuration());
startApplyingFilter(config);
}
d->filterManager->finish();
d->uiFilterDialog.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
KisConfig(false).setShowFilterGallery(d->uiFilterDialog.filterSelection->isFilterGalleryVisible());
}
void KisDlgFilter::slotOnReject()
{
if (d->filterManager->isStrokeRunning()) {
d->filterManager->cancel();
}
KisConfig(false).setShowFilterGallery(d->uiFilterDialog.filterSelection->isFilterGalleryVisible());
}
void KisDlgFilter::createMask()
{
if (d->node->inherits("KisMask")) return;
if (d->filterManager->isStrokeRunning()) {
d->filterManager->cancel();
}
KisLayer *layer = qobject_cast<KisLayer*>(d->node.data());
KisFilterMaskSP mask = new KisFilterMask();
mask->setName(d->currentFilter->name());
mask->initSelection(d->view->selection(), layer);
- mask->setFilter(d->uiFilterDialog.filterSelection->configuration());
+ mask->setFilter(d->uiFilterDialog.filterSelection->configuration()->cloneWithResourcesSnapshot());
Q_ASSERT(layer->allowAsChild(mask));
KisNodeCommandsAdapter adapter(d->view);
adapter.addNode(mask, layer, layer->lastChild());
}
void KisDlgFilter::enablePreviewToggled(bool state)
{
if (state) {
updatePreview();
} else if (d->filterManager->isStrokeRunning()) {
d->filterManager->cancel();
}
KConfigGroup group( KSharedConfig::openConfig(), "filterdialog");
group.writeEntry("showPreview", d->uiFilterDialog.checkBoxPreview->isChecked());
group.config()->sync();
}
void KisDlgFilter::filterSelectionChanged()
{
KisFilterSP filter = d->uiFilterDialog.filterSelection->currentFilter();
setDialogTitle(filter);
d->currentFilter = filter;
d->uiFilterDialog.pushButtonCreateMaskEffect->setEnabled(filter.isNull() ? false : filter->supportsAdjustmentLayers());
updatePreview();
}
void KisDlgFilter::resizeEvent(QResizeEvent* event)
{
QDialog::resizeEvent(event);
// // Workaround, after the initialisation don't center the dialog anymore
// if(d->resizeCount < 2) {
// QWidget* canvas = d->view->canvas();
// QRect rect(canvas->mapToGlobal(canvas->geometry().topLeft()), size());
// int deltaX = (canvas->geometry().width() - geometry().width())/2;
// int deltaY = (canvas->geometry().height() - geometry().height())/2;
// rect.translate(deltaX, deltaY);
// setGeometry(rect);
// d->resizeCount++;
// }
}
diff --git a/libs/ui/dialogs/kis_dlg_generator_layer.cpp b/libs/ui/dialogs/kis_dlg_generator_layer.cpp
index d4819e6bef..7c2bff6644 100644
--- a/libs/ui/dialogs/kis_dlg_generator_layer.cpp
+++ b/libs/ui/dialogs/kis_dlg_generator_layer.cpp
@@ -1,128 +1,126 @@
/* 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>
+#include <KisGlobalResourcesInterface.h>
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);
isEditing = glayer && previousConfig;
if(isEditing){
setModal(false);
layer = glayer;
- configBefore = previousConfig;
+ configBefore = previousConfig->cloneWithResourcesSnapshot();
}
QWidget *page = new QWidget(this);
m_view = view;
dlgWidget.setupUi(page);
dlgWidget.wdgGenerator->initialize(m_view);
setMainWidget(page);
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);
+ configBefore,
+ configAfter->cloneWithResourcesSnapshot());
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());
+ layer->setFilter(configuration()->cloneWithResourcesSnapshot());
}
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_layer_style.cpp b/libs/ui/dialogs/kis_dlg_layer_style.cpp
index d8d7eaabd6..f649e78fb6 100644
--- a/libs/ui/dialogs/kis_dlg_layer_style.cpp
+++ b/libs/ui/dialogs/kis_dlg_layer_style.cpp
@@ -1,1385 +1,1476 @@
/*
* 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 <KisAslStorage.h>
+#include <KisResourceLocator.h>
#include "kis_signals_blocker.h"
#include "kis_signal_compressor.h"
#include "kis_canvas_resource_provider.h"
#include <KoFileDialog.h>
+#include <QMessageBox>
-
-KoAbstractGradient* fetchGradientLazy(KoAbstractGradient *gradient,
+KoAbstractGradientSP fetchGradientLazy(KoAbstractGradientSP 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_initialLayerStyle(layerStyle->clone().dynamicCast<KisPSDLayerStyle>())
, 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()));
// Contour and Texture are sub-styles of Bevel and Emboss
// They are only applied to canvas when Bevel and Emboss is active.
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);
// slotBevelAndEmbossChanged(QListWidgetItem*) enables/disables Contour and Texture on "Bevel and Emboss" toggle.
connect(wdgLayerStyles.lstStyleSelector, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(slotBevelAndEmbossChanged(QListWidgetItem*)));
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*)));
// improve the checkbox visibility by altering the style sheet list a bit
// the dark themes make them hard to see
QPalette newPalette = palette();
newPalette.setColor(QPalette::Active, QPalette::Background, palette().text().color() );
wdgLayerStyles.lstStyleSelector->setPalette(newPalette);
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::slotBevelAndEmbossChanged(QListWidgetItem*) {
QListWidgetItem *item;
if (wdgLayerStyles.lstStyleSelector->item(6)->checkState() == Qt::Checked) {
// Enable "Contour" (list item 7)
item = wdgLayerStyles.lstStyleSelector->item(7);
Qt::ItemFlags currentFlags7 = item->flags();
item->setFlags(currentFlags7 | Qt::ItemIsEnabled);
// Enable "Texture" (list item 8)
item = wdgLayerStyles.lstStyleSelector->item(8);
Qt::ItemFlags currentFlags8 = item->flags();
item->setFlags(currentFlags8 | Qt::ItemIsEnabled);
}
else {
// Disable "Contour"
item = wdgLayerStyles.lstStyleSelector->item(7);
Qt::ItemFlags currentFlags7 = item->flags();
item->setFlags(currentFlags7 & (~Qt::ItemIsEnabled));
// Disable "Texture"
item = wdgLayerStyles.lstStyleSelector->item(8);
Qt::ItemFlags currentFlags8 = item->flags();
item->setFlags(currentFlags8 & (~Qt::ItemIsEnabled));
}
}
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;
+ KoResourceServer<KisPSDLayerStyle> *server = KisResourceServerProvider::instance()->layerStyleServer();
- KisPSDLayerStyleCollectionResource *collection = dynamic_cast<KisPSDLayerStyleCollectionResource*>(resource);
+ KoResourceSP resource = server->resourceByName(customName);
- Q_FOREACH (KisPSDLayerStyleSP style, collection->layerStyles()) {
- if (style->name() == name) {
- return false;
- }
- }
+ return !resource;
- 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());
+ m_stylesSelector->addNewStyle(style->clone().dynamicCast<KisPSDLayerStyle>());
+}
+
+QString createNewAslPath(QString resourceFolderPath, QString filename)
+{
+ return resourceFolderPath + QDir::separator() + "asl" + QDir::separator() + filename;
}
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();
+ QFileInfo oldFileInfo(filename);
- m_stylesSelector->loadCollection(filename);
- wdgLayerStyles.lstStyleSelector->setCurrentRow(0);
+ KisConfig cfg(true);
+ QString newDir = cfg.readEntry<QString>(KisResourceLocator::resourceLocationKey,
+ QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+ QString newName = oldFileInfo.fileName();
+ QString newLocation = createNewAslPath(newDir, newName);
+
+ QFileInfo newFileInfo(newLocation);
+ if (newFileInfo.exists()) {
+ bool done = false;
+ int i = 0;
+ do {
+ // ask for new filename
+ bool ok;
+ newName = QInputDialog::getText(this, i18n("New name for ASL storage"), i18n("The old filename is taken.\nNew name:"),
+ QLineEdit::Normal, newName, &ok);
+ newLocation = createNewAslPath(newDir, newName);
+ newFileInfo.setFile(newLocation);
+ done = !newFileInfo.exists();
+ i++;
+ } while (!done);
+ }
+
+ QFile::copy(filename, newLocation);
+ KisResourceStorageSP storage = QSharedPointer<KisResourceStorage>::create(newLocation);
+ KIS_ASSERT(!storage.isNull());
+ KisResourceLocator::instance()->addStorage(newLocation, storage);
}
void KisDlgLayerStyle::slotSaveStyle()
{
+ // TODO RESOURCES: needs figuring out
+ warnKrita << "Layer style cannot be saved; needs figuring out what to do here";
+
+ /*
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();
+ KisPSDLayerStyleSP newStyle = style()->clone().dynamicCast<KisPSDLayerStyle>();
newStyle->setName(QFileInfo(filename).completeBaseName());
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_layerStyle = style->clone().dynamicCast<KisPSDLayerStyle>();
}
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 **********************************************/
/********************************************************************/
+StylesSelector::LocationProxyModel::LocationProxyModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+{
+
+}
+
+void StylesSelector::LocationProxyModel::setEnableFiltering(bool enableFiltering)
+{
+ m_enableFiltering = enableFiltering;
+ invalidateFilter();
+}
+
+void StylesSelector::LocationProxyModel::setLocationToFilterBy(QString location)
+{
+ m_locationToFilter = location;
+ invalidateFilter();
+}
+
+bool StylesSelector::LocationProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+ if (!m_enableFiltering) {
+ return true;
+ }
+
+ QModelIndex idx = sourceModel()->index(source_row, 0);
+ QString location = sourceModel()->data(idx, Qt::UserRole + KisResourceModel::Location).toString();
+ qDebug() << sourceModel()->data(idx, Qt::UserRole + KisResourceModel::Location).toString()
+ << sourceModel()->data(idx, Qt::UserRole + KisResourceModel::Name).toString();
+ return location == m_locationToFilter;
+}
+
+
+
+
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);
+ //ui.cmbStyleCollections->setModel();
+ m_resourceModel = KisResourceModelProvider::resourceModel(ResourceType::LayerStyles);
+ m_locationsProxyModel = new LocationProxyModel(this);
+ m_locationsProxyModel->setSourceModel(m_resourceModel);
+ m_locationsProxyModel->setEnableFiltering(false);
+
+ ui.listStyles->setModel(m_locationsProxyModel);
+ ui.listStyles->setModelColumn(KisResourceModel::Name);
+
connect(ui.cmbStyleCollections, SIGNAL(activated(QString)), this, SLOT(loadStyles(QString)));
- connect(ui.listStyles, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(selectStyle(QListWidgetItem*,QListWidgetItem*)));
+ connect(ui.listStyles, SIGNAL(clicked(QModelIndex)), this, SLOT(selectStyle(QModelIndex)));
+ connect(m_resourceModel, SIGNAL(afterResourcesLayoutReset()), this, SLOT(slotResourceModelReset()));
refillCollections();
if (ui.cmbStyleCollections->count()) {
ui.cmbStyleCollections->setCurrentIndex(0);
+ m_locationsProxyModel->setEnableFiltering(true);
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);
+ QStringList locationsList;
+ for (int i = 0; i < m_resourceModel->rowCount(); i++) {
+ QModelIndex idx = m_resourceModel->index(i, 0);
+ QString location = m_resourceModel->data(idx, Qt::UserRole + KisResourceModel::Location).toString();
+ if (!locationsList.contains(location)) {
+ locationsList << location;
+ }
}
+ ui.cmbStyleCollections->clear();
+ ui.cmbStyleCollections->addItems(locationsList);
}
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));
- }
- }
+ m_locationsProxyModel->setLocationToFilterBy(name);
}
-void StylesSelector::selectStyle(QListWidgetItem *current, QListWidgetItem* /*previous*/)
+void StylesSelector::selectStyle(QModelIndex current)
{
+
+ // the index is from the proxy model
+ QModelIndex sourceModelIndex = m_locationsProxyModel->mapToSource(current);
+ KoResourceSP resource = m_resourceModel->resourceForIndex(sourceModelIndex);
+ KisPSDLayerStyleSP layerStyle = resource.dynamicCast<KisPSDLayerStyle>();
+ qDebug() << "StylesSelector::selectStyle" << (resource.isNull() ? "(null)" : resource->name()) << (layerStyle.isNull() ? "(null)" : layerStyle->name());
+ if (layerStyle) {
+ emit styleSelected(layerStyle);
+ }
+
+ /*
StyleItem *item = dynamic_cast<StyleItem*>(current);
if (item) {
emit styleSelected(item->m_style);
}
+ */
}
void StylesSelector::loadCollection(const QString &fileName)
{
+ // TODO: RESOURCES: implement or remove
+ warnKrita << "Collection cannot be loaded, because we do not use collections now; please use KisAslStorage instead.";
+
+ /*
if (!QFileInfo(fileName).exists()) {
warnKrita << "Loaded style collection doesn't exist!";
return;
}
- KisPSDLayerStyleCollectionResource *collection =
- new KisPSDLayerStyleCollectionResource(fileName);
+ KisPSDLayerStyleCollectionResourceSP collection = KisPSDLayerStyleCollectionResourceSP(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::slotResourceModelReset()
+{
+ ENTER_FUNCTION() << "MODEL RESET!!!";
+ refillCollections();
}
void StylesSelector::addNewStyle(KisPSDLayerStyleSP style)
{
- KoResourceServer<KisPSDLayerStyleCollectionResource> *server = KisResourceServerProvider::instance()->layerStyleCollectionServer();
+ KoResourceServer<KisPSDLayerStyle> *server = KisResourceServerProvider::instance()->layerStyleServer();
+ server->addResource(style);
+
+ // TODO: RESOURCES: what about adding only to CustomStyles.asl
+
+
+ /*
+ //server->resourceByName(style->name())
// 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;
+ KoResourceSP resource = server->resourceByName(customName);
+ KisPSDLayerStyleSP style;
if (!resource) {
- collection = new KisPSDLayerStyleCollectionResource("");
+ collection = KisPSDLayerStyleSP(new KisPSDLayerStyle(""));
collection->setName(customName);
collection->setFilename(fullFilename);
KisPSDLayerStyleCollectionResource::StylesVector vector;
vector << style;
collection->setLayerStyles(vector);
server->addResource(collection);
- } else {
- collection = dynamic_cast<KisPSDLayerStyleCollectionResource*>(resource);
+ }
+ else {
+ collection = resource.dynamicCast<KisPSDLayerStyleCollectionResource>();
- KisPSDLayerStyleCollectionResource::StylesVector vector;
+ //KisPSDLayerStyle::StylesVector vector;
vector = collection->layerStyles();
vector << style;
collection->setLayerStyles(vector);
collection->save();
}
+ */
refillCollections();
// select in gui
- int index = ui.cmbStyleCollections->findText(customName);
+ //int index = ui.cmbStyleCollections->findText(customName);
+ int index = 0;
KIS_ASSERT_RECOVER_RETURN(index >= 0);
ui.cmbStyleCollections->setCurrentIndex(index);
- loadStyles(customName);
+ loadStyles("");
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.intSize->setExponentRatio(2.0);
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(" %"));
ui.angleSelector->enableGlobalLight(true);
connect(ui.angleSelector, SIGNAL(globalAngleChanged(int)), SIGNAL(globalAngleChanged(int)));
connect(ui.angleSelector, SIGNAL(configChanged()), 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.patternChooser, SIGNAL(resourceSelected(KoResourceSP )), 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.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.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->setTexturePattern(m_texture->ui.patternChooser->currentResource().staticCast<KoPattern>());
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());
}
/********************************************************************/
/***** 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.intSize->setExponentRatio(2.0);
ui.intNoise->setRange(0, 100);
ui.intNoise->setSuffix(i18n(" %"));
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.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::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.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.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 KoAbstractGradientSP resourceToStyle(KoAbstractGradientSP gradient) {
+ return gradient ? KoAbstractGradientSP(gradient->clone().dynamicCast<KoAbstractGradient>()) : KoAbstractGradientSP();
}
- static KoAbstractGradient* styleToResource(KoAbstractGradientSP gradient) {
+ static KoAbstractGradientSP styleToResource(KoAbstractGradientSP gradient) {
if (!gradient) return 0;
KoResourceServer<KoAbstractGradient> *server = KoResourceServerProvider::instance()->gradientServer();
- KoAbstractGradient *resource = server->resourceByMD5(gradient->md5());
+ KoAbstractGradientSP resource = server->resourceByMD5(gradient->md5());
if (!resource) {
- KoAbstractGradient *clone = gradient->clone();
+ KoAbstractGradientSP clone = gradient->clone().dynamicCast<KoAbstractGradient>();
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.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.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);
+ KoAbstractGradientSP 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.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.angleSelector->value());
config->setScale(ui.intScale->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.intSize->setExponentRatio(2.0);
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);
+ KoAbstractGradientSP 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.patternChooser, SIGNAL(resourceSelected(KoResourceSP )), 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->setPattern(ui.patternChooser->currentResource().staticCast<KoPattern>());
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"));
ui.intSize->setExponentRatio(2.0);
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.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::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.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.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.intSize->setExponentRatio(2.0);
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.intScale, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
+ connect(ui.patternChooser, SIGNAL(resourceSelected(KoResourceSP )), 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::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);
+ KoAbstractGradientSP 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.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.angleSelector->value());
stroke->setScale(ui.intScale->value());
- stroke->setPattern(static_cast<KoPattern*>(ui.patternChooser->currentResource()));
+ stroke->setPattern(ui.patternChooser->currentResource().staticCast<KoPattern>());
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 443d3434d4..48ef252b52 100644
--- a/libs/ui/dialogs/kis_dlg_layer_style.h
+++ b/libs/ui/dialogs/kis_dlg_layer_style.h
@@ -1,301 +1,335 @@
/*
* 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 <QSortFilterProxyModel>
+
#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;
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;
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;
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;
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;
Q_SIGNALS:
void configChanged();
private:
Ui::WdgStroke ui;
KisCanvasResourceProvider *m_resourceProvider;
};
+
+class KisResourceModel;
class StylesSelector : public QWidget {
Q_OBJECT
+
+protected:
+ class LocationProxyModel : public QSortFilterProxyModel
+ {
+ public:
+ LocationProxyModel(QObject* parent);
+
+ void setEnableFiltering(bool enableFiltering);
+ void setLocationToFilterBy(QString location);
+
+ private:
+ QString m_locationToFilter;
+ bool m_enableFiltering;
+
+
+ // QSortFilterProxyModel interface
+ protected:
+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
+
+ };
+
+
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 slotResourceModelReset();
+
void loadStyles(const QString &name);
- void selectStyle(QListWidgetItem *previous, QListWidgetItem* current);
+ void selectStyle(QModelIndex current);
Q_SIGNALS:
void styleSelected(KisPSDLayerStyleSP style);
private:
void refillCollections();
+
+
private:
Ui::WdgStylesSelector ui;
+ LocationProxyModel* m_locationsProxyModel;
+ KisResourceModel* m_resourceModel;
};
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 slotBevelAndEmbossChanged(QListWidgetItem*);
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 f0b3afaddd..c98926e02b 100644
--- a/libs/ui/dialogs/kis_dlg_preferences.cc
+++ b/libs/ui/dialogs/kis_dlg_preferences.cc
@@ -1,1773 +1,1800 @@
/*
* 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 <QDesktopWidget>
+#include <QFileDialog>
+#include <QFormLayout>
+#include <QGridLayout>
+#include <QGroupBox>
#include <QLabel>
#include <QLayout>
#include <QLineEdit>
-#include <QPushButton>
-#include <QSlider>
-#include <QToolButton>
-#include <QThread>
-#include <QStandardPaths>
-#include <QGroupBox>
-#include <QGridLayout>
-#include <QRadioButton>
#include <QMdiArea>
#include <QMessageBox>
-#include <QDesktopWidget>
-#include <QFileDialog>
-#include <QFormLayout>
+#include <QPushButton>
+#include <QRadioButton>
#include <QSettings>
+#include <QSlider>
+#include <QStandardPaths>
+#include <QThread>
+#include <QToolButton>
-#include <KisDocument.h>
-#include <KoColorProfile.h>
#include <KisApplication.h>
-#include <KoFileDialog.h>
+#include <KisDocument.h>
+#include <kis_icon.h>
#include <KisPart.h>
+#include <KoColorProfile.h>
#include <KoColorSpaceEngine.h>
-#include <kis_icon.h>
+#include <KoConfigAuthorPage.h>
#include <KoConfig.h>
+#include <KoFileDialog.h>
#include "KoID.h"
-#include <KoConfigAuthorPage.h>
#include <KoVBox.h>
#include <klocalizedstring.h>
#include <kformat.h>
#include <kundo2stack.h>
#include <KoResourcePaths.h>
+
+#include <KisResourceCacheDb.h>
+#include <KisResourceLocator.h>
+
+#include "KisProofingConfiguration.h"
+#include "KoColorConversionTransformation.h"
#include "kis_action_registry.h"
#include <kis_image.h>
#include <KisSqueezedComboBox.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_action_registry.h"
#include "kis_canvas_resource_provider.h"
-#include "kis_preference_set_registry.h"
+#include "kis_clipboard.h"
#include "kis_color_manager.h"
-#include "KisProofingConfiguration.h"
+#include "kis_config.h"
+#include "kis_cursor.h"
#include "kis_image_config.h"
+#include "kis_preference_set_registry.h"
+#include "widgets/kis_cmb_idlist.h"
+#include <kis_image.h>
+#include "kis_file_name_requester.h"
+
+#include <klocalizedstring.h>
+#include <kundo2stack.h>
+#include <KisSqueezedComboBox.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 "config_use_qt_tablet_windows.h"
# ifndef USE_QT_TABLET_WINDOWS
# include <kis_tablet_support_win8.h>
# endif
#include "config-high-dpi-scale-factor-rounding-policy.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()));
QString xml = cfg.getMDIBackgroundColor();
KoColor mdiColor = KoColor::fromXML(xml);
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());
#ifdef HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY
m_chkHiDPIFractionalScaling->setChecked(kritarc.value("EnableHiDPIFractionalScaling", true).toBool());
#else
m_chkHiDPIFractionalScaling->setVisible(false);
#endif
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());
chkEnableTouchRotation->setChecked(!cfg.disableTouchRotation());
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());
//
// File handling
//
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));
+
+ //
+ // Resources
+ //
+ m_urlCacheDbLocation->setMode(KoFileDialog::OpenDirectory);
+ m_urlCacheDbLocation->setConfigurationName("cachedb_location");
+ m_urlCacheDbLocation->setFileName(cfg.readEntry<QString>(KisResourceCacheDb::dbLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)));
+
+ m_urlResourceFolder->setMode(KoFileDialog::OpenDirectory);
+ m_urlResourceFolder->setConfigurationName("resource_directory");
+ m_urlResourceFolder->setFileName(cfg.readEntry<QString>(KisResourceLocator::resourceLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)));
}
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.fromXML(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);
#ifdef HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY
m_chkHiDPIFractionalScaling->setChecked(true);
#endif
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));
chkEnableTouchRotation->setChecked(!cfg.disableTouchRotation(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);
-
-
+ m_urlCacheDbLocation->setFileName(cfg.readEntry<QString>(KisResourceCacheDb::dbLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)));
+ m_urlResourceFolder->setFileName(cfg.readEntry<QString>(KisResourceLocator::resourceLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)));
}
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 < QGuiApplication::screens().count(); ++i) {
QLabel *lbl = new QLabel(i18nc("The number of the screen", "Screen %1:", i + 1));
m_monitorProfileLabels << lbl;
KisSqueezedComboBox *cmb = new KisSqueezedComboBox();
cmb->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
monitorProfileGrid->addRow(lbl, cmb);
m_monitorProfileWidgets << cmb;
}
// disable if not Linux as KisColorManager is not yet implemented outside Linux
#ifndef Q_OS_LINUX
m_page->chkUseSystemMonitorProfile->setChecked(false);
m_page->chkUseSystemMonitorProfile->setDisabled(true);
m_page->chkUseSystemMonitorProfile->setHidden(true);
#endif
refillMonitorProfiles(KoID("RGBA"));
for(int i = 0; i < QApplication::screens().count(); ++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::screens().count(); ++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::screens().count()) {
for(int i = 0; i < QApplication::screens().count(); ++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::screens().count(); ++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::screens().count(); ++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::screens().count(); ++i) {
m_monitorProfileWidgets[i]->addSqueezedItem(profile->name());
}
}
}
for (int i = 0; i < QApplication::screens().count(); ++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);
m_page->chkUseRightMiddleClickWorkaround->setChecked(
KisConfig(true).useRightMiddleTabletButtonWorkaround(true));
#if defined Q_OS_WIN && (!defined USE_QT_TABLET_WINDOWS || defined QT_HAS_WINTAB_SWITCH)
#ifdef USE_QT_TABLET_WINDOWS
// ask Qt if WinInk is actually available
const bool isWinInkAvailable = true;
#else
const bool isWinInkAvailable = KisTabletSupportWin8::isAvailable();
#endif
if (isWinInkAvailable) {
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);
}
#else
m_page->grpTabletApi->setVisible(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);
m_page->chkUseRightMiddleClickWorkaround->setChecked(
cfg.useRightMiddleTabletButtonWorkaround());
#if defined Q_OS_WIN && (!defined USE_QT_TABLET_WINDOWS || defined QT_HAS_WINTAB_SWITCH)
#ifdef USE_QT_TABLET_WINDOWS
// ask Qt if WinInk is actually available
const bool isWinInkAvailable = true;
#else
const bool isWinInkAvailable = KisTabletSupportWin8::isAvailable();
#endif
if (isWinInkAvailable) {
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);
}
#ifdef USE_QT_TABLET_WINDOWS
connect(m_page->btnResolutionSettings, SIGNAL(clicked()), SLOT(slotResolutionSettings()));
connect(m_page->radioWintab, SIGNAL(toggled(bool)), m_page->btnResolutionSettings, SLOT(setEnabled(bool)));
m_page->btnResolutionSettings->setEnabled(m_page->radioWintab->isChecked());
#else
m_page->btnResolutionSettings->setVisible(false);
#endif
#else
m_page->grpTabletApi->setVisible(false);
#endif
connect(m_page->btnTabletTest, SIGNAL(clicked()), SLOT(slotTabletTest()));
}
void TabletSettingsTab::slotTabletTest()
{
TabletTestDialog tabletTestDialog(this);
tabletTestDialog.exec();
}
#if defined Q_OS_WIN && defined USE_QT_TABLET_WINDOWS
#include "KisDlgCustomTabletResolution.h"
#endif
void TabletSettingsTab::slotResolutionSettings()
{
#if defined Q_OS_WIN && defined USE_QT_TABLET_WINDOWS
KisDlgCustomTabletResolution dlg(this);
dlg.exec();
#endif
}
//---------------------------------------------------------------------------------------------------
#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()));
connect(intMemoryLimit, 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, 300);
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)));
#ifndef Q_OS_WIN
// AVX workaround is needed on Windows+GCC only
chkDisableAVXOptimizations->setVisible(false);
#endif
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));
#ifdef Q_OS_WIN
chkDisableAVXOptimizations->setChecked(cfg2.disableAVXOptimizations(requestDefault));
#endif
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());
#ifdef Q_OS_WIN
cfg2.setDisableAVXOptimizations(chkDisableAVXOptimizations->isChecked());
#endif
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(KisSurfaceColorSpace cs, int depth)
{
const QString csString =
#ifdef HAVE_HDR
cs == KisSurfaceColorSpace::bt2020PQColorSpace ? "Rec. 2020 PQ" :
cs == KisSurfaceColorSpace::scRGBColorSpace ? "Rec. 709 Linear" :
#endif
cs == KisSurfaceColorSpace::sRGBColorSpace ? "sRGB" :
cs == KisSurfaceColorSpace::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");
const QString rendererSoftwareText = i18nc("canvas renderer", "Software Renderer (very slow)");
#ifdef Q_OS_WIN
const QString rendererOpenGLESText = i18nc("canvas renderer", "Direct3D 11 via ANGLE");
#else
const QString rendererOpenGLESText = i18nc("canvas renderer", "OpenGL ES");
#endif
const KisOpenGL::OpenGLRenderer renderer = KisOpenGL::getCurrentOpenGLRenderer();
lblCurrentRenderer->setText(renderer == KisOpenGL::RendererOpenGLES ? rendererOpenGLESText :
renderer == KisOpenGL::RendererDesktopGL ? rendererOpenGLText :
renderer == KisOpenGL::RendererSoftware ? rendererSoftwareText :
i18nc("canvas renderer", "Unknown"));
cmbPreferredRenderer->clear();
const KisOpenGL::OpenGLRenderers supportedRenderers = KisOpenGL::getSupportedOpenGLRenderers();
const bool onlyOneRendererSupported =
supportedRenderers == KisOpenGL::RendererDesktopGL ||
supportedRenderers == KisOpenGL::RendererOpenGLES ||
supportedRenderers == KisOpenGL::RendererSoftware;
if (!onlyOneRendererSupported) {
QString qtPreferredRendererText;
if (KisOpenGL::getQtPreferredOpenGLRenderer() == KisOpenGL::RendererOpenGLES) {
qtPreferredRendererText = rendererOpenGLESText;
} else if (KisOpenGL::getQtPreferredOpenGLRenderer() == KisOpenGL::RendererSoftware) {
qtPreferredRendererText = rendererSoftwareText;
} else {
qtPreferredRendererText = rendererOpenGLText;
}
cmbPreferredRenderer->addItem(i18nc("canvas renderer", "Auto (%1)", qtPreferredRendererText), KisOpenGL::RendererAuto);
cmbPreferredRenderer->setCurrentIndex(0);
} else {
cmbPreferredRenderer->setEnabled(false);
}
if (supportedRenderers & KisOpenGL::RendererDesktopGL) {
cmbPreferredRenderer->addItem(rendererOpenGLText, KisOpenGL::RendererDesktopGL);
if (KisOpenGL::getUserPreferredOpenGLRendererConfig() == KisOpenGL::RendererDesktopGL) {
cmbPreferredRenderer->setCurrentIndex(cmbPreferredRenderer->count() - 1);
}
}
#ifdef Q_OS_ANDROID
if (onlyOneRendererSupported) {
if (KisOpenGL::getQtPreferredOpenGLRenderer() == KisOpenGL::RendererOpenGLES) {
cmbPreferredRenderer->addItem(rendererOpenGLESText, KisOpenGL::RendererOpenGLES);
cmbPreferredRenderer->setCurrentIndex(0);
}
}
#endif
#ifdef Q_OS_WIN
if (supportedRenderers & KisOpenGL::RendererOpenGLES) {
cmbPreferredRenderer->addItem(rendererOpenGLESText, KisOpenGL::RendererOpenGLES);
if (KisOpenGL::getUserPreferredOpenGLRendererConfig() == KisOpenGL::RendererOpenGLES) {
cmbPreferredRenderer->setCurrentIndex(cmbPreferredRenderer->count() - 1);
}
}
if (supportedRenderers & KisOpenGL::RendererSoftware) {
cmbPreferredRenderer->addItem(rendererSoftwareText, KisOpenGL::RendererSoftware);
if (KisOpenGL::getUserPreferredOpenGLRendererConfig() == KisOpenGL::RendererSoftware) {
cmbPreferredRenderer->setCurrentIndex(cmbPreferredRenderer->count() - 1);
}
}
#endif
if (!(supportedRenderers &
(KisOpenGL::RendererDesktopGL |
KisOpenGL::RendererOpenGLES |
KisOpenGL::RendererSoftware))) {
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(KisSurfaceColorSpace::sRGBColorSpace, 8));
#ifdef HAVE_HDR
cmbPreferedRootSurfaceFormat->addItem(colorSpaceString(KisSurfaceColorSpace::bt2020PQColorSpace, 10));
cmbPreferedRootSurfaceFormat->addItem(colorSpaceString(KisSurfaceColorSpace::scRGBColorSpace, 16));
#endif
cmbPreferedRootSurfaceFormat->setCurrentIndex(formatToIndex(KisConfig::BT709_G22));
slotPreferredSurfaceFormatChanged(cmbPreferedRootSurfaceFormat->currentIndex());
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
context = QOpenGLContext::globalShareContext();
}
if (context) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
QScreen *screen = QGuiApplication::screenAt(rect().center());
#else
QScreen *screen = 0;
#endif
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();
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
KisSurfaceColorSpace colorSpace = currentFormat.colorSpace();
#else
KisSurfaceColorSpace colorSpace = KisSurfaceColorSpace::DefaultColorSpace;
#endif
lblCurrentRootSurfaceFormat->setText(colorSpaceString(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);
tabWidget->removeTab(tabWidget->indexOf(tabHDR));
#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));
KisImageConfig imageCfg(false);
KoColor c;
c.fromQColor(imageCfg.selectionOverlayMaskColor(true));
c.setOpacity(1.0);
btnSelectionOverlayColor->setColor(c);
sldSelectionOverlayOpacity->setValue(imageCfg.selectionOverlayMaskColor(true).alphaF());
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) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
QScreen *screen = QGuiApplication::screenAt(rect().center());
#else
QScreen *screen = 0;
#endif
KisScreenInformationAdapter adapter(context);
if (adapter.isValid()) {
KisScreenInformationAdapter::ScreenInfo info = adapter.infoForScreen(screen);
if (info.isValid()) {
if (cmbPreferedRootSurfaceFormat->currentIndex() != formatToIndex(KisConfig::BT709_G22) &&
info.colorSpace == KisSurfaceColorSpace::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::List);
// 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();
QStringList keys = preferenceSetRegistry->keys();
keys.sort();
Q_FOREACH(const QString &key, keys) {
KisAbstractPreferenceSetFactory *preferenceSetFactory = preferenceSetRegistry->value(key);
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::slotButtonClicked(QAbstractButton *button)
{
if (buttonBox()->buttonRole(button) == QDialogButtonBox::RejectRole) {
m_cancelClicked = true;
}
}
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()
{
connect(this->buttonBox(), SIGNAL(clicked(QAbstractButton*)), this, SLOT(slotButtonClicked(QAbstractButton*)));
int retval = exec();
Q_UNUSED(retval)
if (!m_cancelClicked) {
// General settings
KisConfig cfg(false);
cfg.setNewCursorStyle(m_general->cursorStyle());
cfg.setNewOutlineStyle(m_general->outlineStyle());
cfg.setShowRootLayer(m_general->showRootLayer());
cfg.setShowOutlineWhilePainting(m_general->showOutlineWhilePainting());
cfg.setForceAlwaysFullSizedOutline(!m_general->m_changeBrushOutline->isChecked());
cfg.setSessionOnStartup(m_general->sessionOnStartup());
cfg.setSaveSessionOnQuit(m_general->saveSessionOnQuit());
KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs");
group.writeEntry("DontUseNativeFileDialog", !m_general->m_chkNativeFileDialog->isChecked());
cfg.writeEntry<int>("maximumBrushSize", m_general->intMaxBrushSize->value());
cfg.writeEntry<int>("mdi_viewmode", m_general->mdiMode());
cfg.setMDIBackgroundColor(m_general->m_mdiColor->color().toXML());
cfg.setMDIBackgroundImage(m_general->m_backgroundimage->text());
cfg.setAutoSaveInterval(m_general->autoSaveInterval());
cfg.writeEntry("autosavefileshidden", m_general->chkHideAutosaveFiles->isChecked());
cfg.setBackupFile(m_general->m_backupFileCheckBox->isChecked());
cfg.writeEntry("backupfilelocation", m_general->cmbBackupFileLocation->currentIndex());
cfg.writeEntry("backupfilesuffix", m_general->txtBackupFileSuffix->text());
cfg.writeEntry("numberofbackupfiles", m_general->intNumBackupFiles->value());
cfg.setShowCanvasMessages(m_general->showCanvasMessages());
cfg.setCompressKra(m_general->compressKra());
cfg.setUseZip64(m_general->useZip64());
const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
kritarc.setValue("EnableHiDPI", m_general->m_chkHiDPI->isChecked());
#ifdef HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY
kritarc.setValue("EnableHiDPIFractionalScaling", m_general->m_chkHiDPIFractionalScaling->isChecked());
#endif
kritarc.setValue("EnableSingleApplication", m_general->m_chkSingleApplication->isChecked());
kritarc.setValue("LogUsage", m_general->chkUsageLogging->isChecked());
cfg.setToolOptionsInDocker(m_general->toolOptionsInDocker());
cfg.writeEntry<bool>("useCreamyAlphaDarken", (bool)!m_general->cmbFlowMode->currentIndex());
cfg.setKineticScrollingEnabled(m_general->kineticScrollingEnabled());
cfg.setKineticScrollingGesture(m_general->kineticScrollingGesture());
cfg.setKineticScrollingSensitivity(m_general->kineticScrollingSensitivity());
cfg.setKineticScrollingHideScrollbars(m_general->kineticScrollingHiddenScrollbars());
cfg.setSwitchSelectionCtrlAlt(m_general->switchSelectionCtrlAlt());
cfg.setDisableTouchOnCanvas(!m_general->chkEnableTouch->isChecked());
cfg.setDisableTouchRotation(!m_general->chkEnableTouchRotation->isChecked());
cfg.setActivateTransformToolAfterPaste(m_general->chkEnableTranformToolAfterPaste->isChecked());
cfg.setConvertToImageColorspaceOnImport(m_general->convertToImageColorspaceOnImport());
cfg.setUndoStackLimit(m_general->undoStackSize());
cfg.setFavoritePresets(m_general->favoritePresets());
+ cfg.writeEntry(KisResourceCacheDb::dbLocationKey, m_general->m_urlCacheDbLocation->fileName());
+ cfg.writeEntry(KisResourceLocator::resourceLocationKey, m_general->m_urlResourceFolder->fileName());
+
// Color settings
cfg.setUseSystemMonitorProfile(m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked());
for (int i = 0; i < QApplication::screens().count(); ++i) {
if (m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()) {
int currentIndex = m_colorSettings->m_monitorProfileWidgets[i]->currentIndex();
QString monitorid = m_colorSettings->m_monitorProfileWidgets[i]->itemData(currentIndex).toString();
cfg.setMonitorForScreen(i, monitorid);
}
else {
cfg.setMonitorProfile(i,
m_colorSettings->m_monitorProfileWidgets[i]->currentUnsqueezedText(),
m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked());
}
}
cfg.setWorkingColorSpace(m_colorSettings->m_page->cmbWorkingColorSpace->currentItem().id());
KisImageConfig cfgImage(false);
cfgImage.setDefaultProofingConfig(m_colorSettings->m_page->proofingSpaceSelector->currentColorSpace(),
m_colorSettings->m_page->cmbProofingIntent->currentIndex(),
m_colorSettings->m_page->ckbProofBlackPoint->isChecked(),
m_colorSettings->m_page->gamutAlarm->color(),
(double)m_colorSettings->m_page->sldAdaptationState->value()/20);
cfg.setUseBlackPointCompensation(m_colorSettings->m_page->chkBlackpoint->isChecked());
cfg.setAllowLCMSOptimization(m_colorSettings->m_page->chkAllowLCMSOptimization->isChecked());
cfg.setForcePaletteColors(m_colorSettings->m_page->chkForcePaletteColor->isChecked());
cfg.setPasteBehaviour(m_colorSettings->m_pasteBehaviourGroup.checkedId());
cfg.setRenderIntent(m_colorSettings->m_page->cmbMonitorIntent->currentIndex());
// Tablet settings
cfg.setPressureTabletCurve( m_tabletSettings->m_page->pressureCurve->curve().toString() );
cfg.setUseRightMiddleTabletButtonWorkaround(
m_tabletSettings->m_page->chkUseRightMiddleClickWorkaround->isChecked());
#if defined Q_OS_WIN && (!defined USE_QT_TABLET_WINDOWS || defined QT_HAS_WINTAB_SWITCH)
#ifdef USE_QT_TABLET_WINDOWS
// ask Qt if WinInk is actually available
const bool isWinInkAvailable = true;
#else
const bool isWinInkAvailable = KisTabletSupportWin8::isAvailable();
#endif
if (isWinInkAvailable) {
cfg.setUseWin8PointerInput(m_tabletSettings->m_page->radioWin8PointerInput->isChecked());
}
#endif
m_performanceSettings->save();
if (!cfg.useOpenGL() && m_displaySettings->grpOpenGL->isChecked())
cfg.setCanvasState("TRY_OPENGL");
if (m_displaySettings->grpOpenGL->isChecked()) {
KisOpenGL::OpenGLRenderer renderer = static_cast<KisOpenGL::OpenGLRenderer>(
m_displaySettings->cmbPreferredRenderer->itemData(
m_displaySettings->cmbPreferredRenderer->currentIndex()).toInt());
KisOpenGL::setUserPreferredOpenGLRendererConfig(renderer);
} else {
KisOpenGL::setUserPreferredOpenGLRendererConfig(KisOpenGL::RendererNone);
}
cfg.setUseOpenGLTextureBuffer(m_displaySettings->chkUseTextureBuffer->isChecked());
cfg.setOpenGLFilteringMode(m_displaySettings->cmbFilterMode->currentIndex());
cfg.setDisableVSync(m_displaySettings->chkDisableVsync->isChecked());
cfg.setRootSurfaceFormat(&kritarc, indexToFormat(m_displaySettings->cmbPreferedRootSurfaceFormat->currentIndex()));
cfg.setCheckSize(m_displaySettings->intCheckSize->value());
cfg.setScrollingCheckers(m_displaySettings->chkMoving->isChecked());
cfg.setCheckersColor1(m_displaySettings->colorChecks1->color().toQColor());
cfg.setCheckersColor2(m_displaySettings->colorChecks2->color().toQColor());
cfg.setCanvasBorderColor(m_displaySettings->canvasBorder->color().toQColor());
cfg.setHideScrollbars(m_displaySettings->hideScrollbars->isChecked());
KoColor c = m_displaySettings->btnSelectionOverlayColor->color();
c.setOpacity(m_displaySettings->sldSelectionOverlayOpacity->value());
cfgImage.setSelectionOverlayMaskColor(c.toQColor());
cfg.setAntialiasCurves(m_displaySettings->chkCurveAntialiasing->isChecked());
cfg.setAntialiasSelectionOutline(m_displaySettings->chkSelectionOutlineAntialiasing->isChecked());
cfg.setShowSingleChannelAsColor(m_displaySettings->chkChannelsAsColor->isChecked());
cfg.setHidePopups(m_displaySettings->chkHidePopups->isChecked());
cfg.setHideDockersFullscreen(m_fullscreenSettings->chkDockers->checkState());
cfg.setHideMenuFullscreen(m_fullscreenSettings->chkMenu->checkState());
cfg.setHideScrollbarsFullscreen(m_fullscreenSettings->chkScrollbars->checkState());
cfg.setHideStatusbarFullscreen(m_fullscreenSettings->chkStatusbar->checkState());
cfg.setHideTitlebarFullscreen(m_fullscreenSettings->chkTitlebar->checkState());
cfg.setHideToolbarFullscreen(m_fullscreenSettings->chkToolbar->checkState());
cfg.setCursorMainColor(m_general->cursorColorBtutton->color().toQColor());
cfg.setPixelGridColor(m_displaySettings->pixelGridColorButton->color().toQColor());
cfg.setPixelGridDrawingThreshold(m_displaySettings->pixelGridDrawingThresholdBox->value() / 100);
m_authorPage->apply();
cfg.logImportantSettings();
}
return !m_cancelClicked;
}
diff --git a/libs/ui/forms/WdgDlgPaletteEditor.ui b/libs/ui/forms/WdgDlgPaletteEditor.ui
index f22c584c6b..98100fa8f2 100644
--- a/libs/ui/forms/WdgDlgPaletteEditor.ui
+++ b/libs/ui/forms/WdgDlgPaletteEditor.ui
@@ -1,217 +1,207 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgDlgPaletteEditor</class>
<widget class="QDialog" name="WdgDlgPaletteEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>524</width>
<height>481</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QGroupBox" name="gbxPalette">
<property name="title">
<string>Palette settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="labelName">
<property name="text">
<string>Palette Name</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEditName"/>
</item>
- <item>
- <widget class="QLabel" name="labelFilename">
- <property name="text">
- <string>File name</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLineEdit" name="lineEditFilename"/>
- </item>
<item>
<widget class="QLabel" name="labelColCount">
<property name="text">
<string>Column count</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBoxCol"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QRadioButton" name="rb_global">
<property name="text">
<string>Resource Folder</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rb_document">
<property name="text">
<string>Document</string>
</property>
</widget>
</item>
</layout>
</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>
<item>
<widget class="QToolButton" name="bnAddGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Add a group</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gbxGroup">
<property name="title">
<string>Group settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QComboBox" name="cbxGroup"/>
</item>
<item>
<widget class="QLabel" name="labelRowCount">
<property name="text">
<string>Row count</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBoxRow"/>
</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>
<item>
<widget class="QToolButton" name="bnRenGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Rename this group</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="bnDelGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Delete this group</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>WdgDlgPaletteEditor</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>WdgDlgPaletteEditor</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
diff --git a/libs/ui/forms/wdgdlgblacklistcleanup.ui b/libs/ui/forms/wdgdlgblacklistcleanup.ui
deleted file mode 100644
index c871b39a0b..0000000000
--- a/libs/ui/forms/wdgdlgblacklistcleanup.ui
+++ /dev/null
@@ -1,134 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>WdgDisplayBlacklist</class>
- <widget class="QWidget" name="WdgDisplayBlacklist">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>457</width>
- <height>200</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="windowTitle">
- <string>Display</string>
- </property>
- <layout class="QGridLayout">
- <item row="6" column="0">
- <widget class="QCheckBox" name="cbRemoveWorkspaces">
- <property name="text">
- <string>Workspaces</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1">
- <item>
- <widget class="QLabel" name="labelWarning">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="label_2">
- <property name="minimumSize">
- <size>
- <width>400</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
-&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
-p, li { white-space: pre-wrap; }
-&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
-&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Warning&lt;/span&gt;: Cleanup will remove resource files permanently.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="textFormat">
- <enum>Qt::RichText</enum>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="4" column="0">
- <widget class="QCheckBox" name="cbRemoveGradients">
- <property name="toolTip">
- <string>Use trilinear filtering when zooming. Disabling this may improve painting performance.</string>
- </property>
- <property name="text">
- <string>Gradients</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="5" column="0">
- <widget class="QCheckBox" name="cbRemovePattern">
- <property name="text">
- <string>Pattern</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QCheckBox" name="cbRemovePresets">
- <property name="text">
- <string>Presets</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QCheckBox" name="cbRemoveColorsets">
- <property name="text">
- <string>Colorsets</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QCheckBox" name="cbRemoveBrushes">
- <property name="text">
- <string>Brushes</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="7" column="0">
- <widget class="QCheckBox" name="cbRemoveGamutMasks">
- <property name="text">
- <string>Gamut Masks</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/libs/ui/forms/wdggeneralsettings.ui b/libs/ui/forms/wdggeneralsettings.ui
index 79716b52e3..c9a6f8752d 100644
--- a/libs/ui/forms/wdggeneralsettings.ui
+++ b/libs/ui/forms/wdggeneralsettings.ui
@@ -1,935 +1,968 @@
<?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>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_chkHiDPIFractionalScaling">
<property name="text">
<string>(Hi-DPI) Enable fractional scale factor</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QCheckBox" name="m_chkSingleApplication">
<property name="text">
<string>Allow only one instance of Krita</string>
</property>
</widget>
</item>
<item row="9" 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="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>
</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="QCheckBox" name="chkEnableTouchRotation">
<property name="text">
<string>Enable Touch Rotation</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>
<widget class="QComboBox" name="m_cmbKineticScrollingGesture"/>
</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>
</item>
<item>
<widget class="QCheckBox" name="m_chkKineticScrollingHideScrollbars">
<property name="text">
<string>Hide docker scrollbars if kinetic scrolling is enabled (needs restart)</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>
<property name="text">
<string>Same Folder as Original File</string>
</property>
</item>
<item>
<property name="text">
<string>User Folder</string>
</property>
</item>
<item>
<property name="text">
<string>Temporary File Location</string>
</property>
</item>
</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>
<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::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="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>
</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">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="chkSaveSessionOnQuit">
<property name="text">
<string>Save session when Krita closes</string>
</property>
</widget>
</item>
<item row="3" 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="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="toolTip">
<string>Only applies to new or newly opened images.</string>
</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="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="toolTip">
<string>Only applies to new or newly opened images.</string>
</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="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>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<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="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="12" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Maximum brush size:</string>
</property>
</widget>
</item>
<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="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>
</layout>
</widget>
+ <widget class="QWidget" name="tab_4">
+ <attribute name="title">
+ <string>Resources</string>
+ </attribute>
+ <layout class="QFormLayout" name="formLayout_7">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Cache Location:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="KisFileNameRequester" name="m_urlCacheDbLocation" native="true"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Resource Folder:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="KisFileNameRequester" name="m_urlResourceFolder" native="true"/>
+ </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>
+ <customwidget>
+ <class>KisFileNameRequester</class>
+ <extends>QWidget</extends>
+ <header>kis_file_name_requester.h</header>
+ <container>1</container>
+ </customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/libs/ui/forms/wdgpaintopsettings.ui b/libs/ui/forms/wdgpaintopsettings.ui
index 2ceb4d3b82..f40f220c1f 100644
--- a/libs/ui/forms/wdgpaintopsettings.ui
+++ b/libs/ui/forms/wdgpaintopsettings.ui
@@ -1,949 +1,949 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgPaintOpSettings</class>
<widget class="QWidget" name="WdgPaintOpSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1203</width>
<height>431</height>
</rect>
</property>
<property name="windowTitle">
<string>Brush Editor</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="3">
<layout class="QHBoxLayout" name="brushEditorDashboard">
<property name="spacing">
<number>9</number>
</property>
<property name="leftMargin">
<number>1</number>
</property>
<item>
<widget class="QLabel" name="presetThumbnailicon">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>55</width>
<height>55</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="KisPresetLivePreviewView" name="liveBrushPreviewView">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>320</width>
<height>60</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>320</width>
<height>60</height>
</size>
</property>
<property name="font">
<font>
<kerning>false</kerning>
</font>
</property>
<property name="contextMenuPolicy">
<enum>Qt::DefaultContextMenu</enum>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="midLineWidth">
<number>0</number>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="interactive">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="renderHints">
<set>QPainter::Antialiasing|QPainter::HighQualityAntialiasing|QPainter::SmoothPixmapTransform</set>
</property>
<property name="resizeAnchor">
<enum>QGraphicsView::AnchorViewCenter</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="spacing">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="currentBrushNameLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
- <string>current brush</string>
+ <string>Current brush preset</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="renameBrushPresetButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="renameBrushNameTextField">
<property name="minimumSize">
<size>
<width>180</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="updateBrushNameButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>28</height>
</size>
</property>
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelBrushNameUpdateButton">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>28</height>
</size>
</property>
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="currentBrushEngineIcon">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>26</width>
<height>26</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="currentBrushEngineLabel">
<property name="text">
<string>Current Brush Engine</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="reloadPresetButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>26</width>
<height>26</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>26</width>
<height>26</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="dirtyPresetIndicatorButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<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>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="saveNewBrushPresetButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>28</height>
</size>
</property>
<property name="text">
<string>Save New Brush Preset...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="saveBrushPresetButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>28</height>
</size>
</property>
<property name="text">
- <string>Overwrite Brush</string>
+ <string>Overwrite Brush Preset</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="presetsContainer">
<property name="title">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QPushButton" name="showPresetsButton">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>12</width>
<height>12</height>
</size>
</property>
<property name="shortcut">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="presetsSidebarLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Presets</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="engineFilterLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Engine:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="brushEgineComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="presetChangeViewToolButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="KisPresetSelectorStrip" name="presetWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>120</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1677215</width>
<height>1677215</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QToolButton" name="newPresetEngineButton">
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="bnBlacklistPreset">
<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="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="presetsSpacer">
<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>
</item>
<item row="1" column="1">
<widget class="QGroupBox" name="brushEditorSettingsControls">
<property name="title">
<string/>
</property>
<property name="flat">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="brushEditorLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QFrame" name="frmOptionWidgetContainer">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>4</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,0,0,0,0">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QCheckBox" name="eraserBrushSizeCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Erase mode will use a separate brush size</string>
</property>
<property name="text">
<string>Eraser switch size</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="eraserBrushOpacityCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Erase mode will use a separate brush opacity</string>
</property>
<property name="text">
<string>Eraser switch opacity</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="dirtyPresetCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Temporarily Save Tweaks To Presets</string>
</property>
<property name="checked">
<bool>true</bool>
</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="KisLodAvailabilityWidget" name="wdgLodAvailability" native="true">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</item>
</layout>
</widget>
</item>
<item row="1" column="2">
<widget class="QGroupBox" name="scratchpadControls">
<property name="title">
<string/>
</property>
<property name="flat">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QPushButton" name="showScratchpadButton">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>20</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>12</width>
<height>12</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="scratchpadSidebarLabel">
<property name="text">
<string>Scratchpad</string>
</property>
</widget>
</item>
</layout>
</item>
<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_2">
<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="KisScratchPad" name="scratchPad" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,0,0,0">
<item>
<widget class="QPushButton" name="paintPresetIcon">
<property name="toolTip">
<string>Fill area with brush preset icon</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fillLayer">
<property name="toolTip">
<string>Fill area with current image</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fillGradient">
<property name="toolTip">
<string>Fill area with gradient</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fillSolid">
<property name="toolTip">
<string>Fill area with background color</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="eraseScratchPad">
<property name="toolTip">
<string>Reset area to white</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisScratchPad</class>
<extends>QWidget</extends>
<header>kis_scratch_pad.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisPresetLivePreviewView</class>
<extends>QGraphicsView</extends>
<header>kis_preset_live_preview_view.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisPresetSelectorStrip</class>
<extends>QWidget</extends>
<header>kis_preset_selector_strip.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisLodAvailabilityWidget</class>
<extends>QWidget</extends>
<header>kis_lod_availability_widget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/libs/ui/forms/wdgsessionmanager.ui b/libs/ui/forms/wdgsessionmanager.ui
index 18b0549761..75338cabc8 100644
--- a/libs/ui/forms/wdgsessionmanager.ui
+++ b/libs/ui/forms/wdgsessionmanager.ui
@@ -1,100 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DlgSessionManager</class>
<widget class="QWidget" name="DlgSessionManager">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>363</width>
<height>238</height>
</rect>
</property>
<property name="windowTitle">
<string>Sessions</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<item>
- <widget class="QListWidget" name="lstSessions"/>
+ <widget class="QListView" name="lstSessions"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="btnNew">
<property name="text">
<string>New...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnRename">
<property name="text">
<string>Rename...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSwitchTo">
<property name="text">
<string>Switch to</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnDelete">
<property name="text">
<string>Delete...</string>
</property>
</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>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<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="QPushButton" name="btnClose">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
diff --git a/libs/ui/input/kis_input_manager_p.cpp b/libs/ui/input/kis_input_manager_p.cpp
index 000c34a070..0fd9346e20 100644
--- a/libs/ui/input/kis_input_manager_p.cpp
+++ b/libs/ui/input/kis_input_manager_p.cpp
@@ -1,678 +1,678 @@
/*
* 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_p.h"
#include <QMap>
#include <QApplication>
#include <QScopedPointer>
#include <QtGlobal>
#include "kis_input_manager.h"
#include "kis_config.h"
#include "kis_abstract_input_action.h"
#include "kis_tool_invocation_action.h"
#include "kis_stroke_shortcut.h"
#include "kis_touch_shortcut.h"
#include "kis_native_gesture_shortcut.h"
#include "kis_input_profile_manager.h"
#include "kis_extended_modifiers_mapper.h"
#include "kis_zoom_and_rotate_action.h"
/**
* This hungry class EventEater encapsulates event masking logic.
*
* Its basic role is to kill synthetic mouseMove events sent by Xorg or Qt after
* tablet events. Those events are sent in order to allow widgets that haven't
* implemented tablet specific functionality to seamlessly behave as if one were
* using a mouse. These synthetic events are *supposed* to be optional, or at
* least come with a flag saying "This is a fake event!!" but neither of those
* methods is trustworthy. (This is correct as of Qt 5.4 + Xorg.)
*
* Qt 5.4 provides no reliable way to see if a user's tablet is being hovered
* over the pad, since it converts all tablethover events into mousemove, with
* no option to turn this off. Moreover, sometimes the MouseButtonPress event
* from the tapping their tablet happens BEFORE the TabletPress event. This
* means we have to resort to a somewhat complicated logic. What makes this
* truly a joke is that we are not guaranteed to observe TabletProximityEnter
* events when we're using a tablet, either, you may only see an Enter event.
*
* Once we see tablet events heading our way, we can say pretty confidently that
* every mouse event is fake. There are two painful cases to consider - a
* mousePress event could arrive before the tabletPress event, or it could
* arrive much later, e.g. after tabletRelease. The first was only seen on Linux
* with Qt's XInput2 code, the solution was to hold onto mousePress events
* temporarily and wait for tabletPress later, this is contained in git history
* but is now removed. The second case is currently handled by the
* eatOneMousePress function, which waits as long as necessary to detect and
* block a single mouse press event.
*/
static bool isMouseEventType(QEvent::Type t)
{
return (t == QEvent::MouseMove ||
t == QEvent::MouseButtonPress ||
t == QEvent::MouseButtonRelease ||
t == QEvent::MouseButtonDblClick);
}
KisInputManager::Private::EventEater::EventEater()
{
KisConfig cfg(true);
activateSecondaryButtonsWorkaround = cfg.useRightMiddleTabletButtonWorkaround();
}
bool KisInputManager::Private::EventEater::eventFilter(QObject* target, QEvent* event )
{
Q_UNUSED(target)
auto debugEvent = [&](int i) {
if (KisTabletDebugger::instance()->debugEnabled()) {
QString pre = QString("[BLOCKED %1:]").arg(i);
QMouseEvent *ev = static_cast<QMouseEvent*>(event);
dbgTablet << KisTabletDebugger::instance()->eventToString(*ev, pre);
}
};
auto debugTabletEvent = [&](int i) {
if (KisTabletDebugger::instance()->debugEnabled()) {
QString pre = QString("[BLOCKED %1:]").arg(i);
QTabletEvent *ev = static_cast<QTabletEvent*>(event);
dbgTablet << KisTabletDebugger::instance()->eventToString(*ev, pre);
}
};
if (peckish && event->type() == QEvent::MouseButtonPress
// Drop one mouse press following tabletPress or touchBegin
&& (static_cast<QMouseEvent*>(event)->button() == Qt::LeftButton)) {
peckish = false;
debugEvent(1);
return true;
}
if (activateSecondaryButtonsWorkaround) {
if (event->type() == QEvent::TabletPress ||
event->type() == QEvent::TabletRelease) {
QTabletEvent *te = static_cast<QTabletEvent*>(event);
if (te->button() != Qt::LeftButton) {
debugTabletEvent(3);
return true;
}
} else if (event->type() == QEvent::MouseButtonPress ||
event->type() == QEvent::MouseButtonRelease ||
event->type() == QEvent::MouseButtonDblClick) {
QMouseEvent *me = static_cast<QMouseEvent*>(event);
if (me->button() != Qt::LeftButton) {
return false;
}
}
}
if (isMouseEventType(event->type()) &&
(hungry
// On Mac, we need mouse events when the tablet is in proximity, but not pressed down
// since tablet move events are not generated until after tablet press.
#ifndef Q_OS_MAC
|| (eatSyntheticEvents && static_cast<QMouseEvent*>(event)->source() != Qt::MouseEventNotSynthesized)
#endif
)) {
// Drop mouse events if enabled or event was synthetic & synthetic events are disabled
debugEvent(2);
return true;
}
return false; // All clear - let this one through!
}
void KisInputManager::Private::EventEater::activate()
{
if (!hungry && (KisTabletDebugger::instance()->debugEnabled())) {
dbgTablet << "Start blocking mouse events";
}
hungry = true;
}
void KisInputManager::Private::EventEater::deactivate()
{
if (hungry && (KisTabletDebugger::instance()->debugEnabled())) {
dbgTablet << "Stop blocking mouse events";
}
hungry = false;
}
void KisInputManager::Private::EventEater::eatOneMousePress()
{
// Enable on other platforms if getting full-pressure splotches
peckish = true;
}
bool KisInputManager::Private::ignoringQtCursorEvents()
{
return eventEater.hungry;
}
void KisInputManager::Private::setMaskSyntheticEvents(bool value)
{
eventEater.eatSyntheticEvents = value;
}
KisInputManager::Private::Private(KisInputManager *qq)
: q(qq)
, moveEventCompressor(10 /* ms */,
KisSignalCompressor::FIRST_ACTIVE,
KisSignalCompressor::ADDITIVE_INTERVAL)
, priorityEventFilterSeqNo(0)
, canvasSwitcher(this, qq)
{
KisConfig cfg(true);
moveEventCompressor.setDelay(cfg.tabletEventsDelay());
testingAcceptCompressedTabletEvents = cfg.testingAcceptCompressedTabletEvents();
testingCompressBrushEvents = cfg.testingCompressBrushEvents();
if (cfg.trackTabletEventLatency()) {
tabletLatencyTracker = new TabletLatencyTracker();
}
matcher.setInputActionGroupsMaskCallback(
[this] () {
return this->canvas ? this->canvas->inputActionGroupsMask() : AllActionGroup;
});
}
static const int InputWidgetsThreshold = 2000;
static const int OtherWidgetsThreshold = 400;
KisInputManager::Private::CanvasSwitcher::CanvasSwitcher(Private *_d, QObject *p)
: QObject(p),
d(_d),
eatOneMouseStroke(false),
focusSwitchThreshold(InputWidgetsThreshold)
{
}
void KisInputManager::Private::CanvasSwitcher::setupFocusThreshold(QObject* object)
{
QWidget *widget = qobject_cast<QWidget*>(object);
KIS_SAFE_ASSERT_RECOVER_RETURN(widget);
thresholdConnections.clear();
thresholdConnections.addConnection(&focusSwitchThreshold, SIGNAL(timeout()), widget, SLOT(setFocus()));
}
void KisInputManager::Private::CanvasSwitcher::addCanvas(KisCanvas2 *canvas)
{
if (!canvas) return;
QObject *canvasWidget = canvas->canvasWidget();
if (!canvasResolver.contains(canvasWidget)) {
canvasResolver.insert(canvasWidget, canvas);
d->q->setupAsEventFilter(canvasWidget);
canvasWidget->installEventFilter(this);
setupFocusThreshold(canvasWidget);
focusSwitchThreshold.setEnabled(false);
d->canvas = canvas;
d->toolProxy = qobject_cast<KisToolProxy*>(canvas->toolProxy());
} else {
KIS_ASSERT_RECOVER_RETURN(d->canvas == canvas);
}
}
void KisInputManager::Private::CanvasSwitcher::removeCanvas(KisCanvas2 *canvas)
{
QObject *widget = canvas->canvasWidget();
canvasResolver.remove(widget);
if (d->eventsReceiver == widget) {
d->q->setupAsEventFilter(0);
}
widget->removeEventFilter(this);
}
bool isInputWidget(QWidget *w)
{
if (!w) return false;
QList<QLatin1String> types;
types << QLatin1String("QAbstractSlider");
types << QLatin1String("QAbstractSpinBox");
types << QLatin1String("QLineEdit");
types << QLatin1String("QTextEdit");
types << QLatin1String("QPlainTextEdit");
types << QLatin1String("QComboBox");
types << QLatin1String("QKeySequenceEdit");
Q_FOREACH (const QLatin1String &type, types) {
if (w->inherits(type.data())) {
return true;
}
}
return false;
}
bool KisInputManager::Private::CanvasSwitcher::eventFilter(QObject* object, QEvent* event )
{
if (canvasResolver.contains(object)) {
switch (event->type()) {
case QEvent::FocusIn: {
QFocusEvent *fevent = static_cast<QFocusEvent*>(event);
KisCanvas2 *canvas = canvasResolver.value(object);
// only relevant canvases from the same main window should be
// registered in the switcher
KIS_SAFE_ASSERT_RECOVER_BREAK(canvas);
if (canvas != d->canvas) {
eatOneMouseStroke = 2 * (fevent->reason() == Qt::MouseFocusReason);
}
d->canvas = canvas;
d->toolProxy = qobject_cast<KisToolProxy*>(canvas->toolProxy());
d->q->setupAsEventFilter(object);
object->removeEventFilter(this);
object->installEventFilter(this);
setupFocusThreshold(object);
focusSwitchThreshold.setEnabled(false);
const QPoint globalPos = QCursor::pos();
const QPoint localPos = d->canvas->canvasWidget()->mapFromGlobal(globalPos);
QWidget *canvasWindow = d->canvas->canvasWidget()->window();
const QPoint windowsPos = canvasWindow ? canvasWindow->mapFromGlobal(globalPos) : localPos;
QEnterEvent event(localPos, windowsPos, globalPos);
d->q->eventFilter(object, &event);
break;
}
case QEvent::FocusOut: {
focusSwitchThreshold.setEnabled(true);
break;
}
case QEvent::Enter: {
break;
}
case QEvent::Leave: {
focusSwitchThreshold.stop();
break;
}
case QEvent::Wheel: {
QWidget *widget = static_cast<QWidget*>(object);
widget->setFocus();
break;
}
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::TabletPress:
case QEvent::TabletRelease:
focusSwitchThreshold.forceDone();
if (eatOneMouseStroke) {
eatOneMouseStroke--;
return true;
}
break;
case QEvent::MouseButtonDblClick:
focusSwitchThreshold.forceDone();
if (eatOneMouseStroke) {
return true;
}
break;
case QEvent::MouseMove:
case QEvent::TabletMove: {
QWidget *widget = static_cast<QWidget*>(object);
if (!widget->hasFocus()) {
const int delay =
isInputWidget(QApplication::focusWidget()) ?
InputWidgetsThreshold : OtherWidgetsThreshold;
focusSwitchThreshold.setDelayThreshold(delay);
focusSwitchThreshold.start();
}
}
break;
default:
break;
}
}
return QObject::eventFilter(object, event);
}
KisInputManager::Private::ProximityNotifier::ProximityNotifier(KisInputManager::Private *_d, QObject *p)
: QObject(p), d(_d)
{}
bool KisInputManager::Private::ProximityNotifier::eventFilter(QObject* object, QEvent* event )
{
/**
* All Qt builds in range 5.7.0...5.11.X on X11 had a problem that made all
* the tablet events be accepted by default. It meant that no mouse
* events were synthesized, and, therefore, no Enter/Leave were generated.
*
* The fix for this bug has been added only in Qt 5.12.0:
* https://codereview.qt-project.org/#/c/239918/
*
* To avoid this problem we should explicitly ignore all the tablet events.
*/
#if defined Q_OS_LINUX && \
QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) && \
QT_VERSION < QT_VERSION_CHECK(5, 12, 0)
if (event->type() == QEvent::TabletMove ||
event->type() == QEvent::TabletPress ||
event->type() == QEvent::TabletRelease) {
event->ignore();
}
#endif
switch (event->type()) {
case QEvent::TabletEnterProximity:
d->debugEvent<QEvent, false>(event);
// Tablet proximity events are unreliable AND fake mouse events do not
// necessarily come after tablet events, so this is insufficient.
// d->eventEater.eatOneMousePress();
// Qt sends fake mouse events instead of hover events, so not very useful.
// Don't block mouse events on tablet since tablet move events are not generated until
// after tablet press.
#ifndef Q_OS_MACOS
d->blockMouseEvents();
#endif
break;
case QEvent::TabletLeaveProximity:
d->debugEvent<QEvent, false>(event);
d->allowMouseEvents();
break;
default:
break;
}
return QObject::eventFilter(object, event);
}
void KisInputManager::Private::addStrokeShortcut(KisAbstractInputAction* action, int index,
const QList<Qt::Key> &modifiers,
Qt::MouseButtons buttons)
{
KisStrokeShortcut *strokeShortcut =
new KisStrokeShortcut(action, index);
QList<Qt::MouseButton> buttonList;
if(buttons & Qt::LeftButton) {
buttonList << Qt::LeftButton;
}
if(buttons & Qt::RightButton) {
buttonList << Qt::RightButton;
}
if(buttons & Qt::MidButton) {
buttonList << Qt::MidButton;
}
if(buttons & Qt::XButton1) {
buttonList << Qt::XButton1;
}
if(buttons & Qt::XButton2) {
buttonList << Qt::XButton2;
}
if (buttonList.size() > 0) {
- strokeShortcut->setButtons(QSet<Qt::Key>::fromList(modifiers), QSet<Qt::MouseButton>::fromList(buttonList));
+ strokeShortcut->setButtons(QSet<Qt::Key>(modifiers.begin(), modifiers.end()), QSet<Qt::MouseButton>(buttonList.begin(), buttonList.end()));
matcher.addShortcut(strokeShortcut);
}
else {
delete strokeShortcut;
}
}
void KisInputManager::Private::addKeyShortcut(KisAbstractInputAction* action, int index,
const QList<Qt::Key> &keys)
{
if (keys.size() == 0) return;
KisSingleActionShortcut *keyShortcut =
new KisSingleActionShortcut(action, index);
//Note: Ordering is important here, Shift + V is different from V + Shift,
//which is the reason we use the last key here since most users will enter
//shortcuts as "Shift + V". Ideally this should not happen, but this is
//the way the shortcut matcher is currently implemented.
QList<Qt::Key> allKeys = keys;
Qt::Key key = allKeys.takeLast();
- QSet<Qt::Key> modifiers = QSet<Qt::Key>::fromList(allKeys);
+ QSet<Qt::Key> modifiers = QSet<Qt::Key>(allKeys.begin(), allKeys.end());
keyShortcut->setKey(modifiers, key);
matcher.addShortcut(keyShortcut);
}
void KisInputManager::Private::addWheelShortcut(KisAbstractInputAction* action, int index,
const QList<Qt::Key> &modifiers,
KisShortcutConfiguration::MouseWheelMovement wheelAction)
{
QScopedPointer<KisSingleActionShortcut> keyShortcut(
new KisSingleActionShortcut(action, index));
KisSingleActionShortcut::WheelAction a;
switch(wheelAction) {
case KisShortcutConfiguration::WheelUp:
a = KisSingleActionShortcut::WheelUp;
break;
case KisShortcutConfiguration::WheelDown:
a = KisSingleActionShortcut::WheelDown;
break;
case KisShortcutConfiguration::WheelLeft:
a = KisSingleActionShortcut::WheelLeft;
break;
case KisShortcutConfiguration::WheelRight:
a = KisSingleActionShortcut::WheelRight;
break;
case KisShortcutConfiguration::WheelTrackpad:
a = KisSingleActionShortcut::WheelTrackpad;
break;
default:
return;
}
- keyShortcut->setWheel(QSet<Qt::Key>::fromList(modifiers), a);
+ keyShortcut->setWheel(QSet<Qt::Key>(modifiers.begin(), modifiers.end()), a);
matcher.addShortcut(keyShortcut.take());
}
void KisInputManager::Private::addTouchShortcut(KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture)
{
KisTouchShortcut *shortcut = new KisTouchShortcut(action, index, gesture);
dbgKrita << "TouchAction:" << action->name();
switch(gesture) {
case KisShortcutConfiguration::RotateGesture:
case KisShortcutConfiguration::PinchGesture:
#ifndef Q_OS_MACOS
case KisShortcutConfiguration::ZoomAndRotateGesture:
#endif
shortcut->setMinimumTouchPoints(2);
shortcut->setMaximumTouchPoints(2);
break;
case KisShortcutConfiguration::PanGesture:
shortcut->setMinimumTouchPoints(3);
shortcut->setMaximumTouchPoints(10);
break;
default:
break;
}
matcher.addShortcut(shortcut);
}
bool KisInputManager::Private::addNativeGestureShortcut(KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture)
{
// Qt5 only implements QNativeGestureEvent for macOS
Qt::NativeGestureType type;
switch (gesture) {
#ifdef Q_OS_MACOS
case KisShortcutConfiguration::PinchGesture:
type = Qt::ZoomNativeGesture;
break;
case KisShortcutConfiguration::PanGesture:
type = Qt::PanNativeGesture;
break;
case KisShortcutConfiguration::RotateGesture:
type = Qt::RotateNativeGesture;
break;
case KisShortcutConfiguration::SmartZoomGesture:
type = Qt::SmartZoomNativeGesture;
break;
#endif
default:
return false;
}
KisNativeGestureShortcut *shortcut = new KisNativeGestureShortcut(action, index, type);
matcher.addShortcut(shortcut);
return true;
}
void KisInputManager::Private::setupActions()
{
QList<KisAbstractInputAction*> actions = KisInputProfileManager::instance()->actions();
Q_FOREACH (KisAbstractInputAction *action, actions) {
KisToolInvocationAction *toolAction =
dynamic_cast<KisToolInvocationAction*>(action);
if(toolAction) {
defaultInputAction = toolAction;
}
}
connect(KisInputProfileManager::instance(), SIGNAL(currentProfileChanged()), q, SLOT(profileChanged()));
if(KisInputProfileManager::instance()->currentProfile()) {
q->profileChanged();
}
}
bool KisInputManager::Private::processUnhandledEvent(QEvent *event)
{
bool retval = false;
if (forwardAllEventsToTool ||
event->type() == QEvent::KeyPress ||
event->type() == QEvent::KeyRelease) {
defaultInputAction->processUnhandledEvent(event);
retval = true;
}
return retval && !forwardAllEventsToTool;
}
bool KisInputManager::Private::tryHidePopupPalette()
{
if (canvas->isPopupPaletteVisible()) {
canvas->slotShowPopupPalette();
return true;
}
return false;
}
#ifdef HAVE_X11
inline QPointF dividePoints(const QPointF &pt1, const QPointF &pt2) {
return QPointF(pt1.x() / pt2.x(), pt1.y() / pt2.y());
}
inline QPointF multiplyPoints(const QPointF &pt1, const QPointF &pt2) {
return QPointF(pt1.x() * pt2.x(), pt1.y() * pt2.y());
}
#endif
void KisInputManager::Private::blockMouseEvents()
{
eventEater.activate();
}
void KisInputManager::Private::allowMouseEvents()
{
eventEater.deactivate();
}
void KisInputManager::Private::eatOneMousePress()
{
eventEater.eatOneMousePress();
}
void KisInputManager::Private::resetCompressor() {
compressedMoveEvent.reset();
moveEventCompressor.stop();
}
bool KisInputManager::Private::handleCompressedTabletEvent(QEvent *event)
{
bool retval = false;
/**
* When Krita (as an application) has no input focus, we cannot
* handle key events. But at the same time, when the user hovers
* Krita canvas, we should still show him the correct cursor.
*
* So here we just add a simple workaround to resync shortcut
* matcher's state at least against the basic modifiers, like
* Shift, Control and Alt.
*/
QWidget *recievingWidget = dynamic_cast<QWidget*>(eventsReceiver);
if (recievingWidget && !recievingWidget->hasFocus()) {
QVector<Qt::Key> guessedKeys;
KisExtendedModifiersMapper mapper;
Qt::KeyboardModifiers modifiers = mapper.queryStandardModifiers();
Q_FOREACH (Qt::Key key, mapper.queryExtendedModifiers()) {
QKeyEvent kevent(QEvent::ShortcutOverride, key, modifiers);
guessedKeys << KisExtendedModifiersMapper::workaroundShiftAltMetaHell(&kevent);
}
matcher.recoveryModifiersWithoutFocus(guessedKeys);
}
if (!matcher.pointerMoved(event) && toolProxy) {
toolProxy->forwardHoverEvent(event);
}
retval = true;
event->setAccepted(true);
return retval;
}
qint64 KisInputManager::Private::TabletLatencyTracker::currentTimestamp() const
{
// on OS X, we need to compute the timestamp that compares correctly against the native event timestamp,
// which seems to be the msecs since system startup. On Linux with WinTab, we produce the timestamp that
// we compare against ourselves in QWindowSystemInterface.
QElapsedTimer elapsed;
elapsed.start();
return elapsed.msecsSinceReference();
}
void KisInputManager::Private::TabletLatencyTracker::print(const QString &message)
{
dbgTablet << qUtf8Printable(message);
}
diff --git a/libs/ui/kis_action.cpp b/libs/ui/kis_action.cpp
index 48f2854a68..9ac973f8e8 100644
--- a/libs/ui/kis_action.cpp
+++ b/libs/ui/kis_action.cpp
@@ -1,147 +1,147 @@
/*
* 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 "kis_action.h"
#include "kis_action_manager.h"
#include <QEvent>
class Q_DECL_HIDDEN KisAction::Private {
public:
Private() : flags(NONE), conditions(NO_CONDITION), actionManager(0) {}
ActivationFlags flags;
ActivationConditions conditions;
QStringList excludedNodeTypes;
QString operationID;
KisActionManager* actionManager;
};
KisAction::KisAction(QObject* parent)
: QWidgetAction(parent)
, d(new Private)
{
connect(this, SIGNAL(changed()), SLOT(slotChanged()));
}
KisAction::KisAction(const QString& text, QObject* parent)
: QWidgetAction(parent)
, d(new KisAction::Private)
{
QAction::setText(text);
connect(this, SIGNAL(changed()), SLOT(slotChanged()));
}
KisAction::KisAction(const QIcon &icon, const QString& text, QObject* parent)
: QWidgetAction(parent)
, d(new Private)
{
QAction::setIcon(icon);
QAction::setText(text);
connect(this, SIGNAL(changed()), SLOT(slotChanged()));
}
KisAction::~KisAction()
{
delete d;
}
// Using a dynamic QObject property is done for compatibility with KAction and
// XmlGui. We may merge KisAction into the XmlGui code to make this unnecessary,
// but that is probably a lot of work for little benefit. We currently store a
// single default shortcut, but the old system used a list (to store default
// primary/alternate shortcuts for local and global settings) so we marshal it
// for compatibility.
void KisAction::setDefaultShortcut(const QKeySequence &shortcut)
{
QList<QKeySequence> listifiedShortcut;
// Use the empty list to represent no shortcut
if (shortcut != QKeySequence("")) {
listifiedShortcut.append(shortcut);
}
- setProperty("defaultShortcuts", qVariantFromValue(listifiedShortcut));
+ setProperty("defaultShortcuts", QVariant::fromValue(listifiedShortcut));
}
QKeySequence KisAction::defaultShortcut() const
{
auto listifiedShortcut = property("defaultShortcuts").value<QList<QKeySequence> >();
if (listifiedShortcut.isEmpty()) {
return QKeySequence();
} else {
return listifiedShortcut.first();
}
}
void KisAction::setActivationFlags(KisAction::ActivationFlags flags)
{
d->flags = flags;
}
KisAction::ActivationFlags KisAction::activationFlags()
{
return d->flags;
}
void KisAction::setActivationConditions(KisAction::ActivationConditions conditions)
{
d->conditions = conditions;
}
KisAction::ActivationConditions KisAction::activationConditions()
{
return d->conditions;
}
void KisAction::setExcludedNodeTypes(const QStringList &nodeTypes)
{
d->excludedNodeTypes = nodeTypes;
}
const QStringList& KisAction::excludedNodeTypes() const
{
return d->excludedNodeTypes;
}
void KisAction::setActionEnabled(bool enabled)
{
setEnabled(enabled);
}
void KisAction::setActionManager(KisActionManager* actionManager)
{
d->actionManager = actionManager;
}
void KisAction::setOperationID(const QString& id)
{
d->operationID = id;
connect(this, SIGNAL(triggered()), this, SLOT(slotTriggered()));
}
void KisAction::slotTriggered()
{
if (d->actionManager && !d->operationID.isEmpty()) {
d->actionManager->runOperation(d->operationID);
}
}
void KisAction::slotChanged()
{
emit sigEnableSlaves(isEnabled());
}
diff --git a/libs/ui/kis_action_manager.cpp b/libs/ui/kis_action_manager.cpp
index 16709928c4..723a42bd22 100644
--- a/libs/ui/kis_action_manager.cpp
+++ b/libs/ui/kis_action_manager.cpp
@@ -1,499 +1,500 @@
/*
* 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 "kis_action_manager.h"
#include <QList>
#include <kactioncollection.h>
#include <kis_icon.h>
#include "KisPart.h"
#include "kis_action.h"
#include "KisViewManager.h"
#include "kis_selection_manager.h"
#include "operations/kis_operation_ui_factory.h"
#include "operations/kis_operation_registry.h"
#include "operations/kis_operation.h"
#include "kis_layer.h"
#include "KisDocument.h"
#include "kis_clipboard.h"
#include <kis_image_animation_interface.h>
#include "kis_config.h"
#include <QMenu>
#include "QFile"
#include <QDomDocument>
#include <QDomElement>
class Q_DECL_HIDDEN KisActionManager::Private {
public:
Private()
: viewManager(0)
{}
~Private()
{
qDeleteAll(uiRegistry.values());
}
KisViewManager* viewManager;
KActionCollection *actionCollection;
QList<QPointer<KisAction>> actions;
KoGenericRegistry<KisOperationUIFactory*> uiRegistry;
KisOperationRegistry operationRegistry;
};
KisActionManager::KisActionManager(KisViewManager* viewManager, KActionCollection *actionCollection)
: d(new Private)
{
d->viewManager = viewManager;
d->actionCollection = actionCollection;
connect(d->actionCollection,
SIGNAL(inserted(QAction*)), SLOT(slotActionAddedToCollection(QAction*)));
}
KisActionManager::~KisActionManager()
{
#if 0
if ((d->actions.size() > 0)) {
QDomDocument doc;
QDomElement e = doc.createElement("Actions");
e.setAttribute("version", "2");
doc.appendChild(e);
Q_FOREACH (KisAction *action, d->actions) {
QDomElement a = doc.createElement("Action");
a.setAttribute("name", action->objectName());
// But seriously, XML is the worst format ever designed
auto addElement = [&](QString title, QString content) {
QDomElement newNode = doc.createElement(title);
QDomText newText = doc.createTextNode(content);
newNode.appendChild(newText);
a.appendChild(newNode);
};
addElement("icon", action->icon().name());
addElement("text", action->text());
addElement("whatsThis" , action->whatsThis());
addElement("toolTip" , action->toolTip());
addElement("iconText" , action->iconText());
addElement("shortcut" , action->shortcut().toString());
addElement("activationFlags" , QString::number(action->activationFlags(),2));
addElement("activationConditions" , QString::number(action->activationConditions(),2));
addElement("defaultShortcut" , action->defaultShortcut().toString());
addElement("isCheckable" , QString((action->isChecked() ? "true" : "false")));
addElement("statusTip", action->statusTip());
e.appendChild(a);
}
QFile f("ActionManager.action");
f.open(QFile::WriteOnly);
f.write(doc.toString().toUtf8());
f.close();
}
#endif
delete d;
}
void KisActionManager::setView(QPointer<KisView> imageView)
{
Q_UNUSED(imageView);
}
void KisActionManager::slotActionAddedToCollection(QAction *action)
{
/**
* Small hack alert: not all the actions are still created by the manager and
* immediately added to the action collection. Some plugins add actions
* directly to the action collection when a document is created. Here we
* catch these cases
*/
KisActionRegistry::instance()->updateShortcut(action->objectName(), action);
}
void KisActionManager::addAction(const QString& name, KisAction* action)
{
Q_ASSERT(!name.isEmpty());
Q_ASSERT(action);
Q_ASSERT(d->viewManager);
Q_ASSERT(d->actionCollection);
d->actionCollection->addAction(name, action);
action->setParent(d->actionCollection);
d->actions.append(action);
action->setActionManager(this);
}
void KisActionManager::takeAction(KisAction* action)
{
d->actions.removeOne(action);
if (!action->objectName().isEmpty()) {
KIS_ASSERT_RECOVER_RETURN(d->actionCollection);
d->actionCollection->takeAction(action);
}
}
KisAction *KisActionManager::actionByName(const QString &name) const
{
Q_FOREACH (KisAction *action, d->actions) {
if (action->objectName() == name) {
return action;
}
}
return 0;
}
KisAction *KisActionManager::createAction(const QString &name)
{
KisAction *a = actionByName(name); // Check if the action already exists
if (a) {
+ dbgAction << name << "already exists";
return a;
}
// There is some tension here. KisActionManager is supposed to be in control
// of global actions, but these actions are supposed to be duplicated. We
// will add them to the KisActionRegistry for the time being so we can get
// properly categorized shortcuts.
a = new KisAction();
KisActionRegistry *actionRegistry = KisActionRegistry::instance();
// Add extra properties
actionRegistry->propertizeAction(name, a);
bool ok; // We will skip this check
int activationFlags = actionRegistry->getActionProperty(name, "activationFlags").toInt(&ok, 2);
int activationConditions = actionRegistry->getActionProperty(name, "activationConditions").toInt(&ok, 2);
a->setActivationFlags((KisAction::ActivationFlags) activationFlags);
a->setActivationConditions((KisAction::ActivationConditions) activationConditions);
addAction(name, a);
return a;
}
void KisActionManager::updateGUI()
{
//TODO other flags
KisAction::ActivationFlags flags;
KisImageWSP image;
KisNodeSP node;
KisLayerSP layer;
KisSelectionManager *selectionManager = 0;
KisAction::ActivationConditions conditions = KisAction::NO_CONDITION;
if (d->viewManager) {
node = d->viewManager->activeNode();
selectionManager = d->viewManager->selectionManager();
// if there are no views, that means no document is open.
// we cannot have nodes (selections), devices, or documents without a view
if ( d->viewManager->viewCount() > 0 )
{
image = d->viewManager->image();
flags |= KisAction::ACTIVE_IMAGE;
if (image && image->animationInterface()->hasAnimation()) {
flags |= KisAction::IMAGE_HAS_ANIMATION;
}
if (d->viewManager->viewCount() > 1) {
flags |= KisAction::MULTIPLE_IMAGES;
}
if (d->viewManager->document() && d->viewManager->document()->isModified()) {
flags |= KisAction::CURRENT_IMAGE_MODIFIED;
}
if (d->viewManager->activeDevice()) {
flags |= KisAction::ACTIVE_DEVICE;
}
}
if (d->viewManager->selectionEditable()) {
conditions |= KisAction::SELECTION_EDITABLE;
}
}
// is there a selection/mask?
// you have to have at least one view (document) open for this to be true
if (node) {
// if a node exists, we know there is an active layer as well
flags |= KisAction::ACTIVE_NODE;
layer = qobject_cast<KisLayer*>(node.data());
if (layer) {
flags |= KisAction::ACTIVE_LAYER;
}
if (node->inherits("KisTransparencyMask")) {
flags |= KisAction::ACTIVE_TRANSPARENCY_MASK;
}
if (layer && layer->inherits("KisShapeLayer")) {
flags |= KisAction::ACTIVE_SHAPE_LAYER;
}
if (KisClipboard::instance()->hasLayers()) {
flags |= KisAction::LAYERS_IN_CLIPBOARD;
}
if (selectionManager) {
if (selectionManager->havePixelsSelected()) {
flags |= KisAction::PIXELS_SELECTED;
}
if (selectionManager->haveShapesSelected()) {
flags |= KisAction::SHAPES_SELECTED;
}
if (selectionManager->haveAnySelectionWithPixels()) {
flags |= KisAction::ANY_SELECTION_WITH_PIXELS;
}
if (selectionManager->haveShapeSelectionWithShapes()) {
flags |= KisAction::SHAPE_SELECTION_WITH_SHAPES;
}
if (selectionManager->haveRasterSelectionWithPixels()) {
flags |= KisAction::PIXEL_SELECTION_WITH_PIXELS;
}
if (selectionManager->havePixelsInClipboard()) {
flags |= KisAction::PIXELS_IN_CLIPBOARD;
}
if (selectionManager->haveShapesInClipboard()) {
flags |= KisAction::SHAPES_IN_CLIPBOARD;
}
}
if (node->isEditable(false)) {
conditions |= KisAction::ACTIVE_NODE_EDITABLE;
}
if (node->hasEditablePaintDevice()) {
conditions |= KisAction::ACTIVE_NODE_EDITABLE_PAINT_DEVICE;
}
}
KisConfig cfg(true);
if (cfg.useOpenGL()) {
conditions |= KisAction::OPENGL_ENABLED;
}
// loop through all actions in action manager and determine what should be enabled
Q_FOREACH (QPointer<KisAction> action, d->actions) {
bool enable;
if (action) {
if (action->activationFlags() == KisAction::NONE) {
enable = true;
}
else {
enable = action->activationFlags() & flags; // combine action flags with updateGUI flags
}
enable = enable && (int)(action->activationConditions() & conditions) == (int)action->activationConditions();
if (node && enable) {
Q_FOREACH (const QString &type, action->excludedNodeTypes()) {
if (node->inherits(type.toLatin1())) {
enable = false;
break;
}
}
}
action->setActionEnabled(enable);
}
}
}
KisAction *KisActionManager::createStandardAction(KStandardAction::StandardAction actionType, const QObject *receiver, const char *member)
{
QAction *standardAction = KStandardAction::create(actionType, receiver, member, 0);
KisAction *action = new KisAction(standardAction->icon(), standardAction->text());
const QList<QKeySequence> defaultShortcuts = standardAction->property("defaultShortcuts").value<QList<QKeySequence> >();
const QKeySequence defaultShortcut = defaultShortcuts.isEmpty() ? QKeySequence() : defaultShortcuts.at(0);
action->setDefaultShortcut(standardAction->shortcut());
#ifdef Q_OS_WIN
if (actionType == KStandardAction::SaveAs && defaultShortcuts.isEmpty()) {
action->setShortcut(QKeySequence("CTRL+SHIFT+S"));
}
#endif
action->setCheckable(standardAction->isCheckable());
if (action->isCheckable()) {
action->setChecked(standardAction->isChecked());
}
action->setMenuRole(standardAction->menuRole());
action->setText(standardAction->text());
action->setToolTip(standardAction->toolTip());
if (receiver && member) {
if (actionType == KStandardAction::OpenRecent) {
QObject::connect(action, SIGNAL(urlSelected(QUrl)), receiver, member);
}
else if (actionType == KStandardAction::ConfigureToolbars) {
QObject::connect(action, SIGNAL(triggered(bool)), receiver, member, Qt::QueuedConnection);
}
else {
QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
}
}
KisActionRegistry *actionRegistry = KisActionRegistry::instance();
actionRegistry->propertizeAction(standardAction->objectName(), action);
addAction(standardAction->objectName(), action);
delete standardAction;
return action;
}
void KisActionManager::safePopulateMenu(QMenu *menu, const QString &actionId, KisActionManager *actionManager)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(actionManager);
KisAction *action = actionManager->actionByName(actionId);
KIS_SAFE_ASSERT_RECOVER_RETURN(action);
menu->addAction(action);
}
void KisActionManager::registerOperationUIFactory(KisOperationUIFactory* factory)
{
d->uiRegistry.add(factory);
}
void KisActionManager::registerOperation(KisOperation* operation)
{
d->operationRegistry.add(operation);
}
void KisActionManager::runOperation(const QString& id)
{
KisOperationConfigurationSP config = new KisOperationConfiguration(id);
KisOperationUIFactory* uiFactory = d->uiRegistry.get(id);
if (uiFactory) {
bool gotConfig = uiFactory->fetchConfiguration(d->viewManager, config);
if (!gotConfig) {
return;
}
}
runOperationFromConfiguration(config);
}
void KisActionManager::runOperationFromConfiguration(KisOperationConfigurationSP config)
{
KisOperation* operation = d->operationRegistry.get(config->id());
Q_ASSERT(operation);
operation->runFromXML(d->viewManager, *config);
}
void KisActionManager::dumpActionFlags()
{
QFile data("actions.txt");
if (data.open(QFile::WriteOnly | QFile::Truncate)) {
QTextStream out(&data);
out.setCodec("UTF-8");
Q_FOREACH (KisAction* action, d->actions) {
KisAction::ActivationFlags flags = action->activationFlags();
out << "-------- " << action->text() << " --------\n";
out << "Action will activate on: \n";
if (flags & KisAction::ACTIVE_IMAGE) {
out << " Active image\n";
}
if (flags & KisAction::MULTIPLE_IMAGES) {
out << " More than one image open\n";
}
if (flags & KisAction::CURRENT_IMAGE_MODIFIED) {
out << " Active image modified\n";
}
if (flags & KisAction::ACTIVE_DEVICE) {
out << " Active device\n";
}
if (flags & KisAction::ACTIVE_LAYER) {
out << " Active layer\n";
}
if (flags & KisAction::ACTIVE_TRANSPARENCY_MASK) {
out << " Active transparency mask\n";
}
if (flags & KisAction::ACTIVE_NODE) {
out << " Active node\n";
}
if (flags & KisAction::ACTIVE_SHAPE_LAYER) {
out << " Active shape layer\n";
}
if (flags & KisAction::PIXELS_SELECTED) {
out << " Pixels selected\n";
}
if (flags & KisAction::SHAPES_SELECTED) {
out << " Shapes selected\n";
}
if (flags & KisAction::ANY_SELECTION_WITH_PIXELS) {
out << " Any selection with pixels\n";
}
if (flags & KisAction::PIXELS_IN_CLIPBOARD) {
out << " Pixels in clipboard\n";
}
if (flags & KisAction::SHAPES_IN_CLIPBOARD) {
out << " Shape in clipboard\n";
}
if (flags & KisAction::IMAGE_HAS_ANIMATION) {
out << " Image has animation\n";
}
out << "\n\n";
out << "Action will only activate if the following conditions are met: \n";
KisAction::ActivationConditions conditions = action->activationConditions();
if ((int)conditions == 0) {
out << " -\n";
}
if (conditions & KisAction::ACTIVE_NODE_EDITABLE) {
out << " Active Node editable\n";
}
if (conditions & KisAction::ACTIVE_NODE_EDITABLE_PAINT_DEVICE) {
out << " Active Node has editable paint device\n";
}
if (conditions & KisAction::SELECTION_EDITABLE) {
out << " Selection is editable\n";
}
if (conditions & KisAction::OPENGL_ENABLED) {
out << " OpenGL is enabled\n";
}
out << "\n\n";
}
}
}
diff --git a/libs/ui/kis_asl_layer_style_serializer.cpp b/libs/ui/kis_asl_layer_style_serializer.cpp
deleted file mode 100644
index 981526a8d0..0000000000
--- a/libs/ui/kis_asl_layer_style_serializer.cpp
+++ /dev/null
@@ -1,1235 +0,0 @@
-/*
- * 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_asl_layer_style_serializer.h"
-
-#include <QDomDocument>
-
-#include <KoResourceServerProvider.h>
-#include <resources/KoAbstractGradient.h>
-#include <resources/KoSegmentGradient.h>
-#include <resources/KoStopGradient.h>
-#include <resources/KoPattern.h>
-
-#include "kis_dom_utils.h"
-
-
-#include "psd.h"
-#include "kis_global.h"
-
-#include "asl/kis_asl_reader.h"
-#include "asl/kis_asl_xml_parser.h"
-#include "asl/kis_asl_writer_utils.h"
-
-#include "asl/kis_asl_xml_writer.h"
-#include "asl/kis_asl_writer.h"
-
-#include <functional>
-using namespace std::placeholders;
-
-KisAslLayerStyleSerializer::KisAslLayerStyleSerializer()
-{
-}
-
-KisAslLayerStyleSerializer::~KisAslLayerStyleSerializer()
-{
-}
-
-QVector<KisPSDLayerStyleSP> KisAslLayerStyleSerializer::styles() const
-{
- return m_stylesVector;
-}
-
-void KisAslLayerStyleSerializer::setStyles(const QVector<KisPSDLayerStyleSP> &styles)
-{
- m_stylesVector = styles;
-}
-
-QString compositeOpToBlendMode(const QString &compositeOp)
-{
- QString mode = "Nrml";
-
- if (compositeOp == COMPOSITE_OVER) {
- mode = "Nrml";
- } else if (compositeOp == COMPOSITE_DISSOLVE) {
- mode = "Dslv";
- } else if (compositeOp == COMPOSITE_DARKEN) {
- mode = "Drkn";
- } else if (compositeOp == COMPOSITE_MULT) {
- mode = "Mltp";
- } else if (compositeOp == COMPOSITE_BURN) {
- mode = "CBrn";
- } else if (compositeOp == COMPOSITE_LINEAR_BURN) {
- mode = "linearBurn";
- } else if (compositeOp == COMPOSITE_DARKER_COLOR) {
- mode = "darkerColor";
- } else if (compositeOp == COMPOSITE_LIGHTEN) {
- mode = "Lghn";
- } else if (compositeOp == COMPOSITE_SCREEN) {
- mode = "Scrn";
- } else if (compositeOp == COMPOSITE_DODGE) {
- mode = "CDdg";
- } else if (compositeOp == COMPOSITE_LINEAR_DODGE) {
- mode = "linearDodge";
- } else if (compositeOp == COMPOSITE_LIGHTER_COLOR) {
- mode = "lighterColor";
- } else if (compositeOp == COMPOSITE_OVERLAY) {
- mode = "Ovrl";
- } else if (compositeOp == COMPOSITE_SOFT_LIGHT_PHOTOSHOP) {
- mode = "SftL";
- } else if (compositeOp == COMPOSITE_HARD_LIGHT) {
- mode = "HrdL";
- } else if (compositeOp == COMPOSITE_VIVID_LIGHT) {
- mode = "vividLight";
- } else if (compositeOp == COMPOSITE_LINEAR_LIGHT) {
- mode = "linearLight";
- } else if (compositeOp == COMPOSITE_PIN_LIGHT) {
- mode = "pinLight";
- } else if (compositeOp == COMPOSITE_HARD_MIX_PHOTOSHOP) {
- mode = "hardMix";
- } else if (compositeOp == COMPOSITE_DIFF) {
- mode = "Dfrn";
- } else if (compositeOp == COMPOSITE_EXCLUSION) {
- mode = "Xclu";
- } else if (compositeOp == COMPOSITE_SUBTRACT) {
- mode = "Sbtr";
- } else if (compositeOp == COMPOSITE_DIVIDE) {
- mode = "divide";
- } else if (compositeOp == COMPOSITE_HUE) {
- mode = "H ";
- } else if (compositeOp == COMPOSITE_SATURATION) {
- mode = "Strt";
- } else if (compositeOp == COMPOSITE_COLOR) {
- mode = "Clr ";
- } else if (compositeOp == COMPOSITE_LUMINIZE) {
- mode = "Lmns";
- } else {
- dbgKrita << "Unknown composite op:" << mode << "Returning \"Nrml\"!";
- }
-
- return mode;
-}
-
-QString techniqueToString(psd_technique_type technique, const QString &typeId)
-{
- QString result = "SfBL";
-
- switch (technique) {
- case psd_technique_softer:
- result = "SfBL";
- break;
- case psd_technique_precise:
- result = "PrBL";
- break;
- case psd_technique_slope_limit:
- result = "Slmt";
- break;
- }
-
- if (typeId == "BETE" && technique == psd_technique_slope_limit) {
- warnKrita << "WARNING: techniqueToString: invalid technique type!" << ppVar(technique) << ppVar(typeId);
- }
-
- return result;
-}
-
-QString bevelStyleToString(psd_bevel_style style)
-{
- QString result = "OtrB";
-
- switch (style) {
- case psd_bevel_outer_bevel:
- result = "OtrB";
- break;
- case psd_bevel_inner_bevel:
- result = "InrB";
- break;
- case psd_bevel_emboss:
- result = "Embs";
- break;
- case psd_bevel_pillow_emboss:
- result = "PlEb";
- break;
- case psd_bevel_stroke_emboss:
- result = "strokeEmboss";
- break;
- }
-
- return result;
-}
-
-QString gradientTypeToString(psd_gradient_style style)
-{
- QString result = "Lnr ";
-
- switch (style) {
- case psd_gradient_style_linear:
- result = "Lnr ";
- break;
- case psd_gradient_style_radial:
- result = "Rdl ";
- break;
- case psd_gradient_style_angle:
- result = "Angl";
- break;
- case psd_gradient_style_reflected:
- result = "Rflc";
- break;
- case psd_gradient_style_diamond:
- result = "Dmnd";
- break;
- }
-
- return result;
-}
-
-QString strokePositionToString(psd_stroke_position position)
-{
- QString result = "OutF";
-
- switch (position) {
- case psd_stroke_outside:
- result = "OutF";
- break;
- case psd_stroke_inside:
- result = "InsF";
- break;
- case psd_stroke_center:
- result = "CtrF";
- break;
- }
-
- return result;
-}
-
-QString strokeFillTypeToString(psd_fill_type position)
-{
- QString result = "SClr";
-
- switch (position) {
- case psd_fill_solid_color:
- result = "SClr";
- break;
- case psd_fill_gradient:
- result = "GrFl";
- break;
- case psd_fill_pattern:
- result = "Ptrn";
- break;
- }
-
- return result;
-}
-
-QVector<KoPattern*> KisAslLayerStyleSerializer::fetchAllPatterns(KisPSDLayerStyle *style) const
-{
- QVector <KoPattern*> allPatterns;
-
- if (style->patternOverlay()->effectEnabled()) {
- allPatterns << style->patternOverlay()->pattern();
- }
-
- if (style->stroke()->effectEnabled() &&
- style->stroke()->fillType() == psd_fill_pattern) {
-
- allPatterns << style->stroke()->pattern();
- }
-
- if(style->bevelAndEmboss()->effectEnabled() &&
- style->bevelAndEmboss()->textureEnabled()) {
-
- allPatterns << style->bevelAndEmboss()->texturePattern();
- }
-
- return allPatterns;
-}
-
-QString fetchPatternUuidSafe(KoPattern *pattern, QHash<KoPattern*, QString> patternToUuid)
-{
- if (patternToUuid.contains(pattern)) {
- return patternToUuid[pattern];
- } else {
- warnKrita << "WARNING: the pattern is not present in the Uuid map!";
- return "invalid-uuid";
- }
-}
-
-QDomDocument KisAslLayerStyleSerializer::formXmlDocument() const
-{
- KIS_ASSERT_RECOVER(!m_stylesVector.isEmpty()) { return QDomDocument(); }
-
- QVector<KoPattern*> allPatterns;
-
- Q_FOREACH (KisPSDLayerStyleSP style, m_stylesVector) {
- allPatterns += fetchAllPatterns(style.data());
- }
-
- QHash<KoPattern*, QString> patternToUuidMap;
-
- KisAslXmlWriter w;
-
- if (!allPatterns.isEmpty()) {
- w.enterList("Patterns");
-
- Q_FOREACH (KoPattern *pattern, allPatterns) {
- if (pattern) {
- if (!patternToUuidMap.contains(pattern)) {
- QString uuid = w.writePattern("", pattern);
- patternToUuidMap.insert(pattern, uuid);
- }
- } else {
- warnKrita << "WARNING: KisAslLayerStyleSerializer::saveToDevice: saved pattern is null!";
- }
- }
-
- w.leaveList();
- }
-
- Q_FOREACH (KisPSDLayerStyleSP style, m_stylesVector) {
-
- w.enterDescriptor("", "", "null");
- w.writeText("Nm ", style->name());
- w.writeText("Idnt", style->psdUuid());
- w.leaveDescriptor();
-
- w.enterDescriptor("", "", "Styl");
-
- w.enterDescriptor("documentMode", "", "documentMode");
- w.leaveDescriptor();
-
- w.enterDescriptor("Lefx", "", "Lefx");
-
- w.writeUnitFloat("Scl ", "#Prc", 100);
- w.writeBoolean("masterFXSwitch", style->isEnabled());
-
-
- // Drop Shadow
- const psd_layer_effects_drop_shadow *dropShadow = style->dropShadow();
- if (dropShadow->effectEnabled()) {
- w.enterDescriptor("DrSh", "", "DrSh");
-
- w.writeBoolean("enab", dropShadow->effectEnabled());
- w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(dropShadow->blendMode()));
- w.writeColor("Clr ", dropShadow->color());
-
- w.writeUnitFloat("Opct", "#Prc", dropShadow->opacity());
- w.writeBoolean("uglg", dropShadow->useGlobalLight());
- w.writeUnitFloat("lagl", "#Ang", dropShadow->angle());
- w.writeUnitFloat("Dstn", "#Pxl", dropShadow->distance());
- w.writeUnitFloat("Ckmt", "#Pxl", dropShadow->spread());
- w.writeUnitFloat("blur", "#Pxl", dropShadow->size());
- w.writeUnitFloat("Nose", "#Prc", dropShadow->noise());
-
- w.writeBoolean("AntA", dropShadow->antiAliased());
-
- // FIXME: save curves
- w.writeCurve("TrnS",
- "Linear",
- QVector<QPointF>() << QPointF() << QPointF(255, 255));
-
- w.writeBoolean("layerConceals", dropShadow->knocksOut());
- w.leaveDescriptor();
- }
-
- // Inner Shadow
- const psd_layer_effects_inner_shadow *innerShadow = style->innerShadow();
- if (innerShadow->effectEnabled()) {
- w.enterDescriptor("IrSh", "", "IrSh");
-
- w.writeBoolean("enab", innerShadow->effectEnabled());
- w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(innerShadow->blendMode()));
- w.writeColor("Clr ", innerShadow->color());
-
- w.writeUnitFloat("Opct", "#Prc", innerShadow->opacity());
- w.writeBoolean("uglg", innerShadow->useGlobalLight());
- w.writeUnitFloat("lagl", "#Ang", innerShadow->angle());
- w.writeUnitFloat("Dstn", "#Pxl", innerShadow->distance());
- w.writeUnitFloat("Ckmt", "#Pxl", innerShadow->spread());
- w.writeUnitFloat("blur", "#Pxl", innerShadow->size());
- w.writeUnitFloat("Nose", "#Prc", innerShadow->noise());
-
- w.writeBoolean("AntA", innerShadow->antiAliased());
-
- // FIXME: save curves
- w.writeCurve("TrnS",
- "Linear",
- QVector<QPointF>() << QPointF() << QPointF(255, 255));
-
- w.leaveDescriptor();
- }
-
- // Outer Glow
- const psd_layer_effects_outer_glow *outerGlow = style->outerGlow();
- if (outerGlow->effectEnabled()) {
- w.enterDescriptor("OrGl", "", "OrGl");
-
- w.writeBoolean("enab", outerGlow->effectEnabled());
- w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(outerGlow->blendMode()));
-
-
- if (outerGlow->fillType() == psd_fill_gradient && outerGlow->gradient()) {
- KoSegmentGradient *segmentGradient = dynamic_cast<KoSegmentGradient*>(outerGlow->gradient().data());
- KoStopGradient *stopGradient = dynamic_cast<KoStopGradient*>(outerGlow->gradient().data());
-
- if (segmentGradient) {
- w.writeSegmentGradient("Grad", segmentGradient);
- } else if (stopGradient) {
- w.writeStopGradient("Grad", stopGradient);
- } else {
- warnKrita << "WARNING: OG: Unknown gradient type!";
- w.writeColor("Clr ", outerGlow->color());
- }
-
- } else {
- w.writeColor("Clr ", outerGlow->color());
- }
-
- w.writeUnitFloat("Opct", "#Prc", outerGlow->opacity());
-
- w.writeEnum("GlwT", "BETE", techniqueToString(outerGlow->technique(), "BETE"));
-
- w.writeUnitFloat("Ckmt", "#Pxl", outerGlow->spread());
- w.writeUnitFloat("blur", "#Pxl", outerGlow->size());
- w.writeUnitFloat("Nose", "#Prc", outerGlow->noise());
-
- w.writeUnitFloat("ShdN", "#Prc", outerGlow->jitter());
-
- w.writeBoolean("AntA", outerGlow->antiAliased());
-
- // FIXME: save curves
- w.writeCurve("TrnS",
- "Linear",
- QVector<QPointF>() << QPointF() << QPointF(255, 255));
-
- w.writeUnitFloat("Inpr", "#Prc", outerGlow->range());
-
- w.leaveDescriptor();
- }
-
- // Inner Glow
- const psd_layer_effects_inner_glow *innerGlow = style->innerGlow();
- if (innerGlow->effectEnabled()) {
- w.enterDescriptor("IrGl", "", "IrGl");
-
- w.writeBoolean("enab", innerGlow->effectEnabled());
- w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(innerGlow->blendMode()));
-
-
- if (innerGlow->fillType() == psd_fill_gradient && innerGlow->gradient()) {
- KoSegmentGradient *segmentGradient = dynamic_cast<KoSegmentGradient*>(innerGlow->gradient().data());
- KoStopGradient *stopGradient = dynamic_cast<KoStopGradient*>(innerGlow->gradient().data());
-
- if (segmentGradient) {
- w.writeSegmentGradient("Grad", segmentGradient);
- } else if (stopGradient) {
- w.writeStopGradient("Grad", stopGradient);
- } else {
- warnKrita << "WARNING: IG: Unknown gradient type!";
- w.writeColor("Clr ", innerGlow->color());
- }
-
- } else {
- w.writeColor("Clr ", innerGlow->color());
- }
-
- w.writeUnitFloat("Opct", "#Prc", innerGlow->opacity());
-
- w.writeEnum("GlwT", "BETE", techniqueToString(innerGlow->technique(), "BETE"));
-
- w.writeUnitFloat("Ckmt", "#Pxl", innerGlow->spread());
- w.writeUnitFloat("blur", "#Pxl", innerGlow->size());
-
- // NOTE: order is swapped in ASL!
- w.writeUnitFloat("ShdN", "#Prc", innerGlow->jitter());
- w.writeUnitFloat("Nose", "#Prc", innerGlow->noise());
-
- w.writeBoolean("AntA", innerGlow->antiAliased());
-
- w.writeEnum("glwS", "IGSr", innerGlow->source() == psd_glow_center ? "SrcC" : "SrcE");
-
- // FIXME: save curves
- w.writeCurve("TrnS",
- "Linear",
- QVector<QPointF>() << QPointF() << QPointF(255, 255));
-
- w.writeUnitFloat("Inpr", "#Prc", innerGlow->range());
-
- w.leaveDescriptor();
- }
-
- // Bevel and Emboss
- const psd_layer_effects_bevel_emboss *bevelAndEmboss = style->bevelAndEmboss();
- if (bevelAndEmboss->effectEnabled()) {
- w.enterDescriptor("ebbl", "", "ebbl");
-
- w.writeBoolean("enab", bevelAndEmboss->effectEnabled());
-
- w.writeEnum("hglM", "BlnM", compositeOpToBlendMode(bevelAndEmboss->highlightBlendMode()));
- w.writeColor("hglC", bevelAndEmboss->highlightColor());
- w.writeUnitFloat("hglO", "#Prc", bevelAndEmboss->highlightOpacity());
-
- w.writeEnum("sdwM", "BlnM", compositeOpToBlendMode(bevelAndEmboss->shadowBlendMode()));
- w.writeColor("sdwC", bevelAndEmboss->shadowColor());
- w.writeUnitFloat("sdwO", "#Prc", bevelAndEmboss->shadowOpacity());
-
- w.writeEnum("bvlT", "bvlT", techniqueToString(bevelAndEmboss->technique(), "bvlT"));
- w.writeEnum("bvlS", "BESl", bevelStyleToString(bevelAndEmboss->style()));
-
- w.writeBoolean("uglg", bevelAndEmboss->useGlobalLight());
- w.writeUnitFloat("lagl", "#Ang", bevelAndEmboss->angle());
- w.writeUnitFloat("Lald", "#Ang", bevelAndEmboss->altitude());
-
- w.writeUnitFloat("srgR", "#Prc", bevelAndEmboss->depth());
- w.writeUnitFloat("blur", "#Pxl", bevelAndEmboss->size());
- w.writeEnum("bvlD", "BESs", bevelAndEmboss->direction() == psd_direction_up ? "In " : "Out ");
-
- // FIXME: save curves
- w.writeCurve("TrnS",
- "Linear",
- QVector<QPointF>() << QPointF() << QPointF(255, 255));
-
- w.writeBoolean("antialiasGloss", bevelAndEmboss->glossAntiAliased());
-
- w.writeUnitFloat("Sftn", "#Pxl", bevelAndEmboss->soften());
-
- if (bevelAndEmboss->contourEnabled()) {
- w.writeBoolean("useShape", bevelAndEmboss->contourEnabled());
-
- // FIXME: save curves
- w.writeCurve("MpgS",
- "Linear",
- QVector<QPointF>() << QPointF() << QPointF(255, 255));
-
- w.writeBoolean("AntA", bevelAndEmboss->antiAliased());
- w.writeUnitFloat("Inpr", "#Prc", bevelAndEmboss->contourRange());
- }
-
- w.writeBoolean("useTexture", bevelAndEmboss->textureEnabled());
-
- if (bevelAndEmboss->textureEnabled()) {
- w.writeBoolean("InvT", bevelAndEmboss->textureInvert());
- w.writeBoolean("Algn", bevelAndEmboss->textureAlignWithLayer());
- w.writeUnitFloat("Scl ", "#Prc", bevelAndEmboss->textureScale());
- w.writeUnitFloat("textureDepth ", "#Prc", bevelAndEmboss->textureDepth());
- w.writePatternRef("Ptrn", bevelAndEmboss->texturePattern(), fetchPatternUuidSafe(bevelAndEmboss->texturePattern(), patternToUuidMap));
- w.writePhasePoint("phase", bevelAndEmboss->texturePhase());
- }
-
- w.leaveDescriptor();
- }
-
- // Satin
- const psd_layer_effects_satin *satin = style->satin();
- if (satin->effectEnabled()) {
- w.enterDescriptor("ChFX", "", "ChFX");
-
- w.writeBoolean("enab", satin->effectEnabled());
- w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(satin->blendMode()));
- w.writeColor("Clr ", satin->color());
-
- w.writeBoolean("AntA", satin->antiAliased());
- w.writeBoolean("Invr", satin->invert());
- w.writeUnitFloat("Opct", "#Prc", satin->opacity());
- w.writeUnitFloat("lagl", "#Ang", satin->angle());
- w.writeUnitFloat("Dstn", "#Pxl", satin->distance());
- w.writeUnitFloat("blur", "#Pxl", satin->size());
-
- // FIXME: save curves
- w.writeCurve("MpgS",
- "Linear",
- QVector<QPointF>() << QPointF() << QPointF(255, 255));
-
- w.leaveDescriptor();
- }
-
- const psd_layer_effects_color_overlay *colorOverlay = style->colorOverlay();
- if (colorOverlay->effectEnabled()) {
- w.enterDescriptor("SoFi", "", "SoFi");
-
- w.writeBoolean("enab", colorOverlay->effectEnabled());
- w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(colorOverlay->blendMode()));
- w.writeUnitFloat("Opct", "#Prc", colorOverlay->opacity());
- w.writeColor("Clr ", colorOverlay->color());
-
- w.leaveDescriptor();
- }
-
- // Gradient Overlay
- const psd_layer_effects_gradient_overlay *gradientOverlay = style->gradientOverlay();
- KoSegmentGradient *segmentGradient = dynamic_cast<KoSegmentGradient*>(gradientOverlay->gradient().data());
- KoStopGradient *stopGradient = dynamic_cast<KoStopGradient*>(gradientOverlay->gradient().data());
-
- if (gradientOverlay->effectEnabled() && (segmentGradient || stopGradient)) {
- w.enterDescriptor("GrFl", "", "GrFl");
-
- w.writeBoolean("enab", gradientOverlay->effectEnabled());
- w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(gradientOverlay->blendMode()));
- w.writeUnitFloat("Opct", "#Prc", gradientOverlay->opacity());
-
- if (segmentGradient) {
- w.writeSegmentGradient("Grad", segmentGradient);
- } else if (stopGradient) {
- w.writeStopGradient("Grad", stopGradient);
- }
-
- w.writeUnitFloat("Angl", "#Ang", gradientOverlay->angle());
-
- w.writeEnum("Type", "GrdT", gradientTypeToString(gradientOverlay->style()));
-
- w.writeBoolean("Rvrs", gradientOverlay->reverse());
- w.writeBoolean("Algn", gradientOverlay->alignWithLayer());
- w.writeUnitFloat("Scl ", "#Prc", gradientOverlay->scale());
-
- w.writeOffsetPoint("Ofst", gradientOverlay->gradientOffset());
-
- // FIXME: Krita doesn't support dithering
- w.writeBoolean("Dthr", true/*gradientOverlay->dither()*/);
-
- w.leaveDescriptor();
- }
-
- // Pattern Overlay
- const psd_layer_effects_pattern_overlay *patternOverlay = style->patternOverlay();
- if (patternOverlay->effectEnabled()) {
- w.enterDescriptor("patternFill", "", "patternFill");
-
- w.writeBoolean("enab", patternOverlay->effectEnabled());
- w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(patternOverlay->blendMode()));
- w.writeUnitFloat("Opct", "#Prc", patternOverlay->opacity());
-
- w.writePatternRef("Ptrn", patternOverlay->pattern(), fetchPatternUuidSafe(patternOverlay->pattern(), patternToUuidMap));
-
- w.writeUnitFloat("Scl ", "#Prc", patternOverlay->scale());
- w.writeBoolean("Algn", patternOverlay->alignWithLayer());
- w.writePhasePoint("phase", patternOverlay->patternPhase());
-
- w.leaveDescriptor();
- }
-
- const psd_layer_effects_stroke *stroke = style->stroke();
- if (stroke->effectEnabled()) {
- w.enterDescriptor("FrFX", "", "FrFX");
-
- w.writeBoolean("enab", stroke->effectEnabled());
-
- w.writeEnum("Styl", "FStl", strokePositionToString(stroke->position()));
- w.writeEnum("PntT", "FrFl", strokeFillTypeToString(stroke->fillType()));
-
- w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(stroke->blendMode()));
- w.writeUnitFloat("Opct", "#Prc", stroke->opacity());
-
- w.writeUnitFloat("Sz ", "#Pxl", stroke->size());
-
- if (stroke->fillType() == psd_fill_solid_color) {
- w.writeColor("Clr ", stroke->color());
- } else if (stroke->fillType() == psd_fill_gradient) {
- KoSegmentGradient *segmentGradient = dynamic_cast<KoSegmentGradient*>(stroke->gradient().data());
- KoStopGradient *stopGradient = dynamic_cast<KoStopGradient*>(stroke->gradient().data());
-
- if (segmentGradient) {
- w.writeSegmentGradient("Grad", segmentGradient);
- } else if (stopGradient) {
- w.writeStopGradient("Grad", stopGradient);
- } else {
- warnKrita << "WARNING: Stroke: Unknown gradient type!";
- w.writeColor("Clr ", stroke->color());
- }
-
- w.writeUnitFloat("Angl", "#Ang", stroke->angle());
- w.writeEnum("Type", "GrdT", gradientTypeToString(stroke->style()));
-
- w.writeBoolean("Rvrs", stroke->reverse());
- w.writeUnitFloat("Scl ", "#Prc", stroke->scale());
- w.writeBoolean("Algn", stroke->alignWithLayer());
- w.writeOffsetPoint("Ofst", stroke->gradientOffset());
-
- // FIXME: Krita doesn't support dithering
- w.writeBoolean("Dthr", true/*stroke->dither()*/);
-
- } else if (stroke->fillType() == psd_fill_pattern) {
- w.writePatternRef("Ptrn", stroke->pattern(), fetchPatternUuidSafe(stroke->pattern(), patternToUuidMap));
- w.writeUnitFloat("Scl ", "#Prc", stroke->scale());
- w.writeBoolean("Lnkd", stroke->alignWithLayer());
- w.writePhasePoint("phase", stroke->patternPhase());
- }
-
- w.leaveDescriptor();
- }
-
- w.leaveDescriptor();
- w.leaveDescriptor();
- }
-
- return w.document();
-}
-
-inline QDomNode findNodeByClassId(const QString &classId, QDomNode parent) {
- return KisDomUtils::findElementByAttibute(parent, "node", "classId", classId);
-}
-
-void replaceAllChildren(QDomNode src, QDomNode dst)
-{
- QDomNode node;
-
- do {
- node = dst.lastChild();
- dst.removeChild(node);
-
- } while(!node.isNull());
-
-
- node = src.firstChild();
- while(!node.isNull()) {
- dst.appendChild(node);
- node = src.firstChild();
- }
-
- src.parentNode().removeChild(src);
-}
-
-QDomDocument KisAslLayerStyleSerializer::formPsdXmlDocument() const
-{
- QDomDocument doc = formXmlDocument();
-
- QDomNode nullNode = findNodeByClassId("null", doc.documentElement());
- QDomNode stylNode = findNodeByClassId("Styl", doc.documentElement());
- QDomNode lefxNode = findNodeByClassId("Lefx", stylNode);
-
- replaceAllChildren(lefxNode, nullNode);
-
- return doc;
-}
-
-void KisAslLayerStyleSerializer::saveToDevice(QIODevice *device)
-{
- QDomDocument doc = formXmlDocument();
- if (doc.isNull()) return ;
-
- KisAslWriter writer;
- writer.writeFile(device, doc);
-}
-
-void convertAndSetBlendMode(const QString &mode,
- boost::function<void (const QString &)> setBlendMode)
-{
- QString compositeOp = COMPOSITE_OVER;
-
- if (mode == "Nrml") {
- compositeOp = COMPOSITE_OVER;
- } else if (mode == "Dslv") {
- compositeOp = COMPOSITE_DISSOLVE;
- } else if (mode == "Drkn") {
- compositeOp = COMPOSITE_DARKEN;
- } else if (mode == "Mltp") {
- compositeOp = COMPOSITE_MULT;
- } else if (mode == "CBrn") {
- compositeOp = COMPOSITE_BURN;
- } else if (mode == "linearBurn") {
- compositeOp = COMPOSITE_LINEAR_BURN;
- } else if (mode == "darkerColor") {
- compositeOp = COMPOSITE_DARKER_COLOR;
- } else if (mode == "Lghn") {
- compositeOp = COMPOSITE_LIGHTEN;
- } else if (mode == "Scrn") {
- compositeOp = COMPOSITE_SCREEN;
- } else if (mode == "CDdg") {
- compositeOp = COMPOSITE_DODGE;
- } else if (mode == "linearDodge") {
- compositeOp = COMPOSITE_LINEAR_DODGE;
- } else if (mode == "lighterColor") {
- compositeOp = COMPOSITE_LIGHTER_COLOR;
- } else if (mode == "Ovrl") {
- compositeOp = COMPOSITE_OVERLAY;
- } else if (mode == "SftL") {
- compositeOp = COMPOSITE_SOFT_LIGHT_PHOTOSHOP;
- } else if (mode == "HrdL") {
- compositeOp = COMPOSITE_HARD_LIGHT;
- } else if (mode == "vividLight") {
- compositeOp = COMPOSITE_VIVID_LIGHT;
- } else if (mode == "linearLight") {
- compositeOp = COMPOSITE_LINEAR_LIGHT;
- } else if (mode == "pinLight") {
- compositeOp = COMPOSITE_PIN_LIGHT;
- } else if (mode == "hardMix") {
- compositeOp = COMPOSITE_HARD_MIX_PHOTOSHOP;
- } else if (mode == "Dfrn") {
- compositeOp = COMPOSITE_DIFF;
- } else if (mode == "Xclu") {
- compositeOp = COMPOSITE_EXCLUSION;
- } else if (mode == "Sbtr") {
- compositeOp = COMPOSITE_SUBTRACT;
- } else if (mode == "divide") {
- compositeOp = COMPOSITE_DIVIDE;
- } else if (mode == "H ") {
- compositeOp = COMPOSITE_HUE;
- } else if (mode == "Strt") {
- compositeOp = COMPOSITE_SATURATION;
- } else if (mode == "Clr ") {
- compositeOp = COMPOSITE_COLOR;
- } else if (mode == "Lmns") {
- compositeOp = COMPOSITE_LUMINIZE;
- } else {
- dbgKrita << "Unknown blending mode:" << mode << "Returning COMPOSITE_OVER!";
- }
-
- setBlendMode(compositeOp);
-}
-
-void convertAndSetCurve(const QString &name,
- const QVector<QPointF> &points,
- boost::function<void (const quint8*)> setCurveLookupTable)
-{
- Q_UNUSED(name);
- Q_UNUSED(points);
- Q_UNUSED(setCurveLookupTable);
-
- warnKrita << "convertAndSetBlendMode:" << "Curve conversion is not implemented yet";
-}
-
-template <typename T>
-void convertAndSetEnum(const QString &value,
- const QMap<QString, T> map,
- boost::function<void (T)> setMappedValue)
-{
- setMappedValue(map[value]);
-}
-
-inline QString _prepaddr(const QString &pref, const QString &addr) {
- return pref + addr;
-}
-
-#define CONN_TEXT_RADDR(addr, method, object, type) m_catcher.subscribeText(addr, std::bind(&type::method, object, _1))
-#define CONN_COLOR(addr, method, object, type, prefix) m_catcher.subscribeColor(_prepaddr(prefix, addr), std::bind(&type::method, object, _1))
-#define CONN_UNITF(addr, unit, method, object, type, prefix) m_catcher.subscribeUnitFloat(_prepaddr(prefix, addr), unit, std::bind(&type::method, object, _1))
-#define CONN_BOOL(addr, method, object, type, prefix) m_catcher.subscribeBoolean(_prepaddr(prefix, addr), std::bind(&type::method, object, _1))
-
-#define CONN_POINT(addr, method, object, type, prefix) m_catcher.subscribePoint(_prepaddr(prefix, addr), std::bind(&type::method, object, _1))
-
-#define CONN_COMPOSITE_OP(addr, method, object, type, prefix) \
- { \
- boost::function<void (const QString&)> setter = \
- std::bind(&type::method, object, _1); \
- m_catcher.subscribeEnum(_prepaddr(prefix, addr), "BlnM", std::bind(convertAndSetBlendMode, _1, setter)); \
- }
-
-#define CONN_CURVE(addr, method, object, type, prefix) \
- { \
- boost::function<void (const quint8*)> setter = \
- std::bind(&type::method, object, _1); \
- m_catcher.subscribeCurve(_prepaddr(prefix, addr), std::bind(convertAndSetCurve, _1, _2, setter)); \
- }
-
-#define CONN_ENUM(addr, tag, method, map, mapped_type, object, type, prefix) \
- { \
- boost::function<void (mapped_type)> setter = \
- std::bind(&type::method, object, _1); \
- m_catcher.subscribeEnum(_prepaddr(prefix, addr), tag, std::bind(convertAndSetEnum<mapped_type>, _1, map, setter)); \
- }
-
-#define CONN_GRADIENT(addr, method, object, type, prefix) \
- { \
- m_catcher.subscribeGradient(_prepaddr(prefix, addr), std::bind(&type::method, object, _1)); \
- }
-
-#define CONN_PATTERN(addr, method, object, type, prefix) \
- { \
- boost::function<void (KoPattern*)> setter = \
- std::bind(&type::method, object, _1); \
- m_catcher.subscribePatternRef(_prepaddr(prefix, addr), std::bind(&KisAslLayerStyleSerializer::assignPatternObject, this, _1, _2, setter)); \
- }
-
-void KisAslLayerStyleSerializer::registerPatternObject(const KoPattern *pattern) {
- QString uuid = KisAslWriterUtils::getPatternUuidLazy(pattern);
-
- if (m_patternsStore.contains(uuid)) {
- warnKrita << "WARNING: ASL style contains a duplicated pattern!" << ppVar(pattern->name()) << ppVar(m_patternsStore[uuid]->name());
- } else {
- KoResourceServer<KoPattern> *server = KoResourceServerProvider::instance()->patternServer();
- KoPattern *patternToAdd = server->resourceByMD5(pattern->md5());
-
- if (!patternToAdd) {
- patternToAdd = pattern->clone();
- server->addResource(patternToAdd, false);
- }
-
- m_patternsStore.insert(uuid, patternToAdd);
- }
-}
-
-void KisAslLayerStyleSerializer::assignPatternObject(const QString &patternUuid,
- const QString &patternName,
- boost::function<void (KoPattern *)> setPattern)
-{
- Q_UNUSED(patternName);
-
- KoPattern *pattern = m_patternsStore[patternUuid];
-
- if (!pattern) {
- warnKrita << "WARNING: ASL style contains inexistent pattern reference!";
-
- QImage dumbImage(32, 32, QImage::Format_ARGB32);
- dumbImage.fill(Qt::red);
- KoPattern *dumbPattern = new KoPattern(dumbImage, "invalid", "");
- registerPatternObject(dumbPattern);
- pattern = dumbPattern;
- }
-
- setPattern(pattern);
-}
-
-class FillStylesCorrector {
-public:
-
- static void correct(KisPSDLayerStyle *style) {
- correctWithoutPattern(style->outerGlow());
- correctWithoutPattern(style->innerGlow());
- correctWithPattern(style->stroke());
- }
-
-private:
-
- template <class T>
- static void correctWithPattern(T *config) {
- if (config->pattern()) {
- config->setFillType(psd_fill_pattern);
- } else if (config->gradient()) {
- config->setFillType(psd_fill_gradient);
- } else {
- config->setFillType(psd_fill_solid_color);
- }
- }
-
- template <class T>
- static void correctWithoutPattern(T *config) {
- if (config->gradient()) {
- config->setFillType(psd_fill_gradient);
- } else {
- config->setFillType(psd_fill_solid_color);
- }
- }
-};
-
-void KisAslLayerStyleSerializer::connectCatcherToStyle(KisPSDLayerStyle *style, const QString &prefix)
-{
- CONN_TEXT_RADDR("/null/Nm ", setName, style, KisPSDLayerStyle);
- CONN_TEXT_RADDR("/null/Idnt", setPsdUuid, style, KisPSDLayerStyle);
-
- CONN_BOOL("/masterFXSwitch", setEnabled, style, KisPSDLayerStyle, prefix);
-
- psd_layer_effects_drop_shadow *dropShadow = style->dropShadow();
-
- CONN_COMPOSITE_OP("/DrSh/Md ", setBlendMode, dropShadow, psd_layer_effects_drop_shadow, prefix);
- CONN_COLOR("/DrSh/Clr ", setColor, dropShadow, psd_layer_effects_drop_shadow, prefix);
- CONN_UNITF("/DrSh/Opct", "#Prc", setOpacity, dropShadow, psd_layer_effects_drop_shadow, prefix);
- CONN_UNITF("/DrSh/lagl", "#Ang", setAngle, dropShadow, psd_layer_effects_drop_shadow, prefix);
- CONN_UNITF("/DrSh/Dstn", "#Pxl", setDistance, dropShadow, psd_layer_effects_drop_shadow, prefix);
- CONN_UNITF("/DrSh/Ckmt", "#Pxl", setSpread, dropShadow, psd_layer_effects_drop_shadow, prefix);
- CONN_UNITF("/DrSh/blur", "#Pxl", setSize, dropShadow, psd_layer_effects_drop_shadow, prefix);
- CONN_UNITF("/DrSh/Nose", "#Prc", setNoise, dropShadow, psd_layer_effects_drop_shadow, prefix);
- CONN_BOOL("/DrSh/enab", setEffectEnabled, dropShadow, psd_layer_effects_drop_shadow, prefix);
- CONN_BOOL("/DrSh/uglg", setUseGlobalLight, dropShadow, psd_layer_effects_drop_shadow, prefix);
- CONN_BOOL("/DrSh/AntA", setAntiAliased, dropShadow, psd_layer_effects_drop_shadow, prefix);
- CONN_BOOL("/DrSh/layerConceals", setKnocksOut, dropShadow, psd_layer_effects_drop_shadow, prefix);
- CONN_CURVE("/DrSh/TrnS", setContourLookupTable, dropShadow, psd_layer_effects_drop_shadow, prefix);
-
- psd_layer_effects_inner_shadow *innerShadow = style->innerShadow();
-
- CONN_COMPOSITE_OP("/IrSh/Md ", setBlendMode, innerShadow, psd_layer_effects_inner_shadow, prefix);
- CONN_COLOR("/IrSh/Clr ", setColor, innerShadow, psd_layer_effects_inner_shadow, prefix);
- CONN_UNITF("/IrSh/Opct", "#Prc", setOpacity, innerShadow, psd_layer_effects_inner_shadow, prefix);
- CONN_UNITF("/IrSh/lagl", "#Ang", setAngle, innerShadow, psd_layer_effects_inner_shadow, prefix);
- CONN_UNITF("/IrSh/Dstn", "#Pxl", setDistance, innerShadow, psd_layer_effects_inner_shadow, prefix);
- CONN_UNITF("/IrSh/Ckmt", "#Pxl", setSpread, innerShadow, psd_layer_effects_inner_shadow, prefix);
- CONN_UNITF("/IrSh/blur", "#Pxl", setSize, innerShadow, psd_layer_effects_inner_shadow, prefix);
- CONN_UNITF("/IrSh/Nose", "#Prc", setNoise, innerShadow, psd_layer_effects_inner_shadow, prefix);
- CONN_BOOL("/IrSh/enab", setEffectEnabled, innerShadow, psd_layer_effects_inner_shadow, prefix);
- CONN_BOOL("/IrSh/uglg", setUseGlobalLight, innerShadow, psd_layer_effects_inner_shadow, prefix);
- CONN_BOOL("/IrSh/AntA", setAntiAliased, innerShadow, psd_layer_effects_inner_shadow, prefix);
- CONN_CURVE("/IrSh/TrnS", setContourLookupTable, innerShadow, psd_layer_effects_inner_shadow, prefix);
-
- psd_layer_effects_outer_glow *outerGlow = style->outerGlow();
-
- CONN_COMPOSITE_OP("/OrGl/Md ", setBlendMode, outerGlow, psd_layer_effects_outer_glow, prefix);
- CONN_COLOR("/OrGl/Clr ", setColor, outerGlow, psd_layer_effects_outer_glow, prefix);
- CONN_UNITF("/OrGl/Opct", "#Prc", setOpacity, outerGlow, psd_layer_effects_outer_glow, prefix);
- CONN_UNITF("/OrGl/Ckmt", "#Pxl", setSpread, outerGlow, psd_layer_effects_outer_glow, prefix);
- CONN_UNITF("/OrGl/blur", "#Pxl", setSize, outerGlow, psd_layer_effects_outer_glow, prefix);
- CONN_UNITF("/OrGl/Nose", "#Prc", setNoise, outerGlow, psd_layer_effects_outer_glow, prefix);
- CONN_BOOL("/OrGl/enab", setEffectEnabled, outerGlow, psd_layer_effects_outer_glow, prefix);
- CONN_BOOL("/OrGl/AntA", setAntiAliased, outerGlow, psd_layer_effects_outer_glow, prefix);
- CONN_CURVE("/OrGl/TrnS", setContourLookupTable, outerGlow, psd_layer_effects_outer_glow, prefix);
-
- QMap<QString, psd_technique_type> fillTechniqueMap;
- fillTechniqueMap.insert("PrBL", psd_technique_precise);
- fillTechniqueMap.insert("SfBL", psd_technique_softer);
- CONN_ENUM("/OrGl/GlwT", "BETE", setTechnique, fillTechniqueMap, psd_technique_type, outerGlow, psd_layer_effects_outer_glow, prefix);
-
- CONN_GRADIENT("/OrGl/Grad", setGradient, outerGlow, psd_layer_effects_outer_glow, prefix);
-
- CONN_UNITF("/OrGl/Inpr", "#Prc", setRange, outerGlow, psd_layer_effects_outer_glow, prefix);
- CONN_UNITF("/OrGl/ShdN", "#Prc", setJitter, outerGlow, psd_layer_effects_outer_glow, prefix);
-
-
- psd_layer_effects_inner_glow *innerGlow = style->innerGlow();
-
- CONN_COMPOSITE_OP("/IrGl/Md ", setBlendMode, innerGlow, psd_layer_effects_inner_glow, prefix);
- CONN_COLOR("/IrGl/Clr ", setColor, innerGlow, psd_layer_effects_inner_glow, prefix);
- CONN_UNITF("/IrGl/Opct", "#Prc", setOpacity, innerGlow, psd_layer_effects_inner_glow, prefix);
- CONN_UNITF("/IrGl/Ckmt", "#Pxl", setSpread, innerGlow, psd_layer_effects_inner_glow, prefix);
- CONN_UNITF("/IrGl/blur", "#Pxl", setSize, innerGlow, psd_layer_effects_inner_glow, prefix);
- CONN_UNITF("/IrGl/Nose", "#Prc", setNoise, innerGlow, psd_layer_effects_inner_glow, prefix);
- CONN_BOOL("/IrGl/enab", setEffectEnabled, innerGlow, psd_layer_effects_inner_glow, prefix);
- CONN_BOOL("/IrGl/AntA", setAntiAliased, innerGlow, psd_layer_effects_inner_glow, prefix);
- CONN_CURVE("/IrGl/TrnS", setContourLookupTable, innerGlow, psd_layer_effects_inner_glow, prefix);
-
- CONN_ENUM("/IrGl/GlwT", "BETE", setTechnique, fillTechniqueMap, psd_technique_type, innerGlow, psd_layer_effects_inner_glow, prefix);
-
- CONN_GRADIENT("/IrGl/Grad", setGradient, innerGlow, psd_layer_effects_inner_glow, prefix);
-
- CONN_UNITF("/IrGl/Inpr", "#Prc", setRange, innerGlow, psd_layer_effects_inner_glow, prefix);
- CONN_UNITF("/IrGl/ShdN", "#Prc", setJitter, innerGlow, psd_layer_effects_inner_glow, prefix);
-
- QMap<QString, psd_glow_source> glowSourceMap;
- glowSourceMap.insert("SrcC", psd_glow_center);
- glowSourceMap.insert("SrcE", psd_glow_edge);
- CONN_ENUM("/IrGl/glwS", "IGSr", setSource, glowSourceMap, psd_glow_source, innerGlow, psd_layer_effects_inner_glow, prefix);
-
-
- psd_layer_effects_satin *satin = style->satin();
-
- CONN_COMPOSITE_OP("/ChFX/Md ", setBlendMode, satin, psd_layer_effects_satin, prefix);
- CONN_COLOR("/ChFX/Clr ", setColor, satin, psd_layer_effects_satin, prefix);
-
- CONN_UNITF("/ChFX/Opct", "#Prc", setOpacity, satin, psd_layer_effects_satin, prefix);
- CONN_UNITF("/ChFX/lagl", "#Ang", setAngle, satin, psd_layer_effects_satin, prefix);
- CONN_UNITF("/ChFX/Dstn", "#Pxl", setDistance, satin, psd_layer_effects_satin, prefix);
- CONN_UNITF("/ChFX/blur", "#Pxl", setSize, satin, psd_layer_effects_satin, prefix);
-
- CONN_BOOL("/ChFX/enab", setEffectEnabled, satin, psd_layer_effects_satin, prefix);
- CONN_BOOL("/ChFX/AntA", setAntiAliased, satin, psd_layer_effects_satin, prefix);
- CONN_BOOL("/ChFX/Invr", setInvert, satin, psd_layer_effects_satin, prefix);
- CONN_CURVE("/ChFX/MpgS", setContourLookupTable, satin, psd_layer_effects_satin, prefix);
-
- psd_layer_effects_color_overlay *colorOverlay = style->colorOverlay();
-
- CONN_COMPOSITE_OP("/SoFi/Md ", setBlendMode, colorOverlay, psd_layer_effects_color_overlay, prefix);
- CONN_COLOR("/SoFi/Clr ", setColor, colorOverlay, psd_layer_effects_color_overlay, prefix);
- CONN_UNITF("/SoFi/Opct", "#Prc", setOpacity, colorOverlay, psd_layer_effects_color_overlay, prefix);
- CONN_BOOL("/SoFi/enab", setEffectEnabled, colorOverlay, psd_layer_effects_color_overlay, prefix);
-
- psd_layer_effects_gradient_overlay *gradientOverlay = style->gradientOverlay();
-
- CONN_COMPOSITE_OP("/GrFl/Md ", setBlendMode, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
- CONN_UNITF("/GrFl/Opct", "#Prc", setOpacity, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
- CONN_UNITF("/GrFl/Scl ", "#Prc", setScale, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
- CONN_UNITF("/GrFl/Angl", "#Ang", setAngle, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
- CONN_BOOL("/GrFl/enab", setEffectEnabled, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
- // CONN_BOOL("/GrFl/Dthr", setDitherNotImplemented, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
- CONN_BOOL("/GrFl/Rvrs", setReverse, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
- CONN_BOOL("/GrFl/Algn", setAlignWithLayer, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
- CONN_POINT("/GrFl/Ofst", setGradientOffset, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
- CONN_GRADIENT("/GrFl/Grad", setGradient, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
-
-
- QMap<QString, psd_gradient_style> gradientStyleMap;
- gradientStyleMap.insert("Lnr ", psd_gradient_style_linear);
- gradientStyleMap.insert("Rdl ", psd_gradient_style_radial);
- gradientStyleMap.insert("Angl", psd_gradient_style_angle);
- gradientStyleMap.insert("Rflc", psd_gradient_style_reflected);
- gradientStyleMap.insert("Dmnd", psd_gradient_style_diamond);
- CONN_ENUM("/GrFl/Type", "GrdT", setStyle, gradientStyleMap, psd_gradient_style, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
-
- psd_layer_effects_pattern_overlay *patternOverlay = style->patternOverlay();
-
- CONN_BOOL("/patternFill/enab", setEffectEnabled, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
- CONN_COMPOSITE_OP("/patternFill/Md ", setBlendMode, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
- CONN_UNITF("/patternFill/Opct", "#Prc", setOpacity, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
- CONN_PATTERN("/patternFill/Ptrn", setPattern, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
- CONN_UNITF("/patternFill/Scl ", "#Prc", setScale, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
- CONN_BOOL("/patternFill/Algn", setAlignWithLayer, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
- CONN_POINT("/patternFill/phase", setPatternPhase, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
-
- psd_layer_effects_stroke *stroke = style->stroke();
-
- CONN_COMPOSITE_OP("/FrFX/Md ", setBlendMode, stroke, psd_layer_effects_stroke, prefix);
- CONN_BOOL("/FrFX/enab", setEffectEnabled, stroke, psd_layer_effects_stroke, prefix);
- CONN_UNITF("/FrFX/Opct", "#Prc", setOpacity, stroke, psd_layer_effects_stroke, prefix);
- CONN_UNITF("/FrFX/Sz ", "#Pxl", setSize, stroke, psd_layer_effects_stroke, prefix);
-
- QMap<QString, psd_stroke_position> strokeStyleMap;
- strokeStyleMap.insert("OutF", psd_stroke_outside);
- strokeStyleMap.insert("InsF", psd_stroke_inside);
- strokeStyleMap.insert("CtrF", psd_stroke_center);
- CONN_ENUM("/FrFX/Styl", "FStl", setPosition, strokeStyleMap, psd_stroke_position, stroke, psd_layer_effects_stroke, prefix);
-
- QMap<QString, psd_fill_type> strokeFillType;
- strokeFillType.insert("SClr", psd_fill_solid_color);
- strokeFillType.insert("GrFl", psd_fill_gradient);
- strokeFillType.insert("Ptrn", psd_fill_pattern);
- CONN_ENUM("/FrFX/PntT", "FrFl", setFillType, strokeFillType, psd_fill_type, stroke, psd_layer_effects_stroke, prefix);
-
- // Color type
- CONN_COLOR("/FrFX/Clr ", setColor, stroke, psd_layer_effects_stroke, prefix);
-
- // Gradient Type
- CONN_GRADIENT("/FrFX/Grad", setGradient, stroke, psd_layer_effects_stroke, prefix);
- CONN_UNITF("/FrFX/Angl", "#Ang", setAngle, stroke, psd_layer_effects_stroke, prefix);
- CONN_UNITF("/FrFX/Scl ", "#Prc", setScale, stroke, psd_layer_effects_stroke, prefix);
- CONN_ENUM("/FrFX/Type", "GrdT", setStyle, gradientStyleMap, psd_gradient_style, stroke, psd_layer_effects_stroke, prefix);
- CONN_BOOL("/FrFX/Rvrs", setReverse, stroke, psd_layer_effects_stroke, prefix);
- CONN_BOOL("/FrFX/Algn", setAlignWithLayer, stroke, psd_layer_effects_stroke, prefix);
- CONN_POINT("/FrFX/Ofst", setGradientOffset, stroke, psd_layer_effects_stroke, prefix);
- // CONN_BOOL("/FrFX/Dthr", setDitherNotImplemented, stroke, psd_layer_effects_stroke, prefix);
-
- // Pattern type
-
- CONN_PATTERN("/FrFX/Ptrn", setPattern, stroke, psd_layer_effects_stroke, prefix);
- CONN_BOOL("/FrFX/Lnkd", setAlignWithLayer, stroke, psd_layer_effects_stroke, prefix); // yes, we share the params...
- CONN_POINT("/FrFX/phase", setPatternPhase, stroke, psd_layer_effects_stroke, prefix);
-
-
- psd_layer_effects_bevel_emboss *bevelAndEmboss = style->bevelAndEmboss();
-
- CONN_BOOL("/ebbl/enab", setEffectEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
- CONN_COMPOSITE_OP("/ebbl/hglM", setHighlightBlendMode, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_COLOR("/ebbl/hglC", setHighlightColor, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_UNITF("/ebbl/hglO", "#Prc", setHighlightOpacity, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
- CONN_COMPOSITE_OP("/ebbl/sdwM", setShadowBlendMode, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_COLOR("/ebbl/sdwC", setShadowColor, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_UNITF("/ebbl/sdwO", "#Prc", setShadowOpacity, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
- QMap<QString, psd_technique_type> bevelTechniqueMap;
- bevelTechniqueMap.insert("PrBL", psd_technique_precise);
- bevelTechniqueMap.insert("SfBL", psd_technique_softer);
- bevelTechniqueMap.insert("Slmt", psd_technique_slope_limit);
- CONN_ENUM("/ebbl/bvlT", "bvlT", setTechnique, bevelTechniqueMap, psd_technique_type, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
- QMap<QString, psd_bevel_style> bevelStyleMap;
- bevelStyleMap.insert("OtrB", psd_bevel_outer_bevel);
- bevelStyleMap.insert("InrB", psd_bevel_inner_bevel);
- bevelStyleMap.insert("Embs", psd_bevel_emboss);
- bevelStyleMap.insert("PlEb", psd_bevel_pillow_emboss);
- bevelStyleMap.insert("strokeEmboss", psd_bevel_stroke_emboss);
- CONN_ENUM("/ebbl/bvlS", "BESl", setStyle, bevelStyleMap, psd_bevel_style, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
- CONN_BOOL("/ebbl/uglg", setUseGlobalLight, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
- CONN_UNITF("/ebbl/lagl", "#Ang", setAngle, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_UNITF("/ebbl/Lald", "#Ang", setAltitude, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
- CONN_UNITF("/ebbl/srgR", "#Prc", setDepth, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
- CONN_UNITF("/ebbl/blur", "#Pxl", setSize, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
-
- QMap<QString, psd_direction> bevelDirectionMap;
- bevelDirectionMap.insert("In ", psd_direction_up);
- bevelDirectionMap.insert("Out ", psd_direction_down);
- CONN_ENUM("/ebbl/bvlD", "BESs", setDirection, bevelDirectionMap, psd_direction, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
- CONN_CURVE("/ebbl/TrnS", setContourLookupTable, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
- CONN_BOOL("/ebbl/antialiasGloss", setGlossAntiAliased, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
- CONN_UNITF("/ebbl/Sftn", "#Pxl", setSoften, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
- // Use shape mode
-
- CONN_BOOL("/ebbl/useShape", setContourEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_CURVE("/ebbl/MpgS", setGlossContourLookupTable, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_BOOL("/ebbl/AntA", setAntiAliased, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_UNITF("/ebbl/Inpr", "#Prc", setContourRange, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-
- // Use texture mode
-
- CONN_BOOL("/ebbl/useTexture", setTextureEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_BOOL("/ebbl/InvT", setTextureInvert, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_BOOL("/ebbl/Algn", setTextureAlignWithLayer, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_UNITF("/ebbl/Scl ", "#Prc", setTextureScale, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_UNITF("/ebbl/textureDepth", "#Prc", setTextureDepth, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_PATTERN("/ebbl/Ptrn", setTexturePattern, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
- CONN_POINT("/ebbl/phase", setTexturePhase, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
-}
-
-void KisAslLayerStyleSerializer::newStyleStarted(bool isPsdStructure)
-{
- m_stylesVector.append(toQShared(new KisPSDLayerStyle()));
- KisPSDLayerStyle *currentStyle = m_stylesVector.last().data();
-
- psd_layer_effects_context *context = currentStyle->context();
- context->keep_original = 0;
-
- QString prefix = isPsdStructure ? "/null" : "/Styl/Lefx";
- connectCatcherToStyle(currentStyle, prefix);
-}
-
-void KisAslLayerStyleSerializer::readFromDevice(QIODevice *device)
-{
- m_stylesVector.clear();
-
- m_catcher.subscribePattern("/Patterns/KisPattern", std::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1));
- m_catcher.subscribeNewStyleStarted(std::bind(&KisAslLayerStyleSerializer::newStyleStarted, this, false));
-
- KisAslReader reader;
- QDomDocument doc = reader.readFile(device);
-
- //dbgKrita << ppVar(doc.toString());
-
- //KisAslObjectCatcher c2;
- KisAslXmlParser parser;
- parser.parseXML(doc, m_catcher);
-
- // correct all the layer styles
- Q_FOREACH (KisPSDLayerStyleSP style, m_stylesVector) {
- FillStylesCorrector::correct(style.data());
- }
-}
-
-void KisAslLayerStyleSerializer::registerPSDPattern(const QDomDocument &doc)
-{
- KisAslCallbackObjectCatcher catcher;
- catcher.subscribePattern("/Patterns/KisPattern", std::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1));
-
- //KisAslObjectCatcher c2;
- KisAslXmlParser parser;
- parser.parseXML(doc, catcher);
-}
-
-void KisAslLayerStyleSerializer::readFromPSDXML(const QDomDocument &doc)
-{
- // The caller prepares the document using th efollowing code
- //
- // KisAslReader reader;
- // QDomDocument doc = reader.readLfx2PsdSection(device);
-
- m_stylesVector.clear();
-
- //m_catcher.subscribePattern("/Patterns/KisPattern", std::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1));
- m_catcher.subscribeNewStyleStarted(std::bind(&KisAslLayerStyleSerializer::newStyleStarted, this, true));
-
- //KisAslObjectCatcher c2;
- KisAslXmlParser parser;
- parser.parseXML(doc, m_catcher);
-
- // correct all the layer styles
- Q_FOREACH (KisPSDLayerStyleSP style, m_stylesVector) {
- FillStylesCorrector::correct(style.data());
- }
-}
diff --git a/libs/ui/kis_asl_layer_style_serializer.h b/libs/ui/kis_asl_layer_style_serializer.h
deleted file mode 100644
index 13bd8dcfe5..0000000000
--- a/libs/ui/kis_asl_layer_style_serializer.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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_ASL_LAYER_STYLE_SERIALIZER_H
-#define __KIS_ASL_LAYER_STYLE_SERIALIZER_H
-
-#include "kritaui_export.h"
-
-class QIODevice;
-class KoPattern;
-
-#include "kis_psd_layer_style.h"
-#include "asl/kis_asl_callback_object_catcher.h"
-
-
-class KRITAUI_EXPORT KisAslLayerStyleSerializer
-{
-public:
- KisAslLayerStyleSerializer();
- ~KisAslLayerStyleSerializer();
-
- void saveToDevice(QIODevice *device);
- void readFromDevice(QIODevice *device);
-
- QVector<KisPSDLayerStyleSP> styles() const;
- void setStyles(const QVector<KisPSDLayerStyleSP> &styles);
-
- void registerPSDPattern(const QDomDocument &doc);
- void readFromPSDXML(const QDomDocument &doc);
-
- QDomDocument formXmlDocument() const;
- QDomDocument formPsdXmlDocument() const;
-
-
-private:
- void registerPatternObject(const KoPattern *pattern);
-
- void assignPatternObject(const QString &patternUuid,
- const QString &patternName,
- boost::function<void (KoPattern *)> setPattern);
-
- QVector<KoPattern*> fetchAllPatterns(KisPSDLayerStyle *style) const;
-
- void newStyleStarted(bool isPsdStructure);
- void connectCatcherToStyle(KisPSDLayerStyle *style, const QString &prefix);
-
-private:
- QHash<QString, KoPattern*> m_patternsStore;
-
- KisAslCallbackObjectCatcher m_catcher;
- QVector<KisPSDLayerStyleSP> m_stylesVector;
-};
-
-#endif /* __KIS_ASL_LAYER_STYLE_SERIALIZER_H */
diff --git a/libs/ui/kis_autogradient.cc b/libs/ui/kis_autogradient.cc
index 5d33501c5b..327ffedba5 100644
--- a/libs/ui/kis_autogradient.cc
+++ b/libs/ui/kis_autogradient.cc
@@ -1,172 +1,172 @@
/*
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* 2004 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_autogradient.h"
#include <QPainter>
#include <QComboBox>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <KoColorSpace.h>
#include <resources/KoSegmentGradient.h>
#include "kis_debug.h"
#include "KisGradientSliderWidget.h"
/****************************** KisAutogradient ******************************/
-KisAutogradientEditor::KisAutogradientEditor(KoSegmentGradient* gradient, QWidget *parent, const char* name, const QString& caption)
+KisAutogradientEditor::KisAutogradientEditor(KoSegmentGradientSP gradient, QWidget *parent, const char* name, const QString& caption)
: QWidget(parent)
, m_autogradientResource(gradient)
{
setObjectName(name);
setupUi(this);
setWindowTitle(caption);
gradientSlider->setGradientResource(m_autogradientResource);
nameedit->setText(gradient->name());
KoGradientSegment* segment = gradientSlider->selectedSegment();
if (segment) {
slotSelectedSegment(segment);
}
connect(nameedit, SIGNAL(editingFinished()), this, SLOT(slotChangedName()));
connect(gradientSlider, SIGNAL(sigSelectedSegment(KoGradientSegment*)), SLOT(slotSelectedSegment(KoGradientSegment*)));
connect(gradientSlider, SIGNAL(sigChangedSegment(KoGradientSegment*)), SLOT(slotChangedSegment(KoGradientSegment*)));
connect(comboBoxColorInterpolationType, SIGNAL(activated(int)), SLOT(slotChangedColorInterpolation(int)));
connect(comboBoxInterpolationType, SIGNAL(activated(int)), SLOT(slotChangedInterpolation(int)));
connect(leftColorButton, SIGNAL(changed(KoColor)), SLOT(slotChangedLeftColor(KoColor)));
connect(rightColorButton, SIGNAL(changed(KoColor)), SLOT(slotChangedRightColor(KoColor)));
connect(intNumInputLeftOpacity, SIGNAL(valueChanged(int)), SLOT(slotChangedLeftOpacity(int)));
connect(intNumInputRightOpacity, SIGNAL(valueChanged(int)), SLOT(slotChangedRightOpacity(int)));
}
void KisAutogradientEditor::activate()
{
paramChanged();
}
void KisAutogradientEditor::slotSelectedSegment(KoGradientSegment* segment)
{
leftColorButton->setColor(segment->startColor());
rightColorButton->setColor(segment->endColor());
comboBoxColorInterpolationType->setCurrentIndex(segment->colorInterpolation());
comboBoxInterpolationType->setCurrentIndex(segment->interpolation());
int leftOpacity = segment->startColor().opacityF();
intNumInputLeftOpacity->setValue(leftOpacity * 100);
intNumInputLeftOpacity->setSuffix(i18n(" %"));
int rightOpacity = segment->endColor().opacityF();
intNumInputRightOpacity->setValue(rightOpacity * 100);
intNumInputRightOpacity->setSuffix(i18n(" %"));
paramChanged();
}
void KisAutogradientEditor::slotChangedSegment(KoGradientSegment*)
{
paramChanged();
}
void KisAutogradientEditor::slotChangedInterpolation(int type)
{
KoGradientSegment* segment = gradientSlider->selectedSegment();
if (segment)
segment->setInterpolation(type);
gradientSlider->update();
paramChanged();
}
void KisAutogradientEditor::slotChangedColorInterpolation(int type)
{
KoGradientSegment* segment = gradientSlider->selectedSegment();
if (segment)
segment->setColorInterpolation(type);
gradientSlider->update();
paramChanged();
}
void KisAutogradientEditor::slotChangedLeftColor(const KoColor& color)
{
KoGradientSegment* segment = gradientSlider->selectedSegment();
if (segment) {
KoColor c(color, segment->startColor().colorSpace());
c.setOpacity(segment->startColor().opacityU8());
segment->setStartColor(c);
}
gradientSlider->update();
paramChanged();
}
void KisAutogradientEditor::slotChangedRightColor(const KoColor& color)
{
KoGradientSegment* segment = gradientSlider->selectedSegment();
if (segment) {
KoColor c(color, segment->endColor().colorSpace());
c.setOpacity(segment->endColor().opacityU8());
segment->setEndColor(c);
}
gradientSlider->repaint();
paramChanged();
}
void KisAutogradientEditor::slotChangedLeftOpacity(int value)
{
KoGradientSegment* segment = gradientSlider->selectedSegment();
if (segment) {
KoColor c(segment->startColor(), segment->startColor().colorSpace());
c.setOpacity(qreal(value) / qreal(100.0));
segment->setStartColor(c);
}
gradientSlider->repaint();
paramChanged();
}
void KisAutogradientEditor::slotChangedRightOpacity(int value)
{
KoGradientSegment* segment = gradientSlider->selectedSegment();
if (segment) {
KoColor c(segment->endColor(), segment->endColor().colorSpace());
c.setOpacity(quint8((value *OPACITY_OPAQUE_U8) / 100));
segment->setEndColor(c);
}
gradientSlider->repaint();
paramChanged();
}
void KisAutogradientEditor::slotChangedName()
{
m_autogradientResource->setName(nameedit->text());
}
void KisAutogradientEditor::paramChanged()
{
m_autogradientResource->updatePreview();
}
diff --git a/libs/ui/kis_autogradient.h b/libs/ui/kis_autogradient.h
index 7ba6b680a4..98c26d3163 100644
--- a/libs/ui/kis_autogradient.h
+++ b/libs/ui/kis_autogradient.h
@@ -1,50 +1,50 @@
/*
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* 2004 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_AUTOGRADIENT_H_
#define _KIS_AUTOGRADIENT_H_
#include "ui_wdgautogradient.h"
class KoGradientSegment;
-class KoSegmentGradient;
+#include<KoSegmentGradient.h>
class KisAutogradientEditor : public QWidget, public Ui::KisWdgAutogradient
{
Q_OBJECT
public:
- KisAutogradientEditor(KoSegmentGradient* gradient, QWidget *parent, const char* name, const QString& caption);
+ KisAutogradientEditor(KoSegmentGradientSP gradient, QWidget *parent, const char* name, const QString& caption);
void activate();
private:
- KoSegmentGradient* m_autogradientResource;
+ KoSegmentGradientSP m_autogradientResource;
private Q_SLOTS:
void slotSelectedSegment(KoGradientSegment* segment);
void slotChangedSegment(KoGradientSegment* segment);
void slotChangedInterpolation(int type);
void slotChangedColorInterpolation(int type);
void slotChangedLeftColor(const KoColor& color);
void slotChangedRightColor(const KoColor& color);
void slotChangedLeftOpacity(int value);
void slotChangedRightOpacity(int value);
void slotChangedName();
void paramChanged();
};
#endif
diff --git a/libs/ui/kis_bookmarked_filter_configurations_model.cc b/libs/ui/kis_bookmarked_filter_configurations_model.cc
index c7ae34d579..798b5c4fdc 100644
--- a/libs/ui/kis_bookmarked_filter_configurations_model.cc
+++ b/libs/ui/kis_bookmarked_filter_configurations_model.cc
@@ -1,59 +1,60 @@
/*
* Copyright (c) 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.
*/
#include "kis_bookmarked_filter_configurations_model.h"
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_paint_device.h>
+#include <KisGlobalResourcesInterface.h>
struct KisBookmarkedFilterConfigurationsModel::Private {
KisPaintDeviceSP thumb;
KisFilterSP filter;
};
KisBookmarkedFilterConfigurationsModel::KisBookmarkedFilterConfigurationsModel(KisPaintDeviceSP thumb, KisFilterSP filter)
: KisBookmarkedConfigurationsModel(filter->bookmarkManager()), d(new Private)
{
d->thumb = thumb;
d->filter = filter;
}
KisBookmarkedFilterConfigurationsModel::~KisBookmarkedFilterConfigurationsModel()
{
delete d;
}
QVariant KisBookmarkedFilterConfigurationsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
return KisBookmarkedConfigurationsModel::data(index, role);
}
KisFilterConfigurationSP KisBookmarkedFilterConfigurationsModel::configuration(const QModelIndex &index) const
{
KisFilterConfigurationSP config = dynamic_cast<KisFilterConfiguration*>(KisBookmarkedConfigurationsModel::configuration(index).data());
if (config) return config;
- return d->filter->defaultConfiguration();
+ return d->filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
}
diff --git a/libs/ui/kis_canvas_resource_provider.cpp b/libs/ui/kis_canvas_resource_provider.cpp
index 3405048221..7689a6b282 100644
--- a/libs/ui/kis_canvas_resource_provider.cpp
+++ b/libs/ui/kis_canvas_resource_provider.cpp
@@ -1,563 +1,567 @@
/*
* 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_canvas_resource_provider.h"
#include <QImage>
#include <QPainter>
#include <KoCanvasBase.h>
#include <KoID.h>
#include <KoColorModelStandardIds.h>
#include <KoColorProfile.h>
#include <resources/KoAbstractGradient.h>
#include <KoCompositeOpRegistry.h>
#include <KoResourceServerProvider.h>
#include <resources/KoStopGradient.h>
#include <KoColorSpaceRegistry.h>
#include <resources/KoPattern.h>
#include <kis_paint_device.h>
#include <filter/kis_filter_configuration.h>
#include <kis_image.h>
#include <kis_group_layer.h>
#include <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_settings.h>
#include "kis_favorite_resource_manager.h"
#include "kis_config.h"
#include "KisViewManager.h"
#include "canvas/kis_canvas2.h"
KisCanvasResourceProvider::KisCanvasResourceProvider(KisViewManager * view)
: m_view(view)
{
m_fGChanged = true;
m_enablefGChange = true; // default to true, so that colour history is working without popup palette
}
KisCanvasResourceProvider::~KisCanvasResourceProvider()
{
disconnect(); // in case Qt gets confused
}
KoCanvasResourceProvider* KisCanvasResourceProvider::resourceManager()
{
return m_resourceManager;
}
void KisCanvasResourceProvider::setResourceManager(KoCanvasResourceProvider *resourceManager)
{
m_resourceManager = resourceManager;
QVariant v;
v.setValue(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8()));
m_resourceManager->setResource(KoCanvasResourceProvider::ForegroundColor, v);
v.setValue(KoColor(Qt::white, KoColorSpaceRegistry::instance()->rgb8()));
m_resourceManager->setResource(KoCanvasResourceProvider::BackgroundColor, v);
setCurrentCompositeOp(COMPOSITE_OVER);
setMirrorHorizontal(false);
setMirrorVertical(false);
m_resourceManager->setResource(HdrExposure, 0.0);
m_resourceManager->setResource(HdrGamma, 1.0);
m_resourceManager->setResource(EffectiveZoom, 1.0);
connect(m_resourceManager, SIGNAL(canvasResourceChanged(int,QVariant)),
this, SLOT(slotCanvasResourceChanged(int,QVariant)));
m_resourceManager->setResource(KoCanvasResourceProvider::ApplicationSpeciality, KoCanvasResourceProvider::NoAdvancedText);
m_resourceManager->setResource(GamutMaskActive, false);
}
KoCanvasBase * KisCanvasResourceProvider::canvas() const
{
return m_view->canvasBase();
}
KoColor KisCanvasResourceProvider::bgColor() const
{
return m_resourceManager->resource(KoCanvasResourceProvider::BackgroundColor).value<KoColor>();
}
KoColor KisCanvasResourceProvider::fgColor() const
{
return m_resourceManager->resource(KoCanvasResourceProvider::ForegroundColor).value<KoColor>();
}
float KisCanvasResourceProvider::HDRExposure() const
{
return static_cast<float>(m_resourceManager->resource(HdrExposure).toDouble());
}
void KisCanvasResourceProvider::setHDRExposure(float exposure)
{
m_resourceManager->setResource(HdrExposure, static_cast<double>(exposure));
}
float KisCanvasResourceProvider::HDRGamma() const
{
return static_cast<float>(m_resourceManager->resource(HdrGamma).toDouble());
}
void KisCanvasResourceProvider::setHDRGamma(float gamma)
{
m_resourceManager->setResource(HdrGamma, static_cast<double>(gamma));
}
-KoPattern * KisCanvasResourceProvider::currentPattern() const
+KoPatternSP KisCanvasResourceProvider::currentPattern() const
{
if (m_resourceManager->hasResource(CurrentPattern)) {
- return m_resourceManager->resource(CurrentPattern).value<KoPattern*>();
+ return m_resourceManager->resource(CurrentPattern).value<KoPatternSP>();
}
else {
return 0;
}
}
-KoAbstractGradient* KisCanvasResourceProvider::currentGradient() const
+KoAbstractGradientSP KisCanvasResourceProvider::currentGradient() const
{
if (m_resourceManager->hasResource(CurrentGradient)) {
- return m_resourceManager->resource(CurrentGradient).value<KoAbstractGradient*>();
+ return m_resourceManager->resource(CurrentGradient).value<KoAbstractGradientSP>();
}
else {
return 0;
}
}
KisImageWSP KisCanvasResourceProvider::currentImage() const
{
return m_view->image();
}
KisNodeSP KisCanvasResourceProvider::currentNode() const
{
return m_view->activeNode();
}
-KoGamutMask *KisCanvasResourceProvider::currentGamutMask() const
+KoGamutMaskSP KisCanvasResourceProvider::currentGamutMask() const
{
if (m_resourceManager->hasResource(CurrentGamutMask)) {
- return m_resourceManager->resource(CurrentGamutMask).value<KoGamutMask*>();
+ return m_resourceManager->resource(CurrentGamutMask).value<KoGamutMaskSP>();
}
else {
return nullptr;
}
}
bool KisCanvasResourceProvider::gamutMaskActive() const
{
return m_resourceManager->resource(GamutMaskActive).toBool();
}
KisPaintOpPresetSP KisCanvasResourceProvider::currentPreset() const
{
KisPaintOpPresetSP preset = m_resourceManager->resource(CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
return preset;
}
void KisCanvasResourceProvider::setPaintOpPreset(const KisPaintOpPresetSP preset)
{
Q_ASSERT(preset->valid());
Q_ASSERT(!preset->paintOp().id().isEmpty());
Q_ASSERT(preset->settings());
if (!preset) return;
dbgUI << "setPaintOpPreset" << preset->paintOp();
QVariant v;
v.setValue(preset);
m_resourceManager->setResource(CurrentPaintOpPreset, v);
}
KisPaintOpPresetSP KisCanvasResourceProvider::previousPreset() const
{
KisPaintOpPresetSP preset = m_resourceManager->resource(PreviousPaintOpPreset).value<KisPaintOpPresetSP>();
return preset;
}
void KisCanvasResourceProvider::setPreviousPaintOpPreset(const KisPaintOpPresetSP preset)
{
Q_ASSERT(preset->valid());
Q_ASSERT(!preset->paintOp().id().isEmpty());
Q_ASSERT(preset->settings());
if (!preset) return;
dbgUI << "setPreviousPaintOpPreset" << preset->paintOp();
QVariant v;
v.setValue(preset);
m_resourceManager->setResource(PreviousPaintOpPreset, v);
}
-void KisCanvasResourceProvider::slotPatternActivated(KoResource * res)
+void KisCanvasResourceProvider::slotPatternActivated(KoResourceSP res)
{
- KoPattern *pattern = dynamic_cast<KoPattern*>(res);
+ KoPatternSP pattern = res.dynamicCast<KoPattern>();
QVariant v;
- v.setValue<KoPattern*>(pattern);
+ v.setValue<KoPatternSP>(pattern);
m_resourceManager->setResource(CurrentPattern, v);
emit sigPatternChanged(pattern);
}
-void KisCanvasResourceProvider::slotGradientActivated(KoResource *res)
+void KisCanvasResourceProvider::slotGradientActivated(KoResourceSP res)
{
- KoAbstractGradient * gradient = dynamic_cast<KoAbstractGradient*>(res);
+ KoAbstractGradientSP gradient = res.dynamicCast<KoAbstractGradient>();
QVariant v;
- v.setValue<KoAbstractGradient*>(gradient);
+ v.setValue<KoAbstractGradientSP>(gradient);
m_resourceManager->setResource(CurrentGradient, v);
emit sigGradientChanged(gradient);
}
void KisCanvasResourceProvider::setBGColor(const KoColor& c)
{
QVariant v;
v.setValue(c);
m_resourceManager->setResource(KoCanvasResourceProvider::BackgroundColor, v);
emit sigBGColorChanged(c);
}
void KisCanvasResourceProvider::setFGColor(const KoColor& c)
{
m_fGChanged = true;
QVariant v;
v.setValue(c);
m_resourceManager->setResource(KoCanvasResourceProvider::ForegroundColor, v);
emit sigFGColorChanged(c);
}
void KisCanvasResourceProvider::slotSetFGColor(const KoColor& c)
{
setFGColor(c);
}
void KisCanvasResourceProvider::slotSetBGColor(const KoColor& c)
{
setBGColor(c);
}
void KisCanvasResourceProvider::slotNodeActivated(const KisNodeSP node)
{
QVariant v;
v.setValue(KisNodeWSP(node));
m_resourceManager->setResource(CurrentKritaNode, v);
emit sigNodeChanged(currentNode());
}
void KisCanvasResourceProvider::slotImageSizeChanged()
{
if (KisImageWSP image = m_view->image()) {
float fw = image->width() / image->xRes();
float fh = image->height() / image->yRes();
QSizeF postscriptSize(fw, fh);
m_resourceManager->setResource(KoCanvasResourceProvider::PageSize, postscriptSize);
}
}
void KisCanvasResourceProvider::slotOnScreenResolutionChanged()
{
KisImageWSP image = m_view->image();
KisCanvas2 *canvas = m_view->canvasBase();
if(!image || !canvas) return;
qreal zoomX, zoomY;
canvas->coordinatesConverter()->zoom(&zoomX, &zoomY);
qreal scaleX = zoomX / image->xRes();
qreal scaleY = zoomY / image->yRes();
emit sigOnScreenResolutionChanged(scaleX, scaleY);
}
void KisCanvasResourceProvider::slotCanvasResourceChanged(int key, const QVariant & res)
{
- if(key == KoCanvasResourceProvider::ForegroundColor || key == KoCanvasResourceProvider::BackgroundColor) {
- KoAbstractGradient* resource = KoResourceServerProvider::instance()->gradientServer()->resources()[0];
- KoStopGradient* stopGradient = dynamic_cast<KoStopGradient*>(resource);
- if(stopGradient) {
- QList<KoGradientStop> stops;
- stops << KoGradientStop(0.0, fgColor()) << KoGradientStop(1.0, KoColor(QColor(0, 0, 0, 0), fgColor().colorSpace()));
- stopGradient->setStops(stops);
- KoResourceServerProvider::instance()->gradientServer()->updateResource(resource);
+ if (key == KoCanvasResourceProvider::ForegroundColor || key == KoCanvasResourceProvider::BackgroundColor) {
+ KoAbstractGradientSP resource = KoResourceServerProvider::instance()->gradientServer()->resourceByFilename("Foreground to Background");
+ if (resource) {
+ KoStopGradientSP stopGradient = resource.dynamicCast<KoStopGradient>();
+ if (stopGradient) {
+ QList<KoGradientStop> stops;
+ stops << KoGradientStop(0.0, fgColor()) << KoGradientStop(1.0, KoColor(QColor(0, 0, 0, 0), fgColor().colorSpace()));
+ stopGradient->setStops(stops);
+ KoResourceServerProvider::instance()->gradientServer()->updateResource(resource);
+ }
}
- resource = KoResourceServerProvider::instance()->gradientServer()->resources()[1];
- stopGradient = dynamic_cast<KoStopGradient*>(resource);
- if(stopGradient) {
- QList<KoGradientStop> stops;
- stops << KoGradientStop(0.0, fgColor()) << KoGradientStop(1.0, bgColor());
- stopGradient->setStops(stops);
- KoResourceServerProvider::instance()->gradientServer()->updateResource(resource);
+ resource = KoResourceServerProvider::instance()->gradientServer()->resourceByFilename("Foreground to Transparent");
+ if (resource){
+ KoStopGradientSP stopGradient = resource.dynamicCast<KoStopGradient>();
+ if (stopGradient) {
+ QList<KoGradientStop> stops;
+ stops << KoGradientStop(0.0, fgColor()) << KoGradientStop(1.0, bgColor());
+ stopGradient->setStops(stops);
+ KoResourceServerProvider::instance()->gradientServer()->updateResource(resource);
+ }
}
}
switch (key) {
case(KoCanvasResourceProvider::ForegroundColor):
m_fGChanged = true;
emit sigFGColorChanged(res.value<KoColor>());
break;
case(KoCanvasResourceProvider::BackgroundColor):
emit sigBGColorChanged(res.value<KoColor>());
break;
case(CurrentPattern):
- emit sigPatternChanged(static_cast<KoPattern *>(res.value<void *>()));
+ emit sigPatternChanged(res.value<KoPatternSP>());
break;
case(CurrentGradient):
- emit sigGradientChanged(static_cast<KoAbstractGradient *>(res.value<void *>()));
+ emit sigGradientChanged(res.value<KoAbstractGradientSP>());
break;
case(CurrentKritaNode) :
emit sigNodeChanged(currentNode());
break;
case (Opacity):
{
emit sigOpacityChanged(res.toDouble());
}
default:
;
// Do nothing
};
}
void KisCanvasResourceProvider::setCurrentCompositeOp(const QString& compositeOp)
{
m_resourceManager->setResource(CurrentCompositeOp,
QVariant::fromValue(compositeOp));
}
QString KisCanvasResourceProvider::currentCompositeOp() const
{
return m_resourceManager->resource(CurrentCompositeOp).value<QString>();
}
bool KisCanvasResourceProvider::eraserMode() const
{
return m_resourceManager->resource(EraserMode).toBool();
}
void KisCanvasResourceProvider::setEraserMode(bool value)
{
m_resourceManager->setResource(EraserMode,
QVariant::fromValue(value));
}
void KisCanvasResourceProvider::slotPainting()
{
if (m_fGChanged && m_enablefGChange) {
emit sigFGColorUsed(fgColor());
m_fGChanged = false;
}
}
-void KisCanvasResourceProvider::slotGamutMaskActivated(KoGamutMask *mask)
+void KisCanvasResourceProvider::slotGamutMaskActivated(KoGamutMaskSP mask)
{
QVariant v;
- v.setValue<KoGamutMask*>(mask);
+ v.setValue<KoGamutMaskSP>(mask);
m_resourceManager->setResource(CurrentGamutMask, v);
m_resourceManager->setResource(GamutMaskActive, QVariant::fromValue(true));
emit sigGamutMaskChanged(mask);
}
void KisCanvasResourceProvider::slotGamutMaskUnset()
{
m_resourceManager->setResource(GamutMaskActive, QVariant::fromValue(false));
m_resourceManager->clearResource(CurrentGamutMask);
emit sigGamutMaskUnset();
}
void KisCanvasResourceProvider::slotGamutMaskPreviewUpdate()
{
emit sigGamutMaskPreviewUpdate();
}
void KisCanvasResourceProvider::slotGamutMaskDeactivate()
{
m_resourceManager->setResource(GamutMaskActive, QVariant::fromValue(false));
emit sigGamutMaskDeactivated();
}
void KisCanvasResourceProvider::slotResetEnableFGChange(bool b)
{
m_enablefGChange = b;
}
QList<QPointer<KisAbstractPerspectiveGrid> > KisCanvasResourceProvider::perspectiveGrids() const
{
return m_perspectiveGrids;
}
void KisCanvasResourceProvider::addPerspectiveGrid(KisAbstractPerspectiveGrid* grid)
{
m_perspectiveGrids.append(grid);
}
void KisCanvasResourceProvider::removePerspectiveGrid(KisAbstractPerspectiveGrid* grid)
{
m_perspectiveGrids.removeOne(grid);
}
void KisCanvasResourceProvider::clearPerspectiveGrids()
{
m_perspectiveGrids.clear();
}
void KisCanvasResourceProvider::setMirrorHorizontal(bool mirrorHorizontal)
{
m_resourceManager->setResource(MirrorHorizontal, mirrorHorizontal);
emit mirrorModeChanged();
}
bool KisCanvasResourceProvider::mirrorHorizontal() const
{
return m_resourceManager->resource(MirrorHorizontal).toBool();
}
void KisCanvasResourceProvider::setMirrorVertical(bool mirrorVertical)
{
m_resourceManager->setResource(MirrorVertical, mirrorVertical);
emit mirrorModeChanged();
}
bool KisCanvasResourceProvider::mirrorVertical() const
{
return m_resourceManager->resource(MirrorVertical).toBool();
}
void KisCanvasResourceProvider::setMirrorHorizontalLock(bool isLocked)
{
m_resourceManager->setResource(MirrorHorizontalLock, isLocked);
emit mirrorModeChanged();
}
bool KisCanvasResourceProvider::mirrorHorizontalLock() {
return m_resourceManager->resource(MirrorHorizontalLock).toBool();
}
void KisCanvasResourceProvider::setMirrorVerticalLock(bool isLocked)
{
m_resourceManager->setResource(MirrorVerticalLock, isLocked);
emit mirrorModeChanged();
}
bool KisCanvasResourceProvider::mirrorVerticalHideDecorations() {
return m_resourceManager->resource(MirrorVerticalHideDecorations).toBool();
}
void KisCanvasResourceProvider::setMirrorVerticalHideDecorations(bool hide)
{
m_resourceManager->setResource(MirrorVerticalHideDecorations, hide);
emit mirrorModeChanged();
}
bool KisCanvasResourceProvider::mirrorHorizontalHideDecorations() {
return m_resourceManager->resource(MirrorHorizontalHideDecorations).toBool();
}
void KisCanvasResourceProvider::setMirrorHorizontalHideDecorations(bool hide)
{
m_resourceManager->setResource(MirrorHorizontalHideDecorations, hide);
emit mirrorModeChanged();
}
bool KisCanvasResourceProvider::mirrorVerticalLock() {
return m_resourceManager->resource(MirrorVerticalLock).toBool();
}
void KisCanvasResourceProvider::mirrorVerticalMoveCanvasToCenter() {
emit moveMirrorVerticalCenter();
}
void KisCanvasResourceProvider::mirrorHorizontalMoveCanvasToCenter() {
emit moveMirrorHorizontalCenter();
}
void KisCanvasResourceProvider::setOpacity(qreal opacity)
{
m_resourceManager->setResource(Opacity, opacity);
}
qreal KisCanvasResourceProvider::opacity() const
{
return m_resourceManager->resource(Opacity).toReal();
}
void KisCanvasResourceProvider::setFlow(qreal flow)
{
m_resourceManager->setResource(Flow, flow);
}
qreal KisCanvasResourceProvider::flow() const
{
return m_resourceManager->resource(Flow).toReal();
}
void KisCanvasResourceProvider::setSize(qreal size)
{
m_resourceManager->setResource(Size, size);
}
qreal KisCanvasResourceProvider::size() const
{
return m_resourceManager->resource(Size).toReal();
}
void KisCanvasResourceProvider::setGlobalAlphaLock(bool lock)
{
m_resourceManager->setResource(GlobalAlphaLock, lock);
}
bool KisCanvasResourceProvider::globalAlphaLock() const
{
return m_resourceManager->resource(GlobalAlphaLock).toBool();
}
void KisCanvasResourceProvider::setDisablePressure(bool value)
{
m_resourceManager->setResource(DisablePressure, value);
}
bool KisCanvasResourceProvider::disablePressure() const
{
return m_resourceManager->resource(DisablePressure).toBool();
}
-void KisCanvasResourceProvider::notifyLoadingWorkspace(KisWorkspaceResource* workspace)
+void KisCanvasResourceProvider::notifyLoadingWorkspace(KisWorkspaceResourceSP workspace)
{
emit sigLoadingWorkspace(workspace);
}
-void KisCanvasResourceProvider::notifySavingWorkspace(KisWorkspaceResource* workspace)
+void KisCanvasResourceProvider::notifySavingWorkspace(KisWorkspaceResourceSP workspace)
{
emit sigSavingWorkspace(workspace);
}
diff --git a/libs/ui/kis_canvas_resource_provider.h b/libs/ui/kis_canvas_resource_provider.h
index ebad9a0e18..2772f5342a 100644
--- a/libs/ui/kis_canvas_resource_provider.h
+++ b/libs/ui/kis_canvas_resource_provider.h
@@ -1,250 +1,253 @@
/*
* 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_CANVAS_RESOURCE_PROVIDER_H_
#define KIS_CANVAS_RESOURCE_PROVIDER_H_
#include <QObject>
#include <KoColor.h>
#include <KoID.h>
#include <KoCanvasResourceProvider.h>
+#include <KoResource.h>
#include "kis_types.h"
#include "kritaui_export.h"
-class KisWorkspaceResource;
+#include <KoPattern.h>
+#include <KoAbstractGradient.h>
+#include <resources/KoGamutMask.h>
+#include <kis_workspace_resource.h>
+
class KoColorProfile;
class KoAbstractGradient;
-class KoResource;
class KoCanvasBase;
class KisViewManager;
-class KoPattern;
-class KoGamutMask;
+
class KisFilterConfiguration;
#include <kis_abstract_perspective_grid.h>
/**
* KisCanvasResourceProvider contains the per-window current settings that
* influence painting, like paintop, color, gradients and so on.
*/
class KRITAUI_EXPORT KisCanvasResourceProvider : public QObject
{
Q_OBJECT
public:
enum Resources {
HdrExposure = KoCanvasResourceProvider::KritaStart + 1,
CurrentPattern,
CurrentGamutMask,
GamutMaskActive,
CurrentGradient,
CurrentDisplayProfile,
CurrentKritaNode,
CurrentPaintOpPreset,
CurrentGeneratorConfiguration,
CurrentCompositeOp,
CurrentEffectiveCompositeOp,
LodAvailability, ///<-user choice
LodSizeThreshold, ///<-user choice
LodSizeThresholdSupported, ///<-paintop property
EffectiveLodAvailablility, ///<- a superposition of user choice, threshold and paintop traits
EraserMode,
MirrorHorizontal,
MirrorVertical,
MirrorHorizontalLock,
MirrorVerticalLock,
MirrorVerticalHideDecorations,
MirrorHorizontalHideDecorations,
Opacity,
Flow,
Size,
HdrGamma,
GlobalAlphaLock,
DisablePressure,
PreviousPaintOpPreset,
EffectiveZoom ///<-Used only by painting tools for non-displaying purposes
};
KisCanvasResourceProvider(KisViewManager * view);
~KisCanvasResourceProvider() override;
void setResourceManager(KoCanvasResourceProvider *resourceManager);
KoCanvasResourceProvider* resourceManager();
KoCanvasBase * canvas() const;
KoColor bgColor() const;
void setBGColor(const KoColor& c);
KoColor fgColor() const;
void setFGColor(const KoColor& c);
float HDRExposure() const;
void setHDRExposure(float exposure);
float HDRGamma() const;
void setHDRGamma(float gamma);
bool eraserMode() const;
void setEraserMode(bool value);
- KoPattern *currentPattern() const;
+ KoPatternSP currentPattern() const;
- KoAbstractGradient *currentGradient() const;
+ KoAbstractGradientSP currentGradient() const;
KisImageWSP currentImage() const;
KisNodeSP currentNode() const;
- KoGamutMask* currentGamutMask() const;
+ KoGamutMaskSP currentGamutMask() const;
bool gamutMaskActive() const;
KisPaintOpPresetSP currentPreset() const;
void setPaintOpPreset(const KisPaintOpPresetSP preset);
KisPaintOpPresetSP previousPreset() const;
void setPreviousPaintOpPreset(const KisPaintOpPresetSP preset);
void setCurrentCompositeOp(const QString& compositeOp);
QString currentCompositeOp() const;
QList<QPointer<KisAbstractPerspectiveGrid> > perspectiveGrids() const;
void addPerspectiveGrid(KisAbstractPerspectiveGrid*);
void removePerspectiveGrid(KisAbstractPerspectiveGrid*);
void clearPerspectiveGrids();
void setMirrorHorizontal(bool mirrorHorizontal);
bool mirrorHorizontal() const;
void setMirrorVertical(bool mirrorVertical);
bool mirrorVertical() const;
// options for horizontal and vertical mirror toolbar
void setMirrorHorizontalLock(bool isLocked);
bool mirrorHorizontalLock();
void setMirrorVerticalLock(bool isLocked);
bool mirrorVerticalLock();
void setMirrorVerticalHideDecorations(bool hide);
bool mirrorVerticalHideDecorations();
void setMirrorHorizontalHideDecorations(bool hide);
bool mirrorHorizontalHideDecorations();
void mirrorVerticalMoveCanvasToCenter();
void mirrorHorizontalMoveCanvasToCenter();
void setOpacity(qreal opacity);
qreal opacity() const;
void setFlow(qreal opacity);
qreal flow() const;
void setSize(qreal size);
qreal size() const;
void setGlobalAlphaLock(bool lock);
bool globalAlphaLock() const;
void setDisablePressure(bool value);
bool disablePressure() const;
///Notify that the workspace is saved and settings should be saved to it
- void notifySavingWorkspace(KisWorkspaceResource* workspace);
+ void notifySavingWorkspace(KisWorkspaceResourceSP workspace);
///Notify that the workspace is loaded and settings can be read
- void notifyLoadingWorkspace(KisWorkspaceResource* workspace);
+ void notifyLoadingWorkspace(KisWorkspaceResourceSP workspace);
public Q_SLOTS:
void slotSetFGColor(const KoColor& c);
void slotSetBGColor(const KoColor& c);
- void slotPatternActivated(KoResource *pattern);
- void slotGradientActivated(KoResource *gradient);
+ void slotPatternActivated(KoResourceSP pattern);
+ void slotGradientActivated(KoResourceSP gradient);
void slotNodeActivated(const KisNodeSP node);
void slotPainting();
- void slotGamutMaskActivated(KoGamutMask* mask);
+ void slotGamutMaskActivated(KoGamutMaskSP mask);
void slotGamutMaskUnset();
void slotGamutMaskPreviewUpdate();
void slotGamutMaskDeactivate();
/**
* Set the image size in pixels. The resource provider will store
* the image size in postscript points.
*/
// FIXME: this slot doesn't catch the case when image resolution is changed
void slotImageSizeChanged();
void slotOnScreenResolutionChanged();
// This is a flag to handle a bug:
// If pop up palette is visible and a new colour is selected, the new colour
// will be added when the user clicks on the canvas to hide the palette
// In general, we want to be able to store recent color if the pop up palette
// is not visible
void slotResetEnableFGChange(bool);
private Q_SLOTS:
void slotCanvasResourceChanged(int key, const QVariant & res);
Q_SIGNALS:
void sigFGColorChanged(const KoColor &);
void sigBGColorChanged(const KoColor &);
- void sigGradientChanged(KoAbstractGradient *);
- void sigPatternChanged(KoPattern *);
+ void sigGradientChanged(KoAbstractGradientSP);
+ void sigPatternChanged(KoPatternSP);
void sigNodeChanged(const KisNodeSP);
void sigDisplayProfileChanged(const KoColorProfile *);
void sigFGColorUsed(const KoColor&);
void sigOnScreenResolutionChanged(qreal scaleX, qreal scaleY);
void sigOpacityChanged(qreal);
- void sigSavingWorkspace(KisWorkspaceResource* workspace);
- void sigLoadingWorkspace(KisWorkspaceResource* workspace);
+ void sigSavingWorkspace(KisWorkspaceResourceSP workspace);
+ void sigLoadingWorkspace(KisWorkspaceResourceSP workspace);
void mirrorModeChanged();
void moveMirrorVerticalCenter();
void moveMirrorHorizontalCenter();
- void sigGamutMaskChanged(KoGamutMask* mask);
+ void sigGamutMaskChanged(KoGamutMaskSP mask);
void sigGamutMaskUnset();
void sigGamutMaskPreviewUpdate();
void sigGamutMaskDeactivated();
private:
KisViewManager * m_view;
KoCanvasResourceProvider *m_resourceManager;
bool m_fGChanged;
QList<QPointer<KisAbstractPerspectiveGrid> > m_perspectiveGrids;
// This is a flag to handle a bug:
// If pop up palette is visible and a new colour is selected, the new colour
// will be added when the user clicks on the canvas to hide the palette
// In general, we want to be able to store recent color if the pop up palette
// is not visible
bool m_enablefGChange;
};
#endif
diff --git a/libs/ui/kis_control_frame.cpp b/libs/ui/kis_control_frame.cpp
index 765bc074fa..acad0eff94 100644
--- a/libs/ui/kis_control_frame.cpp
+++ b/libs/ui/kis_control_frame.cpp
@@ -1,239 +1,246 @@
/*
* kis_control_frame.cc - part of Krita
*
* Copyright (c) 1999 Matthias Elter <elter@kde.org>
* Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Sven Langkamp <sven.langkamp@gmail.com>
* 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.g
*
* 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_control_frame.h"
#include <stdlib.h>
#include <QApplication>
#include <QLayout>
#include <QTabWidget>
#include <QFrame>
#include <QWidget>
#include <QEvent>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QMenu>
#include <QWidgetAction>
#include <QFontDatabase>
#include <klocalizedstring.h>
#include <QAction>
#include <kactioncollection.h>
#include <KoDualColorButton.h>
#include <resources/KoAbstractGradient.h>
#include <KoResourceServer.h>
-#include <KoResourceServerAdapter.h>
#include <KoResourceServerProvider.h>
#include <KoColorSpaceRegistry.h>
#include <kis_image.h>
#include <resources/KoPattern.h>
#include "KisResourceServerProvider.h"
#include "kis_canvas_resource_provider.h"
#include "widgets/kis_iconwidget.h"
#include "widgets/kis_gradient_chooser.h"
#include "KisViewManager.h"
#include "kis_config.h"
#include "kis_paintop_box.h"
#include "kis_custom_pattern.h"
#include "widgets/kis_pattern_chooser.h"
#include "kis_favorite_resource_manager.h"
#include "kis_display_color_converter.h"
#include <kis_canvas2.h>
KisControlFrame::KisControlFrame(KisViewManager *view, QWidget *parent, const char* name)
: QObject(view)
, m_viewManager(view)
, m_patternWidget(0)
, m_gradientWidget(0)
, m_patternChooserPopup(0)
, m_gradientChooserPopup(0)
, m_paintopBox(0)
{
setObjectName(name);
m_font = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
- m_patternWidget = new KisIconWidget(parent, "patterns");
+ m_patternWidget = new KisIconWidget(parent, ResourceType::Patterns);
m_patternWidget->setToolTip(i18n("Fill Patterns"));
m_patternWidget->setFixedSize(32, 32);
- m_gradientWidget = new KisIconWidget(parent, "gradients");
- m_gradientWidget->setToolTip(i18n("Gradients"));
+ m_gradientWidget = new KisIconWidget(parent, ResourceType::Gradients);
+ m_gradientWidget->setToolTip(i18n("Fill Gradients"));
m_gradientWidget->setFixedSize(32, 32);
}
void KisControlFrame::setup(QWidget *parent)
{
createPatternsChooser(m_viewManager);
createGradientsChooser(m_viewManager);
QWidgetAction *action = new QWidgetAction(this);
action->setText(i18n("&Patterns"));
- m_viewManager->actionCollection()->addAction("patterns", action);
+ m_viewManager->actionCollection()->addAction(ResourceType::Patterns, action);
action->setDefaultWidget(m_patternWidget);
action = new QWidgetAction(this);
action->setText(i18n("&Gradients"));
- m_viewManager->actionCollection()->addAction("gradients", action);
+ m_viewManager->actionCollection()->addAction(ResourceType::Gradients, action);
action->setDefaultWidget(m_gradientWidget);
// XXX: KOMVC we don't have a canvas here yet, needs a setImageView
const KoColorDisplayRendererInterface *displayRenderer = \
KisDisplayColorConverter::dumbConverterInstance()->displayRendererInterface();
m_dual = new KoDualColorButton(m_viewManager->canvasResourceProvider()->fgColor(),
m_viewManager->canvasResourceProvider()->bgColor(), displayRenderer,
m_viewManager->mainWindow(), m_viewManager->mainWindow());
m_dual->setPopDialog(true);
action = new QWidgetAction(this);
action->setText(i18n("&Color"));
m_viewManager->actionCollection()->addAction("dual", action);
action->setDefaultWidget(m_dual);
connect(m_dual, SIGNAL(foregroundColorChanged(KoColor)), m_viewManager->canvasResourceProvider(), SLOT(slotSetFGColor(KoColor)));
connect(m_dual, SIGNAL(backgroundColorChanged(KoColor)), m_viewManager->canvasResourceProvider(), SLOT(slotSetBGColor(KoColor)));
connect(m_viewManager->canvasResourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), m_dual, SLOT(setForegroundColor(KoColor)));
connect(m_viewManager->canvasResourceProvider(), SIGNAL(sigBGColorChanged(KoColor)), m_dual, SLOT(setBackgroundColor(KoColor)));
connect(m_viewManager->canvasResourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), m_gradientWidget, SLOT(update()));
connect(m_viewManager->canvasResourceProvider(), SIGNAL(sigBGColorChanged(KoColor)), m_gradientWidget, SLOT(update()));
m_dual->setFixedSize(28, 28);
connect(m_viewManager, SIGNAL(viewChanged()), SLOT(slotUpdateDisplayRenderer()));
m_paintopBox = new KisPaintopBox(m_viewManager, parent, "paintopbox");
action = new QWidgetAction(this);
action->setText(i18n("&Painter's Tools"));
m_viewManager->actionCollection()->addAction("paintops", action);
action->setDefaultWidget(m_paintopBox);
}
void KisControlFrame::slotUpdateDisplayRenderer()
{
if (m_viewManager->canvasBase()){
m_dual->setDisplayRenderer(m_viewManager->canvasBase()->displayColorConverter()->displayRendererInterface());
m_dual->setColorSpace(m_viewManager->canvasBase()->image()->colorSpace());
m_viewManager->canvasBase()->image()->disconnect(m_dual);
connect(m_viewManager->canvasBase()->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), m_dual, SLOT(setColorSpace(const KoColorSpace*)), Qt::UniqueConnection);
} else if (m_viewManager->viewCount()==0) {
m_dual->setDisplayRenderer();
}
}
-void KisControlFrame::slotSetPattern(KoPattern * pattern)
+void KisControlFrame::slotSetPattern(KoPatternSP pattern)
{
- m_patternWidget->setResource(pattern);
+ m_patternWidget->setThumbnail(pattern->image());
m_patternChooser->setCurrentPattern(pattern);
}
-void KisControlFrame::slotSetGradient(KoAbstractGradient * gradient)
+void KisControlFrame::slotSetGradient(KoAbstractGradientSP gradient)
{
- m_gradientWidget->setResource(gradient);
+ m_gradientWidget->setThumbnail(gradient->image());
}
void KisControlFrame::createPatternsChooser(KisViewManager * view)
{
if (m_patternChooserPopup) delete m_patternChooserPopup;
m_patternChooserPopup = new QWidget(m_patternWidget);
m_patternChooserPopup->setObjectName("pattern_chooser_popup");
QHBoxLayout * l2 = new QHBoxLayout(m_patternChooserPopup);
l2->setObjectName("patternpopuplayout");
m_patternsTab = new QTabWidget(m_patternChooserPopup);
m_patternsTab->setObjectName("patternstab");
m_patternsTab->setFocusPolicy(Qt::NoFocus);
m_patternsTab->setFont(m_font);
l2->addWidget(m_patternsTab);
m_patternChooser = new KisPatternChooser(m_patternChooserPopup);
m_patternChooser->setFont(m_font);
QWidget *patternChooserPage = new QWidget(m_patternChooserPopup);
QHBoxLayout *patternChooserPageLayout = new QHBoxLayout(patternChooserPage);
patternChooserPageLayout->addWidget(m_patternChooser);
m_patternsTab->addTab(patternChooserPage, i18n("Patterns"));
KisCustomPattern* customPatterns = new KisCustomPattern(0, "custompatterns",
i18n("Custom Pattern"), m_viewManager);
customPatterns->setFont(m_font);
m_patternsTab->addTab(customPatterns, i18n("Custom Pattern"));
- connect(m_patternChooser, SIGNAL(resourceSelected(KoResource*)),
- view->canvasResourceProvider(), SLOT(slotPatternActivated(KoResource*)));
+ connect(m_patternChooser, SIGNAL(resourceSelected(KoResourceSP )),
+ view->canvasResourceProvider(), SLOT(slotPatternActivated(KoResourceSP )));
- connect(customPatterns, SIGNAL(activatedResource(KoResource*)),
- view->canvasResourceProvider(), SLOT(slotPatternActivated(KoResource*)));
+ connect(customPatterns, SIGNAL(activatedResource(KoResourceSP )),
+ view->canvasResourceProvider(), SLOT(slotPatternActivated(KoResourceSP )));
- connect(view->canvasResourceProvider(), SIGNAL(sigPatternChanged(KoPattern*)),
- this, SLOT(slotSetPattern(KoPattern*)));
+ connect(view->canvasResourceProvider(), SIGNAL(sigPatternChanged(KoPatternSP)),
+ this, SLOT(slotSetPattern(KoPatternSP)));
- m_patternChooser->setCurrentItem(0, 0);
+ m_patternChooser->setCurrentItem(0);
if (m_patternChooser->currentResource() && view->canvasResourceProvider()) {
view->canvasResourceProvider()->slotPatternActivated(m_patternChooser->currentResource());
}
m_patternWidget->setPopupWidget(m_patternChooserPopup);
}
void KisControlFrame::createGradientsChooser(KisViewManager * view)
{
if (m_gradientChooserPopup) {
delete m_gradientChooserPopup;
m_gradientChooserPopup = 0;
}
m_gradientChooserPopup = new QWidget(m_gradientWidget);
m_gradientChooserPopup->setObjectName("gradient_chooser_popup");
QHBoxLayout * l2 = new QHBoxLayout(m_gradientChooserPopup);
l2->setObjectName("gradientpopuplayout");
m_gradientTab = new QTabWidget(m_gradientChooserPopup);
m_gradientTab->setObjectName("gradientstab");
m_gradientTab->setFocusPolicy(Qt::NoFocus);
m_gradientTab->setFont(m_font);
l2->addWidget(m_gradientTab);
m_gradientChooser = new KisGradientChooser(m_gradientChooserPopup);
m_gradientChooser->setFont(m_font);
m_gradientTab->addTab(m_gradientChooser, i18n("Gradients"));
- connect(m_gradientChooser, SIGNAL(resourceSelected(KoResource*)),
- view->canvasResourceProvider(), SLOT(slotGradientActivated(KoResource*)));
+ connect(m_gradientChooser, SIGNAL(resourceSelected(KoResourceSP )),
+ view->canvasResourceProvider(), SLOT(slotGradientActivated(KoResourceSP )));
connect (view->mainWindow(), SIGNAL(themeChanged()), m_gradientChooser, SLOT(slotUpdateIcons()));
- connect(view->canvasResourceProvider(), SIGNAL(sigGradientChanged(KoAbstractGradient*)),
- this, SLOT(slotSetGradient(KoAbstractGradient*)));
+ connect(view->canvasResourceProvider(), SIGNAL(sigGradientChanged(KoAbstractGradientSP)),
+ this, SLOT(slotSetGradient(KoAbstractGradientSP)));
- m_gradientChooser->setCurrentItem(0, 0);
+ connect(m_gradientChooser, SIGNAL(resourceSelected(KoResourceSP)),
+ view->canvasResourceProvider(), SLOT(slotGradientActivated(KoResourceSP)));
+
+ connect (view->mainWindow(), SIGNAL(themeChanged()), m_gradientChooser, SLOT(slotUpdateIcons()));
+
+ connect(view->canvasResourceProvider(), SIGNAL(sigGradientChanged(KoAbstractGradientSP)),
+ this, SLOT(slotSetGradient(KoAbstractGradientSP)));
+
+ m_gradientChooser->setCurrentItem(0);
if (m_gradientChooser->currentResource() && view->canvasResourceProvider())
view->canvasResourceProvider()->slotGradientActivated(m_gradientChooser->currentResource());
m_gradientWidget->setPopupWidget(m_gradientChooserPopup);
}
diff --git a/libs/ui/kis_control_frame.h b/libs/ui/kis_control_frame.h
index 71fc9433cc..3eb3aec75d 100644
--- a/libs/ui/kis_control_frame.h
+++ b/libs/ui/kis_control_frame.h
@@ -1,95 +1,96 @@
/*
* kis_control_frame.h - part of Krita
*
* Copyright (c) 1999 Matthias Elter <elter@kde.org>
* Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Sven Langkamp <sven.langkamp@gmail.com>
* Copyright (c) 2003-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_control_frame_h__
#define __kis_control_frame_h__
#include <QMenu>
#include <QKeyEvent>
#include <QObject>
+#include <KoPattern.h>
+#include <KoAbstractGradient.h>
+
class QWidget;
class QTabWidget;
-class KoAbstractGradient;
class KisGradientChooser;
class KisPatternChooser;
class KisPaintopBox;
class KisViewManager;
class KisIconWidget;
-class KoPattern;
class KoDualColorButton;
/**
* Control Frame - status display with access to
* color selector, gradient, patterns, and paintop presets
*/
class KisControlFrame : public QObject
{
Q_OBJECT
public:
KisControlFrame(KisViewManager *view, QWidget *parent = 0, const char *name = 0);
~KisControlFrame() override {}
void setup(QWidget *parent);
KisPaintopBox* paintopBox() {
return m_paintopBox;
}
private Q_SLOTS:
- void slotSetPattern(KoPattern * pattern);
- void slotSetGradient(KoAbstractGradient * gradient);
+ void slotSetPattern(KoPatternSP pattern);
+ void slotSetGradient(KoAbstractGradientSP gradient);
void slotUpdateDisplayRenderer();
private:
void createPatternsChooser(KisViewManager * view);
void createGradientsChooser(KisViewManager * view);
private:
QFont m_font;
KisViewManager *m_viewManager;
QTabWidget *m_gradientTab;
QTabWidget *m_patternsTab;
KisIconWidget *m_patternWidget;
KisIconWidget *m_gradientWidget;
QWidget *m_patternChooserPopup;
QWidget *m_gradientChooserPopup;
KisGradientChooser *m_gradientChooser;
KisPatternChooser *m_patternChooser;
KisPaintopBox *m_paintopBox;
KoDualColorButton *m_dual;
};
#endif
diff --git a/libs/ui/kis_custom_pattern.cc b/libs/ui/kis_custom_pattern.cc
index dcb723b375..72a07c3af1 100644
--- a/libs/ui/kis_custom_pattern.cc
+++ b/libs/ui/kis_custom_pattern.cc
@@ -1,193 +1,190 @@
/*
* Copyright (c) 2006 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.
*/
#include "kis_custom_pattern.h"
#include <KoResourceServerProvider.h>
#include <resources/KoPattern.h>
#include <QLabel>
#include <QImage>
#include <QPushButton>
#include <QComboBox>
#include <QPixmap>
#include <QShowEvent>
-
+#include <QSharedPointer>
#include <QTemporaryFile>
#include "KisDocument.h"
#include "KisViewManager.h"
#include "kis_image.h"
#include "kis_layer.h"
#include "kis_paint_device.h"
#include "kis_selection.h"
#include "kis_painter.h"
#include <kis_debug.h>
#include "KisResourceServerProvider.h"
#include "kis_paint_layer.h"
KisCustomPattern::KisCustomPattern(QWidget *parent, const char* name, const QString& caption, KisViewManager* view)
- : KisWdgCustomPattern(parent, name), m_view(view)
+ : KisWdgCustomPattern(parent, name)
+ , m_view(view)
{
Q_ASSERT(m_view);
setWindowTitle(caption);
m_pattern = 0;
preview->setScaledContents(true);
- KoResourceServer<KoPattern>* rServer = KoResourceServerProvider::instance()->patternServer();
- m_rServerAdapter = QSharedPointer<KoAbstractResourceServerAdapter>(new KoResourceServerAdapter<KoPattern>(rServer));
+ m_rServer = KoResourceServerProvider::instance()->patternServer();
connect(addButton, SIGNAL(pressed()), this, SLOT(slotAddPredefined()));
connect(patternButton, SIGNAL(pressed()), this, SLOT(slotUsePattern()));
connect(updateButton, SIGNAL(pressed()), this, SLOT(slotUpdateCurrentPattern()));
connect(cmbSource, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateCurrentPattern()));
}
KisCustomPattern::~KisCustomPattern()
{
- delete m_pattern;
+ m_pattern.clear();
}
void KisCustomPattern::slotUpdateCurrentPattern()
{
- delete m_pattern;
- m_pattern = 0;
+ m_pattern.clear();
if (m_view && m_view->image()) {
createPattern();
if (m_pattern) {
const qint32 maxSize = 150;
if ((m_pattern->width() > maxSize) || (m_pattern->height() > maxSize)) {
float aspectRatio = (float)m_pattern->width() / m_pattern->height();
qint32 scaledWidth, scaledHeight;
if (m_pattern->width() > m_pattern->height()) {
scaledWidth = maxSize;
scaledHeight = maxSize / aspectRatio;
} else {
scaledWidth = maxSize * aspectRatio;
scaledHeight = maxSize;
}
if (scaledWidth == 0) scaledWidth++;
if (scaledHeight == 0) scaledHeight++;
QPixmap scaledPixmap = QPixmap::fromImage(m_pattern->pattern());
preview->setPixmap(scaledPixmap.scaled(scaledWidth, scaledHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation));
} else {
preview->setPixmap(QPixmap::fromImage(m_pattern->pattern()));
}
}
}
}
void KisCustomPattern::slotAddPredefined()
{
if (!m_pattern)
return;
// Save in the directory that is likely to be: ~/.kde/share/apps/krita/patterns
// a unique file with this pattern name
QString dir = KoResourceServerProvider::instance()->patternServer()->saveLocation();
- QString extension;
QString tempFileName;
+
+
{
- QTemporaryFile file(dir + QLatin1String("/krita_XXXXXX") + QLatin1String(".pat") );
+ QTemporaryFile file(dir + QLatin1String("/krita_XXXXXX") + m_pattern->defaultFileExtension() );
file.setAutoRemove(false);
file.open();
- tempFileName = file.fileName();
+ tempFileName = file.fileName().split("/").last();
}
// Save it to that file
m_pattern->setFilename(tempFileName);
// Add it to the pattern server, so that it automatically gets to the mediators, and
// so to the other pattern choosers can pick it up, if they want to
- m_rServerAdapter->addResource(m_pattern->clone());
+ m_rServer->addResource(m_pattern->clone().dynamicCast<KoPattern>());
}
void KisCustomPattern::slotUsePattern()
{
if (!m_pattern)
return;
- KoPattern* copy = m_pattern->clone();
-
- Q_CHECK_PTR(copy);
-
+ KoPatternSP copy = m_pattern->clone().dynamicCast<KoPattern>();
emit(activatedResource(copy));
}
void KisCustomPattern::createPattern()
{
if (!m_view) return;
KisPaintDeviceSP dev;
KisPaintDeviceSP cache;
QString name;
KisImageWSP image = m_view->image();
if (!image) return;
QRect rc = image->bounds();
if (cmbSource->currentIndex() == 0) {
dev = m_view->activeNode()->projection();
name = m_view->activeNode()->name();
QRect rc2 = dev->exactBounds();
rc = rc.intersected(rc2);
}
else {
image->barrierLock();
dev = image->projection();
image->unlock();
name = image->objectName();
}
if (!dev) return;
if(m_view->selection()) {
KisSelectionSP selection = m_view->selection();
QRect selectionRect = selection->selectedExactRect();
cache = dev->createCompositionSourceDevice();
KisPainter gc(cache);
gc.setSelection(selection);
gc.bitBlt(selectionRect.topLeft(), dev, selectionRect);
rc = selectionRect;
} else {
cache = dev;
}
if (!cache) return;
// warn when creating large patterns
QSize size = rc.size();
if (size.width() > 1000 || size.height() > 1000) {
lblWarning->setText(i18n("The current image is too big to create a pattern. "
- "The pattern will be scaled down."));
+ "The pattern will be scaled down."));
size.scale(1000, 1000, Qt::KeepAspectRatio);
}
QString dir = KoResourceServerProvider::instance()->patternServer()->saveLocation();
- m_pattern = new KoPattern(cache->createThumbnail(size.width(), size.height(), rc, /*oversample*/ 1,
- KoColorConversionTransformation::internalRenderingIntent(),
- KoColorConversionTransformation::internalConversionFlags()), name, dir);
+ m_pattern = KoPatternSP(new KoPattern(cache->createThumbnail(size.width(), size.height(), rc, /*oversample*/ 1,
+ KoColorConversionTransformation::internalRenderingIntent(),
+ KoColorConversionTransformation::internalConversionFlags()), name, dir));
}
diff --git a/libs/ui/kis_custom_pattern.h b/libs/ui/kis_custom_pattern.h
index 76545bd91f..d40849ca74 100644
--- a/libs/ui/kis_custom_pattern.h
+++ b/libs/ui/kis_custom_pattern.h
@@ -1,67 +1,68 @@
/*
* Copyright (c) 2006 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_CUSTOM_PATTERN_H_
#define KIS_CUSTOM_PATTERN_H_
#include <QObject>
#include <QShowEvent>
-#include <KoResourceServerAdapter.h>
+#include <KoResourceServer.h>
#include "ui_wdgcustompattern.h"
-class KoPattern;
-class KoResource;
+#include <KoPattern.h>
+#include <KoResource.h>
+
class KisViewManager;
class KisWdgCustomPattern : public QWidget, public Ui::KisWdgCustomPattern
{
Q_OBJECT
public:
KisWdgCustomPattern(QWidget *parent, const char *name) : QWidget(parent) {
setObjectName(name); setupUi(this);
}
};
class KisCustomPattern : public KisWdgCustomPattern
{
Q_OBJECT
public:
KisCustomPattern(QWidget *parent, const char* name, const QString& caption, KisViewManager* view);
~KisCustomPattern() override;
Q_SIGNALS:
- void activatedResource(KoResource *);
- void addPattern(KoPattern*);
+ void activatedResource(KoResourceSP);
+ void addPattern(KoPatternSP);
private Q_SLOTS:
void slotAddPredefined();
void slotUsePattern();
void slotUpdateCurrentPattern();
private:
void createPattern();
- KisViewManager* m_view;
- KoPattern* m_pattern;
- QSharedPointer<KoAbstractResourceServerAdapter> m_rServerAdapter;
+ KisViewManager* m_view {0};
+ KoPatternSP m_pattern;
+ KoResourceServer<KoPattern>* m_rServer {0};
};
#endif // KIS_CUSTOM_PATTERN_H_
diff --git a/libs/ui/kis_favorite_resource_manager.cpp b/libs/ui/kis_favorite_resource_manager.cpp
index bca8e0f365..a5da6b3ea8 100644
--- a/libs/ui/kis_favorite_resource_manager.cpp
+++ b/libs/ui/kis_favorite_resource_manager.cpp
@@ -1,354 +1,368 @@
/* This file is part of the KDE project
Copyright 2009 Vera Lukman <shicmap@gmail.com>
Copyright 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 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 <kis_debug.h>
#include <QPoint>
#include <QStringList>
#include <QString>
#include <QColor>
#include <brushengine/kis_paintop_registry.h>
#include <brushengine/kis_paintop_preset.h>
#include <KoID.h>
#include <kconfig.h>
#include "kis_favorite_resource_manager.h"
#include "kis_popup_palette.h"
#include "kis_paintop_box.h"
#include "KisViewManager.h"
#include "KisResourceServerProvider.h"
#include "kis_min_heap.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
+#include <kis_paintop_preset.h>
+
+
class KisFavoriteResourceManager::ColorDataList
{
public:
static const int MAX_RECENT_COLOR = 12;
ColorDataList() {
m_key = 0;
}
~ColorDataList() {
qDeleteAll(m_guiList);
}
int size() {
return m_guiList.size();
}
int leastUsedGuiPos() {
return findPos(m_priorityList.valueAt(0));
}
const KoColor& guiColor(int pos) {
Q_ASSERT_X(pos < size(), "ColorDataList::guiColor", "index out of bound");
Q_ASSERT_X(pos >= 0, "ColorDataList::guiColor", "negative index");
return m_guiList.at(pos)->data;
}
void append(const KoColor& data) {
int pos = findPos(data);
if (pos > -1) updateKey(pos);
else appendNew(data);
}
void appendNew(const KoColor& data) {
if (size() >= ColorDataList::MAX_RECENT_COLOR) removeLeastUsed();
PriorityNode<KoColor> * node;
node = new PriorityNode <KoColor>();
node->data = data;
node->key = m_key++;
m_priorityList.append(node);
int pos = guiInsertPos(data);
pos >= m_guiList.size() ? m_guiList.append(node)
: m_guiList.insert(pos, node);
node = 0;
}
void removeLeastUsed() {
Q_ASSERT_X(size() >= 0, "ColorDataList::removeLeastUsed", "index out of bound");
if (size() <= 0) return;
int pos = findPos(m_priorityList.valueAt(0));
m_guiList.removeAt(pos);
m_priorityList.remove(0);
}
void updateKey(int guiPos) {
if (m_guiList.at(guiPos)->key == m_key - 1) return;
m_priorityList.changeKey(m_guiList.at(guiPos)->pos, m_key++);
}
/*find position of the color on the gui list*/
int findPos(const KoColor& color) {
int low = 0, high = size(), mid = 0;
while (low < high) {
mid = (low + high) / 2;
if (hsvComparison(color, m_guiList.at(mid)->data) == 0) return mid;
else if (hsvComparison(color, m_guiList.at(mid)->data) < 0) high = mid;
else low = mid + 1;
}
return -1;
}
private:
int m_key;
int guiInsertPos(const KoColor& color) {
int low = 0, high = size() - 1, mid = (low + high) / 2;
while (low < high) {
hsvComparison(color, m_guiList[mid]->data) == -1 ? high = mid
: low = mid + 1;
mid = (low + high) / 2;
}
if (m_guiList.size() > 0) {
if (hsvComparison(color, m_guiList[mid]->data) == 1) ++mid;
}
return mid;
}
/*compares c1 and c2 based on HSV.
c1 < c2, returns -1
c1 = c2, returns 0
c1 > c2, returns 1 */
int hsvComparison(const KoColor& c1, const KoColor& c2) {
QColor qc1 = c1.toQColor();
QColor qc2 = c2.toQColor();
if (qc1.hue() < qc2.hue()) return -1;
if (qc1.hue() > qc2.hue()) return 1;
// hue is the same, ok let's compare saturation
if (qc1.saturation() < qc2.saturation()) return -1;
if (qc1.saturation() > qc2.saturation()) return 1;
// oh, also saturation is same?
if (qc1.value() < qc2.value()) return -1;
if (qc1.value() > qc2.value()) return 1;
// user selected two similar colors
return 0;
}
KisMinHeap <KoColor, MAX_RECENT_COLOR> m_priorityList;
QList <PriorityNode <KoColor>*> m_guiList;
};
KisFavoriteResourceManager::KisFavoriteResourceManager(KisPaintopBox *paintopBox)
: m_paintopBox(paintopBox)
, m_colorList(0)
- , m_blockUpdates(false)
, m_initialized(false)
{
KisConfig cfg(true);
m_maxPresets = cfg.favoritePresets();
m_colorList = new ColorDataList();
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(configChanged()));
KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
rServer->addObserver(this);
}
KisFavoriteResourceManager::~KisFavoriteResourceManager()
{
KisPaintOpPresetResourceServer *rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
rServer->removeObserver(this);
delete m_colorList;
}
void KisFavoriteResourceManager::unsetResourceServer()
{
// ...
}
-
-QVector<KisPaintOpPresetSP> KisFavoriteResourceManager::favoritePresetList()
+QVector<QString> KisFavoriteResourceManager::favoritePresetNamesList()
{
init();
- return m_favoritePresetsList;
+
+ QVector<QString> names;
+ for (int i = 0; i < m_maxPresets; i++) {
+ QModelIndex index = m_resourcesProxyModel->index(i, 0);
+ if (index.isValid()) {
+ QString name = m_resourcesProxyModel->data(index, Qt::UserRole + KisResourceModel::Name).toString();
+ names << name;
+ } else {
+ break; // no more valid indeces
+ }
+ }
+
+ return names;
}
QList<QImage> KisFavoriteResourceManager::favoritePresetImages()
{
init();
QList<QImage> images;
- Q_FOREACH (KisPaintOpPresetSP preset, m_favoritePresetsList) {
- if (preset) {
- images.append(preset->image());
+ for (int i = 0; i < m_maxPresets; i++) {
+ QModelIndex index = m_resourcesProxyModel->index(i, 0);
+ if (index.isValid()) {
+ QVariant tmp = m_resourcesProxyModel->data(index, Qt::UserRole + KisResourceModel::Thumbnail);
+ QImage image = tmp.value<QImage>();
+ images << image;
+ } else {
+ break; // no more valid indeces
}
-
}
return images;
}
-void KisFavoriteResourceManager::setCurrentTag(const QString& tagName)
+void KisFavoriteResourceManager::setCurrentTag(const KisTagSP tag)
{
- m_currentTag = tagName;
- KisConfig(false).writeEntry<QString>("favoritePresetsTag", tagName);
+ m_currentTag = tag;
+ m_resourcesProxyModel->setTag(tag);
+ KisConfig(false).writeEntry<QString>("favoritePresetsTag", tag->url());
updateFavoritePresets();
}
void KisFavoriteResourceManager::slotChangeActivePaintop(int pos)
{
- if (pos < 0 || pos >= m_favoritePresetsList.size()) return;
+ ENTER_FUNCTION() << ppVar(pos) << ppVar(numFavoritePresets());
+ if (pos < 0 || pos >= numFavoritePresets()) return;
+
+ QModelIndex index = m_resourcesProxyModel->index(pos, 0);
+ KoResourceSP resource = m_resourcesProxyModel->resourceForIndex(index);
- KoResource* resource = const_cast<KisPaintOpPreset*>(m_favoritePresetsList.at(pos).data());
m_paintopBox->resourceSelected(resource);
emit hidePalettes();
}
int KisFavoriteResourceManager::numFavoritePresets()
{
init();
- return m_favoritePresetsList.size();
+ return favoritePresetNamesList().size();
}
//Recent Colors
void KisFavoriteResourceManager::slotUpdateRecentColor(int pos)
{
// Do not update the key, the colour might be selected but it is not used yet. So we are not supposed
// to update the colour priority when we select it.
m_colorList->updateKey(pos);
emit setSelectedColor(pos);
emit sigSetFGColor(m_colorList->guiColor(pos));
emit hidePalettes();
}
void KisFavoriteResourceManager::slotAddRecentColor(const KoColor& color)
{
m_colorList->append(color);
int pos = m_colorList->findPos(color);
emit setSelectedColor(pos);
}
void KisFavoriteResourceManager::slotChangeFGColorSelector(KoColor c)
{
emit sigChangeFGColorSelector(c);
}
-void KisFavoriteResourceManager::removingResource(PointerType resource)
+void KisFavoriteResourceManager::removingResource(QSharedPointer<KisPaintOpPreset> /*resource*/)
{
- if (m_blockUpdates) {
- return;
- }
- if (m_favoritePresetsList.contains(resource.data())) {
- updateFavoritePresets();
- }
+ updateFavoritePresets();
}
-void KisFavoriteResourceManager::resourceAdded(PointerType /*resource*/)
+void KisFavoriteResourceManager::resourceAdded(QSharedPointer<KisPaintOpPreset> /*resource*/)
{
- if (m_blockUpdates) {
- return;
- }
updateFavoritePresets();
}
-void KisFavoriteResourceManager::resourceChanged(PointerType /*resource*/)
+void KisFavoriteResourceManager::resourceChanged(QSharedPointer<KisPaintOpPreset> /*resource*/)
{
+ updateFavoritePresets();
}
-void KisFavoriteResourceManager::setBlockUpdates(bool block)
+void KisFavoriteResourceManager::syncTaggedResourceView()
{
- m_blockUpdates = block;
- if (!block) {
- updateFavoritePresets();
- }
-}
-
-void KisFavoriteResourceManager::syncTaggedResourceView() {
- if (m_blockUpdates) {
- return;
- }
updateFavoritePresets();
}
void KisFavoriteResourceManager::syncTagAddition(const QString& /*tag*/) {}
void KisFavoriteResourceManager::syncTagRemoval(const QString& /*tag*/) {}
int KisFavoriteResourceManager::recentColorsTotal()
{
return m_colorList->size();
}
const KoColor& KisFavoriteResourceManager::recentColorAt(int pos)
{
return m_colorList->guiColor(pos);
}
void KisFavoriteResourceManager::slotSetBGColor(const KoColor c)
{
m_bgColor = c;
}
KoColor KisFavoriteResourceManager::bgColor() const
{
return m_bgColor;
}
bool sortPresetByName(KisPaintOpPresetSP preset1, KisPaintOpPresetSP preset2)
{
return preset1->name() < preset2->name();
}
void KisFavoriteResourceManager::updateFavoritePresets()
{
-
- m_favoritePresetsList.clear();
- KisPaintOpPresetResourceServer* rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
- QStringList presetFilenames = rServer->searchTag(m_currentTag);
- for(int i = 0; i < qMin(m_maxPresets, presetFilenames.size()); i++) {
- KisPaintOpPresetSP pr = rServer->resourceByFilename(presetFilenames.at(i));
- m_favoritePresetsList.append(pr.data());
- std::sort(m_favoritePresetsList.begin(), m_favoritePresetsList.end(), sortPresetByName);
- }
emit updatePalettes();
}
void KisFavoriteResourceManager::configChanged()
{
KisConfig cfg(true);
m_maxPresets = cfg.favoritePresets();
updateFavoritePresets();
}
void KisFavoriteResourceManager::init()
{
if (!m_initialized) {
m_initialized = true;
+
+ m_tagModel = KisTagModelProvider::tagModel(ResourceType::PaintOpPresets);
+ m_resourcesProxyModel = new KisTagFilterResourceProxyModel(m_tagModel, this);
+ m_resourcesProxyModel->setSourceModel(KisResourceModelProvider::resourceModel(ResourceType::PaintOpPresets));
+
+ m_resourceModel = KisResourceModelProvider::resourceModel(ResourceType::PaintOpPresets);
+
KisResourceServerProvider::instance()->paintOpPresetServer();
- m_currentTag = KisConfig(true).readEntry<QString>("favoritePresetsTag", "★ My Favorites");
+ QString currentTag = KisConfig(true).readEntry<QString>("favoritePresetsTag", "★ My Favorites");
+
+ // TODO: RESOURCES: tag by url?
+ KisTagModel* tagModel = KisTagModelProvider::tagModel(ResourceType::PaintOpPresets);
+ for (int i = 0; i < tagModel->rowCount(); i++) {
+ QModelIndex index = tagModel->index(i, 0);
+ KisTagSP tag = tagModel->tagForIndex(index);
+ if (!tag.isNull() && tag->url() == currentTag) {
+ m_currentTag = tag;
+ break;
+ }
+ }
+ m_resourcesProxyModel->setTag(m_currentTag);
updateFavoritePresets();
}
}
diff --git a/libs/ui/kis_favorite_resource_manager.h b/libs/ui/kis_favorite_resource_manager.h
index aa168a6028..1d3c75dafb 100644
--- a/libs/ui/kis_favorite_resource_manager.h
+++ b/libs/ui/kis_favorite_resource_manager.h
@@ -1,135 +1,134 @@
/* This file is part of the KDE project
Copyright 2009 Vera Lukman <shicmap@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 KIS_FAVORITE_RESOURCE_MANAGER_H
#define KIS_FAVORITE_RESOURCE_MANAGER_H
#include <QObject>
#include <kis_types.h>
#include <QQueue>
#include <QList>
#include "KoResourceServerObserver.h"
+#include <KisTag.h>
+#include "KisTagFilterResourceProxyModel.h"
+#include <KisTagModelProvider.h>
#include <KoColor.h>
+#include <KoResource.h>
class QString;
class KisPaintopBox;
class KisPaintOpPreset;
-class KisFavoriteResourceManager : public QObject, public KoResourceServerObserver<KisPaintOpPreset, SharedPointerStoragePolicy<KisPaintOpPresetSP> >
+class KisFavoriteResourceManager : public QObject, public KoResourceServerObserver<KisPaintOpPreset>
{
Q_OBJECT
public:
KisFavoriteResourceManager(KisPaintopBox *paintopBox);
~KisFavoriteResourceManager() override;
void unsetResourceServer() override;
QList<QImage> favoritePresetImages();
+ QVector<QString> favoritePresetNamesList();
- void setCurrentTag(const QString& tagName);
+ void setCurrentTag(const KisTagSP tag);
int numFavoritePresets();
- QVector<KisPaintOpPresetSP> favoritePresetList();
+ void updateFavoritePresets();
int recentColorsTotal();
const KoColor& recentColorAt(int pos);
// Reimplemented from KoResourceServerObserver
- void removingResource(PointerType resource) override;
- void resourceAdded(PointerType resource) override;
- void resourceChanged(PointerType resource) override;
+ void removingResource(QSharedPointer<KisPaintOpPreset> resource) override;
+ void resourceAdded(QSharedPointer<KisPaintOpPreset> resource) override;
+ void resourceChanged(QSharedPointer<KisPaintOpPreset> resource) override;
void syncTaggedResourceView() override;
void syncTagAddition(const QString& tag) override;
void syncTagRemoval(const QString& tag) override;
//BgColor;
KoColor bgColor() const;
- /**
- * Set palette to block updates, paintops won't be deleted when they are deleted from server
- * Used when overwriting a resource
- */
- void setBlockUpdates(bool block);
-
Q_SIGNALS:
void sigSetFGColor(const KoColor& c);
void sigSetBGColor(const KoColor& c);
// This is a flag to handle a bug:
// If pop up palette is visible and a new colour is selected, the new colour
// will be added when the user clicks on the canvas to hide the palette
// In general, we want to be able to store recent colours if the pop up palette
// is not visible
void sigEnableChangeColor(bool b);
void sigChangeFGColorSelector(const KoColor&);
void setSelectedColor(int);
void updatePalettes();
void hidePalettes();
public Q_SLOTS:
void slotChangeActivePaintop(int);
/*update the priority of a colour in m_colorList, used only by m_popupPalette*/
void slotUpdateRecentColor(int);
/*add a colour to m_colorList, used by KisCanvasResourceProvider*/
void slotAddRecentColor(const KoColor&);
void slotChangeFGColorSelector(KoColor c);
void slotSetBGColor(const KoColor c);
private Q_SLOTS:
- void updateFavoritePresets();
+
void configChanged();
private:
- // Loads the favorite preset list for the first time
void init();
KisPaintopBox *m_paintopBox;
- QVector<KisPaintOpPresetSP> m_favoritePresetsList;
-
class ColorDataList;
ColorDataList *m_colorList;
- bool m_blockUpdates;
-
void saveFavoritePresets();
KoColor m_bgColor;
- QString m_currentTag;
+ KisTagSP m_currentTag;
bool m_initialized;
int m_maxPresets;
+
+ KisTagModel* m_tagModel;
+ KisTagFilterResourceProxyModel* m_resourcesProxyModel;
+ KisResourceModel* m_resourceModel;
+
};
#endif
diff --git a/libs/ui/kis_filter_manager.cc b/libs/ui/kis_filter_manager.cc
index 2afa5b68a1..841387c745 100644
--- a/libs/ui/kis_filter_manager.cc
+++ b/libs/ui/kis_filter_manager.cc
@@ -1,358 +1,361 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 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.
*/
#include "kis_filter_manager.h"
#include <QHash>
#include <KisSignalMapper.h>
#include <QMessageBox>
#include <kactionmenu.h>
#include <kactioncollection.h>
#include <KoID.h>
#include <KisMainWindow.h>
// krita/image
#include <filter/kis_filter.h>
#include <filter/kis_filter_registry.h>
#include <filter/kis_filter_configuration.h>
#include <kis_paint_device.h>
// krita/ui
#include "KisViewManager.h"
#include "kis_canvas2.h"
#include <kis_bookmarked_configuration_manager.h>
#include "kis_action.h"
#include "kis_action_manager.h"
#include "kis_canvas_resource_provider.h"
#include "dialogs/kis_dlg_filter.h"
#include "strokes/kis_filter_stroke_strategy.h"
#include "krita_utils.h"
#include "kis_icon_utils.h"
+#include <KisGlobalResourcesInterface.h>
struct KisFilterManager::Private {
Private()
: reapplyAction(0)
, actionCollection(0)
, actionManager(0)
, view(0)
{
}
KisAction* reapplyAction;
QHash<QString, KActionMenu*> filterActionMenus;
QHash<KisFilter*, QAction *> filters2Action;
KActionCollection *actionCollection;
KisActionManager *actionManager;
KisViewManager *view;
KisFilterConfigurationSP lastConfiguration;
KisFilterConfigurationSP currentlyAppliedConfiguration;
KisStrokeId currentStrokeId;
QRect initialApplyRect;
KisSignalMapper actionsMapper;
QPointer<KisDlgFilter> filterDialog;
};
KisFilterManager::KisFilterManager(KisViewManager * view)
: d(new Private)
{
d->view = view;
}
KisFilterManager::~KisFilterManager()
{
delete d;
}
void KisFilterManager::setView(QPointer<KisView>imageView)
{
Q_UNUSED(imageView);
}
void KisFilterManager::setup(KActionCollection * ac, KisActionManager *actionManager)
{
d->actionCollection = ac;
d->actionManager = actionManager;
// Setup reapply action
d->reapplyAction = d->actionManager->createAction("filter_apply_again");
d->reapplyAction->setActivationFlags(KisAction::ACTIVE_DEVICE);
d->reapplyAction->setEnabled(false);
connect(d->reapplyAction, SIGNAL(triggered()), SLOT(reapplyLastFilter()));
connect(&d->actionsMapper, SIGNAL(mapped(QString)), SLOT(showFilterDialog(QString)));
// Setup list of filters
QStringList keys = KisFilterRegistry::instance()->keys();
keys.sort();
Q_FOREACH (const QString &filterName, keys) {
insertFilter(filterName);
}
connect(KisFilterRegistry::instance(), SIGNAL(filterAdded(QString)), SLOT(insertFilter(QString)));
}
void KisFilterManager::insertFilter(const QString & filterName)
{
Q_ASSERT(d->actionCollection);
KisFilterSP filter = KisFilterRegistry::instance()->value(filterName);
Q_ASSERT(filter);
if (d->filters2Action.contains(filter.data())) {
warnKrita << "Filter" << filterName << " has already been inserted";
return;
}
KoID category = filter->menuCategory();
KActionMenu* actionMenu = d->filterActionMenus[ category.id()];
if (!actionMenu) {
actionMenu = new KActionMenu(category.name(), this);
d->actionCollection->addAction(category.id(), actionMenu);
d->filterActionMenus[category.id()] = actionMenu;
}
KisAction *action = new KisAction(filter->menuEntry(), this);
action->setDefaultShortcut(filter->shortcut());
action->setActivationFlags(KisAction::ACTIVE_DEVICE);
d->actionManager->addAction(QString("krita_filter_%1").arg(filterName), action);
d->filters2Action[filter.data()] = action;
actionMenu->addAction(action);
d->actionsMapper.setMapping(action, filterName);
connect(action, SIGNAL(triggered()), &d->actionsMapper, SLOT(map()));
}
void KisFilterManager::updateGUI()
{
if (!d->view) return;
bool enable = false;
KisNodeSP activeNode = d->view->activeNode();
enable = activeNode && activeNode->hasEditablePaintDevice();
d->reapplyAction->setEnabled(enable);
for (QHash<KisFilter*, QAction *>::iterator it = d->filters2Action.begin();
it != d->filters2Action.end(); ++it) {
bool localEnable = enable;
it.value()->setEnabled(localEnable);
}
}
void KisFilterManager::reapplyLastFilter()
{
if (!d->lastConfiguration) return;
apply(d->lastConfiguration);
finish();
}
void KisFilterManager::showFilterDialog(const QString &filterId)
{
if (!d->view->activeNode()->isEditable()) {
d->view->showFloatingMessage(i18n("Cannot apply filter to locked layer."),
KisIconUtils::loadIcon("object-locked"));
return;
}
if (d->filterDialog && d->filterDialog->isVisible()) {
KisFilterSP filter = KisFilterRegistry::instance()->value(filterId);
d->filterDialog->setFilter(filter);
return;
}
connect(d->view->image(),
SIGNAL(sigStrokeCancellationRequested()),
SLOT(slotStrokeCancelRequested()),
Qt::UniqueConnection);
connect(d->view->image(),
SIGNAL(sigStrokeEndRequested()),
SLOT(slotStrokeEndRequested()),
Qt::UniqueConnection);
/**
* The UI should show only after every running stroke is finished,
* so a virtual barrier is added here.
*/
if (!d->view->blockUntilOperationsFinished(d->view->image())) {
return;
}
Q_ASSERT(d->view);
Q_ASSERT(d->view->activeNode());
KisPaintDeviceSP dev = d->view->activeNode()->paintDevice();
if (!dev) {
warnKrita << "KisFilterManager::showFilterDialog(): Filtering was requested for illegal active layer!" << d->view->activeNode();
return;
}
KisFilterSP filter = KisFilterRegistry::instance()->value(filterId);
if (dev->colorSpace()->willDegrade(filter->colorSpaceIndependence())) {
// Warning bells!
if (filter->colorSpaceIndependence() == TO_LAB16) {
if (QMessageBox::warning(d->view->mainWindow(),
i18nc("@title:window", "Krita"),
i18n("The %1 filter will convert your %2 data to 16-bit L*a*b* and vice versa. ",
filter->name(),
dev->colorSpace()->name()),
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok)
!= QMessageBox::Ok) return;
} else if (filter->colorSpaceIndependence() == TO_RGBA16) {
if (QMessageBox::warning(d->view->mainWindow(),
i18nc("@title:window", "Krita"),
i18n("The %1 filter will convert your %2 data to 16-bit RGBA and vice versa. ",
filter->name() , dev->colorSpace()->name()),
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok)
!= QMessageBox::Ok) return;
}
}
if (filter->showConfigurationWidget()) {
if (!d->filterDialog) {
d->filterDialog = new KisDlgFilter(d->view , d->view->activeNode(), this, d->view->mainWindow());
d->filterDialog->setAttribute(Qt::WA_DeleteOnClose);
}
d->filterDialog->setFilter(filter);
d->filterDialog->setVisible(true);
} else {
- apply(KisFilterConfigurationSP(filter->defaultConfiguration()));
+ apply(KisFilterConfigurationSP(filter->defaultConfiguration(KisGlobalResourcesInterface::instance())));
finish();
}
}
-void KisFilterManager::apply(KisFilterConfigurationSP filterConfig)
+void KisFilterManager::apply(KisFilterConfigurationSP _filterConfig)
{
+ KisFilterConfigurationSP filterConfig = _filterConfig->cloneWithResourcesSnapshot();
+
KisFilterSP filter = KisFilterRegistry::instance()->value(filterConfig->name());
KisImageWSP image = d->view->image();
if (d->currentStrokeId) {
image->addJob(d->currentStrokeId, new KisFilterStrokeStrategy::CancelSilentlyMarker);
image->cancelStroke(d->currentStrokeId);
d->currentStrokeId.clear();
} else {
image->waitForDone();
d->initialApplyRect = d->view->activeNode()->exactBounds();
}
QRect applyRect = d->initialApplyRect;
KisPaintDeviceSP paintDevice = d->view->activeNode()->paintDevice();
if (paintDevice &&
filter->needsTransparentPixels(filterConfig.data(), paintDevice->colorSpace())) {
applyRect |= image->bounds();
}
KoCanvasResourceProvider *resourceManager =
d->view->canvasResourceProvider()->resourceManager();
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image,
d->view->activeNode(),
resourceManager);
d->currentStrokeId =
image->startStroke(new KisFilterStrokeStrategy(filter,
KisFilterConfigurationSP(filterConfig),
resources));
QRect processRect = filter->changedRect(applyRect, filterConfig.data(), 0);
processRect &= image->bounds();
if (filter->supportsThreading()) {
QSize size = KritaUtils::optimalPatchSize();
QVector<QRect> rects = KritaUtils::splitRectIntoPatches(processRect, size);
Q_FOREACH (const QRect &rc, rects) {
image->addJob(d->currentStrokeId,
new KisFilterStrokeStrategy::Data(rc, true));
}
} else {
image->addJob(d->currentStrokeId,
new KisFilterStrokeStrategy::Data(processRect, false));
}
d->currentlyAppliedConfiguration = filterConfig;
}
void KisFilterManager::finish()
{
Q_ASSERT(d->currentStrokeId);
d->view->image()->endStroke(d->currentStrokeId);
KisFilterSP filter = KisFilterRegistry::instance()->value(d->currentlyAppliedConfiguration->name());
if (filter->bookmarkManager()) {
filter->bookmarkManager()->save(KisBookmarkedConfigurationManager::ConfigLastUsed,
d->currentlyAppliedConfiguration.data());
}
d->lastConfiguration = d->currentlyAppliedConfiguration;
d->reapplyAction->setEnabled(true);
d->reapplyAction->setText(i18n("Apply Filter Again: %1", filter->name()));
d->currentStrokeId.clear();
d->currentlyAppliedConfiguration.clear();
}
void KisFilterManager::cancel()
{
Q_ASSERT(d->currentStrokeId);
d->view->image()->cancelStroke(d->currentStrokeId);
d->currentStrokeId.clear();
d->currentlyAppliedConfiguration.clear();
}
bool KisFilterManager::isStrokeRunning() const
{
return d->currentStrokeId;
}
void KisFilterManager::slotStrokeEndRequested()
{
if (d->currentStrokeId && d->filterDialog) {
d->filterDialog->accept();
}
}
void KisFilterManager::slotStrokeCancelRequested()
{
if (d->currentStrokeId && d->filterDialog) {
d->filterDialog->reject();
}
}
diff --git a/libs/ui/kis_layer_manager.cc b/libs/ui/kis_layer_manager.cc
index da4015598a..4506633dbd 100644
--- a/libs/ui/kis_layer_manager.cc
+++ b/libs/ui/kis_layer_manager.cc
@@ -1,995 +1,991 @@
/*
* 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 "dialogs/KisDlgChangeCloneSource.h"
#include "kis_filter_manager.h"
#include "kis_node_visitor.h"
#include "kis_paint_layer.h"
#include "commands/kis_image_commands.h"
#include "commands/kis_node_commands.h"
#include "kis_change_file_layer_command.h"
#include "kis_canvas_resource_provider.h"
#include "kis_selection_manager.h"
#include "kis_statusbar.h"
#include "KisViewManager.h"
#include "kis_zoom_manager.h"
#include "canvas/kis_canvas2.h"
#include "widgets/kis_meta_data_merge_strategy_chooser_widget.h"
#include "widgets/kis_wdg_generator.h"
#include "kis_progress_widget.h"
#include "kis_node_commands_adapter.h"
#include "kis_node_manager.h"
#include "kis_action.h"
#include "kis_action_manager.h"
#include "kis_raster_keyframe_channel.h"
#include "kis_signal_compressor_with_param.h"
#include "kis_abstract_projection_plane.h"
#include "commands_new/kis_set_layer_style_command.h"
#include "kis_post_execution_undo_adapter.h"
#include "kis_selection_mask.h"
#include "kis_layer_utils.h"
#include "lazybrush/kis_colorize_mask.h"
#include "kis_processing_applicator.h"
#include "KisSaveGroupVisitor.h"
KisLayerManager::KisLayerManager(KisViewManager * view)
: m_view(view)
, m_commandsAdapter(new KisNodeCommandsAdapter(m_view))
{
}
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) {
layersUpdated();
if (layer) {
m_view->canvasResourceProvider()->slotNodeActivated(layer.data());
}
}
}
void KisLayerManager::setup(KisActionManager* actionManager)
{
m_imageFlatten = actionManager->createAction("flatten_image");
connect(m_imageFlatten, SIGNAL(triggered()), this, SLOT(flattenImage()));
m_imageMergeLayer = actionManager->createAction("merge_layer");
connect(m_imageMergeLayer, SIGNAL(triggered()), this, SLOT(mergeLayer()));
m_flattenLayer = actionManager->createAction("flatten_layer");
connect(m_flattenLayer, SIGNAL(triggered()), this, SLOT(flattenLayer()));
m_rasterizeLayer = actionManager->createAction("rasterize_layer");
connect(m_rasterizeLayer, SIGNAL(triggered()), this, SLOT(rasterizeLayer()));
m_groupLayersSave = actionManager->createAction("save_groups_as_images");
connect(m_groupLayersSave, SIGNAL(triggered()), this, SLOT(saveGroupLayers()));
m_convertGroupAnimated = actionManager->createAction("convert_group_to_animated");
connect(m_convertGroupAnimated, SIGNAL(triggered()), this, SLOT(convertGroupToAnimated()));
m_imageResizeToLayer = actionManager->createAction("resizeimagetolayer");
connect(m_imageResizeToLayer, SIGNAL(triggered()), this, SLOT(imageResizeToActiveLayer()));
KisAction *action = actionManager->createAction("trim_to_image");
connect(action, SIGNAL(triggered()), this, SLOT(trimToImage()));
m_layerStyle = actionManager->createAction("layer_style");
connect(m_layerStyle, SIGNAL(triggered()), this, SLOT(layerStyle()));
}
void KisLayerManager::updateGUI()
{
KisImageSP image = m_view->image();
KisLayerSP layer = activeLayer();
const bool isGroupLayer = layer && layer->inherits("KisGroupLayer");
m_imageMergeLayer->setText(
isGroupLayer ?
i18nc("@action:inmenu", "Merge Group") :
i18nc("@action:inmenu", "Merge with Layer Below"));
m_flattenLayer->setVisible(!isGroupLayer);
if (m_view->statusBar())
m_view->statusBar()->setProfile(image);
}
void KisLayerManager::imageResizeToActiveLayer()
{
KisLayerSP layer;
KisImageWSP image = m_view->image();
if (image && (layer = activeLayer())) {
QRect cropRect = layer->projection()->nonDefaultPixelArea();
if (!cropRect.isEmpty()) {
image->cropImage(cropRect);
} else {
m_view->showFloatingMessage(
i18nc("floating message in layer manager",
"Layer is empty "),
QIcon(), 2000, KisFloatingMessage::Low);
}
}
}
void KisLayerManager::trimToImage()
{
KisImageWSP image = m_view->image();
if (image) {
image->cropImage(image->bounds());
}
}
void KisLayerManager::layerProperties()
{
if (!m_view) return;
if (!m_view->document()) return;
KisLayerSP layer = activeLayer();
if (!layer) return;
QList<KisNodeSP> selectedNodes = m_view->nodeManager()->selectedNodes();
const bool multipleLayersSelected = selectedNodes.size() > 1;
KisAdjustmentLayerSP adjustmentLayer = KisAdjustmentLayerSP(dynamic_cast<KisAdjustmentLayer*>(layer.data()));
KisGeneratorLayerSP generatorLayer = KisGeneratorLayerSP(dynamic_cast<KisGeneratorLayer*>(layer.data()));
KisFileLayerSP fileLayer = KisFileLayerSP(dynamic_cast<KisFileLayer*>(layer.data()));
if (adjustmentLayer && !multipleLayersSelected) {
KisPaintDeviceSP dev = adjustmentLayer->projection();
KisDlgAdjLayerProps dlg(adjustmentLayer, adjustmentLayer.data(), dev, m_view, adjustmentLayer->filter().data(), adjustmentLayer->name(), i18n("Filter Layer Properties"), m_view->mainWindow(), "dlgadjlayerprops");
dlg.resize(dlg.minimumSizeHint());
KisFilterConfigurationSP configBefore(adjustmentLayer->filter());
KIS_ASSERT_RECOVER_RETURN(configBefore);
QString xmlBefore = configBefore->toXML();
if (dlg.exec() == QDialog::Accepted) {
adjustmentLayer->setName(dlg.layerName());
KisFilterConfigurationSP configAfter(dlg.filterConfiguration());
Q_ASSERT(configAfter);
QString xmlAfter = configAfter->toXML();
if(xmlBefore != xmlAfter) {
KisChangeFilterCmd *cmd
= new KisChangeFilterCmd(adjustmentLayer,
- configBefore->name(),
- xmlBefore,
- configAfter->name(),
- xmlAfter,
- false);
+ configBefore->cloneWithResourcesSnapshot(),
+ configAfter->cloneWithResourcesSnapshot());
// FIXME: check whether is needed
cmd->redo();
m_view->undoAdapter()->addCommand(cmd);
m_view->document()->setModified(true);
}
}
else {
KisFilterConfigurationSP configAfter(dlg.filterConfiguration());
Q_ASSERT(configAfter);
QString xmlAfter = configAfter->toXML();
if(xmlBefore != xmlAfter) {
- adjustmentLayer->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data()));
+ adjustmentLayer->setFilter(configBefore->cloneWithResourcesSnapshot());
adjustmentLayer->setDirty();
}
}
}
else if (generatorLayer && !multipleLayersSelected) {
KisFilterConfigurationSP configBefore(generatorLayer->filter());
Q_ASSERT(configBefore);
- QString xmlBefore = configBefore->toXML();
KisDlgGeneratorLayer *dlg = new KisDlgGeneratorLayer(generatorLayer->name(), m_view, m_view->mainWindow(), generatorLayer, configBefore);
dlg->setCaption(i18n("Fill Layer Properties"));
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setConfiguration(configBefore.data());
dlg->resize(dlg->minimumSizeHint());
Qt::WindowFlags flags = dlg->windowFlags();
dlg->setWindowFlags(flags | Qt::Tool | Qt::Dialog);
dlg->show();
}
else if (fileLayer && !multipleLayersSelected){
QString basePath = QFileInfo(m_view->document()->url().toLocalFile()).absolutePath();
QString fileNameOld = fileLayer->fileName();
KisFileLayer::ScalingMethod scalingMethodOld = fileLayer->scalingMethod();
KisDlgFileLayer dlg(basePath, fileLayer->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;
}
fileLayer->setName(dlg.layerName());
if (fileNameOld!= fileNameNew || scalingMethodOld != scalingMethodNew) {
KisChangeFileLayerCmd *cmd
= new KisChangeFileLayerCmd(fileLayer,
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::Tool | Qt::Dialog);
dialog->show();
}
}
void KisLayerManager::changeCloneSource()
{
QList<KisNodeSP> selectedNodes = m_view->nodeManager()->selectedNodes();
if (selectedNodes.isEmpty()) {
return;
}
QList<KisCloneLayerSP> cloneLayers;
KisNodeSP node;
Q_FOREACH (node, selectedNodes) {
KisCloneLayerSP cloneLayer(qobject_cast<KisCloneLayer *>(node.data()));
if (cloneLayer) {
cloneLayers << cloneLayer;
}
}
if (cloneLayers.isEmpty()) {
return;
}
KisDlgChangeCloneSource *dialog = new KisDlgChangeCloneSource(cloneLayers, m_view);
dialog->setCaption(i18n("Change Clone Layer"));
dialog->resize(dialog->minimumSizeHint());
dialog->setAttribute(Qt::WA_DeleteOnClose);
Qt::WindowFlags flags = dialog->windowFlags();
dialog->setWindowFlags(flags | Qt::Tool | 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();
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()) {
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));
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()).completeBaseName();
location.setFile(location.dir(), location.completeBaseName() + "_" + 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);
}
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();
if (bounds.isEmpty()) {
bounds = image->bounds();
}
KisImageSP dst = new KisImage(doc->createUndoStore(),
image->width(),
image->height(),
image->projection()->compositionSourceColorSpace(),
source->name());
dst->setResolution(image->xRes(), image->yRes());
doc->setFileBatchMode(false);
doc->setCurrentImage(dst);
KisNodeSP node = source->clone();
dst->addNode(node);
dst->initialRefreshGraph();
dst->cropImage(bounds);
dst->waitForDone();
bool r = doc->exportDocumentSync(QUrl::fromLocalFile(path), mimeType.toLatin1());
if (!r) {
qWarning() << "Converting layer to file layer. path:"<< path << "gave errors" << doc->errorMessage();
} else {
QString basePath = QFileInfo(m_view->document()->url().toLocalFile()).absolutePath();
QString relativePath = QDir(basePath).relativeFilePath(path);
KisFileLayer *fileLayer = new KisFileLayer(image, basePath, relativePath, KisFileLayer::None, source->name(), OPACITY_OPAQUE_U8);
fileLayer->setX(bounds.x());
fileLayer->setY(bounds.y());
KisNodeSP dstParent = source->parent();
KisNodeSP dstAboveThis = source->prevSibling();
m_commandsAdapter->beginMacro(kundo2_i18n("Convert to a file layer"));
m_commandsAdapter->removeNode(source);
m_commandsAdapter->addNode(fileLayer, dstParent, dstAboveThis);
m_commandsAdapter->endMacro();
}
doc->closeUrl(false);
}
void KisLayerManager::adjustLayerPosition(KisNodeSP node, KisNodeSP activeNode, KisNodeSP &parent, KisNodeSP &above)
{
Q_ASSERT(activeNode);
parent = activeNode;
above = parent->lastChild();
if (parent->inherits("KisGroupLayer") && parent->collapsed()) {
above = parent;
parent = parent->parent();
return;
}
while (parent &&
(!parent->allowAsChild(node) || parent->userLocked())) {
above = parent;
parent = parent->parent();
}
if (!parent) {
warnKrita << "KisLayerManager::adjustLayerPosition:"
<< "No node accepted newly created node";
parent = m_view->image()->root();
above = parent->lastChild();
}
}
void KisLayerManager::addLayerCommon(KisNodeSP activeNode, KisNodeSP layer, bool updateImage, KisProcessingApplicator *applicator)
{
KisNodeSP parent;
KisNodeSP above;
adjustLayerPosition(layer, activeNode, parent, above);
KisGroupLayer *group = dynamic_cast<KisGroupLayer*>(parent.data());
const bool parentForceUpdate = group && !group->projectionIsValid();
updateImage |= parentForceUpdate;
m_commandsAdapter->addNodeAsync(layer, parent, above, updateImage, updateImage, applicator);
}
KisLayerSP KisLayerManager::addPaintLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
KisLayerSP layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
addLayerCommon(activeNode, layer, false, 0);
return layer;
}
KisNodeSP KisLayerManager::addGroupLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
KisGroupLayerSP group = new KisGroupLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8);
addLayerCommon(activeNode, group, false, 0);
return group;
}
KisNodeSP KisLayerManager::addCloneLayer(KisNodeList nodes)
{
KisImageWSP image = m_view->image();
KisNodeList filteredNodes = KisLayerUtils::sortAndFilterMergableInternalNodes(nodes, false);
if (filteredNodes.isEmpty()) return KisNodeSP();
KisNodeSP newAbove = filteredNodes.last();
KisNodeSP node, lastClonedNode;
Q_FOREACH (node, filteredNodes) {
lastClonedNode = new KisCloneLayer(qobject_cast<KisLayer*>(node.data()), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8);
addLayerCommon(newAbove, lastClonedNode, true, 0 );
}
return lastClonedNode;
}
KisNodeSP KisLayerManager::addShapeLayer(KisNodeSP activeNode)
{
if (!m_view) return 0;
if (!m_view->document()) return 0;
KisImageWSP image = m_view->image();
KisShapeLayerSP layer = new KisShapeLayer(m_view->document()->shapeController(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8);
addLayerCommon(activeNode, layer, false, 0);
return layer;
}
KisNodeSP KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
KisSelectionSP selection = m_view->selection();
KisProcessingApplicator applicator(image, 0, KisProcessingApplicator::NONE,
KisImageSignalVector() << ModifiedSignal,
kundo2_i18n("Add Layer"));
KisAdjustmentLayerSP adjl = addAdjustmentLayer(activeNode, QString(), 0, selection, &applicator);
KisPaintDeviceSP previewDevice = new KisPaintDevice(*adjl->original());
KisDlgAdjustmentLayer dlg(adjl, adjl.data(), previewDevice, image->nextLayerName(), i18n("New Filter Layer"), m_view, qApp->activeWindow());
dlg.resize(dlg.minimumSizeHint());
// ensure that the device may be free'd by the dialog
// when it is not needed anymore
previewDevice = 0;
if (dlg.exec() != QDialog::Accepted || adjl->filter().isNull()) {
// XXX: add messagebox warning if there's no filter set!
applicator.cancel();
} else {
adjl->setName(dlg.layerName());
applicator.end();
}
return adjl;
}
KisAdjustmentLayerSP KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode, const QString & name,
KisFilterConfigurationSP filter,
KisSelectionSP selection,
KisProcessingApplicator *applicator)
{
KisImageWSP image = m_view->image();
- KisAdjustmentLayerSP layer = new KisAdjustmentLayer(image, name, filter, selection);
+ KisAdjustmentLayerSP layer = new KisAdjustmentLayer(image, name, filter ? filter->cloneWithResourcesSnapshot() : 0, selection);
addLayerCommon(activeNode, layer, true, applicator);
return layer;
}
KisNodeSP KisLayerManager::addGeneratorLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
QColor currentForeground = m_view->canvasResourceProvider()->fgColor().toQColor();
KisDlgGeneratorLayer dlg(image->nextLayerName(), m_view, m_view->mainWindow(), 0, 0);
KisFilterConfigurationSP defaultConfig = dlg.configuration();
defaultConfig->setProperty("color", currentForeground);
dlg.setConfiguration(defaultConfig);
dlg.resize(dlg.minimumSizeHint());
if (dlg.exec() == QDialog::Accepted) {
KisSelectionSP selection = m_view->selection();
KisFilterConfigurationSP generator = dlg.configuration();
QString name = dlg.layerName();
- KisNodeSP node = new KisGeneratorLayer(image, name, generator, selection);
+ KisNodeSP node = new KisGeneratorLayer(image, name, generator ? generator->cloneWithResourcesSnapshot() : 0, selection);
addLayerCommon(activeNode, node, true, 0);
return node;
}
return 0;
}
void KisLayerManager::flattenImage()
{
KisImageSP image = m_view->image();
if (!m_view->blockUntilOperationsFinished(image)) return;
if (image) {
bool doIt = true;
if (image->nHiddenLayers() > 0) {
int answer = QMessageBox::warning(m_view->mainWindow(),
i18nc("@title:window", "Flatten Image"),
i18n("The image contains hidden layers that will be lost. Do you want to flatten the image?"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No);
if (answer != QMessageBox::Yes) {
doIt = false;
}
}
if (doIt) {
image->flatten(m_view->activeNode());
}
}
}
inline bool isSelectionMask(KisNodeSP node) {
return dynamic_cast<KisSelectionMask*>(node.data());
}
bool tryMergeSelectionMasks(KisNodeSP currentNode, KisImageSP image)
{
bool result = false;
KisNodeSP prevNode = currentNode->prevSibling();
if (isSelectionMask(currentNode) &&
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) {
image->mergeMultipleLayers(selectedNodes, m_view->activeNode());
}
else if (tryMergeSelectionMasks(m_view->activeNode(), image)) {
// already done!
} else if (tryFlattenGroupLayer(m_view->activeNode(), image)) {
// already done!
} else {
if (!layer->prevSibling()) return;
KisLayer *prevLayer = qobject_cast<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);
}
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.completeBaseName();
KisImageSP image = m_view->image();
if (!image) return;
KisSaveGroupVisitor v(image, chkInvisible->isChecked(), chkDepth->isChecked(), f.absolutePath(), basename, extension, mimeType);
image->rootLayer()->accept(v);
}
bool KisLayerManager::activeLayerHasSelection()
{
return (activeLayer()->selection() != 0);
}
KisNodeSP KisLayerManager::addFileLayer(KisNodeSP activeNode)
{
QString basePath;
QUrl url = m_view->document()->url();
if (url.isLocalFile()) {
basePath = QFileInfo(url.toLocalFile()).absolutePath();
}
KisImageWSP image = m_view->image();
KisDlgFileLayer dlg(basePath, image->nextLayerName(), m_view->mainWindow());
dlg.resize(dlg.minimumSizeHint());
if (dlg.exec() == QDialog::Accepted) {
QString name = dlg.layerName();
QString fileName = dlg.fileName();
if(fileName.isEmpty()){
QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified"));
return 0;
}
KisFileLayer::ScalingMethod scalingMethod = dlg.scaleToImageResolution();
KisNodeSP node = new KisFileLayer(image, basePath, fileName, scalingMethod, name, OPACITY_OPAQUE_U8);
addLayerCommon(activeNode, node, true, 0);
return node;
}
return 0;
}
void updateLayerStyles(KisLayerSP layer, KisDlgLayerStyle *dlg)
{
- KisSetLayerStyleCommand::updateLayerStyle(layer, dlg->style()->clone());
+ KisSetLayerStyleCommand::updateLayerStyle(layer, dlg->style()->clone().dynamicCast<KisPSDLayerStyle>());
}
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();
+ oldStyle = layer->layerStyle()->clone().dynamicCast<KisPSDLayerStyle>();
}
else {
oldStyle = toQShared(new KisPSDLayerStyle());
}
- KisDlgLayerStyle dlg(oldStyle->clone(), m_view->canvasResourceProvider());
+ KisDlgLayerStyle dlg(oldStyle->clone().dynamicCast<KisPSDLayerStyle>(), m_view->canvasResourceProvider());
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));
image->postExecutionUndoAdapter()->addCommand(command);
}
}
diff --git a/libs/ui/kis_mask_manager.cc b/libs/ui/kis_mask_manager.cc
index ff32ca0636..f94bf9dec0 100644
--- a/libs/ui/kis_mask_manager.cc
+++ b/libs/ui/kis_mask_manager.cc
@@ -1,366 +1,364 @@
/* 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.
*/
#include "kis_mask_manager.h"
#include <kactioncollection.h>
#include <KoProperties.h>
#include <kis_transaction.h>
#include <filter/kis_filter_configuration.h>
#include <commands/kis_node_commands.h>
#include <kis_undo_adapter.h>
#include <kis_paint_layer.h>
#include "KisDocument.h"
#include "KisViewManager.h"
#include <kis_layer.h>
#include <kis_clone_layer.h>
#include <kis_group_layer.h>
#include <kis_filter_mask.h>
#include <lazybrush/kis_colorize_mask.h>
#include <kis_transform_mask.h>
#include <kis_transparency_mask.h>
#include <kis_selection_mask.h>
#include <kis_effect_mask.h>
#include "dialogs/kis_dlg_adjustment_layer.h"
#include "widgets/kis_mask_widgets.h"
#include <kis_selection.h>
#include <kis_selection_manager.h>
#include <kis_pixel_selection.h>
#include "dialogs/kis_dlg_adj_layer_props.h"
#include <kis_image.h>
#include <kis_transform_worker.h>
#include <KoColorSpace.h>
#include <KoColor.h>
#include "kis_node_commands_adapter.h"
#include "commands/kis_deselect_global_selection_command.h"
#include "kis_iterator_ng.h"
+#include <KisGlobalResourcesInterface.h>
KisMaskManager::KisMaskManager(KisViewManager * view)
: m_view(view)
, m_imageView(0)
, m_commandsAdapter(new KisNodeCommandsAdapter(m_view))
{
}
void KisMaskManager::setView(QPointer<KisView>imageView)
{
m_imageView = imageView;
}
void KisMaskManager::setup(KActionCollection *actionCollection, KisActionManager *actionManager)
{
Q_UNUSED(actionCollection);
Q_UNUSED(actionManager);
}
void KisMaskManager::updateGUI()
{
// XXX: enable/disable menu items according to whether there's a mask selected currently
// XXX: disable the selection mask item if there's already a selection mask
// YYY: doesn't KisAction do that already?
}
KisMaskSP KisMaskManager::activeMask()
{
if (m_imageView) {
return m_imageView->currentMask();
}
return 0;
}
KisPaintDeviceSP KisMaskManager::activeDevice()
{
KisMaskSP mask = activeMask();
return mask ? mask->paintDevice() : 0;
}
void KisMaskManager::activateMask(KisMaskSP mask)
{
Q_UNUSED(mask);
}
void KisMaskManager::masksUpdated()
{
m_view->updateGUI();
}
void KisMaskManager::adjustMaskPosition(KisNodeSP node, KisNodeSP activeNode, bool avoidActiveNode, KisNodeSP &parent, KisNodeSP &above)
{
Q_ASSERT(node);
Q_ASSERT(activeNode);
if (!avoidActiveNode && activeNode->allowAsChild(node)) {
parent = activeNode;
above = activeNode->lastChild();
} else if (activeNode->parent() && activeNode->parent()->allowAsChild(node)
&& activeNode->parent()->parent() /* we don't want to add masks to root */) {
parent = activeNode->parent();
above = activeNode;
} else {
KisNodeSP t = activeNode;
while ((t = t->nextSibling())) {
if (t->allowAsChild(node)) {
parent = t;
above = t->lastChild();
break;
}
}
if (!t) {
t = activeNode;
while ((t = t->prevSibling())) {
if (t->allowAsChild(node)) {
parent = t;
above = t->lastChild();
break;
}
}
}
if (!t && activeNode->parent()) {
adjustMaskPosition(node, activeNode->parent(), true, parent, above);
} else if (!t) {
KisImageWSP image = m_view->image();
KisLayerSP layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
m_commandsAdapter->addNode(layer, activeNode, 0);
parent = layer;
above = 0;
}
}
}
void KisMaskManager::createMaskCommon(KisMaskSP mask,
KisNodeSP activeNode,
KisPaintDeviceSP copyFrom,
const KUndo2MagicString& macroName,
const QString &nodeType,
const QString &nodeName,
bool suppressSelection,
bool avoidActiveNode,
bool updateImage)
{
m_commandsAdapter->beginMacro(macroName);
KisNodeSP parent;
KisNodeSP above;
adjustMaskPosition(mask, activeNode, avoidActiveNode, parent, above);
KisLayerSP parentLayer = qobject_cast<KisLayer*>(parent.data());
Q_ASSERT(parentLayer);
bool shouldDeselectGlobalSelection = false;
if (!suppressSelection) {
if (copyFrom) {
mask->initSelection(copyFrom, parentLayer);
} else {
mask->initSelection(m_view->selection(), parentLayer);
shouldDeselectGlobalSelection = m_view->selection();
}
}
//counting number of KisSelectionMask
QList<KisNodeSP> masks = parentLayer->childNodes(QStringList(nodeType),KoProperties());
int number = masks.count() + 1;
mask->setName(nodeName + QString(" ") + QString::number(number));
m_commandsAdapter->addNode(mask, parentLayer, above, updateImage, updateImage);
if (shouldDeselectGlobalSelection) {
m_commandsAdapter->addExtraCommand(new KisDeselectGlobalSelectionCommand(m_imageView->image()));
}
m_commandsAdapter->endMacro();
masksUpdated();
}
KisNodeSP KisMaskManager::createSelectionMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool convertActiveNode)
{
if (!activeNode->isEditable()) {
return 0;
}
KisSelectionMaskSP mask = new KisSelectionMask(m_view->image());
createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Selection Mask"), "KisSelectionMask", i18n("Selection"), false, convertActiveNode, false);
mask->setActive(true);
if (convertActiveNode) {
m_commandsAdapter->removeNode(activeNode);
}
return mask;
}
KisNodeSP KisMaskManager::createTransparencyMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool convertActiveNode)
{
if (!activeNode->isEditable()) {
return 0;
}
KisMaskSP mask = new KisTransparencyMask();
createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Transparency Mask"), "KisTransparencyMask", i18n("Transparency Mask"), false, convertActiveNode);
if (convertActiveNode) {
m_commandsAdapter->removeNode(activeNode);
}
return mask;
}
KisNodeSP KisMaskManager::createFilterMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool quiet, bool convertActiveNode)
{
if (!activeNode->isEditable()) {
return 0;
}
KisFilterMaskSP mask = new KisFilterMask();
createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Filter Mask"), "KisFilterMask", i18n("Filter Mask"), false, convertActiveNode);
if (convertActiveNode) {
m_commandsAdapter->removeNode(activeNode);
}
/**
* FIXME: We'll use layer's original for creation of a thumbnail.
* Actually, we can't use it's projection as newly created mask
* may be going to be inserted in the middle of the masks stack
*/
KisPaintDeviceSP originalDevice = mask->parent()->original();
KisDlgAdjustmentLayer dialog(mask, mask.data(), originalDevice,
mask->name(), i18n("New Filter Mask"),
m_view, qApp->activeWindow());
// If we are supposed to not disturb the user, don't start asking them about things.
if(quiet) {
- KisFilterConfigurationSP filter = KisFilterRegistry::instance()->values().first()->defaultConfiguration();
+ KisFilterConfigurationSP filter = KisFilterRegistry::instance()->values().first()->defaultConfiguration(KisGlobalResourcesInterface::instance());
if (filter) {
- mask->setFilter(filter);
+ mask->setFilter(filter->cloneWithResourcesSnapshot());
mask->setName(mask->name());
}
return mask;
}
if (dialog.exec() == QDialog::Accepted) {
KisFilterConfigurationSP filter = dialog.filterConfiguration();
if (filter) {
QString name = dialog.layerName();
- mask->setFilter(filter);
+ mask->setFilter(filter->cloneWithResourcesSnapshot());
mask->setName(name);
}
return mask;
} else {
m_commandsAdapter->undoLastCommand();
}
return 0;
}
KisNodeSP KisMaskManager::createColorizeMask(KisNodeSP activeNode)
{
if (!activeNode->isEditable()) {
return 0;
}
KisColorizeMaskSP mask = new KisColorizeMask();
createMaskCommon(mask, activeNode, 0, kundo2_i18n("Add Colorize Mask"), "KisColorizeMask", i18n("Colorize Mask"), true, false);
mask->setImage(m_view->image());
mask->initializeCompositeOp();
delete mask->setColorSpace(mask->parent()->colorSpace());
return mask;
}
KisNodeSP KisMaskManager::createTransformMask(KisNodeSP activeNode)
{
if (!activeNode->isEditable()) {
return 0;
}
KisTransformMaskSP mask = new KisTransformMask();
createMaskCommon(mask, activeNode, 0, kundo2_i18n("Add Transform Mask"), "KisTransformMask", i18n("Transform Mask"), true, false);
return mask;
}
void KisMaskManager::maskProperties()
{
if (!activeMask()) return;
if (activeMask()->inherits("KisFilterMask")) {
KisFilterMask *mask = static_cast<KisFilterMask*>(activeMask().data());
KisLayerSP layer = qobject_cast<KisLayer*>(mask->parent().data());
if (! layer)
return;
KisPaintDeviceSP dev = layer->original();
if (!dev) {
return;
}
KisDlgAdjLayerProps dlg(layer, mask, dev, m_view, mask->filter().data(), mask->name(), i18n("Filter Mask Properties"), m_view->mainWindow(), "dlgeffectmaskprops");
KisFilterConfigurationSP configBefore(mask->filter());
Q_ASSERT(configBefore);
QString xmlBefore = configBefore->toXML();
if (dlg.exec() == QDialog::Accepted) {
KisFilterConfigurationSP configAfter(dlg.filterConfiguration());
Q_ASSERT(configAfter);
QString xmlAfter = configAfter->toXML();
mask->setName(dlg.layerName());
if(xmlBefore != xmlAfter) {
KisChangeFilterCmd *cmd
= new KisChangeFilterCmd(mask,
- configBefore->name(),
- xmlBefore,
- configAfter->name(),
- xmlAfter,
- false);
+ configBefore->cloneWithResourcesSnapshot(),
+ configAfter->cloneWithResourcesSnapshot());
// 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) {
- mask->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data()));
+ mask->setFilter(configBefore->cloneWithResourcesSnapshot());
mask->setDirty();
}
}
} else {
// Not much to show for transparency or selection masks?
}
}
diff --git a/libs/ui/kis_md5_generator.h b/libs/ui/kis_md5_generator.h
deleted file mode 100644
index 5acc7138fe..0000000000
--- a/libs/ui/kis_md5_generator.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 2015 Stefano Bonicatti <smjert@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 <resources/KoMD5Generator.h>
-#include "kritaui_export.h"
-class KRITAUI_EXPORT KisMD5Generator : public KoMD5Generator
-{
-public:
- KisMD5Generator();
- ~KisMD5Generator() override;
-
- QByteArray generateHash(const QString &filename) override;
- using KoMD5Generator::generateHash;
-};
diff --git a/libs/ui/kis_node_filter_proxy_model.cpp b/libs/ui/kis_node_filter_proxy_model.cpp
index 425ab91919..afcf1c820f 100644
--- a/libs/ui/kis_node_filter_proxy_model.cpp
+++ b/libs/ui/kis_node_filter_proxy_model.cpp
@@ -1,167 +1,167 @@
/*
* 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_filter_proxy_model.h"
#include <QSet>
#include "kis_node.h"
#include "kis_node_model.h"
#include "kis_node_manager.h"
#include "kis_signal_compressor.h"
#include "kis_image.h"
struct KisNodeFilterProxyModel::Private
{
Private()
: nodeModel(0),
activeNodeCompressor(1000, KisSignalCompressor::FIRST_INACTIVE)
{}
KisNodeModel *nodeModel;
KisNodeSP pendingActiveNode;
KisNodeSP activeNode;
QSet<int> acceptedLabels;
KisSignalCompressor activeNodeCompressor;
bool isUpdatingFilter = false;
bool checkIndexAllowedRecursively(QModelIndex srcIndex);
};
KisNodeFilterProxyModel::KisNodeFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent),
m_d(new Private)
{
connect(&m_d->activeNodeCompressor, SIGNAL(timeout()), SLOT(slotUpdateCurrentNodeFilter()), Qt::QueuedConnection);
}
KisNodeFilterProxyModel::~KisNodeFilterProxyModel()
{
}
void KisNodeFilterProxyModel::setNodeModel(KisNodeModel *model)
{
m_d->nodeModel = model;
setSourceModel(model);
}
bool KisNodeFilterProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (m_d->isUpdatingFilter && role == KisNodeModel::ActiveRole) {
return false;
}
return QSortFilterProxyModel::setData(index, value, role);
}
bool KisNodeFilterProxyModel::Private::checkIndexAllowedRecursively(QModelIndex srcIndex)
{
if (!srcIndex.isValid()) return false;
KisNodeSP node = nodeModel->nodeFromIndex(srcIndex);
if (node == activeNode ||
acceptedLabels.contains(node->colorLabelIndex())) {
return true;
}
bool result = false;
const int numChildren = srcIndex.model()->rowCount(srcIndex);
for (int i = 0; i < numChildren; i++) {
QModelIndex child = nodeModel->index(i, 0, srcIndex);
if (checkIndexAllowedRecursively(child)) {
result = true;
break;
}
}
return result;
}
bool KisNodeFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
KIS_ASSERT_RECOVER(m_d->nodeModel) { return true; }
const QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
if (!index.isValid()) return false;
KisNodeSP node = m_d->nodeModel->nodeFromIndex(index);
return !node ||
m_d->acceptedLabels.isEmpty() ||
m_d->checkIndexAllowedRecursively(index);
}
KisNodeSP KisNodeFilterProxyModel::nodeFromIndex(const QModelIndex &index) const
{
KIS_ASSERT_RECOVER(m_d->nodeModel) { return 0; }
QModelIndex srcIndex = mapToSource(index);
return m_d->nodeModel->nodeFromIndex(srcIndex);
}
QModelIndex KisNodeFilterProxyModel::indexFromNode(KisNodeSP node) const
{
KIS_ASSERT_RECOVER(m_d->nodeModel) { return QModelIndex(); }
QModelIndex srcIndex = m_d->nodeModel->indexFromNode(node);
return mapFromSource(srcIndex);
}
void KisNodeFilterProxyModel::setAcceptedLabels(const QList<int> &value)
{
- m_d->acceptedLabels = QSet<int>::fromList(value);
+ m_d->acceptedLabels = QSet<int>(value.begin(), value.end());
invalidateFilter();
}
void KisNodeFilterProxyModel::setActiveNode(KisNodeSP node)
{
// NOTE: the current node might change due to beginRemoveRows, in such case
// we must ensure we don't trigger recursive model invalidation.
// the new node may temporary become null when the last layer
// of the document in removed
m_d->pendingActiveNode = node;
m_d->activeNodeCompressor.start();
}
void KisNodeFilterProxyModel::slotUpdateCurrentNodeFilter()
{
m_d->activeNode = m_d->pendingActiveNode;
/**
* During the filter update the model might emit "current changed" signals,
* which (in their turn) will issue setData(..., KisNodeModel::ActiveRole)
* call, leading to a double recursion. Which, obviously, crashes Krita.
*
* Right now, just blocking the KisNodeModel::ActiveRole call is the
* most obvious solution for the problem.
*/
m_d->isUpdatingFilter = true;
invalidateFilter();
m_d->isUpdatingFilter = false;
}
void KisNodeFilterProxyModel::unsetDummiesFacade()
{
m_d->nodeModel->setDummiesFacade(0, 0, 0, 0, 0);
m_d->pendingActiveNode = 0;
m_d->activeNode = 0;
}
diff --git a/libs/ui/kis_paintop_box.cc b/libs/ui/kis_paintop_box.cc
index 1a6e64a9de..b2b4e4525a 100644
--- a/libs/ui/kis_paintop_box.cc
+++ b/libs/ui/kis_paintop_box.cc
@@ -1,1377 +1,1391 @@
/*
* 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 <kis_types.h>
#include <kactioncollection.h>
#include <kacceleratormanager.h>
#include <QKeySequence>
-
#include <kis_icon.h>
#include <KoColorSpace.h>
#include <KoCompositeOpRegistry.h>
-#include <KoResourceServerAdapter.h>
#include <KoToolManager.h>
#include <KoColorSpaceRegistry.h>
+#include <KoResource.h>
+#include <KisResourceDirtyStateSaver.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 "KisPopupButton.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 "KisHighlightedToolButton.h"
-
-typedef KoResourceServerSimpleConstruction<KisPaintOpPreset, SharedPointerStoragePolicy<KisPaintOpPresetSP> > KisPaintOpPresetResourceServer;
-typedef KoResourceServerAdapter<KisPaintOpPreset, SharedPointerStoragePolicy<KisPaintOpPresetSP> > KisPaintOpPresetResourceServerAdapter;
+#include <KisGlobalResourcesInterface.h>
KisPaintopBox::KisPaintopBox(KisViewManager *view, QWidget *parent, const char *name)
: QWidget(parent)
, m_resourceProvider(view->canvasResourceProvider())
, 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(i18n("%"));
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(i18n("%"));
slFlow->setMinimumWidth(qMax(sliderWidth, slFlow->sizeHint().width()));
slFlow->setFixedHeight(iconsize);
slFlow->setBlockUpdateSignalOnDrag(true);
slSize->setRange(0.01, 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->createBlendmodeActions()) {
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()));
+ 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);
+ KisActionRegistry::instance()->propertizeAction(ResourceType::Workspaces, action);
+ view->actionCollection()->addAction(ResourceType::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(signalResourceSelected(KoResourceSP )), SLOT(resourceSelected(KoResourceSP )));
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_presetsChooserPopup, SIGNAL(resourceSelected(KoResourceSP )) , SLOT(resourceSelected(KoResourceSP )));
+ connect(m_presetsChooserPopup, SIGNAL(resourceClicked(KoResourceSP )) , SLOT(resourceSelected(KoResourceSP )));
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->canvasResourceProvider(), 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;
- }
- }
+ findDefaultPresets();
}
+
KisPaintopBox::~KisPaintopBox()
{
KisConfig cfg(false);
QMapIterator<TabletToolID, TabletToolData> iter(m_tabletToolMap);
while (iter.hasNext()) {
iter.next();
if ((iter.key().pointer) == QTabletEvent::Eraser) {
cfg.writeEntry(QString("LastEraser") , iter.value().preset->name());
}
else {
cfg.writeEntry(QString("LastPreset"), 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)
+void KisPaintopBox::restoreResource(KoResourceSP resource)
{
- KisPaintOpPreset* preset = dynamic_cast<KisPaintOpPreset*>(resource);
+ KisPaintOpPresetSP preset = resource.dynamicCast<KisPaintOpPreset>();
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)
+void KisPaintopBox::resourceSelected(KoResourceSP 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();
+ // qDebug() << ">>>>>>>>>>>>>>>" << resource
+ // << (resource ? resource->name() : "")
+ // << (resource ? QString("%1").arg(resource->valid()) : "")
+ // << (resource ? QString("%1").arg(resource->filename()) : "");
+
+ KisPaintOpPresetSP preset = resource.dynamicCast<KisPaintOpPreset>();
+
+ if (preset && preset->valid() && preset != m_resourceProvider->currentPreset()) {
+ qWarning() << "Preset reloading if presets are dirty is broken";
+ // if (!preset->settings()->isLoadable()) {
+ // return;
+ // }
+ // if (!m_dirtyPresetsEnabled) {
+ // KisSignalsBlocker blocker(m_optionWidget);
+ // Q_UNUSED(blocker)
+ // if (!preset->load()) {
+ // qWarning() << "failed to load the preset.";
+ // }
+ // }
+ dbgResources << "resourceSelected: preset" << preset << (preset ? QString("%1").arg(preset->valid()) : "");
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->setResource(preset.data());
+ m_brushEditorPopupButton->setThumbnail(preset->image());
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";
}
m_currCompositeOpID = preset->settings()->paintOpCompositeOp();
updateCompositeOp(m_currCompositeOpID);
}
void KisPaintopBox::slotUpdateOptionsWidgetPopup()
{
KisPaintOpPresetSP preset = m_resourceProvider->currentPreset();
// This happens when we have a new brush engine for which no default preset exists yet.
if (!preset) return;
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->resourceSelected(preset);
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);
+ KisPaintOpPresetSP preset(new KisPaintOpPreset(path));
- if (!preset->load()) {
- preset = KisPaintOpRegistry::instance()->defaultPreset(paintOp);
+ if (!preset->load(KisGlobalResourcesInterface::instance())) {
+ preset = KisPaintOpRegistry::instance()->defaultPreset(paintOp, KisGlobalResourcesInterface::instance());
}
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);
}
}
else if (!node) {
KisSignalsBlocker b1(m_cmbCompositeOp);
m_cmbCompositeOp->selectCompositeOp(KoID(compositeOpID));
m_currCompositeOpID = compositeOpID;
}
}
void KisPaintopBox::setWidgetState(int flags)
{
if (flags & (ENABLE_COMPOSITEOP | DISABLE_COMPOSITEOP)) {
m_cmbCompositeOp->setEnabled(flags & ENABLE_COMPOSITEOP);
m_eraseModeButton->setEnabled(flags & ENABLE_COMPOSITEOP);
}
if (flags & (ENABLE_PRESETS | DISABLE_PRESETS)) {
m_presetSelectorPopupButton->setEnabled(flags & ENABLE_PRESETS);
m_brushEditorPopupButton->setEnabled(flags & ENABLE_PRESETS);
}
for (int i = 0; i < 3; ++i) {
if (flags & (ENABLE_OPACITY | DISABLE_OPACITY))
m_sliderChooser[i]->getWidget("opacity")->setEnabled(flags & ENABLE_OPACITY);
if (flags & (ENABLE_FLOW | DISABLE_FLOW))
m_sliderChooser[i]->getWidget("flow")->setEnabled(flags & ENABLE_FLOW);
if (flags & (ENABLE_SIZE | DISABLE_SIZE))
m_sliderChooser[i]->getWidget("size")->setEnabled(flags & ENABLE_SIZE);
}
}
void KisPaintopBox::setSliderValue(const QString& sliderID, qreal value)
{
for (int i = 0; i < 3; ++i) {
KisDoubleSliderSpinBox* slider = m_sliderChooser[i]->getWidget<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();
+ //qDebug() << "found stored preset " << preset->name() << "for" << inputDevice.uniqueTabletId();
//else
- //qDebug() << "no preset found for" << inputDevice.uniqueTabletId();
+ //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
+ m_presetsPopup->resourceSelected(preset); // 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->canvasResourceProvider()->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());
+ resourceSelected(preset);
}
/**
* 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());
}
if (key == KisCanvasResourceProvider::MirrorHorizontal) {
m_hMirrorAction->setChecked(value.toBool());
}
if (key == KisCanvasResourceProvider::MirrorVertical) {
m_vMirrorAction->setChecked(value.toBool());
}
sender()->blockSignals(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());
+ m_presetsPopup->resourceSelected(preset);
}
void KisPaintopBox::slotNodeChanged(const KisNodeSP node)
{
if (m_previousNode.isValid() && m_previousNode->paintDevice())
disconnect(m_previousNode->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*)));
// Reconnect colorspace change of node
if (node && node->paintDevice()) {
connect(node->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*)));
m_resourceProvider->setCurrentCompositeOp(m_currCompositeOpID);
m_previousNode = node;
slotColorSpaceChanged(node->colorSpace());
}
if (m_optionWidget) {
m_optionWidget->setNode(node);
}
}
void KisPaintopBox::slotColorSpaceChanged(const KoColorSpace* colorSpace)
{
m_cmbCompositeOp->validate(colorSpace);
}
void KisPaintopBox::slotToggleEraseMode(bool checked)
{
const bool oldEraserMode = m_resourceProvider->eraserMode();
m_resourceProvider->setEraserMode(checked);
if (oldEraserMode != checked && m_eraserBrushSizeEnabled) {
const qreal currentSize = m_resourceProvider->size();
KisPaintOpSettingsSP settings = m_resourceProvider->currentPreset()->settings();
// remember brush size. set the eraser size to the normal brush size if not set
if (checked) {
settings->setSavedBrushSize(currentSize);
if (qFuzzyIsNull(settings->savedEraserSize())) {
settings->setSavedEraserSize(currentSize);
}
} else {
settings->setSavedEraserSize(currentSize);
if (qFuzzyIsNull(settings->savedBrushSize())) {
settings->setSavedBrushSize(currentSize);
}
}
//update value in UI (this is the main place the value is 'stored' in memory)
qreal newSize = checked ? settings->savedEraserSize() : settings->savedBrushSize();
m_resourceProvider->setSize(newSize);
}
- if (oldEraserMode != checked && m_eraserBrushOpacityEnabled) {
+ 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());
+ m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset());
}
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);
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;
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();
+ QVector<QString> presets = m_favoriteResourceManager->favoritePresetNamesList();
for (int i=0; i < presets.size(); ++i) {
if (m_resourceProvider->currentPreset() &&
- m_resourceProvider->currentPreset()->name() == presets[i]->name()) {
+ m_resourceProvider->currentPreset()->name() == presets[i]) {
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())));
+ 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();
+ QVector<QString> presets = m_favoriteResourceManager->favoritePresetNamesList();
for(int i = 0; i < presets.size(); ++i) {
- if (m_resourceProvider->currentPreset()->name() == presets[i]->name()) {
+ if (m_resourceProvider->currentPreset()->name() == presets[i]) {
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())));
+ 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())));
+ 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());
+ // 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();
+ QSharedPointer<KisPaintOpPreset> preset = rserver->resourceByName(m_resourceProvider->currentPreset()->name());
if (preset) {
- preset->load();
+ preset->load(KisGlobalResourcesInterface::instance());
}
if (m_resourceProvider->currentPreset() != preset) {
m_resourceProvider->setPaintOpPreset(preset);
} else {
/**
* HACK ALERT: here we emit a private signal from the resource manager to
* ensure that all the subscribers of resource-changed signal got the
* notification. That is especially important for
* KisPaintopTransformationConnector. See bug 392622.
*/
emit m_resourceProvider->resourceManager()->canvasResourceChanged(
- KisCanvasResourceProvider::CurrentPaintOpPreset,
- QVariant::fromValue(preset));
+ KisCanvasResourceProvider::CurrentPaintOpPreset,
+ QVariant::fromValue(preset));
}
}
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());
+ KisPaintOpPreset::UpdatedPostponer postponer(preset);
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());
+ m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset());
// 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());
+ KisResourceDirtyStateSaver dirtySaver(preset);
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");
}
}
}
}
void KisPaintopBox::slotDirtyPresetToggled(bool value)
{
if (!value) {
slotReloadPreset();
- m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data());
+ m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset());
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();
+ m_resourceProvider->mirrorHorizontalMoveCanvasToCenter();
}
void KisPaintopBox::slotMoveToCenterMirrorY() {
- m_resourceProvider->mirrorVerticalMoveCanvasToCenter();
+ m_resourceProvider->mirrorVerticalMoveCanvasToCenter();
+}
+
+void KisPaintopBox::findDefaultPresets()
+{
+ KisPaintOpPresetResourceServer *rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
+ m_eraserName = "eraser_circle";
+ m_defaultPresetName = "basic_tip_default";
+
+ KisResourceModel *resourceModel = rserver->resourceModel();
+
+ for (int i = 0; i < resourceModel->rowCount(); i++) {
+
+ QModelIndex idx = resourceModel->index(i, 0);
+
+ QString resourceName = idx.data(Qt::UserRole + KisResourceModel::Name).toString().toLower();
+ QString fileName = idx.data(Qt::UserRole + KisResourceModel::Filename).toString().toLower();
+
+ if (resourceName.contains("eraser_circle")) {
+ m_eraserName = resourceName;
+ }
+ else if (resourceName.contains("eraser") || fileName.contains("eraser")) {
+ m_eraserName = resourceName;
+ }
+
+ if (resourceName.contains("basic_tip_default")) {
+ m_defaultPresetName = resourceName;
+ }
+ else if (resourceName.contains("default") || fileName.contains("default")) {
+ m_defaultPresetName = resourceName;
+ }
+ }
}
diff --git a/libs/ui/kis_paintop_box.h b/libs/ui/kis_paintop_box.h
index 173f67f23a..6d7fe5dea3 100644
--- a/libs/ui/kis_paintop_box.h
+++ b/libs/ui/kis_paintop_box.h
@@ -1,266 +1,265 @@
/*
* kis_paintop_box.h - part of KImageShop/Krayon/Krita
*
* Copyright (c) 2004-2008 Boudewijn Rempt (boud@valdyas.org)
* 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.
*/
#ifndef KIS_PAINTOP_BOX_H_
#define KIS_PAINTOP_BOX_H_
#include <QMap>
#include <QWidget>
#include <QList>
-
+#include <KoResource.h>
#include <KoID.h>
#include <KoInputDevice.h>
#include <kis_types.h>
#include <brushengine/kis_paintop_settings.h>
#include <brushengine/kis_locked_properties_proxy.h>
#include <brushengine/kis_locked_properties_server.h>
#include <brushengine/kis_locked_properties.h>
#include "kritaui_export.h"
#include "kis_signal_auto_connection.h"
#include "kis_signal_compressor.h"
-
class QToolButton;
class QString;
class QHBoxLayout;
class KoColorSpace;
-class KoResource;
class KoCanvasController;
class KisViewManager;
class KisCanvasResourceProvider;
class KisPopupButton;
class KisIconWidget;
class KisToolOptionsPopup;
class KisPaintOpPresetsPopup;
class KisPaintOpPresetsChooserPopup;
class KisPaintOpConfigWidget;
class KisCompositeOpComboBox;
class KisWidgetChooser;
class KisFavoriteResourceManager;
class KisAction;
class KisPresetSaveWidget;
/**
* This widget presents all paintops that a user can paint with.
* Paintops represent real-world tools or the well-known Shoup
* computer equivalents that do nothing but change color.
*
* To incorporate the dirty preset functionality and locked settings
* the following slots are added
* void slotReloadPreset();
void slotGuiChangedCurrentPreset();
void slotSaveLockedOptionToPreset(KisPropertiesConfigurationSP p);
void slotDropLockedOption(KisPropertiesConfigurationSP p);
void slotDirtyPresetToggled(bool);
Every time a value is changed in a preset, the preset is made dirty through the onChange() function.
For Locked Settings however, a changed Locked Setting will not cause a preset to become dirty. That is
because it borrows its values from the KisLockedPropertiesServer.
Hence the dirty state of the Preset is kept consistent before and after a writeConfiguration operation in most cases.
* XXX: When we have a lot of paintops, replace the listbox
* with a table, and for every category a combobox.
*
* XXX: instead of text, use pretty pictures.
*/
class KRITAUI_EXPORT KisPaintopBox : public QWidget
{
Q_OBJECT
enum {
ENABLE_PRESETS = 0x0001,
DISABLE_PRESETS = 0x0002,
ENABLE_COMPOSITEOP = 0x0004,
DISABLE_COMPOSITEOP = 0x0008,
ENABLE_OPACITY = 0x0010,
DISABLE_OPACITY = 0x0020,
ENABLE_FLOW = 0x0040,
DISABLE_FLOW = 0x0080,
ENABLE_SIZE = 0x0100,
DISABLE_SIZE = 0x0200,
ENABLE_ALL = 0x5555,
DISABLE_ALL = 0xAAAA
};
public:
KisPaintopBox(KisViewManager* view, QWidget* parent, const char* name);
~KisPaintopBox() override;
- void restoreResource(KoResource* resource);
+ void restoreResource(KoResourceSP resource);
/**
* Update the option widgets to the argument ones, removing the currently set widgets.
*/
void newOptionWidgets(const QList<QPointer<QWidget> > & optionWidgetList);
KisFavoriteResourceManager *favoriteResourcesManager() { return m_favoriteResourceManager; }
public Q_SLOTS:
void slotColorSpaceChanged(const KoColorSpace* colorSpace);
void slotInputDeviceChanged(const KoInputDevice & inputDevice);
void slotCanvasResourceChanged(int key, const QVariant& v);
- void resourceSelected(KoResource* resource);
+ void resourceSelected(KoResourceSP resource);
/// This should take care of creating a new brush preset from scratch
/// It will either load the default brush preset for the engine,
/// or create a new empty preset if a default preset does not exist
void slotCreatePresetFromScratch(QString paintop);
private:
void setCurrentPaintop(const KoID& paintop);
void setCurrentPaintop(KisPaintOpPresetSP preset);
KisPaintOpPresetSP defaultPreset(const KoID& paintOp);
KisPaintOpPresetSP activePreset(const KoID& paintOp);
void updateCompositeOp(QString compositeOpID);
void setWidgetState(int flags);
void setSliderValue(const QString& sliderID, qreal value);
void sliderChanged(int n);
+ void findDefaultPresets();
private Q_SLOTS:
void slotSetupDefaultPreset();
void slotNodeChanged(const KisNodeSP node);
void slotToggleEraseMode(bool checked);
void slotSetCompositeMode(int index);
void slotSetPaintop(const QString& paintOpId);
void slotHorizontalMirrorChanged(bool value);
void slotVerticalMirrorChanged(bool value);
void slotSlider1Changed();
void slotSlider2Changed();
void slotSlider3Changed();
void slotToolChanged(KoCanvasController* canvas, int toolId);
void slotPreviousFavoritePreset();
void slotNextFavoritePreset();
void slotSwitchToPreviousPreset();
void slotUnsetEraseMode();
void slotToggleAlphaLockMode(bool);
void slotDisablePressureMode(bool);
void slotReloadPreset();
void slotGuiChangedCurrentPreset();
void slotSaveLockedOptionToPreset(KisPropertiesConfigurationSP p);
void slotDropLockedOption(KisPropertiesConfigurationSP p);
void slotDirtyPresetToggled(bool);
void slotEraserBrushSizeToggled(bool);
void slotEraserBrushOpacityToggled(bool);
void slotUpdateSelectionIcon();
void slotLockXMirrorToggle(bool);
void slotLockYMirrorToggle(bool);
void slotMoveToCenterMirrorX();
void slotMoveToCenterMirrorY();
void slotHideDecorationMirrorX(bool);
void slotHideDecorationMirrorY(bool);
void slotUpdateOptionsWidgetPopup();
private:
KisCanvasResourceProvider* m_resourceProvider;
QHBoxLayout* m_layout;
QWidget* m_paintopWidget;
KisPaintOpConfigWidget* m_optionWidget;
KisPopupButton* m_toolOptionsPopupButton;
KisPresetSaveWidget* m_savePresetWidget;
KisIconWidget* m_brushEditorPopupButton;
KisPopupButton* m_presetSelectorPopupButton;
KisCompositeOpComboBox* m_cmbCompositeOp;
QToolButton* m_eraseModeButton;
QToolButton* m_alphaLockButton;
QToolButton* m_hMirrorButton;
QToolButton* m_vMirrorButton;
KisToolOptionsPopup* m_toolOptionsPopup;
KisPaintOpPresetsPopup* m_presetsPopup;
KisPaintOpPresetsChooserPopup* m_presetsChooserPopup;
KisViewManager* m_viewManager;
KisPopupButton* m_workspaceWidget;
KisWidgetChooser* m_sliderChooser[3];
QMap<KoID, KisPaintOpConfigWidget*> m_paintopOptionWidgets;
KisFavoriteResourceManager* m_favoriteResourceManager;
QToolButton* m_reloadButton;
KisAction* m_eraseAction;
KisAction* m_reloadAction;
KisAction* m_disablePressureAction;
QString m_currCompositeOpID;
KisNodeWSP m_previousNode;
KisAction* m_hMirrorAction;
KisAction* m_vMirrorAction;
KisAction* hideCanvasDecorationsX;
KisAction* lockActionX;
KisAction* moveToCenterActionX;
KisAction* hideCanvasDecorationsY;
KisAction* lockActionY;
KisAction* moveToCenterActionY;
struct TabletToolID {
TabletToolID(const KoInputDevice& dev) {
// Only the eraser is special, and we don't look at Cursor
pointer = QTabletEvent::Pen;
if (dev.pointer() == QTabletEvent::Eraser) {
pointer = QTabletEvent::Eraser;
}
}
bool operator == (const TabletToolID& id) const {
return pointer == id.pointer;
}
bool operator < (const TabletToolID& id) const {
return pointer < id.pointer;
}
QTabletEvent::PointerType pointer;
};
struct TabletToolData {
KoID paintOpID;
KisPaintOpPresetSP preset;
};
typedef QMap<TabletToolID, TabletToolData> TabletToolMap;
typedef QMap<KoID, KisPaintOpPresetSP> PaintOpPresetMap;
TabletToolMap m_tabletToolMap;
PaintOpPresetMap m_paintOpPresetMap;
TabletToolID m_currTabletToolID;
bool m_presetsEnabled;
bool m_blockUpdate;
bool m_dirtyPresetsEnabled;
bool m_eraserBrushSizeEnabled;
bool m_eraserBrushOpacityEnabled;
KisSignalAutoConnectionsStore m_presetConnections;
QString m_eraserName;
QString m_defaultPresetName;
};
#endif //KIS_PAINTOP_BOX_H_
diff --git a/libs/ui/kis_popup_palette.cpp b/libs/ui/kis_popup_palette.cpp
index 5b2b030b02..408e67ebd5 100644
--- a/libs/ui/kis_popup_palette.cpp
+++ b/libs/ui/kis_popup_palette.cpp
@@ -1,987 +1,1004 @@
/* This file is part of the KDE project
Copyright 2009 Vera Lukman <shicmap@gmail.com>
Copyright 2011 Sven Langkamp <sven.langkamp@gmail.com>
Copyright 2016 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 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 <QtGui>
+#include <QMenu>
+#include <QWhatsThis>
+#include <QVBoxLayout>
+#include <QElapsedTimer>
+
+#include <KisTagModel.h>
+#include <KisTagModelProvider.h>
#include "kis_canvas2.h"
#include "kis_config.h"
#include "kis_popup_palette.h"
#include "kis_favorite_resource_manager.h"
#include "kis_icon_utils.h"
-#include "KisResourceServerProvider.h"
#include <kis_canvas_resource_provider.h>
#include <KoTriangleColorSelector.h>
#include <KisVisualColorSelector.h>
#include <kis_config_notifier.h>
-#include <QtGui>
-#include <QMenu>
-#include <QWhatsThis>
-#include <QVBoxLayout>
-#include <QElapsedTimer>
#include "kis_signal_compressor.h"
#include "brushhud/kis_brush_hud.h"
#include "brushhud/kis_round_hud_button.h"
#include "kis_signals_blocker.h"
#include "kis_canvas_controller.h"
#include "kis_acyclic_signal_connector.h"
+#include <kis_paintop_preset.h>
#include "KisMouseClickEater.h"
+
class PopupColorTriangle : public KoTriangleColorSelector
{
public:
PopupColorTriangle(const KoColorDisplayRendererInterface *displayRenderer, QWidget* parent)
: KoTriangleColorSelector(displayRenderer, parent)
, m_dragging(false)
{
}
~PopupColorTriangle() override {}
void tabletEvent(QTabletEvent* event) override {
event->accept();
QMouseEvent* mouseEvent = 0;
// this will tell the pop-up palette widget to close
if(event->button() == Qt::RightButton) {
emit requestCloseContainer();
}
// ignore any tablet events that are done with the right click
// Tablet move events don't return a "button", so catch that too
if(event->button() == Qt::LeftButton || event->type() == QEvent::TabletMove)
{
switch (event->type()) {
case QEvent::TabletPress:
mouseEvent = new QMouseEvent(QEvent::MouseButtonPress, event->pos(),
Qt::LeftButton, Qt::LeftButton, event->modifiers());
m_dragging = true;
mousePressEvent(mouseEvent);
break;
case QEvent::TabletMove:
mouseEvent = new QMouseEvent(QEvent::MouseMove, event->pos(),
(m_dragging) ? Qt::LeftButton : Qt::NoButton,
(m_dragging) ? Qt::LeftButton : Qt::NoButton, event->modifiers());
mouseMoveEvent(mouseEvent);
break;
case QEvent::TabletRelease:
mouseEvent = new QMouseEvent(QEvent::MouseButtonRelease, event->pos(),
Qt::LeftButton,
Qt::LeftButton,
event->modifiers());
m_dragging = false;
mouseReleaseEvent(mouseEvent);
break;
default: break;
}
}
delete mouseEvent;
}
private:
bool m_dragging;
};
KisPopupPalette::KisPopupPalette(KisViewManager* viewManager, KisCoordinatesConverter* coordinatesConverter ,KisFavoriteResourceManager* manager,
const KoColorDisplayRendererInterface *displayRenderer, KisCanvasResourceProvider *provider, QWidget *parent)
: QWidget(parent, Qt::FramelessWindowHint)
, m_coordinatesConverter(coordinatesConverter)
, m_viewManager(viewManager)
, m_actionManager(viewManager->actionManager())
, m_resourceManager(manager)
, m_displayRenderer(displayRenderer)
, m_colorChangeCompressor(new KisSignalCompressor(50, KisSignalCompressor::POSTPONE))
, m_actionCollection(viewManager->actionCollection())
, m_acyclicConnector(new KisAcyclicSignalConnector(this))
, m_clicksEater(new KisMouseClickEater(Qt::RightButton, 1, this))
{
// some UI controls are defined and created based off these variables
const int borderWidth = 3;
if (KisConfig(true).readEntry<bool>("popuppalette/usevisualcolorselector", false)) {
m_triangleColorSelector = new KisVisualColorSelector(this);
}
else {
m_triangleColorSelector = new PopupColorTriangle(displayRenderer, this);
}
m_triangleColorSelector->setDisplayRenderer(displayRenderer);
m_triangleColorSelector->setConfig(true,false);
m_triangleColorSelector->move(m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth, m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth);
m_triangleColorSelector->resize(m_colorHistoryInnerRadius*2-borderWidth*2, m_colorHistoryInnerRadius*2-borderWidth*2);
m_triangleColorSelector->setVisible(true);
KoColor fgcolor(Qt::black, KoColorSpaceRegistry::instance()->rgb8());
if (m_resourceManager) {
fgcolor = provider->fgColor();
}
m_triangleColorSelector->slotSetColor(fgcolor);
/**
* Tablet support code generates a spurious right-click right after opening
* the window, so we should ignore it. Next right-click will be used for
* closing the popup palette
*/
this->installEventFilter(m_clicksEater);
m_triangleColorSelector->installEventFilter(m_clicksEater);
QRegion maskedRegion(0, 0, m_triangleColorSelector->width(), m_triangleColorSelector->height(), QRegion::Ellipse );
m_triangleColorSelector->setMask(maskedRegion);
//setAttribute(Qt::WA_TranslucentBackground, true);
connect(m_triangleColorSelector, SIGNAL(sigNewColor(KoColor)),
m_colorChangeCompressor.data(), SLOT(start()));
connect(m_colorChangeCompressor.data(), SIGNAL(timeout()),
SLOT(slotEmitColorChanged()));
connect(m_triangleColorSelector, SIGNAL(requestCloseContainer()), this, SLOT(slotHide()));
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), m_triangleColorSelector, SLOT(configurationChanged()));
m_acyclicConnector->connectForwardKoColor(m_resourceManager, SIGNAL(sigChangeFGColorSelector(KoColor)),
this, SLOT(slotExternalFgColorChanged(KoColor)));
m_acyclicConnector->connectBackwardKoColor(this, SIGNAL(sigChangefGColor(KoColor)),
m_resourceManager, SIGNAL(sigSetFGColor(KoColor)));
connect(this, SIGNAL(sigChangeActivePaintop(int)), m_resourceManager, SLOT(slotChangeActivePaintop(int)));
connect(this, SIGNAL(sigUpdateRecentColor(int)), m_resourceManager, SLOT(slotUpdateRecentColor(int)));
connect(m_resourceManager, SIGNAL(setSelectedColor(int)), SLOT(slotSetSelectedColor(int)));
connect(m_resourceManager, SIGNAL(updatePalettes()), SLOT(slotUpdate()));
connect(m_resourceManager, SIGNAL(hidePalettes()), SLOT(slotHide()));
// This is used to handle a bug:
// If pop up palette is visible and a new colour is selected, the new colour
// will be added when the user clicks on the canvas to hide the palette
// In general, we want to be able to store recent color if the pop up palette
// is not visible
m_timer.setSingleShot(true);
connect(this, SIGNAL(sigTriggerTimer()), this, SLOT(slotTriggerTimer()));
connect(&m_timer, SIGNAL(timeout()), this, SLOT(slotEnableChangeFGColor()));
connect(this, SIGNAL(sigEnableChangeFGColor(bool)), m_resourceManager, SIGNAL(sigEnableChangeColor(bool)));
setCursor(Qt::ArrowCursor);
setMouseTracking(true);
setHoveredPreset(-1);
setHoveredColor(-1);
setSelectedColor(-1);
m_brushHud = new KisBrushHud(provider, parent);
m_brushHud->setFixedHeight(int(m_popupPaletteSize));
m_brushHud->setVisible(false);
const int auxButtonSize = 35;
m_settingsButton = new KisRoundHudButton(this);
m_settingsButton->setGeometry(m_popupPaletteSize - 2.2 * auxButtonSize, m_popupPaletteSize - auxButtonSize,
auxButtonSize, auxButtonSize);
connect(m_settingsButton, SIGNAL(clicked()), SLOT(slotShowTagsPopup()));
KisConfig cfg(true);
m_brushHudButton = new KisRoundHudButton(this);
m_brushHudButton->setCheckable(true);
m_brushHudButton->setGeometry(m_popupPaletteSize - 1.0 * auxButtonSize, m_popupPaletteSize - auxButtonSize,
auxButtonSize, auxButtonSize);
connect(m_brushHudButton, SIGNAL(toggled(bool)), SLOT(showHudWidget(bool)));
m_brushHudButton->setChecked(cfg.showBrushHud());
// add some stuff below the pop-up palette that will make it easier to use for tablet people
QVBoxLayout* vLayout = new QVBoxLayout(this); // main layout
QSpacerItem* verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
vLayout->addSpacerItem(verticalSpacer); // this should push the box to the bottom
QHBoxLayout* hLayout = new QHBoxLayout();
vLayout->addLayout(hLayout);
mirrorMode = new KisHighlightedToolButton(this);
mirrorMode->setFixedSize(35, 35);
mirrorMode->setToolTip(i18n("Mirror Canvas"));
mirrorMode->setDefaultAction(m_actionCollection->action("mirror_canvas"));
canvasOnlyButton = new KisHighlightedToolButton(this);
canvasOnlyButton->setFixedSize(35, 35);
canvasOnlyButton->setToolTip(i18n("Canvas Only"));
canvasOnlyButton->setDefaultAction(m_actionCollection->action("view_show_canvas_only"));
zoomToOneHundredPercentButton = new QPushButton(this);
zoomToOneHundredPercentButton->setText(i18n("100%"));
zoomToOneHundredPercentButton->setFixedHeight(35);
zoomToOneHundredPercentButton->setToolTip(i18n("Zoom to 100%"));
connect(zoomToOneHundredPercentButton, SIGNAL(clicked(bool)), this, SLOT(slotZoomToOneHundredPercentClicked()));
zoomCanvasSlider = new QSlider(Qt::Horizontal, this);
zoomSliderMinValue = 10; // set in %
zoomSliderMaxValue = 200; // set in %
zoomCanvasSlider->setRange(zoomSliderMinValue, zoomSliderMaxValue);
zoomCanvasSlider->setFixedHeight(35);
zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent());
zoomCanvasSlider->setSingleStep(1);
zoomCanvasSlider->setPageStep(1);
connect(zoomCanvasSlider, SIGNAL(valueChanged(int)), this, SLOT(slotZoomSliderChanged(int)));
connect(zoomCanvasSlider, SIGNAL(sliderPressed()), this, SLOT(slotZoomSliderPressed()));
connect(zoomCanvasSlider, SIGNAL(sliderReleased()), this, SLOT(slotZoomSliderReleased()));
slotUpdateIcons();
hLayout->addWidget(mirrorMode);
hLayout->addWidget(canvasOnlyButton);
hLayout->addWidget(zoomToOneHundredPercentButton);
hLayout->addWidget(zoomCanvasSlider);
setVisible(true);
setVisible(false);
opacityChange = new QGraphicsOpacityEffect(this);
setGraphicsEffect(opacityChange);
// Prevent tablet events from being captured by the canvas
setAttribute(Qt::WA_NoMousePropagation, true);
}
void KisPopupPalette::slotExternalFgColorChanged(const KoColor &color)
{
//hack to get around cmyk for now.
if (color.colorSpace()->colorChannelCount()>3) {
KoColor c(KoColorSpaceRegistry::instance()->rgb8());
c.fromKoColor(color);
m_triangleColorSelector->slotSetColor(c);
} else {
m_triangleColorSelector->slotSetColor(color);
}
}
void KisPopupPalette::slotEmitColorChanged()
{
if (isVisible()) {
update();
emit sigChangefGColor(m_triangleColorSelector->getCurrentColor());
}
}
//setting KisPopupPalette properties
int KisPopupPalette::hoveredPreset() const
{
return m_hoveredPreset;
}
void KisPopupPalette::setHoveredPreset(int x)
{
m_hoveredPreset = x;
}
int KisPopupPalette::hoveredColor() const
{
return m_hoveredColor;
}
void KisPopupPalette::setHoveredColor(int x)
{
m_hoveredColor = x;
}
int KisPopupPalette::selectedColor() const
{
return m_selectedColor;
}
void KisPopupPalette::setSelectedColor(int x)
{
m_selectedColor = x;
}
void KisPopupPalette::slotTriggerTimer()
{
m_timer.start(750);
}
void KisPopupPalette::slotEnableChangeFGColor()
{
emit sigEnableChangeFGColor(true);
}
void KisPopupPalette::slotZoomSliderChanged(int zoom) {
emit zoomLevelChanged(zoom);
}
void KisPopupPalette::slotZoomSliderPressed()
{
m_isZoomingCanvas = true;
}
void KisPopupPalette::slotZoomSliderReleased()
{
m_isZoomingCanvas = false;
}
void KisPopupPalette::adjustLayout(const QPoint &p)
{
KIS_ASSERT_RECOVER_RETURN(m_brushHud);
if (isVisible() && parentWidget()) {
float hudMargin = 30.0;
const QRect fitRect = kisGrowRect(parentWidget()->rect(), -20.0); // -20 is widget margin
const QPoint paletteCenterOffset(m_popupPaletteSize / 2, m_popupPaletteSize / 2);
QRect paletteRect = rect();
paletteRect.moveTo(p - paletteCenterOffset);
if (m_brushHudButton->isChecked()) {
m_brushHud->updateGeometry();
paletteRect.adjust(0, 0, m_brushHud->width() + hudMargin, 0);
}
paletteRect = kisEnsureInRect(paletteRect, fitRect);
move(paletteRect.topLeft());
m_brushHud->move(paletteRect.topLeft() + QPoint(m_popupPaletteSize + hudMargin, 0));
m_lastCenterPoint = p;
}
}
void KisPopupPalette::slotUpdateIcons()
{
this->setPalette(qApp->palette());
for(int i=0; i<this->children().size(); i++) {
QWidget *w = qobject_cast<QWidget*>(this->children().at(i));
if (w) {
w->setPalette(qApp->palette());
}
}
zoomToOneHundredPercentButton->setIcon(m_actionCollection->action("zoom_to_100pct")->icon());
m_brushHud->updateIcons();
m_settingsButton->setIcon(KisIconUtils::loadIcon("tag"));
m_brushHudButton->setOnOffIcons(KisIconUtils::loadIcon("arrow-left"), KisIconUtils::loadIcon("arrow-right"));
}
void KisPopupPalette::showHudWidget(bool visible)
{
KIS_ASSERT_RECOVER_RETURN(m_brushHud);
const bool reallyVisible = visible && m_brushHudButton->isChecked();
if (reallyVisible) {
m_brushHud->updateProperties();
}
m_brushHud->setVisible(reallyVisible);
adjustLayout(m_lastCenterPoint);
KisConfig cfg(false);
cfg.setShowBrushHud(visible);
}
void KisPopupPalette::showPopupPalette(const QPoint &p)
{
showPopupPalette(!isVisible());
adjustLayout(p);
}
void KisPopupPalette::showPopupPalette(bool show)
{
if (show) {
// don't set the zoom slider if we are outside of the zoom slider bounds. It will change the zoom level to within
// the bounds and cause the canvas to jump between the slider's min and max
if (m_coordinatesConverter->zoomInPercent() > zoomSliderMinValue &&
m_coordinatesConverter->zoomInPercent() < zoomSliderMaxValue ){
KisSignalsBlocker b(zoomCanvasSlider);
zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent()); // sync the zoom slider
}
emit sigEnableChangeFGColor(!show);
} else {
emit sigTriggerTimer();
}
setVisible(show);
m_brushHud->setVisible(show && m_brushHudButton->isChecked());
}
//redefinition of setVariable function to change the scope to private
void KisPopupPalette::setVisible(bool b)
{
QWidget::setVisible(b);
}
void KisPopupPalette::setParent(QWidget *parent) {
m_brushHud->setParent(parent);
QWidget::setParent(parent);
}
QSize KisPopupPalette::sizeHint() const
{
return QSize(m_popupPaletteSize, m_popupPaletteSize + 50); // last number is the space for the toolbar below
}
void KisPopupPalette::resizeEvent(QResizeEvent*)
{
}
void KisPopupPalette::paintEvent(QPaintEvent* e)
{
Q_UNUSED(e);
QPainter painter(this);
QPen pen(palette().color(QPalette::Text));
pen.setWidth(3);
painter.setPen(pen);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
// painting background color indicator
QPainterPath bgColor;
bgColor.addEllipse(QPoint( 50, 80), 30, 30);
painter.fillPath(bgColor, m_displayRenderer->toQColor(m_resourceManager->bgColor()));
painter.drawPath(bgColor);
// painting foreground color indicator
QPainterPath fgColor;
fgColor.addEllipse(QPoint( 60, 50), 30, 30);
painter.fillPath(fgColor, m_displayRenderer->toQColor(m_triangleColorSelector->getCurrentColor()));
painter.drawPath(fgColor);
// create a circle background that everything else will go into
QPainterPath backgroundContainer;
float shrinkCircleAmount = 3;// helps the circle when the stroke is put around it
QRectF circleRect(shrinkCircleAmount, shrinkCircleAmount,
m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2);
backgroundContainer.addEllipse( circleRect );
painter.fillPath(backgroundContainer,palette().brush(QPalette::Background));
painter.drawPath(backgroundContainer);
// create a path slightly inside the container circle. this will create a 'track' to indicate that we can rotate the canvas
// with the indicator
QPainterPath rotationTrackPath;
shrinkCircleAmount = 18;
QRectF circleRect2(shrinkCircleAmount, shrinkCircleAmount,
m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2);
rotationTrackPath.addEllipse( circleRect2 );
pen.setWidth(1);
painter.setPen(pen);
painter.drawPath(rotationTrackPath);
// this thing will help indicate where the starting brush preset is at.
// also what direction they go to give sor order to the presets populated
/*
pen.setWidth(6);
pen.setCapStyle(Qt::RoundCap);
painter.setPen(pen);
painter.drawArc(circleRect, (16*90), (16*-30)); // span angle (last parameter) is in 16th of degrees
QPainterPath brushDir;
brushDir.arcMoveTo(circleRect, 60);
brushDir.lineTo(brushDir.currentPosition().x()-5, brushDir.currentPosition().y() - 14);
painter.drawPath(brushDir);
brushDir.lineTo(brushDir.currentPosition().x()-2, brushDir.currentPosition().y() + 6);
painter.drawPath(brushDir);
*/
// the following things needs to be based off the center, so let's translate the painter
painter.translate(m_popupPaletteSize / 2, m_popupPaletteSize / 2);
// create the canvas rotation handle
QPainterPath rotationIndicator = drawRotationIndicator(m_coordinatesConverter->rotationAngle(), true);
painter.fillPath(rotationIndicator,palette().brush(QPalette::Text));
// hover indicator for the canvas rotation
if (m_isOverCanvasRotationIndicator == true) {
painter.save();
QPen pen(palette().color(QPalette::Highlight));
pen.setWidth(2);
painter.setPen(pen);
painter.drawPath(rotationIndicator);
painter.restore();
}
// create a reset canvas rotation indicator to bring the canvas back to 0 degrees
QPainterPath resetRotationIndicator = drawRotationIndicator(0, false);
QPen resetPen(palette().color(QPalette::Text));
resetPen.setWidth(1);
painter.save();
painter.setPen(resetPen);
painter.drawPath(resetRotationIndicator);
painter.restore();
// painting favorite brushes
QList<QImage> images(m_resourceManager->favoritePresetImages());
// painting favorite brushes pixmap/icon
QPainterPath presetPath;
for (int pos = 0; pos < numSlots(); pos++) {
painter.save();
presetPath = createPathFromPresetIndex(pos);
if (pos < images.size()) {
painter.setClipPath(presetPath);
QRect bounds = presetPath.boundingRect().toAlignedRect();
painter.drawImage(bounds.topLeft() , images.at(pos).scaled(bounds.size() , Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
}
else {
painter.fillPath(presetPath, palette().brush(QPalette::Window)); // brush slot that has no brush in it
}
QPen pen = painter.pen();
pen.setWidth(1);
painter.setPen(pen);
painter.drawPath(presetPath);
painter.restore();
}
if (hoveredPreset() > -1) {
presetPath = createPathFromPresetIndex(hoveredPreset());
QPen pen(palette().color(QPalette::Highlight));
pen.setWidth(3);
painter.setPen(pen);
painter.drawPath(presetPath);
}
// paint recent colors area.
painter.setPen(Qt::NoPen);
float rotationAngle = -360.0 / m_resourceManager->recentColorsTotal();
// there might be no recent colors at the start, so paint a placeholder
if (m_resourceManager->recentColorsTotal() == 0) {
painter.setBrush(Qt::transparent);
QPainterPath emptyRecentColorsPath(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
painter.setPen(QPen(palette().color(QPalette::Background).lighter(150), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
painter.drawPath(emptyRecentColorsPath);
} else {
for (int pos = 0; pos < m_resourceManager->recentColorsTotal(); pos++) {
QPainterPath recentColorsPath(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal()));
//accessing recent color of index pos
painter.fillPath(recentColorsPath, m_displayRenderer->toQColor( m_resourceManager->recentColorAt(pos) ));
painter.drawPath(recentColorsPath);
painter.rotate(rotationAngle);
}
}
// painting hovered color
if (hoveredColor() > -1) {
painter.setPen(QPen(palette().color(QPalette::Highlight), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
if (m_resourceManager->recentColorsTotal() == 1) {
QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
painter.drawPath(path_ColorDonut);
} else {
painter.rotate((m_resourceManager->recentColorsTotal() + hoveredColor()) *rotationAngle);
QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal()));
painter.drawPath(path);
painter.rotate(hoveredColor() * -1 * rotationAngle);
}
}
// painting selected color
if (selectedColor() > -1) {
painter.setPen(QPen(palette().color(QPalette::Highlight).darker(130), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
if (m_resourceManager->recentColorsTotal() == 1) {
QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
painter.drawPath(path_ColorDonut);
} else {
painter.rotate((m_resourceManager->recentColorsTotal() + selectedColor()) *rotationAngle);
QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal()));
painter.drawPath(path);
painter.rotate(selectedColor() * -1 * rotationAngle);
}
}
// if we are actively rotating the canvas or zooming, make the panel slightly transparent to see the canvas better
if(m_isRotatingCanvasIndicator || m_isZoomingCanvas) {
opacityChange->setOpacity(0.4);
} else {
opacityChange->setOpacity(1.0);
}
}
QPainterPath KisPopupPalette::drawDonutPathFull(int x, int y, int inner_radius, int outer_radius)
{
QPainterPath path;
path.addEllipse(QPointF(x, y), outer_radius, outer_radius);
path.addEllipse(QPointF(x, y), inner_radius, inner_radius);
path.setFillRule(Qt::OddEvenFill);
return path;
}
QPainterPath KisPopupPalette::drawDonutPathAngle(int inner_radius, int outer_radius, int limit)
{
QPainterPath path;
path.moveTo(-0.999 * outer_radius * sin(M_PI / limit), 0.999 * outer_radius * cos(M_PI / limit));
path.arcTo(-1 * outer_radius, -1 * outer_radius, 2 * outer_radius, 2 * outer_radius, -90.0 - 180.0 / limit,
360.0 / limit);
path.arcTo(-1 * inner_radius, -1 * inner_radius, 2 * inner_radius, 2 * inner_radius, -90.0 + 180.0 / limit,
- 360.0 / limit);
path.closeSubpath();
return path;
}
QPainterPath KisPopupPalette::drawRotationIndicator(qreal rotationAngle, bool canDrag)
{
// used for canvas rotation. This function gets called twice. Once by the canvas rotation indicator,
// and another time by the reset canvas position
float canvasRotationRadians = qDegreesToRadians(rotationAngle - 90); // -90 will make 0 degrees be at the top
float rotationDialXPosition = qCos(canvasRotationRadians) * (m_popupPaletteSize/2 - 10); // m_popupPaletteSize/2 = radius
float rotationDialYPosition = qSin(canvasRotationRadians) * (m_popupPaletteSize/2 - 10);
QPainterPath canvasRotationIndicator;
int canvasIndicatorSize = 15;
int canvasIndicatorMiddle = canvasIndicatorSize / 2;
QRect indicatorRectangle = QRect( rotationDialXPosition - canvasIndicatorMiddle, rotationDialYPosition - canvasIndicatorMiddle,
canvasIndicatorSize, canvasIndicatorSize );
if (canDrag) {
m_canvasRotationIndicatorRect = indicatorRectangle;
} else {
m_resetCanvasRotationIndicatorRect = indicatorRectangle;
}
canvasRotationIndicator.addEllipse(indicatorRectangle.x(), indicatorRectangle.y(),
indicatorRectangle.width(), indicatorRectangle.height() );
return canvasRotationIndicator;
}
void KisPopupPalette::mouseMoveEvent(QMouseEvent *event)
{
QPointF point = event->localPos();
event->accept();
setToolTip(QString());
setHoveredPreset(-1);
setHoveredColor(-1);
// calculate if we are over the canvas rotation knob
// before we started painting, we moved the painter to the center of the widget, so the X/Y positions are offset. we need to
// correct them first before looking for a click event intersection
float rotationCorrectedXPos = m_canvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2);
float rotationCorrectedYPos = m_canvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2);
QRect correctedCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos,
m_canvasRotationIndicatorRect.width(), m_canvasRotationIndicatorRect.height());
if (correctedCanvasRotationIndicator.contains(point.x(), point.y())) {
m_isOverCanvasRotationIndicator = true;
} else {
m_isOverCanvasRotationIndicator = false;
}
if (m_isRotatingCanvasIndicator) {
// we are rotating the canvas, so calculate the rotation angle based off the center
// calculate the angle we are at first
QPoint widgetCenterPoint = QPoint(m_popupPaletteSize/2, m_popupPaletteSize/2);
float dX = point.x() - widgetCenterPoint.x();
float dY = point.y() - widgetCenterPoint.y();
float finalAngle = qAtan2(dY,dX) * 180 / M_PI; // what we need if we have two points, but don't know the angle
finalAngle = finalAngle + 90; // add 90 degrees so 0 degree position points up
float angleDifference = finalAngle - m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs, so find it out
KisCanvasController *canvasController =
dynamic_cast<KisCanvasController*>(m_viewManager->canvasBase()->canvasController());
canvasController->rotateCanvas(angleDifference);
emit sigUpdateCanvas();
}
// don't highlight the presets if we are in the middle of rotating the canvas
if (m_isRotatingCanvasIndicator == false) {
QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
{
int pos = calculatePresetIndex(point, m_resourceManager->numFavoritePresets());
if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()) {
- setToolTip(m_resourceManager->favoritePresetList().at(pos).data()->name());
+ setToolTip(m_resourceManager->favoritePresetNamesList().at(pos));
setHoveredPreset(pos);
}
}
if (pathColor.contains(point)) {
int pos = calculateIndex(point, m_resourceManager->recentColorsTotal());
if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) {
setHoveredColor(pos);
}
}
}
update();
}
void KisPopupPalette::mousePressEvent(QMouseEvent *event)
{
QPointF point = event->localPos();
event->accept();
if (event->button() == Qt::LeftButton) {
//in favorite brushes area
int pos = calculateIndex(point, m_resourceManager->numFavoritePresets());
if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()
&& isPointInPixmap(point, pos)) {
//setSelectedBrush(pos);
update();
}
if (m_isOverCanvasRotationIndicator) {
m_isRotatingCanvasIndicator = true;
}
// reset the canvas if we are over the reset canvas rotation indicator
float rotationCorrectedXPos = m_resetCanvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2);
float rotationCorrectedYPos = m_resetCanvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2);
QRect correctedResetCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos,
m_resetCanvasRotationIndicatorRect.width(), m_resetCanvasRotationIndicatorRect.height());
if (correctedResetCanvasRotationIndicator.contains(point.x(), point.y())) {
float angleDifference = -m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs
KisCanvasController *canvasController =
dynamic_cast<KisCanvasController*>(m_viewManager->canvasBase()->canvasController());
canvasController->rotateCanvas(angleDifference);
emit sigUpdateCanvas();
}
}
}
void KisPopupPalette::slotShowTagsPopup()
{
- KisPaintOpPresetResourceServer *rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
- QStringList tags = rServer->tagNamesList();
- std::sort(tags.begin(), tags.end());
+ KisTagModel *model = KisTagModelProvider::tagModel(ResourceType::PaintOpPresets);
+ QVector<QString> tags;
+ for (int i = 0; i < model->rowCount(); ++i) {
+ QModelIndex idx = model->index(i, 0);
+ tags << model->data(idx, Qt::DisplayRole).toString();
+ }
+
+ //std::sort(tags.begin(), tags.end());
if (!tags.isEmpty()) {
QMenu menu;
Q_FOREACH (const QString& tag, tags) {
menu.addAction(tag);
}
QAction *action = menu.exec(QCursor::pos());
if (action) {
- m_resourceManager->setCurrentTag(action->text());
+
+ for (int i = 0; i < model->rowCount(); ++i) {
+ QModelIndex idx = model->index(i, 0);
+ if (model->data(idx, Qt::DisplayRole).toString() == action->text()) {
+ m_resourceManager->setCurrentTag(model->tagForIndex(idx));
+ break;
+ }
+ }
}
} else {
QWhatsThis::showText(QCursor::pos(),
i18n("There are no tags available to show in this popup. To add presets, you need to tag them and then select the tag here."));
}
+
}
void KisPopupPalette::slotZoomToOneHundredPercentClicked() {
QAction *action = m_actionCollection->action("zoom_to_100pct");
if (action) {
action->trigger();
}
// also move the zoom slider to 100% position so they are in sync
zoomCanvasSlider->setValue(100);
}
void KisPopupPalette::tabletEvent(QTabletEvent *event) {
event->ignore();
}
void KisPopupPalette::showEvent(QShowEvent *event)
{
m_clicksEater->reset();
QWidget::showEvent(event);
}
void KisPopupPalette::mouseReleaseEvent(QMouseEvent *event)
{
QPointF point = event->localPos();
event->accept();
if (event->buttons() == Qt::NoButton &&
event->button() == Qt::RightButton) {
showPopupPalette(false);
return;
}
m_isOverCanvasRotationIndicator = false;
m_isRotatingCanvasIndicator = false;
if (event->button() == Qt::LeftButton) {
QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
//in favorite brushes area
if (hoveredPreset() > -1) {
//setSelectedBrush(hoveredBrush());
emit sigChangeActivePaintop(hoveredPreset());
}
if (pathColor.contains(point)) {
int pos = calculateIndex(point, m_resourceManager->recentColorsTotal());
if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) {
emit sigUpdateRecentColor(pos);
}
}
}
}
int KisPopupPalette::calculateIndex(QPointF point, int n)
{
calculatePresetIndex(point, n);
//translate to (0,0)
point.setX(point.x() - m_popupPaletteSize / 2);
point.setY(point.y() - m_popupPaletteSize / 2);
//rotate
float smallerAngle = M_PI / 2 + M_PI / n - atan2(point.y(), point.x());
float radius = sqrt((float)point.x() * point.x() + point.y() * point.y());
point.setX(radius * cos(smallerAngle));
point.setY(radius * sin(smallerAngle));
//calculate brush index
int pos = floor(acos(point.x() / radius) * n / (2 * M_PI));
if (point.y() < 0) pos = n - pos - 1;
return pos;
}
bool KisPopupPalette::isPointInPixmap(QPointF &point, int pos)
{
if (createPathFromPresetIndex(pos).contains(point + QPointF(-m_popupPaletteSize / 2, -m_popupPaletteSize / 2))) {
return true;
}
return false;
}
KisPopupPalette::~KisPopupPalette()
{
}
QPainterPath KisPopupPalette::createPathFromPresetIndex(int index)
{
qreal angleSlice = 360.0 / numSlots() ; // how many degrees each slice will get
// the starting angle of the slice we need to draw. the negative sign makes us go clockwise.
// adding 90 degrees makes us start at the top. otherwise we would start at the right
qreal startingAngle = -(index * angleSlice) + 90;
// the radius will get smaller as the amount of presets shown increases. 10 slots == 41
qreal radians = qDegreesToRadians((360.0/10)/2);
qreal maxRadius = (m_colorHistoryOuterRadius * qSin(radians) / (1-qSin(radians)))-2;
radians = qDegreesToRadians(angleSlice/2);
qreal presetRadius = m_colorHistoryOuterRadius * qSin(radians) / (1-qSin(radians));
//If we assume that circles will mesh like a hexagonal grid, then 3.5r is the size of two hexagons interlocking.
qreal length = m_colorHistoryOuterRadius + presetRadius;
// can we can fit in a second row? We don't want the preset icons to get too tiny.
if (maxRadius > presetRadius) {
//redo all calculations assuming a second row.
if (numSlots() % 2) {
angleSlice = 360.0/(numSlots()+1);
startingAngle = -(index * angleSlice) + 90;
}
if (numSlots() != m_cachedNumSlots){
qreal tempRadius = presetRadius;
qreal distance = 0;
do{
tempRadius+=0.1;
// Calculate the XY of two adjectant circles using this tempRadius.
qreal length1 = m_colorHistoryOuterRadius + tempRadius;
qreal length2 = m_colorHistoryOuterRadius + ((maxRadius*2)-tempRadius);
qreal pathX1 = length1 * qCos(qDegreesToRadians(startingAngle)) - tempRadius;
qreal pathY1 = -(length1) * qSin(qDegreesToRadians(startingAngle)) - tempRadius;
qreal startingAngle2 = -(index+1 * angleSlice) + 90;
qreal pathX2 = length2 * qCos(qDegreesToRadians(startingAngle2)) - tempRadius;
qreal pathY2 = -(length2) * qSin(qDegreesToRadians(startingAngle2)) - tempRadius;
// Use Pythagorean Theorem to calculate the distance between these two values.
qreal m1 = pathX2-pathX1;
qreal m2 = pathY2-pathY1;
distance = sqrt((m1*m1)+(m2*m2));
}
//As long at there's more distance than the radius of the two presets, continue increasing the radius.
while((tempRadius+1)*2 < distance);
m_cachedRadius = tempRadius;
}
m_cachedNumSlots = numSlots();
presetRadius = m_cachedRadius;
length = m_colorHistoryOuterRadius + presetRadius;
if (index % 2) {
length = m_colorHistoryOuterRadius + ((maxRadius*2)-presetRadius);
}
}
QPainterPath path;
qreal pathX = length * qCos(qDegreesToRadians(startingAngle)) - presetRadius;
qreal pathY = -(length) * qSin(qDegreesToRadians(startingAngle)) - presetRadius;
qreal pathDiameter = 2 * presetRadius; // distance is used to calculate the X/Y in addition to the preset circle size
path.addEllipse(pathX, pathY, pathDiameter, pathDiameter);
return path;
}
int KisPopupPalette::calculatePresetIndex(QPointF point, int /*n*/)
{
for(int i = 0; i < numSlots(); i++)
{
QPointF adujustedPoint = point - QPointF(m_popupPaletteSize/2, m_popupPaletteSize/2);
if(createPathFromPresetIndex(i).contains(adujustedPoint))
{
return i;
}
}
return -1;
}
int KisPopupPalette::numSlots()
{
KisConfig config(true);
return qMax(config.favoritePresets(), 10);
}
diff --git a/libs/ui/kis_psd_layer_style_resource.cpp b/libs/ui/kis_psd_layer_style_resource.cpp
deleted file mode 100644
index 24a5222d59..0000000000
--- a/libs/ui/kis_psd_layer_style_resource.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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_psd_layer_style_resource.h"
-
-#include <QBuffer>
-#include <QString>
-#include <QByteArray>
-#include <QFile>
-#include <QFileInfo>
-#include <QCryptographicHash>
-
-#include <kis_debug.h>
-#include <klocalizedstring.h>
-
-#include "kis_psd_layer_style.h"
-#include "kis_asl_layer_style_serializer.h"
-
-#include "kis_layer.h"
-
-
-KisPSDLayerStyleCollectionResource::KisPSDLayerStyleCollectionResource(const QString &filename)
- : KoResource(filename)
-{
- if (!filename.isEmpty()) {
- setName(QFileInfo(filename).fileName());
- }
-}
-
-KisPSDLayerStyleCollectionResource::~KisPSDLayerStyleCollectionResource()
-{
- m_layerStyles.clear();
-}
-
-bool KisPSDLayerStyleCollectionResource::load()
-{
- QFile file(filename());
- if (file.size() == 0) return false;
-
- bool result;
- if (!file.open(QIODevice::ReadOnly)) {
- dbgKrita << "Can't open file " << filename();
- return false;
- }
- result = loadFromDevice(&file);
- file.close();
-
- setName(QFileInfo(filename()).fileName());
-
- return result;
-}
-
-bool KisPSDLayerStyleCollectionResource::loadFromDevice(QIODevice *dev)
-{
- KisAslLayerStyleSerializer serializer;
- serializer.readFromDevice(dev);
- m_layerStyles = serializer.styles();
- setValid(true);
-
- return true;
-}
-
-bool KisPSDLayerStyleCollectionResource::save()
-{
- QFile file(filename());
- file.open(QIODevice::WriteOnly | QIODevice::Truncate);
- bool res = saveToDevice(&file);
- file.close();
- return res;
-}
-
-bool KisPSDLayerStyleCollectionResource::saveToDevice(QIODevice *dev) const
-{
- if (m_layerStyles.isEmpty()) return true;
-
- KisAslLayerStyleSerializer serializer;
- serializer.setStyles(m_layerStyles);
- serializer.saveToDevice(dev);
-
- return true;
-}
-
-QString KisPSDLayerStyleCollectionResource::defaultFileExtension() const
-{
- return QString(".asl");
-}
-
-KisPSDLayerStyleCollectionResource::StylesVector KisPSDLayerStyleCollectionResource::layerStyles() const
-{
- return m_layerStyles;
-}
-
-void KisPSDLayerStyleCollectionResource::setLayerStyles(StylesVector styles)
-{
- m_layerStyles = styles;
- setValid(!m_layerStyles.isEmpty());
-}
-
-QByteArray KisPSDLayerStyleCollectionResource::generateMD5() const
-{
- if (m_layerStyles.size() > 0) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- saveToDevice(&buf);
- QCryptographicHash md5(QCryptographicHash::Md5);
- md5.addData(buf.buffer());
- return md5.result();
- }
- return QByteArray();
-}
-
-void KisPSDLayerStyleCollectionResource::collectAllLayerStyles(KisNodeSP root)
-{
- KisLayer* layer = qobject_cast<KisLayer*>(root.data());
-
- if (layer && layer->layerStyle()) {
- KisPSDLayerStyleSP clone = layer->layerStyle()->clone();
- clone->setName(i18nc("Auto-generated layer style name for embedded styles (style itself)", "<%1> (embedded)", layer->name()));
- m_layerStyles << clone;
- setValid(true);
- }
-
- KisNodeSP child = root->firstChild();
- while (child) {
- collectAllLayerStyles(child);
- child = child->nextSibling();
- }
-}
-
-void KisPSDLayerStyleCollectionResource::assignAllLayerStyles(KisNodeSP root)
-{
- KisLayer* layer = qobject_cast<KisLayer*>(root.data());
-
- if (layer && layer->layerStyle()) {
- QUuid uuid = layer->layerStyle()->uuid();
-
- bool found = false;
-
- Q_FOREACH (KisPSDLayerStyleSP style, m_layerStyles) {
- if (style->uuid() == uuid) {
- layer->setLayerStyle(style->clone());
- found = true;
- break;
- }
- }
-
- if (!found) {
- warnKrita << "WARNING: loading layer style for" << layer->name() << "failed! It requests inexistent style:" << uuid;
- }
- }
-
- KisNodeSP child = root->firstChild();
- while (child) {
- assignAllLayerStyles(child);
- child = child->nextSibling();
- }
-}
diff --git a/libs/ui/kis_psd_layer_style_resource.h b/libs/ui/kis_psd_layer_style_resource.h
deleted file mode 100644
index a6eb08356f..0000000000
--- a/libs/ui/kis_psd_layer_style_resource.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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_PSD_LAYER_STYLE_RESOURCE_H
-#define KIS_PSD_LAYER_STYLE_RESOURCE_H
-
-#include <resources/KoResource.h>
-#include <QVector>
-
-#include <kritaui_export.h>
-#include "kis_psd_layer_style.h"
-
-/**
- * @brief The KisPSDLayerStyleResource class represents an ASL file type resource.
- */
-class KRITAUI_EXPORT KisPSDLayerStyleCollectionResource : public KoResource
-{
-public:
- typedef QVector<KisPSDLayerStyleSP> StylesVector;
-
-public:
- explicit KisPSDLayerStyleCollectionResource(const QString &filename);
- ~KisPSDLayerStyleCollectionResource() override;
-
- bool load() override;
- bool loadFromDevice(QIODevice *dev) override;
-
- bool save() override;
- bool saveToDevice(QIODevice *dev) const override;
-
- QString defaultFileExtension() const override;
-
- StylesVector layerStyles() const;
- void setLayerStyles(StylesVector styles);
-
- void collectAllLayerStyles(KisNodeSP root);
- void assignAllLayerStyles(KisNodeSP root);
-
-protected:
-
- QByteArray generateMD5() const override;
-
-private:
- StylesVector m_layerStyles;
-
-};
-
-
-#endif // KIS_PSD_LAYER_STYLE_RESOURCE_H
diff --git a/libs/ui/kis_stopgradient_editor.cpp b/libs/ui/kis_stopgradient_editor.cpp
index 51867bd096..33a2af5429 100644
--- a/libs/ui/kis_stopgradient_editor.cpp
+++ b/libs/ui/kis_stopgradient_editor.cpp
@@ -1,257 +1,257 @@
/*
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* 2016 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_stopgradient_editor.h"
#include <QPainter>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QPoint>
#include <QMenu>
#include <KoColorSpace.h>
#include <resources/KoStopGradient.h>
#include "kis_debug.h"
#include <kis_icon_utils.h>
/****************************** KisStopGradientEditor ******************************/
KisStopGradientEditor::KisStopGradientEditor(QWidget *parent)
: QWidget(parent),
m_gradient(0)
{
setupUi(this);
connect(gradientSlider, SIGNAL(sigSelectedStop(int)), this, SLOT(stopChanged(int)));
connect(nameedit, SIGNAL(editingFinished()), this, SLOT(nameChanged()));
connect(colorButton, SIGNAL(changed(KoColor)), SLOT(colorChanged(KoColor)));
opacitySlider->setPrefix(i18n("Opacity: "));
opacitySlider->setRange(0.0, 1.0, 2);
connect(opacitySlider, SIGNAL(valueChanged(qreal)), this, SLOT(opacityChanged(qreal)));
buttonReverse->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_x"));
buttonReverse->setToolTip(i18n("Flip Gradient"));
KisIconUtils::updateIcon(buttonReverse);
connect(buttonReverse, SIGNAL(pressed()), SLOT(reverse()));
buttonReverseSecond->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_x"));
buttonReverseSecond->setToolTip(i18n("Flip Gradient"));
KisIconUtils::updateIcon(buttonReverseSecond);
connect(buttonReverseSecond, SIGNAL(clicked()), SLOT(reverse()));
this->setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(showContextMenu(const QPoint &)));
setCompactMode(false);
setGradient(0);
stopChanged(-1);
}
-KisStopGradientEditor::KisStopGradientEditor(KoStopGradient* gradient, QWidget *parent, const char* name, const QString& caption)
+KisStopGradientEditor::KisStopGradientEditor(KoStopGradientSP gradient, QWidget *parent, const char* name, const QString& caption)
: KisStopGradientEditor(parent)
{
setObjectName(name);
setWindowTitle(caption);
setGradient(gradient);
}
void KisStopGradientEditor::setCompactMode(bool value)
{
lblName->setVisible(!value);
buttonReverse->setVisible(!value);
nameedit->setVisible(!value);
buttonReverseSecond->setVisible(value);
}
-void KisStopGradientEditor::setGradient(KoStopGradient *gradient)
+void KisStopGradientEditor::setGradient(KoStopGradientSP gradient)
{
m_gradient = gradient;
setEnabled(m_gradient);
if (m_gradient) {
gradientSlider->setGradientResource(m_gradient);
nameedit->setText(gradient->name());
stopChanged(gradientSlider->selectedStop());
}
emit sigGradientChanged();
}
void KisStopGradientEditor::notifyGlobalColorChanged(const KoColor &color)
{
if (colorButton->isEnabled() &&
color != colorButton->color()) {
colorButton->setColor(color);
}
}
boost::optional<KoColor> KisStopGradientEditor::currentActiveStopColor() const
{
if (!colorButton->isEnabled()) return boost::none;
return colorButton->color();
}
void KisStopGradientEditor::stopChanged(int stop)
{
if (!m_gradient) return;
const bool hasStopSelected = stop >= 0;
opacitySlider->setEnabled(hasStopSelected);
colorButton->setEnabled(hasStopSelected);
stopLabel->setEnabled(hasStopSelected);
if (hasStopSelected) {
KoColor color = m_gradient->stops()[stop].second;
opacitySlider->setValue(color.opacityF());
color.setOpacity(1.0);
colorButton->setColor(color);
}
emit sigGradientChanged();
}
void KisStopGradientEditor::colorChanged(const KoColor& color)
{
if (!m_gradient) return;
QList<KoGradientStop> stops = m_gradient->stops();
int currentStop = gradientSlider->selectedStop();
double t = stops[currentStop].first;
KoColor c(color, stops[currentStop].second.colorSpace());
c.setOpacity(stops[currentStop].second.opacityU8());
stops.removeAt(currentStop);
stops.insert(currentStop, KoGradientStop(t, c));
m_gradient->setStops(stops);
gradientSlider->update();
emit sigGradientChanged();
}
void KisStopGradientEditor::opacityChanged(qreal value)
{
if (!m_gradient) return;
QList<KoGradientStop> stops = m_gradient->stops();
int currentStop = gradientSlider->selectedStop();
double t = stops[currentStop].first;
KoColor c = stops[currentStop].second;
c.setOpacity(value);
stops.removeAt(currentStop);
stops.insert(currentStop, KoGradientStop(t, c));
m_gradient->setStops(stops);
gradientSlider->update();
emit sigGradientChanged();
}
void KisStopGradientEditor::nameChanged()
{
if (!m_gradient) return;
m_gradient->setName(nameedit->text());
emit sigGradientChanged();
}
void KisStopGradientEditor::reverse()
{
if (!m_gradient) return;
QList<KoGradientStop> stops = m_gradient->stops();
QList<KoGradientStop> reversedStops;
for(const KoGradientStop& stop : stops) {
reversedStops.push_front(KoGradientStop(1 - stop.first, stop.second));
}
m_gradient->setStops(reversedStops);
gradientSlider->setSelectedStop(stops.size() - 1 - gradientSlider->selectedStop());
emit sigGradientChanged();
}
void KisStopGradientEditor::sortByValue( SortFlags flags = SORT_ASCENDING )
{
if (!m_gradient) return;
bool ascending = (flags & SORT_ASCENDING) > 0;
bool evenDistribution = (flags & EVEN_DISTRIBUTION) > 0;
QList<KoGradientStop> stops = m_gradient->stops();
const int stopCount = stops.size();
QList<KoGradientStop> sortedStops;
std::sort(stops.begin(), stops.end(), KoGradientStopValueSort());
int stopIndex = 0;
for (const KoGradientStop& stop : stops) {
const float value = evenDistribution ? (float)stopIndex / (float)(stopCount - 1) : stop.second.toQColor().valueF();
const float position = ascending ? value : 1.f - value;
if (ascending) {
sortedStops.push_back(KoGradientStop(position, stop.second));
} else {
sortedStops.push_front(KoGradientStop(position, stop.second));
}
stopIndex++;
}
m_gradient->setStops(sortedStops);
gradientSlider->setSelectedStop(stopCount - 1);
gradientSlider->update();
emit sigGradientChanged();
}
void KisStopGradientEditor::showContextMenu(const QPoint &origin)
{
QMenu contextMenu(i18n("Options"), this);
QAction reverseValues(i18n("Reverse Values"), this);
connect(&reverseValues, &QAction::triggered, this, &KisStopGradientEditor::reverse);
QAction sortAscendingValues(i18n("Sort by Value"), this);
connect(&sortAscendingValues, &QAction::triggered, this, [this]{ this->sortByValue(SORT_ASCENDING); } );
QAction sortAscendingDistributed(i18n("Sort by Value (Even Distribution)"), this);
connect(&sortAscendingDistributed, &QAction::triggered, this, [this]{ this->sortByValue(SORT_ASCENDING | EVEN_DISTRIBUTION);} );
contextMenu.addAction(&reverseValues);
contextMenu.addSeparator();
contextMenu.addAction(&sortAscendingValues);
contextMenu.addAction(&sortAscendingDistributed);
contextMenu.exec(mapToGlobal(origin));
}
diff --git a/libs/ui/kis_stopgradient_editor.h b/libs/ui/kis_stopgradient_editor.h
index 7560e5feda..d5e575c3bd 100644
--- a/libs/ui/kis_stopgradient_editor.h
+++ b/libs/ui/kis_stopgradient_editor.h
@@ -1,68 +1,67 @@
/*
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* 2016 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_STOPGRADIENT_EDITOR_H_
#define _KIS_STOPGRADIENT_EDITOR_H_
#include "kritaui_export.h"
#include "ui_wdgstopgradienteditor.h"
#include <boost/optional.hpp>
-
-class KoStopGradient;
+#include <KoStopGradient.h>
class KRITAUI_EXPORT KisStopGradientEditor : public QWidget, public Ui::KisWdgStopGradientEditor
{
Q_OBJECT
public:
enum SortFlag {
SORT_ASCENDING = 1 << 0,
EVEN_DISTRIBUTION = 1 << 1
};
Q_DECLARE_FLAGS( SortFlags, SortFlag);
KisStopGradientEditor(QWidget *parent);
- KisStopGradientEditor(KoStopGradient* gradient, QWidget *parent, const char* name, const QString& caption);
+ KisStopGradientEditor(KoStopGradientSP gradient, QWidget *parent, const char* name, const QString& caption);
void setCompactMode(bool value);
- void setGradient(KoStopGradient* gradient);
+ void setGradient(KoStopGradientSP gradient);
void notifyGlobalColorChanged(const KoColor &color);
boost::optional<KoColor> currentActiveStopColor() const;
Q_SIGNALS:
void sigGradientChanged();
private:
- KoStopGradient* m_gradient;
+ KoStopGradientSP m_gradient;
private Q_SLOTS:
void stopChanged(int stop);
void colorChanged(const KoColor& color);
void opacityChanged(qreal value);
void nameChanged();
void reverse();
void sortByValue(SortFlags flags);
void showContextMenu( const class QPoint& origin );
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KisStopGradientEditor::SortFlags);
#endif
diff --git a/libs/ui/kis_workspace_resource.cpp b/libs/ui/kis_workspace_resource.cpp
index 553d861f49..956f0810d1 100644
--- a/libs/ui/kis_workspace_resource.cpp
+++ b/libs/ui/kis_workspace_resource.cpp
@@ -1,130 +1,136 @@
/* 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_resource.h"
#include <QFile>
#include <QDomDocument>
#include <QTextStream>
+#include <QBuffer>
#define WORKSPACE_VERSION 1
KisWorkspaceResource::KisWorkspaceResource(const QString& filename): KoResource(filename)
{
}
KisWorkspaceResource::~KisWorkspaceResource()
{
}
-bool KisWorkspaceResource::save()
+KisWorkspaceResource::KisWorkspaceResource(const KisWorkspaceResource &rhs)
+ : KoResource(rhs)
+ , KisPropertiesConfiguration(rhs)
+ , m_dockerState(rhs.m_dockerState)
{
- if (filename().isEmpty())
- return false;
-
- QFile file(filename());
- file.open(QIODevice::WriteOnly);
- bool res = saveToDevice(&file);
- file.close();
- return res;
+}
+
+KoResourceSP KisWorkspaceResource::clone() const
+{
+ return KoResourceSP(new KisWorkspaceResource(*this));
}
bool KisWorkspaceResource::saveToDevice(QIODevice *dev) const
{
QDomDocument doc;
QDomElement root = doc.createElement("Workspace");
root.setAttribute("name", name() );
root.setAttribute("version", WORKSPACE_VERSION);
QDomElement state = doc.createElement("state");
state.appendChild(doc.createCDATASection(m_dockerState.toBase64()));
root.appendChild(state);
// Save KisPropertiesConfiguration settings
QDomElement settings = doc.createElement("settings");
KisPropertiesConfiguration::toXML(doc, settings);
root.appendChild(settings);
+
+ if (!image().isNull()) {
+ QDomElement thumb = doc.createElement("image");
+ QByteArray arr;
+ QBuffer buffer(&arr);
+ buffer.open(QIODevice::WriteOnly);
+ image().save(&buffer, "PNG");
+ buffer.close();
+ thumb.appendChild(doc.createCDATASection(arr.toBase64()));
+ root.appendChild(thumb);
+ }
+
+
doc.appendChild(root);
QTextStream textStream(dev);
textStream.setCodec("UTF-8");
doc.save(textStream, 4);
KoResource::saveToDevice(dev);
return true;
}
-bool KisWorkspaceResource::load()
+bool KisWorkspaceResource::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
{
- if (filename().isEmpty())
- return false;
-
- QFile file(filename());
- if (file.size() == 0) return false;
- if (!file.open(QIODevice::ReadOnly)) {
- warnKrita << "Can't open file " << filename();
- return false;
- }
-
- bool res = loadFromDevice(&file);
- file.close();
- return res;
-}
+ Q_UNUSED(resourcesInterface);
-bool KisWorkspaceResource::loadFromDevice(QIODevice *dev)
-{
QDomDocument doc;
if (!doc.setContent(dev)) {
return false;
}
QDomElement element = doc.documentElement();
setName(element.attribute("name"));
QDomElement state = element.firstChildElement("state");
if (!state.isNull()) {
m_dockerState = QByteArray::fromBase64(state.text().toLatin1());
}
QDomElement settings = element.firstChildElement("settings");
if (!settings.isNull()) {
KisPropertiesConfiguration::fromXML(settings);
}
+ QDomElement thumb = element.firstChildElement("image");
+ if (!thumb.isNull()) {
+ QImage img;
+ img.loadFromData(QByteArray::fromBase64(thumb.text().toLatin1()));
+ this->setImage(img);
+ }
+
setValid(true);
return true;
}
QString KisWorkspaceResource::defaultFileExtension() const
{
return QString(".kws");
}
void KisWorkspaceResource::setDockerState(const QByteArray& state)
{
m_dockerState = state;
}
QByteArray KisWorkspaceResource::dockerState()
{
return m_dockerState;
}
diff --git a/libs/ui/kis_workspace_resource.h b/libs/ui/kis_workspace_resource.h
index b0436f8267..10e6a0ed7c 100644
--- a/libs/ui/kis_workspace_resource.h
+++ b/libs/ui/kis_workspace_resource.h
@@ -1,47 +1,55 @@
/* 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.
*/
#ifndef KIS_WORKSPACE_RESOURCE_H
#define KIS_WORKSPACE_RESOURCE_H
-#include <resources/KoResource.h>
+#include <KoResource.h>
#include <kis_properties_configuration.h>
#include "kritaui_export.h"
/// Resource for storing of workspaces
class KRITAUI_EXPORT KisWorkspaceResource : public KoResource , public KisPropertiesConfiguration
{
public:
KisWorkspaceResource(const QString& filename);
~KisWorkspaceResource() override;
- bool load() override;
- bool loadFromDevice(QIODevice *dev) override;
- bool save() override;
+ KisWorkspaceResource(const KisWorkspaceResource &rhs);
+ KisWorkspaceResource &operator=(const KisWorkspaceResource &rhs) = delete;
+ KoResourceSP clone() const override;
+
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
bool saveToDevice(QIODevice* dev) const override;
QString defaultFileExtension() const override;
+ QPair<QString, QString> resourceType() const override
+ {
+ return QPair<QString, QString>(ResourceType::Workspaces, "");
+ }
void setDockerState(const QByteArray& state);
QByteArray dockerState();
private:
QByteArray m_dockerState;
};
+typedef QSharedPointer<KisWorkspaceResource> KisWorkspaceResourceSP;
+
#endif // KIS_WORKSPACE_RESOURCE_H
diff --git a/libs/ui/layerstyles/wdgstylesselector.ui b/libs/ui/layerstyles/wdgstylesselector.ui
index 328d0d0964..bc5b700588 100644
--- a/libs/ui/layerstyles/wdgstylesselector.ui
+++ b/libs/ui/layerstyles/wdgstylesselector.ui
@@ -1,33 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgStylesSelector</class>
<widget class="QWidget" name="WdgStylesSelector">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="grpStyleSelector">
<property name="title">
<string>Styles</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
- <widget class="QListWidget" name="listStyles"/>
+ <widget class="QListView" name="listStyles"/>
</item>
<item row="0" column="0">
<widget class="QComboBox" name="cmbStyleCollections"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
diff --git a/libs/ui/tests/CMakeLists.txt b/libs/ui/tests/CMakeLists.txt
index e3da0f545c..215bfaeea9 100644
--- a/libs/ui/tests/CMakeLists.txt
+++ b/libs/ui/tests/CMakeLists.txt
@@ -1,170 +1,169 @@
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
include_directories(${CMAKE_SOURCE_DIR}/libs/image/metadata
${CMAKE_SOURCE_DIR}/sdk/tests )
include(ECMAddTests)
macro_add_unittest_definitions()
ecm_add_tests(
kis_image_view_converter_test.cpp
kis_shape_selection_test.cpp
kis_doc2_test.cpp
kis_coordinates_converter_test.cpp
kis_grid_config_test.cpp
kis_stabilized_events_sampler_test.cpp
kis_brush_hud_properties_config_test.cpp
kis_shape_commands_test.cpp
kis_stop_gradient_editor_test.cpp
kis_file_layer_test.cpp
kis_multinode_property_test.cpp
KisFrameSerializerTest.cpp
KisFrameCacheStoreTest.cpp
kis_animation_exporter_test.cpp
kis_prescaled_projection_test.cpp
- kis_asl_layer_style_serializer_test.cpp
kis_animation_importer_test.cpp
KisSpinBoxSplineUnitConverterTest.cpp
KisDocumentReplaceTest.cpp
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-"
)
ecm_add_test( kis_selection_decoration_test.cpp ../../../sdk/tests/stroke_testing_utils.cpp
TEST_NAME KisSelectionDecorationTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
ecm_add_test( kis_node_dummies_graph_test.cpp ../../../sdk/tests/testutil.cpp
TEST_NAME KisNodeDummiesGraphTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
ecm_add_test( kis_node_shapes_graph_test.cpp ../../../sdk/tests/testutil.cpp
TEST_NAME KisNodeShapesGraphTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
ecm_add_test( kis_model_index_converter_test.cpp ../../../sdk/tests/testutil.cpp
TEST_NAME KisModelIndexConverterTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
ecm_add_test( kis_categorized_list_model_test.cpp modeltest.cpp
TEST_NAME KisCategorizedListModelTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
ecm_add_test( kis_node_juggler_compressed_test.cpp ../../../sdk/tests/testutil.cpp
TEST_NAME KisNodeJugglerCompressedTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
ecm_add_test(
kis_input_manager_test.cpp ../../../sdk/tests/testutil.cpp
TEST_NAME KisInputManagerTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
ecm_add_test(
kis_node_model_test.cpp modeltest.cpp
TEST_NAME kis_node_model_test
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
##### Tests that currently fail and should be fixed #####
include(KritaAddBrokenUnitTest)
krita_add_broken_unit_test( kis_shape_controller_test.cpp kis_dummies_facade_base_test.cpp
TEST_NAME kis_shape_controller_test
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( kis_exiv2_test.cpp
TEST_NAME KisExiv2Test
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( kis_clipboard_test.cpp
TEST_NAME KisClipboardTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( freehand_stroke_test.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp
TEST_NAME FreehandStrokeTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( FreehandStrokeBenchmark.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp
TEST_NAME FreehandStrokeBenchmark
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( KisPaintOnTransparencyMaskTest.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp
TEST_NAME KisPaintOnTransparencyMaskTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( fill_processing_visitor_test.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp
TEST_NAME
FillProcessingVisitorTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( filter_stroke_test.cpp ../../../sdk/tests/stroke_testing_utils.cpp
TEST_NAME FilterStrokeTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( kis_selection_manager_test.cpp
TEST_NAME KisSelectionManagerTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
#set_tests_properties(libs-ui-KisSelectionManagerTest PROPERTIES TIMEOUT 300)
krita_add_broken_unit_test( kis_node_manager_test.cpp
TEST_NAME KisNodeManagerTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( kis_dummies_facade_test.cpp kis_dummies_facade_base_test.cpp ../../../sdk/tests/testutil.cpp
TEST_NAME KisDummiesFacadeTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( kis_zoom_and_pan_test.cpp ../../../sdk/tests/testutil.cpp
TEST_NAME KisZoomAndPanTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
#set_tests_properties(libs-ui-KisZoomAndPanTest PROPERTIES TIMEOUT 300)
krita_add_broken_unit_test( kis_action_manager_test.cpp
TEST_NAME KisActionManagerTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( kis_categories_mapper_test.cpp testing_categories_mapper.cpp
TEST_NAME KisCategoriesMapperTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( kis_animation_frame_cache_test.cpp
TEST_NAME kis_animation_frame_cache_test
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( kis_derived_resources_test.cpp
TEST_NAME kis_derived_resources_test
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
krita_add_broken_unit_test( kis_shape_layer_test.cpp
TEST_NAME KisShapeLayerTest
LINK_LIBRARIES kritaui Qt5::Test
NAME_PREFIX "libs-ui-")
diff --git a/libs/ui/tests/FreehandStrokeBenchmark.cpp b/libs/ui/tests/FreehandStrokeBenchmark.cpp
index ade91fc02b..20fb4bc59f 100644
--- a/libs/ui/tests/FreehandStrokeBenchmark.cpp
+++ b/libs/ui/tests/FreehandStrokeBenchmark.cpp
@@ -1,157 +1,157 @@
/*
* 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 "FreehandStrokeBenchmark.h"
#include <QTest>
#include <KoCompositeOpRegistry.h>
#include <KoColor.h>
#include "stroke_testing_utils.h"
#include "strokes/freehand_stroke.h"
#include "strokes/KisFreehandStrokeInfo.h"
#include "KisAsyncronousStrokeUpdateHelper.h"
#include "kis_resources_snapshot.h"
#include "kis_image.h"
#include <brushengine/kis_paint_information.h>
class FreehandStrokeBenchmarkTester : public utils::StrokeTester
{
public:
FreehandStrokeBenchmarkTester(const QString &presetFilename)
: StrokeTester("freehand_benchmark", QSize(5000, 5000), presetFilename)
{
setBaseFuzziness(3);
}
void setCpuCoresLimit(int value) {
m_cpuCoresLimit = value;
}
protected:
using utils::StrokeTester::initImage;
void initImage(KisImageWSP image, KisNodeSP activeNode) override {
Q_UNUSED(activeNode);
if (m_cpuCoresLimit > 0) {
image->setWorkingThreadsLimit(m_cpuCoresLimit);
}
}
KisStrokeStrategy* createStroke(KisResourcesSnapshotSP resources,
KisImageWSP image) override {
Q_UNUSED(image);
KisFreehandStrokeInfo *strokeInfo = new KisFreehandStrokeInfo();
QScopedPointer<FreehandStrokeStrategy> stroke(
new FreehandStrokeStrategy(resources, strokeInfo, kundo2_noi18n("Freehand Stroke")));
return stroke.take();
}
void addPaintingJobs(KisImageWSP image,
KisResourcesSnapshotSP resources) override
{
addPaintingJobs(image, resources, 0);
}
void addPaintingJobs(KisImageWSP image, KisResourcesSnapshotSP resources, int iteration) override {
Q_UNUSED(iteration);
Q_UNUSED(resources);
for (int y = 100; y < 4900; y += 300) {
KisPaintInformation pi1;
KisPaintInformation pi2;
pi1 = KisPaintInformation(QPointF(100, y), 0.5);
pi2 = KisPaintInformation(QPointF(4900, y + 100), 1.0);
QScopedPointer<KisStrokeJobData> data(
new FreehandStrokeStrategy::Data(0, pi1, pi2));
image->addJob(strokeId(), data.take());
}
image->addJob(strokeId(), new KisAsyncronousStrokeUpdateHelper::UpdateData(true));
}
private:
int m_cpuCoresLimit = -1;
};
void benchmarkBrush(const QString &presetName)
{
FreehandStrokeBenchmarkTester tester(presetName);
for (int i = 1; i <= QThread::idealThreadCount(); i++) {
tester.setCpuCoresLimit(i);
tester.benchmark();
qDebug() << qPrintable(QString("Cores: %1 Time: %2 (ms)").arg(i).arg(tester.lastStrokeTime()));
}
}
#include <KoResourcePaths.h>
void FreehandStrokeBenchmark::initTestCase()
{
- KoResourcePaths::addResourceType("kis_brushes", "data", FILES_DATA_DIR);
+ KoResourcePaths::addResourceType(ResourceType::Brushes, "data", FILES_DATA_DIR);
}
void FreehandStrokeBenchmark::testDefaultTip()
{
benchmarkBrush("testing_1000px_auto_deafult.kpp");
}
void FreehandStrokeBenchmark::testSoftTip()
{
benchmarkBrush("testing_1000px_auto_soft.kpp");
}
void FreehandStrokeBenchmark::testGaussianTip()
{
benchmarkBrush("testing_1000px_auto_gaussian.kpp");
}
void FreehandStrokeBenchmark::testRectangularTip()
{
benchmarkBrush("testing_1000px_auto_rectangular.kpp");
}
void FreehandStrokeBenchmark::testRectGaussianTip()
{
benchmarkBrush("testing_1000px_auto_gaussian_rect.kpp");
}
void FreehandStrokeBenchmark::testRectSoftTip()
{
benchmarkBrush("testing_1000px_auto_soft_rect.kpp");
}
void FreehandStrokeBenchmark::testStampTip()
{
benchmarkBrush("testing_1000px_stamp_450_rotated.kpp");
}
void FreehandStrokeBenchmark::testColorsmudgeDefaultTip()
{
benchmarkBrush("testing_200px_colorsmudge_default.kpp");
}
QTEST_MAIN(FreehandStrokeBenchmark)
diff --git a/libs/ui/tests/KisPaintOnTransparencyMaskTest.cpp b/libs/ui/tests/KisPaintOnTransparencyMaskTest.cpp
index 953e93caff..686ef54152 100644
--- a/libs/ui/tests/KisPaintOnTransparencyMaskTest.cpp
+++ b/libs/ui/tests/KisPaintOnTransparencyMaskTest.cpp
@@ -1,135 +1,135 @@
#include "KisPaintOnTransparencyMaskTest.h"
#include <QTest>
#include <KoCompositeOpRegistry.h>
#include <KoColor.h>
#include "stroke_testing_utils.h"
#include "strokes/freehand_stroke.h"
#include "strokes/KisFreehandStrokeInfo.h"
#include "KisAsyncronousStrokeUpdateHelper.h"
#include "kis_resources_snapshot.h"
#include "kis_image.h"
#include <brushengine/kis_paint_information.h>
#include "kis_transparency_mask.h"
#include "kis_paint_device_debug_utils.h"
#include "kis_tool_utils.h"
#include "kis_sequential_iterator.h"
class PaintOnTransparencyMaskTester : public utils::StrokeTester
{
public:
PaintOnTransparencyMaskTester(const QString &presetFilename)
: StrokeTester("freehand_benchmark", QSize(5000, 5000), presetFilename)
{
setBaseFuzziness(3);
}
protected:
using utils::StrokeTester::initImage;
void initImage(KisImageWSP image, KisNodeSP activeNode) override {
activeNode->paintDevice()->fill(QRect(0,0,1024,1024), KoColor(Qt::red, image->colorSpace()));
m_mask = new KisTransparencyMask();
m_mask->setSelection(new KisSelection());
m_mask->paintDevice()->clear();
image->addNode(m_mask, activeNode);
image->setWorkingThreadsLimit(8);
}
using utils::StrokeTester::modifyResourceManager;
void modifyResourceManager(KoCanvasResourceProvider *manager,
KisImageWSP image) override {
KoColor color(Qt::red, image->colorSpace());
color.setOpacity(0.5);
QVariant i;
i.setValue(color);
manager->setResource(KoCanvasResourceProvider::ForegroundColor, i);
}
KisStrokeStrategy* createStroke(KisResourcesSnapshotSP resources,
KisImageWSP image) override {
Q_UNUSED(image);
resources->setCurrentNode(m_mask);
KisFreehandStrokeInfo *strokeInfo = new KisFreehandStrokeInfo();
QScopedPointer<FreehandStrokeStrategy> stroke(
new FreehandStrokeStrategy(resources, strokeInfo, kundo2_noi18n("Freehand Stroke")));
return stroke.take();
}
void addPaintingJobs(KisImageWSP image,
KisResourcesSnapshotSP resources) override
{
addPaintingJobs(image, resources, 0);
}
void addPaintingJobs(KisImageWSP image, KisResourcesSnapshotSP resources, int iteration) override {
Q_UNUSED(iteration);
Q_UNUSED(resources);
KisPaintInformation pi1;
KisPaintInformation pi2;
pi1 = KisPaintInformation(QPointF(100, 100), 1.0);
pi2 = KisPaintInformation(QPointF(800, 800), 1.0);
QScopedPointer<KisStrokeJobData> data(
new FreehandStrokeStrategy::Data(0, pi1, pi2));
image->addJob(strokeId(), data.take());
image->addJob(strokeId(), new KisAsyncronousStrokeUpdateHelper::UpdateData(true));
}
void checkDeviceIsEmpty(KisPaintDeviceSP dev, const QString &name)
{
const KoColorSpace *cs = dev->colorSpace();
KisSequentialConstIterator it(dev, QRect(0,0,1024,1024));
while (it.nextPixel()) {
if (cs->opacityU8(it.rawDataConst()) > 0) {
KIS_DUMP_DEVICE_2(dev, QRect(0,0,1024,1024), "image", "dd");
qFatal("%s", QString("failed: %1").arg(name).toLatin1().data());
}
}
}
void beforeCheckingResult(KisImageWSP image, KisNodeSP activeNode) override {
ENTER_FUNCTION() << ppVar(image) << ppVar(activeNode);
KisToolUtils::clearImage(image, activeNode, 0);
image->waitForDone();
checkDeviceIsEmpty(m_mask->paintDevice(), "mask");
checkDeviceIsEmpty(m_mask->parent()->projection(), "projection");
checkDeviceIsEmpty(image->projection(), "image");
}
private:
KisMaskSP m_mask;
};
#include <KoResourcePaths.h>
void KisPaintOnTransparencyMaskTest::initTestCase()
{
- KoResourcePaths::addResourceType("kis_brushes", "data", FILES_DATA_DIR);
+ KoResourcePaths::addResourceType(ResourceType::Brushes, "data", FILES_DATA_DIR);
}
void KisPaintOnTransparencyMaskTest::test()
{
for (int i = 0; i < 1000; i++) {
PaintOnTransparencyMaskTester tester("testing_wet_circle.kpp");
tester.testSimpleStrokeNoVerification();
}
}
QTEST_MAIN(KisPaintOnTransparencyMaskTest)
diff --git a/libs/ui/tests/fill_processing_visitor_test.cpp b/libs/ui/tests/fill_processing_visitor_test.cpp
index 222e8a8dca..8ac31f1d31 100644
--- a/libs/ui/tests/fill_processing_visitor_test.cpp
+++ b/libs/ui/tests/fill_processing_visitor_test.cpp
@@ -1,145 +1,146 @@
/*
* 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 "fill_processing_visitor_test.h"
#include <QTest>
#include "kis_undo_stores.h"
#include "kis_processing_applicator.h"
#include "testutil.h"
#include "qimage_based_test.h"
#include "stroke_testing_utils.h"
#include <resources/KoPattern.h>
#include "kis_canvas_resource_provider.h"
#include <processing/fill_processing_visitor.h>
+#include <KisGlobalResourcesInterface.h>
class FillProcessingVisitorTester : public TestUtil::QImageBasedTest
{
public:
FillProcessingVisitorTester()
: QImageBasedTest("fill_processing")
{
}
void test(const QString &testname, bool haveSelection, bool usePattern, bool selectionOnly) {
KisSurrogateUndoStore *undoStore = new KisSurrogateUndoStore();
KisImageSP image = createImage(undoStore);
if (haveSelection) {
addGlobalSelection(image);
}
image->initialRefreshGraph();
QVERIFY(checkLayersInitial(image));
KisNodeSP fillNode = findNode(image->root(), "paint1");
KoCanvasResourceProvider *manager = utils::createResourceManager(image, fillNode);
- KoPattern *newPattern = new KoPattern(TestUtil::fetchDataFileLazy("HR_SketchPaper_01.pat"));
- newPattern->load();
+ KoPatternSP newPattern(new KoPattern(TestUtil::fetchDataFileLazy("HR_SketchPaper_01.pat")));
+ newPattern->load(KisGlobalResourcesInterface::instance());
Q_ASSERT(newPattern->valid());
QVariant v;
- v.setValue(static_cast<void*>(newPattern));
+ v.setValue<KoPatternSP>(newPattern);
manager->setResource(KisCanvasResourceProvider::CurrentPattern, v);
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image,
fillNode,
manager);
KisProcessingVisitorSP visitor =
new FillProcessingVisitor(QPoint(100,100),
image->globalSelection(),
resources,
false, // useFastMode
usePattern,
selectionOnly,
10, 10, 10, true, false);
KisProcessingApplicator applicator(image, fillNode,
KisProcessingApplicator::NONE);
applicator.applyVisitor(visitor);
applicator.end();
image->waitForDone();
QVERIFY(checkOneLayer(image, fillNode, testname, 500));
undoStore->undo();
image->waitForDone();
QVERIFY(checkLayersInitial(image));
}
};
void FillProcessingVisitorTest::testFillColorNoSelection()
{
FillProcessingVisitorTester tester;
tester.test("fill_color_no_selection", false, false, false);
}
void FillProcessingVisitorTest::testFillPatternNoSelection()
{
FillProcessingVisitorTester tester;
tester.test("fill_pattern_no_selection", false, true, false);
}
void FillProcessingVisitorTest::testFillColorHaveSelection()
{
FillProcessingVisitorTester tester;
tester.test("fill_color_have_selection", true, false, false);
}
void FillProcessingVisitorTest::testFillPatternHaveSelection()
{
FillProcessingVisitorTester tester;
tester.test("fill_pattern_have_selection", true, true, false);
}
void FillProcessingVisitorTest::testFillColorNoSelectionSelectionOnly()
{
FillProcessingVisitorTester tester;
tester.test("fill_color_no_selection_selection_only", false, false, true);
}
void FillProcessingVisitorTest::testFillPatternNoSelectionSelectionOnly()
{
FillProcessingVisitorTester tester;
tester.test("fill_pattern_no_selection_selection_only", false, true, true);
}
void FillProcessingVisitorTest::testFillColorHaveSelectionSelectionOnly()
{
FillProcessingVisitorTester tester;
tester.test("fill_color_have_selection_selection_only", true, false, true);
}
void FillProcessingVisitorTest::testFillPatternHaveSelectionSelectionOnly()
{
FillProcessingVisitorTester tester;
tester.test("fill_pattern_have_selection_selection_only", true, true, true);
}
QTEST_MAIN(FillProcessingVisitorTest)
diff --git a/libs/ui/tests/filter_stroke_test.cpp b/libs/ui/tests/filter_stroke_test.cpp
index 32b59bcbb6..214448e5ff 100644
--- a/libs/ui/tests/filter_stroke_test.cpp
+++ b/libs/ui/tests/filter_stroke_test.cpp
@@ -1,91 +1,91 @@
/*
* 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 "filter_stroke_test.h"
#include <QTest>
#include "stroke_testing_utils.h"
#include "strokes/kis_filter_stroke_strategy.h"
#include "kis_resources_snapshot.h"
#include "kis_image.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter_configuration.h"
-
+#include <KisGlobalResourcesInterface.h>
class FilterStrokeTester : public utils::StrokeTester
{
public:
FilterStrokeTester(const QString &filterName)
: StrokeTester(QString("filter_") + filterName, QSize(500, 500), ""),
m_filterName(filterName)
{
setBaseFuzziness(5);
}
protected:
using utils::StrokeTester::initImage;
using utils::StrokeTester::addPaintingJobs;
void initImage(KisImageWSP image, KisNodeSP activeNode) override {
QImage src(QString(FILES_DATA_DIR) + QDir::separator() + "carrot.png");
activeNode->original()->convertFromQImage(src, 0);
image->refreshGraph();
}
KisStrokeStrategy* createStroke(KisResourcesSnapshotSP resources,
KisImageWSP image) override {
Q_UNUSED(image);
KisFilterSP filter = KisFilterRegistry::instance()->value(m_filterName);
Q_ASSERT(filter);
- KisFilterConfigurationSP filterConfig = filter->defaultConfiguration();
+ KisFilterConfigurationSP filterConfig = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(filterConfig);
return new KisFilterStrokeStrategy(filter, KisFilterConfigurationSP(filterConfig), resources);
}
void addPaintingJobs(KisImageWSP image, KisResourcesSnapshotSP resources) override {
Q_UNUSED(resources);
image->addJob(strokeId(),
new KisFilterStrokeStrategy::
Data(QRect(100,100,100,100), true));
image->addJob(strokeId(),
new KisFilterStrokeStrategy::
Data(QRect(200,100,100,100), true));
image->addJob(strokeId(),
new KisFilterStrokeStrategy::
Data(QRect(100,200,100,100), true));
}
private:
QString m_filterName;
};
void FilterStrokeTest::testBlurFilter()
{
FilterStrokeTester tester("blur");
tester.test();
}
QTEST_MAIN(FilterStrokeTest)
diff --git a/libs/ui/tests/kis_asl_layer_style_serializer_test.cpp b/libs/ui/tests/kis_asl_layer_style_serializer_test.cpp
deleted file mode 100644
index 73fb569553..0000000000
--- a/libs/ui/tests/kis_asl_layer_style_serializer_test.cpp
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * 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_asl_layer_style_serializer_test.h"
-
-#include <QTest>
-
-#include <QDomDocument>
-
-#include <KoCompositeOpRegistry.h>
-#include <resources/KoAbstractGradient.h>
-#include <resources/KoStopGradient.h>
-
-#include <resources/KoPattern.h>
-
-#include "kis_global.h"
-
-#include "testutil.h"
-#include "kis_psd_layer_style.h"
-#include "kis_asl_layer_style_serializer.h"
-#include <asl/kis_asl_reader.h>
-
-
-#define CMP(object, method, value) QCOMPARE(style->object()->method(), value)
-
-void KisAslLayerStyleSerializerTest::testReading()
-{
- KisAslLayerStyleSerializer s;
-
-// QString srcFileName(TestUtil::fetchDataFileLazy("asl/test_all_style.asl"));
- QString srcFileName(TestUtil::fetchDataFileLazy("asl/test_all_and_pattern.asl"));
- QFile aslFile(srcFileName);
- aslFile.open(QIODevice::ReadOnly);
- s.readFromDevice(&aslFile);
-
- QVector<KisPSDLayerStyleSP> styles = s.styles();
-
- QVERIFY(styles.size() == 1);
- KisPSDLayerStyleSP style = styles.first();
-
- CMP(dropShadow, effectEnabled, true);
- CMP(dropShadow, blendMode, COMPOSITE_MULT);
- CMP(dropShadow, color, QColor(Qt::black));
- CMP(dropShadow, opacity, 15);
- CMP(dropShadow, angle, -120);
- CMP(dropShadow, useGlobalLight, false);
- CMP(dropShadow, distance, 2);
- CMP(dropShadow, spread, 1);
- CMP(dropShadow, size, 7);
- CMP(dropShadow, antiAliased, true);
- CMP(dropShadow, noise, 3);
- // CMP(dropShadow, contourLookupTable,);
-
- CMP(innerShadow, effectEnabled, true);
- CMP(innerShadow, blendMode, COMPOSITE_DARKEN);
- CMP(innerShadow, color, QColor(Qt::black));
- CMP(innerShadow, opacity, 28);
- CMP(innerShadow, angle, 120);
- CMP(innerShadow, useGlobalLight, true);
- CMP(innerShadow, distance, 8);
- CMP(innerShadow, spread, 15);
- CMP(innerShadow, size, 27);
- CMP(innerShadow, antiAliased, false);
- CMP(innerShadow, noise, 10);
- // CMP(innerShadow, contourLookupTable,);
-
- CMP(outerGlow, effectEnabled, true);
- CMP(outerGlow, blendMode, COMPOSITE_SCREEN);
- CMP(outerGlow, color, QColor(255, 255, 189));
- CMP(outerGlow, opacity, 43);
- CMP(outerGlow, spread, 23);
- CMP(outerGlow, size, 109);
- CMP(outerGlow, antiAliased, true);
- CMP(outerGlow, noise, 29);
- // CMP(outerGlow, contourLookupTable,);
- // CMP(outerGlow, gradient,);
- CMP(outerGlow, fillType, psd_fill_solid_color);
- CMP(outerGlow, technique, psd_technique_precise);
- CMP(outerGlow, range, 69);
- CMP(outerGlow, jitter, 18);
-
- CMP(innerGlow, effectEnabled, true);
- CMP(innerGlow, blendMode, COMPOSITE_SCREEN);
- CMP(innerGlow, color, QColor(255, 255, 189));
- CMP(innerGlow, opacity, 55);
- CMP(innerGlow, spread, 21);
- CMP(innerGlow, size, 128);
- CMP(innerGlow, antiAliased, true);
- CMP(innerGlow, noise, 33);
- // CMP(innerGlow, contourLookupTable,);
- // CMP(innerGlow, gradient,);
- CMP(innerGlow, fillType, psd_fill_solid_color);
- CMP(innerGlow, technique, psd_technique_softer);
- CMP(innerGlow, range, 32);
- CMP(innerGlow, jitter, 22);
- CMP(innerGlow, source, psd_glow_edge);
-
- CMP(satin, effectEnabled, true);
- CMP(satin, blendMode, COMPOSITE_MULT);
- CMP(satin, color, QColor(Qt::black));
- CMP(satin, opacity, 68);
- CMP(satin, angle, 19);
- CMP(satin, distance, 11);
- CMP(satin, size, 14);
- CMP(satin, antiAliased, false);
- CMP(satin, invert, true);
- // CMP(satin, contourLookupTable,);
-
- CMP(colorOverlay, effectEnabled, true);
- CMP(colorOverlay, blendMode, COMPOSITE_OVER);
- CMP(colorOverlay, color, QColor(Qt::red));
- CMP(colorOverlay, opacity, 63);
-
- CMP(gradientOverlay, effectEnabled, true);
- CMP(gradientOverlay, blendMode, COMPOSITE_OVER);
- CMP(gradientOverlay, opacity, 100);
- CMP(gradientOverlay, angle, 90);
- CMP(gradientOverlay, style, psd_gradient_style_linear);
- CMP(gradientOverlay, reverse, false);
- CMP(gradientOverlay, alignWithLayer, true);
- CMP(gradientOverlay, scale, 100);
- CMP(gradientOverlay, gradientXOffset, 0);
- CMP(gradientOverlay, gradientYOffset, 0);
- //CMP(gradientOverlay, dither, );
- CMP(gradientOverlay, gradient()->name, QString("Two Color"));
-
- CMP(stroke, effectEnabled, true);
- CMP(stroke, blendMode, COMPOSITE_OVER);
- CMP(stroke, opacity, 67);
- CMP(stroke, size, 13);
- CMP(stroke, fillType, psd_fill_solid_color);
- CMP(stroke, position, psd_stroke_outside);
- CMP(stroke, color, QColor(210, 33, 87));
-
- CMP(bevelAndEmboss, effectEnabled, true);
-
- CMP(bevelAndEmboss, highlightBlendMode, COMPOSITE_SCREEN);
- CMP(bevelAndEmboss, highlightOpacity, 75);
- CMP(bevelAndEmboss, highlightColor, QColor(255, 255, 255));
-
- CMP(bevelAndEmboss, shadowBlendMode, COMPOSITE_MULT);
- CMP(bevelAndEmboss, shadowOpacity, 75);
- CMP(bevelAndEmboss, shadowColor, QColor(Qt::black));
-
- CMP(bevelAndEmboss, technique, psd_technique_softer);
- CMP(bevelAndEmboss, style, psd_bevel_inner_bevel);
-
- CMP(bevelAndEmboss, useGlobalLight, true);
- CMP(bevelAndEmboss, angle, 120);
- CMP(bevelAndEmboss, altitude, 30);
-
- CMP(bevelAndEmboss, depth, 83);
- CMP(bevelAndEmboss, size, 49);
-
- CMP(bevelAndEmboss, direction, psd_direction_up);
-
- // FIXME: contour
- CMP(bevelAndEmboss, glossAntiAliased, false);
- CMP(bevelAndEmboss, soften, 2);
- CMP(bevelAndEmboss, contourEnabled, true);
- // FIXME: contour curve
-
- CMP(bevelAndEmboss, antiAliased, true);
- CMP(bevelAndEmboss, contourRange, 60);
- CMP(bevelAndEmboss, textureEnabled, false);
-
- CMP(patternOverlay, effectEnabled, true);
- CMP(patternOverlay, blendMode, COMPOSITE_OVER);
- CMP(patternOverlay, opacity, 100);
- CMP(patternOverlay, alignWithLayer, true);
- CMP(patternOverlay, scale, 100);
- CMP(patternOverlay, horizontalPhase, 201);
- CMP(patternOverlay, verticalPhase, 162);
-
- CMP(patternOverlay, pattern()->name, QString("$$$/Presets/Patterns/Patterns_pat/Bubbles=Bubbles"));
- CMP(patternOverlay, pattern()->filename, QString("b7334da0-122f-11d4-8bb5-e27e45023b5f.pat"));
-
-}
-
-void KisAslLayerStyleSerializerTest::testWriting()
-{
- QVector<KisPSDLayerStyleSP> styles;
-
- QByteArray refXMLDoc;
-
- {
- KisAslLayerStyleSerializer s;
-
- QString srcFileName(TestUtil::fetchDataFileLazy("asl/test_all_and_pattern.asl"));
- QFile aslFile(srcFileName);
- aslFile.open(QIODevice::ReadOnly);
- s.readFromDevice(&aslFile);
-
- styles = s.styles();
-
- {
- aslFile.seek(0);
-
- KisAslReader reader;
- QDomDocument doc = reader.readFile(&aslFile);
- refXMLDoc = doc.toByteArray();
- }
- }
-
- // now we have an initialized KisPSDLayerStyle object
- {
- KisAslLayerStyleSerializer s;
-
- s.setStyles(styles);
-
- QFile dstFile("test_written.asl");
- dstFile.open(QIODevice::WriteOnly);
- s.saveToDevice(&dstFile);
- dstFile.close();
- }
-
- QByteArray resultXMLDoc;
-
- {
- QFile resultFile("test_written.asl");
- resultFile.open(QIODevice::ReadOnly);
-
- KisAslReader reader;
- QDomDocument doc = reader.readFile(&resultFile);
- resultXMLDoc = doc.toByteArray();
- }
-
- QFile refXMLFile("save_round_trip_src.xml");
- refXMLFile.open(QIODevice::WriteOnly);
- refXMLFile.write(refXMLDoc);
- refXMLFile.close();
-
- QFile resultXMLFile("save_round_trip_dst.xml");
- resultXMLFile.open(QIODevice::WriteOnly);
- resultXMLFile.write(resultXMLDoc);
- resultXMLFile.close();
-
- QEXPECT_FAIL("", "Tried to compare two xml files, which are not the same. The order of attributes when serializing is undefined", Continue);
- QCOMPARE(resultXMLDoc, refXMLDoc);
-}
-
-#include <KoResourceServerProvider.h>
-
-
-void KisAslLayerStyleSerializerTest::testWritingGlobalPatterns()
-{
- KisPSDLayerStyleSP style(new KisPSDLayerStyle());
-
- KoResourceServer<KoPattern> *server = KoResourceServerProvider::instance()->patternServer();
- QList<KoPattern*> sortedResources = server->sortedResources();
-
- KoPattern *pattern = sortedResources.first();
- Q_ASSERT(pattern);
-
- dbgKrita << ppVar(pattern->name());
- dbgKrita << ppVar(pattern->filename());
-
- style->patternOverlay()->setEffectEnabled(true);
- style->patternOverlay()->setPattern(pattern);
-
- {
- KisAslLayerStyleSerializer s;
-
- s.setStyles(QVector<KisPSDLayerStyleSP>() << style);
-
- QFile dstFile("test_written_pattern_only.asl");
- dstFile.open(QIODevice::WriteOnly);
- s.saveToDevice(&dstFile);
- dstFile.close();
- }
-/*
- QByteArray resultXMLDoc;
-
- {
- QFile resultFile("test_written.asl");
- resultFile.open(QIODevice::ReadOnly);
-
- KisAslReader reader;
- QDomDocument doc = reader.readFile(&resultFile);
- resultXMLDoc = doc.toByteArray();
- }
-*/
-
-}
-
-void KisAslLayerStyleSerializerTest::testReadMultipleStyles()
-{
- KisPSDLayerStyleSP style(new KisPSDLayerStyle());
-
- QVector<KisPSDLayerStyleSP> styles;
-
- {
- KisAslLayerStyleSerializer s;
-
- QString srcFileName(TestUtil::fetchDataFileLazy("asl/multiple_styles.asl"));
- QFile aslFile(srcFileName);
- aslFile.open(QIODevice::ReadOnly);
- s.readFromDevice(&aslFile);
-
- styles = s.styles();
- }
-
-
- {
- KisAslLayerStyleSerializer s;
-
- QString dstFileName("multiple_styles_out.asl");
- QFile aslFile(dstFileName);
- aslFile.open(QIODevice::WriteOnly);
-
- s.setStyles(styles);
- s.saveToDevice(&aslFile);
- }
-
- {
- KisAslLayerStyleSerializer s;
-
- QString srcFileName("multiple_styles_out.asl");
- QFile aslFile(srcFileName);
- aslFile.open(QIODevice::ReadOnly);
- s.readFromDevice(&aslFile);
-
- styles = s.styles();
-
- dbgKrita << ppVar(styles.size());
- }
-}
-
-void KisAslLayerStyleSerializerTest::testWritingGradients()
-{
- KoStopGradient stopGradient("");
-
- {
- const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
- QList<KoGradientStop> stops;
- stops << KoGradientStop(0.0, KoColor(Qt::black, cs));
- stops << KoGradientStop(0.3, KoColor(Qt::red, cs));
- stops << KoGradientStop(0.6, KoColor(Qt::green, cs));
- stops << KoGradientStop(1.0, KoColor(Qt::white, cs));
- stopGradient.setStops(stops);
- }
-
- KisPSDLayerStyleSP style(new KisPSDLayerStyle());
-
- style->outerGlow()->setEffectEnabled(true);
- style->outerGlow()->setFillType(psd_fill_gradient);
- style->outerGlow()->setGradient(toQShared(new KoStopGradient(stopGradient)));
-
- style->innerGlow()->setEffectEnabled(true);
- style->innerGlow()->setFillType(psd_fill_gradient);
- style->innerGlow()->setGradient(toQShared(new KoStopGradient(stopGradient)));
-
- style->gradientOverlay()->setEffectEnabled(true);
- style->gradientOverlay()->setGradient(toQShared(new KoStopGradient(stopGradient)));
-
- style->stroke()->setEffectEnabled(true);
- style->stroke()->setFillType(psd_fill_gradient);
- style->stroke()->setGradient(toQShared(new KoStopGradient(stopGradient)));
-
- {
- KisAslLayerStyleSerializer s;
-
- s.setStyles(QVector<KisPSDLayerStyleSP>() << style);
-
- QFile dstFile("test_written_stop_gradient.asl");
- dstFile.open(QIODevice::WriteOnly);
- s.saveToDevice(&dstFile);
- dstFile.close();
- }
-
- QString xmlDoc;
-
- {
- QFile resultFile("test_written_stop_gradient.asl");
- resultFile.open(QIODevice::ReadOnly);
-
- KisAslReader reader;
- QDomDocument doc = reader.readFile(&resultFile);
- xmlDoc = doc.toString();
- }
-
- {
- // the reference document has stripped "Idnt" field which is random
-
- QRegExp rx("<node key=\"Idnt\" type=\"Text\" value=\".+\"/>");
- rx.setMinimal(true);
-
- int pos = 0;
- while ((pos = rx.indexIn(xmlDoc, pos)) != -1) {
- xmlDoc.remove(pos, rx.matchedLength());
- }
-
- {
- //QFile xmlFile("reference_gradients.asl.xml");
- //xmlFile.open(QIODevice::WriteOnly);
- //xmlFile.write(xmlDoc.toLatin1());
- //xmlFile.close();
- }
-
- QString refFileName(TestUtil::fetchDataFileLazy("reference_gradients.asl.xml"));
- QFile refFile(refFileName);
- refFile.open(QIODevice::ReadOnly);
- QString refDoc = QString(refFile.readAll());
-
- QEXPECT_FAIL("", "Tried to compare two gradient files, which are not the same. The order of attributes when serializing is undefined.", Continue);
- QCOMPARE(xmlDoc, refDoc);
- }
-}
-
-QTEST_MAIN(KisAslLayerStyleSerializerTest)
diff --git a/libs/ui/tests/kis_derived_resources_test.cpp b/libs/ui/tests/kis_derived_resources_test.cpp
index 12d339b763..4f75c637fb 100644
--- a/libs/ui/tests/kis_derived_resources_test.cpp
+++ b/libs/ui/tests/kis_derived_resources_test.cpp
@@ -1,159 +1,156 @@
/*
* 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_derived_resources_test.h"
#include <QTest>
#include <brushengine/kis_paintop_preset.h>
#include <QApplication>
#include <KoCanvasResourceProvider.h>
#include "kis_canvas_resource_provider.h"
#include <util.h>
#include <KisMainWindow.h>
#include <KisDocument.h>
#include <KisPart.h>
#include <KisView.h>
#include <KisViewManager.h>
#include <kis_paintop_settings.h>
#include <KoResourcePaths.h>
#include <kis_config.h>
#include "testutil.h"
#include "opengl/kis_opengl.h"
-
+#include <KisGlobalResourcesInterface.h>
void addResourceTypes()
{
// All Krita's resource types
KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/");
KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc");
KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/");
KoResourcePaths::addResourceType("kis_actions", "data", "/actions");
- KoResourcePaths::addResourceType("kis_brushes", "data", "/brushes/");
+ KoResourcePaths::addResourceType(ResourceType::Brushes, "data", "/brushes/");
KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/");
KoResourcePaths::addResourceType("kis_images", "data", "/images/");
- KoResourcePaths::addResourceType("kis_paintoppresets", "data", "/paintoppresets/");
+ KoResourcePaths::addResourceType(ResourceType::PaintOpPresets, "data", "/paintoppresets/");
KoResourcePaths::addResourceType("kis_pics", "data", "/pics/");
KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/");
KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/");
KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/");
KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/");
- KoResourcePaths::addResourceType("kis_windowlayouts", "data", "/windowlayouts/");
- KoResourcePaths::addResourceType("kis_workspaces", "data", "/workspaces/");
- KoResourcePaths::addResourceType("ko_effects", "data", "/effects/");
- KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/");
- KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/");
- KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/");
+ KoResourcePaths::addResourceType(ResourceType::WindowLayouts, "data", "/windowlayouts/");
+ KoResourcePaths::addResourceType(ResourceType::Workspaces, "data", "/workspaces/");
+ KoResourcePaths::addResourceType(ResourceType::FilterEffects, "data", "/effects/");
+ KoResourcePaths::addResourceType(ResourceType::Gradients, "data", "/gradients/");
+ KoResourcePaths::addResourceType(ResourceType::Palettes, "data", "/palettes/");
+ KoResourcePaths::addResourceType(ResourceType::Patterns, "data", "/patterns/");
KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/");
KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl");
KoResourcePaths::addResourceType("tags", "data", "/tags/");
KisOpenGL::testingInitializeDefaultSurfaceFormat();
KisConfig cfg(false);
cfg.disableOpenGL();
}
void KisDerivedResourcesTest::test()
{
addResourceTypes();
KisDocument* doc = createEmptyDocument();
-
-
KisMainWindow* mainWindow = KisPart::instance()->createMainWindow();
QPointer<KisView> view = new KisView(doc, mainWindow->viewManager(), mainWindow);
KisViewManager *viewManager = new KisViewManager(mainWindow, mainWindow->actionCollection());
KoCanvasResourceProvider *manager = viewManager->canvasResourceProvider()->resourceManager();
QApplication::processEvents();
QString presetFileName = "autobrush_300px.kpp";
QVariant i;
KisPaintOpPresetSP preset;
if (!presetFileName.isEmpty()) {
QString fullFileName = TestUtil::fetchDataFileLazy(presetFileName);
- preset = new KisPaintOpPreset(fullFileName);
- bool presetValid = preset->load();
+ preset = KisPaintOpPresetSP(new KisPaintOpPreset(fullFileName));
+ bool presetValid = preset->load(KisGlobalResourcesInterface::instance());
Q_ASSERT(presetValid); Q_UNUSED(presetValid);
i.setValue(preset);
-
}
QVERIFY(i.isValid());
QSignalSpy spy(manager, SIGNAL(canvasResourceChanged(int,QVariant)));
manager->setResource(KisCanvasResourceProvider::CurrentPaintOpPreset, i);
QMap<int, QVariant> expectedSignals;
expectedSignals[KisCanvasResourceProvider::CurrentPaintOpPreset] = QVariant::fromValue(preset);
expectedSignals[KisCanvasResourceProvider::EraserMode] = false;
expectedSignals[KisCanvasResourceProvider::LodSizeThresholdSupported] = true;
expectedSignals[KisCanvasResourceProvider::EffectiveLodAvailablility] = true;
expectedSignals[KisCanvasResourceProvider::LodSizeThreshold] = 100;
expectedSignals[KisCanvasResourceProvider::LodAvailability] = true;
expectedSignals[KisCanvasResourceProvider::Opacity] = 1.0;
expectedSignals[KisCanvasResourceProvider::Size] = 300.0;
expectedSignals[KisCanvasResourceProvider::Flow] = 1.0;
expectedSignals[KisCanvasResourceProvider::CurrentEffectiveCompositeOp] = COMPOSITE_OVER;
expectedSignals[KisCanvasResourceProvider::CurrentCompositeOp] = COMPOSITE_OVER;
auto it = spy.begin();
for (; it != spy.end(); ++it) {
const int id = (*it)[0].toInt();
const QVariant value = (*it)[1];
if (!expectedSignals.contains(id)) {
qDebug() << ppVar(id) << ppVar(value);
QFAIL("Unexpected signal!");
} else {
if (expectedSignals[id] != value) {
qDebug() << ppVar(id) << ppVar(value) << ppVar(expectedSignals[id]);
QFAIL("Unexpected value!");
}
}
}
QCOMPARE(spy.size(), expectedSignals.size());
spy.clear();
preset->settings()->setPaintOpOpacity(0.8);
QCOMPARE(spy.size(), 1);
QCOMPARE(spy[0][0].toInt(), (int)KisCanvasResourceProvider::Opacity);
QCOMPARE(spy[0][1].toDouble(), 0.8);
spy.clear();
mainWindow->hide();
QApplication::processEvents();
delete view;
delete doc;
delete mainWindow;
}
KISTEST_MAIN(KisDerivedResourcesTest)
diff --git a/libs/ui/tests/kis_stop_gradient_editor_test.cpp b/libs/ui/tests/kis_stop_gradient_editor_test.cpp
index 4d332ad294..290876f723 100644
--- a/libs/ui/tests/kis_stop_gradient_editor_test.cpp
+++ b/libs/ui/tests/kis_stop_gradient_editor_test.cpp
@@ -1,50 +1,50 @@
/*
* 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_stop_gradient_editor_test.h"
#include <QTest>
#include <QDialog>
#include <QVBoxLayout>
#include <QLinearGradient>
#include "kis_debug.h"
#include "kis_stopgradient_editor.h"
void KisStopGradientEditorTest::test()
{
QLinearGradient gradient;
- QScopedPointer<KoStopGradient> koGradient(KoStopGradient::fromQGradient(&gradient));
+ QSharedPointer<KoStopGradient> koGradient(KoStopGradient::fromQGradient(&gradient));
QDialog dlg;
KisStopGradientEditor *widget = new KisStopGradientEditor(&dlg);
- widget->setGradient(koGradient.data());
+ widget->setGradient(koGradient);
QVBoxLayout *layout = new QVBoxLayout(&dlg);
layout->setContentsMargins(0,0,0,0);
layout->addWidget(widget);
dlg.setLayout(layout);
dlg.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
//dlg.exec();
qWarning() << "WARNING: showing of the dialogs in the unittest is disabled!";
}
QTEST_MAIN(KisStopGradientEditorTest)
diff --git a/libs/ui/tests/util.h b/libs/ui/tests/util.h
index a249d6c182..f7e8a5b64b 100644
--- a/libs/ui/tests/util.h
+++ b/libs/ui/tests/util.h
@@ -1,232 +1,230 @@
/*
* 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 _UTIL_H_
#define _UTIL_H_
#include <QTest>
#include <QBitArray>
#include <KisDocument.h>
#include <KoDocumentInfo.h>
#include <KoColorSpaceRegistry.h>
#include <KoShapeContainer.h>
#include <KoColorSpace.h>
#include <KoPathShape.h>
#include <kis_count_visitor.h>
#include "kis_types.h"
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter.h"
#include "KisPart.h"
#include "kis_image.h"
#include "kis_pixel_selection.h"
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_clone_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_shape_layer.h"
#include "kis_filter_mask.h"
#include "kis_transparency_mask.h"
#include "kis_selection_mask.h"
#include "kis_selection.h"
#include "kis_fill_painter.h"
#include "kis_shape_selection.h"
#include "kis_default_bounds.h"
#include "kis_transform_mask_params_interface.h"
#include "kis_shape_controller.h"
+#include <KisGlobalResourcesInterface.h>
KisSelectionSP createPixelSelection(KisPaintDeviceSP paintDevice)
{
KisSelectionSP pixelSelection = new KisSelection(new KisSelectionDefaultBounds(paintDevice));
KisFillPainter gc(pixelSelection->pixelSelection());
gc.fillRect(10, 10, 200, 200, KoColor(gc.device()->colorSpace()));
gc.fillRect(150, 150, 200, 200, KoColor(QColor(100, 100, 100, 100), gc.device()->colorSpace()));
gc.end();
return pixelSelection;
}
KisSelectionSP createVectorSelection(KisPaintDeviceSP paintDevice, KisImageWSP image, KoShapeControllerBase *shapeController)
{
KisSelectionSP vectorSelection = new KisSelection(new KisSelectionDefaultBounds(paintDevice));
KoPathShape* path = new KoPathShape();
path->setShapeId(KoPathShapeId);
path->moveTo(QPointF(10, 10));
path->lineTo(QPointF(10, 10) + QPointF(100, 0));
path->lineTo(QPointF(100, 100));
path->lineTo(QPointF(10, 10) + QPointF(0, 100));
path->close();
path->normalize();
KisShapeSelection* shapeSelection = new KisShapeSelection(shapeController, image, vectorSelection);
shapeSelection->addShape(path);
vectorSelection->setShapeSelection(shapeSelection);
return vectorSelection;
}
QTransform createTestingTransform() {
return QTransform(1,2,3,4,5,6,7,8,9);
}
KisDocument* createCompleteDocument(bool shouldMaskToShapeLayer = false)
{
KisImageWSP image = new KisImage(0, 1024, 1024, KoColorSpaceRegistry::instance()->rgb8(), "test for roundtrip");
KisDocument *doc = qobject_cast<KisDocument*>(KisPart::instance()->createDocument());
doc->setCurrentImage(image);
doc->documentInfo()->setAboutInfo("title", image->objectName());
KisGroupLayerSP group1 = new KisGroupLayer(image, "group1", 50);
KisGroupLayerSP group2 = new KisGroupLayer(image, "group2", 100);
KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paintlayer1", OPACITY_OPAQUE_U8);
paintLayer1->setUserLocked(true);
QBitArray channelFlags(4);
channelFlags[0] = true;
channelFlags[2] = true;
paintLayer1->setChannelFlags(channelFlags);
{
KisFillPainter gc(paintLayer1->paintDevice());
gc.fillRect(10, 10, 200, 200, KoColor(Qt::red, paintLayer1->paintDevice()->colorSpace()));
gc.end();
}
KisPaintLayerSP paintLayer2 = new KisPaintLayer(image, "paintlayer2", OPACITY_TRANSPARENT_U8, KoColorSpaceRegistry::instance()->lab16());
paintLayer2->setVisible(false);
{
KisFillPainter gc(paintLayer2->paintDevice());
gc.fillRect(0, 0, 900, 1024, KoColor(QColor(10, 20, 30), paintLayer2->paintDevice()->colorSpace()));
gc.end();
}
KisCloneLayerSP cloneLayer1 = new KisCloneLayer(group1, image, "clonelayer1", 150);
cloneLayer1->setX(100);
cloneLayer1->setY(100);
KisSelectionSP pixelSelection = createPixelSelection(paintLayer1->paintDevice());
- KisFilterConfigurationSP kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration();
+ KisFilterConfigurationSP kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(kfc);
- KisAdjustmentLayerSP adjustmentLayer1 = new KisAdjustmentLayer(image, "adjustmentLayer1", kfc, pixelSelection);
- kfc = 0; // kfc cannot be shared!
+ KisAdjustmentLayerSP adjustmentLayer1 = new KisAdjustmentLayer(image, "adjustmentLayer1", kfc->cloneWithResourcesSnapshot(), pixelSelection);
KisSelectionSP vectorSelection = createVectorSelection(paintLayer2->paintDevice(), image, doc->shapeController());
- kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration();
- KisAdjustmentLayerSP adjustmentLayer2 = new KisAdjustmentLayer(image, "adjustmentLayer2", kfc, vectorSelection);
- kfc = 0; // kfc cannot be shared!
+ KisAdjustmentLayerSP adjustmentLayer2 = new KisAdjustmentLayer(image, "adjustmentLayer2", kfc->cloneWithResourcesSnapshot(), vectorSelection);
image->addNode(paintLayer1);
image->addNode(group1);
image->addNode(paintLayer2, group1);
image->addNode(group2);
image->addNode(cloneLayer1, group2);
image->addNode(adjustmentLayer1, group2);
// KoShapeContainer * parentContainer =
// dynamic_cast<KoShapeContainer*>(doc->shapeForNode(group1));
KoPathShape* path = new KoPathShape();
path->setShapeId(KoPathShapeId);
path->moveTo(QPointF(10, 10));
path->lineTo(QPointF(10, 10) + QPointF(100, 0));
path->lineTo(QPointF(100, 100));
path->lineTo(QPointF(10, 10) + QPointF(0, 100));
path->close();
path->normalize();
KisShapeLayerSP shapeLayer = new KisShapeLayer(doc->shapeController(), image, "shapeLayer1", 75);
shapeLayer->addShape(path);
image->addNode(shapeLayer, group1);
image->addNode(adjustmentLayer2, group1);
KisFilterMaskSP filterMask1 = new KisFilterMask();
filterMask1->setName("filterMask1");
- kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration();
- filterMask1->setFilter(kfc);
+ kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(KisGlobalResourcesInterface::instance());
+ filterMask1->setFilter(kfc->cloneWithResourcesSnapshot());
kfc = 0; // kfc cannot be shared!
filterMask1->setSelection(createPixelSelection(paintLayer1->paintDevice()));
image->addNode(filterMask1, paintLayer1);
KisFilterMaskSP filterMask2 = new KisFilterMask();
filterMask2->setName("filterMask2");
- kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration();
- filterMask2->setFilter(kfc);
+ kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(KisGlobalResourcesInterface::instance());
+ filterMask2->setFilter(kfc->cloneWithResourcesSnapshot());
kfc = 0; // kfc cannot be shared!
filterMask2->setSelection(createVectorSelection(paintLayer2->paintDevice(), image, doc->shapeController()));
image->addNode(filterMask2, paintLayer2);
KisTransparencyMaskSP transparencyMask1 = new KisTransparencyMask();
transparencyMask1->setName("transparencyMask1");
transparencyMask1->setSelection(createPixelSelection(paintLayer1->paintDevice()));
image->addNode(transparencyMask1, group1);
KisTransparencyMaskSP transparencyMask2 = new KisTransparencyMask();
transparencyMask2->setName("transparencyMask2");
transparencyMask2->setSelection(createPixelSelection(paintLayer1->paintDevice()));
image->addNode(transparencyMask2, group2);
KisSelectionMaskSP selectionMask1 = new KisSelectionMask(image);
image->addNode(selectionMask1, paintLayer1);
selectionMask1->setName("selectionMask1");
selectionMask1->setSelection(createPixelSelection(paintLayer1->paintDevice()));
KisSelectionMaskSP selectionMask2 = new KisSelectionMask(image);
selectionMask2->setName("selectionMask2");
selectionMask2->setSelection(createPixelSelection(paintLayer2->paintDevice()));
image->addNode(selectionMask2, paintLayer2);
KisTransformMaskSP transformMask = new KisTransformMask();
transformMask->setName("testTransformMask");
transformMask->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(createTestingTransform())));
image->addNode(transformMask, paintLayer2);
if (shouldMaskToShapeLayer) {
// add all-visible transparency mask to crash a shape layer
KisTransparencyMaskSP transparencyMask3 = new KisTransparencyMask();
transparencyMask3->setName("crashy-transparency-mask");
transparencyMask3->initSelection(shapeLayer);
image->addNode(transparencyMask3, shapeLayer);
}
return doc;
}
KisDocument *createEmptyDocument()
{
KisImageWSP image = new KisImage(0, 1024, 1024, KoColorSpaceRegistry::instance()->rgb8(), "test for roundtrip");
KisDocument *doc = qobject_cast<KisDocument*>(KisPart::instance()->createDocument());
doc->setCurrentImage(image);
doc->documentInfo()->setAboutInfo("title", image->objectName());
return doc;
}
#endif
diff --git a/libs/ui/tool/kis_painting_information_builder.cpp b/libs/ui/tool/kis_painting_information_builder.cpp
index b005a4a67b..96a238c2c7 100644
--- a/libs/ui/tool/kis_painting_information_builder.cpp
+++ b/libs/ui/tool/kis_painting_information_builder.cpp
@@ -1,259 +1,259 @@
/*
* 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_painting_information_builder.h"
#include <KoPointerEvent.h>
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_cubic_curve.h"
#include "kis_speed_smoother.h"
#include <KoCanvasResourceProvider.h>
#include "kis_canvas_resource_provider.h"
/***********************************************************************/
/* KisPaintingInformationBuilder */
/***********************************************************************/
const int KisPaintingInformationBuilder::LEVEL_OF_PRESSURE_RESOLUTION = 1024;
KisPaintingInformationBuilder::KisPaintingInformationBuilder()
: m_speedSmoother(new KisSpeedSmoother()),
m_pressureDisabled(false)
{
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()),
SLOT(updateSettings()));
updateSettings();
}
KisPaintingInformationBuilder::~KisPaintingInformationBuilder()
{
}
void KisPaintingInformationBuilder::updateSettings()
{
KisConfig cfg(true);
KisCubicCurve curve;
curve.fromString(cfg.pressureTabletCurve());
m_pressureSamples = curve.floatTransfer(LEVEL_OF_PRESSURE_RESOLUTION + 1);
}
KisPaintInformation KisPaintingInformationBuilder::startStroke(KoPointerEvent *event,
int timeElapsed,
const KoCanvasResourceProvider *manager)
{
if (manager) {
m_pressureDisabled = manager->resource(KisCanvasResourceProvider::DisablePressure).toBool();
}
m_startPoint = event->point;
return createPaintingInformation(event, timeElapsed);
}
KisPaintInformation KisPaintingInformationBuilder::continueStroke(KoPointerEvent *event,
int timeElapsed)
{
return createPaintingInformation(event, timeElapsed);
}
QPointF KisPaintingInformationBuilder::adjustDocumentPoint(const QPointF &point, const QPointF &/*startPoint*/)
{
return point;
}
QPointF KisPaintingInformationBuilder::documentToImage(const QPointF &point)
{
return point;
}
QPointF KisPaintingInformationBuilder::imageToView(const QPointF &point)
{
return point;
}
qreal KisPaintingInformationBuilder::calculatePerspective(const QPointF &documentPoint)
{
Q_UNUSED(documentPoint);
return 1.0;
}
qreal KisPaintingInformationBuilder::canvasRotation() const
{
return 0;
}
bool KisPaintingInformationBuilder::canvasMirroredX() const
{
return false;
}
bool KisPaintingInformationBuilder::canvasMirroredY() const
{
return false;
}
KisPaintInformation KisPaintingInformationBuilder::createPaintingInformation(KoPointerEvent *event,
int timeElapsed)
{
QPointF adjusted = adjustDocumentPoint(event->point, m_startPoint);
QPointF imagePoint = documentToImage(adjusted);
qreal perspective = calculatePerspective(adjusted);
qreal speed = m_speedSmoother->getNextSpeed(imageToView(imagePoint));
KisPaintInformation pi(imagePoint,
- !m_pressureDisabled ? 1.0 : pressureToCurve(event->pressure()),
- event->xTilt(), event->yTilt(),
- event->rotation(),
- event->tangentialPressure(),
- perspective,
- timeElapsed,
- speed);
+ !m_pressureDisabled ? 1.0 : pressureToCurve(event->pressure()),
+ event->xTilt(), event->yTilt(),
+ event->rotation(),
+ event->tangentialPressure(),
+ perspective,
+ timeElapsed,
+ speed);
pi.setCanvasRotation(canvasRotation());
pi.setCanvasMirroredH(canvasMirroredX());
pi.setCanvasMirroredV(canvasMirroredY());
return pi;
}
KisPaintInformation KisPaintingInformationBuilder::hover(const QPointF &imagePoint,
const KoPointerEvent *event)
{
qreal perspective = calculatePerspective(imagePoint);
qreal speed = m_speedSmoother->getNextSpeed(imageToView(imagePoint));
if (event) {
return KisPaintInformation::createHoveringModeInfo(imagePoint,
PRESSURE_DEFAULT,
event->xTilt(), event->yTilt(),
event->rotation(),
event->tangentialPressure(),
perspective,
speed,
canvasRotation(),
canvasMirroredX(),
canvasMirroredY());
} else {
KisPaintInformation pi = KisPaintInformation::createHoveringModeInfo(imagePoint);
pi.setCanvasRotation(canvasRotation());
pi.setCanvasMirroredH(canvasMirroredX());
pi.setCanvasMirroredV(canvasMirroredY());
return pi;
}
}
qreal KisPaintingInformationBuilder::pressureToCurve(qreal pressure)
{
return KisCubicCurve::interpolateLinear(pressure, m_pressureSamples);
}
/***********************************************************************/
/* KisConverterPaintingInformationBuilder */
/***********************************************************************/
#include "kis_coordinates_converter.h"
KisConverterPaintingInformationBuilder::KisConverterPaintingInformationBuilder(const KisCoordinatesConverter *converter)
: m_converter(converter)
{
}
QPointF KisConverterPaintingInformationBuilder::documentToImage(const QPointF &point)
{
return m_converter->documentToImage(point);
}
QPointF KisConverterPaintingInformationBuilder::imageToView(const QPointF &point)
{
return m_converter->documentToWidget(point);
}
qreal KisConverterPaintingInformationBuilder::canvasRotation() const
{
return m_converter->rotationAngle();
}
bool KisConverterPaintingInformationBuilder::canvasMirroredX() const
{
return m_converter->xAxisMirrored();
}
bool KisConverterPaintingInformationBuilder::canvasMirroredY() const
{
return m_converter->yAxisMirrored();
}
/***********************************************************************/
/* KisToolFreehandPaintingInformationBuilder */
/***********************************************************************/
#include "kis_tool_freehand.h"
#include "kis_canvas2.h"
KisToolFreehandPaintingInformationBuilder::KisToolFreehandPaintingInformationBuilder(KisToolFreehand *tool)
: m_tool(tool)
{
}
QPointF KisToolFreehandPaintingInformationBuilder::documentToImage(const QPointF &point)
{
return m_tool->convertToPixelCoord(point);
}
QPointF KisToolFreehandPaintingInformationBuilder::imageToView(const QPointF &point)
{
return m_tool->pixelToView(point);
}
QPointF KisToolFreehandPaintingInformationBuilder::adjustDocumentPoint(const QPointF &point, const QPointF &startPoint)
{
return m_tool->adjustPosition(point, startPoint);
}
qreal KisToolFreehandPaintingInformationBuilder::calculatePerspective(const QPointF &documentPoint)
{
return m_tool->calculatePerspective(documentPoint);
}
qreal KisToolFreehandPaintingInformationBuilder::canvasRotation() const
{
KisCanvas2 *canvas = dynamic_cast<KisCanvas2*>(m_tool->canvas());
return canvas->coordinatesConverter()->rotationAngle();
}
bool KisToolFreehandPaintingInformationBuilder::canvasMirroredX() const
{
KisCanvas2 *canvas = dynamic_cast<KisCanvas2*>(m_tool->canvas());
return canvas->coordinatesConverter()->xAxisMirrored();
}
bool KisToolFreehandPaintingInformationBuilder::canvasMirroredY() const
{
KisCanvas2 *canvas = dynamic_cast<KisCanvas2*>(m_tool->canvas());
return canvas->coordinatesConverter()->yAxisMirrored();
}
diff --git a/libs/ui/tool/kis_resources_snapshot.cpp b/libs/ui/tool/kis_resources_snapshot.cpp
index dcc797b033..6b5bf202fb 100644
--- a/libs/ui/tool/kis_resources_snapshot.cpp
+++ b/libs/ui/tool/kis_resources_snapshot.cpp
@@ -1,419 +1,427 @@
/*
* 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_resources_snapshot.h"
#include <KoColor.h>
#include <resources/KoAbstractGradient.h>
#include <KoCompositeOpRegistry.h>
#include <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_settings.h>
#include <brushengine/kis_paintop_registry.h>
#include <kis_threaded_text_rendering_workaround.h>
#include <resources/KoPattern.h>
#include "kis_canvas_resource_provider.h"
#include "filter/kis_filter_configuration.h"
#include "kis_image.h"
#include "kis_paint_device.h"
#include "kis_paint_layer.h"
#include "kis_selection.h"
#include "kis_selection_mask.h"
#include "kis_algebra_2d.h"
struct KisResourcesSnapshot::Private {
Private()
: currentPattern(0)
, currentGradient(0)
, currentGenerator(0)
, compositeOp(0)
{
}
KisImageSP image;
KisDefaultBoundsBaseSP bounds;
KoColor currentFgColor;
KoColor currentBgColor;
- KoPattern *currentPattern = 0;
- KoAbstractGradient *currentGradient;
+ KoPatternSP currentPattern;
+ KoAbstractGradientSP currentGradient;
KisPaintOpPresetSP currentPaintOpPreset;
KisNodeSP currentNode;
qreal currentExposure;
KisFilterConfigurationSP currentGenerator;
QPointF axesCenter;
bool mirrorMaskHorizontal = false;
bool mirrorMaskVertical = false;
quint8 opacity = OPACITY_OPAQUE_U8;
QString compositeOpId = COMPOSITE_OVER;
const KoCompositeOp *compositeOp;
KisPainter::StrokeStyle strokeStyle = KisPainter::StrokeStyleBrush;
KisPainter::FillStyle fillStyle = KisPainter::FillStyleForegroundColor;
bool globalAlphaLock = false;
qreal effectiveZoom = 1.0;
bool presetAllowsLod = false;
KisSelectionSP selectionOverride;
bool hasOverrideSelection = false;
};
KisResourcesSnapshot::KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KoCanvasResourceProvider *resourceManager, KisDefaultBoundsBaseSP bounds)
: m_d(new Private())
{
m_d->image = image;
if (!bounds) {
bounds = new KisDefaultBounds(m_d->image);
}
m_d->bounds = bounds;
m_d->currentFgColor = resourceManager->resource(KoCanvasResourceProvider::ForegroundColor).value<KoColor>();
m_d->currentBgColor = resourceManager->resource(KoCanvasResourceProvider::BackgroundColor).value<KoColor>();
- m_d->currentPattern = resourceManager->resource(KisCanvasResourceProvider::CurrentPattern).value<KoPattern*>();
- m_d->currentGradient = resourceManager->resource(KisCanvasResourceProvider::CurrentGradient).value<KoAbstractGradient*>();
+ m_d->currentPattern = resourceManager->resource(KisCanvasResourceProvider::CurrentPattern).value<KoPatternSP>();
+ m_d->currentGradient = resourceManager->resource(KisCanvasResourceProvider::CurrentGradient).value<KoAbstractGradientSP>();
/**
* We should deep-copy the preset, so that long-running actions
* will have correct brush parameters. Theoretically this cloning
* can be expensive, but according to measurements, it takes
* something like 0.1 ms for an average preset.
*/
KisPaintOpPresetSP p = resourceManager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
if (p) {
- m_d->currentPaintOpPreset = resourceManager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>()->clone();
+ m_d->currentPaintOpPreset = resourceManager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>()->cloneWithResourcesSnapshot();
}
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset);
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
m_d->currentExposure = resourceManager->resource(KisCanvasResourceProvider::HdrExposure).toDouble();
- m_d->currentGenerator = resourceManager->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value<KisFilterConfiguration*>();
+ m_d->currentGenerator = resourceManager->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value<KisFilterConfiguration*>();
+ if (m_d->currentGenerator) {
+ m_d->currentGenerator = m_d->currentGenerator->cloneWithResourcesSnapshot();
+ }
+
QPointF relativeAxesCenter(0.5, 0.5);
if (m_d->image) {
relativeAxesCenter = m_d->image->mirrorAxesCenter();
}
m_d->axesCenter = KisAlgebra2D::relativeToAbsolute(relativeAxesCenter, m_d->bounds->bounds());
m_d->mirrorMaskHorizontal = resourceManager->resource(KisCanvasResourceProvider::MirrorHorizontal).toBool();
m_d->mirrorMaskVertical = resourceManager->resource(KisCanvasResourceProvider::MirrorVertical).toBool();
qreal normOpacity = resourceManager->resource(KisCanvasResourceProvider::Opacity).toDouble();
m_d->opacity = quint8(normOpacity * OPACITY_OPAQUE_U8);
m_d->compositeOpId = resourceManager->resource(KisCanvasResourceProvider::CurrentEffectiveCompositeOp).toString();
setCurrentNode(currentNode);
/**
* Fill and Stroke styles are not a part of the resource manager
* so the tools should set them manually
* TODO: port stroke and fill styles to be a part
* of the resource manager
*/
m_d->strokeStyle = KisPainter::StrokeStyleBrush;
m_d->fillStyle = KisPainter::FillStyleNone;
m_d->globalAlphaLock = resourceManager->resource(KisCanvasResourceProvider::GlobalAlphaLock).toBool();
m_d->effectiveZoom = resourceManager->resource(KisCanvasResourceProvider::EffectiveZoom).toDouble();
m_d->presetAllowsLod = resourceManager->resource(KisCanvasResourceProvider::EffectiveLodAvailablility).toBool();
}
KisResourcesSnapshot::KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KisDefaultBoundsBaseSP bounds)
: m_d(new Private())
{
m_d->image = image;
if (!bounds) {
bounds = new KisDefaultBounds(m_d->image);
}
m_d->bounds = bounds;
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset);
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
QPointF relativeAxesCenter(0.5, 0.5);
if (m_d->image) {
relativeAxesCenter = m_d->image->mirrorAxesCenter();
}
m_d->axesCenter = KisAlgebra2D::relativeToAbsolute(relativeAxesCenter, m_d->bounds->bounds());
m_d->opacity = OPACITY_OPAQUE_U8;
setCurrentNode(currentNode);
/**
* Fill and Stroke styles are not a part of the resource manager
* so the tools should set them manually
* TODO: port stroke and fill styles to be a part
* of the resource manager
*/
m_d->strokeStyle = KisPainter::StrokeStyleBrush;
m_d->fillStyle = KisPainter::FillStyleNone;
}
KisResourcesSnapshot::~KisResourcesSnapshot()
{
delete m_d;
}
void KisResourcesSnapshot::setupPainter(KisPainter* painter)
{
painter->setPaintColor(m_d->currentFgColor);
painter->setBackgroundColor(m_d->currentBgColor);
painter->setGenerator(m_d->currentGenerator);
painter->setPattern(m_d->currentPattern);
painter->setGradient(m_d->currentGradient);
QBitArray lockflags = channelLockFlags();
if (lockflags.size() > 0) {
painter->setChannelFlags(lockflags);
}
painter->setOpacity(m_d->opacity);
painter->setCompositeOp(m_d->compositeOp);
painter->setMirrorInformation(m_d->axesCenter, m_d->mirrorMaskHorizontal, m_d->mirrorMaskVertical);
painter->setStrokeStyle(m_d->strokeStyle);
painter->setFillStyle(m_d->fillStyle);
/**
* The paintOp should be initialized the last, because it may
* ask the painter for some options while initialization
*/
painter->setPaintOpPreset(m_d->currentPaintOpPreset, m_d->currentNode, m_d->image);
}
void KisResourcesSnapshot::setupMaskingBrushPainter(KisPainter *painter)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(painter->device());
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->currentPaintOpPreset->hasMaskingPreset());
painter->setPaintColor(KoColor(Qt::white, painter->device()->colorSpace()));
painter->setBackgroundColor(KoColor(Qt::black, painter->device()->colorSpace()));
painter->setOpacity(OPACITY_OPAQUE_U8);
painter->setChannelFlags(QBitArray());
// masked brush always paints in indirect mode
painter->setCompositeOp(COMPOSITE_ALPHA_DARKEN);
painter->setMirrorInformation(m_d->axesCenter, m_d->mirrorMaskHorizontal, m_d->mirrorMaskVertical);
painter->setStrokeStyle(m_d->strokeStyle);
/**
* The paintOp should be initialized the last, because it may
* ask the painter for some options while initialization
*/
painter->setPaintOpPreset(m_d->currentPaintOpPreset->createMaskingPreset(),
m_d->currentNode, m_d->image);
}
KisPostExecutionUndoAdapter* KisResourcesSnapshot::postExecutionUndoAdapter() const
{
return m_d->image ? m_d->image->postExecutionUndoAdapter() : 0;
}
void KisResourcesSnapshot::setCurrentNode(KisNodeSP node)
{
m_d->currentNode = node;
KisPaintDeviceSP device;
if(m_d->currentNode && (device = m_d->currentNode->paintDevice())) {
m_d->compositeOp = device->colorSpace()->compositeOp(m_d->compositeOpId);
if(!m_d->compositeOp) {
m_d->compositeOp = device->colorSpace()->compositeOp(COMPOSITE_OVER);
}
}
}
void KisResourcesSnapshot::setStrokeStyle(KisPainter::StrokeStyle strokeStyle)
{
m_d->strokeStyle = strokeStyle;
}
void KisResourcesSnapshot::setFillStyle(KisPainter::FillStyle fillStyle)
{
m_d->fillStyle = fillStyle;
}
KisNodeSP KisResourcesSnapshot::currentNode() const
{
return m_d->currentNode;
}
KisImageSP KisResourcesSnapshot::image() const
{
return m_d->image;
}
bool KisResourcesSnapshot::needsIndirectPainting() const
{
return !m_d->currentPaintOpPreset->settings()->paintIncremental();
}
QString KisResourcesSnapshot::indirectPaintingCompositeOp() const
{
return m_d->currentPaintOpPreset ?
m_d->currentPaintOpPreset->settings()->indirectPaintingCompositeOp()
: COMPOSITE_ALPHA_DARKEN;
}
bool KisResourcesSnapshot::needsMaskingBrushRendering() const
{
return m_d->currentPaintOpPreset && m_d->currentPaintOpPreset->hasMaskingPreset();
}
KisSelectionSP KisResourcesSnapshot::activeSelection() const
{
/**
* It is possible to have/use the snapshot without the image. Such
* usecase is present for example in the scratchpad.
*/
if (m_d->hasOverrideSelection) {
return m_d->selectionOverride;
}
KisSelectionSP selection = m_d->image ? m_d->image->globalSelection() : 0;
KisLayerSP layer = qobject_cast<KisLayer*>(m_d->currentNode.data());
KisSelectionMaskSP mask;
if((layer = qobject_cast<KisLayer*>(m_d->currentNode.data()))) {
selection = layer->selection();
} else if ((mask = dynamic_cast<KisSelectionMask*>(m_d->currentNode.data())) &&
mask->selection() == selection) {
selection = 0;
}
return selection;
}
bool KisResourcesSnapshot::needsAirbrushing() const
{
return m_d->currentPaintOpPreset->settings()->isAirbrushing();
}
qreal KisResourcesSnapshot::airbrushingInterval() const
{
return m_d->currentPaintOpPreset->settings()->airbrushInterval();
}
bool KisResourcesSnapshot::needsSpacingUpdates() const
{
return m_d->currentPaintOpPreset->settings()->useSpacingUpdates();
}
void KisResourcesSnapshot::setOpacity(qreal opacity)
{
m_d->opacity = opacity * OPACITY_OPAQUE_U8;
}
quint8 KisResourcesSnapshot::opacity() const
{
return m_d->opacity;
}
const KoCompositeOp* KisResourcesSnapshot::compositeOp() const
{
return m_d->compositeOp;
}
QString KisResourcesSnapshot::compositeOpId() const
{
return m_d->compositeOpId;
}
-KoPattern* KisResourcesSnapshot::currentPattern() const
+KoPatternSP KisResourcesSnapshot::currentPattern() const
{
return m_d->currentPattern;
}
KoColor KisResourcesSnapshot::currentFgColor() const
{
return m_d->currentFgColor;
}
KoColor KisResourcesSnapshot::currentBgColor() const
{
return m_d->currentBgColor;
}
KisPaintOpPresetSP KisResourcesSnapshot::currentPaintOpPreset() const
{
return m_d->currentPaintOpPreset;
}
QBitArray KisResourcesSnapshot::channelLockFlags() const
{
QBitArray channelFlags;
KisPaintLayer *paintLayer;
if ((paintLayer = dynamic_cast<KisPaintLayer*>(m_d->currentNode.data()))) {
channelFlags = paintLayer->channelLockFlags();
if (m_d->globalAlphaLock) {
if (channelFlags.isEmpty()) {
channelFlags = paintLayer->colorSpace()->channelFlags(true, true);
}
channelFlags &= paintLayer->colorSpace()->channelFlags(true, false);
}
}
return channelFlags;
}
qreal KisResourcesSnapshot::effectiveZoom() const
{
return m_d->effectiveZoom;
}
bool KisResourcesSnapshot::presetAllowsLod() const
{
return m_d->presetAllowsLod;
}
bool KisResourcesSnapshot::presetNeedsAsynchronousUpdates() const
{
return m_d->currentPaintOpPreset && m_d->currentPaintOpPreset->settings()->needsAsynchronousUpdates();
}
void KisResourcesSnapshot::setFGColorOverride(const KoColor &color)
{
m_d->currentFgColor = color;
}
void KisResourcesSnapshot::setBGColorOverride(const KoColor &color)
{
m_d->currentBgColor = color;
}
void KisResourcesSnapshot::setSelectionOverride(KisSelectionSP selection)
{
m_d->selectionOverride = selection;
m_d->hasOverrideSelection = true; // needed if selection passed is null to ignore selection
}
void KisResourcesSnapshot::setBrush(const KisPaintOpPresetSP &brush)
{
- m_d->currentPaintOpPreset = brush;
+ m_d->currentPaintOpPreset = brush->cloneWithResourcesSnapshot();
+
+#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
+ KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset);
+#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
}
diff --git a/libs/ui/tool/kis_resources_snapshot.h b/libs/ui/tool/kis_resources_snapshot.h
index b2361657b6..4e8db07509 100644
--- a/libs/ui/tool/kis_resources_snapshot.h
+++ b/libs/ui/tool/kis_resources_snapshot.h
@@ -1,106 +1,106 @@
/*
* 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_RESOURCES_SNAPSHOT_H
#define __KIS_RESOURCES_SNAPSHOT_H
#include "kis_shared.h"
#include "kis_shared_ptr.h"
#include "kis_types.h"
#include "kritaui_export.h"
#include "kis_painter.h"
#include "kis_default_bounds.h"
class KoCanvasResourceProvider;
class KoCompositeOp;
class KisPainter;
class KisPostExecutionUndoAdapter;
class KoPattern;
/**
* @brief The KisResourcesSnapshot class takes a snapshot of the various resources
* like colors and settings used at the begin of a stroke so subsequent
* changes don't impact the running stroke. The main reason for the snapshot is that the
* user can *change* the options while the stroke is being executed in the background.
*/
class KRITAUI_EXPORT KisResourcesSnapshot : public KisShared
{
public:
KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KoCanvasResourceProvider *resourceManager, KisDefaultBoundsBaseSP bounds = 0);
KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KisDefaultBoundsBaseSP bounds = 0);
~KisResourcesSnapshot();
void setupPainter(KisPainter *painter);
void setupMaskingBrushPainter(KisPainter *painter);
KisPostExecutionUndoAdapter* postExecutionUndoAdapter() const;
void setCurrentNode(KisNodeSP node);
void setStrokeStyle(KisPainter::StrokeStyle strokeStyle);
void setFillStyle(KisPainter::FillStyle fillStyle);
KisNodeSP currentNode() const;
KisImageSP image() const;
bool needsIndirectPainting() const;
QString indirectPaintingCompositeOp() const;
bool needsMaskingBrushRendering() const;
/**
* \return currently active selection. Note that it will return
* null if current node *is* the current selection. This
* is done to avoid recursive selection application when
* painting on selectgion masks.
*/
KisSelectionSP activeSelection() const;
bool needsAirbrushing() const;
qreal airbrushingInterval() const;
bool needsSpacingUpdates() const;
void setOpacity(qreal opacity);
quint8 opacity() const;
const KoCompositeOp* compositeOp() const;
QString compositeOpId() const;
- KoPattern* currentPattern() const;
+ KoPatternSP currentPattern() const;
KoColor currentFgColor() const;
KoColor currentBgColor() const;
KisPaintOpPresetSP currentPaintOpPreset() const;
/// @return the channel lock flags of the current node with the global override applied
QBitArray channelLockFlags() const;
qreal effectiveZoom() const;
bool presetAllowsLod() const;
bool presetNeedsAsynchronousUpdates() const;
void setFGColorOverride(const KoColor &color);
void setBGColorOverride(const KoColor &color);
void setSelectionOverride(KisSelectionSP selection);
void setBrush(const KisPaintOpPresetSP &brush);
private:
struct Private;
Private * const m_d;
};
typedef KisSharedPtr<KisResourcesSnapshot> KisResourcesSnapshotSP;
#endif /* __KIS_RESOURCES_SNAPSHOT_H */
diff --git a/libs/ui/tool/kis_tool.cc b/libs/ui/tool/kis_tool.cc
index 93c9dc8b0e..8117262de2 100644
--- a/libs/ui/tool/kis_tool.cc
+++ b/libs/ui/tool/kis_tool.cc
@@ -1,683 +1,683 @@
/*
* Copyright (c) 2006, 2010 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_tool.h"
#include <QCursor>
#include <QLabel>
#include <QWidget>
#include <QPolygonF>
#include <QTransform>
#include <klocalizedstring.h>
#include <QAction>
#include <kactioncollection.h>
#include <kis_icon.h>
#include <KoConfig.h>
#include <KoColorSpaceRegistry.h>
#include <KoColor.h>
#include <KoCanvasBase.h>
#include <KoCanvasController.h>
#include <KoToolBase.h>
#include <KoID.h>
#include <KoPointerEvent.h>
#include <KoViewConverter.h>
#include <KoSelection.h>
#include <resources/KoAbstractGradient.h>
#include <KoSnapGuide.h>
#include <KisViewManager.h>
#include "kis_node_manager.h"
#include <kis_selection.h>
#include <kis_image.h>
#include <kis_group_layer.h>
#include <kis_adjustment_layer.h>
#include <kis_mask.h>
#include <kis_paint_layer.h>
#include <kis_painter.h>
#include <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_settings.h>
#include <resources/KoPattern.h>
#include <kis_floating_message.h>
#include "opengl/kis_opengl_canvas2.h"
#include "kis_canvas_resource_provider.h"
#include "canvas/kis_canvas2.h"
#include "kis_coordinates_converter.h"
#include "filter/kis_filter_configuration.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_cursor.h"
#include <kis_selection_mask.h>
#include "kis_resources_snapshot.h"
#include <KisView.h>
#include "kis_action_registry.h"
#include "kis_tool_utils.h"
struct Q_DECL_HIDDEN KisTool::Private {
QCursor cursor; // the cursor that should be shown on tool activation.
// From the canvas resources
- KoPattern* currentPattern{0};
- KoAbstractGradient* currentGradient{0};
+ KoPatternSP currentPattern;
+ KoAbstractGradientSP currentGradient;
KoColor currentFgColor;
KoColor currentBgColor;
float currentExposure{1.0};
KisFilterConfigurationSP currentGenerator;
QWidget* optionWidget{0};
ToolMode m_mode{HOVER_MODE};
bool m_isActive{false};
};
KisTool::KisTool(KoCanvasBase * canvas, const QCursor & cursor)
: KoToolBase(canvas)
, d(new Private)
{
d->cursor = cursor;
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle()));
connect(this, SIGNAL(isActiveChanged(bool)), SLOT(resetCursorStyle()));
}
KisTool::~KisTool()
{
delete d;
}
void KisTool::activate(ToolActivation activation, const QSet<KoShape*> &shapes)
{
KoToolBase::activate(activation, shapes);
resetCursorStyle();
if (!canvas()) return;
if (!canvas()->resourceManager()) return;
d->currentFgColor = canvas()->resourceManager()->resource(KoCanvasResourceProvider::ForegroundColor).value<KoColor>();
d->currentBgColor = canvas()->resourceManager()->resource(KoCanvasResourceProvider::BackgroundColor).value<KoColor>();
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentPattern)) {
- d->currentPattern = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPattern).value<KoPattern*>();
+ d->currentPattern = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPattern).value<KoPatternSP>();
}
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGradient)) {
- d->currentGradient = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGradient).value<KoAbstractGradient*>();
+ d->currentGradient = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGradient).value<KoAbstractGradientSP>();
}
KisPaintOpPresetSP preset = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
if (preset && preset->settings()) {
preset->settings()->activate();
}
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::HdrExposure)) {
d->currentExposure = static_cast<float>(canvas()->resourceManager()->resource(KisCanvasResourceProvider::HdrExposure).toDouble());
}
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGeneratorConfiguration)) {
d->currentGenerator = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value<KisFilterConfiguration*>();
}
d->m_isActive = true;
emit isActiveChanged(true);
}
void KisTool::deactivate()
{
d->m_isActive = false;
emit isActiveChanged(false);
KoToolBase::deactivate();
}
void KisTool::canvasResourceChanged(int key, const QVariant & v)
{
QString formattedBrushName;
if (key == KisCanvasResourceProvider::CurrentPaintOpPreset) {
formattedBrushName = v.value<KisPaintOpPresetSP>()->name().replace("_", " ");
}
switch (key) {
case(KoCanvasResourceProvider::ForegroundColor):
d->currentFgColor = v.value<KoColor>();
break;
case(KoCanvasResourceProvider::BackgroundColor):
d->currentBgColor = v.value<KoColor>();
break;
case(KisCanvasResourceProvider::CurrentPattern):
- d->currentPattern = v.value<KoPattern*>();
+ d->currentPattern = v.value<KoPatternSP>();
break;
case(KisCanvasResourceProvider::CurrentGradient):
- d->currentGradient = static_cast<KoAbstractGradient *>(v.value<void *>());
+ d->currentGradient = v.value<KoAbstractGradientSP>();
break;
case(KisCanvasResourceProvider::HdrExposure):
d->currentExposure = static_cast<float>(v.toDouble());
break;
case(KisCanvasResourceProvider::CurrentGeneratorConfiguration):
d->currentGenerator = static_cast<KisFilterConfiguration*>(v.value<void *>());
break;
case(KisCanvasResourceProvider::CurrentPaintOpPreset):
emit statusTextChanged(formattedBrushName);
break;
case(KisCanvasResourceProvider::CurrentKritaNode):
resetCursorStyle();
break;
default:
break; // Do nothing
};
}
void KisTool::updateSettingsViews()
{
}
QPointF KisTool::widgetCenterInWidgetPixels()
{
KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas());
Q_ASSERT(kritaCanvas);
const KisCoordinatesConverter *converter = kritaCanvas->coordinatesConverter();
return converter->flakeToWidget(converter->flakeCenterPoint());
}
QPointF KisTool::convertDocumentToWidget(const QPointF& pt)
{
KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas());
Q_ASSERT(kritaCanvas);
return kritaCanvas->coordinatesConverter()->documentToWidget(pt);
}
QPointF KisTool::convertToPixelCoord(KoPointerEvent *e)
{
if (!image())
return e->point;
return image()->documentToPixel(e->point);
}
QPointF KisTool::convertToPixelCoord(const QPointF& pt)
{
if (!image())
return pt;
return image()->documentToPixel(pt);
}
QPointF KisTool::convertToPixelCoordAndAlignOnWidget(const QPointF &pt)
{
KisCanvas2 *canvas2 = dynamic_cast<KisCanvas2 *>(canvas());
const KisCoordinatesConverter *converter = canvas2->coordinatesConverter();
const QPointF imagePos = converter->widgetToImage(QPointF(converter->documentToWidget(pt).toPoint()));
return imagePos;
}
QPointF KisTool::convertToPixelCoordAndSnap(KoPointerEvent *e, const QPointF &offset, bool useModifiers)
{
if (!image())
return e->point;
KoSnapGuide *snapGuide = canvas()->snapGuide();
QPointF pos = snapGuide->snap(e->point, offset, useModifiers ? e->modifiers() : Qt::NoModifier);
return image()->documentToPixel(pos);
}
QPointF KisTool::convertToPixelCoordAndSnap(const QPointF& pt, const QPointF &offset)
{
if (!image())
return pt;
KoSnapGuide *snapGuide = canvas()->snapGuide();
QPointF pos = snapGuide->snap(pt, offset, Qt::NoModifier);
return image()->documentToPixel(pos);
}
QPoint KisTool::convertToImagePixelCoordFloored(KoPointerEvent *e)
{
if (!image())
return e->point.toPoint();
return image()->documentToImagePixelFloored(e->point);
}
QPointF KisTool::viewToPixel(const QPointF &viewCoord) const
{
if (!image())
return viewCoord;
return image()->documentToPixel(canvas()->viewConverter()->viewToDocument(viewCoord));
}
QRectF KisTool::convertToPt(const QRectF &rect)
{
if (!image())
return rect;
QRectF r;
//We add 1 in the following to the extreme coords because a pixel always has size
r.setCoords(int(rect.left()) / image()->xRes(), int(rect.top()) / image()->yRes(),
int(rect.right()) / image()->xRes(), int( rect.bottom()) / image()->yRes());
return r;
}
qreal KisTool::convertToPt(qreal value)
{
const qreal avgResolution = 0.5 * (image()->xRes() + image()->yRes());
return value / avgResolution;
}
QPointF KisTool::pixelToView(const QPoint &pixelCoord) const
{
if (!image())
return pixelCoord;
QPointF documentCoord = image()->pixelToDocument(pixelCoord);
return canvas()->viewConverter()->documentToView(documentCoord);
}
QPointF KisTool::pixelToView(const QPointF &pixelCoord) const
{
if (!image())
return pixelCoord;
QPointF documentCoord = image()->pixelToDocument(pixelCoord);
return canvas()->viewConverter()->documentToView(documentCoord);
}
QRectF KisTool::pixelToView(const QRectF &pixelRect) const
{
if (!image()) {
return pixelRect;
}
QPointF topLeft = pixelToView(pixelRect.topLeft());
QPointF bottomRight = pixelToView(pixelRect.bottomRight());
return {topLeft, bottomRight};
}
QPainterPath KisTool::pixelToView(const QPainterPath &pixelPolygon) const
{
QTransform matrix;
qreal zoomX, zoomY;
canvas()->viewConverter()->zoom(&zoomX, &zoomY);
matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes());
return matrix.map(pixelPolygon);
}
QPolygonF KisTool::pixelToView(const QPolygonF &pixelPath) const
{
QTransform matrix;
qreal zoomX, zoomY;
canvas()->viewConverter()->zoom(&zoomX, &zoomY);
matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes());
return matrix.map(pixelPath);
}
void KisTool::updateCanvasPixelRect(const QRectF &pixelRect)
{
canvas()->updateCanvas(convertToPt(pixelRect));
}
void KisTool::updateCanvasViewRect(const QRectF &viewRect)
{
canvas()->updateCanvas(canvas()->viewConverter()->viewToDocument(viewRect));
}
KisImageWSP KisTool::image() const
{
// For now, krita tools only work in krita, not for a krita shape. Krita shapes are for 2.1
KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
if (kisCanvas) {
return kisCanvas->currentImage();
}
return 0;
}
QCursor KisTool::cursor() const
{
return d->cursor;
}
void KisTool::notifyModified() const
{
if (image()) {
image()->setModified();
}
}
-KoPattern * KisTool::currentPattern()
+KoPatternSP KisTool::currentPattern()
{
return d->currentPattern;
}
-KoAbstractGradient * KisTool::currentGradient()
+KoAbstractGradientSP KisTool::currentGradient()
{
return d->currentGradient;
}
KisPaintOpPresetSP KisTool::currentPaintOpPreset()
{
return canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
}
KisNodeSP KisTool::currentNode() const
{
KisNodeSP node = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value<KisNodeWSP>();
return node;
}
KisNodeList KisTool::selectedNodes() const
{
KisCanvas2 * kiscanvas = static_cast<KisCanvas2*>(canvas());
KisViewManager* viewManager = kiscanvas->viewManager();
return viewManager->nodeManager()->selectedNodes();
}
KoColor KisTool::currentFgColor()
{
return d->currentFgColor;
}
KoColor KisTool::currentBgColor()
{
return d->currentBgColor;
}
KisImageWSP KisTool::currentImage()
{
return image();
}
KisFilterConfigurationSP KisTool::currentGenerator()
{
return d->currentGenerator;
}
void KisTool::setMode(ToolMode mode) {
d->m_mode = mode;
}
KisTool::ToolMode KisTool::mode() const {
return d->m_mode;
}
void KisTool::setCursor(const QCursor &cursor)
{
d->cursor = cursor;
}
KisTool::AlternateAction KisTool::actionToAlternateAction(ToolAction action) {
KIS_ASSERT_RECOVER_RETURN_VALUE(action != Primary, Secondary);
return (AlternateAction)action;
}
void KisTool::activatePrimaryAction()
{
resetCursorStyle();
}
void KisTool::deactivatePrimaryAction()
{
resetCursorStyle();
}
void KisTool::beginPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::beginPrimaryDoubleClickAction(KoPointerEvent *event)
{
beginPrimaryAction(event);
}
void KisTool::continuePrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::endPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
}
bool KisTool::primaryActionSupportsHiResEvents() const
{
return false;
}
void KisTool::activateAlternateAction(AlternateAction action)
{
Q_UNUSED(action);
}
void KisTool::deactivateAlternateAction(AlternateAction action)
{
Q_UNUSED(action);
}
void KisTool::beginAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(event);
Q_UNUSED(action);
}
void KisTool::beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action)
{
beginAlternateAction(event, action);
}
void KisTool::continueAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(event);
Q_UNUSED(action);
}
void KisTool::endAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(event);
Q_UNUSED(action);
}
void KisTool::mouseDoubleClickEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::mouseTripleClickEvent(KoPointerEvent *event)
{
mouseDoubleClickEvent(event);
}
void KisTool::mousePressEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::mouseReleaseEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::mouseMoveEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::deleteSelection()
{
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager());
if (!blockUntilOperationsFinished()) {
return;
}
if (!KisToolUtils::clearImage(image(), resources->currentNode(), resources->activeSelection())) {
KoToolBase::deleteSelection();
}
}
KisTool::NodePaintAbility KisTool::nodePaintAbility()
{
KisNodeSP node = currentNode();
if (!node) {
return NodePaintAbility::UNPAINTABLE;
}
if (node->inherits("KisShapeLayer")) {
return NodePaintAbility::VECTOR;
}
if (node->inherits("KisCloneLayer")) {
return NodePaintAbility::CLONE;
}
if (node->paintDevice()) {
return NodePaintAbility::PAINT;
}
return NodePaintAbility::UNPAINTABLE;
}
QWidget* KisTool::createOptionWidget()
{
d->optionWidget = new QLabel(i18n("No options"));
d->optionWidget->setObjectName("SpecialSpacer");
return d->optionWidget;
}
#define NEAR_VAL -1000.0
#define FAR_VAL 1000.0
#define PROGRAM_VERTEX_ATTRIBUTE 0
void KisTool::paintToolOutline(QPainter* painter, const QPainterPath &path)
{
KisOpenGLCanvas2 *canvasWidget = dynamic_cast<KisOpenGLCanvas2 *>(canvas()->canvasWidget());
if (canvasWidget) {
painter->beginNativePainting();
canvasWidget->paintToolOutline(path);
painter->endNativePainting();
}
else {
painter->save();
painter->setCompositionMode(QPainter::RasterOp_SourceXorDestination);
painter->setPen(QColor(128, 255, 128));
painter->drawPath(path);
painter->restore();
}
}
void KisTool::resetCursorStyle()
{
useCursor(d->cursor);
}
bool KisTool::overrideCursorIfNotEditable()
{
// override cursor for canvas iff this tool is active
// and we can't paint on the active layer
if (isActive()) {
KisNodeSP node = currentNode();
if (node && !node->isEditable()) {
canvas()->setCursor(Qt::ForbiddenCursor);
return true;
}
}
return false;
}
bool KisTool::blockUntilOperationsFinished()
{
KisCanvas2 * kiscanvas = static_cast<KisCanvas2*>(canvas());
KisViewManager* viewManager = kiscanvas->viewManager();
return viewManager->blockUntilOperationsFinished(image());
}
void KisTool::blockUntilOperationsFinishedForced()
{
KisCanvas2 * kiscanvas = static_cast<KisCanvas2*>(canvas());
KisViewManager* viewManager = kiscanvas->viewManager();
viewManager->blockUntilOperationsFinishedForced(image());
}
bool KisTool::isActive() const
{
return d->m_isActive;
}
bool KisTool::nodeEditable()
{
KisNodeSP node = currentNode();
if (!node) {
return false;
}
bool blockedNoIndirectPainting = false;
const bool presetUsesIndirectPainting =
!currentPaintOpPreset()->settings()->paintIncremental();
if (!presetUsesIndirectPainting) {
const KisIndirectPaintingSupport *indirectPaintingLayer =
dynamic_cast<const KisIndirectPaintingSupport*>(node.data());
if (indirectPaintingLayer) {
blockedNoIndirectPainting = !indirectPaintingLayer->supportsNonIndirectPainting();
}
}
bool nodeEditable = node->isEditable() && !blockedNoIndirectPainting;
if (!nodeEditable) {
KisCanvas2 * kiscanvas = static_cast<KisCanvas2*>(canvas());
QString message;
if (!node->visible() && node->userLocked()) {
message = i18n("Layer is locked and invisible.");
} else if (node->userLocked()) {
message = i18n("Layer is locked.");
} else if(!node->visible()) {
message = i18n("Layer is invisible.");
} else if (blockedNoIndirectPainting) {
message = i18n("Layer can be painted in Wash Mode only.");
} else {
message = i18n("Group not editable.");
}
kiscanvas->viewManager()->showFloatingMessage(message, KisIconUtils::loadIcon("object-locked"));
}
return nodeEditable;
}
bool KisTool::selectionEditable()
{
KisCanvas2 * kisCanvas = static_cast<KisCanvas2*>(canvas());
KisViewManager * view = kisCanvas->viewManager();
bool editable = view->selectionEditable();
if (!editable) {
KisCanvas2 * kiscanvas = static_cast<KisCanvas2*>(canvas());
kiscanvas->viewManager()->showFloatingMessage(i18n("Local selection is locked."), KisIconUtils::loadIcon("object-locked"));
}
return editable;
}
void KisTool::listenToModifiers(bool listen)
{
Q_UNUSED(listen);
}
bool KisTool::listeningToModifiers()
{
return false;
}
diff --git a/libs/ui/tool/kis_tool.h b/libs/ui/tool/kis_tool.h
index 5ab56a068e..85f336661c 100644
--- a/libs/ui/tool/kis_tool.h
+++ b/libs/ui/tool/kis_tool.h
@@ -1,331 +1,331 @@
/*
* 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 <KoPattern.h>
+#include <KoAbstractGradient.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
+#include <kritaui_export.h>
+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 to revert all the preparations it has done 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
};
enum NodePaintAbility {
VECTOR,
CLONE,
PAINT,
UNPAINTABLE
};
Q_ENUMS(NodePaintAbility)
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;
KisTool::NodePaintAbility nodePaintAbility();
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 convertToPixelCoordAndAlignOnWidget(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();
+ KoPatternSP currentPattern();
+ KoAbstractGradientSP 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: int {
HOVER_MODE,
PAINT_MODE,
SECONDARY_PAINT_MODE,
MIRROR_AXIS_SETUP_MODE,
GESTURE_MODE,
PAN_MODE,
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.cpp b/libs/ui/tool/kis_tool_freehand_helper.cpp
index 75b305f9d5..961a6a9249 100644
--- a/libs/ui/tool/kis_tool_freehand_helper.cpp
+++ b/libs/ui/tool/kis_tool_freehand_helper.cpp
@@ -1,977 +1,978 @@
/*
* 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_freehand_helper.h"
#include <QTimer>
+#include <QElapsedTimer>
#include <QQueue>
#include <klocalizedstring.h>
#include <KoPointerEvent.h>
#include <KoCanvasResourceProvider.h>
#include "kis_algebra_2d.h"
#include "kis_distance_information.h"
#include "kis_painting_information_builder.h"
#include "kis_image.h"
#include "kis_painter.h"
#include <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_utils.h>
#include "kis_update_time_monitor.h"
#include "kis_stabilized_events_sampler.h"
#include "KisStabilizerDelayedPaintHelper.h"
#include "kis_config.h"
#include "kis_random_source.h"
#include "KisPerStrokeRandomSource.h"
#include "strokes/freehand_stroke.h"
#include "strokes/KisFreehandStrokeInfo.h"
#include "KisAsyncronousStrokeUpdateHelper.h"
#include "kis_canvas_resource_provider.h"
#include <math.h>
//#define DEBUG_BEZIER_CURVES
// Factor by which to scale the airbrush timer's interval, relative to the actual airbrushing rate.
// Setting this less than 1 makes the timer-generated pseudo-events happen faster than the desired
// airbrush rate, which can improve responsiveness.
const qreal AIRBRUSH_INTERVAL_FACTOR = 0.5;
// The amount of time, in milliseconds, to allow between updates of the spacing information. Only
// used when spacing updates between dabs are enabled.
const qreal SPACING_UPDATE_INTERVAL = 50.0;
// The amount of time, in milliseconds, to allow between updates of the timing information. Only
// used when airbrushing.
const qreal TIMING_UPDATE_INTERVAL = 50.0;
struct KisToolFreehandHelper::Private
{
KoCanvasResourceProvider *resourceManager;
KisPaintingInformationBuilder *infoBuilder;
KisStrokesFacade *strokesFacade;
KisAsyncronousStrokeUpdateHelper asyncUpdateHelper;
KUndo2MagicString transactionText;
bool haveTangent;
QPointF previousTangent;
bool hasPaintAtLeastOnce;
- QTime strokeTime;
+ QElapsedTimer strokeTime;
QTimer strokeTimeoutTimer;
QVector<KisFreehandStrokeInfo*> strokeInfos;
KisResourcesSnapshotSP resources;
KisStrokeId strokeId;
KisPaintInformation previousPaintInformation;
KisPaintInformation olderPaintInformation;
KisSmoothingOptionsSP smoothingOptions;
// fake random sources for hovering outline *only*
KisRandomSourceSP fakeDabRandomSource;
KisPerStrokeRandomSourceSP fakeStrokeRandomSource;
// Timer used to generate paint updates periodically even without input events. This is only
// used for paintops that depend on timely updates even when the cursor is not moving, e.g. for
// airbrushing effects.
QTimer airbrushingTimer;
QList<KisPaintInformation> history;
QList<qreal> distanceHistory;
// Keeps track of past cursor positions. This is used to determine the drawing angle when
// drawing the brush outline or starting a stroke.
KisPaintOpUtils::PositionHistory lastCursorPos;
// Stabilizer data
bool usingStabilizer;
QQueue<KisPaintInformation> stabilizerDeque;
QTimer stabilizerPollTimer;
KisStabilizedEventsSampler stabilizedSampler;
KisStabilizerDelayedPaintHelper stabilizerDelayedPaintHelper;
qreal effectiveSmoothnessDistance() const;
};
KisToolFreehandHelper::KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder,
KoCanvasResourceProvider *resourceManager,
const KUndo2MagicString &transactionText,
KisSmoothingOptions *smoothingOptions)
: m_d(new Private())
{
m_d->resourceManager = resourceManager;
m_d->infoBuilder = infoBuilder;
m_d->transactionText = transactionText;
m_d->smoothingOptions = KisSmoothingOptionsSP(
smoothingOptions ? smoothingOptions : new KisSmoothingOptions());
m_d->fakeDabRandomSource = new KisRandomSource();
m_d->fakeStrokeRandomSource = new KisPerStrokeRandomSource();
m_d->strokeTimeoutTimer.setSingleShot(true);
connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke()));
connect(&m_d->airbrushingTimer, SIGNAL(timeout()), SLOT(doAirbrushing()));
connect(&m_d->stabilizerPollTimer, SIGNAL(timeout()), SLOT(stabilizerPollAndPaint()));
connect(m_d->smoothingOptions.data(), SIGNAL(sigSmoothingTypeChanged()), SLOT(slotSmoothingTypeChanged()));
m_d->stabilizerDelayedPaintHelper.setPaintLineCallback(
[this](const KisPaintInformation &pi1, const KisPaintInformation &pi2) {
paintLine(pi1, pi2);
});
m_d->stabilizerDelayedPaintHelper.setUpdateOutlineCallback(
[this]() {
emit requestExplicitUpdateOutline();
});
}
KisToolFreehandHelper::~KisToolFreehandHelper()
{
delete m_d;
}
void KisToolFreehandHelper::setSmoothness(KisSmoothingOptionsSP smoothingOptions)
{
m_d->smoothingOptions = smoothingOptions;
}
KisSmoothingOptionsSP KisToolFreehandHelper::smoothingOptions() const
{
return m_d->smoothingOptions;
}
QPainterPath KisToolFreehandHelper::paintOpOutline(const QPointF &savedCursorPos,
const KoPointerEvent *event,
const KisPaintOpSettingsSP globalSettings,
KisPaintOpSettings::OutlineMode mode) const
{
KisPaintOpSettingsSP settings = globalSettings;
KisPaintInformation info = m_d->infoBuilder->hover(savedCursorPos, event);
QPointF prevPoint = m_d->lastCursorPos.pushThroughHistory(savedCursorPos, currentZoom());
qreal startAngle = KisAlgebra2D::directionBetweenPoints(prevPoint, savedCursorPos, 0);
KisDistanceInformation distanceInfo(prevPoint, startAngle);
if (!m_d->strokeInfos.isEmpty()) {
settings = m_d->resources->currentPaintOpPreset()->settings();
if (m_d->stabilizerDelayedPaintHelper.running() &&
m_d->stabilizerDelayedPaintHelper.hasLastPaintInformation()) {
info = m_d->stabilizerDelayedPaintHelper.lastPaintInformation();
} else {
info = m_d->previousPaintInformation;
}
/**
* When LoD mode is active it may happen that the helper has
* already started a stroke, but it painted noting, because
* all the work is being calculated by the scaled-down LodN
* stroke. So at first we try to fetch the data from the lodN
* stroke ("buddy") and then check if there is at least
* something has been painted with this distance information
* object.
*/
KisDistanceInformation *buddyDistance =
m_d->strokeInfos.first()->buddyDragDistance();
if (buddyDistance) {
/**
* Tiny hack alert: here we fetch the distance information
* directly from the LodN stroke. Ideally, we should
* upscale its data, but here we just override it with our
* local copy of the coordinates.
*/
distanceInfo = *buddyDistance;
distanceInfo.overrideLastValues(prevPoint, startAngle);
} else if (m_d->strokeInfos.first()->dragDistance->isStarted()) {
distanceInfo = *m_d->strokeInfos.first()->dragDistance;
}
}
KisPaintInformation::DistanceInformationRegistrar registrar =
info.registerDistanceInformation(&distanceInfo);
info.setRandomSource(m_d->fakeDabRandomSource);
info.setPerStrokeRandomSource(m_d->fakeStrokeRandomSource);
QPainterPath outline = settings->brushOutline(info, mode, currentZoom());
if (m_d->resources &&
m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER &&
m_d->smoothingOptions->useDelayDistance()) {
const qreal R = m_d->smoothingOptions->delayDistance() /
m_d->resources->effectiveZoom();
outline.addEllipse(info.pos(), R, R);
}
return outline;
}
void KisToolFreehandHelper::cursorMoved(const QPointF &cursorPos)
{
m_d->lastCursorPos.pushThroughHistory(cursorPos, currentZoom());
}
void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
const QPointF &pixelCoords,
KisImageWSP image, KisNodeSP currentNode,
KisStrokesFacade *strokesFacade,
KisNodeSP overrideNode,
KisDefaultBoundsBaseSP bounds)
{
QPointF prevPoint = m_d->lastCursorPos.pushThroughHistory(pixelCoords, currentZoom());
m_d->strokeTime.start();
KisPaintInformation pi =
m_d->infoBuilder->startStroke(event, elapsedStrokeTime(), m_d->resourceManager);
qreal startAngle = KisAlgebra2D::directionBetweenPoints(prevPoint, pixelCoords, 0.0);
initPaintImpl(startAngle,
pi,
m_d->resourceManager,
image,
currentNode,
strokesFacade,
overrideNode,
bounds);
}
bool KisToolFreehandHelper::isRunning() const
{
return m_d->strokeId;
}
void KisToolFreehandHelper::initPaintImpl(qreal startAngle,
const KisPaintInformation &pi,
KoCanvasResourceProvider *resourceManager,
KisImageWSP image,
KisNodeSP currentNode,
KisStrokesFacade *strokesFacade,
KisNodeSP overrideNode,
KisDefaultBoundsBaseSP bounds)
{
m_d->strokesFacade = strokesFacade;
m_d->haveTangent = false;
m_d->previousTangent = QPointF();
m_d->hasPaintAtLeastOnce = false;
m_d->previousPaintInformation = pi;
m_d->resources = new KisResourcesSnapshot(image,
currentNode,
resourceManager,
bounds);
if(overrideNode) {
m_d->resources->setCurrentNode(overrideNode);
}
const bool airbrushing = m_d->resources->needsAirbrushing();
const bool useSpacingUpdates = m_d->resources->needsSpacingUpdates();
KisDistanceInitInfo startDistInfo(m_d->previousPaintInformation.pos(),
startAngle,
useSpacingUpdates ? SPACING_UPDATE_INTERVAL : LONG_TIME,
airbrushing ? TIMING_UPDATE_INTERVAL : LONG_TIME,
0);
KisDistanceInformation startDist = startDistInfo.makeDistInfo();
createPainters(m_d->strokeInfos,
startDist);
KisStrokeStrategy *stroke =
new FreehandStrokeStrategy(m_d->resources, m_d->strokeInfos, m_d->transactionText);
m_d->strokeId = m_d->strokesFacade->startStroke(stroke);
m_d->history.clear();
m_d->distanceHistory.clear();
if (airbrushing) {
m_d->airbrushingTimer.setInterval(computeAirbrushTimerInterval());
m_d->airbrushingTimer.start();
} else if (m_d->resources->presetNeedsAsynchronousUpdates()) {
m_d->asyncUpdateHelper.startUpdateStream(m_d->strokesFacade, m_d->strokeId);
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
stabilizerStart(m_d->previousPaintInformation);
}
// If airbrushing, paint an initial dab immediately. This is a workaround for an issue where
// some paintops (Dyna, Particle, Sketch) might never initialize their spacing/timing
// information until paintAt is called.
if (airbrushing) {
paintAt(pi);
}
}
KoCanvasResourceProvider *KisToolFreehandHelper::resourceManager() const
{
return m_d->resourceManager;
}
void KisToolFreehandHelper::paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2,
QPointF tangent1, QPointF tangent2)
{
if (tangent1.isNull() || tangent2.isNull()) return;
const qreal maxSanePoint = 1e6;
QPointF controlTarget1;
QPointF controlTarget2;
// Shows the direction in which control points go
QPointF controlDirection1 = pi1.pos() + tangent1;
QPointF controlDirection2 = pi2.pos() - tangent2;
// Lines in the direction of the control points
QLineF line1(pi1.pos(), controlDirection1);
QLineF line2(pi2.pos(), controlDirection2);
// Lines to check whether the control points lay on the opposite
// side of the line
QLineF line3(controlDirection1, controlDirection2);
QLineF line4(pi1.pos(), pi2.pos());
QPointF intersection;
if (line3.intersect(line4, &intersection) == QLineF::BoundedIntersection) {
qreal controlLength = line4.length() / 2;
line1.setLength(controlLength);
line2.setLength(controlLength);
controlTarget1 = line1.p2();
controlTarget2 = line2.p2();
} else {
QLineF::IntersectType type = line1.intersect(line2, &intersection);
if (type == QLineF::NoIntersection ||
intersection.manhattanLength() > maxSanePoint) {
intersection = 0.5 * (pi1.pos() + pi2.pos());
// dbgKrita << "WARNING: there is no intersection point "
// << "in the basic smoothing algorithms";
}
controlTarget1 = intersection;
controlTarget2 = intersection;
}
// shows how near to the controlTarget the value raises
qreal coeff = 0.8;
qreal velocity1 = QLineF(QPointF(), tangent1).length();
qreal velocity2 = QLineF(QPointF(), tangent2).length();
if (velocity1 == 0.0 || velocity2 == 0.0) {
velocity1 = 1e-6;
velocity2 = 1e-6;
warnKrita << "WARNING: Basic Smoothing: Velocity is Zero! Please report a bug:" << ppVar(velocity1) << ppVar(velocity2);
}
qreal similarity = qMin(velocity1/velocity2, velocity2/velocity1);
// the controls should not differ more than 50%
similarity = qMax(similarity, qreal(0.5));
// when the controls are symmetric, their size should be smaller
// to avoid corner-like curves
coeff *= 1 - qMax(qreal(0.0), similarity - qreal(0.8));
Q_ASSERT(coeff > 0);
QPointF control1;
QPointF control2;
if (velocity1 > velocity2) {
control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
coeff *= similarity;
control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
} else {
control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
coeff *= similarity;
control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
}
paintBezierCurve(pi1,
control1,
control2,
pi2);
}
qreal KisToolFreehandHelper::Private::effectiveSmoothnessDistance() const
{
const qreal effectiveSmoothnessDistance =
!smoothingOptions->useScalableDistance() ?
smoothingOptions->smoothnessDistance() :
smoothingOptions->smoothnessDistance() / resources->effectiveZoom();
return effectiveSmoothnessDistance;
}
void KisToolFreehandHelper::paintEvent(KoPointerEvent *event)
{
KisPaintInformation info =
m_d->infoBuilder->continueStroke(event,
elapsedStrokeTime());
KisUpdateTimeMonitor::instance()->reportMouseMove(info.pos());
paint(info);
}
void KisToolFreehandHelper::paint(KisPaintInformation &info)
{
/**
* Smooth the coordinates out using the history and the
* distance. This is a heavily modified version of an algo used in
* Gimp and described in https://bugs.kde.org/show_bug.cgi?id=281267 and
* https://w.atwiki.jp/sigetch_2007/pages/17.html. The main
* differences are:
*
* 1) It uses 'distance' instead of 'velocity', since time
* measurements are too unstable in realworld environment
*
* 2) There is no 'Quality' parameter, since the number of samples
* is calculated automatically
*
* 3) 'Tail Aggressiveness' is used for controlling the end of the
* stroke
*
* 4) The formila is a little bit different: 'Distance' parameter
* stands for $3 \Sigma$
*/
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING
&& m_d->smoothingOptions->smoothnessDistance() > 0.0) {
{ // initialize current distance
QPointF prevPos;
if (!m_d->history.isEmpty()) {
const KisPaintInformation &prevPi = m_d->history.last();
prevPos = prevPi.pos();
} else {
prevPos = m_d->previousPaintInformation.pos();
}
qreal currentDistance = QVector2D(info.pos() - prevPos).length();
m_d->distanceHistory.append(currentDistance);
}
m_d->history.append(info);
qreal x = 0.0;
qreal y = 0.0;
if (m_d->history.size() > 3) {
const qreal sigma = m_d->effectiveSmoothnessDistance() / 3.0; // '3.0' for (3 * sigma) range
qreal gaussianWeight = 1 / (sqrt(2 * M_PI) * sigma);
qreal gaussianWeight2 = sigma * sigma;
qreal distanceSum = 0.0;
qreal scaleSum = 0.0;
qreal pressure = 0.0;
qreal baseRate = 0.0;
Q_ASSERT(m_d->history.size() == m_d->distanceHistory.size());
for (int i = m_d->history.size() - 1; i >= 0; i--) {
qreal rate = 0.0;
const KisPaintInformation nextInfo = m_d->history.at(i);
double distance = m_d->distanceHistory.at(i);
Q_ASSERT(distance >= 0.0);
qreal pressureGrad = 0.0;
if (i < m_d->history.size() - 1) {
pressureGrad = nextInfo.pressure() - m_d->history.at(i + 1).pressure();
const qreal tailAgressiveness = 40.0 * m_d->smoothingOptions->tailAggressiveness();
if (pressureGrad > 0.0 ) {
pressureGrad *= tailAgressiveness * (1.0 - nextInfo.pressure());
distance += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region
}
}
if (gaussianWeight2 != 0.0) {
distanceSum += distance;
rate = gaussianWeight * exp(-distanceSum * distanceSum / (2 * gaussianWeight2));
}
if (m_d->history.size() - i == 1) {
baseRate = rate;
} else if (baseRate / rate > 100) {
break;
}
scaleSum += rate;
x += rate * nextInfo.pos().x();
y += rate * nextInfo.pos().y();
if (m_d->smoothingOptions->smoothPressure()) {
pressure += rate * nextInfo.pressure();
}
}
if (scaleSum != 0.0) {
x /= scaleSum;
y /= scaleSum;
if (m_d->smoothingOptions->smoothPressure()) {
pressure /= scaleSum;
}
}
if ((x != 0.0 && y != 0.0) || (x == info.pos().x() && y == info.pos().y())) {
info.setPos(QPointF(x, y));
if (m_d->smoothingOptions->smoothPressure()) {
info.setPressure(pressure);
}
m_d->history.last() = info;
}
}
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::SIMPLE_SMOOTHING
|| m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING)
{
// Now paint between the coordinates, using the bezier curve interpolation
if (!m_d->haveTangent) {
m_d->haveTangent = true;
m_d->previousTangent =
(info.pos() - m_d->previousPaintInformation.pos()) /
qMax(qreal(1.0), info.currentTime() - m_d->previousPaintInformation.currentTime());
} else {
QPointF newTangent = (info.pos() - m_d->olderPaintInformation.pos()) /
qMax(qreal(1.0), info.currentTime() - m_d->olderPaintInformation.currentTime());
if (newTangent.isNull() || m_d->previousTangent.isNull())
{
paintLine(m_d->previousPaintInformation, info);
} else {
paintBezierSegment(m_d->olderPaintInformation, m_d->previousPaintInformation,
m_d->previousTangent, newTangent);
}
m_d->previousTangent = newTangent;
}
m_d->olderPaintInformation = m_d->previousPaintInformation;
// Enable stroke timeout only when not airbrushing.
if (!m_d->airbrushingTimer.isActive()) {
m_d->strokeTimeoutTimer.start(100);
}
}
else if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::NO_SMOOTHING){
paintLine(m_d->previousPaintInformation, info);
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
m_d->stabilizedSampler.addEvent(info);
if (m_d->stabilizerDelayedPaintHelper.running()) {
// Paint here so we don't have to rely on the timer
// This is just a tricky source for a relatively stable 7ms "timer"
m_d->stabilizerDelayedPaintHelper.paintSome();
}
} else {
m_d->previousPaintInformation = info;
}
if(m_d->airbrushingTimer.isActive()) {
m_d->airbrushingTimer.start();
}
}
void KisToolFreehandHelper::endPaint()
{
if (!m_d->hasPaintAtLeastOnce) {
paintAt(m_d->previousPaintInformation);
} else if (m_d->smoothingOptions->smoothingType() != KisSmoothingOptions::NO_SMOOTHING) {
finishStroke();
}
m_d->strokeTimeoutTimer.stop();
if(m_d->airbrushingTimer.isActive()) {
m_d->airbrushingTimer.stop();
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
stabilizerEnd();
}
if (m_d->asyncUpdateHelper.isActive()) {
m_d->asyncUpdateHelper.endUpdateStream();
}
/**
* There might be some timer events still pending, so
* we should cancel them. Use this flag for the purpose.
* Please note that we are not in MT here, so no mutex
* is needed
*/
m_d->strokeInfos.clear();
m_d->strokesFacade->endStroke(m_d->strokeId);
m_d->strokeId.clear();
}
void KisToolFreehandHelper::cancelPaint()
{
if (!m_d->strokeId) return;
m_d->strokeTimeoutTimer.stop();
if (m_d->airbrushingTimer.isActive()) {
m_d->airbrushingTimer.stop();
}
if (m_d->asyncUpdateHelper.isActive()) {
m_d->asyncUpdateHelper.cancelUpdateStream();
}
if (m_d->stabilizerPollTimer.isActive()) {
m_d->stabilizerPollTimer.stop();
}
if (m_d->stabilizerDelayedPaintHelper.running()) {
m_d->stabilizerDelayedPaintHelper.cancel();
}
// see a comment in endPaint()
m_d->strokeInfos.clear();
m_d->strokesFacade->cancelStroke(m_d->strokeId);
m_d->strokeId.clear();
}
int KisToolFreehandHelper::elapsedStrokeTime() const
{
return m_d->strokeTime.elapsed();
}
void KisToolFreehandHelper::stabilizerStart(KisPaintInformation firstPaintInfo)
{
m_d->usingStabilizer = true;
// FIXME: Ugly hack, this is no a "distance" in any way
int sampleSize = qRound(m_d->effectiveSmoothnessDistance());
sampleSize = qMax(3, sampleSize);
// Fill the deque with the current value repeated until filling the sample
m_d->stabilizerDeque.clear();
for (int i = sampleSize; i > 0; i--) {
m_d->stabilizerDeque.enqueue(firstPaintInfo);
}
// Poll and draw regularly
KisConfig cfg(true);
int stabilizerSampleSize = cfg.stabilizerSampleSize();
m_d->stabilizerPollTimer.setInterval(stabilizerSampleSize);
m_d->stabilizerPollTimer.start();
bool delayedPaintEnabled = cfg.stabilizerDelayedPaint();
if (delayedPaintEnabled) {
m_d->stabilizerDelayedPaintHelper.start(firstPaintInfo);
}
m_d->stabilizedSampler.clear();
m_d->stabilizedSampler.addEvent(firstPaintInfo);
}
KisPaintInformation
KisToolFreehandHelper::getStabilizedPaintInfo(const QQueue<KisPaintInformation> &queue,
const KisPaintInformation &lastPaintInfo)
{
KisPaintInformation result(lastPaintInfo.pos(),
lastPaintInfo.pressure(),
lastPaintInfo.xTilt(),
lastPaintInfo.yTilt(),
lastPaintInfo.rotation(),
lastPaintInfo.tangentialPressure(),
lastPaintInfo.perspective(),
elapsedStrokeTime(),
lastPaintInfo.drawingSpeed());
if (queue.size() > 1) {
QQueue<KisPaintInformation>::const_iterator it = queue.constBegin();
QQueue<KisPaintInformation>::const_iterator end = queue.constEnd();
/**
* The first point is going to be overridden by lastPaintInfo, skip it.
*/
it++;
int i = 2;
if (m_d->smoothingOptions->stabilizeSensors()) {
while (it != end) {
qreal k = qreal(i - 1) / i; // coeff for uniform averaging
result.KisPaintInformation::mixOtherWithoutTime(k, *it);
it++;
i++;
}
} else{
while (it != end) {
qreal k = qreal(i - 1) / i; // coeff for uniform averaging
result.KisPaintInformation::mixOtherOnlyPosition(k, *it);
it++;
i++;
}
}
}
return result;
}
void KisToolFreehandHelper::stabilizerPollAndPaint()
{
KisStabilizedEventsSampler::iterator it;
KisStabilizedEventsSampler::iterator end;
std::tie(it, end) = m_d->stabilizedSampler.range();
QVector<KisPaintInformation> delayedPaintTodoItems;
for (; it != end; ++it) {
KisPaintInformation sampledInfo = *it;
bool canPaint = true;
if (m_d->smoothingOptions->useDelayDistance()) {
const qreal R = m_d->smoothingOptions->delayDistance() /
m_d->resources->effectiveZoom();
QPointF diff = sampledInfo.pos() - m_d->previousPaintInformation.pos();
qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y()));
if (!(dx > R)) {
if (m_d->resources->needsAirbrushing()) {
sampledInfo.setPos(m_d->previousPaintInformation.pos());
}
else {
canPaint = false;
}
}
}
if (canPaint) {
KisPaintInformation newInfo = getStabilizedPaintInfo(m_d->stabilizerDeque, sampledInfo);
if (m_d->stabilizerDelayedPaintHelper.running()) {
delayedPaintTodoItems.append(newInfo);
} else {
paintLine(m_d->previousPaintInformation, newInfo);
}
m_d->previousPaintInformation = newInfo;
// Push the new entry through the queue
m_d->stabilizerDeque.dequeue();
m_d->stabilizerDeque.enqueue(sampledInfo);
} else if (m_d->stabilizerDeque.head().pos() != m_d->previousPaintInformation.pos()) {
QQueue<KisPaintInformation>::iterator it = m_d->stabilizerDeque.begin();
QQueue<KisPaintInformation>::iterator end = m_d->stabilizerDeque.end();
while (it != end) {
*it = m_d->previousPaintInformation;
++it;
}
}
}
m_d->stabilizedSampler.clear();
if (m_d->stabilizerDelayedPaintHelper.running()) {
m_d->stabilizerDelayedPaintHelper.update(delayedPaintTodoItems);
} else {
emit requestExplicitUpdateOutline();
}
}
void KisToolFreehandHelper::stabilizerEnd()
{
// Stop the timer
m_d->stabilizerPollTimer.stop();
// Finish the line
if (m_d->smoothingOptions->finishStabilizedCurve()) {
// Process all the existing events first
stabilizerPollAndPaint();
// Draw the finish line with pending events and a time override
m_d->stabilizedSampler.addFinishingEvent(m_d->stabilizerDeque.size());
stabilizerPollAndPaint();
}
if (m_d->stabilizerDelayedPaintHelper.running()) {
m_d->stabilizerDelayedPaintHelper.end();
}
m_d->usingStabilizer = false;
}
void KisToolFreehandHelper::slotSmoothingTypeChanged()
{
if (!isRunning()) {
return;
}
KisSmoothingOptions::SmoothingType currentSmoothingType =
m_d->smoothingOptions->smoothingType();
if (m_d->usingStabilizer
&& (currentSmoothingType != KisSmoothingOptions::STABILIZER)) {
stabilizerEnd();
} else if (!m_d->usingStabilizer
&& (currentSmoothingType == KisSmoothingOptions::STABILIZER)) {
stabilizerStart(m_d->previousPaintInformation);
}
}
void KisToolFreehandHelper::finishStroke()
{
if (m_d->haveTangent) {
m_d->haveTangent = false;
QPointF newTangent = (m_d->previousPaintInformation.pos() - m_d->olderPaintInformation.pos()) /
(m_d->previousPaintInformation.currentTime() - m_d->olderPaintInformation.currentTime());
paintBezierSegment(m_d->olderPaintInformation,
m_d->previousPaintInformation,
m_d->previousTangent,
newTangent);
}
}
void KisToolFreehandHelper::doAirbrushing()
{
// Check that the stroke hasn't ended.
if (!m_d->strokeInfos.isEmpty()) {
// Add a new painting update at a point identical to the previous one, except for the time
// and speed information.
const KisPaintInformation &prevPaint = m_d->previousPaintInformation;
KisPaintInformation nextPaint(prevPaint.pos(),
prevPaint.pressure(),
prevPaint.xTilt(),
prevPaint.yTilt(),
prevPaint.rotation(),
prevPaint.tangentialPressure(),
prevPaint.perspective(),
elapsedStrokeTime(),
0.0);
paint(nextPaint);
}
}
int KisToolFreehandHelper::computeAirbrushTimerInterval() const
{
qreal realInterval = m_d->resources->airbrushingInterval() * AIRBRUSH_INTERVAL_FACTOR;
return qMax(1, qFloor(realInterval));
}
qreal KisToolFreehandHelper::currentZoom() const
{
return m_d->resourceManager ? m_d->resourceManager->resource(KisCanvasResourceProvider::EffectiveZoom).toReal() : 1.0;
}
void KisToolFreehandHelper::paintAt(int strokeInfoId,
const KisPaintInformation &pi)
{
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(strokeInfoId, pi));
}
void KisToolFreehandHelper::paintLine(int strokeInfoId,
const KisPaintInformation &pi1,
const KisPaintInformation &pi2)
{
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(strokeInfoId, pi1, pi2));
}
void KisToolFreehandHelper::paintBezierCurve(int strokeInfoId,
const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2)
{
#ifdef DEBUG_BEZIER_CURVES
KisPaintInformation tpi1;
KisPaintInformation tpi2;
tpi1 = pi1;
tpi2 = pi2;
tpi1.setPressure(0.3);
tpi2.setPressure(0.3);
paintLine(tpi1, tpi2);
tpi1.setPressure(0.6);
tpi2.setPressure(0.3);
tpi1.setPos(pi1.pos());
tpi2.setPos(control1);
paintLine(tpi1, tpi2);
tpi1.setPos(pi2.pos());
tpi2.setPos(control2);
paintLine(tpi1, tpi2);
#endif
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(strokeInfoId,
pi1, control1, control2, pi2));
}
void KisToolFreehandHelper::createPainters(QVector<KisFreehandStrokeInfo*> &strokeInfos,
const KisDistanceInformation &startDist)
{
strokeInfos << new KisFreehandStrokeInfo(startDist);
}
void KisToolFreehandHelper::paintAt(const KisPaintInformation &pi)
{
paintAt(0, pi);
}
void KisToolFreehandHelper::paintLine(const KisPaintInformation &pi1,
const KisPaintInformation &pi2)
{
paintLine(0, pi1, pi2);
}
void KisToolFreehandHelper::paintBezierCurve(const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2)
{
paintBezierCurve(0, pi1, control1, control2, pi2);
}
diff --git a/libs/ui/tool/strokes/KisMaskedFreehandStrokePainter.h b/libs/ui/tool/strokes/KisMaskedFreehandStrokePainter.h
index 3957876577..99e4e4d7ad 100644
--- a/libs/ui/tool/strokes/KisMaskedFreehandStrokePainter.h
+++ b/libs/ui/tool/strokes/KisMaskedFreehandStrokePainter.h
@@ -1,90 +1,89 @@
/*
* 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 KISMASKEDPAINTINGSTROKEDATA_H
#define KISMASKEDPAINTINGSTROKEDATA_H
#include "kritaui_export.h"
#include <QVector>
+#include <QSharedPointer>
class KisFreehandStrokeInfo;
class KisPaintInformation;
class KisDistanceInformation;
class QPointF;
class QRectF;
class QRect;
class QPainterPath;
class QPen;
class KoColor;
class KisRunnableStrokeJobData;
class KisPaintOpPreset;
-template<class T>
-class KisSharedPtr;
-typedef KisSharedPtr<KisPaintOpPreset> KisPaintOpPresetSP;
+typedef QSharedPointer<KisPaintOpPreset> KisPaintOpPresetSP;
class KRITAUI_EXPORT KisMaskedFreehandStrokePainter
{
public:
KisMaskedFreehandStrokePainter(KisFreehandStrokeInfo *strokeData, KisFreehandStrokeInfo *maskData);
// painter overrides
KisPaintOpPresetSP preset() const;
void paintAt(const KisPaintInformation& pi);
void paintLine(const KisPaintInformation &pi1,
const KisPaintInformation &pi2);
void paintBezierCurve(const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2);
void paintPolyline(const QVector<QPointF> &points,
int index = 0, int numPoints = -1);
void paintPolygon(const QVector<QPointF> &points);
void paintRect(const QRectF &rect);
void paintEllipse(const QRectF &rect);
void paintPainterPath(const QPainterPath& path);
void drawPainterPath(const QPainterPath& path, const QPen& pen);
void drawAndFillPainterPath(const QPainterPath& path, const QPen& pen, const KoColor &customColor);
// paintop overrides
std::pair<int, bool> doAsyncronousUpdate(QVector<KisRunnableStrokeJobData*> &jobs);
bool hasDirtyRegion() const;
QVector<QRect> takeDirtyRegion();
bool hasMasking() const;
private:
template <class Func>
inline void applyToAllPainters(Func func);
private:
KisFreehandStrokeInfo *m_stroke = 0;
KisFreehandStrokeInfo *m_mask = 0;
};
#endif // KISMASKEDPAINTINGSTROKEDATA_H
diff --git a/libs/ui/tool/strokes/freehand_stroke.cpp b/libs/ui/tool/strokes/freehand_stroke.cpp
index 278ba0ac45..1688bbb9ad 100644
--- a/libs/ui/tool/strokes/freehand_stroke.cpp
+++ b/libs/ui/tool/strokes/freehand_stroke.cpp
@@ -1,350 +1,352 @@
/*
* 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 "freehand_stroke.h"
#include <QElapsedTimer>
+#include <QThread>
+#include <QApplication>
#include "kis_canvas_resource_provider.h"
#include <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_settings.h>
#include "kis_painter.h"
#include "kis_paintop.h"
#include "kis_update_time_monitor.h"
#include <brushengine/kis_stroke_random_source.h>
#include <KisRunnableStrokeJobsInterface.h>
#include "FreehandStrokeRunnableJobDataWithUpdate.h"
#include <mutex>
#include "KisStrokeEfficiencyMeasurer.h"
#include <KisStrokeSpeedMonitor.h>
#include <strokes/KisFreehandStrokeInfo.h>
#include <strokes/KisMaskedFreehandStrokePainter.h>
#include "brushengine/kis_paintop_utils.h"
#include "KisAsyncronousStrokeUpdateHelper.h"
struct FreehandStrokeStrategy::Private
{
Private(KisResourcesSnapshotSP _resources)
: resources(_resources),
needsAsynchronousUpdates(_resources->presetNeedsAsynchronousUpdates())
{
if (needsAsynchronousUpdates) {
timeSinceLastUpdate.start();
}
}
Private(const Private &rhs)
: randomSource(rhs.randomSource),
resources(rhs.resources),
needsAsynchronousUpdates(rhs.needsAsynchronousUpdates)
{
if (needsAsynchronousUpdates) {
timeSinceLastUpdate.start();
}
}
KisStrokeRandomSource randomSource;
KisResourcesSnapshotSP resources;
KisStrokeEfficiencyMeasurer efficiencyMeasurer;
QElapsedTimer timeSinceLastUpdate;
int currentUpdatePeriod = 40;
const bool needsAsynchronousUpdates = false;
std::mutex updateEntryMutex;
};
FreehandStrokeStrategy::FreehandStrokeStrategy(KisResourcesSnapshotSP resources,
KisFreehandStrokeInfo *strokeInfo,
const KUndo2MagicString &name)
: KisPainterBasedStrokeStrategy(QLatin1String("FREEHAND_STROKE"), name,
resources, strokeInfo),
m_d(new Private(resources))
{
init();
}
FreehandStrokeStrategy::FreehandStrokeStrategy(KisResourcesSnapshotSP resources,
QVector<KisFreehandStrokeInfo*> strokeInfos,
const KUndo2MagicString &name)
: KisPainterBasedStrokeStrategy(QLatin1String("FREEHAND_STROKE"), name,
resources, strokeInfos),
m_d(new Private(resources))
{
init();
}
FreehandStrokeStrategy::FreehandStrokeStrategy(const FreehandStrokeStrategy &rhs, int levelOfDetail)
: KisPainterBasedStrokeStrategy(rhs, levelOfDetail),
m_d(new Private(*rhs.m_d))
{
m_d->randomSource.setLevelOfDetail(levelOfDetail);
}
FreehandStrokeStrategy::~FreehandStrokeStrategy()
{
KisStrokeSpeedMonitor::instance()->notifyStrokeFinished(m_d->efficiencyMeasurer.averageCursorSpeed(),
m_d->efficiencyMeasurer.averageRenderingSpeed(),
m_d->efficiencyMeasurer.averageFps(),
m_d->resources->currentPaintOpPreset());
KisUpdateTimeMonitor::instance()->endStrokeMeasure();
}
void FreehandStrokeStrategy::init()
{
setSupportsWrapAroundMode(true);
setSupportsMaskingBrush(true);
setSupportsIndirectPainting(true);
enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE);
if (m_d->needsAsynchronousUpdates) {
/**
* In case the paintop uses asynchronous updates, we should set priority to it,
* because FPS is controlled separately, not by the queue's merging algorithm.
*/
setBalancingRatioOverride(0.01); // set priority to updates
}
KisUpdateTimeMonitor::instance()->startStrokeMeasure();
m_d->efficiencyMeasurer.setEnabled(KisStrokeSpeedMonitor::instance()->haveStrokeSpeedMeasurement());
}
void FreehandStrokeStrategy::initStrokeCallback()
{
KisPainterBasedStrokeStrategy::initStrokeCallback();
m_d->efficiencyMeasurer.notifyRenderingStarted();
}
void FreehandStrokeStrategy::finishStrokeCallback()
{
m_d->efficiencyMeasurer.notifyRenderingFinished();
KisPainterBasedStrokeStrategy::finishStrokeCallback();
}
void FreehandStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
if (KisAsyncronousStrokeUpdateHelper::UpdateData *d =
dynamic_cast<KisAsyncronousStrokeUpdateHelper::UpdateData*>(data)) {
// this job is lod-clonable in contrast to FreehandStrokeRunnableJobDataWithUpdate!
tryDoUpdate(d->forceUpdate);
} else if (Data *d = dynamic_cast<Data*>(data)) {
KisMaskedFreehandStrokePainter *maskedPainter = this->maskedPainter(d->strokeInfoId);
KisUpdateTimeMonitor::instance()->reportPaintOpPreset(maskedPainter->preset());
KisRandomSourceSP rnd = m_d->randomSource.source();
KisPerStrokeRandomSourceSP strokeRnd = m_d->randomSource.perStrokeSource();
switch(d->type) {
case Data::POINT:
d->pi1.setRandomSource(rnd);
d->pi1.setPerStrokeRandomSource(strokeRnd);
maskedPainter->paintAt(d->pi1);
m_d->efficiencyMeasurer.addSample(d->pi1.pos());
break;
case Data::LINE:
d->pi1.setRandomSource(rnd);
d->pi2.setRandomSource(rnd);
d->pi1.setPerStrokeRandomSource(strokeRnd);
d->pi2.setPerStrokeRandomSource(strokeRnd);
maskedPainter->paintLine(d->pi1, d->pi2);
m_d->efficiencyMeasurer.addSample(d->pi2.pos());
break;
case Data::CURVE:
d->pi1.setRandomSource(rnd);
d->pi2.setRandomSource(rnd);
d->pi1.setPerStrokeRandomSource(strokeRnd);
d->pi2.setPerStrokeRandomSource(strokeRnd);
maskedPainter->paintBezierCurve(d->pi1,
d->control1,
d->control2,
d->pi2);
m_d->efficiencyMeasurer.addSample(d->pi2.pos());
break;
case Data::POLYLINE:
maskedPainter->paintPolyline(d->points, 0, d->points.size());
m_d->efficiencyMeasurer.addSamples(d->points);
break;
case Data::POLYGON:
maskedPainter->paintPolygon(d->points);
m_d->efficiencyMeasurer.addSamples(d->points);
break;
case Data::RECT:
maskedPainter->paintRect(d->rect);
m_d->efficiencyMeasurer.addSample(d->rect.topLeft());
m_d->efficiencyMeasurer.addSample(d->rect.topRight());
m_d->efficiencyMeasurer.addSample(d->rect.bottomRight());
m_d->efficiencyMeasurer.addSample(d->rect.bottomLeft());
break;
case Data::ELLIPSE:
maskedPainter->paintEllipse(d->rect);
// TODO: add speed measures
break;
case Data::PAINTER_PATH:
maskedPainter->paintPainterPath(d->path);
// TODO: add speed measures
break;
case Data::QPAINTER_PATH:
maskedPainter->drawPainterPath(d->path, d->pen);
break;
case Data::QPAINTER_PATH_FILL:
maskedPainter->drawAndFillPainterPath(d->path, d->pen, d->customColor);
break;
};
tryDoUpdate();
} else {
KisPainterBasedStrokeStrategy::doStrokeCallback(data);
FreehandStrokeRunnableJobDataWithUpdate *dataWithUpdate =
dynamic_cast<FreehandStrokeRunnableJobDataWithUpdate*>(data);
if (dataWithUpdate) {
tryDoUpdate();
}
}
}
void FreehandStrokeStrategy::tryDoUpdate(bool forceEnd)
{
// we should enter this function only once!
std::unique_lock<std::mutex> entryLock(m_d->updateEntryMutex, std::try_to_lock);
if (!entryLock.owns_lock()) return;
if (m_d->needsAsynchronousUpdates) {
if (forceEnd || m_d->timeSinceLastUpdate.elapsed() > m_d->currentUpdatePeriod) {
m_d->timeSinceLastUpdate.restart();
for (int i = 0; i < numMaskedPainters(); i++) {
KisMaskedFreehandStrokePainter *maskedPainter = this->maskedPainter(i);
// TODO: well, we should count all N simultaneous painters for FPS rate!
QVector<KisRunnableStrokeJobData*> jobs;
bool needsMoreUpdates = false;
std::tie(m_d->currentUpdatePeriod, needsMoreUpdates) =
maskedPainter->doAsyncronousUpdate(jobs);
if (!jobs.isEmpty() ||
maskedPainter->hasDirtyRegion() ||
(forceEnd && needsMoreUpdates)) {
jobs.append(new KisRunnableStrokeJobData(
[this] () {
this->issueSetDirtySignals();
},
KisStrokeJobData::SEQUENTIAL));
if (forceEnd && needsMoreUpdates) {
jobs.append(new KisRunnableStrokeJobData(
[this] () {
this->tryDoUpdate(true);
},
KisStrokeJobData::SEQUENTIAL));
}
runnableJobsInterface()->addRunnableJobs(jobs);
m_d->efficiencyMeasurer.notifyFrameRenderingStarted();
}
}
}
} else {
issueSetDirtySignals();
}
}
void FreehandStrokeStrategy::issueSetDirtySignals()
{
QVector<QRect> dirtyRects;
for (int i = 0; i < numMaskedPainters(); i++) {
KisMaskedFreehandStrokePainter *maskedPainter = this->maskedPainter(i);
dirtyRects.append(maskedPainter->takeDirtyRegion());
}
if (needsMaskingUpdates()) {
// optimize the rects so that they would never intersect with each other!
// that is a mandatory step for the multithreaded execution of merging jobs
// sanity check: updates from the brush should have already been normalized
// to the wrapping rect
const KisDefaultBoundsBaseSP defaultBounds = targetNode()->projection()->defaultBounds();
if (defaultBounds->wrapAroundMode()) {
const QRect wrapRect = defaultBounds->bounds();
for (auto it = dirtyRects.begin(); it != dirtyRects.end(); ++it) {
KIS_SAFE_ASSERT_RECOVER(wrapRect.contains(*it)) {
ENTER_FUNCTION() << ppVar(*it) << ppVar(wrapRect);
*it = *it & wrapRect;
}
}
}
const int maxPatchSizeForMaskingUpdates = 64;
const QRect totalRect =
std::accumulate(dirtyRects.constBegin(), dirtyRects.constEnd(), QRect(), std::bit_or<QRect>());
dirtyRects = KisPaintOpUtils::splitAndFilterDabRect(totalRect, dirtyRects, maxPatchSizeForMaskingUpdates);
QVector<KisRunnableStrokeJobData*> jobs = doMaskingBrushUpdates(dirtyRects);
jobs.append(new KisRunnableStrokeJobData(
[this, dirtyRects] () {
this->targetNode()->setDirty(dirtyRects);
},
KisStrokeJobData::SEQUENTIAL));
runnableJobsInterface()->addRunnableJobs(jobs);
} else {
targetNode()->setDirty(dirtyRects);
}
//KisUpdateTimeMonitor::instance()->reportJobFinished(data, dirtyRects);
}
KisStrokeStrategy* FreehandStrokeStrategy::createLodClone(int levelOfDetail)
{
if (!m_d->resources->presetAllowsLod()) return 0;
FreehandStrokeStrategy *clone = new FreehandStrokeStrategy(*this, levelOfDetail);
return clone;
}
void FreehandStrokeStrategy::notifyUserStartedStroke()
{
m_d->efficiencyMeasurer.notifyCursorMoveStarted();
}
void FreehandStrokeStrategy::notifyUserEndedStroke()
{
m_d->efficiencyMeasurer.notifyCursorMoveFinished();
}
diff --git a/libs/ui/utils/KisClipboardUtil.cpp b/libs/ui/utils/KisClipboardUtil.cpp
index 9f1039cc14..6943ba5c34 100644
--- a/libs/ui/utils/KisClipboardUtil.cpp
+++ b/libs/ui/utils/KisClipboardUtil.cpp
@@ -1,76 +1,77 @@
/*
* Copyright (c) 2019 Dmitrii Utkin <loentar@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 "KisClipboardUtil.h"
#include <QApplication>
#include <QClipboard>
#include <QMimeData>
#include <QImage>
#include <QList>
#include <QSet>
#include <QPair>
namespace KisClipboardUtil {
struct ClipboardImageFormat
{
QSet<QString> mimeTypes;
QString format;
};
QImage getImageFromClipboard()
{
static const QList<ClipboardImageFormat> supportedFormats = {
{{"image/png"}, "PNG"},
{{"image/tiff"}, "TIFF"},
{{"image/bmp", "image/x-bmp", "image/x-MS-bmp", "image/x-win-bitmap"}, "BMP"},
{{"image/jpeg"}, "JPG"}
};
QClipboard *clipboard = QApplication::clipboard();
QImage image;
- const QSet<QString> &clipboardMimeTypes = clipboard->mimeData()->formats().toSet();
+ const QSet<QString> &clipboardMimeTypes = QSet<QString>(clipboard->mimeData()->formats().begin(),
+ clipboard->mimeData()->formats().end());
Q_FOREACH (const ClipboardImageFormat &item, supportedFormats) {
const QSet<QString> &intersection = item.mimeTypes & clipboardMimeTypes;
if (intersection.isEmpty()) {
continue;
}
const QString &format = *intersection.constBegin();
const QByteArray &imageData = clipboard->mimeData()->data(format);
if (imageData.isEmpty()) {
continue;
}
if (image.loadFromData(imageData, item.format.toLatin1())) {
break;
}
}
if (image.isNull()) {
image = clipboard->image();
}
return image;
}
}
diff --git a/libs/ui/utils/KisDitherUtil.cpp b/libs/ui/utils/KisDitherUtil.cpp
index 0560aa7595..641b2f3956 100644
--- a/libs/ui/utils/KisDitherUtil.cpp
+++ b/libs/ui/utils/KisDitherUtil.cpp
@@ -1,97 +1,98 @@
/*
* This file is part of the KDE project
*
* Copyright (c) 2019 Carl Olsson <carl.olsson@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 "KisDitherUtil.h"
-#include <KoPattern.h>
-#include <kis_properties_configuration.h>
-#include <KoResourceServerProvider.h>
+#include <kis_filter_configuration.h>
#include <kis_random_generator.h>
+#include <KisResourcesInterface.h>
KisDitherUtil::KisDitherUtil()
- : m_thresholdMode(ThresholdMode::Pattern), m_patternValueMode(PatternValueMode::Auto),
- m_pattern(0), m_noiseSeed(0), m_patternUseAlpha(false), m_spread(1.0)
+ : m_thresholdMode(ThresholdMode::Pattern), m_patternValueMode(PatternValueMode::Auto)
+ , m_noiseSeed(0), m_patternUseAlpha(false), m_spread(1.0)
{
}
void KisDitherUtil::setThresholdMode(const ThresholdMode thresholdMode)
{
m_thresholdMode = thresholdMode;
}
-void KisDitherUtil::setPattern(const QString &name, const PatternValueMode valueMode)
+void KisDitherUtil::setPattern(const QString &name, const PatternValueMode valueMode, KisResourcesInterfaceSP resourcesInterface)
{
m_patternValueMode = valueMode;
- m_pattern = KoResourceServerProvider::instance()->patternServer()->resourceByName(name);
+
+ auto source = resourcesInterface->source<KoPattern>(ResourceType::Patterns);
+ m_pattern = source.resourceForName(name);
if (m_pattern && m_thresholdMode == ThresholdMode::Pattern && m_patternValueMode == PatternValueMode::Auto) {
// Automatically pick between lightness-based and alpha-based patterns by whichever has maximum range
qreal lightnessMin = 1.0, lightnessMax = 0.0;
qreal alphaMin = 1.0, alphaMax = 0.0;
const QImage &image = m_pattern->pattern();
for (int y = 0; y < image.height(); ++y) {
for (int x = 0; x < image.width(); ++x) {
const QColor pixel = image.pixelColor(x, y);
lightnessMin = std::min(lightnessMin, pixel.lightnessF());
lightnessMax = std::max(lightnessMax, pixel.lightnessF());
alphaMin = std::min(alphaMin, pixel.alphaF());
alphaMax = std::max(alphaMax, pixel.alphaF());
}
}
m_patternUseAlpha = (alphaMax - alphaMin > lightnessMax - lightnessMin);
}
else {
m_patternUseAlpha = (m_patternValueMode == PatternValueMode::Alpha);
}
}
void KisDitherUtil::setNoiseSeed(const quint64 &noiseSeed)
{
m_noiseSeed = noiseSeed;
}
void KisDitherUtil::setSpread(const qreal &spread)
{
m_spread = spread;
}
qreal KisDitherUtil::threshold(const QPoint &pos)
{
qreal threshold;
if (m_thresholdMode == ThresholdMode::Pattern && m_pattern) {
const QImage &image = m_pattern->pattern();
const QColor color = image.pixelColor(pos.x() % image.width(), pos.y() % image.height());
threshold = (m_patternUseAlpha ? color.alphaF() : color.lightnessF());
}
else if (m_thresholdMode == ThresholdMode::Noise) {
KisRandomGenerator random(m_noiseSeed);
threshold = random.doubleRandomAt(pos.x(), pos.y());
}
else threshold = 0.5;
return 0.5 - (m_spread / 2.0) + threshold * m_spread;
}
-void KisDitherUtil::setConfiguration(const KisPropertiesConfiguration &config, const QString &prefix)
+void KisDitherUtil::setConfiguration(const KisFilterConfiguration &config, const QString &prefix)
{
setThresholdMode(ThresholdMode(config.getInt(prefix + "thresholdMode")));
- setPattern(config.getString(prefix + "pattern"), PatternValueMode(config.getInt(prefix + "patternValueMode")));
+ setPattern(config.getString(prefix + "pattern"), PatternValueMode(config.getInt(prefix + "patternValueMode")), config.resourcesInterface());
setNoiseSeed(quint64(config.getInt(prefix + "noiseSeed")));
setSpread(config.getDouble(prefix + "spread"));
}
diff --git a/libs/ui/utils/KisDitherUtil.h b/libs/ui/utils/KisDitherUtil.h
index fa8e1e3a27..05c3ed82f6 100644
--- a/libs/ui/utils/KisDitherUtil.h
+++ b/libs/ui/utils/KisDitherUtil.h
@@ -1,64 +1,65 @@
/*
* This file is part of the KDE project
*
* Copyright (c) 2019 Carl Olsson <carl.olsson@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_DITHER_UTIL_H
#define KIS_DITHER_UTIL_H
#include <kritaui_export.h>
#include <kis_types.h>
-class KoPattern;
+#include <KoPattern.h>
+
class KisPropertiesConfiguration;
class KRITAUI_EXPORT KisDitherUtil
{
public:
enum ThresholdMode {
Pattern,
Noise
};
enum PatternValueMode {
Auto,
Lightness,
Alpha
};
KisDitherUtil();
void setThresholdMode(const ThresholdMode thresholdMode);
- void setPattern(const QString &name, const PatternValueMode valueMode);
+ void setPattern(const QString &name, const PatternValueMode valueMode, KisResourcesInterfaceSP resourcesInterface);
void setNoiseSeed(const quint64 &noiseSeed);
void setSpread(const qreal &spread);
qreal threshold(const QPoint &pos);
- void setConfiguration(const KisPropertiesConfiguration &config, const QString &prefix = "");
+ void setConfiguration(const KisFilterConfiguration &config, const QString &prefix = "");
private:
ThresholdMode m_thresholdMode;
PatternValueMode m_patternValueMode;
- KoPattern* m_pattern;
+ KoPatternSP m_pattern;
quint64 m_noiseSeed;
bool m_patternUseAlpha;
qreal m_spread;
};
#endif
diff --git a/libs/ui/widgets/KisDitherWidget.cpp b/libs/ui/widgets/KisDitherWidget.cpp
index cfcebe5b48..2f593438cf 100644
--- a/libs/ui/widgets/KisDitherWidget.cpp
+++ b/libs/ui/widgets/KisDitherWidget.cpp
@@ -1,90 +1,106 @@
/*
* This file is part of Krita
*
* Copyright (c) 2019 Carl Olsson <carl.olsson@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 "KisDitherWidget.h"
#include <kpluginfactory.h>
#include <KoUpdater.h>
-#include <KoResourceServerProvider.h>
-#include <KoResourceServer.h>
-#include <KoResourceServerAdapter.h>
-#include <KoResourceItemChooser.h>
+#include "kis_filter_configuration.h"
+#include "KisResourcesInterface.h"
+#include <KisResourceItemChooser.h>
#include <KoColorSet.h>
#include <KoPattern.h>
#include <kis_properties_configuration.h>
#include "KisDitherUtil.h"
KisDitherWidget::KisDitherWidget(QWidget* parent)
: QWidget(parent), Ui::KisDitherWidget()
{
setupUi(this);
QObject::connect(thresholdModeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &KisDitherWidget::sigConfigurationItemChanged);
patternIconWidget->setFixedSize(32, 32);
- KoResourceServer<KoPattern>* patternServer = KoResourceServerProvider::instance()->patternServer();
- QSharedPointer<KoAbstractResourceServerAdapter> patternAdapter(new KoResourceServerAdapter<KoPattern>(patternServer));
- m_ditherPatternWidget = new KoResourceItemChooser(patternAdapter, this, false);
+
+ // FIXME: the patterns are not rendered correctly in the dialog!
+ m_ditherPatternWidget = new KisResourceItemChooser(ResourceType::Patterns, false, this);
+
patternIconWidget->setPopupWidget(m_ditherPatternWidget);
- QObject::connect(m_ditherPatternWidget, &KoResourceItemChooser::resourceSelected, patternIconWidget, &KisIconWidget::setResource);
- QObject::connect(m_ditherPatternWidget, &KoResourceItemChooser::resourceSelected, this, &KisDitherWidget::sigConfigurationItemChanged);
+ QObject::connect(m_ditherPatternWidget, &KisResourceItemChooser::resourceSelected, patternIconWidget, &KisIconWidget::setResource);
+ QObject::connect(m_ditherPatternWidget, &KisResourceItemChooser::resourceSelected, this, &KisDitherWidget::sigConfigurationItemChanged);
QObject::connect(patternValueModeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &KisDitherWidget::sigConfigurationItemChanged);
noiseSeedLineEdit->setValidator(new QIntValidator(this));
QObject::connect(noiseSeedLineEdit, &QLineEdit::textChanged, this, &KisDitherWidget::sigConfigurationItemChanged);
QObject::connect(noiseSeedRandomizeButton, &QToolButton::clicked, [this](){
noiseSeedLineEdit->setText(QString::number(rand()));
});
spreadSpinBox->setPrefix(QString("%1 ").arg(i18n("Spread:")));
spreadSpinBox->setRange(0.0, 1.0, 3);
spreadSpinBox->setSingleStep(0.125);
QObject::connect(spreadSpinBox, &KisDoubleSliderSpinBox::valueChanged, this, &KisDitherWidget::sigConfigurationItemChanged);
}
-void KisDitherWidget::setConfiguration(const KisPropertiesConfiguration &config, const QString &prefix)
+void KisDitherWidget::setConfiguration(const KisFilterConfiguration &config, const QString &prefix)
{
thresholdModeComboBox->setCurrentIndex(config.getInt(prefix + "thresholdMode"));
- KoPattern* pattern = KoResourceServerProvider::instance()->patternServer()->resourceByName(config.getString(prefix + "pattern"));
+
+ auto source = config.resourcesInterface()->source<KoPattern>(ResourceType::Patterns);
+ KoPatternSP pattern = source.resourceForName(config.getString(prefix + "pattern"));
+
if (pattern) m_ditherPatternWidget->setCurrentResource(pattern);
patternValueModeComboBox->setCurrentIndex(config.getInt(prefix + "patternValueMode"));
noiseSeedLineEdit->setText(QString::number(config.getInt(prefix + "noiseSeed")));
spreadSpinBox->setValue(config.getDouble(prefix + "spread"));
}
void KisDitherWidget::configuration(KisPropertiesConfiguration &config, const QString &prefix) const
{
config.setProperty(prefix + "thresholdMode",thresholdModeComboBox->currentIndex());
if (m_ditherPatternWidget->currentResource()) config.setProperty(prefix + "pattern", QVariant(m_ditherPatternWidget->currentResource()->name()));
config.setProperty(prefix + "patternValueMode", patternValueModeComboBox->currentIndex());
config.setProperty(prefix + "noiseSeed", noiseSeedLineEdit->text().toInt());
config.setProperty(prefix + "spread", spreadSpinBox->value());
}
void KisDitherWidget::factoryConfiguration(KisPropertiesConfiguration &config, const QString &prefix)
{
config.setProperty(prefix + "thresholdMode", KisDitherUtil::ThresholdMode::Pattern);
config.setProperty(prefix + "pattern", "DITH 0202 GEN ");
config.setProperty(prefix + "patternValueMode", KisDitherUtil::PatternValueMode::Auto);
config.setProperty(prefix + "noiseSeed", rand());
config.setProperty(prefix + "spread", 1.0);
}
+
+QList<KoResourceSP> KisDitherWidget::prepareLinkedResources(const KisFilterConfiguration &config, const QString &prefix, KisResourcesInterfaceSP resourcesInterface)
+{
+ auto source = resourcesInterface->source<KoPattern>(ResourceType::Patterns);
+ KoPatternSP pattern = source.resourceForName(config.getString(prefix + "pattern"));
+
+ QList<KoResourceSP> resources;
+ if (pattern) {
+ resources << pattern;
+ }
+
+ return resources;
+}
diff --git a/libs/ui/widgets/KisDitherWidget.h b/libs/ui/widgets/KisDitherWidget.h
index e0cb29a1b9..6bff7cf7be 100644
--- a/libs/ui/widgets/KisDitherWidget.h
+++ b/libs/ui/widgets/KisDitherWidget.h
@@ -1,45 +1,48 @@
/*
* This file is part of the KDE project
*
* Copyright (c) 2019 Carl Olsson <carl.olsson@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_DITHER_WIDGET_H
#define KIS_DITHER_WIDGET_H
#include <kritaui_export.h>
#include <QWidget>
#include "ui_KisDitherWidget.h"
-class KoResourceItemChooser;
+class KisResourceItemChooser;
class KisPropertiesConfiguration;
+class KisFilterConfiguration;
class KRITAUI_EXPORT KisDitherWidget : public QWidget, public Ui::KisDitherWidget
{
Q_OBJECT
public:
KisDitherWidget(QWidget* parent = 0);
- void setConfiguration(const KisPropertiesConfiguration &config, const QString &prefix = "");
+ void setConfiguration(const KisFilterConfiguration &config, const QString &prefix = "");
void configuration(KisPropertiesConfiguration &config, const QString &prefix = "") const;
static void factoryConfiguration(KisPropertiesConfiguration &config, const QString &prefix = "");
+ static QList<KoResourceSP> prepareLinkedResources(const KisFilterConfiguration &config, const QString &prefix, KisResourcesInterfaceSP resourcesInterface);
+
Q_SIGNALS:
void sigConfigurationItemChanged();
private:
- KoResourceItemChooser* m_ditherPatternWidget;
+ KisResourceItemChooser* m_ditherPatternWidget;
};
#endif
diff --git a/libs/ui/widgets/KisGamutMaskToolbar.cpp b/libs/ui/widgets/KisGamutMaskToolbar.cpp
index 2a1851f773..1fdbe3b053 100644
--- a/libs/ui/widgets/KisGamutMaskToolbar.cpp
+++ b/libs/ui/widgets/KisGamutMaskToolbar.cpp
@@ -1,144 +1,145 @@
/*
* 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 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 <QWidget>
#include "KisGamutMaskToolbar.h"
#include <kis_icon_utils.h>
#include <kis_canvas_resource_provider.h>
#include <kis_signals_blocker.h>
KisGamutMaskToolbar::KisGamutMaskToolbar(QWidget* parent) : QWidget(parent)
, m_selectedMask(nullptr)
, m_selfUpdate(false)
{
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*)), Qt::UniqueConnection);
+ connect(resourceProvider, SIGNAL(sigGamutMaskChanged(KoGamutMaskSP)),
+ this, SLOT(slotGamutMaskSet(KoGamutMaskSP)), Qt::UniqueConnection);
connect(resourceProvider, SIGNAL(sigGamutMaskUnset()),
this, SLOT(slotGamutMaskUnset()), Qt::UniqueConnection);
- connect(this, SIGNAL(sigGamutMaskChanged(KoGamutMask*)),
- resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMask*)), Qt::UniqueConnection);
+ connect(this, SIGNAL(sigGamutMaskChanged(KoGamutMaskSP)),
+ resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMaskSP)), Qt::UniqueConnection);
connect(this, SIGNAL(sigGamutMaskDeactivated()),
resourceProvider, SLOT(slotGamutMaskDeactivate()), Qt::UniqueConnection);
connect(resourceProvider, SIGNAL(sigGamutMaskDeactivated()),
this, SLOT(slotGamutMaskDeactivate()), Qt::UniqueConnection);
+
}
-void KisGamutMaskToolbar::slotGamutMaskSet(KoGamutMask *mask)
+void KisGamutMaskToolbar::slotGamutMaskSet(KoGamutMaskSP mask)
{
if (!mask) {
return;
}
if (m_selfUpdate) {
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::slotGamutMaskDeactivate()
{
if (m_selfUpdate) {
return;
}
slotGamutMaskToggle(false);
}
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);
m_selfUpdate = true;
emit sigGamutMaskChanged(m_selectedMask);
m_selfUpdate = false;
} else {
m_ui->bnToggleMask->setIcon(m_iconMaskOff);
m_ui->rotationSlider->hide();
m_ui->labelMaskName->show();
m_ui->labelMaskName->setText(m_textMaskDisabled);
m_selfUpdate = true;
emit sigGamutMaskDeactivated();
m_selfUpdate = false;
}
}
void KisGamutMaskToolbar::slotGamutMaskRotate(int angle)
{
if (!m_selectedMask) {
return;
}
m_selectedMask->setRotation(angle);
emit sigGamutMaskChanged(m_selectedMask);
}
diff --git a/libs/ui/widgets/KisGamutMaskToolbar.h b/libs/ui/widgets/KisGamutMaskToolbar.h
index 941d7794c2..3b8c5e6215 100644
--- a/libs/ui/widgets/KisGamutMaskToolbar.h
+++ b/libs/ui/widgets/KisGamutMaskToolbar.h
@@ -1,65 +1,66 @@
/*
* 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 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 KISGAMUTMASKTOOLBAR_H
#define KISGAMUTMASKTOOLBAR_H
#include <QWidget>
#include <QIcon>
#include <resources/KoGamutMask.h>
#include "kritaui_export.h"
#include "ui_wdgGamutMaskToolbar.h"
class KisCanvasResourceProvider;
class KRITAUI_EXPORT KisGamutMaskToolbar : public QWidget
{
Q_OBJECT
public:
KisGamutMaskToolbar(QWidget* parent = nullptr);
void connectMaskSignals(KisCanvasResourceProvider* resourceProvider);
Q_SIGNALS:
- void sigGamutMaskChanged(KoGamutMask*);
+ void sigGamutMaskToggle(bool state);
+ void sigGamutMaskChanged(KoGamutMaskSP);
void sigGamutMaskDeactivated();
public Q_SLOTS:
- void slotGamutMaskSet(KoGamutMask* mask);
+ void slotGamutMaskSet(KoGamutMaskSP mask);
void slotGamutMaskUnset();
void slotGamutMaskDeactivate();
private Q_SLOTS:
void slotGamutMaskToggle(bool state);
void slotGamutMaskRotate(int angle);
private:
Ui_wdgGamutMaskToolbar* m_ui;
- KoGamutMask* m_selectedMask;
+ KoGamutMaskSP m_selectedMask;
QIcon m_iconMaskOff;
QIcon m_iconMaskOn;
QString m_textNoMask;
QString m_textMaskDisabled;
bool m_selfUpdate;
};
#endif // KISGAMUTMASKTOOLBAR_H
diff --git a/libs/ui/widgets/KoFillConfigWidget.cpp b/libs/ui/widgets/KoFillConfigWidget.cpp
index 2bda14604e..05af46ee0c 100644
--- a/libs/ui/widgets/KoFillConfigWidget.cpp
+++ b/libs/ui/widgets/KoFillConfigWidget.cpp
@@ -1,929 +1,919 @@
/* 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.
*/
#include "KoFillConfigWidget.h"
#include <QToolButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QButtonGroup>
#include <QLabel>
#include <QSizePolicy>
#include <QBitmap>
#include <QAction>
#include <QSharedPointer>
+#include <QMessageBox>
#include <klocalizedstring.h>
#include <KoIcon.h>
#include <KoColor.h>
#include <KoColorPopupAction.h>
#include "KoResourceServerProvider.h"
-#include "KoResourceServerAdapter.h"
#include <KoSelection.h>
#include <KoCanvasBase.h>
#include <KoCanvasResourceProvider.h>
#include <KoDocumentResourceManager.h>
#include <KoShape.h>
#include <KoShapeController.h>
#include <KoShapeBackground.h>
#include <KoShapeBackgroundCommand.h>
#include <KoShapeStrokeCommand.h>
#include <KoShapeStroke.h>
#include <KoSelectedShapesProxy.h>
#include <KoColorBackground.h>
#include <KoGradientBackground.h>
#include <KoPatternBackground.h>
#include <KoImageCollection.h>
#include <KoResourcePopupAction.h>
#include "KoZoomHandler.h"
#include "KoColorPopupButton.h"
#include "ui_KoFillConfigWidget.h"
#include <kis_signals_blocker.h>
#include <kis_signal_compressor.h>
#include <kis_acyclic_signal_connector.h>
#include <kis_assert.h>
#include "kis_canvas_resource_provider.h"
#include <KoStopGradient.h>
#include <QInputDialog>
#include <KoShapeFillWrapper.h>
#include "kis_global.h"
#include "kis_debug.h"
static const char* const buttonnone[]={
"16 16 3 1",
"# c #000000",
"e c #ff0000",
"- c #ffffff",
"################",
"#--------------#",
"#-e----------e-#",
"#--e--------e--#",
"#---e------e---#",
"#----e----e----#",
"#-----e--e-----#",
"#------ee------#",
"#------ee------#",
"#-----e--e-----#",
"#----e----e----#",
"#---e------e---#",
"#--e--------e--#",
"#-e----------e-#",
"#--------------#",
"################"};
static const char* const buttonsolid[]={
"16 16 2 1",
"# c #000000",
". c #969696",
"################",
"#..............#",
"#..............#",
"#..............#",
"#..............#",
"#..............#",
"#..............#",
"#..............#",
"#..............#",
"#..............#",
"#..............#",
"#..............#",
"#..............#",
"#..............#",
"#..............#",
"################"};
// FIXME: Smoother gradient button.
static const char* const buttongradient[]={
"16 16 15 1",
"# c #000000",
"n c #101010",
"m c #202020",
"l c #303030",
"k c #404040",
"j c #505050",
"i c #606060",
"h c #707070",
"g c #808080",
"f c #909090",
"e c #a0a0a0",
"d c #b0b0b0",
"c c #c0c0c0",
"b c #d0d0d0",
"a c #e0e0e0",
"################",
"#abcdefghijklmn#",
"#abcdefghijklmn#",
"#abcdefghijklmn#",
"#abcdefghijklmn#",
"#abcdefghijklmn#",
"#abcdefghijklmn#",
"#abcdefghijklmn#",
"#abcdefghijklmn#",
"#abcdefghijklmn#",
"#abcdefghijklmn#",
"#abcdefghijklmn#",
"#abcdefghijklmn#",
"#abcdefghijklmn#",
"#abcdefghijklmn#",
"################"};
static const char* const buttonpattern[]={
"16 16 4 1",
". c #0a0a0a",
"# c #333333",
"a c #a0a0a0",
"b c #ffffffff",
"################",
"#aaaaabbbbaaaaa#",
"#aaaaabbbbaaaaa#",
"#aaaaabbbbaaaaa#",
"#aaaaabbbbaaaaa#",
"#aaaaabbbbaaaaa#",
"#bbbbbaaaabbbbb#",
"#bbbbbaaaabbbbb#",
"#bbbbbaaaabbbbb#",
"#bbbbbaaaabbbbb#",
"#aaaaabbbbaaaaa#",
"#aaaaabbbbaaaaa#",
"#aaaaabbbbaaaaa#",
"#aaaaabbbbaaaaa#",
"#aaaaabbbbaaaaa#",
"################"};
class Q_DECL_HIDDEN KoFillConfigWidget::Private
{
public:
Private(KoFlake::FillVariant _fillVariant)
: canvas(0),
colorChangedCompressor(100, KisSignalCompressor::FIRST_ACTIVE),
gradientChangedCompressor(100, KisSignalCompressor::FIRST_ACTIVE),
shapeChangedCompressor(200,KisSignalCompressor::FIRST_ACTIVE),
fillVariant(_fillVariant),
noSelectionTrackingMode(false)
{
}
KoColorPopupAction *colorAction;
KoResourcePopupAction *gradientAction;
KoResourcePopupAction *patternAction;
QButtonGroup *group;
KoCanvasBase *canvas;
KisSignalCompressor colorChangedCompressor;
KisAcyclicSignalConnector shapeChangedAcyclicConnector;
KisAcyclicSignalConnector resourceManagerAcyclicConnector;
KoFillConfigWidget::StyleButton selectedFillIndex {KoFillConfigWidget::None};
- QSharedPointer<KoStopGradient> activeGradient;
+ KoStopGradientSP activeGradient;
KisSignalCompressor gradientChangedCompressor;
KisSignalCompressor shapeChangedCompressor;
KoFlake::FillVariant fillVariant;
bool noSelectionTrackingMode;
Ui_KoFillConfigWidget *ui;
std::vector<KisAcyclicSignalConnector::Blocker> deactivationLocks;
boost::optional<KoColor> overriddenColorFromProvider;
};
KoFillConfigWidget::KoFillConfigWidget(KoCanvasBase *canvas, KoFlake::FillVariant fillVariant, bool trackShapeSelection, QWidget *parent)
: QWidget(parent)
, d(new Private(fillVariant))
{
d->canvas = canvas;
if (trackShapeSelection) {
d->shapeChangedAcyclicConnector.connectBackwardVoid(
d->canvas->selectedShapesProxy(), SIGNAL(selectionChanged()),
&d->shapeChangedCompressor, SLOT(start()));
d->shapeChangedAcyclicConnector.connectBackwardVoid(
d->canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()),
&d->shapeChangedCompressor, SLOT(start()));
connect(&d->shapeChangedCompressor, SIGNAL(timeout()), this, SLOT(shapeChanged()));
}
d->resourceManagerAcyclicConnector.connectBackwardResourcePair(
d->canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
this, SLOT(slotCanvasResourceChanged(int,QVariant)));
d->resourceManagerAcyclicConnector.connectForwardVoid(
this, SIGNAL(sigInternalRequestColorToResourceManager()),
this, SLOT(slotProposeCurrentColorToResourceManager()));
KisAcyclicSignalConnector *resetConnector = d->resourceManagerAcyclicConnector.createCoordinatedConnector();
resetConnector->connectForwardVoid(
this, SIGNAL(sigInternalRecoverColorInResourceManager()),
this, SLOT(slotRecoverColorInResourceManager()));
// configure GUI
d->ui = new Ui_KoFillConfigWidget();
d->ui->setupUi(this);
d->group = new QButtonGroup(this);
d->group->setExclusive(true);
d->ui->btnNoFill->setIcon(QPixmap((const char **) buttonnone));
d->group->addButton(d->ui->btnNoFill, None);
d->ui->btnSolidFill->setIcon(QPixmap((const char **) buttonsolid));
d->group->addButton(d->ui->btnSolidFill, Solid);
d->ui->btnGradientFill->setIcon(QPixmap((const char **) buttongradient));
d->group->addButton(d->ui->btnGradientFill, Gradient);
d->ui->btnPatternFill->setIcon(QPixmap((const char **) buttonpattern));
d->group->addButton(d->ui->btnPatternFill, Pattern);
d->ui->btnPatternFill->setVisible(false);
d->colorAction = new KoColorPopupAction(d->ui->btnChooseSolidColor);
d->colorAction->setToolTip(i18n("Change the filling color"));
d->colorAction->setCurrentColor(Qt::white);
d->ui->btnChooseSolidColor->setDefaultAction(d->colorAction);
d->ui->btnChooseSolidColor->setPopupMode(QToolButton::InstantPopup);
d->ui->btnSolidColorPick->setIcon(KisIconUtils::loadIcon("krita_tool_color_picker"));
// TODO: for now the color picking button is disabled!
d->ui->btnSolidColorPick->setEnabled(false);
d->ui->btnSolidColorPick->setVisible(false);
connect(d->colorAction, SIGNAL(colorChanged(KoColor)), &d->colorChangedCompressor, SLOT(start()));
connect(&d->colorChangedCompressor, SIGNAL(timeout()), SLOT(colorChanged()));
connect(d->ui->btnChooseSolidColor, SIGNAL(iconSizeChanged()), d->colorAction, SLOT(updateIcon()));
connect(d->group, SIGNAL(buttonClicked(int)), SLOT(styleButtonPressed(int)));
connect(d->group, SIGNAL(buttonClicked(int)), SLOT(slotUpdateFillTitle()));
slotUpdateFillTitle();
styleButtonPressed(d->group->checkedId());
// Gradient selector
d->ui->wdgGradientEditor->setCompactMode(true);
connect(d->ui->wdgGradientEditor, SIGNAL(sigGradientChanged()), &d->gradientChangedCompressor, SLOT(start()));
connect(&d->gradientChangedCompressor, SIGNAL(timeout()), SLOT(activeGradientChanged()));
- KoResourceServerProvider *serverProvider = KoResourceServerProvider::instance();
- QSharedPointer<KoAbstractResourceServerAdapter> gradientResourceAdapter(
- new KoResourceServerAdapter<KoAbstractGradient>(serverProvider->gradientServer()));
-
- d->gradientAction = new KoResourcePopupAction(gradientResourceAdapter,
- d->ui->btnChoosePredefinedGradient);
+ d->gradientAction = new KoResourcePopupAction(ResourceType::Gradients, d->ui->btnChoosePredefinedGradient);
d->gradientAction->setToolTip(i18n("Change filling gradient"));
d->ui->btnChoosePredefinedGradient->setDefaultAction(d->gradientAction);
d->ui->btnChoosePredefinedGradient->setPopupMode(QToolButton::InstantPopup);
connect(d->gradientAction, SIGNAL(resourceSelected(QSharedPointer<KoShapeBackground>)),
SLOT(gradientResourceChanged()));
connect(d->ui->btnChoosePredefinedGradient, SIGNAL(iconSizeChanged()), d->gradientAction, SLOT(updateIcon()));
d->ui->btnSaveGradient->setIcon(KisIconUtils::loadIcon("document-save"));
connect(d->ui->btnSaveGradient, SIGNAL(clicked()), SLOT(slotSavePredefinedGradientClicked()));
connect(d->ui->cmbGradientRepeat, SIGNAL(currentIndexChanged(int)), SLOT(slotGradientRepeatChanged()));
connect(d->ui->cmbGradientType, SIGNAL(currentIndexChanged(int)), SLOT(slotGradientTypeChanged()));
// initialize deactivation locks
d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->shapeChangedAcyclicConnector));
d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->resourceManagerAcyclicConnector));
-#if 0
-
+/*
// Pattern selector
- QSharedPointer<KoAbstractResourceServerAdapter>patternResourceAdapter(new KoResourceServerAdapter<KoPattern>(serverProvider->patternServer()));
- d->patternAction = new KoResourcePopupAction(patternResourceAdapter, d->colorButton);
+ d->patternAction = new KoResourcePopupAction(ResourceType::Patterns, d->colorButton);
d->patternAction->setToolTip(i18n("Change the filling pattern"));
connect(d->patternAction, SIGNAL(resourceSelected(QSharedPointer<KoShapeBackground>)), this, SLOT(patternChanged(QSharedPointer<KoShapeBackground>)));
connect(d->colorButton, SIGNAL(iconSizeChanged()), d->patternAction, SLOT(updateIcon()));
+*/
-#endif
}
KoFillConfigWidget::~KoFillConfigWidget()
{
delete d;
}
void KoFillConfigWidget::activate()
{
KIS_SAFE_ASSERT_RECOVER_NOOP(!d->deactivationLocks.empty());
d->deactivationLocks.clear();
if (!d->noSelectionTrackingMode) {
d->shapeChangedCompressor.start();
} else {
loadCurrentFillFromResourceServer();
}
updateWidgetComponentVisbility();
}
void KoFillConfigWidget::deactivate()
{
emit sigInternalRecoverColorInResourceManager();
KIS_SAFE_ASSERT_RECOVER_NOOP(d->deactivationLocks.empty());
d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->shapeChangedAcyclicConnector));
d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->resourceManagerAcyclicConnector));
}
void KoFillConfigWidget::forceUpdateOnSelectionChanged()
{
d->shapeChangedCompressor.start();
}
void KoFillConfigWidget::setNoSelectionTrackingMode(bool value)
{
d->noSelectionTrackingMode = value;
if (!d->noSelectionTrackingMode) {
d->shapeChangedCompressor.start();
}
}
void KoFillConfigWidget::slotUpdateFillTitle()
{
QString text = d->group->checkedButton() ? d->group->checkedButton()->text() : QString();
text.replace('&', QString());
d->ui->lblFillTitle->setText(text);
}
void KoFillConfigWidget::slotCanvasResourceChanged(int key, const QVariant &value)
{
if ((key == KoCanvasResourceProvider::ForegroundColor && d->fillVariant == KoFlake::Fill) ||
(key == KoCanvasResourceProvider::BackgroundColor &&
d->fillVariant == KoFlake::StrokeFill && !d->noSelectionTrackingMode) ||
(key == KoCanvasResourceProvider::ForegroundColor && d->noSelectionTrackingMode)) {
KoColor color = value.value<KoColor>();
const int checkedId = d->group->checkedId();
if ((checkedId < 0 || checkedId == None || checkedId == Solid) &&
!(checkedId == Solid && d->colorAction->currentKoColor() == color)) {
d->group->button(Solid)->setChecked(true);
d->selectedFillIndex = Solid;
d->colorAction->setCurrentColor(color);
d->colorChangedCompressor.start();
} else if (checkedId == Gradient && key == KoCanvasResourceProvider::ForegroundColor) {
d->ui->wdgGradientEditor->notifyGlobalColorChanged(color);
}
} else if (key == KisCanvasResourceProvider::CurrentGradient) {
- KoResource *gradient = value.value<KoAbstractGradient*>();
+ KoResourceSP gradient = value.value<KoAbstractGradientSP>();
const int checkedId = d->group->checkedId();
if (gradient && (checkedId < 0 || checkedId == None || checkedId == Gradient)) {
d->group->button(Gradient)->setChecked(true);
d->gradientAction->setCurrentResource(gradient);
}
}
}
QList<KoShape*> KoFillConfigWidget::currentShapes()
{
return d->canvas->selectedShapesProxy()->selection()->selectedEditableShapes();
}
int KoFillConfigWidget::selectedFillIndex() {
return d->selectedFillIndex;
}
void KoFillConfigWidget::styleButtonPressed(int buttonId)
{
QList<KoShape*> shapes = currentShapes();
switch (buttonId) {
case KoFillConfigWidget::None:
noColorSelected();
break;
case KoFillConfigWidget::Solid:
colorChanged();
break;
case KoFillConfigWidget::Gradient:
if (d->activeGradient) {
setNewGradientBackgroundToShape();
updateGradientSaveButtonAvailability();
} else {
gradientResourceChanged();
}
break;
case KoFillConfigWidget::Pattern:
// Only select mode in the widget, don't set actual pattern :/
//d->colorButton->setDefaultAction(d->patternAction);
//patternChanged(d->patternAction->currentBackground());
break;
}
// update tool option fields with first selected object
if (shapes.isEmpty() == false) {
KoShape *firstShape = shapes.first();
updateFillIndexFromShape(firstShape);
updateFillColorFromShape(firstShape);
}
updateWidgetComponentVisbility();
}
KoShapeStrokeSP KoFillConfigWidget::createShapeStroke()
{
KoShapeStrokeSP stroke(new KoShapeStroke());
KIS_ASSERT_RECOVER_RETURN_VALUE(d->fillVariant == KoFlake::StrokeFill, stroke);
switch (d->group->checkedId()) {
case KoFillConfigWidget::None:
stroke->setColor(Qt::transparent);
break;
case KoFillConfigWidget::Solid:
stroke->setColor(d->colorAction->currentColor());
break;
case KoFillConfigWidget::Gradient: {
QScopedPointer<QGradient> g(d->activeGradient->toQGradient());
QBrush newBrush = *g;
stroke->setLineBrush(newBrush);
stroke->setColor(Qt::transparent);
break;
}
case KoFillConfigWidget::Pattern:
break;
}
return stroke;
}
void KoFillConfigWidget::noColorSelected()
{
KisAcyclicSignalConnector::Blocker b(d->shapeChangedAcyclicConnector);
QList<KoShape*> selectedShapes = currentShapes();
if (selectedShapes.isEmpty()) {
emit sigFillChanged();
return;
}
KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant);
KUndo2Command *command = wrapper.setColor(QColor());
if (command) {
d->canvas->addCommand(command);
}
if (d->fillVariant == KoFlake::StrokeFill) {
KUndo2Command *lineCommand = wrapper.setLineWidth(0.0);
if (lineCommand) {
d->canvas->addCommand(lineCommand);
}
}
emit sigFillChanged();
}
void KoFillConfigWidget::colorChanged()
{
KisAcyclicSignalConnector::Blocker b(d->shapeChangedAcyclicConnector);
QList<KoShape*> selectedShapes = currentShapes();
if (selectedShapes.isEmpty()) {
emit sigInternalRequestColorToResourceManager();
emit sigFillChanged();
return;
}
d->overriddenColorFromProvider = boost::none;
KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant);
KUndo2Command *command = wrapper.setColor(d->colorAction->currentColor());
if (command) {
d->canvas->addCommand(command);
}
// only returns true if it is a stroke object that has a 0 for line width
if (wrapper.hasZeroLineWidth() ) {
KUndo2Command *lineCommand = wrapper.setLineWidth(1.0);
if (lineCommand) {
d->canvas->addCommand(lineCommand);
}
// * line to test out
QColor solidColor = d->colorAction->currentColor();
solidColor.setAlpha(255);
command = wrapper.setColor(solidColor);
if (command) {
d->canvas->addCommand(command);
}
}
d->colorAction->setCurrentColor(wrapper.color());
emit sigFillChanged();
emit sigInternalRequestColorToResourceManager();
}
void KoFillConfigWidget::slotProposeCurrentColorToResourceManager()
{
const int checkedId = d->group->checkedId();
bool hasColor = false;
KoColor color;
KoCanvasResourceProvider::CanvasResource colorSlot = KoCanvasResourceProvider::ForegroundColor;
if (checkedId == Solid) {
if (d->fillVariant == KoFlake::StrokeFill) {
colorSlot = KoCanvasResourceProvider::BackgroundColor;
}
color = d->colorAction->currentKoColor();
hasColor = true;
} else if (checkedId == Gradient) {
if (boost::optional<KoColor> gradientColor = d->ui->wdgGradientEditor->currentActiveStopColor()) {
color = *gradientColor;
hasColor = true;
}
}
if (hasColor) {
if (!d->overriddenColorFromProvider) {
d->overriddenColorFromProvider =
d->canvas->resourceManager()->resource(colorSlot).value<KoColor>();
}
/**
* Don't let opacity leak to our resource manager system
*
* NOTE: theoretically, we could guarantee it on a level of the
* resource manager itself,
*/
color.setOpacity(OPACITY_OPAQUE_U8);
d->canvas->resourceManager()->setResource(colorSlot, QVariant::fromValue(color));
}
}
void KoFillConfigWidget::slotRecoverColorInResourceManager()
{
if (d->overriddenColorFromProvider) {
KoCanvasResourceProvider::CanvasResource colorSlot = KoCanvasResourceProvider::ForegroundColor;
if (d->fillVariant == KoFlake::StrokeFill) {
colorSlot = KoCanvasResourceProvider::BackgroundColor;
}
d->canvas->resourceManager()->setResource(colorSlot, QVariant::fromValue(*d->overriddenColorFromProvider));
d->overriddenColorFromProvider = boost::none;
}
}
-template <class ResourceServer>
-QString findFirstAvailableResourceName(const QString &baseName, ResourceServer *server)
-{
- if (!server->resourceByName(baseName)) return baseName;
-
- int counter = 1;
- QString result;
- while ((result = QString("%1%2").arg(baseName).arg(counter)),
- server->resourceByName(result)) {
-
- counter++;
- }
-
- return result;
-}
-
-
void KoFillConfigWidget::slotSavePredefinedGradientClicked()
{
KoResourceServerProvider *serverProvider = KoResourceServerProvider::instance();
auto server = serverProvider->gradientServer();
const QString defaultGradientNamePrefix = i18nc("default prefix for the saved gradient", "gradient");
+ const QString saveLocation = server->saveLocation();
QString name = d->activeGradient->name().isEmpty() ? defaultGradientNamePrefix : d->activeGradient->name();
- name = findFirstAvailableResourceName(name, server);
- name = QInputDialog::getText(this, i18nc("@title:window", "Save Gradient"), i18n("Enter gradient name:"), QLineEdit::Normal, name);
-
- // TODO: currently we do not allow the user to
- // create two resources with the same name!
- // Please add some feedback for it!
- name = findFirstAvailableResourceName(name, server);
+ QFileInfo fileInfo(saveLocation + name.split(" ").join("_") + d->activeGradient->defaultFileExtension());
+ bool fileOverWriteAccepted = false;
+
+ while(!fileOverWriteAccepted) {
+ name = QInputDialog::getText(this,
+ i18nc("@title:window", "Save Gradient"),
+ i18n("Enter gradient name:"),
+ QLineEdit::Normal, name);
+ if (name.isNull() || name.isEmpty()) {
+ return;
+ } else {
+ fileInfo = QFileInfo(saveLocation + name.split(" ").join("_") + d->activeGradient->defaultFileExtension());
+ if (fileInfo.exists()) {
+ int res = QMessageBox::warning(this, i18nc("@title:window", "Name Already Exists")
+ , i18n("The name '%1' already exists, do you wish to overwrite it?", name)
+ , QMessageBox::Yes | QMessageBox::No);
+ if (res == QMessageBox::Yes) fileOverWriteAccepted = true;
+ } else {
+ fileOverWriteAccepted = true;
+ }
+ }
+ }
d->activeGradient->setName(name);
+ d->activeGradient->setFilename(name.split(" ").join("_") + d->activeGradient->defaultFileExtension());
- const QString saveLocation = server->saveLocation();
- d->activeGradient->setFilename(saveLocation + d->activeGradient->name() + d->activeGradient->defaultFileExtension());
-
- KoAbstractGradient *newGradient = d->activeGradient->clone();
+ KoAbstractGradientSP newGradient = d->activeGradient->clone().dynamicCast<KoAbstractGradient>();
server->addResource(newGradient);
d->gradientAction->setCurrentResource(newGradient);
}
void KoFillConfigWidget::activeGradientChanged()
{
setNewGradientBackgroundToShape();
updateGradientSaveButtonAvailability();
emit sigInternalRequestColorToResourceManager();
}
void KoFillConfigWidget::gradientResourceChanged()
{
QSharedPointer<KoGradientBackground> bg =
qSharedPointerDynamicCast<KoGradientBackground>(
d->gradientAction->currentBackground());
uploadNewGradientBackground(bg->gradient());
setNewGradientBackgroundToShape();
updateGradientSaveButtonAvailability();
}
void KoFillConfigWidget::slotGradientTypeChanged()
{
QGradient::Type type =
d->ui->cmbGradientType->currentIndex() == 0 ?
QGradient::LinearGradient : QGradient::RadialGradient;
d->activeGradient->setType(type);
activeGradientChanged();
}
void KoFillConfigWidget::slotGradientRepeatChanged()
{
QGradient::Spread spread =
QGradient::Spread(d->ui->cmbGradientRepeat->currentIndex());
d->activeGradient->setSpread(spread);
activeGradientChanged();
}
void KoFillConfigWidget::uploadNewGradientBackground(const QGradient *gradient)
{
KisSignalsBlocker b1(d->ui->wdgGradientEditor,
d->ui->cmbGradientType,
d->ui->cmbGradientRepeat);
d->ui->wdgGradientEditor->setGradient(0);
- d->activeGradient.reset(KoStopGradient::fromQGradient(gradient));
+ d->activeGradient = KoStopGradient::fromQGradient(gradient);
- d->ui->wdgGradientEditor->setGradient(d->activeGradient.data());
+ d->ui->wdgGradientEditor->setGradient(d->activeGradient);
d->ui->cmbGradientType->setCurrentIndex(d->activeGradient->type() != QGradient::LinearGradient);
d->ui->cmbGradientRepeat->setCurrentIndex(int(d->activeGradient->spread()));
}
void KoFillConfigWidget::setNewGradientBackgroundToShape()
{
QList<KoShape*> selectedShapes = currentShapes();
if (selectedShapes.isEmpty()) {
emit sigFillChanged();
return;
}
KisAcyclicSignalConnector::Blocker b(d->shapeChangedAcyclicConnector);
KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant);
QScopedPointer<QGradient> srcQGradient(d->activeGradient->toQGradient());
KUndo2Command *command = wrapper.applyGradientStopsOnly(srcQGradient.data());
if (command) {
d->canvas->addCommand(command);
}
emit sigFillChanged();
}
void KoFillConfigWidget::updateGradientSaveButtonAvailability()
{
bool savingEnabled = false;
QScopedPointer<QGradient> currentGradient(d->activeGradient->toQGradient());
QSharedPointer<KoShapeBackground> bg = d->gradientAction->currentBackground();
if (bg) {
QSharedPointer<KoGradientBackground> resourceBackground =
qSharedPointerDynamicCast<KoGradientBackground>(bg);
savingEnabled = resourceBackground->gradient()->stops() != currentGradient->stops();
savingEnabled |= resourceBackground->gradient()->type() != currentGradient->type();
savingEnabled |= resourceBackground->gradient()->spread() != currentGradient->spread();
}
d->ui->btnSaveGradient->setEnabled(savingEnabled);
}
void KoFillConfigWidget::patternChanged(QSharedPointer<KoShapeBackground> background)
{
Q_UNUSED(background);
#if 0
QSharedPointer<KoPatternBackground> patternBackground = qSharedPointerDynamicCast<KoPatternBackground>(background);
if (! patternBackground) {
return;
}
QList<KoShape*> selectedShapes = currentShapes();
if (selectedShapes.isEmpty()) {
return;
}
KoImageCollection *imageCollection = d->canvas->shapeController()->resourceManager()->imageCollection();
if (imageCollection) {
QSharedPointer<KoPatternBackground> fill(new KoPatternBackground(imageCollection));
fill->setPattern(patternBackground->pattern());
d->canvas->addCommand(new KoShapeBackgroundCommand(selectedShapes, fill));
}
#endif
}
void KoFillConfigWidget::loadCurrentFillFromResourceServer()
{
{
KoColor color = d->canvas->resourceManager()->backgroundColor();
slotCanvasResourceChanged(KoCanvasResourceProvider::BackgroundColor, QVariant::fromValue(color));
}
{
KoColor color = d->canvas->resourceManager()->foregroundColor();
slotCanvasResourceChanged(KoCanvasResourceProvider::ForegroundColor, QVariant::fromValue(color));
}
Q_FOREACH (QAbstractButton *button, d->group->buttons()) {
button->setEnabled(true);
}
emit sigFillChanged();
}
void KoFillConfigWidget::shapeChanged()
{
if (d->noSelectionTrackingMode) return;
QList<KoShape*> shapes = currentShapes();
bool shouldUploadColorToResourceManager = false;
if (shapes.isEmpty() ||
(shapes.size() > 1 && KoShapeFillWrapper(shapes, d->fillVariant).isMixedFill())) {
Q_FOREACH (QAbstractButton *button, d->group->buttons()) {
button->setEnabled(!shapes.isEmpty());
}
} else {
// only one vector object selected
Q_FOREACH (QAbstractButton *button, d->group->buttons()) {
button->setEnabled(true);
}
// update active index of shape
KoShape *shape = shapes.first();
updateFillIndexFromShape(shape);
updateFillColorFromShape(shape); // updates tool options fields
shouldUploadColorToResourceManager = true;
}
// updates the UI
d->group->button(d->selectedFillIndex)->setChecked(true);
updateWidgetComponentVisbility();
slotUpdateFillTitle();
if (shouldUploadColorToResourceManager) {
emit sigInternalRequestColorToResourceManager();
} else {
emit sigInternalRecoverColorInResourceManager();
}
}
void KoFillConfigWidget::updateFillIndexFromShape(KoShape *shape)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(shape);
KoShapeFillWrapper wrapper(shape, d->fillVariant);
switch (wrapper.type()) {
case KoFlake::None:
d->selectedFillIndex = KoFillConfigWidget::None;
break;
case KoFlake::Solid:
d->selectedFillIndex = KoFillConfigWidget::Solid;
break;
case KoFlake::Gradient:
d->selectedFillIndex = KoFillConfigWidget::Gradient;
break;
case KoFlake::Pattern:
d->selectedFillIndex = KoFillConfigWidget::Pattern;
break;
}
}
void KoFillConfigWidget::updateFillColorFromShape(KoShape *shape)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(shape);
KoShapeFillWrapper wrapper(shape, d->fillVariant);
switch (wrapper.type()) {
case KoFlake::None:
break;
case KoFlake::Solid: {
QColor color = wrapper.color();
if (color.alpha() > 0) {
d->colorAction->setCurrentColor(wrapper.color());
}
break;
}
case KoFlake::Gradient:
uploadNewGradientBackground(wrapper.gradient());
updateGradientSaveButtonAvailability();
break;
case KoFlake::Pattern:
break;
}
}
void KoFillConfigWidget::updateWidgetComponentVisbility()
{
// The UI is showing/hiding things like this because the 'stacked widget' isn't very flexible
// and makes it difficult to put anything underneath it without a lot empty space
// hide everything first
d->ui->wdgGradientEditor->setVisible(false);
d->ui->btnChoosePredefinedGradient->setVisible(false);
d->ui->btnChooseSolidColor->setVisible(false);
d->ui->typeLabel->setVisible(false);
d->ui->repeatLabel->setVisible(false);
d->ui->cmbGradientRepeat->setVisible(false);
d->ui->cmbGradientType->setVisible(false);
d->ui->btnSolidColorPick->setVisible(false);
d->ui->btnSaveGradient->setVisible(false);
d->ui->gradientTypeLine->setVisible(false);
d->ui->soldStrokeColorLabel->setVisible(false);
d->ui->presetLabel->setVisible(false);
// keep options hidden if no vector shapes are selected
if(currentShapes().isEmpty()) {
return;
}
switch (d->selectedFillIndex) {
case KoFillConfigWidget::None:
break;
case KoFillConfigWidget::Solid:
d->ui->btnChooseSolidColor->setVisible(true);
d->ui->btnSolidColorPick->setVisible(false);
d->ui->soldStrokeColorLabel->setVisible(true);
break;
case KoFillConfigWidget::Gradient:
d->ui->wdgGradientEditor->setVisible(true);
d->ui->btnChoosePredefinedGradient->setVisible(true);
d->ui->typeLabel->setVisible(true);
d->ui->repeatLabel->setVisible(true);
d->ui->cmbGradientRepeat->setVisible(true);
d->ui->cmbGradientType->setVisible(true);
d->ui->btnSaveGradient->setVisible(true);
d->ui->gradientTypeLine->setVisible(true);
d->ui->presetLabel->setVisible(true);
break;
case KoFillConfigWidget::Pattern:
break;
}
}
diff --git a/libs/ui/widgets/kis_cie_tongue_widget.cpp b/libs/ui/widgets/kis_cie_tongue_widget.cpp
index 79a8bfc684..997dc5f82f 100644
--- a/libs/ui/widgets/kis_cie_tongue_widget.cpp
+++ b/libs/ui/widgets/kis_cie_tongue_widget.cpp
@@ -1,732 +1,732 @@
/*
* Copyright (C) 2015 by Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
*
* Based on the Digikam CIE Tongue widget
* Copyright (C) 2006-2013 by Gilles Caulier <caulier dot gilles at gmail dot com>
*
* Any source code are inspired from lprof project and
* Copyright (C) 1998-2001 Marti Maria
*
* 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.
*
* 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.
**/
/**
The following table gives the CIE color matching functions
\f$\bar{x}(\lambda)\f$, \f$\bar{y}(\lambda)\f$, and
\f$\bar{z}(\lambda)\f$, for wavelengths \f$\lambda\f$ at 5 nanometer
increments from 380 nm through 780 nm. This table is used in conjunction
with Planck's law for the energy spectrum of a black body at a given
temperature to plot the black body curve on the CIE chart.
The following table gives the spectral chromaticity co-ordinates
\f$x(\lambda)\f$ and \f$y(\lambda)\f$ for wavelengths in 5 nanometer
increments from 380 nm through 780 nm. These coordinates represent the
position in the CIE x-y space of pure spectral colors of the given
wavelength, and thus define the outline of the CIE "tongue" diagram.
*/
#include <QPointF>
#include <QPainter>
#include <QFile>
#include <QTimer>
#include <QPaintEvent>
#include <QImage>
#include <cmath>
#include <klocalizedstring.h>
#include <kpixmapsequence.h>
#include <kis_icon.h>
#include <KoColorSpaceRegistry.h>
#include "kis_cie_tongue_widget.h"
static const double spectral_chromaticity[81][3] =
{
{ 0.1741, 0.0050 }, // 380 nm
{ 0.1740, 0.0050 },
{ 0.1738, 0.0049 },
{ 0.1736, 0.0049 },
{ 0.1733, 0.0048 },
{ 0.1730, 0.0048 },
{ 0.1726, 0.0048 },
{ 0.1721, 0.0048 },
{ 0.1714, 0.0051 },
{ 0.1703, 0.0058 },
{ 0.1689, 0.0069 },
{ 0.1669, 0.0086 },
{ 0.1644, 0.0109 },
{ 0.1611, 0.0138 },
{ 0.1566, 0.0177 },
{ 0.1510, 0.0227 },
{ 0.1440, 0.0297 },
{ 0.1355, 0.0399 },
{ 0.1241, 0.0578 },
{ 0.1096, 0.0868 },
{ 0.0913, 0.1327 },
{ 0.0687, 0.2007 },
{ 0.0454, 0.2950 },
{ 0.0235, 0.4127 },
{ 0.0082, 0.5384 },
{ 0.0039, 0.6548 },
{ 0.0139, 0.7502 },
{ 0.0389, 0.8120 },
{ 0.0743, 0.8338 },
{ 0.1142, 0.8262 },
{ 0.1547, 0.8059 },
{ 0.1929, 0.7816 },
{ 0.2296, 0.7543 },
{ 0.2658, 0.7243 },
{ 0.3016, 0.6923 },
{ 0.3373, 0.6589 },
{ 0.3731, 0.6245 },
{ 0.4087, 0.5896 },
{ 0.4441, 0.5547 },
{ 0.4788, 0.5202 },
{ 0.5125, 0.4866 },
{ 0.5448, 0.4544 },
{ 0.5752, 0.4242 },
{ 0.6029, 0.3965 },
{ 0.6270, 0.3725 },
{ 0.6482, 0.3514 },
{ 0.6658, 0.3340 },
{ 0.6801, 0.3197 },
{ 0.6915, 0.3083 },
{ 0.7006, 0.2993 },
{ 0.7079, 0.2920 },
{ 0.7140, 0.2859 },
{ 0.7190, 0.2809 },
{ 0.7230, 0.2770 },
{ 0.7260, 0.2740 },
{ 0.7283, 0.2717 },
{ 0.7300, 0.2700 },
{ 0.7311, 0.2689 },
{ 0.7320, 0.2680 },
{ 0.7327, 0.2673 },
{ 0.7334, 0.2666 },
{ 0.7340, 0.2660 },
{ 0.7344, 0.2656 },
{ 0.7346, 0.2654 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 },
{ 0.7347, 0.2653 } // 780 nm
};
class Q_DECL_HIDDEN KisCIETongueWidget::Private
{
public:
Private() :
profileDataAvailable(false),
needUpdatePixmap(false),
cieTongueNeedsUpdate(true),
uncalibratedColor(false),
xBias(0),
yBias(0),
pxcols(0),
pxrows(0),
progressCount(0),
gridside(0),
progressTimer(0),
Primaries(9),
whitePoint(3)
{
progressPix = KPixmapSequence("process-working", KisIconUtils::SizeSmallMedium);
}
bool profileDataAvailable;
bool needUpdatePixmap;
bool cieTongueNeedsUpdate;
bool uncalibratedColor;
int xBias;
int yBias;
int pxcols;
int pxrows;
int progressCount; // Position of animation during loading/calculation.
double gridside;
QPainter painter;
QTimer* progressTimer;
QPixmap pixmap;
QPixmap cietongue;
QPixmap gamutMap;
KPixmapSequence progressPix;
QVector <double> Primaries;
QVector <double> whitePoint;
QPolygonF gamut;
model colorModel;
};
KisCIETongueWidget::KisCIETongueWidget(QWidget *parent) :
QWidget(parent), d(new Private)
{
d->progressTimer = new QTimer(this);
setAttribute(Qt::WA_DeleteOnClose);
d->Primaries.resize(9);
d->Primaries.fill(0.0);
d->whitePoint.resize(3);
d->whitePoint<<0.34773<<0.35952<<1.0;
d->gamut = QPolygonF();
connect(d->progressTimer, SIGNAL(timeout()),
this, SLOT(slotProgressTimerDone()));
}
KisCIETongueWidget::~KisCIETongueWidget()
{
delete d;
}
int KisCIETongueWidget::grids(double val) const
{
return (int) floor(val * d->gridside + 0.5);
}
void KisCIETongueWidget::setProfileData(QVector <double> p, QVector <double> w, bool profileData)
{
d->profileDataAvailable = profileData;
if (profileData){
d->Primaries= p;
d->whitePoint = w;
d->needUpdatePixmap = true;
} else {
return;
}
}
void KisCIETongueWidget::setGamut(QPolygonF gamut)
{
d->gamut=gamut;
}
void KisCIETongueWidget::setRGBData(QVector <double> whitepoint, QVector <double> colorants)
{
if (colorants.size()==9){
d->Primaries= colorants;
d->whitePoint = whitepoint;
d->needUpdatePixmap = true;
d->colorModel = KisCIETongueWidget::RGBA;
d->profileDataAvailable = true;
} else {
return;
}
}
void KisCIETongueWidget::setCMYKData(QVector <double> whitepoint)
{
if (whitepoint.size()==3){
//d->Primaries= colorants;
d->whitePoint = whitepoint;
d->needUpdatePixmap = true;
d->colorModel = KisCIETongueWidget::CMYKA;
d->profileDataAvailable = true;
} else {
return;
}
}
void KisCIETongueWidget::setXYZData(QVector <double> whitepoint)
{
if (whitepoint.size()==3){
d->whitePoint = whitepoint;
d->needUpdatePixmap = true;
d->colorModel = KisCIETongueWidget::XYZA;
d->profileDataAvailable = true;
} else {
return;
}
}
void KisCIETongueWidget::setGrayData(QVector <double> whitepoint)
{
if (whitepoint.size()==3){
d->whitePoint = whitepoint;
d->needUpdatePixmap = true;
d->colorModel = KisCIETongueWidget::GRAYA;
d->profileDataAvailable = true;
} else {
return;
}
}
void KisCIETongueWidget::setLABData(QVector <double> whitepoint)
{
if (whitepoint.size()==3){
d->whitePoint = whitepoint;
d->needUpdatePixmap = true;
d->colorModel = KisCIETongueWidget::LABA;
d->profileDataAvailable = true;
} else {
return;
}
}
void KisCIETongueWidget::setYCbCrData(QVector <double> whitepoint)
{
if (whitepoint.size()==3){
d->whitePoint = whitepoint;
d->needUpdatePixmap = true;
d->colorModel = KisCIETongueWidget::YCbCrA;
d->profileDataAvailable = true;
} else {
return;
}
}
void KisCIETongueWidget::setProfileDataAvailable(bool dataAvailable)
{
d->profileDataAvailable = dataAvailable;
}
void KisCIETongueWidget::mapPoint(int& icx, int& icy, QPointF xy)
{
icx = (int) floor((xy.x() * (d->pxcols - 1)) + .5);
icy = (int) floor(((d->pxrows - 1) - xy.y() * (d->pxrows - 1)) + .5);
}
void KisCIETongueWidget::biasedLine(int x1, int y1, int x2, int y2)
{
d->painter.drawLine(x1 + d->xBias, y1, x2 + d->xBias, y2);
}
void KisCIETongueWidget::biasedText(int x, int y, const QString& txt)
{
d->painter.drawText(QPoint(d->xBias + x, y), txt);
}
QRgb KisCIETongueWidget::colorByCoord(double x, double y)
{
// Get xyz components scaled from coordinates
double cx = ((double) x) / (d->pxcols - 1);
double cy = 1.0 - ((double) y) / (d->pxrows - 1);
double cz = 1.0 - cx - cy;
// Project xyz to XYZ space. Note that in this
// particular case we are substituting XYZ with xyz
//Need to use KoColor here.
const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "U8");
quint8 data[4];
data[0]= cx*255;
data[1]= cy*255;
data[2]= cz*255;
data[3]= 1.0*255;
KoColor colXYZ(data, xyzColorSpace);
QColor colRGB = colXYZ.toQColor();
return qRgb(colRGB.red(), colRGB.green(), colRGB.blue());
}
void KisCIETongueWidget::outlineTongue()
{
int lx=0, ly=0;
int fx=0, fy=0;
for (int x = 380; x <= 700; x += 5) {
int ix = (x - 380) / 5;
QPointF p(spectral_chromaticity[ix][0], spectral_chromaticity[ix][1]);
int icx, icy;
mapPoint(icx, icy, p);
if (x > 380) {
biasedLine(lx, ly, icx, icy);
}
else {
fx = icx;
fy = icy;
}
lx = icx;
ly = icy;
}
biasedLine(lx, ly, fx, fy);
}
void KisCIETongueWidget::fillTongue()
{
QImage Img = d->cietongue.toImage();
int x;
for (int y = 0; y < d->pxrows; ++y)
{
int xe = 0;
// Find horizontal extents of tongue on this line.
for (x = 0; x < d->pxcols; ++x)
{
if (QColor(Img.pixel(x + d->xBias, y)) != QColor(Qt::black))
{
for (xe = d->pxcols - 1; xe >= x; --xe)
{
if (QColor(Img.pixel(xe + d->xBias, y)) != QColor(Qt::black))
{
break;
}
}
break;
}
}
if (x < d->pxcols)
{
for ( ; x <= xe; ++x)
{
QRgb Color = colorByCoord(x, y);
Img.setPixel(x + d->xBias, y, Color);
}
}
}
d->cietongue = QPixmap::fromImage(Img, Qt::AvoidDither);
}
void KisCIETongueWidget::drawTongueAxis()
{
QFont font;
font.setPointSize(6);
d->painter.setFont(font);
d->painter.setPen(qRgb(255, 255, 255));
biasedLine(0, 0, 0, d->pxrows - 1);
biasedLine(0, d->pxrows-1, d->pxcols-1, d->pxrows - 1);
for (int y = 1; y <= 9; y += 1)
{
QString s;
int xstart = (y * (d->pxcols - 1)) / 10;
int ystart = (y * (d->pxrows - 1)) / 10;
- s.sprintf("0.%d", y);
+ QTextStream(&s) << y;
biasedLine(xstart, d->pxrows - grids(1), xstart, d->pxrows - grids(4));
biasedText(xstart - grids(11), d->pxrows + grids(15), s);
- s.sprintf("0.%d", 10 - y);
+ QTextStream(&s) << 10 - y;
biasedLine(0, ystart, grids(3), ystart);
biasedText(grids(-25), ystart + grids(5), s);
}
}
void KisCIETongueWidget::drawTongueGrid()
{
d->painter.setPen(qRgb(128, 128, 128));
d->painter.setOpacity(0.5);
for (int y = 1; y <= 9; y += 1)
{
int xstart = (y * (d->pxcols - 1)) / 10;
int ystart = (y * (d->pxrows - 1)) / 10;
biasedLine(xstart, grids(4), xstart, d->pxrows - grids(4) - 1);
biasedLine(grids(7), ystart, d->pxcols-1-grids(7), ystart);
}
d->painter.setOpacity(1.0);
}
void KisCIETongueWidget::drawLabels()
{
QFont font;
font.setPointSize(5);
d->painter.setFont(font);
for (int x = 450; x <= 650; x += (x > 470 && x < 600) ? 5 : 10)
{
QString wl;
int bx = 0, by = 0, tx, ty;
if (x < 520)
{
bx = grids(-22);
by = grids(2);
}
else if (x < 535)
{
bx = grids(-8);
by = grids(-6);
}
else
{
bx = grids(4);
}
int ix = (x - 380) / 5;
QPointF p(spectral_chromaticity[ix][0], spectral_chromaticity[ix][1]);
int icx, icy;
mapPoint(icx, icy, p);
tx = icx + ((x < 520) ? grids(-2) : ((x >= 535) ? grids(2) : 0));
ty = icy + ((x < 520) ? 0 : ((x >= 535) ? grids(-1) : grids(-2)));
d->painter.setPen(qRgb(255, 255, 255));
biasedLine(icx, icy, tx, ty);
QRgb Color = colorByCoord(icx, icy);
d->painter.setPen(Color);
- wl.sprintf("%d", x);
+ QTextStream(&wl) << x;
biasedText(icx+bx, icy+by, wl);
}
}
void KisCIETongueWidget::drawSmallEllipse(QPointF xy, int r, int g, int b, int sz)
{
int icx, icy;
mapPoint(icx, icy, xy);
d->painter.save();
d->painter.setRenderHint(QPainter::Antialiasing);
d->painter.setPen(qRgb(r, g, b));
d->painter.drawEllipse(icx + d->xBias- sz/2, icy-sz/2, sz, sz);
d->painter.setPen(qRgb(r/2, g/2, b/2));
int sz2 = sz-2;
d->painter.drawEllipse(icx + d->xBias- sz2/2, icy-sz2/2, sz2, sz2);
d->painter.restore();
}
void KisCIETongueWidget::drawColorantTriangle()
{
d->painter.save();
d->painter.setPen(qRgb(80, 80, 80));
d->painter.setRenderHint(QPainter::Antialiasing);
if (d->colorModel ==KisCIETongueWidget::RGBA) {
drawSmallEllipse((QPointF(d->Primaries[0],d->Primaries[1])), 255, 128, 128, 6);
drawSmallEllipse((QPointF(d->Primaries[3],d->Primaries[4])), 128, 255, 128, 6);
drawSmallEllipse((QPointF(d->Primaries[6],d->Primaries[7])), 128, 128, 255, 6);
int x1, y1, x2, y2, x3, y3;
mapPoint(x1, y1, (QPointF(d->Primaries[0],d->Primaries[1])) );
mapPoint(x2, y2, (QPointF(d->Primaries[3],d->Primaries[4])) );
mapPoint(x3, y3, (QPointF(d->Primaries[6],d->Primaries[7])) );
biasedLine(x1, y1, x2, y2);
biasedLine(x2, y2, x3, y3);
biasedLine(x3, y3, x1, y1);
} /*else if (d->colorModel ==CMYK){
for (i=0; i<d->Primaries.size();i+++){
drawSmallEllipse((QPointF(d->Primaries[0],d->Primaries[1])), 160, 160, 160, 6);//greyscale for now
//int x1, y1, x2, y2;
//mapPoint(x1, y1, (QPointF(d->Primaries[i],d->Primaries[i+1])) );
//mapPoint(x2, y2, (QPointF(d->Primaries[i+3],d->Primaries[i+4])) );
//biasedLine(x1, y1, x2, y2);
}
}
*/
d->painter.restore();
}
void KisCIETongueWidget::drawWhitePoint()
{
drawSmallEllipse(QPointF (d->whitePoint[0],d->whitePoint[1]), 255, 255, 255, 8);
}
void KisCIETongueWidget::drawGamut()
{
d->gamutMap=QPixmap(size());
d->gamutMap.fill(Qt::black);
QPainter gamutPaint;
gamutPaint.begin(&d->gamutMap);
QPainterPath path;
//gamutPaint.setCompositionMode(QPainter::CompositionMode_Clear);
gamutPaint.setRenderHint(QPainter::Antialiasing);
path.setFillRule(Qt::WindingFill);
gamutPaint.setBrush(Qt::white);
gamutPaint.setPen(Qt::white);
int x, y = 0;
if (!d->gamut.empty()) {
gamutPaint.setOpacity(0.5);
if (d->colorModel == KisCIETongueWidget::RGBA) {
mapPoint(x, y, (QPointF(d->Primaries[0],d->Primaries[1])) );
path.moveTo(QPointF(x + d->xBias,y));
mapPoint(x, y, (QPointF(d->Primaries[3],d->Primaries[4])) );
path.lineTo(QPointF(x + d->xBias,y));
mapPoint(x, y, (QPointF(d->Primaries[6],d->Primaries[7])) );
path.lineTo(QPointF(x + d->xBias,y));
mapPoint(x, y, (QPointF(d->Primaries[0],d->Primaries[1])) );
path.lineTo(QPointF(x + d->xBias,y));
}
gamutPaint.drawPath(path);
gamutPaint.setOpacity(1.0);
foreach (QPointF Point, d->gamut) {
mapPoint(x, y, Point);
gamutPaint.drawEllipse(x + d->xBias- 2, y-2, 4, 4);
//Point.setX(x);
//Point.setY(y);
//path.lineTo(Point);
}
}
gamutPaint.end();
d->painter.save();
d->painter.setOpacity(0.5);
d->painter.setCompositionMode(QPainter::CompositionMode_Multiply);
QRect area(d->xBias, 0, d->pxcols, d->pxrows);
d->painter.drawPixmap(area,d->gamutMap, area);
d->painter.setOpacity(1.0);
d->painter.restore();
}
void KisCIETongueWidget::updatePixmap()
{
d->needUpdatePixmap = false;
d->pixmap = QPixmap(size());
if (d->cieTongueNeedsUpdate){
// Draw the CIE tongue curve. I don't see why we need to redraw it every time the whitepoint and such changes so we cache it.
d->cieTongueNeedsUpdate = false;
d->cietongue = QPixmap(size());
d->cietongue.fill(Qt::black);
d->painter.begin(&d->cietongue);
int pixcols = d->pixmap.width();
int pixrows = d->pixmap.height();
d->gridside = (qMin(pixcols, pixrows)) / 512.0;
d->xBias = grids(32);
d->yBias = grids(20);
d->pxcols = pixcols - d->xBias;
d->pxrows = pixrows - d->yBias;
d->painter.setBackground(QBrush(qRgb(0, 0, 0)));
d->painter.setPen(qRgb(255, 255, 255));
outlineTongue();
d->painter.end();
fillTongue();
d->painter.begin(&d->cietongue);
drawTongueAxis();
drawLabels();
drawTongueGrid();
d->painter.end();
}
d->pixmap = d->cietongue;
d->painter.begin(&d->pixmap);
//draw whitepoint and colorants
if (d->whitePoint[2] > 0.0)
{
drawWhitePoint();
}
if (d->Primaries[2] != 0.0)
{
drawColorantTriangle();
}
drawGamut();
d->painter.end();
}
void KisCIETongueWidget::paintEvent(QPaintEvent*)
{
QPainter p(this);
// Widget is disable : drawing grayed frame.
if ( !isEnabled() )
{
p.fillRect(0, 0, width(), height(),
palette().color(QPalette::Disabled, QPalette::Background));
QPen pen(palette().color(QPalette::Disabled, QPalette::Foreground));
pen.setStyle(Qt::SolidLine);
pen.setWidth(1);
p.setPen(pen);
p.drawRect(0, 0, width(), height());
return;
}
// No profile data to show, or RAW file
if (!d->profileDataAvailable)
{
p.fillRect(0, 0, width(), height(), palette().color(QPalette::Active, QPalette::Background));
QPen pen(palette().color(QPalette::Active, QPalette::Text));
pen.setStyle(Qt::SolidLine);
pen.setWidth(1);
p.setPen(pen);
p.drawRect(0, 0, width(), height());
if (d->uncalibratedColor)
{
p.drawText(0, 0, width(), height(), Qt::AlignCenter,
i18n("Uncalibrated color space"));
}
else
{
p.setPen(Qt::red);
p.drawText(0, 0, width(), height(), Qt::AlignCenter,
i18n("No profile available..."));
}
return;
}
// Create CIE tongue if needed
if (d->needUpdatePixmap)
{
updatePixmap();
}
// draw prerendered tongue
p.drawPixmap(0, 0, d->pixmap);
}
void KisCIETongueWidget::resizeEvent(QResizeEvent* event)
{
Q_UNUSED(event);
setMinimumHeight(width());
setMaximumHeight(width());
d->needUpdatePixmap = true;
d->cieTongueNeedsUpdate = true;
}
void KisCIETongueWidget::slotProgressTimerDone()
{
update();
d->progressTimer->start(200);
}
diff --git a/libs/ui/widgets/kis_cmb_gradient.cpp b/libs/ui/widgets/kis_cmb_gradient.cpp
index 2382b7dd93..da35e2f7d8 100644
--- a/libs/ui/widgets/kis_cmb_gradient.cpp
+++ b/libs/ui/widgets/kis_cmb_gradient.cpp
@@ -1,80 +1,80 @@
/*
* 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 "kis_cmb_gradient.h"
#include <QPainter>
#include <QResizeEvent>
#include <QStyleOptionComboBox>
#include <QApplication>
#include <KoCheckerBoardPainter.h>
-#include <resources/KoResource.h>
+#include <KoResource.h>
#include <resources/KoAbstractGradient.h>
#include "kis_gradient_chooser.h"
KisCmbGradient::KisCmbGradient(QWidget *parent)
: KisPopupButton(parent)
, m_gradientChooser(new KisGradientChooser(this))
{
- connect(m_gradientChooser, SIGNAL(resourceSelected(KoResource*)), SLOT(gradientSelected(KoResource*)));
+ connect(m_gradientChooser, SIGNAL(resourceSelected(KoResourceSP )), SLOT(gradientSelected(KoResourceSP )));
setPopupWidget(m_gradientChooser);
}
-void KisCmbGradient::setGradient(KoAbstractGradient *gradient)
+void KisCmbGradient::setGradient(KoAbstractGradientSP gradient)
{
m_gradientChooser->setCurrentResource(gradient);
}
-KoAbstractGradient *KisCmbGradient::gradient() const
+KoAbstractGradientSP KisCmbGradient::gradient() const
{
- return dynamic_cast<KoAbstractGradient*>(m_gradientChooser->currentResource());
+ return m_gradientChooser->currentResource().dynamicCast<KoAbstractGradient>();
}
-void KisCmbGradient::gradientSelected(KoResource *resource)
+void KisCmbGradient::gradientSelected(KoResourceSP resource)
{
- KoAbstractGradient *gradient = dynamic_cast<KoAbstractGradient*>(resource);
+ KoAbstractGradientSP gradient = resource.dynamicCast<KoAbstractGradient>();
if (!gradient) return;
QImage pm = gradient->generatePreview(iconSize().width(), iconSize().height());
setIcon(QIcon(QPixmap::fromImage(pm)));
emit gradientChanged(gradient);
}
QSize KisCmbGradient::sizeHint() const
{
ensurePolished();
QFontMetrics fm = fontMetrics();
#if QT_VERSION >= QT_VERSION_CHECK(5,11,0)
int maxW = 7 * fm.horizontalAdvance(QChar('x')) + 18;
#else
int maxW = 7 * fm.width(QChar('x')) + 18;
#endif
int maxH = qMax(fm.lineSpacing(), 14) + 2;
QStyleOptionComboBox options;
options.initFrom(this);
return style()->sizeFromContents(QStyle::CT_ComboBox, &options, QSize(maxW, maxH), this);
}
void KisCmbGradient::resizeEvent(QResizeEvent *event)
{
setIconSize(QSize(event->size().width() - 30, event->size().height() - 4));
KisPopupButton::resizeEvent(event);
}
diff --git a/libs/ui/widgets/kis_cmb_gradient.h b/libs/ui/widgets/kis_cmb_gradient.h
index 8bf5bf618a..64148ba72c 100644
--- a/libs/ui/widgets/kis_cmb_gradient.h
+++ b/libs/ui/widgets/kis_cmb_gradient.h
@@ -1,56 +1,57 @@
/*
* 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.
*/
#ifndef KIS_CMB_GRADIENT_H
#define KIS_CMB_GRADIENT_H
-#include <kis_popup_button.h>
+#include <KisPopupButton.h>
+
+#include <KoAbstractGradient.h>
class KoResource;
class KisGradientChooser;
-class KoAbstractGradient;
/**
* @brief The KisCmbGradient class allows the user to select a gradient.
*/
class KisCmbGradient : public KisPopupButton
{
Q_OBJECT
public:
explicit KisCmbGradient(QWidget *parent = 0);
- void setGradient(KoAbstractGradient *gradient);
- KoAbstractGradient *gradient() const;
+ void setGradient(KoAbstractGradientSP gradient);
+ KoAbstractGradientSP gradient() const;
QSize sizeHint() const override;
protected:
void resizeEvent(QResizeEvent *event) override;
Q_SIGNALS:
- void gradientChanged(KoAbstractGradient*);
+ void gradientChanged(KoAbstractGradientSP);
private Q_SLOTS:
- void gradientSelected(KoResource *resource);
+ void gradientSelected(KoResourceSP resource);
private:
KisGradientChooser *m_gradientChooser;
};
#endif // KIS_CMB_GRADIENT_H
diff --git a/libs/ui/widgets/kis_filter_selector_widget.cc b/libs/ui/widgets/kis_filter_selector_widget.cc
index 68717dc03d..1c39a4adda 100644
--- a/libs/ui/widgets/kis_filter_selector_widget.cc
+++ b/libs/ui/widgets/kis_filter_selector_widget.cc
@@ -1,367 +1,368 @@
/*
* Copyright (c) 2007-2008 Cyrille Berger <cberger@cberger.net>
* 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.
*/
#include "kis_filter_selector_widget.h"
#include <QHeaderView>
#include <QTreeView>
#include <QLabel>
#include <QComboBox>
#include <QPushButton>
#include <QScrollArea>
#include <QLayout>
#include <QDialogButtonBox>
#include <QPlainTextEdit>
#include <QDomDocument>
#include <QDomElement>
#include "ui_wdgfilterselector.h"
#include <kis_layer.h>
#include <kis_paint_device.h>
#include <filter/kis_filter.h>
#include <kis_config_widget.h>
#include <filter/kis_filter_configuration.h>
#include "kis_default_bounds.h"
#include <KisKineticScroller.h>
+ #include <KisGlobalResourcesInterface.h>
// From krita/ui
#include "kis_bookmarked_configurations_editor.h"
#include "kis_bookmarked_filter_configurations_model.h"
#include "kis_filters_model.h"
#include "kis_config.h"
class ThumbnailBounds : public KisDefaultBounds {
public:
ThumbnailBounds() : KisDefaultBounds() {}
~ThumbnailBounds() override {}
QRect bounds() const override
{
return QRect(0, 0, 100, 100);
}
private:
Q_DISABLE_COPY(ThumbnailBounds)
};
struct KisFilterSelectorWidget::Private {
QWidget *currentCentralWidget {0};
KisConfigWidget *currentFilterConfigurationWidget {0};
KisFilterSP currentFilter;
KisPaintDeviceSP paintDevice;
Ui_FilterSelector uiFilterSelector;
KisPaintDeviceSP thumb;
KisBookmarkedFilterConfigurationsModel *currentBookmarkedFilterConfigurationsModel {0};
KisFiltersModel *filtersModel {};
QGridLayout *widgetLayout {};
KisViewManager *view{};
bool showFilterGallery {true};
bool usedForMask {false};
};
KisFilterSelectorWidget::KisFilterSelectorWidget(QWidget* parent)
: d(new Private)
{
Q_UNUSED(parent);
setObjectName("KisFilterSelectorWidget");
d->uiFilterSelector.setupUi(this);
d->widgetLayout = new QGridLayout(d->uiFilterSelector.centralWidgetHolder);
d->widgetLayout->setContentsMargins(0,0,0,0);
d->widgetLayout->setHorizontalSpacing(0);
showFilterGallery(false);
connect(d->uiFilterSelector.filtersSelector, SIGNAL(clicked(QModelIndex)), SLOT(setFilterIndex(QModelIndex)));
connect(d->uiFilterSelector.filtersSelector, SIGNAL(activated(QModelIndex)), SLOT(setFilterIndex(QModelIndex)));
connect(d->uiFilterSelector.comboBoxPresets, SIGNAL(activated(int)),SLOT(slotBookmarkedFilterConfigurationSelected(int)));
connect(d->uiFilterSelector.pushButtonEditPressets, SIGNAL(pressed()), SLOT(editConfigurations()));
connect(d->uiFilterSelector.btnXML, SIGNAL(clicked()), this, SLOT(showXMLdialog()));
KisConfig cfg(true);
d->uiFilterSelector.chkRememberPreset->setChecked(cfg.readEntry<bool>("filterdialog/rememberlastpreset", false));
}
KisFilterSelectorWidget::~KisFilterSelectorWidget()
{
KisConfig cfg(false);
cfg.writeEntry<bool>("filterdialog/rememberlastpreset", d->uiFilterSelector.chkRememberPreset->isChecked());
delete d->filtersModel;
delete d->currentBookmarkedFilterConfigurationsModel;
delete d->currentCentralWidget;
delete d->widgetLayout;
delete d;
}
void KisFilterSelectorWidget::setView(KisViewManager *view)
{
d->view = view;
}
void KisFilterSelectorWidget::setPaintDevice(bool showAll, KisPaintDeviceSP _paintDevice)
{
if (!_paintDevice) return;
if (d->filtersModel) delete d->filtersModel;
d->usedForMask = !showAll;
d->paintDevice = _paintDevice;
d->thumb = d->paintDevice->createThumbnailDevice(100, 100);
d->thumb->setDefaultBounds(new ThumbnailBounds());
d->filtersModel = new KisFiltersModel(showAll, d->thumb);
d->uiFilterSelector.filtersSelector->setFilterModel(d->filtersModel);
d->uiFilterSelector.filtersSelector->header()->setVisible(false);
KisConfig cfg(true);
QModelIndex idx = d->filtersModel->indexForFilter(cfg.readEntry<QString>("FilterSelector/LastUsedFilter", "levels"));
if (!idx.isValid()) {
idx = d->filtersModel->indexForFilter("levels");
}
if (d->usedForMask && isFilterGalleryVisible()) {
d->uiFilterSelector.filtersSelector->activateFilter(idx);
}
}
void KisFilterSelectorWidget::showFilterGallery(bool visible)
{
if (d->showFilterGallery == visible) {
return;
}
d->showFilterGallery = visible;
update();
emit sigFilterGalleryToggled(visible);
emit sigSizeChanged();
}
void KisFilterSelectorWidget::showXMLdialog()
{
if (currentFilter()->showConfigurationWidget()) {
QDialog *xmlDialog = new QDialog();
xmlDialog->setMinimumWidth(500);
xmlDialog->setWindowTitle(i18n("Filter configuration XML"));
QVBoxLayout *xmllayout = new QVBoxLayout(xmlDialog);
QPlainTextEdit *text = new QPlainTextEdit(xmlDialog);
KisFilterConfigurationSP config = configuration();
text->setPlainText(config->toXML());
xmllayout->addWidget(text);
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, xmlDialog);
connect(buttons, SIGNAL(accepted()), xmlDialog, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), xmlDialog, SLOT(reject()));
xmllayout->addWidget(buttons);
if (xmlDialog->exec()==QDialog::Accepted) {
QDomDocument doc;
doc.setContent(text->toPlainText());
config->fromXML(doc.documentElement());
if (config) {
d->currentFilterConfigurationWidget->setConfiguration(config);
}
}
}
}
bool KisFilterSelectorWidget::isFilterGalleryVisible() const
{
return d->showFilterGallery;
}
KisFilterSP KisFilterSelectorWidget::currentFilter() const
{
return d->currentFilter;
}
void KisFilterSelectorWidget::setFilter(KisFilterSP f)
{
Q_ASSERT(f);
Q_ASSERT(d->filtersModel);
setWindowTitle(f->name());
dbgKrita << "setFilter: " << f;
d->currentFilter = f;
delete d->currentCentralWidget;
{
bool v = d->uiFilterSelector.filtersSelector->blockSignals(true);
d->uiFilterSelector.filtersSelector->setCurrentIndex(d->filtersModel->indexForFilter(f->id()));
d->uiFilterSelector.filtersSelector->blockSignals(v);
}
KisConfigWidget* widget =
d->currentFilter->createConfigurationWidget(d->uiFilterSelector.centralWidgetHolder, d->paintDevice, d->usedForMask);
if (!widget) { // No widget, so display a label instead
d->uiFilterSelector.comboBoxPresets->setEnabled(false);
d->uiFilterSelector.pushButtonEditPressets->setEnabled(false);
d->uiFilterSelector.btnXML->setEnabled(false);
d->currentFilterConfigurationWidget = 0;
d->currentCentralWidget = new QLabel(i18n("No configuration options"),
d->uiFilterSelector.centralWidgetHolder);
d->uiFilterSelector.scrollArea->setMinimumSize(d->currentCentralWidget->sizeHint());
qobject_cast<QLabel*>(d->currentCentralWidget)->setAlignment(Qt::AlignCenter);
} else {
d->uiFilterSelector.comboBoxPresets->setEnabled(true);
d->uiFilterSelector.pushButtonEditPressets->setEnabled(true);
d->uiFilterSelector.btnXML->setEnabled(true);
d->currentFilterConfigurationWidget = widget;
d->currentCentralWidget = widget;
widget->layout()->setContentsMargins(0,0,0,0);
d->currentFilterConfigurationWidget->setView(d->view);
d->currentFilterConfigurationWidget->blockSignals(true);
- d->currentFilterConfigurationWidget->setConfiguration(d->currentFilter->defaultConfiguration());
+ d->currentFilterConfigurationWidget->setConfiguration(d->currentFilter->defaultConfiguration(KisGlobalResourcesInterface::instance()));
d->currentFilterConfigurationWidget->blockSignals(false);
d->uiFilterSelector.scrollArea->setContentsMargins(0,0,0,0);
d->uiFilterSelector.scrollArea->setMinimumWidth(widget->sizeHint().width() + 18);
connect(d->currentFilterConfigurationWidget, SIGNAL(sigConfigurationUpdated()), this, SIGNAL(configurationChanged()));
}
// Change the list of presets
delete d->currentBookmarkedFilterConfigurationsModel;
d->currentBookmarkedFilterConfigurationsModel = new KisBookmarkedFilterConfigurationsModel(d->thumb, f);
d->uiFilterSelector.comboBoxPresets->setModel(d->currentBookmarkedFilterConfigurationsModel);
// Add the widget to the layout
d->currentCentralWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
d->widgetLayout->addWidget(d->currentCentralWidget, 0 , 0);
if (d->uiFilterSelector.chkRememberPreset->isChecked()) {
int lastBookmarkedFilterConfiguration = KisConfig(true).readEntry<int>("lastBookmarkedFilterConfiguration/" + f->id(), 0);
if (d->uiFilterSelector.comboBoxPresets->count() > lastBookmarkedFilterConfiguration) {
d->uiFilterSelector.comboBoxPresets->setCurrentIndex(lastBookmarkedFilterConfiguration);
slotBookmarkedFilterConfigurationSelected(lastBookmarkedFilterConfiguration);
}
}
update();
}
void KisFilterSelectorWidget::setFilterIndex(const QModelIndex& idx)
{
if (!idx.isValid()) return;
Q_ASSERT(d->filtersModel);
KisFilter* filter = const_cast<KisFilter*>(d->filtersModel->indexToFilter(idx));
if (filter) {
setFilter(filter);
}
else {
if (d->currentFilter) {
bool v = d->uiFilterSelector.filtersSelector->blockSignals(true);
QModelIndex idx = d->filtersModel->indexForFilter(d->currentFilter->id());
d->uiFilterSelector.filtersSelector->setCurrentIndex(idx);
d->uiFilterSelector.filtersSelector->scrollTo(idx);
d->uiFilterSelector.filtersSelector->blockSignals(v);
}
}
slotBookMarkCurrentFilter();
emit(configurationChanged());
}
void KisFilterSelectorWidget::slotBookMarkCurrentFilter() {
KisConfig cfg(false);
cfg.writeEntry<QString>("FilterSelector/LastUsedFilter", d->currentFilter->id());
}
void KisFilterSelectorWidget::slotBookmarkedFilterConfigurationSelected(int index)
{
if (d->currentFilterConfigurationWidget) {
QModelIndex modelIndex = d->currentBookmarkedFilterConfigurationsModel->index(index, 0);
KisFilterConfigurationSP config = d->currentBookmarkedFilterConfigurationsModel->configuration(modelIndex);
d->currentFilterConfigurationWidget->setConfiguration(config);
if (d->currentFilter && index != KisConfig(true).readEntry<int>("lastBookmarkedFilterConfiguration/" + d->currentFilter->id(), 0)) {
KisConfig(false).writeEntry<int>("lastBookmarkedFilterConfiguration/" + d->currentFilter->id(), index);
}
}
}
void KisFilterSelectorWidget::editConfigurations()
{
KisSerializableConfigurationSP config =
d->currentFilterConfigurationWidget ? d->currentFilterConfigurationWidget->configuration() : 0;
KisBookmarkedConfigurationsEditor editor(this, d->currentBookmarkedFilterConfigurationsModel, config);
editor.exec();
}
void KisFilterSelectorWidget::update()
{
d->uiFilterSelector.filtersSelector->setVisible(d->showFilterGallery);
if (d->showFilterGallery) {
setMinimumWidth(qMax(sizeHint().width(), 700));
d->uiFilterSelector.scrollArea->setMinimumHeight(400);
setMinimumHeight(d->uiFilterSelector.verticalLayout->sizeHint().height());
if (d->currentFilter) {
bool v = d->uiFilterSelector.filtersSelector->blockSignals(true);
d->uiFilterSelector.filtersSelector->setCurrentIndex(d->filtersModel->indexForFilter(d->currentFilter->id()));
d->uiFilterSelector.filtersSelector->blockSignals(v);
}
}
else {
if (d->currentCentralWidget) {
d->uiFilterSelector.scrollArea->setMinimumHeight(qMin(400, d->currentCentralWidget->sizeHint().height()));
}
setMinimumSize(d->uiFilterSelector.verticalLayout->sizeHint());
}
}
KisFilterConfigurationSP KisFilterSelectorWidget::configuration()
{
if (d->currentFilterConfigurationWidget) {
KisFilterConfigurationSP config = dynamic_cast<KisFilterConfiguration*>(d->currentFilterConfigurationWidget->configuration().data());
if (config) {
return config;
}
} else if (d->currentFilter) {
- return d->currentFilter->defaultConfiguration();
+ return d->currentFilter->defaultConfiguration(KisGlobalResourcesInterface::instance());
}
return 0;
}
void KisFilterTree::setFilterModel(QAbstractItemModel *model)
{
m_model = model;
}
void KisFilterTree::activateFilter(QModelIndex idx)
{
setModel(m_model);
selectionModel()->select(idx, QItemSelectionModel::SelectCurrent);
expand(idx);
scrollTo(idx);
emit activated(idx);
}
void KisFilterSelectorWidget::setVisible(bool visible)
{
QWidget::setVisible(visible);
if (visible) {
update();
}
}
diff --git a/libs/ui/widgets/kis_gradient_chooser.cc b/libs/ui/widgets/kis_gradient_chooser.cc
index c02a8e1abf..a97dffb294 100644
--- a/libs/ui/widgets/kis_gradient_chooser.cc
+++ b/libs/ui/widgets/kis_gradient_chooser.cc
@@ -1,209 +1,239 @@
/*
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
* 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 "widgets/kis_gradient_chooser.h"
#include <QLabel>
#include <QLayout>
#include <QPushButton>
#include <QVBoxLayout>
#include <QMenu>
+#include <QMessageBox>
#include <klocalizedstring.h>
#include <resources/KoAbstractGradient.h>
-#include <resources/KoResource.h>
+#include <KoResource.h>
#include <resources/KoSegmentGradient.h>
-#include <KoResourceItemView.h>
+#include <KisResourceItemListView.h>
#include <KisKineticScroller.h>
#include <KoStopGradient.h>
#include <KoColorSpaceRegistry.h>
-#include <KoResourceItemChooser.h>
+#include <KisResourceItemChooser.h>
#include <KoResourceServerProvider.h>
-#include <KoResourceServerAdapter.h>
#include <kis_icon.h>
#include <kis_config.h>
#include "KisViewManager.h"
#include "kis_global.h"
#include "kis_autogradient.h"
#include "kis_canvas_resource_provider.h"
#include "kis_stopgradient_editor.h"
-KisCustomGradientDialog::KisCustomGradientDialog(KoAbstractGradient* gradient, QWidget *parent, const char *name)
+KisCustomGradientDialog::KisCustomGradientDialog(KoAbstractGradientSP gradient, QWidget *parent, const char *name)
: KoDialog(parent, Qt::Dialog)
{
- setButtons(Close);
- setDefaultButton(Close);
+ setButtons(Ok|Cancel);
+ setDefaultButton(Ok);
setObjectName(name);
setModal(false);
- KoStopGradient* stopGradient = dynamic_cast<KoStopGradient*>(gradient);
+ connect(this, SIGNAL(okClicked()), this, SLOT(accept()));
+ connect(this, SIGNAL(cancelClicked()), this, SLOT(reject()));
+
+ KoStopGradientSP stopGradient = gradient.dynamicCast<KoStopGradient>();
if (stopGradient) {
m_page = new KisStopGradientEditor(stopGradient, this, "autogradient", i18n("Custom Stop Gradient"));
}
else {
- KoSegmentGradient* segmentedGradient = dynamic_cast<KoSegmentGradient*>(gradient);
+ KoSegmentGradientSP segmentedGradient = gradient.dynamicCast<KoSegmentGradient>();
if (segmentedGradient) {
m_page = new KisAutogradientEditor(segmentedGradient, this, "autogradient", i18n("Custom Segmented Gradient"));
}
}
setCaption(m_page->windowTitle());
setMainWidget(m_page);
}
KisGradientChooser::KisGradientChooser(QWidget *parent, const char *name)
: QFrame(parent)
{
setObjectName(name);
m_lbName = new QLabel();
- KoResourceServer<KoAbstractGradient> * rserver = KoResourceServerProvider::instance()->gradientServer();
- QSharedPointer<KoAbstractResourceServerAdapter> adapter (new KoResourceServerAdapter<KoAbstractGradient>(rserver));
- m_itemChooser = new KoResourceItemChooser(adapter, this);
+ m_itemChooser = new KisResourceItemChooser(ResourceType::Gradients, false, this);
m_itemChooser->showTaggingBar(true);
m_itemChooser->setFixedSize(250, 250);
- m_itemChooser->setColumnCount(1);
+ m_itemChooser->itemView()->setViewMode(QListView::ListMode);
- connect(m_itemChooser, SIGNAL(resourceSelected(KoResource*)),
- this, SLOT(update(KoResource*)));
+ connect(m_itemChooser, SIGNAL(resourceSelected(KoResourceSP )),
+ this, SLOT(update(KoResourceSP )));
- connect(m_itemChooser, SIGNAL(resourceSelected(KoResource*)),
- this, SIGNAL(resourceSelected(KoResource*)));
+ connect(m_itemChooser, SIGNAL(resourceSelected(KoResourceSP )),
+ this, SIGNAL(resourceSelected(KoResourceSP )));
QWidget* buttonWidget = new QWidget(this);
QHBoxLayout* buttonLayout = new QHBoxLayout(buttonWidget);
m_addGradient = new QToolButton(this);
m_addGradient->setText(i18n("Add..."));
m_addGradient->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
connect(m_addGradient, SIGNAL(clicked()), this, SLOT(addStopGradient()));
buttonLayout->addWidget(m_addGradient);
QMenu *menuAddGradient = new QMenu(m_addGradient);
QAction* addStopGradient = new QAction(i18n("Stop gradient"), this);
connect(addStopGradient, SIGNAL(triggered(bool)), this, SLOT(addStopGradient()));
menuAddGradient->addAction(addStopGradient);
QAction* addSegmentedGradient = new QAction(i18n("Segmented gradient"), this);
connect(addSegmentedGradient, SIGNAL(triggered(bool)), this, SLOT(addSegmentedGradient()));
menuAddGradient->addAction(addSegmentedGradient);
m_addGradient->setMenu(menuAddGradient);
m_addGradient->setPopupMode(QToolButton::MenuButtonPopup);
m_editGradient = new QPushButton();
m_editGradient->setText(i18n("Edit..."));
m_editGradient->setEnabled(false);
connect(m_editGradient, SIGNAL(clicked()), this, SLOT(editGradient()));
buttonLayout->addWidget(m_editGradient);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setObjectName("main layout");
mainLayout->setMargin(2);
mainLayout->addWidget(m_lbName);
mainLayout->addWidget(m_itemChooser, 10);
mainLayout->addWidget(buttonWidget);
slotUpdateIcons();
setLayout(mainLayout);
}
KisGradientChooser::~KisGradientChooser()
{
}
-KoResource *KisGradientChooser::currentResource()
+KoResourceSP KisGradientChooser::currentResource()
{
return m_itemChooser->currentResource();
}
-void KisGradientChooser::setCurrentResource(KoResource *resource)
+void KisGradientChooser::setCurrentResource(KoResourceSP resource)
{
m_itemChooser->setCurrentResource(resource);
}
-void KisGradientChooser::setCurrentItem(int row, int column)
+void KisGradientChooser::setCurrentItem(int row)
{
- m_itemChooser->setCurrentItem(row, column);
+ m_itemChooser->setCurrentItem(row);
if (currentResource())
update(currentResource());
}
void KisGradientChooser::slotUpdateIcons()
{
if (m_addGradient && m_editGradient) {
m_addGradient->setIcon(KisIconUtils::loadIcon("list-add"));
m_editGradient->setIcon(KisIconUtils::loadIcon("configure"));
}
}
-void KisGradientChooser::update(KoResource * resource)
+void KisGradientChooser::update(KoResourceSP resource)
{
- KoAbstractGradient *gradient = static_cast<KoAbstractGradient *>(resource);
+ KoAbstractGradientSP gradient = resource.staticCast<KoAbstractGradient>();
m_lbName->setText(gradient ? i18n(gradient->name().toUtf8().data()) : "");
- m_editGradient->setEnabled(gradient && gradient->removable());
+ m_editGradient->setEnabled(true);
}
void KisGradientChooser::addStopGradient()
{
- KoStopGradient* gradient = new KoStopGradient("");
+ KoStopGradientSP gradient(new KoStopGradient(""));
QList<KoGradientStop> stops;
stops << KoGradientStop(0.0, KoColor(QColor(250, 250, 0), KoColorSpaceRegistry::instance()->rgb8())) << KoGradientStop(1.0, KoColor(QColor(255, 0, 0, 255), KoColorSpaceRegistry::instance()->rgb8()));
gradient->setType(QGradient::LinearGradient);
+ gradient->setName(i18n("unnamed"));
gradient->setStops(stops);
addGradient(gradient);
}
void KisGradientChooser::addSegmentedGradient()
{
- KoSegmentGradient* gradient = new KoSegmentGradient("");
+ KoSegmentGradientSP gradient(new KoSegmentGradient(""));
gradient->createSegment(INTERP_LINEAR, COLOR_INTERP_RGB, 0.0, 1.0, 0.5, Qt::black, Qt::white);
gradient->setName(i18n("unnamed"));
addGradient(gradient);
}
-void KisGradientChooser::addGradient(KoAbstractGradient* gradient)
+void KisGradientChooser::addGradient(KoAbstractGradientSP gradient, bool editGradient)
{
KoResourceServer<KoAbstractGradient> * rserver = KoResourceServerProvider::instance()->gradientServer();
QString saveLocation = rserver->saveLocation();
KisCustomGradientDialog dialog(gradient, this, "KisCustomGradientDialog");
- dialog.exec();
- gradient->setFilename(saveLocation + gradient->name() + gradient->defaultFileExtension());
+ QFileInfo fileInfo(saveLocation + gradient->name().split(" ").join("_") + gradient->defaultFileExtension());
+
+ bool fileOverwriteAccepted = false;
+
+ QString oldname = gradient->name();
+
+ while(!fileOverwriteAccepted) {
+ if (dialog.exec() == KoDialog::Accepted) {
+
+ if (gradient->name().isEmpty()) {
+ return;
+ }
+
+ if (editGradient && oldname == gradient->name()) {
+ fileOverwriteAccepted = true;
+ continue;
+ }
+
+ fileInfo = QFileInfo(saveLocation + gradient->name().split(" ").join("_") + gradient->defaultFileExtension());
+ if (fileInfo.exists()) {
+ int res = QMessageBox::warning(this, i18nc("@title:window", "Name Already Exists")
+ , i18n("The name '%1' already exists, do you wish to overwrite it?", gradient->name())
+ , QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ if (res == QMessageBox::Yes) fileOverwriteAccepted = true;
+ } else {
+ fileOverwriteAccepted = true;
+ }
+ } else {
+ return;
+ }
+ }
+ gradient->setFilename(gradient->name() + gradient->defaultFileExtension());
gradient->setValid(true);
rserver->addResource(gradient);
- m_itemChooser->setCurrentResource(gradient);
+ //TODO: select the right gradient from the resource server. Right now this is not possible :(
+ m_itemChooser->setCurrentItem(0);
}
void KisGradientChooser::editGradient()
{
- KisCustomGradientDialog dialog(static_cast<KoAbstractGradient*>(currentResource()), this, "KisCustomGradientDialog");
- dialog.exec();
-
-
+ addGradient(currentResource().staticCast<KoAbstractGradient>(), true);
}
diff --git a/libs/ui/widgets/kis_gradient_chooser.h b/libs/ui/widgets/kis_gradient_chooser.h
index abfbdb16fc..d5d33ed9cf 100644
--- a/libs/ui/widgets/kis_gradient_chooser.h
+++ b/libs/ui/widgets/kis_gradient_chooser.h
@@ -1,91 +1,94 @@
/*
* Copyright (c) 2004 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_GRADIENT_CHOOSER_H_
#define KIS_GRADIENT_CHOOSER_H_
#include <KoDialog.h>
#include <QFrame>
#include <QToolButton>
#include <kritaui_export.h>
+#include <KoResource.h>
+#include <KoAbstractGradient.h>
+#include <KoStopGradient.h>
+#include <KoSegmentGradient.h>
-class KoAbstractGradient;
-class KoStopGradient;
class KisViewManager;
class QLabel;
class QPushButton;
+
+class KisResourceItemChooser;
class KisAutogradientEditor;
class KoResource;
-class KoResourceItemChooser;
class KisCustomGradientDialog : public KoDialog
{
Q_OBJECT
public:
- KisCustomGradientDialog(KoAbstractGradient* gradient, QWidget *parent, const char *name);
+ KisCustomGradientDialog(KoAbstractGradientSP gradient, QWidget *parent, const char *name);
private:
QWidget * m_page;
};
class KRITAUI_EXPORT KisGradientChooser : public QFrame
{
Q_OBJECT
public:
KisGradientChooser(QWidget *parent = 0, const char *name = 0);
~KisGradientChooser() override;
/// Gets the currently selected resource
/// @returns the selected resource, 0 is no resource is selected
- KoResource *currentResource();
- void setCurrentResource(KoResource *resource);
+ KoResourceSP currentResource();
+ void setCurrentResource(KoResourceSP resource);
- void setCurrentItem(int row, int column);
+ void setCurrentItem(int row);
Q_SIGNALS:
/// Emitted when a resource was selected
- void resourceSelected(KoResource * resource);
+ void resourceSelected(KoResourceSP resource);
public Q_SLOTS:
void slotUpdateIcons();
private Q_SLOTS:
- virtual void update(KoResource * resource);
+ virtual void update(KoResourceSP resource);
void addStopGradient();
void addSegmentedGradient();
void editGradient();
private:
- void addGradient(KoAbstractGradient* gradient);
+ void addGradient(KoAbstractGradientSP gradient, bool editGradient = false);
private:
QLabel *m_lbName;
- KoResourceItemChooser * m_itemChooser;
+ KisResourceItemChooser * m_itemChooser;
QToolButton* m_addGradient;
QPushButton* m_editGradient;
};
#endif // KIS_GRADIENT_CHOOSER_H_
diff --git a/libs/ui/widgets/kis_iconwidget.cc b/libs/ui/widgets/kis_iconwidget.cc
index e34059e478..6bd25d0548 100644
--- a/libs/ui/widgets/kis_iconwidget.cc
+++ b/libs/ui/widgets/kis_iconwidget.cc
@@ -1,88 +1,109 @@
/*
* Copyright (c) 2000 Matthias Elter <elter@kde.org>
* Copyright (c) 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.g
*
* 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_iconwidget.h"
#include <QPainter>
#include <QIcon>
#include <QStyleOption>
-#include <resources/KoResource.h>
-#include <KoResourceServerAdapter.h>
+#include <KoResource.h>
-KisIconWidget::KisIconWidget(QWidget *parent, const char *name)
+KisIconWidget::KisIconWidget(QWidget *parent, const QString &name)
: KisPopupButton(parent)
{
setObjectName(name);
m_resource = 0;
}
-void KisIconWidget::setResource(KoResource * resource)
+void KisIconWidget::KisIconWidget::setThumbnail(const QImage &thumbnail)
+{
+ m_thumbnail = thumbnail;
+}
+
+void KisIconWidget::setResource(KoResourceSP resource)
{
m_resource = resource;
update();
}
void KisIconWidget::paintEvent(QPaintEvent *event)
{
QPushButton::paintEvent(event);
QPainter p;
p.begin(this);
const qint32 cw = width();
const qint32 ch = height();
const qint32 border = 3;
const qint32 iconWidth = cw - (border*2);
const qint32 iconHeight = ch - (border*2);
// Round off the corners of the preview
QRegion clipRegion(border, border, iconWidth, iconHeight);
clipRegion -= QRegion(border, border, 1, 1);
clipRegion -= QRegion(cw-border-1, border, 1, 1);
clipRegion -= QRegion(cw-border-1, ch-border-1, 1, 1);
clipRegion -= QRegion(border, ch-border-1, 1, 1);
p.setClipRegion(clipRegion);
p.setClipping(true);
p.setBrush(this->palette().window());
- p.drawRect(QRect(0, 0, cw, ch));
- if (m_resource && !m_resource->image().isNull()) {
+ p.drawRect(QRect(0,0,cw,ch));
+
+ if (!m_thumbnail.isNull()) {
+ QImage img = QImage(iconWidth, iconHeight, QImage::Format_ARGB32);
+ img.fill(Qt::transparent);
+ if (m_thumbnail.width() < iconWidth || m_thumbnail.height() < iconHeight) {
+ QPainter paint2;
+ paint2.begin(&img);
+ for (int x = 0; x < iconWidth; x += m_thumbnail.width()) {
+ for (int y = 0; y < iconHeight; y+= m_thumbnail.height()) {
+ paint2.drawImage(x, y, m_thumbnail);
+ }
+ }
+ } else {
+ img = m_thumbnail.scaled(iconWidth, iconHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ }
+ p.drawImage(QRect(border, border, iconWidth, iconHeight), img);
+ }
+ else if (m_resource) {
QImage img = QImage(iconWidth, iconHeight, QImage::Format_ARGB32);
img.fill(Qt::transparent);
- if (m_resource->image().width() < iconWidth || m_resource->image().height() < iconHeight) {
+ if (m_resource->image().width()<iconWidth || m_resource->image().height()<iconHeight) {
QPainter paint2;
paint2.begin(&img);
for (int x = 0; x < iconWidth; x += m_resource->image().width()) {
for (int y = 0; y < iconHeight; y += m_resource->image().height()) {
paint2.drawImage(x, y, m_resource->image());
}
}
} else {
img = m_resource->image().scaled(iconWidth, iconHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
p.drawImage(QRect(border, border, iconWidth, iconHeight), img);
} else if (!icon().isNull()) {
int border2 = qRound((cw - 16) * 0.5);
p.drawImage(QRect(border2, border2, 16, 16), icon().pixmap(16, 16).toImage());
}
p.setClipping(false);
}
diff --git a/libs/ui/widgets/kis_iconwidget.h b/libs/ui/widgets/kis_iconwidget.h
index 5d9e6e9262..c2c2eb29a1 100644
--- a/libs/ui/widgets/kis_iconwidget.h
+++ b/libs/ui/widgets/kis_iconwidget.h
@@ -1,49 +1,50 @@
/*
* Copyright (c) 2000 Matthias Elter <elter@kde.org>
* Copyright (c) 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_ICONWIDGET_H_
#define KIS_ICONWIDGET_H_
-#include <kis_popup_button.h>
+#include <KisPopupButton.h>
#include <kritaui_export.h>
-class KoResource;
+#include <KoResource.h>
/**
* The icon widget is used in the control box where the current color and brush
* are shown.
*/
class KRITAUI_EXPORT KisIconWidget : public KisPopupButton
{
Q_OBJECT
public:
- KisIconWidget(QWidget *parent = 0, const char *name = 0);
- void setResource(KoResource * resource);
-
+ KisIconWidget(QWidget *parent = 0, const QString &name = QString());
+ void setThumbnail(const QImage &thumbnail);
+ void setResource(KoResourceSP resource);
protected:
void paintEvent(QPaintEvent *) override;
private:
- KoResource *m_resource;
+ QImage m_thumbnail;
+ KoResourceSP m_resource;
};
#endif // KIS_ICONWIDGET_H_
diff --git a/libs/ui/widgets/kis_multi_bool_filter_widget.cc b/libs/ui/widgets/kis_multi_bool_filter_widget.cc
index 87de712a3a..53aaae792c 100644
--- a/libs/ui/widgets/kis_multi_bool_filter_widget.cc
+++ b/libs/ui/widgets/kis_multi_bool_filter_widget.cc
@@ -1,80 +1,81 @@
/*
* Copyright (c) 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.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 "widgets/kis_multi_bool_filter_widget.h"
#include <QLabel>
#include <QLayout>
#include <QCheckBox>
#include <QVBoxLayout>
#include <filter/kis_filter_configuration.h>
+#include <KisGlobalResourcesInterface.h>
#include <klocalizedstring.h>
KisBoolWidgetParam::KisBoolWidgetParam(bool ninitvalue, const QString & nlabel, const QString & nname) :
initvalue(ninitvalue),
label(nlabel),
name(nname)
{
}
KisMultiBoolFilterWidget::KisMultiBoolFilterWidget(const QString & filterid, QWidget * parent, const QString & caption, vKisBoolWidgetParam iwparam)
: KisConfigWidget(parent)
, m_filterid(filterid)
{
qint32 nbboolWidgets = iwparam.size();
this->setWindowTitle(caption);
QVBoxLayout *widgetLayout = new QVBoxLayout(this);
widgetLayout->setMargin(nbboolWidgets + 1);
widgetLayout->setContentsMargins(0,0,0,0);
for (qint32 i = 0; i < nbboolWidgets; ++i) {
QCheckBox * cb = new QCheckBox(this);
cb->setObjectName(iwparam[i].name);
cb->setChecked(iwparam[i].initvalue);
cb->setText(iwparam[i].label);
connect(cb, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
widgetLayout->addWidget(cb);
m_boolWidgets.append(cb);
}
widgetLayout->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Minimum, QSizePolicy::Expanding));
widgetLayout->addStretch();
}
void KisMultiBoolFilterWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
if (!config) return;
for (int i = 0; i < nbValues(); ++i) {
bool val = config->getBool(m_boolWidgets[i]->objectName(), true);
m_boolWidgets[i]->setChecked(val);
}
}
KisPropertiesConfigurationSP KisMultiBoolFilterWidget::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration(m_filterid, 0);
+ KisFilterConfigurationSP config = new KisFilterConfiguration(m_filterid, 0, KisGlobalResourcesInterface::instance());
for (int i = 0; i < nbValues(); ++i) {
config->setProperty(m_boolWidgets[i]->objectName(), valueAt(i));
}
return config;
}
diff --git a/libs/ui/widgets/kis_multi_double_filter_widget.cc b/libs/ui/widgets/kis_multi_double_filter_widget.cc
index 452d393cb7..6f2ff9a98b 100644
--- a/libs/ui/widgets/kis_multi_double_filter_widget.cc
+++ b/libs/ui/widgets/kis_multi_double_filter_widget.cc
@@ -1,122 +1,123 @@
/*
* Copyright (c) 2004 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 "widgets/kis_multi_double_filter_widget.h"
#include <QLabel>
#include <QLayout>
#include <QTimer>
#include <QGridLayout>
#include <QSpinBox>
#include <kis_config_widget.h>
#include <filter/kis_filter_configuration.h>
#include <klocalizedstring.h>
+#include <KisGlobalResourcesInterface.h>
KisDelayedActionDoubleInput::KisDelayedActionDoubleInput(QWidget * parent, const QString & name)
: KisDoubleParseSpinBox(parent)
{
setObjectName(name);
m_timer = new QTimer(this);
m_timer->setObjectName(name);
m_timer->setSingleShot(true);
connect(m_timer, SIGNAL(timeout()), SLOT(slotValueChanged()));
connect(this, SIGNAL(valueChanged(double)), SLOT(slotTimeToUpdate()));
}
void KisDelayedActionDoubleInput::slotTimeToUpdate()
{
m_timer->start(50);
}
void KisDelayedActionDoubleInput::slotValueChanged()
{
emit valueChangedDelayed(value());
}
void KisDelayedActionDoubleInput::cancelDelayedSignal()
{
m_timer->stop();
}
KisDoubleWidgetParam::KisDoubleWidgetParam(double nmin, double nmax, double ninitvalue, const QString & nlabel, const QString & nname) :
min(nmin),
max(nmax),
initvalue(ninitvalue),
label(nlabel),
name(nname)
{
}
KisMultiDoubleFilterWidget::KisMultiDoubleFilterWidget(const QString & filterid, QWidget * parent, const QString & caption, vKisDoubleWidgetParam dwparam)
: KisConfigWidget(parent), m_filterid(filterid)
{
m_nbdoubleWidgets = dwparam.size();
this->setWindowTitle(caption);
QGridLayout *widgetLayout = new QGridLayout(this);
widgetLayout->setColumnStretch(1, 1);
widgetLayout->setContentsMargins(0,0,0,0);
widgetLayout->setHorizontalSpacing(0);
m_doubleWidgets.resize(m_nbdoubleWidgets);
for (qint32 i = 0; i < m_nbdoubleWidgets; ++i) {
m_doubleWidgets[i] = new KisDelayedActionDoubleInput(this, dwparam[i].name);
m_doubleWidgets[i]->setRange(dwparam[i].min, dwparam[i].max);
m_doubleWidgets[i]->setValue(dwparam[i].initvalue);
m_doubleWidgets[i]->cancelDelayedSignal();
connect(m_doubleWidgets[i], SIGNAL(valueChangedDelayed(double)), SIGNAL(sigConfigurationItemChanged()));
QLabel* lbl = new QLabel(dwparam[i].label + ':', this);
widgetLayout->addWidget(lbl, i , 0);
widgetLayout->addWidget(m_doubleWidgets[i], i , 1);
}
widgetLayout->setRowStretch(m_nbdoubleWidgets,1);
QSpacerItem * sp = new QSpacerItem(1, 1);
widgetLayout->addItem(sp, m_nbdoubleWidgets, 0);
}
void KisMultiDoubleFilterWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
if (!config) return;
for (int i = 0; i < m_nbdoubleWidgets ; ++i) {
KisDelayedActionDoubleInput * w = m_doubleWidgets[i];
if (w) {
double val = config->getDouble(m_doubleWidgets[i]->objectName());
m_doubleWidgets[i]->setValue(val);
m_doubleWidgets[i]->cancelDelayedSignal();
}
}
}
KisPropertiesConfigurationSP KisMultiDoubleFilterWidget::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration(m_filterid, 0);
+ KisFilterConfigurationSP config = new KisFilterConfiguration(m_filterid, 0, KisGlobalResourcesInterface::instance());
for (int i = 0; i < nbValues(); ++i) {
config->setProperty(m_doubleWidgets[i]->objectName(), m_doubleWidgets[i]->value());
}
return config;
}
diff --git a/libs/ui/widgets/kis_multi_integer_filter_widget.cc b/libs/ui/widgets/kis_multi_integer_filter_widget.cc
index b7388c20f4..fdbbd30e41 100644
--- a/libs/ui/widgets/kis_multi_integer_filter_widget.cc
+++ b/libs/ui/widgets/kis_multi_integer_filter_widget.cc
@@ -1,154 +1,155 @@
/*
* Copyright (c) 2004 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 "widgets/kis_multi_integer_filter_widget.h"
#include <QLabel>
#include <QLayout>
#include <QTimer>
#include <QSpinBox>
#include <QGridLayout>
#include <filter/kis_filter_configuration.h>
#include <klocalizedstring.h>
+#include <KisGlobalResourcesInterface.h>
KisDelayedActionIntegerInput::KisDelayedActionIntegerInput(QWidget * parent, const QString & name)
: KisIntParseSpinBox(parent)
{
setObjectName(name);
m_timer = new QTimer(this);
m_timer->setObjectName(name);
m_timer->setSingleShot(true);
connect(m_timer, SIGNAL(timeout()), SLOT(slotValueChanged()));
connect(this, SIGNAL(valueChanged(int)), SLOT(slotTimeToUpdate()));
}
void KisDelayedActionIntegerInput::slotTimeToUpdate()
{
m_timer->start(50);
}
void KisDelayedActionIntegerInput::slotValueChanged()
{
emit valueChangedDelayed(value());
}
void KisDelayedActionIntegerInput::cancelDelayedSignal()
{
m_timer->stop();
}
KisIntegerWidgetParam::KisIntegerWidgetParam(qint32 nmin, qint32 nmax, qint32 ninitvalue, const QString & label, const QString & nname)
: min(nmin)
, max(nmax)
, initvalue(ninitvalue)
, label(label)
, name(nname)
{
}
KisMultiIntegerFilterWidget::KisMultiIntegerFilterWidget(const QString& filterid,
QWidget* parent,
const QString& caption,
vKisIntegerWidgetParam iwparam)
: KisConfigWidget(parent)
, m_filterid(filterid)
- , m_config(new KisFilterConfiguration(filterid, 0))
+ , m_config(new KisFilterConfiguration(filterid, 0, KisGlobalResourcesInterface::instance()))
{
this->setWindowTitle(caption);
QGridLayout *widgetLayout = new QGridLayout(this);
widgetLayout->setColumnStretch(1, 1);
widgetLayout->setContentsMargins(0,0,0,0);
widgetLayout->setHorizontalSpacing(0);
for (uint i = 0; i < iwparam.size(); ++i) {
KisDelayedActionIntegerInput *widget = new KisDelayedActionIntegerInput(this, iwparam[i].name);
widget->setRange(iwparam[i].min, iwparam[i].max);
widget->setValue(iwparam[i].initvalue);
widget->cancelDelayedSignal();
connect(widget, SIGNAL(valueChangedDelayed(int)), SIGNAL(sigConfigurationItemChanged()));
QLabel* lbl = new QLabel(iwparam[i].label + ':', this);
widgetLayout->addWidget(lbl, i , 0);
widgetLayout->addWidget(widget, i , 1);
m_integerWidgets.append(widget);
}
widgetLayout->setRowStretch(iwparam.size(),1);
QSpacerItem * sp = new QSpacerItem(1, 1);
widgetLayout->addItem(sp, iwparam.size(), 0);
}
KisMultiIntegerFilterWidget::~KisMultiIntegerFilterWidget()
{
}
void KisMultiIntegerFilterWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
if (!config) return;
if (!m_config) {
- m_config = new KisFilterConfiguration(m_filterid, 0);
+ m_config = new KisFilterConfiguration(m_filterid, 0, KisGlobalResourcesInterface::instance());
}
m_config->fromXML(config->toXML());
for (int i = 0; i < nbValues(); ++i) {
KisDelayedActionIntegerInput* w = m_integerWidgets[i];
if (w) {
int val = config->getInt(m_integerWidgets[i]->objectName());
m_integerWidgets[i]->setValue(val);
m_integerWidgets[i]->cancelDelayedSignal();
}
}
}
KisPropertiesConfigurationSP KisMultiIntegerFilterWidget::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration(m_filterid, 0);
+ KisFilterConfigurationSP config = new KisFilterConfiguration(m_filterid, 0, KisGlobalResourcesInterface::instance());
if (m_config) {
config->fromXML(m_config->toXML());
}
for (int i = 0; i < nbValues(); ++i) {
KisDelayedActionIntegerInput* w = m_integerWidgets[i];
if (w) {
config->setProperty(w->objectName(), w->value());
}
}
return config;
}
qint32 KisMultiIntegerFilterWidget::nbValues() const {
return m_integerWidgets.size();
}
qint32 KisMultiIntegerFilterWidget::valueAt(qint32 i) {
if (i < m_integerWidgets.size()) {
return m_integerWidgets[i]->value();
}
else {
warnKrita << "Trying to access integer widget" << i << "but there are only" << m_integerWidgets.size() << "widgets";
return 0;
}
}
diff --git a/libs/ui/widgets/kis_paintop_presets_chooser_popup.cpp b/libs/ui/widgets/kis_paintop_presets_chooser_popup.cpp
index beba8024af..a3db9e091d 100644
--- a/libs/ui/widgets/kis_paintop_presets_chooser_popup.cpp
+++ b/libs/ui/widgets/kis_paintop_presets_chooser_popup.cpp
@@ -1,165 +1,165 @@
/* This file is part of the KDE project
* Copyright (c) 2010 Sven Langkamp <sven.langkamp@gmail.com>
* Copyright 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 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_paintop_presets_chooser_popup.h"
#include <QToolButton>
#include <QCompleter>
#include <QMenu>
#include <QWidgetAction>
#include <QSlider>
-#include <resources/KoResource.h>
-#include <KoResourceItemChooser.h>
+#include <KoResource.h>
+#include <KisResourceItemChooser.h>
#include <ui_wdgpaintoppresets.h>
#include <kis_config.h>
#include <KisResourceServerProvider.h>
#include <brushengine/kis_paintop_preset.h>
#include <kis_icon.h>
#include <brushengine/kis_paintop_settings.h>
struct KisPaintOpPresetsChooserPopup::Private
{
public:
Ui_WdgPaintOpPresets uiWdgPaintOpPresets;
bool firstShown;
QToolButton *viewModeButton;
};
KisPaintOpPresetsChooserPopup::KisPaintOpPresetsChooserPopup(QWidget * parent)
: QWidget(parent)
, m_d(new Private())
{
m_d->uiWdgPaintOpPresets.setupUi(this);
QMenu* menu = new QMenu(this);
menu->setStyleSheet("margin: 6px");
menu->addSection(i18nc("@title Which elements to display (e.g., thumbnails or details)", "Display"));
QActionGroup *actionGroup = new QActionGroup(this);
KisPresetChooser::ViewMode mode = (KisPresetChooser::ViewMode)KisConfig(true).presetChooserViewMode();
QAction* action = menu->addAction(KisIconUtils::loadIcon("view-preview"), i18n("Thumbnails"), this, SLOT(slotThumbnailMode()));
action->setCheckable(true);
action->setChecked(mode == KisPresetChooser::THUMBNAIL);
action->setActionGroup(actionGroup);
action = menu->addAction(KisIconUtils::loadIcon("view-list-details"), i18n("Details"), this, SLOT(slotDetailMode()));
action->setCheckable(true);
action->setChecked(mode == KisPresetChooser::DETAIL);
action->setActionGroup(actionGroup);
// add widget slider to control icon size
QSlider* iconSizeSlider = new QSlider(this);
iconSizeSlider->setOrientation(Qt::Horizontal);
iconSizeSlider->setRange(30, 80);
iconSizeSlider->setValue(m_d->uiWdgPaintOpPresets.wdgPresetChooser->iconSize());
iconSizeSlider->setMinimumHeight(20);
iconSizeSlider->setMinimumWidth(40);
iconSizeSlider->setTickInterval(10);
QWidgetAction *sliderAction= new QWidgetAction(this);
sliderAction->setDefaultWidget(iconSizeSlider);
menu->addSection(i18n("Icon Size"));
menu->addAction(sliderAction);
// setting the view mode
m_d->uiWdgPaintOpPresets.wdgPresetChooser->setViewMode(mode);
m_d->uiWdgPaintOpPresets.wdgPresetChooser->showTaggingBar(true);
m_d->uiWdgPaintOpPresets.wdgPresetChooser->itemChooser()->setViewModeButtonVisible(true);
m_d->viewModeButton = m_d->uiWdgPaintOpPresets.wdgPresetChooser->itemChooser()->viewModeButton();
m_d->viewModeButton->setMenu(menu);
m_d->viewModeButton->setIcon(KisIconUtils::loadIcon("configure"));
- connect(m_d->uiWdgPaintOpPresets.wdgPresetChooser, SIGNAL(resourceSelected(KoResource*)),
- this, SIGNAL(resourceSelected(KoResource*)));
- connect(m_d->uiWdgPaintOpPresets.wdgPresetChooser, SIGNAL(resourceClicked(KoResource*)),
- this, SIGNAL(resourceClicked(KoResource*))) ;
+ connect(m_d->uiWdgPaintOpPresets.wdgPresetChooser, SIGNAL(resourceSelected(KoResourceSP )),
+ this, SIGNAL(resourceSelected(KoResourceSP )));
+ connect(m_d->uiWdgPaintOpPresets.wdgPresetChooser, SIGNAL(resourceClicked(KoResourceSP )),
+ this, SIGNAL(resourceClicked(KoResourceSP ))) ;
connect (iconSizeSlider, SIGNAL(sliderMoved(int)),
m_d->uiWdgPaintOpPresets.wdgPresetChooser, SLOT(setIconSize(int)));
connect( iconSizeSlider, SIGNAL(sliderReleased()),
m_d->uiWdgPaintOpPresets.wdgPresetChooser, SLOT(saveIconSize()));
m_d->firstShown = true;
}
KisPaintOpPresetsChooserPopup::~KisPaintOpPresetsChooserPopup()
{
delete m_d;
}
void KisPaintOpPresetsChooserPopup::slotThumbnailMode()
{
KisConfig(false).setPresetChooserViewMode(KisPresetChooser::THUMBNAIL);
m_d->uiWdgPaintOpPresets.wdgPresetChooser->setViewMode(KisPresetChooser::THUMBNAIL);
}
void KisPaintOpPresetsChooserPopup::slotDetailMode()
{
KisConfig(false).setPresetChooserViewMode(KisPresetChooser::DETAIL);
m_d->uiWdgPaintOpPresets.wdgPresetChooser->setViewMode(KisPresetChooser::DETAIL);
}
void KisPaintOpPresetsChooserPopup::paintEvent(QPaintEvent* event)
{
QWidget::paintEvent(event);
//Workaround to get the column and row size right
if(m_d->firstShown) {
m_d->uiWdgPaintOpPresets.wdgPresetChooser->updateViewSettings();
m_d->firstShown = false;
}
}
void KisPaintOpPresetsChooserPopup::showButtons(bool show)
{
m_d->uiWdgPaintOpPresets.wdgPresetChooser->showButtons(show);
}
void KisPaintOpPresetsChooserPopup::canvasResourceChanged(KisPaintOpPresetSP preset)
{
if (preset) {
blockSignals(true);
- m_d->uiWdgPaintOpPresets.wdgPresetChooser->setCurrentResource(preset.data());
+ m_d->uiWdgPaintOpPresets.wdgPresetChooser->setCurrentResource(preset);
blockSignals(false);
}
m_d->uiWdgPaintOpPresets.wdgPresetChooser->updateViewSettings();
}
void KisPaintOpPresetsChooserPopup::slotThemeChanged()
{
m_d->viewModeButton->setIcon(KisIconUtils::loadIcon("configure"));
}
void KisPaintOpPresetsChooserPopup::updateViewSettings()
{
m_d->uiWdgPaintOpPresets.wdgPresetChooser->updateViewSettings();
}
diff --git a/libs/ui/widgets/kis_paintop_presets_chooser_popup.h b/libs/ui/widgets/kis_paintop_presets_chooser_popup.h
index 98a4bf73a4..f8a1c2b70a 100644
--- a/libs/ui/widgets/kis_paintop_presets_chooser_popup.h
+++ b/libs/ui/widgets/kis_paintop_presets_chooser_popup.h
@@ -1,59 +1,59 @@
/* This file is part of the KDE project
* Copyright (c) 2010 Sven Langkamp <sven.langkamp@gmail.com>
* Copyright 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 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_PRESETS_CHOOSER_POPUP_H
#define KIS_PAINTOP_PRESETS_CHOOSER_POPUP_H
#include <QWidget>
#include <KoID.h>
#include <kritaui_export.h>
#include <brushengine/kis_paintop_preset.h>
class KoResource;
class KRITAUI_EXPORT KisPaintOpPresetsChooserPopup : public QWidget
{
Q_OBJECT
public:
KisPaintOpPresetsChooserPopup(QWidget * parent = 0);
~KisPaintOpPresetsChooserPopup() override;
void showButtons(bool show);
void updateViewSettings();
public Q_SLOTS:
- void canvasResourceChanged(KisPaintOpPresetSP preset );
+ void canvasResourceChanged(KisPaintOpPresetSP preset);
void slotThemeChanged();
Q_SIGNALS:
- void resourceSelected( KoResource * resource);
- void resourceClicked( KoResource * resource);
+ void resourceSelected(KoResourceSP resource);
+ void resourceClicked(KoResourceSP resource);
private Q_SLOTS:
void slotThumbnailMode();
void slotDetailMode();
- void paintEvent(QPaintEvent* ) override;
+ void paintEvent(QPaintEvent *) override;
private:
struct Private;
Private * const m_d;
};
#endif // KIS_PAINTOP_PRESETS_CHOOSER_POPUP_H
diff --git a/libs/ui/widgets/kis_paintop_presets_popup.cpp b/libs/ui/widgets/kis_paintop_presets_popup.cpp
index 9a27502383..c74a69315b 100644
--- a/libs/ui/widgets/kis_paintop_presets_popup.cpp
+++ b/libs/ui/widgets/kis_paintop_presets_popup.cpp
@@ -1,850 +1,845 @@
/* This file is part of the KDE project
* Copyright (C) 2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2010 Lukáš Tvrdý <lukast.dev@gmail.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 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_paintop_presets_popup.h"
#include <QList>
#include <QComboBox>
#include <QHBoxLayout>
#include <QToolButton>
#include <QGridLayout>
#include <QFont>
#include <QMenu>
#include <QAction>
#include <QShowEvent>
#include <QFontDatabase>
#include <QWidgetAction>
#include <QDesktopWidget>
#include <kconfig.h>
#include <klocalizedstring.h>
#include <KoDockRegistry.h>
#include <kis_icon.h>
#include <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_config_widget.h>
#include <kis_canvas_resource_provider.h>
#include <widgets/kis_preset_chooser.h>
#include <widgets/kis_preset_selector_strip.h>
#include <ui_wdgpaintopsettings.h>
#include <kis_node.h>
#include "kis_config.h"
#include "KisResourceServerProvider.h"
#include "kis_lod_availability_widget.h"
#include "kis_signal_auto_connection.h"
#include <kis_paintop_settings_update_proxy.h>
// ones from brush engine selector
#include <brushengine/kis_paintop_factory.h>
#include <kis_preset_live_preview_view.h>
struct KisPaintOpPresetsPopup::Private
{
public:
Ui_WdgPaintOpSettings uiWdgPaintOpPresetSettings;
QGridLayout *layout;
KisPaintOpConfigWidget *settingsWidget;
QFont smallFont;
KisCanvasResourceProvider *resourceProvider;
KisFavoriteResourceManager *favoriteResManager;
bool detached;
bool ignoreHideEvents;
bool isCreatingBrushFromScratch = false;
QSize minimumSettingsWidgetSize;
QRect detachedGeometry;
KisSignalAutoConnectionsStore widgetConnections;
};
KisPaintOpPresetsPopup::KisPaintOpPresetsPopup(KisCanvasResourceProvider * resourceProvider,
KisFavoriteResourceManager* favoriteResourceManager,
KisPresetSaveWidget* savePresetWidget,
QWidget * parent)
: QWidget(parent)
, m_d(new Private())
{
setObjectName("KisPaintOpPresetsPopup");
setFont(KoDockRegistry::dockFont());
current_paintOpId = "";
m_d->resourceProvider = resourceProvider;
m_d->favoriteResManager = favoriteResourceManager;
m_d->uiWdgPaintOpPresetSettings.setupUi(this);
m_d->layout = new QGridLayout(m_d->uiWdgPaintOpPresetSettings.frmOptionWidgetContainer);
m_d->layout->setSizeConstraint(QLayout::SetFixedSize);
m_d->uiWdgPaintOpPresetSettings.scratchPad->setupScratchPad(resourceProvider, Qt::white);
m_d->uiWdgPaintOpPresetSettings.scratchPad->setCutoutOverlayRect(QRect(25, 25, 200, 200));
m_d->uiWdgPaintOpPresetSettings.dirtyPresetIndicatorButton->setToolTip(i18n("The settings for this preset have changed from their default."));
m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setToolTip(i18n("Toggle showing presets"));
m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setToolTip(i18n("Toggle showing scratchpad"));
m_d->uiWdgPaintOpPresetSettings.reloadPresetButton->setToolTip(i18n("Reload the brush preset"));
m_d->uiWdgPaintOpPresetSettings.renameBrushPresetButton->setToolTip(i18n("Rename the brush preset"));
// creating a new preset from scratch. Part of the brush presets area
// the menu options will get filled up later when we are generating all available paintops
// in the filter drop-down
newPresetBrushEnginesMenu = new QMenu();
// overwrite existing preset and saving a new preset use the same dialog
saveDialog = savePresetWidget;
saveDialog->scratchPadSetup(resourceProvider);
saveDialog->setFavoriteResourceManager(m_d->favoriteResManager); // this is needed when saving the preset
saveDialog->hide();
// the area on the brush editor for renaming the brush. make sure edit fields are hidden by default
toggleBrushRenameUIActive(false);
// DETAIL and THUMBNAIL view changer
QMenu* menu = new QMenu(this);
menu->setStyleSheet("margin: 6px");
menu->addSection(i18nc("@title Which elements to display (e.g., thumbnails or details)", "Display"));
QActionGroup *actionGroup = new QActionGroup(this);
KisPresetChooser::ViewMode mode = (KisPresetChooser::ViewMode)KisConfig(true).presetChooserViewMode();
QAction* action = menu->addAction(KisIconUtils::loadIcon("view-preview"), i18n("Thumbnails"), m_d->uiWdgPaintOpPresetSettings.presetWidget, SLOT(slotThumbnailMode()));
action->setCheckable(true);
action->setChecked(mode == KisPresetChooser::THUMBNAIL);
action->setActionGroup(actionGroup);
action = menu->addAction(KisIconUtils::loadIcon("view-list-details"), i18n("Details"), m_d->uiWdgPaintOpPresetSettings.presetWidget, SLOT(slotDetailMode()));
action->setCheckable(true);
action->setChecked(mode == KisPresetChooser::DETAIL);
action->setActionGroup(actionGroup);
// add horizontal slider for the icon size
QSlider* iconSizeSlider = new QSlider(this);
iconSizeSlider->setOrientation(Qt::Horizontal);
iconSizeSlider->setRange(30, 80);
iconSizeSlider->setValue(m_d->uiWdgPaintOpPresetSettings.presetWidget->iconSize());
iconSizeSlider->setMinimumHeight(20);
iconSizeSlider->setMinimumWidth(40);
iconSizeSlider->setTickInterval(10);
QWidgetAction *sliderAction= new QWidgetAction(this);
sliderAction->setDefaultWidget(iconSizeSlider);
menu->addSection(i18n("Icon Size"));
menu->addAction(sliderAction);
// configure the button and assign menu
m_d->uiWdgPaintOpPresetSettings.presetChangeViewToolButton->setMenu(menu);
m_d->uiWdgPaintOpPresetSettings.presetChangeViewToolButton->setPopupMode(QToolButton::InstantPopup);
// loading preset from scratch option
m_d->uiWdgPaintOpPresetSettings.newPresetEngineButton->setPopupMode(QToolButton::InstantPopup);
// show/hide buttons
KisConfig cfg(true);
m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setCheckable(true);
m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setChecked(cfg.scratchpadVisible());
if (cfg.scratchpadVisible()) {
slotSwitchScratchpad(true); // show scratchpad
} else {
slotSwitchScratchpad(false);
}
m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setCheckable(true);
m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setChecked(false);
slotSwitchShowPresets(false); // hide presets by default
// Connections
connect(m_d->uiWdgPaintOpPresetSettings.paintPresetIcon, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(paintPresetImage()));
- connect(saveDialog, SIGNAL(resourceSelected(KoResource*)), this, SLOT(resourceSelected(KoResource*)));
+ connect(saveDialog, SIGNAL(resourceSelected(KoResourceSP )), this, SLOT(resourceSelected(KoResourceSP )));
connect (m_d->uiWdgPaintOpPresetSettings.renameBrushPresetButton, SIGNAL(clicked(bool)),
this, SLOT(slotRenameBrushActivated()));
connect (m_d->uiWdgPaintOpPresetSettings.cancelBrushNameUpdateButton, SIGNAL(clicked(bool)),
this, SLOT(slotRenameBrushDeactivated()));
connect(m_d->uiWdgPaintOpPresetSettings.updateBrushNameButton, SIGNAL(clicked(bool)),
this, SLOT(slotSaveRenameCurrentBrush()));
connect(m_d->uiWdgPaintOpPresetSettings.renameBrushNameTextField, SIGNAL(returnPressed()),
SLOT(slotSaveRenameCurrentBrush()));
connect(iconSizeSlider, SIGNAL(sliderMoved(int)),
m_d->uiWdgPaintOpPresetSettings.presetWidget, SLOT(slotSetIconSize(int)));
connect(iconSizeSlider, SIGNAL(sliderReleased()),
m_d->uiWdgPaintOpPresetSettings.presetWidget, SLOT(slotSaveIconSize()));
connect(m_d->uiWdgPaintOpPresetSettings.showScratchpadButton, SIGNAL(clicked(bool)),
this, SLOT(slotSwitchScratchpad(bool)));
connect(m_d->uiWdgPaintOpPresetSettings.showPresetsButton, SIGNAL(clicked(bool)), this, SLOT(slotSwitchShowPresets(bool)));
connect(m_d->uiWdgPaintOpPresetSettings.eraseScratchPad, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillDefault()));
connect(m_d->uiWdgPaintOpPresetSettings.fillLayer, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillLayer()));
connect(m_d->uiWdgPaintOpPresetSettings.fillGradient, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillGradient()));
connect(m_d->uiWdgPaintOpPresetSettings.fillSolid, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillBackground()));
m_d->settingsWidget = 0;
setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
connect(m_d->uiWdgPaintOpPresetSettings.saveBrushPresetButton, SIGNAL(clicked()),
this, SLOT(slotSaveBrushPreset()));
connect(m_d->uiWdgPaintOpPresetSettings.saveNewBrushPresetButton, SIGNAL(clicked()),
this, SLOT(slotSaveNewBrushPreset()));
connect(m_d->uiWdgPaintOpPresetSettings.reloadPresetButton, SIGNAL(clicked()),
this, SIGNAL(reloadPresetClicked()));
connect(m_d->uiWdgPaintOpPresetSettings.dirtyPresetCheckBox, SIGNAL(toggled(bool)),
this, SIGNAL(dirtyPresetToggled(bool)));
connect(m_d->uiWdgPaintOpPresetSettings.eraserBrushSizeCheckBox, SIGNAL(toggled(bool)),
this, SIGNAL(eraserBrushSizeToggled(bool)));
connect(m_d->uiWdgPaintOpPresetSettings.eraserBrushOpacityCheckBox, SIGNAL(toggled(bool)),
this, SIGNAL(eraserBrushOpacityToggled(bool)));
// preset widget connections
- connect(m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser, SIGNAL(resourceSelected(KoResource*)),
- this, SIGNAL(signalResourceSelected(KoResource*)));
+ connect(m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser, SIGNAL(resourceSelected(KoResourceSP )),
+ this, SIGNAL(signalResourceSelected(KoResourceSP )));
connect(m_d->uiWdgPaintOpPresetSettings.reloadPresetButton, SIGNAL(clicked()),
m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser, SLOT(updateViewSettings()));
connect(m_d->uiWdgPaintOpPresetSettings.reloadPresetButton, SIGNAL(clicked()), SLOT(slotUpdatePresetSettings()));
m_d->detached = false;
m_d->ignoreHideEvents = false;
m_d->minimumSettingsWidgetSize = QSize(0, 0);
m_d->detachedGeometry = QRect(100, 100, 0, 0);
m_d->uiWdgPaintOpPresetSettings.dirtyPresetCheckBox->setChecked(cfg.useDirtyPresets());
m_d->uiWdgPaintOpPresetSettings.eraserBrushSizeCheckBox->setChecked(cfg.useEraserBrushSize());
m_d->uiWdgPaintOpPresetSettings.eraserBrushOpacityCheckBox->setChecked(cfg.useEraserBrushOpacity());
m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->setCanvasResourceManager(resourceProvider->resourceManager());
connect(resourceProvider->resourceManager(),
SIGNAL(canvasResourceChanged(int,QVariant)),
SLOT(slotResourceChanged(int,QVariant)));
connect(m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability,
SIGNAL(sigUserChangedLodAvailability(bool)),
SLOT(slotLodAvailabilityChanged(bool)));
connect(m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability,
SIGNAL(sigUserChangedLodThreshold(qreal)),
SLOT(slotLodThresholdChanged(qreal)));
slotResourceChanged(KisCanvasResourceProvider::LodAvailability,
resourceProvider->resourceManager()->
resource(KisCanvasResourceProvider::LodAvailability));
slotResourceChanged(KisCanvasResourceProvider::LodSizeThreshold,
resourceProvider->resourceManager()->
resource(KisCanvasResourceProvider::LodSizeThreshold));
connect(m_d->uiWdgPaintOpPresetSettings.brushEgineComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdatePaintOpFilter()));
connect(m_d->uiWdgPaintOpPresetSettings.bnBlacklistPreset, SIGNAL(clicked()), this, SLOT(slotBlackListCurrentPreset()));
updateThemedIcons();
// setup things like the scene construct images, layers, etc that is a one-time thing
m_d->uiWdgPaintOpPresetSettings.liveBrushPreviewView->setup();
}
void KisPaintOpPresetsPopup::slotBlackListCurrentPreset()
{
KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
KisPaintOpPresetSP curPreset = m_d->resourceProvider->currentPreset();
if (rServer->resourceByName(curPreset->name())) {
- rServer->removeResourceAndBlacklist(curPreset);
+ rServer->removeResourceFromServer(curPreset);
}
}
void KisPaintOpPresetsPopup::slotRenameBrushActivated()
{
toggleBrushRenameUIActive(true);
}
void KisPaintOpPresetsPopup::slotRenameBrushDeactivated()
{
toggleBrushRenameUIActive(false);
}
void KisPaintOpPresetsPopup::toggleBrushRenameUIActive(bool isRenaming)
{
// This function doesn't really do anything except get the UI in a state to rename a brush preset
m_d->uiWdgPaintOpPresetSettings.renameBrushNameTextField->setVisible(isRenaming);
m_d->uiWdgPaintOpPresetSettings.updateBrushNameButton->setVisible(isRenaming);
m_d->uiWdgPaintOpPresetSettings.cancelBrushNameUpdateButton->setVisible(isRenaming);
// hide these below areas while renaming
m_d->uiWdgPaintOpPresetSettings.currentBrushNameLabel->setVisible(!isRenaming);
m_d->uiWdgPaintOpPresetSettings.renameBrushPresetButton->setVisible(!isRenaming);
m_d->uiWdgPaintOpPresetSettings.saveBrushPresetButton->setEnabled(!isRenaming);
m_d->uiWdgPaintOpPresetSettings.saveBrushPresetButton->setVisible(!isRenaming);
m_d->uiWdgPaintOpPresetSettings.saveNewBrushPresetButton->setEnabled(!isRenaming);
m_d->uiWdgPaintOpPresetSettings.saveNewBrushPresetButton->setVisible(!isRenaming);
// if the presets area is shown, only then can you show/hide the load default brush
// need to think about weird state when you are in the middle of renaming a brush
// what happens if you try to change presets. maybe we should auto-hide (or disable)
// the presets area in this case
if (m_d->uiWdgPaintOpPresetSettings.presetWidget->isVisible()) {
m_d->uiWdgPaintOpPresetSettings.newPresetEngineButton->setVisible(!isRenaming);
m_d->uiWdgPaintOpPresetSettings.bnBlacklistPreset->setVisible(!isRenaming);
}
}
void KisPaintOpPresetsPopup::slotSaveRenameCurrentBrush()
{
// if you are renaming a brush, that is different than updating the settings
// make sure we are in a clean state before renaming. This logic might change,
// but that is what we are going with for now
emit reloadPresetClicked();
-
- m_d->favoriteResManager->setBlockUpdates(true);
-
// get a reference to the existing (and new) file name and path that we are working with
KisPaintOpPresetSP curPreset = m_d->resourceProvider->currentPreset();
if (!curPreset)
return;
KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
- QString saveLocation = rServer->saveLocation();
QString originalPresetName = curPreset->name();
QString renamedPresetName = m_d->uiWdgPaintOpPresetSettings.renameBrushNameTextField->text();
- QString originalPresetPathAndFile = saveLocation + originalPresetName + curPreset->defaultFileExtension();
- QString renamedPresetPathAndFile = saveLocation + renamedPresetName + curPreset->defaultFileExtension();
+ QString renamedPresetPathAndFile = renamedPresetName + curPreset->defaultFileExtension();
// create a new brush preset with the name specified and add to resource provider
- KisPaintOpPresetSP newPreset = curPreset->clone();
+ KisPaintOpPresetSP newPreset = curPreset->clone().dynamicCast<KisPaintOpPreset>();
newPreset->setFilename(renamedPresetPathAndFile); // this also contains the path
newPreset->setName(renamedPresetName);
newPreset->setImage(curPreset->image()); // use existing thumbnail (might not need to do this)
newPreset->setDirty(false);
newPreset->setValid(true);
rServer->addResource(newPreset);
- resourceSelected(newPreset.data()); // refresh and select our freshly renamed resource
+ resourceSelected(newPreset); // refresh and select our freshly renamed resource
// Now blacklist the original file
if (rServer->resourceByName(originalPresetName)) {
- rServer->removeResourceAndBlacklist(curPreset);
+ rServer->removeResourceFromServer(curPreset);
}
- m_d->favoriteResManager->setBlockUpdates(false);
+ m_d->favoriteResManager->updateFavoritePresets();
toggleBrushRenameUIActive(false); // this returns the UI to its original state after saving
slotUpdatePresetSettings(); // update visibility of dirty preset and icon
}
void KisPaintOpPresetsPopup::slotResourceChanged(int key, const QVariant &value)
{
if (key == KisCanvasResourceProvider::LodAvailability) {
m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->slotUserChangedLodAvailability(value.toBool());
} else if (key == KisCanvasResourceProvider::LodSizeThreshold) {
m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->slotUserChangedLodThreshold(value.toDouble());
} else if (key == KisCanvasResourceProvider::Size) {
m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->slotUserChangedSize(value.toDouble());
}
}
void KisPaintOpPresetsPopup::slotLodAvailabilityChanged(bool value)
{
m_d->resourceProvider->resourceManager()->setResource(KisCanvasResourceProvider::LodAvailability, QVariant(value));
}
void KisPaintOpPresetsPopup::slotLodThresholdChanged(qreal value)
{
m_d->resourceProvider->resourceManager()->setResource(KisCanvasResourceProvider::LodSizeThreshold, QVariant(value));
}
KisPaintOpPresetsPopup::~KisPaintOpPresetsPopup()
{
if (m_d->settingsWidget) {
m_d->layout->removeWidget(m_d->settingsWidget);
m_d->settingsWidget->hide();
m_d->settingsWidget->setParent(0);
m_d->settingsWidget = 0;
}
delete m_d;
delete newPresetBrushEnginesMenu;
}
void KisPaintOpPresetsPopup::setPaintOpSettingsWidget(QWidget * widget)
{
if (m_d->settingsWidget) {
m_d->layout->removeWidget(m_d->settingsWidget);
m_d->uiWdgPaintOpPresetSettings.frmOptionWidgetContainer->updateGeometry();
}
m_d->layout->update();
updateGeometry();
m_d->widgetConnections.clear();
m_d->settingsWidget = 0;
if (widget) {
m_d->settingsWidget = dynamic_cast<KisPaintOpConfigWidget*>(widget);
KIS_ASSERT_RECOVER_RETURN(m_d->settingsWidget);
KisConfig cfg(true);
if (m_d->settingsWidget->supportScratchBox() && cfg.scratchpadVisible()) {
slotSwitchScratchpad(true);
} else {
slotSwitchScratchpad(false);
}
m_d->widgetConnections.addConnection(m_d->settingsWidget, SIGNAL(sigConfigurationItemChanged()),
this, SLOT(slotUpdateLodAvailability()));
widget->setFont(m_d->smallFont);
QSize hint = widget->sizeHint();
m_d->minimumSettingsWidgetSize = QSize(qMax(hint.width(), m_d->minimumSettingsWidgetSize.width()),
qMax(hint.height(), m_d->minimumSettingsWidgetSize.height()));
widget->setMinimumSize(m_d->minimumSettingsWidgetSize);
m_d->layout->addWidget(widget);
// hook up connections that will monitor if our preset is dirty or not. Show a notification if it is
if (m_d->resourceProvider && m_d->resourceProvider->currentPreset() ) {
KisPaintOpPresetSP preset = m_d->resourceProvider->currentPreset();
m_d->widgetConnections.addConnection(preset->updateProxy(), SIGNAL(sigSettingsChanged()),
this, SLOT(slotUpdatePresetSettings()));
}
m_d->layout->update();
widget->show();
}
slotUpdateLodAvailability();
}
void KisPaintOpPresetsPopup::slotUpdateLodAvailability()
{
if (!m_d->settingsWidget) return;
KisPaintopLodLimitations l = m_d->settingsWidget->lodLimitations();
m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->setLimitations(l);
}
QImage KisPaintOpPresetsPopup::cutOutOverlay()
{
return m_d->uiWdgPaintOpPresetSettings.scratchPad->cutoutOverlay();
}
void KisPaintOpPresetsPopup::contextMenuEvent(QContextMenuEvent *e)
{
Q_UNUSED(e);
}
void KisPaintOpPresetsPopup::switchDetached(bool show)
{
if (parentWidget()) {
m_d->detached = !m_d->detached;
if (m_d->detached) {
m_d->ignoreHideEvents = true;
if (show) {
parentWidget()->show();
}
m_d->ignoreHideEvents = false;
}
else {
parentWidget()->hide();
}
KisConfig cfg(false);
cfg.setPaintopPopupDetached(m_d->detached);
}
}
void KisPaintOpPresetsPopup::setCreatingBrushFromScratch(bool enabled)
{
m_d->isCreatingBrushFromScratch = enabled;
}
-void KisPaintOpPresetsPopup::resourceSelected(KoResource* resource)
+void KisPaintOpPresetsPopup::resourceSelected(KoResourceSP resource)
{
// this gets called every time the brush editor window is opened
// TODO: this gets called multiple times whenever the preset is changed in the presets area
// the connections probably need to be thought about with this a bit more to keep things in sync
m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser->setCurrentResource(resource);
// find the display name of the brush engine and append it to the selected preset display
QString currentBrushEngineName;
QPixmap currentBrushEngineIcon = QPixmap(26, 26);
currentBrushEngineIcon.fill(Qt::transparent);
for(int i=0; i < sortedBrushEnginesList.length(); i++) {
if (sortedBrushEnginesList.at(i).id == currentPaintOpId() ) {
currentBrushEngineName = sortedBrushEnginesList.at(i).name;
currentBrushEngineIcon = sortedBrushEnginesList.at(i).icon.pixmap(26, 26);
}
}
// brush names have underscores as part of the file name (to help with building). We don't really need underscores
// when viewing the names, so replace them with spaces
QString formattedBrushName = resource->name().replace("_", " ");
m_d->uiWdgPaintOpPresetSettings.currentBrushNameLabel->setText(formattedBrushName);
m_d->uiWdgPaintOpPresetSettings.currentBrushEngineLabel->setText(i18nc("%1 is the name of a brush engine", "%1 Engine", currentBrushEngineName));
m_d->uiWdgPaintOpPresetSettings.currentBrushEngineIcon->setPixmap(currentBrushEngineIcon);
m_d->uiWdgPaintOpPresetSettings.renameBrushNameTextField->setText(resource->name()); // use file name
// get the preset image and pop it into the thumbnail area on the top of the brush editor
m_d->uiWdgPaintOpPresetSettings.presetThumbnailicon->setPixmap(QPixmap::fromImage(resource->image().scaled(55, 55, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
toggleBrushRenameUIActive(false); // reset the UI state of renaming a brush if we are changing brush presets
slotUpdatePresetSettings(); // check to see if the dirty preset icon needs to be shown
}
bool variantLessThan(const KisPaintOpInfo v1, const KisPaintOpInfo v2)
{
return v1.priority < v2.priority;
}
void KisPaintOpPresetsPopup::setPaintOpList(const QList< KisPaintOpFactory* >& list)
{
m_d->uiWdgPaintOpPresetSettings.brushEgineComboBox->clear(); // reset combobox list just in case
// create a new list so we can sort it and populate the brush engine combo box
sortedBrushEnginesList.clear(); // just in case this function is called again, don't keep adding to the list
for(int i=0; i < list.length(); i++) {
KisPaintOpInfo paintOpInfo;
paintOpInfo.id = list.at(i)->id();
paintOpInfo.name = list.at(i)->name();
paintOpInfo.icon = list.at(i)->icon();
paintOpInfo.priority = list.at(i)->priority();
sortedBrushEnginesList.append(paintOpInfo);
}
std::stable_sort(sortedBrushEnginesList.begin(), sortedBrushEnginesList.end(), variantLessThan );
// add an "All" option at the front to show all presets
QPixmap emptyPixmap = QPixmap(22,22);
emptyPixmap.fill(Qt::transparent);
// if we create a new brush from scratch, we need a full list of paintops to choose from
// we don't want "All", so populate the list before that is added
newPresetBrushEnginesMenu->actions().clear(); // clean out list in case we run this again
newBrushEngineOptions.clear();
for (int j = 0; j < sortedBrushEnginesList.length(); j++) {
KisAction * newEngineAction = static_cast<KisAction*>( newPresetBrushEnginesMenu->addAction(sortedBrushEnginesList[j].name));
newEngineAction->setObjectName(sortedBrushEnginesList[j].id); // we need the ID for changing the paintop when action triggered
newEngineAction->setIcon(sortedBrushEnginesList[j].icon);
newBrushEngineOptions.append(newEngineAction);
connect(newEngineAction, SIGNAL(triggered()), this, SLOT(slotCreateNewBrushPresetEngine()));
}
m_d->uiWdgPaintOpPresetSettings.newPresetEngineButton->setMenu(newPresetBrushEnginesMenu);
// fill the list into the brush combo box
sortedBrushEnginesList.push_front(KisPaintOpInfo(QString("all_options"), i18n("All"), QString(""), QIcon(emptyPixmap), 0 ));
for (int m = 0; m < sortedBrushEnginesList.length(); m++) {
m_d->uiWdgPaintOpPresetSettings.brushEgineComboBox->addItem(sortedBrushEnginesList[m].icon, sortedBrushEnginesList[m].name, QVariant(sortedBrushEnginesList[m].id));
}
}
void KisPaintOpPresetsPopup::setCurrentPaintOpId(const QString& paintOpId)
{
current_paintOpId = paintOpId;
}
QString KisPaintOpPresetsPopup::currentPaintOpId() {
return current_paintOpId;
}
void KisPaintOpPresetsPopup::setPresetImage(const QImage& image)
{
m_d->uiWdgPaintOpPresetSettings.scratchPad->setPresetImage(image);
saveDialog->brushPresetThumbnailWidget->setPresetImage(image);
}
void KisPaintOpPresetsPopup::hideEvent(QHideEvent *event)
{
if (m_d->ignoreHideEvents) {
return;
}
if (m_d->detached) {
m_d->detachedGeometry = window()->geometry();
}
QWidget::hideEvent(event);
}
void KisPaintOpPresetsPopup::showEvent(QShowEvent *)
{
if (m_d->detached) {
window()->setGeometry(m_d->detachedGeometry);
}
emit brushEditorShown();
}
void KisPaintOpPresetsPopup::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
if (parentWidget()) {
// Make sure resizing doesn't push this widget out of the screen
QRect screenRect = QApplication::desktop()->availableGeometry(this);
QRect newPositionRect = kisEnsureInRect(parentWidget()->geometry(), screenRect);
parentWidget()->setGeometry(newPositionRect);
}
}
bool KisPaintOpPresetsPopup::detached() const
{
return m_d->detached;
}
void KisPaintOpPresetsPopup::slotSwitchScratchpad(bool visible)
{
// hide all the internal controls except the toggle button
m_d->uiWdgPaintOpPresetSettings.scratchPad->setVisible(visible);
m_d->uiWdgPaintOpPresetSettings.paintPresetIcon->setVisible(visible);
m_d->uiWdgPaintOpPresetSettings.fillGradient->setVisible(visible);
m_d->uiWdgPaintOpPresetSettings.fillLayer->setVisible(visible);
m_d->uiWdgPaintOpPresetSettings.fillSolid->setVisible(visible);
m_d->uiWdgPaintOpPresetSettings.eraseScratchPad->setVisible(visible);
m_d->uiWdgPaintOpPresetSettings.scratchpadSidebarLabel->setVisible(visible);
if (visible) {
m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setIcon(KisIconUtils::loadIcon("arrow-left"));
} else {
m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setIcon(KisIconUtils::loadIcon("arrow-right"));
}
KisConfig cfg(false);
cfg.setScratchpadVisible(visible);
}
void KisPaintOpPresetsPopup::slotSwitchShowEditor(bool visible) {
m_d->uiWdgPaintOpPresetSettings.brushEditorSettingsControls->setVisible(visible);
}
void KisPaintOpPresetsPopup::slotSwitchShowPresets(bool visible) {
m_d->uiWdgPaintOpPresetSettings.presetWidget->setVisible(visible);
m_d->uiWdgPaintOpPresetSettings.presetChangeViewToolButton->setVisible(visible);
m_d->uiWdgPaintOpPresetSettings.brushEgineComboBox->setVisible(visible);
m_d->uiWdgPaintOpPresetSettings.engineFilterLabel->setVisible(visible);
m_d->uiWdgPaintOpPresetSettings.presetsSidebarLabel->setVisible(visible);
m_d->uiWdgPaintOpPresetSettings.newPresetEngineButton->setVisible(visible);
m_d->uiWdgPaintOpPresetSettings.bnBlacklistPreset->setVisible(visible);
// we only want a spacer to work when the toggle icon is present. Otherwise the list of presets will shrink
// which is something we don't want
if (visible) {
m_d->uiWdgPaintOpPresetSettings.presetsSpacer->changeSize(0,0, QSizePolicy::Ignored,QSizePolicy::Ignored);
m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setIcon(KisIconUtils::loadIcon("arrow-right"));
} else {
m_d->uiWdgPaintOpPresetSettings.presetsSpacer->changeSize(0,0, QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding);
m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setIcon(KisIconUtils::loadIcon("arrow-left"));
}
}
void KisPaintOpPresetsPopup::slotUpdatePaintOpFilter() {
QVariant userData = m_d->uiWdgPaintOpPresetSettings.brushEgineComboBox->currentData(); // grab paintOpID from data
QString filterPaintOpId = userData.toString();
if (filterPaintOpId == "all_options") {
filterPaintOpId = "";
}
m_d->uiWdgPaintOpPresetSettings.presetWidget->setPresetFilter(filterPaintOpId);
}
void KisPaintOpPresetsPopup::slotSaveBrushPreset() {
// here we are assuming that people want to keep their existing preset icon. We will just update the
// settings and save a new copy with the same name.
// there is a dialog with save options, but we don't need to show it in this situation
saveDialog->useNewBrushDialog(false); // this mostly just makes sure we keep the existing brush preset name when saving
saveDialog->loadExistingThumbnail(); // This makes sure we use the existing preset icon when updating the existing brush preset
saveDialog->savePreset();
// refresh the view settings so the brush doesn't appear dirty
slotUpdatePresetSettings();
}
void KisPaintOpPresetsPopup::slotSaveNewBrushPreset() {
saveDialog->useNewBrushDialog(true);
saveDialog->saveScratchPadThumbnailArea(m_d->uiWdgPaintOpPresetSettings.scratchPad->cutoutOverlay());
saveDialog->showDialog();
}
void KisPaintOpPresetsPopup::slotCreateNewBrushPresetEngine()
{
emit createPresetFromScratch(sender()->objectName());
}
void KisPaintOpPresetsPopup::updateViewSettings()
{
m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser->updateViewSettings();
}
void KisPaintOpPresetsPopup::currentPresetChanged(KisPaintOpPresetSP preset)
{
if (preset) {
- m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser->setCurrentResource(preset.data());
+ m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser->setCurrentResource(preset);
setCurrentPaintOpId(preset->paintOp().id());
}
}
void KisPaintOpPresetsPopup::updateThemedIcons()
{
m_d->uiWdgPaintOpPresetSettings.paintPresetIcon->setIcon(KisIconUtils::loadIcon("krita_tool_freehand"));
m_d->uiWdgPaintOpPresetSettings.fillLayer->setIcon(KisIconUtils::loadIcon("document-new"));
m_d->uiWdgPaintOpPresetSettings.fillLayer->hide();
m_d->uiWdgPaintOpPresetSettings.fillGradient->setIcon(KisIconUtils::loadIcon("krita_tool_gradient"));
m_d->uiWdgPaintOpPresetSettings.fillSolid->setIcon(KisIconUtils::loadIcon("krita_tool_color_fill"));
m_d->uiWdgPaintOpPresetSettings.eraseScratchPad->setIcon(KisIconUtils::loadIcon("edit-delete"));
m_d->uiWdgPaintOpPresetSettings.newPresetEngineButton->setIcon(KisIconUtils::loadIcon("addlayer"));
m_d->uiWdgPaintOpPresetSettings.bnBlacklistPreset->setIcon(KisIconUtils::loadIcon("deletelayer"));
m_d->uiWdgPaintOpPresetSettings.reloadPresetButton->setIcon(KisIconUtils::loadIcon("updateColorize")); // refresh icon
m_d->uiWdgPaintOpPresetSettings.renameBrushPresetButton->setIcon(KisIconUtils::loadIcon("dirty-preset")); // edit icon
m_d->uiWdgPaintOpPresetSettings.dirtyPresetIndicatorButton->setIcon(KisIconUtils::loadIcon("warning"));
m_d->uiWdgPaintOpPresetSettings.newPresetEngineButton->setIcon(KisIconUtils::loadIcon("addlayer"));
m_d->uiWdgPaintOpPresetSettings.bnBlacklistPreset->setIcon(KisIconUtils::loadIcon("deletelayer"));
m_d->uiWdgPaintOpPresetSettings.presetChangeViewToolButton->setIcon(KisIconUtils::loadIcon("configure"));
// if we cannot see the "Preset label", we know it is not visible
// maybe this can also be stored in the config like the scratchpad?
if (m_d->uiWdgPaintOpPresetSettings.presetsSidebarLabel->isVisible()) {
m_d->uiWdgPaintOpPresetSettings.presetsSpacer->changeSize(0,0, QSizePolicy::Ignored,QSizePolicy::Ignored);
m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setIcon(KisIconUtils::loadIcon("arrow-right"));
} else {
m_d->uiWdgPaintOpPresetSettings.presetsSpacer->changeSize(0,0, QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding);
m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setIcon(KisIconUtils::loadIcon("arrow-left"));
}
// we store whether the scratchpad if visible in the config.
KisConfig cfg(true);
if (cfg.scratchpadVisible()) {
m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setIcon(KisIconUtils::loadIcon("arrow-left"));
} else {
m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setIcon(KisIconUtils::loadIcon("arrow-right"));
}
}
void KisPaintOpPresetsPopup::slotUpdatePresetSettings()
{
if (!m_d->resourceProvider) {
return;
}
if (!m_d->resourceProvider->currentPreset()) {
return;
}
// hide options on UI if we are creating a brush preset from scratch to prevent confusion
if (m_d->isCreatingBrushFromScratch) {
m_d->uiWdgPaintOpPresetSettings.dirtyPresetIndicatorButton->setVisible(false);
m_d->uiWdgPaintOpPresetSettings.reloadPresetButton->setVisible(false);
m_d->uiWdgPaintOpPresetSettings.saveBrushPresetButton->setVisible(false);
m_d->uiWdgPaintOpPresetSettings.renameBrushPresetButton->setVisible(false);
} else {
bool isPresetDirty = m_d->resourceProvider->currentPreset()->isDirty();
// don't need to reload or overwrite a clean preset
m_d->uiWdgPaintOpPresetSettings.dirtyPresetIndicatorButton->setVisible(isPresetDirty);
m_d->uiWdgPaintOpPresetSettings.reloadPresetButton->setVisible(isPresetDirty);
m_d->uiWdgPaintOpPresetSettings.saveBrushPresetButton->setEnabled(isPresetDirty);
m_d->uiWdgPaintOpPresetSettings.renameBrushPresetButton->setVisible(true);
}
// update live preview area in here...
// don't update the live preview if the widget is not visible.
if (m_d->uiWdgPaintOpPresetSettings.liveBrushPreviewView->isVisible()) {
m_d->uiWdgPaintOpPresetSettings.liveBrushPreviewView->setCurrentPreset(m_d->resourceProvider->currentPreset());
m_d->uiWdgPaintOpPresetSettings.liveBrushPreviewView->requestUpdateStroke();
}
}
diff --git a/libs/ui/widgets/kis_paintop_presets_popup.h b/libs/ui/widgets/kis_paintop_presets_popup.h
index 0222ae9337..8b094171a0 100644
--- a/libs/ui/widgets/kis_paintop_presets_popup.h
+++ b/libs/ui/widgets/kis_paintop_presets_popup.h
@@ -1,147 +1,147 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2008
* Copyright (C) 2010 Lukáš Tvrdý <lukast.dev@gmail.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 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_PRESETS_POPUP_H
#define KIS_PAINTOP_PRESETS_POPUP_H
#include <QWidget>
#include <QList>
#include <KoID.h>
#include <kis_types.h>
#include <brushengine/kis_paintop_factory.h>
#include "../kis_paint_ops_model.h"
#include <kis_action.h>
#include <widgets/kis_paintop_presets_save.h>
#include "widgets/kis_paintop_presets_popup.h"
#include "kis_favorite_resource_manager.h"
class QString;
class KisCanvasResourceProvider;
class KoResource;
/**
* Popup widget for presets with built-in functionality
* for adding and removing presets.
*/
class KisPaintOpPresetsPopup : public QWidget
{
Q_OBJECT
public:
KisPaintOpPresetsPopup(KisCanvasResourceProvider * resourceProvider,
KisFavoriteResourceManager* favoriteResourceManager,
KisPresetSaveWidget* savePresetWidget,
QWidget * parent = 0);
~KisPaintOpPresetsPopup() override;
void setPaintOpSettingsWidget(QWidget * widget);
///Image for preset preview
///@return image cut out from the scratchpad
QImage cutOutOverlay();
void setPaintOpList(const QList<KisPaintOpFactory*>& list);
void setCurrentPaintOpId(const QString & paintOpId);
/// returns the internal ID for the paint op (brush engine)
QString currentPaintOpId();
///fill the cutoutOverlay rect with the content of an image, used to get the image back when selecting a preset
///@param image image that will be used, should be image of an existing preset resource
void setPresetImage(const QImage& image);
void resizeEvent(QResizeEvent* ) override;
bool detached() const;
void updateViewSettings();
void currentPresetChanged(KisPaintOpPresetSP preset);
KisPresetSaveWidget * saveDialog;
// toggle the state when we are creating a brush from scratch
void setCreatingBrushFromScratch(bool enable);
protected:
void contextMenuEvent(QContextMenuEvent *) override;
void hideEvent(QHideEvent *) override;
void showEvent(QShowEvent *) override;
public Q_SLOTS:
void switchDetached(bool show = true);
- void resourceSelected(KoResource* resource);
+ void resourceSelected(KoResourceSP resource);
void updateThemedIcons();
void slotUpdatePresetSettings();
void slotUpdateLodAvailability();
void slotRenameBrushActivated();
void slotRenameBrushDeactivated();
void slotSaveRenameCurrentBrush();
void slotCreateNewBrushPresetEngine();
Q_SIGNALS:
void savePresetClicked();
void saveBrushPreset();
void defaultPresetClicked();
void paintopActivated(const QString& presetName);
- void signalResourceSelected(KoResource* resource);
+ void signalResourceSelected(KoResourceSP resource);
void reloadPresetClicked();
void dirtyPresetToggled(bool value);
void eraserBrushSizeToggled(bool value);
void eraserBrushOpacityToggled(bool value);
void brushEditorShown();
void createPresetFromScratch(const QString& paintOpName);
private Q_SLOTS:
void slotSwitchScratchpad(bool visible);
void slotResourceChanged(int key, const QVariant &value);
void slotLodAvailabilityChanged(bool value);
void slotLodThresholdChanged(qreal value);
void slotSwitchShowEditor(bool visible);
void slotUpdatePaintOpFilter();
void slotSwitchShowPresets(bool visible);
void slotSaveBrushPreset();
void slotSaveNewBrushPreset();
/// we do not delete brushe presets, but blacklist them so they disappear from the interface
void slotBlackListCurrentPreset();
private:
struct Private;
Private * const m_d;
QString current_paintOpId;
QList<KisPaintOpInfo> sortedBrushEnginesList;
QMenu * newPresetBrushEnginesMenu;
QList<KisAction*> newBrushEngineOptions;
void toggleBrushRenameUIActive(bool isRenaming);
};
#endif
diff --git a/libs/ui/widgets/kis_paintop_presets_save.cpp b/libs/ui/widgets/kis_paintop_presets_save.cpp
index 05d90ea2ec..907d00a3e9 100644
--- a/libs/ui/widgets/kis_paintop_presets_save.cpp
+++ b/libs/ui/widgets/kis_paintop_presets_save.cpp
@@ -1,271 +1,222 @@
/* This file is part of the KDE project
* Copyright (C) 2017 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 "widgets/kis_paintop_presets_save.h"
#include <QDebug>
#include <QDate>
#include <QTime>
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <KoFileDialog.h>
#include "KisImportExportManager.h"
#include "QDesktopServices"
#include "KisResourceServerProvider.h"
#include <kis_paintop_preset_icon_library.h>
KisPresetSaveWidget::KisPresetSaveWidget(QWidget * parent)
: KisPaintOpPresetSaveDialog(parent)
{
// this is setting the area we will "capture" for saving the brush preset. It can potentially be a different
// area that the entire scratchpad
brushPresetThumbnailWidget->setCutoutOverlayRect(QRect(0, 0, brushPresetThumbnailWidget->height(), brushPresetThumbnailWidget->width()));
// we will default to reusing the previous preset thumbnail
// have that checked by default, hide the other elements, and load the last preset image
connect(clearBrushPresetThumbnailButton, SIGNAL(clicked(bool)), brushPresetThumbnailWidget, SLOT(fillDefault()));
connect(loadImageIntoThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadImageFromFile()));
connect(loadScratchPadThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadScratchpadThumbnail()));
connect(loadExistingThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadExistingThumbnail()));
connect(loadIconLibraryThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadImageFromLibrary()));
connect(savePresetButton, SIGNAL(clicked(bool)), this, SLOT(savePreset()));
connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(close()));
}
KisPresetSaveWidget::~KisPresetSaveWidget()
{
}
void KisPresetSaveWidget::scratchPadSetup(KisCanvasResourceProvider* resourceProvider)
{
m_resourceProvider = resourceProvider;
brushPresetThumbnailWidget->setupScratchPad(m_resourceProvider, Qt::white);
}
void KisPresetSaveWidget::showDialog()
{
setModal(true);
// set the name of the current brush preset area.
KisPaintOpPresetSP preset = m_resourceProvider->currentPreset();
// UI will look a bit different if we are saving a new brush
if (m_useNewBrushDialog) {
setWindowTitle(i18n("Save New Brush Preset"));
newBrushNameTexField->setVisible(true);
clearBrushPresetThumbnailButton->setVisible(true);
loadImageIntoThumbnailButton->setVisible(true);
currentBrushNameLabel->setVisible(false);
if (preset) {
newBrushNameTexField->setText(preset->name().append(" ").append(i18n("Copy")));
}
} else {
setWindowTitle(i18n("Save Brush Preset"));
if (preset) {
currentBrushNameLabel->setText(preset->name());
}
newBrushNameTexField->setVisible(false);
currentBrushNameLabel->setVisible(true);
}
brushPresetThumbnailWidget->paintPresetImage();
show();
}
void KisPresetSaveWidget::loadImageFromFile()
{
// create a dialog to retrieve an image file.
KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument");
dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import));
dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
QString filename = dialog.filename(); // the filename() returns the entire path & file name, not just the file name
if (filename != "") { // empty if "cancel" is pressed
// take that file and load it into the thumbnail are
const QImage imageToLoad(filename);
brushPresetThumbnailWidget->fillTransparent(); // clear the background in case our new image has transparency
brushPresetThumbnailWidget->paintCustomImage(imageToLoad);
}
}
void KisPresetSaveWidget::loadScratchpadThumbnail()
{
brushPresetThumbnailWidget->paintCustomImage(scratchPadThumbnailArea);
}
void KisPresetSaveWidget::loadExistingThumbnail()
{
brushPresetThumbnailWidget->paintPresetImage();
}
void KisPresetSaveWidget::loadImageFromLibrary()
{
//add dialog code here.
QDialog *dlg = new QDialog(this);
dlg->setWindowTitle(i18n("Preset Icon Library"));
QVBoxLayout *layout = new QVBoxLayout();
dlg->setLayout(layout);
KisPaintopPresetIconLibrary *libWidget = new KisPaintopPresetIconLibrary(dlg);
layout->addWidget(libWidget);
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, dlg);
connect(buttons, SIGNAL(accepted()), dlg, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), dlg, SLOT(reject()));
layout->addWidget(buttons);
//if dialog accepted, get image.
if (dlg->exec()==QDialog::Accepted) {
QImage presetImage = libWidget->getImage();
brushPresetThumbnailWidget->paintCustomImage(presetImage);
}
}
void KisPresetSaveWidget::setFavoriteResourceManager(KisFavoriteResourceManager * favManager)
{
m_favoriteResourceManager = favManager;
}
void KisPresetSaveWidget::savePreset()
{
KisPaintOpPresetSP curPreset = m_resourceProvider->currentPreset();
- if (!curPreset)
+ if (!curPreset) {
return;
+ }
- m_favoriteResourceManager->setBlockUpdates(true);
-
- KisPaintOpPresetSP oldPreset = curPreset->clone(); // tags are not cloned with this
- oldPreset->load();
KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
QString saveLocation = rServer->saveLocation();
// if we are saving a new brush, use what we type in for the input
- QString presetName = m_useNewBrushDialog ? newBrushNameTexField->text() : curPreset->name();
- QString currentPresetFileName = saveLocation + presetName.replace(" ", "_") + curPreset->defaultFileExtension();
- bool isSavingOverExistingPreset = rServer->resourceByName(presetName);
-
- // make a back up of the existing preset if we are saving over it
- if (isSavingOverExistingPreset) {
- QString currentDate = QDate::currentDate().toString(Qt::ISODate);
- QString currentTime = QTime::currentTime().toString(Qt::ISODate).remove(QChar(':'));
- QString presetFilename = saveLocation + presetName.replace(" ", "_") + "_backup_" + currentDate + "-" + currentTime + oldPreset->defaultFileExtension();
- oldPreset->setFilename(presetFilename);
- oldPreset->setName(presetName);
- oldPreset->setDirty(false);
- oldPreset->setValid(true);
-
- // add backup resource to the blacklist
- rServer->addResource(oldPreset);
- rServer->removeResourceAndBlacklist(oldPreset.data());
-
-
- QStringList tags;
- tags = rServer->assignedTagsList(curPreset.data());
- Q_FOREACH (const QString & tag, tags) {
- rServer->addTag(oldPreset.data(), tag);
- }
- }
-
+ QString presetFileName = m_useNewBrushDialog ? newBrushNameTexField->text() : curPreset->name();
+ // We don't want dots or spaces in the filenames
+ presetFileName = presetFileName.replace(' ', '_').replace('.', '_');
if (m_useNewBrushDialog) {
- KisPaintOpPresetSP newPreset = curPreset->clone();
- newPreset->setFilename(currentPresetFileName);
- newPreset->setName(presetName);
+ KisPaintOpPresetSP newPreset = curPreset->clone().dynamicCast<KisPaintOpPreset>();
+ newPreset->setFilename(presetFileName);
+ newPreset->setName(m_useNewBrushDialog ? newBrushNameTexField->text() : curPreset->name());
newPreset->setImage(brushPresetThumbnailWidget->cutoutOverlay());
newPreset->setDirty(false);
newPreset->setValid(true);
-
- // keep tags if we are saving over existing preset
- if (isSavingOverExistingPreset) {
- QStringList tags;
- tags = rServer->assignedTagsList(curPreset.data());
- Q_FOREACH (const QString & tag, tags) {
- rServer->addTag(newPreset.data(), tag);
- }
- }
-
rServer->addResource(newPreset);
// trying to get brush preset to load after it is created
- emit resourceSelected(newPreset.data());
+ emit resourceSelected(newPreset);
}
else { // saving a preset that is replacing an existing one
-
- if (curPreset->filename().contains(saveLocation) == false || curPreset->filename().contains(presetName) == false) {
- rServer->removeResourceAndBlacklist(curPreset.data());
- curPreset->setFilename(currentPresetFileName);
- curPreset->setName(presetName);
- }
-
- if (!rServer->resourceByFilename(curPreset->filename())){
- //this is necessary so that we can get the preset afterwards.
- rServer->addResource(curPreset, false, false);
- rServer->removeFromBlacklist(curPreset.data());
- }
+ curPreset->setName(m_useNewBrushDialog ? newBrushNameTexField->text() : curPreset->name());
if (curPreset->image().isNull()) {
curPreset->setImage(brushPresetThumbnailWidget->cutoutOverlay());
}
- // we should not load() the brush right after saving because it will reset all our saved
- // eraser size and opacity values
- curPreset->save();
+ rServer->updateResource(curPreset);
}
- // HACK ALERT! the server does not notify the observers
- // automatically, so we need to call theupdate manually!
- rServer->tagCategoryMembersChanged();
+// // HACK ALERT! the server does not notify the observers
+// // automatically, so we need to call theupdate manually!
+// rServer->tagCategoryMembersChanged();
- m_favoriteResourceManager->setBlockUpdates(false);
+ m_favoriteResourceManager->updateFavoritePresets();
close(); // we are done... so close the save brush dialog
}
void KisPresetSaveWidget::saveScratchPadThumbnailArea(QImage image)
{
scratchPadThumbnailArea = image;
}
void KisPresetSaveWidget::useNewBrushDialog(bool show)
{
m_useNewBrushDialog = show;
}
#include "moc_kis_paintop_presets_save.cpp"
diff --git a/libs/ui/widgets/kis_paintop_presets_save.h b/libs/ui/widgets/kis_paintop_presets_save.h
index 3185a8b478..3362087e6f 100644
--- a/libs/ui/widgets/kis_paintop_presets_save.h
+++ b/libs/ui/widgets/kis_paintop_presets_save.h
@@ -1,81 +1,79 @@
/* This file is part of the KDE project
* Copyright (C) 2017 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.
*/
#ifndef KIS_PAINTOP_PRESETS_SAVE_H
#define KIS_PAINTOP_PRESETS_SAVE_H
#include <QWidget>
#include <QDialog>
#include "ui_wdgsavebrushpreset.h"
#include "kis_canvas_resource_provider.h"
#include "kis_favorite_resource_manager.h"
class KisPaintOpPresetSaveDialog : public QDialog , public Ui::WdgSaveBrushPreset
{
Q_OBJECT
-
-
public:
KisPaintOpPresetSaveDialog(QWidget* parent) : QDialog(parent) {
setupUi(this);
}
};
class KisPresetSaveWidget : public KisPaintOpPresetSaveDialog
{
Q_OBJECT
public:
KisPresetSaveWidget(QWidget* parent);
virtual ~KisPresetSaveWidget();
void showDialog();
/// determines if we should show the save as dialog (true) or save in the background (false)
void useNewBrushDialog(bool show);
void scratchPadSetup(KisCanvasResourceProvider* resourceProvider);
void saveScratchPadThumbnailArea(const QImage image);
KisCanvasResourceProvider* m_resourceProvider;
void setFavoriteResourceManager(KisFavoriteResourceManager * favManager);
Q_SIGNALS:
- void resourceSelected(KoResource* resource);
+ void resourceSelected(KoResourceSP resource);
public Q_SLOTS:
void loadImageFromFile();
void savePreset();
void loadScratchpadThumbnail();
void loadExistingThumbnail();
void loadImageFromLibrary();
private:
bool m_useNewBrushDialog;
KisFavoriteResourceManager * m_favoriteResourceManager;
QImage scratchPadThumbnailArea;
};
#endif
diff --git a/libs/ui/widgets/kis_pattern_chooser.cc b/libs/ui/widgets/kis_pattern_chooser.cc
index 0131a3ed1c..6b3ae4189f 100644
--- a/libs/ui/widgets/kis_pattern_chooser.cc
+++ b/libs/ui/widgets/kis_pattern_chooser.cc
@@ -1,117 +1,114 @@
/*
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
* 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 "widgets/kis_pattern_chooser.h"
#include <math.h>
#include <QLabel>
#include <QLayout>
#include <QVBoxLayout>
#include <QResizeEvent>
#include <QShowEvent>
#include <klocalizedstring.h>
-#include <KoResourceItemChooser.h>
-#include <KoResourceServerAdapter.h>
+#include <KisResourceItemChooser.h>
#include <KoResourceServerProvider.h>
#include "kis_signals_blocker.h"
#include "kis_global.h"
#include <kis_config.h>
#include <resources/KoPattern.h>
#include <ksqueezedtextlabel.h>
KisPatternChooser::KisPatternChooser(QWidget *parent)
: QFrame(parent)
{
m_lblName = new KSqueezedTextLabel(this);
m_lblName->setTextElideMode(Qt::ElideLeft);
- KoResourceServer<KoPattern> * rserver = KoResourceServerProvider::instance()->patternServer();
- QSharedPointer<KoAbstractResourceServerAdapter> adapter (new KoResourceServerAdapter<KoPattern>(rserver));
- m_itemChooser = new KoResourceItemChooser(adapter, this, true);
+ m_itemChooser = new KisResourceItemChooser(ResourceType::Patterns, true, this);
m_itemChooser->setPreviewTiled(true);
m_itemChooser->setPreviewOrientation(Qt::Horizontal);
m_itemChooser->showTaggingBar(true);
m_itemChooser->setSynced(true);
- connect(m_itemChooser, SIGNAL(resourceSelected(KoResource*)),
- this, SLOT(update(KoResource*)));
+ connect(m_itemChooser, SIGNAL(resourceSelected(KoResourceSP )),
+ this, SLOT(update(KoResourceSP )));
- connect(m_itemChooser, SIGNAL(resourceSelected(KoResource*)),
- this, SIGNAL(resourceSelected(KoResource*)));
+ connect(m_itemChooser, SIGNAL(resourceSelected(KoResourceSP )),
+ this, SIGNAL(resourceSelected(KoResourceSP )));
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
mainLayout->setMargin(0);
mainLayout->addWidget(m_lblName);
mainLayout->addWidget(m_itemChooser, 10);
setLayout(mainLayout);
}
KisPatternChooser::~KisPatternChooser()
{
}
-KoResource * KisPatternChooser::currentResource()
+KoResourceSP KisPatternChooser::currentResource()
{
if (!m_itemChooser->currentResource()) {
KoResourceServer<KoPattern> * rserver = KoResourceServerProvider::instance()->patternServer();
- if (rserver->resources().size() > 0) {
+ if (rserver->resourceCount() > 0) {
KisSignalsBlocker blocker(m_itemChooser);
- m_itemChooser->setCurrentResource(rserver->resources().first());
+ m_itemChooser->setCurrentResource(rserver->firstResource());
}
}
return m_itemChooser->currentResource();
}
-void KisPatternChooser::setCurrentPattern(KoResource *resource)
+void KisPatternChooser::setCurrentPattern(KoResourceSP resource)
{
m_itemChooser->setCurrentResource(resource);
}
-void KisPatternChooser::setCurrentItem(int row, int column)
+void KisPatternChooser::setCurrentItem(int row)
{
- m_itemChooser->setCurrentItem(row, column);
+ m_itemChooser->setCurrentItem(row);
if (currentResource()) {
update(currentResource());
}
}
void KisPatternChooser::setPreviewOrientation(Qt::Orientation orientation)
{
m_itemChooser->setPreviewOrientation(orientation);
}
-void KisPatternChooser::update(KoResource * resource)
+void KisPatternChooser::update(KoResourceSP resource)
{
m_lblName->setFixedWidth(m_itemChooser->width());
- KoPattern *pattern = static_cast<KoPattern *>(resource);
+ KoPatternSP pattern = resource.staticCast<KoPattern>();
m_lblName->setText(QString("%1 (%2 x %3)").arg(i18n(pattern->name().toUtf8().data())).arg(pattern->width()).arg(pattern->height()));
}
void KisPatternChooser::setGrayscalePreview(bool grayscale)
{
m_itemChooser->setGrayscalePreview(grayscale);
}
diff --git a/libs/ui/widgets/kis_pattern_chooser.h b/libs/ui/widgets/kis_pattern_chooser.h
index 462df2a9f8..9ffc7e5f2f 100644
--- a/libs/ui/widgets/kis_pattern_chooser.h
+++ b/libs/ui/widgets/kis_pattern_chooser.h
@@ -1,63 +1,66 @@
/*
* Copyright (c) 2004 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_PATTERN_CHOOSER_H_
#define KIS_PATTERN_CHOOSER_H_
#include <QFrame>
+
+#include <KoResource.h>
+
#include <kritaui_export.h>
class KSqueezedTextLabel;
-class KoResourceItemChooser;
-class KoResource;
+class KisResourceItemChooser;
+
class KRITAUI_EXPORT KisPatternChooser : public QFrame
{
Q_OBJECT
public:
KisPatternChooser(QWidget *parent = 0);
~KisPatternChooser() override;
/// Gets the currently selected resource
/// @returns the selected resource, 0 is no resource is selected
- KoResource *currentResource();
- void setCurrentPattern(KoResource *resource);
- void setCurrentItem(int row, int column);
+ KoResourceSP currentResource();
+ void setCurrentPattern(KoResourceSP resource);
+ void setCurrentItem(int row);
void setGrayscalePreview(bool grayscale);
/// determines whether the preview right or below the splitter
void setPreviewOrientation(Qt::Orientation orientation);
Q_SIGNALS:
/// Emitted when a resource was selected
- void resourceSelected(KoResource *resource);
+ void resourceSelected(KoResourceSP resource);
void updateItemSize();
private Q_SLOTS:
- void update(KoResource *resource);
+ void update(KoResourceSP resource);
private:
KSqueezedTextLabel *m_lblName;
- KoResourceItemChooser *m_itemChooser;
+ KisResourceItemChooser *m_itemChooser;
};
#endif // KIS_PATTERN_CHOOSER_H_
diff --git a/libs/ui/widgets/kis_preset_chooser.cpp b/libs/ui/widgets/kis_preset_chooser.cpp
index 63d52ee9f0..5affde2aa3 100644
--- a/libs/ui/widgets/kis_preset_chooser.cpp
+++ b/libs/ui/widgets/kis_preset_chooser.cpp
@@ -1,368 +1,459 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2009 Sven Langkamp <sven.langkamp@gmail.com>
* Copyright (C) 2011 Silvio Heinrich <plassy@web.de>
* Copyright (C) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
* 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_preset_chooser.h"
#include <QVBoxLayout>
#include <QPainter>
#include <QAbstractItemDelegate>
#include <QStyleOptionViewItem>
#include <QSortFilterProxyModel>
+#include <KisResourceModel.h>
#include <QApplication>
#include <kis_config.h>
#include <klocalizedstring.h>
#include <KisKineticScroller.h>
#include <KoIcon.h>
-#include <KoResourceItemChooser.h>
-#include <KoResourceModel.h>
-#include <KoResourceServerAdapter.h>
-#include <KoResourceItemChooserSync.h>
-#include "KoResourceItemView.h"
+#include <KisResourceItemChooser.h>
+#include <KisResourceItemChooserSync.h>
+#include <KisResourceItemListView.h>
+#include <KisResourceModel.h>
+#include <KisResourceLocator.h>
#include <brushengine/kis_paintop_settings.h>
#include <brushengine/kis_paintop_preset.h>
#include "KisResourceServerProvider.h"
#include "kis_global.h"
#include "kis_slider_spin_box.h"
#include "kis_config_notifier.h"
#include <kis_icon.h>
/// The resource item delegate for rendering the resource preview
class KisPresetDelegate : public QAbstractItemDelegate
{
public:
KisPresetDelegate(QObject * parent = 0) : QAbstractItemDelegate(parent), m_showText(false), m_useDirtyPresets(false) {}
~KisPresetDelegate() 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 setShowText(bool showText) {
m_showText = showText;
}
void setUseDirtyPresets(bool value) {
m_useDirtyPresets = value;
}
private:
bool m_showText;
bool m_useDirtyPresets;
};
void KisPresetDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
painter->save();
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
- if (! index.isValid())
+ if (!index.isValid()) {
+ qDebug() << "KisPresetDelegate::paint: index is invalid";
+ painter->restore();
return;
+ }
- KisPaintOpPreset* preset = static_cast<KisPaintOpPreset*>(index.internalPointer());
+ bool dirty = index.data(Qt::UserRole + KisResourceModel::Dirty).toBool();
- QImage preview = preset->image();
+ QImage preview = index.data(Qt::UserRole + KisResourceModel::Thumbnail).value<QImage>();
- if(preview.isNull()) {
+ if (preview.isNull()) {
+ qDebug() << "KisPresetDelegate::paint: Preview is null";
+ painter->restore();
return;
}
+ QMap<QString, QVariant> metaData = index.data(Qt::UserRole + KisResourceModel::MetaData).value<QMap<QString, QVariant>>();
+
QRect paintRect = option.rect.adjusted(1, 1, -1, -1);
if (!m_showText) {
painter->drawImage(paintRect.x(), paintRect.y(),
preview.scaled(paintRect.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
- } else {
+ }
+ else {
QSize pixSize(paintRect.height(), paintRect.height());
painter->drawImage(paintRect.x(), paintRect.y(),
preview.scaled(pixSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
// Put an asterisk after the preset if it is dirty. This will help in case the pixmap icon is too small
- QString dirtyPresetIndicator = QString("");
- if (m_useDirtyPresets && preset->isDirty()) {
+
+ QString dirtyPresetIndicator = QString("");
+ if (m_useDirtyPresets && dirty) {
dirtyPresetIndicator = QString("*");
}
- qreal brushSize = preset->settings()->paintOpSize();
- QString brushSizeText;
+// qreal brushSize = metaData["paintopSize"].toReal();
+// QString brushSizeText;
- // Disable displayed decimal precision beyond a certain brush size
- if (brushSize < 100) {
- brushSizeText = QString::number(brushSize, 'g', 3);
- } else {
- brushSizeText = QString::number(brushSize, 'f', 0);
- }
+// // Disable displayed decimal precision beyond a certain brush size
+// if (brushSize < 100) {
+// brushSizeText = QString::number(brushSize, 'g', 3);
+// } else {
+// brushSizeText = QString::number(brushSize, 'f', 0);
+// }
- painter->drawText(pixSize.width() + 10, option.rect.y() + option.rect.height() - 10, brushSizeText); // brush size
+// painter->drawText(pixSize.width() + 10, option.rect.y() + option.rect.height() - 10, brushSizeText); // brush size
- QString presetDisplayName = preset->name().replace("_", " "); // don't need underscores that might be part of the file name
+ QString presetDisplayName = index.data(Qt::UserRole + KisResourceModel::Name).toString().replace("_", " "); // don't need underscores that might be part of the file name
painter->drawText(pixSize.width() + 40, option.rect.y() + option.rect.height() - 10, presetDisplayName.append(dirtyPresetIndicator));
}
- if (m_useDirtyPresets && preset->isDirty()) {
+
+ if (m_useDirtyPresets && dirty) {
const QIcon icon = KisIconUtils::loadIcon(koIconName("dirty-preset"));
QPixmap pixmap = icon.pixmap(QSize(15,15));
painter->drawPixmap(paintRect.x() + 3, paintRect.y() + 3, pixmap);
}
- if (!preset->settings() || !preset->settings()->isValid()) {
- const QIcon icon = KisIconUtils::loadIcon("broken-preset");
- icon.paint(painter, QRect(paintRect.x() + paintRect.height() - 25, paintRect.y() + paintRect.height() - 25, 25, 25));
- }
+// if (!preset->settings() || !preset->settings()->isValid()) {
+// const QIcon icon = KisIconUtils::loadIcon("broken-preset");
+// icon.paint(painter, QRect(paintRect.x() + paintRect.height() - 25, paintRect.y() + paintRect.height() - 25, 25, 25));
+// }
+
if (option.state & QStyle::State_Selected) {
painter->setCompositionMode(QPainter::CompositionMode_HardLight);
painter->setOpacity(1.0);
painter->fillRect(option.rect, option.palette.highlight());
// highlight is not strong enough to pick out preset. draw border around it.
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
painter->setPen(QPen(option.palette.highlight(), 4, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
QRect selectedBorder = option.rect.adjusted(2 , 2, -2, -2); // constrict the rectangle so it doesn't bleed into other presets
painter->drawRect(selectedBorder);
- }
+ }
+
painter->restore();
+
}
-class KisPresetProxyAdapter : public KisPaintOpPresetResourceServerAdapter
+class KisPresetChooser::PaintOpFilterModel : public QSortFilterProxyModel, public KisAbstractResourceModel
{
+ Q_OBJECT
+public:
+
+ PaintOpFilterModel(QObject *parent = 0)
+ : QSortFilterProxyModel(parent)
+ {
+ }
+
+ ~PaintOpFilterModel() override
+ {
+ }
+ void setPaintOpId(const QString &id)
+ {
+ m_id = id;
+ }
+
+ QString currentPaintOpId() const
+ {
+ return m_id;
+ }
+ // KisAbstractResourceModel interface
public:
- KisPresetProxyAdapter(KisPaintOpPresetResourceServer* resourceServer)
- : KisPaintOpPresetResourceServerAdapter(resourceServer)
+ KoResourceSP resourceForIndex(QModelIndex index) const override
{
- setSortingEnabled(true);
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->resourceForIndex(mapToSource(index));
+ }
+ return 0;
}
- ~KisPresetProxyAdapter() override {}
- QList< KoResource* > resources() override {
+ QModelIndex indexFromResource(KoResourceSP resource) const override
+ {
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return mapFromSource(source->indexFromResource(resource));
+ }
+ return QModelIndex();
+ }
- QList<KoResource*> serverResources =
- KisPaintOpPresetResourceServerAdapter::resources();
+ bool removeResource(const QModelIndex &index) override
+ {
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->removeResource(mapToSource(index));
+ }
+ return false;
+ }
- if (m_paintopID.isEmpty()) {
- return serverResources;
+ bool importResourceFile(const QString &filename) override
+ {
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->importResourceFile(filename);
}
+ return false;
+ }
- QList<KoResource*> resources;
- Q_FOREACH (KoResource *resource, serverResources) {
- KisPaintOpPreset *preset = dynamic_cast<KisPaintOpPreset*>(resource);
+ bool addResource(KoResourceSP resource, const QString &storageId = QString()) override
+ {
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->addResource(resource, storageId);
+ }
+ return false;
+ }
- if (preset && preset->paintOp().id() == m_paintopID) {
- resources.append(preset);
- }
+ bool updateResource(KoResourceSP resource) override
+ {
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->updateResource(resource);
}
- return resources;
+ return false;
}
- ///Set id for paintop to be accept by the proxy model, if not filter is set all
- ///presets will be shown.
- void setPresetFilter(const QString& paintOpId)
+ bool renameResource(KoResourceSP resource, const QString &name) override
{
- m_paintopID = paintOpId;
- invalidate();
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->renameResource(resource, name);
+ }
+ return false;
}
- ///Resets the model connected to the adapter
- void invalidate() {
- emitRemovingResource(0);
+ bool removeResource(KoResourceSP resource) override
+ {
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->removeResource(resource);
+ }
+ return false;
}
- QString currentPaintOpId() const {
- return m_paintopID;
+ bool setResourceMetaData(KoResourceSP resource, QMap<QString, QVariant> metadata) override
+ {
+ KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel());
+ if (source) {
+ return source->setResourceMetaData(resource, metadata);
+ }
+ return false;
+ }
+
+
+ // QSortFilterProxyModel interface
+protected:
+
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ return sourceModel()->data(mapToSource(index), role);
+ }
+
+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
+ {
+ if (m_id.isEmpty()) return true;
+
+ QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
+ QMap<QString, QVariant> metadata = sourceModel()->data(idx, Qt::UserRole + KisResourceModel::MetaData).toMap();
+ if (metadata.contains("paintopid")) {
+ return (metadata["paintopid"].toString() == m_id);
+ }
+
+ return false;
+ }
+
+ bool filterAcceptsColumn(int /*source_column*/, const QModelIndex &/*source_parent*/) const override
+ {
+ return true;
+ }
+
+ bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override
+ {
+ QString nameLeft = sourceModel()->data(source_left, Qt::UserRole + KisResourceModel::Name).toString();
+ QString nameRight = sourceModel()->data(source_right, Qt::UserRole + KisResourceModel::Name).toString();
+ return nameLeft < nameRight;
}
private:
- QString m_paintopID;
+
+ QString m_id;
};
KisPresetChooser::KisPresetChooser(QWidget *parent, const char *name)
: QWidget(parent)
{
setObjectName(name);
QVBoxLayout * layout = new QVBoxLayout(this);
layout->setMargin(0);
- KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
- m_adapter = QSharedPointer<KoAbstractResourceServerAdapter>(new KisPresetProxyAdapter(rserver));
+ m_paintOpFilterModel = new PaintOpFilterModel();
- m_chooser = new KoResourceItemChooser(m_adapter, this);
+ m_chooser = new KisResourceItemChooser(ResourceType::PaintOpPresets, false, this, m_paintOpFilterModel);
m_chooser->setObjectName("ResourceChooser");
- m_chooser->setColumnCount(10);
m_chooser->setRowHeight(50);
m_delegate = new KisPresetDelegate(this);
m_chooser->setItemDelegate(m_delegate);
m_chooser->setSynced(true);
layout->addWidget(m_chooser);
{
QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(this->itemChooser()->itemView());
if (scroller) {
connect(scroller, SIGNAL(stateChanged(QScroller::State)),
this, SLOT(slotScrollerStateChanged(QScroller::State)));
}
}
- connect(m_chooser, SIGNAL(resourceSelected(KoResource*)),
- this, SIGNAL(resourceSelected(KoResource*)));
- connect(m_chooser, SIGNAL(resourceClicked(KoResource*)),
- this, SIGNAL(resourceClicked(KoResource*)));
+ connect(m_chooser, SIGNAL(resourceSelected(KoResourceSP )),
+ this, SIGNAL(resourceSelected(KoResourceSP )));
+ connect(m_chooser, SIGNAL(resourceClicked(KoResourceSP )),
+ this, SIGNAL(resourceClicked(KoResourceSP )));
m_mode = THUMBNAIL;
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()),
SLOT(notifyConfigChanged()));
notifyConfigChanged();
}
KisPresetChooser::~KisPresetChooser()
{
}
void KisPresetChooser::showButtons(bool show)
{
m_chooser->showButtons(show);
}
void KisPresetChooser::setViewMode(KisPresetChooser::ViewMode mode)
{
m_mode = mode;
updateViewSettings();
}
void KisPresetChooser::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
updateViewSettings();
}
void KisPresetChooser::notifyConfigChanged()
{
KisConfig cfg(true);
m_delegate->setUseDirtyPresets(cfg.useDirtyPresets());
setIconSize(cfg.presetIconSize());
updateViewSettings();
}
void KisPresetChooser::updateViewSettings()
{
if (m_mode == THUMBNAIL) {
m_chooser->setSynced(true);
m_delegate->setShowText(false);
- } else if (m_mode == DETAIL) {
+ m_chooser->itemView()->setViewMode(QListView::IconMode);
+ m_chooser->itemView()->setFlow(QListView::LeftToRight);
+ }
+ else if (m_mode == DETAIL) {
m_chooser->setSynced(false);
- m_chooser->setColumnCount(1);
+ m_chooser->itemView()->setViewMode(QListView::ListMode);
+ m_chooser->itemView()->setFlow(QListView::TopToBottom);
m_chooser->setColumnWidth(m_chooser->width());
- KoResourceItemChooserSync* chooserSync = KoResourceItemChooserSync::instance();
+ KisResourceItemChooserSync* chooserSync = KisResourceItemChooserSync::instance();
m_chooser->setRowHeight(chooserSync->baseLength());
m_delegate->setShowText(true);
- } else if (m_mode == STRIP) {
+ }
+ else if (m_mode == STRIP) {
m_chooser->setSynced(false);
- m_chooser->setRowCount(1);
- m_chooser->itemView()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- m_chooser->itemView()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_chooser->itemView()->setViewMode(QListView::ListMode);
+ m_chooser->itemView()->setFlow(QListView::LeftToRight);
// An offset of 7 keeps the cell exactly square, TODO: use constants, not hardcoded numbers
m_chooser->setColumnWidth(m_chooser->viewSize().height() - 7);
m_delegate->setShowText(false);
}
}
-void KisPresetChooser::setCurrentResource(KoResource *resource)
+void KisPresetChooser::setCurrentResource(KoResourceSP resource)
{
- /**
- * HACK ALERT: here we use a direct call to an adapter to notify the view
- * that the preset might have changed its dirty state. This state
- * doesn't affect the filtering so the server's cache must not be
- * invalidated!
- *
- * Ideally, we should call some method of KoResourceServer instead,
- * but it seems like a bit too much effort for such a small fix.
- */
- if (resource == currentResource()) {
- KisPresetProxyAdapter *adapter = static_cast<KisPresetProxyAdapter*>(m_adapter.data());
- KisPaintOpPreset *preset = dynamic_cast<KisPaintOpPreset*>(resource);
- if (preset) {
- adapter->resourceChangedNoCacheInvalidation(preset);
- }
- }
-
m_chooser->setCurrentResource(resource);
}
-KoResource* KisPresetChooser::currentResource() const
+KoResourceSP KisPresetChooser::currentResource() const
{
return m_chooser->currentResource();
}
void KisPresetChooser::showTaggingBar(bool show)
{
m_chooser->showTaggingBar(show);
}
-KoResourceItemChooser *KisPresetChooser::itemChooser()
+KisResourceItemChooser *KisPresetChooser::itemChooser()
{
return m_chooser;
}
void KisPresetChooser::setPresetFilter(const QString& paintOpId)
{
- KisPresetProxyAdapter *adapter = static_cast<KisPresetProxyAdapter*>(m_adapter.data());
-
- if (adapter->currentPaintOpId() != paintOpId) {
- adapter->setPresetFilter(paintOpId);
+ if (m_paintOpFilterModel && m_paintOpFilterModel->currentPaintOpId() != paintOpId) {
+ m_paintOpFilterModel->setPaintOpId(paintOpId);
updateViewSettings();
}
}
void KisPresetChooser::setIconSize(int newSize)
{
- KoResourceItemChooserSync* chooserSync = KoResourceItemChooserSync::instance();
+ KisResourceItemChooserSync* chooserSync = KisResourceItemChooserSync::instance();
chooserSync->setBaseLength(newSize);
updateViewSettings();
}
int KisPresetChooser::iconSize()
{
- KoResourceItemChooserSync* chooserSync = KoResourceItemChooserSync::instance();
-
+ KisResourceItemChooserSync* chooserSync = KisResourceItemChooserSync::instance();
return chooserSync->baseLength();
}
void KisPresetChooser::saveIconSize()
{
// save icon size
KisConfig cfg(false);
cfg.setPresetIconSize(iconSize());
}
void KisPresetChooser::slotScrollerStateChanged(QScroller::State state)
{
KisKineticScroller::updateCursor(this, state);
}
+
+#include "kis_preset_chooser.moc"
diff --git a/libs/ui/widgets/kis_preset_chooser.h b/libs/ui/widgets/kis_preset_chooser.h
index 22fed7d71b..f28a784e92 100644
--- a/libs/ui/widgets/kis_preset_chooser.h
+++ b/libs/ui/widgets/kis_preset_chooser.h
@@ -1,96 +1,103 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (C) 2011 Silvio Heinrich <plassy@web.de>
* 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.
*/
#ifndef KIS_ITEM_CHOOSER_H_
#define KIS_ITEM_CHOOSER_H_
#include <QWidget>
#include <QScroller>
-#include <kritaui_export.h>
+#include <QPointer>
+
+#include <KoResource.h>
#include <KoID.h>
class KoAbstractResourceServerAdapter;
class KisPresetDelegate;
-class KoResourceItemChooser;
-class KoResource;
+class KisResourceItemChooser;
+
+
+#include <kritaui_export.h>
/**
* A special type of item chooser that can contain extra widgets that show
* more information about the currently selected item. Reimplement update()
* to extract that information and fill the appropriate widgets.
*/
class KRITAUI_EXPORT KisPresetChooser : public QWidget
{
Q_OBJECT
public:
KisPresetChooser(QWidget *parent = 0, const char *name = 0);
~KisPresetChooser() override;
enum ViewMode{
THUMBNAIL, /// Shows thumbnails
DETAIL, /// Shows thumbsnails with text next to it
STRIP /// Shows thumbnails arranged in a single row
};
/// Sets a list of resources in the paintop list, when ever user press enter in the linedit of paintop_presets_popup Class
void setViewMode(ViewMode mode);
void showButtons(bool show);
- void setCurrentResource(KoResource *resource);
- KoResource* currentResource() const;
+ void setCurrentResource(KoResourceSP resource);
+ KoResourceSP currentResource() const;
/// Sets the visibility of tagging klineEdits
void showTaggingBar(bool show);
- KoResourceItemChooser *itemChooser();
+ KisResourceItemChooser *itemChooser();
void setPresetFilter(const QString& paintOpId);
/// get the base size for the icons. Used by the slider in the view options
int iconSize();
Q_SIGNALS:
- void resourceSelected(KoResource *resource);
- void resourceClicked(KoResource *resource);
+ void resourceSelected(KoResourceSP resource);
+ void resourceClicked(KoResourceSP resource);
public Q_SLOTS:
void updateViewSettings();
/// sets the icon size. Used by slider in view menu
void setIconSize(int newSize);
/// saves the icon size for the presets. called by the horizontal slider release event.
void saveIconSize();
void slotScrollerStateChanged(QScroller::State state);
private Q_SLOTS:
void notifyConfigChanged();
protected:
void resizeEvent(QResizeEvent* event) override;
private:
- KoResourceItemChooser *m_chooser;
- KisPresetDelegate* m_delegate;
+ KisResourceItemChooser *m_chooser {0};
+ KisPresetDelegate* m_delegate {0};
ViewMode m_mode;
- QSharedPointer<KoAbstractResourceServerAdapter> m_adapter;
+
+ class PaintOpFilterModel;
+ QPointer<PaintOpFilterModel> m_paintOpFilterModel;
+
};
#endif // KIS_ITEM_CHOOSER_H_
diff --git a/libs/ui/widgets/kis_preset_live_preview_view.cpp b/libs/ui/widgets/kis_preset_live_preview_view.cpp
index b6af4ab74a..b7974e1291 100644
--- a/libs/ui/widgets/kis_preset_live_preview_view.cpp
+++ b/libs/ui/widgets/kis_preset_live_preview_view.cpp
@@ -1,383 +1,384 @@
/*
* 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_preset_live_preview_view.h>
#include <QDebug>
#include <QGraphicsPixmapItem>
#include "kis_paintop_settings.h"
#include <strokes/freehand_stroke.h>
#include <strokes/KisFreehandStrokeInfo.h>
#include "KisAsyncronousStrokeUpdateHelper.h"
#include <kis_brush.h>
+#include <KisGlobalResourcesInterface.h>
KisPresetLivePreviewView::KisPresetLivePreviewView(QWidget *parent)
: QGraphicsView(parent),
m_updateCompressor(100, KisSignalCompressor::FIRST_ACTIVE)
{
connect(&m_updateCompressor, SIGNAL(timeout()), SLOT(updateStroke()));
}
KisPresetLivePreviewView::~KisPresetLivePreviewView()
{
delete m_noPreviewText;
delete m_brushPreviewScene;
}
void KisPresetLivePreviewView::setup()
{
// initializing to 0 helps check later if they actually have something in them
m_noPreviewText = 0;
m_sceneImageItem = 0;
setHorizontalScrollBarPolicy ( Qt::ScrollBarAlwaysOff );
setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOff );
// layer image needs to be big enough to get an entire stroke of data
m_canvasSize.setWidth(this->width());
m_canvasSize.setHeight(this->height());
m_canvasCenterPoint.setX(m_canvasSize.width()*0.5);
m_canvasCenterPoint.setY(m_canvasSize.height()*0.5);
m_colorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_image = new KisImage(0, m_canvasSize.width(), m_canvasSize.height(), m_colorSpace, "stroke sample image");
m_layer = new KisPaintLayer(m_image, "livePreviewStrokeSample", OPACITY_OPAQUE_U8, m_colorSpace);
// set scene for the view
m_brushPreviewScene = new QGraphicsScene();
setScene(m_brushPreviewScene);
}
void KisPresetLivePreviewView::setCurrentPreset(KisPaintOpPresetSP preset)
{
m_currentPreset = preset;
}
void KisPresetLivePreviewView::requestUpdateStroke()
{
m_updateCompressor.start();
}
void KisPresetLivePreviewView::updateStroke()
{
// do not paint a stroke if we are any of these engines (they have some issue currently)
if (m_currentPreset->paintOp().id() == "roundmarker" ||
m_currentPreset->paintOp().id() == "experimentbrush" ||
m_currentPreset->paintOp().id() == "duplicate") {
paintBackground();
slotPreviewGenerationCompleted();
return;
}
if (!m_previewGenerationInProgress) {
paintBackground();
setupAndPaintStroke();
} else {
m_updateCompressor.start();
}
}
void KisPresetLivePreviewView::slotPreviewGenerationCompleted()
{
m_previewGenerationInProgress = false;
QImage m_temp_image;
m_temp_image = m_layer->paintDevice()->convertToQImage(0, m_image->bounds());
// only add the object once...then just update the pixmap so we can move the preview around
if (!m_sceneImageItem) {
m_sceneImageItem = m_brushPreviewScene->addPixmap(QPixmap::fromImage(m_temp_image));
} else {
m_sceneImageItem->setPixmap(QPixmap::fromImage(m_temp_image));
}
}
void KisPresetLivePreviewView::paintBackground()
{
// clean up "no preview" text object if it exists. we will add it later if we need it
if (m_noPreviewText) {
this->scene()->removeItem(m_noPreviewText);
m_noPreviewText = 0;
}
if (m_currentPreset->paintOp().id() == "colorsmudge" ||
m_currentPreset->paintOp().id() == "deformbrush" ||
m_currentPreset->paintOp().id() == "filter") {
// easier to see deformations and smudging with alternating stripes in the background
// paint the whole background with alternating stripes
// filter engine may or may not show things depending on the filter...but it is better than nothing
int grayStrips = 20;
for (int i=0; i < grayStrips; i++ ) {
float sectionPercent = 1.0 / (float)grayStrips;
bool isAlternating = i % 2;
KoColor fillColor(m_layer->paintDevice()->colorSpace());
if (isAlternating) {
fillColor.fromQColor(QColor(80,80,80));
} else {
fillColor.fromQColor(QColor(140,140,140));
}
const QRect fillRect(m_layer->image()->width()*sectionPercent*i,
0,
m_layer->image()->width()*(sectionPercent*i +sectionPercent),
m_layer->image()->height());
m_layer->paintDevice()->fill(fillRect, fillColor);
}
m_paintColor = KoColor(Qt::white, m_colorSpace);
}
else if (m_currentPreset->paintOp().id() == "roundmarker" ||
m_currentPreset->paintOp().id() == "experimentbrush" ||
m_currentPreset->paintOp().id() == "duplicate" ) {
// cases where we will not show a preview for now
// roundbrush (quick) -- this isn't showing anything, disable showing preview
// experimentbrush -- this creates artifacts that carry over to other previews and messes up their display
// duplicate (clone) brush doesn't have a preview as it doesn't show anything)
if(m_sceneImageItem) {
this->scene()->removeItem(m_sceneImageItem);
m_sceneImageItem = 0;
}
QFont font;
font.setPixelSize(14);
font.setBold(false);
m_noPreviewText = this->scene()->addText(i18n("No Preview for this engine"),font);
m_noPreviewText->setPos(50, this->height()/4);
return;
}
else {
// fill with gray first to clear out what existed from previous preview
m_layer->paintDevice()->fill(m_image->bounds(), KoColor(palette().color(QPalette::Background) , m_colorSpace));
m_paintColor = KoColor(palette().color(QPalette::Text), m_colorSpace);
}
}
class NotificationStroke : public QObject, public KisSimpleStrokeStrategy
{
Q_OBJECT
public:
NotificationStroke()
: KisSimpleStrokeStrategy(QLatin1String("NotificationStroke"))
{
setClearsRedoOnStart(false);
this->enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER);
this->enableJob(JOB_CANCEL, true, KisStrokeJobData::BARRIER);
}
void initStrokeCallback() {
emit timeout();
}
void cancelStrokeCallback() {
emit cancelled();
}
Q_SIGNALS:
void timeout();
void cancelled();
};
void KisPresetLivePreviewView::setupAndPaintStroke()
{
// limit the brush stroke size. larger brush strokes just don't look good and are CPU intensive
// we are making a proxy preset and setting it to the painter...otherwise setting the brush size of the original preset
// will fire off signals that make this run in an infinite loop
qreal previewSize = qBound(3.0, m_currentPreset->settings()->paintOpSize(), 25.0 ); // constrain live preview brush size
//Except for the sketchbrush where it determine sthe history.
if (m_currentPreset->paintOp().id() == "sketchbrush" ||
m_currentPreset->paintOp().id() == "spraybrush") {
previewSize = qMax(3.0, m_currentPreset->settings()->paintOpSize());
}
- KisPaintOpPresetSP proxy_preset = m_currentPreset->clone();
+ KisPaintOpPresetSP proxy_preset = m_currentPreset->clone().dynamicCast<KisPaintOpPreset>();
KisPaintOpSettingsSP settings = proxy_preset->settings();
settings->setPaintOpSize(previewSize);
int maxTextureSize = 200;
int textureOffsetX = settings->getInt("Texture/Pattern/MaximumOffsetX")*2;
int textureOffsetY = settings->getInt("Texture/Pattern/MaximumOffsetY")*2;
double textureScale = settings->getDouble("Texture/Pattern/Scale");
if ( textureOffsetX*textureScale> maxTextureSize || textureOffsetY*textureScale > maxTextureSize) {
int maxSize = qMax(textureOffsetX, textureOffsetY);
double result = qreal(maxTextureSize) / maxSize;
settings->setProperty("Texture/Pattern/Scale", result);
}
if (proxy_preset->paintOp().id() == "spraybrush") {
QDomElement element;
QDomDocument d;
QString brushDefinition = settings->getString("brush_definition");
if (!brushDefinition.isEmpty()) {
d.setContent(brushDefinition, false);
element = d.firstChildElement("Brush");
- KisBrushSP brush = KisBrush::fromXML(element);
+ KisBrushSP brush = KisBrush::fromXML(element, KisGlobalResourcesInterface::instance());
qreal width = brush->image().width();
qreal scale = brush->scale();
qreal diameterToBrushRatio = 1.0;
qreal diameter = settings->getInt("Spray/diameter");
//hack, 1000 being the maximum possible brushsize.
if (brush->filename().endsWith(".svg")) {
diameterToBrushRatio = diameter/(1000.0*scale);
scale = 25.0 / 1000.0;
} else {
if (width * scale > 25.0) {
diameterToBrushRatio = diameter / (width * scale);
scale = 25.0 / width;
}
}
settings->setProperty("Spray/diameter", int(25.0 * diameterToBrushRatio));
brush->setScale(scale);
d.clear();
element = d.createElement("Brush");
brush->toXML(d, element);
d.appendChild(element);
settings->setProperty("brush_definition", d.toString());
}
}
// Preset preview cannot display gradient color source: there is
// no resource manager for KisResourcesSnapshot, therefore gradient is nullptr.
// BUG: 385521 (Selecting "Gradient" in brush editor crashes krita)
if (proxy_preset->paintOp().id() == "paintbrush") {
QString colorSourceType = settings->getString("ColorSource/Type", "plain");
if (colorSourceType == "gradient") {
settings->setProperty("ColorSource/Type", "plain");
}
}
proxy_preset->setSettings(settings);
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(m_image,
m_layer);
resources->setOpacity(settings->paintOpOpacity());
resources->setBrush(proxy_preset);
resources->setFGColorOverride(m_paintColor);
KisFreehandStrokeInfo *strokeInfo = new KisFreehandStrokeInfo();
KisStrokeStrategy *stroke =
new FreehandStrokeStrategy(resources, strokeInfo, kundo2_noi18n("temp_stroke"));
KisStrokeId strokeId = m_image->startStroke(stroke);
// paint the stroke. The sketchbrush gets a different shape than the others to show how it works
if (proxy_preset->paintOp().id() == "sketchbrush"
|| proxy_preset->paintOp().id() == "curvebrush"
|| proxy_preset->paintOp().id() == "particlebrush") {
qreal startX = m_canvasCenterPoint.x() - (this->width()*0.4);
qreal endX = m_canvasCenterPoint.x() + (this->width()*0.4);
qreal middle = m_canvasCenterPoint.y();
KisPaintInformation pointOne;
pointOne.setPressure(0.0);
pointOne.setPos(QPointF(startX, middle));
KisPaintInformation pointTwo;
pointTwo.setPressure(0.0);
pointTwo.setPos(QPointF(startX, middle));
int repeats = 8;
for (int i = 0; i < repeats; i++) {
pointOne.setPos(pointTwo.pos());
pointOne.setPressure(pointTwo.pressure());
pointTwo.setPressure((1.0/repeats)*(i+1));
qreal xPos = ((1.0/repeats) * (i+1) * (endX-startX) )+startX;
pointTwo.setPos(QPointF(xPos, middle));
qreal offset = (this->height()/(repeats*1.5))*(i+1);
qreal handleY = middle + offset;
if (i%2 == 0) {
handleY = middle - offset;
}
m_image->addJob(strokeId,
new FreehandStrokeStrategy::Data(0,
pointOne,
QPointF(pointOne.pos().x(),
handleY),
QPointF(pointTwo.pos().x(),
handleY),
pointTwo));
m_image->addJob(strokeId, new KisAsyncronousStrokeUpdateHelper::UpdateData(true));
}
} else {
// paint an S curve
m_curvePointPI1.setPos(QPointF(m_canvasCenterPoint.x() - (this->width()*0.45),
m_canvasCenterPoint.y() + (this->height()*0.2)));
m_curvePointPI1.setPressure(0.0);
m_curvePointPI2.setPos(QPointF(m_canvasCenterPoint.x() + (this->width()*0.4),
m_canvasCenterPoint.y() - (this->height()*0.2) ));
m_curvePointPI2.setPressure(1.0);
m_image->addJob(strokeId,
new FreehandStrokeStrategy::Data(0,
m_curvePointPI1,
QPointF(m_canvasCenterPoint.x(),
m_canvasCenterPoint.y()-this->height()),
QPointF(m_canvasCenterPoint.x(),
m_canvasCenterPoint.y()+this->height()),
m_curvePointPI2));
m_image->addJob(strokeId, new KisAsyncronousStrokeUpdateHelper::UpdateData(true));
}
m_image->endStroke(strokeId);
m_previewGenerationInProgress = true;
NotificationStroke *notificationStroke = new NotificationStroke();
connect(notificationStroke, SIGNAL(timeout()), SLOT(slotPreviewGenerationCompleted()));
KisStrokeId notificationId = m_image->startStroke(notificationStroke);
m_image->endStroke(notificationId);
// TODO: if we don't have any regressions because of it until 4.2.8, then
// just remove this code.
// even though the brush is cloned, the proxy_preset still has some connection to the original preset which will mess brush sizing
// we need to return brush size to normal.The normal brush sends out a lot of extra signals, so keeping the proxy for now
//proxy_preset->settings()->setPaintOpSize(originalPresetSize);
}
#include "kis_preset_live_preview_view.moc"
diff --git a/libs/ui/widgets/kis_preset_selector_strip.cpp b/libs/ui/widgets/kis_preset_selector_strip.cpp
index 69373cce65..c43b560949 100644
--- a/libs/ui/widgets/kis_preset_selector_strip.cpp
+++ b/libs/ui/widgets/kis_preset_selector_strip.cpp
@@ -1,109 +1,108 @@
/*
* 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_preset_selector_strip.h"
-#include "KoResourceModel.h"
-#include "KoResourceItemView.h"
-#include "KoResourceItemChooser.h"
+#include <KisResourceItemListView.h>
+#include <KisResourceItemChooser.h>
#include <brushengine/kis_paintop_registry.h>
#include "kis_config.h"
#include <kis_icon.h>
#include <QAbstractScrollArea>
#include <QMouseEvent>
KisPresetSelectorStrip::KisPresetSelectorStrip(QWidget* parent)
: QWidget(parent)
{
setupUi(this);
smallPresetChooser->showButtons(false); //loading and saving buttons. don't need these with the brush editor
smallPresetChooser->setViewMode((KisPresetChooser::ViewMode)KisConfig(true).presetChooserViewMode());
smallPresetChooser->showTaggingBar(true);
m_resourceItemView = smallPresetChooser->itemChooser()->itemView();
/* This is an heuristic to fill smallPresetChooser with only the presets
* for the paintop that comes selected by default: Pixel Brush. */
const QString PIXEL_BRUSH_ID = "paintbrush";
m_currentPaintopID = PIXEL_BRUSH_ID;
// hide the left and right arrows that are used by the "strip" view by default
rightScrollBtn->hide();
leftScrollBtn->hide();
}
KisPresetSelectorStrip::~KisPresetSelectorStrip()
{
}
void KisPresetSelectorStrip::setPresetFilter(const QString& paintOpId)
{
smallPresetChooser->setPresetFilter(paintOpId);
if (m_currentPaintopID != paintOpId) {
m_resourceItemView->scrollTo(m_resourceItemView->model()->index(0, 0));
m_currentPaintopID = paintOpId;
}
}
void KisPresetSelectorStrip::on_leftScrollBtn_pressed()
{
// Deciding how far beyond the left margin (10 pixels) was an arbitrary decision
QPoint beyondLeftMargin(-10, 0);
m_resourceItemView->scrollTo(m_resourceItemView->indexAt(beyondLeftMargin), QAbstractItemView::EnsureVisible);
}
void KisPresetSelectorStrip::on_rightScrollBtn_pressed()
{
// Deciding how far beyond the right margin to put the point (10 pixels) was an arbitrary decision
QPoint beyondRightMargin(10 + m_resourceItemView->viewport()->width(), 0);
m_resourceItemView->scrollTo(m_resourceItemView->indexAt(beyondRightMargin), QAbstractItemView::EnsureVisible);
}
void KisPresetSelectorStrip::slotThumbnailMode()
{
smallPresetChooser->setViewMode(KisPresetChooser::THUMBNAIL); // set to details view by default to see names
m_resourceItemView = smallPresetChooser->itemChooser()->itemView();
}
void KisPresetSelectorStrip::slotDetailMode()
{
smallPresetChooser->setViewMode(KisPresetChooser::DETAIL); // set to details view by default to see names
m_resourceItemView = smallPresetChooser->itemChooser()->itemView();
}
int KisPresetSelectorStrip::iconSize()
{
return smallPresetChooser->iconSize();
}
void KisPresetSelectorStrip::slotSetIconSize(int size)
{
smallPresetChooser->setIconSize(size);
}
void KisPresetSelectorStrip::slotSaveIconSize() {
smallPresetChooser->saveIconSize();
}
diff --git a/libs/ui/widgets/kis_preset_selector_strip.h b/libs/ui/widgets/kis_preset_selector_strip.h
index 3be00ef3a7..5869a7123a 100644
--- a/libs/ui/widgets/kis_preset_selector_strip.h
+++ b/libs/ui/widgets/kis_preset_selector_strip.h
@@ -1,84 +1,84 @@
/*
* 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.
*/
#ifndef KIS_PRESET_SELECTOR_STRIP_H
#define KIS_PRESET_SELECTOR_STRIP_H
#include <QWidget>
#include "ui_wdgpresetselectorstrip.h"
-class KoResourceItemView;
+class KisResourceItemListView;
/**
*
* KisPresetSelectorStrip is a composite widget around KisPresetChooser. It provides
* a strip of icons with two scroll buttons at the sides and a small delete button
* that appears when a user selects a preset icon.
*
* KisPresetSelectorStrip makes it possible to quickly select and modify presets.
*
* Note that KisPresetSelectorStrip uses the QObject tree to access properties of the contained
* classes, and uses heuristics to approximate pixel offsets, times, and other
* properties that cannot be accessed through the QObject tree.
*
*/
class KisPresetSelectorStrip : public QWidget, public Ui::WdgPresetSelectorStrip
{
Q_OBJECT
public:
KisPresetSelectorStrip(QWidget *parent);
~KisPresetSelectorStrip() override;
void setPresetFilter(const QString& paintOpId);
int iconSize();
void setIconSize(int size);
public Q_SLOTS:
/// saving the icon base size. This affects all preset selectors
/// outside UI elements adjusting icon size
void slotSetIconSize(int size);
/// saves the icon size to the config file
/// when UI element is released, it is ok to save icon size to config
void slotSaveIconSize();
private Q_SLOTS:
/// Scrolls the strip's item view to the left
void on_leftScrollBtn_pressed();
/// Scrolls the strip's item view to the right
void on_rightScrollBtn_pressed();
/// Changes the preset list view type
void slotThumbnailMode();
void slotDetailMode();
private:
/**
* This is a workaround to access members of KisPresetChooser using the QObject tree
* instead of class methods
*/
- KoResourceItemView* m_resourceItemView;
+ KisResourceItemListView* m_resourceItemView;
QString m_currentPaintopID;
};
#endif // KIS_PRESET_SELECTOR_STRIP_H
diff --git a/libs/ui/widgets/kis_scratch_pad.cpp b/libs/ui/widgets/kis_scratch_pad.cpp
index 3f482cf9d5..cb309ae26d 100644
--- a/libs/ui/widgets/kis_scratch_pad.cpp
+++ b/libs/ui/widgets/kis_scratch_pad.cpp
@@ -1,521 +1,521 @@
/* This file is part of the KDE project
* Copyright 2010 (C) Boudewijn Rempt <boud@valdyas.org>
* Copyright 2011 (C) Dmitry Kazakov <dimula73@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_scratch_pad.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QMutex>
#include <KoColorSpace.h>
#include <KoColorProfile.h>
#include <KoColorSpaceRegistry.h>
#include <KoPointerEvent.h>
#include <resources/KoAbstractGradient.h>
#include <kis_cursor.h>
#include <kis_tool_utils.h>
#include <kis_paint_layer.h>
#include <kis_paint_device.h>
#include <kis_gradient_painter.h>
#include <kis_default_bounds.h>
#include <kis_canvas_resource_provider.h>
#include "kis_config.h"
#include "kis_image.h"
#include "kis_undo_stores.h"
#include "kis_update_scheduler.h"
#include "kis_post_execution_undo_adapter.h"
#include "kis_scratch_pad_event_filter.h"
#include "kis_painting_information_builder.h"
#include "kis_tool_freehand_helper.h"
#include "kis_image_patch.h"
#include "kis_canvas_widget_base.h"
#include "kis_layer_projection_plane.h"
#include "kis_node_graph_listener.h"
class KisScratchPadNodeListener : public KisNodeGraphListener
{
public:
KisScratchPadNodeListener(KisScratchPad *scratchPad)
: m_scratchPad(scratchPad)
{
}
void requestProjectionUpdate(KisNode *node, const QVector<QRect> &rects, bool resetAnimationCache) override {
KisNodeGraphListener::requestProjectionUpdate(node, rects, resetAnimationCache);
QMutexLocker locker(&m_lock);
Q_FOREACH (const QRect &rc, rects) {
m_scratchPad->imageUpdated(rc);
}
}
private:
KisScratchPad *m_scratchPad;
QMutex m_lock;
};
class KisScratchPadDefaultBounds : public KisDefaultBounds
{
public:
KisScratchPadDefaultBounds(KisScratchPad *scratchPad)
: m_scratchPad(scratchPad)
{
}
~KisScratchPadDefaultBounds() override {}
QRect bounds() const override {
return m_scratchPad->imageBounds();
}
void * sourceCookie() const override {
return m_scratchPad;
}
private:
Q_DISABLE_COPY(KisScratchPadDefaultBounds)
KisScratchPad *m_scratchPad;
};
KisScratchPad::KisScratchPad(QWidget *parent)
: QWidget(parent)
, m_toolMode(HOVERING)
, m_paintLayer(0)
, m_displayProfile(0)
, m_resourceProvider(0)
{
setAutoFillBackground(false);
setMouseTracking(true);
m_cursor = KisCursor::load("tool_freehand_cursor.png", 5, 5);
setCursor(m_cursor);
KisConfig cfg(true);
QImage checkImage = KisCanvasWidgetBase::createCheckersImage(cfg.checkSize());
m_checkBrush = QBrush(checkImage);
// We are not supposed to use updates here,
// so just set the listener to null
m_updateScheduler = new KisUpdateScheduler(0);
m_undoStore = new KisSurrogateUndoStore();
m_undoAdapter = new KisPostExecutionUndoAdapter(m_undoStore, m_updateScheduler);
m_nodeListener = new KisScratchPadNodeListener(this);
connect(this, SIGNAL(sigUpdateCanvas(QRect)), SLOT(slotUpdateCanvas(QRect)), Qt::QueuedConnection);
// filter will be deleted by the QObject hierarchy
m_eventFilter = new KisScratchPadEventFilter(this);
m_infoBuilder = new KisPaintingInformationBuilder();
m_scaleBorderWidth = 1;
}
KisScratchPad::~KisScratchPad()
{
delete m_infoBuilder;
delete m_undoAdapter;
delete m_undoStore;
delete m_updateScheduler;
delete m_nodeListener;
}
KisScratchPad::Mode KisScratchPad::modeFromButton(Qt::MouseButton button) const
{
return
button == Qt::NoButton ? HOVERING :
button == Qt::MidButton ? PANNING :
button == Qt::RightButton ? PICKING :
PAINTING;
}
void KisScratchPad::pointerPress(KoPointerEvent *event)
{
if (m_toolMode != HOVERING) return;
m_toolMode = modeFromButton(event->button());
if (m_toolMode == PAINTING) {
beginStroke(event);
event->accept();
}
else if (m_toolMode == PANNING) {
beginPan(event);
event->accept();
}
else if (m_toolMode == PICKING) {
pick(event);
event->accept();
}
}
void KisScratchPad::pointerRelease(KoPointerEvent *event)
{
if (modeFromButton(event->button()) != m_toolMode) return;
if (m_toolMode == PAINTING) {
endStroke(event);
m_toolMode = HOVERING;
event->accept();
}
else if (m_toolMode == PANNING) {
endPan(event);
m_toolMode = HOVERING;
event->accept();
}
else if (m_toolMode == PICKING) {
event->accept();
m_toolMode = HOVERING;
}
}
void KisScratchPad::pointerMove(KoPointerEvent *event)
{
m_helper->cursorMoved(documentToWidget().map(event->point));
if (m_toolMode == PAINTING) {
doStroke(event);
event->accept();
}
else if (m_toolMode == PANNING) {
doPan(event);
event->accept();
}
else if (m_toolMode == PICKING) {
pick(event);
event->accept();
}
}
void KisScratchPad::beginStroke(KoPointerEvent *event)
{
m_helper->initPaint(event,
documentToWidget().map(event->point),
0,
0,
m_updateScheduler,
m_paintLayer,
m_paintLayer->paintDevice()->defaultBounds());
}
void KisScratchPad::doStroke(KoPointerEvent *event)
{
m_helper->paintEvent(event);
}
void KisScratchPad::endStroke(KoPointerEvent *event)
{
Q_UNUSED(event);
m_helper->endPaint();
}
void KisScratchPad::beginPan(KoPointerEvent *event)
{
setCursor(QCursor(Qt::ClosedHandCursor));
m_panDocPoint = event->point;
}
void KisScratchPad::doPan(KoPointerEvent *event)
{
QPointF docOffset = event->point - m_panDocPoint;
m_translateTransform.translate(-docOffset.x(), -docOffset.y());
updateTransformations();
update();
}
void KisScratchPad::endPan(KoPointerEvent *event)
{
Q_UNUSED(event);
setCursor(m_cursor);
}
void KisScratchPad::pick(KoPointerEvent *event)
{
KoColor color;
if (KisToolUtils::pickColor(color, m_paintLayer->projection(), event->point.toPoint())) {
emit colorSelected(color);
}
}
void KisScratchPad::setOnScreenResolution(qreal scaleX, qreal scaleY)
{
m_scaleBorderWidth = BORDER_SIZE(qMax(scaleX, scaleY));
m_scaleTransform = QTransform::fromScale(scaleX, scaleY);
updateTransformations();
update();
}
QTransform KisScratchPad::documentToWidget() const
{
return m_translateTransform.inverted() * m_scaleTransform;
}
QTransform KisScratchPad::widgetToDocument() const
{
return m_scaleTransform.inverted() * m_translateTransform;
}
void KisScratchPad::updateTransformations()
{
m_eventFilter->setWidgetToDocumentTransform(widgetToDocument());
}
QRect KisScratchPad::imageBounds() const
{
return widgetToDocument().mapRect(rect());
}
void KisScratchPad::imageUpdated(const QRect &rect)
{
emit sigUpdateCanvas(documentToWidget().mapRect(QRectF(rect)).toAlignedRect());
}
void KisScratchPad::slotUpdateCanvas(const QRect &rect)
{
update(rect);
}
void KisScratchPad::paintEvent ( QPaintEvent * event ) {
if(!m_paintLayer) return;
QRectF imageRect = widgetToDocument().mapRect(QRectF(event->rect()));
QRect alignedImageRect =
imageRect.adjusted(-m_scaleBorderWidth, -m_scaleBorderWidth,
m_scaleBorderWidth, m_scaleBorderWidth).toAlignedRect();
QPointF offset = alignedImageRect.topLeft();
m_paintLayer->projectionPlane()->recalculate(alignedImageRect, m_paintLayer);
KisPaintDeviceSP projection = m_paintLayer->projection();
QImage image = projection->convertToQImage(m_displayProfile,
alignedImageRect.x(),
alignedImageRect.y(),
alignedImageRect.width(),
alignedImageRect.height(),
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
QPainter gc(this);
gc.fillRect(event->rect(), m_checkBrush);
gc.setRenderHints(QPainter::SmoothPixmapTransform);
gc.drawImage(QRectF(event->rect()), image, imageRect.translated(-offset));
QBrush brush(Qt::lightGray);
QPen pen(brush, 1, Qt::DotLine);
gc.setPen(pen);
if (m_cutoutOverlay.isValid()) {
gc.drawRect(m_cutoutOverlay);
}
if(!isEnabled()) {
QColor color(Qt::lightGray);
color.setAlphaF(0.5);
QBrush disabledBrush(color);
gc.fillRect(event->rect(), disabledBrush);
}
gc.end();
}
void KisScratchPad::setupScratchPad(KisCanvasResourceProvider* resourceProvider,
const QColor &defaultColor)
{
m_resourceProvider = resourceProvider;
KisConfig cfg(true);
setDisplayProfile(cfg.displayProfile(QApplication::desktop()->screenNumber(this)));
connect(m_resourceProvider, SIGNAL(sigDisplayProfileChanged(const KoColorProfile*)),
SLOT(setDisplayProfile(const KoColorProfile*)));
connect(m_resourceProvider, SIGNAL(sigOnScreenResolutionChanged(qreal,qreal)),
SLOT(setOnScreenResolution(qreal,qreal)));
connect(this, SIGNAL(colorSelected(KoColor)),
m_resourceProvider, SLOT(slotSetFGColor(KoColor)));
m_helper.reset(new KisToolFreehandHelper(m_infoBuilder, m_resourceProvider->resourceManager()));
m_defaultColor = KoColor(defaultColor, KoColorSpaceRegistry::instance()->rgb8());
KisPaintDeviceSP paintDevice =
new KisPaintDevice(m_defaultColor.colorSpace(), "scratchpad");
m_paintLayer = new KisPaintLayer(0, "ScratchPad", OPACITY_OPAQUE_U8, paintDevice);
m_paintLayer->setGraphListener(m_nodeListener);
m_paintLayer->paintDevice()->setDefaultBounds(new KisScratchPadDefaultBounds(this));
fillDefault();
}
void KisScratchPad::setCutoutOverlayRect(const QRect& rc)
{
m_cutoutOverlay = rc;
}
QImage KisScratchPad::cutoutOverlay() const
{
if(!m_paintLayer) return QImage();
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
QRect rc = widgetToDocument().mapRect(m_cutoutOverlay);
QImage rawImage = paintDevice->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
QImage scaledImage = rawImage.scaled(m_cutoutOverlay.size(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
return scaledImage;
}
void KisScratchPad::setPresetImage(const QImage& image)
{
m_presetImage = image;
}
void KisScratchPad::paintCustomImage(const QImage& loadedImage)
{
// this is 99% copied from the normal paintPresetImage()
// we don't want to save over the preset image, so we don't
// want to store it in the m_presetImage
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
QRect overlayRect = widgetToDocument().mapRect(m_cutoutOverlay);
QRect imageRect(QPoint(), overlayRect.size());
QImage scaledImage = loadedImage.scaled(overlayRect.size(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace());
device->convertFromQImage(scaledImage, 0);
KisPainter painter(paintDevice);
painter.bitBlt(overlayRect.topLeft(), device, imageRect);
update();
}
void KisScratchPad::paintPresetImage()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
QRect overlayRect = widgetToDocument().mapRect(m_cutoutOverlay);
QRect imageRect(QPoint(), overlayRect.size());
QImage scaledImage = m_presetImage.scaled(overlayRect.size(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace());
device->convertFromQImage(scaledImage, 0);
KisPainter painter(paintDevice);
painter.bitBlt(overlayRect.topLeft(), device, imageRect);
update();
}
void KisScratchPad::setDisplayProfile(const KoColorProfile *colorProfile)
{
if (colorProfile) {
m_displayProfile = colorProfile;
QWidget::update();
}
}
void KisScratchPad::fillDefault()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
paintDevice->setDefaultPixel(m_defaultColor);
paintDevice->clear();
update();
}
void KisScratchPad::fillTransparent() {
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
QColor transQColor(0,0,0,0);
KoColor transparentColor(transQColor, KoColorSpaceRegistry::instance()->rgb8());
transparentColor.setOpacity(0.0);
paintDevice->setDefaultPixel(transparentColor);
paintDevice->clear();
update();
}
void KisScratchPad::fillGradient()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
- KoAbstractGradient* gradient = m_resourceProvider->currentGradient();
+ KoAbstractGradientSP gradient = m_resourceProvider->currentGradient();
QRect gradientRect = widgetToDocument().mapRect(rect());
paintDevice->clear();
KisGradientPainter painter(paintDevice);
painter.setGradient(gradient);
painter.setGradientShape(KisGradientPainter::GradientShapeLinear);
painter.paintGradient(gradientRect.topLeft(),
gradientRect.bottomRight(),
KisGradientPainter::GradientRepeatNone,
0.2, false,
gradientRect.left(), gradientRect.top(),
gradientRect.width(), gradientRect.height());
update();
}
void KisScratchPad::fillBackground()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
paintDevice->setDefaultPixel(m_resourceProvider->bgColor());
paintDevice->clear();
update();
}
void KisScratchPad::fillLayer()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
KisPainter painter(paintDevice);
QRect sourceRect(0, 0, paintDevice->exactBounds().width(), paintDevice->exactBounds().height());
painter.bitBlt(QPoint(0, 0), m_resourceProvider->currentImage()->projection(), sourceRect);
update();
}
diff --git a/libs/ui/widgets/kis_stopgradient_slider_widget.cpp b/libs/ui/widgets/kis_stopgradient_slider_widget.cpp
index 210486ab72..259f958594 100644
--- a/libs/ui/widgets/kis_stopgradient_slider_widget.cpp
+++ b/libs/ui/widgets/kis_stopgradient_slider_widget.cpp
@@ -1,367 +1,367 @@
/*
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* 2016 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 "widgets/kis_stopgradient_slider_widget.h"
#include <QWindow>
#include <QPainter>
#include <QPixmap>
#include <QMouseEvent>
#include <QPolygon>
#include <QPaintEvent>
#include <QFontMetrics>
#include <QStyle>
#include <QApplication>
#include <QStyleOptionToolButton>
#include "kis_global.h"
#include "kis_debug.h"
#include "krita_utils.h"
KisStopGradientSliderWidget::KisStopGradientSliderWidget(QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f)
, m_selectedStop(0)
, m_drag(0)
{
QLinearGradient defaultGradient;
- m_defaultGradient.reset(KoStopGradient::fromQGradient(&defaultGradient));
+ m_defaultGradient = KoStopGradient::fromQGradient(&defaultGradient);
setGradientResource(0);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
setMouseTracking(true);
QWindow *window = this->window()->windowHandle();
if (window) {
connect(window, SIGNAL(screenChanged(QScreen*)), SLOT(updateHandleSize()));
}
updateHandleSize();
}
void KisStopGradientSliderWidget::updateHandleSize()
{
QFontMetrics fm(font());
const int h = fm.height();
m_handleSize = QSize(0.34 * h, h);
}
int KisStopGradientSliderWidget::handleClickTolerance() const
{
// the size of the default text!
return m_handleSize.width();
}
-void KisStopGradientSliderWidget::setGradientResource(KoStopGradient* gradient)
+void KisStopGradientSliderWidget::setGradientResource(KoStopGradientSP gradient)
{
- m_gradient = gradient ? gradient : m_defaultGradient.data();
+ m_gradient = gradient ? gradient : m_defaultGradient;
if (m_gradient && m_selectedStop >= 0) {
m_selectedStop = qBound(0, m_selectedStop, m_gradient->stops().size() - 1);
emit sigSelectedStop(m_selectedStop);
} else {
m_selectedStop = -1;
}
}
void KisStopGradientSliderWidget::paintHandle(qreal position, const QColor &color, bool isSelected, QPainter *painter)
{
const QRect handlesRect = this->handlesStipeRect();
const int handleCenter = handlesRect.left() + position * handlesRect.width();
const int handlesHalfWidth = handlesRect.height() * 0.26; // = 1.0 / 0.66 * 0.34 / 2.0 <-- golden ratio
QPolygon triangle(3);
triangle[0] = QPoint(handleCenter, handlesRect.top());
triangle[1] = QPoint(handleCenter - handlesHalfWidth, handlesRect.bottom());
triangle[2] = QPoint(handleCenter + handlesHalfWidth, handlesRect.bottom());
const qreal lineWidth = 1.0;
if (!isSelected) {
painter->setPen(QPen(palette().text(), lineWidth));
painter->setBrush(QBrush(color));
painter->setRenderHint(QPainter::Antialiasing);
painter->drawPolygon(triangle);
} else {
painter->setPen(QPen(palette().highlight(), 1.5 * lineWidth));
painter->setBrush(QBrush(color));
painter->setRenderHint(QPainter::Antialiasing);
painter->drawPolygon(triangle);
}
}
void KisStopGradientSliderWidget::paintEvent(QPaintEvent* pe)
{
QWidget::paintEvent(pe);
QPainter painter(this);
painter.setPen(Qt::black);
const QRect previewRect = gradientStripeRect();
KritaUtils::renderExactRect(&painter, kisGrowRect(previewRect, 1));
painter.drawRect(previewRect);
if (m_gradient) {
QImage image = m_gradient->generatePreview(previewRect.width(), previewRect.height());
if (!image.isNull()) {
painter.drawImage(previewRect.topLeft(), image);
}
QList<KoGradientStop> handlePositions = m_gradient->stops();
for (int i = 0; i < handlePositions.count(); i++) {
if (i == m_selectedStop) continue;
paintHandle(handlePositions[i].first,
handlePositions[i].second.toQColor(),
false, &painter);
}
if (m_selectedStop >= 0) {
paintHandle(handlePositions[m_selectedStop].first,
handlePositions[m_selectedStop].second.toQColor(),
true, &painter);
}
}
}
int findNearestHandle(qreal t, const qreal tolerance, const QList<KoGradientStop> &stops)
{
int result = -1;
qreal minDistance = tolerance;
for (int i = 0; i < stops.size(); i++) {
const KoGradientStop &stop = stops[i];
const qreal distance = qAbs(t - stop.first);
if (distance < minDistance) {
minDistance = distance;
result = i;
}
}
return result;
}
void KisStopGradientSliderWidget::mousePressEvent(QMouseEvent * e)
{
if (!allowedClickRegion(handleClickTolerance()).contains(e->pos())) {
QWidget::mousePressEvent(e);
return;
}
if (e->buttons() != Qt::LeftButton ) {
QWidget::mousePressEvent(e);
return;
}
const QRect handlesRect = this->handlesStipeRect();
const qreal t = (qreal(e->x()) - handlesRect.x()) / handlesRect.width();
const QList<KoGradientStop> stops = m_gradient->stops();
const int clickedStop = findNearestHandle(t, qreal(handleClickTolerance()) / handlesRect.width(), stops);
if (clickedStop >= 0) {
if (m_selectedStop != clickedStop) {
m_selectedStop = clickedStop;
emit sigSelectedStop(m_selectedStop);
}
m_drag = true;
} else {
insertStop(qBound(0.0, t, 1.0));
m_drag = true;
}
update();
updateCursor(e->pos());
}
void KisStopGradientSliderWidget::mouseReleaseEvent(QMouseEvent * e)
{
Q_UNUSED(e);
m_drag = false;
updateCursor(e->pos());
}
int getNewInsertPosition(const KoGradientStop &stop, const QList<KoGradientStop> &stops)
{
int result = 0;
for (int i = 0; i < stops.size(); i++) {
if (stop.first <= stops[i].first) break;
result = i + 1;
}
return result;
}
void KisStopGradientSliderWidget::mouseMoveEvent(QMouseEvent * e)
{
updateCursor(e->pos());
if (m_drag) {
const QRect handlesRect = this->handlesStipeRect();
double t = (qreal(e->x()) - handlesRect.x()) / handlesRect.width();
QList<KoGradientStop> stops = m_gradient->stops();
if (t < -0.1 || t > 1.1) {
if (stops.size() > 2 && m_selectedStop >= 0) {
m_removedStop = stops[m_selectedStop];
stops.removeAt(m_selectedStop);
m_selectedStop = -1;
}
} else {
if (m_selectedStop < 0) {
m_removedStop.first = qBound(0.0, t, 1.0);
const int newPos = getNewInsertPosition(m_removedStop, stops);
stops.insert(newPos, m_removedStop);
m_selectedStop = newPos;
} else {
KoGradientStop draggedStop = stops[m_selectedStop];
draggedStop.first = qBound(0.0, t, 1.0);
stops.removeAt(m_selectedStop);
const int newPos = getNewInsertPosition(draggedStop, stops);
stops.insert(newPos, draggedStop);
m_selectedStop = newPos;
}
}
m_gradient->setStops(stops);
emit sigSelectedStop(m_selectedStop);
update();
} else {
QWidget::mouseMoveEvent(e);
}
}
void KisStopGradientSliderWidget::updateCursor(const QPoint &pos)
{
const bool isInAllowedRegion =
allowedClickRegion(handleClickTolerance()).contains(pos);
QCursor currentCursor;
if (isInAllowedRegion) {
const QRect handlesRect = this->handlesStipeRect();
const qreal t = (qreal(pos.x()) - handlesRect.x()) / handlesRect.width();
const QList<KoGradientStop> stops = m_gradient->stops();
const int clickedStop = findNearestHandle(t, qreal(handleClickTolerance()) / handlesRect.width(), stops);
if (clickedStop >= 0) {
currentCursor = m_drag ? Qt::ClosedHandCursor : Qt::OpenHandCursor;
}
}
if (currentCursor.shape() != Qt::ArrowCursor) {
setCursor(currentCursor);
} else {
unsetCursor();
}
}
void KisStopGradientSliderWidget::insertStop(double t)
{
KIS_ASSERT_RECOVER(t >= 0 && t <= 1.0 ) {
t = qBound(0.0, t, 1.0);
}
QList<KoGradientStop> stops = m_gradient->stops();
KoColor color;
m_gradient->colorAt(color, t);
const KoGradientStop stop(t, color);
const int newPos = getNewInsertPosition(stop, stops);
stops.insert(newPos, stop);
m_gradient->setStops(stops);
m_selectedStop = newPos;
emit sigSelectedStop(m_selectedStop);
}
QRect KisStopGradientSliderWidget::sliderRect() const
{
return QRect(QPoint(), size()).adjusted(m_handleSize.width(), 1, -m_handleSize.width(), -1);
}
QRect KisStopGradientSliderWidget::gradientStripeRect() const
{
const QRect rc = sliderRect();
return rc.adjusted(0, 0, 0, -m_handleSize.height());
}
QRect KisStopGradientSliderWidget::handlesStipeRect() const
{
const QRect rc = sliderRect();
return rc.adjusted(0, rc.height() - m_handleSize.height(), 0, 0);
}
QRegion KisStopGradientSliderWidget::allowedClickRegion(int tolerance) const
{
QRegion result;
result += sliderRect();
result += handlesStipeRect().adjusted(-tolerance, 0, tolerance, 0);
return result;
}
int KisStopGradientSliderWidget::selectedStop()
{
return m_selectedStop;
}
void KisStopGradientSliderWidget::setSelectedStop(int selected)
{
m_selectedStop = selected;
emit sigSelectedStop(m_selectedStop);
update();
}
int KisStopGradientSliderWidget::minimalHeight() const
{
QFontMetrics fm(font());
const int h = fm.height();
QStyleOptionToolButton opt;
QSize sz = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(h, h), this);
return sz.height() + m_handleSize.height();
}
QSize KisStopGradientSliderWidget::sizeHint() const
{
const int h = minimalHeight();
return QSize(2 * h, h);
}
QSize KisStopGradientSliderWidget::minimumSizeHint() const
{
const int h = minimalHeight();
return QSize(h, h);
}
diff --git a/libs/ui/widgets/kis_stopgradient_slider_widget.h b/libs/ui/widgets/kis_stopgradient_slider_widget.h
index f00d7f83c4..9b1663b15f 100644
--- a/libs/ui/widgets/kis_stopgradient_slider_widget.h
+++ b/libs/ui/widgets/kis_stopgradient_slider_widget.h
@@ -1,82 +1,82 @@
/*
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* 2016 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_STOP_GRADIENT_SLIDER_WIDGET_H_
#define _KIS_STOP_GRADIENT_SLIDER_WIDGET_H_
#include <QWidget>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QScopedPointer>
-
+#include <KoStopGradient.h>
#include <resources/KoStopGradient.h>
class KisStopGradientSliderWidget : public QWidget
{
Q_OBJECT
public:
KisStopGradientSliderWidget(QWidget *parent = 0, Qt::WindowFlags f = 0);
public:
void paintEvent(QPaintEvent *) override;
- void setGradientResource(KoStopGradient* gradient);
+ void setGradientResource(KoStopGradientSP gradient);
int selectedStop();
void setSelectedStop(int selected);
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
Q_SIGNALS:
void sigSelectedStop(int stop);
protected:
void mousePressEvent(QMouseEvent * e) override;
void mouseReleaseEvent(QMouseEvent * e) override;
void mouseMoveEvent(QMouseEvent * e) override;
private Q_SLOTS:
void updateHandleSize();
private:
void insertStop(double t);
QRect sliderRect() const;
QRect gradientStripeRect() const;
QRect handlesStipeRect() const;
QRegion allowedClickRegion(int tolerance) const;
void updateCursor(const QPoint &pos);
void paintHandle(qreal position, const QColor &color, bool isSelected, QPainter *painter);
int handleClickTolerance() const;
int minimalHeight() const;
private:
- QScopedPointer<KoStopGradient> m_defaultGradient;
- KoStopGradient* m_gradient;
+ KoStopGradientSP m_defaultGradient;
+ KoStopGradientSP m_gradient;
int m_selectedStop;
KoGradientStop m_removedStop;
bool m_drag;
QSize m_handleSize;
};
#endif
diff --git a/libs/ui/widgets/kis_tone_curve_widget.cpp b/libs/ui/widgets/kis_tone_curve_widget.cpp
index 679fed4fd5..6f3208217f 100644
--- a/libs/ui/widgets/kis_tone_curve_widget.cpp
+++ b/libs/ui/widgets/kis_tone_curve_widget.cpp
@@ -1,362 +1,363 @@
/*
* Copyright (C) 2015 by Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
*
* Based on the Digikam CIE Tongue widget
* Copyright (C) 2006-2013 by Gilles Caulier <caulier dot gilles at gmail dot com>
*
* Any source code are inspired from lprof project and
* Copyright (C) 1998-2001 Marti Maria
*
* 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.
*
* 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 <QPointF>
#include <QPolygonF>
#include <QPainter>
#include <QPaintEvent>
#include <QImage>
+#include <QTextStream>
#include <cmath>
#include <klocalizedstring.h>
#include "kis_tone_curve_widget.h"
class Q_DECL_HIDDEN KisToneCurveWidget::Private
{
public:
Private() :
profileDataAvailable(false),
needUpdatePixmap(false),
TRCGray(true),
xBias(0),
yBias(0),
pxcols(0),
pxrows(0),
gridside(0)
{
}
bool profileDataAvailable;
bool needUpdatePixmap;
bool TRCGray;
bool TRCRGB;
int xBias;
int yBias;
int pxcols;
int pxrows;
QPolygonF ToneCurveGray;
QPolygonF ToneCurveRed;
QPolygonF ToneCurveGreen;
QPolygonF ToneCurveBlue;
double gridside;
QPainter painter;
QPainter painter2;
QPixmap pixmap;
QPixmap curvemap;
};
KisToneCurveWidget::KisToneCurveWidget(QWidget *parent) :
QWidget(parent), d(new Private)
{
/*this is a tone curve widget*/
}
KisToneCurveWidget::~KisToneCurveWidget()
{
delete d;
}
void KisToneCurveWidget::setGreyscaleCurve(QPolygonF poly)
{
d->ToneCurveGray = poly;
d->TRCGray = true;
d->TRCRGB = false;
d->profileDataAvailable = true;
d->needUpdatePixmap = true;
}
void KisToneCurveWidget::setRGBCurve(QPolygonF red, QPolygonF green, QPolygonF blue)
{
d->ToneCurveRed = red;
d->ToneCurveGreen = green;
d->ToneCurveBlue = blue;
d->profileDataAvailable = true;
d->TRCGray = false;
d->TRCRGB = true;
d->needUpdatePixmap = true;
}
void KisToneCurveWidget::setCMYKCurve(QPolygonF cyan, QPolygonF magenta, QPolygonF yellow, QPolygonF key)
{
d->ToneCurveRed = cyan;
d->ToneCurveGreen = magenta;
d->ToneCurveBlue = yellow;
d->ToneCurveGray = key;
d->profileDataAvailable = true;
d->TRCGray = false;
d->TRCRGB = false;
d->needUpdatePixmap = true;
}
void KisToneCurveWidget::setProfileDataAvailable(bool dataAvailable)
{
d->profileDataAvailable = dataAvailable;
}
int KisToneCurveWidget::grids(double val) const
{
return (int) floor(val * d->gridside + 0.5);
}
void KisToneCurveWidget::mapPoint(QPointF & xy)
{
QPointF dummy = xy;
xy.setX( (int) floor((dummy.x() * (d->pxcols - 1)) + .5) + d->xBias);
xy.setY( (int) floor(((d->pxrows - 1) - dummy.y() * (d->pxrows - 1)) + .5) );
}
void KisToneCurveWidget::biasedLine(int x1, int y1, int x2, int y2)
{
d->painter.drawLine(x1 + d->xBias, y1, x2 + d->xBias, y2);
}
void KisToneCurveWidget::biasedText(int x, int y, const QString& txt)
{
d->painter.drawText(QPoint(d->xBias + x, y), txt);
}
void KisToneCurveWidget::drawGrid()
{
d->painter.setOpacity(1.0);
d->painter.setPen(qRgb(255, 255, 255));
biasedLine(0, 0, 0, d->pxrows - 1);
biasedLine(0, d->pxrows-1, d->pxcols-1, d->pxrows - 1);
QFont font;
font.setPointSize(6);
d->painter.setFont(font);
for (int y = 1; y <= 9; y += 1)
{
QString s;
int xstart = (y * (d->pxcols - 1)) / 10;
int ystart = (y * (d->pxrows - 1)) / 10;
- s.sprintf("0.%d", y);
+ QTextStream(&s) << y;
biasedLine(xstart, d->pxrows - grids(1), xstart, d->pxrows - grids(4));
biasedText(xstart - grids(11), d->pxrows + grids(15), s);
- s.sprintf("0.%d", 10 - y);
+ QTextStream(&s) << 10 - y;
biasedLine(0, ystart, grids(3), ystart);
biasedText(grids(-25), ystart + grids(5), s);
}
d->painter.setPen(qRgb(128, 128, 128));
d->painter.setOpacity(0.5);
for (int y = 1; y <= 9; y += 1)
{
int xstart = (y * (d->pxcols - 1)) / 10;
int ystart = (y * (d->pxrows - 1)) / 10;
biasedLine(xstart, grids(4), xstart, d->pxrows - grids(4) - 1);
biasedLine(grids(7), ystart, d->pxcols-1-grids(7), ystart);
}
d->painter.setOpacity(1.0);
d->painter.setOpacity(1.0);
}
void KisToneCurveWidget::updatePixmap()
{
d->needUpdatePixmap = false;
d->pixmap = QPixmap(size());
d->curvemap = QPixmap(size());
d->pixmap.fill(Qt::black);
d->curvemap.fill(Qt::transparent);
d->painter.begin(&d->pixmap);
int pixcols = d->pixmap.width();
int pixrows = d->pixmap.height();
d->gridside = (qMin(pixcols, pixrows)) / 512.0;
d->xBias = grids(32);
d->yBias = grids(20);
d->pxcols = pixcols - d->xBias;
d->pxrows = pixrows - d->yBias;
d->painter.setBackground(QBrush(qRgb(0, 0, 0)));
QPointF start;
drawGrid();
d->painter.setRenderHint(QPainter::Antialiasing);
if (d->TRCGray && d->ToneCurveGray.size()>0){
QPainterPath path;
start = d->ToneCurveGray.at(0);
mapPoint(start);
path.moveTo(start);
foreach (QPointF Point, d->ToneCurveGray) {
mapPoint(Point);
path.lineTo(Point);
}
d->painter.setPen(qRgb(255, 255, 255));
d->painter.drawPath(path);
} else if (d->TRCRGB && d->ToneCurveRed.size()>0 && d->ToneCurveGreen.size()>0 && d->ToneCurveBlue.size()>0){
d->painter.save();
d->painter.setCompositionMode(QPainter::CompositionMode_Screen);
QPainterPath path;
start = d->ToneCurveRed.at(0);
mapPoint(start);
path.moveTo(start);
foreach (QPointF Point, d->ToneCurveRed) {
mapPoint(Point);
path.lineTo(Point);
}
d->painter.setPen(qRgb(255, 0, 0));
d->painter.drawPath(path);
QPainterPath path2;
start = d->ToneCurveGreen.at(0);
mapPoint(start);
path2.moveTo(start);
foreach (QPointF Point, d->ToneCurveGreen) {
mapPoint(Point);
path2.lineTo(Point);
}
d->painter.setPen(qRgb(0, 255, 0));
d->painter.drawPath(path2);
QPainterPath path3;
start = d->ToneCurveBlue.at(0);
mapPoint(start);
path3.moveTo(start);
foreach (QPointF Point, d->ToneCurveBlue) {
mapPoint(Point);
path3.lineTo(Point);
}
d->painter.setPen(qRgb(0, 0, 255));
d->painter.drawPath(path3);
d->painter.restore();
} else {
d->painter2.begin(&d->curvemap);
d->painter2.setRenderHint(QPainter::Antialiasing);
//d->painter2.setCompositionMode(QPainter::CompositionMode_Multiply);
QPainterPath path;
start = d->ToneCurveRed.at(0);
mapPoint(start);
path.moveTo(start);
foreach (QPointF Point, d->ToneCurveRed) {
mapPoint(Point);
path.lineTo(Point);
}
d->painter2.setPen(qRgb(0, 255, 255));
d->painter2.drawPath(path);
QPainterPath path2;
start = d->ToneCurveGreen.at(0);
mapPoint(start);
path2.moveTo(start);
foreach (QPointF Point, d->ToneCurveGreen) {
mapPoint(Point);
path2.lineTo(Point);
}
d->painter2.setPen(qRgb(255, 0, 255));
d->painter2.drawPath(path2);
QPainterPath path3;
start = d->ToneCurveBlue.at(0);
mapPoint(start);
path3.moveTo(start);
foreach (QPointF Point, d->ToneCurveBlue) {
mapPoint(Point);
path3.lineTo(Point);
}
d->painter2.setPen(qRgb(255, 255, 0));
d->painter2.drawPath(path3);
QPainterPath path4;
start = d->ToneCurveGray.at(0);
mapPoint(start);
path4.moveTo(start);
foreach (QPointF Point, d->ToneCurveGray) {
mapPoint(Point);
path4.lineTo(Point);
}
d->painter2.setPen(qRgb(80, 80, 80));
d->painter2.drawPath(path4);
d->painter2.end();
QRect area(d->xBias, 0, d->pxcols, d->pxrows);
d->painter.drawPixmap(area,d->curvemap, area);
}
d->painter.end();
}
void KisToneCurveWidget::paintEvent(QPaintEvent*)
{
QPainter p(this);
// Widget is disable : drawing grayed frame.
if ( !isEnabled() )
{
p.fillRect(0, 0, width(), height(),
palette().color(QPalette::Disabled, QPalette::Background));
QPen pen(palette().color(QPalette::Disabled, QPalette::Foreground));
pen.setStyle(Qt::SolidLine);
pen.setWidth(1);
p.setPen(pen);
p.drawRect(0, 0, width(), height());
return;
}
// No profile data to show, or RAW file
if (!d->profileDataAvailable)
{
p.fillRect(0, 0, width(), height(), palette().color(QPalette::Active, QPalette::Background));
QPen pen(palette().color(QPalette::Active, QPalette::Text));
pen.setStyle(Qt::SolidLine);
pen.setWidth(1);
p.setPen(pen);
p.drawRect(0, 0, width(), height());
p.setPen(Qt::red);
p.drawText(0, 0, width(), height(), Qt::AlignCenter,
i18n("No tone curve available..."));
return;
}
// Create CIE tongue if needed
if (d->needUpdatePixmap)
{
updatePixmap();
}
// draw prerendered tongue
p.drawPixmap(0, 0, d->pixmap);
}
void KisToneCurveWidget::resizeEvent(QResizeEvent* event)
{
Q_UNUSED(event);
setMinimumWidth(height());
setMaximumWidth(height());
d->needUpdatePixmap = true;
}
diff --git a/libs/ui/widgets/kis_wdg_generator.cpp b/libs/ui/widgets/kis_wdg_generator.cpp
index 7c0d5d429a..9308fc5b09 100644
--- a/libs/ui/widgets/kis_wdg_generator.cpp
+++ b/libs/ui/widgets/kis_wdg_generator.cpp
@@ -1,174 +1,175 @@
/* 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 <KisGlobalResourcesInterface.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 d->currentGenerator->defaultConfiguration(KisGlobalResourcesInterface::instance());
}
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, true);
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());
+ widget->setConfiguration(d->currentGenerator->defaultConfiguration(KisGlobalResourcesInterface::instance()));
}
}
d->widgetLayout->addWidget(d->centralWidget, 0 , 0);
d->uiWdgGenerators.centralWidgetHolder->setMinimumSize(d->centralWidget->minimumSize());
}
diff --git a/libs/ui/widgets/kis_workspace_chooser.cpp b/libs/ui/widgets/kis_workspace_chooser.cpp
index d15f31bbbb..c72247de3a 100644
--- a/libs/ui/widgets/kis_workspace_chooser.cpp
+++ b/libs/ui/widgets/kis_workspace_chooser.cpp
@@ -1,238 +1,231 @@
/* 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 <QListView>
#include <KisKineticScroller.h>
#include <klocalizedstring.h>
-#include <KoResourceItemChooser.h>
-#include <KoResourceItemView.h>
-#include <KoResourceServerAdapter.h>
-#include <resources/KoResource.h>
+#include <KisResourceItemChooser.h>
+#include <KisResourceItemListView.h>
+#include <KoResource.h>
+#include <KisResourceModel.h>
+#include <KisResourceServerProvider.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>
+#include "kis_canvas_resource_provider.h"
+#include "KisMainWindow.h"
+#include "KisPart.h"
+#include "KisWindowLayoutManager.h"
+#include "dialogs/KisNewWindowLayoutDialog.h"
+#include "kis_config.h"
+#include <kis_icon.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());
+ QString name = index.data(Qt::UserRole + KisResourceModel::Name).toString();
+ painter->drawText(option.rect.x() + 5, option.rect.y() + painter->fontMetrics().ascent() + 5, 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"));
+ m_workspaceWidgets = createChooserWidgets(ResourceType::Workspaces, i18n("Workspaces"));
+ m_windowLayoutWidgets = createChooserWidgets(ResourceType::WindowLayouts, i18n("Window layouts"));
- connect(m_workspaceWidgets.itemChooser, SIGNAL(resourceSelected(KoResource*)),
- this, SLOT(workspaceSelected(KoResource*)));
+ connect(m_workspaceWidgets.itemChooser, SIGNAL(resourceSelected(KoResourceSP )),
+ this, SLOT(workspaceSelected(KoResourceSP )));
connect(m_workspaceWidgets.saveButton, SIGNAL(clicked(bool)), this, SLOT(slotSaveWorkspace()));
+ connect(m_workspaceWidgets.nameEdit, SIGNAL(textEdited(const QString&)), this, SLOT(slotUpdateWorkspaceSaveButton()));
- connect(m_windowLayoutWidgets.itemChooser, SIGNAL(resourceSelected(KoResource*)),
- this, SLOT(windowLayoutSelected(KoResource*)));
+ connect(m_windowLayoutWidgets.itemChooser, SIGNAL(resourceSelected(KoResourceSP )),
+ this, SLOT(windowLayoutSelected(KoResourceSP )));
connect(m_windowLayoutWidgets.saveButton, SIGNAL(clicked(bool)), this, SLOT(slotSaveWindowLayout()));
+ connect(m_windowLayoutWidgets.nameEdit, SIGNAL(textEdited(const QString&)), this, SLOT(slotUpdateWindowLayoutSaveButton()));
}
-KisWorkspaceChooser::ChooserWidgets KisWorkspaceChooser::createChooserWidgets(QSharedPointer<KoAbstractResourceServerAdapter> adapter, const QString &title)
+KisWorkspaceChooser::ChooserWidgets KisWorkspaceChooser::createChooserWidgets(const QString &resourceType, 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);
+ m_workspaceSaveLocation = KisResourceServerProvider::instance()->workspaceServer()->saveLocation();
+ m_windowLayoutSaveLocation = KisResourceServerProvider::instance()->windowLayoutServer()->saveLocation();
+
+ widgets.itemChooser = new KisResourceItemChooser(resourceType, false, this);
widgets.itemChooser->setItemDelegate(new KisWorkspaceDelegate(this));
widgets.itemChooser->setFixedSize(250, 250);
widgets.itemChooser->setRowHeight(30);
- widgets.itemChooser->setColumnCount(1);
+ widgets.itemChooser->itemView()->setViewMode(QListView::ListMode);
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());
+ KisWorkspaceResourceSP workspace(new KisWorkspaceResource(QString()));
workspace->setDockerState(m_view->qtMainWindow()->saveState());
+ workspace->setImage(m_view->mainWindow()->layoutThumbnail());
m_view->canvasResourceProvider()->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());
+ workspace->setName(name);
+ workspace->setFilename(name.split(" ").join("_")+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);
+ KisResourceModelProvider::resourceModel(ResourceType::Workspaces)->addResource(workspace);
+}
+
+void KisWorkspaceChooser::slotUpdateWorkspaceSaveButton()
+{
+ if (QFileInfo(m_workspaceSaveLocation + m_workspaceWidgets.nameEdit->text().split(" ").join("_") + ".kws").exists()) {
+ m_workspaceWidgets.saveButton->setIcon(KisIconUtils::loadIcon("warning"));
+ m_workspaceWidgets.saveButton->setToolTip(i18n("File name already in use. Saving will overwrite the original Workspace."));
+ //m_workspaceWidgets.saveButton->setText(i18n("Overwrite"));
+ } else {
+ m_workspaceWidgets.saveButton->setIcon(QIcon());
+ m_workspaceWidgets.saveButton->setToolTip(i18n("Save current workspace."));
+ //m_workspaceWidgets.saveButton->setText(i18n("Save"));
}
- workspace->setName(name);
- rserver->addResource(workspace);
}
-void KisWorkspaceChooser::workspaceSelected(KoResource *resource)
+void KisWorkspaceChooser::workspaceSelected(KoResourceSP resource)
{
if (!m_view->qtMainWindow()) {
return;
}
KisConfig cfg(false);
cfg.writeEntry("CurrentWorkspace", resource->name());
- KisWorkspaceResource* workspace = static_cast<KisWorkspaceResource*>(resource);
+ KisWorkspaceResourceSP workspace = resource.staticCast<KisWorkspaceResource>();
KisMainWindow *mainWindow = qobject_cast<KisMainWindow*>(m_view->qtMainWindow());
- mainWindow->restoreWorkspace(workspace);
-
+ mainWindow->restoreWorkspace(workspace->resourceId());
}
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);
+ KisWindowLayoutResourceSP 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());
+ layout->setName(name);
+ layout->setFilename(name.split(" ").join("_") + layout->defaultFileExtension());
+ KisResourceModelProvider::resourceModel(ResourceType::WindowLayouts)->addResource(layout);
+}
- if (newName) {
- name = i18n("Window Layout %1", i);
+void KisWorkspaceChooser::slotUpdateWindowLayoutSaveButton()
+{
+ if (QFileInfo(m_windowLayoutSaveLocation + m_windowLayoutWidgets.nameEdit->text().split(" ").join("_") + ".kwl").exists()) {
+ m_windowLayoutWidgets.saveButton->setIcon(KisIconUtils::loadIcon("warning"));
+ m_workspaceWidgets.saveButton->setToolTip(i18n("File name already in use. Saving will overwrite the original window layout."));
+ } else {
+ m_windowLayoutWidgets.saveButton->setIcon(QIcon());
+ m_workspaceWidgets.saveButton->setToolTip(i18n("Save current window layout."));
}
- layout->setName(name);
- rserver->addResource(layout);
}
-void KisWorkspaceChooser::windowLayoutSelected(KoResource * resource)
+void KisWorkspaceChooser::windowLayoutSelected(KoResourceSP resource)
{
- auto *layout = static_cast<KisWindowLayoutResource*>(resource);
+ KisWindowLayoutResourceSP layout = resource.dynamicCast<KisWindowLayoutResource>();
layout->applyLayout();
}
diff --git a/libs/ui/widgets/kis_workspace_chooser.h b/libs/ui/widgets/kis_workspace_chooser.h
index 645b5001cc..71389ccf42 100644
--- a/libs/ui/widgets/kis_workspace_chooser.h
+++ b/libs/ui/widgets/kis_workspace_chooser.h
@@ -1,65 +1,69 @@
/* 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.
*/
#ifndef KIS_WORKSPACE_CHOOSER_H
#define KIS_WORKSPACE_CHOOSER_H
#include <QWidget>
+#include <KoResource.h>
class QLineEdit;
class QPushButton;
class QGridLayout;
-class KoResourceItemChooser;
+class KisResourceItemChooser;
class KisViewManager;
-class KoResource;
-class KoAbstractResourceServerAdapter;
class KisWorkspaceChooser : public QWidget
{
Q_OBJECT
public:
KisWorkspaceChooser(KisViewManager * view, QWidget* parent = 0);
~KisWorkspaceChooser() override;
private Q_SLOTS:
void slotSaveWorkspace();
- void workspaceSelected( KoResource * resource );
+ void slotUpdateWorkspaceSaveButton();
+ void workspaceSelected( KoResourceSP resource );
void slotSaveWindowLayout();
- void windowLayoutSelected( KoResource * resource );
+ void slotUpdateWindowLayoutSaveButton();
+ void windowLayoutSelected( KoResourceSP resource );
private:
struct ChooserWidgets
{
- KoResourceItemChooser *itemChooser;
+ KisResourceItemChooser *itemChooser;
QLineEdit *nameEdit;
QPushButton *saveButton;
};
KisViewManager *m_view;
QGridLayout* m_layout;
ChooserWidgets m_workspaceWidgets;
ChooserWidgets m_windowLayoutWidgets;
- ChooserWidgets createChooserWidgets(QSharedPointer<KoAbstractResourceServerAdapter> adapter, const QString &title);
+ QString m_workspaceSaveLocation;
+ QString m_windowLayoutSaveLocation;
+
+ ChooserWidgets createChooserWidgets(const QString &resourceType, const QString &title);
};
#endif // KIS_WORKSPACE_CHOOSER_H
diff --git a/libs/widgets/CMakeLists.txt b/libs/widgets/CMakeLists.txt
index c709f4fcab..73b775a6b9 100644
--- a/libs/widgets/CMakeLists.txt
+++ b/libs/widgets/CMakeLists.txt
@@ -1,134 +1,118 @@
add_subdirectory( tests )
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(kritawidgets_LIB_SRCS
KoVBox.cpp
KoDialog.cpp
KoZoomWidget.cpp
- KoTagToolButton.cpp
- KoTagChooserWidget.cpp
- KoTagFilterWidget.cpp
- KoResourceTaggingManager.cpp
- KoResourceItemChooserContextMenu.cpp
KoAspectButton.cpp
KoPagePreviewWidget.cpp
KoSliderCombo.cpp
KoColorPopupButton.cpp
KoConfigAuthorPage.cpp
KoUnitDoubleSpinBox.cpp
KoZoomAction.cpp
KoZoomController.cpp
KoZoomInput.cpp
KoZoomHandler.cpp
KoZoomMode.cpp
KoDpi.cpp
KoColorPatch.cpp
KoColorPopupAction.cpp
KoColorSetWidget.cpp
KoColorSlider.cpp
KoTriangleColorSelector.cpp
KoResourcePopupAction.cpp
- KoIconToolTip.cpp
- KoResourceItemChooser.cpp
- KoResourceItemChooserSync.cpp
- KoResourceModel.cpp
- KoResourceItemDelegate.cpp
- KoResourceItemView.cpp
- KoResourceTagStore.cpp
KoRuler.cpp
- KoItemToolTip.cpp
- KoCheckerBoardPainter.cpp
- KoResourceServerAdapter.cpp
KoResourceServerProvider.cpp
KoLineStyleSelector.cpp
KoLineStyleItemDelegate.cpp
KoLineStyleModel.cpp
- KoResourceFiltering.cpp
KoTitledTabWidget.cpp
KoToolBoxButton.cpp
KoToolBox.cpp
KoToolBoxDocker.cpp
KoToolBoxFactory.cpp
KoToolDocker.cpp
KoPageLayoutWidget.cpp
KoPageLayoutDialog.cpp
KoShadowConfigWidget.cpp
KoMarkerSelector.cpp
KoMarkerModel.cpp
KoMarkerItemDelegate.cpp
KoDocumentInfoDlg.cpp
- KoTableView.cpp
WidgetsDebug.cpp
kis_file_name_requester.cpp
KisColorSelectorInterface.cpp
KoAnchorSelectionWidget.cpp
KisGradientSlider.cpp
KisGradientSliderWidget.cpp
kis_color_input.cpp
# classes used by internal color selector
kis_spinbox_color_selector.cpp
KisVisualColorSelector.cpp
KisVisualColorSelectorShape.cpp
KisVisualEllipticalSelectorShape.cpp
KisVisualRectangleSelectorShape.cpp
KisVisualTriangleSelectorShape.cpp
KisScreenColorPickerBase.cpp
KisDlgInternalColorSelector.cpp
KisPaletteModel.cpp
KisPaletteDelegate.cpp
kis_palette_view.cpp
- KisPaletteListWidget.cpp
+ KisPaletteChooser.cpp
KisPaletteComboBox.cpp
- kis_popup_button.cc
kis_color_button.cpp
)
ki18n_wrap_ui( kritawidgets_LIB_SRCS
KoConfigAuthorPage.ui
koDocumentInfoAboutWidget.ui
koDocumentInfoAuthorWidget.ui
wdg_file_name_requester.ui
KoPageLayoutWidget.ui
KoShadowConfigWidget.ui
WdgDlgInternalColorSelector.ui
WdgPaletteListWidget.ui
)
add_library(kritawidgets SHARED ${kritawidgets_LIB_SRCS})
generate_export_header(kritawidgets BASE_NAME kritawidgets)
target_link_libraries(kritawidgets
kritaodf
kritaglobal
kritaflake
kritapigment
kritawidgetutils
+ kritaresources
+ kritaresourcewidgets
Qt5::PrintSupport
KF5::CoreAddons
KF5::ConfigGui
KF5::GuiAddons
KF5::WidgetsAddons
KF5::ConfigCore
KF5::Completion
)
if(X11_FOUND)
target_link_libraries(kritawidgets Qt5::X11Extras ${X11_LIBRARIES})
endif()
set_target_properties(kritawidgets PROPERTIES
VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritawidgets ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/libs/widgets/KisColorsetChooser.h b/libs/widgets/KisColorsetChooser.h
deleted file mode 100644
index f51cdda0f6..0000000000
--- a/libs/widgets/KisColorsetChooser.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2013 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.
- */
-
-
-#ifndef KIS_COLORSET_CHOOSER_H
-#define KIS_COLORSET_CHOOSER_H
-
-#include <QWidget>
-
-class QSpinBox;
-class KoColorSet;
-class QLineEdit;
-class KoResourceItemChooser;
-class KoResource;
-
-#include "kritawidgets_export.h"
-
-
-class KRITAWIDGETS_EXPORT KisColorsetChooser : public QWidget
-{
- Q_OBJECT
-public:
- KisColorsetChooser(QWidget* parent = 0);
- ~KisColorsetChooser() override;
-
-Q_SIGNALS:
- void paletteSelected(KoColorSet* colorSet);
-
-private Q_SLOTS:
- void resourceSelected(KoResource* resource);
- void slotSave();
-
-private:
- KoResourceItemChooser * m_itemChooser;
- QLineEdit* m_nameEdit;
- QSpinBox* m_columnEdit;
-};
-
-#endif // COLORSET_CHOOSER_H
diff --git a/libs/widgets/KisDlgInternalColorSelector.cpp b/libs/widgets/KisDlgInternalColorSelector.cpp
index dfce1dac69..afb780a512 100644
--- a/libs/widgets/KisDlgInternalColorSelector.cpp
+++ b/libs/widgets/KisDlgInternalColorSelector.cpp
@@ -1,348 +1,348 @@
/*
* 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 <KisPaletteChooser.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 "KisSqueezedComboBox.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;
+ KisPaletteChooser *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->paletteChooser = new KisPaletteChooser(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_d->paletteChooser, SIGNAL(sigPaletteSelected(KoColorSetSP)), this, SLOT(slotChangePalette(KoColorSetSP)));
connect(m_ui->cmbNameList, SIGNAL(sigColorSelected(KoColor)), SLOT(slotColorUpdated(KoColor)));
// For some bizarre 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);
+ KoColorSetSP savedPal = rServer->resourceByName(paletteName);
if (savedPal) {
this->slotChangePalette(savedPal);
} else {
- if (rServer->resources().count()) {
- savedPal = rServer->resources().first();
+ if (rServer->resourceCount()) {
+ savedPal = rServer->firstResource();
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();
}
}
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()), 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)
{
// not-so-nice solution: if someone calls this slot directly and that code was
// triggered by our compressor signal, our compressor is technically the sender()!
if (sender() == m_d->compressColorChanges) {
return;
}
// Do not accept external updates while a color update emit is pending;
// Note: Assumes external updates only come from parent(), a separate slot might be better
if (m_d->allowUpdates || (QObject::sender() && QObject::sender() != this->parent())) {
// Enforce palette colors
KConfigGroup group(KSharedConfig::openConfig(), "");
if (group.readEntry("colorsettings/forcepalettecolors", false)) {
newColor = m_ui->paletteBox->closestColor(newColor);
}
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);
if (m_d->currentColor.colorSpace() != m_d->currentColorSpace) {
m_d->currentColor.convertTo(m_d->currentColorSpace);
}
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::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) {
m_ui->paletteBox->selectClosestColor(m_d->currentColor);
}
m_ui->previousColor->setColor(m_d->previousColor);
m_ui->currentColor->setColor(m_d->currentColor);
if (source && source != this->parent()) {
m_d->allowUpdates = false;
m_d->compressColorChanges->start();
}
if (m_d->screenColorPicker) {
m_d->screenColorPicker->updateIcons();
}
}
void KisDlgInternalColorSelector::endUpdateWithNewColor()
{
emit signalForegroundColorChosen(m_d->currentColor);
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)
+void KisDlgInternalColorSelector::slotChangePalette(KoColorSetSP 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 424e2c8042..6c90a35044 100644
--- a/libs/widgets/KisDlgInternalColorSelector.h
+++ b/libs/widgets/KisDlgInternalColorSelector.h
@@ -1,187 +1,187 @@
/*
* 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 <QDialog>
#include "KisScreenColorPickerBase.h"
class Ui_WdgDlgInternalColorSelector;
class KoColorPatch;
/**
* @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 parent parent widget.
* @param caption the dialog caption.
*/
static KoColor getModalColorDialog(const KoColor color, QWidget* parent = 0, 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:
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);
+ void slotChangePalette(KoColorSetSP 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/KisGradientSliderWidget.cpp b/libs/widgets/KisGradientSliderWidget.cpp
index 924cb57533..214427afde 100644
--- a/libs/widgets/KisGradientSliderWidget.cpp
+++ b/libs/widgets/KisGradientSliderWidget.cpp
@@ -1,225 +1,225 @@
/*
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* 2004 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 "KisGradientSliderWidget.h"
#include <QPainter>
#include <QContextMenuEvent>
#include <QPixmap>
#include <QMouseEvent>
#include <QPolygon>
#include <QPaintEvent>
#include <QMenu>
#include <kis_debug.h>
#include <klocalizedstring.h>
#include <QAction>
#include <resources/KoSegmentGradient.h>
#define MARGIN 5
#define HANDLE_SIZE 10
KisGradientSliderWidget::KisGradientSliderWidget(QWidget *parent, const char* name, Qt::WindowFlags f)
: QWidget(parent, f),
m_currentSegment(0),
m_selectedSegment(0),
m_drag(0)
{
setObjectName(name);
setMinimumHeight(30);
m_segmentMenu = new QMenu();
m_segmentMenu->addAction(i18n("Split Segment"), this, SLOT(slotSplitSegment()));
m_segmentMenu->addAction(i18n("Duplicate Segment"), this, SLOT(slotDuplicateSegment()));
m_segmentMenu->addAction(i18n("Mirror Segment"), this, SLOT(slotMirrorSegment()));
m_removeSegmentAction = new QAction(i18n("Remove Segment"), this);
connect(m_removeSegmentAction, SIGNAL(triggered()), this, SLOT(slotRemoveSegment()));
m_segmentMenu->addAction(m_removeSegmentAction);
}
-void KisGradientSliderWidget::setGradientResource(KoSegmentGradient* agr)
+void KisGradientSliderWidget::setGradientResource(KoSegmentGradientSP agr)
{
m_autogradientResource = agr;
m_selectedSegment = m_autogradientResource->segmentAt(0.0);
emit sigSelectedSegment(m_selectedSegment);
}
void KisGradientSliderWidget::paintEvent(QPaintEvent* pe)
{
QWidget::paintEvent(pe);
QPainter painter(this);
painter.fillRect(rect(), palette().window());
painter.setPen(Qt::black);
painter.drawRect(MARGIN, MARGIN, width() - 2 * MARGIN, height() - 2 * MARGIN - HANDLE_SIZE);
if (m_autogradientResource) {
QImage image = m_autogradientResource->generatePreview(width() - 2 * MARGIN - 2, height() - 2 * MARGIN - HANDLE_SIZE - 2);
QPixmap pixmap(image.width(), image.height());
if (!image.isNull()) {
painter.drawImage(MARGIN + 1, MARGIN + 1, image);
}
painter.fillRect(MARGIN + 1, height() - MARGIN - HANDLE_SIZE, width() - 2 * MARGIN, HANDLE_SIZE, QBrush(Qt::white));
if (m_selectedSegment) {
QRect selection(qRound(m_selectedSegment->startOffset()*(double)(width() - 2 * MARGIN - 2)) + 6,
height() - HANDLE_SIZE - MARGIN,
qRound((m_selectedSegment->endOffset() - m_selectedSegment->startOffset())*(double)(width() - 12)),
HANDLE_SIZE);
painter.fillRect(selection, QBrush(palette().highlight()));
}
QPolygon triangle(3);
QList<double> handlePositions = m_autogradientResource->getHandlePositions();
int position;
painter.setBrush(QBrush(Qt::black));
for (int i = 0; i < handlePositions.count(); i++) {
position = qRound(handlePositions[i] * (double)(width() - 12)) + 6;
triangle[0] = QPoint(position, height() - HANDLE_SIZE - MARGIN);
triangle[1] = QPoint(position + (HANDLE_SIZE / 2 - 1), height() - MARGIN);
triangle[2] = QPoint(position - (HANDLE_SIZE / 2 - 1), height() - MARGIN);
painter.drawPolygon(triangle);
}
painter.setBrush(QBrush(Qt::white));
QList<double> middleHandlePositions = m_autogradientResource->getMiddleHandlePositions();
for (int i = 0; i < middleHandlePositions.count(); i++) {
position = qRound(middleHandlePositions[i] * (double)(width() - 12)) + 6;
triangle[0] = QPoint(position, height() - HANDLE_SIZE - MARGIN);
triangle[1] = QPoint(position + (HANDLE_SIZE / 2 - 2), height() - MARGIN);
triangle[2] = QPoint(position - (HANDLE_SIZE / 2 - 2), height() - MARGIN);
painter.drawPolygon(triangle);
}
}
}
void KisGradientSliderWidget::mousePressEvent(QMouseEvent * e)
{
if ((e->y() < MARGIN || e->y() > height() - MARGIN) || (e->x() < MARGIN || e->x() > width() - MARGIN) || e-> button() != Qt::LeftButton) {
QWidget::mousePressEvent(e);
return;
}
double t = (double)(e->x() - MARGIN) / (double)(width() - 2 * MARGIN);
KoGradientSegment* segment = 0;
segment = m_autogradientResource->segmentAt(t);
if (segment != 0) {
m_currentSegment = segment;
QRect leftHandle(qRound(m_currentSegment->startOffset() *(double)(width() - 2*MARGIN - 2) + MARGIN - (HANDLE_SIZE / 2 - 1)),
height() - HANDLE_SIZE,
HANDLE_SIZE - 1,
HANDLE_SIZE);
QRect middleHandle(qRound(m_currentSegment->middleOffset() *(double)(width() - 2*MARGIN - 2) + MARGIN - (HANDLE_SIZE / 2 - 2)),
height() - HANDLE_SIZE - MARGIN,
HANDLE_SIZE - 1,
HANDLE_SIZE);
QRect rightHandle(qRound(m_currentSegment->endOffset() *(double)(width() - 2*MARGIN - 2) + MARGIN - (HANDLE_SIZE / 2 - 1)),
height() - HANDLE_SIZE,
HANDLE_SIZE - 1,
HANDLE_SIZE);
// Change the activation order of the handles to avoid deadlocks
if (t > 0.5) {
if (leftHandle.contains(e->pos()))
m_drag = LEFT_DRAG;
else if (middleHandle.contains(e->pos()))
m_drag = MIDDLE_DRAG;
else if (rightHandle.contains(e->pos()))
m_drag = RIGHT_DRAG;
} else {
if (rightHandle.contains(e->pos()))
m_drag = RIGHT_DRAG;
else if (middleHandle.contains(e->pos()))
m_drag = MIDDLE_DRAG;
else if (leftHandle.contains(e->pos()))
m_drag = LEFT_DRAG;
}
if (m_drag == NO_DRAG) {
m_selectedSegment = m_currentSegment;
emit sigSelectedSegment(m_selectedSegment);
}
}
repaint();
}
void KisGradientSliderWidget::mouseReleaseEvent(QMouseEvent * e)
{
Q_UNUSED(e);
m_drag = NO_DRAG;
}
void KisGradientSliderWidget::mouseMoveEvent(QMouseEvent * e)
{
if ((e->y() < MARGIN || e->y() > height() - MARGIN) || (e->x() < MARGIN || e->x() > width() - MARGIN)) {
QWidget::mouseMoveEvent(e);
return;
}
double t = (double)(e->x() - MARGIN) / (double)(width() - 2 * MARGIN);
switch (m_drag) {
case RIGHT_DRAG:
m_autogradientResource->moveSegmentEndOffset(m_currentSegment, t);
break;
case LEFT_DRAG:
m_autogradientResource->moveSegmentStartOffset(m_currentSegment, t);
break;
case MIDDLE_DRAG:
m_autogradientResource->moveSegmentMiddleOffset(m_currentSegment, t);
break;
}
if (m_drag != NO_DRAG)
emit sigChangedSegment(m_currentSegment);
repaint();
}
void KisGradientSliderWidget::contextMenuEvent(QContextMenuEvent * e)
{
m_removeSegmentAction->setEnabled(m_autogradientResource->removeSegmentPossible());
m_segmentMenu->popup(e->globalPos());
}
void KisGradientSliderWidget::slotSplitSegment()
{
m_autogradientResource->splitSegment(m_selectedSegment);
emit sigSelectedSegment(m_selectedSegment);
repaint();
}
void KisGradientSliderWidget::slotDuplicateSegment()
{
m_autogradientResource->duplicateSegment(m_selectedSegment);
emit sigSelectedSegment(m_selectedSegment);
repaint();
}
void KisGradientSliderWidget::slotMirrorSegment()
{
m_autogradientResource->mirrorSegment(m_selectedSegment);
emit sigSelectedSegment(m_selectedSegment);
repaint();
}
void KisGradientSliderWidget::slotRemoveSegment()
{
m_selectedSegment = m_autogradientResource->removeSegment(m_selectedSegment);
emit sigSelectedSegment(m_selectedSegment);
repaint();
}
diff --git a/libs/widgets/KisGradientSliderWidget.h b/libs/widgets/KisGradientSliderWidget.h
index d48866ead0..6951aa9b99 100644
--- a/libs/widgets/KisGradientSliderWidget.h
+++ b/libs/widgets/KisGradientSliderWidget.h
@@ -1,91 +1,91 @@
/*
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* 2004 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_GRADIENT_SLIDER_WIDGET_H_
#define _KIS_GRADIENT_SLIDER_WIDGET_H_
#include <QWidget>
#include <QMouseEvent>
#include <QPaintEvent>
+#include <KoSegmentGradient.h>
+
class QAction;
class QMenu;
+
class KoGradientSegment;
-class KoSegmentGradient;
#include "kritawidgets_export.h"
/**
* @brief The KisGradientSliderWidget class makes it possible to edit gradients.
*/
class KRITAWIDGETS_EXPORT KisGradientSliderWidget : public QWidget
{
Q_OBJECT
public:
KisGradientSliderWidget(QWidget *parent = 0, const char* name = 0, Qt::WindowFlags f = 0);
public:
void paintEvent(QPaintEvent *) override;
- void setGradientResource(KoSegmentGradient* agr);
- KoGradientSegment* selectedSegment() {
- return m_selectedSegment;
- }
+ void setGradientResource(KoSegmentGradientSP agr);
+ KoGradientSegment *selectedSegment() { return m_selectedSegment; }
Q_SIGNALS:
void sigSelectedSegment(KoGradientSegment*);
void sigChangedSegment(KoGradientSegment*);
protected:
void mousePressEvent(QMouseEvent * e) override;
void mouseReleaseEvent(QMouseEvent * e) override;
void mouseMoveEvent(QMouseEvent * e) override;
void contextMenuEvent(QContextMenuEvent * e) override;
private Q_SLOTS:
void slotSplitSegment();
void slotDuplicateSegment();
void slotMirrorSegment();
void slotRemoveSegment();
private:
enum {
NO_DRAG,
LEFT_DRAG,
RIGHT_DRAG,
MIDDLE_DRAG
};
enum {
SPLIT_SEGMENT,
DUPLICATE_SEGMENT,
MIRROR_SEGMENT,
REMOVE_SEGMENT
};
- KoSegmentGradient* m_autogradientResource;
+ KoSegmentGradientSP m_autogradientResource;
KoGradientSegment* m_currentSegment;
KoGradientSegment* m_selectedSegment;
QMenu* m_segmentMenu;
int m_drag;
QAction *m_removeSegmentAction;
};
#endif
diff --git a/libs/widgets/KisPaletteChooser.cpp b/libs/widgets/KisPaletteChooser.cpp
new file mode 100644
index 0000000000..905a20744e
--- /dev/null
+++ b/libs/widgets/KisPaletteChooser.cpp
@@ -0,0 +1,194 @@
+/*
+ * 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 <QPointer>
+#include <QScopedPointer>
+#include <QGridLayout>
+#include <QSet>
+#include <QStringList>
+#include <QFile>
+#include <QMessageBox>
+#include <QInputDialog>
+#include <QDir>
+
+#include <kis_icon.h>
+#include <KoFileDialog.h>
+
+#include <KisResourceModel.h>
+#include <KisResourceItemListView.h>
+
+#include <ui_WdgPaletteListWidget.h>
+#include "KisPaletteChooser.h"
+#include "KisPaletteChooser_p.h"
+
+KisPaletteChooser::KisPaletteChooser(QWidget *parent)
+ : QWidget(parent)
+ , m_ui(new Ui_WdgPaletteListWidget)
+ , m_d(new KisPaletteChooserPrivate(this))
+{
+ m_d->allowModification = false;
+
+ m_d->actAdd.reset(new QAction(KisIconUtils::loadIcon("list-add"),
+ i18n("Add a new palette")));
+ m_d->actRemove.reset(new QAction(KisIconUtils::loadIcon("list-remove"),
+ i18n("Remove current palette")));
+ m_d->actImport.reset(new QAction(KisIconUtils::loadIcon("document-import"),
+ i18n("Import a new palette from file")));
+ m_d->actExport.reset(new QAction(KisIconUtils::loadIcon("document-export"),
+ i18n("Export current palette to file")));
+ m_ui->setupUi(this);
+ m_ui->bnAdd->setDefaultAction(m_d->actAdd.data());
+ m_ui->bnRemove->setDefaultAction(m_d->actRemove.data());
+ m_ui->bnImport->setDefaultAction(m_d->actImport.data());
+ m_ui->bnExport->setDefaultAction(m_d->actExport.data());
+
+ m_ui->bnAdd->setEnabled(false);
+ m_ui->bnRemove->setEnabled(false);
+ m_ui->bnImport->setEnabled(false);
+ m_ui->bnExport->setEnabled(false);
+
+ connect(m_d->actAdd.data(), SIGNAL(triggered()), SLOT(slotAdd()));
+ connect(m_d->actRemove.data(), SIGNAL(triggered()), SLOT(slotRemove()));
+ connect(m_d->actImport.data(), SIGNAL(triggered()), SLOT(slotImport()));
+ connect(m_d->actExport.data(), SIGNAL(triggered()), SLOT(slotExport()));
+
+ m_d->itemChooser->setItemDelegate(m_d->delegate.data());
+ m_d->itemChooser->setRowHeight(40);
+ m_d->itemChooser->itemView()->setViewMode(QListView::ListMode);
+ m_d->itemChooser->showButtons(false);
+ m_d->itemChooser->showTaggingBar(true);
+ m_ui->viewPalette->setLayout(new QHBoxLayout(m_ui->viewPalette));
+ m_ui->viewPalette->layout()->addWidget(m_d->itemChooser.data());
+
+ connect(m_d->itemChooser.data(), SIGNAL(resourceSelected(KoResourceSP )), SLOT(slotPaletteResourceSelected(KoResourceSP )));
+}
+
+KisPaletteChooser::~KisPaletteChooser()
+{ }
+
+void KisPaletteChooser::slotPaletteResourceSelected(KoResourceSP r)
+{
+ KoColorSetSP g = r.staticCast<KoColorSet>();
+ emit sigPaletteSelected(g);
+ if (!m_d->allowModification) { return; }
+ if (g->isEditable()) {
+ m_ui->bnRemove->setEnabled(true);
+ } else {
+ m_ui->bnRemove->setEnabled(false);
+ }
+}
+
+void KisPaletteChooser::slotAdd()
+{
+ if (!m_d->allowModification) { return; }
+ emit sigAddPalette();
+ m_d->itemChooser->setCurrentItem(m_d->itemChooser->rowCount() - 1);
+}
+
+void KisPaletteChooser::slotRemove()
+{
+ if (!m_d->allowModification) { return; }
+ if (m_d->itemChooser->currentResource()) {
+ KoColorSetSP cs = m_d->itemChooser->currentResource().staticCast<KoColorSet>();
+ emit sigRemovePalette(cs);
+ }
+ m_d->itemChooser->setCurrentItem(0);
+}
+
+void KisPaletteChooser::slotImport()
+{
+ if (!m_d->allowModification) { return; }
+ emit sigImportPalette();
+ m_d->itemChooser->setCurrentItem(m_d->itemChooser->rowCount() - 1);
+}
+
+void KisPaletteChooser::slotExport()
+{
+ if (!m_d->allowModification) { return; }
+ emit sigExportPalette(m_d->itemChooser->currentResource().staticCast<KoColorSet>());
+}
+
+void KisPaletteChooser::setAllowModification(bool allowModification)
+{
+ m_d->allowModification = allowModification;
+ m_ui->bnAdd->setEnabled(allowModification);
+ m_ui->bnImport->setEnabled(allowModification);
+ m_ui->bnExport->setEnabled(allowModification);
+ KoColorSetSP cs = m_d->itemChooser->currentResource().staticCast<KoColorSet>();
+ m_ui->bnRemove->setEnabled(allowModification && cs && cs->isEditable());
+}
+
+/************************* KisPaletteChooserPrivate **********************/
+
+KisPaletteChooserPrivate::KisPaletteChooserPrivate(KisPaletteChooser *a_c)
+ : c(a_c)
+ , itemChooser(new KisResourceItemChooser(ResourceType::Palettes, false, a_c))
+ , delegate(new Delegate(a_c))
+{ }
+
+KisPaletteChooserPrivate::~KisPaletteChooserPrivate()
+{ }
+
+/******************* KisPaletteChooserPrivate::Delegate ******************/
+
+KisPaletteChooserPrivate::Delegate::Delegate(QObject *parent)
+ : QAbstractItemDelegate(parent)
+{ }
+
+KisPaletteChooserPrivate::Delegate::~Delegate()
+{ }
+
+void KisPaletteChooserPrivate::Delegate::paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ painter->save();
+ if (!index.isValid())
+ return;
+
+ QImage preview = index.data(Qt::UserRole + KisResourceModel::Thumbnail).value<QImage>();
+ QString name = index.data(Qt::UserRole + KisResourceModel::Name).toString();
+
+ QRect previewRect(option.rect.x() + 2,
+ option.rect.y() + 2,
+ option.rect.height() - 4,
+ option.rect.height() - 4);
+
+ painter->drawImage(previewRect, preview);
+
+ if (option.state & QStyle::State_Selected) {
+ painter->fillRect(option.rect, option.palette.highlight());
+ painter->drawImage(previewRect, preview);
+ painter->setPen(option.palette.highlightedText().color());
+ } else {
+ painter->setBrush(option.palette.text().color());
+ }
+
+ painter->drawText(option.rect.x() + previewRect.width() + 10,
+ option.rect.y() + painter->fontMetrics().ascent() + 5,
+ name);
+
+ painter->restore();
+}
+
+inline QSize KisPaletteChooserPrivate::Delegate::sizeHint(const QStyleOptionViewItem & option,
+ const QModelIndex &) const
+{
+ return option.decorationSize;
+}
diff --git a/libs/widgets/KisPaletteChooser.h b/libs/widgets/KisPaletteChooser.h
new file mode 100644
index 0000000000..a1a0be1795
--- /dev/null
+++ b/libs/widgets/KisPaletteChooser.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef KISPALETTELISTWIDGET_H
+#define KISPALETTELISTWIDGET_H
+
+#include <QString>
+#include <QWidget>
+#include <ui_WdgPaletteListWidget.h>
+
+#include "kritawidgets_export.h"
+
+#include <KoColorSet.h>
+
+class KoResource;
+
+
+struct KisPaletteChooserPrivate;
+
+class KRITAWIDGETS_EXPORT KisPaletteChooser : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit KisPaletteChooser(QWidget *parent = nullptr);
+ virtual ~KisPaletteChooser();
+
+public:
+ void setAllowModification(bool allowModification);
+
+Q_SIGNALS:
+ void sigPaletteSelected(KoColorSetSP);
+ void sigAddPalette();
+ void sigRemovePalette(KoColorSetSP);
+ void sigImportPalette();
+ void sigExportPalette(KoColorSetSP);
+
+public Q_SLOTS:
+
+private /* methods */:
+ QString newPaletteFileName();
+
+private Q_SLOTS:
+ void slotPaletteResourceSelected(KoResourceSP);
+ void slotAdd();
+ void slotRemove();
+ void slotImport();
+ void slotExport();
+
+private:
+ QScopedPointer<Ui_WdgPaletteListWidget> m_ui;
+ QScopedPointer<KisPaletteChooserPrivate> m_d;
+};
+
+#endif // KISPALETTELISTWIDGET_H
diff --git a/libs/widgets/KisPaletteChooser_p.h b/libs/widgets/KisPaletteChooser_p.h
new file mode 100644
index 0000000000..1cddc82201
--- /dev/null
+++ b/libs/widgets/KisPaletteChooser_p.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+#ifndef KISPALETTELISTWIDGET_P_H
+#define KISPALETTELISTWIDGET_P_H
+
+#include <QAbstractItemDelegate>
+#include <QListView>
+#include <QAbstractListModel>
+#include <QPointer>
+#include <QCheckBox>
+#include <QAction>
+#include <QPainter>
+
+#include <KisPaletteChooser.h>
+#include <KisResourceItemView.h>
+#include <KisResourceItemChooser.h>
+#include <KoColorSet.h>
+
+struct KisPaletteChooserPrivate
+{
+ class View;
+ class Delegate;
+ class Model;
+ KisPaletteChooserPrivate(KisPaletteChooser *);
+ virtual ~KisPaletteChooserPrivate();
+
+ bool allowModification;
+
+ QPointer<KisPaletteChooser> c;
+
+ QSharedPointer<KisResourceItemChooser> itemChooser;
+
+ QScopedPointer<Delegate> delegate;
+
+ QScopedPointer<QAction> actAdd;
+ QScopedPointer<QAction> actImport;
+ QScopedPointer<QAction> actExport;
+ QScopedPointer<QAction> actModify;
+ QScopedPointer<QAction> actRemove;
+};
+
+class KisPaletteChooserPrivate::Delegate : public QAbstractItemDelegate
+{
+public:
+ Delegate(QObject *);
+ virtual ~Delegate();
+ void paint(QPainter * painter,
+ const QStyleOptionViewItem & option,
+ const QModelIndex & index) const override;
+ QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const override;
+};
+
+#endif // KISPALETTELISTWIDGET_P_H
diff --git a/libs/widgets/KisPaletteComboBox.cpp b/libs/widgets/KisPaletteComboBox.cpp
index e0cf48d691..3999ea85e2 100644
--- a/libs/widgets/KisPaletteComboBox.cpp
+++ b/libs/widgets/KisPaletteComboBox.cpp
@@ -1,160 +1,160 @@
/*
* 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)
: KisSqueezedComboBox(parent)
, m_model(0)
{
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)));
}
void KisPaletteComboBox::slotPaletteChanged()
{
clear();
m_groupMapMap.clear();
m_idxSwatchMap.clear();
- if (QPointer<KoColorSet>(m_model->colorSet()).isNull()) { return; }
+ if (QSharedPointer<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/KisPaletteListWidget.cpp b/libs/widgets/KisPaletteListWidget.cpp
deleted file mode 100644
index 22fcb3632d..0000000000
--- a/libs/widgets/KisPaletteListWidget.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * 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 <QPointer>
-#include <QScopedPointer>
-#include <QGridLayout>
-#include <QSet>
-#include <QStringList>
-#include <QFile>
-#include <QMessageBox>
-#include <QInputDialog>
-#include <QDir>
-
-#include <kis_icon.h>
-#include <KoFileDialog.h>
-
-#include <ui_WdgPaletteListWidget.h>
-#include "KisPaletteListWidget.h"
-#include "KisPaletteListWidget_p.h"
-
-KisPaletteListWidget::KisPaletteListWidget(QWidget *parent)
- : QWidget(parent)
- , m_ui(new Ui_WdgPaletteListWidget)
- , m_d(new KisPaletteListWidgetPrivate(this))
-{
- m_d->allowModification = false;
-
- m_d->actAdd.reset(new QAction(KisIconUtils::loadIcon("list-add"),
- i18n("Add a new palette")));
- m_d->actRemove.reset(new QAction(KisIconUtils::loadIcon("list-remove"),
- i18n("Remove current palette")));
- m_d->actImport.reset(new QAction(KisIconUtils::loadIcon("document-import"),
- i18n("Import a new palette from file")));
- m_d->actExport.reset(new QAction(KisIconUtils::loadIcon("document-export"),
- i18n("Export current palette to file")));
- m_ui->setupUi(this);
- m_ui->bnAdd->setDefaultAction(m_d->actAdd.data());
- m_ui->bnRemove->setDefaultAction(m_d->actRemove.data());
- m_ui->bnImport->setDefaultAction(m_d->actImport.data());
- m_ui->bnExport->setDefaultAction(m_d->actExport.data());
-
- m_ui->bnAdd->setEnabled(false);
- m_ui->bnRemove->setEnabled(false);
- m_ui->bnImport->setEnabled(false);
- m_ui->bnExport->setEnabled(false);
-
- connect(m_d->actAdd.data(), SIGNAL(triggered()), SLOT(slotAdd()));
- connect(m_d->actRemove.data(), SIGNAL(triggered()), SLOT(slotRemove()));
- connect(m_d->actImport.data(), SIGNAL(triggered()), SLOT(slotImport()));
- connect(m_d->actExport.data(), SIGNAL(triggered()), SLOT(slotExport()));
-
- m_d->itemChooser->setItemDelegate(m_d->delegate.data());
- m_d->itemChooser->setRowHeight(40);
- m_d->itemChooser->setColumnCount(1);
- m_d->itemChooser->showButtons(false);
- m_d->itemChooser->showTaggingBar(true);
- m_ui->viewPalette->setLayout(new QHBoxLayout(m_ui->viewPalette));
- m_ui->viewPalette->layout()->addWidget(m_d->itemChooser.data());
-
- connect(m_d->itemChooser.data(), SIGNAL(resourceSelected(KoResource*)), SLOT(slotPaletteResourceSelected(KoResource*)));
-}
-
-KisPaletteListWidget::~KisPaletteListWidget()
-{ }
-
-void KisPaletteListWidget::slotPaletteResourceSelected(KoResource *r)
-{
- KoColorSet *g = static_cast<KoColorSet*>(r);
- emit sigPaletteSelected(g);
- if (!m_d->allowModification) { return; }
- if (g->isEditable()) {
- m_ui->bnRemove->setEnabled(true);
- } else {
- m_ui->bnRemove->setEnabled(false);
- }
-}
-
-void KisPaletteListWidget::slotAdd()
-{
- if (!m_d->allowModification) { return; }
- emit sigAddPalette();
- m_d->itemChooser->setCurrentItem(m_d->rAdapter->resources().size() - 1, 0);
-}
-
-void KisPaletteListWidget::slotRemove()
-{
- if (!m_d->allowModification) { return; }
- if (m_d->itemChooser->currentResource()) {
- KoColorSet *cs = static_cast<KoColorSet*>(m_d->itemChooser->currentResource());
- emit sigRemovePalette(cs);
- }
- m_d->itemChooser->setCurrentItem(0, 0);
-}
-
-void KisPaletteListWidget::slotImport()
-{
- if (!m_d->allowModification) { return; }
- emit sigImportPalette();
- m_d->itemChooser->setCurrentItem(m_d->rAdapter->resources().size()-1, 0);
-}
-
-void KisPaletteListWidget::slotExport()
-{
- if (!m_d->allowModification) { return; }
- emit sigExportPalette(static_cast<KoColorSet*>(m_d->itemChooser->currentResource()));
-}
-
-void KisPaletteListWidget::setAllowModification(bool allowModification)
-{
- m_d->allowModification = allowModification;
- m_ui->bnAdd->setEnabled(allowModification);
- m_ui->bnImport->setEnabled(allowModification);
- m_ui->bnExport->setEnabled(allowModification);
- KoColorSet *cs = static_cast<KoColorSet*>(m_d->itemChooser->currentResource());
- m_ui->bnRemove->setEnabled(allowModification && cs && cs->isEditable());
-}
-
-/************************* KisPaletteListWidgetPrivate **********************/
-
-KisPaletteListWidgetPrivate::KisPaletteListWidgetPrivate(KisPaletteListWidget *a_c)
- : c(a_c)
- , rAdapter(new KoResourceServerAdapter<KoColorSet>(KoResourceServerProvider::instance()->paletteServer()))
- , itemChooser(new KoResourceItemChooser(rAdapter, a_c))
- , delegate(new Delegate(a_c))
-{ }
-
-KisPaletteListWidgetPrivate::~KisPaletteListWidgetPrivate()
-{ }
-
-/******************* KisPaletteListWidgetPrivate::Delegate ******************/
-
-KisPaletteListWidgetPrivate::Delegate::Delegate(QObject *parent)
- : QAbstractItemDelegate(parent)
-{ }
-
-KisPaletteListWidgetPrivate::Delegate::~Delegate()
-{ }
-
-void KisPaletteListWidgetPrivate::Delegate::paint(QPainter * painter,
- const QStyleOptionViewItem & option,
- const QModelIndex & index) const
-{
- painter->save();
- if (!index.isValid())
- return;
-
- KoResource* resource = static_cast<KoResource*>(index.internalPointer());
- KoColorSet* colorSet = static_cast<KoColorSet*>(resource);
-
- QRect previewRect(option.rect.x() + 2,
- option.rect.y() + 2,
- option.rect.height() - 4,
- option.rect.height() - 4);
-
- painter->drawImage(previewRect, colorSet->image());
-
- if (option.state & QStyle::State_Selected) {
- painter->fillRect(option.rect, option.palette.highlight());
- painter->drawImage(previewRect, colorSet->image());
- painter->setPen(option.palette.highlightedText().color());
- } else {
- painter->setBrush(option.palette.text().color());
- }
- QString drawnText = colorSet->name()
- + (colorSet->isEditable() ? "" : i18n(" [READONLY]"));
- painter->drawText(option.rect.x() + previewRect.width() + 10,
- option.rect.y() + painter->fontMetrics().ascent() + 5,
- drawnText);
-
- painter->restore();
-}
-
-inline QSize KisPaletteListWidgetPrivate::Delegate::sizeHint(const QStyleOptionViewItem & option,
- const QModelIndex &) const
-{
- return option.decorationSize;
-}
diff --git a/libs/widgets/KisPaletteListWidget.h b/libs/widgets/KisPaletteListWidget.h
deleted file mode 100644
index ffd5e76995..0000000000
--- a/libs/widgets/KisPaletteListWidget.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef KISPALETTELISTWIDGET_H
-#define KISPALETTELISTWIDGET_H
-
-#include <QString>
-#include <QWidget>
-#include <ui_WdgPaletteListWidget.h>
-
-#include "kritawidgets_export.h"
-
-class KoResource;
-class KoColorSet;
-
-struct KisPaletteListWidgetPrivate;
-
-class KRITAWIDGETS_EXPORT KisPaletteListWidget : public QWidget
-{
- Q_OBJECT
-public:
- explicit KisPaletteListWidget(QWidget *parent = nullptr);
- virtual ~KisPaletteListWidget();
-
-public:
- void setAllowModification(bool allowModification);
-
-Q_SIGNALS:
- void sigPaletteSelected(KoColorSet*);
- void sigAddPalette();
- void sigRemovePalette(KoColorSet *);
- void sigImportPalette();
- void sigExportPalette(KoColorSet *);
-
-public Q_SLOTS:
-
-private /* methods */:
- QString newPaletteFileName();
-
-private Q_SLOTS:
- void slotPaletteResourceSelected(KoResource *);
- void slotAdd();
- void slotRemove();
- void slotImport();
- void slotExport();
-
-private:
- QScopedPointer<Ui_WdgPaletteListWidget> m_ui;
- QScopedPointer<KisPaletteListWidgetPrivate> m_d;
-};
-
-#endif // KISPALETTELISTWIDGET_H
diff --git a/libs/widgets/KisPaletteListWidget_p.h b/libs/widgets/KisPaletteListWidget_p.h
deleted file mode 100644
index ef9d0c54a3..0000000000
--- a/libs/widgets/KisPaletteListWidget_p.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef KISPALETTELISTWIDGET_P_H
-#define KISPALETTELISTWIDGET_P_H
-
-#include <QAbstractItemDelegate>
-#include <QListView>
-#include <QAbstractListModel>
-#include <QPointer>
-#include <QCheckBox>
-#include <QAction>
-
-#include "KisPaletteListWidget.h"
-#include "KoResourceItemView.h"
-#include "KoResourceItemChooser.h"
-#include "KoResourceServer.h"
-#include "KoResourceServerAdapter.h"
-#include "KoResourceServerProvider.h"
-#include "KoColorSet.h"
-
-struct KisPaletteListWidgetPrivate
-{
- class View;
- class Delegate;
- class Model;
- KisPaletteListWidgetPrivate(KisPaletteListWidget *);
- virtual ~KisPaletteListWidgetPrivate();
-
- bool allowModification;
-
- QPointer<KisPaletteListWidget> c;
-
- QSharedPointer<KoResourceServerAdapter<KoColorSet> > rAdapter;
- QSharedPointer<KoResourceItemChooser> itemChooser;
-
- QScopedPointer<Delegate> delegate;
-
- QScopedPointer<QAction> actAdd;
- QScopedPointer<QAction> actImport;
- QScopedPointer<QAction> actExport;
- QScopedPointer<QAction> actModify;
- QScopedPointer<QAction> actRemove;
-};
-
-class KisPaletteListWidgetPrivate::Delegate : public QAbstractItemDelegate
-{
-public:
- Delegate(QObject *);
- virtual ~Delegate();
- void paint(QPainter * painter,
- const QStyleOptionViewItem & option,
- const QModelIndex & index) const override;
- QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const override;
-};
-#endif // KISPALETTELISTWIDGET_P_H
diff --git a/libs/widgets/KisPaletteModel.cpp b/libs/widgets/KisPaletteModel.cpp
index f7a4526d01..8df5155288 100644
--- a/libs/widgets/KisPaletteModel.cpp
+++ b/libs/widgets/KisPaletteModel.cpp
@@ -1,511 +1,512 @@
/*
* 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>
+#include <KisResourceModel.h>
+#include <KisResourceModelProvider.h>
KisPaletteModel::KisPaletteModel(QObject* parent)
: QAbstractTableModel(parent)
, m_colorSet(0)
, 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);
if (m_rowGroupNameMap.isEmpty()) {
return {};
}
int groupNameRow = groupNameRowForRow(row);
KisSwatchGroup *group = m_colorSet->getGroup(m_rowGroupNameMap[groupNameRow]);
KIS_ASSERT_RECOVER_RETURN_VALUE(group,QModelIndex());
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)
+void KisPaletteModel::setPalette(KoColorSetSP palette)
{
beginResetModel();
m_colorSet = palette;
if (palette) {
resetGroupNameRows();
}
endResetModel();
emit sigPaletteChanged();
}
-KoColorSet* KisPaletteModel::colorSet() const
+KoColorSetSP 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();
- }
+ saveModification();
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());
+ 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();
- }
+ saveModification();
}
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();
- }
+ saveModification();
}
}
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();
- }
+ saveModification();
}
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()), Qt::UniqueConnection);
} else {
m_displayRenderer = KoDumbColorDisplayRenderer::instance();
}
}
+void KisPaletteModel::saveModification()
+{
+ qDebug() << "saving modification in palette model" << m_colorSet->filename() << m_colorSet->storageLocation();
+ KisResourceModel *model = KisResourceModelProvider::resourceModel(m_colorSet->resourceType().first);
+ model->updateResource(m_colorSet);
+}
+
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 46119a050d..3509753502 100644
--- a/libs/widgets/KisPaletteModel.h
+++ b/libs/widgets/KisPaletteModel.h
@@ -1,180 +1,182 @@
/*
* 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 = 0);
~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 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;
+ void setPalette(KoColorSetSP colorSet);
+ KoColorSetSP 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);
+ void saveModification();
+
private /* member variables */:
- QPointer<KoColorSet> m_colorSet;
+ QSharedPointer<KoColorSet> m_colorSet;
QPointer<const KoColorDisplayRendererInterface> m_displayRenderer;
QMap<int, QString> m_rowGroupNameMap;
friend class KisPaletteView;
};
#endif
diff --git a/libs/widgets/KoCheckerBoardPainter.cpp b/libs/widgets/KoCheckerBoardPainter.cpp
deleted file mode 100644
index 8b33f5f524..0000000000
--- a/libs/widgets/KoCheckerBoardPainter.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2008 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 "KoCheckerBoardPainter.h"
-#include <QPainter>
-
-KoCheckerBoardPainter::KoCheckerBoardPainter( int checkerSize )
-: m_checkerSize(checkerSize), m_lightColor( Qt::lightGray ), m_darkColor( Qt::darkGray )
-{
- createChecker();
-}
-
-void KoCheckerBoardPainter::setCheckerColors( const QColor &lightColor, const QColor &darkColor )
-{
- m_lightColor = lightColor;
- m_darkColor = darkColor;
- createChecker();
-}
-
-void KoCheckerBoardPainter::setCheckerSize( int checkerSize )
-{
- m_checkerSize = checkerSize;
- createChecker();
-}
-
-void KoCheckerBoardPainter::paint( QPainter &painter, const QRectF &rect ) const
-{
- painter.fillRect( rect, QBrush(m_checker));
-}
-
-void KoCheckerBoardPainter::createChecker()
-{
- m_checker = QPixmap( 2*m_checkerSize, 2*m_checkerSize );
- QPainter p(&m_checker);
- p.fillRect(0, 0, m_checkerSize, m_checkerSize, m_lightColor);
- p.fillRect(m_checkerSize, 0, m_checkerSize, m_checkerSize, m_darkColor);
- p.fillRect(0, m_checkerSize, m_checkerSize, m_checkerSize, m_darkColor);
- p.fillRect(m_checkerSize, m_checkerSize, m_checkerSize, m_checkerSize, m_lightColor);
- p.end();
-}
diff --git a/libs/widgets/KoCheckerBoardPainter.h b/libs/widgets/KoCheckerBoardPainter.h
deleted file mode 100644
index 7e02f7f98f..0000000000
--- a/libs/widgets/KoCheckerBoardPainter.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2008 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 KOCHECKERBOARDPAINTER_H
-#define KOCHECKERBOARDPAINTER_H
-
-#include <QPixmap>
-#include <QColor>
-#include "kritawidgets_export.h"
-
-class QPainter;
-
-class KRITAWIDGETS_EXPORT KoCheckerBoardPainter
-{
-public:
- explicit KoCheckerBoardPainter( int checkerSize );
- void setCheckerColors( const QColor &lightColor, const QColor &darkColor );
- void setCheckerSize( int checkerSize );
- void paint( QPainter &painter, const QRectF &rect ) const;
-
-private:
- void createChecker();
- int m_checkerSize;
- QPixmap m_checker;
- QColor m_lightColor;
- QColor m_darkColor;
-};
-
-#endif // KOCHECKERBOARDPAINTER_H
diff --git a/libs/widgets/KoColorPopupAction.cpp b/libs/widgets/KoColorPopupAction.cpp
index 0d5f9e30ce..349fd3ff59 100644
--- a/libs/widgets/KoColorPopupAction.cpp
+++ b/libs/widgets/KoColorPopupAction.cpp
@@ -1,247 +1,246 @@
/* This file is part of the KDE project
* Copyright (c) 2007 C. Boemann <cbo@boemann.dk>
* Copyright (C) 2007 Fredy Yanardi <fyanardi@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 "KoColorPopupAction.h"
#include "KoColorSetWidget.h"
#include "KoTriangleColorSelector.h"
#include "KoColorSlider.h"
#include "KoCheckerBoardPainter.h"
#include "KoResourceServer.h"
#include "KoResourceServerProvider.h"
#include <KoColorSpaceRegistry.h>
#include <KoColor.h>
#include <WidgetsDebug.h>
#include <klocalizedstring.h>
#include <QPainter>
#include <QWidgetAction>
#include <QMenu>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QToolButton>
-#include <QPointer>
class KoColorPopupAction::KoColorPopupActionPrivate
{
public:
KoColorPopupActionPrivate()
: colorSetWidget(0)
, colorChooser(0)
, opacitySlider(0)
, menu(0)
, checkerPainter(4)
, showFilter(true)
, applyMode(true)
, firstTime(true)
{}
~KoColorPopupActionPrivate()
{
delete menu;
}
KoColor currentColor;
KoColor buddyColor;
KoColorSetWidget *colorSetWidget;
KoTriangleColorSelector * colorChooser;
KoColorSlider * opacitySlider;
QMenu *menu;
KoCheckerBoardPainter checkerPainter;
bool showFilter;
bool applyMode;
bool firstTime;
};
KoColorPopupAction::KoColorPopupAction(QObject *parent)
: QAction(parent),
d(new KoColorPopupActionPrivate())
{
d->menu = new QMenu();
QWidget *widget = new QWidget(d->menu);
QWidgetAction *wdgAction = new QWidgetAction(d->menu);
d->colorSetWidget = new KoColorSetWidget(widget);
KoResourceServer<KoColorSet>* rServer = KoResourceServerProvider::instance()->paletteServer();
- QPointer<KoColorSet> defaultColorSet = rServer->resourceByName("Default");
- if (!defaultColorSet && rServer->resources().count() > 0) {
- defaultColorSet = rServer->resources().first();
+ KoColorSetSP defaultColorSet = rServer->resourceByName("Default");
+ if (!defaultColorSet && rServer->resourceCount() > 0) {
+ defaultColorSet = rServer->firstResource();
}
d->colorSetWidget->setColorSet(defaultColorSet);
d->colorChooser = new KoTriangleColorSelector( widget );
// prevent mouse release on color selector from closing popup
d->colorChooser->setAttribute( Qt::WA_NoMousePropagation );
d->opacitySlider = new KoColorSlider( Qt::Vertical, widget );
d->opacitySlider->setFixedWidth(25);
d->opacitySlider->setRange(0, 255);
d->opacitySlider->setValue(255);
d->opacitySlider->setToolTip( i18n( "Opacity" ) );
QGridLayout * layout = new QGridLayout( widget );
layout->addWidget( d->colorSetWidget, 0, 0, 1, -1 );
layout->addWidget( d->colorChooser, 1, 0 );
layout->addWidget( d->opacitySlider, 1, 1 );
layout->setMargin(4);
wdgAction->setDefaultWidget(widget);
d->menu->addAction(wdgAction);
setMenu(d->menu);
new QHBoxLayout(d->menu);
d->menu->layout()->addWidget(widget);
d->menu->layout()->setMargin(0);
connect(this, SIGNAL(triggered()), this, SLOT(emitColorChanged()));
connect(d->colorSetWidget, SIGNAL(colorChanged(KoColor,bool)),
this, SLOT(colorWasSelected(KoColor,bool)));
connect(d->colorChooser, SIGNAL(colorChanged(QColor)),
this, SLOT(colorWasEdited(QColor)));
connect(d->opacitySlider, SIGNAL(valueChanged(int)),
this, SLOT(opacityWasChanged(int)));
}
KoColorPopupAction::~KoColorPopupAction()
{
delete d;
}
void KoColorPopupAction::setCurrentColor( const KoColor &color )
{
KoColor minColor( color );
d->currentColor = minColor;
d->colorChooser->blockSignals(true);
d->colorChooser->slotSetColor(color);
d->colorChooser->blockSignals(false);
KoColor maxColor( color );
minColor.setOpacity( OPACITY_TRANSPARENT_U8 );
maxColor.setOpacity( OPACITY_OPAQUE_U8 );
d->opacitySlider->blockSignals( true );
d->opacitySlider->setColors( minColor, maxColor );
d->opacitySlider->setValue( color.opacityU8() );
d->opacitySlider->blockSignals( false );
updateIcon();
}
void KoColorPopupAction::setCurrentColor( const QColor &_color )
{
#ifndef NDEBUG
if (!_color.isValid()) {
warnWidgets << "Invalid color given, defaulting to black";
}
#endif
const QColor color(_color.isValid() ? _color : QColor(0,0,0,255));
setCurrentColor(KoColor(color, KoColorSpaceRegistry::instance()->rgb8() ));
}
QColor KoColorPopupAction::currentColor() const
{
return d->currentColor.toQColor();
}
KoColor KoColorPopupAction::currentKoColor() const
{
return d->currentColor;
}
void KoColorPopupAction::updateIcon()
{
QSize iconSize;
QToolButton *toolButton = dynamic_cast<QToolButton*>(parentWidget());
if (toolButton) {
iconSize = QSize(toolButton->iconSize());
} else {
iconSize = QSize(16, 16);
}
// This must be a QImage, as drawing to a QPixmap outside the
// UI thread will cause sporadic crashes.
QImage pm;
if (icon().isNull()) {
d->applyMode = false;
}
if(d->applyMode) {
pm = icon().pixmap(iconSize).toImage();
if (pm.isNull()) {
pm = QImage(iconSize, QImage::Format_ARGB32_Premultiplied);
pm.fill(Qt::transparent);
}
QPainter p(&pm);
p.fillRect(0, iconSize.height() - 4, iconSize.width(), 4, d->currentColor.toQColor());
p.end();
} else {
pm = QImage(iconSize, QImage::Format_ARGB32_Premultiplied);
pm.fill(Qt::transparent);
QPainter p(&pm);
d->checkerPainter.paint(p, QRect(QPoint(),iconSize));
p.fillRect(0, 0, iconSize.width(), iconSize.height(), d->currentColor.toQColor());
p.end();
}
setIcon(QIcon(QPixmap::fromImage(pm)));
}
void KoColorPopupAction::emitColorChanged()
{
emit colorChanged( d->currentColor );
}
void KoColorPopupAction::colorWasSelected(const KoColor &color, bool final)
{
d->currentColor = color;
if (final) {
menu()->hide();
emitColorChanged();
}
updateIcon();
}
void KoColorPopupAction::colorWasEdited( const QColor &color )
{
d->currentColor = KoColor( color, KoColorSpaceRegistry::instance()->rgb8() );
quint8 opacity = d->opacitySlider->value();
d->currentColor.setOpacity( opacity );
KoColor minColor = d->currentColor;
minColor.setOpacity( OPACITY_TRANSPARENT_U8 );
KoColor maxColor = minColor;
maxColor.setOpacity( OPACITY_OPAQUE_U8 );
d->opacitySlider->setColors( minColor, maxColor );
emitColorChanged();
updateIcon();
}
void KoColorPopupAction::opacityWasChanged( int opacity )
{
d->currentColor.setOpacity( quint8(opacity) );
emitColorChanged();
}
diff --git a/libs/widgets/KoColorSetWidget.cpp b/libs/widgets/KoColorSetWidget.cpp
index b57c56019c..e1191e599e 100644
--- a/libs/widgets/KoColorSetWidget.cpp
+++ b/libs/widgets/KoColorSetWidget.cpp
@@ -1,217 +1,212 @@
/* This file is part of the KDE project
Copyright (c) 2007, 2012 C. Boemann <cbo@boemann.dk>
Copyright (c) 2007-2008 Fredy Yanardi <fyanardi@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 "KoColorSetWidget.h"
#include "KoColorSetWidget_p.h"
#include <QApplication>
#include <QSize>
#include <QToolButton>
#include <QHBoxLayout>
#include <QCheckBox>
#include <QFrame>
#include <QLabel>
#include <QMouseEvent>
#include <QMenu>
#include <QWidgetAction>
#include <QDir>
-#include <QPointer>
#include <QScrollArea>
#include <QGroupBox>
#include <QVBoxLayout>
#include <klocalizedstring.h>
#include <ksharedconfig.h>
#include <resources/KoColorSet.h>
#include <KoColorPatch.h>
#include <KoColorSpaceRegistry.h>
#include <KoResourceServer.h>
#include <KoResourceServerProvider.h>
-#include <KoResourceServerAdapter.h>
#include <kis_palette_view.h>
#include <KisPaletteDelegate.h>
#include <KisPaletteModel.h>
#include <kis_icon_utils.h>
void KoColorSetWidget::KoColorSetWidgetPrivate::addRecent(const KoColor &color)
{
if(numRecents < 6) {
recentPatches[numRecents] = new KoColorPatch(thePublic);
recentPatches[numRecents]->setFrameShape(QFrame::StyledPanel);
recentPatches[numRecents]->setDisplayRenderer(displayRenderer);
recentsLayout->insertWidget(numRecents + 1, recentPatches[numRecents]);
connect(recentPatches[numRecents], SIGNAL(triggered(KoColorPatch*)), thePublic, SLOT(slotPatchTriggered(KoColorPatch*)));
numRecents++;
}
// shift colors to the right
for (int i = numRecents- 1; i >0; i--) {
recentPatches[i]->setColor(recentPatches[i-1]->color());
}
//Finally set the recent color
recentPatches[0]->setColor(color);
}
void KoColorSetWidget::KoColorSetWidgetPrivate::activateRecent(int i)
{
KoColor color = recentPatches[i]->color();
while (i >0) {
recentPatches[i]->setColor(recentPatches[i-1]->color());
i--;
}
recentPatches[0]->setColor(color);
}
KoColorSetWidget::KoColorSetWidget(QWidget *parent)
: QFrame(parent)
, d(new KoColorSetWidgetPrivate())
{
d->thePublic = this;
d->numRecents = 0;
d->recentsLayout = new QHBoxLayout;
d->recentsLayout->setMargin(0);
d->recentsLayout->addWidget(new QLabel(i18n("Recent:")));
d->recentsLayout->addStretch(1);
KoColor color(KoColorSpaceRegistry::instance()->rgb8());
color.fromQColor(QColor(128,0,0));
d->addRecent(color);
d->paletteView = new KisPaletteView(this);
KisPaletteModel *paletteModel = new KisPaletteModel(d->paletteView);
d->paletteView->setPaletteModel(paletteModel);
d->paletteView->setDisplayRenderer(d->displayRenderer);
- d->paletteChooser = new KisPaletteListWidget(this);
+ d->paletteChooser = new KisPaletteChooser(this);
d->paletteChooserButton = new KisPopupButton(this);
d->paletteChooserButton->setPopupWidget(d->paletteChooser);
d->paletteChooserButton->setIcon(KisIconUtils::loadIcon("hi16-palette_library"));
d->paletteChooserButton->setToolTip(i18n("Choose palette"));
d->colorNameCmb = new KisPaletteComboBox(this);
d->colorNameCmb->setCompanionView(d->paletteView);
d->bottomLayout = new QHBoxLayout;
d->bottomLayout->addWidget(d->paletteChooserButton);
d->bottomLayout->addWidget(d->colorNameCmb);
d->bottomLayout->setStretch(0, 0); // minimize chooser button
d->bottomLayout->setStretch(1, 1); // maximize color name cmb
d->mainLayout = new QVBoxLayout(this);
d->mainLayout->setMargin(4);
d->mainLayout->setSpacing(2);
d->mainLayout->addLayout(d->recentsLayout);
d->mainLayout->addWidget(d->paletteView);
d->mainLayout->addLayout(d->bottomLayout);
setLayout(d->mainLayout);
- connect(d->paletteChooser, SIGNAL(sigPaletteSelected(KoColorSet*)),
- SLOT(slotPaletteChoosen(KoColorSet*)));
- connect(d->paletteView, SIGNAL(sigColorSelected(KoColor)),
- SLOT(slotColorSelectedByPalette(KoColor)));
- connect(d->colorNameCmb, SIGNAL(sigColorSelected(KoColor)),
- SLOT(slotNameListSelection(KoColor)));
+ connect(d->paletteChooser, SIGNAL(sigPaletteSelected(KoColorSetSP)), SLOT(slotPaletteChoosen(KoColorSetSP)));
+ connect(d->paletteView, SIGNAL(sigColorSelected(KoColor)), SLOT(slotColorSelectedByPalette(KoColor)));
+ connect(d->colorNameCmb, SIGNAL(sigColorSelected(KoColor)), SLOT(slotNameListSelection(KoColor)));
d->rServer = KoResourceServerProvider::instance()->paletteServer();
- QPointer<KoColorSet> defaultColorSet = d->rServer->resourceByName("Default");
- if (!defaultColorSet && d->rServer->resources().count() > 0) {
- defaultColorSet = d->rServer->resources().first();
+ KoColorSetSP defaultColorSet = d->rServer->resourceByName("Default");
+ if (!defaultColorSet && d->rServer->resourceCount() > 0) {
+ defaultColorSet = d->rServer->firstResource();
}
setColorSet(defaultColorSet);
}
KoColorSetWidget::~KoColorSetWidget()
{
delete d;
}
-void KoColorSetWidget::setColorSet(QPointer<KoColorSet> colorSet)
+void KoColorSetWidget::setColorSet(KoColorSetSP colorSet)
{
if (!colorSet) return;
if (colorSet == d->colorSet) return;
- d->paletteView->paletteModel()->setPalette(colorSet.data());
+ d->paletteView->paletteModel()->setPalette(colorSet);
d->colorSet = colorSet;
}
-KoColorSet* KoColorSetWidget::colorSet()
+KoColorSetSP KoColorSetWidget::colorSet()
{
return d->colorSet;
}
void KoColorSetWidget::setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer)
{
if (displayRenderer) {
d->displayRenderer = displayRenderer;
for (int i=0; i<6; i++) {
if (d->recentPatches[i]) {
d->recentPatches[i]->setDisplayRenderer(displayRenderer);
}
}
}
}
void KoColorSetWidget::resizeEvent(QResizeEvent *event)
{
emit widgetSizeChanged(event->size());
QFrame::resizeEvent(event);
}
void KoColorSetWidget::slotColorSelectedByPalette(const KoColor &color)
{
emit colorChanged(color, true);
d->addRecent(color);
}
void KoColorSetWidget::slotPatchTriggered(KoColorPatch *patch)
{
emit colorChanged(patch->color(), true);
int i;
for (i = 0; i < d->numRecents; i++) {
if(patch == d->recentPatches[i]) {
d->activateRecent(i);
break;
}
}
if (i == d->numRecents) { // we didn't find it above
d->addRecent(patch->color());
}
}
-void KoColorSetWidget::slotPaletteChoosen(KoColorSet *colorSet)
+void KoColorSetWidget::slotPaletteChoosen(KoColorSetSP colorSet)
{
d->colorSet = colorSet;
d->paletteView->paletteModel()->setPalette(colorSet);
}
void KoColorSetWidget::slotNameListSelection(const KoColor &color)
{
emit colorChanged(color, true);
}
//have to include this because of Q_PRIVATE_SLOT
#include "moc_KoColorSetWidget.cpp"
diff --git a/libs/widgets/KoColorSetWidget.h b/libs/widgets/KoColorSetWidget.h
index fa920f5dad..a0174be89e 100644
--- a/libs/widgets/KoColorSetWidget.h
+++ b/libs/widgets/KoColorSetWidget.h
@@ -1,120 +1,121 @@
/* This file is part of the KDE project
Copyright (c) 2007 C. Boemann <cbo@boemann.dk>
Copyright (c) 2007 Fredy Yanardi <fyanardi@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 KOCOLORSETWIDGET_H_
#define KOCOLORSETWIDGET_H_
#include <QWidgetAction>
#include <QFrame>
#include <KisSwatch.h>
+#include <KoColorSet.h>
+
#include <KoColorDisplayRendererInterface.h>
#include "kritawidgets_export.h"
class KoColor;
-class KoColorSet;
class KoColorPatch;
/**
* @short A colormanaged widget for choosing a color from a colorset
*
* KoColorSetWidget is a widget for choosing a color (colormanaged via pigment). It shows a color
* set plus optionally a checkbox to filter away bad matching colors.
*/
class KRITAWIDGETS_EXPORT KoColorSetWidget : public QFrame
{
Q_OBJECT
public:
/**
* Constructor for the widget, where color is initially blackpoint of sRGB
*
* @param parent parent QWidget
*/
explicit KoColorSetWidget(QWidget *parent=0);
/**
* Destructor
*/
~KoColorSetWidget() override;
/**
* Sets the color set that this widget shows.
* @param colorSet pointer to the color set
*/
- void setColorSet(QPointer<KoColorSet> colorSet);
+ void setColorSet(KoColorSetSP colorSet);
/**
* @brief setDisplayRenderer
* Set the display renderer of this object.
* @param displayRenderer
*/
void setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer);
/**
* Gets the current color set
* @returns current color set,, 0 if none set
*/
- KoColorSet* colorSet();
+ KoColorSetSP colorSet();
protected:
void resizeEvent(QResizeEvent *event) override; ///< reimplemented from QFrame
Q_SIGNALS:
/**
* Emitted every time the color changes (by calling setColor() or
* by user interaction.
* @param color the new color
* @param final if the value is final (ie not produced by the pointer moving over around)
*/
void colorChanged(const KoColor &color, bool final);
/**
* Emitted every time the size of this widget changes because of new colorset with
* different number of colors is loaded. This is useful for KoColorSetAction to update
* correct size of the menu showing this widget.
* @param size the new size
*/
void widgetSizeChanged(const QSize &size);
private Q_SLOTS:
/**
* @brief slotPatchTriggered
* Triggered when a recent patch is triggered
*/
void slotPatchTriggered(KoColorPatch *);
/**
* @brief slotEntrySelected
* Triggered when a color is choose from the palette view
*/
void slotColorSelectedByPalette(const KoColor &color);
- void slotPaletteChoosen(KoColorSet *);
+ void slotPaletteChoosen(KoColorSetSP );
void slotNameListSelection(const KoColor &);
private:
class KoColorSetWidgetPrivate;
KoColorSetWidgetPrivate * const d;
};
#endif
diff --git a/libs/widgets/KoColorSetWidget_p.h b/libs/widgets/KoColorSetWidget_p.h
index 4c38123401..aaf99faf16 100644
--- a/libs/widgets/KoColorSetWidget_p.h
+++ b/libs/widgets/KoColorSetWidget_p.h
@@ -1,81 +1,80 @@
/* This file is part of the KDE project
Copyright (c) 2007, 2012 C. Boemann <cbo@boemann.dk>
Copyright (c) 2007-2008 Fredy Yanardi <fyanardi@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 KoColorSetWidget_p_h
#define KoColorSetWidget_p_h
#include "KoColorSetWidget.h"
#include <QTimer>
#include <QApplication>
#include <QSize>
#include <QToolButton>
#include <QHBoxLayout>
#include <QCheckBox>
#include <QFrame>
#include <QLabel>
#include <QMouseEvent>
#include <QMenu>
#include <QWidgetAction>
#include <QDir>
-#include <QPointer>
#include <QScrollArea>
#include <QComboBox>
#include <klocalizedstring.h>
#include <WidgetsDebug.h>
#include <KoResourceServer.h>
-#include <kis_popup_button.h>
-#include <KisPaletteListWidget.h>
+#include <KisPopupButton.h>
+#include <KisPaletteChooser.h>
#include <KisPaletteComboBox.h>
#include <resources/KoColorSet.h>
#include <KoColorDisplayRendererInterface.h>
class KoColorPatch;
class KisPaletteView;
class Q_DECL_HIDDEN KoColorSetWidget::KoColorSetWidgetPrivate {
public:
KoColorSetWidget *thePublic;
- QPointer<KoColorSet> colorSet;
+ KoColorSetSP colorSet;
KisPaletteView *paletteView;
- KisPaletteListWidget *paletteChooser;
+ KisPaletteChooser *paletteChooser;
KisPopupButton *paletteChooserButton;
QVBoxLayout *mainLayout;
QVBoxLayout *colorSetLayout;
QHBoxLayout *recentsLayout;
QHBoxLayout *bottomLayout;
KoColorPatch *recentPatches[6];
QToolButton *addRemoveButton;
KisPaletteComboBox *colorNameCmb;
int numRecents;
const KoColorDisplayRendererInterface *displayRenderer;
KoResourceServer<KoColorSet> *rServer;
void addRecent(const KoColor &);
void activateRecent(int i);
void addRemoveColors();
};
#endif
diff --git a/libs/widgets/KoIconToolTip.cpp b/libs/widgets/KoIconToolTip.cpp
deleted file mode 100644
index d48b6260f5..0000000000
--- a/libs/widgets/KoIconToolTip.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/* This file is part of the KDE project
- Copyright (c) 1999 Carsten Pfeiffer <pfeiffer@kde.org>
- Copyright (c) 2002 Igor Jansen <rm@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.
-*/
-
-#include "KoIconToolTip.h"
-
-#include <QTextDocument>
-#include <QUrl>
-
-#include <KoResourceModel.h>
-#include <klocalizedstring.h>
-
-// #include <WidgetsDebug.h>
-
-KoIconToolTip::KoIconToolTip()
-{
-}
-
-KoIconToolTip::~KoIconToolTip()
-{
-}
-
-QTextDocument *KoIconToolTip::createDocument( const QModelIndex &index )
-{
- QTextDocument *doc = new QTextDocument( this );
-
- QImage thumb = index.data( KoResourceModel::LargeThumbnailRole ).value<QImage>();
- doc->addResource( QTextDocument::ImageResource, QUrl( "data:thumbnail" ), thumb );
-
- QString name = index.data( Qt::DisplayRole ).toString();
-
- QString tags;
- QString tagsData = index.data( KoResourceModel::TagsRole ).toString();
- if (tagsData.length() > 0) {
- const QString list = QString( "<ul style=\"list-style-type: none; margin: 0px;\">%1</ul> ").arg(tagsData);
- tags = QString("<p><table><tr><td>%1:</td><td>%2</td></tr></table></p>").arg(i18n("Tags"), list);
- }
-
- const QString image = QString( "<center><img src=\"data:thumbnail\"></center>");
- const QString body = QString( "<h3 align=\"center\">%1</h3>%2%3" ).arg( name, image, tags );
- const QString html = QString( "<html><body>%1</body></html>" ).arg( body );
-
- doc->setHtml( html );
-
- const int margin = 16;
- doc->setTextWidth( qMin( doc->size().width() + 2 * margin, qreal(500.0) ) );
- doc->setDocumentMargin( margin );
- doc->setUseDesignMetrics( true );
-
- return doc;
-}
diff --git a/libs/widgets/KoIconToolTip.h b/libs/widgets/KoIconToolTip.h
deleted file mode 100644
index 35735f058a..0000000000
--- a/libs/widgets/KoIconToolTip.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* This file is part of the KDE project
- Copyright (c) 1999 Carsten Pfeiffer (pfeiffer@kde.org)
- Copyright (c) 2002 Igor Jansen (rm@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 KOICONTOOLTIP_H
-#define KOICONTOOLTIP_H
-
-#include "KoItemToolTip.h"
-
-#include "kritawidgets_export.h"
-
-class KRITAWIDGETS_EXPORT KoIconToolTip: public KoItemToolTip
-{
-
- public:
- KoIconToolTip();
- ~KoIconToolTip() override;
-
- protected:
- QTextDocument *createDocument( const QModelIndex &index ) override;
-
- private:
- typedef KoItemToolTip super;
-};
-
-#endif // KOICONTOOLTIP_H
diff --git a/libs/widgets/KoItemToolTip.h b/libs/widgets/KoItemToolTip.h
deleted file mode 100644
index 6c242d35fc..0000000000
--- a/libs/widgets/KoItemToolTip.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- 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 KO_ITEM_TOOLTIP_H
-#define KO_ITEM_TOOLTIP_H
-
-#include <QFrame>
-#include "kritawidgets_export.h"
-
-class QStyleOptionViewItem;
-class QModelIndex;
-class QTextDocument;
-
-/**
- * Base class for tooltips that can show extensive information about
- * the contents of the data pointed to by something that contains a
- * QModelIndex. Subclasses need to use this data to create a
- * QTextDocument that is formatted to provide the complete tooltip.
- *
- * (KoItemToolTip is currently used in kopainter/KoResourceChooser)
- */
-class KRITAWIDGETS_EXPORT KoItemToolTip : public QFrame
-{
- Q_OBJECT
-public:
- KoItemToolTip();
- ~KoItemToolTip() override;
- void showTip(QWidget *widget, const QPoint &pos, const QStyleOptionViewItem &option, const QModelIndex &index);
-
-protected:
-
- /**
- * Re-implement this to provide the actual tooltip contents.
- * For instance:
- * @code
- * QTextDocument *doc = new QTextDocument(this);
- *
- * QImage thumb = index.data(KoResourceModel::LargeThumbnailRole).value<QImage>();
- * doc->addResource(QTextDocument::ImageResource, QUrl("data:thumbnail"), thumb);
- *
- * QString name = index.data(Qt::DisplayRole).toString();
- *
- * const QString image = QString("<img src=\"data:thumbnail\">");
- * const QString body = QString("<h3 align=\"center\">%1</h3>").arg(name) + image;
- * const QString html = QString("<html><body>%1</body></html>").arg(body);
- *
- * doc->setHtml(html);
- * doc->setTextWidth(qMin(doc->size().width(), 500.0));
- *
- * return doc;
- * @endcode
- */
- virtual QTextDocument *createDocument(const QModelIndex &index) = 0;
-
-private:
- class Private;
- Private* const d;
-
- void updatePosition(QWidget *widget, const QPoint &pos, const QStyleOptionViewItem &option);
-
-public:
- QSize sizeHint() const override;
-
-protected:
- void paintEvent(QPaintEvent *e) override;
- void timerEvent(QTimerEvent *e) override;
- bool eventFilter(QObject *object, QEvent *event) override;
-};
-
-#endif
diff --git a/libs/widgets/KoResourceFiltering.cpp b/libs/widgets/KoResourceFiltering.cpp
deleted file mode 100644
index 03fd8b6f7f..0000000000
--- a/libs/widgets/KoResourceFiltering.cpp
+++ /dev/null
@@ -1,261 +0,0 @@
-/* This file is part of the KDE project
-
- 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
- */
-
-#include "KoResourceFiltering.h"
-
-#include "KoResourceServer.h"
-
-#include <QStringList>
-#include <QString>
-
-
-class Q_DECL_HIDDEN KoResourceFiltering::Private
-{
-public:
- Private()
- : isTag("\\[([\\w\\s]+)\\]")
- , isExactMatch("\"([\\w\\s]+)\"")
- , searchTokenizer("\\s*,+\\s*")
- , hasNewFilters(false)
- , name(true)
- , filename(true)
- , resourceServer(0)
- {}
- QRegExp isTag;
- QRegExp isExactMatch;
- QRegExp searchTokenizer;
- bool hasNewFilters;
- bool name,filename;
- KoResourceServerBase *resourceServer;
- QStringList tagSetFilenames;
- QStringList includedNames;
- QStringList excludedNames;
- QString currentTag;
-};
-
-KoResourceFiltering::KoResourceFiltering() : d(new Private())
-{}
-
-KoResourceFiltering::~KoResourceFiltering()
-{
- delete d;
-}
-
-void KoResourceFiltering::configure(int filterType, bool enable) {
- switch (filterType) {
- case 0:
- d->name=true;
- d->filename=enable;
- break;
- case 1:
- d->name=enable;
- break;
- case 2:
- d->filename=enable;
- break;
- }
-}
-
-void KoResourceFiltering::setChanged()
-{
- d->hasNewFilters = true;
-}
-
-void KoResourceFiltering::setTagSetFilenames(const QStringList& filenames)
-{
- d->tagSetFilenames = filenames;
- d->excludedNames.clear();
- d->includedNames.clear();
- setChanged();
-}
-
-bool KoResourceFiltering::matchesResource(const QStringList &filteredList,const QStringList &filterList) const
-{
- Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive;
- foreach (QString filter, filterList) {
- if (!filter.startsWith('"')) {
- foreach (QString filtered, filteredList) {
- if (filtered.contains(filter,sensitivity)) {
- return true;
- }
- }
- }
- else if (d->name) {
- filter.remove('"');
- if (!filteredList.at(0).compare(filter)) {
- return true;
- }
- }
- }
- return false;
-}
-
-void KoResourceFiltering::sanitizeExclusionList()
-{
- if(!d->includedNames.isEmpty()) {
-
- foreach (const QString &exclusion, d->excludedNames) {
- if (!excludeFilterIsValid(exclusion))
- d->excludedNames.removeAll(exclusion);
- }
- }
-}
-
-QStringList KoResourceFiltering::tokenizeSearchString(const QString& searchString) const
-{
- return searchString.split(d->searchTokenizer, QString::SkipEmptyParts);
-}
-
-void KoResourceFiltering::populateIncludeExcludeFilters(const QStringList& filteredNames)
-{
- foreach (QString name, filteredNames) {
- QStringList* target;
-
- if(name.startsWith('!')) {
- name.remove('!');
- target = &d->excludedNames;
- } else {
- target = &d->includedNames;
- }
-
- if(!name.isEmpty()) {
- if (name.startsWith('[')) {
- if (d->isTag.exactMatch(name) && d->resourceServer) {
- name = d->isTag.cap(1);
- (*target) += d->resourceServer->queryResources(name);
- }
- }
- else if (name.startsWith('"')) {
- if (d->isExactMatch.exactMatch(name)) {
- target->push_back(name);
- }
- }
- else {
- target->push_back(name);
- }
- }
- }
- sanitizeExclusionList();
-}
-
-bool KoResourceFiltering::hasFilters() const
-{
- return (!d->tagSetFilenames.isEmpty() || !d->includedNames.isEmpty() || !d->excludedNames.isEmpty());
-}
-
-bool KoResourceFiltering::filtersHaveChanged() const
-{
- return d->hasNewFilters;
-}
-
-void KoResourceFiltering::setFilters(const QString &searchString)
-{
- d->excludedNames.clear();
- d->includedNames.clear();
- QStringList filteredNames = tokenizeSearchString(searchString);
- populateIncludeExcludeFilters(filteredNames);
- setChanged();
-}
-
-bool KoResourceFiltering::presetMatchesSearch(KoResource * resource) const
-{
- QList<QString> filteredList;
-
- QString resourceFileName = resource->shortFilename();
- QString resourceName = resource->name();
-
- if (d->name) {
- filteredList.push_front(resourceName);
- }
-
- if (d->filename) {
- filteredList.push_back(resourceFileName);
- }
-
- if (matchesResource(filteredList,d->excludedNames)) {
- return false;
- }
-
- if (matchesResource(filteredList,d->includedNames)) {
- return true;
- }
-
- foreach (const QString &filter, d->tagSetFilenames) {
- if (!resourceFileName.compare(filter) || !resourceName.compare(filter)) {
- return true;
- }
- }
-
- return false;
-}
-
-void KoResourceFiltering::setInclusions(const QStringList &inclusions)
-{
- d->includedNames = inclusions;
- setChanged();
-}
-
-void KoResourceFiltering::setExclusions(const QStringList &exclusions)
-{
- d->excludedNames = exclusions;
- setChanged();
-}
-
-bool KoResourceFiltering::excludeFilterIsValid(const QString &exclusion)
-{
- Q_FOREACH (const QString &inclusion, d->includedNames) {
- if ((inclusion.startsWith(exclusion) && exclusion.size() <= inclusion.size())) {
- return false;
- }
- }
- return true;
-}
-
-QList< KoResource* > KoResourceFiltering::filterResources(QList< KoResource* > resources)
-{
-
- Q_FOREACH (KoResource* resource, resources) {
- if(!presetMatchesSearch(resource)) {
- resources.removeAll(resource);
- }
- }
- setDoneFiltering();
- return resources;
-}
-
-void KoResourceFiltering::setDoneFiltering()
-{
- d->hasNewFilters = false;
-}
-
-void KoResourceFiltering::rebuildCurrentTagFilenames()
-{
- d->tagSetFilenames = d->resourceServer->queryResources(d->currentTag);
-}
-
-void KoResourceFiltering::setCurrentTag(const QString& tagSet)
-{
- d->currentTag = tagSet;
- rebuildCurrentTagFilenames();
-}
-
-void KoResourceFiltering::setResourceServer(KoResourceServerBase* resourceServer)
-{
- d->resourceServer = resourceServer;
-}
diff --git a/libs/widgets/KoResourceFiltering.h b/libs/widgets/KoResourceFiltering.h
deleted file mode 100644
index 10f16d7869..0000000000
--- a/libs/widgets/KoResourceFiltering.h
+++ /dev/null
@@ -1,67 +0,0 @@
-
-/* This file is part of the KDE project
-
- 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 KORESOURCEFILTER_H
-#define KORESOURCEFILTER_H
-
-#include "kritawidgets_export.h"
-
-#include <QList>
-
-class KoResourceServerBase;
-class KoResource;
-class QStringList;
-class QString;
-
-class KRITAWIDGETS_EXPORT KoResourceFiltering
-{
-
-public:
- KoResourceFiltering();
- virtual ~KoResourceFiltering();
- void configure(int filterType, bool enable);
- bool hasFilters() const;
- bool filtersHaveChanged() const;
- void setTagSetFilenames(const QStringList& filenames);
- void setCurrentTag(const QString& tagSet);
- void rebuildCurrentTagFilenames();
- void setResourceServer(KoResourceServerBase *resourceServer);
- void setFilters(const QString& searchString);
- QList<KoResource*> filterResources(QList< KoResource* > resources);
- void setInclusions(const QStringList &inclusions);
- void setExclusions(const QStringList &exclusions);
-
-private:
-
- void setDoneFiltering();
- bool presetMatchesSearch(KoResource * resource) const;
- void setChanged();
- bool excludeFilterIsValid(const QString &exclusion);
- bool matchesResource(const QStringList& filtered,const QStringList &filterList) const;
- void populateIncludeExcludeFilters(const QStringList& filteredNames);
- void sanitizeExclusionList();
- QStringList tokenizeSearchString(const QString& searchString) const;
-
- class Private;
- Private * const d;
-
-};
-
-#endif // KORESOURCEFILTER_H
diff --git a/libs/widgets/KoResourceItemChooser.cpp b/libs/widgets/KoResourceItemChooser.cpp
deleted file mode 100644
index ec7220f2b2..0000000000
--- a/libs/widgets/KoResourceItemChooser.cpp
+++ /dev/null
@@ -1,579 +0,0 @@
-/* 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 "KoResourceItemChooser.h"
-
-#include <math.h>
-
-#include <QGridLayout>
-#include <QButtonGroup>
-#include <QHeaderView>
-#include <QAbstractProxyModel>
-#include <QLabel>
-#include <QScrollArea>
-#include <QImage>
-#include <QPixmap>
-#include <QPainter>
-#include <QSplitter>
-#include <QToolButton>
-#include <QWheelEvent>
-#include <QLineEdit>
-
-#include <klocalizedstring.h>
-
-#include <KoIcon.h>
-#include <KoFileDialog.h>
-#include <KisMimeDatabase.h>
-#include "KoResourceServerAdapter.h"
-#include "KoResourceItemView.h"
-#include "KoResourceItemDelegate.h"
-#include "KoResourceModel.h"
-#include <resources/KoResource.h>
-#include "KoResourceTaggingManager.h"
-#include "KoTagFilterWidget.h"
-#include "KoTagChooserWidget.h"
-#include "KoResourceItemChooserSync.h"
-#include "kis_assert.h"
-#include <KisKineticScroller.h>
-
-
-class Q_DECL_HIDDEN KoResourceItemChooser::Private
-{
-public:
- Private()
- : model(0)
- , view(0)
- , buttonGroup(0)
- , viewModeButton(0)
- , usePreview(false)
- , previewScroller(0)
- , previewLabel(0)
- , splitter(0)
- , tiledPreview(false)
- , grayscalePreview(false)
- , synced(false)
- , updatesBlocked(false)
- , savedResourceWhileReset(0)
- {}
- KoResourceModel *model;
- KoResourceTaggingManager *tagManager;
- KoResourceItemView *view;
- QButtonGroup *buttonGroup;
- QToolButton *viewModeButton;
-
- bool usePreview;
- QScrollArea *previewScroller;
- QLabel *previewLabel;
- QSplitter *splitter;
- QGridLayout *buttonLayout;
- bool tiledPreview;
- bool grayscalePreview;
- bool synced;
- bool updatesBlocked;
-
- KoResource *savedResourceWhileReset;
-
- QList<QAbstractButton*> customButtons;
-};
-
-KoResourceItemChooser::KoResourceItemChooser(QSharedPointer<KoAbstractResourceServerAdapter> resourceAdapter, QWidget *parent, bool usePreview)
- : QWidget(parent)
- , d(new Private())
-{
- Q_ASSERT(resourceAdapter);
-
- d->splitter = new QSplitter(this);
-
- d->model = new KoResourceModel(resourceAdapter, this);
- connect(d->model, SIGNAL(beforeResourcesLayoutReset(KoResource*)), SLOT(slotBeforeResourcesLayoutReset(KoResource*)));
- connect(d->model, SIGNAL(afterResourcesLayoutReset()), SLOT(slotAfterResourcesLayoutReset()));
-
- d->view = new KoResourceItemView(this);
- d->view->setObjectName("ResourceItemview");
- d->view->setModel(d->model);
- d->view->setItemDelegate(new KoResourceItemDelegate(this));
- d->view->setSelectionMode(QAbstractItemView::SingleSelection);
- d->view->viewport()->installEventFilter(this);
-
- connect(d->view, SIGNAL(currentResourceChanged(QModelIndex)), this, SLOT(activated(QModelIndex)));
- connect(d->view, SIGNAL(currentResourceClicked(QModelIndex)), this, SLOT(clicked(QModelIndex)));
- connect(d->view, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint)));
-
- connect(d->view, SIGNAL(sigSizeChanged()), this, SLOT(updateView()));
-
- d->splitter->addWidget(d->view);
- d->splitter->setStretchFactor(0, 2);
-
- d->usePreview = usePreview;
- if (d->usePreview) {
- d->previewScroller = new QScrollArea(this);
- d->previewScroller->setWidgetResizable(true);
- d->previewScroller->setBackgroundRole(QPalette::Dark);
- d->previewScroller->setVisible(true);
- d->previewScroller->setAlignment(Qt::AlignCenter);
- d->previewLabel = new QLabel(this);
- d->previewScroller->setWidget(d->previewLabel);
- d->splitter->addWidget(d->previewScroller);
-
- if (d->splitter->count() == 2) {
- d->splitter->setSizes(QList<int>() << 280 << 160);
- }
-
- QScroller* scroller = KisKineticScroller::createPreconfiguredScroller(d->previewScroller);
- if (scroller) {
- connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChanged(QScroller::State)));
- }
- }
-
- d->splitter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
- connect(d->splitter, SIGNAL(splitterMoved(int,int)), SIGNAL(splitterMoved()));
-
- d->buttonGroup = new QButtonGroup(this);
- d->buttonGroup->setExclusive(false);
-
- QGridLayout *layout = new QGridLayout(this);
-
- d->buttonLayout = new QGridLayout();
-
- importButton = new QPushButton(this);
-
- importButton->setToolTip(i18nc("@info:tooltip", "Import resource"));
- importButton->setEnabled(true);
- d->buttonGroup->addButton(importButton, Button_Import);
- d->buttonLayout->addWidget(importButton, 0, 0);
-
- deleteButton = new QPushButton(this);
- deleteButton->setToolTip(i18nc("@info:tooltip", "Delete resource"));
- deleteButton->setEnabled(false);
- d->buttonGroup->addButton(deleteButton, Button_Remove);
- d->buttonLayout->addWidget(deleteButton, 0, 1);
-
- connect(d->buttonGroup, SIGNAL(buttonClicked(int)), this, SLOT(slotButtonClicked(int)));
-
- d->buttonLayout->setColumnStretch(0, 1);
- d->buttonLayout->setColumnStretch(1, 1);
- d->buttonLayout->setColumnStretch(2, 2);
- d->buttonLayout->setSpacing(0);
- d->buttonLayout->setMargin(0);
-
- d->viewModeButton = new QToolButton(this);
- d->viewModeButton->setPopupMode(QToolButton::InstantPopup);
- d->viewModeButton->setVisible(false);
-
- d->tagManager = new KoResourceTaggingManager(d->model, this);
- connect(d->tagManager, SIGNAL(updateView()), this, SLOT(updateView()));
-
- layout->addWidget(d->tagManager->tagChooserWidget(), 0, 0);
- layout->addWidget(d->viewModeButton, 0, 1);
- layout->addWidget(d->splitter, 1, 0, 1, 2);
- layout->addWidget(d->tagManager->tagFilterWidget(), 2, 0, 1, 2);
- layout->addLayout(d->buttonLayout, 3, 0, 1, 2);
- layout->setMargin(0);
- layout->setSpacing(0);
-
- updateView();
-
- updateButtonState();
- showTaggingBar(false);
- activated(d->model->index(0, 0));
-}
-
-KoResourceItemChooser::~KoResourceItemChooser()
-{
- disconnect();
- delete d;
-}
-
-void KoResourceItemChooser::slotButtonClicked(int button)
-{
- if (button == Button_Import) {
- QString extensions = d->model->extensions();
- QStringList mimeTypes;
- Q_FOREACH(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->model->importResourceFile(filename);
- } else if (button == Button_Remove) {
- QModelIndex index = d->view->currentIndex();
- int row = index.row();
- int column = index.column();
- if (index.isValid()) {
-
- KoResource *resource = resourceFromModelIndex(index);
- if (resource) {
- d->model->removeResource(resource);
- }
- }
- if (column == 0) {
- int rowMin = --row;
- row = qBound(0, rowMin, row);
- }
- int columnMin = --column;
- column = qBound(0, columnMin, column);
- setCurrentItem(row, column);
- activated(d->model->index(row, column));
- }
- updateButtonState();
-}
-
-void KoResourceItemChooser::showButtons(bool show)
-{
- foreach (QAbstractButton * button, d->buttonGroup->buttons()) {
- show ? button->show() : button->hide();
- }
-
- Q_FOREACH (QAbstractButton *button, d->customButtons) {
- show ? button->show() : button->hide();
- }
-}
-
-void KoResourceItemChooser::addCustomButton(QAbstractButton *button, int cell)
-{
- d->buttonLayout->addWidget(button, 0, cell);
- d->buttonLayout->setColumnStretch(2, 1);
- d->buttonLayout->setColumnStretch(3, 1);
-}
-
-void KoResourceItemChooser::showTaggingBar(bool show)
-{
- d->tagManager->showTaggingBar(show);
-
-}
-
-void KoResourceItemChooser::setRowCount(int rowCount)
-{
- int resourceCount = d->model->resourcesCount();
- d->model->setColumnCount(static_cast<qreal>(resourceCount) / rowCount);
- //Force an update to get the right row height (in theory)
- QRect geometry = d->view->geometry();
- d->view->setViewMode(KoResourceItemView::FIXED_ROWS);
- d->view->setGeometry(geometry.adjusted(0, 0, 0, 1));
- d->view->setGeometry(geometry);
-}
-
-void KoResourceItemChooser::setColumnCount(int columnCount)
-{
- d->model->setColumnCount(columnCount);
-}
-
-void KoResourceItemChooser::setRowHeight(int rowHeight)
-{
- d->view->verticalHeader()->setDefaultSectionSize(rowHeight);
-}
-
-void KoResourceItemChooser::setColumnWidth(int columnWidth)
-{
- d->view->horizontalHeader()->setDefaultSectionSize(columnWidth);
-}
-
-void KoResourceItemChooser::setItemDelegate(QAbstractItemDelegate *delegate)
-{
- d->view->setItemDelegate(delegate);
-}
-
-KoResource *KoResourceItemChooser::currentResource() const
-{
- QModelIndex index = d->view->currentIndex();
- if (index.isValid()) {
- return resourceFromModelIndex(index);
- }
- return 0;
-}
-
-void KoResourceItemChooser::setCurrentResource(KoResource *resource)
-{
- // don't update if the change came from the same chooser
- if (d->updatesBlocked) {
- return;
- }
-
- QModelIndex index = d->model->indexFromResource(resource);
-
- d->view->setCurrentIndex(index);
- updatePreview(index.isValid() ? resource : 0);
-}
-
-void KoResourceItemChooser::slotBeforeResourcesLayoutReset(KoResource *activateAfterReset)
-{
- d->savedResourceWhileReset = activateAfterReset ? activateAfterReset : currentResource();
-}
-
-void KoResourceItemChooser::slotAfterResourcesLayoutReset()
-{
- if (d->savedResourceWhileReset) {
- this->blockSignals(true);
- setCurrentResource(d->savedResourceWhileReset);
- this->blockSignals(false);
- }
-}
-
-void KoResourceItemChooser::setPreviewOrientation(Qt::Orientation orientation)
-{
- d->splitter->setOrientation(orientation);
-}
-
-void KoResourceItemChooser::setPreviewTiled(bool tiled)
-{
- d->tiledPreview = tiled;
-}
-
-void KoResourceItemChooser::setGrayscalePreview(bool grayscale)
-{
- d->grayscalePreview = grayscale;
-}
-
-void KoResourceItemChooser::setCurrentItem(int row, int column)
-{
- QModelIndex index = d->model->index(row, column);
- if (!index.isValid())
- return;
-
- d->view->setCurrentIndex(index);
- if (index.isValid()) {
- updatePreview(resourceFromModelIndex(index));
- }
-}
-
-void KoResourceItemChooser::setProxyModel(QAbstractProxyModel *proxyModel)
-{
- proxyModel->setSourceModel(d->model);
- d->view->setModel(proxyModel);
-}
-
-void KoResourceItemChooser::activated(const QModelIndex &index)
-{
- if (!index.isValid()) return;
-
- KoResource *resource = 0;
-
- if (index.isValid()) {
- resource = resourceFromModelIndex(index);
- }
-
- KIS_SAFE_ASSERT_RECOVER (resource) {
- resource = currentResource();
- }
-
- if (resource) {
- d->updatesBlocked = true;
- emit resourceSelected(resource);
- d->updatesBlocked = false;
- updatePreview(resource);
- updateButtonState();
- }
-}
-
-void KoResourceItemChooser::clicked(const QModelIndex &index)
-{
- Q_UNUSED(index);
-
- KoResource *resource = currentResource();
- if (resource) {
- emit resourceClicked(resource);
- }
-}
-
-void KoResourceItemChooser::updateButtonState()
-{
- QAbstractButton *removeButton = d->buttonGroup->button(Button_Remove);
- if (! removeButton)
- return;
-
- KoResource *resource = currentResource();
- if (resource) {
- removeButton->setEnabled(!resource->permanent());
- return;
- }
- removeButton->setEnabled(false);
-}
-
-void KoResourceItemChooser::updatePreview(KoResource *resource)
-{
- if (!d->usePreview) return;
-
- if (!resource) {
- d->previewLabel->setPixmap(QPixmap());
- return;
- }
-
- QImage image = resource->image();
-
- if (image.format() != QImage::Format_RGB32 &&
- image.format() != QImage::Format_ARGB32 &&
- image.format() != QImage::Format_ARGB32_Premultiplied) {
-
- image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
- }
-
- if (d->tiledPreview) {
- int width = d->previewScroller->width() * 4;
- int height = d->previewScroller->height() * 4;
- QImage img(width, height, image.format());
- QPainter gc(&img);
- gc.fillRect(img.rect(), Qt::white);
- gc.setPen(Qt::NoPen);
- gc.setBrush(QBrush(image));
- gc.drawRect(img.rect());
- image = img;
- }
-
- // Only convert to grayscale if it is rgb. Otherwise, it's gray already.
- if (d->grayscalePreview && !image.isGrayscale()) {
-
- QRgb *pixel = reinterpret_cast<QRgb *>(image.bits());
- for (int row = 0; row < image.height(); ++row) {
- for (int col = 0; col < image.width(); ++col) {
- const QRgb currentPixel = pixel[row * image.width() + col];
- const int red = qRed(currentPixel);
- const int green = qGreen(currentPixel);
- const int blue = qBlue(currentPixel);
- const int grayValue = (red * 11 + green * 16 + blue * 5) / 32;
- pixel[row * image.width() + col] = qRgb(grayValue, grayValue, grayValue);
- }
- }
- }
- d->previewLabel->setPixmap(QPixmap::fromImage(image));
-
-}
-
-KoResource *KoResourceItemChooser::resourceFromModelIndex(const QModelIndex &index) const
-{
- if (!index.isValid())
- return 0;
-
- const QAbstractProxyModel *proxyModel = dynamic_cast<const QAbstractProxyModel *>(index.model());
- if (proxyModel) {
- //Get original model index, because proxy models destroy the internalPointer
- QModelIndex originalIndex = proxyModel->mapToSource(index);
- return static_cast<KoResource *>(originalIndex.internalPointer());
- }
-
- return static_cast<KoResource *>(index.internalPointer());
-}
-
-QSize KoResourceItemChooser::viewSize() const
-{
- return d->view->size();
-}
-
-KoResourceItemView *KoResourceItemChooser::itemView() const
-{
- return d->view;
-}
-
-void KoResourceItemChooser::contextMenuRequested(const QPoint &pos)
-{
- d->tagManager->contextMenuRequested(currentResource(), pos);
-}
-
-void KoResourceItemChooser::setViewModeButtonVisible(bool visible)
-{
- d->viewModeButton->setVisible(visible);
-}
-
-QToolButton *KoResourceItemChooser::viewModeButton() const
-{
- return d->viewModeButton;
-}
-
-void KoResourceItemChooser::setSynced(bool sync)
-{
- if (d->synced == sync)
- return;
-
- d->synced = sync;
- KoResourceItemChooserSync *chooserSync = KoResourceItemChooserSync::instance();
- if (sync) {
- connect(chooserSync, SIGNAL(baseLengthChanged(int)), SLOT(baseLengthChanged(int)));
- baseLengthChanged(chooserSync->baseLength());
- } else {
- chooserSync->disconnect(this);
- }
-}
-
-void KoResourceItemChooser::baseLengthChanged(int length)
-{
- if (d->synced) {
- int resourceCount = d->model->resourcesCount();
- int width = d->view->width();
- int maxColumns = width / length;
- int cols = width / (2 * length) + 1;
- while (cols <= maxColumns) {
- int size = width / cols;
- int rows = ceil(resourceCount / (double)cols);
- if (rows * size < (d->view->height())) {
- break;
- }
- cols++;
- }
- setColumnCount(cols);
- }
- d->view->updateView();
-}
-
-bool KoResourceItemChooser::eventFilter(QObject *object, QEvent *event)
-{
- if (d->synced && event->type() == QEvent::Wheel) {
- KoResourceItemChooserSync *chooserSync = KoResourceItemChooserSync::instance();
- QWheelEvent *qwheel = static_cast<QWheelEvent *>(event);
- if (qwheel->modifiers() & Qt::ControlModifier) {
-
- int degrees = qwheel->delta() / 8;
- int newBaseLength = chooserSync->baseLength() + degrees / 15 * 10;
- chooserSync->setBaseLength(newBaseLength);
- return true;
- }
- }
- return QObject::eventFilter(object, event);
-}
-
-void KoResourceItemChooser::resizeEvent(QResizeEvent *event)
-{
- QWidget::resizeEvent(event);
- updateView();
-}
-
-void KoResourceItemChooser::showEvent(QShowEvent *event)
-{
- QWidget::showEvent(event);
- updateView();
-}
-
-void KoResourceItemChooser::updateView()
-{
- if (d->synced) {
- KoResourceItemChooserSync *chooserSync = KoResourceItemChooserSync::instance();
- baseLengthChanged(chooserSync->baseLength());
- }
-
- /// helps to set icons here in case the theme is changed
- d->viewModeButton->setIcon(koIcon("view-choose"));
- importButton->setIcon(koIcon("document-open"));
- deleteButton->setIcon(koIcon("trash-empty"));
-}
diff --git a/libs/widgets/KoResourceItemChooser.h b/libs/widgets/KoResourceItemChooser.h
deleted file mode 100644
index de994e7c1c..0000000000
--- a/libs/widgets/KoResourceItemChooser.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/* 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) 2010 Boudewijn Rempt <boud@valdyas.org>
- 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.
-*/
-
-#ifndef KO_RESOURCE_ITEM_CHOOSER
-#define KO_RESOURCE_ITEM_CHOOSER
-
-#include <QWidget>
-#include <KisKineticScroller.h>
-
-#include "kritawidgets_export.h"
-#include <QPushButton>
-
-class QModelIndex;
-class QAbstractProxyModel;
-class QAbstractItemDelegate;
-class QAbstractButton;
-class QToolButton;
-class KoAbstractResourceServerAdapter;
-class KoResourceItemView;
-class KoResource;
-
-/**
- * A widget that contains a KoResourceChooser as well
- * as an import/export button
- */
-class KRITAWIDGETS_EXPORT KoResourceItemChooser : public QWidget
-{
- Q_OBJECT
-public:
- enum Buttons { Button_Import, Button_Remove };
-
- /// \p usePreview shows the aside preview with the resource's image
- explicit KoResourceItemChooser(QSharedPointer<KoAbstractResourceServerAdapter> resourceAdapter, QWidget *parent = 0, bool usePreview = false);
- ~KoResourceItemChooser() override;
-
- /// Sets number of columns in the view and causes the number of rows to be calculated accordingly
- void setColumnCount(int columnCount);
-
- /// Sets number of rows in the view and causes the number of columns to be calculated accordingly
- void setRowCount(int rowCount);
-
- /// Sets the height of the view rows
- void setRowHeight(int rowHeight);
-
- /// Sets the width of the view columns
- void setColumnWidth(int columnWidth);
-
- /// Sets a custom delegate for the view
- void setItemDelegate(QAbstractItemDelegate *delegate);
-
- /// Gets the currently selected resource
- /// @returns the selected resource, 0 is no resource is selected
- KoResource *currentResource() const;
-
- /// Sets the item representing the resource as selected
- void setCurrentResource(KoResource *resource);
-
- /**
- * Sets the selected resource, does nothing if there is no valid item
- * @param row row of the item
- * @param column column of the item
- */
- void setCurrentItem(int row, int column);
-
- void showButtons(bool show);
-
- void addCustomButton(QAbstractButton *button, int cell);
-
- /// determines whether the preview right or below the splitter
- void setPreviewOrientation(Qt::Orientation orientation);
- /// determines whether the preview should tile the resource's image or not
- void setPreviewTiled(bool tiled);
- /// shows the preview converted to grayscale
- void setGrayscalePreview(bool grayscale);
-
- /// sets the visibility of tagging KlineEdits.
- void showTaggingBar(bool show);
-
- ///Set a proxy model with will be used to filter the resources
- void setProxyModel(QAbstractProxyModel *proxyModel);
-
- QSize viewSize() const;
-
- KoResourceItemView *itemView() const;
-
- void setViewModeButtonVisible(bool visible);
- QToolButton *viewModeButton() const;
-
- void setSynced(bool sync);
-
- bool eventFilter(QObject *object, QEvent *event) override;
-
-Q_SIGNALS:
- /// Emitted when a resource was selected
- void resourceSelected(KoResource *resource);
- /// Emitted when an *already selected* resource is clicked
- /// again
- void resourceClicked(KoResource *resource);
- void splitterMoved();
-public Q_SLOTS:
- void slotButtonClicked(int button);
- void slotScrollerStateChanged(QScroller::State state){ KisKineticScroller::updateCursor(this, state); }
-
-private Q_SLOTS:
- void activated(const QModelIndex &index);
- void clicked(const QModelIndex &index);
- void contextMenuRequested(const QPoint &pos);
- void baseLengthChanged(int length);
- void updateView();
- void slotBeforeResourcesLayoutReset(KoResource *activateAfterReset);
- void slotAfterResourcesLayoutReset();
-
-protected:
- void showEvent(QShowEvent *event) override;
-
-private:
- void updateButtonState();
- void updatePreview(KoResource *resource);
-
-
-
- void resizeEvent(QResizeEvent *event) override;
-
- /// Resource for a given model index
- /// @returns the resource pointer, 0 is index not valid
- KoResource *resourceFromModelIndex(const QModelIndex &index) const;
-
- class Private;
- Private *const d;
-
- QPushButton *importButton;
- QPushButton *deleteButton;
-
-};
-
-#endif // KO_RESOURCE_ITEM_CHOOSER
diff --git a/libs/widgets/KoResourceItemChooserContextMenu.cpp b/libs/widgets/KoResourceItemChooserContextMenu.cpp
deleted file mode 100644
index 24ff410121..0000000000
--- a/libs/widgets/KoResourceItemChooserContextMenu.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-
-/* This file is part of the KDE project
- * 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 "KoResourceItemChooserContextMenu.h"
-
-#include <QDebug>
-#include <QLabel>
-#include <QGridLayout>
-
-#include <KoIcon.h>
-#include <klocalizedstring.h>
-
-#include <resources/KoResource.h>
-
-KoLineEditAction::KoLineEditAction(QObject* parent)
- : QWidgetAction(parent)
- , m_closeParentOnTrigger(false)
-{
- QWidget* pWidget = new QWidget (0);
- QHBoxLayout* pLayout = new QHBoxLayout();
- m_label = new QLabel(0);
- m_editBox = new QLineEdit(0);
- m_editBox->setClearButtonEnabled(true);
- m_AddButton = new QPushButton();
- m_AddButton->setIcon(koIcon("list-add"));
- pLayout->addWidget(m_label);
- pLayout->addWidget(m_editBox);
- pLayout->addWidget(m_AddButton);
- pWidget->setLayout(pLayout);
- setDefaultWidget(pWidget);
-
- connect (m_editBox, &QLineEdit::returnPressed, this, &KoLineEditAction::onTriggered);
- connect (m_AddButton, &QPushButton::clicked, this, &KoLineEditAction::onTriggered);
-}
-
-KoLineEditAction::~KoLineEditAction()
-{
-
-}
-
-void KoLineEditAction::setIcon(const QIcon &icon)
-{
- QPixmap pixmap = QPixmap(icon.pixmap(16,16));
- m_label->setPixmap(pixmap);
-}
-
-void KoLineEditAction::closeParentOnTrigger(bool closeParent)
-{
- m_closeParentOnTrigger = closeParent;
-}
-
-bool KoLineEditAction::closeParentOnTrigger()
-{
- return m_closeParentOnTrigger;
-}
-
-void KoLineEditAction::onTriggered()
-{
- if (! m_editBox->text().isEmpty()) {
- emit triggered( m_editBox->text());
- m_editBox->text().clear();
-
- if (m_closeParentOnTrigger) {
- this->parentWidget()->close();
- m_editBox->clearFocus();
- }
- }
-}
-
-void KoLineEditAction::setPlaceholderText(const QString& clickMessage)
-{
- m_editBox->setPlaceholderText(clickMessage);
-}
-
-void KoLineEditAction::setText(const QString& text)
-{
- m_editBox->setText(text);
-}
-
-void KoLineEditAction::setVisible(bool showAction)
-{
- QLayout* currentLayout = defaultWidget()->layout();
-
- this->QAction::setVisible(showAction);
-
- for(int i=0;i<currentLayout->count();i++) {
- currentLayout->itemAt(i)->widget()->setVisible(showAction);
- }
- defaultWidget()->setVisible(showAction);
-}
-
-ContextMenuExistingTagAction::ContextMenuExistingTagAction(KoResource* resource, QString tag, QObject* parent)
- : QAction(parent)
- , m_resource(resource)
- , m_tag(tag)
-{
- setText(tag);
- connect (this, SIGNAL(triggered()),
- this, SLOT(onTriggered()));
-}
-
-ContextMenuExistingTagAction::~ContextMenuExistingTagAction()
-{
-}
-
-void ContextMenuExistingTagAction::onTriggered()
-{
- emit triggered(m_resource, m_tag);
-}
-NewTagAction::~NewTagAction()
-{
-}
-
-NewTagAction::NewTagAction(KoResource* resource, QMenu* parent)
- :KoLineEditAction (parent)
-{
- m_resource = resource;
- setIcon(koIcon("document-new"));
- setPlaceholderText(i18n("New tag"));
- closeParentOnTrigger(true);
-
- connect (this, SIGNAL(triggered(QString)),
- this, SLOT(onTriggered(QString)));
-}
-
-void NewTagAction::onTriggered(const QString & tagName)
-{
- emit triggered(m_resource,tagName);
-}
-
-KoResourceItemChooserContextMenu::KoResourceItemChooserContextMenu(KoResource* resource,
- const QStringList& resourceTags,
- const QString& currentlySelectedTag,
- const QStringList& allTags)
-{
- QImage image = resource->image();
- QIcon icon(QPixmap::fromImage(image));
- QAction * label = new QAction(resource->name(), this);
- label->setIcon(icon);
-
- addAction(label);
-
- QMenu * removableTagsMenu;
- QMenu * assignableTagsMenu;
-
- QStringList removables = resourceTags;
- QStringList assignables = allTags;
-
- removables.sort();
- assignables.sort();
-
- assignableTagsMenu = addMenu(koIcon("list-add"),i18n("Assign to tag"));
-
- if (!removables.isEmpty()) {
- addSeparator();
- QString currentTag = currentlySelectedTag;
- if (removables.contains(currentTag)) {
- assignables.removeAll(currentTag);
- removables.removeAll(currentTag);
- ContextMenuExistingTagAction * removeTagAction = new ContextMenuExistingTagAction(resource, currentTag, this);
- removeTagAction->setText(i18n("Remove from this tag"));
- removeTagAction->setIcon(koIcon("list-remove"));
-
- connect(removeTagAction, SIGNAL(triggered(KoResource*,QString)),
- this, SIGNAL(resourceTagRemovalRequested(KoResource*,QString)));
- addAction(removeTagAction);
- }
- if (!removables.isEmpty()) {
- removableTagsMenu = addMenu(koIcon("list-remove"),i18n("Remove from other tag"));
- foreach (const QString &tag, removables) {
- assignables.removeAll(tag);
- ContextMenuExistingTagAction * removeTagAction = new ContextMenuExistingTagAction(resource, tag, this);
-
- connect(removeTagAction, SIGNAL(triggered(KoResource*,QString)),
- this, SIGNAL(resourceTagRemovalRequested(KoResource*,QString)));
- removableTagsMenu->addAction(removeTagAction);
- }
- }
- }
-
- foreach (const QString &tag, assignables) {
- ContextMenuExistingTagAction * addTagAction = new ContextMenuExistingTagAction(resource, tag, this);
-
- connect(addTagAction, SIGNAL(triggered(KoResource*,QString)),
- this, SIGNAL(resourceTagAdditionRequested(KoResource*,QString)));
- assignableTagsMenu->addAction(addTagAction);
- }
- assignableTagsMenu->addSeparator();
-
- NewTagAction * addTagAction = new NewTagAction(resource, this);
- connect(addTagAction, SIGNAL(triggered(KoResource*,QString)),
- this, SIGNAL(resourceAssignmentToNewTagRequested(KoResource*,QString)));
- assignableTagsMenu->addAction(addTagAction);
-}
-
-KoResourceItemChooserContextMenu::~KoResourceItemChooserContextMenu()
-{
-
-}
diff --git a/libs/widgets/KoResourceItemChooserContextMenu.h b/libs/widgets/KoResourceItemChooserContextMenu.h
deleted file mode 100644
index fc58380c6b..0000000000
--- a/libs/widgets/KoResourceItemChooserContextMenu.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * This file is part of the KDE project
- * 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.
- * */
-
-#ifndef KORESOURCEITEMCHOOSERCONTEXTMENU_H
-#define KORESOURCEITEMCHOOSERCONTEXTMENU_H
-
-#include <QMenu>
-#include <QWidgetAction>
-#include <QLabel>
-#include <QLineEdit>
-#include <QPushButton>
-
-class KoResource;
-
-
-class ContextMenuExistingTagAction : public QAction
-{
- Q_OBJECT
-public:
- explicit ContextMenuExistingTagAction( KoResource * resource, QString tag, QObject* parent = 0);
- ~ContextMenuExistingTagAction() override;
-
-Q_SIGNALS:
- void triggered(KoResource * resource, QString tag);
-
-protected Q_SLOTS:
- void onTriggered();
-
-private:
- KoResource * m_resource;
- QString m_tag;
-};
-
-/*!
- * A line edit QWidgetAction.
- * Default behavior: Closes its parent upon triggering.
- */
-class KoLineEditAction : public QWidgetAction
-{
- Q_OBJECT
-public:
- explicit KoLineEditAction(QObject* parent);
- ~KoLineEditAction() override;
- void setIcon(const QIcon &icon);
- void closeParentOnTrigger(bool closeParent);
- bool closeParentOnTrigger();
- void setPlaceholderText(const QString& clickMessage);
- void setText(const QString& text);
- void setVisible(bool showAction);
-
- Q_SIGNALS:
- void triggered(const QString &tag);
-
-protected Q_SLOTS:
- void onTriggered();
-
-private:
- bool m_closeParentOnTrigger;
- QLabel * m_label;
- QLineEdit * m_editBox;
- QPushButton * m_AddButton;
-};
-
-class NewTagAction : public KoLineEditAction
-{
- Q_OBJECT
-public:
- explicit NewTagAction (KoResource* resource, QMenu* parent);
- ~NewTagAction() override;
-
- Q_SIGNALS:
- void triggered(KoResource * resource, const QString &tag);
-
-protected Q_SLOTS:
- void onTriggered(const QString& tagName);
-
-private:
- KoResource * m_resource;
-};
-
-class KoResourceItemChooserContextMenu : public QMenu
-{
- Q_OBJECT
-public:
- explicit KoResourceItemChooserContextMenu
- (
- KoResource* resource,
- const QStringList& resourceTags,
- const QString& currentlySelectedTag,
- const QStringList& allTags
- );
- ~KoResourceItemChooserContextMenu() override;
-
-Q_SIGNALS:
- /// Emitted when a resource should be added to an existing tag.
- void resourceTagAdditionRequested(KoResource* resource, const QString& tag);
- /// Emitted when a resource should be removed from an existing tag.
- void resourceTagRemovalRequested(KoResource* resource, const QString& tag);
- /// Emitted when a resource should be added to a new tag, which will need to be created.
- void resourceAssignmentToNewTagRequested(KoResource* resource, const QString& tag);
-
-};
-
-#endif // KORESOURCEITEMCHOOSERCONTEXTMENU_H
diff --git a/libs/widgets/KoResourceItemChooserSync.cpp b/libs/widgets/KoResourceItemChooserSync.cpp
deleted file mode 100644
index 9885472c09..0000000000
--- a/libs/widgets/KoResourceItemChooserSync.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/* This file is part of the KDE project
-
- 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 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 "KoResourceItemChooserSync.h"
-
-#include <QGlobalStatic>
-
-Q_GLOBAL_STATIC(KoResourceItemChooserSync, s_instance)
-
-struct Q_DECL_HIDDEN KoResourceItemChooserSync::Private
-{
- int baseLength;
-};
-
-
-KoResourceItemChooserSync::KoResourceItemChooserSync()
- : d(new Private)
-{
- d->baseLength = 50;
-}
-
-KoResourceItemChooserSync::~KoResourceItemChooserSync()
-{
-}
-
-KoResourceItemChooserSync* KoResourceItemChooserSync::instance()
-{
- return s_instance;
-}
-
-int KoResourceItemChooserSync::baseLength()
-{
- return d->baseLength;
-}
-
-void KoResourceItemChooserSync::setBaseLength(int length)
-{
- d->baseLength = qBound(25, length, 100);
- emit baseLengthChanged(d->baseLength);
-}
-
-
-
-
-
-
-
diff --git a/libs/widgets/KoResourceItemChooserSync.h b/libs/widgets/KoResourceItemChooserSync.h
deleted file mode 100644
index e85e112324..0000000000
--- a/libs/widgets/KoResourceItemChooserSync.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* This file is part of the KDE project
-
- 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 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 KORESOURCEITEMCHOOSERSYNC_H
-#define KORESOURCEITEMCHOOSERSYNC_H
-
-#include <QObject>
-#include <QScopedPointer>
-
-#include "kritawidgets_export.h"
-
-/**
- * KoResourceItemChooserSync is a singleton that syncs the size of entries in the
- * resource item choosers between different choosers
- * To use the syncing it has to be turned on in the KoResourceItemChooser
- */
-class KRITAWIDGETS_EXPORT KoResourceItemChooserSync : public QObject
-{
- Q_OBJECT
-public:
- KoResourceItemChooserSync();
- ~KoResourceItemChooserSync() override;
- static KoResourceItemChooserSync* instance();
-
- /// Gets the base length
- /// @returns the base length of items
- int baseLength();
-
- /// Set the base length
- /// @param length base length for the items, will be clamped if outside range
- void setBaseLength(int length);
-
-Q_SIGNALS:
- /// Signal is emitted when the base length is changed and will trigger and update in
- /// the resource item choosers
- void baseLengthChanged(int length);
-
-private:
-
- KoResourceItemChooserSync(const KoResourceItemChooserSync&);
- KoResourceItemChooserSync operator=(const KoResourceItemChooserSync&);
-
-private:
- struct Private;
- const QScopedPointer<Private> d;
-};
-
-#endif // KORESOURCEITEMCHOOSERSYNC_H
diff --git a/libs/widgets/KoResourceItemDelegate.h b/libs/widgets/KoResourceItemDelegate.h
deleted file mode 100644
index 4580764bfb..0000000000
--- a/libs/widgets/KoResourceItemDelegate.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2008 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 KORESOURCEITEMDELEGATE_H
-#define KORESOURCEITEMDELEGATE_H
-
-#include <QAbstractItemDelegate>
-#include "KoCheckerBoardPainter.h"
-
-#include "kritawidgets_export.h"
-
-/// The resource item delegate for rendering the resource preview
-class KRITAWIDGETS_EXPORT KoResourceItemDelegate : public QAbstractItemDelegate
-{
-public:
- explicit KoResourceItemDelegate(QObject *parent = 0);
- ~KoResourceItemDelegate() override {}
- /// reimplemented
- void paint( QPainter *, const QStyleOptionViewItem &, const QModelIndex & ) const override;
- /// reimplemented
- QSize sizeHint ( const QStyleOptionViewItem &, const QModelIndex & ) const override;
-private:
- KoCheckerBoardPainter m_checkerPainter;
-};
-
-#endif // KORESOURCEITEMDELEGATE_H
diff --git a/libs/widgets/KoResourceItemView.cpp b/libs/widgets/KoResourceItemView.cpp
deleted file mode 100644
index 094ea05ec7..0000000000
--- a/libs/widgets/KoResourceItemView.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2008 Jan Hambrecht <jaham@gmx.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 "KoResourceItemView.h"
-#include <KisKineticScroller.h>
-#include <QEvent>
-#include <QScroller>
-#include <QHelpEvent>
-
-#include <QDebug>
-
-KoResourceItemView::KoResourceItemView(QWidget *parent)
- : KoTableView(parent)
-{
- connect(this, SIGNAL(clicked(QModelIndex)), SLOT(slotItemClicked(QModelIndex)));
-
- QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(this);
- if (scroller) {
- connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChange(QScroller::State)));
- }
-}
-
-bool KoResourceItemView::viewportEvent(QEvent *event)
-{
- if (event->type() == QEvent::ToolTip && model()) {
- QHelpEvent *he = static_cast<QHelpEvent *>(event);
- QStyleOptionViewItem option = viewOptions();
- QModelIndex index = model()->buddy(indexAt(he->pos()));
- if (index.isValid()) {
- option.rect = visualRect(index);
- m_tip.showTip(this, he->pos(), option, index);
- return true;
- }
- }
-
- return QTableView::viewportEvent(event);
-}
-
-
-void KoResourceItemView::selectionChanged(const QItemSelection &selected, const QItemSelection &/*deselected*/)
-{
- if (selected.isEmpty()) {
- emit currentResourceChanged(QModelIndex());
- } else {
- emit currentResourceChanged(selected.indexes().first());
- }
-}
-
-void KoResourceItemView::mousePressEvent(QMouseEvent *event)
-{
- m_beforeClickIndex = currentIndex();
- KoTableView::mousePressEvent(event);
-}
-
-void KoResourceItemView::slotItemClicked(const QModelIndex &index)
-{
- if (m_beforeClickIndex == index) {
- emit currentResourceClicked(index);
- }
-
- m_beforeClickIndex = QModelIndex();
-}
-
-void KoResourceItemView::contextMenuEvent(QContextMenuEvent *event)
-{
- QTableView::contextMenuEvent(event);
- emit contextMenuRequested(event->globalPos());
-}
diff --git a/libs/widgets/KoResourceItemView.h b/libs/widgets/KoResourceItemView.h
deleted file mode 100644
index a988bb6954..0000000000
--- a/libs/widgets/KoResourceItemView.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2008 Jan Hambrecht <jaham@gmx.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.
- */
-
-#ifndef KORESOURCEITEMVIEW_H
-#define KORESOURCEITEMVIEW_H
-
-#include <KoTableView.h>
-#include <KoIconToolTip.h>
-#include <QScroller>
-#include <KisKineticScroller.h>
-
-class QEvent;
-class QModelIndex;
-
-#include "kritawidgets_export.h"
-
-/// The resource view
-class KRITAWIDGETS_EXPORT KoResourceItemView : public KoTableView
-{
- Q_OBJECT
-
-public:
-
- explicit KoResourceItemView(QWidget *parent = 0);
- ~KoResourceItemView() override { disconnect(); }
-
- /// reimplemented
- bool viewportEvent(QEvent *event) override;
-
-Q_SIGNALS:
-
- void currentResourceChanged(const QModelIndex &);
- void currentResourceClicked(const QModelIndex &);
-
- void contextMenuRequested(const QPoint &);
-
-protected:
- void contextMenuEvent(QContextMenuEvent *event) override;
- void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
-
- void mousePressEvent(QMouseEvent *event) override;
-
-private Q_SLOTS:
- void slotItemClicked(const QModelIndex &index);
- void slotScrollerStateChange(QScroller::State state){ KisKineticScroller::updateCursor(this, state); }
-
-private:
- KoIconToolTip m_tip;
- QModelIndex m_beforeClickIndex;
-};
-
-#endif // KORESOURCEITEMVIEW_H
diff --git a/libs/widgets/KoResourceModel.cpp b/libs/widgets/KoResourceModel.cpp
deleted file mode 100644
index 0dde56b9c4..0000000000
--- a/libs/widgets/KoResourceModel.cpp
+++ /dev/null
@@ -1,327 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2008-2009 Jan Hambrecht <jaham@gmx.net>
- * 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 "KoResourceModel.h"
-
-#include <klocalizedstring.h>
-#include <kconfiggroup.h>
-#include <ksharedconfig.h>
-
-#include <KoResourceServerAdapter.h>
-#include <math.h>
-
-KoResourceModel::KoResourceModel(QSharedPointer<KoAbstractResourceServerAdapter> resourceAdapter, QObject * parent)
- : QAbstractTableModel(parent)
- , m_resourceAdapter(resourceAdapter)
- , m_columnCount(4)
-{
- Q_ASSERT(m_resourceAdapter);
- m_resourceAdapter->connectToResourceServer();
- connect(m_resourceAdapter.data(), SIGNAL(resourceAdded(KoResource*)),
- this, SLOT(resourceAdded(KoResource*)));
- connect(m_resourceAdapter.data(), SIGNAL(removingResource(KoResource*)),
- this, SLOT(resourceRemoved(KoResource*)));
- connect(m_resourceAdapter.data(), SIGNAL(resourceChanged(KoResource*)),
- this, SLOT(resourceChanged(KoResource*)));
- connect(m_resourceAdapter.data(), SIGNAL(tagsWereChanged()),
- this, SLOT(tagBoxEntryWasModified()));
- connect(m_resourceAdapter.data(), SIGNAL(tagCategoryWasAdded(QString)),
- this, SLOT(tagBoxEntryWasAdded(QString)));
- connect(m_resourceAdapter.data(), SIGNAL(tagCategoryWasRemoved(QString)),
- this, SLOT(tagBoxEntryWasRemoved(QString)));
-}
-
-KoResourceModel::~KoResourceModel()
-{
- if (!m_currentTag.isEmpty()) {
- KConfigGroup group = KSharedConfig::openConfig()->group("SelectedTags");
- group.writeEntry(serverType(), m_currentTag);
- }
-
-}
-
-int KoResourceModel::rowCount( const QModelIndex &/*parent*/ ) const
-{
- int resourceCount = m_resourceAdapter->resources().count();
- if (!resourceCount)
- return 0;
-
- return static_cast<int>(ceil(static_cast<qreal>(resourceCount) / m_columnCount));
-}
-
-int KoResourceModel::columnCount ( const QModelIndex & ) const
-{
- return m_columnCount;
-}
-
-QVariant KoResourceModel::data( const QModelIndex &index, int role ) const
-{
- if( ! index.isValid() )
- return QVariant();
-
- switch( role )
- {
- case Qt::DisplayRole:
- {
- KoResource * resource = static_cast<KoResource*>(index.internalPointer());
- if( ! resource )
- return QVariant();
- QString resName = i18n( resource->name().toUtf8().data());
-
- return QVariant( resName );
- }
- case KoResourceModel::TagsRole:
- {
- KoResource * resource = static_cast<KoResource*>(index.internalPointer());
- if( ! resource )
- return QVariant();
- if (m_resourceAdapter->assignedTagsList(resource).count()) {
- QString taglist = m_resourceAdapter->assignedTagsList(resource).join("</li><li>");
- return QString("<li>%2</li>").arg(taglist);
- } else {
- return QString();
- }
- }
- case Qt::DecorationRole:
- {
- KoResource * resource = static_cast<KoResource*>(index.internalPointer());
- if( ! resource )
- return QVariant();
-
- return QVariant( resource->image() );
- }
- case KoResourceModel::LargeThumbnailRole:
- {
- KoResource * resource = static_cast<KoResource*>(index.internalPointer());
- if( ! resource )
- return QVariant();
-
- QSize imageSize = resource->image().size();
- QSize thumbSize( 100, 100 );
- if(imageSize.height() > thumbSize.height() || imageSize.width() > thumbSize.width()) {
- qreal scaleW = static_cast<qreal>( thumbSize.width() ) / static_cast<qreal>( imageSize.width() );
- qreal scaleH = static_cast<qreal>( thumbSize.height() ) / static_cast<qreal>( imageSize.height() );
-
- qreal scale = qMin( scaleW, scaleH );
-
- int thumbW = static_cast<int>( imageSize.width() * scale );
- int thumbH = static_cast<int>( imageSize.height() * scale );
-
- return QVariant(resource->image().scaled( thumbW, thumbH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
- }
- else
- return QVariant(resource->image());
- }
-
- default:
- return QVariant();
- }
-}
-
-QModelIndex KoResourceModel::index ( int row, int column, const QModelIndex & ) const
-{
- int index = row * m_columnCount + column;
- const QList<KoResource*> resources = m_resourceAdapter->resources();
- if( index >= resources.count() || index < 0)
- return QModelIndex();
-
- return createIndex( row, column, resources[index] );
-}
-
-void KoResourceModel::doSafeLayoutReset(KoResource *activateAfterReformat)
-{
- emit beforeResourcesLayoutReset(activateAfterReformat);
- beginResetModel();
- endResetModel();
- emit afterResourcesLayoutReset();
-}
-
-void KoResourceModel::setColumnCount( int columnCount )
-{
- if (columnCount != m_columnCount) {
- emit beforeResourcesLayoutReset(0);
- m_columnCount = columnCount;
- beginResetModel();
- endResetModel();
- emit afterResourcesLayoutReset();
- }
-}
-
-void KoResourceModel::resourceAdded(KoResource *resource)
-{
- int newIndex = m_resourceAdapter->resources().indexOf(resource);
- if (newIndex >= 0) {
- doSafeLayoutReset(0);
- }
-}
-
-void KoResourceModel::resourceRemoved(KoResource *resource)
-{
- Q_UNUSED(resource);
- doSafeLayoutReset(0);
-}
-
-void KoResourceModel::resourceChanged(KoResource* resource)
-{
- int resourceIndex = m_resourceAdapter->resources().indexOf(resource);
- int row = resourceIndex / columnCount();
- int column = resourceIndex % columnCount();
-
- QModelIndex modelIndex = index(row, column);
- if (!modelIndex.isValid()) {
- return;
- }
-
- emit dataChanged(modelIndex, modelIndex);
-}
-
-void KoResourceModel::tagBoxEntryWasModified()
-{
- m_resourceAdapter->updateServer();
- emit tagBoxEntryModified();
-}
-
-void KoResourceModel::tagBoxEntryWasAdded(const QString& tag)
-{
- emit tagBoxEntryAdded(tag);
-}
-
-void KoResourceModel::tagBoxEntryWasRemoved(const QString& tag)
-{
- emit tagBoxEntryRemoved(tag);
-}
-
-QModelIndex KoResourceModel::indexFromResource(KoResource* resource) const
-{
- int resourceIndex = m_resourceAdapter->resources().indexOf(resource);
- if (columnCount() > 0) {
- int row = resourceIndex / columnCount();
- int column = resourceIndex % columnCount();
- return index(row, column);
- }
- return QModelIndex();
-}
-
-QString KoResourceModel::extensions() const
-{
- return m_resourceAdapter->extensions();
-}
-
-void KoResourceModel::importResourceFile(const QString &filename)
-{
- m_resourceAdapter->importResourceFile(filename);
-}
-
-void KoResourceModel::importResourceFile(const QString & filename, bool fileCreation)
-{
- m_resourceAdapter->importResourceFile(filename, fileCreation);
-}
-
-bool KoResourceModel::removeResource(KoResource* resource)
-{
- return m_resourceAdapter->removeResource(resource);
-}
-
-void KoResourceModel::removeResourceFile(const QString &filename)
-{
- m_resourceAdapter->removeResourceFile(filename);
-}
-
-QStringList KoResourceModel::assignedTagsList(KoResource *resource) const
-{
- return m_resourceAdapter->assignedTagsList(resource);
-}
-
-void KoResourceModel::addTag(KoResource* resource, const QString& tag)
-{
- m_resourceAdapter->addTag(resource, tag);
- emit tagBoxEntryAdded(tag);
-}
-
-void KoResourceModel::deleteTag(KoResource *resource, const QString &tag)
-{
- m_resourceAdapter->deleteTag(resource, tag);
-}
-
-QStringList KoResourceModel::tagNamesList() const
-{
- return m_resourceAdapter->tagNamesList();
-}
-
-QStringList KoResourceModel::searchTag(const QString& lineEditText)
-{
- return m_resourceAdapter->searchTag(lineEditText);
-}
-
-void KoResourceModel::searchTextChanged(const QString& searchString)
-{
- m_resourceAdapter->searchTextChanged(searchString);
-}
-
-void KoResourceModel::enableResourceFiltering(bool enable)
-{
- m_resourceAdapter->enableResourceFiltering(enable);
-}
-
-void KoResourceModel::setCurrentTag(const QString& currentTag)
-{
- m_currentTag = currentTag;
- m_resourceAdapter->setCurrentTag(currentTag);
-}
-
-void KoResourceModel::updateServer()
-{
- m_resourceAdapter->updateServer();
-}
-
-int KoResourceModel::resourcesCount() const
-{
- return m_resourceAdapter->resources().count();
-}
-
-QList<KoResource *> KoResourceModel::currentlyVisibleResources() const
-{
- return m_resourceAdapter->resources();
-}
-
-void KoResourceModel::tagCategoryMembersChanged()
-{
- m_resourceAdapter->tagCategoryMembersChanged();
-}
-
-void KoResourceModel::tagCategoryAdded(const QString& tag)
-{
- m_resourceAdapter->tagCategoryAdded(tag);
-}
-
-void KoResourceModel::tagCategoryRemoved(const QString& tag)
-{
- m_resourceAdapter->tagCategoryRemoved(tag);
-}
-
-QString KoResourceModel::serverType() const
-{
- return m_resourceAdapter->serverType();
-}
-
-QList< KoResource* > KoResourceModel::serverResources() const
-{
- return m_resourceAdapter->serverResources();
-}
diff --git a/libs/widgets/KoResourceModel.h b/libs/widgets/KoResourceModel.h
deleted file mode 100644
index e80db65e2c..0000000000
--- a/libs/widgets/KoResourceModel.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2008 Jan Hambrecht <jaham@gmx.net>
- * 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.
- */
-
-#ifndef KORESOURCEMODEL_H
-#define KORESOURCEMODEL_H
-
-#include <QSharedPointer>
-
-#include "QAbstractTableModel"
-
-#include "kritawidgets_export.h"
-
-class KoAbstractResourceServerAdapter;
-class KoResource;
-
-/// The resource model managing the resource data
-class KRITAWIDGETS_EXPORT KoResourceModel : public QAbstractTableModel
-{
- Q_OBJECT
-public:
- explicit KoResourceModel(QSharedPointer<KoAbstractResourceServerAdapter> resourceAdapter, QObject * parent = 0);
- ~KoResourceModel() override;
-
- /// reimplemented
- int rowCount(const QModelIndex &parent = QModelIndex()) const override;
- /// reimplemented
- int columnCount ( const QModelIndex & parent = QModelIndex() ) const override;
- /// reimplemented
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- /// reimplemented
- QModelIndex index ( int row, int column = 0, const QModelIndex & parent = QModelIndex() ) const override;
- /// Sets the number of columns to display
- void setColumnCount( int columnCount );
-
- /// Extensions to Qt::ItemDataRole.
- enum ItemDataRole
- {
- /// A larger thumbnail for displaying in a tooltip. 200x200 or so.
- LargeThumbnailRole = Qt::UserRole + 1,
- TagsRole
- };
-
- QModelIndex indexFromResource(KoResource* resource) const;
-
- /// facade for KoAbstractResourceServerAdapter
- QString extensions() const;
- void importResourceFile(const QString &filename);
- void importResourceFile(const QString &filename, bool fileCreation);
- bool removeResource(KoResource* resource);
- void removeResourceFile(const QString & filename);
- QStringList assignedTagsList(KoResource *resource) const;
- void addTag(KoResource* resource, const QString& tag);
- void deleteTag( KoResource* resource, const QString& tag);
- QStringList tagNamesList() const;
- QStringList searchTag(const QString& lineEditText);
- void enableResourceFiltering(bool enable);
- void setCurrentTag(const QString& currentTag);
- void searchTextChanged(const QString& searchString);
- void updateServer();
- int resourcesCount() const;
- QList<KoResource *> currentlyVisibleResources() const;
- QList<KoResource *> serverResources() const;
- void tagCategoryMembersChanged();
- void tagCategoryAdded(const QString& tag);
- void tagCategoryRemoved(const QString& tag);
-
- QString serverType() const;
-
-Q_SIGNALS:
- /// XXX: not sure if this is the best place for these
- void tagBoxEntryModified();
- void tagBoxEntryAdded(const QString& tag);
- void tagBoxEntryRemoved(const QString& tag);
-
- void beforeResourcesLayoutReset(KoResource *activateAfterReformat);
- void afterResourcesLayoutReset();
-
-private:
- void doSafeLayoutReset(KoResource *activateAfterReformat);
-
-private Q_SLOTS:
- void resourceAdded(KoResource *resource);
- void resourceRemoved(KoResource *resource);
- void resourceChanged(KoResource *resource);
- void tagBoxEntryWasModified();
- void tagBoxEntryWasAdded(const QString& tag);
- void tagBoxEntryWasRemoved(const QString& tag);
-
-private:
- QSharedPointer<KoAbstractResourceServerAdapter> m_resourceAdapter;
- int m_columnCount;
- QString m_currentTag;
-};
-
-#endif // KORESOURCEMODEL_H
diff --git a/libs/widgets/KoResourcePopupAction.cpp b/libs/widgets/KoResourcePopupAction.cpp
index 182eb7739b..bd684989dd 100644
--- a/libs/widgets/KoResourcePopupAction.cpp
+++ b/libs/widgets/KoResourcePopupAction.cpp
@@ -1,210 +1,205 @@
-/* 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>
+ * 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 "KoResourcePopupAction.h"
-#include "KoResourceServerAdapter.h"
-#include "KoResourceItemView.h"
-#include "KoResourceModel.h"
-#include "KoResourceItemDelegate.h"
-#include <resources/KoResource.h>
-#include "KoCheckerBoardPainter.h"
-#include "KoShapeBackground.h"
+#include <KisResourceItemListView.h>
+#include <KisResourceModel.h>
+#include <KisResourceModelProvider.h>
+#include <KisResourceItemDelegate.h>
+#include <KoResource.h>
+
+#include <KoCheckerBoardPainter.h>
+#include <KoShapeBackground.h>
#include <resources/KoAbstractGradient.h>
#include <resources/KoPattern.h>
#include <KoGradientBackground.h>
#include <KoPatternBackground.h>
#include <KoImageCollection.h>
#include <QMenu>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QPainter>
#include <QGradient>
#include <QToolButton>
#include <QRect>
#include <QWidgetAction>
class KoResourcePopupAction::Private
{
public:
QMenu *menu = 0;
- KoResourceModel *model = 0;
- KoResourceItemView *resourceList = 0;
+ KisResourceModel *model = 0;
+ KisResourceItemListView *resourceList = 0;
QSharedPointer<KoShapeBackground> background;
KoImageCollection *imageCollection = 0;
KoCheckerBoardPainter checkerPainter {4};
};
-KoResourcePopupAction::KoResourcePopupAction(QSharedPointer<KoAbstractResourceServerAdapter>resourceAdapter, QObject *parent)
- : QAction(parent)
+KoResourcePopupAction::KoResourcePopupAction(const QString &resourceType, QObject *parent)
+ : QAction(parent)
, d(new Private())
{
- Q_ASSERT(resourceAdapter);
-
d->menu = new QMenu();
QWidget *widget = new QWidget();
QWidgetAction *wdgAction = new QWidgetAction(this);
- d->resourceList = new KoResourceItemView(widget);
+ d->resourceList = new KisResourceItemListView(widget);
- d->model = new KoResourceModel(resourceAdapter, widget);
+ d->model = KisResourceModelProvider::resourceModel(resourceType);
d->resourceList->setModel(d->model);
- d->resourceList->setItemDelegate(new KoResourceItemDelegate(widget));
- KoResourceModel * resourceModel = qobject_cast<KoResourceModel*>(d->resourceList->model());
- if (resourceModel) {
- resourceModel->setColumnCount(1);
- }
-
- KoResource *resource = 0;
- QList<KoResource*> resources = resourceAdapter->resources();
- if (resources.count() > 0) {
- resource = resources.at(0);
- d->resourceList->setCurrentIndex(d->model->indexFromResource(resource));
- indexChanged(d->resourceList->currentIndex());
+ d->resourceList->setItemDelegate(new KisResourceItemDelegate(widget));
+ d->resourceList->setCurrentIndex(d->model->index(0, 0));
+ if (resourceType==ResourceType::Gradients) {
+ d->resourceList->setViewMode(QListView::ListMode);
}
-
+ indexChanged(d->resourceList->currentIndex());
QHBoxLayout *layout = new QHBoxLayout(widget);
layout->addWidget(d->resourceList);
widget->setLayout(layout);
wdgAction->setDefaultWidget(widget);
d->menu->addAction(wdgAction);
setMenu(d->menu);
new QHBoxLayout(d->menu);
d->menu->layout()->addWidget(widget);
d->menu->layout()->setMargin(0);
connect(d->resourceList, SIGNAL(clicked(QModelIndex)), this, SLOT(indexChanged(QModelIndex)));
updateIcon();
}
KoResourcePopupAction::~KoResourcePopupAction()
{
/* Removing the actions here make them be deleted together with their default widget.
* This happens only if the actions are QWidgetAction, and we know they are since
* the only ones added are in KoResourcePopupAction constructor. */
int i = 0;
while(d->menu->actions().size() > 0) {
d->menu->removeAction(d->menu->actions()[i]);
++i;
}
delete d->menu;
delete d->imageCollection;
delete d;
}
QSharedPointer<KoShapeBackground> KoResourcePopupAction::currentBackground() const
{
return d->background;
}
void KoResourcePopupAction::setCurrentBackground(QSharedPointer<KoShapeBackground> background)
{
d->background = background;
updateIcon();
}
-void KoResourcePopupAction::setCurrentResource(KoResource *resource)
+void KoResourcePopupAction::setCurrentResource(KoResourceSP resource)
{
QModelIndex index = d->model->indexFromResource(resource);
if (index.isValid()) {
d->resourceList->setCurrentIndex(index);
indexChanged(index);
}
}
-KoResource* KoResourcePopupAction::currentResource() const
+KoResourceSP KoResourcePopupAction::currentResource() const
{
QModelIndex index = d->resourceList->currentIndex();
if (!index.isValid()) return 0;
- return static_cast<KoResource*>(index.internalPointer());
+ KoResourceSP resource = d->model->resourceForIndex(index);
+ return resource;
}
void KoResourcePopupAction::indexChanged(const QModelIndex &modelIndex)
{
if (! modelIndex.isValid()) {
return;
}
d->menu->hide();
- KoResource *resource = static_cast<KoResource*>(modelIndex.internalPointer());
- if(resource) {
- KoAbstractGradient *gradient = dynamic_cast<KoAbstractGradient*>(resource);
- KoPattern *pattern = dynamic_cast<KoPattern*>(resource);
+ KoResourceSP resource = d->model->resourceForIndex(modelIndex);
+
+ if (resource) {
+ KoAbstractGradientSP gradient = resource.dynamicCast<KoAbstractGradient>();
+ KoPatternSP pattern = resource.dynamicCast<KoPattern>();
if (gradient) {
QGradient *qg = gradient->toQGradient();
qg->setCoordinateMode(QGradient::ObjectBoundingMode);
d->background = QSharedPointer<KoShapeBackground>(new KoGradientBackground(qg));
} else if (pattern) {
KoImageCollection *collection = new KoImageCollection();
d->background = QSharedPointer<KoShapeBackground>(new KoPatternBackground(collection));
qSharedPointerDynamicCast<KoPatternBackground>(d->background)->setPattern(pattern->pattern());
}
emit resourceSelected(d->background);
updateIcon();
}
}
void KoResourcePopupAction::updateIcon()
{
QSize iconSize;
QToolButton *toolButton = dynamic_cast<QToolButton*>(parentWidget());
if (toolButton) {
iconSize = QSize(toolButton->iconSize());
} else {
iconSize = QSize(16, 16);
}
// This must be a QImage, as drawing to a QPixmap outside the
// UI thread will cause sporadic crashes.
QImage pm = QImage(iconSize, QImage::Format_ARGB32_Premultiplied);
pm.fill(Qt::transparent);
QPainter p(&pm);
QSharedPointer<KoGradientBackground> gradientBackground = qSharedPointerDynamicCast<KoGradientBackground>(d->background);
QSharedPointer<KoPatternBackground> patternBackground = qSharedPointerDynamicCast<KoPatternBackground>(d->background);
if (gradientBackground) {
QRect innerRect(0, 0, iconSize.width(), iconSize.height());
QLinearGradient paintGradient;
paintGradient.setStops(gradientBackground->gradient()->stops());
paintGradient.setStart(innerRect.topLeft());
paintGradient.setFinalStop(innerRect.topRight());
d->checkerPainter.paint(p, innerRect);
p.fillRect(innerRect, QBrush(paintGradient));
- } else if (patternBackground) {
+ }
+ else if (patternBackground) {
d->checkerPainter.paint(p, QRect(QPoint(),iconSize));
p.fillRect(0, 0, iconSize.width(), iconSize.height(), patternBackground->pattern());
}
p.end();
setIcon(QIcon(QPixmap::fromImage(pm)));
}
diff --git a/libs/widgets/KoResourcePopupAction.h b/libs/widgets/KoResourcePopupAction.h
index ce64f1ad17..17781cfccf 100644
--- a/libs/widgets/KoResourcePopupAction.h
+++ b/libs/widgets/KoResourcePopupAction.h
@@ -1,74 +1,75 @@
-/* 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>
+ * 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 KORESOURCEPOPUPACTION_H
#define KORESOURCEPOPUPACTION_H
#include <QAction>
#include <QSharedPointer>
-#include "kritawidgets_export.h"
+#include <KoResource.h>
class KoShapeBackground;
-class KoAbstractResourceServerAdapter;
class QModelIndex;
-class KoResource;
+
+#include "kritawidgets_export.h"
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);
+ explicit KoResourcePopupAction(const QString &resourceType, QObject *parent = 0);
/**
* Destructor
*/
~KoResourcePopupAction() override;
QSharedPointer<KoShapeBackground> currentBackground() const;
void setCurrentBackground(QSharedPointer<KoShapeBackground> background);
- void setCurrentResource(KoResource *resource);
- KoResource *currentResource() const;
+ void setCurrentResource(KoResourceSP resource);
+ KoResourceSP 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
deleted file mode 100644
index 2f9cf1b2e7..0000000000
--- a/libs/widgets/KoResourceServer.h
+++ /dev/null
@@ -1,759 +0,0 @@
-/* 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 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. "*.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 two 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.completeBaseName() + "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.completeBaseName() + resource->defaultFileExtension();
- QFileInfo fileInfo(newFilename);
-
- int i = 1;
- while (fileInfo.exists()) {
- fileInfo.setFile(saveLocation() + fi.completeBaseName() + 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/KoResourceServerAdapter.cpp b/libs/widgets/KoResourceServerAdapter.cpp
deleted file mode 100644
index 99ef8731ed..0000000000
--- a/libs/widgets/KoResourceServerAdapter.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/* This file is part of the KDE project
-
- 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 "KoResourceServerAdapter.h"
-
-KoAbstractResourceServerAdapter::KoAbstractResourceServerAdapter(QObject *parent)
- : QObject(parent)
-{
-}
-
-KoAbstractResourceServerAdapter::~KoAbstractResourceServerAdapter()
-{
-}
-
-void KoAbstractResourceServerAdapter::emitResourceAdded(KoResource* resource)
-{
- emit resourceAdded(resource);
-}
-
-void KoAbstractResourceServerAdapter::emitRemovingResource(KoResource* resource)
-{
- emit removingResource(resource);
-}
-
-void KoAbstractResourceServerAdapter::emitResourceChanged(KoResource* resource)
-{
- emit resourceChanged(resource);
-}
-
-void KoAbstractResourceServerAdapter::emitTagsWereChanged()
-{
- emit tagsWereChanged();
-}
-
-void KoAbstractResourceServerAdapter::emitTagCategoryWasAdded(const QString& tag)
-{
- emit tagCategoryWasAdded(tag);
-}
-
-void KoAbstractResourceServerAdapter::emitTagCategoryWasRemoved(const QString& tag)
-{
- emit tagCategoryWasRemoved(tag);
-}
diff --git a/libs/widgets/KoResourceServerAdapter.h b/libs/widgets/KoResourceServerAdapter.h
deleted file mode 100644
index 7e04dad842..0000000000
--- a/libs/widgets/KoResourceServerAdapter.h
+++ /dev/null
@@ -1,355 +0,0 @@
-/* This file is part of the KDE project
-
- Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
- 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 KO_RESOURCESERVER_ADAPTER_H_
-#define KO_RESOURCESERVER_ADAPTER_H_
-
-#include "KoResourceServer.h"
-#include <resources/KoResource.h>
-#include <KoResourceFiltering.h>
-
-#include "kritawidgets_export.h"
-
-/// The resource server adapter provides a adapter pattern for a templated resource server
-class KRITAWIDGETS_EXPORT KoAbstractResourceServerAdapter : public QObject
-{
- Q_OBJECT
-public:
- KoAbstractResourceServerAdapter(QObject *parent = 0);
- ~KoAbstractResourceServerAdapter() override;
-
- virtual void connectToResourceServer() = 0;
- virtual QList<KoResource*> resources() = 0;
- virtual QList<KoResource*> serverResources() = 0;
- virtual bool addResource(KoResource* resource) = 0;
- virtual bool removeResource(KoResource* resource) = 0;
- virtual void removeResourceFile(const QString & filename) = 0;
- virtual void importResourceFile(const QString & filename, bool fileCreation = true) = 0;
- virtual QString extensions() const = 0;
- virtual void setCurrentTag(const QString& currentTag) = 0;
- virtual void enableResourceFiltering(bool tagSearch) = 0;
- virtual void updateServer() = 0;
- virtual QStringList assignedTagsList(KoResource* resource) = 0;
- virtual QStringList tagNamesList() = 0;
- virtual void addTag(const QString& tag) = 0;
- virtual void addTag(KoResource* resource, const QString& tag) = 0;
- virtual void deleteTag(KoResource* resource, const QString& tag) = 0;
- virtual void searchTextChanged(const QString& searchString) = 0;
- // these call the server.
- virtual void tagCategoryMembersChanged() = 0;
- virtual void tagCategoryAdded(const QString& tag) = 0;
- virtual void tagCategoryRemoved(const QString& tag) = 0;
-
- virtual void setFilterIncludes(const QStringList& filteredNames) = 0;
- virtual QStringList searchTag(const QString& lineEditText) = 0;
- virtual void configureFilters(int filterType, bool enable) = 0;
-
- virtual QString serverType() const { return QString(); }
-
- virtual void setSortingEnabled(bool value) = 0;
- virtual bool sortingEnabled() const = 0;
-
-Q_SIGNALS:
- void resourceAdded(KoResource*);
- void removingResource(KoResource*);
- void resourceChanged(KoResource*);
- void tagsWereChanged();
- void tagCategoryWasAdded(const QString& tag);
- void tagCategoryWasRemoved(const QString& tag);
-
-protected:
- void emitResourceAdded(KoResource* resource);
- void emitRemovingResource(KoResource* resource);
- void emitResourceChanged(KoResource* resource);
- void emitTagsWereChanged();
- void emitTagCategoryWasAdded(const QString& tag);
- void emitTagCategoryWasRemoved(const QString& tag);
-};
-
-/**
- * The KoResourceServerAdapter provides adapter to a specific resource server
- * It provides a resource type independent interface to the server.
- */
-template <class T, class Policy = PointerStoragePolicy<T> >
- class KoResourceServerAdapter : public KoAbstractResourceServerAdapter, public KoResourceServerObserver<T, Policy>
-{
- typedef KoResourceServer<T, Policy> ServerType;
- typedef typename Policy::PointerType PointerType;
-public:
- KoResourceServerAdapter(ServerType* resourceServer, QObject *parent = 0)
- : KoAbstractResourceServerAdapter(parent)
- , m_resourceServer(resourceServer)
- , m_sortingEnabled(false)
- {
- m_changeCounter = 0;
- m_oldChangeCounter = 0;
- m_enableFiltering = false;
- m_resourceFilter.setResourceServer(m_resourceServer);
- }
-
-
- ~KoResourceServerAdapter() override
- {
- if (m_resourceServer)
- m_resourceServer->removeObserver(this);
- }
-
- QString serverType() const override
- {
- if (m_resourceServer) {
- return m_resourceServer->type();
- }
- return KoAbstractResourceServerAdapter::serverType();
- }
-
- void unsetResourceServer() override
- {
- m_resourceServer = 0;
- }
-
- void connectToResourceServer() override
- {
- if (m_resourceServer)
- m_resourceServer->addObserver(this);
- }
-
- QList<KoResource*> resources() override
- {
- if (! m_resourceServer)
- return QList<KoResource*>();
-
- bool cacheDirty = serverResourceCacheInvalid();
- if (cacheDirty) {
- QList<PointerType> serverResources =
- m_sortingEnabled ?
- m_resourceServer->sortedResources() :
- m_resourceServer->resources();
-
- cacheServerResources(serverResources);
- }
- if (m_enableFiltering) {
- if (m_resourceFilter.filtersHaveChanged() || cacheDirty) {
- m_filteredResources = m_resourceFilter.filterResources(m_serverResources);
- }
- return m_filteredResources;
- }
- return m_serverResources;
- }
-
- bool addResource(KoResource* resource) override
- {
- if (! m_resourceServer)
- return false;
-
- T* res = dynamic_cast<T*>(resource);
- if (res) {
- return m_resourceServer->addResource(res);
- }
-
- return false;
- }
-
- bool removeResource(KoResource* resource) override
- {
- if (! m_resourceServer)
- return false;
-
- T* res = dynamic_cast<T*>(resource);
- if (res) {
-
- return m_resourceServer->removeResourceAndBlacklist(res);
-
- }
-
- return false;
- }
-
- void importResourceFile(const QString & filename , bool fileCreation = true) override
- {
- if (! m_resourceServer)
- return;
- m_resourceServer->importResourceFile(filename, fileCreation);
- }
-
- void removeResourceFile(const QString & filename) override
- {
- if (!m_resourceServer) {
- return;
- }
-
- m_resourceServer->removeResourceFile(filename);
- }
-
- void resourceAdded(PointerType resource) override {
- serverResourceCacheInvalid(true);
- emitResourceAdded(Policy::toResourcePointer(resource));
- }
-
- void removingResource(PointerType resource) override {
- serverResourceCacheInvalid(true);
- emitRemovingResource(Policy::toResourcePointer(resource));
- }
-
- void resourceChanged(PointerType resource) override {
- serverResourceCacheInvalid(true);
- emitResourceChanged(Policy::toResourcePointer(resource));
- }
-
- void resourceChangedNoCacheInvalidation(PointerType resource) {
- emitResourceChanged(Policy::toResourcePointer(resource));
- }
-
- void syncTaggedResourceView() override {
- serverResourceCacheInvalid(true);
- m_resourceFilter.rebuildCurrentTagFilenames();
- emitTagsWereChanged();
- }
-
- void syncTagAddition(const QString& tag) override {
- emitTagCategoryWasAdded(tag);
- }
-
- void syncTagRemoval(const QString& tag) override {
- emitTagCategoryWasRemoved(tag);
- }
-
- QString extensions() const override {
- if (! m_resourceServer)
- return QString();
-
- return m_resourceServer->extensions();
- }
-
- void setCurrentTag(const QString& resourceFileNames) override {
- serverResourceCacheInvalid(true);
- m_resourceFilter.setCurrentTag(resourceFileNames);
- }
-
- void enableResourceFiltering(bool enable) override {
- m_enableFiltering = enable;
- }
-
- void updateServer() override {
- emitRemovingResource(0);
- }
-
- QStringList assignedTagsList(KoResource* resource) override {
- return m_resourceServer->assignedTagsList(resource);
- }
-
- QStringList tagNamesList() override {
- return m_resourceServer->tagNamesList();
- }
-
- void addTag(const QString& tag) override {
- m_resourceServer->addTag(0, tag);
- }
-
- void addTag(KoResource* resource, const QString& tag) override {
- m_resourceServer->addTag(resource, tag);
- }
-
- void deleteTag(KoResource* resource, const QString& tag) override {
- m_resourceServer->delTag(resource, tag);
- }
-
- void setFilterIncludes(const QStringList& filteredNames) override {
- m_resourceFilter.setInclusions(filteredNames);
- }
-
- void searchTextChanged(const QString& searchString) override {
- m_resourceFilter.setFilters(searchString);
- serverResourceCacheInvalid(true);
- }
-
- QStringList searchTag(const QString& lineEditText) override {
- return m_resourceServer->searchTag(lineEditText);
- }
-
- // called by model to notify server of change
- void tagCategoryMembersChanged() override {
- m_resourceServer->tagCategoryMembersChanged();
- }
-
- void tagCategoryAdded(const QString& tag) override {
- m_resourceServer->tagCategoryAdded(tag);
- }
-
- void tagCategoryRemoved(const QString& tag) override {
- m_resourceServer->tagCategoryRemoved(tag);
- }
-
- QList<KoResource*> serverResources() override {
- return m_serverResources;
- }
-
- void configureFilters(int filterType, bool enable) override{
- m_resourceFilter.configure(filterType,enable);
- }
-
- void setSortingEnabled(bool value) override {
- m_sortingEnabled = value;
- serverResourceCacheInvalid(true);
- }
-
- bool sortingEnabled() const override {
- return m_sortingEnabled;
- }
-
-protected:
- ServerType* resourceServer() const {
- return m_resourceServer;
- }
-protected:
- KoResourceFiltering m_resourceFilter;
-private:
- bool serverResourceCacheInvalid() const {
- return m_changeCounter != m_oldChangeCounter;
- }
-
- void serverResourceCacheInvalid(bool yes) {
- if (yes) {
- ++m_changeCounter;
- } else {
- m_oldChangeCounter = m_changeCounter;
- }
- }
-
- void cacheServerResources(const QList<PointerType> &serverResources) {
- m_serverResources.clear();
-
- Q_FOREACH (PointerType resource, serverResources) {
- m_serverResources.append(Policy::toResourcePointer(resource));
- }
- serverResourceCacheInvalid(false);
- }
-
- ServerType* m_resourceServer;
- unsigned int m_changeCounter;
- unsigned int m_oldChangeCounter;
- QList<KoResource*> m_serverResources;
- QList<KoResource*> m_filteredResources;
- bool m_enableFiltering;
- bool m_sortingEnabled;
-};
-
-#endif // KO_RESOURCESERVER_ADAPTER_H_
diff --git a/libs/widgets/KoResourceServerObserver.h b/libs/widgets/KoResourceServerObserver.h
index 604c96900c..6906841d9d 100644
--- a/libs/widgets/KoResourceServerObserver.h
+++ b/libs/widgets/KoResourceServerObserver.h
@@ -1,77 +1,74 @@
/* This file is part of the KDE project
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 KORESOURCESERVEROBSERVER_H
#define KORESOURCESERVEROBSERVER_H
#include "kritawidgets_export.h"
-
-#include "KoResourceServerPolicies.h"
-
+#include <QSharedPointer>
/**
* The KoResourceServerObserver class provides a interface to observe a KoResourceServer.
* To receive notifications it needs to be added to the resource server.
*/
-template <class T, class Policy = PointerStoragePolicy<T> >
+template <class T>
class KoResourceServerObserver
{
public:
virtual ~KoResourceServerObserver() {}
- typedef typename Policy::PointerType PointerType;
virtual void unsetResourceServer() = 0;
/**
* Will be called by the resource server after a resource is added
* @param resource the added resource
*/
- virtual void resourceAdded(PointerType resource) = 0;
+ virtual void resourceAdded(QSharedPointer<T> resource) = 0;
/**
* Will be called by the resource server before a resource will be removed
* @param resource the resource which is going to be removed
*/
- virtual void removingResource(PointerType resource) = 0;
+ virtual void removingResource(QSharedPointer<T> resource) = 0;
/**
* Will be called by the resource server when a resource is changed
* @param resource the resource which is going to be removed
*/
- virtual void resourceChanged(PointerType resource) = 0;
+ virtual void resourceChanged(QSharedPointer<T> resource) = 0;
/**
* Will be called by the resource server when resources are added or removed
* from a tag category
*/
virtual void syncTaggedResourceView()=0;
/**
* Will be called by the resource server when a new tag category has been created
*/
virtual void syncTagAddition(const QString& tag)=0;
/**
* Will be called by the resource server when a new tag category has been deleted
*/
virtual void syncTagRemoval(const QString& tag)=0;
};
#endif // KORESOURCESERVEROBSERVER_H
diff --git a/libs/widgets/KoResourceServerPolicies.h b/libs/widgets/KoResourceServerPolicies.h
deleted file mode 100644
index 04f51efcef..0000000000
--- a/libs/widgets/KoResourceServerPolicies.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* This file is part of the KDE project
-
- Copyright (c) 2014 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; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef KORESOURCESERVERPOLICIES_H
-#define KORESOURCESERVERPOLICIES_H
-
-#include "kritawidgets_export.h"
-
-class KoResource;
-
-template <class T> struct PointerStoragePolicy
-{
- typedef T* PointerType;
- static inline void deleteResource(PointerType resource) {
- delete resource;
- }
- static inline KoResource* toResourcePointer(PointerType resource) {
- return resource;
- }
-};
-
-template <class SharedPointer> struct SharedPointerStoragePolicy
-{
- typedef SharedPointer PointerType;
- static inline void deleteResource(PointerType resource) {
- Q_UNUSED(resource);
- }
- static inline KoResource* toResourcePointer(PointerType resource) {
- return resource.data();
- }
-};
-
-#endif // KORESOURCESERVERPOLICIES_H
diff --git a/libs/widgets/KoResourceServerProvider.cpp b/libs/widgets/KoResourceServerProvider.cpp
index 1370a357a9..94657b3baa 100644
--- a/libs/widgets/KoResourceServerProvider.cpp
+++ b/libs/widgets/KoResourceServerProvider.cpp
@@ -1,199 +1,183 @@
/* 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 "klocalizedstring.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)
+ GradientResourceServer(const QString& type)
+ : KoResourceServer<KoAbstractGradient>(type)
{
insertSpecialGradients();
}
void insertSpecialGradients()
{
+ qDebug() << "insertSpecialGradients broken because we don't have a list we can insert in front of anymore";
+
+
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
QList<KoGradientStop> stops;
- KoStopGradient* gradient = new KoStopGradient();
+ KoStopGradientSP gradient(new KoStopGradient());
gradient->setType(QGradient::LinearGradient);
gradient->setName(i18n("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);
+ addResource(gradient, false);
m_foregroundToTransparent = gradient;
- gradient = new KoStopGradient();
+ gradient.reset(new KoStopGradient());
gradient->setType(QGradient::LinearGradient);
gradient->setName(i18n("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);
+ addResource(gradient, false);
m_foregroundToBackground = gradient;
}
private:
friend class KoResourceBundle;
- KoAbstractGradient* createResource( const QString & filename ) override {
+ KoAbstractGradientSP createResource( const QString & filename ) {
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;
- }
+ KoAbstractGradientSP 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(fileExtension == ".svg" || fileExtension == ".kgr") {
+ grad.reset(new KoStopGradient(filename));
}
- if (m_foregroundToBackground && resources.contains(m_foregroundToBackground)) {
- sorted.append(resources.takeAt(resources.indexOf(m_foregroundToBackground)));
+ else if(fileExtension == ".ggr" ) {
+ grad.reset(new KoSegmentGradient(filename));
}
- return sorted + resources;
+
+ return grad;
}
- KoAbstractGradient* m_foregroundToTransparent;
- KoAbstractGradient* m_foregroundToBackground;
+ KoAbstractGradientSP m_foregroundToTransparent;
+ KoAbstractGradientSP m_foregroundToBackground;
};
struct Q_DECL_HIDDEN KoResourceServerProvider::Private
{
- KoResourceServer<KoPattern>* patternServer;
- KoResourceServer<KoAbstractGradient>* gradientServer;
- KoResourceServer<KoColorSet>* paletteServer;
+ KoResourceServer<KoPattern> *patternServer;
+ KoResourceServer<KoAbstractGradient> *gradientServer;
+ KoResourceServer<KoColorSet> *paletteServer;
KoResourceServer<KoSvgSymbolCollectionResource> *svgSymbolCollectionServer;
- KoResourceServer<KoGamutMask>* gamutMaskServer;
+ 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", "*.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()));
+ d->patternServer = new KoResourceServer<KoPattern>(ResourceType::Patterns);
+ d->gradientServer = new GradientResourceServer(ResourceType::Gradients);
+ d->paletteServer = new KoResourceServer<KoColorSet>(ResourceType::Palettes);
+ d->svgSymbolCollectionServer = new KoResourceServer<KoSvgSymbolCollectionResource>(ResourceType::Symbols);
+ d->gamutMaskServer = new KoResourceServer<KoGamutMask>(ResourceType::GamutMasks);
}
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()
+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()
+KoResourceServer<KoPattern> *KoResourceServerProvider::patternServer()
{
return d->patternServer;
}
-KoResourceServer<KoAbstractGradient>* KoResourceServerProvider::gradientServer()
+KoResourceServer<KoAbstractGradient> *KoResourceServerProvider::gradientServer()
{
return d->gradientServer;
}
-KoResourceServer<KoColorSet>* KoResourceServerProvider::paletteServer()
+KoResourceServer<KoColorSet> *KoResourceServerProvider::paletteServer()
{
return d->paletteServer;
}
KoResourceServer<KoSvgSymbolCollectionResource> *KoResourceServerProvider::svgSymbolCollectionServer()
{
return d->svgSymbolCollectionServer;
}
-KoResourceServer<KoGamutMask>* KoResourceServerProvider::gamutMaskServer()
+KoResourceServer<KoGamutMask> *KoResourceServerProvider::gamutMaskServer()
{
return d->gamutMaskServer;
}
diff --git a/libs/widgets/KoResourceServerProvider.h b/libs/widgets/KoResourceServerProvider.h
index a9f2798c32..e2553def80 100644
--- a/libs/widgets/KoResourceServerProvider.h
+++ b/libs/widgets/KoResourceServerProvider.h
@@ -1,76 +1,75 @@
/* 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>
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 KORESOURCESERVERPROVIDER_H
#define KORESOURCESERVERPROVIDER_H
#include <kritawidgets_export.h>
#include <QThread>
#include <WidgetsDebug.h>
#include "KoResourceServer.h"
#include <resources/KoPattern.h>
#include <resources/KoAbstractGradient.h>
#include <resources/KoColorSet.h>
#include <resources/KoSvgSymbolCollectionResource.h>
#include <resources/KoGamutMask.h>
/**
* Provides default resource servers for gradients, patterns and palettes
*/
class KRITAWIDGETS_EXPORT KoResourceServerProvider : public QObject
{
Q_OBJECT
public:
KoResourceServerProvider();
~KoResourceServerProvider() override;
static KoResourceServerProvider* instance();
/**
* @brief blacklistFileNames filters the filenames with the list of blacklisted file names
* @param fileNames all files
* @param blacklistedFileNames the files we don't want
* @return the result
*/
static QStringList blacklistFileNames(QStringList fileNames, const QStringList &blacklistedFileNames);
-
- KoResourceServer<KoPattern>* patternServer();
- KoResourceServer<KoAbstractGradient>* gradientServer();
- KoResourceServer<KoColorSet>* paletteServer();
- KoResourceServer<KoSvgSymbolCollectionResource>* svgSymbolCollectionServer();
- KoResourceServer<KoGamutMask>* gamutMaskServer();
+ KoResourceServer<KoPattern> *patternServer();
+ KoResourceServer<KoAbstractGradient> *gradientServer();
+ KoResourceServer<KoColorSet> *paletteServer();
+ KoResourceServer<KoSvgSymbolCollectionResource> *svgSymbolCollectionServer();
+ KoResourceServer<KoGamutMask> *gamutMaskServer();
private:
KoResourceServerProvider(const KoResourceServerProvider&);
KoResourceServerProvider operator=(const KoResourceServerProvider&);
private:
struct Private;
Private* const d;
};
#endif // KORESOURCESERVERPROVIDER_H
diff --git a/libs/widgets/KoResourceTagStore.cpp b/libs/widgets/KoResourceTagStore.cpp
deleted file mode 100644
index 2ff7deb509..0000000000
--- a/libs/widgets/KoResourceTagStore.cpp
+++ /dev/null
@@ -1,415 +0,0 @@
-/* This file is part of the KDE project
-
- Copyright (c) 2011 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 "KoResourceTagStore.h"
-
-#include <QDebug>
-#include <QStringList>
-#include <QFile>
-#include <QDir>
-#include <QDomDocument>
-#include <KoResourcePaths.h>
-#include <KoResourceServer.h>
-
-#include <kis_debug.h>
-
-#define BLACKLISTED "blacklisted" ///< xml tag for blacklisted tags
-
-static const QStringList krita3PresetSystemTags = {"Ink", "Block", "Wet", "FX", "Erasers", "Circle", "Smudge", "Mix", "PixelArt", "ink", "sketch", "demo", "paint"};
-
-class Q_DECL_HIDDEN KoResourceTagStore::Private
-{
-public:
- QMultiHash<QByteArray, QString> md5ToTag;
- QMultiHash<QString, QString> identifierToTag;
-
- QHash<QString, int> tagList;
-
- QStringList blacklistedTags;
-
- KoResourceServerBase *resourceServer;
-};
-
-KoResourceTagStore::KoResourceTagStore(KoResourceServerBase *resourceServer)
- : d(new Private)
-{
- d->resourceServer = resourceServer;
-}
-
-KoResourceTagStore::~KoResourceTagStore()
-{
- delete d;
-}
-
-QStringList KoResourceTagStore::assignedTagsList(const KoResource* resource) const
-{
- if (!resource) return QStringList();
-
- QStringList tags = d->md5ToTag.values(resource->md5());
- tags += d->identifierToTag.values(resource->filename());
- tags.removeDuplicates();
- return tags;
-}
-
-void KoResourceTagStore::removeResource(const KoResource *resource)
-{
- QStringList tags = assignedTagsList(resource);
-
- d->md5ToTag.remove(resource->md5());
- d->identifierToTag.remove(resource->filename());
-
- Q_FOREACH (const QString &tag, tags) {
- if (d->tagList.contains(tag)) {
- if (d->tagList[tag] > 0) {
- d->tagList[tag]--;
- }
- }
- }
-}
-
-QStringList KoResourceTagStore::tagNamesList() const
-{
- QStringList tagList = d->tagList.uniqueKeys();
- Q_FOREACH(const QString &tag, d->blacklistedTags) {
- tagList.removeAll(tag);
- }
- return tagList;
-}
-
-void KoResourceTagStore::addTag(KoResource* resource, const QString& tag)
-{
-// if (d->resourceServer->type() == "kis_paintoppresets" && resource) {
-// qDebug() << "\t\t\taddTag" << tag << resource->filename() << d->tagList[tag] << d->md5ToTag.value(resource->md5()) << d->identifierToTag.values(resource->filename());
-// }
-
- if (d->blacklistedTags.contains(tag)) {
- d->blacklistedTags.removeAll(tag);
- }
-
- if (!d->tagList.contains(tag)) {
- d->tagList.insert(tag, 0);
- }
-
- if (resource) {
- bool added = false;
-
- if (!d->md5ToTag.contains(resource->md5(), tag)) {
- added = true;
- d->md5ToTag.insert(resource->md5(), tag);
- }
-
- if (!d->identifierToTag.contains(resource->filename())) {
- added = true;
- d->identifierToTag.insert(resource->filename(), tag);
- }
-
- if (added) {
- d->tagList[tag]++;
- }
- }
-// if (d->resourceServer->type() == "kis_paintoppresets" && resource) {
-// qDebug() << "\t\t\t\tafter addTag" << tag << resource->filename() << d->tagList[tag] << d->md5ToTag.value(resource->md5()) << d->identifierToTag.values(resource->filename());
-// }
-
-}
-
-void KoResourceTagStore::delTag(KoResource* resource, const QString& tag)
-{
- int res = d->md5ToTag.remove(resource->md5(), tag);
- res += d->identifierToTag.remove(resource->filename(), tag);
-
- if (res > 0) { // decrease the usecount for this tag
- if (d->tagList.contains(tag)) {
- if (d->tagList[tag] > 0) {
- d->tagList[tag]--;
- }
- }
- }
-}
-
-void KoResourceTagStore::delTag(const QString& tag)
-{
- Q_FOREACH (const QByteArray &res, d->md5ToTag.keys(tag)) {
- d->md5ToTag.remove(res, tag);
- }
- Q_FOREACH (const QString &identifier, d->identifierToTag.keys(tag)) {
- d->identifierToTag.remove(identifier, tag);
- }
-
- Q_ASSERT(!d->md5ToTag.values().contains(tag));
- Q_ASSERT(!d->identifierToTag.values().contains(tag));
- d->tagList.remove(tag);
- d->blacklistedTags << tag;
- serializeTags();
-}
-
-QStringList KoResourceTagStore::searchTag(const QString& query) const
-{
- QStringList tagsList = query.split(QRegExp("[,]\\s*"), QString::SkipEmptyParts);
- if (tagsList.isEmpty()) {
- return QStringList();
- }
-
- QSet<const KoResource*> resources;
-
- Q_FOREACH (QString tag, tagsList) {
- Q_FOREACH (const QByteArray &md5, d->md5ToTag.keys(tag)) {
- KoResource *res = d->resourceServer->byMd5(md5);
- if (res)
- resources << res;
- }
- Q_FOREACH (const QString &identifier, d->identifierToTag.keys(tag)) {
- KoResource *res = d->resourceServer->byFileName(identifier);
- if (res)
- resources << res;
- }
- }
-
- QStringList filenames;
- Q_FOREACH (const KoResource *res, resources) {
- if (res) {
- filenames << res->shortFilename();
- }
- }
- return filenames;
-}
-
-void KoResourceTagStore::loadTags()
-{
- QStringList tagFiles = KoResourcePaths::findDirs("tags");
- Q_FOREACH (const QString &tagFile, tagFiles) {
- QString fileName = tagFile + d->resourceServer->type() + "_tags.xml";
- if (QFileInfo(fileName).exists()) {
- readXMLFile(fileName);
- }
- }
-}
-
-void KoResourceTagStore::clearOldSystemTags()
-{
- if (d->resourceServer->type() == "kis_paintoppresets") {
-// qDebug() << "clearOldSystemTags" << d->tagList;
- Q_FOREACH(const QString &systemTag, krita3PresetSystemTags) {
-// qDebug() << "\t" << systemTag << d->tagList[systemTag];
- if (d->tagList[systemTag] == 0) {
- d->tagList.remove(systemTag);
- }
- }
- }
-}
-
-void KoResourceTagStore::writeXMLFile(const QString &tagstore)
-{
- QFile f(tagstore);
- if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
- warnWidgets << "Cannot write meta information to '" << tagstore << "'.";
- return;
- }
- QDomDocument doc;
- QDomElement root;
-
- QDomDocument docTemp("tags");
- doc = docTemp;
- doc.appendChild(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""));
- root = doc.createElement("tags");
- doc.appendChild(root);
-
- QSet<KoResource*> taggedResources;
- Q_FOREACH (const QByteArray &md5, d->md5ToTag.keys()) {
- KoResource *res = d->resourceServer->byMd5(md5);
- if (res) {
- taggedResources << res;
- }
- }
-
- Q_FOREACH (const QString &identifier, d->identifierToTag.keys()) {
- KoResource *res = d->resourceServer->byFileName(identifier);
- if (res) {
- taggedResources << res;
- }
- }
-
- Q_FOREACH (const KoResource *res, taggedResources) {
-
- QDomElement resourceEl = doc.createElement("resource");
- resourceEl.setAttribute("identifier", res->filename().replace(QDir::homePath(), QString("~")));
- resourceEl.setAttribute("md5", QString(res->md5().toBase64()));
-
- Q_FOREACH (const QString &tag, assignedTagsList(res)) {
- QDomElement tagEl = doc.createElement("tag");
- tagEl.setAttribute(BLACKLISTED, "false");
- tagEl.appendChild(doc.createTextNode(tag));
- resourceEl.appendChild(tagEl);
- }
- root.appendChild(resourceEl);
-
- }
-
- // Now write empty tags
- Q_FOREACH (const QString &tag, d->tagList.uniqueKeys()) {
- if (d->tagList[tag] == 0) {
- QDomElement resourceEl = doc.createElement("resource");
- resourceEl.setAttribute("identifier", "dummy");
- QDomElement tagEl = doc.createElement("tag");
- tagEl.setAttribute(BLACKLISTED, d->blacklistedTags.contains(tag) ? "true" : "false");
- tagEl.appendChild(doc.createTextNode(tag));
- resourceEl.appendChild(tagEl);
- root.appendChild(resourceEl);
- }
- }
-
- // Now write blacklisted tags.
- Q_FOREACH (const QString &tag, d->blacklistedTags) {
- if (d->tagList[tag] == 0) {
- QDomElement resourceEl = doc.createElement("resource");
- resourceEl.setAttribute("identifier", "dummy");
- QDomElement tagEl = doc.createElement("tag");
- tagEl.setAttribute(BLACKLISTED, "true");
- tagEl.appendChild(doc.createTextNode(tag));
- resourceEl.appendChild(tagEl);
- root.appendChild(resourceEl);
- }
- }
- QTextStream metastream(&f);
- metastream.setCodec("UTF-8");
- metastream << doc.toString();
-
- f.close();
-
-}
-
-void KoResourceTagStore::readXMLFile(const QString &tagstore)
-{
- QString inputFile;
- if (QFile::exists(tagstore)) {
- inputFile = tagstore;
- }
- else {
- return;
- }
-
-// qDebug() << "\treadXMLFile()." << tagstore << d->resourceServer->type() << "Server has" << d->resourceServer->resourceCount() << "resources";
- if (d->resourceServer->type() == "kis_paintoppresets") {
-// Q_FOREACH(const QString &line, kisBacktrace().split("\n")) {
-// qDebug() << line;
-// }
- }
-
- QFile f(inputFile);
- if (!f.open(QIODevice::ReadOnly)) {
- qWarning() << "Could not open tag file" << tagstore;
- return;
- }
-
- QDomDocument doc;
- if (!doc.setContent(&f)) {
- warnWidgets << "The file could not be parsed.";
- return;
- }
-
- QDomElement root = doc.documentElement();
- if (root.tagName() != "tags") {
- warnWidgets << "The file doesn't seem to be of interest.";
- return;
- }
-
- QDomNodeList resourceNodesList = root.childNodes();
-
- for (int i = 0; i < resourceNodesList.count(); i++) {
-
- QByteArray resourceMD5;
- QString identifier;
-
- QDomElement element = resourceNodesList.at(i).toElement();
- if (element.tagName() == "resource") {
-
- KoResource *resByMd5 = 0;
- KoResource *resByFileName = 0;
-
- if (element.hasAttribute("md5")) {
- resourceMD5 = QByteArray::fromBase64(element.attribute("md5").toLatin1());
- resByMd5 = d->resourceServer->byMd5(resourceMD5);
- }
-
- if (element.hasAttribute("identifier")) {
- identifier = element.attribute("identifier");
- QFileInfo fi(identifier);
- resByFileName = d->resourceServer->byFileName(fi.fileName());
- }
-
-// qDebug() << "\t\tmd5" << QString::fromLatin1(resourceMD5.toHex()) << "resByMd5" << resByMd5 << "identifier" << identifier << "resByFileName" << resByFileName;
-
- if (identifier == "dummy") {
- QDomNodeList tagNodesList = resourceNodesList.at(i).childNodes();
- for (int j = 0; j < tagNodesList.count() ; j++) {
- QDomElement tagEl = tagNodesList.at(j).toElement();
- bool blacklisted = (tagEl.attribute(BLACKLISTED, "false") == "true");
- if (blacklisted || d->blacklistedTags.contains(tagEl.text())) {
- if (!d->blacklistedTags.contains(tagEl.text())) {
- d->blacklistedTags << tagEl.text();
- }
- }
- else {
- addTag(0, tagEl.text());
- }
- }
- }
- else {
- KoResource *res = 0;
-
- if (resByMd5 && resByFileName && (resByMd5 != resByFileName)) {
- warnWidgets << "MD5sum and filename point to different resources -- was the resource renamed? We go with md5";
- res = resByMd5;
- }
- else if (!resByMd5 && resByFileName) {
- // We didn't find the resource by md5, but did find it by filename, so take that one
- res = resByFileName;
- }
- else {
- res = resByMd5;
- }
-
- QDomNodeList tagNodesList = resourceNodesList.at(i).childNodes();
- for (int j = 0; j < tagNodesList.count() ; j++) {
- QDomElement tagEl = tagNodesList.at(j).toElement();
- bool blacklisted = (tagEl.attribute(BLACKLISTED, "false") == "true");
- if (blacklisted || d->blacklistedTags.contains(tagEl.text())) {
- if (!d->blacklistedTags.contains(tagEl.text())) {
- d->blacklistedTags << tagEl.text();
- }
- }
- else {
- if (res) {
- addTag(res, tagEl.text());
- }
- d->md5ToTag.insert(resourceMD5, tagEl.text());
- d->identifierToTag.insert(identifier, tagEl.text());
- }
- }
- }
- }
- }
-// qDebug() << "Done reading XML file from" << tagstore << d->tagList;
-}
-
-void KoResourceTagStore::serializeTags()
-{
- writeXMLFile(KoResourcePaths::saveLocation("tags") + d->resourceServer->type() + "_tags.xml");
-}
diff --git a/libs/widgets/KoResourceTagStore.h b/libs/widgets/KoResourceTagStore.h
deleted file mode 100644
index d66046790c..0000000000
--- a/libs/widgets/KoResourceTagStore.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* This file is part of the KDE project
-
- Copyright (c) 2011 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
- */
-
-#ifndef KORESOURCETAGSTORE_H
-#define KORESOURCETAGSTORE_H
-
-
-#include <WidgetsDebug.h>
-#include "kritawidgets_export.h"
-
-class KoResourceServerBase;
-class KoResource;
-class QStringList;
-class QString;
-
-/**
- * KoResourceTagging allows to add and delete tags to resources and also search resources using tags
- */
-class KRITAWIDGETS_EXPORT KoResourceTagStore
-{
-public:
-
- /**
- * Constructs a KoResourceTagging object
- *
- */
- explicit KoResourceTagStore(KoResourceServerBase *resourceServer);
- ~KoResourceTagStore();
-
- QStringList assignedTagsList(const KoResource *resource) const;
-
- /// remote the given resource from the tagstore
- void removeResource(const KoResource *resource);
-
- /// Add the given tag to the tag store. The resource can be empty, in which case
- /// the tag is added but unused
- void addTag(KoResource* resource, const QString& tag);
-
- /// Remove the given tag for the given resource. It will be blacklisted if there are no users left.
- void delTag(KoResource* resource, const QString& tag);
-
- /// Remove the tag altogether. It will be blacklisted, too.
- void delTag(const QString& tag);
-
- /// @return a list of all the tags in this store
- QStringList tagNamesList() const;
-
- /// Return a list of filenames for the given tag
- QStringList searchTag(const QString& query) const;
-
- void loadTags();
- void clearOldSystemTags();
-
- void serializeTags();
-
-private:
- friend class KoResourceTaggingTest;
-
- void readXMLFile(const QString &tagstore);
- void writeXMLFile(const QString &tagstore);
-
- class Private;
- Private * const d;
-};
-
-
-#endif // KORESOURCETAGSTORE_H
diff --git a/libs/widgets/KoResourceTaggingManager.cpp b/libs/widgets/KoResourceTaggingManager.cpp
deleted file mode 100644
index 3e9f443018..0000000000
--- a/libs/widgets/KoResourceTaggingManager.cpp
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * 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 "KoResourceTaggingManager.h"
-
-#include <QInputDialog>
-#include <QMessageBox>
-#include <QPointer>
-#include <QStringList>
-
-#include <WidgetsDebug.h>
-
-#include <klocalizedstring.h>
-#include <ksharedconfig.h>
-
-#include "KoTagFilterWidget.h"
-#include "KoTagChooserWidget.h"
-#include "KoResourceModel.h"
-#include <resources/KoResource.h>
-#include "KoResourceItemChooserContextMenu.h"
-
-#include <kconfiggroup.h>
-
-class TaggedResourceSet
-{
-public:
- TaggedResourceSet()
- {}
-
- TaggedResourceSet(const QString& tagName, const QList<KoResource*>& resources)
- : tagName(tagName)
- , resources(resources)
- {}
-
- QString tagName;
- QList<KoResource*> resources;
-};
-
-
-class KoResourceTaggingManager::Private
-{
-public:
- QString currentTag;
- QList<KoResource*> originalResources;
- TaggedResourceSet lastDeletedTag;
-
- KoTagChooserWidget* tagChooser;
- KoTagFilterWidget* tagFilter;
-
- QCompleter* tagCompleter;
-
- QPointer<KoResourceModel> model;
-};
-
-
-KoResourceTaggingManager::KoResourceTaggingManager(KoResourceModel *model, QWidget* parent)
- : QObject(parent)
- , d(new Private())
-{
- d->model = model;
-
- d->tagChooser = new KoTagChooserWidget(parent);
- d->tagChooser->addReadOnlyItem("All"); // not translatable until other tags made translatable!
- d->tagChooser->addItems(d->model->tagNamesList());
-
- d->tagFilter = new KoTagFilterWidget(parent);
-
- connect(d->tagChooser, SIGNAL(tagChosen(QString)),
- this, SLOT(tagChooserIndexChanged(QString)));
- connect(d->tagChooser, SIGNAL(newTagRequested(QString)),
- this, SLOT(contextCreateNewTag(QString)));
- connect(d->tagChooser, SIGNAL(tagDeletionRequested(QString)),
- this, SLOT(removeTagFromComboBox(QString)));
- connect(d->tagChooser, SIGNAL(tagRenamingRequested(QString,QString)),
- this, SLOT(renameTag(QString,QString)));
- connect(d->tagChooser, SIGNAL(tagUndeletionRequested(QString)),
- this, SLOT(undeleteTag(QString)));
- connect(d->tagChooser, SIGNAL(tagUndeletionListPurgeRequested()),
- this, SLOT(purgeTagUndeleteList()));
-
- connect(d->tagFilter, SIGNAL(saveButtonClicked()),
- this, SLOT(tagSaveButtonPressed()));
- connect(d->tagFilter, SIGNAL(filterTextChanged(QString)),
- this, SLOT(tagSearchLineEditTextChanged(QString)));
-
- connect(d->model, SIGNAL(tagBoxEntryAdded(QString)),
- this, SLOT(syncTagBoxEntryAddition(QString)));
- connect(d->model, SIGNAL(tagBoxEntryRemoved(QString)),
- this, SLOT(syncTagBoxEntryRemoval(QString)));
- connect(d->model, SIGNAL(tagBoxEntryModified()),
- this, SLOT(syncTagBoxEntries()));
-
- // FIXME: fix tag completer
- // d->tagCompleter = new QCompleter(this);
- // d->tagSearchLineEdit->setCompleter(d->tagCompleter);
-
- syncTagBoxEntries();
-}
-
-KoResourceTaggingManager::~KoResourceTaggingManager()
-{
- delete d;
-}
-
-void KoResourceTaggingManager::showTaggingBar(bool show)
-{
- show ? d->tagFilter->show() : d->tagFilter->hide();
- show ? d->tagChooser->show() : d->tagChooser->hide();
-
- blockSignals(!show);
-
- QString tag("All");
- if (show) {
- KConfigGroup group = KSharedConfig::openConfig()->group("SelectedTags");
- tag = group.readEntry<QString>(d->model->serverType(), "All");
- }
- int idx = d->tagChooser->findIndexOf(tag);
- if (idx < 0) idx = 0;
- d->tagChooser->setCurrentIndex(idx);
-}
-
-void KoResourceTaggingManager::purgeTagUndeleteList()
-{
- d->lastDeletedTag = TaggedResourceSet();
- d->tagChooser->setUndeletionCandidate(QString());
-}
-
-void KoResourceTaggingManager::undeleteTag(const QString & tagToUndelete)
-{
- QString tagName = tagToUndelete;
- QStringList allTags = availableTags();
-
- if (allTags.contains(tagName)) {
- bool ok;
- tagName = QInputDialog::getText(
- d->tagChooser, i18n("Unable to undelete tag"),
- i18n("<qt>The tag you are trying to undelete already exists in tag list.<br>Please enter a new, unique name for it.</qt>"),
- QLineEdit::Normal,
- tagName, &ok);
-
- if (!ok || allTags.contains(tagName) || tagName.isEmpty()) {
- QMessageBox msgBox;
- msgBox.setIcon(QMessageBox::Warning);
- msgBox.setText(i18n("Tag was not undeleted."));
- msgBox.exec();
- return;
- }
- }
-
- QList<KoResource*> serverResources = d->model->serverResources();
-
- Q_FOREACH (KoResource * resource, d->lastDeletedTag.resources) {
- if (serverResources.contains(resource)) {
- addResourceTag(resource, tagName);
- }
- }
- d->model->tagCategoryAdded(tagName);
- d->tagChooser->setCurrentIndex(d->tagChooser->findIndexOf(tagName));
- d->tagChooser->setUndeletionCandidate(QString());
- d->lastDeletedTag = TaggedResourceSet();
-}
-
-QStringList KoResourceTaggingManager::availableTags() const
-{
- return d->tagChooser->allTags();
-}
-
-void KoResourceTaggingManager::addResourceTag(KoResource* resource, const QString& tagName)
-{
- QStringList tagsList = d->model->assignedTagsList(resource);
- if (tagsList.isEmpty()) {
- d->model->addTag(resource, tagName);
- } else {
- Q_FOREACH (const QString & tag, tagsList) {
- if (tag.compare(tagName)) {
- d->model->addTag(resource, tagName);
- }
- }
- }
-}
-
-void KoResourceTaggingManager::syncTagBoxEntryAddition(const QString& tag)
-{
- d->tagChooser->insertItem(tag);
-}
-
-void KoResourceTaggingManager::contextCreateNewTag(const QString& tag)
-{
- if (!tag.isEmpty()) {
- d->model->addTag(0, tag);
- d->model->tagCategoryAdded(tag);
- d->tagChooser->setCurrentIndex(d->tagChooser->findIndexOf(tag));
- updateTaggedResourceView();
- }
-}
-
-void KoResourceTaggingManager::contextCreateNewTag(KoResource* resource , const QString& tag)
-{
- if (!tag.isEmpty()) {
- d->model->tagCategoryAdded(tag);
- if (resource) {
- addResourceTag(resource, tag);
- }
- }
-}
-
-void KoResourceTaggingManager::syncTagBoxEntryRemoval(const QString& tag)
-{
- d->tagChooser->removeItem(tag);
-}
-
-void KoResourceTaggingManager::syncTagBoxEntries()
-{
- QStringList tags = d->model->tagNamesList();
- tags.sort();
- Q_FOREACH (const QString &tag, tags) {
- d->tagChooser->insertItem(tag);
- }
-}
-
-void KoResourceTaggingManager::contextAddTagToResource(KoResource* resource, const QString& tag)
-{
- addResourceTag(resource, tag);
- d->model->tagCategoryMembersChanged();
- updateTaggedResourceView();
-}
-
-void KoResourceTaggingManager::contextRemoveTagFromResource(KoResource* resource, const QString& tag)
-{
- removeResourceTag(resource, tag);
- d->model->tagCategoryMembersChanged();
- updateTaggedResourceView();
-}
-
-void KoResourceTaggingManager::removeTagFromComboBox(const QString &tag)
-{
- QList<KoResource*> resources = d->model->currentlyVisibleResources();
- Q_FOREACH (KoResource * resource, resources) {
- removeResourceTag(resource, tag);
- }
- d->model->tagCategoryRemoved(tag);
- d->lastDeletedTag = TaggedResourceSet(tag, resources);
- d->tagChooser->setUndeletionCandidate(tag);
-}
-
-void KoResourceTaggingManager::removeResourceTag(KoResource* resource, const QString& tagName)
-{
- QStringList tagsList = d->model->assignedTagsList(resource);
-
- Q_FOREACH (const QString & oldName, tagsList) {
- if (!oldName.compare(tagName)) {
- d->model->deleteTag(resource, oldName);
- }
- }
-}
-
-void KoResourceTaggingManager::renameTag(const QString &oldName, const QString& newName)
-{
- if (!d->model->tagNamesList().contains(newName)) {
- QList<KoResource*> resources = d->model->currentlyVisibleResources();
-
- Q_FOREACH (KoResource * resource, resources) {
- removeResourceTag(resource, oldName);
- addResourceTag(resource, newName);
- }
- contextCreateNewTag(newName);
- d->model->tagCategoryRemoved(oldName);
- d->model->tagCategoryAdded(newName);
- }
-}
-
-void KoResourceTaggingManager::updateTaggedResourceView()
-{
- d->model->setCurrentTag(d->currentTag);
- d->model->updateServer();
- d->originalResources = d->model->currentlyVisibleResources();
- emit updateView();
-}
-
-void KoResourceTaggingManager::tagChooserIndexChanged(const QString& lineEditText)
-{
- // HACK This allows tag "All" to be written in kritarc BUG:
- if (lineEditText == "All" || !d->tagChooser->selectedTagIsReadOnly()) {
- d->tagFilter->allowSave(true);
- d->currentTag = lineEditText;
- d->model->enableResourceFiltering( (lineEditText == "All")? false : true);
-
- } else {
- d->model->enableResourceFiltering(false);
- d->tagFilter->allowSave(false);
- d->currentTag.clear();
- }
-
- d->tagFilter->clear();
- updateTaggedResourceView();
-}
-
-void KoResourceTaggingManager::tagSearchLineEditTextChanged(const QString& lineEditText)
-{
- if (d->tagChooser->selectedTagIsReadOnly()) {
- d->model->enableResourceFiltering(!lineEditText.isEmpty());
- } else {
- d->model->enableResourceFiltering(true);
- }
-
- d->model->searchTextChanged(lineEditText);
- d->model->updateServer();
-
- ///FIXME: fix completer
- // d->tagCompleter = new QCompleter(tagNamesList(lineEditText),this);
- // d->tagSearchLineEdit->setCompleter(d->tagCompleter);
-
- emit updateView();
-}
-
-void KoResourceTaggingManager::tagSaveButtonPressed()
-{
- if (!d->tagChooser->selectedTagIsReadOnly()) {
- QList<KoResource*> newResources = d->model->currentlyVisibleResources();
- Q_FOREACH (KoResource * oldRes, d->originalResources) {
- if (!newResources.contains(oldRes))
- removeResourceTag(oldRes, d->currentTag);
- }
- Q_FOREACH (KoResource * newRes, newResources) {
- if (!d->originalResources.contains(newRes))
- addResourceTag(newRes, d->currentTag);
- }
- d->model->tagCategoryMembersChanged();
- }
- updateTaggedResourceView();
-}
-
-void KoResourceTaggingManager::contextMenuRequested(KoResource* resource, const QStringList& resourceTags, const QPoint& pos)
-{
- /* no visible tag chooser usually means no intended tag interaction,
- * context menu makes no sense then either */
- if (!resource || !d->tagChooser->isVisible())
- return;
-
- KoResourceItemChooserContextMenu menu(resource,
- resourceTags,
- d->tagChooser->currentlySelectedTag(),
- d->tagChooser->allTags());
-
- connect(&menu, SIGNAL(resourceTagAdditionRequested(KoResource*,QString)),
- this, SLOT(contextAddTagToResource(KoResource*,QString)));
-
- connect(&menu, SIGNAL(resourceTagRemovalRequested(KoResource*,QString)),
- this, SLOT(contextRemoveTagFromResource(KoResource*,QString)));
-
- connect(&menu, SIGNAL(resourceAssignmentToNewTagRequested(KoResource*,QString)),
- this, SLOT(contextCreateNewTag(KoResource*,QString)));
- menu.exec(pos);
-}
-
-void KoResourceTaggingManager::contextMenuRequested(KoResource* currentResource, QPoint pos)
-{
- if (currentResource) {
- contextMenuRequested(currentResource, d->model->assignedTagsList(currentResource), pos);
- }
-}
-
-KoTagChooserWidget* KoResourceTaggingManager::tagChooserWidget()
-{
- return d->tagChooser;
-}
-
-KoTagFilterWidget* KoResourceTaggingManager::tagFilterWidget()
-{
- return d->tagFilter;
-}
-
diff --git a/libs/widgets/KoResourceTaggingManager.h b/libs/widgets/KoResourceTaggingManager.h
deleted file mode 100644
index 161266e0f2..0000000000
--- a/libs/widgets/KoResourceTaggingManager.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef KORESOURCETAGGINGMANAGER_H
-#define KORESOURCETAGGINGMANAGER_H
-
-#include <QObject>
-
-class QWidget;
-class QStringList;
-class QString;
-class QPoint;
-
-class KoTagFilterWidget;
-class KoTagChooserWidget;
-class KoResourceModel;
-class KoResource;
-
-/**
- * @brief The KoResourceTaggingManager class is ...
- *
- * XXX: this needs to be documented!
- */
-class KoResourceTaggingManager : public QObject
-{
- Q_OBJECT
-
-public:
-
- explicit KoResourceTaggingManager(KoResourceModel* model, QWidget* parent);
- ~KoResourceTaggingManager() override;
- void showTaggingBar(bool show);
- QStringList availableTags() const;
- void contextMenuRequested(KoResource* currentResource, QPoint pos);
- void allowTagModification( bool set );
- bool allowTagModification();
- KoTagFilterWidget* tagFilterWidget();
- KoTagChooserWidget* tagChooserWidget();
-
-Q_SIGNALS:
- void updateView();
-
-private Q_SLOTS:
-
- void undeleteTag(const QString& tagToUndelete);
- void purgeTagUndeleteList();
- void contextCreateNewTag(KoResource* resource, const QString& tag);
- void contextCreateNewTag(const QString& tag);
- void syncTagBoxEntryRemoval(const QString& tag);
- void syncTagBoxEntryAddition(const QString& tag);
- void syncTagBoxEntries();
- void tagSaveButtonPressed();
- void contextRemoveTagFromResource(KoResource* resource, const QString& tag);
- void contextAddTagToResource(KoResource* resource, const QString& tag);
- void renameTag(const QString &oldName, const QString &newName);
- void tagChooserIndexChanged(const QString& lineEditText);
- void tagSearchLineEditTextChanged(const QString& lineEditText);
- void removeTagFromComboBox(const QString& tag);
-
-private:
- void contextMenuRequested(KoResource* resource, const QStringList& resourceTags, const QPoint& pos);
- void enableContextMenu(bool enable);
- void removeResourceTag(KoResource* resource, const QString& tagName);
- void addResourceTag(KoResource* resource, const QString& tagName);
- void updateTaggedResourceView();
- class Private;
- Private* const d;
-};
-
-
-#endif // KORESOURCETAGGINGINTERFACE_H
diff --git a/libs/widgets/KoTableView.cpp b/libs/widgets/KoTableView.cpp
deleted file mode 100644
index a03fd0f1ba..0000000000
--- a/libs/widgets/KoTableView.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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 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 "KoTableView.h"
-
-#include <QEvent>
-#include <QHeaderView>
-#include <QtMath>
-
-KoTableView::KoTableView(QWidget *parent)
- : QTableView(parent)
-{
- setSelectionMode(QAbstractItemView::SingleSelection);
- verticalHeader()->hide();
- horizontalHeader()->hide();
- verticalHeader()->setDefaultSectionSize(20);
- setContextMenuPolicy(Qt::DefaultContextMenu);
- setViewMode(FIXED_COLUMNS);
-
- QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(this);
- if (scroller) {
- connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChange(QScroller::State)));
- }
-}
-
-void KoTableView::resizeEvent(QResizeEvent *event)
-{
- QTableView::resizeEvent(event);
- updateView();
-
- emit sigSizeChanged();
-}
-
-void KoTableView::setViewMode(KoTableView::ViewMode mode)
-{
- m_viewMode = mode;
-
- switch (m_viewMode) {
- case FIXED_COLUMNS:
- setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Horizontal scrollbar is never needed
- setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
- break;
- case FIXED_ROWS:
- setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
- setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Vertical scrollbar is never needed
- break;
- default:
- setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
- setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
- }
-
-}
-
-void KoTableView::updateView()
-{
- int columnCount = model()->columnCount(QModelIndex());
- int rowCount = model()->rowCount(QModelIndex());
- int rowHeight, columnWidth;
-
- if (m_viewMode == FIXED_COLUMNS) {
- columnWidth = qFloor(viewport()->size().width() / static_cast<double>(columnCount));
-
- for (int i = 0; i < columnCount; ++i) {
- setColumnWidth(i, columnWidth);
- }
- // keep aspect ratio always square.
- if (columnCount > 1) {
- for (int i = 0; i < rowCount; ++i) {
- setRowHeight(i, columnWidth);
- }
- }
- } else if (m_viewMode == FIXED_ROWS) {
- if (rowCount == 0) return; // Don't divide by zero
- rowHeight = qFloor(viewport()->size().height() / static_cast<double>(rowCount));
-
- for (int i = 0; i < rowCount; ++i) {
- setRowHeight(i, rowHeight);
- }
- }
-}
diff --git a/libs/widgets/KoTableView.h b/libs/widgets/KoTableView.h
deleted file mode 100644
index f8afa41219..0000000000
--- a/libs/widgets/KoTableView.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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 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 KOTABLEVIEW_H
-#define KOTABLEVIEW_H
-
-#include <QTableView>
-#include <QScroller>
-
-#include "kritawidgets_export.h"
-#include <KisKineticScroller.h>
-
-class QEvent;
-class QModelIndex;
-
-/**
- * @brief The KoTableView class provides a QTableView with fixed columns or rows
- */
-class KRITAWIDGETS_EXPORT KoTableView: public QTableView
-{
- Q_OBJECT
-
-public:
- enum ViewMode {
- FIXED_COLUMNS, /// The number of columns is fixed
- FIXED_ROWS /// The number of rows is fixed
- };
-
- explicit KoTableView(QWidget *parent = 0);
- ~KoTableView() override {}
-
- /** reimplemented
- * This will draw a number of rows based on the number of columns if m_viewMode is FIXED_COLUMNS
- * And it will draw a number of columns based on the number of rows if m_viewMode is FIXED_ROWS
- */
- void resizeEvent(QResizeEvent *event) override;
-
- void setViewMode(ViewMode mode);
-
- void updateView();
-
-public Q_SLOTS:
- void slotScrollerStateChange(QScroller::State state){ KisKineticScroller::updateCursor(this, state); }
-
-Q_SIGNALS:
-
- void sigSizeChanged();
-
-private:
- ViewMode m_viewMode;
-};
-
-#endif // KOTABLEVIEW_H
diff --git a/libs/widgets/KoTagChooserWidget.cpp b/libs/widgets/KoTagChooserWidget.cpp
deleted file mode 100644
index f2b553ecfe..0000000000
--- a/libs/widgets/KoTagChooserWidget.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * 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 <KisSqueezedComboBox.h>
-
-#include <KoIcon.h>
-
-#include "KoResourceItemChooserContextMenu.h"
-
-#include "KoTagToolButton.h"
-
-class Q_DECL_HIDDEN KoTagChooserWidget::Private
-{
-public:
- KisSqueezedComboBox* comboBox;
- KoTagToolButton* tagToolButton;
- QStringList readOnlyTags;
- QStringList tags;
-};
-
-KoTagChooserWidget::KoTagChooserWidget(QWidget* parent): QWidget(parent)
- , d(new Private())
-{
- d->comboBox = new KisSqueezedComboBox(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(int)), this, SLOT(tagChanged(int)));
-
- 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::tagChanged(int)
-{
- emit tagChosen(d->comboBox->currentUnsqueezedText());
-}
-
-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->currentUnsqueezedText();
-}
-
-QStringList KoTagChooserWidget::allTags()
-{
- return d->tags;
-}
-
-bool KoTagChooserWidget::selectedTagIsReadOnly()
-{
- return d->readOnlyTags.contains(d->comboBox->currentUnsqueezedText()) ;
-}
-
-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/KoTagChooserWidget.h b/libs/widgets/KoTagChooserWidget.h
deleted file mode 100644
index 04e57577db..0000000000
--- a/libs/widgets/KoTagChooserWidget.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef KOTAGCHOOSERWIDGET_H
-#define KOTAGCHOOSERWIDGET_H
-
-#include <QWidget>
-#include "kritawidgets_export.h"
-
-class KRITAWIDGETS_EXPORT KoTagChooserWidget : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit KoTagChooserWidget(QWidget* parent);
- ~KoTagChooserWidget() override;
- void setCurrentIndex(int index);
- int findIndexOf(QString tagName);
- void insertItem(QString tagName);
- QString currentlySelectedTag();
- QStringList allTags();
- bool selectedTagIsReadOnly();
- void removeItem(QString item);
- void addItems(QStringList tagNames);
- void addReadOnlyItem(QString tagName);
- void clear();
- void setUndeletionCandidate(const QString &tag);
- void showTagToolButton(bool show);
-
-Q_SIGNALS:
- void newTagRequested(const QString &tagname);
- void tagDeletionRequested(const QString &tagname);
- void tagRenamingRequested(const QString &oldTagname, const QString &newTagname);
- void tagUndeletionRequested(const QString &tagname);
- void tagUndeletionListPurgeRequested();
- void popupMenuAboutToShow();
- void tagChosen(const QString &tag);
-
-private Q_SLOTS:
- void tagRenamingRequested(const QString &newName);
- void tagOptionsContextMenuAboutToShow();
- void contextDeleteCurrentTag();
- void tagChanged(int index);
-
-private:
- class Private;
- Private* const d;
-
-};
-;
-
-#endif // KOTAGCHOOSERWIDGET_H
diff --git a/libs/widgets/KoTagFilterWidget.cpp b/libs/widgets/KoTagFilterWidget.cpp
deleted file mode 100644
index 36448d79bf..0000000000
--- a/libs/widgets/KoTagFilterWidget.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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 "KoTagFilterWidget.h"
-
-#include <QPushButton>
-#include <QAction>
-#include <QGridLayout>
-
-#include <klineedit.h>
-#include <klocalizedstring.h>
-
-#include <KoIcon.h>
-
-class KoTagFilterWidget::Private
-{
-public:
- QString tagSearchBarTooltip_saving_disabled;
- QString tagSearchBarTooltip_saving_enabled;
- QLineEdit* tagSearchLineEdit;
- QPushButton* tagSearchSaveButton;
- QGridLayout* filterBarLayout;
-};
-
-KoTagFilterWidget::KoTagFilterWidget(QWidget* parent): QWidget(parent)
-,d( new Private())
-{
- QString searchTooltipMaintext = i18nc(
- "@info:tooltip",
- "<p>Enter search terms here to add resources to, or remove them from, the current tag view.</p>"
- "<p>To filter based on the partial, case insensitive name of a resource:<br/>"
- "<tt>partialname</tt> or <tt>!partialname</tt></p>"
- "<p>To include or exclude other tag sets:<br/>"
- "<tt>[Tagname]</tt> or <tt>![Tagname]</tt></p>"
- "<p>For case sensitive and full name matching in-/exclusion:<br/>"
- "<tt>\"ExactMatch\"</tt> or <tt>!\"ExactMatch\"</tt></p>");
-
- d->tagSearchBarTooltip_saving_disabled = searchTooltipMaintext + i18nc(
- "@info:tooltip",
- "<p>Filter results cannot be saved for the <b>All Presets</b> view. "
- "In this view, pressing <b>Enter</b> or clearing the filter box will restore all items. "
- "Create and/or switch to a different tag if you want to save filtered resources into named sets.</p>");
-
- d->tagSearchBarTooltip_saving_enabled = searchTooltipMaintext + i18nc(
- "@info:tooltip",
- "<p>Pressing <b>Enter</b> or clicking the <b>Save</b> button will save the changes.</p>");
-
- QGridLayout* filterBarLayout = new QGridLayout;
-
-
- d->tagSearchLineEdit = new QLineEdit(this);
- d->tagSearchLineEdit->setClearButtonEnabled(true);
- d->tagSearchLineEdit->setPlaceholderText(i18n("Search"));
- d->tagSearchLineEdit->setToolTip(d->tagSearchBarTooltip_saving_disabled);
- d->tagSearchLineEdit->setEnabled(true);
-
- filterBarLayout->setSpacing(0);
- filterBarLayout->setMargin(0);
- filterBarLayout->setColumnStretch(0, 1);
- filterBarLayout->addWidget(d->tagSearchLineEdit, 0, 0);
-
- d->tagSearchSaveButton = new QPushButton(this);
- d->tagSearchSaveButton->setIcon(koIcon("media-floppy"));
- d->tagSearchSaveButton->setToolTip(i18nc("@info:tooltip", "<qt>Save the currently filtered set as the new members of the current tag.</qt>"));
- d->tagSearchSaveButton->setEnabled(false);
-
- filterBarLayout->addWidget(d->tagSearchSaveButton, 0, 1);
-
- connect(d->tagSearchSaveButton, SIGNAL(pressed()),
- this, SLOT(onSaveButtonClicked()));
- connect(d->tagSearchLineEdit, SIGNAL(returnPressed()),
- this, SLOT(onSaveButtonClicked()));
- connect(d->tagSearchLineEdit, SIGNAL(textChanged(QString)),
- this, SLOT(onTextChanged(QString)));
- allowSave(false);
- this->setLayout(filterBarLayout);
-
-}
-
-KoTagFilterWidget::~KoTagFilterWidget()
-{
- delete d;
-}
-void KoTagFilterWidget::allowSave(bool allow)
-{
- if (allow) {
- d->tagSearchSaveButton->show();
- d->tagSearchLineEdit->setToolTip(d->tagSearchBarTooltip_saving_enabled);
- }
- else {
- d->tagSearchSaveButton->hide();
- d->tagSearchLineEdit->setToolTip(d->tagSearchBarTooltip_saving_disabled);
- }
-}
-
-void KoTagFilterWidget::clear()
-{
- d->tagSearchLineEdit->clear();
- d->tagSearchSaveButton->setEnabled(false);
-}
-
-void KoTagFilterWidget::onTextChanged(const QString& lineEditText)
-{
- d->tagSearchSaveButton->setEnabled(!lineEditText.isEmpty());
- emit filterTextChanged(lineEditText);
-}
-
-void KoTagFilterWidget::onSaveButtonClicked()
-{
- emit saveButtonClicked();
- clear();
-}
diff --git a/libs/widgets/KoTagFilterWidget.h b/libs/widgets/KoTagFilterWidget.h
deleted file mode 100644
index 917f6c3db6..0000000000
--- a/libs/widgets/KoTagFilterWidget.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef KOTAGFILTERWIDGET_H
-#define KOTAGFILTERWIDGET_H
-
-#include <QWidget>
-
-class KoTagFilterWidget : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit KoTagFilterWidget(QWidget* parent);
- ~KoTagFilterWidget() override;
- void allowSave(bool allow);
- void clear();
-
-Q_SIGNALS:
- void filterTextChanged(const QString &filterText);
- void saveButtonClicked();
-private Q_SLOTS:
- void onTextChanged(const QString &lineEditText);
- void onSaveButtonClicked();
-private:
- class Private;
- Private* const d;
-};
-
-#endif // KOTAGFILTERWIDGET_H
diff --git a/libs/widgets/KoTagToolButton.cpp b/libs/widgets/KoTagToolButton.cpp
deleted file mode 100644
index 44ca8bfa39..0000000000
--- a/libs/widgets/KoTagToolButton.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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 "KoTagToolButton.h"
-
-#include <QToolButton>
-#include <QGridLayout>
-
-#include <klocalizedstring.h>
-
-#include <KoIcon.h>
-
-#include "KoResourceItemChooserContextMenu.h"
-
-class KoTagToolButton::Private
-{
-public:
- QToolButton* tagToolButton;
- QAction* action_undeleteTag;
- QAction* action_deleteTag;
- KoLineEditAction* action_renameTag;
- QAction* action_purgeTagUndeleteList;
- QString undeleteCandidate;
-};
-
-KoTagToolButton::KoTagToolButton(QWidget* parent)
- :QWidget(parent), d(new Private())
-{
- QGridLayout* buttonLayout = new QGridLayout(this);
- buttonLayout->setMargin(0);
- buttonLayout->setSpacing(0);
-
- d->tagToolButton = new QToolButton(this);
- d->tagToolButton->setIcon(koIcon("bookmarks"));
- d->tagToolButton->setText(i18n("Tag"));
- d->tagToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
- d->tagToolButton->setToolTip(i18nc("@info:tooltip", "<qt>Show the tag box options.</qt>"));
- d->tagToolButton->setPopupMode(QToolButton::InstantPopup);
- d->tagToolButton->setEnabled(true);
-
- QMenu* popup = new QMenu(this);
-
- KoLineEditAction* addTagAction = new KoLineEditAction(popup);
- addTagAction->setPlaceholderText(i18n("New tag"));
- addTagAction->setIcon(koIcon("document-new"));
- addTagAction->closeParentOnTrigger(true);
- popup->addAction(addTagAction);
-
- connect(addTagAction, SIGNAL(triggered(QString)),
- this, SIGNAL(newTagRequested(QString)));
-
- d->action_renameTag = new KoLineEditAction(popup);
- d->action_renameTag->setPlaceholderText(i18n("Rename tag"));
- d->action_renameTag->setIcon(koIcon("edit-rename"));
- d->action_renameTag->closeParentOnTrigger(true);
- popup->addAction(d->action_renameTag);
-
- connect(d->action_renameTag, SIGNAL(triggered(QString)),
- this, SIGNAL(renamingOfCurrentTagRequested(QString)));
-
- popup->addSeparator();
-
- d->action_deleteTag = new QAction(popup);
- d->action_deleteTag->setText(i18n("Delete this tag"));
- d->action_deleteTag->setIcon(koIcon("edit-delete"));
- popup->addAction(d->action_deleteTag);
-
- connect(d->action_deleteTag, SIGNAL(triggered()),
- this, SIGNAL(deletionOfCurrentTagRequested()));
-
- popup->addSeparator();
-
- d->action_undeleteTag = new QAction(popup);
- d->action_undeleteTag->setIcon(koIcon("edit-redo"));
- d->action_undeleteTag->setVisible(false);
- popup->addAction(d->action_undeleteTag);
-
- connect(d->action_undeleteTag, SIGNAL(triggered()),
- this, SLOT(onTagUndeleteClicked()));
-
- d->action_purgeTagUndeleteList = new QAction(popup);
- d->action_purgeTagUndeleteList->setText(i18n("Clear undelete list"));
- d->action_purgeTagUndeleteList->setIcon(koIcon("edit-clear"));
- d->action_purgeTagUndeleteList->setVisible(false);
- popup->addAction(d->action_purgeTagUndeleteList);
-
- connect(d->action_purgeTagUndeleteList, SIGNAL(triggered()),
- this, SIGNAL(purgingOfTagUndeleteListRequested()));
-
- connect(popup, SIGNAL(aboutToShow()),
- this, SIGNAL(popupMenuAboutToShow()));
-
- d->tagToolButton->setMenu(popup);
- buttonLayout->addWidget(d->tagToolButton);
-}
-
-KoTagToolButton::~KoTagToolButton()
-{
- delete d;
-}
-
-void KoTagToolButton::readOnlyMode(bool activate)
-{
- activate = !activate;
- d->action_renameTag->setVisible(activate);
- d->action_deleteTag->setVisible(activate);
-}
-
-void KoTagToolButton::setUndeletionCandidate(const QString& deletedTagName)
-{
- d->undeleteCandidate = deletedTagName;
- d->action_undeleteTag->setText(i18n("Undelete") +" "+ deletedTagName);
- d->action_undeleteTag->setVisible(!deletedTagName.isEmpty());
- d->action_purgeTagUndeleteList->setVisible(!deletedTagName.isEmpty());
-}
-
-void KoTagToolButton::onTagUndeleteClicked()
-{
- emit undeletionOfTagRequested(d->undeleteCandidate);
-}
-
diff --git a/libs/widgets/KoTagToolButton.h b/libs/widgets/KoTagToolButton.h
deleted file mode 100644
index 3dcc903f5b..0000000000
--- a/libs/widgets/KoTagToolButton.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef KOTAGTOOLBUTTON_H
-#define KOTAGTOOLBUTTON_H
-
-#include <QWidget>
-
-class KoTagToolButton : public QWidget
-{
- Q_OBJECT
-
-private:
- explicit KoTagToolButton(QWidget* parent = 0);
- ~KoTagToolButton() override;
- void readOnlyMode(bool activate);
- void setUndeletionCandidate(const QString &deletedTagName);
-
-Q_SIGNALS:
- void newTagRequested(const QString &tagname);
- void renamingOfCurrentTagRequested(const QString &tagname);
- void deletionOfCurrentTagRequested();
- void undeletionOfTagRequested(const QString &tagname);
- void purgingOfTagUndeleteListRequested();
- void popupMenuAboutToShow();
-
-private Q_SLOTS:
- void onTagUndeleteClicked();
-
-private:
- class Private;
- Private* const d;
- friend class KoTagChooserWidget;
-};
-
-#endif // KOTAGTOOLBUTTON_H
diff --git a/libs/widgets/WdgDlgInternalColorSelector.ui b/libs/widgets/WdgDlgInternalColorSelector.ui
index 6a17a05e6a..e31b2b9888 100644
--- a/libs/widgets/WdgDlgInternalColorSelector.ui
+++ b/libs/widgets/WdgDlgInternalColorSelector.ui
@@ -1,273 +1,273 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgDlgInternalColorSelector</class>
<widget class="QDialog" name="WdgDlgInternalColorSelector">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>505</width>
<height>483</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontal">
<item>
<layout class="QVBoxLayout" name="leftPane">
<item>
<widget class="KisVisualColorSelector" name="visualSelector" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>90</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="KisSpinboxColorSelector" name="spinboxselector" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>2</number>
</property>
<property name="midLineWidth">
<number>2</number>
</property>
<layout class="QHBoxLayout" name="nextprevlayout">
<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="KoColorPatch" name="currentColor" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>70</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="KoColorPatch" name="previousColor" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="rightPane">
<item>
<widget class="KisPaletteView" name="paletteBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="KisPopupButton" name="bnPaletteChooser">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="KisPaletteComboBox" name="cmbNameList">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="screenColorPickerWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisPopupButton</class>
<extends>QPushButton</extends>
- <header>kis_popup_button.h</header>
+ <header>KisPopupButton.h</header>
</customwidget>
<customwidget>
<class>KisPaletteView</class>
<extends>QTableView</extends>
<header location="global">kis_palette_view.h</header>
</customwidget>
<customwidget>
<class>KisSpinboxColorSelector</class>
<extends>QWidget</extends>
<header>kis_spinbox_color_selector.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisVisualColorSelector</class>
<extends>QWidget</extends>
<header>KisVisualColorSelector.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KoColorPatch</class>
<extends>QWidget</extends>
<header>KoColorPatch.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisPaletteComboBox</class>
<extends>QComboBox</extends>
<header>KisPaletteComboBox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>WdgDlgInternalColorSelector</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>WdgDlgInternalColorSelector</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
diff --git a/libs/widgets/kis_palette_view.cpp b/libs/widgets/kis_palette_view.cpp
index 09cbb7498d..e98b638ef2 100644
--- a/libs/widgets/kis_palette_view.cpp
+++ b/libs/widgets/kis_palette_view.cpp
@@ -1,300 +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>
+#include <KisResourceModel.h>
+#include <KisResourceModelProvider.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)));
}
connect(this, SIGNAL(clicked(QModelIndex)), SLOT(slotCurrentSelectionChanged(QModelIndex)));
}
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);
+ saveModification();
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();
+ saveModification();
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();
- }
+
+ saveModification();
+
return true;
}
void KisPaletteView::selectClosestColor(const KoColor &color)
{
- KoColorSet* color_set = m_d->model->colorSet();
+ KoColorSetSP 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);
}
const KoColor KisPaletteView::closestColor(const KoColor &color) const
{
QModelIndex index = m_d->model->indexForClosest(color);
KisSwatch swatch = m_d->model->getEntry(index);
return swatch.color();
}
void KisPaletteView::slotFGColorChanged(const KoColor &color)
{
selectClosestColor(color);
}
void KisPaletteView::setPaletteModel(KisPaletteModel *model)
{
if (m_d->model) {
disconnect(m_d->model, 0, this, 0);
}
m_d->model = model;
setModel(model);
slotAdditionalGuiUpdate();
connect(model, SIGNAL(sigPaletteModified()), SLOT(slotAdditionalGuiUpdate()));
connect(model, SIGNAL(sigPaletteChanged()), SLOT(slotAdditionalGuiUpdate()));
}
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::saveModification()
+{
+ qDebug() << "saving modification in palette view" << m_d->model->colorSet()->filename() << m_d->model->colorSet()->storageLocation();
+ KisResourceModel *model = KisResourceModelProvider::resourceModel(m_d->model->colorSet()->resourceType().first);
+ model->updateResource(m_d->model->colorSet());
+}
+
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 65bae3620f..d8eff6e116 100644
--- a/libs/widgets/kis_palette_view.h
+++ b/libs/widgets/kis_palette_view.h
@@ -1,130 +1,131 @@
/*
* 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 = 0);
~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);
/**
* @brief closestColor
* determines closest swatch in the active palette and returns it's color as KoColor
* @param color
* @return KoColor
*/
const KoColor closestColor(const KoColor& color) const;
/**
* add an entry with a dialog window.
* @warning deprecated.
* 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 PaletteView in libkis
*/
bool removeEntryWithDialog(QModelIndex index);
/**
* add entry with a dialog window.
* @warning deprecated.
* 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 &);
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);
+ void saveModification();
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif /* __KIS_PALETTE_VIEW_H */
diff --git a/libs/widgets/kis_popup_button.cc b/libs/widgets/kis_popup_button.cc
deleted file mode 100644
index 05e0a129b8..0000000000
--- a/libs/widgets/kis_popup_button.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
- * 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.
- */
-
-#include "kis_popup_button.h"
-
-#include <QPointer>
-#include <QApplication>
-#include <QDesktopWidget>
-#include <QFrame>
-#include <QHBoxLayout>
-#include <QStyleOption>
-#include <QStylePainter>
-
-#include "kis_global.h"
-#include <kis_debug.h>
-
-struct KisPopupButton::Private {
- Private()
- : frameLayout(0)
- {}
- QScopedPointer<QFrame> frame;
- QPointer<QWidget> popupWidget;
- QPointer<QHBoxLayout> frameLayout;
-};
-
-KisPopupButton::KisPopupButton(QWidget* parent)
- : QPushButton(parent)
- , m_d(new Private)
-{
- setObjectName("KisPopupButton");
- connect(this, SIGNAL(released()), SLOT(showPopupWidget()));
-}
-
-KisPopupButton::~KisPopupButton()
-{
- delete m_d;
-}
-
-void KisPopupButton::setAlwaysVisible(bool v)
-{
- if (v) {
- m_d->frame->setFrameStyle(Qt::SubWindow);
- showPopupWidget();
- } else {
- m_d->frame->setFrameStyle(Qt::Popup);
- }
-}
-
-void KisPopupButton::setPopupWidget(QWidget* widget)
-{
- if (widget) {
- /**
- * Set parent explicitly to null to avoid propagation of the
- * ignored events (specifically, QEvent::ShortcutOverride) to
- * the view object. This avoids starting global actions while
- * the PopUp is active. See bug 329842.
- */
- m_d->frame.reset(new QFrame(0));
- m_d->frame->setObjectName("popup frame");
- m_d->frame->setFrameStyle(QFrame::Box | QFrame::Plain);
- m_d->frame->setWindowFlags(Qt::Popup);
- m_d->frameLayout = new QHBoxLayout(m_d->frame.data());
- m_d->frameLayout->setMargin(0);
- m_d->frameLayout->setSizeConstraint(QLayout::SetFixedSize);
- m_d->frame->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
- m_d->popupWidget = widget;
- m_d->popupWidget->setParent(m_d->frame.data());
- m_d->frameLayout->addWidget(m_d->popupWidget);
- }
-}
-
-void KisPopupButton::setPopupWidgetWidth(int w)
-{
- m_d->frame->resize(w, m_d->frame->height());
-}
-
-void KisPopupButton::showPopupWidget()
-{
- if (m_d->popupWidget && !m_d->frame->isVisible()) {
- m_d->frame->raise();
- m_d->frame->show();
- adjustPosition();
- }
- else {
- hidePopupWidget();
- }
-}
-
-void KisPopupButton::hidePopupWidget()
-{
- if (m_d->popupWidget) {
- m_d->frame->setVisible(false);
- }
-}
-
-void KisPopupButton::paintEvent ( QPaintEvent * event )
-{
- QPushButton::paintEvent(event);
- paintPopupArrow();
-}
-
-void KisPopupButton::paintPopupArrow()
-{
- QStylePainter p(this);
- QStyleOption option;
- option.rect = QRect(rect().right() - 15, rect().bottom() - 15, 14, 14);
- option.palette = palette();
- option.palette.setBrush(QPalette::ButtonText, Qt::black); // Force color to black
- option.state = QStyle::State_Enabled;
- p.setBrush(Qt::black); // work around some theme that don't use QPalette::ButtonText like they should, but instead the QPainter brushes and pen
- p.setPen(Qt::black);
- p.drawPrimitive(QStyle::PE_IndicatorArrowDown, option);
-}
-
-void KisPopupButton::adjustPosition()
-{
- QSize popSize = m_d->popupWidget->size();
- QRect popupRect(this->mapToGlobal(QPoint(0, this->size().height())), popSize);
-
- // Get the available geometry of the screen which contains this KisPopupButton
- QDesktopWidget* desktopWidget = QApplication::desktop();
- QRect screenRect = desktopWidget->availableGeometry(this);
- popupRect = kisEnsureInRect(popupRect, screenRect);
-
- m_d->frame->setGeometry(popupRect);
-}
-
diff --git a/libs/widgets/kis_popup_button.h b/libs/widgets/kis_popup_button.h
deleted file mode 100644
index 1e25a80cca..0000000000
--- a/libs/widgets/kis_popup_button.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 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_POPUP_BUTTON_H_
-#define _KIS_POPUP_BUTTON_H_
-
-#include <QPushButton>
-
-#include <kritawidgets_export.h>
-
-/**
- * This class is a convenience class for a button that
- * when clicked displays a popup widget.
- */
-class KRITAWIDGETS_EXPORT KisPopupButton : public QPushButton
-{
-
- Q_OBJECT
-
-public:
-
- KisPopupButton(QWidget* parent);
- ~KisPopupButton() override;
-
- /**
- * Set the popup widget, the KisPopupButton becomes
- * the owner and parent of the widget.
- */
- void setPopupWidget(QWidget* widget);
-
- /**
- * This function allow to force the popup to be visible.
- * @param v set to true to force the popup to be visible, set to false
- * to allow the popup to be hidden
- */
- void setAlwaysVisible(bool v);
-
- /**
- * Set the width of the popup widget.
- * @return new width of the popup widget
- */
- void setPopupWidgetWidth(int w);
-
- /**
- * @brief adjustPosition
- * adjusts the position of the popup widget based on the position
- * of this button and the size of the widget
- */
- void adjustPosition();
-
-public Q_SLOTS:
-
- void showPopupWidget();
-
- void hidePopupWidget();
-
-protected:
- void paintEvent(QPaintEvent* event) override;
-
- void paintPopupArrow();
-private:
- struct Private;
- Private* const m_d;
-};
-
-#endif
diff --git a/libs/widgets/tests/CMakeLists.txt b/libs/widgets/tests/CMakeLists.txt
index 7cb62d2545..80f4e92506 100644
--- a/libs/widgets/tests/CMakeLists.txt
+++ b/libs/widgets/tests/CMakeLists.txt
@@ -1,17 +1,16 @@
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/")
add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}")
#add_subdirectory(filedialogtester)
include(ECMAddTests)
include(KritaAddBrokenUnitTest)
ecm_add_tests(
zoomhandler_test.cpp
zoomcontroller_test.cpp
- KoResourceTaggingTest.cpp
KoAnchorSelectionWidgetTest.cpp
NAME_PREFIX "libs-widgets-"
LINK_LIBRARIES kritawidgets Qt5::Test)
diff --git a/libs/widgets/tests/KoResourceTaggingTest.cpp b/libs/widgets/tests/KoResourceTaggingTest.cpp
deleted file mode 100644
index 5d5a0b8364..0000000000
--- a/libs/widgets/tests/KoResourceTaggingTest.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * 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 "KoResourceTaggingTest.h"
-
-#include <QTest>
-#include <QCoreApplication>
-
-#include <kactioncollection.h>
-#include <WidgetsDebug.h>
-
-#include <resources/KoResource.h>
-#include "KoResourceServerProvider.h"
-
-#include "sdk/tests/kistest.h"
-
-void KoResourceTaggingTest::testInitialization()
-{
- // All Krita's resource types
- KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/");
- KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc");
- KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/");
- KoResourcePaths::addResourceType("kis_actions", "data", "/actions");
- KoResourcePaths::addResourceType("kis_brushes", "data", "/brushes/");
- KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/");
- KoResourcePaths::addResourceType("kis_images", "data", "/images/");
- KoResourcePaths::addResourceType("kis_paintoppresets", "data", "/paintoppresets/");
- KoResourcePaths::addResourceType("kis_pics", "data", "/pics/");
- KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/");
- KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/");
- KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/");
- KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/");
- KoResourcePaths::addResourceType("kis_windowlayouts", "data", "/windowlayouts/");
- KoResourcePaths::addResourceType("kis_workspaces", "data", "/workspaces/");
- KoResourcePaths::addResourceType("ko_effects", "data", "/effects/");
- KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/");
- KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/");
- KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/");
- KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/");
- KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl");
- KoResourcePaths::addResourceType("tags", "data", "/tags/");
-
- KoResourceTagStore tagStore(KoResourceServerProvider::instance()->patternServer());
- QVERIFY(tagStore.tagNamesList().isEmpty());
- QVERIFY(tagStore.assignedTagsList(0).isEmpty());
- QVERIFY(tagStore.searchTag("bla").isEmpty());
-}
-
-void KoResourceTaggingTest::testTagging()
-{
- KoResourceServer<KoPattern>* patServer = KoResourceServerProvider::instance()->patternServer();
- KoResourceTagStore tagStore(patServer);
- KoResource *res = patServer->resources().first();
- QVERIFY(res);
- QVERIFY(patServer->resourceByFilename(res->shortFilename()) == res);
-
- tagStore.addTag(res, "test1");
- QVERIFY(tagStore.tagNamesList().size() == 1);
- QStringList resources = tagStore.searchTag("test1");
- QVERIFY(resources.size() == 1);
- KoResource *res2 = patServer->resourceByFilename(resources.first());
- QVERIFY(res == res2);
-
- tagStore.addTag(res, "test2");
- QVERIFY(tagStore.tagNamesList().size() == 2);
- resources = tagStore.searchTag("test1");
- QVERIFY(resources.size() == 1);
- res2 = patServer->resourceByFilename(resources.first());
- QVERIFY(res == res2);
-
- tagStore.addTag(res, "test2");
- QVERIFY(tagStore.tagNamesList().size() == 2);
-
- resources = tagStore.searchTag("test2");
- QVERIFY(resources.size() == 1);
- res2 = patServer->resourceByFilename(resources.first());
- QVERIFY(res == res2);
-
- resources = tagStore.searchTag("test1,test2");
- QVERIFY(resources.size() == 1);
- res2 = patServer->resourceByFilename(resources.first());
- QVERIFY(res == res2);
-
- tagStore.delTag(res, "test1");
- QVERIFY(tagStore.tagNamesList().size() == 2);
- resources = tagStore.searchTag("test1");
- QVERIFY(resources.size() == 0);
-
- resources = tagStore.searchTag("test2");
- QVERIFY(resources.size() == 1);
- res2 = patServer->resourceByFilename(resources.first());
- QVERIFY(res == res2);
-
- tagStore.delTag("test1");
- QVERIFY(tagStore.tagNamesList().size() == 1);
-
-}
-
-void KoResourceTaggingTest::testReadWriteXML()
-{
- KoResourceServer<KoPattern>* patServer = KoResourceServerProvider::instance()->patternServer();
- KoResourceTagStore tagStore(patServer);
-
- QList<KoPattern*> patterns = patServer->resources();
- Q_ASSERT(patterns.size() > 5);
- tagStore.addTag(patterns[0], "test0");
- tagStore.addTag(patterns[1], "test1");
- tagStore.addTag(patterns[2], "test2");
- tagStore.addTag(patterns[2], "test2");
- tagStore.addTag(patterns[2], "test1");
- tagStore.addTag(patterns[3], "test3");
- tagStore.addTag(patterns[4], "test4");
- tagStore.addTag(patterns[5], "test5");
- tagStore.addTag(patterns[5], "test5.1");
- tagStore.addTag(patterns[5], "test5.2");
- tagStore.addTag(0, "dummytest");
-
- QVERIFY(tagStore.tagNamesList().size() == 9);
-
- tagStore.writeXMLFile(QString(FILES_OUTPUT_DIR) + "/" + "kis_pattern_tags.xml");
-
- KoResourceTagStore tagStore2(patServer);
- tagStore2.readXMLFile(QString(FILES_OUTPUT_DIR) + "/" + "kis_pattern_tags.xml");
-
- QVERIFY(tagStore2.tagNamesList().size() == 9);
- QStringList resources = tagStore2.searchTag("test0");
- QVERIFY(resources.size() == 1);
- QVERIFY(patServer->resourceByFilename(resources[0]) == patterns[0]);
-
- resources = tagStore2.searchTag("test1");
- QVERIFY(resources.size() == 2);
-
- resources = tagStore2.searchTag("test2");
- QVERIFY(resources.size() == 1);
-
- resources = tagStore2.searchTag("test3");
- QVERIFY(resources.size() == 1);
-
- resources = tagStore2.searchTag("test4");
- QVERIFY(resources.size() == 1);
-
- resources = tagStore2.searchTag("test5");
- QVERIFY(resources.size() == 1);
-
- resources = tagStore2.searchTag("test5.1");
- QVERIFY(resources.size() == 1);
-
- resources = tagStore2.searchTag("test5.2");
- QVERIFY(resources.size() == 1);
-
- resources = tagStore2.searchTag("dummytest");
- QVERIFY(resources.size() == 0);
-}
-
-KISTEST_MAIN(KoResourceTaggingTest)
diff --git a/libs/widgets/tests/KoResourceTaggingTest.h b/libs/widgets/tests/KoResourceTaggingTest.h
deleted file mode 100644
index 748ee63e5d..0000000000
--- a/libs/widgets/tests/KoResourceTaggingTest.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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 KORESOURCETAGGING_TEST_H
-#define KORESOURCETAGGING_TEST_H
-
-#include <QObject>
-#include <KoConfig.h>
-#include "KoResourceTagStore.h"
-
-class KoResourceTaggingTest : public QObject
-{
- Q_OBJECT
-
-private Q_SLOTS:
-
- // tests
- void testInitialization();
- void testTagging();
- void testReadWriteXML();
-};
-
-#endif
diff --git a/libs/widgetutils/CMakeLists.txt b/libs/widgetutils/CMakeLists.txt
index c62eeafb79..5ec2533a7d 100644
--- a/libs/widgetutils/CMakeLists.txt
+++ b/libs/widgetutils/CMakeLists.txt
@@ -1,140 +1,143 @@
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
+ KoCheckerBoardPainter.cpp
+ KoItemToolTip.cpp
KisSqueezedComboBox.cpp
KisDialogStateSaver.cpp
+ KisPopupButton.cpp
kis_cursor.cc
kis_cursor_cache.cpp
kis_double_parse_spin_box.cpp
kis_double_parse_unit_spin_box.cpp
kis_int_parse_spin_box.cpp
kis_num_parser.cpp
kis_slider_spin_box.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/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
+ kritaresources
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/KisActionsSnapshot.cpp b/libs/widgetutils/KisActionsSnapshot.cpp
index 9398b0c653..d75aaf53a7 100644
--- a/libs/widgetutils/KisActionsSnapshot.cpp
+++ b/libs/widgetutils/KisActionsSnapshot.cpp
@@ -1,106 +1,105 @@
/*
* 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 "KisActionsSnapshot.h"
#include "kis_action_registry.h"
#include "./kactioncollection.h"
#include "kis_debug.h"
//#define ACTIONS_CHECKSUM_SANITY_CHECK
struct KisActionsSnapshot::Private
{
QMap<QString, KActionCollection*> actionCollections;
~Private() {
qDeleteAll(actionCollections);
qDeleteAll(fakeActions);
}
QSet<QString> nonRegisteredShortcuts;
QVector<QAction*> fakeActions;
};
KisActionsSnapshot::KisActionsSnapshot()
: m_d(new Private)
{
- m_d->nonRegisteredShortcuts =
- QSet<QString>::fromList(
- KisActionRegistry::instance()->registeredShortcutIds());
+ QList<QString> registeredShortcutIds = KisActionRegistry::instance()->registeredShortcutIds();
+ m_d->nonRegisteredShortcuts = QSet<QString>(registeredShortcutIds.begin(), registeredShortcutIds.end());
}
KisActionsSnapshot::~KisActionsSnapshot()
{
}
void KisActionsSnapshot::addAction(const QString &name, QAction *action)
{
m_d->nonRegisteredShortcuts.remove(name);
KisActionRegistry::ActionCategory cat = KisActionRegistry::instance()->fetchActionCategory(name);
if (!cat.isValid()) {
warnKrita << "WARNING: Uncategorized action" << name << "Dropping...";
return;
}
#ifdef ACTIONS_CHECKSUM_SANITY_CHECK
if (!KisActionRegistry::instance()->sanityCheckPropertized(action->objectName())) {
warnKrita << "WARNING: action" << name << "was not propertized!" << ppVar(action->property("isShortcutConfigurable").toBool());
}
#endif /* ACTIONS_CHECKSUM_SANITY_CHECK */
KActionCollection *collection = m_d->actionCollections[cat.componentName];
if (!collection) {
collection = new KActionCollection(0, cat.componentName);
m_d->actionCollections.insert(cat.componentName, collection);
}
collection->addCategorizedAction(name, action, cat.categoryName);
}
QMap<QString, KActionCollection *> KisActionsSnapshot::actionCollections()
{
/**
* A small heuristics to show warnings only when unknown shortcuts appear
* in the non-registered list
*/
if (m_d->nonRegisteredShortcuts.size() > 4 &&
m_d->nonRegisteredShortcuts.size() < 160) {
warnKrita << "WARNING: The following shortcuts are not registered in the collection, "
"they might have wrong shortcuts in the end:";
Q_FOREACH (const QString &str, m_d->nonRegisteredShortcuts) {
warnKrita << str;
}
warnKrita << "=== end ===";
}
// try to workaround non-registered shortcuts by faking them manually
Q_FOREACH (const QString &str, m_d->nonRegisteredShortcuts) {
QAction *action = KisActionRegistry::instance()->makeQAction(str, 0);
m_d->fakeActions << action;
addAction(action->objectName(), action);
}
return m_d->actionCollections;
}
diff --git a/libs/widgetutils/KisPopupButton.cpp b/libs/widgetutils/KisPopupButton.cpp
new file mode 100644
index 0000000000..28a18838c1
--- /dev/null
+++ b/libs/widgetutils/KisPopupButton.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
+ * 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.
+ */
+
+#include "KisPopupButton.h"
+
+#include <QPointer>
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QFrame>
+#include <QHBoxLayout>
+#include <QStyleOption>
+#include <QStylePainter>
+
+#include "kis_global.h"
+#include <kis_debug.h>
+
+struct KisPopupButton::Private {
+ Private()
+ : frameLayout(0)
+ {}
+ QScopedPointer<QFrame> frame;
+ QPointer<QWidget> popupWidget;
+ QPointer<QHBoxLayout> frameLayout;
+};
+
+KisPopupButton::KisPopupButton(QWidget* parent)
+ : QPushButton(parent)
+ , m_d(new Private)
+{
+ setObjectName("KisPopupButton");
+ connect(this, SIGNAL(released()), SLOT(showPopupWidget()));
+}
+
+KisPopupButton::~KisPopupButton()
+{
+ delete m_d;
+}
+
+void KisPopupButton::setAlwaysVisible(bool v)
+{
+ if (v) {
+ m_d->frame->setFrameStyle(Qt::SubWindow);
+ showPopupWidget();
+ } else {
+ m_d->frame->setFrameStyle(Qt::Popup);
+ }
+}
+
+void KisPopupButton::setPopupWidget(QWidget* widget)
+{
+ if (widget) {
+ /**
+ * Set parent explicitly to null to avoid propagation of the
+ * ignored events (specifically, QEvent::ShortcutOverride) to
+ * the view object. This avoids starting global actions while
+ * the PopUp is active. See bug 329842.
+ */
+ m_d->frame.reset(new QFrame(0));
+ m_d->frame->setObjectName("popup frame");
+ m_d->frame->setFrameStyle(QFrame::Box | QFrame::Plain);
+ m_d->frame->setWindowFlags(Qt::Popup);
+ m_d->frameLayout = new QHBoxLayout(m_d->frame.data());
+ m_d->frameLayout->setMargin(0);
+ m_d->frameLayout->setSizeConstraint(QLayout::SetFixedSize);
+ m_d->frame->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
+ m_d->popupWidget = widget;
+ m_d->popupWidget->setParent(m_d->frame.data());
+ m_d->frameLayout->addWidget(m_d->popupWidget);
+ }
+}
+
+void KisPopupButton::setPopupWidgetWidth(int w)
+{
+ m_d->frame->resize(w, m_d->frame->height());
+}
+
+void KisPopupButton::showPopupWidget()
+{
+ if (m_d->popupWidget && !m_d->frame->isVisible()) {
+ m_d->frame->raise();
+ m_d->frame->show();
+ adjustPosition();
+ }
+ else {
+ hidePopupWidget();
+ }
+}
+
+void KisPopupButton::hidePopupWidget()
+{
+ if (m_d->popupWidget) {
+ m_d->frame->setVisible(false);
+ }
+}
+
+void KisPopupButton::paintEvent ( QPaintEvent * event )
+{
+ QPushButton::paintEvent(event);
+ paintPopupArrow();
+}
+
+void KisPopupButton::paintPopupArrow()
+{
+ QStylePainter p(this);
+ QStyleOption option;
+ option.rect = QRect(rect().right() - 15, rect().bottom() - 15, 14, 14);
+ option.palette = palette();
+ option.palette.setBrush(QPalette::ButtonText, Qt::black); // Force color to black
+ option.state = QStyle::State_Enabled;
+ p.setBrush(Qt::black); // work around some theme that don't use QPalette::ButtonText like they should, but instead the QPainter brushes and pen
+ p.setPen(Qt::black);
+ p.drawPrimitive(QStyle::PE_IndicatorArrowDown, option);
+}
+
+void KisPopupButton::adjustPosition()
+{
+ QSize popSize = m_d->popupWidget->size();
+ QRect popupRect(this->mapToGlobal(QPoint(0, this->size().height())), popSize);
+
+ // Get the available geometry of the screen which contains this KisPopupButton
+ QDesktopWidget* desktopWidget = QApplication::desktop();
+ QRect screenRect = desktopWidget->availableGeometry(this);
+ popupRect = kisEnsureInRect(popupRect, screenRect);
+
+ m_d->frame->setGeometry(popupRect);
+}
+
diff --git a/libs/widgetutils/KisPopupButton.h b/libs/widgetutils/KisPopupButton.h
new file mode 100644
index 0000000000..6755b401cb
--- /dev/null
+++ b/libs/widgetutils/KisPopupButton.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 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_POPUP_BUTTON_H_
+#define _KIS_POPUP_BUTTON_H_
+
+#include <QPushButton>
+
+#include <kritawidgetutils_export.h>
+
+/**
+ * This class is a convenience class for a button that
+ * when clicked displays a popup widget.
+ */
+class KRITAWIDGETUTILS_EXPORT KisPopupButton : public QPushButton
+{
+
+ Q_OBJECT
+
+public:
+
+ KisPopupButton(QWidget* parent);
+ ~KisPopupButton() override;
+
+ /**
+ * Set the popup widget, the KisPopupButton becomes
+ * the owner and parent of the widget.
+ */
+ void setPopupWidget(QWidget* widget);
+
+ /**
+ * This function allow to force the popup to be visible.
+ * @param v set to true to force the popup to be visible, set to false
+ * to allow the popup to be hidden
+ */
+ void setAlwaysVisible(bool v);
+
+ /**
+ * Set the width of the popup widget.
+ * @return new width of the popup widget
+ */
+ void setPopupWidgetWidth(int w);
+
+ /**
+ * @brief adjustPosition
+ * adjusts the position of the popup widget based on the position
+ * of this button and the size of the widget
+ */
+ void adjustPosition();
+
+public Q_SLOTS:
+
+ void showPopupWidget();
+
+ void hidePopupWidget();
+
+protected:
+ void paintEvent(QPaintEvent* event) override;
+
+ void paintPopupArrow();
+private:
+ struct Private;
+ Private* const m_d;
+};
+
+#endif
diff --git a/libs/widgetutils/KoCheckerBoardPainter.cpp b/libs/widgetutils/KoCheckerBoardPainter.cpp
new file mode 100644
index 0000000000..0161f42af5
--- /dev/null
+++ b/libs/widgetutils/KoCheckerBoardPainter.cpp
@@ -0,0 +1,58 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 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 "KoCheckerBoardPainter.h"
+#include <QPainter>
+
+KoCheckerBoardPainter::KoCheckerBoardPainter(int checkerSize)
+ : m_checkerSize(checkerSize)
+ , m_lightColor(Qt::lightGray)
+ , m_darkColor(Qt::darkGray)
+{
+ createChecker();
+}
+
+void KoCheckerBoardPainter::setCheckerColors(const QColor &lightColor, const QColor &darkColor)
+{
+ m_lightColor = lightColor;
+ m_darkColor = darkColor;
+ createChecker();
+}
+
+void KoCheckerBoardPainter::setCheckerSize(int checkerSize)
+{
+ m_checkerSize = checkerSize;
+ createChecker();
+}
+
+void KoCheckerBoardPainter::paint(QPainter &painter, const QRectF &rect) const
+{
+ painter.fillRect(rect, QBrush(m_checker));
+}
+
+void KoCheckerBoardPainter::createChecker()
+{
+ m_checker = QPixmap(2 * m_checkerSize, 2 * m_checkerSize);
+ QPainter p(&m_checker);
+ p.fillRect(0, 0, m_checkerSize, m_checkerSize, m_lightColor);
+ p.fillRect(m_checkerSize, 0, m_checkerSize, m_checkerSize, m_darkColor);
+ p.fillRect(0, m_checkerSize, m_checkerSize, m_checkerSize, m_darkColor);
+ p.fillRect(m_checkerSize, m_checkerSize, m_checkerSize, m_checkerSize, m_lightColor);
+ p.end();
+}
diff --git a/libs/widgetutils/KoCheckerBoardPainter.h b/libs/widgetutils/KoCheckerBoardPainter.h
new file mode 100644
index 0000000000..d172e714f3
--- /dev/null
+++ b/libs/widgetutils/KoCheckerBoardPainter.h
@@ -0,0 +1,45 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 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 KOCHECKERBOARDPAINTER_H
+#define KOCHECKERBOARDPAINTER_H
+
+#include <QPixmap>
+#include <QColor>
+#include "kritawidgetutils_export.h"
+
+class QPainter;
+
+class KRITAWIDGETUTILS_EXPORT KoCheckerBoardPainter
+{
+public:
+ explicit KoCheckerBoardPainter(int checkerSize);
+ void setCheckerColors(const QColor &lightColor, const QColor &darkColor);
+ void setCheckerSize(int checkerSize);
+ void paint(QPainter &painter, const QRectF &rect) const;
+
+private:
+ void createChecker();
+ int m_checkerSize;
+ QPixmap m_checker;
+ QColor m_lightColor;
+ QColor m_darkColor;
+};
+
+#endif // KOCHECKERBOARDPAINTER_H
diff --git a/libs/widgets/KoItemToolTip.cpp b/libs/widgetutils/KoItemToolTip.cpp
similarity index 100%
rename from libs/widgets/KoItemToolTip.cpp
rename to libs/widgetutils/KoItemToolTip.cpp
diff --git a/libs/widgetutils/KoItemToolTip.h b/libs/widgetutils/KoItemToolTip.h
new file mode 100644
index 0000000000..174daa2449
--- /dev/null
+++ b/libs/widgetutils/KoItemToolTip.h
@@ -0,0 +1,84 @@
+/*
+ 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 KO_ITEM_TOOLTIP_H
+#define KO_ITEM_TOOLTIP_H
+
+#include <QFrame>
+#include "kritawidgetutils_export.h"
+
+class QStyleOptionViewItem;
+class QModelIndex;
+class QTextDocument;
+
+/**
+ * Base class for tooltips that can show extensive information about
+ * the contents of the data pointed to by something that contains a
+ * QModelIndex. Subclasses need to use this data to create a
+ * QTextDocument that is formatted to provide the complete tooltip.
+ */
+class KRITAWIDGETUTILS_EXPORT KoItemToolTip : public QFrame
+{
+ Q_OBJECT
+public:
+ KoItemToolTip();
+ ~KoItemToolTip() override;
+ void showTip(QWidget *widget, const QPoint &pos, const QStyleOptionViewItem &option, const QModelIndex &index);
+
+protected:
+
+ /**
+ * Re-implement this to provide the actual tooltip contents.
+ * For instance:
+ * @code
+ * QTextDocument *doc = new QTextDocument(this);
+ *
+ * QImage thumb = index.data(Qt::UserRole + KisResourceModel::LargeThumbnail).value<QImage>();
+ * doc->addResource(QTextDocument::ImageResource, QUrl("data:thumbnail"), thumb);
+ *
+ * QString name = index.data(Qt::DisplayRole).toString();
+ *
+ * const QString image = QString("<img src=\"data:thumbnail\">");
+ * const QString body = QString("<h3 align=\"center\">%1</h3>").arg(name) + image;
+ * const QString html = QString("<html><body>%1</body></html>").arg(body);
+ *
+ * doc->setHtml(html);
+ * doc->setTextWidth(qMin(doc->size().width(), 500.0));
+ *
+ * return doc;
+ * @endcode
+ */
+ virtual QTextDocument *createDocument(const QModelIndex &index) = 0;
+
+private:
+ class Private;
+ Private* const d;
+
+ void updatePosition(QWidget *widget, const QPoint &pos, const QStyleOptionViewItem &option);
+
+public:
+ QSize sizeHint() const override;
+
+protected:
+ void paintEvent(QPaintEvent *e) override;
+ void timerEvent(QTimerEvent *e) override;
+ bool eventFilter(QObject *object, QEvent *event) override;
+};
+
+#endif
diff --git a/libs/widgetutils/KoResourcePaths.cpp b/libs/widgetutils/KoResourcePaths.cpp
deleted file mode 100644
index 7cc3b4a3c5..0000000000
--- a/libs/widgetutils/KoResourcePaths.cpp
+++ /dev/null
@@ -1,602 +0,0 @@
-/*
- * 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; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#include "KoResourcePaths.h"
-
-#include <QGlobalStatic>
-#include <QString>
-#include <QStringList>
-#include <QMap>
-#include <QStandardPaths>
-#include <QDir>
-#include <QFileInfo>
-#include <QDebug>
-#include <QApplication>
-#include <QMutex>
-#include "kis_debug.h"
-
-
-Q_GLOBAL_STATIC(KoResourcePaths, s_instance)
-
-static QString cleanup(const QString &path)
-{
- return QDir::cleanPath(path);
-}
-
-
-static QStringList cleanup(const QStringList &pathList)
-{
- QStringList cleanedPathList;
- Q_FOREACH(const QString &path, pathList) {
- cleanedPathList << cleanup(path);
- }
- return cleanedPathList;
-}
-
-
-static QString cleanupDirs(const QString &path)
-{
- return QDir::cleanPath(path) + QDir::separator();
-}
-
-static QStringList cleanupDirs(const QStringList &pathList)
-{
- QStringList cleanedPathList;
- Q_FOREACH(const QString &path, pathList) {
- cleanedPathList << cleanupDirs(path);
- }
- return cleanedPathList;
-}
-
-void appendResources(QStringList *dst, const QStringList &src, bool eliminateDuplicates)
-{
- Q_FOREACH (const QString &resource, src) {
- QString realPath = QDir::cleanPath(resource);
- if (!eliminateDuplicates || !dst->contains(realPath)) {
- *dst << realPath;
- }
- }
-}
-
-
-#ifdef Q_OS_WIN
-static const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
-#else
-static const Qt::CaseSensitivity cs = Qt::CaseSensitive;
-#endif
-
-#ifdef Q_OS_MACOS
-#include <ApplicationServices/ApplicationServices.h>
-#include <CoreFoundation/CoreFoundation.h>
-#include <CoreServices/CoreServices.h>
-#endif
-
-QString getInstallationPrefix() {
-#ifdef Q_OS_MACOS
- QString appPath = qApp->applicationDirPath();
-
- dbgResources << "1" << appPath;
- appPath.chop(QString("MacOS/").length());
- dbgResources << "2" << appPath;
-
- bool makeInstall = QDir(appPath + "/../../../share/kritaplugins").exists();
- bool inBundle = QDir(appPath + "/Resources/kritaplugins").exists();
-
- dbgResources << "3. After make install" << makeInstall;
- dbgResources << "4. In Bundle" << inBundle;
-
- QString bundlePath;
-
- if (inBundle) {
- bundlePath = appPath + "/";
- }
- else if (makeInstall) {
- appPath.chop(QString("Contents/").length());
- bundlePath = appPath + "/../../";
- }
- else {
- qFatal("Cannot calculate the bundle path from the app path");
- }
-
- dbgResources << ">>>>>>>>>>>" << bundlePath;
- return bundlePath;
-#else
-#ifdef Q_OS_QWIN
- QDir appdir(qApp->applicationDirPath());
-
- // Corrects for mismatched case errors in path (qtdeclarative fails to load)
- wchar_t buffer[1024];
- QString absolute = appdir.absolutePath();
- DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024);
- rv = ::GetLongPathName(buffer, buffer, 1024);
- QString correctedPath((QChar *)buffer);
- appdir.setPath(correctedPath);
- appdir.cdUp();
- return appdir.canonicalPath();
-#else
-#ifdef Q_OS_ANDROID
- // qApp->applicationDirPath() isn't writable and android system won't allow
- // any files other than libraries
- return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/";
-#else
- return qApp->applicationDirPath() + "/../";
-#endif
-#endif
-#endif
-}
-
-class Q_DECL_HIDDEN KoResourcePaths::Private {
-public:
- QMap<QString, QStringList> absolutes; // For each resource type, the list of absolute paths, from most local (most priority) to most global
- QMap<QString, QStringList> relatives; // Same with relative paths
-
- QMutex relativesMutex;
- QMutex absolutesMutex;
-
- QStringList aliases(const QString &type)
- {
- QStringList r;
- QStringList a;
- relativesMutex.lock();
- if (relatives.contains(type)) {
- r += relatives[type];
- }
- relativesMutex.unlock();
- dbgResources << "\trelatives" << r;
- absolutesMutex.lock();
- if (absolutes.contains(type)) {
- a += absolutes[type];
- }
- dbgResources << "\tabsolutes" << a;
- absolutesMutex.unlock();
-
- return r + a;
- }
-
- QStandardPaths::StandardLocation mapTypeToQStandardPaths(const QString &type)
- {
- if (type == "tmp") {
- return QStandardPaths::TempLocation;
- }
- else if (type == "appdata") {
- return QStandardPaths::AppDataLocation;
- }
- else if (type == "data") {
- return QStandardPaths::AppDataLocation;
- }
- else if (type == "cache") {
- return QStandardPaths::CacheLocation;
- }
- else if (type == "locale") {
- return QStandardPaths::AppDataLocation;
- }
- else {
- return QStandardPaths::AppDataLocation;
- }
- }
-};
-
-KoResourcePaths::KoResourcePaths()
- : d(new Private)
-{
-}
-
-KoResourcePaths::~KoResourcePaths()
-{
-}
-
-QString KoResourcePaths::getApplicationRoot()
-{
- return getInstallationPrefix();
-}
-
-void KoResourcePaths::addResourceType(const char *type, const char *basetype,
- const QString &relativeName, bool priority)
-{
- s_instance->addResourceTypeInternal(QString::fromLatin1(type), QString::fromLatin1(basetype), relativeName, priority);
-}
-
-void KoResourcePaths::addResourceDir(const char *type, const QString &dir, bool priority)
-{
- s_instance->addResourceDirInternal(QString::fromLatin1(type), dir, priority);
-}
-
-QString KoResourcePaths::findResource(const char *type, const QString &fileName)
-{
- return cleanup(s_instance->findResourceInternal(QString::fromLatin1(type), fileName));
-}
-
-QStringList KoResourcePaths::findDirs(const char *type)
-{
- return cleanupDirs(s_instance->findDirsInternal(QString::fromLatin1(type)));
-}
-
-QStringList KoResourcePaths::findAllResources(const char *type,
- const QString &filter,
- SearchOptions options)
-{
- return cleanup(s_instance->findAllResourcesInternal(QString::fromLatin1(type), filter, options));
-}
-
-QStringList KoResourcePaths::resourceDirs(const char *type)
-{
- return cleanupDirs(s_instance->resourceDirsInternal(QString::fromLatin1(type)));
-}
-
-QString KoResourcePaths::saveLocation(const char *type, const QString &suffix, bool create)
-{
- return cleanupDirs(s_instance->saveLocationInternal(QString::fromLatin1(type), suffix, create));
-}
-
-QString KoResourcePaths::locate(const char *type, const QString &filename)
-{
- return cleanup(s_instance->locateInternal(QString::fromLatin1(type), filename));
-}
-
-QString KoResourcePaths::locateLocal(const char *type, const QString &filename, bool createDir)
-{
- return cleanup(s_instance->locateLocalInternal(QString::fromLatin1(type), filename, createDir));
-}
-
-void KoResourcePaths::addResourceTypeInternal(const QString &type, const QString &basetype,
- const QString &relativename,
- bool priority)
-{
- Q_UNUSED(basetype);
- if (relativename.isEmpty()) return;
-
- QString copy = relativename;
-
- Q_ASSERT(basetype == "data");
-
- if (!copy.endsWith(QLatin1Char('/'))) {
- copy += QLatin1Char('/');
- }
-
- d->relativesMutex.lock();
- QStringList &rels = d->relatives[type]; // find or insert
-
- if (!rels.contains(copy, cs)) {
- if (priority) {
- rels.prepend(copy);
- } else {
- rels.append(copy);
- }
- }
- d->relativesMutex.unlock();
-
- dbgResources << "addResourceType: type" << type << "basetype" << basetype << "relativename" << relativename << "priority" << priority << d->relatives[type];
-}
-
-void KoResourcePaths::addResourceDirInternal(const QString &type, const QString &absdir, bool priority)
-{
- if (absdir.isEmpty() || type.isEmpty()) return;
-
- // find or insert entry in the map
- QString copy = absdir;
- if (copy.at(copy.length() - 1) != QLatin1Char('/')) {
- copy += QLatin1Char('/');
- }
-
- d->absolutesMutex.lock();
- QStringList &paths = d->absolutes[type];
- if (!paths.contains(copy, cs)) {
- if (priority) {
- paths.prepend(copy);
- } else {
- paths.append(copy);
- }
- }
- d->absolutesMutex.unlock();
-
- dbgResources << "addResourceDir: type" << type << "absdir" << absdir << "priority" << priority << d->absolutes[type];
-}
-
-QString KoResourcePaths::findResourceInternal(const QString &type, const QString &fileName)
-{
- QStringList aliases = d->aliases(type);
- dbgResources<< "aliases" << aliases << getApplicationRoot();
- QString resource = QStandardPaths::locate(QStandardPaths::AppDataLocation, fileName, QStandardPaths::LocateFile);
-
- if (resource.isEmpty()) {
- Q_FOREACH (const QString &alias, aliases) {
- resource = QStandardPaths::locate(d->mapTypeToQStandardPaths(type), alias + '/' + fileName, QStandardPaths::LocateFile);
- dbgResources << "\t1" << resource;
- if (QFile::exists(resource)) {
- continue;
- }
- }
- }
- if (resource.isEmpty() || !QFile::exists(resource)) {
- QString approot = getApplicationRoot();
- Q_FOREACH (const QString &alias, aliases) {
- resource = approot + "/share/" + alias + '/' + fileName;
- dbgResources << "\t2" << resource;
-
- if (QFile::exists(resource)) {
- continue;
- }
- }
- }
- if (resource.isEmpty() || !QFile::exists(resource)) {
- QString approot = getApplicationRoot();
- Q_FOREACH (const QString &alias, aliases) {
- resource = approot + "/share/krita/" + alias + '/' + fileName;
- dbgResources << "\t3" << resource;
- if (QFile::exists(resource)) {
- continue;
- }
- }
- }
-
- if (resource.isEmpty() || !QFile::exists(resource)) {
- QString extraResourceDirs = qgetenv("EXTRA_RESOURCE_DIRS");
- if (!extraResourceDirs.isEmpty()) {
- Q_FOREACH(const QString &extraResourceDir, extraResourceDirs.split(':', QString::SkipEmptyParts)) {
- if (aliases.isEmpty()) {
- resource = extraResourceDir + '/' + fileName;
- dbgResources<< "\t4" << resource;
- if (QFile::exists(resource)) {
- continue;
- }
- }
- else {
- Q_FOREACH (const QString &alias, aliases) {
- resource = extraResourceDir + '/' + alias + '/' + fileName;
- dbgResources<< "\t4" << resource;
- if (QFile::exists(resource)) {
- continue;
- }
- }
- }
- }
- }
- }
-
- dbgResources<< "findResource: type" << type << "filename" << fileName << "resource" << resource;
- Q_ASSERT(!resource.isEmpty());
- return resource;
-}
-
-
-QStringList filesInDir(const QString &startdir, const QString & filter, bool recursive)
-{
- dbgResources << "filesInDir: startdir" << startdir << "filter" << filter << "recursive" << recursive;
- QStringList result;
-
- // First the entries in this path
- QStringList nameFilters;
- nameFilters << filter;
- const QStringList fileNames = QDir(startdir).entryList(nameFilters, QDir::Files | QDir::CaseSensitive, QDir::Name);
- dbgResources << "\tFound:" << fileNames.size() << ":" << fileNames;
- Q_FOREACH (const QString &fileName, fileNames) {
- QString file = startdir + '/' + fileName;
- result << file;
- }
-
- // And then everything underneath, if recursive is specified
- if (recursive) {
- const QStringList entries = QDir(startdir).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
- Q_FOREACH (const QString &subdir, entries) {
- dbgResources << "\tGoing to look in subdir" << subdir << "of" << startdir;
- result << filesInDir(startdir + '/' + subdir, filter, recursive);
- }
- }
- return result;
-}
-
-QStringList KoResourcePaths::findDirsInternal(const QString &type)
-{
- QStringList aliases = d->aliases(type);
- dbgResources << type << aliases << d->mapTypeToQStandardPaths(type);
-
- QStringList dirs;
- QStringList standardDirs =
- QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), "", QStandardPaths::LocateDirectory);
- appendResources(&dirs, standardDirs, true);
-
- Q_FOREACH (const QString &alias, aliases) {
- QStringList aliasDirs =
- QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias + '/', QStandardPaths::LocateDirectory);
- appendResources(&dirs, aliasDirs, true);
-
-#ifdef Q_OS_MACOS
- dbgResources << "MAC:" << getApplicationRoot();
- QStringList bundlePaths;
- bundlePaths << getApplicationRoot() + "/share/krita/" + alias;
- bundlePaths << getApplicationRoot() + "/../share/krita/" + alias;
- dbgResources << "bundlePaths" << bundlePaths;
- appendResources(&dirs, bundlePaths, true);
- Q_ASSERT(!dirs.isEmpty());
-#endif
-
- QStringList fallbackPaths;
- fallbackPaths << getApplicationRoot() + "/share/" + alias;
- fallbackPaths << getApplicationRoot() + "/share/krita/" + alias;
- appendResources(&dirs, fallbackPaths, true);
-
- }
- dbgResources << "findDirs: type" << type << "resource" << dirs;
- return dirs;
-}
-
-
-QStringList KoResourcePaths::findAllResourcesInternal(const QString &type,
- const QString &_filter,
- SearchOptions options) const
-{
- dbgResources << "=====================================================";
- dbgResources << type << _filter << QStandardPaths::standardLocations(d->mapTypeToQStandardPaths(type));
-
- bool recursive = options & KoResourcePaths::Recursive;
-
- dbgResources << "findAllResources: type" << type << "filter" << _filter << "recursive" << recursive;
-
- QStringList aliases = d->aliases(type);
- QString filter = _filter;
-
- // In cases where the filter is like "color-schemes/*.colors" instead of "*.kpp", used with unregistered resource types
- if (filter.indexOf('*') > 0) {
- aliases << filter.split('*').first();
- filter = '*' + filter.split('*')[1];
- dbgResources << "Split up alias" << aliases << "filter" << filter;
- }
-
- QStringList resources;
- if (aliases.isEmpty()) {
- QStringList standardResources =
- QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type),
- filter, QStandardPaths::LocateFile);
- dbgResources << "standardResources" << standardResources;
- appendResources(&resources, standardResources, true);
- dbgResources << "1" << resources;
- }
-
-
- QString extraResourceDirs = qgetenv("EXTRA_RESOURCE_DIRS");
- dbgResources << "extraResourceDirs" << extraResourceDirs;
- if (!extraResourceDirs.isEmpty()) {
- Q_FOREACH(const QString &extraResourceDir, extraResourceDirs.split(':', QString::SkipEmptyParts)) {
- if (aliases.isEmpty()) {
- appendResources(&resources, filesInDir(extraResourceDir + '/' + type, filter, recursive), true);
- }
- else {
- Q_FOREACH (const QString &alias, aliases) {
- appendResources(&resources, filesInDir(extraResourceDir + '/' + alias + '/', filter, recursive), true);
- }
- }
- }
-
- }
-
- dbgResources << "\tresources from qstandardpaths:" << resources.size();
-
- Q_FOREACH (const QString &alias, aliases) {
- dbgResources << "\t\talias:" << alias;
- QStringList dirs;
-
- QFileInfo dirInfo(alias);
- if (dirInfo.exists() && dirInfo.isDir() && dirInfo.isAbsolute()) {
- dirs << alias;
- } else {
- dirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory)
- << getInstallationPrefix() + "share/" + alias + "/"
- << getInstallationPrefix() + "share/krita/" + alias + "/";
- }
-
- Q_FOREACH (const QString &dir, dirs) {
- appendResources(&resources,
- filesInDir(dir, filter, recursive),
- true);
- }
- }
-
- dbgResources << "\tresources also from aliases:" << resources.size();
-
- // if the original filter is "input/*", we only want share/input/* and share/krita/input/* here, but not
- // share/*. therefore, use _filter here instead of filter which was split into alias and "*".
- QFileInfo fi(_filter);
-
- QStringList prefixResources;
- prefixResources << filesInDir(getInstallationPrefix() + "share/" + fi.path(), fi.fileName(), false);
- prefixResources << filesInDir(getInstallationPrefix() + "share/krita/" + fi.path(), fi.fileName(), false);
- appendResources(&resources, prefixResources, true);
-
- dbgResources << "\tresources from installation:" << resources.size();
- dbgResources << "=====================================================";
-
- return resources;
-}
-
-QStringList KoResourcePaths::resourceDirsInternal(const QString &type)
-{
- QStringList resourceDirs;
- QStringList aliases = d->aliases(type);
-
- Q_FOREACH (const QString &alias, aliases) {
- QStringList aliasDirs;
-
- aliasDirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory);
-
- aliasDirs << getInstallationPrefix() + "share/" + alias + "/"
- << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory);
- aliasDirs << getInstallationPrefix() + "share/krita/" + alias + "/"
- << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory);
-
- appendResources(&resourceDirs, aliasDirs, true);
- }
-
- dbgResources << "resourceDirs: type" << type << resourceDirs;
-
- return resourceDirs;
-}
-
-QString KoResourcePaths::saveLocationInternal(const QString &type, const QString &suffix, bool create)
-{
- QStringList aliases = d->aliases(type);
- QString path;
- if (aliases.size() > 0) {
- path = QStandardPaths::writableLocation(d->mapTypeToQStandardPaths(type)) + '/' + aliases.first();
- }
- else {
- path = QStandardPaths::writableLocation(d->mapTypeToQStandardPaths(type));
- if (!path.endsWith("krita")) {
- path += "/krita";
- }
- if (!suffix.isEmpty()) {
- path += "/" + suffix;
- }
- }
-
- QDir d(path);
-
- if (!d.exists() && create) {
- d.mkpath(path);
- }
- dbgResources << "saveLocation: type" << type << "suffix" << suffix << "create" << create << "path" << path;
-
- return path;
-}
-
-QString KoResourcePaths::locateInternal(const QString &type, const QString &filename)
-{
- QStringList aliases = d->aliases(type);
-
- QStringList locations;
- if (aliases.isEmpty()) {
- locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type), filename, QStandardPaths::LocateFile);
- }
-
- Q_FOREACH (const QString &alias, aliases) {
- locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type),
- (alias.endsWith('/') ? alias : alias + '/') + filename, QStandardPaths::LocateFile);
- }
- dbgResources << "locate: type" << type << "filename" << filename << "locations" << locations;
- if (locations.size() > 0) {
- return locations.first();
- }
- else {
- return "";
- }
-}
-
-QString KoResourcePaths::locateLocalInternal(const QString &type, const QString &filename, bool createDir)
-{
- QString path = saveLocationInternal(type, "", createDir);
- dbgResources << "locateLocal: type" << type << "filename" << filename << "CreateDir" << createDir << "path" << path;
- return path + '/' + filename;
-}
diff --git a/libs/widgetutils/KoResourcePaths.h b/libs/widgetutils/KoResourcePaths.h
deleted file mode 100644
index 5dd831396a..0000000000
--- a/libs/widgetutils/KoResourcePaths.h
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * 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; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#ifndef KORESOURCEPATHS_H
-#define KORESOURCEPATHS_H
-
-#include <QScopedPointer>
-#include <QString>
-#include <QStringList>
-
-#include <kritawidgetutils_export.h>
-
-
-/**
- * DEBUGGING KoResourcePaths:
- *
- * The usual place to look for resources is Qt's AppDataLocation.
- * This corresponds to XDG_DATA_DIRS on Linux. To ensure your installation and
- * path are configured correctly, ensure your files are located in the directories
- * contained in this variable:
- *
- * QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
- *
- * There are many debug lines that can be uncommented for more specific installation
- * checks. In the future these should be converted to qloggingcategory to enable
- * convenient enable/disable functionality.
- */
-
-class KRITAWIDGETUTILS_EXPORT KoResourcePaths
-{
-public:
-
- KoResourcePaths();
- virtual ~KoResourcePaths();
-
- enum SearchOption { NoSearchOptions = 0,
- Recursive = 1,
- IgnoreExecBit = 4
- };
- Q_DECLARE_FLAGS(SearchOptions, SearchOption)
-
- static QString getApplicationRoot();
-
- /**
- * Adds suffixes for types.
- *
- * You may add as many as you need, but it is advised that there
- * is exactly one to make writing definite.
- *
- * The later a suffix is added, the higher its priority. Note, that the
- * suffix should end with / but doesn't have to start with one (as prefixes
- * should end with one). So adding a suffix for app_pics would look
- * like KoStandardPaths::addResourceType("app_pics", "data", "app/pics");
- *
- * @param type Specifies a short descriptive string to access
- * files of this type.
- * @param basetype Specifies an already known type, or 0 if none
- * @param relativename Specifies a directory relative to the basetype
- * @param priority if true, the directory is added before any other,
- * otherwise after
- */
- static void addResourceType(const char *type, const char *basetype,
- const QString &relativeName, bool priority = true);
-
-
- /**
- * Adds absolute path at the beginning of the search path for
- * particular types (for example in case of icons where
- * the user specifies extra paths).
- *
- * You shouldn't need this function in 99% of all cases besides
- * adding user-given paths.
- *
- * @param type Specifies a short descriptive string to access files
- * of this type.
- * @param absdir Points to directory where to look for this specific
- * type. Non-existent directories may be saved but pruned.
- * @param priority if true, the directory is added before any other,
- * otherwise after
- */
- static void addResourceDir(const char *type, const QString &dir, bool priority = true);
-
- /**
- * Tries to find a resource in the following order:
- * @li All PREFIX/\<relativename> paths (most recent first).
- * @li All absolute paths (most recent first).
- *
- * The filename should be a filename relative to the base dir
- * for resources. So is a way to get the path to libkdecore.la
- * to findResource("lib", "libkdecore.la"). KStandardDirs will
- * then look into the subdir lib of all elements of all prefixes
- * ($KDEDIRS) for a file libkdecore.la and return the path to
- * the first one it finds (e.g. /opt/kde/lib/libkdecore.la).
- *
- * Example:
- * @code
- * QString iconfilename = KStandardPaths::findResource("icon",QString("oxygen/22x22/apps/ktip.png"));
- * @endcode
- *
- * @param type The type of the wanted resource
- * @param filename A relative filename of the resource.
- *
- * @return A full path to the filename specified in the second
- * argument, or QString() if not found.
- */
-
- static QString findResource(const char *type, const QString &fileName);
-
- /**
- * Tries to find all directories whose names consist of the
- * specified type and a relative path. So
- * findDirs("xdgdata-apps", "Settings") would return
- * @li /home/joe/.local/share/applications/Settings/
- * @li /usr/share/applications/Settings/
- *
- * (from the most local to the most global)
- *
- * Note that it appends @c / to the end of the directories,
- * so you can use this right away as directory names.
- *
- * @param type The type of the base directory.
- * @param reldir Relative directory.
- *
- * @return A list of matching directories, or an empty
- * list if the resource specified is not found.
- */
- static QStringList findDirs(const char *type);
-
- /**
- * Tries to find all resources with the specified type.
- *
- * The function will look into all specified directories
- * and return all filenames in these directories.
- *
- * The "most local" files are returned before the "more global" files.
- *
- * @param type The type of resource to locate directories for.
- * @param filter Only accept filenames that fit to filter. The filter
- * may consist of an optional directory and a QRegExp
- * wildcard expression. E.g. <tt>"images\*.jpg"</tt>.
- * Use QString() if you do not want a filter.
- * @param options if the flags passed include Recursive, subdirectories
- * will also be search.
- *
- * @return List of all the files whose filename matches the
- * specified filter.
- */
- static QStringList findAllResources(const char *type,
- const QString &filter = QString(),
- SearchOptions options = NoSearchOptions);
-
- /**
- * @param type The type of resource
- * @return The list of possible directories for the specified @p type.
- * The function updates the cache if possible. If the resource
- * type specified is unknown, it will return an empty list.
- * Note, that the directories are assured to exist beside the save
- * location, which may not exist, but is returned anyway.
- */
- static QStringList resourceDirs(const char *type);
-
- /**
- * Finds a location to save files into for the given type
- * in the user's home directory.
- *
- * @param type The type of location to return.
- * @param suffix A subdirectory name.
- * Makes it easier for you to create subdirectories.
- * You can't pass filenames here, you _have_ to pass
- * directory names only and add possible filename in
- * that directory yourself. A directory name always has a
- * trailing slash ('/').
- * @param create If set, saveLocation() will create the directories
- * needed (including those given by @p suffix).
- *
- * @return A path where resources of the specified type should be
- * saved, or QString() if the resource type is unknown.
- */
- static QString saveLocation(const char *type, const QString &suffix = QString(), bool create = true);
-
- /**
- * This function is just for convenience. It simply calls
- * KoResourcePaths::findResource((type, filename).
- *
- * @param type The type of the wanted resource, see KStandardDirs
- * @param filename A relative filename of the resource
- *
- * @return A full path to the filename specified in the second
- * argument, or QString() if not found
- **/
- static QString locate(const char *type, const QString &filename);
-
- /**
- * This function is much like locate. However it returns a
- * filename suitable for writing to. No check is made if the
- * specified @p filename actually exists. Missing directories
- * are created. If @p filename is only a directory, without a
- * specific file, @p filename must have a trailing slash.
- *
- * @param type The type of the wanted resource, see KStandardDirs
- * @param filename A relative filename of the resource
- *
- * @return A full path to the filename specified in the second
- * argument, or QString() if not found
- **/
- static QString locateLocal(const char *type, const QString &filename, bool createDir = false);
-
-private:
-
- void addResourceTypeInternal(const QString &type, const QString &basetype,
- const QString &relativeName, bool priority);
-
- void addResourceDirInternal(const QString &type, const QString &absdir, bool priority);
-
- QString findResourceInternal(const QString &type, const QString &fileName);
-
- QStringList findDirsInternal(const QString &type);
-
- QStringList findAllResourcesInternal(const QString &type,
- const QString &filter = QString(),
- SearchOptions options = NoSearchOptions) const;
-
- QStringList resourceDirsInternal(const QString &type);
-
- QString saveLocationInternal(const QString &type, const QString &suffix = QString(), bool create = true);
-
- QString locateInternal(const QString &type, const QString &filename);
-
- QString locateLocalInternal(const QString &type, const QString &filename, bool createDir = false);
-
- class Private;
- QScopedPointer<Private> d;
-};
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(KoResourcePaths::SearchOptions)
-
-#endif // KORESOURCEPATHS_H
diff --git a/libs/widgetutils/kis_action_registry.cpp b/libs/widgetutils/kis_action_registry.cpp
index 56bcc8742c..f1d9887c91 100644
--- a/libs/widgetutils/kis_action_registry.cpp
+++ b/libs/widgetutils/kis_action_registry.cpp
@@ -1,423 +1,426 @@
/*
* 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::~KisActionRegistry()
{
}
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()));
+ action->setProperty("defaultShortcuts", QVariant::fromValue(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;
+ warnAction << "propertizeAction: 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
if (!icon.isEmpty()) {
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;
+ dbgAction << "getActionProperty: 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);
+ dbgAction << "Action Definitions" << actionDefinitions;
// Extract actions all XML .action files.
Q_FOREACH (const QString &actionDefinition, actionDefinitions) {
+ dbgAction << "\tLoading Action File" << actionDefinition;
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");
+ dbgAction << "\t\tloading xml data for action" << name;
// Bad things
if (name.isEmpty()) {
errAction << "Unnamed action in definitions file " << actionDefinition;
}
else if (actionInfoList.contains(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/KisShortcutsEditorItem.cpp b/libs/widgetutils/xmlgui/KisShortcutsEditorItem.cpp
index a6fff26895..99aecf1b54 100644
--- a/libs/widgetutils/xmlgui/KisShortcutsEditorItem.cpp
+++ b/libs/widgetutils/xmlgui/KisShortcutsEditorItem.cpp
@@ -1,247 +1,247 @@
/* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe <donohoe@kde.org>
Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org>
Copyright (C) 1998 Matthias Ettrich <ettrich@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>
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 "KisShortcutsDialog_p.h"
#include "KisShortcutsEditor_p.h"
#include <QAction>
#include <QTreeWidgetItem>
#include <kis_debug.h>
KisShortcutsEditorItem::KisShortcutsEditorItem(QTreeWidgetItem *parent, QAction *action)
: QTreeWidgetItem(parent, ActionItem)
, m_action(action)
{
// Filtering message requested by translators (scripting).
m_id = m_action->objectName();
m_actionNameInTable = i18nc("@item:intable Action name in shortcuts configuration",
"%1", KLocalizedString::removeAcceleratorMarker(m_action->text()));
if (m_actionNameInTable.isEmpty()) {
warnKrita << "Action without text!" << m_action->objectName();
m_actionNameInTable = m_id;
}
m_collator.setNumericMode(true);
m_collator.setCaseSensitivity(Qt::CaseSensitive);
// qDebug() << "Adding new action" << m_id << "with shortcut" << keySequence(LocalPrimary).toString();
}
KisShortcutsEditorItem::~KisShortcutsEditorItem()
{
delete m_oldLocalShortcut;
}
bool KisShortcutsEditorItem::isModified() const
{
return m_oldLocalShortcut;
}
QVariant KisShortcutsEditorItem::data(int column, int role) const
{
switch (role) {
case Qt::DisplayRole:
switch (column) {
case Name:
return m_actionNameInTable;
case Id:
return m_id;
case LocalPrimary:
case LocalAlternate:
- return qVariantFromValue(keySequence(column));
+ return QVariant::fromValue(keySequence(column));
default:
break;
}
break;
case Qt::DecorationRole:
if (column == Name) {
return m_action->icon();
} else {
return QIcon();
}
break;
case Qt::WhatsThisRole:
return m_action->whatsThis();
case Qt::ToolTipRole:
// TODO: show command descriptions/tooltips in the shortcut editor
return QVariant();
case Qt::FontRole:
if (column == Name && m_isNameBold) {
QFont modifiedFont = treeWidget()->font();
modifiedFont.setBold(true);
return modifiedFont;
}
break;
case KExtendableItemDelegate::ShowExtensionIndicatorRole:
switch (column) {
case Name:
return false;
case LocalPrimary:
case LocalAlternate:
return !m_action->property("isShortcutConfigurable").isValid()
|| m_action->property("isShortcutConfigurable").toBool();
default:
return false;
}
//the following are custom roles, defined in this source file only
case ShortcutRole:
switch (column) {
case LocalPrimary:
case LocalAlternate:
- return qVariantFromValue(keySequence(column));
+ return QVariant::fromValue(keySequence(column));
default:
// Column not valid for this role
Q_ASSERT(false);
return QVariant();
}
case DefaultShortcutRole: {
// Note: we are using the QMetaObject system to store this property.
QList<QKeySequence> defaultShortcuts = m_action->property("defaultShortcuts").value<QList<QKeySequence> >();
switch (column) {
case LocalPrimary:
- return qVariantFromValue(primarySequence(defaultShortcuts));
+ return QVariant::fromValue(primarySequence(defaultShortcuts));
case LocalAlternate:
- return qVariantFromValue(alternateSequence(defaultShortcuts));
+ return QVariant::fromValue(alternateSequence(defaultShortcuts));
default:
// Column not valid for this role
Q_ASSERT(false);
return QVariant();
}
}
case ObjectRole:
- return qVariantFromValue((QObject *)m_action);
+ return QVariant::fromValue((QObject *)m_action);
default:
break;
}
return QVariant();
}
bool KisShortcutsEditorItem::operator<(const QTreeWidgetItem &other) const
{
const int column = treeWidget() ? treeWidget()->sortColumn() : 0;
return m_collator.compare(text(column), other.text(column)) < 0;
}
QKeySequence KisShortcutsEditorItem::keySequence(uint column) const
{
QList<QKeySequence> shortcuts = m_action->shortcuts();
switch (column) {
case LocalPrimary:
return primarySequence(shortcuts);
case LocalAlternate:
return alternateSequence(shortcuts);
default:
return QKeySequence();
}
}
void KisShortcutsEditorItem::setKeySequence(uint column, const QKeySequence &seq)
{
QList<QKeySequence> ks;
ks = m_action->shortcuts();
if (!m_oldLocalShortcut) {
m_oldLocalShortcut = new QList<QKeySequence>(ks);
}
if (column == LocalAlternate) {
if (ks.isEmpty()) {
ks << QKeySequence();
}
if (ks.size() <= 1) {
ks << seq;
} else {
ks[1] = seq;
}
} else {
if (ks.isEmpty()) {
ks << seq;
} else {
ks[0] = seq;
}
}
m_action->setShortcuts(ks);
updateModified();
}
//our definition of modified is "modified since the chooser was shown".
void KisShortcutsEditorItem::updateModified()
{
if (m_oldLocalShortcut && *m_oldLocalShortcut == m_action->shortcuts()) {
delete m_oldLocalShortcut;
m_oldLocalShortcut = 0;
}
}
bool KisShortcutsEditorItem::isModified(uint column) const
{
switch (column) {
case Name:
return false;
case LocalPrimary:
case LocalAlternate:
if (!m_oldLocalShortcut) {
return false;
}
if (column == LocalPrimary) {
return primarySequence(*m_oldLocalShortcut) != primarySequence(m_action->shortcuts());
} else {
return alternateSequence(*m_oldLocalShortcut) != alternateSequence(m_action->shortcuts());
}
default:
return false;
}
}
void KisShortcutsEditorItem::undo()
{
//dbgKrita << "Undoing changes for " << data(Name, Qt::DisplayRole).toString();
if (m_oldLocalShortcut) {
// We only ever reset the active Shortcut
m_action->setShortcuts(*m_oldLocalShortcut);
}
updateModified();
}
void KisShortcutsEditorItem::commit()
{
if (m_oldLocalShortcut) { // || m_oldShapeGesture || m_oldRockerGesture) {
dbgUI << "Committing changes for " << data(Name, Qt::DisplayRole).toString();
}
delete m_oldLocalShortcut;
m_oldLocalShortcut = 0;
}
diff --git a/libs/widgetutils/xmlgui/kactioncollection.cpp b/libs/widgetutils/xmlgui/kactioncollection.cpp
index 27e10bd13a..06f0e14381 100644
--- a/libs/widgetutils/xmlgui/kactioncollection.cpp
+++ b/libs/widgetutils/xmlgui/kactioncollection.cpp
@@ -1,758 +1,759 @@
/* 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-2007 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 "kactioncollection.h"
#include "config-xmlgui.h"
#include "kactioncategory.h"
#include "kxmlguiclient.h"
#include "kxmlguifactory.h"
#include "kis_action_registry.h"
#include <kauthorized.h>
#include <kconfiggroup.h>
#include <ksharedconfig.h>
#include <QDebug>
#include <QDomDocument>
#include <QSet>
#include <QGuiApplication>
#include <QMap>
#include <QList>
#include <QAction>
#include <QMetaMethod>
-
+#include <QTextStream>
#include <stdio.h>
#if defined(KCONFIG_BEFORE_5_24)
# define authorizeAction authorizeKAction
#endif
class KActionCollectionPrivate
{
public:
KActionCollectionPrivate()
: m_parentGUIClient(0L),
configGroup(QStringLiteral("Shortcuts")),
connectTriggered(false),
connectHovered(false),
q(0)
{
}
void setComponentForAction(QAction *action)
{
Q_UNUSED(action)
}
static QList<KActionCollection *> s_allCollections;
void _k_associatedWidgetDestroyed(QObject *obj);
void _k_actionDestroyed(QObject *obj);
bool writeKXMLGUIConfigFile();
QString m_componentName;
QString m_componentDisplayName;
//! Remove a action from our internal bookkeeping. Returns 0 if the
//! action doesn't belong to us.
QAction *unlistAction(QAction *);
QMap<QString, QAction *> actionByName;
QList<QAction *> actions;
const KXMLGUIClient *m_parentGUIClient;
QString configGroup;
bool configIsGlobal : 1;
bool connectTriggered : 1;
bool connectHovered : 1;
KActionCollection *q;
QList<QWidget *> associatedWidgets;
};
QList<KActionCollection *> KActionCollectionPrivate::s_allCollections;
KActionCollection::KActionCollection(QObject *parent, const QString &cName)
: QObject(parent)
, d(new KActionCollectionPrivate)
{
d->q = this;
KActionCollectionPrivate::s_allCollections.append(this);
setComponentName(cName);
}
KActionCollection::KActionCollection(const KXMLGUIClient *parent)
: QObject(0)
, d(new KActionCollectionPrivate)
{
d->q = this;
KActionCollectionPrivate::s_allCollections.append(this);
d->m_parentGUIClient = parent;
d->m_componentName = parent->componentName();
}
KActionCollection::~KActionCollection()
{
KActionCollectionPrivate::s_allCollections.removeAll(this);
delete d;
}
QList<KActionCategory *> KActionCollection::categories() const
{
return this->findChildren<KActionCategory *>();
}
KActionCategory *KActionCollection::getCategory(const QString &name) {
KActionCategory *category = 0;
foreach (KActionCategory *c, categories()) {
if (c->text() == name) {
category = c;
}
}
if (category == 0) {
category = new KActionCategory(name, this);
}
return category;
}
void KActionCollection::clear()
{
d->actionByName.clear();
qDeleteAll(d->actions);
d->actions.clear();
}
QAction *KActionCollection::action(const QString &name) const
{
QAction *action = 0L;
if (!name.isEmpty()) {
action = d->actionByName.value(name);
}
return action;
}
QAction *KActionCollection::action(int index) const
{
// ### investigate if any apps use this at all
return actions().value(index);
}
int KActionCollection::count() const
{
return d->actions.count();
}
bool KActionCollection::isEmpty() const
{
return count() == 0;
}
void KActionCollection::setComponentName(const QString &cName)
{
if (count() > 0) {
// Its component name is part of an action's signature in the context of
// global shortcuts and the semantics of changing an existing action's
// signature are, as it seems, impossible to get right.
// As of now this only matters for global shortcuts. We could
// thus relax the requirement and only refuse to change the component data
// if we have actions with global shortcuts in this collection.
qWarning() << "this does not work on a KActionCollection containing actions!";
}
if (!cName.isEmpty()) {
d->m_componentName = cName;
} else {
d->m_componentName = QCoreApplication::applicationName();
}
}
QString KActionCollection::componentName() const
{
return d->m_componentName;
}
void KActionCollection::setComponentDisplayName(const QString &displayName)
{
d->m_componentDisplayName = displayName;
}
QString KActionCollection::componentDisplayName() const
{
if (!d->m_componentDisplayName.isEmpty()) {
return d->m_componentDisplayName;
}
if (!QGuiApplication::applicationDisplayName().isEmpty()) {
return QGuiApplication::applicationDisplayName();
}
return QCoreApplication::applicationName();
}
const KXMLGUIClient *KActionCollection::parentGUIClient() const
{
return d->m_parentGUIClient;
}
QList<QAction *> KActionCollection::actions() const
{
return d->actions;
}
const QList< QAction * > KActionCollection::actionsWithoutGroup() const
{
QList<QAction *> ret;
Q_FOREACH (QAction *action, d->actions)
if (!action->actionGroup()) {
ret.append(action);
}
return ret;
}
const QList< QActionGroup * > KActionCollection::actionGroups() const
{
QSet<QActionGroup *> set;
Q_FOREACH (QAction *action, d->actions)
if (action->actionGroup()) {
set.insert(action->actionGroup());
}
- return set.toList();
+ return QList<QActionGroup*>(set.begin(), set.end());
}
QAction *KActionCollection::addCategorizedAction(const QString &name, QAction *action, const QString &categoryName)
{
return getCategory(categoryName)->addAction(name, action);
}
QAction *KActionCollection::addAction(const QString &name, QAction *action)
{
if (!action) {
return action;
}
const QString objectName = action->objectName();
QString indexName = name;
if (indexName.isEmpty()) {
// No name provided. Use the objectName.
indexName = objectName;
} else {
// Set the new name
action->setObjectName(indexName);
}
// No name provided and the action had no name. Make one up. This will not
// work when trying to save shortcuts.
if (indexName.isEmpty()) {
- indexName = indexName.sprintf("unnamed-%p", (void *)action);
+ QTextStream(&indexName) << (void *)action;
+
action->setObjectName(indexName);
}
// From now on the objectName has to have a value. Else we cannot safely
// remove actions.
Q_ASSERT(!action->objectName().isEmpty());
// look if we already have THIS action under THIS name ;)
if (d->actionByName.value(indexName, 0) == action) {
// This is not a multi map!
Q_ASSERT(d->actionByName.count(indexName) == 1);
return action;
}
if (!KAuthorized::authorizeAction(indexName)) {
// Disable this action
action->setEnabled(false);
action->setVisible(false);
action->blockSignals(true);
}
// Check if we have another action under this name
if (QAction *oldAction = d->actionByName.value(indexName)) {
takeAction(oldAction);
}
// Check if we have this action under a different name.
// Not using takeAction because we don't want to remove it from categories,
// and because it has the new name already.
const int oldIndex = d->actions.indexOf(action);
if (oldIndex != -1) {
d->actionByName.remove(d->actionByName.key(action));
d->actions.removeAt(oldIndex);
}
// Add action to our lists.
d->actionByName.insert(indexName, action);
d->actions.append(action);
Q_FOREACH (QWidget *widget, d->associatedWidgets) {
widget->addAction(action);
}
connect(action, SIGNAL(destroyed(QObject*)), SLOT(_k_actionDestroyed(QObject*)));
d->setComponentForAction(action);
if (d->connectHovered) {
connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
}
if (d->connectTriggered) {
connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
}
emit inserted(action);
return action;
}
void KActionCollection::addActions(const QList<QAction *> &actions)
{
Q_FOREACH (QAction *action, actions) {
addAction(action->objectName(), action);
}
}
void KActionCollection::removeAction(QAction *action)
{
delete takeAction(action);
}
QAction *KActionCollection::takeAction(QAction *action)
{
if (!d->unlistAction(action)) {
return 0;
}
// Remove the action from all widgets
Q_FOREACH (QWidget *widget, d->associatedWidgets) {
widget->removeAction(action);
}
action->disconnect(this);
emit removed(action); //deprecated
return action;
}
QAction *KActionCollection::addAction(KStandardAction::StandardAction actionType, const QObject *receiver, const char *member)
{
QAction *action = KStandardAction::create(actionType, receiver, member, this);
return action;
}
QAction *KActionCollection::addAction(KStandardAction::StandardAction actionType, const QString &name,
const QObject *receiver, const char *member)
{
// pass 0 as parent, because if the parent is a KActionCollection KStandardAction::create automatically
// adds the action to it under the default name. We would trigger the
// warning about renaming the action then.
QAction *action = KStandardAction::create(actionType, receiver, member, 0);
// Give it a parent for gc.
action->setParent(this);
// Remove the name to get rid of the "rename action" warning above
action->setObjectName(name);
// And now add it with the desired name.
return addAction(name, action);
}
QAction *KActionCollection::addAction(const QString &name, const QObject *receiver, const char *member)
{
QAction *a = new QAction(this);
if (receiver && member) {
connect(a, SIGNAL(triggered(bool)), receiver, member);
}
return addAction(name, a);
}
QKeySequence KActionCollection::defaultShortcut(QAction *action) const
{
const QList<QKeySequence> shortcuts = defaultShortcuts(action);
return shortcuts.isEmpty() ? QKeySequence() : shortcuts.first();
}
QList<QKeySequence> KActionCollection::defaultShortcuts(QAction *action) const
{
return action->property("defaultShortcuts").value<QList<QKeySequence> >();
}
void KActionCollection::setDefaultShortcut(QAction *action, const QKeySequence &shortcut)
{
setDefaultShortcuts(action, QList<QKeySequence>() << shortcut);
}
void KActionCollection::setDefaultShortcuts(QAction *action, const QList<QKeySequence> &shortcuts)
{
action->setShortcuts(shortcuts);
action->setProperty("defaultShortcuts", QVariant::fromValue(shortcuts));
}
bool KActionCollection::isShortcutsConfigurable(QAction *action) const
{
// Considered as true by default
const QVariant value = action->property("isShortcutConfigurable");
return value.isValid() ? value.toBool() : true;
}
void KActionCollection::setShortcutsConfigurable(QAction *action, bool configurable)
{
action->setProperty("isShortcutConfigurable", configurable);
}
QString KActionCollection::configGroup() const
{
return d->configGroup;
}
void KActionCollection::setConfigGroup(const QString &group)
{
d->configGroup = group;
}
void KActionCollection::updateShortcuts()
{
auto actionRegistry = KisActionRegistry::instance();
for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
it != d->actionByName.constEnd(); ++it) {
actionRegistry->updateShortcut(it.key(), it.value());
}
}
void KActionCollection::readSettings()
{
auto ar = KisActionRegistry::instance();
ar->loadCustomShortcuts();
for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
it != d->actionByName.constEnd(); ++it) {
QAction *action = it.value();
if (!action) {
continue;
}
if (isShortcutsConfigurable(action)) {
QString actionName = it.key();
ar->updateShortcut(actionName, action);
}
}
}
bool KActionCollectionPrivate::writeKXMLGUIConfigFile()
{
const KXMLGUIClient *kxmlguiClient = q->parentGUIClient();
// return false if there is no KXMLGUIClient
if (!kxmlguiClient || kxmlguiClient->xmlFile().isEmpty()) {
return false;
}
QString attrShortcut = QStringLiteral("shortcut");
// Read XML file
QString sXml(KXMLGUIFactory::readConfigFile(kxmlguiClient->xmlFile(), q->componentName()));
QDomDocument doc;
doc.setContent(sXml);
// Process XML data
// Get hold of ActionProperties tag
QDomElement elem = KXMLGUIFactory::actionPropertiesElement(doc);
// now, iterate through our actions
for (QMap<QString, QAction *>::ConstIterator it = actionByName.constBegin();
it != actionByName.constEnd(); ++it) {
QAction *action = it.value();
if (!action) {
continue;
}
QString actionName = it.key();
// If the action name starts with unnamed- spit out a warning and ignore
// it. That name will change at will and will break loading writing
if (actionName.startsWith(QLatin1String("unnamed-"))) {
qCritical() << "Skipped writing shortcut for action " << actionName << "(" << action->text() << ")!";
continue;
}
bool bSameAsDefault = (action->shortcuts() == q->defaultShortcuts(action));
// now see if this element already exists
// and create it if necessary (unless bSameAsDefault)
QDomElement act_elem = KXMLGUIFactory::findActionByName(elem, actionName, !bSameAsDefault);
if (act_elem.isNull()) {
continue;
}
if (bSameAsDefault) {
act_elem.removeAttribute(attrShortcut);
if (act_elem.attributes().count() == 1) {
elem.removeChild(act_elem);
}
} else {
act_elem.setAttribute(attrShortcut, QKeySequence::listToString(action->shortcuts()));
}
}
// Write back to XML file
KXMLGUIFactory::saveConfigFile(doc, kxmlguiClient->localXMLFile(), q->componentName());
return true;
}
void KActionCollection::writeSettings(KConfigGroup *config,
bool writeScheme,
QAction *oneAction) const
{
// If the caller didn't provide a config group we try to save the KXMLGUI
// Configuration file. (This will work if the parentGUI was set and has a
// valid configuration file.)
if (config == 0 && d->writeKXMLGUIConfigFile()) {
return;
}
KConfigGroup cg(KSharedConfig::openConfig(), configGroup());
if (!config) {
config = &cg;
}
QList<QAction *> writeActions;
if (oneAction) {
writeActions.append(oneAction);
} else {
writeActions = actions();
}
for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
it != d->actionByName.constEnd(); ++it) {
QAction *action = it.value();
if (!action) {
continue;
}
QString actionName = it.key();
// If the action name starts with unnamed- spit out a warning and ignore
// it. That name will change at will and will break loading writing
if (actionName.startsWith(QLatin1String("unnamed-"))) {
qCritical() << "Skipped saving shortcut for action without name " \
<< action->text() << "!";
continue;
}
// Write the shortcut
if (isShortcutsConfigurable(action)) {
bool bConfigHasAction = !config->readEntry(actionName, QString()).isEmpty();
bool bSameAsDefault = (action->shortcuts() == defaultShortcuts(action));
// If the current shortcut differs from the default, we want to write.
KConfigGroup::WriteConfigFlags flags = KConfigGroup::Persistent;
if (writeScheme || !bSameAsDefault) {
// We are instructed to write all shortcuts or the shortcut is
// not set to its default value. Write it
QString s = QKeySequence::listToString(action->shortcuts());
if (s.isEmpty()) {
s = QStringLiteral("none");
}
config->writeEntry(actionName, s, flags);
} else if (bConfigHasAction) {
// This key is the same as default but exists in config file.
// Remove it.
config->deleteEntry(actionName, flags);
}
}
}
config->sync();
}
void KActionCollection::slotActionTriggered()
{
QAction *action = qobject_cast<QAction *>(sender());
if (action) {
emit actionTriggered(action);
}
}
void KActionCollection::slotActionHighlighted()
{
slotActionHovered();
}
void KActionCollection::slotActionHovered()
{
QAction *action = qobject_cast<QAction *>(sender());
if (action) {
emit actionHighlighted(action);
emit actionHovered(action);
}
}
void KActionCollectionPrivate::_k_actionDestroyed(QObject *obj)
{
// obj isn't really a QAction anymore. So make sure we don't do fancy stuff
// with it.
QAction *action = static_cast<QAction *>(obj);
if (!unlistAction(action)) {
return;
}
//HACK the object we emit is partly destroyed
emit q->removed(action); //deprecated. remove in KDE5
}
void KActionCollection::connectNotify(const QMetaMethod &signal)
{
if (d->connectHovered && d->connectTriggered) {
return;
}
if (signal.methodSignature() == "actionHighlighted(QAction*)" ||
signal.methodSignature() == "actionHovered(QAction*)") {
if (!d->connectHovered) {
d->connectHovered = true;
Q_FOREACH (QAction *action, actions()) {
connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
}
}
} else if (signal.methodSignature() == "actionTriggered(QAction*)") {
if (!d->connectTriggered) {
d->connectTriggered = true;
Q_FOREACH (QAction *action, actions()) {
connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
}
}
}
QObject::connectNotify(signal);
}
const QList< KActionCollection * > &KActionCollection::allCollections()
{
return KActionCollectionPrivate::s_allCollections;
}
void KActionCollection::associateWidget(QWidget *widget) const
{
Q_FOREACH (QAction *action, actions()) {
if (!widget->actions().contains(action)) {
widget->addAction(action);
}
}
}
void KActionCollection::addAssociatedWidget(QWidget *widget)
{
if (!d->associatedWidgets.contains(widget)) {
widget->addActions(actions());
d->associatedWidgets.append(widget);
connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*)));
}
}
void KActionCollection::removeAssociatedWidget(QWidget *widget)
{
Q_FOREACH (QAction *action, actions()) {
widget->removeAction(action);
}
d->associatedWidgets.removeAll(widget);
disconnect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*)));
}
QAction *KActionCollectionPrivate::unlistAction(QAction *action)
{
// ATTENTION:
// This method is called with an QObject formerly known as a QAction
// during _k_actionDestroyed(). So don't do fancy stuff here that needs a
// real QAction!
// Get the index for the action
int index = actions.indexOf(action);
// Action not found.
if (index == -1) {
return 0;
}
// An action collection can't have the same action twice.
Q_ASSERT(actions.indexOf(action, index + 1) == -1);
// Get the actions name
const QString name = action->objectName();
// Remove the action
actionByName.remove(name);
actions.removeAt(index);
// Remove the action from the categories. Should be only one
QList<KActionCategory *> categories = q->findChildren<KActionCategory *>();
Q_FOREACH (KActionCategory *category, categories) {
category->unlistAction(action);
}
return action;
}
QList< QWidget * > KActionCollection::associatedWidgets() const
{
return d->associatedWidgets;
}
void KActionCollection::clearAssociatedWidgets()
{
Q_FOREACH (QWidget *widget, d->associatedWidgets)
Q_FOREACH (QAction *action, actions()) {
widget->removeAction(action);
}
d->associatedWidgets.clear();
}
void KActionCollectionPrivate::_k_associatedWidgetDestroyed(QObject *obj)
{
associatedWidgets.removeAll(static_cast<QWidget *>(obj));
}
#include "moc_kactioncollection.cpp"
diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector.cpp
index 92688e12da..10a3741820 100644
--- a/plugins/dockers/advancedcolorselector/kis_color_selector.cpp
+++ b/plugins/dockers/advancedcolorselector/kis_color_selector.cpp
@@ -1,415 +1,415 @@
/*
* 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 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_color_selector.h"
#include <cmath>
#include <QHBoxLayout>
#include <QColor>
#include <QPainter>
#include <QMouseEvent>
#include <QTimer>
#include <QPushButton>
#include <QApplication>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <ksharedconfig.h>
#include <kis_debug.h>
#include <KoCanvasResourceProvider.h>
#include <kis_canvas_resource_provider.h>
#include <kis_icon.h>
#include "kis_color_selector_ring.h"
#include "kis_color_selector_triangle.h"
#include "kis_color_selector_simple.h"
#include "kis_color_selector_wheel.h"
#include "kis_color_selector_container.h"
#include "kis_canvas2.h"
#include "kis_signal_compressor.h"
#include "KisViewManager.h"
KisColorSelector::KisColorSelector(KisColorSelectorConfiguration conf, QWidget* parent)
: KisColorSelectorBase(parent),
m_ring(0),
m_triangle(0),
m_slider(0),
m_square(0),
m_wheel(0),
m_mainComponent(0),
m_subComponent(0),
m_grabbingComponent(0),
m_blipDisplay(true)
{
init();
updateSettings();
setConfiguration(conf);
}
KisColorSelector::KisColorSelector(QWidget* parent)
: KisColorSelectorBase(parent),
m_ring(0),
m_triangle(0),
m_slider(0),
m_square(0),
m_wheel(0),
m_button(0),
m_mainComponent(0),
m_subComponent(0),
m_grabbingComponent(0),
m_blipDisplay(true)
{
init();
updateSettings();
}
KisColorSelectorBase* KisColorSelector::createPopup() const
{
KisColorSelectorBase* popup = new KisColorSelector(0);
popup->setColor(m_lastRealColor);
return popup;
}
void KisColorSelector::setConfiguration(KisColorSelectorConfiguration conf)
{
m_configuration = conf;
if(m_mainComponent!=0) {
Q_ASSERT(m_subComponent!=0);
m_mainComponent->setGeometry(0, 0, 0, 0);
m_subComponent->setGeometry(0, 0, 0, 0);
m_mainComponent->disconnect();
m_subComponent->disconnect();
}
switch (m_configuration.mainType) {
case KisColorSelectorConfiguration::Square:
m_mainComponent=m_square;
break;
case KisColorSelectorConfiguration::Wheel:
m_mainComponent=m_wheel;
break;
case KisColorSelectorConfiguration::Triangle:
m_mainComponent=m_triangle;
break;
default:
Q_ASSERT(false);
}
switch (m_configuration.subType) {
case KisColorSelectorConfiguration::Ring:
m_subComponent=m_ring;
break;
case KisColorSelectorConfiguration::Slider:
m_subComponent=m_slider;
break;
default:
Q_ASSERT(false);
}
connect(m_mainComponent, SIGNAL(paramChanged(qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal)),
m_subComponent, SLOT(setParam(qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal)), Qt::UniqueConnection);
connect(m_subComponent, SIGNAL(paramChanged(qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal)),
m_mainComponent, SLOT(setParam(qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal)), Qt::UniqueConnection);
connect(m_mainComponent, SIGNAL(update()), m_signalCompressor, SLOT(start()), Qt::UniqueConnection);
connect(m_subComponent, SIGNAL(update()), m_signalCompressor, SLOT(start()), Qt::UniqueConnection);
m_mainComponent->setConfiguration(m_configuration.mainTypeParameter, m_configuration.mainType);
m_subComponent->setConfiguration(m_configuration.subTypeParameter, m_configuration.subType);
QResizeEvent event(QSize(width(), height()), QSize());
resizeEvent(&event);
}
KisColorSelectorConfiguration KisColorSelector::configuration() const
{
return m_configuration;
}
void KisColorSelector::updateSettings()
{
KisColorSelectorBase::updateSettings();
KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
setConfiguration(KisColorSelectorConfiguration::fromString(cfg.readEntry("colorSelectorConfiguration", KisColorSelectorConfiguration().toString())));
if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->canvasResourceProvider()) {
bool gamutMaskActive = m_canvas->viewManager()->canvasResourceProvider()->gamutMaskActive();
if (gamutMaskActive) {
- KoGamutMask* currentMask = m_canvas->viewManager()->canvasResourceProvider()->currentGamutMask();
+ KoGamutMaskSP currentMask = m_canvas->viewManager()->canvasResourceProvider()->currentGamutMask();
if (currentMask) {
slotGamutMaskSet(currentMask);
}
} else {
slotGamutMaskToggle(false);
}
}
}
-void KisColorSelector::slotGamutMaskSet(KoGamutMask *gamutMask)
+void KisColorSelector::slotGamutMaskSet(KoGamutMaskSP gamutMask)
{
m_mainComponent->setGamutMask(gamutMask);
m_subComponent->setGamutMask(gamutMask);
slotGamutMaskToggle(true);
}
void KisColorSelector::slotGamutMaskUnset()
{
m_mainComponent->unsetGamutMask();
m_subComponent->unsetGamutMask();
slotGamutMaskToggle(false);
}
void KisColorSelector::slotGamutMaskPreviewUpdate()
{
m_mainComponent->updateGamutMaskPreview();
m_subComponent->updateGamutMaskPreview();
}
void KisColorSelector::slotGamutMaskDeactivate()
{
slotGamutMaskToggle(false);
}
void KisColorSelector::slotGamutMaskToggle(bool state)
{
m_mainComponent->toggleGamutMask(state);
m_subComponent->toggleGamutMask(state);
}
void KisColorSelector::updateIcons() {
if (m_button) {
m_button->setIcon(KisIconUtils::loadIcon("configure"));
}
}
void KisColorSelector::hasAtLeastOneDocument(bool value)
{
m_hasAtLeastOneDocumentOpen = value;
}
void KisColorSelector::reset()
{
if (m_mainComponent) {
m_mainComponent->setDirty();
}
if (m_subComponent) {
m_subComponent->setDirty();
}
KisColorSelectorBase::reset();
}
void KisColorSelector::paintEvent(QPaintEvent* e)
{
Q_UNUSED(e);
QPainter p(this);
p.fillRect(0,0,width(), height(), QColor(128,128,128));
p.setRenderHint(QPainter::Antialiasing);
// this variable name isn't entirely accurate to what always happens. see definition in header file to understand it better
if (!m_hasAtLeastOneDocumentOpen) {
p.setOpacity(0.2);
}
m_mainComponent->paintEvent(&p);
m_subComponent->paintEvent(&p);
p.setOpacity(1.0);
}
inline int iconSize(qreal width, qreal height) {
qreal radius = qMin(width, height)/2.;
qreal xm = width/2.;
qreal ym = height/2.;
if(xm>=2*ym || ym>=2*xm)
return qBound<qreal>(5., radius, 32.);
qreal a=-2;
qreal b=2.*(xm+ym);
qreal c=radius*radius-xm*xm-ym*ym;
return qBound<qreal>(5., ((-b+sqrt(b*b-4*a*c))/(2*a)), 32.);
}
void KisColorSelector::resizeEvent(QResizeEvent* e) {
if (m_configuration.subType == KisColorSelectorConfiguration::Ring) {
m_ring->setGeometry(0,0,width(), height());
if (displaySettingsButton()) {
int size = iconSize(width(), height());
m_button->setGeometry(0, 0, size, size);
}
if (m_configuration.mainType == KisColorSelectorConfiguration::Triangle) {
m_triangle->setGeometry(width()/2-m_ring->innerRadius(),
height()/2-m_ring->innerRadius(),
m_ring->innerRadius()*2,
m_ring->innerRadius()*2);
}
else {
int size = m_ring->innerRadius()*2/sqrt(2.);
m_square->setGeometry(width()/2-size/2,
height()/2-size/2,
size,
size);
}
}
else {
// type wheel and square
if (m_configuration.mainType == KisColorSelectorConfiguration::Wheel) {
if(displaySettingsButton()) {
int size = iconSize(width(), height()*0.9);
m_button->setGeometry(0, height()*0.1, size, size);
}
m_mainComponent->setGeometry(0, height()*0.1, width(), height()*0.9);
m_subComponent->setGeometry( 0, 0, width(), height()*0.1);
}
else {
int buttonSize = 0;
if(displaySettingsButton()) {
buttonSize = qBound(20, int(0.1*height()), 32);
m_button->setGeometry(0, 0, buttonSize, buttonSize);
}
if(height()>width()) {
int selectorHeight=height()-buttonSize;
m_mainComponent->setGeometry(0, buttonSize+selectorHeight*0.1, width(), selectorHeight*0.9);
m_subComponent->setGeometry( 0, buttonSize, width(), selectorHeight*0.1);
}
else {
int selectorWidth=width()-buttonSize;
m_mainComponent->setGeometry(buttonSize, height()*0.1, selectorWidth, height()*0.9);
m_subComponent->setGeometry( buttonSize, 0, selectorWidth, height()*0.1);
}
}
}
// reset the correct color after resizing the widget
setColor(m_lastRealColor);
KisColorSelectorBase::resizeEvent(e);
}
void KisColorSelector::mousePressEvent(QMouseEvent* e)
{
e->setAccepted(false);
KisColorSelectorBase::mousePressEvent(e);
if(!e->isAccepted()) {
if(m_mainComponent->wantsGrab(e->x(), e->y()))
m_grabbingComponent=m_mainComponent;
else if(m_subComponent->wantsGrab(e->x(), e->y()))
m_grabbingComponent=m_subComponent;
mouseEvent(e);
updatePreviousColorPreview();
e->accept();
}
}
void KisColorSelector::mouseMoveEvent(QMouseEvent* e)
{
KisColorSelectorBase::mouseMoveEvent(e);
mouseEvent(e);
e->accept();
}
void KisColorSelector::mouseReleaseEvent(QMouseEvent* e)
{
e->setAccepted(false);
KisColorSelectorBase::mouseReleaseEvent(e);
if(!e->isAccepted() &&
!(m_lastRealColor == m_currentRealColor)) {
m_lastRealColor = m_currentRealColor;
m_lastColorRole = Acs::buttonToRole(e->button());
updateColor(m_lastRealColor, m_lastColorRole, false);
updateBaseColorPreview(m_currentRealColor);
e->accept();
}
m_grabbingComponent=0;
}
bool KisColorSelector::displaySettingsButton()
{
return dynamic_cast<KisColorSelectorContainer*>(parent());
}
void KisColorSelector::setColor(const KoColor &color)
{
m_mainComponent->setColor(color);
m_subComponent->setColor(color);
m_lastRealColor = color;
m_signalCompressor->start();
}
void KisColorSelector::mouseEvent(QMouseEvent *e)
{
if (m_grabbingComponent && (e->buttons() & Qt::LeftButton || e->buttons() & Qt::RightButton)) {
m_grabbingComponent->mouseEvent(e->x(), e->y());
KoColor color = m_mainComponent->currentColor();
Acs::ColorRole role = Acs::buttonsToRole(e->button(), e->buttons());
m_currentRealColor = color;
requestUpdateColorAndPreview(color, role);
}
}
void KisColorSelector::init()
{
setAcceptDrops(true);
m_lastColorRole = Acs::Foreground;
m_ring = new KisColorSelectorRing(this);
m_triangle = new KisColorSelectorTriangle(this);
m_slider = new KisColorSelectorSimple(this);
m_square = new KisColorSelectorSimple(this);
m_wheel = new KisColorSelectorWheel(this);
if(displaySettingsButton()) {
m_button = new QPushButton(this);
m_button->setIcon(KisIconUtils::loadIcon("configure"));
m_button->setFlat(true);
connect(m_button, SIGNAL(clicked()), SIGNAL(settingsButtonClicked()));
}
// a tablet can send many more signals, than a mouse
// this causes many repaints, if updating after every signal.
m_signalCompressor = new KisSignalCompressor(20, KisSignalCompressor::FIRST_INACTIVE, this);
connect(m_signalCompressor, SIGNAL(timeout()), SLOT(update()));
setMinimumSize(40, 40);
}
diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector.h b/plugins/dockers/advancedcolorselector/kis_color_selector.h
index a28c860b27..e0533b4d5a 100644
--- a/plugins/dockers/advancedcolorselector/kis_color_selector.h
+++ b/plugins/dockers/advancedcolorselector/kis_color_selector.h
@@ -1,106 +1,107 @@
/*
* 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 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_COLOR_SELECTOR_H
#define KIS_COLOR_SELECTOR_H
#include "kis_color_selector_base.h"
#include <KisColorSelectorConfiguration.h>
+#include <resources/KoGamutMask.h>
class KisColorSelectorRing;
class KisColorSelectorComponent;
class KisColorSelectorSimple;
class KisColorSelectorWheel;
class QPushButton;
class KisSignalCompressor;
class KisColorSelector : public KisColorSelectorBase
{
Q_OBJECT
public:
KisColorSelector(KisColorSelectorConfiguration conf, QWidget* parent = 0);
KisColorSelector(QWidget* parent=0);
KisColorSelectorBase* createPopup() const override;
void setConfiguration(KisColorSelectorConfiguration conf);
KisColorSelectorConfiguration configuration() const;
void setColor(const KoColor &color) override;
/// update icons when a theme update happens
void updateIcons();
void hasAtLeastOneDocument(bool value);
public Q_SLOTS:
void reset() override;
void updateSettings() override;
- void slotGamutMaskSet(KoGamutMask* gamutMask);
+ void slotGamutMaskSet(KoGamutMaskSP gamutMask);
void slotGamutMaskUnset();
void slotGamutMaskPreviewUpdate();
void slotGamutMaskToggle(bool state);
void slotGamutMaskDeactivate();
Q_SIGNALS:
void settingsButtonClicked();
protected:
void paintEvent(QPaintEvent*) override;
void resizeEvent(QResizeEvent*) override;
void mousePressEvent(QMouseEvent*) override;
void mouseMoveEvent(QMouseEvent*) override;
void mouseReleaseEvent(QMouseEvent*) override;
bool displaySettingsButton();
private:
void mouseEvent(QMouseEvent* e);
void init();
KisColorSelectorRing* m_ring;
KisColorSelectorComponent* m_triangle;
KisColorSelectorSimple* m_slider;
KisColorSelectorSimple* m_square;
KisColorSelectorWheel* m_wheel;
QPushButton* m_button;
KisColorSelectorComponent* m_mainComponent;
KisColorSelectorComponent* m_subComponent;
KisColorSelectorComponent* m_grabbingComponent;
KisSignalCompressor *m_signalCompressor;
KisColorSelectorConfiguration m_configuration;
KoColor m_lastRealColor;
KoColor m_currentRealColor;
bool m_blipDisplay;
Acs::ColorRole m_lastColorRole;
/// if Krita starts with a reference to this component that is attached to a canvas, it will call setCanvas()
/// that check will be what ultimately decides whether this component will look enabled or disabled
/// This color selector is sometimes not attached to the canvas, so we shouldn't disable it in that situation
/// One instance of that is when you select the color wheel type from the settings.
bool m_hasAtLeastOneDocumentOpen = true;
public:
void setDisplayBlip(bool disp) {m_blipDisplay = disp;}
bool displayBlip() const {return m_blipDisplay;}
};
#endif // KIS_COLSELNG_COLOR_SELECTOR_H
diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_component.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_component.cpp
index 621772de50..85107a3de5 100644
--- a/plugins/dockers/advancedcolorselector/kis_color_selector_component.cpp
+++ b/plugins/dockers/advancedcolorselector/kis_color_selector_component.cpp
@@ -1,253 +1,253 @@
/*
* 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_component.h"
#include "kis_color_selector_base.h"
#include "KoColorSpace.h"
#include <QPainter>
#include <QMouseEvent>
#include <resources/KoGamutMask.h>
KisColorSelectorComponent::KisColorSelectorComponent(KisColorSelector* parent) :
QObject(parent),
m_hue(0),
m_hsvSaturation(1),
m_value(1),
m_hslSaturation(1),
m_lightness(0.5),
m_hsiSaturation(1),
m_intensity(0.333),
m_hsySaturation(1),
m_luma(0.299),
m_parent(parent),
m_gamutMaskOn(false),
m_currentGamutMask(nullptr),
m_maskPreviewActive(true),
m_lastX(0),
m_lastY(0),
m_x(0),
m_y(0),
m_width(0),
m_height(0),
m_dirty(true),
m_lastColorSpace(0)
{
Q_ASSERT(parent);
}
void KisColorSelectorComponent::setGeometry(int x, int y, int width, int height)
{
m_x=x;
m_y=y;
m_width=width;
m_height=height;
m_dirty=true;
}
void KisColorSelectorComponent::paintEvent(QPainter* painter)
{
painter->save();
painter->translate(m_x, m_y);
paint(painter);
painter->restore();
m_dirty=false;
m_lastColorSpace=colorSpace();
}
void KisColorSelectorComponent::mouseEvent(int x, int y)
{
int newX=qBound(0, (x-m_x), width());
int newY=qBound(0, (y-m_y), height());
if (allowsColorSelectionAtPoint(QPoint(newX, newY))) {
m_lastSelectedColor = selectColor(newX, newY);
m_lastX=newX;
m_lastY=newY;
}
}
const KoColorSpace* KisColorSelectorComponent::colorSpace() const
{
const KoColorSpace* cs = m_parent->colorSpace();
Q_ASSERT(cs);
return cs;
}
void KisColorSelectorComponent::setDirty()
{
m_dirty = true;
setColor(m_lastSelectedColor);
}
-void KisColorSelectorComponent::setGamutMask(KoGamutMask *gamutMask)
+void KisColorSelectorComponent::setGamutMask(KoGamutMaskSP gamutMask)
{
m_currentGamutMask = gamutMask;
m_gamutMaskOn = true;
}
void KisColorSelectorComponent::unsetGamutMask()
{
m_gamutMaskOn = false;
m_currentGamutMask = nullptr;
}
void KisColorSelectorComponent::updateGamutMaskPreview()
{
setDirty();
update();
}
void KisColorSelectorComponent::toggleGamutMask(bool state)
{
m_gamutMaskOn = state;
setDirty();
update();
}
bool KisColorSelectorComponent::isDirty() const
{
return m_dirty || m_lastColorSpace!=colorSpace();
}
bool KisColorSelectorComponent::containsPointInComponentCoords(int x, int y) const
{
if(x>=0 && y>=0 && x<=width() && y<=height())
return true;
else
return false;
}
bool KisColorSelectorComponent::allowsColorSelectionAtPoint(const QPoint & /*pt*/) const
{
return true;
}
KoColor KisColorSelectorComponent::currentColor()
{
return selectColor(m_lastX, m_lastY);
}
void KisColorSelectorComponent::setParam(qreal hue, qreal hsvSaturation, qreal value, qreal hslSaturation, qreal lightness, qreal hsiSaturation, qreal intensity, qreal hsySaturation, qreal luma)
{
if(qFuzzyCompare(m_hue, hue) &&
qFuzzyCompare(m_hsvSaturation, hsvSaturation) &&
qFuzzyCompare(m_value, value) &&
qFuzzyCompare(m_hslSaturation, hslSaturation) &&
qFuzzyCompare(m_lightness, lightness) &&
qFuzzyCompare(m_hsiSaturation, hsiSaturation) &&
qFuzzyCompare(m_intensity, intensity) &&
qFuzzyCompare(m_hsySaturation, hsySaturation) &&
qFuzzyCompare(m_luma, luma))
return;
if(hue>=0. && hue<=1.)
m_hue=hue;
if(hsvSaturation>=0. && hsvSaturation<=1.) {
m_hsvSaturation=hsvSaturation;
m_hslSaturation=-1;
m_hsiSaturation=-1;
m_hsySaturation=-1;
}
if(value>=0. && value<=1.) {
m_value=value;
m_intensity=-1;
m_luma=-1;
m_lightness=-1;
}
if(hslSaturation>=0. && hslSaturation<=1.) {
m_hslSaturation=hslSaturation;
m_hsvSaturation=-1;
m_hsiSaturation=-1;
m_hsySaturation=-1;
}
if(lightness>=0. && lightness<=1.) {
m_lightness=lightness;
m_value=-1;
m_luma=-1;
m_intensity=-1;
}
if(hsiSaturation>=0. && hsiSaturation<=1.) {
m_hsiSaturation=hsiSaturation;
m_hsvSaturation=-1;
m_hslSaturation=-1;
m_hsySaturation=-1;
}
if(intensity>=0. && intensity<=1.) {
m_intensity=intensity;
m_value=-1;
m_luma=-1;
m_lightness=-1;
}
if(hsySaturation>=0. && hsySaturation<=1.) {
m_hsySaturation=hsySaturation;
m_hsvSaturation=-1;
m_hsiSaturation=-1;
m_hslSaturation=-1;
}
if(luma>=0. && luma<=1.) {
m_intensity=-1;
m_value=-1;
m_luma=luma;
m_lightness=-1;
}
m_dirty=true;
emit update();
}
int KisColorSelectorComponent::width() const
{
return m_width;
}
int KisColorSelectorComponent::height() const
{
return m_height;
}
void KisColorSelectorComponent::setConfiguration(Parameter param, Type type)
{
m_parameter = param;
m_type = type;
}
void KisColorSelectorComponent::setColor(const KoColor &color)
{
m_lastSelectedColor = color;
}
void KisColorSelectorComponent::setLastMousePosition(int x, int y)
{
// prevent movement due to rounding errors
if (abs((int)m_lastX - x) > 1 || abs((int)m_lastY - y) > 1) {
m_lastX = x;
m_lastY = y;
}
}
diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_component.h b/plugins/dockers/advancedcolorselector/kis_color_selector_component.h
index 79da0c12aa..e6c2786441 100644
--- a/plugins/dockers/advancedcolorselector/kis_color_selector_component.h
+++ b/plugins/dockers/advancedcolorselector/kis_color_selector_component.h
@@ -1,131 +1,131 @@
/*
* 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_COMPONENT_H
#define KIS_COLOR_SELECTOR_COMPONENT_H
#include <QObject>
#include <QColor>
#include <resources/KoGamutMask.h>
#include "kis_color_selector.h"
class KoColorSpace;
class QPainter;
class KisColorSelectorComponent : public QObject
{
Q_OBJECT
public:
typedef KisColorSelectorConfiguration::Parameters Parameter;
typedef KisColorSelectorConfiguration::Type Type;
explicit KisColorSelectorComponent(KisColorSelector* parent);
void setGeometry(int x, int y, int width, int height);
void paintEvent(QPainter*);
/// saves the mouse position, so that a blip can be created.
virtual void mouseEvent(int x, int y);
/// return the color, that was selected by calling mouseEvent
KoColor currentColor();
int width() const;
int height() const;
/// setConfiguration can be ignored (for instance ring and triangle, as they can have only one config)
void setConfiguration(Parameter param, Type type);
/// set the color, blibs etc
virtual void setColor(const KoColor& color);
/// force subsequent redraw of the component
void setDirty();
/// returns true, if this component wants to grab the mouse (normally true, if containsPoint returns true)
bool wantsGrab(int x, int y) {return containsPointInComponentCoords(x-m_x, y-m_y);}
- void setGamutMask(KoGamutMask* gamutMask);
+ void setGamutMask(KoGamutMaskSP gamutMask);
void unsetGamutMask();
void updateGamutMaskPreview();
void toggleGamutMask(bool state);
public Q_SLOTS:
/// set hue, saturation, value or/and lightness
/// unused parameters should be set to -1
void setParam(qreal hue, qreal hsvSaturation, qreal value, qreal hslSaturation, qreal lightness, qreal hsiSaturation, qreal intensity, qreal hsySaturation, qreal luma);
Q_SIGNALS:
/// request for repaint, for instance, if the hue changes.
void update();
/// -1, if unaffected
void paramChanged(qreal hue, qreal hsvSaturation, qreal value, qreal hslSaturation, qreal lightness, qreal hsiSaturation, qreal intensity, qreal hsySaturation, qreal luma);
protected:
const KoColorSpace* colorSpace() const;
/// returns true, if ether the color space, the size or the parameters have changed since the last paint event
bool isDirty() const;
/// this method must be overloaded to return the color at position x/y and draw a marker on that position
virtual KoColor selectColor(int x, int y) = 0;
/// paint component using given painter
/// the component should respect width() and height() (eg. scale to width and height), but doesn't
/// have to care about x/y coordinates (top left corner)
virtual void paint(QPainter*) = 0;
/// a subclass can implement this method, the default returns true if the coordinates are in the component rect
/// values for the subclasses are provided in component coordinates, eg (0,0) is top left of component
virtual bool containsPointInComponentCoords(int x, int y) const;
/// a subclass can implement this method to note that the point, although it is in
/// containsPointInComponentCoords area, still cannot be selected as a color (e.g.
/// it is masked out). Default implementation always returns true.
virtual bool allowsColorSelectionAtPoint(const QPoint &) const;
// Workaround for Bug 287001
void setLastMousePosition(int x, int y);
qreal m_hue;
qreal m_hsvSaturation;
qreal m_value;
qreal m_hslSaturation;
qreal m_lightness;
qreal m_hsiSaturation;
qreal m_intensity;
qreal m_hsySaturation;
qreal m_luma;
Parameter m_parameter;
Type m_type;
KisColorSelector* m_parent;
bool m_gamutMaskOn;
- KoGamutMask* m_currentGamutMask;
+ KoGamutMaskSP m_currentGamutMask;
bool m_maskPreviewActive;
qreal m_lastX;
qreal m_lastY;
int m_x;
int m_y;
private:
int m_width;
int m_height;
bool m_dirty;
const KoColorSpace* m_lastColorSpace;
KoColor m_lastSelectedColor;
};
#endif // KIS_COLOR_SELECTOR_COMPONENT_H
diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_container.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_container.cpp
index 050074edb1..e44952784e 100644
--- a/plugins/dockers/advancedcolorselector/kis_color_selector_container.cpp
+++ b/plugins/dockers/advancedcolorselector/kis_color_selector_container.cpp
@@ -1,229 +1,229 @@
/*
* 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()) {
- connect(m_canvas->viewManager()->canvasResourceProvider(), SIGNAL(sigGamutMaskChanged(KoGamutMask*)),
- m_colorSelector, SLOT(slotGamutMaskSet(KoGamutMask*)), Qt::UniqueConnection);
+ connect(m_canvas->viewManager()->canvasResourceProvider(), SIGNAL(sigGamutMaskChanged(KoGamutMaskSP)),
+ m_colorSelector, SLOT(slotGamutMaskSet(KoGamutMaskSP)), Qt::UniqueConnection);
connect(m_canvas->viewManager()->canvasResourceProvider(), SIGNAL(sigGamutMaskUnset()),
m_colorSelector, SLOT(slotGamutMaskUnset()), Qt::UniqueConnection);
connect(m_canvas->viewManager()->canvasResourceProvider(), SIGNAL(sigGamutMaskPreviewUpdate()),
m_colorSelector, SLOT(slotGamutMaskPreviewUpdate()), Qt::UniqueConnection);
connect(m_canvas->viewManager()->canvasResourceProvider(), SIGNAL(sigGamutMaskDeactivated()),
m_colorSelector, SLOT(slotGamutMaskDeactivate()), Qt::UniqueConnection);
m_gamutMaskToolbar->connectMaskSignals(m_canvas->viewManager()->canvasResourceProvider());
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();
}
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_common_colors_recalculation_runner.cpp b/plugins/dockers/advancedcolorselector/kis_common_colors_recalculation_runner.cpp
index a740764934..5aa605e9a4 100644
--- a/plugins/dockers/advancedcolorselector/kis_common_colors_recalculation_runner.cpp
+++ b/plugins/dockers/advancedcolorselector/kis_common_colors_recalculation_runner.cpp
@@ -1,213 +1,213 @@
#include "kis_common_colors_recalculation_runner.h"
#include <cmath>
#include <QImage>
#include "KoColor.h"
#include "KoColorSpaceRegistry.h"
#include "kis_common_colors.h"
enum ColorAxis {RedAxis=0, GreenAxis, BlueAxis};
class Color
{
public:
Color(QRgb rgb) : r(qRed(rgb)), g(qGreen(rgb)), b(qBlue(rgb)) {}
unsigned char r;
unsigned char g;
unsigned char b;
inline unsigned char operator[](ColorAxis i) const
{
if(i==RedAxis) return r;
if(i==GreenAxis) return g;
return b;
}
};
class VBox
{
QList<Color> m_colors;
public:
VBox(QList<QRgb> rgbList)
{
QList<Color> colorList;
for(int i=0; i<rgbList.size(); i++) {
colorList.append(Color(rgbList.at(i)));
}
m_colors = colorList;
}
VBox(QList<Color> colorList) : m_colors(colorList) {}
int population() const { return m_colors.size(); }
VBox divide()
{
ColorAxis axis = biggestAxis();
Q_ASSERT(axisSize(axis)>=3);
unsigned char divpos = divPos(axis);
QList<Color> newVBoxColors;
for(int i=m_colors.size()-1; i>=0; i--) {
Color c = m_colors.at(i);
if(c[axis]>divpos) {
m_colors.removeAt(i);
newVBoxColors.append(c);
}
}
return VBox(newVBoxColors);
}
QRgb mean() const
{
int r=0;
int g=0;
int b=0;
for(int i=0;i<m_colors.size(); i++) {
r+=(int) m_colors.at(i)[RedAxis];
g+=(int) m_colors.at(i)[GreenAxis];
b+=(int) m_colors.at(i)[BlueAxis];
}
int size = m_colors.size();
Q_ASSERT(size>0);
return qRgb(r/size, g/size, b/size);
}
unsigned char axisSize(ColorAxis axis) const
{
unsigned char valMin = 255;
unsigned char valMax = 0;
for(int i=0; i<m_colors.size(); i++) {
if(m_colors.at(i)[axis]>valMax)
valMax=m_colors.at(i)[axis];
if(m_colors.at(i)[axis]<valMin)
valMin=m_colors.at(i)[axis];
}
return valMax-valMin;
}
ColorAxis biggestAxis() const
{
unsigned char sR = axisSize(RedAxis);
unsigned char sG = axisSize(GreenAxis);
unsigned char sB = axisSize(BlueAxis);
if(sR>sG && sR>sB) return RedAxis;
if(sG>sR && sG>sB) return GreenAxis;
return BlueAxis;
}
private:
// unsigned char divPos(ColorAxis axis) const
// {
// QList<unsigned char> values;
// for(int i=0;i<m_colors.size(); i++) {
// values.append(m_colors.at(i)[axis]);
// }
// std::sort(values.begin(), values.end());
// return values.at(values.size()*4/5);
// }
unsigned char divPos(ColorAxis axis) const
{
short min=m_colors.at(0)[axis];
short max=m_colors.at(0)[axis];
for(int i=0;i<m_colors.size(); i++) {
if(min>m_colors.at(i)[axis]) min=m_colors.at(i)[axis];
if(max<m_colors.at(i)[axis]) max=m_colors.at(i)[axis];
}
return (min+max)/2;
}
};
void KisCommonColorsRecalculationRunner::run()
{
m_commonColors->setColors(extractColors());
}
QList<KoColor> KisCommonColorsRecalculationRunner::extractColors()
{
QList<QRgb> colors = getColors();
VBox startBox(colors);
QList<VBox> boxes;
boxes.append(startBox);
while (boxes.size()<m_numColors*3/5 && colors.size()>m_numColors*3/5) {
int biggestBox=-1;
int biggestBoxPopulation=-1;
for(int i=0; i<boxes.size(); i++) {
if(boxes.at(i).population()>biggestBoxPopulation &&
boxes.at(i).axisSize(boxes.at(i).biggestAxis())>=3) {
biggestBox=i;
biggestBoxPopulation=boxes.at(i).population();
}
}
if(biggestBox==-1 || boxes[biggestBox].population()<=3)
break;
VBox newBox = boxes[biggestBox].divide();
boxes.append(newBox);
}
while (boxes.size()<m_numColors && colors.size()>m_numColors) {
int biggestBox=-1;
int biggestBoxAxisSize=-1;
for(int i=0; i<boxes.size(); i++) {
if(boxes.at(i).axisSize(boxes.at(i).biggestAxis())>biggestBoxAxisSize &&
boxes.at(i).axisSize(boxes.at(i).biggestAxis())>=3) {
biggestBox=i;
biggestBoxAxisSize=boxes.at(i).axisSize(boxes.at(i).biggestAxis());
}
}
if(biggestBox==-1 || boxes[biggestBox].population()<=3)
break;
VBox newBox = boxes[biggestBox].divide();
boxes.append(newBox);
}
const KoColorSpace* colorSpace = KoColorSpaceRegistry::instance()->rgb8();
QList<KoColor> colorList;
for(int i=0; i<boxes.size(); i++) {
if(boxes.at(i).population()>=1) {
colorList.append(KoColor(QColor(boxes.at(i).mean()), colorSpace));
}
}
return colorList;
}
QList<QRgb> KisCommonColorsRecalculationRunner::getColors()
{
int width = m_imageData.width();
int height = m_imageData.height();
QImage tmpImage;
int pixelCount = height*width;
if(pixelCount> (1<<16)) {
qreal factor = sqrt((1<<16)/(qreal) pixelCount);
tmpImage = m_imageData.scaledToWidth(width*factor);
}
else {
tmpImage = m_imageData;
}
width=tmpImage.width();
height=tmpImage.height();
QSet<QRgb> colorList;
for (int i=0; i<width; i++) {
for (int j=0; j<height; j++) {
colorList.insert(tmpImage.pixel(i, j)|qRgba(0,0,0,255));
}
}
- return colorList.toList();
+ return QList<QRgb>(colorList.begin(), colorList.end());
}
diff --git a/plugins/dockers/animation/kis_animation_curves_view.cpp b/plugins/dockers/animation/kis_animation_curves_view.cpp
index caadb945ef..a7889ec772 100644
--- a/plugins/dockers/animation/kis_animation_curves_view.cpp
+++ b/plugins/dockers/animation/kis_animation_curves_view.cpp
@@ -1,788 +1,788 @@
/*
* Copyright (c) 2016 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 "kis_animation_curves_view.h"
#include <QPaintEvent>
#include <QMouseEvent>
#include <QApplication>
#include <QScrollBar>
#include <qpainter.h>
#include <QtMath>
#include "kis_animation_curves_model.h"
#include "timeline_ruler_header.h"
#include "kis_animation_curves_value_ruler.h"
#include "kis_animation_curves_keyframe_delegate.h"
#include "kis_scalar_keyframe_channel.h"
#include "kis_zoom_button.h"
#include "kis_custom_modifiers_catcher.h"
#include "krita_utils.h"
const int VERTICAL_PADDING = 30;
struct KisAnimationCurvesView::Private
{
Private()
{}
KisAnimationCurvesModel *model {0};
TimelineRulerHeader *horizontalHeader {0};
KisAnimationCurvesValueRuler *verticalHeader {0};
KisAnimationCurvesKeyframeDelegate *itemDelegate {0};
KisZoomButton *horizontalZoomButton {0};
KisZoomButton *verticalZoomButton {0};
KisCustomModifiersCatcher *modifiersCatcher {0};
bool isDraggingKeyframe {false};
bool isAdjustingHandle {false};
int adjustedHandle {0}; // 0 = left, 1 = right
QPoint dragStart;
QPoint dragOffset;
int horizontalZoomStillPointIndex {0};
int horizontalZoomStillPointOriginalOffset {0};
qreal verticalZoomStillPoint {0.0};
qreal verticalZoomStillPointOriginalOffset {0.0};
bool panning {false};
QPoint panStartOffset;
};
KisAnimationCurvesView::KisAnimationCurvesView(QWidget *parent)
: QAbstractItemView(parent)
, m_d(new Private())
{
m_d->horizontalHeader = new TimelineRulerHeader(this);
m_d->verticalHeader = new KisAnimationCurvesValueRuler(this);
m_d->itemDelegate = new KisAnimationCurvesKeyframeDelegate(m_d->horizontalHeader, m_d->verticalHeader, this);
m_d->modifiersCatcher = new KisCustomModifiersCatcher(this);
m_d->modifiersCatcher->addModifier("pan-zoom", Qt::Key_Space);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(this);
if (scroller){
connect(scroller, SIGNAL(stateChanged(QScroller::State)),
this, SLOT(slotScrollerStateChanged(QScroller::State)));
}
}
KisAnimationCurvesView::~KisAnimationCurvesView()
{}
void KisAnimationCurvesView::setModel(QAbstractItemModel *model)
{
m_d->model = dynamic_cast<KisAnimationCurvesModel*>(model);
QAbstractItemView::setModel(model);
m_d->horizontalHeader->setModel(model);
connect(model, &QAbstractItemModel::rowsInserted,
this, &KisAnimationCurvesView::slotRowsChanged);
connect(model, &QAbstractItemModel::rowsRemoved,
this, &KisAnimationCurvesView::slotRowsChanged);
connect(model, &QAbstractItemModel::dataChanged,
this, &KisAnimationCurvesView::slotDataChanged);
connect(model, &QAbstractItemModel::headerDataChanged,
this, &KisAnimationCurvesView::slotHeaderDataChanged);
}
void KisAnimationCurvesView::setZoomButtons(KisZoomButton *horizontal, KisZoomButton *vertical)
{
m_d->horizontalZoomButton = horizontal;
m_d->verticalZoomButton = vertical;
connect(horizontal, &KisZoomButton::zoomStarted, this, &KisAnimationCurvesView::slotHorizontalZoomStarted);
connect(horizontal, &KisZoomButton::zoomLevelChanged, this, &KisAnimationCurvesView::slotHorizontalZoomLevelChanged);
connect(vertical, &KisZoomButton::zoomStarted, this, &KisAnimationCurvesView::slotVerticalZoomStarted);
connect(vertical, &KisZoomButton::zoomLevelChanged, this, &KisAnimationCurvesView::slotVerticalZoomLevelChanged);
}
QRect KisAnimationCurvesView::visualRect(const QModelIndex &index) const
{
return m_d->itemDelegate->itemRect(index);
}
void KisAnimationCurvesView::scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint hint)
{
// TODO
Q_UNUSED(index);
Q_UNUSED(hint);
}
QModelIndex KisAnimationCurvesView::indexAt(const QPoint &point) const
{
if (!model()) return QModelIndex();
int time = m_d->horizontalHeader->logicalIndexAt(point.x());
int rows = model()->rowCount();
for (int row=0; row < rows; row++) {
QModelIndex index = model()->index(row, time);
if (index.data(KisTimeBasedItemModel::SpecialKeyframeExists).toBool()) {
QRect nodePos = m_d->itemDelegate->itemRect(index);
if (nodePos.contains(point)) {
return index;
}
}
}
return QModelIndex();
}
void KisAnimationCurvesView::paintEvent(QPaintEvent *e)
{
QPainter painter(viewport());
QRect r = e->rect();
r.translate(dirtyRegionOffset());
int firstFrame = m_d->horizontalHeader->logicalIndexAt(r.left());
int lastFrame = m_d->horizontalHeader->logicalIndexAt(r.right());
if (lastFrame == -1) lastFrame = model()->columnCount();
paintFrames(painter);
paintCurves(painter, firstFrame, lastFrame);
paintKeyframes(painter, firstFrame, lastFrame);
}
void KisAnimationCurvesView::paintFrames(QPainter &painter)
{
const QColor textColor = qApp->palette().text().color();
- const QColor backgroundColor = qApp->palette().background().color();
+ const QColor backgroundColor = qApp->palette().window().color();
// paint vertical lines so it is easier to tell where each frame starts/stops
QColor blendedColor = KritaUtils::blendColors(textColor, backgroundColor, 0.2);
painter.setPen(QPen(blendedColor, 1));
int channels = model()->rowCount();
for (int channel = 0; channel < channels; channel++) {
// draw border around entire frame, so override the height and Y position
for (int time = 0; time <= model()->columnCount(); time++) {
- QModelIndex index = model()->index(channel, time);
+ //QModelIndex index = model()->index(channel, time);
int offset = 0;
if (m_d->horizontalHeader && m_d->horizontalHeader->offset()) {
offset = m_d->horizontalHeader->offset();
}
int horizontalStepSize = m_d->horizontalHeader->defaultSectionSize();
int xPosition = horizontalStepSize * time - offset;
QRect frameRect = QRect(xPosition, -10, horizontalStepSize, 9999);
painter.drawLine(frameRect.topRight(), frameRect.bottomRight());
}
}
}
void KisAnimationCurvesView::paintCurves(QPainter &painter, int firstFrame, int lastFrame)
{
int channels = model()->rowCount();
for (int channel = 0; channel < channels; channel++) {
QModelIndex index0 = model()->index(channel, 0);
if (!isIndexHidden(index0)) {
QColor color = index0.data(KisAnimationCurvesModel::CurveColorRole).value<QColor>();
painter.setPen(QPen(color, 1));
paintCurve(channel, firstFrame, lastFrame, painter);
}
}
}
void KisAnimationCurvesView::paintCurve(int channel, int firstFrame, int lastFrame, QPainter &painter)
{
int selectionOffset = m_d->isDraggingKeyframe ? (m_d->dragOffset.x() / m_d->horizontalHeader->defaultSectionSize()) : 0;
QModelIndex index = findNextKeyframeIndex(channel, firstFrame+1, selectionOffset, true);
if (!index.isValid()) {
index = findNextKeyframeIndex(channel, firstFrame, selectionOffset, false);
}
if (!index.isValid()) return;
QPointF previousKeyPos = m_d->itemDelegate->nodeCenter(index, selectionModel()->isSelected(index));
QPointF rightTangent = m_d->itemDelegate->rightHandle(index, index == currentIndex());
while(index.column() <= lastFrame) {
int interpolationMode = index.data(KisAnimationCurvesModel::InterpolationModeRole).toInt();
int time = (m_d->isDraggingKeyframe && selectionModel()->isSelected(index)) ? index.column() + selectionOffset : index.column();
index = findNextKeyframeIndex(channel, time, selectionOffset, false);
if (!index.isValid()) return;
bool active = (index == currentIndex());
QPointF nextKeyPos = m_d->itemDelegate->nodeCenter(index, selectionModel()->isSelected(index));
QPointF leftTangent = m_d->itemDelegate->leftHandle(index, active);
if (interpolationMode == KisKeyframe::Constant) {
painter.drawLine(previousKeyPos, QPointF(nextKeyPos.x(), previousKeyPos.y()));
} else if (interpolationMode == KisKeyframe::Linear) {
painter.drawLine(previousKeyPos, nextKeyPos);
} else {
paintCurveSegment(painter, previousKeyPos, rightTangent, leftTangent, nextKeyPos);
}
previousKeyPos = nextKeyPos;
rightTangent = m_d->itemDelegate->rightHandle(index, active);
}
}
void KisAnimationCurvesView::paintCurveSegment(QPainter &painter, QPointF pos1, QPointF rightTangent, QPointF leftTangent, QPointF pos2) {
const int steps = 16;
QPointF previousPos;
for (int step = 0; step <= steps; step++) {
qreal t = 1.0 * step / steps;
QPointF nextPos = KisScalarKeyframeChannel::interpolate(pos1, rightTangent, leftTangent, pos2, t);
if (step > 0) {
painter.drawLine(previousPos, nextPos);
}
previousPos = nextPos;
}
}
void KisAnimationCurvesView::paintKeyframes(QPainter &painter, int firstFrame, int lastFrame)
{
int channels = model()->rowCount();
for (int channel = 0; channel < channels; channel++) {
for (int time=firstFrame; time <= lastFrame; time++) {
QModelIndex index = model()->index(channel, time);
bool keyframeExists = model()->data(index, KisAnimationCurvesModel::SpecialKeyframeExists).toReal();
if (keyframeExists && !isIndexHidden(index)) {
QStyleOptionViewItem opt;
if (selectionModel()->isSelected(index)) {
opt.state |= QStyle::State_Selected;
}
if (index == selectionModel()->currentIndex()) {
opt.state |= QStyle::State_HasFocus;
}
m_d->itemDelegate->paint(&painter, opt, index);
}
}
}
}
QModelIndex KisAnimationCurvesView::findNextKeyframeIndex(int channel, int time, int selectionOffset, bool backward)
{
KisAnimationCurvesModel::ItemDataRole role =
backward ? KisAnimationCurvesModel::PreviousKeyframeTime : KisAnimationCurvesModel::NextKeyframeTime;
QModelIndex currentIndex = model()->index(channel, time);
if (!selectionOffset) {
QVariant next = currentIndex.data(role);
return (next.isValid()) ? model()->index(channel, next.toInt()) : QModelIndex();
} else {
// Find the next unselected index
QModelIndex nextIndex = currentIndex;
do {
QVariant next = nextIndex.data(role);
nextIndex = (next.isValid()) ? model()->index(channel, next.toInt()) : QModelIndex();
} while(nextIndex.isValid() && selectionModel()->isSelected(nextIndex));
// Find the next selected index, accounting for D&D offset
QModelIndex draggedIndex = model()->index(channel, qMax(0, time - selectionOffset));
do {
QVariant next = draggedIndex.data(role);
draggedIndex = (next.isValid()) ? model()->index(channel, next.toInt()) : QModelIndex();
} while(draggedIndex.isValid() && !selectionModel()->isSelected(draggedIndex));
// Choose the earlier of the two
if (draggedIndex.isValid() && nextIndex.isValid()) {
if (draggedIndex.column() + selectionOffset <= nextIndex.column()) {
return draggedIndex;
} else {
return nextIndex;
}
} else if (draggedIndex.isValid()) {
return draggedIndex;
} else {
return nextIndex;
}
}
}
void KisAnimationCurvesView::findExtremes(qreal *minimum, qreal *maximum)
{
if (!model()) return;
qreal min = qInf();
qreal max = -qInf();
int rows = model()->rowCount();
for (int row = 0; row < rows; row++) {
QModelIndex index = model()->index(row, 0);
if (isIndexHidden(index)) continue;
QVariant nextTime;
do {
qreal value = index.data(KisAnimationCurvesModel::ScalarValueRole).toReal();
if (value < min) min = value;
if (value > max) max = value;
nextTime = index.data(KisAnimationCurvesModel::NextKeyframeTime);
if (nextTime.isValid()) index = model()->index(row, nextTime.toInt());
} while (nextTime.isValid());
}
if (qIsFinite(min)) *minimum = min;
if (qIsFinite(max)) *maximum = max;
}
void KisAnimationCurvesView::updateVerticalRange()
{
if (!model()) return;
qreal minimum = 0;
qreal maximum = 0;
findExtremes(&minimum, &maximum);
int viewMin = maximum * m_d->verticalHeader->scaleFactor();
int viewMax = minimum * m_d->verticalHeader->scaleFactor();
viewMin -= VERTICAL_PADDING;
viewMax += VERTICAL_PADDING;
verticalScrollBar()->setRange(viewMin, viewMax - viewport()->height());
}
void KisAnimationCurvesView::startPan(QPoint mousePos)
{
m_d->dragStart = mousePos;
m_d->panStartOffset = QPoint(horizontalOffset(), verticalOffset());
m_d->panning = true;
}
QModelIndex KisAnimationCurvesView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{
// TODO
Q_UNUSED(cursorAction);
Q_UNUSED(modifiers);
return QModelIndex();
}
int KisAnimationCurvesView::horizontalOffset() const
{
return m_d->horizontalHeader->offset();
}
int KisAnimationCurvesView::verticalOffset() const
{
return m_d->verticalHeader->offset();
}
bool KisAnimationCurvesView::isIndexHidden(const QModelIndex &index) const
{
return !index.data(KisAnimationCurvesModel::CurveVisibleRole).toBool();
}
void KisAnimationCurvesView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
{
int timeFrom = m_d->horizontalHeader->logicalIndexAt(rect.left());
int timeTo = m_d->horizontalHeader->logicalIndexAt(rect.right());
QItemSelection selection;
int rows = model()->rowCount();
for (int row=0; row < rows; row++) {
for (int time = timeFrom; time <= timeTo; time++) {
QModelIndex index = model()->index(row, time);
if (index.data(KisTimeBasedItemModel::SpecialKeyframeExists).toBool()) {
QRect itemRect = m_d->itemDelegate->itemRect(index);
if (itemRect.intersects(rect)) {
selection.select(index, index);
}
}
}
}
selectionModel()->select(selection, command);
}
QRegion KisAnimationCurvesView::visualRegionForSelection(const QItemSelection &selection) const
{
QRegion region;
Q_FOREACH(QModelIndex index, selection.indexes()) {
region += m_d->itemDelegate->visualRect(index);
}
return region;
}
void KisAnimationCurvesView::mousePressEvent(QMouseEvent *e)
{
if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) {
if (e->button() == Qt::LeftButton) {
startPan(e->pos());
} else {
qreal horizontalStaticPoint = m_d->horizontalHeader->logicalIndexAt(e->pos().x());
qreal verticalStaticPoint = m_d->verticalHeader->mapViewToValue(e->pos().y());
m_d->horizontalZoomButton->beginZoom(QPoint(e->pos().x(), 0), horizontalStaticPoint);
m_d->verticalZoomButton->beginZoom(QPoint(0, e->pos().y()), verticalStaticPoint);
}
} else if (e->button() == Qt::LeftButton) {
m_d->dragStart = e->pos();
Q_FOREACH(QModelIndex index, selectedIndexes()) {
QPointF center = m_d->itemDelegate->nodeCenter(index, false);
bool hasLeftHandle = m_d->itemDelegate->hasHandle(index, 0);
bool hasRightHandle = m_d->itemDelegate->hasHandle(index, 1);
QPointF leftHandle = center + m_d->itemDelegate->leftHandle(index, false);
QPointF rightHandle = center + m_d->itemDelegate->rightHandle(index, false);
if (hasLeftHandle && (e->localPos() - leftHandle).manhattanLength() < 8) {
m_d->isAdjustingHandle = true;
m_d->adjustedHandle = 0;
setCurrentIndex(index);
return;
} else if (hasRightHandle && (e->localPos() - rightHandle).manhattanLength() < 8) {
m_d->isAdjustingHandle = true;
m_d->adjustedHandle = 1;
setCurrentIndex(index);
return;
}
}
}
QAbstractItemView::mousePressEvent(e);
}
void KisAnimationCurvesView::mouseMoveEvent(QMouseEvent *e)
{
if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) {
if (e->buttons() & Qt::LeftButton) {
if (!m_d->panning) startPan(e->pos());
QPoint diff = e->pos() - m_d->dragStart;
QPoint newOffset = m_d->panStartOffset - diff;
horizontalScrollBar()->setValue(newOffset.x());
verticalScrollBar()->setValue(newOffset.y());
m_d->verticalHeader->setOffset(newOffset.y());
viewport()->update();
} else {
m_d->horizontalZoomButton->continueZoom(QPoint(e->pos().x(), 0));
m_d->verticalZoomButton->continueZoom(QPoint(0, e->pos().y()));
}
} else if (e->buttons() & Qt::LeftButton) {
m_d->dragOffset = e->pos() - m_d->dragStart;
if (m_d->isAdjustingHandle) {
m_d->itemDelegate->setHandleAdjustment(m_d->dragOffset, m_d->adjustedHandle);
viewport()->update();
return;
} else if (m_d->isDraggingKeyframe) {
m_d->itemDelegate->setSelectedItemVisualOffset(m_d->dragOffset);
viewport()->update();
return;
} else if (selectionModel()->hasSelection()) {
if ((e->pos() - m_d->dragStart).manhattanLength() > QApplication::startDragDistance()) {
m_d->isDraggingKeyframe = true;
}
}
}
QAbstractItemView::mouseMoveEvent(e);
}
void KisAnimationCurvesView::mouseReleaseEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton) {
m_d->panning = false;
if (m_d->isDraggingKeyframe) {
QModelIndexList indexes = selectedIndexes();
int timeOffset = m_d->dragOffset.x() / m_d->horizontalHeader->defaultSectionSize();
qreal valueOffset = m_d->dragOffset.y() / m_d->verticalHeader->scaleFactor();
KisAnimationCurvesModel *curvesModel = dynamic_cast<KisAnimationCurvesModel*>(model());
curvesModel->adjustKeyframes(indexes, timeOffset, valueOffset);
m_d->isDraggingKeyframe = false;
m_d->itemDelegate->setSelectedItemVisualOffset(QPointF());
viewport()->update();
} else if (m_d->isAdjustingHandle) {
QModelIndex index = currentIndex();
int mode = index.data(KisAnimationCurvesModel::TangentsModeRole).toInt();
m_d->model->beginCommand(kundo2_i18n("Adjust tangent"));
if (mode == KisKeyframe::Smooth) {
QPointF leftHandle = m_d->itemDelegate->leftHandle(index, true);
QPointF rightHandle = m_d->itemDelegate->rightHandle(index, true);
QPointF leftTangent = m_d->itemDelegate->unscaledTangent(leftHandle);
QPointF rightTangent = m_d->itemDelegate->unscaledTangent(rightHandle);
model()->setData(index, leftTangent, KisAnimationCurvesModel::LeftTangentRole);
model()->setData(index, rightTangent, KisAnimationCurvesModel::RightTangentRole);
} else {
if (m_d->adjustedHandle == 0) {
QPointF leftHandle = m_d->itemDelegate->leftHandle(index, true);
model()->setData(index, m_d->itemDelegate->unscaledTangent(leftHandle), KisAnimationCurvesModel::LeftTangentRole);
} else {
QPointF rightHandle = m_d->itemDelegate->rightHandle(index, true);
model()->setData(index, m_d->itemDelegate->unscaledTangent(rightHandle), KisAnimationCurvesModel::RightTangentRole);
}
}
m_d->model->endCommand();
m_d->isAdjustingHandle = false;
m_d->itemDelegate->setHandleAdjustment(QPointF(), m_d->adjustedHandle);
}
}
QAbstractItemView::mouseReleaseEvent(e);
}
void KisAnimationCurvesView::scrollContentsBy(int dx, int dy)
{
m_d->horizontalHeader->setOffset(horizontalScrollBar()->value());
m_d->verticalHeader->setOffset(verticalScrollBar()->value());
scrollDirtyRegion(dx, dy);
viewport()->scroll(dx, dy);
viewport()->update();
}
void KisAnimationCurvesView::updateGeometries()
{
int topMargin = qMax(m_d->horizontalHeader->minimumHeight(),
m_d->horizontalHeader->sizeHint().height());
int leftMargin = m_d->verticalHeader->sizeHint().width();
setViewportMargins(leftMargin, topMargin, 0, 0);
QRect viewRect = viewport()->geometry();
m_d->horizontalHeader->setGeometry(leftMargin, 0, viewRect.width(), topMargin);
m_d->verticalHeader->setGeometry(0, topMargin, leftMargin, viewRect.height());
horizontalScrollBar()->setRange(0, m_d->horizontalHeader->length() - viewport()->width());
updateVerticalRange();
QAbstractItemView::updateGeometries();
}
void KisAnimationCurvesView::slotRowsChanged(const QModelIndex &parentIndex, int first, int last)
{
Q_UNUSED(parentIndex);
Q_UNUSED(first);
Q_UNUSED(last);
updateVerticalRange();
viewport()->update();
}
void KisAnimationCurvesView::slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
Q_UNUSED(topLeft);
Q_UNUSED(bottomRight);
updateVerticalRange();
viewport()->update();
// this forces the horizontal ruler to refresh. Repaint() doesn't do it for some reason
// If you remove this, scrubbing the timeline will probably stop updating the indicator
m_d->horizontalHeader->resize(m_d->horizontalHeader->width()-1, m_d->horizontalHeader->height());
m_d->horizontalHeader->resize(m_d->horizontalHeader->width()+1, m_d->horizontalHeader->height());
}
void KisAnimationCurvesView::slotHeaderDataChanged(Qt::Orientation orientation, int first, int last)
{
Q_UNUSED(orientation);
Q_UNUSED(first);
Q_UNUSED(last);
viewport()->update();
}
void KisAnimationCurvesView::slotHorizontalZoomStarted(qreal staticPoint)
{
m_d->horizontalZoomStillPointIndex =
qIsNaN(staticPoint) ? currentIndex().column() : staticPoint;
const int w = m_d->horizontalHeader->defaultSectionSize();
m_d->horizontalZoomStillPointOriginalOffset =
w * m_d->horizontalZoomStillPointIndex -
horizontalScrollBar()->value();
}
void KisAnimationCurvesView::slotHorizontalZoomLevelChanged(qreal zoomLevel)
{
if (m_d->horizontalHeader->setZoom(zoomLevel)) {
const int w = m_d->horizontalHeader->defaultSectionSize();
horizontalScrollBar()->setValue(w * m_d->horizontalZoomStillPointIndex - m_d->horizontalZoomStillPointOriginalOffset);
viewport()->update();
}
}
void KisAnimationCurvesView::slotVerticalZoomStarted(qreal staticPoint)
{
m_d->verticalZoomStillPoint = qIsNaN(staticPoint) ? 0 : staticPoint;
const float scale = m_d->verticalHeader->scaleFactor();
m_d->verticalZoomStillPointOriginalOffset =
scale * m_d->verticalZoomStillPoint - m_d->verticalHeader->offset();
}
void KisAnimationCurvesView::slotVerticalZoomLevelChanged(qreal zoomLevel)
{
if (!qFuzzyCompare((float)zoomLevel, m_d->verticalHeader->scaleFactor())) {
m_d->verticalHeader->setScale(zoomLevel);
m_d->verticalHeader->setOffset(-zoomLevel * m_d->verticalZoomStillPoint - m_d->verticalZoomStillPointOriginalOffset);
verticalScrollBar()->setValue(m_d->verticalHeader->offset());
viewport()->update();
}
}
void KisAnimationCurvesView::applyConstantMode()
{
m_d->model->beginCommand(kundo2_i18n("Set interpolation mode"));
Q_FOREACH(QModelIndex index, selectedIndexes()) {
m_d->model->setData(index, KisKeyframe::Constant, KisAnimationCurvesModel::InterpolationModeRole);
}
m_d->model->endCommand();
}
void KisAnimationCurvesView::applyLinearMode()
{
m_d->model->beginCommand(kundo2_i18n("Set interpolation mode"));
Q_FOREACH(QModelIndex index, selectedIndexes()) {
m_d->model->setData(index, KisKeyframe::Linear, KisAnimationCurvesModel::InterpolationModeRole);
}
m_d->model->endCommand();
}
void KisAnimationCurvesView::applyBezierMode()
{
m_d->model->beginCommand(kundo2_i18n("Set interpolation mode"));
Q_FOREACH(QModelIndex index, selectedIndexes()) {
m_d->model->setData(index, KisKeyframe::Bezier, KisAnimationCurvesModel::InterpolationModeRole);
}
m_d->model->endCommand();
}
void KisAnimationCurvesView::applySmoothMode()
{
m_d->model->beginCommand(kundo2_i18n("Set interpolation mode"));
Q_FOREACH(QModelIndex index, selectedIndexes()) {
QVector2D leftVisualTangent(m_d->itemDelegate->leftHandle(index, false));
QVector2D rightVisualTangent(m_d->itemDelegate->rightHandle(index, false));
if (leftVisualTangent.lengthSquared() > 0 && rightVisualTangent.lengthSquared() > 0) {
float leftAngle = qAtan2(-leftVisualTangent.y(), -leftVisualTangent.x());
float rightAngle = qAtan2(rightVisualTangent.y(), rightVisualTangent.x());
float angle = (leftAngle + rightAngle) / 2;
QVector2D unit(qCos(angle), qSin(angle));
leftVisualTangent = -unit * QVector2D(leftVisualTangent).length();
rightVisualTangent = unit * QVector2D(rightVisualTangent).length();
QPointF leftTangent = m_d->itemDelegate->unscaledTangent(leftVisualTangent.toPointF());
QPointF rightTangent = m_d->itemDelegate->unscaledTangent(rightVisualTangent.toPointF());
model()->setData(index, leftTangent, KisAnimationCurvesModel::LeftTangentRole);
model()->setData(index, rightTangent, KisAnimationCurvesModel::RightTangentRole);
}
model()->setData(index, KisKeyframe::Smooth, KisAnimationCurvesModel::TangentsModeRole);
}
m_d->model->endCommand();
}
void KisAnimationCurvesView::applySharpMode()
{
m_d->model->beginCommand(kundo2_i18n("Set interpolation mode"));
Q_FOREACH(QModelIndex index, selectedIndexes()) {
model()->setData(index, KisKeyframe::Sharp, KisAnimationCurvesModel::TangentsModeRole);
}
m_d->model->endCommand();
}
void KisAnimationCurvesView::createKeyframe()
{
QModelIndex active = currentIndex();
int channel = active.isValid() ? active.row() : 0;
int time = m_d->model->currentTime();
QModelIndex index = m_d->model->index(channel, time);
qreal value = index.data(KisAnimationCurvesModel::ScalarValueRole).toReal();
m_d->model->setData(index, value, KisAnimationCurvesModel::ScalarValueRole);
}
void KisAnimationCurvesView::removeKeyframes()
{
m_d->model->removeFrames(selectedIndexes());
}
void KisAnimationCurvesView::zoomToFit()
{
if (!model()) return;
qreal minimum, maximum;
findExtremes(&minimum, &maximum);
if (minimum == maximum) return;
qreal zoomLevel = (viewport()->height() - 2 * VERTICAL_PADDING) / (maximum - minimum);
qreal offset = -VERTICAL_PADDING - zoomLevel * maximum;
m_d->verticalHeader->setScale(zoomLevel);
m_d->verticalHeader->setOffset(offset);
verticalScrollBar()->setValue(offset);
viewport()->update();
}
diff --git a/plugins/dockers/animation/timeline_frames_model.cpp b/plugins/dockers/animation/timeline_frames_model.cpp
index 6d3cb1aabf..aba15fd868 100644
--- a/plugins/dockers/animation/timeline_frames_model.cpp
+++ b/plugins/dockers/animation/timeline_frames_model.cpp
@@ -1,1026 +1,1026 @@
/*
* 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_model.h"
#include <QFont>
#include <QSize>
#include <QColor>
#include <QMimeData>
#include <QPointer>
-#include <KoResourceModel.h>
+#include <KisResourceModel.h>
#include "kis_layer.h"
#include "kis_config.h"
#include "kis_global.h"
#include "kis_debug.h"
#include "kis_image.h"
#include "kis_image_animation_interface.h"
#include "kis_undo_adapter.h"
#include "kis_node_dummies_graph.h"
#include "kis_dummies_facade_base.h"
#include "KisNodeDisplayModeAdapter.h"
#include "kis_signal_compressor.h"
#include "kis_signal_compressor_with_param.h"
#include "kis_keyframe_channel.h"
#include "kundo2command.h"
#include "kis_post_execution_undo_adapter.h"
#include <commands/kis_node_property_list_command.h>
#include <commands_new/kis_switch_current_time_command.h>
#include "kis_animation_utils.h"
#include "timeline_color_scheme.h"
#include "kis_node_model.h"
#include "kis_projection_leaf.h"
#include "kis_time_range.h"
#include "kis_node_view_color_scheme.h"
#include "krita_utils.h"
#include "KisPart.h"
#include <QApplication>
#include "KisDocument.h"
#include "KisViewManager.h"
#include "kis_processing_applicator.h"
#include <KisImageBarrierLockerWithFeedback.h>
#include "kis_node_uuid_info.h"
struct TimelineFramesModel::Private
{
Private()
: activeLayerIndex(0),
dummiesFacade(0),
needFinishInsertRows(false),
needFinishRemoveRows(false),
updateTimer(200, KisSignalCompressor::FIRST_INACTIVE),
parentOfRemovedNode(0)
{}
int activeLayerIndex;
QPointer<KisDummiesFacadeBase> dummiesFacade;
KisImageWSP image;
bool needFinishInsertRows;
bool needFinishRemoveRows;
QList<KisNodeDummy*> updateQueue;
KisSignalCompressor updateTimer;
KisNodeDummy* parentOfRemovedNode;
QScopedPointer<TimelineNodeListKeeper> converter;
QScopedPointer<NodeManipulationInterface> nodeInterface;
QPersistentModelIndex lastClickedIndex;
QVariant layerName(int row) const {
KisNodeDummy *dummy = converter->dummyFromRow(row);
if (!dummy) return QVariant();
return dummy->node()->name();
}
bool layerEditable(int row) const {
KisNodeDummy *dummy = converter->dummyFromRow(row);
if (!dummy) return true;
return dummy->node()->visible() && !dummy->node()->userLocked();
}
bool frameExists(int row, int column) const {
KisNodeDummy *dummy = converter->dummyFromRow(row);
if (!dummy) return false;
KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id());
return (primaryChannel && primaryChannel->keyframeAt(column));
}
bool frameHasContent(int row, int column) {
KisNodeDummy *dummy = converter->dummyFromRow(row);
KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id());
if (!primaryChannel) return false;
// first check if we are a key frame
KisKeyframeSP frame = primaryChannel->activeKeyframeAt(column);
if (!frame) return false;
return frame->hasContent();
}
bool specialKeyframeExists(int row, int column) {
KisNodeDummy *dummy = converter->dummyFromRow(row);
if (!dummy) return false;
Q_FOREACH(KisKeyframeChannel *channel, dummy->node()->keyframeChannels()) {
if (channel->id() != KisKeyframeChannel::Content.id() && channel->keyframeAt(column)) {
return true;
}
}
return false;
}
int frameColorLabel(int row, int column) {
KisNodeDummy *dummy = converter->dummyFromRow(row);
if (!dummy) return -1;
KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id());
if (!primaryChannel) return -1;
KisKeyframeSP frame = primaryChannel->activeKeyframeAt(column);
if (!frame) return -1;
return frame->colorLabel();
}
void setFrameColorLabel(int row, int column, int color) {
KisNodeDummy *dummy = converter->dummyFromRow(row);
if (!dummy) return;
KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id());
if (!primaryChannel) return;
KisKeyframeSP frame = primaryChannel->keyframeAt(column);
if (!frame) return;
frame->setColorLabel(color);
}
int layerColorLabel(int row) const {
KisNodeDummy *dummy = converter->dummyFromRow(row);
if (!dummy) return -1;
return dummy->node()->colorLabelIndex();
}
QVariant layerProperties(int row) const {
KisNodeDummy *dummy = converter->dummyFromRow(row);
if (!dummy) return QVariant();
PropertyList props = dummy->node()->sectionModelProperties();
return QVariant::fromValue(props);
}
bool setLayerProperties(int row, PropertyList props) {
KisNodeDummy *dummy = converter->dummyFromRow(row);
if (!dummy) return false;
nodeInterface->setNodeProperties(dummy->node(), image, props);
return true;
}
bool addKeyframe(int row, int column, bool copy) {
KisNodeDummy *dummy = converter->dummyFromRow(row);
if (!dummy) return false;
KisNodeSP node = dummy->node();
if (!KisAnimationUtils::supportsContentFrames(node)) return false;
KisAnimationUtils::createKeyframeLazy(image, node, KisKeyframeChannel::Content.id(), column, copy);
return true;
}
bool addNewLayer(int row) {
Q_UNUSED(row);
if (nodeInterface) {
KisLayerSP layer = nodeInterface->addPaintLayer();
layer->setUseInTimeline(true);
}
return true;
}
bool removeLayer(int row) {
KisNodeDummy *dummy = converter->dummyFromRow(row);
if (!dummy) return false;
if (nodeInterface) {
nodeInterface->removeNode(dummy->node());
}
return true;
}
};
TimelineFramesModel::TimelineFramesModel(QObject *parent)
: ModelWithExternalNotifications(parent),
m_d(new Private)
{
connect(&m_d->updateTimer, SIGNAL(timeout()), SLOT(processUpdateQueue()));
}
TimelineFramesModel::~TimelineFramesModel()
{
}
bool TimelineFramesModel::hasConnectionToCanvas() const
{
return m_d->dummiesFacade;
}
void TimelineFramesModel::setNodeManipulationInterface(NodeManipulationInterface *iface)
{
m_d->nodeInterface.reset(iface);
}
KisNodeSP TimelineFramesModel::nodeAt(QModelIndex index) const
{
/**
* The dummy might not exist because the user could (quickly) change
* active layer and the list of the nodes in m_d->converter will change.
*/
KisNodeDummy *dummy = m_d->converter->dummyFromRow(index.row());
return dummy ? dummy->node() : 0;
}
QMap<QString, KisKeyframeChannel*> TimelineFramesModel::channelsAt(QModelIndex index) const
{
KisNodeDummy *srcDummy = m_d->converter->dummyFromRow(index.row());
return srcDummy->node()->keyframeChannels();
}
void TimelineFramesModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade,
KisImageSP image,
KisNodeDisplayModeAdapter *displayModeAdapter)
{
KisDummiesFacadeBase *oldDummiesFacade = m_d->dummiesFacade;
if (m_d->dummiesFacade && m_d->image) {
m_d->image->animationInterface()->disconnect(this);
m_d->image->disconnect(this);
m_d->dummiesFacade->disconnect(this);
}
m_d->image = image;
KisTimeBasedItemModel::setImage(image);
m_d->dummiesFacade = dummiesFacade;
m_d->converter.reset();
if (m_d->dummiesFacade) {
m_d->converter.reset(new TimelineNodeListKeeper(this, m_d->dummiesFacade, displayModeAdapter));
connect(m_d->dummiesFacade, SIGNAL(sigDummyChanged(KisNodeDummy*)),
SLOT(slotDummyChanged(KisNodeDummy*)));
connect(m_d->image->animationInterface(),
SIGNAL(sigFullClipRangeChanged()), SIGNAL(sigInfiniteTimelineUpdateNeeded()));
connect(m_d->image->animationInterface(),
SIGNAL(sigAudioChannelChanged()), SIGNAL(sigAudioChannelChanged()));
connect(m_d->image->animationInterface(),
SIGNAL(sigAudioVolumeChanged()), SIGNAL(sigAudioChannelChanged()));
connect(m_d->image, SIGNAL(sigImageModified()), SLOT(slotImageContentChanged()));
}
if (m_d->dummiesFacade != oldDummiesFacade) {
beginResetModel();
endResetModel();
}
if (m_d->dummiesFacade) {
emit sigInfiniteTimelineUpdateNeeded();
emit sigAudioChannelChanged();
}
}
void TimelineFramesModel::slotDummyChanged(KisNodeDummy *dummy)
{
if (!m_d->updateQueue.contains(dummy)) {
m_d->updateQueue.append(dummy);
}
m_d->updateTimer.start();
}
void TimelineFramesModel::slotImageContentChanged()
{
if (m_d->activeLayerIndex < 0) return;
KisNodeDummy *dummy = m_d->converter->dummyFromRow(m_d->activeLayerIndex);
if (!dummy) return;
slotDummyChanged(dummy);
}
void TimelineFramesModel::processUpdateQueue()
{
if (!m_d->converter) return;
Q_FOREACH (KisNodeDummy *dummy, m_d->updateQueue) {
int row = m_d->converter->rowForDummy(dummy);
if (row >= 0) {
emit headerDataChanged (Qt::Vertical, row, row);
emit dataChanged(this->index(row, 0), this->index(row, columnCount() - 1));
}
}
m_d->updateQueue.clear();
}
void TimelineFramesModel::slotCurrentNodeChanged(KisNodeSP node)
{
if (!node) {
m_d->activeLayerIndex = -1;
return;
}
KisNodeDummy *dummy = m_d->dummiesFacade->dummyForNode(node);
if (!dummy) {
// It's perfectly normal that dummyForNode returns 0; that happens
// when views get activated while Krita is closing down.
return;
}
m_d->converter->updateActiveDummy(dummy);
const int row = m_d->converter->rowForDummy(dummy);
if (row < 0) {
qWarning() << "WARNING: TimelineFramesModel::slotCurrentNodeChanged: node not found!";
}
if (row >= 0 && m_d->activeLayerIndex != row) {
setData(index(row, 0), true, ActiveLayerRole);
}
}
int TimelineFramesModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
if(!m_d->dummiesFacade) return 0;
return m_d->converter->rowCount();
}
QVariant TimelineFramesModel::data(const QModelIndex &index, int role) const
{
if(!m_d->dummiesFacade) return QVariant();
switch (role) {
case ActiveLayerRole: {
return index.row() == m_d->activeLayerIndex;
}
case FrameEditableRole: {
return m_d->layerEditable(index.row());
}
case FrameHasContent: {
return m_d->frameHasContent(index.row(), index.column());
}
case FrameExistsRole: {
return m_d->frameExists(index.row(), index.column());
}
case SpecialKeyframeExists: {
return m_d->specialKeyframeExists(index.row(), index.column());
}
case FrameColorLabelIndexRole: {
int label = m_d->frameColorLabel(index.row(), index.column());
return label > 0 ? label : QVariant();
}
case Qt::DisplayRole: {
return m_d->layerName(index.row());
}
case Qt::TextAlignmentRole: {
return QVariant(Qt::AlignHCenter | Qt::AlignVCenter);
}
- case KoResourceModel::LargeThumbnailRole: {
+ case Qt::UserRole + KisResourceModel::LargeThumbnail: {
KisNodeDummy *dummy = m_d->converter->dummyFromRow(index.row());
if (!dummy) {
return QVariant();
}
const int maxSize = 200;
QSize size = dummy->node()->extent().size();
size.scale(maxSize, maxSize, Qt::KeepAspectRatio);
if (size.width() == 0 || size.height() == 0) {
// No thumbnail can be shown if there isn't width or height...
return QVariant();
}
QImage image(dummy->node()->createThumbnailForFrame(size.width(), size.height(), index.column()));
return image;
}
}
return ModelWithExternalNotifications::data(index, role);
}
bool TimelineFramesModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || !m_d->dummiesFacade) return false;
switch (role) {
case ActiveLayerRole: {
if (value.toBool() &&
index.row() != m_d->activeLayerIndex) {
int prevLayer = m_d->activeLayerIndex;
m_d->activeLayerIndex = index.row();
emit dataChanged(this->index(prevLayer, 0), this->index(prevLayer, columnCount() - 1));
emit dataChanged(this->index(m_d->activeLayerIndex, 0), this->index(m_d->activeLayerIndex, columnCount() - 1));
emit headerDataChanged(Qt::Vertical, prevLayer, prevLayer);
emit headerDataChanged(Qt::Vertical, m_d->activeLayerIndex, m_d->activeLayerIndex);
KisNodeDummy *dummy = m_d->converter->dummyFromRow(m_d->activeLayerIndex);
KIS_ASSERT_RECOVER(dummy) { return true; }
emit requestCurrentNodeChanged(dummy->node());
emit sigEnsureRowVisible(m_d->activeLayerIndex);
}
break;
}
case FrameColorLabelIndexRole: {
m_d->setFrameColorLabel(index.row(), index.column(), value.toInt());
}
break;
}
return ModelWithExternalNotifications::setData(index, value, role);
}
QVariant TimelineFramesModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(!m_d->dummiesFacade) return QVariant();
if (orientation == Qt::Vertical) {
switch (role) {
case ActiveLayerRole:
return section == m_d->activeLayerIndex;
case Qt::DisplayRole: {
QVariant value = headerData(section, orientation, Qt::ToolTipRole);
if (!value.isValid()) return value;
QString name = value.toString();
const int maxNameSize = 13;
if (name.size() > maxNameSize) {
name = QString("%1...").arg(name.left(maxNameSize));
}
return name;
}
case Qt::TextColorRole: {
// WARNING: this role doesn't work for header views! Use
// bold font to show isolated mode instead!
return QVariant();
}
case Qt::FontRole: {
KisNodeDummy *dummy = m_d->converter->dummyFromRow(section);
if (!dummy) return QVariant();
KisNodeSP node = dummy->node();
QFont baseFont;
if (node->projectionLeaf()->isDroppedNode()) {
baseFont.setStrikeOut(true);
} else if (m_d->image && m_d->image->isolatedModeRoot() &&
KisNodeModel::belongsToIsolatedGroup(m_d->image, node, m_d->dummiesFacade)) {
baseFont.setBold(true);
}
return baseFont;
}
case Qt::ToolTipRole: {
return m_d->layerName(section);
}
case TimelinePropertiesRole: {
return QVariant::fromValue(m_d->layerProperties(section));
}
case OtherLayersRole: {
TimelineNodeListKeeper::OtherLayersList list =
m_d->converter->otherLayersList();
return QVariant::fromValue(list);
}
case LayerUsedInTimelineRole: {
KisNodeDummy *dummy = m_d->converter->dummyFromRow(section);
if (!dummy) return QVariant();
return dummy->node()->useInTimeline();
}
case Qt::BackgroundRole: {
int label = m_d->layerColorLabel(section);
if (label > 0) {
KisNodeViewColorScheme scm;
QColor color = scm.colorLabel(label);
QPalette pal = qApp->palette();
color = KritaUtils::blendColors(color, pal.color(QPalette::Button), 0.3);
return QBrush(color);
} else {
return QVariant();
}
}
}
}
return ModelWithExternalNotifications::headerData(section, orientation, role);
}
bool TimelineFramesModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
{
if (!m_d->dummiesFacade) return false;
if (orientation == Qt::Vertical) {
switch (role) {
case ActiveLayerRole: {
setData(index(section, 0), value, role);
break;
}
case TimelinePropertiesRole: {
TimelineFramesModel::PropertyList props = value.value<TimelineFramesModel::PropertyList>();
int result = m_d->setLayerProperties(section, props);
emit headerDataChanged (Qt::Vertical, section, section);
return result;
}
case LayerUsedInTimelineRole: {
KisNodeDummy *dummy = m_d->converter->dummyFromRow(section);
if (!dummy) return false;
dummy->node()->setUseInTimeline(value.toBool());
return true;
}
}
}
return ModelWithExternalNotifications::setHeaderData(section, orientation, value, role);
}
Qt::DropActions TimelineFramesModel::supportedDragActions() const
{
return Qt::MoveAction | Qt::CopyAction;
}
Qt::DropActions TimelineFramesModel::supportedDropActions() const
{
return Qt::MoveAction | Qt::CopyAction;
}
QStringList TimelineFramesModel::mimeTypes() const
{
QStringList types;
types << QLatin1String("application/x-krita-frame");
return types;
}
void TimelineFramesModel::setLastClickedIndex(const QModelIndex &index)
{
m_d->lastClickedIndex = index;
}
QMimeData* TimelineFramesModel::mimeData(const QModelIndexList &indexes) const
{
return mimeDataExtended(indexes, m_d->lastClickedIndex, UndefinedPolicy);
}
QMimeData *TimelineFramesModel::mimeDataExtended(const QModelIndexList &indexes,
const QModelIndex &baseIndex,
TimelineFramesModel::MimeCopyPolicy copyPolicy) const
{
QMimeData *data = new QMimeData();
QByteArray encoded;
QDataStream stream(&encoded, QIODevice::WriteOnly);
const int baseRow = baseIndex.row();
const int baseColumn = baseIndex.column();
const QByteArray uuidDataRoot = m_d->image->root()->uuid().toRfc4122();
stream << int(uuidDataRoot.size());
stream.writeRawData(uuidDataRoot.data(), uuidDataRoot.size());
stream << indexes.size();
stream << baseRow << baseColumn;
Q_FOREACH (const QModelIndex &index, indexes) {
KisNodeSP node = nodeAt(index);
KIS_SAFE_ASSERT_RECOVER(node) { continue; }
stream << index.row() - baseRow << index.column() - baseColumn;
const QByteArray uuidData = node->uuid().toRfc4122();
stream << int(uuidData.size());
stream.writeRawData(uuidData.data(), uuidData.size());
}
stream << int(copyPolicy);
data->setData("application/x-krita-frame", encoded);
return data;
}
inline void decodeBaseIndex(QByteArray *encoded, int *row, int *col)
{
int size_UNUSED = 0;
QDataStream stream(encoded, QIODevice::ReadOnly);
stream >> size_UNUSED >> *row >> *col;
}
bool TimelineFramesModel::canDropFrameData(const QMimeData */*data*/, const QModelIndex &index)
{
if (!index.isValid()) return false;
/**
* Now we support D&D around any layer, so just return 'true' all
* the time.
*/
return true;
}
bool TimelineFramesModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
Q_UNUSED(row);
Q_UNUSED(column);
return dropMimeDataExtended(data, action, parent);
}
bool TimelineFramesModel::dropMimeDataExtended(const QMimeData *data, Qt::DropAction action, const QModelIndex &parent, bool *dataMoved)
{
bool result = false;
if ((action != Qt::MoveAction && action != Qt::CopyAction) ||
!parent.isValid()) return result;
QByteArray encoded = data->data("application/x-krita-frame");
QDataStream stream(&encoded, QIODevice::ReadOnly);
int uuidLenRoot = 0;
stream >> uuidLenRoot;
QByteArray uuidDataRoot(uuidLenRoot, '\0');
stream.readRawData(uuidDataRoot.data(), uuidLenRoot);
QUuid nodeUuidRoot = QUuid::fromRfc4122(uuidDataRoot);
KisPart *partInstance = KisPart::instance();
QList<QPointer<KisDocument>> documents = partInstance->documents();
KisImageSP srcImage = 0;
Q_FOREACH(KisDocument *doc, documents) {
KisImageSP tmpSrcImage = doc->image();
if (tmpSrcImage->root()->uuid() == nodeUuidRoot) {
srcImage = tmpSrcImage;
break;
}
}
if (!srcImage) {
KisPart *kisPartInstance = KisPart::instance();
kisPartInstance->currentMainwindow()->viewManager()->showFloatingMessage(
i18n("Dropped frames are not available in this Krita instance")
, QIcon());
return false;
}
int size, baseRow, baseColumn;
stream >> size >> baseRow >> baseColumn;
const QPoint offset(parent.column() - baseColumn, parent.row() - baseRow);
KisAnimationUtils::FrameMovePairList frameMoves;
for (int i = 0; i < size; i++) {
int relRow, relColumn;
stream >> relRow >> relColumn;
const int srcRow = baseRow + relRow;
const int srcColumn = baseColumn + relColumn;
int uuidLen = 0;
stream >> uuidLen;
QByteArray uuidData(uuidLen, '\0');
stream.readRawData(uuidData.data(), uuidLen);
QUuid nodeUuid = QUuid::fromRfc4122(uuidData);
KisNodeSP srcNode;
if (!nodeUuid.isNull()) {
KisNodeUuidInfo nodeInfo(nodeUuid);
srcNode = nodeInfo.findNode(srcImage->root());
} else {
QModelIndex index = this->index(srcRow, srcColumn);
srcNode = nodeAt(index);
}
KIS_SAFE_ASSERT_RECOVER(srcNode) { continue; }
const QModelIndex dstRowIndex = this->index(srcRow + offset.y(), 0);
if (!dstRowIndex.isValid()) continue;
KisNodeSP dstNode = nodeAt(dstRowIndex);
KIS_SAFE_ASSERT_RECOVER(dstNode) { continue; }
Q_FOREACH (KisKeyframeChannel *channel, srcNode->keyframeChannels().values()) {
KisAnimationUtils::FrameItem srcItem(srcNode, channel->id(), srcColumn);
KisAnimationUtils::FrameItem dstItem(dstNode, channel->id(), srcColumn + offset.x());
frameMoves << std::make_pair(srcItem, dstItem);
}
}
MimeCopyPolicy copyPolicy = UndefinedPolicy;
if (!stream.atEnd()) {
int value = 0;
stream >> value;
copyPolicy = MimeCopyPolicy(value);
}
const bool copyFrames =
copyPolicy == UndefinedPolicy ?
action == Qt::CopyAction :
copyPolicy == CopyFramesPolicy;
if (dataMoved) {
*dataMoved = !copyFrames;
}
KUndo2Command *cmd = 0;
if (!frameMoves.isEmpty()) {
KisImageBarrierLockerWithFeedback locker(m_d->image);
cmd = KisAnimationUtils::createMoveKeyframesCommand(frameMoves, copyFrames, false, 0);
}
if (cmd) {
KisProcessingApplicator::runSingleCommandStroke(m_d->image, cmd,
KisStrokeJobData::BARRIER,
KisStrokeJobData::EXCLUSIVE);
}
return cmd;
}
Qt::ItemFlags TimelineFramesModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = ModelWithExternalNotifications::flags(index);
if (!index.isValid()) return flags;
if (m_d->frameExists(index.row(), index.column()) || m_d->specialKeyframeExists(index.row(), index.column())) {
if (data(index, FrameEditableRole).toBool()) {
flags |= Qt::ItemIsDragEnabled;
}
}
/**
* Basically we should forbid overrides only if we D&D a single frame
* and allow it when we D&D multiple frames. But we cannot distinguish
* it here... So allow all the time.
*/
flags |= Qt::ItemIsDropEnabled;
return flags;
}
bool TimelineFramesModel::insertRows(int row, int count, const QModelIndex &parent)
{
Q_UNUSED(parent);
KIS_ASSERT_RECOVER(count == 1) { return false; }
if (row < 0 || row > rowCount()) return false;
bool result = m_d->addNewLayer(row);
return result;
}
bool TimelineFramesModel::removeRows(int row, int count, const QModelIndex &parent)
{
Q_UNUSED(parent);
KIS_ASSERT_RECOVER(count == 1) { return false; }
if (row < 0 || row >= rowCount()) return false;
bool result = m_d->removeLayer(row);
return result;
}
bool TimelineFramesModel::insertOtherLayer(int index, int dstRow)
{
Q_UNUSED(dstRow);
TimelineNodeListKeeper::OtherLayersList list =
m_d->converter->otherLayersList();
if (index < 0 || index >= list.size()) return false;
list[index].dummy->node()->setUseInTimeline(true);
dstRow = m_d->converter->rowForDummy(list[index].dummy);
setData(this->index(dstRow, 0), true, ActiveLayerRole);
return true;
}
int TimelineFramesModel::activeLayerRow() const
{
return m_d->activeLayerIndex;
}
bool TimelineFramesModel::createFrame(const QModelIndex &dstIndex)
{
if (!dstIndex.isValid()) return false;
return m_d->addKeyframe(dstIndex.row(), dstIndex.column(), false);
}
bool TimelineFramesModel::copyFrame(const QModelIndex &dstIndex)
{
if (!dstIndex.isValid()) return false;
return m_d->addKeyframe(dstIndex.row(), dstIndex.column(), true);
}
bool TimelineFramesModel::insertFrames(int dstColumn, const QList<int> &dstRows, int count, int timing)
{
if (dstRows.isEmpty() || count <= 0) return true;
timing = qMax(timing, 1);
KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18np("Insert frame", "Insert %1 frames", count));
{
KisImageBarrierLockerWithFeedback locker(m_d->image);
QModelIndexList indexes;
Q_FOREACH (int row, dstRows) {
for (int column = dstColumn; column < columnCount(); column++) {
indexes << index(row, column);
}
}
setLastVisibleFrame(columnCount() + (count * timing) - 1);
createOffsetFramesCommand(indexes, QPoint((count * timing), 0), false, false, parentCommand);
Q_FOREACH (int row, dstRows) {
KisNodeDummy *dummy = m_d->converter->dummyFromRow(row);
if (!dummy) continue;
KisNodeSP node = dummy->node();
if (!KisAnimationUtils::supportsContentFrames(node)) continue;
for (int column = dstColumn; column < dstColumn + (count * timing); column += timing) {
KisAnimationUtils::createKeyframeCommand(m_d->image, node, KisKeyframeChannel::Content.id(), column, false, parentCommand);
}
}
const int oldTime = m_d->image->animationInterface()->currentUITime();
const int newTime = dstColumn > oldTime ? dstColumn : dstColumn + (count * timing) - 1;
new KisSwitchCurrentTimeCommand(m_d->image->animationInterface(),
oldTime,
newTime, parentCommand);
}
KisProcessingApplicator::runSingleCommandStroke(m_d->image, parentCommand,
KisStrokeJobData::BARRIER,
KisStrokeJobData::EXCLUSIVE);
return true;
}
bool TimelineFramesModel::insertHoldFrames(QModelIndexList selectedIndexes, int count)
{
if (selectedIndexes.isEmpty() || count == 0) return true;
QScopedPointer<KUndo2Command> parentCommand(new KUndo2Command(kundo2_i18np("Insert frame", "Insert %1 frames", count)));
{
KisImageBarrierLockerWithFeedback locker(m_d->image);
QSet<KisKeyframeSP> uniqueKeyframesInSelection;
int minSelectedTime = std::numeric_limits<int>::max();
Q_FOREACH (const QModelIndex &index, selectedIndexes) {
KisNodeSP node = nodeAt(index);
KIS_SAFE_ASSERT_RECOVER(node) { continue; }
KisKeyframeChannel *channel = node->getKeyframeChannel(KisKeyframeChannel::Content.id());
if (!channel) continue;
minSelectedTime = qMin(minSelectedTime, index.column());
KisKeyframeSP keyFrame = channel->activeKeyframeAt(index.column());
if (keyFrame) {
uniqueKeyframesInSelection.insert(keyFrame);
}
}
QList<KisKeyframeSP> keyframesToMove;
for (auto it = uniqueKeyframesInSelection.begin(); it != uniqueKeyframesInSelection.end(); ++it) {
KisKeyframeSP keyframe = *it;
KisKeyframeChannel *channel = keyframe->channel();
KisKeyframeSP nextKeyframe = channel->nextKeyframe(keyframe);
if (nextKeyframe) {
keyframesToMove << nextKeyframe;
}
}
std::sort(keyframesToMove.begin(), keyframesToMove.end(),
[] (KisKeyframeSP lhs, KisKeyframeSP rhs) {
return lhs->time() > rhs->time();
});
if (keyframesToMove.isEmpty()) return true;
const int maxColumn = columnCount();
if (count > 0) {
setLastVisibleFrame(columnCount() + count);
}
Q_FOREACH (KisKeyframeSP keyframe, keyframesToMove) {
int plannedFrameMove = count;
if (count < 0) {
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(keyframe->time() > 0, false);
KisKeyframeSP prevFrame = keyframe->channel()->previousKeyframe(keyframe);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(prevFrame, false);
plannedFrameMove = qMax(count, prevFrame->time() - keyframe->time() + 1);
minSelectedTime = qMin(minSelectedTime, prevFrame->time());
}
KisNodeDummy *dummy = m_d->dummiesFacade->dummyForNode(keyframe->channel()->node());
KIS_SAFE_ASSERT_RECOVER(dummy) { continue; }
const int row = m_d->converter->rowForDummy(dummy);
KIS_SAFE_ASSERT_RECOVER(row >= 0) { continue; }
QModelIndexList indexes;
for (int column = keyframe->time(); column < maxColumn; column++) {
indexes << index(row, column);
}
createOffsetFramesCommand(indexes,
QPoint(plannedFrameMove, 0),
false, true, parentCommand.data());
}
const int oldTime = m_d->image->animationInterface()->currentUITime();
const int newTime = minSelectedTime;
new KisSwitchCurrentTimeCommand(m_d->image->animationInterface(),
oldTime,
newTime, parentCommand.data());
}
KisProcessingApplicator::runSingleCommandStroke(m_d->image, parentCommand.take(),
KisStrokeJobData::BARRIER,
KisStrokeJobData::EXCLUSIVE);
return true;
}
QString TimelineFramesModel::audioChannelFileName() const
{
return m_d->image ? m_d->image->animationInterface()->audioChannelFileName() : QString();
}
void TimelineFramesModel::setAudioChannelFileName(const QString &fileName)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->image);
m_d->image->animationInterface()->setAudioChannelFileName(fileName);
}
bool TimelineFramesModel::isAudioMuted() const
{
return m_d->image ? m_d->image->animationInterface()->isAudioMuted() : false;
}
void TimelineFramesModel::setAudioMuted(bool value)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->image);
m_d->image->animationInterface()->setAudioMuted(value);
}
qreal TimelineFramesModel::audioVolume() const
{
return m_d->image ? m_d->image->animationInterface()->audioVolume() : 0.5;
}
void TimelineFramesModel::setAudioVolume(qreal value)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->image);
m_d->image->animationInterface()->setAudioVolume(value);
}
void TimelineFramesModel::setFullClipRangeStart(int column)
{
m_d->image->animationInterface()->setFullClipRangeStartTime(column);
}
void TimelineFramesModel::setFullClipRangeEnd(int column)
{
m_d->image->animationInterface()->setFullClipRangeEndTime(column);
}
diff --git a/plugins/dockers/animation/timeline_frames_view.cpp b/plugins/dockers/animation/timeline_frames_view.cpp
index 0d4f895aca..1a8c76addb 100644
--- a/plugins/dockers/animation/timeline_frames_view.cpp
+++ b/plugins/dockers/animation/timeline_frames_view.cpp
@@ -1,1564 +1,1564 @@
/*
* 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_keyframe_channel.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>
+#include <KisIconToolTip.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;
+ KisIconToolTip 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(i18n("%"));
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();
{ // Tween submenu.
QMenu *frames = menu->addMenu(i18nc("@item:inmenu", "Tweening"));
KisActionManager::safePopulateMenu(frames, "insert_opacity_keyframe", m_d->actionMan);
KisActionManager::safePopulateMenu(frames, "remove_opacity_keyframe", m_d->actionMan);
// only allow to add an opacity keyframe if one doesn't exist
bool opacityKeyframeExists = model()->data(currentIndex(), TimelineFramesModel::SpecialKeyframeExists).toBool();
m_d->actionMan->actionByName("insert_opacity_keyframe")->setEnabled(!opacityKeyframeExists);
m_d->actionMan->actionByName("remove_opacity_keyframe")->setEnabled(opacityKeyframeExists);
}
{ //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);
enableAction("insert_opacity_keyframe", hasEditableFrames);
enableAction("remove_opacity_keyframe", hasEditableFrames);
- QClipboard *cp = QApplication::clipboard();
- const QMimeData *data = cp->mimeData();
+ //QClipboard *cp = QApplication::clipboard();
+ //const QMimeData *data = cp->mimeData();
//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);
+ m_d->model->insertFrames(insertionColumn, QList<int>(rows.begin(), rows.end()), 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()) {
// add extra columns to the end of the timeline if we are adding hold frames
// they will be truncated if we don't do this
if (count > 0) {
// Scan all the layers and find out what layer has the most keyframes
// only keep a reference of layer that has the most keyframes
int keyframesInLayerNode = 0;
Q_FOREACH (const QModelIndex &index, indexes) {
KisNodeSP layerNode = m_d->model->nodeAt(index);
KisKeyframeChannel *channel = layerNode->getKeyframeChannel(KisKeyframeChannel::Content.id());
if (!channel) continue;
if (keyframesInLayerNode < channel->allKeyframeIds().count()) {
keyframesInLayerNode = channel->allKeyframeIds().count();
}
}
m_d->model->setLastVisibleFrame(m_d->model->columnCount() + count*keyframesInLayerNode);
}
m_d->model->insertHoldFrames(indexes, count);
// bulk adding frames can add too many
// trim timeline to clean up extra frames that might have been added
slotUpdateInfiniteFramesCount();
}
}
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/artisticcolorselector/artisticcolorselector_dock.cpp b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp
index ab2b45c0fe..b5ed73f664 100644
--- a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp
+++ b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp
@@ -1,465 +1,464 @@
/*
* 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 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_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 <KisResourceItemChooser.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)));
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->canvasResourceProvider();
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*)), Qt::UniqueConnection);
+ connect(m_resourceProvider, SIGNAL(sigGamutMaskChanged(KoGamutMaskSP)),
+ this, SLOT(slotGamutMaskSet(KoGamutMaskSP)), Qt::UniqueConnection);
connect(m_resourceProvider, SIGNAL(sigGamutMaskUnset()),
this, SLOT(slotGamutMaskUnset()), Qt::UniqueConnection);
connect(m_resourceProvider, SIGNAL(sigGamutMaskPreviewUpdate()),
this, SLOT(slotGamutMaskPreviewUpdate()), Qt::UniqueConnection);
connect(m_resourceProvider, SIGNAL(sigGamutMaskDeactivated()),
this, SLOT(slotGamutMaskDeactivate()), 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)), Qt::UniqueConnection);
connect(m_canvas->displayColorConverter(), SIGNAL(displayConfigurationChanged()),
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)
+void ArtisticColorSelectorDock::slotGamutMaskSet(KoGamutMaskSP 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::slotGamutMaskDeactivate()
{
slotGamutMaskToggle(false);
}
void ArtisticColorSelectorDock::slotSelectorSettingsChanged()
{
m_selectorUI->colorSelector->setDirty();
}
diff --git a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.h b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.h
index edafebacc7..cd5635f31c 100644
--- a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.h
+++ b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.h
@@ -1,88 +1,87 @@
/*
* 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 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 H_ARTISTIC_COLOR_SELECTOR_DOCK_H
#define H_ARTISTIC_COLOR_SELECTOR_DOCK_H
#include <QDockWidget>
#include <QPointer>
#include <QRegExpValidator>
#include <KoCanvasObserverBase.h>
#include <KoResourceServerProvider.h>
-#include <KoResourceServerAdapter.h>
#include <KoResourceServerObserver.h>
#include <resources/KoGamutMask.h>
#include <KisDocument.h>
#include <kis_types.h>
-#include <KoResourceItemChooser.h>
+#include <KisResourceItemChooser.h>
#include <kis_mainwindow_observer.h>
class KisCanvasResourceProvider;
class KisColor;
class QButtonGroup;
class QMenu;
struct ArtisticColorSelectorUI;
struct ARCSSettingsUI;
struct WheelPreferencesPopupUI;
class ArtisticColorSelectorDock: public QDockWidget, public KisMainwindowObserver
{
Q_OBJECT
public:
ArtisticColorSelectorDock();
~ArtisticColorSelectorDock() override;
QString observerName() override { return "ArtisticColorSelectorDock"; }
void setViewManager(KisViewManager* kisview) override;
void setCanvas(KoCanvasBase* canvas) override;
void unsetCanvas() override;
private Q_SLOTS:
void slotCanvasResourceChanged(int key, const QVariant& value);
void slotFgColorChanged(const KisColor& color);
void slotBgColorChanged(const KisColor& color);
void slotColorSpaceSelected();
void slotPreferenceChanged();
void slotResetDefaultSettings();
void slotGamutMaskToggle(bool value);
- void slotGamutMaskSet(KoGamutMask* mask);
+ void slotGamutMaskSet(KoGamutMaskSP mask);
void slotGamutMaskUnset();
void slotGamutMaskPreviewUpdate();
void slotGamutMaskDeactivate();
void slotSelectorSettingsChanged();
private:
KisCanvas2* m_canvas;
KisCanvasResourceProvider* m_resourceProvider;
QButtonGroup* m_hsxButtons;
ArtisticColorSelectorUI* m_selectorUI;
ARCSSettingsUI* m_preferencesUI;
WheelPreferencesPopupUI* m_wheelPrefsUI;
- KoGamutMask* m_selectedMask;
+ KoGamutMaskSP m_selectedMask;
QIcon m_iconMaskOff;
QIcon m_iconMaskOn;
QPixmap m_infinityPixmap;
};
#endif // H_ARTISTIC_COLOR_SELECTOR_DOCK_H
diff --git a/plugins/dockers/artisticcolorselector/forms/wdgArtisticColorSelector.ui b/plugins/dockers/artisticcolorselector/forms/wdgArtisticColorSelector.ui
index 280c7ad346..34946d737c 100644
--- a/plugins/dockers/artisticcolorselector/forms/wdgArtisticColorSelector.ui
+++ b/plugins/dockers/artisticcolorselector/forms/wdgArtisticColorSelector.ui
@@ -1,136 +1,136 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>wdgArtisticColorSelector</class>
<widget class="QWidget" name="wdgArtisticColorSelector">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>334</width>
<height>284</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="KisGamutMaskToolbar" name="gamutMaskToolbar" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="KisPopupButton" name="bnWheelPrefs">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<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="toolTip">
<string>Color wheel preferences</string>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string/>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="KisPopupButton" name="bnDockerPrefs">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Docker settings</string>
</property>
<property name="text">
<string/>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="KisColorSelector" name="colorSelector" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisPopupButton</class>
<extends>QPushButton</extends>
- <header>kis_popup_button.h</header>
+ <header>KisPopupButton.h</header>
</customwidget>
<customwidget>
<class>KisColorSelector</class>
<extends>QWidget</extends>
<header>kis_color_selector.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisGamutMaskToolbar</class>
<extends>QWidget</extends>
<header>KisGamutMaskToolbar.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/plugins/dockers/artisticcolorselector/kis_color_selector.cpp b/plugins/dockers/artisticcolorselector/kis_color_selector.cpp
index e3290fe6a4..e2ddf379e8 100644
--- a/plugins/dockers/artisticcolorselector/kis_color_selector.cpp
+++ b/plugins/dockers/artisticcolorselector/kis_color_selector.cpp
@@ -1,1185 +1,1185 @@
/*
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 <QPaintDevice>
#include <QPainter>
#include <QColor>
#include <QBrush>
#include <QPen>
#include <QRadialGradient>
#include <QConicalGradient>
#include <QMouseEvent>
#include <QResizeEvent>
#include <QTransform>
#include <QList>
#include <cmath>
#include <kis_config.h>
#include <kis_arcs_constants.h>
#include <resources/KoGamutMask.h>
//#include <KisGamutMaskViewConverter.h>
#include "kis_color_selector.h"
//#define DEBUG_ARC_SELECTOR
KisColorSelector::KisColorSelector(QWidget* parent, KisColor::Type type)
: QWidget(parent)
, m_colorConverter(KisDisplayColorConverter::dumbConverterInstance())
, m_colorSpace(type)
, m_inverseSaturation(false)
, m_selectedColor(m_colorConverter)
, m_fgColor(m_colorConverter)
, m_bgColor(m_colorConverter)
, m_clickedRing(-1)
, m_gamutMaskOn(false)
, m_currentGamutMask(nullptr)
, m_maskPreviewActive(true)
, m_gamutMaskViewTransform(QTransform())
, m_widgetUpdatesSelf(false)
, m_isDirtyWheel(false)
, m_isDirtyLightStrip(false)
, m_isDirtyGamutMask(false)
, m_isDirtyColorPreview(false)
{
// m_viewConverter = new KisGamutMaskViewConverter();
setLumaCoefficients(DEFAULT_LUMA_R, DEFAULT_LUMA_G, DEFAULT_LUMA_B,DEFAULT_LUMA_GAMMA);
recalculateRings(DEFAULT_SATURATION_STEPS, DEFAULT_HUE_STEPS);
recalculateAreas(DEFAULT_VALUE_SCALE_STEPS);
selectColor(KisColor(Qt::red, m_colorConverter, KisColor::HSY, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma));
using namespace std::placeholders; // For _1 placeholder
auto function = std::bind(&KisColorSelector::slotUpdateColorAndPreview, this, _1);
m_updateColorCompressor.reset(new ColorCompressorType(20 /* ms */, function));
}
void KisColorSelector::setColorSpace(KisColor::Type type)
{
m_colorSpace = type;
m_selectedColor = KisColor(m_selectedColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
m_isDirtyLightStrip = true;
m_isDirtyWheel = true;
#ifdef DEBUG_ARC_SELECTOR
dbgPlugins << "KisColorSelector::setColorSpace: set to:" << m_colorSpace;
#endif
update();
}
void KisColorSelector::setColorConverter(KisDisplayColorConverter *colorConverter)
{
m_colorConverter = colorConverter;
m_selectedColor = KisColor(m_selectedColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
m_fgColor = KisColor(m_fgColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
m_bgColor = KisColor(m_bgColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
update();
}
void KisColorSelector::setNumLightPieces(int num)
{
num = qBound(MIN_NUM_LIGHT_PIECES, num, MAX_NUM_LIGHT_PIECES);
recalculateAreas(quint8(num));
if (m_selectedLightPiece >= 0)
m_selectedLightPiece = getLightIndex(m_selectedColor.getX());
update();
}
void KisColorSelector::setNumPieces(int num)
{
num = qBound(MIN_NUM_HUE_PIECES, num, MAX_NUM_HUE_PIECES);
recalculateRings(quint8(getNumRings()), quint8(num));
if (m_selectedPiece >= 0)
m_selectedPiece = getHueIndex(m_selectedColor.getH() * PI2);
update();
}
void KisColorSelector::setNumRings(int num)
{
num = qBound(MIN_NUM_SATURATION_RINGS, num, MAX_NUM_SATURATION_RINGS);
recalculateRings(quint8(num), quint8(getNumPieces()));
if (m_selectedRing >= 0)
m_selectedRing = getSaturationIndex(m_selectedColor.getS());
update();
}
void KisColorSelector::selectColor(const KisColor& color)
{
m_selectedColor = KisColor(color, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
m_selectedPiece = getHueIndex(m_selectedColor.getH() * PI2);
m_selectedRing = getSaturationIndex(m_selectedColor.getS());
m_selectedLightPiece = getLightIndex(m_selectedColor.getX());
update();
}
void KisColorSelector::setFgColor(const KoColor& fgColor)
{
if (!m_widgetUpdatesSelf) {
m_fgColor = KisColor(fgColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
m_selectedColor = KisColor(fgColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
m_isDirtyWheel = true;
m_isDirtyLightStrip = true;
m_isDirtyColorPreview = true;
#ifdef DEBUG_ARC_SELECTOR
dbgPlugins << "KisColorSelector::setFgColor: m_fgColor set to:"
<< "H:" << m_fgColor.getH()
<< "S:" << m_fgColor.getS()
<< "X:" << m_fgColor.getX();
dbgPlugins << "KisColorSelector::setFgColor: m_selectedColor set to:"
<< "H:" << m_selectedColor.getH()
<< "S:" << m_selectedColor.getS()
<< "X:" << m_selectedColor.getX();
#endif
update();
}
}
void KisColorSelector::setBgColor(const KoColor& bgColor)
{
if (!m_widgetUpdatesSelf) {
m_bgColor = KisColor(bgColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
m_isDirtyColorPreview = true;
#ifdef DEBUG_ARC_SELECTOR
dbgPlugins << "KisColorSelector::setBgColor: m_bgColor set to:"
<< "H:" << m_bgColor.getH()
<< "S:" << m_bgColor.getS()
<< "X:" << m_bgColor.getX();
#endif
update();
}
}
void KisColorSelector::setLight(qreal light)
{
m_selectedColor.setX(qBound(0.0, light, 1.0));
m_selectedLightPiece = getLightIndex(m_selectedColor.getX());
m_isDirtyLightStrip = true;
update();
}
void KisColorSelector::setLumaCoefficients(qreal lR, qreal lG, qreal lB, qreal lGamma)
{
m_lumaR = lR;
m_lumaG = lG;
m_lumaB = lB;
m_lumaGamma = lGamma;
m_selectedColor = KisColor(m_selectedColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
m_isDirtyLightStrip = true;
m_isDirtyWheel = true;
#ifdef DEBUG_ARC_SELECTOR
dbgPlugins << "KisColorSelector::setLumaCoefficients: " << m_lumaR
<< " " << m_lumaG << " " << m_lumaB << " " << m_lumaGamma;
#endif
update();
}
void KisColorSelector::setInverseSaturation(bool inverse)
{
if (m_inverseSaturation != inverse) {
m_selectedRing = (getNumRings()-1) - m_selectedRing;
m_inverseSaturation = inverse;
recalculateRings(quint8(getNumRings()), quint8(getNumPieces()));
update();
}
}
-void KisColorSelector::setGamutMask(KoGamutMask* gamutMask)
+void KisColorSelector::setGamutMask(KoGamutMaskSP gamutMask)
{
if (!gamutMask) {
return;
}
m_currentGamutMask = gamutMask;
m_gamutMaskViewTransform = m_currentGamutMask->maskToViewTransform(m_renderArea.width());
if (m_enforceGamutMask) {
m_isDirtyWheel = true;
} else {
m_isDirtyGamutMask = true;
}
update();
}
void KisColorSelector::setDirty()
{
m_isDirtyWheel = true;
m_isDirtyLightStrip = true;
m_isDirtyGamutMask = true;
m_isDirtyColorPreview = true;
update();
}
-KoGamutMask* KisColorSelector::gamutMask()
+KoGamutMaskSP KisColorSelector::gamutMask()
{
return m_currentGamutMask;
}
bool KisColorSelector::gamutMaskOn()
{
return m_gamutMaskOn;
}
void KisColorSelector::setGamutMaskOn(bool gamutMaskOn)
{
if (m_currentGamutMask) {
m_gamutMaskOn = gamutMaskOn;
if (m_enforceGamutMask) {
m_isDirtyWheel = true;
} else {
m_isDirtyGamutMask = true;
}
update();
}
}
void KisColorSelector::setEnforceGamutMask(bool enforce)
{
m_enforceGamutMask = enforce;
m_isDirtyGamutMask = true;
m_isDirtyWheel = true;
update();
}
QPointF KisColorSelector::mapCoordToView(const QPointF& pt, const QRectF& viewRect) const
{
qreal w = viewRect.width() / 2.0;
qreal h = viewRect.height() / 2.0;
qreal x = pt.x() + 1.0;
qreal y = (pt.y()) + 1.0;
return QPointF(x*w, y*h);
}
QPointF KisColorSelector::mapCoordToUnit(const QPointF& pt, const QRectF& viewRect) const
{
qreal w = viewRect.width() / 2.0;
qreal h = viewRect.height() / 2.0;
qreal x = pt.x() - (viewRect.x() + w);
qreal y = pt.y() - (viewRect.y() + h);
return QPointF(x/w, y/h);
}
QPointF KisColorSelector::mapColorToUnit(const KisColor& color, bool invertSaturation) const
{
qreal radius;
if (invertSaturation && m_inverseSaturation) {
radius = 1.0 - color.getS();
} else {
radius = color.getS();
}
QPointF hueCoord = mapHueToAngle(color.getH());
qreal x = hueCoord.x()*radius;
qreal y = hueCoord.y()*radius;
return QPointF(x,y);
}
KisColorSelector::Radian KisColorSelector::mapCoordToAngle(qreal x, qreal y) const
{
qreal angle = std::atan2(-y, -x);
#ifdef DEBUG_ARC_SELECTOR
dbgPlugins << "KisColorSelector::mapCoordToAngle: "
<< "X:" << x
<< "Y:" << y
<< "angle:" << angle;
#endif
return angle;
}
QPointF KisColorSelector::mapHueToAngle(qreal hue) const
{
qreal angle = hue * 2.0 * M_PI - M_PI;
qreal x = std::cos(angle);
qreal y = std::sin(angle);
return QPointF(x,y);
}
qint8 KisColorSelector::getLightIndex(const QPointF& pt) const
{
if (m_lightStripArea.contains(pt.toPoint(), true)) {
qreal t = (pt.x() - m_lightStripArea.x()) / qreal(m_lightStripArea.width());
t = (pt.y() - m_lightStripArea.y()) / qreal(m_lightStripArea.height());
return qint8(t * getNumLightPieces());
}
return -1;
}
qint8 KisColorSelector::getLightIndex(qreal light) const
{
light = qreal(1) - qBound(qreal(0), light, qreal(1));
return qint8(qRound(light * (getNumLightPieces()-1)));
}
qreal KisColorSelector::getLight(const QPointF& pt) const
{
qint8 clickedLightPiece = getLightIndex(pt);
if (clickedLightPiece >= 0) {
if (getNumLightPieces() > 1) {
return 1.0 - (qreal(clickedLightPiece) / qreal(getNumLightPieces()-1));
}
return 1.0 - (qreal(pt.y()) / qreal(m_lightStripArea.height()));
}
return qreal(0);
}
qint8 KisColorSelector::getHueIndex(Radian hue) const
{
qreal partSize = 1.0 / qreal(getNumPieces());
return qint8(qRound(hue.scaled(0.0, 1.0) / partSize) % getNumPieces());
}
qreal KisColorSelector::getHue(int hueIdx, Radian shift) const
{
Radian hue = (qreal(hueIdx) / qreal(getNumPieces())) * PI2;
hue += shift;
return hue.scaled(0.0, 1.0);
}
qint8 KisColorSelector::getSaturationIndex(qreal saturation) const
{
saturation = qBound(qreal(0), saturation, qreal(1));
saturation = m_inverseSaturation ? (qreal(1) - saturation) : saturation;
return qint8(saturation * qreal(getNumRings() - 1));
}
qint8 KisColorSelector::getSaturationIndex(const QPointF& pt) const
{
qreal length = std::sqrt(pt.x()*pt.x() + pt.y()*pt.y());
for(int i=0; i<m_colorRings.size(); ++i) {
if (length >= m_colorRings[i].innerRadius && length < m_colorRings[i].outerRadius)
return qint8(i);
}
return -1;
}
qreal KisColorSelector::getSaturation(int saturationIdx) const
{
qreal sat = qreal(saturationIdx) / qreal(getNumRings()-1);
return m_inverseSaturation ? (1.0 - sat) : sat;
}
void KisColorSelector::recalculateAreas(quint8 numLightPieces)
{
qreal LIGHT_STRIP_RATIO = 0.075;
if (m_showValueScaleNumbers) {
LIGHT_STRIP_RATIO = 0.25;
}
int width = QWidget::width();
int height = QWidget::height();
int size = qMin(width, height);
int stripThick = int(size * LIGHT_STRIP_RATIO);
width -= stripThick;
size = qMin(width, height);
int x = (width - size) / 2;
int y = (height - size) / 2;
m_widgetArea = QRect(0, 0, QWidget::width(), QWidget::height());
m_renderArea = QRect(x+stripThick, y, size, size);
m_lightStripArea = QRect(0, 0, stripThick, QWidget::height());
m_renderBuffer = QImage(size, size, QImage::Format_ARGB32_Premultiplied);
m_colorPreviewBuffer = QImage(QWidget::width(), QWidget::height(), QImage::Format_ARGB32_Premultiplied);
m_maskBuffer = QImage(size, size, QImage::Format_ARGB32_Premultiplied);
m_lightStripBuffer = QImage(stripThick, QWidget::height(), QImage::Format_ARGB32_Premultiplied);
m_numLightPieces = numLightPieces;
m_isDirtyGamutMask = true;
m_isDirtyLightStrip = true;
m_isDirtyWheel = true;
m_isDirtyColorPreview = true;
}
void KisColorSelector::recalculateRings(quint8 numRings, quint8 numPieces)
{
m_colorRings.resize(numRings);
m_numPieces = numPieces;
for(int i=0; i<numRings; ++i) {
qreal innerRadius = qreal(i) / qreal(numRings);
qreal outerRadius = qreal(i+1) / qreal(numRings);
qreal saturation = qreal(i) / qreal(numRings-1);
createRing(m_colorRings[i], numPieces, innerRadius, outerRadius+0.001);
m_colorRings[i].saturation = m_inverseSaturation ? (1.0 - saturation) : saturation;
}
m_isDirtyWheel = true;
}
void KisColorSelector::createRing(ColorRing& ring, quint8 numPieces, qreal innerRadius, qreal outerRadius)
{
int numParts = qMax<int>(numPieces, 1);
ring.innerRadius = innerRadius;
ring.outerRadius = outerRadius;
ring.pieced.resize(numParts);
qreal partSize = 360.0 / qreal(numParts);
QRectF outerRect(-outerRadius, -outerRadius, outerRadius*2.0, outerRadius*2.0);
QRectF innerRect(-innerRadius, -innerRadius, innerRadius*2.0, innerRadius*2.0);
for(int i=0; i<numParts; ++i) {
qreal aBeg = partSize*i;
qreal aEnd = aBeg + partSize;
aBeg -= partSize / 2.0;
aEnd -= partSize / 2.0;
ring.pieced[i] = QPainterPath();
ring.pieced[i].arcMoveTo(innerRect, aBeg);
ring.pieced[i].arcTo(outerRect, aBeg, partSize);
ring.pieced[i].arcTo(innerRect, aEnd,-partSize);
}
}
bool KisColorSelector::colorIsClear(const KisColor &color)
{
if (m_gamutMaskOn && m_currentGamutMask) {
QPointF colorCoord = mapCoordToView(mapColorToUnit(color, false), m_renderArea);
QPointF translatedPoint = m_currentGamutMask->viewToMaskTransform(m_renderArea.width()).map(colorCoord);
bool isClear = m_currentGamutMask->coordIsClear(translatedPoint, m_maskPreviewActive);
if (isClear) {
return true;
} else {
return false;
}
} else {
return true;
}
return false;
}
void KisColorSelector::requestUpdateColorAndPreview(const KisColor &color, Acs::ColorRole role)
{
#ifdef DEBUG_ARC_SELECTOR
dbgPlugins << "KisColorSelector::requestUpdateColorAndPreview: requesting update to: "
<< "H:" << color.getH()
<< "S:" << color.getS()
<< "X:" << color.getX();
#endif
m_updateColorCompressor->start(qMakePair(color, role));
}
void KisColorSelector::slotUpdateColorAndPreview(QPair<KisColor, Acs::ColorRole> color)
{
const bool selectAsFgColor = color.second == Acs::Foreground;
if (selectAsFgColor) {
m_fgColor = KisColor(color.first, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
} else {
m_bgColor = KisColor(color.first, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
}
m_selectedColor = KisColor(color.first, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
m_isDirtyLightStrip = true;
m_isDirtyColorPreview = true;
m_isDirtyWheel = true;
#ifdef DEBUG_ARC_SELECTOR
dbgPlugins << "KisColorSelector::slotUpdateColorAndPreview: m_selectedColor set to:"
<< "H:" << m_selectedColor.getH()
<< "S:" << m_selectedColor.getS()
<< "X:" << m_selectedColor.getX();
#endif
if (selectAsFgColor) { emit sigFgColorChanged(m_selectedColor); }
else { emit sigBgColorChanged(m_selectedColor); }
}
void KisColorSelector::drawRing(QPainter& painter, KisColorSelector::ColorRing& ring, const QRect& rect)
{
painter.save();
painter.setRenderHint(QPainter::Antialiasing, true);
painter.resetTransform();
painter.translate(rect.width()/2, rect.height()/2);
if (ring.pieced.size() > 1) {
QTransform mirror;
mirror.rotate(180, Qt::YAxis);
painter.setTransform(mirror, true);
painter.scale(rect.width()/2, rect.height()/2);
QPen normalPen = QPen(QBrush(COLOR_NORMAL_OUTLINE), 0.005);
QPen clearMaskPen = QPen(QBrush(COLOR_MASK_CLEAR), 0.005);
QBrush brush(Qt::SolidPattern);
for(int i=0; i<ring.pieced.size(); ++i) {
qreal hue = qreal(i) / qreal(ring.pieced.size());
hue = (hue >= 1.0) ? (hue - 1.0) : hue;
hue = (hue < 0.0) ? (hue + 1.0) : hue;
KisColor color(hue, m_colorConverter, m_colorSpace);
color.setS(ring.saturation);
color.setX(m_selectedColor.getX());
if(m_gamutMaskOn && m_enforceGamutMask && colorIsClear(color)) {
painter.setPen(clearMaskPen);
} else {
painter.setPen(normalPen);
}
if ((m_enforceGamutMask) && (!colorIsClear(color))) {
brush.setColor(COLOR_MASK_FILL);
} else {
brush.setColor(color.toQColor());
}
painter.setBrush(brush);
painter.drawPath(ring.pieced[i]);
}
}
else {
KisColor colors[7] = {
KisColor(Qt::cyan , m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma),
KisColor(Qt::green , m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma),
KisColor(Qt::yellow , m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma),
KisColor(Qt::red , m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma),
KisColor(Qt::magenta, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma),
KisColor(Qt::blue , m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma),
KisColor(Qt::cyan , m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma)
};
QConicalGradient gradient(0, 0, 0);
for(int i=0; i<=6; ++i) {
qreal hue = qreal(i) / 6.0;
colors[i].setS(ring.saturation);
colors[i].setX(m_selectedColor.getX());
gradient.setColorAt(hue, colors[i].toQColor());
}
painter.scale(rect.width()/2, rect.height()/2);
painter.fillPath(ring.pieced[0], QBrush(gradient));
}
painter.restore();
}
void KisColorSelector::drawOutline(QPainter& painter, const QRect& rect)
{
painter.save();
painter.setRenderHint(QPainter::Antialiasing, true);
painter.resetTransform();
painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2);
painter.scale(rect.width()/2, rect.height()/2);
QPen normalPen = QPen(QBrush(COLOR_NORMAL_OUTLINE), 0.005);
QPen selectedPen;
painter.setPen(normalPen);
if (getNumPieces() > 1) {
if (m_selectedRing >= 0 && m_selectedPiece >= 0) {
painter.resetTransform();
painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2);
QTransform mirror;
mirror.rotate(180, Qt::YAxis);
painter.setTransform(mirror, true);
painter.scale(rect.width()/2, rect.height()/2);
if (m_selectedColor.getX() < 0.55) {
selectedPen = QPen(QBrush(COLOR_SELECTED_LIGHT), 0.007);
} else {
selectedPen = QPen(QBrush(COLOR_SELECTED_DARK), 0.007);
}
painter.setPen(selectedPen);
painter.drawPath(m_colorRings[m_selectedRing].pieced[m_selectedPiece]);
}
}
else {
for(int i=0; i<getNumRings(); ++i) {
qreal rad = m_colorRings[i].outerRadius;
painter.drawEllipse(QRectF(-rad, -rad, rad*2.0, rad*2.0));
}
if (m_selectedRing >= 0) {
qreal iRad = m_colorRings[m_selectedRing].innerRadius;
qreal oRad = m_colorRings[m_selectedRing].outerRadius;
if (m_selectedColor.getX() < 0.55) {
selectedPen = QPen(QBrush(COLOR_SELECTED_LIGHT), 0.005);
} else {
selectedPen = QPen(QBrush(COLOR_SELECTED_DARK), 0.005);
}
painter.setPen(selectedPen);
painter.drawEllipse(QRectF(-iRad, -iRad, iRad*2.0, iRad*2.0));
painter.drawEllipse(QRectF(-oRad, -oRad, oRad*2.0, oRad*2.0));
QPointF lineCoords = mapHueToAngle(m_selectedColor.getH());
painter.drawLine(QPointF(lineCoords.x()*iRad, lineCoords.y()*iRad), QPointF(lineCoords.x()*oRad, lineCoords.y()*oRad));
}
}
painter.restore();
}
void KisColorSelector::drawLightStrip(QPainter& painter, const QRect& rect)
{
qreal penSize = qreal(qMin(QWidget::width(), QWidget::height())) / 200.0;
qreal penSizeSmall = penSize / 1.2;
QPen selectedPen;
KisColor valueScaleColor(m_selectedColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
KisColor grayScaleColor(Qt::gray, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma);
int rectSize = rect.height();
painter.save();
painter.resetTransform();
painter.setRenderHint(QPainter::Antialiasing, true);
QTransform matrix;
matrix.translate(rect.x(), rect.y());
matrix.scale(rect.width(), rect.height());
qreal rectColorLeftX;
qreal rectColorWidth;
if (m_showValueScaleNumbers) {
rectColorLeftX = 0.6;
rectColorWidth = 0.4;
} else {
rectColorLeftX = 0.0;
rectColorWidth = 1.0;
}
if (getNumLightPieces() > 1) {
for(int i=0; i<getNumLightPieces(); ++i) {
qreal t1 = qreal(i) / qreal(getNumLightPieces());
qreal t2 = qreal(i+1) / qreal(getNumLightPieces());
qreal light = 1.0 - (qreal(i) / qreal(getNumLightPieces()-1));
qreal diff = t2 - t1;// + 0.001;
QRectF rectColor = QRectF(rectColorLeftX, t1, rectColorWidth, diff);
rectColor = matrix.mapRect(rectColor);
valueScaleColor.setX(light);
painter.fillRect(rectColor, valueScaleColor.toQColor());
if (i == m_selectedLightPiece) {
if (light < 0.55) {
selectedPen = QPen(QBrush(COLOR_SELECTED_LIGHT), penSize);
} else {
selectedPen = QPen(QBrush(COLOR_SELECTED_DARK), penSize);
}
painter.setPen(selectedPen);
painter.drawRect(rectColor);
}
}
} else {
painter.setRenderHint(QPainter::Antialiasing, false);
for(int i=0; i<rectSize; ++i) {
int y = rect.y() + i;
qreal light = 1.0 - (qreal(i) / qreal(rectSize-1));
valueScaleColor.setX(light);
painter.setPen(QPen(QBrush(valueScaleColor.toQColor()), penSize));
painter.drawLine(rect.left(), y, rect.right(), y);
}
}
// draw color blip
painter.setRenderHint(QPainter::Antialiasing, false);
// draw position of fg color value on the strip
qreal fgColorValue = 1.0 - m_fgColor.getX();
int y = rect.y() + int(rectSize * fgColorValue);
painter.setPen(QPen(QBrush(COLOR_SELECTED_LIGHT), penSizeSmall));
painter.drawLine(rect.left(), y, rect.right(), y);
painter.setPen(QPen(QBrush(COLOR_SELECTED_DARK), penSizeSmall));
painter.drawLine(rect.left(), y+2*penSizeSmall, rect.right(), y+2*penSizeSmall);
// draw color blip
if (m_showValueScaleNumbers) {
painter.setRenderHint(QPainter::Antialiasing, true);
int valueScalePieces = getNumLightPieces();
if (getNumLightPieces() == 1) {
valueScalePieces = 11;
}
QFont font = painter.font();
QFontMetrics fm = painter.fontMetrics();
int retries = 10; // limit number of font size searches to prevent endless cycles
while ((retries > 0) && (fm.boundingRect("100%").width() > rect.width()*rectColorLeftX)) {
font.setPointSize(font.pointSize() - 1);
painter.setFont(font);
fm = painter.fontMetrics();
retries--;
}
for(int i=0; i<valueScalePieces; ++i) {
qreal t1 = qreal(i) / qreal(valueScalePieces);
qreal t2 = qreal(i+1) / qreal(valueScalePieces);
qreal light = 1.0 - (qreal(i) / qreal(valueScalePieces-1));
qreal diff = t2 - t1;// + 0.001;
grayScaleColor.setX(light);
QRectF rectValue = QRectF(0.0, t1, rectColorLeftX, diff);
rectValue = matrix.mapRect(rectValue);
painter.fillRect(rectValue, grayScaleColor.toQColor());
// if the right font size was not found in time,
// skip drawing the numbers
if (retries > 0) {
int valueNumber = 0;
if (m_colorSpace == KisColor::HSY) {
valueNumber = 100 - round(pow(pow(grayScaleColor.getX(), m_lumaGamma), 1.0/2.2)*100);
} else {
valueNumber = 100 - grayScaleColor.getX()*100;
}
if (valueNumber < 55) {
painter.setPen(QPen(QBrush(COLOR_DARK), penSize));
} else {
painter.setPen(QPen(QBrush(COLOR_LIGHT), penSize));
}
painter.drawText(rectValue, Qt::AlignRight|Qt::AlignBottom, QString("%1%").arg(QString::number(valueNumber)));
}
}
}
painter.restore();
}
void KisColorSelector::drawBlip(QPainter& painter, const QRect& rect)
{
painter.save();
painter.setRenderHint(QPainter::Antialiasing, true);
painter.resetTransform();
painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2);
painter.scale(rect.width()/2, rect.height()/2);
QPointF fgColorPos = mapColorToUnit(m_fgColor);
#ifdef DEBUG_ARC_SELECTOR
dbgPlugins << "KisColorSelector::drawBlip: "
<< "colorPoint H:" << m_fgColor.getH() << " S:" << m_fgColor.getS()
<< "-> coord X:" << fgColorPos.x() << " Y:" << fgColorPos.y();
#endif
painter.setPen(QPen(QBrush(COLOR_SELECTED_DARK), 0.01));
painter.drawEllipse(fgColorPos, 0.05, 0.05);
painter.setPen(QPen(QBrush(COLOR_SELECTED_LIGHT), 0.01));
painter.drawEllipse(fgColorPos, 0.04, 0.04);
painter.restore();
}
void KisColorSelector::drawGamutMaskShape(QPainter &painter, const QRect &rect)
{
painter.save();
painter.setRenderHint(QPainter::Antialiasing, true);
painter.resetTransform();
painter.translate(rect.width()/2, rect.height()/2);
painter.scale(rect.width()/2, rect.height()/2);
painter.setPen(Qt::NoPen);
painter.setBrush(COLOR_MASK_FILL);
painter.drawEllipse(QPointF(0,0), 1.0, 1.0);
painter.setWorldTransform(m_gamutMaskViewTransform);
painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
m_currentGamutMask->paint(painter, m_maskPreviewActive);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
m_currentGamutMask->paintStroke(painter, m_maskPreviewActive);
painter.restore();
}
void KisColorSelector::drawColorPreview(QPainter &painter, const QRect &rect)
{
painter.save();
painter.setRenderHint(QPainter::Antialiasing, true);
painter.fillRect(rect, m_fgColor.toQColor());
int bgSide = qMin(rect.width()*0.15,rect.height()*0.15);
if (m_showBgColor) {
QPointF bgPolyPoints[3] = {
QPointF(rect.width(), rect.height()),
QPointF(rect.width()-bgSide, rect.height()),
QPointF(rect.width(), rect.height()-bgSide)
};
painter.setBrush(m_bgColor.toQColor());
painter.setPen(m_bgColor.toQColor());
painter.drawPolygon(bgPolyPoints, 3);
}
painter.restore();
}
void KisColorSelector::paintEvent(QPaintEvent* /*event*/)
{
QPainter wdgPainter(this);
// draw the fg and bg color previews
if (m_isDirtyColorPreview) {
m_colorPreviewBuffer.fill(Qt::transparent);
QPainter colorPreviewPainter(&m_colorPreviewBuffer);
drawColorPreview(colorPreviewPainter, m_widgetArea);
m_isDirtyColorPreview = false;
}
wdgPainter.drawImage(m_widgetArea, m_colorPreviewBuffer);
// draw the fg and bg color previews
// draw the wheel
if (m_isDirtyWheel) {
m_renderBuffer.fill(Qt::transparent);
QPainter wheelPainter(&m_renderBuffer);
for(int i=0; i<m_colorRings.size(); ++i)
drawRing(wheelPainter, m_colorRings[i], m_renderArea);
m_isDirtyWheel = false;
}
wdgPainter.drawImage(m_renderArea, m_renderBuffer);
// draw the wheel
// draw the mask either in continuous mode or in discrete mode when enforcing is turned off
// if enforcing is turned on in discrete mode,
// drawRing function takes care of delineating the mask swatches
if (m_gamutMaskOn
&& ((getNumPieces() == 1) || (!m_enforceGamutMask))) {
if (m_isDirtyGamutMask) {
m_maskBuffer.fill(Qt::transparent);
QPainter maskPainter(&m_maskBuffer);
drawGamutMaskShape(maskPainter, m_renderArea);
m_isDirtyGamutMask = false;
}
wdgPainter.drawImage(m_renderArea, m_maskBuffer);
}
// draw gamut mask
drawOutline(wdgPainter, m_renderArea);
// draw light strip
if (m_isDirtyLightStrip) {
m_lightStripBuffer.fill(Qt::transparent);
QPainter lightStripPainter(&m_lightStripBuffer);
drawLightStrip(lightStripPainter, m_lightStripArea);
m_isDirtyLightStrip = false;
}
wdgPainter.drawImage(m_lightStripArea, m_lightStripBuffer);
// draw light strip
drawBlip(wdgPainter, m_renderArea);
}
void KisColorSelector::mousePressEvent(QMouseEvent* event)
{
m_widgetUpdatesSelf = true;
#ifdef DEBUG_ARC_SELECTOR
dbgPlugins << "KisColorSelector::mousePressEvent: m_widgetUpdatesSelf = true";
#endif
m_clickPos = mapCoordToUnit(event->localPos(), m_renderArea);
m_mouseMoved = false;
m_pressedButtons = event->buttons();
m_clickedRing = getSaturationIndex(m_clickPos);
Acs::ColorRole colorRole = Acs::buttonsToRole(Qt::NoButton, m_pressedButtons);
qint8 clickedLightPiece = getLightIndex(event->localPos());
if (clickedLightPiece >= 0) {
setLight(getLight(event->localPos()));
m_selectedLightPiece = clickedLightPiece;
requestUpdateColorAndPreview(m_selectedColor, colorRole);
m_mouseMoved = true;
}
else if (m_clickedRing >= 0) {
if (getNumPieces() == 1) {
Radian angle = mapCoordToAngle(m_clickPos.x(), m_clickPos.y());
KisColor color(m_colorConverter, m_colorSpace);
color.setHSX(angle.scaled(0.0, 1.0)
, getSaturation(m_clickedRing)
, m_selectedColor.getX()
);
#ifdef DEBUG_ARC_SELECTOR
dbgPlugins << "KisColorSelector::mousePressEvent: picked color: "
<< "H:" << color.getH()
<< "S:" << color.getS()
<< "X:" << color.getX();
#endif
if ((!m_enforceGamutMask) || colorIsClear(color)) {
m_selectedColor.setHSX(color.getH(), color.getS(), color.getX());
requestUpdateColorAndPreview(m_selectedColor, colorRole);
m_selectedRing = m_clickedRing;
m_mouseMoved = true;
update();
}
}
}
}
void KisColorSelector::mouseMoveEvent(QMouseEvent* event)
{
QPointF dragPos = mapCoordToUnit(event->localPos(), m_renderArea);
qint8 clickedLightPiece = getLightIndex(event->localPos());
Acs::ColorRole colorRole = Acs::buttonsToRole(Qt::NoButton, m_pressedButtons);
if (clickedLightPiece >= 0) {
setLight(getLight(event->localPos()));
m_selectedLightPiece = clickedLightPiece;
requestUpdateColorAndPreview(m_selectedColor, colorRole);
}
if (m_clickedRing < 0)
return;
if (getNumPieces() == 1) {
Radian angle = mapCoordToAngle(dragPos.x(), dragPos.y());
KisColor color(m_colorConverter, m_colorSpace);
color.setHSX(angle.scaled(0.0, 1.0)
, getSaturation(m_clickedRing)
, m_selectedColor.getX()
);
if ((!m_enforceGamutMask) || colorIsClear(color)) {
m_selectedColor.setHSX(color.getH(), color.getS(), color.getX());
requestUpdateColorAndPreview(m_selectedColor, colorRole);
}
}
update();
}
void KisColorSelector::mouseReleaseEvent(QMouseEvent* /*event*/)
{
Acs::ColorRole colorRole = Acs::buttonsToRole(Qt::NoButton, m_pressedButtons);
if (!m_mouseMoved && m_clickedRing >= 0) {
Radian angle = mapCoordToAngle(m_clickPos.x(), m_clickPos.y());
KisColor color(m_colorConverter, m_colorSpace);
qint8 hueIndex = getHueIndex(angle);
if (getNumPieces() > 1) {
color.setH(getHue(hueIndex));
} else {
color.setH(angle.scaled(0.0, 1.0));
}
color.setS(getSaturation(m_clickedRing));
color.setX(m_selectedColor.getX());
if ((!m_enforceGamutMask) || colorIsClear(color)) {
m_selectedColor.setHSX(color.getH(), color.getS(), color.getX());
m_selectedPiece = hueIndex;
m_selectedRing = m_clickedRing;
requestUpdateColorAndPreview(m_selectedColor, colorRole);
}
}
else if (m_mouseMoved)
requestUpdateColorAndPreview(m_selectedColor, colorRole);
m_clickedRing = -1;
m_widgetUpdatesSelf = false;
#ifdef DEBUG_ARC_SELECTOR
dbgPlugins << "KisColorSelector::ReleasePressEvent: m_widgetUpdatesSelf = false";
#endif
update();
}
void KisColorSelector::resizeEvent(QResizeEvent* /*event*/)
{
recalculateAreas(quint8(getNumLightPieces()));
}
void KisColorSelector::leaveEvent(QEvent* /*e*/)
{
m_widgetUpdatesSelf = false;
#ifdef DEBUG_ARC_SELECTOR
dbgPlugins << "KisColorSelector::leaveEvent: m_widgetUpdatesSelf = false";
#endif
}
void KisColorSelector::saveSettings()
{
KisConfig cfg(false);
cfg.writeEntry("ArtColorSel.ColorSpace" , qint32(m_colorSpace));
cfg.writeEntry("ArtColorSel.lumaR", qreal(m_lumaR));
cfg.writeEntry("ArtColorSel.lumaG", qreal(m_lumaG));
cfg.writeEntry("ArtColorSel.lumaB", qreal(m_lumaB));
cfg.writeEntry("ArtColorSel.lumaGamma", qreal(m_lumaGamma));
cfg.writeEntry("ArtColorSel.NumRings" , m_colorRings.size());
cfg.writeEntry("ArtColorSel.RingPieces" , qint32(m_numPieces));
cfg.writeEntry("ArtColorSel.LightPieces", qint32(m_numLightPieces));
cfg.writeEntry("ArtColorSel.InversedSaturation", m_inverseSaturation);
cfg.writeEntry("ArtColorSel.Light" , m_selectedColor.getX());
cfg.writeEntry("ArtColorSel.SelColorH", m_selectedColor.getH());
cfg.writeEntry("ArtColorSel.SelColorS", m_selectedColor.getS());
cfg.writeEntry("ArtColorSel.SelColorX", m_selectedColor.getX());
cfg.writeEntry("ArtColorSel.defaultHueSteps", quint32(m_defaultHueSteps));
cfg.writeEntry("ArtColorSel.defaultSaturationSteps", quint32(m_defaultSaturationSteps));
cfg.writeEntry("ArtColorSel.defaultValueScaleSteps", quint32(m_defaultValueScaleSteps));
cfg.writeEntry("ArtColorSel.showBgColor", m_showBgColor);
cfg.writeEntry("ArtColorSel.showValueScale", m_showValueScaleNumbers);
cfg.writeEntry("ArtColorSel.enforceGamutMask", m_enforceGamutMask);
}
void KisColorSelector::loadSettings()
{
KisConfig cfg(true);
m_defaultHueSteps = cfg.readEntry("ArtColorSel.defaultHueSteps", DEFAULT_HUE_STEPS);
m_defaultSaturationSteps = cfg.readEntry("ArtColorSel.defaultSaturationSteps", DEFAULT_SATURATION_STEPS);
m_defaultValueScaleSteps = cfg.readEntry("ArtColorSel.defaultValueScaleSteps", DEFAULT_VALUE_SCALE_STEPS);
setNumLightPieces(cfg.readEntry("ArtColorSel.LightPieces", DEFAULT_VALUE_SCALE_STEPS));
KisColor::Type colorSpace = KisColor::Type(cfg.readEntry<qint32>("ArtColorSel.ColorSpace" , KisColor::HSY));
setColorSpace(colorSpace);
setLumaCoefficients(cfg.readEntry("ArtColorSel.lumaR", DEFAULT_LUMA_R),
cfg.readEntry("ArtColorSel.lumaG", DEFAULT_LUMA_G),
cfg.readEntry("ArtColorSel.lumaB", DEFAULT_LUMA_B),
cfg.readEntry("ArtColorSel.lumaGamma", DEFAULT_LUMA_GAMMA));
m_selectedColor.setH(cfg.readEntry<qreal>("ArtColorSel.SelColorH", 0.0));
m_selectedColor.setS(cfg.readEntry<qreal>("ArtColorSel.SelColorS", 0.0));
m_selectedColor.setX(cfg.readEntry<qreal>("ArtColorSel.SelColorX", 0.0));
setInverseSaturation(cfg.readEntry<bool>("ArtColorSel.InversedSaturation", false));
setLight(cfg.readEntry<qreal>("ArtColorSel.Light", 0.5f));
setNumRings(cfg.readEntry("ArtColorSel.NumRings", DEFAULT_SATURATION_STEPS));
setNumPieces(cfg.readEntry("ArtColorSel.RingPieces", DEFAULT_HUE_STEPS));
m_showBgColor = cfg.readEntry("ArtColorSel.showBgColor", true);
m_showValueScaleNumbers = cfg.readEntry("ArtColorSel.showValueScale", false);
m_enforceGamutMask = cfg.readEntry("ArtColorSel.enforceGamutMask", false);
selectColor(m_selectedColor);
update();
}
void KisColorSelector::setDefaultHueSteps(int num)
{
num = qBound(MIN_NUM_HUE_PIECES, num, MAX_NUM_HUE_PIECES);
m_defaultHueSteps = num;
}
void KisColorSelector::setDefaultSaturationSteps(int num)
{
num = qBound(MIN_NUM_SATURATION_RINGS, num, MAX_NUM_SATURATION_RINGS);
m_defaultSaturationSteps = num;
}
void KisColorSelector::setDefaultValueScaleSteps(int num)
{
num = qBound(MIN_NUM_LIGHT_PIECES, num, MAX_NUM_LIGHT_PIECES);
m_defaultValueScaleSteps = num;
}
void KisColorSelector::setShowBgColor(bool value)
{
m_showBgColor = value;
m_isDirtyColorPreview = true;
update();
}
void KisColorSelector::setShowValueScaleNumbers(bool value)
{
m_showValueScaleNumbers = value;
recalculateAreas(quint8(getNumLightPieces()));
update();
}
diff --git a/plugins/dockers/artisticcolorselector/kis_color_selector.h b/plugins/dockers/artisticcolorselector/kis_color_selector.h
index 3d4395646b..409c8d64bc 100644
--- a/plugins/dockers/artisticcolorselector/kis_color_selector.h
+++ b/plugins/dockers/artisticcolorselector/kis_color_selector.h
@@ -1,210 +1,210 @@
/*
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.
*/
#ifndef H_KIS_COLOR_SELECTOR_H
#define H_KIS_COLOR_SELECTOR_H
#include <QWidget>
#include <QVector>
#include <QImage>
#include <QPainterPath>
#include "kis_color.h"
#include "kis_radian.h"
#include "kis_acs_types.h"
#include "kis_signal_compressor_with_param.h"
#include <resources/KoGamutMask.h>
class QPainter;
class KisDisplayColorConverter;
class QTransform;
class KisColorSelector: public QWidget
{
Q_OBJECT
typedef KisRadian<qreal> Radian;
struct ColorRing
{
ColorRing()
: saturation(0)
, outerRadius(0)
, innerRadius(0)
{ }
qreal saturation;
qreal outerRadius;
qreal innerRadius;
QVector<QPainterPath> pieced;
};
public:
KisColorSelector(QWidget* parent, KisColor::Type type=KisColor::HSL);
void setColorSpace(KisColor::Type type);
void setColorConverter(KisDisplayColorConverter* colorConverter);
void setNumPieces(int num);
void setNumLightPieces(int num);
void setNumRings(int num);
void setLight(qreal light=0.0f);
void setLumaCoefficients(qreal lR, qreal lG, qreal lB, qreal lGamma);
inline qreal lumaR() const { return m_lumaR; }
inline qreal lumaG() const { return m_lumaG; }
inline qreal lumaB() const { return m_lumaB; }
inline qreal lumaGamma() const { return m_lumaGamma; }
void setInverseSaturation(bool inverse);
void selectColor(const KisColor& color);
void setFgColor(const KoColor& fgColor);
void setBgColor(const KoColor& bgColor);
void setDefaultHueSteps(int num);
void setDefaultSaturationSteps(int num);
void setDefaultValueScaleSteps(int num);
void setShowBgColor(bool value);
void setShowValueScaleNumbers(bool value);
- void setGamutMask(KoGamutMask* gamutMask);
+ void setGamutMask(KoGamutMaskSP gamutMask);
void setDirty();
bool gamutMaskOn();
void setGamutMaskOn(bool gamutMaskOn);
void setEnforceGamutMask(bool enforce);
- KoGamutMask* gamutMask();
+ KoGamutMaskSP gamutMask();
void saveSettings();
void loadSettings();
KisColor::Type getColorSpace () const { return m_colorSpace; }
qint32 getNumRings () const { return m_colorRings.size(); }
qint32 getNumPieces () const { return m_numPieces; }
qint32 getNumLightPieces () const { return m_numLightPieces; }
bool isSaturationInverted() const { return m_inverseSaturation; }
quint32 getDefaultHueSteps () const { return m_defaultHueSteps; }
quint32 getDefaultSaturationSteps () const { return m_defaultSaturationSteps; }
quint32 getDefaultValueScaleSteps () const { return m_defaultValueScaleSteps; }
bool getShowBgColor () const { return m_showBgColor; }
bool getShowValueScaleNumbers () const { return m_showValueScaleNumbers; }
bool enforceGamutMask () const { return m_enforceGamutMask; }
Q_SIGNALS:
void sigFgColorChanged(const KisColor& color);
void sigBgColorChanged(const KisColor& color);
private:
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
void paintEvent(QPaintEvent* event) override;
void leaveEvent(QEvent* e) override;
bool colorIsClear(const KisColor &color);
bool colorIsClear(const QPointF &colorPoint);
void requestUpdateColorAndPreview(const KisColor &color, Acs::ColorRole role);
void recalculateAreas(quint8 numLightPieces);
void recalculateRings(quint8 numRings, quint8 numPieces);
void createRing(ColorRing& wheel, quint8 numPieces, qreal innerRadius, qreal outerRadius);
void drawRing(QPainter& painter, ColorRing& wheel, const QRect& rect);
void drawOutline(QPainter& painter, const QRect& rect);
void drawBlip(QPainter& painter, const QRect& rect);
void drawLightStrip(QPainter& painter, const QRect& rect);
void drawGamutMaskShape(QPainter& painter, const QRect& rect);
void drawColorPreview(QPainter& painter, const QRect& rect);
qint8 getHueIndex(Radian hue) const;
qreal getHue(int hueIdx, Radian shift=0.0f) const;
qint8 getLightIndex(const QPointF& pt) const;
qint8 getLightIndex(qreal light) const;
qreal getLight(const QPointF& pt) const;
qint8 getSaturationIndex(const QPointF& pt) const;
qint8 getSaturationIndex(qreal saturation) const;
qreal getSaturation(int saturationIdx) const;
QPointF mapCoordToView(const QPointF& pt, const QRectF& viewRect) const;
QPointF mapCoordToUnit(const QPointF& pt, const QRectF& viewRect) const;
QPointF mapColorToUnit(const KisColor& color, bool invertSaturation = true) const;
Radian mapCoordToAngle(qreal x, qreal y) const;
QPointF mapHueToAngle(qreal hue) const;
public:
// This is a private interface for signal compressor, don't use it.
// Use requestUpdateColorAndPreview() instead
void slotUpdateColorAndPreview(QPair<KisColor, Acs::ColorRole> color);
private:
KisDisplayColorConverter* m_colorConverter;
KisColor::Type m_colorSpace;
quint8 m_numPieces;
quint8 m_numLightPieces;
bool m_inverseSaturation;
qint8 m_selectedRing;
qint8 m_selectedPiece;
qint8 m_selectedLightPiece;
KisColor m_selectedColor;
KisColor m_fgColor;
KisColor m_bgColor;
QImage m_renderBuffer;
QImage m_maskBuffer;
QImage m_lightStripBuffer;
QImage m_colorPreviewBuffer;
QRect m_widgetArea;
QRect m_renderArea;
QRect m_lightStripArea;
bool m_mouseMoved;
QPointF m_clickPos;
qint8 m_clickedRing;
QVector<ColorRing> m_colorRings;
Qt::MouseButtons m_pressedButtons;
// docker settings
quint8 m_defaultHueSteps;
quint8 m_defaultSaturationSteps;
quint8 m_defaultValueScaleSteps;
bool m_showValueScaleNumbers {false};
bool m_showBgColor {true};
bool m_gamutMaskOn;
- KoGamutMask* m_currentGamutMask;
+ KoGamutMaskSP m_currentGamutMask;
bool m_enforceGamutMask;
// QSize m_renderAreaSize;
bool m_maskPreviewActive;
// KisGamutMaskViewConverter* m_viewConverter;
QTransform m_gamutMaskViewTransform;
bool m_widgetUpdatesSelf;
bool m_isDirtyWheel;
bool m_isDirtyLightStrip;
bool m_isDirtyGamutMask;
bool m_isDirtyColorPreview;
qreal m_lumaR;
qreal m_lumaG;
qreal m_lumaB;
qreal m_lumaGamma;
typedef KisSignalCompressorWithParam<QPair<KisColor, Acs::ColorRole>> ColorCompressorType;
QScopedPointer<ColorCompressorType> m_updateColorCompressor;
};
#endif // H_KIS_COLOR_SELECTOR_H
diff --git a/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp b/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp
index 7c7e19440d..088a6be379 100644
--- a/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp
+++ b/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp
@@ -1,252 +1,243 @@
/*
* Copyright (c) 2018 Anna Medonosova <anna.medonosova@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 "KisGamutMaskChooser.h"
#include <QWidget>
#include <QVBoxLayout>
#include <QAbstractItemDelegate>
#include <QMenu>
#include <QActionGroup>
#include <QToolButton>
#include <QFontMetrics>
#include <QTextDocument>
#include <QTextLayout>
-#include <KoResourceServer.h>
-#include <KoResourceServerProvider.h>
-#include <KoResourceItemChooser.h>
-#include <KoResourceServerAdapter.h>
+#include <KisResourceItemChooser.h>
+#include <KisResourceItemListView.h>
+#include <KisResourceModel.h>
#include <kis_icon_utils.h>
#include <kis_config.h>
/// The resource item delegate for rendering the resource preview
class KisGamutMaskDelegate: public QAbstractItemDelegate
{
public:
KisGamutMaskDelegate(QObject * parent = 0) : QAbstractItemDelegate(parent)
, m_mode(KisGamutMaskChooser::ViewMode::THUMBNAIL) {}
~KisGamutMaskDelegate() 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 setViewMode(KisGamutMaskChooser::ViewMode mode) {
m_mode = mode;
}
private:
KisGamutMaskChooser::ViewMode m_mode;
};
void KisGamutMaskDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
painter->save();
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
if (!index.isValid())
return;
- KoResource* resource = static_cast<KoResource*>(index.internalPointer());
- KoGamutMask* mask = static_cast<KoGamutMask*>(resource);
-
- if (!mask) {
- return;
- }
-
- QImage preview = mask->image();
+ QImage preview = index.data(Qt::UserRole + KisResourceModel::Thumbnail).value<QImage>();
+ QString name = index.data(Qt::UserRole + KisResourceModel::Name).value<QString>();
if(preview.isNull()) {
return;
}
QRect paintRect = option.rect.adjusted(1, 1, -1, -1);
if (m_mode == KisGamutMaskChooser::ViewMode::THUMBNAIL) {
painter->drawImage(paintRect.x(), paintRect.y(),
preview.scaled(paintRect.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
if (option.state & QStyle::State_Selected) {
painter->setCompositionMode(QPainter::CompositionMode_Overlay);
painter->setOpacity(0.5);
painter->fillRect(paintRect, Qt::white);
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
painter->setOpacity(1.0);
painter->setPen(QPen(option.palette.highlight(), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
QRect selectedBorder = option.rect.adjusted(1, 1, -1, -1);
painter->drawRect(selectedBorder);
}
} else {
QSize previewSize(paintRect.height(), paintRect.height());
painter->drawImage(paintRect.x(), paintRect.y(),
preview.scaled(previewSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
int leftMargin = 8;
int rightMargin = 7;
int vertMargin = 4;
int descOffset = 7;
QFont font = option.font;
font.setBold(true);
painter->setFont(font);
QRectF titleRect(QPointF(previewSize.width() + leftMargin, paintRect.y() + vertMargin),
QPointF(paintRect.width() - rightMargin, paintRect.y() + descOffset + painter->fontMetrics().lineSpacing()));
painter->drawText(titleRect, Qt::AlignLeft,
painter->fontMetrics().elidedText(
- mask->title(), Qt::ElideRight, titleRect.width()
+ name, Qt::ElideRight, titleRect.width()
)
);
-
+/*
+ * We currently cannot actually get the mask description, so lets stop this for now.
if (!mask->description().isEmpty() && !mask->description().isNull()) {
font.setPointSize(font.pointSize()-1);
font.setBold(false);
font.setStyle(QFont::StyleItalic);
painter->setFont(font);
QRectF descRect(QPointF(previewSize.width() + leftMargin, paintRect.y() + descOffset + painter->fontMetrics().lineSpacing()),
QPointF(paintRect.right() - rightMargin, paintRect.bottom() - vertMargin));
int numLines = floor(((float)descRect.height() / (float)painter->fontMetrics().lineSpacing()));
if (numLines > 0) {
int elideWidth = 0;
QTextLayout textLayout(mask->description());
textLayout.beginLayout();
for (int i = 0; i < numLines; i++) {
QTextLine line = textLayout.createLine();
if (line.isValid()) {
line.setLineWidth(descRect.width());
elideWidth += line.naturalTextWidth();
}
}
QString elidedText = painter->fontMetrics().elidedText(mask->description(), Qt::ElideRight, elideWidth);
painter->drawText(descRect, Qt::AlignLeft|Qt::TextWordWrap, elidedText);
}
- }
+ }*/
}
painter->restore();
}
KisGamutMaskChooser::KisGamutMaskChooser(QWidget *parent) : QWidget(parent)
{
m_delegate = new KisGamutMaskDelegate(this);
- KoResourceServer<KoGamutMask>* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
- QSharedPointer<KoAbstractResourceServerAdapter> adapter(new KoResourceServerAdapter<KoGamutMask>(rServer));
- m_itemChooser = new KoResourceItemChooser(adapter, this);
+ m_itemChooser = new KisResourceItemChooser(ResourceType::GamutMasks, false, this);
m_itemChooser->setItemDelegate(m_delegate);
m_itemChooser->showTaggingBar(true);
m_itemChooser->showButtons(false);
- m_itemChooser->setColumnCount(4);
m_itemChooser->setSynced(true);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(0,0,0,0);
// TODO: menu for view mode change
QMenu* menu = new QMenu(this);
menu->setStyleSheet("margin: 6px");
menu->addSection(i18nc("@title Which elements to display (e.g., thumbnails or details)", "Display"));
QActionGroup *actionGroup = new QActionGroup(this);
KisConfig cfg(true);
m_mode = KisGamutMaskChooser::ViewMode(cfg.readEntry<quint32>("GamutMasks.viewMode", KisGamutMaskChooser::THUMBNAIL));
QAction* action = menu->addAction(KisIconUtils::loadIcon("view-preview"),
i18n("Thumbnails"), this, SLOT(slotSetModeThumbnail()));
action->setCheckable(true);
action->setChecked(m_mode == KisGamutMaskChooser::THUMBNAIL);
action->setActionGroup(actionGroup);
action = menu->addAction(KisIconUtils::loadIcon("view-list-details"),
i18n("Details"), this, SLOT(slotSetModeDetail()));
action->setCheckable(true);
action->setChecked(m_mode == KisGamutMaskChooser::DETAIL);
action->setActionGroup(actionGroup);
// setting the view mode
setViewMode(m_mode);
m_itemChooser->setViewModeButtonVisible(true);
QToolButton* viewModeButton = m_itemChooser->viewModeButton();
viewModeButton->setMenu(menu);
layout->addWidget(m_itemChooser);
setLayout(layout);
- connect(m_itemChooser, SIGNAL(resourceSelected(KoResource*)), this, SLOT(resourceSelected(KoResource*)));
+ connect(m_itemChooser, SIGNAL(resourceSelected(KoResourceSP )), this, SLOT(resourceSelected(KoResourceSP )));
}
KisGamutMaskChooser::~KisGamutMaskChooser()
{
}
-void KisGamutMaskChooser::setCurrentResource(KoResource *resource)
+void KisGamutMaskChooser::setCurrentResource(KoResourceSP resource)
{
m_itemChooser->setCurrentResource(resource);
}
void KisGamutMaskChooser::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
updateViewSettings();
}
void KisGamutMaskChooser::setViewMode(KisGamutMaskChooser::ViewMode mode)
{
m_mode = mode;
updateViewSettings();
}
void KisGamutMaskChooser::updateViewSettings()
{
KisConfig cfg(false);
cfg.writeEntry("GamutMasks.viewMode", qint32(m_mode));
if (m_mode == KisGamutMaskChooser::THUMBNAIL) {
m_itemChooser->setSynced(true);
m_delegate->setViewMode(m_mode);
} else if (m_mode == KisGamutMaskChooser::DETAIL) {
m_itemChooser->setSynced(false);
- m_itemChooser->setColumnCount(1);
+ m_itemChooser->itemView()->setViewMode(QListView::ListMode);
m_itemChooser->setRowHeight(this->fontMetrics().lineSpacing()*4);
m_itemChooser->setColumnWidth(m_itemChooser->width());
m_delegate->setViewMode(m_mode);
}
}
-void KisGamutMaskChooser::resourceSelected(KoResource* resource)
+void KisGamutMaskChooser::resourceSelected(KoResourceSP resource)
{
- emit sigGamutMaskSelected(static_cast<KoGamutMask*>(resource));
+ emit sigGamutMaskSelected(resource.staticCast<KoGamutMask>());
}
void KisGamutMaskChooser::slotSetModeThumbnail()
{
setViewMode(KisGamutMaskChooser::THUMBNAIL);
}
void KisGamutMaskChooser::slotSetModeDetail()
{
setViewMode(KisGamutMaskChooser::DETAIL);
}
diff --git a/plugins/dockers/gamutmask/KisGamutMaskChooser.h b/plugins/dockers/gamutmask/KisGamutMaskChooser.h
index 56865508e8..680ee29b62 100644
--- a/plugins/dockers/gamutmask/KisGamutMaskChooser.h
+++ b/plugins/dockers/gamutmask/KisGamutMaskChooser.h
@@ -1,63 +1,64 @@
/*
* Copyright (c) 2018 Anna Medonosova <anna.medonosova@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 KISGAMUTMASKCHOOSER_H
#define KISGAMUTMASKCHOOSER_H
#include <QWidget>
-class KoResourceItemChooser;
-class KoResource;
-class KoGamutMask;
+class KisResourceItemChooser;
+#include <KoResource.h>
+#include <resources/KoGamutMask.h>
+
class KisGamutMaskDelegate;
class KisGamutMaskChooser : public QWidget
{
Q_OBJECT
public:
explicit KisGamutMaskChooser(QWidget *parent = nullptr);
~KisGamutMaskChooser() override;
enum ViewMode {
THUMBNAIL, // Shows thumbnails
DETAIL // Shows thumbsnails with text next to it
};
- void setCurrentResource(KoResource* resource);
+ void setCurrentResource(KoResourceSP resource);
protected:
void resizeEvent(QResizeEvent* event) override;
Q_SIGNALS:
- void sigGamutMaskSelected(KoGamutMask* mask);
+ void sigGamutMaskSelected(KoGamutMaskSP mask);
private Q_SLOTS:
- void resourceSelected(KoResource* resource);
+ void resourceSelected(KoResourceSP resource);
void slotSetModeThumbnail();
void slotSetModeDetail();
private:
void setViewMode(ViewMode mode);
void updateViewSettings();
- KoResourceItemChooser* m_itemChooser;
+ KisResourceItemChooser* m_itemChooser;
KisGamutMaskDelegate* m_delegate;
ViewMode m_mode;
};
#endif // KISGAMUTMASKCHOOSER_H
diff --git a/plugins/dockers/gamutmask/gamutmask_dock.cpp b/plugins/dockers/gamutmask/gamutmask_dock.cpp
index 75385386ea..9607618044 100644
--- a/plugins/dockers/gamutmask/gamutmask_dock.cpp
+++ b/plugins/dockers/gamutmask/gamutmask_dock.cpp
@@ -1,630 +1,630 @@
/*
* Copyright (c) 2018 Anna Medonosova <anna.medonosova@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_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 <KisResourceItemChooser.h>
#include <QWidget>
#include <QMenu>
#include <QButtonGroup>
#include <QRegularExpressionValidator>
#include <QRegularExpression>
#include <QFileInfo>
#include <QMessageBox>
+#include <QInputDialog>
#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->maskChooser, SIGNAL(sigGamutMaskSelected(KoGamutMaskSP)), SLOT(slotGamutMaskSelected(KoGamutMaskSP)));
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->canvasResourceProvider();
selectMask(m_resourceProvider->currentGamutMask());
- 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(sigGamutMaskSet(KoGamutMaskSP)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMaskSP)), Qt::UniqueConnection);
+ connect(this, SIGNAL(sigGamutMaskChanged(KoGamutMaskSP)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMaskSP)), 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");
+ QString maskTemplateFile = KoResourcePaths::findResource(ResourceType::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)
+void GamutMaskDock::selectMask(KoGamutMaskSP 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();
+ KoResourceServerProvider::instance()->gamutMaskServer()->addResource(m_selectedMask);
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);
+ rServer->removeResourceFromServer(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)
+KoGamutMaskSP GamutMaskDock::createMaskResource(KoGamutMaskSP sourceMask, QString newTitle)
{
m_creatingNewMask = true;
- KoGamutMask* newMask = nullptr;
+ KoGamutMaskSP newMask;
if (sourceMask) {
- newMask = new KoGamutMask(sourceMask);
+ newMask = KoGamutMaskSP(new KoGamutMask(sourceMask.data()));
newMask->setImage(sourceMask->image());
} else {
- newMask = new KoGamutMask();
+ newMask = KoGamutMaskSP(new KoGamutMask());
- QString defaultPreviewPath = KoResourcePaths::findResource("ko_gamutmasks", "empty_mask_preview.png");
+ QString defaultPreviewPath = KoResourcePaths::findResource(ResourceType::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 name = newTitle;
+
+ QFileInfo fileInfo(saveLocation + name + newMask->defaultFileExtension());
+ bool fileOverWriteAccepted = false;
- QString resourceName = processedTitle;
- while (rServer->resourceByName(resourceName)) {
- resourceName = resourceName + QString(" (Copy)");
+ while(!fileOverWriteAccepted) {
+ name = QInputDialog::getText(this, i18nc("@title:window", "New Gamut Mask..."),
+ i18nc("@label:textbox", "Name:"), QLineEdit::Normal, name);
+ if (name.isNull() || name.isEmpty()) {
+ QMessageBox::warning(this, i18nc("@title:window", "Name invalid"), i18n("Please enter a name"));
+ } else {
+ fileInfo = QFileInfo(saveLocation + name.split(" ").join("_") + newMask->defaultFileExtension());
+ if (fileInfo.exists()) {
+ int res = QMessageBox::warning(this, i18nc("@title:window", "Name Already Exists")
+ , i18n("The name '%1' already exists, do you wish to overwrite it?", name)
+ , QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ if (res == QMessageBox::Yes) fileOverWriteAccepted = true;
+ } else {
+ fileOverWriteAccepted = true;
+ }
+ }
}
- QString maskTitle = resourceName;
- QString maskFile = maskTitle + ".kgm";
- QString path = saveLocation + maskFile.replace(QRegularExpression("\\s+"), "_");
- QFileInfo fileInfo(path);
+ newMask->setTitle(name);
+ newMask->setFilename(fileInfo.fileName());
+ newMask->setValid(true);
+ rServer->addResource(newMask, true);
- return QPair<QString, QFileInfo>(maskTitle, fileInfo);
+ return newMask;
}
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);
+ KoGamutMaskSP 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)
+void GamutMaskDock::slotGamutMaskSelected(KoGamutMaskSP 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)
+void GamutMaskDock::removingResource(KoGamutMaskSP 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)
+void GamutMaskDock::resourceChanged(KoGamutMaskSP 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");
+ KoGamutMaskSP 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());
+ KoGamutMaskSP newMask = createMaskResource(m_selectedMask, m_selectedMask->title() + QString(" (Copy)"));
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/gamutmask/gamutmask_dock.h b/plugins/dockers/gamutmask/gamutmask_dock.h
index 023e0c036a..a625d01dd6 100644
--- a/plugins/dockers/gamutmask/gamutmask_dock.h
+++ b/plugins/dockers/gamutmask/gamutmask_dock.h
@@ -1,126 +1,125 @@
/*
* Copyright (c) 2018 Anna Medonosova <anna.medonosova@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 H_GAMUT_MASK_DOCK_H
#define H_GAMUT_MASK_DOCK_H
#include <QDockWidget>
#include <QPointer>
#include <QRegExpValidator>
#include <QMessageBox>
#include <KoCanvasObserverBase.h>
#include <KoResourceServerProvider.h>
-#include <KoResourceServerAdapter.h>
#include <KoResourceServerObserver.h>
#include <resources/KoGamutMask.h>
#include <KisDocument.h>
#include <KisView.h>
#include <kis_types.h>
-#include <KoResourceItemChooser.h>
+#include <KisResourceItemChooser.h>
#include <kis_mainwindow_observer.h>
class KisCanvasResourceProvider;
class QButtonGroup;
class QMenu;
struct GamutMaskChooserUI;
class GamutMaskDock: public QDockWidget, public KisMainwindowObserver, public KoResourceServerObserver<KoGamutMask>
{
Q_OBJECT
public:
GamutMaskDock();
~GamutMaskDock() override;
QString observerName() override { return "GamutMaskDock"; }
void setViewManager(KisViewManager* kisview) override;
void setCanvas(KoCanvasBase *canvas) override;
void unsetCanvas() override;
public: // KoResourceServerObserver
void unsetResourceServer() override;
- void resourceAdded(KoGamutMask* /*resource*/) override {};
- void removingResource(KoGamutMask* resource) override;
- void resourceChanged(KoGamutMask* resource) override;
+ void resourceAdded(KoGamutMaskSP /*resource*/) override {}
+ void removingResource(KoGamutMaskSP resource) override;
+ void resourceChanged(KoGamutMaskSP resource) override;
void syncTaggedResourceView() override {}
void syncTagAddition(const QString&) override {}
void syncTagRemoval(const QString&) override {}
Q_SIGNALS:
- void sigGamutMaskSet(KoGamutMask* mask);
- void sigGamutMaskChanged(KoGamutMask* mask);
+ void sigGamutMaskSet(KoGamutMaskSP mask);
+ void sigGamutMaskChanged(KoGamutMaskSP mask);
void sigGamutMaskUnset();
void sigGamutMaskPreviewUpdate();
private Q_SLOTS:
void slotGamutMaskEdit();
void slotGamutMaskSave();
void slotGamutMaskCancelEdit();
- void slotGamutMaskSelected(KoGamutMask* mask);
+ void slotGamutMaskSelected(KoGamutMaskSP mask);
void slotGamutMaskPreview();
void slotGamutMaskCreateNew();
void slotGamutMaskDuplicate();
void slotGamutMaskDelete();
void slotDocumentRemoved(QString filename);
void slotViewChanged();
void slotDocumentSaved();
private:
void closeMaskDocument();
bool openMaskEditor();
void cancelMaskEdit();
- void selectMask(KoGamutMask* mask, bool notifyItemChooser = true);
+ void selectMask(KoGamutMaskSP mask, bool notifyItemChooser = true);
bool saveSelectedMaskResource();
void deleteMask();
int getUserFeedback(QString text, QString informativeText = "",
QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No,
QMessageBox::StandardButton defaultButton = QMessageBox::Yes,
QMessageBox::Icon severity = QMessageBox::Warning);
int saveOrCancel(QMessageBox::StandardButton defaultAction = QMessageBox::Save);
- KoGamutMask* createMaskResource(KoGamutMask* sourceMask, QString newTitle);
+ KoGamutMaskSP createMaskResource(KoGamutMaskSP sourceMask, QString newTitle);
QPair<QString, QFileInfo> resolveMaskTitle(QString suggestedTitle);
QList<KoShape*> getShapesFromLayer();
KisShapeLayerSP getShapeLayer();
KisCanvasResourceProvider* m_resourceProvider;
bool m_selfClosingTemplate;
bool m_externalTemplateClose;
bool m_creatingNewMask;
bool m_templatePrevSaved;
bool m_selfSelectingMask;
GamutMaskChooserUI* m_dockerUI;
- KoResourceItemChooser* m_maskChooser;
+ KisResourceItemChooser* m_maskChooser;
- KoGamutMask* m_selectedMask;
+ KoGamutMaskSP m_selectedMask;
QRegExpValidator* m_maskTitleValidator;
KisDocument* m_maskDocument;
KisView* m_view;
};
#endif // H_GAMUT_MASK_DOCK_H
diff --git a/plugins/dockers/layerdocker/NodeView.cpp b/plugins/dockers/layerdocker/NodeView.cpp
index 8c18321979..057a9676af 100644
--- a/plugins/dockers/layerdocker/NodeView.cpp
+++ b/plugins/dockers/layerdocker/NodeView.cpp
@@ -1,592 +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 "NodeView.h"
#include "NodePropertyAction_p.h"
#include "NodeDelegate.h"
#include "NodeViewVisibilityDelegate.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 NodeView::Private
{
public:
Private(NodeView* _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);
}
NodeDelegate delegate;
DisplayMode mode;
QPersistentModelIndex hovered;
QPoint lastPos;
#ifdef DRAG_WHILE_DRAG_WORKAROUND
bool isDragging;
#endif
};
NodeView::NodeView(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)));
}
}
}
NodeView::~NodeView()
{
delete d;
}
void NodeView::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();
}
}
NodeView::DisplayMode NodeView::displayMode() const
{
return d->mode;
}
void NodeView::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 NodeView::updateNode(const QModelIndex &index)
{
dataChanged(index, index);
}
QItemSelectionModel::SelectionFlags NodeView::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 NodeView::visualRect(const QModelIndex &index) const
{
QRect rc = QTreeView::visualRect(index);
if (layoutDirection() == Qt::RightToLeft)
rc.setRight(width());
else
rc.setLeft(0);
return rc;
}
QRect NodeView::originalVisualRect(const QModelIndex &index) const
{
return QTreeView::visualRect(index);
}
QModelIndex NodeView::indexAt(const QPoint &point) const
{
KisNodeViewColorScheme scm;
QModelIndex index = QTreeView::indexAt(point);
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 NodeView::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 NodeView::contextMenuEvent(QContextMenuEvent *e)
{
QTreeView::contextMenuEvent(e);
QModelIndex i = indexAt(e->pos());
if (model())
i = model()->buddy(i);
showContextMenu(e->globalPos(), i);
}
void NodeView::showContextMenu(const QPoint &globalPos, const QModelIndex &index)
{
emit contextMenuRequested(globalPos, index);
}
void NodeView::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 NodeView::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 NodeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
QTreeView::selectionChanged(selected, deselected);
emit selectionChanged(selectedIndexes());
}
void NodeView::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 NodeView::optionForIndex(const QModelIndex &index) const
{
QStyleOptionViewItem option = viewOptions();
option.rect = visualRect(index);
if (index == currentIndex())
option.state |= QStyle::State_HasFocus;
return option;
}
void NodeView::startDrag(Qt::DropActions supportedActions)
{
DRAG_WHILE_DRAG_WORKAROUND_START();
if (displayMode() == NodeView::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 NodeView::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 NodeView::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 NodeView::paintEvent(QPaintEvent *event)
{
event->accept();
QTreeView::paintEvent(event);
// Paint the line where the slide should go
if (isDragging() && (displayMode() == NodeView::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 NodeView::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 NodeDelegate.
* So this override basically disables painting of Qt's branch-lines.
*/
}
void NodeView::dropEvent(QDropEvent *ev)
{
if (displayMode() == NodeView::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 NodeView::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 NodeView::dragEnterEvent(QDragEnterEvent *ev)
{
DRAG_WHILE_DRAG_WORKAROUND_START();
- QVariant data = qVariantFromValue(
+ QVariant data = QVariant::fromValue(
static_cast<void*>(const_cast<QMimeData*>(ev->mimeData())));
model()->setData(QModelIndex(), data, KisNodeModel::DropEnabled);
QTreeView::dragEnterEvent(ev);
}
void NodeView::dragMoveEvent(QDragMoveEvent *ev)
{
DRAG_WHILE_DRAG_WORKAROUND_START();
if (displayMode() == NodeView::ThumbnailMode) {
ev->accept();
if (!model()) {
return;
}
QTreeView::dragMoveEvent(ev);
setDraggingFlag();
viewport()->update();
return;
}
QTreeView::dragMoveEvent(ev);
}
void NodeView::dragLeaveEvent(QDragLeaveEvent *e)
{
if (displayMode() == NodeView::ThumbnailMode) {
setDraggingFlag(false);
} else {
QTreeView::dragLeaveEvent(e);
}
DRAG_WHILE_DRAG_WORKAROUND_STOP();
}
bool NodeView::isDragging() const
{
return m_draggingFlag;
}
void NodeView::setDraggingFlag(bool flag)
{
m_draggingFlag = flag;
}
void NodeView::slotUpdateIcons()
{
d->delegate.slotUpdateIcon();
}
void NodeView::slotScrollerStateChanged(QScroller::State state){
KisKineticScroller::updateCursor(this, state);
}
diff --git a/plugins/dockers/palettedocker/palettedocker_dock.cpp b/plugins/dockers/palettedocker/palettedocker_dock.cpp
index 722026f156..3e6472ec50 100644
--- a/plugins/dockers/palettedocker/palettedocker_dock.cpp
+++ b/plugins/dockers/palettedocker/palettedocker_dock.cpp
@@ -1,402 +1,377 @@
/*
* 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 <KisSqueezedComboBox.h>
#include <klocalizedstring.h>
#include <KoResourceServerProvider.h>
+#include <KisResourceLocator.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 <KisPaletteChooser.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_paletteChooser(new KisPaletteChooser(this))
, m_view(0)
, m_resourceProvider(0)
, m_rServer(KoResourceServerProvider::instance()->paletteServer())
, m_activeDocument(0)
, 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);
+ connect(KisResourceLocator::instance(), SIGNAL(storageRemoved()), this, SLOT(slotStoragesChanged()));
+
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)));
connect(m_ui->cmbNameList, SIGNAL(sigColorSelected(const KoColor&)), SLOT(slotNameListSelection(const KoColor&)));
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(sigPaletteSelected(KoColorSetSP)), SLOT(slotSetColorSet(KoColorSetSP)));
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*)));
+ connect(m_paletteChooser, SIGNAL(sigRemovePalette(KoColorSetSP)), SLOT(slotRemovePalette(KoColorSetSP)));
+ connect(m_paletteChooser, SIGNAL(sigExportPalette(KoColorSetSP)), SLOT(slotExportPalette(KoColorSetSP)));
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);
+ KoColorSetSP 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->canvasResourceProvider();
- connect(m_resourceProvider, SIGNAL(sigSavingWorkspace(KisWorkspaceResource*)),
- SLOT(saveToWorkspace(KisWorkspaceResource*)));
- connect(m_resourceProvider, SIGNAL(sigLoadingWorkspace(KisWorkspaceResource*)),
- SLOT(loadFromWorkspace(KisWorkspaceResource*)));
+ connect(m_resourceProvider, SIGNAL(sigSavingWorkspace(KisWorkspaceResourceSP)),
+ SLOT(saveToWorkspace(KisWorkspaceResourceSP)));
+ connect(m_resourceProvider, SIGNAL(sigLoadingWorkspace(KisWorkspaceResourceSP)),
+ SLOT(loadFromWorkspace(KisWorkspaceResourceSP)));
connect(m_resourceProvider, SIGNAL(sigFGColorChanged(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)
+void PaletteDockerDock::slotRemovePalette(KoColorSetSP cs)
{
m_paletteEditor->removePalette(cs);
}
void PaletteDockerDock::slotImportPalette()
{
m_paletteEditor->importPalette();
}
-void PaletteDockerDock::slotExportPalette(KoColorSet *palette)
+void PaletteDockerDock::slotExportPalette(KoColorSetSP 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 != 0);
if (canvas) {
KisCanvas2 *cv = qobject_cast<KisCanvas2*>(canvas);
m_ui->paletteView->setDisplayRenderer(cv->displayColorConverter()->displayRendererInterface());
}
- if (m_activeDocument) {
- m_connections.clear();
- 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);
- }
- m_connections.addConnection(m_activeDocument, &KisDocument::sigPaletteListChanged,
- this, &PaletteDockerDock::slotUpdatePaletteList);
}
if (!m_currentColorSet) {
slotSetColorSet(0);
}
}
void PaletteDockerDock::unsetCanvas()
{
setEnabled(false);
m_ui->paletteView->setDisplayRenderer(0);
m_paletteEditor->setView(0);
- for (KoResource *r : m_rServer->resources()) {
- KoColorSet *c = static_cast<KoColorSet*>(r);
- if (!c->isGlobal()) {
- m_rServer->removeResourceFromServer(c);
- }
- }
if (!m_currentColorSet) {
slotSetColorSet(0);
}
}
-void PaletteDockerDock::slotSetColorSet(KoColorSet* colorSet)
+void PaletteDockerDock::slotSetColorSet(KoColorSetSP 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)
+void PaletteDockerDock::saveToWorkspace(KisWorkspaceResourceSP workspace)
{
if (!m_currentColorSet.isNull()) {
workspace->setProperty("palette", m_currentColorSet->name());
}
}
-void PaletteDockerDock::loadFromWorkspace(KisWorkspaceResource* workspace)
+void PaletteDockerDock::loadFromWorkspace(KisWorkspaceResourceSP workspace)
{
if (workspace->hasProperty("palette")) {
KoResourceServer<KoColorSet>* rServer = KoResourceServerProvider::instance()->paletteServer();
- KoColorSet* colorSet = rServer->resourceByName(workspace->getString("palette"));
+ KoColorSetSP colorSet = rServer->resourceByName(workspace->getString("palette"));
if (colorSet) {
slotSetColorSet(colorSet);
}
}
}
void PaletteDockerDock::slotFGColorResourceChanged(const KoColor &color)
{
if (!m_colorSelfUpdate) {
m_ui->paletteView->slotFGColorChanged(color);
}
}
+void PaletteDockerDock::slotStoragesChanged()
+{
+ if (m_activeDocument.isNull()) {
+ slotSetColorSet(0);
+ }
+ if (m_currentColorSet) {
+ if (!m_rServer->resourceByFilename(m_currentColorSet->filename())) {
+ slotSetColorSet(0);
+ }
+ }
+}
+
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_ui->paletteView->selectClosestColor(color);
m_resourceProvider->setFGColor(color);
m_colorSelfUpdate = false;
}
-
-void PaletteDockerDock::slotUpdatePaletteList(const QList<KoColorSet *> &oldPaletteList, const QList<KoColorSet *> &newPaletteList)
-{
- for (KoColorSet *cs : oldPaletteList) {
- m_rServer->removeResourceFromServer(cs);
- }
-
- for (KoColorSet *cs : newPaletteList) {
- m_rServer->addResource(cs);
- }
-
- if (!m_currentColorSet) {
- slotSetColorSet(0);
- }
-}
diff --git a/plugins/dockers/palettedocker/palettedocker_dock.h b/plugins/dockers/palettedocker/palettedocker_dock.h
index fff55179a2..0bb2f86563 100644
--- a/plugins/dockers/palettedocker/palettedocker_dock.h
+++ b/plugins/dockers/palettedocker/palettedocker_dock.h
@@ -1,119 +1,119 @@
/*
* 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>
+#include <kis_workspace_resource.h>
#include <kis_signal_auto_connection.h>
+
class KisViewManager;
class KisCanvasResourceProvider;
-class KisWorkspaceResource;
-class KisPaletteListWidget;
+class KisPaletteChooser;
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 slotRemovePalette(KoColorSetSP );
void slotImportPalette();
- void slotExportPalette(KoColorSet *);
+ void slotExportPalette(KoColorSetSP );
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 slotSetColorSet(KoColorSetSP colorSet);
- void saveToWorkspace(KisWorkspaceResource* workspace);
- void loadFromWorkspace(KisWorkspaceResource* workspace);
+ void saveToWorkspace(KisWorkspaceResourceSP workspace);
+ void loadFromWorkspace(KisWorkspaceResourceSP workspace);
void slotFGColorResourceChanged(const KoColor& color);
- void slotUpdatePaletteList(const QList<KoColorSet *> &oldPaletteList, const QList<KoColorSet *> &newPaletteList);
+
+ void slotStoragesChanged();
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;
+ KisPaletteChooser *m_paletteChooser;
QPointer<KisViewManager> m_view;
KisCanvasResourceProvider *m_resourceProvider;
KoResourceServer<KoColorSet> * const m_rServer;
QPointer<KisDocument> m_activeDocument;
- QPointer<KoColorSet> m_currentColorSet;
+ QSharedPointer<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;
-
- KisSignalAutoConnectionsStore m_connections;
};
#endif
diff --git a/plugins/dockers/palettedocker/wdgpalettedock.ui b/plugins/dockers/palettedocker/wdgpalettedock.ui
index d5f4d88aeb..1870a4f1ae 100644
--- a/plugins/dockers/palettedocker/wdgpalettedock.ui
+++ b/plugins/dockers/palettedocker/wdgpalettedock.ui
@@ -1,180 +1,180 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgPaletteDock</class>
<widget class="QWidget" name="WdgPaletteDock">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>647</width>
<height>422</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>
<widget class="KisPaletteView" name="paletteView">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="KisPaletteComboBox" name="cmbNameList">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="7">
<widget class="QToolButton" name="bnRemove">
<property name="toolTip">
<string>Delete color</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset theme="list-remove">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="autoRaise">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="2">
<spacer name="hBottomSpacer">
<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 row="2" column="5">
<widget class="QToolButton" name="bnRename">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="edit-rename">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="KisPopupButton" name="bnColorSets">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QToolButton" name="bnAdd">
<property name="toolTip">
<string>Add foreground color</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset theme="list-add">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="autoRaise">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="6">
<widget class="QToolButton" name="bnEditPalette">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset theme="document-layer">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="KSqueezedTextLabel" name="lblPaletteName">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisPopupButton</class>
<extends>QPushButton</extends>
- <header>kis_popup_button.h</header>
+ <header>KisPopupButton.h</header>
</customwidget>
<customwidget>
<class>KisPaletteView</class>
<extends>QTableView</extends>
<header location="global">kis_palette_view.h</header>
</customwidget>
<customwidget>
<class>KisPaletteComboBox</class>
<extends>QComboBox</extends>
<header>KisPaletteComboBox.h</header>
</customwidget>
<customwidget>
<class>KSqueezedTextLabel</class>
<extends>QLabel</extends>
<header location="global">ksqueezedtextlabel.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>bnRemove</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
diff --git a/plugins/dockers/patterndocker/patterndocker_dock.cpp b/plugins/dockers/patterndocker/patterndocker_dock.cpp
index 0b43d6b1fb..ee28316094 100644
--- a/plugins/dockers/patterndocker/patterndocker_dock.cpp
+++ b/plugins/dockers/patterndocker/patterndocker_dock.cpp
@@ -1,70 +1,70 @@
/*
* 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 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 "patterndocker_dock.h"
#include <QHBoxLayout>
#include <QPushButton>
#include <klocalizedstring.h>
#include <kis_canvas_resource_provider.h>
#include <kis_pattern_chooser.h>
#include <KisViewManager.h>
#include <resources/KoPattern.h>
PatternDockerDock::PatternDockerDock( )
: QDockWidget(i18n("Patterns"))
{
m_patternChooser = new KisPatternChooser(this);
m_patternChooser->setPreviewOrientation(Qt::Vertical);
- m_patternChooser->setCurrentItem(0,0);
+ m_patternChooser->setCurrentItem(0);
m_patternChooser->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
m_patternChooser->setMinimumHeight(160);
setWidget(m_patternChooser);
}
void PatternDockerDock::setViewManager(KisViewManager* kisview)
{
KisCanvasResourceProvider* resourceProvider = kisview->canvasResourceProvider();
- connect(resourceProvider, SIGNAL(sigPatternChanged(KoPattern*)),
- this, SLOT(patternChanged(KoPattern*)));
+ connect(resourceProvider, SIGNAL(sigPatternChanged(KoPatternSP)),
+ this, SLOT(patternChanged(KoPatternSP)));
- connect(m_patternChooser, SIGNAL(resourceSelected(KoResource*)),
- resourceProvider, SLOT(slotPatternActivated(KoResource*)));
+ connect(m_patternChooser, SIGNAL(resourceSelected(KoResourceSP )),
+ resourceProvider, SLOT(slotPatternActivated(KoResourceSP )));
}
void PatternDockerDock::setCanvas(KoCanvasBase *canvas)
{
setEnabled(canvas != 0);
}
void PatternDockerDock::unsetCanvas()
{
setEnabled(false);
}
-void PatternDockerDock::patternChanged(KoPattern *pattern)
+void PatternDockerDock::patternChanged(KoPatternSP pattern)
{
m_patternChooser->setCurrentPattern(pattern);
}
diff --git a/plugins/dockers/patterndocker/patterndocker_dock.h b/plugins/dockers/patterndocker/patterndocker_dock.h
index 29f40fd71a..a6eaa6aeb9 100644
--- a/plugins/dockers/patterndocker/patterndocker_dock.h
+++ b/plugins/dockers/patterndocker/patterndocker_dock.h
@@ -1,47 +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 Lesser General Public License as published by
* the Free Software Foundation; 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 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 _PATTERN_DOCK_H_
#define _PATTERN_DOCK_H_
#include <QDockWidget>
#include <kis_mainwindow_observer.h>
-class KoPattern;
+#include <KoPattern.h>
+
class KisPatternChooser;
class PatternDockerDock : public QDockWidget, public KisMainwindowObserver {
Q_OBJECT
public:
PatternDockerDock( );
void setViewManager(KisViewManager* kisview) override;
void setCanvas(KoCanvasBase *canvas) override;
void unsetCanvas() override;
QString observerName() override { return "PatternDockerDock"; }
public Q_SLOTS:
- void patternChanged(KoPattern *pattern);
+ void patternChanged(KoPatternSP pattern);
private Q_SLOTS:
private:
KisPatternChooser* m_patternChooser;
};
#endif
diff --git a/plugins/dockers/presetdocker/presetdocker_dock.cpp b/plugins/dockers/presetdocker/presetdocker_dock.cpp
index a1c17397f5..1374389fe1 100644
--- a/plugins/dockers/presetdocker/presetdocker_dock.cpp
+++ b/plugins/dockers/presetdocker/presetdocker_dock.cpp
@@ -1,86 +1,86 @@
/*
* 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 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 "presetdocker_dock.h"
#include <QHBoxLayout>
#include <QPushButton>
#include <klocalizedstring.h>
#include <KoCanvasResourceProvider.h>
#include <KoCanvasBase.h>
#include "kis_canvas2.h"
#include "KisViewManager.h"
#include "kis_paintop_box.h"
#include "kis_paintop_presets_chooser_popup.h"
#include "kis_canvas_resource_provider.h"
#include <brushengine/kis_paintop_preset.h>
PresetDockerDock::PresetDockerDock( )
: QDockWidget(i18n("Brush Presets"))
, m_canvas(0)
{
m_presetChooser = new KisPaintOpPresetsChooserPopup(this);
m_presetChooser->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
m_presetChooser->showButtons(false);
setWidget(m_presetChooser);
}
void PresetDockerDock::setCanvas(KoCanvasBase *canvas)
{
setEnabled(canvas != 0);
if (m_canvas) {
m_canvas->disconnectCanvasObserver(this);
m_presetChooser->disconnect(m_canvas->viewManager()->paintOpBox());
}
m_canvas = dynamic_cast<KisCanvas2*>(canvas);
if (!m_canvas || !m_canvas->viewManager() || !m_canvas->resourceManager()) return;
- connect(m_presetChooser, SIGNAL(resourceSelected(KoResource*)),
- m_canvas->viewManager()->paintOpBox(), SLOT(resourceSelected(KoResource*)));
- connect(m_presetChooser, SIGNAL(resourceClicked(KoResource*)),
- m_canvas->viewManager()->paintOpBox(), SLOT(resourceSelected(KoResource*)));
- connect(m_canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
+ connect(m_presetChooser, SIGNAL(resourceSelected(KoResourceSP )),
+ m_canvas->viewManager()->paintOpBox(), SLOT(resourceSelected(KoResourceSP )));
+ connect(m_presetChooser, SIGNAL(resourceClicked(KoResourceSP )),
+ m_canvas->viewManager()->paintOpBox(), SLOT(resourceSelected(KoResourceSP )));
+ connect(canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
this, SLOT(canvasResourceChanged(int,QVariant)));
connect(m_canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), m_presetChooser, SLOT(slotThemeChanged()));
canvasResourceChanged();
}
void PresetDockerDock::canvasResourceChanged(int /*key*/, const QVariant& /*v*/)
{
if (m_canvas && m_canvas->resourceManager()) {
if (sender()) sender()->blockSignals(true);
KisPaintOpPresetSP preset = m_canvas->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
if(preset)
m_presetChooser->canvasResourceChanged(preset);
if (sender()) sender()->blockSignals(false);
m_presetChooser->updateViewSettings();
}
}
diff --git a/plugins/dockers/presethistory/presethistory_dock.cpp b/plugins/dockers/presethistory/presethistory_dock.cpp
index d01e9335ff..38e6c05a89 100644
--- a/plugins/dockers/presethistory/presethistory_dock.cpp
+++ b/plugins/dockers/presethistory/presethistory_dock.cpp
@@ -1,151 +1,151 @@
/*
* 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; 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 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 "presethistory_dock.h"
#include <QHBoxLayout>
#include <QPushButton>
#include <QListWidget>
#include <QImage>
#include <klocalizedstring.h>
#include <KoCanvasResourceProvider.h>
#include <KoCanvasBase.h>
#include "kis_config.h"
#include "kis_canvas2.h"
#include "KisViewManager.h"
#include "kis_paintop_box.h"
#include "kis_paintop_presets_chooser_popup.h"
#include "kis_canvas_resource_provider.h"
#include "KisResourceServerProvider.h"
#include <KisKineticScroller.h>
#include <brushengine/kis_paintop_preset.h>
#include <kis_types.h>
#define ICON_SIZE 48
PresetHistoryDock::PresetHistoryDock( )
: QDockWidget(i18n("Brush Preset History"))
, m_canvas(0)
, m_block(false)
, m_initialized(false)
{
m_presetHistory = new QListWidget(this);
m_presetHistory->setIconSize(QSize(ICON_SIZE, ICON_SIZE));
m_presetHistory->setDragEnabled(false);
m_presetHistory->setSelectionBehavior(QAbstractItemView::SelectRows);
m_presetHistory->setSelectionMode(QAbstractItemView::SingleSelection);
m_presetHistory->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
setWidget(m_presetHistory);
QScroller* scroller = KisKineticScroller::createPreconfiguredScroller(m_presetHistory);
if( scroller ) {
connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChanged(QScroller::State)));
}
connect(m_presetHistory, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(presetSelected(QListWidgetItem*)));
}
void PresetHistoryDock::setCanvas(KoCanvasBase * canvas)
{
setEnabled(canvas != 0);
if (m_canvas) {
m_canvas->disconnectCanvasObserver(this);
disconnect(m_canvas->resourceManager());
}
m_canvas = dynamic_cast<KisCanvas2*>(canvas);
if (!m_canvas || !m_canvas->viewManager() || !m_canvas->resourceManager()) return;
connect(m_canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), SLOT(canvasResourceChanged(int,QVariant)));
if (!m_initialized) {
KisConfig cfg(true);
QStringList presetHistory = cfg.readEntry<QString>("presethistory", "").split(",", QString::SkipEmptyParts);
KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
Q_FOREACH (const QString &p, presetHistory) {
KisPaintOpPresetSP preset = rserver->resourceByName(p);
addPreset(preset);
}
m_initialized = true;
}
}
void PresetHistoryDock::unsetCanvas()
{
m_canvas = 0;
setEnabled(false);
QStringList presetHistory;
for(int i = m_presetHistory->count() -1; i >=0; --i) {
QListWidgetItem *item = m_presetHistory->item(i);
QVariant v = item->data(Qt::UserRole);
KisPaintOpPresetSP preset = v.value<KisPaintOpPresetSP>();
presetHistory << preset->name();
}
KisConfig cfg(false);
cfg.writeEntry("presethistory", presetHistory.join(","));
}
void PresetHistoryDock::presetSelected(QListWidgetItem *item)
{
if (item) {
QVariant v = item->data(Qt::UserRole);
KisPaintOpPresetSP preset = v.value<KisPaintOpPresetSP>();
m_block = true;
- m_canvas->viewManager()->paintOpBox()->resourceSelected(preset.data());
+ m_canvas->viewManager()->paintOpBox()->resourceSelected(preset);
m_block = false;
}
}
void PresetHistoryDock::canvasResourceChanged(int key, const QVariant& /*v*/)
{
if (m_block) return;
if (m_canvas && key == KisCanvasResourceProvider::CurrentPaintOpPreset) {
KisPaintOpPresetSP preset = m_canvas->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
if (preset) {
for (int i = 0; i < m_presetHistory->count(); ++i) {
if (preset->name() == m_presetHistory->item(i)->text()) {
m_presetHistory->setCurrentRow(i);
return;
}
}
addPreset(preset);
}
}
}
void PresetHistoryDock::addPreset(KisPaintOpPresetSP preset)
{
if (preset) {
QListWidgetItem *item = new QListWidgetItem(QPixmap::fromImage(preset->image()), preset->name());
QVariant v = QVariant::fromValue<KisPaintOpPresetSP>(preset);
item->setData(Qt::UserRole, v);
m_presetHistory->insertItem(0, item);
m_presetHistory->setCurrentRow(0);
if (m_presetHistory->count() > 10) {
delete m_presetHistory->takeItem(10);
}
}
}
diff --git a/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc b/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc
index 94f5cab633..5f88726f2a 100644
--- a/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc
+++ b/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc
@@ -1,270 +1,270 @@
/*
* 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 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_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 <KisPopupButton.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()), 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/dockers/specificcolorselector/wdgSpecificColorSelectorWidget.ui b/plugins/dockers/specificcolorselector/wdgSpecificColorSelectorWidget.ui
index 6a584d0565..418c27ed18 100644
--- a/plugins/dockers/specificcolorselector/wdgSpecificColorSelectorWidget.ui
+++ b/plugins/dockers/specificcolorselector/wdgSpecificColorSelectorWidget.ui
@@ -1,113 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>wdgSpecificColorSelectorWidget</class>
<widget class="QWidget" name="wdgSpecificColorSelectorWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>268</width>
<height>207</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="toolbar" stretch="0,0">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="KisPopupButton" name="colorspacePopupButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>2</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>72</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string/>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Set color space</string>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="chkUsePercentage">
<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="toolTip">
<string>Use percentage</string>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="slidersLayout"/>
</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>KisPopupButton</class>
<extends>QPushButton</extends>
- <header>kis_popup_button.h</header>
+ <header>KisPopupButton.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.cpp b/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.cpp
index ca26e9cf57..6a731caa3e 100644
--- a/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.cpp
+++ b/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.cpp
@@ -1,254 +1,313 @@
/* This file is part of the KDE project
* Copyright (C) 2008 Peter Simonsson <peter.simonsson@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 "SvgSymbolCollectionDocker.h"
#include <klocalizedstring.h>
#include <QDebug>
#include <QAbstractListModel>
#include <QMimeData>
#include <QDomDocument>
#include <QDomElement>
#include <KisSqueezedComboBox.h>
#include <QWidgetAction>
#include <QMenu>
#include <KoResourceServerProvider.h>
#include <KoResourceServer.h>
#include <KoShapeFactoryBase.h>
#include <KoProperties.h>
#include <KoDrag.h>
#include "kis_icon_utils.h"
#include <kconfiggroup.h>
#include <ksharedconfig.h>
#include "ui_WdgSvgCollection.h"
#include <resources/KoSvgSymbolCollectionResource.h>
//
// SvgCollectionModel
//
SvgCollectionModel::SvgCollectionModel(QObject *parent)
: QAbstractListModel(parent)
{
}
QVariant SvgCollectionModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() > m_symbolCollection->symbols().count()) {
return QVariant();
}
switch (role) {
case Qt::ToolTipRole:
return m_symbolCollection->symbols()[index.row()]->title;
case Qt::DecorationRole:
{
QPixmap px = QPixmap::fromImage(m_symbolCollection->symbols()[index.row()]->icon());
QIcon icon(px);
return icon;
}
case Qt::UserRole:
return m_symbolCollection->symbols()[index.row()]->id;
case Qt::DisplayRole:
return m_symbolCollection->symbols()[index.row()]->title;
default:
return QVariant();
}
return QVariant();
}
int SvgCollectionModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_symbolCollection->symbols().count();
}
QMimeData *SvgCollectionModel::mimeData(const QModelIndexList &indexes) const
{
if (indexes.isEmpty()) {
return 0;
}
QModelIndex index = indexes.first();
if (!index.isValid()) {
return 0;
}
if (m_symbolCollection->symbols().isEmpty()) {
return 0;
}
QList<KoShape*> shapes;
shapes.append(m_symbolCollection->symbols()[index.row()]->shape);
KoDrag drag;
drag.setSvg(shapes);
QMimeData *mimeData = drag.mimeData();
return mimeData;
}
QStringList SvgCollectionModel::mimeTypes() const
{
return QStringList() << SHAPETEMPLATE_MIMETYPE << "image/svg+xml";
}
Qt::ItemFlags SvgCollectionModel::flags(const QModelIndex &index) const
{
if (index.isValid()) {
return QAbstractListModel::flags(index) | Qt::ItemIsDragEnabled;
}
return QAbstractListModel::flags(index);
}
Qt::DropActions SvgCollectionModel::supportedDragActions() const
{
return Qt::CopyAction;
}
-void SvgCollectionModel::setSvgSymbolCollectionResource(KoSvgSymbolCollectionResource *resource)
+void SvgCollectionModel::setSvgSymbolCollectionResource(QSharedPointer<KoSvgSymbolCollectionResource> resource)
{
m_symbolCollection = resource;
}
//
// SvgSymbolCollectionDockerFactory
//
SvgSymbolCollectionDockerFactory::SvgSymbolCollectionDockerFactory()
: KoDockFactoryBase()
{
}
QString SvgSymbolCollectionDockerFactory::id() const
{
return QString("SvgSymbolCollectionDocker");
}
QDockWidget *SvgSymbolCollectionDockerFactory::createDockWidget()
{
SvgSymbolCollectionDocker *docker = new SvgSymbolCollectionDocker();
return docker;
}
//
// SvgSymbolCollectionDocker
//
SvgSymbolCollectionDocker::SvgSymbolCollectionDocker(QWidget *parent)
: QDockWidget(parent)
, m_wdgSvgCollection(new Ui_WdgSvgCollection())
{
setWindowTitle(i18n("Vector Libraries"));
QWidget* mainWidget = new QWidget(this);
setWidget(mainWidget);
m_wdgSvgCollection->setupUi(mainWidget);
connect(m_wdgSvgCollection->cmbCollections, SIGNAL(activated(int)), SLOT(collectionActivated(int)));
- KoResourceServer<KoSvgSymbolCollectionResource> *svgCollectionProvider = KoResourceServerProvider::instance()->svgSymbolCollectionServer();
- Q_FOREACH(KoSvgSymbolCollectionResource *r, svgCollectionProvider->resources()) {
- m_wdgSvgCollection->cmbCollections->addSqueezedItem(r->name());
- SvgCollectionModel *model = new SvgCollectionModel();
- model->setSvgSymbolCollectionResource(r);
- m_models.append(model);
- }
+ m_resourceModel = KisResourceModelProvider::resourceModel(ResourceType::Symbols);
+
+ m_wdgSvgCollection->cmbCollections->setModel(m_resourceModel);
+ m_wdgSvgCollection->cmbCollections->setModelColumn(KisResourceModel::Name);
m_wdgSvgCollection->listCollection->setDragEnabled(true);
m_wdgSvgCollection->listCollection->setDragDropMode(QAbstractItemView::DragOnly);
m_wdgSvgCollection->listCollection->setSelectionMode(QListView::SingleSelection);
QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(m_wdgSvgCollection->listCollection);
if (scroller) {
connect(scroller, SIGNAL(stateChanged(QScroller::State)),
this, SLOT(slotScrollerStateChanged(QScroller::State)));
}
// thumbnail icon changer
QMenu* configureMenu = new QMenu(this);
configureMenu->setStyleSheet("margin: 6px");
m_wdgSvgCollection->vectorPresetsConfigureButton->setIcon(KisIconUtils::loadIcon("configure"));
m_wdgSvgCollection->vectorPresetsConfigureButton->setPopupMode(QToolButton::InstantPopup);
// add horizontal slider for changing the icon size
m_iconSizeSlider = new QSlider(this);
m_iconSizeSlider->setOrientation(Qt::Horizontal);
m_iconSizeSlider->setRange(20, 80);
m_iconSizeSlider->setValue(20); // defaults to small icon size
m_iconSizeSlider->setMinimumHeight(20);
m_iconSizeSlider->setMinimumWidth(40);
m_iconSizeSlider->setTickInterval(10);
QWidgetAction *sliderAction= new QWidgetAction(this);
sliderAction->setDefaultWidget(m_iconSizeSlider);
configureMenu->addSection(i18n("Icon Size"));
configureMenu->addAction(sliderAction);
m_wdgSvgCollection->vectorPresetsConfigureButton->setMenu(configureMenu);
connect(m_iconSizeSlider, SIGNAL(sliderReleased()), this, SLOT(slotSetIconSize())); // resizing while sliding is too heavy of an operation
KConfigGroup cfg = KSharedConfig::openConfig()->group("SvgSymbolCollection");
int i = cfg.readEntry("currentCollection", 0);
if (i > m_wdgSvgCollection->cmbCollections->count()) {
i = 0;
}
m_wdgSvgCollection->cmbCollections->setCurrentIndex(i);
collectionActivated(i);
+
+ connect(m_resourceModel, SIGNAL(modelAboutToBeReset()), this, SLOT(slotResourceModelAboutToBeReset()));
+ connect(m_resourceModel, SIGNAL(modelReset()), this, SLOT(slotResourceModelReset()));
+}
+
+SvgSymbolCollectionDocker::~SvgSymbolCollectionDocker()
+{
+ clearModels();
}
void SvgSymbolCollectionDocker::slotSetIconSize()
{
m_wdgSvgCollection->listCollection->setIconSize(QSize(m_iconSizeSlider->value(),m_iconSizeSlider->value()));
}
+void SvgSymbolCollectionDocker::slotResourceModelAboutToBeReset()
+{
+ int index = m_wdgSvgCollection->cmbCollections->currentIndex();
+ QModelIndex idx = m_resourceModel->index(index, 0);
+ int id = m_resourceModel->data(idx, Qt::UserRole + KisResourceModel::Id).toInt();
+ m_rememberedSvgCollectionId = id;
+}
+
+void SvgSymbolCollectionDocker::slotResourceModelReset()
+{
+ int indexToSet = 0;
+ if (m_rememberedSvgCollectionId < 0) {
+ indexToSet = 0;
+ } else {
+ for (int i = 0; i < m_resourceModel->rowCount(); i++) {
+ QModelIndex idx = m_resourceModel->index(i, 0);
+ int id = m_resourceModel->data(idx, Qt::UserRole + KisResourceModel::Id).toInt();
+ if (id == m_rememberedSvgCollectionId) {
+ indexToSet = i;
+ break;
+ }
+ }
+ }
+ // remove the current model from the view
+ m_wdgSvgCollection->listCollection->setModel(0);
+ // delete all models
+ clearModels();
+ // setting current index will create and set the model
+ m_wdgSvgCollection->cmbCollections->setCurrentIndex(indexToSet);
+ collectionActivated(indexToSet);
+ m_rememberedSvgCollectionId = -1;
+}
+
void SvgSymbolCollectionDocker::setCanvas(KoCanvasBase *canvas)
{
setEnabled(canvas != 0);
}
void SvgSymbolCollectionDocker::unsetCanvas()
{
setEnabled(false);
}
void SvgSymbolCollectionDocker::collectionActivated(int index)
{
- if (index < m_models.size()) {
+ if (index < m_resourceModel->rowCount()) {
+ SvgCollectionModel *model;
+ if (m_collectionsModelsCache.contains(index)) {
+ model = m_collectionsModelsCache.value(index);
+ } else {
+ QModelIndex idx = m_resourceModel->index(index, 0);
+ QSharedPointer<KoSvgSymbolCollectionResource> r = m_resourceModel->resourceForIndex(idx).dynamicCast<KoSvgSymbolCollectionResource>();
+ model = new SvgCollectionModel();
+ model->setSvgSymbolCollectionResource(r);
+ m_collectionsModelsCache.insert(index, model);
+ }
+
KConfigGroup cfg = KSharedConfig::openConfig()->group("SvgSymbolCollection");
cfg.writeEntry("currentCollection", index);
- m_wdgSvgCollection->listCollection->setModel(m_models[index]);
+
+ m_wdgSvgCollection->listCollection->setModel(model);
+
}
}
+
+void SvgSymbolCollectionDocker::clearModels()
+{
+ Q_FOREACH(int key, m_collectionsModelsCache.keys()) {
+ delete m_collectionsModelsCache.value(key);
+ }
+ m_collectionsModelsCache.clear();
+}
diff --git a/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.h b/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.h
index 22a0b1224c..3030a56f6b 100644
--- a/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.h
+++ b/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.h
@@ -1,92 +1,104 @@
/* This file is part of the KDE project
* 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 SVGSYMBOLCOLLECTIONDOCKER_H
#define SVGSYMBOLCOLLECTIONDOCKER_H
#include <QDockWidget>
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QMap>
#include <QIcon>
#include <KoDockFactoryBase.h>
#include <KoCanvasObserverBase.h>
#include <KisKineticScroller.h>
#include "ui_WdgSvgCollection.h"
class KoSvgSymbolCollectionResource;
+class KisResourceModel;
class SvgCollectionModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit SvgCollectionModel(QObject *parent = 0);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
QStringList mimeTypes() const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
Qt::DropActions supportedDragActions() const override;
public:
- void setSvgSymbolCollectionResource(KoSvgSymbolCollectionResource *resource);
+ void setSvgSymbolCollectionResource(QSharedPointer<KoSvgSymbolCollectionResource> resource);
private:
- KoSvgSymbolCollectionResource *m_symbolCollection;
+ QSharedPointer<KoSvgSymbolCollectionResource> m_symbolCollection;
};
class SvgSymbolCollectionDockerFactory : public KoDockFactoryBase
{
public:
SvgSymbolCollectionDockerFactory();
QString id() const override;
QDockWidget *createDockWidget() override;
DockPosition defaultDockPosition() const override
{
return DockRight;
}
};
class SvgSymbolCollectionDocker : public QDockWidget, public KoCanvasObserverBase
{
Q_OBJECT
public:
explicit SvgSymbolCollectionDocker(QWidget *parent = 0);
+ ~SvgSymbolCollectionDocker();
/// reimplemented
void setCanvas(KoCanvasBase *canvas) override;
void unsetCanvas() override;
public Q_SLOTS:
void slotScrollerStateChanged(QScroller::State state){KisKineticScroller::updateCursor(this, state);}
private Q_SLOTS:
void collectionActivated(int index);
void slotSetIconSize();
+
+ void slotResourceModelAboutToBeReset();
+ void slotResourceModelReset();
+
+
private:
+ void clearModels();
+
Ui_WdgSvgCollection *m_wdgSvgCollection;
- QVector<SvgCollectionModel*> m_models;
+ QMap<int, SvgCollectionModel*> m_collectionsModelsCache;
QSlider* m_iconSizeSlider;
+
+ KisResourceModel* m_resourceModel;
+ int m_rememberedSvgCollectionId;
};
#endif
diff --git a/plugins/dockers/tasksetdocker/taskset_resource.cpp b/plugins/dockers/tasksetdocker/taskset_resource.cpp
index ca6a81ede1..a8a014f100 100644
--- a/plugins/dockers/tasksetdocker/taskset_resource.cpp
+++ b/plugins/dockers/tasksetdocker/taskset_resource.cpp
@@ -1,129 +1,111 @@
/*
* 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 Lesser General Public License as published by
* the Free Software Foundation; 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 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 "taskset_resource.h"
#include <QFile>
#include <QDomDocument>
#include <QTextStream>
#include <QBuffer>
#include <QByteArray>
#include <kis_debug.h>
#define TASKSET_VERSION 1
TasksetResource::TasksetResource(const QString& f)
: KoResource(f)
{
}
TasksetResource::~TasksetResource()
{
}
-bool TasksetResource::save()
+TasksetResource::TasksetResource(const TasksetResource &rhs)
+ : KoResource(rhs),
+ m_actions(rhs.m_actions)
{
- if (filename().isEmpty())
- return false;
-
- QFile file(filename());
- file.open(QIODevice::WriteOnly);
- bool res = saveToDevice(&file);
- file.close();
- return res;
}
-bool TasksetResource::load()
+KoResourceSP TasksetResource::clone() const
{
- QString fn = filename();
- if (fn.isEmpty()) return false;
-
- QFile file(fn);
- if (file.size() == 0) return false;
- if (!file.open(QIODevice::ReadOnly)) {
- warnKrita << "Can't open file " << filename();
- return false;
- }
-
- bool res = loadFromDevice(&file);
-
- file.close();
-
- return res;
+ return KoResourceSP(new TasksetResource(*this));
}
-bool TasksetResource::loadFromDevice(QIODevice *dev)
+bool TasksetResource::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
{
+ Q_UNUSED(resourcesInterface);
+
QDomDocument doc;
if (!doc.setContent(dev)) {
return false;
}
QDomElement element = doc.documentElement();
setName(element.attribute("name"));
QDomNode node = element.firstChild();
while (!node.isNull()) {
QDomElement child = node.toElement();
if (!child.isNull() && child.tagName() == "action") {
m_actions.append(child.text());
}
node = node.nextSibling();
}
setValid(true);
return true;
}
QString TasksetResource::defaultFileExtension() const
{
return QString(".kts");
}
void TasksetResource::setActionList(const QStringList actions)
{
m_actions = actions;
}
QStringList TasksetResource::actionList()
{
return m_actions;
}
bool TasksetResource::saveToDevice(QIODevice *io) const
{
QDomDocument doc;
QDomElement root = doc.createElement("Taskset");
root.setAttribute("name", name() );
root.setAttribute("version", TASKSET_VERSION);
Q_FOREACH (const QString& action, m_actions) {
QDomElement element = doc.createElement("action");
element.appendChild(doc.createTextNode(action));
root.appendChild(element);
}
doc.appendChild(root);
QTextStream textStream(io);
textStream.setCodec("UTF-8");
doc.save(textStream, 4);
KoResource::saveToDevice(io);
return true;
}
diff --git a/plugins/dockers/tasksetdocker/taskset_resource.h b/plugins/dockers/tasksetdocker/taskset_resource.h
index 5128ccb561..ba2277117f 100644
--- a/plugins/dockers/tasksetdocker/taskset_resource.h
+++ b/plugins/dockers/tasksetdocker/taskset_resource.h
@@ -1,49 +1,58 @@
/*
* 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 Lesser General Public License as published by
* the Free Software Foundation; 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 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 TASKSET_RESOURCE_H
#define TASKSET_RESOURCE_H
-#include <resources/KoResource.h>
+#include <KoResource.h>
#include <QStringList>
class TasksetResource : public KoResource
{
public:
TasksetResource(const QString& filename);
~TasksetResource() override;
-
- bool load() override;
- bool loadFromDevice(QIODevice *dev) override;
- bool save() override;
+
+ TasksetResource(const TasksetResource &rhs);
+ TasksetResource &operator=(const TasksetResource &rhs) = delete;
+ KoResourceSP clone() const override;
+
+ bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override;
bool saveToDevice(QIODevice* dev) const override;
QString defaultFileExtension() const override;
-
+
+ QPair<QString, QString> resourceType() const override
+ {
+ return QPair<QString, QString>(ResourceType::TaskSets, "");
+ }
+
void setActionList(const QStringList actions);
QStringList actionList();
private:
QStringList m_actions;
};
+typedef QSharedPointer<TasksetResource> TasksetResourceSP;
+
#endif // TASKSET_RESOURCE_H
diff --git a/plugins/dockers/tasksetdocker/tasksetdocker_dock.cpp b/plugins/dockers/tasksetdocker/tasksetdocker_dock.cpp
index b78ff73ee5..165d39d7dc 100644
--- a/plugins/dockers/tasksetdocker/tasksetdocker_dock.cpp
+++ b/plugins/dockers/tasksetdocker/tasksetdocker_dock.cpp
@@ -1,242 +1,241 @@
/*
* 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 Lesser General Public License as published by
* the Free Software Foundation; 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 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 "tasksetdocker_dock.h"
#include <QGridLayout>
#include <QListView>
#include <QHeaderView>
#include <QStyledItemDelegate>
#include <QPainter>
#include <QInputDialog>
#include <QAction>
+#include <QMessageBox>
#include <klocalizedstring.h>
#include <kactioncollection.h>
#include <kis_icon.h>
#include <KoCanvasBase.h>
-#include <KoResourceItemChooser.h>
-#include <KoResourceServerAdapter.h>
-#include <KoResourceServerProvider.h>
-
-#include <KisResourceServerProvider.h>
+#include <KisResourceItemChooser.h>
+#include <KisResourceLoader.h>
+#include <KisResourceItemListView.h>
+#include <KisResourceLoaderRegistry.h>
#include <KisViewManager.h>
#include <kis_canvas2.h>
#include <KisMainWindow.h>
#include "tasksetmodel.h"
class KisTasksetDelegate : public QStyledItemDelegate
{
public:
KisTasksetDelegate(QObject * parent = 0) : QStyledItemDelegate(parent) {}
~KisTasksetDelegate() override {}
/// reimplemented
QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const override {
return QSize(QStyledItemDelegate::sizeHint(option, index).width(),
qMin(QStyledItemDelegate::sizeHint(option, index).width(), 25));
}
};
class KisTasksetResourceDelegate : public QStyledItemDelegate
{
public:
KisTasksetResourceDelegate(QObject * parent = 0) : QStyledItemDelegate(parent) {}
~KisTasksetResourceDelegate() override {}
/// reimplemented
void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override;
};
void KisTasksetResourceDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
if (! index.isValid())
return;
- TasksetResource* taskset = static_cast<TasksetResource*>(index.internalPointer());
+ QString name = index.data(Qt::UserRole + KisResourceModel::Name).toString();
if (option.state & QStyle::State_Selected) {
painter->setPen(QPen(option.palette.highlight(), 2.0));
painter->fillRect(option.rect, option.palette.highlight());
painter->setBrush(option.palette.highlightedText());
}
else {
painter->setBrush(option.palette.text());
}
- painter->drawText(option.rect.x() + 5, option.rect.y() + painter->fontMetrics().ascent() + 5, taskset->name());
-
+ painter->drawText(option.rect.x() + 5, option.rect.y() + painter->fontMetrics().ascent() + 5, name);
}
TasksetDockerDock::TasksetDockerDock( ) : QDockWidget(i18n("Task Sets")), m_canvas(0), m_blocked(false)
{
QWidget* widget = new QWidget(this);
setupUi(widget);
m_model = new TasksetModel(this);
tasksetView->setModel(m_model);
tasksetView->setItemDelegate(new KisTasksetDelegate(this));
recordButton->setIcon(KisIconUtils::loadIcon("media-record"));
recordButton->setCheckable(true);
clearButton->setIcon(KisIconUtils::loadIcon("edit-delete"));
saveButton->setIcon(KisIconUtils::loadIcon("document-save"));
saveButton->setEnabled(false);
chooserButton->setIcon(KisIconUtils::loadIcon("edit-copy"));
- m_rserver = new KoResourceServerSimpleConstruction<TasksetResource>("kis_taskset", "*.kts");
- if (!QFileInfo(m_rserver->saveLocation()).exists()) {
- QDir().mkpath(m_rserver->saveLocation());
- }
- QSharedPointer<KoAbstractResourceServerAdapter> adapter (new KoResourceServerAdapter<TasksetResource>(m_rserver));
- m_rserver->loadResources(KoResourceServerProvider::blacklistFileNames(m_rserver->fileNames(), m_rserver->blackListedFiles()));
- m_rserver->loadTags();
-
- KoResourceItemChooser* itemChooser = new KoResourceItemChooser(adapter, this);
+ m_rserver = new KoResourceServer<TasksetResource>(ResourceType::TaskSets);
+ KisResourceLoaderRegistry::instance()->registerLoader(new KisResourceLoader<TasksetResource>(ResourceType::TaskSets, ResourceType::TaskSets, i18n("Task sets"), QStringList() << "application/x-krita-taskset"));
+ KisResourceItemChooser *itemChooser = new KisResourceItemChooser(ResourceType::TaskSets, false, this);
itemChooser->setItemDelegate(new KisTasksetResourceDelegate(this));
itemChooser->setFixedSize(500, 250);
itemChooser->setRowHeight(30);
- itemChooser->setColumnCount(1);
+ itemChooser->itemView()->setViewMode(QListView::ListMode);
itemChooser->showTaggingBar(true);
chooserButton->setPopupWidget(itemChooser);
- connect(itemChooser, SIGNAL(resourceSelected(KoResource*)), this, SLOT(resourceSelected(KoResource*)));
+ connect(itemChooser, SIGNAL(resourceSelected(KoResourceSP )), this, SLOT(resourceSelected(KoResourceSP )));
setWidget(widget);
connect( tasksetView, SIGNAL(clicked(QModelIndex)),
this, SLOT(activated(QModelIndex)) );
connect( recordButton, SIGNAL(toggled(bool)), this, SLOT(recordClicked()));
connect( clearButton, SIGNAL(clicked(bool)), this, SLOT(clearClicked()));
connect( saveButton, SIGNAL(clicked(bool)), this, SLOT(saveClicked()));
}
TasksetDockerDock::~TasksetDockerDock()
{
delete m_rserver;
}
void TasksetDockerDock::setCanvas(KoCanvasBase * canvas)
{
if (m_canvas && m_canvas->viewManager()) {
m_canvas->viewManager()->actionCollection()->disconnect(this);
Q_FOREACH (KXMLGUIClient* client, m_canvas->viewManager()->mainWindow()->childClients()) {
client->actionCollection()->disconnect(this);
}
}
m_canvas = dynamic_cast<KisCanvas2*>(canvas);
}
void TasksetDockerDock::unsetCanvas()
{
m_canvas = 0;
m_model->clear();
}
void TasksetDockerDock::actionTriggered(QAction* action)
{
if(action && !action->objectName().isEmpty() &&
!m_blocked && recordButton->isChecked()) {
m_model->addAction(action);
saveButton->setEnabled(true);
}
}
void TasksetDockerDock::activated(const QModelIndex& index)
{
QAction* action = m_model->actionFromIndex(index);
m_blocked = true;
action->trigger();
m_blocked = false;
}
void TasksetDockerDock::recordClicked()
{
if(m_canvas) {
KisViewManager* view = m_canvas->viewManager();
connect(view->actionCollection(), SIGNAL(actionTriggered(QAction*)),
this, SLOT(actionTriggered(QAction*)), Qt::UniqueConnection);
Q_FOREACH (KXMLGUIClient* client, view->mainWindow()->childClients()) {
connect(client->actionCollection(), SIGNAL(actionTriggered(QAction*)),
this, SLOT(actionTriggered(QAction*)), Qt::UniqueConnection);
}
}
}
void TasksetDockerDock::saveClicked()
{
- bool ok;
- QString name = QInputDialog::getText(this, i18n("Taskset Name"),
- i18n("Name:"), QLineEdit::Normal,
- QString(), &ok);
- if (!ok) {
- return;
- }
+ QString name;
- TasksetResource* taskset = new TasksetResource(QString());
+ TasksetResourceSP taskset(new TasksetResource(QString()));
QStringList actionNames;
Q_FOREACH (QAction* action, m_model->actions()) {
actionNames.append(action->objectName());
}
taskset->setActionList(actionNames);
taskset->setValid(true);
QString saveLocation = m_rserver->saveLocation();
- bool newName = false;
- if(name.isEmpty()) {
- newName = true;
+ if (name.isEmpty()) {
name = i18n("Taskset");
}
QFileInfo fileInfo(saveLocation + name + taskset->defaultFileExtension());
- int i = 1;
- while (fileInfo.exists()) {
- fileInfo.setFile(saveLocation + name + QString("%1").arg(i) + taskset->defaultFileExtension());
- i++;
- }
- taskset->setFilename(fileInfo.filePath());
- if(newName) {
- name = i18n("Taskset %1", i);
+ bool fileOverwriteAccepted = false;
+ bool ok = false;
+
+ while(!fileOverwriteAccepted) {
+ name = QInputDialog::getText(this, i18n("Taskset Name"),
+ i18n("Name:"), QLineEdit::Normal,
+ QString(), &ok);
+ if (name.isNull() || name.isEmpty()) {
+ return;
+ } else {
+ fileInfo = QFileInfo(saveLocation + name.split(" ").join("_") + taskset->defaultFileExtension());
+ if (fileInfo.exists()) {
+ int res = QMessageBox::warning(this, i18nc("@title:window", "Name Already Exists")
+ , i18n("The name '%1' already exists, do you wish to overwrite it?", name)
+ , QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ if (res == QMessageBox::Yes) fileOverwriteAccepted = true;
+ } else {
+ fileOverwriteAccepted = true;
+ }
+ }
}
+
taskset->setName(name);
+ taskset->setFilename(fileInfo.fileName());
m_rserver->addResource(taskset);
}
void TasksetDockerDock::clearClicked()
{
saveButton->setEnabled(false);
m_model->clear();
}
-void TasksetDockerDock::resourceSelected(KoResource* resource)
+void TasksetDockerDock::resourceSelected(KoResourceSP resource)
{
if(!m_canvas) {
return;
}
m_model->clear();
saveButton->setEnabled(true);
- Q_FOREACH (const QString& actionName, static_cast<TasksetResource*>(resource)->actionList()) {
+ Q_FOREACH (const QString& actionName, resource.staticCast<TasksetResource>()->actionList()) {
QAction* action = m_canvas->viewManager()->actionCollection()->action(actionName);
if(action) {
m_model->addAction(action);
}
}
}
diff --git a/plugins/dockers/tasksetdocker/tasksetdocker_dock.h b/plugins/dockers/tasksetdocker/tasksetdocker_dock.h
index 6aecd2c25a..58d7a5d404 100644
--- a/plugins/dockers/tasksetdocker/tasksetdocker_dock.h
+++ b/plugins/dockers/tasksetdocker/tasksetdocker_dock.h
@@ -1,62 +1,62 @@
/*
* 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 Lesser General Public License as published by
* the Free Software Foundation; 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 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 TASKSETDOCKER_DOCK_H
#define TASKSETDOCKER_DOCK_H
#include <QDockWidget>
#include <QModelIndex>
#include <QPointer>
#include <KoCanvasObserverBase.h>
#include <KoResourceServer.h>
#include <kis_canvas2.h>
#include "taskset_resource.h"
#include "ui_wdgtasksetdocker.h"
class TasksetModel;
class TasksetDockerDock : public QDockWidget, public KoCanvasObserverBase, public Ui_WdgTasksetDocker {
Q_OBJECT
public:
TasksetDockerDock();
~TasksetDockerDock() override;
QString observerName() override { return "TasksetDockerDock"; }
void setCanvas(KoCanvasBase *canvas) override;
void unsetCanvas() override;
private Q_SLOTS:
void actionTriggered(QAction* action);
void activated (const QModelIndex& index);
void recordClicked();
void saveClicked();
void clearClicked();
- void resourceSelected( KoResource * resource );
+ void resourceSelected( KoResourceSP resource );
private:
QPointer<KisCanvas2> m_canvas;
TasksetModel *m_model;
bool m_blocked;
- KoResourceServer<TasksetResource>* m_rserver;
+ KoResourceServer<TasksetResource> *m_rserver {0};
};
#endif
diff --git a/plugins/dockers/tasksetdocker/wdgtasksetdocker.ui b/plugins/dockers/tasksetdocker/wdgtasksetdocker.ui
index e4d20f755c..89db3e9ea7 100644
--- a/plugins/dockers/tasksetdocker/wdgtasksetdocker.ui
+++ b/plugins/dockers/tasksetdocker/wdgtasksetdocker.ui
@@ -1,66 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgTasksetDocker</class>
<widget class="QWidget" name="WdgTasksetDocker">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="5">
<widget class="QListView" name="tasksetView"/>
</item>
<item row="1" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>199</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="recordButton">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QPushButton" name="clearButton">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="saveButton">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="KisPopupButton" name="chooserButton"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisPopupButton</class>
<extends>QWidget</extends>
- <header>kis_popup_button.h</header>
+ <header>KisPopupButton.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/plugins/extensions/layersplit/dlg_layersplit.cpp b/plugins/extensions/layersplit/dlg_layersplit.cpp
index 68dca6421c..60cbacf586 100644
--- a/plugins/extensions/layersplit/dlg_layersplit.cpp
+++ b/plugins/extensions/layersplit/dlg_layersplit.cpp
@@ -1,160 +1,160 @@
/*
* 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 "dlg_layersplit.h"
#include <klocalizedstring.h>
#include <KoResourceServerProvider.h>
#include <kis_debug.h>
#include <KisViewManager.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include "kis_slider_spin_box.h"
#include <QCheckBox>
#include <QSpinBox>
#include <kis_config.h>
#include <KisDialogStateSaver.h>
DlgLayerSplit::DlgLayerSplit()
: KoDialog()
{
m_page = new WdgLayerSplit(this);
setCaption(i18n("Split Layer"));
setButtons(Apply | Cancel);
setDefaultButton(Apply);
m_page->intFuzziness->setRange(0, 200);
m_page->intFuzziness->setSingleStep(1);
- m_colorSetChooser = new KisPaletteListWidget();
+ m_colorSetChooser = new KisPaletteChooser();
m_page->paletteChooser->setPopupWidget(m_colorSetChooser);
-
- connect(m_colorSetChooser, SIGNAL(sigPaletteSelected(KoColorSet*)), this, SLOT(slotSetPalette(KoColorSet*)));
+ connect(m_colorSetChooser, SIGNAL(sigPaletteSelected(KoColorSetSP)), this, SLOT(slotSetPalette(KoColorSetSP)));
KisDialogStateSaver::restoreState(m_page, "krita/layer_split");
connect(m_page->cmbMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChangeMode(int)));
KisConfig cfg(true);
QString paletteName = cfg.readEntry<QString>("layersplit/paletteName", i18n("Default"));
KoResourceServer<KoColorSet> *pserver = KoResourceServerProvider::instance()->paletteServer();
- KoColorSet *pal = pserver->resourceByName(paletteName);
- modeToMask = m_page->cmbMode->currentIndex();
- slotChangeMode(modeToMask);
+ KoColorSetSP pal = pserver->resourceByName(paletteName);
+ m_modeToMask = m_page->cmbMode->currentIndex();
+ slotChangeMode(m_modeToMask);
+
if (pal) {
m_palette = pal;
m_page->paletteChooser->setText(pal->name());
QIcon icon(QPixmap::fromImage(pal->image()));
m_page->paletteChooser->setIcon(icon);
}
connect(this, SIGNAL(applyClicked()), this, SLOT(slotApplyClicked()));
setMainWidget(m_page);
}
DlgLayerSplit::~DlgLayerSplit()
{
}
void DlgLayerSplit::slotApplyClicked()
{
KisDialogStateSaver::saveState(m_page, "krita/layer_split");
KisConfig cfg(false);
if (m_palette) {
cfg.writeEntry("layersplit/paletteName", m_palette->name());
}
accept();
}
bool DlgLayerSplit::createBaseGroup() const
{
return m_page->chkCreateGroupLayer->isChecked();
}
bool DlgLayerSplit::createSeparateGroups() const
{
return m_page->chkSeparateGroupLayers->isChecked();
}
bool DlgLayerSplit::lockAlpha() const
{
return m_page->chkAlphaLock->isChecked();
}
bool DlgLayerSplit::hideOriginal() const
{
return m_page->chkHideOriginal->isChecked();
}
bool DlgLayerSplit::sortLayers() const
{
return m_page->chkSortLayers->isChecked();
}
bool DlgLayerSplit::disregardOpacity() const
{
return m_page->chkDisregardOpacity->isChecked();
}
int DlgLayerSplit::fuzziness() const
{
return m_page->intFuzziness->value();
}
-KoColorSet *DlgLayerSplit::palette() const
+KoColorSetSP DlgLayerSplit::palette() const
{
return m_palette;
}
-void DlgLayerSplit::slotSetPalette(KoColorSet *pal)
+void DlgLayerSplit::slotSetPalette(KoColorSetSP pal)
{
if (pal) {
m_palette = pal;
m_page->paletteChooser->setText(pal->name());
QIcon icon(QPixmap::fromImage(pal->image()));
m_page->paletteChooser->setIcon(icon);
}
}
void DlgLayerSplit::slotChangeMode(int idx){
- modeToMask = idx;
- if( modeToMask){
+ m_modeToMask = idx;
+ if( m_modeToMask){
m_page->chkCreateGroupLayer->hide();
m_page->chkSeparateGroupLayers->hide();
m_page->chkAlphaLock->hide();
m_page->chkHideOriginal->hide();
}
else{
m_page->chkCreateGroupLayer->show();
m_page->chkSeparateGroupLayers->show();
m_page->chkAlphaLock->show();
m_page->chkHideOriginal->show();
}
}
diff --git a/plugins/extensions/layersplit/dlg_layersplit.h b/plugins/extensions/layersplit/dlg_layersplit.h
index 85512eafaf..fdcf29767b 100644
--- a/plugins/extensions/layersplit/dlg_layersplit.h
+++ b/plugins/extensions/layersplit/dlg_layersplit.h
@@ -1,65 +1,68 @@
/*
* 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 DLG_LAYERSPLIT
#define DLG_LAYERSPLIT
#include <KoDialog.h>
#include <KoColorSet.h>
-#include <KisPaletteListWidget.h>
+#include <KisPaletteChooser.h>
#include <kis_types.h>
#include "wdg_layersplit.h"
/**
* This dialog allows the user to create a selection mask based
* on a (range of) colors.
*/
class DlgLayerSplit: public KoDialog
{
Q_OBJECT
public:
DlgLayerSplit();
~DlgLayerSplit() override;
bool createBaseGroup() const;
bool createSeparateGroups() const;
bool lockAlpha() const;
bool hideOriginal() const;
bool sortLayers() const;
bool disregardOpacity() const;
int fuzziness() const;
- KoColorSet* palette() const;
- bool modeToMask;
+ KoColorSetSP palette() const;
private Q_SLOTS:
void slotApplyClicked();
- void slotSetPalette(KoColorSet *pal);
+ void slotSetPalette(KoColorSetSP pal);
void slotChangeMode(int);
private:
+
+ friend class LayerSplit;
+ bool m_modeToMask;
+
WdgLayerSplit *m_page {0};
- KisPaletteListWidget *m_colorSetChooser {0};
- KoColorSet *m_palette {0};
+ KisPaletteChooser *m_colorSetChooser {0};
+ KoColorSetSP m_palette {0};
};
#endif // DLG_LAYERSPLIT
diff --git a/plugins/extensions/layersplit/layersplit.cpp b/plugins/extensions/layersplit/layersplit.cpp
index 1abedd29f5..c7220694e6 100644
--- a/plugins/extensions/layersplit/layersplit.cpp
+++ b/plugins/extensions/layersplit/layersplit.cpp
@@ -1,247 +1,247 @@
/*
* 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 "layersplit.h"
#include <QMap>
#include <QPointer>
#include <QHash>
#include <klocalizedstring.h>
#include <kpluginfactory.h>
#include <KoColorSpace.h>
#include <KoChannelInfo.h>
#include <KoColor.h>
#include <kis_debug.h>
#include <kis_types.h>
#include <KisViewManager.h>
#include <kis_image.h>
#include <kis_action.h>
#include <KisDocument.h>
#include <kis_node.h>
#include <kis_painter.h>
#include <kis_paint_device.h>
#include <kis_paint_layer.h>
#include <kis_group_layer.h>
#include <kis_random_accessor_ng.h>
#include "dlg_layersplit.h"
#include "kis_node_manager.h"
#include "kis_node_commands_adapter.h"
#include "kis_undo_adapter.h"
#include <kis_image_barrier_locker.h>
#include "kis_selection_mask.h"
#include <KoUpdater.h>
#include <KoProgressUpdater.h>
K_PLUGIN_FACTORY_WITH_JSON(LayerSplitFactory, "kritalayersplit.json", registerPlugin<LayerSplit>();)
LayerSplit::LayerSplit(QObject *parent, const QVariantList &)
: KisActionPlugin(parent)
{
KisAction *action = createAction("layersplit");
connect(action, SIGNAL(triggered()), this, SLOT(slotLayerSplit()));
}
LayerSplit::~LayerSplit()
{
}
struct Layer {
KoColor color;
KisPaintDeviceSP device;
KisRandomAccessorSP accessor;
int pixelsWritten;
bool operator<(const Layer& other) const
{
return pixelsWritten < other.pixelsWritten;
}
};
void LayerSplit::slotLayerSplit()
{
DlgLayerSplit dlg;
if (dlg.exec() == QDialog::Accepted) {
- bool modeToLayer = !dlg.modeToMask;
+ bool modeToLayer = !dlg.m_modeToMask;
dlg.hide();
QApplication::setOverrideCursor(Qt::WaitCursor);
QPointer<KoUpdater> updater;
if( modeToLayer){
updater = viewManager()->createUnthreadedUpdater(i18n("Split into Layers"));
}
else {
updater = viewManager()->createUnthreadedUpdater(i18n("Split into Masks"));
}
KisImageSP image = viewManager()->image();
if (!image) return;
KisImageBarrierLocker locker(image);
KisNodeSP node = viewManager()->activeNode();
if (!node) return;
KisPaintDeviceSP projection = node->projection();
if (!projection) return;
QList<Layer> colorMap;
const KoColorSpace *cs = projection->colorSpace();
QRect rc = image->bounds();
int fuzziness = dlg.fuzziness();
updater->setProgress(0);
KisRandomConstAccessorSP acc = projection->createRandomConstAccessorNG(rc.x(), rc.y());
for (int row = rc.y(); row < rc.height(); ++row) {
for (int col = rc.x(); col < rc.width(); ++col) {
acc->moveTo(col, row);
KoColor c(cs);
c.setColor(acc->rawDataConst(), cs);
if (c.opacityU8() == OPACITY_TRANSPARENT_U8) {
continue;
}
if (dlg.disregardOpacity()) {
c.setOpacity(OPACITY_OPAQUE_U8);
}
bool found = false;
Q_FOREACH (const Layer &l, colorMap) {
if (fuzziness == 0) {
found = (l.color == c);
}
else {
quint8 match = cs->difference(l.color.data(), c.data());
found = (match <= fuzziness);
}
if (found) {
KisRandomAccessorSP dstAcc = l.accessor;
dstAcc->moveTo(col, row);
memcpy(dstAcc->rawData(), acc->rawDataConst(), cs->pixelSize());
const_cast<Layer*>(&l)->pixelsWritten++;
break;
}
}
if (!found) {
QString name = "";
if (dlg.palette()) {
name = dlg.palette()->getClosestColorInfo(c).swatch.name();
}
if (name.toLower() == "untitled" || name.toLower() == "none" || name.toLower() == "") {
name = KoColor::toQString(c);
}
Layer l;
l.color = c;
l.device = new KisPaintDevice(cs, name);
l.accessor = l.device->createRandomAccessorNG(col, row);
l.accessor->moveTo(col, row);
memcpy(l.accessor->rawData(), acc->rawDataConst(), cs->pixelSize());
l.pixelsWritten = 1;
colorMap << l;
}
}
if (updater->interrupted()) {
return;
}
updater->setProgress((row - rc.y()) * 100 / rc.height() - rc.y());
}
updater->setProgress(100);
dbgKrita << "Created" << colorMap.size() << "layers";
// Q_FOREACH (const Layer &l, colorMap) {
// dbgKrita << "\t" << l.device->objectName() << ":" << l.pixelsWritten;
// }
if (dlg.sortLayers()) {
std::sort(colorMap.begin(), colorMap.end());
}
KisUndoAdapter *undo = image->undoAdapter();
undo->beginMacro(kundo2_i18n("Split Layer"));
KisNodeCommandsAdapter adapter(viewManager());
if(modeToLayer){
KisGroupLayerSP baseGroup = dynamic_cast<KisGroupLayer*>(node->parent().data());
if (!baseGroup) {
// Masks are never nested
baseGroup = dynamic_cast<KisGroupLayer*>(node->parent()->parent().data());
}
if (dlg.hideOriginal()) {
node->setVisible(false);
}
if (dlg.createBaseGroup()) {
KisGroupLayerSP grp = new KisGroupLayer(image, i18n("Color"), OPACITY_OPAQUE_U8);
adapter.addNode(grp, baseGroup, 1);
baseGroup = grp;
}
Q_FOREACH (const Layer &l, colorMap) {
KisGroupLayerSP grp = baseGroup;
if (dlg.createSeparateGroups()) {
grp = new KisGroupLayer(image, l.device->objectName(), OPACITY_OPAQUE_U8);
adapter.addNode(grp, baseGroup, 1);
}
KisPaintLayerSP paintLayer = new KisPaintLayer(image, l.device->objectName(), OPACITY_OPAQUE_U8, l.device);
adapter.addNode(paintLayer, grp, 0);
paintLayer->setAlphaLocked(dlg.lockAlpha());
}
}
else{
KisLayerSP baseGroup = dynamic_cast<KisLayer*>(node.data());
Q_FOREACH (const Layer &l, colorMap) {
KisSelectionMaskSP mask = new KisSelectionMask(image);
mask->setName( l.device->objectName());
KisPaintDeviceSP temp = KisPainter::convertToAlphaAsPureAlpha(l.device);
mask->initSelection(temp , baseGroup);
adapter.addNode(mask, baseGroup,0);
mask->setActive(true);
}
}
undo->endMacro();
image->setModified();
}
QApplication::restoreOverrideCursor();
}
#include "layersplit.moc"
diff --git a/plugins/extensions/layersplit/wdg_layersplit.ui b/plugins/extensions/layersplit/wdg_layersplit.ui
index 67bc7c1e97..7f864ac810 100644
--- a/plugins/extensions/layersplit/wdg_layersplit.ui
+++ b/plugins/extensions/layersplit/wdg_layersplit.ui
@@ -1,175 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgLayerSplit</class>
<widget class="QWidget" name="WdgLayerSplit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>449</width>
<height>412</height>
</rect>
</property>
<property name="windowTitle">
<string>Image Size</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="1" column="0">
<widget class="QCheckBox" name="chkCreateGroupLayer">
<property name="text">
<string>Put all new layers in a group layer</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="chkSeparateGroupLayers">
<property name="text">
<string>Put every layer in its own, separate group layer</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="chkAlphaLock">
<property name="text">
<string>Alpha-lock every new layer</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="chkHideOriginal">
<property name="text">
<string>Hide the original layer</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="chkSortLayers">
<property name="text">
<string>Sort layers by amount of non-transparent pixels</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="chkDisregardOpacity">
<property name="text">
<string>Disregard opacity</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Fuzziness:</string>
</property>
<property name="buddy">
<cstring>intFuzziness</cstring>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="KisSliderSpinBox" name="intFuzziness" native="true"/>
</item>
<item row="8" column="0" colspan="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Palette to use for naming the layers:</string>
</property>
</widget>
</item>
<item row="9" column="0" colspan="2">
<widget class="KisPopupButton" name="paletteChooser">
<property name="text">
<string>Choose Palette</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QComboBox" name="cmbMode">
<item>
<property name="text">
<string>Split Into Layers</string>
</property>
</item>
<item>
<property name="text">
<string>Split Into Local Selection Masks</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label">
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Split a layer according to color&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Creates a new layer for every color in the active layer.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</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>
<customwidgets>
<customwidget>
<class>KisPopupButton</class>
<extends>QPushButton</extends>
- <header>kis_popup_button.h</header>
+ <header>KisPopupButton.h</header>
</customwidget>
<customwidget>
<class>KisSliderSpinBox</class>
<extends>QWidget</extends>
<header location="global">kis_slider_spin_box.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>chkCreateGroupLayer</tabstop>
<tabstop>chkSeparateGroupLayers</tabstop>
<tabstop>chkAlphaLock</tabstop>
<tabstop>chkHideOriginal</tabstop>
<tabstop>chkSortLayers</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
diff --git a/plugins/extensions/pykrita/kritarunner/main.cpp b/plugins/extensions/pykrita/kritarunner/main.cpp
index cdfc41d9f8..7aaeffaa59 100644
--- a/plugins/extensions/pykrita/kritarunner/main.cpp
+++ b/plugins/extensions/pykrita/kritarunner/main.cpp
@@ -1,129 +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 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 <QCommandLineParser>
#include <QCommandLineOption>
#include <KisApplication.h>
-#include <resources/KoHashGeneratorProvider.h>
-#include "kis_md5_generator.h"
#include "PythonPluginManager.h"
#include <opengl/kis_opengl.h>
extern "C" int main(int argc, char **argv)
{
// The global initialization of the random generator
qsrand(time(0));
KLocalizedString::setApplicationDomain("kritarunner");
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
KisOpenGL::testingInitializeDefaultSurfaceFormat();
// first create the application so we can create a pixmap
KisApplication app("kritarunner", argc, argv);
app.setApplicationDisplayName("Krita Script Runner");
app.setApplicationName("kritarunner");
app.setOrganizationDomain("krita.org");
QCommandLineParser parser;
parser.setApplicationDescription("kritarunner executes one python script and then returns.");
parser.addVersionOption();
parser.addHelpOption();
QCommandLineOption scriptOption(QStringList() << "s" << "script", "The script to run. Do not append the .py extension.", "script");
parser.addOption(scriptOption);
QCommandLineOption functionOption(QStringList() << "f" << "function",
"The function to call (by default __main__ is called).", "function", "__main__");
parser.addOption(functionOption);
parser.addPositionalArgument("[argument(s)]", "The arguments for the script");
parser.process(app);
if (!parser.isSet(scriptOption)) {
qDebug("No script given, aborting.");
return 1;
}
qDebug() << "running:" << parser.value(scriptOption) << parser.value(functionOption);
qDebug() << parser.positionalArguments();
- KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator());
app.addResourceTypes();
- app.loadResources();
+ app.registerResources();
app.loadPlugins();
QByteArray pythonPath = qgetenv("PYTHONPATH");
qDebug() << "\tPython path:" << pythonPath;
qDebug() << "Creating engine";
// TODO: refactor to share common parts with plugin.cpp
PyKrita::InitResult initResult = PyKrita::initialize();
switch (initResult) {
case PyKrita::INIT_OK:
break;
case PyKrita::INIT_CANNOT_LOAD_PYTHON_LIBRARY:
qWarning() << i18n("Cannot load Python library");
return 1;
case PyKrita::INIT_CANNOT_SET_PYTHON_PATHS:
qWarning() << i18n("Cannot set Python paths");
return 1;
case PyKrita::INIT_CANNOT_LOAD_PYKRITA_MODULE:
qWarning() << i18n("Cannot load built-in pykrita module");
return 1;
default:
qWarning() << i18n("Unexpected error initializing python plugin.");
return 1;
}
qDebug() << "Try to import the pykrita module";
PyKrita::Python py = PyKrita::Python();
PyObject* pykritaPackage = py.moduleImport("pykrita");
pykritaPackage = py.moduleImport("krita");
if (!pykritaPackage) {
qDebug("Cannot load the PyKrita module, aborting");
return 1;
}
PyObject *argsList = PyList_New(0);
Q_FOREACH(const QString arg, parser.positionalArguments()) {
PyObject* const u = py.unicode(arg);
PyList_Append(argsList, u);
Py_DECREF(u);
}
PyObject *args = PyTuple_New(1);
PyTuple_SetItem(args, 0, argsList);
py.functionCall(parser.value(functionOption).toUtf8().constData(), parser.value(scriptOption).toUtf8().constData(), args);
Py_DECREF(argsList);
Py_DECREF(args);
app.quit();
return 0;
}
diff --git a/plugins/extensions/pykrita/sip/krita/PresetChooser.sip b/plugins/extensions/pykrita/sip/krita/PresetChooser.sip
index c53c004aa9..cf1ebec034 100644
--- a/plugins/extensions/pykrita/sip/krita/PresetChooser.sip
+++ b/plugins/extensions/pykrita/sip/krita/PresetChooser.sip
@@ -1,20 +1,20 @@
class PresetChooser : public QWidget /NoDefaultCtors/
{
%TypeHeaderCode
#include "PresetChooser.h"
%End
public:
PresetChooser(QWidget *parent = 0);
public Q_SLOTS:
void setCurrentPreset(Resource *resource);
Resource *currentPreset() const /Factory/;
Q_SIGNALS:
- void presetSelected(Resource *resource) /Factory/;
- void presetClicked(Resource *resource) /Factory/;
+ void presetSelected(Resource resource) /Factory/;
+ void presetClicked(Resource resource) /Factory/;
};
diff --git a/plugins/extensions/pykrita/sip/krita/Resource.sip b/plugins/extensions/pykrita/sip/krita/Resource.sip
index 1b2d641553..360516e640 100644
--- a/plugins/extensions/pykrita/sip/krita/Resource.sip
+++ b/plugins/extensions/pykrita/sip/krita/Resource.sip
@@ -1,24 +1,22 @@
class Resource : QObject
{
%TypeHeaderCode
#include "Resource.h"
%End
Resource(const Resource & __0);
public:
bool operator==(const Resource &other) const;
bool operator!=(const Resource &other) const;
public:
virtual ~Resource();
public Q_SLOTS:
QString type() const;
QString name() const;
void setName(QString value);
QString filename() const;
QImage image() const;
void setImage(QImage image);
- QByteArray data() const;
- bool setData(QByteArray data);
public Q_SLOTS:
Q_SIGNALS:
private:
};
diff --git a/plugins/extensions/resourcemanager/CMakeLists.txt b/plugins/extensions/resourcemanager/CMakeLists.txt
index fc446a64ff..5ef049c81a 100644
--- a/plugins/extensions/resourcemanager/CMakeLists.txt
+++ b/plugins/extensions/resourcemanager/CMakeLists.txt
@@ -1,15 +1,17 @@
set(kritaresourcemanager_SOURCES
resourcemanager.cpp
dlg_create_bundle.cpp
dlg_bundle_manager.cpp
+ dlg_embed_tags.cpp
)
ki18n_wrap_ui(kritaresourcemanager_SOURCES
wdgdlgcreatebundle.ui
wdgdlgbundlemanager.ui
+ wdgdlgembedtags.ui
)
add_library(kritaresourcemanager MODULE ${kritaresourcemanager_SOURCES})
-target_link_libraries(kritaresourcemanager kritawidgets kritaui kritalibpaintop)
+target_link_libraries(kritaresourcemanager kritawidgets kritaui kritalibpaintop kritaresources kritaresourcewidgets)
install(TARGETS kritaresourcemanager DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install(FILES resourcemanager.xmlgui DESTINATION ${DATA_INSTALL_DIR}/kritaplugins)
diff --git a/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp b/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp
index bdf4ddfca0..6a664797a5 100644
--- a/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp
+++ b/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp
@@ -1,433 +1,200 @@
/*
* 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.
*/
#include "dlg_bundle_manager.h"
#include "ui_wdgdlgbundlemanager.h"
#include "resourcemanager.h"
#include "dlg_create_bundle.h"
#include <QListWidget>
#include <QTreeWidget>
#include <QListWidgetItem>
#include <QPainter>
#include <QPixmap>
#include <QMessageBox>
+#include <QInputDialog>
+#include <QItemSelectionModel>
+
+
+#include <kconfiggroup.h>
+#include <ksharedconfig.h>
+#include <KoIcon.h>
+#include <KoFileDialog.h>
#include <kis_icon.h>
#include "kis_action.h"
+#include <KisResourceStorage.h>
#include <KisResourceServerProvider.h>
-#include <kconfiggroup.h>
-#include <ksharedconfig.h>
+#include <KisStorageModel.h>
+#include <KisStorageFilterProxyModel.h>
+#include <kis_config.h>
+#include <KisResourceLocator.h>
-#define ICON_SIZE 48
-DlgBundleManager::DlgBundleManager(ResourceManager *resourceManager, KisActionManager* actionMgr, QWidget *parent)
+DlgBundleManager::DlgBundleManager(QWidget *parent)
: KoDialog(parent)
, m_page(new QWidget())
, m_ui(new Ui::WdgDlgBundleManager)
- , m_currentBundle(0)
- , m_resourceManager(resourceManager)
{
- setCaption(i18n("Manage Resource Bundles"));
+ setCaption(i18n("Manage Resource Libraries"));
m_ui->setupUi(m_page);
setMainWidget(m_page);
resize(m_page->sizeHint());
- setButtons(Ok | Cancel);
- setDefaultButton(Ok);
-
- m_ui->listActive->setIconSize(QSize(ICON_SIZE, ICON_SIZE));
- m_ui->listActive->setSelectionMode(QAbstractItemView::SingleSelection);
- connect(m_ui->listActive, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*,QListWidgetItem*)));
- connect(m_ui->listActive, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*)));
- m_ui->listInactive->setIconSize(QSize(ICON_SIZE, ICON_SIZE));
- m_ui->listInactive->setSelectionMode(QAbstractItemView::SingleSelection);
- connect(m_ui->listInactive, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*,QListWidgetItem*)));
- connect(m_ui->listInactive, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*)));
+ m_ui->bnAdd->setIcon(KisIconUtils::loadIcon("list-add"));
+ m_ui->bnAdd->setText(i18n("Import"));
+ connect(m_ui->bnAdd, SIGNAL(clicked(bool)), SLOT(addBundle()));
- m_ui->bnAdd->setIcon(KisIconUtils::loadIcon("arrow-right"));
- connect(m_ui->bnAdd, SIGNAL(clicked()), SLOT(addSelected()));
+ m_ui->bnNew->setIcon(KisIconUtils::loadIcon("document-new"));
+ m_ui->bnNew->setText(i18n("Create"));
+ connect(m_ui->bnNew, SIGNAL(clicked(bool)), SLOT(createBundle()));
- m_ui->bnRemove->setIcon(KisIconUtils::loadIcon("arrow-left"));
- connect(m_ui->bnRemove, SIGNAL(clicked()), SLOT(removeSelected()));
+ m_ui->bnDelete->setIcon(KisIconUtils::loadIcon("edit-delete"));
+ m_ui->bnDelete->setText(i18n("Delete"));
+ connect(m_ui->bnDelete, SIGNAL(clicked(bool)), SLOT(deleteBundle()));
- m_ui->listBundleContents->setHeaderLabel(i18n("Resource"));
- m_ui->listBundleContents->setSelectionMode(QAbstractItemView::NoSelection);
+ setButtons(Close);
- m_actionManager = actionMgr;
+ m_proxyModel = new KisStorageFilterProxyModel(this);
+ m_proxyModel->setSourceModel(KisStorageModel::instance());
+ m_proxyModel->setFilter(KisStorageFilterProxyModel::ByStorageType,
+ QStringList()
+ << KisResourceStorage::storageTypeToUntranslatedString(KisResourceStorage::StorageType::Bundle)
+ << KisResourceStorage::storageTypeToUntranslatedString(KisResourceStorage::StorageType::Folder));
+ m_ui->tableView->setModel(m_proxyModel);
- refreshListData();
+ m_ui->tableView->setColumnHidden(KisStorageModel::PreInstalled, true);
+ m_ui->tableView->setColumnHidden(KisStorageModel::Id, true);
+ m_ui->tableView->setColumnHidden(KisStorageModel::TimeStamp, true);
- connect(m_ui->bnEditBundle, SIGNAL(clicked()), SLOT(editBundle()));
+ QItemSelectionModel* selectionModel = m_ui->tableView->selectionModel();
+ connect(selectionModel, &QItemSelectionModel::currentChanged, this, &DlgBundleManager::currentCellSelectedChanged);
- connect(m_ui->bnImportBrushes, SIGNAL(clicked()), SLOT(slotImportResource()));
- connect(m_ui->bnImportGradients, SIGNAL(clicked()), SLOT(slotImportResource()));
- connect(m_ui->bnImportPalettes, SIGNAL(clicked()), SLOT(slotImportResource()));
- connect(m_ui->bnImportPatterns, SIGNAL(clicked()), SLOT(slotImportResource()));
- connect(m_ui->bnImportPresets, SIGNAL(clicked()), SLOT(slotImportResource()));
- connect(m_ui->bnImportWorkspaces, SIGNAL(clicked()), SLOT(slotImportResource()));
- connect(m_ui->bnImportBundles, SIGNAL(clicked()), SLOT(slotImportResource()));
+ connect(KisStorageModel::instance(), &KisStorageModel::modelAboutToBeReset, this, &DlgBundleManager::slotModelAboutToBeReset);
+ connect(KisStorageModel::instance(), &KisStorageModel::modelReset, this, &DlgBundleManager::slotModelReset);
- connect(m_ui->createBundleButton, SIGNAL(clicked()), SLOT(slotCreateBundle()));
- connect(m_ui->deleteBackupFilesButton, SIGNAL(clicked()), SLOT(slotDeleteBackupFiles()));
- connect(m_ui->openResourceFolderButton, SIGNAL(clicked()), SLOT(slotOpenResourceFolder()));
}
-
-void DlgBundleManager::refreshListData()
+void DlgBundleManager::addBundle()
{
- KoResourceServer<KisResourceBundle> *bundleServer = KisResourceBundleServerProvider::instance()->resourceBundleServer();
-
- m_ui->listInactive->clear();
- m_ui->listActive->clear();
-
- Q_FOREACH (const QString &f, bundleServer->blackListedFiles()) {
- KisResourceBundle *bundle = new KisResourceBundle(f);
- bundle->load();
- if (bundle->valid()) {
- bundle->setInstalled(false);
- m_blacklistedBundles[f] = bundle;
- }
+ KoFileDialog* dlg = new KoFileDialog(this, KoFileDialog::OpenFile, i18n("Choose the bundle to import"));
+ dlg->setCaption(i18n("Select the bundle"));
+ QString filename = dlg->filename();
+ if (!filename.isEmpty()) {
+ addBundleToActiveResources(filename);
}
- fillListWidget(m_blacklistedBundles.values(), m_ui->listInactive);
-
- Q_FOREACH (KisResourceBundle *bundle, bundleServer->resources()) {
- if (bundle->valid()) {
- m_activeBundles[bundle->filename()] = bundle;
- }
- }
- fillListWidget(m_activeBundles.values(), m_ui->listActive);
}
-void DlgBundleManager::accept()
+void DlgBundleManager::createBundle()
{
- KoResourceServer<KisResourceBundle> *bundleServer = KisResourceBundleServerProvider::instance()->resourceBundleServer();
-
- for (int i = 0; i < m_ui->listActive->count(); ++i) {
- QListWidgetItem *item = m_ui->listActive->item(i);
- QByteArray ba = item->data(Qt::UserRole).toByteArray();
- QString name = item->text();
- KisResourceBundle *bundle = bundleServer->resourceByMD5(ba);
- QMessageBox bundleFeedback;
- bundleFeedback.setIcon(QMessageBox::Warning);
- QString feedback = "bundlefeedback";
-
- if (!bundle) {
- // Get it from the blacklisted bundles
- Q_FOREACH (KisResourceBundle *b2, m_blacklistedBundles.values()) {
- if (b2->md5() == ba) {
- bundle = b2;
- break;
- }
- }
- }
-
- if (bundle) {
- bool isKrita3Bundle = false;
- if (bundle->filename().endsWith("Krita_3_Default_Resources.bundle")) {
- isKrita3Bundle = true;
- KConfigGroup group = KSharedConfig::openConfig()->group("BundleHack");
- group.writeEntry("HideKrita3Bundle", false);
- }
- else {
- if (!bundle->isInstalled()) {
- bundle->install();
- //this removes the bundle from the blacklist and add it to the server without saving or putting it in front//
- if (!bundleServer->addResource(bundle, false, false)){
-
- feedback = i18n("Couldn't add bundle \"%1\" to resource server", name);
- bundleFeedback.setText(feedback);
- bundleFeedback.exec();
- }
- if (!isKrita3Bundle) {
- if (!bundleServer->removeFromBlacklist(bundle)) {
- feedback = i18n("Couldn't remove bundle \"%1\" from blacklist", name);
- bundleFeedback.setText(feedback);
- bundleFeedback.exec();
- }
- }
- }
- else {
- if (!isKrita3Bundle) {
- bundleServer->removeFromBlacklist(bundle);
- }
- //let's assume that bundles that exist and are installed have to be removed from the blacklist, and if they were already this returns false, so that's not a problem.
- }
- }
- }
- else{
- QString feedback = i18n("Bundle \"%1\" doesn't exist!", name);
- bundleFeedback.setText(feedback);
- bundleFeedback.exec();
-
- }
- }
-
- for (int i = 0; i < m_ui->listInactive->count(); ++i) {
- QListWidgetItem *item = m_ui->listInactive->item(i);
- QByteArray ba = item->data(Qt::UserRole).toByteArray();
- KisResourceBundle *bundle = bundleServer->resourceByMD5(ba);
- bool isKrits3Bundle = false;
- if (bundle) {
- if (bundle->filename().contains("Krita_3_Default_Resources.bundle")) {
- isKrits3Bundle = true;
- KConfigGroup group = KSharedConfig::openConfig()->group("BundleHack");
- group.writeEntry("HideKrita3Bundle", true);
- }
- if (bundle->isInstalled()) {
- bundle->uninstall();
- if (!isKrits3Bundle) {
- bundleServer->removeResourceAndBlacklist(bundle);
- }
- }
- }
- }
-
-
- KoDialog::accept();
+ DlgCreateBundle* dlg = new DlgCreateBundle(0, this);
+ dlg->exec();
}
-void DlgBundleManager::addSelected()
+void DlgBundleManager::deleteBundle()
{
- Q_FOREACH (QListWidgetItem *item, m_ui->listActive->selectedItems()) {
- m_ui->listInactive->addItem(m_ui->listActive->takeItem(m_ui->listActive->row(item)));
+ QModelIndex idx = m_ui->tableView->currentIndex();
+ KIS_ASSERT(m_proxyModel);
+ if (!idx.isValid()) {
+ ENTER_FUNCTION() << "Index is invalid\n";
+ return;
}
-
+ bool active = m_proxyModel->data(idx, Qt::UserRole + KisStorageModel::Active).toBool();
+ idx = m_proxyModel->index(idx.row(), 0);
+ m_proxyModel->setData(idx, QVariant(!active), Qt::CheckStateRole);
}
-void DlgBundleManager::removeSelected()
+void DlgBundleManager::slotModelAboutToBeReset()
{
- Q_FOREACH (QListWidgetItem *item, m_ui->listInactive->selectedItems()) {
- m_ui->listActive->addItem(m_ui->listInactive->takeItem(m_ui->listInactive->row(item)));
- }
+ ENTER_FUNCTION();
+ lastIndex = QPersistentModelIndex(m_proxyModel->mapToSource(m_ui->tableView->currentIndex()));
+ ENTER_FUNCTION() << ppVar(lastIndex) << ppVar(lastIndex.isValid());
}
-void DlgBundleManager::itemSelected(QListWidgetItem *current, QListWidgetItem *)
+void DlgBundleManager::slotModelReset()
{
- if (!current) {
- m_ui->lblName->clear();
- m_ui->lblAuthor->clear();
- m_ui->lblEmail->clear();
- m_ui->lblLicense->clear();
- m_ui->lblWebsite->clear();
- m_ui->lblDescription->clear();
- m_ui->lblCreated->clear();
- m_ui->lblUpdated->clear();
- m_ui->lblPreview->setPixmap(QPixmap::fromImage(QImage()));
- m_ui->listBundleContents->clear();
- m_ui->bnEditBundle->setEnabled(false);
- m_currentBundle = 0;
- }
- else {
-
- QByteArray ba = current->data(Qt::UserRole).toByteArray();
- KoResourceServer<KisResourceBundle> *bundleServer = KisResourceBundleServerProvider::instance()->resourceBundleServer();
- KisResourceBundle *bundle = bundleServer->resourceByMD5(ba);
-
- if (!bundle) {
- // Get it from the blacklisted bundles
- Q_FOREACH (KisResourceBundle *b2, m_blacklistedBundles.values()) {
- if (b2->md5() == ba) {
- bundle = b2;
- break;
- }
- }
- }
-
-
- if (bundle) {
- QFontMetrics metrics(this->font());
-
- m_currentBundle = bundle;
- m_ui->bnEditBundle->setEnabled(true);
-
- m_ui->lblName->setText(bundle->name());
- m_ui->lblAuthor->setText(metrics.elidedText(bundle->getMeta("author"), Qt::ElideRight, m_ui->lblAuthor->width()));
- m_ui->lblAuthor->setToolTip(bundle->getMeta("author"));
- m_ui->lblEmail->setText(metrics.elidedText(bundle->getMeta("email"), Qt::ElideRight, m_ui->lblEmail->width()));
- m_ui->lblEmail->setToolTip(bundle->getMeta("email"));
- m_ui->lblLicense->setText(metrics.elidedText(bundle->getMeta("license"), Qt::ElideRight, m_ui->lblLicense->width()));
- m_ui->lblLicense->setToolTip(bundle->getMeta("license"));
- m_ui->lblWebsite->setText(metrics.elidedText(bundle->getMeta("website"), Qt::ElideRight, m_ui->lblWebsite->width()));
- m_ui->lblWebsite->setToolTip(bundle->getMeta("website"));
- m_ui->lblDescription->setPlainText(bundle->getMeta("description"));
- if (QDateTime::fromString(bundle->getMeta("created"), Qt::ISODate).isValid()) {
- m_ui->lblCreated->setText(QDateTime::fromString(bundle->getMeta("created"), Qt::ISODate).toLocalTime().toString(Qt::DefaultLocaleShortDate));
- } else {
- m_ui->lblCreated->setText(QDate::fromString(bundle->getMeta("created"), "dd/MM/yyyy").toString(Qt::DefaultLocaleShortDate));
- }
- if (QDateTime::fromString(bundle->getMeta("updated"), Qt::ISODate).isValid()) {
- m_ui->lblUpdated->setText(QDateTime::fromString(bundle->getMeta("updated"), Qt::ISODate).toLocalTime().toString(Qt::DefaultLocaleShortDate));
- } else {
- m_ui->lblUpdated->setText(QDate::fromString(bundle->getMeta("updated"), "dd/MM/yyyy").toString(Qt::DefaultLocaleShortDate));
- }
- m_ui->lblPreview->setPixmap(QPixmap::fromImage(bundle->image().scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
- m_ui->listBundleContents->clear();
-
- Q_FOREACH (const QString & resType, bundle->resourceTypes()) {
-
- QTreeWidgetItem *toplevel = new QTreeWidgetItem();
- if (resType == "gradients") {
- toplevel->setText(0, i18n("Gradients"));
- }
- else if (resType == "patterns") {
- toplevel->setText(0, i18n("Patterns"));
- }
- else if (resType == "brushes") {
- toplevel->setText(0, i18n("Brushes"));
- }
- else if (resType == "palettes") {
- toplevel->setText(0, i18n("Palettes"));
- }
- else if (resType == "workspaces") {
- toplevel->setText(0, i18n("Workspaces"));
- }
- else if (resType == "paintoppresets") {
- toplevel->setText(0, i18n("Brush Presets"));
- }
- else if (resType == "gamutmasks") {
- toplevel->setText(0, i18n("Gamut Masks"));
- }
-
-
- m_ui->listBundleContents->addTopLevelItem(toplevel);
-
- Q_FOREACH (const KoResource *res, bundle->resources(resType)) {
- if (res) {
- QTreeWidgetItem *i = new QTreeWidgetItem();
- i->setIcon(0, QIcon(QPixmap::fromImage(res->image())));
- i->setText(0, res->name());
- toplevel->addChild(i);
- }
- }
- }
- }
- else {
- m_currentBundle = 0;
- }
+ ENTER_FUNCTION();
+ ENTER_FUNCTION() << ppVar(lastIndex) << ppVar(lastIndex.isValid());
+ if (lastIndex.isValid()) {
+ ENTER_FUNCTION() << "last index valid!";
+ m_ui->tableView->setCurrentIndex(m_proxyModel->mapToSource(lastIndex));
}
+ lastIndex = QModelIndex();
}
-void DlgBundleManager::itemSelected(QListWidgetItem *current)
-{
- itemSelected(current, 0);
-}
-
-void DlgBundleManager::editBundle()
+void DlgBundleManager::currentCellSelectedChanged(QModelIndex current, QModelIndex previous)
{
- if (m_currentBundle) {
- DlgCreateBundle dlg(m_currentBundle);
- m_activeBundles.remove(m_currentBundle->filename());
- m_currentBundle = 0;
- if (dlg.exec() != QDialog::Accepted) {
- return;
- }
- m_currentBundle = m_resourceManager->saveBundle(dlg);
- refreshListData();
+ ENTER_FUNCTION() << "Current cell changed!";
+ QModelIndex idx = m_ui->tableView->currentIndex();
+ KIS_ASSERT(m_proxyModel);
+ if (!idx.isValid()) {
+ ENTER_FUNCTION() << "Index is invalid\n";
+ return;
}
-}
-
-void DlgBundleManager::fillListWidget(QList<KisResourceBundle *> bundles, QListWidget *w)
-{
- w->setIconSize(QSize(ICON_SIZE, ICON_SIZE));
- w->setSelectionMode(QAbstractItemView::MultiSelection);
+ bool active = m_proxyModel->data(idx, Qt::UserRole + KisStorageModel::Active).toBool();
- Q_FOREACH (KisResourceBundle *bundle, bundles) {
- QPixmap pixmap(ICON_SIZE, ICON_SIZE);
- pixmap.fill(Qt::gray);
- if (!bundle->image().isNull()) {
- QImage scaled = bundle->image().scaled(ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation);
- int x = (ICON_SIZE - scaled.width()) / 2;
- int y = (ICON_SIZE - scaled.height()) / 2;
- QPainter gc(&pixmap);
- gc.drawImage(x, y, scaled);
- gc.end();
- }
-
- QListWidgetItem *item = new QListWidgetItem(pixmap, bundle->name());
- item->setData(Qt::UserRole, bundle->md5());
- w->addItem(item);
+ if (active) {
+ m_ui->bnDelete->setText(i18n("Deactivate"));
+ } else {
+ m_ui->bnDelete->setText(i18n("Activate"));
}
}
-
-void DlgBundleManager::slotImportResource()
+QString createNewBundlePath(QString resourceFolder, QString filename)
{
- if (m_actionManager) {
- QObject *button = sender();
- QString buttonName = button->objectName();
- KisAction *action = 0;
- if (buttonName == "bnImportBundles") {
- action = m_actionManager->actionByName("import_bundles");
- }
- else if (buttonName == "bnImportBrushes") {
- action = m_actionManager->actionByName("import_brushes");
- }
- else if (buttonName == "bnImportGradients") {
- action = m_actionManager->actionByName("import_gradients");
- }
- else if (buttonName == "bnImportPalettes") {
- action = m_actionManager->actionByName("import_palettes");
- }
- else if (buttonName == "bnImportPatterns") {
- action = m_actionManager->actionByName("import_patterns");
- }
- else if (buttonName == "bnImportPresets") {
- action = m_actionManager->actionByName("import_presets");
- }
- else if (buttonName == "bnImportWorkspaces") {
- action = m_actionManager->actionByName("import_workspaces");
- }
- else {
- warnUI << "Unhandled bundle manager import button " << buttonName;
- return;
- }
-
- action->trigger();
- refreshListData();
- }
-}
-
-void DlgBundleManager::slotCreateBundle() {
-
- if (m_actionManager) {
- KisAction *action = m_actionManager->actionByName("create_bundle");
- action->trigger();
- refreshListData();
- }
+ return resourceFolder + QDir::separator() + "bundles" + QDir::separator() + filename;
}
-void DlgBundleManager::slotDeleteBackupFiles() {
-
- if (m_actionManager) {
- KisAction *action = m_actionManager->actionByName("edit_blacklist_cleanup");
- action->trigger();
+void DlgBundleManager::addBundleToActiveResources(QString filename)
+{
+ warnKrita << "DlgBundleManager::addBundle(): Loading a bundle is not implemented yet.";
+ Q_UNUSED(filename);
+ // 1. Copy the bundle to the resource folder
+ // 2. Add the bundle as a storage/update database
+ QFileInfo oldFileInfo(filename);
+
+ KisConfig cfg(true);
+ QString newDir = cfg.readEntry<QString>(KisResourceLocator::resourceLocationKey,
+ QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+ QString newName = oldFileInfo.fileName();
+ QString newLocation = createNewBundlePath(newDir, newName);
+
+ QFileInfo newFileInfo(newLocation);
+ if (newFileInfo.exists()) {
+ bool done = false;
+ int i = 0;
+ do {
+ // ask for new filename
+ bool ok;
+ newName = QInputDialog::getText(this, i18n("New name for the bundle"), i18n("The old filename %s is taken.\nNew name:", newName),
+ QLineEdit::Normal, newName, &ok);
+ newLocation = createNewBundlePath(newDir, newName);
+ newFileInfo.setFile(newLocation);
+ done = !newFileInfo.exists();
+ i++;
+ } while (!done);
}
-}
-void DlgBundleManager::slotOpenResourceFolder() {
-
- if (m_actionManager) {
- KisAction *action = m_actionManager->actionByName("open_resources_directory");
- action->trigger();
- }
+ QFile::copy(filename, newLocation);
+ KisResourceStorageSP storage = QSharedPointer<KisResourceStorage>::create(newLocation);
+ KIS_ASSERT(!storage.isNull());
+ KisResourceLocator::instance()->addStorage(newLocation, storage);
}
-
diff --git a/plugins/extensions/resourcemanager/dlg_bundle_manager.h b/plugins/extensions/resourcemanager/dlg_bundle_manager.h
index ba8c72cf08..99c9e83666 100644
--- a/plugins/extensions/resourcemanager/dlg_bundle_manager.h
+++ b/plugins/extensions/resourcemanager/dlg_bundle_manager.h
@@ -1,72 +1,63 @@
/*
* 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 DLG_BUNDLE_MANAGER_H
#define DLG_BUNDLE_MANAGER_H
#include <QWidget>
-
#include <KoDialog.h>
-#include "kis_action_manager.h"
-#include "resourcemanager.h"
+#include <QModelIndex>
+#include <QPersistentModelIndex>
-class KisResourceBundle;
-class QListWidget;
-class QListWidgetItem;
+class KisStorageModel;
+class KisStorageFilterProxyModel;
namespace Ui
{
class WdgDlgBundleManager;
}
class DlgBundleManager : public KoDialog
{
Q_OBJECT
public:
- explicit DlgBundleManager(ResourceManager *resourceManager, KisActionManager* actionMgr, QWidget *parent = 0);
+ explicit DlgBundleManager(QWidget *parent = 0);
private Q_SLOTS:
- void accept() override;
- void addSelected();
- void removeSelected();
- void itemSelected(QListWidgetItem *current, QListWidgetItem *previous);
- void itemSelected(QListWidgetItem *current);
- void editBundle();
- void slotImportResource();
- void slotCreateBundle();
- void slotDeleteBackupFiles();
- void slotOpenResourceFolder();
+ void addBundle();
+ void createBundle();
+ void deleteBundle();
+
+ void slotModelAboutToBeReset();
+ void slotModelReset();
+ void currentCellSelectedChanged(QModelIndex current, QModelIndex previous);
+
private:
+ void addBundleToActiveResources(QString filename);
+
QWidget *m_page;
Ui::WdgDlgBundleManager *m_ui;
+ QPersistentModelIndex lastIndex;
+ KisStorageFilterProxyModel* m_proxyModel;
- void fillListWidget(QList<KisResourceBundle*> bundles, QListWidget *w);
- void refreshListData();
-
- QMap<QString, KisResourceBundle*> m_blacklistedBundles;
- QMap<QString, KisResourceBundle*> m_activeBundles;
-
- KisResourceBundle *m_currentBundle;
- KisActionManager *m_actionManager;
- ResourceManager *m_resourceManager;
};
#endif // DLG_BUNDLE_MANAGER_H
diff --git a/plugins/extensions/resourcemanager/dlg_create_bundle.cpp b/plugins/extensions/resourcemanager/dlg_create_bundle.cpp
index 7e998437e9..3cf4ad6477 100644
--- a/plugins/extensions/resourcemanager/dlg_create_bundle.cpp
+++ b/plugins/extensions/resourcemanager/dlg_create_bundle.cpp
@@ -1,467 +1,453 @@
/*
* Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr
+ * Copyright (c) 2020 Agata Cacko cacko.azh@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 "dlg_create_bundle.h"
#include "ui_wdgdlgcreatebundle.h"
#include <QProcessEnvironment>
#include <QFileInfo>
#include <QMessageBox>
#include <QStandardPaths>
#include <QGridLayout>
#include <QTableWidget>
#include <QPainter>
+#include <QStack>
#include <KisImportExportManager.h>
#include <KoDocumentInfo.h>
#include <KoFileDialog.h>
#include <kis_icon.h>
-#include <resources/KoResource.h>
+#include <KoResource.h>
#include <KoResourceServer.h>
#include <KoResourceServerProvider.h>
+#include <KoResource.h>
-#include <KisResourceServerProvider.h>
#include <kis_workspace_resource.h>
#include <brushengine/kis_paintop_preset.h>
-#include <kis_brush_server.h>
+#include <dlg_embed_tags.h>
+#include <KisGlobalResourcesInterface.h>
#include <kis_config.h>
-#include "KisResourceBundle.h"
-
#define ICON_SIZE 48
-DlgCreateBundle::DlgCreateBundle(KisResourceBundle *bundle, QWidget *parent)
+DlgCreateBundle::DlgCreateBundle(KoResourceBundleSP bundle, QWidget *parent)
: KoDialog(parent)
, m_ui(new Ui::WdgDlgCreateBundle)
, m_bundle(bundle)
{
m_page = new QWidget();
m_ui->setupUi(m_page);
setMainWidget(m_page);
setFixedSize(m_page->sizeHint());
setButtons(Ok | Cancel);
setDefaultButton(Ok);
setButtonText(Ok, i18n("Save"));
connect(m_ui->bnSelectSaveLocation, SIGNAL(clicked()), SLOT(selectSaveLocation()));
KoDocumentInfo info;
info.updateParameters();
if (bundle) {
setCaption(i18n("Edit Resource Bundle"));
-
+#if 0
+ /*
m_ui->lblSaveLocation->setText(QFileInfo(bundle->filename()).absolutePath());
m_ui->editBundleName->setText(bundle->name());
m_ui->editAuthor->setText(bundle->getMeta("author"));
m_ui->editEmail->setText(bundle->getMeta("email"));
m_ui->editLicense->setText(bundle->getMeta("license"));
m_ui->editWebsite->setText(bundle->getMeta("website"));
m_ui->editDescription->document()->setPlainText(bundle->getMeta("description"));
m_ui->lblPreview->setPixmap(QPixmap::fromImage(bundle->image().scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
Q_FOREACH (const QString & resType, bundle->resourceTypes()) {
- if (resType == "gradients") {
- Q_FOREACH (const KoResource *res, bundle->resources(resType)) {
+ if (resType == ResourceType::Gradients) {
+ Q_FOREACH (const KoResourceSP res, bundle->resources(resType)) {
if (res) {
- m_selectedGradients << res->shortFilename();
+ m_selectedGradients << res->filename();
}
}
}
- else if (resType == "patterns") {
- Q_FOREACH (const KoResource *res, bundle->resources(resType)) {
+ else if (resType == ResourceType::Patterns) {
+ Q_FOREACH (const KoResourceSP res, bundle->resources(resType)) {
if (res) {
- m_selectedPatterns << res->shortFilename();
+ m_selectedPatterns << res->filename();
}
}
}
- else if (resType == "brushes") {
- Q_FOREACH (const KoResource *res, bundle->resources(resType)) {
+ else if (resType == ResourceType::Brushes) {
+ Q_FOREACH (const KoResourceSP res, bundle->resources(resType)) {
if (res) {
- m_selectedBrushes << res->shortFilename();
+ m_selectedBrushes << res->filename();
}
}
}
- else if (resType == "palettes") {
- Q_FOREACH (const KoResource *res, bundle->resources(resType)) {
+ else if (resType == ResourceType::Palettes) {
+ Q_FOREACH (const KoResourceSP res, bundle->resources(resType)) {
if (res) {
- m_selectedPalettes << res->shortFilename();
+ m_selectedPalettes << res->filename();
}
}
}
- else if (resType == "workspaces") {
- Q_FOREACH (const KoResource *res, bundle->resources(resType)) {
+ else if (resType == ResourceType::Workspaces) {
+ Q_FOREACH (const KoResourceSP res, bundle->resources(resType)) {
if (res) {
- m_selectedWorkspaces << res->shortFilename();
+ m_selectedWorkspaces << res->filename();
}
}
}
- else if (resType == "paintoppresets") {
- Q_FOREACH (const KoResource *res, bundle->resources(resType)) {
+ else if (resType == ResourceType::PaintOpPresets) {
+ Q_FOREACH (const KoResourceSP res, bundle->resources(resType)) {
if (res) {
- m_selectedPresets << res->shortFilename();
+ m_selectedPresets << res->filename();
}
}
}
- else if (resType == "gamutmasks") {
- Q_FOREACH (const KoResource *res, bundle->resources(resType)) {
+ else if (resType == ResourceType::GamutMasks) {
+ Q_FOREACH (const KoResourceSP res, bundle->resources(resType)) {
if (res) {
- m_selectedGamutMasks << res->shortFilename();
+ m_selectedGamutMasks << res->filename();
}
}
}
}
+ */
+#endif
}
else {
setCaption(i18n("Create Resource Bundle"));
KisConfig cfg(true);
m_ui->editAuthor->setText(cfg.readEntry<QString>("BundleAuthorName", info.authorInfo("creator")));
m_ui->editEmail->setText(cfg.readEntry<QString>("BundleAuthorEmail", info.authorInfo("email")));
m_ui->editWebsite->setText(cfg.readEntry<QString>("BundleWebsite", "http://"));
m_ui->editLicense->setText(cfg.readEntry<QString>("BundleLicense", "CC-BY-SA"));
+ m_ui->editBundleName->setText(cfg.readEntry<QString>("BundleName", "New Bundle"));
+ m_ui->editDescription->document()->setPlainText(cfg.readEntry<QString>("BundleDescription", "New Bundle"));
+ m_previewImage = cfg.readEntry<QString>("BundleImage", "");
+ if (!m_previewImage.isEmpty()) {
+ QImage img(m_previewImage);
+ img = img.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ m_ui->lblPreview->setPixmap(QPixmap::fromImage(img));
+ }
+
m_ui->lblSaveLocation->setText(cfg.readEntry<QString>("BundleExportLocation", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)));
}
m_ui->bnAdd->setIcon(KisIconUtils::loadIcon("arrow-right"));
connect(m_ui->bnAdd, SIGNAL(clicked()), SLOT(addSelected()));
m_ui->bnRemove->setIcon(KisIconUtils::loadIcon("arrow-left"));
connect(m_ui->bnRemove, SIGNAL(clicked()), SLOT(removeSelected()));
- m_ui->cmbResourceTypes->addItem(i18n("Brushes"), QString("brushes"));
- m_ui->cmbResourceTypes->addItem(i18n("Brush Presets"), QString("presets"));
- m_ui->cmbResourceTypes->addItem(i18n("Gradients"), QString("gradients"));
- m_ui->cmbResourceTypes->addItem(i18n("Gamut Masks"), QString("gamutmasks"));
- m_ui->cmbResourceTypes->addItem(i18n("Patterns"), QString("patterns"));
- m_ui->cmbResourceTypes->addItem(i18n("Palettes"), QString("palettes"));
- m_ui->cmbResourceTypes->addItem(i18n("Workspaces"), QString("workspaces"));
+ m_ui->cmbResourceTypes->addItem(i18n("Brushes"), ResourceType::Brushes);
+ m_ui->cmbResourceTypes->addItem(i18n("Brush Presets"), ResourceType::PaintOpPresets);
+ m_ui->cmbResourceTypes->addItem(i18n("Gradients"), ResourceType::Gradients);
+ m_ui->cmbResourceTypes->addItem(i18n("Gamut Masks"), ResourceType::GamutMasks);
+ m_ui->cmbResourceTypes->addItem(i18n("Patterns"), ResourceType::Patterns);
+ m_ui->cmbResourceTypes->addItem(i18n("Palettes"), ResourceType::Palettes);
+ m_ui->cmbResourceTypes->addItem(i18n("Workspaces"), ResourceType::Workspaces);
connect(m_ui->cmbResourceTypes, SIGNAL(activated(int)), SLOT(resourceTypeSelected(int)));
m_ui->tableAvailable->setIconSize(QSize(ICON_SIZE, ICON_SIZE));
m_ui->tableAvailable->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_ui->tableSelected->setIconSize(QSize(ICON_SIZE, ICON_SIZE));
m_ui->tableSelected->setSelectionMode(QAbstractItemView::ExtendedSelection);
connect(m_ui->bnGetPreview, SIGNAL(clicked()), SLOT(getPreviewImage()));
+ connect(m_ui->bnEmbedTags, SIGNAL(clicked()), SLOT(slotEmbedTags()));
resourceTypeSelected(0);
}
DlgCreateBundle::~DlgCreateBundle()
{
delete m_ui;
}
QString DlgCreateBundle::bundleName() const
{
return m_ui->editBundleName->text().replace(" ", "_");
}
QString DlgCreateBundle::authorName() const
{
return m_ui->editAuthor->text();
}
QString DlgCreateBundle::email() const
{
return m_ui->editEmail->text();
}
QString DlgCreateBundle::website() const
{
return m_ui->editWebsite->text();
}
QString DlgCreateBundle::license() const
{
return m_ui->editLicense->text();
}
QString DlgCreateBundle::description() const
{
return m_ui->editDescription->document()->toPlainText();
}
QString DlgCreateBundle::saveLocation() const
{
return m_ui->lblSaveLocation->text();
}
QString DlgCreateBundle::previewImage() const
{
return m_previewImage;
}
+QVector<KisTagSP> DlgCreateBundle::getTagsForEmbeddingInResource(QVector<KisTagSP> resourceTags) const
+{
+ QVector<KisTagSP> tagsToEmbed;
+ Q_FOREACH(KisTagSP tag, resourceTags) {
+ if (m_selectedTagIds.contains(tag->id())) {
+ tagsToEmbed << tag;
+ }
+ }
+ return tagsToEmbed;
+}
+
+void DlgCreateBundle::putResourcesInTheBundle() const
+{
+ KisResourceModel* emptyModel = KisResourceModelProvider::resourceModel("");
+ QStack<int> allResourcesIds;
+ Q_FOREACH(int id, m_selectedResourcesIds) {
+ allResourcesIds << id;
+ }
+
+ // note: if there are repetitions, it's fine; the bundle will filter them out
+ while(!allResourcesIds.isEmpty()) {
+ int id = allResourcesIds.takeFirst();
+ KoResourceSP res = emptyModel->resourceForId(id);
+ if (!res) {
+ warnKrita << "No resource for id " << id;
+ continue;
+ }
+ KisResourceModel* resModel = KisResourceModelProvider::resourceModel(res->resourceType().first);
+ QVector<KisTagSP> tags = getTagsForEmbeddingInResource(resModel->tagsForResource(id));
+ m_bundle->addResource(res->resourceType().first, res->filename(), tags, res->md5());
+
+ QList<KoResourceSP> linkedResources = res->linkedResources(KisGlobalResourcesInterface::instance());
+ if (!linkedResources.isEmpty()) {
+ Q_FOREACH(KoResourceSP resource, linkedResources) {
+ if (!allResourcesIds.contains(resource->resourceId())) {
+ allResourcesIds.append(resource->resourceId());
+ }
+ }
+ }
+ }
+}
+
void DlgCreateBundle::accept()
{
- QString name = m_ui->editBundleName->text().remove(" ");
+ QString name = bundleName();
+ QString filename = m_ui->lblSaveLocation->text() + "/" + name + ".bundle";
if (name.isEmpty()) {
m_ui->editBundleName->setStyleSheet(QString(" border: 1px solid red"));
QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The resource bundle name cannot be empty."));
return;
}
else {
- QFileInfo fileInfo(m_ui->lblSaveLocation->text() + "/" + name + ".bundle");
+ QFileInfo fileInfo(filename);
if (fileInfo.exists() && !m_bundle) {
m_ui->editBundleName->setStyleSheet("border: 1px solid red");
- QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("A bundle with this name already exists."));
- return;
- }
- else {
- if (!m_bundle) {
- KisConfig cfg(false);
- cfg.writeEntry<QString>("BunleExportLocation", m_ui->lblSaveLocation->text());
- cfg.writeEntry<QString>("BundleAuthorName", m_ui->editAuthor->text());
- cfg.writeEntry<QString>("BundleAuthorEmail", m_ui->editEmail->text());
- cfg.writeEntry<QString>("BundleWebsite", m_ui->editWebsite->text());
- cfg.writeEntry<QString>("BundleLicense", m_ui->editLicense->text());
+
+ QMessageBox msgBox;
+ msgBox.setText(i18nc("In a dialog asking whether to overwrite a bundle (resource pack)", "A bundle with this name already exists."));
+ msgBox.setInformativeText(i18nc("In a dialog regarding overwriting a bundle (resource pack)", "Do you want to overwrite the existing bundle?"));
+ msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
+ msgBox.setDefaultButton(QMessageBox::Cancel);
+ int ret = msgBox.exec();
+ if (ret == QMessageBox::Cancel) {
+ return;
}
- KoDialog::accept();
}
+
+ if (!m_bundle) {
+ saveToConfiguration(false);
+
+ m_bundle.reset(new KoResourceBundle(filename));
+ putResourcesInTheBundle();
+ m_bundle->save();
+
+ } else {
+ KIS_SAFE_ASSERT_RECOVER(!m_bundle) { warnKrita << "Updating a bundle is not implemented yet"; };
+ }
+ KoDialog::accept();
+ }
+}
+
+void DlgCreateBundle::saveToConfiguration(bool full)
+{
+ KisConfig cfg(false);
+ if (full) {
+ cfg.writeEntry<QString>("BundleName", bundleName());
+ cfg.writeEntry<QString>("BundleDescription", description());
+ cfg.writeEntry<QString>("BundleImage", previewImage());
+ } else {
+ cfg.writeEntry<QString>("BundleName", "");
+ cfg.writeEntry<QString>("BundleDescription", "");
+ cfg.writeEntry<QString>("BundleImage", "");
+ }
+ cfg.writeEntry<QString>("BundleExportLocation", saveLocation());
+ cfg.writeEntry<QString>("BundleAuthorName", authorName());
+ cfg.writeEntry<QString>("BundleAuthorEmail", email());
+ cfg.writeEntry<QString>("BundleWebsite", website());
+ cfg.writeEntry<QString>("BundleLicense", license());
+}
+
+void DlgCreateBundle::slotEmbedTags()
+{
+ DlgEmbedTags* dlg = new DlgEmbedTags(m_selectedTagIds);
+ int response = dlg->exec();
+ if (response == KoDialog::Accepted) {
+ m_selectedTagIds = dlg->selectedTagIds();
}
}
+void DlgCreateBundle::reject()
+{
+ saveToConfiguration(true);
+ KoDialog::reject();
+}
+
void DlgCreateBundle::selectSaveLocation()
{
KoFileDialog dialog(this, KoFileDialog::OpenDirectory, "resourcebundlesavelocation");
dialog.setDefaultDir(m_ui->lblSaveLocation->text());
dialog.setCaption(i18n("Select a directory to save the bundle"));
QString location = dialog.filename();
m_ui->lblSaveLocation->setText(location);
}
void DlgCreateBundle::addSelected()
{
int row = m_ui->tableAvailable->currentRow();
Q_FOREACH (QListWidgetItem *item, m_ui->tableAvailable->selectedItems()) {
m_ui->tableSelected->addItem(m_ui->tableAvailable->takeItem(m_ui->tableAvailable->row(item)));
- QString resourceType = m_ui->cmbResourceTypes->itemData(m_ui->cmbResourceTypes->currentIndex()).toString();
- if (resourceType == "brushes") {
- m_selectedBrushes.append(item->data(Qt::UserRole).toString());
- }
- else if (resourceType == "presets") {
- m_selectedPresets.append(item->data(Qt::UserRole).toString());
- }
- else if (resourceType == "gradients") {
- m_selectedGradients.append(item->data(Qt::UserRole).toString());
-
- }
- else if (resourceType == "patterns") {
- m_selectedPatterns.append(item->data(Qt::UserRole).toString());
- }
- else if (resourceType == "palettes") {
- m_selectedPalettes.append(item->data(Qt::UserRole).toString());
- }
- else if (resourceType == "workspaces") {
- m_selectedWorkspaces.append(item->data(Qt::UserRole).toString());
- }
- else if (resourceType == "gamutmasks") {
- m_selectedGamutMasks.append(item->data(Qt::UserRole).toString());
- }
+ m_selectedResourcesIds.append(item->data(Qt::UserRole).toInt());
}
m_ui->tableAvailable->setCurrentRow(row);
}
void DlgCreateBundle::removeSelected()
{
int row = m_ui->tableSelected->currentRow();
Q_FOREACH (QListWidgetItem *item, m_ui->tableSelected->selectedItems()) {
m_ui->tableAvailable->addItem(m_ui->tableSelected->takeItem(m_ui->tableSelected->row(item)));
- QString resourceType = m_ui->cmbResourceTypes->itemData(m_ui->cmbResourceTypes->currentIndex()).toString();
- if (resourceType == "brushes") {
- m_selectedBrushes.removeAll(item->data(Qt::UserRole).toString());
- }
- else if (resourceType == "presets") {
- m_selectedPresets.removeAll(item->data(Qt::UserRole).toString());
- }
- else if (resourceType == "gradients") {
- m_selectedGradients.removeAll(item->data(Qt::UserRole).toString());
-
- }
- else if (resourceType == "patterns") {
- m_selectedPatterns.removeAll(item->data(Qt::UserRole).toString());
- }
- else if (resourceType == "palettes") {
- m_selectedPalettes.removeAll(item->data(Qt::UserRole).toString());
- }
- else if (resourceType == "workspaces") {
- m_selectedWorkspaces.removeAll(item->data(Qt::UserRole).toString());
- }
- else if (resourceType == "gamutmasks") {
- m_selectedGamutMasks.removeAll(item->data(Qt::UserRole).toString());
- }
+ m_selectedResourcesIds.removeAll(item->data(Qt::UserRole).toInt());
}
m_ui->tableSelected->setCurrentRow(row);
}
QPixmap imageToIcon(const QImage &img) {
QPixmap pixmap(ICON_SIZE, ICON_SIZE);
pixmap.fill();
QImage scaled = img.scaled(ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation);
int x = (ICON_SIZE - scaled.width()) / 2;
int y = (ICON_SIZE - scaled.height()) / 2;
QPainter gc(&pixmap);
gc.drawImage(x, y, scaled);
gc.end();
return pixmap;
}
void DlgCreateBundle::resourceTypeSelected(int idx)
{
QString resourceType = m_ui->cmbResourceTypes->itemData(idx).toString();
m_ui->tableAvailable->clear();
m_ui->tableSelected->clear();
- if (resourceType == "brushes") {
- KisBrushResourceServer *server = KisBrushServer::instance()->brushServer();
- Q_FOREACH (KisBrushSP res, server->resources()) {
- QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name());
- item->setData(Qt::UserRole, res->shortFilename());
+ QString standarizedResourceType = (resourceType == "presets" ? ResourceType::PaintOpPresets : resourceType);
- if (m_selectedBrushes.contains(res->shortFilename())) {
- m_ui->tableSelected->addItem(item);
- }
- else {
- m_ui->tableAvailable->addItem(item);
- }
- }
- }
- else if (resourceType == "presets") {
- KisPaintOpPresetResourceServer* server = KisResourceServerProvider::instance()->paintOpPresetServer();
- Q_FOREACH (KisPaintOpPresetSP res, server->resources()) {
- QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name());
- item->setData(Qt::UserRole, res->shortFilename());
-
- if (m_selectedPresets.contains(res->shortFilename())) {
- m_ui->tableSelected->addItem(item);
- }
- else {
- m_ui->tableAvailable->addItem(item);
- }
- }
- }
- else if (resourceType == "gradients") {
- KoResourceServer<KoAbstractGradient>* server = KoResourceServerProvider::instance()->gradientServer();
- Q_FOREACH (KoResource *res, server->resources()) {
- if (res->filename()!="Foreground to Transparent" && res->filename()!="Foreground to Background") {
- //technically we should read from the file-name whether or not the file can be opened, but this works for now. The problem is making sure that bundle-resource know where they are stored.//
- //dbgKrita<<res->filename();
- QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name());
- item->setData(Qt::UserRole, res->shortFilename());
-
- if (m_selectedGradients.contains(res->shortFilename())) {
- m_ui->tableSelected->addItem(item);
- }
- else {
- m_ui->tableAvailable->addItem(item);
- }
- }
- }
- }
- else if (resourceType == "patterns") {
- KoResourceServer<KoPattern>* server = KoResourceServerProvider::instance()->patternServer();
- Q_FOREACH (KoResource *res, server->resources()) {
- QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name());
- item->setData(Qt::UserRole, res->shortFilename());
-
- if (m_selectedPatterns.contains(res->shortFilename())) {
- m_ui->tableSelected->addItem(item);
- }
- else {
- m_ui->tableAvailable->addItem(item);
- }
- }
- }
- else if (resourceType == "palettes") {
- KoResourceServer<KoColorSet>* server = KoResourceServerProvider::instance()->paletteServer();
- Q_FOREACH (KoResource *res, server->resources()) {
- QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name());
- item->setData(Qt::UserRole, res->shortFilename());
-
- if (m_selectedPalettes.contains(res->shortFilename())) {
- m_ui->tableSelected->addItem(item);
- }
- else {
- m_ui->tableAvailable->addItem(item);
+ KisResourceModel* model = KisResourceModelProvider::resourceModel(standarizedResourceType);
+ for (int i = 0; i < model->rowCount(); i++) {
+ QModelIndex idx = model->index(i, 0);
+ QString filename = model->data(idx, Qt::UserRole + KisResourceModel::Filename).toString();
+ int id = model->data(idx, Qt::UserRole + KisResourceModel::Id).toInt();
+
+ if (resourceType == ResourceType::Gradients) {
+ if (filename == "Foreground to Transparent" || filename == "Foreground to Background") {
+ continue;
}
}
- }
- else if (resourceType == "workspaces") {
- KoResourceServer<KisWorkspaceResource>* server = KisResourceServerProvider::instance()->workspaceServer();
- Q_FOREACH (KoResource *res, server->resources()) {
- QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name());
- item->setData(Qt::UserRole, res->shortFilename());
-
- if (m_selectedWorkspaces.contains(res->shortFilename())) {
- m_ui->tableSelected->addItem(item);
- }
- else {
- m_ui->tableAvailable->addItem(item);
- }
+
+ QImage image = (model->data(idx, Qt::UserRole + KisResourceModel::Thumbnail)).value<QImage>();
+ QString name = model->data(idx, Qt::UserRole + KisResourceModel::Name).toString();
+
+ // Function imageToIcon(QImage()) returns a square white pixmap and a warning "QImage::scaled: Image is a null image"
+ // while QPixmap() returns an empty pixmap.
+ // The difference between them is relevant in case of Workspaces which has no images.
+ // Using QPixmap() makes them appear in a dense list without icons, while imageToIcon(QImage())
+ // would give a list with big white rectangles and names of the workspaces.
+ QListWidgetItem *item = new QListWidgetItem(image.isNull() ? QPixmap() : imageToIcon(image), name);
+ item->setData(Qt::UserRole, id);
+
+ if (m_selectedResourcesIds.contains(id)) {
+ m_ui->tableSelected->addItem(item);
}
- }
- else if (resourceType == "gamutmasks") {
- KoResourceServer<KoGamutMask>* server = KoResourceServerProvider::instance()->gamutMaskServer();
- Q_FOREACH (KoResource *res, server->resources()) {
- QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name());
- item->setData(Qt::UserRole, res->shortFilename());
-
- if (m_selectedGamutMasks.contains(res->shortFilename())) {
- m_ui->tableSelected->addItem(item);
- }
- else {
- m_ui->tableAvailable->addItem(item);
- }
+ else {
+ m_ui->tableAvailable->addItem(item);
}
}
}
void DlgCreateBundle::getPreviewImage()
{
KoFileDialog dialog(this, KoFileDialog::OpenFile, "BundlePreviewImage");
dialog.setCaption(i18n("Select file to use as bundle icon"));
dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import));
m_previewImage = dialog.filename();
QImage img(m_previewImage);
img = img.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation);
m_ui->lblPreview->setPixmap(QPixmap::fromImage(img));
}
diff --git a/plugins/extensions/resourcemanager/dlg_create_bundle.h b/plugins/extensions/resourcemanager/dlg_create_bundle.h
index 9253ecd74e..373895b08c 100644
--- a/plugins/extensions/resourcemanager/dlg_create_bundle.h
+++ b/plugins/extensions/resourcemanager/dlg_create_bundle.h
@@ -1,83 +1,79 @@
/*
* Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr
+ * Copyright (c) 2020 Agata Cacko cacko.azh@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 KOBUNDLECREATIONWIDGET_H
#define KOBUNDLECREATIONWIDGET_H
#include <KoDialog.h>
-class KisResourceBundle;
+#include <KoResourceBundle.h>
namespace Ui
{
class WdgDlgCreateBundle;
}
class DlgCreateBundle : public KoDialog
{
Q_OBJECT
public:
- explicit DlgCreateBundle(KisResourceBundle *bundle = 0, QWidget *parent = 0);
+ explicit DlgCreateBundle(KoResourceBundleSP bundle = nullptr, QWidget *parent = 0);
~DlgCreateBundle() override;
QString bundleName() const;
QString authorName() const;
QString email() const;
QString website() const;
QString license() const;
QString description() const;
QString saveLocation() const;
QString previewImage() const;
- QStringList selectedBrushes() const { return m_selectedBrushes; }
- QStringList selectedPresets() const { return m_selectedPresets; }
- QStringList selectedGradients() const { return m_selectedGradients; }
- QStringList selectedPatterns() const { return m_selectedPatterns; }
- QStringList selectedPalettes() const { return m_selectedPalettes; }
- QStringList selectedWorkspaces() const { return m_selectedWorkspaces; }
- QStringList selectedGamutMasks() const { return m_selectedGamutMasks; }
-
private Q_SLOTS:
void accept() override;
+ void reject() override;
+
void selectSaveLocation();
void addSelected();
void removeSelected();
void resourceTypeSelected(int idx);
void getPreviewImage();
+ void saveToConfiguration(bool full);
+ void slotEmbedTags();
+ QVector<KisTagSP> getTagsForEmbeddingInResource(QVector<KisTagSP> resourceTags) const;
private:
+
+ void putResourcesInTheBundle() const;
+
QWidget *m_page;
Ui::WdgDlgCreateBundle *m_ui;
- QStringList m_selectedBrushes;
- QStringList m_selectedPresets;
- QStringList m_selectedGradients;
- QStringList m_selectedPatterns;
- QStringList m_selectedPalettes;
- QStringList m_selectedWorkspaces;
- QStringList m_selectedGamutMasks;
+ QList<int> m_selectedResourcesIds;
+ QList<int> m_selectedTagIds;
QString m_previewImage;
- KisResourceBundle *m_bundle;
+ KoResourceBundleSP m_bundle;
};
#endif // KOBUNDLECREATIONWIDGET_H
diff --git a/plugins/extensions/resourcemanager/dlg_embed_tags.cpp b/plugins/extensions/resourcemanager/dlg_embed_tags.cpp
new file mode 100644
index 0000000000..2380a339c3
--- /dev/null
+++ b/plugins/extensions/resourcemanager/dlg_embed_tags.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2020 Agata Cacko cacko.azh@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 "ui_wdgdlgembedtags.h"
+
+#include <QProcessEnvironment>
+#include <QFileInfo>
+#include <QMessageBox>
+#include <QStandardPaths>
+#include <QGridLayout>
+#include <QTableWidget>
+#include <QPainter>
+#include <QListWidget>
+
+#include <KisImportExportManager.h>
+#include <KoDocumentInfo.h>
+#include <KoFileDialog.h>
+#include <kis_icon.h>
+#include <KoResource.h>
+#include <KoResourceServer.h>
+#include <KoResourceServerProvider.h>
+
+#include <kis_workspace_resource.h>
+#include <brushengine/kis_paintop_preset.h>
+#include <dlg_embed_tags.h>
+
+#include <kis_config.h>
+
+#define ICON_SIZE 48
+
+DlgEmbedTags::DlgEmbedTags(QList<int> selectedTags, QWidget *parent)
+ : KoDialog(parent)
+ , m_ui(new Ui::WdgDlgEmbedTags)
+{
+ m_page = new QWidget();
+ m_ui->setupUi(m_page);
+ setMainWidget(m_page);
+ setFixedSize(m_page->sizeHint());
+ setButtons(Ok | Cancel);
+ setDefaultButton(Ok);
+ setButtonText(Ok, i18n("Save"));
+ m_selectedTagIds = selectedTags;
+
+
+ KoDocumentInfo info;
+ info.updateParameters();
+
+
+ m_ui->bnAdd->setIcon(KisIconUtils::loadIcon("arrow-right"));
+ connect(m_ui->bnAdd, SIGNAL(clicked()), SLOT(addSelected()));
+
+ m_ui->bnRemove->setIcon(KisIconUtils::loadIcon("arrow-left"));
+ connect(m_ui->bnRemove, SIGNAL(clicked()), SLOT(removeSelected()));
+
+
+ m_ui->cmbResourceTypes->addItem(i18n("Brushes"), ResourceType::Brushes);
+ m_ui->cmbResourceTypes->addItem(i18n("Brush Presets"), ResourceType::PaintOpPresets);
+ m_ui->cmbResourceTypes->addItem(i18n("Gradients"), ResourceType::Gradients);
+ m_ui->cmbResourceTypes->addItem(i18n("Gamut Masks"), ResourceType::GamutMasks);
+ m_ui->cmbResourceTypes->addItem(i18n("Patterns"), ResourceType::Patterns);
+ m_ui->cmbResourceTypes->addItem(i18n("Palettes"), ResourceType::Palettes);
+ m_ui->cmbResourceTypes->addItem(i18n("Workspaces"), ResourceType::Workspaces);
+
+ connect(m_ui->cmbResourceTypes, SIGNAL(activated(int)), SLOT(resourceTypeSelected(int)));
+
+ m_ui->tableAvailable->setIconSize(QSize(ICON_SIZE, ICON_SIZE));
+ m_ui->tableAvailable->setSelectionMode(QAbstractItemView::ExtendedSelection);
+
+ m_ui->tableSelected->setIconSize(QSize(ICON_SIZE, ICON_SIZE));
+ m_ui->tableSelected->setSelectionMode(QAbstractItemView::ExtendedSelection);
+
+ resourceTypeSelected(0);
+}
+
+DlgEmbedTags::~DlgEmbedTags()
+{
+ delete m_ui;
+}
+
+QList<int> DlgEmbedTags::selectedTagIds()
+{
+ return m_selectedTagIds;
+}
+
+void DlgEmbedTags::addSelected()
+{
+ int row = m_ui->tableAvailable->currentRow();
+
+ Q_FOREACH (QListWidgetItem *item, m_ui->tableAvailable->selectedItems()) {
+ m_ui->tableSelected->addItem(m_ui->tableAvailable->takeItem(m_ui->tableAvailable->row(item)));
+ m_selectedTagIds.append(item->data(Qt::UserRole).toInt());
+ }
+
+ m_ui->tableAvailable->setCurrentRow(row);
+}
+
+void DlgEmbedTags::removeSelected()
+{
+ int row = m_ui->tableSelected->currentRow();
+
+ Q_FOREACH (QListWidgetItem *item, m_ui->tableSelected->selectedItems()) {
+ m_ui->tableAvailable->addItem(m_ui->tableSelected->takeItem(m_ui->tableSelected->row(item)));
+ m_selectedTagIds.removeAll(item->data(Qt::UserRole).toInt());
+ }
+
+ m_ui->tableSelected->setCurrentRow(row);
+}
+
+void DlgEmbedTags::resourceTypeSelected(int idx)
+{
+ QString resourceType = m_ui->cmbResourceTypes->itemData(idx).toString();
+
+ m_ui->tableAvailable->clear();
+ m_ui->tableSelected->clear();
+
+ QString standarizedResourceType = (resourceType == "presets" ? ResourceType::PaintOpPresets : resourceType);
+
+ KisTagModel* model = KisTagModelProvider::tagModel(standarizedResourceType);
+
+ for (int i = 0; i < model->rowCount(); i++) {
+
+ QModelIndex idx = model->index(i, 0);
+ QString name = model->data(idx, Qt::DisplayRole).toString();
+ int id = model->data(idx, Qt::UserRole + KisTagModel::Id).toInt();
+
+ if (id < 0) {
+ // skip automated tags
+ continue;
+ }
+
+ QListWidgetItem *item = new QListWidgetItem(QPixmap(), name);
+ item->setData(Qt::UserRole, id);
+
+ if (m_selectedTagIds.contains(id)) {
+ m_ui->tableSelected->addItem(item);
+ }
+ else {
+ m_ui->tableAvailable->addItem(item);
+ }
+ }
+
+}
+
+
diff --git a/plugins/extensions/resourcemanager/dlg_embed_tags.h b/plugins/extensions/resourcemanager/dlg_embed_tags.h
new file mode 100644
index 0000000000..1bc8ccb1e3
--- /dev/null
+++ b/plugins/extensions/resourcemanager/dlg_embed_tags.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020 Agata Cacko cacko.azh@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 DLG_EMBED_TAGS_H
+#define DLG_EMBED_TAGS_H
+
+#include <KoDialog.h>
+
+#include <KoResourceBundle.h>
+
+namespace Ui
+{
+class WdgDlgEmbedTags;
+}
+
+class DlgEmbedTags : public KoDialog
+{
+ Q_OBJECT
+
+public:
+ explicit DlgEmbedTags(QList<int> selectedTags, QWidget *parent = 0);
+ ~DlgEmbedTags() override;
+
+ QList<int> selectedTagIds();
+
+private Q_SLOTS:
+
+ void addSelected();
+ void removeSelected();
+ void resourceTypeSelected(int idx);
+
+private:
+
+ QWidget *m_page;
+ Ui::WdgDlgEmbedTags *m_ui;
+
+ QList<int> m_selectedTagIds;
+};
+
+#endif // KOBUNDLECREATIONWIDGET_H
diff --git a/plugins/extensions/resourcemanager/resourcemanager.cpp b/plugins/extensions/resourcemanager/resourcemanager.cpp
index 8e1d4d0753..48edc4fec3 100644
--- a/plugins/extensions/resourcemanager/resourcemanager.cpp
+++ b/plugins/extensions/resourcemanager/resourcemanager.cpp
@@ -1,334 +1,333 @@
/*
* 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 <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 <KisBrushServerProvider.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();
+ brushServer = KisBrushServerProvider::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;
+ KoResourceServer<KisBrush>* 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)
+KoResourceBundleSP ResourceManager::saveBundle(const DlgCreateBundle &dlgCreateBundle)
{
QString bundlePath = dlgCreateBundle.saveLocation() + "/" + dlgCreateBundle.bundleName() + ".bundle";
- KisResourceBundle *newBundle = new KisResourceBundle(bundlePath);
+ KoResourceBundleSP newBundle(new KoResourceBundle(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->setMetaData("name", dlgCreateBundle.bundleName());
+ newBundle->setMetaData("author", dlgCreateBundle.authorName());
+ newBundle->setMetaData("email", dlgCreateBundle.email());
+ newBundle->setMetaData("license", dlgCreateBundle.license());
+ newBundle->setMetaData("website", dlgCreateBundle.website());
+ newBundle->setMetaData("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());
+ KoResourceSP res = d->brushServer->resourceByFilename(r);
+ newBundle->addResource(ResourceType::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());
+ KoResourceSP res = d->gradientServer->resourceByFilename(r);
+ newBundle->addResource(ResourceType::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());
+ KoResourceSP res = d->paletteServer->resourceByFilename(r);
+ newBundle->addResource(ResourceType::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());
+ KoResourceSP res = d->patternServer->resourceByFilename(r);
+ newBundle->addResource(ResourceType::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());
+ KoResourceSP res = preset;
+ newBundle->addResource(ResourceType::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();
+ KisBrushSP brush = d->brushServer->resourceByFilename(brushFile);
if (brush) {
- newBundle->addResource("kis_brushes", brushFile, d->brushServer->assignedTagsList(brush), brush->md5());
+ newBundle->addResource(ResourceType::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());
+ KoResourceSP res = d->workspaceServer->resourceByFilename(r);
+ newBundle->addResource(ResourceType::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());
+ KoResourceSP res = d->gamutMaskServer->resourceByFilename(r);
+ newBundle->addResource(ResourceType::GamutMasks, res->filename(), d->gamutMaskServer->assignedTagsList(res), res->md5());
}
+ */
- newBundle->addMeta("fileName", bundlePath);
- newBundle->addMeta("created", QDateTime::currentDateTime().toOffsetFromUtc(0).toString(Qt::ISODate));
+ newBundle->setMetaData("fileName", bundlePath);
+ newBundle->setMetaData("created", QDateTime::currentDateTime().toOffsetFromUtc(0).toString(Qt::ISODate));
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);
+// if (QDir(KoResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation()) != QDir(QFileInfo(bundlePath).path())) {
+// newBundle->setFilename(KoResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + "/" + dlgCreateBundle.bundleName() + ".bundle");
+// }
+// if (KoResourceBundleServerProvider::instance()->resourceBundleServer()->resourceByName(newBundle->name())) {
+// KoResourceBundleServerProvider::instance()->resourceBundleServer()->removeResourceFromServer(
+// KoResourceBundleServerProvider::instance()->resourceBundleServer()->resourceByName(newBundle->name()));
+// }
+// KoResourceBundleServerProvider::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;
- }
+ QPointer<DlgBundleManager> dlg = new DlgBundleManager();
+ dlg->exec();
}
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");
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.completeBaseName() + bundle->defaultFileExtension();
- QFileInfo fileInfo(newFilename);
-
- int i = 1;
- while (fileInfo.exists()) {
- fileInfo.setFile(KisResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.completeBaseName() + QString("%1").arg(i) + bundle->defaultFileExtension());
- i++;
- }
- bundle->setFilename(fileInfo.filePath());
- QFile::copy(res, newFilename);
- KisResourceBundleServerProvider::instance()->resourceBundleServer()->addResource(bundle, false);
- }
+// QStringList resources = importResources(i18n("Import Bundles"), QStringList() << "application/x-krita-bundle");
+// Q_FOREACH (const QString &res, resources) {
+// KoResourceBundleSP bundle = KoResourceBundleServerProvider::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 = KoResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + bundle->defaultFileExtension();
+// QFileInfo fileInfo(newFilename);
+
+// int i = 1;
+// while (fileInfo.exists()) {
+// fileInfo.setFile(KoResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + QString("%1").arg(i) + bundle->defaultFileExtension());
+// i++;
+// }
+// bundle->setFilename(fileInfo.filePath());
+// QFile::copy(res, newFilename);
+// KoResourceBundleServerProvider::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/extensions/resourcemanager/resourcemanager.h b/plugins/extensions/resourcemanager/resourcemanager.h
index 968fed07a0..0ebd51547c 100644
--- a/plugins/extensions/resourcemanager/resourcemanager.h
+++ b/plugins/extensions/resourcemanager/resourcemanager.h
@@ -1,63 +1,63 @@
/*
* resourcemanager.h -- Part of Krita
*
* 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 RESOURCEMANAGER_H
#define RESOURCEMANAGER_H
#include <QVariant>
#include <QStringList>
#include <QString>
#include <KoResourceServer.h>
#include <KisActionPlugin.h>
-#include "KisResourceBundle.h"
+#include <KoResourceBundle.h>
#include "dlg_create_bundle.h"
class ResourceManager : public KisActionPlugin
{
Q_OBJECT
public:
ResourceManager(QObject *parent, const QVariantList &);
~ResourceManager() override;
- KisResourceBundle *saveBundle(const DlgCreateBundle &dlgCreateBundle);
+ KoResourceBundleSP saveBundle(const DlgCreateBundle &dlgCreateBundle);
private Q_SLOTS:
void slotCreateBundle();
void slotManageBundles();
void slotImportBrushes();
void slotImportGradients();
void slotImportPalettes();
void slotImportPatterns();
void slotImportPresets();
void slotImportWorkspaces();
void slotImportBundles();
private:
QStringList importResources(const QString &title, const QStringList &mimes) const;
class Private;
QScopedPointer<Private> d;
};
#endif // RESOURCEMANAGER_H
diff --git a/plugins/extensions/resourcemanager/wdgdlgbundlemanager.ui b/plugins/extensions/resourcemanager/wdgdlgbundlemanager.ui
index 754784712a..7a8f05a23b 100644
--- a/plugins/extensions/resourcemanager/wdgdlgbundlemanager.ui
+++ b/plugins/extensions/resourcemanager/wdgdlgbundlemanager.ui
@@ -1,539 +1,371 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgDlgBundleManager</class>
<widget class="QWidget" name="WdgDlgBundleManager">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>835</width>
- <height>712</height>
+ <width>778</width>
+ <height>572</height>
</rect>
</property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <item>
- <widget class="QGroupBox" name="groupBox">
- <property name="title">
- <string>Active Bundles</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_6">
- <item>
- <widget class="QListWidget" name="listActive">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>200</height>
- </size>
- </property>
- <property name="alternatingRowColors">
- <bool>true</bool>
- </property>
- <property name="selectionMode">
- <enum>QAbstractItemView::ExtendedSelection</enum>
- </property>
- <property name="spacing">
- <number>1</number>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="createBundleButton">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Create New Bundle</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
- <spacer name="verticalSpacer_4">
- <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>
- <widget class="QToolButton" name="bnAdd">
- <property name="text">
- <string>...</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="bnRemove">
- <property name="text">
- <string>...</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer_3">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
+ <widget class="QTableView" name="tableView">
+ <property name="iconSize">
<size>
- <width>20</width>
- <height>40</height>
+ <width>48</width>
+ <height>48</height>
</size>
</property>
- </spacer>
- </item>
- </layout>
- </item>
- <item row="0" column="2">
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <item>
- <widget class="QGroupBox" name="groupBox_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Inactive Bundles</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_7">
- <item>
- <widget class="QListWidget" name="listInactive">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>200</height>
- </size>
- </property>
- <property name="selectionMode">
- <enum>QAbstractItemView::ExtendedSelection</enum>
- </property>
- <property name="spacing">
- <number>1</number>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="3">
- <layout class="QVBoxLayout" name="verticalLayout_5">
- <item>
- <widget class="QPushButton" name="bnImportBundles">
- <property name="text">
- <string>Import Bundles</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="bnImportBrushes">
- <property name="text">
- <string>Import Brushes</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="bnImportPatterns">
- <property name="text">
- <string>Import Patterns</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="bnImportGradients">
- <property name="text">
- <string>Import Gradients</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="bnImportPresets">
- <property name="text">
- <string>Import Presets</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="bnImportPalettes">
- <property name="text">
- <string>Import Palettes</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="bnImportWorkspaces">
- <property name="text">
- <string>Import Workspaces</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="deleteBackupFilesButton">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Delete Backup Files</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="openResourceFolderButton">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Open Resource Folder</string>
- </property>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
</widget>
</item>
<item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>18</width>
- <height>17</height>
- </size>
- </property>
- </spacer>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <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="QPushButton" name="bnAdd">
+ <property name="toolTip">
+ <string>Import a resource library</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="new">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="bnNew">
+ <property name="toolTip">
+ <string>Create a resource library</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="add">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="bnDelete">
+ <property name="toolTip">
+ <string>Delete a resource library</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="delete">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
</layout>
</item>
- <item row="1" column="0" colspan="4">
+ <item>
<widget class="QGroupBox" name="BundleSelectedGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
- <string>Selected Bundle</string>
+ <string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QSplitter" name="splitter_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QWidget" name="selectedBundleInfoContainer">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>10</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="lblName">
<property name="text">
<string>Bundle Name</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
</widget>
</item>
- <item>
- <widget class="QPushButton" name="bnEditBundle">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="text">
- <string>&amp;Edit bundle...</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>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="lblDescription">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>50</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,0">
<item>
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QLabel" name="lblPreview">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="frameShape">
- <enum>QFrame::Box</enum>
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</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>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="verticalSpacing">
<number>3</number>
</property>
<item row="0" column="1">
<widget class="QLabel" name="lblAuthor">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lblLicense">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="lblCreated">
<property name="text">
<string/>
</property>
</widget>
</item>
<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="text">
<string>Author:</string>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>License:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="lblUpdated">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Created:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Email:</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Updated:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="lblEmail">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Website:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblWebsite">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<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>
</item>
</layout>
</item>
</layout>
</widget>
- <widget class="QTreeWidget" name="listBundleContents">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>200</width>
- <height>0</height>
- </size>
- </property>
- <column>
- <property name="text">
- <string notr="true">1</string>
- </property>
- </column>
- </widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
+ <tabstops>
+ <tabstop>tableView</tabstop>
+ <tabstop>bnAdd</tabstop>
+ <tabstop>bnNew</tabstop>
+ <tabstop>bnDelete</tabstop>
+ <tabstop>lblDescription</tabstop>
+ </tabstops>
<resources/>
<connections/>
</ui>
diff --git a/plugins/extensions/resourcemanager/wdgdlgcreatebundle.ui b/plugins/extensions/resourcemanager/wdgdlgcreatebundle.ui
index cd6ffeae41..29cd137eeb 100644
--- a/plugins/extensions/resourcemanager/wdgdlgcreatebundle.ui
+++ b/plugins/extensions/resourcemanager/wdgdlgcreatebundle.ui
@@ -1,480 +1,487 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgDlgCreateBundle</class>
<widget class="QWidget" name="WdgDlgCreateBundle">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>895</width>
- <height>460</height>
+ <height>491</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>New Bundle...</string>
</property>
<property name="windowOpacity">
<double>1.000000000000000</double>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>15</number>
</property>
<property name="topMargin">
<number>15</number>
</property>
<property name="rightMargin">
<number>15</number>
</property>
<property name="bottomMargin">
<number>15</number>
</property>
<property name="horizontalSpacing">
<number>20</number>
</property>
<item row="0" column="1">
<layout class="QGridLayout" name="gridLayout_4">
<property name="verticalSpacing">
<number>12</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>12</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cmbResourceTypes">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="insertPolicy">
<enum>QComboBox::InsertAlphabetically</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>12</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="spacing">
<number>12</number>
</property>
<item row="1" column="1">
<widget class="QListWidget" name="tableAvailable">
<property name="minimumSize">
<size>
<width>0</width>
<height>350</height>
</size>
</property>
<property name="spacing">
<number>2</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Available</string>
</property>
</widget>
</item>
<item row="2" column="1">
<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>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>12</number>
</property>
<item>
<spacer name="verticalSpacer_4">
<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>
<widget class="QToolButton" name="bnAdd">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="bnRemove">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<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>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<property name="spacing">
<number>12</number>
</property>
<item row="1" column="0">
<widget class="QListWidget" name="tableSelected">
<property name="minimumSize">
<size>
<width>0</width>
<height>350</height>
</size>
</property>
<property name="spacing">
<number>2</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Selected</string>
</property>
</widget>
</item>
<item row="2" column="0">
<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>
</item>
</layout>
</item>
</layout>
</item>
<item row="0" column="0">
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>
</property>
<property name="horizontalSpacing">
<number>8</number>
</property>
<property name="verticalSpacing">
<number>8</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="bundleName">
<property name="text">
<string>Bundle Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="editBundleName"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="description">
<property name="text">
<string>Description:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPlainTextEdit" name="editDescription">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>120</height>
</size>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="author">
<property name="text">
<string>Author:</string>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="editAuthor">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Email:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="editEmail"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="website">
<property name="text">
<string>Website:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="editWebsite">
<property name="text">
<string>http://</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="license">
<property name="text">
<string>License:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="editLicense">
<property name="text">
<string/>
</property>
</widget>
</item>
- <item row="9" column="0">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Save to:</string>
- </property>
- </widget>
- </item>
- <item row="9" column="1">
- <layout class="QHBoxLayout" name="horizontalLayout">
- <property name="spacing">
- <number>8</number>
- </property>
- <property name="topMargin">
- <number>3</number>
- </property>
- <item>
- <widget class="QLabel" name="lblSaveLocation">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>20</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>25</height>
- </size>
- </property>
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Sunken</enum>
- </property>
- <property name="text">
- <string>TextLabel</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
- </property>
- <property name="margin">
- <number>0</number>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="bnSelectSaveLocation">
- <property name="text">
- <string>...</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
<item row="7" column="0">
<widget class="QLabel" name="label_5">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Icon:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>2</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="lblPreview">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</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>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="bnGetPreview">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>(256 x 256)</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>
</layout>
</item>
+ <item row="9" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Save to:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>8</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="lblSaveLocation">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>25</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="bnSelectSaveLocation">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="10" column="1">
+ <widget class="QPushButton" name="bnEmbedTags">
+ <property name="text">
+ <string>Embed tags...</string>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
diff --git a/plugins/extensions/resourcemanager/wdgdlgembedtags.ui b/plugins/extensions/resourcemanager/wdgdlgembedtags.ui
new file mode 100644
index 0000000000..0b66ed63e1
--- /dev/null
+++ b/plugins/extensions/resourcemanager/wdgdlgembedtags.ui
@@ -0,0 +1,216 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WdgDlgEmbedTags</class>
+ <widget class="QWidget" name="WdgDlgEmbedTags">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>895</width>
+ <height>491</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>New Bundle...</string>
+ </property>
+ <property name="windowOpacity">
+ <double>1.000000000000000</double>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <property name="leftMargin">
+ <number>15</number>
+ </property>
+ <property name="topMargin">
+ <number>15</number>
+ </property>
+ <property name="rightMargin">
+ <number>15</number>
+ </property>
+ <property name="bottomMargin">
+ <number>15</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>20</number>
+ </property>
+ <item row="0" column="0">
+ <layout class="QGridLayout" name="gridLayout_4">
+ <property name="verticalSpacing">
+ <number>12</number>
+ </property>
+ <item row="0" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>12</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Type:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="cmbResourceTypes">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="insertPolicy">
+ <enum>QComboBox::InsertAlphabetically</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="spacing">
+ <number>12</number>
+ </property>
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>12</number>
+ </property>
+ <item row="1" column="1">
+ <widget class="QListWidget" name="tableAvailable">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>350</height>
+ </size>
+ </property>
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Available</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <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>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>12</number>
+ </property>
+ <item>
+ <spacer name="verticalSpacer_4">
+ <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>
+ <widget class="QToolButton" name="bnAdd">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="bnRemove">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <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>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="spacing">
+ <number>12</number>
+ </property>
+ <item row="1" column="0">
+ <widget class="QListWidget" name="tableSelected">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>350</height>
+ </size>
+ </property>
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Selected</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <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>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/plugins/filters/asccdl/kis_asccdl_filter.cpp b/plugins/filters/asccdl/kis_asccdl_filter.cpp
index 4a8326721b..54427645ba 100644
--- a/plugins/filters/asccdl/kis_asccdl_filter.cpp
+++ b/plugins/filters/asccdl/kis_asccdl_filter.cpp
@@ -1,131 +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.
*/
#include "kis_asccdl_filter.h"
#include "kis_wdg_asccdl.h"
#include <kpluginfactory.h>
#include <klocalizedstring.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_registry.h>
#include <filter/kis_color_transformation_configuration.h>
#include <qmath.h>
K_PLUGIN_FACTORY_WITH_JSON(KritaASCCDLFactory,
"kritaasccdl.json",
registerPlugin<KritaASCCDL>();)
KritaASCCDL::KritaASCCDL(QObject *parent, const QVariantList &) : QObject(parent)
{
KisFilterRegistry::instance()->add(KisFilterSP(new KisFilterASCCDL()));
}
KritaASCCDL::~KritaASCCDL()
{
}
KisFilterASCCDL::KisFilterASCCDL(): KisColorTransformationFilter(id(), FiltersCategoryAdjustId, i18n("&Slope, Offset, Power..."))
{
setSupportsPainting(true);
setSupportsAdjustmentLayers(true);
setSupportsLevelOfDetail(true);
setSupportsThreading(true);
setColorSpaceIndependence(FULLY_INDEPENDENT);
setShowConfigurationWidget(true);
}
KoColorTransformation *KisFilterASCCDL::createTransformation(const KoColorSpace *cs,
const KisFilterConfigurationSP config) const
{
KoColor black(Qt::black, cs);
KoColor white(Qt::white, cs);
return new KisASCCDLTransformation(cs,
config->getColor("slope", black),
config->getColor("offset", white),
config->getColor("power", black));
}
KisConfigWidget *KisFilterASCCDL::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool) const
{
return new KisASCCDLConfigWidget(parent, dev->colorSpace());
}
bool KisFilterASCCDL::needsTransparentPixels(const KisFilterConfigurationSP config, const KoColorSpace *cs) const
{
KoColor black(Qt::black, cs);
KoColor offset = config->getColor("offset", black);
offset.convertTo(cs);
if (cs->difference(black.data(), offset.data())>0) {
return true;
}
return false;
}
-KisFilterConfigurationSP KisFilterASCCDL::defaultConfiguration() const
+KisFilterConfigurationSP KisFilterASCCDL::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
QVariant colorVariant("KoColor");
KoColor black;
black.fromQColor(QColor(Qt::black));
KoColor white;
white.fromQColor(QColor(Qt::white));
colorVariant.setValue(white);
config->setProperty( "slope", colorVariant);
config->setProperty( "power", colorVariant);
colorVariant.setValue(black);
config->setProperty("offset", colorVariant);
return config;
}
KisASCCDLTransformation::KisASCCDLTransformation(const KoColorSpace *cs, KoColor slope, KoColor offset, KoColor power)
{
QVector<float> slopeN(cs->channelCount());
slope.convertTo(cs);
slope.colorSpace()->normalisedChannelsValue(slope.data(), slopeN);
m_slope = slopeN;
offset.convertTo(cs);
QVector<float> offsetN(cs->channelCount());
offset.colorSpace()->normalisedChannelsValue(offset.data(), offsetN);
m_offset = offsetN;
power.convertTo(cs);
QVector<float> powerN(cs->channelCount());
power.colorSpace()->normalisedChannelsValue(power.data(), powerN);
m_power = powerN;
m_cs = cs;
}
void KisASCCDLTransformation::transform(const quint8 *src, quint8 *dst, qint32 nPixels) const
{
QVector<float> normalised(m_cs->channelCount());
const int pixelSize = m_cs->pixelSize();
while (nPixels--) {
m_cs->normalisedChannelsValue(src, normalised);
for (uint c=0; c<m_cs->channelCount(); c++){
if (m_cs->channels().at(c)->channelType()!=KoChannelInfo::ALPHA) {
normalised[c] = qPow( (normalised.at(c)*m_slope.at(c))+m_offset.at(c), m_power.at(c));
}
}
m_cs->fromNormalisedChannelsValue(dst, normalised);
src += pixelSize;
dst += pixelSize;
}
}
#include "kis_asccdl_filter.moc"
diff --git a/plugins/filters/asccdl/kis_asccdl_filter.h b/plugins/filters/asccdl/kis_asccdl_filter.h
index 096b2ed14c..9c21a4e70c 100644
--- a/plugins/filters/asccdl/kis_asccdl_filter.h
+++ b/plugins/filters/asccdl/kis_asccdl_filter.h
@@ -1,61 +1,61 @@
/*
* 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_ASCCDL_FILTER_H
#define KIS_ASCCDL_FILTER_H
#include <filter/kis_filter.h>
#include "filter/kis_color_transformation_filter.h"
class KritaASCCDL : public QObject
{
Q_OBJECT
public:
KritaASCCDL(QObject *parent, const QVariantList &);
~KritaASCCDL() override;
};
class KisFilterASCCDL: public KisColorTransformationFilter
{
public:
KisFilterASCCDL();
public:
static inline KoID id() {
return KoID("asc-cdl", i18n("Slope, Offset, Power(ASC-CDL)"));
}
KoColorTransformation *createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const override;
KisConfigWidget *createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
bool needsTransparentPixels(const KisFilterConfigurationSP config, const KoColorSpace *cs) const override;
protected:
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
};
class KisASCCDLTransformation : public KoColorTransformation
{
public:
KisASCCDLTransformation(const KoColorSpace *cs, KoColor slope, KoColor offset, KoColor power);
void transform(const quint8* src, quint8* dst, qint32 nPixels) const override;
private:
QVector<float> m_slope;
QVector<float> m_offset;
QVector<float> m_power;
const KoColorSpace *m_cs;
};
#endif // KIS_ASCCDL_H
diff --git a/plugins/filters/asccdl/kis_wdg_asccdl.cpp b/plugins/filters/asccdl/kis_wdg_asccdl.cpp
index 5ad76ce753..c6a44aa732 100644
--- a/plugins/filters/asccdl/kis_wdg_asccdl.cpp
+++ b/plugins/filters/asccdl/kis_wdg_asccdl.cpp
@@ -1,116 +1,117 @@
/*
* 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.
*/
#include "kis_wdg_asccdl.h"
#include <kis_config.h>
#include <kis_color_button.h>
#include <kis_filter_configuration.h>
#include <KisVisualColorSelectorShape.h>
#include <KisVisualRectangleSelectorShape.h>
#include <KisVisualEllipticalSelectorShape.h>
+#include <KisGlobalResourcesInterface.h>
KisASCCDLConfigWidget::KisASCCDLConfigWidget(QWidget *parent, const KoColorSpace *cs)
:KisConfigWidget(parent),
m_page(new Ui_WdgASCCDL),
m_cs(cs)
{
KoColor black(Qt::black, cs);
m_page->setupUi(this);
m_page->btnSlope->setColor(black);
m_page->btnOffset->setColor(black);
m_page->btnPower->setColor(black);
connect(m_page->btnSlope , SIGNAL(changed(KoColor)), this, SLOT(slopeColorChanged(KoColor)));
connect(m_page->btnOffset, SIGNAL(changed(KoColor)), this, SLOT(offsetColorChanged(KoColor)));
connect(m_page->btnPower , SIGNAL(changed(KoColor)), this, SLOT(powerColorChanged(KoColor)));
connect(m_page->slopeSelector, SIGNAL(sigNewColor(KoColor)), this, SLOT(slopeColorChanged(KoColor)));
connect(m_page->offsetSelector, SIGNAL(sigNewColor(KoColor)), this, SLOT(offsetColorChanged(KoColor)));
connect(m_page->powerSelector, SIGNAL(sigNewColor(KoColor)), this, SLOT(powerColorChanged(KoColor)));
}
KisASCCDLConfigWidget::~KisASCCDLConfigWidget()
{
delete m_page;
}
KisPropertiesConfigurationSP KisASCCDLConfigWidget::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("asc-cdl", 0);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("asc-cdl", 0, KisGlobalResourcesInterface::instance());
QVariant colorVariant("KoColor");
colorVariant.setValue(m_page->btnSlope->color());
config->setProperty("slope", colorVariant);
colorVariant.setValue(m_page->btnOffset->color());
config->setProperty("offset", colorVariant);
colorVariant.setValue(m_page->btnPower->color());
config->setProperty("power", colorVariant);
return config;
}
void KisASCCDLConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
KoColor white(m_cs);
QVector<float> channels(m_cs->channelCount());
m_cs->normalisedChannelsValue(white.data(), channels);
channels.fill(1.0);
m_cs->fromNormalisedChannelsValue(white.data(), channels);
KoColor black(Qt::black, m_cs);
KoColor slope = config->getColor("slope", white);
slope.convertTo(m_cs);
KoColor offset = config->getColor("offset", black);
offset.convertTo(m_cs);
KoColor power = config->getColor("power", white);
power.convertTo(m_cs);
m_page->btnSlope->setColor(slope);
m_page->slopeSelector->slotSetColor(slope);
m_page->btnOffset->setColor(offset);
m_page->offsetSelector->slotSetColor(offset);
m_page->btnPower->setColor (power);
m_page->powerSelector->slotSetColor(power);
}
void KisASCCDLConfigWidget::slopeColorChanged(const KoColor &c)
{
if (QObject::sender() == m_page->btnSlope) {
m_page->slopeSelector->slotSetColor(c);
} else {
m_page->btnSlope->setColor(c);
}
emit sigConfigurationItemChanged();
}
void KisASCCDLConfigWidget::offsetColorChanged(const KoColor &c)
{
if (QObject::sender() == m_page->btnOffset) {
m_page->offsetSelector->slotSetColor(c);
} else {
m_page->btnOffset->setColor(c);
}
emit sigConfigurationItemChanged();
}
void KisASCCDLConfigWidget::powerColorChanged(const KoColor &c)
{
if (QObject::sender() == m_page->btnPower) {
m_page->powerSelector->slotSetColor(c);
} else {
m_page->btnPower->setColor(c);
}
emit sigConfigurationItemChanged();
}
diff --git a/plugins/filters/blur/kis_blur_filter.cpp b/plugins/filters/blur/kis_blur_filter.cpp
index 058a397718..21975fc5f7 100644
--- a/plugins/filters/blur/kis_blur_filter.cpp
+++ b/plugins/filters/blur/kis_blur_filter.cpp
@@ -1,138 +1,138 @@
/*
* 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 <compositeops/KoVcMultiArchBuildSupport.h> //MSVC requires that Vc come first
#include "kis_blur_filter.h"
#include <KoCompositeOp.h>
#include <kis_convolution_kernel.h>
#include <kis_convolution_painter.h>
#include "kis_wdg_blur.h"
#include "ui_wdgblur.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_mask_generator.h"
#include "kis_lod_transform.h"
KisBlurFilter::KisBlurFilter() : KisFilter(id(), FiltersCategoryBlurId, i18n("&Blur..."))
{
setSupportsPainting(true);
setSupportsAdjustmentLayers(true);
setSupportsLevelOfDetail(true);
setColorSpaceIndependence(FULLY_INDEPENDENT);
}
KisConfigWidget * KisBlurFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool) const
{
return new KisWdgBlur(parent);
}
-KisFilterConfigurationSP KisBlurFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisBlurFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("halfWidth", 5);
config->setProperty("halfHeight", 5);
config->setProperty("rotate", 0);
config->setProperty("strength", 0);
config->setProperty("shape", 0);
return config;
}
void KisBlurFilter::processImpl(KisPaintDeviceSP device,
const QRect& rect,
- const KisFilterConfigurationSP _config,
+ const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
QPoint srcTopLeft = rect.topLeft();
Q_ASSERT(device != 0);
- KisFilterConfigurationSP config = _config ? _config : new KisFilterConfiguration(id().id(), 1);
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
KisLodTransformScalar t(device);
QVariant value;
const uint halfWidth = t.scale((config->getProperty("halfWidth", value)) ? value.toUInt() : 5);
const uint halfHeight = t.scale((config->getProperty("halfHeight", value)) ? value.toUInt() : 5);
int shape = (config->getProperty("shape", value)) ? value.toInt() : 0;
uint width = 2 * halfWidth + 1;
uint height = 2 * halfHeight + 1;
qreal aspectRatio = (qreal) height / width;
int rotate = (config->getProperty("rotate", value)) ? value.toInt() : 0;
qreal strength = (config->getProperty("strength", value) ? value.toUInt() : 0) / (qreal) 100;
qreal hFade = strength;
qreal vFade = strength;
KisMaskGenerator* kas;
dbgKrita << width << "" << height << "" << hFade << "" << vFade;
switch (shape) {
case 1:
kas = new KisRectangleMaskGenerator(width, aspectRatio, hFade, vFade, 2, true);
break;
case 0:
default:
kas = new KisCircleMaskGenerator(width, aspectRatio, hFade, vFade, 2, true);
break;
}
QBitArray channelFlags;
if (config) {
channelFlags = config->channelFlags();
}
if (channelFlags.isEmpty() || !config) {
channelFlags = QBitArray(device->colorSpace()->channelCount(), true);
}
KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMaskGenerator(kas, rotate * M_PI / 180.0);
delete kas;
KisConvolutionPainter painter(device);
painter.setChannelFlags(channelFlags);
painter.setProgress(progressUpdater);
painter.applyMatrix(kernel, device, srcTopLeft, srcTopLeft, rect.size(), BORDER_REPEAT);
}
QRect KisBlurFilter::neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
const int halfWidth = t.scale(_config->getProperty("halfWidth", value) ? value.toUInt() : 5);
const int halfHeight = t.scale(_config->getProperty("halfHeight", value) ? value.toUInt() : 5);
return rect.adjusted(-halfWidth * 2, -halfHeight * 2, halfWidth * 2, halfHeight * 2);
}
QRect KisBlurFilter::changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
const int halfWidth = t.scale(_config->getProperty("halfWidth", value) ? value.toUInt() : 5);
const int halfHeight = t.scale(_config->getProperty("halfHeight", value) ? value.toUInt() : 5);
return rect.adjusted(-halfWidth, -halfHeight, halfWidth, halfHeight);
}
diff --git a/plugins/filters/blur/kis_blur_filter.h b/plugins/filters/blur/kis_blur_filter.h
index fbed93efaf..b62657843d 100644
--- a/plugins/filters/blur/kis_blur_filter.h
+++ b/plugins/filters/blur/kis_blur_filter.h
@@ -1,48 +1,48 @@
/*
* 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.
*/
#ifndef KIS_BLUR_FILTER_H
#define KIS_BLUR_FILTER_H
#include "filter/kis_filter.h"
class KisBlurFilter : public KisFilter
{
public:
KisBlurFilter();
public:
void processImpl(KisPaintDeviceSP device,
const QRect& size,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("blur", i18n("Blur"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
QRect neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
QRect changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
};
#endif
diff --git a/plugins/filters/blur/kis_gaussian_blur_filter.cpp b/plugins/filters/blur/kis_gaussian_blur_filter.cpp
index 45ecf787bf..58cf3884b6 100644
--- a/plugins/filters/blur/kis_gaussian_blur_filter.cpp
+++ b/plugins/filters/blur/kis_gaussian_blur_filter.cpp
@@ -1,148 +1,148 @@
/*
* This file is part of Krita
*
* Copyright (c) 2009 Edward Apap <schumifer@hotmail.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_gaussian_blur_filter.h"
#include "kis_wdg_gaussian_blur.h"
#include <KoCompositeOp.h>
#include <kis_convolution_kernel.h>
#include <kis_convolution_painter.h>
#include <kis_gaussian_kernel.h>
#include "ui_wdg_gaussian_blur.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_lod_transform.h"
#include <math.h>
KisGaussianBlurFilter::KisGaussianBlurFilter() : KisFilter(id(), FiltersCategoryBlurId, i18n("&Gaussian Blur..."))
{
setSupportsPainting(true);
setSupportsAdjustmentLayers(true);
setSupportsLevelOfDetail(true);
setColorSpaceIndependence(FULLY_INDEPENDENT);
}
KisConfigWidget * KisGaussianBlurFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool usedForMasks) const
{
return new KisWdgGaussianBlur(usedForMasks, parent);
}
-KisFilterConfigurationSP KisGaussianBlurFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisGaussianBlurFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("horizRadius", 5);
config->setProperty("vertRadius", 5);
config->setProperty("lockAspect", true);
return config;
}
void KisGaussianBlurFilter::processImpl(KisPaintDeviceSP device,
const QRect& rect,
- const KisFilterConfigurationSP _config,
+ const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
Q_ASSERT(device != 0);
- KisFilterConfigurationSP config = _config ? _config : new KisFilterConfiguration(id().id(), 1);
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
KisLodTransformScalar t(device);
QVariant value;
config->getProperty("horizRadius", value);
float horizontalRadius = t.scale(value.toFloat());
config->getProperty("vertRadius", value);
float verticalRadius = t.scale(value.toFloat());
QBitArray channelFlags;
if (config) {
channelFlags = config->channelFlags();
}
if (channelFlags.isEmpty() || !config) {
channelFlags = QBitArray(device->colorSpace()->channelCount(), true);
}
KisGaussianKernel::applyGaussian(device, rect,
horizontalRadius, verticalRadius,
channelFlags, progressUpdater);
}
QRect KisGaussianBlurFilter::neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
/**
* NOTE: integer division by two is done on purpose,
* because the kernel size is always odd
*/
const int halfWidth = _config->getProperty("horizRadius", value) ? KisGaussianKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
const int halfHeight = _config->getProperty("vertRadius", value) ? KisGaussianKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
return rect.adjusted(-halfWidth * 2, -halfHeight * 2, halfWidth * 2, halfHeight * 2);
}
QRect KisGaussianBlurFilter::changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
const int halfWidth = _config->getProperty("horizRadius", value) ? KisGaussianKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
const int halfHeight = _config->getProperty("vertRadius", value) ? KisGaussianKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
return rect.adjusted( -halfWidth, -halfHeight, halfWidth, halfHeight);
}
bool KisGaussianBlurFilter::configurationAllowedForMask(KisFilterConfigurationSP config) const
{
//ENTER_FUNCTION() << config->getFloat("horizRadius", 5.0) << config->getFloat("vertRadius", 5.0);
const float maxRadiusForMask = 100.0;
return config->getFloat("horizRadius", 5.0) <= maxRadiusForMask &&
config->getFloat("vertRadius", 5.0) <= maxRadiusForMask;
}
void KisGaussianBlurFilter::fixLoadedFilterConfigurationForMasks(KisFilterConfigurationSP config) const
{
ENTER_FUNCTION();
const float maxRadiusForMask = 100.0;
if (config->getFloat("horizRadius", 5.0) > maxRadiusForMask) {
config->setProperty("horizRadius", maxRadiusForMask);
}
if (config->getFloat("vertRadius", 5.0) > maxRadiusForMask) {
config->setProperty("vertRadius", maxRadiusForMask);
}
}
diff --git a/plugins/filters/blur/kis_gaussian_blur_filter.h b/plugins/filters/blur/kis_gaussian_blur_filter.h
index 1bbadb734f..cab5aad7e2 100644
--- a/plugins/filters/blur/kis_gaussian_blur_filter.h
+++ b/plugins/filters/blur/kis_gaussian_blur_filter.h
@@ -1,53 +1,53 @@
/* This file is part of Krita
*
* Copyright (c) 2009 Edward Apap <schumifer@hotmail.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_GAUSSIAN_BLUR_FILTER_H
#define KIS_GAUSSIAN_BLUR_FILTER_H
#include "filter/kis_filter.h"
#include "ui_wdg_gaussian_blur.h"
#include <Eigen/Core>
class KisGaussianBlurFilter : public KisFilter
{
public:
KisGaussianBlurFilter();
public:
void processImpl(KisPaintDeviceSP device,
const QRect& rect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("gaussian blur", i18n("Gaussian Blur"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
QRect neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
QRect changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
bool configurationAllowedForMask(KisFilterConfigurationSP config) const override;
void fixLoadedFilterConfigurationForMasks(KisFilterConfigurationSP config) const override;
};
#endif
diff --git a/plugins/filters/blur/kis_lens_blur_filter.cpp b/plugins/filters/blur/kis_lens_blur_filter.cpp
index 7b1ef6d55b..3fc01080da 100644
--- a/plugins/filters/blur/kis_lens_blur_filter.cpp
+++ b/plugins/filters/blur/kis_lens_blur_filter.cpp
@@ -1,205 +1,201 @@
/*
* This file is part of Krita
*
* Copyright (c) 2010 Edward Apap <schumifer@hotmail.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_lens_blur_filter.h"
#include "kis_wdg_lens_blur.h"
#include <KoCompositeOp.h>
#include <kis_convolution_kernel.h>
#include <kis_convolution_painter.h>
#include "ui_wdg_lens_blur.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_lod_transform.h"
#include <QPainter>
#include <math.h>
KisLensBlurFilter::KisLensBlurFilter() : KisFilter(id(), FiltersCategoryBlurId, i18n("&Lens Blur..."))
{
setSupportsPainting(true);
setSupportsAdjustmentLayers(true);
setSupportsLevelOfDetail(true);
setColorSpaceIndependence(FULLY_INDEPENDENT);
}
KisConfigWidget * KisLensBlurFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool) const
{
return new KisWdgLensBlur(parent);
}
QSize KisLensBlurFilter::getKernelHalfSize(const KisFilterConfigurationSP config, int lod)
{
QPolygonF iris = getIrisPolygon(config, lod);
QRect rect = iris.boundingRect().toAlignedRect();
int w = std::ceil(qreal(rect.width()) / 2.0);
int h = std::ceil(qreal(rect.height()) / 2.0);
return QSize(w, h);
}
-KisFilterConfigurationSP KisLensBlurFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisLensBlurFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("irisShape", "Pentagon (5)");
config->setProperty("irisRadius", 5);
config->setProperty("irisRotation", 0);
QSize halfSize = getKernelHalfSize(config, 0);
config->setProperty("halfWidth", halfSize.width());
config->setProperty("halfHeight", halfSize.height());
return config;
}
QPolygonF KisLensBlurFilter::getIrisPolygon(const KisFilterConfigurationSP config, int lod)
{
KIS_ASSERT_RECOVER(config) { return QPolygonF(); }
KisLodTransformScalar t(lod);
QVariant value;
config->getProperty("irisShape", value);
QString irisShape = value.toString();
config->getProperty("irisRadius", value);
uint irisRadius = t.scale(value.toUInt());
config->getProperty("irisRotation", value);
uint irisRotation = value.toUInt();
if (irisRadius < 1)
return QPolygon();
QPolygonF irisShapePoly;
int sides = 1;
qreal angle = 0;
if (irisShape == "Triangle") sides = 3;
else if (irisShape == "Quadrilateral (4)") sides = 4;
else if (irisShape == "Pentagon (5)") sides = 5;
else if (irisShape == "Hexagon (6)") sides = 6;
else if (irisShape == "Heptagon (7)") sides = 7;
else if (irisShape == "Octagon (8)") sides = 8;
else return QPolygonF();
for (int i = 0; i < sides; ++i) {
irisShapePoly << QPointF(0.5 * cos(angle), 0.5 * sin(angle));
angle += 2 * M_PI / sides;
}
QTransform transform;
transform.rotate(irisRotation);
transform.scale(irisRadius * 2, irisRadius * 2);
QPolygonF transformedIris = transform.map(irisShapePoly);
return transformedIris;
}
void KisLensBlurFilter::processImpl(KisPaintDeviceSP device,
const QRect& rect,
- const KisFilterConfigurationSP _config,
+ const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
QPoint srcTopLeft = rect.topLeft();
Q_ASSERT(device != 0);
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
- KisFilterConfigurationSP config = _config ? _config : new KisFilterConfiguration(id().id(), 1);
-
- QBitArray channelFlags;
- if (config) {
- channelFlags = config->channelFlags();
- }
- if (channelFlags.isEmpty() || !config) {
+ QBitArray channelFlags = config->channelFlags();
+ if (channelFlags.isEmpty()) {
channelFlags = QBitArray(device->colorSpace()->channelCount(), true);
}
const int lod = device->defaultBounds()->currentLevelOfDetail();
QPolygonF transformedIris = getIrisPolygon(config, lod);
if (transformedIris.isEmpty()) return;
QRectF boundingRect = transformedIris.boundingRect();
int kernelWidth = boundingRect.toAlignedRect().width();
int kernelHeight = boundingRect.toAlignedRect().height();
QImage kernelRepresentation(kernelWidth, kernelHeight, QImage::Format_RGB32);
kernelRepresentation.fill(0);
QPainter imagePainter(&kernelRepresentation);
imagePainter.setRenderHint(QPainter::Antialiasing);
imagePainter.setBrush(QColor::fromRgb(255, 255, 255));
QTransform offsetTransform;
offsetTransform.translate(-boundingRect.x(), -boundingRect.y());
imagePainter.setTransform(offsetTransform);
imagePainter.drawPolygon(transformedIris, Qt::WindingFill);
// construct kernel from image
Eigen::Matrix<qreal, Eigen::Dynamic, Eigen::Dynamic> irisKernel(kernelHeight, kernelWidth);
for (int j = 0; j < kernelHeight; ++j) {
for (int i = 0; i < kernelWidth; ++i) {
irisKernel(j, i) = qRed(kernelRepresentation.pixel(i, j));
}
}
// apply convolution
KisConvolutionPainter painter(device);
painter.setChannelFlags(channelFlags);
painter.setProgress(progressUpdater);
KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMatrix(irisKernel, 0, irisKernel.sum());
painter.applyMatrix(kernel, device, srcTopLeft, srcTopLeft, rect.size(), BORDER_REPEAT);
}
QRect KisLensBlurFilter::neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
const int halfWidth = t.scale(_config->getProperty("halfWidth", value) ? value.toUInt() : 5);
const int halfHeight = t.scale(_config->getProperty("halfHeight", value) ? value.toUInt() : 5);
return rect.adjusted(-halfWidth * 2, -halfHeight * 2, halfWidth * 2, halfHeight * 2);
}
QRect KisLensBlurFilter::changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
const int halfWidth = t.scale(_config->getProperty("halfWidth", value) ? value.toUInt() : 5);
const int halfHeight = t.scale(_config->getProperty("halfHeight", value) ? value.toUInt() : 5);
return rect.adjusted(-halfWidth, -halfHeight, halfWidth, halfHeight);
}
diff --git a/plugins/filters/blur/kis_lens_blur_filter.h b/plugins/filters/blur/kis_lens_blur_filter.h
index ef53307f9f..8059cf538d 100644
--- a/plugins/filters/blur/kis_lens_blur_filter.h
+++ b/plugins/filters/blur/kis_lens_blur_filter.h
@@ -1,59 +1,59 @@
/*
* This file is part of Krita
*
* Copyright (c) 2010 Edward Apap <schumifer@hotmail.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_LENS_BLUR_FILTER_H
#define KIS_LENS_BLUR_FILTER_H
#include "filter/kis_filter.h"
#include "ui_wdg_lens_blur.h"
#include <Eigen/Core>
class KisLensBlurFilter : public KisFilter
{
public:
KisLensBlurFilter();
public:
void processImpl(KisPaintDeviceSP src,
const QRect& size,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("lens blur", i18n("Lens Blur"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
static QSize getKernelHalfSize(const KisFilterConfigurationSP config, int lod);
QRect neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
QRect changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
private:
static QPolygonF getIrisPolygon(const KisFilterConfigurationSP config, int lod);
};
#endif
diff --git a/plugins/filters/blur/kis_motion_blur_filter.cpp b/plugins/filters/blur/kis_motion_blur_filter.cpp
index 74bc13ace5..5881f5a500 100644
--- a/plugins/filters/blur/kis_motion_blur_filter.cpp
+++ b/plugins/filters/blur/kis_motion_blur_filter.cpp
@@ -1,164 +1,163 @@
/*
* This file is part of Krita
*
* Copyright (c) 2010 Edward Apap <schumifer@hotmail.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_motion_blur_filter.h"
#include "kis_wdg_motion_blur.h"
#include <KoCompositeOp.h>
#include <kis_convolution_kernel.h>
#include <kis_convolution_painter.h>
#include "ui_wdg_motion_blur.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_lod_transform.h"
#include <QPainter>
#include <math.h>
KisMotionBlurFilter::KisMotionBlurFilter() : KisFilter(id(), FiltersCategoryBlurId, i18n("&Motion Blur..."))
{
setSupportsPainting(true);
setSupportsAdjustmentLayers(true);
setSupportsLevelOfDetail(true);
setColorSpaceIndependence(FULLY_INDEPENDENT);
}
KisConfigWidget * KisMotionBlurFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool) const
{
return new KisWdgMotionBlur(parent);
}
-KisFilterConfigurationSP KisMotionBlurFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisMotionBlurFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("blurAngle", 0);
config->setProperty("blurLength", 5);
return config;
}
namespace {
struct MotionBlurProperties
{
MotionBlurProperties(KisFilterConfigurationSP config, const KisLodTransformScalar &t)
{
const int blurAngle = config->getInt("blurAngle", 0);
const int blurLength = config->getInt("blurLength", 5);
// convert angle to radians
const qreal angleRadians = kisDegreesToRadians(qreal(blurAngle));
// construct image
const qreal halfWidth = 0.5 * t.scale(blurLength) * cos(angleRadians);
const qreal halfHeight = 0.5 * t.scale(blurLength) * sin(angleRadians);
kernelHalfSize.rwidth() = ceil(fabs(halfWidth));
kernelHalfSize.rheight() = ceil(fabs(halfHeight));
kernelSize = kernelHalfSize * 2 + QSize(1, 1);
this->blurLength = blurLength;
QPointF p1(0.5 * kernelSize.width(), 0.5 * kernelSize.height());
QPointF p2(halfWidth, halfHeight);
motionLine = QLineF(p1 - p2, p1 + p2);
}
int blurLength;
QSize kernelSize;
QSize kernelHalfSize;
QLineF motionLine;
};
}
void KisMotionBlurFilter::processImpl(KisPaintDeviceSP device,
const QRect& rect,
- const KisFilterConfigurationSP _config,
+ const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
QPoint srcTopLeft = rect.topLeft();
Q_ASSERT(device);
-
- KisFilterConfigurationSP config = _config ? _config : new KisFilterConfiguration(id().id(), 1);
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
KisLodTransformScalar t(device);
MotionBlurProperties props(config, t);
if (props.blurLength == 0) {
return;
}
QBitArray channelFlags;
if (config) {
channelFlags = config->channelFlags();
}
if (channelFlags.isEmpty() || !config) {
channelFlags = QBitArray(device->colorSpace()->channelCount(), true);
}
QImage kernelRepresentation(props.kernelSize, QImage::Format_RGB32);
kernelRepresentation.fill(0);
QPainter imagePainter(&kernelRepresentation);
imagePainter.setRenderHint(QPainter::Antialiasing);
imagePainter.setPen(QPen(QColor::fromRgb(255, 255, 255), 1.0));
imagePainter.drawLine(props.motionLine);
// construct kernel from image
Eigen::Matrix<qreal, Eigen::Dynamic, Eigen::Dynamic> motionBlurKernel(props.kernelSize.height(), props.kernelSize.width());
for (int j = 0; j < props.kernelSize.height(); ++j) {
for (int i = 0; i < props.kernelSize.width(); ++i) {
motionBlurKernel(j, i) = qRed(kernelRepresentation.pixel(i, j));
}
}
// apply convolution
KisConvolutionPainter painter(device);
painter.setChannelFlags(channelFlags);
painter.setProgress(progressUpdater);
KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMatrix(motionBlurKernel, 0, motionBlurKernel.sum());
painter.applyMatrix(kernel, device, srcTopLeft, srcTopLeft, rect.size(), BORDER_REPEAT);
}
QRect KisMotionBlurFilter::neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const
{
KisLodTransformScalar t(lod);
MotionBlurProperties props(_config, t);
return rect.adjusted(-props.kernelHalfSize.width(), -props.kernelHalfSize.height(), props.kernelHalfSize.width(), props.kernelHalfSize.height());
}
QRect KisMotionBlurFilter::changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const
{
return neededRect(rect, _config, lod);
}
diff --git a/plugins/filters/blur/kis_motion_blur_filter.h b/plugins/filters/blur/kis_motion_blur_filter.h
index 84748a70fe..48185c1d8d 100644
--- a/plugins/filters/blur/kis_motion_blur_filter.h
+++ b/plugins/filters/blur/kis_motion_blur_filter.h
@@ -1,52 +1,52 @@
/*
* This file is part of Krita
*
* Copyright (c) 2010 Edward Apap <schumifer@hotmail.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_MOTION_BLUR_FILTER_H
#define KIS_MOTION_BLUR_FILTER_H
#include "filter/kis_filter.h"
#include "ui_wdg_motion_blur.h"
#include <Eigen/Core>
class KisMotionBlurFilter : public KisFilter
{
public:
KisMotionBlurFilter();
public:
void processImpl(KisPaintDeviceSP src,
const QRect& size,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("motion blur", i18n("Motion Blur"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
QRect neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
QRect changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
};
#endif
diff --git a/plugins/filters/blur/kis_wdg_blur.cpp b/plugins/filters/blur/kis_wdg_blur.cpp
index 968dcd6cf1..5b6a80f0e0 100644
--- a/plugins/filters/blur/kis_wdg_blur.cpp
+++ b/plugins/filters/blur/kis_wdg_blur.cpp
@@ -1,118 +1,119 @@
/*
* 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_blur.h"
#include <QLayout>
#include <QToolButton>
#include <QIcon>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_selection.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
+#include <KisGlobalResourcesInterface.h>
#include "ui_wdgblur.h"
KisWdgBlur::KisWdgBlur(QWidget * parent) : KisConfigWidget(parent)
{
m_widget = new Ui_WdgBlur();
m_widget->setupUi(this);
linkSpacingToggled(true);
connect(widget()->aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(linkSpacingToggled(bool)));
connect(widget()->intHalfWidth, SIGNAL(valueChanged(int)), this, SLOT(spinBoxHalfWidthChanged(int)));
connect(widget()->intHalfHeight, SIGNAL(valueChanged(int)), this, SLOT(spinBoxHalfHeightChanged(int)));
connect(widget()->intStrength, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->intAngle, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->cbShape, SIGNAL(activated(int)), SIGNAL(sigConfigurationItemChanged()));
}
KisWdgBlur::~KisWdgBlur()
{
delete m_widget;
}
KisPropertiesConfigurationSP KisWdgBlur::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("blur", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("blur", 1, KisGlobalResourcesInterface::instance());
config->setProperty("lockAspect", widget()->aspectButton->keepAspectRatio());
config->setProperty("halfWidth", widget()->intHalfWidth->value());
config->setProperty("halfHeight", widget()->intHalfHeight->value());
config->setProperty("rotate", widget()->intAngle->value());
config->setProperty("strength", widget()->intStrength->value());
config->setProperty("shape", widget()->cbShape->currentIndex());
return config;
}
void KisWdgBlur::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("lockAspect", value)) {
m_widget->aspectButton->setKeepAspectRatio(value.toBool());
}
if (config->getProperty("shape", value)) {
widget()->cbShape->setCurrentIndex(value.toUInt());
}
if (config->getProperty("halfWidth", value)) {
widget()->intHalfWidth->setValue(value.toUInt());
}
if (config->getProperty("halfHeight", value)) {
widget()->intHalfHeight->setValue(value.toUInt());
}
if (config->getProperty("rotate", value)) {
widget()->intAngle->setValue(value.toUInt());
}
if (config->getProperty("strength", value)) {
widget()->intStrength->setValue(value.toUInt());
}
}
void KisWdgBlur::linkSpacingToggled(bool b)
{
m_halfSizeLink = b;
widget()->intHalfHeight->setValue(widget()->intHalfWidth->value());
}
void KisWdgBlur::spinBoxHalfWidthChanged(int v)
{
if (m_halfSizeLink) {
widget()->intHalfHeight->setValue(v);
}
/* if( widget()->intHalfHeight->value() == v && widget()->cbShape->currentItem() != 1)
widget()->intAngle->setEnabled(false);
else
widget()->intAngle->setEnabled(true);*/
emit sigConfigurationItemChanged();
}
void KisWdgBlur::spinBoxHalfHeightChanged(int v)
{
if (m_halfSizeLink) {
widget()->intHalfWidth->setValue(v);
}
/* if( widget()->intHalfWidth->value() == v && widget()->cbShape->currentItem() != 1)
widget()->intAngle->setEnabled(false);
else
widget()->intAngle->setEnabled(true);*/
emit sigConfigurationItemChanged();
}
diff --git a/plugins/filters/blur/kis_wdg_gaussian_blur.cpp b/plugins/filters/blur/kis_wdg_gaussian_blur.cpp
index 99ed865e38..8608834824 100644
--- a/plugins/filters/blur/kis_wdg_gaussian_blur.cpp
+++ b/plugins/filters/blur/kis_wdg_gaussian_blur.cpp
@@ -1,123 +1,124 @@
/*
* This file is part of Krita
*
* Copyright (c) 2009 Edward Apap <schumifer@hotmail.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_wdg_gaussian_blur.h"
#include <QLayout>
#include <QToolButton>
#include <QIcon>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_selection.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
+#include <KisGlobalResourcesInterface.h>
#include "ui_wdg_gaussian_blur.h"
KisWdgGaussianBlur::KisWdgGaussianBlur(bool usedForMasks, QWidget * parent) : KisConfigWidget(parent)
{
m_widget = new Ui_WdgGaussianBlur();
m_widget->setupUi(this);
m_widget->aspectButton->setKeepAspectRatio(false);
const qreal maxRadius = usedForMasks ? 100.0 : 1000.0;
m_widget->horizontalRadius->setRange(0.0, maxRadius, 2);
m_widget->horizontalRadius->setSingleStep(0.2);
m_widget->horizontalRadius->setValue(0.5);
m_widget->horizontalRadius->setExponentRatio(3.0);
m_widget->horizontalRadius->setSuffix(i18n(" px"));
connect(m_widget->horizontalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(horizontalRadiusChanged(qreal)));
m_widget->verticalRadius->setRange(0.0, maxRadius, 2);
m_widget->verticalRadius->setSingleStep(0.2);
m_widget->verticalRadius->setValue(0.5);
m_widget->verticalRadius->setExponentRatio(3.0);
m_widget->verticalRadius->setSuffix(i18n(" px"));
connect(m_widget->verticalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(verticalRadiusChanged(qreal)));
connect(m_widget->aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(aspectLockChanged(bool)));
connect(m_widget->horizontalRadius, SIGNAL(valueChanged(qreal)), SIGNAL(sigConfigurationItemChanged()));
connect(m_widget->verticalRadius, SIGNAL(valueChanged(qreal)), SIGNAL(sigConfigurationItemChanged()));
}
KisWdgGaussianBlur::~KisWdgGaussianBlur()
{
delete m_widget;
}
KisPropertiesConfigurationSP KisWdgGaussianBlur::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("gaussian blur", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("gaussian blur", 1, KisGlobalResourcesInterface::instance());
config->setProperty("horizRadius", m_widget->horizontalRadius->value());
config->setProperty("vertRadius", m_widget->verticalRadius->value());
config->setProperty("lockAspect", m_widget->aspectButton->keepAspectRatio());
return config;
}
void KisWdgGaussianBlur::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("horizRadius", value)) {
m_widget->horizontalRadius->setValue(value.toFloat());
}
if (config->getProperty("vertRadius", value)) {
m_widget->verticalRadius->setValue(value.toFloat());
}
if (config->getProperty("lockAspect", value)) {
m_widget->aspectButton->setKeepAspectRatio(value.toBool());
}
}
void KisWdgGaussianBlur::horizontalRadiusChanged(qreal v)
{
m_widget->horizontalRadius->blockSignals(true);
m_widget->horizontalRadius->setValue(v);
m_widget->horizontalRadius->blockSignals(false);
if (m_widget->aspectButton->keepAspectRatio()) {
m_widget->verticalRadius->blockSignals(true);
m_widget->verticalRadius->setValue(v);
m_widget->verticalRadius->blockSignals(false);
}
}
void KisWdgGaussianBlur::verticalRadiusChanged(qreal v)
{
m_widget->verticalRadius->blockSignals(true);
m_widget->verticalRadius->setValue(v);
m_widget->verticalRadius->blockSignals(false);
if (m_widget->aspectButton->keepAspectRatio()) {
m_widget->horizontalRadius->blockSignals(true);
m_widget->horizontalRadius->setValue(v);
m_widget->horizontalRadius->blockSignals(false);
}
}
void KisWdgGaussianBlur::aspectLockChanged(bool v)
{
if (v) {
m_widget->verticalRadius->setValue( m_widget->horizontalRadius->value() );
}
}
diff --git a/plugins/filters/blur/kis_wdg_lens_blur.cpp b/plugins/filters/blur/kis_wdg_lens_blur.cpp
index 74bb07a196..1a9ac67b5f 100644
--- a/plugins/filters/blur/kis_wdg_lens_blur.cpp
+++ b/plugins/filters/blur/kis_wdg_lens_blur.cpp
@@ -1,82 +1,83 @@
/*
* This file is part of Krita
*
* Copyright (c) 2010 Edward Apap <schumifer@hotmail.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_wdg_lens_blur.h"
#include <QLayout>
#include <QToolButton>
#include <QIcon>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_selection.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
+#include <KisGlobalResourcesInterface.h>
#include "kis_lens_blur_filter.h"
#include "ui_wdg_lens_blur.h"
KisWdgLensBlur::KisWdgLensBlur(QWidget * parent) : KisConfigWidget(parent)
{
m_widget = new Ui_WdgLensBlur();
m_widget->setupUi(this);
connect(m_widget->irisShapeCombo, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_widget->irisRadiusSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_widget->irisRotationSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
}
KisWdgLensBlur::~KisWdgLensBlur()
{
delete m_widget;
}
KisPropertiesConfigurationSP KisWdgLensBlur::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("lens blur", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("lens blur", 1, KisGlobalResourcesInterface::instance());
config->setProperty("irisShape", m_widget->irisShapeCombo->currentText());
config->setProperty("irisRadius", m_widget->irisRadiusSlider->value());
config->setProperty("irisRotation", m_widget->irisRotationSlider->value());
QSize halfSize = KisLensBlurFilter::getKernelHalfSize(config, 0);
config->setProperty("halfWidth", halfSize.width());
config->setProperty("halfHeight", halfSize.height());
return config;
}
void KisWdgLensBlur::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("irisShape", value)) {
for (int i = 0; i < m_widget->irisShapeCombo->count(); ++i) {
if (value.toString() == m_widget->irisShapeCombo->itemText(i)) {
m_widget->irisShapeCombo->setCurrentIndex(i);
}
}
}
if (config->getProperty("irisRadius", value)) {
m_widget->irisRadiusSlider->setValue(value.toInt());
}
if (config->getProperty("irisRotation", value)) {
m_widget->irisRotationSlider->setValue(value.toInt());
}
}
diff --git a/plugins/filters/blur/kis_wdg_motion_blur.cpp b/plugins/filters/blur/kis_wdg_motion_blur.cpp
index b3c325bd75..0f86598eb2 100644
--- a/plugins/filters/blur/kis_wdg_motion_blur.cpp
+++ b/plugins/filters/blur/kis_wdg_motion_blur.cpp
@@ -1,96 +1,97 @@
/*
* This file is part of Krita
*
* Copyright (c) 2010 Edward Apap <schumifer@hotmail.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_wdg_motion_blur.h"
#include <QLayout>
#include <QToolButton>
#include <QIcon>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_selection.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
+#include <KisGlobalResourcesInterface.h>
#include "ui_wdg_motion_blur.h"
KisWdgMotionBlur::KisWdgMotionBlur(QWidget * parent) : KisConfigWidget(parent)
{
m_widget = new Ui_WdgMotionBlur();
m_widget->setupUi(this);
connect(m_widget->blurAngleSlider, SIGNAL(valueChanged(int)), SLOT(angleSliderChanged(int)));
connect(m_widget->blurAngleDial, SIGNAL(valueChanged(int)), SLOT(angleDialChanged(int)));
connect(m_widget->blurAngleSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_widget->blurLength, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
}
KisWdgMotionBlur::~KisWdgMotionBlur()
{
delete m_widget;
}
KisPropertiesConfigurationSP KisWdgMotionBlur::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("motion blur", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("motion blur", 1, KisGlobalResourcesInterface::instance());
config->setProperty("blurAngle", m_widget->blurAngleSlider->value());
config->setProperty("blurLength", m_widget->blurLength->value());
return config;
}
void KisWdgMotionBlur::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("blurAngle", value)) {
m_widget->blurAngleSlider->setValue(value.toInt());
}
if (config->getProperty("blurLength", value)) {
m_widget->blurLength->setValue(value.toInt());
}
}
void KisWdgMotionBlur::angleSliderChanged(int v)
{
int absoluteValue = -v + 270;
if (absoluteValue < 0) {
absoluteValue += 360;
}
else if (absoluteValue > 360) {
absoluteValue = absoluteValue - 360;
}
m_widget->blurAngleDial->setValue(absoluteValue);
}
void KisWdgMotionBlur::angleDialChanged(int v)
{
int absoluteValue = v - 270;
if (absoluteValue < 0) {
absoluteValue = 360 + absoluteValue;
}
absoluteValue = -absoluteValue;
if (absoluteValue < 0) {
absoluteValue += 360;
}
m_widget->blurAngleSlider->setValue(absoluteValue);
}
diff --git a/plugins/filters/colors/kis_color_to_alpha.cpp b/plugins/filters/colors/kis_color_to_alpha.cpp
index 21173c2179..9bc7b296d2 100644
--- a/plugins/filters/colors/kis_color_to_alpha.cpp
+++ b/plugins/filters/colors/kis_color_to_alpha.cpp
@@ -1,189 +1,188 @@
/*
* 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, bool) const
{
return new KisWdgColorToAlpha(parent);
}
-KisFilterConfigurationSP KisFilterColorToAlpha::defaultConfiguration() const
+KisFilterConfigurationSP KisFilterColorToAlpha::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
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,
+ const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
Q_ASSERT(device != 0);
-
- KisFilterConfigurationSP config = _config ? _config : new KisFilterConfiguration("colortoalpha", 1);
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
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:
#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/colors/kis_color_to_alpha.h b/plugins/filters/colors/kis_color_to_alpha.h
index 5c71c8462d..c0ba4571e0 100644
--- a/plugins/filters/colors/kis_color_to_alpha.h
+++ b/plugins/filters/colors/kis_color_to_alpha.h
@@ -1,49 +1,49 @@
/*
* 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.
*/
#ifndef KIS_COLOR_TO_ALPHA_H_
#define KIS_COLOR_TO_ALPHA_H_
#include "filter/kis_filter.h"
class KisFilterColorToAlpha : public KisFilter
{
public:
KisFilterColorToAlpha();
void processImpl(KisPaintDeviceSP device,
const QRect& rect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("colortoalpha", i18n("Color to Alpha"));
}
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
};
#endif
diff --git a/plugins/filters/colors/kis_wdg_color_to_alpha.cpp b/plugins/filters/colors/kis_wdg_color_to_alpha.cpp
index 03a6eeaf48..ab4bcff7b4 100644
--- a/plugins/filters/colors/kis_wdg_color_to_alpha.cpp
+++ b/plugins/filters/colors/kis_wdg_color_to_alpha.cpp
@@ -1,125 +1,126 @@
/*
* 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_to_alpha.h"
#include <QCheckBox>
#include <QLayout>
#include <QSpinBox>
#include <KoColor.h>
#include <KoToolManager.h>
#include <KisViewManager.h>
#include <kis_canvas_resource_provider.h>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_selection.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
#include <KoColorSpaceRegistry.h>
+#include <KisGlobalResourcesInterface.h>
#include "ui_wdgcolortoalphabase.h"
KisWdgColorToAlpha::KisWdgColorToAlpha(QWidget * parent)
: KisConfigWidget(parent),
m_view(0)
{
m_widget = new Ui_WdgColorToAlphaBase();
m_widget->setupUi(this);
m_widget->textLabel1->hide();
m_widget->intThreshold->setRange(1, 255, 0);
connect(m_widget->colorSelector, SIGNAL(sigNewColor(KoColor)), SLOT(slotColorSelectorChanged(KoColor)));
connect(m_widget->intThreshold, SIGNAL(valueChanged(qreal)), SIGNAL(sigConfigurationItemChanged()));
connect(m_widget->btnCustomColor, SIGNAL(changed(KoColor)), SLOT(slotCustomColorSelected(KoColor)));
KoColor c(Qt::white, KoColorSpaceRegistry::instance()->rgb8());
m_widget->btnCustomColor->setColor(c);
}
KisWdgColorToAlpha::~KisWdgColorToAlpha()
{
delete m_widget;
}
void KisWdgColorToAlpha::setView(KisViewManager *view)
{
m_view = view;
}
void KisWdgColorToAlpha::slotFgColorChanged(const KoColor &color)
{
m_widget->btnCustomColor->setColor(color);
}
void KisWdgColorToAlpha::slotColorSelectorChanged(const KoColor &color)
{
m_widget->btnCustomColor->setColor(color);
}
void KisWdgColorToAlpha::slotCustomColorSelected(const KoColor &color)
{
KoColor c(color, KoColorSpaceRegistry::instance()->rgb8());
m_widget->colorSelector->slotSetColor(color);
emit sigConfigurationItemChanged();
}
void KisWdgColorToAlpha::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("targetcolor", value)) {
KoColor c;
if (value.value<QColor>().isValid()) {
c = KoColor(value.value<QColor>(), KoColorSpaceRegistry::instance()->rgb8());
} else {
c = value.value<KoColor>();
}
m_widget->colorSelector->slotSetColor(c);
}
if (config->getProperty("threshold", value)) {
m_widget->intThreshold->setValue(value.toInt());
}
}
KisPropertiesConfigurationSP KisWdgColorToAlpha::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("colortoalpha", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("colortoalpha", 1, KisGlobalResourcesInterface::instance());
config->setProperty("targetcolor", widget()->colorSelector->getCurrentColor().toQColor());
config->setProperty("threshold", widget()->intThreshold->value());
return config;
}
void KisWdgColorToAlpha::hideEvent(QHideEvent *)
{
if (m_view) {
disconnect(m_view->canvasResourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), this, SLOT(slotFgColorChanged(KoColor)));
}
}
void KisWdgColorToAlpha::showEvent(QShowEvent *)
{
if (m_view) {
connect(m_view->canvasResourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), this, SLOT(slotFgColorChanged(KoColor)));
}
}
diff --git a/plugins/filters/colorsfilters/kis_color_balance_filter.cpp b/plugins/filters/colorsfilters/kis_color_balance_filter.cpp
index 8c392a4fc8..17c4a91d9a 100644
--- a/plugins/filters/colorsfilters/kis_color_balance_filter.cpp
+++ b/plugins/filters/colorsfilters/kis_color_balance_filter.cpp
@@ -1,206 +1,207 @@
/*
* Copyright (c) 2013 Sahil Nagpal <nagpal.sahil01@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_color_balance_filter.h"
#include <filter/kis_filter_category_ids.h>
#include "filter/kis_color_transformation_configuration.h"
#include "kis_selection.h"
#include "kis_paint_device.h"
#include "kis_processing_information.h"
+#include <KisGlobalResourcesInterface.h>
KisColorBalanceFilter::KisColorBalanceFilter()
: KisColorTransformationFilter(id(), FiltersCategoryAdjustId, i18n("&Color Balance..."))
{
setShortcut(QKeySequence(Qt::CTRL + Qt::Key_B));
setSupportsPainting(true);
}
KisConfigWidget * KisColorBalanceFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
return new KisColorBalanceConfigWidget(parent);
}
KoColorTransformation * KisColorBalanceFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const
{
QHash<QString, QVariant> params;
if (config) {
params["cyan_red_midtones"] = config->getInt("cyan_red_midtones", 0) * 0.01;
params["magenta_green_midtones"] = config->getInt("magenta_green_midtones", 0) * 0.01;
params["yellow_blue_midtones"] = config->getInt("yellow_blue_midtones", 0) * 0.01;
params["cyan_red_shadows"] = config->getInt("cyan_red_shadows", 0) * 0.01;
params["magenta_green_shadows"] = config->getInt("magenta_green_shadows", 0) * 0.01;
params["yellow_blue_shadows"] = config->getInt("yellow_blue_shadows", 0) * 0.01;
params["cyan_red_highlights"] = config->getInt("cyan_red_highlights", 0) * 0.01;
params["magenta_green_highlights"] = config->getInt("magenta_green_highlights", 0) * 0.01;
params["yellow_blue_highlights"] = config->getInt("yellow_blue_highlights", 0) * 0.01;
params["preserve_luminosity"] = config->getBool("preserve_luminosity", true);
}
return cs->createColorTransformation("ColorBalance" , params);
}
-KisFilterConfigurationSP KisColorBalanceFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisColorBalanceFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("cyan_red_midtones", 0);
config->setProperty("yellow_green_midtones", 0);
config->setProperty("magenta_blue_midtones", 0);
config->setProperty("cyan_red_shadows", 0);
config->setProperty("yellow_green_shadows", 0);
config->setProperty("magenta_blue_shadows", 0);
config->setProperty("cyan_red_highlights", 0);
config->setProperty("yellow_green_highlights", 0);
config->setProperty("magenta_blue_highlights", 0);
config->setProperty("preserve_luminosity", true);
return config;
}
KisColorBalanceConfigWidget::KisColorBalanceConfigWidget(QWidget* parent) : KisConfigWidget(parent)
{
m_page = new Ui_Form();
m_page->setupUi(this);
m_page->cyanRedShadowsSlider->setMaximum(100);
m_page->cyanRedShadowsSlider->setMinimum(-100);
m_page->yellowBlueShadowsSlider->setMaximum(100);
m_page->yellowBlueShadowsSlider->setMinimum(-100);
m_page->magentaGreenShadowsSlider->setMaximum(100);
m_page->magentaGreenShadowsSlider->setMinimum(-100);
m_page->cyanRedMidtonesSlider->setMaximum(100);
m_page->cyanRedMidtonesSlider->setMinimum(-100);
m_page->yellowBlueMidtonesSlider->setMaximum(100);
m_page->yellowBlueMidtonesSlider->setMinimum(-100);
m_page->magentaGreenMidtonesSlider->setMaximum(100);
m_page->magentaGreenMidtonesSlider->setMinimum(-100);
m_page->cyanRedHighlightsSlider->setMaximum(100);
m_page->cyanRedHighlightsSlider->setMinimum(-100);
m_page->yellowBlueHighlightsSlider->setMaximum(100);
m_page->yellowBlueHighlightsSlider->setMinimum(-100);
m_page->magentaGreenHighlightsSlider->setMaximum(100);
m_page->magentaGreenHighlightsSlider->setMinimum(-100);
connect(m_page->cyanRedShadowsSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->magentaGreenShadowsSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->yellowBlueShadowsSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->cyanRedMidtonesSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->magentaGreenMidtonesSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->yellowBlueMidtonesSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->cyanRedHighlightsSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->magentaGreenHighlightsSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->yellowBlueHighlightsSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->chkPreserveLuminosity, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->pushResetShadows, SIGNAL(clicked()), SLOT(slotShadowsClear()));
connect(m_page->pushResetMidtones, SIGNAL(clicked()), SLOT(slotMidtonesClear()));
connect(m_page->pushResetHighlights, SIGNAL(clicked()), SLOT(slotHighlightsClear()));
m_page->cyanRedShadowsSpinbox->setMaximum(100);
m_page->cyanRedShadowsSpinbox->setMinimum(-100);
m_page->yellowBlueShadowsSpinbox->setMaximum(100);
m_page->yellowBlueShadowsSpinbox->setMinimum(-100);
m_page->magentaGreenShadowsSpinbox->setMaximum(100);
m_page->magentaGreenShadowsSpinbox->setMinimum(-100);
m_page->cyanRedMidtonesSpinbox->setMaximum(100);
m_page->cyanRedMidtonesSpinbox->setMinimum(-100);
m_page->yellowBlueMidtonesSpinbox->setMaximum(100);
m_page->yellowBlueMidtonesSpinbox->setMinimum(-100);
m_page->magentaGreenMidtonesSpinbox->setMaximum(100);
m_page->magentaGreenMidtonesSpinbox->setMinimum(-100);
m_page->cyanRedHighlightsSpinbox->setMaximum(100);
m_page->cyanRedHighlightsSpinbox->setMinimum(-100);
m_page->yellowBlueHighlightsSpinbox->setMaximum(100);
m_page->yellowBlueHighlightsSpinbox->setMinimum(-100);
m_page->magentaGreenHighlightsSpinbox->setMaximum(100);
m_page->magentaGreenHighlightsSpinbox->setMinimum(-100);
}
KisColorBalanceConfigWidget::~KisColorBalanceConfigWidget()
{
delete m_page;
}
KisPropertiesConfigurationSP KisColorBalanceConfigWidget::configuration() const
{
- KisColorTransformationConfigurationSP c = new KisColorTransformationConfiguration(KisColorBalanceFilter::id().id(), 0);
+ KisColorTransformationConfigurationSP c = new KisColorTransformationConfiguration(KisColorBalanceFilter::id().id(), 0, KisGlobalResourcesInterface::instance());
c->setProperty("cyan_red_shadows", m_page->cyanRedShadowsSlider->value());
c->setProperty("magenta_green_shadows", m_page->magentaGreenShadowsSlider->value());
c->setProperty("yellow_blue_shadows", m_page->yellowBlueShadowsSlider->value());
c->setProperty("cyan_red_midtones", m_page->cyanRedMidtonesSlider->value());
c->setProperty("magenta_green_midtones", m_page->magentaGreenMidtonesSlider->value());
c->setProperty("yellow_blue_midtones", m_page->yellowBlueMidtonesSlider->value());
c->setProperty("cyan_red_highlights", m_page->cyanRedHighlightsSlider->value());
c->setProperty("magenta_green_highlights", m_page->magentaGreenHighlightsSlider->value());
c->setProperty("yellow_blue_highlights", m_page->yellowBlueHighlightsSlider->value());
c->setProperty("preserve_luminosity", m_page->chkPreserveLuminosity->isChecked());
return c;
}
void KisColorBalanceConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
m_page->cyanRedMidtonesSlider->setValue( config->getDouble("cyan_red_midtones", 0));
m_page->magentaGreenMidtonesSlider->setValue( config->getDouble("magenta_green_midtones", 0));
m_page->yellowBlueMidtonesSlider->setValue( config->getDouble("yellow_blue_midtones", 0));
m_page->cyanRedShadowsSlider->setValue( config->getDouble("cyan_red_shadows", 0));
m_page->magentaGreenShadowsSlider->setValue( config->getDouble("magenta_green_shadows", 0));
m_page->yellowBlueShadowsSlider->setValue( config->getDouble("yellow_blue_shadows", 0));
m_page->cyanRedHighlightsSlider->setValue( config->getDouble("cyan_red_highlights", 0));
m_page->magentaGreenHighlightsSlider->setValue( config->getDouble("magenta_green_highlights", 0));
m_page->yellowBlueHighlightsSlider->setValue( config->getDouble("yellow_blue_highlights", 0));
m_page->chkPreserveLuminosity->setChecked(config->getBool("preserve_luminosity", true));
}
void KisColorBalanceConfigWidget::slotMidtonesClear()
{
m_page->cyanRedMidtonesSlider->setValue(0);
m_page->magentaGreenMidtonesSlider->setValue(0);
m_page->yellowBlueMidtonesSlider->setValue(0);
}
void KisColorBalanceConfigWidget::slotHighlightsClear()
{
m_page->cyanRedHighlightsSlider->setValue(0);
m_page->magentaGreenHighlightsSlider->setValue(0);
m_page->yellowBlueHighlightsSlider->setValue(0);
}
void KisColorBalanceConfigWidget::slotShadowsClear()
{
m_page->cyanRedShadowsSlider->setValue(0);
m_page->magentaGreenShadowsSlider->setValue(0);
m_page->yellowBlueShadowsSlider->setValue(0);
}
diff --git a/plugins/filters/colorsfilters/kis_color_balance_filter.h b/plugins/filters/colorsfilters/kis_color_balance_filter.h
index 490378c74d..eb12fc3148 100644
--- a/plugins/filters/colorsfilters/kis_color_balance_filter.h
+++ b/plugins/filters/colorsfilters/kis_color_balance_filter.h
@@ -1,80 +1,80 @@
/*
* Copyright (c) 2013 Sahil Nagpal <nagpal.sahil01@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_COLOR_BALANCE_FILTER_H_
#define _KIS_COLOR_BALANCE_FILTER_H_
#include <QList>
#include "filter/kis_filter.h"
#include "kis_config_widget.h"
#include "ui_wdg_color_balance.h"
#include "filter/kis_color_transformation_filter.h"
class QWidget;
class KoColorTransformation;
class KisColorBalanceFilter : public KisColorTransformationFilter
{
public:
KisColorBalanceFilter();
public:
enum Type {
SHADOWS,
MIDTONES,
HIGHLIGHTS
};
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const override;
static inline KoID id() {
return KoID("colorbalance", i18n("Color Balance"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
};
class KisColorBalanceConfigWidget : public KisConfigWidget
{
Q_OBJECT
public:
KisColorBalanceConfigWidget(QWidget * parent);
~KisColorBalanceConfigWidget() override;
KisPropertiesConfigurationSP configuration() const override;
void setConfiguration(const KisPropertiesConfigurationSP config) override;
Ui_Form * m_page;
QString m_id;
public Q_SLOTS:
void slotShadowsClear();
void slotMidtonesClear();
void slotHighlightsClear();
};
#endif
diff --git a/plugins/filters/colorsfilters/kis_cross_channel_filter.cpp b/plugins/filters/colorsfilters/kis_cross_channel_filter.cpp
index db93504a3c..c8d841ace7 100644
--- a/plugins/filters/colorsfilters/kis_cross_channel_filter.cpp
+++ b/plugins/filters/colorsfilters/kis_cross_channel_filter.cpp
@@ -1,293 +1,305 @@
/*
* 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_cross_channel_filter.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_configuration.h>
#include <kis_selection.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
#include <libs/global/kis_dom_utils.h>
#include "kis_histogram.h"
#include "kis_painter.h"
#include "widgets/kis_curve_widget.h"
+#include <KisGlobalResourcesInterface.h>
#include "../../color/colorspaceextensions/kis_hsv_adjustment.h"
// KisCrossChannelFilterConfiguration
-KisCrossChannelFilterConfiguration::KisCrossChannelFilterConfiguration(int channelCount, const KoColorSpace *cs)
- : KisMultiChannelFilterConfiguration(channelCount, "crosschannel", 1)
+KisCrossChannelFilterConfiguration::KisCrossChannelFilterConfiguration(int channelCount, const KoColorSpace *cs, KisResourcesInterfaceSP resourcesInterface)
+ : KisMultiChannelFilterConfiguration(channelCount, "crosschannel", 1, resourcesInterface)
{
init();
int defaultDriver = 0;
if (cs) {
QVector<VirtualChannelInfo> virtualChannels = KisMultiChannelFilter::getVirtualChannels(cs);
defaultDriver = qMax(0, KisMultiChannelFilter::findChannel(virtualChannels, VirtualChannelInfo::LIGHTNESS));
}
m_driverChannels.fill(defaultDriver, channelCount);
}
+KisCrossChannelFilterConfiguration::KisCrossChannelFilterConfiguration(const KisCrossChannelFilterConfiguration &rhs)
+ : KisMultiChannelFilterConfiguration(rhs),
+ m_driverChannels(rhs.m_driverChannels)
+{
+}
+
KisCrossChannelFilterConfiguration::~KisCrossChannelFilterConfiguration()
{}
+KisFilterConfigurationSP KisCrossChannelFilterConfiguration::clone() const
+{
+ return new KisCrossChannelFilterConfiguration(*this);
+}
+
const QVector<int> KisCrossChannelFilterConfiguration::driverChannels() const
{
return m_driverChannels;
}
void KisCrossChannelFilterConfiguration::setDriverChannels(QVector<int> driverChannels)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(driverChannels.size() == m_curves.size());
m_driverChannels = driverChannels;
}
void KisCrossChannelFilterConfiguration::fromXML(const QDomElement& root)
{
KisMultiChannelFilterConfiguration::fromXML(root);
m_driverChannels.resize(m_curves.size());
QRegExp rx("driver(\\d+)");
for (QDomElement e = root.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
const QString attributeName = e.attribute("name");
if (rx.exactMatch(attributeName)) {
int channel = rx.cap(1).toUShort();
int driver = KisDomUtils::toInt(e.text());
if (0 <= channel && channel < m_driverChannels.size()) {
m_driverChannels[channel] = driver;
}
}
}
}
void KisCrossChannelFilterConfiguration::toXML(QDomDocument& doc, QDomElement& root) const
{
KisMultiChannelFilterConfiguration::toXML(doc, root);
for (int i = 0; i < m_driverChannels.size(); i++) {
QDomElement param = doc.createElement("param");
param.setAttribute("name", QString("driver%1").arg(i));
QDomText text = doc.createTextNode(KisDomUtils::toString(m_driverChannels[i]));
param.appendChild(text);
root.appendChild(param);
}
}
KisCubicCurve KisCrossChannelFilterConfiguration::getDefaultCurve()
{
const QList<QPointF> points { QPointF(0.0f, 0.5f), QPointF(1.0f, 0.5f) };
return KisCubicCurve(points);
}
KisCrossChannelConfigWidget::KisCrossChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f)
: KisMultiChannelConfigWidget(parent, dev, f)
{
const int virtualChannelCount = m_virtualChannels.size();
m_driverChannels.resize(virtualChannelCount);
init();
for (int i = 0; i < virtualChannelCount; i++) {
const VirtualChannelInfo &info = m_virtualChannels[i];
if (info.type() == VirtualChannelInfo::ALL_COLORS) {
continue;
}
m_page->cmbDriverChannel->addItem(info.name(), i);
}
connect(m_page->cmbDriverChannel, SIGNAL(activated(int)), this, SLOT(slotDriverChannelSelected(int)));
}
// KisCrossChannelConfigWidget
KisCrossChannelConfigWidget::~KisCrossChannelConfigWidget()
{}
void KisCrossChannelConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
const auto *cfg = dynamic_cast<const KisCrossChannelFilterConfiguration*>(config.data());
m_driverChannels = cfg->driverChannels();
KisMultiChannelConfigWidget::setConfiguration(config);
// Show the first channel with a curve, or saturation by default
int initialChannel = -1;
for (int i = 0; i < m_virtualChannels.size(); i++) {
if (!m_curves[i].isConstant(0.5)) {
initialChannel = i;
break;
}
}
if (initialChannel < 0) {
initialChannel = qMax(0, KisMultiChannelFilter::findChannel(m_virtualChannels, VirtualChannelInfo::SATURATION));
}
setActiveChannel(initialChannel);
}
KisPropertiesConfigurationSP KisCrossChannelConfigWidget::configuration() const
{
- auto *cfg = new KisCrossChannelFilterConfiguration(m_virtualChannels.count(), m_dev->colorSpace());
+ auto *cfg = new KisCrossChannelFilterConfiguration(m_virtualChannels.count(), m_dev->colorSpace(), KisGlobalResourcesInterface::instance());
KisPropertiesConfigurationSP cfgSP = cfg;
m_curves[m_activeVChannel] = m_page->curveWidget->curve();
cfg->setCurves(m_curves);
cfg->setDriverChannels(m_driverChannels);
return cfgSP;
}
void KisCrossChannelConfigWidget::updateChannelControls()
{
m_page->curveWidget->setupInOutControls(m_page->intIn, m_page->intOut, 0, 100, -100, 100);
const int index = m_page->cmbDriverChannel->findData(m_driverChannels[m_activeVChannel]);
m_page->cmbDriverChannel->setCurrentIndex(index);
}
KisPropertiesConfigurationSP KisCrossChannelConfigWidget::getDefaultConfiguration()
{
- return new KisCrossChannelFilterConfiguration(m_virtualChannels.size(), m_dev->colorSpace());
+ return new KisCrossChannelFilterConfiguration(m_virtualChannels.size(), m_dev->colorSpace(), KisGlobalResourcesInterface::instance());
}
void KisCrossChannelConfigWidget::slotDriverChannelSelected(int index)
{
const int channel = m_page->cmbDriverChannel->itemData(index).toInt();
KIS_SAFE_ASSERT_RECOVER_RETURN(0 <= channel && channel < m_virtualChannels.size());
m_driverChannels[m_activeVChannel] = channel;
updateChannelControls();
}
// KisCrossChannelFilter
KisCrossChannelFilter::KisCrossChannelFilter() : KisMultiChannelFilter(id(), i18n("&Cross-channel adjustment curves..."))
{}
KisCrossChannelFilter::~KisCrossChannelFilter()
{}
KisConfigWidget * KisCrossChannelFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool) const
{
return new KisCrossChannelConfigWidget(parent, dev);
}
-KisFilterConfigurationSP KisCrossChannelFilter::factoryConfiguration() const
+KisFilterConfigurationSP KisCrossChannelFilter::factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- return new KisCrossChannelFilterConfiguration(0, nullptr);
+ return new KisCrossChannelFilterConfiguration(0, nullptr, resourcesInterface);
}
int mapChannel(const VirtualChannelInfo &channel) {
switch (channel.type()) {
case VirtualChannelInfo::REAL: {
int pixelIndex = channel.pixelIndex();
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(0 <= pixelIndex && pixelIndex < 4, 0);
return pixelIndex;
}
case VirtualChannelInfo::ALL_COLORS:
return KisHSVCurve::AllColors;
case VirtualChannelInfo::HUE:
return KisHSVCurve::Hue;
case VirtualChannelInfo::SATURATION:
return KisHSVCurve::Saturation;
case VirtualChannelInfo::LIGHTNESS:
return KisHSVCurve::Value;
};
KIS_SAFE_ASSERT_RECOVER_NOOP(false);
return 0;
}
KoColorTransformation* KisCrossChannelFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const
{
const KisCrossChannelFilterConfiguration* configBC =
dynamic_cast<const KisCrossChannelFilterConfiguration*>(config.data());
Q_ASSERT(configBC);
const QVector<QVector<quint16> > &originalTransfers = configBC->transfers();
const QList<KisCubicCurve> &curves = configBC->curves();
const QVector<int> &drivers = configBC->driverChannels();
const QVector<VirtualChannelInfo> virtualChannels =
KisMultiChannelFilter::getVirtualChannels(cs, originalTransfers.size());
if (originalTransfers.size() > int(virtualChannels.size())) {
// We got an illegal number of colorchannels :(
return 0;
}
QVector<KoColorTransformation*> transforms;
// Channel order reversed in order to adjust saturation before hue. This allows mapping grays to colors.
for (int i = virtualChannels.size() - 1; i >= 0; i--) {
if (!curves[i].isConstant(0.5)) {
int channel = mapChannel(virtualChannels[i]);
int driverChannel = mapChannel(virtualChannels[drivers[i]]);
QHash<QString, QVariant> params;
params["channel"] = channel;
params["driverChannel"] = driverChannel;
params["curve"] = QVariant::fromValue(originalTransfers[i]);
params["relative"] = true;
params["lumaRed"] = cs->lumaCoefficients()[0];
params["lumaGreen"] = cs->lumaCoefficients()[1];
params["lumaBlue"] = cs->lumaCoefficients()[2];
transforms << cs->createColorTransformation("hsv_curve_adjustment", params);
}
}
return KoCompositeColorTransformation::createOptimizedCompositeTransform(transforms);
}
diff --git a/plugins/filters/colorsfilters/kis_cross_channel_filter.h b/plugins/filters/colorsfilters/kis_cross_channel_filter.h
index 98d22e451b..a1b0b65829 100644
--- a/plugins/filters/colorsfilters/kis_cross_channel_filter.h
+++ b/plugins/filters/colorsfilters/kis_cross_channel_filter.h
@@ -1,102 +1,105 @@
/*
* 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.
*/
#ifndef _KIS_CROSSCHANNEL_FILTER_H_
#define _KIS_CROSSCHANNEL_FILTER_H_
#include <QPair>
#include <QList>
#include <filter/kis_color_transformation_filter.h>
#include <filter/kis_color_transformation_configuration.h>
#include <kis_config_widget.h>
#include <kis_paint_device.h>
#include "ui_wdg_perchannel.h"
#include "virtual_channel_info.h"
#include "kis_multichannel_filter_base.h"
/**
* Filter which applies a relative adjustment to a (virtual) color channel based on the value of another.
* The amount of adjustment for a given input is controlled by a user-defined curve.
*/
class KisCrossChannelFilter : public KisMultiChannelFilter
{
public:
KisCrossChannelFilter();
~KisCrossChannelFilter() override;
KisConfigWidget * createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
- KisFilterConfigurationSP factoryConfiguration() const override;
+ KisFilterConfigurationSP factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
KoColorTransformation* createTransformation(const KoColorSpace *cs, const KisFilterConfigurationSP config) const override;
static inline KoID id() {
return KoID("crosschannel", i18n("Cross-channel color adjustment"));
}
};
class KisCrossChannelFilterConfiguration : public KisMultiChannelFilterConfiguration
{
public:
- KisCrossChannelFilterConfiguration(int channelCount, const KoColorSpace *cs);
+ KisCrossChannelFilterConfiguration(int channelCount, const KoColorSpace *cs, KisResourcesInterfaceSP resourcesInterface);
+ KisCrossChannelFilterConfiguration(const KisCrossChannelFilterConfiguration&rhs);
~KisCrossChannelFilterConfiguration() override;
+ KisFilterConfigurationSP clone() const override;
+
const QVector<int> driverChannels() const;
void setDriverChannels(QVector<int> driverChannels);
using KisFilterConfiguration::fromXML;
using KisFilterConfiguration::toXML;
void fromXML(const QDomElement& e) override;
void toXML(QDomDocument& doc, QDomElement& root) const override;
KisCubicCurve getDefaultCurve() override;
private:
QVector<int> m_driverChannels;
};
class KisCrossChannelConfigWidget : public KisMultiChannelConfigWidget
{
Q_OBJECT
public:
KisCrossChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f = 0);
~KisCrossChannelConfigWidget() override;
void setConfiguration(const KisPropertiesConfigurationSP config) override;
KisPropertiesConfigurationSP configuration() const override;
protected:
void updateChannelControls() override;
virtual KisPropertiesConfigurationSP getDefaultConfiguration() override;
private Q_SLOTS:
void slotDriverChannelSelected(int index);
private:
QVector<int> m_driverChannels;
};
#endif
diff --git a/plugins/filters/colorsfilters/kis_desaturate_filter.cpp b/plugins/filters/colorsfilters/kis_desaturate_filter.cpp
index d703886a33..2acd5e7374 100644
--- a/plugins/filters/colorsfilters/kis_desaturate_filter.cpp
+++ b/plugins/filters/colorsfilters/kis_desaturate_filter.cpp
@@ -1,123 +1,124 @@
/*
* This file is part of Krita
*
* Copyright (c) 2004 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_desaturate_filter.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <QSlider>
#include <QPoint>
#include <QColor>
#include <QButtonGroup>
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include "KoBasicHistogramProducers.h"
#include <KoColorSpace.h>
#include <KoColorTransformation.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_color_transformation_configuration.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_global.h>
#include <kis_types.h>
#include <kis_selection.h>
#include "filter/kis_filter_registry.h"
#include <kis_painter.h>
#include <KoColorSpaceConstants.h>
#include <KoCompositeOp.h>
#include <kis_iterator_ng.h>
+#include <KisGlobalResourcesInterface.h>
KisDesaturateFilter::KisDesaturateFilter()
: KisColorTransformationFilter(id(), FiltersCategoryAdjustId, i18n("&Desaturate..."))
{
setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_U));
setSupportsPainting(true);
}
KisDesaturateFilter::~KisDesaturateFilter()
{
}
KisConfigWidget *KisDesaturateFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
return new KisDesaturateConfigWidget(parent);
}
KoColorTransformation* KisDesaturateFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const
{
QHash<QString, QVariant> params;
if (config) {
params["type"] = config->getInt("type", 0);
}
return cs->createColorTransformation("desaturate_adjustment", params);
}
-KisFilterConfigurationSP KisDesaturateFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisDesaturateFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("type", 0);
return config;
}
KisDesaturateConfigWidget::KisDesaturateConfigWidget(QWidget * parent, Qt::WindowFlags f) : KisConfigWidget(parent, f)
{
m_page = new Ui_WdgDesaturate();
m_page->setupUi(this);
m_group = new QButtonGroup(this);
m_group->addButton(m_page->radioLightness, 0);
m_group->addButton(m_page->radioLuminosityBT709, 1);
m_group->addButton(m_page->radioLuminosityBT601, 2);
m_group->addButton(m_page->radioAverage, 3);
m_group->addButton(m_page->radioMin, 4);
m_group->addButton(m_page->radioMax, 5);
m_group->setExclusive(true);
connect(m_group, SIGNAL(buttonClicked(int)), SIGNAL(sigConfigurationItemChanged()));
}
KisDesaturateConfigWidget::~KisDesaturateConfigWidget()
{
delete m_page;
}
KisPropertiesConfigurationSP KisDesaturateConfigWidget::configuration() const
{
- KisColorTransformationConfigurationSP c = new KisColorTransformationConfiguration(KisDesaturateFilter::id().id(), 0);
+ KisColorTransformationConfigurationSP c = new KisColorTransformationConfiguration(KisDesaturateFilter::id().id(), 0, KisGlobalResourcesInterface::instance());
c->setProperty("type", m_group->checkedId());
return c;
}
void KisDesaturateConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
m_group->button(config->getInt("type", 0))->setChecked(true);
emit sigConfigurationItemChanged();
}
diff --git a/plugins/filters/colorsfilters/kis_desaturate_filter.h b/plugins/filters/colorsfilters/kis_desaturate_filter.h
index b97f90ee94..c3cd8b528a 100644
--- a/plugins/filters/colorsfilters/kis_desaturate_filter.h
+++ b/plugins/filters/colorsfilters/kis_desaturate_filter.h
@@ -1,70 +1,70 @@
/*
* This file is part of Krita
*
* Copyright (c) 2004 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_DESATURATE_FILTER_H
#define KIS_DESATURATE_FILTER_H
#include <QObject>
#include <QVariant>
#include <kis_config_widget.h>
#include <filter/kis_color_transformation_filter.h>
#include "ui_wdg_desaturate.h"
class KoColorSpace;
class KoColorTransformation;
class KisDesaturateFilter : public KisColorTransformationFilter
{
public:
KisDesaturateFilter();
~KisDesaturateFilter() override;
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const override;
static inline KoID id() {
return KoID("desaturate", i18n("Desaturate"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
};
class KisDesaturateConfigWidget : public KisConfigWidget
{
Q_OBJECT
public:
KisDesaturateConfigWidget(QWidget * parent, Qt::WindowFlags f = 0);
~KisDesaturateConfigWidget() override;
KisPropertiesConfigurationSP configuration() const override;
void setConfiguration(const KisPropertiesConfigurationSP config) override;
Ui_WdgDesaturate *m_page;
QButtonGroup *m_group;
};
#endif
diff --git a/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.cpp b/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.cpp
index 2622af8d5f..7ffad6a0d8 100644
--- a/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.cpp
+++ b/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.cpp
@@ -1,221 +1,222 @@
/*
* 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
* 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; 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_hsv_adjustment_filter.h"
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_color_transformation_configuration.h>
#include <kis_selection.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
#include <KoColorProfile.h>
+#include <KisGlobalResourcesInterface.h>
namespace {
struct SliderConfig {
QString m_text;
int m_minimum;
int m_maximum;
inline void apply(QSpinBox* spinBox, QSlider* slider, QLabel* label) const
{
label->setText(m_text);
slider->setMinimum(m_minimum);
slider->setMaximum(m_maximum);
spinBox->setMinimum(m_minimum);
spinBox->setMaximum(m_maximum);
int sliderValue = slider->value();
if (sliderValue < m_minimum || sliderValue > m_maximum) {
slider->setValue((m_minimum + m_maximum) / 2);
}
}
inline double normalize(int value) const
{
return (double)value / (double)m_maximum;
}
inline void resetSlider( QSlider* slider) const
{
slider->setValue(0);
}
};
struct WidgetSlidersConfig {
SliderConfig m_sliders[3];
};
#define PERCENT_FIELD_REL(x) {x, -100, 100}
#define PERCENT_FIELD_ABS(x) {x, 0, 100}
#define DEGREES_FIELD_REL(x) {x, -180, 180}
#define DEGREES_FIELD_ABS(x) {x, 0, 360}
#define HSX_CONFIGS(x) { \
{ {DEGREES_FIELD_REL(i18n("Hue:")), PERCENT_FIELD_REL(i18n("Saturation:")), PERCENT_FIELD_REL(x)} }, \
{ {DEGREES_FIELD_ABS(i18n("Hue:")), PERCENT_FIELD_ABS(i18n("Saturation:")), PERCENT_FIELD_REL(x)} } \
}
const WidgetSlidersConfig WIDGET_CONFIGS[][2] = {
// Hue/Saturation/Value
HSX_CONFIGS(i18n("Value:")),
// Hue/Saturation/Lightness
HSX_CONFIGS(i18n("Lightness:")),
// Hue/Saturation/Intensity
HSX_CONFIGS(i18n("Intensity:")),
// Hue/Saturation/Luminosity
HSX_CONFIGS(i18n("Luma:")),
// Blue Chroma/Red Chroma/Luma
{{ {PERCENT_FIELD_REL(i18n("Yellow-Blue:")), PERCENT_FIELD_REL(i18n("Green-Red:")), PERCENT_FIELD_REL(i18n("Luma:"))} },
{ {PERCENT_FIELD_ABS(i18n("Yellow-Blue:")), PERCENT_FIELD_ABS(i18n("Green-Red:")), PERCENT_FIELD_REL(i18n("Luma:"))} }}
};
inline const WidgetSlidersConfig& getCurrentWidgetConfig(int type, bool colorize) {
return WIDGET_CONFIGS[type][colorize ? 1 : 0];
}
}
KisHSVAdjustmentFilter::KisHSVAdjustmentFilter()
: KisColorTransformationFilter(id(), FiltersCategoryAdjustId, i18n("&HSV Adjustment..."))
{
setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U));
setSupportsPainting(true);
}
KisConfigWidget * KisHSVAdjustmentFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
return new KisHSVConfigWidget(parent);
}
KoColorTransformation* KisHSVAdjustmentFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const
{
QHash<QString, QVariant> params;
if (config) {
int type = config->getInt("type", 1);
bool colorize = config->getBool("colorize", false);
bool compatibilityMode = config->getBool("compatibilityMode", true);
const WidgetSlidersConfig& widgetConfig = getCurrentWidgetConfig(type, colorize);
params["h"] = widgetConfig.m_sliders[0].normalize(config->getInt("h", 0));
params["s"] = widgetConfig.m_sliders[1].normalize(config->getInt("s", 0));
params["v"] = widgetConfig.m_sliders[2].normalize(config->getInt("v", 0));
params["type"] = type;
params["colorize"] = colorize;
params["lumaRed"] = cs->lumaCoefficients()[0];
params["lumaGreen"] = cs->lumaCoefficients()[1];
params["lumaBlue"] = cs->lumaCoefficients()[2];
params["compatibilityMode"] = compatibilityMode;
}
return cs->createColorTransformation("hsv_adjustment", params);
}
-KisFilterConfigurationSP KisHSVAdjustmentFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisHSVAdjustmentFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("h", 0);
config->setProperty("s", 0);
config->setProperty("v", 0);
config->setProperty("type", 1);
config->setProperty("colorize", false);
config->setProperty("compatibilityMode", false);
return config;
}
KisHSVConfigWidget::KisHSVConfigWidget(QWidget * parent, Qt::WindowFlags f) : KisConfigWidget(parent, f)
{
m_page = new Ui_WdgHSVAdjustment();
m_page->setupUi(this);
connect(m_page->cmbType, SIGNAL(activated(int)), this, SLOT(configureSliderLimitsAndLabels()));
connect(m_page->chkColorize, SIGNAL(toggled(bool)), this, SLOT(configureSliderLimitsAndLabels()));
connect(m_page->chkCompatibilityMode, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->reset,SIGNAL(clicked(bool)),this,SLOT(resetFilter()));
// connect horizontal sliders
connect(m_page->hueSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->saturationSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->valueSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->hueSpinBox, SIGNAL(valueChanged(int)), m_page->hueSlider, SLOT(setValue(int)));
connect(m_page->saturationSpinBox, SIGNAL(valueChanged(int)), m_page->saturationSlider, SLOT(setValue(int)));
connect(m_page->valueSpinBox, SIGNAL(valueChanged(int)), m_page->valueSlider, SLOT(setValue(int)));
connect(m_page->hueSlider, SIGNAL(valueChanged(int)), m_page->hueSpinBox, SLOT(setValue(int)));
connect(m_page->saturationSlider, SIGNAL(valueChanged(int)), m_page->saturationSpinBox, SLOT(setValue(int)));
connect(m_page->valueSlider, SIGNAL(valueChanged(int)), m_page->valueSpinBox, SLOT(setValue(int)));
}
KisHSVConfigWidget::~KisHSVConfigWidget()
{
delete m_page;
}
KisPropertiesConfigurationSP KisHSVConfigWidget::configuration() const
{
- KisColorTransformationConfigurationSP c = new KisColorTransformationConfiguration(KisHSVAdjustmentFilter::id().id(), 0);
+ KisColorTransformationConfigurationSP c = new KisColorTransformationConfiguration(KisHSVAdjustmentFilter::id().id(), 0, KisGlobalResourcesInterface::instance());
c->setProperty("h", m_page->hueSlider->value());
c->setProperty("s", m_page->saturationSlider->value());
c->setProperty("v", m_page->valueSlider->value());
c->setProperty("type", m_page->cmbType->currentIndex());
c->setProperty("colorize", m_page->chkColorize->isChecked());
c->setProperty("compatibilityMode", m_page->chkCompatibilityMode->isChecked());
return c;
}
void KisHSVConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
m_page->cmbType->setCurrentIndex(config->getInt("type", 1));
m_page->chkColorize->setChecked(config->getBool("colorize", false));
m_page->hueSlider->setValue(config->getInt("h", 0));
m_page->saturationSlider->setValue(config->getInt("s", 0));
m_page->valueSlider->setValue(config->getInt("v", 0));
m_page->chkCompatibilityMode->setChecked(config->getInt("compatibilityMode", true));
configureSliderLimitsAndLabels();
}
void KisHSVConfigWidget::configureSliderLimitsAndLabels()
{
const WidgetSlidersConfig& widget = getCurrentWidgetConfig(m_page->cmbType->currentIndex(), m_page->chkColorize->isChecked());
widget.m_sliders[0].apply(m_page->hueSpinBox, m_page->hueSlider, m_page->label);
widget.m_sliders[1].apply(m_page->saturationSpinBox, m_page->saturationSlider, m_page->label_2);
widget.m_sliders[2].apply(m_page->valueSpinBox, m_page->valueSlider, m_page->label_3);
const bool compatibilityEnabled =
!m_page->chkColorize->isChecked() &&
m_page->cmbType->currentIndex() >= 0 && m_page->cmbType->currentIndex() <= 3;
m_page->chkCompatibilityMode->setEnabled(compatibilityEnabled);
emit sigConfigurationItemChanged();
}
void KisHSVConfigWidget::resetFilter()
{
const WidgetSlidersConfig& widget = getCurrentWidgetConfig(m_page->cmbType->currentIndex(), m_page->chkColorize->isChecked());
widget.m_sliders[0].resetSlider(m_page->hueSlider);
widget.m_sliders[1].resetSlider(m_page->saturationSlider);
widget.m_sliders[2].resetSlider(m_page->valueSlider);
}
diff --git a/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.h b/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.h
index 333df12b2d..dcd9644e2d 100644
--- a/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.h
+++ b/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.h
@@ -1,78 +1,78 @@
/*
* 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
* 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; 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_HSV_ADJUSTMENT_FILTER_H_
#define _KIS_HSV_ADJUSTMENT_FILTER_H_
#include <QList>
#include "filter/kis_filter.h"
#include "kis_config_widget.h"
#include "ui_wdg_hsv_adjustment.h"
#include "filter/kis_color_transformation_filter.h"
class QWidget;
class KoColorTransformation;
/**
* This class affect Intensity Y of the image
*/
class KisHSVAdjustmentFilter : public KisColorTransformationFilter
{
public:
KisHSVAdjustmentFilter();
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const override;
static inline KoID id() {
return KoID("hsvadjustment", i18n("HSV/HSL Adjustment"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
};
class KisHSVConfigWidget : public KisConfigWidget
{
Q_OBJECT
public:
KisHSVConfigWidget(QWidget * parent, Qt::WindowFlags f = 0);
~KisHSVConfigWidget() override;
KisPropertiesConfigurationSP configuration() const override;
void setConfiguration(const KisPropertiesConfigurationSP config) override;
Ui_WdgHSVAdjustment * m_page;
private Q_SLOTS:
void configureSliderLimitsAndLabels();
void resetFilter();
};
#endif
diff --git a/plugins/filters/colorsfilters/kis_multichannel_filter_base.cpp b/plugins/filters/colorsfilters/kis_multichannel_filter_base.cpp
index 5a56750637..a3c26959ee 100644
--- a/plugins/filters/colorsfilters/kis_multichannel_filter_base.cpp
+++ b/plugins/filters/colorsfilters/kis_multichannel_filter_base.cpp
@@ -1,523 +1,531 @@
/*
* 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 <QMessageBox>
#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)
+KisMultiChannelFilterConfiguration::KisMultiChannelFilterConfiguration(int channelCount, const QString & name, qint32 version, KisResourcesInterfaceSP resourcesInterface)
+ : KisColorTransformationConfiguration(name, version, resourcesInterface)
, m_channelCount(channelCount)
{
m_transfers.resize(m_channelCount);
}
+KisMultiChannelFilterConfiguration::KisMultiChannelFilterConfiguration(const KisMultiChannelFilterConfiguration &rhs)
+ : KisColorTransformationConfiguration(rhs),
+ m_channelCount(rhs.m_channelCount),
+ m_curves(rhs.m_curves),
+ m_transfers(rhs.m_transfers)
+{
+}
+
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()) {
QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The current configuration was created for a different colorspace and cannot be used. All curves will be reset."));
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/filters/colorsfilters/kis_multichannel_filter_base.h b/plugins/filters/colorsfilters/kis_multichannel_filter_base.h
index 4a3f5e4263..517eec6076 100644
--- a/plugins/filters/colorsfilters/kis_multichannel_filter_base.h
+++ b/plugins/filters/colorsfilters/kis_multichannel_filter_base.h
@@ -1,138 +1,139 @@
/*
* 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.
*/
#ifndef _KIS_MULTICHANNEL_FILTER_BASE_H_
#define _KIS_MULTICHANNEL_FILTER_BASE_H_
#include <QPair>
#include <QList>
#include <filter/kis_color_transformation_filter.h>
#include <filter/kis_color_transformation_configuration.h>
#include <kis_config_widget.h>
#include <kis_paint_device.h>
#include "ui_wdg_perchannel.h"
#include "virtual_channel_info.h"
/**
* Base class for filters which use curves to operate on multiple channels.
*/
class KisMultiChannelFilter : public KisColorTransformationFilter
{
public:
bool needsTransparentPixels(const KisFilterConfigurationSP config, const KoColorSpace *cs) const override;
/**
* Get a list of adjustable channels for the color space.
* If maxChannels is non-negative, the number of channels is capped to the number. This is useful configurations
* from older documents (created in versions which supported fewer channels).
*/
static QVector<VirtualChannelInfo> getVirtualChannels(const KoColorSpace *cs, int maxChannels = -1);
static int findChannel(const QVector<VirtualChannelInfo> &virtualChannels, const VirtualChannelInfo::Type &channelType);
protected:
KisMultiChannelFilter(const KoID &id, const QString &entry);
};
/**
* Base class for configurations of KisMultiChannelFilter subclasses
*/
class KisMultiChannelFilterConfiguration : public KisColorTransformationConfiguration
{
public:
- KisMultiChannelFilterConfiguration(int channelCount, const QString & name, qint32 version);
+ KisMultiChannelFilterConfiguration(int channelCount, const QString & name, qint32 version, KisResourcesInterfaceSP resourcesInterface);
+ KisMultiChannelFilterConfiguration(const KisMultiChannelFilterConfiguration &rhs);
~KisMultiChannelFilterConfiguration() override;
using KisFilterConfiguration::fromXML;
using KisFilterConfiguration::toXML;
using KisFilterConfiguration::fromLegacyXML;
void fromLegacyXML(const QDomElement& root) override;
void fromXML(const QDomElement& e) override;
void toXML(QDomDocument& doc, QDomElement& root) const override;
void setCurves(QList<KisCubicCurve> &curves) override;
bool isCompatible(const KisPaintDeviceSP) const override;
const QVector<QVector<quint16> >& transfers() const;
const QList<KisCubicCurve>& curves() const override;
protected:
int m_channelCount;
QList<KisCubicCurve> m_curves;
QVector<QVector<quint16>> m_transfers;
void init();
void updateTransfers();
virtual KisCubicCurve getDefaultCurve() = 0;
};
class WdgPerChannel : public QWidget, public Ui::WdgPerChannel
{
Q_OBJECT
public:
WdgPerChannel(QWidget *parent) : QWidget(parent) {
setupUi(this);
}
};
/**
* Base class for configuration widgets of KisMultiChannelFilter subclasses
*/
class KisMultiChannelConfigWidget : public KisConfigWidget
{
Q_OBJECT
public:
KisMultiChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f = 0);
~KisMultiChannelConfigWidget() override;
void setConfiguration(const KisPropertiesConfigurationSP config) override;
protected Q_SLOTS:
void logHistView();
void resetCurve();
void slotChannelSelected(int index);
protected:
void init();
void resetCurves();
void setActiveChannel(int ch);
virtual void updateChannelControls() = 0;
virtual KisPropertiesConfigurationSP getDefaultConfiguration() = 0;
inline QPixmap getHistogram();
inline QPixmap createGradient(Qt::Orientation orient /*, int invert (not used now) */);
QVector<VirtualChannelInfo> m_virtualChannels;
int m_activeVChannel = 0;
mutable QList<KisCubicCurve> m_curves;
KisPaintDeviceSP m_dev;
WdgPerChannel * m_page;
KisHistogram *m_histogram;
};
#endif
diff --git a/plugins/filters/colorsfilters/kis_perchannel_filter.cpp b/plugins/filters/colorsfilters/kis_perchannel_filter.cpp
index efe442eb01..9868a397c7 100644
--- a/plugins/filters/colorsfilters/kis_perchannel_filter.cpp
+++ b/plugins/filters/colorsfilters/kis_perchannel_filter.cpp
@@ -1,323 +1,334 @@
/*
* This file is part of Krita
*
* 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_perchannel_filter.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_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"
+#include <KisGlobalResourcesInterface.h>
#include "../../color/colorspaceextensions/kis_hsv_adjustment.h"
KisPerChannelConfigWidget::KisPerChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f)
: KisMultiChannelConfigWidget(parent, dev, f)
{
init();
// These are not used by this filter,
// but the dialog is shared with KisCrossChannelFilter
m_page->lblDriverChannel->hide();
m_page->cmbDriverChannel->hide();
}
KisPerChannelConfigWidget::~KisPerChannelConfigWidget()
{}
#define BITS_PER_BYTE 8
#define pwr2(p) (1<<p)
void KisPerChannelConfigWidget::updateChannelControls()
{
// Getting range accepted by channel
VirtualChannelInfo &currentVChannel = m_virtualChannels[m_activeVChannel];
KoChannelInfo::enumChannelValueType valueType = currentVChannel.valueType();
int order = BITS_PER_BYTE * currentVChannel.channelSize();
int maxValue = pwr2(order);
int min;
int max;
m_page->curveWidget->dropInOutControls();
switch (valueType) {
case KoChannelInfo::UINT8:
case KoChannelInfo::UINT16:
case KoChannelInfo::UINT32:
min = 0;
max = maxValue - 1;
break;
case KoChannelInfo::INT8:
case KoChannelInfo::INT16:
min = -maxValue / 2;
max = maxValue / 2 - 1;
break;
case KoChannelInfo::FLOAT16:
case KoChannelInfo::FLOAT32:
case KoChannelInfo::FLOAT64:
default:
//Hack Alert: should be changed to float
if (m_dev->colorSpace()->colorModelId() == LABAColorModelID || m_dev->colorSpace()->colorModelId() == CMYKAColorModelID) {
min = m_dev->colorSpace()->channels()[m_activeVChannel]->getUIMin();
max = m_dev->colorSpace()->channels()[m_activeVChannel]->getUIMax();
} else {
min = 0;
max = 100;
}
break;
}
m_page->curveWidget->setupInOutControls(m_page->intIn, m_page->intOut, min, max, min, max);
}
KisPropertiesConfigurationSP KisPerChannelConfigWidget::configuration() const
{
int numChannels = m_virtualChannels.size();
- KisPropertiesConfigurationSP cfg = new KisPerChannelFilterConfiguration(numChannels);
+ KisPropertiesConfigurationSP cfg = new KisPerChannelFilterConfiguration(numChannels, KisGlobalResourcesInterface::instance());
KIS_ASSERT_RECOVER(m_activeVChannel < m_curves.size()) { return cfg; }
m_curves[m_activeVChannel] = m_page->curveWidget->curve();
static_cast<KisPerChannelFilterConfiguration*>(cfg.data())->setCurves(m_curves);
return cfg;
}
KisPropertiesConfigurationSP KisPerChannelConfigWidget::getDefaultConfiguration()
{
- return new KisPerChannelFilterConfiguration(m_virtualChannels.size());
+ return new KisPerChannelFilterConfiguration(m_virtualChannels.size(), KisGlobalResourcesInterface::instance());
}
-KisPerChannelFilterConfiguration::KisPerChannelFilterConfiguration(int channelCount)
- : KisMultiChannelFilterConfiguration(channelCount, "perchannel", 1)
+KisPerChannelFilterConfiguration::KisPerChannelFilterConfiguration(int channelCount, KisResourcesInterfaceSP resourcesInterface)
+ : KisMultiChannelFilterConfiguration(channelCount, "perchannel", 1, resourcesInterface)
{
init();
}
+KisPerChannelFilterConfiguration::KisPerChannelFilterConfiguration(const KisPerChannelFilterConfiguration &rhs)
+ : KisMultiChannelFilterConfiguration(rhs)
+{
+}
+
KisPerChannelFilterConfiguration::~KisPerChannelFilterConfiguration()
{
}
+KisFilterConfigurationSP KisPerChannelFilterConfiguration::clone() const
+{
+ return new KisPerChannelFilterConfiguration(*this);
+}
+
KisCubicCurve KisPerChannelFilterConfiguration::getDefaultCurve()
{
return KisCubicCurve();
}
// KisPerChannelFilter
KisPerChannelFilter::KisPerChannelFilter() : KisMultiChannelFilter(id(), i18n("&Color Adjustment curves..."))
{
setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M));
}
KisConfigWidget * KisPerChannelFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool) const
{
return new KisPerChannelConfigWidget(parent, dev);
}
-KisFilterConfigurationSP KisPerChannelFilter::factoryConfiguration() const
+KisFilterConfigurationSP KisPerChannelFilter::factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- return new KisPerChannelFilterConfiguration(0);
+ return new KisPerChannelFilterConfiguration(0, resourcesInterface);
}
KoColorTransformation* KisPerChannelFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const
{
const KisPerChannelFilterConfiguration* configBC =
dynamic_cast<const KisPerChannelFilterConfiguration*>(config.data()); // Somehow, this shouldn't happen
Q_ASSERT(configBC);
const QVector<QVector<quint16> > &originalTransfers = configBC->transfers();
const QList<KisCubicCurve> &originalCurves = configBC->curves();
/**
* TODO: What about the order of channels? (DK)
*
* Virtual channels are sorted in display order, does Lcms accepts
* transforms in display order? Why on Earth it works?! Is it
* documented anywhere?
*/
const QVector<VirtualChannelInfo> virtualChannels =
KisMultiChannelFilter::getVirtualChannels(cs, originalTransfers.size());
if (originalTransfers.size() > int(virtualChannels.size())) {
// We got an illegal number of colorchannels :(
return 0;
}
bool colorsNull = true;
bool hueNull = true;
bool saturationNull = true;
bool lightnessNull = true;
bool allColorsNull = true;
int alphaIndexInReal = -1;
QVector<QVector<quint16> > realTransfers;
QVector<quint16> hueTransfer;
QVector<quint16> saturationTransfer;
QVector<quint16> lightnessTransfer;
QVector<quint16> allColorsTransfer;
for (int i = 0; i < virtualChannels.size(); i++) {
if (virtualChannels[i].type() == VirtualChannelInfo::REAL) {
realTransfers << originalTransfers[i];
if (virtualChannels[i].isAlpha()) {
alphaIndexInReal = realTransfers.size() - 1;
}
if (colorsNull && !originalCurves[i].isIdentity()) {
colorsNull = false;
}
} else if (virtualChannels[i].type() == VirtualChannelInfo::HUE) {
KIS_ASSERT_RECOVER_NOOP(hueTransfer.isEmpty());
hueTransfer = originalTransfers[i];
if (hueNull && !originalCurves[i].isIdentity()) {
hueNull = false;
}
} else if (virtualChannels[i].type() == VirtualChannelInfo::SATURATION) {
KIS_ASSERT_RECOVER_NOOP(saturationTransfer.isEmpty());
saturationTransfer = originalTransfers[i];
if (saturationNull && !originalCurves[i].isIdentity()) {
saturationNull = false;
}
} else if (virtualChannels[i].type() == VirtualChannelInfo::LIGHTNESS) {
KIS_ASSERT_RECOVER_NOOP(lightnessTransfer.isEmpty());
lightnessTransfer = originalTransfers[i];
if (lightnessNull && !originalCurves[i].isIdentity()) {
lightnessNull = false;
}
} else if (virtualChannels[i].type() == VirtualChannelInfo::ALL_COLORS) {
KIS_ASSERT_RECOVER_NOOP(allColorsTransfer.isEmpty());
allColorsTransfer = originalTransfers[i];
if (allColorsNull && !originalCurves[i].isIdentity()) {
allColorsNull = false;
}
}
}
KoColorTransformation *hueTransform = 0;
KoColorTransformation *saturationTransform = 0;
KoColorTransformation *lightnessTransform = 0;
KoColorTransformation *allColorsTransform = 0;
KoColorTransformation *colorTransform = 0;
if (!colorsNull) {
const quint16** transfers = new const quint16*[realTransfers.size()];
for(int i = 0; i < realTransfers.size(); ++i) {
transfers[i] = realTransfers[i].constData();
/**
* createPerChannelAdjustment() expects alpha channel to
* be the last channel in the list, so just it here
*/
KIS_ASSERT_RECOVER_NOOP(i != alphaIndexInReal ||
alphaIndexInReal == (realTransfers.size() - 1));
}
colorTransform = cs->createPerChannelAdjustment(transfers);
delete [] transfers;
}
if (!hueNull) {
QHash<QString, QVariant> params;
params["curve"] = QVariant::fromValue(hueTransfer);
params["channel"] = KisHSVCurve::Hue;
params["relative"] = false;
params["lumaRed"] = cs->lumaCoefficients()[0];
params["lumaGreen"] = cs->lumaCoefficients()[1];
params["lumaBlue"] = cs->lumaCoefficients()[2];
hueTransform = cs->createColorTransformation("hsv_curve_adjustment", params);
}
if (!saturationNull) {
QHash<QString, QVariant> params;
params["curve"] = QVariant::fromValue(saturationTransfer);
params["channel"] = KisHSVCurve::Saturation;
params["relative"] = false;
params["lumaRed"] = cs->lumaCoefficients()[0];
params["lumaGreen"] = cs->lumaCoefficients()[1];
params["lumaBlue"] = cs->lumaCoefficients()[2];
saturationTransform = cs->createColorTransformation("hsv_curve_adjustment", params);
}
if (!lightnessNull) {
lightnessTransform = cs->createBrightnessContrastAdjustment(lightnessTransfer.constData());
}
if (!allColorsNull) {
const quint16** allColorsTransfers = new const quint16*[realTransfers.size()];
for(int i = 0; i < realTransfers.size(); ++i) {
allColorsTransfers[i] = (i != alphaIndexInReal) ?
allColorsTransfer.constData() : 0;
/**
* createPerChannelAdjustment() expects alpha channel to
* be the last channel in the list, so just it here
*/
KIS_ASSERT_RECOVER_NOOP(i != alphaIndexInReal ||
alphaIndexInReal == (realTransfers.size() - 1));
}
allColorsTransform = cs->createPerChannelAdjustment(allColorsTransfers);
delete[] allColorsTransfers;
}
QVector<KoColorTransformation*> allTransforms;
allTransforms << colorTransform;
allTransforms << allColorsTransform;
allTransforms << hueTransform;
allTransforms << saturationTransform;
allTransforms << lightnessTransform;
return KoCompositeColorTransformation::createOptimizedCompositeTransform(allTransforms);
}
diff --git a/plugins/filters/colorsfilters/kis_perchannel_filter.h b/plugins/filters/colorsfilters/kis_perchannel_filter.h
index 279e26b044..3f09c526b3 100644
--- a/plugins/filters/colorsfilters/kis_perchannel_filter.h
+++ b/plugins/filters/colorsfilters/kis_perchannel_filter.h
@@ -1,81 +1,84 @@
/*
* This file is part of Krita
*
* Copyright (c) 2004 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_PERCHANNEL_FILTER_H_
#define _KIS_PERCHANNEL_FILTER_H_
#include <QPair>
#include <QList>
#include <filter/kis_color_transformation_filter.h>
#include <filter/kis_color_transformation_configuration.h>
#include <kis_config_widget.h>
#include <kis_paint_device.h>
#include "virtual_channel_info.h"
#include "kis_multichannel_filter_base.h"
class KisPerChannelFilterConfiguration
: public KisMultiChannelFilterConfiguration
{
public:
- KisPerChannelFilterConfiguration(int channelCount);
+ KisPerChannelFilterConfiguration(int channelCount, KisResourcesInterfaceSP resourcesInterface);
+ KisPerChannelFilterConfiguration(const KisPerChannelFilterConfiguration &rhs);
~KisPerChannelFilterConfiguration() override;
+ KisFilterConfigurationSP clone() const override;
+
KisCubicCurve getDefaultCurve() override;
};
/**
* This class is a filter to adjust channels independently
*/
class KisPerChannelFilter : public KisMultiChannelFilter
{
public:
KisPerChannelFilter();
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
- KisFilterConfigurationSP factoryConfiguration() const override;
+ KisFilterConfigurationSP factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const override;
static inline KoID id() {
return KoID("perchannel", i18n("Color Adjustment"));
}
};
class KisPerChannelConfigWidget : public KisMultiChannelConfigWidget
{
Q_OBJECT
public:
KisPerChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f = 0);
~KisPerChannelConfigWidget() override;
KisPropertiesConfigurationSP configuration() const override;
protected:
void updateChannelControls() override;
virtual KisPropertiesConfigurationSP getDefaultConfiguration() override;
};
#endif
diff --git a/plugins/filters/convertheightnormalmap/kis_convert_height_to_normal_map_filter.cpp b/plugins/filters/convertheightnormalmap/kis_convert_height_to_normal_map_filter.cpp
index ff82aea2f9..f327e77ece 100644
--- a/plugins/filters/convertheightnormalmap/kis_convert_height_to_normal_map_filter.cpp
+++ b/plugins/filters/convertheightnormalmap/kis_convert_height_to_normal_map_filter.cpp
@@ -1,171 +1,172 @@
/*
* 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.
*/
#include "kis_convert_height_to_normal_map_filter.h"
#include "kis_wdg_convert_height_to_normal_map.h"
#include <kpluginfactory.h>
#include <klocalizedstring.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_registry.h>
#include <filter/kis_filter_configuration.h>
#include "kis_lod_transform.h"
#include <kis_edge_detection_kernel.h>
K_PLUGIN_FACTORY_WITH_JSON(KritaConvertHeightToNormalMapFilterFactory, "kritaconvertheighttonormalmap.json", registerPlugin<KritaConvertHeightToNormalMapFilter>();)
KritaConvertHeightToNormalMapFilter::KritaConvertHeightToNormalMapFilter(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisFilterRegistry::instance()->add(KisFilterSP(new KisConvertHeightToNormalMapFilter()));
}
KritaConvertHeightToNormalMapFilter::~KritaConvertHeightToNormalMapFilter()
{
}
KisConvertHeightToNormalMapFilter::KisConvertHeightToNormalMapFilter(): KisFilter(id(), FiltersCategoryEdgeDetectionId, i18n("&Height to Normal Map..."))
{
setSupportsPainting(true);
setSupportsAdjustmentLayers(true);
setSupportsLevelOfDetail(true);
setColorSpaceIndependence(FULLY_INDEPENDENT);
setShowConfigurationWidget(true);
}
void KisConvertHeightToNormalMapFilter::processImpl(KisPaintDeviceSP device, const QRect &rect, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const
{
Q_ASSERT(device);
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
- KisFilterConfigurationSP configuration = config ? config : new KisFilterConfiguration(id().id(), 1);
+ KisFilterConfigurationSP configuration = config;
KisLodTransformScalar t(device);
QVariant value;
float horizontalRadius = 1.0;
if (configuration->getProperty("horizRadius", value)) {
horizontalRadius = t.scale(value.toFloat());
}
float verticalRadius = 1.0;
if (configuration->getProperty("vertRadius", value)) {
verticalRadius = t.scale(value.toFloat());
}
QBitArray channelFlags;
if (configuration) {
channelFlags = configuration->channelFlags();
}
KisEdgeDetectionKernel::FilterType type = KisEdgeDetectionKernel::SobelVector;
if (configuration->getString("type") == "prewitt") {
type = KisEdgeDetectionKernel::Prewit;
} else if (configuration->getString("type") == "simple") {
type = KisEdgeDetectionKernel::Simple;
}
int channelToConvert = configuration->getInt("channelToConvert", 0);
QVector<int> channelOrder(3);
QVector<bool> channelFlip(3);
channelFlip.fill(false);
int i = config->getInt("redSwizzle", 0);
if (i%2==1 || i==2) {
channelFlip[0] = true;
}
if (i==3) {
channelFlip[0] = false;
}
channelOrder[device->colorSpace()->channels().at(0)->displayPosition()] = qMax(i/2,0);
i = config->getInt("greenSwizzle", 2);
if (i%2==1 || i==2) {
channelFlip[1] = true;
}
if (i==3) {
channelFlip[1] = false;
}
channelOrder[device->colorSpace()->channels().at(1)->displayPosition()] = qMax(i/2,0);
i = config->getInt("blueSwizzle", 4);
if (i%2==1 || i==2) {
channelFlip[2] = true;
}
if (i==3) {
channelFlip[2] = false;
}
channelOrder[device->colorSpace()->channels().at(2)->displayPosition()] = qMax(i/2,0);
KisEdgeDetectionKernel::convertToNormalMap(device,
rect,
horizontalRadius,
verticalRadius,
type,
channelToConvert,
channelOrder,
channelFlip,
channelFlags,
progressUpdater);
}
-KisFilterConfigurationSP KisConvertHeightToNormalMapFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisConvertHeightToNormalMapFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("horizRadius", 1);
config->setProperty("vertRadius", 1);
config->setProperty("type", "sobol");
config->setProperty("channelToConvert", 0);
config->setProperty("lockAspect", true);
config->setProperty("redSwizzle", KisWdgConvertHeightToNormalMap::xPlus);
config->setProperty("greenSwizzle", KisWdgConvertHeightToNormalMap::yPlus);
config->setProperty("blueSwizzle", KisWdgConvertHeightToNormalMap::zPlus);
return config;
}
KisConfigWidget *KisConvertHeightToNormalMapFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool) const
{
return new KisWdgConvertHeightToNormalMap(parent, dev->colorSpace());
}
QRect KisConvertHeightToNormalMapFilter::neededRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
/**
* NOTE: integer division by two is done on purpose,
* because the kernel size is always odd
*/
const int halfWidth = _config->getProperty("horizRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
const int halfHeight = _config->getProperty("vertRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
return rect.adjusted(-halfWidth * 2, -halfHeight * 2, halfWidth * 2, halfHeight * 2);
}
QRect KisConvertHeightToNormalMapFilter::changedRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
const int halfWidth = _config->getProperty("horizRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
const int halfHeight = _config->getProperty("vertRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
return rect.adjusted( -halfWidth, -halfHeight, halfWidth, halfHeight);
}
#include "kis_convert_height_to_normal_map_filter.moc"
diff --git a/plugins/filters/convertheightnormalmap/kis_convert_height_to_normal_map_filter.h b/plugins/filters/convertheightnormalmap/kis_convert_height_to_normal_map_filter.h
index 64a24a937d..b828771580 100644
--- a/plugins/filters/convertheightnormalmap/kis_convert_height_to_normal_map_filter.h
+++ b/plugins/filters/convertheightnormalmap/kis_convert_height_to_normal_map_filter.h
@@ -1,52 +1,52 @@
/*
* 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_CONVERT_HEIGHT_TO_NORMAL_MAP_FILTER_H
#define KIS_CONVERT_HEIGHT_TO_NORMAL_MAP_FILTER_H
#include "filter/kis_filter.h"
class KritaConvertHeightToNormalMapFilter : public QObject
{
Q_OBJECT
public:
KritaConvertHeightToNormalMapFilter(QObject *parent, const QVariantList &);
~KritaConvertHeightToNormalMapFilter() override;
};
class KisConvertHeightToNormalMapFilter : public KisFilter
{
public:
KisConvertHeightToNormalMapFilter();
void processImpl(KisPaintDeviceSP device,
const QRect& rect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("height to normal", i18n("Height to Normal Map"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
QRect neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
QRect changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
};
#endif // KIS_CONVERT_HEIGHT_TO_NORMAL_MAP_FILTER_H
diff --git a/plugins/filters/convertheightnormalmap/kis_wdg_convert_height_to_normal_map.cpp b/plugins/filters/convertheightnormalmap/kis_wdg_convert_height_to_normal_map.cpp
index b91901cb73..89dbe8cfe2 100644
--- a/plugins/filters/convertheightnormalmap/kis_wdg_convert_height_to_normal_map.cpp
+++ b/plugins/filters/convertheightnormalmap/kis_wdg_convert_height_to_normal_map.cpp
@@ -1,132 +1,133 @@
/*
* 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.
*/
#include "kis_wdg_convert_height_to_normal_map.h"
#include <filter/kis_filter_configuration.h>
#include <QComboBox>
#include <klocalizedstring.h>
#include <KoChannelInfo.h>
+#include <KisGlobalResourcesInterface.h>
KisWdgConvertHeightToNormalMap::KisWdgConvertHeightToNormalMap(QWidget *parent, const KoColorSpace *cs) :
KisConfigWidget(parent),
ui(new Ui_WidgetConvertHeightToNormalMap),
m_cs(cs)
{
ui->setupUi(this);
m_types << "prewitt"<< "sobol"<< "simple";
m_types_translatable << i18n("Prewitt") << i18n("Sobel") << i18n("Simple");
QStringList swizzle;
swizzle<< "X+" << "X-" << "Y+" << "Y-" << "Z+" << "Z-";
ui->cmbType->addItems(m_types_translatable);
ui->cmbRed->addItems(swizzle);
ui->cmbGreen->addItems(swizzle);
ui->cmbBlue->addItems(swizzle);
for (int c = 0; c < (int)m_cs->channelCount(); c++) {
ui->cmbChannel->addItem(m_cs->channels().at(c)->name());
}
ui->btnAspect->setKeepAspectRatio(false);
ui->sldHorizontalRadius->setRange(1.0, 100.0, 2);
ui->sldHorizontalRadius->setPrefix(i18n("Horizontal Radius:"));
connect(ui->sldHorizontalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(horizontalRadiusChanged(qreal)));
ui->sldVerticalRadius->setRange(1.0, 100.0, 2);
ui->sldVerticalRadius->setPrefix(i18n("Vertical Radius:"));
connect(ui->sldVerticalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(verticalRadiusChanged(qreal)));
connect(ui->sldHorizontalRadius, SIGNAL(valueChanged(qreal)), this, SIGNAL(sigConfigurationItemChanged()));
connect(ui->sldVerticalRadius, SIGNAL(valueChanged(qreal)), this, SIGNAL(sigConfigurationItemChanged()));
connect(ui->btnAspect, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(aspectLockChanged(bool)));
connect(ui->cmbType, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged()));
connect(ui->cmbChannel, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged()));
connect(ui->cmbRed, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged()));
connect(ui->cmbGreen, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged()));
connect(ui->cmbBlue, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged()));
}
KisWdgConvertHeightToNormalMap::~KisWdgConvertHeightToNormalMap()
{
delete ui;
}
KisPropertiesConfigurationSP KisWdgConvertHeightToNormalMap::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("height to normal", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("height to normal", 1, KisGlobalResourcesInterface::instance());
config->setProperty("horizRadius", ui->sldHorizontalRadius->value());
config->setProperty("vertRadius", ui->sldVerticalRadius->value());
config->setProperty("type", m_types.at(ui->cmbType->currentIndex()));
config->setProperty("lockAspect", ui->btnAspect->keepAspectRatio());
config->setProperty("channelToConvert", ui->cmbChannel->currentIndex());
config->setProperty("redSwizzle", ui->cmbRed->currentIndex());
config->setProperty("greenSwizzle", ui->cmbGreen->currentIndex());
config->setProperty("blueSwizzle", ui->cmbBlue->currentIndex());
return config;
}
void KisWdgConvertHeightToNormalMap::setConfiguration(const KisPropertiesConfigurationSP config)
{
ui->sldHorizontalRadius->setValue(config->getFloat("horizRadius", 1.0));
ui->sldVerticalRadius->setValue(config->getFloat("vertRadius", 1.0));
int index = 0;
if (m_types.contains(config->getString("type", "prewitt"))){
index = m_types.indexOf(config->getString("type", "sobol"));
}
ui->cmbType->setCurrentIndex(index);
ui->cmbChannel->setCurrentIndex(config->getInt("channelToConvert", 0));
ui->btnAspect->setKeepAspectRatio(config->getBool("lockAspect", false));
ui->cmbRed->setCurrentIndex(config->getInt("redSwizzle", xPlus));
ui->cmbGreen->setCurrentIndex(config->getInt("greenSwizzle", yPlus));
ui->cmbBlue->setCurrentIndex(config->getInt("blueSwizzle", zPlus));
}
void KisWdgConvertHeightToNormalMap::horizontalRadiusChanged(qreal r)
{
ui->sldHorizontalRadius->blockSignals(true);
ui->sldHorizontalRadius->setValue(r);
ui->sldHorizontalRadius->blockSignals(false);
if (ui->btnAspect->keepAspectRatio()) {
ui->sldVerticalRadius->blockSignals(true);
ui->sldVerticalRadius->setValue(r);
ui->sldVerticalRadius->blockSignals(false);
}
}
void KisWdgConvertHeightToNormalMap::verticalRadiusChanged(qreal r)
{
ui->sldVerticalRadius->blockSignals(true);
ui->sldVerticalRadius->setValue(r);
ui->sldVerticalRadius->blockSignals(false);
if (ui->btnAspect->keepAspectRatio()) {
ui->sldHorizontalRadius->blockSignals(true);
ui->sldHorizontalRadius->setValue(r);
ui->sldHorizontalRadius->blockSignals(false);
}
}
void KisWdgConvertHeightToNormalMap::aspectLockChanged(bool v)
{
if (v) {
ui->sldVerticalRadius->setValue( ui->sldHorizontalRadius->value() );
}
}
diff --git a/plugins/filters/dodgeburn/DodgeBurn.cpp b/plugins/filters/dodgeburn/DodgeBurn.cpp
index f5526f255c..4ed9c60fa5 100644
--- a/plugins/filters/dodgeburn/DodgeBurn.cpp
+++ b/plugins/filters/dodgeburn/DodgeBurn.cpp
@@ -1,113 +1,114 @@
/*
* 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 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 "DodgeBurn.h"
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_color_transformation_configuration.h>
#include <kis_paint_device.h>
+#include <KisGlobalResourcesInterface.h>
#include "ui_DodgeBurnConfigurationBaseWidget.h"
KisFilterDodgeBurn::KisFilterDodgeBurn(const QString& id, const QString& prefix, const QString& name ) : KisColorTransformationFilter(KoID(id, name), FiltersCategoryAdjustId, name), m_prefix(prefix)
{
setColorSpaceIndependence(FULLY_INDEPENDENT);
setSupportsPainting(true);
}
KisConfigWidget * KisFilterDodgeBurn::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
return new KisDodgeBurnConfigWidget(parent, id());
}
KoColorTransformation* KisFilterDodgeBurn::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const
{
QHash<QString, QVariant> params;
QString suffix = "Midtones";
if (config) {
params["exposure"] = config->getDouble("exposure", 0.5);
int type = config->getInt("type", KisFilterDodgeBurn::MIDTONES);
switch(type)
{
case KisFilterDodgeBurn::HIGHLIGHTS:
suffix = "Highlights";
break;
case KisFilterDodgeBurn::SHADOWS:
suffix = "Shadows";
break;
default:
break;
}
}
return cs->createColorTransformation(m_prefix + suffix, params);
}
KisDodgeBurnConfigWidget::KisDodgeBurnConfigWidget(QWidget * parent, const QString& id) : KisConfigWidget(parent), m_id(id)
{
m_page = new Ui_DodgeBurnConfigurationBaseWidget();
m_page->setupUi(this);
connect(m_page->radioButtonHighlights, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->radioButtonMidtones, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->radioButtonShadows, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->sliderExposure, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
}
KisDodgeBurnConfigWidget::~KisDodgeBurnConfigWidget()
{
delete m_page;
}
KisPropertiesConfigurationSP KisDodgeBurnConfigWidget::configuration() const
{
- KisColorTransformationConfigurationSP c = new KisColorTransformationConfiguration(m_id, 0);
+ KisColorTransformationConfigurationSP c = new KisColorTransformationConfiguration(m_id, 0, KisGlobalResourcesInterface::instance());
int type = 0;
if(m_page->radioButtonHighlights->isChecked())
{
type = KisFilterDodgeBurn::HIGHLIGHTS;
} else if(m_page->radioButtonShadows->isChecked())
{
type = KisFilterDodgeBurn::SHADOWS;
} else {
type = KisFilterDodgeBurn::MIDTONES;
}
c->setProperty("type", type);
c->setProperty("exposure", m_page->sliderExposure->value() / 100.0);
return c;
}
void KisDodgeBurnConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
int type = config->getInt("type", KisFilterDodgeBurn::MIDTONES);
switch(type)
{
case KisFilterDodgeBurn::HIGHLIGHTS:
m_page->radioButtonHighlights->setChecked(true);
break;
case KisFilterDodgeBurn::SHADOWS:
m_page->radioButtonShadows->setChecked(true);
break;
default:
case KisFilterDodgeBurn::MIDTONES:
m_page->radioButtonMidtones->setChecked(true);
break;
}
m_page->sliderExposure->setValue(config->getDouble("exposure", 0.5) * 100);
}
diff --git a/plugins/filters/edgedetection/kis_edge_detection_filter.cpp b/plugins/filters/edgedetection/kis_edge_detection_filter.cpp
index 0564f73efc..8bacba1af1 100644
--- a/plugins/filters/edgedetection/kis_edge_detection_filter.cpp
+++ b/plugins/filters/edgedetection/kis_edge_detection_filter.cpp
@@ -1,156 +1,157 @@
/*
* 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.
*/
#include "kis_edge_detection_filter.h"
#include "kis_wdg_edge_detection.h"
#include <kis_edge_detection_kernel.h>
#include <kis_convolution_kernel.h>
#include <kis_convolution_painter.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.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_lod_transform.h"
#include <kpluginfactory.h>
#include <klocalizedstring.h>
#include <filter/kis_filter_registry.h>
K_PLUGIN_FACTORY_WITH_JSON(KritaEdgeDetectionFilterFactory, "kritaedgedetection.json", registerPlugin<KritaEdgeDetectionFilter>();)
KritaEdgeDetectionFilter::KritaEdgeDetectionFilter(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisFilterRegistry::instance()->add(KisFilterSP(new KisEdgeDetectionFilter()));
}
KritaEdgeDetectionFilter::~KritaEdgeDetectionFilter()
{
}
KisEdgeDetectionFilter::KisEdgeDetectionFilter(): KisFilter(id(), FiltersCategoryEdgeDetectionId, i18n("&Edge Detection..."))
{
setSupportsPainting(true);
setSupportsAdjustmentLayers(true);
setSupportsLevelOfDetail(true);
setColorSpaceIndependence(FULLY_INDEPENDENT);
setShowConfigurationWidget(true);
}
void KisEdgeDetectionFilter::processImpl(KisPaintDeviceSP device, const QRect &rect, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const
{
Q_ASSERT(device != 0);
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
- KisFilterConfigurationSP configuration = config ? config : new KisFilterConfiguration(id().id(), 1);
+ KisFilterConfigurationSP configuration = config;
KisLodTransformScalar t(device);
QVariant value;
configuration->getProperty("horizRadius", value);
float horizontalRadius = t.scale(value.toFloat());
configuration->getProperty("vertRadius", value);
float verticalRadius = t.scale(value.toFloat());
QBitArray channelFlags;
if (configuration) {
channelFlags = configuration->channelFlags();
}
KisEdgeDetectionKernel::FilterType type = KisEdgeDetectionKernel::SobelVector;
if (config->getString("type") == "prewitt") {
type = KisEdgeDetectionKernel::Prewit;
} else if (config->getString("type") == "simple") {
type = KisEdgeDetectionKernel::Simple;
}
KisEdgeDetectionKernel::FilterOutput output = KisEdgeDetectionKernel::pythagorean;
if (config->getString("output") == "xGrowth") {
output = KisEdgeDetectionKernel::xGrowth;
} else if (config->getString("output") == "xFall") {
output = KisEdgeDetectionKernel::xFall;
} else if (config->getString("output") == "yGrowth") {
output = KisEdgeDetectionKernel::yGrowth;
} else if (config->getString("output") == "yFall") {
output = KisEdgeDetectionKernel::yFall;
} else if (config->getString("output") == "radian") {
output = KisEdgeDetectionKernel::radian;
}
KisEdgeDetectionKernel::applyEdgeDetection(device,
rect,
horizontalRadius,
verticalRadius,
type,
channelFlags,
progressUpdater,
output,
config->getBool("transparency", false));
}
-KisFilterConfigurationSP KisEdgeDetectionFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisEdgeDetectionFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("horizRadius", 1);
config->setProperty("vertRadius", 1);
config->setProperty("type", "prewitt");
config->setProperty("output", "pythagorean");
config->setProperty("lockAspect", true);
config->setProperty("transparency", false);
return config;
}
KisConfigWidget *KisEdgeDetectionFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
return new KisWdgEdgeDetection(parent);
}
QRect KisEdgeDetectionFilter::neededRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
/**
* NOTE: integer division by two is done on purpose,
* because the kernel size is always odd
*/
const int halfWidth = _config->getProperty("horizRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
const int halfHeight = _config->getProperty("vertRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
return rect.adjusted(-halfWidth * 2, -halfHeight * 2, halfWidth * 2, halfHeight * 2);
}
QRect KisEdgeDetectionFilter::changedRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
const int halfWidth = _config->getProperty("horizRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
const int halfHeight = _config->getProperty("vertRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
return rect.adjusted( -halfWidth, -halfHeight, halfWidth, halfHeight);
}
#include "kis_edge_detection_filter.moc"
diff --git a/plugins/filters/edgedetection/kis_edge_detection_filter.h b/plugins/filters/edgedetection/kis_edge_detection_filter.h
index 9bde8cc5dd..c6c35c5299 100644
--- a/plugins/filters/edgedetection/kis_edge_detection_filter.h
+++ b/plugins/filters/edgedetection/kis_edge_detection_filter.h
@@ -1,53 +1,53 @@
/*
* 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_FILTER_H
#define KIS_EDGE_DETECTION_FILTER_H
#include "filter/kis_filter.h"
#include <Eigen/Core>
class KritaEdgeDetectionFilter : public QObject
{
Q_OBJECT
public:
KritaEdgeDetectionFilter(QObject *parent, const QVariantList &);
~KritaEdgeDetectionFilter() override;
};
class KisEdgeDetectionFilter : public KisFilter
{
public:
KisEdgeDetectionFilter();
void processImpl(KisPaintDeviceSP device,
const QRect& rect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("edge detection", i18n("Edge Detection"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
QRect neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
QRect changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
};
#endif // KIS_EDGE_DETECTION_FILTER_H
diff --git a/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp b/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp
index 2828216ebe..3e58911d27 100644
--- a/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp
+++ b/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp
@@ -1,129 +1,130 @@
/*
* 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.
*/
#include "kis_wdg_edge_detection.h"
#include <filter/kis_filter_configuration.h>
#include <QComboBox>
#include <klocalizedstring.h>
+#include <KisGlobalResourcesInterface.h>
KisWdgEdgeDetection::KisWdgEdgeDetection(QWidget *parent) :
KisConfigWidget(parent),
ui(new Ui_WidgetEdgeDetection)
{
ui->setupUi(this);
m_types << "prewitt"<< "sobol"<< "simple";
m_types_translatable << i18n("Prewitt") << i18n("Sobel") << i18n("Simple");
m_output << "pythagorean" << "xGrowth" << "xFall" << "yGrowth" << "yFall" << "radian";
m_output_translatable << i18n("All sides")
<< i18n("Top Edge")
<< i18n("Bottom Edge")
<< i18n("Right Edge")
<< i18n("Left Edge")
<< i18n("Direction in Radians");
ui->cmbType->addItems(m_types_translatable);
ui->cmbOutput->addItems(m_output_translatable);
ui->btnAspect->setKeepAspectRatio(false);
ui->sldHorizontalRadius->setRange(1.0, 100.0, 2);
ui->sldHorizontalRadius->setPrefix(i18n("Horizontal Radius:"));
connect(ui->sldHorizontalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(horizontalRadiusChanged(qreal)));
ui->sldVerticalRadius->setRange(1.0, 100.0, 2);
ui->sldVerticalRadius->setPrefix(i18n("Vertical Radius:"));
connect(ui->sldVerticalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(verticalRadiusChanged(qreal)));
connect(ui->btnAspect, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(aspectLockChanged(bool)));
connect(ui->cmbType, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged()));
connect(ui->cmbOutput, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged()));
connect(ui->sldHorizontalRadius, SIGNAL(valueChanged(qreal)), this, SIGNAL(sigConfigurationItemChanged()));
connect(ui->sldVerticalRadius, SIGNAL(valueChanged(qreal)), this, SIGNAL(sigConfigurationItemChanged()));
connect(ui->chkTransparent, SIGNAL(clicked()), this, SIGNAL(sigConfigurationItemChanged()));
}
KisWdgEdgeDetection::~KisWdgEdgeDetection()
{
delete ui;
}
KisPropertiesConfigurationSP KisWdgEdgeDetection::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("edge detection", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("edge detection", 1, KisGlobalResourcesInterface::instance());
config->setProperty("horizRadius", ui->sldHorizontalRadius->value());
config->setProperty("vertRadius", ui->sldVerticalRadius->value());
config->setProperty("type", m_types.at(ui->cmbType->currentIndex()));
config->setProperty("output", m_output.at(ui->cmbOutput->currentIndex()));
config->setProperty("lockAspect", ui->btnAspect->keepAspectRatio());
config->setProperty("transparency", ui->chkTransparent->isChecked());
return config;
}
void KisWdgEdgeDetection::setConfiguration(const KisPropertiesConfigurationSP config)
{
ui->sldHorizontalRadius->setValue(config->getFloat("horizRadius", 1.0));
ui->sldVerticalRadius->setValue(config->getFloat("vertRadius", 1.0));
int index = 0;
if (m_types.contains(config->getString("type", "prewitt"))){
index = m_types.indexOf(config->getString("type", "prewitt"));
}
ui->cmbType->setCurrentIndex(index);
index = 0;
if (m_output.contains(config->getString("output", "pythagorean"))){
index = m_output.indexOf(config->getString("output", "pythagorean"));
}
ui->cmbOutput->setCurrentIndex(index);
ui->chkTransparent->setChecked(config->getBool("transparency", false));
ui->btnAspect->setKeepAspectRatio(config->getBool("lockAspect", false));
}
void KisWdgEdgeDetection::horizontalRadiusChanged(qreal r)
{
ui->sldHorizontalRadius->blockSignals(true);
ui->sldHorizontalRadius->setValue(r);
ui->sldHorizontalRadius->blockSignals(false);
if (ui->btnAspect->keepAspectRatio()) {
ui->sldVerticalRadius->blockSignals(true);
ui->sldVerticalRadius->setValue(r);
ui->sldVerticalRadius->blockSignals(false);
}
}
void KisWdgEdgeDetection::verticalRadiusChanged(qreal r)
{
ui->sldVerticalRadius->blockSignals(true);
ui->sldVerticalRadius->setValue(r);
ui->sldVerticalRadius->blockSignals(false);
if (ui->btnAspect->keepAspectRatio()) {
ui->sldHorizontalRadius->blockSignals(true);
ui->sldHorizontalRadius->setValue(r);
ui->sldHorizontalRadius->blockSignals(false);
}
}
void KisWdgEdgeDetection::aspectLockChanged(bool v)
{
if (v) {
ui->sldVerticalRadius->setValue( ui->sldHorizontalRadius->value() );
}
}
diff --git a/plugins/filters/embossfilter/kis_emboss_filter.cpp b/plugins/filters/embossfilter/kis_emboss_filter.cpp
index defcb83c96..98f98db5a8 100644
--- a/plugins/filters/embossfilter/kis_emboss_filter.cpp
+++ b/plugins/filters/embossfilter/kis_emboss_filter.cpp
@@ -1,160 +1,160 @@
/*
* This file is part of Krita
*
* Copyright (c) 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
*
* ported from digikam, Copyrighted 2004 Gilles Caulier,
* Original Emboss algorithm copyrighted 2004 by
* Pieter Z. Voloshyn <pieter_voloshyn at ame.com.br>.
*
* 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_emboss_filter.h"
#include <stdlib.h>
#include <vector>
#include <QPoint>
#include <QSpinBox>
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include "KoIntegerMaths.h"
#include <KoUpdater.h>
#include <kis_random_accessor_ng.h>
#include <filter/kis_filter_registry.h>
#include <kis_global.h>
#include <kis_selection.h>
#include <kis_types.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
#include "widgets/kis_multi_integer_filter_widget.h"
#include <KisSequentialIteratorProgress.h>
KisEmbossFilter::KisEmbossFilter() : KisFilter(id(), FiltersCategoryEmbossId, i18n("&Emboss with Variable Depth..."))
{
setSupportsPainting(false);
setColorSpaceIndependence(TO_RGBA8);
setSupportsThreading(false);
setSupportsAdjustmentLayers(false);
}
-KisFilterConfigurationSP KisEmbossFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisEmbossFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("depth", 30);
return config;
}
// This method have been ported from Pieter Z. Voloshyn algorithm code.
/* Function to apply the Emboss effect
*
* data => The image data in RGBA mode.
* Width => Width of image.
* Height => Height of image.
* d => Emboss value
*
* Theory => This is an amazing effect. And the theory is very simple to
* understand. You get the difference between the colors and
* increase it. After this, get the gray tone
*/
void KisEmbossFilter::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
QPoint srcTopLeft = applyRect.topLeft();
Q_ASSERT(device);
//read the filter configuration values from the KisFilterConfiguration object
quint32 embossdepth = config ? config->getInt("depth", 30) : 30;
//the actual filter function from digikam. It needs a pointer to a quint8 array
//with the actual pixel data.
float Depth = embossdepth / 10.0;
int R = 0, G = 0, B = 0;
uchar Gray = 0;
int Width = applyRect.width();
int Height = applyRect.height();
KisSequentialIteratorProgress it(device, applyRect, progressUpdater);
QColor color1;
QColor color2;
KisRandomConstAccessorSP acc = device->createRandomAccessorNG(srcTopLeft.x(), srcTopLeft.y());
while (it.nextPixel()) {
// XXX: COLORSPACE_INDEPENDENCE or at least work IN RGB16A
device->colorSpace()->toQColor(it.oldRawData(), &color1);
acc->moveTo(srcTopLeft.x() + it.x() + Lim_Max(it.x(), 1, Width), srcTopLeft.y() + it.y() + Lim_Max(it.y(), 1, Height));
device->colorSpace()->toQColor(acc->oldRawData(), &color2);
R = abs((int)((color1.red() - color2.red()) * (int)Depth + (quint8_MAX / 2)));
G = abs((int)((color1.green() - color2.green()) * (int)Depth + (quint8_MAX / 2)));
B = abs((int)((color1.blue() - color2.blue()) * (int)Depth + (quint8_MAX / 2)));
Gray = CLAMP((R + G + B) / 3, 0, quint8_MAX);
device->colorSpace()->fromQColor(QColor(Gray, Gray, Gray, color1.alpha()), it.rawData());
}
}
// This method have been ported from Pieter Z. Voloshyn algorithm code.
/* This function limits the max and min values
* defined by the developer
*
* Now => Original value
* Up => Increments
* Max => Maximum value
*
* Theory => This function is used in some functions to limit the
* "for step". E.g. I have a picture with 309 pixels (width), and
* my "for step" is 5. All the code goes alright until it reaches the
* w = 305, because in the next step we will go to 310, but we want
* to analyze all the pixels. So, this function will reduce the
* "for step", when necessary, until we reach the last possible value
*/
int KisEmbossFilter::Lim_Max(int Now, int Up, int Max) const
{
--Max;
while (Now > Max - Up)
--Up;
return (Up);
}
KisConfigWidget * KisEmbossFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
vKisIntegerWidgetParam param;
param.push_back(KisIntegerWidgetParam(10, 300, 30, i18nc("Emboss depth", "Depth"), "depth"));
KisConfigWidget * w = new KisMultiIntegerFilterWidget(id().id(), parent, id().id(), param);
Q_CHECK_PTR(w);
return w;
}
diff --git a/plugins/filters/embossfilter/kis_emboss_filter.h b/plugins/filters/embossfilter/kis_emboss_filter.h
index f4fd55b090..1e5e08f308 100644
--- a/plugins/filters/embossfilter/kis_emboss_filter.h
+++ b/plugins/filters/embossfilter/kis_emboss_filter.h
@@ -1,51 +1,51 @@
/*
* This file is part of the KDE project
*
* Copyright (c) Michael Thaler <michael.thaler@physik.tu-muenchen.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.
*/
#ifndef _KIS_EMBOSS_FILTER_H_
#define _KIS_EMBOSS_FILTER_H_
#include "filter/kis_filter.h"
#include "kis_config_widget.h"
class KisEmbossFilter : public KisFilter
{
public:
KisEmbossFilter();
public:
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("emboss", i18n("Emboss with Variable Depth"));
}
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
protected:
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
private:
inline int Lim_Max(int Now, int Up, int Max) const;
};
#endif
diff --git a/plugins/filters/fastcolortransfer/fastcolortransfer.cpp b/plugins/filters/fastcolortransfer/fastcolortransfer.cpp
index 266df72d5a..ea2afc46db 100644
--- a/plugins/filters/fastcolortransfer/fastcolortransfer.cpp
+++ b/plugins/filters/fastcolortransfer/fastcolortransfer.cpp
@@ -1,171 +1,171 @@
/*
* 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 "fastcolortransfer.h"
#include <math.h>
#include <kpluginfactory.h>
#include <kundo2command.h>
#include <KoColorSpaceRegistry.h>
#include <KoUpdater.h>
#include <filter/kis_filter_registry.h>
#include <kis_image.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 "kis_wdg_fastcolortransfer.h"
#include "ui_wdgfastcolortransfer.h"
#include <KisSequentialIteratorProgress.h>
#include <KoProgressUpdater.h>
K_PLUGIN_FACTORY_WITH_JSON(KritaFastColorTransferFactory, "kritafastcolortransfer.json", registerPlugin<FastColorTransferPlugin>();)
FastColorTransferPlugin::FastColorTransferPlugin(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisFilterRegistry::instance()->add(new KisFilterFastColorTransfer());
}
FastColorTransferPlugin::~FastColorTransferPlugin()
{
}
KisFilterFastColorTransfer::KisFilterFastColorTransfer() : KisFilter(id(), FiltersCategoryColorId, i18n("&Color Transfer..."))
{
setColorSpaceIndependence(FULLY_INDEPENDENT);
setSupportsThreading(false);
setSupportsPainting(false);
setSupportsAdjustmentLayers(false);
}
KisConfigWidget * KisFilterFastColorTransfer::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
return new KisWdgFastColorTransfer(parent);
}
-KisFilterConfigurationSP KisFilterFastColorTransfer::defaultConfiguration() const
+KisFilterConfigurationSP KisFilterFastColorTransfer::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("filename", "");
return config;
}
#define CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x)))
void KisFilterFastColorTransfer::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater) const
{
Q_ASSERT(device != 0);
dbgPlugins << "Start transferring color";
// Convert ref and src to LAB
const KoColorSpace* labCS = KoColorSpaceRegistry::instance()->lab16();
if (!labCS) {
dbgPlugins << "The LAB colorspace is not available.";
return;
}
dbgPlugins << "convert a copy of src to lab";
const KoColorSpace* oldCS = device->colorSpace();
KisPaintDeviceSP srcLAB = new KisPaintDevice(*device.data());
dbgPlugins << "srcLab : " << srcLAB->extent();
srcLAB->convertTo(labCS, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
KoProgressUpdater compositeUpdater(progressUpdater, KoProgressUpdater::Unthreaded);
KoUpdater *updaterStats = compositeUpdater.startSubtask(1);
KoUpdater *updaterMap = compositeUpdater.startSubtask(2);
// Compute the means and sigmas of src
dbgPlugins << "Compute the means and sigmas of src";
double meanL_src = 0., meanA_src = 0., meanB_src = 0.;
double sigmaL_src = 0., sigmaA_src = 0., sigmaB_src = 0.;
{
KisSequentialConstIteratorProgress srcIt(srcLAB, applyRect, updaterStats);
while (srcIt.nextPixel()) {
const quint16* data = reinterpret_cast<const quint16*>(srcIt.oldRawData());
quint32 L = data[0];
quint32 A = data[1];
quint32 B = data[2];
meanL_src += L;
meanA_src += A;
meanB_src += B;
sigmaL_src += L * L;
sigmaA_src += A * A;
sigmaB_src += B * B;
}
}
double totalSize = 1. / (applyRect.width() * applyRect.height());
meanL_src *= totalSize;
meanA_src *= totalSize;
meanB_src *= totalSize;
sigmaL_src *= totalSize;
sigmaA_src *= totalSize;
sigmaB_src *= totalSize;
dbgPlugins << totalSize << "" << meanL_src << "" << meanA_src << "" << meanB_src << "" << sigmaL_src << "" << sigmaA_src << "" << sigmaB_src;
double meanL_ref = config->getDouble("meanL");
double meanA_ref = config->getDouble("meanA");
double meanB_ref = config->getDouble("meanB");
double sigmaL_ref = config->getDouble("sigmaL");
double sigmaA_ref = config->getDouble("sigmaA");
double sigmaB_ref = config->getDouble("sigmaB");
// Transfer colors
dbgPlugins << "Transfer colors";
{
double coefL = sqrt((sigmaL_ref - meanL_ref * meanL_ref) / (sigmaL_src - meanL_src * meanL_src));
double coefA = sqrt((sigmaA_ref - meanA_ref * meanA_ref) / (sigmaA_src - meanA_src * meanA_src));
double coefB = sqrt((sigmaB_ref - meanB_ref * meanB_ref) / (sigmaB_src - meanB_src * meanB_src));
quint16 labPixel[4];
KisSequentialConstIteratorProgress srcLabIt(srcLAB, applyRect, updaterMap);
KisSequentialIterator dstIt(device, applyRect);
while (srcLabIt.nextPixel() && dstIt.nextPixel()) {
const quint16* data = reinterpret_cast<const quint16*>(srcLabIt.oldRawData());
labPixel[0] = (quint16)CLAMP(((double)data[0] - meanL_src) * coefL + meanL_ref, 0., 65535.);
labPixel[1] = (quint16)CLAMP(((double)data[1] - meanA_src) * coefA + meanA_ref, 0., 65535.);
labPixel[2] = (quint16)CLAMP(((double)data[2] - meanB_src) * coefB + meanB_ref, 0., 65535.);
labPixel[3] = data[3];
oldCS->fromLabA16(reinterpret_cast<const quint8*>(labPixel), dstIt.rawData(), 1);
}
}
}
#include "fastcolortransfer.moc"
diff --git a/plugins/filters/fastcolortransfer/fastcolortransfer.h b/plugins/filters/fastcolortransfer/fastcolortransfer.h
index e8dd9a0347..e5a1da47de 100644
--- a/plugins/filters/fastcolortransfer/fastcolortransfer.h
+++ b/plugins/filters/fastcolortransfer/fastcolortransfer.h
@@ -1,55 +1,55 @@
/*
* 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.
*/
#ifndef COLORTRANSFER_H
#define COLORTRANSFER_H
#include <QObject>
#include <QVariant>
#include <filter/kis_filter.h>
class FastColorTransferPlugin : public QObject
{
Q_OBJECT
public:
FastColorTransferPlugin(QObject *parent, const QVariantList &);
~FastColorTransferPlugin() override;
};
class KisFilterFastColorTransfer : public KisFilter
{
public:
KisFilterFastColorTransfer();
public:
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater) const override;
static inline KoID id() {
return KoID("colortransfer", i18n("Color Transfer"));
}
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
};
#endif
diff --git a/plugins/filters/fastcolortransfer/kis_wdg_fastcolortransfer.cpp b/plugins/filters/fastcolortransfer/kis_wdg_fastcolortransfer.cpp
index b99a1b89cf..cb0453405b 100644
--- a/plugins/filters/fastcolortransfer/kis_wdg_fastcolortransfer.cpp
+++ b/plugins/filters/fastcolortransfer/kis_wdg_fastcolortransfer.cpp
@@ -1,144 +1,145 @@
/*
* 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_fastcolortransfer.h"
#include <QLayout>
#include <KisImportExportFilter.h>
#include <KisImportExportManager.h>
#include <filter/kis_filter_configuration.h>
#include <KisDocument.h>
#include <KisPart.h>
#include <kis_image.h>
#include <kis_iterator_ng.h>
#include <kis_paint_device.h>
#include <kundo2command.h>
#include <KoColorSpaceRegistry.h>
#include <kis_file_name_requester.h>
#include "ui_wdgfastcolortransfer.h"
+#include <KisGlobalResourcesInterface.h>
KisWdgFastColorTransfer::KisWdgFastColorTransfer(QWidget * parent) : KisConfigWidget(parent)
{
m_widget = new Ui_WdgFastColorTransfer();
m_widget->setupUi(this);
m_widget->fileNameURLRequester->setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import));
connect(m_widget->fileNameURLRequester, SIGNAL(textChanged(QString)), this, SIGNAL(sigConfigurationItemChanged()));
}
KisWdgFastColorTransfer::~KisWdgFastColorTransfer()
{
delete m_widget;
}
void KisWdgFastColorTransfer::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("filename", value)) {
widget()->fileNameURLRequester->setFileName(value.toString());
}
}
KisPropertiesConfigurationSP KisWdgFastColorTransfer::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("colortransfer", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("colortransfer", 1, KisGlobalResourcesInterface::instance());
QString fileName = this->widget()->fileNameURLRequester->fileName();
if (fileName.isEmpty()) return config;
KisPaintDeviceSP ref;
dbgPlugins << "Use as reference file : " << fileName;
KisDocument *d = KisPart::instance()->createDocument();
KisImportExportManager manager(d);
KisImportExportErrorCode status = manager.importDocument(fileName, QString());
dbgPlugins << "import returned status" << status.errorMessage();
KisImageWSP importedImage = d->image();
if (importedImage) {
ref = importedImage->projection();
}
if (!ref) {
dbgPlugins << "No reference image was specified.";
delete d;
return config;
}
// Convert ref to LAB
const KoColorSpace* labCS = KoColorSpaceRegistry::instance()->lab16();
if (!labCS) {
dbgPlugins << "The LAB colorspace is not available.";
delete d;
return config;
}
dbgPlugins << "convert ref to lab";
ref->convertTo(labCS, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
// Compute the means and sigmas of ref
double meanL_ref = 0., meanA_ref = 0., meanB_ref = 0.;
double sigmaL_ref = 0., sigmaA_ref = 0., sigmaB_ref = 0.;
KisSequentialConstIterator refIt(ref, importedImage->bounds());
while (refIt.nextPixel()) {
const quint16* data = reinterpret_cast<const quint16*>(refIt.oldRawData());
quint32 L = data[0];
quint32 A = data[1];
quint32 B = data[2];
meanL_ref += L;
meanA_ref += A;
meanB_ref += B;
sigmaL_ref += L * L;
sigmaA_ref += A * A;
sigmaB_ref += B * B;
}
double totalSize = 1. / (importedImage->width() * importedImage->height());
meanL_ref *= totalSize;
meanA_ref *= totalSize;
meanB_ref *= totalSize;
sigmaL_ref *= totalSize;
sigmaA_ref *= totalSize;
sigmaB_ref *= totalSize;
dbgPlugins << totalSize << "" << meanL_ref << "" << meanA_ref << "" << meanB_ref << "" << sigmaL_ref << "" << sigmaA_ref << "" << sigmaB_ref;
config->setProperty("filename", fileName);
config->setProperty("meanL", meanL_ref);
config->setProperty("meanA", meanA_ref);
config->setProperty("meanB", meanB_ref);
config->setProperty("sigmaL", sigmaL_ref);
config->setProperty("sigmaA", sigmaA_ref);
config->setProperty("sigmaB", sigmaB_ref);
delete d;
return config;
}
diff --git a/plugins/filters/gaussianhighpass/gaussianhighpass_filter.cpp b/plugins/filters/gaussianhighpass/gaussianhighpass_filter.cpp
index c258f3efc7..6ef415fce8 100644
--- a/plugins/filters/gaussianhighpass/gaussianhighpass_filter.cpp
+++ b/plugins/filters/gaussianhighpass/gaussianhighpass_filter.cpp
@@ -1,124 +1,123 @@
/*
* This file is part of Krita
*
* Copyright (c) 2019 Miguel Lopez <reptillia39@live.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 <compositeops/KoVcMultiArchBuildSupport.h> //MSVC requires that Vc come first
#include "gaussianhighpass_filter.h"
#include <QBitArray>
#include <KoColorSpace.h>
#include <KoChannelInfo.h>
#include <KoColor.h>
#include <kis_painter.h>
#include <kis_paint_device.h>
#include <kis_paint_layer.h>
#include <kis_group_layer.h>
#include <kis_mask_generator.h>
#include <kis_gaussian_kernel.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include <KoProgressUpdater.h>
#include <KoUpdater.h>
#include <KoMixColorsOp.h>
#include "kis_lod_transform.h"
#include <KoCompositeOpRegistry.h>
#include "wdg_gaussianhighpass.h"
#include "ui_wdggaussianhighpass.h"
#include "KoColorSpaceTraits.h"
#include <KisSequentialIteratorProgress.h>
KisGaussianHighPassFilter::KisGaussianHighPassFilter() : KisFilter(id(), FiltersCategoryEdgeDetectionId, i18n("&Gaussian High Pass..."))
{
setSupportsPainting(true);
setSupportsAdjustmentLayers(true);
setSupportsThreading(true);
setSupportsLevelOfDetail(true);
setColorSpaceIndependence(FULLY_INDEPENDENT);
}
KisConfigWidget * KisGaussianHighPassFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool /* useForMasks */) const
{
return new KisWdgGaussianHighPass(parent);
}
-KisFilterConfigurationSP KisGaussianHighPassFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisGaussianHighPassFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("blurAmount", 1);
return config;
}
void KisGaussianHighPassFilter::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
- const KisFilterConfigurationSP _config,
+ const KisFilterConfigurationSP config,
KoUpdater *
) const
{
QPointer<KoUpdater> convolutionUpdater = 0;
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
- KisFilterConfigurationSP config = _config ? _config : new KisFilterConfiguration(id().id(), 1);
-
QVariant value;
KisLodTransformScalar t(device);
const qreal blurAmount = t.scale(config->getProperty("blurAmount", value) ? value.toDouble() : 1.0);
QBitArray channelFlags = config->channelFlags();
const QRect gaussNeedRect = this->neededRect(applyRect, config, device->defaultBounds()->currentLevelOfDetail());
KisCachedPaintDevice::Guard d1(device, m_cachedPaintDevice);
KisPaintDeviceSP blur = d1.device();
KisPainter::copyAreaOptimizedOldData(gaussNeedRect.topLeft(), device, blur, gaussNeedRect);
KisGaussianKernel::applyGaussian(blur, applyRect,
blurAmount, blurAmount,
channelFlags,
convolutionUpdater,
true); // make sure we craate an internal transaction on temp device
KisPainter painter(device);
painter.setCompositeOp(blur->colorSpace()->compositeOp(COMPOSITE_GRAIN_EXTRACT));
painter.bitBlt(applyRect.topLeft(), blur, applyRect);
painter.end();
}
QRect KisGaussianHighPassFilter::neededRect(const QRect & rect, const KisFilterConfigurationSP config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
const int halfSize = config->getProperty("blurAmount", value) ? KisGaussianKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
return rect.adjusted( -halfSize * 2, -halfSize * 2, halfSize * 2, halfSize * 2);
}
QRect KisGaussianHighPassFilter::changedRect(const QRect & rect, const KisFilterConfigurationSP config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
const int halfSize = config->getProperty("blurAmount", value) ? KisGaussianKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5;
return rect.adjusted( -halfSize, -halfSize, halfSize, halfSize);
}
diff --git a/plugins/filters/gaussianhighpass/gaussianhighpass_filter.h b/plugins/filters/gaussianhighpass/gaussianhighpass_filter.h
index 321a242699..2b4bb40579 100644
--- a/plugins/filters/gaussianhighpass/gaussianhighpass_filter.h
+++ b/plugins/filters/gaussianhighpass/gaussianhighpass_filter.h
@@ -1,54 +1,54 @@
/*
* This file is part of Krita
*
* Copyright (c) 2019 Miguel Lopez <reptillia39@live.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_GAUSSIANHIGHPASS_FILTER_H
#define KIS_GAUSSIANHIGHPASS_FILTER_H
#include "filter/kis_filter.h"
#include "kis_cached_paint_device.h"
class KisGaussianHighPassFilter : public KisFilter
{
public:
KisGaussianHighPassFilter();
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater *
) const override;
static inline KoID id() {
return KoID("gaussianhighpass", i18n("Gaussian High Pass"));
}
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
QRect changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
QRect neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
private:
mutable KisCachedPaintDevice m_cachedPaintDevice;
};
#endif
diff --git a/plugins/filters/gaussianhighpass/wdg_gaussianhighpass.cpp b/plugins/filters/gaussianhighpass/wdg_gaussianhighpass.cpp
index 3f350b8309..28fc85acfd 100644
--- a/plugins/filters/gaussianhighpass/wdg_gaussianhighpass.cpp
+++ b/plugins/filters/gaussianhighpass/wdg_gaussianhighpass.cpp
@@ -1,57 +1,58 @@
/*
* This file is part of Krita
*
* Copyright (c) 2019 Miguel Lopez <reptillia39@live.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_gaussianhighpass.h"
#include <QLayout>
#include <QToolButton>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
+#include <KisGlobalResourcesInterface.h>
#include "ui_wdggaussianhighpass.h"
KisWdgGaussianHighPass::KisWdgGaussianHighPass(QWidget * parent) : KisConfigWidget(parent)
{
m_widget = new Ui_WdgGaussianHighPass();
m_widget->setupUi(this);
connect(widget()->doubleblurAmount, SIGNAL(valueChanged(double)), SIGNAL(sigConfigurationItemChanged()));
}
KisWdgGaussianHighPass::~KisWdgGaussianHighPass()
{
delete m_widget;
}
void KisWdgGaussianHighPass::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
widget()->doubleblurAmount->setValue((config->getProperty("blurAmount", value)) ? value.toDouble() : 1.0);
}
KisPropertiesConfigurationSP KisWdgGaussianHighPass::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("gaussianhighpass", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("gaussianhighpass", 1, KisGlobalResourcesInterface::instance());
config->setProperty("blurAmount", widget()->doubleblurAmount->value());
return config;
}
diff --git a/plugins/filters/gradientmap/gradientmap.cpp b/plugins/filters/gradientmap/gradientmap.cpp
index 70274986c1..c4d703c67b 100644
--- a/plugins/filters/gradientmap/gradientmap.cpp
+++ b/plugins/filters/gradientmap/gradientmap.cpp
@@ -1,135 +1,137 @@
/*
* This file is part of the KDE project
*
* Copyright (c) 2016 Spencer Brown <sbrown655@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 "QObject"
#include "gradientmap.h"
#include <kpluginfactory.h>
#include <filter/kis_filter_registry.h>
#include "krita_filter_gradient_map.h"
#include "KoResourceServerProvider.h"
#include "kis_config_widget.h"
#include <KoAbstractGradient.h>
#include <KoStopGradient.h>
#include <KoGradientBackground.h>
-#include <KoResourceServerAdapter.h>
+#include <KisGlobalResourcesInterface.h>
K_PLUGIN_FACTORY_WITH_JSON(KritaGradientMapFactory, "kritagradientmap.json", registerPlugin<KritaGradientMap>();)
KritaGradientMapConfigWidget::KritaGradientMapConfigWidget(QWidget *parent, KisPaintDeviceSP dev, Qt::WindowFlags f)
: KisConfigWidget(parent, f)
{
Q_UNUSED(dev)
m_page = new WdgGradientMap(this);
QHBoxLayout *l = new QHBoxLayout(this);
Q_CHECK_PTR(l);
l->addWidget(m_page);
l->setContentsMargins(0, 0, 0, 0);
- KoResourceServerProvider *serverProvider = KoResourceServerProvider::instance();
- QSharedPointer<KoAbstractResourceServerAdapter> gradientResourceAdapter(
- new KoResourceServerAdapter<KoAbstractGradient>(serverProvider->gradientServer()));
+
m_gradientChangedCompressor = new KisSignalCompressor(100, KisSignalCompressor::FIRST_ACTIVE);
- m_gradientPopUp = new KoResourcePopupAction(gradientResourceAdapter,
- m_page->btnGradientChooser);
- m_activeGradient = KoStopGradient::fromQGradient(dynamic_cast<KoAbstractGradient*>(gradientResourceAdapter->resources().first())->toQGradient());
+ m_gradientPopUp = new KoResourcePopupAction(ResourceType::Gradients, m_page->btnGradientChooser);
+
+ m_activeGradient = KoStopGradient::fromQGradient(m_gradientPopUp->currentResource().dynamicCast<KoAbstractGradient>()->toQGradient());
m_page->gradientEditor->setGradient(m_activeGradient);
m_page->gradientEditor->setCompactMode(true);
m_page->gradientEditor->setEnabled(true);
m_page->btnGradientChooser->setDefaultAction(m_gradientPopUp);
m_page->btnGradientChooser->setPopupMode(QToolButton::InstantPopup);
connect(m_gradientPopUp, SIGNAL(resourceSelected(QSharedPointer<KoShapeBackground>)), this, SLOT(setAbstractGradientToEditor()));
connect(m_page->gradientEditor, SIGNAL(sigGradientChanged()), m_gradientChangedCompressor, SLOT(start()));
connect(m_gradientChangedCompressor, SIGNAL(timeout()), this, SIGNAL(sigConfigurationItemChanged()));
QObject::connect(m_page->colorModeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &KisConfigWidget::sigConfigurationItemChanged);
QObject::connect(m_page->ditherWidget, &KisDitherWidget::sigConfigurationItemChanged, this, &KisConfigWidget::sigConfigurationItemChanged);
}
KritaGradientMapConfigWidget::~KritaGradientMapConfigWidget()
{
delete m_page;
}
void KritaGradientMapConfigWidget::setAbstractGradientToEditor()
{
QSharedPointer<KoGradientBackground> bg =
qSharedPointerDynamicCast<KoGradientBackground>(
m_gradientPopUp->currentBackground());
m_activeGradient = KoStopGradient::fromQGradient(bg->gradient());
m_page->gradientEditor->setGradient(m_activeGradient);
}
KisPropertiesConfigurationSP KritaGradientMapConfigWidget::configuration() const
{
- KisFilterConfigurationSP cfg = new KisFilterConfiguration("gradientmap", 2);
+ KisFilterSP filter = KisFilterRegistry::instance()->get("gradientmap");
+ KisFilterConfigurationSP cfg = filter->factoryConfiguration(KisGlobalResourcesInterface::instance());
if (m_activeGradient) {
QDomDocument doc;
QDomElement elt = doc.createElement("gradient");
m_activeGradient->toXML(doc, elt);
doc.appendChild(elt);
cfg->setProperty("gradientXML", doc.toString());
}
cfg->setProperty("colorMode", m_page->colorModeComboBox->currentIndex());
m_page->ditherWidget->configuration(*cfg, "dither/");
return cfg;
}
//-----------------------------
void KritaGradientMapConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
Q_ASSERT(config);
QDomDocument doc;
if (config->hasProperty("gradientXML")) {
doc.setContent(config->getString("gradientXML", ""));
KoStopGradient gradient = KoStopGradient::fromXML(doc.firstChildElement());
if (gradient.stops().size() > 0) {
m_activeGradient->setStops(gradient.stops());
}
m_page->gradientEditor->setGradient(m_activeGradient);
}
m_page->colorModeComboBox->setCurrentIndex(config->getInt("colorMode"));
- m_page->ditherWidget->setConfiguration(*config, "dither/");
+
+ const KisFilterConfiguration *filterConfig = dynamic_cast<const KisFilterConfiguration*>(config.data());
+ KIS_SAFE_ASSERT_RECOVER_RETURN(filterConfig);
+ m_page->ditherWidget->setConfiguration(*filterConfig, "dither/");
}
void KritaGradientMapConfigWidget::setView(KisViewManager *view)
{
Q_UNUSED(view)
}
//------------------------------
KritaGradientMap::KritaGradientMap(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisFilterRegistry::instance()->add(KisFilterSP(new KritaFilterGradientMap()));
}
KritaGradientMap::~KritaGradientMap()
{
}
//-----------------------------
#include "gradientmap.moc"
diff --git a/plugins/filters/gradientmap/gradientmap.h b/plugins/filters/gradientmap/gradientmap.h
index bcaf3eaa29..e37ea26483 100644
--- a/plugins/filters/gradientmap/gradientmap.h
+++ b/plugins/filters/gradientmap/gradientmap.h
@@ -1,83 +1,68 @@
/*
* This file is part of Krita
*
* Copyright (c) 2016 Spencer Brown <sbrown655@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.
*/
#pragma once
#include "QObject"
#include "ui_wdg_gradientmap.h"
#include "kis_properties_configuration.h"
#include "filter/kis_color_transformation_configuration.h"
#include "kis_config_widget.h"
#include <KoResourcePopupAction.h>
#include <kis_signal_compressor.h>
#include <KoStopGradient.h>
class WdgGradientMap : public QWidget, public Ui::WdgGradientMap
{
Q_OBJECT
public:
WdgGradientMap(QWidget *parent) : QWidget(parent) {
setupUi(this);
}
};
-
-class KritaGradientMapFilterConfiguration : public KisColorTransformationConfiguration
-{
-public:
- KritaGradientMapFilterConfiguration();
- ~KritaGradientMapFilterConfiguration() override;
-
- virtual void setGradient(const KoResource* gradient);
-
- virtual const KoResource* gradient() const;
-
-private:
- KoResource const* m_gradient;
-};
-
class KritaGradientMapConfigWidget : public KisConfigWidget
{
Q_OBJECT
public:
KritaGradientMapConfigWidget(QWidget *parent, KisPaintDeviceSP dev, Qt::WindowFlags f = 0);
~KritaGradientMapConfigWidget() override;
KisPropertiesConfigurationSP configuration() const override;
void setConfiguration(const KisPropertiesConfigurationSP config) override;
WdgGradientMap *m_page;
KoResourcePopupAction *m_gradientPopUp;
KisSignalCompressor *m_gradientChangedCompressor;
- KoStopGradient *m_activeGradient;
+ KoStopGradientSP m_activeGradient;
void setView(KisViewManager *view) override;
private Q_SLOTS:
void setAbstractGradientToEditor();
};
class KritaGradientMap : public QObject
{
Q_OBJECT
public:
KritaGradientMap(QObject *parent, const QVariantList &);
~KritaGradientMap() override;
};
diff --git a/plugins/filters/gradientmap/krita_filter_gradient_map.cpp b/plugins/filters/gradientmap/krita_filter_gradient_map.cpp
index 17e647b826..a2b1c179e0 100644
--- a/plugins/filters/gradientmap/krita_filter_gradient_map.cpp
+++ b/plugins/filters/gradientmap/krita_filter_gradient_map.cpp
@@ -1,143 +1,231 @@
/*
* This file is part of the KDE project
*
* Copyright (c) 2016 Spencer Brown <sbrown655@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 "krita_filter_gradient_map.h"
#include <KoColorSpace.h>
#include <KoColor.h>
#include <kis_paint_device.h>
#include <kis_global.h>
#include <kis_types.h>
#include <filter/kis_filter_category_ids.h>
#include "kis_config_widget.h"
-#include <KoResourceServerProvider.h>
-#include <KoResourceServer.h>
#include <KoAbstractGradient.h>
#include <KoStopGradient.h>
#include <KoColorSet.h>
#include "gradientmap.h"
#include <KisDitherUtil.h>
+#include <KisGlobalResourcesInterface.h>
+#include <QDomDocument>
#include <KisSequentialIteratorProgress.h>
+class KritaFilterGradientMapConfiguration : public KisFilterConfiguration
+{
+public:
+ KritaFilterGradientMapConfiguration(const QString & name, qint32 version, KisResourcesInterfaceSP resourcesInterface)
+ : KisFilterConfiguration(name, version, resourcesInterface)
+ {
+ }
+
+ KritaFilterGradientMapConfiguration(const KritaFilterGradientMapConfiguration &rhs)
+ : KisFilterConfiguration(rhs)
+ {
+ }
+
+ virtual KisFilterConfigurationSP clone() const override {
+ return new KritaFilterGradientMapConfiguration(*this);
+ }
+
+ KoStopGradientSP gradientImpl(KisResourcesInterfaceSP resourcesInterface) const {
+ KoStopGradientSP gradient;
+
+ if (this->version() == 1) {
+ auto source = resourcesInterface->source<KoAbstractGradient>(ResourceType::Gradients);
+
+ KoAbstractGradientSP gradientAb = source.resourceForName(this->getString("gradientName"));
+ if (!gradientAb) {
+ qWarning() << "Could not find gradient" << this->getString("gradientName");
+ gradientAb = source.fallbackResource();
+ }
+
+ gradient = gradientAb.dynamicCast<KoStopGradient>();
+
+ if (!gradient) {
+ QScopedPointer<QGradient> qGradient(gradientAb->toQGradient());
+
+ QDomDocument doc;
+ QDomElement elt = doc.createElement("gradient");
+ KoStopGradient::fromQGradient(qGradient.data())->toXML(doc, elt);
+ doc.appendChild(elt);
+
+ gradient = KoStopGradient::fromXML(doc.firstChildElement())
+ .clone()
+ .dynamicCast<KoStopGradient>();
+ }
+ } else {
+ QDomDocument doc;
+ doc.setContent(this->getString("gradientXML", ""));
+ gradient = KoStopGradient::fromXML(doc.firstChildElement())
+ .clone()
+ .dynamicCast<KoStopGradient>();
+
+ }
+ return gradient;
+ }
+
+ KoStopGradientSP gradient() const {
+ return gradientImpl(resourcesInterface());
+ }
+
+ QList<KoResourceSP> linkedResources(KisResourcesInterfaceSP globalResourcesInterface) const override
+ {
+ QList<KoResourceSP> resources;
+
+ // only the first version of the filter loaded the gradient by name
+ if (this->version() == 1) {
+ KoStopGradientSP gradient = gradientImpl(globalResourcesInterface);
+ if (gradient) {
+ resources << gradient;
+ }
+ }
+
+ resources << KisDitherWidget::prepareLinkedResources(*this, "dither/", globalResourcesInterface);
+
+ return resources;
+ }
+
+ QList<KoResourceSP> embeddedResources(KisResourcesInterfaceSP globalResourcesInterface) const override
+ {
+ QList<KoResourceSP> resources;
+
+ // the second version of the filter embeds the gradient
+ if (this->version() > 1) {
+ KoStopGradientSP gradient = gradientImpl(globalResourcesInterface);
+
+ if (gradient) {
+ resources << gradient;
+ }
+ }
+
+ return resources;
+ }
+};
+
+using KritaFilterGradientMapConfigurationSP = KisPinnedSharedPtr<KritaFilterGradientMapConfiguration>;
KritaFilterGradientMap::KritaFilterGradientMap() : KisFilter(id(), FiltersCategoryMapId, i18n("&Gradient Map..."))
{
setColorSpaceIndependence(FULLY_INDEPENDENT);
setShowConfigurationWidget(true);
setSupportsLevelOfDetail(true);
setSupportsPainting(true);
setSupportsAdjustmentLayers(true);
setSupportsThreading(true);
}
void KritaFilterGradientMap::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
- const KisFilterConfigurationSP config,
+ const KisFilterConfigurationSP _config,
KoUpdater *progressUpdater) const
{
Q_ASSERT(!device.isNull());
- QDomDocument doc;
- if (config->version()==1) {
- QDomElement elt = doc.createElement("gradient");
- KoAbstractGradient *gradientAb = KoResourceServerProvider::instance()->gradientServer()->resourceByName(config->getString("gradientName"));
- if (!gradientAb) {
- qWarning() << "Could not find gradient" << config->getString("gradientName");
- }
- gradientAb = KoResourceServerProvider::instance()->gradientServer()->resources().first();
- QScopedPointer<QGradient> qGradient(gradientAb->toQGradient());
- KoStopGradient::fromQGradient(qGradient.data())->toXML(doc, elt);
- doc.appendChild(elt);
- } else {
- doc.setContent(config->getString("gradientXML", ""));
- }
- KoStopGradient gradient = KoStopGradient::fromXML(doc.firstChildElement());
+ const KritaFilterGradientMapConfiguration* config =
+ dynamic_cast<const KritaFilterGradientMapConfiguration*>(_config.data());
+
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
+
+ KoStopGradientSP gradient = config->gradient();
const ColorMode colorMode = ColorMode(config->getInt("colorMode"));
KisDitherUtil ditherUtil;
if (colorMode == ColorMode::Dither) ditherUtil.setConfiguration(*config, "dither/");
KoColor outColor(Qt::white, device->colorSpace());
KisSequentialIteratorProgress it(device, applyRect, progressUpdater);
qreal grey;
const int pixelSize = device->colorSpace()->pixelSize();
while (it.nextPixel()) {
grey = qreal(device->colorSpace()->intensity8(it.oldRawData())) / 255;
if (colorMode == ColorMode::Nearest) {
KoGradientStop leftStop, rightStop;
- if (!gradient.stopsAt(leftStop, rightStop, grey)) continue;
+ if (!gradient->stopsAt(leftStop, rightStop, grey)) continue;
if (std::abs(grey - leftStop.first) < std::abs(grey - rightStop.first)) {
outColor = leftStop.second;
}
else {
outColor = rightStop.second;
}
}
else if (colorMode == ColorMode::Dither) {
KoGradientStop leftStop, rightStop;
- if (!gradient.stopsAt(leftStop, rightStop, grey)) continue;
+ if (!gradient->stopsAt(leftStop, rightStop, grey)) continue;
qreal localT = (grey - leftStop.first) / (rightStop.first - leftStop.first);
if (localT < ditherUtil.threshold(QPoint(it.x(), it.y()))) {
outColor = leftStop.second;
}
else {
outColor = rightStop.second;
}
}
else {
- gradient.colorAt(outColor, grey);
+ gradient->colorAt(outColor, grey);
}
outColor.setOpacity(qMin(KoColor(it.oldRawData(), device->colorSpace()).opacityF(), outColor.opacityF()));
outColor.convertTo(device->colorSpace());
memcpy(it.rawData(), outColor.data(), pixelSize);
}
}
-KisFilterConfigurationSP KritaFilterGradientMap::factoryConfiguration() const
+KisFilterConfigurationSP KritaFilterGradientMap::factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- return new KisFilterConfiguration(id().id(), 2);
+
+ return new KritaFilterGradientMapConfiguration(id().id(), 2, resourcesInterface);
}
-KisFilterConfigurationSP KritaFilterGradientMap::defaultConfiguration() const
+KisFilterConfigurationSP KritaFilterGradientMap::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
- KoAbstractGradient *gradient = KoResourceServerProvider::instance()->gradientServer()->resources().first();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
+
+ auto source = resourcesInterface->source<KoAbstractGradient>(ResourceType::Gradients);
+ KoAbstractGradientSP gradient = source.fallbackResource();
+
KoStopGradient stopGradient;
QScopedPointer<QGradient> qGradient(gradient->toQGradient());
stopGradient.fromQGradient(qGradient.data());
QDomDocument doc;
QDomElement elt = doc.createElement("gradient");
stopGradient.toXML(doc, elt);
doc.appendChild(elt);
config->setProperty("gradientXML", doc.toString());
config->setProperty("colorMode", false);
KisDitherWidget::factoryConfiguration(*config, "dither/");
return config;
}
-KisConfigWidget * KritaFilterGradientMap::createConfigurationWidget(QWidget * parent, const KisPaintDeviceSP dev, bool) const
+KisConfigWidget *KritaFilterGradientMap::createConfigurationWidget(QWidget * parent, const KisPaintDeviceSP dev, bool) const
{
return new KritaGradientMapConfigWidget(parent, dev);
}
diff --git a/plugins/filters/gradientmap/krita_filter_gradient_map.h b/plugins/filters/gradientmap/krita_filter_gradient_map.h
index 4100671bba..2d827e08d2 100644
--- a/plugins/filters/gradientmap/krita_filter_gradient_map.h
+++ b/plugins/filters/gradientmap/krita_filter_gradient_map.h
@@ -1,58 +1,58 @@
/*
* This file is part of Krita
*
* Copyright (c) 2016 Spencer Brown <sbrown655@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_GRADIENT_MAP_H
#define KIS_GRADIENT_MAP_H
#include <kis_global.h>
#include <kis_types.h>
#include <kis_paint_device.h>
#include <filter/kis_filter.h>
#include <kis_filter_configuration.h>
#include <kis_config_widget.h>
#include <KoUpdater.h>
class KritaFilterGradientMap : public KisFilter
{
public:
KritaFilterGradientMap();
public:
enum ColorMode {
Blend,
Nearest,
Dither,
};
static inline KoID id() {
return KoID("gradientmap", i18n("Gradient Map"));
}
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater *progressUpdater) const override;
- KisFilterConfigurationSP factoryConfiguration() const override;
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
KisConfigWidget* createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
};
#endif
diff --git a/plugins/filters/halftone/kis_halftone_filter.cpp b/plugins/filters/halftone/kis_halftone_filter.cpp
index f2a34e6094..0710573388 100644
--- a/plugins/filters/halftone/kis_halftone_filter.cpp
+++ b/plugins/filters/halftone/kis_halftone_filter.cpp
@@ -1,270 +1,271 @@
/*
* This file is part of Krita
*
* Copyright (c) 2016 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.
*/
#include <cmath>
#include <QtMath>
#include <kpluginfactory.h>
#include <klocalizedstring.h>
#include <KoUpdater.h>
#include <filter/kis_filter_category_ids.h>
#include "kis_filter_configuration.h"
#include <kis_filter_registry.h>
#include <KoCompositeOpRegistry.h>
#include <kis_random_accessor_ng.h>
#include <kis_sequential_iterator.h>
#include <kis_types.h>
#include <kis_painter.h>
#include <kis_pixel_selection.h>
#include <kis_selection.h>
+#include <KisGlobalResourcesInterface.h>
#include "kis_halftone_filter.h"
K_PLUGIN_FACTORY_WITH_JSON(KritaHalftoneFactory, "kritahalftone.json", registerPlugin<KritaHalftone>();)
KritaHalftone::KritaHalftone(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisFilterRegistry::instance()->add(new KisHalftoneFilter());
}
KritaHalftone::~KritaHalftone()
{
}
KisHalftoneFilter::KisHalftoneFilter()
: KisFilter(id(), FiltersCategoryArtisticId, i18n("&Halftone..."))
{
setColorSpaceIndependence(FULLY_INDEPENDENT);
setSupportsPainting(false);
setShowConfigurationWidget(true);
setSupportsLevelOfDetail(false);
setSupportsAdjustmentLayers(false);
setSupportsThreading(false);
}
//I am pretty terrible at trigonometry, hence all the comments.
void KisHalftoneFilter::processImpl(KisPaintDeviceSP device,
const QRect &applyRect,
const KisFilterConfigurationSP config,
KoUpdater *progressUpdater) const
{
qreal cellSize = (qreal)config->getInt("cellSize", 8);
qreal angle = fmod((qreal)config->getInt("patternAngle", 45), 90.0);
KoColor foregroundC(Qt::black, device->colorSpace());
foregroundC.fromKoColor(config->getColor("foreGroundColor", KoColor(Qt::black, device->colorSpace()) ) );
KoColor backgroundC(Qt::white, device->colorSpace());
backgroundC.fromKoColor(config->getColor("backGroundColor", KoColor(Qt::white, device->colorSpace()) ) );
//First calculate the full diameter, using pythagoras theorem.
qreal diameter = qSqrt((cellSize*cellSize)*2);
qreal cellSpacingH = cellSize;
qreal cellSpacingV = cellSize;
qreal cellOffsetV = 0;
if (angle>0.0){
//2/3=0.6 sin=o/h
//0.6*3=2 sin*h=o
//2/0.6=3 o/sin=h
qreal angleRad = qDegreesToRadians(angle);
//Horizontal cellspacing is the hypotenuse of the Angle and the cellSize(opposite).
cellSpacingH = cellSize/qSin(angleRad);
//Vertical cellspacing is the opposite of the Angle and the cellSize(hypotenuse).
cellSpacingV = cellSize*qSin(angleRad);
//cellSpacingoffset is the oppose of the (90-angle) and the vertical cellspacing(adjectant) toa t=o/a, t*a=o.
cellOffsetV = qTan(qDegreesToRadians(90-angle))*cellSpacingV;
}
QPolygonF gridPoints;
QRect totalRect(QPoint(0,0), applyRect.bottomRight());
if (gridPoints.size()<1) {
int rows = (totalRect.height()/cellSpacingV)+3;
for (int r=0; r<rows; r++) {
qreal offset = fmod(((qreal)r*cellOffsetV), cellSpacingH);
int columns = ((totalRect.width()+offset)/cellSpacingH)+3;
for (int c = 0; c<columns; c++) {
gridPoints.append(QPointF((c*cellSpacingH)+offset-cellSpacingH,
r*cellSpacingV-cellSpacingV));
}
}
}
//qDebug()<<applyRect;
//qDebug()<<device->exactBounds();
if (progressUpdater) {
progressUpdater->setRange(0, (applyRect.height()/cellSpacingV+3)*(applyRect.width()/cellSpacingH+3));
}
KisRandomConstAccessorSP iterator = device->createRandomConstAccessorNG( 0, 0);
//iterator->numContiguousColumns(qCeil(cellSize));
//iterator->numContiguousRows(qCeil(cellSize));
KisPainter painter(device);
painter.setCompositeOp(device->colorSpace()->compositeOp(COMPOSITE_OVER));
KisPaintDeviceSP dab = device->createCompositionSourceDevice();
KisPainter dbPainter(dab);
KisSelectionSP alpha = new KisSelection(new KisSelectionEmptyBounds(0));
alpha->pixelSelection()->copyAlphaFrom(device, applyRect);
device->fill(applyRect, backgroundC);
dbPainter.setAntiAliasPolygonFill(config->getBool("antiAliasing", true));
dbPainter.setPaintColor(foregroundC);
dbPainter.setFillStyle(KisPainter::FillStyleForegroundColor);
dbPainter.setCompositeOp(device->colorSpace()->compositeOp(COMPOSITE_OVER));
quint8 eightbit = 255;
if (config->getBool("invert", false)) {
eightbit = 0;
}
QRect cellRect(applyRect.topLeft()-QPoint(qFloor(cellSpacingH), qFloor(qMax(cellSpacingV, diameter))), applyRect.bottomRight()+QPoint(qCeil(cellSpacingH), qCeil(qMax(cellSpacingV, diameter))));
for (int i=0; i<gridPoints.size(); i++) {
QPointF samplePoint = gridPoints.at(i);
if (cellRect.contains(samplePoint.toPoint())) {
qint32 xdifference = qFloor(samplePoint.x())- applyRect.left();
qint32 ydifference = qFloor(samplePoint.y())- applyRect.top();
QPoint center(qBound(applyRect.left()+1, qFloor(samplePoint.x())+qCeil(cellSize*0.5), applyRect.right()-1),
qBound(applyRect.top()+1, qFloor(samplePoint.y())+qCeil(cellSize*0.5), applyRect.bottom()-1));
iterator->moveTo(center.x(), center.y());
quint8 intensity = device->colorSpace()->intensity8(iterator->oldRawData());
qreal size = diameter*((qAbs(intensity-eightbit))/255.0);
QPoint sPoint(qMax(qFloor(samplePoint.x()), applyRect.left()),
qMax(qFloor(samplePoint.y()), applyRect.top()));
dbPainter.bitBlt(0, 0, device,
sPoint.x(),
sPoint.y(),
diameter,
diameter);
dbPainter.paintEllipse((samplePoint.x()-qFloor(samplePoint.x()))+qCeil(size)-size,
(samplePoint.y()-qFloor(samplePoint.y()))+qCeil(size)-size,
size,
size);
dab->crop(qAbs(qMin(0, xdifference)),
qAbs(qMin(0, ydifference)),
diameter,
diameter);
//we only want to paint the bits actually in the apply rect...)
painter.bitBlt(sPoint,
dab,
dab->exactBounds());
if (progressUpdater) {
progressUpdater->setValue(i);
}
}
}
alpha->pixelSelection()->invert();
device->clearSelection(alpha);
}
-KisFilterConfigurationSP KisHalftoneFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisHalftoneFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("cellSize", 8.0);
config->setProperty("patternAngle", 45.0);
QVariant v;
KoColor black;
black.fromQColor(QColor(Qt::black));
v.setValue(black);
config->setProperty("foreGroundColor", v);
KoColor white;
white.fromQColor(QColor(Qt::white));
v.setValue(white);
config->setProperty("backGroundColor", v);
config->setProperty("antiAliasing", true);
config->setProperty("invert", false);
return config;
}
KisConfigWidget *KisHalftoneFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool) const
{
return new KisHalftoneConfigWidget(parent, dev);
}
//----------config---------//
KisHalftoneConfigWidget::KisHalftoneConfigWidget(QWidget *parent, KisPaintDeviceSP dev)
: KisConfigWidget(parent)
{
Q_ASSERT(dev);
m_page.setupUi(this);
KoColor white(Qt::white, dev->colorSpace());
KoColor black(Qt::black, dev->colorSpace());
m_page.bnforeground->setColor(white);
m_page.bnbackground->setColor(black);
m_page.bnforeground->setDefaultColor(white);
m_page.bnbackground->setDefaultColor(black);
m_page.sld_cellSize->setRange(3, 90);
connect(m_page.sld_cellSize, SIGNAL(valueChanged(int)), SLOT(slotConfigChanged()));
connect(m_page.dial_angle, SIGNAL(valueChanged(int)), m_page.spb_angle, SLOT(setValue(int)));
connect(m_page.dial_angle, SIGNAL(valueChanged(int)), SLOT(slotConfigChanged()));
connect(m_page.spb_angle, SIGNAL(valueChanged(int)), SLOT(slotConfigChanged()));
connect(m_page.bnforeground, SIGNAL(changed(KoColor)), SLOT(slotConfigChanged()));
connect(m_page.bnbackground, SIGNAL(changed(KoColor)), SLOT(slotConfigChanged()));
connect(m_page.ckbAntialiasing, SIGNAL(toggled(bool)), SLOT(slotConfigChanged()));
connect(m_page.ckbInvert, SIGNAL(toggled(bool)), SLOT(slotConfigChanged()));
}
KisHalftoneConfigWidget::~KisHalftoneConfigWidget()
{
}
KisPropertiesConfigurationSP KisHalftoneConfigWidget::configuration() const
{
- KisFilterConfiguration *config = new KisFilterConfiguration("halftone", 1);
+ KisFilterConfiguration *config = new KisFilterConfiguration("halftone", 1, KisGlobalResourcesInterface::instance());
config->setProperty("cellSize", m_page.sld_cellSize->value());
config->setProperty("patternAngle", m_page.spb_angle->value());
QVariant v;
v.setValue(m_page.bnforeground->color());
config->setProperty("foreGroundColor", v);
v.setValue(m_page.bnbackground->color());
config->setProperty("backGroundColor", v);
config->setProperty("antiAliasing", m_page.ckbAntialiasing->isChecked());
config->setProperty("invert", m_page.ckbInvert->isChecked());
return config;
}
void KisHalftoneConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("cellSize", value)) {
m_page.sld_cellSize->setValue(value.toUInt());
}
if (config->getProperty("patternAngle", value)) {
m_page.dial_angle->setValue(value.toUInt());
m_page.spb_angle->setValue(value.toUInt());
}
if (config->getProperty("antiAliasing", value)) {
m_page.ckbAntialiasing->setChecked(value.toBool());
}
if (config->getProperty("invert", value)) {
m_page.ckbInvert->setChecked(value.toBool());
}
m_page.bnforeground->setColor(config->getColor("foreGroundColor",m_page.bnforeground->defaultColor()));
m_page.bnbackground->setColor(config->getColor("backGroundColor",m_page.bnbackground->defaultColor()));
}
#include "kis_halftone_filter.moc"
diff --git a/plugins/filters/halftone/kis_halftone_filter.h b/plugins/filters/halftone/kis_halftone_filter.h
index e70084f899..33c0b715e4 100644
--- a/plugins/filters/halftone/kis_halftone_filter.h
+++ b/plugins/filters/halftone/kis_halftone_filter.h
@@ -1,101 +1,101 @@
/*
* This file is part of Krita
*
* Copyright (c) 2016 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 KISHALFTONEFILTER_H
#define KISHALFTONEFILTER_H
#include <QObject>
#include <filter/kis_filter.h>
#include <kis_filter_configuration.h>
#include <kis_config_widget.h>
#include "ui_wdg_halftone_filter.h"
class WdgHalftone;
class KritaHalftone : public QObject
{
Q_OBJECT
public:
KritaHalftone(QObject *parent, const QVariantList &);
~KritaHalftone() override;
};
/**
* @brief The kisHalftoneFilter class
* This filter will allow the user to input an image and have it be approximated with
* a halftone pattern. https://en.wikipedia.org/wiki/Halftone
*
* The primary usecase of such a filter is for specialized printing techniques, but for
* many people the half-tone pattern also serves as a neutral pattern that is more pleasant
* than plain flat look. The half tone in this case also becomes a stylistic technique.
*
* Based on that, there's a few ways a user could want to use this technique:
* 1. Per-component. Per patch, each component will have a halftone approximated.
* 2. Intensity only. The relative luminosity of the patch is determined and will be used
* for the approximation, resulting in a black/white pattern.
* 3. Intensity and then two colors mapped to the black/white pattern.
*
* On top of that, the pattern can be rotated, the shape can be chosen, and the user will want to
* decide whether or not to use antialiasing(as printers themselves give
* inefficient results with anti-aliasing).
*
* As of currently, 2 and 3 can be done. 1 is not impossible to code with some creative usage of composite
* modes, but might be a little slow.
*/
class KisHalftoneFilter : public KisFilter
{
public:
KisHalftoneFilter();
static inline KoID id() {
return KoID("halftone", i18n("Halftone"));
}
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater *progressUpdater) const override;
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
KisConfigWidget *createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
private:
QPolygonF m_gridPoints;
};
class KisHalftoneConfigWidget : public KisConfigWidget
{
Q_OBJECT
public:
KisHalftoneConfigWidget(QWidget *parent, KisPaintDeviceSP dev);
~KisHalftoneConfigWidget() override;
KisPropertiesConfigurationSP configuration() const override;
void setConfiguration(const KisPropertiesConfigurationSP config) override;
Ui::WdgHalftone m_page;
};
#endif // KISHALFTONEFILTER_H
diff --git a/plugins/filters/imageenhancement/kis_simple_noise_reducer.cpp b/plugins/filters/imageenhancement/kis_simple_noise_reducer.cpp
index f9202d54e7..c215e23264 100644
--- a/plugins/filters/imageenhancement/kis_simple_noise_reducer.cpp
+++ b/plugins/filters/imageenhancement/kis_simple_noise_reducer.cpp
@@ -1,128 +1,128 @@
/*
* 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.
*/
#include <compositeops/KoVcMultiArchBuildSupport.h> //MSVC requires that Vc come first
#include "kis_simple_noise_reducer.h"
#include <kundo2command.h>
#include <KoColorSpace.h>
#include <KoCompositeOp.h>
#include <KoUpdater.h>
#include <kis_mask_generator.h>
#include <kis_convolution_kernel.h>
#include <kis_convolution_painter.h>
#include <kis_global.h>
#include <widgets/kis_multi_integer_filter_widget.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include <kis_paint_device.h>
#include <kis_selection.h>
#include <KisSequentialIteratorProgress.h>
#include "kis_lod_transform.h"
KisSimpleNoiseReducer::KisSimpleNoiseReducer()
: KisFilter(id(), FiltersCategoryEnhanceId, i18n("&Gaussian Noise Reduction..."))
{
setSupportsPainting(false);
setSupportsLevelOfDetail(true);
}
KisSimpleNoiseReducer::~KisSimpleNoiseReducer()
{
}
KisConfigWidget * KisSimpleNoiseReducer::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
vKisIntegerWidgetParam param;
param.push_back(KisIntegerWidgetParam(0, 255, 15, i18n("Threshold"), "threshold"));
param.push_back(KisIntegerWidgetParam(0, 10, 1, i18n("Window size"), "windowsize"));
return new KisMultiIntegerFilterWidget(id().id(), parent, id().id(), param);
}
-KisFilterConfigurationSP KisSimpleNoiseReducer::defaultConfiguration() const
+KisFilterConfigurationSP KisSimpleNoiseReducer::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("threshold", 15);
config->setProperty("windowsize", 1);
return config;
}
inline int ABS(int v)
{
if (v < 0) return -v;
return v;
}
void KisSimpleNoiseReducer::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
- const KisFilterConfigurationSP _config,
+ const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
QPoint srcTopLeft = applyRect.topLeft();
Q_ASSERT(device);
- KisFilterConfigurationSP config = _config ? _config : defaultConfiguration();
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
const int threshold = config->getInt("threshold", 15);
const int windowsize = config->getInt("windowsize", 1);
const KoColorSpace* cs = device->colorSpace();
// Compute the blur mask
KisCircleMaskGenerator* kas = new KisCircleMaskGenerator(2*windowsize + 1, 1, windowsize, windowsize, 2, true);
KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMaskGenerator(kas);
delete kas;
KisPaintDeviceSP interm = new KisPaintDevice(*device); // TODO no need for a full copy and then a transaction
KisConvolutionPainter painter(interm);
painter.beginTransaction();
painter.applyMatrix(kernel, interm, srcTopLeft, srcTopLeft, applyRect.size(), BORDER_REPEAT);
painter.deleteTransaction();
KisSequentialConstIteratorProgress intermIt(interm, applyRect, progressUpdater);
KisSequentialIterator dstIt(device, applyRect);
while (dstIt.nextPixel() && intermIt.nextPixel()) {
const quint8 diff = cs->difference(dstIt.oldRawData(), intermIt.oldRawData());
if (diff > threshold) {
memcpy(dstIt.rawData(), intermIt.oldRawData(), cs->pixelSize());
}
}
}
QRect KisSimpleNoiseReducer::neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const
{
KisLodTransformScalar t(lod);
const int windowsize = _config->getInt("windowsize", 1);
const int margin = qCeil(t.scale(qreal(windowsize))) + 1;
return kisGrowRect(rect, margin);
}
QRect KisSimpleNoiseReducer::changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const
{
return neededRect(rect, _config, lod);
}
diff --git a/plugins/filters/imageenhancement/kis_simple_noise_reducer.h b/plugins/filters/imageenhancement/kis_simple_noise_reducer.h
index a06fa47254..343bc2d43b 100644
--- a/plugins/filters/imageenhancement/kis_simple_noise_reducer.h
+++ b/plugins/filters/imageenhancement/kis_simple_noise_reducer.h
@@ -1,52 +1,52 @@
/*
a * 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 KISSIMPLENOISEREDUCER_H
#define KISSIMPLENOISEREDUCER_H
#include <filter/kis_filter.h>
#include "kis_config_widget.h"
/**
@author Cyrille Berger
*/
class KisSimpleNoiseReducer : public KisFilter
{
public:
KisSimpleNoiseReducer();
~KisSimpleNoiseReducer() override;
public:
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
static inline KoID id() {
return KoID("gaussiannoisereducer", i18n("Gaussian Noise Reducer"));
}
QRect changedRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const override;
QRect neededRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const override;
protected:
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
};
#endif
diff --git a/plugins/filters/imageenhancement/kis_wavelet_noise_reduction.cpp b/plugins/filters/imageenhancement/kis_wavelet_noise_reduction.cpp
index da81ca1866..960055cfb2 100644
--- a/plugins/filters/imageenhancement/kis_wavelet_noise_reduction.cpp
+++ b/plugins/filters/imageenhancement/kis_wavelet_noise_reduction.cpp
@@ -1,125 +1,125 @@
/*
* 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.
*/
#include "kis_wavelet_noise_reduction.h"
#include <cmath>
#include <KoUpdater.h>
#include <kis_layer.h>
#include <kis_math_toolbox.h>
#include <widgets/kis_multi_double_filter_widget.h>
#include <widgets/kis_multi_integer_filter_widget.h>
#include <kis_paint_device.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include "kis_global.h"
KisWaveletNoiseReduction::KisWaveletNoiseReduction()
: KisFilter(id(), FiltersCategoryEnhanceId, i18n("&Wavelet Noise Reducer..."))
{
setSupportsPainting(false);
setSupportsThreading(false);
}
KisWaveletNoiseReduction::~KisWaveletNoiseReduction()
{
}
KisConfigWidget * KisWaveletNoiseReduction::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool) const
{
vKisDoubleWidgetParam param;
param.push_back(KisDoubleWidgetParam(0.0, 256.0, BEST_WAVELET_THRESHOLD_VALUE, i18n("Threshold"), "threshold"));
return new KisMultiDoubleFilterWidget(id().id(), parent, id().id(), param);
}
-KisFilterConfigurationSP KisWaveletNoiseReduction::defaultConfiguration() const
+KisFilterConfigurationSP KisWaveletNoiseReduction::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("threshold", BEST_WAVELET_THRESHOLD_VALUE);
return config;
}
void KisWaveletNoiseReduction::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
- const KisFilterConfigurationSP _config,
+ const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
Q_ASSERT(device);
- KisFilterConfigurationSP config = _config ? _config : defaultConfiguration();
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
const float threshold = config->getDouble("threshold", BEST_WAVELET_THRESHOLD_VALUE);
KisMathToolbox mathToolbox;
// dbgFilters << size <<"" << maxrectsize <<"" << srcTopLeft.x() <<"" << srcTopLeft.y();
// dbgFilters <<"Transforming...";
KisMathToolbox::KisWavelet* buff = 0;
KisMathToolbox::KisWavelet* wav = 0;
try {
buff = mathToolbox.initWavelet(device, applyRect);
} catch (const std::bad_alloc&) {
if (buff) delete buff;
return;
}
try {
wav = mathToolbox.fastWaveletTransformation(device, applyRect, buff);
} catch (const std::bad_alloc&) {
if (wav) delete wav;
return;
}
float* const fin = wav->coeffs + wav->depth * pow2(wav->size);
float* const begin = wav->coeffs + wav->depth;
const int size = fin - begin;
const int progressOffset = int(std::ceil(std::log2(size / 100)));
const int progressMask = (1 << progressOffset) - 1;
const int numProgressSteps = size >> progressOffset;
int pointsProcessed = 0;
progressUpdater->setRange(0, numProgressSteps);
for (float* it = begin; it < fin; it++) {
if (*it > threshold) {
*it -= threshold;
} else if (*it < -threshold) {
*it += threshold;
} else {
*it = 0.;
}
if (!(pointsProcessed & progressMask)) {
progressUpdater->setValue(pointsProcessed >> progressOffset);
}
pointsProcessed++;
}
mathToolbox.fastWaveletUntransformation(device, applyRect, wav, buff);
delete wav;
delete buff;
}
diff --git a/plugins/filters/imageenhancement/kis_wavelet_noise_reduction.h b/plugins/filters/imageenhancement/kis_wavelet_noise_reduction.h
index 20c0878d11..bd00c04806 100644
--- a/plugins/filters/imageenhancement/kis_wavelet_noise_reduction.h
+++ b/plugins/filters/imageenhancement/kis_wavelet_noise_reduction.h
@@ -1,56 +1,56 @@
/*
* 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_WAVELET_NOISE_REDUCTION_H
#define KIS_WAVELET_NOISE_REDUCTION_H
#include <vector>
#include <filter/kis_filter.h>
#define BEST_WAVELET_THRESHOLD_VALUE 7.0
/**
@author Cyrille Berger
*/
class KisWaveletNoiseReduction : public KisFilter
{
public:
KisWaveletNoiseReduction();
~KisWaveletNoiseReduction() override;
public:
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
static inline KoID id() {
return KoID("waveletnoisereducer", i18n("Wavelet Noise Reducer"));
}
private:
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
};
#endif
diff --git a/plugins/filters/indexcolors/indexcolors.cpp b/plugins/filters/indexcolors/indexcolors.cpp
index c3460028c6..1ed0609258 100644
--- a/plugins/filters/indexcolors/indexcolors.cpp
+++ b/plugins/filters/indexcolors/indexcolors.cpp
@@ -1,145 +1,145 @@
/*
* Copyright 2014 Manuel Riecke <spell1337@gmail.com>
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name of the author not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
*
* The author disclaim all warranties with regard to this
* software, including all implied warranties of merchantability
* and fitness. In no event shall the author be liable for any
* special, indirect or consequential damages or any damages
* whatsoever resulting from loss of use, data or profits, whether
* in an action of contract, negligence or other tortious action,
* arising out of or in connection with the use or performance of
* this software.
*/
#include "indexcolors.h"
#include <kpluginfactory.h>
#include <filter/kis_filter_registry.h>
#include <kis_global.h>
#include <KoColorSpaceMaths.h>
#include <KoColorSpaceRegistry.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_color_transformation_configuration.h>
#include <widgets/kis_multi_integer_filter_widget.h>
#include "kiswdgindexcolors.h"
#include "palettegeneratorconfig.h"
K_PLUGIN_FACTORY_WITH_JSON(IndexColorsFactory, "kritaindexcolors.json", registerPlugin<IndexColors>();)
IndexColors::IndexColors(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisFilterRegistry::instance()->add(KisFilterSP(new KisFilterIndexColors()));
}
IndexColors::~IndexColors()
{
}
KisFilterIndexColors::KisFilterIndexColors() : KisColorTransformationFilter(id(), FiltersCategoryArtisticId, i18n("&Index Colors..."))
{
setColorSpaceIndependence(FULLY_INDEPENDENT); // Technically it is TO_LAB16 but that would only display a warning we don't want
// This filter will always degrade the color space, that is it's purpose
setSupportsPainting(true);
setShowConfigurationWidget(true);
}
KoColorTransformation* KisFilterIndexColors::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const
{
IndexColorPalette pal;
PaletteGeneratorConfig palCfg;
palCfg.fromByteArray(config->getProperty("paletteGen").toByteArray());
pal = palCfg.generate();
if(config->getBool("reduceColorsEnabled"))
{
int maxClrs = config->getInt("colorLimit");
while(pal.numColors() > maxClrs)
pal.mergeMostReduantColors();
}
pal.similarityFactors.L = config->getFloat("LFactor");
pal.similarityFactors.a = config->getFloat("aFactor");
pal.similarityFactors.b = config->getFloat("bFactor");
return new KisIndexColorTransformation(pal, cs, config->getInt("alphaSteps"));
}
KisConfigWidget* KisFilterIndexColors::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
KisWdgIndexColors* w = new KisWdgIndexColors(parent);
w->setup(
QStringList() << i18nc("Color palette shade", "Bright") << i18nc("Color palette shade", "Light") << i18nc("Color palette shade", "Base") << i18nc("Color palette shade", "Shadow")
, 4
);
return w;
}
-KisFilterConfigurationSP KisFilterIndexColors::defaultConfiguration() const
+KisFilterConfigurationSP KisFilterIndexColors::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
PaletteGeneratorConfig palCfg; // Default constructor is factory config
config->setProperty("paletteGen", palCfg.toByteArray());
config->setProperty("LFactor", 1.f);
config->setProperty("aFactor", 1.f);
config->setProperty("bFactor", 1.f);
config->setProperty("reduceColorsEnabled", false);
config->setProperty("colorLimit", 32);
config->setProperty("alphaSteps", 1);
return config;
}
KisIndexColorTransformation::KisIndexColorTransformation(IndexColorPalette palette, const KoColorSpace* cs, int alphaSteps)
: m_colorSpace(cs),
m_psize(cs->pixelSize())
{
m_palette = palette;
static const qreal max = KoColorSpaceMathsTraits<quint16>::max;
if(alphaSteps > 0)
{
m_alphaStep = max / alphaSteps;
m_alphaHalfStep = m_alphaStep / 2;
}
else
{
m_alphaStep = 0;
m_alphaHalfStep = 0;
}
}
void KisIndexColorTransformation::transform(const quint8* src, quint8* dst, qint32 nPixels) const
{
union
{
quint16 laba[4];
LabColor lab;
} clr;
while (nPixels--)
{
m_colorSpace->toLabA16(src, reinterpret_cast<quint8 *>(clr.laba), 1);
clr.lab = m_palette.getNearestIndex(clr.lab);
if(m_alphaStep)
{
quint16 amod = clr.laba[3] % m_alphaStep;
clr.laba[3] = clr.laba[3] + (amod > m_alphaHalfStep ? m_alphaStep - amod : -amod);
}
m_colorSpace->fromLabA16(reinterpret_cast<quint8 *>(clr.laba), dst, 1);
src += m_psize;
dst += m_psize;
}
}
#include "indexcolors.moc"
diff --git a/plugins/filters/indexcolors/indexcolors.h b/plugins/filters/indexcolors/indexcolors.h
index 2c5d386810..4d8bcb12d9 100644
--- a/plugins/filters/indexcolors/indexcolors.h
+++ b/plugins/filters/indexcolors/indexcolors.h
@@ -1,69 +1,69 @@
/*
* Copyright 2014 Manuel Riecke <spell1337@gmail.com>
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name of the author not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
*
* The author disclaim all warranties with regard to this
* software, including all implied warranties of merchantability
* and fitness. In no event shall the author be liable for any
* special, indirect or consequential damages or any damages
* whatsoever resulting from loss of use, data or profits, whether
* in an action of contract, negligence or other tortious action,
* arising out of or in connection with the use or performance of
* this software.
*/
#ifndef INDEXCOLORS_H
#define INDEXCOLORS_H
#include <QObject>
#include <QVariant>
#include "filter/kis_color_transformation_filter.h"
#include "kis_config_widget.h"
#include <KoColor.h>
#include "indexcolorpalette.h"
class IndexColors : public QObject
{
Q_OBJECT
public:
IndexColors(QObject *parent, const QVariantList &);
~IndexColors() override;
};
class KisFilterIndexColors : public KisColorTransformationFilter
{
public:
KisFilterIndexColors();
public:
KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const override;
KisConfigWidget* createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
static inline KoID id() {
return KoID("indexcolors", i18n("Index Colors"));
}
protected:
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
};
class KisIndexColorTransformation : public KoColorTransformation
{
public:
KisIndexColorTransformation(IndexColorPalette palette, const KoColorSpace* cs, int alphaSteps);
void transform(const quint8* src, quint8* dst, qint32 nPixels) const override;
private:
const KoColorSpace* m_colorSpace;
quint32 m_psize;
IndexColorPalette m_palette;
quint16 m_alphaStep;
quint16 m_alphaHalfStep;
};
#endif
diff --git a/plugins/filters/indexcolors/kiswdgindexcolors.cpp b/plugins/filters/indexcolors/kiswdgindexcolors.cpp
index 63d2a81b1d..6ac4899b53 100644
--- a/plugins/filters/indexcolors/kiswdgindexcolors.cpp
+++ b/plugins/filters/indexcolors/kiswdgindexcolors.cpp
@@ -1,192 +1,193 @@
/*
* Copyright 2014 Manuel Riecke <spell1337@gmail.com>
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name of the author not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
*
* The author disclaim all warranties with regard to this
* software, including all implied warranties of merchantability
* and fitness. In no event shall the author be liable for any
* special, indirect or consequential damages or any damages
* whatsoever resulting from loss of use, data or profits, whether
* in an action of contract, negligence or other tortious action,
* arising out of or in connection with the use or performance of
* this software.
*/
#include "filter/kis_color_transformation_configuration.h"
#include "kiswdgindexcolors.h"
#include "palettegeneratorconfig.h"
#include "ui_kiswdgindexcolors.h"
+#include <KisGlobalResourcesInterface.h>
#include "kis_int_parse_spin_box.h"
#include <kis_color_button.h>
KisWdgIndexColors::KisWdgIndexColors(QWidget* parent, Qt::WindowFlags f, int delay): KisConfigWidget(parent, f, delay)
{
ui = new Ui::KisWdgIndexColors;
ui->setupUi(this);
connect(ui->diagCheck, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(ui->inbetweenSpinBox, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(ui->alphaStepsSpinBox, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(ui->colorLimit, SIGNAL(valueChanged(int)), SLOT(slotColorLimitChanged(int)));
connect(ui->colorLimit, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(ui->colorLimitCheck, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(ui->luminanceSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(ui->aSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(ui->bSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
}
void KisWdgIndexColors::slotColorLimitChanged(int value)
{
ui->colorLimit->setSuffix(i18ncp("suffix for a spinbox",
" color", " colors", value));
}
void KisWdgIndexColors::setup(QStringList shadesLabels, int ramps)
{
int rows = shadesLabels.length();
int columns = ramps;
m_colorSelectors.resize(rows);
m_stepSpinners.resize(rows-1);
// Labels for the shades
for(int row = 0; row < rows; ++row)
{
QLabel* l = new QLabel(shadesLabels[row], ui->colorsBox);
ui->layoutColors->addWidget(l, row+1, 0);
m_colorSelectors[row].resize(columns);
}
// Labels for the ramps
/*for(int col = 0; col < columns; ++col)
{
QLabel* l = new QLabel(rampsLabels[col], ui->colorsBox);
l->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
ui->layoutColors->addWidget(l, 0, col+1);
}*/
// Step selectors for the shade gradients
for(int row = 0; row < (rows-1); ++row)
{
QLabel* l0 = new QLabel(shadesLabels[row+1]);
QLabel* l1 = new QLabel(QString::fromUtf8("↔"));
QLabel* l2 = new QLabel(shadesLabels[row]);
QSpinBox* s = new KisIntParseSpinBox();
s->setMinimum(0);
s->setMaximum(32);
s->setValue(2);
connect(s, SIGNAL(valueChanged(int)), this, SIGNAL(sigConfigurationItemChanged()));
m_stepSpinners[row] = s;
ui->layoutRowSteps->addWidget(l0, row, 0);
ui->layoutRowSteps->addWidget(l1, row, 1);
ui->layoutRowSteps->addWidget(l2, row, 2);
ui->layoutRowSteps->addWidget(s, row, 3);
}
// Color selectors
for(int y = 0; y < rows; ++y)
for(int x = 0; x < columns; ++x)
{
KisColorButton* b = new KisColorButton;
QCheckBox* c = new QCheckBox;
c->setChecked(false);
b->setEnabled(false);
b->setMaximumWidth(50);
c->setMaximumWidth(21); // Ugh. I hope this won't be causing any issues. Trying to get rid of the unnecessary spacing after it.
connect(c, SIGNAL(toggled(bool)), b, SLOT(setEnabled(bool)));
connect(c, SIGNAL(toggled(bool)), this, SIGNAL(sigConfigurationItemChanged()));
connect(b, SIGNAL(changed(KoColor)), this, SIGNAL(sigConfigurationItemChanged()));
QHBoxLayout* cell = new QHBoxLayout();
cell->setSpacing(0);
cell->setContentsMargins(0, 0, 0, 0);
cell->addWidget(c);
cell->addWidget(b);
ui->layoutColors->addLayout(cell, 1+y, 1+x);
m_colorSelectors[y][x].button = b;
m_colorSelectors[y][x].checkbox = c;
}
}
KisPropertiesConfigurationSP KisWdgIndexColors::configuration() const
{
- KisColorTransformationConfigurationSP config = new KisColorTransformationConfiguration("indexcolors", 1);
+ KisColorTransformationConfigurationSP config = new KisColorTransformationConfiguration("indexcolors", 1, KisGlobalResourcesInterface::instance());
PaletteGeneratorConfig palCfg;
for(int y = 0; y < 4; ++y)
for(int x = 0; x < 4; ++x)
{
palCfg.colors[y][x] = m_colorSelectors[y][x].button->color().toQColor();
palCfg.colorsEnabled[y][x] = m_colorSelectors[y][x].button->isEnabled();
}
for(int y = 0; y < 3; ++y)
palCfg.gradientSteps[y] = m_stepSpinners[y]->value();
palCfg.diagonalGradients = ui->diagCheck->isChecked();
palCfg.inbetweenRampSteps = ui->inbetweenSpinBox->value();
IndexColorPalette pal = palCfg.generate();
ui->colorCount->setText(QString::number(pal.numColors()));
config->setProperty("paletteGen", palCfg.toByteArray());
config->setProperty("LFactor", ui->luminanceSlider->value() / 100.f);
config->setProperty("aFactor", ui->aSlider->value() / 100.f);
config->setProperty("bFactor", ui->bSlider->value() / 100.f);
config->setProperty("reduceColorsEnabled", ui->colorLimitCheck->isChecked());
config->setProperty("colorLimit", ui->colorLimit->value());
config->setProperty("alphaSteps", ui->alphaStepsSpinBox->value());
return config;
}
void KisWdgIndexColors::setConfiguration(const KisPropertiesConfigurationSP config)
{
PaletteGeneratorConfig palCfg;
palCfg.fromByteArray(config->getProperty("paletteGen").toByteArray());
ui->luminanceSlider->setValue(config->getFloat("LFactor")*100);
ui->aSlider->setValue(config->getFloat("aFactor")*100);
ui->bSlider->setValue(config->getFloat("bFactor")*100);
ui->alphaStepsSpinBox->setValue(config->getInt("alphaSteps"));
ui->colorLimitCheck->setChecked(config->getBool("reduceColorsEnabled"));
ui->colorLimit->setEnabled(config->getBool("reduceColorsEnabled"));
ui->colorLimit->setValue(config->getInt("colorLimit"));
ui->diagCheck->setChecked(palCfg.diagonalGradients);
ui->inbetweenSpinBox->setValue(palCfg.inbetweenRampSteps);
for(int y = 0; y < 4; ++y)
for(int x = 0; x < 4; ++x)
{
m_colorSelectors[y][x].checkbox->setChecked(palCfg.colorsEnabled[y][x]);
m_colorSelectors[y][x].button->setEnabled(palCfg.colorsEnabled[y][x]);
KoColor c;
c.fromQColor(palCfg.colors[y][x]);
m_colorSelectors[y][x].button->setColor(c);
}
for(int y = 0; y < 3; ++y)
m_stepSpinners[y]->setValue(palCfg.gradientSteps[y]);
IndexColorPalette pal = palCfg.generate();
ui->colorCount->setText(QString::number(pal.numColors()));
}
diff --git a/plugins/filters/levelfilter/kis_level_filter.cpp b/plugins/filters/levelfilter/kis_level_filter.cpp
index 4e37018236..e00ddefe77 100644
--- a/plugins/filters/levelfilter/kis_level_filter.cpp
+++ b/plugins/filters/levelfilter/kis_level_filter.cpp
@@ -1,345 +1,346 @@
/*
* This file is part of Krita
*
* Copyright (c) 2006 Frederic Coiffier <fcoiffie@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_level_filter.h"
#include <cmath>
#include <klocalizedstring.h>
#include <QtGlobal>
#include <QLayout>
#include <QPixmap>
#include <QPainter>
#include <QLabel>
#include <QSpinBox>
#include <KoBasicHistogramProducers.h>
#include <KoColorSpace.h>
#include <KoColorTransformation.h>
#include "kis_paint_device.h"
#include "kis_histogram.h"
#include "kis_painter.h"
#include "KisGradientSlider.h"
#include "kis_processing_information.h"
#include "kis_selection.h"
#include "kis_types.h"
#include <filter/kis_filter_category_ids.h>
#include "filter/kis_color_transformation_configuration.h"
+#include <KisGlobalResourcesInterface.h>
KisLevelFilter::KisLevelFilter()
: KisColorTransformationFilter(id(), FiltersCategoryAdjustId, i18n("&Levels..."))
{
setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L));
setSupportsPainting(false);
setColorSpaceIndependence(TO_LAB16);
}
KisLevelFilter::~KisLevelFilter()
{
}
KisConfigWidget * KisLevelFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
return new KisLevelConfigWidget(parent, dev);
}
KoColorTransformation* KisLevelFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const
{
if (!config) {
warnKrita << "No configuration object for level filter\n";
return 0;
}
Q_ASSERT(config);
int blackvalue = config->getInt("blackvalue");
int whitevalue = config->getInt("whitevalue", 255);
double gammavalue = config->getDouble("gammavalue", 1.0);
int outblackvalue = config->getInt("outblackvalue");
int outwhitevalue = config->getInt("outwhitevalue", 255);
quint16 transfer[256];
for (int i = 0; i < 256; i++) {
if (i <= blackvalue)
transfer[i] = outblackvalue;
else if (i < whitevalue) {
double a = (double)(i - blackvalue) / (double)(whitevalue - blackvalue);
a = (double)(outwhitevalue - outblackvalue) * pow(a, (1.0 / gammavalue));
transfer[i] = int(outblackvalue + a);
} else
transfer[i] = outwhitevalue;
// TODO use floats instead of integer in the configuration
transfer[i] = ((int)transfer[i] * 0xFFFF) / 0xFF ;
}
return cs->createBrightnessContrastAdjustment(transfer);
}
KisLevelConfigWidget::KisLevelConfigWidget(QWidget * parent, KisPaintDeviceSP dev)
: KisConfigWidget(parent)
{
Q_ASSERT(dev);
m_page.setupUi(this);
m_page.ingradient->enableGamma(true);
m_page.blackspin->setValue(0);
m_page.whitespin->setValue(255);
m_page.gammaspin->setValue(1.0);
m_page.ingradient->slotModifyGamma(1.0);
m_page.outblackspin->setValue(0);
m_page.outwhitespin->setValue(255);
connect(m_page.blackspin, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page.whitespin, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page.ingradient, SIGNAL(sigModifiedGamma(double)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page.blackspin, SIGNAL(valueChanged(int)), m_page.ingradient, SLOT(slotModifyBlack(int)));
connect(m_page.whitespin, SIGNAL(valueChanged(int)), m_page.ingradient, SLOT(slotModifyWhite(int)));
connect(m_page.gammaspin, SIGNAL(valueChanged(double)), m_page.ingradient, SLOT(slotModifyGamma(double)));
connect(m_page.blackspin, SIGNAL(valueChanged(int)), this, SLOT(slotModifyInWhiteLimit(int)));
connect(m_page.whitespin, SIGNAL(valueChanged(int)), this, SLOT(slotModifyInBlackLimit(int)));
connect(m_page.ingradient, SIGNAL(sigModifiedBlack(int)), m_page.blackspin, SLOT(setValue(int)));
connect(m_page.ingradient, SIGNAL(sigModifiedWhite(int)), m_page.whitespin, SLOT(setValue(int)));
connect(m_page.ingradient, SIGNAL(sigModifiedGamma(double)), m_page.gammaspin, SLOT(setValue(double)));
connect(m_page.outblackspin, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page.outwhitespin, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page.outblackspin, SIGNAL(valueChanged(int)), m_page.outgradient, SLOT(slotModifyBlack(int)));
connect(m_page.outwhitespin, SIGNAL(valueChanged(int)), m_page.outgradient, SLOT(slotModifyWhite(int)));
connect(m_page.outblackspin, SIGNAL(valueChanged(int)), this, SLOT(slotModifyOutWhiteLimit(int)));
connect(m_page.outwhitespin, SIGNAL(valueChanged(int)), this, SLOT(slotModifyOutBlackLimit(int)));
connect(m_page.outgradient, SIGNAL(sigModifiedBlack(int)), m_page.outblackspin, SLOT(setValue(int)));
connect(m_page.outgradient, SIGNAL(sigModifiedWhite(int)), m_page.outwhitespin, SLOT(setValue(int)));
connect(m_page.butauto, SIGNAL(clicked(bool)), this, SLOT(slotAutoLevel()));
connect(m_page.butinvert, SIGNAL(clicked(bool)), this, SLOT(slotInvert()));
connect((QObject*)(m_page.chkLogarithmic), SIGNAL(toggled(bool)), this, SLOT(slotDrawHistogram(bool)));
KoHistogramProducer *producer = new KoGenericLabHistogramProducer();
m_histogram.reset( new KisHistogram(dev, dev->exactBounds(), producer, LINEAR) );
m_isLogarithmic = false;
//m_page.histview->resize(288,100);
m_inverted = false;
slotDrawHistogram(m_page.chkLogarithmic->isChecked());
}
KisLevelConfigWidget::~KisLevelConfigWidget()
{
}
void KisLevelConfigWidget::slotDrawHistogram(bool isLogarithmic)
{
int wHeight = m_page.histview->height();
int wHeightMinusOne = wHeight - 1;
int wWidth = m_page.histview->width();
if (m_isLogarithmic != isLogarithmic) {
// Update the m_histogram
if (isLogarithmic)
m_histogram->setHistogramType(LOGARITHMIC);
else
m_histogram->setHistogramType(LINEAR);
m_isLogarithmic = isLogarithmic;
}
QPalette appPalette = QApplication::palette();
QPixmap pix(wWidth, wHeight);
pix.fill(QColor(appPalette.color(QPalette::Base)));
QPainter p(&pix);
p.setPen(QPen(Qt::gray, 1, Qt::SolidLine));
double highest = (double)m_histogram->calculations().getHighest();
qint32 bins = m_histogram->producer()->numberOfBins();
// use nearest neighbour interpolation
if (m_histogram->getHistogramType() == LINEAR) {
double factor = (double)(wHeight - wHeight / 5.0) / highest;
for (int i = 0; i < wWidth; i++) {
int binNo = qRound((double)i / wWidth * (bins - 1));
if ((int)m_histogram->getValue(binNo) != 0)
p.drawLine(i, wHeightMinusOne, i, wHeightMinusOne - (int)m_histogram->getValue(binNo) * factor);
}
} else {
double factor = (double)(wHeight - wHeight / 5.0) / (double)log(highest);
for (int i = 0; i < wWidth; i++) {
int binNo = qRound((double)i / wWidth * (bins - 1)) ;
if ((int)m_histogram->getValue(binNo) != 0)
p.drawLine(i, wHeightMinusOne, i, wHeightMinusOne - log((double)m_histogram->getValue(binNo)) * factor);
}
}
m_page.histview->setPixmap(pix);
}
void KisLevelConfigWidget::slotModifyInBlackLimit(int limit)
{
m_page.blackspin->setMaximum(limit - 1);
}
void KisLevelConfigWidget::slotModifyInWhiteLimit(int limit)
{
m_page.whitespin->setMinimum(limit + 1);
}
void KisLevelConfigWidget::slotModifyOutBlackLimit(int limit)
{
if (m_inverted) {
m_page.outblackspin->setMinimum(limit + 1);
} else {
m_page.outblackspin->setMaximum(limit - 1);
}
}
void KisLevelConfigWidget::slotModifyOutWhiteLimit(int limit)
{
if (m_inverted) {
m_page.outwhitespin->setMaximum(limit - 1);
} else {
m_page.outwhitespin->setMinimum(limit + 1);
}
}
void KisLevelConfigWidget::slotAutoLevel(void)
{
Q_ASSERT(m_histogram);
qint32 num_bins = m_histogram->producer()->numberOfBins();
Q_ASSERT(num_bins > 1);
int chosen_low_bin = 0, chosen_high_bin = num_bins-1;
int count_thus_far = m_histogram->getValue(0);
const int total_count = m_histogram->producer()->count();
const double threshold = 0.006;
// find the low and hi point/bins based on summing count percentages
//
// this implementation is a port of GIMP's auto level implementation
// (use a GPLv2 version as reference, specifically commit 51bfd07f18ef045a3e43632218fd92cae9ff1e48)
for (int bin=0; bin<(num_bins-1); ++bin) {
int next_count_thus_far = count_thus_far + m_histogram->getValue(bin+1);
double this_percentage = static_cast<double>(count_thus_far) / total_count;
double next_percentage = static_cast<double>(next_count_thus_far) / total_count;
//dbgKrita << "bin" << bin << "this_percentage" << this_percentage << "next_percentage" << next_percentage;
if (fabs(this_percentage - threshold) < fabs(next_percentage - threshold)) {
chosen_low_bin = bin;
break;
}
count_thus_far = next_count_thus_far;
}
count_thus_far = m_histogram->getValue(num_bins-1);
for (int bin=(num_bins-1); bin>0; --bin) {
int next_count_thus_far = count_thus_far + m_histogram->getValue(bin-1);
double this_percentage = static_cast<double>(count_thus_far) / total_count;
double next_percentage = static_cast<double>(next_count_thus_far) / total_count;
//dbgKrita << "hi-bin" << bin << "this_percentage" << this_percentage << "next_percentage" << next_percentage;
if (fabs(this_percentage - threshold) < fabs(next_percentage - threshold)) {
chosen_high_bin = bin;
break;
}
count_thus_far = next_count_thus_far;
}
if (chosen_low_bin < chosen_high_bin) {
m_page.blackspin->setValue(chosen_low_bin);
m_page.ingradient->slotModifyBlack(chosen_low_bin);
m_page.whitespin->setValue(chosen_high_bin);
m_page.ingradient->slotModifyWhite(chosen_high_bin);
}
}
void KisLevelConfigWidget::slotInvert(void)
{
m_inverted = !m_inverted;
int white = m_page.outwhitespin->value();
int black = m_page.outblackspin->value();
resetOutSpinLimit();
m_page.outgradient->setInverted(m_inverted);
m_page.outwhitespin->setValue(black);
m_page.outblackspin->setValue(white);
}
KisPropertiesConfigurationSP KisLevelConfigWidget::configuration() const
{
- KisColorTransformationConfiguration * config = new KisColorTransformationConfiguration(KisLevelFilter::id().id(), 1);
+ KisColorTransformationConfiguration * config = new KisColorTransformationConfiguration(KisLevelFilter::id().id(), 1, KisGlobalResourcesInterface::instance());
config->setProperty("blackvalue", m_page.blackspin->value());
config->setProperty("whitevalue", m_page.whitespin->value());
config->setProperty("gammavalue", m_page.gammaspin->value());
config->setProperty("outblackvalue", m_page.outblackspin->value());
config->setProperty("outwhitevalue", m_page.outwhitespin->value());
return config;
}
void KisLevelConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("blackvalue", value)) {
m_page.blackspin->setValue(value.toUInt());
m_page.ingradient->slotModifyBlack(value.toUInt());
}
if (config->getProperty("whitevalue", value)) {
m_page.whitespin->setValue(value.toUInt());
m_page.ingradient->slotModifyWhite(value.toUInt());
}
if (config->getProperty("gammavalue", value)) {
m_page.gammaspin->setValue(value.toUInt());
m_page.ingradient->slotModifyGamma(value.toDouble());
}
if (config->getProperty("outblackvalue", value)) {
m_page.outblackspin->setValue(value.toUInt());
m_page.outgradient->slotModifyBlack(value.toUInt());
}
if (config->getProperty("outwhitevalue", value)) {
m_page.outwhitespin->setValue(value.toUInt());
m_page.outgradient->slotModifyWhite(value.toUInt());
}
}
void KisLevelConfigWidget::resetOutSpinLimit() {
if (m_inverted) {
m_page.outblackspin->setMaximum(255);
m_page.outwhitespin->setMinimum(0);
} else {
m_page.outblackspin->setMinimum(0);
m_page.outwhitespin->setMaximum(255);
}
}
diff --git a/plugins/filters/noisefilter/kis_wdg_noise.cpp b/plugins/filters/noisefilter/kis_wdg_noise.cpp
index f7020848a9..ace96de7f9 100644
--- a/plugins/filters/noisefilter/kis_wdg_noise.cpp
+++ b/plugins/filters/noisefilter/kis_wdg_noise.cpp
@@ -1,72 +1,73 @@
/*
* 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_noise.h"
#include <QLayout>
#include <filter/kis_filter_configuration.h>
+#include <KisGlobalResourcesInterface.h>
#include "ui_wdgnoiseoptions.h"
KisWdgNoise::KisWdgNoise(KisFilter* /*nfilter*/, QWidget* parent)
: KisConfigWidget(parent)
{
m_widget = new Ui_WdgNoiseOptions();
m_widget->setupUi(this);
connect(widget()->intLevel, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->intOpacity, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
m_seedThreshold = rand();
m_seedRed = rand();
m_seedGreen = rand();
m_seedBlue = rand();
}
KisWdgNoise::~KisWdgNoise()
{
delete m_widget;
}
void KisWdgNoise::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("level", value)) {
widget()->intLevel->setValue(value.toUInt());
}
if (config->getProperty("opacity", value)) {
widget()->intOpacity->setValue(value.toUInt());
}
}
KisPropertiesConfigurationSP KisWdgNoise::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("noise", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("noise", 1, KisGlobalResourcesInterface::instance());
config->setProperty("level", this->widget()->intLevel->value());
config->setProperty("opacity", this->widget()->intOpacity->value());
config->setProperty("seedThreshold", m_seedThreshold);
config->setProperty("seedRed", m_seedRed);
config->setProperty("seedGreen", m_seedGreen);
config->setProperty("seedBlue", m_seedBlue);
return config;
}
diff --git a/plugins/filters/noisefilter/noisefilter.cpp b/plugins/filters/noisefilter/noisefilter.cpp
index 3005f3c8c7..bfc25a4078 100644
--- a/plugins/filters/noisefilter/noisefilter.cpp
+++ b/plugins/filters/noisefilter/noisefilter.cpp
@@ -1,149 +1,149 @@
/*
* 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.
*/
#include "noisefilter.h"
#include <stdlib.h>
#include <vector>
#include <QPoint>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <klocalizedstring.h>
#include <KoUpdater.h>
#include <KoMixColorsOp.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_layer.h>
#include <filter/kis_filter_registry.h>
#include <kis_global.h>
#include <kis_random_generator.h>
#include <kis_selection.h>
#include <kis_types.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include "kis_wdg_noise.h"
#include "ui_wdgnoiseoptions.h"
#include <KisSequentialIteratorProgress.h>
K_PLUGIN_FACTORY_WITH_JSON(KritaNoiseFilterFactory, "kritanoisefilter.json", registerPlugin<KritaNoiseFilter>();)
KritaNoiseFilter::KritaNoiseFilter(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisFilterRegistry::instance()->add(new KisFilterNoise());
}
KritaNoiseFilter::~KritaNoiseFilter()
{
}
KisFilterNoise::KisFilterNoise() : KisFilter(id(), FiltersCategoryOtherId, i18n("&Random Noise..."))
{
setColorSpaceIndependence(FULLY_INDEPENDENT);
setSupportsPainting(true);
}
-KisFilterConfigurationSP KisFilterNoise::defaultConfiguration() const
+KisFilterConfigurationSP KisFilterNoise::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("level", 50);
config->setProperty("opacity", 100);
config->setProperty("seedThreshold", rand());
config->setProperty("seedRed", rand());
config->setProperty("seedGreen", rand());
config->setProperty("seedBlue", rand());
return config;
}
KisConfigWidget * KisFilterNoise::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
return new KisWdgNoise((KisFilter*)this, (QWidget*)parent);
}
void KisFilterNoise::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
Q_ASSERT(!device.isNull());
const KoColorSpace * cs = device->colorSpace();
QVariant value;
const int level = (config && config->getProperty("level", value)) ? value.toInt() : 50;
const int opacity = (config && config->getProperty("opacity", value)) ? value.toInt() : 100;
quint8* interm = new quint8[cs->pixelSize()];
const double threshold = (100.0 - level) * 0.01;
qint16 weights[2];
weights[0] = (255 * opacity) / 100; weights[1] = 255 - weights[0];
const quint8* pixels[2];
pixels[0] = interm;
KoMixColorsOp * mixOp = cs->mixColorsOp();
int seedThreshold = rand();
int seedRed = rand();
int seedGreen = rand();
int seedBlue = rand();
if (config) {
seedThreshold = config->getInt("seedThreshold", seedThreshold);
seedRed = config->getInt("seedRed", seedRed);
seedGreen = config->getInt("seedGreen", seedGreen);
seedBlue = config->getInt("seedBlue", seedBlue);
}
KisRandomGenerator randt(seedThreshold);
KisRandomGenerator randr(seedRed);
KisRandomGenerator randg(seedGreen);
KisRandomGenerator randb(seedBlue);
KisSequentialIteratorProgress it(device, applyRect, progressUpdater);
while (it.nextPixel()) {
if (randt.doubleRandomAt(it.x(), it.y()) > threshold) {
// XXX: Added static_cast to get rid of warnings
QColor c = qRgb(static_cast<int>((double)randr.doubleRandomAt(it.x(), it.y()) * 255),
static_cast<int>((double)randg.doubleRandomAt(it.x(), it.y()) * 255),
static_cast<int>((double)randb.doubleRandomAt(it.x(), it.y()) * 255));
cs->fromQColor(c, interm, 0);
pixels[1] = it.oldRawData();
mixOp->mixColors(pixels, weights, 2, it.rawData());
}
}
delete [] interm;
}
#include "noisefilter.moc"
diff --git a/plugins/filters/noisefilter/noisefilter.h b/plugins/filters/noisefilter/noisefilter.h
index 34bd76204a..7eb8c9087c 100644
--- a/plugins/filters/noisefilter/noisefilter.h
+++ b/plugins/filters/noisefilter/noisefilter.h
@@ -1,56 +1,56 @@
/*
* 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.
*/
#ifndef NOISEFILTER_H
#define NOISEFILTER_H
#include <QObject>
#include <QVariant>
#include "filter/kis_filter.h"
class KisConfigWidget;
class KritaNoiseFilter : public QObject
{
Q_OBJECT
public:
KritaNoiseFilter(QObject *parent, const QVariantList &);
~KritaNoiseFilter() override;
};
class KisFilterNoise : public KisFilter
{
public:
KisFilterNoise();
public:
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("noise", i18n("Random Noise"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
};
#endif
diff --git a/plugins/filters/oilpaintfilter/kis_oilpaint_filter.cpp b/plugins/filters/oilpaintfilter/kis_oilpaint_filter.cpp
index 64ff6cf932..0a0ac54844 100644
--- a/plugins/filters/oilpaintfilter/kis_oilpaint_filter.cpp
+++ b/plugins/filters/oilpaintfilter/kis_oilpaint_filter.cpp
@@ -1,204 +1,205 @@
/*
* This file is part of Krita
*
* Copyright (c) 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
*
* ported from digikam, Copyrighted by Gilles Caulier,
* Original Oilpaint algorithm copyrighted 2004 by
* Pieter Z. Voloshyn <pieter_voloshyn at ame.com.br>.
*
* 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_oilpaint_filter.h"
#include <stdlib.h>
#include <vector>
#include <QPoint>
#include <QSpinBox>
#include <QDateTime>
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <KoUpdater.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <KisSequentialIteratorProgress.h>
#include <kis_layer.h>
#include <filter/kis_filter_registry.h>
#include <kis_global.h>
#include <kis_types.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include <kis_paint_device.h>
#include "widgets/kis_multi_integer_filter_widget.h"
+#include <KisGlobalResourcesInterface.h>
KisOilPaintFilter::KisOilPaintFilter() : KisFilter(id(), FiltersCategoryArtisticId, i18n("&Oilpaint..."))
{
setSupportsPainting(true);
setSupportsThreading(false);
setSupportsAdjustmentLayers(true);
}
void KisOilPaintFilter::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
Q_ASSERT(!device.isNull());
//read the filter configuration values from the KisFilterConfiguration object
const quint32 brushSize = config ? config->getInt("brushSize", 1) : 1;
const quint32 smooth = config ? config->getInt("smooth", 30) : 30;
OilPaint(device, device, applyRect, brushSize, smooth, progressUpdater);
}
// This method have been ported from Pieter Z. Voloshyn algorithm code.
/* Function to apply the OilPaint effect.
*
* data => The image data in RGBA mode.
* w => Width of image.
* h => Height of image.
* BrushSize => Brush size.
* Smoothness => Smooth value.
*
* Theory => Using MostFrequentColor function we take the main color in
* a matrix and simply write at the original position.
*/
void KisOilPaintFilter::OilPaint(const KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &applyRect,
int BrushSize, int Smoothness, KoUpdater* progressUpdater) const
{
KisSequentialConstIteratorProgress it(src, applyRect, progressUpdater);
KisSequentialIterator dstIt(dst, applyRect);
while (it.nextPixel() && dstIt.nextPixel()) {
MostFrequentColor(src, dstIt.rawData(), applyRect, it.x(), it.y(), BrushSize, Smoothness);
}
}
// This method has been ported from Pieter Z. Voloshyn's algorithm code in Digikam.
/* Function to determine the most frequent color in a matrix
*
* Bits => Bits array
* Width => Image width
* Height => Image height
* X => Position horizontal
* Y => Position vertical
* Radius => Is the radius of the matrix to be analyzed
* Intensity => Intensity to calculate
*
* Theory => This function creates a matrix with the analyzed pixel in
* the center of this matrix and find the most frequenty color
*/
void KisOilPaintFilter::MostFrequentColor(KisPaintDeviceSP src, quint8* dst, const QRect& bounds, int X, int Y, int Radius, int Intensity) const
{
uint I;
double Scale = Intensity / 255.0;
// Alloc some arrays to be used
uchar *IntensityCount = new uchar[(Intensity + 1) * sizeof(uchar)];
const KoColorSpace* cs = src->colorSpace();
QVector<float> channel(cs->channelCount());
QVector<float>* AverageChannels = new QVector<float>[(Intensity + 1)];
// Erase the array
memset(IntensityCount, 0, (Intensity + 1) * sizeof(uchar));
int startx = qMax(X - Radius, bounds.left());
int starty = qMax(Y - Radius, bounds.top());
int width = (2 * Radius) + 1;
if ((startx + width - 1) > bounds.right()) width = bounds.right() - startx + 1;
Q_ASSERT((startx + width - 1) <= bounds.right());
int height = (2 * Radius) + 1;
if ((starty + height) > bounds.bottom()) height = bounds.bottom() - starty + 1;
Q_ASSERT((starty + height - 1) <= bounds.bottom());
KisSequentialConstIterator srcIt(src, QRect(startx, starty, width, height));
while (srcIt.nextPixel()) {
cs->normalisedChannelsValue(srcIt.rawDataConst(), channel);
I = (uint)(cs->intensity8(srcIt.rawDataConst()) * Scale);
IntensityCount[I]++;
if (IntensityCount[I] == 1) {
AverageChannels[I] = channel;
} else {
for (int i = 0; i < channel.size(); i++) {
AverageChannels[I][i] += channel[i];
}
}
}
I = 0;
int MaxInstance = 0;
for (int i = 0 ; i <= Intensity ; ++i) {
if (IntensityCount[i] > MaxInstance) {
I = i;
MaxInstance = IntensityCount[i];
}
}
if (MaxInstance != 0) {
channel = AverageChannels[I];
for (int i = 0; i < channel.size(); i++) {
channel[i] /= MaxInstance;
}
cs->fromNormalisedChannelsValue(dst, channel);
} else {
memset(dst, 0, cs->pixelSize());
cs->setOpacity(dst, OPACITY_OPAQUE_U8, 1);
}
delete [] IntensityCount; // free all the arrays
delete [] AverageChannels;
}
KisConfigWidget * KisOilPaintFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool) const
{
vKisIntegerWidgetParam param;
param.push_back(KisIntegerWidgetParam(1, 5, 1, i18n("Brush size"), "brushSize"));
param.push_back(KisIntegerWidgetParam(10, 255, 30, i18nc("smooth out the painting strokes the filter creates", "Smooth"), "smooth"));
KisMultiIntegerFilterWidget * w = new KisMultiIntegerFilterWidget(id().id(), parent, id().id(), param);
- w->setConfiguration(defaultConfiguration());
+ w->setConfiguration(defaultConfiguration(KisGlobalResourcesInterface::instance()));
return w;
}
-KisFilterConfigurationSP KisOilPaintFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisOilPaintFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("brushSize", 1);
config->setProperty("smooth", 30);
return config;
}
diff --git a/plugins/filters/oilpaintfilter/kis_oilpaint_filter.h b/plugins/filters/oilpaintfilter/kis_oilpaint_filter.h
index 2ce3fafc1a..979d3e49ed 100644
--- a/plugins/filters/oilpaintfilter/kis_oilpaint_filter.h
+++ b/plugins/filters/oilpaintfilter/kis_oilpaint_filter.h
@@ -1,51 +1,51 @@
/*
* This file is part of the KDE project
*
* Copyright (c) Michael Thaler <michael.thaler@physik.tu-muenchen.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.
*/
#ifndef _KIS_OILPAINT_FILTER_H_
#define _KIS_OILPAINT_FILTER_H_
#include "filter/kis_filter.h"
#include "kis_config_widget.h"
class KisOilPaintFilter : public KisFilter
{
public:
KisOilPaintFilter();
public:
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater ) const override;
static inline KoID id() {
return KoID("oilpaint", i18n("Oilpaint"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
private:
void OilPaint(const KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &applyRect,
int BrushSize, int Smoothness, KoUpdater* progressUpdater) const;
void MostFrequentColor(KisPaintDeviceSP src, quint8* dst, const QRect& bounds, int X, int Y, int Radius, int Intensity) const;
};
#endif
diff --git a/plugins/filters/palettize/palettize.cpp b/plugins/filters/palettize/palettize.cpp
index e37b519e6d..945e9fbf19 100644
--- a/plugins/filters/palettize/palettize.cpp
+++ b/plugins/filters/palettize/palettize.cpp
@@ -1,287 +1,355 @@
/*
* This file is part of Krita
*
* Copyright (c) 2019 Carl Olsson <carl.olsson@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 "palettize.h"
#include <kis_types.h>
#include <kpluginfactory.h>
#include <kis_config_widget.h>
#include <kis_filter_registry.h>
#include <kis_filter_configuration.h>
#include <kis_filter_category_ids.h>
#include <KoUpdater.h>
#include <KisSequentialIteratorProgress.h>
-#include <KoResourceServerProvider.h>
-#include <KoResourceServer.h>
-#include <KoResourceServerAdapter.h>
-#include <KoResourceItemChooser.h>
+#include <KisResourceItemChooser.h>
#include <KoColorSet.h>
#include <KoPattern.h>
#include <kis_random_generator.h>
#include <KisDitherUtil.h>
+#include <KisGlobalResourcesInterface.h>
K_PLUGIN_FACTORY_WITH_JSON(PalettizeFactory, "kritapalettize.json", registerPlugin<Palettize>();)
Palettize::Palettize(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisFilterRegistry::instance()->add(new KisFilterPalettize());
}
#include "palettize.moc"
-KisFilterPalettize::KisFilterPalettize() : KisFilter(id(), FiltersCategoryMapId, i18n("&Palettize..."))
+
+/*******************************************************************************/
+/* KisFilterPalettizeConfiguration */
+/*******************************************************************************/
+
+class KisFilterPalettizeConfiguration : public KisFilterConfiguration
{
- setColorSpaceIndependence(FULLY_INDEPENDENT);
- setSupportsPainting(true);
- setShowConfigurationWidget(true);
-}
+public:
+ KisFilterPalettizeConfiguration(const QString & name, qint32 version, KisResourcesInterfaceSP resourcesInterface)
+ : KisFilterConfiguration(name, version, resourcesInterface)
+ {
+ }
+
+ KisFilterPalettizeConfiguration(const KisFilterPalettizeConfiguration &rhs)
+ : KisFilterConfiguration(rhs)
+ {
+ }
+
+ virtual KisFilterConfigurationSP clone() const override {
+ return new KisFilterPalettizeConfiguration(*this);
+ }
+
+ KoColorSetSP palette(KisResourcesInterfaceSP resourcesInterface) const
+ {
+ auto source = resourcesInterface->source<KoColorSet>(ResourceType::Palettes);
+ return source.resourceForName(this->getString("palette"));
+ }
+
+ KoColorSetSP palette() const
+ {
+ return palette(resourcesInterface());
+ }
+
+ QList<KoResourceSP> linkedResources(KisResourcesInterfaceSP globalResourcesInterface) const override
+ {
+ KoColorSetSP palette = this->palette(globalResourcesInterface);
+
+ QList<KoResourceSP> resources;
+ if (palette) {
+ resources << palette;
+ }
+
+ resources << KisDitherWidget::prepareLinkedResources(*this, "dither/", globalResourcesInterface);
+ resources << KisDitherWidget::prepareLinkedResources(*this, "alphaDither/", globalResourcesInterface);
+
+ return resources;
+ }
+};
+
+/*******************************************************************************/
+/* KisPalettizeWidget */
+/*******************************************************************************/
KisPalettizeWidget::KisPalettizeWidget(QWidget* parent)
: KisConfigWidget(parent)
{
Q_UNUSED(m_ditherPatternWidget);
setupUi(this);
paletteIconWidget->setFixedSize(32, 32);
- KoResourceServer<KoColorSet>* paletteServer = KoResourceServerProvider::instance()->paletteServer();
- QSharedPointer<KoAbstractResourceServerAdapter> paletteAdapter(new KoResourceServerAdapter<KoColorSet>(paletteServer));
- m_paletteWidget = new KoResourceItemChooser(paletteAdapter, this, false);
+ m_paletteWidget = new KisResourceItemChooser(ResourceType::Palettes, false, this);
paletteIconWidget->setPopupWidget(m_paletteWidget);
- QObject::connect(m_paletteWidget, &KoResourceItemChooser::resourceSelected, paletteIconWidget, &KisIconWidget::setResource);
- QObject::connect(m_paletteWidget, &KoResourceItemChooser::resourceSelected, this, &KisConfigWidget::sigConfigurationItemChanged);
+ QObject::connect(m_paletteWidget, &KisResourceItemChooser::resourceSelected, paletteIconWidget, &KisIconWidget::setResource);
+ QObject::connect(m_paletteWidget, &KisResourceItemChooser::resourceSelected, this, &KisConfigWidget::sigConfigurationItemChanged);
QObject::connect(colorspaceComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &KisConfigWidget::sigConfigurationItemChanged);
QObject::connect(ditherGroupBox, &QGroupBox::toggled, this, &KisConfigWidget::sigConfigurationItemChanged);
QObject::connect(ditherWidget, &KisDitherWidget::sigConfigurationItemChanged, this, &KisConfigWidget::sigConfigurationItemChanged);
QObject::connect(colorModeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &KisConfigWidget::sigConfigurationItemChanged);
offsetScaleSpinBox->setPrefix(QString("%1 ").arg(i18n("Offset Scale:")));
offsetScaleSpinBox->setRange(0.0, 1.0, 3);
offsetScaleSpinBox->setSingleStep(0.125);
QObject::connect(offsetScaleSpinBox, &KisDoubleSliderSpinBox::valueChanged, this, &KisConfigWidget::sigConfigurationItemChanged);
QObject::connect(alphaGroupBox, &QGroupBox::toggled, this, &KisConfigWidget::sigConfigurationItemChanged);
QObject::connect(alphaModeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &KisConfigWidget::sigConfigurationItemChanged);
alphaClipSpinBox->setPrefix(QString("%1 ").arg(i18n("Clip:")));
alphaClipSpinBox->setRange(0.0, 1.0, 3);
alphaClipSpinBox->setSingleStep(0.125);
QObject::connect(alphaClipSpinBox, &KisDoubleSliderSpinBox::valueChanged, this, &KisConfigWidget::sigConfigurationItemChanged);
alphaIndexSpinBox->setPrefix(QString("%1 ").arg(i18n("Index:")));
alphaIndexSpinBox->setRange(0, 255);
QObject::connect(alphaIndexSpinBox, &KisSliderSpinBox::valueChanged, this, &KisConfigWidget::sigConfigurationItemChanged);
- QObject::connect(m_paletteWidget, &KoResourceItemChooser::resourceSelected, [this](){
- const KoColorSet* const palette = static_cast<const KoColorSet*>(m_paletteWidget->currentResource());
+ QObject::connect(m_paletteWidget, &KisResourceItemChooser::resourceSelected, [this](){
+ const KoColorSetSP palette = m_paletteWidget->currentResource().staticCast<KoColorSet>();
alphaIndexSpinBox->setMaximum(palette ? int(palette->colorCount() - 1) : 0);
alphaIndexSpinBox->setValue(std::min(alphaIndexSpinBox->value(), alphaIndexSpinBox->maximum()));
});
QObject::connect(alphaDitherWidget, &KisDitherWidget::sigConfigurationItemChanged, this, &KisConfigWidget::sigConfigurationItemChanged);
}
-void KisPalettizeWidget::setConfiguration(const KisPropertiesConfigurationSP config)
+void KisPalettizeWidget::setConfiguration(const KisPropertiesConfigurationSP _config)
{
- KoColorSet* palette = KoResourceServerProvider::instance()->paletteServer()->resourceByName(config->getString("palette"));
+ const KisFilterPalettizeConfiguration *config = dynamic_cast<const KisFilterPalettizeConfiguration*>(_config.data());
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
+
+ KoColorSetSP palette = config->palette();
if (palette) m_paletteWidget->setCurrentResource(palette);
colorspaceComboBox->setCurrentIndex(config->getInt("colorspace"));
ditherGroupBox->setChecked(config->getBool("ditherEnabled"));
ditherWidget->setConfiguration(*config, "dither/");
colorModeComboBox->setCurrentIndex(config->getInt("dither/colorMode"));
offsetScaleSpinBox->setValue(config->getDouble("dither/offsetScale"));
alphaGroupBox->setChecked(config->getBool("alphaEnabled"));
alphaModeComboBox->setCurrentIndex(config->getInt("alphaMode"));
alphaClipSpinBox->setValue(config->getDouble("alphaClip"));
alphaIndexSpinBox->setValue(config->getInt("alphaIndex"));
alphaDitherWidget->setConfiguration(*config, "alphaDither/");
}
KisPropertiesConfigurationSP KisPalettizeWidget::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("palettize", 1);
+ KisFilterSP filter = KisFilterRegistry::instance()->get("palettize");
+ KisFilterConfigurationSP config = filter->factoryConfiguration(KisGlobalResourcesInterface::instance());
if (m_paletteWidget->currentResource()) config->setProperty("palette", QVariant(m_paletteWidget->currentResource()->name()));
config->setProperty("colorspace", colorspaceComboBox->currentIndex());
config->setProperty("ditherEnabled", ditherGroupBox->isChecked());
ditherWidget->configuration(*config, "dither/");
config->setProperty("dither/colorMode", colorModeComboBox->currentIndex());
config->setProperty("dither/offsetScale", offsetScaleSpinBox->value());
config->setProperty("alphaEnabled", alphaGroupBox->isChecked());
config->setProperty("alphaMode", alphaModeComboBox->currentIndex());
config->setProperty("alphaClip", alphaClipSpinBox->value());
config->setProperty("alphaIndex", alphaIndexSpinBox->value());
alphaDitherWidget->configuration(*config, "alphaDither/");
return config;
}
KisConfigWidget* KisFilterPalettize::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool useForMasks) const
{
Q_UNUSED(dev)
Q_UNUSED(useForMasks)
return new KisPalettizeWidget(parent);
}
-KisFilterConfigurationSP KisFilterPalettize::defaultConfiguration() const
+/*******************************************************************************/
+/* KisFilterPalettize */
+/*******************************************************************************/
+
+KisFilterPalettize::KisFilterPalettize() : KisFilter(id(), FiltersCategoryMapId, i18n("&Palettize..."))
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ setColorSpaceIndependence(FULLY_INDEPENDENT);
+ setSupportsPainting(true);
+ setShowConfigurationWidget(true);
+}
+
+KisFilterConfigurationSP KisFilterPalettize::factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const
+{
+ return new KisFilterPalettizeConfiguration("palettize", 1, resourcesInterface);
+}
+
+
+KisFilterConfigurationSP KisFilterPalettize::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
+{
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("palette", "Default");
config->setProperty("colorspace", Colorspace::Lab);
config->setProperty("ditherEnabled", false);
KisDitherWidget::factoryConfiguration(*config, "dither/");
config->setProperty("dither/colorMode", ColorMode::PerChannelOffset);
config->setProperty("dither/offsetScale", 0.125);
config->setProperty("alphaEnabled", true);
config->setProperty("alphaMode", AlphaMode::Clip);
config->setProperty("alphaClip", 0.5);
config->setProperty("alphaIndex", 0);
KisDitherWidget::factoryConfiguration(*config, "alphaDither/");
return config;
}
-void KisFilterPalettize::processImpl(KisPaintDeviceSP device, const QRect& applyRect, const KisFilterConfigurationSP config, KoUpdater* progressUpdater) const
+void KisFilterPalettize::processImpl(KisPaintDeviceSP device, const QRect& applyRect, const KisFilterConfigurationSP _config, KoUpdater* progressUpdater) const
{
- const KoColorSet* palette = KoResourceServerProvider::instance()->paletteServer()->resourceByName(config->getString("palette"));
+ const KisFilterPalettizeConfiguration *config = dynamic_cast<const KisFilterPalettizeConfiguration*>(_config.data());
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(config->hasLocalResourcesSnapshot());
+
+ const KoColorSetSP palette = config->palette();
+
const int searchColorspace = config->getInt("colorspace");
const bool ditherEnabled = config->getBool("ditherEnabled");
const int colorMode = config->getInt("dither/colorMode");
const double offsetScale = config->getDouble("dither/offsetScale");
const bool alphaEnabled = config->getBool("alphaEnabled");
const int alphaMode = config->getInt("alphaMode");
const double alphaClip = config->getDouble("alphaClip");
const int alphaIndex = config->getInt("alphaIndex");
const KoColorSpace* colorspace = device->colorSpace();
const KoColorSpace* workColorspace = (searchColorspace == Colorspace::Lab
? KoColorSpaceRegistry::instance()->lab16()
: KoColorSpaceRegistry::instance()->rgb16("sRGB-elle-V2-srgbtrc.icc"));
const quint8 colorCount = ditherEnabled && colorMode == ColorMode::NearestColors ? 2 : 1;
using SearchColor = boost::geometry::model::point<quint16, 3, boost::geometry::cs::cartesian>;
struct ColorCandidate {
KoColor color;
quint16 index;
double distance;
};
using SearchEntry = std::pair<SearchColor, ColorCandidate>;
boost::geometry::index::rtree<SearchEntry, boost::geometry::index::quadratic<16>> rtree;
if (palette) {
// Add palette colors to search tree
quint16 index = 0;
for (int row = 0; row < palette->rowCount(); ++row) {
for (int column = 0; column < palette->columnCount(); ++column) {
KisSwatch swatch = palette->getColorGlobal(column, row);
if (swatch.isValid()) {
KoColor color = swatch.color().convertedTo(colorspace);
KoColor workColor = swatch.color().convertedTo(workColorspace);
SearchColor searchColor;
memcpy(&searchColor, workColor.data(), sizeof(SearchColor));
// Don't add duplicates so won't dither between identical colors
std::vector<SearchEntry> result;
rtree.query(boost::geometry::index::contains(searchColor), std::back_inserter(result));
if (result.empty()) rtree.insert(SearchEntry(searchColor, {color, index, 0.0}));
}
++index;
}
}
KisDitherUtil ditherUtil;
if (ditherEnabled) ditherUtil.setConfiguration(*config, "dither/");
KisDitherUtil alphaDitherUtil;
if (alphaMode == AlphaMode::Dither) alphaDitherUtil.setConfiguration(*config, "alphaDither/");
KisSequentialIteratorProgress pixel(device, applyRect, progressUpdater);
while (pixel.nextPixel()) {
KoColor workColor(pixel.oldRawData(), colorspace);
workColor.convertTo(workColorspace);
// Find dither threshold
double threshold = 0.5;
if (ditherEnabled) {
threshold = ditherUtil.threshold(QPoint(pixel.x(), pixel.y()));
// Traditional per-channel ordered dithering
if (colorMode == ColorMode::PerChannelOffset) {
QVector<float> normalized(int(workColorspace->channelCount()));
workColorspace->normalisedChannelsValue(workColor.data(), normalized);
for (int channel = 0; channel < int(workColorspace->channelCount()); ++channel) {
normalized[channel] += (threshold - 0.5) * offsetScale;
}
workColorspace->fromNormalisedChannelsValue(workColor.data(), normalized);
}
}
// Get candidate colors and their distances
SearchColor searchColor;
memcpy(reinterpret_cast<quint8 *>(&searchColor), workColor.data(), sizeof(SearchColor));
std::vector<ColorCandidate> candidateColors;
candidateColors.reserve(size_t(colorCount));
double distanceSum = 0.0;
for (auto it = rtree.qbegin(boost::geometry::index::nearest(searchColor, colorCount)); it != rtree.qend() && candidateColors.size() < colorCount; ++it) {
ColorCandidate candidate = it->second;
candidate.distance = boost::geometry::distance(searchColor, it->first);
candidateColors.push_back(candidate);
distanceSum += candidate.distance;
}
// Select color candidate
quint16 selected;
if (ditherEnabled && colorMode == ColorMode::NearestColors) {
// Sort candidates by palette order for stable dither color ordering
const bool swap = candidateColors[0].index > candidateColors[1].index;
selected = swap ^ (candidateColors[swap].distance / distanceSum > threshold);
}
else {
selected = 0;
}
ColorCandidate &candidate = candidateColors[selected];
// Set alpha
const double oldAlpha = colorspace->opacityF(pixel.oldRawData());
double newAlpha = oldAlpha;
if (alphaEnabled && !(!ditherEnabled && alphaMode == AlphaMode::Dither)) {
if (alphaMode == AlphaMode::Clip) {
newAlpha = oldAlpha < alphaClip? 0.0 : 1.0;
}
else if (alphaMode == AlphaMode::Index) {
newAlpha = (candidate.index == alphaIndex ? 0.0 : 1.0);
}
else if (alphaMode == AlphaMode::Dither) {
newAlpha = oldAlpha < alphaDitherUtil.threshold(QPoint(pixel.x(), pixel.y())) ? 0.0 : 1.0;
}
}
colorspace->setOpacity(candidate.color.data(), newAlpha, 1);
// Copy color to pixel
memcpy(pixel.rawData(), candidate.color.data(), colorspace->pixelSize());
}
}
}
diff --git a/plugins/filters/palettize/palettize.h b/plugins/filters/palettize/palettize.h
index 9f37a4166e..3a43b3c7cb 100644
--- a/plugins/filters/palettize/palettize.h
+++ b/plugins/filters/palettize/palettize.h
@@ -1,85 +1,86 @@
/*
* This file is part of the KDE project
*
* Copyright (c) 2019 Carl Olsson <carl.olsson@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 PALETTIZE_H
#define PALETTIZE_H
#include "ui_palettize.h"
#include <kis_filter.h>
#include <kis_config_widget.h>
#include <kis_filter_configuration.h>
#include <boost/geometry.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/register/point.hpp>
-class KoResourceItemChooser;
+class KisResourceItemChooser;
class Palettize : public QObject
{
public:
Palettize(QObject *parent, const QVariantList &);
};
class KisPalettizeWidget : public KisConfigWidget, public Ui::Palettize
{
public:
KisPalettizeWidget(QWidget* parent = 0);
void setConfiguration(const KisPropertiesConfigurationSP) override;
KisPropertiesConfigurationSP configuration() const override;
private:
- KoResourceItemChooser* m_paletteWidget;
- KoResourceItemChooser* m_ditherPatternWidget;
+ KisResourceItemChooser* m_paletteWidget;
+ KisResourceItemChooser* m_ditherPatternWidget;
};
class KisFilterPalettize : public KisFilter
{
public:
enum Colorspace {
Lab,
RGB
};
enum AlphaMode {
Clip,
Index,
Dither
};
enum ThresholdMode {
Pattern,
Noise
};
enum PatternValueMode {
Auto,
Lightness,
Alpha
};
enum ColorMode {
PerChannelOffset,
NearestColors
};
KisFilterPalettize();
static inline KoID id() { return KoID("palettize", i18n("Palettize")); }
KisConfigWidget* createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
void processImpl(KisPaintDeviceSP device, const QRect &applyRect, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const override;
};
#endif
diff --git a/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.cpp b/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.cpp
index 8fbfe0775d..b859430c64 100644
--- a/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.cpp
+++ b/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.cpp
@@ -1,186 +1,187 @@
/*
* Copyright (c) 2010-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_phong_bumpmap_config_widget.h"
#include <filter/kis_filter_configuration.h>
#include <kis_size_group.h>
#include "phong_bumpmap_constants.h"
#include "KoChannelInfo.h"
#include "KoColorSpace.h"
+#include <KisGlobalResourcesInterface.h>
KisPhongBumpmapConfigWidget::KisPhongBumpmapConfigWidget(const KisPaintDeviceSP dev, QWidget *parent, Qt::WindowFlags f)
: KisConfigWidget(parent, f)
, m_device(dev)
{
Q_ASSERT(m_device);
m_page = new KisPhongBumpmapWidget(this);
KisSizeGroup *matPropLabelsGroup = new KisSizeGroup(this);
matPropLabelsGroup->addWidget(m_page->lblAmbientReflectivity);
matPropLabelsGroup->addWidget(m_page->lblDiffuseReflectivity);
matPropLabelsGroup->addWidget(m_page->lblSpecularReflectivity);
matPropLabelsGroup->addWidget(m_page->lblSpecularShinyExp);
// Connect widgets to each other
connect(m_page->azimuthDial1, SIGNAL(valueChanged(int)), m_page->azimuthSpinBox1, SLOT(setValue(int)));
connect(m_page->azimuthDial2, SIGNAL(valueChanged(int)), m_page->azimuthSpinBox2, SLOT(setValue(int)));
connect(m_page->azimuthDial3, SIGNAL(valueChanged(int)), m_page->azimuthSpinBox3, SLOT(setValue(int)));
connect(m_page->azimuthDial4, SIGNAL(valueChanged(int)), m_page->azimuthSpinBox4, SLOT(setValue(int)));
connect(m_page->azimuthSpinBox1, SIGNAL(valueChanged(int)), m_page->azimuthDial1, SLOT(setValue(int)));
connect(m_page->azimuthSpinBox2, SIGNAL(valueChanged(int)), m_page->azimuthDial2, SLOT(setValue(int)));
connect(m_page->azimuthSpinBox3, SIGNAL(valueChanged(int)), m_page->azimuthDial3, SLOT(setValue(int)));
connect(m_page->azimuthSpinBox4, SIGNAL(valueChanged(int)), m_page->azimuthDial4, SLOT(setValue(int)));
//Let widgets warn the preview of when they are updated
connect(m_page->azimuthDial1, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->azimuthDial2, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->azimuthDial3, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->azimuthDial4, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightKColorCombo1, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightKColorCombo2, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightKColorCombo3, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightKColorCombo4, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->inclinationSpinBox1, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->inclinationSpinBox2, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->inclinationSpinBox3, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->inclinationSpinBox4, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->useNormalMap, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->diffuseReflectivityGroup, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->specularReflectivityGroup, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->ambientReflectivityKisDoubleSliderSpinBox, SIGNAL(valueChanged(qreal)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->diffuseReflectivityKisDoubleSliderSpinBox, SIGNAL(valueChanged(qreal)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->specularReflectivityKisDoubleSliderSpinBox, SIGNAL(valueChanged(qreal)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->shinynessExponentKisSliderSpinBox, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->heightChannelComboBox, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightSourceGroupBox1, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightSourceGroupBox2, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightSourceGroupBox3, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightSourceGroupBox4, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
QVBoxLayout *l = new QVBoxLayout(this);
Q_CHECK_PTR(l);
l->addWidget(m_page);
/* fill in the channel chooser */
QList<KoChannelInfo *> channels = m_device->colorSpace()->channels();
for (quint8 ch = 0; ch < m_device->colorSpace()->colorChannelCount(); ch++)
m_page->heightChannelComboBox->addItem(channels.at(ch)->name());
connect(m_page->useNormalMap, SIGNAL(toggled(bool)), this, SLOT(slotDisableHeightChannelCombobox(bool)) );
}
void KisPhongBumpmapConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
if (!config) return;
QVariant tempcolor;
if (config->getBool(USE_NORMALMAP_IS_ENABLED)) {
m_page->heightChannelComboBox->setEnabled(false);
} else {
m_page->heightChannelComboBox->setEnabled(true);
}
m_page->ambientReflectivityKisDoubleSliderSpinBox->setValue( config->getDouble(PHONG_AMBIENT_REFLECTIVITY) );
m_page->diffuseReflectivityKisDoubleSliderSpinBox->setValue( config->getDouble(PHONG_DIFFUSE_REFLECTIVITY) );
m_page->specularReflectivityKisDoubleSliderSpinBox->setValue( config->getDouble(PHONG_SPECULAR_REFLECTIVITY) );
m_page->shinynessExponentKisSliderSpinBox->setValue( config->getInt(PHONG_SHINYNESS_EXPONENT) );
m_page->useNormalMap->setChecked( config->getBool(USE_NORMALMAP_IS_ENABLED) );
m_page->diffuseReflectivityGroup->setChecked( config->getBool(PHONG_DIFFUSE_REFLECTIVITY_IS_ENABLED) );
m_page->specularReflectivityGroup->setChecked( config->getBool(PHONG_SPECULAR_REFLECTIVITY_IS_ENABLED) );
// NOTE: Indexes are off by 1 simply because arrays start at 0 and the GUI naming scheme started at 1
m_page->lightSourceGroupBox1->setChecked( config->getBool(PHONG_ILLUMINANT_IS_ENABLED[0]) );
m_page->lightSourceGroupBox2->setChecked( config->getBool(PHONG_ILLUMINANT_IS_ENABLED[1]) );
m_page->lightSourceGroupBox3->setChecked( config->getBool(PHONG_ILLUMINANT_IS_ENABLED[2]) );
m_page->lightSourceGroupBox4->setChecked( config->getBool(PHONG_ILLUMINANT_IS_ENABLED[3]) );
config->getProperty(PHONG_ILLUMINANT_COLOR[0], tempcolor);
m_page->lightKColorCombo1->setColor(tempcolor.value<QColor>());
config->getProperty(PHONG_ILLUMINANT_COLOR[1], tempcolor);
m_page->lightKColorCombo2->setColor(tempcolor.value<QColor>());
config->getProperty(PHONG_ILLUMINANT_COLOR[2], tempcolor);
m_page->lightKColorCombo3->setColor(tempcolor.value<QColor>());
config->getProperty(PHONG_ILLUMINANT_COLOR[3], tempcolor);
m_page->lightKColorCombo4->setColor(tempcolor.value<QColor>());
m_page->azimuthSpinBox1->setValue( config->getDouble(PHONG_ILLUMINANT_AZIMUTH[0]) );
m_page->azimuthSpinBox2->setValue( config->getDouble(PHONG_ILLUMINANT_AZIMUTH[1]) );
m_page->azimuthSpinBox3->setValue( config->getDouble(PHONG_ILLUMINANT_AZIMUTH[2]) );
m_page->azimuthSpinBox4->setValue( config->getDouble(PHONG_ILLUMINANT_AZIMUTH[3]) );
m_page->inclinationSpinBox1->setValue( config->getDouble(PHONG_ILLUMINANT_INCLINATION[0]) );
m_page->inclinationSpinBox2->setValue( config->getDouble(PHONG_ILLUMINANT_INCLINATION[1]) );
m_page->inclinationSpinBox3->setValue( config->getDouble(PHONG_ILLUMINANT_INCLINATION[2]) );
m_page->inclinationSpinBox4->setValue( config->getDouble(PHONG_ILLUMINANT_INCLINATION[3]) );
}
KisPropertiesConfigurationSP KisPhongBumpmapConfigWidget::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("phongbumpmap", 2);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("phongbumpmap", 2, KisGlobalResourcesInterface::instance());
config->setProperty(PHONG_HEIGHT_CHANNEL, m_page->heightChannelComboBox->currentText());
config->setProperty(USE_NORMALMAP_IS_ENABLED, m_page->useNormalMap->isChecked());
config->setProperty(PHONG_AMBIENT_REFLECTIVITY, m_page->ambientReflectivityKisDoubleSliderSpinBox->value());
config->setProperty(PHONG_DIFFUSE_REFLECTIVITY, m_page->diffuseReflectivityKisDoubleSliderSpinBox->value());
config->setProperty(PHONG_SPECULAR_REFLECTIVITY, m_page->specularReflectivityKisDoubleSliderSpinBox->value());
config->setProperty(PHONG_SHINYNESS_EXPONENT, m_page->shinynessExponentKisSliderSpinBox->value());
config->setProperty(PHONG_DIFFUSE_REFLECTIVITY_IS_ENABLED, m_page->diffuseReflectivityGroup->isChecked());
config->setProperty(PHONG_SPECULAR_REFLECTIVITY_IS_ENABLED, m_page->specularReflectivityGroup->isChecked());
//config->setProperty(PHONG_SHINYNESS_EXPONENT_IS_ENABLED, m_page->specularReflectivityCheckBox->isChecked());
// Indexes are off by 1 simply because arrays start at 0 and the GUI naming scheme started at 1
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[0], m_page->lightSourceGroupBox1->isChecked());
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[1], m_page->lightSourceGroupBox2->isChecked());
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[2], m_page->lightSourceGroupBox3->isChecked());
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[3], m_page->lightSourceGroupBox4->isChecked());
config->setProperty(PHONG_ILLUMINANT_COLOR[0], m_page->lightKColorCombo1->color());
config->setProperty(PHONG_ILLUMINANT_COLOR[1], m_page->lightKColorCombo2->color());
config->setProperty(PHONG_ILLUMINANT_COLOR[2], m_page->lightKColorCombo3->color());
config->setProperty(PHONG_ILLUMINANT_COLOR[3], m_page->lightKColorCombo4->color());
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[0], m_page->azimuthSpinBox1->value());
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[1], m_page->azimuthSpinBox2->value());
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[2], m_page->azimuthSpinBox3->value());
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[3], m_page->azimuthSpinBox4->value());
config->setProperty(PHONG_ILLUMINANT_INCLINATION[0], m_page->inclinationSpinBox1->value());
config->setProperty(PHONG_ILLUMINANT_INCLINATION[1], m_page->inclinationSpinBox2->value());
config->setProperty(PHONG_ILLUMINANT_INCLINATION[2], m_page->inclinationSpinBox3->value());
config->setProperty(PHONG_ILLUMINANT_INCLINATION[3], m_page->inclinationSpinBox4->value());
// Read configuration
/*
QMap<QString, QVariant> rofl = QMap<QString, QVariant>(config->getProperties());
QMap<QString, QVariant>::const_iterator i;
for (i = rofl.constBegin(); i != rofl.constEnd(); ++i)
dbgKrita << i.key() << ":" << i.value();
*/
return config;
}
void KisPhongBumpmapConfigWidget::slotDisableHeightChannelCombobox(bool normalmapchecked) {
if (normalmapchecked) {
m_page->heightChannelComboBox->setEnabled(false);
} else {
m_page->heightChannelComboBox->setEnabled(true);
}
}
diff --git a/plugins/filters/phongbumpmap/kis_phong_bumpmap_filter.cpp b/plugins/filters/phongbumpmap/kis_phong_bumpmap_filter.cpp
index 8d7a1a2dfa..4ff8e1a8aa 100644
--- a/plugins/filters/phongbumpmap/kis_phong_bumpmap_filter.cpp
+++ b/plugins/filters/phongbumpmap/kis_phong_bumpmap_filter.cpp
@@ -1,238 +1,238 @@
/*
* Copyright (c) 2010-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_phong_bumpmap_filter.h"
#include "kis_phong_bumpmap_config_widget.h"
#include "phong_pixel_processor.h"
#include "kis_debug.h"
#include "kis_paint_device.h"
#include "kis_config_widget.h"
#include "KoUpdater.h"
#include "kis_math_toolbox.h"
#include "KoColorSpaceRegistry.h"
#include <KoChannelInfo.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include "kis_iterator_ng.h"
#include "kundo2command.h"
#include "kis_painter.h"
KisFilterPhongBumpmap::KisFilterPhongBumpmap()
: KisFilter(KoID("phongbumpmap", i18n("Phong Bumpmap")),
FiltersCategoryMapId, i18n("&Phong Bumpmap..."))
{
setColorSpaceIndependence(TO_LAB16);
setSupportsPainting(true);
setSupportsLevelOfDetail(true);
}
void KisFilterPhongBumpmap::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater *progressUpdater
) const
{
if (!config) return;
if (progressUpdater) progressUpdater->setProgress(0);
QString userChosenHeightChannel = config->getString(PHONG_HEIGHT_CHANNEL, "FAIL");
bool m_usenormalmap = config->getBool(USE_NORMALMAP_IS_ENABLED);
if (userChosenHeightChannel == "FAIL") {
qDebug("FIX YOUR FILTER");
return;
}
KoChannelInfo *m_heightChannel = 0;
Q_FOREACH (KoChannelInfo* channel, device->colorSpace()->channels()) {
if (userChosenHeightChannel == channel->name()) {
m_heightChannel = channel;
}
}
if (!m_heightChannel) {
m_heightChannel = device->colorSpace()->channels().first();
}
KIS_ASSERT_RECOVER_RETURN(m_heightChannel);
QRect inputArea = applyRect;
QRect outputArea = applyRect;
if (m_usenormalmap==false) {
inputArea.adjust(-1, -1, 1, 1);
}
quint32 posup;
quint32 posdown;
quint32 posleft;
quint32 posright;
QColor I; //Reflected light
if (progressUpdater) progressUpdater->setProgress(1);
//======Preparation paraphlenalia=======
//Hardcoded facts about Phong Bumpmap: it _will_ generate an RGBA16 bumpmap
const quint8 BYTE_DEPTH_OF_BUMPMAP = 2; // 16 bits per channel
const quint8 CHANNEL_COUNT_OF_BUMPMAP = 4; // RGBA
const quint32 pixelsOfInputArea = abs(inputArea.width() * inputArea.height());
const quint32 pixelsOfOutputArea = abs(outputArea.width() * outputArea.height());
const quint8 pixelSize = BYTE_DEPTH_OF_BUMPMAP * CHANNEL_COUNT_OF_BUMPMAP;
const quint32 bytesToFillBumpmapArea = pixelsOfOutputArea * pixelSize;
QVector<quint8> bumpmap(bytesToFillBumpmapArea);
quint8 *bumpmapDataPointer = bumpmap.data();
quint32 ki = KoChannelInfo::displayPositionToChannelIndex(m_heightChannel->displayPosition(), device->colorSpace()->channels());
PhongPixelProcessor tileRenderer(pixelsOfInputArea, config);
if (progressUpdater) progressUpdater->setProgress(2);
//===============RENDER=================
QVector<PtrToDouble> toDoubleFuncPtr(device->colorSpace()->channels().count());
KisMathToolbox mathToolbox;
if (!mathToolbox.getToDoubleChannelPtr(device->colorSpace()->channels(), toDoubleFuncPtr)) {
return;
}
KisHLineConstIteratorSP iterator;
quint32 curPixel = 0;
iterator = device->createHLineConstIteratorNG(inputArea.x(),
inputArea.y(),
inputArea.width()
);
if (m_usenormalmap==false) {
for (qint32 srcRow = 0; srcRow < inputArea.height(); ++srcRow) {
do {
const quint8 *data = iterator->oldRawData();
tileRenderer.realheightmap[curPixel] = toDoubleFuncPtr[ki](data, device->colorSpace()->channels()[ki]->pos());
curPixel++;
}
while (iterator->nextPixel());
iterator->nextRow();
}
if (progressUpdater) progressUpdater->setProgress(50);
const int tileHeightMinus1 = inputArea.height() - 1;
const int tileWidthMinus1 = inputArea.width() - 1;
// Foreach INNER pixel in tile
for (int y = 1; y < tileHeightMinus1; ++y) {
for (int x = 1; x < tileWidthMinus1; ++x) {
posup = (y + 1) * inputArea.width() + x;
posdown = (y - 1) * inputArea.width() + x;
posleft = y * inputArea.width() + x - 1;
posright = y * inputArea.width() + x + 1;
memcpy(bumpmapDataPointer,
tileRenderer.IlluminatePixelFromHeightmap(posup, posdown, posleft, posright).data(),
pixelSize);
bumpmapDataPointer += pixelSize;
}
}
} else {
for (qint32 srcRow = 0; srcRow < inputArea.height(); ++srcRow) {
do {
const quint8 *data = iterator->oldRawData();
tileRenderer.realheightmap[curPixel] = toDoubleFuncPtr[ki](data, device->colorSpace()->channels()[ki]->pos());
QVector <float> current_pixel_values(4);
device->colorSpace()->normalisedChannelsValue(data, current_pixel_values );
//dbgKrita<< "Vector:" << current_pixel_values[2] << "," << current_pixel_values[1] << "," << current_pixel_values[0];
memcpy(bumpmapDataPointer,
tileRenderer.IlluminatePixelFromNormalmap(current_pixel_values[2], current_pixel_values[1], current_pixel_values[0]).data(),
pixelSize);
curPixel++;
//pointer that crashes here, but not in the other if statement.
bumpmapDataPointer += pixelSize;
}
while (iterator->nextPixel());
iterator->nextRow();
}
}
if (progressUpdater) progressUpdater->setProgress(90);
KisPaintDeviceSP bumpmapPaintDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb16());
bumpmapPaintDevice->writeBytes(bumpmap.data(), outputArea.x(), outputArea.y(), outputArea.width(), outputArea.height());
bumpmapPaintDevice->convertTo(device->colorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
KisPainter copier(device);
copier.bitBlt(outputArea.x(), outputArea.y(), bumpmapPaintDevice,
outputArea.x(), outputArea.y(), outputArea.width(), outputArea.height());
//device->prepareClone(bumpmapPaintDevice);
//device->makeCloneFrom(bumpmapPaintDevice, bumpmapPaintDevice->extent()); // THIS COULD BE BUG GY
if (progressUpdater) progressUpdater->setProgress(100);
}
-KisFilterConfigurationSP KisFilterPhongBumpmap::defaultConfiguration() const
+KisFilterConfigurationSP KisFilterPhongBumpmap::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty(PHONG_AMBIENT_REFLECTIVITY, 0.2);
config->setProperty(PHONG_DIFFUSE_REFLECTIVITY, 0.5);
config->setProperty(PHONG_SPECULAR_REFLECTIVITY, 0.3);
config->setProperty(PHONG_SHINYNESS_EXPONENT, 2);
config->setProperty(USE_NORMALMAP_IS_ENABLED, false);
config->setProperty(PHONG_DIFFUSE_REFLECTIVITY_IS_ENABLED, true);
config->setProperty(PHONG_SPECULAR_REFLECTIVITY_IS_ENABLED, true);
// Indexes are off by 1 simply because arrays start at 0 and the GUI naming scheme started at 1
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[0], true);
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[1], true);
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[2], false);
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[3], false);
config->setProperty(PHONG_ILLUMINANT_COLOR[0], QColor(255, 255, 0));
config->setProperty(PHONG_ILLUMINANT_COLOR[1], QColor(255, 0, 0));
config->setProperty(PHONG_ILLUMINANT_COLOR[2], QColor(0, 0, 255));
config->setProperty(PHONG_ILLUMINANT_COLOR[3], QColor(0, 255, 0));
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[0], 50);
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[1], 100);
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[2], 150);
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[3], 200);
config->setProperty(PHONG_ILLUMINANT_INCLINATION[0], 25);
config->setProperty(PHONG_ILLUMINANT_INCLINATION[1], 20);
config->setProperty(PHONG_ILLUMINANT_INCLINATION[2], 30);
config->setProperty(PHONG_ILLUMINANT_INCLINATION[3], 40);
return config;
}
QRect KisFilterPhongBumpmap::neededRect(const QRect &rect, const KisFilterConfigurationSP /*config*/, int /*lod*/) const
{
return rect.adjusted(-1, -1, 1, 1);
}
QRect KisFilterPhongBumpmap::changedRect(const QRect &rect, const KisFilterConfigurationSP /*config*/, int /*lod*/) const
{
return rect;
}
KisConfigWidget *KisFilterPhongBumpmap::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool) const
{
KisPhongBumpmapConfigWidget *w = new KisPhongBumpmapConfigWidget(dev, parent);
return w;
}
diff --git a/plugins/filters/phongbumpmap/kis_phong_bumpmap_filter.h b/plugins/filters/phongbumpmap/kis_phong_bumpmap_filter.h
index e6d6dd3b4e..7d8e3642f6 100644
--- a/plugins/filters/phongbumpmap/kis_phong_bumpmap_filter.h
+++ b/plugins/filters/phongbumpmap/kis_phong_bumpmap_filter.h
@@ -1,54 +1,54 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
* Copyright (c) 2010-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.
*/
#ifndef KIS_PHONG_BUMPMAP_FILTER_H
#define KIS_PHONG_BUMPMAP_FILTER_H
#include <kis_types.h>
#include <filter/kis_filter.h>
/**
* This class is an implementation of the phong illumination model.
* It uses a heightmap as an input mesh (normally taken from 1
* channel of a colorspace) to achieve a bumpmapping effect with
* multiple illumination sources.
*/
class KisFilterPhongBumpmap : public KisFilter
{
public:
KisFilterPhongBumpmap();
public:
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater *progressUpdater
) const override;
QRect neededRect(const QRect &rect, const KisFilterConfigurationSP config, int lod) const override;
QRect changedRect(const QRect &rect, const KisFilterConfigurationSP config, int lod) const override;
KisConfigWidget *createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
private:
//bool m_usenormalmap;
};
#endif //KIS_PHONG_BUMPMAP_FILTER_H
diff --git a/plugins/filters/pixelizefilter/kis_pixelize_filter.cpp b/plugins/filters/pixelizefilter/kis_pixelize_filter.cpp
index 7885ef47cc..c7ce436699 100644
--- a/plugins/filters/pixelizefilter/kis_pixelize_filter.cpp
+++ b/plugins/filters/pixelizefilter/kis_pixelize_filter.cpp
@@ -1,163 +1,163 @@
/*
* This file is part of Krita
*
* Copyright (c) 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
*
* ported from Gimp, Copyright (C) 1997 Eiichi Takamori <taka@ma1.seikyou.ne.jp>
* original pixelize.c for GIMP 0.54 by Tracy Scott
*
* 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_pixelize_filter.h"
#include <stdlib.h>
#include <vector>
#include <QPoint>
#include <QSpinBox>
#include <QVector>
#include <klocalizedstring.h>
#include <kpluginfactory.h>
#include <KoUpdater.h>
#include <kis_debug.h>
#include <KisDocument.h>
#include <filter/kis_filter_registry.h>
#include <kis_global.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_selection.h>
#include <kis_types.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include "widgets/kis_multi_integer_filter_widget.h"
#include <KoMixColorsOp.h>
#include <KisSequentialIteratorProgress.h>
#include "kis_algebra_2d.h"
#include "kis_lod_transform.h"
KisPixelizeFilter::KisPixelizeFilter() : KisFilter(id(), FiltersCategoryArtisticId, i18n("&Pixelize..."))
{
setSupportsPainting(true);
setSupportsThreading(true);
setSupportsAdjustmentLayers(true);
setSupportsLevelOfDetail(true);
setColorSpaceIndependence(FULLY_INDEPENDENT);
}
void KisPixelizeFilter::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
Q_ASSERT(device);
KisLodTransformScalar t(device);
const int pixelWidth = qCeil(t.scale(config ? qMax(1, config->getInt("pixelWidth", 10)) : 10));
const int pixelHeight = qCeil(t.scale(config ? qMax(1, config->getInt("pixelHeight", 10)) : 10));
const qint32 pixelSize = device->pixelSize();
const QRect deviceBounds = device->defaultBounds()->bounds();
const int bufferSize = pixelSize * pixelWidth * pixelHeight;
QScopedArrayPointer<quint8> buffer(new quint8[bufferSize]);
KoColor pixelColor(Qt::black, device->colorSpace());
KoMixColorsOp *mixOp = device->colorSpace()->mixColorsOp();
using namespace KisAlgebra2D;
const qint32 firstCol = divideFloor(applyRect.x(), pixelWidth);
const qint32 firstRow = divideFloor(applyRect.y(), pixelHeight);
const qint32 lastCol = divideFloor(applyRect.x() + applyRect.width() - 1, pixelWidth);
const qint32 lastRow = divideFloor(applyRect.y() + applyRect.height() - 1, pixelHeight);
progressUpdater->setRange(firstRow, lastRow);
for(qint32 i = firstRow; i <= lastRow; i++) {
for(qint32 j = firstCol; j <= lastCol; j++) {
const QRect maxPatchRect(j * pixelWidth, i * pixelHeight,
pixelWidth, pixelHeight);
const QRect pixelRect = maxPatchRect & deviceBounds;
const int numColors = pixelRect.width() * pixelRect.height();
//read
KisSequentialConstIterator srcIt(device, pixelRect);
memset(buffer.data(), 0, bufferSize);
quint8 *bufferPtr = buffer.data();
while (srcIt.nextPixel()) {
memcpy(bufferPtr, srcIt.oldRawData(), pixelSize);
bufferPtr += pixelSize;
}
// mix all the colors
mixOp->mixColors(buffer.data(), numColors, pixelColor.data());
// write only colors in applyRect
const QRect writeRect = pixelRect & applyRect;
KisSequentialIterator dstIt(device, writeRect);
while (dstIt.nextPixel()) {
memcpy(dstIt.rawData(), pixelColor.data(), pixelSize);
}
}
progressUpdater->setValue(i);
}
}
QRect KisPixelizeFilter::neededRect(const QRect &rect, const KisFilterConfigurationSP config, int lod) const
{
KisLodTransformScalar t(lod);
const int pixelWidth = qCeil(t.scale(config ? qMax(1, config->getInt("pixelWidth", 10)) : 10));
const int pixelHeight = qCeil(t.scale(config ? qMax(1, config->getInt("pixelHeight", 10)) : 10));
// TODO: make more precise calculation of the rect, including the alignment
return rect.adjusted(-2*pixelWidth, -2*pixelHeight, 2*pixelWidth, 2*pixelHeight);
}
QRect KisPixelizeFilter::changedRect(const QRect &rect, const KisFilterConfigurationSP config, int lod) const
{
return neededRect(rect, config, lod);
}
KisConfigWidget * KisPixelizeFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool) const
{
vKisIntegerWidgetParam param;
param.push_back(KisIntegerWidgetParam(2, 512, 10, i18n("Pixel width"), "pixelWidth"));
param.push_back(KisIntegerWidgetParam(2, 512, 10, i18n("Pixel height"), "pixelHeight"));
return new KisMultiIntegerFilterWidget(id().id(), parent, id().id(), param);
}
-KisFilterConfigurationSP KisPixelizeFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisPixelizeFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("pixelWidth", 10);
config->setProperty("pixelHeight", 10);
return config;
}
diff --git a/plugins/filters/pixelizefilter/kis_pixelize_filter.h b/plugins/filters/pixelizefilter/kis_pixelize_filter.h
index 6e385ae81b..db5767f537 100644
--- a/plugins/filters/pixelizefilter/kis_pixelize_filter.h
+++ b/plugins/filters/pixelizefilter/kis_pixelize_filter.h
@@ -1,50 +1,50 @@
/*
* This file is part of the KDE project
*
* Copyright (c) Michael Thaler <michael.thaler@physik.tu-muenchen.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.
*/
#ifndef _KIS_PIXELIZE_FILTER_H_
#define _KIS_PIXELIZE_FILTER_H_
#include "filter/kis_filter.h"
#include "kis_config_widget.h"
class KisPixelizeFilter : public KisFilter
{
public:
KisPixelizeFilter();
public:
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater) const override;
static inline KoID id() {
return KoID("pixelize", i18n("Pixelize"));
}
QRect neededRect(const QRect & rect, const KisFilterConfigurationSP config, int lod) const override;
QRect changedRect(const QRect & rect, const KisFilterConfigurationSP config, int lod) const override;
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
};
#endif
diff --git a/plugins/filters/posterize/posterize.cpp b/plugins/filters/posterize/posterize.cpp
index 09d677ab29..3a7ad211ea 100644
--- a/plugins/filters/posterize/posterize.cpp
+++ b/plugins/filters/posterize/posterize.cpp
@@ -1,128 +1,128 @@
/*
* Copyright (c) 2014 Manuel Riecke <spell1337@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 "posterize.h"
#include <stdlib.h>
#include <vector>
#include <QPoint>
#include <QTime>
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <kis_processing_information.h>
#include <kis_types.h>
#include <kis_selection.h>
#include <kis_layer.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_registry.h>
#include <kis_global.h>
#include <KoColorSpaceMaths.h>
#include <filter/kis_color_transformation_configuration.h>
#include <widgets/kis_multi_integer_filter_widget.h>
K_PLUGIN_FACTORY_WITH_JSON(PosterizeFactory, "kritaposterize.json", registerPlugin<Posterize>();)
Posterize::Posterize(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisFilterRegistry::instance()->add(KisFilterSP(new KisFilterPosterize()));
}
Posterize::~Posterize()
{
}
KisFilterPosterize::KisFilterPosterize() : KisColorTransformationFilter(id(), FiltersCategoryArtisticId, i18n("&Posterize..."))
{
setColorSpaceIndependence(FULLY_INDEPENDENT);
setSupportsPainting(true);
setShowConfigurationWidget(true);
}
KoColorTransformation* KisFilterPosterize::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const
{
return new KisPosterizeColorTransformation(config->getInt("steps", 16), cs);
}
KisPosterizeColorTransformation::KisPosterizeColorTransformation(int steps, const KoColorSpace* cs) : m_colorSpace(cs), m_psize(cs->pixelSize())
{
m_step = KoColorSpaceMathsTraits<quint16>::max / steps;
m_halfStep = m_step / 2;
m_fromConversion = KoColorSpaceRegistry::instance()->createColorConverter(
m_colorSpace,
KoColorSpaceRegistry::instance()->rgb16("sRGB-elle-V2-srgbtrc.icc"),
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
m_toConversion = KoColorSpaceRegistry::instance()->createColorConverter(
KoColorSpaceRegistry::instance()->rgb16("sRGB-elle-V2-srgbtrc.icc"),
m_colorSpace,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
}
KisPosterizeColorTransformation::~KisPosterizeColorTransformation()
{
delete m_fromConversion;
delete m_toConversion;
}
KisConfigWidget* KisFilterPosterize::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
vKisIntegerWidgetParam param;
param.push_back(KisIntegerWidgetParam(2, 128, 16, i18n("Steps"), "steps"));
return new KisMultiIntegerFilterWidget(id().id(), parent, id().id(), param);
}
-KisFilterConfigurationSP KisFilterPosterize::defaultConfiguration() const
+KisFilterConfigurationSP KisFilterPosterize::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("steps", 16);
return config;
}
void KisPosterizeColorTransformation::transform(const quint8* src, quint8* dst, qint32 nPixels) const
{
quint16 m_rgba[4];
quint16 m_mod[4];
while (nPixels--) {
m_fromConversion->transform(src, reinterpret_cast<quint8 *>(m_rgba), 1);
m_mod[0] = m_rgba[0] % m_step;
m_mod[1] = m_rgba[1] % m_step;
m_mod[2] = m_rgba[2] % m_step;
m_mod[3] = m_rgba[3] % m_step;
m_rgba[0] = m_rgba[0] + (m_mod[0] > m_halfStep ? m_step - m_mod[0] : -m_mod[0]);
m_rgba[1] = m_rgba[1] + (m_mod[1] > m_halfStep ? m_step - m_mod[1] : -m_mod[1]);
m_rgba[2] = m_rgba[2] + (m_mod[2] > m_halfStep ? m_step - m_mod[2] : -m_mod[2]);
m_rgba[3] = m_rgba[3] + (m_mod[3] > m_halfStep ? m_step - m_mod[3] : -m_mod[3]);
m_toConversion->transform(reinterpret_cast<quint8 *>(m_rgba), dst, 1);
src += m_psize;
dst += m_psize;
}
}
#include "posterize.moc"
diff --git a/plugins/filters/posterize/posterize.h b/plugins/filters/posterize/posterize.h
index 2b3fca42d0..016cf8b7ca 100644
--- a/plugins/filters/posterize/posterize.h
+++ b/plugins/filters/posterize/posterize.h
@@ -1,64 +1,64 @@
/*
* Copyright (c) 2014 Manuel Riecke <spell1337@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 POSTERIZE_H
#define POSTERIZE_H
#include <QObject>
#include <QVariant>
#include "filter/kis_color_transformation_filter.h"
#include "kis_config_widget.h"
class Posterize : public QObject
{
Q_OBJECT
public:
Posterize(QObject *parent, const QVariantList &);
~Posterize() override;
};
class KisFilterPosterize : public KisColorTransformationFilter
{
public:
KisFilterPosterize();
public:
KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const override;
KisConfigWidget* createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
static inline KoID id() {
return KoID("posterize", i18n("Posterize"));
}
protected:
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
};
class KisPosterizeColorTransformation : public KoColorTransformation
{
public:
KisPosterizeColorTransformation(int steps, const KoColorSpace* cs);
~KisPosterizeColorTransformation() override;
void transform(const quint8* src, quint8* dst, qint32 nPixels) const override;
private:
const KoColorSpace* m_colorSpace;
quint32 m_psize;
quint16 m_step;
quint16 m_halfStep;
KoColorConversionTransformation* m_fromConversion;
KoColorConversionTransformation* m_toConversion;
};
#endif
diff --git a/plugins/filters/raindropsfilter/kis_raindrops_filter.cpp b/plugins/filters/raindropsfilter/kis_raindrops_filter.cpp
index fa3d88c75b..97222b45ac 100644
--- a/plugins/filters/raindropsfilter/kis_raindrops_filter.cpp
+++ b/plugins/filters/raindropsfilter/kis_raindrops_filter.cpp
@@ -1,397 +1,398 @@
/*
* This file is part of the KDE project
*
* Copyright (c) 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
*
* ported from digikam, copyrighted 2004 by Gilles Caulier,
* Original RainDrops algorithm copyrighted 2004 by
* Pieter Z. Voloshyn <pieter_voloshyn at ame.com.br>.
*
* 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_raindrops_filter.h"
#include <stdlib.h>
#include <vector>
#include <math.h>
#include <QDateTime>
#include <QPoint>
#include <QSpinBox>
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include "KoIntegerMaths.h"
#include <KoUpdater.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_registry.h>
#include <filter/kis_filter.h>
#include <kis_global.h>
#include <kis_selection.h>
#include <kis_types.h>
#include <kis_paint_device.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include <kis_random_accessor_ng.h>
+#include <KisGlobalResourcesInterface.h>
#include "widgets/kis_multi_integer_filter_widget.h"
KisRainDropsFilter::KisRainDropsFilter()
: KisFilter(id(), FiltersCategoryArtisticId, i18n("&Raindrops..."))
{
setSupportsPainting(false);
setSupportsThreading(false);
setSupportsAdjustmentLayers(false);
}
// This method have been ported from Pieter Z. Voloshyn algorithm code.
/* Function to apply the RainDrops effect (inspired from Jason Waltman code)
*
* data => The image data in RGBA mode.
* Width => Width of image.
* Height => Height of image.
* DropSize => Raindrop size
* number => Maximum number of raindrops
* fishEyes => FishEye coefficient
*
* Theory => This functions does several math's functions and the engine
* is simple to understand, but a little hard to implement. A
* control will indicate if there is or not a raindrop in that
* area, if not, a fisheye effect with a random size (max=DropSize)
* will be applied, after this, a shadow will be applied too.
* and after this, a blur function will finish the effect.
*/
void KisRainDropsFilter::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater ) const
{
QPoint srcTopLeft = applyRect.topLeft();
Q_ASSERT(device);
//read the filter configuration values from the KisFilterConfiguration object
quint32 DropSize = config->getInt("dropSize", 80);
quint32 number = config->getInt("number", 80);
quint32 fishEyes = config->getInt("fishEyes", 30);
qsrand(config->getInt("seed"));
if (fishEyes <= 0) fishEyes = 1;
if (fishEyes > 100) fishEyes = 100;
int Width = applyRect.width();
int Height = applyRect.height();
bool** BoolMatrix = CreateBoolArray(Width, Height);
int i, j, k, l, m, n; // loop variables
int Bright; // Bright value for shadows and highlights
int x, y; // center coordinates
int Counter = 0; // Counter (duh !)
int NewSize; // Size of current raindrop
int halfSize; // Half of the current raindrop
int Radius; // Maximum radius for raindrop
int BlurRadius; // Blur Radius
int BlurPixels;
double r, a; // polar coordinates
double OldRadius; // Radius before processing
double NewfishEyes = (double)fishEyes * 0.01; // FishEye fishEyesicients
double s;
double R, G, B;
bool FindAnother = false; // To search for good coordinates
const KoColorSpace * cs = device->colorSpace();
// Init boolean Matrix.
for (i = 0 ; i < Width; ++i) {
for (j = 0 ; j < Height; ++j) {
BoolMatrix[i][j] = false;
}
}
progressUpdater->setRange(0, number);
KisRandomAccessorSP dstAccessor = device->createRandomAccessorNG(srcTopLeft.x(), srcTopLeft.y());
for (uint NumBlurs = 0; NumBlurs <= number; ++NumBlurs) {
NewSize = (int)(qrand() * ((double)(DropSize - 5) / RAND_MAX) + 5);
halfSize = NewSize / 2;
Radius = halfSize;
s = Radius / log(NewfishEyes * Radius + 1);
Counter = 0;
do {
FindAnother = false;
y = (int)(qrand() * ((double)(Width - 1) / RAND_MAX));
x = (int)(qrand() * ((double)(Height - 1) / RAND_MAX));
if (BoolMatrix[y][x])
FindAnother = true;
else
for (i = x - halfSize ; i <= x + halfSize; i++)
for (j = y - halfSize ; j <= y + halfSize; j++)
if ((i >= 0) && (i < Height) && (j >= 0) && (j < Width))
if (BoolMatrix[j][i])
FindAnother = true;
Counter++;
} while (FindAnother && Counter < 10000);
if (Counter >= 10000) {
NumBlurs = number;
break;
}
for (i = -1 * halfSize ; i < NewSize - halfSize; i++) {
for (j = -1 * halfSize ; j < NewSize - halfSize; j++) {
r = sqrt((double)i * i + j * j);
a = atan2(static_cast<double>(i), static_cast<double>(j));
if (r <= Radius) {
OldRadius = r;
r = (exp(r / s) - 1) / NewfishEyes;
k = x + (int)(r * sin(a));
l = y + (int)(r * cos(a));
m = x + i;
n = y + j;
if ((k >= 0) && (k < Height) && (l >= 0) && (l < Width)) {
if ((m >= 0) && (m < Height) && (n >= 0) && (n < Width)) {
Bright = 0;
if (OldRadius >= 0.9 * Radius) {
if ((a <= 0) && (a > -2.25))
Bright = -80;
else if ((a <= -2.25) && (a > -2.5))
Bright = -40;
else if ((a <= 0.25) && (a > 0))
Bright = -40;
}
else if (OldRadius >= 0.8 * Radius) {
if ((a <= -0.75) && (a > -1.50))
Bright = -40;
else if ((a <= 0.10) && (a > -0.75))
Bright = -30;
else if ((a <= -1.50) && (a > -2.35))
Bright = -30;
}
else if (OldRadius >= 0.7 * Radius) {
if ((a <= -0.10) && (a > -2.0))
Bright = -20;
else if ((a <= 2.50) && (a > 1.90))
Bright = 60;
}
else if (OldRadius >= 0.6 * Radius) {
if ((a <= -0.50) && (a > -1.75))
Bright = -20;
else if ((a <= 0) && (a > -0.25))
Bright = 20;
else if ((a <= -2.0) && (a > -2.25))
Bright = 20;
}
else if (OldRadius >= 0.5 * Radius) {
if ((a <= -0.25) && (a > -0.50))
Bright = 30;
else if ((a <= -1.75) && (a > -2.0))
Bright = 30;
}
else if (OldRadius >= 0.4 * Radius) {
if ((a <= -0.5) && (a > -1.75))
Bright = 40;
}
else if (OldRadius >= 0.3 * Radius) {
if ((a <= 0) && (a > -2.25))
Bright = 30;
}
else if (OldRadius >= 0.2 * Radius) {
if ((a <= -0.5) && (a > -1.75))
Bright = 20;
}
BoolMatrix[n][m] = true;
QColor originalColor;
dstAccessor->moveTo(srcTopLeft.x() + l, srcTopLeft.y() + k);
cs->toQColor(dstAccessor->oldRawData(), &originalColor);
int newRed = CLAMP(originalColor.red() + Bright, 0, quint8_MAX);
int newGreen = CLAMP(originalColor.green() + Bright, 0, quint8_MAX);
int newBlue = CLAMP(originalColor.blue() + Bright, 0, quint8_MAX);
QColor newColor;
newColor.setRgb(newRed, newGreen, newBlue);
dstAccessor->moveTo(srcTopLeft.x() + n, srcTopLeft.y() + m);
cs->fromQColor(newColor, dstAccessor->rawData());
}
}
}
}
}
BlurRadius = NewSize / 25 + 1;
for (i = -1 * halfSize - BlurRadius ; i < NewSize - halfSize + BlurRadius; i++) {
for (j = -1 * halfSize - BlurRadius; j < NewSize - halfSize + BlurRadius; ++j) {
r = sqrt((double)i * i + j * j);
if (r <= Radius * 1.1) {
R = G = B = 0;
BlurPixels = 0;
for (k = -1 * BlurRadius; k < BlurRadius + 1; k++)
for (l = -1 * BlurRadius; l < BlurRadius + 1; l++) {
m = x + i + k;
n = y + j + l;
if ((m >= 0) && (m < Height) && (n >= 0) && (n < Width)) {
QColor color;
dstAccessor->moveTo(srcTopLeft.x() + n, srcTopLeft.y() + m);
cs->toQColor(dstAccessor->rawData(), &color);
R += color.red();
G += color.green();
B += color.blue();
BlurPixels++;
}
}
m = x + i;
n = y + j;
if ((m >= 0) && (m < Height) && (n >= 0) && (n < Width)) {
QColor color;
color.setRgb((int)(R / BlurPixels), (int)(G / BlurPixels), (int)(B / BlurPixels));
dstAccessor->moveTo(srcTopLeft.x() + n, srcTopLeft.y() + m);
cs->fromQColor(color, dstAccessor->rawData());
}
}
}
}
progressUpdater->setValue(NumBlurs);
}
FreeBoolArray(BoolMatrix, Width);
}
// This method have been ported from Pieter Z. Voloshyn algorithm code.
/* Function to free a dynamic boolean array
*
* lpbArray => Dynamic boolean array
* Columns => The array bidimension value
*
* Theory => An easy to understand 'for' statement
*/
void KisRainDropsFilter::FreeBoolArray(bool** lpbArray, uint Columns) const
{
for (uint i = 0; i < Columns; ++i)
free(lpbArray[i]);
free(lpbArray);
}
/* Function to create a bidimentional dynamic boolean array
*
* Columns => Number of columns
* Rows => Number of rows
*
* Theory => Using 'for' statement, we can alloc multiple dynamic arrays
* To create more dimensions, just add some 'for's, ok?
*/
bool** KisRainDropsFilter::CreateBoolArray(uint Columns, uint Rows) const
{
bool** lpbArray = 0;
lpbArray = (bool**) malloc(Columns * sizeof(bool*));
if (lpbArray == 0)
return (0);
for (uint i = 0; i < Columns; ++i) {
lpbArray[i] = (bool*) malloc(Rows * sizeof(bool));
if (lpbArray[i] == 0) {
FreeBoolArray(lpbArray, Columns);
return (0);
}
}
return (lpbArray);
}
// This method have been ported from Pieter Z. Voloshyn algorithm code.
/* This function limits the RGB values
*
* ColorValue => Here, is an RGB value to be analyzed
*
* Theory => A color is represented in RGB value (e.g. 0xFFFFFF is
* white color). But R, G and B values have 256 values to be used
* so, this function analyzes the value and limits to this range
*/
uchar KisRainDropsFilter::LimitValues(int ColorValue) const
{
if (ColorValue > 255) // MAX = 255
ColorValue = 255;
if (ColorValue < 0) // MIN = 0
ColorValue = 0;
return ((uchar) ColorValue);
}
KisConfigWidget * KisRainDropsFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool) const
{
vKisIntegerWidgetParam param;
param.push_back(KisIntegerWidgetParam(1, 200, 80, i18n("Drop size"), "dropsize"));
param.push_back(KisIntegerWidgetParam(1, 500, 80, i18n("Number of drops"), "number"));
param.push_back(KisIntegerWidgetParam(1, 100, 30, i18n("Fish eyes"), "fishEyes"));
KisMultiIntegerFilterWidget * w = new KisMultiIntegerFilterWidget(id().id(), parent, id().id(), param);
- w->setConfiguration(defaultConfiguration());
+ w->setConfiguration(defaultConfiguration(KisGlobalResourcesInterface::instance()));
return w;
}
-KisFilterConfigurationSP KisRainDropsFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisRainDropsFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("dropsize", 80);
config->setProperty("number", 80);
config->setProperty("fishEyes", 30);
config->setProperty("seed", QTime::currentTime().msec());
return config;
}
diff --git a/plugins/filters/raindropsfilter/kis_raindrops_filter.h b/plugins/filters/raindropsfilter/kis_raindrops_filter.h
index a155a7bb56..3895bd41fb 100644
--- a/plugins/filters/raindropsfilter/kis_raindrops_filter.h
+++ b/plugins/filters/raindropsfilter/kis_raindrops_filter.h
@@ -1,51 +1,51 @@
/*
* This file is part of Krita
*
* Copyright (c) Michael Thaler <michael.thaler@physik.tu-muenchen.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.
*/
#ifndef _KIS_RAINDROPS_FILTER_H_
#define _KIS_RAINDROPS_FILTER_H_
#include "filter/kis_filter.h"
#include "kis_config_widget.h"
#include "kis_paint_device.h"
class KisRainDropsFilter : public KisFilter
{
public:
KisRainDropsFilter();
public:
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater) const override;
static inline KoID id() {
return KoID("raindrops", i18n("Raindrops"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
private:
bool** CreateBoolArray(uint Columns, uint Rows) const;
void FreeBoolArray(bool** lpbArray, uint Columns) const;
uchar LimitValues(int ColorValue) const;
};
#endif
diff --git a/plugins/filters/randompickfilter/kis_wdg_random_pick.cpp b/plugins/filters/randompickfilter/kis_wdg_random_pick.cpp
index a78bc24463..a5a796d253 100644
--- a/plugins/filters/randompickfilter/kis_wdg_random_pick.cpp
+++ b/plugins/filters/randompickfilter/kis_wdg_random_pick.cpp
@@ -1,76 +1,77 @@
/*
* 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_random_pick.h"
#include <QLayout>
#include <filter/kis_filter_configuration.h>
+#include <KisGlobalResourcesInterface.h>
#include "ui_wdgrandompickoptions.h"
KisWdgRandomPick::KisWdgRandomPick(KisFilter* /*nfilter*/, QWidget* parent)
: KisConfigWidget(parent)
{
m_widget = new Ui_WdgRandomPickOptions();
m_widget->setupUi(this);
connect(widget()->intLevel, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->intWindowSize, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->intOpacity, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
m_seedH = rand();
m_seedV = rand();
m_seedThreshold = rand();
}
KisWdgRandomPick::~KisWdgRandomPick()
{
delete m_widget;
}
void KisWdgRandomPick::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("level", value)) {
widget()->intLevel->setValue(value.toUInt());
}
if (config->getProperty("windowsize", value)) {
widget()->intWindowSize->setValue(value.toUInt());
}
if (config->getProperty("opacity", value)) {
widget()->intOpacity->setValue(value.toUInt());
}
}
KisPropertiesConfigurationSP KisWdgRandomPick::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("randompick", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("randompick", 1, KisGlobalResourcesInterface::instance());
config->setProperty("level", this->widget()->intLevel->value());
config->setProperty("windowsize", this->widget()->intWindowSize->value());
config->setProperty("opacity", this->widget()->intOpacity->value());
config->setProperty("seedH", m_seedH);
config->setProperty("seedV", m_seedV);
config->setProperty("seedThreshold", m_seedThreshold);
return config;
}
diff --git a/plugins/filters/randompickfilter/randompickfilter.cpp b/plugins/filters/randompickfilter/randompickfilter.cpp
index da1ee22bf9..e02dd21ceb 100644
--- a/plugins/filters/randompickfilter/randompickfilter.cpp
+++ b/plugins/filters/randompickfilter/randompickfilter.cpp
@@ -1,157 +1,157 @@
/*
* 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.
*/
#include "randompickfilter.h"
#include <stdlib.h>
#include <vector>
#include <math.h>
#include <QPoint>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <klocalizedstring.h>
#include <KoUpdater.h>
#include <KoMixColorsOp.h>
#include <filter/kis_filter_registry.h>
#include <kis_global.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_paint_device.h>
#include <kis_random_accessor_ng.h>
#include <kis_random_generator.h>
#include <kis_selection.h>
#include <kis_types.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include "kis_wdg_random_pick.h"
#include "ui_wdgrandompickoptions.h"
#include <kis_iterator_ng.h>
#include <KisSequentialIteratorProgress.h>
K_PLUGIN_FACTORY_WITH_JSON(KritaRandomPickFilterFactory, "kritarandompickfilter.json", registerPlugin<KritaRandomPickFilter>();)
KritaRandomPickFilter::KritaRandomPickFilter(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisFilterRegistry::instance()->add(new KisFilterRandomPick());
}
KritaRandomPickFilter::~KritaRandomPickFilter()
{
}
KisFilterRandomPick::KisFilterRandomPick() : KisFilter(id(), FiltersCategoryOtherId, i18n("&Random Pick..."))
{
setColorSpaceIndependence(FULLY_INDEPENDENT);
setSupportsPainting(true);
}
void KisFilterRandomPick::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
Q_UNUSED(config);
Q_ASSERT(!device.isNull());
const KoColorSpace * cs = device->colorSpace();
QVariant value;
int level = (config && config->getProperty("level", value)) ? value.toInt() : 50;
int opacity = (config && config->getProperty("opacity", value)) ? value.toInt() : 100;
double windowsize = (config && config->getProperty("windowsize", value)) ? value.toDouble() : 2.5;
int seedThreshold = rand();
int seedH = rand();
int seedV = rand();
if (config) {
seedThreshold = config->getInt("seedThreshold", seedThreshold);
seedH = config->getInt("seedH", seedH);
seedV = config->getInt("seedV", seedV);
}
KisRandomGenerator randT(seedThreshold);
KisRandomGenerator randH(seedH);
KisRandomGenerator randV(seedV);
KisSequentialIteratorProgress dstIt(device, applyRect, progressUpdater);
KisRandomConstAccessorSP srcRA = device->createRandomConstAccessorNG(0, 0);
double threshold = (100 - level) / 100.0;
qint16 weights[2];
weights[0] = (255 * opacity) / 100; weights[1] = 255 - weights[0];
const quint8* pixels[2];
KoMixColorsOp * mixOp = cs->mixColorsOp();
while (dstIt.nextPixel()) {
if (randT.doubleRandomAt(dstIt.x(), dstIt.y()) > threshold) {
int x = static_cast<int>(dstIt.x() + windowsize * (randH.doubleRandomAt(dstIt.x(), dstIt.y()) - 0.5));
int y = static_cast<int>(dstIt.y() + windowsize * (randV.doubleRandomAt(dstIt.x(), dstIt.y()) -0.5));
srcRA->moveTo(x, y);
pixels[0] = srcRA->oldRawData();
pixels[1] = dstIt.oldRawData();
mixOp->mixColors(pixels, weights, 2, dstIt.rawData());
}
}
}
KisConfigWidget * KisFilterRandomPick::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
return new KisWdgRandomPick((KisFilter*)this, (QWidget*)parent);
}
-KisFilterConfigurationSP KisFilterRandomPick::defaultConfiguration() const
+KisFilterConfigurationSP KisFilterRandomPick::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("level", 50);
config->setProperty("windowsize", 2.5);
config->setProperty("opacity", 100);
config->setProperty("seedThreshold", rand());
config->setProperty("seedH", rand());
config->setProperty("seedV", rand());
return config;
}
QRect KisFilterRandomPick::neededRect(const QRect& rect, const KisFilterConfigurationSP config, int lod) const
{
Q_UNUSED(lod);
QVariant value;
int windowsize = ceil((config && config->getProperty("windowsize", value)) ? value.toDouble() : 2.5);
return rect.adjusted(-windowsize, -windowsize, windowsize, windowsize);
}
QRect KisFilterRandomPick::changedRect(const QRect &rect, const KisFilterConfigurationSP config, int lod) const
{
return neededRect(rect, config, lod);
}
#include "randompickfilter.moc"
diff --git a/plugins/filters/randompickfilter/randompickfilter.h b/plugins/filters/randompickfilter/randompickfilter.h
index 919ee23118..33702cc791 100644
--- a/plugins/filters/randompickfilter/randompickfilter.h
+++ b/plugins/filters/randompickfilter/randompickfilter.h
@@ -1,60 +1,60 @@
/*
* 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.
*/
#ifndef RANDOMPICKFILTER_H
#define RANDOMPICKFILTER_H
#include <QObject>
#include <QVariant>
#include "filter/kis_filter.h"
class KisConfigWidget;
class KritaRandomPickFilter : public QObject
{
Q_OBJECT
public:
KritaRandomPickFilter(QObject *parent, const QVariantList &);
~KritaRandomPickFilter() override;
};
class KisFilterRandomPick : public KisFilter
{
public:
KisFilterRandomPick();
public:
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("randompick", i18n("Random Pick"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
QRect neededRect(const QRect& rect, const KisFilterConfigurationSP config, int lod = 0) const override;
QRect changedRect(const QRect& rect, const KisFilterConfigurationSP config, int lod = 0) const override;
};
#endif
diff --git a/plugins/filters/roundcorners/kis_round_corners_filter.cpp b/plugins/filters/roundcorners/kis_round_corners_filter.cpp
index 6df920c813..ca4a9c3dc0 100644
--- a/plugins/filters/roundcorners/kis_round_corners_filter.cpp
+++ b/plugins/filters/roundcorners/kis_round_corners_filter.cpp
@@ -1,155 +1,155 @@
/*
* This file is part of Krita
*
* Copyright (c) 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
*
* ported from Gimp, Copyright (C) 1997 Eiichi Takamori <taka@ma1.seikyou.ne.jp>
* original pixelize.c for GIMP 0.54 by Tracy Scott
*
* 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_round_corners_filter.h"
#include <stdlib.h>
#include <vector>
#include <math.h>
#include <QPoint>
#include <klocalizedstring.h>
#include <kpluginfactory.h>
#include <KoUpdater.h>
#include <kis_debug.h>
#include <KisDocument.h>
#include <filter/kis_filter_registry.h>
#include <kis_global.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <widgets/kis_multi_integer_filter_widget.h>
#include <kis_selection.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include <kis_types.h>
#include <KisSequentialIteratorProgress.h>
#include <kis_algebra_2d.h>
#include <KoProgressUpdater.h>
KisRoundCornersFilter::KisRoundCornersFilter() : KisFilter(id(), FiltersCategoryMapId, i18n("&Round Corners..."))
{
setSupportsPainting(false);
}
void fadeOneCorner(KisPaintDeviceSP device,
const QPoint &basePoint,
const QRect &processRect,
const qreal thresholdSq,
KoUpdater* progressUpdater)
{
const KoColorSpace *cs = device->colorSpace();
KisSequentialIteratorProgress dstIt(device, processRect, progressUpdater);
while (dstIt.nextPixel()) {
const QPointF point(dstIt.x(), dstIt.y());
const qreal distanceSq = kisSquareDistance(point, basePoint);
if (distanceSq >= thresholdSq) {
cs->setOpacity(dstIt.rawData(), OPACITY_TRANSPARENT_U8, 1);
}
}
}
void KisRoundCornersFilter::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
Q_UNUSED(config);
Q_ASSERT(!device.isNull());
if (!device || !config) {
warnKrita << "Invalid parameters for round corner filter";
dbgPlugins << device << " " << config;
return;
}
const QRect bounds = device->defaultBounds()->bounds();
const qint32 radius = qMin(KisAlgebra2D::minDimension(bounds) / 2, qMax(1, config->getInt("radius" , 30)));
const qreal radiusSq = pow2(radius);
struct CornerJob {
QRect rc;
QPoint pt;
KoUpdater *progressUpdater;
};
QVector<CornerJob> jobs;
KoProgressUpdater compositeUpdater(progressUpdater, KoProgressUpdater::Unthreaded);
{
QRect rc(bounds.x(), bounds.y(), radius, radius);
QPoint pt(rc.bottomRight());
jobs << CornerJob({rc, pt, compositeUpdater.startSubtask()});
}
{
QRect rc(bounds.x() + bounds.width() - radius, bounds.y(), radius, radius);
QPoint pt(rc.bottomLeft());
jobs << CornerJob({rc, pt, compositeUpdater.startSubtask()});
}
{
QRect rc(bounds.x(), bounds.y() + bounds.height() - radius, radius, radius);
QPoint pt(rc.topRight());
jobs << CornerJob({rc, pt, compositeUpdater.startSubtask()});
}
{
QRect rc(bounds.x() + bounds.width() - radius, bounds.y() + bounds.height() - radius, radius, radius);
QPoint pt(rc.topLeft());
jobs << CornerJob({rc, pt, compositeUpdater.startSubtask()});
}
Q_FOREACH (const CornerJob &job, jobs) {
const QRect processRect = job.rc & applyRect;
if (!processRect.isEmpty()) {
fadeOneCorner(device, job.pt, processRect, radiusSq, job.progressUpdater);
}
}
}
KisConfigWidget * KisRoundCornersFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool) const
{
vKisIntegerWidgetParam param;
param.push_back(KisIntegerWidgetParam(2, 100, 30, i18n("Radius"), "radius"));
return new KisMultiIntegerFilterWidget(id().id(), parent, id().id(), param);
}
-KisFilterConfigurationSP KisRoundCornersFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisRoundCornersFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("radius", 30);
return config;
}
diff --git a/plugins/filters/roundcorners/kis_round_corners_filter.h b/plugins/filters/roundcorners/kis_round_corners_filter.h
index d22003d50c..da74e751d1 100644
--- a/plugins/filters/roundcorners/kis_round_corners_filter.h
+++ b/plugins/filters/roundcorners/kis_round_corners_filter.h
@@ -1,47 +1,47 @@
/*
* This file is part of the KDE project
*
* Copyright (c) Michael Thaler <michael.thaler@physik.tu-muenchen.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.
*/
#ifndef _KIS_ROUND_CORNERS_FILTER_H_
#define _KIS_ROUND_CORNERS_FILTER_H_
#include "kis_paint_device.h"
#include "filter/kis_filter.h"
#include "kis_config_widget.h"
class KisRoundCornersFilter : public KisFilter
{
public:
KisRoundCornersFilter();
public:
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("roundcorners", i18n("Round Corners"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
private:
};
#endif
diff --git a/plugins/filters/smalltilesfilter/kis_small_tiles_filter.cpp b/plugins/filters/smalltilesfilter/kis_small_tiles_filter.cpp
index ee25e5a2bf..b5d31c69c0 100644
--- a/plugins/filters/smalltilesfilter/kis_small_tiles_filter.cpp
+++ b/plugins/filters/smalltilesfilter/kis_small_tiles_filter.cpp
@@ -1,115 +1,115 @@
/*
* This file is part of Krita
*
* Copyright (c) 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
*
* ported from Gimp, Copyright (C) 1997 Eiichi Takamori <taka@ma1.seikyou.ne.jp>
* original pixelize.c for GIMP 0.54 by Tracy Scott
*
* 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_small_tiles_filter.h"
#include <stdlib.h>
#include <vector>
#include <QPoint>
#include <QSpinBox>
#include <QVector>
#include <klocalizedstring.h>
#include <kpluginfactory.h>
#include <KoUpdater.h>
#include <KisDocument.h>
#include <kis_debug.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <filter/kis_filter_registry.h>
#include <kis_global.h>
#include <kis_types.h>
#include <kis_paint_device.h>
#include <kis_filter_strategy.h>
#include <kis_painter.h>
#include <kis_selection.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include <KoCompositeOpRegistry.h>
#include "widgets/kis_multi_integer_filter_widget.h"
KisSmallTilesFilter::KisSmallTilesFilter() : KisFilter(id(), FiltersCategoryMapId, i18n("&Small Tiles..."))
{
setSupportsPainting(true);
setSupportsThreading(false);
setSupportsAdjustmentLayers(false);
}
void KisSmallTilesFilter::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
Q_ASSERT(!device.isNull());
//read the filter configuration values from the KisFilterConfiguration object
const quint32 numberOfTiles = config->getInt("numberOfTiles", 2);
const QRect srcRect = applyRect;
const int w = static_cast<int>(srcRect.width() / numberOfTiles);
const int h = static_cast<int>(srcRect.height() / numberOfTiles);
KisPaintDeviceSP tile = device->createThumbnailDevice(w, h);
if (tile.isNull()) return;
device->clear(applyRect);
KisPainter gc(device);
gc.setCompositeOp(COMPOSITE_COPY);
if (progressUpdater) {
progressUpdater->setRange(0, numberOfTiles);
}
for (uint y = 0; y < numberOfTiles; ++y) {
for (uint x = 0; x < numberOfTiles; ++x) {
gc.bitBlt(w * x, h * y, tile, 0, 0, w, h);
}
if (progressUpdater) progressUpdater->setValue(y);
}
gc.end();
}
KisConfigWidget * KisSmallTilesFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool) const
{
vKisIntegerWidgetParam param;
param.push_back(KisIntegerWidgetParam(2, 5, 1, i18n("Number of tiles"), "numberOfTiles"));
return new KisMultiIntegerFilterWidget(id().id(), parent, id().id(), param);
}
-KisFilterConfigurationSP KisSmallTilesFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisSmallTilesFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("numberOfTiles", 2);
return config;
}
diff --git a/plugins/filters/smalltilesfilter/kis_small_tiles_filter.h b/plugins/filters/smalltilesfilter/kis_small_tiles_filter.h
index dbc1d549a0..f98db2a662 100644
--- a/plugins/filters/smalltilesfilter/kis_small_tiles_filter.h
+++ b/plugins/filters/smalltilesfilter/kis_small_tiles_filter.h
@@ -1,51 +1,51 @@
/*
* This file is part of the KDE project
*
* Copyright (c) Michael Thaler <michael.thaler@physik.tu-muenchen.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.
*/
#ifndef _KIS_SMALL_TILES_FILTER_H_
#define _KIS_SMALL_TILES_FILTER_H_
#include "kis_paint_device.h"
#include "filter/kis_filter.h"
#include "kis_config_widget.h"
class KisSmallTilesFilter : public KisFilter
{
public:
KisSmallTilesFilter();
public:
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("smalltiles", i18n("Small Tiles"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
public:
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
};
#endif
diff --git a/plugins/filters/tests/kis_all_filter_test.cpp b/plugins/filters/tests/kis_all_filter_test.cpp
index c6c7638711..e24bbacf0a 100644
--- a/plugins/filters/tests/kis_all_filter_test.cpp
+++ b/plugins/filters/tests/kis_all_filter_test.cpp
@@ -1,288 +1,289 @@
/*
* 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.
*/
#include "kis_all_filter_test.h"
#include <QTest>
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_selection.h"
#include "kis_processing_information.h"
#include "filter/kis_filter.h"
#include "kis_pixel_selection.h"
#include "kis_transaction.h"
#include <KoColorSpaceRegistry.h>
#include <sdk/tests/qimage_test_util.h>
#include <sdk/tests/testing_timed_default_bounds.h>
+#include <KisGlobalResourcesInterface.h>
bool testFilterSrcNotIsDev(KisFilterSP f)
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "carrot.png");
QImage result(QString(FILES_DATA_DIR) + QDir::separator() + "carrot_" + f->id() + ".png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->setDefaultBounds(new TestUtil::TestingTimedDefaultBounds(qimage.rect()));
KisPaintDeviceSP dstdev = new KisPaintDevice(cs);
dstdev->setDefaultBounds(new TestUtil::TestingTimedDefaultBounds(qimage.rect()));
dev->convertFromQImage(qimage, 0, 0, 0);
// Get the predefined configuration from a file
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
//qDebug() << "creating new file for " << f->id();
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out.setCodec("UTF-8");
out << kfc->toXML();
} else {
QString s;
QTextStream in(&file);
in.setCodec("UTF-8");
s = in.readAll();
//qDebug() << "Read for " << f->id() << "\n" << s;
kfc->fromXML(s);
}
dbgKrita << f->id();// << "\n" << kfc->toXML() << "\n";
f->process(dev, dstdev, 0, QRect(QPoint(0,0), qimage.size()), kfc);
QPoint errpoint;
QImage actualResult = dstdev->convertToQImage(0, 0, 0, qimage.width(), qimage.height());
if (!TestUtil::compareQImages(errpoint, result, actualResult, 1, 1)) {
qDebug() << "Failed compare result images for: " << f->id();
qDebug() << errpoint;
actualResult.save(QString("carrot_%1.png").arg(f->id()));
result.save(QString("carrot_%1_expected.png").arg(f->id()));
return false;
}
return true;
}
bool testFilter(KisFilterSP f)
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "carrot.png");
QString resultFileName = QString(FILES_DATA_DIR) + QDir::separator() + "carrot_" + f->id() + ".png";
QImage result(resultFileName);
//if (!f->id().contains("hsv")) return true;
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->setDefaultBounds(new TestUtil::TestingTimedDefaultBounds(qimage.rect()));
dev->convertFromQImage(qimage, 0, 0, 0);
KisTransaction * cmd = new KisTransaction(kundo2_noi18n(f->name()), dev);
// Get the predefined configuration from a file
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
//qDebug() << "creating new file for " << f->id();
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out.setCodec("UTF-8");
out << kfc->toXML();
} else {
QString s;
QTextStream in(&file);
in.setCodec("UTF-8");
s = in.readAll();
//qDebug() << "Read for " << f->id() << "\n" << s;
const bool validConfig = kfc->fromXML(s);
if (!validConfig) {
qDebug() << QString("Couldn't parse XML settings for filter %1").arg(f->id()).toLatin1();
return false;
}
}
dbgKrita << f->id();// << "\n" << kfc->toXML() << "\n";
f->process(dev, QRect(QPoint(0,0), qimage.size()), kfc);
QPoint errpoint;
delete cmd;
QImage actualResult = dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height());
if (!TestUtil::compareQImages(errpoint, result, actualResult, 1, 1)) {
qDebug() << "Failed compare result images for: " << f->id();
qDebug() << errpoint;
actualResult.save(QString("carrot_%1.png").arg(f->id()));
result.save(QString("carrot_%1_expected.png").arg(f->id()));
return false;
}
return true;
}
bool testFilterWithSelections(KisFilterSP f)
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "carrot.png");
QImage result(QString(FILES_DATA_DIR) + QDir::separator() + "carrot_" + f->id() + ".png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->setDefaultBounds(new TestUtil::TestingTimedDefaultBounds(qimage.rect()));
dev->convertFromQImage(qimage, 0, 0, 0);
// Get the predefined configuration from a file
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
//qDebug() << "creating new file for " << f->id();
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out.setCodec("UTF-8");
out << kfc->toXML();
} else {
QString s;
QTextStream in(&file);
in.setCodec("UTF-8");
s = in.readAll();
//qDebug() << "Read for " << f->id() << "\n" << s;
kfc->fromXML(s);
}
dbgKrita << f->id();// << "\n"; << kfc->toXML() << "\n";
KisSelectionSP sel1 = new KisSelection(new KisSelectionDefaultBounds(dev));
sel1->pixelSelection()->select(qimage.rect());
f->process(dev, dev, sel1, QRect(QPoint(0,0), qimage.size()), kfc);
QPoint errpoint;
QImage actualResult = dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height());
if (!TestUtil::compareQImages(errpoint, result, actualResult, 1, 1)) {
qDebug() << "Failed compare result images for: " << f->id();
qDebug() << errpoint;
actualResult.save(QString("carrot_%1.png").arg(f->id()));
result.save(QString("carrot_%1_expected.png").arg(f->id()));
return false;
}
return true;
}
void KisAllFilterTest::testAllFilters()
{
QStringList excludeFilters;
excludeFilters << "colortransfer";
excludeFilters << "gradientmap";
excludeFilters << "phongbumpmap";
excludeFilters << "raindrops";
// halftone has some bezier curve painting drifts, so
// let's just exclude it
excludeFilters << "halftone";
QStringList failures;
QStringList successes;
QList<QString> filterList = KisFilterRegistry::instance()->keys();
std::sort(filterList.begin(), filterList.end());
for (QList<QString>::Iterator it = filterList.begin(); it != filterList.end(); ++it) {
if (excludeFilters.contains(*it)) continue;
if (testFilter(KisFilterRegistry::instance()->value(*it)))
successes << *it;
else
failures << *it;
}
dbgKrita << "Success: " << successes;
if (failures.size() > 0) {
QFAIL(QString("Failed filters:\n\t %1").arg(failures.join("\n\t")).toLatin1());
}
}
void KisAllFilterTest::testAllFiltersSrcNotIsDev()
{
QStringList excludeFilters;
excludeFilters << "colortransfer";
excludeFilters << "gradientmap";
excludeFilters << "phongbumpmap";
excludeFilters << "raindrops";
// halftone has some bezier curve painting drifts, so
// let's just exclude it
excludeFilters << "halftone";
QStringList failures;
QStringList successes;
QList<QString> filterList = KisFilterRegistry::instance()->keys();
std::sort(filterList.begin(), filterList.end());
for (QList<QString>::Iterator it = filterList.begin(); it != filterList.end(); ++it) {
if (excludeFilters.contains(*it)) continue;
if (testFilterSrcNotIsDev(KisFilterRegistry::instance()->value(*it)))
successes << *it;
else
failures << *it;
}
dbgKrita << "Src!=Dev Success: " << successes;
if (failures.size() > 0) {
QFAIL(QString("Src!=Dev Failed filters:\n\t %1").arg(failures.join("\n\t")).toLatin1());
}
}
void KisAllFilterTest::testAllFiltersWithSelections()
{
QStringList excludeFilters;
excludeFilters << "colortransfer";
excludeFilters << "gradientmap";
excludeFilters << "phongbumpmap";
excludeFilters << "raindrops";
// halftone has some bezier curve painting drifts, so
// let's just exclude it
excludeFilters << "halftone";
QStringList failures;
QStringList successes;
QList<QString> filterList = KisFilterRegistry::instance()->keys();
std::sort(filterList.begin(), filterList.end());
for (QList<QString>::Iterator it = filterList.begin(); it != filterList.end(); ++it) {
if (excludeFilters.contains(*it)) continue;
if (testFilterWithSelections(KisFilterRegistry::instance()->value(*it)))
successes << *it;
else
failures << *it;
}
dbgKrita << "Success: " << successes;
if (failures.size() > 0) {
QFAIL(QString("Failed filters with selections:\n\t %1").arg(failures.join("\n\t")).toLatin1());
}
}
QTEST_MAIN(KisAllFilterTest)
diff --git a/plugins/filters/tests/kis_crash_filter_test.cpp b/plugins/filters/tests/kis_crash_filter_test.cpp
index 427157f46e..8f22939162 100644
--- a/plugins/filters/tests/kis_crash_filter_test.cpp
+++ b/plugins/filters/tests/kis_crash_filter_test.cpp
@@ -1,118 +1,119 @@
/*
* 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.
*/
#include "kis_crash_filter_test.h"
#include <KoColorProfile.h>
#include <QTest>
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_selection.h"
#include "kis_processing_information.h"
#include "filter/kis_filter.h"
#include "kis_pixel_selection.h"
#include <KoColorSpaceRegistry.h>
#include "kis_transaction.h"
#include <sdk/tests/testing_timed_default_bounds.h>
+#include <KisGlobalResourcesInterface.h>
bool KisCrashFilterTest::applyFilter(const KoColorSpace * cs, KisFilterSP f)
{
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "carrot.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->setDefaultBounds(new TestUtil::TestingTimedDefaultBounds(qimage.rect()));
dev->convertFromQImage(qimage, 0, 0, 0);
// Get the predefined configuration from a file
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
dbgKrita << "creating new file for " << f->id();
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out.setCodec("UTF-8");
out << kfc->toXML();
} else {
QString s;
QTextStream in(&file);
in.setCodec("UTF-8");
s = in.readAll();
kfc->fromXML(s);
}
dbgKrita << f->id() << ", " << cs->id() << ", " << cs->profile()->name();// << kfc->toXML() << "\n";
{
KisTransaction t(kundo2_noi18n(f->name()), dev);
f->process(dev, QRect(QPoint(0,0), qimage.size()), kfc);
}
return true;
}
bool KisCrashFilterTest::testFilter(KisFilterSP f)
{
QList<const KoColorSpace*> colorSpaces = KoColorSpaceRegistry::instance()->allColorSpaces(KoColorSpaceRegistry::AllColorSpaces, KoColorSpaceRegistry::OnlyDefaultProfile);
bool ok = false;
Q_FOREACH (const KoColorSpace* colorSpace, colorSpaces) {
// Alpha color spaces are never processed directly. They are
// first converted into GrayA color space
if (colorSpace->id().startsWith("ALPHA", Qt::CaseInsensitive)) {
continue;
}
ok = applyFilter(colorSpace, f);
}
return ok;
}
void KisCrashFilterTest::testCrashFilters()
{
QStringList excludeFilters;
excludeFilters << "colortransfer";
excludeFilters << "gradientmap";
excludeFilters << "phongbumpmap";
excludeFilters << "perchannel";
excludeFilters << "height to normal";
QStringList failures;
QStringList successes;
QList<QString> filterList = KisFilterRegistry::instance()->keys();
std::sort(filterList.begin(), filterList.end());
for (QList<QString>::Iterator it = filterList.begin(); it != filterList.end(); ++it) {
if (excludeFilters.contains(*it)) continue;
if (testFilter(KisFilterRegistry::instance()->value(*it)))
successes << *it;
else
failures << *it;
}
dbgKrita << "Success: " << successes;
if (failures.size() > 0) {
QFAIL(QString("Failed filters:\n\t %1").arg(failures.join("\n\t")).toLatin1());
}
}
#include <sdk/tests/kistest.h>
KISTEST_MAIN(KisCrashFilterTest)
diff --git a/plugins/filters/threshold/threshold.cpp b/plugins/filters/threshold/threshold.cpp
index 0ae8e1c105..a69f336bb2 100644
--- a/plugins/filters/threshold/threshold.cpp
+++ b/plugins/filters/threshold/threshold.cpp
@@ -1,216 +1,217 @@
/*
* This file is part of the KDE project
*
* 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 "threshold.h"
#include <stdlib.h>
#include <vector>
#include <QPoint>
#include <QTime>
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_registry.h>
#include <kis_global.h>
#include "KisGradientSlider.h"
#include "kis_histogram.h"
#include <kis_layer.h>
#include "kis_paint_device.h"
#include "kis_painter.h"
#include <kis_processing_information.h>
#include <kis_selection.h>
#include <kis_types.h>
#include <KisSequentialIteratorProgress.h>
#include <KoBasicHistogramProducers.h>
#include "KoColorModelStandardIds.h"
#include <KoColorSpace.h>
#include <KoColorTransformation.h>
#include <KoUpdater.h>
+#include <KisGlobalResourcesInterface.h>
K_PLUGIN_FACTORY_WITH_JSON(KritaThresholdFactory, "kritathreshold.json", registerPlugin<KritaThreshold>();)
KritaThreshold::KritaThreshold(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisFilterRegistry::instance()->add(new KisFilterThreshold());
}
KritaThreshold::~KritaThreshold()
{
}
KisFilterThreshold::KisFilterThreshold()
: KisFilter(id(), FiltersCategoryAdjustId, i18n("&Threshold..."))
{
setColorSpaceIndependence(FULLY_INDEPENDENT);
setSupportsPainting(false);
setShowConfigurationWidget(true);
setSupportsLevelOfDetail(true);
setSupportsAdjustmentLayers(true);
setSupportsThreading(true);
}
void KisFilterThreshold::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater *progressUpdater) const
{
Q_ASSERT(!device.isNull());
const int threshold = config->getInt("threshold");
KoColor white(Qt::white, device->colorSpace());
KoColor black(Qt::black, device->colorSpace());
KisSequentialIteratorProgress it(device, applyRect, progressUpdater);
const int pixelSize = device->colorSpace()->pixelSize();
while (it.nextPixel()) {
if (device->colorSpace()->intensity8(it.oldRawData()) > threshold) {
white.setOpacity(device->colorSpace()->opacityU8(it.oldRawData()));
memcpy(it.rawData(), white.data(), pixelSize);
}
else {
black.setOpacity(device->colorSpace()->opacityU8(it.oldRawData()));
memcpy(it.rawData(), black.data(), pixelSize);
}
}
}
-KisFilterConfigurationSP KisFilterThreshold::defaultConfiguration() const
+KisFilterConfigurationSP KisFilterThreshold::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("threshold", 128);
return config;
}
KisConfigWidget *KisFilterThreshold::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool) const
{
return new KisThresholdConfigWidget(parent, dev);
}
KisThresholdConfigWidget::KisThresholdConfigWidget(QWidget * parent, KisPaintDeviceSP dev)
: KisConfigWidget(parent)
{
Q_ASSERT(dev);
m_page.setupUi(this);
m_page.thresholdGradient->enableGamma(false);
m_page.thresholdGradient->enableWhite(false);
m_page.intThreshold->setValue(128);
connect(m_page.intThreshold, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page.thresholdGradient, SIGNAL(sigModifiedGamma(double)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page.intThreshold, SIGNAL(valueChanged(int)), m_page.thresholdGradient, SLOT(slotModifyBlack(int)));
connect(m_page.thresholdGradient, SIGNAL(sigModifiedBlack(int)), m_page.intThreshold, SLOT(setValue(int)));
connect((QObject*)(m_page.chkLogarithmic), SIGNAL(toggled(bool)), this, SLOT(slotDrawHistogram(bool)));
KoHistogramProducer *producer = new KoGenericLabHistogramProducer();
m_histogram.reset( new KisHistogram(dev, dev->exactBounds(), producer, LINEAR) );
m_histlog = false;
m_page.histview->resize(288,100);
slotDrawHistogram();
}
KisThresholdConfigWidget::~KisThresholdConfigWidget()
{
}
void KisThresholdConfigWidget::slotDrawHistogram(bool logarithmic)
{
int wHeight = m_page.histview->height();
int wHeightMinusOne = wHeight - 1;
int wWidth = m_page.histview->width();
if (m_histlog != logarithmic) {
// Update the m_histogram
if (logarithmic)
m_histogram->setHistogramType(LOGARITHMIC);
else
m_histogram->setHistogramType(LINEAR);
m_histlog = logarithmic;
}
QPalette appPalette = QApplication::palette();
QPixmap pix(wWidth-100, wHeight);
pix.fill(QColor(appPalette.color(QPalette::Base)));
QPainter p(&pix);
p.setPen(QPen(Qt::gray, 1, Qt::SolidLine));
double highest = (double)m_histogram->calculations().getHighest();
qint32 bins = m_histogram->producer()->numberOfBins();
// use nearest neighbour interpolation
if (m_histogram->getHistogramType() == LINEAR) {
double factor = (double)(wHeight - wHeight / 5.0) / highest;
for (int i = 0; i < wWidth; i++) {
int binNo = qRound((double)i / wWidth * (bins - 1));
if ((int)m_histogram->getValue(binNo) != 0)
p.drawLine(i, wHeightMinusOne, i, wHeightMinusOne - (int)m_histogram->getValue(binNo) * factor);
}
} else {
double factor = (double)(wHeight - wHeight / 5.0) / (double)log(highest);
for (int i = 0; i < wWidth; i++) {
int binNo = qRound((double)i / wWidth * (bins - 1)) ;
if ((int)m_histogram->getValue(binNo) != 0)
p.drawLine(i, wHeightMinusOne, i, wHeightMinusOne - log((double)m_histogram->getValue(binNo)) * factor);
}
}
m_page.histview->setPixmap(pix);
}
void KisThresholdConfigWidget::slotSetThreshold(int limit)
{
m_page.intThreshold->setMaximum(limit - 1);
}
KisPropertiesConfigurationSP KisThresholdConfigWidget::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("threshold", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("threshold", 1, KisGlobalResourcesInterface::instance());
config->setProperty("threshold", m_page.intThreshold->value());
return config;
}
void KisThresholdConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("threshold", value)) {
m_page.intThreshold->setValue(value.toUInt());
m_page.thresholdGradient->slotModifyBlack(value.toUInt());
}
}
#include "threshold.moc"
diff --git a/plugins/filters/threshold/threshold.h b/plugins/filters/threshold/threshold.h
index f61a44e28b..0aa8be783d 100644
--- a/plugins/filters/threshold/threshold.h
+++ b/plugins/filters/threshold/threshold.h
@@ -1,91 +1,91 @@
/*
* This file is part of Krita
*
* 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 THRESHOLD_H
#define THRESHOLD_H
#include <QObject>
#include <QVariant>
#include <filter/kis_filter.h>
#include <kis_filter_configuration.h>
#include <kis_config_widget.h>
#include "ui_wdg_threshold.h"
class WdgThreshold;
class QWidget;
class KisHistogram;
class KritaThreshold : public QObject
{
Q_OBJECT
public:
KritaThreshold(QObject *parent, const QVariantList &);
~KritaThreshold() override;
};
class KisFilterThreshold : public KisFilter
{
public:
KisFilterThreshold();
public:
static inline KoID id() {
return KoID("threshold", i18n("Threshold"));
}
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater *progressUpdater) const override;
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
KisConfigWidget *createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
};
class KisThresholdConfigWidget : public KisConfigWidget
{
Q_OBJECT
public:
KisThresholdConfigWidget(QWidget *parent, KisPaintDeviceSP dev);
~KisThresholdConfigWidget() override;
KisPropertiesConfigurationSP configuration() const override;
void setConfiguration(const KisPropertiesConfigurationSP config) override;
Ui::WdgThreshold m_page;
private Q_SLOTS:
void slotDrawHistogram(bool logarithmic = false);
void slotSetThreshold(int);
protected:
QScopedPointer<KisHistogram> m_histogram;
bool m_histlog;
};
#endif
diff --git a/plugins/filters/unsharp/kis_unsharp_filter.cpp b/plugins/filters/unsharp/kis_unsharp_filter.cpp
index cb12116791..146d5611d7 100644
--- a/plugins/filters/unsharp/kis_unsharp_filter.cpp
+++ b/plugins/filters/unsharp/kis_unsharp_filter.cpp
@@ -1,229 +1,229 @@
/*
* 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 <compositeops/KoVcMultiArchBuildSupport.h> //MSVC requires that Vc come first
#include "kis_unsharp_filter.h"
#include <QBitArray>
#include <kis_mask_generator.h>
#include <kis_convolution_kernel.h>
#include <kis_convolution_painter.h>
#include <kis_gaussian_kernel.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include <KoProgressUpdater.h>
#include <KoUpdater.h>
#include <KoConvolutionOp.h>
#include <kis_paint_device.h>
#include "kis_lod_transform.h"
#include "kis_wdg_unsharp.h"
#include "ui_wdgunsharp.h"
#include "KoColorSpaceTraits.h"
#include <KisSequentialIteratorProgress.h>
KisUnsharpFilter::KisUnsharpFilter() : KisFilter(id(), FiltersCategoryEnhanceId, i18n("&Unsharp Mask..."))
{
setSupportsPainting(true);
setSupportsAdjustmentLayers(true);
setSupportsThreading(true);
/**
* Officially Unsharp Mask doesn't support LoD, because it
* generates subtle artifacts when the unsharp radius is smaller
* than current zoom level. But LoD devices can still appear when
* the filter is used in Adjustment Layer. So the actual LoD is
* still counted on.
*/
setSupportsLevelOfDetail(false);
setColorSpaceIndependence(FULLY_INDEPENDENT);
}
KisConfigWidget * KisUnsharpFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool) const
{
return new KisWdgUnsharp(parent);
}
-KisFilterConfigurationSP KisUnsharpFilter::defaultConfiguration() const
+KisFilterConfigurationSP KisUnsharpFilter::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("halfSize", 1);
config->setProperty("amount", 0.5);
config->setProperty("threshold", 0);
config->setProperty("lightnessOnly", true);
return config;
}
void KisUnsharpFilter::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
- const KisFilterConfigurationSP _config,
+ const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
QPointer<KoUpdater> filterUpdater = 0;
QPointer<KoUpdater> convolutionUpdater = 0;
QScopedPointer<KoProgressUpdater> updater;
if (progressUpdater) {
updater.reset(new KoProgressUpdater(progressUpdater));
updater->start(100, i18n("Unsharp Mask"));
// Two sub-sub tasks that each go from 0 to 100.
convolutionUpdater = updater->startSubtask();
filterUpdater = updater->startSubtask();
}
- KisFilterConfigurationSP config = _config ? _config : new KisFilterConfiguration(id().id(), 1);
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
QVariant value;
KisLodTransformScalar t(device);
const qreal halfSize = t.scale(config->getProperty("halfSize", value) ? value.toDouble() : 1.0);
const qreal amount = (config->getProperty("amount", value)) ? value.toDouble() : 0.5;
const uint threshold = (config->getProperty("threshold", value)) ? value.toUInt() : 0;
const uint lightnessOnly = (config->getProperty("lightnessOnly", value)) ? value.toBool() : true;
QBitArray channelFlags = config->channelFlags();
KisGaussianKernel::applyGaussian(device, applyRect,
halfSize, halfSize,
channelFlags,
convolutionUpdater);
qreal weights[2];
qreal factor = 128;
weights[0] = factor * (1. + amount);
weights[1] = -factor * amount;
if (lightnessOnly) {
processLightnessOnly(device, applyRect, threshold, weights, factor, channelFlags, filterUpdater);
} else {
processRaw(device, applyRect, threshold, weights, factor, channelFlags, filterUpdater);
}
}
void KisUnsharpFilter::processRaw(KisPaintDeviceSP device,
const QRect &rect,
quint8 threshold,
qreal weights[2],
qreal factor,
const QBitArray &channelFlags,
KoUpdater *progressUpdater) const
{
const KoColorSpace *cs = device->colorSpace();
const int pixelSize = cs->pixelSize();
KoConvolutionOp * convolutionOp = cs->convolutionOp();
quint8 *colors[2];
colors[0] = new quint8[pixelSize];
colors[1] = new quint8[pixelSize];
KisSequentialIteratorProgress dstIt(device, rect, progressUpdater);
while (dstIt.nextPixel()) {
quint8 diff = 0;
if (threshold == 1) {
if (memcmp(dstIt.oldRawData(), dstIt.rawDataConst(), cs->pixelSize()) == 0) {
diff = 1;
}
}
else {
diff = cs->difference(dstIt.oldRawData(), dstIt.rawDataConst());
}
if (diff >= threshold) {
memcpy(colors[0], dstIt.oldRawData(), pixelSize);
memcpy(colors[1], dstIt.rawDataConst(), pixelSize);
convolutionOp->convolveColors(colors, weights, dstIt.rawData(), factor, 0, 2, channelFlags);
} else {
memcpy(dstIt.rawData(), dstIt.oldRawData(), pixelSize);
}
}
delete[] colors[0];
delete[] colors[1];
}
void KisUnsharpFilter::processLightnessOnly(KisPaintDeviceSP device,
const QRect &rect,
quint8 threshold,
qreal weights[2],
qreal factor,
const QBitArray & /*channelFlags*/,
KoUpdater *progressUpdater) const
{
const KoColorSpace *cs = device->colorSpace();
const int pixelSize = cs->pixelSize();
quint16 labColorSrc[4];
quint16 labColorDst[4];
const int posL = 0;
const int posAplha = 3;
const qreal factorInv = 1.0 / factor;
KisSequentialIteratorProgress dstIt(device, rect, progressUpdater);
while (dstIt.nextPixel()) {
quint8 diff = cs->differenceA(dstIt.oldRawData(), dstIt.rawDataConst());
if (diff >= threshold) {
cs->toLabA16(dstIt.oldRawData(), (quint8*)labColorSrc, 1);
cs->toLabA16(dstIt.rawDataConst(), (quint8*)labColorDst, 1);
qint32 valueL = (labColorSrc[posL] * weights[0] + labColorDst[posL] * weights[1]) * factorInv;
labColorSrc[posL] = CLAMP(valueL,
KoColorSpaceMathsTraits<quint16>::min,
KoColorSpaceMathsTraits<quint16>::max);
qint32 valueAlpha = (labColorSrc[posAplha] * weights[0] + labColorDst[posAplha] * weights[1]) * factorInv;
labColorSrc[posAplha] = CLAMP(valueAlpha,
KoColorSpaceMathsTraits<quint16>::min,
KoColorSpaceMathsTraits<quint16>::max);
cs->fromLabA16((quint8*)labColorSrc, dstIt.rawData(), 1);
} else {
memcpy(dstIt.rawData(), dstIt.oldRawData(), pixelSize);
}
}
}
QRect KisUnsharpFilter::neededRect(const QRect & rect, const KisFilterConfigurationSP config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
const qreal halfSize = t.scale(config->getProperty("halfSize", value) ? value.toDouble() : 1.0);
return rect.adjusted(-halfSize * 2, -halfSize * 2, halfSize * 2, halfSize * 2);
}
QRect KisUnsharpFilter::changedRect(const QRect & rect, const KisFilterConfigurationSP config, int lod) const
{
KisLodTransformScalar t(lod);
QVariant value;
const qreal halfSize = t.scale(config->getProperty("halfSize", value) ? value.toDouble() : 1.0);
return rect.adjusted( -halfSize, -halfSize, halfSize, halfSize);
}
diff --git a/plugins/filters/unsharp/kis_unsharp_filter.h b/plugins/filters/unsharp/kis_unsharp_filter.h
index 6cc243e6a4..3d74941fa7 100644
--- a/plugins/filters/unsharp/kis_unsharp_filter.h
+++ b/plugins/filters/unsharp/kis_unsharp_filter.h
@@ -1,64 +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.
*/
#ifndef KIS_UNSHARP_FILTER_H
#define KIS_UNSHARP_FILTER_H
#include "filter/kis_filter.h"
class KisUnsharpFilter : public KisFilter
{
public:
KisUnsharpFilter();
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("unsharp", i18n("Unsharp Mask"));
}
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
QRect changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
QRect neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override;
private:
void processLightnessOnly(KisPaintDeviceSP device,
const QRect &rect,
quint8 threshold,
qreal weights[2],
qreal factor,
const QBitArray &channelFlags, KoUpdater *progressUpdater) const;
void processRaw(KisPaintDeviceSP device,
const QRect &rect,
quint8 threshold,
qreal weights[2],
qreal factor,
const QBitArray &channelFlags, KoUpdater *progressUpdater) const;
};
#endif
diff --git a/plugins/filters/unsharp/kis_wdg_unsharp.cpp b/plugins/filters/unsharp/kis_wdg_unsharp.cpp
index b6588d789b..0271b632a7 100644
--- a/plugins/filters/unsharp/kis_wdg_unsharp.cpp
+++ b/plugins/filters/unsharp/kis_wdg_unsharp.cpp
@@ -1,66 +1,67 @@
/*
* 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_unsharp.h"
#include <QLayout>
#include <QToolButton>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
+#include <KisGlobalResourcesInterface.h>
#include "ui_wdgunsharp.h"
KisWdgUnsharp::KisWdgUnsharp(QWidget * parent) : KisConfigWidget(parent)
{
m_widget = new Ui_WdgUnsharp();
m_widget->setupUi(this);
connect(widget()->doubleHalfSize, SIGNAL(valueChanged(double)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->doubleAmount, SIGNAL(valueChanged(double)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->intThreshold, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->chkLightnessOnly, SIGNAL(stateChanged(int)), SIGNAL(sigConfigurationItemChanged()));
}
KisWdgUnsharp::~KisWdgUnsharp()
{
delete m_widget;
}
void KisWdgUnsharp::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
widget()->doubleHalfSize->setValue((config->getProperty("halfSize", value)) ? value.toDouble() : 1.0);
widget()->doubleAmount->setValue((config->getProperty("amount", value)) ? value.toDouble() : 0.0);
widget()->intThreshold->setValue((config->getProperty("threshold", value)) ? value.toUInt() : 2);
widget()->chkLightnessOnly->setChecked((config->getProperty("lightnessOnly", value)) ? value.toBool() : true);
}
KisPropertiesConfigurationSP KisWdgUnsharp::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("unsharp", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("unsharp", 1, KisGlobalResourcesInterface::instance());
config->setProperty("halfSize", widget()->doubleHalfSize->value());
config->setProperty("amount", widget()->doubleAmount->value());
config->setProperty("threshold", widget()->intThreshold->value());
config->setProperty("lightnessOnly", widget()->chkLightnessOnly->isChecked());
return config;
}
diff --git a/plugins/filters/unsharp/tests/kis_unsharp_mask_test.cpp b/plugins/filters/unsharp/tests/kis_unsharp_mask_test.cpp
index 826da4297f..f19815967c 100644
--- a/plugins/filters/unsharp/tests/kis_unsharp_mask_test.cpp
+++ b/plugins/filters/unsharp/tests/kis_unsharp_mask_test.cpp
@@ -1,67 +1,67 @@
/*
* 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_unsharp_mask_test.h"
#include <QTest>
#include "kis_transaction.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "testutil.h"
-
+#include <KisGlobalResourcesInterface.h>
void KisUnsharpMaskTest::testUnsharpWithTransparency()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage srcImage(TestUtil::fetchDataFileLazy("source_with_transparency.png"));
QVERIFY(!srcImage.isNull());
QRect imageRect = srcImage.rect();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(srcImage, 0, 0, 0);
KisFilterSP f = KisFilterRegistry::instance()->value("unsharp");
Q_ASSERT(f);
- KisFilterConfigurationSP kfc = f->defaultConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(kfc);
kfc->setProperty("halfSize", 3);
kfc->setProperty("amount", 2.5);
kfc->setProperty("threshold", 0);
KisTransaction t(dev);
f->process(dev, QRect(imageRect.topLeft(), imageRect.size()), kfc);
t.end();
QImage resultImage =
dev->convertToQImage(0,
imageRect.x(), imageRect.y(),
imageRect.width(), imageRect.height());
TestUtil::checkQImage(resultImage,
"unsharp_mask_test",
"with_transparency",
"unsharp_with_transparency");
}
QTEST_MAIN(KisUnsharpMaskTest)
diff --git a/plugins/filters/wavefilter/kis_wdg_wave.cpp b/plugins/filters/wavefilter/kis_wdg_wave.cpp
index 25a1a7e40f..29d0042167 100644
--- a/plugins/filters/wavefilter/kis_wdg_wave.cpp
+++ b/plugins/filters/wavefilter/kis_wdg_wave.cpp
@@ -1,93 +1,94 @@
/*
* 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_wave.h"
#include <QSpinBox>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
+#include <KisGlobalResourcesInterface.h>
#include "ui_wdgwaveoptions.h"
KisWdgWave::KisWdgWave(KisFilter* /*nfilter*/, QWidget* parent)
: KisConfigWidget(parent)
{
m_widget = new Ui_WdgWaveOptions();
m_widget->setupUi(this);
connect(widget()->intHWavelength, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->intHShift, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->intHAmplitude, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->cbHShape, SIGNAL(activated(int)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->intVWavelength, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->intVShift, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->intVAmplitude, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(widget()->cbVShape, SIGNAL(activated(int)), SIGNAL(sigConfigurationItemChanged()));
}
KisWdgWave::~KisWdgWave()
{
delete m_widget;
}
void KisWdgWave::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if (config->getProperty("horizontalwavelength", value)) {
widget()->intHWavelength->setValue(value.toUInt());
}
if (config->getProperty("horizontalshift", value)) {
widget()->intHShift->setValue(value.toUInt());
}
if (config->getProperty("horizontalamplitude", value)) {
widget()->intHAmplitude->setValue(value.toUInt());
}
if (config->getProperty("horizontalshape", value)) {
widget()->cbHShape->setCurrentIndex(value.toUInt());
}
if (config->getProperty("verticalwavelength", value)) {
widget()->intVWavelength->setValue(value.toUInt());
}
if (config->getProperty("verticalshift", value)) {
widget()->intVShift->setValue(value.toUInt());
}
if (config->getProperty("verticalamplitude", value)) {
widget()->intVAmplitude->setValue(value.toUInt());
}
if (config->getProperty("verticalshape", value)) {
widget()->cbVShape->setCurrentIndex(value.toUInt());
}
}
KisPropertiesConfigurationSP KisWdgWave::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("wave", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("wave", 1, KisGlobalResourcesInterface::instance());
config->setProperty("horizontalwavelength", this->widget()->intHWavelength->value());
config->setProperty("horizontalshift", this->widget()->intHShift->value());
config->setProperty("horizontalamplitude", this->widget()->intHAmplitude->value());
config->setProperty("horizontalshape", this->widget()->cbHShape->currentIndex());
config->setProperty("verticalwavelength", this->widget()->intVWavelength->value());
config->setProperty("verticalshift", this->widget()->intVShift->value());
config->setProperty("verticalamplitude", this->widget()->intVAmplitude->value());
config->setProperty("verticalshape", this->widget()->cbVShape->currentIndex());
return config;
}
diff --git a/plugins/filters/wavefilter/wavefilter.cpp b/plugins/filters/wavefilter/wavefilter.cpp
index 0b9a570d10..6b540dfe90 100644
--- a/plugins/filters/wavefilter/wavefilter.cpp
+++ b/plugins/filters/wavefilter/wavefilter.cpp
@@ -1,177 +1,177 @@
/*
* 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.
*/
#include "wavefilter.h"
#include <stdlib.h>
#include <vector>
#include <math.h>
#include <QPoint>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <klocalizedstring.h>
#include <KoUpdater.h>
#include <kis_image.h>
#include <filter/kis_filter_registry.h>
#include <kis_global.h>
#include <kis_layer.h>
#include <kis_random_sub_accessor.h>
#include <kis_selection.h>
#include <kis_types.h>
#include <kis_paint_device.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include "kis_wdg_wave.h"
#include "ui_wdgwaveoptions.h"
#include <kis_iterator_ng.h>
#include <KisSequentialIteratorProgress.h>
K_PLUGIN_FACTORY_WITH_JSON(KritaWaveFilterFactory, "kritawavefilter.json", registerPlugin<KritaWaveFilter>();)
class KisWaveCurve
{
public:
virtual ~KisWaveCurve() {}
virtual double valueAt(int x, int y) = 0;
};
class KisSinusoidalWaveCurve : public KisWaveCurve
{
public:
KisSinusoidalWaveCurve(int amplitude, int wavelength, int shift) : m_amplitude(amplitude), m_wavelength(wavelength), m_shift(shift) {
}
~KisSinusoidalWaveCurve() override {}
double valueAt(int x, int y) override {
return y + m_amplitude * cos((double)(m_shift + x) / m_wavelength);
}
private:
int m_amplitude, m_wavelength, m_shift;
};
class KisTriangleWaveCurve : public KisWaveCurve
{
public:
KisTriangleWaveCurve(int amplitude, int wavelength, int shift) : m_amplitude(amplitude), m_wavelength(wavelength), m_shift(shift) {
}
~KisTriangleWaveCurve() override {}
double valueAt(int x, int y) override {
return y + m_amplitude * pow(-1.0, (m_shift + x) / m_wavelength) *(0.5 - (double)((m_shift + x) % m_wavelength) / m_wavelength);
}
private:
int m_amplitude, m_wavelength, m_shift;
}; KritaWaveFilter::KritaWaveFilter(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisFilterRegistry::instance()->add(new KisFilterWave());
}
KritaWaveFilter::~KritaWaveFilter()
{
}
KisFilterWave::KisFilterWave() : KisFilter(id(), FiltersCategoryOtherId, i18n("&Wave..."))
{
setColorSpaceIndependence(FULLY_INDEPENDENT);
setSupportsPainting(false);
setSupportsAdjustmentLayers(false);
}
-KisFilterConfigurationSP KisFilterWave::defaultConfiguration() const
+KisFilterConfigurationSP KisFilterWave::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("horizontalwavelength", 50);
config->setProperty("horizontalshift", 50);
config->setProperty("horizontalamplitude", 4);
config->setProperty("horizontalshape", 0);
config->setProperty("verticalwavelength", 50);
config->setProperty("verticalshift", 50);
config->setProperty("verticalamplitude", 4);
config->setProperty("verticalshape", 0);
return config;
}
KisConfigWidget * KisFilterWave::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP, bool) const
{
return new KisWdgWave((KisFilter*)this, (QWidget*)parent);
}
void KisFilterWave::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const
{
Q_ASSERT(device.data() != 0);
QVariant value;
int horizontalwavelength = (config && config->getProperty("horizontalwavelength", value)) ? value.toInt() : 50;
int horizontalshift = (config && config->getProperty("horizontalshift", value)) ? value.toInt() : 50;
int horizontalamplitude = (config && config->getProperty("horizontalamplitude", value)) ? value.toInt() : 4;
int horizontalshape = (config && config->getProperty("horizontalshape", value)) ? value.toInt() : 0;
int verticalwavelength = (config && config->getProperty("verticalwavelength", value)) ? value.toInt() : 50;
int verticalshift = (config && config->getProperty("verticalshift", value)) ? value.toInt() : 50;
int verticalamplitude = (config && config->getProperty("verticalamplitude", value)) ? value.toInt() : 4;
int verticalshape = (config && config->getProperty("verticalshape", value)) ? value.toInt() : 0;
KisWaveCurve* verticalcurve;
if (verticalshape == 1)
verticalcurve = new KisTriangleWaveCurve(verticalamplitude, verticalwavelength, verticalshift);
else
verticalcurve = new KisSinusoidalWaveCurve(verticalamplitude, verticalwavelength, verticalshift);
KisWaveCurve* horizontalcurve;
if (horizontalshape == 1)
horizontalcurve = new KisTriangleWaveCurve(horizontalamplitude, horizontalwavelength, horizontalshift);
else
horizontalcurve = new KisSinusoidalWaveCurve(horizontalamplitude, horizontalwavelength, horizontalshift);
KisSequentialIteratorProgress dstIt(device, applyRect, progressUpdater);
KisRandomSubAccessorSP srcRSA = device->createRandomSubAccessor();
while (dstIt.nextPixel()) {
double xv = horizontalcurve->valueAt(dstIt.y(), dstIt.x());
double yv = verticalcurve->valueAt(dstIt.x(), dstIt.y());
srcRSA->moveTo(QPointF(xv, yv));
srcRSA->sampledOldRawData(dstIt.rawData());
}
delete horizontalcurve;
delete verticalcurve;
}
QRect KisFilterWave::neededRect(const QRect& rect, const KisFilterConfigurationSP config, int lod) const
{
Q_UNUSED(lod);
QVariant value;
int horizontalamplitude = (config && config->getProperty("horizontalamplitude", value)) ? value.toInt() : 4;
int verticalamplitude = (config && config->getProperty("verticalamplitude", value)) ? value.toInt() : 4;
return rect.adjusted(-horizontalamplitude, -verticalamplitude, horizontalamplitude, verticalamplitude);
}
#include "wavefilter.moc"
diff --git a/plugins/filters/wavefilter/wavefilter.h b/plugins/filters/wavefilter/wavefilter.h
index 9b7ecda564..daf9b1d31b 100644
--- a/plugins/filters/wavefilter/wavefilter.h
+++ b/plugins/filters/wavefilter/wavefilter.h
@@ -1,61 +1,61 @@
/*
* 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.
*/
#ifndef WAVEFILTER_H
#define WAVEFILTER_H
#include <QObject>
#include <QVariant>
#include "filter/kis_filter.h"
class KisConfigWidget;
class KritaWaveFilter : public QObject
{
Q_OBJECT
public:
KritaWaveFilter(QObject *parent, const QVariantList &);
~KritaWaveFilter() override;
};
class KisFilterWave : public KisFilter
{
public:
KisFilterWave();
public:
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater) const override;
static inline KoID id() {
return KoID("wave", i18n("Wave"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
public:
QRect neededRect(const QRect& rect, const KisFilterConfigurationSP config = 0, int lod = 0) const override;
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
};
#endif
diff --git a/plugins/generators/pattern/kis_wdg_pattern.cpp b/plugins/generators/pattern/kis_wdg_pattern.cpp
index 9397f9ca22..3e5e70164c 100644
--- a/plugins/generators/pattern/kis_wdg_pattern.cpp
+++ b/plugins/generators/pattern/kis_wdg_pattern.cpp
@@ -1,71 +1,70 @@
/*
* 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 <KisGlobalResourcesInterface.h>
+#include <kis_generator_registry.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()));
+ connect(m_widget->patternChooser, SIGNAL(resourceSelected(KoResourceSP)), 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);
- }
-
+ auto source = KisGlobalResourcesInterface::instance()->source<KoPattern>(ResourceType::Patterns);
+ KoPatternSP pattern = source.resourceForName(config->getString("pattern", "Grid01.pat"));
+ widget()->patternChooser->setCurrentPattern(pattern ? pattern : source.fallbackResource());
}
KisPropertiesConfigurationSP KisWdgPattern::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("pattern", 1);
+ KisGeneratorSP generator = KisGeneratorRegistry::instance()->get("pattern");
+ KisFilterConfigurationSP config = generator->factoryConfiguration(KisGlobalResourcesInterface::instance());
+
QVariant v;
v.setValue(widget()->patternChooser->currentResource()->name());
config->setProperty("pattern", v);
return config;
}
diff --git a/plugins/generators/pattern/patterngenerator.cpp b/plugins/generators/pattern/patterngenerator.cpp
index 60678895a5..a29c0f5c42 100644
--- a/plugins/generators/pattern/patterngenerator.cpp
+++ b/plugins/generators/pattern/patterngenerator.cpp
@@ -1,121 +1,170 @@
/*
* This file is part of the KDE project
*
* 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.
*/
#include "patterngenerator.h"
#include <QPoint>
#include <kpluginfactory.h>
#include <klocalizedstring.h>
#include <KoColor.h>
-#include <KoResourceServer.h>
+#include <KisResourceTypes.h>
#include <resources/KoPattern.h>
-#include <KoResourceServerProvider.h>
#include <kis_debug.h>
#include <kis_fill_painter.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_layer.h>
#include <generator/kis_generator_registry.h>
#include <kis_global.h>
#include <kis_selection.h>
#include <kis_types.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include <kis_pattern_chooser.h>
+#include <KisResourcesInterface.h>
#include "kis_wdg_pattern.h"
#include "ui_wdgpatternoptions.h"
K_PLUGIN_FACTORY_WITH_JSON(KritaPatternGeneratorFactory, "kritapatterngenerator.json", registerPlugin<KritaPatternGenerator>();)
KritaPatternGenerator::KritaPatternGenerator(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisGeneratorRegistry::instance()->add(new KoPatternGenerator());
}
KritaPatternGenerator::~KritaPatternGenerator()
{
}
-KoPatternGenerator::KoPatternGenerator() : KisGenerator(id(), KoID("basic"), i18n("&Pattern..."))
+/****************************************************************************/
+/* KoPatternGeneratorConfiguration */
+/****************************************************************************/
+
+class KoPatternGeneratorConfiguration : public KisFilterConfiguration
+{
+public:
+ KoPatternGeneratorConfiguration(const QString & name, qint32 version, KisResourcesInterfaceSP resourcesInterface)
+ : KisFilterConfiguration(name, version, resourcesInterface)
+ {
+ }
+
+ KoPatternGeneratorConfiguration(const KoPatternGeneratorConfiguration &rhs)
+ : KisFilterConfiguration(rhs)
+ {
+ }
+
+ virtual KisFilterConfigurationSP clone() const override {
+ return new KoPatternGeneratorConfiguration(*this);
+ }
+
+ KoPatternSP pattern(KisResourcesInterfaceSP resourcesInterface) const {
+ const QString patternName = this->getString("pattern", "Grid01.pat");
+ auto source = resourcesInterface->source<KoPattern>(ResourceType::Patterns);
+ return source.resourceForName(patternName);
+ }
+
+ KoPatternSP pattern() const {
+ return pattern(resourcesInterface());
+ }
+
+ QList<KoResourceSP> linkedResources(KisResourcesInterfaceSP globalResourcesInterface) const override
+ {
+ KoPatternSP pattern = this->pattern(globalResourcesInterface);
+
+ QList<KoResourceSP> resources;
+ if (pattern) {
+ resources << pattern;
+ }
+
+ return resources;
+ }
+};
+
+
+
+/****************************************************************************/
+/* KoPatternGenerator */
+/****************************************************************************/
+
+KoPatternGenerator::KoPatternGenerator()
+ : KisGenerator(id(), KoID("basic"), i18n("&Pattern..."))
{
setColorSpaceIndependence(FULLY_INDEPENDENT);
setSupportsPainting(true);
}
-KisFilterConfigurationSP KoPatternGenerator::defaultConfiguration() const
+KisFilterConfigurationSP KoPatternGenerator::factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ return new KoPatternGeneratorConfiguration(id().id(), 1, resourcesInterface);
+}
- QVariant v;
- v.setValue(QString("Grid01.pat"));
- config->setProperty("pattern", v);
+KisFilterConfigurationSP KoPatternGenerator::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
+{
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
-// v.setValue(KoColor());
-// config->setProperty("color", v);
+ auto source = resourcesInterface->source<KoPattern>(ResourceType::Patterns);
+ config->setProperty("pattern", QVariant::fromValue(source.fallbackResource()->name()));
return config;
}
KisConfigWidget * KoPatternGenerator::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
return new KisWdgPattern(parent);
}
void KoPatternGenerator::generate(KisProcessingInformation dstInfo,
const QSize& size,
- const KisFilterConfigurationSP config,
+ const KisFilterConfigurationSP _config,
KoUpdater* progressUpdater) const
{
KisPaintDeviceSP dst = dstInfo.paintDevice();
Q_ASSERT(!dst.isNull());
- Q_ASSERT(config);
- if (!config) return;
- QString patternName = config->getString("pattern", "Grid01.pat");
- KoResourceServer<KoPattern> *rserver = KoResourceServerProvider::instance()->patternServer();
- KoPattern *pattern = rserver->resourceByName(patternName);
+ const KoPatternGeneratorConfiguration *config =
+ dynamic_cast<const KoPatternGeneratorConfiguration*>(_config.data());
-// KoColor c = config->getColor("color");
+ KIS_SAFE_ASSERT_RECOVER_RETURN(config);
+ KoPatternSP pattern = config->pattern();
KisFillPainter gc(dst);
gc.setPattern(pattern);
-// gc.setPaintColor(c);
gc.setProgress(progressUpdater);
gc.setChannelFlags(config->channelFlags());
gc.setOpacity(OPACITY_OPAQUE_U8);
gc.setSelection(dstInfo.selection());
gc.setWidth(size.width());
gc.setHeight(size.height());
gc.setFillStyle(KisFillPainter::FillStylePattern);
gc.fillRect(QRect(dstInfo.topLeft(), size), pattern);
gc.end();
}
#include "patterngenerator.moc"
diff --git a/plugins/generators/pattern/patterngenerator.h b/plugins/generators/pattern/patterngenerator.h
index fc5ac5e8c2..277717e66a 100644
--- a/plugins/generators/pattern/patterngenerator.h
+++ b/plugins/generators/pattern/patterngenerator.h
@@ -1,59 +1,61 @@
/*
* 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.
*/
#ifndef PATTERN_GENERATOR_H
#define PATTERN_GENERATOR_H
#include <QObject>
#include <QVariant>
#include "generator/kis_generator.h"
class KisConfigWidget;
class KritaPatternGenerator : public QObject
{
Q_OBJECT
public:
KritaPatternGenerator(QObject *parent, const QVariantList &);
~KritaPatternGenerator() override;
};
class KoPatternGenerator : public KisGenerator
{
public:
KoPatternGenerator();
using KisGenerator::generate;
void generate(KisProcessingInformation dst,
const QSize& size,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("pattern", i18n("Pattern"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+
+ KisFilterConfigurationSP factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
};
#endif
diff --git a/plugins/generators/simplexnoise/kis_wdg_simplex_noise.cpp b/plugins/generators/simplexnoise/kis_wdg_simplex_noise.cpp
index b7b47b63de..29efe8c174 100644
--- a/plugins/generators/simplexnoise/kis_wdg_simplex_noise.cpp
+++ b/plugins/generators/simplexnoise/kis_wdg_simplex_noise.cpp
@@ -1,90 +1,91 @@
/*
* KDE. Krita Project.
*
* Copyright (c) 2019 Eoin O'Neill <eoinoneill1991@gmail.com>
* Copyright (c) 2019 Emmet O'Neill <emmetoneill.pdx@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_wdg_simplex_noise.h"
#include "ui_wdgsimplexnoiseoptions.h"
#include <QLayout>
#include <filter/kis_filter_configuration.h>
+#include <KisGlobalResourcesInterface.h>
KisWdgSimplexNoise::KisWdgSimplexNoise(KisFilter* /*nfilter*/, QWidget* parent)
: KisConfigWidget(parent),
updateCompressor(250, KisSignalCompressor::Mode::POSTPONE)
{
m_widget = new Ui_WdgSimplexNoiseOptions();
m_widget->setupUi(this);
connect(m_widget->slider_frequency, SIGNAL(valueChanged(qreal)), &updateCompressor, SLOT(start()));
connect(m_widget->cb_looping, SIGNAL(stateChanged(int)), &updateCompressor, SLOT(start()));
connect(m_widget->seed_text, SIGNAL(textChanged(QString)), &updateCompressor, SLOT(start()));
connect(m_widget->ratiox_slider, SIGNAL(valueChanged(qreal)), &updateCompressor, SLOT(start()));
connect(m_widget->ratioy_slider, SIGNAL(valueChanged(qreal)), &updateCompressor, SLOT(start()));
connect(&updateCompressor, SIGNAL(timeout()), this, SIGNAL(sigConfigurationItemChanged()));
m_widget->slider_frequency->setRange(1.0f, 500.0f, 2);
m_widget->slider_frequency->setValue(25.0f);
m_widget->slider_frequency->setExponentRatio(3.0);
m_widget->ratiox_slider->setRange(0.0f, 2.0f, 2);
m_widget->ratiox_slider->setValue(1.0f);
m_widget->ratioy_slider->setRange(0.0f, 2.0f, 2);
m_widget->ratioy_slider->setValue(1.0f);
}
KisWdgSimplexNoise::~KisWdgSimplexNoise()
{
delete m_widget;
}
void KisWdgSimplexNoise::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
if( config->getProperty("looping", value)) {
Qt::CheckState state = value.toBool() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked;
widget()->cb_looping->setCheckState(state);
}
if( config->getProperty("frequency", value)) {
widget()->slider_frequency->setValue(value.toDouble());
}
if( config->getProperty("custom_seed_string", value)) {
m_widget->seed_text->setText(value.toString());
}
if( config->getProperty("ratio_x", value)) {
m_widget->ratiox_slider->setValue(value.toDouble());
}
if( config->getProperty("ratio_y", value)) {
m_widget->ratioy_slider->setValue(value.toDouble());
}
if( config->getProperty("seed", value)) {
this->seed = value.toUInt();
}
}
KisPropertiesConfigurationSP KisWdgSimplexNoise::configuration() const
{
- KisFilterConfigurationSP config = new KisFilterConfiguration("simplex_noise", 1);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("simplex_noise", 1, KisGlobalResourcesInterface::instance());
config->setProperty("looping", m_widget->cb_looping->isChecked());
config->setProperty("frequency", m_widget->slider_frequency->value());
config->setProperty("ratio_x", m_widget->ratiox_slider->value());
config->setProperty("ratio_y", m_widget->ratioy_slider->value());
config->setProperty("custom_seed_string", m_widget->seed_text->text());
config->setProperty("seed", this->seed);
return config;
}
diff --git a/plugins/generators/simplexnoise/simplexnoisegenerator.cpp b/plugins/generators/simplexnoise/simplexnoisegenerator.cpp
index 58a452489a..6254db7723 100644
--- a/plugins/generators/simplexnoise/simplexnoisegenerator.cpp
+++ b/plugins/generators/simplexnoise/simplexnoisegenerator.cpp
@@ -1,149 +1,149 @@
/*
* KDE. Krita Project.
*
* Copyright (c) 2019 Eoin O'Neill <eoinoneill1991@gmail.com>
* Copyright (c) 2019 Emmet O'Neill <emmetoneill.pdx@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 "simplexnoisegenerator.h"
#include "ui_wdgsimplexnoiseoptions.h"
#include "kis_wdg_simplex_noise.h"
#include "3rdparty/c-open-simplex/open-simplex-noise.h"
#include <QCryptographicHash>
#include <kpluginfactory.h>
#include <KoUpdater.h>
#include <kis_processing_information.h>
#include <KisSequentialIteratorProgress.h>
#include <filter/kis_filter_configuration.h>
#include <generator/kis_generator_registry.h>
K_PLUGIN_FACTORY_WITH_JSON(KritaSimplexNoiseGeneratorFactory, "kritasimplexnoisegenerator.json", registerPlugin<KisSimplexNoiseGeneratorHandle>();)
KisSimplexNoiseGeneratorHandle::KisSimplexNoiseGeneratorHandle(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisGeneratorRegistry::instance()->add(new KisSimplexNoiseGenerator());
}
KisSimplexNoiseGeneratorHandle::~KisSimplexNoiseGeneratorHandle()
{
}
KisSimplexNoiseGenerator::KisSimplexNoiseGenerator() : KisGenerator(id(), KoID("basic"), i18n("&Simplex Noise..."))
{
setColorSpaceIndependence(FULLY_INDEPENDENT);
setSupportsPainting(true);
}
void KisSimplexNoiseGenerator::generate(KisProcessingInformation dst, const QSize &size, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const
{
KisPaintDeviceSP device = dst.paintDevice();
Q_ASSERT(!device.isNull());
osn_context *noise_context;
QRect bounds = QRect(dst.topLeft(), size);
const KoColorSpace * cs = device->colorSpace();
KisSequentialIteratorProgress it(device, bounds, progressUpdater);
QVariant property;
const uint default_seed = (config->getProperty("seed", property)) ? property.toUInt() : 0;
const QString custom_seed_string = (config->getProperty("custom_seed_string", property)) ? property.toString() : "";
const bool use_custom_seed = !custom_seed_string.trimmed().isEmpty();
const uint seed = use_custom_seed ? seedFromString(custom_seed_string) : default_seed;
open_simplex_noise(seed, &noise_context);
double frequency = (config && config->getProperty("frequency", property)) ? property.toDouble() : 25.0;
double ratio_x = (config && config->getProperty("ratio_x", property)) ? property.toDouble() : 1.0;
double ratio_y = (config && config->getProperty("ratio_y", property)) ? property.toDouble() : 1.0;
bool looping = (config && config->getProperty("looping", property)) ? property.toBool() : false;
if( looping ){
float major_radius = 0.5f * frequency * ratio_x;
float minor_radius = 0.5f * frequency * ratio_y;
while(it.nextPixel()){
double x_phase = (double)it.x() / (double)bounds.width() * M_PI * 2;
double y_phase = (double)it.y() / (double)(bounds.height()) * M_PI * 2;
double x_coordinate = major_radius * map_range(cos(x_phase), -1.0, 1.0, 0.0, 1.0);
double y_coordinate = major_radius * map_range(sin(x_phase), -1.0, 1.0, 0.0, 1.0);
double z_coordinate = minor_radius * map_range(cos(y_phase), -1.0, 1.0, 0.0, 1.0);
double w_coordinate = minor_radius * map_range(sin(y_phase), -1.0, 1.0, 0.0, 1.0);
double value = open_simplex_noise4(noise_context, x_coordinate, y_coordinate, z_coordinate, w_coordinate);
value = map_range(value, -1.0, 1.0, 0.0, 255.0);
QColor color = qRgb(static_cast<int>(value),
static_cast<int>(value),
static_cast<int>(value));
cs->fromQColor(color, it.rawData());
}
} else {
while(it.nextPixel()){
double x_phase = (double)it.x() / (double)(bounds.width()) * ratio_x;
double y_phase = (double)it.y() / (double)(bounds.height()) * ratio_y;
double value = open_simplex_noise4(noise_context, x_phase * frequency, y_phase * frequency, x_phase * frequency, y_phase * frequency);
value = map_range(value, -1.0, 1.0, 0.0, 255.0);
QColor color = qRgb(static_cast<int>(value),
static_cast<int>(value),
static_cast<int>(value));
cs->fromQColor(color, it.rawData());
}
}
open_simplex_noise_free(noise_context);
}
-KisFilterConfigurationSP KisSimplexNoiseGenerator::defaultConfiguration() const
+KisFilterConfigurationSP KisSimplexNoiseGenerator::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
config->setProperty("looping", false);
config->setProperty("frequency", 25.0);
uint seed = static_cast<uint>(rand());
config->setProperty("seed", seed);
config->setProperty("custom_seed_string", "");
config->setProperty("ratio_x", 1.0f);
config->setProperty("ratio_y", 1.0f);
return config;
}
KisConfigWidget * KisSimplexNoiseGenerator::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
return new KisWdgSimplexNoise((KisFilter*)this, (QWidget*)parent);
}
uint KisSimplexNoiseGenerator::seedFromString(const QString &string) const
{
QByteArray bytes = QCryptographicHash::hash(string.toUtf8(),QCryptographicHash::Md5);
uint hash = 0;
for( int index = 0; index < bytes.length(); index++){
hash += rotateLeft(bytes[index], index % 32);
}
return hash;
}
quint64 KisSimplexNoiseGenerator::rotateLeft(const quint64 input, uint shift) const
{
return (input << shift)|(input >> (64 - shift));
}
#include "simplexnoisegenerator.moc"
diff --git a/plugins/generators/simplexnoise/simplexnoisegenerator.h b/plugins/generators/simplexnoise/simplexnoisegenerator.h
index 9719507b00..c15bd4a31f 100644
--- a/plugins/generators/simplexnoise/simplexnoisegenerator.h
+++ b/plugins/generators/simplexnoise/simplexnoisegenerator.h
@@ -1,65 +1,65 @@
/*
* KDE. Krita Project.
*
* Copyright (c) 2019 Eoin O'Neill <eoinoneill1991@gmail.com>
* Copyright (c) 2019 Emmet O'Neill <emmetoneill.pdx@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 NOISEFILTER_H
#define NOISEFILTER_H
#include <QObject>
#include "generator/kis_generator.h"
class KisConfigWidget;
class KisSimplexNoiseGeneratorHandle : public QObject
{
Q_OBJECT
public:
KisSimplexNoiseGeneratorHandle(QObject *parent, const QVariantList &);
~KisSimplexNoiseGeneratorHandle() override;
};
class KisSimplexNoiseGenerator : public KisGenerator
{
public:
KisSimplexNoiseGenerator();
using KisGenerator::generate;
virtual void generate(KisProcessingInformation dst,
const QSize& size,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("simplex_noise", i18n("Simplex Noise"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
uint seedFromString(const QString &string) const;
quint64 rotateLeft(const quint64 input, uint d) const;
static inline double map_range(double value, double curr_min, double curr_max, double new_min, double new_max ) {
return (value - curr_min) * (new_max - new_min) / (curr_max - curr_min) + new_min;
}
};
#endif
diff --git a/plugins/generators/solid/colorgenerator.cpp b/plugins/generators/solid/colorgenerator.cpp
index 5ce44970db..c4c3d07b2a 100644
--- a/plugins/generators/solid/colorgenerator.cpp
+++ b/plugins/generators/solid/colorgenerator.cpp
@@ -1,103 +1,103 @@
/*
* This file is part of the KDE project
*
* 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.
*/
#include "colorgenerator.h"
#include <QPoint>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <klocalizedstring.h>
#include <kis_fill_painter.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_layer.h>
#include <generator/kis_generator_registry.h>
#include <kis_global.h>
#include <kis_selection.h>
#include <kis_types.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include "kis_wdg_color.h"
#include "ui_wdgcoloroptions.h"
K_PLUGIN_FACTORY_WITH_JSON(KritaColorGeneratorFactory, "kritacolorgenerator.json", registerPlugin<KritaColorGenerator>();)
KritaColorGenerator::KritaColorGenerator(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisGeneratorRegistry::instance()->add(new KisColorGenerator());
}
KritaColorGenerator::~KritaColorGenerator()
{
}
KisColorGenerator::KisColorGenerator() : KisGenerator(id(), KoID("basic"), i18n("&Solid Color..."))
{
setColorSpaceIndependence(FULLY_INDEPENDENT);
setSupportsPainting(true);
}
-KisFilterConfigurationSP KisColorGenerator::defaultConfiguration() const
+KisFilterConfigurationSP KisColorGenerator::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
{
- KisFilterConfigurationSP config = factoryConfiguration();
+ KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
QVariant v;
v.setValue(KoColor());
config->setProperty("color", v);
return config;
}
KisConfigWidget * KisColorGenerator::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
{
Q_UNUSED(dev);
return new KisWdgColor(parent);
}
void KisColorGenerator::generate(KisProcessingInformation dstInfo,
const QSize& size,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater) const
{
KisPaintDeviceSP dst = dstInfo.paintDevice();
Q_ASSERT(!dst.isNull());
Q_ASSERT(config);
KoColor c;
if (config) {
c = config->getColor("color");
KisFillPainter gc(dst);
gc.setProgress(progressUpdater);
gc.setChannelFlags(config->channelFlags());
gc.setOpacity(100);
gc.setSelection(dstInfo.selection());
gc.fillRect(QRect(dstInfo.topLeft(), size), c);
gc.end();
}
}
#include "colorgenerator.moc"
diff --git a/plugins/generators/solid/colorgenerator.h b/plugins/generators/solid/colorgenerator.h
index 4411620760..fb12ce5585 100644
--- a/plugins/generators/solid/colorgenerator.h
+++ b/plugins/generators/solid/colorgenerator.h
@@ -1,59 +1,59 @@
/*
* 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.
*/
#ifndef COLOR_GENERATOR_H
#define COLOR_GENERATOR_H
#include <QObject>
#include <QVariant>
#include "generator/kis_generator.h"
class KisConfigWidget;
class KritaColorGenerator : public QObject
{
Q_OBJECT
public:
KritaColorGenerator(QObject *parent, const QVariantList &);
~KritaColorGenerator() override;
};
class KisColorGenerator : public KisGenerator
{
public:
KisColorGenerator();
using KisGenerator::generate;
void generate(KisProcessingInformation dst,
const QSize& size,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
static inline KoID id() {
return KoID("color", i18n("Color"));
}
- KisFilterConfigurationSP defaultConfiguration() const override;
+ KisFilterConfigurationSP defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const override;
KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override;
};
#endif
diff --git a/plugins/generators/solid/kis_wdg_color.cpp b/plugins/generators/solid/kis_wdg_color.cpp
index f3ce030119..5f8f97a235 100644
--- a/plugins/generators/solid/kis_wdg_color.cpp
+++ b/plugins/generators/solid/kis_wdg_color.cpp
@@ -1,64 +1,65 @@
/*
* 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 <KisGlobalResourcesInterface.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);
+ KisFilterConfigurationSP config = new KisFilterConfiguration("color", 1, KisGlobalResourcesInterface::instance());
KoColor c;
c.fromKoColor(this->widget()->bnColor->color());
QVariant v;
v.setValue(c);
config->setProperty("color", v);
return config;
}
diff --git a/plugins/impex/brush/kis_brush_export.cpp b/plugins/impex/brush/kis_brush_export.cpp
index 2c617e0897..090191fad8 100644
--- a/plugins/impex/brush/kis_brush_export.cpp
+++ b/plugins/impex/brush/kis_brush_export.cpp
@@ -1,250 +1,250 @@
/*
* 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 "kis_brush_export.h"
#include <QCheckBox>
#include <QSlider>
#include <QBuffer>
#include <KoProperties.h>
#include <KoDialog.h>
#include <kpluginfactory.h>
#include <QFileInfo>
#include <KisExportCheckRegistry.h>
#include <kis_paint_device.h>
#include <KisViewManager.h>
#include <kis_image.h>
#include <KisDocument.h>
#include <kis_paint_layer.h>
#include <kis_spacing_selection_widget.h>
#include <kis_gbr_brush.h>
#include <kis_imagepipe_brush.h>
#include <kis_pipebrush_parasite.h>
#include <KisAnimatedBrushAnnotation.h>
#include <KisWdgOptionsBrush.h>
#include <KisImportExportManager.h>
#include <kis_config.h>
struct KisBrushExportOptions {
qreal spacing;
bool mask;
int brushStyle;
int dimensions;
qint32 ranks[KisPipeBrushParasite::MaxDim];
qint32 selectionModes[KisPipeBrushParasite::MaxDim];
QString name;
};
K_PLUGIN_FACTORY_WITH_JSON(KisBrushExportFactory, "krita_brush_export.json", registerPlugin<KisBrushExport>();)
KisBrushExport::KisBrushExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisBrushExport::~KisBrushExport()
{
}
KisImportExportErrorCode KisBrushExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
// XXX: Loading the parasite itself was commented out -- needs investigation
// KisAnnotationSP annotation = document->savingImage()->annotation("ImagePipe Parasite");
// KisPipeBrushParasite parasite;
// if (annotation) {
// QBuffer buf(const_cast<QByteArray*>(&annotation->annotation()));
// buf.open(QBuffer::ReadOnly);
// parasite.loadFromDevice(&buf);
// buf.close();
// }
KisBrushExportOptions exportOptions;
if (document->savingImage()->dynamicPropertyNames().contains("brushspacing")) {
exportOptions.spacing = document->savingImage()->property("brushspacing").toFloat();
}
else {
exportOptions.spacing = configuration->getInt("spacing");
}
if (!configuration->getString("name").isEmpty()) {
exportOptions.name = configuration->getString("name");
}
else {
exportOptions.name = document->savingImage()->objectName();
}
exportOptions.mask = configuration->getBool("mask");
exportOptions.brushStyle = configuration->getInt("brushStyle");
exportOptions.dimensions = configuration->getInt("dimensions");
for (int i = 0; i < KisPipeBrushParasite::MaxDim; ++i) {
exportOptions.selectionModes[i] = configuration->getInt("selectionMode" + QString::number(i));
exportOptions.ranks[i] = configuration->getInt("rank" + QString::number(i));
}
KisGbrBrush *brush = 0;
if (mimeType() == "image/x-gimp-brush") {
brush = new KisGbrBrush(filename());
}
else if (mimeType() == "image/x-gimp-brush-animated") {
brush = new KisImagePipeBrush(filename());
}
else {
return ImportExportCodes::FileFormatIncorrect;
}
qApp->processEvents(); // For vector layers to be updated
QRect rc = document->savingImage()->bounds();
brush->setSpacing(exportOptions.spacing);
KisImagePipeBrush *pipeBrush = dynamic_cast<KisImagePipeBrush*>(brush);
if (pipeBrush) {
// Create parasite. XXX: share with KisCustomBrushWidget
QVector< QVector<KisPaintDevice*> > devices;
devices.push_back(QVector<KisPaintDevice*>());
KoProperties properties;
properties.setProperty("visible", true);
QList<KisNodeSP> layers = document->savingImage()->root()->childNodes(QStringList("KisLayer"), properties);
Q_FOREACH (KisNodeSP node, layers) {
// push_front to behave exactly as gimp for gih creation
devices[0].push_front(node->projection().data());
}
QVector<KisParasite::SelectionMode > modes;
for (int i = 0; i < KisPipeBrushParasite::MaxDim; ++i) {
switch (exportOptions.selectionModes[i]) {
case 0: modes.push_back(KisParasite::Constant); break;
case 1: modes.push_back(KisParasite::Random); break;
case 2: modes.push_back(KisParasite::Incremental); break;
case 3: modes.push_back(KisParasite::Pressure); break;
case 4: modes.push_back(KisParasite::Angular); break;
case 5: modes.push_back(KisParasite::Velocity); break;
default: modes.push_back(KisParasite::Incremental);
}
}
KisPipeBrushParasite parasite;
parasite.dim = exportOptions.dimensions;
parasite.ncells = devices.at(0).count();
int maxRanks = 0;
for (int i = 0; i < KisPipeBrushParasite::MaxDim; ++i) {
// ### This can mask some bugs, be careful here in the future
parasite.rank[i] = exportOptions.ranks[i];
parasite.selection[i] = modes.at(i);
maxRanks += exportOptions.ranks[i];
}
if (maxRanks > layers.count()) {
return ImportExportCodes::FileFormatIncorrect;
}
// XXX needs movement!
parasite.setBrushesCount();
pipeBrush->setParasite(parasite);
pipeBrush->setDevices(devices, rc.width(), rc.height());
if (exportOptions.mask) {
- QVector<KisGbrBrush*> brushes = pipeBrush->brushes();
- Q_FOREACH(KisGbrBrush* brush, brushes) {
+ QVector<KisGbrBrushSP> brushes = pipeBrush->brushes();
+ Q_FOREACH(KisGbrBrushSP brush, brushes) {
brush->setHasColor(false);
}
}
}
else {
if (exportOptions.mask) {
QImage image = document->savingImage()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
brush->setImage(image);
brush->setBrushTipImage(image);
} else {
brush->initFromPaintDev(document->savingImage()->projection(),0,0,rc.width(), rc.height());
}
}
brush->setName(exportOptions.name);
// brushes are created after devices are loaded, call mask mode after that
brush->setUseColorAsMask(exportOptions.mask);
brush->setWidth(rc.width());
brush->setHeight(rc.height());
if (brush->saveToDevice(io)) {
return ImportExportCodes::OK;
}
else {
return ImportExportCodes::Failure;
}
}
KisPropertiesConfigurationSP KisBrushExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("spacing", 1.0);
cfg->setProperty("name", "");
cfg->setProperty("mask", true);
cfg->setProperty("brushStyle", 0);
cfg->setProperty("dimensions", 1);
for (int i = 0; i < KisPipeBrushParasite::MaxDim; ++i) {
cfg->setProperty("selectionMode" + QString::number(i), 2);
cfg->getInt("rank" + QString::number(i), 0);
}
return cfg;
}
KisConfigWidget *KisBrushExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &to) const
{
KisWdgOptionsBrush *wdg = new KisWdgOptionsBrush(parent);
if (to == "image/x-gimp-brush") {
wdg->groupBox->setVisible(false);
wdg->animStyleGroup->setVisible(false);
}
else if (to == "image/x-gimp-brush-animated") {
wdg->groupBox->setVisible(true);
wdg->animStyleGroup->setVisible(true);
}
// preload gih name with chosen filename
QFileInfo fileLocation(filename());
wdg->nameLineEdit->setText(fileLocation.completeBaseName());
return wdg;
}
void KisBrushExport::initializeCapabilities()
{
QList<QPair<KoID, KoID> > supportedColorModels;
supportedColorModels << QPair<KoID, KoID>()
<< QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID)
<< QPair<KoID, KoID>(GrayAColorModelID, Integer8BitsColorDepthID);
addSupportedColorModels(supportedColorModels, "Gimp Brushes");
if (mimeType() == "image/x-gimp-brush-animated") {
addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED));
}
}
#include "kis_brush_export.moc"
diff --git a/plugins/impex/brush/kis_brush_import.cpp b/plugins/impex/brush/kis_brush_import.cpp
index 56bc545c11..a2deb8b23a 100644
--- a/plugins/impex/brush/kis_brush_import.cpp
+++ b/plugins/impex/brush/kis_brush_import.cpp
@@ -1,123 +1,121 @@
/*
* 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 "kis_brush_import.h"
#include <QCheckBox>
#include <QBuffer>
#include <QSlider>
#include <QApplication>
#include <kpluginfactory.h>
#include <QFileInfo>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <KisDocument.h>
#include <kis_transaction.h>
#include <kis_paint_device.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <kis_node.h>
#include <kis_group_layer.h>
#include <kis_gbr_brush.h>
#include <kis_imagepipe_brush.h>
#include <KisAnimatedBrushAnnotation.h>
+#include <KisGlobalResourcesInterface.h>
K_PLUGIN_FACTORY_WITH_JSON(KisBrushImportFactory, "krita_brush_import.json", registerPlugin<KisBrushImport>();)
KisBrushImport::KisBrushImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisBrushImport::~KisBrushImport()
{
}
KisImportExportErrorCode KisBrushImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
- KisBrush *brush;
+ KisBrushSP brush;
if (mimeType() == "image/x-gimp-brush") {
- brush = new KisGbrBrush(filename());
+ brush = KisBrushSP(new KisGbrBrush(filename()));
}
else if (mimeType() == "image/x-gimp-brush-animated") {
- brush = new KisImagePipeBrush(filename());
+ brush = KisBrushSP(new KisImagePipeBrush(filename()));
}
else {
return ImportExportCodes::FileFormatIncorrect;
}
- if (!brush->loadFromDevice(io)) {
- delete brush;
+ if (!brush->loadFromDevice(io, KisGlobalResourcesInterface::instance())) {
return ImportExportCodes::FileFormatIncorrect;
}
if (!brush->valid()) {
- delete brush;
return ImportExportCodes::FileFormatIncorrect;;
}
const KoColorSpace *colorSpace = 0;
if (brush->hasColor()) {
colorSpace = KoColorSpaceRegistry::instance()->rgb8();
}
else {
colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), "");
}
KisImageSP image = new KisImage(document->createUndoStore(), brush->width(), brush->height(), colorSpace, brush->name());
image->setProperty("brushspacing", brush->spacing());
- KisImagePipeBrush *pipeBrush = dynamic_cast<KisImagePipeBrush*>(brush);
+ KisImagePipeBrushSP pipeBrush = brush.dynamicCast<KisImagePipeBrush>();
if (pipeBrush) {
- QVector<KisGbrBrush*> brushes = pipeBrush->brushes();
+ QVector<KisGbrBrushSP> brushes = pipeBrush->brushes();
for(int i = brushes.size(); i > 0; i--) {
- KisGbrBrush *subbrush = brushes.at(i - 1);
+ KisGbrBrushSP subbrush = brushes.at(i - 1);
const KoColorSpace *subColorSpace = 0;
if (brush->hasColor()) {
subColorSpace = KoColorSpaceRegistry::instance()->rgb8();
}
else {
subColorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), "");
}
KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255, subColorSpace);
layer->paintDevice()->convertFromQImage(subbrush->brushTipImage(), 0, 0, 0);
image->addNode(layer, image->rootLayer());
}
KisAnnotationSP ann = new KisAnimatedBrushAnnotation(pipeBrush->parasite());
image->addAnnotation(ann);
}
else {
KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255, colorSpace);
layer->paintDevice()->convertFromQImage(brush->brushTipImage(), 0, 0, 0);
image->addNode(layer, image->rootLayer(), 0);
}
document->setCurrentImage(image);
- delete brush;
return ImportExportCodes::OK;
}
#include "kis_brush_import.moc"
diff --git a/plugins/impex/libkra/kis_kra_load_visitor.cpp b/plugins/impex/libkra/kis_kra_load_visitor.cpp
index d495abd1ff..4fb4f5847d 100644
--- a/plugins/impex/libkra/kis_kra_load_visitor.cpp
+++ b/plugins/impex/libkra/kis_kra_load_visitor.cpp
@@ -1,789 +1,804 @@
/*
* 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 <KoHashGenerator.h>
-#include <KoHashGeneratorProvider.h>
+#include <KoMD5Generator.h>
+#include <KoMD5Generator.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include <KoFileDialog.h>
#include <KoStore.h>
#include <KoColorSpace.h>
#include <KoShapeControllerBase.h>
+#include <KisGlobalResourcesInterface.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"
#include "kis_filter_registry.h"
+#include "kis_generator_registry.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_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);
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, getLocation(layer, DOT_FILTERCONFIG));
- fixOldFilterConfigurations(layer->filter());
+ KisFilterSP filter = KisFilterRegistry::instance()->value(layer->filter()->name());
+ KisFilterConfigurationSP kfc = filter->factoryConfiguration(KisGlobalResourcesInterface::instance());
+
+ loadFilterConfiguration(kfc, getLocation(layer, DOT_FILTERCONFIG));
+ fixOldFilterConfigurations(kfc);
+ kfc->createLocalResourcesSnapshot();
+
+ layer->setFilter(kfc);
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
- result = loadFilterConfiguration(layer, getLocation(layer, DOT_FILTERCONFIG));
- layer->setFilter(layer->filter());
+ KisGeneratorSP filter = KisGeneratorRegistry::instance()->value(layer->filter()->name());
+ KisFilterConfigurationSP kfc = filter->factoryConfiguration(KisGlobalResourcesInterface::instance());
+
+ result = loadFilterConfiguration(kfc, getLocation(layer, DOT_FILTERCONFIG));
+ kfc->createLocalResourcesSnapshot();
+
+ layer->setFilter(kfc);
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());
if (!srcNode.isNull()) {
KisLayerSP srcLayer = qobject_cast<KisLayer*>(srcNode.data());
Q_ASSERT(srcLayer);
layer->setCopyFrom(srcLayer);
} else {
m_warningMessages.append(i18nc("Loading a .kra file", "The file contains a clone layer that has an incorrect source node id. "
"This layer will be converted into a paint layer."));
}
// 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, getLocation(mask, DOT_FILTERCONFIG));
- fixOldFilterConfigurations(mask->filter());
+
+ KisFilterSP filter = KisFilterRegistry::instance()->value(mask->filter()->name());
+ KisFilterConfigurationSP kfc = filter->factoryConfiguration(KisGlobalResourcesInterface::instance());
+ result = loadFilterConfiguration(kfc, getLocation(mask, DOT_FILTERCONFIG));
+ fixOldFilterConfigurations(kfc);
+ kfc->createLocalResourcesSnapshot();
+
+ mask->setFilter(kfc);
+
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();
- KoHashGenerator *hashGenerator = KoHashGeneratorProvider::instance()->getGenerator("MD5");
- QByteArray hash = hashGenerator->generateHash(data);
+ QByteArray hash = KoMD5Generator::generateHash(data);
if (m_profileCache.contains(hash)) {
if (device->setProfile(m_profileCache[hash], 0)) {
return true;
}
}
else {
// Create a colorspace with the embedded profile
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(device->colorSpace()->colorModelId().id(), device->colorSpace()->colorDepthId().id(), data);
m_profileCache[hash] = profile;
if (device->setProfile(profile, 0)) {
return true;
}
}
}
m_warningMessages << i18n("Could not load profile: %1.", location);
return true;
}
-bool KisKraLoadVisitor::loadFilterConfiguration(KisNodeFilterInterface *nodeInterface, const QString& location)
+bool KisKraLoadVisitor::loadFilterConfiguration(KisFilterConfigurationSP kfc, const QString& location)
{
- KisFilterConfigurationSP kfc = nodeInterface->filter();
-
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;
}
void KisKraLoadVisitor::fixOldFilterConfigurations(KisFilterConfigurationSP kfc)
{
KisFilterSP filter = KisFilterRegistry::instance()->value(kfc->name());
KIS_SAFE_ASSERT_RECOVER_RETURN(filter);
if (!filter->configurationAllowedForMask(kfc)) {
filter->fixLoadedFilterConfigurationForMasks(kfc);
}
KIS_SAFE_ASSERT_RECOVER_NOOP(filter->configurationAllowedForMask(kfc));
}
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_load_visitor.h b/plugins/impex/libkra/kis_kra_load_visitor.h
index d0d675caca..b152cd092b 100644
--- a/plugins/impex/libkra/kis_kra_load_visitor.h
+++ b/plugins/impex/libkra/kis_kra_load_visitor.h
@@ -1,113 +1,113 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.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.
*/
#ifndef KIS_KRA_LOAD_VISITOR_H_
#define KIS_KRA_LOAD_VISITOR_H_
#include <QRect>
#include <QStringList>
// kritaimage
#include "kis_types.h"
#include "kis_node_visitor.h"
#include "kritalibkra_export.h"
class KisFilterConfiguration;
class KoStore;
class KoShapeControllerBase;
class KoColorProfile;
class KisNodeFilterInterface;
class KRITALIBKRA_EXPORT KisKraLoadVisitor : public KisNodeVisitor
{
public:
KisKraLoadVisitor(KisImageSP image,
KoStore *store,
KoShapeControllerBase *shapeController,
QMap<KisNode *, QString> &layerFilenames,
QMap<KisNode *, QString> &keyframeFilenames,
const QString & name,
int syntaxVersion);
public:
void setExternalUri(const QString &uri);
bool visit(KisNode*) override {
return true;
}
bool visit(KisExternalLayer *) override;
bool visit(KisPaintLayer *layer) override;
bool visit(KisGroupLayer *layer) override;
bool visit(KisAdjustmentLayer* layer) override;
bool visit(KisGeneratorLayer* layer) override;
bool visit(KisCloneLayer *layer) override;
bool visit(KisFilterMask *mask) override;
bool visit(KisTransformMask *mask) override;
bool visit(KisTransparencyMask *mask) override;
bool visit(KisSelectionMask *mask) override;
bool visit(KisColorizeMask *mask) override;
QStringList errorMessages() const;
QStringList warningMessages() const;
private:
bool loadPaintDevice(KisPaintDeviceSP device, const QString& location);
template<class DevicePolicy>
bool loadPaintDeviceFrame(KisPaintDeviceSP device, const QString &location, DevicePolicy policy);
bool loadProfile(KisPaintDeviceSP device, const QString& location);
- bool loadFilterConfiguration(KisNodeFilterInterface *nodeInterface, const QString& location);
+ bool loadFilterConfiguration(KisFilterConfigurationSP kfc, const QString& location);
void fixOldFilterConfigurations(KisFilterConfigurationSP kfc);
bool loadMetaData(KisNode* node);
void initSelectionForMask(KisMask *mask);
bool loadSelection(const QString& location, KisSelectionSP dstSelection);
QString getLocation(KisNode* node, const QString& suffix = QString());
QString getLocation(const QString &filename, const QString &suffix = QString());
void loadNodeKeyframes(KisNode *node);
/**
* Load deprecated filters.
* Most deprecated filters can be handled by this, but the brightnesscontact to perchannels
* conversion needs to be handled in the perchannel class because those filters
* have their own xml loading functionality.
*/
void loadDeprecatedFilter(KisFilterConfigurationSP cfg);
private:
KisImageSP m_image;
KoStore *m_store;
bool m_external;
QString m_uri;
QMap<KisNode *, QString> m_layerFilenames;
QMap<KisNode *, QString> m_keyframeFilenames;
QString m_name;
int m_syntaxVersion;
QStringList m_errorMessages;
QStringList m_warningMessages;
KoShapeControllerBase *m_shapeController;
QMap<QByteArray, const KoColorProfile *> m_profileCache;
};
#endif // KIS_KRA_LOAD_VISITOR_H_
diff --git a/plugins/impex/libkra/kis_kra_loader.cpp b/plugins/impex/libkra/kis_kra_loader.cpp
index b8b2a2623b..22a5092d48 100644
--- a/plugins/impex/libkra/kis_kra_loader.cpp
+++ b/plugins/impex/libkra/kis_kra_loader.cpp
@@ -1,1267 +1,1289 @@
/* 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 <KisResourceServerProvider.h>
+#include <KoResourceServer.h>
+#include <KisResourceStorage.h>
+#include <KisGlobalResourcesInterface.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"
#include "KisMirrorAxisConfig.h"
+#include <KisGlobalResourcesInterface.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;
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, m_d->imageName);
}
else {
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() == MIRROR_AXIS) {
loadMirrorAxis(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, true);
image->waitForDone();
}
if (!res) {
const QString defaultProfileId = KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(image->colorSpace()->id());
profile = KoColorSpaceRegistry::instance()->profileByName(defaultProfileId);
Q_ASSERT(profile && profile->valid());
image->assignImageProfile(profile, true);
image->waitForDone();
}
}
}
}
//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);
+ warnKrita << "WARNING: Asl Layer Styles cannot be read (part of resource rewrite).";
+ // TODO: RESOURCES: needs implementing of creation of the storage and linking it to the database etc.
+
+ // Question: do we need to use KisAslStorage or the document storage?
+ // see: QSharedPointer<KoResourceServer> storage = KisResourceServerProvider::instance()->storageByName(m_d->document->uniqueID());
+ // and then: storage->addResource(aslStyle);
+ // or through the server: get the server, add everything directly to the server
+ // but I believe it should go through the KisAslStorage? // tiar
+
+
+
+// //KisPSDLayerStyleSP collection(new KisPSDLayerStyle("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!";
+// }
- 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;
+ qDebug() << ">>>> loadPalettes" << m_d->paletteFilenames;
+ QList<KoColorSetSP> list;
Q_FOREACH (const QString &filename, m_d->paletteFilenames) {
- KoColorSet *newPalette = new KoColorSet(filename);
+ qDebug() << "loading palettes" << filename;
+ KoColorSetSP 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->fromByteArray(data, KisGlobalResourcesInterface::instance());
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;
}
QString KisKraLoader::imageName() const
{
return m_d->imageName;
}
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());
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->factoryConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
+ kfc->createLocalResourcesSnapshot();
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->factoryConfiguration();
+ KisFilterConfigurationSP kgc = generator->defaultConfiguration(KisGlobalResourcesInterface::instance());
+ kgc->createLocalResourcesSnapshot();
// 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->factoryConfiguration();
+ KisFilterConfigurationSP kfc = f->defaultConfiguration(KisGlobalResourcesInterface::instance());
+ kfc->createLocalResourcesSnapshot();
// 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::loadMirrorAxis(const KoXmlElement &elem)
{
QDomDocument dom;
KoXml::asQDomElement(dom, elem);
QDomElement domElement = dom.firstChildElement();
KisMirrorAxisConfig mirrorAxis;
mirrorAxis.loadFromXml(domElement);
m_d->document->setMirrorAxisConfig(mirrorAxis);
}
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/libkra/kis_kra_saver.cpp b/plugins/impex/libkra/kis_kra_saver.cpp
index 70c0382816..94709b2fe5 100644
--- a/plugins/impex/libkra/kis_kra_saver.cpp
+++ b/plugins/impex/libkra/kis_kra_saver.cpp
@@ -1,527 +1,530 @@
/* This file is part of the KDE project
* Copyright 2008 (C) 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 "kis_kra_saver.h"
#include "kis_kra_tags.h"
#include "kis_kra_save_visitor.h"
#include "kis_kra_savexml_visitor.h"
#include <QDomDocument>
#include <QDomElement>
#include <QString>
#include <QStringList>
#include <QScopedPointer>
#include <QUrl>
#include <QBuffer>
#include <KoDocumentInfo.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpace.h>
#include <KoColorProfile.h>
#include <KoColor.h>
#include <KoColorSet.h>
#include <KoResourceServer.h>
#include <KoResourceServerProvider.h>
#include <KoStore.h>
#include <KoStoreDevice.h>
#include <kis_annotation.h>
#include <kis_image.h>
#include <kis_image_animation_interface.h>
#include <kis_group_layer.h>
#include <kis_layer.h>
#include <kis_adjustment_layer.h>
#include <kis_layer_composition.h>
#include <kis_painting_assistants_decoration.h>
-#include <kis_psd_layer_style_resource.h>
#include "kis_png_converter.h"
#include "kis_keyframe_channel.h"
#include <kis_time_range.h>
#include "KisDocument.h"
#include <string>
#include "kis_dom_utils.h"
#include "kis_grid_config.h"
#include "kis_guides_config.h"
#include "KisProofingConfiguration.h"
#include <KisMirrorAxisConfig.h>
#include <QFileInfo>
#include <QDir>
using namespace KRA;
struct KisKraSaver::Private
{
public:
KisDocument* doc;
QMap<const KisNode*, QString> nodeFileNames;
QMap<const KisNode*, QString> keyframeFilenames;
QString imageName;
QString filename;
QStringList errorMessages;
};
KisKraSaver::KisKraSaver(KisDocument* document, const QString &filename)
: m_d(new Private)
{
m_d->doc = document;
m_d->filename = filename;
m_d->imageName = m_d->doc->documentInfo()->aboutInfo("title");
if (m_d->imageName.isEmpty()) {
m_d->imageName = i18n("Unnamed");
}
}
KisKraSaver::~KisKraSaver()
{
delete m_d;
}
QDomElement KisKraSaver::saveXML(QDomDocument& doc, KisImageSP image)
{
QDomElement imageElement = doc.createElement("IMAGE");
Q_ASSERT(image);
imageElement.setAttribute(NAME, m_d->imageName);
imageElement.setAttribute(MIME, NATIVE_MIMETYPE);
imageElement.setAttribute(WIDTH, KisDomUtils::toString(image->width()));
imageElement.setAttribute(HEIGHT, KisDomUtils::toString(image->height()));
imageElement.setAttribute(COLORSPACE_NAME, image->colorSpace()->id());
imageElement.setAttribute(DESCRIPTION, m_d->doc->documentInfo()->aboutInfo("comment"));
// XXX: Save profile as blob inside the image, instead of the product name.
if (image->profile() && image->profile()-> valid()) {
imageElement.setAttribute(PROFILE, image->profile()->name());
}
imageElement.setAttribute(X_RESOLUTION, KisDomUtils::toString(image->xRes()*72.0));
imageElement.setAttribute(Y_RESOLUTION, KisDomUtils::toString(image->yRes()*72.0));
//now the proofing options:
if (image->proofingConfiguration()) {
imageElement.setAttribute(PROOFINGPROFILENAME, KisDomUtils::toString(image->proofingConfiguration()->proofingProfile));
imageElement.setAttribute(PROOFINGMODEL, KisDomUtils::toString(image->proofingConfiguration()->proofingModel));
imageElement.setAttribute(PROOFINGDEPTH, KisDomUtils::toString(image->proofingConfiguration()->proofingDepth));
imageElement.setAttribute(PROOFINGINTENT, KisDomUtils::toString(image->proofingConfiguration()->intent));
imageElement.setAttribute(PROOFINGADAPTATIONSTATE, KisDomUtils::toString(image->proofingConfiguration()->adaptationState));
}
quint32 count = 1; // We don't save the root layer, but it does count
KisSaveXmlVisitor visitor(doc, imageElement, count, m_d->doc->url().toLocalFile(), true);
visitor.setSelectedNodes({m_d->doc->preActivatedNode()});
image->rootLayer()->accept(visitor);
m_d->errorMessages.append(visitor.errorMessages());
m_d->nodeFileNames = visitor.nodeFileNames();
m_d->keyframeFilenames = visitor.keyframeFileNames();
saveBackgroundColor(doc, imageElement, image);
saveAssistantsGlobalColor(doc, imageElement);
saveWarningColor(doc, imageElement, image);
saveCompositions(doc, imageElement, image);
saveAssistantsList(doc, imageElement);
saveGrid(doc, imageElement);
saveGuides(doc, imageElement);
saveMirrorAxis(doc, imageElement);
saveAudio(doc, imageElement);
savePalettesToXML(doc, imageElement);
QDomElement animationElement = doc.createElement("animation");
KisDomUtils::saveValue(&animationElement, "framerate", image->animationInterface()->framerate());
KisDomUtils::saveValue(&animationElement, "range", image->animationInterface()->fullClipRange());
KisDomUtils::saveValue(&animationElement, "currentTime", image->animationInterface()->currentUITime());
imageElement.appendChild(animationElement);
return imageElement;
}
bool KisKraSaver::savePalettes(KoStore *store, KisImageSP image, const QString &uri)
{
Q_UNUSED(image);
Q_UNUSED(uri);
+ qDebug() << "saving palettes to document" << m_d->doc->paletteList().size();
+
bool res = false;
if (m_d->doc->paletteList().size() == 0) {
return true;
}
- for (const KoColorSet *palette : m_d->doc->paletteList()) {
- if (!palette->isGlobal()) {
- if (!store->open(m_d->imageName + PALETTE_PATH + palette->filename())) {
- m_d->errorMessages << i18n("could not save palettes");
- return false;
- }
- QByteArray ba = palette->toByteArray();
- if (!ba.isEmpty()) {
- store->write(ba);
- } else {
- qWarning() << "Cannot save the palette to a byte array:" << palette->name();
- }
- store->close();
- res = true;
+ for (const KoColorSetSP palette : m_d->doc->paletteList()) {
+ qDebug() << "saving palette..." << palette->storageLocation() << palette->filename();
+ if (!store->open(m_d->imageName + PALETTE_PATH + palette->filename())) {
+ m_d->errorMessages << i18n("could not save palettes");
+ return false;
}
+ QByteArray ba = palette->toByteArray();
+ if (!ba.isEmpty()) {
+ store->write(ba);
+ } else {
+ qWarning() << "Cannot save the palette to a byte array:" << palette->name();
+ }
+ store->close();
+ res = true;
}
return res;
}
void KisKraSaver::savePalettesToXML(QDomDocument &doc, QDomElement &element)
{
QDomElement ePalette = doc.createElement(PALETTES);
- for (const KoColorSet *palette : m_d->doc->paletteList()) {
- if (!palette->isGlobal()) {
- QDomElement eFile = doc.createElement("palette");
- eFile.setAttribute("filename", palette->filename());
- ePalette.appendChild(eFile);
- }
+ for (const KoColorSetSP palette : m_d->doc->paletteList()) {
+ QDomElement eFile = doc.createElement("palette");
+ eFile.setAttribute("filename", palette->filename());
+ ePalette.appendChild(eFile);
}
element.appendChild(ePalette);
}
bool KisKraSaver::saveKeyframes(KoStore *store, const QString &uri, bool external)
{
QMap<const KisNode*, QString>::iterator it;
for (it = m_d->keyframeFilenames.begin(); it != m_d->keyframeFilenames.end(); it++) {
const KisNode *node = it.key();
QString filename = it.value();
QString location =
(external ? QString() : uri)
+ m_d->imageName + LAYER_PATH + filename;
if (!saveNodeKeyframes(store, location, node)) {
return false;
}
}
return true;
}
bool KisKraSaver::saveNodeKeyframes(KoStore *store, QString location, const KisNode *node)
{
QDomDocument doc = KisDocument::createDomDocument("krita-keyframes", "keyframes", "1.0");
QDomElement root = doc.documentElement();
KisKeyframeChannel *channel;
Q_FOREACH (channel, node->keyframeChannels()) {
QDomElement element = channel->toXML(doc, m_d->nodeFileNames[node]);
root.appendChild(element);
}
if (store->open(location)) {
QByteArray xml = doc.toByteArray();
store->write(xml);
store->close();
} else {
m_d->errorMessages << i18n("could not save keyframes");
return false;
}
return true;
}
bool KisKraSaver::saveBinaryData(KoStore* store, KisImageSP image, const QString &uri, bool external, bool autosave)
{
QString location;
// Save the layers data
KisKraSaveVisitor visitor(store, m_d->imageName, m_d->nodeFileNames);
if (external)
visitor.setExternalUri(uri);
image->rootLayer()->accept(visitor);
m_d->errorMessages.append(visitor.errorMessages());
if (!m_d->errorMessages.isEmpty()) {
return false;
}
// saving annotations
// XXX this only saves EXIF and ICC info. This would probably need
// a redesign of the dtd of the krita file to do this more generally correct
// e.g. have <ANNOTATION> tags or so.
KisAnnotationSP annotation = image->annotation("exif");
if (annotation) {
location = external ? QString() : uri;
location += m_d->imageName + EXIF_PATH;
if (store->open(location)) {
store->write(annotation->annotation());
store->close();
}
}
if (image->profile()) {
const KoColorProfile *profile = image->profile();
KisAnnotationSP annotation;
if (profile) {
QByteArray profileRawData = profile->rawData();
if (!profileRawData.isEmpty()) {
if (profile->type() == "icc") {
annotation = new KisAnnotation(ICC, profile->name(), profile->rawData());
} else {
annotation = new KisAnnotation(PROFILE, profile->name(), profile->rawData());
}
}
}
if (annotation) {
location = external ? QString() : uri;
location += m_d->imageName + ICC_PATH;
if (store->open(location)) {
store->write(annotation->annotation());
store->close();
}
}
}
//This'll embed the profile used for proofing into the kra file.
if (image->proofingConfiguration()) {
const KoColorProfile *proofingProfile = KoColorSpaceRegistry::instance()->profileByName(image->proofingConfiguration()->proofingProfile);
if (proofingProfile && proofingProfile->valid()) {
QByteArray proofingProfileRaw = proofingProfile->rawData();
if (!proofingProfileRaw.isEmpty()) {
annotation = new KisAnnotation(ICCPROOFINGPROFILE, proofingProfile->name(), proofingProfile->rawData());
}
}
if (annotation) {
location = external ? QString() : uri;
location += m_d->imageName + ICC_PROOFING_PATH;
if (store->open(location)) {
store->write(annotation->annotation());
store->close();
}
}
}
{
+ warnKrita << "WARNING: Asl Layer Styles cannot be written (part of resource rewrite).";
+ // TODO: RESOURCES: needs implementation
+
+ /*
KisPSDLayerStyleCollectionResource collection("not-nexists.asl");
KIS_ASSERT_RECOVER_NOOP(!collection.valid());
collection.collectAllLayerStyles(image->root());
if (collection.valid()) {
location = external ? QString() : uri;
location += m_d->imageName + LAYER_STYLES_PATH;
if (store->open(location)) {
QBuffer aslBuffer;
aslBuffer.open(QIODevice::WriteOnly);
collection.saveToDevice(&aslBuffer);
aslBuffer.close();
store->write(aslBuffer.buffer());
store->close();
}
}
+ */
}
if (!autosave) {
KisPaintDeviceSP dev = image->projection();
KisPNGConverter::saveDeviceToStore("mergedimage.png", image->bounds(), image->xRes(), image->yRes(), dev, store);
}
saveAssistants(store, uri,external);
return true;
}
QStringList KisKraSaver::errorMessages() const
{
return m_d->errorMessages;
}
void KisKraSaver::saveBackgroundColor(QDomDocument& doc, QDomElement& element, KisImageSP image)
{
QDomElement e = doc.createElement(CANVASPROJECTIONCOLOR);
KoColor color = image->defaultProjectionColor();
QByteArray colorData = QByteArray::fromRawData((const char*)color.data(), color.colorSpace()->pixelSize());
e.setAttribute(COLORBYTEDATA, QString(colorData.toBase64()));
element.appendChild(e);
}
void KisKraSaver::saveAssistantsGlobalColor(QDomDocument& doc, QDomElement& element)
{
QDomElement e = doc.createElement(GLOBALASSISTANTSCOLOR);
QString colorString = KisDomUtils::qColorToQString(m_d->doc->assistantsGlobalColor());
e.setAttribute(SIMPLECOLORDATA, QString(colorString));
element.appendChild(e);
}
void KisKraSaver::saveWarningColor(QDomDocument& doc, QDomElement& element, KisImageSP image)
{
if (image->proofingConfiguration()) {
QDomElement e = doc.createElement(PROOFINGWARNINGCOLOR);
KoColor color = image->proofingConfiguration()->warningColor;
color.toXML(doc, e);
element.appendChild(e);
}
}
void KisKraSaver::saveCompositions(QDomDocument& doc, QDomElement& element, KisImageSP image)
{
if (!image->compositions().isEmpty()) {
QDomElement e = doc.createElement("compositions");
Q_FOREACH (KisLayerCompositionSP composition, image->compositions()) {
composition->save(doc, e);
}
element.appendChild(e);
}
}
bool KisKraSaver::saveAssistants(KoStore* store, QString uri, bool external)
{
QString location;
QMap<QString, int> assistantcounters;
QByteArray data;
QList<KisPaintingAssistantSP> assistants = m_d->doc->assistants();
QMap<KisPaintingAssistantHandleSP, int> handlemap;
if (!assistants.isEmpty()) {
Q_FOREACH (KisPaintingAssistantSP assist, assistants){
if (!assistantcounters.contains(assist->id())){
assistantcounters.insert(assist->id(),0);
}
location = external ? QString() : uri;
location += m_d->imageName + ASSISTANTS_PATH;
location += QString(assist->id()+"%1.assistant").arg(assistantcounters[assist->id()]);
data = assist->saveXml(handlemap);
store->open(location);
store->write(data);
store->close();
assistantcounters[assist->id()]++;
}
}
return true;
}
bool KisKraSaver::saveAssistantsList(QDomDocument& doc, QDomElement& element)
{
int count_ellipse = 0, count_perspective = 0, count_ruler = 0, count_vanishingpoint = 0,count_infiniteruler = 0, count_parallelruler = 0, count_concentricellipse = 0, count_fisheyepoint = 0, count_spline = 0;
QList<KisPaintingAssistantSP> assistants = m_d->doc->assistants();
if (!assistants.isEmpty()) {
QDomElement assistantsElement = doc.createElement("assistants");
Q_FOREACH (KisPaintingAssistantSP assist, assistants){
if (assist->id() == "ellipse"){
assist->saveXmlList(doc, assistantsElement, count_ellipse);
count_ellipse++;
}
else if (assist->id() == "spline"){
assist->saveXmlList(doc, assistantsElement, count_spline);
count_spline++;
}
else if (assist->id() == "perspective"){
assist->saveXmlList(doc, assistantsElement, count_perspective);
count_perspective++;
}
else if (assist->id() == "vanishing point"){
assist->saveXmlList(doc, assistantsElement, count_vanishingpoint);
count_vanishingpoint++;
}
else if (assist->id() == "infinite ruler"){
assist->saveXmlList(doc, assistantsElement, count_infiniteruler);
count_infiniteruler++;
}
else if (assist->id() == "parallel ruler"){
assist->saveXmlList(doc, assistantsElement, count_parallelruler);
count_parallelruler++;
}
else if (assist->id() == "concentric ellipse"){
assist->saveXmlList(doc, assistantsElement, count_concentricellipse);
count_concentricellipse++;
}
else if (assist->id() == "fisheye-point"){
assist->saveXmlList(doc, assistantsElement, count_fisheyepoint);
count_fisheyepoint++;
}
else if (assist->id() == "ruler"){
assist->saveXmlList(doc, assistantsElement, count_ruler);
count_ruler++;
}
}
element.appendChild(assistantsElement);
}
return true;
}
bool KisKraSaver::saveGrid(QDomDocument& doc, QDomElement& element)
{
KisGridConfig config = m_d->doc->gridConfig();
if (!config.isDefault()) {
QDomElement gridElement = config.saveDynamicDataToXml(doc, "grid");
element.appendChild(gridElement);
}
return true;
}
bool KisKraSaver::saveGuides(QDomDocument& doc, QDomElement& element)
{
KisGuidesConfig guides = m_d->doc->guidesConfig();
if (!guides.isDefault()) {
QDomElement guidesElement = guides.saveToXml(doc, "guides");
element.appendChild(guidesElement);
}
return true;
}
bool KisKraSaver::saveMirrorAxis(QDomDocument &doc, QDomElement &element)
{
KisMirrorAxisConfig mirrorAxisConfig = m_d->doc->mirrorAxisConfig();
if (!mirrorAxisConfig.isDefault()) {
QDomElement mirrorAxisElement = mirrorAxisConfig.saveToXml(doc, MIRROR_AXIS);
element.appendChild(mirrorAxisElement);
}
return true;
}
bool KisKraSaver::saveAudio(QDomDocument& doc, QDomElement& element)
{
const KisImageAnimationInterface *interface = m_d->doc->image()->animationInterface();
QString fileName = interface->audioChannelFileName();
if (fileName.isEmpty()) return true;
if (!QFileInfo::exists(fileName)) {
m_d->errorMessages << i18n("Audio channel file %1 doesn't exist!", fileName);
return false;
}
const QDir documentDir = QFileInfo(m_d->filename).absoluteDir();
KIS_ASSERT_RECOVER_RETURN_VALUE(documentDir.exists(), false);
fileName = documentDir.relativeFilePath(fileName);
fileName = QDir::fromNativeSeparators(fileName);
KIS_ASSERT_RECOVER_RETURN_VALUE(!fileName.isEmpty(), false);
QDomElement audioElement = doc.createElement("audio");
KisDomUtils::saveValue(&audioElement, "masterChannelPath", fileName);
KisDomUtils::saveValue(&audioElement, "audioMuted", interface->isAudioMuted());
KisDomUtils::saveValue(&audioElement, "audioVolume", interface->audioVolume());
element.appendChild(audioElement);
return true;
}
diff --git a/plugins/impex/libkra/kis_kra_tags.h b/plugins/impex/libkra/kis_kra_tags.h
index f09f0eef6a..10833a6a37 100644
--- a/plugins/impex/libkra/kis_kra_tags.h
+++ b/plugins/impex/libkra/kis_kra_tags.h
@@ -1,145 +1,145 @@
/* This file is part of the KDE project
* Copyright 2008 (C) 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 KIS_KRA_TAGS
#define KIS_KRA_TAGS
#include <QString>
-
+#include <KisResourceTypes.h>
/**
* Tag definitions for our xml file format
*/
namespace KRA
{
// mimetype
const QString NATIVE_MIMETYPE = "application/x-kra";
// xml tags
const QString SEPARATOR = "/";
const QString SHAPE_LAYER_PATH = "/shapelayers/";
const QString EXIF_PATH = "/annotations/exif";
const QString ICC_PATH = "/annotations/icc";
const QString ICC_PROOFING_PATH = "/annotations/proofing/icc";
const QString LAYER_STYLES_PATH = "/annotations/layerstyles.asl";
const QString ASSISTANTS_PATH = "/assistants/";
const QString LAYER_PATH = "/layers/";
const QString PALETTE_PATH = "/palettes/";
const QString ADJUSTMENT_LAYER = "adjustmentlayer";
const QString CHANNEL_FLAGS = "channelflags";
const QString CHANNEL_LOCK_FLAGS = "channellockflags";
const QString CLONE_FROM = "clonefrom";
const QString CLONE_FROM_UUID = "clonefromuuid";
const QString CLONE_LAYER = "clonelayer";
const QString CLONE_TYPE = "clonetype";
const QString COLORSPACE_NAME = "colorspacename";
const QString COMPOSITE_OP = "compositeop";
const QString DESCRIPTION = "description";
const QString ONION_SKIN_ENABLED = "onionskin";
const QString VISIBLE_IN_TIMELINE = "intimeline";
const QString DOT_FILTERCONFIG = ".filterconfig";
const QString DOT_TRANSFORMCONFIG = ".transformconfig";
const QString DOT_ICC = ".icc";
const QString DOT_PIXEL_SELECTION = ".pixelselection";
const QString DOT_SHAPE_SELECTION = ".shapeselection";
const QString DOT_SHAPE_LAYER = ".shapelayer";
const QString DOT_COLORIZE_MASK = ".colorizemask";
const QString DOT_METADATA = ".metadata";
const QString FILE_NAME = "filename";
const QString FILTER_MASK = "filtermask";
const QString FILTER_NAME = "filtername";
const QString FILTER_STATEGY = "filter_strategy";
const QString FILTER_VERSION = "filterversion";
const QString GENERATOR_LAYER = "generatorlayer";
const QString GENERATOR_NAME = "generatorname";
const QString GENERATOR_VERSION = "generatorversion";
const QString GROUP_LAYER = "grouplayer";
const QString HEIGHT = "height";
const QString ICC = "icc";
const QString LAYER = "layer";
const QString LAYERS = "layers";
const QString NODE_TYPE = "nodetype";
const QString LOCKED = "locked";
const QString MASK = "mask";
const QString MASKS = "masks";
const QString MIME = "mime";
const QString NAME = "name";
const QString OPACITY = "opacity";
const QString COLLAPSED = "collapsed";
const QString COLOR_LABEL = "colorlabel";
const QString PAINT_LAYER = "paintlayer";
const QString PROFILE = "profile";
const QString ROTATION = "rotation";
const QString SELECTION_MASK = "selectionmask";
const QString SHAPE_LAYER = "shapelayer";
const QString REFERENCE_IMAGES_LAYER = "referenceimages";
const QString FILE_LAYER = "filelayer";
const QString TRANSPARENCY_MASK = "transparencymask";
const QString COLORIZE_MASK = "colorizemask";
const QString COLORIZE_SHOW_COLORING = "show-coloring";
const QString COLORIZE_EDIT_KEYSTROKES = "edit-keystrokes";
const QString COLORIZE_KEYSTROKE = "keystroke";
const QString COLORIZE_KEYSTROKE_COLOR = "color";
const QString COLORIZE_KEYSTROKE_IS_TRANSPARENT = "is-transparent";
const QString COLORIZE_COLORING_DEVICE = "colorize-coloring";
const QString COLORIZE_KEYSTROKES_SECTION = "keystrokes";
const QString COLORIZE_USE_EDGE_DETECTION = "use-edge-detection";
const QString COLORIZE_EDGE_DETECTION_SIZE = "edge-detection-size";
const QString COLORIZE_FUZZY_RADIUS = "fuzzy-radius";
const QString COLORIZE_CLEANUP = "cleanup";
const QString COLORIZE_LIMIT_TO_DEVICE = "limit-to-device";
const QString TRANSFORM_MASK = "transformmask";
const QString UUID = "uuid";
const QString VISIBLE = "visible";
const QString WIDTH = "width";
const QString X = "x";
const QString X_RESOLUTION = "x-res";
const QString X_SCALE = "x_scale";
const QString X_SHEAR = "x_shear";
const QString X_TRANSLATION = "x_translation";
const QString Y = "y";
const QString Y_RESOLUTION = "y-res";
const QString Y_SCALE = "y_scale";
const QString Y_SHEAR = "y_shear";
const QString Y_TRANSLATION = "y_translation";
const QString ACTIVE = "active";
const QString LAYER_STYLE_UUID = "layerstyle";
const QString PASS_THROUGH_MODE = "passthrough";
const QString KEYFRAME_FILE = "keyframes";
const QString PROOFINGPROFILENAME = "proofing-profile-name";
const QString PROOFINGMODEL = "proofing-model";
const QString PROOFINGDEPTH = "proofing-depth";
const QString PROOFINGINTENT = "proofing-intent";
const QString PROOFINGWARNINGCOLOR ="ProofingWarningColor";
const QString PROOFINGADAPTATIONSTATE = "proofing-adaptation-state";
const QString ICCPROOFINGPROFILE ="icc-proofing-profile";
const QString CANVASPROJECTIONCOLOR = "ProjectionBackgroundColor";
const QString COLORBYTEDATA = "ColorData";
const QString SIMPLECOLORDATA = "SimpleColorData"; // easier 8-bit color data that works well with XML
const QString GLOBALASSISTANTSCOLOR = "GlobalAssistantsColor";
-const QString PALETTES = "Palettes";
+const QString PALETTES = ResourceType::Palettes;
const QString MIRROR_AXIS = "MirrorAxis";
}
#endif
diff --git a/plugins/impex/libkra/tests/kis_kra_saver_test.cpp b/plugins/impex/libkra/tests/kis_kra_saver_test.cpp
index ec4e6885eb..83b057859d 100644
--- a/plugins/impex/libkra/tests/kis_kra_saver_test.cpp
+++ b/plugins/impex/libkra/tests/kis_kra_saver_test.cpp
@@ -1,552 +1,553 @@
/*
* 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 "kis_kra_saver_test.h"
#include <QTest>
#include <QBitArray>
#include <KisDocument.h>
#include <KoDocumentInfo.h>
#include <KoShapeContainer.h>
#include <KoPathShape.h>
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter.h"
#include "kis_image.h"
#include "kis_pixel_selection.h"
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_clone_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_shape_layer.h"
#include "kis_filter_mask.h"
#include "kis_transparency_mask.h"
#include "kis_selection_mask.h"
#include "kis_selection.h"
#include "kis_fill_painter.h"
#include "kis_shape_selection.h"
#include "util.h"
#include "testutil.h"
#include "kis_keyframe_channel.h"
#include "kis_image_animation_interface.h"
#include "kis_layer_properties_icons.h"
+#include <KisGlobalResourcesInterface.h>
#include "kis_transform_mask_params_interface.h"
#include <generator/kis_generator_registry.h>
#include <KoResourcePaths.h>
#include <sdk/tests/kistest.h>
#include <filestest.h>
const QString KraMimetype = "application/x-krita";
void KisKraSaverTest::initTestCase()
{
- KoResourcePaths::addResourceDir("ko_patterns", QString(SYSTEM_RESOURCES_DATA_DIR) + "/patterns");
+ KoResourcePaths::addResourceDir(ResourceType::Patterns, QString(SYSTEM_RESOURCES_DATA_DIR) + "/patterns");
KisFilterRegistry::instance();
KisGeneratorRegistry::instance();
}
void KisKraSaverTest::testCrashyShapeLayer()
{
/**
* KisShapeLayer used to call setImage from its destructor and
* therefore causing an infinite recursion (when at least one transparency
* mask was preset. This testcase just checks that.
*/
//QScopedPointer<KisDocument> doc(createCompleteDocument(true));
//Q_UNUSED(doc);
}
void KisKraSaverTest::testRoundTrip()
{
KisDocument* doc = createCompleteDocument();
KoColor bgColor(Qt::red, doc->image()->colorSpace());
doc->image()->setDefaultProjectionColor(bgColor);
doc->exportDocumentSync(QUrl::fromLocalFile("roundtriptest.kra"), doc->mimeType());
QStringList list;
KisCountVisitor cv1(list, KoProperties());
doc->image()->rootLayer()->accept(cv1);
KisDocument *doc2 = KisPart::instance()->createDocument();
bool result = doc2->loadNativeFormat("roundtriptest.kra");
QVERIFY(result);
KisCountVisitor cv2(list, KoProperties());
doc2->image()->rootLayer()->accept(cv2);
QCOMPARE(cv1.count(), cv2.count());
// check whether the BG color is saved correctly
QCOMPARE(doc2->image()->defaultProjectionColor(), bgColor);
// test round trip of a transform mask
KisNode* tnode =
TestUtil::findNode(doc2->image()->rootLayer(), "testTransformMask").data();
QVERIFY(tnode);
KisTransformMask *tmask = dynamic_cast<KisTransformMask*>(tnode);
QVERIFY(tmask);
KisDumbTransformMaskParams *params = dynamic_cast<KisDumbTransformMaskParams*>(tmask->transformParams().data());
QVERIFY(params);
QTransform t = params->testingGetTransform();
QCOMPARE(t, createTestingTransform());
delete doc2;
delete doc;
}
void KisKraSaverTest::testSaveEmpty()
{
KisDocument* doc = createEmptyDocument();
doc->exportDocumentSync(QUrl::fromLocalFile("emptytest.kra"), doc->mimeType());
QStringList list;
KisCountVisitor cv1(list, KoProperties());
doc->image()->rootLayer()->accept(cv1);
KisDocument *doc2 = KisPart::instance()->createDocument();
doc2->loadNativeFormat("emptytest.kra");
KisCountVisitor cv2(list, KoProperties());
doc2->image()->rootLayer()->accept(cv2);
QCOMPARE(cv1.count(), cv2.count());
delete doc2;
delete doc;
}
#include <generator/kis_generator.h>
void testRoundTripFillLayerImpl(const QString &testName, KisFilterConfigurationSP config)
{
TestUtil::ReferenceImageChecker chk(testName, "fill_layer");
chk.setFuzzy(2);
QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
// mask parent should be destructed before the document!
QRect refRect(0,0,512,512);
TestUtil::MaskParent p(refRect);
doc->setCurrentImage(p.image);
doc->documentInfo()->setAboutInfo("title", p.image->objectName());
KisSelectionSP selection;
- KisGeneratorLayerSP glayer = new KisGeneratorLayer(p.image, "glayer", config, selection);
+ KisGeneratorLayerSP glayer = new KisGeneratorLayer(p.image, "glayer", config->cloneWithResourcesSnapshot(), selection);
p.image->addNode(glayer, p.image->root(), KisNodeSP());
glayer->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "00_initial_layer_update");
doc->exportDocumentSync(QUrl::fromLocalFile("roundtrip_fill_layer_test.kra"), doc->mimeType());
QScopedPointer<KisDocument> doc2(KisPart::instance()->createDocument());
doc2->loadNativeFormat("roundtrip_fill_layer_test.kra");
doc2->image()->waitForDone();
chk.checkImage(doc2->image(), "01_fill_layer_round_trip");
QVERIFY(chk.testPassed());
}
void KisKraSaverTest::testRoundTripFillLayerColor()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisGeneratorSP generator = KisGeneratorRegistry::instance()->get("color");
Q_ASSERT(generator);
// warning: we pass null paint device to the default constructed value
- KisFilterConfigurationSP config = generator->defaultConfiguration();
+ KisFilterConfigurationSP config = generator->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(config);
QVariant v;
v.setValue(KoColor(Qt::red, cs));
config->setProperty("color", v);
testRoundTripFillLayerImpl("fill_layer_color", config);
}
void KisKraSaverTest::testRoundTripFillLayerPattern()
{
KisGeneratorSP generator = KisGeneratorRegistry::instance()->get("pattern");
QVERIFY(generator);
// warning: we pass null paint device to the default constructed value
- KisFilterConfigurationSP config = generator->defaultConfiguration();
+ KisFilterConfigurationSP config = generator->defaultConfiguration(KisGlobalResourcesInterface::instance());
QVERIFY(config);
QVariant v;
v.setValue(QString("11_drawed_furry.png"));
config->setProperty("pattern", v);
testRoundTripFillLayerImpl("fill_layer_pattern", config);
}
#include "kis_psd_layer_style.h"
void KisKraSaverTest::testRoundTripLayerStyles()
{
TestUtil::ReferenceImageChecker chk("kra_saver_test", "layer_styles");
QRect imageRect(0,0,512,512);
// the document should be created before the image!
QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(new KisSurrogateUndoStore(), imageRect.width(), imageRect.height(), cs, "test image");
KisPaintLayerSP layer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisPaintLayerSP layer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisPaintLayerSP layer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
image->addNode(layer1);
image->addNode(layer2);
image->addNode(layer3);
doc->setCurrentImage(image);
doc->documentInfo()->setAboutInfo("title", image->objectName());
layer1->paintDevice()->fill(QRect(100, 100, 100, 100), KoColor(Qt::red, cs));
layer2->paintDevice()->fill(QRect(200, 200, 100, 100), KoColor(Qt::green, cs));
layer3->paintDevice()->fill(QRect(300, 300, 100, 100), KoColor(Qt::blue, cs));
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->dropShadow()->setEffectEnabled(true);
style->dropShadow()->setAngle(-90);
style->dropShadow()->setUseGlobalLight(false);
- layer1->setLayerStyle(style->clone());
+ layer1->setLayerStyle(style->clone().dynamicCast<KisPSDLayerStyle>());
style->dropShadow()->setAngle(180);
style->dropShadow()->setUseGlobalLight(true);
- layer2->setLayerStyle(style->clone());
+ layer2->setLayerStyle(style->clone().dynamicCast<KisPSDLayerStyle>());
style->dropShadow()->setAngle(90);
style->dropShadow()->setUseGlobalLight(false);
- layer3->setLayerStyle(style->clone());
+ layer3->setLayerStyle(style->clone().dynamicCast<KisPSDLayerStyle>());
image->initialRefreshGraph();
chk.checkImage(image, "00_initial_layers");
doc->exportDocumentSync(QUrl::fromLocalFile("roundtrip_layer_styles.kra"), doc->mimeType());
QScopedPointer<KisDocument> doc2(KisPart::instance()->createDocument());
doc2->loadNativeFormat("roundtrip_layer_styles.kra");
doc2->image()->waitForDone();
chk.checkImage(doc2->image(), "00_initial_layers");
QVERIFY(chk.testPassed());
}
void KisKraSaverTest::testRoundTripAnimation()
{
QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
QRect imageRect(0,0,512,512);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(new KisSurrogateUndoStore(), imageRect.width(), imageRect.height(), cs, "test image");
KisPaintLayerSP layer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
image->addNode(layer1);
layer1->paintDevice()->fill(QRect(100, 100, 50, 50), KoColor(Qt::black, cs));
layer1->paintDevice()->setDefaultPixel(KoColor(Qt::red, cs));
KUndo2Command parentCommand;
layer1->enableAnimation();
KisKeyframeChannel *rasterChannel = layer1->getKeyframeChannel(KisKeyframeChannel::Content.id(), true);
QVERIFY(rasterChannel);
rasterChannel->addKeyframe(10, &parentCommand);
image->animationInterface()->switchCurrentTimeAsync(10);
image->waitForDone();
layer1->paintDevice()->fill(QRect(200, 50, 10, 10), KoColor(Qt::black, cs));
layer1->paintDevice()->moveTo(25, 15);
layer1->paintDevice()->setDefaultPixel(KoColor(Qt::green, cs));
rasterChannel->addKeyframe(20, &parentCommand);
image->animationInterface()->switchCurrentTimeAsync(20);
image->waitForDone();
layer1->paintDevice()->fill(QRect(150, 200, 30, 30), KoColor(Qt::black, cs));
layer1->paintDevice()->moveTo(100, 50);
layer1->paintDevice()->setDefaultPixel(KoColor(Qt::blue, cs));
QVERIFY(!layer1->useInTimeline());
layer1->setUseInTimeline(true);
doc->setCurrentImage(image);
doc->exportDocumentSync(QUrl::fromLocalFile("roundtrip_animation.kra"), doc->mimeType());
QScopedPointer<KisDocument> doc2(KisPart::instance()->createDocument());
doc2->loadNativeFormat("roundtrip_animation.kra");
KisImageSP image2 = doc2->image();
KisNodeSP node = image2->root()->firstChild();
QVERIFY(node->inherits("KisPaintLayer"));
KisPaintLayerSP layer2 = qobject_cast<KisPaintLayer*>(node.data());
cs = layer2->paintDevice()->colorSpace();
QCOMPARE(image2->animationInterface()->currentTime(), 20);
KisKeyframeChannel *channel = layer2->getKeyframeChannel(KisKeyframeChannel::Content.id());
QVERIFY(channel);
QCOMPARE(channel->keyframeCount(), 3);
image2->animationInterface()->switchCurrentTimeAsync(0);
image2->waitForDone();
QCOMPARE(layer2->paintDevice()->nonDefaultPixelArea(), QRect(64, 64, 128, 128));
QCOMPARE(layer2->paintDevice()->x(), 0);
QCOMPARE(layer2->paintDevice()->y(), 0);
QCOMPARE(layer2->paintDevice()->defaultPixel(), KoColor(Qt::red, cs));
image2->animationInterface()->switchCurrentTimeAsync(10);
image2->waitForDone();
QCOMPARE(layer2->paintDevice()->nonDefaultPixelArea(), QRect(217, 15, 64, 64));
QCOMPARE(layer2->paintDevice()->x(), 25);
QCOMPARE(layer2->paintDevice()->y(), 15);
QCOMPARE(layer2->paintDevice()->defaultPixel(), KoColor(Qt::green, cs));
image2->animationInterface()->switchCurrentTimeAsync(20);
image2->waitForDone();
QCOMPARE(layer2->paintDevice()->nonDefaultPixelArea(), QRect(228, 242, 64, 64));
QCOMPARE(layer2->paintDevice()->x(), 100);
QCOMPARE(layer2->paintDevice()->y(), 50);
QCOMPARE(layer2->paintDevice()->defaultPixel(), KoColor(Qt::blue, cs));
QVERIFY(layer2->useInTimeline());
}
#include "lazybrush/kis_lazy_fill_tools.h"
void KisKraSaverTest::testRoundTripColorizeMask()
{
QRect imageRect(0,0,512,512);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
const KoColorSpace * weirdCS = KoColorSpaceRegistry::instance()->rgb16();
QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
KisImageSP image = new KisImage(new KisSurrogateUndoStore(), imageRect.width(), imageRect.height(), cs, "test image");
doc->setCurrentImage(image);
KisPaintLayerSP layer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, weirdCS);
image->addNode(layer1);
KisColorizeMaskSP mask = new KisColorizeMask();
image->addNode(mask, layer1);
mask->initializeCompositeOp();
delete mask->setColorSpace(layer1->colorSpace());
{
KisPaintDeviceSP key1 = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
key1->fill(QRect(50,50,10,20), KoColor(Qt::black, key1->colorSpace()));
mask->testingAddKeyStroke(key1, KoColor(Qt::green, layer1->colorSpace()));
// KIS_DUMP_DEVICE_2(key1, refRect, "key1", "dd");
}
{
KisPaintDeviceSP key2 = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
key2->fill(QRect(150,50,10,20), KoColor(Qt::black, key2->colorSpace()));
mask->testingAddKeyStroke(key2, KoColor(Qt::red, layer1->colorSpace()));
// KIS_DUMP_DEVICE_2(key2, refRect, "key2", "dd");
}
{
KisPaintDeviceSP key3 = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
key3->fill(QRect(0,0,10,10), KoColor(Qt::black, key3->colorSpace()));
mask->testingAddKeyStroke(key3, KoColor(Qt::blue, layer1->colorSpace()), true);
// KIS_DUMP_DEVICE_2(key3, refRect, "key3", "dd");
}
KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, false, image);
KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, false, image);
doc->exportDocumentSync(QUrl::fromLocalFile("roundtrip_colorize.kra"), doc->mimeType());
QScopedPointer<KisDocument> doc2(KisPart::instance()->createDocument());
doc2->loadNativeFormat("roundtrip_colorize.kra");
KisImageSP image2 = doc2->image();
KisNodeSP node = image2->root()->firstChild()->firstChild();
KisColorizeMaskSP mask2 = dynamic_cast<KisColorizeMask*>(node.data());
QVERIFY(mask2);
QCOMPARE(mask2->compositeOpId(), mask->compositeOpId());
QCOMPARE(mask2->colorSpace(), mask->colorSpace());
QCOMPARE(KisLayerPropertiesIcons::nodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, true).toBool(), false);
QCOMPARE(KisLayerPropertiesIcons::nodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, true).toBool(), false);
QList<KisLazyFillTools::KeyStroke> strokes = mask->fetchKeyStrokesDirect();
qDebug() << ppVar(strokes.size());
QCOMPARE(strokes[0].dev->exactBounds(), QRect(50,50,10,20));
QCOMPARE(strokes[0].isTransparent, false);
QCOMPARE(strokes[0].color.colorSpace(), weirdCS);
QCOMPARE(strokes[1].dev->exactBounds(), QRect(150,50,10,20));
QCOMPARE(strokes[1].isTransparent, false);
QCOMPARE(strokes[1].color.colorSpace(), weirdCS);
QCOMPARE(strokes[2].dev->exactBounds(), QRect(0,0,10,10));
QCOMPARE(strokes[2].isTransparent, true);
QCOMPARE(strokes[2].color.colorSpace(), weirdCS);
}
#include <KoColorBackground.h>
void KisKraSaverTest::testRoundTripShapeLayer()
{
TestUtil::ReferenceImageChecker chk("kra_saver_test", "shape_layer");
QRect refRect(0,0,512,512);
QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
TestUtil::MaskParent p(refRect);
const qreal resolution = 144.0 / 72.0;
p.image->setResolution(resolution, resolution);
doc->setCurrentImage(p.image);
doc->documentInfo()->setAboutInfo("title", p.image->objectName());
KoPathShape* path = new KoPathShape();
path->setShapeId(KoPathShapeId);
path->moveTo(QPointF(10, 10));
path->lineTo(QPointF( 10, 110));
path->lineTo(QPointF(110, 110));
path->lineTo(QPointF(110, 10));
path->close();
path->normalize();
path->setBackground(toQShared(new KoColorBackground(Qt::red)));
path->setName("my_precious_shape");
KisShapeLayerSP shapeLayer = new KisShapeLayer(doc->shapeController(), p.image, "shapeLayer1", 75);
shapeLayer->addShape(path);
p.image->addNode(shapeLayer);
shapeLayer->setDirty();
qApp->processEvents();
p.image->waitForDone();
chk.checkImage(p.image, "00_initial_layer_update");
doc->exportDocumentSync(QUrl::fromLocalFile("roundtrip_shapelayer_test.kra"), doc->mimeType());
QScopedPointer<KisDocument> doc2(KisPart::instance()->createDocument());
doc2->loadNativeFormat("roundtrip_shapelayer_test.kra");
qApp->processEvents();
doc2->image()->waitForDone();
QCOMPARE(doc2->image()->xRes(), resolution);
QCOMPARE(doc2->image()->yRes(), resolution);
chk.checkImage(doc2->image(), "01_shape_layer_round_trip");
QVERIFY(chk.testPassed());
}
void KisKraSaverTest::testRoundTripShapeSelection()
{
TestUtil::ReferenceImageChecker chk("kra_saver_test", "shape_selection");
QRect refRect(0,0,512,512);
QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
TestUtil::MaskParent p(refRect);
doc->setCurrentImage(p.image);
const qreal resolution = 144.0 / 72.0;
p.image->setResolution(resolution, resolution);
doc->setCurrentImage(p.image);
doc->documentInfo()->setAboutInfo("title", p.image->objectName());
p.layer->paintDevice()->setDefaultPixel(KoColor(Qt::green, p.layer->colorSpace()));
KisSelectionSP selection = new KisSelection(p.layer->paintDevice()->defaultBounds());
KisShapeSelection *shapeSelection = new KisShapeSelection(doc->shapeController(), p.image, selection);
selection->setShapeSelection(shapeSelection);
KoPathShape* path = new KoPathShape();
path->setShapeId(KoPathShapeId);
path->moveTo(QPointF(10, 10));
path->lineTo(QPointF( 10, 110));
path->lineTo(QPointF(110, 110));
path->lineTo(QPointF(110, 10));
path->close();
path->normalize();
path->setBackground(toQShared(new KoColorBackground(Qt::red)));
path->setName("my_precious_shape");
shapeSelection->addShape(path);
KisTransparencyMaskSP tmask = new KisTransparencyMask();
tmask->setSelection(selection);
p.image->addNode(tmask, p.layer);
tmask->setDirty(p.image->bounds());
qApp->processEvents();
p.image->waitForDone();
chk.checkImage(p.image, "00_initial_shape_selection");
doc->exportDocumentSync(QUrl::fromLocalFile("roundtrip_shapeselection_test.kra"), doc->mimeType());
QScopedPointer<KisDocument> doc2(KisPart::instance()->createDocument());
doc2->loadNativeFormat("roundtrip_shapeselection_test.kra");
qApp->processEvents();
doc2->image()->waitForDone();
QCOMPARE(doc2->image()->xRes(), resolution);
QCOMPARE(doc2->image()->yRes(), resolution);
chk.checkImage(doc2->image(), "00_initial_shape_selection");
KisNodeSP node = doc2->image()->root()->firstChild()->firstChild();
KisTransparencyMask *newMask = dynamic_cast<KisTransparencyMask*>(node.data());
QVERIFY(newMask);
QVERIFY(newMask->selection()->hasShapeSelection());
QVERIFY(chk.testPassed());
}
void KisKraSaverTest::testExportToReadonly()
{
TestUtil::testExportToReadonly(QString(FILES_DATA_DIR), KraMimetype);
}
KISTEST_MAIN(KisKraSaverTest)
diff --git a/plugins/impex/libkra/tests/util.h b/plugins/impex/libkra/tests/util.h
index 3f04c5fe60..ce4c705662 100644
--- a/plugins/impex/libkra/tests/util.h
+++ b/plugins/impex/libkra/tests/util.h
@@ -1,224 +1,222 @@
/*
* 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 _UTIL_H_
#define _UTIL_H_
#include <QTest>
#include <QBitArray>
#include <KisDocument.h>
#include <KoDocumentInfo.h>
#include <KoColorSpaceRegistry.h>
#include <KoShapeContainer.h>
#include <KoColorSpace.h>
#include <KoPathShape.h>
#include <kis_count_visitor.h>
#include "kis_types.h"
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter.h"
#include "KisPart.h"
#include "kis_image.h"
#include "kis_pixel_selection.h"
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_clone_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_shape_layer.h"
#include "kis_filter_mask.h"
#include "kis_transparency_mask.h"
#include "kis_selection_mask.h"
#include "kis_selection.h"
#include "kis_fill_painter.h"
#include "kis_shape_selection.h"
#include "kis_default_bounds.h"
#include "kis_transform_mask_params_interface.h"
+#include <KisGlobalResourcesInterface.h>
KisSelectionSP createPixelSelection(KisPaintDeviceSP paintDevice)
{
KisSelectionSP pixelSelection = new KisSelection(new KisSelectionDefaultBounds(paintDevice));
KisFillPainter gc(pixelSelection->pixelSelection());
gc.fillRect(10, 10, 200, 200, KoColor(gc.device()->colorSpace()));
gc.fillRect(150, 150, 200, 200, KoColor(QColor(100, 100, 100, 100), gc.device()->colorSpace()));
gc.end();
return pixelSelection;
}
KisSelectionSP createVectorSelection(KisPaintDeviceSP paintDevice, KisImageSP image, KoShapeControllerBase *shapeController)
{
KisSelectionSP vectorSelection = new KisSelection(new KisSelectionDefaultBounds(paintDevice));
KoPathShape* path = new KoPathShape();
path->setShapeId(KoPathShapeId);
path->moveTo(QPointF(10, 10));
path->lineTo(QPointF(10, 10) + QPointF(100, 0));
path->lineTo(QPointF(100, 100));
path->lineTo(QPointF(10, 10) + QPointF(0, 100));
path->close();
path->normalize();
KisShapeSelection* shapeSelection = new KisShapeSelection(shapeController, image, vectorSelection);
shapeSelection->addShape(path);
vectorSelection->setShapeSelection(shapeSelection);
return vectorSelection;
}
QTransform createTestingTransform() {
return QTransform(1,2,3,4,5,6,7,8,9);
}
KisDocument* createCompleteDocument()
{
KisImageSP image = new KisImage(0, 1024, 1024, KoColorSpaceRegistry::instance()->rgb8(), "test for roundtrip");
KisDocument *doc = qobject_cast<KisDocument*>(KisPart::instance()->createDocument());
doc->setCurrentImage(image);
doc->documentInfo()->setAboutInfo("title", image->objectName());
KisGroupLayerSP group1 = new KisGroupLayer(image, "group1", 50);
KisGroupLayerSP group2 = new KisGroupLayer(image, "group2", 100);
KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paintlayer1", OPACITY_OPAQUE_U8);
paintLayer1->setUserLocked(true);
QBitArray channelFlags(4);
channelFlags[0] = true;
channelFlags[2] = true;
paintLayer1->setChannelFlags(channelFlags);
{
KisFillPainter gc(paintLayer1->paintDevice());
gc.fillRect(10, 10, 200, 200, KoColor(Qt::red, paintLayer1->paintDevice()->colorSpace()));
gc.end();
}
KisPaintLayerSP paintLayer2 = new KisPaintLayer(image, "paintlayer2", OPACITY_TRANSPARENT_U8, KoColorSpaceRegistry::instance()->lab16());
paintLayer2->setVisible(false);
{
KisFillPainter gc(paintLayer2->paintDevice());
gc.fillRect(0, 0, 900, 1024, KoColor(QColor(10, 20, 30), paintLayer2->paintDevice()->colorSpace()));
gc.end();
}
KisCloneLayerSP cloneLayer1 = new KisCloneLayer(group1, image, "clonelayer1", 150);
cloneLayer1->setX(100);
cloneLayer1->setY(100);
KisSelectionSP pixelSelection = createPixelSelection(paintLayer1->paintDevice());
- KisFilterConfigurationSP kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration();
+ KisFilterConfigurationSP kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(kfc);
- KisAdjustmentLayerSP adjustmentLayer1 = new KisAdjustmentLayer(image, "adjustmentLayer1", kfc, pixelSelection);
- kfc = 0; // kfc cannot be shared!
+ KisAdjustmentLayerSP adjustmentLayer1 = new KisAdjustmentLayer(image, "adjustmentLayer1", kfc->cloneWithResourcesSnapshot(), pixelSelection);
KisSelectionSP vectorSelection = createVectorSelection(paintLayer2->paintDevice(), image, doc->shapeController());
- kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration();
- KisAdjustmentLayerSP adjustmentLayer2 = new KisAdjustmentLayer(image, "adjustmentLayer2", kfc, vectorSelection);
- kfc = 0; // kfc cannot be shared!
+ KisAdjustmentLayerSP adjustmentLayer2 = new KisAdjustmentLayer(image, "adjustmentLayer2", kfc->cloneWithResourcesSnapshot(), vectorSelection);
image->addNode(paintLayer1);
image->addNode(group1);
image->addNode(paintLayer2, group1);
image->addNode(group2);
image->addNode(cloneLayer1, group2);
image->addNode(adjustmentLayer1, group2);
// KoShapeContainer * parentContainer =
// dynamic_cast<KoShapeContainer*>(doc->shapeForNode(group1));
KoPathShape* path = new KoPathShape();
path->setShapeId(KoPathShapeId);
path->moveTo(QPointF(10, 10));
path->lineTo(QPointF(10, 10) + QPointF(100, 0));
path->lineTo(QPointF(100, 100));
path->lineTo(QPointF(10, 10) + QPointF(0, 100));
path->close();
path->normalize();
KisShapeLayerSP shapeLayer = new KisShapeLayer(doc->shapeController(), image, "shapeLayer1", 75);
shapeLayer->addShape(path);
image->addNode(shapeLayer, group1);
image->addNode(adjustmentLayer2, group1);
KisFilterMaskSP filterMask1 = new KisFilterMask();
filterMask1->setName("filterMask1");
- kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration();
- filterMask1->setFilter(kfc);
+ kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(KisGlobalResourcesInterface::instance());
+ filterMask1->setFilter(kfc->cloneWithResourcesSnapshot());
kfc = 0; // kfc cannot be shared!
filterMask1->setSelection(createPixelSelection(paintLayer1->paintDevice()));
image->addNode(filterMask1, paintLayer1);
KisFilterMaskSP filterMask2 = new KisFilterMask();
filterMask2->setName("filterMask2");
- kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration();
+ kfc = KisFilterRegistry::instance()->get("pixelize")->defaultConfiguration(KisGlobalResourcesInterface::instance());
filterMask2->setFilter(kfc);
kfc = 0; // kfc cannot be shared!
filterMask2->setSelection(createVectorSelection(paintLayer2->paintDevice(), image, doc->shapeController()));
image->addNode(filterMask2, paintLayer2);
KisTransparencyMaskSP transparencyMask1 = new KisTransparencyMask();
transparencyMask1->setName("transparencyMask1");
transparencyMask1->setSelection(createPixelSelection(paintLayer1->paintDevice()));
image->addNode(transparencyMask1, group1);
KisTransparencyMaskSP transparencyMask2 = new KisTransparencyMask();
transparencyMask2->setName("transparencyMask2");
transparencyMask2->setSelection(createPixelSelection(paintLayer1->paintDevice()));
image->addNode(transparencyMask2, group2);
KisSelectionMaskSP selectionMask1 = new KisSelectionMask(image);
image->addNode(selectionMask1, paintLayer1);
selectionMask1->setName("selectionMask1");
selectionMask1->setSelection(createPixelSelection(paintLayer1->paintDevice()));
KisSelectionMaskSP selectionMask2 = new KisSelectionMask(image);
selectionMask2->setName("selectionMask2");
selectionMask2->setSelection(createPixelSelection(paintLayer2->paintDevice()));
image->addNode(selectionMask2, paintLayer2);
KisTransformMaskSP transformMask = new KisTransformMask();
transformMask->setName("testTransformMask");
transformMask->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(createTestingTransform())));
image->addNode(transformMask, paintLayer2);
return doc;
}
KisDocument *createEmptyDocument()
{
KisImageSP image = new KisImage(0, 1024, 1024, KoColorSpaceRegistry::instance()->rgb8(), "test for roundtrip");
KisDocument *doc = qobject_cast<KisDocument*>(KisPart::instance()->createDocument());
doc->setCurrentImage(image);
doc->documentInfo()->setAboutInfo("title", image->objectName());
return doc;
}
#endif
diff --git a/plugins/impex/ora/kis_open_raster_stack_load_visitor.cpp b/plugins/impex/ora/kis_open_raster_stack_load_visitor.cpp
index 589a5c07f3..1f9fc4c9d6 100644
--- a/plugins/impex/ora/kis_open_raster_stack_load_visitor.cpp
+++ b/plugins/impex/ora/kis_open_raster_stack_load_visitor.cpp
@@ -1,251 +1,253 @@
/*
* Copyright (c) 2006-2007,2009 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_open_raster_stack_load_visitor.h"
#include <QDomElement>
#include <QDomNode>
#include <KoColorSpaceRegistry.h>
// Includes from krita/image
#include <kis_adjustment_layer.h>
#include <filter/kis_filter.h>
#include <filter/kis_filter_registry.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <KoCompositeOpRegistry.h>
#include <kis_filter_configuration.h>
+#include <KisGlobalResourcesInterface.h>
#include <kis_paint_layer.h>
#include <kis_png_converter.h>
#include <kis_selection.h>
#include <kis_dom_utils.h>
#include "KisDocument.h"
#include "kis_open_raster_load_context.h"
struct KisOpenRasterStackLoadVisitor::Private {
KisImageSP image;
vKisNodeSP activeNodes;
KisUndoStore* undoStore;
KisOpenRasterLoadContext* loadContext;
double xRes;
double yRes;
};
KisOpenRasterStackLoadVisitor::KisOpenRasterStackLoadVisitor(KisUndoStore* undoStore, KisOpenRasterLoadContext* orlc)
: d(new Private)
{
d->undoStore = undoStore;
d->loadContext = orlc;
}
KisOpenRasterStackLoadVisitor::~KisOpenRasterStackLoadVisitor()
{
delete d;
}
KisImageSP KisOpenRasterStackLoadVisitor::image()
{
return d->image;
}
vKisNodeSP KisOpenRasterStackLoadVisitor::activeNodes()
{
return d->activeNodes;
}
void KisOpenRasterStackLoadVisitor::loadImage()
{
QDomDocument doc = d->loadContext->loadStack();
for (QDomNode node = doc.firstChild(); !node.isNull(); node = node.nextSibling()) {
if (node.isElement() && node.nodeName() == "image") { // it's the image root
QDomElement subelem = node.toElement();
int width = 0;
if (!subelem.attribute("w").isNull()) {
width = subelem.attribute("w").toInt();
}
int height = 0;
if (!subelem.attribute("h").isNull()) {
height = subelem.attribute("h").toInt();
}
d->xRes = 75.0/72; // Setting the default value of the X Resolution = 75ppi
if (!subelem.attribute("xres").isNull()){
d->xRes = (KisDomUtils::toDouble(subelem.attribute("xres")) / 72);
}
d->yRes = 75.0/72;
if (!subelem.attribute("yres").isNull()){
d->yRes = (KisDomUtils::toDouble(subelem.attribute("yres")) / 72);
}
dbgFile << ppVar(width) << ppVar(height);
d->image = new KisImage(d->undoStore, width, height, KoColorSpaceRegistry::instance()->rgb8(), "OpenRaster Image (name)");
for (QDomNode node2 = node.firstChild(); !node2.isNull(); node2 = node2.nextSibling()) {
if (node2.isElement() && node2.nodeName() == "stack") { // it's the root layer !
QDomElement subelem2 = node2.toElement();
loadGroupLayer(subelem2, d->image->rootLayer());
break;
}
}
}
}
}
void KisOpenRasterStackLoadVisitor::loadLayerInfo(const QDomElement& elem, KisLayerSP layer)
{
layer->setName(elem.attribute("name"));
layer->setX(elem.attribute("x").toInt());
layer->setY(elem.attribute("y").toInt());
if (elem.attribute("visibility") == "hidden") {
layer->setVisible(false);
} else {
layer->setVisible(true);
}
if (elem.hasAttribute("edit-locked")) {
layer->setUserLocked(elem.attribute("edit-locked") == "true");
}
if (elem.hasAttribute("selected") && elem.attribute("selected") == "true") {
d->activeNodes.append(layer);
}
QString compop = elem.attribute("composite-op");
if (compop.startsWith("svg:")) {
//we don't have a 'composite op clear' despite the registery reserving a string for it, doesn't matter, ora doesn't use it.
//if (compop == "svg:clear") layer->setCompositeOpId(COMPOSITE_CLEAR);
if (compop == "svg:src-over") layer->setCompositeOpId(COMPOSITE_OVER);
//not part of the spec.
//if (compop == "svg:dst-over") layer->setCompositeOpId(COMPOSITE_BEHIND);
//dst-in "The source that overlaps the destination, replaces the destination."
if (compop == "svg:dst-in") layer->setCompositeOpId(COMPOSITE_DESTINATION_IN);
//dst-out "dst is placed, where it falls outside of the source."
if (compop == "svg:dst-out") layer->setCompositeOpId(COMPOSITE_ERASE);
//src-atop "Destination which overlaps the source replaces the source. Source is placed elsewhere."
//this is basically our alpha-inherit.
if (compop == "svg:src-atop") layer->disableAlphaChannel(true);
//dst-atop
if (compop == "svg:dst-atop") layer->setCompositeOpId(COMPOSITE_DESTINATION_ATOP);
//plus is svg standard's way of saying addition... photoshop calls this linear dodge, btw, maybe make a similar alias?
if (compop == "svg:plus") layer->setCompositeOpId(COMPOSITE_ADD);
if (compop == "svg:multiply") layer->setCompositeOpId(COMPOSITE_MULT);
if (compop == "svg:screen") layer->setCompositeOpId(COMPOSITE_SCREEN);
if (compop == "svg:overlay") layer->setCompositeOpId(COMPOSITE_OVERLAY);
if (compop == "svg:darken") layer->setCompositeOpId(COMPOSITE_DARKEN);
if (compop == "svg:lighten") layer->setCompositeOpId(COMPOSITE_LIGHTEN);
if (compop == "svg:color-dodge") layer->setCompositeOpId(COMPOSITE_DODGE);
if (compop == "svg:color-burn") layer->setCompositeOpId(COMPOSITE_BURN);
if (compop == "svg:hard-light") layer->setCompositeOpId(COMPOSITE_HARD_LIGHT);
if (compop == "svg:soft-light") layer->setCompositeOpId(COMPOSITE_SOFT_LIGHT_SVG);
if (compop == "svg:difference") layer->setCompositeOpId(COMPOSITE_DIFF);
if (compop == "svg:color") layer->setCompositeOpId(COMPOSITE_COLOR);
if (compop == "svg:luminosity") layer->setCompositeOpId(COMPOSITE_LUMINIZE);
if (compop == "svg:hue") layer->setCompositeOpId(COMPOSITE_HUE);
if (compop == "svg:saturation") layer->setCompositeOpId(COMPOSITE_SATURATION);
//Exclusion isn't in the official list.
//if (compop == "svg:exclusion") layer->setCompositeOpId(COMPOSITE_EXCLUSION);
}
else if (compop.startsWith("krita:")) {
compop = compop.remove(0, 6);
layer->setCompositeOpId(compop);
}
else {
// to fix old bugs in krita's ora export
if (compop == "color-dodge") layer->setCompositeOpId(COMPOSITE_DODGE);
if (compop == "difference") layer->setCompositeOpId(COMPOSITE_DIFF);
if (compop == "svg:add") layer->setCompositeOpId(COMPOSITE_ADD);
}
}
void KisOpenRasterStackLoadVisitor::loadAdjustmentLayer(const QDomElement& elem, KisAdjustmentLayerSP aL)
{
loadLayerInfo(elem, aL);
}
void KisOpenRasterStackLoadVisitor::loadPaintLayer(const QDomElement& elem, KisPaintLayerSP pL)
{
loadLayerInfo(elem, pL);
dbgFile << "Loading was unsuccessful";
}
void KisOpenRasterStackLoadVisitor::loadGroupLayer(const QDomElement& elem, KisGroupLayerSP groupLayer)
{
dbgFile << "Loading group layer" << d->image;
loadLayerInfo(elem, groupLayer);
for (QDomNode node = elem.firstChild(); !node.isNull(); node = node.nextSibling()) {
if (node.isElement()) {
QDomElement subelem = node.toElement();
if (node.nodeName() == "stack") {
double opacity = 1.0;
if (!subelem.attribute("opacity").isNull()) {
opacity = KisDomUtils::toDouble(subelem.attribute("opacity", "1.0"));
}
KisGroupLayerSP layer = new KisGroupLayer(d->image, "", opacity * 255);
bool passThrough = true;
if (subelem.attribute("isolation")=="isolate") {
passThrough = false;
}
layer->setPassThroughMode(passThrough);
d->image->addNode(layer, groupLayer.data(), 0);
loadGroupLayer(subelem, layer);
} else if (node.nodeName() == "layer") {
QString filename = subelem.attribute("src");
if (!filename.isNull()) {
const qreal opacity = KisDomUtils::toDouble(subelem.attribute("opacity", "1.0"));
KisImageSP pngImage = d->loadContext->loadDeviceData(filename);
if (pngImage) {
// If ORA doesn't have resolution info, load the default value(75 ppi) else fetch from stack.xml
d->image->setResolution(d->xRes, d->yRes);
// now get the device
KisPaintDeviceSP device = pngImage->projection();
KisPaintLayerSP layer = new KisPaintLayer(groupLayer->image() , "", opacity * 255, device);
d->image->addNode(layer, groupLayer, 0);
loadPaintLayer(subelem, layer);
dbgFile << "Loading was successful";
}
}
} else if (node.nodeName() == "filter") {
QString filterType = subelem.attribute("type");
QStringList filterTypeSplit = filterType.split(':');
KisFilterSP f = 0;
if (filterTypeSplit[0] == "applications" && filterTypeSplit[1] == "krita") {
f = KisFilterRegistry::instance()->value(filterTypeSplit[2]);
}
- KisFilterConfigurationSP kfc = f->factoryConfiguration();
+ KisFilterConfigurationSP kfc = f->factoryConfiguration(KisGlobalResourcesInterface::instance());
+ kfc->createLocalResourcesSnapshot();
KisAdjustmentLayerSP layer = new KisAdjustmentLayer(groupLayer->image() , "", kfc, KisSelectionSP(0));
d->image->addNode(layer.data(), groupLayer.data(), 0);
loadAdjustmentLayer(subelem, layer);
} else {
dbgFile << "Unknown element : " << node.nodeName();
}
}
}
}
diff --git a/plugins/impex/psd/psd_layer_section.cpp b/plugins/impex/psd/psd_layer_section.cpp
index ac15eaa1e8..7b4d98527b 100644
--- a/plugins/impex/psd/psd_layer_section.cpp
+++ b/plugins/impex/psd/psd_layer_section.cpp
@@ -1,582 +1,582 @@
/*
* 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.
*/
#include "psd_layer_section.h"
#include <QIODevice>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <kis_debug.h>
#include <kis_node.h>
#include <kis_paint_layer.h>
#include <kis_group_layer.h>
#include <kis_effect_mask.h>
#include <kis_image.h>
#include "kis_dom_utils.h"
#include "psd_header.h"
#include "psd_utils.h"
#include "compression.h"
#include <asl/kis_offset_on_exit_verifier.h>
#include <asl/kis_asl_reader_utils.h>
#include <kis_asl_layer_style_serializer.h>
#include <asl/kis_asl_writer_utils.h>
PSDLayerMaskSection::PSDLayerMaskSection(const PSDHeader& header)
: globalInfoSection(header)
, m_header(header)
{
}
PSDLayerMaskSection::~PSDLayerMaskSection()
{
qDeleteAll(layers);
}
bool PSDLayerMaskSection::read(QIODevice* io)
{
bool retval = true; // be optimistic! <:-)
try {
retval = readImpl(io);
} catch (KisAslReaderUtils::ASLParseException &e) {
warnKrita << "WARNING: PSD (emb. pattern):" << e.what();
retval = false;
}
return retval;
}
bool PSDLayerMaskSection::readLayerInfoImpl(QIODevice* io)
{
quint32 layerInfoSectionSize = 0;
SAFE_READ_EX(io, layerInfoSectionSize);
if (layerInfoSectionSize & 0x1) {
warnKrita << "WARNING: layerInfoSectionSize is NOT even! Fixing...";
layerInfoSectionSize++;
}
{
SETUP_OFFSET_VERIFIER(layerInfoSectionTag, io, layerInfoSectionSize, 0);
dbgFile << "Layer info block size" << layerInfoSectionSize;
if (layerInfoSectionSize > 0 ) {
if (!psdread(io, &nLayers) || nLayers == 0) {
error = QString("Could not read read number of layers or no layers in image. %1").arg(nLayers);
return false;
}
hasTransparency = nLayers < 0; // first alpha channel is the alpha channel of the projection.
nLayers = qAbs(nLayers);
dbgFile << "Number of layers:" << nLayers;
dbgFile << "Has separate projection transparency:" << hasTransparency;
for (int i = 0; i < nLayers; ++i) {
dbgFile << "Going to read layer" << i << "pos" << io->pos();
dbgFile << "== Enter PSDLayerRecord";
QScopedPointer<PSDLayerRecord> layerRecord(new PSDLayerRecord(m_header));
if (!layerRecord->read(io)) {
error = QString("Could not load layer %1: %2").arg(i).arg(layerRecord->error);
return false;
}
dbgFile << "== Leave PSDLayerRecord";
dbgFile << "Finished reading layer" << i << layerRecord->layerName << "blending mode"
<< layerRecord->blendModeKey << io->pos()
<< "Number of channels:" << layerRecord->channelInfoRecords.size();
layers << layerRecord.take();
}
}
// get the positions for the channels belonging to each layer
for (int i = 0; i < nLayers; ++i) {
dbgFile << "Going to seek channel positions for layer" << i << "pos" << io->pos();
if (i > layers.size()) {
error = QString("Expected layer %1, but only have %2 layers").arg(i).arg(layers.size());
return false;
}
PSDLayerRecord *layerRecord = layers.at(i);
for (int j = 0; j < layerRecord->nChannels; ++j) {
// save the current location so we can jump beyond this block later on.
quint64 channelStartPos = io->pos();
dbgFile << "\tReading channel image data for channel" << j << "from pos" << io->pos();
KIS_ASSERT_RECOVER(j < layerRecord->channelInfoRecords.size()) { return false; }
ChannelInfo* channelInfo = layerRecord->channelInfoRecords.at(j);
quint16 compressionType;
if (!psdread(io, &compressionType)) {
error = "Could not read compression type for channel";
return false;
}
channelInfo->compressionType = (Compression::CompressionType)compressionType;
dbgFile << "\t\tChannel" << j << "has compression type" << compressionType;
QRect channelRect = layerRecord->channelRect(channelInfo);
// read the rle row lengths;
if (channelInfo->compressionType == Compression::RLE) {
for(qint64 row = 0; row < channelRect.height(); ++row) {
//dbgFile << "Reading the RLE bytecount position of row" << row << "at pos" << io->pos();
quint32 byteCount;
if (m_header.version == 1) {
quint16 _byteCount;
if (!psdread(io, &_byteCount)) {
error = QString("Could not read byteCount for rle-encoded channel");
return 0;
}
byteCount = _byteCount;
}
else {
if (!psdread(io, &byteCount)) {
error = QString("Could not read byteCount for rle-encoded channel");
return 0;
}
}
////dbgFile << "rle byte count" << byteCount;
channelInfo->rleRowLengths << byteCount;
}
}
// we're beyond all the length bytes, rle bytes and whatever, this is the
// location of the real pixel data
channelInfo->channelDataStart = io->pos();
dbgFile << "\t\tstart" << channelStartPos
<< "data start" << channelInfo->channelDataStart
<< "data length" << channelInfo->channelDataLength
<< "pos" << io->pos();
// make sure we are at the start of the next channel data block
io->seek(channelStartPos + channelInfo->channelDataLength);
// this is the length of the actual channel data bytes
channelInfo->channelDataLength = channelInfo->channelDataLength - (channelInfo->channelDataStart - channelStartPos);
dbgFile << "\t\tchannel record" << j << "for layer" << i << "with id" << channelInfo->channelId
<< "starting position" << channelInfo->channelDataStart
<< "with length" << channelInfo->channelDataLength
<< "and has compression type" << channelInfo->compressionType;
}
}
}
return true;
}
bool PSDLayerMaskSection::readImpl(QIODevice* io)
{
dbgFile << "reading layer section. Pos:" << io->pos() << "bytes left:" << io->bytesAvailable();
layerMaskBlockSize = 0;
if (m_header.version == 1) {
quint32 _layerMaskBlockSize = 0;
if (!psdread(io, &_layerMaskBlockSize) || _layerMaskBlockSize > (quint64)io->bytesAvailable()) {
error = QString("Could not read layer + mask block size. Got %1. Bytes left %2")
.arg(_layerMaskBlockSize).arg(io->bytesAvailable());
return false;
}
layerMaskBlockSize = _layerMaskBlockSize;
}
else if (m_header.version == 2) {
if (!psdread(io, &layerMaskBlockSize) || layerMaskBlockSize > (quint64)io->bytesAvailable()) {
error = QString("Could not read layer + mask block size. Got %1. Bytes left %2")
.arg(layerMaskBlockSize).arg(io->bytesAvailable());
return false;
}
}
quint64 start = io->pos();
dbgFile << "layer + mask section size" << layerMaskBlockSize;
if (layerMaskBlockSize == 0) {
dbgFile << "No layer + mask info, so no layers, only a background layer";
return true;
}
if (!readLayerInfoImpl(io)) {
return false;
}
quint32 globalMaskBlockLength;
if (!psdread(io, &globalMaskBlockLength)) {
error = "Could not read global mask info block";
return false;
}
if (globalMaskBlockLength > 0) {
if (!psdread(io, &globalLayerMaskInfo.overlayColorSpace)) {
error = "Could not read global mask info overlay colorspace";
return false;
}
for (int i = 0; i < 4; ++i) {
if (!psdread(io, &globalLayerMaskInfo.colorComponents[i])) {
error = QString("Could not read mask info visualizaion color component %1").arg(i);
return false;
}
}
if (!psdread(io, &globalLayerMaskInfo.opacity)) {
error = "Could not read global mask info visualization opacity";
return false;
}
if (!psdread(io, &globalLayerMaskInfo.kind)) {
error = "Could not read global mask info visualization type";
return false;
}
}
// global additional sections
/**
* Newer versions of PSD have layers info block wrapped into
* 'Lr16' or 'Lr32' additional section, while the main block is
* absent.
*
* Here we pass the callback which should be used when such
* additional section is recognized.
*/
globalInfoSection.setExtraLayerInfoBlockHandler(std::bind(&PSDLayerMaskSection::readLayerInfoImpl, this, std::placeholders::_1));
globalInfoSection.read(io);
/* put us after this section so reading the next section will work even if we mess up */
io->seek(start + layerMaskBlockSize);
return true;
}
struct FlattenedNode {
FlattenedNode() : type(RASTER_LAYER) {}
KisNodeSP node;
enum Type {
RASTER_LAYER,
FOLDER_OPEN,
FOLDER_CLOSED,
SECTION_DIVIDER
};
Type type;
};
void addBackgroundIfNeeded(KisNodeSP root, QList<FlattenedNode> &nodes)
{
KisGroupLayer *group = dynamic_cast<KisGroupLayer*>(root.data());
if (!group) return;
KoColor projectionColor = group->defaultProjectionColor();
if (projectionColor.opacityU8() == OPACITY_TRANSPARENT_U8) return;
KisPaintLayerSP layer =
new KisPaintLayer(group->image(),
i18nc("Automatically created layer name when saving into PSD", "Background"),
OPACITY_OPAQUE_U8);
layer->paintDevice()->setDefaultPixel(projectionColor);
{
FlattenedNode item;
item.node = layer;
item.type = FlattenedNode::RASTER_LAYER;
nodes << item;
}
}
void flattenNodes(KisNodeSP node, QList<FlattenedNode> &nodes)
{
KisNodeSP child = node->firstChild();
while (child) {
const bool isLayer = child->inherits("KisLayer");
const bool isGroupLayer = child->inherits("KisGroupLayer");
if (isGroupLayer) {
{
FlattenedNode item;
item.node = child;
item.type = FlattenedNode::SECTION_DIVIDER;
nodes << item;
}
flattenNodes(child, nodes);
{
FlattenedNode item;
item.node = child;
item.type = FlattenedNode::FOLDER_OPEN;
nodes << item;
}
} else if (isLayer) {
FlattenedNode item;
item.node = child;
item.type = FlattenedNode::RASTER_LAYER;
nodes << item;
}
child = child->nextSibling();
}
}
KisNodeSP findOnlyTransparencyMask(KisNodeSP node, FlattenedNode::Type type)
{
if (type != FlattenedNode::FOLDER_OPEN &&
type != FlattenedNode::FOLDER_CLOSED &&
type != FlattenedNode::RASTER_LAYER) {
return 0;
}
KisLayer *layer = qobject_cast<KisLayer*>(node.data());
QList<KisEffectMaskSP> masks = layer->effectMasks();
if (masks.size() != 1) return 0;
KisEffectMaskSP onlyMask = masks.first();
return onlyMask->inherits("KisTransparencyMask") ? onlyMask : 0;
}
QDomDocument fetchLayerStyleXmlData(KisNodeSP node)
{
const KisLayer *layer = qobject_cast<KisLayer*>(node.data());
KisPSDLayerStyleSP layerStyle = layer->layerStyle();
if (!layerStyle) return QDomDocument();
KisAslLayerStyleSerializer serializer;
serializer.setStyles(QVector<KisPSDLayerStyleSP>() << layerStyle);
return serializer.formPsdXmlDocument();
}
inline QDomNode findNodeByKey(const QString &key, QDomNode parent) {
return KisDomUtils::findElementByAttibute(parent, "node", "key", key);
}
void mergePatternsXMLSection(const QDomDocument &src, QDomDocument &dst)
{
- QDomNode srcPatternsNode = findNodeByKey("Patterns", src.documentElement());
- QDomNode dstPatternsNode = findNodeByKey("Patterns", dst.documentElement());
+ QDomNode srcPatternsNode = findNodeByKey(ResourceType::Patterns, src.documentElement());
+ QDomNode dstPatternsNode = findNodeByKey(ResourceType::Patterns, dst.documentElement());
if (srcPatternsNode.isNull()) return;
if (dstPatternsNode.isNull()) {
dst = src;
return;
}
KIS_ASSERT_RECOVER_RETURN(!srcPatternsNode.isNull());
KIS_ASSERT_RECOVER_RETURN(!dstPatternsNode.isNull());
QDomNode node = srcPatternsNode.firstChild();
while(!node.isNull()) {
QDomNode importedNode = dst.importNode(node, true);
KIS_ASSERT_RECOVER_RETURN(!importedNode.isNull());
dstPatternsNode.appendChild(importedNode);
node = node.nextSibling();
}
}
bool PSDLayerMaskSection::write(QIODevice* io, KisNodeSP rootLayer)
{
bool retval = true;
try {
writeImpl(io, rootLayer);
} catch (KisAslWriterUtils::ASLWriteException &e) {
error = PREPEND_METHOD(e.what());
retval = false;
}
return retval;
}
void PSDLayerMaskSection::writeImpl(QIODevice* io, KisNodeSP rootLayer)
{
dbgFile << "Writing layer layer section";
// Build the whole layer structure
QList<FlattenedNode> nodes;
addBackgroundIfNeeded(rootLayer, nodes);
flattenNodes(rootLayer, nodes);
if (nodes.isEmpty()) {
throw KisAslWriterUtils::ASLWriteException("Could not find paint layers to save");
}
{
KisAslWriterUtils::OffsetStreamPusher<quint32> layerAndMaskSectionSizeTag(io, 2);
QDomDocument mergedPatternsXmlDoc;
{
KisAslWriterUtils::OffsetStreamPusher<quint32> layerInfoSizeTag(io, 4);
{
// number of layers (negative, because krita always has alpha)
const qint16 layersSize = -nodes.size();
SAFE_WRITE_EX(io, layersSize);
dbgFile << "Number of layers" << layersSize << "at" << io->pos();
}
// Layer records section
Q_FOREACH (const FlattenedNode &item, nodes) {
KisNodeSP node = item.node;
PSDLayerRecord *layerRecord = new PSDLayerRecord(m_header);
layers.append(layerRecord);
KisNodeSP onlyTransparencyMask = findOnlyTransparencyMask(node, item.type);
const QRect maskRect = onlyTransparencyMask ? onlyTransparencyMask->paintDevice()->exactBounds() : QRect();
const bool nodeVisible = node->visible();
const KoColorSpace *colorSpace = node->colorSpace();
const quint8 nodeOpacity = node->opacity();
const quint8 nodeClipping = 0;
const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer*>(node.data());
const bool alphaLocked = (paintLayer && paintLayer->alphaLocked());
const QString nodeCompositeOp = node->compositeOpId();
const KisGroupLayer *groupLayer = qobject_cast<KisGroupLayer*>(node.data());
const bool nodeIsPassThrough = groupLayer && groupLayer->passThroughMode();
QDomDocument stylesXmlDoc = fetchLayerStyleXmlData(node);
if (mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
mergedPatternsXmlDoc = stylesXmlDoc;
} else if (!mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
mergePatternsXMLSection(stylesXmlDoc, mergedPatternsXmlDoc);
}
bool nodeIrrelevant = false;
QString nodeName;
KisPaintDeviceSP layerContentDevice;
psd_section_type sectionType;
if (item.type == FlattenedNode::RASTER_LAYER) {
nodeIrrelevant = false;
nodeName = node->name();
layerContentDevice = onlyTransparencyMask ? node->original() : node->projection();
sectionType = psd_other;
} else {
nodeIrrelevant = true;
nodeName = item.type == FlattenedNode::SECTION_DIVIDER ?
QString("</Layer group>") :
node->name();
layerContentDevice = 0;
sectionType =
item.type == FlattenedNode::SECTION_DIVIDER ? psd_bounding_divider :
item.type == FlattenedNode::FOLDER_OPEN ? psd_open_folder :
psd_closed_folder;
}
// === no access to node anymore
QRect layerRect;
if (layerContentDevice) {
QRect rc = layerContentDevice->exactBounds();
rc = rc.normalized();
// keep to the max of photoshop's capabilities
if (rc.width() > 30000) rc.setWidth(30000);
if (rc.height() > 30000) rc.setHeight(30000);
layerRect = rc;
}
layerRecord->top = layerRect.y();
layerRecord->left = layerRect.x();
layerRecord->bottom = layerRect.y() + layerRect.height();
layerRecord->right = layerRect.x() + layerRect.width();
// colors + alpha channel
// note: transparency mask not included
layerRecord->nChannels = colorSpace->colorChannelCount() + 1;
ChannelInfo *info = new ChannelInfo;
info->channelId = -1; // For the alpha channel, which we always have in Krita, and should be saved first in
layerRecord->channelInfoRecords << info;
// the rest is in display order: rgb, cmyk, lab...
for (int i = 0; i < (int)colorSpace->colorChannelCount(); ++i) {
info = new ChannelInfo;
info->channelId = i; // 0 for red, 1 = green, etc
layerRecord->channelInfoRecords << info;
}
layerRecord->blendModeKey = composite_op_to_psd_blendmode(nodeCompositeOp);
layerRecord->isPassThrough = nodeIsPassThrough;
layerRecord->opacity = nodeOpacity;
layerRecord->clipping = nodeClipping;
layerRecord->transparencyProtected = alphaLocked;
layerRecord->visible = nodeVisible;
layerRecord->irrelevant = nodeIrrelevant;
layerRecord->layerName = nodeName.isEmpty() ? i18n("Unnamed Layer") : nodeName;
layerRecord->write(io,
layerContentDevice,
onlyTransparencyMask,
maskRect,
sectionType,
stylesXmlDoc,
node->inherits("KisGroupLayer"));
}
dbgFile << "start writing layer pixel data" << io->pos();
// Now save the pixel data
Q_FOREACH (PSDLayerRecord *layerRecord, layers) {
layerRecord->writePixelData(io);
}
}
{
// write the global layer mask info -- which is empty
const quint32 globalMaskSize = 0;
SAFE_WRITE_EX(io, globalMaskSize);
}
globalInfoSection.writePattBlockEx(io, mergedPatternsXmlDoc);
}
}
diff --git a/plugins/impex/psd/psd_loader.cpp b/plugins/impex/psd/psd_loader.cpp
index 5a125c7656..a695f89514 100644
--- a/plugins/impex/psd/psd_loader.cpp
+++ b/plugins/impex/psd/psd_loader.cpp
@@ -1,378 +1,386 @@
/*
* 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.
*/
#include "psd_loader.h"
#include <QApplication>
#include <QFileInfo>
#include <QStack>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <KoColorProfile.h>
#include <KoCompositeOp.h>
#include <KoUnit.h>
#include <kis_annotation.h>
#include <kis_types.h>
#include <kis_paint_layer.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_group_layer.h>
#include <kis_paint_device.h>
#include <kis_transaction.h>
#include <kis_transparency_mask.h>
#include <kis_asl_layer_style_serializer.h>
-#include <kis_psd_layer_style_resource.h>
#include "KisResourceServerProvider.h"
#include "psd.h"
#include "psd_header.h"
#include "psd_colormode_block.h"
#include "psd_utils.h"
#include "psd_resource_section.h"
#include "psd_layer_section.h"
#include "psd_resource_block.h"
#include "psd_image_data.h"
#include "kis_image_barrier_locker.h"
PSDLoader::PSDLoader(KisDocument *doc)
: m_image(0)
, m_doc(doc)
, m_stop(false)
{
}
PSDLoader::~PSDLoader()
{
}
KisImportExportErrorCode PSDLoader::decode(QIODevice *io)
{
// open the file
dbgFile << "pos:" << io->pos();
PSDHeader header;
if (!header.read(io)) {
dbgFile << "failed reading header: " << header.error;
return ImportExportCodes::FileFormatIncorrect;
}
dbgFile << header;
dbgFile << "Read header. pos:" << io->pos();
PSDColorModeBlock colorModeBlock(header.colormode);
if (!colorModeBlock.read(io)) {
dbgFile << "failed reading colormode block: " << colorModeBlock.error;
return ImportExportCodes::FileFormatIncorrect;
}
dbgFile << "Read color mode block. pos:" << io->pos();
PSDImageResourceSection resourceSection;
if (!resourceSection.read(io)) {
dbgFile << "failed image reading resource section: " << resourceSection.error;
return ImportExportCodes::FileFormatIncorrect;
}
dbgFile << "Read image resource section. pos:" << io->pos();
PSDLayerMaskSection layerSection(header);
if (!layerSection.read(io)) {
dbgFile << "failed reading layer/mask section: " << layerSection.error;
return ImportExportCodes::FileFormatIncorrect;
}
dbgFile << "Read layer/mask section. " << layerSection.nLayers << "layers. pos:" << io->pos();
// Done reading, except possibly for the image data block, which is only relevant if there
// are no layers.
// Get the right colorspace
QPair<QString, QString> colorSpaceId = psd_colormode_to_colormodelid(header.colormode,
header.channelDepth);
if (colorSpaceId.first.isNull()) {
dbgFile << "Unsupported colorspace" << header.colormode << header.channelDepth;
return ImportExportCodes::FormatColorSpaceUnsupported;
}
// Get the icc profile from the image resource section
const KoColorProfile* profile = 0;
if (resourceSection.resources.contains(PSDImageResourceSection::ICC_PROFILE)) {
ICC_PROFILE_1039 *iccProfileData = dynamic_cast<ICC_PROFILE_1039*>(resourceSection.resources[PSDImageResourceSection::ICC_PROFILE]->resource);
if (iccProfileData ) {
profile = KoColorSpaceRegistry::instance()->createColorProfile(colorSpaceId.first,
colorSpaceId.second,
iccProfileData->icc);
dbgFile << "Loaded ICC profile" << profile->name();
delete resourceSection.resources.take(PSDImageResourceSection::ICC_PROFILE);
}
}
// Create the colorspace
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(colorSpaceId.first, colorSpaceId.second, profile);
if (!cs) {
return ImportExportCodes::FormatColorSpaceUnsupported;
}
// Creating the KisImage
QFile *file = dynamic_cast<QFile*>(io);
QString name = file ? file->fileName() : "Imported";
m_image = new KisImage(m_doc->createUndoStore(), header.width, header.height, cs, name);
Q_CHECK_PTR(m_image);
KisImageBarrierLocker locker(m_image);
// set the correct resolution
if (resourceSection.resources.contains(PSDImageResourceSection::RESN_INFO)) {
RESN_INFO_1005 *resInfo = dynamic_cast<RESN_INFO_1005*>(resourceSection.resources[PSDImageResourceSection::RESN_INFO]->resource);
if (resInfo) {
// check resolution size is not zero
if (resInfo->hRes * resInfo->vRes > 0)
m_image->setResolution(POINT_TO_INCH(resInfo->hRes), POINT_TO_INCH(resInfo->vRes));
// let's skip the unit for now; we can only set that on the KisDocument, and krita doesn't use it.
delete resourceSection.resources.take(PSDImageResourceSection::RESN_INFO);
}
}
// Preserve all the annotations
Q_FOREACH (PSDResourceBlock *resourceBlock, resourceSection.resources.values()) {
m_image->addAnnotation(resourceBlock);
}
// Preserve the duotone colormode block for saving back to psd
if (header.colormode == DuoTone) {
KisAnnotationSP annotation = new KisAnnotation("DuotoneColormodeBlock",
i18n("Duotone Colormode Block"),
colorModeBlock.data);
m_image->addAnnotation(annotation);
}
// Read the projection into our single layer. Since we only read the projection when
// we have just one layer, we don't need to later on apply the alpha channel of the
// first layer to the projection if the number of layers is negative/
// See https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_16000.
if (layerSection.nLayers == 0) {
dbgFile << "Position" << io->pos() << "Going to read the projection into the first layer, which Photoshop calls 'Background'";
KisPaintLayerSP layer = new KisPaintLayer(m_image, i18n("Background"), OPACITY_OPAQUE_U8);
PSDImageData imageData(&header);
imageData.read(io, layer->paintDevice());
m_image->addNode(layer, m_image->rootLayer());
// Only one layer, the background layer, so we're done.
return ImportExportCodes::OK;
}
// More than one layer, so now construct the Krita image from the info we read.
QStack<KisGroupLayerSP> groupStack;
groupStack.push(m_image->rootLayer());
/**
* PSD has a weird "optimization": if a group layer has only one
* child layer, it omits it's 'psd_bounding_divider' section. So
* fi you ever see an unbalanced layers group in PSD, most
* probably, it is just a single layered group.
*/
KisNodeSP lastAddedLayer;
typedef QPair<QDomDocument, KisLayerSP> LayerStyleMapping;
QVector<LayerStyleMapping> allStylesXml;
// read the channels for the various layers
for(int i = 0; i < layerSection.nLayers; ++i) {
PSDLayerRecord* layerRecord = layerSection.layers.at(i);
dbgFile << "Going to read channels for layer" << i << layerRecord->layerName;
KisLayerSP newLayer;
if (layerRecord->infoBlocks.keys.contains("lsct") &&
layerRecord->infoBlocks.sectionDividerType != psd_other) {
if (layerRecord->infoBlocks.sectionDividerType == psd_bounding_divider && !groupStack.isEmpty()) {
KisGroupLayerSP groupLayer = new KisGroupLayer(m_image, "temp", OPACITY_OPAQUE_U8);
m_image->addNode(groupLayer, groupStack.top());
groupStack.push(groupLayer);
newLayer = groupLayer;
}
else if ((layerRecord->infoBlocks.sectionDividerType == psd_open_folder ||
layerRecord->infoBlocks.sectionDividerType == psd_closed_folder) &&
(groupStack.size() > 1 || (lastAddedLayer && !groupStack.isEmpty()))) {
KisGroupLayerSP groupLayer;
if (groupStack.size() <= 1) {
groupLayer = new KisGroupLayer(m_image, "temp", OPACITY_OPAQUE_U8);
m_image->addNode(groupLayer, groupStack.top());
m_image->moveNode(lastAddedLayer, groupLayer, KisNodeSP());
} else {
groupLayer = groupStack.pop();
}
const QDomDocument &styleXml = layerRecord->infoBlocks.layerStyleXml;
if (!styleXml.isNull()) {
allStylesXml << LayerStyleMapping(styleXml, groupLayer);
}
groupLayer->setName(layerRecord->layerName);
groupLayer->setVisible(layerRecord->visible);
QString compositeOp = psd_blendmode_to_composite_op(layerRecord->infoBlocks.sectionDividerBlendMode);
// Krita doesn't support pass-through blend
// mode. Instead it is just a property of a group
// layer, so flip it
if (compositeOp == COMPOSITE_PASS_THROUGH) {
compositeOp = COMPOSITE_OVER;
groupLayer->setPassThroughMode(true);
}
groupLayer->setCompositeOpId(compositeOp);
newLayer = groupLayer;
} else {
/**
* In some files saved by PS CS6 the group layer sections seem
* to be unbalanced. I don't know why it happens because the
* reporter didn't provide us an example file. So here we just
* check if the new layer was created, and if not, skip the
* initialization of masks.
*
* See bug: 357559
*/
warnKrita << "WARNING: Provided PSD has unbalanced group "
<< "layer markers. Some masks and/or layers can "
<< "be lost while loading this file. Please "
<< "report a bug to Krita developers and attach "
<< "this file to the bugreport\n"
<< " " << ppVar(layerRecord->layerName) << "\n"
<< " " << ppVar(layerRecord->infoBlocks.sectionDividerType) << "\n"
<< " " << ppVar(groupStack.size());
continue;
}
}
else {
KisPaintLayerSP layer = new KisPaintLayer(m_image, layerRecord->layerName, layerRecord->opacity);
layer->setCompositeOpId(psd_blendmode_to_composite_op(layerRecord->blendModeKey));
const QDomDocument &styleXml = layerRecord->infoBlocks.layerStyleXml;
if (!styleXml.isNull()) {
allStylesXml << LayerStyleMapping(styleXml, layer);
}
if (!layerRecord->readPixelData(io, layer->paintDevice())) {
dbgFile << "failed reading channels for layer: " << layerRecord->layerName << layerRecord->error;
return ImportExportCodes::FileFormatIncorrect;
}
if (!groupStack.isEmpty()) {
m_image->addNode(layer, groupStack.top());
}
else {
m_image->addNode(layer, m_image->root());
}
layer->setVisible(layerRecord->visible);
newLayer = layer;
}
Q_FOREACH (ChannelInfo *channelInfo, layerRecord->channelInfoRecords) {
if (channelInfo->channelId < -1) {
KisTransparencyMaskSP mask = new KisTransparencyMask();
mask->setName(i18n("Transparency Mask"));
mask->initSelection(newLayer);
if (!layerRecord->readMask(io, mask->paintDevice(), channelInfo)) {
dbgFile << "failed reading masks for layer: " << layerRecord->layerName << layerRecord->error;
}
m_image->addNode(mask, newLayer);
}
}
lastAddedLayer = newLayer;
}
const QVector<QDomDocument> &embeddedPatterns =
layerSection.globalInfoSection.embeddedPatterns;
KisAslLayerStyleSerializer serializer;
if (!embeddedPatterns.isEmpty()) {
Q_FOREACH (const QDomDocument &doc, embeddedPatterns) {
serializer.registerPSDPattern(doc);
}
}
QVector<KisPSDLayerStyleSP> allStylesForServer;
if (!allStylesXml.isEmpty()) {
Q_FOREACH (const LayerStyleMapping &mapping, allStylesXml) {
serializer.readFromPSDXML(mapping.first);
if (serializer.styles().size() == 1) {
KisPSDLayerStyleSP layerStyle = serializer.styles().first();
KisLayerSP layer = mapping.second;
layerStyle->setName(layer->name());
allStylesForServer << layerStyle;
- layer->setLayerStyle(layerStyle->clone());
+ layer->setLayerStyle(layerStyle->clone().dynamicCast<KisPSDLayerStyle>());
} else {
warnKrita << "WARNING: Couldn't read layer style!" << ppVar(serializer.styles());
}
}
}
if (!allStylesForServer.isEmpty()) {
- KisPSDLayerStyleCollectionResource *collection =
- new KisPSDLayerStyleCollectionResource("Embedded PSD Styles.asl");
+
+ warnKrita << "WARNING: Asl Layer Styles cannot be read (part of resource rewrite).";
+ // TODO: RESOURCES: needs implementing of creation of the storage and linking it to the database etc.
+ // Note: it would be possible to just read them and add to the server, but it would be better to do it through the storage
+
+
+ /*
+
+ KisPSDLayerStyleCollectionResourceSP collection(new KisPSDLayerStyleCollectionResource("Embedded PSD Styles.asl"));
collection->setName(i18nc("Auto-generated layer style collection name for embedded styles (collection)", "<%1> (embedded)", m_image->objectName()));
KIS_SAFE_ASSERT_RECOVER_NOOP(!collection->valid());
collection->setLayerStyles(allStylesForServer);
KIS_SAFE_ASSERT_RECOVER_NOOP(collection->valid());
- KoResourceServer<KisPSDLayerStyleCollectionResource> *server = KisResourceServerProvider::instance()->layerStyleCollectionServer();
+ KoResourceServer<KisPSDLayerStyle> *server = KisResourceServerProvider::instance()->layerStyleServer();
server->addResource(collection, false);
+ */
+
}
return ImportExportCodes::OK;
}
KisImportExportErrorCode PSDLoader::buildImage(QIODevice *io)
{
return decode(io);
}
KisImageSP PSDLoader::image()
{
return m_image;
}
void PSDLoader::cancel()
{
m_stop = true;
}
diff --git a/plugins/paintops/colorsmudge/kis_colorsmudgeop.h b/plugins/paintops/colorsmudge/kis_colorsmudgeop.h
index e4a937a3a9..c10043391e 100644
--- a/plugins/paintops/colorsmudge/kis_colorsmudgeop.h
+++ b/plugins/paintops/colorsmudge/kis_colorsmudgeop.h
@@ -1,102 +1,104 @@
/*
* 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.
*/
#ifndef _KIS_COLORSMUDGEOP_H_
#define _KIS_COLORSMUDGEOP_H_
#include <QRect>
+#include "KoColorTransformation.h"
+#include <KoAbstractGradient.h>
+
#include <kis_brush_based_paintop.h>
#include <kis_types.h>
#include <kis_pressure_size_option.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_ratio_option.h>
#include <kis_pressure_spacing_option.h>
#include <kis_pressure_rate_option.h>
#include <kis_pressure_rotation_option.h>
#include <kis_pressure_scatter_option.h>
#include <kis_pressure_gradient_option.h>
#include <kis_pressure_hsv_option.h>
#include <kis_airbrush_option_widget.h>
-#include "KoColorTransformation.h"
#include "kis_overlay_mode_option.h"
#include "kis_rate_option.h"
#include "kis_smudge_option.h"
#include "kis_smudge_radius_option.h"
#include "KisPrecisePaintDeviceWrapper.h"
class QPointF;
-class KoAbstractGradient;
+
class KisBrushBasedPaintOpSettings;
class KisPainter;
class KoColorSpace;
class KisColorSmudgeOp: public KisBrushBasedPaintOp
{
public:
KisColorSmudgeOp(const KisPaintOpSettingsSP settings, KisPainter* painter, KisNodeSP node, KisImageSP image);
~KisColorSmudgeOp() override;
protected:
KisSpacingInformation paintAt(const KisPaintInformation& info) override;
KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override;
KisTimingInformation updateTimingImpl(const KisPaintInformation &info) const;
private:
// Sets the m_maskDab _and m_maskDabRect
void updateMask(const KisPaintInformation& info, const KisDabShape &shape, const QPointF &cursorPoint);
inline void getTopLeftAligned(const QPointF &pos, const QPointF &hotSpot, qint32 *x, qint32 *y);
private:
bool m_firstRun;
KisImageWSP m_image;
KisPrecisePaintDeviceWrapper m_precisePainterWrapper;
KoColor m_paintColor;
KisPaintDeviceSP m_tempDev;
QScopedPointer<KisPrecisePaintDeviceWrapper> m_preciseImageDeviceWrapper;
QScopedPointer<KisPainter> m_backgroundPainter;
QScopedPointer<KisPainter> m_smudgePainter;
QScopedPointer<KisPainter> m_colorRatePainter;
QScopedPointer<KisPainter> m_finalPainter;
- const KoAbstractGradient* m_gradient {0};
+ KoAbstractGradientSP m_gradient;
KisPressureSizeOption m_sizeOption;
KisPressureOpacityOption m_opacityOption;
KisPressureRatioOption m_ratioOption;
KisPressureSpacingOption m_spacingOption;
KisPressureRateOption m_rateOption;
KisSmudgeOption m_smudgeRateOption;
KisRateOption m_colorRateOption;
KisSmudgeRadiusOption m_smudgeRadiusOption;
KisOverlayModeOption m_overlayModeOption;
KisPressureRotationOption m_rotationOption;
KisPressureScatterOption m_scatterOption;
KisPressureGradientOption m_gradientOption;
QList<KisPressureHSVOption*> m_hsvOptions;
KisAirbrushOptionProperties m_airbrushOption;
QRect m_dstDabRect;
KisFixedPaintDeviceSP m_maskDab;
QPointF m_lastPaintPos;
KoColorTransformation *m_hsvTransform {0};
const KoCompositeOp *m_preciseColorRateCompositeOp {0};
};
#endif // _KIS_COLORSMUDGEOP_H_
diff --git a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.cpp b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.cpp
index f51fcbcced..4b7ca8a4ee 100644
--- a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.cpp
+++ b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.cpp
@@ -1,121 +1,122 @@
/*
* 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_colorsmudgeop_settings.h"
#include "kis_smudge_option.h"
struct KisColorSmudgeOpSettings::Private
{
QList<KisUniformPaintOpPropertyWSP> uniformProperties;
};
-KisColorSmudgeOpSettings::KisColorSmudgeOpSettings()
- : m_d(new Private)
+KisColorSmudgeOpSettings::KisColorSmudgeOpSettings(KisResourcesInterfaceSP resourcesInterface)
+ : KisBrushBasedPaintOpSettings(resourcesInterface),
+ m_d(new Private)
{
}
KisColorSmudgeOpSettings::~KisColorSmudgeOpSettings()
{
}
#include <brushengine/kis_slider_based_paintop_property.h>
#include <brushengine/kis_combo_based_paintop_property.h>
#include "kis_paintop_preset.h"
#include "kis_paintop_settings_update_proxy.h"
#include "kis_curve_option_uniform_property.h"
#include "kis_smudge_radius_option.h"
QList<KisUniformPaintOpPropertySP> KisColorSmudgeOpSettings::uniformProperties(KisPaintOpSettingsSP settings)
{
QList<KisUniformPaintOpPropertySP> props =
listWeakToStrong(m_d->uniformProperties);
if (props.isEmpty()) {
{
KisComboBasedPaintOpPropertyCallback *prop =
new KisComboBasedPaintOpPropertyCallback(
"smudge_mode",
i18n("Smudge Mode"),
settings, 0);
QList<QString> modes;
modes << i18n("Smearing");
modes << i18n("Dulling");
prop->setItems(modes);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
KisSmudgeOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(int(option.getMode()));
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
KisSmudgeOption option;
option.readOptionSetting(prop->settings().data());
option.setMode(KisSmudgeOption::Mode(prop->value().toInt()));
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisCurveOptionUniformProperty *prop =
new KisCurveOptionUniformProperty(
"smudge_length",
new KisSmudgeOption(),
settings, 0);
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisCurveOptionUniformProperty *prop =
new KisCurveOptionUniformProperty(
"smudge_radius",
new KisSmudgeRadiusOption(),
settings, 0);
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisCurveOptionUniformProperty *prop =
new KisCurveOptionUniformProperty(
"smudge_color_rate",
new KisRateOption("ColorRate", KisPaintOpOption::GENERAL, false),
settings, 0);
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
}
return KisBrushBasedPaintOpSettings::uniformProperties(settings) + props;
}
diff --git a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.h b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.h
index 5bef9202ae..c00b65ac55 100644
--- a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.h
+++ b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings.h
@@ -1,41 +1,41 @@
/*
* 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_COLORSMUDGEOP_SETTINGS_H
#define __KIS_COLORSMUDGEOP_SETTINGS_H
#include <QScopedPointer>
#include <kis_brush_based_paintop_settings.h>
class KisColorSmudgeOpSettings : public KisBrushBasedPaintOpSettings
{
public:
- KisColorSmudgeOpSettings();
+ KisColorSmudgeOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisColorSmudgeOpSettings() override;
QList<KisUniformPaintOpPropertySP> uniformProperties(KisPaintOpSettingsSP settings) override;
private:
struct Private;
const QScopedPointer<Private> m_d;
};
typedef KisSharedPtr<KisColorSmudgeOpSettings> KisColorSmudgeOpSettingsSP;
#endif /* __KIS_COLORSMUDGEOP_SETTINGS_H */
diff --git a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings_widget.cpp b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings_widget.cpp
index d3994a7054..daccc2fed2 100644
--- a/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings_widget.cpp
+++ b/plugins/paintops/colorsmudge/kis_colorsmudgeop_settings_widget.cpp
@@ -1,96 +1,97 @@
/*
* 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_colorsmudgeop_settings_widget.h"
#include "kis_brush_based_paintop_settings.h"
#include "kis_overlay_mode_option.h"
#include "kis_rate_option.h"
#include "kis_smudge_option_widget.h"
#include "kis_smudge_radius_option.h"
#include <kis_properties_configuration.h>
#include <kis_paintop_settings_widget.h>
#include <kis_pressure_size_option.h>
#include <kis_pressure_ratio_option.h>
#include <kis_curve_option_widget.h>
#include <kis_pressure_rotation_option.h>
#include <kis_pressure_scatter_option_widget.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_gradient_option.h>
#include <kis_airbrush_option_widget.h>
#include <kis_compositeop_option.h>
#include <kis_pressure_spacing_option_widget.h>
#include <kis_pressure_rate_option.h>
#include "kis_texture_option.h"
#include <kis_pressure_mirror_option_widget.h>
#include "kis_pressure_texture_strength_option.h"
#include "kis_pressure_hsv_option.h"
#include "kis_colorsmudgeop_settings.h"
+#include <KisGlobalResourcesInterface.h>
KisColorSmudgeOpSettingsWidget::KisColorSmudgeOpSettingsWidget(QWidget* parent):
KisBrushBasedPaintopOptionWidget(parent)
{
setObjectName("brush option widget");
setPrecisionEnabled(true);
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRatioOption(), i18n("0%"), i18n("100%")), i18n("Ratio"));
addPaintOpOption(new KisPressureSpacingOptionWidget(), i18n("Spacing"));
addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror"));
m_smudgeOptionWidget = new KisSmudgeOptionWidget();
addPaintOpOption(m_smudgeOptionWidget, i18n("Smudge Length"));
addPaintOpOption(new KisCurveOptionWidget(new KisSmudgeRadiusOption(), i18n("0.0"), i18n("1.0")), i18n("Smudge Radius"));
addPaintOpOption(new KisCurveOptionWidget(new KisRateOption("ColorRate", KisPaintOpOption::GENERAL, false), i18n("0.0"), i18n("1.0")), i18n("Color Rate"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation"));
addPaintOpOption(new KisPressureScatterOptionWidget(), i18n("Scatter"));
addPaintOpOption(new KisOverlayModeOptionWidget(), i18n("Overlay Mode"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureGradientOption(), i18n("0%"), i18n("100%")), i18n("Gradient"));
addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createHueOption(), KisPressureHSVOption::hueMinLabel(), KisPressureHSVOption::huemaxLabel()), i18n("Hue"));
addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createSaturationOption(), KisPressureHSVOption::saturationMinLabel(), KisPressureHSVOption::saturationmaxLabel()), i18n("Saturation"));
addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createValueOption(), KisPressureHSVOption::valueMinLabel(), KisPressureHSVOption::valuemaxLabel()), i18nc("HSV Value", "Value"));
addPaintOpOption(new KisAirbrushOptionWidget(false), i18n("Airbrush"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"), i18n("100%")), i18n("Rate"));
addPaintOpOption(new KisTextureOption(), i18n("Pattern"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength"));
}
KisColorSmudgeOpSettingsWidget::~KisColorSmudgeOpSettingsWidget() { }
KisPropertiesConfigurationSP KisColorSmudgeOpSettingsWidget::configuration() const
{
- KisColorSmudgeOpSettingsSP config = new KisColorSmudgeOpSettings();
+ KisColorSmudgeOpSettingsSP config = new KisColorSmudgeOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisColorSmudgeOpSettingsWidget*>(this));
config->setProperty("paintop", "colorsmudge");
writeConfiguration(config);
return config;
}
void KisColorSmudgeOpSettingsWidget::notifyPageChanged()
{
KisBrushSP brush = this->brush();
bool pierced = brush ? brush->isPiercedApprox() : false;
m_smudgeOptionWidget->updateBrushPierced(pierced);
}
diff --git a/plugins/paintops/curvebrush/kis_curve_paintop_settings.cpp b/plugins/paintops/curvebrush/kis_curve_paintop_settings.cpp
index 2dcd0d00b4..62acd99992 100644
--- a/plugins/paintops/curvebrush/kis_curve_paintop_settings.cpp
+++ b/plugins/paintops/curvebrush/kis_curve_paintop_settings.cpp
@@ -1,203 +1,203 @@
/*
* Copyright (c) 2008,2009 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.
*/
#include <kis_curve_paintop_settings.h>
#include <kis_paint_action_type_option.h>
#include "kis_curve_line_option.h"
struct KisCurvePaintOpSettings::Private
{
QList<KisUniformPaintOpPropertyWSP> uniformProperties;
};
-KisCurvePaintOpSettings::KisCurvePaintOpSettings()
- : m_d(new Private)
+KisCurvePaintOpSettings::KisCurvePaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
+ : KisPaintOpSettings(resourcesInterface),
+ m_d(new Private)
{
}
KisCurvePaintOpSettings::~KisCurvePaintOpSettings()
{
}
void KisCurvePaintOpSettings::setPaintOpSize(qreal value)
{
KisCurveOptionProperties option;
option.readOptionSetting(this);
option.curve_line_width = value;
option.writeOptionSetting(this);
}
qreal KisCurvePaintOpSettings::paintOpSize() const
{
KisCurveOptionProperties option;
option.readOptionSetting(this);
return option.curve_line_width;
}
bool KisCurvePaintOpSettings::paintIncremental()
{
return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP;
}
#include <brushengine/kis_slider_based_paintop_property.h>
#include "kis_paintop_preset.h"
#include "kis_paintop_settings_update_proxy.h"
#include "kis_standard_uniform_properties_factory.h"
QList<KisUniformPaintOpPropertySP> KisCurvePaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings)
{
QList<KisUniformPaintOpPropertySP> props =
listWeakToStrong(m_d->uniformProperties);
if (props.isEmpty()) {
{
KisIntSliderBasedPaintOpPropertyCallback *prop =
new KisIntSliderBasedPaintOpPropertyCallback(
KisIntSliderBasedPaintOpPropertyCallback::Int,
"curve_linewidth",
i18n("Line Width"),
settings, 0);
prop->setRange(1, 100);
prop->setSingleStep(1);
prop->setSuffix(i18n(" px"));
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
KisCurveOptionProperties option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.curve_line_width);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
KisCurveOptionProperties option;
option.readOptionSetting(prop->settings().data());
option.curve_line_width = prop->value().toInt();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisIntSliderBasedPaintOpPropertyCallback *prop =
new KisIntSliderBasedPaintOpPropertyCallback(
KisIntSliderBasedPaintOpPropertyCallback::Int,
"curve_historysize",
i18n("History Size"),
settings, 0);
prop->setRange(2, 300);
prop->setSingleStep(1);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
KisCurveOptionProperties option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.curve_stroke_history_size);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
KisCurveOptionProperties option;
option.readOptionSetting(prop->settings().data());
option.curve_stroke_history_size = prop->value().toInt();
option.writeOptionSetting(prop->settings().data());
});
-
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisDoubleSliderBasedPaintOpPropertyCallback *prop =
new KisDoubleSliderBasedPaintOpPropertyCallback(
KisDoubleSliderBasedPaintOpPropertyCallback::Double,
"curve_lineopacity",
i18n("Line Opacity"),
settings, 0);
prop->setRange(0, 100.0);
prop->setSingleStep(0.01);
prop->setDecimals(2);
prop->setSuffix(i18n("%"));
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
KisCurveOptionProperties option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.curve_curves_opacity * 100.0);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
KisCurveOptionProperties option;
option.readOptionSetting(prop->settings().data());
option.curve_curves_opacity = prop->value().toReal() / 100.0;
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisUniformPaintOpPropertyCallback *prop =
new KisUniformPaintOpPropertyCallback(
KisUniformPaintOpPropertyCallback::Bool,
"curve_connectionline",
i18n("Connection Line"),
settings, 0);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
KisCurveOptionProperties option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.curve_paint_connection_line);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
KisCurveOptionProperties option;
option.readOptionSetting(prop->settings().data());
option.curve_paint_connection_line = prop->value().toBool();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
}
{
using namespace KisStandardUniformPropertiesFactory;
Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) {
if (prop->id() == opacity.id()) {
props.prepend(prop);
}
}
}
return props;
}
diff --git a/plugins/paintops/curvebrush/kis_curve_paintop_settings.h b/plugins/paintops/curvebrush/kis_curve_paintop_settings.h
index 1e7128fa2d..2609373a63 100644
--- a/plugins/paintops/curvebrush/kis_curve_paintop_settings.h
+++ b/plugins/paintops/curvebrush/kis_curve_paintop_settings.h
@@ -1,43 +1,43 @@
/*
* Copyright (c) 2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2008 Lukas Tvrdy <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_CURVE_PAINTOP_SETTINGS_H_
#define KIS_CURVE_PAINTOP_SETTINGS_H_
#include <QScopedPointer>
#include <brushengine/kis_paintop_settings.h>
class KisCurvePaintOpSettings : public KisPaintOpSettings
{
public:
- KisCurvePaintOpSettings();
+ KisCurvePaintOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisCurvePaintOpSettings() override;
void setPaintOpSize(qreal value) override;
qreal paintOpSize() const override;
bool paintIncremental() override;
QList<KisUniformPaintOpPropertySP> uniformProperties(KisPaintOpSettingsSP settings) override;
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif
diff --git a/plugins/paintops/curvebrush/kis_curve_paintop_settings_widget.cpp b/plugins/paintops/curvebrush/kis_curve_paintop_settings_widget.cpp
index dd75f2ca82..f63fcb1fc6 100644
--- a/plugins/paintops/curvebrush/kis_curve_paintop_settings_widget.cpp
+++ b/plugins/paintops/curvebrush/kis_curve_paintop_settings_widget.cpp
@@ -1,55 +1,56 @@
/*
* Copyright (c) 2008,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.
*/
#include <kis_curve_paintop_settings_widget.h>
#include <kis_properties_configuration.h>
#include <kis_curve_paintop_settings.h>
#include "kis_curve_line_option.h"
#include <kis_compositeop_option.h>
#include <kis_paint_action_type_option.h>
#include <kis_curve_option_widget.h>
#include <kis_pressure_opacity_option.h>
#include <kis_linewidth_option.h>
#include "kis_curves_opacity_option.h"
+#include <KisGlobalResourcesInterface.h>
KisCurvePaintOpSettingsWidget:: KisCurvePaintOpSettingsWidget(QWidget* parent)
: KisPaintOpSettingsWidget(parent)
{
m_curveOption = new KisCurveOpOption();
addPaintOpOption(m_curveOption, i18nc("Brush settings curve value", "Value"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity"));
addPaintOpOption(new KisCurveOptionWidget(new KisLineWidthOption(), i18n("0%"), i18n("100%")), i18n("Line width"));
addPaintOpOption(new KisCurveOptionWidget(new KisCurvesOpacityOption(), i18n("0%"), i18n("100%")), i18n("Curves opacity"));
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode"));
}
KisCurvePaintOpSettingsWidget::~ KisCurvePaintOpSettingsWidget()
{
}
KisPropertiesConfigurationSP KisCurvePaintOpSettingsWidget::configuration() const
{
- KisCurvePaintOpSettings* config = new KisCurvePaintOpSettings();
+ KisCurvePaintOpSettings* config = new KisCurvePaintOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisCurvePaintOpSettingsWidget*>(this));
config->setProperty("paintop", "curvebrush"); // XXX: make this a const id string
writeConfiguration(config);
return config;
}
diff --git a/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp b/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp
index 5da1c8ea48..9a1b29a4e6 100644
--- a/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp
+++ b/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp
@@ -1,95 +1,95 @@
/*
* 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 "KisBrushOpResources.h"
#include <KoColorSpace.h>
#include <KoColorTransformation.h>
#include "kis_color_source.h"
#include "kis_pressure_mix_option.h"
#include "kis_pressure_darken_option.h"
#include "kis_pressure_hsv_option.h"
#include "kis_color_source_option.h"
#include "kis_pressure_sharpness_option.h"
#include "kis_texture_option.h"
#include "kis_painter.h"
#include "kis_paintop_settings.h"
struct KisBrushOpResources::Private
{
QList<KisPressureHSVOption*> hsvOptions;
KoColorTransformation *hsvTransformation = 0;
KisPressureMixOption mixOption;
KisPressureDarkenOption darkenOption;
};
KisBrushOpResources::KisBrushOpResources(const KisPaintOpSettingsSP settings, KisPainter *painter)
: m_d(new Private)
{
KisColorSourceOption colorSourceOption;
colorSourceOption.readOptionSetting(settings);
colorSource.reset(colorSourceOption.createColorSource(painter));
sharpnessOption.reset(new KisPressureSharpnessOption());
sharpnessOption->readOptionSetting(settings);
sharpnessOption->resetAllSensors();
textureOption.reset(new KisTextureProperties(painter->device()->defaultBounds()->currentLevelOfDetail()));
- textureOption->fillProperties(settings);
+ textureOption->fillProperties(settings, settings->resourcesInterface());
m_d->hsvOptions.append(KisPressureHSVOption::createHueOption());
m_d->hsvOptions.append(KisPressureHSVOption::createSaturationOption());
m_d->hsvOptions.append(KisPressureHSVOption::createValueOption());
Q_FOREACH (KisPressureHSVOption * option, m_d->hsvOptions) {
option->readOptionSetting(settings);
option->resetAllSensors();
if (option->isChecked() && !m_d->hsvTransformation) {
m_d->hsvTransformation = painter->backgroundColor().colorSpace()->createColorTransformation("hsv_adjustment", QHash<QString, QVariant>());
}
}
m_d->darkenOption.readOptionSetting(settings);
m_d->mixOption.readOptionSetting(settings);
m_d->darkenOption.resetAllSensors();
m_d->mixOption.resetAllSensors();
}
KisBrushOpResources::~KisBrushOpResources()
{
qDeleteAll(m_d->hsvOptions);
delete m_d->hsvTransformation;
}
void KisBrushOpResources::syncResourcesToSeqNo(int seqNo, const KisPaintInformation &info)
{
colorSource->selectColor(m_d->mixOption.apply(info), info);
m_d->darkenOption.apply(colorSource.data(), info);
if (m_d->hsvTransformation) {
Q_FOREACH (KisPressureHSVOption * option, m_d->hsvOptions) {
option->apply(m_d->hsvTransformation, info);
}
colorSource->applyColorTransformation(m_d->hsvTransformation);
}
KisDabCacheUtils::DabRenderingResources::syncResourcesToSeqNo(seqNo, info);
}
diff --git a/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.cpp b/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.cpp
index f7573c09a5..686a5c5cfd 100644
--- a/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.cpp
+++ b/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.cpp
@@ -1,25 +1,30 @@
/*
* 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 "KisBrushOpSettings.h"
+KisBrushOpSettings::KisBrushOpSettings(KisResourcesInterfaceSP resourcesInterface)
+ : KisBrushBasedPaintOpSettings(resourcesInterface)
+{
+}
+
bool KisBrushOpSettings::needsAsynchronousUpdates() const
{
return true;
}
diff --git a/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.h b/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.h
index f4dcd7ed66..c9fd160b7b 100644
--- a/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.h
+++ b/plugins/paintops/defaultpaintops/brush/KisBrushOpSettings.h
@@ -1,31 +1,32 @@
/*
* 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 KISBRUSHOPSETTINGS_H
#define KISBRUSHOPSETTINGS_H
#include "kis_brush_based_paintop_settings.h"
class KisBrushOpSettings : public KisBrushBasedPaintOpSettings
{
public:
+ KisBrushOpSettings(KisResourcesInterfaceSP resourcesInterface);
bool needsAsynchronousUpdates() const;
};
#endif // KISBRUSHOPSETTINGS_H
diff --git a/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp b/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp
index 9d45f92f55..58ed906ad5 100644
--- a/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp
+++ b/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp
@@ -1,432 +1,432 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004-2008 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) 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.
*/
#include "kis_brushop.h"
#include <QRect>
#include <kis_image.h>
#include <kis_vec.h>
#include <kis_debug.h>
#include <KoColorTransformation.h>
#include <KoColor.h>
#include <kis_brush.h>
#include <kis_global.h>
#include <kis_paint_device.h>
#include <kis_painter.h>
#include <kis_brush_based_paintop_settings.h>
#include <kis_lod_transform.h>
#include <kis_paintop_plugin_utils.h>
#include "krita_utils.h"
#include <QtConcurrent>
#include "kis_algebra_2d.h"
#include <KisDabRenderingExecutor.h>
#include <KisDabCacheUtils.h>
#include <KisRenderedDab.h>
#include "KisBrushOpResources.h"
#include <KisRunnableStrokeJobData.h>
#include <KisRunnableStrokeJobsInterface.h>
#include <QSharedPointer>
#include <QThread>
#include "kis_image_config.h"
#include "kis_wrapped_rect.h"
KisBrushOp::KisBrushOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image)
: KisBrushBasedPaintOp(settings, painter)
, m_opacityOption(node)
, m_avgSpacing(50)
, m_avgNumDabs(50)
, m_avgUpdateTimePerDab(50)
, m_idealNumRects(KisImageConfig(true).maxNumberOfThreads())
, m_minUpdatePeriod(10)
, m_maxUpdatePeriod(100)
{
Q_UNUSED(image);
Q_ASSERT(settings);
/**
* We do our own threading here, so we need to forbid the brushes
* to do threading internally
*/
m_brush->setThreadingAllowed(false);
m_airbrushOption.readOptionSetting(settings);
m_opacityOption.readOptionSetting(settings);
m_flowOption.readOptionSetting(settings);
m_sizeOption.readOptionSetting(settings);
m_ratioOption.readOptionSetting(settings);
m_spacingOption.readOptionSetting(settings);
m_rateOption.readOptionSetting(settings);
m_softnessOption.readOptionSetting(settings);
m_rotationOption.readOptionSetting(settings);
m_scatterOption.readOptionSetting(settings);
m_sharpnessOption.readOptionSetting(settings);
m_opacityOption.resetAllSensors();
m_flowOption.resetAllSensors();
m_sizeOption.resetAllSensors();
m_ratioOption.resetAllSensors();
m_rateOption.resetAllSensors();
m_softnessOption.resetAllSensors();
m_sharpnessOption.resetAllSensors();
m_rotationOption.resetAllSensors();
m_scatterOption.resetAllSensors();
m_sharpnessOption.resetAllSensors();
m_rotationOption.applyFanCornersInfo(this);
m_precisionOption.setHasImprecisePositionOptions(
m_precisionOption.hasImprecisePositionOptions() |
m_scatterOption.isChecked() |
m_rotationOption.isChecked() |
m_airbrushOption.enabled);
KisBrushSP baseBrush = m_brush;
auto resourcesFactory =
[baseBrush, settings, painter] () {
KisDabCacheUtils::DabRenderingResources *resources =
new KisBrushOpResources(settings, painter);
- resources->brush = baseBrush->clone();
+ resources->brush = baseBrush->clone().dynamicCast<KisBrush>();
return resources;
};
m_dabExecutor.reset(
new KisDabRenderingExecutor(
painter->device()->compositionSourceColorSpace(),
resourcesFactory,
painter->runnableStrokeJobsInterface(),
&m_mirrorOption,
&m_precisionOption));
}
KisBrushOp::~KisBrushOp()
{
}
KisSpacingInformation KisBrushOp::paintAt(const KisPaintInformation& info)
{
if (!painter()->device()) return KisSpacingInformation(1.0);
KisBrushSP brush = m_brush;
Q_ASSERT(brush);
if (!brush)
return KisSpacingInformation(1.0);
if (!brush->canPaintFor(info))
return KisSpacingInformation(1.0);
qreal scale = m_sizeOption.apply(info);
scale *= KisLodTransform::lodToScale(painter()->device());
if (checkSizeTooSmall(scale)) return KisSpacingInformation();
qreal rotation = m_rotationOption.apply(info);
qreal ratio = m_ratioOption.apply(info);
KisDabShape shape(scale, ratio, rotation);
QPointF cursorPos =
m_scatterOption.apply(info,
brush->maskWidth(shape, 0, 0, info),
brush->maskHeight(shape, 0, 0, info));
m_opacityOption.setFlow(m_flowOption.apply(info));
quint8 dabOpacity = OPACITY_OPAQUE_U8;
quint8 dabFlow = OPACITY_OPAQUE_U8;
m_opacityOption.apply(info, &dabOpacity, &dabFlow);
KisDabCacheUtils::DabRequestInfo request(painter()->paintColor(),
cursorPos,
shape,
info,
m_softnessOption.apply(info));
m_dabExecutor->addDab(request, qreal(dabOpacity) / 255.0, qreal(dabFlow) / 255.0);
KisSpacingInformation spacingInfo =
effectiveSpacing(scale, rotation, &m_airbrushOption, &m_spacingOption, info);
// gather statistics about dabs
m_avgSpacing(spacingInfo.scalarApprox());
return spacingInfo;
}
struct KisBrushOp::UpdateSharedState
{
// rendering data
KisPainter *painter = 0;
QList<KisRenderedDab> dabsQueue;
// speed metrics
QVector<QPointF> dabPoints;
QElapsedTimer dabRenderingTimer;
// final report
QVector<QRect> allDirtyRects;
};
void KisBrushOp::addMirroringJobs(Qt::Orientation direction,
QVector<QRect> &rects,
UpdateSharedStateSP state,
QVector<KisRunnableStrokeJobData*> &jobs)
{
jobs.append(new KisRunnableStrokeJobData(0, KisStrokeJobData::SEQUENTIAL));
for (KisRenderedDab &dab : state->dabsQueue) {
jobs.append(
new KisRunnableStrokeJobData(
[state, &dab, direction] () {
state->painter->mirrorDab(direction, &dab);
},
KisStrokeJobData::CONCURRENT));
}
jobs.append(new KisRunnableStrokeJobData(0, KisStrokeJobData::SEQUENTIAL));
for (QRect &rc : rects) {
state->painter->mirrorRect(direction, &rc);
jobs.append(
new KisRunnableStrokeJobData(
[rc, state] () {
state->painter->bltFixed(rc, state->dabsQueue);
},
KisStrokeJobData::CONCURRENT));
}
state->allDirtyRects.append(rects);
}
std::pair<int, bool> KisBrushOp::doAsyncronousUpdate(QVector<KisRunnableStrokeJobData*> &jobs)
{
bool someDabsAreStillInQueue = false;
const bool hasPreparedDabsAtStart = m_dabExecutor->hasPreparedDabs();
if (!m_updateSharedState && hasPreparedDabsAtStart) {
m_updateSharedState = toQShared(new UpdateSharedState());
UpdateSharedStateSP state = m_updateSharedState;
state->painter = painter();
{
const qreal dabRenderingTime = m_dabExecutor->averageDabRenderingTime();
const qreal totalRenderingTimePerDab = dabRenderingTime + m_avgUpdateTimePerDab.rollingMeanSafe();
// we limit the number of fetched dabs to fit the maximum update period and not
// make visual hiccups
const int dabsLimit =
totalRenderingTimePerDab > 0 ?
qMax(10, int(m_maxUpdatePeriod / totalRenderingTimePerDab * m_idealNumRects)) :
-1;
state->dabsQueue = m_dabExecutor->takeReadyDabs(painter()->hasMirroring(), dabsLimit, &someDabsAreStillInQueue);
}
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!state->dabsQueue.isEmpty(),
std::make_pair(m_currentUpdatePeriod, false));
const int diameter = m_dabExecutor->averageDabSize();
const qreal spacing = m_avgSpacing.rollingMean();
const int idealNumRects = m_idealNumRects;
QVector<QRect> rects;
// wrap the dabs if needed
if (painter()->device()->defaultBounds()->wrapAroundMode()) {
/**
* In WA mode we do two things:
*
* 1) We ensure that the parallel threads do not access the same are on
* the image. For normal updates that is ensured by the code in KisImage
* and the scheduler. Here we should do that manually by adjusting 'rects'
* so that they would not intersect in the wrapped space.
*
* 2) We duplicate dabs, to ensure that all the pieces of dabs are painted
* inside the wrapped rect. No pieces are dabs are painted twice, because
* we paint only the parts intersecting the wrap rect.
*/
const QRect wrapRect = painter()->device()->defaultBounds()->bounds();
QList<KisRenderedDab> wrappedDabs;
Q_FOREACH (const KisRenderedDab &dab, state->dabsQueue) {
const QVector<QPoint> normalizationOrigins =
KisWrappedRect::normalizationOriginsForRect(dab.realBounds(), wrapRect);
Q_FOREACH(const QPoint &pt, normalizationOrigins) {
KisRenderedDab newDab = dab;
newDab.offset = pt;
rects.append(newDab.realBounds() & wrapRect);
wrappedDabs.append(newDab);
}
}
state->dabsQueue = wrappedDabs;
} else {
// just get all rects
Q_FOREACH (const KisRenderedDab &dab, state->dabsQueue) {
rects.append(dab.realBounds());
}
}
// split/merge rects into non-overlapping areas
rects = KisPaintOpUtils::splitDabsIntoRects(rects,
idealNumRects, diameter, spacing);
state->allDirtyRects = rects;
Q_FOREACH (const KisRenderedDab &dab, state->dabsQueue) {
state->dabPoints.append(dab.realBounds().center());
}
state->dabRenderingTimer.start();
Q_FOREACH (const QRect &rc, rects) {
jobs.append(
new KisRunnableStrokeJobData(
[rc, state] () {
state->painter->bltFixed(rc, state->dabsQueue);
},
KisStrokeJobData::CONCURRENT));
}
/**
* After the dab has been rendered once, we should mirror it either one
* (h __or__ v) or three (h __and__ v) times. This sequence of 'if's achieves
* the goal without any extra copying. Please note that it has __no__ 'else'
* branches, which is done intentionally!
*/
if (state->painter->hasHorizontalMirroring()) {
addMirroringJobs(Qt::Horizontal, rects, state, jobs);
}
if (state->painter->hasVerticalMirroring()) {
addMirroringJobs(Qt::Vertical, rects, state, jobs);
}
if (state->painter->hasHorizontalMirroring() && state->painter->hasVerticalMirroring()) {
addMirroringJobs(Qt::Horizontal, rects, state, jobs);
}
jobs.append(
new KisRunnableStrokeJobData(
[state, this, someDabsAreStillInQueue] () {
Q_FOREACH(const QRect &rc, state->allDirtyRects) {
state->painter->addDirtyRect(rc);
}
state->painter->setAverageOpacity(state->dabsQueue.last().averageOpacity);
const int updateRenderingTime = state->dabRenderingTimer.elapsed();
const qreal dabRenderingTime = m_dabExecutor->averageDabRenderingTime();
m_avgNumDabs(state->dabsQueue.size());
const qreal currentUpdateTimePerDab = qreal(updateRenderingTime) / state->dabsQueue.size();
m_avgUpdateTimePerDab(currentUpdateTimePerDab);
/**
* NOTE: using currentUpdateTimePerDab in the calculation for the next update time instead
* of the average one makes rendering speed about 40% faster. It happens because the
* adaptation period is shorter than if it used
*/
const qreal totalRenderingTimePerDab = dabRenderingTime + currentUpdateTimePerDab;
const int approxDabRenderingTime =
qreal(totalRenderingTimePerDab) * m_avgNumDabs.rollingMean() / m_idealNumRects;
m_currentUpdatePeriod =
someDabsAreStillInQueue ? m_minUpdatePeriod :
qBound(m_minUpdatePeriod, int(1.5 * approxDabRenderingTime), m_maxUpdatePeriod);
{ // debug chunk
// ENTER_FUNCTION() << ppVar(state->allDirtyRects.size()) << ppVar(state->dabsQueue.size()) << ppVar(dabRenderingTime) << ppVar(updateRenderingTime);
// ENTER_FUNCTION() << ppVar(m_currentUpdatePeriod) << ppVar(someDabsAreStillInQueue);
}
// release all the dab devices
state->dabsQueue.clear();
m_updateSharedState.clear();
},
KisStrokeJobData::SEQUENTIAL));
} else if (m_updateSharedState && hasPreparedDabsAtStart) {
someDabsAreStillInQueue = true;
}
return std::make_pair(m_currentUpdatePeriod, someDabsAreStillInQueue);
}
KisSpacingInformation KisBrushOp::updateSpacingImpl(const KisPaintInformation &info) const
{
const qreal scale = m_sizeOption.apply(info) * KisLodTransform::lodToScale(painter()->device());
qreal rotation = m_rotationOption.apply(info);
return effectiveSpacing(scale, rotation, &m_airbrushOption, &m_spacingOption, info);
}
KisTimingInformation KisBrushOp::updateTimingImpl(const KisPaintInformation &info) const
{
return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOption, &m_rateOption, info);
}
void KisBrushOp::paintLine(const KisPaintInformation& pi1, const KisPaintInformation& pi2, KisDistanceInformation *currentDistance)
{
if (m_sharpnessOption.isChecked() && m_brush && (m_brush->width() == 1) && (m_brush->height() == 1)) {
if (!m_lineCacheDevice) {
m_lineCacheDevice = source()->createCompositionSourceDevice();
}
else {
m_lineCacheDevice->clear();
}
KisPainter p(m_lineCacheDevice);
p.setPaintColor(painter()->paintColor());
p.drawDDALine(pi1.pos(), pi2.pos());
QRect rc = m_lineCacheDevice->extent();
painter()->bitBlt(rc.x(), rc.y(), m_lineCacheDevice, rc.x(), rc.y(), rc.width(), rc.height());
//fixes Bug 338011
painter()->renderMirrorMask(rc, m_lineCacheDevice);
}
else {
KisPaintOp::paintLine(pi1, pi2, currentDistance);
}
}
diff --git a/plugins/paintops/defaultpaintops/brush/kis_brushop_settings_widget.cpp b/plugins/paintops/defaultpaintops/brush/kis_brushop_settings_widget.cpp
index b0d7b31147..3fdf0710bd 100644
--- a/plugins/paintops/defaultpaintops/brush/kis_brushop_settings_widget.cpp
+++ b/plugins/paintops/defaultpaintops/brush/kis_brushop_settings_widget.cpp
@@ -1,164 +1,165 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004-2008 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>
*
* 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_brushop_settings_widget.h"
#include <KisBrushOpSettings.h>
#include <kis_pressure_darken_option.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_flow_option.h>
#include <kis_pressure_size_option.h>
#include <kis_pressure_ratio_option.h>
#include <kis_paint_action_type_option.h>
#include <kis_pressure_rotation_option.h>
#include <kis_pressure_mix_option.h>
#include <kis_curve_option_widget.h>
#include <kis_pressure_hsv_option.h>
#include <kis_airbrush_option_widget.h>
#include <kis_pressure_scatter_option_widget.h>
#include <kis_pressure_softness_option.h>
#include <kis_pressure_sharpness_option_widget.h>
#include <kis_color_source_option_widget.h>
#include <kis_compositeop_option.h>
#include <kis_pressure_flow_opacity_option_widget.h>
#include <kis_pressure_spacing_option_widget.h>
#include <kis_pressure_rate_option.h>
#include "kis_texture_option.h"
#include <kis_pressure_mirror_option_widget.h>
#include "kis_pressure_texture_strength_option.h"
#include <KisMaskingBrushOption.h>
#include <KisPrefixedPaintOpOptionWrapper.h>
#include <KisPaintopSettingsIds.h>
+#include <KisGlobalResourcesInterface.h>
KisBrushOpSettingsWidget::KisBrushOpSettingsWidget(QWidget* parent)
: KisBrushBasedPaintopOptionWidget(parent)
{
setObjectName("brush option widget");
setPrecisionEnabled(true);
// Brush tip options
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisFlowOpacityOptionWidget(), i18n("Opacity"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureFlowOption(), i18n("0%"), i18n("100%")), i18n("Flow"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRatioOption(), i18n("0%"), i18n("100%")), i18n("Ratio"));
addPaintOpOption(new KisPressureSpacingOptionWidget(), i18n("Spacing"));
addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSoftnessOption(), i18n("Soft"), i18n("Hard")), i18n("Softness"));
addPaintOpOption(new KisPressureSharpnessOptionWidget(), i18n("Sharpness"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation"));
addPaintOpOption(new KisPressureScatterOptionWidget(), i18n("Scatter"));
// Colors options
addPaintOpOption(new KisColorSourceOptionWidget(), i18n("Source"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureDarkenOption(), i18n("0.0"), i18n("1.0")), i18n("Darken"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureMixOption(), i18n("Foreground"), i18n("Background")), i18n("Mix"));
addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createHueOption(), KisPressureHSVOption::hueMinLabel(), KisPressureHSVOption::huemaxLabel()), i18n("Hue"));
addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createSaturationOption(), KisPressureHSVOption::saturationMinLabel(), KisPressureHSVOption::saturationmaxLabel()), i18n("Saturation"));
addPaintOpOption(new KisCurveOptionWidget(KisPressureHSVOption::createValueOption(), KisPressureHSVOption::valueMinLabel(), KisPressureHSVOption::valuemaxLabel()), i18nc("HSV Value", "Value"));
addPaintOpOption(new KisAirbrushOptionWidget(false), i18n("Airbrush"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"), i18n("100%")), i18n("Rate"));
KisPaintActionTypeOption *actionTypeOption = new KisPaintActionTypeOption();
addPaintOpOption(actionTypeOption, i18n("Painting Mode"));
addPaintOpOption(new KisTextureOption(), i18n("Pattern"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength"));
KisMaskingBrushOption::MasterBrushSizeAdapter sizeAdapter =
[this] () { return this->brush()->userEffectiveSize(); };
KisMaskingBrushOption *maskingOption = new KisMaskingBrushOption(sizeAdapter);
addPaintOpOption(maskingOption, i18n("Brush Tip"));
connect(maskingOption, SIGNAL(sigCheckedChanged(bool)),
actionTypeOption, SLOT(slotForceWashMode(bool)));
{
KisCurveOption *maskingSizeOption = new KisPressureSizeOption();
maskingSizeOption->setChecked(false);
addPaintOpOption(
new KisPrefixedPaintOpOptionWrapper<KisCurveOptionWidget>(
KisPaintOpUtils::MaskingBrushPresetPrefix,
maskingSizeOption,
i18n("0%"), i18n("100%")),
i18n("Size"), KisPaintOpOption::MASKING_BRUSH);
}
addPaintOpOption(
new KisPrefixedPaintOpOptionWrapper<KisFlowOpacityOptionWidget>(
KisPaintOpUtils::MaskingBrushPresetPrefix),
i18n("Opacity"), KisPaintOpOption::MASKING_BRUSH);
KisCurveOption *maskingFlowOption = new KisPressureFlowOption();
maskingFlowOption->setChecked(false);
KisCurveOption *maskingRatioOption = new KisPressureRatioOption();
maskingRatioOption->setChecked(false);
addPaintOpOption(
new KisPrefixedPaintOpOptionWrapper<KisCurveOptionWidget>(
KisPaintOpUtils::MaskingBrushPresetPrefix,
maskingFlowOption,
i18n("0%"), i18n("100%")),
i18n("Flow"), KisPaintOpOption::MASKING_BRUSH);
addPaintOpOption(
new KisPrefixedPaintOpOptionWrapper<KisCurveOptionWidget>(
KisPaintOpUtils::MaskingBrushPresetPrefix,
maskingRatioOption,
i18n("0%"), i18n("100%")),
i18n("Ratio"), KisPaintOpOption::MASKING_BRUSH);
addPaintOpOption(
new KisPrefixedPaintOpOptionWrapper<KisPressureMirrorOptionWidget>(
KisPaintOpUtils::MaskingBrushPresetPrefix),
i18n("Mirror"), KisPaintOpOption::MASKING_BRUSH);
addPaintOpOption(
new KisPrefixedPaintOpOptionWrapper<KisCurveOptionWidget>(
KisPaintOpUtils::MaskingBrushPresetPrefix,
new KisPressureRotationOption(), i18n("-180°"), i18n("180°")),
i18n("Rotation"), KisPaintOpOption::MASKING_BRUSH);
addPaintOpOption(
new KisPrefixedPaintOpOptionWrapper<KisPressureScatterOptionWidget>(
KisPaintOpUtils::MaskingBrushPresetPrefix),
i18n("Scatter"), KisPaintOpOption::MASKING_BRUSH);
}
KisBrushOpSettingsWidget::~KisBrushOpSettingsWidget()
{
}
KisPropertiesConfigurationSP KisBrushOpSettingsWidget::configuration() const
{
- KisBrushBasedPaintOpSettingsSP config = new KisBrushOpSettings();
+ KisBrushBasedPaintOpSettingsSP config = new KisBrushOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisBrushOpSettingsWidget*>(this));
config->setProperty("paintop", "paintbrush"); // XXX: make this a const id string
writeConfiguration(config);
return config;
}
diff --git a/plugins/paintops/defaultpaintops/brush/tests/KisDabRenderingQueueTest.cpp b/plugins/paintops/defaultpaintops/brush/tests/KisDabRenderingQueueTest.cpp
index c72f7603b4..f75c35284a 100644
--- a/plugins/paintops/defaultpaintops/brush/tests/KisDabRenderingQueueTest.cpp
+++ b/plugins/paintops/defaultpaintops/brush/tests/KisDabRenderingQueueTest.cpp
@@ -1,541 +1,541 @@
/*
* 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 "KisDabRenderingQueueTest.h"
#include <QTest>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <../KisDabRenderingQueue.h>
#include <../KisRenderedDab.h>
#include <../KisDabRenderingJob.h>
struct SurrogateCacheInterface : public KisDabRenderingQueue::CacheInterface
{
void getDabType(bool hasDabInCache,
KisDabCacheUtils::DabRenderingResources *resources,
const KisDabCacheUtils::DabRequestInfo &request,
/* out */
KisDabCacheUtils::DabGenerationInfo *di,
bool *shouldUseCache) override
{
Q_UNUSED(resources);
Q_UNUSED(request);
if (!hasDabInCache || typeOverride == KisDabRenderingJob::Dab) {
di->needsPostprocessing = false;
*shouldUseCache = false;
} else if (typeOverride == KisDabRenderingJob::Copy) {
di->needsPostprocessing = false;
*shouldUseCache = true;
} else if (typeOverride == KisDabRenderingJob::Postprocess) {
di->needsPostprocessing = true;
*shouldUseCache = true;
}
di->info = request.info;
}
bool hasSeparateOriginal(KisDabCacheUtils::DabRenderingResources *resources) const override {
Q_UNUSED(resources);
return typeOverride == KisDabRenderingJob::Postprocess;
}
KisDabRenderingJob::JobType typeOverride = KisDabRenderingJob::Dab;
};
#include <kis_mask_generator.h>
#include "kis_auto_brush.h"
KisDabCacheUtils::DabRenderingResources *testResourcesFactory()
{
KisDabCacheUtils::DabRenderingResources *resources =
new KisDabCacheUtils::DabRenderingResources();
KisCircleMaskGenerator* circle = new KisCircleMaskGenerator(10, 1.0, 1.0, 1.0, 2, false);
- KisBrushSP brush = new KisAutoBrush(circle, 0.0, 0.0);
+ KisBrushSP brush(new KisAutoBrush(circle, 0.0, 0.0));
resources->brush = brush;
return resources;
}
void KisDabRenderingQueueTest::testCachedDabs()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
SurrogateCacheInterface *cacheInterface = new SurrogateCacheInterface();
KisDabRenderingQueue queue(cs, testResourcesFactory);
queue.setCacheInterface(cacheInterface);
KoColor color;
QPointF pos1(10,10);
QPointF pos2(20,20);
KisDabShape shape;
KisPaintInformation pi1(pos1);
KisPaintInformation pi2(pos2);
KisDabCacheUtils::DabRequestInfo request1(color, pos1, shape, pi1, 1.0);
KisDabCacheUtils::DabRequestInfo request2(color, pos2, shape, pi2, 1.0);
cacheInterface->typeOverride = KisDabRenderingJob::Dab;
KisDabRenderingJobSP job0 = queue.addDab(request1, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(job0);
QCOMPARE(job0->seqNo, 0);
QCOMPARE(job0->generationInfo.info.pos(), request1.info.pos());
QCOMPARE(job0->type, KisDabRenderingJob::Dab);
QVERIFY(!job0->originalDevice);
QVERIFY(!job0->postprocessedDevice);
cacheInterface->typeOverride = KisDabRenderingJob::Dab;
KisDabRenderingJobSP job1 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(job1);
QCOMPARE(job1->seqNo, 1);
QCOMPARE(job1->generationInfo.info.pos(), request2.info.pos());
QCOMPARE(job1->type, KisDabRenderingJob::Dab);
QVERIFY(!job1->originalDevice);
QVERIFY(!job1->postprocessedDevice);
cacheInterface->typeOverride = KisDabRenderingJob::Copy;
KisDabRenderingJobSP job2 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(!job2);
cacheInterface->typeOverride = KisDabRenderingJob::Copy;
KisDabRenderingJobSP job3 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(!job3);
// we only added the dabs, but we haven't completed them yet
QVERIFY(!queue.hasPreparedDabs());
QCOMPARE(queue.testingGetQueueSize(), 4);
QList<KisDabRenderingJobSP > jobs;
QList<KisRenderedDab> renderedDabs;
{
// we've completed job0
job0->originalDevice = new KisFixedPaintDevice(cs);
job0->postprocessedDevice = job0->originalDevice;
jobs = queue.notifyJobFinished(job0->seqNo);
QVERIFY(jobs.isEmpty());
// now we should have at least one job in prepared state
QVERIFY(queue.hasPreparedDabs());
// take the prepared dabs
renderedDabs = queue.takeReadyDabs();
QCOMPARE(renderedDabs.size(), 1);
// the list should be empty again
QVERIFY(!queue.hasPreparedDabs());
QCOMPARE(queue.testingGetQueueSize(), 3);
}
{
// we've completed job1
job1->originalDevice = new KisFixedPaintDevice(cs);
job1->postprocessedDevice = job1->originalDevice;
jobs = queue.notifyJobFinished(job1->seqNo);
QVERIFY(jobs.isEmpty());
// now we should have at least one job in prepared state
QVERIFY(queue.hasPreparedDabs());
// take the prepared dabs
renderedDabs = queue.takeReadyDabs();
QCOMPARE(renderedDabs.size(), 3);
// since they are copies, they should be the same
QCOMPARE(renderedDabs[1].device, renderedDabs[0].device);
QCOMPARE(renderedDabs[2].device, renderedDabs[0].device);
// the list should be empty again
QVERIFY(!queue.hasPreparedDabs());
// we delete all the painted jobs except the latest 'dab' job
QCOMPARE(queue.testingGetQueueSize(), 1);
}
{
// add one more cached job and take it
cacheInterface->typeOverride = KisDabRenderingJob::Copy;
KisDabRenderingJobSP job = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(!job);
// now we should have at least one job in prepared state
QVERIFY(queue.hasPreparedDabs());
// take the prepared dabs
renderedDabs = queue.takeReadyDabs();
QCOMPARE(renderedDabs.size(), 1);
// the list should be empty again
QVERIFY(!queue.hasPreparedDabs());
// we delete all the painted jobs except the latest 'dab' job
QCOMPARE(queue.testingGetQueueSize(), 1);
}
{
// add a 'dab' job and complete it
cacheInterface->typeOverride = KisDabRenderingJob::Dab;
KisDabRenderingJobSP job = queue.addDab(request1, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(job);
QCOMPARE(job->seqNo, 5);
QCOMPARE(job->generationInfo.info.pos(), request1.info.pos());
QCOMPARE(job->type, KisDabRenderingJob::Dab);
QVERIFY(!job->originalDevice);
QVERIFY(!job->postprocessedDevice);
// now the queue can be cleared from the completed dabs!
QCOMPARE(queue.testingGetQueueSize(), 1);
job->originalDevice = new KisFixedPaintDevice(cs);
job->postprocessedDevice = job->originalDevice;
jobs = queue.notifyJobFinished(job->seqNo);
QVERIFY(jobs.isEmpty());
// now we should have at least one job in prepared state
QVERIFY(queue.hasPreparedDabs());
// take the prepared dabs
renderedDabs = queue.takeReadyDabs();
QCOMPARE(renderedDabs.size(), 1);
// the list should be empty again
QVERIFY(!queue.hasPreparedDabs());
// we do not delete the queue of jobs until the next 'dab'
// job arrives
QCOMPARE(queue.testingGetQueueSize(), 1);
}
}
void KisDabRenderingQueueTest::testPostprocessedDabs()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
SurrogateCacheInterface *cacheInterface = new SurrogateCacheInterface();
KisDabRenderingQueue queue(cs, testResourcesFactory);
queue.setCacheInterface(cacheInterface);
KoColor color;
QPointF pos1(10,10);
QPointF pos2(20,20);
KisDabShape shape;
KisPaintInformation pi1(pos1);
KisPaintInformation pi2(pos2);
KisDabCacheUtils::DabRequestInfo request1(color, pos1, shape, pi1, 1.0);
KisDabCacheUtils::DabRequestInfo request2(color, pos2, shape, pi2, 1.0);
cacheInterface->typeOverride = KisDabRenderingJob::Dab;
KisDabRenderingJobSP job0 = queue.addDab(request1, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(job0);
QCOMPARE(job0->seqNo, 0);
QCOMPARE(job0->generationInfo.info.pos(), request1.info.pos());
QCOMPARE(job0->type, KisDabRenderingJob::Dab);
QVERIFY(!job0->originalDevice);
QVERIFY(!job0->postprocessedDevice);
cacheInterface->typeOverride = KisDabRenderingJob::Dab;
KisDabRenderingJobSP job1 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(job1);
QCOMPARE(job1->seqNo, 1);
QCOMPARE(job1->generationInfo.info.pos(), request2.info.pos());
QCOMPARE(job1->type, KisDabRenderingJob::Dab);
QVERIFY(!job1->originalDevice);
QVERIFY(!job1->postprocessedDevice);
cacheInterface->typeOverride = KisDabRenderingJob::Postprocess;
KisDabRenderingJobSP job2 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(!job2);
cacheInterface->typeOverride = KisDabRenderingJob::Postprocess;
KisDabRenderingJobSP job3 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(!job3);
// we only added the dabs, but we haven't completed them yet
QVERIFY(!queue.hasPreparedDabs());
QCOMPARE(queue.testingGetQueueSize(), 4);
QList<KisDabRenderingJobSP > jobs;
QList<KisRenderedDab> renderedDabs;
{
// we've completed job0
job0->originalDevice = new KisFixedPaintDevice(cs);
job0->postprocessedDevice = job0->originalDevice;
jobs = queue.notifyJobFinished(job0->seqNo);
QVERIFY(jobs.isEmpty());
// now we should have at least one job in prepared state
QVERIFY(queue.hasPreparedDabs());
// take the prepared dabs
renderedDabs = queue.takeReadyDabs();
QCOMPARE(renderedDabs.size(), 1);
// the list should be empty again
QVERIFY(!queue.hasPreparedDabs());
QCOMPARE(queue.testingGetQueueSize(), 3);
}
{
// we've completed job1
job1->originalDevice = new KisFixedPaintDevice(cs);
job1->postprocessedDevice = job1->originalDevice;
jobs = queue.notifyJobFinished(job1->seqNo);
QCOMPARE(jobs.size(), 2);
QCOMPARE(jobs[0]->seqNo, 2);
QCOMPARE(jobs[1]->seqNo, 3);
QVERIFY(jobs[0]->originalDevice);
QVERIFY(!jobs[0]->postprocessedDevice);
QVERIFY(jobs[1]->originalDevice);
QVERIFY(!jobs[1]->postprocessedDevice);
// pretend we have created a postprocessed device
jobs[0]->postprocessedDevice = new KisFixedPaintDevice(cs);
jobs[1]->postprocessedDevice = new KisFixedPaintDevice(cs);
// now we should have at least one job in prepared state
QVERIFY(queue.hasPreparedDabs());
// take the prepared dabs
renderedDabs = queue.takeReadyDabs();
QCOMPARE(renderedDabs.size(), 1);
// the list should be empty again
QVERIFY(!queue.hasPreparedDabs());
// return back two postprocessed dabs
QList<KisDabRenderingJobSP > emptyJobs;
emptyJobs = queue.notifyJobFinished(jobs[0]->seqNo);
QVERIFY(emptyJobs.isEmpty());
emptyJobs = queue.notifyJobFinished(jobs[1]->seqNo);
QVERIFY(emptyJobs.isEmpty());
// now we should have at least one job in prepared state
QVERIFY(queue.hasPreparedDabs());
// take the prepared dabs
renderedDabs = queue.takeReadyDabs();
QCOMPARE(renderedDabs.size(), 2);
// the list should be empty again
QVERIFY(!queue.hasPreparedDabs());
// we delete all the painted jobs except the latest 'dab' job
QCOMPARE(queue.testingGetQueueSize(), 1);
}
{
// add one more postprocessed job and take it
cacheInterface->typeOverride = KisDabRenderingJob::Postprocess;
KisDabRenderingJobSP job = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(job);
QCOMPARE(job->seqNo, 4);
QCOMPARE(job->generationInfo.info.pos(), request2.info.pos());
ENTER_FUNCTION() << ppVar(job->type);
QCOMPARE(job->type, KisDabRenderingJob::Postprocess);
QVERIFY(job->originalDevice);
QVERIFY(!job->postprocessedDevice);
// the list should still be empty
QVERIFY(!queue.hasPreparedDabs());
// pretend we have created a postprocessed device
job->postprocessedDevice = new KisFixedPaintDevice(cs);
// return back the postprocessed dab
QList<KisDabRenderingJobSP > emptyJobs;
emptyJobs = queue.notifyJobFinished(job->seqNo);
QVERIFY(emptyJobs.isEmpty());
// now we should have at least one job in prepared state
QVERIFY(queue.hasPreparedDabs());
// take the prepared dabs
renderedDabs = queue.takeReadyDabs();
QCOMPARE(renderedDabs.size(), 1);
// the list should be empty again
QVERIFY(!queue.hasPreparedDabs());
// we delete all the painted jobs except the latest 'dab' job
QCOMPARE(queue.testingGetQueueSize(), 1);
}
{
// add a 'dab' job and complete it. That will clear the queue!
cacheInterface->typeOverride = KisDabRenderingJob::Dab;
KisDabRenderingJobSP job = queue.addDab(request1, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(job);
QCOMPARE(job->seqNo, 5);
QCOMPARE(job->generationInfo.info.pos(), request1.info.pos());
QCOMPARE(job->type, KisDabRenderingJob::Dab);
QVERIFY(!job->originalDevice);
QVERIFY(!job->postprocessedDevice);
// now the queue can be cleared from the completed dabs!
QCOMPARE(queue.testingGetQueueSize(), 1);
job->originalDevice = new KisFixedPaintDevice(cs);
job->postprocessedDevice = job->originalDevice;
jobs = queue.notifyJobFinished(job->seqNo);
QVERIFY(jobs.isEmpty());
// now we should have at least one job in prepared state
QVERIFY(queue.hasPreparedDabs());
// take the prepared dabs
renderedDabs = queue.takeReadyDabs();
QCOMPARE(renderedDabs.size(), 1);
// the list should be empty again
QVERIFY(!queue.hasPreparedDabs());
// we do not delete the queue of jobs until the next 'dab'
// job arrives
QCOMPARE(queue.testingGetQueueSize(), 1);
}
}
#include <../KisDabRenderingQueueCache.h>
void KisDabRenderingQueueTest::testRunningJobs()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisDabRenderingQueueCache *cacheInterface = new KisDabRenderingQueueCache();
// we do *not* initialize any options yet!
KisDabRenderingQueue queue(cs, testResourcesFactory);
queue.setCacheInterface(cacheInterface);
KoColor color(Qt::red, cs);
QPointF pos1(10,10);
QPointF pos2(20,20);
KisDabShape shape;
KisPaintInformation pi1(pos1);
KisPaintInformation pi2(pos2);
KisDabCacheUtils::DabRequestInfo request1(color, pos1, shape, pi1, 1.0);
KisDabCacheUtils::DabRequestInfo request2(color, pos2, shape, pi2, 1.0);
KisDabRenderingJobSP job0 = queue.addDab(request1, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(job0);
QCOMPARE(job0->seqNo, 0);
QCOMPARE(job0->generationInfo.info.pos(), request1.info.pos());
QCOMPARE(job0->type, KisDabRenderingJob::Dab);
QVERIFY(!job0->originalDevice);
QVERIFY(!job0->postprocessedDevice);
KisDabRenderingJobRunner runner(job0, &queue, 0);
runner.run();
QVERIFY(job0->originalDevice);
QVERIFY(job0->postprocessedDevice);
QCOMPARE(job0->originalDevice, job0->postprocessedDevice);
QVERIFY(!job0->originalDevice->bounds().isEmpty());
KisDabRenderingJobSP job1 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
QVERIFY(!job1);
QList<KisRenderedDab> renderedDabs = queue.takeReadyDabs();
QCOMPARE(renderedDabs.size(), 2);
// we did the caching
QVERIFY(renderedDabs[0].device == renderedDabs[1].device);
QCOMPARE(renderedDabs[0].offset, QPoint(5,5));
QCOMPARE(renderedDabs[1].offset, QPoint(15,15));
}
#include "../KisDabRenderingExecutor.h"
#include "KisFakeRunnableStrokeJobsExecutor.h"
void KisDabRenderingQueueTest::testExecutor()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
QScopedPointer<KisRunnableStrokeJobsInterface> runner(new KisFakeRunnableStrokeJobsExecutor());
KisDabRenderingExecutor executor(cs, testResourcesFactory, runner.data());
KoColor color(Qt::red, cs);
QPointF pos1(10,10);
QPointF pos2(20,20);
KisDabShape shape;
KisPaintInformation pi1(pos1);
KisPaintInformation pi2(pos2);
KisDabCacheUtils::DabRequestInfo request1(color, pos1, shape, pi1, 1.0);
KisDabCacheUtils::DabRequestInfo request2(color, pos2, shape, pi2, 1.0);
executor.addDab(request1, 0.5, 0.25);
executor.addDab(request2, 0.125, 1.0);
QList<KisRenderedDab> renderedDabs = executor.takeReadyDabs();
QCOMPARE(renderedDabs.size(), 2);
// we did the caching
QVERIFY(renderedDabs[0].device == renderedDabs[1].device);
QCOMPARE(renderedDabs[0].offset, QPoint(5,5));
QCOMPARE(renderedDabs[1].offset, QPoint(15,15));
QCOMPARE(renderedDabs[0].opacity, 0.5);
QCOMPARE(renderedDabs[0].flow, 0.25);
QCOMPARE(renderedDabs[1].opacity, 0.125);
QCOMPARE(renderedDabs[1].flow, 1.0);
}
QTEST_MAIN(KisDabRenderingQueueTest)
diff --git a/plugins/paintops/defaultpaintops/brush/tests/kis_brushop_test.cpp b/plugins/paintops/defaultpaintops/brush/tests/kis_brushop_test.cpp
index 30b0f85fdf..7389cbc8f5 100644
--- a/plugins/paintops/defaultpaintops/brush/tests/kis_brushop_test.cpp
+++ b/plugins/paintops/defaultpaintops/brush/tests/kis_brushop_test.cpp
@@ -1,254 +1,254 @@
/*
* 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_brushop_test.h"
#include <QTest>
#include <qimage_based_test.h>
#include <stroke_testing_utils.h>
#include <brushengine/kis_paint_information.h>
#include <kis_canvas_resource_provider.h>
#include <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_settings.h>
#include <kis_pressure_mirror_option.h>
#include <kis_pressure_rotation_option.h>
class TestBrushOp : public TestUtil::QImageBasedTest
{
public:
TestBrushOp(const QString &presetFileName, const QString &prefix = "simple")
: QImageBasedTest("brushop") {
m_presetFileName = presetFileName;
m_prefix = prefix;
}
virtual ~TestBrushOp() {}
void test() {
test(false, false, 0.0);
test(false, false, 10.0);
test(false, false, 20.0);
test(true, false, 0.0);
test(true, false, 10.0);
test(true, false, 20.0);
test(false, true, 0.0);
test(false, true, 10.0);
test(false, true, 20.0);
test(true, true, 0.0);
test(true, true, 10.0);
test(true, true, 20.0);
}
void test(bool mirrorX, bool mirrorY, qreal rotation) {
test(mirrorX, mirrorY, rotation, false, false);
test(mirrorX, mirrorY, rotation, true, false);
test(mirrorX, mirrorY, rotation, false, true);
test(mirrorX, mirrorY, rotation, true, true);
}
void test(bool mirrorX, bool mirrorY, qreal rotation, bool mirrorDabX, bool mirrorDabY) {
test(mirrorX, mirrorY, rotation, mirrorDabX, mirrorDabY, 0.0);
test(mirrorX, mirrorY, rotation, mirrorDabX, mirrorDabY, 360.0 - 10.0);
test(mirrorX, mirrorY, rotation, mirrorDabX, mirrorDabY, 360.0 - 20.0);
}
void test(bool mirrorX, bool mirrorY, qreal rotation, bool mirrorDabX, bool mirrorDabY, qreal dabRotation) {
KisSurrogateUndoStore *undoStore = new KisSurrogateUndoStore();
KisImageSP image = createTrivialImage(undoStore);
image->initialRefreshGraph();
KisNodeSP paint1 = findNode(image->root(), "paint1");
QVERIFY(paint1->extent().isEmpty());
KisPainter gc(paint1->paintDevice());
QScopedPointer<KoCanvasResourceProvider> manager(
utils::createResourceManager(image, 0, m_presetFileName));
KisPaintOpPresetSP preset =
manager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
if (mirrorDabX || mirrorDabY) {
KisPaintOpSettingsSP settings = preset->settings()->clone();
KisPressureMirrorOption mirrorOption;
mirrorOption.readOptionSetting(settings);
mirrorOption.setChecked(true);
mirrorOption.setCurveUsed(false);
mirrorOption.enableHorizontalMirror(mirrorDabX);
mirrorOption.enableVerticalMirror(mirrorDabY);
mirrorOption.writeOptionSetting(settings.data());
preset->setSettings(settings);
}
if (dabRotation != 0.0) {
KisPaintOpSettingsSP settings = preset->settings()->clone();
KisPressureRotationOption rotationOption;
rotationOption.readOptionSetting(settings);
rotationOption.setChecked(true);
rotationOption.setCurveUsed(false);
rotationOption.setValue(dabRotation / 360.0);
rotationOption.writeOptionSetting(settings.data());
preset->setSettings(settings);
}
QString testName =
QString("%7_cmY_%1_cmX_%2_cR_%3_dmX_%4_dmY_%5_dR_%6")
.arg(mirrorY)
.arg(mirrorX)
.arg(rotation)
.arg(mirrorDabX)
.arg(mirrorDabY)
.arg(std::fmod(360.0 - dabRotation, 360.0))
.arg(m_prefix);
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image,
paint1,
manager.data());
resources->setupPainter(&gc);
doPaint(gc, rotation, mirrorX, mirrorY);
checkOneLayer(image, paint1, testName);
}
virtual void doPaint(KisPainter &gc, qreal rotation, bool mirrorX, bool mirrorY) {
KisPaintInformation pi(QPointF(100, 100), 1.0);
pi.setCanvasRotation(rotation);
pi.setCanvasMirroredH(mirrorX);
pi.setCanvasMirroredV(mirrorY);
KisDistanceInformation dist;
gc.paintAt(pi, &dist);
}
QString m_presetFileName;
QString m_prefix;
};
class TestBrushOpLines : public TestBrushOp
{
public:
TestBrushOpLines(const QString &presetFileName)
: TestBrushOp(presetFileName) {
}
void doPaint(KisPainter &gc, qreal rotation, bool mirrorX, bool mirrorY) override {
QVector<KisPaintInformation> vector;
vector << KisPaintInformation(QPointF(100, 100));
vector << KisPaintInformation(QPointF(200, 150));
vector << KisPaintInformation(QPointF(100, 350));
for (auto pi : vector) {
pi.setCanvasRotation(rotation);
pi.setCanvasMirroredH(mirrorX);
pi.setCanvasMirroredV(mirrorY);
}
KisDistanceInformation dist;
for (int i = 1; i < vector.size(); i++) {
gc.paintLine(vector[i - 1], vector[i], &dist);
}
}
};
class TestBrushOpPressureLines : public TestBrushOp
{
public:
TestBrushOpPressureLines(const QString &presetFileName, const QString &prefix)
: TestBrushOp(presetFileName, prefix) {
}
void doPaint(KisPainter &gc, qreal rotation, bool mirrorX, bool mirrorY) override {
QVector<KisPaintInformation> vector;
vector << KisPaintInformation(QPointF(0, 0), 0.2);
vector << KisPaintInformation(QPointF(200, 50), 1.0);
vector << KisPaintInformation(QPointF(100, 250), 0.0);
vector << KisPaintInformation(QPointF(200, 150), 1.0);
vector << KisPaintInformation(QPointF(100, 350), 1.0);
for (auto pi : vector) {
pi.setCanvasRotation(rotation);
pi.setCanvasMirroredH(mirrorX);
pi.setCanvasMirroredV(mirrorY);
}
KisDistanceInformation dist;
for (int i = 1; i < vector.size(); i++) {
gc.paintLine(vector[i - 1], vector[i], &dist);
}
}
};
#include <KoResourcePaths.h>
void KisBrushOpTest::initTestCase()
{
- KoResourcePaths::addResourceDir("kis_brushes", QString(SYSTEM_RESOURCES_DATA_DIR) + "/brushes");
+ KoResourcePaths::addResourceDir(ResourceType::Brushes, QString(SYSTEM_RESOURCES_DATA_DIR) + "/brushes");
}
void KisBrushOpTest::testRotationMirroring()
{
TestBrushOp t("LR_simple.kpp");
t.test();
}
void KisBrushOpTest::testRotationMirroringDrawingAngle()
{
TestBrushOpLines t("LR_drawing_angle.kpp");
t.test();
}
void KisBrushOpTest::testMagicSeven()
{
/**
* A special preset that forces Qt to bug:
* mask size: 56
* brush size: 7
* therefore scale is: 0.125
* which causes QTransform work as a pure Translate in the mipmap
*/
TestBrushOpPressureLines t("magic_seven.kpp", "magicseven");
t.test();
}
QTEST_MAIN(KisBrushOpTest)
diff --git a/plugins/paintops/defaultpaintops/defaultpaintops_plugin.cc b/plugins/paintops/defaultpaintops/defaultpaintops_plugin.cc
index 1e1df6a90d..936e4ce4a3 100644
--- a/plugins/paintops/defaultpaintops/defaultpaintops_plugin.cc
+++ b/plugins/paintops/defaultpaintops/defaultpaintops_plugin.cc
@@ -1,55 +1,55 @@
/*
* defaultpaintops_plugin.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 "defaultpaintops_plugin.h"
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <KoCompositeOpRegistry.h>
#include "kis_simple_paintop_factory.h"
#include "kis_brushop.h"
#include "kis_brushop_settings_widget.h"
#include "kis_duplicateop.h"
#include "kis_duplicateop_settings.h"
#include "kis_global.h"
#include <brushengine/kis_paintop_registry.h>
#include "KisBrushOpSettings.h"
-#include "kis_brush_server.h"
+#include "KisBrushServerProvider.h"
#include "kis_duplicateop_settings_widget.h"
K_PLUGIN_FACTORY_WITH_JSON(DefaultPaintOpsPluginFactory, "kritadefaultpaintops.json", registerPlugin<DefaultPaintOpsPlugin>();)
DefaultPaintOpsPlugin::DefaultPaintOpsPlugin(QObject *parent, const QVariantList &)
: QObject(parent)
{
KisPaintOpRegistry *r = KisPaintOpRegistry::instance();
r->add(new KisSimplePaintOpFactory<KisBrushOp, KisBrushOpSettings, KisBrushOpSettingsWidget>("paintbrush", i18nc("Pixel paintbrush", "Pixel"), KisPaintOpFactory::categoryStable(), "krita-paintbrush.png", QString(), QStringList(), 1));
r->add(new KisSimplePaintOpFactory<KisDuplicateOp, KisDuplicateOpSettings, KisDuplicateOpSettingsWidget>("duplicate", i18nc("clone paintbrush (previously \"Duplicate\")", "Clone"), KisPaintOpFactory::categoryStable(), "krita-duplicate.png", QString(), QStringList(COMPOSITE_COPY), 15));
}
DefaultPaintOpsPlugin::~DefaultPaintOpsPlugin()
{
}
#include "defaultpaintops_plugin.moc"
diff --git a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.cpp b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.cpp
index 41439ca721..1aec19c6ff 100644
--- a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.cpp
+++ b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.cpp
@@ -1,261 +1,263 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004-2008 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>
*
* 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_duplicateop_settings.h"
#include "kis_duplicateop_option.h"
#include "kis_duplicateop_settings_widget.h"
#include <QDomElement>
#include <QDomDocument>
#include <KoPointerEvent.h>
#include <KoCompositeOpRegistry.h>
#include <kis_image.h>
#include <kis_brush_option_widget.h>
#include <kis_paintop_settings_widget.h>
#include <kis_pressure_darken_option.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_size_option.h>
#include <kis_paint_action_type_option.h>
#include <kis_dom_utils.h>
-KisDuplicateOpSettings::KisDuplicateOpSettings()
- : m_isOffsetNotUptodate(false), m_duringPaintingStroke(false)
+KisDuplicateOpSettings::KisDuplicateOpSettings(KisResourcesInterfaceSP resourcesInterface)
+ : KisBrushBasedPaintOpSettings(resourcesInterface),
+ m_isOffsetNotUptodate(false),
+ m_duringPaintingStroke(false)
{
}
KisDuplicateOpSettings::~KisDuplicateOpSettings()
{
}
bool KisDuplicateOpSettings::paintIncremental()
{
return false;
}
QString KisDuplicateOpSettings::indirectPaintingCompositeOp() const
{
return COMPOSITE_COPY;
}
QPointF KisDuplicateOpSettings::offset() const
{
return m_offset;
}
QPointF KisDuplicateOpSettings::position() const
{
return m_position;
}
bool KisDuplicateOpSettings::mousePressEvent(const KisPaintInformation &info, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode)
{
bool ignoreEvent = true;
if (modifiers & Qt::ControlModifier) {
if (!m_sourceNode || !(modifiers & Qt::AltModifier)) {
m_sourceNode = currentNode;
}
m_position = info.pos();
m_isOffsetNotUptodate = true;
ignoreEvent = false;
}
else {
bool resetOrigin = getBool(DUPLICATE_RESET_SOURCE_POINT);
if (m_isOffsetNotUptodate || resetOrigin) {
m_offset = info.pos() - m_position;
m_isOffsetNotUptodate = false;
}
m_duringPaintingStroke = true;
ignoreEvent = true;
}
return ignoreEvent;
}
bool KisDuplicateOpSettings::mouseReleaseEvent()
{
m_duringPaintingStroke = false;
bool ignoreEvent = true;
return ignoreEvent;
}
KisNodeWSP KisDuplicateOpSettings::sourceNode() const
{
return m_sourceNode;
}
void KisDuplicateOpSettings::activate()
{
}
void KisDuplicateOpSettings::fromXML(const QDomElement& elt)
{
// First, call the parent class fromXML to make sure all the
// properties are saved to the map
KisPaintOpSettings::fromXML(elt);
m_offset.setX(KisDomUtils::toDouble(elt.attribute("OffsetX", "0.0")));
m_offset.setY(KisDomUtils::toDouble(elt.attribute("OffsetY", "0.0")));
m_isOffsetNotUptodate = false;
}
void KisDuplicateOpSettings::toXML(QDomDocument& doc, QDomElement& rootElt) const
{
// Then call the parent class fromXML
KisPropertiesConfiguration::toXML(doc, rootElt);
rootElt.setAttribute("OffsetX", QString::number(m_offset.x()));
rootElt.setAttribute("OffsetY", QString::number(m_offset.y()));
}
KisPaintOpSettingsSP KisDuplicateOpSettings::clone() const
{
KisPaintOpSettingsSP setting = KisBrushBasedPaintOpSettings::clone();
KisDuplicateOpSettings* s = static_cast<KisDuplicateOpSettings*>(setting.data());
s->m_offset = m_offset;
s->m_isOffsetNotUptodate = m_isOffsetNotUptodate;
s->m_position = m_position;
s->m_sourceNode = m_sourceNode;
s->m_duringPaintingStroke = m_duringPaintingStroke;
return setting;
}
QPainterPath KisDuplicateOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom)
{
QPainterPath path;
OutlineMode forcedMode = mode;
if (!forcedMode.isVisible) {
forcedMode.isVisible = true;
forcedMode.forceCircle = true;
}
// clone tool should always show an outline
path = KisBrushBasedPaintOpSettings::brushOutlineImpl(info, forcedMode, alignForZoom, 1.0);
QPainterPath copy(path);
QRectF rect2 = copy.boundingRect();
bool shouldStayInOrigin = m_isOffsetNotUptodate // the clone brush right now waits for first stroke with a new origin, so stays at origin point
|| !getBool(DUPLICATE_MOVE_SOURCE_POINT) // the brush always use the same source point, so stays at origin point
|| (!m_duringPaintingStroke && getBool(DUPLICATE_RESET_SOURCE_POINT)); // during the stroke, with reset Origin selected, outline should stay at origin point
if (shouldStayInOrigin) {
copy.translate(m_position - info.pos());
}
else {
copy.translate(-m_offset);
}
path.addPath(copy);
qreal dx = rect2.width() / 4.0;
qreal dy = rect2.height() / 4.0;
rect2.adjust(dx, dy, -dx, -dy);
path.moveTo(rect2.topLeft());
path.lineTo(rect2.bottomRight());
path.moveTo(rect2.topRight());
path.lineTo(rect2.bottomLeft());
return path;
}
#include <brushengine/kis_uniform_paintop_property.h>
#include "kis_paintop_preset.h"
#include "kis_paintop_settings_update_proxy.h"
#include "kis_standard_uniform_properties_factory.h"
QList<KisUniformPaintOpPropertySP> KisDuplicateOpSettings::uniformProperties(KisPaintOpSettingsSP settings)
{
QList<KisUniformPaintOpPropertySP> props =
- listWeakToStrong(m_uniformProperties);
+ listWeakToStrong(m_uniformProperties);
if (props.isEmpty()) {
{
KisUniformPaintOpPropertyCallback *prop =
- new KisUniformPaintOpPropertyCallback(
- KisUniformPaintOpPropertyCallback::Bool,
- "clone_healing",
- i18n("Healing"),
- settings, 0);
+ new KisUniformPaintOpPropertyCallback(
+ KisUniformPaintOpPropertyCallback::Bool,
+ "clone_healing",
+ i18n("Healing"),
+ settings, 0);
prop->setReadCallback(
- [](KisUniformPaintOpProperty *prop) {
- KisDuplicateOptionProperties option;
- option.readOptionSetting(prop->settings().data());
+ [](KisUniformPaintOpProperty *prop) {
+ KisDuplicateOptionProperties option;
+ option.readOptionSetting(prop->settings().data());
- prop->setValue(option.duplicate_healing);
- });
+ prop->setValue(option.duplicate_healing);
+ });
prop->setWriteCallback(
- [](KisUniformPaintOpProperty *prop) {
- KisDuplicateOptionProperties option;
- option.readOptionSetting(prop->settings().data());
- option.duplicate_healing = prop->value().toBool();
- option.writeOptionSetting(prop->settings().data());
- });
-
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ [](KisUniformPaintOpProperty *prop) {
+ KisDuplicateOptionProperties option;
+ option.readOptionSetting(prop->settings().data());
+ option.duplicate_healing = prop->value().toBool();
+ option.writeOptionSetting(prop->settings().data());
+ });
+
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisUniformPaintOpPropertyCallback *prop =
- new KisUniformPaintOpPropertyCallback(
- KisUniformPaintOpPropertyCallback::Bool,
- "clone_movesource",
- i18n("Move Source"),
- settings, 0);
+ new KisUniformPaintOpPropertyCallback(
+ KisUniformPaintOpPropertyCallback::Bool,
+ "clone_movesource",
+ i18n("Move `Source"),
+ settings, 0);
prop->setReadCallback(
- [](KisUniformPaintOpProperty *prop) {
- KisDuplicateOptionProperties option;
- option.readOptionSetting(prop->settings().data());
+ [](KisUniformPaintOpProperty *prop) {
+ KisDuplicateOptionProperties option;
+ option.readOptionSetting(prop->settings().data());
- prop->setValue(option.duplicate_move_source_point);
- });
+ prop->setValue(option.duplicate_move_source_point);
+ });
prop->setWriteCallback(
- [](KisUniformPaintOpProperty *prop) {
- KisDuplicateOptionProperties option;
- option.readOptionSetting(prop->settings().data());
- option.duplicate_move_source_point = prop->value().toBool();
- option.writeOptionSetting(prop->settings().data());
- });
-
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ [](KisUniformPaintOpProperty *prop) {
+ KisDuplicateOptionProperties option;
+ option.readOptionSetting(prop->settings().data());
+ option.duplicate_move_source_point = prop->value().toBool();
+ option.writeOptionSetting(prop->settings().data());
+ });
+
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
}
return KisPaintOpSettings::uniformProperties(settings) + props;
}
diff --git a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.h b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.h
index caab483d54..d255cf6d02 100644
--- a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.h
+++ b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.h
@@ -1,86 +1,86 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004-2008 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>
*
* 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_DUPLICATEOP_SETTINGS_H_
#define KIS_DUPLICATEOP_SETTINGS_H_
#include <kis_brush_based_paintop_settings.h>
#include <kis_types.h>
#include <QPointF>
class QDomElement;
class KisDuplicateOpSettings : public KisBrushBasedPaintOpSettings
{
public:
using KisPaintOpSettings::fromXML;
using KisPaintOpSettings::toXML;
- KisDuplicateOpSettings();
+ KisDuplicateOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisDuplicateOpSettings() override;
bool paintIncremental() override;
QString indirectPaintingCompositeOp() const override;
QPointF offset() const;
QPointF position() const;
/**
* This function is called by a tool when the mouse is pressed.
* Returns false if picking new origin is in action,
* and returns true otherwise (i.e. if brush is starting a new stroke).
* See kis_tool_freehand:tryPickByPaintOp()
*/
bool mousePressEvent(const KisPaintInformation& pos, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode) override;
/**
* This function is called by a tool when the mouse is released.
* If the tool is supposed to ignore the event, the paint op should return true
* and if the tool is supposed to use the event, return false.
*/
bool mouseReleaseEvent() override;
void activate() override;
void fromXML(const QDomElement& elt) override;
void toXML(QDomDocument& doc, QDomElement& rootElt) const override;
KisPaintOpSettingsSP clone() const override;
using KisBrushBasedPaintOpSettings::brushOutline;
QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override;
KisNodeWSP sourceNode() const;
QList<KisUniformPaintOpPropertySP> uniformProperties(KisPaintOpSettingsSP settings) override;
public:
Q_DISABLE_COPY(KisDuplicateOpSettings)
QPointF m_offset;
bool m_isOffsetNotUptodate; // true between the act of setting a new origin and the first stroke
bool m_duringPaintingStroke; // true if the stroke is begin painted now, false otherwise
QPointF m_position; // Give the position of the last alt-click
KisNodeWSP m_sourceNode; // Give the node of the source point (origin)
QList<KisUniformPaintOpPropertyWSP> m_uniformProperties;
};
typedef KisSharedPtr<KisDuplicateOpSettings> KisDuplicateOpSettingsSP;
#endif // KIS_DUPLICATEOP_SETTINGS_H_
diff --git a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings_widget.cpp b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings_widget.cpp
index 84b7fb0ce4..cb28b69095 100644
--- a/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings_widget.cpp
+++ b/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings_widget.cpp
@@ -1,76 +1,76 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004-2008 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>
*
* 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_duplicateop_settings_widget.h"
#include "kis_duplicateop_settings.h"
#include "kis_duplicateop_option.h"
#include <kis_image.h>
#include <kis_properties_configuration.h>
#include <kis_paintop_settings_widget.h>
#include <kis_pressure_size_option.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_rotation_option.h>
#include <kis_curve_option_widget.h>
#include <kis_compositeop_option.h>
#include "kis_texture_option.h"
#include <kis_pressure_mirror_option_widget.h>
#include "kis_pressure_texture_strength_option.h"
#include <brushengine/kis_paintop_lod_limitations.h>
-
+#include <KisGlobalResourcesInterface.h>
KisDuplicateOpSettingsWidget::KisDuplicateOpSettingsWidget(QWidget* parent)
: KisBrushBasedPaintopOptionWidget(parent)
{
setObjectName("brush option widget");
setPrecisionEnabled(true);
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation"));
addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror"));
addPaintOpOption(new KisDuplicateOpOption(), i18n("Painting Mode"));
addPaintOpOption(new KisTextureOption(), i18n("Pattern"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength"));
}
KisDuplicateOpSettingsWidget::~KisDuplicateOpSettingsWidget()
{
}
KisPropertiesConfigurationSP KisDuplicateOpSettingsWidget::configuration() const
{
- KisDuplicateOpSettings *config = new KisDuplicateOpSettings();
+ KisDuplicateOpSettings *config = new KisDuplicateOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisDuplicateOpSettingsWidget*>(this));
config->setProperty("paintop", "duplicate"); // XXX: make this a const id string
writeConfiguration(config);
return config;
}
KisPaintopLodLimitations KisDuplicateOpSettingsWidget::lodLimitations() const
{
KisPaintopLodLimitations l = KisBrushBasedPaintopOptionWidget::lodLimitations();
l.blockers << KoID("clone-brush", i18nc("PaintOp instant preview limitation", "Clone Brush (temporarily disabled)"));
return l;
}
diff --git a/plugins/paintops/deform/kis_deform_paintop_settings.cpp b/plugins/paintops/deform/kis_deform_paintop_settings.cpp
index 9850f10e8e..c5ac437a97 100644
--- a/plugins/paintops/deform/kis_deform_paintop_settings.cpp
+++ b/plugins/paintops/deform/kis_deform_paintop_settings.cpp
@@ -1,222 +1,223 @@
/*
* Copyright (c) 2008,2009,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.
*/
#include <kis_deform_paintop_settings.h>
#include <kis_deform_paintop_settings_widget.h>
#include <kis_brush_size_option.h>
#include <kis_airbrush_option_widget.h>
#include <kis_deform_option.h>
struct KisDeformPaintOpSettings::Private
{
QList<KisUniformPaintOpPropertyWSP> uniformProperties;
};
-KisDeformPaintOpSettings::KisDeformPaintOpSettings()
+KisDeformPaintOpSettings::KisDeformPaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
: KisOutlineGenerationPolicy<KisPaintOpSettings>(KisCurrentOutlineFetcher::SIZE_OPTION |
- KisCurrentOutlineFetcher::ROTATION_OPTION),
+ KisCurrentOutlineFetcher::ROTATION_OPTION,
+ resourcesInterface),
m_d(new Private)
{
}
KisDeformPaintOpSettings::~KisDeformPaintOpSettings()
{
}
void KisDeformPaintOpSettings::setPaintOpSize(qreal value)
{
KisBrushSizeOptionProperties option;
option.readOptionSetting(this);
option.brush_diameter = value;
option.writeOptionSetting(this);
}
qreal KisDeformPaintOpSettings::paintOpSize() const
{
KisBrushSizeOptionProperties option;
option.readOptionSetting(this);
return option.brush_diameter;
}
bool KisDeformPaintOpSettings::paintIncremental()
{
return true;
}
bool KisDeformPaintOpSettings::isAirbrushing() const
{
// version 2.3
if (hasProperty(AIRBRUSH_ENABLED)) {
return getBool(AIRBRUSH_ENABLED);
}
else {
return getBool(DEFORM_USE_MOVEMENT_PAINT);
}
}
QPainterPath KisDeformPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom)
{
QPainterPath path;
if (mode.isVisible) {
qreal width = getInt(BRUSH_DIAMETER);
qreal height = getInt(BRUSH_DIAMETER) * getDouble(BRUSH_ASPECT);
path = ellipseOutline(width, height, getDouble(BRUSH_SCALE), getDouble(BRUSH_ROTATION));
path = outlineFetcher()->fetchOutline(info, this, path, mode, alignForZoom);
if (mode.showTiltDecoration) {
QPainterPath tiltLine = makeTiltIndicator(info, QPointF(0.0, 0.0), width * 0.5, 3.0);
path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, 1.0, 0.0, true, 0, 0));
}
}
return path;
}
#include <brushengine/kis_slider_based_paintop_property.h>
#include <brushengine/kis_combo_based_paintop_property.h>
#include "kis_paintop_preset.h"
#include "kis_paintop_settings_update_proxy.h"
#include "kis_standard_uniform_properties_factory.h"
QList<KisUniformPaintOpPropertySP> KisDeformPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings)
{
QList<KisUniformPaintOpPropertySP> props =
listWeakToStrong(m_d->uniformProperties);
if (props.isEmpty()) {
{
KisDoubleSliderBasedPaintOpPropertyCallback *prop =
new KisDoubleSliderBasedPaintOpPropertyCallback(
KisDoubleSliderBasedPaintOpPropertyCallback::Double,
"deform_amount",
i18n("Amount"),
settings, 0);
prop->setRange(0.01, 1.0);
prop->setSingleStep(0.01);
prop->setDecimals(2);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
DeformOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.deform_amount);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
DeformOption option;
option.readOptionSetting(prop->settings().data());
option.deform_amount = prop->value().toReal();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisComboBasedPaintOpPropertyCallback *prop =
new KisComboBasedPaintOpPropertyCallback(
"deform_mode",
i18n("Deform Mode"),
settings, 0);
QList<QString> modes;
modes << i18n("Grow");
modes << i18n("Shrink");
modes << i18n("Swirl CW");
modes << i18n("Swirl CCW");
modes << i18n("Move");
modes << i18n("Lens Zoom In");
modes << i18n("Lens Zoom Out");
modes << i18n("Color Deformation");
prop->setItems(modes);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
DeformOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(int(option.deform_action - 1));
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
DeformOption option;
option.readOptionSetting(prop->settings().data());
option.deform_action = prop->value().toInt() + 1;
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisIntSliderBasedPaintOpPropertyCallback *prop =
new KisIntSliderBasedPaintOpPropertyCallback(
KisIntSliderBasedPaintOpPropertyCallback::Int,
"deform_angle",
i18n("Angle"),
settings, 0);
const QString degree = QChar(Qt::Key_degree);
prop->setRange(0, 360);
prop->setSingleStep(1);
prop->setSuffix(degree);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
KisBrushSizeOptionProperties option;
option.readOptionSetting(prop->settings().data());
prop->setValue(int(option.brush_rotation));
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
KisBrushSizeOptionProperties option;
option.readOptionSetting(prop->settings().data());
option.brush_rotation = prop->value().toInt();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
}
{
using namespace KisStandardUniformPropertiesFactory;
Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) {
if (prop->id() == opacity.id() ||
prop->id() == size.id()) {
props.prepend(prop);
}
}
}
return props;
}
diff --git a/plugins/paintops/deform/kis_deform_paintop_settings.h b/plugins/paintops/deform/kis_deform_paintop_settings.h
index 6ea39db433..7cf29bb222 100644
--- a/plugins/paintops/deform/kis_deform_paintop_settings.h
+++ b/plugins/paintops/deform/kis_deform_paintop_settings.h
@@ -1,48 +1,48 @@
/*
* Copyright (c) 2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2008,2009 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_DEFORM_PAINTOP_SETTINGS_H_
#define KIS_DEFORM_PAINTOP_SETTINGS_H_
#include <QScopedPointer>
#include <brushengine/kis_paintop_settings.h>
#include <kis_types.h>
#include <kis_outline_generation_policy.h>
class KisDeformPaintOpSettings : public KisOutlineGenerationPolicy<KisPaintOpSettings>
{
public:
- KisDeformPaintOpSettings();
+ KisDeformPaintOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisDeformPaintOpSettings() override;
void setPaintOpSize(qreal value) override;
qreal paintOpSize() const override;
QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override;
bool paintIncremental() override;
bool isAirbrushing() const override;
QList<KisUniformPaintOpPropertySP> uniformProperties(KisPaintOpSettingsSP settings) override;
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif
diff --git a/plugins/paintops/deform/kis_deform_paintop_settings_widget.cpp b/plugins/paintops/deform/kis_deform_paintop_settings_widget.cpp
index bf7f8ea73d..680f1a55ff 100644
--- a/plugins/paintops/deform/kis_deform_paintop_settings_widget.cpp
+++ b/plugins/paintops/deform/kis_deform_paintop_settings_widget.cpp
@@ -1,63 +1,64 @@
/*
* Copyright (c) 2008,2009,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.
*/
#include "kis_deform_paintop_settings.h"
#include "kis_deform_paintop_settings_widget.h"
#include "kis_deform_option.h"
#include <kis_paintop_settings_widget.h>
#include <kis_brush_size_option.h>
#include <kis_pressure_rotation_option.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_size_option.h>
#include <kis_pressure_rate_option.h>
#include <kis_curve_option_widget.h>
#include <kis_airbrush_option_widget.h>
#include <kis_compositeop_option.h>
+#include <KisGlobalResourcesInterface.h>
KisDeformPaintOpSettingsWidget::KisDeformPaintOpSettingsWidget(QWidget* parent)
: KisPaintOpSettingsWidget(parent)
{
m_deformOption = new KisDeformOption();
m_brushSizeOption = new KisBrushSizeOption();
m_brushSizeOption->setDiameter(200);
addPaintOpOption(m_brushSizeOption, i18n("Brush size"));
addPaintOpOption(m_deformOption, i18n("Deform Options"));
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation"));
addPaintOpOption(new KisAirbrushOptionWidget(false), i18n("Airbrush"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"), i18n("100%")), i18n("Rate"));
}
KisDeformPaintOpSettingsWidget::~ KisDeformPaintOpSettingsWidget()
{
}
KisPropertiesConfigurationSP KisDeformPaintOpSettingsWidget::configuration() const
{
- KisDeformPaintOpSettings* config = new KisDeformPaintOpSettings();
+ KisDeformPaintOpSettings* config = new KisDeformPaintOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisDeformPaintOpSettingsWidget*>(this));
config->setProperty("paintop", "deformBrush");
writeConfiguration(config);
return config;
}
diff --git a/plugins/paintops/experiment/kis_experiment_paintop_settings.cpp b/plugins/paintops/experiment/kis_experiment_paintop_settings.cpp
index 350294fc15..774944dc84 100644
--- a/plugins/paintops/experiment/kis_experiment_paintop_settings.cpp
+++ b/plugins/paintops/experiment/kis_experiment_paintop_settings.cpp
@@ -1,266 +1,267 @@
/*
* Copyright (c) 2009,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.
*/
#include "kis_experiment_paintop_settings.h"
#include "kis_current_outline_fetcher.h"
#include "kis_algebra_2d.h"
struct KisExperimentPaintOpSettings::Private
{
QList<KisUniformPaintOpPropertyWSP> uniformProperties;
};
-KisExperimentPaintOpSettings::KisExperimentPaintOpSettings()
- : m_d(new Private)
+KisExperimentPaintOpSettings::KisExperimentPaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
+ : KisNoSizePaintOpSettings(resourcesInterface),
+ m_d(new Private)
{
}
KisExperimentPaintOpSettings::~KisExperimentPaintOpSettings()
{
}
bool KisExperimentPaintOpSettings::lodSizeThresholdSupported() const
{
return false;
}
bool KisExperimentPaintOpSettings::paintIncremental()
{
/**
* The experiment brush supports working in the
* WASH mode only!
*/
return false;
}
QPainterPath KisExperimentPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom)
{
QPainterPath path;
if (mode.isVisible) {
QRectF ellipse(0, 0, 3, 3);
ellipse.translate(-ellipse.center());
path.addEllipse(ellipse);
ellipse.setRect(0,0, 12, 12);
ellipse.translate(-ellipse.center());
path.addEllipse(ellipse);
if (mode.showTiltDecoration) {
path.addPath(makeTiltIndicator(info, QPointF(0.0, 0.0), 0.0, 3.0));
}
path.translate(KisAlgebra2D::alignForZoom(info.pos(), alignForZoom));
}
return path;
}
#include <brushengine/kis_slider_based_paintop_property.h>
#include "kis_paintop_preset.h"
#include "kis_paintop_settings_update_proxy.h"
#include "kis_experimentop_option.h"
#include "kis_standard_uniform_properties_factory.h"
QList<KisUniformPaintOpPropertySP> KisExperimentPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings)
{
QList<KisUniformPaintOpPropertySP> props =
listWeakToStrong(m_d->uniformProperties);
if (props.isEmpty()) {
{
KisIntSliderBasedPaintOpPropertyCallback *prop =
new KisIntSliderBasedPaintOpPropertyCallback(
KisIntSliderBasedPaintOpPropertyCallback::Int,
"shape_speed",
i18n("Speed"),
settings, 0);
prop->setRange(0, 100);
prop->setSingleStep(1);
prop->setSuffix(i18n("%"));
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
ExperimentOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(int(option.speed));
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
ExperimentOption option;
option.readOptionSetting(prop->settings().data());
option.speed = prop->value().toInt();
option.writeOptionSetting(prop->settings().data());
});
prop->setIsVisibleCallback(
[](const KisUniformPaintOpProperty *prop) -> bool {
ExperimentOption option;
option.readOptionSetting(prop->settings().data());
return option.isSpeedEnabled;
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisIntSliderBasedPaintOpPropertyCallback *prop =
new KisIntSliderBasedPaintOpPropertyCallback(
KisIntSliderBasedPaintOpPropertyCallback::Int,
"shape_smooth",
i18n("Smooth"),
settings, 0);
prop->setRange(0, 100);
prop->setSingleStep(1);
prop->setSuffix(i18n(" px"));
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
ExperimentOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(int(option.smoothing));
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
ExperimentOption option;
option.readOptionSetting(prop->settings().data());
option.smoothing = prop->value().toInt();
option.writeOptionSetting(prop->settings().data());
});
prop->setIsVisibleCallback(
[](const KisUniformPaintOpProperty *prop) {
ExperimentOption option;
option.readOptionSetting(prop->settings().data());
return option.isSmoothingEnabled;
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisIntSliderBasedPaintOpPropertyCallback *prop =
new KisIntSliderBasedPaintOpPropertyCallback(
KisIntSliderBasedPaintOpPropertyCallback::Int,
"shape_displace",
i18n("Displace"),
settings, 0);
prop->setRange(0, 100);
prop->setSingleStep(1);
prop->setSuffix(i18n("%"));
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
ExperimentOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(int(option.displacement));
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
ExperimentOption option;
option.readOptionSetting(prop->settings().data());
option.displacement = prop->value().toInt();
option.writeOptionSetting(prop->settings().data());
});
prop->setIsVisibleCallback(
[](const KisUniformPaintOpProperty *prop) {
ExperimentOption option;
option.readOptionSetting(prop->settings().data());
return option.isDisplacementEnabled;
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisUniformPaintOpPropertyCallback *prop =
new KisUniformPaintOpPropertyCallback(
KisUniformPaintOpPropertyCallback::Bool,
"shape_windingfill",
i18n("Winding Fill"),
settings, 0);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
ExperimentOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.windingFill);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
ExperimentOption option;
option.readOptionSetting(prop->settings().data());
option.windingFill = prop->value().toBool();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisUniformPaintOpPropertyCallback *prop =
new KisUniformPaintOpPropertyCallback(
KisUniformPaintOpPropertyCallback::Bool,
"shape_hardedge",
i18n("Hard Edge"),
settings, 0);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
ExperimentOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.hardEdge);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
ExperimentOption option;
option.readOptionSetting(prop->settings().data());
option.hardEdge = prop->value().toBool();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
}
{
using namespace KisStandardUniformPropertiesFactory;
Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) {
if (prop->id() == opacity.id()) {
props.prepend(prop);
}
}
}
return props;
}
diff --git a/plugins/paintops/experiment/kis_experiment_paintop_settings.h b/plugins/paintops/experiment/kis_experiment_paintop_settings.h
index 3020c771b4..5e46d338fa 100644
--- a/plugins/paintops/experiment/kis_experiment_paintop_settings.h
+++ b/plugins/paintops/experiment/kis_experiment_paintop_settings.h
@@ -1,43 +1,43 @@
/*
* Copyright (c) 2009,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_EXPERIMENT_PAINTOP_SETTINGS_H_
#define KIS_EXPERIMENT_PAINTOP_SETTINGS_H_
#include <brushengine/kis_no_size_paintop_settings.h>
#include <QScopedPointer>
class KisExperimentPaintOpSettings : public KisNoSizePaintOpSettings
{
public:
- KisExperimentPaintOpSettings();
+ KisExperimentPaintOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisExperimentPaintOpSettings() override;
bool lodSizeThresholdSupported() const override;
bool paintIncremental() override;
QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override;
QList<KisUniformPaintOpPropertySP> uniformProperties(KisPaintOpSettingsSP settings) override;
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif
diff --git a/plugins/paintops/experiment/kis_experiment_paintop_settings_widget.cpp b/plugins/paintops/experiment/kis_experiment_paintop_settings_widget.cpp
index c7d123c0b4..b797a845fb 100644
--- a/plugins/paintops/experiment/kis_experiment_paintop_settings_widget.cpp
+++ b/plugins/paintops/experiment/kis_experiment_paintop_settings_widget.cpp
@@ -1,52 +1,53 @@
/*
* Copyright (c) 2009,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.
*/
#include "kis_experiment_paintop_settings_widget.h"
#include "kis_experimentop_option.h"
#include "kis_experiment_paintop_settings.h"
#include <kis_color_option.h>
#include <kis_paintop_settings_widget.h>
#include <kis_paint_action_type_option.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_size_option.h>
#include <kis_curve_option_widget.h>
#include <kis_compositeop_option.h>
+#include <KisGlobalResourcesInterface.h>
KisExperimentPaintOpSettingsWidget:: KisExperimentPaintOpSettingsWidget(QWidget* parent)
: KisPaintOpSettingsWidget(parent)
{
addPaintOpOption(new KisExperimentOpOption(), i18n("Experiment option"));
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
//addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity"));
}
KisExperimentPaintOpSettingsWidget::~ KisExperimentPaintOpSettingsWidget()
{
}
KisPropertiesConfigurationSP KisExperimentPaintOpSettingsWidget::configuration() const
{
- KisExperimentPaintOpSettings* config = new KisExperimentPaintOpSettings();
+ KisExperimentPaintOpSettings* config = new KisExperimentPaintOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisExperimentPaintOpSettingsWidget*>(this));
config->setProperty("paintop", "experimentbrush"); // XXX: make this a const id string
writeConfiguration(config);
return config;
}
diff --git a/plugins/paintops/filterop/kis_filterop.cpp b/plugins/paintops/filterop/kis_filterop.cpp
index 36acc4ae76..50345db171 100644
--- a/plugins/paintops/filterop/kis_filterop.cpp
+++ b/plugins/paintops/filterop/kis_filterop.cpp
@@ -1,150 +1,170 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004-2008 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>
*
* 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_filterop.h"
#include <kis_debug.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorTransformation.h>
#include <KoColor.h>
#include <KoCompositeOpRegistry.h>
#include <kis_processing_information.h>
#include <filter/kis_filter_registry.h>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <kis_brush.h>
#include <kis_global.h>
#include <kis_painter.h>
#include <kis_paint_device.h>
#include <kis_properties_configuration.h>
#include <kis_selection.h>
#include <kis_pressure_size_option.h>
#include <kis_filter_option.h>
#include <kis_filterop_settings.h>
#include <kis_iterator_ng.h>
#include <kis_fixed_paint_device.h>
#include <kis_transaction.h>
#include <kis_lod_transform.h>
#include <kis_spacing_information.h>
KisFilterOp::KisFilterOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image)
: KisBrushBasedPaintOp(settings, painter)
, m_filterConfiguration(0)
{
Q_UNUSED(node);
Q_UNUSED(image);
Q_ASSERT(settings);
Q_ASSERT(painter);
m_tmpDevice = source()->createCompositionSourceDevice();
m_sizeOption.readOptionSetting(settings);
m_rotationOption.readOptionSetting(settings);
m_sizeOption.resetAllSensors();
m_rotationOption.resetAllSensors();
m_filter = KisFilterRegistry::instance()->get(settings->getString(FILTER_ID));
m_filterConfiguration = static_cast<const KisFilterOpSettings *>(settings.data())->filterConfig();
m_smudgeMode = settings->getBool(FILTER_SMUDGE_MODE);
m_rotationOption.applyFanCornersInfo(this);
}
KisFilterOp::~KisFilterOp()
{
}
KisSpacingInformation KisFilterOp::paintAt(const KisPaintInformation& info)
{
if (!painter()) {
return KisSpacingInformation(1.0);
}
if (!m_filter) {
return KisSpacingInformation(1.0);
}
if (!source()) {
return KisSpacingInformation(1.0);
}
KisBrushSP brush = m_brush;
if (!brush) return KisSpacingInformation(1.0);
if (! brush->canPaintFor(info))
return KisSpacingInformation(1.0);
qreal scale = m_sizeOption.apply(info);
scale *= KisLodTransform::lodToScale(painter()->device());
if (checkSizeTooSmall(scale)) return KisSpacingInformation();
qreal rotation = m_rotationOption.apply(info);
KisDabShape shape(scale, 1.0, rotation);
static const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8();
static KoColor color(Qt::black, cs);
QRect dstRect;
KisFixedPaintDeviceSP dab = m_dabCache->fetchDab(cs, color, info.pos(),
shape,
info, 1.0,
&dstRect);
if (dstRect.isEmpty()) return KisSpacingInformation(1.0);
QRect dabRect = dab->bounds();
// sanity check
Q_ASSERT(dstRect.size() == dabRect.size());
// Filter the paint device
QRect neededRect = m_filter->neededRect(dstRect, m_filterConfiguration, painter()->device()->defaultBounds()->currentLevelOfDetail());
KisPainter p(m_tmpDevice);
if (!m_smudgeMode) {
p.setCompositeOp(COMPOSITE_COPY);
}
p.bitBltOldData(neededRect.topLeft() - dstRect.topLeft(), source(), neededRect);
KisTransaction transaction(m_tmpDevice);
m_filter->process(m_tmpDevice, dabRect, m_filterConfiguration, 0);
transaction.end();
painter()->bitBltWithFixedSelection(dstRect.x(), dstRect.y(),
m_tmpDevice, dab,
0, 0,
dabRect.x(), dabRect.y(),
dabRect.width(), dabRect.height());
painter()->renderMirrorMaskSafe(dstRect, m_tmpDevice, 0, 0, dab,
!m_dabCache->needSeparateOriginal());
return effectiveSpacing(scale, rotation, info);
}
KisSpacingInformation KisFilterOp::updateSpacingImpl(const KisPaintInformation &info) const
{
const qreal scale = m_sizeOption.apply(info) * KisLodTransform::lodToScale(painter()->device());
const qreal rotation = m_rotationOption.apply(info);
return effectiveSpacing(scale, rotation, info);
}
+
+QList<KoResourceSP> KisFilterOp::prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface)
+{
+ QList<KoResourceSP> resources = KisBrushBasedPaintOp::prepareLinkedResources(settings, resourcesInterface);
+
+ KisFilterConfigurationSP config = static_cast<const KisFilterOpSettings *>(settings.data())->filterConfig();
+ resources << config->linkedResources(resourcesInterface);
+
+ return resources;
+}
+
+QList<KoResourceSP> KisFilterOp::prepareEmbeddedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface)
+{
+ QList<KoResourceSP> resources = KisBrushBasedPaintOp::prepareEmbeddedResources(settings, resourcesInterface);
+
+ KisFilterConfigurationSP config = static_cast<const KisFilterOpSettings *>(settings.data())->filterConfig();
+ resources << config->embeddedResources(resourcesInterface);
+
+ return resources;
+}
diff --git a/plugins/paintops/filterop/kis_filterop.h b/plugins/paintops/filterop/kis_filterop.h
index 13300a7c55..0ab8d42c49 100644
--- a/plugins/paintops/filterop/kis_filterop.h
+++ b/plugins/paintops/filterop/kis_filterop.h
@@ -1,59 +1,62 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004-2008 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>
*
* 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_FILTEROP_H_
#define KIS_FILTEROP_H_
#include "kis_brush_based_paintop.h"
#include <kis_pressure_size_option.h>
#include <kis_pressure_rotation_option.h>
class KisFilterConfiguration;
class KisFilterOpSettings;
class KisPaintInformation;
class KisPainter;
class KisFilterOp : public KisBrushBasedPaintOp
{
public:
KisFilterOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image);
~KisFilterOp() override;
+ static QList<KoResourceSP> prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface);
+ static QList<KoResourceSP> prepareEmbeddedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface);
+
protected:
KisSpacingInformation paintAt(const KisPaintInformation& info) override;
KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override;
private:
KisPaintDeviceSP m_tmpDevice;
KisPressureSizeOption m_sizeOption;
KisPressureRotationOption m_rotationOption;
KisFilterSP m_filter;
KisFilterConfigurationSP m_filterConfiguration;
bool m_smudgeMode;
};
#endif // KIS_FILTEROP_H_
diff --git a/plugins/paintops/filterop/kis_filterop_settings.cpp b/plugins/paintops/filterop/kis_filterop_settings.cpp
index 13aeef995e..16251fb75d 100644
--- a/plugins/paintops/filterop/kis_filterop_settings.cpp
+++ b/plugins/paintops/filterop/kis_filterop_settings.cpp
@@ -1,88 +1,89 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004-2008 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>
*
* 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_filterop_settings.h"
#include <QDomDocument>
#include <kis_filter_option.h>
#include <filter/kis_filter.h>
#include <filter/kis_filter_registry.h>
#include <filter/kis_filter_configuration.h>
#include <kis_node.h>
#include <kis_image.h>
#include <kis_types.h>
#include <kis_paint_device.h>
-KisFilterOpSettings::KisFilterOpSettings()
+KisFilterOpSettings::KisFilterOpSettings(KisResourcesInterfaceSP resourcesInterface)
+ : KisBrushBasedPaintOpSettings(resourcesInterface)
{
setPropertyNotSaved(FILTER_CONFIGURATION);
}
KisFilterOpSettings::~KisFilterOpSettings()
{
}
bool KisFilterOpSettings::paintIncremental()
{
return true; // We always paint on the existing data
}
KisFilterConfigurationSP KisFilterOpSettings::filterConfig() const
{
if (hasProperty(FILTER_ID)) {
KisFilterSP filter = KisFilterRegistry::instance()->get(getString(FILTER_ID));
if (filter) {
- KisFilterConfigurationSP configuration = filter->factoryConfiguration();
+ KisFilterConfigurationSP configuration = filter->factoryConfiguration(resourcesInterface());
configuration->fromXML(getString(FILTER_CONFIGURATION));
return configuration;
}
}
return 0;
}
void KisFilterOpSettings::toXML(QDomDocument& doc, QDomElement& root) const
{
KisPaintOpSettings::toXML(doc, root);
KisFilterConfigurationSP configuration = filterConfig();
if (configuration) {
QDomElement e = doc.createElement("filterconfig");
configuration->toXML(doc, e);
root.appendChild(e);
}
}
void KisFilterOpSettings::fromXML(const QDomElement& e)
{
KisPaintOpSettings::fromXML(e);
QDomElement element = e.firstChildElement("filterconfig");
if (hasProperty(FILTER_ID)) {
KisFilterSP filter = KisFilterRegistry::instance()->get(getString(FILTER_ID));
if (filter) {
- KisFilterConfigurationSP configuration = filter->factoryConfiguration();
+ KisFilterConfigurationSP configuration = filter->factoryConfiguration(resourcesInterface());
configuration->fromXML(element);
setProperty(FILTER_CONFIGURATION, configuration->toXML());
}
}
}
diff --git a/plugins/paintops/filterop/kis_filterop_settings.h b/plugins/paintops/filterop/kis_filterop_settings.h
index a50b005773..b0ebc88374 100644
--- a/plugins/paintops/filterop/kis_filterop_settings.h
+++ b/plugins/paintops/filterop/kis_filterop_settings.h
@@ -1,54 +1,54 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004-2008 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>
*
* 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_FILTEROP_SETTINGS_H_
#define KIS_FILTEROP_SETTINGS_H_
#include <kis_brush_based_paintop_settings.h>
#include <kis_types.h>
#include "kis_filterop_settings_widget.h"
class QDomElement;
class KisFilterConfiguration;
class KisFilterOpSettings : public KisBrushBasedPaintOpSettings
{
public:
- KisFilterOpSettings();
+ KisFilterOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisFilterOpSettings() override;
bool paintIncremental() override;
KisFilterConfigurationSP filterConfig() const;
using KisPaintOpSettings::toXML;
void toXML(QDomDocument& doc, QDomElement& root) const override;
using KisPaintOpSettings::fromXML;
void fromXML(const QDomElement& e) override;
};
#endif // KIS_FILTEROP_SETTINGS_H_
diff --git a/plugins/paintops/filterop/kis_filterop_settings_widget.cpp b/plugins/paintops/filterop/kis_filterop_settings_widget.cpp
index 85f41da250..bf28d92ff3 100644
--- a/plugins/paintops/filterop/kis_filterop_settings_widget.cpp
+++ b/plugins/paintops/filterop/kis_filterop_settings_widget.cpp
@@ -1,70 +1,71 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004-2008 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>
*
* 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_filterop_settings_widget.h"
#include "kis_filterop_settings.h"
#include <kis_properties_configuration.h>
#include <filter/kis_filter.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_paintop_settings_widget.h>
#include <kis_pressure_size_option.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_rotation_option.h>
#include <kis_curve_option_widget.h>
#include <kis_compositeop_option.h>
#include <kis_filter_option.h>
#include "kis_texture_option.h"
#include <kis_pressure_mirror_option_widget.h>
#include "kis_pressure_texture_strength_option.h"
+#include <KisGlobalResourcesInterface.h>
KisFilterOpSettingsWidget::KisFilterOpSettingsWidget(QWidget* parent)
: KisBrushBasedPaintopOptionWidget(parent)
{
setObjectName("filter option widget");
setPrecisionEnabled(true);
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation"));
addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror"));
m_filterOption = new KisFilterOption();
addPaintOpOption(m_filterOption, i18nc("option name", "Filter"));
}
KisFilterOpSettingsWidget::~KisFilterOpSettingsWidget()
{
}
KisPropertiesConfigurationSP KisFilterOpSettingsWidget::configuration() const
{
- KisFilterOpSettings *config = new KisFilterOpSettings();
+ KisFilterOpSettings *config = new KisFilterOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisFilterOpSettingsWidget*>(this));
config->setProperty("paintop", "filter"); // XXX: make this a const id string
writeConfiguration(config);
return config;
}
diff --git a/plugins/paintops/gridbrush/kis_grid_paintop_settings.cpp b/plugins/paintops/gridbrush/kis_grid_paintop_settings.cpp
index b02ed5410b..d651531d69 100644
--- a/plugins/paintops/gridbrush/kis_grid_paintop_settings.cpp
+++ b/plugins/paintops/gridbrush/kis_grid_paintop_settings.cpp
@@ -1,129 +1,130 @@
/*
* Copyright (c) 2009,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.
*/
#include <kis_paint_action_type_option.h>
#include "kis_grid_paintop_settings.h"
#include "kis_grid_paintop_settings_widget.h"
#include "kis_gridop_option.h"
#include "kis_grid_shape_option.h"
#include <kis_color_option.h>
struct KisGridPaintOpSettings::Private
{
QList<KisUniformPaintOpPropertyWSP> uniformProperties;
};
-KisGridPaintOpSettings::KisGridPaintOpSettings()
- : KisOutlineGenerationPolicy<KisPaintOpSettings>(KisCurrentOutlineFetcher::NO_OPTION),
+KisGridPaintOpSettings::KisGridPaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
+ : KisOutlineGenerationPolicy<KisPaintOpSettings>(KisCurrentOutlineFetcher::NO_OPTION,
+ resourcesInterface),
m_d(new Private)
{
}
void KisGridPaintOpSettings::setPaintOpSize(qreal value)
{
KisGridOpProperties option;
option.readOptionSetting(this);
option.grid_width = value;
option.grid_height = value;
option.writeOptionSetting(this);
}
qreal KisGridPaintOpSettings::paintOpSize() const
{
KisGridOpProperties option;
option.readOptionSetting(this);
return option.grid_width;
}
KisGridPaintOpSettings::~KisGridPaintOpSettings()
{
}
bool KisGridPaintOpSettings::paintIncremental()
{
return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP;
}
QPainterPath KisGridPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom)
{
QPainterPath path;
if (mode.isVisible) {
qreal sizex = getInt(GRID_WIDTH) * getDouble(GRID_SCALE);
qreal sizey = getInt(GRID_HEIGHT) * getDouble(GRID_SCALE);
QRectF rc(0, 0, sizex, sizey);
rc.translate(-rc.center());
path.addRect(rc);
path = outlineFetcher()->fetchOutline(info, this, path, mode, alignForZoom);
if (mode.showTiltDecoration) {
QPainterPath tiltLine = makeTiltIndicator(info, QPointF(0.0, 0.0), sizex * 0.5, 3.0);
path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, 1.0, 0.0, true, 0, 0));
}
}
return path;
}
#include <brushengine/kis_slider_based_paintop_property.h>
#include "kis_paintop_preset.h"
#include "kis_paintop_settings_update_proxy.h"
QList<KisUniformPaintOpPropertySP> KisGridPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings)
{
QList<KisUniformPaintOpPropertySP> props =
listWeakToStrong(m_d->uniformProperties);
if (props.isEmpty()) {
{
KisIntSliderBasedPaintOpPropertyCallback *prop =
new KisIntSliderBasedPaintOpPropertyCallback(
KisIntSliderBasedPaintOpPropertyCallback::Int,
"grid_divisionlevel",
i18n("Division Level"),
settings, 0);
prop->setRange(1, 25);
prop->setSingleStep(1);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
KisGridOpProperties option;
option.readOptionSetting(prop->settings().data());
prop->setValue(int(option.grid_division_level));
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
KisGridOpProperties option;
option.readOptionSetting(prop->settings().data());
option.grid_division_level = prop->value().toInt();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
}
return KisPaintOpSettings::uniformProperties(settings) + props;
}
diff --git a/plugins/paintops/gridbrush/kis_grid_paintop_settings.h b/plugins/paintops/gridbrush/kis_grid_paintop_settings.h
index 131a735b2e..9c42201b29 100644
--- a/plugins/paintops/gridbrush/kis_grid_paintop_settings.h
+++ b/plugins/paintops/gridbrush/kis_grid_paintop_settings.h
@@ -1,55 +1,55 @@
/*
* Copyright (c) 2009,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_GRID_PAINTOP_SETTINGS_H_
#define KIS_GRID_PAINTOP_SETTINGS_H_
#include <QScopedPointer>
#include <brushengine/kis_paintop_settings.h>
#include <kis_types.h>
#include <kis_outline_generation_policy.h>
#include "kis_grid_paintop_settings_widget.h"
class KisGridPaintOpSettings : public KisOutlineGenerationPolicy<KisPaintOpSettings>
{
public:
- KisGridPaintOpSettings();
+ KisGridPaintOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisGridPaintOpSettings() override;
void setPaintOpSize(qreal value) override;
qreal paintOpSize() const override;
QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override;
bool paintIncremental() override;
QList<KisUniformPaintOpPropertySP> uniformProperties(KisPaintOpSettingsSP settings) override;
private:
struct Private;
const QScopedPointer<Private> m_d;
};
typedef KisSharedPtr<KisGridPaintOpSettings> KisGridPaintOpSettingsSP;
#endif
diff --git a/plugins/paintops/gridbrush/kis_grid_paintop_settings_widget.cpp b/plugins/paintops/gridbrush/kis_grid_paintop_settings_widget.cpp
index 4b756a1927..e7f0ed3fa4 100644
--- a/plugins/paintops/gridbrush/kis_grid_paintop_settings_widget.cpp
+++ b/plugins/paintops/gridbrush/kis_grid_paintop_settings_widget.cpp
@@ -1,56 +1,57 @@
/*
* Copyright (c) 2009 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.
*/
#include "kis_grid_paintop_settings_widget.h"
#include "kis_gridop_option.h"
#include "kis_grid_paintop_settings.h"
#include "kis_grid_shape_option.h"
#include <kis_color_option.h>
#include <kis_paintop_settings_widget.h>
#include <kis_paint_action_type_option.h>
#include <kis_compositeop_option.h>
#include <klocalizedstring.h>
+#include <KisGlobalResourcesInterface.h>
KisGridPaintOpSettingsWidget:: KisGridPaintOpSettingsWidget(QWidget* parent)
: KisPaintOpSettingsWidget(parent)
{
m_gridOption = new KisGridOpOption();
m_gridShapeOption = new KisGridShapeOption();
m_ColorOption = new KisColorOption();
addPaintOpOption(m_gridOption, i18n("Brush size"));
addPaintOpOption(m_gridShapeOption, i18n("Particle type"));
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(m_ColorOption, i18n("Color options"));
addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode"));
}
KisGridPaintOpSettingsWidget::~ KisGridPaintOpSettingsWidget()
{
}
KisPropertiesConfigurationSP KisGridPaintOpSettingsWidget::configuration() const
{
- KisGridPaintOpSettings* config = new KisGridPaintOpSettings();
+ KisGridPaintOpSettings* config = new KisGridPaintOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisGridPaintOpSettingsWidget*>(this));
config->setProperty("paintop", "gridbrush"); // XXX: make this a const id string
writeConfiguration(config);
return config;
}
diff --git a/plugins/paintops/hairy/kis_hairy_ink_option.cpp b/plugins/paintops/hairy/kis_hairy_ink_option.cpp
index 427351d275..8909cf2119 100644
--- a/plugins/paintops/hairy/kis_hairy_ink_option.cpp
+++ b/plugins/paintops/hairy/kis_hairy_ink_option.cpp
@@ -1,158 +1,158 @@
/*
* Copyright (c) 2008-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.
*/
#include "kis_hairy_ink_option.h"
#include "kis_hairy_paintop_settings.h"
#include <klocalizedstring.h>
#include "ui_wdgInkOptions.h"
class KisInkOptionsWidget: public QWidget, public Ui::WdgInkOptions
{
public:
KisInkOptionsWidget(QWidget *parent = 0)
: QWidget(parent) {
setupUi(this);
}
};
KisHairyInkOption::KisHairyInkOption()
: KisPaintOpOption(KisPaintOpOption::COLOR, false)
{
setObjectName("KisHairyInkOption");
m_checkable = true;
m_options = new KisInkOptionsWidget();
// init values for slider
m_options->pressureSlider->setRange(0.0, 100, 0);
m_options->pressureSlider->setValue(50);
m_options->pressureSlider->setSuffix(i18n("%"));
m_options->bristleLengthSlider->setRange(0, 100, 0);
m_options->bristleLengthSlider->setValue(50);
m_options->bristleLengthSlider->setSuffix(i18n("%"));
m_options->bristleInkAmountSlider->setRange(0, 100, 0);
m_options->bristleInkAmountSlider->setValue(50);
m_options->bristleInkAmountSlider->setSuffix(i18n("%"));
m_options->inkDepletionSlider->setRange(0, 100, 0);
m_options->inkDepletionSlider->setValue(50);
m_options->inkDepletionSlider->setSuffix(i18n("%"));
connect(m_options->inkAmountSpinBox, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged()));
connect(m_options->saturationCBox, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_options->opacityCBox, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_options->useWeightCHBox, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_options->pressureSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
connect(m_options->bristleLengthSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
connect(m_options->bristleInkAmountSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
connect(m_options->inkDepletionSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
connect(m_options->inkCurve, SIGNAL(modified()), SLOT(emitSettingChanged()));
connect(m_options->soakInkCBox, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
setConfigurationPage(m_options);
}
KisHairyInkOption::~KisHairyInkOption()
{
delete m_options;
}
void KisHairyInkOption::readOptionSetting(const KisPropertiesConfigurationSP settings)
{
setChecked(settings->getBool(HAIRY_INK_DEPLETION_ENABLED));
m_options->inkAmountSpinBox->setValue(settings->getInt(HAIRY_INK_AMOUNT));
m_options->saturationCBox->setChecked(settings->getBool(HAIRY_INK_USE_SATURATION));
m_options->opacityCBox->setChecked(settings->getBool(HAIRY_INK_USE_OPACITY));
m_options->useWeightCHBox->setChecked(settings->getBool(HAIRY_INK_USE_WEIGHTS));
m_options->pressureSlider->setValue(settings->getInt(HAIRY_INK_PRESSURE_WEIGHT));
m_options->bristleLengthSlider->setValue(settings->getInt(HAIRY_INK_BRISTLE_LENGTH_WEIGHT));
m_options->bristleInkAmountSlider->setValue(settings->getInt(HAIRY_INK_BRISTLE_INK_AMOUNT_WEIGHT));
m_options->inkDepletionSlider->setValue(settings->getInt(HAIRY_INK_DEPLETION_WEIGHT));
m_options->inkCurve->setCurve(settings->getCubicCurve(HAIRY_INK_DEPLETION_CURVE));
m_options->soakInkCBox->setChecked(settings->getBool(HAIRY_INK_SOAK));
}
void KisHairyInkOption::writeOptionSetting(KisPropertiesConfigurationSP settings) const
{
settings->setProperty(HAIRY_INK_DEPLETION_ENABLED, isChecked());
settings->setProperty(HAIRY_INK_AMOUNT, inkAmount());
settings->setProperty(HAIRY_INK_USE_SATURATION, useSaturation());
settings->setProperty(HAIRY_INK_USE_OPACITY, useOpacity());
settings->setProperty(HAIRY_INK_USE_WEIGHTS, useWeights());
settings->setProperty(HAIRY_INK_PRESSURE_WEIGHT, pressureWeight());
settings->setProperty(HAIRY_INK_BRISTLE_LENGTH_WEIGHT, bristleLengthWeight());
settings->setProperty(HAIRY_INK_BRISTLE_INK_AMOUNT_WEIGHT, bristleInkAmountWeight());
settings->setProperty(HAIRY_INK_DEPLETION_WEIGHT, inkDepletionWeight());
- settings->setProperty(HAIRY_INK_DEPLETION_CURVE, qVariantFromValue(m_options->inkCurve->curve()));
+ settings->setProperty(HAIRY_INK_DEPLETION_CURVE, QVariant::fromValue(m_options->inkCurve->curve()));
settings->setProperty(HAIRY_INK_SOAK, m_options->soakInkCBox->isChecked());
}
int KisHairyInkOption::inkAmount() const
{
return m_options->inkAmountSpinBox->value();
}
bool KisHairyInkOption::useOpacity() const
{
return m_options->opacityCBox->isChecked();
}
bool KisHairyInkOption::useSaturation() const
{
return m_options->saturationCBox->isChecked();
}
bool KisHairyInkOption::useWeights() const
{
return m_options->useWeightCHBox->isChecked();
}
int KisHairyInkOption::pressureWeight() const
{
return m_options->pressureSlider->value();
}
int KisHairyInkOption::bristleLengthWeight() const
{
return m_options->bristleLengthSlider->value();
}
int KisHairyInkOption::bristleInkAmountWeight() const
{
return m_options->bristleInkAmountSlider->value();
}
int KisHairyInkOption::inkDepletionWeight() const
{
return m_options->inkDepletionSlider->value();
}
diff --git a/plugins/paintops/hairy/kis_hairy_paintop.cpp b/plugins/paintops/hairy/kis_hairy_paintop.cpp
index bd53194983..ea88d4f648 100644
--- a/plugins/paintops/hairy/kis_hairy_paintop.cpp
+++ b/plugins/paintops/hairy/kis_hairy_paintop.cpp
@@ -1,170 +1,175 @@
/*
* Copyright (c) 2008-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.
*/
#include "kis_hairy_paintop.h"
#include "kis_hairy_paintop_settings.h"
#include <cmath>
#include <QRect>
#include <kis_image.h>
#include <kis_debug.h>
#include "kis_paint_device.h"
#include "kis_painter.h"
#include <kis_vec.h>
#include <kis_hairy_ink_option.h>
#include <kis_hairy_bristle_option.h>
#include <kis_brush_option.h>
#include <kis_brush_based_paintop_settings.h>
#include <kis_fixed_paint_device.h>
#include <kis_lod_transform.h>
#include <kis_spacing_information.h>
#include "kis_brush.h"
KisHairyPaintOp::KisHairyPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image)
: KisPaintOp(painter)
{
- Q_UNUSED(image)
+ Q_UNUSED(image);
Q_ASSERT(settings);
m_dev = node ? node->paintDevice() : 0;
KisBrushOptionProperties brushOption;
- brushOption.readOptionSetting(settings);
+ brushOption.readOptionSetting(settings, settings->resourcesInterface());
KisBrushSP brush = brushOption.brush();
KisFixedPaintDeviceSP dab = cachedDab(painter->device()->compositionSourceColorSpace());
// properly initialize fake paint information to avoid warnings
KisPaintInformation fakePaintInformation;
fakePaintInformation.setRandomSource(new KisRandomSource());
fakePaintInformation.setPerStrokeRandomSource(new KisPerStrokeRandomSource());
if (brush->brushType() == IMAGE || brush->brushType() == PIPE_IMAGE) {
dab = brush->paintDevice(source()->colorSpace(), KisDabShape(), fakePaintInformation);
} else {
brush->mask(dab, painter->paintColor(), KisDabShape(), fakePaintInformation);
}
m_brush.fromDabWithDensity(dab, settings->getDouble(HAIRY_BRISTLE_DENSITY) * 0.01);
m_brush.setInkColor(painter->paintColor());
loadSettings(static_cast<const KisBrushBasedPaintOpSettings*>(settings.data()));
m_brush.setProperties(&m_properties);
m_rotationOption.readOptionSetting(settings);
m_opacityOption.readOptionSetting(settings);
m_sizeOption.readOptionSetting(settings);
m_rotationOption.resetAllSensors();
m_opacityOption.resetAllSensors();
m_sizeOption.resetAllSensors();
}
+QList<KoResourceSP> KisHairyPaintOp::prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface)
+{
+ KisBrushOptionProperties brushOption;
+ return brushOption.prepareLinkedResources(settings, resourcesInterface);
+}
void KisHairyPaintOp::loadSettings(const KisBrushBasedPaintOpSettings *settings)
{
m_properties.inkAmount = settings->getInt(HAIRY_INK_AMOUNT);
//TODO: wait for the transfer function with variable size
m_properties.inkDepletionCurve = settings->getCubicCurve(HAIRY_INK_DEPLETION_CURVE).floatTransfer(m_properties.inkAmount);
m_properties.inkDepletionEnabled = settings->getBool(HAIRY_INK_DEPLETION_ENABLED);
m_properties.useSaturation = settings->getBool(HAIRY_INK_USE_SATURATION);
m_properties.useOpacity = settings->getBool(HAIRY_INK_USE_OPACITY);
m_properties.useWeights = settings->getBool(HAIRY_INK_USE_WEIGHTS);
m_properties.pressureWeight = settings->getDouble(HAIRY_INK_PRESSURE_WEIGHT) / 100.0;
m_properties.bristleLengthWeight = settings->getDouble(HAIRY_INK_BRISTLE_LENGTH_WEIGHT) / 100.0;
m_properties.bristleInkAmountWeight = settings->getDouble(HAIRY_INK_BRISTLE_INK_AMOUNT_WEIGHT) / 100.0;
m_properties.inkDepletionWeight = settings->getDouble(HAIRY_INK_DEPLETION_WEIGHT);
m_properties.useSoakInk = settings->getBool(HAIRY_INK_SOAK);
m_properties.useMousePressure = settings->getBool(HAIRY_BRISTLE_USE_MOUSEPRESSURE);
m_properties.shearFactor = settings->getDouble(HAIRY_BRISTLE_SHEAR);
m_properties.randomFactor = settings->getDouble(HAIRY_BRISTLE_RANDOM);
m_properties.scaleFactor = settings->getDouble(HAIRY_BRISTLE_SCALE);
m_properties.threshold = settings->getBool(HAIRY_BRISTLE_THRESHOLD);
m_properties.antialias = settings->getBool(HAIRY_BRISTLE_ANTI_ALIASING);
m_properties.useCompositing = settings->getBool(HAIRY_BRISTLE_USE_COMPOSITING);
m_properties.connectedPath = settings->getBool(HAIRY_BRISTLE_CONNECTED);
}
KisSpacingInformation KisHairyPaintOp::paintAt(const KisPaintInformation& info)
{
return updateSpacingImpl(info);
}
KisSpacingInformation KisHairyPaintOp::updateSpacingImpl(const KisPaintInformation &info) const
{
Q_UNUSED(info);
return KisSpacingInformation(0.5);
}
void KisHairyPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance)
{
Q_UNUSED(currentDistance);
if (!painter()) return;
if (!m_dab) {
m_dab = source()->createCompositionSourceDevice();
}
else {
m_dab->clear();
}
/**
* Even though we don't use spacing in hairy brush, we should still
* initialize its distance information to ensure drawing angle and
* other history-based sensors work fine.
*/
KisPaintInformation pi(pi2);
KisPaintInformation::DistanceInformationRegistrar r =
pi.registerDistanceInformation(currentDistance);
// Hairy Brush is capable of working with zero scale,
// so no additional checks for 'zero'ness are needed
qreal scale = m_sizeOption.apply(pi);
scale *= KisLodTransform::lodToScale(painter()->device());
qreal rotation = m_rotationOption.apply(pi);
quint8 origOpacity = m_opacityOption.apply(painter(), pi);
const bool mirrorFlip = pi1.canvasMirroredH() != pi1.canvasMirroredV();
// we don't use spacing here (the brush itself is used only once
// during initialization), so we should just skip the distance info
// update
m_brush.paintLine(m_dab, m_dev, pi1, pi, scale * m_properties.scaleFactor, mirrorFlip ? -rotation : rotation);
//QRect rc = m_dab->exactBounds();
QRect rc = m_dab->extent();
painter()->bitBlt(rc.topLeft(), m_dab, rc);
painter()->renderMirrorMask(rc, m_dab);
painter()->setOpacity(origOpacity);
// we don't use spacing in hairy brush, but history is
// still important for us
currentDistance->registerPaintedDab(pi,
KisSpacingInformation(),
KisTimingInformation());
}
diff --git a/plugins/paintops/hairy/kis_hairy_paintop.h b/plugins/paintops/hairy/kis_hairy_paintop.h
index e9de44d51d..94300816b6 100644
--- a/plugins/paintops/hairy/kis_hairy_paintop.h
+++ b/plugins/paintops/hairy/kis_hairy_paintop.h
@@ -1,63 +1,65 @@
/*
* Copyright (c) 2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2008-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_HAIRYPAINTOP_H_
#define KIS_HAIRYPAINTOP_H_
#include <klocalizedstring.h>
#include <brushengine/kis_paintop.h>
#include <brushengine/kis_paintop_factory.h>
#include <kis_types.h>
#include "hairy_brush.h"
#include <kis_pressure_size_option.h>
#include <kis_pressure_rotation_option.h>
#include <kis_pressure_opacity_option.h>
class KisPainter;
class KisBrushBasedPaintOpSettings;
+class KisResourcesInterface;
class KisHairyPaintOp : public KisPaintOp
{
public:
KisHairyPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image);
void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) override;
+ static QList<KoResourceSP> prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface);
protected:
KisSpacingInformation paintAt(const KisPaintInformation& info) override;
KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override;
private:
KisHairyProperties m_properties;
KisPaintDeviceSP m_dab;
KisPaintDeviceSP m_dev;
HairyBrush m_brush;
KisPressureRotationOption m_rotationOption;
KisPressureSizeOption m_sizeOption;
KisPressureOpacityOption m_opacityOption;
void loadSettings(const KisBrushBasedPaintOpSettings* settings);
};
#endif // KIS_HAIRYPAINTOP_H_
diff --git a/plugins/paintops/hairy/kis_hairy_paintop_settings.cpp b/plugins/paintops/hairy/kis_hairy_paintop_settings.cpp
index 2484b1f995..34e89a2348 100644
--- a/plugins/paintops/hairy/kis_hairy_paintop_settings.cpp
+++ b/plugins/paintops/hairy/kis_hairy_paintop_settings.cpp
@@ -1,36 +1,37 @@
/*
* Copyright (c) 2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2008-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.
*/
#include <QPainter>
#include "kis_image.h"
#include "kis_hairy_paintop_settings.h"
#include "kis_hairy_bristle_option.h"
#include "kis_brush_based_paintop_options_widget.h"
#include "kis_boundary.h"
-KisHairyPaintOpSettings::KisHairyPaintOpSettings()
+KisHairyPaintOpSettings::KisHairyPaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
+ : KisBrushBasedPaintOpSettings(resourcesInterface)
{
}
QPainterPath KisHairyPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom)
{
return brushOutlineImpl(info, mode, alignForZoom, getDouble(HAIRY_BRISTLE_SCALE));
}
diff --git a/plugins/paintops/hairy/kis_hairy_paintop_settings.h b/plugins/paintops/hairy/kis_hairy_paintop_settings.h
index 132ff2a067..345a313113 100644
--- a/plugins/paintops/hairy/kis_hairy_paintop_settings.h
+++ b/plugins/paintops/hairy/kis_hairy_paintop_settings.h
@@ -1,41 +1,41 @@
/*
* Copyright (c) 2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2008-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_HAIRYPAINTOP_SETTINGS_H_
#define KIS_HAIRYPAINTOP_SETTINGS_H_
#include <QList>
#include <kis_brush_based_paintop_settings.h>
#include <kis_types.h>
class KisHairyPaintOpSettings : public KisBrushBasedPaintOpSettings
{
public:
using KisPaintOpSettings::fromXML;
- KisHairyPaintOpSettings();
+ KisHairyPaintOpSettings(KisResourcesInterfaceSP resourcesInterface);
using KisBrushBasedPaintOpSettings::brushOutline;
QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override;
};
#endif
diff --git a/plugins/paintops/hairy/kis_hairy_paintop_settings_widget.cpp b/plugins/paintops/hairy/kis_hairy_paintop_settings_widget.cpp
index 3ded30acfe..dce14d39d6 100644
--- a/plugins/paintops/hairy/kis_hairy_paintop_settings_widget.cpp
+++ b/plugins/paintops/hairy/kis_hairy_paintop_settings_widget.cpp
@@ -1,73 +1,74 @@
/*
* Copyright (c) 2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2008 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.
*/
#include "kis_hairy_paintop_settings_widget.h"
#include "kis_hairy_paintop_settings.h"
#include "kis_hairy_ink_option.h"
#include <kis_paint_action_type_option.h>
#include "kis_hairy_bristle_option.h"
#include <kis_curve_option_widget.h>
#include <kis_pressure_size_option.h>
#include <kis_pressure_rotation_option.h>
#include <kis_pressure_opacity_option.h>
#include <kis_compositeop_option.h>
#include <kis_brush_option_widget.h>
+#include <KisGlobalResourcesInterface.h>
KisHairyPaintOpSettingsWidget:: KisHairyPaintOpSettingsWidget(QWidget* parent)
: KisBrushBasedPaintopOptionWidget(parent)
{
addPaintOpOption(new KisHairyBristleOption(), i18n("Bristle options"));
addPaintOpOption(new KisHairyInkOption(), i18n("Ink depletion"));
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation"));
addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode"));
KisBrushOptionWidget *brushWidget = brushOptionWidget();
QStringList hiddenOptions;
hiddenOptions << "KisBrushChooser/lblSpacing"
<< "KisBrushChooser/Spacing"
<< "KisBrushChooser/ColorAsMask"
<< "KisAutoBrushWidget/btnAntiAliasing"
<< "KisAutoBrushWidget/grpFade"
<< "KisAutoBrushWidget/lblDensity"
<< "KisAutoBrushWidget/density"
<< "KisAutoBrushWidget/lblSpacing"
<< "KisAutoBrushWidget/spacingWidget"
<< "KisAutoBrushWidget/lblRandomness"
<< "KisAutoBrushWidget/inputRandomness"
;
brushWidget->hideOptions(hiddenOptions);
}
KisHairyPaintOpSettingsWidget::~ KisHairyPaintOpSettingsWidget()
{
}
KisPropertiesConfigurationSP KisHairyPaintOpSettingsWidget::configuration() const
{
- KisHairyPaintOpSettings* config = new KisHairyPaintOpSettings();
+ KisHairyPaintOpSettings* config = new KisHairyPaintOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisHairyPaintOpSettingsWidget*>(this));
config->setProperty("paintop", "hairybrush"); // XXX: make this a const id string
writeConfiguration(config);
return config;
}
diff --git a/plugins/paintops/hatching/kis_hatching_paintop.cpp b/plugins/paintops/hatching/kis_hatching_paintop.cpp
index 5d70a05a08..9a16040fb2 100644
--- a/plugins/paintops/hatching/kis_hatching_paintop.cpp
+++ b/plugins/paintops/hatching/kis_hatching_paintop.cpp
@@ -1,213 +1,213 @@
/*
* Copyright (c) 2008,2009 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2010 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_hatching_paintop.h"
#include "kis_hatching_paintop_settings.h"
#include <cmath>
#include <QRect>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <kis_image.h>
#include <kis_debug.h>
#include <kis_global.h>
#include <kis_paint_device.h>
#include <kis_painter.h>
#include <kis_types.h>
#include <brushengine/kis_paintop.h>
#include <kis_brush_based_paintop.h>
#include <brushengine/kis_paint_information.h>
#include <kis_fixed_paint_device.h>
#include <kis_pressure_opacity_option.h>
#include <kis_lod_transform.h>
#include <kis_spacing_information.h>
#include <KoColorSpaceRegistry.h>
KisHatchingPaintOp::KisHatchingPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP /*image*/)
: KisBrushBasedPaintOp(settings, painter)
{
Q_UNUSED(node);
- m_settings = new KisHatchingPaintOpSettings();
+ m_settings = static_cast<KisHatchingPaintOpSettings*>(settings->clone().data());
static_cast<const KisHatchingPaintOpSettings*>(settings.data())->initializeTwin(m_settings);
m_hatchingBrush = new HatchingBrush(m_settings);
m_angleOption.readOptionSetting(settings);
m_crosshatchingOption.readOptionSetting(settings);
m_separationOption.readOptionSetting(settings);
m_thicknessOption.readOptionSetting(settings);
m_opacityOption.readOptionSetting(settings);
m_sizeOption.readOptionSetting(settings);
m_angleOption.resetAllSensors();
m_crosshatchingOption.resetAllSensors();
m_separationOption.resetAllSensors();
m_thicknessOption.resetAllSensors();
m_opacityOption.resetAllSensors();
m_sizeOption.resetAllSensors();
}
KisHatchingPaintOp::~KisHatchingPaintOp()
{
delete m_hatchingBrush;
}
KisSpacingInformation KisHatchingPaintOp::paintAt(const KisPaintInformation& info)
{
//------START SIMPLE ERROR CATCHING-------
if (!painter()->device()) return KisSpacingInformation(1.0);
if (!m_hatchedDab)
m_hatchedDab = source()->createCompositionSourceDevice();
else
m_hatchedDab->clear();
//Simple convenience renaming, I'm thinking of removing these inherited quirks
KisBrushSP brush = m_brush;
KisPaintDeviceSP device = painter()->device();
//Macro to catch errors
Q_ASSERT(brush);
//----------SIMPLE error catching code, maybe it's not even needed------
if (!brush) return KisSpacingInformation(1.0);
if (!brush->canPaintFor(info)) return KisSpacingInformation(1.0);
//SENSOR-depending settings
m_settings->anglesensorvalue = m_angleOption.apply(info);
m_settings->crosshatchingsensorvalue = m_crosshatchingOption.apply(info);
m_settings->separationsensorvalue = m_separationOption.apply(info);
m_settings->thicknesssensorvalue = m_thicknessOption.apply(info);
const qreal additionalScale = KisLodTransform::lodToScale(painter()->device());
const double scale = additionalScale * m_sizeOption.apply(info);
if ((scale * brush->width()) <= 0.01 || (scale * brush->height()) <= 0.01) return KisSpacingInformation(1.0);
KisDabShape shape(scale, 1.0, 0.0);
quint8 origOpacity = m_opacityOption.apply(painter(), info);
/*----Fetch the Dab----*/
static const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8();
static KoColor color(Qt::black, cs);
QRect dstRect;
KisFixedPaintDeviceSP maskDab =
m_dabCache->fetchDab(cs, color, info.pos(),
shape,
info, 1.0, &dstRect);
// sanity check
KIS_ASSERT_RECOVER_NOOP(dstRect.size() == maskDab->bounds().size());
/*-----Convenient renaming for the limits of the maskDab, this will be used
to hatch a dab of just the right size------*/
qint32 x, y, sw, sh;
dstRect.getRect(&x, &y, &sw, &sh);
//------This If_block pre-fills the future m_hatchedDab with a pretty backgroundColor
if (m_settings->opaquebackground) {
KoColor aersh = painter()->backgroundColor();
m_hatchedDab->fill(0, 0, (sw - 1), (sh - 1), aersh.data()); //this plus yellow background = french fry brush
}
/* If block describing how to stack hatching passes to generate
crosshatching according to user specifications */
if (m_settings->enabledcurvecrosshatching) {
if (m_settings->perpendicular) {
if (m_settings->crosshatchingsensorvalue > 0.5)
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(90), painter()->paintColor(), additionalScale);
}
else if (m_settings->minusthenplus) {
if (m_settings->crosshatchingsensorvalue > 0.33)
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale);
if (m_settings->crosshatchingsensorvalue > 0.67)
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale);
}
else if (m_settings->plusthenminus) {
if (m_settings->crosshatchingsensorvalue > 0.33)
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale);
if (m_settings->crosshatchingsensorvalue > 0.67)
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale);
}
else if (m_settings->moirepattern) {
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle((m_settings->crosshatchingsensorvalue) * 360), painter()->paintColor(), additionalScale);
}
} else {
if (m_settings->perpendicular) {
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(90), painter()->paintColor(), additionalScale);
}
else if (m_settings->minusthenplus) {
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale);
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale);
}
else if (m_settings->plusthenminus) {
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale);
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale);
}
else if (m_settings->moirepattern) {
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-10), painter()->paintColor(), additionalScale);
}
}
if (m_settings->enabledcurveangle)
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle((m_settings->anglesensorvalue)*360+m_settings->angle), painter()->paintColor(), additionalScale);
// The base hatch... unless moiré or angle
if (!m_settings->moirepattern && !m_settings->enabledcurveangle)
m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, m_settings->angle, painter()->paintColor(), additionalScale);
// The most important line, the one that paints to the screen.
painter()->bitBltWithFixedSelection(x, y, m_hatchedDab, maskDab, sw, sh);
painter()->renderMirrorMaskSafe(QRect(QPoint(x, y), QSize(sw, sh)), m_hatchedDab, 0, 0, maskDab,
!m_dabCache->needSeparateOriginal());
painter()->setOpacity(origOpacity);
return effectiveSpacing(scale);
}
KisSpacingInformation KisHatchingPaintOp::updateSpacingImpl(const KisPaintInformation &info) const
{
const qreal scale = KisLodTransform::lodToScale(painter()->device()) * m_sizeOption.apply(info);
return effectiveSpacing(scale);
}
double KisHatchingPaintOp::spinAngle(double spin)
{
double tempangle = m_settings->angle + spin;
qint8 factor = 1;
if (tempangle < 0)
factor = -1;
tempangle = fabs(fmod(tempangle, 180));
if ((tempangle >= 0) && (tempangle <= 90))
return factor * tempangle;
else if ((tempangle > 90) && (tempangle <= 180))
return factor * -(180 - tempangle);
return 0; // this should never be executed except if NAN
}
diff --git a/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp b/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp
index 362ae1acb9..e3446ca6fb 100644
--- a/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp
+++ b/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp
@@ -1,220 +1,221 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2010 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_hatching_paintop_settings.h"
#include <kis_paint_action_type_option.h>
#include <QDomDocument>
#include <QDomElement>
const QString HATCHING_VERSION = "Hatching/Version";
struct KisHatchingPaintOpSettings::Private
{
QList<KisUniformPaintOpPropertyWSP> uniformProperties;
};
-KisHatchingPaintOpSettings::KisHatchingPaintOpSettings()
- : m_d(new Private)
+KisHatchingPaintOpSettings::KisHatchingPaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
+ : KisBrushBasedPaintOpSettings(resourcesInterface),
+ m_d(new Private)
{
setProperty(HATCHING_VERSION, "2");
}
KisHatchingPaintOpSettings::~KisHatchingPaintOpSettings()
{
}
void KisHatchingPaintOpSettings::initializeTwin(KisPaintOpSettingsSP settings) const
{
// XXX: this is a nice way to reinvent the copy constructor?
/*--------DO NOT REMOVE please, use this to review the XML config tree
QMap<QString, QVariant> rofl = QMap<QString, QVariant>(getProperties());
QMap<QString, QVariant>::const_iterator i;
for (i = rofl.constBegin(); i != rofl.constEnd(); ++i)
dbgKrita << i.key() << ":" << i.value();
/----------DO NOT REMOVE----------------*/
KisHatchingPaintOpSettings *convenienttwin = static_cast<KisHatchingPaintOpSettings*>(settings.data());
convenienttwin->enabledcurveangle = getBool("PressureAngle");
convenienttwin->enabledcurvecrosshatching = getBool("PressureCrosshatching");
convenienttwin->enabledcurveopacity = getBool("PressureOpacity");
convenienttwin->enabledcurveseparation = getBool("PressureSeparation");
convenienttwin->enabledcurvesize = getBool("PressureSize");
convenienttwin->enabledcurvethickness = getBool("PressureThickness");
convenienttwin->angle = getDouble("Hatching/angle");
convenienttwin->separation = getDouble("Hatching/separation");
convenienttwin->thickness = getDouble("Hatching/thickness");
convenienttwin->origin_x = getDouble("Hatching/origin_x");
convenienttwin->origin_y = getDouble("Hatching/origin_y");
convenienttwin->nocrosshatching = getBool("Hatching/bool_nocrosshatching");
convenienttwin->perpendicular = getBool("Hatching/bool_perpendicular");
convenienttwin->minusthenplus = getBool("Hatching/bool_minusthenplus");
convenienttwin->plusthenminus = getBool("Hatching/bool_plusthenminus");
convenienttwin->moirepattern = getBool("Hatching/bool_moirepattern");
convenienttwin->separationintervals = getInt("Hatching/separationintervals");
//convenienttwin->trigonometryalgebra = getBool("Hatching/bool_trigonometryalgebra");
//convenienttwin->scratchoff = getBool("Hatching/bool_scratchoff");
convenienttwin->antialias = getBool("Hatching/bool_antialias");
convenienttwin->opaquebackground = getBool("Hatching/bool_opaquebackground");
convenienttwin->subpixelprecision = getBool("Hatching/bool_subpixelprecision");
if (getBool("Hatching/bool_nocrosshatching"))
convenienttwin->crosshatchingstyle = 0;
else if (getBool("Hatching/bool_perpendicular"))
convenienttwin->crosshatchingstyle = 1;
else if (getBool("Hatching/bool_minusthenplus"))
convenienttwin->crosshatchingstyle = 2;
else if (getBool("Hatching/bool_plusthenminus"))
convenienttwin->crosshatchingstyle = 3;
if (getBool("Hatching/bool_moirepattern"))
convenienttwin->crosshatchingstyle = 4;
}
void KisHatchingPaintOpSettings::fromXML(const QDomElement& elt)
{
setProperty(HATCHING_VERSION, "1"); // This make sure that fromXML will override HAIRY_VERSION with 2, or will default to 1
KisBrushBasedPaintOpSettings::fromXML(elt);
QVariant v;
if (!getProperty(HATCHING_VERSION, v) || v == "1") {
setProperty("Hatching/thickness", 2.0 * getDouble("Hatching/thickness"));
}
setProperty(HATCHING_VERSION, "2"); // make sure it's saved as version 2 next time
}
#include <brushengine/kis_slider_based_paintop_property.h>
#include "kis_paintop_preset.h"
#include "kis_paintop_settings_update_proxy.h"
#include "kis_hatching_options.h"
QList<KisUniformPaintOpPropertySP> KisHatchingPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings)
{
QList<KisUniformPaintOpPropertySP> props =
listWeakToStrong(m_d->uniformProperties);
if (props.isEmpty()) {
{
KisDoubleSliderBasedPaintOpPropertyCallback *prop =
new KisDoubleSliderBasedPaintOpPropertyCallback(
KisDoubleSliderBasedPaintOpPropertyCallback::Double,
"hatching_angle",
i18n("Hatching Angle"),
settings, 0);
const QString degree = QChar(Qt::Key_degree);
prop->setRange(-90, 90);
prop->setSingleStep(0.01);
prop->setDecimals(2);
prop->setSuffix(degree);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
HatchingOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.angle);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
HatchingOption option;
option.readOptionSetting(prop->settings().data());
option.angle = prop->value().toReal();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisDoubleSliderBasedPaintOpPropertyCallback *prop =
new KisDoubleSliderBasedPaintOpPropertyCallback(
KisDoubleSliderBasedPaintOpPropertyCallback::Double,
"hatching_separation",
i18n("Separation"),
settings, 0);
prop->setRange(1.0, 30);
prop->setSingleStep(0.01);
prop->setDecimals(2);
prop->setSuffix(i18n(" px"));
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
HatchingOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.separation);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
HatchingOption option;
option.readOptionSetting(prop->settings().data());
option.separation = prop->value().toReal();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisDoubleSliderBasedPaintOpPropertyCallback *prop =
new KisDoubleSliderBasedPaintOpPropertyCallback(
KisDoubleSliderBasedPaintOpPropertyCallback::Double,
"hatching_thickness",
i18n("Thickness"),
settings, 0);
prop->setRange(1.0, 30);
prop->setSingleStep(0.01);
prop->setDecimals(2);
prop->setSuffix(i18n(" px"));
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
HatchingOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.thickness);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
HatchingOption option;
option.readOptionSetting(prop->settings().data());
option.thickness = prop->value().toReal();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
}
return KisPaintOpSettings::uniformProperties(settings) + props;
}
diff --git a/plugins/paintops/hatching/kis_hatching_paintop_settings.h b/plugins/paintops/hatching/kis_hatching_paintop_settings.h
index 089b3ed3bc..2cc3b0fb63 100644
--- a/plugins/paintops/hatching/kis_hatching_paintop_settings.h
+++ b/plugins/paintops/hatching/kis_hatching_paintop_settings.h
@@ -1,89 +1,89 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2010 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.
*/
#ifndef KIS_HATCHING_PAINTOP_SETTINGS_H_
#define KIS_HATCHING_PAINTOP_SETTINGS_H_
#include <brushengine/kis_paintop_settings.h>
#include <kis_brush_based_paintop_settings.h>
#include "kis_hatching_paintop_settings_widget.h"
#include <QScopedPointer>
class KisHatchingPaintOpSettings : public KisBrushBasedPaintOpSettings
{
public:
- KisHatchingPaintOpSettings();
+ KisHatchingPaintOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisHatchingPaintOpSettings() override;
//Dialogs enabled
bool enabledcurveangle;
bool enabledcurvecrosshatching;
bool enabledcurveopacity;
bool enabledcurveseparation;
bool enabledcurvesize;
bool enabledcurvethickness;
//Hatching Options
double angle;
double separation;
double thickness;
double origin_x;
double origin_y;
bool nocrosshatching;
bool perpendicular;
bool minusthenplus;
bool plusthenminus;
bool moirepattern;
int crosshatchingstyle;
int separationintervals;
//Hatching Preferences
//bool trigonometryalgebra;
//bool scratchoff;
bool antialias;
bool subpixelprecision;
bool opaquebackground;
//Angle, Crosshatching, Separation and Thickness curves
double anglesensorvalue;
double crosshatchingsensorvalue;
double separationsensorvalue;
double thicknesssensorvalue;
void initializeTwin(KisPaintOpSettingsSP convenienttwin) const;
using KisPropertiesConfiguration::fromXML;
void fromXML(const QDomElement&) override;
QList<KisUniformPaintOpPropertySP> uniformProperties(KisPaintOpSettingsSP settings) override;
private:
Q_DISABLE_COPY(KisHatchingPaintOpSettings)
struct Private;
const QScopedPointer<Private> m_d;
};
typedef KisSharedPtr<KisHatchingPaintOpSettings> KisHatchingPaintOpSettingsSP;
#endif
diff --git a/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp b/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp
index a1b67a70af..3b61c29b58 100644
--- a/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp
+++ b/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp
@@ -1,138 +1,139 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2010 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_hatching_paintop_settings_widget.h"
#include "kis_hatching_options.h"
#include "kis_hatching_preferences.h"
#include "kis_hatching_paintop_settings.h"
#include "kis_hatching_pressure_angle_option.h"
#include "kis_hatching_pressure_crosshatching_option.h"
#include "kis_hatching_pressure_separation_option.h"
#include "kis_hatching_pressure_thickness_option.h"
#include <kis_brush_option_widget.h>
#include <kis_curve_option_widget.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_size_option.h>
#include <kis_paintop_settings_widget.h>
#include <kis_paint_action_type_option.h>
#include <kis_compositeop_option.h>
#include "kis_texture_option.h"
#include <kis_pressure_mirror_option_widget.h>
#include "kis_pressure_texture_strength_option.h"
+#include <KisGlobalResourcesInterface.h>
#include <QDomDocument>
#include <QDomElement>
KisHatchingPaintOpSettingsWidget:: KisHatchingPaintOpSettingsWidget(QWidget* parent)
: KisBrushBasedPaintopOptionWidget(parent)
{
setPrecisionEnabled(true);
//-------Adding widgets to the screen------------
addPaintOpOption(new KisHatchingOptions(), i18n("Hatching options"));
addPaintOpOption(new KisHatchingPreferences(), i18n("Hatching preferences"));
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureSeparationOption(), i18n("0.0"), i18n("1.0")), i18n("Separation"));
addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureThicknessOption(), i18n("0.0"), i18n("1.0")), i18n("Thickness"));
addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureAngleOption(), i18n("0.0"), i18n("1.0")), i18n("Angle"));
addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureCrosshatchingOption(), i18n("0.0"), i18n("1.0")), i18n("Crosshatching"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size"));
addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror"));
addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode"));
addPaintOpOption(new KisTextureOption(), i18n("Pattern"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength"));
//-----Useful to read first:------
/*
Below you will encounter a reasonably correct solution to the problem of changing
the default presets of the "BrushTip" popup configuration dialogue.
In my (Pentalis) opinion, the best solution is code refactoring (simpler ways
to change the defaults). On the meanwhile, copypasting this code
won't give your class a charisma penalty.
In kis_hatching_paintop_settings.cpp you will find a snippet of code to
discover the structure of your XML config tree if you need to edit it at build
time like here.
*/
//---------START ALTERING DEFAULT VALUES-----------
//As the name implies, reconfigurationCourier is the KisPropertiesConfigurationSP
//we'll use as an intermediary to edit the default settings
KisPropertiesConfigurationSP reconfigurationCourier = configuration();
/*xMLAnalyzer is an empty document we'll use to analyze and edit the config string part by part
I know the important string is "brush_definition" because I read the tree with the snippet
in kis_hatching_paintop_settings.cpp */
QDomDocument xMLAnalyzer;
xMLAnalyzer.setContent(reconfigurationCourier->getString("brush_definition"));
/*More things I know by reading the XML tree. At this point you can just read it with:
dbgKrita << xMLAnalyzer.toString() ;
those QDomElements are the way to navigate the XML tree, read
https://doc.qt.io/qt-5/qdomdocument.html for more information */
QDomElement firstTag = xMLAnalyzer.documentElement();
QDomElement firstTagsChild = firstTag.elementsByTagName("MaskGenerator").item(0).toElement();
// SET THE DEFAULT VALUES
firstTag.attributeNode("spacing").setValue("0.4");
firstTagsChild.attributeNode("diameter").setValue("30");
//Write them into the intermediary config file
reconfigurationCourier->setProperty("brush_definition", xMLAnalyzer.toString());
KisCubicCurve CurveSize;
CurveSize.fromString("0,1;1,0.1;");
//dbgKrita << "\n\n\n" << CurveSize.toString() << "\n\n\n";
QVariant QVCurveSize = QVariant::fromValue(CurveSize);
reconfigurationCourier->setProperty("CurveSize", QVCurveSize);
setConfiguration(reconfigurationCourier); // Finished.
/* Debugging block
QMap<QString, QVariant> rofl = QMap<QString, QVariant>(reconfigurationCourier->getProperties());
QMap<QString, QVariant>::const_iterator i;
for (i = rofl.constBegin(); i != rofl.constEnd(); ++i)
dbgKrita << i.key() << ":" << i.value();
*/
}
KisHatchingPaintOpSettingsWidget::~ KisHatchingPaintOpSettingsWidget()
{
}
KisPropertiesConfigurationSP KisHatchingPaintOpSettingsWidget::configuration() const
{
- KisHatchingPaintOpSettingsSP config = new KisHatchingPaintOpSettings();
+ KisHatchingPaintOpSettingsSP config = new KisHatchingPaintOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisHatchingPaintOpSettingsWidget*>(this));
config->setProperty("paintop", "hatchingbrush"); // XXX: make this a const id string
writeConfiguration(config);
return config;
}
diff --git a/plugins/paintops/libpaintop/KisDabCacheUtils.h b/plugins/paintops/libpaintop/KisDabCacheUtils.h
index 6e694a08d3..aad40fad10 100644
--- a/plugins/paintops/libpaintop/KisDabCacheUtils.h
+++ b/plugins/paintops/libpaintop/KisDabCacheUtils.h
@@ -1,122 +1,122 @@
/*
* 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 KISDABCACHEUTILS_H
#define KISDABCACHEUTILS_H
#include <QRect>
#include <QSize>
#include "kis_types.h"
#include <kis_pressure_mirror_option.h>
#include "kis_dab_shape.h"
#include "kritapaintop_export.h"
#include <functional>
class KisBrush;
-typedef KisSharedPtr<KisBrush> KisBrushSP;
+typedef QSharedPointer<KisBrush> KisBrushSP;
class KisColorSource;
class KisPressureSharpnessOption;
class KisTextureProperties;
namespace KisDabCacheUtils
{
struct PAINTOP_EXPORT DabRenderingResources
{
DabRenderingResources();
virtual ~DabRenderingResources();
virtual void syncResourcesToSeqNo(int seqNo, const KisPaintInformation &info);
KisBrushSP brush;
QScopedPointer<KisColorSource> colorSource;
QScopedPointer<KisPressureSharpnessOption> sharpnessOption;
QScopedPointer<KisTextureProperties> textureOption;
KisPaintDeviceSP colorSourceDevice;
private:
DabRenderingResources(const DabRenderingResources &rhs) = delete;
};
typedef std::function<DabRenderingResources*()> ResourcesFactory;
struct PAINTOP_EXPORT DabRequestInfo
{
DabRequestInfo(const KoColor &_color,
const QPointF &_cursorPoint,
const KisDabShape &_shape,
const KisPaintInformation &_info,
qreal _softnessFactor)
: color(_color),
cursorPoint(_cursorPoint),
shape(_shape),
info(_info),
softnessFactor(_softnessFactor)
{
}
const KoColor &color;
const QPointF &cursorPoint;
const KisDabShape &shape;
const KisPaintInformation &info;
const qreal softnessFactor;
private:
DabRequestInfo(const DabRequestInfo &rhs);
};
struct PAINTOP_EXPORT DabGenerationInfo
{
MirrorProperties mirrorProperties;
KisDabShape shape;
QRect dstDabRect;
QPointF subPixel;
bool solidColorFill = true;
KoColor paintColor;
KisPaintInformation info;
qreal softnessFactor = 1.0;
bool needsPostprocessing = false;
};
PAINTOP_EXPORT QRect correctDabRectWhenFetchedFromCache(const QRect &dabRect,
const QSize &realDabSize);
PAINTOP_EXPORT void generateDab(const DabGenerationInfo &di,
DabRenderingResources *resources,
KisFixedPaintDeviceSP *dab);
PAINTOP_EXPORT void postProcessDab(KisFixedPaintDeviceSP dab,
const QPoint &dabTopLeft,
const KisPaintInformation& info,
DabRenderingResources *resources);
}
template<class T> class QSharedPointer;
class KisDabRenderingJob;
typedef QSharedPointer<KisDabRenderingJob> KisDabRenderingJobSP;
#endif // KISDABCACHEUTILS_H
diff --git a/plugins/paintops/libpaintop/KisMaskingBrushOption.cpp b/plugins/paintops/libpaintop/KisMaskingBrushOption.cpp
index c180d5962c..f1bfb917b7 100644
--- a/plugins/paintops/libpaintop/KisMaskingBrushOption.cpp
+++ b/plugins/paintops/libpaintop/KisMaskingBrushOption.cpp
@@ -1,129 +1,130 @@
/*
* 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>
+#include <KisGlobalResourcesInterface.h>
struct KisMaskingBrushOption::Private
{
Private()
: ui(new QWidget())
{
QVBoxLayout *l = new QVBoxLayout();
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());
+ props.read(setting.data(), m_d->masterBrushSizeAdapter(), KisGlobalResourcesInterface::instance());
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/KisMaskingBrushOptionProperties.cpp b/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.cpp
index 739bb3e701..d05ff9bb25 100644
--- a/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.cpp
+++ b/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.cpp
@@ -1,87 +1,93 @@
/*
* 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 "KisMaskingBrushOptionProperties.h"
#include "kis_brush_option.h"
#include <brushengine/KisPaintopSettingsIds.h>
#include <KoCompositeOpRegistry.h>
KisMaskingBrushOptionProperties::KisMaskingBrushOptionProperties()
: compositeOpId(COMPOSITE_MULT)
{
}
void KisMaskingBrushOptionProperties::write(KisPropertiesConfiguration *setting, qreal masterBrushSize) const
{
setting->setProperty(KisPaintOpUtils::MaskingBrushEnabledTag, isEnabled);
setting->setProperty(KisPaintOpUtils::MaskingBrushCompositeOpTag, compositeOpId);
setting->setProperty(KisPaintOpUtils::MaskingBrushUseMasterSizeTag, useMasterSize);
const qreal masterSizeCoeff =
brush && masterBrushSize > 0 ? brush->userEffectiveSize() / masterBrushSize : 1.0;
setting->setProperty(KisPaintOpUtils::MaskingBrushMasterSizeCoeffTag, masterSizeCoeff);
// TODO: skip saving in some cases?
// if (!isEnabled) return;
if (brush) {
KisPropertiesConfigurationSP embeddedConfig = new KisPropertiesConfiguration();
{
KisBrushOptionProperties option;
option.setBrush(brush);
option.writeOptionSetting(embeddedConfig);
}
setting->setPrefixedProperties(KisPaintOpUtils::MaskingBrushPresetPrefix, embeddedConfig);
- const QString brushFileName = brush->shortFilename();
+ const QString brushFileName = brush->filename();
if (!brushFileName.isEmpty()) {
QStringList requiredFiles =
setting->getStringList(KisPaintOpUtils::RequiredBrushFilesListTag);
requiredFiles << brushFileName;
setting->setProperty(KisPaintOpUtils::RequiredBrushFilesListTag, requiredFiles);
}
}
}
-void KisMaskingBrushOptionProperties::read(const KisPropertiesConfiguration *setting, qreal masterBrushSize)
+QList<KoResourceSP> KisMaskingBrushOptionProperties::prepareLinkedResources(const KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface)
+{
+ KisBrushOptionProperties option;
+ return option.prepareLinkedResources(settings, resourcesInterface);
+}
+
+void KisMaskingBrushOptionProperties::read(const KisPropertiesConfiguration *setting, qreal masterBrushSize, KisResourcesInterfaceSP resourcesInterface)
{
isEnabled = setting->getBool(KisPaintOpUtils::MaskingBrushEnabledTag);
compositeOpId = setting->getString(KisPaintOpUtils::MaskingBrushCompositeOpTag, COMPOSITE_MULT);
useMasterSize = setting->getBool(KisPaintOpUtils::MaskingBrushUseMasterSizeTag, true);
KisPropertiesConfigurationSP embeddedConfig = new KisPropertiesConfiguration();
setting->getPrefixedProperties(KisPaintOpUtils::MaskingBrushPresetPrefix, embeddedConfig);
KisBrushOptionProperties option;
- option.readOptionSetting(embeddedConfig);
+ option.readOptionSetting(embeddedConfig, resourcesInterface);
brush = option.brush();
if (brush && useMasterSize) {
const qreal masterSizeCoeff = setting->getDouble(KisPaintOpUtils::MaskingBrushMasterSizeCoeffTag, 1.0);
brush->setUserEffectiveSize(masterSizeCoeff * masterBrushSize);
}
}
diff --git a/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.h b/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.h
index 8ad9ff1a1a..f8703b7093 100644
--- a/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.h
+++ b/plugins/paintops/libpaintop/KisMaskingBrushOptionProperties.h
@@ -1,42 +1,44 @@
/*
* 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 KISMASKINGBRUSHOPTIONPROPERTIES_H
#define KISMASKINGBRUSHOPTIONPROPERTIES_H
#include "kritapaintop_export.h"
#include <kis_types.h>
+#include <kis_brush.h>
+class KisResourcesInterface;
+using KisResourcesInterfaceSP = QSharedPointer<KisResourcesInterface>;
-class KisBrush;
-typedef KisSharedPtr<KisBrush> KisBrushSP;
struct PAINTOP_EXPORT KisMaskingBrushOptionProperties
{
KisMaskingBrushOptionProperties();
bool isEnabled = false;
KisBrushSP brush;
QString compositeOpId;
bool useMasterSize = true;
void write(KisPropertiesConfiguration *setting, qreal masterBrushSize) const;
- void read(const KisPropertiesConfiguration *setting, qreal masterBrushSize);
+ void read(const KisPropertiesConfiguration *setting, qreal masterBrushSize, KisResourcesInterfaceSP resourcesInterface);
+ QList<KoResourceSP> prepareLinkedResources(const KisPropertiesConfigurationSP settings, KisResourcesInterfaceSP resourcesInterface);
};
#endif // KISMASKINGBRUSHOPTIONPROPERTIES_H
diff --git a/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp b/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp
index 74ac1d9b1f..2b91ac3234 100644
--- a/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp
+++ b/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp
@@ -1,229 +1,229 @@
/*
* 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 "KisTextureMaskInfo.h"
#include <kis_paintop_settings.h>
#include <resources/KoPattern.h>
#include "kis_embedded_pattern_manager.h"
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <kis_algebra_2d.h>
#include <kis_lod_transform.h>
#include <kis_iterator_ng.h>
#include <QGlobalStatic>
/**********************************************************************/
/* KisTextureMaskInfo */
/**********************************************************************/
KisTextureMaskInfo::KisTextureMaskInfo(int levelOfDetail)
: m_levelOfDetail(levelOfDetail)
{
}
KisTextureMaskInfo::KisTextureMaskInfo(const KisTextureMaskInfo &rhs)
: m_levelOfDetail(rhs.m_levelOfDetail),
m_pattern(rhs.m_pattern),
m_scale(rhs.m_scale),
m_brightness(rhs.m_brightness),
m_contrast(rhs.m_contrast),
m_invert(rhs.m_invert),
m_cutoffLeft(rhs.m_cutoffLeft),
m_cutoffRight(rhs.m_cutoffRight),
m_cutoffPolicy(rhs.m_cutoffPolicy)
{
}
KisTextureMaskInfo::~KisTextureMaskInfo()
{
}
bool operator==(const KisTextureMaskInfo &lhs, const KisTextureMaskInfo &rhs) {
return
lhs.m_levelOfDetail == rhs.m_levelOfDetail &&
(lhs.m_pattern == rhs.m_pattern ||
(lhs.m_pattern &&
rhs.m_pattern &&
lhs.m_pattern->md5() == rhs.m_pattern->md5())) &&
qFuzzyCompare(lhs.m_scale, rhs.m_scale) &&
qFuzzyCompare(lhs.m_brightness, rhs.m_brightness) &&
qFuzzyCompare(lhs.m_contrast, rhs.m_contrast) &&
lhs.m_invert == rhs.m_invert &&
lhs.m_cutoffLeft == rhs.m_cutoffLeft &&
lhs.m_cutoffRight == rhs.m_cutoffRight &&
lhs.m_cutoffPolicy == rhs.m_cutoffPolicy;
}
KisTextureMaskInfo &KisTextureMaskInfo::operator=(const KisTextureMaskInfo &rhs)
{
m_levelOfDetail = rhs.m_levelOfDetail;
m_pattern = rhs.m_pattern;
m_scale = rhs.m_scale;
m_brightness = rhs.m_brightness;
m_contrast = rhs.m_contrast;
m_invert = rhs.m_invert;
m_cutoffLeft = rhs.m_cutoffLeft;
m_cutoffRight = rhs.m_cutoffRight;
m_cutoffPolicy = rhs.m_cutoffPolicy;
return *this;
}
int KisTextureMaskInfo::levelOfDetail() const {
return m_levelOfDetail;
}
bool KisTextureMaskInfo::hasMask() const {
return m_mask;
}
KisPaintDeviceSP KisTextureMaskInfo::mask() {
return m_mask;
}
QRect KisTextureMaskInfo::maskBounds() const {
return m_maskBounds;
}
-bool KisTextureMaskInfo::fillProperties(const KisPropertiesConfigurationSP setting)
+bool KisTextureMaskInfo::fillProperties(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface)
{
if (!setting->hasProperty("Texture/Pattern/PatternMD5")) {
return false;
}
- m_pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting);
+ m_pattern = KisEmbeddedPatternManager::tryFetchPattern(setting, resourcesInterface);
if (!m_pattern) {
warnKrita << "WARNING: Couldn't load the pattern for a stroke";
return false;
}
m_scale = setting->getDouble("Texture/Pattern/Scale", 1.0);
m_brightness = setting->getDouble("Texture/Pattern/Brightness");
m_contrast = setting->getDouble("Texture/Pattern/Contrast", 1.0);
m_invert = setting->getBool("Texture/Pattern/Invert");
m_cutoffLeft = setting->getInt("Texture/Pattern/CutoffLeft", 0);
m_cutoffRight = setting->getInt("Texture/Pattern/CutoffRight", 255);
m_cutoffPolicy = setting->getInt("Texture/Pattern/CutoffPolicy", 0);
return true;
}
void KisTextureMaskInfo::recalculateMask()
{
if (!m_pattern) return;
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8();
if (!m_mask) {
m_mask = new KisPaintDevice(cs);
}
QImage mask = m_pattern->pattern();
if ((mask.format() != QImage::Format_RGB32) |
(mask.format() != QImage::Format_ARGB32)) {
mask = mask.convertToFormat(QImage::Format_ARGB32);
}
qreal scale = m_scale * KisLodTransform::lodToScale(m_levelOfDetail);
if (!qFuzzyCompare(scale, 0.0)) {
QTransform tf;
tf.scale(scale, scale);
QRect rc = KisAlgebra2D::ensureRectNotSmaller(tf.mapRect(mask.rect()), QSize(2,2));
mask = mask.scaled(rc.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
const QRgb* pixel = reinterpret_cast<const QRgb*>(mask.constBits());
const int width = mask.width();
const int height = mask.height();
KisHLineIteratorSP iter = m_mask->createHLineIteratorNG(0, 0, width);
for (int row = 0; row < height; ++row) {
for (int col = 0; col < width; ++col) {
const QRgb currentPixel = pixel[row * width + col];
const int red = qRed(currentPixel);
const int green = qGreen(currentPixel);
const int blue = qBlue(currentPixel);
float alpha = qAlpha(currentPixel) / 255.0;
const int grayValue = (red * 11 + green * 16 + blue * 5) / 32;
float maskValue = (grayValue / 255.0) * alpha + (1 - alpha);
maskValue = maskValue - m_brightness;
maskValue = ((maskValue - 0.5)*m_contrast)+0.5;
if (maskValue > 1.0) {maskValue = 1;}
else if (maskValue < 0) {maskValue = 0;}
if (m_invert) {
maskValue = 1 - maskValue;
}
if (m_cutoffPolicy == 1 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) {
// mask out the dab if it's outside the pattern's cuttoff points
maskValue = OPACITY_TRANSPARENT_F;
}
else if (m_cutoffPolicy == 2 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) {
maskValue = OPACITY_OPAQUE_F;
}
cs->setOpacity(iter->rawData(), maskValue, 1);
iter->nextPixel();
}
iter->nextRow();
}
m_maskBounds = QRect(0, 0, width, height);
}
/**********************************************************************/
/* KisTextureMaskInfoCache */
/**********************************************************************/
Q_GLOBAL_STATIC(KisTextureMaskInfoCache, s_instance)
KisTextureMaskInfoCache *KisTextureMaskInfoCache::instance()
{
return s_instance;
}
KisTextureMaskInfoSP KisTextureMaskInfoCache::fetchCachedTextureInfo(KisTextureMaskInfoSP info) {
QMutexLocker locker(&m_mutex);
KisTextureMaskInfoSP &cachedInfo =
info->levelOfDetail() > 0 ? m_lodInfo : m_mainInfo;
if (!cachedInfo || *cachedInfo != *info) {
cachedInfo = info;
cachedInfo->recalculateMask();
}
return cachedInfo;
}
diff --git a/plugins/paintops/libpaintop/KisTextureMaskInfo.h b/plugins/paintops/libpaintop/KisTextureMaskInfo.h
index 9ac1834388..c2c8a98326 100644
--- a/plugins/paintops/libpaintop/KisTextureMaskInfo.h
+++ b/plugins/paintops/libpaintop/KisTextureMaskInfo.h
@@ -1,89 +1,91 @@
/*
* 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 KISTEXTUREMASKINFO_H
#define KISTEXTUREMASKINFO_H
#include <kis_paint_device.h>
#include <QSharedPointer>
#include <QMutex>
#include <boost/operators.hpp>
-class KoPattern;
+#include <KoPattern.h>
+
class KisTextureMaskInfo;
+class KisResourcesInterface;
class KisTextureMaskInfo : public boost::equality_comparable<KisTextureMaskInfo>
{
public:
KisTextureMaskInfo(int levelOfDetail);
KisTextureMaskInfo(const KisTextureMaskInfo &rhs);
~KisTextureMaskInfo();
friend bool operator==(const KisTextureMaskInfo &lhs, const KisTextureMaskInfo &rhs);
KisTextureMaskInfo& operator=(const KisTextureMaskInfo &rhs);
int levelOfDetail() const;
bool hasMask() const;
KisPaintDeviceSP mask();
QRect maskBounds() const;
- bool fillProperties(const KisPropertiesConfigurationSP setting);
+ bool fillProperties(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface);
void recalculateMask();
private:
int m_levelOfDetail = 0;
- KoPattern *m_pattern = 0;
+ KoPatternSP m_pattern = 0;
qreal m_scale = 1.0;
qreal m_brightness = 0.0;
qreal m_contrast = 1.0;
bool m_invert = false;
int m_cutoffLeft = 0;
int m_cutoffRight = 255;
int m_cutoffPolicy = 0;
KisPaintDeviceSP m_mask;
QRect m_maskBounds;
};
typedef QSharedPointer<KisTextureMaskInfo> KisTextureMaskInfoSP;
struct KisTextureMaskInfoCache
{
static KisTextureMaskInfoCache *instance();
KisTextureMaskInfoSP fetchCachedTextureInfo(KisTextureMaskInfoSP info);
private:
QMutex m_mutex;
QSharedPointer<KisTextureMaskInfo> m_lodInfo;
QSharedPointer<KisTextureMaskInfo> m_mainInfo;
};
#endif // KISTEXTUREMASKINFO_H
diff --git a/plugins/paintops/libpaintop/forms/wdgclipboardbrush.ui b/plugins/paintops/libpaintop/forms/wdgclipboardbrush.ui
index eebf22f2a3..69de1c94c9 100644
--- a/plugins/paintops/libpaintop/forms/wdgclipboardbrush.ui
+++ b/plugins/paintops/libpaintop/forms/wdgclipboardbrush.ui
@@ -1,209 +1,209 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KisWdgClipboardBrush</class>
<widget class="QDialog" name="KisWdgClipboardBrush">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>454</width>
<height>251</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>15</number>
</property>
<property name="topMargin">
<number>15</number>
</property>
<property name="rightMargin">
<number>15</number>
</property>
<property name="bottomMargin">
<number>15</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="preview">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>110</width>
<height>110</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>2</number>
</property>
<property name="midLineWidth">
<number>0</number>
</property>
<property name="text">
<string/>
</property>
</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>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLineEdit" name="nameEdit"/>
</item>
<item row="1" column="1">
- <widget class="KisSpacingSelectionWidget" name="spacingWidget"/>
+ <widget class="KisSpacingSelectionWidget" name="spacingWidget" native="true"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="spacingLbl">
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Spacing:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="colorAsmask">
<property name="text">
<string>Create mask from color</string>
</property>
</widget>
</item>
</layout>
</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>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<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="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisSpacingSelectionWidget</class>
<extends>QWidget</extends>
<header>kis_spacing_selection_widget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>KisWdgClipboardBrush</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>KisWdgClipboardBrush</receiver>
<slot>reject()</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>
</ui>
diff --git a/plugins/paintops/libpaintop/forms/wdgcustombrush.ui b/plugins/paintops/libpaintop/forms/wdgcustombrush.ui
index 384c4ea30b..6d04007636 100644
--- a/plugins/paintops/libpaintop/forms/wdgcustombrush.ui
+++ b/plugins/paintops/libpaintop/forms/wdgcustombrush.ui
@@ -1,318 +1,318 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KisWdgCustomBrush</class>
<widget class="QDialog" name="KisWdgCustomBrush">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>462</width>
<height>311</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>15</number>
</property>
<property name="topMargin">
<number>15</number>
</property>
<property name="rightMargin">
<number>15</number>
</property>
<property name="bottomMargin">
<number>15</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="preview">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>110</width>
<height>110</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="text">
<string/>
</property>
</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>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLineEdit" name="nameLineEdit"/>
</item>
<item row="1" column="1">
- <widget class="KisSpacingSelectionWidget" name="spacingWidget"/>
+ <widget class="KisSpacingSelectionWidget" name="spacingWidget" native="true"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="spacingLbl">
<property name="text">
<string>Spacing:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="brushNameLbl">
<property name="text">
<string>Name:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="colorAsMask">
<property name="text">
<string>Create mask from color</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="minimumSize">
<size>
<width>0</width>
<height>110</height>
</size>
</property>
<property name="title">
<string>Brush Style</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<widget class="QWidget" name="gridLayoutWidget_2">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>301</width>
<height>71</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="textLabel2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Style:</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="brushStyle">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Regular</string>
</property>
</item>
<item>
<property name="text">
<string>Animated</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="textLabel3">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Selection mode:</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="comboBox2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>2</number>
</property>
<item>
<property name="text">
<string>Constant</string>
</property>
</item>
<item>
<property name="text">
<string>Random</string>
</property>
</item>
<item>
<property name="text">
<string>Incremental</string>
</property>
</item>
<item>
<property name="text">
<string>Pressure</string>
</property>
</item>
<item>
<property name="text">
<string>Angular</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</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>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<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="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisSpacingSelectionWidget</class>
<extends>QWidget</extends>
<header>kis_spacing_selection_widget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>KisWdgCustomBrush</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>KisWdgCustomBrush</receiver>
<slot>reject()</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>
</ui>
diff --git a/plugins/paintops/libpaintop/kis_auto_brush_widget.cpp b/plugins/paintops/libpaintop/kis_auto_brush_widget.cpp
index 2655434f52..92352acf83 100644
--- a/plugins/paintops/libpaintop/kis_auto_brush_widget.cpp
+++ b/plugins/paintops/libpaintop/kis_auto_brush_widget.cpp
@@ -1,299 +1,299 @@
/*
* Copyright (c) 2004,2007,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.
*/
#include <compositeops/KoVcMultiArchBuildSupport.h> //MSVC requires that Vc come first
#include "kis_auto_brush_widget.h"
#include <kconfig.h>
#include <ksharedconfig.h>
#include <kconfiggroup.h>
#include <math.h>
#include <kis_debug.h>
#include <QSpinBox>
#include <QToolButton>
#include <QImage>
#include <QComboBox>
#include <QLabel>
#include <QPixmap>
#include <QResizeEvent>
#include <kis_fixed_paint_device.h>
#include <kis_mask_generator.h>
#include <kis_slider_spin_box.h>
#include "kis_signals_blocker.h"
#include "kis_signal_compressor.h"
#include "kis_aspect_ratio_locker.h"
#define showSlider(input, step) input->setRange(input->minimum(), input->maximum(), step)
#include <kis_cubic_curve.h>
KisAutoBrushWidget::KisAutoBrushWidget(QWidget *parent, const char* name)
: KisWdgAutoBrush(parent, name)
, m_autoBrush(0)
, m_updateCompressor(new KisSignalCompressor(100, KisSignalCompressor::FIRST_ACTIVE))
, m_fadeAspectLocker(new KisAspectRatioLocker())
{
connect(m_updateCompressor.data(), SIGNAL(timeout()), SLOT(paramChanged()));
connect((QObject*)comboBoxShape, SIGNAL(activated(int)), m_updateCompressor.data(), SLOT(start()));
inputRadius->setRange(0.01, KSharedConfig::openConfig()->group("").readEntry("maximumBrushSize", 1000), 2);
inputRadius->setExponentRatio(3.0);
inputRadius->setSingleStep(1);
inputRadius->setValue(5);
inputRadius->setSuffix(i18n(" px"));
inputRadius->setBlockUpdateSignalOnDrag(true);
connect(inputRadius, SIGNAL(valueChanged(qreal)), m_updateCompressor.data(), SLOT(start()));
inputRatio->setRange(0.0, 1.0, 2);
inputRatio->setSingleStep(0.1);
inputRatio->setValue(1.0);
inputRatio->setBlockUpdateSignalOnDrag(true);
connect(inputRatio, SIGNAL(valueChanged(qreal)), m_updateCompressor.data(), SLOT(start()));
inputHFade->setRange(0.0, 1.0, 2);
inputHFade->setSingleStep(0.1);
inputHFade->setValue(0.5);
inputVFade->setRange(0.0, 1.0, 2);
inputVFade->setSingleStep(0.1);
inputVFade->setValue(0.5);
aspectButton->setKeepAspectRatio(true);
m_fadeAspectLocker->connectSpinBoxes(inputHFade, inputVFade, aspectButton);
m_fadeAspectLocker->setBlockUpdateSignalOnDrag(true);
connect(m_fadeAspectLocker.data(), SIGNAL(sliderValueChanged()), m_updateCompressor.data(), SLOT(start()));
connect(m_fadeAspectLocker.data(), SIGNAL(aspectButtonChanged()), m_updateCompressor.data(), SLOT(start()));
inputSpikes->setRange(2, 20);
inputSpikes->setValue(2);
inputSpikes->setBlockUpdateSignalOnDrag(true);
connect(inputSpikes, SIGNAL(valueChanged(int)), m_updateCompressor.data(), SLOT(start()));
inputRandomness->setRange(0, 100);
inputRandomness->setValue(0);
inputRandomness->setBlockUpdateSignalOnDrag(true);
connect(inputRandomness, SIGNAL(valueChanged(qreal)), m_updateCompressor.data(), SLOT(start()));
inputAngle->setRange(0, 360);
inputAngle->setSuffix(QChar(Qt::Key_degree));
inputAngle->setValue(0);
inputAngle->setBlockUpdateSignalOnDrag(true);
connect(inputAngle, SIGNAL(valueChanged(int)), m_updateCompressor.data(), SLOT(start()));
connect(spacingWidget, SIGNAL(sigSpacingChanged()), m_updateCompressor.data(), SLOT(start()));
density->setRange(0, 100, 0);
density->setSingleStep(1);
density->setValue(100);
density->setSuffix(i18n("%"));
density->setBlockUpdateSignalOnDrag(true);
connect(density, SIGNAL(valueChanged(qreal)), m_updateCompressor.data(), SLOT(start()));
KisCubicCurve topLeftBottomRightLinearCurve;
topLeftBottomRightLinearCurve.setPoint(0, QPointF(0.0, 1.0));
topLeftBottomRightLinearCurve.setPoint(1, QPointF(1.0, 0.0));
bool blockedBefore = softnessCurve->blockSignals(true);
softnessCurve->setCurve(topLeftBottomRightLinearCurve);
softnessCurve->blockSignals(blockedBefore);
connect(softnessCurve, SIGNAL(modified()), m_updateCompressor.data(), SLOT(start()));
m_brush = QImage(1, 1, QImage::Format_RGB32);
connect(brushPreview, SIGNAL(clicked()), m_updateCompressor.data(), SLOT(start()));
QList<KoID> ids = KisMaskGenerator::maskGeneratorIds();
for (int i = 0; i < ids.size(); i++) {
comboBoxMaskType->insertItem(i, ids[i].name());
}
connect(comboBoxMaskType, SIGNAL(activated(int)), m_updateCompressor.data(), SLOT(start()));
connect(comboBoxMaskType, SIGNAL(currentIndexChanged(int)), SLOT(setStackedWidget(int)));
setStackedWidget(comboBoxMaskType->currentIndex());
brushPreview->setIconSize(QSize(100, 100));
connect(btnAntialiasing, SIGNAL(toggled(bool)), m_updateCompressor.data(), SLOT(start()));
m_updateCompressor->start();
}
KisAutoBrushWidget::~KisAutoBrushWidget()
{
}
void KisAutoBrushWidget::resizeEvent(QResizeEvent *)
{
brushPreview->setMinimumHeight(brushPreview->width()); // dirty hack !
brushPreview->setMaximumHeight(brushPreview->width()); // dirty hack !
}
void KisAutoBrushWidget::activate()
{
m_updateCompressor->start();
}
void KisAutoBrushWidget::paramChanged()
{
KisMaskGenerator* kas;
bool antialiasEdges = btnAntialiasing->isChecked();
if (comboBoxMaskType->currentIndex() == 2) { // gaussian brush
if (comboBoxShape->currentIndex() == 0) {
kas = new KisGaussCircleMaskGenerator(inputRadius->value(), inputRatio->value(), inputHFade->value(), inputVFade->value(), inputSpikes->value(), antialiasEdges);
}
else {
kas = new KisGaussRectangleMaskGenerator(inputRadius->value(), inputRatio->value(), inputHFade->value(), inputVFade->value(), inputSpikes->value(), antialiasEdges);
}
}
else if (comboBoxMaskType->currentIndex() == 1) { // soft brush
if (comboBoxShape->currentIndex() == 0) {
kas = new KisCurveCircleMaskGenerator(inputRadius->value(), inputRatio->value(), inputHFade->value(), inputVFade->value(), inputSpikes->value(), softnessCurve->curve(), antialiasEdges);
}
else {
kas = new KisCurveRectangleMaskGenerator(inputRadius->value(), inputRatio->value(), inputHFade->value(), inputVFade->value(), inputSpikes->value(), softnessCurve->curve(), antialiasEdges);
}
}
else {// default == 0 or any other
if (comboBoxShape->currentIndex() == 0) { // use index compare instead of comparing a translatable string
kas = new KisCircleMaskGenerator(inputRadius->value(), inputRatio->value(), inputHFade->value(), inputVFade->value(), inputSpikes->value(), antialiasEdges);
}
else {
kas = new KisRectangleMaskGenerator(inputRadius->value(), inputRatio->value(), inputHFade->value(), inputVFade->value(), inputSpikes->value(), antialiasEdges);
}
}
Q_CHECK_PTR(kas);
- m_autoBrush = new KisAutoBrush(kas, inputAngle->value() / 180.0 * M_PI, inputRandomness->value() / 100.0, density->value() / 100.0);
+ m_autoBrush = KisBrushSP(new KisAutoBrush(kas, inputAngle->value() / 180.0 * M_PI, inputRandomness->value() / 100.0, density->value() / 100.0));
m_autoBrush->setSpacing(spacingWidget->spacing());
m_autoBrush->setAutoSpacing(spacingWidget->autoSpacingActive(), spacingWidget->autoSpacingCoeff());
m_brush = m_autoBrush->image();
drawBrushPreviewArea();
emit sigBrushChanged();
}
void KisAutoBrushWidget::drawBrushPreviewArea() {
QImage pi(m_brush);
double coeff = 1.0;
int bPw = brushPreview->width() - 3;
if (pi.width() > bPw) {
coeff = bPw / (double)pi.width();
}
int bPh = brushPreview->height() - 3;
if (pi.height() > coeff * bPh) {
coeff = bPh / (double)pi.height();
}
if (coeff < 1.0) {
pi = pi.scaled((int)(coeff * pi.width()) , (int)(coeff * pi.height()), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
QPixmap p = QPixmap::fromImage(pi);
brushPreview->setIcon(QIcon(p));
}
void KisAutoBrushWidget::setStackedWidget(int index)
{
if (index == 1) {
stackedWidget->setCurrentIndex(1);
}
else {
stackedWidget->setCurrentIndex(0);
}
}
KisBrushSP KisAutoBrushWidget::brush()
{
return m_autoBrush;
}
void KisAutoBrushWidget::setBrush(KisBrushSP brush)
{
m_autoBrush = brush;
m_brush = brush->image();
// XXX: lock, set and unlock the widgets.
KisAutoBrush* aBrush = dynamic_cast<KisAutoBrush*>(brush.data());
KisSignalsBlocker b1(comboBoxShape, comboBoxMaskType);
KisSignalsBlocker b2(inputRadius, inputRatio, inputHFade, inputVFade, inputAngle, inputSpikes);
KisSignalsBlocker b3(spacingWidget, inputRandomness, density, softnessCurve, btnAntialiasing);
if (aBrush->maskGenerator()->type() == KisMaskGenerator::CIRCLE) {
comboBoxShape->setCurrentIndex(0);
}
else if (aBrush->maskGenerator()->type() == KisMaskGenerator::RECTANGLE) {
comboBoxShape->setCurrentIndex(1);
}
else {
comboBoxShape->setCurrentIndex(2);
}
const int mastTypeIndex = comboBoxMaskType->findText(aBrush->maskGenerator()->name());
comboBoxMaskType->setCurrentIndex(mastTypeIndex);
setStackedWidget(mastTypeIndex); // adjusting manually because the signals are blocked
inputRadius->setValue(aBrush->maskGenerator()->diameter());
inputRatio->setValue(aBrush->maskGenerator()->ratio());
inputHFade->setValue(aBrush->maskGenerator()->horizontalFade());
inputVFade->setValue(aBrush->maskGenerator()->verticalFade());
inputAngle->setValue(aBrush->angle() * 180 / M_PI);
inputSpikes->setValue(aBrush->maskGenerator()->spikes());
spacingWidget->setSpacing(aBrush->autoSpacingActive(),
aBrush->autoSpacingActive() ?
aBrush->autoSpacingCoeff() : aBrush->spacing());
inputRandomness->setValue(aBrush->randomness() * 100);
density->setValue(aBrush->density() * 100);
if (!aBrush->maskGenerator()->curveString().isEmpty()) {
KisCubicCurve curve;
curve.fromString(aBrush->maskGenerator()->curveString());
softnessCurve->setCurve(curve);
}
btnAntialiasing->setChecked(aBrush->maskGenerator()->antialiasEdges());
drawBrushPreviewArea(); // sync up what the brush preview area looks like
}
void KisAutoBrushWidget::setBrushSize(qreal dxPixels, qreal dyPixels)
{
Q_UNUSED(dyPixels);
qreal newWidth = inputRadius->value() + dxPixels;
newWidth = qMax(newWidth, qreal(0.1));
inputRadius->setValue(newWidth);
}
QSizeF KisAutoBrushWidget::brushSize() const
{
return QSizeF(inputRadius->value(), inputRadius->value() * inputRatio->value());
}
#include "moc_kis_auto_brush_widget.cpp"
diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp
index eca8aff271..90b50ec5d5 100644
--- a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp
+++ b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp
@@ -1,185 +1,203 @@
/*
* 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.
*/
#include "kis_brush_based_paintop.h"
#include "kis_properties_configuration.h"
#include <brushengine/kis_paintop_settings.h>
#include "kis_brush_option.h"
#include <kis_pressure_spacing_option.h>
#include <kis_pressure_rate_option.h>
#include "kis_painter.h"
#include <kis_lod_transform.h>
#include "kis_paintop_utils.h"
#include "kis_paintop_plugin_utils.h"
#include <QGlobalStatic>
#include <QImage>
#include <QPainter>
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
Q_GLOBAL_STATIC(TextBrushInitializationWorkaround, s_instance)
TextBrushInitializationWorkaround *TextBrushInitializationWorkaround::instance()
{
return s_instance;
}
-void TextBrushInitializationWorkaround::preinitialize(KisPropertiesConfigurationSP settings)
+void TextBrushInitializationWorkaround::preinitialize(KisPaintOpSettingsSP settings)
{
if (KisBrushOptionProperties::isTextBrush(settings.data())) {
KisBrushOptionProperties brushOption;
- brushOption.readOptionSetting(settings);
+ brushOption.readOptionSetting(settings, settings->resourcesInterface());
m_brush = brushOption.brush();
m_settings = settings;
}
else {
m_brush = 0;
m_settings = 0;
}
}
KisBrushSP TextBrushInitializationWorkaround::tryGetBrush(const KisPropertiesConfigurationSP settings)
{
return (settings && settings == m_settings ? m_brush : 0);
}
TextBrushInitializationWorkaround::TextBrushInitializationWorkaround()
: m_settings(0)
{}
TextBrushInitializationWorkaround::~TextBrushInitializationWorkaround()
{}
-
-
void KisBrushBasedPaintOp::preinitializeOpStatically(KisPaintOpSettingsSP settings)
{
TextBrushInitializationWorkaround::instance()->preinitialize(settings);
}
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
-KisBrushBasedPaintOp::KisBrushBasedPaintOp(const KisPropertiesConfigurationSP settings, KisPainter* painter)
+KisBrushBasedPaintOp::KisBrushBasedPaintOp(const KisPaintOpSettingsSP settings, KisPainter* painter)
: KisPaintOp(painter),
m_textureProperties(painter->device()->defaultBounds()->currentLevelOfDetail())
{
Q_ASSERT(settings);
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
m_brush =
TextBrushInitializationWorkaround::instance()->tryGetBrush(settings);
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
if (!m_brush) {
KisBrushOptionProperties brushOption;
- brushOption.readOptionSetting(settings);
+ brushOption.readOptionSetting(settings, settings->resourcesInterface());
m_brush = brushOption.brush();
}
m_brush->notifyStrokeStarted();
m_precisionOption.readOptionSetting(settings);
m_dabCache = new KisDabCache(m_brush);
m_dabCache->setPrecisionOption(&m_precisionOption);
m_mirrorOption.readOptionSetting(settings);
m_dabCache->setMirrorPostprocessing(&m_mirrorOption);
- m_textureProperties.fillProperties(settings);
+ m_textureProperties.fillProperties(settings, settings->resourcesInterface());
m_dabCache->setTexturePostprocessing(&m_textureProperties);
m_precisionOption.setHasImprecisePositionOptions(
m_precisionOption.hasImprecisePositionOptions() |
m_mirrorOption.isChecked() | m_textureProperties.m_enabled);
}
KisBrushBasedPaintOp::~KisBrushBasedPaintOp()
{
delete m_dabCache;
}
+QList<KoResourceSP> KisBrushBasedPaintOp::prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface)
+{
+ QList<KoResourceSP> resources;
+
+ KisBrushOptionProperties brushOption;
+ resources << brushOption.prepareLinkedResources(settings, resourcesInterface);
+
+ return resources;
+}
+
+QList<KoResourceSP> KisBrushBasedPaintOp::prepareEmbeddedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface)
+{
+ QList<KoResourceSP> resources;
+
+ KisTextureProperties textureProperties(0);
+ resources << textureProperties.prepareEmbeddedResources(settings, resourcesInterface);
+
+ return resources;
+}
+
bool KisBrushBasedPaintOp::checkSizeTooSmall(qreal scale)
{
scale *= m_brush->scale();
return KisPaintOpUtils::checkSizeTooSmall(scale, m_brush->width(), m_brush->height());
}
KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale) const
{
// we parse dab rotation separately, so don't count it
QSizeF metric = m_brush->characteristicSize(KisDabShape(scale, 1.0, 0));
return effectiveSpacing(metric.width(), metric.height(), 1.0, false, 0.0, false);
}
KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPaintInformation &pi) const
{
return effectiveSpacing(scale, rotation, nullptr, nullptr, pi);
}
KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPressureSpacingOption &spacingOption, const KisPaintInformation &pi) const
{
return effectiveSpacing(scale, rotation, nullptr, &spacingOption, pi);
}
KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation,
const KisAirbrushOptionProperties *airbrushOption,
const KisPressureSpacingOption *spacingOption,
const KisPaintInformation &pi) const
{
bool isotropicSpacing = spacingOption && spacingOption->isotropicSpacing();
MirrorProperties prop = m_mirrorOption.apply(pi);
const bool implicitFlipped = prop.horizontalMirror != prop.verticalMirror;
// we parse dab rotation separately, so don't count it
QSizeF metric = m_brush->characteristicSize(KisDabShape(scale, 1.0, 0));
return KisPaintOpPluginUtils::effectiveSpacing(metric.width(), metric.height(),
isotropicSpacing, rotation, implicitFlipped,
m_brush->spacing(),
m_brush->autoSpacingActive(),
m_brush->autoSpacingCoeff(),
KisLodTransform::lodToScale(painter()->device()),
airbrushOption, spacingOption,
pi);
}
KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal dabWidth, qreal dabHeight,
qreal extraScale,
bool isotropicSpacing, qreal rotation,
bool axesFlipped) const
{
return KisPaintOpUtils::effectiveSpacing(dabWidth, dabHeight,
extraScale,
true,
isotropicSpacing,
rotation,
axesFlipped,
m_brush->spacing(),
m_brush->autoSpacingActive(),
m_brush->autoSpacingCoeff(),
KisLodTransform::lodToScale(painter()->device()));
}
bool KisBrushBasedPaintOp::canPaint() const
{
return m_brush != 0;
}
diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop.h b/plugins/paintops/libpaintop/kis_brush_based_paintop.h
index c884c3be46..b27fe8199f 100644
--- a/plugins/paintops/libpaintop/kis_brush_based_paintop.h
+++ b/plugins/paintops/libpaintop/kis_brush_based_paintop.h
@@ -1,103 +1,107 @@
/*
* 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_BRUSH_BASED_PAINTOP_H
#define KIS_BRUSH_BASED_PAINTOP_H
#include "kritapaintop_export.h"
#include <brushengine/kis_paintop.h>
#include "kis_dab_cache.h"
#include "kis_brush.h"
#include "kis_texture_option.h"
#include "kis_precision_option.h"
#include "kis_airbrush_option_widget.h"
#include "kis_pressure_mirror_option.h"
#include <kis_threaded_text_rendering_workaround.h>
class KisPropertiesConfiguration;
class KisPressureSpacingOption;
class KisPressureRateOption;
class KisDabCache;
+class KisResourcesInterface;
/// Internal
class TextBrushInitializationWorkaround
{
public:
TextBrushInitializationWorkaround();
~TextBrushInitializationWorkaround();
static TextBrushInitializationWorkaround* instance();
- void preinitialize(KisPropertiesConfigurationSP settings);
+ void preinitialize(KisPaintOpSettingsSP settings);
KisBrushSP tryGetBrush(const KisPropertiesConfigurationSP settings);
private:
KisBrushSP m_brush;
KisPropertiesConfigurationSP m_settings;
};
/**
* This is a base class for paintops that use a KisBrush or derived
* brush to paint with. This is mainly important for the spacing
* generation.
*/
class PAINTOP_EXPORT KisBrushBasedPaintOp : public KisPaintOp
{
public:
- KisBrushBasedPaintOp(const KisPropertiesConfigurationSP settings, KisPainter* painter);
+ KisBrushBasedPaintOp(const KisPaintOpSettingsSP settings, KisPainter* painter);
~KisBrushBasedPaintOp() override;
bool checkSizeTooSmall(qreal scale);
KisSpacingInformation effectiveSpacing(qreal scale) const;
KisSpacingInformation effectiveSpacing(qreal scale, qreal rotation, const KisPaintInformation &pi) const;
KisSpacingInformation effectiveSpacing(qreal scale, qreal rotation, const KisPressureSpacingOption &spacingOption, const KisPaintInformation &pi) const;
KisSpacingInformation effectiveSpacing(qreal scale,
qreal rotation,
const KisAirbrushOptionProperties *airbrushOption,
const KisPressureSpacingOption *spacingOption,
const KisPaintInformation &pi) const;
///Reimplemented, false if brush is 0
bool canPaint() const override;
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
typedef int needs_preinitialization;
static void preinitializeOpStatically(KisPaintOpSettingsSP settings);
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
+ static QList<KoResourceSP> prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface);
+ static QList<KoResourceSP> prepareEmbeddedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface);
+
private:
KisSpacingInformation effectiveSpacing(qreal dabWidth, qreal dabHeight, qreal extraScale, bool isotropicSpacing, qreal rotation, bool axesFlipped) const;
protected: // XXX: make private!
KisDabCache *m_dabCache;
KisBrushSP m_brush;
private:
KisTextureProperties m_textureProperties;
protected:
KisPressureMirrorOption m_mirrorOption;
KisPrecisionOption m_precisionOption;
};
#endif
diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp b/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp
index 0f34ca48df..0e9c6097c2 100644
--- a/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp
+++ b/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp
@@ -1,340 +1,338 @@
/*
* Copyright (c) 2010 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_brush_based_paintop_settings.h"
#include <kis_paint_action_type_option.h>
#include <kis_airbrush_option_widget.h>
#include "kis_brush_based_paintop_options_widget.h"
#include <kis_boundary.h>
-#include "kis_brush_server.h"
+#include "KisBrushServerProvider.h"
#include <QLineF>
#include "kis_signals_blocker.h"
#include "kis_brush_option.h"
#include <KisPaintopSettingsIds.h>
-#include <brushengine/kis_paintop_preset.h>
+#include <kis_paintop_preset.h>
+#include <KisGlobalResourcesInterface.h>
struct BrushReader {
BrushReader(const KisBrushBasedPaintOpSettings *parent)
: m_parent(parent)
{
- m_option.readOptionSetting(m_parent);
+ m_option.readOptionSetting(m_parent, parent->resourcesInterface());
}
KisBrushSP brush() {
return m_option.brush();
}
const KisBrushBasedPaintOpSettings *m_parent;
KisBrushOptionProperties m_option;
};
struct BrushWriter {
BrushWriter(KisBrushBasedPaintOpSettings *parent)
: m_parent(parent)
{
- m_option.readOptionSetting(m_parent);
+ m_option.readOptionSetting(m_parent, parent->resourcesInterface());
}
~BrushWriter() {
m_option.writeOptionSetting(m_parent);
}
KisBrushSP brush() {
return m_option.brush();
}
KisBrushBasedPaintOpSettings *m_parent;
KisBrushOptionProperties m_option;
};
-KisBrushBasedPaintOpSettings::KisBrushBasedPaintOpSettings()
+KisBrushBasedPaintOpSettings::KisBrushBasedPaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
: KisOutlineGenerationPolicy<KisPaintOpSettings>(KisCurrentOutlineFetcher::SIZE_OPTION |
- KisCurrentOutlineFetcher::ROTATION_OPTION |
- KisCurrentOutlineFetcher::MIRROR_OPTION |
- KisCurrentOutlineFetcher::SHARPNESS_OPTION)
+ KisCurrentOutlineFetcher::ROTATION_OPTION |
+ KisCurrentOutlineFetcher::MIRROR_OPTION |
+ KisCurrentOutlineFetcher::SHARPNESS_OPTION,
+ resourcesInterface)
{
}
bool KisBrushBasedPaintOpSettings::paintIncremental()
{
if (hasProperty("PaintOpAction")) {
return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP;
}
return true;
}
KisPaintOpSettingsSP KisBrushBasedPaintOpSettings::clone() const
{
KisPaintOpSettingsSP _settings = KisOutlineGenerationPolicy<KisPaintOpSettings>::clone();
KisBrushBasedPaintOpSettingsSP settings = dynamic_cast<KisBrushBasedPaintOpSettings*>(_settings.data());
settings->m_savedBrush = 0;
return settings;
}
KisBrushSP KisBrushBasedPaintOpSettings::brush() const
{
KisBrushSP brush = m_savedBrush;
if (!brush) {
BrushReader w(this);
brush = w.brush();
m_savedBrush = brush;
}
return brush;
}
QPainterPath KisBrushBasedPaintOpSettings::brushOutlineImpl(const KisPaintInformation &info,
const OutlineMode &mode,
qreal alignForZoom,
qreal additionalScale)
{
QPainterPath path;
if (mode.isVisible) {
KisBrushSP brush = this->brush();
if (!brush) return path;
qreal finalScale = brush->scale() * additionalScale;
QPainterPath realOutline = brush->outline();
if (mode.forceCircle) {
QPainterPath ellipse;
ellipse.addEllipse(realOutline.boundingRect());
realOutline = ellipse;
}
path = outlineFetcher()->fetchOutline(info, this, realOutline, mode, alignForZoom, finalScale, brush->angle());
if (mode.showTiltDecoration) {
const QPainterPath tiltLine = makeTiltIndicator(info,
realOutline.boundingRect().center(),
realOutline.boundingRect().width() * 0.5,
3.0);
path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, finalScale, 0.0, true, realOutline.boundingRect().center().x(), realOutline.boundingRect().center().y()));
}
}
return path;
}
QPainterPath KisBrushBasedPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom)
{
return brushOutlineImpl(info, mode, alignForZoom, 1.0);
}
bool KisBrushBasedPaintOpSettings::isValid() const
{
QStringList files = getStringList(KisPaintOpUtils::RequiredBrushFilesListTag);
files << getString(KisPaintOpUtils::RequiredBrushFileTag);
Q_FOREACH (const QString &file, files) {
if (!file.isEmpty()) {
- KisBrushSP brush = KisBrushServer::instance()->brushServer()->resourceByFilename(file);
+ KisBrushSP brush = resourcesInterface()->source<KisBrush>(ResourceType::Brushes).resourceForFilename(file);
if (!brush) {
return false;
}
}
}
return true;
}
-bool KisBrushBasedPaintOpSettings::isLoadable()
-{
- return (KisBrushServer::instance()->brushServer()->resources().count() > 0);
-}
void KisBrushBasedPaintOpSettings::setAngle(qreal value)
{
BrushWriter w(this);
if (!w.brush()) return;
w.brush()->setAngle(value);
}
qreal KisBrushBasedPaintOpSettings::angle()
{
return this->brush()->angle();
}
void KisBrushBasedPaintOpSettings::setSpacing(qreal value)
{
BrushWriter w(this);
if (!w.brush()) return;
w.brush()->setSpacing(value);
}
qreal KisBrushBasedPaintOpSettings::spacing()
{
return this->brush()->spacing();
}
void KisBrushBasedPaintOpSettings::setAutoSpacing(bool active, qreal coeff)
{
BrushWriter w(this);
if (!w.brush()) return;
w.brush()->setAutoSpacing(active, coeff);
}
bool KisBrushBasedPaintOpSettings::autoSpacingActive()
{
return this->brush()->autoSpacingActive();
}
qreal KisBrushBasedPaintOpSettings::autoSpacingCoeff()
{
return this->brush()->autoSpacingCoeff();
}
void KisBrushBasedPaintOpSettings::setPaintOpSize(qreal value)
{
BrushWriter w(this);
if (!w.brush()) return;
w.brush()->setUserEffectiveSize(value);
}
qreal KisBrushBasedPaintOpSettings::paintOpSize() const
{
return this->brush()->userEffectiveSize();
}
#include <brushengine/kis_slider_based_paintop_property.h>
#include "kis_paintop_preset.h"
#include "kis_paintop_settings_update_proxy.h"
QList<KisUniformPaintOpPropertySP> KisBrushBasedPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings)
{
QList<KisUniformPaintOpPropertySP> props =
listWeakToStrong(m_uniformProperties);
if (props.isEmpty()) {
{
KisIntSliderBasedPaintOpPropertyCallback *prop =
new KisIntSliderBasedPaintOpPropertyCallback(
KisIntSliderBasedPaintOpPropertyCallback::Int,
"angle",
"Angle",
settings, 0);
prop->setRange(0, 360);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
KisBrushBasedPaintOpSettings *s =
dynamic_cast<KisBrushBasedPaintOpSettings*>(prop->settings().data());
const qreal angleResult = kisRadiansToDegrees(s->angle());
prop->setValue(angleResult);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
KisBrushBasedPaintOpSettings *s =
dynamic_cast<KisBrushBasedPaintOpSettings*>(prop->settings().data());
s->setAngle(kisDegreesToRadians(prop->value().toReal()));
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisUniformPaintOpPropertyCallback *prop =
new KisUniformPaintOpPropertyCallback(
KisUniformPaintOpPropertyCallback::Bool,
"auto_spacing",
"Auto Spacing",
settings, 0);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
KisBrushBasedPaintOpSettings *s =
dynamic_cast<KisBrushBasedPaintOpSettings*>(prop->settings().data());
prop->setValue(s->autoSpacingActive());
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
KisBrushBasedPaintOpSettings *s =
dynamic_cast<KisBrushBasedPaintOpSettings*>(prop->settings().data());
s->setAutoSpacing(prop->value().toBool(), s->autoSpacingCoeff());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisDoubleSliderBasedPaintOpPropertyCallback *prop =
new KisDoubleSliderBasedPaintOpPropertyCallback(
KisDoubleSliderBasedPaintOpPropertyCallback::Double,
"spacing",
"Spacing",
settings, 0);
prop->setRange(0.01, 10);
prop->setSingleStep(0.01);
prop->setExponentRatio(3.0);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
KisBrushBasedPaintOpSettings *s =
dynamic_cast<KisBrushBasedPaintOpSettings*>(prop->settings().data());
if (s) {
const qreal value = s->autoSpacingActive() ?
s->autoSpacingCoeff() : s->spacing();
prop->setValue(value);
}
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
KisBrushBasedPaintOpSettings *s =
dynamic_cast<KisBrushBasedPaintOpSettings*>(prop->settings().data());
if (s) {
if (s->autoSpacingActive()) {
s->setAutoSpacing(true, prop->value().toReal());
} else {
s->setSpacing(prop->value().toReal());
}
}
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
}
return KisPaintOpSettings::uniformProperties(settings) + props;
}
void KisBrushBasedPaintOpSettings::onPropertyChanged()
{
m_savedBrush.clear();
KisOutlineGenerationPolicy<KisPaintOpSettings>::onPropertyChanged();
}
diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.h b/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.h
index a5b4e4d79f..3d96ac526c 100644
--- a/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.h
+++ b/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.h
@@ -1,84 +1,81 @@
/*
* Copyright (c) 2010 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_BRUSH_BASED_PAINTOP_SETTINGS_H
#define KIS_BRUSH_BASED_PAINTOP_SETTINGS_H
#include <brushengine/kis_paintop_settings.h>
#include <kritapaintop_export.h>
#include <kis_outline_generation_policy.h>
#include <kis_brush.h>
#include <kis_shared.h>
#include <kis_shared_ptr.h>
class PAINTOP_EXPORT KisBrushBasedPaintOpSettings : public KisOutlineGenerationPolicy<KisPaintOpSettings>
{
public:
- KisBrushBasedPaintOpSettings();
+ KisBrushBasedPaintOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisBrushBasedPaintOpSettings() override {}
///Reimplemented
bool paintIncremental() override;
using KisPaintOpSettings::brushOutline;
QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override;
///Reimplemented
bool isValid() const override;
- ///Reimplemented
- bool isLoadable() override;
-
KisBrushSP brush() const;
KisPaintOpSettingsSP clone() const override;
void setAngle(qreal value);
qreal angle();
void setSpacing(qreal spacing);
qreal spacing();
void setAutoSpacing(bool active, qreal coeff);
bool autoSpacingActive();
qreal autoSpacingCoeff();
void setPaintOpSize(qreal value) override;
qreal paintOpSize() const override;
QList<KisUniformPaintOpPropertySP> uniformProperties(KisPaintOpSettingsSP settings) override;
protected:
void onPropertyChanged() override;
QPainterPath brushOutlineImpl(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom, qreal additionalScale);
mutable KisBrushSP m_savedBrush;
QList<KisUniformPaintOpPropertyWSP> m_uniformProperties;
private:
Q_DISABLE_COPY(KisBrushBasedPaintOpSettings)
};
class KisBrushBasedPaintOpSettings;
typedef KisPinnedSharedPtr<KisBrushBasedPaintOpSettings> KisBrushBasedPaintOpSettingsSP;
#endif // KIS_BRUSH_BASED_PAINTOP_SETTINGS_H
diff --git a/plugins/paintops/libpaintop/kis_brush_chooser.cpp b/plugins/paintops/libpaintop/kis_brush_chooser.cpp
index dc9662b966..e6ec79984a 100644
--- a/plugins/paintops/libpaintop/kis_brush_chooser.cpp
+++ b/plugins/paintops/libpaintop/kis_brush_chooser.cpp
@@ -1,421 +1,418 @@
/*
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2009 Sven Langkamp <sven.langkamp@gmail.com>
* Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* 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 "kis_brush_chooser.h"
#include <QLabel>
#include <QLayout>
#include <QCheckBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QPainter>
#include <QAbstractItemDelegate>
#include <klocalizedstring.h>
#include <kconfig.h>
#include <ksharedconfig.h>
#include <kconfiggroup.h>
#include <KisKineticScroller.h>
-#include <KoResourceItemView.h>
-#include <KoResourceItemChooser.h>
+#include <KisResourceItemView.h>
+#include <KisResourceItemChooser.h>
+#include <KisResourceModel.h>
#include <kis_icon.h>
-#include "kis_brush_server.h"
+#include "KisBrushServerProvider.h"
#include "widgets/kis_slider_spin_box.h"
#include "widgets/kis_multipliers_double_slider_spinbox.h"
#include "kis_spacing_selection_widget.h"
#include "kis_signals_blocker.h"
#include "kis_imagepipe_brush.h"
#include "kis_custom_brush_widget.h"
#include "kis_clipboard_brush_widget.h"
#include <kis_config.h>
#include "kis_global.h"
#include "kis_gbr_brush.h"
#include "kis_debug.h"
#include "kis_image.h"
+#include <KisGlobalResourcesInterface.h>
/// The resource item delegate for rendering the resource preview
class KisBrushDelegate : public QAbstractItemDelegate
{
public:
KisBrushDelegate(QObject * parent = 0) : QAbstractItemDelegate(parent) {}
~KisBrushDelegate() 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 KisBrushDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
if (! index.isValid())
return;
- KisBrush *brush = static_cast<KisBrush*>(index.internalPointer());
+ QImage thumbnail = index.data(Qt::UserRole + KisResourceModel::Thumbnail).value<QImage>();
QRect itemRect = option.rect;
- QImage thumbnail = brush->image();
if (thumbnail.height() > itemRect.height() || thumbnail.width() > itemRect.width()) {
thumbnail = thumbnail.scaled(itemRect.size() , Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
painter->save();
int dx = (itemRect.width() - thumbnail.width()) / 2;
int dy = (itemRect.height() - thumbnail.height()) / 2;
painter->drawImage(itemRect.x() + dx, itemRect.y() + dy, thumbnail);
if (option.state & QStyle::State_Selected) {
painter->setPen(QPen(option.palette.highlight(), 2.0));
painter->drawRect(option.rect);
painter->setCompositionMode(QPainter::CompositionMode_HardLight);
painter->setOpacity(0.65);
painter->fillRect(option.rect, option.palette.highlight());
}
painter->restore();
}
KisPredefinedBrushChooser::KisPredefinedBrushChooser(QWidget *parent, const char *name)
: QWidget(parent),
m_stampBrushWidget(0),
m_clipboardBrushWidget(0)
{
setObjectName(name);
setupUi(this);
brushSizeSpinBox->setRange(0, KSharedConfig::openConfig()->group("").readEntry("maximumBrushSize", 1000), 2);
brushSizeSpinBox->setValue(5);
brushSizeSpinBox->setExponentRatio(3.0);
brushSizeSpinBox->setSuffix(i18n(" px"));
brushSizeSpinBox->setExponentRatio(3.0);
QObject::connect(brushSizeSpinBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetItemSize(qreal)));
brushRotationSpinBox->setRange(0, 360, 0);
brushRotationSpinBox->setValue(0);
brushRotationSpinBox->setSuffix(QChar(Qt::Key_degree));
QObject::connect(brushRotationSpinBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetItemRotation(qreal)));
brushSpacingSelectionWidget->setSpacing(true, 1.0);
connect(brushSpacingSelectionWidget, SIGNAL(sigSpacingChanged()), SLOT(slotSpacingChanged()));
QObject::connect(useColorAsMaskCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetItemUseColorAsMask(bool)));
- KisBrushResourceServer* rServer = KisBrushServer::instance()->brushServer();
- QSharedPointer<KisBrushResourceServerAdapter> adapter(new KisBrushResourceServerAdapter(rServer));
-
- m_itemChooser = new KoResourceItemChooser(adapter, this);
+ m_itemChooser = new KisResourceItemChooser(ResourceType::Brushes, false, this);
m_itemChooser->setObjectName("brush_selector");
m_itemChooser->showTaggingBar(true);
- m_itemChooser->setColumnCount(10);
m_itemChooser->setRowHeight(30);
m_itemChooser->setItemDelegate(new KisBrushDelegate(this));
- m_itemChooser->setCurrentItem(0, 0);
+ m_itemChooser->setCurrentItem(0);
m_itemChooser->setSynced(true);
m_itemChooser->setMinimumWidth(100);
m_itemChooser->setMinimumHeight(150);
m_itemChooser->showButtons(false); // turn the import and delete buttons since we want control over them
addPresetButton->setIcon(KisIconUtils::loadIcon("list-add"));
deleteBrushTipButton->setIcon(KisIconUtils::loadIcon("trash-empty"));
connect(addPresetButton, SIGNAL(clicked(bool)), this, SLOT(slotImportNewBrushResource()));
connect(deleteBrushTipButton, SIGNAL(clicked(bool)), this, SLOT(slotDeleteBrushResource()));
presetsLayout->addWidget(m_itemChooser);
- connect(m_itemChooser, SIGNAL(resourceSelected(KoResource*)), this, SLOT(updateBrushTip(KoResource*)));
+ connect(m_itemChooser, SIGNAL(resourceSelected(KoResourceSP )), this, SLOT(updateBrushTip(KoResourceSP )));
stampButton->setIcon(KisIconUtils::loadIcon("list-add"));
stampButton->setToolTip(i18n("Creates a brush tip from the current image selection."
"\n If no selection is present the whole image will be used."));
clipboardButton->setIcon(KisIconUtils::loadIcon("list-add"));
clipboardButton->setToolTip(i18n("Creates a brush tip from the image in the clipboard."));
connect(stampButton, SIGNAL(clicked()), this, SLOT(slotOpenStampBrush()));
connect(clipboardButton, SIGNAL(clicked()), SLOT(slotOpenClipboardBrush()));
QGridLayout *spacingLayout = new QGridLayout();
spacingLayout->setObjectName("spacing grid layout");
resetBrushButton->setToolTip(i18n("Reloads Spacing from file\nSets Scale to 1.0\nSets Rotation to 0.0"));
connect(resetBrushButton, SIGNAL(clicked()), SLOT(slotResetBrush()));
updateBrushTip(m_itemChooser->currentResource());
}
KisPredefinedBrushChooser::~KisPredefinedBrushChooser()
{
}
void KisPredefinedBrushChooser::setBrush(KisBrushSP brush)
{
/**
* Warning: since the brushes are always cloned after loading from XML or
* fetching from the server, we cannot just ask for that brush explicitly.
* Instead, we should search for the brush with the same filename and/or name
* and load it. Please take it into account that after selecting the brush
* explicitly in the chooser, m_itemChooser->currentResource() might be
* **not** the same as the value in m_brush.
*
* Ideally, if the resource is not found on the server, we should add it, but
* it might lead to a set of weird consequences. So for now we just
* select nothing.
*/
- KisBrushResourceServer* server = KisBrushServer::instance()->brushServer();
- KoResource *resource = server->resourceByFilename(brush->shortFilename()).data();
+ KoResourceServer<KisBrush>* server = KisBrushServerProvider::instance()->brushServer();
+ KoResourceSP resource = server->resourceByFilename(brush->filename());
if (!resource) {
- resource = server->resourceByName(brush->name()).data();
+ resource = server->resourceByName(brush->name());
}
if (!resource) {
- resource = brush.data();
+ resource = brush;
}
m_itemChooser->setCurrentResource(resource);
- updateBrushTip(brush.data(), true);
+ updateBrushTip(brush, true);
}
void KisPredefinedBrushChooser::slotResetBrush()
{
/**
* The slot also resets the brush on the server
*
* TODO: technically, after we refactored all the brushes to be forked,
* we can just re-update the brush from the server without reloading.
* But it needs testing.
*/
- KisBrush *brush = dynamic_cast<KisBrush *>(m_itemChooser->currentResource());
+ KisBrushSP brush = m_itemChooser->currentResource().dynamicCast<KisBrush>();
if (brush) {
- brush->load();
+ brush->load(KisGlobalResourcesInterface::instance());
brush->setScale(1.0);
brush->setAngle(0.0);
updateBrushTip(brush);
emit sigBrushChanged();
}
}
void KisPredefinedBrushChooser::slotSetItemSize(qreal sizeValue)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_brush);
if (m_brush) {
int brushWidth = m_brush->width();
m_brush->setScale(sizeValue / qreal(brushWidth));
emit sigBrushChanged();
}
}
void KisPredefinedBrushChooser::slotSetItemRotation(qreal rotationValue)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_brush);
if (m_brush) {
m_brush->setAngle(rotationValue / 180.0 * M_PI);
emit sigBrushChanged();
}
}
void KisPredefinedBrushChooser::slotSpacingChanged()
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_brush);
if (m_brush) {
m_brush->setSpacing(brushSpacingSelectionWidget->spacing());
m_brush->setAutoSpacing(brushSpacingSelectionWidget->autoSpacingActive(), brushSpacingSelectionWidget->autoSpacingCoeff());
emit sigBrushChanged();
}
}
void KisPredefinedBrushChooser::slotSetItemUseColorAsMask(bool useColorAsMask)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_brush);
KisGbrBrush *brush = dynamic_cast<KisGbrBrush *>(m_brush.data());
if (brush) {
brush->setUseColorAsMask(useColorAsMask);
emit sigBrushChanged();
}
}
void KisPredefinedBrushChooser::slotOpenStampBrush()
{
if(!m_stampBrushWidget) {
m_stampBrushWidget = new KisCustomBrushWidget(this, i18n("Stamp"), m_image);
m_stampBrushWidget->setModal(false);
- connect(m_stampBrushWidget, SIGNAL(sigNewPredefinedBrush(KoResource*)),
- SLOT(slotNewPredefinedBrush(KoResource*)));
+ connect(m_stampBrushWidget, SIGNAL(sigNewPredefinedBrush(KoResourceSP )),
+ SLOT(slotNewPredefinedBrush(KoResourceSP )));
} else {
m_stampBrushWidget->setImage(m_image);
}
QDialog::DialogCode result = (QDialog::DialogCode)m_stampBrushWidget->exec();
if(result) {
updateBrushTip(m_itemChooser->currentResource());
}
}
void KisPredefinedBrushChooser::slotOpenClipboardBrush()
{
if(!m_clipboardBrushWidget) {
m_clipboardBrushWidget = new KisClipboardBrushWidget(this, i18n("Clipboard"), m_image);
m_clipboardBrushWidget->setModal(true);
- connect(m_clipboardBrushWidget, SIGNAL(sigNewPredefinedBrush(KoResource*)),
- SLOT(slotNewPredefinedBrush(KoResource*)));
+ connect(m_clipboardBrushWidget, SIGNAL(sigNewPredefinedBrush(KoResourceSP )),
+ SLOT(slotNewPredefinedBrush(KoResourceSP )));
}
QDialog::DialogCode result = (QDialog::DialogCode)m_clipboardBrushWidget->exec();
if(result) {
updateBrushTip(m_itemChooser->currentResource());
}
}
-void KisPredefinedBrushChooser::updateBrushTip(KoResource * resource, bool isChangingBrushPresets)
+void KisPredefinedBrushChooser::updateBrushTip(KoResourceSP resource, bool isChangingBrushPresets)
{
QString animatedBrushTipSelectionMode; // incremental, random, etc
{
- KisBrush* brush = dynamic_cast<KisBrush*>(resource);
- m_brush = brush ? brush->clone() : 0;
+ KisBrushSP brush = resource.dynamicCast<KisBrush>();
+ m_brush = brush ? brush->clone().dynamicCast<KisBrush>() : 0;
}
if (m_brush) {
brushTipNameLabel->setText(i18n(m_brush->name().toUtf8().data()));
QString brushTypeString = "";
if (m_brush->brushType() == INVALID) {
brushTypeString = i18n("Invalid");
} else if (m_brush->brushType() == MASK) {
brushTypeString = i18n("Mask");
} else if (m_brush->brushType() == IMAGE) {
brushTypeString = i18n("GBR");
} else if (m_brush->brushType() == PIPE_MASK ) {
brushTypeString = i18n("Animated Mask"); // GIH brush
// cast to GIH brush and grab parasite name
//m_brush
- KisImagePipeBrush* pipeBrush = dynamic_cast<KisImagePipeBrush*>(resource);
+ KisImagePipeBrushSP pipeBrush = resource.dynamicCast<KisImagePipeBrush>();
animatedBrushTipSelectionMode = pipeBrush->parasiteSelection();
} else if (m_brush->brushType() == PIPE_IMAGE ) {
brushTypeString = i18n("Animated Image");
}
QString brushDetailsText = QString("%1 (%2 x %3) %4")
.arg(brushTypeString)
.arg(m_brush->width())
.arg(m_brush->height())
.arg(animatedBrushTipSelectionMode);
brushDetailsLabel->setText(brushDetailsText);
// keep the current preset's tip settings if we are preserving it
// this will set the brush's model data to keep what it currently has for size, spacing, etc.
if (preserveBrushPresetSettings->isChecked() && !isChangingBrushPresets) {
m_brush->setAutoSpacing(brushSpacingSelectionWidget->autoSpacingActive(), brushSpacingSelectionWidget->autoSpacingCoeff());
m_brush->setAngle(brushRotationSpinBox->value() * M_PI / 180);
m_brush->setSpacing(brushSpacingSelectionWidget->spacing());
m_brush->setUserEffectiveSize(brushSizeSpinBox->value());
}
brushSpacingSelectionWidget->setSpacing(m_brush->autoSpacingActive(),
m_brush->autoSpacingActive() ?
m_brush->autoSpacingCoeff() : m_brush->spacing());
brushRotationSpinBox->setValue(m_brush->angle() * 180 / M_PI);
brushSizeSpinBox->setValue(m_brush->width() * m_brush->scale());
// useColorAsMask support is only in gimp brush so far
bool prevColorAsMaskState = useColorAsMaskCheckbox->isChecked();
KisGbrBrush *gimpBrush = dynamic_cast<KisGbrBrush*>(m_brush.data());
if (gimpBrush) {
useColorAsMaskCheckbox->setChecked(gimpBrush->useColorAsMask() || prevColorAsMaskState);
gimpBrush->setUseColorAsMask(prevColorAsMaskState);
}
useColorAsMaskCheckbox->setEnabled(m_brush->hasColor() && gimpBrush);
emit sigBrushChanged();
}
}
-void KisPredefinedBrushChooser::slotNewPredefinedBrush(KoResource *resource)
+void KisPredefinedBrushChooser::slotNewPredefinedBrush(KoResourceSP resource)
{
m_itemChooser->setCurrentResource(resource);
updateBrushTip(resource);
}
void KisPredefinedBrushChooser::setBrushSize(qreal xPixels, qreal yPixels)
{
Q_UNUSED(yPixels);
qreal oldWidth = m_brush->width() * m_brush->scale();
qreal newWidth = oldWidth + xPixels;
newWidth = qMax(newWidth, qreal(0.1));
brushSizeSpinBox->setValue(newWidth);
}
void KisPredefinedBrushChooser::setImage(KisImageWSP image)
{
m_image = image;
}
void KisPredefinedBrushChooser::slotImportNewBrushResource() {
- m_itemChooser->slotButtonClicked(KoResourceItemChooser::Button_Import);
+ m_itemChooser->slotButtonClicked(KisResourceItemChooser::Button_Import);
}
void KisPredefinedBrushChooser::slotDeleteBrushResource() {
- m_itemChooser->slotButtonClicked(KoResourceItemChooser::Button_Remove);
+ m_itemChooser->slotButtonClicked(KisResourceItemChooser::Button_Remove);
}
#include "moc_kis_brush_chooser.cpp"
diff --git a/plugins/paintops/libpaintop/kis_brush_chooser.h b/plugins/paintops/libpaintop/kis_brush_chooser.h
index 7610e5385c..76c711a44b 100644
--- a/plugins/paintops/libpaintop/kis_brush_chooser.h
+++ b/plugins/paintops/libpaintop/kis_brush_chooser.h
@@ -1,86 +1,86 @@
/*
* Copyright (c) 2004 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_PREDEFINED_BRUSH_CHOOSER_H_
#define KIS_PREDEFINED_BRUSH_CHOOSER_H_
#include <QLabel>
#include <kis_brush.h>
#include <QScroller>
#include "kritapaintop_export.h"
#include "ui_wdgpredefinedbrushchooser.h"
class KisDoubleSliderSpinBox;
class QLabel;
class QCheckBox;
class KisDoubleSliderSpinBox;
class KisSpacingSelectionWidget;
class KisCustomBrushWidget;
class KisClipboardBrushWidget;
-class KoResourceItemChooser;
+class KisResourceItemChooser;
class KoResource;
class PAINTOP_EXPORT KisPredefinedBrushChooser : public QWidget, public Ui::WdgPredefinedBrushChooser
{
Q_OBJECT
public:
KisPredefinedBrushChooser(QWidget *parent = 0, const char *name = 0);
~KisPredefinedBrushChooser() override;
KisBrushSP brush() {
return m_brush;
};
void setBrush(KisBrushSP brush);
void setBrushSize(qreal xPixels, qreal yPixels);
void setImage(KisImageWSP image);
private Q_SLOTS:
void slotResetBrush();
void slotSetItemSize(qreal);
void slotSetItemRotation(qreal);
void slotSpacingChanged();
void slotSetItemUseColorAsMask(bool);
void slotOpenStampBrush();
void slotOpenClipboardBrush();
void slotImportNewBrushResource();
void slotDeleteBrushResource();
- void slotNewPredefinedBrush(KoResource *);
- void updateBrushTip(KoResource *, bool isChangingBrushPresets = false);
+ void slotNewPredefinedBrush(KoResourceSP );
+ void updateBrushTip(KoResourceSP , bool isChangingBrushPresets = false);
Q_SIGNALS:
void sigBrushChanged();
private:
KisBrushSP m_brush;
- KoResourceItemChooser* m_itemChooser;
+ KisResourceItemChooser* m_itemChooser;
KisImageWSP m_image;
KisCustomBrushWidget* m_stampBrushWidget;
KisClipboardBrushWidget* m_clipboardBrushWidget;
};
#endif // KIS_PREDEFINED_BRUSH_CHOOSER_H_
diff --git a/plugins/paintops/libpaintop/kis_brush_option.cpp b/plugins/paintops/libpaintop/kis_brush_option.cpp
index 1e85893227..a0f4a64e0d 100644
--- a/plugins/paintops/libpaintop/kis_brush_option.cpp
+++ b/plugins/paintops/libpaintop/kis_brush_option.cpp
@@ -1,104 +1,130 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2008
* Copyright (C) Sven Langkamp <sven.langkamp@gmail.com>, (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_brush_option.h"
#include <QDomDocument>
#include <QDomElement>
#include "kis_properties_configuration.h"
#include <KisPaintopSettingsIds.h>
+#include <kis_brush.h>
+#include <KoEphemeralResource.h>
+
void KisBrushOptionProperties::writeOptionSettingImpl(KisPropertiesConfiguration *setting) const
{
if (!m_brush)
return;
QDomDocument d;
QDomElement e = d.createElement("Brush");
m_brush->toXML(d, e);
d.appendChild(e);
setting->setProperty("brush_definition", d.toString());
QString brushFileName = !m_brush->filename().isEmpty() ?
- m_brush->shortFilename() : QString();
+ m_brush->filename() : QString();
setting->setProperty(KisPaintOpUtils::RequiredBrushFileTag, brushFileName);
{
QStringList requiredFiles =
setting->getStringList(KisPaintOpUtils::RequiredBrushFilesListTag);
requiredFiles << brushFileName;
setting->setProperty(KisPaintOpUtils::RequiredBrushFilesListTag, requiredFiles);
}
}
QDomElement getBrushXMLElement(const KisPropertiesConfiguration *setting)
{
QDomElement element;
QString brushDefinition = setting->getString("brush_definition");
if (!brushDefinition.isEmpty()) {
QDomDocument d;
d.setContent(brushDefinition, false);
element = d.firstChildElement("Brush");
}
return element;
}
-void KisBrushOptionProperties::readOptionSettingImpl(const KisPropertiesConfiguration *setting)
+void KisBrushOptionProperties::readOptionSettingResourceImpl(const KisPropertiesConfiguration *setting, KisResourcesInterfaceSP resourcesInterface)
{
QDomElement element = getBrushXMLElement(setting);
if (!element.isNull()) {
- m_brush = KisBrush::fromXML(element);
+ m_brush = KisBrush::fromXML(element, resourcesInterface);
}
}
+QList<KoResourceSP> KisBrushOptionProperties::prepareLinkedResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const
+{
+ QList<KoResourceSP> resources;
+
+ QDomElement element = getBrushXMLElement(settings);
+ if (element.isNull()) return resources;
+
+ KisBrushSP brush = KisBrush::fromXML(element, resourcesInterface);
+ // TODO: implement proper property for KoResource about ephemerality
+ if (brush && !dynamic_cast<KoEphemeralResource<KisBrush>*>(brush.data())) {
+ resources << brush;
+ }
+
+ return resources;
+}
+
+QList<KoResourceSP> KisBrushOptionProperties::prepareEmbeddedResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const
+{
+ Q_UNUSED(settings)
+ Q_UNUSED(resourcesInterface);
+ return {};
+}
+
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
#include "kis_text_brush_factory.h"
bool KisBrushOptionProperties::isTextBrush(const KisPropertiesConfiguration *setting)
{
static QString textBrushId = KisTextBrushFactory().id();
QDomElement element = getBrushXMLElement(setting);
QString brushType = element.attribute("type");
return brushType == textBrushId;
}
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
KisBrushSP KisBrushOptionProperties::brush() const
{
return m_brush;
}
void KisBrushOptionProperties::setBrush(KisBrushSP brush)
{
m_brush = brush;
}
diff --git a/plugins/paintops/libpaintop/kis_brush_option.h b/plugins/paintops/libpaintop/kis_brush_option.h
index ac34d1e30b..ffcf9cb415 100644
--- a/plugins/paintops/libpaintop/kis_brush_option.h
+++ b/plugins/paintops/libpaintop/kis_brush_option.h
@@ -1,50 +1,52 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2008
* Copyright (C) Sven Langkamp <sven.langkamp@gmail.com>, (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_BRUSH_OPTION_H_
#define KIS_BRUSH_OPTION_H_
#include <kis_brush.h>
#include <KisPaintopPropertiesBase.h>
#include <kis_properties_configuration.h>
#include <kis_threaded_text_rendering_workaround.h>
-
+#include <KisResourcesInterface.h>
#include <kritapaintop_export.h>
-class PAINTOP_EXPORT KisBrushOptionProperties : public KisPaintopPropertiesBase
+class PAINTOP_EXPORT KisBrushOptionProperties : public KisPaintopPropertiesResourcesBase
{
public:
void writeOptionSettingImpl(KisPropertiesConfiguration *setting) const override;
- void readOptionSettingImpl(const KisPropertiesConfiguration *setting) override;
+ void readOptionSettingResourceImpl(const KisPropertiesConfiguration *setting, KisResourcesInterfaceSP resourcesInterface) override;
+ QList<KoResourceSP> prepareLinkedResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const override;
+ QList<KoResourceSP> prepareEmbeddedResourcesImpl(const KisPropertiesConfiguration *settings, KisResourcesInterfaceSP resourcesInterface) const override;
KisBrushSP brush() const;
void setBrush(KisBrushSP brush);
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
static bool isTextBrush(const KisPropertiesConfiguration *setting);
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
private:
KisBrushSP m_brush;
};
#endif
diff --git a/plugins/paintops/libpaintop/kis_brush_option_widget.cpp b/plugins/paintops/libpaintop/kis_brush_option_widget.cpp
index 770f822b4d..1d9f46882d 100644
--- a/plugins/paintops/libpaintop/kis_brush_option_widget.cpp
+++ b/plugins/paintops/libpaintop/kis_brush_option_widget.cpp
@@ -1,112 +1,112 @@
/* 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_brush_option_widget.h"
#include <klocalizedstring.h>
#include <kis_image.h>
#include "kis_brush_selection_widget.h"
#include "kis_brush.h"
-
+#include <KisGlobalResourcesInterface.h>
KisBrushOptionWidget::KisBrushOptionWidget()
: KisPaintOpOption(KisPaintOpOption::GENERAL, true)
{
m_checkable = false;
m_brushSelectionWidget = new KisBrushSelectionWidget();
connect(m_brushSelectionWidget, SIGNAL(sigPrecisionChanged()), SLOT(emitSettingChanged()));
connect(m_brushSelectionWidget, SIGNAL(sigBrushChanged()), SLOT(brushChanged()));
m_brushSelectionWidget->hide();
setConfigurationPage(m_brushSelectionWidget);
m_brushOption.setBrush(brush());
setObjectName("KisBrushOptionWidget");
}
KisBrushSP KisBrushOptionWidget::brush() const
{
return m_brushSelectionWidget->brush();
}
void KisBrushOptionWidget::setAutoBrush(bool on)
{
m_brushSelectionWidget->setAutoBrush(on);
}
void KisBrushOptionWidget::setPredefinedBrushes(bool on)
{
m_brushSelectionWidget->setPredefinedBrushes(on);
}
void KisBrushOptionWidget::setCustomBrush(bool on)
{
m_brushSelectionWidget->setCustomBrush(on);
}
void KisBrushOptionWidget::setTextBrush(bool on)
{
m_brushSelectionWidget->setTextBrush(on);
}
void KisBrushOptionWidget::setImage(KisImageWSP image)
{
m_brushSelectionWidget->setImage(image);
}
void KisBrushOptionWidget::setPrecisionEnabled(bool value)
{
m_brushSelectionWidget->setPrecisionEnabled(value);
}
void KisBrushOptionWidget::writeOptionSetting(KisPropertiesConfigurationSP settings) const
{
m_brushSelectionWidget->writeOptionSetting(settings);
m_brushOption.writeOptionSetting(settings);
}
void KisBrushOptionWidget::readOptionSetting(const KisPropertiesConfigurationSP setting)
{
m_brushSelectionWidget->readOptionSetting(setting);
- m_brushOption.readOptionSetting(setting);
+ m_brushOption.readOptionSetting(setting, KisGlobalResourcesInterface::instance());
m_brushSelectionWidget->setCurrentBrush(m_brushOption.brush());
}
void KisBrushOptionWidget::lodLimitations(KisPaintopLodLimitations *l) const
{
KisBrushSP brush = this->brush();
brush->lodLimitations(l);
}
void KisBrushOptionWidget::brushChanged()
{
m_brushOption.setBrush(brush());
emitSettingChanged();
}
bool KisBrushOptionWidget::presetIsValid()
{
return m_brushSelectionWidget->presetIsValid();
}
void KisBrushOptionWidget::hideOptions(const QStringList &options)
{
m_brushSelectionWidget->hideOptions(options);
}
#include "moc_kis_brush_option_widget.cpp"
diff --git a/plugins/paintops/libpaintop/kis_clipboard_brush_widget.cpp b/plugins/paintops/libpaintop/kis_clipboard_brush_widget.cpp
index 9d70629ded..4c95e14c65 100644
--- a/plugins/paintops/libpaintop/kis_clipboard_brush_widget.cpp
+++ b/plugins/paintops/libpaintop/kis_clipboard_brush_widget.cpp
@@ -1,160 +1,164 @@
/*
* Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2013 Somsubhra Bairi <somsubhra.bairi@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_clipboard_brush_widget.h"
#include <QLabel>
#include <QImage>
#include <QPixmap>
#include <QShowEvent>
#include <QPushButton>
-
+#include <QDialogButtonBox>
#include <KoResourcePaths.h>
#include <kis_debug.h>
#include "kis_image.h"
#include "kis_clipboard.h"
#include "kis_paint_device.h"
#include "kis_gbr_brush.h"
-#include "kis_brush_server.h"
+#include "KisBrushServerProvider.h"
+#include "kis_icon.h"
KisClipboardBrushWidget::KisClipboardBrushWidget(QWidget *parent, const QString &caption, KisImageWSP /*image*/)
: KisWdgClipboardBrush(parent)
{
setWindowTitle(caption);
- preview->setScaledContents(true);
+ preview->setScaledContents(false);
preview->setFixedSize(preview->size());
preview->setStyleSheet("border: 2px solid #222; border-radius: 4px; padding: 5px; font: normal 10px;");
- KisBrushResourceServer* rServer = KisBrushServer::instance()->brushServer();
- m_rServerAdapter = QSharedPointer<KisBrushResourceServerAdapter>(new KisBrushResourceServerAdapter(rServer));
+ m_rServer = KisBrushServerProvider::instance()->brushServer();
m_brush = 0;
m_clipboard = KisClipboard::instance();
connect(m_clipboard, SIGNAL(clipChanged()), this, SLOT(slotCreateBrush()));
connect(colorAsmask, SIGNAL(toggled(bool)), this, SLOT(slotUpdateUseColorAsMask(bool)));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotAddPredefined()));
+ connect(nameEdit, SIGNAL(textEdited(const QString&)), this, SLOT(slotUpdateSaveButton()));
spacingWidget->setSpacing(true, 1.0);
connect(spacingWidget, SIGNAL(sigSpacingChanged()), SLOT(slotSpacingChanged()));
}
KisClipboardBrushWidget::~KisClipboardBrushWidget()
{
}
void KisClipboardBrushWidget::slotCreateBrush()
{
// do nothing if it's hidden otherwise it can break the active brush is something is copied
if (m_clipboard->hasClip() && !isHidden()) {
pd = m_clipboard->clip(QRect(0, 0, 0, 0), false); //Weird! Don't know how this works!
if (pd) {
QRect rc = pd->exactBounds();
- m_brush = new KisGbrBrush(pd, rc.x(), rc.y(), rc.width(), rc.height());
+ m_brush = KisBrushSP(new KisGbrBrush(pd, rc.x(), rc.y(), rc.width(), rc.height()));
m_brush->setSpacing(spacingWidget->spacing());
m_brush->setAutoSpacing(spacingWidget->autoSpacingActive(), spacingWidget->autoSpacingCoeff());
m_brush->setFilename(TEMPORARY_CLIPBOARD_BRUSH_FILENAME);
m_brush->setName(TEMPORARY_CLIPBOARD_BRUSH_NAME);
m_brush->setValid(true);
- preview->setPixmap(QPixmap::fromImage(m_brush->image()));
+ int w = preview->size().width()-10;
+ preview->setPixmap(QPixmap::fromImage(m_brush->image().scaled(w, w, Qt::KeepAspectRatio)));
}
} else {
preview->setText(i18n("Nothing copied\n to Clipboard"));
}
- if(m_brush == 0) {
- buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
- } else {
- buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
+ if (!m_brush) {
+ buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
+ } else {
+ buttonBox->button(QDialogButtonBox::Save)->setEnabled(true);
colorAsmask->setChecked(true); // initializing this has to happen here since we need a valid brush for it to work
}
}
void KisClipboardBrushWidget::slotSpacingChanged()
{
if (m_brush) {
m_brush->setSpacing(spacingWidget->spacing());
m_brush->setAutoSpacing(spacingWidget->autoSpacingActive(), spacingWidget->autoSpacingCoeff());
}
}
void KisClipboardBrushWidget::showEvent(QShowEvent *)
{
slotCreateBrush();
}
void KisClipboardBrushWidget::slotUpdateUseColorAsMask(bool useColorAsMask)
{
if (m_brush) {
static_cast<KisGbrBrush*>(m_brush.data())->setUseColorAsMask(useColorAsMask);
- preview->setPixmap(QPixmap::fromImage(m_brush->brushTipImage()));
+ int w = preview->size().width()-10;
+ preview->setPixmap(QPixmap::fromImage(m_brush->image().scaled(w, w, Qt::KeepAspectRatio)));
}
}
void KisClipboardBrushWidget::slotAddPredefined()
{
if(!m_brush)
return;
- QString dir = KoResourcePaths::saveLocation("data", "brushes");
+ QString dir = KoResourcePaths::saveLocation("data", ResourceType::Brushes);
QString extension = ".gbr";
QString name = nameEdit->text();
- QString tempFileName;
- QFileInfo fileInfo;
- fileInfo.setFile(dir + name + extension);
-
- int i = 1;
- while (fileInfo.exists()) {
- fileInfo.setFile(dir + name + QString("%1").arg(i) + extension);
- i++;
- }
- tempFileName = fileInfo.filePath();
-
- if (m_rServerAdapter) {
- KisGbrBrush *resource = dynamic_cast<KisGbrBrush*>(m_brush->clone());
- resource->setFilename(tempFileName);
+ if (m_rServer) {
+ KisGbrBrushSP resource = m_brush->clone().dynamicCast<KisGbrBrush>();
if (nameEdit->text().isEmpty()) {
resource->setName(QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm"));
}
else {
resource->setName(name);
}
+ resource->setFilename(resource->name().split(" ").join("_") + extension);
+
+
if (colorAsmask->isChecked()) {
resource->makeMaskImage();
}
- m_rServerAdapter->addResource(resource);
+ m_rServer->addResource(resource.dynamicCast<KisBrush>());
emit sigNewPredefinedBrush(resource);
}
close();
}
+void KisClipboardBrushWidget::slotUpdateSaveButton()
+{
+ if (QFileInfo(m_rServer->saveLocation() + "/" + nameEdit->text().split(" ").join("_")
+ + ".gbr").exists()) {
+ buttonBox->button(QDialogButtonBox::Save)->setText(i18n("Overwrite"));
+ } else {
+ buttonBox->button(QDialogButtonBox::Save)->setText(i18n("Save"));
+ }
+}
+
#include "moc_kis_clipboard_brush_widget.cpp"
diff --git a/plugins/paintops/libpaintop/kis_clipboard_brush_widget.h b/plugins/paintops/libpaintop/kis_clipboard_brush_widget.h
index 458cf48005..673fb458d2 100644
--- a/plugins/paintops/libpaintop/kis_clipboard_brush_widget.h
+++ b/plugins/paintops/libpaintop/kis_clipboard_brush_widget.h
@@ -1,77 +1,75 @@
/*
* Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
* Copyright (c) 2013 Somsubhra Bairi <somsubhra.bairi@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_CLIPBOARD_BRUSH_WIDGET_H
#define KIS_CLIPBOARD_BRUSH_WIDGET_H
-//Qt includes
#include <QObject>
#include <QShowEvent>
-//Calligra includes
-#include <KoResourceServerAdapter.h>
+#include <KoResourceServer.h>
-//Krita includes
#include <kis_types.h>
#include <kis_brush.h>
#include "ui_wdgclipboardbrush.h"
const QString TEMPORARY_CLIPBOARD_BRUSH_FILENAME = "/tmp/temporaryClipboardBrush.gbr";
const QString TEMPORARY_CLIPBOARD_BRUSH_NAME = "Temporary clipboard brush";
const double DEFAULT_CLIPBOARD_BRUSH_SPACING = 0.25;
class KisClipboard;
class KoResource;
class KisWdgClipboardBrush : public QDialog, public Ui::KisWdgClipboardBrush
{
Q_OBJECT
public:
KisWdgClipboardBrush(QWidget* parent) : QDialog(parent) {
setupUi(this);
}
};
class KisClipboardBrushWidget : public KisWdgClipboardBrush
{
Q_OBJECT
public:
KisClipboardBrushWidget(QWidget* parent, const QString& caption, KisImageWSP image);
virtual ~KisClipboardBrushWidget();
private Q_SLOTS:
void slotCreateBrush();
void slotSpacingChanged();
void slotUpdateUseColorAsMask(bool useColorAsMask);
void slotAddPredefined();
+ void slotUpdateSaveButton();
protected:
void showEvent(QShowEvent *);
Q_SIGNALS:
- void sigNewPredefinedBrush(KoResource *);
+ void sigNewPredefinedBrush(KoResourceSP );
private:
KisClipboard* m_clipboard;
KisPaintDeviceSP pd;
KisBrushSP m_brush;
- QSharedPointer<KoAbstractResourceServerAdapter> m_rServerAdapter;
+ KoResourceServer<KisBrush> *m_rServer;
};
#endif // KIS_CLIPBOARD_BRUSH_WIDGET_H
diff --git a/plugins/paintops/libpaintop/kis_color_source.cpp b/plugins/paintops/libpaintop/kis_color_source.cpp
index 5b7ea8ede6..b4772ead99 100644
--- a/plugins/paintops/libpaintop/kis_color_source.cpp
+++ b/plugins/paintops/libpaintop/kis_color_source.cpp
@@ -1,297 +1,297 @@
/*
* 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 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_color_source.h"
#include <kis_paint_device.h>
#include <resources/KoAbstractGradient.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorTransformation.h>
#include <KoMixColorsOp.h>
#include <kis_datamanager.h>
#include <kis_fill_painter.h>
#include "kis_iterator_ng.h"
#include "kis_selection.h"
#include <brushengine/kis_random_source.h>
#include <brushengine/kis_paint_information.h>
#include <random>
KisColorSource::~KisColorSource() { }
const KoColor black;
const KoColor& KisColorSource::uniformColor() const
{
qFatal("Not an uniform color.");
return black;
}
KisUniformColorSource::KisUniformColorSource()
{
}
KisUniformColorSource::~KisUniformColorSource()
{
}
void KisUniformColorSource::rotate(double)
{}
void KisUniformColorSource::resize(double , double)
{
// Do nothing as plain color does not have size
}
void KisUniformColorSource::colorize(KisPaintDeviceSP dev, const QRect& size, const QPoint&) const
{
Q_UNUSED(size);
KoColor c(dev->colorSpace());
c.fromKoColor(m_color);
dev->dataManager()->setDefaultPixel(c.data());
dev->clear();
}
const KoColor& KisUniformColorSource::uniformColor() const
{
return m_color;
}
void KisUniformColorSource::applyColorTransformation(const KoColorTransformation* transfo)
{
transfo->transform(m_color.data(), m_color.data(), 1);
}
const KoColorSpace* KisUniformColorSource::colorSpace() const
{
return m_color.colorSpace();
}
bool KisUniformColorSource::isUniformColor() const
{
return true;
}
//-------------------------------------------------//
//---------------- KisPlainColorSource ---------------//
//-------------------------------------------------//
KisPlainColorSource::KisPlainColorSource(const KoColor& backGroundColor, const KoColor& foreGroundColor)
: m_backGroundColor(backGroundColor)
, m_cachedBackGroundColor(backGroundColor)
, m_foreGroundColor(foreGroundColor)
{
}
KisPlainColorSource::~KisPlainColorSource()
{
}
void KisPlainColorSource::selectColor(double mix, const KisPaintInformation &pi)
{
Q_UNUSED(pi);
if (m_color.colorSpace() != m_foreGroundColor.colorSpace()) {
m_color = KoColor(m_foreGroundColor.colorSpace());
m_cachedBackGroundColor = KoColor(m_foreGroundColor.colorSpace());
m_cachedBackGroundColor.fromKoColor(m_backGroundColor);
}
const quint8 *colors[2];
colors[0] = m_cachedBackGroundColor.data();
colors[1] = m_foreGroundColor.data();
// equally distribute mix factor over [0..255]
// mix * 256 ensures that, with exception of mix==1.0, which gets special handling
const int weight = (mix == 1.0) ? 255 : (int)(mix * 256);
const qint16 weights[2] = { (qint16)(255 - weight), (qint16)weight };
m_color.colorSpace()->mixColorsOp()->mixColors(colors, weights, 2, m_color.data());
}
//-------------------------------------------------//
//--------------- KisGradientColorSource -------------//
//-------------------------------------------------//
-KisGradientColorSource::KisGradientColorSource(const KoAbstractGradient* gradient, const KoColorSpace* workingCS)
+KisGradientColorSource::KisGradientColorSource(const KoAbstractGradientSP gradient, const KoColorSpace* workingCS)
: m_gradient(gradient)
{
m_color = KoColor(workingCS);
Q_ASSERT(gradient);
}
KisGradientColorSource::~KisGradientColorSource()
{
}
void KisGradientColorSource::selectColor(double mix, const KisPaintInformation &pi)
{
Q_UNUSED(pi);
if (m_gradient) {
m_gradient->colorAt(m_color, mix);
}
}
//-------------------------------------------------//
//--------------- KisGradientColorSource -------------//
//-------------------------------------------------//
KisUniformRandomColorSource::KisUniformRandomColorSource()
{
}
KisUniformRandomColorSource::~KisUniformRandomColorSource()
{
}
void KisUniformRandomColorSource::selectColor(double mix, const KisPaintInformation &pi)
{
Q_UNUSED(pi);
Q_UNUSED(mix);
KisRandomSourceSP source = pi.randomSource();
m_color.fromQColor(QColor((int)source->generate(0, 255),
(int)source->generate(0, 255),
(int)source->generate(0, 255)));
}
//------------------------------------------------------//
//--------------- KisTotalRandomColorSource ---------------//
//------------------------------------------------------//
KisTotalRandomColorSource::KisTotalRandomColorSource() : m_colorSpace(KoColorSpaceRegistry::instance()->rgb8())
{
}
KisTotalRandomColorSource::~KisTotalRandomColorSource()
{
}
void KisTotalRandomColorSource::selectColor(double mix, const KisPaintInformation &pi)
{
Q_UNUSED(mix);
Q_UNUSED(pi);
}
void KisTotalRandomColorSource::applyColorTransformation(const KoColorTransformation*) {}
const KoColorSpace* KisTotalRandomColorSource::colorSpace() const
{
return m_colorSpace;
}
void KisTotalRandomColorSource::colorize(KisPaintDeviceSP dev, const QRect& rect, const QPoint&) const
{
KoColor kc(dev->colorSpace());
QColor qc;
std::random_device rand_dev;
std::default_random_engine rand_engine{rand_dev()};
std::uniform_int_distribution<> rand_distr(0, 255);
int pixelSize = dev->colorSpace()->pixelSize();
KisHLineIteratorSP it = dev->createHLineIteratorNG(rect.x(), rect.y(), rect.width());
for (int y = 0; y < rect.height(); y++) {
do {
qc.setRgb(rand_distr(rand_engine), rand_distr(rand_engine), rand_distr(rand_engine));
kc.fromQColor(qc);
memcpy(it->rawData(), kc.data(), pixelSize);
} while (it->nextPixel());
it->nextRow();
}
}
bool KisTotalRandomColorSource::isUniformColor() const
{
return false;
}
void KisTotalRandomColorSource::rotate(double) {}
void KisTotalRandomColorSource::resize(double , double) {}
KoPatternColorSource::KoPatternColorSource(KisPaintDeviceSP _pattern, int _width, int _height, bool _locked)
: m_device(_pattern)
, m_bounds(QRect(0, 0, _width, _height))
, m_locked(_locked)
{
}
KoPatternColorSource::~KoPatternColorSource()
{
}
void KoPatternColorSource::selectColor(double mix, const KisPaintInformation &pi)
{
Q_UNUSED(mix);
Q_UNUSED(pi);
}
void KoPatternColorSource::applyColorTransformation(const KoColorTransformation* transfo)
{
Q_UNUSED(transfo);
}
const KoColorSpace* KoPatternColorSource::colorSpace() const
{
return m_device->colorSpace();
}
void KoPatternColorSource::colorize(KisPaintDeviceSP device, const QRect& rect, const QPoint& offset) const
{
KisFillPainter painter(device);
if (m_locked) {
painter.fillRect(rect.x(), rect.y(), rect.width(), rect.height(), m_device, m_bounds);
}
else {
int x = offset.x() % m_bounds.width();
int y = offset.y() % m_bounds.height();
// Change the position, because the pattern is always applied starting
// from (0,0) in the paint device reference
device->setX(x);
device->setY(y);
painter.fillRect(rect.x() + x, rect.y() + y, rect.width(), rect.height(), m_device, m_bounds);
device->setX(0);
device->setY(0);
}
}
void KoPatternColorSource::rotate(double r)
{
Q_UNUSED(r);
}
void KoPatternColorSource::resize(double xs, double ys)
{
Q_UNUSED(xs);
Q_UNUSED(ys);
}
bool KoPatternColorSource::isUniformColor() const
{
return false;
}
diff --git a/plugins/paintops/libpaintop/kis_color_source.h b/plugins/paintops/libpaintop/kis_color_source.h
index ece32ca36e..961ec4eba4 100644
--- a/plugins/paintops/libpaintop/kis_color_source.h
+++ b/plugins/paintops/libpaintop/kis_color_source.h
@@ -1,153 +1,153 @@
/*
* 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 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_DYNAMIC_COLORING_H_
#define _KIS_DYNAMIC_COLORING_H_
#include "kis_paintop_option.h"
#include <QRect>
#include <KoColor.h>
+#include <KoAbstractGradient.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(const KoAbstractGradientSP gradient, const KoColorSpace* workingCS);
~KisGradientColorSource() override;
void selectColor(double mix, const KisPaintInformation &pi) override;
private:
- const KoAbstractGradient* m_gradient;
+ const KoAbstractGradientSP 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_curve_option.cpp b/plugins/paintops/libpaintop/kis_curve_option.cpp
index f205824fd5..36a8080199 100644
--- a/plugins/paintops/libpaintop/kis_curve_option.cpp
+++ b/plugins/paintops/libpaintop/kis_curve_option.cpp
@@ -1,484 +1,484 @@
/* This file is part of the KDE project
* Copyright (C) 2008 Boudewijn Rempt <boud@valdyas.org>
* 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.
*/
#include "kis_curve_option.h"
#include <QDomNode>
KisCurveOption::KisCurveOption(const QString& name, KisPaintOpOption::PaintopCategory category,
bool checked, qreal value, qreal min, qreal max)
: m_name(name)
, m_category(category)
, m_checkable(true)
, m_checked(checked)
, m_useCurve(true)
, m_useSameCurve(true)
, m_separateCurveValue(false)
, m_curveMode(0)
{
Q_FOREACH (const DynamicSensorType sensorType, KisDynamicSensor::sensorsTypes()) {
KisDynamicSensorSP sensor = KisDynamicSensor::type2Sensor(sensorType, m_name);
sensor->setActive(false);
replaceSensor(sensor);
}
m_sensorMap[PRESSURE]->setActive(true);
setValueRange(min, max);
setValue(value);
m_commonCurve = defaultCurve();
}
KisCurveOption::~KisCurveOption()
{
}
const QString& KisCurveOption::name() const
{
return m_name;
}
KisPaintOpOption::PaintopCategory KisCurveOption::category() const
{
return m_category;
}
qreal KisCurveOption::minValue() const
{
return m_minValue;
}
qreal KisCurveOption::maxValue() const
{
return m_maxValue;
}
qreal KisCurveOption::value() const
{
return m_value;
}
void KisCurveOption::resetAllSensors()
{
Q_FOREACH (KisDynamicSensorSP sensor, m_sensorMap.values()) {
if (sensor->isActive()) {
sensor->reset();
}
}
}
void KisCurveOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const
{
if (m_checkable) {
setting->setProperty("Pressure" + m_name, isChecked());
}
if (activeSensors().size() == 1) {
setting->setProperty(m_name + "Sensor", activeSensors().first()->toXML());
}
else {
QDomDocument doc = QDomDocument("params");
QDomElement root = doc.createElement("params");
doc.appendChild(root);
root.setAttribute("id", "sensorslist");
Q_FOREACH (KisDynamicSensorSP sensor, activeSensors()) {
QDomElement childelt = doc.createElement("ChildSensor");
sensor->toXML(doc, childelt);
root.appendChild(childelt);
}
setting->setProperty(m_name + "Sensor", doc.toString());
}
setting->setProperty(m_name + "UseCurve", m_useCurve);
setting->setProperty(m_name + "UseSameCurve", m_useSameCurve);
setting->setProperty(m_name + "Value", m_value);
setting->setProperty(m_name + "curveMode", m_curveMode);
- setting->setProperty(m_name + "commonCurve", qVariantFromValue(m_commonCurve));
+ setting->setProperty(m_name + "commonCurve", QVariant::fromValue(m_commonCurve));
}
void KisCurveOption::readOptionSetting(KisPropertiesConfigurationSP setting)
{
readNamedOptionSetting(m_name, setting);
}
void KisCurveOption::lodLimitations(KisPaintopLodLimitations *l) const
{
Q_UNUSED(l);
}
int KisCurveOption::intMinValue() const
{
return 0;
}
int KisCurveOption::intMaxValue() const
{
return 100;
}
QString KisCurveOption::valueSuffix() const
{
return i18n("%");
}
void KisCurveOption::readNamedOptionSetting(const QString& prefix, const KisPropertiesConfigurationSP setting)
{
if (!setting) return;
KisCubicCurve commonCurve = m_commonCurve;
//dbgKrita << "readNamedOptionSetting" << prefix;
// setting->dump();
if (m_checkable) {
setChecked(setting->getBool("Pressure" + prefix, false));
}
//dbgKrita << "\tPressure" + prefix << isChecked();
m_sensorMap.clear();
// Replace all sensors with the inactive defaults
Q_FOREACH (const DynamicSensorType sensorType, KisDynamicSensor::sensorsTypes()) {
replaceSensor(KisDynamicSensor::type2Sensor(sensorType, m_name));
}
QString sensorDefinition = setting->getString(prefix + "Sensor");
if (!sensorDefinition.contains("sensorslist")) {
KisDynamicSensorSP s = KisDynamicSensor::createFromXML(sensorDefinition, m_name);
if (s) {
replaceSensor(s);
s->setActive(true);
commonCurve = s->curve();
//dbgKrita << "\tsingle sensor" << s::id(s->sensorType()) << s->isActive() << "added";
}
}
else {
QDomDocument doc;
doc.setContent(sensorDefinition);
QDomElement elt = doc.documentElement();
QDomNode node = elt.firstChild();
while (!node.isNull()) {
if (node.isElement()) {
QDomElement childelt = node.toElement();
if (childelt.tagName() == "ChildSensor") {
KisDynamicSensorSP s = KisDynamicSensor::createFromXML(childelt, m_name);
if (s) {
replaceSensor(s);
s->setActive(true);
commonCurve = s->curve();
//dbgKrita << "\tchild sensor" << s::id(s->sensorType()) << s->isActive() << "added";
}
}
}
node = node.nextSibling();
}
}
m_useSameCurve = setting->getBool(m_name + "UseSameCurve", true);
// Only load the old curve format if the curve wasn't saved by the sensor
// This will give every sensor the same curve.
//dbgKrita << ">>>>>>>>>>>" << prefix + "Sensor" << setting->getString(prefix + "Sensor");
if (!setting->getString(prefix + "Sensor").contains("curve")) {
//dbgKrita << "\told format";
if (setting->getBool("Custom" + prefix, false)) {
Q_FOREACH (KisDynamicSensorSP s, m_sensorMap.values()) {
s->setCurve(setting->getCubicCurve("Curve" + prefix));
commonCurve = s->curve();
}
} else {
commonCurve = emptyCurve();
}
}
if (m_useSameCurve) {
m_commonCurve = setting->getCubicCurve(prefix + "commonCurve", commonCurve);
}
// At least one sensor needs to be active
if (activeSensors().size() == 0) {
m_sensorMap[PRESSURE]->setActive(true);
}
m_value = setting->getDouble(m_name + "Value", m_maxValue);
//dbgKrita << "\t" + m_name + "Value" << m_value;
m_useCurve = setting->getBool(m_name + "UseCurve", true);
//dbgKrita << "\t" + m_name + "UseCurve" << m_useSameCurve;
//dbgKrita << "\t" + m_name + "UseSameCurve" << m_useSameCurve;
m_curveMode = setting->getInt(m_name + "curveMode");
//dbgKrita << "-----------------";
}
void KisCurveOption::replaceSensor(KisDynamicSensorSP s)
{
Q_ASSERT(s);
m_sensorMap[s->sensorType()] = s;
}
KisDynamicSensorSP KisCurveOption::sensor(DynamicSensorType sensorType, bool active) const
{
if (m_sensorMap.contains(sensorType)) {
if (!active) {
return m_sensorMap[sensorType];
}
else {
if (m_sensorMap[sensorType]->isActive()) {
return m_sensorMap[sensorType];
}
}
}
return 0;
}
bool KisCurveOption::isRandom() const
{
return bool(sensor(FUZZY_PER_DAB, true)) ||
bool(sensor(FUZZY_PER_STROKE, true));
}
bool KisCurveOption::isCurveUsed() const
{
return m_useCurve;
}
bool KisCurveOption::isSameCurveUsed() const
{
return m_useSameCurve;
}
int KisCurveOption::getCurveMode() const
{
return m_curveMode;
}
KisCubicCurve KisCurveOption::getCommonCurve() const
{
return m_commonCurve;
}
void KisCurveOption::setSeparateCurveValue(bool separateCurveValue)
{
m_separateCurveValue = separateCurveValue;
}
bool KisCurveOption::isCheckable()
{
return m_checkable;
}
bool KisCurveOption::isChecked() const
{
return m_checked;
}
void KisCurveOption::setChecked(bool checked)
{
m_checked = checked;
}
void KisCurveOption::setCurveUsed(bool useCurve)
{
m_useCurve = useCurve;
}
void KisCurveOption::setCurveMode(int mode)
{
m_curveMode = mode;
}
void KisCurveOption::setUseSameCurve(bool useSameCurve)
{
m_useSameCurve = useSameCurve;
}
void KisCurveOption::setCommonCurve(KisCubicCurve curve)
{
m_commonCurve = curve;
}
void KisCurveOption::setCurve(DynamicSensorType sensorType, bool useSameCurve, const KisCubicCurve &curve)
{
if (useSameCurve == m_useSameCurve) {
if (useSameCurve) {
m_commonCurve = curve;
}
else {
KisDynamicSensorSP s = sensor(sensorType, false);
if (s) {
s->setCurve(curve);
}
}
}
else {
if (!m_useSameCurve && useSameCurve) {
m_commonCurve = curve;
}
else { //if (m_useSameCurve && !useSameCurve)
KisDynamicSensorSP s = 0;
// And set the current sensor to the current curve
if (!m_sensorMap.contains(sensorType)) {
s = KisDynamicSensor::type2Sensor(sensorType, m_name);
} else {
KisDynamicSensorSP s = sensor(sensorType, false);
}
if (s) {
s->setCurve(curve);
}
}
m_useSameCurve = useSameCurve;
}
}
void KisCurveOption::setValueRange(qreal min, qreal max)
{
m_minValue = qMin(min, max);
m_maxValue = qMax(min, max);
}
void KisCurveOption::setValue(qreal value)
{
m_value = qBound(m_minValue, value, m_maxValue);
}
KisCurveOption::ValueComponents KisCurveOption::computeValueComponents(const KisPaintInformation& info) const
{
ValueComponents components;
if (m_useCurve) {
QMap<DynamicSensorType, KisDynamicSensorSP>::const_iterator i;
QList<double> sensorValues;
for (i = m_sensorMap.constBegin(); i != m_sensorMap.constEnd(); ++i) {
KisDynamicSensorSP s(i.value());
if (s->isActive()) {
qreal valueFromCurve = m_useSameCurve ? s->parameter(info, m_commonCurve, true) : s->parameter(info);
if (s->isAdditive()) {
components.additive += valueFromCurve;
components.hasAdditive = true;
} else if (s->isAbsoluteRotation()) {
components.absoluteOffset = valueFromCurve;
components.hasAbsoluteOffset =true;
} else {
sensorValues << valueFromCurve;
components.hasScaling = true;
}
}
}
if (sensorValues.count() == 1) {
components.scaling = sensorValues.first();
} else {
if (m_curveMode == 1){ // add
components.scaling = 0;
double i;
foreach (i, sensorValues) {
components.scaling += i;
}
} else if (m_curveMode == 2){ //max
components.scaling = *std::max_element(sensorValues.begin(), sensorValues.end());
} else if (m_curveMode == 3){ //min
components.scaling = *std::min_element(sensorValues.begin(), sensorValues.end());
} else if (m_curveMode == 4){ //difference
double max = *std::max_element(sensorValues.begin(), sensorValues.end());
double min = *std::min_element(sensorValues.begin(), sensorValues.end());
components.scaling = max-min;
} else { //multuply - default
double i;
foreach (i, sensorValues) {
components.scaling *= i;
}
}
}
}
if (!m_separateCurveValue) {
components.constant = m_value;
}
components.minSizeLikeValue = m_minValue;
components.maxSizeLikeValue = m_maxValue;
return components;
}
qreal KisCurveOption::computeSizeLikeValue(const KisPaintInformation& info) const
{
const ValueComponents components = computeValueComponents(info);
return components.sizeLikeValue();
}
qreal KisCurveOption::computeRotationLikeValue(const KisPaintInformation& info, qreal baseValue, bool absoluteAxesFlipped) const
{
const ValueComponents components = computeValueComponents(info);
return components.rotationLikeValue(baseValue, absoluteAxesFlipped);
}
KisCubicCurve KisCurveOption::defaultCurve()
{
QList<QPointF> points;
// needs to be set to something, weird curve is better for debugging
// it will be reset to the curve from the preset anyway though
points.push_back(QPointF(0,0));
points.push_back(QPointF(0.25,0.9));
points.push_back(QPointF(0.5,0));
points.push_back(QPointF(0.75,0.6));
points.push_back(QPointF(1,0));
return KisCubicCurve(points);
}
KisCubicCurve KisCurveOption::emptyCurve()
{
QList<QPointF> points;
points.push_back(QPointF(0,0));
points.push_back(QPointF(1,1));
return KisCubicCurve(points);
}
QList<KisDynamicSensorSP> KisCurveOption::sensors()
{
//dbgKrita << "ID" << name() << "has" << m_sensorMap.count() << "Sensors of which" << sensorList.count() << "are active.";
return m_sensorMap.values();
}
QList<KisDynamicSensorSP> KisCurveOption::activeSensors() const
{
QList<KisDynamicSensorSP> sensorList;
Q_FOREACH (KisDynamicSensorSP sensor, m_sensorMap.values()) {
if (sensor->isActive()) {
sensorList << sensor;
}
}
//dbgKrita << "ID" << name() << "has" << m_sensorMap.count() << "Sensors of which" << sensorList.count() << "are active.";
return sensorList;
}
diff --git a/plugins/paintops/libpaintop/kis_curve_option_uniform_property.cpp b/plugins/paintops/libpaintop/kis_curve_option_uniform_property.cpp
index 88dc3176f3..fa33e1e35e 100644
--- a/plugins/paintops/libpaintop/kis_curve_option_uniform_property.cpp
+++ b/plugins/paintops/libpaintop/kis_curve_option_uniform_property.cpp
@@ -1,69 +1,64 @@
/*
* 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_curve_option_uniform_property.h"
#include "kis_curve_option.h"
#include "kis_paintop_settings.h"
#include "kis_paintop_preset.h"
#include "kis_paintop_settings_update_proxy.h"
KisCurveOptionUniformProperty::KisCurveOptionUniformProperty(const QString &name,
KisCurveOption *option,
KisPaintOpSettingsRestrictedSP settings,
QObject *parent)
: KisDoubleSliderBasedPaintOpProperty(Double,
name,
option->name(),
settings,
parent),
m_option(option)
{
setRange(option->minValue(), option->maxValue());
setSingleStep(0.01);
-
- KisPaintOpPresetSP preset = settings->preset();
-
- KIS_SAFE_ASSERT_RECOVER_RETURN(preset);
- connect(preset->updateProxy(), SIGNAL(sigSettingsChanged()), this, SLOT(requestReadValue()));
-
+ connect(settings->updateProxy(), SIGNAL(sigSettingsChanged()), this, SLOT(requestReadValue()));
requestReadValue();
}
KisCurveOptionUniformProperty::~KisCurveOptionUniformProperty()
{
}
void KisCurveOptionUniformProperty::readValueImpl()
{
m_option->readOptionSetting(settings().data());
setValue(m_option->value());
}
void KisCurveOptionUniformProperty::writeValueImpl()
{
m_option->readOptionSetting(settings().data());
m_option->setValue(value().toReal());
m_option->writeOptionSetting(settings().data());
}
bool KisCurveOptionUniformProperty::isVisible() const
{
return !m_option->isCheckable() || m_option->isChecked();
}
diff --git a/plugins/paintops/libpaintop/kis_custom_brush_widget.cpp b/plugins/paintops/libpaintop/kis_custom_brush_widget.cpp
index ed624ac2c3..5a7103e4b9 100644
--- a/plugins/paintops/libpaintop/kis_custom_brush_widget.cpp
+++ b/plugins/paintops/libpaintop/kis_custom_brush_widget.cpp
@@ -1,267 +1,268 @@
/*
* Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
* 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.
*/
#include "kis_custom_brush_widget.h"
#include <kis_debug.h>
#include <QLabel>
#include <QImage>
#include <QPushButton>
#include <QComboBox>
#include <QCheckBox>
#include <QDateTime>
#include <QPixmap>
#include <QShowEvent>
#include <KoResourcePaths.h>
#include "kis_image.h"
#include "kis_layer.h"
#include "kis_paint_device.h"
#include "kis_gbr_brush.h"
#include "kis_imagepipe_brush.h"
#include <kis_fixed_paint_device.h>
-#include "kis_brush_server.h"
+#include "KisBrushServerProvider.h"
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include <kis_selection.h>
#include <KoProperties.h>
#include "kis_iterator_ng.h"
#include "kis_image_barrier_locker.h"
KisCustomBrushWidget::KisCustomBrushWidget(QWidget *parent, const QString& caption, KisImageWSP image)
: KisWdgCustomBrush(parent)
, m_image(image)
{
setWindowTitle(caption);
preview->setScaledContents(false);
preview->setFixedSize(preview->size());
preview->setStyleSheet("border: 2px solid #222; border-radius: 4px; padding: 5px; font: normal 10px;");
- KisBrushResourceServer* rServer = KisBrushServer::instance()->brushServer();
- m_rServerAdapter = QSharedPointer<KisBrushResourceServerAdapter>(new KisBrushResourceServerAdapter(rServer));
+ m_rServer = KisBrushServerProvider::instance()->brushServer();
m_brush = 0;
connect(this, SIGNAL(accepted()), SLOT(slotAddPredefined()));
connect(brushStyle, SIGNAL(activated(int)), this, SLOT(slotUpdateCurrentBrush(int)));
connect(colorAsMask, SIGNAL(toggled(bool)), this, SLOT(slotUpdateUseColorAsMask(bool)));
connect(comboBox2, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateCurrentBrush(int)));
colorAsMask->setChecked(true); // use color as mask by default. This is by far the most common way to make tip.
spacingWidget->setSpacing(true, 1.0);
connect(spacingWidget, SIGNAL(sigSpacingChanged()), SLOT(slotSpacingChanged()));
}
KisCustomBrushWidget::~KisCustomBrushWidget()
{
}
KisBrushSP KisCustomBrushWidget::brush()
{
return m_brush;
}
void KisCustomBrushWidget::setImage(KisImageWSP image){
m_image = image;
createBrush();
updatePreviewImage();
}
void KisCustomBrushWidget::showEvent(QShowEvent *)
{
slotUpdateCurrentBrush(0);
}
void KisCustomBrushWidget::updatePreviewImage()
{
QImage brushImage = m_brush ? m_brush->brushTipImage() : QImage();
if (!brushImage.isNull()) {
- brushImage = brushImage.scaled(preview->size(), Qt::KeepAspectRatio);
+ int w = preview->size().width() - 10; // 10 for the padding...
+ brushImage = brushImage.scaled(w, w, Qt::KeepAspectRatio);
}
preview->setPixmap(QPixmap::fromImage(brushImage));
}
void KisCustomBrushWidget::slotUpdateCurrentBrush(int)
{
if (brushStyle->currentIndex() == 0) {
comboBox2->setEnabled(false);
} else {
comboBox2->setEnabled(true);
}
if (m_image) {
createBrush();
updatePreviewImage();
}
}
void KisCustomBrushWidget::slotSpacingChanged()
{
if (m_brush) {
m_brush->setSpacing(spacingWidget->spacing());
m_brush->setAutoSpacing(spacingWidget->autoSpacingActive(), spacingWidget->autoSpacingCoeff());
}
}
void KisCustomBrushWidget::slotUpdateUseColorAsMask(bool useColorAsMask)
{
if (m_brush) {
static_cast<KisGbrBrush*>(m_brush.data())->setUseColorAsMask(useColorAsMask);
updatePreviewImage();
}
}
-
-void KisCustomBrushWidget::slotAddPredefined()
+void KisCustomBrushWidget::slotUpdateSaveButton()
{
- QString dir = KoResourcePaths::saveLocation("data", "brushes");
- QString extension;
-
- if (brushStyle->currentIndex() == 0) {
- extension = ".gbr";
+ QString suffix = ".gbr";
+ if (brushStyle->currentIndex() != 0) {
+ suffix = ".gih";
}
- else {
- extension = ".gih";
+ if (QFileInfo(m_rServer->saveLocation() + "/" + nameLineEdit->text().split(" ").join("_")
+ + suffix).exists()) {
+ buttonBox->button(QDialogButtonBox::Save)->setText(i18n("Overwrite"));
+ } else {
+ buttonBox->button(QDialogButtonBox::Save)->setText(i18n("Save"));
}
+}
+
+
+void KisCustomBrushWidget::slotAddPredefined()
+{
+ QString dir = KoResourcePaths::saveLocation("data", ResourceType::Brushes);
QString name = nameLineEdit->text();
- QString tempFileName;
- {
- QFileInfo fileInfo;
- fileInfo.setFile(dir + name + extension);
-
- int i = 1;
- while (fileInfo.exists()) {
- fileInfo.setFile(dir + name + QString("%1").arg(i) + extension);
- i++;
- }
- tempFileName = fileInfo.filePath();
+ if (nameLineEdit->text().isEmpty()) {
+ name = (QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm"));
}
// Add it to the brush server, so that it automatically gets to the mediators, and
// so to the other brush choosers can pick it up, if they want to
- if (m_rServerAdapter && m_brush) {
- qDebug() << "m_brush" << m_brush;
- KisGbrBrush *resource = dynamic_cast<KisGbrBrush*>(m_brush->clone());
- resource->setFilename(tempFileName);
+ if (m_rServer && m_brush) {
+
- if (nameLineEdit->text().isEmpty()) {
- resource->setName(QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm"));
+ if (m_brush->clone().dynamicCast<KisGbrBrush>()) {
+ KisGbrBrushSP resource = m_brush->clone().dynamicCast<KisGbrBrush>();
+ resource->setName(name);
+ resource->setFilename(resource->name().split(" ").join("_") + resource->defaultFileExtension());
+ if (colorAsMask->isChecked()) {
+ resource->makeMaskImage();
+ }
+ m_rServer->addResource(resource.dynamicCast<KisBrush>());
+ emit sigNewPredefinedBrush(resource);
}
else {
+ KisImagePipeBrushSP resource = m_brush->clone().dynamicCast<KisImagePipeBrush>();
resource->setName(name);
+ resource->setFilename(resource->name().split(" ").join("_") + resource->defaultFileExtension());
+ if (colorAsMask->isChecked()) {
+ resource->makeMaskImage();
+ }
+ m_rServer->addResource(resource.dynamicCast<KisBrush>());
+ emit sigNewPredefinedBrush(resource);
}
- if (colorAsMask->isChecked()) {
- resource->makeMaskImage();
- }
-
- m_rServerAdapter->addResource(resource);
- emit sigNewPredefinedBrush(resource);
}
close();
}
void KisCustomBrushWidget::createBrush()
{
if (!m_image)
return;
if (brushStyle->currentIndex() == 0) {
KisSelectionSP selection = m_image->globalSelection();
+
// create copy of the data
m_image->barrierLock();
KisPaintDeviceSP dev = new KisPaintDevice(*m_image->projection());
m_image->unlock();
if (!selection) {
- m_brush = new KisGbrBrush(dev, 0, 0, m_image->width(), m_image->height());
+ m_brush = KisBrushSP(new KisGbrBrush(dev, 0, 0, m_image->width(), m_image->height()));
}
else {
// apply selection mask
QRect r = selection->selectedExactRect();
- dev->crop(r);
KisHLineIteratorSP pixelIt = dev->createHLineIteratorNG(r.x(), r.top(), r.width());
KisHLineConstIteratorSP maskIt = selection->projection()->createHLineIteratorNG(r.x(), r.top(), r.width());
for (qint32 y = r.top(); y <= r.bottom(); ++y) {
do {
dev->colorSpace()->applyAlphaU8Mask(pixelIt->rawData(), maskIt->oldRawData(), 1);
} while (pixelIt->nextPixel() && maskIt->nextPixel());
pixelIt->nextRow();
maskIt->nextRow();
}
-
- QRect rc = dev->exactBounds();
- m_brush = new KisGbrBrush(dev, rc.x(), rc.y(), rc.width(), rc.height());
+ m_brush = KisBrushSP(new KisGbrBrush(dev, r.x(), r.y(), r.width(), r.height()));
}
}
else {
// For each layer in the current image, create a new image, and add it to the list
QVector< QVector<KisPaintDevice*> > devices;
devices.push_back(QVector<KisPaintDevice*>());
int w = m_image->width();
int h = m_image->height();
KisImageBarrierLocker locker(m_image);
// We only loop over the rootLayer. Since we actually should have a layer selection
// list, no need to elaborate on that here and now
KoProperties properties;
properties.setProperty("visible", true);
QList<KisNodeSP> layers = m_image->root()->childNodes(QStringList("KisLayer"), properties);
KisNodeSP node;
Q_FOREACH (KisNodeSP node, layers) {
devices[0].push_back(node->projection().data());
}
QVector<KisParasite::SelectionMode> modes;
switch (comboBox2->currentIndex()) {
case 0: modes.push_back(KisParasite::Constant); break;
case 1: modes.push_back(KisParasite::Random); break;
case 2: modes.push_back(KisParasite::Incremental); break;
case 3: modes.push_back(KisParasite::Pressure); break;
case 4: modes.push_back(KisParasite::Angular); break;
default: modes.push_back(KisParasite::Incremental);
}
- m_brush = new KisImagePipeBrush(m_image->objectName(), w, h, devices, modes);
+ m_brush = KisBrushSP(new KisImagePipeBrush(m_image->objectName(), w, h, devices, modes));
}
static_cast<KisGbrBrush*>(m_brush.data())->setUseColorAsMask(colorAsMask->isChecked());
m_brush->setSpacing(spacingWidget->spacing());
m_brush->setAutoSpacing(spacingWidget->autoSpacingActive(), spacingWidget->autoSpacingCoeff());
m_brush->setFilename(TEMPORARY_FILENAME);
m_brush->setName(TEMPORARY_BRUSH_NAME);
m_brush->setValid(true);
}
diff --git a/plugins/paintops/libpaintop/kis_custom_brush_widget.h b/plugins/paintops/libpaintop/kis_custom_brush_widget.h
index ffeebf0207..52767e2464 100644
--- a/plugins/paintops/libpaintop/kis_custom_brush_widget.h
+++ b/plugins/paintops/libpaintop/kis_custom_brush_widget.h
@@ -1,81 +1,82 @@
/*
* Copyright (c) 2005 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_CUSTOM_BRUSH_H_
#define KIS_CUSTOM_BRUSH_H_
#include <QObject>
#include <QShowEvent>
-#include <KoResourceServerAdapter.h>
-
#include "ui_wdgcustombrush.h"
#include <kis_types.h>
#include <kis_brush.h>
+#include <KoResourceServer.h>
const QString TEMPORARY_FILENAME = "/tmp/temporaryKritaBrush.gbr";
const QString TEMPORARY_BRUSH_NAME = "Temporary custom brush";
const double DEFAULT_SPACING = 0.25;
class KoResource;
+
class KisWdgCustomBrush : public QDialog, public Ui::KisWdgCustomBrush
{
Q_OBJECT
public:
KisWdgCustomBrush(QWidget *parent) : QDialog(parent) {
setupUi(this);
}
};
class KisCustomBrushWidget : public KisWdgCustomBrush
{
Q_OBJECT
public:
KisCustomBrushWidget(QWidget *parent, const QString& caption, KisImageWSP image);
virtual ~KisCustomBrushWidget();
KisBrushSP brush();
void setImage(KisImageWSP image);
protected:
virtual void showEvent(QShowEvent *);
private Q_SLOTS:
void slotAddPredefined();
void slotUpdateCurrentBrush(int i = 0); // To connect with activated(int)
void slotSpacingChanged();
void slotUpdateUseColorAsMask(bool useColorAsMask);
+ void slotUpdateSaveButton();
Q_SIGNALS:
- void sigNewPredefinedBrush(KoResource *);
+ void sigNewPredefinedBrush(KoResourceSP );
private:
void createBrush();
void updatePreviewImage();
KisImageWSP m_image;
KisBrushSP m_brush;
- QSharedPointer<KoAbstractResourceServerAdapter> m_rServerAdapter;
+ KoResourceServer<KisBrush> *m_rServer {0};
};
#endif // KIS_CUSTOM_BRUSH_H_
diff --git a/plugins/paintops/libpaintop/kis_embedded_pattern_manager.cpp b/plugins/paintops/libpaintop/kis_embedded_pattern_manager.cpp
index 42c47291a8..1870d0fda3 100644
--- a/plugins/paintops/libpaintop/kis_embedded_pattern_manager.cpp
+++ b/plugins/paintops/libpaintop/kis_embedded_pattern_manager.cpp
@@ -1,104 +1,113 @@
/*
* 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_embedded_pattern_manager.h"
#include <QBuffer>
#include <QByteArray>
#include <KoResourceServerProvider.h>
#include <resources/KoPattern.h>
#include <kis_properties_configuration.h>
+#include <KisResourcesInterface.h>
struct KisEmbeddedPatternManager::Private {
- static KoPattern* tryLoadEmbeddedPattern(const KisPropertiesConfigurationSP setting) {
- KoPattern *pattern = 0;
+ static KoPatternSP tryLoadEmbeddedPattern(const KisPropertiesConfigurationSP setting) {
+ KoPatternSP pattern;
QByteArray ba = QByteArray::fromBase64(setting->getString("Texture/Pattern/Pattern").toLatin1());
QImage img;
img.loadFromData(ba, "PNG");
QString name = setting->getString("Texture/Pattern/Name");
QString filename = setting->getString("Texture/Pattern/PatternFileName");
if (name.isEmpty() || name != QFileInfo(name).fileName()) {
QFileInfo info(filename);
name = info.completeBaseName();
}
if (!img.isNull()) {
- pattern = new KoPattern(img, name, KoResourceServerProvider::instance()->patternServer()->saveLocation());
+ pattern = KoPatternSP(new KoPattern(img, name, KoResourceServerProvider::instance()->patternServer()->saveLocation()));
}
return pattern;
}
};
-void KisEmbeddedPatternManager::saveEmbeddedPattern(KisPropertiesConfigurationSP setting, const KoPattern *pattern)
+void KisEmbeddedPatternManager::saveEmbeddedPattern(KisPropertiesConfigurationSP setting, const KoPatternSP pattern)
{
QByteArray patternMD5 = pattern->md5();
/**
* The process of saving a pattern may be quite expensive, so
* we won't rewrite the pattern if has the same md5-sum and at
* least some data is present
*/
QByteArray existingMD5 = QByteArray::fromBase64(setting->getString("Texture/Pattern/PatternMD5").toLatin1());
QString existingPatternBase64 = setting->getString("Texture/Pattern/PatternMD5").toLatin1();
if (patternMD5 == existingMD5 && !existingPatternBase64.isEmpty()) {
return;
}
setting->setProperty("Texture/Pattern/PatternMD5", patternMD5.toBase64());
setting->setProperty("Texture/Pattern/PatternFileName", pattern->filename());
setting->setProperty("Texture/Pattern/Name", pattern->name());
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
pattern->pattern().save(&buffer, "PNG");
setting->setProperty("Texture/Pattern/Pattern", ba.toBase64());
}
-KoPattern* KisEmbeddedPatternManager::loadEmbeddedPattern(const KisPropertiesConfigurationSP setting)
+KoPatternSP KisEmbeddedPatternManager::tryFetchPattern(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface)
{
- KoPattern *pattern = 0;
- KoResourceServer<KoPattern> *server = KoResourceServerProvider::instance()->patternServer();
+ KoPatternSP pattern;
+ auto resourceSourceAdapter = resourcesInterface->source<KoPattern>(ResourceType::Patterns);
QByteArray md5 = QByteArray::fromBase64(setting->getString("Texture/Pattern/PatternMD5").toLatin1());
- pattern = server->resourceByMD5(md5);
+ pattern = resourceSourceAdapter.resourceForMD5(md5);
if (pattern) return pattern;
QString name = setting->getString("Texture/Pattern/Name");
- pattern = server->resourceByName(name);
+ pattern = resourceSourceAdapter.resourceForName(name);
if (pattern) return pattern;
QString fileName = setting->getString("Texture/Pattern/PatternFileName");
- pattern = server->resourceByFilename(fileName);
+ pattern = resourceSourceAdapter.resourceForFilename(fileName);
+
+ return pattern;
+}
+
+KoPatternSP KisEmbeddedPatternManager::loadEmbeddedPattern(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface)
+{
+ KoPatternSP pattern = tryFetchPattern(setting, resourcesInterface);
if (pattern) return pattern;
pattern = Private::tryLoadEmbeddedPattern(setting);
if (pattern) {
- KoResourceServerProvider::instance()->patternServer()->addResource(pattern, false);
+ // TODO: implement addition of the embedded resources in KisResourceLocator::resource()
+ //KoResourceServerProvider::instance()->patternServer()->addResource(pattern, false);
}
return pattern;
}
diff --git a/plugins/paintops/libpaintop/kis_embedded_pattern_manager.h b/plugins/paintops/libpaintop/kis_embedded_pattern_manager.h
index 1908de5bb4..a3bda14707 100644
--- a/plugins/paintops/libpaintop/kis_embedded_pattern_manager.h
+++ b/plugins/paintops/libpaintop/kis_embedded_pattern_manager.h
@@ -1,38 +1,41 @@
/*
* 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_EMBEDDED_PATTERN_MANAGER_H
#define __KIS_EMBEDDED_PATTERN_MANAGER_H
#include <kritapaintop_export.h>
#include <kis_properties_configuration.h>
+#include <KoPattern.h>
-class KoPattern;
class KoAbstractGradient;
+class KisResourcesInterface;
+using KisResourcesInterfaceSP = QSharedPointer<KisResourcesInterface>;
class PAINTOP_EXPORT KisEmbeddedPatternManager
{
public:
- static void saveEmbeddedPattern(KisPropertiesConfigurationSP setting, const KoPattern *pattern);
- static KoPattern* loadEmbeddedPattern(const KisPropertiesConfigurationSP setting);
+ static void saveEmbeddedPattern(KisPropertiesConfigurationSP setting, const KoPatternSP pattern);
+ static KoPatternSP loadEmbeddedPattern(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface);
+ static KoPatternSP tryFetchPattern(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface);
private:
struct Private;
};
#endif /* __KIS_EMBEDDED_PATTERN_MANAGER_H */
diff --git a/plugins/paintops/libpaintop/kis_outline_generation_policy.h b/plugins/paintops/libpaintop/kis_outline_generation_policy.h
index 386531ebe5..e311f71f2a 100644
--- a/plugins/paintops/libpaintop/kis_outline_generation_policy.h
+++ b/plugins/paintops/libpaintop/kis_outline_generation_policy.h
@@ -1,63 +1,65 @@
/*
* 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_OUTLINE_GENERATION_POLICY_H
#define __KIS_OUTLINE_GENERATION_POLICY_H
#include <kis_current_outline_fetcher.h>
/**
* This is a policy class that adds an ability to have a Outline Generator
* to a KisPaintOpSettings-based class.
*
* \see Andrei Alexandrescu "Modern C++ Design: Generic Programming and Design Patterns Applied"
*/
template <class ParentClass>
class KisOutlineGenerationPolicy : public ParentClass
{
public:
- KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::Options options)
- : m_outlineFetcher(options)
+ KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::Options options,
+ KisResourcesInterfaceSP resourcesInterface)
+ : ParentClass(resourcesInterface),
+ m_outlineFetcher(options)
{
}
~KisOutlineGenerationPolicy() override
{
}
KisOutlineGenerationPolicy(const KisOutlineGenerationPolicy &rhs)
: ParentClass(rhs)
{
}
const KisCurrentOutlineFetcher *outlineFetcher() const
{
return &m_outlineFetcher;
}
void onPropertyChanged() override
{
m_outlineFetcher.setDirty();
ParentClass::onPropertyChanged();
}
private:
KisCurrentOutlineFetcher m_outlineFetcher;
};
#endif /* __KIS_OUTLINE_GENERATION_POLICY_H */
diff --git a/plugins/paintops/libpaintop/kis_pressure_gradient_option.cpp b/plugins/paintops/libpaintop/kis_pressure_gradient_option.cpp
index 49d104bccc..f0510df834 100644
--- a/plugins/paintops/libpaintop/kis_pressure_gradient_option.cpp
+++ b/plugins/paintops/libpaintop/kis_pressure_gradient_option.cpp
@@ -1,36 +1,36 @@
/* This file is part of the KDE project
* 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.
*/
#include "kis_pressure_gradient_option.h"
#include <kis_curve_label.h>
#include <klocalizedstring.h>
#include <KoColor.h>
#include <resources/KoAbstractGradient.h>
KisPressureGradientOption::KisPressureGradientOption()
: KisCurveOption("Gradient", KisPaintOpOption::GENERAL, false)
{
}
-void KisPressureGradientOption::apply(KoColor& color, const KoAbstractGradient* gradient, const KisPaintInformation& info) const
+void KisPressureGradientOption::apply(KoColor& color, const KoAbstractGradientSP gradient, const KisPaintInformation& info) const
{
if (isChecked() && gradient) {
gradient->colorAt(color, computeSizeLikeValue(info));
}
}
diff --git a/plugins/paintops/libpaintop/kis_pressure_gradient_option.h b/plugins/paintops/libpaintop/kis_pressure_gradient_option.h
index 53a61117d5..6940b7f9a1 100644
--- a/plugins/paintops/libpaintop/kis_pressure_gradient_option.h
+++ b/plugins/paintops/libpaintop/kis_pressure_gradient_option.h
@@ -1,37 +1,39 @@
/* This file is part of the KDE project
* 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 KIS_PRESSURE_GRADIENT_OPTION_H
#define KIS_PRESSURE_GRADIENT_OPTION_H
+#include <KoAbstractGradient.h>
+
#include "kis_curve_option.h"
#include <brushengine/kis_paint_information.h>
#include <kritapaintop_export.h>
class KoColor;
-class KoAbstractGradient;
+
class PAINTOP_EXPORT KisPressureGradientOption: public KisCurveOption
{
public:
KisPressureGradientOption();
- void apply(KoColor& color, const KoAbstractGradient* gradient, const KisPaintInformation& info) const;
+ void apply(KoColor& color, const KoAbstractGradientSP gradient, const KisPaintInformation& info) const;
};
#endif // KIS_PRESSURE_GRADIENT_OPTION_H
diff --git a/plugins/paintops/libpaintop/kis_simple_paintop_factory.h b/plugins/paintops/libpaintop/kis_simple_paintop_factory.h
index 68b4900531..2ae63cc19c 100644
--- a/plugins/paintops/libpaintop/kis_simple_paintop_factory.h
+++ b/plugins/paintops/libpaintop/kis_simple_paintop_factory.h
@@ -1,132 +1,187 @@
/*
* Copyright (c) 2009 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_SIMPLE_PAINTOP_FACTORY_H
#define KIS_SIMPLE_PAINTOP_FACTORY_H
#include <brushengine/kis_paintop_factory.h>
#include <brushengine/kis_paintop_settings.h>
#include <kis_icon.h>
+#include <KisCppQuirks.h>
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
-#include <boost/type_traits.hpp>
-#include <boost/utility/enable_if.hpp>
+namespace detail {
+
+template< class, class = std::void_t<> >
+struct has_preinitialize_statically : std::false_type { };
+
+template< class T >
+struct has_preinitialize_statically<T, std::void_t<decltype(std::declval<T>().preinitializeOpStatically(KisPaintOpSettingsSP()))>> : std::true_type { };
+
template <typename T>
-struct __impl_has_typedef_needs_preinitialization {
- typedef char yes[1];
- typedef char no[2];
+void preinitializeOpStatically(const KisPaintOpSettingsSP settings, typename std::enable_if_t<has_preinitialize_statically<T>::value> * = 0)
+{
+ T::preinitializeOpStatically(settings);
+}
- template <typename C>
- static yes& test(typename C::needs_preinitialization*);
+template <typename T>
+void preinitializeOpStatically(const KisPaintOpSettingsSP settings, typename std::enable_if_t<!has_preinitialize_statically<T>::value> * = 0)
+{
+ Q_UNUSED(settings);
+ // noop
+}
- template <typename>
- static no& test(...);
+}
- static const bool value = sizeof(test<T>(0)) == sizeof(yes);
-};
+#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
+
+namespace detail {
+
+template< class, class = std::void_t<> >
+struct has_prepare_linked_resources : std::false_type { };
+
+template< class T >
+struct has_prepare_linked_resources<T, std::void_t<decltype(std::declval<T>().prepareLinkedResources(KisPaintOpSettingsSP(),KisResourcesInterfaceSP()))>> : std::true_type { };
+
+template <typename T>
+QList<KoResourceSP> prepareLinkedResources(const KisPaintOpSettingsSP settings,
+ KisResourcesInterfaceSP resourcesInterface,
+ std::enable_if_t<has_prepare_linked_resources<T>::value> * = 0)
+{
+ return T::prepareLinkedResources(settings, resourcesInterface);
+}
template <typename T>
-struct has_typedef_needs_preinitialization
- : public boost::integral_constant <bool, __impl_has_typedef_needs_preinitialization<T>::value>
-{};
+QList<KoResourceSP> prepareLinkedResources(const KisPaintOpSettingsSP settings,
+ KisResourcesInterfaceSP resourcesInterface,
+ std::enable_if_t<!has_prepare_linked_resources<T>::value> * = 0)
+{
+ Q_UNUSED(settings);
+ Q_UNUSED(resourcesInterface);
+ // noop
+ return {};
+}
+
+
+template< class, class = std::void_t<> >
+struct has_prepare_embedded_resources : std::false_type { };
+
+template< class T >
+struct has_prepare_embedded_resources<T, std::void_t<decltype(std::declval<T>().prepareEmbeddedResources(KisPaintOpSettingsSP(),KisResourcesInterfaceSP()))>> : std::true_type { };
template <typename T>
-void preinitializeOpStatically(const KisPaintOpSettingsSP settings, typename boost::enable_if<has_typedef_needs_preinitialization<T> >::type * = 0)
+QList<KoResourceSP> prepareEmbeddedResources(const KisPaintOpSettingsSP settings,
+ KisResourcesInterfaceSP resourcesInterface,
+ std::enable_if_t<has_prepare_embedded_resources<T>::value> * = 0)
{
- T::preinitializeOpStatically(settings);
+ return T::prepareEmbeddedResources(settings, resourcesInterface);
}
template <typename T>
-void preinitializeOpStatically(const KisPaintOpSettingsSP settings, typename boost::disable_if<has_typedef_needs_preinitialization<T> >::type * = 0)
+QList<KoResourceSP> prepareEmbeddedResources(const KisPaintOpSettingsSP settings,
+ KisResourcesInterfaceSP resourcesInterface,
+ std::enable_if_t<!has_prepare_embedded_resources<T>::value> * = 0)
{
Q_UNUSED(settings);
+ Q_UNUSED(resourcesInterface);
+ // noop
+ return {};
}
-#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
+
+}
/**
* Base template class for simple paintop factories
*/
template <class Op, class OpSettings, class OpSettingsWidget> class KisSimplePaintOpFactory : public KisPaintOpFactory
{
public:
KisSimplePaintOpFactory(const QString& id, const QString& name, const QString& category,
const QString& pixmap, const QString& model = QString(),
const QStringList& whiteListedCompositeOps = QStringList(), int priority = 100)
: KisPaintOpFactory(whiteListedCompositeOps)
, m_id(id)
, m_name(name)
, m_category(category)
, m_pixmap(pixmap)
, m_model(model) {
setPriority(priority);
}
~KisSimplePaintOpFactory() override {
}
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
void preinitializePaintOpIfNeeded(const KisPaintOpSettingsSP settings) override {
- preinitializeOpStatically<Op>(settings);
+ detail::preinitializeOpStatically<Op>(settings);
}
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
KisPaintOp *createOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image) override {
KisPaintOp * op = new Op(settings, painter, node, image);
Q_CHECK_PTR(op);
return op;
}
- KisPaintOpSettingsSP settings() override {
- KisPaintOpSettingsSP settings = new OpSettings();
+ QList<KoResourceSP> prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface) {
+ return detail::prepareLinkedResources<Op>(settings, resourcesInterface);
+ }
+
+ QList<KoResourceSP> prepareEmbeddedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface) {
+ return detail::prepareEmbeddedResources<Op>(settings, resourcesInterface);
+ }
+
+ KisPaintOpSettingsSP createSettings(KisResourcesInterfaceSP resourcesInterface) override {
+ KisPaintOpSettingsSP settings = new OpSettings(resourcesInterface);
settings->setModelName(m_model);
return settings;
}
KisPaintOpConfigWidget* createConfigWidget(QWidget* parent) override {
return new OpSettingsWidget(parent);
}
QString id() const override {
return m_id;
}
QString name() const override {
return m_name;
}
QIcon icon() override {
return KisIconUtils::loadIcon(id());
}
QString category() const override {
return m_category;
}
private:
QString m_id;
QString m_name;
QString m_category;
QString m_pixmap;
QString m_model;
};
#endif // KIS_SIMPLE_PAINTOP_FACTORY_H
diff --git a/plugins/paintops/libpaintop/kis_texture_chooser.cpp b/plugins/paintops/libpaintop/kis_texture_chooser.cpp
index e3dbc1562e..51342e7f67 100644
--- a/plugins/paintops/libpaintop/kis_texture_chooser.cpp
+++ b/plugins/paintops/libpaintop/kis_texture_chooser.cpp
@@ -1,69 +1,69 @@
/*
* 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_texture_chooser.h"
#include "kis_texture_option.h"
KisTextureChooser::KisTextureChooser(QWidget *parent)
: QWidget(parent)
{
setupUi(this);
textureSelectorWidget->setGrayscalePreview(true);
- textureSelectorWidget->setCurrentItem(0, 0);
+ textureSelectorWidget->setCurrentItem(0);
scaleSlider->setRange(0.0, 2.0, 2);
scaleSlider->setValue(1.0);
scaleSlider->addMultiplier(0.1);
scaleSlider->addMultiplier(2);
scaleSlider->addMultiplier(10);
brightnessSlider->setRange(-1.0, 1.0, 2);
brightnessSlider->setValue(0.0);
brightnessSlider->setToolTip(i18n("Makes texture lighter or darker"));
contrastSlider->setRange(0.0, 2.0, 2);
contrastSlider->setValue(1.0);
offsetSliderX->setSuffix(i18n(" px"));
offsetSliderY->setSuffix(i18n(" px"));
QStringList texturingModes;
texturingModes << i18n("Multiply") << i18n("Subtract");
cmbTexturingMode->addItems(texturingModes);
cmbTexturingMode->setCurrentIndex(KisTextureProperties::SUBTRACT);
QStringList cutOffPolicies;
cutOffPolicies << i18n("Cut Off Disabled") << i18n("Cut Off Brush") << i18n("Cut Off Pattern");
cmbCutoffPolicy->addItems(cutOffPolicies);
cutoffSlider->setMinimumSize(256, 30);
cutoffSlider->enableGamma(false);
cutoffSlider->setToolTip(i18n("When pattern texture values are outside the range specified"
" by the slider, the cut-off policy will be applied."));
chkInvert->setChecked(false);
}
KisTextureChooser::~KisTextureChooser()
{
}
diff --git a/plugins/paintops/libpaintop/kis_texture_option.cpp b/plugins/paintops/libpaintop/kis_texture_option.cpp
index 4a851c23a8..de4511ca62 100644
--- a/plugins/paintops/libpaintop/kis_texture_option.cpp
+++ b/plugins/paintops/libpaintop/kis_texture_option.cpp
@@ -1,273 +1,289 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2012
* Copyright (C) Mohit Goyal <mohit.bits2011@gmail.com>, (C) 2014
*
* 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_texture_option.h"
#include <QWidget>
#include <QString>
#include <QByteArray>
#include <QCheckBox>
#include <QBuffer>
#include <QFormLayout>
#include <QLabel>
#include <QComboBox>
#include <QTransform>
#include <QPainter>
#include <QBoxLayout>
#include <klocalizedstring.h>
#include <kis_pattern_chooser.h>
#include <kis_slider_spin_box.h>
#include <kis_multipliers_double_slider_spinbox.h>
#include <resources/KoPattern.h>
#include <kis_paint_device.h>
#include <kis_fill_painter.h>
#include <kis_painter.h>
#include <kis_iterator_ng.h>
#include <kis_fixed_paint_device.h>
#include <KisGradientSlider.h>
#include "kis_embedded_pattern_manager.h"
#include <brushengine/kis_paintop_lod_limitations.h>
#include "kis_texture_chooser.h"
#include <time.h>
-
-
+#include "kis_signals_blocker.h"
+#include <KisGlobalResourcesInterface.h>
KisTextureOption::KisTextureOption()
: KisPaintOpOption(KisPaintOpOption::TEXTURE, true)
, m_textureOptions(new KisTextureChooser())
{
setObjectName("KisTextureOption");
setConfigurationPage(m_textureOptions);
- connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResource*)), SLOT(resetGUI(KoResource*)));
- connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResource*)), SLOT(emitSettingChanged()));
+ connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResourceSP )), SLOT(resetGUI(KoResourceSP )));
+ connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResourceSP )), SLOT(emitSettingChanged()));
connect(m_textureOptions->scaleSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
connect(m_textureOptions->brightnessSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
connect(m_textureOptions->contrastSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
connect(m_textureOptions->offsetSliderX, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged()));
connect(m_textureOptions->randomOffsetX, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_textureOptions->randomOffsetY, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_textureOptions->offsetSliderY, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged()));
connect(m_textureOptions->cmbTexturingMode, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged()));
connect(m_textureOptions->cmbCutoffPolicy, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged()));
connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedBlack(int)), SLOT(emitSettingChanged()));
connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedWhite(int)), SLOT(emitSettingChanged()));
connect(m_textureOptions->chkInvert, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
resetGUI(m_textureOptions->textureSelectorWidget->currentResource());
}
KisTextureOption::~KisTextureOption()
{
delete m_textureOptions;
}
void KisTextureOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const
{
- m_textureOptions->textureSelectorWidget->blockSignals(true); // Checking
- if (!m_textureOptions->textureSelectorWidget->currentResource()) return;
- KoPattern *pattern = static_cast<KoPattern*>(m_textureOptions->textureSelectorWidget->currentResource());
- m_textureOptions->textureSelectorWidget->blockSignals(false); // Checking
- if (!pattern) return;
+ KoPatternSP pattern;
+
+ {
+ KisSignalsBlocker b(m_textureOptions->textureSelectorWidget);
+ KoResourceSP resource = m_textureOptions->textureSelectorWidget->currentResource();
+ if (!resource) return;
+
+ pattern = resource.staticCast<KoPattern>();
+ if (!pattern) return;
+ }
setting->setProperty("Texture/Pattern/Enabled", isChecked());
if (!isChecked()) {
return;
}
qreal scale = m_textureOptions->scaleSlider->value();
qreal brightness = m_textureOptions->brightnessSlider->value();
qreal contrast = m_textureOptions->contrastSlider->value();
int offsetX = m_textureOptions->offsetSliderX->value();
if (m_textureOptions ->randomOffsetX->isChecked()) {
m_textureOptions->offsetSliderX ->setEnabled(false);
m_textureOptions->offsetSliderX ->blockSignals(true);
m_textureOptions->offsetSliderX ->setValue(offsetX);
m_textureOptions->offsetSliderX ->blockSignals(false);
}
else {
m_textureOptions->offsetSliderX ->setEnabled(true);
}
int offsetY = m_textureOptions->offsetSliderY->value();
if (m_textureOptions ->randomOffsetY->isChecked()) {
m_textureOptions->offsetSliderY ->setEnabled(false);
m_textureOptions->offsetSliderY ->blockSignals(true);
m_textureOptions->offsetSliderY ->setValue(offsetY);
m_textureOptions->offsetSliderY ->blockSignals(false);
}
else {
m_textureOptions->offsetSliderY ->setEnabled(true);
}
int texturingMode = m_textureOptions->cmbTexturingMode->currentIndex();
bool invert = (m_textureOptions->chkInvert->checkState() == Qt::Checked);
setting->setProperty("Texture/Pattern/Scale", scale);
setting->setProperty("Texture/Pattern/Brightness", brightness);
setting->setProperty("Texture/Pattern/Contrast", contrast);
setting->setProperty("Texture/Pattern/OffsetX", offsetX);
setting->setProperty("Texture/Pattern/OffsetY", offsetY);
setting->setProperty("Texture/Pattern/TexturingMode", texturingMode);
setting->setProperty("Texture/Pattern/CutoffLeft", m_textureOptions->cutoffSlider->black());
setting->setProperty("Texture/Pattern/CutoffRight", m_textureOptions->cutoffSlider->white());
setting->setProperty("Texture/Pattern/CutoffPolicy", m_textureOptions->cmbCutoffPolicy->currentIndex());
setting->setProperty("Texture/Pattern/Invert", invert);
setting->setProperty("Texture/Pattern/MaximumOffsetX",m_textureOptions->offsetSliderX ->maximum());
setting->setProperty("Texture/Pattern/MaximumOffsetY",m_textureOptions->offsetSliderY ->maximum());
setting->setProperty("Texture/Pattern/isRandomOffsetX",m_textureOptions ->randomOffsetX ->isChecked());
setting->setProperty("Texture/Pattern/isRandomOffsetY",m_textureOptions ->randomOffsetY ->isChecked());
KisEmbeddedPatternManager::saveEmbeddedPattern(setting, pattern);
}
void KisTextureOption::readOptionSetting(const KisPropertiesConfigurationSP setting)
{
setChecked(setting->getBool("Texture/Pattern/Enabled"));
if (!isChecked()) {
return;
}
- KoPattern *pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting);
+ KoPatternSP pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting, KisGlobalResourcesInterface::instance());
if (!pattern) {
- pattern = static_cast<KoPattern*>(m_textureOptions->textureSelectorWidget->currentResource());
+ pattern =m_textureOptions->textureSelectorWidget->currentResource().staticCast<KoPattern>();
}
m_textureOptions->textureSelectorWidget->setCurrentPattern(pattern);
m_textureOptions->scaleSlider->setValue(setting->getDouble("Texture/Pattern/Scale", 1.0));
m_textureOptions->brightnessSlider->setValue(setting->getDouble("Texture/Pattern/Brightness"));
m_textureOptions->contrastSlider->setValue(setting->getDouble("Texture/Pattern/Contrast", 1.0));
m_textureOptions->offsetSliderX->setValue(setting->getInt("Texture/Pattern/OffsetX"));
m_textureOptions->offsetSliderY->setValue(setting->getInt("Texture/Pattern/OffsetY"));
m_textureOptions->randomOffsetX->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetX"));
m_textureOptions->randomOffsetY->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetY"));
m_textureOptions->cmbTexturingMode->setCurrentIndex(setting->getInt("Texture/Pattern/TexturingMode", KisTextureProperties::MULTIPLY));
m_textureOptions->cmbCutoffPolicy->setCurrentIndex(setting->getInt("Texture/Pattern/CutoffPolicy"));
m_textureOptions->cutoffSlider->slotModifyBlack(setting->getInt("Texture/Pattern/CutoffLeft", 0));
m_textureOptions->cutoffSlider->slotModifyWhite(setting->getInt("Texture/Pattern/CutoffRight", 255));
m_textureOptions->chkInvert->setChecked(setting->getBool("Texture/Pattern/Invert"));
}
void KisTextureOption::lodLimitations(KisPaintopLodLimitations *l) const
{
l->limitations << KoID("texture-pattern", i18nc("PaintOp instant preview limitation", "Texture->Pattern (low quality preview)"));
}
-void KisTextureOption::resetGUI(KoResource* res)
+void KisTextureOption::resetGUI(KoResourceSP res)
{
- KoPattern *pattern = static_cast<KoPattern *>(res);
+ KoPatternSP pattern = res.staticCast<KoPattern>();
if (!pattern) return;
m_textureOptions->offsetSliderX->setRange(0, pattern->pattern().width() / 2);
m_textureOptions->offsetSliderY->setRange(0, pattern->pattern().height() / 2);
}
/**********************************************************************/
/* KisTextureProperties */
/**********************************************************************/
KisTextureProperties::KisTextureProperties(int levelOfDetail)
: m_levelOfDetail(levelOfDetail)
{
}
-void KisTextureProperties::fillProperties(const KisPropertiesConfigurationSP setting)
+void KisTextureProperties::fillProperties(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface)
{
-
if (!setting->hasProperty("Texture/Pattern/PatternMD5")) {
m_enabled = false;
return;
}
m_maskInfo = toQShared(new KisTextureMaskInfo(m_levelOfDetail));
- if (!m_maskInfo->fillProperties(setting)) {
+ if (!m_maskInfo->fillProperties(setting, resourcesInterface)) {
warnKrita << "WARNING: Couldn't load the pattern for a stroke";
m_enabled = false;
return;
}
m_maskInfo = KisTextureMaskInfoCache::instance()->fetchCachedTextureInfo(m_maskInfo);
m_enabled = setting->getBool("Texture/Pattern/Enabled", false);
m_offsetX = setting->getInt("Texture/Pattern/OffsetX");
m_offsetY = setting->getInt("Texture/Pattern/OffsetY");
m_texturingMode = (TexturingMode) setting->getInt("Texture/Pattern/TexturingMode", MULTIPLY);
m_strengthOption.readOptionSetting(setting);
m_strengthOption.resetAllSensors();
}
+QList<KoResourceSP> KisTextureProperties::prepareEmbeddedResources(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface)
+{
+ QList<KoResourceSP> resources;
+
+ KoPatternSP pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting, resourcesInterface);
+ if (pattern) {
+ resources << pattern;
+ }
+
+ return resources;
+}
+
void KisTextureProperties::apply(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation & info)
{
if (!m_enabled) return;
KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
QRect rect = dab->bounds();
KisPaintDeviceSP mask = m_maskInfo->mask();
const QRect maskBounds = m_maskInfo->maskBounds();
KIS_SAFE_ASSERT_RECOVER_RETURN(mask);
int x = offset.x() % maskBounds.width() - m_offsetX;
int y = offset.y() % maskBounds.height() - m_offsetY;
KisFillPainter fillPainter(fillDevice);
fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds);
fillPainter.end();
qreal pressure = m_strengthOption.apply(info);
quint8 *dabData = dab->data();
KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width());
for (int row = 0; row < rect.height(); ++row) {
for (int col = 0; col < rect.width(); ++col) {
if (m_texturingMode == MULTIPLY) {
dab->colorSpace()->multiplyAlpha(dabData, quint8(*iter->oldRawData() * pressure), 1);
}
else {
int pressureOffset = (1.0 - pressure) * 255;
qint16 maskA = *iter->oldRawData() + pressureOffset;
quint8 dabA = dab->colorSpace()->opacityU8(dabData);
dabA = qMax(0, (qint16)dabA - maskA);
dab->colorSpace()->setOpacity(dabData, dabA, 1);
}
iter->nextPixel();
dabData += dab->pixelSize();
}
iter->nextRow();
}
}
diff --git a/plugins/paintops/libpaintop/kis_texture_option.h b/plugins/paintops/libpaintop/kis_texture_option.h
index 90a6194708..748e493fdc 100644
--- a/plugins/paintops/libpaintop/kis_texture_option.h
+++ b/plugins/paintops/libpaintop/kis_texture_option.h
@@ -1,98 +1,100 @@
/* 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 KisResourcesInterface;
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
+ void resetGUI(KoResourceSP ); /// 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);
+ void fillProperties(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface);
+ QList<KoResourceSP> prepareEmbeddedResources(const KisPropertiesConfigurationSP setting, KisResourcesInterfaceSP resourcesInterface);
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/paintops/libpaintop/tests/kis_embedded_pattern_manager_test.cpp b/plugins/paintops/libpaintop/tests/kis_embedded_pattern_manager_test.cpp
index 2d06a50608..006824438b 100644
--- a/plugins/paintops/libpaintop/tests/kis_embedded_pattern_manager_test.cpp
+++ b/plugins/paintops/libpaintop/tests/kis_embedded_pattern_manager_test.cpp
@@ -1,213 +1,210 @@
/*
* 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_embedded_pattern_manager_test.h"
#include <QTest>
#include <QPainter>
#include <KoResourceServerProvider.h>
#include <resources/KoPattern.h>
#include "kis_embedded_pattern_manager.h"
#include <kis_properties_configuration.h>
#include <KisResourceServerProvider.h>
+#include <KisGlobalResourcesInterface.h>
#include "sdk/tests/kistest.h"
-KoPattern *KisEmbeddedPatternManagerTest::createPattern()
+KoPatternSP KisEmbeddedPatternManagerTest::createPattern()
{
QImage image(512, 512, QImage::Format_ARGB32);
image.fill(255);
QPainter gc(&image);
gc.fillRect(100, 100, 312, 312, Qt::red);
- KoPattern *pattern = new KoPattern(image,
+ KoPatternSP pattern (new KoPattern(image,
"__test_pattern",
- KoResourceServerProvider::instance()->patternServer()->saveLocation());
+ KoResourceServerProvider::instance()->patternServer()->saveLocation()));
return pattern;
}
void KisEmbeddedPatternManagerTest::testRoundTrip()
{
- KoPattern *pattern = createPattern();
+ KoPatternSP pattern = createPattern();
KisPropertiesConfigurationSP config(new KisPropertiesConfiguration);
KisEmbeddedPatternManager::saveEmbeddedPattern(config, pattern);
- KoPattern *newPattern = KisEmbeddedPatternManager::loadEmbeddedPattern(config);
+ KoPatternSP newPattern = KisEmbeddedPatternManager::loadEmbeddedPattern(config, KisGlobalResourcesInterface::instance());
QCOMPARE(newPattern->pattern(), pattern->pattern());
QCOMPARE(newPattern->name(), pattern->name());
QCOMPARE(QFileInfo(newPattern->filename()).fileName(),
QFileInfo(pattern->filename()).fileName());
- delete pattern;
- // will be deleted by the server
- // delete newPattern;
}
void KisEmbeddedPatternManagerTest::init()
{
- Q_FOREACH(KoPattern *pa, KoResourceServerProvider::instance()->patternServer()->resources()) {
+ Q_FOREACH(KoPatternSP pa, KoResourceServerProvider::instance()->patternServer()->resources()) {
if (pa) {
KoResourceServerProvider::instance()->patternServer()->removeResourceFile(pa->filename());
}
}
}
KisPropertiesConfigurationSP KisEmbeddedPatternManagerTest::createXML(NameStatus nameStatus, bool hasMd5)
{
KisPropertiesConfigurationSP setting(new KisPropertiesConfiguration);
switch (nameStatus) {
case VALID: {
setting->setProperty("Texture/Pattern/PatternFileName", "./__test_pattern_path.pat");
setting->setProperty("Texture/Pattern/Name", "__test_pattern");
break;
}
case PATH: {
QString path = KoResourceServerProvider::instance()->patternServer()->saveLocation() + "/__test_pattern.pat";
setting->setProperty("Texture/Pattern/PatternFileName", path);
setting->setProperty("Texture/Pattern/Name", "__test_pattern");
break;
}
case EMPTY: {
setting->setProperty("Texture/Pattern/PatternFileName", "./__test_pattern_path.pat");
setting->setProperty("Texture/Pattern/Name", "");
break;
}
}
{
- KoPattern *pattern = createPattern();
+ KoPatternSP pattern = createPattern();
if (hasMd5) {
QByteArray patternMD5 = pattern->md5();
Q_ASSERT(!patternMD5.isEmpty());
setting->setProperty("Texture/Pattern/PatternMD5", patternMD5.toBase64());
}
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
pattern->pattern().save(&buffer, "PNG");
setting->setProperty("Texture/Pattern/Pattern", ba.toBase64());
- delete pattern;
}
return setting;
}
-KoPattern* findOnServer(QByteArray md5)
+KoPatternSP findOnServer(QByteArray md5)
{
- KoPattern *pattern = 0;
+ KoPatternSP pattern;
if (!md5.isEmpty()) {
return KoResourceServerProvider::instance()->patternServer()->resourceByMD5(md5);
}
return pattern;
}
void KisEmbeddedPatternManagerTest::checkOneConfig(NameStatus nameStatus, bool hasMd5, QString expectedName, bool isOnServer)
{
- QScopedPointer<KoPattern> basePattern(createPattern());
+ QSharedPointer<KoPattern> basePattern(createPattern());
- KoPattern *initialPattern = findOnServer(basePattern->md5());
+ KoPatternSP initialPattern = findOnServer(basePattern->md5());
QCOMPARE((bool)initialPattern, isOnServer);
KisPropertiesConfigurationSP setting = createXML(nameStatus, hasMd5);
- KoPattern *pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting);
+ KoPatternSP pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting, KisGlobalResourcesInterface::instance());
QVERIFY(pattern);
QCOMPARE(pattern->pattern(), basePattern->pattern());
QVERIFY(pattern->name().startsWith(expectedName));
QFileInfo info(pattern->filename());
QVERIFY(info.completeBaseName().startsWith(expectedName));
QCOMPARE(info.dir().path(), QDir(KoResourceServerProvider::instance()->patternServer()->saveLocation()).path());
// We can only find things on the server by name or by md5; the file path as an identifier does not work.
if (isOnServer && nameStatus != EMPTY && !hasMd5) {
QCOMPARE(initialPattern, pattern);
}
// will be deleted by the server
// delete pattern;
}
void KisEmbeddedPatternManagerTest::testLoadingNotOnServerValidName()
{
checkOneConfig(VALID, false, "__test_pattern", false);
}
void KisEmbeddedPatternManagerTest::testLoadingNotOnServerEmptyName()
{
checkOneConfig(EMPTY, false, "__test_pattern_path", false);
}
void KisEmbeddedPatternManagerTest::testLoadingNotOnServerPathName()
{
checkOneConfig(PATH, false, "__test_pattern", false);
}
void KisEmbeddedPatternManagerTest::testLoadingOnServerValidName()
{
KoResourceServerProvider::instance()->patternServer()->addResource(createPattern(), false);
checkOneConfig(VALID, false, "__test_pattern", true);
}
void KisEmbeddedPatternManagerTest::testLoadingOnServerEmptyName()
{
KoResourceServerProvider::instance()->patternServer()->addResource(createPattern(), false);
checkOneConfig(EMPTY, false, "__test_pattern_path", true);
}
void KisEmbeddedPatternManagerTest::testLoadingOnServerPathName()
{
KoResourceServerProvider::instance()->patternServer()->addResource(createPattern(), false);
checkOneConfig(PATH, false, "__test_pattern", true);
}
void KisEmbeddedPatternManagerTest::testLoadingOnServerValidNameMd5()
{
KoResourceServerProvider::instance()->patternServer()->addResource(createPattern(), true);
checkOneConfig(VALID, true, "__test_pattern", true);
}
void KisEmbeddedPatternManagerTest::testLoadingOnServerEmptyNameMd5()
{
KoResourceServerProvider::instance()->patternServer()->addResource(createPattern(), true);
checkOneConfig(EMPTY, true, "__test_pattern", true);
}
void KisEmbeddedPatternManagerTest::testLoadingOnServerPathNameMd5()
{
KoResourceServerProvider::instance()->patternServer()->addResource(createPattern(), false);
checkOneConfig(PATH, true, "__test_pattern", true);
}
KISTEST_MAIN(KisEmbeddedPatternManagerTest)
diff --git a/plugins/paintops/libpaintop/tests/kis_embedded_pattern_manager_test.h b/plugins/paintops/libpaintop/tests/kis_embedded_pattern_manager_test.h
index 8a363bcbbc..d31963f998 100644
--- a/plugins/paintops/libpaintop/tests/kis_embedded_pattern_manager_test.h
+++ b/plugins/paintops/libpaintop/tests/kis_embedded_pattern_manager_test.h
@@ -1,60 +1,60 @@
/*
* 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_EMBEDDED_PATTERN_MANAGER_TEST_H
#define __KIS_EMBEDDED_PATTERN_MANAGER_TEST_H
#include <QtTest>
#include <kis_properties_configuration.h>
-class KoPattern;
+#include <KoPattern.h>
class KisEmbeddedPatternManagerTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testRoundTrip();
void init();
void testLoadingNotOnServerValidName();
void testLoadingNotOnServerEmptyName();
void testLoadingNotOnServerPathName();
void testLoadingOnServerValidName();
void testLoadingOnServerEmptyName();
void testLoadingOnServerPathName();
void testLoadingOnServerValidNameMd5();
void testLoadingOnServerEmptyNameMd5();
void testLoadingOnServerPathNameMd5();
private:
enum NameStatus {
VALID,
PATH,
EMPTY
};
void checkOneConfig(NameStatus nameStatus, bool hasMd5, QString expectedName, bool isOnServer);
KisPropertiesConfigurationSP createXML(NameStatus nameStatus, bool hasMd5);
- KoPattern *createPattern();
+ KoPatternSP createPattern();
};
#endif /* __KIS_EMBEDDED_PATTERN_MANAGER_TEST_H */
diff --git a/plugins/paintops/particle/kis_particle_paintop_settings.cpp b/plugins/paintops/particle/kis_particle_paintop_settings.cpp
index 4d3d646d03..5ce49c2f83 100644
--- a/plugins/paintops/particle/kis_particle_paintop_settings.cpp
+++ b/plugins/paintops/particle/kis_particle_paintop_settings.cpp
@@ -1,256 +1,257 @@
/*
* 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.
*/
#include "kis_particle_paintop_settings.h"
#include "kis_particle_paintop_settings_widget.h"
#include "kis_particleop_option.h"
#include <kis_paint_action_type_option.h>
#include <kis_airbrush_option_widget.h>
struct KisParticlePaintOpSettings::Private
{
QList<KisUniformPaintOpPropertyWSP> uniformProperties;
};
-KisParticlePaintOpSettings::KisParticlePaintOpSettings()
- : m_d(new Private)
+KisParticlePaintOpSettings::KisParticlePaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
+ : KisNoSizePaintOpSettings(resourcesInterface),
+ m_d(new Private)
{
}
KisParticlePaintOpSettings::~KisParticlePaintOpSettings()
{
}
bool KisParticlePaintOpSettings::lodSizeThresholdSupported() const
{
return false;
}
bool KisParticlePaintOpSettings::paintIncremental()
{
return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP;
}
#include <brushengine/kis_slider_based_paintop_property.h>
#include "kis_paintop_preset.h"
#include "kis_paintop_settings_update_proxy.h"
#include "kis_standard_uniform_properties_factory.h"
QList<KisUniformPaintOpPropertySP> KisParticlePaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings)
{
QList<KisUniformPaintOpPropertySP> props =
listWeakToStrong(m_d->uniformProperties);
if (props.isEmpty()) {
{
KisIntSliderBasedPaintOpPropertyCallback *prop =
new KisIntSliderBasedPaintOpPropertyCallback(
KisIntSliderBasedPaintOpPropertyCallback::Int,
"particle_particles",
i18n("Particles"),
settings, 0);
prop->setRange(1, 500);
prop->setSingleStep(1);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
ParticleOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(int(option.particle_count));
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
ParticleOption option;
option.readOptionSetting(prop->settings().data());
option.particle_count = prop->value().toInt();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisDoubleSliderBasedPaintOpPropertyCallback *prop =
new KisDoubleSliderBasedPaintOpPropertyCallback(
KisDoubleSliderBasedPaintOpPropertyCallback::Double,
"particle_opecityweight",
i18n("Opacity Weight"),
settings, 0);
prop->setRange(0.01, 1.0);
prop->setSingleStep(0.01);
prop->setDecimals(2);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
ParticleOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.particle_weight);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
ParticleOption option;
option.readOptionSetting(prop->settings().data());
option.particle_weight = prop->value().toReal();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisDoubleSliderBasedPaintOpPropertyCallback *prop =
new KisDoubleSliderBasedPaintOpPropertyCallback(
KisDoubleSliderBasedPaintOpPropertyCallback::Double,
"particle_dx_scale",
i18n("dx scale"),
settings, 0);
prop->setRange(-2, 2);
prop->setSingleStep(0.01);
prop->setDecimals(2);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
ParticleOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.particle_scale_x);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
ParticleOption option;
option.readOptionSetting(prop->settings().data());
option.particle_scale_x = prop->value().toReal();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisDoubleSliderBasedPaintOpPropertyCallback *prop =
new KisDoubleSliderBasedPaintOpPropertyCallback(
KisDoubleSliderBasedPaintOpPropertyCallback::Double,
"particle_dy_scale",
i18n("dy scale"),
settings, 0);
prop->setRange(-2, 2);
prop->setSingleStep(0.01);
prop->setDecimals(2);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
ParticleOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.particle_scale_y);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
ParticleOption option;
option.readOptionSetting(prop->settings().data());
option.particle_scale_y = prop->value().toReal();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisDoubleSliderBasedPaintOpPropertyCallback *prop =
new KisDoubleSliderBasedPaintOpPropertyCallback(
KisDoubleSliderBasedPaintOpPropertyCallback::Double,
"particle_gravity",
i18n("Gravity"),
settings, 0);
prop->setRange(0.01, 1.0);
prop->setSingleStep(0.01);
prop->setDecimals(2);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
ParticleOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.particle_gravity);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
ParticleOption option;
option.readOptionSetting(prop->settings().data());
option.particle_gravity = prop->value().toReal();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisIntSliderBasedPaintOpPropertyCallback *prop =
new KisIntSliderBasedPaintOpPropertyCallback(
KisIntSliderBasedPaintOpPropertyCallback::Int,
"particle_iterations",
i18n("Iterations"),
settings, 0);
prop->setRange(1, 300);
prop->setSingleStep(1);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
ParticleOption option;
option.readOptionSetting(prop->settings().data());
prop->setValue(int(option.particle_iterations));
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
ParticleOption option;
option.readOptionSetting(prop->settings().data());
option.particle_iterations = prop->value().toInt();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
}
{
using namespace KisStandardUniformPropertiesFactory;
Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) {
if (prop->id() == opacity.id()) {
props.prepend(prop);
}
}
}
return props;
}
diff --git a/plugins/paintops/particle/kis_particle_paintop_settings.h b/plugins/paintops/particle/kis_particle_paintop_settings.h
index 57772c3e83..ca7fd76db4 100644
--- a/plugins/paintops/particle/kis_particle_paintop_settings.h
+++ b/plugins/paintops/particle/kis_particle_paintop_settings.h
@@ -1,44 +1,44 @@
/*
* 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_PARTICLE_PAINTOP_SETTINGS_H_
#define KIS_PARTICLE_PAINTOP_SETTINGS_H_
#include <QScopedPointer>
#include <brushengine/kis_no_size_paintop_settings.h>
#include <kis_types.h>
class KisParticlePaintOpSettings : public KisNoSizePaintOpSettings
{
public:
- KisParticlePaintOpSettings();
+ KisParticlePaintOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisParticlePaintOpSettings() override;
bool lodSizeThresholdSupported() const override;
bool paintIncremental() override;
QList<KisUniformPaintOpPropertySP> uniformProperties(KisPaintOpSettingsSP settings) override;
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif
diff --git a/plugins/paintops/particle/kis_particle_paintop_settings_widget.cpp b/plugins/paintops/particle/kis_particle_paintop_settings_widget.cpp
index 8f8a5acc4e..9697a87b2f 100644
--- a/plugins/paintops/particle/kis_particle_paintop_settings_widget.cpp
+++ b/plugins/paintops/particle/kis_particle_paintop_settings_widget.cpp
@@ -1,56 +1,57 @@
/*
* Copyright (c) 2008 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.
*/
#include "kis_particle_paintop_settings_widget.h"
#include "kis_particleop_option.h"
#include "kis_particle_paintop_settings.h"
#include <kis_paintop_settings_widget.h>
#include <kis_curve_option_widget.h>
#include <kis_paint_action_type_option.h>
#include <kis_airbrush_option_widget.h>
#include <kis_compositeop_option.h>
#include <kis_pressure_rate_option.h>
+#include <KisGlobalResourcesInterface.h>
KisParticlePaintOpSettingsWidget:: KisParticlePaintOpSettingsWidget(QWidget* parent)
: KisPaintOpSettingsWidget(parent)
{
m_paintActionTypeOption = new KisPaintActionTypeOption();
m_particleOption = new KisParticleOpOption();
addPaintOpOption(m_particleOption, i18n("Brush size"));
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisAirbrushOptionWidget(false, false), i18n("Airbrush"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"),
i18n("100%")), i18n("Rate"));
addPaintOpOption(m_paintActionTypeOption, i18n("Painting Mode"));
}
KisParticlePaintOpSettingsWidget::~ KisParticlePaintOpSettingsWidget()
{
}
KisPropertiesConfigurationSP KisParticlePaintOpSettingsWidget::configuration() const
{
- KisParticlePaintOpSettings* config = new KisParticlePaintOpSettings();
+ KisParticlePaintOpSettings* config = new KisParticlePaintOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisParticlePaintOpSettingsWidget*>(this));
config->setProperty("paintop", "particlebrush"); // XXX: make this a const id string
writeConfiguration(config);
return config;
}
diff --git a/plugins/paintops/particle/kis_particle_paintop_settings_widget.h b/plugins/paintops/particle/kis_particle_paintop_settings_widget.h
index 7d8a92fe8b..7595ec6782 100644
--- a/plugins/paintops/particle/kis_particle_paintop_settings_widget.h
+++ b/plugins/paintops/particle/kis_particle_paintop_settings_widget.h
@@ -1,46 +1,46 @@
/*
* Copyright (c) 2008 Lukas Tvrdy <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_PARTICLEPAINTOP_SETTINGS_WIDGET_H_
#define KIS_PARTICLEPAINTOP_SETTINGS_WIDGET_H_
#include <kis_paintop_settings_widget.h>
#include "ui_wdgparticleoptions.h"
-#include "kis_popup_button.h"
+#include "KisPopupButton.h"
class KisPaintActionTypeOption;
class KisParticleOpOption;
class KisParticlePaintOpSettingsWidget : public KisPaintOpSettingsWidget
{
Q_OBJECT
public:
KisParticlePaintOpSettingsWidget(QWidget* parent = 0);
~KisParticlePaintOpSettingsWidget() override;
KisPropertiesConfigurationSP configuration() const override;
public:
KisPaintActionTypeOption* m_paintActionTypeOption;
KisParticleOpOption* m_particleOption;
};
#endif
diff --git a/plugins/paintops/roundmarker/kis_roundmarkerop_settings.cpp b/plugins/paintops/roundmarker/kis_roundmarkerop_settings.cpp
index e54272ef11..80af598109 100644
--- a/plugins/paintops/roundmarker/kis_roundmarkerop_settings.cpp
+++ b/plugins/paintops/roundmarker/kis_roundmarkerop_settings.cpp
@@ -1,103 +1,104 @@
/*
* 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_roundmarkerop_settings.h"
#include "kis_roundmarker_option.h"
struct KisRoundMarkerOpSettings::Private
{
};
-KisRoundMarkerOpSettings::KisRoundMarkerOpSettings()
+KisRoundMarkerOpSettings::KisRoundMarkerOpSettings(KisResourcesInterfaceSP resourcesInterface)
: KisOutlineGenerationPolicy<KisPaintOpSettings>(KisCurrentOutlineFetcher::SIZE_OPTION |
KisCurrentOutlineFetcher::ROTATION_OPTION |
- KisCurrentOutlineFetcher::MIRROR_OPTION),
+ KisCurrentOutlineFetcher::MIRROR_OPTION,
+ resourcesInterface),
m_d(new Private)
{
}
KisRoundMarkerOpSettings::~KisRoundMarkerOpSettings()
{
}
bool KisRoundMarkerOpSettings::paintIncremental() {
return false;
}
void KisRoundMarkerOpSettings::setPaintOpSize(qreal value)
{
RoundMarkerOption op;
op.readOptionSetting(*this);
op.diameter = value;
op.writeOptionSetting(this);
}
qreal KisRoundMarkerOpSettings::paintOpSize() const
{
RoundMarkerOption op;
op.readOptionSetting(*this);
return op.diameter;
}
QPainterPath KisRoundMarkerOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom)
{
QPainterPath path;
if (mode.isVisible) {
qreal finalScale = 1.0;
RoundMarkerOption op;
op.readOptionSetting(*this);
const qreal radius = 0.5 * op.diameter;
QPainterPath realOutline;
realOutline.addEllipse(QPointF(), radius, radius);
path = outlineFetcher()->fetchOutline(info, this, realOutline, mode, alignForZoom, finalScale);
if (mode.showTiltDecoration) {
QPainterPath tiltLine = makeTiltIndicator(info,
realOutline.boundingRect().center(),
realOutline.boundingRect().width() * 0.5,
3.0);
path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, finalScale, 0.0, true, realOutline.boundingRect().center().x(), realOutline.boundingRect().center().y()));
}
}
return path;
}
#include "kis_standard_uniform_properties_factory.h"
QList<KisUniformPaintOpPropertySP> KisRoundMarkerOpSettings::uniformProperties(KisPaintOpSettingsSP settings)
{
QList<KisUniformPaintOpPropertySP> props;
{
using namespace KisStandardUniformPropertiesFactory;
Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) {
if (prop->id() != flow.id()) {
props.prepend(prop);
}
}
}
return props;
}
diff --git a/plugins/paintops/roundmarker/kis_roundmarkerop_settings.h b/plugins/paintops/roundmarker/kis_roundmarkerop_settings.h
index eff10ec1ce..566840a9fd 100644
--- a/plugins/paintops/roundmarker/kis_roundmarkerop_settings.h
+++ b/plugins/paintops/roundmarker/kis_roundmarkerop_settings.h
@@ -1,57 +1,57 @@
/*
* 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_ROUNDMARKEROP_SETTINGS_H
#define __KIS_ROUNDMARKEROP_SETTINGS_H
#include <QScopedPointer>
#include <kis_paintop_settings.h>
#include <kis_outline_generation_policy.h>
class KisRoundMarkerOpSettings : public KisOutlineGenerationPolicy<KisPaintOpSettings>
{
public:
- KisRoundMarkerOpSettings();
+ KisRoundMarkerOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisRoundMarkerOpSettings() override;
bool paintIncremental() override;
qreal paintOpSize() const override;
void setPaintOpSize(qreal value) override;
bool isAirbrushing() const override
{
return false;
}
qreal airbrushInterval() const override
{
return 1000.0;
}
QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override;
QList<KisUniformPaintOpPropertySP> uniformProperties(KisPaintOpSettingsSP settings) override;
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif /* __KIS_ROUNDMARKEROP_SETTINGS_H */
diff --git a/plugins/paintops/roundmarker/kis_roundmarkerop_settings_widget.cpp b/plugins/paintops/roundmarker/kis_roundmarkerop_settings_widget.cpp
index 63034b0bbc..1e58a8d7b5 100644
--- a/plugins/paintops/roundmarker/kis_roundmarkerop_settings_widget.cpp
+++ b/plugins/paintops/roundmarker/kis_roundmarkerop_settings_widget.cpp
@@ -1,58 +1,59 @@
/*
* 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_roundmarkerop_settings_widget.h"
#include "kis_brush_based_paintop_settings.h"
#include <kis_properties_configuration.h>
#include <kis_paintop_settings_widget.h>
#include <kis_pressure_size_option.h>
#include <kis_curve_option_widget.h>
#include <kis_compositeop_option.h>
#include <kis_pressure_spacing_option_widget.h>
//#include "kis_texture_option.h"
//#include "kis_pressure_texture_strength_option.h"
#include "kis_roundmarkerop_settings.h"
#include "kis_roundmarker_option.h"
+#include <KisGlobalResourcesInterface.h>
KisRoundMarkerOpSettingsWidget::KisRoundMarkerOpSettingsWidget(QWidget* parent)
: KisPaintOpSettingsWidget(parent)
{
setObjectName("roundmarker option widget");
//setPrecisionEnabled(true);
addPaintOpOption(new KisRoundMarkerOption(), i18n("Brush"));
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size"));
addPaintOpOption(new KisPressureSpacingOptionWidget(), i18n("Spacing"));
//addPaintOpOption(new KisTextureOption(), i18n("Pattern"));
//addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength"));
}
KisRoundMarkerOpSettingsWidget::~KisRoundMarkerOpSettingsWidget() { }
KisPropertiesConfigurationSP KisRoundMarkerOpSettingsWidget::configuration() const
{
- KisRoundMarkerOpSettings *config = new KisRoundMarkerOpSettings();
+ KisRoundMarkerOpSettings *config = new KisRoundMarkerOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisRoundMarkerOpSettingsWidget*>(this));
config->setProperty("paintop", "roundmarker");
writeConfiguration(config);
return config;
}
diff --git a/plugins/paintops/sketch/kis_sketch_paintop.cpp b/plugins/paintops/sketch/kis_sketch_paintop.cpp
index fb20f0f335..8adf0f0491 100644
--- a/plugins/paintops/sketch/kis_sketch_paintop.cpp
+++ b/plugins/paintops/sketch/kis_sketch_paintop.cpp
@@ -1,325 +1,331 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2010 Ricardo Cabello <hello@mrdoob.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_sketch_paintop.h"
#include "kis_sketch_paintop_settings.h"
#include <cmath>
#include <QRect>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <kis_image.h>
#include <kis_debug.h>
#include <kis_global.h>
#include <kis_paint_device.h>
#include <kis_painter.h>
#include <kis_types.h>
#include <kis_paintop_plugin_utils.h>
#include <brushengine/kis_paintop.h>
#include <brushengine/kis_paint_information.h>
#include <kis_fixed_paint_device.h>
#include <kis_pressure_opacity_option.h>
#include <kis_dab_cache.h>
#include "kis_lod_transform.h"
#include <QtGlobal>
/*
* Based on Harmony project https://github.com/mrdoob/harmony/
*/
// chrome : diff 0.2, sketchy : 0.3, fur: 0.5
// fur : distance / thresholdDistance
// shaded: opacity per line :/
// ((1 - (d / 1000)) * 0.1 * BRUSH_PRESSURE), offset == 0
// chrome: color per line :/
//this.context.strokeStyle = "rgba(" + Math.floor(Math.random() * COLOR[0]) + ", " + Math.floor(Math.random() * COLOR[1]) + ", " + Math.floor(Math.random() * COLOR[2]) + ", " + 0.1 * BRUSH_PRESSURE + " )";
// long fur
// from: count + offset * -random
// to: i point - (offset * -random) + random * 2
// probability distance / thresholdDistnace
// shaded: probability : paint always - 0.0 density
KisSketchPaintOp::KisSketchPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image)
: KisPaintOp(painter)
{
Q_UNUSED(image);
Q_UNUSED(node);
m_airbrushOption.readOptionSetting(settings);
m_opacityOption.readOptionSetting(settings);
m_sizeOption.readOptionSetting(settings);
m_rotationOption.readOptionSetting(settings);
m_rateOption.readOptionSetting(settings);
m_sketchProperties.readOptionSetting(settings);
- m_brushOption.readOptionSetting(settings);
+ m_brushOption.readOptionSetting(settings, settings->resourcesInterface());
m_densityOption.readOptionSetting(settings);
m_lineWidthOption.readOptionSetting(settings);
m_offsetScaleOption.readOptionSetting(settings);
m_brush = m_brushOption.brush();
m_dabCache = new KisDabCache(m_brush);
m_opacityOption.resetAllSensors();
m_sizeOption.resetAllSensors();
m_rotationOption.resetAllSensors();
m_rateOption.resetAllSensors();
m_painter = 0;
m_count = 0;
}
KisSketchPaintOp::~KisSketchPaintOp()
{
delete m_painter;
delete m_dabCache;
}
+QList<KoResourceSP> KisSketchPaintOp::prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface)
+{
+ KisBrushOptionProperties brushOption;
+ return brushOption.prepareLinkedResources(settings, resourcesInterface);
+}
+
void KisSketchPaintOp::drawConnection(const QPointF& start, const QPointF& end, double lineWidth)
{
if (lineWidth == 1.0) {
m_painter->drawThickLine(start, end, lineWidth, lineWidth);
}
else {
m_painter->drawLine(start, end, lineWidth, true);
}
}
void KisSketchPaintOp::updateBrushMask(const KisPaintInformation& info, qreal scale, qreal rotation)
{
QRect dstRect;
m_maskDab = m_dabCache->fetchDab(m_dab->colorSpace(),
painter()->paintColor(),
info.pos(),
KisDabShape(scale, 1.0, rotation),
info, 1.0,
&dstRect);
m_brushBoundingBox = dstRect;
m_hotSpot = QPointF(0.5 * m_brushBoundingBox.width(),
0.5 * m_brushBoundingBox.height());
}
void KisSketchPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2,
KisDistanceInformation *currentDistance)
{
// Use superclass behavior for lines of zero length. Otherwise, airbrushing can happen faster
// than it is supposed to.
if (pi1.pos() == pi2.pos()) {
KisPaintOp::paintLine(pi1, pi2, currentDistance);
}
else {
doPaintLine(pi1, pi2);
}
}
void KisSketchPaintOp::doPaintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2)
{
if (!m_brush || !painter()) return;
if (!m_dab) {
m_dab = source()->createCompositionSourceDevice();
m_painter = new KisPainter(m_dab);
m_painter->setPaintColor(painter()->paintColor());
}
else {
m_dab->clear();
}
QPointF prevMouse = pi1.pos();
QPointF mousePosition = pi2.pos();
m_points.append(mousePosition);
const qreal lodAdditionalScale = KisLodTransform::lodToScale(painter()->device());
const qreal scale = lodAdditionalScale * m_sizeOption.apply(pi2);
if ((scale * m_brush->width()) <= 0.01 || (scale * m_brush->height()) <= 0.01) return;
const qreal currentLineWidth = qMax(0.9, lodAdditionalScale * m_lineWidthOption.apply(pi2, m_sketchProperties.lineWidth));
const qreal currentOffsetScale = m_offsetScaleOption.apply(pi2, m_sketchProperties.offset);
const double rotation = m_rotationOption.apply(pi2);
const double currentProbability = m_densityOption.apply(pi2, m_sketchProperties.probability);
// shaded: does not draw this line, chrome does, fur does
if (m_sketchProperties.makeConnection) {
drawConnection(prevMouse, mousePosition, currentLineWidth);
}
qreal thresholdDistance = 0.0;
// update the mask for simple mode only once
// determine the radius
if (m_count == 0 && m_sketchProperties.simpleMode) {
updateBrushMask(pi2, 1.0, 0.0);
//m_radius = qMax(m_maskDab->bounds().width(),m_maskDab->bounds().height()) * 0.5;
m_radius = 0.5 * qMax(m_brush->width(), m_brush->height());
}
if (!m_sketchProperties.simpleMode) {
updateBrushMask(pi2, scale, rotation);
m_radius = qMax(m_maskDab->bounds().width(), m_maskDab->bounds().height()) * 0.5;
thresholdDistance = pow(m_radius, 2);
}
if (m_sketchProperties.simpleMode) {
// update the radius according scale in simple mode
thresholdDistance = pow(m_radius * scale, 2);
}
// determine density
const qreal density = thresholdDistance * currentProbability;
// probability behaviour
qreal probability = 1.0 - currentProbability;
QColor painterColor = painter()->paintColor().toQColor();
QColor randomColor;
KoColor color(m_dab->colorSpace());
int w = m_maskDab->bounds().width();
quint8 opacityU8 = 0;
quint8 * pixel;
qreal distance;
QPoint positionInMask;
QPointF diff;
int size = m_points.size();
// MAIN LOOP
for (int i = 0; i < size; i++) {
diff = m_points.at(i) - mousePosition;
distance = diff.x() * diff.x() + diff.y() * diff.y();
// circle test
bool makeConnection = false;
if (m_sketchProperties.simpleMode) {
if (distance < thresholdDistance) {
makeConnection = true;
}
// mask test
}
else {
if (m_brushBoundingBox.contains(m_points.at(i))) {
positionInMask = (diff + m_hotSpot).toPoint();
uint pos = ((positionInMask.y() * w + positionInMask.x()) * m_maskDab->pixelSize());
if (pos < m_maskDab->allocatedPixels() * m_maskDab->pixelSize()) {
pixel = m_maskDab->data() + pos;
opacityU8 = m_maskDab->colorSpace()->opacityU8(pixel);
if (opacityU8 != 0) {
makeConnection = true;
}
}
}
}
if (!makeConnection) {
// check next point
continue;
}
if (m_sketchProperties.distanceDensity) {
probability = distance / density;
}
KisRandomSourceSP randomSource = pi2.randomSource();
// density check
if (randomSource->generateNormalized() >= probability) {
QPointF offsetPt = diff * currentOffsetScale;
if (m_sketchProperties.randomRGB) {
/**
* Since the order of calculation of function
* parameters is not defined by C++ standard, we
* should generate values in an external code snippet
* which has a definite order of execution.
*/
qreal r1 = randomSource->generateNormalized();
qreal r2 = randomSource->generateNormalized();
qreal r3 = randomSource->generateNormalized();
// some color transformation per line goes here
randomColor.setRgbF(r1 * painterColor.redF(),
r2 * painterColor.greenF(),
r3 * painterColor.blueF());
color.fromQColor(randomColor);
m_painter->setPaintColor(color);
}
// distance based opacity
quint8 opacity = OPACITY_OPAQUE_U8;
if (m_sketchProperties.distanceOpacity) {
opacity *= qRound((1.0 - (distance / thresholdDistance)));
}
if (m_sketchProperties.randomOpacity) {
opacity *= randomSource->generateNormalized();
}
m_painter->setOpacity(opacity);
if (m_sketchProperties.magnetify) {
drawConnection(mousePosition + offsetPt, m_points.at(i) - offsetPt, currentLineWidth);
}
else {
drawConnection(mousePosition + offsetPt, mousePosition - offsetPt, currentLineWidth);
}
}
}// end of MAIN LOOP
m_count++;
QRect rc = m_dab->extent();
quint8 origOpacity = m_opacityOption.apply(painter(), pi2);
painter()->bitBlt(rc.x(), rc.y(), m_dab, rc.x(), rc.y(), rc.width(), rc.height());
painter()->renderMirrorMask(rc, m_dab);
painter()->setOpacity(origOpacity);
}
KisSpacingInformation KisSketchPaintOp::paintAt(const KisPaintInformation& info)
{
doPaintLine(info, info);
return updateSpacingImpl(info);
}
KisSpacingInformation KisSketchPaintOp::updateSpacingImpl(const KisPaintInformation &info) const
{
return KisPaintOpPluginUtils::effectiveSpacing(0.0, 0.0, true, 0.0, false, 0.0, false, 0.0,
KisLodTransform::lodToScale(painter()->device()),
&m_airbrushOption, nullptr, info);
}
KisTimingInformation KisSketchPaintOp::updateTimingImpl(const KisPaintInformation &info) const
{
return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOption, &m_rateOption, info);
}
diff --git a/plugins/paintops/sketch/kis_sketch_paintop.h b/plugins/paintops/sketch/kis_sketch_paintop.h
index f760983b95..be0cc0f4ba 100644
--- a/plugins/paintops/sketch/kis_sketch_paintop.h
+++ b/plugins/paintops/sketch/kis_sketch_paintop.h
@@ -1,94 +1,96 @@
/*
* 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_SKETCH_PAINTOP_H_
#define KIS_SKETCH_PAINTOP_H_
#include <brushengine/kis_paintop.h>
#include <kis_types.h>
#include "kis_density_option.h"
#include "kis_sketchop_option.h"
#include "kis_sketch_paintop_settings.h"
#include "kis_painter.h"
#include <kis_airbrush_option_widget.h>
#include <kis_pressure_size_option.h>
#include <kis_brush_option.h>
#include <kis_pressure_rotation_option.h>
#include <kis_pressure_rate_option.h>
#include "kis_linewidth_option.h"
#include "kis_offset_scale_option.h"
class KisDabCache;
class KisSketchPaintOp : public KisPaintOp
{
public:
KisSketchPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image);
~KisSketchPaintOp() override;
void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) override;
+ static QList<KoResourceSP> prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface);
+
protected:
KisSpacingInformation paintAt(const KisPaintInformation& info) override;
KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override;
KisTimingInformation updateTimingImpl(const KisPaintInformation &info) const override;
private:
// pixel buffer
KisPaintDeviceSP m_dab;
// mask detection area
KisFixedPaintDeviceSP m_maskDab;
QRectF m_brushBoundingBox;
QPointF m_hotSpot;
// simple mode
qreal m_radius;
KisPressureOpacityOption m_opacityOption;
KisPressureSizeOption m_sizeOption;
KisPressureRotationOption m_rotationOption;
KisPressureRateOption m_rateOption;
KisDensityOption m_densityOption;
KisLineWidthOption m_lineWidthOption;
KisOffsetScaleOption m_offsetScaleOption;
KisAirbrushOptionProperties m_airbrushOption;
KisBrushOptionProperties m_brushOption;
SketchProperties m_sketchProperties;
QVector<QPointF> m_points;
int m_count;
KisPainter * m_painter;
KisBrushSP m_brush;
KisDabCache *m_dabCache;
private:
void drawConnection(const QPointF &start, const QPointF &end, double lineWidth);
void updateBrushMask(const KisPaintInformation& info, qreal scale, qreal rotation);
void doPaintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2);
};
#endif // KIS_SKETCH_PAINTOP_H_
diff --git a/plugins/paintops/sketch/kis_sketch_paintop_settings.cpp b/plugins/paintops/sketch/kis_sketch_paintop_settings.cpp
index 848889c00d..b183c3cec6 100644
--- a/plugins/paintops/sketch/kis_sketch_paintop_settings.cpp
+++ b/plugins/paintops/sketch/kis_sketch_paintop_settings.cpp
@@ -1,62 +1,63 @@
/*
* 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.
*/
#include "kis_sketch_paintop_settings.h"
#include <kis_sketchop_option.h>
#include <kis_paint_action_type_option.h>
#include "kis_current_outline_fetcher.h"
-KisSketchPaintOpSettings::KisSketchPaintOpSettings()
+KisSketchPaintOpSettings::KisSketchPaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
+ : KisBrushBasedPaintOpSettings(resourcesInterface)
{
}
bool KisSketchPaintOpSettings::paintIncremental()
{
return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP;
}
QPainterPath KisSketchPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom)
{
bool isSimpleMode = getBool(SKETCH_USE_SIMPLE_MODE);
if (!isSimpleMode) {
return KisBrushBasedPaintOpSettings::brushOutline(info, mode, alignForZoom);
}
QPainterPath path;
KisBrushSP brush = this->brush();
if (brush && mode.isVisible) {
// just circle supported
qreal diameter = qMax(brush->width(), brush->height());
path = ellipseOutline(diameter, diameter, 1.0, 0.0);
path = outlineFetcher()->fetchOutline(info, this, path, mode, alignForZoom);
if (mode.showTiltDecoration) {
QPainterPath tiltLine =
makeTiltIndicator(info, path.boundingRect().center(), diameter * 0.5, 3.0);
path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, 1.0, 0.0, true, path.boundingRect().center().x(), path.boundingRect().center().y()));
}
}
return path;
}
diff --git a/plugins/paintops/sketch/kis_sketch_paintop_settings.h b/plugins/paintops/sketch/kis_sketch_paintop_settings.h
index 1ad2541caa..9b7d5b2a63 100644
--- a/plugins/paintops/sketch/kis_sketch_paintop_settings.h
+++ b/plugins/paintops/sketch/kis_sketch_paintop_settings.h
@@ -1,44 +1,44 @@
/*
* 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_SKETCH_PAINTOP_SETTINGS_H_
#define KIS_SKETCH_PAINTOP_SETTINGS_H_
#include <kis_brush_based_paintop_settings.h>
#include <kis_types.h>
#include "kis_sketch_paintop_settings_widget.h"
#include <kis_pressure_opacity_option.h>
class KisSketchPaintOpSettings : public KisBrushBasedPaintOpSettings
{
public:
- KisSketchPaintOpSettings();
+ KisSketchPaintOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisSketchPaintOpSettings() override {}
QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override;
bool paintIncremental() override;
};
typedef KisSharedPtr<KisSketchPaintOpSettings> KisSketchPaintOpSettingsSP;
#endif
diff --git a/plugins/paintops/sketch/kis_sketch_paintop_settings_widget.cpp b/plugins/paintops/sketch/kis_sketch_paintop_settings_widget.cpp
index a739832dd7..3ca9032759 100644
--- a/plugins/paintops/sketch/kis_sketch_paintop_settings_widget.cpp
+++ b/plugins/paintops/sketch/kis_sketch_paintop_settings_widget.cpp
@@ -1,88 +1,89 @@
/*
* 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.
*/
#include "kis_sketch_paintop_settings_widget.h"
#include "kis_sketchop_option.h"
#include "kis_sketch_paintop_settings.h"
#include <kis_curve_option_widget.h>
#include <kis_pressure_opacity_option.h>
#include <kis_paintop_settings_widget.h>
#include <kis_paint_action_type_option.h>
#include <kis_airbrush_option_widget.h>
#include <kis_pressure_size_option.h>
#include <kis_pressure_rate_option.h>
#include <kis_compositeop_option.h>
#include <QDomDocument>
#include <QDomElement>
#include <kis_pressure_rotation_option.h>
#include "kis_density_option.h"
#include "kis_linewidth_option.h"
#include "kis_offset_scale_option.h"
+#include <KisGlobalResourcesInterface.h>
KisSketchPaintOpSettingsWidget::KisSketchPaintOpSettingsWidget(QWidget* parent)
: KisBrushBasedPaintopOptionWidget(parent)
{
m_sketchOption = new KisSketchOpOption();
addPaintOpOption(m_sketchOption, i18n("Brush size"));
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation"));
addPaintOpOption(new KisCurveOptionWidget(new KisLineWidthOption() , i18n("0%"), i18n("100%")), i18n("Line width"));
addPaintOpOption(new KisCurveOptionWidget(new KisOffsetScaleOption(), i18n("0%"), i18n("100%")), i18n("Offset scale"));
addPaintOpOption(new KisCurveOptionWidget(new KisDensityOption(), i18n("0%"), i18n("100%")), i18n("Density"));
addPaintOpOption(new KisAirbrushOptionWidget(false, false), i18n("Airbrush"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"), i18n("100%")), i18n("Rate"));
m_paintActionType = new KisPaintActionTypeOption();
KisPropertiesConfigurationSP defaultSetting = new KisPropertiesConfiguration();
defaultSetting->setProperty("PaintOpAction", BUILDUP);
m_paintActionType->readOptionSetting(defaultSetting);
addPaintOpOption(m_paintActionType, i18n("Painting Mode"));
KisPropertiesConfigurationSP reconfigurationCourier = configuration();
QDomDocument xMLAnalyzer;
xMLAnalyzer.setContent(reconfigurationCourier->getString("brush_definition"));
QDomElement firstTag = xMLAnalyzer.documentElement();
QDomElement firstTagsChild = firstTag.elementsByTagName("MaskGenerator").item(0).toElement();
firstTagsChild.attributeNode("diameter").setValue("128");
reconfigurationCourier->setProperty("brush_definition", xMLAnalyzer.toString());
setConfiguration(reconfigurationCourier);
}
KisSketchPaintOpSettingsWidget::~ KisSketchPaintOpSettingsWidget()
{
}
KisPropertiesConfigurationSP KisSketchPaintOpSettingsWidget::configuration() const
{
- KisSketchPaintOpSettingsSP config = new KisSketchPaintOpSettings();
+ KisSketchPaintOpSettingsSP config = new KisSketchPaintOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisSketchPaintOpSettingsWidget*>(this));
config->setProperty("paintop", "sketchbrush"); // XXX: make this a const id string
writeConfiguration(config);
return config;
}
diff --git a/plugins/paintops/spray/kis_spray_paintop.cpp b/plugins/paintops/spray/kis_spray_paintop.cpp
index ff83355007..81cf08e907 100644
--- a/plugins/paintops/spray/kis_spray_paintop.cpp
+++ b/plugins/paintops/spray/kis_spray_paintop.cpp
@@ -1,153 +1,159 @@
/*
* Copyright (c) 2008-2012 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.
*/
#include "kis_spray_paintop.h"
#include "kis_spray_paintop_settings.h"
#include <cmath>
#include <QRect>
#include <kis_global.h>
#include <kis_paint_device.h>
#include <kis_painter.h>
#include <kis_types.h>
#include <brushengine/kis_paintop.h>
#include <kis_node.h>
#include <kis_pressure_rotation_option.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_size_option.h>
#include <kis_fixed_paint_device.h>
#include <kis_brush_option.h>
#include <kis_sprayop_option.h>
#include <kis_spray_shape_option.h>
#include <kis_color_option.h>
#include <kis_lod_transform.h>
#include <kis_paintop_plugin_utils.h>
KisSprayPaintOp::KisSprayPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image)
: KisPaintOp(painter)
, m_isPresetValid(true)
, m_node(node)
{
Q_ASSERT(settings);
Q_ASSERT(painter);
Q_UNUSED(image);
m_airbrushOption.readOptionSetting(settings);
m_rotationOption.readOptionSetting(settings);
m_opacityOption.readOptionSetting(settings);
m_sizeOption.readOptionSetting(settings);
m_rateOption.readOptionSetting(settings);
m_rotationOption.resetAllSensors();
m_opacityOption.resetAllSensors();
m_sizeOption.resetAllSensors();
m_rateOption.resetAllSensors();
- m_brushOption.readOptionSetting(settings);
+ m_brushOption.readOptionSetting(settings, settings->resourcesInterface());
m_colorProperties.fillProperties(settings);
m_properties.readOptionSetting(settings);
// first load tip properties as shape properties are dependent on diameter/scale/aspect
m_shapeProperties.loadSettings(settings, m_properties.diameter * m_properties.scale, m_properties.diameter * m_properties.aspect * m_properties.scale);
// TODO: what to do with proportional sizes?
m_shapeDynamicsProperties.loadSettings(settings);
if (!m_shapeProperties.enabled && !m_brushOption.brush()) {
// in case the preset does not contain the definition for KisBrush
m_isPresetValid = false;
dbgKrita << "Preset is not valid. Painting is not possible. Use the preset editor to fix current brush engine preset.";
}
m_sprayBrush.setProperties(&m_properties, &m_colorProperties,
&m_shapeProperties, &m_shapeDynamicsProperties, m_brushOption.brush());
m_sprayBrush.setFixedDab(cachedDab());
// spacing
if ((m_properties.diameter * 0.5) > 1) {
m_ySpacing = m_xSpacing = m_properties.diameter * 0.5 * m_properties.spacing;
}
else {
m_ySpacing = m_xSpacing = 1.0;
}
m_spacing = m_xSpacing;
}
KisSprayPaintOp::~KisSprayPaintOp()
{
}
+QList<KoResourceSP> KisSprayPaintOp::prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface)
+{
+ KisBrushOptionProperties brushOption;
+ return brushOption.prepareLinkedResources(settings, resourcesInterface);
+}
+
KisSpacingInformation KisSprayPaintOp::paintAt(const KisPaintInformation& info)
{
if (!painter() || !m_isPresetValid) {
return KisSpacingInformation(m_spacing);
}
if (!m_dab) {
m_dab = source()->createCompositionSourceDevice();
}
else {
m_dab->clear();
}
qreal rotation = m_rotationOption.apply(info);
quint8 origOpacity = m_opacityOption.apply(painter(), info);
// Spray Brush is capable of working with zero scale,
// so no additional checks for 'zero'ness are needed
const qreal scale = m_sizeOption.apply(info);
const qreal lodScale = KisLodTransform::lodToScale(painter()->device());
m_sprayBrush.paint(m_dab,
m_node->paintDevice(),
info,
rotation,
scale, lodScale,
painter()->paintColor(),
painter()->backgroundColor());
QRect rc = m_dab->extent();
painter()->bitBlt(rc.topLeft(), m_dab, rc);
painter()->renderMirrorMask(rc, m_dab);
painter()->setOpacity(origOpacity);
return computeSpacing(info, lodScale);
}
KisSpacingInformation KisSprayPaintOp::updateSpacingImpl(const KisPaintInformation &info) const
{
return computeSpacing(info, KisLodTransform::lodToScale(painter()->device()));
}
KisTimingInformation KisSprayPaintOp::updateTimingImpl(const KisPaintInformation &info) const
{
return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushOption, &m_rateOption, info);
}
KisSpacingInformation KisSprayPaintOp::computeSpacing(const KisPaintInformation &info,
qreal lodScale) const
{
return KisPaintOpPluginUtils::effectiveSpacing(1.0, 1.0, true, 0.0, false,
m_spacing * lodScale, false, 1.0, lodScale,
&m_airbrushOption, nullptr, info);
}
diff --git a/plugins/paintops/spray/kis_spray_paintop.h b/plugins/paintops/spray/kis_spray_paintop.h
index 600f6bcf48..6cf8d58bc6 100644
--- a/plugins/paintops/spray/kis_spray_paintop.h
+++ b/plugins/paintops/spray/kis_spray_paintop.h
@@ -1,75 +1,77 @@
/*
* Copyright (c) 2008-2012 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_SPRAY_PAINTOP_H_
#define KIS_SPRAY_PAINTOP_H_
#include <brushengine/kis_paintop.h>
#include <kis_types.h>
#include "spray_brush.h"
#include "kis_spray_paintop_settings.h"
#include "kis_brush_option.h"
#include <kis_airbrush_option_widget.h>
#include <kis_pressure_rotation_option.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_size_option.h>
#include <kis_pressure_rate_option.h>
class KisPainter;
class KisSprayPaintOp : public KisPaintOp
{
public:
KisSprayPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image);
~KisSprayPaintOp() override;
+ static QList<KoResourceSP> prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface);
+
protected:
KisSpacingInformation paintAt(const KisPaintInformation& info) override;
KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override;
KisTimingInformation updateTimingImpl(const KisPaintInformation &info) const override;
private:
KisSpacingInformation computeSpacing(const KisPaintInformation &info, qreal lodScale) const;
private:
KisShapeProperties m_shapeProperties;
KisSprayOptionProperties m_properties;
KisShapeDynamicsProperties m_shapeDynamicsProperties;
KisColorProperties m_colorProperties;
KisBrushOptionProperties m_brushOption;
KisPaintDeviceSP m_dab;
SprayBrush m_sprayBrush;
qreal m_xSpacing, m_ySpacing, m_spacing;
bool m_isPresetValid;
KisAirbrushOptionProperties m_airbrushOption;
KisPressureRotationOption m_rotationOption;
KisPressureSizeOption m_sizeOption;
KisPressureOpacityOption m_opacityOption;
KisPressureRateOption m_rateOption;
KisNodeSP m_node;
};
#endif // KIS_SPRAY_PAINTOP_H_
diff --git a/plugins/paintops/spray/kis_spray_paintop_settings.cpp b/plugins/paintops/spray/kis_spray_paintop_settings.cpp
index e2908c5771..e6cecadae2 100644
--- a/plugins/paintops/spray/kis_spray_paintop_settings.cpp
+++ b/plugins/paintops/spray/kis_spray_paintop_settings.cpp
@@ -1,219 +1,219 @@
/*
* Copyright (c) 2008,2009,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.
*/
#include <cmath>
#include <kis_paint_action_type_option.h>
#include <kis_color_option.h>
#include "kis_spray_paintop_settings.h"
#include "kis_sprayop_option.h"
#include "kis_spray_shape_option.h"
#include <kis_airbrush_option_widget.h>
struct KisSprayPaintOpSettings::Private
{
QList<KisUniformPaintOpPropertyWSP> uniformProperties;
};
-KisSprayPaintOpSettings::KisSprayPaintOpSettings()
+KisSprayPaintOpSettings::KisSprayPaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
: KisOutlineGenerationPolicy<KisPaintOpSettings>(KisCurrentOutlineFetcher::SIZE_OPTION |
- KisCurrentOutlineFetcher::ROTATION_OPTION),
+ KisCurrentOutlineFetcher::ROTATION_OPTION,
+ resourcesInterface),
m_d(new Private)
{
}
KisSprayPaintOpSettings::~KisSprayPaintOpSettings()
{
}
void KisSprayPaintOpSettings::setPaintOpSize(qreal value)
{
KisSprayOptionProperties option;
option.readOptionSetting(this);
option.diameter = value;
option.writeOptionSetting(this);
}
qreal KisSprayPaintOpSettings::paintOpSize() const
{
KisSprayOptionProperties option;
option.readOptionSetting(this);
return option.diameter;
}
bool KisSprayPaintOpSettings::paintIncremental()
{
return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP;
}
QPainterPath KisSprayPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom)
{
QPainterPath path;
if (mode.isVisible) {
qreal width = getInt(SPRAY_DIAMETER);
qreal height = getInt(SPRAY_DIAMETER) * getDouble(SPRAY_ASPECT);
path = ellipseOutline(width, height, getDouble(SPRAY_SCALE), getDouble(SPRAY_ROTATION));
path = outlineFetcher()->fetchOutline(info, this, path, mode, alignForZoom);
if (mode.forceFullSize) {
QPainterPath tiltLine =
makeTiltIndicator(info, QPointF(0.0, 0.0), width * 0.5, 3.0);
path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, 1.0, 0.0, true, 0, 0));
}
}
return path;
}
#include <brushengine/kis_slider_based_paintop_property.h>
#include "kis_paintop_preset.h"
#include "kis_paintop_settings_update_proxy.h"
#include "kis_standard_uniform_properties_factory.h"
QList<KisUniformPaintOpPropertySP> KisSprayPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings)
{
QList<KisUniformPaintOpPropertySP> props =
listWeakToStrong(m_d->uniformProperties);
if (props.isEmpty()) {
{
KisDoubleSliderBasedPaintOpPropertyCallback *prop =
new KisDoubleSliderBasedPaintOpPropertyCallback(
KisDoubleSliderBasedPaintOpPropertyCallback::Double,
"spacing",
i18n("Spacing"),
settings, 0);
prop->setRange(0.01, 10);
prop->setSingleStep(0.01);
prop->setExponentRatio(3.0);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
KisSprayOptionProperties option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.spacing);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
KisSprayOptionProperties option;
option.readOptionSetting(prop->settings().data());
option.spacing = prop->value().toReal();
option.writeOptionSetting(prop->settings().data());
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisIntSliderBasedPaintOpPropertyCallback *prop =
new KisIntSliderBasedPaintOpPropertyCallback(
KisIntSliderBasedPaintOpPropertyCallback::Int,
"spray_particlecount",
i18n("Particle Count"),
settings, 0);
prop->setRange(0, 1000);
prop->setExponentRatio(3);
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
KisSprayOptionProperties option;
option.readOptionSetting(prop->settings().data());
prop->setValue(int(option.particleCount));
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
KisSprayOptionProperties option;
option.readOptionSetting(prop->settings().data());
option.particleCount = prop->value().toInt();
option.writeOptionSetting(prop->settings().data());
});
prop->setIsVisibleCallback(
[](const KisUniformPaintOpProperty *prop) {
KisSprayOptionProperties option;
option.readOptionSetting(prop->settings().data());
return !option.useDensity;
});
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
{
KisDoubleSliderBasedPaintOpPropertyCallback *prop =
new KisDoubleSliderBasedPaintOpPropertyCallback(
KisDoubleSliderBasedPaintOpPropertyCallback::Double,
"spray_density",
i18n("Density"),
settings, 0);
prop->setRange(0.1, 100);
prop->setSingleStep(0.01);
prop->setDecimals(2);
prop->setExponentRatio(3);
prop->setSuffix(i18n("%"));
prop->setReadCallback(
[](KisUniformPaintOpProperty *prop) {
KisSprayOptionProperties option;
option.readOptionSetting(prop->settings().data());
prop->setValue(option.coverage);
});
prop->setWriteCallback(
[](KisUniformPaintOpProperty *prop) {
KisSprayOptionProperties option;
option.readOptionSetting(prop->settings().data());
option.coverage = prop->value().toReal();
option.writeOptionSetting(prop->settings().data());
});
prop->setIsVisibleCallback(
[](const KisUniformPaintOpProperty *prop) {
KisSprayOptionProperties option;
option.readOptionSetting(prop->settings().data());
return option.useDensity;
});
-
- QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
+ QObject::connect(updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
prop->requestReadValue();
props << toQShared(prop);
}
}
{
using namespace KisStandardUniformPropertiesFactory;
Q_FOREACH (KisUniformPaintOpPropertySP prop, KisPaintOpSettings::uniformProperties(settings)) {
if (prop->id() == opacity.id() ||
prop->id() == size.id()) {
props.prepend(prop);
}
}
}
return props;
}
diff --git a/plugins/paintops/spray/kis_spray_paintop_settings.h b/plugins/paintops/spray/kis_spray_paintop_settings.h
index c9b0111aed..e034c105d9 100644
--- a/plugins/paintops/spray/kis_spray_paintop_settings.h
+++ b/plugins/paintops/spray/kis_spray_paintop_settings.h
@@ -1,62 +1,62 @@
/*
* Copyright (c) 2008,2009,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_SPRAY_PAINTOP_SETTINGS_H_
#define KIS_SPRAY_PAINTOP_SETTINGS_H_
#include <QScopedPointer>
#include <brushengine/kis_no_size_paintop_settings.h>
#include <kis_types.h>
#include <kis_outline_generation_policy.h>
#include "kis_spray_paintop_settings_widget.h"
class KisSprayPaintOpSettings : public KisOutlineGenerationPolicy<KisPaintOpSettings>
{
public:
- KisSprayPaintOpSettings();
+ KisSprayPaintOpSettings(KisResourcesInterfaceSP resourcesInterface);
~KisSprayPaintOpSettings() override;
void setPaintOpSize(qreal value) override;
qreal paintOpSize() const override;
QPainterPath brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom) override;
QString modelName() const override {
return "airbrush";
}
bool paintIncremental() override;
protected:
QList<KisUniformPaintOpPropertySP> uniformProperties(KisPaintOpSettingsSP settings) override;
private:
Q_DISABLE_COPY(KisSprayPaintOpSettings)
struct Private;
const QScopedPointer<Private> m_d;
};
typedef KisSharedPtr<KisSprayPaintOpSettings> KisSprayPaintOpSettingsSP;
#endif
diff --git a/plugins/paintops/spray/kis_spray_paintop_settings_widget.cpp b/plugins/paintops/spray/kis_spray_paintop_settings_widget.cpp
index b5f345cf99..19b0470090 100644
--- a/plugins/paintops/spray/kis_spray_paintop_settings_widget.cpp
+++ b/plugins/paintops/spray/kis_spray_paintop_settings_widget.cpp
@@ -1,69 +1,70 @@
/*
* Copyright (c) 2008,2009,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.
*/
#include "kis_spray_paintop_settings_widget.h"
#include "kis_sprayop_option.h"
#include "kis_spray_paintop_settings.h"
#include "kis_spray_shape_option.h"
#include <kis_color_option.h>
#include <kis_paintop_settings_widget.h>
#include <kis_paint_action_type_option.h>
#include <kis_pressure_rotation_option.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_size_option.h>
#include <kis_pressure_rate_option.h>
#include <kis_curve_option_widget.h>
#include <kis_brush_option_widget.h>
#include "kis_spray_shape_dynamics.h"
#include <kis_airbrush_option_widget.h>
#include <kis_compositeop_option.h>
+#include <KisGlobalResourcesInterface.h>
KisSprayPaintOpSettingsWidget:: KisSprayPaintOpSettingsWidget(QWidget* parent)
: KisPaintOpSettingsWidget(parent)
, m_sprayArea(new KisSprayOpOption())
{
addPaintOpOption(m_sprayArea, i18n("Spray Area"));
addPaintOpOption(new KisSprayShapeOption(), i18n("Spray shape"));
addPaintOpOption(new KisBrushOptionWidget(), i18n("Brush Tip"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size"));
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisSprayShapeDynamicsOption(), i18n("Shape dynamics"));
addPaintOpOption(new KisColorOption(), i18n("Color options"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation"));
addPaintOpOption(new KisAirbrushOptionWidget(false), i18n("Airbrush"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"), i18n("100%")), i18n("Rate"));
addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode"));
}
KisSprayPaintOpSettingsWidget::~ KisSprayPaintOpSettingsWidget()
{
}
KisPropertiesConfigurationSP KisSprayPaintOpSettingsWidget::configuration() const
{
- KisSprayPaintOpSettings* config = new KisSprayPaintOpSettings();
+ KisSprayPaintOpSettings* config = new KisSprayPaintOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisSprayPaintOpSettingsWidget*>(this));
config->setProperty("paintop", "spraybrush"); // XXX: make this a const id string
writeConfiguration(config);
return config;
}
diff --git a/plugins/paintops/spray/kis_spray_shape_option.cpp b/plugins/paintops/spray/kis_spray_shape_option.cpp
index 7954382758..c4f95b2ffe 100644
--- a/plugins/paintops/spray/kis_spray_shape_option.cpp
+++ b/plugins/paintops/spray/kis_spray_shape_option.cpp
@@ -1,144 +1,144 @@
/*
* Copyright (c) 2008,2009,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.
*/
#include "kis_spray_shape_option.h"
#include <klocalizedstring.h>
#include <QImage>
#include <QFile>
#include <KisImportExportManager.h>
#include <kis_file_name_requester.h>
#include "kis_aspect_ratio_locker.h"
#include "kis_signals_blocker.h"
#include "ui_wdgsprayshapeoptions.h"
class KisShapeOptionsWidget: public QWidget, public Ui::WdgSprayShapeOptions
{
public:
KisShapeOptionsWidget(QWidget *parent = 0)
: QWidget(parent) {
setupUi(this);
imageUrl->setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import));
}
};
KisSprayShapeOption::KisSprayShapeOption()
: KisPaintOpOption(KisPaintOpOption::GENERAL, true),
m_sizeRatioLocker(new KisAspectRatioLocker(this))
{
setObjectName("KisSprayShapeOption");
m_checkable = true;
// save this to be able to restore it back
m_maxSize = 1000;
m_options = new KisShapeOptionsWidget();
- //initializer slider values
+ //initialize slider values
m_options->widthSpin->setRange(1, 1000, 0);
m_options->widthSpin->setValue(6);
m_options->widthSpin->setSuffix(i18n(" px"));
m_options->heightSpin->setRange(1, 1000, 0);
m_options->heightSpin->setValue(6);
m_options->heightSpin->setSuffix(i18n(" px"));
// UI signals
connect(m_options->proportionalBox, SIGNAL(clicked(bool)), SLOT(changeSizeUI(bool)));
connect(m_options->imageUrl, SIGNAL(textChanged(QString)), this, SLOT(prepareImage()));
m_sizeRatioLocker->connectSpinBoxes(m_options->widthSpin, m_options->heightSpin, m_options->aspectButton);
m_sizeRatioLocker->setBlockUpdateSignalOnDrag(true);
connect(m_sizeRatioLocker, SIGNAL(sliderValueChanged()), SLOT(emitSettingChanged()));
connect(m_sizeRatioLocker, SIGNAL(aspectButtonChanged()), SLOT(emitSettingChanged()));
connect(m_options->proportionalBox, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_options->proportionalBox, SIGNAL(clicked(bool)), SLOT(emitSettingChanged()));
connect(m_options->shapeBox, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged()));
connect(m_options->imageUrl, SIGNAL(textChanged(QString)), SLOT(emitSettingChanged()));
setConfigurationPage(m_options);
}
KisSprayShapeOption::~KisSprayShapeOption()
{
delete m_options;
}
int KisSprayShapeOption::shape() const
{
return m_options->shapeBox->currentIndex();
}
void KisSprayShapeOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const
{
setting->setProperty(SPRAYSHAPE_ENABLED, isChecked());
setting->setProperty(SPRAYSHAPE_SHAPE, shape());
setting->setProperty(SPRAYSHAPE_USE_ASPECT, m_options->aspectButton->keepAspectRatio());
setting->setProperty(SPRAYSHAPE_PROPORTIONAL, m_options->proportionalBox->isChecked());
setting->setProperty(SPRAYSHAPE_WIDTH, m_options->widthSpin->value());
setting->setProperty(SPRAYSHAPE_HEIGHT, m_options->heightSpin->value());
setting->setProperty(SPRAYSHAPE_IMAGE_URL, m_options->imageUrl->fileName());
}
void KisSprayShapeOption::readOptionSetting(const KisPropertiesConfigurationSP setting)
{
// in 2.2 there is not shape enabled so true by default
setChecked(setting->getBool(SPRAYSHAPE_ENABLED, true));
m_options->shapeBox->setCurrentIndex(setting->getInt(SPRAYSHAPE_SHAPE));
m_options->proportionalBox->setChecked(setting->getBool(SPRAYSHAPE_PROPORTIONAL));
m_options->aspectButton->setKeepAspectRatio(setting->getBool(SPRAYSHAPE_USE_ASPECT, false));
m_options->widthSpin->setValue(setting->getInt(SPRAYSHAPE_WIDTH));
m_options->heightSpin->setValue(setting->getInt(SPRAYSHAPE_HEIGHT));
m_options->imageUrl->setFileName(setting->getString(SPRAYSHAPE_IMAGE_URL));
}
void KisSprayShapeOption::prepareImage()
{
QString path = m_options->imageUrl->fileName();
if (QFile::exists(path)) {
QImage image(path);
if (!image.isNull()) {
KisSignalsBlocker b(m_options->widthSpin, m_options->heightSpin);
m_options->widthSpin->setValue(image.width());
m_options->heightSpin->setValue(image.height());
}
}
}
void KisSprayShapeOption::changeSizeUI(bool proportionalSize)
{
// if proportionalSize is false, pixel size is used
if (!proportionalSize) {
m_options->widthSpin->setMaximum(m_maxSize);
m_options->widthSpin->setSuffix(i18n(" px"));
m_options->heightSpin->setMaximum(m_maxSize);
m_options->heightSpin->setSuffix(i18n(" px"));
}
else {
m_options->widthSpin->setMaximum(100);
m_options->widthSpin->setSuffix(i18n("%"));
m_options->heightSpin->setMaximum(100);
m_options->heightSpin->setSuffix(i18n("%"));
}
}
diff --git a/plugins/paintops/tangentnormal/kis_tangent_normal_paintop_settings_widget.cpp b/plugins/paintops/tangentnormal/kis_tangent_normal_paintop_settings_widget.cpp
index dd181346ed..6dd556bd1b 100644
--- a/plugins/paintops/tangentnormal/kis_tangent_normal_paintop_settings_widget.cpp
+++ b/plugins/paintops/tangentnormal/kis_tangent_normal_paintop_settings_widget.cpp
@@ -1,84 +1,84 @@
/*
* Copyright (C) 2015 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.
*/
#include "kis_tangent_normal_paintop_settings_widget.h"
#include "kis_brush_based_paintop_settings.h"
#include "kis_tangent_tilt_option.h"
#include <kis_properties_configuration.h>
#include <kis_paintop_settings_widget.h>
#include <kis_curve_option_widget.h>
#include <kis_pressure_rotation_option.h>
#include <kis_pressure_scatter_option_widget.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_flow_option.h>
#include <kis_paint_action_type_option.h>
#include <kis_pressure_size_option.h>
#include <kis_pressure_softness_option.h>
#include <kis_pressure_sharpness_option_widget.h>
#include <kis_airbrush_option_widget.h>
#include <kis_pressure_flow_opacity_option_widget.h>
#include <kis_pressure_spacing_option_widget.h>
#include <kis_pressure_rate_option.h>
#include <kis_compositeop_option.h>
#include "kis_texture_option.h"
#include <kis_pressure_mirror_option_widget.h>
#include "kis_pressure_texture_strength_option.h"
-
+#include <KisGlobalResourcesInterface.h>
KisTangentNormalPaintOpSettingsWidget::KisTangentNormalPaintOpSettingsWidget(QWidget* parent):
KisBrushBasedPaintopOptionWidget(parent)
{
setObjectName("brush option widget");
setPrecisionEnabled(true);
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureFlowOption(), i18n("0%"), i18n("100%")), i18n("Flow"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size"));
addPaintOpOption(new KisTangentTiltOption(), i18n("Tangent Tilt"));
addPaintOpOption(new KisPressureSpacingOptionWidget(), i18n("Spacing"));
addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSoftnessOption(), i18n("Soft"), i18n("Hard")), i18n("Softness"));
addPaintOpOption(new KisPressureSharpnessOptionWidget(), i18n("Sharpness"));
addPaintOpOption(new KisPressureScatterOptionWidget(), i18n("Scatter"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")), i18n("Rotation"));
addPaintOpOption(new KisAirbrushOptionWidget(false), i18n("Airbrush"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureRateOption(), i18n("0%"), i18n("100%")), i18n("Rate"));
addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode"));
addPaintOpOption(new KisTextureOption(), i18n("Pattern"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength"));
}
KisTangentNormalPaintOpSettingsWidget::~KisTangentNormalPaintOpSettingsWidget() { }
KisPropertiesConfigurationSP KisTangentNormalPaintOpSettingsWidget::configuration() const
{
- KisBrushBasedPaintOpSettingsSP config = new KisBrushBasedPaintOpSettings();
+ KisBrushBasedPaintOpSettingsSP config = new KisBrushBasedPaintOpSettings(KisGlobalResourcesInterface::instance());
config->setOptionsWidget(const_cast<KisTangentNormalPaintOpSettingsWidget*>(this));
config->setProperty("paintop", "tangentnormal");
writeConfiguration(config);
return config;
}
diff --git a/plugins/tools/basictools/kis_tool_colorpicker.cc b/plugins/tools/basictools/kis_tool_colorpicker.cc
index 19a090d52e..eb06bcdd9b 100644
--- a/plugins/tools/basictools/kis_tool_colorpicker.cc
+++ b/plugins/tools/basictools/kis_tool_colorpicker.cc
@@ -1,421 +1,365 @@
/*
* Copyright (c) 1999 Matthias Elter <me@kde.org>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2018 Emmet & Eoin O'Neill <emmetoneill.pdx@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_colorpicker.h"
#include <boost/thread/locks.hpp>
#include <QMessageBox>
#include "kis_cursor.h"
#include "KisDocument.h"
#include "kis_canvas2.h"
#include "KisReferenceImagesLayer.h"
#include "KoCanvasBase.h"
#include "kis_random_accessor_ng.h"
#include "KoResourceServerProvider.h"
#include <KoMixColorsOp.h>
#include "kis_wrapped_rect.h"
#include "kis_tool_utils.h"
namespace
{
// GUI ComboBox index constants
const int SAMPLE_MERGED = 0;
}
KisToolColorPicker::KisToolColorPicker(KoCanvasBase *canvas)
: KisTool(canvas, KisCursor::pickerCursor()),
m_config(new KisToolUtils::ColorPickerConfig)
{
setObjectName("tool_colorpicker");
m_isActivated = false;
m_optionsWidget = 0;
m_pickedColor = KoColor();
- KoResourceServer<KoColorSet> *srv = KoResourceServerProvider::instance()->paletteServer();
- srv->addObserver(this);
}
KisToolColorPicker::~KisToolColorPicker()
{
if (m_isActivated) {
m_config->save(m_toolActivationSource == KisTool::DefaultActivation);
}
- KoResourceServer<KoColorSet> *srv = KoResourceServerProvider::instance()->paletteServer();
- srv->removeObserver(this);
}
void KisToolColorPicker::paint(QPainter &gc, const KoViewConverter &converter)
{
Q_UNUSED(gc);
Q_UNUSED(converter);
}
void KisToolColorPicker::activate(ToolActivation activation, const QSet<KoShape*> &shapes)
{
m_isActivated = true;
m_toolActivationSource = activation;
m_config->load(m_toolActivationSource == KisTool::DefaultActivation);
updateOptionWidget();
KisTool::activate(activation, shapes);
}
void KisToolColorPicker::deactivate()
{
m_config->save(m_toolActivationSource == KisTool::DefaultActivation);
m_isActivated = false;
KisTool::deactivate();
}
bool KisToolColorPicker::pickColor(const QPointF &pos)
{
// Timer check.
if (m_colorPickerDelayTimer.isActive()) {
return false;
}
else {
m_colorPickerDelayTimer.setSingleShot(true);
m_colorPickerDelayTimer.start(100);
}
QScopedPointer<boost::lock_guard<KisImage>> imageLocker;
m_pickedColor.setOpacity(0.0);
// Pick from reference images.
if (m_optionsWidget->cmbSources->currentIndex() == SAMPLE_MERGED) {
auto *kisCanvas = dynamic_cast<KisCanvas2 *>(canvas());
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(kisCanvas, false);
KisSharedPtr<KisReferenceImagesLayer> referenceImageLayer =
kisCanvas->imageView()->document()->referenceImagesLayer();
if (referenceImageLayer && kisCanvas->referenceImagesDecoration()->visible()) {
QColor color = referenceImageLayer->getPixel(pos);
if (color.isValid()) {
m_pickedColor.fromQColor(color);
}
}
}
if (m_pickedColor.opacityU8() == OPACITY_TRANSPARENT_U8) {
if (!currentImage()->bounds().contains(pos.toPoint()) &&
!currentImage()->wrapAroundModePermitted()) {
return false;
}
KisPaintDeviceSP dev;
if (m_optionsWidget->cmbSources->currentIndex() != SAMPLE_MERGED &&
currentNode() && currentNode()->colorPickSourceDevice()) {
dev = currentNode()->colorPickSourceDevice();
}
else {
imageLocker.reset(new boost::lock_guard<KisImage>(*currentImage()));
dev = currentImage()->projection();
}
KoColor previousColor = canvas()->resourceManager()->foregroundColor();
KisToolUtils::pickColor(m_pickedColor, dev, pos.toPoint(), &previousColor, m_config->radius, m_config->blend);
}
if (m_config->updateColor &&
m_pickedColor.opacityU8() != OPACITY_TRANSPARENT_U8) {
KoColor publicColor = m_pickedColor;
publicColor.setOpacity(OPACITY_OPAQUE_U8); // Alpha is unwanted for FG and BG colors.
if (m_config->toForegroundColor) {
canvas()->resourceManager()->setResource(KoCanvasResourceProvider::ForegroundColor, publicColor);
}
else {
canvas()->resourceManager()->setResource(KoCanvasResourceProvider::BackgroundColor, publicColor);
}
}
return true;
}
void KisToolColorPicker::beginPrimaryAction(KoPointerEvent *event)
{
bool sampleMerged = m_optionsWidget->cmbSources->currentIndex() == SAMPLE_MERGED;
if (!sampleMerged) {
if (!currentNode()) {
QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Cannot pick a color as no layer is active."));
event->ignore();
return;
}
if (!currentNode()->visible()) {
QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Cannot pick a color as the active layer is not visible."));
event->ignore();
return;
}
}
QPoint pos = convertToImagePixelCoordFloored(event);
setMode(KisTool::PAINT_MODE);
bool picked = pickColor(pos);
if (!picked) {
// Color picking has to start in the visible part of the layer
event->ignore();
return;
}
displayPickedColor();
}
void KisToolColorPicker::continuePrimaryAction(KoPointerEvent *event)
{
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
QPoint pos = convertToImagePixelCoordFloored(event);
pickColor(pos);
displayPickedColor();
}
#include "kis_display_color_converter.h"
void KisToolColorPicker::endPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
if (m_config->addColorToCurrentPalette) {
KisSwatch swatch;
swatch.setColor(m_pickedColor);
// We don't ask for a name, too intrusive here
-
- KoColorSet *palette = m_palettes.at(m_optionsWidget->cmbPalette->currentIndex());
+ KoColorSetSP palette = m_palettes.at(m_optionsWidget->cmbPalette->currentIndex());
palette->add(swatch);
if (!palette->save()) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Cannot write to palette file %1. Maybe it is read-only.", palette->filename()));
- }
+ }
}
}
struct PickedChannel {
QString name;
QString valueText;
};
void KisToolColorPicker::displayPickedColor()
{
if (m_pickedColor.data() && m_optionsWidget) {
QList<KoChannelInfo *> channels = m_pickedColor.colorSpace()->channels();
m_optionsWidget->listViewChannels->clear();
QVector<PickedChannel> pickedChannels;
for (int i = 0; i < channels.count(); ++i) {
pickedChannels.append(PickedChannel());
}
for (int i = 0; i < channels.count(); ++i) {
PickedChannel pc;
pc.name = channels[i]->name();
if (m_config->normaliseValues) {
pc.valueText = m_pickedColor.colorSpace()->normalisedChannelValueText(m_pickedColor.data(), i);
} else {
pc.valueText = m_pickedColor.colorSpace()->channelValueText(m_pickedColor.data(), i);
}
pickedChannels[channels[i]->displayPosition()] = pc;
}
Q_FOREACH (const PickedChannel &pc, pickedChannels) {
QTreeWidgetItem *item = new QTreeWidgetItem(m_optionsWidget->listViewChannels);
item->setText(0, pc.name);
item->setText(1, pc.valueText);
}
KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas());
KoColor newColor = kritaCanvas->displayColorConverter()->applyDisplayFiltering(m_pickedColor, Float32BitsColorDepthID);
QVector<float> values(4);
newColor.colorSpace()->normalisedChannelsValue(newColor.data(), values);
for (int i = 0; i < values.size(); i++) {
QTreeWidgetItem *item = new QTreeWidgetItem(m_optionsWidget->listViewChannels);
item->setText(0, QString("DisplayCh%1").arg(i));
item->setText(1, QString::number(values[i]));
}
}
}
QWidget* KisToolColorPicker::createOptionWidget()
{
m_optionsWidget = new ColorPickerOptionsWidget(0);
m_optionsWidget->setObjectName(toolId() + " option widget");
m_optionsWidget->listViewChannels->setSortingEnabled(false);
// 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);
// Initialize blend KisSliderSpinBox
m_optionsWidget->blend->setRange(0,100);
m_optionsWidget->blend->setSuffix(i18n("%"));
updateOptionWidget();
connect(m_optionsWidget->cbUpdateCurrentColor, SIGNAL(toggled(bool)), SLOT(slotSetUpdateColor(bool)));
connect(m_optionsWidget->cbNormaliseValues, SIGNAL(toggled(bool)), SLOT(slotSetNormaliseValues(bool)));
connect(m_optionsWidget->cbPalette, SIGNAL(toggled(bool)),
SLOT(slotSetAddPalette(bool)));
connect(m_optionsWidget->radius, SIGNAL(valueChanged(int)),
SLOT(slotChangeRadius(int)));
connect(m_optionsWidget->blend, SIGNAL(valueChanged(int)),
SLOT(slotChangeBlend(int)));
connect(m_optionsWidget->cmbSources, SIGNAL(currentIndexChanged(int)),
SLOT(slotSetColorSource(int)));
KoResourceServer<KoColorSet> *srv = KoResourceServerProvider::instance()->paletteServer();
if (!srv) {
return m_optionsWidget;
}
- QList<KoColorSet*> palettes = srv->resources();
-
- Q_FOREACH (KoColorSet *palette, palettes) {
- if (palette) {
- m_optionsWidget->cmbPalette->addSqueezedItem(palette->name());
- m_palettes.append(palette);
- }
- }
+ m_optionsWidget->cmbPalette->setModel(srv->resourceModel());
+ m_optionsWidget->cmbPalette->setModelColumn(KisResourceModel::Name);
return m_optionsWidget;
}
void KisToolColorPicker::updateOptionWidget()
{
if (!m_optionsWidget) return;
m_optionsWidget->cbNormaliseValues->setChecked(m_config->normaliseValues);
m_optionsWidget->cbUpdateCurrentColor->setChecked(m_config->updateColor);
m_optionsWidget->cmbSources->setCurrentIndex(SAMPLE_MERGED + !m_config->sampleMerged);
m_optionsWidget->cbPalette->setChecked(m_config->addColorToCurrentPalette);
m_optionsWidget->radius->setValue(m_config->radius);
m_optionsWidget->blend->setValue(m_config->blend);
}
void KisToolColorPicker::setToForeground(bool newValue)
{
m_config->toForegroundColor = newValue;
emit toForegroundChanged();
}
bool KisToolColorPicker::toForeground() const
{
return m_config->toForegroundColor;
}
void KisToolColorPicker::slotSetUpdateColor(bool state)
{
m_config->updateColor = state;
}
void KisToolColorPicker::slotSetNormaliseValues(bool state)
{
m_config->normaliseValues = state;
displayPickedColor();
}
void KisToolColorPicker::slotSetAddPalette(bool state)
{
m_config->addColorToCurrentPalette = state;
}
void KisToolColorPicker::slotChangeRadius(int value)
{
m_config->radius = value;
}
void KisToolColorPicker::slotChangeBlend(int value)
{
m_config->blend = value;
}
void KisToolColorPicker::slotSetColorSource(int value)
{
m_config->sampleMerged = value == SAMPLE_MERGED;
}
-void KisToolColorPicker::unsetResourceServer()
+void KisToolColorPicker::slotAddPalette(KoResourceSP resource)
{
- KoResourceServer<KoColorSet> *srv = KoResourceServerProvider::instance()->paletteServer();
- srv->removeObserver(this);
-}
-
-void KisToolColorPicker::resourceAdded(KoColorSet* resource){
- if(!resource || !m_optionsWidget) return;
- if(m_palettes.contains(resource)) return;
-
- if(m_config->addColorToCurrentPalette){
- updateCmbPalette();
+ KoColorSetSP palette = resource.dynamicCast<KoColorSet>();
+ if (palette) {
+ m_optionsWidget->cmbPalette->addSqueezedItem(palette->name());
+ m_palettes.append(palette);
}
}
-
-void KisToolColorPicker::removingResource(KoColorSet* resource){
- if(!resource || !m_optionsWidget) return;
- if(!m_palettes.contains(resource)) return;
-
- if(m_config->addColorToCurrentPalette){
- updateCmbPalette();
- }
-}
-
-void KisToolColorPicker::updateCmbPalette(){
- m_optionsWidget->cmbPalette->clear();
- m_palettes.clear();
-
- KoResourceServer<KoColorSet> *srv = KoResourceServerProvider::instance()->paletteServer();
-
- if (!srv) {
- return ;
- }
-
- QList<KoColorSet*> palettes = srv->resources();
-
- Q_FOREACH (KoColorSet *palette, palettes) {
- if (palette) {
- m_optionsWidget->cmbPalette->addSqueezedItem(palette->name());
- m_palettes.append(palette);
- }
- }
-
-}
-
-void KisToolColorPicker::resourceChanged(PointerType /*resource*/)
-{}
-
-void KisToolColorPicker::syncTaggedResourceView() {}
-
-void KisToolColorPicker::syncTagAddition(const QString& /*tag*/) {}
-
-void KisToolColorPicker::syncTagRemoval(const QString& /*tag*/) {}
\ No newline at end of file
diff --git a/plugins/tools/basictools/kis_tool_colorpicker.h b/plugins/tools/basictools/kis_tool_colorpicker.h
index a7c078570e..75415d625e 100644
--- a/plugins/tools/basictools/kis_tool_colorpicker.h
+++ b/plugins/tools/basictools/kis_tool_colorpicker.h
@@ -1,152 +1,141 @@
/*
* Copyright (c) 1999 Matthias Elter <elter@kde.org>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2018 Emmet & Eoin O'Neill <emmetoneill.pdx@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_COLOR_PICKER_H_
#define KIS_TOOL_COLOR_PICKER_H_
#include <QTimer>
#include "KoToolFactoryBase.h"
#include "ui_wdgcolorpicker.h"
#include "kis_tool.h"
#include <kis_icon.h>
-#include <KoResourceServerObserver.h>
-
-class KoResource;
-class KoColorSet;
+#include <KoColorSet.h>
namespace KisToolUtils {
struct ColorPickerConfig;
}
class ColorPickerOptionsWidget : public QWidget, public Ui::ColorPickerOptionsWidget
{
Q_OBJECT
public:
ColorPickerOptionsWidget(QWidget *parent) : QWidget(parent) {
setupUi(this);
}
};
-class KisToolColorPicker : public KisTool,public KoResourceServerObserver<KoColorSet>
+class KisToolColorPicker : public KisTool
{
Q_OBJECT
Q_PROPERTY(bool toForeground READ toForeground WRITE setToForeground NOTIFY toForegroundChanged)
public:
KisToolColorPicker(KoCanvasBase *canvas);
~KisToolColorPicker() override;
public:
struct Configuration {
Configuration();
bool toForegroundColor;
bool updateColor;
bool addPalette;
bool normaliseValues;
bool sampleMerged;
int radius;
int blend;
void save(ToolActivation activation) const;
void load(ToolActivation activation);
};
public:
QWidget* createOptionWidget() override;
void beginPrimaryAction(KoPointerEvent *event) override;
void continuePrimaryAction(KoPointerEvent *event) override;
void endPrimaryAction(KoPointerEvent *event) override;
void paint(QPainter &gc, const KoViewConverter &converter) override;
bool toForeground() const;
-public: //KoResourceServerObserver
- void unsetResourceServer() override;
- void resourceAdded(KoColorSet* resource) override;
- void removingResource(KoColorSet* resource) override;
- void resourceChanged(KoColorSet* resource) override;
- void syncTaggedResourceView() override;
- void syncTagAddition(const QString& tag) override;
- void syncTagRemoval(const QString& tag) override;
-
Q_SIGNALS:
void toForegroundChanged();
protected:
void activate(ToolActivation activation, const QSet<KoShape*> &) override;
void deactivate() override;
public Q_SLOTS:
void setToForeground(bool newValue);
void slotSetUpdateColor(bool);
void slotSetNormaliseValues(bool);
void slotSetAddPalette(bool);
void slotChangeRadius(int);
void slotChangeBlend(int);
+ void slotAddPalette(KoResourceSP resource);
void slotSetColorSource(int value);
private:
void displayPickedColor();
bool pickColor(const QPointF& pos);
void updateOptionWidget();
- void updateCmbPalette();
+
// Configuration
QScopedPointer<KisToolUtils::ColorPickerConfig> m_config;
ToolActivation m_toolActivationSource;
bool m_isActivated;
KoColor m_pickedColor;
// Used to skip some tablet events and update color less often
QTimer m_colorPickerDelayTimer;
ColorPickerOptionsWidget *m_optionsWidget;
- QList<KoColorSet*> m_palettes;
+ QList<KoColorSetSP> m_palettes;
};
class KisToolColorPickerFactory : public KoToolFactoryBase
{
public:
KisToolColorPickerFactory()
: KoToolFactoryBase("KritaSelected/KisToolColorPicker") {
setToolTip(i18n("Color Selector Tool"));
setSection(TOOL_TYPE_FILL);
setPriority(2);
setIconName(koIconNameCStr("krita_tool_color_picker"));
setShortcut(QKeySequence(Qt::Key_P));
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
}
~KisToolColorPickerFactory() override {}
KoToolBase *createTool(KoCanvasBase *canvas) override {
return new KisToolColorPicker(canvas);
}
};
#endif // KIS_TOOL_COLOR_PICKER_H_
diff --git a/plugins/tools/karbonplugins/CMakeLists.txt b/plugins/tools/karbonplugins/CMakeLists.txt
index 11fdd0957e..03cec287de 100644
--- a/plugins/tools/karbonplugins/CMakeLists.txt
+++ b/plugins/tools/karbonplugins/CMakeLists.txt
@@ -1,2 +1 @@
add_subdirectory( tools )
-add_subdirectory( filtereffects )
diff --git a/plugins/tools/karbonplugins/filtereffects/BlendEffect.cpp b/plugins/tools/karbonplugins/filtereffects/BlendEffect.cpp
deleted file mode 100644
index 0f10b6db27..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/BlendEffect.cpp
+++ /dev/null
@@ -1,188 +0,0 @@
-/* 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.
- */
-
-#include "BlendEffect.h"
-#include "ColorChannelConversion.h"
-#include <KoFilterEffectRenderContext.h>
-#include <KoXmlWriter.h>
-#include <KoXmlReader.h>
-#include <klocalizedstring.h>
-#include <QRect>
-#include <QImage>
-#include <math.h>
-
-BlendEffect::BlendEffect()
- : KoFilterEffect(BlendEffectId, i18n("Blend"))
- , m_blendMode(Normal)
-{
- setRequiredInputCount(2);
- setMaximalInputCount(2);
-}
-
-BlendEffect::BlendMode BlendEffect::blendMode() const
-{
- return m_blendMode;
-}
-
-void BlendEffect::setBlendMode(BlendMode blendMode)
-{
- m_blendMode = blendMode;
-}
-
-QImage BlendEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const
-{
- Q_UNUSED(context);
- return image;
-}
-
-QImage BlendEffect::processImages(const QList<QImage> &images, const KoFilterEffectRenderContext &context) const
-{
- int imageCount = images.count();
- if (!imageCount) {
- return QImage();
- }
-
- QImage result = images[0];
- if (images.count() != 2) {
- return result;
- }
- const QRgb *src = (const QRgb *)images[1].constBits();
- QRgb *dst = (QRgb *)result.bits();
- int w = result.width();
-
- qreal sa, sr, sg, sb;
- qreal da, dr, dg, db;
- int pixel = 0;
-
- QRect roi = context.filterRegion().toRect();
- for (int row = roi.top(); row < roi.bottom(); ++row) {
- for (int col = roi.left(); col < roi.right(); ++col) {
- pixel = row * w + col;
- const QRgb &s = src[pixel];
- QRgb &d = dst[pixel];
-
- sa = fromIntColor[qAlpha(s)];
- sr = fromIntColor[qRed(s)];
- sg = fromIntColor[qGreen(s)];
- sb = fromIntColor[qBlue(s)];
-
- da = fromIntColor[qAlpha(d)];
- dr = fromIntColor[qRed(d)];
- dg = fromIntColor[qGreen(d)];
- db = fromIntColor[qBlue(d)];
-
- switch (m_blendMode) {
- case Normal:
- dr = (qreal(1.0) - da) * sr + dr;
- dg = (qreal(1.0) - da) * sg + dg;
- db = (qreal(1.0) - da) * sb + db;
- break;
- case Multiply:
- dr = (qreal(1.0) - da) * sr + (qreal(1.0) - sa) * dr + dr * sr;
- dg = (qreal(1.0) - da) * sg + (qreal(1.0) - sa) * dg + dg * sg;
- db = (qreal(1.0) - da) * sb + (qreal(1.0) - sa) * db + db * sb;
- break;
- case Screen:
- dr = sr + dr - dr * sr;
- dg = sg + dg - dg * sg;
- db = sb + db - db * sb;
- break;
- case Darken:
- dr = qMin((qreal(1.0) - da) * sr + dr, (qreal(1.0) - sa) * dr + sr);
- dg = qMin((qreal(1.0) - da) * sg + dg, (qreal(1.0) - sa) * dg + sg);
- db = qMin((qreal(1.0) - da) * sb + db, (qreal(1.0) - sa) * db + sb);
- break;
- case Lighten:
- dr = qMax((qreal(1.0) - da) * sr + dr, (qreal(1.0) - sa) * dr + sr);
- dg = qMax((qreal(1.0) - da) * sg + dg, (qreal(1.0) - sa) * dg + sg);
- db = qMax((qreal(1.0) - da) * sb + db, (qreal(1.0) - sa) * db + sb);
- break;
- }
- da = qreal(1.0) - (qreal(1.0) - da) * (qreal(1.0) - sa);
-
- d = qRgba(static_cast<quint8>(qBound(qreal(0.0), dr * qreal(255.0), qreal(255.0))),
- static_cast<quint8>(qBound(qreal(0.0), dg * qreal(255.0), qreal(255.0))),
- static_cast<quint8>(qBound(qreal(0.0), db * qreal(255.0), qreal(255.0))),
- static_cast<quint8>(qBound(qreal(0.0), da * qreal(255.0), qreal(255.0))));
- }
- }
-
- return result;
-}
-
-bool BlendEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &)
-{
- if (element.tagName() != id()) {
- return false;
- }
-
- m_blendMode = Normal; // default blend mode
-
- QString modeStr = element.attribute("mode");
- if (!modeStr.isEmpty()) {
- if (modeStr == "multiply") {
- m_blendMode = Multiply;
- } else if (modeStr == "screen") {
- m_blendMode = Screen;
- } else if (modeStr == "darken") {
- m_blendMode = Darken;
- } else if (modeStr == "lighten") {
- m_blendMode = Lighten;
- }
- }
-
- if (element.hasAttribute("in2")) {
- if (inputs().count() == 2) {
- setInput(1, element.attribute("in2"));
- } else {
- addInput(element.attribute("in2"));
- }
- }
-
- return true;
-}
-
-void BlendEffect::save(KoXmlWriter &writer)
-{
- writer.startElement(BlendEffectId);
-
- saveCommonAttributes(writer);
-
- switch (m_blendMode) {
- case Normal:
- writer.addAttribute("mode", "normal");
- break;
- case Multiply:
- writer.addAttribute("mode", "multiply");
- break;
- case Screen:
- writer.addAttribute("mode", "screen");
- break;
- case Darken:
- writer.addAttribute("mode", "darken");
- break;
- case Lighten:
- writer.addAttribute("mode", "lighten");
- break;
- }
-
- writer.addAttribute("in2", inputs().at(1));
-
- writer.endElement();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/BlendEffect.h b/plugins/tools/karbonplugins/filtereffects/BlendEffect.h
deleted file mode 100644
index 272133e200..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/BlendEffect.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* 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 BLENDEFFECT_H
-#define BLENDEFFECT_H
-
-#include "KoFilterEffect.h"
-
-#define BlendEffectId "feBlend"
-
-/// A color matrix effect
-class BlendEffect : public KoFilterEffect
-{
-public:
- enum BlendMode {
- Normal,
- Multiply,
- Screen,
- Darken,
- Lighten
- };
-
- BlendEffect();
-
- /// Returns the type of the color matrix
- BlendMode blendMode() const;
-
- /// Sets the blend mode
- void setBlendMode(BlendMode blendMode);
-
- /// reimplemented from KoFilterEffect
- QImage processImage(const QImage &image, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- QImage processImages(const QList<QImage> &images, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- bool load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context) override;
- /// reimplemented from KoFilterEffect
- void save(KoXmlWriter &writer) override;
-
-private:
-
- BlendMode m_blendMode; ///< the blend mode
-};
-
-#endif // BLENDEFFECT_H
diff --git a/plugins/tools/karbonplugins/filtereffects/BlendEffectConfigWidget.cpp b/plugins/tools/karbonplugins/filtereffects/BlendEffectConfigWidget.cpp
deleted file mode 100644
index b0821bfc14..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/BlendEffectConfigWidget.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/* 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.
- */
-
-#include "BlendEffectConfigWidget.h"
-#include "BlendEffect.h"
-#include "KoFilterEffect.h"
-
-#include <kcombobox.h>
-#include <klocalizedstring.h>
-
-#include <QGridLayout>
-#include <QLabel>
-
-BlendEffectConfigWidget::BlendEffectConfigWidget(QWidget *parent)
- : KoFilterEffectConfigWidgetBase(parent)
- , m_effect(0)
-{
- QGridLayout *g = new QGridLayout(this);
-
- g->addWidget(new QLabel(i18n("Blend mode"), this), 0, 0);
- m_mode = new KComboBox(this);
- m_mode->addItem(i18n("Normal"));
- m_mode->addItem(i18n("Multiply"));
- m_mode->addItem(i18n("Screen"));
- m_mode->addItem(i18n("Darken"));
- m_mode->addItem(i18n("Lighten"));
- g->addWidget(m_mode, 0, 1);
- g->addItem(new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 1, 0);
-
- setLayout(g);
-
- connect(m_mode, SIGNAL(currentIndexChanged(int)), this, SLOT(modeChanged(int)));
-}
-
-bool BlendEffectConfigWidget::editFilterEffect(KoFilterEffect *filterEffect)
-{
- m_effect = dynamic_cast<BlendEffect *>(filterEffect);
- if (!m_effect) {
- return false;
- }
-
- m_mode->blockSignals(true);
-
- switch (m_effect->blendMode()) {
- case BlendEffect::Normal:
- m_mode->setCurrentIndex(0);
- break;
- case BlendEffect::Multiply:
- m_mode->setCurrentIndex(1);
- break;
- case BlendEffect::Screen:
- m_mode->setCurrentIndex(2);
- break;
- case BlendEffect::Darken:
- m_mode->setCurrentIndex(3);
- break;
- case BlendEffect::Lighten:
- m_mode->setCurrentIndex(4);
- break;
- }
-
- m_mode->blockSignals(false);
-
- return true;
-}
-
-void BlendEffectConfigWidget::modeChanged(int index)
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->setBlendMode(static_cast<BlendEffect::BlendMode>(index));
-
- emit filterChanged();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/BlendEffectConfigWidget.h b/plugins/tools/karbonplugins/filtereffects/BlendEffectConfigWidget.h
deleted file mode 100644
index d92753cba0..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/BlendEffectConfigWidget.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* 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 BLENDEFFECTCONFIGWIDGET_H
-#define BLENDEFFECTCONFIGWIDGET_H
-
-#include "KoFilterEffectConfigWidgetBase.h"
-
-class BlendEffect;
-class KoFilterEffect;
-class KComboBox;
-
-class BlendEffectConfigWidget : public KoFilterEffectConfigWidgetBase
-{
- Q_OBJECT
-public:
- explicit BlendEffectConfigWidget(QWidget *parent = 0);
-
- /// reimplemented from KoFilterEffectConfigWidgetBase
- bool editFilterEffect(KoFilterEffect *filterEffect) override;
-
-private Q_SLOTS:
- void modeChanged(int index);
-private:
- KComboBox *m_mode;
- BlendEffect *m_effect;
-};
-
-#endif // BLENDEFFECTCONFIGWIDGET_H
diff --git a/plugins/tools/karbonplugins/filtereffects/BlendEffectFactory.cpp b/plugins/tools/karbonplugins/filtereffects/BlendEffectFactory.cpp
deleted file mode 100644
index bb0259087f..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/BlendEffectFactory.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/* 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.
- */
-
-#include "BlendEffectFactory.h"
-#include "BlendEffect.h"
-#include "BlendEffectConfigWidget.h"
-
-#include <klocalizedstring.h>
-
-BlendEffectFactory::BlendEffectFactory()
- : KoFilterEffectFactoryBase(BlendEffectId, i18n("Blend"))
-{
-}
-
-KoFilterEffect *BlendEffectFactory::createFilterEffect() const
-{
- return new BlendEffect();
-}
-
-KoFilterEffectConfigWidgetBase *BlendEffectFactory::createConfigWidget() const
-{
- return new BlendEffectConfigWidget();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/BlendEffectFactory.h b/plugins/tools/karbonplugins/filtereffects/BlendEffectFactory.h
deleted file mode 100644
index 9d300a9d1f..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/BlendEffectFactory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* 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 BLENDEFFECTFACTORY_H
-#define BLENDEFFECTFACTORY_H
-
-#include "KoFilterEffectFactoryBase.h"
-
-class KoFilterEffect;
-
-class BlendEffectFactory : public KoFilterEffectFactoryBase
-{
-public:
- BlendEffectFactory();
- KoFilterEffect *createFilterEffect() const override;
- KoFilterEffectConfigWidgetBase *createConfigWidget() const override;
-};
-
-#endif // BLENDEFFECTFACTORY_H
diff --git a/plugins/tools/karbonplugins/filtereffects/BlurEffect.cpp b/plugins/tools/karbonplugins/filtereffects/BlurEffect.cpp
deleted file mode 100644
index 9f1b9918a0..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/BlurEffect.cpp
+++ /dev/null
@@ -1,350 +0,0 @@
-/* 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.
- */
-
-#include "BlurEffect.h"
-#include "KoFilterEffectRenderContext.h"
-#include "KoFilterEffectLoadingContext.h"
-#include "KoViewConverter.h"
-#include "KoXmlWriter.h"
-#include "KoXmlReader.h"
-#include <klocalizedstring.h>
-#include <QColor>
-#include <QImage>
-
-// Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
-// fixed to handle alpha channel correctly by Zack Rusin
-void fastbluralpha(QImage &img, int radius)
-{
- if (radius < 1) {
- return;
- }
-
- QRgb *pix = (QRgb *)img.bits();
- int w = img.width();
- int h = img.height();
- int wm = w - 1;
- int hm = h - 1;
- int wh = w * h;
- int div = radius + radius + 1;
-
- int *r = new int[wh];
- int *g = new int[wh];
- int *b = new int[wh];
- int *a = new int[wh];
- int rsum, gsum, bsum, asum, x, y, i, yp, yi, yw;
- QRgb p;
- int *vmin = new int[qMax(w, h)];
-
- int divsum = (div + 1) >> 1;
- divsum *= divsum;
- int *dv = new int[256 * divsum];
- for (i = 0; i < 256 * divsum; ++i) {
- dv[i] = (i / divsum);
- }
-
- yw = yi = 0;
-
- int **stack = new int *[div];
- for (int i = 0; i < div; ++i) {
- stack[i] = new int[4];
- }
-
- int stackpointer;
- int stackstart;
- int *sir;
- int rbs;
- int r1 = radius + 1;
- int routsum, goutsum, boutsum, aoutsum;
- int rinsum, ginsum, binsum, ainsum;
-
- for (y = 0; y < h; ++y) {
- rinsum = ginsum = binsum = ainsum
- = routsum = goutsum = boutsum = aoutsum
- = rsum = gsum = bsum = asum = 0;
- for (i = - radius; i <= radius; ++i) {
- p = pix[yi + qMin(wm, qMax(i, 0))];
- sir = stack[i + radius];
- sir[0] = qRed(p);
- sir[1] = qGreen(p);
- sir[2] = qBlue(p);
- sir[3] = qAlpha(p);
-
- rbs = r1 - abs(i);
- rsum += sir[0] * rbs;
- gsum += sir[1] * rbs;
- bsum += sir[2] * rbs;
- asum += sir[3] * rbs;
-
- if (i > 0) {
- rinsum += sir[0];
- ginsum += sir[1];
- binsum += sir[2];
- ainsum += sir[3];
- } else {
- routsum += sir[0];
- goutsum += sir[1];
- boutsum += sir[2];
- aoutsum += sir[3];
- }
- }
- stackpointer = radius;
-
- for (x = 0; x < w; ++x) {
-
- r[yi] = dv[rsum];
- g[yi] = dv[gsum];
- b[yi] = dv[bsum];
- a[yi] = dv[asum];
-
- rsum -= routsum;
- gsum -= goutsum;
- bsum -= boutsum;
- asum -= aoutsum;
-
- stackstart = stackpointer - radius + div;
- sir = stack[stackstart % div];
-
- routsum -= sir[0];
- goutsum -= sir[1];
- boutsum -= sir[2];
- aoutsum -= sir[3];
-
- if (y == 0) {
- vmin[x] = qMin(x + radius + 1, wm);
- }
- p = pix[yw + vmin[x]];
-
- sir[0] = qRed(p);
- sir[1] = qGreen(p);
- sir[2] = qBlue(p);
- sir[3] = qAlpha(p);
-
- rinsum += sir[0];
- ginsum += sir[1];
- binsum += sir[2];
- ainsum += sir[3];
-
- rsum += rinsum;
- gsum += ginsum;
- bsum += binsum;
- asum += ainsum;
-
- stackpointer = (stackpointer + 1) % div;
- sir = stack[(stackpointer) % div];
-
- routsum += sir[0];
- goutsum += sir[1];
- boutsum += sir[2];
- aoutsum += sir[3];
-
- rinsum -= sir[0];
- ginsum -= sir[1];
- binsum -= sir[2];
- ainsum -= sir[3];
-
- ++yi;
- }
- yw += w;
- }
- for (x = 0; x < w; ++x) {
- rinsum = ginsum = binsum = ainsum
- = routsum = goutsum = boutsum = aoutsum
- = rsum = gsum = bsum = asum = 0;
-
- yp = - radius * w;
-
- for (i = -radius; i <= radius; ++i) {
- yi = qMax(0, yp) + x;
-
- sir = stack[i + radius];
-
- sir[0] = r[yi];
- sir[1] = g[yi];
- sir[2] = b[yi];
- sir[3] = a[yi];
-
- rbs = r1 - abs(i);
-
- rsum += r[yi] * rbs;
- gsum += g[yi] * rbs;
- bsum += b[yi] * rbs;
- asum += a[yi] * rbs;
-
- if (i > 0) {
- rinsum += sir[0];
- ginsum += sir[1];
- binsum += sir[2];
- ainsum += sir[3];
- } else {
- routsum += sir[0];
- goutsum += sir[1];
- boutsum += sir[2];
- aoutsum += sir[3];
- }
-
- if (i < hm) {
- yp += w;
- }
- }
-
- yi = x;
- stackpointer = radius;
-
- for (y = 0; y < h; ++y) {
- pix[yi] = qRgba(dv[rsum], dv[gsum], dv[bsum], dv[asum]);
-
- rsum -= routsum;
- gsum -= goutsum;
- bsum -= boutsum;
- asum -= aoutsum;
-
- stackstart = stackpointer - radius + div;
- sir = stack[stackstart % div];
-
- routsum -= sir[0];
- goutsum -= sir[1];
- boutsum -= sir[2];
- aoutsum -= sir[3];
-
- if (x == 0) {
- vmin[y] = qMin(y + r1, hm) * w;
- }
- p = x + vmin[y];
-
- sir[0] = r[p];
- sir[1] = g[p];
- sir[2] = b[p];
- sir[3] = a[p];
-
- rinsum += sir[0];
- ginsum += sir[1];
- binsum += sir[2];
- ainsum += sir[3];
-
- rsum += rinsum;
- gsum += ginsum;
- bsum += binsum;
- asum += ainsum;
-
- stackpointer = (stackpointer + 1) % div;
- sir = stack[stackpointer];
-
- routsum += sir[0];
- goutsum += sir[1];
- boutsum += sir[2];
- aoutsum += sir[3];
-
- rinsum -= sir[0];
- ginsum -= sir[1];
- binsum -= sir[2];
- ainsum -= sir[3];
-
- yi += w;
- }
- }
- delete [] r;
- delete [] g;
- delete [] b;
- delete [] a;
- delete [] vmin;
- delete [] dv;
-
- for (int i = 0; i < div; ++i) {
- delete [] stack[i];
- }
- delete [] stack;
-}
-
-BlurEffect::BlurEffect()
- : KoFilterEffect(BlurEffectId, i18n("Gaussian blur"))
- , m_deviation(0, 0)
-{
-}
-
-QPointF BlurEffect::deviation() const
-{
- return m_deviation;
-}
-
-void BlurEffect::setDeviation(const QPointF &deviation)
-{
- m_deviation.setX(qMax(qreal(0.0), deviation.x()));
- m_deviation.setY(qMax(qreal(0.0), deviation.y()));
-}
-
-QImage BlurEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const
-{
- if (m_deviation.x() == 0.0 || m_deviation.y() == 0.0) {
- return image;
- }
-
- // TODO: take filter region into account
- // TODO: blur with different kernels in x and y
- // convert from bounding box coordinates
- QPointF dev = context.toUserSpace(m_deviation);
- // transform to view coordinates
- dev = context.viewConverter()->documentToView(dev);
-
- QImage result = image;
- fastbluralpha(result, dev.x());
-
- return result;
-}
-
-bool BlurEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context)
-{
- if (element.tagName() != id()) {
- return false;
- }
-
- QString deviationStr = element.attribute("stdDeviation");
- QStringList params = deviationStr.replace(',', ' ').simplified().split(' ');
-
- switch (params.count()) {
- case 1:
- m_deviation.rx() = params[0].toDouble();
- m_deviation.ry() = m_deviation.x();
- break;
- case 2:
- m_deviation.rx() = params[0].toDouble();
- m_deviation.ry() = params[1].toDouble();
- break;
- default:
- return false;
- }
-
- m_deviation = context.convertFilterPrimitiveUnits(m_deviation);
-
- return true;
-}
-
-void BlurEffect::save(KoXmlWriter &writer)
-{
- writer.startElement(BlurEffectId);
-
- saveCommonAttributes(writer);
-
- if (m_deviation.x() != m_deviation.y()) {
- writer.addAttribute("stdDeviation", QString("%1, %2").arg(m_deviation.x()).arg(m_deviation.y()));
- } else {
- writer.addAttribute("stdDeviation", m_deviation.x());
- }
-
- writer.endElement();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/BlurEffect.h b/plugins/tools/karbonplugins/filtereffects/BlurEffect.h
deleted file mode 100644
index 076ea74dbc..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/BlurEffect.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* 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 BLUREFFECT_H
-#define BLUREFFECT_H
-
-#include "KoFilterEffect.h"
-#include <QPointF>
-
-#define BlurEffectId "feGaussianBlur"
-
-/// A gaussian blur effect
-class BlurEffect : public KoFilterEffect
-{
-public:
- BlurEffect();
-
- QPointF deviation() const;
- void setDeviation(const QPointF &deviation);
-
- /// reimplemented from KoFilterEffect
- QImage processImage(const QImage &image, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- bool load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context) override;
- /// reimplemented from KoFilterEffect
- void save(KoXmlWriter &writer) override;
-
-private:
- QPointF m_deviation;
-};
-
-#endif // BLUREFFECT_H
diff --git a/plugins/tools/karbonplugins/filtereffects/BlurEffectConfigWidget.cpp b/plugins/tools/karbonplugins/filtereffects/BlurEffectConfigWidget.cpp
deleted file mode 100644
index 85b57cdba5..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/BlurEffectConfigWidget.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/* 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.
- */
-
-#include "BlurEffectConfigWidget.h"
-#include "BlurEffect.h"
-#include "KoFilterEffect.h"
-
-#include <QSpinBox>
-#include <klocalizedstring.h>
-
-#include <QGridLayout>
-#include <QLabel>
-
-#include "kis_double_parse_spin_box.h"
-
-BlurEffectConfigWidget::BlurEffectConfigWidget(QWidget *parent)
- : KoFilterEffectConfigWidgetBase(parent), m_effect(0)
-{
- QGridLayout *g = new QGridLayout(this);
-
- g->addWidget(new QLabel(i18n("Radius"), this), 0, 0);
- m_stdDeviation = new KisDoubleParseSpinBox(this);
- m_stdDeviation->setRange(0.0, 100);
- m_stdDeviation->setSingleStep(0.5);
- g->addWidget(m_stdDeviation, 0, 1);
- setLayout(g);
-
- connect(m_stdDeviation, SIGNAL(valueChanged(double)), this, SLOT(stdDeviationChanged(double)));
-}
-
-bool BlurEffectConfigWidget::editFilterEffect(KoFilterEffect *filterEffect)
-{
- m_effect = dynamic_cast<BlurEffect *>(filterEffect);
- if (!m_effect) {
- return false;
- }
-
- m_stdDeviation->setValue(m_effect->deviation().x() * 100.0);
- return true;
-}
-
-void BlurEffectConfigWidget::stdDeviationChanged(double stdDeviation)
-{
- if (!m_effect) {
- return;
- }
-
- qreal newDev = 0.01 * stdDeviation;
- m_effect->setDeviation(QPointF(newDev, newDev));
- emit filterChanged();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/BlurEffectConfigWidget.h b/plugins/tools/karbonplugins/filtereffects/BlurEffectConfigWidget.h
deleted file mode 100644
index 9543fbac0c..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/BlurEffectConfigWidget.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* 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 BLUREFFECTCONFIGWIDGET_H
-#define BLUREFFECTCONFIGWIDGET_H
-
-#include "KoFilterEffectConfigWidgetBase.h"
-
-class KoFilterEffect;
-class BlurEffect;
-class QDoubleSpinBox;
-
-class BlurEffectConfigWidget : public KoFilterEffectConfigWidgetBase
-{
- Q_OBJECT
-public:
- explicit BlurEffectConfigWidget(QWidget *parent = 0);
-
- /// reimplemented from KoFilterEffectConfigWidgetBase
- bool editFilterEffect(KoFilterEffect *filterEffect) override;
-
-private Q_SLOTS:
- void stdDeviationChanged(double stdDeviation);
-
-private:
- BlurEffect *m_effect;
- QDoubleSpinBox *m_stdDeviation;
-};
-
-#endif // BLUREFFECTCONFIGWIDGET_H
diff --git a/plugins/tools/karbonplugins/filtereffects/BlurEffectFactory.cpp b/plugins/tools/karbonplugins/filtereffects/BlurEffectFactory.cpp
deleted file mode 100644
index 4d7046308a..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/BlurEffectFactory.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/* 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.
- */
-
-#include "BlurEffectFactory.h"
-#include "BlurEffect.h"
-#include "BlurEffectConfigWidget.h"
-
-#include <klocalizedstring.h>
-
-BlurEffectFactory::BlurEffectFactory()
- : KoFilterEffectFactoryBase(BlurEffectId, i18n("Gaussian blur"))
-{
-}
-
-KoFilterEffect *BlurEffectFactory::createFilterEffect() const
-{
- return new BlurEffect();
-}
-
-KoFilterEffectConfigWidgetBase *BlurEffectFactory::createConfigWidget() const
-{
- return new BlurEffectConfigWidget();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/BlurEffectFactory.h b/plugins/tools/karbonplugins/filtereffects/BlurEffectFactory.h
deleted file mode 100644
index a932495b7c..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/BlurEffectFactory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* 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 BLUREFFECTFACTORY_H
-#define BLUREFFECTFACTORY_H
-
-#include "KoFilterEffectFactoryBase.h"
-
-class KoFilterEffect;
-
-class BlurEffectFactory : public KoFilterEffectFactoryBase
-{
-public:
- BlurEffectFactory();
- KoFilterEffect *createFilterEffect() const override;
- KoFilterEffectConfigWidgetBase *createConfigWidget() const override;
-};
-
-#endif // BLUREFFECTFACTORY_H
diff --git a/plugins/tools/karbonplugins/filtereffects/CMakeLists.txt b/plugins/tools/karbonplugins/filtereffects/CMakeLists.txt
deleted file mode 100644
index 10e9997ab5..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/CMakeLists.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-set(karbon_filtereffects_SOURCES
- FilterEffectsPlugin.cpp
- BlurEffect.cpp
- BlurEffectFactory.cpp
- BlurEffectConfigWidget.cpp
- OffsetEffect.cpp
- OffsetEffectFactory.cpp
- OffsetEffectConfigWidget.cpp
- MergeEffect.cpp
- MergeEffectFactory.cpp
- MergeEffectConfigWidget.cpp
- ColorMatrixEffect.cpp
- ColorMatrixEffectFactory.cpp
- ColorMatrixEffectConfigWidget.cpp
- FloodEffect.cpp
- FloodEffectFactory.cpp
- FloodEffectConfigWidget.cpp
- CompositeEffect.cpp
- CompositeEffectFactory.cpp
- CompositeEffectConfigWidget.cpp
- BlendEffect.cpp
- BlendEffectFactory.cpp
- BlendEffectConfigWidget.cpp
- ComponentTransferEffect.cpp
- ComponentTransferEffectFactory.cpp
- ComponentTransferEffectConfigWidget.cpp
- ImageEffect.cpp
- ImageEffectFactory.cpp
- ImageEffectConfigWidget.cpp
- MorphologyEffect.cpp
- MorphologyEffectFactory.cpp
- MorphologyEffectConfigWidget.cpp
- ConvolveMatrixEffect.cpp
- ConvolveMatrixEffectFactory.cpp
- ConvolveMatrixEffectConfigWidget.cpp
- MatrixDataModel.cpp
- )
-
-add_library(krita_filtereffects MODULE ${karbon_filtereffects_SOURCES})
-
-target_link_libraries(krita_filtereffects kritaflake kritawidgets kritaui KF5::Completion)
-
-install(TARGETS krita_filtereffects DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
diff --git a/plugins/tools/karbonplugins/filtereffects/ColorChannelConversion.h b/plugins/tools/karbonplugins/filtereffects/ColorChannelConversion.h
deleted file mode 100644
index 66a10f0959..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ColorChannelConversion.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* 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.
-*/
-
-#ifndef COLORCHANNELCONVERSION_H
-#define COLORCHANNELCONVERSION_H
-
-/**
-* Lookup table to convert color value from int [0..255] to double [0..1]
-*/
-static const qreal fromIntColor[256] = {
- 0.00000000000000000, 0.00392156862745098, 0.007843137254901961, 0.01176470588235294, 0.01568627450980392, 0.0196078431372549, 0.02352941176470588, 0.02745098039215686, 0.03137254901960784,
- 0.03529411764705882, 0.0392156862745098, 0.04313725490196078, 0.04705882352941176, 0.05098039215686274, 0.05490196078431372, 0.05882352941176471, 0.06274509803921569,
- 0.06666666666666667, 0.07058823529411765, 0.07450980392156863, 0.07843137254901961, 0.08235294117647059, 0.08627450980392157, 0.09019607843137255, 0.09411764705882353,
- 0.09803921568627451, 0.1019607843137255, 0.1058823529411765, 0.1098039215686274, 0.1137254901960784, 0.1176470588235294, 0.1215686274509804, 0.1254901960784314,
- 0.1294117647058824, 0.1333333333333333, 0.1372549019607843, 0.1411764705882353, 0.1450980392156863, 0.1490196078431373, 0.1529411764705882, 0.1568627450980392,
- 0.1607843137254902, 0.1647058823529412, 0.1686274509803922, 0.1725490196078431, 0.1764705882352941, 0.1803921568627451, 0.1843137254901961, 0.1882352941176471,
- 0.1921568627450981, 0.196078431372549, 0.2, 0.203921568627451, 0.207843137254902, 0.2117647058823529, 0.2156862745098039, 0.2196078431372549,
- 0.2235294117647059, 0.2274509803921569, 0.2313725490196079, 0.2352941176470588, 0.2392156862745098, 0.2431372549019608, 0.2470588235294118, 0.2509803921568627,
- 0.2549019607843137, 0.2588235294117647, 0.2627450980392157, 0.2666666666666667, 0.2705882352941176, 0.2745098039215687, 0.2784313725490196, 0.2823529411764706,
- 0.2862745098039216, 0.2901960784313726, 0.2941176470588235, 0.2980392156862745, 0.3019607843137255, 0.3058823529411765, 0.3098039215686275, 0.3137254901960784,
- 0.3176470588235294, 0.3215686274509804, 0.3254901960784314, 0.3294117647058823, 0.3333333333333333, 0.3372549019607843, 0.3411764705882353, 0.3450980392156863,
- 0.3490196078431372, 0.3529411764705883, 0.3568627450980392, 0.3607843137254902, 0.3647058823529412, 0.3686274509803922, 0.3725490196078431, 0.3764705882352941,
- 0.3803921568627451, 0.3843137254901961, 0.3882352941176471, 0.392156862745098, 0.396078431372549, 0.4, 0.403921568627451, 0.407843137254902,
- 0.4117647058823529, 0.4156862745098039, 0.4196078431372549, 0.4235294117647059, 0.4274509803921568, 0.4313725490196079, 0.4352941176470588, 0.4392156862745098,
- 0.4431372549019608, 0.4470588235294118, 0.4509803921568628, 0.4549019607843137, 0.4588235294117647, 0.4627450980392157, 0.4666666666666667, 0.4705882352941176,
- 0.4745098039215686, 0.4784313725490196, 0.4823529411764706, 0.4862745098039216, 0.4901960784313725, 0.4941176470588236, 0.4980392156862745, 0.5019607843137255,
- 0.5058823529411764, 0.5098039215686274, 0.5137254901960784, 0.5176470588235295, 0.5215686274509804, 0.5254901960784314, 0.5294117647058824, 0.5333333333333333,
- 0.5372549019607843, 0.5411764705882353, 0.5450980392156862, 0.5490196078431373, 0.5529411764705883, 0.5568627450980392, 0.5607843137254902, 0.5647058823529412,
- 0.5686274509803921, 0.5725490196078431, 0.5764705882352941, 0.5803921568627451, 0.5843137254901961, 0.5882352941176471, 0.592156862745098, 0.596078431372549,
- 0.6000000000000000, 0.6039215686274509, 0.6078431372549019, 0.611764705882353, 0.615686274509804, 0.6196078431372549, 0.6235294117647059, 0.6274509803921569,
- 0.6313725490196078, 0.6352941176470588, 0.6392156862745098, 0.6431372549019608, 0.6470588235294118, 0.6509803921568628, 0.6549019607843137, 0.6588235294117647,
- 0.6627450980392157, 0.6666666666666666, 0.6705882352941176, 0.6745098039215687, 0.6784313725490196, 0.6823529411764706, 0.6862745098039216, 0.6901960784313725,
- 0.6941176470588235, 0.6980392156862745, 0.7019607843137254, 0.7058823529411765, 0.7098039215686275, 0.7137254901960784, 0.7176470588235294, 0.7215686274509804,
- 0.7254901960784313, 0.7294117647058823, 0.7333333333333333, 0.7372549019607844, 0.7411764705882353, 0.7450980392156863, 0.7490196078431373, 0.7529411764705882,
- 0.7568627450980392, 0.7607843137254902, 0.7647058823529411, 0.7686274509803922, 0.7725490196078432, 0.7764705882352941, 0.7803921568627451, 0.7843137254901961,
- 0.788235294117647, 0.792156862745098, 0.796078431372549, 0.8, 0.803921568627451, 0.807843137254902, 0.8117647058823529, 0.8156862745098039,
- 0.8196078431372549, 0.8235294117647058, 0.8274509803921568, 0.8313725490196079, 0.8352941176470589, 0.8392156862745098, 0.8431372549019608, 0.8470588235294118,
- 0.8509803921568627, 0.8549019607843137, 0.8588235294117647, 0.8627450980392157, 0.8666666666666667, 0.8705882352941177, 0.8745098039215686, 0.8784313725490196,
- 0.8823529411764706, 0.8862745098039215, 0.8901960784313725, 0.8941176470588236, 0.8980392156862745, 0.9019607843137255, 0.9058823529411765, 0.9098039215686274,
- 0.9137254901960784, 0.9176470588235294, 0.9215686274509803, 0.9254901960784314, 0.9294117647058824, 0.9333333333333333, 0.9372549019607843, 0.9411764705882353,
- 0.9450980392156862, 0.9490196078431372, 0.9529411764705882, 0.9568627450980393, 0.9607843137254902, 0.9647058823529412, 0.9686274509803922, 0.9725490196078431,
- 0.9764705882352941, 0.9803921568627451, 0.984313725490196, 0.9882352941176471, 0.9921568627450981, 0.996078431372549, 1
-};
-
-#endif // COLORCHANNELCONVERSION_H
diff --git a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffect.cpp b/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffect.cpp
deleted file mode 100644
index 06ea096b45..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffect.cpp
+++ /dev/null
@@ -1,280 +0,0 @@
-/* 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.
- */
-
-#include "ColorMatrixEffect.h"
-#include "ColorChannelConversion.h"
-#include <KoFilterEffectRenderContext.h>
-#include <KoXmlWriter.h>
-#include <KoXmlReader.h>
-#include <klocalizedstring.h>
-#include <QRect>
-#include <QImage>
-#include <math.h>
-
-const int MatrixSize = 20;
-const int MatrixRows = 4;
-const int MatrixCols = 5;
-
-ColorMatrixEffect::ColorMatrixEffect()
- : KoFilterEffect(ColorMatrixEffectId, i18n("Color Matrix"))
- , m_type(Matrix)
-{
- setIdentity();
-}
-
-void ColorMatrixEffect::setIdentity()
-{
- // set identity matrix
- m_matrix.resize(MatrixSize);
- for (int r = 0; r < MatrixRows; ++r) {
- for (int c = 0; c < MatrixCols; ++c) {
- m_matrix[r * MatrixCols + c] = r == c ? 1.0 : 0.0;
- }
- }
-}
-
-ColorMatrixEffect::Type ColorMatrixEffect::type() const
-{
- return m_type;
-}
-
-int ColorMatrixEffect::colorMatrixSize()
-{
- return MatrixSize;
-}
-
-int ColorMatrixEffect::colorMatrixRowCount()
-{
- return MatrixRows;
-}
-
-int ColorMatrixEffect::colorMatrixColumnCount()
-{
- return MatrixCols;
-}
-
-QVector<qreal> ColorMatrixEffect::colorMatrix() const
-{
- return m_matrix;
-}
-
-void ColorMatrixEffect::setColorMatrix(const QVector<qreal> &colorMatrix)
-{
- if (colorMatrix.count() == MatrixSize) {
- m_matrix = colorMatrix;
- }
- m_type = Matrix;
-}
-
-void ColorMatrixEffect::setSaturate(qreal value)
-{
- m_type = Saturate;
- m_value = qBound(qreal(0.0), value, qreal(1.0));
-
- setIdentity();
-
- m_matrix[0] = 0.213 + 0.787 * value;
- m_matrix[1] = 0.715 - 0.715 * value;
- m_matrix[2] = 0.072 - 0.072 * value;
-
- m_matrix[5] = 0.213 - 0.213 * value;
- m_matrix[6] = 0.715 + 0.285 * value;
- m_matrix[7] = 0.072 - 0.072 * value;
-
- m_matrix[10] = 0.213 - 0.213 * value;
- m_matrix[11] = 0.715 - 0.715 * value;
- m_matrix[12] = 0.072 + 0.928 * value;
-}
-
-qreal ColorMatrixEffect::saturate() const
-{
- if (m_type == Saturate) {
- return m_value;
- } else {
- return 1.0;
- }
-}
-
-void ColorMatrixEffect::setHueRotate(qreal value)
-{
- m_type = HueRotate;
- m_value = value;
-
- const qreal rad = m_value * M_PI / 180.0;
- const qreal c = cos(rad);
- const qreal s = sin(rad);
-
- setIdentity();
-
- m_matrix[0] = 0.213 + 0.787 * c - 0.213 * s;
- m_matrix[1] = 0.715 - 0.715 * c - 0.715 * s;
- m_matrix[2] = 0.072 - 0.072 * c + 0.928 * s;
-
- m_matrix[5] = 0.213 - 0.213 * c + 0.143 * s;
- m_matrix[6] = 0.715 + 0.285 * c + 0.140 * s;
- m_matrix[7] = 0.072 - 0.072 * c - 0.283 * s;
-
- m_matrix[10] = 0.213 - 0.213 * c - 0.787 * s;
- m_matrix[11] = 0.715 - 0.715 * c + 0.715 * s;
- m_matrix[12] = 0.072 + 0.928 * c + 0.072 * s;
-}
-
-qreal ColorMatrixEffect::hueRotate() const
-{
- if (m_type == HueRotate) {
- return m_value;
- } else {
- return 0.0;
- }
-}
-
-void ColorMatrixEffect::setLuminanceAlpha()
-{
- m_type = LuminanceAlpha;
-
- memset(m_matrix.data(), 0, MatrixSize * sizeof(qreal));
-
- m_matrix[15] = 0.2125;
- m_matrix[16] = 0.7154;
- m_matrix[17] = 0.0721;
- m_matrix[18] = 0.0;
-}
-
-QImage ColorMatrixEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const
-{
- QImage result = image;
-
- const QRgb *src = (const QRgb *)image.constBits();
- QRgb *dst = (QRgb *)result.bits();
- int w = result.width();
-
- const qreal *m = m_matrix.data();
- qreal sa, sr, sg, sb;
- qreal da, dr, dg, db;
-
- QRect roi = context.filterRegion().toRect();
- for (int row = roi.top(); row < roi.bottom(); ++row) {
- for (int col = roi.left(); col < roi.right(); ++col) {
- const QRgb &s = src[row * w + col];
- sa = fromIntColor[qAlpha(s)];
- sr = fromIntColor[qRed(s)];
- sg = fromIntColor[qGreen(s)];
- sb = fromIntColor[qBlue(s)];
- // the matrix is applied to non-premultiplied color values
- // so we have to convert colors by dividing by alpha value
- if (sa > 0.0 && sa < 1.0) {
- sr /= sa;
- sb /= sa;
- sg /= sa;
- }
-
- // apply matrix to color values
- dr = m[ 0] * sr + m[ 1] * sg + m[ 2] * sb + m[ 3] * sa + m[ 4];
- dg = m[ 5] * sr + m[ 6] * sg + m[ 7] * sb + m[ 8] * sa + m[ 9];
- db = m[10] * sr + m[11] * sg + m[12] * sb + m[13] * sa + m[14];
- da = m[15] * sr + m[16] * sg + m[17] * sb + m[18] * sa + m[19];
-
- // the new alpha value
- da *= 255.0;
-
- // set pre-multiplied color values on destination image
- dst[row * w + col] = qRgba(static_cast<quint8>(qBound(qreal(0.0), dr * da, qreal(255.0))),
- static_cast<quint8>(qBound(qreal(0.0), dg * da, qreal(255.0))),
- static_cast<quint8>(qBound(qreal(0.0), db * da, qreal(255.0))),
- static_cast<quint8>(qBound(qreal(0.0), da, qreal(255.0))));
- }
- }
-
- return result;
-}
-
-bool ColorMatrixEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &)
-{
- if (element.tagName() != id()) {
- return false;
- }
-
- QString typeStr = element.attribute("type");
- if (typeStr.isEmpty()) {
- return false;
- }
-
- QString valueStr = element.attribute("values");
-
- setIdentity();
- m_type = Matrix;
-
- if (typeStr == "matrix") {
- // values are separated by whitespace and/or comma
- QStringList values = valueStr.trimmed().split(QRegExp("(\\s+|,)"), QString::SkipEmptyParts);
- if (values.count() == MatrixSize) {
- for (int i = 0; i < MatrixSize; ++i) {
- m_matrix[i] = values[i].toDouble();
- }
- }
- } else if (typeStr == "saturate") {
- if (!valueStr.isEmpty()) {
- setSaturate(valueStr.toDouble());
- }
- } else if (typeStr == "hueRotate") {
- if (!valueStr.isEmpty()) {
- setHueRotate(valueStr.toDouble());
- }
- } else if (typeStr == "luminanceToAlpha") {
- setLuminanceAlpha();
- } else {
- return false;
- }
-
- return true;
-}
-
-void ColorMatrixEffect::save(KoXmlWriter &writer)
-{
- writer.startElement(ColorMatrixEffectId);
-
- saveCommonAttributes(writer);
-
- switch (m_type) {
- case Matrix: {
- writer.addAttribute("type", "matrix");
- QString matrix;
- for (int r = 0; r < MatrixRows; ++r) {
- for (int c = 0; c < MatrixCols; ++c) {
- matrix += QString("%1 ").arg(m_matrix[r * MatrixCols + c]);
- }
- }
- writer.addAttribute("values", matrix);
- }
- break;
- case Saturate:
- writer.addAttribute("type", "saturate");
- writer.addAttribute("values", QString("%1").arg(m_value));
- break;
- case HueRotate:
- writer.addAttribute("type", "hueRotate");
- writer.addAttribute("values", QString("%1").arg(m_value));
- break;
- case LuminanceAlpha:
- writer.addAttribute("type", "luminanceToAlpha");
- break;
- }
-
- writer.endElement();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffect.h b/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffect.h
deleted file mode 100644
index e2e2124411..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffect.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/* 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 COLORMATRIXEFFECT_H
-#define COLORMATRIXEFFECT_H
-
-#include "KoFilterEffect.h"
-#include <QVector>
-
-#define ColorMatrixEffectId "feColorMatrix"
-
-/// A color matrix effect
-class ColorMatrixEffect : public KoFilterEffect
-{
-public:
- enum Type {
- Matrix,
- Saturate,
- HueRotate,
- LuminanceAlpha
- };
-
- ColorMatrixEffect();
-
- /// Returns the type of the color matrix
- Type type() const;
-
- /// Returns the size of the color matrix
- static int colorMatrixSize();
-
- /// Returns the row count of the color matrix
- static int colorMatrixRowCount();
-
- /// Returns the column count of the color matrix
- static int colorMatrixColumnCount();
-
- QVector<qreal> colorMatrix() const;
-
- /// Sets a color matrix and changes type to Matrix
- void setColorMatrix(const QVector<qreal> &matrix);
-
- /// Sets a saturate value and changes type to Saturate
- void setSaturate(qreal value);
-
- /// Returns the saturate value if type == Saturate
- qreal saturate() const;
-
- /// Sets a hue rotate value and changes type to HueRotate
- void setHueRotate(qreal value);
-
- /// Returns the saturate value if type == HueRotate
- qreal hueRotate() const;
-
- /// Sets luminance alpha an changes type to LuminanceAlpha
- void setLuminanceAlpha();
-
- /// reimplemented from KoFilterEffect
- QImage processImage(const QImage &image, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- bool load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context) override;
- /// reimplemented from KoFilterEffect
- void save(KoXmlWriter &writer) override;
-
-private:
- /// sets color matrix to identity matrix
- void setIdentity();
-
- Type m_type; ///< the color matrix type
- QVector<qreal> m_matrix; ///< the color matrix to apply
- qreal m_value; ///< the value (saturate or hueRotate)
-};
-
-#endif // COLORMATRIXEFFECT_H
diff --git a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectConfigWidget.cpp b/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectConfigWidget.cpp
deleted file mode 100644
index a97c78abe7..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectConfigWidget.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (c) 2009-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 "ColorMatrixEffectConfigWidget.h"
-#include "ColorMatrixEffect.h"
-#include "KoFilterEffect.h"
-#include "MatrixDataModel.h"
-
-#include <QSpinBox>
-#include <kcombobox.h>
-#include <klocalizedstring.h>
-
-#include <QGridLayout>
-#include <QLabel>
-#include <QStackedWidget>
-#include <QTableView>
-#include <QHeaderView>
-
-#include "kis_double_parse_spin_box.h"
-
-ColorMatrixEffectConfigWidget::ColorMatrixEffectConfigWidget(QWidget *parent)
- : KoFilterEffectConfigWidgetBase(parent)
- , m_effect(0)
-{
- QGridLayout *g = new QGridLayout(this);
-
- m_type = new KComboBox(this);
- m_type->addItem(i18n("Apply color matrix"));
- m_type->addItem(i18n("Saturate colors"));
- m_type->addItem(i18n("Rotate hue"));
- m_type->addItem(i18n("Luminance to alpha"));
- g->addWidget(m_type, 0, 0);
-
- m_stack = new QStackedWidget(this);
- m_stack->setContentsMargins(0, 0, 0, 0);
- g->addWidget(m_stack, 1, 0);
-
- m_matrixModel = new MatrixDataModel(this);
-
- QTableView *matrixWidget = new QTableView(m_stack);
- matrixWidget->setModel(m_matrixModel);
- matrixWidget->horizontalHeader()->hide();
- matrixWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
- matrixWidget->verticalHeader()->hide();
- matrixWidget->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
- m_stack->addWidget(matrixWidget);
-
- QWidget *saturateWidget = new QWidget(m_stack);
- QGridLayout *saturateLayout = new QGridLayout(saturateWidget);
- saturateLayout->addWidget(new QLabel(i18n("Saturate value"), saturateWidget), 0, 0);
- m_saturate = new KisDoubleParseSpinBox(saturateWidget);
- m_saturate->setRange(0.0, 1.0);
- m_saturate->setSingleStep(0.05);
- saturateLayout->addWidget(m_saturate, 0, 1);
- saturateLayout->addItem(new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 1, 0);
- saturateWidget->setLayout(saturateLayout);
- m_stack->addWidget(saturateWidget);
-
- QWidget *hueRotateWidget = new QWidget(m_stack);
- QGridLayout *hueRotateLayout = new QGridLayout(hueRotateWidget);
- hueRotateLayout->addWidget(new QLabel(i18n("Angle"), hueRotateWidget), 0, 0);
- m_hueRotate = new KisDoubleParseSpinBox(hueRotateWidget);
- m_hueRotate->setRange(0.0, 360.0);
- m_hueRotate->setSingleStep(1.0);
- hueRotateLayout->addWidget(m_hueRotate, 0, 1);
- hueRotateLayout->addItem(new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 1, 0);
- hueRotateWidget->setLayout(hueRotateLayout);
- m_stack->addWidget(hueRotateWidget);
-
- QWidget *luminanceWidget = new QWidget(m_stack);
- m_stack->addWidget(luminanceWidget);
-
- setLayout(g);
-
- connect(m_type, SIGNAL(currentIndexChanged(int)), m_stack, SLOT(setCurrentIndex(int)));
- connect(m_type, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged(int)));
- connect(m_saturate, SIGNAL(valueChanged(double)), this, SLOT(saturateChanged(double)));
- connect(m_hueRotate, SIGNAL(valueChanged(double)), this, SLOT(hueRotateChanged(double)));
- connect(m_matrixModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(matrixChanged()));
-}
-
-bool ColorMatrixEffectConfigWidget::editFilterEffect(KoFilterEffect *filterEffect)
-{
- m_effect = dynamic_cast<ColorMatrixEffect *>(filterEffect);
- if (!m_effect) {
- return false;
- }
-
- m_type->blockSignals(true);
-
- switch (m_effect->type()) {
- case ColorMatrixEffect::Matrix:
- m_type->setCurrentIndex(0);
- m_matrixModel->setMatrix(m_effect->colorMatrix(), m_effect->colorMatrixRowCount(), m_effect->colorMatrixColumnCount());
- break;
- case ColorMatrixEffect::Saturate:
- m_type->setCurrentIndex(1);
- m_saturate->blockSignals(true);
- m_saturate->setValue(m_effect->saturate());
- m_saturate->blockSignals(false);
- break;
- case ColorMatrixEffect::HueRotate:
- m_type->setCurrentIndex(2);
- m_hueRotate->blockSignals(true);
- m_hueRotate->setValue(m_effect->hueRotate());
- m_hueRotate->blockSignals(false);
- break;
- case ColorMatrixEffect::LuminanceAlpha:
- m_type->setCurrentIndex(3);
- break;
- }
-
- m_type->blockSignals(false);
- m_stack->setCurrentIndex(m_type->currentIndex());
-
- return true;
-}
-
-void ColorMatrixEffectConfigWidget::matrixChanged()
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->setColorMatrix(m_matrixModel->matrix());
- emit filterChanged();
-}
-
-void ColorMatrixEffectConfigWidget::saturateChanged(double saturate)
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->setSaturate(saturate);
- emit filterChanged();
-}
-
-void ColorMatrixEffectConfigWidget::hueRotateChanged(double angle)
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->setHueRotate(angle);
- emit filterChanged();
-}
-
-void ColorMatrixEffectConfigWidget::typeChanged(int index)
-{
- if (!m_effect) {
- return;
- }
-
- if (index == ColorMatrixEffect::Matrix) {
- m_effect->setColorMatrix(m_matrixModel->matrix());
- } else if (index == ColorMatrixEffect::Saturate) {
- m_effect->setSaturate(m_saturate->value());
- } else if (index == ColorMatrixEffect::HueRotate) {
- m_effect->setHueRotate(m_hueRotate->value());
- } else {
- m_effect->setLuminanceAlpha();
- }
- emit filterChanged();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectConfigWidget.h b/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectConfigWidget.h
deleted file mode 100644
index 2c2f0dd7cd..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectConfigWidget.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* 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 COLORMATRIXEFFECTCONFIGWIDGET_H
-#define COLORMATRIXEFFECTCONFIGWIDGET_H
-
-#include "KoFilterEffectConfigWidgetBase.h"
-
-class ColorMatrixEffect;
-class KoFilterEffect;
-class KComboBox;
-class QStackedWidget;
-class QDoubleSpinBox;
-class MatrixDataModel;
-
-class ColorMatrixEffectConfigWidget : public KoFilterEffectConfigWidgetBase
-{
- Q_OBJECT
-public:
- explicit ColorMatrixEffectConfigWidget(QWidget *parent = 0);
-
- /// reimplemented from KoFilterEffectConfigWidgetBase
- bool editFilterEffect(KoFilterEffect *filterEffect) override;
-
-private Q_SLOTS:
- void matrixChanged();
- void saturateChanged(double saturate);
- void hueRotateChanged(double angle);
- void typeChanged(int index);
-private:
- KComboBox *m_type;
- ColorMatrixEffect *m_effect;
- MatrixDataModel *m_matrixModel;
- QStackedWidget *m_stack;
- QDoubleSpinBox *m_saturate;
- QDoubleSpinBox *m_hueRotate;
-};
-
-#endif // COLORMATRIXEFFECTCONFIGWIDGET_H
diff --git a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectFactory.cpp b/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectFactory.cpp
deleted file mode 100644
index aee4873e76..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectFactory.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/* 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.
- */
-
-#include "ColorMatrixEffectFactory.h"
-#include "ColorMatrixEffect.h"
-#include "ColorMatrixEffectConfigWidget.h"
-
-#include <klocalizedstring.h>
-
-ColorMatrixEffectFactory::ColorMatrixEffectFactory()
- : KoFilterEffectFactoryBase(ColorMatrixEffectId, i18n("Color matrix"))
-{
-}
-
-KoFilterEffect *ColorMatrixEffectFactory::createFilterEffect() const
-{
- return new ColorMatrixEffect();
-}
-
-KoFilterEffectConfigWidgetBase *ColorMatrixEffectFactory::createConfigWidget() const
-{
- return new ColorMatrixEffectConfigWidget();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectFactory.h b/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectFactory.h
deleted file mode 100644
index 85288e9bfb..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectFactory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* 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 COLORMATRIXEFFECTFACTORY_H
-#define COLORMATRIXEFFECTFACTORY_H
-
-#include "KoFilterEffectFactoryBase.h"
-
-class KoFilterEffect;
-
-class ColorMatrixEffectFactory : public KoFilterEffectFactoryBase
-{
-public:
- ColorMatrixEffectFactory();
- KoFilterEffect *createFilterEffect() const override;
- KoFilterEffectConfigWidgetBase *createConfigWidget() const override;
-};
-
-#endif // COLORMATRIXEFFECTFACTORY_H
diff --git a/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffect.cpp b/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffect.cpp
deleted file mode 100644
index 10701a8a91..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffect.cpp
+++ /dev/null
@@ -1,331 +0,0 @@
-/* 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.
- */
-
-#include "ComponentTransferEffect.h"
-#include "ColorChannelConversion.h"
-#include <KoFilterEffectRenderContext.h>
-#include <KoXmlWriter.h>
-#include <KoXmlReader.h>
-#include <klocalizedstring.h>
-#include <QRect>
-#include <QImage>
-#include <math.h>
-
-ComponentTransferEffect::ComponentTransferEffect()
- : KoFilterEffect(ComponentTransferEffectId, i18n("Component transfer"))
-{
-}
-
-ComponentTransferEffect::Function ComponentTransferEffect::function(Channel channel) const
-{
- return m_data[channel].function;
-}
-
-void ComponentTransferEffect::setFunction(Channel channel, Function function)
-{
- m_data[channel].function = function;
-}
-
-QList<qreal> ComponentTransferEffect::tableValues(Channel channel) const
-{
- return m_data[channel].tableValues;
-}
-
-void ComponentTransferEffect::setTableValues(Channel channel, QList<qreal> tableValues)
-{
- m_data[channel].tableValues = tableValues;
-}
-
-void ComponentTransferEffect::setSlope(Channel channel, qreal slope)
-{
- m_data[channel].slope = slope;
-}
-
-qreal ComponentTransferEffect::slope(Channel channel) const
-{
- return m_data[channel].slope;
-}
-
-void ComponentTransferEffect::setIntercept(Channel channel, qreal intercept)
-{
- m_data[channel].intercept = intercept;
-}
-
-qreal ComponentTransferEffect::intercept(Channel channel) const
-{
- return m_data[channel].intercept;
-}
-
-void ComponentTransferEffect::setAmplitude(Channel channel, qreal amplitude)
-{
- m_data[channel].amplitude = amplitude;
-}
-
-qreal ComponentTransferEffect::amplitude(Channel channel) const
-{
- return m_data[channel].amplitude;
-}
-
-void ComponentTransferEffect::setExponent(Channel channel, qreal exponent)
-{
- m_data[channel].exponent = exponent;
-}
-
-qreal ComponentTransferEffect::exponent(Channel channel) const
-{
- return m_data[channel].exponent;
-}
-
-void ComponentTransferEffect::setOffset(Channel channel, qreal offset)
-{
- m_data[channel].offset = offset;
-}
-
-qreal ComponentTransferEffect::offset(Channel channel) const
-{
- return m_data[channel].offset;
-}
-
-QImage ComponentTransferEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const
-{
- QImage result = image;
-
- const QRgb *src = (const QRgb *)image.constBits();
- QRgb *dst = (QRgb *)result.bits();
- int w = result.width();
-
- qreal sa, sr, sg, sb;
- qreal da, dr, dg, db;
- int pixel;
-
- const QRect roi = context.filterRegion().toRect();
- const int minRow = roi.top();
- const int maxRow = roi.bottom();
- const int minCol = roi.left();
- const int maxCol = roi.right();
-
- for (int row = minRow; row <= maxRow; ++row) {
- for (int col = minCol; col <= maxCol; ++col) {
- pixel = row * w + col;
- const QRgb &s = src[pixel];
-
- sa = fromIntColor[qAlpha(s)];
- sr = fromIntColor[qRed(s)];
- sg = fromIntColor[qGreen(s)];
- sb = fromIntColor[qBlue(s)];
- // the matrix is applied to non-premultiplied color values
- // so we have to convert colors by dividing by alpha value
- if (sa > 0.0 && sa < 1.0) {
- sr /= sa;
- sb /= sa;
- sg /= sa;
- }
-
- dr = transferChannel(ChannelR, sr);
- dg = transferChannel(ChannelG, sg);
- db = transferChannel(ChannelB, sb);
- da = transferChannel(ChannelA, sa);
-
- da *= 255.0;
-
- // set pre-multiplied color values on destination image
- dst[pixel] = qRgba(static_cast<quint8>(qBound(qreal(0.0), dr * da, qreal(255.0))),
- static_cast<quint8>(qBound(qreal(0.0), dg * da, qreal(255.0))),
- static_cast<quint8>(qBound(qreal(0.0), db * da, qreal(255.0))),
- static_cast<quint8>(qBound(qreal(0.0), da, qreal(255.0))));
- }
- }
-
- return result;
-}
-
-qreal ComponentTransferEffect::transferChannel(Channel channel, qreal value) const
-{
- const Data &d = m_data[channel];
-
- switch (d.function) {
- case Identity:
- return value;
- case Table: {
- qreal valueCount = d.tableValues.count() - 1;
- if (valueCount < 0.0) {
- return value;
- }
- qreal k1 = static_cast<int>(value * valueCount);
- qreal k2 = qMin(k1 + 1, valueCount);
- qreal vk1 = d.tableValues[k1];
- qreal vk2 = d.tableValues[k2];
- return vk1 + (value - static_cast<qreal>(k1) / valueCount) * valueCount * (vk2 - vk1);
- }
- case Discrete: {
- qreal valueCount = d.tableValues.count() - 1;
- if (valueCount < 0.0) {
- return value;
- }
- return d.tableValues[static_cast<int>(value * valueCount)];
- }
- case Linear:
- return d.slope * value + d.intercept;
- case Gamma:
- return d.amplitude * pow(value, d.exponent) + d.offset;
- }
-
- return value;
-}
-
-bool ComponentTransferEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &)
-{
- if (element.tagName() != id()) {
- return false;
- }
-
- // reset data
- m_data[ChannelR] = Data();
- m_data[ChannelG] = Data();
- m_data[ChannelB] = Data();
- m_data[ChannelA] = Data();
-
- for (KoXmlNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
- KoXmlElement node = n.toElement();
- if (node.tagName() == "feFuncR") {
- loadChannel(ChannelR, node);
- } else if (node.tagName() == "feFuncG") {
- loadChannel(ChannelG, node);
- } else if (node.tagName() == "feFuncB") {
- loadChannel(ChannelB, node);
- } else if (node.tagName() == "feFuncA") {
- loadChannel(ChannelA, node);
- }
- }
-
- return true;
-}
-
-void ComponentTransferEffect::loadChannel(Channel channel, const KoXmlElement &element)
-{
- QString typeStr = element.attribute("type");
- if (typeStr.isEmpty()) {
- return;
- }
-
- Data &d = m_data[channel];
-
- if (typeStr == "table" || typeStr == "discrete") {
- d.function = typeStr == "table" ? Table : Discrete;
- QString valueStr = element.attribute("tableValues");
- QStringList values = valueStr.split(QRegExp("(\\s+|,)"), QString::SkipEmptyParts);
- Q_FOREACH (const QString &v, values) {
- d.tableValues.append(v.toDouble());
- }
- } else if (typeStr == "linear") {
- d.function = Linear;
- if (element.hasAttribute("slope")) {
- d.slope = element.attribute("slope").toDouble();
- }
- if (element.hasAttribute("intercept")) {
- d.intercept = element.attribute("intercept").toDouble();
- }
- } else if (typeStr == "gamma") {
- d.function = Gamma;
- if (element.hasAttribute("amplitude")) {
- d.amplitude = element.attribute("amplitude").toDouble();
- }
- if (element.hasAttribute("exponent")) {
- d.exponent = element.attribute("exponent").toDouble();
- }
- if (element.hasAttribute("offset")) {
- d.offset = element.attribute("offset").toDouble();
- }
- }
-}
-
-void ComponentTransferEffect::save(KoXmlWriter &writer)
-{
- writer.startElement(ComponentTransferEffectId);
-
- saveCommonAttributes(writer);
-
- saveChannel(ChannelR, writer);
- saveChannel(ChannelG, writer);
- saveChannel(ChannelB, writer);
- saveChannel(ChannelA, writer);
-
- writer.endElement();
-}
-
-void ComponentTransferEffect::saveChannel(Channel channel, KoXmlWriter &writer)
-{
- Function function = m_data[channel].function;
- // we can omit writing the transfer function when
- if (function == Identity) {
- return;
- }
-
- switch (channel) {
- case ChannelR:
- writer.startElement("feFuncR");
- break;
- case ChannelG:
- writer.startElement("feFuncG");
- break;
- case ChannelB:
- writer.startElement("feFuncB");
- break;
- case ChannelA:
- writer.startElement("feFuncA");
- break;
- }
-
- Data defaultData;
- const Data &currentData = m_data[channel];
-
- if (function == Linear) {
- writer.addAttribute("type", "linear");
- // only write non default data
- if (defaultData.slope != currentData.slope) {
- writer.addAttribute("slope", QString("%1").arg(currentData.slope));
- }
- if (defaultData.intercept != currentData.intercept) {
- writer.addAttribute("intercept", QString("%1").arg(currentData.intercept));
- }
- } else if (function == Gamma) {
- writer.addAttribute("type", "gamma");
- // only write non default data
- if (defaultData.amplitude != currentData.amplitude) {
- writer.addAttribute("amplitude", QString("%1").arg(currentData.amplitude));
- }
- if (defaultData.exponent != currentData.exponent) {
- writer.addAttribute("exponent", QString("%1").arg(currentData.exponent));
- }
- if (defaultData.offset != currentData.offset) {
- writer.addAttribute("offset", QString("%1").arg(currentData.offset));
- }
- } else {
- writer.addAttribute("type", function == Table ? "table" : "discrete");
- if (currentData.tableValues.count()) {
- QString tableStr;
- Q_FOREACH (qreal v, currentData.tableValues) {
- tableStr += QString("%1 ").arg(v);
- }
- writer.addAttribute("tableValues", tableStr.trimmed());
- }
- }
-
- writer.endElement();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffect.h b/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffect.h
deleted file mode 100644
index d5706cd692..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffect.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/* 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 COMPONENTTRANSFEREFFECT_H
-#define COMPONENTTRANSFEREFFECT_H
-
-#include "KoFilterEffect.h"
-
-#define ComponentTransferEffectId "feComponentTransfer"
-
-/// A component transfer effect
-class ComponentTransferEffect : public KoFilterEffect
-{
-public:
- /// the different transfer functions
- enum Function {
- Identity,
- Table,
- Discrete,
- Linear,
- Gamma
- };
-
- /// the different color channels
- enum Channel {
- ChannelR,
- ChannelG,
- ChannelB,
- ChannelA
- };
-
- ComponentTransferEffect();
-
- /// Returns the component transfer function of the specified channel
- Function function(Channel channel) const;
-
- /// Sets the component transfer function to use for the specified channel
- void setFunction(Channel channel, Function function);
-
- /// Returns the lookup table for the specified channel
- QList<qreal> tableValues(Channel channel) const;
-
- /// Sets the lookup table for the specified channel
- void setTableValues(Channel channel, QList<qreal> tableValues);
-
- /// Sets the slope for the specified channel
- void setSlope(Channel channel, qreal slope);
-
- /// Returns the slope for the specified channel
- qreal slope(Channel channel) const;
-
- /// Sets the intercept for the specified channel
- void setIntercept(Channel channel, qreal intercept);
-
- /// Returns the intercept for the specified channel
- qreal intercept(Channel channel) const;
-
- /// Sets the amplitude for the specified channel
- void setAmplitude(Channel channel, qreal amplitude);
-
- /// Returns the amplitude for the specified channel
- qreal amplitude(Channel channel) const;
-
- /// Sets the exponent for the specified channel
- void setExponent(Channel channel, qreal exponent);
-
- /// Returns the exponent for the specified channel
- qreal exponent(Channel channel) const;
-
- /// Sets the offset for the specified channel
- void setOffset(Channel channel, qreal offset);
-
- /// Returns the offset for the specified channel
- qreal offset(Channel channel) const;
-
- /// reimplemented from KoFilterEffect
- QImage processImage(const QImage &image, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- bool load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context) override;
- /// reimplemented from KoFilterEffect
- void save(KoXmlWriter &writer) override;
-
-private:
- /// loads channel transfer function from given xml element
- void loadChannel(Channel channel, const KoXmlElement &element);
-
- /// saves channel transfer function to given xml writer
- void saveChannel(Channel channel, KoXmlWriter &writer);
-
- /// transfers color channel
- qreal transferChannel(Channel channel, qreal value) const;
-
- struct Data {
- Data()
- : function(Identity), slope(1.0), intercept(0.0)
- , amplitude(1.0), exponent(1.0), offset(0.0)
- {
- }
-
- Function function; ///< the component transfer function
- QList<qreal> tableValues; ///< lookup table for table or discrete function
- qreal slope; ///< slope for linear function
- qreal intercept; ///< intercept for linear function
- qreal amplitude; ///< amplitude for gamma function
- qreal exponent; ///< exponent for gamma function
- qreal offset; ///< offset for gamma function
- };
-
- Data m_data[4];
-};
-
-#endif // COMPONENTTRANSFEREFFECT_H
diff --git a/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffectConfigWidget.cpp b/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffectConfigWidget.cpp
deleted file mode 100644
index a47c1e1eb4..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffectConfigWidget.cpp
+++ /dev/null
@@ -1,305 +0,0 @@
-/* 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.
- */
-
-#include "ComponentTransferEffectConfigWidget.h"
-#include "KoFilterEffect.h"
-
-#include <QSpinBox>
-#include <kcombobox.h>
-#include <klineedit.h>
-#include <klocalizedstring.h>
-
-#include <QGridLayout>
-#include <QLabel>
-#include <QStackedWidget>
-#include <QRadioButton>
-#include <QButtonGroup>
-
-#include "kis_double_parse_spin_box.h"
-
-const qreal ValueStep = 0.1;
-
-ComponentTransferEffectConfigWidget::ComponentTransferEffectConfigWidget(QWidget *parent)
- : KoFilterEffectConfigWidgetBase(parent)
- , m_effect(0)
- , m_currentChannel(ComponentTransferEffect::ChannelR)
-{
- QGridLayout *g = new QGridLayout(this);
-
- QButtonGroup *group = new QButtonGroup(this);
-
- QRadioButton *butR = new QRadioButton("R", this);
- QRadioButton *butG = new QRadioButton("G", this);
- QRadioButton *butB = new QRadioButton("B", this);
- QRadioButton *butA = new QRadioButton("A", this);
- g->addWidget(butR, 0, 0);
- g->addWidget(butG, 0, 1);
- g->addWidget(butB, 0, 2);
- g->addWidget(butA, 0, 3);
- group->addButton(butR, ComponentTransferEffect::ChannelR);
- group->addButton(butG, ComponentTransferEffect::ChannelG);
- group->addButton(butB, ComponentTransferEffect::ChannelB);
- group->addButton(butA, ComponentTransferEffect::ChannelA);
- butR->setChecked(true);
-
- g->addWidget(new QLabel(i18n("Function"), this), 1, 0, 1, 2);
- m_function = new KComboBox(this);
- m_function->addItem(i18n("Identity"));
- m_function->addItem(i18n("Table"));
- m_function->addItem(i18n("Discrete"));
- m_function->addItem(i18n("Linear"));
- m_function->addItem(i18n("Gamma"));
- g->addWidget(m_function, 1, 2, 1, 2);
-
- m_stack = new QStackedWidget(this);
- m_stack->setContentsMargins(0, 0, 0, 0);
- g->addWidget(m_stack, 2, 0, 1, 4);
-
- // Identity widget
- m_stack->addWidget(new QWidget(this));
-
- // Table widget
- QWidget *tableWidget = new QWidget(m_stack);
- QGridLayout *tableLayout = new QGridLayout(tableWidget);
- tableLayout->addWidget(new QLabel(i18n("Values"), tableWidget), 0, 0);
- m_tableValues = new KLineEdit(tableWidget);
- tableLayout->addWidget(m_tableValues, 0, 1);
- tableLayout->setContentsMargins(0, 0, 0, 0);
- tableLayout->addItem(new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 1, 0);
- m_stack->addWidget(tableWidget);
-
- // Discrete widget
- QWidget *discreteWidget = new QWidget(m_stack);
- QGridLayout *discreteLayout = new QGridLayout(discreteWidget);
- discreteLayout->addWidget(new QLabel(i18n("Values"), discreteWidget), 0, 0);
- m_discreteValues = new KLineEdit(discreteWidget);
- discreteLayout->addWidget(m_discreteValues, 0, 1);
- discreteLayout->setContentsMargins(0, 0, 0, 0);
- discreteLayout->addItem(new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 1, 0);
- m_stack->addWidget(discreteWidget);
-
- // Linear widget
- QWidget *linearWidget = new QWidget(m_stack);
- QGridLayout *linearLayout = new QGridLayout(linearWidget);
- linearLayout->addWidget(new QLabel(i18n("Slope"), linearWidget), 0, 0);
- m_slope = new KisDoubleParseSpinBox(linearWidget);
- m_slope->setRange(m_slope->minimum(), m_slope->maximum());
- m_slope->setSingleStep(ValueStep);
- linearLayout->addWidget(m_slope, 0, 1);
- linearLayout->addWidget(new QLabel(i18n("Intercept")), 1, 0);
- m_intercept = new KisDoubleParseSpinBox(linearWidget);
- m_intercept->setRange(m_intercept->minimum(), m_intercept->maximum());
- m_intercept->setSingleStep(ValueStep);
- linearLayout->addWidget(m_intercept, 1, 1);
- linearLayout->addItem(new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 2, 0);
- linearLayout->setContentsMargins(0, 0, 0, 0);
- linearWidget->setLayout(linearLayout);
- m_stack->addWidget(linearWidget);
-
- QWidget *gammaWidget = new QWidget(m_stack);
- QGridLayout *gammaLayout = new QGridLayout(gammaWidget);
- gammaLayout->addWidget(new QLabel(i18n("Amplitude"), gammaWidget), 0, 0);
- m_amplitude = new KisDoubleParseSpinBox(gammaWidget);
- m_amplitude->setRange(m_amplitude->minimum(), m_amplitude->maximum());
- m_amplitude->setSingleStep(ValueStep);
- gammaLayout->addWidget(m_amplitude, 0, 1);
- gammaLayout->addWidget(new QLabel(i18n("Exponent"), gammaWidget), 1, 0);
- m_exponent = new KisDoubleParseSpinBox(gammaWidget);
- m_exponent->setRange(m_exponent->minimum(), m_exponent->maximum());
- m_exponent->setSingleStep(ValueStep);
- gammaLayout->addWidget(m_exponent, 1, 1);
- gammaLayout->addWidget(new QLabel(i18n("Offset"), gammaWidget), 2, 0);
- m_offset = new KisDoubleParseSpinBox(gammaWidget);
- m_offset->setRange(m_offset->minimum(), m_offset->maximum());
- m_offset->setSingleStep(ValueStep);
- gammaLayout->addWidget(m_offset, 2, 1);
- gammaLayout->addItem(new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 3, 0);
- gammaLayout->setContentsMargins(0, 0, 0, 0);
- gammaWidget->setLayout(gammaLayout);
- m_stack->addWidget(gammaWidget);
-
- setLayout(g);
-
- connect(m_function, SIGNAL(currentIndexChanged(int)), m_stack, SLOT(setCurrentIndex(int)));
- connect(m_function, SIGNAL(currentIndexChanged(int)), this, SLOT(functionChanged(int)));
- connect(m_tableValues, SIGNAL(editingFinished()), this, SLOT(tableValuesChanged()));
- connect(m_discreteValues, SIGNAL(editingFinished()), this, SLOT(discreteValuesChanged()));
- connect(m_slope, SIGNAL(valueChanged(double)), this, SLOT(slopeChanged(double)));
- connect(m_intercept, SIGNAL(valueChanged(double)), this, SLOT(interceptChanged(double)));
- connect(m_amplitude, SIGNAL(valueChanged(double)), this, SLOT(amplitudeChanged(double)));
- connect(m_exponent, SIGNAL(valueChanged(double)), this, SLOT(exponentChanged(double)));
- connect(m_offset, SIGNAL(valueChanged(double)), this, SLOT(offsetChanged(double)));
- connect(group, SIGNAL(buttonClicked(int)), this, SLOT(channelSelected(int)));
-}
-
-void ComponentTransferEffectConfigWidget::updateControls()
-{
- m_function->blockSignals(true);
-
- QString values;
-
- switch (m_effect->function(m_currentChannel)) {
- case ComponentTransferEffect::Identity:
- m_function->setCurrentIndex(0);
- break;
- case ComponentTransferEffect::Table:
- m_function->setCurrentIndex(1);
- m_tableValues->blockSignals(true);
- Q_FOREACH (qreal v, m_effect->tableValues(m_currentChannel)) {
- values += QString("%1;").arg(v);
- }
- m_tableValues->setText(values);
- m_tableValues->blockSignals(false);
- break;
- case ComponentTransferEffect::Discrete:
- m_function->setCurrentIndex(2);
- m_discreteValues->blockSignals(true);
- Q_FOREACH (qreal v, m_effect->tableValues(m_currentChannel)) {
- values += QString("%1;").arg(v);
- }
- m_discreteValues->setText(values);
- m_discreteValues->blockSignals(false);
- break;
- case ComponentTransferEffect::Linear:
- m_function->setCurrentIndex(3);
- m_slope->blockSignals(true);
- m_slope->setValue(m_effect->slope(m_currentChannel));
- m_slope->blockSignals(false);
- m_intercept->blockSignals(true);
- m_intercept->setValue(m_effect->intercept(m_currentChannel));
- m_intercept->blockSignals(false);
- break;
- case ComponentTransferEffect::Gamma:
- m_function->setCurrentIndex(4);
- m_amplitude->blockSignals(true);
- m_amplitude->setValue(m_effect->amplitude(m_currentChannel));
- m_amplitude->blockSignals(false);
- m_exponent->blockSignals(true);
- m_exponent->setValue(m_effect->exponent(m_currentChannel));
- m_exponent->blockSignals(false);
- m_offset->blockSignals(true);
- m_offset->setValue(m_effect->offset(m_currentChannel));
- m_offset->blockSignals(false);
- break;
- }
-
- m_function->blockSignals(false);
- m_stack->setCurrentIndex(m_function->currentIndex());
-}
-
-bool ComponentTransferEffectConfigWidget::editFilterEffect(KoFilterEffect *filterEffect)
-{
- m_effect = dynamic_cast<ComponentTransferEffect *>(filterEffect);
- if (!m_effect) {
- return false;
- }
-
- updateControls();
-
- return true;
-}
-
-void ComponentTransferEffectConfigWidget::slopeChanged(double slope)
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->setSlope(m_currentChannel, slope);
- emit filterChanged();
-}
-
-void ComponentTransferEffectConfigWidget::interceptChanged(double intercept)
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->setIntercept(m_currentChannel, intercept);
- emit filterChanged();
-}
-
-void ComponentTransferEffectConfigWidget::amplitudeChanged(double amplitude)
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->setAmplitude(m_currentChannel, amplitude);
- emit filterChanged();
-}
-
-void ComponentTransferEffectConfigWidget::exponentChanged(double exponent)
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->setExponent(m_currentChannel, exponent);
- emit filterChanged();
-}
-
-void ComponentTransferEffectConfigWidget::offsetChanged(double offset)
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->setOffset(m_currentChannel, offset);
- emit filterChanged();
-}
-
-void ComponentTransferEffectConfigWidget::tableValuesChanged()
-{
- QStringList values = m_tableValues->text().split(';', QString::SkipEmptyParts);
- QList<qreal> tableValues;
- Q_FOREACH (const QString &v, values) {
- tableValues.append(v.toDouble());
- }
- m_effect->setTableValues(m_currentChannel, tableValues);
- emit filterChanged();
-}
-
-void ComponentTransferEffectConfigWidget::discreteValuesChanged()
-{
- QStringList values = m_discreteValues->text().split(';', QString::SkipEmptyParts);
- QList<qreal> tableValues;
- Q_FOREACH (const QString &v, values) {
- tableValues.append(v.toDouble());
- }
- m_effect->setTableValues(m_currentChannel, tableValues);
- emit filterChanged();
-}
-
-void ComponentTransferEffectConfigWidget::functionChanged(int index)
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->setFunction(m_currentChannel, static_cast<ComponentTransferEffect::Function>(index));
-
- emit filterChanged();
-}
-
-void ComponentTransferEffectConfigWidget::channelSelected(int channel)
-{
- m_currentChannel = static_cast<ComponentTransferEffect::Channel>(channel);
- updateControls();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffectConfigWidget.h b/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffectConfigWidget.h
deleted file mode 100644
index 6d438e6d18..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffectConfigWidget.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/* 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 COMPONENTTRANSFEREFFECTCONFIGWIDGET_H
-#define COMPONENTTRANSFEREFFECTCONFIGWIDGET_H
-
-#include "KoFilterEffectConfigWidgetBase.h"
-#include "ComponentTransferEffect.h"
-
-class KoFilterEffect;
-class QDoubleSpinBox;
-class KComboBox;
-class KLineEdit;
-class QStackedWidget;
-
-class ComponentTransferEffectConfigWidget : public KoFilterEffectConfigWidgetBase
-{
- Q_OBJECT
-public:
- explicit ComponentTransferEffectConfigWidget(QWidget *parent = 0);
-
- /// reimplemented from KoFilterEffectConfigWidgetBase
- bool editFilterEffect(KoFilterEffect *filterEffect) override;
-
-private Q_SLOTS:
- void slopeChanged(double slope);
- void interceptChanged(double intercept);
- void amplitudeChanged(double amplitude);
- void exponentChanged(double exponent);
- void offsetChanged(double offset);
- void functionChanged(int index);
- void channelSelected(int channel);
- void tableValuesChanged();
- void discreteValuesChanged();
-private:
- void updateControls();
-
- ComponentTransferEffect *m_effect;
- KComboBox *m_function;
- QStackedWidget *m_stack;
- KLineEdit *m_tableValues;
- KLineEdit *m_discreteValues;
- QDoubleSpinBox *m_slope;
- QDoubleSpinBox *m_intercept;
- QDoubleSpinBox *m_amplitude;
- QDoubleSpinBox *m_exponent;
- QDoubleSpinBox *m_offset;
- ComponentTransferEffect::Channel m_currentChannel;
-};
-
-#endif // COMPONENTTRANSFEREFFECTCONFIGWIDGET_H
diff --git a/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffectFactory.cpp b/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffectFactory.cpp
deleted file mode 100644
index bf4d8f64ba..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffectFactory.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/* 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.
- */
-
-#include "ComponentTransferEffectFactory.h"
-#include "ComponentTransferEffect.h"
-#include "ComponentTransferEffectConfigWidget.h"
-
-#include <klocalizedstring.h>
-
-ComponentTransferEffectFactory::ComponentTransferEffectFactory()
- : KoFilterEffectFactoryBase(ComponentTransferEffectId, i18n("Component transfer"))
-{
-}
-
-KoFilterEffect *ComponentTransferEffectFactory::createFilterEffect() const
-{
- return new ComponentTransferEffect();
-}
-
-KoFilterEffectConfigWidgetBase *ComponentTransferEffectFactory::createConfigWidget() const
-{
- return new ComponentTransferEffectConfigWidget();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffectFactory.h b/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffectFactory.h
deleted file mode 100644
index 51f10d4d1d..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ComponentTransferEffectFactory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* 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 COMPONENTTRANSFEREFFECTFACTORY_H
-#define COMPONENTTRANSFEREFFECTFACTORY_H
-
-#include "KoFilterEffectFactoryBase.h"
-
-class KoFilterEffect;
-
-class ComponentTransferEffectFactory : public KoFilterEffectFactoryBase
-{
-public:
- ComponentTransferEffectFactory();
- KoFilterEffect *createFilterEffect() const override;
- KoFilterEffectConfigWidgetBase *createConfigWidget() const override;
-};
-
-#endif // COMPONENTTRANSFEREFFECTFACTORY_H
diff --git a/plugins/tools/karbonplugins/filtereffects/CompositeEffect.cpp b/plugins/tools/karbonplugins/filtereffects/CompositeEffect.cpp
deleted file mode 100644
index a16bd57548..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/CompositeEffect.cpp
+++ /dev/null
@@ -1,228 +0,0 @@
-/* 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.
- */
-
-#include "CompositeEffect.h"
-#include "ColorChannelConversion.h"
-#include <KoFilterEffectRenderContext.h>
-#include <KoViewConverter.h>
-#include <KoXmlWriter.h>
-#include <KoXmlReader.h>
-#include <klocalizedstring.h>
-#include <QDebug>
-#include <QRect>
-#include <QPainter>
-
-CompositeEffect::CompositeEffect()
- : KoFilterEffect(CompositeEffectId, i18n("Composite"))
- , m_operation(CompositeOver)
-{
- setRequiredInputCount(2);
- setMaximalInputCount(2);
- memset(m_k, 0, 4 * sizeof(qreal));
-}
-
-CompositeEffect::Operation CompositeEffect::operation() const
-{
- return m_operation;
-}
-
-void CompositeEffect::setOperation(Operation op)
-{
- m_operation = op;
-}
-
-const qreal *CompositeEffect::arithmeticValues() const
-{
- return m_k;
-}
-
-void CompositeEffect::setArithmeticValues(qreal *values)
-{
- memcpy(m_k, values, 4 * sizeof(qreal));
-}
-
-QImage CompositeEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &) const
-{
- return image;
-}
-
-QImage CompositeEffect::processImages(const QList<QImage> &images, const KoFilterEffectRenderContext &context) const
-{
- int imageCount = images.count();
- if (!imageCount) {
- return QImage();
- }
-
- QImage result = images[0];
- if (images.count() != 2) {
- return result;
- }
-
- if (m_operation == Arithmetic) {
- const QRgb *src = (QRgb *)images[1].constBits();
- QRgb *dst = (QRgb *)result.bits();
- int w = result.width();
-
- qreal sa, sr, sg, sb;
- qreal da, dr, dg, db;
- int pixel = 0;
-
- // TODO: do we have to calculate with non-premuliplied colors here ???
-
- QRect roi = context.filterRegion().toRect();
- for (int row = roi.top(); row < roi.bottom(); ++row) {
- for (int col = roi.left(); col < roi.right(); ++col) {
- pixel = row * w + col;
- const QRgb &s = src[pixel];
- QRgb &d = dst[pixel];
-
- sa = fromIntColor[qAlpha(s)];
- sr = fromIntColor[qRed(s)];
- sg = fromIntColor[qGreen(s)];
- sb = fromIntColor[qBlue(s)];
-
- da = fromIntColor[qAlpha(d)];
- dr = fromIntColor[qRed(d)];
- dg = fromIntColor[qGreen(d)];
- db = fromIntColor[qBlue(d)];
-
- da = m_k[0] * sa * da + m_k[1] * da + m_k[2] * sa + m_k[3];
- dr = m_k[0] * sr * dr + m_k[1] * dr + m_k[2] * sr + m_k[3];
- dg = m_k[0] * sg * dg + m_k[1] * dg + m_k[2] * sg + m_k[3];
- db = m_k[0] * sb * db + m_k[1] * db + m_k[2] * sb + m_k[3];
-
- da *= 255.0;
-
- // set pre-multiplied color values on destination image
- d = qRgba(static_cast<quint8>(qBound(qreal(0.0), dr * da, qreal(255.0))),
- static_cast<quint8>(qBound(qreal(0.0), dg * da, qreal(255.0))),
- static_cast<quint8>(qBound(qreal(0.0), db * da, qreal(255.0))),
- static_cast<quint8>(qBound(qreal(0.0), da, qreal(255.0))));
- }
- }
- } else {
- QPainter painter(&result);
-
- switch (m_operation) {
- case CompositeOver:
- painter.setCompositionMode(QPainter::CompositionMode_DestinationOver);
- break;
- case CompositeIn:
- painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
- break;
- case CompositeOut:
- painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
- break;
- case CompositeAtop:
- painter.setCompositionMode(QPainter::CompositionMode_DestinationAtop);
- break;
- case CompositeXor:
- painter.setCompositionMode(QPainter::CompositionMode_Xor);
- break;
- default:
- // no composition mode
- break;
- }
- painter.drawImage(context.filterRegion(), images[1], context.filterRegion());
- }
-
- return result;
-}
-
-bool CompositeEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &)
-{
- if (element.tagName() != id()) {
- return false;
- }
-
- QString opStr = element.attribute("operator");
- if (opStr == "over") {
- m_operation = CompositeOver;
- } else if (opStr == "in") {
- m_operation = CompositeIn;
- } else if (opStr == "out") {
- m_operation = CompositeOut;
- } else if (opStr == "atop") {
- m_operation = CompositeAtop;
- } else if (opStr == "xor") {
- m_operation = CompositeXor;
- } else if (opStr == "arithmetic") {
- m_operation = Arithmetic;
- if (element.hasAttribute("k1")) {
- m_k[0] = element.attribute("k1").toDouble();
- }
- if (element.hasAttribute("k2")) {
- m_k[1] = element.attribute("k2").toDouble();
- }
- if (element.hasAttribute("k3")) {
- m_k[2] = element.attribute("k3").toDouble();
- }
- if (element.hasAttribute("k4")) {
- m_k[3] = element.attribute("k4").toDouble();
- }
- } else {
- return false;
- }
-
- if (element.hasAttribute("in2")) {
- if (inputs().count() == 2) {
- setInput(1, element.attribute("in2"));
- } else {
- addInput(element.attribute("in2"));
- }
- }
-
- return true;
-}
-
-void CompositeEffect::save(KoXmlWriter &writer)
-{
- writer.startElement(CompositeEffectId);
-
- saveCommonAttributes(writer);
-
- switch (m_operation) {
- case CompositeOver:
- writer.addAttribute("operator", "over");
- break;
- case CompositeIn:
- writer.addAttribute("operator", "in");
- break;
- case CompositeOut:
- writer.addAttribute("operator", "out");
- break;
- case CompositeAtop:
- writer.addAttribute("operator", "atop");
- break;
- case CompositeXor:
- writer.addAttribute("operator", "xor");
- break;
- case Arithmetic:
- writer.addAttribute("operator", "arithmetic");
- writer.addAttribute("k1", QString("%1").arg(m_k[0]));
- writer.addAttribute("k2", QString("%1").arg(m_k[1]));
- writer.addAttribute("k3", QString("%1").arg(m_k[2]));
- writer.addAttribute("k4", QString("%1").arg(m_k[3]));
- break;
- }
-
- writer.addAttribute("in2", inputs().at(1));
-
- writer.endElement();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/CompositeEffect.h b/plugins/tools/karbonplugins/filtereffects/CompositeEffect.h
deleted file mode 100644
index 143506e943..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/CompositeEffect.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* 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 COMPOSITEEFFECT_H
-#define COMPOSITEEFFECT_H
-
-#include "KoFilterEffect.h"
-
-#define CompositeEffectId "feComposite"
-
-/// A flood fill effect
-class CompositeEffect : public KoFilterEffect
-{
-public:
- enum Operation {
- CompositeOver,
- CompositeIn,
- CompositeOut,
- CompositeAtop,
- CompositeXor,
- Arithmetic
- };
-
- CompositeEffect();
-
- /// Returns the composite operation
- Operation operation() const;
-
- /// Sets the composite operation
- void setOperation(Operation op);
-
- /// Returns the arithmetic values
- const qreal *arithmeticValues() const;
-
- /// Sets the arithmetic values
- void setArithmeticValues(qreal *values);
-
- /// reimplemented from KoFilterEffect
- QImage processImage(const QImage &image, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- QImage processImages(const QList<QImage> &images, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- bool load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context) override;
- /// reimplemented from KoFilterEffect
- void save(KoXmlWriter &writer) override;
-
-private:
- Operation m_operation;
- qreal m_k[4];
-};
-
-#endif // COMPOSITEEFFECT_H
diff --git a/plugins/tools/karbonplugins/filtereffects/CompositeEffectConfigWidget.cpp b/plugins/tools/karbonplugins/filtereffects/CompositeEffectConfigWidget.cpp
deleted file mode 100644
index 53ea1d4a0e..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/CompositeEffectConfigWidget.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/* 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.
- */
-
-#include "CompositeEffectConfigWidget.h"
-#include "CompositeEffect.h"
-#include "KoFilterEffect.h"
-
-#include <QSpinBox>
-#include <kcombobox.h>
-#include <klocalizedstring.h>
-
-#include <QGridLayout>
-#include <QLabel>
-
-#include "kis_double_parse_spin_box.h"
-
-CompositeEffectConfigWidget::CompositeEffectConfigWidget(QWidget *parent)
- : KoFilterEffectConfigWidgetBase(parent)
- , m_effect(0)
-{
- QGridLayout *g = new QGridLayout(this);
-
- g->addWidget(new QLabel(i18n("Operation"), this), 0, 0);
-
- m_operation = new KComboBox(this);
- m_operation->addItem(i18nc("blending mode", "Over"));
- m_operation->addItem(i18nc("blending mode", "In"));
- m_operation->addItem(i18nc("blending mode", "Out"));
- m_operation->addItem(i18nc("blending mode", "Atop"));
- m_operation->addItem(i18nc("blending mode", "Xor"));
- m_operation->addItem(i18nc("blending mode", "Arithmetic"));
- g->addWidget(m_operation, 0, 1);
-
- m_arithmeticWidget = new QWidget(this);
- QGridLayout *arithmeticLayout = new QGridLayout(m_arithmeticWidget);
- for (int i = 0; i < 4; ++i) {
- m_k[i] = new KisDoubleParseSpinBox(m_arithmeticWidget);
- arithmeticLayout->addWidget(new QLabel(QString("k%1").arg(i + 1)), i / 2, (2 * i) % 4);
- arithmeticLayout->addWidget(m_k[i], i / 2, (2 * i + 1) % 4);
- connect(m_k[i], SIGNAL(valueChanged(double)), this, SLOT(valueChanged()));
- }
- m_arithmeticWidget->setContentsMargins(0, 0, 0, 0);
- g->addWidget(m_arithmeticWidget, 1, 0, 1, 2);
- g->addItem(new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 2, 0);
-
- connect(m_operation, SIGNAL(currentIndexChanged(int)), this, SLOT(operationChanged(int)));
-}
-
-bool CompositeEffectConfigWidget::editFilterEffect(KoFilterEffect *filterEffect)
-{
- m_effect = dynamic_cast<CompositeEffect *>(filterEffect);
- if (!m_effect) {
- return false;
- }
-
- m_operation->blockSignals(true);
- m_operation->setCurrentIndex(m_effect->operation());
- m_operation->blockSignals(false);
-
- const qreal *k = m_effect->arithmeticValues();
- for (int i = 0; i < 4; ++i) {
- m_k[i]->blockSignals(true);
- m_k[i]->setValue(k[i]);
- m_k[i]->blockSignals(false);
- }
- m_arithmeticWidget->setVisible(m_effect->operation() == CompositeEffect::Arithmetic);
-
- return true;
-}
-
-void CompositeEffectConfigWidget::operationChanged(int index)
-{
- m_arithmeticWidget->setVisible(index == 6);
- if (m_effect) {
- m_effect->setOperation(static_cast<CompositeEffect::Operation>(index));
- emit filterChanged();
- }
-}
-
-void CompositeEffectConfigWidget::valueChanged()
-{
- if (!m_effect) {
- return;
- }
-
- qreal k[4] = {0};
- for (int i = 0; i < 4; ++i) {
- k[i] = m_k[i]->value();
- }
-
- m_effect->setArithmeticValues(k);
- emit filterChanged();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/CompositeEffectConfigWidget.h b/plugins/tools/karbonplugins/filtereffects/CompositeEffectConfigWidget.h
deleted file mode 100644
index 475d7970ff..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/CompositeEffectConfigWidget.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* 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 COMPOSITEEFFECTCONFIGWIDGET_H
-#define COMPOSITEEFFECTCONFIGWIDGET_H
-
-#include "KoFilterEffectConfigWidgetBase.h"
-
-class KoFilterEffect;
-class CompositeEffect;
-class QDoubleSpinBox;
-class KComboBox;
-
-class CompositeEffectConfigWidget : public KoFilterEffectConfigWidgetBase
-{
- Q_OBJECT
-public:
- explicit CompositeEffectConfigWidget(QWidget *parent = 0);
-
- /// reimplemented from KoFilterEffectConfigWidgetBase
- bool editFilterEffect(KoFilterEffect *filterEffect) override;
-
-private Q_SLOTS:
- void valueChanged();
- void operationChanged(int index);
-
-private:
- CompositeEffect *m_effect;
- KComboBox *m_operation;
- QDoubleSpinBox *m_k[4];
- QWidget *m_arithmeticWidget;
-};
-
-#endif // COMPOSITEEFFECTCONFIGWIDGET_H
diff --git a/plugins/tools/karbonplugins/filtereffects/CompositeEffectFactory.cpp b/plugins/tools/karbonplugins/filtereffects/CompositeEffectFactory.cpp
deleted file mode 100644
index 8971612562..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/CompositeEffectFactory.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/* 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.
- */
-
-#include "CompositeEffectFactory.h"
-#include "CompositeEffect.h"
-#include "CompositeEffectConfigWidget.h"
-
-#include <klocalizedstring.h>
-
-CompositeEffectFactory::CompositeEffectFactory()
- : KoFilterEffectFactoryBase(CompositeEffectId, i18n("Composite"))
-{
-}
-
-KoFilterEffect *CompositeEffectFactory::createFilterEffect() const
-{
- return new CompositeEffect();
-}
-
-KoFilterEffectConfigWidgetBase *CompositeEffectFactory::createConfigWidget() const
-{
- return new CompositeEffectConfigWidget();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/CompositeEffectFactory.h b/plugins/tools/karbonplugins/filtereffects/CompositeEffectFactory.h
deleted file mode 100644
index 1495393903..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/CompositeEffectFactory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* 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 COMPOSITEEFFECTFACTORY_H
-#define COMPOSITEEFFECTFACTORY_H
-
-#include "KoFilterEffectFactoryBase.h"
-
-class KoFilterEffect;
-
-class CompositeEffectFactory : public KoFilterEffectFactoryBase
-{
-public:
- CompositeEffectFactory();
- KoFilterEffect *createFilterEffect() const override;
- KoFilterEffectConfigWidgetBase *createConfigWidget() const override;
-};
-
-#endif // COMPOSITEEFFECTFACTORY_H
diff --git a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffect.cpp b/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffect.cpp
deleted file mode 100644
index 2f6a7f069a..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffect.cpp
+++ /dev/null
@@ -1,365 +0,0 @@
-/* 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
- 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/karbonplugins/filtereffects/ConvolveMatrixEffect.h b/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffect.h
deleted file mode 100644
index ea673e35cc..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffect.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/* 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.
- */
-
-#ifndef CONVOLVEMATRIXEFFECT_H
-#define CONVOLVEMATRIXEFFECT_H
-
-#include "KoFilterEffect.h"
-#include <QPointF>
-#include <QVector>
-
-#define ConvolveMatrixEffectId "feConvolveMatrix"
-
-class KoFilterEffectLoadingContext;
-
-/// A convolve matrix effect
-class ConvolveMatrixEffect : public KoFilterEffect
-{
-public:
- /// Edge mode, i.e. how the kernel behaves at image edges
- enum EdgeMode {
- Duplicate, ///< duplicates colors at the edges
- Wrap, ///< takes the colors at the opposite edge
- None ///< uses values of zero for each color channel
- };
-
- ConvolveMatrixEffect();
-
- /// Returns the order of the kernel matrix
- QPoint order() const;
-
- /// Sets the order of the kernel matrix
- void setOrder(const QPoint &order);
-
- /// Returns the kernel matrix
- QVector<qreal> kernel() const;
-
- /// Sets the kernel matrix
- void setKernel(const QVector<qreal> &kernel);
-
- /// Returns the divisor
- qreal divisor() const;
-
- /// Sets the divisor
- void setDivisor(qreal divisor);
-
- /// Returns the bias
- qreal bias() const;
-
- /// Sets the bias
- void setBias(qreal bias);
-
- /// Returns the target cell within the kernel
- QPoint target() const;
-
- /// Sets the target cell within the kernel
- void setTarget(const QPoint &target);
-
- /// Returns edge mode
- EdgeMode edgeMode() const;
-
- /// Sets the edge mode
- void setEdgeMode(EdgeMode edgeMode);
-
- /// Returns if alpha values are preserved
- bool isPreserveAlphaEnabled() const;
-
- /// Enables/disables preserving alpha values
- void enablePreserveAlpha(bool on);
-
- /// reimplemented from KoFilterEffect
- QImage processImage(const QImage &image, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- bool load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context) override;
- /// reimplemented from KoFilterEffect
- void save(KoXmlWriter &writer) override;
-
-private:
- void setDefaults();
-
- QPoint m_order; ///< the dimension of the kernel
- QVector<qreal> m_kernel; ///< the kernel
- qreal m_divisor; ///< the divisor
- qreal m_bias; ///< the bias
- QPoint m_target; ///< target cell within the kernel
- EdgeMode m_edgeMode; ///< the edge mode
- QPointF m_kernelUnitLength; ///< the kernel unit length
- bool m_preserveAlpha; ///< indicates if original alpha values are left intact
-};
-
-#endif // CONVOLVEMATRIXEFFECT_H
diff --git a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectConfigWidget.cpp b/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectConfigWidget.cpp
deleted file mode 100644
index 7d8a7e8c2f..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectConfigWidget.cpp
+++ /dev/null
@@ -1,262 +0,0 @@
-/* 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 "ConvolveMatrixEffectConfigWidget.h"
-#include "ConvolveMatrixEffect.h"
-#include "KoFilterEffect.h"
-#include "MatrixDataModel.h"
-
-#include <klocalizedstring.h>
-#include <kcombobox.h>
-#include <QDialog>
-
-#include <QGridLayout>
-#include <QLabel>
-#include <QDoubleSpinBox>
-#include <QPushButton>
-#include <QCheckBox>
-#include <QTableView>
-#include <QHeaderView>
-#include <KConfigGroup>
-
-#include "kis_double_parse_spin_box.h"
-#include "kis_int_parse_spin_box.h"
-
-ConvolveMatrixEffectConfigWidget::ConvolveMatrixEffectConfigWidget(QWidget *parent)
- : KoFilterEffectConfigWidgetBase(parent)
- , m_effect(0)
-{
- QGridLayout *g = new QGridLayout(this);
-
- m_edgeMode = new KComboBox(this);
- m_edgeMode->addItem(i18n("Duplicate"));
- m_edgeMode->addItem(i18n("Wrap"));
- m_edgeMode->addItem(i18n("None"));
- g->addWidget(new QLabel(i18n("Edge mode:"), this), 0, 0);
- g->addWidget(m_edgeMode, 0, 1, 1, 3);
-
- m_orderX = new KisIntParseSpinBox(this);
- m_orderX->setRange(1, 30);
- m_orderY = new KisIntParseSpinBox(this);
- m_orderY->setRange(1, 30);
- g->addWidget(new QLabel(i18n("Kernel size:"), this), 1, 0);
- g->addWidget(m_orderX, 1, 1);
- g->addWidget(new QLabel("X", this), 1, 2, Qt::AlignHCenter);
- g->addWidget(m_orderY, 1, 3);
-
- m_targetX = new KisIntParseSpinBox(this);
- m_targetX->setRange(0, 30);
- m_targetY = new KisIntParseSpinBox(this);
- m_targetY->setRange(0, 30);
- g->addWidget(new QLabel(i18n("Target point:"), this), 2, 0);
- g->addWidget(m_targetX, 2, 1);
- g->addWidget(new QLabel("X", this), 2, 2, Qt::AlignHCenter);
- g->addWidget(m_targetY, 2, 3);
-
- m_divisor = new KisDoubleParseSpinBox(this);
- m_bias = new KisDoubleParseSpinBox(this);
- g->addWidget(new QLabel(i18n("Divisor:"), this), 3, 0);
- g->addWidget(m_divisor, 3, 1);
- g->addWidget(new QLabel(i18n("Bias:"), this), 3, 2);
- g->addWidget(m_bias, 3, 3);
-
- m_preserveAlpha = new QCheckBox(i18n("Preserve alpha"), this);
- g->addWidget(m_preserveAlpha, 4, 1, 1, 3);
-
- QPushButton *kernelButton = new QPushButton(i18n("Edit kernel"), this);
- g->addWidget(kernelButton, 5, 0, 1, 4);
-
- setLayout(g);
-
- connect(m_edgeMode, SIGNAL(currentIndexChanged(int)), this, SLOT(edgeModeChanged(int)));
- connect(m_orderX, SIGNAL(valueChanged(int)), this, SLOT(orderChanged(int)));
- connect(m_orderY, SIGNAL(valueChanged(int)), this, SLOT(orderChanged(int)));
- connect(m_targetX, SIGNAL(valueChanged(int)), this, SLOT(targetChanged(int)));
- connect(m_targetY, SIGNAL(valueChanged(int)), this, SLOT(targetChanged(int)));
- connect(m_divisor, SIGNAL(valueChanged(double)), this, SLOT(divisorChanged(double)));
- connect(m_bias, SIGNAL(valueChanged(double)), this, SLOT(biasChanged(double)));
- connect(kernelButton, SIGNAL(clicked(bool)), this, SLOT(editKernel()));
- connect(m_preserveAlpha, SIGNAL(toggled(bool)), this, SLOT(preserveAlphaChanged(bool)));
-
- m_matrixModel = new MatrixDataModel(this);
-}
-
-bool ConvolveMatrixEffectConfigWidget::editFilterEffect(KoFilterEffect *filterEffect)
-{
- m_effect = dynamic_cast<ConvolveMatrixEffect *>(filterEffect);
- if (!m_effect) {
- return false;
- }
-
- m_edgeMode->blockSignals(true);
- m_edgeMode->setCurrentIndex(m_effect->edgeMode());
- m_edgeMode->blockSignals(false);
- m_orderX->blockSignals(true);
- m_orderX->setValue(m_effect->order().x());
- m_orderX->blockSignals(false);
- m_orderY->blockSignals(true);
- m_orderY->setValue(m_effect->order().y());
- m_orderY->blockSignals(false);
- m_targetX->blockSignals(true);
- m_targetX->setMaximum(m_orderX->value());
- m_targetX->setValue(m_effect->target().x() + 1);
- m_targetX->blockSignals(false);
- m_targetY->blockSignals(true);
- m_targetY->setMaximum(m_orderY->value());
- m_targetY->setValue(m_effect->target().y() + 1);
- m_targetY->blockSignals(false);
- m_divisor->blockSignals(true);
- m_divisor->setValue(m_effect->divisor());
- m_divisor->blockSignals(false);
- m_bias->blockSignals(true);
- m_bias->setValue(m_effect->bias());
- m_bias->blockSignals(false);
- m_preserveAlpha->blockSignals(true);
- m_preserveAlpha->setChecked(m_effect->isPreserveAlphaEnabled());
- m_preserveAlpha->blockSignals(false);
-
- return true;
-}
-
-void ConvolveMatrixEffectConfigWidget::edgeModeChanged(int id)
-{
- if (!m_effect) {
- return;
- }
-
- switch (id) {
- case ConvolveMatrixEffect::Duplicate:
- m_effect->setEdgeMode(ConvolveMatrixEffect::Duplicate);
- break;
- case ConvolveMatrixEffect::Wrap:
- m_effect->setEdgeMode(ConvolveMatrixEffect::Wrap);
- break;
- case ConvolveMatrixEffect::None:
- m_effect->setEdgeMode(ConvolveMatrixEffect::None);
- break;
- }
- emit filterChanged();
-}
-
-void ConvolveMatrixEffectConfigWidget::orderChanged(int)
-{
- if (!m_effect) {
- return;
- }
-
- QPoint newOrder(m_orderX->value(), m_orderY->value());
- QPoint oldOrder = m_effect->order();
- if (newOrder != oldOrder) {
- m_effect->setOrder(newOrder);
- emit filterChanged();
- }
-
- m_targetX->setMaximum(newOrder.x());
- m_targetY->setMaximum(newOrder.y());
-}
-
-void ConvolveMatrixEffectConfigWidget::targetChanged(int)
-{
- if (!m_effect) {
- return;
- }
-
- QPoint newTarget(m_targetX->value() - 1, m_targetY->value() - 1);
- QPoint oldTarget = m_effect->target();
- if (newTarget != oldTarget) {
- m_effect->setTarget(newTarget);
- emit filterChanged();
- }
-}
-
-void ConvolveMatrixEffectConfigWidget::divisorChanged(double divisor)
-{
- if (!m_effect) {
- return;
- }
-
- if (divisor != m_effect->divisor()) {
- m_effect->setDivisor(divisor);
- emit filterChanged();
- }
-}
-
-void ConvolveMatrixEffectConfigWidget::biasChanged(double bias)
-{
- if (!m_effect) {
- return;
- }
-
- if (bias != m_effect->bias()) {
- m_effect->setBias(bias);
- emit filterChanged();
- }
-}
-
-void ConvolveMatrixEffectConfigWidget::preserveAlphaChanged(bool checked)
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->enablePreserveAlpha(checked);
- emit filterChanged();
-}
-
-void ConvolveMatrixEffectConfigWidget::editKernel()
-{
- if (!m_effect) {
- return;
- }
-
- QVector<qreal> oldKernel = m_effect->kernel();
- QPoint kernelSize = m_effect->order();
- m_matrixModel->setMatrix(oldKernel, kernelSize.y(), kernelSize.x());
- connect(m_matrixModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(kernelChanged()));
-
- QPointer<QDialog> dlg = new QDialog(this);
- QTableView *table = new QTableView(dlg);
- table->setModel(m_matrixModel);
- table->horizontalHeader()->hide();
- table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
- table->verticalHeader()->hide();
- table->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
- QVBoxLayout *mainLayout = new QVBoxLayout;
- dlg->setLayout(mainLayout);
- mainLayout->addWidget(table);
- if (dlg->exec() == QDialog::Accepted) {
- m_effect->setKernel(m_matrixModel->matrix());
- emit filterChanged();
- } else {
- m_effect->setKernel(oldKernel);
- }
- delete dlg;
-
- disconnect(m_matrixModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(kernelChanged()));
-}
-
-void ConvolveMatrixEffectConfigWidget::kernelChanged()
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->setKernel(m_matrixModel->matrix());
- emit filterChanged();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectConfigWidget.h b/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectConfigWidget.h
deleted file mode 100644
index e32bbf9fac..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectConfigWidget.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* 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.
- */
-
-#ifndef CONVOLVEMATRIXEFFECTCONFIGWIDGET_H
-#define CONVOLVEMATRIXEFFECTCONFIGWIDGET_H
-
-#include "KoFilterEffectConfigWidgetBase.h"
-
-class QDoubleSpinBox;
-class KoFilterEffect;
-class ConvolveMatrixEffect;
-class KComboBox;
-class QSpinBox;
-class QCheckBox;
-class MatrixDataModel;
-
-class ConvolveMatrixEffectConfigWidget : public KoFilterEffectConfigWidgetBase
-{
- Q_OBJECT
-public:
- explicit ConvolveMatrixEffectConfigWidget(QWidget *parent = 0);
-
- /// reimplemented from KoFilterEffectConfigWidgetBase
- bool editFilterEffect(KoFilterEffect *filterEffect) override;
-
-private Q_SLOTS:
- void orderChanged(int value);
- void targetChanged(int value);
- void divisorChanged(double divisor);
- void biasChanged(double bias);
- void edgeModeChanged(int mode);
- void preserveAlphaChanged(bool checked);
- void editKernel();
- void kernelChanged();
-private:
- ConvolveMatrixEffect *m_effect;
- KComboBox *m_edgeMode;
- QSpinBox *m_orderX;
- QSpinBox *m_orderY;
- QSpinBox *m_targetX;
- QSpinBox *m_targetY;
- QDoubleSpinBox *m_divisor;
- QDoubleSpinBox *m_bias;
- QCheckBox *m_preserveAlpha;
- MatrixDataModel *m_matrixModel;
-};
-
-#endif // CONVOLVEMATRIXEFFECTCONFIGWIDGET_H
diff --git a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectFactory.cpp b/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectFactory.cpp
deleted file mode 100644
index b038c2f9de..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectFactory.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/* 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 "ConvolveMatrixEffectFactory.h"
-#include "ConvolveMatrixEffect.h"
-#include "ConvolveMatrixEffectConfigWidget.h"
-
-#include <klocalizedstring.h>
-
-ConvolveMatrixEffectFactory::ConvolveMatrixEffectFactory()
- : KoFilterEffectFactoryBase(ConvolveMatrixEffectId, i18n("Convolve Matrix"))
-{
-}
-
-KoFilterEffect *ConvolveMatrixEffectFactory::createFilterEffect() const
-{
- return new ConvolveMatrixEffect();
-}
-
-KoFilterEffectConfigWidgetBase *ConvolveMatrixEffectFactory::createConfigWidget() const
-{
- return new ConvolveMatrixEffectConfigWidget();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectFactory.h b/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectFactory.h
deleted file mode 100644
index 437a065c6a..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectFactory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* 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.
- */
-
-#ifndef CONVOLVEMATRIXEFFECTFACTORY_H
-#define CONVOLVEMATRIXEFFECTFACTORY_H
-
-#include "KoFilterEffectFactoryBase.h"
-
-class KoFilterEffect;
-
-class ConvolveMatrixEffectFactory : public KoFilterEffectFactoryBase
-{
-public:
- ConvolveMatrixEffectFactory();
- KoFilterEffect *createFilterEffect() const override;
- KoFilterEffectConfigWidgetBase *createConfigWidget() const override;
-};
-
-#endif // CONVOLVEMATRIXEFFECTFACTORY_H
diff --git a/plugins/tools/karbonplugins/filtereffects/FilterEffectsPlugin.cpp b/plugins/tools/karbonplugins/filtereffects/FilterEffectsPlugin.cpp
deleted file mode 100644
index 25511c2388..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/FilterEffectsPlugin.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/* 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.
-*/
-
-#include "FilterEffectsPlugin.h"
-#include "BlurEffectFactory.h"
-#include "OffsetEffectFactory.h"
-#include "MergeEffectFactory.h"
-#include "ColorMatrixEffectFactory.h"
-#include "FloodEffectFactory.h"
-#include "CompositeEffectFactory.h"
-#include "BlendEffectFactory.h"
-#include "ComponentTransferEffectFactory.h"
-#include "ImageEffectFactory.h"
-#include "MorphologyEffectFactory.h"
-#include "ConvolveMatrixEffectFactory.h"
-
-#include "KoFilterEffectRegistry.h"
-
-#include <kpluginfactory.h>
-
-K_PLUGIN_FACTORY_WITH_JSON(FilterEffectsPluginFacory, "karbon_filtereffects.json", registerPlugin<FilterEffectsPlugin>();)
-
-FilterEffectsPlugin::FilterEffectsPlugin(QObject *parent, const QList<QVariant> &)
- : QObject(parent)
-{
- KoFilterEffectRegistry::instance()->add(new BlurEffectFactory());
- KoFilterEffectRegistry::instance()->add(new OffsetEffectFactory());
- KoFilterEffectRegistry::instance()->add(new MergeEffectFactory());
- KoFilterEffectRegistry::instance()->add(new ColorMatrixEffectFactory());
- KoFilterEffectRegistry::instance()->add(new FloodEffectFactory());
- KoFilterEffectRegistry::instance()->add(new CompositeEffectFactory());
- KoFilterEffectRegistry::instance()->add(new BlendEffectFactory());
- KoFilterEffectRegistry::instance()->add(new ComponentTransferEffectFactory());
- KoFilterEffectRegistry::instance()->add(new ImageEffectFactory());
- KoFilterEffectRegistry::instance()->add(new MorphologyEffectFactory());
- KoFilterEffectRegistry::instance()->add(new ConvolveMatrixEffectFactory());
-}
-#include <FilterEffectsPlugin.moc>
diff --git a/plugins/tools/karbonplugins/filtereffects/FilterEffectsPlugin.h b/plugins/tools/karbonplugins/filtereffects/FilterEffectsPlugin.h
deleted file mode 100644
index c29db0df23..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/FilterEffectsPlugin.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* 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 KARBONFILTEREFFECTSPLUGIN_H
-#define KARBONFILTEREFFECTSPLUGIN_H
-
-#include <QObject>
-
-class FilterEffectsPlugin : public QObject
-{
- Q_OBJECT
-public:
- FilterEffectsPlugin(QObject *parent, const QList<QVariant> &);
- ~FilterEffectsPlugin() override {}
-};
-
-#endif // KARBONFILTEREFFECTSPLUGIN_H
diff --git a/plugins/tools/karbonplugins/filtereffects/FloodEffect.cpp b/plugins/tools/karbonplugins/filtereffects/FloodEffect.cpp
deleted file mode 100644
index 7dd82dd16b..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/FloodEffect.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/* 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.
- */
-
-#include "FloodEffect.h"
-#include "KoFilterEffectRenderContext.h"
-#include "KoViewConverter.h"
-#include "KoXmlWriter.h"
-#include "KoXmlReader.h"
-#include <klocalizedstring.h>
-#include <QPainter>
-
-FloodEffect::FloodEffect()
- : KoFilterEffect(FloodEffectId, i18n("Flood fill"))
- , m_color(Qt::black)
-{
-}
-
-QColor FloodEffect::floodColor() const
-{
- return m_color;
-}
-
-void FloodEffect::setFloodColor(const QColor &color)
-{
- m_color = color;
-}
-
-QImage FloodEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const
-{
- QImage result = image;
- QPainter painter(&result);
- painter.fillRect(context.filterRegion(), m_color);
-
- return result;
-}
-
-bool FloodEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &)
-{
- if (element.tagName() != id()) {
- return false;
- }
-
- m_color = Qt::black;
-
- if (element.hasAttribute("flood-color")) {
- QString colorStr = element.attribute("flood-color").trimmed();
- if (colorStr.startsWith(QLatin1String("rgb("))) {
- QStringList channels = colorStr.mid(4, colorStr.length() - 5).split(',');
- float r = channels[0].toDouble();
- if (channels[0].contains('%')) {
- r /= 100.0;
- } else {
- r /= 255.0;
- }
- float g = channels[1].toDouble();
- if (channels[1].contains('%')) {
- g /= 100.0;
- } else {
- g /= 255.0;
- }
- float b = channels[2].toDouble();
- if (channels[2].contains('%')) {
- b /= 100.0;
- } else {
- b /= 255.0;
- }
- m_color.setRgbF(r, g, b);
-
- } else {
- m_color.setNamedColor(colorStr);
- }
- // TODO: add support for currentColor
- }
-
- if (element.hasAttribute("flood-opacity")) {
- m_color.setAlphaF(element.attribute("flood-opacity").toDouble());
- }
-
- return true;
-}
-
-void FloodEffect::save(KoXmlWriter &writer)
-{
- writer.startElement(FloodEffectId);
-
- saveCommonAttributes(writer);
-
- writer.addAttribute("flood-color", m_color.name());
- if (m_color.alpha() < 255) {
- writer.addAttribute("flood-opacity", QString("%1").arg(m_color.alphaF()));
- }
-
- writer.endElement();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/FloodEffect.h b/plugins/tools/karbonplugins/filtereffects/FloodEffect.h
deleted file mode 100644
index 724fea492d..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/FloodEffect.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* 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 FLOODEFFECT_H
-#define FLOODEFFECT_H
-
-#include "KoFilterEffect.h"
-#include <QColor>
-
-#define FloodEffectId "feFlood"
-
-class KoFilterEffectLoadingContext;
-
-/// A flood fill effect
-class FloodEffect : public KoFilterEffect
-{
-public:
- FloodEffect();
-
- QColor floodColor() const;
- void setFloodColor(const QColor &color);
-
- /// reimplemented from KoFilterEffect
- QImage processImage(const QImage &image, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- bool load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context) override;
- /// reimplemented from KoFilterEffect
- void save(KoXmlWriter &writer) override;
-
-private:
- QColor m_color;
-};
-
-#endif // FLOODEFFECT_H
diff --git a/plugins/tools/karbonplugins/filtereffects/FloodEffectConfigWidget.cpp b/plugins/tools/karbonplugins/filtereffects/FloodEffectConfigWidget.cpp
deleted file mode 100644
index 3517d6ce26..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/FloodEffectConfigWidget.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/* 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.
- */
-
-#include "FloodEffectConfigWidget.h"
-#include "FloodEffect.h"
-#include "KoFilterEffect.h"
-
-#include <KoColorPopupAction.h>
-
-#include <klocalizedstring.h>
-
-#include <QGridLayout>
-#include <QLabel>
-#include <QToolButton>
-
-FloodEffectConfigWidget::FloodEffectConfigWidget(QWidget *parent)
- : KoFilterEffectConfigWidgetBase(parent)
- , m_effect(0)
-{
- QGridLayout *g = new QGridLayout(this);
-
- g->addWidget(new QLabel(i18n("Flood color"), this), 0, 0);
- QToolButton *button = new QToolButton(this);
- g->addWidget(button, 0, 1);
- m_actionStopColor = new KoColorPopupAction(this);
- button->setDefaultAction(m_actionStopColor);
- setLayout(g);
-
- connect(m_actionStopColor, SIGNAL(colorChanged(KoColor)), this, SLOT(colorChanged()));
-}
-
-bool FloodEffectConfigWidget::editFilterEffect(KoFilterEffect *filterEffect)
-{
- m_effect = dynamic_cast<FloodEffect *>(filterEffect);
- if (!m_effect) {
- return false;
- }
-
- m_actionStopColor->setCurrentColor(m_effect->floodColor());
- return true;
-}
-
-void FloodEffectConfigWidget::colorChanged()
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->setFloodColor(m_actionStopColor->currentColor());
- emit filterChanged();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/FloodEffectConfigWidget.h b/plugins/tools/karbonplugins/filtereffects/FloodEffectConfigWidget.h
deleted file mode 100644
index 40aa5a988c..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/FloodEffectConfigWidget.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* 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 FLOODEFFECTCONFIGWIDGET_H
-#define FLOODEFFECTCONFIGWIDGET_H
-
-#include "KoFilterEffectConfigWidgetBase.h"
-
-class KoFilterEffect;
-class FloodEffect;
-class KoColorPopupAction;
-
-class FloodEffectConfigWidget : public KoFilterEffectConfigWidgetBase
-{
- Q_OBJECT
-public:
- explicit FloodEffectConfigWidget(QWidget *parent = 0);
-
- /// reimplemented from KoFilterEffectConfigWidgetBase
- bool editFilterEffect(KoFilterEffect *filterEffect) override;
-
-private Q_SLOTS:
- void colorChanged();
-
-private:
- FloodEffect *m_effect;
- KoColorPopupAction *m_actionStopColor;
-};
-
-#endif // FLOODEFFECTCONFIGWIDGET_H
diff --git a/plugins/tools/karbonplugins/filtereffects/FloodEffectFactory.cpp b/plugins/tools/karbonplugins/filtereffects/FloodEffectFactory.cpp
deleted file mode 100644
index a59005b4c9..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/FloodEffectFactory.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/* 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.
- */
-
-#include "FloodEffectFactory.h"
-#include "FloodEffect.h"
-#include "FloodEffectConfigWidget.h"
-
-#include <klocalizedstring.h>
-
-FloodEffectFactory::FloodEffectFactory()
- : KoFilterEffectFactoryBase(FloodEffectId, i18n("Flood fill"))
-{
-}
-
-KoFilterEffect *FloodEffectFactory::createFilterEffect() const
-{
- return new FloodEffect();
-}
-
-KoFilterEffectConfigWidgetBase *FloodEffectFactory::createConfigWidget() const
-{
- return new FloodEffectConfigWidget();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/FloodEffectFactory.h b/plugins/tools/karbonplugins/filtereffects/FloodEffectFactory.h
deleted file mode 100644
index 432a7c6362..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/FloodEffectFactory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* 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 FLOODEFFECTFACTORY_H
-#define FLOODEFFECTFACTORY_H
-
-#include "KoFilterEffectFactoryBase.h"
-
-class KoFilterEffect;
-
-class FloodEffectFactory : public KoFilterEffectFactoryBase
-{
-public:
- FloodEffectFactory();
- KoFilterEffect *createFilterEffect() const override;
- KoFilterEffectConfigWidgetBase *createConfigWidget() const override;
-};
-
-#endif // FLOODEFFECTFACTORY_H
diff --git a/plugins/tools/karbonplugins/filtereffects/ImageEffect.cpp b/plugins/tools/karbonplugins/filtereffects/ImageEffect.cpp
deleted file mode 100644
index 62a71d6050..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ImageEffect.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/* 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 "ImageEffect.h"
-
-#include "KoFilterEffectRenderContext.h"
-#include "KoFilterEffectLoadingContext.h"
-#include "KoViewConverter.h"
-#include "KoXmlWriter.h"
-#include "KoXmlReader.h"
-
-#include <KisMimeDatabase.h>
-#include <QBuffer>
-#include <QPainter>
-#include <QDebug>
-
-#include <klocalizedstring.h>
-
-ImageEffect::ImageEffect()
- : KoFilterEffect(ImageEffectId, i18n("Image"))
-{
- setRequiredInputCount(0);
- setMaximalInputCount(0);
-}
-
-QImage ImageEffect::image() const
-{
- return m_image;
-}
-
-void ImageEffect::setImage(const QImage &image)
-{
- m_image = image;
-}
-
-QImage ImageEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const
-{
- QImage result(image.size(), QImage::Format_ARGB32_Premultiplied);
- result.fill(qRgba(0, 0, 0, 0));
-
- QPainter p(&result);
- p.drawImage(context.filterRegion(), m_image);
- return result;
-}
-
-bool ImageEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context)
-{
- if (element.tagName() != id()) {
- return false;
- }
-
- QString href = element.attribute("xlink:href");
- if (href.startsWith(QLatin1String("data:"))) {
- int start = href.indexOf("base64,");
- if (start <= 0 || !m_image.loadFromData(QByteArray::fromBase64(href.mid(start + 7).toLatin1()))) {
- return false;
- }
- } else if (!m_image.load(context.pathFromHref(href))) {
- return false;
- }
-
- return true;
-}
-
-void ImageEffect::save(KoXmlWriter &writer)
-{
- writer.startElement(ImageEffectId);
-
- saveCommonAttributes(writer);
-
- QByteArray ba;
- QBuffer buffer(&ba);
- buffer.open(QIODevice::WriteOnly);
- if (m_image.save(&buffer, "PNG")) {
- writer.addAttribute("xlink:href", "data:image/png;base64," + ba.toBase64());
- }
-
- writer.endElement();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/ImageEffect.h b/plugins/tools/karbonplugins/filtereffects/ImageEffect.h
deleted file mode 100644
index b81ede9a72..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ImageEffect.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* 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.
- */
-
-#ifndef IMAGEEFFECT_H
-#define IMAGEEFFECT_H
-
-#include "KoFilterEffect.h"
-#include <QImage>
-
-#define ImageEffectId "feImage"
-
-/// An image offset effect
-class ImageEffect : public KoFilterEffect
-{
-public:
- ImageEffect();
-
- /// Returns the image
- QImage image() const;
-
- /// Sets the image
- void setImage(const QImage &image);
-
- /// reimplemented from KoFilterEffect
- QImage processImage(const QImage &image, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- bool load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context) override;
- /// reimplemented from KoFilterEffect
- void save(KoXmlWriter &writer) override;
-
-private:
- QImage m_image;
- QRectF m_bound;
-};
-
-#endif // IMAGEEFFECT_H
diff --git a/plugins/tools/karbonplugins/filtereffects/ImageEffectConfigWidget.cpp b/plugins/tools/karbonplugins/filtereffects/ImageEffectConfigWidget.cpp
deleted file mode 100644
index 32d1957802..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ImageEffectConfigWidget.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/* 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 "ImageEffectConfigWidget.h"
-#include "ImageEffect.h"
-
-#include "KoFilterEffect.h"
-#include <KoFileDialog.h>
-
-#include <klocalizedstring.h>
-
-#include <QGridLayout>
-#include <QLabel>
-#include <QPushButton>
-#include <QImageReader>
-#include <KConfigGroup>
-
-ImageEffectConfigWidget::ImageEffectConfigWidget(QWidget *parent)
- : KoFilterEffectConfigWidgetBase(parent)
- , m_effect(0)
-{
- QGridLayout *g = new QGridLayout(this);
-
- m_image = new QLabel(this);
- QPushButton *button = new QPushButton(i18n("Select image..."), this);
-
- g->addWidget(m_image, 0, 0, Qt::AlignCenter);
- g->addWidget(button, 0, 1);
-
- setLayout(g);
-
- connect(button, SIGNAL(clicked()), this, SLOT(selectImage()));
-}
-
-bool ImageEffectConfigWidget::editFilterEffect(KoFilterEffect *filterEffect)
-{
- m_effect = dynamic_cast<ImageEffect *>(filterEffect);
- if (!m_effect) {
- return false;
- }
-
- m_image->setPixmap(QPixmap::fromImage(m_effect->image().scaledToWidth(80)));
-
- return true;
-}
-
-void ImageEffectConfigWidget::selectImage()
-{
- if (!m_effect) {
- return;
- }
-
- QStringList imageFilter;
- // add filters for all formats supported by QImage
- Q_FOREACH (const QByteArray &format, QImageReader::supportedImageFormats()) {
- imageFilter << QString("image/") + format;
- }
-
- KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument");
- dialog.setCaption(i18n("Select image"));
- dialog.setImageFilters();
-
- QString fname = dialog.filename();
-
- if (fname.isEmpty()) {
- return;
- }
-
- QImage newImage;
- if (!newImage.load(fname)) {
- return;
- }
-
- m_effect->setImage(newImage);
- editFilterEffect(m_effect);
-
- emit filterChanged();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/ImageEffectConfigWidget.h b/plugins/tools/karbonplugins/filtereffects/ImageEffectConfigWidget.h
deleted file mode 100644
index dfe635723d..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ImageEffectConfigWidget.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* 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.
- */
-
-#ifndef IMAGEEFFECTCONFIGWIDGET_H
-#define IMAGEEFFECTCONFIGWIDGET_H
-
-#include "KoFilterEffectConfigWidgetBase.h"
-
-class KoFilterEffect;
-class ImageEffect;
-class QLabel;
-
-class ImageEffectConfigWidget : public KoFilterEffectConfigWidgetBase
-{
- Q_OBJECT
-public:
- explicit ImageEffectConfigWidget(QWidget *parent = 0);
-
- /// reimplemented from KoFilterEffectConfigWidgetBase
- bool editFilterEffect(KoFilterEffect *filterEffect) override;
-
-private Q_SLOTS:
- void selectImage();
-
-private:
- ImageEffect *m_effect;
- QLabel *m_image;
-};
-
-#endif // IMAGEEFFECTCONFIGWIDGET_H
diff --git a/plugins/tools/karbonplugins/filtereffects/ImageEffectFactory.cpp b/plugins/tools/karbonplugins/filtereffects/ImageEffectFactory.cpp
deleted file mode 100644
index d1d381e4a1..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ImageEffectFactory.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/* 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 "ImageEffectFactory.h"
-#include "ImageEffect.h"
-#include "ImageEffectConfigWidget.h"
-#include <klocalizedstring.h>
-
-ImageEffectFactory::ImageEffectFactory()
- : KoFilterEffectFactoryBase(ImageEffectId, i18n("Image"))
-{
-}
-
-KoFilterEffect *ImageEffectFactory::createFilterEffect() const
-{
- return new ImageEffect();
-}
-
-KoFilterEffectConfigWidgetBase *ImageEffectFactory::createConfigWidget() const
-{
- return new ImageEffectConfigWidget();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/ImageEffectFactory.h b/plugins/tools/karbonplugins/filtereffects/ImageEffectFactory.h
deleted file mode 100644
index 22e0192ee4..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/ImageEffectFactory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* 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.
- */
-
-#ifndef IMAGEEFFECTFACTORY_H
-#define IMAGEEFFECTFACTORY_H
-
-#include "KoFilterEffectFactoryBase.h"
-
-class KoFilterEffect;
-
-class ImageEffectFactory : public KoFilterEffectFactoryBase
-{
-public:
- ImageEffectFactory();
- KoFilterEffect *createFilterEffect() const override;
- KoFilterEffectConfigWidgetBase *createConfigWidget() const override;
-};
-
-#endif // IMAGEEFFECTFACTORY_H
diff --git a/plugins/tools/karbonplugins/filtereffects/MatrixDataModel.cpp b/plugins/tools/karbonplugins/filtereffects/MatrixDataModel.cpp
deleted file mode 100644
index 6c0a699657..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MatrixDataModel.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/* 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 "MatrixDataModel.h"
-
-#include "kis_num_parser.h"
-
-MatrixDataModel::MatrixDataModel(QObject *parent)
- : QAbstractTableModel(parent)
- , m_rows(0)
- , m_cols(0)
-{
-}
-
-void MatrixDataModel::setMatrix(const QVector<qreal> &matrix, int rows, int cols)
-{
- m_matrix = matrix;
- m_rows = rows;
- m_cols = cols;
- Q_ASSERT(m_rows);
- Q_ASSERT(m_cols);
- Q_ASSERT(m_matrix.count() == m_rows * m_cols);
- beginResetModel();
- endResetModel();
-}
-
-QVector<qreal> MatrixDataModel::matrix() const
-{
- return m_matrix;
-}
-
-int MatrixDataModel::rowCount(const QModelIndex &/*parent*/) const
-{
- return m_rows;
-}
-
-int MatrixDataModel::columnCount(const QModelIndex &/*parent*/) const
-{
- return m_cols;
-}
-
-QVariant MatrixDataModel::data(const QModelIndex &index, int role) const
-{
- int element = index.row() * m_cols + index.column();
- switch (role) {
- case Qt::DisplayRole:
- case Qt::EditRole:
- return QVariant(QString("%1").arg(m_matrix[element], 2));
- break;
- default:
- return QVariant();
- }
-}
-
-bool MatrixDataModel::setData(const QModelIndex &index, const QVariant &value, int /*role*/)
-{
- int element = index.row() * m_cols + index.column();
- bool valid = false;
- qreal elementValue = KisNumericParser::parseSimpleMathExpr(value.toString(), &valid);
- if (!valid) {
- return false;
- }
- m_matrix[element] = elementValue;
- emit dataChanged(index, index);
- return true;
-}
-
-Qt::ItemFlags MatrixDataModel::flags(const QModelIndex &/*index*/) const
-{
- return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/MatrixDataModel.h b/plugins/tools/karbonplugins/filtereffects/MatrixDataModel.h
deleted file mode 100644
index 65cbc5953b..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MatrixDataModel.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* 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.
-*/
-
-#ifndef MATRIXDATAMODEL_H
-#define MATRIXDATAMODEL_H
-
-#include <QAbstractTableModel>
-#include <QVector>
-
-class MatrixDataModel : public QAbstractTableModel
-{
-public:
- /// Creates a new matrix data model
- explicit MatrixDataModel(QObject *parent = 0);
-
- /// Sets the matrix data and rows/columns to use
- void setMatrix(const QVector<qreal> &matrix, int rows, int cols);
-
- /// Returns the matrix data
- QVector<qreal> matrix() const;
-
- // reimplemented
- int rowCount(const QModelIndex &/*parent*/) const override;
- // reimplemented
- int columnCount(const QModelIndex &/*parent*/) const override;
- // reimplemented
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- // reimplemented
- bool setData(const QModelIndex &index, const QVariant &value, int /*role*/) override;
- // reimplemented
- Qt::ItemFlags flags(const QModelIndex &/*index*/) const override;
-
-private:
- QVector<qreal> m_matrix; ///< the matrix data to handle
- int m_rows; ///< the number or rows in the matrix
- int m_cols; ///< the number of columns in the matrix
-};
-
-#endif // MATRIXDATAMODEL_H
diff --git a/plugins/tools/karbonplugins/filtereffects/MergeEffect.cpp b/plugins/tools/karbonplugins/filtereffects/MergeEffect.cpp
deleted file mode 100644
index 2c9acb253a..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MergeEffect.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/* 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.
- */
-
-#include "MergeEffect.h"
-#include "KoViewConverter.h"
-#include "KoXmlWriter.h"
-#include "KoXmlReader.h"
-#include <klocalizedstring.h>
-#include <QPainter>
-#include <limits.h>
-
-MergeEffect::MergeEffect()
- : KoFilterEffect(MergeEffectId, i18n("Merge"))
-{
- setRequiredInputCount(2);
- setMaximalInputCount(INT_MAX);
-}
-
-QImage MergeEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &/*context*/) const
-{
- Q_UNUSED(image);
-
- return image;
-}
-
-QImage MergeEffect::processImages(const QList<QImage> &images, const KoFilterEffectRenderContext &/*context*/) const
-{
- int imageCount = images.count();
- if (!imageCount) {
- return QImage();
- }
-
- QImage result = images[0];
- if (imageCount == 1) {
- return result;
- }
-
- QPainter p(&result);
-
- for (int i = 1; i < imageCount; ++i) {
- p.drawImage(QPoint(), images[i]);
- }
-
- return result;
-}
-
-bool MergeEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &)
-{
- if (element.tagName() != id()) {
- return false;
- }
-
- int inputCount = inputs().count();
- int inputIndex = 0;
- for (KoXmlNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
- KoXmlElement node = n.toElement();
- if (node.tagName() == "feMergeNode") {
- if (node.hasAttribute("in")) {
- if (inputIndex < inputCount) {
- setInput(inputIndex, node.attribute("in"));
- } else {
- addInput(node.attribute("in"));
- }
- inputIndex++;
- }
- }
- }
-
- return true;
-}
-
-void MergeEffect::save(KoXmlWriter &writer)
-{
- writer.startElement(MergeEffectId);
-
- saveCommonAttributes(writer);
-
- Q_FOREACH (const QString &input, inputs()) {
- writer.startElement("feMergeNode");
- writer.addAttribute("in", input);
- writer.endElement();
- }
-
- writer.endElement();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/MergeEffect.h b/plugins/tools/karbonplugins/filtereffects/MergeEffect.h
deleted file mode 100644
index 93ea481eb5..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MergeEffect.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* 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 MERGEEFFECT_H
-#define MERGEEFFECT_H
-
-#include "KoFilterEffect.h"
-
-#define MergeEffectId "feMerge"
-
-/// A gaussian blur effect
-class MergeEffect : public KoFilterEffect
-{
-public:
- MergeEffect();
-
- /// reimplemented from KoFilterEffect
- QImage processImage(const QImage &image, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- QImage processImages(const QList<QImage> &images, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- bool load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context) override;
- /// reimplemented from KoFilterEffect
- void save(KoXmlWriter &writer) override;
-};
-
-#endif // MERGEEFFECT_H
diff --git a/plugins/tools/karbonplugins/filtereffects/MergeEffectConfigWidget.cpp b/plugins/tools/karbonplugins/filtereffects/MergeEffectConfigWidget.cpp
deleted file mode 100644
index 09d4118c80..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MergeEffectConfigWidget.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/* 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.
- */
-
-#include "MergeEffectConfigWidget.h"
-#include "MergeEffect.h"
-#include "KoFilterEffect.h"
-#include <QGridLayout>
-
-MergeEffectConfigWidget::MergeEffectConfigWidget(QWidget *parent)
- : KoFilterEffectConfigWidgetBase(parent)
- , m_effect(0)
-{
- QGridLayout *g = new QGridLayout(this);
-
- setLayout(g);
-}
-
-bool MergeEffectConfigWidget::editFilterEffect(KoFilterEffect *filterEffect)
-{
- m_effect = dynamic_cast<MergeEffect *>(filterEffect);
- if (!m_effect) {
- return false;
- }
-
- return true;
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/MergeEffectConfigWidget.h b/plugins/tools/karbonplugins/filtereffects/MergeEffectConfigWidget.h
deleted file mode 100644
index 2c22881cd5..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MergeEffectConfigWidget.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* 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 MERGEEFFECTCONFIGWIDGET_H
-#define MERGEEFFECTCONFIGWIDGET_H
-
-#include "KoFilterEffectConfigWidgetBase.h"
-
-class KoFilterEffect;
-class MergeEffect;
-
-class MergeEffectConfigWidget : public KoFilterEffectConfigWidgetBase
-{
- Q_OBJECT
-public:
- explicit MergeEffectConfigWidget(QWidget *parent = 0);
-
- /// reimplemented from KoFilterEffectConfigWidgetBase
- bool editFilterEffect(KoFilterEffect *filterEffect) override;
-
-private Q_SLOTS:
-
-private:
- MergeEffect *m_effect;
-};
-
-#endif // MERGEEFFECTCONFIGWIDGET_H
diff --git a/plugins/tools/karbonplugins/filtereffects/MergeEffectFactory.cpp b/plugins/tools/karbonplugins/filtereffects/MergeEffectFactory.cpp
deleted file mode 100644
index 82bed73e98..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MergeEffectFactory.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/* 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.
- */
-
-#include "MergeEffectFactory.h"
-#include "MergeEffect.h"
-#include "MergeEffectConfigWidget.h"
-
-#include <klocalizedstring.h>
-
-MergeEffectFactory::MergeEffectFactory()
- : KoFilterEffectFactoryBase(MergeEffectId, i18n("Merge"))
-{
-}
-
-KoFilterEffect *MergeEffectFactory::createFilterEffect() const
-{
- return new MergeEffect();
-}
-
-KoFilterEffectConfigWidgetBase *MergeEffectFactory::createConfigWidget() const
-{
- return new MergeEffectConfigWidget();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/MergeEffectFactory.h b/plugins/tools/karbonplugins/filtereffects/MergeEffectFactory.h
deleted file mode 100644
index b18026e7e4..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MergeEffectFactory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* 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 MERGEEFFECTFACTORY_H
-#define MERGEEFFECTFACTORY_H
-
-#include "KoFilterEffectFactoryBase.h"
-
-class KoFilterEffect;
-
-class MergeEffectFactory : public KoFilterEffectFactoryBase
-{
-public:
- MergeEffectFactory();
- KoFilterEffect *createFilterEffect() const override;
- KoFilterEffectConfigWidgetBase *createConfigWidget() const override;
-};
-
-#endif // MERGEEFFECTFACTORY_H
diff --git a/plugins/tools/karbonplugins/filtereffects/MorphologyEffect.cpp b/plugins/tools/karbonplugins/filtereffects/MorphologyEffect.cpp
deleted file mode 100644
index f48bfbbe0f..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MorphologyEffect.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/* 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 "MorphologyEffect.h"
-#include "KoFilterEffectRenderContext.h"
-#include "KoFilterEffectLoadingContext.h"
-#include "KoViewConverter.h"
-#include "KoXmlWriter.h"
-#include "KoXmlReader.h"
-#include <klocalizedstring.h>
-#include <QRect>
-#include <QImage>
-#include <cmath>
-
-MorphologyEffect::MorphologyEffect()
- : KoFilterEffect(MorphologyEffectId, i18n("Morphology"))
- , m_radius(0, 0)
- , m_operator(Erode)
-{
-}
-
-QPointF MorphologyEffect::morphologyRadius() const
-{
- return m_radius;
-}
-
-void MorphologyEffect::setMorphologyRadius(const QPointF &radius)
-{
- m_radius = radius;
-}
-
-MorphologyEffect::Operator MorphologyEffect::morphologyOperator() const
-{
- return m_operator;
-}
-
-void MorphologyEffect::setMorphologyOperator(Operator op)
-{
- m_operator = op;
-}
-
-QImage MorphologyEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const
-{
- QImage result = image;
-
- QPointF radius = context.toUserSpace(m_radius);
-
- const int rx = static_cast<int>(ceil(radius.x()));
- const int ry = static_cast<int>(ceil(radius.y()));
-
- const int w = result.width();
- const int h = result.height();
-
- // setup mask
- const int maskSize = (1 + 2 * rx) * (1 + 2 * ry);
- int *mask = new int[maskSize];
- int index = 0;
- for (int y = -ry; y <= ry; ++y) {
- for (int x = -rx; x <= rx; ++x) {
- mask[index] = y * w + x;
- index++;
- }
- }
-
- int dstPixel, srcPixel;
- uchar s0, s1, s2, s3;
- const uchar *src = image.constBits();
- uchar *dst = result.bits();
-
- const QRect roi = context.filterRegion().toRect();
- const int minX = qMax(rx, roi.left());
- const int maxX = qMin(w - rx, roi.right());
- const int minY = qMax(ry, roi.top());
- const int maxY = qMin(h - ry, roi.bottom());
- const int defValue = m_operator == Erode ? 255 : 0;
-
- uchar *d = 0;
-
- for (int row = minY; row < maxY; ++row) {
- for (int col = minX; col < maxX; ++col) {
- dstPixel = row * w + col;
- s0 = s1 = s2 = s3 = defValue;
- for (int i = 0; i < maskSize; ++i) {
- srcPixel = dstPixel + mask[i];
- const uchar *s = &src[4 * srcPixel];
- if (m_operator == Erode) {
- s0 = qMin(s0, s[0]);
- s1 = qMin(s1, s[1]);
- s2 = qMin(s2, s[2]);
- s3 = qMin(s3, s[3]);
- } else {
- s0 = qMax(s0, s[0]);
- s1 = qMax(s1, s[1]);
- s2 = qMax(s2, s[2]);
- s3 = qMax(s3, s[3]);
- }
- }
- d = &dst[4 * dstPixel];
- d[0] = s0;
- d[1] = s1;
- d[2] = s2;
- d[3] = s3;
- }
- }
-
- delete [] mask;
-
- return result;
-}
-
-bool MorphologyEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context)
-{
- if (element.tagName() != id()) {
- return false;
- }
-
- m_radius = QPointF();
- m_operator = Erode;
-
- if (element.hasAttribute("radius")) {
- QString radiusStr = element.attribute("radius").trimmed();
- QStringList params = radiusStr.replace(',', ' ').simplified().split(' ');
- switch (params.count()) {
- case 1:
- m_radius.rx() = params[0].toDouble() * 72. / 90.;
- m_radius.ry() = m_radius.x();
- break;
- case 2:
- m_radius.rx() = params[0].toDouble() * 72. / 90.;
- m_radius.ry() = params[1].toDouble() * 72. / 90.;
- break;
- default:
- m_radius = QPointF();
- }
- }
-
- m_radius = context.convertFilterPrimitiveUnits(m_radius);
-
- if (element.hasAttribute("operator")) {
- QString op = element.attribute("operator");
- if (op == "dilate") {
- m_operator = Dilate;
- }
- }
-
- return true;
-}
-
-void MorphologyEffect::save(KoXmlWriter &writer)
-{
- writer.startElement(MorphologyEffectId);
-
- saveCommonAttributes(writer);
-
- if (m_operator != Erode) {
- writer.addAttribute("operator", "dilate");
- }
- if (!m_radius.isNull()) {
- if (m_radius.x() == m_radius.y()) {
- writer.addAttribute("radius", QString("%1").arg(m_radius.x()));
- } else {
- writer.addAttribute("radius", QString("%1 %2").arg(m_radius.x()).arg(m_radius.y()));
- }
- }
-
- writer.endElement();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/MorphologyEffect.h b/plugins/tools/karbonplugins/filtereffects/MorphologyEffect.h
deleted file mode 100644
index 9d91520275..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MorphologyEffect.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* 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.
- */
-
-#ifndef MORPHOLOGYEFFECT_H
-#define MORPHOLOGYEFFECT_H
-
-#include "KoFilterEffect.h"
-#include <QPointF>
-
-#define MorphologyEffectId "feMorphology"
-
-class KoFilterEffectLoadingContext;
-
-/// A morphology filter effect
-class MorphologyEffect : public KoFilterEffect
-{
-public:
- /// Morphology operator type
- enum Operator {
- Erode,
- Dilate
- };
-
- MorphologyEffect();
-
- /// Returns the morphology radius
- QPointF morphologyRadius() const;
-
- /// Sets the morphology radius
- void setMorphologyRadius(const QPointF &radius);
-
- /// Returns the morphology operator
- Operator morphologyOperator() const;
-
- /// Sets the morphology operator
- void setMorphologyOperator(Operator op);
-
- /// reimplemented from KoFilterEffect
- QImage processImage(const QImage &image, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- bool load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context) override;
- /// reimplemented from KoFilterEffect
- void save(KoXmlWriter &writer) override;
-
-private:
- QPointF m_radius;
- Operator m_operator;
-};
-
-#endif // MORPHOLOGYEFFECT_H
diff --git a/plugins/tools/karbonplugins/filtereffects/MorphologyEffectConfigWidget.cpp b/plugins/tools/karbonplugins/filtereffects/MorphologyEffectConfigWidget.cpp
deleted file mode 100644
index 242a5ff688..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MorphologyEffectConfigWidget.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/* 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 "MorphologyEffectConfigWidget.h"
-#include "MorphologyEffect.h"
-#include "KoFilterEffect.h"
-
-#include <QSpinBox>
-#include <klocalizedstring.h>
-
-#include <QGridLayout>
-#include <QLabel>
-#include <QRadioButton>
-#include <QButtonGroup>
-
-#include "kis_double_parse_spin_box.h"
-
-MorphologyEffectConfigWidget::MorphologyEffectConfigWidget(QWidget *parent)
- : KoFilterEffectConfigWidgetBase(parent)
- , m_effect(0)
-{
- QGridLayout *g = new QGridLayout(this);
-
- m_operator = new QButtonGroup(this);
- QRadioButton *erode = new QRadioButton(i18n("Erode"), this);
- QRadioButton *dilate = new QRadioButton(i18n("Dilate"), this);
- m_operator->addButton(erode, MorphologyEffect::Erode);
- m_operator->addButton(dilate, MorphologyEffect::Dilate);
- g->addWidget(new QLabel(i18n("Operator:"), this), 0, 0);
- g->addWidget(erode, 0, 1);
- g->addWidget(dilate, 0, 2);
-
- g->addWidget(new QLabel(i18n("Radius x:"), this), 1, 0);
- m_radiusX = new KisDoubleParseSpinBox(this);
- m_radiusX->setRange(0.0, 100);
- m_radiusX->setSingleStep(0.5);
- g->addWidget(m_radiusX, 1, 1, 1, 2);
-
- g->addWidget(new QLabel(i18n("Radius y:"), this), 2, 0);
- m_radiusY = new KisDoubleParseSpinBox(this);
- m_radiusY->setRange(0.0, 100);
- m_radiusY->setSingleStep(0.5);
- g->addWidget(m_radiusY, 2, 1, 1, 2);
-
- setLayout(g);
-
- connect(m_operator, SIGNAL(buttonClicked(int)), this, SLOT(operatorChanged(int)));
- connect(m_radiusX, SIGNAL(valueChanged(double)), this, SLOT(radiusXChanged(double)));
- connect(m_radiusY, SIGNAL(valueChanged(double)), this, SLOT(radiusYChanged(double)));
-}
-
-bool MorphologyEffectConfigWidget::editFilterEffect(KoFilterEffect *filterEffect)
-{
- m_effect = dynamic_cast<MorphologyEffect *>(filterEffect);
- if (!m_effect) {
- return false;
- }
-
- m_operator->blockSignals(true);
- m_operator->button(m_effect->morphologyOperator())->setChecked(true);
- m_operator->blockSignals(false);
- m_radiusX->blockSignals(true);
- m_radiusX->setValue(m_effect->morphologyRadius().x() * 100);
- m_radiusX->blockSignals(false);
- m_radiusY->blockSignals(true);
- m_radiusY->setValue(m_effect->morphologyRadius().y() * 100);
- m_radiusY->blockSignals(false);
-
- return true;
-}
-
-void MorphologyEffectConfigWidget::operatorChanged(int id)
-{
- if (!m_effect) {
- return;
- }
-
- switch (id) {
- case MorphologyEffect::Erode:
- m_effect->setMorphologyOperator(MorphologyEffect::Erode);
- break;
- case MorphologyEffect::Dilate:
- m_effect->setMorphologyOperator(MorphologyEffect::Dilate);
- break;
- }
- emit filterChanged();
-}
-
-void MorphologyEffectConfigWidget::radiusXChanged(double x)
-{
- if (!m_effect) {
- return;
- }
-
- QPointF radius = m_effect->morphologyRadius();
- if (radius.x() != x) {
- m_effect->setMorphologyRadius(QPointF(x * 0.01, radius.y()));
- }
-
- emit filterChanged();
-}
-
-void MorphologyEffectConfigWidget::radiusYChanged(double y)
-{
- if (!m_effect) {
- return;
- }
-
- QPointF radius = m_effect->morphologyRadius();
- if (radius.y() != y) {
- m_effect->setMorphologyRadius(QPointF(radius.x(), y * 0.01));
- }
-
- emit filterChanged();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/MorphologyEffectConfigWidget.h b/plugins/tools/karbonplugins/filtereffects/MorphologyEffectConfigWidget.h
deleted file mode 100644
index f0b68afcf7..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MorphologyEffectConfigWidget.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* 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.
- */
-
-#ifndef MORPHOLOGYEFFECTCONFIGWIDGET_H
-#define MORPHOLOGYEFFECTCONFIGWIDGET_H
-
-#include "KoFilterEffectConfigWidgetBase.h"
-
-class KoFilterEffect;
-class MorphologyEffect;
-class QDoubleSpinBox;
-class QButtonGroup;
-
-class MorphologyEffectConfigWidget : public KoFilterEffectConfigWidgetBase
-{
- Q_OBJECT
-public:
- explicit MorphologyEffectConfigWidget(QWidget *parent = 0);
-
- /// reimplemented from KoFilterEffectConfigWidgetBase
- bool editFilterEffect(KoFilterEffect *filterEffect) override;
-
-private Q_SLOTS:
- void radiusXChanged(double x);
- void radiusYChanged(double y);
- void operatorChanged(int op);
-private:
- MorphologyEffect *m_effect;
- QButtonGroup *m_operator;
- QDoubleSpinBox *m_radiusX;
- QDoubleSpinBox *m_radiusY;
-};
-
-#endif // MORPHOLOGYEFFECTCONFIGWIDGET_H
diff --git a/plugins/tools/karbonplugins/filtereffects/MorphologyEffectFactory.cpp b/plugins/tools/karbonplugins/filtereffects/MorphologyEffectFactory.cpp
deleted file mode 100644
index 5ed3d9b3a5..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MorphologyEffectFactory.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/* 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 "MorphologyEffectFactory.h"
-#include "MorphologyEffect.h"
-#include "MorphologyEffectConfigWidget.h"
-
-#include <klocalizedstring.h>
-
-MorphologyEffectFactory::MorphologyEffectFactory()
- : KoFilterEffectFactoryBase(MorphologyEffectId, i18n("Morphology"))
-{
-}
-
-KoFilterEffect *MorphologyEffectFactory::createFilterEffect() const
-{
- return new MorphologyEffect();
-}
-
-KoFilterEffectConfigWidgetBase *MorphologyEffectFactory::createConfigWidget() const
-{
- return new MorphologyEffectConfigWidget();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/MorphologyEffectFactory.h b/plugins/tools/karbonplugins/filtereffects/MorphologyEffectFactory.h
deleted file mode 100644
index 1706120062..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/MorphologyEffectFactory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* 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.
- */
-
-#ifndef MORPHOLOGYEFFECTFACTORY_H
-#define MORPHOLOGYEFFECTFACTORY_H
-
-#include "KoFilterEffectFactoryBase.h"
-
-class KoFilterEffect;
-
-class MorphologyEffectFactory : public KoFilterEffectFactoryBase
-{
-public:
- MorphologyEffectFactory();
- KoFilterEffect *createFilterEffect() const override;
- KoFilterEffectConfigWidgetBase *createConfigWidget() const override;
-};
-
-#endif // MORPHOLOGYEFFECTFACTORY_H
diff --git a/plugins/tools/karbonplugins/filtereffects/OffsetEffect.cpp b/plugins/tools/karbonplugins/filtereffects/OffsetEffect.cpp
deleted file mode 100644
index 87c0329ad4..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/OffsetEffect.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/* 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.
- */
-
-#include "OffsetEffect.h"
-#include "KoFilterEffectRenderContext.h"
-#include "KoFilterEffectLoadingContext.h"
-#include "KoViewConverter.h"
-#include "KoXmlWriter.h"
-#include "KoXmlReader.h"
-#include <klocalizedstring.h>
-#include <QPainter>
-
-OffsetEffect::OffsetEffect()
- : KoFilterEffect(OffsetEffectId, i18n("Offset"))
- , m_offset(0, 0)
-{
-}
-
-QPointF OffsetEffect::offset() const
-{
- return m_offset;
-}
-
-void OffsetEffect::setOffset(const QPointF &offset)
-{
- m_offset = offset;
-}
-
-QImage OffsetEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const
-{
- if (m_offset.x() == 0.0 && m_offset.y() == 0.0) {
- return image;
- }
-
- // transform from bounding box coordinates
- QPointF offset = context.toUserSpace(m_offset);
- // transform to view coordinates
- offset = context.viewConverter()->documentToView(offset);
-
- QImage result(image.size(), image.format());
- result.fill(qRgba(0, 0, 0, 0));
-
- QPainter p(&result);
- p.drawImage(context.filterRegion().topLeft() + offset, image, context.filterRegion());
- return result;
-}
-
-bool OffsetEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context)
-{
- if (element.tagName() != id()) {
- return false;
- }
-
- if (element.hasAttribute("dx")) {
- m_offset.rx() = element.attribute("dx").toDouble();
- }
- if (element.hasAttribute("dy")) {
- m_offset.ry() = element.attribute("dy").toDouble();
- }
-
- m_offset = context.convertFilterPrimitiveUnits(m_offset);
-
- return true;
-}
-
-void OffsetEffect::save(KoXmlWriter &writer)
-{
- writer.startElement(OffsetEffectId);
-
- saveCommonAttributes(writer);
-
- if (m_offset.x() != 0.0) {
- writer.addAttribute("dx", m_offset.x());
- }
- if (m_offset.y() != 0.0) {
- writer.addAttribute("dy", m_offset.x());
- }
-
- writer.endElement();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/OffsetEffect.h b/plugins/tools/karbonplugins/filtereffects/OffsetEffect.h
deleted file mode 100644
index 9543ade734..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/OffsetEffect.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* 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 OFFSETEFFECT_H
-#define OFFSETEFFECT_H
-
-#include "KoFilterEffect.h"
-#include <QPointF>
-
-#define OffsetEffectId "feOffset"
-
-/// An image offset effect
-class OffsetEffect : public KoFilterEffect
-{
-public:
- OffsetEffect();
-
- QPointF offset() const;
- void setOffset(const QPointF &offset);
-
- /// reimplemented from KoFilterEffect
- QImage processImage(const QImage &image, const KoFilterEffectRenderContext &context) const override;
- /// reimplemented from KoFilterEffect
- bool load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context) override;
- /// reimplemented from KoFilterEffect
- void save(KoXmlWriter &writer) override;
-
-private:
- QPointF m_offset;
-};
-
-#endif // OFFSETEFFECT_H
diff --git a/plugins/tools/karbonplugins/filtereffects/OffsetEffectConfigWidget.cpp b/plugins/tools/karbonplugins/filtereffects/OffsetEffectConfigWidget.cpp
deleted file mode 100644
index 5c60535ec8..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/OffsetEffectConfigWidget.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/* 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.
- */
-
-#include "OffsetEffectConfigWidget.h"
-#include "OffsetEffect.h"
-#include "KoFilterEffect.h"
-#include <QSpinBox>
-#include <klocalizedstring.h>
-#include <QGridLayout>
-#include <QLabel>
-
-#include "kis_double_parse_spin_box.h"
-
-const qreal OffsetLimit = 100.0;
-
-OffsetEffectConfigWidget::OffsetEffectConfigWidget(QWidget *parent)
- : KoFilterEffectConfigWidgetBase(parent)
- , m_effect(0)
-{
- QGridLayout *g = new QGridLayout(this);
-
- g->addWidget(new QLabel(i18n("dx"), this), 0, 0);
- m_offsetX = new KisDoubleParseSpinBox(this);
- m_offsetX->setRange(-OffsetLimit, OffsetLimit);
- m_offsetX->setSingleStep(1.0);
- g->addWidget(m_offsetX, 0, 1);
-
- g->addWidget(new QLabel(i18n("dy"), this), 0, 2);
- m_offsetY = new KisDoubleParseSpinBox(this);
- m_offsetY->setRange(-OffsetLimit, OffsetLimit);
- m_offsetY->setSingleStep(1.0);
- g->addWidget(m_offsetY, 0, 3);
- setLayout(g);
-
- connect(m_offsetX, SIGNAL(valueChanged(double)), this, SLOT(offsetChanged(double)));
- connect(m_offsetY, SIGNAL(valueChanged(double)), this, SLOT(offsetChanged(double)));
-}
-
-bool OffsetEffectConfigWidget::editFilterEffect(KoFilterEffect *filterEffect)
-{
- m_effect = dynamic_cast<OffsetEffect *>(filterEffect);
- if (!m_effect) {
- return false;
- }
-
- m_offsetX->blockSignals(true);
- m_offsetY->blockSignals(true);
- m_offsetX->setValue(m_effect->offset().x() * 100.0);
- m_offsetY->setValue(m_effect->offset().y() * 100.0);
- m_offsetX->blockSignals(false);
- m_offsetY->blockSignals(false);
-
- return true;
-}
-
-void OffsetEffectConfigWidget::offsetChanged(double /*offset*/)
-{
- if (!m_effect) {
- return;
- }
-
- m_effect->setOffset(0.01 * QPointF(m_offsetX->value(), m_offsetY->value()));
- emit filterChanged();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/OffsetEffectConfigWidget.h b/plugins/tools/karbonplugins/filtereffects/OffsetEffectConfigWidget.h
deleted file mode 100644
index 9bd17de7a3..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/OffsetEffectConfigWidget.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* 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 OFFSETEFFECTCONFIGWIDGET_H
-#define OFFSETEFFECTCONFIGWIDGET_H
-
-#include "KoFilterEffectConfigWidgetBase.h"
-
-class KoFilterEffect;
-class OffsetEffect;
-class QDoubleSpinBox;
-
-class OffsetEffectConfigWidget : public KoFilterEffectConfigWidgetBase
-{
- Q_OBJECT
-public:
- explicit OffsetEffectConfigWidget(QWidget *parent = 0);
-
- /// reimplemented from KoFilterEffectConfigWidgetBase
- bool editFilterEffect(KoFilterEffect *filterEffect) override;
-
-private Q_SLOTS:
- void offsetChanged(double offset);
-
-private:
- OffsetEffect *m_effect;
- QDoubleSpinBox *m_offsetX;
- QDoubleSpinBox *m_offsetY;
-};
-
-#endif // OFFSETEFFECTCONFIGWIDGET_H
diff --git a/plugins/tools/karbonplugins/filtereffects/OffsetEffectFactory.cpp b/plugins/tools/karbonplugins/filtereffects/OffsetEffectFactory.cpp
deleted file mode 100644
index cb438a6a1c..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/OffsetEffectFactory.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/* 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.
- */
-
-#include "OffsetEffectFactory.h"
-#include "OffsetEffect.h"
-#include "OffsetEffectConfigWidget.h"
-#include <klocalizedstring.h>
-
-OffsetEffectFactory::OffsetEffectFactory()
- : KoFilterEffectFactoryBase(OffsetEffectId, i18n("Offset"))
-{
-}
-
-KoFilterEffect *OffsetEffectFactory::createFilterEffect() const
-{
- return new OffsetEffect();
-}
-
-KoFilterEffectConfigWidgetBase *OffsetEffectFactory::createConfigWidget() const
-{
- return new OffsetEffectConfigWidget();
-}
diff --git a/plugins/tools/karbonplugins/filtereffects/OffsetEffectFactory.h b/plugins/tools/karbonplugins/filtereffects/OffsetEffectFactory.h
deleted file mode 100644
index 679c9a56c2..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/OffsetEffectFactory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* 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 OFFSETEFFECTFACTORY_H
-#define OFFSETEFFECTFACTORY_H
-
-#include "KoFilterEffectFactoryBase.h"
-
-class KoFilterEffect;
-
-class OffsetEffectFactory : public KoFilterEffectFactoryBase
-{
-public:
- OffsetEffectFactory();
- KoFilterEffect *createFilterEffect() const override;
- KoFilterEffectConfigWidgetBase *createConfigWidget() const override;
-};
-
-#endif // OFFSETEFFECTFACTORY_H
diff --git a/plugins/tools/karbonplugins/filtereffects/karbon_filtereffects.json b/plugins/tools/karbonplugins/filtereffects/karbon_filtereffects.json
deleted file mode 100644
index 0cb19b12f5..0000000000
--- a/plugins/tools/karbonplugins/filtereffects/karbon_filtereffects.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Icon": "",
- "Id": "Filter Effects",
-
- "Type": "Service",
- "X-Flake-MinVersion": "28",
- "X-Flake-PluginVersion": "28",
- "X-KDE-Library": "krita_filtereffects",
- "X-KDE-ServiceTypes": [
- "Calligra/FilterEffect"
- ]
-}
diff --git a/plugins/tools/karbonplugins/tools/22-actions-pattern.png b/plugins/tools/karbonplugins/tools/22-actions-pattern.png
deleted file mode 100644
index a5665e618d..0000000000
Binary files a/plugins/tools/karbonplugins/tools/22-actions-pattern.png and /dev/null differ
diff --git a/plugins/tools/karbonplugins/tools/32-actions-tool_imageeffects.png b/plugins/tools/karbonplugins/tools/32-actions-tool_imageeffects.png
deleted file mode 100644
index 238eb9c65a..0000000000
Binary files a/plugins/tools/karbonplugins/tools/32-actions-tool_imageeffects.png and /dev/null differ
diff --git a/plugins/tools/karbonplugins/tools/CMakeLists.txt b/plugins/tools/karbonplugins/tools/CMakeLists.txt
index 277596400a..d11c693f3b 100644
--- a/plugins/tools/karbonplugins/tools/CMakeLists.txt
+++ b/plugins/tools/karbonplugins/tools/CMakeLists.txt
@@ -1,49 +1,20 @@
-include_directories(
- ${CMAKE_CURRENT_SOURCE_DIR}/filterEffectTool
-)
-
-########### next target ###############
-
set(karbon_tools_SOURCES
KarbonToolsPlugin.cpp
KarbonCursor.cpp
CalligraphyTool/KarbonCalligraphyTool.cpp
CalligraphyTool/KarbonCalligraphyOptionWidget.cpp
CalligraphyTool/KarbonCalligraphyToolFactory.cpp
CalligraphyTool/KarbonCalligraphicShape.cpp
CalligraphyTool/KarbonCalligraphicShapeFactory.cpp
CalligraphyTool/KarbonSimplifyPath.cpp
- KarbonPatternTool.cpp
- KarbonPatternToolFactory.cpp
- KarbonPatternEditStrategy.cpp
- filterEffectTool/KarbonFilterEffectsTool.cpp
- filterEffectTool/KarbonFilterEffectsToolFactory.cpp
- filterEffectTool/FilterEffectEditWidget.cpp
- filterEffectTool/FilterEffectScene.cpp
- filterEffectTool/FilterEffectSceneItems.cpp
- filterEffectTool/FilterInputChangeCommand.cpp
- filterEffectTool/FilterAddCommand.cpp
- filterEffectTool/FilterRemoveCommand.cpp
- filterEffectTool/FilterStackSetCommand.cpp
- filterEffectTool/FilterRegionChangeCommand.cpp
- filterEffectTool/FilterEffectResource.cpp
- filterEffectTool/FilterResourceServerProvider.cpp
- filterEffectTool/FilterRegionEditStrategy.cpp
- filterEffectTool/KoResourceSelector.cpp
- KarbonPatternOptionsWidget.cpp
-)
-
-ki18n_wrap_ui(karbon_tools_SOURCES
- filterEffectTool/FilterEffectEditWidget.ui
- KarbonPatternOptionsWidget.ui
)
qt5_add_resources(karbon_tools_SOURCES karbontools.qrc)
add_library(krita_karbontools MODULE ${karbon_tools_SOURCES})
target_link_libraries(krita_karbontools kritaui kritawidgets KF5::Completion)
install(TARGETS krita_karbontools DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install(FILES CalligraphyTool/KarbonCalligraphyTool.action DESTINATION ${DATA_INSTALL_DIR}/krita/actions)
diff --git a/plugins/tools/karbonplugins/tools/KarbonPatternEditStrategy.cpp b/plugins/tools/karbonplugins/tools/KarbonPatternEditStrategy.cpp
deleted file mode 100644
index f9d8233f68..0000000000
--- a/plugins/tools/karbonplugins/tools/KarbonPatternEditStrategy.cpp
+++ /dev/null
@@ -1,389 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2007,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.
- */
-
-#include "KarbonPatternEditStrategy.h"
-
-#include <KoShape.h>
-#include <KoViewConverter.h>
-#include <KoShapeBackgroundCommand.h>
-
-#include <QPainter>
-#include <kundo2command.h>
-
-#include <math.h>
-
-uint KarbonPatternEditStrategyBase::m_handleRadius = 3;
-uint KarbonPatternEditStrategyBase::m_grabSensitivity = 3;
-
-KarbonPatternEditStrategyBase::KarbonPatternEditStrategyBase(KoShape *s, KoImageCollection *imageCollection)
- : m_selectedHandle(-1)
- , m_oldFill(new KoPatternBackground(imageCollection))
- , m_newFill(new KoPatternBackground(imageCollection))
- , m_shape(s)
- , m_imageCollection(imageCollection)
- , m_editing(false)
- , m_modified(false)
-{
- Q_ASSERT(m_shape);
- Q_ASSERT(imageCollection);
- // cache the shapes transformation matrix
- m_matrix = shape()->absoluteTransformation();
-}
-
-KarbonPatternEditStrategyBase::~KarbonPatternEditStrategyBase()
-{
-}
-
-void KarbonPatternEditStrategyBase::setEditing(bool on)
-{
- m_editing = on;
- // if we are going into editing mode, save the old background
- // for use inside the command emitted when finished
- if (on) {
- m_modified = false;
- QSharedPointer<KoPatternBackground> fill = qSharedPointerDynamicCast<KoPatternBackground>(m_shape->background());
- if (fill) {
- m_oldFill = fill;
- }
- }
-}
-
-void KarbonPatternEditStrategyBase::setModified()
-{
- m_modified = true;
-}
-
-bool KarbonPatternEditStrategyBase::isModified() const
-{
- return m_modified;
-}
-
-KUndo2Command *KarbonPatternEditStrategyBase::createCommand()
-{
- QSharedPointer<KoPatternBackground> fill = qSharedPointerDynamicCast<KoPatternBackground>(m_shape->background());
- if (fill && isModified()) {
- fill = m_oldFill;
- QSharedPointer<KoPatternBackground> newFill(new KoPatternBackground(m_imageCollection));
- newFill = m_newFill;
- return new KoShapeBackgroundCommand(m_shape, newFill, 0);
- }
- return 0;
-}
-
-void KarbonPatternEditStrategyBase::paintHandle(QPainter &painter, const KoViewConverter &converter, const QPointF &position) const
-{
- QRectF handleRect = converter.viewToDocument(QRectF(0, 0, 2 * m_handleRadius, 2 * m_handleRadius));
- handleRect.moveCenter(position);
- painter.drawRect(handleRect);
-}
-
-bool KarbonPatternEditStrategyBase::mouseInsideHandle(const QPointF &mousePos, const QPointF &handlePos, const KoViewConverter &converter) const
-{
- qreal grabSensitivityInPt = converter.viewToDocumentX(m_grabSensitivity);
- if (mousePos.x() < handlePos.x() - grabSensitivityInPt) {
- return false;
- }
- if (mousePos.x() > handlePos.x() + grabSensitivityInPt) {
- return false;
- }
- if (mousePos.y() < handlePos.y() - grabSensitivityInPt) {
- return false;
- }
- if (mousePos.y() > handlePos.y() + grabSensitivityInPt) {
- return false;
- }
- return true;
-}
-
-void KarbonPatternEditStrategyBase::repaint() const
-{
- m_shape->update();
-}
-
-KoShape *KarbonPatternEditStrategyBase::shape() const
-{
- return m_shape;
-}
-
-KoImageCollection *KarbonPatternEditStrategyBase::imageCollection()
-{
- return m_imageCollection;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-KarbonPatternEditStrategy::KarbonPatternEditStrategy(KoShape *s, KoImageCollection *imageCollection)
- : KarbonPatternEditStrategyBase(s, imageCollection)
-{
- // cache the shapes transformation matrix
- m_matrix = shape()->absoluteTransformation();
- QSizeF size = shape()->size();
- // the fixed length of half the average shape dimension
- m_normalizedLength = 0.25 * (size.width() + size.height());
- // get the brush transformation matrix
- QTransform brushMatrix;
- QSharedPointer<KoPatternBackground> fill = qSharedPointerDynamicCast<KoPatternBackground>(shape()->background());
- if (fill) {
- brushMatrix = fill->transform();
- }
-
- // the center handle at the center point of the shape
- //m_origin = QPointF( 0.5 * size.width(), 0.5 * size.height() );
- m_handles.append(brushMatrix.map(QPointF()));
- // the direction handle with the length of half the average shape dimension
- QPointF dirVec = QPointF(m_normalizedLength, 0.0);
- m_handles.append(brushMatrix.map(dirVec));
-}
-
-KarbonPatternEditStrategy::~KarbonPatternEditStrategy()
-{
-}
-
-void KarbonPatternEditStrategy::paint(QPainter &painter, const KoViewConverter &converter) const
-{
- QPointF centerPoint = m_matrix.map(m_origin + m_handles[center]);
- QPointF directionPoint = m_matrix.map(m_origin + m_handles[direction]);
-
- painter.drawLine(centerPoint, directionPoint);
- paintHandle(painter, converter, centerPoint);
- paintHandle(painter, converter, directionPoint);
-}
-
-bool KarbonPatternEditStrategy::selectHandle(const QPointF &mousePos, const KoViewConverter &converter)
-{
- int handleIndex = 0;
- Q_FOREACH (const QPointF &handle, m_handles) {
- if (mouseInsideHandle(mousePos, m_matrix.map(m_origin + handle), converter)) {
- m_selectedHandle = handleIndex;
- return true;
- }
- handleIndex++;
- }
- m_selectedHandle = -1;
- return false;
-}
-
-void KarbonPatternEditStrategy::handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers)
-{
- Q_UNUSED(modifiers)
-
- if (m_selectedHandle == direction) {
- QPointF newPos = m_matrix.inverted().map(mouseLocation) - m_origin - m_handles[center];
- // calculate the temporary length after handle movement
- qreal newLength = sqrt(newPos.x() * newPos.x() + newPos.y() * newPos.y());
- // set the new direction vector with the new direction and normalized length
- m_handles[m_selectedHandle] = m_handles[center] + m_normalizedLength / newLength * newPos;
- } else if (m_selectedHandle == center) {
- QPointF diffPos = m_matrix.inverted().map(mouseLocation) - m_origin - m_handles[center];
- m_handles[center] += diffPos;
- m_handles[direction] += diffPos;
- } else {
- return;
- }
-
- setModified();
-
- QSharedPointer<KoPatternBackground> fill = qSharedPointerDynamicCast<KoPatternBackground>(shape()->background());
- if (fill) {
- m_newFill = updatedBackground();
- fill = m_newFill;
- }
-}
-
-QRectF KarbonPatternEditStrategy::boundingRect() const
-{
- // calculate the bounding rect of the handles
- QRectF bbox(m_matrix.map(m_origin + m_handles[0]), QSize(0, 0));
- for (int i = 1; i < m_handles.count(); ++i) {
- QPointF handle = m_matrix.map(m_origin + m_handles[i]);
- bbox.setLeft(qMin(handle.x(), bbox.left()));
- bbox.setRight(qMax(handle.x(), bbox.right()));
- bbox.setTop(qMin(handle.y(), bbox.top()));
- bbox.setBottom(qMax(handle.y(), bbox.bottom()));
- }
- qreal hr = handleRadius();
- return bbox.adjusted(-hr, -hr, hr, hr);
-}
-
-QSharedPointer<KoPatternBackground> KarbonPatternEditStrategy::updatedBackground()
-{
- // the direction vector controls the rotation of the pattern
- QPointF dirVec = m_handles[direction] - m_handles[center];
- qreal angle = atan2(dirVec.y(), dirVec.x()) * 180.0 / M_PI;
- QTransform matrix;
- // the center handle controls the translation
- matrix.translate(m_handles[center].x(), m_handles[center].y());
- matrix.rotate(angle);
-
- QSharedPointer<KoPatternBackground> newFill(new KoPatternBackground(imageCollection()));
- newFill->setTransform(matrix);
-
- return newFill;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-KarbonOdfPatternEditStrategy::KarbonOdfPatternEditStrategy(KoShape *s, KoImageCollection *imageCollection)
- : KarbonPatternEditStrategyBase(s, imageCollection)
-{
- m_handles.append(QPointF());
- m_handles.append(QPointF());
- updateHandles(qSharedPointerDynamicCast<KoPatternBackground>(shape()->background()));
-}
-
-KarbonOdfPatternEditStrategy::~KarbonOdfPatternEditStrategy()
-{
-}
-
-void KarbonOdfPatternEditStrategy::paint(QPainter &painter, const KoViewConverter &converter) const
-{
- QSharedPointer<KoPatternBackground> fill = qSharedPointerDynamicCast<KoPatternBackground>(shape()->background());
- if (!fill) {
- return;
- }
-
- painter.save();
- painter.setTransform(m_matrix * painter.transform());
- painter.setBrush(Qt::NoBrush);
- painter.drawRect(QRectF(m_handles[origin], m_handles[size]));
- painter.restore();
-
- if (fill->repeat() == KoPatternBackground::Tiled) {
- paintHandle(painter, converter, m_matrix.map(m_handles[origin]));
- }
- if (fill->repeat() != KoPatternBackground::Stretched) {
- paintHandle(painter, converter, m_matrix.map(m_handles[size]));
- }
-
-}
-
-bool KarbonOdfPatternEditStrategy::selectHandle(const QPointF &mousePos, const KoViewConverter &converter)
-{
- QSharedPointer<KoPatternBackground> fill = qSharedPointerDynamicCast<KoPatternBackground>(shape()->background());
- if (!fill) {
- return false;
- }
-
- if (fill->repeat() == KoPatternBackground::Stretched) {
- return false;
- }
-
- m_selectedHandle = -1;
-
- if (mouseInsideHandle(mousePos, m_matrix.map(m_handles[size]), converter)) {
- m_selectedHandle = size;
- return true;
- }
-
- if (fill->repeat() == KoPatternBackground::Original) {
- return false;
- }
-
- if (mouseInsideHandle(mousePos, m_matrix.map(m_handles[origin]), converter)) {
- m_selectedHandle = origin;
- return true;
- }
-
- return false;
-}
-
-void KarbonOdfPatternEditStrategy::handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers)
-{
- Q_UNUSED(modifiers);
-
- QSharedPointer<KoPatternBackground> fill = qSharedPointerDynamicCast<KoPatternBackground>(shape()->background());
- if (!fill) {
- return;
- }
-
- if (fill->repeat() == KoPatternBackground::Stretched) {
- return;
- }
-
- if (m_selectedHandle == origin) {
- if (fill->repeat() == KoPatternBackground::Original) {
- return;
- }
-
- QPointF diffPos = m_matrix.inverted().map(mouseLocation) - m_handles[origin];
- m_handles[origin] += diffPos;
- m_handles[size] += diffPos;
- } else if (m_selectedHandle == size) {
- QPointF newPos = m_matrix.inverted().map(mouseLocation);
- newPos.setX(qMax(newPos.x(), m_handles[origin].x()));
- newPos.setY(qMax(newPos.y(), m_handles[origin].y()));
- if (fill->repeat() == KoPatternBackground::Original) {
- QPointF diffPos = newPos - m_handles[size];
- m_handles[size] += 0.5 * diffPos;
- m_handles[origin] -= 0.5 * diffPos;
- } else {
- m_handles[size] = newPos;
- }
- } else {
- return;
- }
-
- setModified();
-
- m_newFill = updatedBackground();
- updateHandles(m_newFill);
-}
-
-QRectF KarbonOdfPatternEditStrategy::boundingRect() const
-{
- // calculate the bounding rect of the handles
- QRectF bbox(m_matrix.map(m_handles[origin]), m_matrix.map(m_handles[size]));
- qreal hr = handleRadius();
- return bbox.adjusted(-hr, -hr, hr, hr);
-}
-
-QSharedPointer<KoPatternBackground> KarbonOdfPatternEditStrategy::updatedBackground()
-{
- QSizeF displaySize(m_handles[size].x() - m_handles[origin].x(), m_handles[size].y() - m_handles[origin].y());
- qreal offsetX = 100.0 * (m_handles[origin].x() / displaySize.width());
- qreal offsetY = 100.0 * (m_handles[origin].y() / displaySize.height());
-
- QSharedPointer<KoPatternBackground> newFill(new KoPatternBackground(imageCollection()));
- newFill = m_oldFill;
- newFill->setReferencePoint(KoPatternBackground::TopLeft);
- newFill->setReferencePointOffset(QPointF(offsetX, offsetY));
- newFill->setPatternDisplaySize(displaySize);
-
- return newFill;
-}
-
-void KarbonOdfPatternEditStrategy::updateHandles(QSharedPointer<KoPatternBackground> fill)
-{
- if (!fill) {
- return;
- }
-
- QRectF patternRect = fill->patternRectFromFillSize(shape()->size());
- m_handles[origin] = patternRect.topLeft();
- m_handles[size] = patternRect.bottomRight();
-}
-
-void KarbonOdfPatternEditStrategy::updateHandles()
-{
- updateHandles(qSharedPointerDynamicCast<KoPatternBackground>(shape()->background()));
-}
diff --git a/plugins/tools/karbonplugins/tools/KarbonPatternEditStrategy.h b/plugins/tools/karbonplugins/tools/KarbonPatternEditStrategy.h
deleted file mode 100644
index 5b79d3e336..0000000000
--- a/plugins/tools/karbonplugins/tools/KarbonPatternEditStrategy.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2007,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 _KARBONPATTERNEDITSTRATEGY_H_
-#define _KARBONPATTERNEDITSTRATEGY_H_
-
-#include <KoPatternBackground.h>
-#include <QBrush>
-#include <QSharedPointer>
-
-class KoShape;
-class KoViewConverter;
-class KoImageCollection;
-
-class QPainter;
-class KUndo2Command;
-
-/// The class used for editing a shapes pattern
-class KarbonPatternEditStrategyBase
-{
-public:
- /// constructs an edit strategy working on the given shape
- explicit KarbonPatternEditStrategyBase(KoShape *shape, KoImageCollection *imageCollection);
-
- /// destroy the edit strategy
- virtual ~KarbonPatternEditStrategyBase();
-
- /// painting of the pattern editing handles
- virtual void paint(QPainter &painter, const KoViewConverter &converter) const = 0;
-
- /// selects handle at the given position
- virtual bool selectHandle(const QPointF &mousePos, const KoViewConverter &converter) = 0;
-
- /// mouse position handling for moving handles
- virtual void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) = 0;
-
- /// sets the strategy into editing mode
- void setEditing(bool on);
-
- /// checks if strategy is in editing mode
- bool isEditing() const
- {
- return m_editing;
- }
-
- /// create the command for changing the shapes background
- KUndo2Command *createCommand();
-
- /// schedules a repaint of the shape and gradient handles
- void repaint() const;
-
- /// returns the pattern handles bounding rect
- virtual QRectF boundingRect() const = 0;
-
- /// returns the actual background brush
- virtual QSharedPointer<KoPatternBackground> updatedBackground() = 0;
-
- /// Returns the shape we are working on
- KoShape *shape() const;
-
- /// sets the handle radius in pixel used for painting the handles
- static void setHandleRadius(uint radius)
- {
- m_handleRadius = radius;
- }
-
- /// returns the actual handle radius in pixel
- static uint handleRadius()
- {
- return m_handleRadius;
- }
-
- /// sets the grab sensitivity in pixel used for grabbing the handles
- static void setGrabSensitivity(uint grabSensitivity)
- {
- m_grabSensitivity = grabSensitivity;
- }
-
- /// returns the actual grab sensitivity in pixel
- static uint grabSensitivity()
- {
- return m_grabSensitivity;
- }
-
- virtual void updateHandles() {}
-
-protected:
- /// Returns the image collectio used to create new pattern background
- KoImageCollection *imageCollection();
-
- /// Flags the background as modified
- void setModified();
-
- /// Returns if background is modified
- bool isModified() const;
-
- /// paints a single handle
- void paintHandle(QPainter &painter, const KoViewConverter &converter, const QPointF &position) const;
-
- /// checks if mouse position is inside handle rect
- bool mouseInsideHandle(const QPointF &mousePos, const QPointF &handlePos, const KoViewConverter &converter) const;
-
- QList<QPointF> m_handles; ///< the list of handles
- int m_selectedHandle; ///< index of currently deleted handle or -1 if none selected
- QSharedPointer<KoPatternBackground> m_oldFill;
- QSharedPointer<KoPatternBackground> m_newFill;
- QTransform m_matrix; ///< matrix to map handle into document coordinate system
-
-private:
-
- static uint m_handleRadius; ///< the handle radius for all gradient strategies
- static uint m_grabSensitivity; ///< the grab sensitivity
- KoShape *m_shape; ///< the shape we are working on
- KoImageCollection *m_imageCollection;
- bool m_editing; ///< the edit mode flag
- bool m_modified; ///< indicated if background was modified
-};
-
-/// The class used for editing a shapes pattern
-class KarbonPatternEditStrategy : public KarbonPatternEditStrategyBase
-{
-public:
- explicit KarbonPatternEditStrategy(KoShape *shape, KoImageCollection *imageCollection);
- ~KarbonPatternEditStrategy() override;
- void paint(QPainter &painter, const KoViewConverter &converter) const override;
- bool selectHandle(const QPointF &mousePos, const KoViewConverter &converter) override;
- void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) override;
- QRectF boundingRect() const override;
- QSharedPointer<KoPatternBackground> updatedBackground() override;
-
-private:
-
- enum Handles { center, direction };
-
- qreal m_normalizedLength; ///< the normalized direction vector length
- QPointF m_origin; ///< the pattern handle origin
-};
-
-/// The class used for editing a shapes pattern
-class KarbonOdfPatternEditStrategy : public KarbonPatternEditStrategyBase
-{
-public:
- explicit KarbonOdfPatternEditStrategy(KoShape *shape, KoImageCollection *imageCollection);
- ~KarbonOdfPatternEditStrategy() override;
- void paint(QPainter &painter, const KoViewConverter &converter) const override;
- bool selectHandle(const QPointF &mousePos, const KoViewConverter &converter) override;
- void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) override;
- QRectF boundingRect() const override;
- QSharedPointer<KoPatternBackground> updatedBackground() override;
- void updateHandles() override;
-private:
-
- enum Handles { origin, size };
-
- void updateHandles(QSharedPointer<KoPatternBackground> fill);
-};
-
-#endif // _KARBONPATTERNEDITSTRATEGY_H_
diff --git a/plugins/tools/karbonplugins/tools/KarbonPatternOptionsWidget.cpp b/plugins/tools/karbonplugins/tools/KarbonPatternOptionsWidget.cpp
deleted file mode 100644
index ccc876586e..0000000000
--- a/plugins/tools/karbonplugins/tools/KarbonPatternOptionsWidget.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2008 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 "KarbonPatternOptionsWidget.h"
-#include "ui_KarbonPatternOptionsWidget.h"
-
-class Q_DECL_HIDDEN KarbonPatternOptionsWidget::Private
-{
-public:
- Ui_PatternOptionsWidget widget;
-};
-
-KarbonPatternOptionsWidget::KarbonPatternOptionsWidget(QWidget *parent)
- : QWidget(parent)
- , d(new Private())
-{
- d->widget.setupUi(this);
-
- d->widget.patternRepeat->insertItem(0, i18n("Original"));
- d->widget.patternRepeat->insertItem(1, i18n("Tiled"));
- d->widget.patternRepeat->insertItem(2, i18n("Stretched"));
-
- d->widget.referencePoint->insertItem(0, i18n("Top Left"));
- d->widget.referencePoint->insertItem(1, i18n("Top"));
- d->widget.referencePoint->insertItem(2, i18n("Top Right"));
- d->widget.referencePoint->insertItem(3, i18n("Left"));
- d->widget.referencePoint->insertItem(4, i18n("Center"));
- d->widget.referencePoint->insertItem(5, i18n("Right"));
- d->widget.referencePoint->insertItem(6, i18n("Bottom Left"));
- d->widget.referencePoint->insertItem(7, i18n("Bottom"));
- d->widget.referencePoint->insertItem(8, i18n("Bottom Right"));
-
- d->widget.refPointOffsetX->setRange(0.0, 100.0);
- d->widget.refPointOffsetX->setSuffix(QString('%'));
- d->widget.refPointOffsetY->setRange(0.0, 100.0);
- d->widget.refPointOffsetY->setSuffix(QString('%'));
- d->widget.tileOffsetX->setRange(0.0, 100.0);
- d->widget.tileOffsetX->setSuffix(QString('%'));
- d->widget.tileOffsetY->setRange(0.0, 100.0);
- d->widget.tileOffsetY->setSuffix(QString('%'));
- d->widget.patternWidth->setRange(1, 10000);
- d->widget.patternHeight->setRange(1, 10000);
-
- connect(d->widget.patternRepeat, SIGNAL(activated(int)), this, SIGNAL(patternChanged()));
- connect(d->widget.patternRepeat, SIGNAL(activated(int)), this, SLOT(updateControls()));
- connect(d->widget.referencePoint, SIGNAL(activated(int)), this, SIGNAL(patternChanged()));
- connect(d->widget.refPointOffsetX, SIGNAL(valueChanged(double)), this, SIGNAL(patternChanged()));
- connect(d->widget.refPointOffsetY, SIGNAL(valueChanged(double)), this, SIGNAL(patternChanged()));
- connect(d->widget.tileOffsetX, SIGNAL(valueChanged(double)), this, SIGNAL(patternChanged()));
- connect(d->widget.tileOffsetY, SIGNAL(valueChanged(double)), this, SIGNAL(patternChanged()));
- connect(d->widget.patternWidth, SIGNAL(valueChanged(int)), this, SIGNAL(patternChanged()));
- connect(d->widget.patternHeight, SIGNAL(valueChanged(int)), this, SIGNAL(patternChanged()));
-}
-
-KarbonPatternOptionsWidget::~KarbonPatternOptionsWidget()
-{
- delete d;
-}
-
-void KarbonPatternOptionsWidget::setRepeat(KoPatternBackground::PatternRepeat repeat)
-{
- d->widget.patternRepeat->blockSignals(true);
- d->widget.patternRepeat->setCurrentIndex(repeat);
- d->widget.patternRepeat->blockSignals(false);
- updateControls();
-}
-
-KoPatternBackground::PatternRepeat KarbonPatternOptionsWidget::repeat() const
-{
- return static_cast<KoPatternBackground::PatternRepeat>(d->widget.patternRepeat->currentIndex());
-}
-
-KoPatternBackground::ReferencePoint KarbonPatternOptionsWidget::referencePoint() const
-{
- return static_cast<KoPatternBackground::ReferencePoint>(d->widget.referencePoint->currentIndex());
-}
-
-void KarbonPatternOptionsWidget::setReferencePoint(KoPatternBackground::ReferencePoint referencePoint)
-{
- d->widget.referencePoint->blockSignals(true);
- d->widget.referencePoint->setCurrentIndex(referencePoint);
- d->widget.referencePoint->blockSignals(false);
-}
-
-QPointF KarbonPatternOptionsWidget::referencePointOffset() const
-{
- return QPointF(d->widget.refPointOffsetX->value(), d->widget.refPointOffsetY->value());
-}
-
-void KarbonPatternOptionsWidget::setReferencePointOffset(const QPointF &offset)
-{
- d->widget.refPointOffsetX->blockSignals(true);
- d->widget.refPointOffsetY->blockSignals(true);
- d->widget.refPointOffsetX->setValue(offset.x());
- d->widget.refPointOffsetY->setValue(offset.y());
- d->widget.refPointOffsetX->blockSignals(false);
- d->widget.refPointOffsetY->blockSignals(false);
-}
-
-QPointF KarbonPatternOptionsWidget::tileRepeatOffset() const
-{
- return QPointF(d->widget.tileOffsetX->value(), d->widget.tileOffsetY->value());
-}
-
-void KarbonPatternOptionsWidget::setTileRepeatOffset(const QPointF &offset)
-{
- d->widget.tileOffsetX->blockSignals(true);
- d->widget.tileOffsetY->blockSignals(true);
- d->widget.tileOffsetX->setValue(offset.x());
- d->widget.tileOffsetY->setValue(offset.y());
- d->widget.tileOffsetX->blockSignals(false);
- d->widget.tileOffsetY->blockSignals(false);
-}
-
-QSize KarbonPatternOptionsWidget::patternSize() const
-{
- return QSize(d->widget.patternWidth->value(), d->widget.patternHeight->value());
-}
-
-void KarbonPatternOptionsWidget::setPatternSize(const QSize &size)
-{
- d->widget.patternWidth->blockSignals(true);
- d->widget.patternHeight->blockSignals(true);
- d->widget.patternWidth->setValue(size.width());
- d->widget.patternHeight->setValue(size.height());
- d->widget.patternWidth->blockSignals(false);
- d->widget.patternHeight->blockSignals(false);
-}
-
-void KarbonPatternOptionsWidget::updateControls()
-{
- bool stretch = d->widget.patternRepeat->currentIndex() == KoPatternBackground::Stretched;
- d->widget.patternWidth->setEnabled(! stretch);
- d->widget.patternHeight->setEnabled(! stretch);
-
- bool tiled = d->widget.patternRepeat->currentIndex() == KoPatternBackground::Tiled;
- d->widget.referencePoint->setEnabled(tiled);
- d->widget.refPointOffsetX->setEnabled(tiled);
- d->widget.refPointOffsetY->setEnabled(tiled);
- d->widget.tileOffsetX->setEnabled(tiled);
- d->widget.tileOffsetY->setEnabled(tiled);
-}
-
diff --git a/plugins/tools/karbonplugins/tools/KarbonPatternOptionsWidget.h b/plugins/tools/karbonplugins/tools/KarbonPatternOptionsWidget.h
deleted file mode 100644
index 62e9b63d9e..0000000000
--- a/plugins/tools/karbonplugins/tools/KarbonPatternOptionsWidget.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2008 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 KARBONPATTERNOPTIONSWIDGET_H
-#define KARBONPATTERNOPTIONSWIDGET_H
-
-#include <KoPatternBackground.h>
-
-#include <QWidget>
-
-class KarbonPatternOptionsWidget : public QWidget
-{
- Q_OBJECT
-public:
- explicit KarbonPatternOptionsWidget(QWidget *parent = 0);
- ~KarbonPatternOptionsWidget() override;
-
- /// Sets the pattern repeat
- void setRepeat(KoPatternBackground::PatternRepeat repeat);
-
- /// Return the pattern repeat
- KoPatternBackground::PatternRepeat repeat() const;
-
- /// Returns the pattern reference point identifier
- KoPatternBackground::ReferencePoint referencePoint() const;
-
- /// Sets the pattern reference point
- void setReferencePoint(KoPatternBackground::ReferencePoint referencePoint);
-
- /// Returns reference point offset in percent of the size to fill
- QPointF referencePointOffset() const;
-
- /// Sets the reference point offset in percent of the size to fill
- void setReferencePointOffset(const QPointF &offset);
-
- /// Returns tile repeat offset in percent of the size to fill
- QPointF tileRepeatOffset() const;
-
- /// Sets the tile repeat offset in percent of the size to fill
- void setTileRepeatOffset(const QPointF &offset);
-
- /// Returns the pattern size
- QSize patternSize() const;
-
- /// Sets the pattern size
- void setPatternSize(const QSize &size);
-
-Q_SIGNALS:
- /// is emitted whenever an option has changed
- void patternChanged();
-private Q_SLOTS:
- void updateControls();
-private:
- class Private;
- Private *const d;
-
-};
-
-#endif // KARBONPATTERNOPTIONSWIDGET_H
diff --git a/plugins/tools/karbonplugins/tools/KarbonPatternOptionsWidget.ui b/plugins/tools/karbonplugins/tools/KarbonPatternOptionsWidget.ui
deleted file mode 100644
index ddd78a06cc..0000000000
--- a/plugins/tools/karbonplugins/tools/KarbonPatternOptionsWidget.ui
+++ /dev/null
@@ -1,161 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>PatternOptionsWidget</class>
- <widget class="QWidget" name="PatternOptionsWidget">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>240</width>
- <height>253</height>
- </rect>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <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 row="0" column="0" colspan="3">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Repeat:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="3" colspan="2">
- <widget class="KComboBox" name="patternRepeat"/>
- </item>
- <item row="1" column="0" colspan="3">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Reference Point:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="3" colspan="2">
- <widget class="KComboBox" name="referencePoint"/>
- </item>
- <item row="2" column="0" colspan="5">
- <widget class="QLabel" name="label_9">
- <property name="text">
- <string>Reference Point Offset</string>
- </property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QLabel" name="label_4">
- <property name="text">
- <string>X:</string>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="KisDoubleParseSpinBox" name="refPointOffsetX"/>
- </item>
- <item row="3" column="2" colspan="2">
- <widget class="QLabel" name="label_5">
- <property name="text">
- <string>Y:</string>
- </property>
- </widget>
- </item>
- <item row="3" column="4">
- <widget class="KisDoubleParseSpinBox" name="refPointOffsetY"/>
- </item>
- <item row="4" column="0" colspan="4">
- <widget class="QLabel" name="label_10">
- <property name="text">
- <string>Tile Offset</string>
- </property>
- </widget>
- </item>
- <item row="5" column="0">
- <widget class="QLabel" name="label_7">
- <property name="text">
- <string>X:</string>
- </property>
- </widget>
- </item>
- <item row="5" column="1">
- <widget class="KisDoubleParseSpinBox" name="tileOffsetX"/>
- </item>
- <item row="5" column="2" colspan="2">
- <widget class="QLabel" name="label_8">
- <property name="text">
- <string>Y:</string>
- </property>
- </widget>
- </item>
- <item row="5" column="4">
- <widget class="KisDoubleParseSpinBox" name="tileOffsetY"/>
- </item>
- <item row="6" column="0" colspan="4">
- <widget class="QLabel" name="label_11">
- <property name="text">
- <string>Pattern Size</string>
- </property>
- </widget>
- </item>
- <item row="7" column="0">
- <widget class="QLabel" name="label_3">
- <property name="text">
- <string>W:</string>
- </property>
- </widget>
- </item>
- <item row="7" column="1">
- <widget class="KisIntParseSpinBox" name="patternWidth"/>
- </item>
- <item row="7" column="2" colspan="2">
- <widget class="QLabel" name="label_6">
- <property name="text">
- <string>H:</string>
- </property>
- </widget>
- </item>
- <item row="7" column="4">
- <widget class="KisIntParseSpinBox" name="patternHeight"/>
- </item>
- <item row="8" column="1" colspan="3">
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>94</width>
- <height>121</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <customwidgets>
- <customwidget>
- <class>KisIntParseSpinBox</class>
- <extends>QSpinBox</extends>
- <header>kis_int_parse_spin_box.h</header>
- </customwidget>
- <customwidget>
- <class>KisDoubleParseSpinBox</class>
- <extends>QDoubleSpinBox</extends>
- <header>kis_double_parse_spin_box.h</header>
- </customwidget>
- <customwidget>
- <class>KComboBox</class>
- <extends>QComboBox</extends>
- <header>kcombobox.h</header>
- </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
diff --git a/plugins/tools/karbonplugins/tools/KarbonPatternTool.cpp b/plugins/tools/karbonplugins/tools/KarbonPatternTool.cpp
deleted file mode 100644
index 1d7bf33b03..0000000000
--- a/plugins/tools/karbonplugins/tools/KarbonPatternTool.cpp
+++ /dev/null
@@ -1,372 +0,0 @@
-/* This file is part of the KDE project
- * 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.
- */
-
-#include "KarbonPatternTool.h"
-#include "KarbonPatternEditStrategy.h"
-#include <KarbonPatternOptionsWidget.h>
-
-#include <KoCanvasBase.h>
-#include <KoShapeManager.h>
-#include <KoSelection.h>
-#include <KoShape.h>
-#include <KoDocumentResourceManager.h>
-#include <KoShapeBackgroundCommand.h>
-#include <KoPointerEvent.h>
-#include <resources/KoPattern.h>
-#include <KoPatternBackground.h>
-#include <KoImageCollection.h>
-#include <KoShapeController.h>
-#include <resources/KoResource.h>
-#include <KoResourceServerProvider.h>
-#include <KoResourceItemChooser.h>
-#include <KoResourceServerAdapter.h>
-
-#include <klocalizedstring.h>
-#include <KoViewConverter.h>
-
-#include <QPainter>
-#include <QWidget>
-#include <kundo2command.h>
-
-KarbonPatternTool::KarbonPatternTool(KoCanvasBase *canvas)
- : KoToolBase(canvas)
- , m_currentStrategy(0)
- , m_optionsWidget(0)
-{
-}
-
-KarbonPatternTool::~KarbonPatternTool()
-{
-}
-
-void KarbonPatternTool::paint(QPainter &painter, const KoViewConverter &converter)
-{
- painter.setBrush(Qt::green); //TODO make configurable
- painter.setPen(Qt::blue); //TODO make configurable
- painter.setTransform(converter.documentToView(), true);
-
- // paint all the strategies
- Q_FOREACH (KarbonPatternEditStrategyBase *strategy, m_strategies) {
- if (strategy == m_currentStrategy) {
- continue;
- }
-
- painter.save();
- strategy->paint(painter, converter);
- painter.restore();
- }
-
- // paint selected strategy with another color
- if (m_currentStrategy) {
- painter.setBrush(Qt::red); //TODO make configurable
- m_currentStrategy->paint(painter, converter);
- }
-}
-
-void KarbonPatternTool::repaintDecorations()
-{
- Q_FOREACH (KarbonPatternEditStrategyBase *strategy, m_strategies) {
- canvas()->updateCanvas(strategy->boundingRect());
- }
-}
-
-void KarbonPatternTool::mousePressEvent(KoPointerEvent *event)
-{
- //m_currentStrategy = 0;
-
- Q_FOREACH (KarbonPatternEditStrategyBase *strategy, m_strategies) {
- if (strategy->selectHandle(event->point, *canvas()->viewConverter())) {
- m_currentStrategy = strategy;
- m_currentStrategy->repaint();
- useCursor(Qt::SizeAllCursor);
- break;
- }
- }
- if (m_currentStrategy) {
- m_currentStrategy->setEditing(true);
- updateOptionsWidget();
- }
-}
-
-void KarbonPatternTool::mouseMoveEvent(KoPointerEvent *event)
-{
- if (m_currentStrategy) {
- m_currentStrategy->repaint();
- if (m_currentStrategy->isEditing()) {
- m_currentStrategy->handleMouseMove(event->point, event->modifiers());
- m_currentStrategy->repaint();
- return;
- }
- }
- Q_FOREACH (KarbonPatternEditStrategyBase *strategy, m_strategies) {
- if (strategy->selectHandle(event->point, *canvas()->viewConverter())) {
- useCursor(Qt::SizeAllCursor);
- return;
- }
- }
- useCursor(Qt::ArrowCursor);
-}
-
-void KarbonPatternTool::mouseReleaseEvent(KoPointerEvent *event)
-{
- Q_UNUSED(event)
- // if we are editing, get out of edit mode and add a command to the stack
- if (m_currentStrategy && m_currentStrategy->isEditing()) {
- m_currentStrategy->setEditing(false);
- KUndo2Command *cmd = m_currentStrategy->createCommand();
- if (cmd) {
- canvas()->addCommand(cmd);
- }
-
- updateOptionsWidget();
- }
-}
-
-void KarbonPatternTool::keyPressEvent(QKeyEvent *event)
-{
- switch (event->key()) {
- case Qt::Key_I: {
- KoDocumentResourceManager *rm = canvas()->shapeController()->resourceManager();
- uint handleRadius = rm->handleRadius();
- if (event->modifiers() & Qt::ControlModifier) {
- handleRadius--;
- } else {
- handleRadius++;
- }
- rm->setHandleRadius(handleRadius);
- }
- break;
- default:
- event->ignore();
- return;
- }
- event->accept();
-}
-
-void KarbonPatternTool::initialize()
-{
- if (m_currentStrategy && m_currentStrategy->isEditing()) {
- return;
- }
-
- QList<KoShape *> selectedShapes = canvas()->shapeManager()->selection()->selectedShapes();
-
- // remove all pattern strategies no longer applicable
- Q_FOREACH (KarbonPatternEditStrategyBase *strategy, m_strategies) {
- // is this gradient shape still selected ?
- if (!selectedShapes.contains(strategy->shape()) || ! strategy->shape()->isShapeEditable()) {
- m_strategies.remove(strategy->shape());
- if (m_currentStrategy == strategy) {
- m_currentStrategy = 0;
- }
- delete strategy;
- continue;
- }
-
- // does the shape has no fill pattern anymore ?
- QSharedPointer<KoPatternBackground> fill = qSharedPointerDynamicCast<KoPatternBackground>(strategy->shape()->background());
- if (!fill) {
- // delete the gradient
- m_strategies.remove(strategy->shape());
- if (m_currentStrategy == strategy) {
- m_currentStrategy = 0;
- }
- delete strategy;
- continue;
- }
-
- strategy->updateHandles();
- strategy->repaint();
- }
-
- KoImageCollection *imageCollection = canvas()->shapeController()->resourceManager()->imageCollection();
-
- // now create new strategies if needed
- Q_FOREACH (KoShape *shape, selectedShapes) {
- if (!shape->isShapeEditable()) {
- continue;
- }
-
- // do we already have a strategy for that shape?
- if (m_strategies.contains(shape)) {
- continue;
- }
-
- if (qSharedPointerDynamicCast<KoPatternBackground>(shape->background())) {
- KarbonPatternEditStrategyBase *s = new KarbonOdfPatternEditStrategy(shape, imageCollection);
- m_strategies.insert(shape, s);
- s->repaint();
- }
- }
- // automatically select strategy when editing single shape
- if (m_strategies.count() == 1 && ! m_currentStrategy) {
- m_currentStrategy = m_strategies.begin().value();
- updateOptionsWidget();
- }
-
- if (m_currentStrategy) {
- m_currentStrategy->repaint();
- }
-}
-
-void KarbonPatternTool::activate(ToolActivation activation, const QSet<KoShape *> &shapes)
-{
- KoToolBase::activate(activation, shapes);
-
- if (shapes.isEmpty()) {
- emit done();
- return;
- }
-
- initialize();
-
- KarbonPatternEditStrategyBase::setHandleRadius(handleRadius());
- KarbonPatternEditStrategyBase::setGrabSensitivity(grabSensitivity());
-
- useCursor(Qt::ArrowCursor);
-
- connect(canvas()->shapeManager(), SIGNAL(selectionContentChanged()), this, SLOT(initialize()));
-}
-
-void KarbonPatternTool::deactivate()
-{
- // we are not interested in selection content changes when not active
- disconnect(canvas()->shapeManager(), SIGNAL(selectionContentChanged()), this, SLOT(initialize()));
-
- Q_FOREACH (KarbonPatternEditStrategyBase *strategy, m_strategies) {
- strategy->repaint();
- }
-
- qDeleteAll(m_strategies);
- m_strategies.clear();
-
- Q_FOREACH (KoShape *shape, canvas()->shapeManager()->selection()->selectedShapes()) {
- shape->update();
- }
-
- m_currentStrategy = 0;
-
- KoToolBase::deactivate();
-}
-
-void KarbonPatternTool::documentResourceChanged(int key, const QVariant &res)
-{
- switch (key) {
- case KoDocumentResourceManager::HandleRadius:
- Q_FOREACH (KarbonPatternEditStrategyBase *strategy, m_strategies) {
- strategy->repaint();
- }
-
- KarbonPatternEditStrategyBase::setHandleRadius(res.toUInt());
-
- Q_FOREACH (KarbonPatternEditStrategyBase *strategy, m_strategies) {
- strategy->repaint();
- }
- break;
- case KoDocumentResourceManager::GrabSensitivity:
- KarbonPatternEditStrategyBase::setGrabSensitivity(res.toUInt());
- break;
- default:
- return;
- }
-}
-
-QList<QPointer<QWidget> > KarbonPatternTool::createOptionWidgets()
-{
- QList<QPointer<QWidget> > widgets;
-
- m_optionsWidget = new KarbonPatternOptionsWidget();
- connect(m_optionsWidget, SIGNAL(patternChanged()),
- this, SLOT(patternChanged()));
-
- KoResourceServer<KoPattern> *rserver = KoResourceServerProvider::instance()->patternServer();
- QSharedPointer<KoAbstractResourceServerAdapter> adapter(new KoResourceServerAdapter<KoPattern>(rserver));
- KoResourceItemChooser *chooser = new KoResourceItemChooser(adapter, m_optionsWidget);
- chooser->setObjectName("KarbonPatternChooser");
-
- connect(chooser, SIGNAL(resourceSelected(KoResource*)),
- this, SLOT(patternSelected(KoResource*)));
-
- m_optionsWidget->setWindowTitle(i18n("Pattern Options"));
- widgets.append(m_optionsWidget);
- chooser->setWindowTitle(i18n("Patterns"));
- widgets.append(chooser);
- updateOptionsWidget();
- return widgets;
-}
-
-void KarbonPatternTool::patternSelected(KoResource *resource)
-{
- KoPattern *currentPattern = dynamic_cast<KoPattern *>(resource);
- if (!currentPattern || ! currentPattern->valid()) {
- return;
- }
-
- KoImageCollection *imageCollection = canvas()->shapeController()->resourceManager()->imageCollection();
- if (imageCollection) {
- QList<KoShape *> selectedShapes = canvas()->shapeManager()->selection()->selectedShapes();
- QSharedPointer<KoPatternBackground> newFill(new KoPatternBackground(imageCollection));
- newFill->setPattern(currentPattern->pattern());
- canvas()->addCommand(new KoShapeBackgroundCommand(selectedShapes, newFill));
- initialize();
- }
-}
-
-void KarbonPatternTool::updateOptionsWidget()
-{
- if (m_optionsWidget && m_currentStrategy) {
- QSharedPointer<KoPatternBackground> fill = qSharedPointerDynamicCast<KoPatternBackground>(m_currentStrategy->shape()->background());
- if (fill) {
- m_optionsWidget->setRepeat(fill->repeat());
- m_optionsWidget->setReferencePoint(fill->referencePoint());
- m_optionsWidget->setReferencePointOffset(fill->referencePointOffset());
- m_optionsWidget->setTileRepeatOffset(fill->tileRepeatOffset());
- m_optionsWidget->setPatternSize(fill->patternDisplaySize().toSize());
- }
- }
-}
-
-void KarbonPatternTool::patternChanged()
-{
- if (m_currentStrategy) {
- KoShape *shape = m_currentStrategy->shape();
- QSharedPointer<KoPatternBackground> oldFill = qSharedPointerDynamicCast<KoPatternBackground>(shape->background());
- if (!oldFill) {
- return;
- }
- KoImageCollection *imageCollection = canvas()->shapeController()->resourceManager()->imageCollection();
- if (!imageCollection) {
- return;
- }
- QSharedPointer<KoPatternBackground> newFill(new KoPatternBackground(imageCollection));
- if (!newFill) {
- return;
- }
- newFill->setTransform(oldFill->transform());
- newFill->setPattern(oldFill->pattern());
-
- newFill->setRepeat(m_optionsWidget->repeat());
- newFill->setReferencePoint(m_optionsWidget->referencePoint());
- newFill->setReferencePointOffset(m_optionsWidget->referencePointOffset());
- newFill->setTileRepeatOffset(m_optionsWidget->tileRepeatOffset());
- newFill->setPatternDisplaySize(m_optionsWidget->patternSize());
- canvas()->addCommand(new KoShapeBackgroundCommand(shape, newFill));
- }
-}
-
diff --git a/plugins/tools/karbonplugins/tools/KarbonPatternTool.h b/plugins/tools/karbonplugins/tools/KarbonPatternTool.h
deleted file mode 100644
index 0ccddd7b02..0000000000
--- a/plugins/tools/karbonplugins/tools/KarbonPatternTool.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2007,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 _KARBONPATTERNTOOL_H_
-#define _KARBONPATTERNTOOL_H_
-
-#include <KoToolBase.h>
-#include <QMap>
-
-class QPainter;
-class KoResource;
-class KarbonPatternEditStrategyBase;
-class KarbonPatternOptionsWidget;
-class KoShape;
-
-class KarbonPatternTool : public KoToolBase
-{
- Q_OBJECT
-public:
- explicit KarbonPatternTool(KoCanvasBase *canvas);
- ~KarbonPatternTool() override;
-
- void paint(QPainter &painter, const KoViewConverter &converter) override;
- void repaintDecorations() override;
-
- void mousePressEvent(KoPointerEvent *event) override;
- void mouseMoveEvent(KoPointerEvent *event) override;
- void mouseReleaseEvent(KoPointerEvent *event) override;
- void keyPressEvent(QKeyEvent *event) override;
-
- void activate(ToolActivation activation, const QSet<KoShape *> &shapes) override;
- void deactivate() override;
-
-public Q_SLOTS:
- void documentResourceChanged(int key, const QVariant &res) override;
-
-protected:
- QList<QPointer<QWidget> > createOptionWidgets() override;
-
-private Q_SLOTS:
- void patternSelected(KoResource *resource);
- void initialize();
- /// updates options widget from selected pattern
- void updateOptionsWidget();
- void patternChanged();
-private:
- QMap<KoShape *, KarbonPatternEditStrategyBase *> m_strategies; ///< the list of editing strategies, one for each shape
- KarbonPatternEditStrategyBase *m_currentStrategy; ///< the current editing strategy
- KarbonPatternOptionsWidget *m_optionsWidget;
-};
-
-#endif // _KARBONPATTERNTOOL_H_
diff --git a/plugins/tools/karbonplugins/tools/KarbonPatternToolFactory.cpp b/plugins/tools/karbonplugins/tools/KarbonPatternToolFactory.cpp
deleted file mode 100644
index eb8df40727..0000000000
--- a/plugins/tools/karbonplugins/tools/KarbonPatternToolFactory.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/* 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 "KarbonPatternToolFactory.h"
-#include "KarbonPatternTool.h"
-
-#include <KoIcon.h>
-#include <klocalizedstring.h>
-#include <QDebug>
-
-KarbonPatternToolFactory::KarbonPatternToolFactory()
- : KoToolFactoryBase("KarbonPatternTool")
-{
- setToolTip(i18n("Pattern editing"));
- setSection(mainToolType());
- setIconName(koIconNameCStr("pattern"));
- setPriority(8);
-}
-
-KarbonPatternToolFactory::~KarbonPatternToolFactory()
-{
-}
-
-KoToolBase *KarbonPatternToolFactory::createTool(KoCanvasBase *canvas)
-{
- return new KarbonPatternTool(canvas);
-}
diff --git a/plugins/tools/karbonplugins/tools/KarbonPatternToolFactory.h b/plugins/tools/karbonplugins/tools/KarbonPatternToolFactory.h
deleted file mode 100644
index 976dffb9c1..0000000000
--- a/plugins/tools/karbonplugins/tools/KarbonPatternToolFactory.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* 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.
- */
-
-#ifndef _KARBONPATTERNTOOLFACTORY_H_
-#define _KARBONPATTERNTOOLFACTORY_H_
-
-#include <KoToolFactoryBase.h>
-
-class KarbonPatternToolFactory : public KoToolFactoryBase
-{
-public:
- KarbonPatternToolFactory();
- ~KarbonPatternToolFactory() override;
-
- KoToolBase *createTool(KoCanvasBase *canvas) override;
-};
-
-#endif // _KARBONPATTERNTOOLFACTORY_H_
diff --git a/plugins/tools/karbonplugins/tools/KarbonToolsPlugin.cpp b/plugins/tools/karbonplugins/tools/KarbonToolsPlugin.cpp
index 9e48d217cf..3d2a0805fc 100644
--- a/plugins/tools/karbonplugins/tools/KarbonToolsPlugin.cpp
+++ b/plugins/tools/karbonplugins/tools/KarbonToolsPlugin.cpp
@@ -1,43 +1,38 @@
/* 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 "KarbonToolsPlugin.h"
#include "CalligraphyTool/KarbonCalligraphyToolFactory.h"
#include "CalligraphyTool/KarbonCalligraphicShapeFactory.h"
-#include "KarbonPatternToolFactory.h"
-#include "KarbonFilterEffectsToolFactory.h"
#include <KoToolRegistry.h>
#include <KoShapeRegistry.h>
#include <kpluginfactory.h>
#include <kpluginloader.h>
K_PLUGIN_FACTORY_WITH_JSON(KarbonToolsPluginFactory, "karbon_tools.json", registerPlugin<KarbonToolsPlugin>();)
KarbonToolsPlugin::KarbonToolsPlugin(QObject *parent, const QVariantList &)
: QObject(parent)
{
KoToolRegistry::instance()->add(new KarbonCalligraphyToolFactory());
- //KoToolRegistry::instance()->add(new KarbonPatternToolFactory());
- //KoToolRegistry::instance()->add(new KarbonFilterEffectsToolFactory());
-
KoShapeRegistry::instance()->add(new KarbonCalligraphicShapeFactory());
}
#include <KarbonToolsPlugin.moc>
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterAddCommand.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterAddCommand.cpp
deleted file mode 100644
index 719bf205c7..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterAddCommand.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/* 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.
- */
-
-#include "FilterAddCommand.h"
-#include "KoShape.h"
-#include "KoFilterEffect.h"
-#include "KoFilterEffectStack.h"
-
-#include <klocalizedstring.h>
-
-FilterAddCommand::FilterAddCommand(KoFilterEffect *filterEffect, KoShape *shape, KUndo2Command *parent)
- : KUndo2Command(parent)
- , m_filterEffect(filterEffect)
- , m_shape(shape)
- , m_isAdded(false)
-{
- Q_ASSERT(m_shape);
- setText(kundo2_i18n("Add filter effect"));
-}
-
-FilterAddCommand::~FilterAddCommand()
-{
- if (!m_isAdded) {
- delete m_filterEffect;
- }
-}
-
-void FilterAddCommand::redo()
-{
- KUndo2Command::redo();
-
- if (m_shape->filterEffectStack()) {
- m_shape->update();
- m_shape->filterEffectStack()->appendFilterEffect(m_filterEffect);
- m_shape->update();
- m_isAdded = true;
- }
-}
-
-void FilterAddCommand::undo()
-{
- if (m_shape->filterEffectStack()) {
- int index = m_shape->filterEffectStack()->filterEffects().indexOf(m_filterEffect);
- if (index >= 0) {
- m_shape->update();
- m_shape->filterEffectStack()->takeFilterEffect(index);
- m_shape->update();
- }
- m_isAdded = false;
- }
- KUndo2Command::undo();
-}
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterAddCommand.h b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterAddCommand.h
deleted file mode 100644
index e13bc801e0..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterAddCommand.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* 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 FILTERADDCOMMAND_H
-#define FILTERADDCOMMAND_H
-
-#include <kundo2command.h>
-
-class KoShape;
-class KoFilterEffect;
-
-/// A command do add a new filter effect to a filter effect stack
-class FilterAddCommand : public KUndo2Command
-{
-public:
- FilterAddCommand(KoFilterEffect *filterEffect, KoShape *shape, KUndo2Command *parent = 0);
- ~FilterAddCommand() override;
- /// redo the command
- void redo() override;
- /// revert the actions done in redo
- void undo() override;
-
-private:
- KoFilterEffect *m_filterEffect;
- KoShape *m_shape;
- bool m_isAdded;
-};
-
-#endif // FILTERADDCOMMAND_H
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectEditWidget.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectEditWidget.cpp
deleted file mode 100644
index 800dfc7b42..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectEditWidget.cpp
+++ /dev/null
@@ -1,558 +0,0 @@
-/* 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.
- */
-
-#include "FilterEffectEditWidget.h"
-#include "FilterEffectResource.h"
-#include "FilterResourceServerProvider.h"
-#include "FilterInputChangeCommand.h"
-#include "FilterAddCommand.h"
-#include "FilterRemoveCommand.h"
-#include "FilterStackSetCommand.h"
-#include "KoGenericRegistryModel.h"
-#include "KoFilterEffectRegistry.h"
-#include "KoFilterEffect.h"
-#include "KoFilterEffectStack.h"
-#include "KoFilterEffectConfigWidgetBase.h"
-#include "KoFilterEffectFactoryBase.h"
-#include "KoShape.h"
-#include "KoCanvasBase.h"
-#include "KoResourceModel.h"
-#include "KoResourceServerAdapter.h"
-
-#include <KoIcon.h>
-
-#include <QInputDialog>
-#include <QDebug>
-
-#include <QGraphicsItem>
-#include <QSet>
-
-FilterEffectEditWidget::FilterEffectEditWidget(QWidget *parent)
- : QWidget(parent)
- , m_scene(new FilterEffectScene(this))
- , m_shape(0)
- , m_canvas(0)
- , m_effects(0)
-{
- setupUi(this);
-
- FilterResourceServerProvider *serverProvider = FilterResourceServerProvider::instance();
- KoResourceServer<FilterEffectResource> *server = serverProvider->filterEffectServer();
- QSharedPointer<KoAbstractResourceServerAdapter> adapter(new KoResourceServerAdapter<FilterEffectResource>(server));
-
- presets->setResourceAdapter(adapter);
- presets->setDisplayMode(KoResourceSelector::TextMode);
- presets->setColumnCount(1);
-
- connect(presets, SIGNAL(resourceSelected(KoResource*)),
- this, SLOT(presetSelected(KoResource*)));
-
- connect(presets, SIGNAL(resourceApplied(KoResource*)),
- this, SLOT(presetSelected(KoResource*)));
-
- KoGenericRegistryModel<KoFilterEffectFactoryBase *> *filterEffectModel = new KoGenericRegistryModel<KoFilterEffectFactoryBase *>(KoFilterEffectRegistry::instance());
-
- effectSelector->setModel(filterEffectModel);
- removeEffect->setIcon(koIcon("list-remove"));
- connect(removeEffect, SIGNAL(clicked()), this, SLOT(removeSelectedItem()));
- addEffect->setIcon(koIcon("list-add"));
- addEffect->setToolTip(i18n("Add effect to current filter stack"));
- connect(addEffect, SIGNAL(clicked()), this, SLOT(addSelectedEffect()));
-
- // TODO: make these buttons do something useful
- raiseEffect->setIcon(koIcon("arrow-up"));
- raiseEffect->hide();
- lowerEffect->setIcon(koIcon("arrow-down"));
- lowerEffect->hide();
-
- addPreset->setIcon(koIcon("list-add"));
- addPreset->setToolTip(i18n("Add to filter presets"));
- connect(addPreset, SIGNAL(clicked()), this, SLOT(addToPresets()));
-
- removePreset->setIcon(koIcon("list-remove"));
- removePreset->setToolTip(i18n("Remove filter preset"));
- connect(removePreset, SIGNAL(clicked()), this, SLOT(removeFromPresets()));
-
- view->setScene(m_scene);
- view->setRenderHint(QPainter::Antialiasing, true);
- view->setResizeAnchor(QGraphicsView::AnchorViewCenter);
-
- connect(m_scene, SIGNAL(connectionCreated(ConnectionSource,ConnectionTarget)),
- this, SLOT(connectionCreated(ConnectionSource,ConnectionTarget)));
- connect(m_scene, SIGNAL(selectionChanged()), this, SLOT(sceneSelectionChanged()));
-
- QSet<ConnectionSource::SourceType> inputs;
- inputs << ConnectionSource::SourceGraphic;
- inputs << ConnectionSource::SourceAlpha;
- inputs << ConnectionSource::BackgroundImage;
- inputs << ConnectionSource::BackgroundAlpha;
- inputs << ConnectionSource::FillPaint;
- inputs << ConnectionSource::StrokePaint;
-
- m_defaultSourceSelector = new KComboBox(this);
- Q_FOREACH (ConnectionSource::SourceType source, inputs) {
- m_defaultSourceSelector->addItem(ConnectionSource::typeToString(source));
- }
- m_defaultSourceSelector->hide();
- m_defaultSourceSelector->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
- connect(m_defaultSourceSelector, SIGNAL(currentIndexChanged(int)),
- this, SLOT(defaultSourceChanged(int)));
-}
-
-FilterEffectEditWidget::~FilterEffectEditWidget()
-{
- if (!m_shape) {
- delete m_effects;
- }
-}
-
-void FilterEffectEditWidget::editShape(KoShape *shape, KoCanvasBase *canvas)
-{
- if (!m_shape) {
- delete m_effects;
- m_effects = 0;
- }
-
- m_shape = shape;
- m_canvas = canvas;
-
- if (m_shape) {
- m_effects = m_shape->filterEffectStack();
- }
- if (!m_effects) {
- m_effects = new KoFilterEffectStack();
- }
-
- m_scene->initialize(m_effects);
- fitScene();
-}
-
-void FilterEffectEditWidget::fitScene()
-{
- QRectF bbox = m_scene->itemsBoundingRect();
- m_scene->setSceneRect(bbox);
- bbox.adjust(-25, -25, 25, 25);
- view->centerOn(bbox.center());
- view->fitInView(bbox, Qt::KeepAspectRatio);
-}
-
-void FilterEffectEditWidget::resizeEvent(QResizeEvent *event)
-{
- Q_UNUSED(event);
- fitScene();
-}
-
-void FilterEffectEditWidget::showEvent(QShowEvent *event)
-{
- Q_UNUSED(event);
- fitScene();
-}
-
-void FilterEffectEditWidget::addSelectedEffect()
-{
- KoFilterEffectRegistry *registry = KoFilterEffectRegistry::instance();
- KoFilterEffectFactoryBase *factory = registry->values()[effectSelector->currentIndex()];
- if (!factory) {
- return;
- }
-
- KoFilterEffect *effect = factory->createFilterEffect();
- if (!effect) {
- return;
- }
-
- if (m_shape) {
- if (!m_shape->filterEffectStack()) {
- m_effects->appendFilterEffect(effect);
- m_canvas->addCommand(new FilterStackSetCommand(m_effects, m_shape));
- } else {
- m_canvas->addCommand(new FilterAddCommand(effect, m_shape));
- }
- } else {
- m_effects->appendFilterEffect(effect);
- }
-
- m_scene->initialize(m_effects);
- fitScene();
-}
-
-void FilterEffectEditWidget::removeSelectedItem()
-{
- QList<ConnectionSource> selectedItems = m_scene->selectedEffectItems();
- if (!selectedItems.count()) {
- return;
- }
-
- QList<InputChangeData> changeData;
- QList<KoFilterEffect *> filterEffects = m_effects->filterEffects();
- int effectIndexToDelete = -1;
-
- const ConnectionSource &item = selectedItems.first();
- KoFilterEffect *effect = item.effect();
- if (item.type() == ConnectionSource::Effect) {
- int effectIndex = filterEffects.indexOf(effect);
- // adjust inputs of all following effects in the stack
- for (int i = effectIndex + 1; i < filterEffects.count(); ++i) {
- KoFilterEffect *nextEffect = filterEffects[i];
- QList<QString> inputs = nextEffect->inputs();
- int inputIndex = 0;
- Q_FOREACH (const QString &input, inputs) {
- if (input == effect->output()) {
- InputChangeData data(nextEffect, inputIndex, input, "");
- changeData.append(data);
- }
- }
- // if one of the next effects has the same output name we stop
- if (nextEffect->output() == effect->output()) {
- break;
- }
- }
- effectIndexToDelete = effectIndex;
- } else {
- QString outputName = ConnectionSource::typeToString(item.type());
- QList<QString> inputs = effect->inputs();
- int inputIndex = 0;
- Q_FOREACH (const QString &input, inputs) {
- if (input == outputName) {
- InputChangeData data(effect, inputIndex, input, "");
- changeData.append(data);
- }
- inputIndex++;
- }
- }
-
- KUndo2Command *cmd = new KUndo2Command();
- if (changeData.count()) {
- KUndo2Command *subCmd = new FilterInputChangeCommand(changeData, m_shape, cmd);
- cmd->setText(subCmd->text());
- }
- if (effectIndexToDelete >= 0) {
- KUndo2Command *subCmd = new FilterRemoveCommand(effectIndexToDelete, m_effects, m_shape, cmd);
- cmd->setText(subCmd->text());
- }
- if (m_canvas && m_shape) {
- m_canvas->addCommand(cmd);
- } else {
- cmd->redo();
- delete cmd;
- }
- m_scene->initialize(m_effects);
- fitScene();
-}
-
-void FilterEffectEditWidget::connectionCreated(ConnectionSource source, ConnectionTarget target)
-{
- QList<KoFilterEffect *> filterEffects = m_effects->filterEffects();
-
- int targetEffectIndex = filterEffects.indexOf(target.effect());
- if (targetEffectIndex < 0) {
- return;
- }
-
- QList<InputChangeData> changeData;
- QString sourceName;
-
- if (source.type() == ConnectionSource::Effect) {
- sourceName = source.effect()->output();
- int sourceEffectIndex = filterEffects.indexOf(source.effect());
- if (targetEffectIndex - sourceEffectIndex > 1) {
- // there are effects between source effect and target effect
- // so we have to take extra care
- bool renameOutput = false;
- if (sourceName.isEmpty()) {
- // output is not named so we have to rename the source output
- // and adjust the next effect in case it uses this output
- renameOutput = true;
- } else {
- // output is named but if there is an effect with the same
- // output name, we have to rename the source output
- for (int i = sourceEffectIndex + 1; i < targetEffectIndex; ++i) {
- KoFilterEffect *effect = filterEffects[i];
- if (effect->output() == sourceName) {
- renameOutput = true;
- break;
- }
- }
- }
- if (renameOutput) {
- QSet<QString> uniqueOutputNames;
- Q_FOREACH (KoFilterEffect *effect, filterEffects) {
- uniqueOutputNames.insert(effect->output());
- }
- int index = 0;
- QString newOutputName;
- do {
- newOutputName = QString("result%1").arg(index);
- } while (uniqueOutputNames.contains(newOutputName));
-
- // rename source output
- source.effect()->setOutput(newOutputName);
- // adjust following effects
- for (int i = sourceEffectIndex + 1; i < targetEffectIndex; ++i) {
- KoFilterEffect *effect = filterEffects[i];
- int inputIndex = 0;
- Q_FOREACH (const QString &input, effect->inputs()) {
- if (input.isEmpty() && (i == sourceEffectIndex + 1 || input == sourceName)) {
- InputChangeData data(effect, inputIndex, input, newOutputName);
- changeData.append(data);
- }
- inputIndex++;
- }
- if (sourceName.isEmpty() || effect->output() == sourceName) {
- break;
- }
- }
- sourceName = newOutputName;
- }
- }
- } else {
- // source is an predefined input image
- sourceName = ConnectionSource::typeToString(source.type());
- }
-
- // finally set the input of the target
- if (target.inputIndex() >= target.effect()->inputs().count()) {
- // insert new input here
- target.effect()->addInput(sourceName);
- } else {
- QString oldInput = target.effect()->inputs()[target.inputIndex()];
- InputChangeData data(target.effect(), target.inputIndex(), oldInput, sourceName);
- changeData.append(data);
- }
-
- if (changeData.count()) {
- KUndo2Command *cmd = new FilterInputChangeCommand(changeData, m_shape);
- if (m_canvas) {
- m_canvas->addCommand(cmd);
- } else {
- cmd->redo();
- delete cmd;
- }
- }
- m_scene->initialize(m_effects);
- fitScene();
-}
-
-void FilterEffectEditWidget::addToPresets()
-{
- if (!m_effects) {
- return;
- }
-
- bool ok = false;
- QString effectName = QInputDialog::getText(this, i18n("Effect name"),
- i18n("Please enter a name for the filter effect"),
- QLineEdit::Normal,
- QString(),
- &ok);
- if (!ok) {
- return;
- }
-
- FilterEffectResource *resource = FilterEffectResource::fromFilterEffectStack(m_effects);
- if (!resource) {
- return;
- }
-
- resource->setName(effectName);
-
- FilterResourceServerProvider *serverProvider = FilterResourceServerProvider::instance();
- KoResourceServer<FilterEffectResource> *server = serverProvider->filterEffectServer();
-
- QString savePath = server->saveLocation();
-
- int i = 1;
- QFileInfo fileInfo;
-
- do {
- fileInfo.setFile(savePath + QString("%1.svg").arg(i++, 4, 10, QChar('0')));
- } while (fileInfo.exists());
-
- resource->setFilename(fileInfo.filePath());
- resource->setValid(true);
-
- if (!server->addResource(resource)) {
- delete resource;
- }
-}
-
-void FilterEffectEditWidget::removeFromPresets()
-{
- if (!presets->count()) {
- return;
- }
- FilterResourceServerProvider *serverProvider = FilterResourceServerProvider::instance();
- if (!serverProvider) {
- return;
- }
- KoResourceServer<FilterEffectResource> *server = serverProvider->filterEffectServer();
- if (!server) {
- return;
- }
-
- FilterEffectResource *resource = server->resources().at(presets->currentIndex());
- if (!resource) {
- return;
- }
-
- server->removeResourceAndBlacklist(resource);
-}
-
-void FilterEffectEditWidget::presetSelected(KoResource *resource)
-{
- FilterEffectResource *effectResource = dynamic_cast<FilterEffectResource *>(resource);
- if (!effectResource) {
- return;
- }
-
- KoFilterEffectStack *filterStack = effectResource->toFilterStack();
- if (!filterStack) {
- return;
- }
-
- if (m_shape) {
- KUndo2Command *cmd = new FilterStackSetCommand(filterStack, m_shape);
- if (m_canvas) {
- m_canvas->addCommand(cmd);
- } else {
- cmd->redo();
- delete cmd;
- }
- } else {
- delete m_effects;
- }
- m_effects = filterStack;
-
- m_scene->initialize(m_effects);
- fitScene();
-}
-
-void FilterEffectEditWidget::addWidgetForItem(ConnectionSource item)
-{
- // get the filter effect from the item
- KoFilterEffect *filterEffect = item.effect();
- if (item.type() != ConnectionSource::Effect) {
- filterEffect = 0;
- }
-
- KoFilterEffect *currentEffect = m_currentItem.effect();
- if (m_currentItem.type() != ConnectionSource::Effect) {
- currentEffect = 0;
- }
-
- m_defaultSourceSelector->hide();
-
- // remove current widget if new effect is zero or effect type has changed
- if (!filterEffect || !currentEffect || (filterEffect->id() != currentEffect->id())) {
- while (configStack->count()) {
- configStack->removeWidget(configStack->widget(0));
- }
- }
-
- m_currentItem = item;
-
- KoFilterEffectConfigWidgetBase *currentPanel = 0;
-
- if (!filterEffect) {
- if (item.type() != ConnectionSource::Effect) {
- configStack->insertWidget(0, m_defaultSourceSelector);
- m_defaultSourceSelector->blockSignals(true);
- m_defaultSourceSelector->setCurrentIndex(item.type() - 1);
- m_defaultSourceSelector->blockSignals(false);
- m_defaultSourceSelector->show();
- }
- } else if (!currentEffect || currentEffect->id() != filterEffect->id()) {
- // when a shape is set and is differs from the previous one
- // get the config widget and insert it into the option widget
-
- KoFilterEffectRegistry *registry = KoFilterEffectRegistry::instance();
- KoFilterEffectFactoryBase *factory = registry->value(filterEffect->id());
- if (!factory) {
- return;
- }
-
- currentPanel = factory->createConfigWidget();
- if (!currentPanel) {
- return;
- }
-
- configStack->insertWidget(0, currentPanel);
- connect(currentPanel, SIGNAL(filterChanged()), this, SLOT(filterChanged()));
- }
-
- currentPanel = qobject_cast<KoFilterEffectConfigWidgetBase *>(configStack->widget(0));
- if (currentPanel) {
- currentPanel->editFilterEffect(filterEffect);
- }
-}
-
-void FilterEffectEditWidget::filterChanged()
-{
- if (m_shape) {
- m_shape->update();
- }
-}
-
-void FilterEffectEditWidget::sceneSelectionChanged()
-{
- QList<ConnectionSource> selectedItems = m_scene->selectedEffectItems();
- if (!selectedItems.count()) {
- addWidgetForItem(ConnectionSource());
- } else {
- addWidgetForItem(selectedItems.first());
- }
-}
-
-void FilterEffectEditWidget::defaultSourceChanged(int index)
-{
- if (m_currentItem.type() == ConnectionSource::Effect) {
- return;
- }
-
- KoFilterEffect *filterEffect = m_currentItem.effect();
- if (!filterEffect) {
- return;
- }
-
- QString oldInput = ConnectionSource::typeToString(m_currentItem.type());
- QString newInput = m_defaultSourceSelector->itemText(index);
-
- const QString defInput = "SourceGraphic";
- int effectIndex = m_effects->filterEffects().indexOf(filterEffect);
-
- InputChangeData data;
- int inputIndex = 0;
- Q_FOREACH (const QString &input, filterEffect->inputs()) {
- if (input == oldInput || (effectIndex == 0 && oldInput == defInput)) {
- data = InputChangeData(filterEffect, inputIndex, input, newInput);
- break;
- }
- inputIndex++;
- }
- KUndo2Command *cmd = new FilterInputChangeCommand(data, m_shape);
- if (m_canvas && m_shape) {
- m_canvas->addCommand(cmd);
- } else {
- cmd->redo();
- delete cmd;
- }
-
- m_scene->initialize(m_effects);
- fitScene();
-}
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectEditWidget.h b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectEditWidget.h
deleted file mode 100644
index d2a76bb65f..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectEditWidget.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/* 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 FILTEREFFECTEDITWIDGET_H
-#define FILTEREFFECTEDITWIDGET_H
-
-#include "ui_FilterEffectEditWidget.h"
-#include "FilterEffectScene.h"
-#include <QWidget>
-#include <QPointer>
-
-#include <KoCanvasBase.h>
-
-class KoShape;
-class KoFilterEffect;
-class KoFilterEffectStack;
-
-class FilterEffectEditWidget : public QWidget, Ui::FilterEffectEditWidget
-{
- Q_OBJECT
-public:
- explicit FilterEffectEditWidget(QWidget *parent = 0);
- ~FilterEffectEditWidget() override;
-
- /// Edits effects of given shape
- void editShape(KoShape *shape, KoCanvasBase *canvas);
-
-protected:
- /// reimplemented from QWidget
- void resizeEvent(QResizeEvent *event) override;
- /// reimplemented from QWidget
- void showEvent(QShowEvent *event) override;
-private Q_SLOTS:
- void addSelectedEffect();
- void removeSelectedItem();
- void connectionCreated(ConnectionSource source, ConnectionTarget target);
- void addToPresets();
- void removeFromPresets();
- void presetSelected(KoResource *resource);
- void filterChanged();
- void sceneSelectionChanged();
- void defaultSourceChanged(int);
-private:
- void fitScene();
- void addWidgetForItem(ConnectionSource item);
-
- FilterEffectScene *m_scene;
- KoShape *m_shape;
- QPointer<KoCanvasBase> m_canvas;
- KoFilterEffectStack *m_effects;
- ConnectionSource m_currentItem;
- KComboBox *m_defaultSourceSelector;
-};
-
-#endif // FILTEREFFECTEDITWIDGET_H
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectEditWidget.ui b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectEditWidget.ui
deleted file mode 100644
index d507339135..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectEditWidget.ui
+++ /dev/null
@@ -1,175 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>FilterEffectEditWidget</class>
- <widget class="QWidget" name="FilterEffectEditWidget">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>446</width>
- <height>414</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <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="windowTitle">
- <string>Filter Effect Editor</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="0" column="0">
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0" colspan="5">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Effects and Connections</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0" colspan="5">
- <widget class="QGraphicsView" name="view">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>350</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QToolButton" name="removeEffect">
- <property name="text">
- <string>...</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="KComboBox" name="effectSelector"/>
- </item>
- <item row="2" column="2">
- <widget class="QToolButton" name="addEffect">
- <property name="text">
- <string>...</string>
- </property>
- </widget>
- </item>
- <item row="2" column="3">
- <widget class="QToolButton" name="raiseEffect">
- <property name="text">
- <string>...</string>
- </property>
- </widget>
- </item>
- <item row="2" column="4">
- <widget class="QToolButton" name="lowerEffect">
- <property name="text">
- <string>...</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="1">
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="0" column="0" colspan="3">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Filter Presets</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="KoResourceSelector" name="presets"/>
- </item>
- <item row="1" column="1">
- <widget class="QToolButton" name="addPreset">
- <property name="text">
- <string>...</string>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QToolButton" name="removePreset">
- <property name="text">
- <string>...</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0" colspan="3">
- <widget class="QLabel" name="label_3">
- <property name="text">
- <string>Effect Properties</string>
- </property>
- </widget>
- </item>
- <item row="3" column="0" colspan="3">
- <widget class="QStackedWidget" name="configStack">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="currentIndex">
- <number>1</number>
- </property>
- <widget class="QWidget" name="page"/>
- <widget class="QWidget" name="page_2"/>
- </widget>
- </item>
- <item row="4" column="0">
- <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>20</width>
- <height>295</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- <customwidgets>
- <customwidget>
- <class>KComboBox</class>
- <extends>QComboBox</extends>
- <header>kcombobox.h</header>
- </customwidget>
- <customwidget>
- <class>KoResourceSelector</class>
- <extends>QComboBox</extends>
- <header>KoResourceSelector.h</header>
- </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectResource.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectResource.cpp
deleted file mode 100644
index 380fbe1de3..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectResource.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/* 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.
- */
-
-#include "FilterEffectResource.h"
-#include <KoFilterEffect.h>
-#include <KoFilterEffectStack.h>
-#include <KoFilterEffectRegistry.h>
-#include <KoFilterEffectLoadingContext.h>
-#include <KoXmlWriter.h>
-
-#include <QDebug>
-
-#include <QFile>
-#include <QBuffer>
-#include <QCryptographicHash>
-#include <QDomDocument>
-
-double fromPercentage(QString s)
-{
- if (s.endsWith('%')) {
- return s.remove('%').toDouble() / 100.0;
- } else {
- return s.toDouble();
- }
-}
-
-FilterEffectResource::FilterEffectResource(const QString &filename)
- : KoResource(filename)
-{
-}
-
-bool FilterEffectResource::load()
-{
- QFile file(filename());
-
- if (file.size() == 0) {
- return false;
- }
- if (!file.open(QIODevice::ReadOnly)) {
- return false;
- }
-
- bool res = loadFromDevice(&file);
-
- file.close();
- return res;
-}
-
-bool FilterEffectResource::loadFromDevice(QIODevice *dev)
-{
- if (!m_data.setContent(dev)) {
- return false;
- }
-
- setName(m_data.documentElement().attribute("id"));
- setValid(true);
-
- return true;
-}
-
-bool FilterEffectResource::save()
-{
- QFile file(filename());
- if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- return false;
- }
- bool result = saveToDevice(&file);
- file.close();
- return result;
-}
-
-bool FilterEffectResource::saveToDevice(QIODevice *dev) const
-{
- m_data.documentElement().setAttribute("id", name());
- QByteArray ba = m_data.toByteArray(2);
- bool success = (dev->write(ba) == ba.size());
- return success;
-}
-
-QString FilterEffectResource::defaultFileExtension() const
-{
- return QString(".svg");
-}
-
-FilterEffectResource *FilterEffectResource::fromFilterEffectStack(KoFilterEffectStack *filterStack)
-{
- if (!filterStack) {
- return 0;
- }
-
- QByteArray ba;
- QBuffer buffer(&ba);
- buffer.open(QIODevice::ReadWrite);
- KoXmlWriter writer(&buffer);
-
- filterStack->save(writer, "");
-
- buffer.close();
-
- FilterEffectResource *resource = new FilterEffectResource(QString());
- if (!resource->m_data.setContent(ba)) {
- delete resource;
- return 0;
- }
-
- return resource;
-}
-
-KoFilterEffectStack *FilterEffectResource::toFilterStack() const
-{
- QScopedPointer<KoFilterEffectStack> filterStack(new KoFilterEffectStack());
-
- QByteArray data = m_data.toByteArray();
- KoXmlDocument doc;
- doc.setContent(data);
- KoXmlElement e = doc.documentElement();
-
- // only allow object bounding box units
- if (e.hasAttribute("filterUnits") && e.attribute("filterUnits") != "objectBoundingBox") {
- return 0;
- }
-
- if (e.attribute("primitiveUnits") != "objectBoundingBox") {
- return 0;
- }
-
- // parse filter region rectangle
- QRectF filterRegion;
- filterRegion.setX(fromPercentage(e.attribute("x", "-0.1")));
- filterRegion.setY(fromPercentage(e.attribute("y", "-0.1")));
- filterRegion.setWidth(fromPercentage(e.attribute("width", "1.2")));
- filterRegion.setHeight(fromPercentage(e.attribute("height", "1.2")));
- filterStack->setClipRect(filterRegion);
-
- KoFilterEffectLoadingContext context;
-
- KoFilterEffectRegistry *registry = KoFilterEffectRegistry::instance();
-
- // create the filter effects and add them to the shape
- for (KoXmlNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
- KoXmlElement primitive = n.toElement();
- KoFilterEffect *filterEffect = registry->createFilterEffectFromXml(primitive, context);
- if (!filterEffect) {
- qWarning() << "filter effect" << primitive.tagName() << "is not implemented yet";
- continue;
- }
-
- // parse subregion
- qreal x = fromPercentage(primitive.attribute("x", "0"));
- qreal y = fromPercentage(primitive.attribute("y", "0"));
- qreal w = fromPercentage(primitive.attribute("width", "1"));
- qreal h = fromPercentage(primitive.attribute("height", "1"));
- QRectF subRegion(QPointF(x, y), QSizeF(w, h));
-
- if (primitive.hasAttribute("in")) {
- filterEffect->setInput(0, primitive.attribute("in"));
- }
- if (primitive.hasAttribute("result")) {
- filterEffect->setOutput(primitive.attribute("result"));
- }
-
- filterEffect->setFilterRect(subRegion);
-
- filterStack->appendFilterEffect(filterEffect);
- }
-
- return filterStack.take();
-}
-
-QByteArray FilterEffectResource::generateMD5() const
-{
- QByteArray ba = m_data.toByteArray();
- if (!ba.isEmpty()) {
- QCryptographicHash md5(QCryptographicHash::Md5);
- md5.addData(ba);
- return md5.result();
- }
- return ba;
-
-}
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectResource.h b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectResource.h
deleted file mode 100644
index 5cb4d1dfc2..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectResource.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* 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 FILTEREFFECTRESOURCE_H
-#define FILTEREFFECTRESOURCE_H
-
-#include <resources/KoResource.h>
-#include <KoXmlReader.h>
-#include <QDomDocument>
-
-class KoFilterEffectStack;
-
-class FilterEffectResource : public KoResource
-{
-public:
- explicit FilterEffectResource(const QString &filename);
-
- /// reimplemented from KoResource
- bool load() override;
-
- /// reimplemented from KoResource
- bool loadFromDevice(QIODevice *dev) override;
-
- /// reimplemented from KoResource
- bool save() override;
-
- /// reimplemented from KoResource
- bool saveToDevice(QIODevice *dev) const override;
-
- /// reimplemented from KoResource
- QString defaultFileExtension() const override;
-
- /// Creates resource from given filter effect stack
- static FilterEffectResource *fromFilterEffectStack(KoFilterEffectStack *filterStack);
-
- /// Creates a new filter stack from this filter resource
- KoFilterEffectStack *toFilterStack() const;
-protected:
- QByteArray generateMD5() const override;
-private:
- QDomDocument m_data;
-};
-
-#endif // FILTEREFFECTRESOURCE_H
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectScene.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectScene.cpp
deleted file mode 100644
index fca94d5be3..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectScene.cpp
+++ /dev/null
@@ -1,369 +0,0 @@
-/* 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.
- */
-
-#include "FilterEffectScene.h"
-#include "FilterEffectSceneItems.h"
-#include "KoShape.h"
-#include "KoFilterEffect.h"
-#include "KoFilterEffectStack.h"
-
-#include <QDebug>
-#include <kcombobox.h>
-
-#include <QGraphicsProxyWidget>
-#include <QPushButton>
-
-const qreal ItemSpacing = 10.0;
-const qreal ConnectionDistance = 10.0;
-
-ConnectionSource::ConnectionSource()
- : m_type(Effect)
- , m_effect(0)
-{
-}
-
-ConnectionSource::ConnectionSource(KoFilterEffect *effect, SourceType type)
- : m_type(type)
- , m_effect(effect)
-{
-}
-
-ConnectionSource::SourceType ConnectionSource::type() const
-{
- return m_type;
-}
-
-KoFilterEffect *ConnectionSource::effect() const
-{
- return m_effect;
-}
-
-ConnectionSource::SourceType ConnectionSource::typeFromString(const QString &str)
-{
- if (str == "SourceGraphic") {
- return SourceGraphic;
- } else if (str == "SourceAlpha") {
- return SourceAlpha;
- } else if (str == "BackgroundImage") {
- return BackgroundImage;
- } else if (str == "BackgroundAlpha") {
- return BackgroundAlpha;
- } else if (str == "FillPaint") {
- return FillPaint;
- } else if (str == "StrokePaint") {
- return StrokePaint;
- } else {
- return Effect;
- }
-}
-
-QString ConnectionSource::typeToString(SourceType type)
-{
- if (type == SourceGraphic) {
- return "SourceGraphic";
- } else if (type == SourceAlpha) {
- return "SourceAlpha";
- } else if (type == BackgroundImage) {
- return "BackgroundImage";
- } else if (type == BackgroundAlpha) {
- return "BackgroundAlpha";
- } else if (type == FillPaint) {
- return "FillPaint";
- } else if (type == StrokePaint) {
- return "StrokePaint";
- } else {
- return "";
- }
-}
-
-ConnectionTarget::ConnectionTarget()
- : m_inputIndex(0)
- , m_effect(0)
-{
-}
-
-ConnectionTarget::ConnectionTarget(KoFilterEffect *effect, int inputIndex)
- : m_inputIndex(inputIndex)
- , m_effect(effect)
-{
-}
-
-int ConnectionTarget::inputIndex() const
-{
- return m_inputIndex;
-}
-
-KoFilterEffect *ConnectionTarget::effect() const
-{
- return m_effect;
-}
-
-FilterEffectScene::FilterEffectScene(QObject *parent)
- : QGraphicsScene(parent)
- , m_effectStack(0)
-{
- m_defaultInputs << "SourceGraphic" << "SourceAlpha";
- m_defaultInputs << "FillPaint" << "StrokePaint";
- m_defaultInputs << "BackgroundImage" << "BackgroundAlpha";
-
- connect(this, SIGNAL(selectionChanged()), this, SLOT(selectionChanged()));
-}
-
-FilterEffectScene::~FilterEffectScene()
-{
-}
-
-void FilterEffectScene::initialize(KoFilterEffectStack *effectStack)
-{
- m_items.clear();
- m_connectionItems.clear();
- m_outputs.clear();
- clear();
-
- m_effectStack = effectStack;
-
- if (!m_effectStack) {
- return;
- }
-
- QList<KoFilterEffect *> filterEffects = m_effectStack->filterEffects();
- if (!filterEffects.count()) {
- return;
- }
-
- Q_FOREACH (KoFilterEffect *effect, filterEffects) {
- createEffectItems(effect);
- }
-
- layoutEffects();
- layoutConnections();
-}
-
-void FilterEffectScene::createEffectItems(KoFilterEffect *effect)
-{
- const bool isFirstItem = m_items.count() == 0;
- const QString defaultInput = isFirstItem ? "SourceGraphic" : m_items.last()->outputName();
-
- QList<QString> inputs = effect->inputs();
- for (int i = inputs.count(); i < effect->requiredInputCount(); ++i) {
- inputs.append(defaultInput);
- }
-
- QSet<QString> defaultItems;
- Q_FOREACH (const QString &currentInput, inputs) {
- const QString &input = currentInput.isEmpty() ? defaultInput : currentInput;
- if (m_defaultInputs.contains(input) && ! defaultItems.contains(input)) {
- DefaultInputItem *item = new DefaultInputItem(input, effect);
- addSceneItem(item);
- m_outputs.insert(item->outputName(), item);
- defaultItems.insert(input);
- }
- }
-
- EffectItem *effectItem = new EffectItem(effect);
-
- // create connections
- int index = 0;
- Q_FOREACH (const QString &currentInput, inputs) {
- const QString &input = currentInput.isEmpty() ? defaultInput : currentInput;
- EffectItemBase *outputItem = m_outputs.value(input, 0);
- if (outputItem) {
- ConnectionItem *connectionItem = new ConnectionItem(outputItem, effectItem, index);
- addSceneItem(connectionItem);
- }
- index++;
- }
-
- addSceneItem(effectItem);
-
- m_outputs.insert(effectItem->outputName(), effectItem);
-}
-
-void FilterEffectScene::addSceneItem(QGraphicsItem *item)
-{
- addItem(item);
- EffectItemBase *effectItem = dynamic_cast<EffectItemBase *>(item);
- if (effectItem) {
- m_items.append(effectItem);
- } else {
- ConnectionItem *connectionItem = dynamic_cast<ConnectionItem *>(item);
- if (connectionItem) {
- m_connectionItems.append(connectionItem);
- }
- }
-}
-
-void FilterEffectScene::layoutEffects()
-{
- QPointF position(25, 25);
- Q_FOREACH (EffectItemBase *item, m_items) {
- item->setPos(position);
- position.ry() += item->rect().height() + ItemSpacing;
- }
-}
-
-void FilterEffectScene::layoutConnections()
-{
- QList<QPair<int, int> > sortedConnections;
-
- // calculate connection sizes from item distances
- int connectionIndex = 0;
- Q_FOREACH (ConnectionItem *item, m_connectionItems) {
- int sourceIndex = m_items.indexOf(item->sourceItem());
- int targetIndex = m_items.indexOf(item->targetItem());
- sortedConnections.append(QPair<int, int>(targetIndex - sourceIndex, connectionIndex));
- connectionIndex++;
- }
-
- std::sort(sortedConnections.begin(), sortedConnections.end());
- qreal distance = ConnectionDistance;
- int lastSize = -1;
- int connectionCount = sortedConnections.count();
- for (int i = 0; i < connectionCount; ++i) {
- const QPair<int, int> &connection = sortedConnections[i];
-
- int size = connection.first;
- if (size > lastSize) {
- lastSize = size;
- distance += ConnectionDistance;
- }
-
- ConnectionItem *connectionItem = m_connectionItems[connection.second];
- if (!connectionItem) {
- continue;
- }
- EffectItemBase *sourceItem = connectionItem->sourceItem();
- EffectItemBase *targetItem = connectionItem->targetItem();
- if (!sourceItem || ! targetItem) {
- continue;
- }
-
- int targetInput = connectionItem->targetInput();
- QPointF sourcePos = sourceItem->mapToScene(sourceItem->outputPosition());
- QPointF targetPos = targetItem->mapToScene(targetItem->inputPosition(targetInput));
- QPainterPath path;
- path.moveTo(sourcePos + QPointF(0.5 * sourceItem->connectorSize().width(), 0));
- path.lineTo(sourcePos + QPointF(distance, 0));
- path.lineTo(targetPos + QPointF(distance, 0));
- path.lineTo(targetPos + QPointF(0.5 * targetItem->connectorSize().width(), 0));
- connectionItem->setPath(path);
- }
-}
-
-void FilterEffectScene::selectionChanged()
-{
- if (selectedItems().count()) {
- Q_FOREACH (EffectItemBase *item, m_items) {
- if (item->isSelected()) {
- item->setOpacity(1.0);
- } else {
- item->setOpacity(0.25);
- }
- }
- } else {
- Q_FOREACH (EffectItemBase *item, m_items) {
- item->setOpacity(1);
- }
- }
-}
-
-QList<ConnectionSource> FilterEffectScene::selectedEffectItems() const
-{
- QList<ConnectionSource> effectItems;
-
- QList<QGraphicsItem *> selectedGraphicsItems = selectedItems();
- if (!selectedGraphicsItems.count()) {
- return effectItems;
- }
- if (!m_items.count()) {
- return effectItems;
- }
-
- Q_FOREACH (QGraphicsItem *item, selectedGraphicsItems) {
- EffectItemBase *effectItem = dynamic_cast<EffectItemBase *>(item);
- if (!effectItem) {
- continue;
- }
-
- ConnectionSource::SourceType type = ConnectionSource::Effect;
-
- KoFilterEffect *effect = effectItem->effect();
- if (dynamic_cast<DefaultInputItem *>(item)) {
- type = ConnectionSource::typeFromString(effectItem->outputName());
- }
-
- effectItems.append(ConnectionSource(effect, type));
- }
-
- return effectItems;
-}
-
-void FilterEffectScene::dropEvent(QGraphicsSceneDragDropEvent *event)
-{
- ConnectorItem *dropTargetItem = 0;
- QList<QGraphicsItem *> itemsAtPositon = items(event->scenePos());
- Q_FOREACH (QGraphicsItem *item, itemsAtPositon) {
- dropTargetItem = dynamic_cast<ConnectorItem *>(item);
- if (dropTargetItem) {
- break;
- }
- }
- if (!dropTargetItem) {
- return;
- }
-
- const ConnectorMimeData *data = dynamic_cast<const ConnectorMimeData *>(event->mimeData());
- if (!data) {
- return;
- }
-
- ConnectorItem *dropSourceItem = data->connector();
- if (!dropSourceItem) {
- return;
- }
-
- EffectItemBase *outputParentItem = 0;
- KoFilterEffect *inputEffect = 0;
- KoFilterEffect *outputEffect = 0;
- int inputIndex = 0;
-
- if (dropTargetItem->connectorType() == ConnectorItem::Input) {
- // dropped output onto an input
- outputParentItem = dynamic_cast<EffectItemBase *>(dropSourceItem->parentItem());
- outputEffect = dropSourceItem->effect();
- inputEffect = dropTargetItem->effect();
- inputIndex = dropTargetItem->connectorIndex();
- } else {
- // dropped input onto an output
- outputParentItem = dynamic_cast<EffectItemBase *>(dropTargetItem->parentItem());
- outputEffect = dropTargetItem->effect();
- inputEffect = dropSourceItem->effect();
- inputIndex = dropSourceItem->connectorIndex();
- }
-
- ConnectionSource::SourceType outputType = ConnectionSource::Effect;
- // check if item with the output is a predefined one
- if (m_defaultInputs.contains(outputParentItem->outputName())) {
- outputType = ConnectionSource::typeFromString(outputParentItem->outputName());
- outputEffect = 0;
- }
- ConnectionSource source(outputEffect, outputType);
- ConnectionTarget target(inputEffect, inputIndex);
- emit connectionCreated(source, target);
-}
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectScene.h b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectScene.h
deleted file mode 100644
index e7092e5be5..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectScene.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/* 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 FILTEREFFECTSCENE_H
-#define FILTEREFFECTSCENE_H
-
-#include <QGraphicsScene>
-#include <QString>
-#include <QMap>
-
-class KoFilterEffect;
-class KoFilterEffectStack;
-class QGraphicsItem;
-class EffectItemBase;
-class EffectItem;
-class ConnectionItem;
-
-class ConnectionSource
-{
-public:
- enum SourceType {
- Effect, ///< a complete effect item
- SourceGraphic, ///< SourceGraphic predefined input image
- SourceAlpha, ///< SourceAlpha predefined input image
- BackgroundImage, ///< BackgroundImage predefined input image
- BackgroundAlpha, ///< BackgroundAlpha predefined input image
- FillPaint, ///< FillPaint predefined input image
- StrokePaint ///< StrokePaint predefined input image
- };
- ConnectionSource();
- ConnectionSource(KoFilterEffect *effect, SourceType type);
- /// Returns the source type
- SourceType type() const;
- /// Returns the corresponding filter effect, or 0 if type == Effect
- KoFilterEffect *effect() const;
-
- static SourceType typeFromString(const QString &str);
- static QString typeToString(SourceType type);
-
-private:
- SourceType m_type; ///< the source type
- KoFilterEffect *m_effect; ///< the corresponding effect if type == Effect, 0 otherwise
-};
-
-class ConnectionTarget
-{
-public:
- ConnectionTarget();
- ConnectionTarget(KoFilterEffect *effect, int inputIndex);
-
- /// Returns the target input index
- int inputIndex() const;
- /// Returns the corresponding filter effect
- KoFilterEffect *effect() const;
-
-private:
- int m_inputIndex; ///< the index of the input of the target effect
- KoFilterEffect *m_effect; ///< the target effect
-};
-
-class FilterEffectScene : public QGraphicsScene
-{
- Q_OBJECT
-public:
- explicit FilterEffectScene(QObject *parent = 0);
- ~FilterEffectScene() override;
-
- /// initializes the scene from the filter effect stack
- void initialize(KoFilterEffectStack *effectStack);
-
- /// Returns list of selected effect items
- QList<ConnectionSource> selectedEffectItems() const;
-
-Q_SIGNALS:
- void connectionCreated(ConnectionSource source, ConnectionTarget target);
-
-protected:
- /// reimplemented from QGraphicsScene
- void dropEvent(QGraphicsSceneDragDropEvent *event) override;
-
-private Q_SLOTS:
- void selectionChanged();
-private:
- void createEffectItems(KoFilterEffect *effect);
- void addSceneItem(QGraphicsItem *item);
- void layoutConnections();
- void layoutEffects();
-
- QList<QString> m_defaultInputs;
- KoFilterEffectStack *m_effectStack;
- QList<EffectItemBase *> m_items;
- QList<ConnectionItem *> m_connectionItems;
- QMap<QString, EffectItemBase *> m_outputs;
- QGraphicsProxyWidget *m_defaultInputProxy;
-};
-
-#endif // FILTEREFFECTSCENE_H
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectSceneItems.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectSceneItems.cpp
deleted file mode 100644
index c06d4059d3..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectSceneItems.cpp
+++ /dev/null
@@ -1,321 +0,0 @@
-/* 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.
- */
-
-#include "FilterEffectSceneItems.h"
-#include "KoFilterEffect.h"
-
-#include <QPen>
-#include <QBrush>
-#include <QFont>
-#include <QDrag>
-#include <QWidget>
-
-const QSizeF ConnectorSize = QSize(20, 20);
-const qreal ItemWidth = 15 * ConnectorSize.height();
-const qreal FontSize = 0.8 * ConnectorSize.height();
-
-ConnectorItem::ConnectorItem(ConnectorType type, int index, QGraphicsItem *parent)
- : QGraphicsEllipseItem(parent)
- , m_type(type)
- , m_index(index)
-{
- if (m_type == Output) {
- setBrush(QBrush(Qt::red));
- } else if (m_type == Input) {
- setBrush(QBrush(Qt::green));
- }
- setAcceptDrops(true);
- setRect(QRectF(QPointF(), ConnectorSize));
-}
-
-void ConnectorItem::setCenter(const QPointF &position)
-{
- QRectF r = rect();
- r.moveCenter(position);
- setRect(r);
-}
-
-ConnectorItem::ConnectorType ConnectorItem::connectorType()
-{
- return m_type;
-}
-
-int ConnectorItem::connectorIndex() const
-{
- return m_index;
-}
-
-KoFilterEffect *ConnectorItem::effect() const
-{
- if (!parentItem()) {
- return 0;
- }
- EffectItemBase *effectItem = dynamic_cast<EffectItemBase *>(parentItem());
- if (!effectItem) {
- return 0;
- }
-
- return effectItem->effect();
-}
-
-ConnectorMimeData::ConnectorMimeData(ConnectorItem *connector)
- : m_connector(connector)
-{
-}
-
-ConnectorItem *ConnectorMimeData::connector() const
-{
- return m_connector;
-}
-
-EffectItemBase::EffectItemBase(KoFilterEffect *effect)
- : QGraphicsRectItem(0), m_effect(effect)
-{
- setZValue(1);
- setFlags(QGraphicsItem::ItemIsSelectable);
- setAcceptDrops(true);
- setHandlesChildEvents(true);
-}
-
-void EffectItemBase::createText(const QString &text)
-{
- QGraphicsSimpleTextItem *textItem = new QGraphicsSimpleTextItem(text, this);
- QFont font = textItem->font();
- font.setPointSize(FontSize);
- textItem->setFont(font);
- QRectF textBox = textItem->boundingRect();
- QPointF offset = rect().center() - textBox.center();
- setTransform(QTransform::fromTranslate(offset.x(), offset.y()), true);
-}
-
-void EffectItemBase::createOutput(const QPointF &position, const QString &name)
-{
- ConnectorItem *connector = new ConnectorItem(ConnectorItem::Output, 0, this);
- connector->setCenter(position);
-
- m_outputPosition = position;
- m_outputName = name;
-}
-
-void EffectItemBase::createInput(const QPointF &position)
-{
- int inputCount = m_inputPositions.count();
- ConnectorItem *connector = new ConnectorItem(ConnectorItem::Input, inputCount, this);
- connector->setCenter(position);
-
- m_inputPositions.append(position);
-}
-
-QPointF EffectItemBase::outputPosition() const
-{
- return m_outputPosition;
-}
-
-QPointF EffectItemBase::inputPosition(int index) const
-{
- if (index < 0 || index >= m_inputPositions.count()) {
- return QPointF();
- }
- return m_inputPositions[index];
-}
-
-QString EffectItemBase::outputName() const
-{
- return m_outputName;
-}
-
-QSizeF EffectItemBase::connectorSize() const
-{
- return ConnectorSize;
-}
-
-KoFilterEffect *EffectItemBase::effect() const
-{
- return m_effect;
-}
-
-void EffectItemBase::mousePressEvent(QGraphicsSceneMouseEvent *event)
-{
- ConnectorItem *connector = connectorAtPosition(event->scenePos());
- if (!connector) {
- return;
- }
-
- ConnectorMimeData *data = new ConnectorMimeData(connector);
-
- QDrag *drag = new QDrag(event->widget());
- drag->setMimeData(data);
- drag->exec();
-}
-
-void EffectItemBase::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
-{
- event->ignore();
- ConnectorItem *targetItem = connectorAtPosition(event->scenePos());
- if (!targetItem) {
- return;
- }
-
- const ConnectorMimeData *data = dynamic_cast<const ConnectorMimeData *>(event->mimeData());
- if (!data) {
- return;
- }
-
- ConnectorItem *sourceItem = data->connector();
- int sourceItemType = sourceItem->connectorType();
- int targetItemType = targetItem->connectorType();
-
- if (sourceItemType == targetItemType) {
- return;
- }
-
- // do not accept connection within single effect item
- if (sourceItem->parentItem() == targetItem->parentItem()) {
- return;
- }
-
- if (sourceItemType == ConnectorItem::Input) {
- // we can only connect input with output above
- if (sourceItem->scenePos().y() < targetItem->scenePos().y()) {
- return;
- }
- }
- if (sourceItemType == ConnectorItem::Output) {
- // we can only connect output with input below
- if (sourceItem->scenePos().y() > targetItem->scenePos().y()) {
- return;
- }
- }
-
- event->accept();
-}
-
-void EffectItemBase::dropEvent(QGraphicsSceneDragDropEvent *event)
-{
- ConnectorItem *connector = connectorAtPosition(event->scenePos());
- if (!connector) {
- return;
- }
-
- const ConnectorMimeData *data = dynamic_cast<const ConnectorMimeData *>(event->mimeData());
- if (!data) {
- return;
- }
-}
-
-ConnectorItem *EffectItemBase::connectorAtPosition(const QPointF &scenePosition)
-{
- Q_FOREACH (QGraphicsItem *childItem, childItems()) {
- ConnectorItem *connector = dynamic_cast<ConnectorItem *>(childItem);
- if (!connector) {
- continue;
- }
- if (connector->contains(connector->mapFromScene(scenePosition))) {
- return connector;
- }
- }
-
- return 0;
-}
-
-DefaultInputItem::DefaultInputItem(const QString &name, KoFilterEffect *effect)
- : EffectItemBase(effect), m_name(name)
-{
- setRect(0, 0, ItemWidth, 2 * ConnectorSize.height());
-
- createOutput(QPointF(ItemWidth, 0.5 * rect().height()), name);
- createText(name);
-
- QLinearGradient g(QPointF(0, 0), QPointF(1, 1));
- g.setCoordinateMode(QGradient::ObjectBoundingMode);
- g.setColorAt(0, Qt::white);
- g.setColorAt(1, QColor(255, 168, 88));
- setBrush(QBrush(g));
-}
-
-EffectItem::EffectItem(KoFilterEffect *effect)
- : EffectItemBase(effect)
-{
- Q_ASSERT(effect);
- //QRectF circle(QPointF(), ConnectorSize);
-
- QPointF position(ItemWidth, ConnectorSize.height());
-
- // create input connectors
- int requiredInputCount = effect->requiredInputCount();
- int usedInputCount = qMax(requiredInputCount, effect->inputs().count());
- for (int i = 0; i < usedInputCount; ++i) {
- createInput(position);
- position.ry() += 1.5 * ConnectorSize.height();
- }
-
- // create a new input connector when maximal input count in not reached yet
- if (usedInputCount < effect->maximalInputCount()) {
- createInput(position);
- position.ry() += 1.5 * ConnectorSize.height();
- }
- // create output connector
- position.ry() += 0.5 * ConnectorSize.height();
- createOutput(position, effect->output());
-
- setRect(0, 0, ItemWidth, position.y() + ConnectorSize.height());
-
- createText(effect->id());
-
- QLinearGradient g(QPointF(0, 0), QPointF(1, 1));
- g.setCoordinateMode(QGradient::ObjectBoundingMode);
- g.setColorAt(0, Qt::white);
- g.setColorAt(1, QColor(0, 192, 192));
- setBrush(QBrush(g));
-}
-
-ConnectionItem::ConnectionItem(EffectItemBase *source, EffectItemBase *target, int targetInput)
- : QGraphicsPathItem(0)
- , m_source(source)
- , m_target(target)
- , m_targetInput(targetInput)
-{
- setPen(QPen(Qt::black));
-}
-
-EffectItemBase *ConnectionItem::sourceItem() const
-{
- return m_source;
-}
-
-EffectItemBase *ConnectionItem::targetItem() const
-{
- return m_target;
-}
-
-int ConnectionItem::targetInput() const
-{
- return m_targetInput;
-}
-
-void ConnectionItem::setSourceItem(EffectItemBase *source)
-{
- m_source = source;
-}
-
-void ConnectionItem::setTargetItem(EffectItemBase *target, int targetInput)
-{
- m_target = target;
- m_targetInput = targetInput;
-}
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectSceneItems.h b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectSceneItems.h
deleted file mode 100644
index f552009dca..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectSceneItems.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/* 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 FILTEREFFECTSCENEITEMS_H
-#define FILTEREFFECTSCENEITEMS_H
-
-#include <QGraphicsRectItem>
-#include <QGraphicsSceneMouseEvent>
-#include <QMimeData>
-
-class KoFilterEffect;
-
-/// Graphics item representing a connector (input/output)
-class ConnectorItem : public QGraphicsEllipseItem
-{
-public:
- enum ConnectorType { Input, Output };
-
- ConnectorItem(ConnectorType type, int index, QGraphicsItem *parent);
- void setCenter(const QPointF &position);
- ConnectorType connectorType();
- int connectorIndex() const;
- KoFilterEffect *effect() const;
-private:
- ConnectorType m_type;
- int m_index;
-};
-
-/// Custom mime data for connector drag and drop
-class ConnectorMimeData : public QMimeData
-{
-public:
- explicit ConnectorMimeData(ConnectorItem *connector);
- ConnectorItem *connector() const;
-private:
- ConnectorItem *m_connector;
-};
-
-/// Base class for effect items
-class EffectItemBase : public QGraphicsRectItem
-{
-public:
- explicit EffectItemBase(KoFilterEffect *effect);
-
- /// Returns the position of the output connector
- QPointF outputPosition() const;
-
- /// Returns the position of the specified input connector
- QPointF inputPosition(int index) const;
-
- /// Returns the name of the output
- QString outputName() const;
-
- /// Returns the size of the connectors
- QSizeF connectorSize() const;
-
- /// Returns the corresponding filter effect
- KoFilterEffect *effect() const;
-
-protected:
- void createText(const QString &text);
- void createOutput(const QPointF &position, const QString &name);
- void createInput(const QPointF &position);
-
- void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
- void dragMoveEvent(QGraphicsSceneDragDropEvent *event) override;
- void dropEvent(QGraphicsSceneDragDropEvent *event) override;
-
- ConnectorItem *connectorAtPosition(const QPointF &scenePosition);
-
-private:
- QPointF m_outputPosition;
- QString m_outputName;
- QList<QPointF> m_inputPositions;
- KoFilterEffect *m_effect;
-};
-
-/// Graphics item representing a predefined input image
-class DefaultInputItem : public EffectItemBase
-{
-public:
- DefaultInputItem(const QString &name, KoFilterEffect *effect);
-private:
- QString m_name;
-};
-
-/// Graphics item representing a effect primitive
-class EffectItem : public EffectItemBase
-{
-public:
- explicit EffectItem(KoFilterEffect *effect);
-};
-
-/// Graphics item representing an connection between an output and input
-class ConnectionItem : public QGraphicsPathItem
-{
-public:
- ConnectionItem(EffectItemBase *source, EffectItemBase *target, int targetInput);
-
- /// Returns the source item of the connection
- EffectItemBase *sourceItem() const;
- /// Returns the target item of the connection
- EffectItemBase *targetItem() const;
- /// Returns the input index of the target item
- int targetInput() const;
-
- /// Sets the source item
- void setSourceItem(EffectItemBase *source);
- /// Set the target item and the corresponding input index
- void setTargetItem(EffectItemBase *target, int targetInput);
-
-private:
- EffectItemBase *m_source;
- EffectItemBase *m_target;
- int m_targetInput;
-};
-
-#endif // FILTEREFFECTSCENEITEMS_H
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterInputChangeCommand.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterInputChangeCommand.cpp
deleted file mode 100644
index 45230ee4e2..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterInputChangeCommand.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/* 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.
- */
-
-#include "FilterInputChangeCommand.h"
-#include "KoFilterEffect.h"
-#include "KoShape.h"
-
-FilterInputChangeCommand::FilterInputChangeCommand(const InputChangeData &data, KoShape *shape, KUndo2Command *parent)
- : KUndo2Command(parent)
- , m_shape(shape)
-{
- m_data.append(data);
-}
-
-FilterInputChangeCommand::FilterInputChangeCommand(const QList<InputChangeData> &data, KoShape *shape, KUndo2Command *parent)
- : KUndo2Command(parent)
- , m_data(data)
- , m_shape(shape)
-{
-}
-
-void FilterInputChangeCommand::redo()
-{
- if (m_shape) {
- m_shape->update();
- }
-
- Q_FOREACH (const InputChangeData &data, m_data) {
- data.filterEffect->setInput(data.inputIndex, data.newInput);
- }
-
- if (m_shape) {
- m_shape->update();
- }
-
- KUndo2Command::redo();
-}
-
-void FilterInputChangeCommand::undo()
-{
- if (m_shape) {
- m_shape->update();
- }
-
- Q_FOREACH (const InputChangeData &data, m_data) {
- data.filterEffect->setInput(data.inputIndex, data.oldInput);
- }
-
- if (m_shape) {
- m_shape->update();
- }
-
- KUndo2Command::undo();
-}
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterInputChangeCommand.h b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterInputChangeCommand.h
deleted file mode 100644
index c31e975204..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterInputChangeCommand.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* 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 FILTERINPUTCHANGECOMMAND_H
-#define FILTERINPUTCHANGECOMMAND_H
-
-#include <kundo2command.h>
-
-class KoShape;
-class KoFilterEffect;
-
-struct InputChangeData {
- InputChangeData()
- : filterEffect(0), inputIndex(-1)
- {
- }
-
- InputChangeData(KoFilterEffect *effect, int index, const QString &oldIn, const QString &newIn)
- : filterEffect(effect), inputIndex(index), oldInput(oldIn), newInput(newIn)
- {
- }
-
- KoFilterEffect *filterEffect;
- int inputIndex;
- QString oldInput;
- QString newInput;
-};
-
-/// A command to change the input of a filter effect
-class FilterInputChangeCommand : public KUndo2Command
-{
-public:
- explicit FilterInputChangeCommand(const InputChangeData &data, KoShape *shape = 0, KUndo2Command *parent = 0);
-
- explicit FilterInputChangeCommand(const QList<InputChangeData> &data, KoShape *shape = 0, KUndo2Command *parent = 0);
-
- /// redo the command
- void redo() override;
- /// revert the actions done in redo
- void undo() override;
-private:
- QList<InputChangeData> m_data;
- KoShape *m_shape;
-};
-
-#endif // FILTERINPUTCHANGECOMMAND_H
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRegionChangeCommand.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRegionChangeCommand.cpp
deleted file mode 100644
index 8ad46ba6df..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRegionChangeCommand.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/* 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 "FilterRegionChangeCommand.h"
-#include "KoFilterEffect.h"
-#include "KoShape.h"
-
-FilterRegionChangeCommand::FilterRegionChangeCommand(KoFilterEffect *effect, const QRectF &filterRegion, KoShape *shape, KUndo2Command *parent)
- : KUndo2Command(parent)
- , m_effect(effect)
- , m_newRegion(filterRegion)
- , m_shape(shape)
-{
- Q_ASSERT(m_effect);
- m_oldRegion = m_effect->filterRect();
-}
-
-void FilterRegionChangeCommand::redo()
-{
- if (m_shape) {
- m_shape->update();
- }
-
- m_effect->setFilterRect(m_newRegion);
-
- if (m_shape) {
- m_shape->update();
- m_shape->notifyChanged();
- }
-
- KUndo2Command::redo();
-}
-
-void FilterRegionChangeCommand::undo()
-{
- if (m_shape) {
- m_shape->update();
- }
-
- m_effect->setFilterRect(m_oldRegion);
-
- if (m_shape) {
- m_shape->update();
- m_shape->notifyChanged();
- }
-
- KUndo2Command::undo();
-}
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRegionChangeCommand.h b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRegionChangeCommand.h
deleted file mode 100644
index 5080e597a5..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRegionChangeCommand.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* 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.
- */
-
-#ifndef FILTERREGIONCHANGECOMMAND_H
-#define FILTERREGIONCHANGECOMMAND_H
-
-#include <kundo2command.h>
-#include <QRectF>
-
-class KoShape;
-class KoFilterEffect;
-
-/// A command to change the region of a filter effect
-class FilterRegionChangeCommand : public KUndo2Command
-{
-public:
- /**
- * Creates new command to change filter region of a filter effect
- * @param effect the effect to change the filter region of
- * @param filterRegion the new filter region to set
- * @param shape the shape the filter effect is applied to
- * @param parent the parent undo command
- */
- explicit FilterRegionChangeCommand(KoFilterEffect *effect, const QRectF &filterRegion, KoShape *shape = 0, KUndo2Command *parent = 0);
-
- /// redo the command
- void redo() override;
- /// revert the actions done in redo
- void undo() override;
-
-private:
- KoFilterEffect *m_effect; ///< the filter effect we are working on
- QRectF m_oldRegion; ///< the old filter region
- QRectF m_newRegion; ///< the new filter region
- KoShape *m_shape; ///< the shape the effect is applied to, might be zero
-};
-
-#endif // FILTERREGIONCHANGECOMMAND_H
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRegionEditStrategy.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRegionEditStrategy.cpp
deleted file mode 100644
index cf1e766985..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRegionEditStrategy.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/* 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 "FilterRegionEditStrategy.h"
-#include "FilterRegionChangeCommand.h"
-#include <KoShape.h>
-#include <KoFilterEffect.h>
-#include <KoViewConverter.h>
-#include <QPainter>
-
-FilterRegionEditStrategy::FilterRegionEditStrategy(KoToolBase *parent, KoShape *shape, KoFilterEffect *effect, KarbonFilterEffectsTool::EditMode mode)
- : KoInteractionStrategy(parent)
- , m_effect(effect)
- , m_shape(shape)
- , m_editMode(mode)
-{
- Q_ASSERT(m_effect);
- Q_ASSERT(m_shape);
- // get the size rect of the shape
- m_sizeRect = QRectF(QPointF(), m_shape->size());
- // get the filter rectangle in shape coordinates
- m_filterRect = m_effect->filterRectForBoundingRect(m_sizeRect);
-}
-
-void FilterRegionEditStrategy::handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers)
-{
- Q_UNUSED(modifiers);
- QPointF shapePoint = m_shape->documentToShape(mouseLocation);
- if (m_lastPosition.isNull()) {
- m_lastPosition = shapePoint;
- }
- QPointF delta = shapePoint - m_lastPosition;
- if (delta.isNull()) {
- return;
- }
-
- switch (m_editMode) {
- case KarbonFilterEffectsTool::MoveAll:
- m_filterRect.translate(delta.x(), delta.y());
- break;
- case KarbonFilterEffectsTool::MoveLeft:
- m_filterRect.setLeft(m_filterRect.left() + delta.x());
- break;
- case KarbonFilterEffectsTool::MoveRight:
- m_filterRect.setRight(m_filterRect.right() + delta.x());
- break;
- case KarbonFilterEffectsTool::MoveTop:
- m_filterRect.setTop(m_filterRect.top() + delta.y());
- break;
- case KarbonFilterEffectsTool::MoveBottom:
- m_filterRect.setBottom(m_filterRect.bottom() + delta.y());
- break;
- default:
- // nothing to do here
- return;
- }
- tool()->repaintDecorations();
- m_lastPosition = shapePoint;
-}
-
-KUndo2Command *FilterRegionEditStrategy::createCommand()
-{
- qreal x = m_filterRect.left() / m_sizeRect.width();
- qreal y = m_filterRect.top() / m_sizeRect.height();
- qreal w = m_filterRect.width() / m_sizeRect.width();
- qreal h = m_filterRect.height() / m_sizeRect.height();
- return new FilterRegionChangeCommand(m_effect, QRectF(x, y, w, h), m_shape);
-}
-
-void FilterRegionEditStrategy::finishInteraction(Qt::KeyboardModifiers modifiers)
-{
- Q_UNUSED(modifiers);
-}
-
-void FilterRegionEditStrategy::paint(QPainter &painter, const KoViewConverter &converter)
-{
- Q_UNUSED(converter);
- // paint the filter subregion rect
- painter.setBrush(Qt::NoBrush);
- painter.setPen(Qt::red);
- painter.drawRect(m_filterRect);
-}
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRegionEditStrategy.h b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRegionEditStrategy.h
deleted file mode 100644
index 1f60cc5f02..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRegionEditStrategy.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* 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.
-*/
-
-#ifndef FILTERREGIONEDITSTRATEGY_H
-#define FILTERREGIONEDITSTRATEGY_H
-
-#include <KoInteractionStrategy.h>
-#include "KarbonFilterEffectsTool.h"
-
-#include <QRectF>
-
-class KoShape;
-class KoFilterEffect;
-
-class FilterRegionEditStrategy : public KoInteractionStrategy
-{
-public:
- FilterRegionEditStrategy(KoToolBase *parent, KoShape *shape, KoFilterEffect *effect, KarbonFilterEffectsTool::EditMode mode);
-
- // reimplemented from KoInteractionStrategy
- void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) override;
- // reimplemented from KoInteractionStrategy
- KUndo2Command *createCommand() override;
- // reimplemented from KoInteractionStrategy
- void finishInteraction(Qt::KeyboardModifiers modifiers) override;
- // reimplemented from KoInteractionStrategy
- void paint(QPainter &painter, const KoViewConverter &converter) override;
-
-private:
- KoFilterEffect *m_effect;
- KoShape *m_shape;
- QRectF m_sizeRect;
- QRectF m_filterRect;
- KarbonFilterEffectsTool::EditMode m_editMode;
- QPointF m_lastPosition;
-};
-
-#endif // FILTERREGIONEDITSTRATEGY_H
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRemoveCommand.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRemoveCommand.cpp
deleted file mode 100644
index a04816ef3f..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRemoveCommand.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/* 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.
- */
-
-#include "FilterRemoveCommand.h"
-#include "KoShape.h"
-#include "KoFilterEffect.h"
-#include "KoFilterEffectStack.h"
-
-#include <klocalizedstring.h>
-
-FilterRemoveCommand::FilterRemoveCommand(int filterEffectIndex, KoFilterEffectStack *filterStack, KoShape *shape, KUndo2Command *parent)
- : KUndo2Command(parent)
- , m_filterEffect(0)
- , m_filterStack(filterStack)
- , m_shape(shape)
- , m_isRemoved(false)
- , m_filterEffectIndex(filterEffectIndex)
-{
- Q_ASSERT(filterStack);
- setText(kundo2_i18n("Remove filter effect"));
-}
-
-FilterRemoveCommand::~FilterRemoveCommand()
-{
- if (m_isRemoved) {
- delete m_filterEffect;
- }
-}
-
-void FilterRemoveCommand::redo()
-{
- KUndo2Command::redo();
-
- if (m_shape) {
- m_shape->update();
- }
-
- m_filterEffect = m_filterStack->takeFilterEffect(m_filterEffectIndex);
- m_isRemoved = true;
-
- if (m_shape) {
- m_shape->update();
- }
-}
-
-void FilterRemoveCommand::undo()
-{
- if (m_shape) {
- m_shape->update();
- }
-
- m_filterStack->insertFilterEffect(m_filterEffectIndex, m_filterEffect);
- m_isRemoved = false;
-
- if (m_shape) {
- m_shape->update();
- }
-
- KUndo2Command::undo();
-}
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRemoveCommand.h b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRemoveCommand.h
deleted file mode 100644
index 3733b59917..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterRemoveCommand.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* 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 FILTERREMOVECOMMAND_H
-#define FILTERREMOVECOMMAND_H
-
-#include <kundo2command.h>
-
-class KoShape;
-class KoFilterEffect;
-class KoFilterEffectStack;
-
-/// A command do remove a filter effect from a filter effect stack
-class FilterRemoveCommand : public KUndo2Command
-{
-public:
- FilterRemoveCommand(int filterEffectIndex, KoFilterEffectStack *filterStack, KoShape *shape, KUndo2Command *parent = 0);
- ~FilterRemoveCommand() override;
- /// redo the command
- void redo() override;
- /// revert the actions done in redo
- void undo() override;
-
-private:
- KoFilterEffect *m_filterEffect;
- KoFilterEffectStack *m_filterStack;
- KoShape *m_shape;
- bool m_isRemoved;
- int m_filterEffectIndex;
-};
-
-#endif // FILTERREMOVECOMMAND_H
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterResourceServerProvider.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterResourceServerProvider.cpp
deleted file mode 100644
index 9c332fb299..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterResourceServerProvider.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/* 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>
-
- 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 "FilterResourceServerProvider.h"
-#include "FilterEffectResource.h"
-
-#include <QFileInfo>
-#include <QDir>
-
-#include <KoResourcePaths.h>
-#include <KoResourceServerProvider.h>
-#include <resources/KoSegmentGradient.h>
-#include <resources/KoStopGradient.h>
-
-FilterResourceServerProvider *FilterResourceServerProvider::m_singleton = 0;
-
-FilterResourceServerProvider::FilterResourceServerProvider()
-{
- m_filterEffectServer = new KoResourceServerSimpleConstruction<FilterEffectResource>("ko_effects", "*.svg");
- if (!QFileInfo(m_filterEffectServer->saveLocation()).exists()) {
- QDir().mkpath(m_filterEffectServer->saveLocation());
- }
- m_filterEffectServer->loadResources(KoResourceServerProvider::blacklistFileNames(m_filterEffectServer->fileNames(), m_filterEffectServer->blackListedFiles()));
- m_filterEffectServer->loadTags();
-}
-
-FilterResourceServerProvider::~FilterResourceServerProvider()
-{
- delete m_filterEffectServer;
-}
-
-FilterResourceServerProvider *FilterResourceServerProvider::instance()
-{
- if (FilterResourceServerProvider::m_singleton == 0) {
- FilterResourceServerProvider::m_singleton = new FilterResourceServerProvider();
- }
- return FilterResourceServerProvider::m_singleton;
-}
-
-KoResourceServer<FilterEffectResource> *FilterResourceServerProvider::filterEffectServer()
-{
- return m_filterEffectServer;
-}
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterResourceServerProvider.h b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterResourceServerProvider.h
deleted file mode 100644
index b429fa4be8..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterResourceServerProvider.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* 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>
-
- 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 FILTERRESOURCESERVERPROVIDER_H
-#define FILTERRESOURCESERVERPROVIDER_H
-
-#include "KoResourceServer.h"
-
-class FilterEffectResource;
-
-/// Provides resource server for filter effect resources
-class FilterResourceServerProvider : public QObject
-{
- Q_OBJECT
-
-public:
- ~FilterResourceServerProvider() override;
-
- static FilterResourceServerProvider *instance();
-
- KoResourceServer<FilterEffectResource> *filterEffectServer();
-
-private:
-
- FilterResourceServerProvider();
- FilterResourceServerProvider(const FilterResourceServerProvider &);
- FilterResourceServerProvider operator=(const FilterResourceServerProvider &);
-
- static FilterResourceServerProvider *m_singleton;
- KoResourceServer<FilterEffectResource> *m_filterEffectServer;
-};
-
-#endif // FILTERRESOURCESERVERPROVIDER_H
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterStackSetCommand.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterStackSetCommand.cpp
deleted file mode 100644
index 3a71a702b9..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterStackSetCommand.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/* 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.
- */
-
-#include "FilterStackSetCommand.h"
-#include "KoShape.h"
-#include "KoFilterEffectStack.h"
-
-#include <klocalizedstring.h>
-
-FilterStackSetCommand::FilterStackSetCommand(KoFilterEffectStack *newStack, KoShape *shape, KUndo2Command *parent)
- : KUndo2Command(parent)
- , m_newFilterStack(newStack)
- , m_shape(shape)
-{
- Q_ASSERT(m_shape);
- m_oldFilterStack = m_shape->filterEffectStack();
- if (m_newFilterStack) {
- m_newFilterStack->ref();
- }
- if (m_oldFilterStack) {
- m_oldFilterStack->ref();
- }
-
- setText(kundo2_i18n("Set filter stack"));
-}
-
-FilterStackSetCommand::~FilterStackSetCommand()
-{
- if (m_newFilterStack && !m_newFilterStack->deref()) {
- delete m_newFilterStack;
- }
- if (m_oldFilterStack && !m_oldFilterStack->deref()) {
- delete m_oldFilterStack;
- }
-}
-
-void FilterStackSetCommand::redo()
-{
- KUndo2Command::redo();
-
- m_shape->update();
- m_shape->setFilterEffectStack(m_newFilterStack);
- m_shape->update();
-}
-
-void FilterStackSetCommand::undo()
-{
- m_shape->update();
- m_shape->setFilterEffectStack(m_oldFilterStack);
- m_shape->update();
-
- KUndo2Command::undo();
-}
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterStackSetCommand.h b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterStackSetCommand.h
deleted file mode 100644
index 2b5dc2044b..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterStackSetCommand.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* 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 FILTERSTACKSETCOMMAND_H
-#define FILTERSTACKSETCOMMAND_H
-
-#include <kundo2command.h>
-
-class KoFilterEffectStack;
-class KoShape;
-
-/// Command to set a filter stack on a shape
-class FilterStackSetCommand : public KUndo2Command
-{
-public:
- FilterStackSetCommand(KoFilterEffectStack *newStack, KoShape *shape, KUndo2Command *parent = 0);
- ~FilterStackSetCommand() override;
-
- /// redo the command
- void redo() override;
- /// revert the actions done in redo
- void undo() override;
-
-private:
- KoFilterEffectStack *m_newFilterStack;
- KoFilterEffectStack *m_oldFilterStack;
- KoShape *m_shape;
-};
-
-#endif // FILTERSTACKSETCOMMAND_H
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/KarbonFilterEffectsTool.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/KarbonFilterEffectsTool.cpp
deleted file mode 100644
index 58833df01d..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/KarbonFilterEffectsTool.cpp
+++ /dev/null
@@ -1,553 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (c) 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 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 "KarbonFilterEffectsTool.h"
-
-#include "KoFilterEffect.h"
-#include "KoFilterEffectStack.h"
-#include "KoFilterEffectFactoryBase.h"
-#include "KoFilterEffectRegistry.h"
-#include "KoFilterEffectConfigWidgetBase.h"
-#include "KoCanvasBase.h"
-#include "KoDocumentResourceManager.h"
-#include "KoSelectedShapesProxy.h"
-#include "KoViewConverter.h"
-#include "KoSelection.h"
-#include "FilterEffectEditWidget.h"
-#include "FilterEffectResource.h"
-#include "FilterResourceServerProvider.h"
-#include "FilterStackSetCommand.h"
-#include "FilterRegionChangeCommand.h"
-#include "FilterRegionEditStrategy.h"
-#include "KoResourceServerAdapter.h"
-#include "KoResourceSelector.h"
-#include <KoPointerEvent.h>
-
-#include <KoIcon.h>
-
-#include <kcombobox.h>
-#include <klocalizedstring.h>
-#include <QDialog>
-#include <QSpinBox>
-
-#include <QWidget>
-#include <QGridLayout>
-#include <QToolButton>
-#include <QStackedWidget>
-#include <QLabel>
-#include <QDialogButtonBox>
-#include <QPushButton>
-#include <QVBoxLayout>
-
-#include "kis_double_parse_spin_box.h"
-
-class KarbonFilterEffectsTool::Private
-{
-public:
- Private()
- : filterSelector(0)
- , configSelector(0)
- , configStack(0)
- , posX(0)
- , posY(0)
- , posW(0)
- , posH(0)
- , clearButton(0)
- , currentEffect(0)
- , currentPanel(0)
- , currentShape(0)
- {
- }
-
- void fillConfigSelector(KoShape *shape, KarbonFilterEffectsTool *tool)
- {
- if (!configSelector) {
- return;
- }
-
- configSelector->clear();
- clearButton->setEnabled(false);
-
- if (!shape || !shape->filterEffectStack()) {
- addWidgetForEffect(0, tool);
- return;
- }
-
- configSelector->blockSignals(true);
-
- int index = 0;
- Q_FOREACH (KoFilterEffect *effect, shape->filterEffectStack()->filterEffects()) {
- configSelector->addItem(QString("%1 - ").arg(index) + effect->name());
- index++;
- }
-
- configSelector->blockSignals(false);
-
- KoFilterEffect *effect = index > 0 ? shape->filterEffectStack()->filterEffects().first() : 0;
-
- addWidgetForEffect(effect, tool);
- clearButton->setEnabled(shape->filterEffectStack() != 0);
- }
-
- void addWidgetForEffect(KoFilterEffect *filterEffect, KarbonFilterEffectsTool *tool)
- {
- // remove current widget if new effect is zero or effect type has changed
- if (!filterEffect || (currentEffect && filterEffect->id() != currentEffect->id())) {
- while (configStack->count()) {
- configStack->removeWidget(configStack->widget(0));
- }
- }
-
- if (!filterEffect) {
- currentEffect = 0;
- currentPanel = 0;
- } else if (!currentEffect || currentEffect->id() != filterEffect->id()) {
- // when a effect is set and is differs from the previous one
- // get the config widget and insert it into the option widget
- currentEffect = filterEffect;
-
- KoFilterEffectRegistry *registry = KoFilterEffectRegistry::instance();
- KoFilterEffectFactoryBase *factory = registry->value(currentEffect->id());
- if (!factory) {
- return;
- }
-
- currentPanel = factory->createConfigWidget();
- if (!currentPanel) {
- return;
- }
-
- currentPanel->layout()->setContentsMargins(0, 0, 0, 0);
- configStack->insertWidget(0, currentPanel);
- configStack->layout()->setContentsMargins(0, 0, 0, 0);
- connect(currentPanel, SIGNAL(filterChanged()), tool, SLOT(filterChanged()));
- }
-
- if (currentPanel) {
- currentPanel->editFilterEffect(filterEffect);
- }
-
- updateFilterRegion();
- }
-
- void updateFilterRegion()
- {
- QRectF region = currentEffect ? currentEffect->filterRect() : QRectF(0, 0, 0, 0);
-
- posX->blockSignals(true);
- posX->setValue(100.0 * region.x());
- posX->blockSignals(false);
- posX->setEnabled(currentEffect != 0);
- posY->blockSignals(true);
- posY->setValue(100.0 * region.y());
- posY->blockSignals(false);
- posY->setEnabled(currentEffect != 0);
- posW->blockSignals(true);
- posW->setValue(100.0 * region.width());
- posW->blockSignals(false);
- posW->setEnabled(currentEffect != 0);
- posH->blockSignals(true);
- posH->setValue(100.0 * region.height());
- posH->blockSignals(false);
- posH->setEnabled(currentEffect != 0);
- }
-
- EditMode editModeFromMousePosition(const QPointF &mousePosition, KarbonFilterEffectsTool *tool)
- {
- if (currentShape && currentShape->filterEffectStack() && currentEffect) {
- // get the size rect of the shape
- QRectF sizeRect(QPointF(), currentShape->size());
- // get the filter rectangle in shape coordinates
- QRectF filterRect = currentEffect->filterRectForBoundingRect(sizeRect);
- // get the transformation from document to shape coordinates
- QTransform transform = currentShape->absoluteTransformation().inverted();
- // adjust filter rectangle by grab sensitivity
- const int grabDistance = tool->grabSensitivity();
- QPointF border = tool->canvas()->viewConverter()->viewToDocument(QPointF(grabDistance, grabDistance));
- filterRect.adjust(-border.x(), -border.y(), border.x(), border.y());
- // map event point from document to shape coordinates
- QPointF shapePoint = transform.map(mousePosition);
- // check if the mouse is inside/near our filter rect
- if (filterRect.contains(shapePoint)) {
- if (qAbs(shapePoint.x() - filterRect.left()) <= border.x()) {
- return MoveLeft;
- } else if (qAbs(shapePoint.x() - filterRect.right()) <= border.x()) {
- return MoveRight;
- } else if (qAbs(shapePoint.y() - filterRect.top()) <= border.y()) {
- return MoveTop;
- } else if (qAbs(shapePoint.y() - filterRect.bottom()) <= border.y()) {
- return MoveBottom;
- } else {
- return MoveAll;
- }
- } else {
- return None;
- }
- }
- return None;
- }
-
- KoResourceSelector *filterSelector;
- KComboBox *configSelector;
- QStackedWidget *configStack;
- QDoubleSpinBox *posX;
- QDoubleSpinBox *posY;
- QDoubleSpinBox *posW;
- QDoubleSpinBox *posH;
- QToolButton *clearButton;
- KoFilterEffect *currentEffect;
- KoFilterEffectConfigWidgetBase *currentPanel;
- KoShape *currentShape;
-};
-
-KarbonFilterEffectsTool::KarbonFilterEffectsTool(KoCanvasBase *canvas)
- : KoInteractionTool(canvas)
- , d(new Private())
-{
- connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()),
- this, SLOT(selectionChanged()));
- connect(canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()),
- this, SLOT(selectionChanged()));
-}
-
-KarbonFilterEffectsTool::~KarbonFilterEffectsTool()
-{
- delete d;
-}
-
-void KarbonFilterEffectsTool::paint(QPainter &painter, const KoViewConverter &converter)
-{
- if (d->currentShape && d->currentShape->filterEffectStack()) {
- painter.save();
- // apply the shape transformation
-
- QTransform transform = d->currentShape->absoluteTransformation();
- painter.setTransform(transform * converter.documentToView(), true);
- // get the size rect of the shape
- QRectF sizeRect(QPointF(), d->currentShape->size());
- // get the clipping rect of the filter stack
- KoFilterEffectStack *filterStack = d->currentShape->filterEffectStack();
- QRectF clipRect = filterStack->clipRectForBoundingRect(sizeRect);
- // finally paint the clipping rect
- painter.setBrush(Qt::NoBrush);
- painter.setPen(Qt::blue);
- painter.drawRect(clipRect);
-
- if (currentStrategy()) {
- currentStrategy()->paint(painter, converter);
- } else if (d->currentEffect) {
- QRectF filterRect = d->currentEffect->filterRectForBoundingRect(sizeRect);
- // paint the filter subregion rect
- painter.setBrush(Qt::NoBrush);
- painter.setPen(Qt::red);
- painter.drawRect(filterRect);
- }
-
- painter.restore();
- }
-}
-
-void KarbonFilterEffectsTool::repaintDecorations()
-{
- if (d->currentShape && d->currentShape->filterEffectStack()) {
- QRectF bb = d->currentShape->boundingRect();
- const int radius = handleRadius();
- canvas()->updateCanvas(bb.adjusted(-radius, -radius, radius, radius));
- }
-}
-
-void KarbonFilterEffectsTool::activate(ToolActivation toolActivation, const QSet<KoShape *> &shapes)
-{
- Q_UNUSED(toolActivation);
- if (shapes.isEmpty()) {
- emit done();
- return;
- }
-
- d->currentShape = canvas()->selectedShapesProxy()->selection()->firstSelectedShape();
- d->fillConfigSelector(d->currentShape, this);
-}
-
-void KarbonFilterEffectsTool::mouseMoveEvent(KoPointerEvent *event)
-{
- if (currentStrategy()) {
- KoInteractionTool::mouseMoveEvent(event);
- } else {
- EditMode mode = d->editModeFromMousePosition(event->point, this);
- switch (mode) {
- case MoveAll:
- useCursor(Qt::SizeAllCursor);
- break;
- case MoveLeft:
- case MoveRight:
- useCursor(Qt::SizeHorCursor);
- break;
- case MoveTop:
- case MoveBottom:
- useCursor(Qt::SizeVerCursor);
- break;
- case None:
- useCursor(Qt::ArrowCursor);
- break;
- }
- }
-}
-
-KoInteractionStrategy *KarbonFilterEffectsTool::createStrategy(KoPointerEvent *event)
-{
- EditMode mode = d->editModeFromMousePosition(event->point, this);
- if (mode == None) {
- return 0;
- }
-
- return new FilterRegionEditStrategy(this, d->currentShape, d->currentEffect, mode);
-}
-
-void KarbonFilterEffectsTool::presetSelected(KoResource *resource)
-{
- if (!d->currentShape) {
- return;
- }
-
- FilterEffectResource *effectResource = dynamic_cast<FilterEffectResource *>(resource);
- if (!effectResource) {
- return;
- }
-
- KoFilterEffectStack *filterStack = effectResource->toFilterStack();
- if (!filterStack) {
- return;
- }
-
- canvas()->addCommand(new FilterStackSetCommand(filterStack, d->currentShape));
- d->fillConfigSelector(d->currentShape, this);
-}
-
-void KarbonFilterEffectsTool::editFilter()
-{
- QPointer<QDialog> dlg = new QDialog();
- dlg->setWindowTitle(i18n("Filter Effect Editor"));
- QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
- QWidget *mainWidget = new QWidget(0);
- QVBoxLayout *mainLayout = new QVBoxLayout;
- dlg->setLayout(mainLayout);
- mainLayout->addWidget(mainWidget);
- connect(buttonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()), dlg, SLOT(close()));
-
- FilterEffectEditWidget *editor = new FilterEffectEditWidget(dlg);
- editor->editShape(d->currentShape, canvas());
-
- mainLayout->addWidget(editor);
- mainLayout->addWidget(buttonBox);
- dlg->exec();
- delete dlg;
-
- d->fillConfigSelector(d->currentShape, this);
-}
-
-void KarbonFilterEffectsTool::clearFilter()
-{
- if (!d->currentShape) {
- return;
- }
- if (!d->currentShape->filterEffectStack()) {
- return;
- }
-
- canvas()->addCommand(new FilterStackSetCommand(0, d->currentShape));
-
- d->fillConfigSelector(d->currentShape, this);
-}
-
-void KarbonFilterEffectsTool::filterChanged()
-{
- if (!d->currentShape) {
- return;
- }
-
- d->currentShape->update();
-}
-
-void KarbonFilterEffectsTool::filterSelected(int index)
-{
- if (!d->currentShape || ! d->currentShape->filterEffectStack()) {
- return;
- }
-
- KoFilterEffect *effect = 0;
- QList<KoFilterEffect *> filterEffects = d->currentShape->filterEffectStack()->filterEffects();
- if (index >= 0 && index < filterEffects.count()) {
- effect = filterEffects[index];
- }
-
- d->addWidgetForEffect(effect, this);
-
- repaintDecorations();
-}
-
-void KarbonFilterEffectsTool::selectionChanged()
-{
- d->currentShape = canvas()->selectedShapesProxy()->selection()->firstSelectedShape();
- d->fillConfigSelector(d->currentShape, this);
-}
-
-void KarbonFilterEffectsTool::regionXChanged(double x)
-{
- if (!d->currentEffect) {
- return;
- }
-
- QRectF region = d->currentEffect->filterRect();
- region.setX(x / 100.0);
- canvas()->addCommand(new FilterRegionChangeCommand(d->currentEffect, region, d->currentShape));
-}
-
-void KarbonFilterEffectsTool::regionYChanged(double y)
-{
- if (!d->currentEffect) {
- return;
- }
-
- QRectF region = d->currentEffect->filterRect();
- region.setY(y / 100.0);
- canvas()->addCommand(new FilterRegionChangeCommand(d->currentEffect, region, d->currentShape));
-}
-
-void KarbonFilterEffectsTool::regionWidthChanged(double width)
-{
- if (!d->currentEffect) {
- return;
- }
-
- QRectF region = d->currentEffect->filterRect();
- region.setWidth(width / 100.0);
- canvas()->addCommand(new FilterRegionChangeCommand(d->currentEffect, region, d->currentShape));
-}
-
-void KarbonFilterEffectsTool::regionHeightChanged(double height)
-{
- if (!d->currentEffect) {
- return;
- }
-
- QRectF region = d->currentEffect->filterRect();
- region.setHeight(height / 100.0);
- canvas()->addCommand(new FilterRegionChangeCommand(d->currentEffect, region, d->currentShape));
-}
-
-QList<QPointer<QWidget> > KarbonFilterEffectsTool::createOptionWidgets()
-{
- QList<QPointer<QWidget> > widgets;
-
- FilterResourceServerProvider *serverProvider = FilterResourceServerProvider::instance();
- KoResourceServer<FilterEffectResource> *server = serverProvider->filterEffectServer();
- QSharedPointer<KoAbstractResourceServerAdapter> adapter(new KoResourceServerAdapter<FilterEffectResource>(server));
-
- //---------------------------------------------------------------------
-
- QWidget *addFilterWidget = new QWidget();
- addFilterWidget->setObjectName("AddEffect");
- QGridLayout *addFilterLayout = new QGridLayout(addFilterWidget);
-
- d->filterSelector = new KoResourceSelector(addFilterWidget);
- d->filterSelector->setResourceAdapter(adapter);
- d->filterSelector->setDisplayMode(KoResourceSelector::TextMode);
- d->filterSelector->setColumnCount(1);
- addFilterLayout->addWidget(new QLabel(i18n("Effects"), addFilterWidget), 0, 0);
- addFilterLayout->addWidget(d->filterSelector, 0, 1);
- connect(d->filterSelector, SIGNAL(resourceSelected(KoResource*)),
- this, SLOT(presetSelected(KoResource*)));
-
- connect(d->filterSelector, SIGNAL(resourceApplied(KoResource*)),
- this, SLOT(presetSelected(KoResource*)));
-
- QToolButton *editButton = new QToolButton(addFilterWidget);
- editButton->setIcon(koIcon("view-filter"));
- editButton->setToolTip(i18n("View and edit filter"));
- addFilterLayout->addWidget(editButton, 0, 2);
- connect(editButton, SIGNAL(clicked()), this, SLOT(editFilter()));
-
- d->clearButton = new QToolButton(addFilterWidget);
- d->clearButton->setIcon(koIcon("edit-delete"));
- d->clearButton->setToolTip(i18n("Remove filter from object"));
- addFilterLayout->addWidget(d->clearButton, 0, 3);
- connect(d->clearButton, SIGNAL(clicked()), this, SLOT(clearFilter()));
-
- addFilterWidget->setWindowTitle(i18n("Add Filter"));
- widgets.append(addFilterWidget);
-
- //---------------------------------------------------------------------
-
- QWidget *configFilterWidget = new QWidget();
- configFilterWidget->setObjectName("ConfigEffect");
- QGridLayout *configFilterLayout = new QGridLayout(configFilterWidget);
-
- d->configSelector = new KComboBox(configFilterWidget);
- configFilterLayout->addWidget(d->configSelector, 0, 0);
- connect(d->configSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(filterSelected(int)));
-
- d->configStack = new QStackedWidget(configFilterWidget);
- configFilterLayout->addWidget(d->configStack, 1, 0);
- configFilterLayout->setContentsMargins(0, 0, 0, 0);
-
- configFilterWidget->setWindowTitle(i18n("Effect Properties"));
- widgets.append(configFilterWidget);
-
- //---------------------------------------------------------------------
-
- QWidget *filterRegionWidget = new QWidget();
- filterRegionWidget->setObjectName("EffectRegion");
- QGridLayout *filterRegionLayout = new QGridLayout(filterRegionWidget);
-
- d->posX = new KisDoubleParseSpinBox(filterRegionWidget);
- d->posX->setSuffix(i18n("%"));
- connect(d->posX, SIGNAL(valueChanged(double)), this, SLOT(regionXChanged(double)));
- filterRegionLayout->addWidget(new QLabel(i18n("X:")), 0, 0);
- filterRegionLayout->addWidget(d->posX, 0, 1);
-
- d->posY = new KisDoubleParseSpinBox(filterRegionWidget);
- d->posY->setSuffix(i18n("%"));
- connect(d->posY, SIGNAL(valueChanged(double)), this, SLOT(regionYChanged(double)));
- filterRegionLayout->addWidget(new QLabel(i18n("Y:")), 1, 0);
- filterRegionLayout->addWidget(d->posY, 1, 1);
-
- d->posW = new KisDoubleParseSpinBox(filterRegionWidget);
- d->posW->setSuffix(i18n("%"));
- connect(d->posW, SIGNAL(valueChanged(double)), this, SLOT(regionWidthChanged(double)));
- filterRegionLayout->addWidget(new QLabel(i18n("W:")), 0, 2);
- filterRegionLayout->addWidget(d->posW, 0, 3);
-
- d->posH = new KisDoubleParseSpinBox(filterRegionWidget);
- d->posH->setSuffix(i18n("%"));
- connect(d->posH, SIGNAL(valueChanged(double)), this, SLOT(regionHeightChanged(double)));
- filterRegionLayout->addWidget(new QLabel(i18n("H:")), 1, 2);
- filterRegionLayout->addWidget(d->posH, 1, 3);
- filterRegionLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 2, 0);
- filterRegionLayout->setContentsMargins(0, 0, 0, 0);
-
- filterRegionWidget->setWindowTitle(i18n("Effect Region"));
- widgets.append(filterRegionWidget);
-
- //---------------------------------------------------------------------
-
- d->fillConfigSelector(d->currentShape, this);
-
- return widgets;
-}
-
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/KarbonFilterEffectsTool.h b/plugins/tools/karbonplugins/tools/filterEffectTool/KarbonFilterEffectsTool.h
deleted file mode 100644
index 965e3b17cd..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/KarbonFilterEffectsTool.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (c) 2009-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.
- */
-
-#ifndef KARBONFILTEREFFECTSTOOL_H
-#define KARBONFILTEREFFECTSTOOL_H
-
-#include "KoInteractionTool.h"
-
-class KoResource;
-class KoInteractionStrategy;
-
-class KarbonFilterEffectsTool : public KoInteractionTool
-{
- Q_OBJECT
-public:
- enum EditMode {
- None,
- MoveAll,
- MoveLeft,
- MoveRight,
- MoveTop,
- MoveBottom
- };
-
- explicit KarbonFilterEffectsTool(KoCanvasBase *canvas);
- ~KarbonFilterEffectsTool() override;
-
- /// reimplemented from KoToolBase
- void paint(QPainter &painter, const KoViewConverter &converter) override;
- /// reimplemented from KoToolBase
- void repaintDecorations() override;
- /// reimplemented from KoToolBase
- void mouseMoveEvent(KoPointerEvent *event) override;
-
- /// reimplemented from KoToolBase
- void activate(ToolActivation toolActivation, const QSet<KoShape *> &shapes) override;
-
-protected:
- /// reimplemented from KoToolBase
- QList<QPointer<QWidget> > createOptionWidgets() override;
- /// reimplemented from KoToolBase
- KoInteractionStrategy *createStrategy(KoPointerEvent *event) override;
-private Q_SLOTS:
- void editFilter();
- void clearFilter();
- void filterChanged();
- void filterSelected(int index);
- void selectionChanged();
- void presetSelected(KoResource *resource);
- void regionXChanged(double x);
- void regionYChanged(double y);
- void regionWidthChanged(double width);
- void regionHeightChanged(double height);
-private:
- class Private;
- Private *const d;
-};
-
-#endif // KARBONFILTEREFFECTSTOOL_H
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/KarbonFilterEffectsToolFactory.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/KarbonFilterEffectsToolFactory.cpp
deleted file mode 100644
index b5d1ca5dff..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/KarbonFilterEffectsToolFactory.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/* 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.
- */
-
-#include "KarbonFilterEffectsToolFactory.h"
-#include "KarbonFilterEffectsTool.h"
-
-#include <KoIcon.h>
-#include <klocalizedstring.h>
-
-KarbonFilterEffectsToolFactory::KarbonFilterEffectsToolFactory()
- : KoToolFactoryBase("KarbonFilterEffectsTool")
-{
- setToolTip(i18n("Filter effects editing"));
- setSection("karbon,krita");
- setIconName(koIconNameCStr("tool_imageeffects")); // TODO: better icon, e.g. black Fx bad on dark UI
- setPriority(3);
-}
-
-KarbonFilterEffectsToolFactory::~KarbonFilterEffectsToolFactory()
-{
-}
-
-KoToolBase *KarbonFilterEffectsToolFactory::createTool(KoCanvasBase *canvas)
-{
- return new KarbonFilterEffectsTool(canvas);
-}
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/KarbonFilterEffectsToolFactory.h b/plugins/tools/karbonplugins/tools/filterEffectTool/KarbonFilterEffectsToolFactory.h
deleted file mode 100644
index 821e2c6ca9..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/KarbonFilterEffectsToolFactory.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* 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 KARBONFILTEREFFECTSTOOLFACTORY_H
-#define KARBONFILTEREFFECTSTOOLFACTORY_H
-
-#include <KoToolFactoryBase.h>
-
-class KarbonFilterEffectsToolFactory : public KoToolFactoryBase
-{
-public:
- KarbonFilterEffectsToolFactory();
- ~KarbonFilterEffectsToolFactory() override;
- KoToolBase *createTool(KoCanvasBase *canvas) override;
-};
-
-#endif // KARBONFILTEREFFECTSTOOLFACTORY_H
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/KoResourceSelector.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/KoResourceSelector.cpp
deleted file mode 100644
index 7cb24e4d84..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/KoResourceSelector.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2008 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 "KoResourceSelector.h"
-#include <KoResourceServerAdapter.h>
-#include <KoResourceModel.h>
-#include <KoResourceItemView.h>
-#include <KoResourceItemDelegate.h>
-#include <QPainter>
-#include <QTableView>
-#include <QListView>
-#include <QHeaderView>
-#include <QMouseEvent>
-#include <QStyledItemDelegate>
-
-#include <WidgetsDebug.h>
-
-class Q_DECL_HIDDEN KoResourceSelector::Private
-{
-public:
- Private() : displayMode(ImageMode) {}
- DisplayMode displayMode;
-
- void updateIndex( KoResourceSelector * me )
- {
- KoResourceModel * resourceModel = qobject_cast<KoResourceModel*>(me->model());
- if (!resourceModel)
- return;
- if (!resourceModel->rowCount())
- return;
-
- int currentIndex = me->currentIndex();
- QModelIndex currentModelIndex = me->view()->currentIndex();
-
- if (currentIndex < 0 || !currentModelIndex.isValid()) {
- me->blockSignals(true);
- me->view()->setCurrentIndex( resourceModel->index( 0, 0 ) );
- me->setCurrentIndex(0);
- me->blockSignals(false);
- me->update();
- }
- }
-};
-
-KoResourceSelector::KoResourceSelector(QWidget * parent)
- : QComboBox( parent ), d( new Private() )
-{
- connect( this, SIGNAL(currentIndexChanged(int)),
- this, SLOT(indexChanged(int)) );
-
- setMouseTracking(true);
-}
-
-KoResourceSelector::KoResourceSelector( QSharedPointer<KoAbstractResourceServerAdapter> resourceAdapter, QWidget * parent )
- : QComboBox( parent ), d( new Private() )
-{
- Q_ASSERT(resourceAdapter);
-
- setView( new KoResourceItemView(this) );
- setModel( new KoResourceModel(resourceAdapter, this) );
- setItemDelegate( new KoResourceItemDelegate( this ) );
- setMouseTracking(true);
- d->updateIndex(this);
-
- connect( this, SIGNAL(currentIndexChanged(int)),
- this, SLOT(indexChanged(int)) );
-
- connect(resourceAdapter.data(), SIGNAL(resourceAdded(KoResource*)),
- this, SLOT(resourceAdded(KoResource*)));
- connect(resourceAdapter.data(), SIGNAL(removingResource(KoResource*)),
- this, SLOT(resourceRemoved(KoResource*)));
-}
-
-KoResourceSelector::~KoResourceSelector()
-{
- delete d;
-}
-
-void KoResourceSelector::paintEvent( QPaintEvent *pe )
-{
- QComboBox::paintEvent( pe );
-
- if (d->displayMode == ImageMode) {
- QStyleOptionComboBox option;
- option.initFrom( this );
- QRect r = style()->subControlRect( QStyle::CC_ComboBox, &option, QStyle::SC_ComboBoxEditField, this );
-
- QStyleOptionViewItem viewOption;
- viewOption.initFrom( this );
- viewOption.rect = r;
-
- QPainter painter( this );
- itemDelegate()->paint( &painter, viewOption, view()->currentIndex() );
- }
-}
-
-void KoResourceSelector::mousePressEvent( QMouseEvent * event )
-{
- QStyleOptionComboBox opt;
- opt.initFrom( this );
- opt.subControls = QStyle::SC_All;
- opt.activeSubControls = QStyle::SC_ComboBoxArrow;
- QStyle::SubControl sc = style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt,
- mapFromGlobal(event->globalPos()),
- this);
- // only clicking on combobox arrow shows popup,
- // otherwise the resourceApplied signal is send with the current resource
- if (sc == QStyle::SC_ComboBoxArrow)
- QComboBox::mousePressEvent( event );
- else {
- QModelIndex index = view()->currentIndex();
- if( ! index.isValid() )
- return;
-
- KoResource * resource = static_cast<KoResource*>( index.internalPointer() );
- if( resource )
- emit resourceApplied( resource );
- }
-}
-
-void KoResourceSelector::mouseMoveEvent( QMouseEvent * event )
-{
- QStyleOptionComboBox option;
- option.initFrom( this );
- QRect r = style()->subControlRect( QStyle::CC_ComboBox, &option, QStyle::SC_ComboBoxEditField, this );
- if (r.contains(event->pos()))
- setCursor(Qt::PointingHandCursor);
- else
- unsetCursor();
-}
-
-void KoResourceSelector::setResourceAdapter(QSharedPointer<KoAbstractResourceServerAdapter>resourceAdapter)
-{
- Q_ASSERT(resourceAdapter);
- setModel(new KoResourceModel(resourceAdapter, this));
- d->updateIndex(this);
-
- connect(resourceAdapter.data(), SIGNAL(resourceAdded(KoResource*)),
- this, SLOT(resourceAdded(KoResource*)));
- connect(resourceAdapter.data(), SIGNAL(removingResource(KoResource*)),
- this, SLOT(resourceRemoved(KoResource*)));
-}
-
-void KoResourceSelector::setDisplayMode(DisplayMode mode)
-{
- if (mode == d->displayMode)
- return;
-
- switch(mode) {
- case ImageMode:
- setItemDelegate(new KoResourceItemDelegate(this));
- setView( new KoResourceItemView(this) );
- break;
- case TextMode:
- setItemDelegate(new QStyledItemDelegate(this));
- setView(new QListView(this));
- break;
- }
-
- d->displayMode = mode;
- d->updateIndex(this);
-}
-
-void KoResourceSelector::setColumnCount( int columnCount )
-{
- KoResourceModel * resourceModel = qobject_cast<KoResourceModel*>(model());
- if (resourceModel)
- resourceModel->setColumnCount( columnCount );
-}
-
-void KoResourceSelector::setRowHeight( int rowHeight )
-{
- QTableView * tableView = qobject_cast<QTableView*>(view());
- if (tableView)
- tableView->verticalHeader()->setDefaultSectionSize( rowHeight );
-}
-
-void KoResourceSelector::indexChanged( int )
-{
- QModelIndex index = view()->currentIndex();
- if( ! index.isValid() )
- return;
-
- KoResource * resource = static_cast<KoResource*>( index.internalPointer() );
- if( resource )
- emit resourceSelected( resource );
-}
-
-void KoResourceSelector::resourceAdded(KoResource*)
-{
- d->updateIndex(this);
-}
-
-void KoResourceSelector::resourceRemoved(KoResource*)
-{
- d->updateIndex(this);
-}
diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/KoResourceSelector.h b/plugins/tools/karbonplugins/tools/filterEffectTool/KoResourceSelector.h
deleted file mode 100644
index 61507a0032..0000000000
--- a/plugins/tools/karbonplugins/tools/filterEffectTool/KoResourceSelector.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2008 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 KORESOURCESELECTOR_H
-#define KORESOURCESELECTOR_H
-
-#include <QComboBox>
-
-class QMouseEvent;
-class KoAbstractResourceServerAdapter;
-class KoResource;
-
-/**
- * A custom combobox widget for selecting resource items like gradients or patterns.
- */
-class KoResourceSelector : public QComboBox
-{
- Q_OBJECT
-public:
- enum DisplayMode {
- ImageMode, ///< Displays image of resources (default)
- TextMode ///< Displays name of resources
- };
-
- /**
- * Constructs a new resource selector.
- * @param parent the parent widget
- */
- explicit KoResourceSelector(QWidget *parent = 0);
-
- /**
- * Constructs a new resource selector showing the resources of the given resource adapter.
- * @param resourceAdapter the resource adapter providing the resources to display
- * @param parent the parent widget
- */
- explicit KoResourceSelector( QSharedPointer<KoAbstractResourceServerAdapter> resourceAdapter, QWidget * parent = 0 );
-
- /// Destroys the resource selector
- ~KoResourceSelector() override;
-
- /// Sets the resource adaptor to get resources from
- void setResourceAdapter(QSharedPointer<KoAbstractResourceServerAdapter>resourceAdapter);
-
- /// Sets the display mode
- void setDisplayMode(DisplayMode mode);
-
- /// Sets number of columns to display in the popup view
- void setColumnCount( int columnCount );
-
- /// Sets the height of the popup view rows
- void setRowHeight( int rowHeight );
-
-Q_SIGNALS:
- /// Emitted when a resource was selected
- void resourceSelected( KoResource * resource );
-
- /// Is emitted when the user has clicked on the current resource
- void resourceApplied( KoResource * resource );
-
-protected:
- /// reimplemented
- void paintEvent( QPaintEvent * ) override;
- /// reimplemented
- void mousePressEvent( QMouseEvent * ) override;
- /// reimplemented
- void mouseMoveEvent( QMouseEvent * event ) override;
-
-private Q_SLOTS:
- void indexChanged( int index );
- void resourceAdded(KoResource*);
- void resourceRemoved(KoResource*);
-private:
- class Private;
- Private * const d;
-};
-
-#endif // KORESOURCESELECTOR_H
diff --git a/plugins/tools/karbonplugins/tools/karbontools.qrc b/plugins/tools/karbonplugins/tools/karbontools.qrc
index b2323305b3..4b38d80356 100644
--- a/plugins/tools/karbonplugins/tools/karbontools.qrc
+++ b/plugins/tools/karbonplugins/tools/karbontools.qrc
@@ -1,7 +1,5 @@
<RCC>
<qresource prefix="/">
<file alias="calligraphy.png">22-actions-calligraphy.png</file>
- <file alias="pattern.png">22-actions-pattern.png</file>
- <file alias="tool_imageeffects.png">32-actions-tool_imageeffects.png</file>
</qresource>
</RCC>
diff --git a/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.cpp b/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.cpp
index 0d81311997..2cead87cc1 100644
--- a/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.cpp
+++ b/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.cpp
@@ -1,379 +1,378 @@
/*
* 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_tool_lazy_brush_options_widget.h"
#include "ui_kis_tool_lazy_brush_options_widget.h"
#include <KoColorSpaceRegistry.h>
#include "KisPaletteModel.h"
#include "kis_config.h"
#include <resources/KoColorSet.h>
#include "kis_canvas_resource_provider.h"
#include "kis_signal_auto_connection.h"
#include "lazybrush/kis_colorize_mask.h"
#include "kis_image.h"
#include "kis_signals_blocker.h"
#include "kis_signal_compressor.h"
#include "kis_layer_properties_icons.h"
struct KisToolLazyBrushOptionsWidget::Private
{
Private()
: baseNodeChangedCompressor(500, KisSignalCompressor::FIRST_ACTIVE)
{
}
Ui_KisToolLazyBrushOptionsWidget *ui;
KisPaletteModel *colorModel;
KisCanvasResourceProvider *provider;
KisSignalAutoConnectionsStore providerSignals;
KisSignalAutoConnectionsStore maskSignals;
KisColorizeMaskSP activeMask;
- KoColorSet colorSet;
+ KoColorSetSP colorSet {new KoColorSet(QString())};
int transparentColorIndex;
KisSignalCompressor baseNodeChangedCompressor;
};
KisToolLazyBrushOptionsWidget::KisToolLazyBrushOptionsWidget(KisCanvasResourceProvider *provider, QWidget *parent)
: QWidget(parent),
m_d(new Private)
{
m_d->ui = new Ui_KisToolLazyBrushOptionsWidget();
m_d->ui->setupUi(this);
m_d->colorModel = new KisPaletteModel(this);
m_d->ui->colorView->setPaletteModel(m_d->colorModel);
m_d->ui->colorView->setAllowModification(false); //people proly shouldn't be able to edit the colorentries themselves.
m_d->ui->colorView->setCrossedKeyword("transparent");
connect(m_d->ui->chkUseEdgeDetection, SIGNAL(toggled(bool)), SLOT(slotUseEdgeDetectionChanged(bool)));
connect(m_d->ui->intEdgeDetectionSize, SIGNAL(valueChanged(int)), SLOT(slotEdgeDetectionSizeChanged(int)));
connect(m_d->ui->intRadius, SIGNAL(valueChanged(int)), SLOT(slotRadiusChanged(int)));
connect(m_d->ui->intCleanUp, SIGNAL(valueChanged(int)), SLOT(slotCleanUpChanged(int)));
connect(m_d->ui->chkLimitToDevice, SIGNAL(toggled(bool)), SLOT(slotLimitToDeviceChanged(bool)));
m_d->ui->intEdgeDetectionSize->setRange(0, 100);
m_d->ui->intEdgeDetectionSize->setExponentRatio(2.0);
m_d->ui->intEdgeDetectionSize->setSuffix(i18n(" px"));
m_d->ui->intEdgeDetectionSize->setPrefix(i18n("Edge detection: "));
m_d->ui->intEdgeDetectionSize->setToolTip(
i18nc("@info:tooltip",
"Activate for images with vast solid areas. "
"Set the value to the width of the thinnest "
"lines on the image"));
m_d->ui->intRadius->setRange(0, 1000);
m_d->ui->intRadius->setExponentRatio(3.0);
m_d->ui->intRadius->setSuffix(i18n(" px"));
m_d->ui->intRadius->setPrefix(i18n("Gap close hint: "));
m_d->ui->intRadius->setToolTip(
i18nc("@info:tooltip",
"The mask will try to close non-closed contours "
"if the gap is smaller than \"Gap close hint\" value"));
m_d->ui->intCleanUp->setRange(0, 100);
m_d->ui->intCleanUp->setSuffix(i18n(" %"));
m_d->ui->intCleanUp->setPrefix(i18n("Clean up: "));
m_d->ui->intCleanUp->setToolTip(
i18nc("@info:tooltip",
"The mask will try to remove parts of the key strokes "
"that are placed outside the closed contours. 0% - no effect, 100% - max effect"));
connect(m_d->ui->colorView, SIGNAL(sigIndexSelected(QModelIndex)), this, SLOT(entrySelected(QModelIndex)));
connect(m_d->ui->btnTransparent, SIGNAL(toggled(bool)), this, SLOT(slotMakeTransparent(bool)));
connect(m_d->ui->btnRemove, SIGNAL(clicked()), this, SLOT(slotRemove()));
connect(m_d->ui->chkAutoUpdates, SIGNAL(toggled(bool)), m_d->ui->btnUpdate, SLOT(setDisabled(bool)));
connect(m_d->ui->btnUpdate, SIGNAL(clicked()), this, SLOT(slotUpdate()));
connect(m_d->ui->chkAutoUpdates, SIGNAL(toggled(bool)), this, SLOT(slotSetAutoUpdates(bool)));
connect(m_d->ui->chkShowKeyStrokes, SIGNAL(toggled(bool)), this, SLOT(slotSetShowKeyStrokes(bool)));
connect(m_d->ui->chkShowOutput, SIGNAL(toggled(bool)), this, SLOT(slotSetShowOutput(bool)));
connect(&m_d->baseNodeChangedCompressor, SIGNAL(timeout()), this, SLOT(slotUpdateNodeProperties()));
m_d->provider = provider;
- m_d->colorSet.setIsGlobal(false);
- m_d->colorSet.setIsEditable(true);
- m_d->colorModel->setPalette(&m_d->colorSet);
+ m_d->colorSet->setIsEditable(true);
+ m_d->colorModel->setPalette(m_d->colorSet);
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
m_d->colorModel->addEntry(KisSwatch(KoColor(Qt::red, cs), "color1"));
m_d->colorModel->addEntry(KisSwatch(KoColor(Qt::green, cs), "color2"));
m_d->colorModel->addEntry(KisSwatch(KoColor(Qt::blue, cs), "color3"));
}
KisToolLazyBrushOptionsWidget::~KisToolLazyBrushOptionsWidget()
{
}
void KisToolLazyBrushOptionsWidget::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
m_d->providerSignals.addConnection(
m_d->provider, SIGNAL(sigNodeChanged(KisNodeSP)),
this, SLOT(slotCurrentNodeChanged(KisNodeSP)));
m_d->providerSignals.addConnection(
m_d->provider, SIGNAL(sigFGColorChanged(KoColor)),
this, SLOT(slotCurrentFgColorChanged(KoColor)));
slotCurrentNodeChanged(m_d->provider->currentNode());
slotCurrentFgColorChanged(m_d->provider->fgColor());
}
void KisToolLazyBrushOptionsWidget::hideEvent(QHideEvent *event)
{
QWidget::hideEvent(event);
m_d->providerSignals.clear();
}
void KisToolLazyBrushOptionsWidget::entrySelected(QModelIndex index)
{
if (!index.isValid()) return;
if (!qvariant_cast<bool>(index.data(KisPaletteModel::CheckSlotRole))) return;
KisSwatch entry = m_d->colorModel->getEntry(index);
m_d->provider->setFGColor(entry.color());
int idxInList = m_d->activeMask->keyStrokesColors().colors.indexOf(entry.color());
if (idxInList != -1) {
const bool transparentChecked = idxInList == m_d->transparentColorIndex;
KisSignalsBlocker b(m_d->ui->btnTransparent);
m_d->ui->btnTransparent->setChecked(transparentChecked);
}
}
void KisToolLazyBrushOptionsWidget::slotCurrentFgColorChanged(const KoColor &color)
{
bool found = false;
QModelIndex candidateIdx = m_d->colorModel->indexForClosest(color);
if (m_d->colorModel->getEntry(candidateIdx).color() == color) {
found = true;
}
m_d->ui->btnRemove->setEnabled(found);
m_d->ui->btnTransparent->setEnabled(found);
if (!found) {
KisSignalsBlocker b(m_d->ui->btnTransparent);
m_d->ui->btnTransparent->setChecked(false);
}
QModelIndex newIndex = found ? candidateIdx : QModelIndex();
if (!found) {
m_d->ui->colorView->selectionModel()->clear();
}
if (newIndex.isValid() && newIndex != m_d->ui->colorView->currentIndex()) {
m_d->ui->colorView->setCurrentIndex(newIndex);
m_d->ui->colorView->update(newIndex);
}
}
void KisToolLazyBrushOptionsWidget::slotColorLabelsChanged()
{
m_d->colorModel->clear();
m_d->transparentColorIndex = -1;
if (m_d->activeMask) {
KisColorizeMask::KeyStrokeColors colors = m_d->activeMask->keyStrokesColors();
m_d->transparentColorIndex = colors.transparentIndex;
for (int i = 0; i < colors.colors.size(); i++) {
const QString name = i == m_d->transparentColorIndex ? "transparent" : "";
m_d->colorModel->addEntry(KisSwatch(colors.colors[i], name));
}
}
slotCurrentFgColorChanged(m_d->provider->fgColor());
}
void KisToolLazyBrushOptionsWidget::slotUpdateNodeProperties()
{
KisSignalsBlocker b1(m_d->ui->chkAutoUpdates,
m_d->ui->btnUpdate,
m_d->ui->chkShowKeyStrokes,
m_d->ui->chkShowOutput);
KisSignalsBlocker b2(m_d->ui->chkUseEdgeDetection,
m_d->ui->intEdgeDetectionSize,
m_d->ui->intRadius,
m_d->ui->intCleanUp,
m_d->ui->chkLimitToDevice);
// not implemented yet!
//m_d->ui->chkAutoUpdates->setEnabled(m_d->activeMask);
m_d->ui->chkAutoUpdates->setEnabled(false);
m_d->ui->chkAutoUpdates->setVisible(false);
bool value = false;
value = m_d->activeMask && KisLayerPropertiesIcons::nodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeNeedsUpdate, true).toBool();
m_d->ui->btnUpdate->setEnabled(m_d->activeMask && !m_d->ui->chkAutoUpdates->isChecked() && value);
value = m_d->activeMask && KisLayerPropertiesIcons::nodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, true).toBool();
m_d->ui->chkShowKeyStrokes->setEnabled(m_d->activeMask);
m_d->ui->chkShowKeyStrokes->setChecked(value);
value = m_d->activeMask && KisLayerPropertiesIcons::nodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeShowColoring, true).toBool();
m_d->ui->chkShowOutput->setEnabled(m_d->activeMask);
m_d->ui->chkShowOutput->setChecked(value);
m_d->ui->chkUseEdgeDetection->setEnabled(m_d->activeMask);
m_d->ui->chkUseEdgeDetection->setChecked(m_d->activeMask && m_d->activeMask->useEdgeDetection());
m_d->ui->intEdgeDetectionSize->setEnabled(m_d->activeMask && m_d->ui->chkUseEdgeDetection->isChecked());
m_d->ui->intEdgeDetectionSize->setValue(m_d->activeMask ? m_d->activeMask->edgeDetectionSize() : 4.0);
m_d->ui->intRadius->setEnabled(m_d->activeMask);
m_d->ui->intRadius->setValue(2 * (m_d->activeMask ? m_d->activeMask->fuzzyRadius() : 15));
m_d->ui->intCleanUp->setEnabled(m_d->activeMask);
m_d->ui->intCleanUp->setValue(100 * (m_d->activeMask ? m_d->activeMask->cleanUpAmount() : 0.7));
m_d->ui->chkLimitToDevice->setEnabled(m_d->activeMask);
m_d->ui->chkLimitToDevice->setChecked(m_d->activeMask && m_d->activeMask->limitToDeviceBounds());
}
void KisToolLazyBrushOptionsWidget::slotCurrentNodeChanged(KisNodeSP node)
{
m_d->maskSignals.clear();
KisColorizeMask *mask = dynamic_cast<KisColorizeMask*>(node.data());
m_d->activeMask = mask;
if (m_d->activeMask) {
m_d->maskSignals.addConnection(
m_d->activeMask, SIGNAL(sigKeyStrokesListChanged()),
this, SLOT(slotColorLabelsChanged()));
m_d->maskSignals.addConnection(
m_d->provider->currentImage(), SIGNAL(sigNodeChanged(KisNodeSP)),
this, SLOT(slotUpdateNodeProperties()));
}
slotColorLabelsChanged();
slotUpdateNodeProperties();
m_d->ui->colorView->setEnabled(m_d->activeMask);
}
void KisToolLazyBrushOptionsWidget::slotMakeTransparent(bool value)
{
KIS_ASSERT_RECOVER_RETURN(m_d->activeMask);
QModelIndex index = m_d->ui->colorView->currentIndex();
KisSwatch activeSwatch = m_d->colorModel->getEntry(index);
if (!index.isValid()) return;
int activeIndex = -1;
KisColorizeMask::KeyStrokeColors colors;
int i = 0;
- Q_FOREACH (const QString &groupName, m_d->colorSet.getGroupNames()) {
- KisSwatchGroup *group = m_d->colorSet.getGroup(groupName);
+ Q_FOREACH (const QString &groupName, m_d->colorSet->getGroupNames()) {
+ KisSwatchGroup *group = m_d->colorSet->getGroup(groupName);
Q_FOREACH (const KisSwatchGroup::SwatchInfo &info, group->infoList()) {
colors.colors << info.swatch.color();
if (activeSwatch == info.swatch) { activeIndex = i; }
i++;
}
}
colors.transparentIndex = value ? activeIndex : -1;
m_d->activeMask->setKeyStrokesColors(colors);
}
void KisToolLazyBrushOptionsWidget::slotRemove()
{
KIS_ASSERT_RECOVER_RETURN(m_d->activeMask);
QModelIndex index = m_d->ui->colorView->currentIndex();
if (!index.isValid()) return;
const KoColor color = m_d->colorModel->getEntry(index).color();
m_d->activeMask->removeKeyStroke(color);
}
void KisToolLazyBrushOptionsWidget::slotUpdate()
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask);
KisLayerPropertiesIcons::setNodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeNeedsUpdate, false, m_d->provider->currentImage());
}
void KisToolLazyBrushOptionsWidget::slotSetAutoUpdates(bool value)
{
// not implemented yet!
ENTER_FUNCTION() << ppVar(value);
}
void KisToolLazyBrushOptionsWidget::slotSetShowKeyStrokes(bool value)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask);
KisLayerPropertiesIcons::setNodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, value, m_d->provider->currentImage());
}
void KisToolLazyBrushOptionsWidget::slotSetShowOutput(bool value)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask);
KisLayerPropertiesIcons::setNodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeShowColoring, value, m_d->provider->currentImage());
}
void KisToolLazyBrushOptionsWidget::slotUseEdgeDetectionChanged(bool value)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask);
m_d->activeMask->setUseEdgeDetection(value);
m_d->ui->intEdgeDetectionSize->setEnabled(value);
}
void KisToolLazyBrushOptionsWidget::slotEdgeDetectionSizeChanged(int value)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask);
m_d->activeMask->setEdgeDetectionSize(value);
}
void KisToolLazyBrushOptionsWidget::slotRadiusChanged(int value)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask);
m_d->activeMask->setFuzzyRadius(0.5 * value);
}
void KisToolLazyBrushOptionsWidget::slotCleanUpChanged(int value)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask);
m_d->activeMask->setCleanUpAmount(qreal(value) / 100.0);
}
void KisToolLazyBrushOptionsWidget::slotLimitToDeviceChanged(bool value)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask);
m_d->activeMask->setLimitToDeviceBounds(value);
}
diff --git a/plugins/tools/tool_transform2/kis_liquify_paint_helper.cpp b/plugins/tools/tool_transform2/kis_liquify_paint_helper.cpp
index 93b0067db8..015ef10d32 100644
--- a/plugins/tools/tool_transform2/kis_liquify_paint_helper.cpp
+++ b/plugins/tools/tool_transform2/kis_liquify_paint_helper.cpp
@@ -1,142 +1,143 @@
/*
* 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_liquify_paint_helper.h"
+#include <QElapsedTimer>
+
#include "kis_algebra_2d.h"
#include "KoPointerEvent.h"
#include <brushengine/kis_paint_information.h>
#include "kis_painting_information_builder.h"
#include "kis_liquify_transform_worker.h"
#include <brushengine/kis_paintop_utils.h>
#include "kis_coordinates_converter.h"
#include "kis_liquify_paintop.h"
#include "kis_liquify_properties.h"
struct KisLiquifyPaintHelper::Private
{
Private(const KisCoordinatesConverter *_converter)
: converter(_converter),
infoBuilder(new KisConverterPaintingInformationBuilder(converter)),
hasPaintedAtLeastOnce(false)
{
}
KisPaintInformation previousPaintInfo;
QScopedPointer<KisLiquifyPaintop> paintOp;
KisDistanceInformation currentDistance;
const KisCoordinatesConverter *converter;
QScopedPointer<KisPaintingInformationBuilder> infoBuilder;
- QTime strokeTime;
+ QElapsedTimer strokeTime;
bool hasPaintedAtLeastOnce;
KisDistanceInformation previousDistanceInfo;
KisPaintOpUtils::PositionHistory lastOutlinePos;
void updatePreviousPaintInfo(const KisPaintInformation &info);
};
KisLiquifyPaintHelper::KisLiquifyPaintHelper(const KisCoordinatesConverter *converter)
: m_d(new Private(converter))
{
}
KisLiquifyPaintHelper::~KisLiquifyPaintHelper()
{
}
void KisLiquifyPaintHelper::Private::updatePreviousPaintInfo(const KisPaintInformation &info)
{
QPointF prevPos = lastOutlinePos.pushThroughHistory(info.pos(), converter->effectiveZoom());
qreal angle = KisAlgebra2D::directionBetweenPoints(prevPos, info.pos(), 0);
previousDistanceInfo =
KisDistanceInformation(prevPos, angle);
previousPaintInfo = info;
}
QPainterPath KisLiquifyPaintHelper::brushOutline(const KisLiquifyProperties &props)
{
KisPaintInformation::DistanceInformationRegistrar registrar =
m_d->previousPaintInfo.registerDistanceInformation(&m_d->previousDistanceInfo);
return KisLiquifyPaintop::brushOutline(props, m_d->previousPaintInfo);
}
void KisLiquifyPaintHelper::configurePaintOp(const KisLiquifyProperties &props,
KisLiquifyTransformWorker *worker)
{
m_d->paintOp.reset(new KisLiquifyPaintop(props, worker));
}
void KisLiquifyPaintHelper::startPaint(KoPointerEvent *event, const KoCanvasResourceProvider *manager)
{
KIS_ASSERT_RECOVER_RETURN(m_d->paintOp);
m_d->strokeTime.start();
KisPaintInformation pi =
m_d->infoBuilder->startStroke(event, m_d->strokeTime.elapsed(), manager);
m_d->updatePreviousPaintInfo(pi);
m_d->hasPaintedAtLeastOnce = false;
}
void KisLiquifyPaintHelper::continuePaint(KoPointerEvent *event)
{
KIS_ASSERT_RECOVER_RETURN(m_d->paintOp);
KisPaintInformation pi =
m_d->infoBuilder->continueStroke(event, m_d->strokeTime.elapsed());
KisPaintOpUtils::paintLine(*m_d->paintOp.data(),
m_d->previousPaintInfo,
pi,
&m_d->currentDistance,
false, false);
m_d->updatePreviousPaintInfo(pi);
m_d->hasPaintedAtLeastOnce = true;
}
bool KisLiquifyPaintHelper::endPaint(KoPointerEvent *event)
{
KIS_ASSERT_RECOVER(m_d->paintOp) { return false; }
if (!m_d->hasPaintedAtLeastOnce) {
KisPaintInformation pi =
m_d->infoBuilder->continueStroke(event, m_d->strokeTime.elapsed());
pi.paintAt(*m_d->paintOp.data(), &m_d->previousDistanceInfo);
}
m_d->paintOp.reset();
return !m_d->hasPaintedAtLeastOnce;
}
void KisLiquifyPaintHelper::hoverPaint(KoPointerEvent *event)
{
QPointF imagePoint = m_d->converter->documentToImage(event->pos());
KisPaintInformation pi = m_d->infoBuilder->hover(imagePoint, event);
m_d->updatePreviousPaintInfo(pi);
}
diff --git a/sdk/tests/qimage_based_test.h b/sdk/tests/qimage_based_test.h
index 453de817fc..b6cb7d8d45 100644
--- a/sdk/tests/qimage_based_test.h
+++ b/sdk/tests/qimage_based_test.h
@@ -1,329 +1,331 @@
/*
* 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 __QIMAGE_BASED_TEST_H
#define __QIMAGE_BASED_TEST_H
#ifndef USE_DOCUMENT
#define USE_DOCUMENT 1
#endif /* USE_DOCUMENT */
#include "testutil.h"
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoShapeContainer.h>
#include <KoShapeRegistry.h>
#include <KoShapeFactoryBase.h>
#if USE_DOCUMENT
#include "KisDocument.h"
#include "kis_shape_layer.h"
#else
#include "kis_filter_configuration.h"
#endif /* USE_DOCUMENT */
#include "kis_undo_stores.h"
#include "kis_image.h"
#include "kis_selection.h"
#include "kis_paint_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_transparency_mask.h"
#include "kis_clone_layer.h"
+#include <KisGlobalResourcesInterface.h>
#include "filter/kis_filter.h"
#include "filter/kis_filter_registry.h"
+#include "kis_filter_configuration.h"
#include "commands/kis_selection_commands.h"
namespace TestUtil
{
class QImageBasedTest
{
public:
QImageBasedTest(const QString &directoryName)
: m_directoryName(directoryName)
{
}
// you need to declare your own test function
// See KisProcessingTest for example
protected:
/**
* Creates a complex image connected to a surrogate undo store
*/
KisImageSP createImage(KisSurrogateUndoStore *undoStore) {
QImage sourceImage(fetchDataFileLazy("hakonepa.png"));
QRect imageRect = QRect(QPoint(0,0), sourceImage.size());
QRect transpRect(50,50,300,300);
QRect blurRect(66,66,300,300);
QPoint blurShift(34,34);
QPoint cloneShift(75,75);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(undoStore, imageRect.width(), imageRect.height(), cs, "merge test");
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
- KisFilterConfigurationSP configuration = filter->defaultConfiguration();
+ KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
Q_ASSERT(configuration);
- KisAdjustmentLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration, 0);
+ KisAdjustmentLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration->cloneWithResourcesSnapshot(), 0);
blur1->internalSelection()->clear();
blur1->internalSelection()->pixelSelection()->select(blurRect);
blur1->setX(blurShift.x());
blur1->setY(blurShift.y());
KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
paintLayer1->paintDevice()->convertFromQImage(sourceImage, 0, 0, 0);
KisCloneLayerSP cloneLayer1 =
new KisCloneLayer(paintLayer1, image, "clone1", OPACITY_OPAQUE_U8);
cloneLayer1->setX(cloneShift.x());
cloneLayer1->setY(cloneShift.y());
image->addNode(cloneLayer1);
image->addNode(blur1);
image->addNode(paintLayer1);
KisTransparencyMaskSP transparencyMask1 = new KisTransparencyMask();
transparencyMask1->setName("tmask1");
transparencyMask1->testingInitSelection(transpRect, paintLayer1);
image->addNode(transparencyMask1, paintLayer1);
return image;
}
/**
* Creates a simple image with one empty layer and connects it to
* a surrogate undo store
*/
KisImageSP createTrivialImage(KisSurrogateUndoStore *undoStore) {
QRect imageRect = QRect(0, 0, 640, 441);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(undoStore, imageRect.width(), imageRect.height(), cs, "merge test");
KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1);
return image;
}
void addGlobalSelection(KisImageSP image) {
QRect selectionRect(40,40,300,300);
KisSelectionSP selection = new KisSelection(new KisDefaultBounds(image));
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
pixelSelection->select(selectionRect);
KUndo2Command *cmd = new KisSetGlobalSelectionCommand(image, selection);
image->undoAdapter()->addCommand(cmd);
}
#if USE_DOCUMENT
void addShapeLayer(KisDocument *doc, KisImageSP image) {
KisShapeLayerSP shapeLayer = new KisShapeLayer(doc->shapeController(), image.data(), "shape", OPACITY_OPAQUE_U8);
image->addNode(shapeLayer);
KoShapeFactoryBase *f1 = KoShapeRegistry::instance()->get("StarShape");
KoShapeFactoryBase *f2 = KoShapeRegistry::instance()->get("RectangleShape");
KoShape *shape1 = f1->createDefaultShape();
KoShape *shape2 = f2->createDefaultShape();
shape1->setPosition(QPointF(100,100));
shape2->setPosition(QPointF(200,200));
shapeLayer->addShape(shape1);
shapeLayer->addShape(shape2);
QApplication::processEvents();
}
#endif /* USE_DOCUMENT*/
bool checkLayersInitial(KisImageWSP image, int baseFuzzyness = 0) {
QString prefix = "initial_with_selection";
QString prefix2 = findNode(image->root(), "shape") ? "_with_shape" : "";
return checkLayers(image, prefix + prefix2, baseFuzzyness);
}
bool checkLayersInitialRootOnly(KisImageWSP image, int baseFuzzyness = 0) {
QString prefix = "initial_with_selection";
QString prefix2 = findNode(image->root(), "shape") ? "_with_shape" : "";
return checkLayers(image, prefix + prefix2, baseFuzzyness, false);
}
/**
* Checks the content of image's layers against the set of
* QImages stored in @p prefix subfolder
*/
bool checkLayers(KisImageWSP image, const QString &prefix, int baseFuzzyness = 0, bool recursive = true) {
QVector<QImage> images;
QVector<QString> names;
fillNamesImages(image->root(), image->bounds(), images, names, recursive);
bool valid = true;
const int stackSize = images.size();
for(int i = 0; i < stackSize; i++) {
if(!checkOneQImage(images[i], prefix, names[i], baseFuzzyness)) {
valid = false;
}
}
return valid;
}
/**
* Checks the content of one image's layer against the QImage
* stored in @p prefix subfolder
*/
bool checkOneLayer(KisImageWSP image, KisNodeSP node, const QString &prefix, int baseFuzzyness = 0) {
QVector<QImage> images;
QVector<QString> names;
fillNamesImages(node, image->bounds(), images, names);
return checkOneQImage(images.first(), prefix, names.first(), baseFuzzyness);
}
// add default bounds param
bool checkOneDevice(KisPaintDeviceSP device,
const QString &prefix,
const QString &name,
int baseFuzzyness = 0)
{
QImage image = device->convertToQImage(0);
return checkOneQImage(image, prefix, name, baseFuzzyness);
}
KisNodeSP findNode(KisNodeSP root, const QString &name) {
return TestUtil::findNode(root, name);
}
private:
bool checkOneQImage(const QImage &image,
const QString &prefix,
const QString &name,
int baseFuzzyness)
{
QString realName = prefix + "_" + name + ".png";
QString expectedName = prefix + "_" + name + "_expected.png";
bool valid = true;
QString fullPath = fetchDataFileLazy(m_directoryName + QDir::separator() +
prefix + QDir::separator() + realName);
if (fullPath.isEmpty()) {
// Try without the testname subdirectory
fullPath = fetchDataFileLazy(prefix + QDir::separator() +
realName);
}
if (fullPath.isEmpty()) {
// Try without the prefix subdirectory
fullPath = fetchDataFileLazy(m_directoryName + QDir::separator() +
realName);
}
QImage ref(fullPath);
QPoint temp;
int fuzzy = baseFuzzyness;
{
QStringList terms = name.split('_');
if(terms[0] == "root" ||
terms[0] == "blur1" ||
terms[0] == "shape") {
fuzzy++;
}
}
if(ref != image &&
!TestUtil::compareQImages(temp, ref, image, fuzzy, fuzzy)) {
dbgKrita << "--- Wrong image:" << realName;
valid = false;
image.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + realName);
ref.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + expectedName);
}
return valid;
}
void fillNamesImages(KisNodeSP node, const QRect &rc,
QVector<QImage> &images,
QVector<QString> &names,
bool recursive = true) {
while (node) {
if(node->paintDevice()) {
names.append(node->name() + "_paintDevice");
images.append(node->paintDevice()->
convertToQImage(0, rc.x(), rc.y(),
rc.width(), rc.height()));
}
if(node->original() && node->original() != node->paintDevice()) {
names.append(node->name() + "_original");
images.append(node->original()->
convertToQImage(0, rc.x(), rc.y(),
rc.width(), rc.height()));
}
if(node->projection() && node->projection() != node->paintDevice()) {
names.append(node->name() + "_projection");
images.append(node->projection()->
convertToQImage(0, rc.x(), rc.y(),
rc.width(), rc.height()));
}
if (recursive) {
fillNamesImages(node->firstChild(), rc, images, names);
}
node = node->nextSibling();
}
}
private:
QString m_directoryName;
};
}
#endif /* __QIMAGE_BASED_TEST_H */
diff --git a/sdk/tests/stroke_testing_utils.cpp b/sdk/tests/stroke_testing_utils.cpp
index 76c4f5bd2c..8df5633fab 100644
--- a/sdk/tests/stroke_testing_utils.cpp
+++ b/sdk/tests/stroke_testing_utils.cpp
@@ -1,369 +1,370 @@
/*
* 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 "stroke_testing_utils.h"
#include <QtTest>
#include <QDir>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOpRegistry.h>
#include <brushengine/kis_paintop_preset.h>
#include <resources/KoPattern.h>
#include "kis_canvas_resource_provider.h"
#include "kis_image.h"
#include "kis_paint_device.h"
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include <KisViewManager.h>
#include "testutil.h"
+#include <KisGlobalResourcesInterface.h>
KisImageSP utils::createImage(KisUndoStore *undoStore, const QSize &imageSize) {
QRect imageRect(0,0,imageSize.width(),imageSize.height());
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(undoStore, imageRect.width(), imageRect.height(), cs, "stroke test");
KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisPaintLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisPaintLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisPaintLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisPaintLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
image->barrierLock();
image->addNode(paintLayer1);
image->addNode(paintLayer2);
image->addNode(paintLayer3);
image->addNode(paintLayer4);
image->addNode(paintLayer5);
image->unlock();
return image;
}
KoCanvasResourceProvider* utils::createResourceManager(KisImageWSP image,
KisNodeSP node,
const QString &presetFileName)
{
KoCanvasResourceProvider *manager = new KoCanvasResourceProvider();
KisViewManager::initializeResourceManager(manager);
QVariant i;
i.setValue(KoColor(Qt::black, image->colorSpace()));
manager->setResource(KoCanvasResourceProvider::ForegroundColor, i);
i.setValue(KoColor(Qt::white, image->colorSpace()));
manager->setResource(KoCanvasResourceProvider::BackgroundColor, i);
i.setValue(static_cast<void*>(0));
manager->setResource(KisCanvasResourceProvider::CurrentPattern, i);
manager->setResource(KisCanvasResourceProvider::CurrentGradient, i);
manager->setResource(KisCanvasResourceProvider::CurrentGeneratorConfiguration, i);
if(!node) {
node = image->root();
while(node && !dynamic_cast<KisPaintLayer*>(node.data())) {
node = node->firstChild();
}
Q_ASSERT(node && dynamic_cast<KisPaintLayer*>(node.data()));
}
i.setValue(node);
manager->setResource(KisCanvasResourceProvider::CurrentKritaNode, i);
KisPaintOpPresetSP preset;
if (!presetFileName.isEmpty()) {
QString fullFileName = TestUtil::fetchDataFileLazy(presetFileName);
- preset = new KisPaintOpPreset(fullFileName);
- bool presetValid = preset->load();
+ preset = KisPaintOpPresetSP(new KisPaintOpPreset(fullFileName));
+ bool presetValid = preset->load(KisGlobalResourcesInterface::instance());
Q_ASSERT(presetValid); Q_UNUSED(presetValid);
i.setValue(preset);
manager->setResource(KisCanvasResourceProvider::CurrentPaintOpPreset, i);
}
i.setValue(COMPOSITE_OVER);
manager->setResource(KisCanvasResourceProvider::CurrentCompositeOp, i);
i.setValue(false);
manager->setResource(KisCanvasResourceProvider::MirrorHorizontal, i);
i.setValue(false);
manager->setResource(KisCanvasResourceProvider::MirrorVertical, i);
i.setValue(1.0);
manager->setResource(KisCanvasResourceProvider::Opacity, i);
i.setValue(1.0);
manager->setResource(KisCanvasResourceProvider::HdrExposure, i);
return manager;
}
utils::StrokeTester::StrokeTester(const QString &name, const QSize &imageSize, const QString &presetFilename)
: m_name(name),
m_imageSize(imageSize),
m_presetFilename(presetFilename),
m_numIterations(1),
m_baseFuzziness(1)
{
}
utils::StrokeTester::~StrokeTester()
{
}
void utils::StrokeTester::setNumIterations(int value)
{
m_numIterations = value;
}
void utils::StrokeTester::setBaseFuzziness(int value)
{
m_baseFuzziness = value;
}
void utils::StrokeTester::testSimpleStroke()
{
testOneStroke(false, true, false, true);
}
int utils::StrokeTester::lastStrokeTime() const
{
return m_strokeTime;
}
void utils::StrokeTester::test()
{
testOneStroke(false, false, false);
testOneStroke(false, true, false);
testOneStroke(true, false, false);
testOneStroke(true, true, false);
// The same but with updates (compare against projection)
testOneStroke(false, false, false, true);
testOneStroke(false, true, false, true);
testOneStroke(true, false, false, true);
testOneStroke(true, true, false, true);
// The same, but with an external layer
testOneStroke(false, false, true);
testOneStroke(false, true, true);
testOneStroke(true, false, true);
testOneStroke(true, true, true);
}
void utils::StrokeTester::benchmark()
{
// not cancelled, indirect painting, internal, no updates, no qimage
doStroke(false, false, false, false);
}
void utils::StrokeTester::testSimpleStrokeNoVerification()
{
doStroke(false, false, true, false);
}
void utils::StrokeTester::testOneStroke(bool cancelled,
bool indirectPainting,
bool externalLayer,
bool testUpdates)
{
// TODO: indirectPainting option is not used anymore! The real value is
// taken from the preset!
QString testName = formatTestName(m_name,
cancelled,
indirectPainting,
externalLayer);
dbgKrita << "Testcase:" << testName
<< "(compare against " << (testUpdates ? "projection" : "layer") << ")";
QImage resultImage;
resultImage = doStroke(cancelled, externalLayer, testUpdates);
QImage refImage;
refImage.load(referenceFile(testName));
QPoint temp;
if(!TestUtil::compareQImages(temp, refImage, resultImage, m_baseFuzziness, m_baseFuzziness)) {
refImage.save(dumpReferenceFile(testName));
resultImage.save(resultFile(testName));
QFAIL("Images do not coincide");
}
}
QString utils::StrokeTester::formatTestName(const QString &baseName,
bool cancelled,
bool indirectPainting,
bool externalLayer)
{
QString result = baseName;
result += "_" + m_presetFilename;
result += indirectPainting ? "_indirect" : "_incremental";
result += cancelled ? "_cancelled" : "_finished";
result += externalLayer ? "_external" : "_internal";
return result;
}
QString utils::StrokeTester::referenceFile(const QString &testName)
{
QString path =
QString(FILES_DATA_DIR) + QDir::separator() +
m_name + QDir::separator();
path += testName;
path += ".png";
return path;
}
QString utils::StrokeTester::dumpReferenceFile(const QString &testName)
{
QString path = QString(FILES_OUTPUT_DIR) + QDir::separator();
path += testName;
path += "_expected";
path += ".png";
return path;
}
QString utils::StrokeTester::resultFile(const QString &testName)
{
QString path = QString(FILES_OUTPUT_DIR) + QDir::separator();
path += testName;
path += ".png";
return path;
}
QImage utils::StrokeTester::doStroke(bool cancelled,
bool externalLayer,
bool testUpdates,
bool needQImage)
{
KisImageSP image = utils::createImage(0, m_imageSize);
KoCanvasResourceProvider *manager = utils::createResourceManager(image, 0, m_presetFilename);
KisNodeSP currentNode;
for (int i = 0; i < m_numIterations; i++) {
modifyResourceManager(manager, image, i);
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image,
image->rootLayer()->firstChild(),
manager);
if(externalLayer) {
KisNodeSP externalNode = new KisPaintLayer(0, "extlyr", OPACITY_OPAQUE_U8, image->colorSpace());
resources->setCurrentNode(externalNode);
Q_ASSERT(resources->currentNode() == externalNode);
}
initImage(image, resources->currentNode(), i);
QElapsedTimer strokeTime;
strokeTime.start();
KisStrokeStrategy *stroke = createStroke(resources, image);
m_strokeId = image->startStroke(stroke);
addPaintingJobs(image, resources, i);
if(!cancelled) {
image->endStroke(m_strokeId);
}
else {
image->cancelStroke(m_strokeId);
}
image->waitForDone();
m_strokeTime = strokeTime.elapsed();
currentNode = resources->currentNode();
}
beforeCheckingResult(image, currentNode);
QImage resultImage;
if(needQImage) {
KisPaintDeviceSP device = testUpdates ?
image->projection() :
currentNode->paintDevice();
resultImage = device->convertToQImage(0, 0, 0, image->width(), image->height());
}
image = 0;
delete manager;
return resultImage;
}
void utils::StrokeTester::modifyResourceManager(KoCanvasResourceProvider *manager,
KisImageWSP image, int iteration)
{
Q_UNUSED(iteration);
modifyResourceManager(manager, image);
}
void utils::StrokeTester::modifyResourceManager(KoCanvasResourceProvider *manager,
KisImageWSP image)
{
Q_UNUSED(manager);
Q_UNUSED(image);
}
void utils::StrokeTester::initImage(KisImageWSP image, KisNodeSP activeNode, int iteration)
{
Q_UNUSED(iteration);
initImage(image, activeNode);
}
void utils::StrokeTester::initImage(KisImageWSP image, KisNodeSP activeNode)
{
Q_UNUSED(image);
Q_UNUSED(activeNode);
}
void utils::StrokeTester::addPaintingJobs(KisImageWSP image,
KisResourcesSnapshotSP resources,
int iteration)
{
Q_UNUSED(iteration);
addPaintingJobs(image, resources);
}
void utils::StrokeTester::addPaintingJobs(KisImageWSP image,
KisResourcesSnapshotSP resources)
{
Q_UNUSED(image);
Q_UNUSED(resources);
}
void utils::StrokeTester::beforeCheckingResult(KisImageWSP image, KisNodeSP activeNode)
{
Q_UNUSED(image);
Q_UNUSED(activeNode);
}
diff --git a/sdk/tests/ui_manager_test.h b/sdk/tests/ui_manager_test.h
index 2f3b891ac5..63224f510d 100644
--- a/sdk/tests/ui_manager_test.h
+++ b/sdk/tests/ui_manager_test.h
@@ -1,180 +1,181 @@
/*
* 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 __UI_MANAGER_TEST_H
#define __UI_MANAGER_TEST_H
#include "testutil.h"
#include "qimage_based_test.h"
#include "ksharedconfig.h"
#include <kis_filter_configuration.h>
#include <resources/KoPattern.h>
#include "KisResourceServerProvider.h"
#include "kis_canvas_resource_provider.h"
#include "kis_filter_strategy.h"
#include "kis_selection_manager.h"
#include "kis_node_manager.h"
#include "KisViewManager.h"
#include "KisView.h"
#include "KisPart.h"
#include <KisDocument.h>
#include <kis_action_manager.h>
#include "KisMainWindow.h"
#include "kis_selection_mask.h"
+#include <KisGlobalResourcesInterface.h>
namespace TestUtil
{
class UiManagerTest : public TestUtil::QImageBasedTest
{
public:
UiManagerTest(bool useSelection, bool useShapeLayer, const QString &testName)
: QImageBasedTest(testName) // "selection_manager_test"
{
undoStore = new KisSurrogateUndoStore();
image = createImage(undoStore);
part = KisPart::instance();
doc = qobject_cast<KisDocument*>(part->createDocument());
doc->setCurrentImage(image);
if(useSelection) addGlobalSelection(image);
if(useShapeLayer) addShapeLayer(doc, image);
image->initialRefreshGraph();
mainWindow = new KisMainWindow();
imageView = new KisView(doc, mainWindow->viewManager(), mainWindow);
view = new KisViewManager(mainWindow, mainWindow->actionCollection());
- KoPattern *newPattern = new KoPattern(fetchDataFileLazy("HR_SketchPaper_01.pat"));
- newPattern->load();
+ KoPatternSP newPattern(new KoPattern(fetchDataFileLazy("HR_SketchPaper_01.pat")));
+ newPattern->load(KisGlobalResourcesInterface::instance());
Q_ASSERT(newPattern->valid());
view->canvasResourceProvider()->slotPatternActivated(newPattern);
KoColor fgColor(Qt::black, image->colorSpace());
KoColor bgColor(Qt::white, image->colorSpace());
view->canvasResourceProvider()->blockSignals(true);
view->canvasResourceProvider()->setBGColor(bgColor);
view->canvasResourceProvider()->setFGColor(fgColor);
view->canvasResourceProvider()->setOpacity(1.0);
KisNodeSP paint1 = findNode(image->root(), "paint1");
Q_ASSERT(paint1);
imageView->setViewManager(view);
view->setCurrentView(imageView);
view->nodeManager()->slotUiActivatedNode(paint1);
selectionManager = view->selectionManager();
Q_ASSERT(selectionManager);
actionManager = view->actionManager();
Q_ASSERT(actionManager);
QVERIFY(checkLayersInitial());
}
~UiManagerTest() {
/**
* Here is a weird way of precessing pending events.
* This is needed for the dummies facade could process
* all the queued events telling it some nodes were
* added/deleted
*/
QApplication::processEvents();
QTest::qSleep(500);
QApplication::processEvents();
delete mainWindow;
delete doc;
/**
* The event queue may have up to 200k events
* by the time all the tests are finished. Removing
* all of them may last forever, so clear them after
* every single test is finished
*/
QApplication::removePostedEvents(0);
}
void checkUndo() {
undoStore->undo();
image->waitForDone();
QVERIFY(checkLayersInitial());
}
void checkDoubleUndo() {
undoStore->undo();
undoStore->undo();
image->waitForDone();
QVERIFY(checkLayersInitial());
}
void startConcurrentTask() {
KisFilterStrategy * filter = new KisBoxFilterStrategy();
QSize initialSize = image->size();
image->scaleImage(2 * initialSize, image->xRes(), image->yRes(), filter);
image->waitForDone();
image->scaleImage(initialSize, image->xRes(), image->yRes(), filter);
}
using QImageBasedTest::checkLayers;
bool checkLayers(const QString &name) {
return checkLayers(image, name);
}
using QImageBasedTest::checkLayersInitial;
bool checkLayersInitial() {
return checkLayersInitial(image);
}
bool checkLayersFuzzy(const QString &name) {
return checkLayers(image, name, 1);
}
bool checkSelectionOnly(const QString &name) {
KisNodeSP mask = dynamic_cast<const KisLayer*>(image->root().data())->selectionMask();
return checkOneLayer(image, mask, name);
}
bool checkNoSelection() {
KisNodeSP mask = dynamic_cast<const KisLayer*>(image->root().data())->selectionMask();
return !mask && !image->globalSelection();
}
KisImageSP image;
KisSelectionManager *selectionManager;
KisActionManager *actionManager;
KisSurrogateUndoStore *undoStore;
protected:
KisView *imageView;
KisViewManager *view;
KisDocument *doc;
KisPart *part;
KisMainWindow *mainWindow;
};
}
#endif /* __UI_MANAGER_TEST_H */