diff --git a/3rdparty/ext_qt/CMakeLists.txt b/3rdparty/ext_qt/CMakeLists.txt
index 130d39fc80..f0743de6e3 100644
--- a/3rdparty/ext_qt/CMakeLists.txt
+++ b/3rdparty/ext_qt/CMakeLists.txt
@@ -1,214 +1,217 @@
SET(EXTPREFIX_qt "${EXTPREFIX}")
if (WIN32)
list(APPEND _QT_conf -skip qt3d -skip qtactiveqt -skip qtcanvas3d
-skip qtconnectivity -skip qtdoc -skip qtenginio -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 -no-ssl
-no-openssl -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
#
-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)
+
ExternalProject_Add(
ext_qt
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
URL https://download.qt.io/archive/qt/5.9/5.9.3/single/qt-everywhere-opensource-src-5.9.3.zip
URL_HASH SHA1=2d3c53cd9dc76a479873548921a20d3d9b6fb9ac
PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/disable-wintab.diff
COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qtgui-private-headers.diff
COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0001-Don-t-request-the-MIME-image-every-time-Windows-asks.patch
COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0002-Hack-always-return-we-support-DIBV5.patch
COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0003-Hack-for-fullscreen-workaround.patch
COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qopengldebug-gles.patch
COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/gerrit-189539-ANGLE-mingw-fix.patch
COMMAND ${PATCH_COMMAND} -p1 -d qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/gerrit-212811_qtbase-angle-d3d11-warp-crash-fix.patch
INSTALL_DIR ${EXTPREFIX_qt}
CONFIGURE_COMMAND /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
)
elseif (NOT APPLE)
ExternalProject_Add(
ext_qt
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
URL https://download.qt.io/official_releases/qt/5.6/5.6.1-1/single/qt-everywhere-opensource-src-5.6.1-1.tar.gz
URL_MD5 8fdec6d657bc370bd3183d8fe8e9c47a
PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qt-no-motion-compression.diff
INSTALL_DIR ${EXTPREFIX_qt}
CONFIGURE_COMMAND /configure -prefix ${EXTPREFIX_qt} -opensource -confirm-license -nomake examples -no-sql-sqlite -no-openssl -no-qml-debug -no-mtdev -no-journald -no-syslog -no-nis -no-cups -no-tslib -no-directfb -no-linuxfb -no-libproxy -no-pch -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -qt-harfbuzz -qt-freetype -qt-xcb -qt-xkbcommon-x11 -optimized-qmake -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtandroidextras -skip qtserialport
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 -i ${CMAKE_CURRENT_SOURCE_DIR}/gerrit-166202.diff
- COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/macdeploy-qt.diff
- COMMAND ${PATCH_COMMAND} -p1 -b -d /qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/qtbase-configure.patch
- COMMAND ${PATCH_COMMAND} -p1 -b -d /qtbase/mkspecs/features/mac -i ${CMAKE_CURRENT_SOURCE_DIR}/mac-default.patch)
+ set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/macdeploy-qt.diff
+ #COMMAND ${PATCH_COMMAND} -p1 -b -d /qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/qtbase-configure.patch
+ #COMMAND ${PATCH_COMMAND} -p1 -b -d /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 -i ${CMAKE_CURRENT_SOURCE_DIR}/gerrit-166202.diff
- COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/macdeploy-qt.diff)
+ set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/macdeploy-qt.diff)
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
+ BUILD_IN_SOURCE ON
- URL https://download.qt.io/official_releases/qt/5.7/5.7.0/single/qt-everywhere-opensource-src-5.7.0.tar.gz
- URL_MD5 9a46cce61fc64c20c3ac0a0e0fa41b42
+ URL https://download.qt.io/development_releases/qt/5.10/5.10.0-beta3/single/qt-everywhere-src-5.10.0.tar.xz
+ URL_MD5 e809bf33732fb68d531e609979ba529f
PATCH_COMMAND ${ext_qt_PATCH_COMMAND}
INSTALL_DIR ${EXTPREFIX_qt}
- CONFIGURE_COMMAND /configure -confirm-license -opensource -nomake examples -no-openssl -no-compile-examples -qt-freetype -qt-harfbuzz -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtgraphicaleffects -skip qtlocation -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -prefix ${EXTPREFIX_qt}
+ CONFIGURE_COMMAND /configure
+ -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtsensors -skip qtserialport -skip qtwayland
+ -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -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 -prefix ${EXTPREFIX_qt}
BUILD_COMMAND ${PARALLEL_MAKE}
INSTALL_COMMAND make install
UPDATE_COMMAND ""
BUILD_IN_SOURCE 1
)
endif()
diff --git a/3rdparty/ext_qt/gerrit-166202.diff b/3rdparty/ext_qt/gerrit-166202.diff
deleted file mode 100644
index 233767ddaf..0000000000
--- a/3rdparty/ext_qt/gerrit-166202.diff
+++ /dev/null
@@ -1,1067 +0,0 @@
-diff --git a/qtbase/src/gui/opengl/qopenglengineshadermanager.cpp b/qtbase/src/gui/opengl/qopenglengineshadermanager.cpp
-index 4e3d14b..c633236 100644
---- a/qtbase/src/gui/opengl/qopenglengineshadermanager.cpp
-+++ b/qtbase/src/gui/opengl/qopenglengineshadermanager.cpp
-@@ -131,58 +131,116 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context)
-
- const char** code = qShaderSnippets; // shortcut
-
-- code[MainVertexShader] = qopenglslMainVertexShader;
-- code[MainWithTexCoordsVertexShader] = qopenglslMainWithTexCoordsVertexShader;
-- code[MainWithTexCoordsAndOpacityVertexShader] = qopenglslMainWithTexCoordsAndOpacityVertexShader;
--
-- code[UntransformedPositionVertexShader] = qopenglslUntransformedPositionVertexShader;
-- code[PositionOnlyVertexShader] = qopenglslPositionOnlyVertexShader;
-- code[ComplexGeometryPositionOnlyVertexShader] = qopenglslComplexGeometryPositionOnlyVertexShader;
-- code[PositionWithPatternBrushVertexShader] = qopenglslPositionWithPatternBrushVertexShader;
-- code[PositionWithLinearGradientBrushVertexShader] = qopenglslPositionWithLinearGradientBrushVertexShader;
-- code[PositionWithConicalGradientBrushVertexShader] = qopenglslPositionWithConicalGradientBrushVertexShader;
-- code[PositionWithRadialGradientBrushVertexShader] = qopenglslPositionWithRadialGradientBrushVertexShader;
-- code[PositionWithTextureBrushVertexShader] = qopenglslPositionWithTextureBrushVertexShader;
-- code[AffinePositionWithPatternBrushVertexShader] = qopenglslAffinePositionWithPatternBrushVertexShader;
-- code[AffinePositionWithLinearGradientBrushVertexShader] = qopenglslAffinePositionWithLinearGradientBrushVertexShader;
-- code[AffinePositionWithConicalGradientBrushVertexShader] = qopenglslAffinePositionWithConicalGradientBrushVertexShader;
-- code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader;
-- code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader;
--
-- code[MainFragmentShader_CMO] = qopenglslMainFragmentShader_CMO;
-- code[MainFragmentShader_CM] = qopenglslMainFragmentShader_CM;
-- code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO;
-- code[MainFragmentShader_M] = qopenglslMainFragmentShader_M;
-- code[MainFragmentShader_CO] = qopenglslMainFragmentShader_CO;
-- code[MainFragmentShader_C] = qopenglslMainFragmentShader_C;
-- code[MainFragmentShader_O] = qopenglslMainFragmentShader_O;
-- code[MainFragmentShader] = qopenglslMainFragmentShader;
-- code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays;
--
-- code[ImageSrcFragmentShader] = qopenglslImageSrcFragmentShader;
-- code[ImageSrcWithPatternFragmentShader] = qopenglslImageSrcWithPatternFragmentShader;
-- code[NonPremultipliedImageSrcFragmentShader] = qopenglslNonPremultipliedImageSrcFragmentShader;
-- code[GrayscaleImageSrcFragmentShader] = qopenglslGrayscaleImageSrcFragmentShader;
-- code[AlphaImageSrcFragmentShader] = qopenglslAlphaImageSrcFragmentShader;
-- code[CustomImageSrcFragmentShader] = qopenglslCustomSrcFragmentShader; // Calls "customShader", which must be appended
-- code[SolidBrushSrcFragmentShader] = qopenglslSolidBrushSrcFragmentShader;
-- if (context->isOpenGLES())
-- code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_ES;
-- else
-- code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_desktop;
-- code[TextureBrushSrcWithPatternFragmentShader] = qopenglslTextureBrushSrcWithPatternFragmentShader;
-- code[PatternBrushSrcFragmentShader] = qopenglslPatternBrushSrcFragmentShader;
-- code[LinearGradientBrushSrcFragmentShader] = qopenglslLinearGradientBrushSrcFragmentShader;
-- code[RadialGradientBrushSrcFragmentShader] = qopenglslRadialGradientBrushSrcFragmentShader;
-- code[ConicalGradientBrushSrcFragmentShader] = qopenglslConicalGradientBrushSrcFragmentShader;
-- code[ShockingPinkSrcFragmentShader] = qopenglslShockingPinkSrcFragmentShader;
--
-- code[NoMaskFragmentShader] = "";
-- code[MaskFragmentShader] = qopenglslMaskFragmentShader;
-- code[RgbMaskFragmentShaderPass1] = qopenglslRgbMaskFragmentShaderPass1;
-- code[RgbMaskFragmentShaderPass2] = qopenglslRgbMaskFragmentShaderPass2;
-- code[RgbMaskWithGammaFragmentShader] = ""; //###
-+ // Check if the user has requested an OpenGL 3.2 Core Profile or higher
-+ // and if so use GLSL 1.5 core shaders instead of legacy ones.
-+ const QSurfaceFormat &fmt = context->format();
-+ if (fmt.profile() == QSurfaceFormat::CoreProfile && fmt.version() >= qMakePair(3,2)) {
-+ code[MainVertexShader] = qopenglslMainVertexShader_core;
-+ code[MainWithTexCoordsVertexShader] = qopenglslMainWithTexCoordsVertexShader_core;
-+ code[MainWithTexCoordsAndOpacityVertexShader] = qopenglslMainWithTexCoordsAndOpacityVertexShader_core;
-+
-+ code[UntransformedPositionVertexShader] = qopenglslUntransformedPositionVertexShader_core;
-+ code[PositionOnlyVertexShader] = qopenglslPositionOnlyVertexShader_core;
-+ code[ComplexGeometryPositionOnlyVertexShader] = qopenglslComplexGeometryPositionOnlyVertexShader_core;
-+ code[PositionWithPatternBrushVertexShader] = qopenglslPositionWithPatternBrushVertexShader_core;
-+ code[PositionWithLinearGradientBrushVertexShader] = qopenglslPositionWithLinearGradientBrushVertexShader_core;
-+ code[PositionWithConicalGradientBrushVertexShader] = qopenglslPositionWithConicalGradientBrushVertexShader_core;
-+ code[PositionWithRadialGradientBrushVertexShader] = qopenglslPositionWithRadialGradientBrushVertexShader_core;
-+ code[PositionWithTextureBrushVertexShader] = qopenglslPositionWithTextureBrushVertexShader_core;
-+ code[AffinePositionWithPatternBrushVertexShader] = qopenglslAffinePositionWithPatternBrushVertexShader_core;
-+ code[AffinePositionWithLinearGradientBrushVertexShader] = qopenglslAffinePositionWithLinearGradientBrushVertexShader_core;
-+ code[AffinePositionWithConicalGradientBrushVertexShader] = qopenglslAffinePositionWithConicalGradientBrushVertexShader_core;
-+ code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader_core;
-+ code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader_core;
-+
-+ code[MainFragmentShader_CMO] = qopenglslMainFragmentShader_CMO_core;
-+ code[MainFragmentShader_CM] = qopenglslMainFragmentShader_CM_core;
-+ code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO_core;
-+ code[MainFragmentShader_M] = qopenglslMainFragmentShader_M_core;
-+ code[MainFragmentShader_CO] = qopenglslMainFragmentShader_CO_core;
-+ code[MainFragmentShader_C] = qopenglslMainFragmentShader_C_core;
-+ code[MainFragmentShader_O] = qopenglslMainFragmentShader_O_core;
-+ code[MainFragmentShader] = qopenglslMainFragmentShader_core;
-+ code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays_core;
-+
-+ code[ImageSrcFragmentShader] = qopenglslImageSrcFragmentShader_core;
-+ code[ImageSrcWithPatternFragmentShader] = qopenglslImageSrcWithPatternFragmentShader_core;
-+ code[NonPremultipliedImageSrcFragmentShader] = qopenglslNonPremultipliedImageSrcFragmentShader_core;
-+ code[GrayscaleImageSrcFragmentShader] = qopenglslGrayscaleImageSrcFragmentShader_core;
-+ code[AlphaImageSrcFragmentShader] = qopenglslAlphaImageSrcFragmentShader_core;
-+ code[CustomImageSrcFragmentShader] = qopenglslCustomSrcFragmentShader_core; // Calls "customShader", which must be appended
-+ code[SolidBrushSrcFragmentShader] = qopenglslSolidBrushSrcFragmentShader_core;
-+
-+ code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_desktop_core;
-+ code[TextureBrushSrcWithPatternFragmentShader] = qopenglslTextureBrushSrcWithPatternFragmentShader_core;
-+ code[PatternBrushSrcFragmentShader] = qopenglslPatternBrushSrcFragmentShader_core;
-+ code[LinearGradientBrushSrcFragmentShader] = qopenglslLinearGradientBrushSrcFragmentShader_core;
-+ code[RadialGradientBrushSrcFragmentShader] = qopenglslRadialGradientBrushSrcFragmentShader_core;
-+ code[ConicalGradientBrushSrcFragmentShader] = qopenglslConicalGradientBrushSrcFragmentShader_core;
-+ code[ShockingPinkSrcFragmentShader] = qopenglslShockingPinkSrcFragmentShader_core;
-+
-+ code[NoMaskFragmentShader] = "";
-+ code[MaskFragmentShader] = qopenglslMaskFragmentShader_core;
-+ code[RgbMaskFragmentShaderPass1] = qopenglslRgbMaskFragmentShaderPass1_core;
-+ code[RgbMaskFragmentShaderPass2] = qopenglslRgbMaskFragmentShaderPass2_core;
-+ code[RgbMaskWithGammaFragmentShader] = ""; //###
-+ } else {
-+ code[MainVertexShader] = qopenglslMainVertexShader;
-+ code[MainWithTexCoordsVertexShader] = qopenglslMainWithTexCoordsVertexShader;
-+ code[MainWithTexCoordsAndOpacityVertexShader] = qopenglslMainWithTexCoordsAndOpacityVertexShader;
-+
-+ code[UntransformedPositionVertexShader] = qopenglslUntransformedPositionVertexShader;
-+ code[PositionOnlyVertexShader] = qopenglslPositionOnlyVertexShader;
-+ code[ComplexGeometryPositionOnlyVertexShader] = qopenglslComplexGeometryPositionOnlyVertexShader;
-+ code[PositionWithPatternBrushVertexShader] = qopenglslPositionWithPatternBrushVertexShader;
-+ code[PositionWithLinearGradientBrushVertexShader] = qopenglslPositionWithLinearGradientBrushVertexShader;
-+ code[PositionWithConicalGradientBrushVertexShader] = qopenglslPositionWithConicalGradientBrushVertexShader;
-+ code[PositionWithRadialGradientBrushVertexShader] = qopenglslPositionWithRadialGradientBrushVertexShader;
-+ code[PositionWithTextureBrushVertexShader] = qopenglslPositionWithTextureBrushVertexShader;
-+ code[AffinePositionWithPatternBrushVertexShader] = qopenglslAffinePositionWithPatternBrushVertexShader;
-+ code[AffinePositionWithLinearGradientBrushVertexShader] = qopenglslAffinePositionWithLinearGradientBrushVertexShader;
-+ code[AffinePositionWithConicalGradientBrushVertexShader] = qopenglslAffinePositionWithConicalGradientBrushVertexShader;
-+ code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader;
-+ code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader;
-+
-+ code[MainFragmentShader_CMO] = qopenglslMainFragmentShader_CMO;
-+ code[MainFragmentShader_CM] = qopenglslMainFragmentShader_CM;
-+ code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO;
-+ code[MainFragmentShader_M] = qopenglslMainFragmentShader_M;
-+ code[MainFragmentShader_CO] = qopenglslMainFragmentShader_CO;
-+ code[MainFragmentShader_C] = qopenglslMainFragmentShader_C;
-+ code[MainFragmentShader_O] = qopenglslMainFragmentShader_O;
-+ code[MainFragmentShader] = qopenglslMainFragmentShader;
-+ code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays;
-+
-+ code[ImageSrcFragmentShader] = qopenglslImageSrcFragmentShader;
-+ code[ImageSrcWithPatternFragmentShader] = qopenglslImageSrcWithPatternFragmentShader;
-+ code[NonPremultipliedImageSrcFragmentShader] = qopenglslNonPremultipliedImageSrcFragmentShader;
-+ code[GrayscaleImageSrcFragmentShader] = qopenglslGrayscaleImageSrcFragmentShader;
-+ code[AlphaImageSrcFragmentShader] = qopenglslAlphaImageSrcFragmentShader;
-+ code[CustomImageSrcFragmentShader] = qopenglslCustomSrcFragmentShader; // Calls "customShader", which must be appended
-+ code[SolidBrushSrcFragmentShader] = qopenglslSolidBrushSrcFragmentShader;
-+ if (context->isOpenGLES())
-+ code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_ES;
-+ else
-+ code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_desktop;
-+ code[TextureBrushSrcWithPatternFragmentShader] = qopenglslTextureBrushSrcWithPatternFragmentShader;
-+ code[PatternBrushSrcFragmentShader] = qopenglslPatternBrushSrcFragmentShader;
-+ code[LinearGradientBrushSrcFragmentShader] = qopenglslLinearGradientBrushSrcFragmentShader;
-+ code[RadialGradientBrushSrcFragmentShader] = qopenglslRadialGradientBrushSrcFragmentShader;
-+ code[ConicalGradientBrushSrcFragmentShader] = qopenglslConicalGradientBrushSrcFragmentShader;
-+ code[ShockingPinkSrcFragmentShader] = qopenglslShockingPinkSrcFragmentShader;
-+
-+ code[NoMaskFragmentShader] = "";
-+ code[MaskFragmentShader] = qopenglslMaskFragmentShader;
-+ code[RgbMaskFragmentShaderPass1] = qopenglslRgbMaskFragmentShaderPass1;
-+ code[RgbMaskFragmentShaderPass2] = qopenglslRgbMaskFragmentShaderPass2;
-+ code[RgbMaskWithGammaFragmentShader] = ""; //###
-+ }
-
-+ // These shaders are not implemented yet and therefore are the same
-+ // for all profiles. Implementations should make a version for both
-+ // profiles and put the appropriate lines in the if-statement above.
- code[NoCompositionModeFragmentShader] = "";
- code[MultiplyCompositionModeFragmentShader] = ""; //###
- code[ScreenCompositionModeFragmentShader] = ""; //###
-diff --git a/qtbase/src/gui/opengl/qopenglengineshadersource_p.h b/qtbase/src/gui/opengl/qopenglengineshadersource_p.h
-index 876d277..73aeb79 100644
---- a/qtbase/src/gui/opengl/qopenglengineshadersource_p.h
-+++ b/qtbase/src/gui/opengl/qopenglengineshadersource_p.h
-@@ -56,8 +56,6 @@
-
- QT_BEGIN_NAMESPACE
-
--
--
- static const char* const qopenglslMainVertexShader = "\n\
- void setPosition(); \n\
- void main(void) \n\
-@@ -531,40 +529,498 @@ static const char* const qopenglslRgbMaskFragmentShaderPass2 = "\n\
- ExclusionCompositionModeFragmentShader,
- */
-
--// OpenGL 3.2 core profile versions of shaders that are used by QOpenGLTextureGlyphCache
-+/*
-+ OpenGL 3.2+ Core Profile shaders
-+ The following shader snippets are copies of the snippets above
-+ but use the modern GLSL 1.5 keywords. New shaders should make
-+ a snippet for both profiles and add them appropriately in the
-+ shader manager.
-+*/
-+static const char* const qopenglslMainVertexShader_core =
-+ "#version 150 core\n\
-+ void setPosition(); \n\
-+ void main(void) \n\
-+ { \n\
-+ setPosition(); \n\
-+ }\n";
-+
-+static const char* const qopenglslMainWithTexCoordsVertexShader_core =
-+ "#version 150 core\n\
-+ in vec2 textureCoordArray; \n\
-+ out vec2 textureCoords; \n\
-+ void setPosition(); \n\
-+ void main(void) \n\
-+ { \n\
-+ setPosition(); \n\
-+ textureCoords = textureCoordArray; \n\
-+ }\n";
-
--static const char* const qopenglslMainWithTexCoordsVertexShader_core = "#version 150 core \n\
-- in vec2 textureCoordArray; \n\
-- out vec2 textureCoords; \n\
-- void setPosition(); \n\
-- void main(void) \n\
-- { \n\
-- setPosition(); \n\
-- textureCoords = textureCoordArray; \n\
-- }\n";
-+static const char* const qopenglslMainWithTexCoordsAndOpacityVertexShader_core =
-+ "#version 150 core\n\
-+ in vec2 textureCoordArray; \n\
-+ in float opacityArray; \n\
-+ out vec2 textureCoords; \n\
-+ out float opacity; \n\
-+ void setPosition(); \n\
-+ void main(void) \n\
-+ { \n\
-+ setPosition(); \n\
-+ textureCoords = textureCoordArray; \n\
-+ opacity = opacityArray; \n\
-+ }\n";
-+
-+// NOTE: We let GL do the perspective correction so texture lookups in the fragment
-+// shader are also perspective corrected.
-+static const char* const qopenglslPositionOnlyVertexShader_core = "\n\
-+ in vec2 vertexCoordsArray; \n\
-+ in vec3 pmvMatrix1; \n\
-+ in vec3 pmvMatrix2; \n\
-+ in vec3 pmvMatrix3; \n\
-+ void setPosition(void) \n\
-+ { \n\
-+ mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\
-+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\
-+ gl_Position = vec4(transformedPos.xy, 0.0, transformedPos.z); \n\
-+ }\n";
-+
-+static const char* const qopenglslComplexGeometryPositionOnlyVertexShader_core = "\n\
-+ in vec2 vertexCoordsArray; \n\
-+ uniform mat3 matrix; \n\
-+ void setPosition(void) \n\
-+ { \n\
-+ gl_Position = vec4(matrix * vec3(vertexCoordsArray, 1), 1);\n\
-+ } \n";
-
- static const char* const qopenglslUntransformedPositionVertexShader_core = "\n\
-- in vec4 vertexCoordsArray; \n\
-- void setPosition(void) \n\
-- { \n\
-- gl_Position = vertexCoordsArray; \n\
-- }\n";
--
--static const char* const qopenglslMainFragmentShader_core = "#version 150 core \n\
-- vec4 srcPixel(); \n\
-- out vec4 fragColor; \n\
-- void main() \n\
-- { \n\
-- fragColor = srcPixel(); \n\
-- }\n";
-+ in vec4 vertexCoordsArray; \n\
-+ void setPosition(void) \n\
-+ { \n\
-+ gl_Position = vertexCoordsArray; \n\
-+ }\n";
-+
-+// Pattern Brush - This assumes the texture size is 8x8 and thus, the inverted size is 0.125
-+static const char* const qopenglslPositionWithPatternBrushVertexShader_core = "\n\
-+ in vec2 vertexCoordsArray; \n\
-+ in vec3 pmvMatrix1; \n\
-+ in vec3 pmvMatrix2; \n\
-+ in vec3 pmvMatrix3; \n\
-+ out vec2 patternTexCoords; \n\
-+ uniform vec2 halfViewportSize; \n\
-+ uniform vec2 invertedTextureSize; \n\
-+ uniform mat3 brushTransform; \n\
-+ void setPosition(void) \n\
-+ { \n\
-+ mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\
-+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\
-+ gl_Position.xy = transformedPos.xy / transformedPos.z; \n\
-+ vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\
-+ vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1.0); \n\
-+ float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\
-+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\
-+ patternTexCoords.xy = (hTexCoords.xy * 0.125) * invertedHTexCoordsZ; \n\
-+ }\n";
-+
-+static const char* const qopenglslAffinePositionWithPatternBrushVertexShader_core
-+ = qopenglslPositionWithPatternBrushVertexShader_core;
-+
-+static const char* const qopenglslPatternBrushSrcFragmentShader_core = "\n\
-+ in vec2 patternTexCoords;\n\
-+ uniform sampler2D brushTexture; \n\
-+ uniform vec4 patternColor; \n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ return patternColor * (1.0 - texture(brushTexture, patternTexCoords).r); \n\
-+ }\n";
-+
-+
-+// Linear Gradient Brush
-+static const char* const qopenglslPositionWithLinearGradientBrushVertexShader_core = "\n\
-+ in vec2 vertexCoordsArray; \n\
-+ in vec3 pmvMatrix1; \n\
-+ in vec3 pmvMatrix2; \n\
-+ in vec3 pmvMatrix3; \n\
-+ out float index; \n\
-+ uniform vec2 halfViewportSize; \n\
-+ uniform vec3 linearData; \n\
-+ uniform mat3 brushTransform; \n\
-+ void setPosition() \n\
-+ { \n\
-+ mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\
-+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\
-+ gl_Position.xy = transformedPos.xy / transformedPos.z; \n\
-+ vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\
-+ vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\
-+ float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\
-+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\
-+ index = (dot(linearData.xy, hTexCoords.xy) * linearData.z) * invertedHTexCoordsZ; \n\
-+ }\n";
-+
-+static const char* const qopenglslAffinePositionWithLinearGradientBrushVertexShader_core
-+ = qopenglslPositionWithLinearGradientBrushVertexShader_core;
-+
-+static const char* const qopenglslLinearGradientBrushSrcFragmentShader_core = "\n\
-+ uniform sampler2D brushTexture; \n\
-+ in float index; \n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ vec2 val = vec2(index, 0.5); \n\
-+ return texture(brushTexture, val); \n\
-+ }\n";
-+
-+
-+// Conical Gradient Brush
-+static const char* const qopenglslPositionWithConicalGradientBrushVertexShader_core = "\n\
-+ in vec2 vertexCoordsArray; \n\
-+ in vec3 pmvMatrix1; \n\
-+ in vec3 pmvMatrix2; \n\
-+ in vec3 pmvMatrix3; \n\
-+ out vec2 A; \n\
-+ uniform vec2 halfViewportSize; \n\
-+ uniform mat3 brushTransform; \n\
-+ void setPosition(void) \n\
-+ { \n\
-+ mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\
-+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\
-+ gl_Position.xy = transformedPos.xy / transformedPos.z; \n\
-+ vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\
-+ vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\
-+ float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\
-+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\
-+ A = hTexCoords.xy * invertedHTexCoordsZ; \n\
-+ }\n";
-+
-+static const char* const qopenglslAffinePositionWithConicalGradientBrushVertexShader_core
-+ = qopenglslPositionWithConicalGradientBrushVertexShader_core;
-+
-+static const char* const qopenglslConicalGradientBrushSrcFragmentShader_core = "\n\
-+ #define INVERSE_2PI 0.1591549430918953358 \n\
-+ in vec2 A; \n\
-+ uniform sampler2D brushTexture; \n\
-+ uniform float angle; \n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ float t; \n\
-+ if (abs(A.y) == abs(A.x)) \n\
-+ t = (atan(-A.y + 0.002, A.x) + angle) * INVERSE_2PI; \n\
-+ else \n\
-+ t = (atan(-A.y, A.x) + angle) * INVERSE_2PI; \n\
-+ return texture(brushTexture, vec2(t - floor(t), 0.5)); \n\
-+ }\n";
-+
-+
-+// Radial Gradient Brush
-+static const char* const qopenglslPositionWithRadialGradientBrushVertexShader_core = "\n\
-+ in vec2 vertexCoordsArray;\n\
-+ in vec3 pmvMatrix1; \n\
-+ in vec3 pmvMatrix2; \n\
-+ in vec3 pmvMatrix3; \n\
-+ out float b; \n\
-+ out vec2 A; \n\
-+ uniform vec2 halfViewportSize; \n\
-+ uniform mat3 brushTransform; \n\
-+ uniform vec2 fmp; \n\
-+ uniform vec3 bradius; \n\
-+ void setPosition(void) \n\
-+ {\n\
-+ mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\
-+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\
-+ gl_Position.xy = transformedPos.xy / transformedPos.z; \n\
-+ vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\
-+ vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\
-+ float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\
-+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\
-+ A = hTexCoords.xy * invertedHTexCoordsZ; \n\
-+ b = bradius.x + 2.0 * dot(A, fmp); \n\
-+ }\n";
-+
-+static const char* const qopenglslAffinePositionWithRadialGradientBrushVertexShader_core
-+ = qopenglslPositionWithRadialGradientBrushVertexShader_core;
-+
-+static const char* const qopenglslRadialGradientBrushSrcFragmentShader_core = "\n\
-+ in float b; \n\
-+ in vec2 A; \n\
-+ uniform sampler2D brushTexture; \n\
-+ uniform float fmp2_m_radius2; \n\
-+ uniform float inverse_2_fmp2_m_radius2; \n\
-+ uniform float sqrfr; \n\
-+ uniform vec3 bradius; \n\
-+ \n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ float c = sqrfr-dot(A, A); \n\
-+ float det = b*b - 4.0*fmp2_m_radius2*c; \n\
-+ vec4 result = vec4(0.0); \n\
-+ if (det >= 0.0) { \n\
-+ float detSqrt = sqrt(det); \n\
-+ float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2); \n\
-+ if (bradius.y + w * bradius.z >= 0.0) \n\
-+ result = texture(brushTexture, vec2(w, 0.5)); \n\
-+ } \n\
-+ return result; \n\
-+ }\n";
-+
-+
-+// Texture Brush
-+static const char* const qopenglslPositionWithTextureBrushVertexShader_core = "\n\
-+ in vec2 vertexCoordsArray; \n\
-+ in vec3 pmvMatrix1; \n\
-+ in vec3 pmvMatrix2; \n\
-+ in vec3 pmvMatrix3; \n\
-+ out vec2 brushTextureCoords; \n\
-+ uniform vec2 halfViewportSize; \n\
-+ uniform vec2 invertedTextureSize; \n\
-+ uniform mat3 brushTransform; \n\
-+ \n\
-+ void setPosition(void) \n\
-+ { \n\
-+ mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\
-+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\
-+ gl_Position.xy = transformedPos.xy / transformedPos.z; \n\
-+ vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\
-+ vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\
-+ float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\
-+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\
-+ brushTextureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \n\
-+ }\n";
-+
-+static const char* const qopenglslAffinePositionWithTextureBrushVertexShader_core
-+ = qopenglslPositionWithTextureBrushVertexShader_core;
-+
-+static const char* const qopenglslTextureBrushSrcFragmentShader_desktop_core = "\n\
-+ in vec2 brushTextureCoords; \n\
-+ uniform sampler2D brushTexture; \n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ return texture(brushTexture, brushTextureCoords); \n\
-+ }\n";
-+
-+static const char* const qopenglslTextureBrushSrcWithPatternFragmentShader_core = "\n\
-+ in vec2 brushTextureCoords; \n\
-+ uniform vec4 patternColor; \n\
-+ uniform sampler2D brushTexture; \n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ return patternColor * (1.0 - texture(brushTexture, brushTextureCoords).r); \n\
-+ }\n";
-+
-+// Solid Fill Brush
-+static const char* const qopenglslSolidBrushSrcFragmentShader_core = "\n\
-+ uniform vec4 fragmentColor; \n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ return fragmentColor; \n\
-+ }\n";
-
- static const char* const qopenglslImageSrcFragmentShader_core = "\n\
-- in vec2 textureCoords; \n\
-- uniform sampler2D imageTexture; \n\
-- vec4 srcPixel() \n\
-- { \n"
-- "return texture(imageTexture, textureCoords); \n"
-- "}\n";
-+ in vec2 textureCoords; \n\
-+ uniform sampler2D imageTexture; \n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ return texture(imageTexture, textureCoords); \n\
-+ }\n";
-+
-+static const char* const qopenglslCustomSrcFragmentShader_core = "\n\
-+ in vec2 textureCoords; \n\
-+ uniform sampler2D imageTexture; \n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ return customShader(imageTexture, textureCoords); \n\
-+ }\n";
-+
-+static const char* const qopenglslImageSrcWithPatternFragmentShader_core = "\n\
-+ in vec2 textureCoords; \n\
-+ uniform vec4 patternColor; \n\
-+ uniform sampler2D imageTexture; \n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ return patternColor * (1.0 - texture(imageTexture, textureCoords).r); \n\
-+ }\n";
-+
-+static const char* const qopenglslNonPremultipliedImageSrcFragmentShader_core = "\n\
-+ in vec2 textureCoords; \n\
-+ uniform sampler2D imageTexture; \n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ vec4 sample = texture(imageTexture, textureCoords); \n\
-+ sample.rgb = sample.rgb * sample.a; \n\
-+ return sample; \n\
-+ }\n";
-+
-+static const char* const qopenglslGrayscaleImageSrcFragmentShader_core = "\n\
-+ in vec2 textureCoords; \n\
-+ uniform sampler2D imageTexture; \n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ return texture(imageTexture, textureCoords).rrra; \n\
-+ }\n";
-+
-+static const char* const qopenglslAlphaImageSrcFragmentShader_core = "\n\
-+ in vec2 textureCoords; \n\
-+ uniform sampler2D imageTexture; \n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ return vec4(0, 0, 0, texture(imageTexture, textureCoords).r); \n\
-+ }\n";
-+
-+static const char* const qopenglslShockingPinkSrcFragmentShader_core = "\n\
-+ vec4 srcPixel() \n\
-+ { \n\
-+ return vec4(0.98, 0.06, 0.75, 1.0); \n\
-+ }\n";
-+
-+static const char* const qopenglslMainFragmentShader_ImageArrays_core =
-+ "#version 150 core\n\
-+ in float opacity; \n\
-+ out vec4 fragColor; \n\
-+ vec4 srcPixel(); \n\
-+ void main() \n\
-+ { \n\
-+ fragColor = srcPixel() * opacity; \n\
-+ }\n";
-+
-+static const char* const qopenglslMainFragmentShader_CMO_core =
-+ "#version 150 core\n\
-+ out vec4 fragColor; \n\
-+ uniform float globalOpacity; \n\
-+ vec4 srcPixel(); \n\
-+ vec4 applyMask(vec4); \n\
-+ vec4 compose(vec4); \n\
-+ void main() \n\
-+ { \n\
-+ fragColor = applyMask(compose(srcPixel()*globalOpacity))); \n\
-+ }\n";
-+
-+static const char* const qopenglslMainFragmentShader_CM_core =
-+ "#version 150 core\n\
-+ out vec4 fragColor; \n\
-+ vec4 srcPixel(); \n\
-+ vec4 applyMask(vec4); \n\
-+ vec4 compose(vec4); \n\
-+ void main() \n\
-+ { \n\
-+ fragColor = applyMask(compose(srcPixel())); \n\
-+ }\n";
-+
-+static const char* const qopenglslMainFragmentShader_MO_core =
-+ "#version 150 core\n\
-+ out vec4 fragColor; \n\
-+ uniform float globalOpacity; \n\
-+ vec4 srcPixel(); \n\
-+ vec4 applyMask(vec4); \n\
-+ void main() \n\
-+ { \n\
-+ fragColor = applyMask(srcPixel()*globalOpacity); \n\
-+ }\n";
-+
-+static const char* const qopenglslMainFragmentShader_M_core =
-+ "#version 150 core\n\
-+ out vec4 fragColor; \n\
-+ vec4 srcPixel(); \n\
-+ vec4 applyMask(vec4); \n\
-+ void main() \n\
-+ { \n\
-+ fragColor = applyMask(srcPixel()); \n\
-+ }\n";
-+
-+static const char* const qopenglslMainFragmentShader_CO_core =
-+ "#version 150 core\n\
-+ out vec4 fragColor; \n\
-+ uniform float globalOpacity; \n\
-+ vec4 srcPixel(); \n\
-+ vec4 compose(vec4); \n\
-+ void main() \n\
-+ { \n\
-+ fragColor = compose(srcPixel()*globalOpacity); \n\
-+ }\n";
-+
-+static const char* const qopenglslMainFragmentShader_C_core =
-+ "#version 150 core\n\
-+ out vec4 fragColor; \n\
-+ vec4 srcPixel(); \n\
-+ vec4 compose(vec4); \n\
-+ void main() \n\
-+ { \n\
-+ fragColor = compose(srcPixel()); \n\
-+ }\n";
-+
-+static const char* const qopenglslMainFragmentShader_O_core =
-+ "#version 150 core\n\
-+ out vec4 fragColor; \n\
-+ uniform float globalOpacity; \n\
-+ vec4 srcPixel(); \n\
-+ void main() \n\
-+ { \n\
-+ fragColor = srcPixel()*globalOpacity; \n\
-+ }\n";
-+
-+static const char* const qopenglslMainFragmentShader_core =
-+ "#version 150 core\n\
-+ out vec4 fragColor; \n\
-+ vec4 srcPixel(); \n\
-+ void main() \n\
-+ { \n\
-+ fragColor = srcPixel(); \n\
-+ }\n";
-+
-+static const char* const qopenglslMaskFragmentShader_core = "\n\
-+ in vec2 textureCoords;\n\
-+ uniform sampler2D maskTexture;\n\
-+ vec4 applyMask(vec4 src) \n\
-+ {\n\
-+ vec4 mask = texture(maskTexture, textureCoords); \n\
-+ return src * mask.a; \n\
-+ }\n";
-+
-+// For source over with subpixel antialiasing, the final color is calculated per component as follows
-+// (.a is alpha component, .c is red, green or blue component):
-+// alpha = src.a * mask.c * opacity
-+// dest.c = dest.c * (1 - alpha) + src.c * alpha
-+//
-+// In the first pass, calculate: dest.c = dest.c * (1 - alpha) with blend funcs: zero, 1 - source color
-+// In the second pass, calculate: dest.c = dest.c + src.c * alpha with blend funcs: one, one
-+//
-+// If source is a solid color (src is constant), only the first pass is needed, with blend funcs: constant, 1 - source color
-+
-+// For source composition with subpixel antialiasing, the final color is calculated per component as follows:
-+// alpha = src.a * mask.c * opacity
-+// dest.c = dest.c * (1 - mask.c) + src.c * alpha
-+//
-+
-+static const char* const qopenglslRgbMaskFragmentShaderPass1_core = "\n\
-+ in vec2 textureCoords;\n\
-+ uniform sampler2D maskTexture;\n\
-+ vec4 applyMask(vec4 src) \n\
-+ { \n\
-+ vec4 mask = texture(maskTexture, textureCoords); \n\
-+ return src.a * mask; \n\
-+ }\n";
-+
-+static const char* const qopenglslRgbMaskFragmentShaderPass2_core = "\n\
-+ in vec2 textureCoords;\n\
-+ uniform sampler2D maskTexture;\n\
-+ vec4 applyMask(vec4 src) \n\
-+ { \n\
-+ vec4 mask = texture(maskTexture, textureCoords); \n\
-+ return src * mask; \n\
-+ }\n";
-+
-+/*
-+ Left to implement:
-+ RgbMaskFragmentShader_core,
-+ RgbMaskWithGammaFragmentShader_core,
-+
-+ MultiplyCompositionModeFragmentShader_core,
-+ ScreenCompositionModeFragmentShader_core,
-+ OverlayCompositionModeFragmentShader_core,
-+ DarkenCompositionModeFragmentShader_core,
-+ LightenCompositionModeFragmentShader_core,
-+ ColorDodgeCompositionModeFragmentShader_core,
-+ ColorBurnCompositionModeFragmentShader_core,
-+ HardLightCompositionModeFragmentShader_core,
-+ SoftLightCompositionModeFragmentShader_core,
-+ DifferenceCompositionModeFragmentShader_core,
-+ ExclusionCompositionModeFragmentShader_core,
-+*/
-
- QT_END_NAMESPACE
-
-diff --git a/qtbase/src/gui/opengl/qopenglpaintengine.cpp b/qtbase/src/gui/opengl/qopenglpaintengine.cpp
-index d93871c..4cc4218 100644
---- a/qtbase/src/gui/opengl/qopenglpaintengine.cpp
-+++ b/qtbase/src/gui/opengl/qopenglpaintengine.cpp
-@@ -99,6 +99,10 @@ QOpenGL2PaintEngineExPrivate::~QOpenGL2PaintEngineExPrivate()
- {
- delete shaderManager;
-
-+ vertexBuffer.destroy();
-+ texCoordBuffer.destroy();
-+ vao.destroy();
-+
- if (elementIndicesVBOId != 0) {
- funcs.glDeleteBuffers(1, &elementIndicesVBOId);
- elementIndicesVBOId = 0;
-@@ -578,6 +582,12 @@ void QOpenGL2PaintEngineExPrivate::drawTexture(const QOpenGLRect& dest, const QO
- setCoords(staticVertexCoordinateArray, dest);
- setCoords(staticTextureCoordinateArray, srcTextureRect);
-
-+ setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true);
-+ setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, true);
-+
-+ uploadData(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray, 8);
-+ uploadData(QT_TEXTURE_COORDS_ATTR, staticTextureCoordinateArray, 8);
-+
- funcs.glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
- }
-
-@@ -664,6 +674,8 @@ void QOpenGL2PaintEngineExPrivate::resetGLState()
- float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
- funcs.glVertexAttrib4fv(3, color);
- }
-+ if (vao.isCreated())
-+ vao.release();
- }
-
- void QOpenGL2PaintEngineEx::endNativePainting()
-@@ -696,16 +708,16 @@ void QOpenGL2PaintEngineExPrivate::transferMode(EngineMode newMode)
- }
-
- if (newMode == ImageDrawingMode) {
-- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray);
-- setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, staticTextureCoordinateArray);
-+ uploadData(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray, 8);
-+ uploadData(QT_TEXTURE_COORDS_ATTR, staticTextureCoordinateArray, 8);
- }
-
- if (newMode == ImageArrayDrawingMode || newMode == ImageOpacityArrayDrawingMode) {
-- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data());
-- setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data());
-+ uploadData(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data(), vertexCoordinateArray.vertexCount() * 2);
-+ uploadData(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data(), textureCoordinateArray.vertexCount() * 2);
-
- if (newMode == ImageOpacityArrayDrawingMode)
-- setVertexAttributePointer(QT_OPACITY_ATTR, (GLfloat*)opacityArray.data());
-+ uploadData(QT_OPACITY_ATTR, (GLfloat*)opacityArray.data(), opacityArray.size());
- }
-
- // This needs to change when we implement high-quality anti-aliasing...
-@@ -824,9 +836,10 @@ void QOpenGL2PaintEngineExPrivate::fill(const QVectorPath& path)
- prepareForDraw(currentBrush.isOpaque());
- #ifdef QT_OPENGL_CACHE_AS_VBOS
- funcs.glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
-+ uploadData(QT_VERTEX_COORD_ATTR, 0, cache->vertexCount);
- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0);
- #else
-- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, cache->vertices);
-+ uploadData(QT_VERTEX_COORDS_ATTR, cache->vertices, cache->vertexCount * 2);
- #endif
- funcs.glDrawArrays(cache->primitiveType, 0, cache->vertexCount);
-
-@@ -920,6 +933,7 @@ void QOpenGL2PaintEngineExPrivate::fill(const QVectorPath& path)
- #ifdef QT_OPENGL_CACHE_AS_VBOS
- funcs.glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
- funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo);
-+ uploadData(QT_VERTEX_COORDS_ATTR, 0, cache->vertexCount);
- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0);
- if (cache->indexType == QVertexIndexVector::UnsignedInt)
- funcs.glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, 0);
-@@ -928,7 +942,7 @@ void QOpenGL2PaintEngineExPrivate::fill(const QVectorPath& path)
- funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- funcs.glBindBuffer(GL_ARRAY_BUFFER, 0);
- #else
-- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, cache->vertices);
-+ uploadData(QT_VERTEX_COORDS_ATTR, cache->vertices, cache->vertexCount * 2);
- if (cache->indexType == QVertexIndexVector::UnsignedInt)
- funcs.glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, (qint32 *)cache->indices);
- else
-@@ -957,7 +971,7 @@ void QOpenGL2PaintEngineExPrivate::fill(const QVectorPath& path)
- vertices[i] = float(inverseScale * polys.vertices.at(i));
-
- prepareForDraw(currentBrush.isOpaque());
-- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, vertices.constData());
-+ uploadData(QT_VERTEX_COORDS_ATTR, vertices.constData(), vertices.size());
- if (funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint))
- funcs.glDrawElements(GL_TRIANGLES, polys.indices.size(), GL_UNSIGNED_INT, polys.indices.data());
- else
-@@ -1075,7 +1089,6 @@ void QOpenGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data,
- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, data);
- funcs.glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
- #else
--
- funcs.glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
- if (q->state()->clipTestEnabled) {
- funcs.glStencilFunc(GL_LEQUAL, q->state()->currentClip | GL_STENCIL_HIGH_BIT,
-@@ -1083,7 +1096,8 @@ void QOpenGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data,
- } else {
- funcs.glStencilFunc(GL_ALWAYS, GL_STENCIL_HIGH_BIT, 0xff);
- }
-- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, data);
-+
-+ uploadData(QT_VERTEX_COORDS_ATTR, data, count * 2);
- funcs.glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
- #endif
- }
-@@ -1213,7 +1227,8 @@ bool QOpenGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque)
- void QOpenGL2PaintEngineExPrivate::composite(const QOpenGLRect& boundingRect)
- {
- setCoords(staticVertexCoordinateArray, boundingRect);
-- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray);
-+
-+ uploadData(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray, 8);
- funcs.glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
- }
-
-@@ -1222,16 +1237,12 @@ void QOpenGL2PaintEngineExPrivate::drawVertexArrays(const float *data, int *stop
- GLenum primitive)
- {
- // Now setup the pointer to the vertex array:
-- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, data);
-+ uploadData(QT_VERTEX_COORDS_ATTR, data, stops[stopCount-1] * 2);
-
- int previousStop = 0;
- for (int i=0; i %d:", previousStop, stop-1);
-- for (int i=previousStop; isetBrush(penBrush);
- d->stroke(path, pen);
-@@ -1320,17 +1331,12 @@ void QOpenGL2PaintEngineExPrivate::stroke(const QVectorPath &path, const QPen &p
-
- if (!stroker.vertexCount())
- return;
--
-+ funcs.glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
- if (opaque) {
- prepareForDraw(opaque);
-- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, stroker.vertices());
-- funcs.glDrawArrays(GL_TRIANGLE_STRIP, 0, stroker.vertexCount() / 2);
--
--// QBrush b(Qt::green);
--// d->setBrush(&b);
--// d->prepareForDraw(true);
--// glDrawArrays(GL_LINE_STRIP, 0, d->stroker.vertexCount() / 2);
-
-+ uploadData(QT_VERTEX_COORDS_ATTR, stroker.vertices(), stroker.vertexCount());
-+ funcs.glDrawArrays(GL_TRIANGLE_STRIP, 0, stroker.vertexCount() / 2);
- } else {
- qreal width = qpen_widthf(pen) / 2;
- if (width == 0)
-@@ -1839,8 +1845,8 @@ void QOpenGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat gly
- }
-
- if (glyphFormat != QFontEngine::Format_ARGB || recreateVertexArrays) {
-- setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinates->data());
-- setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinates->data());
-+ uploadData(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinates->data(), vertexCoordinates->vertexCount() * 2);
-+ uploadData(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinates->data(), textureCoordinates->vertexCount() * 2);
- }
-
- if (!snapToPixelGrid) {
-@@ -2081,6 +2087,29 @@ bool QOpenGL2PaintEngineEx::begin(QPaintDevice *pdev)
-
- d->funcs.initializeOpenGLFunctions();
-
-+ // Generate a new Vertex Array Object if we don't have one already
-+ if (!d->vao.isCreated()) {
-+ bool created = d->vao.create();
-+
-+ // If we managed to create it then we have a profile that supports VAOs
-+ if (created) {
-+ d->vao.bind();
-+
-+ // Generate a new Vertex Buffer Object if we don't have one already
-+ if (!d->vertexBuffer.isCreated()) {
-+ d->vertexBuffer.create();
-+ // Set its usage to StreamDraw, we will use this buffer only a few times before refilling it
-+ d->vertexBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
-+ }
-+ // Generate a new Texture Buffer Object if we don't have one already
-+ if (!d->texCoordBuffer.isCreated()) {
-+ d->texCoordBuffer.create();
-+ // Set its usage to StreamDraw, we will use this buffer only a few times before refilling it
-+ d->texCoordBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
-+ }
-+ }
-+ }
-+
- for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i)
- d->vertexAttributeArraysEnabledState[i] = false;
-
-@@ -2162,6 +2191,10 @@ void QOpenGL2PaintEngineEx::ensureActive()
- Q_D(QOpenGL2PaintEngineEx);
- QOpenGLContext *ctx = d->ctx;
-
-+ if (d->vao.isCreated()) {
-+ d->vao.bind();
-+ }
-+
- if (isActive() && ctx->d_func()->active_engine != this) {
- ctx->d_func()->active_engine = this;
- d->needsSync = true;
-diff --git a/qtbase/src/gui/opengl/qopenglpaintengine_p.h b/qtbase/src/gui/opengl/qopenglpaintengine_p.h
-index c9f3282..fe63747 100644
---- a/qtbase/src/gui/opengl/qopenglpaintengine_p.h
-+++ b/qtbase/src/gui/opengl/qopenglpaintengine_p.h
-@@ -64,6 +64,9 @@
-
- #include
-
-+#include
-+#include
-+
- enum EngineMode {
- ImageDrawingMode,
- TextDrawingMode,
-@@ -192,7 +195,9 @@ public:
- snapToPixelGrid(false),
- nativePaintingActive(false),
- inverseScale(1),
-- lastTextureUnitUsed(QT_UNKNOWN_TEXTURE_UNIT)
-+ lastTextureUnitUsed(QT_UNKNOWN_TEXTURE_UNIT),
-+ vertexBuffer(QOpenGLBuffer::VertexBuffer),
-+ texCoordBuffer(QOpenGLBuffer::VertexBuffer)
- { }
-
- ~QOpenGL2PaintEngineExPrivate();
-@@ -221,7 +226,7 @@ public:
- void drawCachedGlyphs(QFontEngine::GlyphFormat glyphFormat, QStaticTextItem *staticTextItem);
-
- // Calls glVertexAttributePointer if the pointer has changed
-- inline void setVertexAttributePointer(unsigned int arrayIndex, const GLfloat *pointer);
-+ inline void uploadData(unsigned int arrayIndex, const GLfloat *data, const GLuint count);
-
- // draws whatever is in the vertex array:
- void drawVertexArrays(const float *data, int *stops, int stopCount, GLenum primitive);
-@@ -312,6 +317,10 @@ public:
- GLenum lastTextureUnitUsed;
- GLuint lastTextureUsed;
-
-+ QOpenGLVertexArrayObject vao;
-+ QOpenGLBuffer vertexBuffer;
-+ QOpenGLBuffer texCoordBuffer;
-+
- bool needsSync;
- bool multisamplingAlwaysEnabled;
-
-@@ -325,17 +334,38 @@ public:
- };
-
-
--void QOpenGL2PaintEngineExPrivate::setVertexAttributePointer(unsigned int arrayIndex, const GLfloat *pointer)
-+void QOpenGL2PaintEngineExPrivate::uploadData(unsigned int arrayIndex, const GLfloat *data, const GLuint count)
- {
- Q_ASSERT(arrayIndex < 3);
-- if (pointer == vertexAttribPointers[arrayIndex])
-- return;
--
-- vertexAttribPointers[arrayIndex] = pointer;
-- if (arrayIndex == QT_OPACITY_ATTR)
-- funcs.glVertexAttribPointer(arrayIndex, 1, GL_FLOAT, GL_FALSE, 0, pointer);
-- else
-- funcs.glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, pointer);
-+
-+ // If a vertex array object is created we have a profile that supports them
-+ // and we will upload the data via a QOpenGLBuffer. Otherwise we will use
-+ // the legacy way of uploading the data via glVertexAttribPointer.
-+ if (vao.isCreated()) {
-+ if (arrayIndex == QT_VERTEX_COORDS_ATTR) {
-+ vertexBuffer.bind();
-+ vertexBuffer.allocate(data, count * sizeof(float));
-+ }
-+ if (arrayIndex == QT_TEXTURE_COORDS_ATTR) {
-+ texCoordBuffer.bind();
-+ texCoordBuffer.allocate(data, count * sizeof(float));
-+ }
-+ if (arrayIndex == QT_OPACITY_ATTR)
-+ funcs.glVertexAttribPointer(arrayIndex, 1, GL_FLOAT, GL_FALSE, 0, 0);
-+ else
-+ funcs.glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, 0);
-+ } else {
-+ // If we already uploaded the data we don't have to do it again
-+ if (data == vertexAttribPointers[arrayIndex])
-+ return;
-+
-+ // Store the data in cache and upload it to the graphics card.
-+ vertexAttribPointers[arrayIndex] = data;
-+ if (arrayIndex == QT_OPACITY_ATTR)
-+ funcs.glVertexAttribPointer(arrayIndex, 1, GL_FLOAT, GL_FALSE, 0, data);
-+ else
-+ funcs.glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, data);
-+ }
- }
-
- QT_END_NAMESPACE
-diff --git a/qtbase/src/gui/opengl/qopengltextureglyphcache.cpp b/qtbase/src/gui/opengl/qopengltextureglyphcache.cpp
-index 9a7b1eb..3a03989 100644
---- a/qtbase/src/gui/opengl/qopengltextureglyphcache.cpp
-+++ b/qtbase/src/gui/opengl/qopengltextureglyphcache.cpp
-@@ -380,8 +380,8 @@ void QOpenGLTextureGlyphCache::resizeTextureData(int width, int height)
- blitProgram = m_blitProgram;
-
- } else {
-- pex->setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, m_vertexCoordinateArray);
-- pex->setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, m_textureCoordinateArray);
-+ pex->uploadData(QT_VERTEX_COORDS_ATTR, m_vertexCoordinateArray, 8);
-+ pex->uploadData(QT_TEXTURE_COORDS_ATTR, m_textureCoordinateArray, 8);
-
- pex->shaderManager->useBlitProgram();
- blitProgram = pex->shaderManager->blitProgram();
-diff --git a/qtbase/src/gui/opengl/qtriangulatingstroker.cpp b/qtbase/src/gui/opengl/qtriangulatingstroker.cpp
-index d9a3231..a7ce6c8 100644
---- a/qtbase/src/gui/opengl/qtriangulatingstroker.cpp
-+++ b/qtbase/src/gui/opengl/qtriangulatingstroker.cpp
-@@ -261,7 +261,7 @@ void QTriangulatingStroker::moveTo(const qreal *pts)
- normalVector(m_cx, m_cy, x2, y2, &m_nvx, &m_nvy);
-
-
-- // To acheive jumps we insert zero-area tringles. This is done by
-+ // To achieve jumps we insert zero-area triangles. This is done by
- // adding two identical points in both the end of previous strip
- // and beginning of next strip
- bool invisibleJump = m_vertices.size();
diff --git a/3rdparty/ext_qt/macdeploy-qt.diff b/3rdparty/ext_qt/macdeploy-qt.diff
index 0840194026..f871cff882 100644
--- a/3rdparty/ext_qt/macdeploy-qt.diff
+++ b/3rdparty/ext_qt/macdeploy-qt.diff
@@ -1,128 +1,134 @@
+commit 26d6c76d5a51504ebabec5f4ea2643069743f962
+Author: Boudewijn Rempt
+Date: Sat Nov 4 14:15:25 2017 +0100
+
+ Fix macdeployqt
+
diff --git a/qttools/src/macdeployqt/macdeployqt/main.cpp b/qttools/src/macdeployqt/macdeployqt/main.cpp
-index 2e6ad0c..8a90c1a 100644
+index 5488a5f..1e90c72 100644
--- a/qttools/src/macdeployqt/macdeployqt/main.cpp
+++ b/qttools/src/macdeployqt/macdeployqt/main.cpp
-@@ -52,6 +52,7 @@ int main(int argc, char **argv)
- qDebug() << " -always-overwrite : Copy files even if the target file exists";
+@@ -53,6 +53,7 @@ int main(int argc, char **argv)
qDebug() << " -codesign= : Run codesign with the given identity on all executables";
qDebug() << " -appstore-compliant: Skip deployment of components that use private API";
+ qDebug() << " -libpath= : Add the given path to the library search path";
+ qDebug() << " -extra-plugins= : Deploy plugins from given extra directory";
qDebug() << "";
qDebug() << "macdeployqt takes an application bundle as input and makes it";
qDebug() << "self-contained by copying in the Qt frameworks and plugins that";
-@@ -92,6 +93,7 @@ int main(int argc, char **argv)
+@@ -94,6 +95,7 @@ int main(int argc, char **argv)
extern QString codesignIdentiy;
extern bool appstoreCompliant;
extern bool deployFramework;
+ QStringList extraPluginDirectories;
for (int i = 2; i < argc; ++i) {
QByteArray argument = QByteArray(argv[i]);
-@@ -153,6 +155,14 @@ int main(int argc, char **argv)
+@@ -162,6 +164,14 @@ int main(int argc, char **argv)
LogDebug() << "Argument found:" << argument;
deployFramework = true;
+ } else if (argument.startsWith(QByteArray("-extra-plugins"))) {
+ LogDebug() << "Argument found:" << argument;
+ int index = argument.indexOf('=');
+ if (index == -1)
+ LogError() << "Missing extra plugins directory";
+ else
+ extraPluginDirectories << argument.mid(index+1);
+
} else if (argument.startsWith("-")) {
LogError() << "Unknown argument" << argument << "\n";
return 1;
-@@ -183,10 +193,13 @@ int main(int argc, char **argv)
+@@ -192,10 +202,13 @@ int main(int argc, char **argv)
deploymentInfo.deployedFrameworks = deploymentInfo.deployedFrameworks.toSet().toList();
}
- if (plugins && !deploymentInfo.qtPath.isEmpty()) {
+ if ((plugins && !deploymentInfo.qtPath.isEmpty()) || !extraPluginDirectories.isEmpty()) {
deploymentInfo.pluginPath = deploymentInfo.qtPath + "/plugins";
LogNormal();
- deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
+ if (plugins && !deploymentInfo.qtPath.isEmpty())
+ deployPlugins(appBundlePath, deploymentInfo, useDebugLibs);
+ if (!extraPluginDirectories.isEmpty())
+ deployExtraPlugins(appBundlePath, deploymentInfo, useDebugLibs, extraPluginDirectories);
createQtConf(appBundlePath);
}
diff --git a/qttools/src/macdeployqt/shared/shared.cpp b/qttools/src/macdeployqt/shared/shared.cpp
-index 5577265..a590039 100644
+index 9575090..477f7a0 100644
--- a/qttools/src/macdeployqt/shared/shared.cpp
+++ b/qttools/src/macdeployqt/shared/shared.cpp
-@@ -1070,6 +1070,43 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
+@@ -1120,6 +1120,43 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
}
}
+void deployExtraPlugins(const ApplicationBundleInfo &appBundleInfo,
+ const QString pluginDestinationPath, DeploymentInfo deploymentInfo, bool useDebugLibs, const QStringList &extraPluginDirectories)
+{
+ foreach (const QString &extraPluginDir, extraPluginDirectories) {
+ LogNormal() << "Deploying plugins from" << extraPluginDir;
+
+ // search for dylib and so files, both work as plugins on mac os
+ QDir dir(extraPluginDir);
+ dir.setFilter(QDir::Files);
+ dir.setNameFilters(QStringList() << "*.dylib" << "*.so");
+ QDirIterator dirIterator(dir, QDirIterator::Subdirectories);
+ QStringList pluginList;
+ while (dirIterator.hasNext()) {
+ dirIterator.next();
+ if (!QFileInfo(dirIterator.filePath()).isFile())
+ continue;
+ pluginList.append(dir.relativeFilePath(dirIterator.filePath()));
+ }
+
+ // deploy all found plugins
+ foreach (const QString &plugin, pluginList) {
+ QString sourcePath = extraPluginDir + "/" + plugin;
+ const QString destinationPath = pluginDestinationPath + "/" + plugin;
+ QDir dir;
+ dir.mkpath(QFileInfo(destinationPath).path());
+
+ if (copyFilePrintStatus(sourcePath, destinationPath)) {
+ runStrip(destinationPath);
+
+ QList frameworks = getQtFrameworks(destinationPath, appBundleInfo.path, deploymentInfo.rpathsUsed, useDebugLibs);
+ deployQtFrameworks(frameworks, appBundleInfo.path, QStringList() << destinationPath, useDebugLibs, deploymentInfo.useLoaderPath);
+
+ }
+ }
+ }
+}
+
void createQtConf(const QString &appBundlePath)
{
// Set Plugins and imports paths. These are relative to App.app/Contents.
-@@ -1111,6 +1148,16 @@ void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo,
+@@ -1161,6 +1198,16 @@ void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo,
deployPlugins(applicationBundle, deploymentInfo.pluginPath, pluginDestinationPath, deploymentInfo, useDebugLibs);
}
+void deployExtraPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs, const QStringList &extraPluginDirectories)
+{
+ ApplicationBundleInfo applicationBundle;
+ applicationBundle.path = appBundlePath;
+ applicationBundle.binaryPath = findAppBinary(appBundlePath);
+
+ const QString pluginDestinationPath = appBundlePath + "/" + "Contents/PlugIns";
+ deployExtraPlugins(applicationBundle, pluginDestinationPath, deploymentInfo, useDebugLibs, extraPluginDirectories);
+}
+
void deployQmlImport(const QString &appBundlePath, const QSet &rpaths, const QString &importSourcePath, const QString &importName)
{
QString importDestinationPath = appBundlePath + "/Contents/Resources/qml/" + importName;
diff --git a/qttools/src/macdeployqt/shared/shared.h b/qttools/src/macdeployqt/shared/shared.h
index c173846..cceac3a 100644
--- a/qttools/src/macdeployqt/shared/shared.h
+++ b/qttools/src/macdeployqt/shared/shared.h
@@ -116,6 +116,7 @@ DeploymentInfo deployQtFrameworks(const QString &appBundlePath, const QStringLis
DeploymentInfo deployQtFrameworks(QList frameworks,const QString &bundlePath, const QStringList &binaryPaths, bool useDebugLibs, bool useLoaderPath);
void createQtConf(const QString &appBundlePath);
void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs);
+void deployExtraPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs, const QStringList &extraPluginDirectories);
bool deployQmlImports(const QString &appBundlePath, DeploymentInfo deploymentInfo, QStringList &qmlDirs);
void changeIdentification(const QString &id, const QString &binaryPath);
void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath);
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4a120aa28b..f770015fb8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,687 +1,700 @@
project(krita)
message(STATUS "Using CMake version: ${CMAKE_VERSION}")
cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
set(MIN_QT_VERSION 5.6.0)
option(OVERRIDE_QT_VERSION "Use this to make it possible to build with Qt < 5.6.0. There will be bugs." OFF)
if (OVERRIDE_QT_VERSION)
set(MIN_QT_VERSION 5.4.0)
endif()
set(MIN_FRAMEWORKS_VERSION 5.7.0)
if (POLICY CMP0002)
cmake_policy(SET CMP0002 OLD)
endif()
if (POLICY CMP0017)
cmake_policy(SET CMP0017 NEW)
endif ()
if (POLICY CMP0022)
cmake_policy(SET CMP0022 OLD)
endif ()
if (POLICY CMP0026)
cmake_policy(SET CMP0026 OLD)
endif()
if (POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif()
if (POLICY CMP0046)
cmake_policy(SET CMP0046 OLD)
endif ()
if (POLICY CMP0059)
cmake_policy(SET CMP0059 OLD)
endif()
if (POLICY CMP0063)
cmake_policy(SET CMP0063 OLD)
endif()
if (POLICY CMP0054)
cmake_policy(SET CMP0054 OLD)
endif()
if (POLICY CMP0064)
cmake_policy(SET CMP0064 OLD)
endif()
if (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.9 -Wno-macro-redefined -Wno-deprecated-register)
endif()
if (LINUX)
if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 AND NOT WINDOWS)
add_definitions(-Werror=delete-incomplete)
endif()
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.0.0-pre-alpha")
set(KRITA_STABLE_VERSION_MAJOR 4) # 3 for 3.x, 4 for 4.x, etc.
set(KRITA_STABLE_VERSION_MINOR 0) # 0 for 3.0, 1 for 3.1, etc.
set(KRITA_VERSION_RELEASE 0) # 88 for pre-alpha, 89 for Alpha, increase for next test releases, set 0 for first Stable, etc.
set(KRITA_ALPHA 1) # uncomment only for Alpha
#set(KRITA_BETA 1) # uncomment only for Beta
#set(KRITA_RC 1) # uncomment only for RC
set(KRITA_YEAR 2017) # update every year
if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC)
set(KRITA_STABLE 1) # do not edit
endif()
message(STATUS "Krita version: ${KRITA_VERSION_STRING}")
# Define the generic version of the Krita libraries here
# This makes it easy to advance it when the next Krita release comes.
# 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series
# (2.x) so we're starting with 15 in 3.x series, 16 in 4.x series
if(KRITA_STABLE_VERSION_MAJOR EQUAL 4)
math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 16")
else()
# let's make sure we won't forget to update the "16"
message(FATAL_ERROR "Reminder: please update offset == 16 used to compute GENERIC_KRITA_LIB_VERSION_MAJOR to something bigger")
endif()
set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0")
set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}")
LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro")
# fetch git revision for the current build
set(KRITA_GIT_SHA1_STRING "")
set(KRITA_GIT_BRANCH_STRING "")
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
get_git_branch(GIT_BRANCH)
if(GIT_SHA1 AND GIT_BRANCH)
string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1)
set(KRITA_GIT_SHA1_STRING ${GIT_SHA1})
set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH})
endif()
if(NOT DEFINED RELEASE_BUILD)
# estimate mode by CMAKE_BUILD_TYPE content if not set on cmdline
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_TOLOWER)
set(RELEASE_BUILD_TYPES "release" "relwithdebinfo" "minsizerel")
list(FIND RELEASE_BUILD_TYPES "${CMAKE_BUILD_TYPE_TOLOWER}" INDEX)
if (INDEX EQUAL -1)
set(RELEASE_BUILD FALSE)
else()
set(RELEASE_BUILD TRUE)
endif()
endif()
message(STATUS "Release build: ${RELEASE_BUILD}")
# 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("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(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).")
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
int main(int argc, char *argv[]) {
Py_InitializeEx(0);
}" ${OUTPUT_VARNAME})
endfunction()
if(MINGW)
find_package(PythonInterp 3.6 EXACT)
find_package(PythonLibs 3.6 EXACT)
if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND)
find_package(PythonLibrary 3.6)
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)
find_package(PythonInterp 3.0)
find_package(PythonLibrary 3.0)
endif(MINGW)
########################
#########################
## Look for KDE and Qt ##
#########################
########################
find_package(ECM 5.19 REQUIRED NOMODULE)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
include(ECMOptionalAddSubdirectory)
include(ECMAddAppIcon)
include(ECMSetupVersion)
include(ECMMarkNonGuiExecutable)
include(ECMGenerateHeaders)
include(GenerateExportHeader)
include(ECMMarkAsTest)
include(ECMInstallIcons)
include(CMakePackageConfigHelpers)
include(WriteBasicConfigVersionFile)
include(CheckFunctionExists)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings)
# do not reorder to be alphabetical: this is the order in which the frameworks
# depend on each other.
find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS
Archive
Config
WidgetsAddons
Completion
CoreAddons
GuiAddons
I18n
ItemModels
ItemViews
WindowSystem
)
# KConfig deprecated authorizeKAction. In order to be warning free,
# compile with the updated function when the dependency is new enough.
# Remove this (and the uses of the define) when the minimum KF5
# version is >= 5.24.0.
if (${KF5Config_VERSION} VERSION_LESS "5.24.0" )
message("Old KConfig (< 5.24.0) found.")
add_definitions(-DKCONFIG_BEFORE_5_24)
endif()
find_package(Qt5 ${MIN_QT_VERSION}
REQUIRED COMPONENTS
Core
Gui
Widgets
Xml
Network
PrintSupport
Svg
Test
Concurrent
)
include (MacroAddFileDependencies)
include (MacroBoolTo01)
include (MacroEnsureOutOfSourceBuild)
macro_ensure_out_of_source_build("Compiling Krita inside the source directory is not possible. Please refer to the build instruction https://community.kde.org/Krita#Build_Instructions")
# Note: OPTIONAL_COMPONENTS does not seem to be reliable
# (as of ECM 5.15.0, CMake 3.2)
find_package(Qt5Multimedia ${MIN_QT_VERSION})
set_package_properties(Qt5Multimedia PROPERTIES
DESCRIPTION "Qt multimedia integration"
URL "http://www.qt.io/"
TYPE OPTIONAL
PURPOSE "Optionally used to provide sound support for animations")
macro_bool_to_01(Qt5Multimedia_FOUND HAVE_QT_MULTIMEDIA)
configure_file(config-qtmultimedia.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-qtmultimedia.h )
find_package(Qt5Quick ${MIN_QT_VERSION})
set_package_properties(Qt5Quick PROPERTIES
DESCRIPTION "QtQuick"
URL "http://www.qt.io/"
TYPE OPTIONAL
PURPOSE "Optionally used for the touch gui for Krita")
macro_bool_to_01(Qt5Quick_FOUND HAVE_QT_QUICK)
find_package(Qt5QuickWidgets ${MIN_QT_VERSION})
set_package_properties(Qt5QuickWidgets PROPERTIES
DESCRIPTION "QtQuickWidgets"
URL "http://www.qt.io/"
TYPE OPTIONAL
PURPOSE "Optionally used for the touch gui for Krita")
if (NOT WIN32 AND NOT APPLE)
find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras)
find_package(Qt5DBus ${MIN_QT_VERSION})
set(HAVE_DBUS ${Qt5DBus_FOUND})
set_package_properties(Qt5DBus PROPERTIES
DESCRIPTION "Qt DBUS integration"
URL "http://www.qt.io/"
TYPE OPTIONAL
PURPOSE "Optionally used to provide a dbus api on Linux")
find_package(KF5KIO ${MIN_FRAMEWORKS_VERSION})
macro_bool_to_01(KF5KIO_FOUND HAVE_KIO)
set_package_properties(KF5KIO PROPERTIES
DESCRIPTION "KDE's KIO Framework"
URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kio/html/index.html"
TYPE OPTIONAL
PURPOSE "Optionally used for recent document handling")
find_package(KF5Crash ${MIN_FRAMEWORKS_VERSION})
macro_bool_to_01(KF5Crash_FOUND HAVE_KCRASH)
set_package_properties(KF5Crash PROPERTIES
DESCRIPTION "KDE's Crash Handler"
URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kcrash/html/index.html"
TYPE OPTIONAL
PURPOSE "Optionally used to provide crash reporting on Linux")
find_package(X11 REQUIRED COMPONENTS Xinput)
set(HAVE_X11 TRUE)
add_definitions(-DHAVE_X11)
find_package(XCB COMPONENTS XCB ATOM)
set(HAVE_XCB ${XCB_FOUND})
else()
set(HAVE_DBUS FALSE)
set(HAVE_X11 FALSE)
set(HAVE_XCB FALSE)
endif()
add_definitions(
-DQT_USE_QSTRINGBUILDER
-DQT_STRICT_ITERATORS
-DQT_NO_SIGNALS_SLOTS_KEYWORDS
-DQT_USE_FAST_OPERATOR_PLUS
-DQT_USE_FAST_CONCATENATION
-DQT_NO_URL_CAST_FROM_STRING
)
if (${Qt5_VERSION} VERSION_GREATER "5.8.0" )
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50900)
elseif(${Qt5_VERSION} VERSION_GREATER "5.7.0" )
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50800)
elseif(${Qt5_VERSION} VERSION_GREATER "5.6.0" )
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50700)
else()
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50600)
endif()
add_definitions(-DTRANSLATION_DOMAIN=\"krita\")
#
# The reason for this mode is that the Debug mode disable inlining
#
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS_KRITADEVS "-O3 -g" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals")
endif()
if(UNIX)
set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m")
endif()
if(WIN32)
if(MSVC)
# C4522: 'class' : multiple assignment operators specified
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4522")
endif()
endif()
+# KDECompilerSettings adds the `--export-all-symbols` linker flag.
+# We don't really need it.
+if(MINGW)
+ string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}")
+ string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}")
+endif(MINGW)
+
# enable exceptions globally
kde_enable_exceptions()
# only with this definition will all the FOO_TEST_EXPORT macro do something
# TODO: check if this can be moved to only those places which make use of it,
# to reduce global compiler definitions that would trigger a recompile of
# everything on a change (like adding/removing tests to/from the build)
if(BUILD_TESTING)
add_definitions(-DCOMPILING_TESTS)
endif()
set(KRITA_DEFAULT_TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/sdk/tests/data/)
macro(macro_add_unittest_definitions)
add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/")
add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}")
add_definitions(-DFILES_DEFAULT_DATA_DIR="${KRITA_DEFAULT_TEST_DATA_DIR}")
add_definitions(-DSYSTEM_RESOURCES_DATA_DIR="${CMAKE_SOURCE_DIR}/krita/data/")
endmacro()
# overcome some platform incompatibilities
if(WIN32)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks)
add_definitions(-D_USE_MATH_DEFINES)
add_definitions(-DNOMINMAX)
set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib)
endif()
# set custom krita plugin installdir
set(KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins)
###########################
############################
## Required dependencies ##
############################
###########################
find_package(PNG REQUIRED)
if (APPLE)
# this is not added correctly on OSX -- see http://forum.kde.org/viewtopic.php?f=139&t=101867&p=221242#p221242
include_directories(SYSTEM ${PNG_INCLUDE_DIR})
endif()
add_definitions(-DBOOST_ALL_NO_LIB)
find_package(Boost 1.55 REQUIRED COMPONENTS system) # for pigment and stage
-include_directories(${Boost_INCLUDE_DIRS})
+include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
##
## Test for GNU Scientific Library
##
find_package(GSL)
set_package_properties(GSL PROPERTIES
URL "http://www.gnu.org/software/gsl"
TYPE RECOMMENDED
PURPOSE "Required by Krita's Transform tool.")
macro_bool_to_01(GSL_FOUND HAVE_GSL)
configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h )
###########################
############################
## Optional dependencies ##
############################
###########################
##
## Check for OpenEXR
##
find_package(ZLIB)
set_package_properties(ZLIB PROPERTIES
DESCRIPTION "Compression library"
URL "http://www.zlib.net/"
TYPE OPTIONAL
PURPOSE "Optionally used by the G'Mic and the PSD plugins")
macro_bool_to_01(ZLIB_FOUND HAVE_ZLIB)
find_package(OpenEXR)
set_package_properties(OpenEXR PROPERTIES
DESCRIPTION "High dynamic-range (HDR) image file format"
URL "http://www.openexr.com"
TYPE OPTIONAL
PURPOSE "Required by the Krita OpenEXR filter")
macro_bool_to_01(OPENEXR_FOUND HAVE_OPENEXR)
set(LINK_OPENEXR_LIB)
if(OPENEXR_FOUND)
include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR})
set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES})
add_definitions(${OPENEXR_DEFINITIONS})
endif()
find_package(TIFF)
set_package_properties(TIFF PROPERTIES
DESCRIPTION "TIFF Library and Utilities"
URL "http://www.remotesensing.org/libtiff"
TYPE OPTIONAL
PURPOSE "Required by the Krita TIFF filter")
find_package(JPEG)
set_package_properties(JPEG PROPERTIES
DESCRIPTION "Free library for JPEG image compression. Note: libjpeg8 is NOT supported."
URL "http://www.libjpeg-turbo.org"
TYPE OPTIONAL
PURPOSE "Required by the Krita JPEG filter")
set(LIBRAW_MIN_VERSION "0.16")
find_package(LibRaw ${LIBRAW_MIN_VERSION})
set_package_properties(LibRaw PROPERTIES
DESCRIPTION "Library to decode RAW images"
URL "http://www.libraw.org"
TYPE OPTIONAL
PURPOSE "Required to build the raw import plugin")
find_package(FFTW3)
set_package_properties(FFTW3 PROPERTIES
DESCRIPTION "A fast, free C FFT library"
URL "http://www.fftw.org/"
TYPE OPTIONAL
PURPOSE "Required by the Krita for fast convolution operators and some G'Mic features")
macro_bool_to_01(FFTW3_FOUND HAVE_FFTW3)
find_package(OCIO)
set_package_properties(OCIO PROPERTIES
DESCRIPTION "The OpenColorIO Library"
URL "http://www.opencolorio.org"
TYPE OPTIONAL
PURPOSE "Required by the Krita LUT docker")
macro_bool_to_01(OCIO_FOUND HAVE_OCIO)
##
## Look for OpenGL
##
# TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes)
if(Qt5Gui_OPENGL_IMPLEMENTATION)
message(STATUS "Found QtGui OpenGL support")
else()
message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.")
endif()
##
## Test for eigen3
##
find_package(Eigen3 3.0 REQUIRED)
set_package_properties(Eigen3 PROPERTIES
DESCRIPTION "C++ template library for linear algebra"
URL "http://eigen.tuxfamily.org"
TYPE REQUIRED)
##
## Test for exiv2
##
find_package(Exiv2 0.16 REQUIRED)
set_package_properties(Exiv2 PROPERTIES
DESCRIPTION "Image metadata library and tools"
URL "http://www.exiv2.org"
PURPOSE "Required by Krita")
##
## Test for lcms
##
find_package(LCMS2 2.4 REQUIRED)
set_package_properties(LCMS2 PROPERTIES
DESCRIPTION "LittleCMS Color management engine"
URL "http://www.littlecms.com"
TYPE REQUIRED
PURPOSE "Will be used for color management and is necessary for Krita")
if(LCMS2_FOUND)
if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 )
set(HAVE_LCMS24 TRUE)
endif()
set(HAVE_REQUIRED_LCMS_VERSION TRUE)
set(HAVE_LCMS2 TRUE)
endif()
##
## Test for Vc
##
set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} )
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
set(HAVE_VC FALSE)
if( NOT 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()
configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h )
if(HAVE_VC)
message(STATUS "Vc found!")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_SOURCE_DIR}/cmake/vc")
include (VcMacros)
if(Vc_COMPILER_IS_CLANG)
- set(ADDITIONAL_VC_FLAGS "-Wabi -ffp-contract=fast -fPIC")
+ set(ADDITIONAL_VC_FLAGS "-Wabi -ffp-contract=fast")
+ if(NOT WIN32)
+ set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC")
+ endif()
elseif (NOT MSVC)
- set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast -fPIC")
+ set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast")
+ if(NOT WIN32)
+ set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC")
+ endif()
endif()
#Handle Vc master
if(Vc_COMPILER_IS_GCC OR Vc_COMPILER_IS_CLANG)
AddCompilerFlag("-std=c++11" _ok)
if(NOT _ok)
AddCompilerFlag("-std=c++0x" _ok)
endif()
endif()
macro(ko_compile_for_all_implementations_no_scalar _objs _src)
vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2)
endmacro()
macro(ko_compile_for_all_implementations _objs _src)
vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2)
endmacro()
endif()
set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} )
add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS})
if(WIN32)
set(LIB_INSTALL_DIR ${LIB_INSTALL_DIR}
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
LIBRARY ${INSTALL_TARGETS_DEFAULT_ARGS}
ARCHIVE ${INSTALL_TARGETS_DEFAULT_ARGS} )
endif()
##
## Test endianess
##
include (TestBigEndian)
test_big_endian(CMAKE_WORDS_BIGENDIAN)
##
## Test for qt-poppler
##
find_package(Poppler COMPONENTS Qt5)
set_package_properties(Poppler PROPERTIES
DESCRIPTION "A PDF rendering library"
URL "http://poppler.freedesktop.org"
TYPE OPTIONAL
PURPOSE "Required by the Krita PDF filter.")
############################
#############################
## Add Krita helper macros ##
#############################
############################
include(MacroKritaAddBenchmark)
####################
#####################
## Define includes ##
#####################
####################
# for config.h and includes (if any?)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/interfaces
)
add_subdirectory(libs)
add_subdirectory(plugins)
add_subdirectory(benchmarks)
add_subdirectory(krita)
configure_file(KoConfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h )
configure_file(config_convolution.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_convolution.h)
configure_file(config-ocio.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ocio.h )
check_function_exists(powf HAVE_POWF)
configure_file(config-powf.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-powf.h)
message("\nBroken tests:")
foreach(tst ${KRITA_BROKEN_TESTS})
message(" * ${tst}")
endforeach()
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt
index 4031146311..01b8414ed0 100644
--- a/benchmarks/CMakeLists.txt
+++ b/benchmarks/CMakeLists.txt
@@ -1,94 +1,93 @@
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
include_directories(
${CMAKE_SOURCE_DIR}/sdk/tests
${CMAKE_SOURCE_DIR}/libs/pigment
${CMAKE_SOURCE_DIR}/libs/pigment/compositeops
)
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
- ${Boost_INCLUDE_DIRS}
)
set(LINK_VC_LIB)
if(HAVE_VC)
include_directories(${Vc_INCLUDE_DIR})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Vc_DEFINITIONS}")
set(LINK_VC_LIB ${Vc_LIBRARIES})
endif()
macro_add_unittest_definitions()
########### next target ###############
set(kis_datamanager_benchmark_SRCS kis_datamanager_benchmark.cpp)
set(kis_hiterator_benchmark_SRCS kis_hline_iterator_benchmark.cpp)
set(kis_viterator_benchmark_SRCS kis_vline_iterator_benchmark.cpp)
set(kis_random_iterator_benchmark_SRCS kis_random_iterator_benchmark.cpp)
set(kis_projection_benchmark_SRCS kis_projection_benchmark.cpp)
set(kis_bcontrast_benchmark_SRCS kis_bcontrast_benchmark.cpp)
set(kis_blur_benchmark_SRCS kis_blur_benchmark.cpp)
set(kis_level_filter_benchmark_SRCS kis_level_filter_benchmark.cpp)
set(kis_painter_benchmark_SRCS kis_painter_benchmark.cpp)
set(kis_stroke_benchmark_SRCS kis_stroke_benchmark.cpp)
set(kis_fast_math_benchmark_SRCS kis_fast_math_benchmark.cpp)
set(kis_floodfill_benchmark_SRCS kis_floodfill_benchmark.cpp)
set(kis_gradient_benchmark_SRCS kis_gradient_benchmark.cpp)
set(kis_mask_generator_benchmark_SRCS kis_mask_generator_benchmark.cpp)
set(kis_low_memory_benchmark_SRCS kis_low_memory_benchmark.cpp)
set(KisAnimationRenderingBenchmark_SRCS KisAnimationRenderingBenchmark.cpp)
set(kis_filter_selections_benchmark_SRCS kis_filter_selections_benchmark.cpp)
if (UNIX)
# set(kis_composition_benchmark_SRCS kis_composition_benchmark.cpp)
endif()
set(kis_thumbnail_benchmark_SRCS kis_thumbnail_benchmark.cpp)
krita_add_benchmark(KisDatamanagerBenchmark TESTNAME krita-benchmarks-KisDataManager ${kis_datamanager_benchmark_SRCS})
krita_add_benchmark(KisHLineIteratorBenchmark TESTNAME krita-benchmarks-KisHLineIterator ${kis_hiterator_benchmark_SRCS})
krita_add_benchmark(KisVLineIteratorBenchmark TESTNAME krita-benchmarks-KisVLineIterator ${kis_viterator_benchmark_SRCS})
krita_add_benchmark(KisRandomIteratorBenchmark TESTNAME krita-benchmarks-KisRandomIterator ${kis_random_iterator_benchmark_SRCS})
krita_add_benchmark(KisProjectionBenchmark TESTNAME krita-benchmarks-KisProjectionBenchmark ${kis_projection_benchmark_SRCS})
krita_add_benchmark(KisBContrastBenchmark TESTNAME krita-benchmarks-KisBContrastBenchmark ${kis_bcontrast_benchmark_SRCS})
krita_add_benchmark(KisBlurBenchmark TESTNAME krita-benchmarks-KisBlurBenchmark ${kis_blur_benchmark_SRCS})
krita_add_benchmark(KisLevelFilterBenchmark TESTNAME krita-benchmarks-KisLevelFilterBenchmark ${kis_level_filter_benchmark_SRCS})
krita_add_benchmark(KisPainterBenchmark TESTNAME krita-benchmarks-KisPainterBenchmark ${kis_painter_benchmark_SRCS})
krita_add_benchmark(KisStrokeBenchmark TESTNAME krita-benchmarks-KisStrokeBenchmark ${kis_stroke_benchmark_SRCS})
krita_add_benchmark(KisFastMathBenchmark TESTNAME krita-benchmarks-KisFastMath ${kis_fast_math_benchmark_SRCS})
krita_add_benchmark(KisFloodfillBenchmark TESTNAME krita-benchmarks-KisFloodFill ${kis_floodfill_benchmark_SRCS})
krita_add_benchmark(KisGradientBenchmark TESTNAME krita-benchmarks-KisGradientFill ${kis_gradient_benchmark_SRCS})
krita_add_benchmark(KisMaskGeneratorBenchmark TESTNAME krita-benchmarks-KisMaskGenerator ${kis_mask_generator_benchmark_SRCS})
krita_add_benchmark(KisLowMemoryBenchmark TESTNAME krita-benchmarks-KisLowMemory ${kis_low_memory_benchmark_SRCS})
krita_add_benchmark(KisAnimationRenderingBenchmark TESTNAME krita-benchmarks-KisAnimationRenderingBenchmark ${KisAnimationRenderingBenchmark_SRCS})
krita_add_benchmark(KisFilterSelectionsBenchmark TESTNAME krita-image-KisFilterSelectionsBenchmark ${kis_filter_selections_benchmark_SRCS})
if(UNIX)
# krita_add_benchmark(KisCompositionBenchmark TESTNAME krita-benchmarks-KisComposition ${kis_composition_benchmark_SRCS})
endif()
krita_add_benchmark(KisThumbnailBenchmark TESTNAME krita-benchmarks-KisThumbnail ${kis_thumbnail_benchmark_SRCS})
target_link_libraries(KisDatamanagerBenchmark kritaimage Qt5::Test)
target_link_libraries(KisHLineIteratorBenchmark kritaimage Qt5::Test)
target_link_libraries(KisVLineIteratorBenchmark kritaimage Qt5::Test)
target_link_libraries(KisRandomIteratorBenchmark kritaimage Qt5::Test)
target_link_libraries(KisProjectionBenchmark kritaimage kritaui Qt5::Test)
target_link_libraries(KisBContrastBenchmark kritaimage Qt5::Test)
target_link_libraries(KisBlurBenchmark kritaimage Qt5::Test)
target_link_libraries(KisLevelFilterBenchmark kritaimage Qt5::Test)
target_link_libraries(KisPainterBenchmark kritaimage Qt5::Test)
target_link_libraries(KisStrokeBenchmark kritaimage Qt5::Test)
target_link_libraries(KisFastMathBenchmark kritaimage Qt5::Test)
target_link_libraries(KisFloodfillBenchmark kritaimage Qt5::Test)
target_link_libraries(KisGradientBenchmark kritaimage Qt5::Test)
target_link_libraries(KisLowMemoryBenchmark kritaimage Qt5::Test)
target_link_libraries(KisAnimationRenderingBenchmark kritaimage kritaui Qt5::Test)
target_link_libraries(KisFilterSelectionsBenchmark kritaimage Qt5::Test)
if(UNIX)
# target_link_libraries(KisCompositionBenchmark kritaimage Qt5::Test ${LINK_VC_LIB})
if(HAVE_VC)
# set_property(TARGET KisCompositionBenchmark APPEND PROPERTY COMPILE_OPTIONS "${Vc_ARCHITECTURE_FLAGS}")
endif()
endif()
target_link_libraries(KisMaskGeneratorBenchmark kritaimage Qt5::Test)
target_link_libraries(KisThumbnailBenchmark kritaimage Qt5::Test)
diff --git a/build-tools/windows/build.cmd b/build-tools/windows/build.cmd
index 37920aaa46..0e636a61aa 100644
--- a/build-tools/windows/build.cmd
+++ b/build-tools/windows/build.cmd
@@ -1,768 +1,767 @@
@echo off
setlocal enabledelayedexpansion
goto begin
:: Subroutines
:find_on_path out_variable file_name
set %1=%~f$PATH:2
goto :EOF
:get_dir_path out_variable file_path
set %1=%~dp2
goto :EOF
:get_full_path out_variable file_path
setlocal
set FULL_PATH=%~f2
if not exist "%FULL_PATH%" (
set FULL_PATH=
) else (
if exist "%FULL_PATH%\" (
set FULL_PATH=
)
)
endlocal & set "%1=%FULL_PATH%"
goto :EOF
:get_full_path_dir out_variable file_path
setlocal
set FULL_PATH=%~dp2
if not exist "%FULL_PATH%" (
set FULL_PATH=
)
endlocal & set "%1=%FULL_PATH%"
goto :EOF
:prompt_for_string out_variable prompt
set /p %1=%~2^>
goto :EOF
:prompt_for_positive_integer out_variable prompt
setlocal
call :prompt_for_string USER_INPUT "%~2"
if "%USER_INPUT%" == "" set USER_INPUT=0
set /a RESULT=%USER_INPUT%
if not %RESULT% GTR 0 (
set RESULT=
)
endlocal & set "%1=%RESULT%"
goto :EOF
:prompt_for_file out_variable prompt
setlocal
:prompt_for_file__retry
call :prompt_for_string USER_INPUT "%~2"
if "%USER_INPUT%" == "" (
endlocal
set %1=
goto :EOF
)
call :get_full_path RESULT "%USER_INPUT%"
if "%RESULT%" == "" (
echo Input does not point to valid file!
set USER_INPUT=
goto prompt_for_file__retry
)
endlocal & set "%1=%RESULT%"
goto :EOF
:prompt_for_dir out_variable prompt
setlocal
:prompt_for_dir__retry
call :prompt_for_string USER_INPUT "%~2"
if "%USER_INPUT%" == "" (
endlocal
set %1=
goto :EOF
)
call :get_full_path_dir RESULT "%USER_INPUT%\"
if "%RESULT%" == "" (
echo Input does not point to valid dir!
set USER_INPUT=
goto prompt_for_dir__retry
)
endlocal & set "%1=%RESULT%"
goto :EOF
:usage
echo Usage:
echo %~n0 [--no-interactive] [ OPTIONS ... ]
echo.
echo Basic options:
echo --no-interactive Run without interactive prompts
echo When not specified, the script will prompt
echo for some of the parameters.
echo --jobs ^ Set parallel jobs count when building
echo Defaults to no. of logical cores
echo --skip-deps Skips (re)building of deps
echo --skip-krita Skips (re)building of Krita
echo.
echo Path options:
echo --src-dir ^ Specify Krita source dir
echo If unspecified, this will be determined from
echo the script location.
echo --download-dir ^ Specify deps download dir
echo Can be omitted if --skip-deps is used
echo --deps-build-dir ^ Specify deps build dir
echo Can be omitted if --skip-deps is used
echo --deps-install-dir ^ Specify deps install dir
echo --krita-build-dir ^ Specify Krita build dir
echo Can be omitted if --skip-krita is used
echo --krita-install-dir ^ Specify Krita install dir
echo Can be omitted if --skip-krita is used
echo.
goto :EOF
:usage_and_exit
call :usage
exit /b
:usage_and_fail
call :usage
exit /b 100
:: ----------------------------
:begin
echo Krita build script for Windows
echo.
:: command-line args parsing
set ARG_NO_INTERACTIVE=
set ARG_JOBS=
set ARG_SKIP_DEPS=
set ARG_SKIP_KRITA=
set ARG_SRC_DIR=
set ARG_DOWNLOAD_DIR=
set ARG_DEPS_BUILD_DIR=
set ARG_DEPS_INSTALL_DIR=
set ARG_KRITA_BUILD_DIR=
set ARG_KRITA_INSTALL_DIR=
:args_parsing_loop
set CURRENT_MATCHED=
if not "%1" == "" (
if "%1" == "--no-interactive" (
set ARG_NO_INTERACTIVE=1
set CURRENT_MATCHED=1
)
if "%1" == "--jobs" (
if not "%ARG_JOBS%" == "" (
echo ERROR: Arg --jobs specified more than once 1>&2
echo.
goto usage_and_fail
)
set /a "ARG_JOBS=%2"
if not !ARG_JOBS! GTR 0 (
echo ERROR: Arg --jobs is not a positive integer 1>&2
echo.
goto usage_and_fail
)
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--skip-deps" (
set ARG_SKIP_DEPS=1
set CURRENT_MATCHED=1
)
if "%1" == "--skip-krita" (
set ARG_SKIP_KRITA=1
set CURRENT_MATCHED=1
)
if "%1" == "--src-dir" (
if not "%ARG_SRC_DIR%" == "" (
echo ERROR: Arg --src-dir specified more than once 1>&2
echo.
goto usage_and_fail
)
if not exist "%~f2\" (
echo ERROR: Arg --src-dir does not point to a directory 1>&2
echo.
goto usage_and_fail
)
call :get_dir_path ARG_SRC_DIR "%~f2\"
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--download-dir" (
if not "%ARG_DOWNLOAD_DIR%" == "" (
echo ERROR: Arg --download-dir specified more than once 1>&2
echo.
goto usage_and_fail
)
if "%~f2" == "" (
echo ERROR: Arg --download-dir does not point to a valid path 1>&2
echo.
goto usage_and_fail
)
call :get_dir_path ARG_DOWNLOAD_DIR "%~f2\"
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--deps-build-dir" (
if not "%ARG_DEPS_BUILD_DIR%" == "" (
echo ERROR: Arg --deps-build-dir specified more than once 1>&2
echo.
goto usage_and_fail
)
if "%~f2" == "" (
echo ERROR: Arg --deps-build-dir does not point to a valid path 1>&2
echo.
goto usage_and_fail
)
call :get_dir_path ARG_DEPS_BUILD_DIR "%~f2\"
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--deps-install-dir" (
if not "%ARG_DEPS_INSTALL_DIR%" == "" (
echo ERROR: Arg --deps-install-dir specified more than once 1>&2
echo.
goto usage_and_fail
)
if "%~f2" == "" (
echo ERROR: Arg --deps-install-dir does not point to a valid path 1>&2
echo.
goto usage_and_fail
)
call :get_dir_path ARG_DEPS_INSTALL_DIR "%~f2\"
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--krita-build-dir" (
if not "%ARG_KRITA_BUILD_DIR%" == "" (
echo ERROR: Arg --krita-build-dir specified more than once 1>&2
echo.
goto usage_and_fail
)
if "%~f2" == "" (
echo ERROR: Arg --krita-build-dir does not point to a valid path 1>&2
echo.
goto usage_and_fail
)
call :get_dir_path ARG_KRITA_BUILD_DIR "%~f2\"
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--krita-install-dir" (
if not "%ARG_KRITA_INSTALL_DIR%" == "" (
echo ERROR: Arg --krita-install-dir specified more than once 1>&2
echo.
goto usage_and_fail
)
if "%~f2" == "" (
echo ERROR: Arg --krita-install-dir does not point to a valid path 1>&2
echo.
goto usage_and_fail
)
call :get_dir_path ARG_KRITA_INSTALL_DIR "%~f2\"
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--help" (
goto usage_and_exit
)
if not "!CURRENT_MATCHED!" == "1" (
echo ERROR: Unknown option %1 1>&2
echo.
goto usage_and_fail
)
shift /1
goto args_parsing_loop
)
if "%ARG_NO_INTERACTIVE%" == "1" (
echo Non-interactive mode
) else (
echo Interactive mode
:: Trick to pause on exit
call :real_begin
pause
exit /b !ERRORLEVEL!
)
:real_begin
echo.
if "%ARG_SKIP_DEPS%" == "1" (
if "%ARG_SKIP_KRITA%" == "1" (
echo ERROR: You cannot skip both deps and Krita 1>&2
echo.
exit /b 102
)
echo Building of deps will be skipped.
) else (
if "%ARG_SKIP_KRITA%" == "1" (
echo Building of Krita will be skipped.
) else (
echo Both deps and Krita will be built.
)
)
:: Check environment config
if "%CMAKE_EXE%" == "" (
call :find_on_path CMAKE_EXE cmake.exe
if "!CMAKE_EXE!" == "" (
if not "%ARG_NO_INTERACTIVE%" == "1" (
call :prompt_for_file CMAKE_EXE "Provide path to cmake.exe"
)
if "!CMAKE_EXE!" == "" (
echo ERROR: CMake not found! 1>&2
exit /b 102
)
) else (
echo Found CMake on PATH: !CMAKE_EXE!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this correct? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_file CMAKE_EXE "Provide path to cmake.exe"
if "!CMAKE_EXE!" == "" (
echo ERROR: CMake not found! 1>&2
exit /b 102
)
)
)
)
)
echo CMake: %CMAKE_EXE%
if "%SEVENZIP_EXE%" == "" (
call :find_on_path SEVENZIP_EXE 7z.exe
if "!SEVENZIP_EXE!" == "" (
set "SEVENZIP_EXE=%ProgramFiles%\7-Zip\7z.exe"
if "!SEVENZIP_EXE!" == "" (
set "SEVENZIP_EXE=%ProgramFiles(x86)%\7-Zip\7z.exe"
)
if "!SEVENZIP_EXE!" == "" (
echo 7-Zip not found
)
)
)
if "%SEVENZIP_EXE%" == "" (
echo 7-Zip: %SEVENZIP_EXE%
)
if "%MINGW_BIN_DIR%" == "" (
call :find_on_path MINGW_BIN_DIR_MAKE_EXE mingw32-make.exe
if "!MINGW_BIN_DIR_MAKE_EXE!" == "" (
if not "%ARG_NO_INTERACTIVE%" == "1" (
call :prompt_for_file MINGW_BIN_DIR_MAKE_EXE "Provide path to mingw32-make.exe of mingw-w64"
)
if "!MINGW_BIN_DIR_MAKE_EXE!" == "" (
echo ERROR: mingw-w64 not found! 1>&2
exit /b 102
)
call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!"
) else (
call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!"
echo Found mingw on PATH: !MINGW_BIN_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this correct? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_file MINGW_BIN_DIR_MAKE_EXE "Provide path to mingw32-make.exe of mingw-w64"
if "!MINGW_BIN_DIR_MAKE_EXE!" == "" (
echo ERROR: mingw-w64 not found! 1>&2
exit /b 102
)
call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!"
)
)
)
)
echo mingw-w64: %MINGW_BIN_DIR%
if "%PYTHON_BIN_DIR%" == "" (
call :find_on_path PYTHON_BIN_DIR_PYTHON_EXE python.exe
if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" (
if not "%ARG_NO_INTERACTIVE%" == "1" (
call :prompt_for_file PYTHON_BIN_DIR_PYTHON_EXE "Provide path to python.exe of Python 3.6.2"
)
if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" (
echo ERROR: Python not found! 1>&2
exit /b 102
)
call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!"
) else (
call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!"
echo Found Python on PATH: !PYTHON_BIN_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this correct? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_file PYTHON_BIN_DIR_PYTHON_EXE "Provide path to python.exe of Python 3.6.2"
if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" (
echo ERROR: Python not found! 1>&2
exit /b 102
)
call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!"
)
)
)
)
echo Python: %PYTHON_BIN_DIR%
if "%ARG_SKIP_DEPS%" == "1" goto skip_windows_sdk_dir_check
if "%WindowsSdkDir%" == "" if not "%ProgramFiles(x86)%" == "" set "WindowsSdkDir=%ProgramFiles(x86)%\Windows Kits\10"
if "%WindowsSdkDir%" == "" set "WindowsSdkDir=%ProgramFiles(x86)%\Windows Kits\10"
if exist "%WindowsSdkDir%\" (
pushd "%WindowsSdkDir%"
if exist "bin\x64\fxc.exe" (
set HAVE_FXC_EXE=1
) else (
for /f "delims=" %%a in ('dir /a:d /b "bin\10.*"') do (
if exist "bin\%%a\x64\fxc.exe" (
set HAVE_FXC_EXE=1
)
)
)
popd
)
set QT_ENABLE_DYNAMIC_OPENGL=ON
if not "%HAVE_FXC_EXE%" == "1" (
set WindowsSdkDir=
echo Windows SDK 10 with fxc.exe not found
echo Qt will *not* be built with ANGLE ^(dynamic OpenGL^) support.
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
exit /b 102
)
)
set QT_ENABLE_DYNAMIC_OPENGL=OFF
) else echo Windows SDK 10 with fxc.exe found on %WindowsSdkDir%
:skip_windows_sdk_dir_check
if not "%ARG_JOBS%" == "" (
set "PARALLEL_JOBS=%ARG_JOBS%"
)
if "%PARALLEL_JOBS%" == "" (
echo Number of logical CPU cores detected: %NUMBER_OF_PROCESSORS%
echo Enabling %NUMBER_OF_PROCESSORS% parallel jobs
set PARALLEL_JOBS=%NUMBER_OF_PROCESSORS%
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this correct? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_positive_integer PARALLEL_JOBS "Provide no. of parallel jobs"
if "!PARALLEL_JOBS!" == "" (
echo ERROR: Invalid job count! 1>&2
exit /b 102
)
)
)
)
echo Parallel jobs count: %PARALLEL_JOBS%
if not "%ARG_SRC_DIR%" == "" (
set "KRITA_SRC_DIR=%ARG_SRC_DIR%"
)
if "%KRITA_SRC_DIR%" == "" (
:: Check whether this looks like to be in the source tree
set "_temp=%~dp0"
if "!_temp:~-21!" == "\build-tools\windows\" (
if exist "!_temp:~0,-21!\CMakeLists.txt" (
if exist "!_temp:~0,-21!\3rdparty\CMakeLists.txt" (
set "KRITA_SRC_DIR=!_temp:~0,-21!\"
echo Script is running inside Krita src dir
)
)
)
)
if "%KRITA_SRC_DIR%" == "" (
if not "%ARG_NO_INTERACTIVE%" == "1" (
call :prompt_for_dir KRITA_SRC_DIR "Provide path of Krita src dir"
)
if "!KRITA_SRC_DIR!" == "" (
echo ERROR: Krita src dir not found! 1>&2
exit /b 102
)
)
echo Krita src: %KRITA_SRC_DIR%
if "%ARG_SKIP_DEPS%" == "1" goto skip_deps_args_check
if not "%ARG_DOWNLOAD_DIR%" == "" (
set "DEPS_DOWNLOAD_DIR=%ARG_DOWNLOAD_DIR%"
)
if "%DEPS_DOWNLOAD_DIR%" == "" (
set DEPS_DOWNLOAD_DIR=%CD%\d\
echo Using default deps download dir: !DEPS_DOWNLOAD_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_dir DEPS_DOWNLOAD_DIR "Provide path of depps download dir"
)
)
if "!DEPS_DOWNLOAD_DIR!" == "" (
echo ERROR: Deps download dir not set! 1>&2
exit /b 102
)
)
echo Deps download dir: %DEPS_DOWNLOAD_DIR%
if not "%ARG_DEPS_BUILD_DIR%" == "" (
set "DEPS_BUILD_DIR=%ARG_DEPS_BUILD_DIR%"
)
if "%DEPS_BUILD_DIR%" == "" (
set DEPS_BUILD_DIR=%CD%\b_deps\
echo Using default deps build dir: !DEPS_BUILD_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_dir DEPS_BUILD_DIR "Provide path of deps build dir"
)
)
if "!DEPS_BUILD_DIR!" == "" (
echo ERROR: Deps build dir not set! 1>&2
exit /b 102
)
)
echo Deps build dir: %DEPS_BUILD_DIR%
:skip_deps_args_check
if not "%ARG_DEPS_INSTALL_DIR%" == "" (
set "DEPS_INSTALL_DIR=%ARG_DEPS_INSTALL_DIR%"
)
if "%DEPS_INSTALL_DIR%" == "" (
set DEPS_INSTALL_DIR=%CD%\i_deps\
echo Using default deps install dir: !DEPS_INSTALL_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_dir DEPS_INSTALL_DIR "Provide path of deps install dir"
)
)
if "!DEPS_INSTALL_DIR!" == "" (
echo ERROR: Deps install dir not set! 1>&2
exit /b 102
)
)
echo Deps install dir: %DEPS_INSTALL_DIR%
if "%ARG_SKIP_KRITA%" == "1" goto skip_krita_args_check
if not "%ARG_KRITA_BUILD_DIR%" == "" (
set "KRITA_BUILD_DIR=%ARG_KRITA_BUILD_DIR%"
)
if "%KRITA_BUILD_DIR%" == "" (
set KRITA_BUILD_DIR=%CD%\b\
echo Using default Krita build dir: !KRITA_BUILD_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_dir KRITA_BUILD_DIR "Provide path of Krita build dir"
)
)
if "!KRITA_BUILD_DIR!" == "" (
echo ERROR: Krita build dir not set! 1>&2
exit /b 102
)
)
echo Krita build dir: %KRITA_BUILD_DIR%
if not "%ARG_KRITA_INSTALL_DIR%" == "" (
set "KRITA_INSTALL_DIR=%ARG_KRITA_INSTALL_DIR%"
)
if "%KRITA_INSTALL_DIR%" == "" (
set KRITA_INSTALL_DIR=%CD%\i\
echo Using default Krita install dir: !KRITA_INSTALL_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_dir KRITA_INSTALL_DIR "Provide path of Krita install dir"
)
)
if "!KRITA_INSTALL_DIR!" == "" (
echo ERROR: Krita install dir not set! 1>&2
exit /b 102
)
)
echo Krita install dir: %KRITA_INSTALL_DIR%
:skip_krita_args_check
echo.
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is the above ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
exit /b 1
)
echo.
)
:: Initialize clean PATH
set PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\
set PATH=%MINGW_BIN_DIR%;%PYTHON_BIN_DIR%;%PATH%
echo Creating dirs...
if NOT "%ARG_SKIP_DEPS%" == "1" (
mkdir %DEPS_DOWNLOAD_DIR%
if errorlevel 1 (
if not exist "%DEPS_DOWNLOAD_DIR%\" (
echo ERROR: Cannot create deps download dir! 1>&2
exit /b 103
)
)
mkdir %DEPS_BUILD_DIR%
if errorlevel 1 (
if not exist "%DEPS_BUILD_DIR%\" (
echo ERROR: Cannot create deps build dir! 1>&2
exit /b 103
)
)
mkdir %DEPS_INSTALL_DIR%
if errorlevel 1 (
if not exist "%DEPS_INSTALL_DIR%\" (
echo ERROR: Cannot create deps install dir! 1>&2
exit /b 103
)
)
)
if NOT "%ARG_SKIP_KRITA%" == "1" (
mkdir %KRITA_BUILD_DIR%
if errorlevel 1 (
if not exist "%KRITA_BUILD_DIR%\" (
echo ERROR: Cannot create Krita build dir! 1>&2
exit /b 103
)
)
mkdir %KRITA_INSTALL_DIR%
if errorlevel 1 (
if not exist "%KRITA_INSTALL_DIR%\" (
echo ERROR: Cannot create Krita install dir! 1>&2
exit /b 103
)
)
)
echo.
set CMAKE_BUILD_TYPE=RelWithDebInfo
set QT_ENABLE_DEBUG_INFO=OFF
:: Paths for CMake
set "BUILDDIR_DOWNLOAD_CMAKE=%DEPS_DOWNLOAD_DIR:\=/%"
set "BUILDDIR_DOWNLOAD_CMAKE=%BUILDDIR_DOWNLOAD_CMAKE: =\ %"
set "BUILDDIR_DEPS_INSTALL_CMAKE=%DEPS_INSTALL_DIR:\=/%"
set "BUILDDIR_DEPS_INSTALL_CMAKE=%BUILDDIR_DEPS_INSTALL_CMAKE: =\ %"
set "BUILDDIR_KRITA_INSTALL_CMAKE=%KRITA_INSTALL_DIR:\=/%"
set "BUILDDIR_KRITA_INSTALL_CMAKE=%BUILDDIR_KRITA_INSTALL_CMAKE: =\ %"
set PATH=%DEPS_INSTALL_DIR%\bin;%PATH%
if not "%GETTEXT_SEARCH_PATH%" == "" (
set PATH=%PATH%;%GETTEXT_SEARCH_PATH%
)
if "%ARG_SKIP_DEPS%" == "1" goto skip_build_deps
pushd %DEPS_BUILD_DIR%
if errorlevel 1 (
echo ERROR: Cannot enter deps build dir! 1>&2
exit /b 104
)
echo Running CMake for deps...
"%CMAKE_EXE%" "%KRITA_SRC_DIR%\3rdparty" ^
-DSUBMAKE_JOBS=%PARALLEL_JOBS% ^
-DQT_ENABLE_DEBUG_INFO=%QT_ENABLE_DEBUG_INFO% ^
-DQT_ENABLE_DYNAMIC_OPENGL=%QT_ENABLE_DYNAMIC_OPENGL% ^
-DEXTERNALS_DOWNLOAD_DIR=%BUILDDIR_DOWNLOAD_CMAKE% ^
-DINSTALL_ROOT=%BUILDDIR_DEPS_INSTALL_CMAKE% ^
-G "MinGW Makefiles" ^
-DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%
if errorlevel 1 (
echo ERROR: CMake configure failed! 1>&2
exit /b 104
)
echo.
set EXT_TARGETS=patch png2ico gettext qt zlib boost eigen3 exiv2 fftw3 ilmbase
set EXT_TARGETS=%EXT_TARGETS% jpeg lcms2 ocio openexr png tiff gsl vc libraw
set EXT_TARGETS=%EXT_TARGETS% freetype poppler kwindowsystem drmingw gmic
set EXT_TARGETS=%EXT_TARGETS% python sip pyqt
for %%a in (%EXT_TARGETS%) do (
echo Building ext_%%a...
"%CMAKE_EXE%" --build . --config %CMAKE_BUILD_TYPE% --target ext_%%a
if errorlevel 1 (
echo ERROR: Building of ext_%%a failed! 1>&2
exit /b 105
)
- echo.
)
echo.
echo ******** Built deps ********
popd
:skip_build_deps
if "%ARG_SKIP_KRITA%" == "1" goto skip_build_krita
pushd %KRITA_BUILD_DIR%
if errorlevel 1 (
echo ERROR: Cannot enter Krita build dir! 1>&2
exit /b 104
)
echo Running CMake for Krita...
"%CMAKE_EXE%" "%KRITA_SRC_DIR%\." ^
-DBoost_DEBUG=OFF ^
-DBOOST_INCLUDEDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/include ^
-DBOOST_ROOT=%BUILDDIR_DEPS_INSTALL_CMAKE% ^
-DBOOST_LIBRARYDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/lib ^
-DCMAKE_PREFIX_PATH=%BUILDDIR_DEPS_INSTALL_CMAKE% ^
-DCMAKE_INSTALL_PREFIX=%BUILDDIR_KRITA_INSTALL_CMAKE% ^
-DBUILD_TESTING=OFF ^
-DHAVE_MEMORY_LEAK_TRACKER=OFF ^
-Wno-dev ^
-G "MinGW Makefiles" ^
-DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%
if errorlevel 1 (
echo ERROR: CMake configure failed! 1>&2
exit /b 104
)
echo.
echo Building Krita...
"%CMAKE_EXE%" --build . --config %CMAKE_BUILD_TYPE% --target install -- -j%PARALLEL_JOBS%
if errorlevel 1 (
echo ERROR: Building of Krita failed! 1>&2
exit /b 105
)
echo.
echo ******** Built Krita ********
popd
:skip_build_krita
echo Krita build completed!
diff --git a/cmake/modules/GetGitRevisionDescription.cmake b/cmake/modules/GetGitRevisionDescription.cmake
index 28db419cca..4550c6af4c 100644
--- a/cmake/modules/GetGitRevisionDescription.cmake
+++ b/cmake/modules/GetGitRevisionDescription.cmake
@@ -1,157 +1,161 @@
# - Returns a version string from Git
#
# These functions force a re-configure on each git commit so that you can
# trust the values of the variables in your build system.
#
# get_git_head_revision( [ ...])
#
# Returns the refspec and sha hash of the current head revision
#
# git_describe( [ ...])
#
# Returns the results of git describe on the source tree, and adjusting
# the output so that it tests false if an error occurs.
#
# git_get_exact_tag( [ ...])
#
# Returns the results of git describe --exact-match on the source tree,
# and adjusting the output so that it tests false if there was no exact
# matching tag.
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
if(__get_git_revision_description)
return()
endif()
set(__get_git_revision_description YES)
# We must run the following at "include" time, not at function call time,
# to find the path to this module rather than the path to a calling list file
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
function(get_git_head_revision _refspecvar _hashvar)
set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
# We have reached the root directory, we are not in git
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
return()
endif()
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
endwhile()
- # check if this is a submodule
+ # check if this is a linked working tree (e.g. submodule or git-worktree)
if(NOT IS_DIRECTORY ${GIT_DIR})
- file(READ ${GIT_DIR} submodule)
- string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
- get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
- get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
+ file(READ ${GIT_DIR} gitdirfile)
+ string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_PATH ${gitdirfile})
+ if(IS_ABSOLUTE ${GIT_DIR_PATH})
+ get_filename_component(GIT_DIR ${GIT_DIR_PATH} ABSOLUTE)
+ else()
+ get_filename_component(LINKED_DIR ${GIT_DIR} PATH)
+ get_filename_component(GIT_DIR ${LINKED_DIR}/${GIT_DIR_PATH} ABSOLUTE)
+ endif()
endif()
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
if(NOT EXISTS "${GIT_DATA}")
file(MAKE_DIRECTORY "${GIT_DATA}")
endif()
if(NOT EXISTS "${GIT_DIR}/HEAD")
return()
endif()
set(HEAD_FILE "${GIT_DATA}/HEAD")
configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
"${GIT_DATA}/grabRef.cmake"
@ONLY)
include("${GIT_DATA}/grabRef.cmake")
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
endfunction()
function(git_describe _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
return()
endif()
# TODO sanitize
#if((${ARGN}" MATCHES "&&") OR
# (ARGN MATCHES "||") OR
# (ARGN MATCHES "\\;"))
# message("Please report the following error to the project!")
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
#endif()
#message(STATUS "Arguments to execute_process: ${ARGN}")
execute_process(COMMAND
"${GIT_EXECUTABLE}"
describe
${hash}
${ARGN}
WORKING_DIRECTORY
"${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var} "${out}" PARENT_SCOPE)
endfunction()
function(get_git_branch _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
if(NOT GIT_FOUND)
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
return()
endif()
execute_process(COMMAND
"${GIT_EXECUTABLE}"
symbolic-ref --short HEAD
WORKING_DIRECTORY
"${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var} "${out}" PARENT_SCOPE)
endfunction()
function(git_get_exact_tag _var)
git_describe(out --exact-match ${ARGN})
set(${_var} "${out}" PARENT_SCOPE)
endfunction()
diff --git a/cmake/modules/GetGitRevisionDescription.cmake.in b/cmake/modules/GetGitRevisionDescription.cmake.in
index 888ce13aab..afd305cd5e 100644
--- a/cmake/modules/GetGitRevisionDescription.cmake.in
+++ b/cmake/modules/GetGitRevisionDescription.cmake.in
@@ -1,38 +1,50 @@
#
# Internal file for GetGitRevisionDescription.cmake
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
set(HEAD_HASH)
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
+set(GIT_DIR "@GIT_DIR@")
+# handle git-worktree
+if(EXISTS "${GIT_DIR}/commondir")
+ file(READ "${GIT_DIR}/commondir" GIT_DIR_NEW LIMIT 1024)
+ string(STRIP "${GIT_DIR_NEW}" GIT_DIR_NEW)
+ if(NOT IS_ABSOLUTE "${GIT_DIR_NEW}")
+ get_filename_component(GIT_DIR_NEW ${GIT_DIR}/${GIT_DIR_NEW} ABSOLUTE)
+ endif()
+ if(EXISTS "${GIT_DIR_NEW}")
+ set(GIT_DIR "${GIT_DIR_NEW}")
+ endif()
+endif()
if(HEAD_CONTENTS MATCHES "ref")
# named branch
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
- if(EXISTS "@GIT_DIR@/${HEAD_REF}")
- configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
- elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}")
- configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
+ if(EXISTS "${GIT_DIR}/${HEAD_REF}")
+ configure_file("${GIT_DIR}/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
+ elseif(EXISTS "${GIT_DIR}/logs/${HEAD_REF}")
+ configure_file("${GIT_DIR}/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
set(HEAD_HASH "${HEAD_REF}")
endif()
else()
# detached HEAD
- configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
+ configure_file("${GIT_DIR}/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
endif()
if(NOT HEAD_HASH)
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
endif()
diff --git a/krita/data/profiles/elles-icc-profiles/CMakeLists.txt b/krita/data/profiles/elles-icc-profiles/CMakeLists.txt
index 1b3d466513..725f634049 100644
--- a/krita/data/profiles/elles-icc-profiles/CMakeLists.txt
+++ b/krita/data/profiles/elles-icc-profiles/CMakeLists.txt
@@ -1,28 +1,25 @@
########### install files ###############
install(FILES
ACEScg-elle-V4-g10.icc
ClayRGB-elle-V4-srgbtrc.icc
Gray-D50-elle-V2-g10.icc
Gray-D50-elle-V2-g18.icc
Gray-D50-elle-V2-g22.icc
Gray-D50-elle-V2-srgbtrc.icc
Gray-D50-elle-V4-g10.icc
Gray-D50-elle-V4-g18.icc
Gray-D50-elle-V4-g22.icc
Gray-D50-elle-V4-srgbtrc.icc
Gray-D50-elle-V2-labl.icc
Gray-D50-elle-V4-labl.icc
Gray-D50-elle-V2-rec709.icc
Gray-D50-elle-V4-rec709.icc
- Lab-D50-Identity-elle-V2.icc
- Lab-D50-Identity-elle-V4.icc
-
sRGB-elle-V2-g10.icc
sRGB-elle-V2-srgbtrc.icc
Lab-D50-Identity-elle-V2.icc
Lab-D50-Identity-elle-V4.icc
XYZ-D50-Identity-elle-V4.icc
DESTINATION ${SHARE_INSTALL_PREFIX}/color/icc/krita)
diff --git a/krita/krita.action b/krita/krita.action
index cbc3dfbe8c..6015284b87 100644
--- a/krita/krita.action
+++ b/krita/krita.action
@@ -1,3090 +1,3102 @@
General
Open Resources Folder
Opens a file browser at the location Krita saves resources such as brushes to.
Opens a file browser at the location Krita saves resources such as brushes to.
Open Resources Folder
0
0
false
Cleanup removed files...
Cleanup removed files
Cleanup removed files
0
0
false
C&ascade
Cascade
Cascade
10
0
false
&Tile
Tile
Tile
10
0
false
Create Resource Bundle...
Create Resource Bundle
Create Resource Bundle
0
0
false
Show File Toolbar
Show File Toolbar
Show File Toolbar
false
Show color selector
Show color selector
Show color selector
Shift+I
false
Show MyPaint shade selector
Show MyPaint shade selector
Show MyPaint shade selector
Shift+M
false
Show minimal shade selector
Show minimal shade selector
Show minimal shade selector
Shift+N
false
Show color history
Show color history
Show color history
H
false
Show common colors
Show common colors
Show common colors
U
false
Show Tool Options
Show Tool Options
Show Tool Options
\
false
Show Brush Editor
Show Brush Editor
Show Brush Editor
F5
false
Show Brush Presets
Show Brush Presets
Show Brush Presets
F6
false
Toggle Tablet Debugger
Toggle Tablet Debugger
Toggle Tablet Debugger
0
0
Ctrl+Shift+T
false
Show system information for bug reports.
Show system information for bug reports.
Show system information for bug reports.
false
Rename Composition...
Rename Composition
Rename Composition
0
0
false
Update Composition
Update Composition
Update Composition
0
0
false
+
+
+ Use multiple of 2 for pixel scale
+ Use multiple of 2 for pixel scale
+ Use multiple of 2 for pixel scale
+ Use multiple of 2 for pixel scale
+ 1
+ 0
+
+ true
+
+
Painting
lightness-increase
Make brush color lighter
Make brush color lighter
Make brush color lighter
0
0
L
false
lightness-decrease
Make brush color darker
Make brush color darker
Make brush color darker
0
0
K
false
Make brush color more saturated
Make brush color more saturated
Make brush color more saturated
false
Make brush color more desaturated
Make brush color more desaturated
Make brush color more desaturated
false
Shift brush color hue clockwise
Shift brush color hue clockwise
Shift brush color hue clockwise
false
Shift brush color hue counter-clockwise
Shift brush color hue counter-clockwise
Shift brush color hue counter-clockwise
false
Make brush color more red
Make brush color more red
Make brush color more red
false
Make brush color more green
Make brush color more green
Make brush color more green
false
Make brush color more blue
Make brush color more blue
Make brush color more blue
false
Make brush color more yellow
Make brush color more yellow
Make brush color more yellow
false
opacity-increase
Increase opacity
Increase opacity
Increase opacity
0
0
O
false
opacity-decrease
Decrease opacity
Decrease opacity
Decrease opacity
0
0
I
false
draw-eraser
Set eraser mode
Set eraser mode
Set eraser mode
10000
0
E
true
view-refresh
Reload Original Preset
Reload Original Preset
Reload Original Preset
10000
false
transparency-unlocked
Preserve Alpha
Preserve Alpha
Preserve Alpha
10000
true
transform_icons_penPressure
Use Pen Pressure
Use Pen Pressure
Use Pen Pressure
10000
true
symmetry-horizontal
Horizontal Mirror Tool
Horizontal Mirror Tool
Horizontal Mirror Tool
10000
true
symmetry-vertical
Vertical Mirror Tool
Vertical Mirror Tool
Vertical Mirror Tool
10000
true
Hide Mirror X Line
Hide Mirror X Line
Hide Mirror X Line
10000
true
Hide Mirror Y Line
Hide Mirror Y Line
Hide Mirror Y Line
10000
true
Lock
Lock X Line
Lock X Line
10000
true
Lock Y Line
Lock Y Line
Lock Y Line
10000
true
Move to Canvas Center
Move to Canvas Center X
Move to Canvas Center X
10000
false
Move to Canvas Center Y
Move to Canvas Center Y
Move to Canvas Center Y
10000
false
&Invert Selection
Invert current selection
Invert Selection
10000000000
100
Ctrl+Shift+I
false
&Toggle Selection Display Mode
Toggle Selection Display Mode
Toggle Selection Display Mode
0
0
false
Next Favourite Preset
Next Favourite Preset
Next Favourite Preset
,
false
Previous Favourite Preset
Previous Favourite Preset
Previous Favourite Preset
.
false
preset-switcher
Switch to Previous Preset
Switch to Previous Preset
Switch to Previous Preset
/
false
Hide Brushes and Stuff Toolbar
Hide Brushes and Stuff Toolbar
Hide Brushes and Stuff Toolbar
true
Reset Foreground and Background Color
Reset Foreground and Background Color
Reset Foreground and Background Color
D
false
Swap Foreground and Background Color
Swap Foreground and Background Color
Swap Foreground and Background Color
X
false
smoothing-weighted
Brush Smoothing: Weighted
Brush Smoothing: Weighted
Brush Smoothing: Weighted
false
smoothing-no
Brush Smoothing: Disabled
Brush Smoothing: Disabled
Brush Smoothing: Disabled
false
smoothing-stabilizer
Brush Smoothing: Stabilizer
Brush Smoothing: Stabilizer
Brush Smoothing: Stabilizer
false
brushsize-decrease
Decrease Brush Size
Decrease Brush Size
Decrease Brush Size
0
0
[
false
smoothing-basic
Brush Smoothing: Basic
Brush Smoothing: Basic
Brush Smoothing: Basic
false
brushsize-increase
Increase Brush Size
Increase Brush Size
Increase Brush Size
0
0
]
false
Toggle Assistant
Toggle Assistant
ToggleAssistant
Ctrl+Shift+L
true
Undo Polygon Selection Points
Undo Polygon Selection Points
Undo Polygon Selection Points
Shift+Z
false
Fill with Foreground Color (Opacity)
Fill with Foreground Color (Opacity)
Fill with Foreground Color (Opacity)
10000
1
Ctrl+Shift+Backspace
false
Fill with Background Color (Opacity)
Fill with Background Color (Opacity)
Fill with Background Color (Opacity)
10000
1
Ctrl+Backspace
false
Fill with Pattern (Opacity)
Fill with Pattern (Opacity)
Fill with Pattern (Opacity)
10000
1
false
Convert &to Shape
Convert to Shape
Convert to Shape
10000000000
0
false
&Select Opaque
Select Opaque
Select Opaque
100000
100
false
&Show Global Selection Mask
Shows global selection as a usual selection mask in <interface>Layers</interface> docker
Show Global Selection Mask
100000
100
true
Filters
color-to-alpha
&Color to Alpha...
Color to Alpha
Color to Alpha
10000
0
false
&Top Edge Detection
Top Edge Detection
Top Edge Detection
10000
0
false
&Index Colors...
Index Colors
Index Colors
10000
0
false
Emboss Horizontal &Only
Emboss Horizontal Only
Emboss Horizontal Only
10000
0
false
D&odge
Dodge
Dodge
10000
0
false
&Sharpen
Sharpen
Sharpen
10000
0
false
B&urn
Burn
Burn
10000
0
false
&Mean Removal
Mean Removal
Mean Removal
10000
0
false
&Gaussian Blur...
Gaussian Blur
Gaussian Blur
10000
0
false
Emboss &in All Directions
Emboss in All Directions
Emboss in All Directions
10000
0
false
&Small Tiles...
Small Tiles
Small Tiles
10000
0
false
&Levels...
Levels
Levels
10000
0
Ctrl+L
false
&Sobel...
Sobel
Sobel
10000
0
false
&Wave...
Wave
Wave
10000
0
false
&Motion Blur...
Motion Blur
Motion Blur
10000
0
false
&Color Adjustment curves...
Color Adjustment curves
Color Adjustment curves
10000
0
Ctrl+M
false
Pi&xelize...
Pixelize
Pixelize
10000
0
false
Emboss (&Laplacian)
Emboss (Laplacian)
Emboss (Laplacian)
10000
0
false
&Left Edge Detection
Left Edge Detection
Left Edge Detection
10000
0
false
&Blur...
Blur
Blur
10000
0
false
&Raindrops...
Raindrops
Raindrops
10000
0
false
&Bottom Edge Detection
Bottom Edge Detection
Bottom Edge Detection
10000
0
false
&Random Noise...
Random Noise
Random Noise
10000
0
false
&Brightness/Contrast curve...
Brightness/Contrast curve
Brightness/Contrast curve
10000
0
false
Colo&r Balance..
Color Balance..
Color Balance..
10000
0
Ctrl+B
false
&Phong Bumpmap...
Phong Bumpmap
Phong Bumpmap
10000
0
false
&Desaturate
Desaturate
Desaturate
10000
0
Ctrl+Shift+U
false
Color &Transfer...
Color Transfer
Color Transfer
10000
0
false
Emboss &Vertical Only
Emboss Vertical Only
Emboss Vertical Only
10000
0
false
&Lens Blur...
Lens Blur
Lens Blur
10000
0
false
M&inimize Channel
Minimize Channel
Minimize Channel
10000
0
false
M&aximize Channel
Maximize Channel
Maximize Channel
10000
0
false
&Oilpaint...
Oilpaint
Oilpaint
10000
0
false
&Right Edge Detection
Right Edge Detection
Right Edge Detection
10000
0
false
&Auto Contrast
Auto Contrast
Auto Contrast
10000
0
false
&Round Corners...
Round Corners
Round Corners
10000
0
false
&Unsharp Mask...
Unsharp Mask
Unsharp Mask
10000
0
false
&Emboss with Variable Depth...
Emboss with Variable Depth
Emboss with Variable Depth
10000
0
false
Emboss &Horizontal && Vertical
Emboss Horizontal & Vertical
Emboss Horizontal & Vertical
10000
0
false
Random &Pick...
Random Pick
Random Pick
10000
0
false
&Gaussian Noise Reduction...
Gaussian Noise Reduction
Gaussian Noise Reduction
10000
0
false
&Posterize...
Posterize
Posterize
10000
0
false
&Wavelet Noise Reducer...
Wavelet Noise Reducer
Wavelet Noise Reducer
10000
0
false
&HSV Adjustment...
HSV Adjustment
HSV Adjustment
10000
0
Ctrl+U
false
Tool Shortcuts
Dynamic Brush Tool
Dynamic Brush Tool
Dynamic Brush Tool
false
Crop Tool
Crop the image to an area
Crop the image to an area
C
false
Polygon Tool
Polygon Tool. Shift-mouseclick ends the polygon.
Polygon Tool. Shift-mouseclick ends the polygon.
false
References
References
References
false
Rectangle Tool
Rectangle Tool
Rectangle Tool
false
Multibrush Tool
Multibrush Tool
Multibrush Tool
Q
false
Lazy Brush Tool
Lazy Brush Tool
Lazy Brush Tool
Smart Patch Tool
Smart Patch Tool
Smart Patch Tool
Pan Tool
Pan Tool
Pan Tool
Shape Manipulation Tool
Shape Manipulation Tool
Shape Manipulation Tool
false
Color Picker
Select a color from the image or current layer
Select a color from the image or current layer
P
false
Text Editing Tool
Text editing
Text editing
false
Outline Selection Tool
Outline Selection Tool
Outline Selection Tool
false
Artistic Text Tool
Artistic text editing
Artistic text editing
false
Bezier Curve Selection Tool
Select a
Bezier Curve Selection Tool
false
Similar Color Selection Tool
Select a
Similar Color Selection Tool
false
Fill Tool
Fill a contiguous area of color with a color, or fill a selection.
Fill a contiguous area of color with a color, or fill a selection.
F
false
Line Tool
Line Tool
Line Tool
false
Freehand Path Tool
Freehand Path Tool
Freehand Path Tool
false
Bezier Curve Tool
Bezier Curve Tool. Shift-mouseclick ends the curve.
Bezier Curve Tool. Shift-mouseclick ends the curve.
false
Ellipse Tool
Ellipse Tool
Ellipse Tool
false
Freehand Brush Tool
Freehand Brush Tool
Freehand Brush Tool
B
false
Create object
Create object
Create object
false
Elliptical Selection Tool
Elliptical Selection Tool
Elliptical Selection Tool
J
false
Contiguous Selection Tool
Contiguous Selection Tool
Contiguous Selection Tool
false
Pattern editing
Pattern editing
Pattern editing
false
Review
Review
Review
false
Draw a gradient.
Draw a gradient.
Draw a gradient.
G
false
Polygonal Selection Tool
Polygonal Selection Tool
Polygonal Selection Tool
false
Measurement Tool
Measure the distance between two points
Measure the distance between two points
false
Rectangular Selection Tool
Rectangular Selection Tool
Rectangular Selection Tool
Ctrl+R
false
Move Tool
Move a layer
Move a layer
T
false
Vector Image Tool
Vector Image (EMF/WMF/SVM/SVG) tool
Vector Image (EMF/WMF/SVM/SVG) tool
false
Calligraphy
Calligraphy
Calligraphy
false
Path editing
Path editing
Path editing
false
Zoom Tool
Zoom Tool
Zoom Tool
false
Polyline Tool
Polyline Tool. Shift-mouseclick ends the polyline.
Polyline Tool. Shift-mouseclick ends the polyline.
false
Transform Tool
Transform a layer or a selection
Transform a layer or a selection
Ctrl+T
false
-
+
- Ruler assistant editor tool
+ Assistant Tool
- Ruler assistant editor tool
- Ruler assistant editor tool
+ Assistant Tool
+ Assistant Tool
false
Text tool
Text tool
Text tool
false
Gradient Editing Tool
Gradient editing
Gradient editing
false
Blending Modes
Select Normal Blending Mode
Select Normal Blending Mode
Select Normal Blending Mode
0
0
Alt+Shift+N
false
Select Dissolve Blending Mode
Select Dissolve Blending Mode
Select Dissolve Blending Mode
0
0
Alt+Shift+I
false
Select Behind Blending Mode
Select Behind Blending Mode
Select Behind Blending Mode
0
0
Alt+Shift+Q
false
Select Clear Blending Mode
Select Clear Blending Mode
Select Clear Blending Mode
0
0
Alt+Shift+R
false
Select Darken Blending Mode
Select Darken Blending Mode
Select Darken Blending Mode
0
0
Alt+Shift+K
false
Select Multiply Blending Mode
Select Multiply Blending Mode
Select Multiply Blending Mode
0
0
Alt+Shift+M
false
Select Color Burn Blending Mode
Select Color Burn Blending Mode
Select Color Burn Blending Mode
0
0
Alt+Shift+B
false
Select Linear Burn Blending Mode
Select Linear Burn Blending Mode
Select Linear Burn Blending Mode
0
0
Alt+Shift+A
false
Select Lighten Blending Mode
Select Lighten Blending Mode
Select Lighten Blending Mode
0
0
Alt+Shift+G
false
Select Screen Blending Mode
Select Screen Blending Mode
Select Screen Blending Mode
0
0
Alt+Shift+S
false
Select Color Dodge Blending Mode
Select Color Dodge Blending Mode
Select Color Dodge Blending Mode
0
0
Alt+Shift+D
false
Select Linear Dodge Blending Mode
Select Linear Dodge Blending Mode
Select Linear Dodge Blending Mode
0
0
Alt+Shift+W
false
Select Overlay Blending Mode
Select Overlay Blending Mode
Select Overlay Blending Mode
0
0
Alt+Shift+O
false
Select Hard Overlay Blending Mode
Select Hard Overlay Blending Mode
Select Hard Overlay Blending Mode
0
0
Alt+Shift+P
false
Select Soft Light Blending Mode
Select Soft Light Blending Mode
Select Soft Light Blending Mode
0
0
Alt+Shift+F
false
Select Hard Light Blending Mode
Select Hard Light Blending Mode
Select Hard Light Blending Mode
0
0
Alt+Shift+H
false
Select Vivid Light Blending Mode
Select Vivid Light Blending Mode
Select Vivid Light Blending Mode
0
0
Alt+Shift+V
false
Select Linear Light Blending Mode
Select Linear Light Blending Mode
Select Linear Light Blending Mode
0
0
Alt+Shift+J
false
Select Pin Light Blending Mode
Select Pin Light Blending Mode
Select Pin Light Blending Mode
0
0
Alt+Shift+Z
false
Select Hard Mix Blending Mode
Select Hard Mix Blending Mode
Select Hard Mix Blending Mode
0
0
Alt+Shift+L
false
Select Difference Blending Mode
Select Difference Blending Mode
Select Difference Blending Mode
0
0
Alt+Shift+E
false
Select Exclusion Blending Mode
Select Exclusion Blending Mode
Select Exclusion Blending Mode
0
0
Alt+Shift+X
false
Select Hue Blending Mode
Select Hue Blending Mode
Select Hue Blending Mode
0
0
Alt+Shift+U
false
Select Saturation Blending Mode
Select Saturation Blending Mode
Select Saturation Blending Mode
0
0
Alt+Shift+T
false
Select Color Blending Mode
Select Color Blending Mode
Select Color Blending Mode
0
0
Alt+Shift+C
false
Select Luminosity Blending Mode
Select Luminosity Blending Mode
Select Luminosity Blending Mode
0
0
Alt+Shift+Y
false
Animation
Previous frame
Move to previous frame
Move to previous frame
1
0
false
Next frame
Move to next frame
Move to next frame
1
0
false
Play / pause animation
Play / pause animation
Play / pause animation
1
0
false
Add blank frame
Add blank frame
Add blank frame
100000
0
false
Copy Frame
Add duplicate frame
Add duplicate frame
100000
0
false
Toggle onion skin
Toggle onion skin
Toggle onion skin
100000
0
false
Previous Keyframe
false
Next Keyframe
false
First Frame
false
Last Frame
false
Auto Frame Mode
true
true
Add blank frame
Add blank frame
Add blank frame
100000
0
false
Show in Timeline
true
Layers
Activate next layer
Activate next layer
Activate next layer
1000
0
PgUp
false
Activate previous layer
Activate previous layer
Activate previous layer
1000
0
PgDown
false
Activate previously selected layer
Activate previously selected layer
Activate previously selected layer
1000
0
;
false
groupLayer
&Group Layer
Group Layer
Group Layer
1000
0
false
cloneLayer
&Clone Layer
Clone Layer
Clone Layer
1000
0
false
vectorLayer
&Vector Layer
Vector Layer
Vector Layer
1000
0
false
filterLayer
&Filter Layer...
Filter Layer
Filter Layer
1000
0
false
fillLayer
&Fill Layer...
Fill Layer
Fill Layer
1000
0
false
fileLayer
&File Layer...
File Layer
File Layer
1000
0
false
transparencyMask
&Transparency Mask
Transparency Mask
Transparency Mask
100000
0
false
filterMask
&Filter Mask...
Filter Mask
Filter Mask
100000
0
false
filterMask
&Colorize Mask
Colorize Mask
Colorize Mask
100000
0
false
transformMask
&Transform Mask...
Transform Mask
Transform Mask
100000
0
false
selectionMask
&Local Selection
Local Selection
Local Selection
100000
0
false
view-filter
&Isolate Layer
Isolate Layer
Isolate Layer
1000
0
true
layer-locked
&Toggle layer lock
Toggle layer lock
Toggle layer lock
1000
0
false
visible
Toggle layer &visibility
Toggle layer visibility
Toggle layer visibility
1000
0
false
transparency-locked
Toggle layer &alpha
Toggle layer alpha
Toggle layer alpha
1000
0
false
transparency-enabled
Toggle layer alpha &inheritance
Toggle layer alpha inheritance
Toggle layer alpha inheritance
1000
0
false
paintLayer
&Paint Layer
Paint Layer
Paint Layer
1000
0
Insert
false
&New Layer From Visible
New layer from visible
New layer from visible
1000
0
false
duplicatelayer
&Duplicate Layer or Mask
Duplicate Layer or Mask
Duplicate Layer or Mask
1000
0
Ctrl+J
false
&Cut Selection to New Layer
Cut Selection to New Layer
Cut Selection to New Layer
100000000
1
Ctrl+Shift+J
false
Copy &Selection to New Layer
Copy Selection to New Layer
Copy Selection to New Layer
100000000
0
Ctrl+Alt+J
false
Copy Layer
Copy layer to clipboard
Copy layer to clipboard
1000
0
false
Cut Layer
Cut layer to clipboard
Cut layer to clipboard
1000
0
false
Paste Layer
Paste layer from clipboard
Paste layer from clipboard
1000
0
false
Quick Group
Create a group layer containing selected layers
Quick Group
100000
0
Ctrl+G
false
Quick Ungroup
Remove grouping of the layers or remove one layer out of the group
Quick Ungroup
100000
0
Ctrl+Alt+G
false
Quick Clipping Group
Group selected layers and add a layer with clipped alpha channel
Quick Clipping Group
100000
0
Ctrl+Shift+G
false
All Layers
Select all layers
Select all layers
10000
0
false
Visible Layers
Select all visible layers
Select all visible layers
10000
0
false
Locked Layers
Select all locked layers
Select all locked layers
10000
0
false
Invisible Layers
Select all invisible layers
Select all invisible layers
10000
0
false
Unlocked Layers
Select all unlocked layers
Select all unlocked layers
10000
0
false
document-save
&Save Layer/Mask...
Save Layer/Mask
Save Layer/Mask
1000
0
false
document-save
Save &Group Layers...
Save Group Layers
Save Group Layers
100000
0
false
Convert group to &animated layer
Convert child layers into animation frames
Convert child layers into animation frames
100000
0
false
fileLayer
to &File Layer
Saves out the layers into a new image and then references that image.
Convert to File Layer
100000
0
false
I&mport Layer...
Import Layer
Import Layer
100000
0
false
paintLayer
&as Paint Layer...
as Paint Layer
as Paint Layer
1000
0
false
transparencyMask
as &Transparency Mask...
as Transparency Mask
as Transparency Mask
1000
0
false
filterMask
as &Filter Mask...
as Filter Mask
as Filter Mask
1000
0
false
selectionMask
as &Selection Mask...
as Selection Mask
as Selection Mask
1000
0
false
paintLayer
to &Paint Layer
to Paint Layer
to Paint Layer
1000
0
false
transparencyMask
to &Transparency Mask
to Transparency Mask
to Transparency Mask
1000
0
false
filterMask
to &Filter Mask...
to Filter Mask
to Filter Mask
1000
0
false
selectionMask
to &Selection Mask
to Selection Mask
to Selection Mask
1000
0
false
transparencyMask
&Alpha into Mask
Alpha into Mask
Alpha into Mask
100000
10
false
transparency-enabled
&Write as Alpha
Write as Alpha
Write as Alpha
1000000
1
false
document-save
&Save Merged...
Save Merged
Save Merged
1000000
0
false
split-layer
Split Layer...
Split Layer
Split Layer
1000
0
false
Wavelet Decompose ...
Wavelet Decompose
Wavelet Decompose
1000
1
false
symmetry-horizontal
Mirror Layer Hori&zontally
Mirror Layer Horizontally
Mirror Layer Horizontally
1000
1
false
symmetry-vertical
Mirror Layer &Vertically
Mirror Layer Vertically
Mirror Layer Vertically
1000
1
false
&Rotate Layer...
Rotate Layer
Rotate Layer
1000
1
false
object-rotate-right
Rotate &Layer 90° to the Right
Rotate Layer 90° to the Right
Rotate Layer 90° to the Right
1000
1
false
object-rotate-left
Rotate Layer &90° to the Left
Rotate Layer 90° to the Left
Rotate Layer 90° to the Left
1000
1
false
Rotate Layer &180°
Rotate Layer 180°
Rotate Layer 180°
1000
1
false
Scale &Layer to new Size...
Scale Layer to new Size
Scale Layer to new Size
100000
1
false
&Shear Layer...
Shear Layer
Shear Layer
1000
1
false
&Offset Layer...
Offset Layer
Offset Layer
100000
1
false
Clones &Array...
Clones Array
Clones Array
100000
0
false
&Edit metadata...
Edit metadata
Edit metadata
100000
1
false
&Histogram...
Histogram
Histogram
100000
0
false
&Convert Layer Color Space...
Convert Layer Color Space
Convert Layer Color Space
100000
1
false
merge-layer-below
&Merge with Layer Below
Merge with Layer Below
Merge with Layer Below
100000
0
Ctrl+E
false
&Flatten Layer
Flatten Layer
Flatten Layer
100000
0
false
Ras&terize Layer
Rasterize Layer
Rasterize Layer
10000000
1
false
Flatten ima&ge
Flatten image
Flatten image
100000
0
Ctrl+Shift+E
false
La&yer Style...
Layer Style
Layer Style
100000
1
false
Move into previous group
Move into previous group
Move into previous group
0
0
false
Move into next group
Move into next group
Move into next group
0
0
false
Rename current layer
Rename current layer
Rename current layer
100000
0
F2
false
deletelayer
&Remove Layer
Remove Layer
Remove Layer
1000
1
Shift+Delete
false
arrowupblr
Move Layer or Mask Up
Move Layer or Mask Up
Ctrl+PgUp
false
arrowdown
Move Layer or Mask Down
Move Layer or Mask Down
Ctrl+PgDown
false
properties
&Properties...
Properties
Properties
1000
1
F3
false
diff --git a/krita/pics/tools/SVG/16/dark_krita_tool_ruler_assistant.svg b/krita/pics/tools/SVG/16/dark_krita_tool_assistant.svg
similarity index 100%
rename from krita/pics/tools/SVG/16/dark_krita_tool_ruler_assistant.svg
rename to krita/pics/tools/SVG/16/dark_krita_tool_assistant.svg
diff --git a/krita/pics/tools/SVG/16/light_krita_tool_ruler_assistant.svg b/krita/pics/tools/SVG/16/light_krita_tool_assistant.svg
similarity index 100%
rename from krita/pics/tools/SVG/16/light_krita_tool_ruler_assistant.svg
rename to krita/pics/tools/SVG/16/light_krita_tool_assistant.svg
diff --git a/krita/pics/tools/SVG/16/tools-svg-16-icons.qrc b/krita/pics/tools/SVG/16/tools-svg-16-icons.qrc
index f440a8eeb1..ffa77b7afd 100644
--- a/krita/pics/tools/SVG/16/tools-svg-16-icons.qrc
+++ b/krita/pics/tools/SVG/16/tools-svg-16-icons.qrc
@@ -1,81 +1,80 @@
-
-
-
+
+
dark_calligraphy.svg
dark_draw-text.svg
dark_format-fill-color.svg
dark_krita_draw_path.svg
dark_krita_tool_color_fill.svg
dark_krita_tool_color_picker.svg
dark_krita_tool_dyna.svg
dark_krita_tool_ellipse.svg
dark_krita_tool_freehand.svg
dark_krita_tool_freehandvector.svg
dark_krita_tool_gradient.svg
dark_krita_tool_grid.svg
dark_krita_tool_line.svg
dark_krita_tool_measure.svg
dark_krita_tool_move.svg
dark_krita_tool_multihand.svg
dark_krita_tool_polygon.svg
dark_krita_tool_rectangle.svg
- dark_krita_tool_ruler_assistant.svg
dark_krita_tool_transform.svg
dark_pattern.svg
dark_polyline.svg
dark_select.svg
dark_tool_contiguous_selection.svg
dark_tool_crop.svg
dark_tool_elliptical_selection.svg
dark_tool_outline_selection.svg
dark_tool_pan.svg
dark_tool_path_selection.svg
dark_tool_perspectivegrid.svg
dark_tool_polygonal_selection.svg
dark_tool_rect_selection.svg
dark_tool_similar_selection.svg
dark_tool_zoom.svg
light_calligraphy.svg
light_draw-text.svg
light_format-fill-color.svg
light_krita_draw_path.svg
light_krita_tool_color_fill.svg
light_krita_tool_color_picker.svg
light_krita_tool_dyna.svg
light_krita_tool_ellipse.svg
light_krita_tool_freehand.svg
light_krita_tool_freehandvector.svg
light_krita_tool_gradient.svg
light_krita_tool_grid.svg
light_krita_tool_line.svg
light_krita_tool_measure.svg
light_krita_tool_move.svg
light_krita_tool_multihand.svg
light_krita_tool_polygon.svg
light_krita_tool_rectangle.svg
- light_krita_tool_ruler_assistant.svg
light_krita_tool_transform.svg
light_pattern.svg
light_polyline.svg
light_select.svg
light_tool_contiguous_selection.svg
light_tool_crop.svg
light_tool_elliptical_selection.svg
light_tool_outline_selection.svg
light_tool_pan.svg
light_tool_path_selection.svg
light_tool_perspectivegrid.svg
light_tool_polygonal_selection.svg
light_tool_rect_selection.svg
light_tool_similar_selection.svg
light_tool_zoom.svg
dark_shape_handling.svg
dark_artistic_text.svg
light_artistic_text.svg
light_shape_handling.svg
dark_krita_tool_lazybrush.svg
light_krita_tool_lazybrush.svg
dark_krita_tool_smart_patch.svg
light_krita_tool_smart_patch.svg
+ light_krita_tool_assistant.svg
+ dark_krita_tool_assistant.svg
diff --git a/libs/flake/KoShape.h b/libs/flake/KoShape.h
index 2005eabadd..e54723a70d 100644
--- a/libs/flake/KoShape.h
+++ b/libs/flake/KoShape.h
@@ -1,1290 +1,1290 @@
/* This file is part of the KDE project
Copyright (C) 2006-2008 Thorsten Zachmann
Copyright (C) 2006, 2008 C. Boemann
Copyright (C) 2006-2010 Thomas Zander
Copyright (C) 2007-2009,2011 Jan Hambrecht
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOSHAPE_H
#define KOSHAPE_H
#include "KoFlake.h"
#include "KoFlakeTypes.h"
#include "KoConnectionPoint.h"
#include
#include
#include
#include
#include "kritaflake_export.h"
class QPainter;
class QRectF;
class QPainterPath;
class QTransform;
class KoShapeContainer;
class KoShapeStrokeModel;
class KoShapeUserData;
class KoViewConverter;
class KoShapeApplicationData;
class KoShapeSavingContext;
class KoShapeLoadingContext;
class KoGenStyle;
class KoShapeShadow;
class KoShapePrivate;
class KoFilterEffectStack;
class KoSnapData;
class KoClipPath;
class KoClipMask;
class KoShapePaintingContext;
class KoShapeAnchor;
class KoBorder;
struct KoInsets;
class KoShapeBackground;
class KisHandlePainterHelper;
/**
*
* Base class for all flake shapes. Shapes extend this class
* to allow themselves to be manipulated. This class just represents
* a graphical shape in the document and can be manipulated by some default
* tools in this library.
*
* Due to the limited responsibility of this class, the extending object
* can have any data backend and is responsible for painting itself.
*
* We strongly suggest that any extending class will use a Model View
* Controller (MVC) design where the View part is all in this class, as well
* as the one that inherits from this one. This allows the data that rests
* in the model to be reused in different parts of the document. For example
* by having two flake objects that show that same data. Or each showing a section of it.
*
* The KoShape data is completely in postscript-points (pt) (see KoUnit
* for conversion methods to and from points).
* This image will explain the real-world use of the shape and its options.
*
* The Rotation center can be returned with absolutePosition()
*
* Flake objects can be created in three ways:
*
* - a simple new KoDerivedFlake(),
*
- through an associated tool,
*
- through a factory
*
*
* Shape interaction notifications
* We had several notification methods that allow your shape to be notified of changes in other
* shapes positions or rotation etc.
* - The most general is KoShape::shapeChanged().
* a virtual method that you can use to check various changed to your shape made by tools or otherwise.
* - for shape hierarchies the parent may receive a notification when a child was modified.
* This is done though KoShapeContainerModel::childChanged()
* - 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
* other 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.
*
*/
class KRITAFLAKE_EXPORT KoShape
{
public:
/// Used by shapeChanged() to select which change was made
enum ChangeType {
PositionChanged, ///< used after a setPosition()
RotationChanged, ///< used after a setRotation()
ScaleChanged, ///< used after a scale()
ShearChanged, ///< used after a shear()
SizeChanged, ///< used after a setSize()
GenericMatrixChange, ///< used after the matrix was changed without knowing which property explicitly changed
KeepAspectRatioChange, ///< used after setKeepAspectRatio()
ParentChanged, ///< used after a setParent()
CollisionDetected, ///< used when another shape moved in our boundingrect
Deleted, ///< the shape was deleted
StrokeChanged, ///< the shapes stroke has changed
BackgroundChanged, ///< the shapes background has changed
ShadowChanged, ///< the shapes shadow has changed
BorderChanged, ///< the shapes border has changed
ParameterChanged, ///< the shapes parameter has changed (KoParameterShape only)
ContentChanged, ///< the content of the shape changed e.g. a new image inside a pixmap/text change inside a textshape
TextRunAroundChanged, ///< used after a setTextRunAroundSide()
ChildChanged, ///< a child of a container was changed/removed. This is propagated to all parents
ConnectionPointChanged, ///< a connection point has changed
ClipPathChanged, ///< the shapes clip path has changed
TransparencyChanged ///< the shapetransparency value has changed
};
/// The behavior text should do when intersecting this shape.
enum TextRunAroundSide {
BiggestRunAroundSide, ///< Run other text around the side that has the most space
LeftRunAroundSide, ///< Run other text around the left side of the frame
RightRunAroundSide, ///< Run other text around the right side of the frame
EnoughRunAroundSide, ///< Run other text dynamically around both sides of the shape, provided there is sufficient space left
BothRunAroundSide, ///< Run other text around both sides of the shape
NoRunAround, ///< The text will be completely avoiding the frame by keeping the horizontal space that this frame occupies blank.
RunThrough ///< The text will completely ignore the frame and layout as if it was not there
};
/// The behavior text should do when intersecting this shape.
enum TextRunAroundContour {
ContourBox, /// Run other text around a bounding rect of the outline
ContourFull, ///< Run other text around also on the inside
ContourOutside ///< Run other text around only on the outside
};
/**
* TODO
*/
enum RunThroughLevel {
Background,
Foreground
};
/**
* @brief Constructor
*/
KoShape();
/**
* @brief Destructor
*/
virtual ~KoShape();
/**
* @brief creates a deep copy of thie shape or shapes subtree
* @return a cloned shape
*/
virtual KoShape* cloneShape() const;
/**
* @brief Paint the shape fill
* The class extending this one is responsible for painting itself. Since we do not
* assume the shape is square the paint must also clear its background if it will draw
* something transparent on top.
* This can be done with a method like:
*
painter.fillRect(converter.normalToView(QRectF(QPointF(0.0,0.0), size())), background());
* Or equavalent for non-square objects.
* Do note that a shape's top-left is always at coordinate 0,0. Even if the shape itself is rotated
* or translated.
* @param painter used for painting the shape
* @param converter to convert between internal and view coordinates.
* @see applyConversion()
* @param paintcontext the painting context.
*/
virtual void paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext) = 0;
/**
* @brief paintStroke paints the shape's stroked outline
* @param painter used for painting the shape
* @param converter to convert between internal and view coordinates.
* @see applyConversion()
* @param paintcontext the painting context.
*/
virtual void paintStroke(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext);
/**
* @brief Paint the shape's border
* This is a helper function that could be called from the paint() method of all shapes.
* @param painter used for painting the shape
* @param converter to convert between internal and view coordinates.
* @see applyConversion()
*/
virtual void paintBorder(QPainter &painter, const KoViewConverter &converter);
/**
* Load a shape from odf
*
* @param context the KoShapeLoadingContext used for loading
* @param element element which represents the shape in odf
*
* @return false if loading failed
*/
virtual bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) = 0;
/**
* @brief store the shape data as ODF XML.
* This is the method that will be called when saving a shape as a described in
* OpenDocument 9.2 Drawing Shapes.
* @see saveOdfAttributes
*/
virtual void saveOdf(KoShapeSavingContext &context) const = 0;
/**
* This method can be used while saving the shape as ODF to add the data
* stored on this shape to the current element.
*
* @param context the context for the current save.
* @param attributes a number of OdfAttribute items to state which attributes to save.
* @see saveOdf
*/
void saveOdfAttributes(KoShapeSavingContext &context, int attributes) const;
/**
* This method can be used while saving the shape as Odf to add common child elements
*
* The office:event-listeners and draw:glue-point are saved.
* @param context the context for the current save.
*/
void saveOdfCommonChildElements(KoShapeSavingContext &context) const;
/**
* This method can be used to save contour data from the clipPath()
*
* The draw:contour-polygon or draw:contour-path elements are saved.
* @param context the context for the current save.
* @param originalSize the original size of the unscaled image.
*/
void saveOdfClipContour(KoShapeSavingContext &context, const QSizeF &originalSize) const;
/**
* @brief Scale the shape using the zero-point which is the top-left corner.
* @see position()
*
* @param sx scale in x direction
* @param sy scale in y direction
*/
void scale(qreal sx, qreal sy);
/**
* @brief Rotate the shape (relative)
*
* The shape will be rotated from the current rotation using the center of the shape using the size()
*
* @param angle change the angle of rotation increasing it with 'angle' degrees
*/
void rotate(qreal angle);
/**
* Return the current rotation in degrees.
* It returns NaN if the shape has a shearing or scaling transformation applied.
*/
qreal rotation() const;
/**
* @brief Shear the shape
* The shape will be sheared using the zero-point which is the top-left corner.
* @see position()
*
* @param sx shear in x direction
* @param sy shear in y direction
*/
void shear(qreal sx, qreal sy);
/**
* @brief Resize the shape
*
* @param size the new size of the shape. This is different from scaling as
* scaling is a so called secondary operation which is comparable to zooming in
* instead of changing the size of the basic shape.
* Easiest example of this difference is that using this method will not distort the
* size of pattern-fills and strokes.
*/
virtual void setSize(const QSizeF &size);
/**
* @brief Get the size of the shape in pt.
*
* The size is in shape coordinates.
*
* @return the size of the shape as set by setSize()
*/
virtual QSizeF size() const;
/**
* @brief Set the position of the shape in pt
*
* @param position the new position of the shape
*/
virtual void setPosition(const QPointF &position);
/**
* @brief Get the position of the shape in pt
*
* @return the position of the shape
*/
QPointF position() const;
/**
* @brief Check if the shape is hit on position
* @param position the position where the user clicked.
* @return true when it hits.
*/
virtual bool hitTest(const QPointF &position) const;
/**
* @brief Get the bounding box of the shape
*
* This includes the line width and the shadow of the shape
*
* @return the bounding box of the shape
*/
virtual QRectF boundingRect() const;
/**
* Get the united bounding box of a group of shapes. This is a utility
* function used in many places in Krita.
*/
static QRectF boundingRect(const QList &shapes);
/**
* @return the bounding rect of the outline of the shape measured
* in absolute coordinate system. Please note that in contrast to
* boundingRect() this rect doesn't include the stroke and other
* insets.
*/
QRectF absoluteOutlineRect(KoViewConverter *converter = 0) const;
/**
* Same as a member function, but applies to a list of shapes and returns a
* united rect.
*/
static QRectF absoluteOutlineRect(const QList &shapes, KoViewConverter *converter = 0);
/**
* @brief Add a connector point to the shape
*
* A connector is a place on the shape that allows a graphical connection to be made
* using a line, for example.
*
* @param point the connection point to add
* @return the id of the new connection point
*/
int addConnectionPoint(const KoConnectionPoint &point);
/**
* Sets data of connection point with specified id.
*
* The position of the connector is restricted to the bounding rectangle of the shape.
* When setting a default connection point, the new position is ignored, as these
* are fixed at their default position.
* The function will insert a new connection point if the specified id was not used
* before.
*
* @param connectionPointId the id of the connection point to set
* @param point the connection point data
* @return false if specified connection point id is invalid, else true
*/
bool setConnectionPoint(int connectionPointId, const KoConnectionPoint &point);
/// Checks if a connection point with the specified id exists
bool hasConnectionPoint(int connectionPointId) const;
/// Returns connection point with specified connection point id
KoConnectionPoint connectionPoint(int connectionPointId) const;
/**
* Return a list of the connection points that have been added to this shape.
* All the points are relative to the shape position, see absolutePosition().
* @return a list of the connectors that have been added to this shape.
*/
KoConnectionPoints connectionPoints() const;
/// Removes connection point with specified id
void removeConnectionPoint(int connectionPointId);
/// Removes all connection points
void clearConnectionPoints();
/**
* Return the side text should flow around this shape. This implements the ODF style:wrap
* attribute that specifies how text is displayed around a frame or graphic object.
*/
TextRunAroundSide textRunAroundSide() const;
/**
* Set the side text should flow around this shape.
* @param side the requested side
* @param runThrought run through the foreground or background or...
*/
void setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrough = Background);
/**
* The space between this shape's left edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceLeft() const;
/**
* Set the space between this shape's left edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceLeft(qreal distance);
/**
* The space between this shape's top edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceTop() const;
/**
* Set the space between this shape's top edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceTop(qreal distance);
/**
* The space between this shape's right edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceRight() const;
/**
* Set the space between this shape's right edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceRight(qreal distance);
/**
* The space between this shape's bottom edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceBottom() const;
/**
* Set the space between this shape's bottom edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceBottom(qreal distance);
/**
* Return the threshold above which text should flow around this shape.
* The text will not flow around the shape on a side unless the space available on that side
* is above this threshold. Only used when the text run around side is EnoughRunAroundSide.
* @return threshold the threshold
*/
qreal textRunAroundThreshold() const;
/**
* Set the threshold above which text should flow around this shape.
* The text will not flow around the shape on a side unless the space available on that side
* is above this threshold. Only used when the text run around side is EnoughRunAroundSide.
* @param threshold the new threshold
*/
void setTextRunAroundThreshold(qreal threshold);
/**
* Return the how tight text run around is done around this shape.
* @return the contour
*/
TextRunAroundContour textRunAroundContour() const;
/**
* Set how tight text run around is done around this shape.
* @param contour the new contour
*/
void setTextRunAroundContour(TextRunAroundContour contour);
/**
* Set the KoShapeAnchor
*/
void setAnchor(KoShapeAnchor *anchor);
/**
* Return the KoShapeAnchor, or 0
*/
KoShapeAnchor *anchor() const;
/**
* Set the minimum height of the shape.
* Currently it's not respected but only for informational purpose
* @param minimumShapeHeight the minimum height of the frame.
*/
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 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 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()
*/
int 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.
* 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(int zIndex);
/**
* Retrieve the run through property of this shape.
* The run through property is used to determine if the shape is behind, inside or before text.
* @return the run through of this shape.
*/
int runThrough();
/**
* Set the run through property of this shape.
* The run through property is used to determine if the shape is behind, inside or before text.
* @param runThrough the new run through;
*/
virtual void setRunThrough(short int runThrough);
/**
* Changes the Shape to be visible or invisible.
* Being visible means being painted, as well as being used for
* things like guidelines or searches.
* @param on when true; set the shape to be visible.
* @see setGeometryProtected(), setContentProtected(), setSelectable()
*/
void setVisible(bool on);
/**
* Returns current visibility state of this shape.
* Being visible means being painted, as well as being used for
* things like guidelines or searches.
* @param recursive when true, checks visibility recursively
* @return current visibility state of this shape.
* @see isGeometryProtected(), isContentProtected(), isSelectable()
*/
bool isVisible(bool recursive = false) 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 ancestorsInQuestion) const;
/**
* Request a repaint to be queued.
* The repaint will be of the entire Shape, including its selection handles should this
* shape be selected.
* 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.
*
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/hight 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/hight 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.
* @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.
* The newPosition is going to be the center of the shape.
* This has the convenient effect that:
shape->setAbsolutePosition(QPointF(0,0));
shape->rotate(45);
Will result in the same visual position of the shape as the opposite:
shape->rotate(45);
shape->setAbsolutePosition(QPointF(0,0));
* @param newPosition the new absolute center of the shape.
* @param anchor The place on the (unaltered) shape that you set the position of.
*/
void setAbsolutePosition(const QPointF &newPosition, KoFlake::AnchorPosition anchor = KoFlake::Center);
/**
* Set a data object on the shape to be used by an application.
* This is specifically useful when a shape is created in a plugin and that data from that
* shape should be accessible outside the plugin.
* @param userData the new user data, or 0 to delete the current one.
*/
void setUserData(KoShapeUserData *userData);
/**
* Return the current userData.
*/
KoShapeUserData *userData() const;
/**
* Return the Id of this shape, identifying the type of shape by the id of the factory.
* @see KoShapeFactoryBase::shapeId()
* @return the id of the shape-type
*/
QString shapeId() const;
/**
* Set the Id of this shape. A shapeFactory is expected to set the Id at creation
* so applications can find out what kind of shape this is.
* @see KoShapeFactoryBase::shapeId()
* @param id the ID from the factory that created this shape
*/
void setShapeId(const QString &id);
/**
* Create a matrix that describes all the transformations done on this shape.
*
* The absolute transformation is the combined transformation of this shape
* and all its parents and grandparents.
*
* @param converter if not null, this method uses the converter to mark the right
* offsets in the current view.
*/
QTransform absoluteTransformation(const KoViewConverter *converter) const;
/**
* Applies a transformation to this shape.
*
* The transformation given is relative to the global coordinate system, i.e. the document.
* This is a convenience function to apply a global transformation to this shape.
* @see applyTransformation
*
* @param matrix the transformation matrix to apply
*/
void applyAbsoluteTransformation(const QTransform &matrix);
/**
* Sets a new transformation matrix describing the local transformations on this shape.
* @param matrix the new transformation matrix
*/
void setTransformation(const QTransform &matrix);
/// Returns the shapes local transformation matrix
QTransform transformation() const;
/**
* Applies a transformation to this shape.
*
* The transformation given is relative to the shape coordinate system.
*
* @param matrix the transformation matrix to apply
*/
void applyTransformation(const QTransform &matrix);
/**
* Copy all the settings from the parameter shape and apply them to this shape.
* Settings like the position and rotation to visible and locked. The parent
* is a notable exclusion.
* @param shape the shape to use as original
*/
void copySettings(const KoShape *shape);
/**
* Convenience method that allows people implementing paint() to use the shape
* internal coordinate system directly to paint itself instead of considering the
* views zoom.
* @param painter the painter to alter the zoom level of.
* @param converter the converter for the current views zoom.
*/
static void applyConversion(QPainter &painter, const KoViewConverter &converter);
/**
* A convenience method that creates a handles helper with applying transformations at
* the same time. Please note that you shouldn't save/restore additionally. All the work
* on restoring original painter's transformations is done by the helper.
*/
static KisHandlePainterHelper createHandlePainterHelper(QPainter *painter, KoShape *shape, const KoViewConverter &converter, qreal handleRadius = 0.0);
/**
* @brief Transforms point from shape coordinates to document coordinates
* @param point in shape coordinates
* @return point in document coordinates
*/
QPointF shapeToDocument(const QPointF &point) const;
/**
* @brief Transforms rect from shape coordinates to document coordinates
* @param rect in shape coordinates
* @return rect in document coordinates
*/
QRectF shapeToDocument(const QRectF &rect) const;
/**
* @brief Transforms point from document coordinates to shape coordinates
* @param point in document coordinates
* @return point in shape coordinates
*/
QPointF documentToShape(const QPointF &point) const;
/**
* @brief Transform rect from document coordinates to shape coordinates
* @param rect in document coordinates
* @return rect in shape coordinates
*/
QRectF documentToShape(const QRectF &rect) const;
/**
* Returns the name of the shape.
* @return the shapes name
*/
QString name() const;
/**
* Sets the name of the shape.
* @param name the new shape name
*/
void setName(const QString &name);
/**
* Update the position of the shape in the tree of the KoShapeManager.
*/
void notifyChanged();
/**
* A shape can be in a state that it is doing processing data like loading or text layout.
* In this case it can be shown on screen probably partially but it should really not be printed
* until it is fully done processing.
* Warning! This method can be blocking for a long time
* @param 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 finised.
* In case of printing Flake will call this method from a non-main thread and only
* start printing it when the in case of printing method returned.
* If set to false the processing needs to be done synchronously and will
* block until the result is finished.
*/
virtual void waitUntilReady(const KoViewConverter &converter, bool asynchronous = true) const;
/// checks recursively if the shape or one of its parents is not visible or locked
bool isEditable() 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 dependees() const;
/// Returns additional snap data the shape wants to have snapping to
virtual KoSnapData snapData() const;
/**
* Set additional attribute
*
* This can be used to attach additional attributes to a shape for attributes
* that are application specific like presentation:placeholder
*
* @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder
* @param value The value of the attribute
*/
void setAdditionalAttribute(const QString &name, const QString &value);
/**
* Remove additional attribute
*
* @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder
*/
void removeAdditionalAttribute(const QString &name);
/**
* Check if additional attribute is set
*
* @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder
*
* @return true if there is a attribute with prefix:tag set, false otherwise
*/
bool hasAdditionalAttribute(const QString &name) const;
/**
* Get additional attribute
*
* @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder
*
* @return The value of the attribute if it exists or a null string if not found.
*/
QString additionalAttribute(const QString &name) const;
void setAdditionalStyleAttribute(const char *name, const QString &value);
void removeAdditionalStyleAttribute(const char *name);
/**
* Returns the filter effect stack of the shape
*
* @return the list of filter effects applied on the shape when rendering.
*/
KoFilterEffectStack *filterEffectStack() const;
/// Sets the new filter effect stack, removing the old one
void setFilterEffectStack(KoFilterEffectStack *filterEffectStack);
/**
* Set the property collision detection.
* Setting this to true will result in calls to shapeChanged() with the CollisionDetected
* parameter whenever either this or another shape is moved/rotated etc and intersects this shape.
* @param detect if true detect collisions.
*/
void setCollisionDetection(bool detect);
/**
* get the property collision detection.
* @returns true if collision detection is on.
*/
bool collisionDetection();
/**
* Return the tool delegates for this shape.
* In Flake a shape being selected will cause the tool manager to make available all tools that
* can edit the selected shapes. In some cases selecting one shape should allow the tool to
* edit a related shape be available too. The tool delegates allows this to happen by taking
* all the shapes in the set into account on tool selection.
* Notice that if the set is non-empty 'this' shape is no longer looked at. You can choose
* to add itself to the set too.
*/
QSet toolDelegates() const;
/**
* Set the tool delegates.
* @param delegates the new delegates.
* @see toolDelegates()
*/
void setToolDelegates(const QSet &delegates);
/**
* Return the hyperlink for this shape.
*/
QString hyperLink () const;
/**
* Set hyperlink for this shape.
* @param hyperLink name.
*/
void setHyperLink(const QString &hyperLink);
/**
* \internal
* Returns the private object for use within the flake lib
*/
KoShapePrivate *priv();
public:
- struct ShapeChangeListener {
+ struct KRITAFLAKE_EXPORT ShapeChangeListener {
virtual ~ShapeChangeListener();
virtual void notifyShapeChanged(ChangeType type, KoShape *shape) = 0;
private:
friend class KoShape;
friend class KoShapePrivate;
void registerShape(KoShape *shape);
void unregisterShape(KoShape *shape);
void notifyShapeChangedImpl(ChangeType type, KoShape *shape);
QList m_registeredShapes;
};
void addShapeChangeListener(ShapeChangeListener *listener);
void removeShapeChangeListener(ShapeChangeListener *listener);
public:
static QList linearizeSubtree(const QList &shapes);
protected:
/// constructor
KoShape(KoShapePrivate *);
/* ** loading saving helper methods */
/// attributes from ODF 1.1 chapter 9.2.15 Common Drawing Shape Attributes
enum OdfAttribute {
OdfTransformation = 1, ///< Store transformation information
OdfSize = 2, ///< Store size information
OdfPosition = 8, ///< Store position
OdfAdditionalAttributes = 4, ///< Store additional attributes of the shape
OdfCommonChildElements = 16, ///< Event actions and connection points
OdfLayer = 64, ///< Store layer name
OdfStyle = 128, ///< Store the style
OdfId = 256, ///< Store the unique ID
OdfName = 512, ///< Store the name of the shape
OdfZIndex = 1024, ///< Store the z-index
OdfViewbox = 2048, ///< Store the viewbox
/// A mask for all mandatory attributes
OdfMandatories = OdfLayer | OdfStyle | OdfId | OdfName | OdfZIndex,
/// A mask for geometry attributes
OdfGeometry = OdfPosition | OdfSize,
/// A mask for all the attributes
OdfAllAttributes = OdfTransformation | OdfGeometry | OdfAdditionalAttributes | OdfMandatories | OdfCommonChildElements
};
/**
* This method is used during loading of the shape to load common attributes
*
* @param context the KoShapeLoadingContext used for loading
* @param element element which represents the shape in odf
* @param attributes a number of OdfAttribute items to state which attributes to load.
*/
bool loadOdfAttributes(const KoXmlElement &element, KoShapeLoadingContext &context, int attributes);
/**
* Parses the transformation attribute from the given string
* @param transform the transform attribute string
* @return the resulting transformation matrix
*/
QTransform parseOdfTransform(const QString &transform);
/**
* @brief Saves the style used for the shape
*
* This method fills the given style object with the stroke and
* background properties and then adds the style to the context.
*
* @param style the style object to fill
* @param context used for saving
* @return the name of the style
* @see saveOdf
*/
virtual QString saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const;
/**
* Loads the stroke and fill style from the given element.
*
* @param element the xml element to load the style from
* @param context the loading context used for loading
*/
virtual void loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context);
/// Loads the stroke style
KoShapeStrokeModelSP loadOdfStroke(const KoXmlElement &element, KoShapeLoadingContext &context) const;
/// Loads the fill style
QSharedPointer 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.
*/
virtual void shapeChanged(ChangeType type, KoShape *shape = 0);
/// return the current matrix that contains the rotation/scale/position of this shape
QTransform transform() const;
KoShapePrivate *d_ptr;
private:
Q_DECLARE_PRIVATE(KoShape)
};
Q_DECLARE_METATYPE(KoShape*)
#endif
diff --git a/libs/flake/svg/SvgUtil.h b/libs/flake/svg/SvgUtil.h
index 217eeff2e9..22493ca9c2 100644
--- a/libs/flake/svg/SvgUtil.h
+++ b/libs/flake/svg/SvgUtil.h
@@ -1,150 +1,150 @@
/* This file is part of the KDE project
* Copyright (C) 2009 Jan Hambrecht
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef SVGUTIL_H
#define SVGUTIL_H
#include "kritaflake_export.h"
#include
class QString;
class QTransform;
class QStringList;
class KoXmlWriter;
#include
class SvgGraphicsContext;
class KRITAFLAKE_EXPORT SvgUtil
{
public:
// remove later! pixels *are* user coordinates
static double fromUserSpace(double value);
static double toUserSpace(double value);
static double ptToPx(SvgGraphicsContext *gc, double value);
/// Converts given point from points to userspace units.
static QPointF toUserSpace(const QPointF &point);
/// Converts given rectangle from points to userspace units.
static QRectF toUserSpace(const QRectF &rect);
/// Converts given rectangle from points to userspace units.
static QSizeF toUserSpace(const QSizeF &size);
/**
* Parses the given string containing a percentage number.
* @param s the input string containing the percentage
* @return the percentage number normalized to 0..100
*/
static QString toPercentage(qreal value);
/**
* Parses the given string containing a percentage number.
* @param s the input string containing the percentage
* @return the percentage number normalized to 0..1
*/
static double fromPercentage(QString s);
/**
* Converts position from objectBoundingBox units to userSpace units.
*/
static QPointF objectToUserSpace(const QPointF &position, const QRectF &objectBound);
/**
* Converts size from objectBoundingBox units to userSpace units.
*/
static QSizeF objectToUserSpace(const QSizeF &size, const QRectF &objectBound);
/**
* Converts position from userSpace units to objectBoundingBox units.
*/
static QPointF userSpaceToObject(const QPointF &position, const QRectF &objectBound);
/**
* Converts size from userSpace units to objectBoundingBox units.
*/
static QSizeF userSpaceToObject(const QSizeF &size, const QRectF &objectBound);
/// Converts specified transformation to a string
static QString transformToString(const QTransform &transform);
/// Writes a \p transform as an attribute \p name iff the transform is not empty
static void writeTransformAttributeLazy(const QString &name, const QTransform &transform, KoXmlWriter &shapeWriter);
/// Parses a viewbox attribute into an rectangle
static bool parseViewBox(SvgGraphicsContext *gc, const KoXmlElement &e, const QRectF &elementBounds, QRectF *_viewRect, QTransform *_viewTransform);
struct PreserveAspectRatioParser;
static void parseAspectRatio(const PreserveAspectRatioParser &p, const QRectF &elementBounds, const QRectF &viewRect, QTransform *_viewTransform);
/// Parses a length attribute
static qreal parseUnit(SvgGraphicsContext *gc, const QString &, bool horiz = false, bool vert = false, const QRectF &bbox = QRectF());
/// parses a length attribute in x-direction
static qreal parseUnitX(SvgGraphicsContext *gc, const QString &unit);
/// parses a length attribute in y-direction
static qreal parseUnitY(SvgGraphicsContext *gc, const QString &unit);
/// parses a length attribute in xy-direction
static qreal parseUnitXY(SvgGraphicsContext *gc, const QString &unit);
/// parses angle, result in *radians*!
static qreal parseUnitAngular(SvgGraphicsContext *gc, const QString &unit);
/// parses the number into parameter number
static const char * parseNumber(const char *ptr, qreal &number);
static qreal parseNumber(const QString &string);
static QString mapExtendedShapeTag(const QString &tagName, const KoXmlElement &element);
static QStringList simplifyList(const QString &str);
- struct PreserveAspectRatioParser
+ struct KRITAFLAKE_EXPORT PreserveAspectRatioParser
{
PreserveAspectRatioParser(const QString &str);
enum Alignment {
Min,
Middle,
Max
};
bool defer = false;
Qt::AspectRatioMode mode = Qt::IgnoreAspectRatio;
Alignment xAlignment = Min;
Alignment yAlignment = Min;
QPointF rectAnchorPoint(const QRectF &rc) const;
QString toString() const;
private:
Alignment alignmentFromString(const QString &str) const;
QString alignmentToString(Alignment alignment) const;
static qreal alignedValue(qreal min, qreal max, Alignment alignment);
};
};
#endif // SVGUTIL_H
diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt
index 9b6ff83ee7..d494e04d77 100644
--- a/libs/image/CMakeLists.txt
+++ b/libs/image/CMakeLists.txt
@@ -1,401 +1,400 @@
add_subdirectory( tests )
add_subdirectory( tiles3 )
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/metadata
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty
${CMAKE_CURRENT_SOURCE_DIR}/brushengine
${CMAKE_CURRENT_SOURCE_DIR}/commands
${CMAKE_CURRENT_SOURCE_DIR}/commands_new
${CMAKE_CURRENT_SOURCE_DIR}/filter
${CMAKE_CURRENT_SOURCE_DIR}/floodfill
${CMAKE_CURRENT_SOURCE_DIR}/generator
${CMAKE_CURRENT_SOURCE_DIR}/layerstyles
${CMAKE_CURRENT_SOURCE_DIR}/processing
${CMAKE_CURRENT_SOURCE_DIR}/recorder
${CMAKE_SOURCE_DIR}/sdk/tests
)
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
- ${Boost_INCLUDE_DIRS}
)
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/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
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
commands/kis_deselect_global_selection_command.cpp
commands/kis_image_change_layers_command.cpp
commands/kis_image_change_visibility_command.cpp
commands/kis_image_command.cpp
commands/kis_image_set_projection_color_space_command.cpp
commands/kis_image_layer_add_command.cpp
commands/kis_image_layer_move_command.cpp
commands/kis_image_layer_remove_command.cpp
commands/kis_image_layer_remove_command_impl.cpp
commands/kis_image_lock_command.cpp
commands/kis_layer_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/kis_set_global_selection_command.cpp
commands_new/kis_saved_commands.cpp
commands_new/kis_processing_command.cpp
commands_new/kis_image_resize_command.cpp
commands_new/kis_image_set_resolution_command.cpp
commands_new/kis_node_move_command2.cpp
commands_new/kis_set_layer_style_command.cpp
commands_new/kis_selection_move_command2.cpp
commands_new/kis_update_command.cpp
commands_new/kis_switch_current_time_command.cpp
commands_new/kis_change_projection_color_command.cpp
commands_new/kis_activate_selection_mask_command.cpp
processing/kis_do_nothing_processing_visitor.cpp
processing/kis_simple_processing_visitor.cpp
processing/kis_crop_processing_visitor.cpp
processing/kis_crop_selections_processing_visitor.cpp
processing/kis_transform_processing_visitor.cpp
processing/kis_mirror_processing_visitor.cpp
filter/kis_filter.cc
filter/kis_filter_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/kis_colorize_mask.cpp
lazybrush/kis_colorize_stroke_strategy.cpp
KisDelayedUpdateNodeInterface.cpp
kis_adjustment_layer.cc
kis_selection_based_layer.cpp
kis_node_filter_interface.cpp
kis_base_accessor.cpp
kis_base_node.cpp
kis_base_processor.cpp
kis_bookmarked_configuration_manager.cc
kis_clone_info.cpp
kis_clone_layer.cpp
kis_colorspace_convert_visitor.cpp
kis_config_widget.cpp
kis_convolution_kernel.cc
kis_convolution_painter.cc
kis_gaussian_kernel.cpp
kis_edge_detection_kernel.cpp
kis_cubic_curve.cpp
kis_default_bounds.cpp
kis_default_bounds_base.cpp
kis_effect_mask.cc
kis_fast_math.cpp
kis_fill_painter.cc
kis_filter_mask.cpp
kis_filter_strategy.cc
kis_transform_mask.cpp
kis_transform_mask_params_interface.cpp
kis_recalculate_transform_mask_job.cpp
kis_recalculate_generator_layer_job.cpp
kis_transform_mask_params_factory_registry.cpp
kis_safe_transform.cpp
kis_gradient_painter.cc
kis_gradient_shape_strategy.cpp
kis_cached_gradient_shape_strategy.cpp
kis_polygonal_gradient_shape_strategy.cpp
kis_iterator_ng.cpp
kis_async_merger.cpp
kis_merge_walker.cc
kis_updater_context.cpp
kis_update_job_item.cpp
kis_stroke_strategy_undo_command_based.cpp
kis_simple_stroke_strategy.cpp
KisRunnableBasedStrokeStrategy.cpp
KisRunnableStrokeJobData.cpp
KisRunnableStrokeJobsInterface.cpp
KisFakeRunnableStrokeJobsExecutor.cpp
kis_stroke_job_strategy.cpp
kis_stroke_strategy.cpp
kis_stroke.cpp
kis_strokes_queue.cpp
KisStrokesQueueMutatedJobInterface.cpp
kis_simple_update_queue.cpp
kis_update_scheduler.cpp
kis_queues_progress_updater.cpp
kis_composite_progress_proxy.cpp
kis_sync_lod_cache_stroke_strategy.cpp
kis_lod_capable_layer_offset.cpp
kis_update_time_monitor.cpp
KisUpdateSchedulerConfigNotifier.cpp
kis_group_layer.cc
kis_count_visitor.cpp
kis_histogram.cc
kis_image_interfaces.cpp
kis_image_animation_interface.cpp
kis_time_range.cpp
kis_node_graph_listener.cpp
kis_image.cc
kis_image_signal_router.cpp
kis_image_config.cpp
kis_projection_updates_filter.cpp
kis_suspend_projection_updates_stroke_strategy.cpp
kis_regenerate_frame_stroke_strategy.cpp
kis_switch_time_stroke_strategy.cpp
kis_crop_saved_extra_data.cpp
kis_thread_safe_signal_compressor.cpp
kis_timed_signal_threshold.cpp
kis_layer.cc
kis_indirect_painting_support.cpp
kis_abstract_projection_plane.cpp
kis_layer_projection_plane.cpp
kis_layer_utils.cpp
kis_mask_projection_plane.cpp
kis_projection_leaf.cpp
kis_mask.cc
kis_base_mask_generator.cpp
kis_rect_mask_generator.cpp
kis_circle_mask_generator.cpp
kis_gauss_circle_mask_generator.cpp
kis_gauss_rect_mask_generator.cpp
${__per_arch_circle_mask_generator_objs}
kis_curve_circle_mask_generator.cpp
kis_curve_rect_mask_generator.cpp
kis_math_toolbox.cpp
kis_memory_statistics_server.cpp
kis_name_server.cpp
kis_node.cpp
kis_node_facade.cpp
kis_node_progress_proxy.cpp
kis_busy_progress_indicator.cpp
kis_node_visitor.cpp
kis_paint_device.cc
kis_paint_device_debug_utils.cpp
kis_fixed_paint_device.cpp
KisOptimizedByteArray.cpp
kis_paint_layer.cc
kis_perspective_math.cpp
kis_pixel_selection.cpp
kis_processing_information.cpp
kis_properties_configuration.cc
kis_random_accessor_ng.cpp
kis_random_generator.cc
kis_random_sub_accessor.cpp
kis_wrapped_random_accessor.cpp
kis_selection.cc
kis_selection_mask.cpp
kis_update_outline_job.cpp
kis_update_selection_job.cpp
kis_serializable_configuration.cc
kis_transaction_data.cpp
kis_transform_worker.cc
kis_perspectivetransform_worker.cpp
bsplines/kis_bspline_1d.cpp
bsplines/kis_bspline_2d.cpp
bsplines/kis_nu_bspline_2d.cpp
kis_warptransform_worker.cc
kis_cage_transform_worker.cpp
kis_liquify_transform_worker.cpp
kis_green_coordinates_math.cpp
kis_transparency_mask.cc
kis_undo_adapter.cpp
kis_macro_based_undo_store.cpp
kis_surrogate_undo_adapter.cpp
kis_legacy_undo_adapter.cpp
kis_post_execution_undo_adapter.cpp
kis_processing_visitor.cpp
kis_processing_applicator.cpp
krita_utils.cpp
kis_outline_generator.cpp
kis_layer_composition.cpp
kis_selection_filters.cpp
KisProofingConfiguration.h
metadata/kis_meta_data_entry.cc
metadata/kis_meta_data_filter.cc
metadata/kis_meta_data_filter_p.cc
metadata/kis_meta_data_filter_registry.cc
metadata/kis_meta_data_filter_registry_model.cc
metadata/kis_meta_data_io_backend.cc
metadata/kis_meta_data_merge_strategy.cc
metadata/kis_meta_data_merge_strategy_p.cc
metadata/kis_meta_data_merge_strategy_registry.cc
metadata/kis_meta_data_parser.cc
metadata/kis_meta_data_schema.cc
metadata/kis_meta_data_schema_registry.cc
metadata/kis_meta_data_store.cc
metadata/kis_meta_data_type_info.cc
metadata/kis_meta_data_validator.cc
metadata/kis_meta_data_value.cc
recorder/kis_action_recorder.cc
recorder/kis_macro.cc
recorder/kis_macro_player.cc
recorder/kis_node_query_path.cc
recorder/kis_play_info.cc
recorder/kis_recorded_action.cc
recorder/kis_recorded_action_factory_registry.cc
recorder/kis_recorded_action_load_context.cpp
recorder/kis_recorded_action_save_context.cpp
recorder/kis_recorded_filter_action.cpp
recorder/kis_recorded_fill_paint_action.cpp
recorder/kis_recorded_node_action.cc
recorder/kis_recorded_paint_action.cpp
recorder/kis_recorded_path_paint_action.cpp
recorder/kis_recorded_shape_paint_action.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
KisProofingConfiguration.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
Qt5::Concurrent
)
target_link_libraries(kritaimage PUBLIC ${Boost_SYSTEM_LIBRARY})
if(OPENEXR_FOUND)
target_link_libraries(kritaimage PUBLIC ${OPENEXR_LIBRARIES})
endif()
if(FFTW3_FOUND)
target_link_libraries(kritaimage PRIVATE ${FFTW3_LIBRARIES})
endif()
if(HAVE_VC)
target_link_libraries(kritaimage PUBLIC ${Vc_LIBRARIES})
endif()
if (NOT GSL_FOUND)
message (WARNING "KRITA WARNING! No GNU Scientific Library was found! Krita's Shaped Gradients might be non-normalized! Please install GSL library.")
else ()
target_link_libraries(kritaimage PRIVATE ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES})
endif ()
target_include_directories(kritaimage
PUBLIC
$
$
$
$
$
$
$
)
set_target_properties(kritaimage PROPERTIES
VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritaimage ${INSTALL_TARGETS_DEFAULT_ARGS})
########### install schemas #############
install( FILES
metadata/schemas/dc.schema
metadata/schemas/exif.schema
metadata/schemas/tiff.schema
metadata/schemas/mkn.schema
metadata/schemas/xmp.schema
metadata/schemas/xmpmm.schema
metadata/schemas/xmprights.schema
DESTINATION ${DATA_INSTALL_DIR}/krita/metadata/schemas)
diff --git a/libs/image/brushengine/kis_paintop_preset.h b/libs/image/brushengine/kis_paintop_preset.h
index 8a7e5c1ff7..582e2292fc 100644
--- a/libs/image/brushengine/kis_paintop_preset.h
+++ b/libs/image/brushengine/kis_paintop_preset.h
@@ -1,138 +1,138 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt , (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
#include "KoID.h"
#include "kis_types.h"
#include "kis_shared.h"
#include "kritaimage_export.h"
#include
class KisPaintopSettingsUpdateProxy;
class KisPaintOpConfigWidget;
/**
* 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
{
public:
KisPaintOpPreset();
KisPaintOpPreset(const QString& filename);
~KisPaintOpPreset() override;
KisPaintOpPresetSP clone() const;
/// 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 save() override;
bool saveToDevice(QIODevice* dev) const override;
void toXML(QDomDocument& doc, QDomElement& elt) const;
void fromXML(const QDomElement& elt);
bool removable() const {
return true;
}
QString defaultFileExtension() const override {
return ".kpp";
}
void setPresetDirty(bool value);
bool isPresetDirty() 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 DirtyStateSaver {
+ class KRITAIMAGE_EXPORT DirtyStateSaver {
public:
DirtyStateSaver(KisPaintOpPreset *preset)
: m_preset(preset), m_isDirty(preset->isPresetDirty())
{
}
~DirtyStateSaver() {
m_preset->setPresetDirty(m_isDirty);
}
private:
KisPaintOpPreset *m_preset;
bool m_isDirty;
};
/**
* @brief The UpdatedPostponer class
* @see KisPaintopSettingsUpdateProxy::postponeSettingsChanges()
*/
- class UpdatedPostponer{
+ class KRITAIMAGE_EXPORT UpdatedPostponer{
public:
UpdatedPostponer(KisPaintOpPreset *preset);
~UpdatedPostponer();
private:
KisPaintopSettingsUpdateProxy *m_updateProxy;
};
void setOptionsWidget(KisPaintOpConfigWidget *widget);
KisPaintopSettingsUpdateProxy* updateProxy() const;
KisPaintopSettingsUpdateProxy* updateProxyNoCreate() const;
QList uniformProperties();
private:
struct Private;
Private * const m_d;
};
Q_DECLARE_METATYPE(KisPaintOpPresetSP)
#endif
diff --git a/libs/image/kis_scalar_keyframe_channel.h b/libs/image/kis_scalar_keyframe_channel.h
index 4ee6a653eb..23b5141ac0 100644
--- a/libs/image/kis_scalar_keyframe_channel.h
+++ b/libs/image/kis_scalar_keyframe_channel.h
@@ -1,70 +1,70 @@
/*
* Copyright (c) 2015 Jouni Pentikäinen
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* 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_SCALAR_KEYFRAME_CHANNEL_H
#define _KIS_SCALAR_KEYFRAME_CHANNEL_H
#include "kis_keyframe_channel.h"
#include "kis_keyframe_commands.h"
class KRITAIMAGE_EXPORT KisScalarKeyframeChannel : public KisKeyframeChannel
{
Q_OBJECT
public:
- struct AddKeyframeCommand : public KisReplaceKeyframeCommand
+ struct KRITAIMAGE_EXPORT AddKeyframeCommand : public KisReplaceKeyframeCommand
{
AddKeyframeCommand(KisScalarKeyframeChannel *channel, int time, qreal value, KUndo2Command *parentCommand);
};
KisScalarKeyframeChannel(const KoID& id, qreal minValue, qreal maxValue, KisDefaultBoundsBaseSP defaultBounds, KisKeyframe::InterpolationMode defaultInterpolation=KisKeyframe::Constant);
KisScalarKeyframeChannel(const KisScalarKeyframeChannel &rhs, KisNode *newParentNode);
~KisScalarKeyframeChannel() override;
bool hasScalarValue() const override;
qreal minScalarValue() const override;
qreal maxScalarValue() const override;
qreal scalarValue(const KisKeyframeSP keyframe) const override;
void setScalarValue(KisKeyframeSP keyframe, qreal value, KUndo2Command *parentCommand = 0) override;
void setInterpolationMode(KisKeyframeSP keyframe, KisKeyframe::InterpolationMode mode, KUndo2Command *parentCommand = 0);
void setInterpolationTangents(KisKeyframeSP keyframe, KisKeyframe::InterpolationTangentsMode, QPointF leftTangent, QPointF rightTangent, KUndo2Command *parentCommand);
qreal interpolatedValue(int time) const;
qreal currentValue() const;
static QPointF interpolate(QPointF point1, QPointF rightTangent, QPointF leftTangent, QPointF point2, qreal t);
protected:
KisKeyframeSP createKeyframe(int time, const KisKeyframeSP copySrc, KUndo2Command *parentCommand) override;
KisKeyframeSP createKeyframe(int time, qreal value, KUndo2Command *parentCommand);
void destroyKeyframe(KisKeyframeSP key, KUndo2Command *parentCommand) override;
void uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame) override;
QRect affectedRect(KisKeyframeSP key) override;
void saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename) override;
KisKeyframeSP loadKeyframe(const QDomElement &keyframeNode) override;
private:
void notifyKeyframeChanged(KisKeyframeSP keyframe);
struct Private;
QScopedPointer m_d;
};
#endif
diff --git a/libs/image/tests/CMakeLists.txt b/libs/image/tests/CMakeLists.txt
index 280bd167f5..6c536c8163 100644
--- a/libs/image/tests/CMakeLists.txt
+++ b/libs/image/tests/CMakeLists.txt
@@ -1,242 +1,241 @@
# 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_SOURCE_DIR}/libs/image/metadata
${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}
- ${Boost_INCLUDE_DIRS}
)
if(HAVE_VC)
include_directories(${Vc_INCLUDE_DIR})
endif()
include(ECMAddTests)
include(KritaAddBrokenUnitTest)
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_selection_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_change_profile_visitor_test.cpp
kis_clone_layer_test.cpp
kis_colorspace_convert_visitor_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_macro_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_recorded_action_factory_registry_test.cpp
kis_recorded_action_test.cpp
kis_recorded_filter_action_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_node_query_path_test.cpp
kis_fixed_point_maths_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_colorize_mask_test.cpp
NAME_PREFIX "krita-image-"
LINK_LIBRARIES kritaimage Qt5::Test)
ecm_add_test(kis_layer_style_filter_environment_test.cpp
TEST_NAME kritaimage-layer_style_filter_environment_test
LINK_LIBRARIES ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test)
ecm_add_test(kis_asl_parser_test.cpp
TEST_NAME kritalibpsd-asl_parser_test
LINK_LIBRARIES kritapsd kritapigment kritawidgetutils kritacommand Qt5::Xml Qt5::Test)
ecm_add_test(KisPerStrokeRandomSourceTest.cpp
TEST_NAME KisPerStrokeRandomSourceTest
LINK_LIBRARIES kritaimage Qt5::Test)
# ecm_add_test(kis_dom_utils_test.cpp
# TEST_NAME krita-image-DomUtils-Test
# LINK_LIBRARIES kritaimage Qt5::Test)
# kisdoc dep
# kis_transform_worker_test.cpp
# TEST_NAME krita-image-KisTransformWorkerTest
#LINK_LIBRARIES kritaimage Qt5::Test)
# kisdoc
# kis_perspective_transform_worker_test.cpp
# TEST_NAME krita-image-KisPerspectiveTransformWorkerTest
#LINK_LIBRARIES kritaimage Qt5::Test)
# kis_cs_conversion_test.cpp
# TEST_NAME krita-image-KisCsConversionTest
# LINK_LIBRARIES kritaimage Qt5::Test)
# kisdoc
# kis_processings_test.cpp
# TEST_NAME krita-image-KisProcessingsTest
#LINK_LIBRARIES kritaimage Qt5::Test)
# image/tests cannot use stuff that needs kisdocument
# kis_projection_leaf_test.cpp
# TEST_NAME kritaimage-projection_leaf_test
# LINK_LIBRARIES kritaimage Qt5::Test)
if (NOT HAVE_FAILING_CMAKE)
krita_add_broken_unit_test(kis_paint_device_test.cpp
TEST_NAME krita-image-KisPaintDeviceTest
LINK_LIBRARIES kritaimage kritaodf Qt5::Test)
else()
message(WARNING "Skipping KisPaintDeviceTest!!!!!!!!!!!!!!")
endif()
if (NOT HAVE_FAILING_CMAKE)
krita_add_broken_unit_test(kis_filter_mask_test.cpp
TEST_NAME krita-image-KisFilterMaskTest
LINK_LIBRARIES kritaimage Qt5::Test)
else()
message(WARNING "Skipping KisFilterMaskTest!!!!!!!!!!!!!!")
endif()
krita_add_broken_unit_test(kis_transform_mask_test.cpp
TEST_NAME krita-image-KisTransformMaskTest
LINK_LIBRARIES kritaimage Qt5::Test)
krita_add_broken_unit_test(kis_histogram_test.cpp
TEST_NAME krita-image-KisHistogramTest
LINK_LIBRARIES kritaimage Qt5::Test)
krita_add_broken_unit_test(kis_walkers_test.cpp
TEST_NAME krita-image-KisWalkersTest
LINK_LIBRARIES kritaimage Qt5::Test)
#krita_add_broken_unit_test(kis_async_merger_test.cpp
# TEST_NAME krita-image-KisAsyncMergerTest
# LINK_LIBRARIES kritaimage Qt5::Test)
krita_add_broken_unit_test(kis_update_scheduler_test.cpp
TEST_NAME krita-image-KisUpdateSchedulerTest
LINK_LIBRARIES kritaimage Qt5::Test)
krita_add_broken_unit_test(kis_queues_progress_updater_test.cpp
TEST_NAME krita-image-KisQueuesProgressUpdaterTest
LINK_LIBRARIES kritaimage Qt5::Test)
krita_add_broken_unit_test(kis_cage_transform_worker_test.cpp
TEST_NAME krita-image-KisCageTransformWorkerTest
LINK_LIBRARIES kritaimage Qt5::Test)
krita_add_broken_unit_test(kis_meta_data_test.cpp
TEST_NAME krita-image-KisMetaDataTest
LINK_LIBRARIES kritaimage Qt5::Test)
krita_add_broken_unit_test(kis_random_generator_test.cpp
TEST_NAME krita-image-KisRandomGeneratorTest
LINK_LIBRARIES kritaimage Qt5::Test)
krita_add_broken_unit_test(kis_keyframing_test.cpp
TEST_NAME krita-image-Keyframing-Test
LINK_LIBRARIES kritaimage Qt5::Test)
krita_add_broken_unit_test(kis_image_animation_interface_test.cpp
TEST_NAME krita-image-ImageAnimationInterface-Test
LINK_LIBRARIES ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test)
krita_add_broken_unit_test(kis_onion_skin_compositor_test.cpp
TEST_NAME krita-image-OnionSkinCompositor-Test
LINK_LIBRARIES ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test)
krita_add_broken_unit_test(kis_layer_styles_test.cpp
TEST_NAME krita-image-LayerStylesTest
LINK_LIBRARIES kritaimage Qt5::Test)
diff --git a/libs/image/tiles3/CMakeLists.txt b/libs/image/tiles3/CMakeLists.txt
index 4e7c26ef58..88c0516360 100644
--- a/libs/image/tiles3/CMakeLists.txt
+++ b/libs/image/tiles3/CMakeLists.txt
@@ -1,2 +1 @@
-include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
add_subdirectory(tests)
diff --git a/libs/libqml/CMakeLists.txt b/libs/libqml/CMakeLists.txt
index 2560c1f04f..7e3b996efb 100644
--- a/libs/libqml/CMakeLists.txt
+++ b/libs/libqml/CMakeLists.txt
@@ -1,46 +1,45 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
include_directories(SYSTEM
- ${Boost_INCLUDE_DIRS}
${EIGEN3_INCLUDE_DIR}
)
add_subdirectory(plugins)
set(kritaqml_SRCS
DocumentManager.cpp
DocumentListModel.cpp
KisSelectionExtras.cpp
RecentFileManager.cpp
ProgressProxy.cpp
PropertyContainer.cpp
Settings.cpp
VirtualKeyboardController.cpp
Theme.cpp
QmlGlobalEngine.cpp
KisSketchView.cpp
)
qt5_add_resources(kritaqml_SRCS qml/qml.qrc)
add_library(kritaqml SHARED ${kritaqml_SRCS})
generate_export_header(kritaqml BASE_NAME krita_sketch)
set_target_properties(kritaqml
PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
target_link_libraries(kritaqml
Qt5::Quick
Qt5::Gui
Qt5::Core
kritawidgets
kritaui
)
include(GNUInstallDirs)
install(TARGETS kritaqml ${INSTALL_TARGETS_DEFAULT_ARGS})
install(DIRECTORY qmlthemes DESTINATION ${DATA_INSTALL_DIR}/krita)
diff --git a/libs/pigment/CMakeLists.txt b/libs/pigment/CMakeLists.txt
index b1552b6da6..9525f9ee82 100644
--- a/libs/pigment/CMakeLists.txt
+++ b/libs/pigment/CMakeLists.txt
@@ -1,124 +1,121 @@
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)
-include_directories(SYSTEM
- ${Boost_INCLUDE_DIRS}
-)
set(FILE_OPENEXR_SOURCES)
set(LINK_OPENEXR_LIB)
if(OPENEXR_FOUND)
include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR})
set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES})
add_definitions(${OPENEXR_DEFINITIONS})
endif()
set(LINK_VC_LIB)
if(HAVE_VC)
include_directories(SYSTEM ${Vc_INCLUDE_DIR})
set(LINK_VC_LIB ${Vc_LIBRARIES})
ko_compile_for_all_implementations_no_scalar(__per_arch_factory_objs compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp)
message("Following objects are generated from the per-arch lib")
message(${__per_arch_factory_objs})
endif()
add_subdirectory(tests)
add_subdirectory(benchmarks)
set(kritapigment_SRCS
DebugPigment.cpp
KoBasicHistogramProducers.cpp
KoColor.cpp
KoColorDisplayRendererInterface.cpp
KoColorConversionAlphaTransformation.cpp
KoColorConversionCache.cpp
KoColorConversions.cpp
KoColorConversionSystem.cpp
KoColorConversionTransformation.cpp
KoColorProofingConversionTransformation.cpp
KoColorConversionTransformationFactory.cpp
KoColorModelStandardIds.cpp
KoColorProfile.cpp
KoColorSpace.cpp
KoColorSpaceEngine.cpp
KoColorSpaceFactory.cpp
KoColorSpaceMaths.cpp
KoColorSpaceRegistry.cpp
KoColorProfileStorage.cpp
KoColorTransformation.cpp
KoColorTransformationFactory.cpp
KoColorTransformationFactoryRegistry.cpp
KoCompositeColorTransformation.cpp
KoCompositeOp.cpp
KoCompositeOpRegistry.cpp
KoCopyColorConversionTransformation.cpp
KoFallBackColorTransformation.cpp
KoHistogramProducer.cpp
KoMultipleColorConversionTransformation.cpp
KoUniqueNumberForIdServer.cpp
colorspaces/KoAlphaColorSpace.cpp
colorspaces/KoLabColorSpace.cpp
colorspaces/KoRgbU16ColorSpace.cpp
colorspaces/KoRgbU8ColorSpace.cpp
colorspaces/KoSimpleColorSpaceEngine.cpp
compositeops/KoOptimizedCompositeOpFactory.cpp
compositeops/KoOptimizedCompositeOpFactoryPerArch_Scalar.cpp
${__per_arch_factory_objs}
colorprofiles/KoDummyColorProfile.cpp
resources/KoAbstractGradient.cpp
resources/KoColorSet.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
$
$
)
target_link_libraries( kritapigment
PUBLIC
kritaplugin
kritastore
kritaglobal
${EXTRA_LIBRARIES}
KF5::I18n
KF5::ConfigCore
Qt5::Core
Qt5::Gui
Qt5::Xml
${WIN32_PLATFORM_NET_LIBS}
)
set_target_properties(kritapigment PROPERTIES
VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritapigment ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/libs/psd/CMakeLists.txt b/libs/psd/CMakeLists.txt
index 9fcdb6eb4e..611cbf52f6 100644
--- a/libs/psd/CMakeLists.txt
+++ b/libs/psd/CMakeLists.txt
@@ -1,36 +1,35 @@
include_directories( ${CMAKE_BINARY_DIR}/libs/psd #For kispsd_include.h
- ${Boost_INCLUDE_DIRS}
)
set(kritapsd_LIB_SRCS
psd_utils.cpp
psd.cpp
compression.cpp
psd_pattern.cpp
asl/kis_asl_reader.cpp
asl/kis_asl_reader_utils.cpp
asl/kis_asl_xml_parser.cpp
asl/kis_asl_object_catcher.cpp
asl/kis_asl_callback_object_catcher.cpp
asl/kis_asl_xml_writer.cpp
asl/kis_asl_writer_utils.cpp
asl/kis_asl_patterns_writer.cpp
asl/kis_asl_writer.cpp
)
add_library(kritapsd SHARED ${kritapsd_LIB_SRCS} )
generate_export_header(kritapsd BASE_NAME kritapsd)
if (WIN32)
target_link_libraries(kritapsd kritapigment kritaglobal KF5::I18n ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${WIN32_PLATFORM_NET_LIBS})
else (WIN32)
target_link_libraries(kritapsd kritapigment kritaglobal KF5::I18n ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY})
endif (WIN32)
set_target_properties(kritapsd PROPERTIES
VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritapsd ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index 30d7be8d73..dc9b264b29 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -1,580 +1,579 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile
${EXIV2_INCLUDE_DIR}
)
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
${OCIO_INCLUDE_DIR}
- ${Boost_INCLUDE_DIRS}
)
add_subdirectory( tests )
if (APPLE)
find_library(FOUNDATION_LIBRARY Foundation)
find_library(APPKIT_LIBRARY AppKit)
endif ()
set(kritaui_LIB_SRCS
canvas/kis_canvas_widget_base.cpp
canvas/kis_canvas2.cpp
canvas/kis_canvas_updates_compressor.cpp
canvas/kis_canvas_controller.cpp
canvas/kis_paintop_transformation_connector.cpp
canvas/kis_display_color_converter.cpp
canvas/kis_display_filter.cpp
canvas/kis_exposure_gamma_correction_interface.cpp
canvas/kis_tool_proxy.cpp
canvas/kis_canvas_decoration.cc
canvas/kis_coordinates_converter.cpp
canvas/kis_grid_manager.cpp
canvas/kis_grid_decoration.cpp
canvas/kis_grid_config.cpp
canvas/kis_prescaled_projection.cpp
canvas/kis_qpainter_canvas.cpp
canvas/kis_projection_backend.cpp
canvas/kis_update_info.cpp
canvas/kis_image_patch.cpp
canvas/kis_image_pyramid.cpp
canvas/kis_infinity_manager.cpp
canvas/kis_change_guides_command.cpp
canvas/kis_guides_decoration.cpp
canvas/kis_guides_manager.cpp
canvas/kis_guides_config.cpp
canvas/kis_snap_config.cpp
canvas/kis_snap_line_strategy.cpp
canvas/KisSnapPointStrategy.cpp
dialogs/kis_about_application.cpp
dialogs/kis_dlg_adj_layer_props.cc
dialogs/kis_dlg_adjustment_layer.cc
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_generator_layer.cpp
dialogs/kis_dlg_file_layer.cpp
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_stroke_selection_properties.cpp
dialogs/kis_dlg_image_properties.cc
dialogs/kis_dlg_layer_properties.cc
dialogs/kis_dlg_preferences.cc
dialogs/slider_and_spin_box_sync.cpp
dialogs/kis_dlg_blacklist_cleanup.cpp
dialogs/kis_dlg_layer_style.cpp
dialogs/kis_dlg_png_import.cpp
dialogs/kis_dlg_import_image_sequence.cpp
dialogs/kis_delayed_save_dialog.cpp
dialogs/kis_dlg_internal_color_selector.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
kis_base_option.cpp
kis_canvas_resource_provider.cpp
kis_derived_resources.cpp
kis_categories_mapper.cpp
kis_categorized_list_model.cpp
kis_categorized_item_delegate.cpp
kis_clipboard.cc
kis_config.cc
kis_config_notifier.cpp
kis_control_frame.cpp
kis_composite_ops_model.cc
kis_paint_ops_model.cpp
kis_cursor.cc
kis_cursor_cache.cpp
kis_custom_pattern.cc
kis_file_layer.cpp
kis_change_file_layer_command.h
kis_safe_document_loader.cpp
kis_splash_screen.cpp
kis_filter_manager.cc
kis_filters_model.cc
kis_histogram_view.cc
KisImageBarrierLockerWithFeedback.cpp
kis_image_manager.cc
kis_image_view_converter.cpp
kis_import_catcher.cc
kis_layer_manager.cc
kis_mask_manager.cc
kis_mimedata.cpp
kis_node_commands_adapter.cpp
kis_node_manager.cpp
kis_node_juggler_compressed.cpp
kis_node_selection_adapter.cpp
kis_node_insertion_adapter.cpp
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
kis_painting_assistants_manager.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
kis_script_manager.cpp
kis_resource_server_provider.cpp
KisSelectedShapesProxy.cpp
kis_selection_decoration.cc
kis_selection_manager.cc
kis_statusbar.cc
kis_zoom_manager.cc
kis_favorite_resource_manager.cpp
kis_workspace_resource.cpp
kis_action.cpp
kis_action_manager.cpp
kis_view_plugin.cpp
kis_canvas_controls_manager.cpp
kis_tooltip_manager.cpp
kis_multinode_property.cpp
kis_stopgradient_editor.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
kis_fps_decoration.cpp
recorder/kis_node_query_path_editor.cc
recorder/kis_recorded_action_creator.cc
recorder/kis_recorded_action_creator_factory.cc
recorder/kis_recorded_action_creator_factory_registry.cc
recorder/kis_recorded_action_editor_factory.cc
recorder/kis_recorded_action_editor_factory_registry.cc
recorder/kis_recorded_filter_action_editor.cc
recorder/kis_recorded_filter_action_creator.cpp
recorder/kis_recorded_paint_action_editor.cc
tool/kis_selection_tool_helper.cpp
tool/kis_selection_tool_config_widget_helper.cpp
tool/kis_rectangle_constraint_widget.cpp
tool/kis_shape_tool_helper.cpp
tool/kis_tool.cc
tool/kis_delegated_tool_policies.cpp
tool/kis_tool_freehand.cc
tool/kis_speed_smoother.cpp
tool/kis_painting_information_builder.cpp
tool/kis_stabilized_events_sampler.cpp
tool/kis_tool_freehand_helper.cpp
tool/kis_tool_multihand_helper.cpp
tool/kis_figure_painting_tool_helper.cpp
tool/kis_recording_adapter.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
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_gradient_slider_widget.cc
widgets/kis_gradient_slider.cpp
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_popup_button.cc
widgets/kis_preset_chooser.cpp
widgets/kis_progress_widget.cpp
widgets/kis_selection_options.cc
widgets/kis_scratch_pad.cpp
widgets/kis_scratch_pad_event_filter.cpp
widgets/kis_preset_selector_strip.cpp
widgets/kis_slider_spin_box.cpp
widgets/kis_size_group.cpp
widgets/kis_size_group_p.cpp
widgets/kis_wdg_generator.cpp
widgets/kis_workspace_chooser.cpp
widgets/squeezedcombobox.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_spinbox_color_selector.cpp
widgets/kis_screen_color_picker.cpp
widgets/kis_preset_live_preview_view.cpp
widgets/KoDualColorButton.cpp
widgets/kis_color_input.cpp
widgets/kis_color_button.cpp
widgets/KisVisualColorSelector.cpp
widgets/KisVisualColorSelectorShape.cpp
widgets/KisVisualEllipticalSelectorShape.cpp
widgets/KisVisualRectangleSelectorShape.cpp
widgets/KisVisualTriangleSelectorShape.cpp
widgets/KoStrokeConfigWidget.cpp
widgets/KoFillConfigWidget.cpp
widgets/KoShapeFillWrapper.cpp
utils/kis_document_aware_spin_box_unit_manager.cpp
input/kis_input_manager.cpp
input/kis_input_manager_p.cpp
input/kis_extended_modifiers_mapper.cpp
input/kis_abstract_input_action.cpp
input/kis_tool_invocation_action.cpp
input/kis_pan_action.cpp
input/kis_alternate_invocation_action.cpp
input/kis_rotate_canvas_action.cpp
input/kis_zoom_action.cpp
input/kis_change_frame_action.cpp
input/kis_gamma_exposure_action.cpp
input/kis_show_palette_action.cpp
input/kis_change_primary_setting_action.cpp
input/kis_abstract_shortcut.cpp
input/kis_native_gesture_shortcut.cpp
input/kis_single_action_shortcut.cpp
input/kis_stroke_shortcut.cpp
input/kis_shortcut_matcher.cpp
input/kis_select_layer_action.cpp
input/KisQtWidgetsTweaker.cpp
operations/kis_operation.cpp
operations/kis_operation_configuration.cpp
operations/kis_operation_registry.cpp
operations/kis_operation_ui_factory.cpp
operations/kis_operation_ui_widget.cpp
operations/kis_filter_selection_operation.cpp
actions/kis_selection_action_factories.cpp
actions/KisPasteActionFactory.cpp
input/kis_touch_shortcut.cpp
kis_document_undo_store.cpp
kis_transaction_based_command.cpp
kis_gui_context_command.cpp
kis_gui_context_command_p.cpp
input/kis_tablet_debugger.cpp
input/kis_input_profile_manager.cpp
input/kis_input_profile.cpp
input/kis_shortcut_configuration.cpp
input/config/kis_input_configuration_page.cpp
input/config/kis_edit_profiles_dialog.cpp
input/config/kis_input_profile_model.cpp
input/config/kis_input_configuration_page_item.cpp
input/config/kis_action_shortcuts_model.cpp
input/config/kis_input_type_delegate.cpp
input/config/kis_input_mode_delegate.cpp
input/config/kis_input_button.cpp
input/config/kis_input_editor_delegate.cpp
input/config/kis_mouse_input_editor.cpp
input/config/kis_wheel_input_editor.cpp
input/config/kis_key_input_editor.cpp
processing/fill_processing_visitor.cpp
kis_asl_layer_style_serializer.cpp
kis_psd_layer_style_resource.cpp
canvas/kis_mirror_axis.cpp
kis_abstract_perspective_grid.cpp
KisApplication.cpp
KisAutoSaveRecoveryDialog.cpp
KisDetailsPane.cpp
KisDocument.cpp
KisNodeDelegate.cpp
kis_node_view_visibility_delegate.cpp
KisNodeToolTip.cpp
KisNodeView.cpp
kis_node_view_color_scheme.cpp
KisImportExportFilter.cpp
KisFilterEntry.cpp
KisImportExportManager.cpp
KisImportExportUtils.cpp
kis_async_action_feedback.cpp
KisMainWindow.cpp
KisOpenPane.cpp
KisPart.cpp
KisPrintJob.cpp
KisTemplate.cpp
KisTemplateCreateDia.cpp
KisTemplateGroup.cpp
KisTemplates.cpp
KisTemplatesPane.cpp
KisTemplateTree.cpp
KisUndoStackAction.cpp
KisView.cpp
thememanager.cpp
kis_mainwindow_observer.cpp
KisViewManager.cpp
kis_mirror_manager.cpp
qtlockedfile/qtlockedfile.cpp
qtsingleapplication/qtlocalpeer.cpp
qtsingleapplication/qtsingleapplication.cpp
KisResourceBundle.cpp
KisResourceBundleManifest.cpp
kis_md5_generator.cpp
KisApplicationArguments.cpp
KisNetworkAccessManager.cpp
KisMultiFeedRSSModel.cpp
KisRemoteFileFetcher.cpp
KisPaletteModel.cpp
kis_palette_delegate.cpp
kis_palette_view.cpp
KisColorsetChooser.cpp
KisSaveGroupVisitor.cpp
)
if(WIN32)
if (NOT Qt5Gui_PRIVATE_INCLUDE_DIRS)
message(FATAL_ERROR "Qt5Gui Private header are missing!")
endif()
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/kis_tablet_event.cpp
input/wintab/kis_tablet_support_win.cpp
input/wintab/kis_screen_size_choice_dialog.cpp
qtlockedfile/qtlockedfile_win.cpp
input/wintab/kis_tablet_support_win8.cpp
opengl/kis_opengl_win.cpp
)
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
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
)
if(UNIX)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/kis_tablet_event.cpp
input/wintab/kis_tablet_support.cpp
qtlockedfile/qtlockedfile_unix.cpp
)
if(NOT APPLE)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/wintab/kis_tablet_support_x11.cpp
input/wintab/qxcbconnection_xi2.cpp
input/wintab/qxcbconnection.cpp
input/wintab/kis_xi2_event_filter.cpp
)
endif()
endif()
if(APPLE)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
osx.mm
)
endif()
ki18n_wrap_ui(kritaui_LIB_SRCS
widgets/KoFillConfigWidget.ui
widgets/KoStrokeConfigWidget.ui
forms/wdgdlgpngimport.ui
forms/wdgfullscreensettings.ui
forms/wdgautogradient.ui
forms/wdggeneralsettings.ui
forms/wdgperformancesettings.ui
forms/wdggenerators.ui
forms/wdgbookmarkedconfigurationseditor.ui
forms/wdgapplyprofile.ui
forms/wdgcustompattern.ui
forms/wdglayerproperties.ui
forms/wdgcolorsettings.ui
forms/wdgtabletsettings.ui
forms/wdgcolorspaceselector.ui
forms/wdgcolorspaceselectoradvanced.ui
forms/wdgdisplaysettings.ui
forms/kis_previewwidgetbase.ui
forms/kis_matrix_widget.ui
forms/wdgselectionoptions.ui
forms/wdggeometryoptions.ui
forms/wdgnewimage.ui
forms/wdgimageproperties.ui
forms/wdgmaskfromselection.ui
forms/wdgmasksource.ui
forms/wdgfilterdialog.ui
forms/wdgmetadatamergestrategychooser.ui
forms/wdgpaintoppresets.ui
forms/wdgpaintopsettings.ui
forms/wdgdlggeneratorlayer.ui
forms/wdgdlgfilelayer.ui
forms/wdgfilterselector.ui
forms/wdgfilternodecreation.ui
forms/wdgpaintactioneditor.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
brushhud/kis_dlg_brush_hud_config.ui
forms/wdgdlginternalcolorselector.ui
dialogs/kis_delayed_save_dialog.ui
input/config/kis_input_configuration_page.ui
input/config/kis_edit_profiles_dialog.ui
input/config/kis_input_configuration_page_item.ui
input/config/kis_mouse_input_editor.ui
input/config/kis_wheel_input_editor.ui
input/config/kis_key_input_editor.ui
layerstyles/wdgBevelAndEmboss.ui
layerstyles/wdgblendingoptions.ui
layerstyles/WdgColorOverlay.ui
layerstyles/wdgContour.ui
layerstyles/wdgdropshadow.ui
layerstyles/WdgGradientOverlay.ui
layerstyles/wdgInnerGlow.ui
layerstyles/wdglayerstyles.ui
layerstyles/WdgPatternOverlay.ui
layerstyles/WdgSatin.ui
layerstyles/WdgStroke.ui
layerstyles/wdgstylesselector.ui
layerstyles/wdgTexture.ui
wdgsplash.ui
input/wintab/kis_screen_size_choice_dialog.ui
)
QT5_WRAP_CPP(kritaui_HEADERS_MOC KisNodePropertyAction_p.h)
add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} )
generate_export_header(kritaui BASE_NAME kritaui)
target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network
kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} ${EXIV2_LIBRARIES}
)
if (HAVE_QT_MULTIMEDIA)
target_link_libraries(kritaui Qt5::Multimedia)
endif()
if (HAVE_KIO)
target_link_libraries(kritaui KF5::KIOCore)
endif()
if (NOT WIN32 AND NOT APPLE)
target_link_libraries(kritaui ${X11_X11_LIB}
${X11_Xinput_LIB}
${XCB_LIBRARIES})
endif()
if(APPLE)
target_link_libraries(kritaui ${FOUNDATION_LIBRARY})
target_link_libraries(kritaui ${APPKIT_LIBRARY})
endif ()
target_link_libraries(kritaui ${OPENEXR_LIBRARIES})
# Add VSync disable workaround
if(NOT WIN32 AND NOT APPLE)
target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras)
endif()
if(X11_FOUND)
target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES})
endif()
target_include_directories(kritaui
PUBLIC
$
$
$
$
$
$
$
)
set_target_properties(kritaui
PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS})
if (APPLE)
install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita)
endif ()
diff --git a/libs/ui/KisDocument.cpp b/libs/ui/KisDocument.cpp
index e1661fa781..5878fe0d0c 100644
--- a/libs/ui/KisDocument.cpp
+++ b/libs/ui/KisDocument.cpp
@@ -1,1669 +1,1674 @@
/* This file is part of the Krita project
*
* Copyright (C) 2014 Boudewijn Rempt
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisMainWindow.h" // XXX: remove
#include // XXX: remove
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// Krita Image
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_layer_utils.h"
// Local
#include "KisViewManager.h"
#include "kis_clipboard.h"
#include "widgets/kis_custom_image_widget.h"
#include "canvas/kis_canvas2.h"
#include "flake/kis_shape_controller.h"
#include "kis_statusbar.h"
#include "widgets/kis_progress_widget.h"
#include "kis_canvas_resource_provider.h"
#include "kis_resource_server_provider.h"
#include "kis_node_manager.h"
#include "KisPart.h"
#include "KisApplication.h"
#include "KisDocument.h"
#include "KisImportExportManager.h"
#include "KisPart.h"
#include "KisView.h"
#include "kis_grid_config.h"
#include "kis_guides_config.h"
#include "kis_image_barrier_lock_adapter.h"
#include
#include "kis_config_notifier.h"
#include "kis_async_action_feedback.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
using namespace std;
namespace {
constexpr int errorMessageTimeout = 5000;
constexpr int successMessageTimeout = 1000;
}
/**********************************************************
*
* KisDocument
*
**********************************************************/
//static
QString KisDocument::newObjectName()
{
static int s_docIFNumber = 0;
QString name; name.setNum(s_docIFNumber++); name.prepend("document_");
return name;
}
class UndoStack : public KUndo2Stack
{
public:
UndoStack(KisDocument *doc)
: KUndo2Stack(doc),
m_doc(doc)
{
}
void setIndex(int idx) override {
KisImageWSP image = this->image();
image->requestStrokeCancellation();
if(image->tryBarrierLock()) {
KUndo2Stack::setIndex(idx);
image->unlock();
}
}
void notifySetIndexChangedOneCommand() override {
KisImageWSP image = this->image();
image->unlock();
/**
* Some very weird commands may emit blocking signals to
* the GUI (e.g. KisGuiContextCommand). Here is the best thing
* we can do to avoid the deadlock
*/
while(!image->tryBarrierLock()) {
QApplication::processEvents();
}
}
void undo() override {
KisImageWSP image = this->image();
image->requestUndoDuringStroke();
if (image->tryUndoUnfinishedLod0Stroke() == UNDO_OK) {
return;
}
if(image->tryBarrierLock()) {
KUndo2Stack::undo();
image->unlock();
}
}
void redo() override {
KisImageWSP image = this->image();
if(image->tryBarrierLock()) {
KUndo2Stack::redo();
image->unlock();
}
}
private:
KisImageWSP image() {
KisImageWSP currentImage = m_doc->image();
Q_ASSERT(currentImage);
return currentImage;
}
private:
KisDocument *m_doc;
};
class Q_DECL_HIDDEN KisDocument::Private
{
public:
Private(KisDocument *q) :
docInfo(new KoDocumentInfo(q)), // deleted by QObject
importExportManager(new KisImportExportManager(q)), // deleted manually
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*/),
savingLock(&savingMutex)
{
if (QLocale().measurementSystem() == QLocale::ImperialSystem) {
unit = KoUnit::Inch;
} else {
unit = KoUnit::Centimeter;
}
}
Private(const Private &rhs, KisDocument *q) :
docInfo(new KoDocumentInfo(*rhs.docInfo, q)),
unit(rhs.unit),
importExportManager(new KisImportExportManager(q)),
mimeType(rhs.mimeType),
outputMimeType(rhs.outputMimeType),
undoStack(new UndoStack(q)),
guidesConfig(rhs.guidesConfig),
m_bAutoDetectedMime(rhs.m_bAutoDetectedMime),
m_url(rhs.m_url),
m_file(rhs.m_file),
modified(rhs.modified),
readwrite(rhs.readwrite),
firstMod(rhs.firstMod),
lastMod(rhs.lastMod),
nserver(new KisNameServer(*rhs.nserver)),
preActivatedNode(0), // the node is from another hierarchy!
imageIdleWatcher(2000 /*ms*/),
assistants(rhs.assistants), // WARNING: assistants should not store pointers to the document!
gridConfig(rhs.gridConfig),
savingLock(&savingMutex)
{
}
~Private() {
// Don't delete m_d->shapeController because it's in a QObject hierarchy.
delete nserver;
}
KoDocumentInfo *docInfo = 0;
KoUnit unit;
KisImportExportManager *importExportManager = 0; // The filter-manager to use when loading/saving [for the options]
QByteArray mimeType; // The actual mimetype of the document
QByteArray outputMimeType; // The mimetype to use when saving
QTimer autoSaveTimer;
QString lastErrorMessage; // see openFile()
QString lastWarningMessage;
int autoSaveDelay = 300; // in seconds, 0 to disable.
bool modifiedAfterAutosave = false;
bool isAutosaving = false;
bool disregardAutosaveFailure = false;
KUndo2Stack *undoStack = 0;
KisGuidesConfig guidesConfig;
bool m_bAutoDetectedMime = false; // whether the mimetype in the arguments was detected by the part itself
QUrl m_url; // local url - the one displayed to the user.
QString m_file; // Local file - the only one the part implementation should deal with.
QMutex savingMutex;
bool modified = false;
bool readwrite = false;
QDateTime firstMod;
QDateTime lastMod;
KisNameServer *nserver;
KisImageSP image;
KisImageSP savingImage;
KisNodeWSP preActivatedNode;
KisShapeController* shapeController = 0;
KoShapeController* koShapeController = 0;
KisIdleWatcher imageIdleWatcher;
QScopedPointer imageIdleConnection;
QList assistants;
KisGridConfig gridConfig;
StdLockableWrapper savingLock;
bool modifiedWhileSaving = false;
QScopedPointer backgroundSaveDocument;
QPointer savingUpdater;
QFuture childSavingFuture;
KritaUtils::ExportFileJob backgroundSaveJob;
bool isRecovered = false;
void setImageAndInitIdleWatcher(KisImageSP _image) {
image = _image;
imageIdleWatcher.setTrackedImage(image);
if (image) {
imageIdleConnection.reset(
new KisSignalAutoConnection(
&imageIdleWatcher, SIGNAL(startedIdleMode()),
image.data(), SLOT(explicitRegenerateLevelOfDetail())));
}
}
class StrippedSafeSavingLocker;
};
class KisDocument::Private::StrippedSafeSavingLocker {
public:
StrippedSafeSavingLocker(QMutex *savingMutex, KisImageSP image)
: m_locked(false)
, m_image(image)
, m_savingLock(savingMutex)
, m_imageLock(image, true)
{
/**
* Initial try to lock both objects. Locking the image guards
* us from any image composition threads running in the
* background, while savingMutex guards us from entering the
* saving code twice by autosave and main threads.
*
* Since we are trying to lock multiple objects, so we should
* do it in a safe manner.
*/
m_locked = std::try_lock(m_imageLock, m_savingLock) < 0;
if (!m_locked) {
m_image->requestStrokeEnd();
QApplication::processEvents();
// one more try...
m_locked = std::try_lock(m_imageLock, m_savingLock) < 0;
}
}
~StrippedSafeSavingLocker() {
if (m_locked) {
m_imageLock.unlock();
m_savingLock.unlock();
}
}
bool successfullyLocked() const {
return m_locked;
}
private:
bool m_locked;
KisImageSP m_image;
StdLockableWrapper m_savingLock;
KisImageBarrierLockAdapter m_imageLock;
};
KisDocument::KisDocument()
: d(new Private(this))
{
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
connect(d->undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(slotUndoStackCleanChanged(bool)));
connect(&d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
setObjectName(newObjectName());
// preload the krita resources
KisResourceServerProvider::instance();
d->shapeController = new KisShapeController(this, d->nserver),
d->koShapeController = new KoShapeController(0, d->shapeController),
slotConfigChanged();
}
KisDocument::KisDocument(const KisDocument &rhs)
: QObject(),
d(new Private(*rhs.d, this))
{
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
connect(d->undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(slotUndoStackCleanChanged(bool)));
connect(&d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
setObjectName(rhs.objectName());
d->shapeController = new KisShapeController(this, d->nserver),
d->koShapeController = new KoShapeController(0, d->shapeController),
slotConfigChanged();
// clone the image with keeping the GUIDs of the layers intact
// NOTE: we expect the image to be locked!
setCurrentImage(rhs.image()->clone(true));
if (rhs.d->preActivatedNode) {
// since we clone uuid's, we can use them for lacating new
// nodes. Otherwise we would need to use findSymmetricClone()
d->preActivatedNode =
KisLayerUtils::findNodeByUuid(d->image->root(), rhs.d->preActivatedNode->uuid());
}
}
KisDocument::~KisDocument()
{
// wait until all the pending operations are in progress
waitForSavingToComplete();
/**
* Push a timebomb, which will try to release the memory after
* the document has been deleted
*/
KisPaintDevice::createMemoryReleaseObject()->deleteLater();
d->autoSaveTimer.disconnect(this);
d->autoSaveTimer.stop();
delete d->importExportManager;
// Despite being QObject they needs to be deleted before the image
delete d->shapeController;
delete d->koShapeController;
if (d->image) {
d->image->notifyAboutToBeDeleted();
/**
* WARNING: We should wait for all the internal image jobs to
* finish before entering KisImage's destructor. The problem is,
* while execution of KisImage::~KisImage, all the weak shared
* pointers pointing to the image enter an inconsistent
* state(!). The shared counter is already zero and destruction
* has started, but the weak reference doesn't know about it,
* because KisShared::~KisShared hasn't been executed yet. So all
* the threads running in background and having weak pointers will
* enter the KisImage's destructor as well.
*/
d->image->requestStrokeCancellation();
d->image->waitForDone();
// clear undo commands that can still point to the image
d->undoStack->clear();
d->image->waitForDone();
KisImageWSP sanityCheckPointer = d->image;
Q_UNUSED(sanityCheckPointer);
// The following line trigger the deletion of the image
d->image.clear();
// check if the image has actually been deleted
KIS_SAFE_ASSERT_RECOVER_NOOP(!sanityCheckPointer.isValid());
}
delete d;
}
bool KisDocument::reload()
{
// XXX: reimplement!
return false;
}
KisDocument *KisDocument::clone()
{
return new KisDocument(*this);
}
bool KisDocument::exportDocumentImpl(const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration)
{
QFileInfo filePathInfo(job.filePath);
if (filePathInfo.exists() && !filePathInfo.isWritable()) {
slotCompleteSavingDocument(job,
KisImportExportFilter::CreationError,
i18n("%1 cannot be written to. Please save under a different name.", job.filePath));
return false;
}
KisConfig cfg;
if (cfg.backupFile() && filePathInfo.exists()) {
KBackup::backupFile(job.filePath);
}
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!job.mimeType.isEmpty(), false);
const QString actionName =
job.flags & KritaUtils::SaveIsExporting ?
i18n("Exporting Document...") :
i18n("Saving Document...");
bool started =
initiateSavingInBackground(actionName,
this, SLOT(slotCompleteSavingDocument(KritaUtils::ExportFileJob, KisImportExportFilter::ConversionStatus,QString)),
job, exportConfiguration);
if (!started) {
emit canceled(QString());
}
return started;
}
bool KisDocument::exportDocument(const QUrl &url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
{
using namespace KritaUtils;
SaveFlags flags = SaveIsExporting;
if (showWarnings) {
flags |= SaveShowWarnings;
}
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;
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 filter(KisImportExportManager::filterForMimeType(nativeFormatMimeType(), KisImportExportManager::Export));
filter->setBatchMode(true);
filter->setMimeType(nativeFormatMimeType());
Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image);
if (!locker.successfullyLocked()) {
return byteArray;
}
d->savingImage = d->image;
if (filter->convert(this, &buffer) != KisImportExportFilter::OK) {
qWarning() << "serializeToByteArray():: Could not export to our native format";
}
return byteArray;
}
void KisDocument::slotCompleteSavingDocument(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage)
{
+ if (status == KisImportExportFilter::UserCancelled)
+ return;
+
const QString fileName = QFileInfo(job.filePath).fileName();
if (status != KisImportExportFilter::OK) {
emit statusBarMessage(i18nc("%1 --- failing file name, %2 --- error mesage",
"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, errorMessage));
}
} else {
if (!(job.flags & KritaUtils::SaveIsExporting)) {
setUrl(QUrl::fromLocalFile(job.filePath));
setLocalFilePath(job.filePath);
setMimeType(job.mimeType);
updateEditingTime(true);
if (!d->modifiedWhileSaving) {
d->undoStack->setClean();
}
setRecovered(false);
removeAutoSaveFiles();
}
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->importExportManager->batchMode();
}
void KisDocument::setFileBatchMode(const bool batchMode)
{
d->importExportManager->setBatchMode(batchMode);
}
KisDocument* KisDocument::lockAndCloneForSaving()
{
Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image);
if (!locker.successfullyLocked()) {
return 0;
}
return new KisDocument(*this);
}
bool KisDocument::exportDocumentSync(const QUrl &url, const QByteArray &mimeType, KisPropertiesConfigurationSP exportConfiguration)
{
Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image);
if (!locker.successfullyLocked()) {
return false;
}
d->savingImage = d->image;
const QString fileName = url.toLocalFile();
KisImportExportFilter::ConversionStatus status =
d->importExportManager->
exportDocument(fileName, fileName, mimeType, false, exportConfiguration);
d->savingImage = 0;
return status == KisImportExportFilter::OK;
}
bool KisDocument::initiateSavingInBackground(const QString actionName,
const QObject *receiverObject, const char *receiverMethod,
const KritaUtils::ExportFileJob &job,
KisPropertiesConfigurationSP exportConfiguration)
{
KIS_ASSERT_RECOVER_RETURN_VALUE(job.isValid(), false);
QScopedPointer clonedDocument(lockAndCloneForSaving());
// we block saving until the current saving is finished!
if (!clonedDocument || !d->savingMutex.tryLock()) {
return false;
}
KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveDocument, false);
KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveJob.isValid(), false);
d->backgroundSaveDocument.reset(clonedDocument.take());
d->backgroundSaveJob = job;
d->modifiedWhileSaving = false;
if (d->backgroundSaveJob.flags & KritaUtils::SaveInAutosaveMode) {
d->backgroundSaveDocument->d->isAutosaving = true;
}
connect(d->backgroundSaveDocument.data(),
SIGNAL(sigBackgroundSavingFinished(KisImportExportFilter::ConversionStatus, const QString&)),
this,
SLOT(slotChildCompletedSavingInBackground(KisImportExportFilter::ConversionStatus, const QString&)));
connect(this, SIGNAL(sigCompleteBackgroundSaving(KritaUtils::ExportFileJob,KisImportExportFilter::ConversionStatus,QString)),
receiverObject, receiverMethod, Qt::UniqueConnection);
bool started =
d->backgroundSaveDocument->startExportInBackground(actionName,
job.filePath,
job.filePath,
job.mimeType,
job.flags & KritaUtils::SaveShowWarnings,
exportConfiguration);
if (!started) {
d->backgroundSaveDocument.take()->deleteLater();
d->savingMutex.unlock();
d->backgroundSaveJob = KritaUtils::ExportFileJob();
}
return started;
}
void KisDocument::slotChildCompletedSavingInBackground(KisImportExportFilter::ConversionStatus status, const QString &errorMessage)
{
KIS_SAFE_ASSERT_RECOVER(!d->savingMutex.tryLock()) {
d->savingMutex.unlock();
return;
}
KIS_SAFE_ASSERT_RECOVER_RETURN(d->backgroundSaveDocument);
if (d->backgroundSaveJob.flags & KritaUtils::SaveInAutosaveMode) {
d->backgroundSaveDocument->d->isAutosaving = false;
}
d->backgroundSaveDocument.take()->deleteLater();
d->savingMutex.unlock();
KIS_SAFE_ASSERT_RECOVER_RETURN(d->backgroundSaveJob.isValid());
const KritaUtils::ExportFileJob job = d->backgroundSaveJob;
d->backgroundSaveJob = KritaUtils::ExportFileJob();
emit sigCompleteBackgroundSaving(job, status, errorMessage);
}
void KisDocument::slotAutoSave()
{
if (!d->modified || !d->modifiedAfterAutosave) return;
const QString autoSaveFileName = generateAutoSaveFileName(localFilePath());
emit statusBarMessage(i18n("Autosaving... %1", autoSaveFileName), successMessageTimeout);
bool started =
initiateSavingInBackground(i18n("Autosaving..."),
this, SLOT(slotCompleteAutoSaving(KritaUtils::ExportFileJob, KisImportExportFilter::ConversionStatus, const QString&)),
KritaUtils::ExportFileJob(autoSaveFileName, nativeFormatMimeType(), KritaUtils::SaveIsExporting | KritaUtils::SaveInAutosaveMode),
0);
if (!started) {
const int emergencyAutoSaveInterval = 10; // sec
setAutoSaveDelay(emergencyAutoSaveInterval);
} else {
d->modifiedAfterAutosave = false;
}
}
void KisDocument::slotCompleteAutoSaving(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage)
{
Q_UNUSED(job);
const QString fileName = QFileInfo(job.filePath).fileName();
if (status != KisImportExportFilter::OK) {
const int emergencyAutoSaveInterval = 10; // sec
setAutoSaveDelay(emergencyAutoSaveInterval);
emit statusBarMessage(i18nc("%1 --- failing file name, %2 --- error mesage",
"Error during autosaving %1: %2",
fileName,
exportErrorToUserMessage(status, errorMessage)), errorMessageTimeout);
} else {
KisConfig cfg;
d->autoSaveDelay = cfg.autoSaveInterval();
if (!d->modifiedWhileSaving) {
d->autoSaveTimer.stop(); // until the next change
} else {
setAutoSaveDelay(d->autoSaveDelay); // restart the timer
}
emit statusBarMessage(i18n("Finished autosaving %1", fileName), successMessageTimeout);
}
}
bool KisDocument::startExportInBackground(const QString &actionName,
const QString &location,
const QString &realLocation,
const QByteArray &mimeType,
bool showWarnings,
KisPropertiesConfigurationSP exportConfiguration)
{
d->savingImage = d->image;
KisMainWindow *window = KisPart::instance()->currentMainwindow();
if (window) {
if (window->viewManager()) {
d->savingUpdater = window->viewManager()->createThreadedUpdater(actionName);
d->importExportManager->setUpdater(d->savingUpdater);
}
}
+ KisImportExportFilter::ConversionStatus initializationStatus;
d->childSavingFuture =
d->importExportManager->exportDocumentAsyc(location,
realLocation,
mimeType,
+ initializationStatus,
showWarnings,
exportConfiguration);
- if (d->childSavingFuture.isCanceled()) {
+ if (initializationStatus != KisImportExportFilter::ConversionStatus::OK) {
if (d->savingUpdater) {
d->savingUpdater->cancel();
}
d->savingImage.clear();
- emit sigBackgroundSavingFinished(KisImportExportFilter::InternalError, this->errorMessage());
+ emit sigBackgroundSavingFinished(initializationStatus, this->errorMessage());
return false;
}
typedef QFutureWatcher StatusWatcher;
StatusWatcher *watcher = new StatusWatcher();
watcher->setFuture(d->childSavingFuture);
connect(watcher, SIGNAL(finished()), SLOT(finishExportInBackground()));
connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater()));
return true;
}
void KisDocument::finishExportInBackground()
{
KIS_SAFE_ASSERT_RECOVER(d->childSavingFuture.isFinished()) {
emit sigBackgroundSavingFinished(KisImportExportFilter::InternalError, "");
return;
}
KisImportExportFilter::ConversionStatus status =
d->childSavingFuture.result();
const QString errorMessage = this->errorMessage();
d->savingImage.clear();
d->childSavingFuture = QFuture();
d->lastErrorMessage.clear();
if (d->savingUpdater) {
d->savingUpdater->setProgress(100);
}
emit sigBackgroundSavingFinished(status, errorMessage);
}
void KisDocument::setReadWrite(bool readwrite)
{
d->readwrite = readwrite;
setAutoSaveDelay(d->autoSaveDelay);
Q_FOREACH (KisMainWindow *mainWindow, KisPart::instance()->mainWindows()) {
mainWindow->setReadWrite(readwrite);
}
}
void KisDocument::setAutoSaveDelay(int delay)
{
//qDebug() << "setting autosave delay from" << d->autoSaveDelay << "to" << delay;
d->autoSaveDelay = delay;
if (isReadWrite() && d->autoSaveDelay > 0) {
d->autoSaveTimer.start(d->autoSaveDelay * 1000);
}
else {
d->autoSaveTimer.stop();
}
}
KoDocumentInfo *KisDocument::documentInfo() const
{
return d->docInfo;
}
bool KisDocument::isModified() const
{
return d->modified;
}
QPixmap KisDocument::generatePreview(const QSize& size)
{
KisImageSP image = d->image;
if (d->savingImage) image = d->savingImage;
if (image) {
QRect bounds = image->bounds();
QSize newSize = bounds.size();
newSize.scale(size, Qt::KeepAspectRatio);
QPixmap px = QPixmap::fromImage(image->convertToQImage(newSize, 0));
if (px.size() == QSize(0,0)) {
px = QPixmap(newSize);
QPainter gc(&px);
QBrush checkBrush = QBrush(KisCanvasWidgetBase::createCheckersImage(newSize.width() / 5));
gc.fillRect(px.rect(), checkBrush);
gc.end();
}
return px;
}
return QPixmap(size);
}
QString KisDocument::generateAutoSaveFileName(const QString & path) const
{
QString retval;
// Using the extension allows to avoid relying on the mime magic when opening
const QString extension (".kra");
QRegularExpression autosavePattern("^\\..+-autosave.kra$");
QFileInfo fi(path);
QString dir = fi.absolutePath();
QString filename = fi.fileName();
if (path.isEmpty() || autosavePattern.match(filename).hasMatch()) {
// Never saved?
#ifdef Q_OS_WIN
// On Windows, use the temp location (https://bugs.kde.org/show_bug.cgi?id=314921)
retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::tempPath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension);
#else
// On Linux, use a temp file in $HOME then. Mark it with the pid so two instances don't overwrite each other's autosave file
retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::homePath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension);
#endif
} else {
retval = QString("%1%2.%3-autosave%4").arg(dir).arg(QDir::separator()).arg(filename).arg(extension);
}
//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(qApp);
kisApp->hideSplashScreen();
//dbgUI <<"asf=" << asf;
// ## TODO compare timestamps ?
int res = QMessageBox::warning(0,
i18nc("@title:window", "Krita"),
i18n("An autosaved file exists for this document.\nDo you want to open the autosaved file instead?"),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes);
switch (res) {
case QMessageBox::Yes :
url.setPath(asf);
autosaveOpened = true;
break;
case QMessageBox::No :
QFile::remove(asf);
break;
default: // Cancel
return false;
}
}
}
bool ret = openUrlInternal(url);
if (autosaveOpened || flags & RecoveryFile) {
setReadWrite(true); // enable save button
setModified(true);
setRecovered(true);
}
else {
if (!(flags & DontAddToRecent)) {
KisPart::instance()->addRecentURLToAllMainWindows(_url);
}
if (ret) {
// Detect readonly local-files; remote files are assumed to be writable
QFileInfo fi(url.toLocalFile());
setReadWrite(fi.isWritable());
}
setRecovered(false);
}
return ret;
}
class DlgLoadMessages : public KoDialog {
public:
DlgLoadMessages(const QString &title, const QString &message, const QStringList &warnings) {
setWindowTitle(title);
setWindowIcon(KisIconUtils::loadIcon("warning"));
QWidget *page = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout(page);
QHBoxLayout *hlayout = new QHBoxLayout();
QLabel *labelWarning= new QLabel();
labelWarning->setPixmap(KisIconUtils::loadIcon("warning").pixmap(32, 32));
hlayout->addWidget(labelWarning);
hlayout->addWidget(new QLabel(message));
layout->addLayout(hlayout);
QTextBrowser *browser = new QTextBrowser();
QString warning = "";
if (warnings.size() == 1) {
warning += " Reason:
";
}
else {
warning += " Reasons:
";
}
warning += "";
Q_FOREACH(const QString &w, warnings) {
warning += "\n- " + w + "
";
}
warning += "
";
browser->setHtml(warning);
browser->setMinimumHeight(200);
browser->setMinimumWidth(400);
layout->addWidget(browser);
setMainWidget(page);
setButtons(KoDialog::Ok);
resize(minimumSize());
}
};
bool KisDocument::openFile()
{
//dbgUI <<"for" << localFilePath();
if (!QFile::exists(localFilePath())) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("File %1 does not exist.", localFilePath()));
return false;
}
QString filename = localFilePath();
QString typeName = mimeType();
if (typeName.isEmpty()) {
typeName = KisMimeDatabase::mimeTypeForFile(filename);
}
//qDebug() << "mimetypes 4:" << typeName;
// Allow to open backup files, don't keep the mimetype application/x-trash.
if (typeName == "application/x-trash") {
QString path = filename;
while (path.length() > 0) {
path.chop(1);
typeName = KisMimeDatabase::mimeTypeForFile(path);
//qDebug() << "\t" << path << typeName;
if (!typeName.isEmpty()) {
break;
}
}
//qDebug() << "chopped" << filename << "to" << path << "Was trash, is" << typeName;
}
dbgUI << localFilePath() << "type:" << typeName;
KisMainWindow *window = KisPart::instance()->currentMainwindow();
if (window && window->viewManager()) {
KoUpdaterPtr updater = window->viewManager()->createUnthreadedUpdater(i18n("Opening document"));
d->importExportManager->setUpdater(updater);
}
KisImportExportFilter::ConversionStatus status;
status = d->importExportManager->importDocument(localFilePath(), typeName);
if (status != KisImportExportFilter::OK) {
QString msg = KisImportExportFilter::conversionStatusString(status);
if (!msg.isEmpty()) {
DlgLoadMessages dlg(i18nc("@title:window", "Krita"),
i18n("Could not open %2.\nReason: %1.", msg, prettyPathOrUrl()),
errorMessage().split("\n") + warningMessage().split("\n"));
dlg.exec();
}
return false;
}
else if (!warningMessage().isEmpty()) {
DlgLoadMessages dlg(i18nc("@title:window", "Krita"),
i18n("There were problems opening %1.", prettyPathOrUrl()),
warningMessage().split("\n"));
dlg.exec();
setUrl(QUrl());
}
setMimeTypeAfterLoading(typeName);
emit sigLoadingFinished();
undoStack()->clear();
return true;
}
// shared between openFile and koMainWindow's "create new empty document" code
void KisDocument::setMimeTypeAfterLoading(const QString& mimeType)
{
d->mimeType = mimeType.toLatin1();
d->outputMimeType = d->mimeType;
}
bool KisDocument::loadNativeFormat(const QString & file_)
{
return openUrl(QUrl::fromLocalFile(file_));
}
void KisDocument::setModified()
{
d->modified = true;
}
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="<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()
{
//qDebug() << "removeAutoSaveFiles";
// Eliminate any auto-save file
QString asf = generateAutoSaveFileName(localFilePath()); // the one in the current dir
//qDebug() << "\tfilename:" << asf << "exists:" << QFile::exists(asf);
if (QFile::exists(asf)) {
//qDebug() << "\tremoving autosavefile" << asf;
QFile::remove(asf);
}
asf = generateAutoSaveFileName(QString()); // and the one in $HOME
//qDebug() << "Autsavefile in $home" << asf;
if (QFile::exists(asf)) {
//qDebug() << "\tremoving autsavefile 2" << asf;
QFile::remove(asf);
}
}
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;
d->undoStack->setUndoLimit(cfg.undoStackLimit());
setAutoSaveDelay(cfg.autoSaveInterval());
}
void KisDocument::clearUndoHistory()
{
d->undoStack->clear();
}
KisGridConfig KisDocument::gridConfig() const
{
return d->gridConfig;
}
void KisDocument::setGridConfig(const KisGridConfig &config)
{
d->gridConfig = config;
}
const KisGuidesConfig& KisDocument::guidesConfig() const
{
return d->guidesConfig;
}
void KisDocument::setGuidesConfig(const KisGuidesConfig &data)
{
if (d->guidesConfig == data) return;
d->guidesConfig = data;
emit sigGuidesConfigChanged(d->guidesConfig);
}
void KisDocument::resetURL() {
setUrl(QUrl());
setLocalFilePath(QString());
}
KoDocumentInfoDlg *KisDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const
{
return new KoDocumentInfoDlg(parent, docInfo);
}
bool KisDocument::isReadWrite() const
{
return d->readwrite;
}
QUrl KisDocument::url() const
{
return d->m_url;
}
bool KisDocument::closeUrl(bool promptToSave)
{
if (promptToSave) {
if ( isReadWrite() && isModified()) {
Q_FOREACH (KisView *view, KisPart::instance()->views()) {
if (view && view->document() == this) {
if (!view->queryClose()) {
return false;
}
}
}
}
}
// Not modified => ok and delete temp file.
d->mimeType = QByteArray();
// It always succeeds for a read-only part,
// but the return value exists for reimplementations
// (e.g. pressing cancel for a modified read-write part)
return true;
}
void KisDocument::setUrl(const QUrl &url)
{
d->m_url = url;
}
QString KisDocument::localFilePath() const
{
return d->m_file;
}
void KisDocument::setLocalFilePath( const QString &localFilePath )
{
d->m_file = localFilePath;
}
bool KisDocument::openUrlInternal(const QUrl &url)
{
if ( !url.isValid() )
return false;
if (d->m_bAutoDetectedMime) {
d->mimeType = QByteArray();
d->m_bAutoDetectedMime = false;
}
QByteArray mimetype = d->mimeType;
if ( !closeUrl() )
return false;
d->mimeType = mimetype;
setUrl(url);
d->m_file.clear();
if (d->m_url.isLocalFile()) {
d->m_file = d->m_url.toLocalFile();
bool ret;
// set the mimetype only if it was not already set (for example, by the host application)
if (d->mimeType.isEmpty()) {
// get the mimetype of the file
// using findByUrl() to avoid another string -> url conversion
QString mime = KisMimeDatabase::mimeTypeForFile(d->m_url.toLocalFile());
d->mimeType = mime.toLocal8Bit();
d->m_bAutoDetectedMime = true;
}
setUrl(d->m_url);
ret = openFile();
if (ret) {
emit completed();
} else {
emit canceled(QString());
}
return ret;
}
return false;
}
bool KisDocument::newImage(const QString& name,
qint32 width, qint32 height,
const KoColorSpace* cs,
const KoColor &bgColor, bool backgroundAsLayer,
int numberOfLayers,
const QString &description, const double imageResolution)
{
Q_ASSERT(cs);
KisConfig cfg;
KisImageSP image;
KisPaintLayerSP layer;
if (!cs) return false;
QApplication::setOverrideCursor(Qt::BusyCursor);
image = new KisImage(createUndoStore(), width, height, cs, name);
Q_CHECK_PTR(image);
connect(image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection);
image->setResolution(imageResolution, imageResolution);
image->assignImageProfile(cs->profile());
documentInfo()->setAboutInfo("title", name);
if (name != i18n("Unnamed") && !name.isEmpty()) {
setUrl(QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation) + '/' + name + ".kra"));
}
documentInfo()->setAboutInfo("abstract", description);
layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, cs);
Q_CHECK_PTR(layer);
if (backgroundAsLayer) {
image->setDefaultProjectionColor(KoColor(cs));
if (bgColor.opacityU8() == OPACITY_OPAQUE_U8) {
layer->paintDevice()->setDefaultPixel(bgColor);
} else {
// Hack: with a semi-transparent background color, the projection isn't composited right if we just set the default pixel
KisFillPainter painter;
painter.begin(layer->paintDevice());
painter.fillRect(0, 0, width, height, bgColor, bgColor.opacityU8());
}
} else {
image->setDefaultProjectionColor(bgColor);
}
layer->setDirty(QRect(0, 0, width, height));
image->addNode(layer.data(), image->rootLayer().data());
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));
}
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());
QApplication::restoreOverrideCursor();
return true;
}
bool KisDocument::isSaving() const
{
const bool result = d->savingMutex.tryLock();
if (result) {
d->savingMutex.unlock();
}
return !result;
}
void KisDocument::waitForSavingToComplete()
{
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);
}
KoShapeBasedDocumentBase *KisDocument::shapeController() const
{
return d->shapeController;
}
KoShapeLayer* KisDocument::shapeForNode(KisNodeSP layer) const
{
return d->shapeController->shapeForNode(layer);
}
QList KisDocument::assistants() const
{
return d->assistants;
}
void KisDocument::setAssistants(const QList value)
{
d->assistants = value;
}
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)
{
if (d->image) {
// Disconnect existing sig/slot connections
d->image->disconnect(this);
d->shapeController->setImage(0);
d->image = 0;
}
if (!image) return;
d->setImageAndInitIdleWatcher(image);
d->shapeController->setImage(image);
setModified(false);
connect(d->image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection);
d->image->initialRefreshGraph();
}
void KisDocument::setImageModified()
{
setModified(true);
}
KisUndoStore* KisDocument::createUndoStore()
{
return new KisDocumentUndoStore(this);
}
bool KisDocument::isAutosaving() const
{
return d->isAutosaving;
}
QString KisDocument::exportErrorToUserMessage(KisImportExportFilter::ConversionStatus status, const QString &errorMessage)
{
return errorMessage.isEmpty() ? KisImportExportFilter::conversionStatusString(status) : errorMessage;
}
diff --git a/libs/ui/KisImportExportManager.cpp b/libs/ui/KisImportExportManager.cpp
index 2279ee8b03..820b290c24 100644
--- a/libs/ui/KisImportExportManager.cpp
+++ b/libs/ui/KisImportExportManager.cpp
@@ -1,618 +1,623 @@
/*
* Copyright (C) 2016 Boudewijn Rempt
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisImportExportManager.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_config.h"
#include "KisImportExportFilter.h"
#include "KisDocument.h"
#include
#include
#include "kis_painter.h"
#include "kis_guides_config.h"
#include "kis_grid_config.h"
#include "kis_popup_button.h"
#include
#include "kis_async_action_feedback.h"
// static cache for import and export mimetypes
QStringList KisImportExportManager::m_importMimeTypes;
QStringList KisImportExportManager::m_exportMimeTypes;
class Q_DECL_HIDDEN KisImportExportManager::Private
{
public:
bool batchMode {false};
KoUpdaterPtr updater;
QString cachedExportFilterMimeType;
QSharedPointer cachedExportFilter;
};
struct KisImportExportManager::ConversionResult {
ConversionResult()
{
}
ConversionResult(const QFuture &futureStatus)
: m_isAsync(true),
m_futureStatus(futureStatus)
{
}
ConversionResult(KisImportExportFilter::ConversionStatus status)
: m_isAsync(false),
m_status(status)
{
}
bool isAsync() const {
return m_isAsync;
}
QFuture futureStatus() const {
// if the result is not async, then it means some failure happened,
// just return a cancelled future
KIS_SAFE_ASSERT_RECOVER_NOOP(m_isAsync || m_status != KisImportExportFilter::OK);
return m_futureStatus;
}
KisImportExportFilter::ConversionStatus status() const {
return m_status;
}
+ void setStatus(KisImportExportFilter::ConversionStatus value) {
+ m_status = value;
+ }
private:
bool m_isAsync = false;
QFuture m_futureStatus;
KisImportExportFilter::ConversionStatus m_status = KisImportExportFilter::UsageError;
};
KisImportExportManager::KisImportExportManager(KisDocument* document)
: m_document(document)
, d(new Private)
{
}
KisImportExportManager::~KisImportExportManager()
{
delete d;
}
KisImportExportFilter::ConversionStatus KisImportExportManager::importDocument(const QString& location, const QString& mimeType)
{
ConversionResult result = convert(Import, location, location, mimeType, false, 0, false);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!result.isAsync(), KisImportExportFilter::UsageError);
return result.status();
}
KisImportExportFilter::ConversionStatus KisImportExportManager::exportDocument(const QString& location, const QString& realLocation, const QByteArray& mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
{
ConversionResult result = convert(Export, location, realLocation, mimeType, showWarnings, exportConfiguration, false);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!result.isAsync(), KisImportExportFilter::UsageError);
return result.status();
}
-QFuture KisImportExportManager::exportDocumentAsyc(const QString &location, const QString &realLocation, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
+QFuture KisImportExportManager::exportDocumentAsyc(const QString &location, const QString &realLocation, const QByteArray &mimeType, KisImportExportFilter::ConversionStatus &status, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
{
ConversionResult result = convert(Export, location, realLocation, mimeType, showWarnings, exportConfiguration, true);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(result.isAsync() ||
result.status() != KisImportExportFilter::OK, QFuture());
+ 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::mimeFilter(Direction direction)
{
// Find the right mimetype by the extension
QSet mimeTypes;
// mimeTypes << KisDocument::nativeFormatMimeType() << "application/x-krita-paintoppreset" << "image/openraster";
if (direction == KisImportExportManager::Import) {
if (m_importMimeTypes.isEmpty()) {
QListlist = KoJsonTrader::instance()->query("Krita/FileFilter", "");
Q_FOREACH(QPluginLoader *loader, list) {
QJsonObject json = loader->metaData().value("MetaData").toObject();
Q_FOREACH(const QString &mimetype, json.value("X-KDE-Import").toString().split(",", QString::SkipEmptyParts)) {
//qDebug() << "Adding import mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader;
mimeTypes << mimetype;
}
}
qDeleteAll(list);
m_importMimeTypes = mimeTypes.toList();
}
return m_importMimeTypes;
}
else if (direction == KisImportExportManager::Export) {
if (m_exportMimeTypes.isEmpty()) {
QListlist = KoJsonTrader::instance()->query("Krita/FileFilter", "");
Q_FOREACH(QPluginLoader *loader, list) {
QJsonObject json = loader->metaData().value("MetaData").toObject();
Q_FOREACH(const QString &mimetype, json.value("X-KDE-Export").toString().split(",", QString::SkipEmptyParts)) {
//qDebug() << "Adding export mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader;
mimeTypes << mimetype;
}
}
qDeleteAll(list);
m_exportMimeTypes = mimeTypes.toList();
}
return m_exportMimeTypes;
}
return QStringList();
}
KisImportExportFilter *KisImportExportManager::filterForMimeType(const QString &mimetype, KisImportExportManager::Direction direction)
{
int weight = -1;
KisImportExportFilter *filter = 0;
QListlist = 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(loader->instance());
if (!factory) {
warnUI << loader->errorString();
continue;
}
QObject* obj = factory->create(0);
if (!obj || !obj->inherits("KisImportExportFilter")) {
delete obj;
continue;
}
KisImportExportFilter *f = qobject_cast(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;
}
void KisImportExportManager::setBatchMode(const bool batch)
{
d->batchMode = batch;
}
bool KisImportExportManager::batchMode(void) const
{
return d->batchMode;
}
void KisImportExportManager::setUpdater(KoUpdaterPtr updater)
{
d->updater = updater;
}
QString KisImportExportManager::askForAudioFileName(const QString &defaultDir, QWidget *parent)
{
KoFileDialog dialog(parent, KoFileDialog::ImportFiles, "ImportAudio");
if (!defaultDir.isEmpty()) {
dialog.setDefaultDir(defaultDir);
}
QStringList mimeTypes;
mimeTypes << "audio/mpeg";
mimeTypes << "audio/ogg";
mimeTypes << "audio/vorbis";
mimeTypes << "audio/vnd.wave";
mimeTypes << "audio/flac";
dialog.setMimeTypeFilters(mimeTypes);
dialog.setCaption(i18nc("@titile:window", "Open Audio"));
return dialog.filename();
}
KisImportExportManager::ConversionResult KisImportExportManager::convert(KisImportExportManager::Direction direction, const QString &location, const QString& realLocation, const QString &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration, bool isAsync)
{
// export configuration is supported for export only
KIS_SAFE_ASSERT_RECOVER_NOOP(direction == Export || !bool(exportConfiguration));
QString typeName = mimeType;
if (typeName.isEmpty()) {
typeName = KisMimeDatabase::mimeTypeForFile(location, direction == KisImportExportManager::Export ? false : true);
}
QSharedPointer filter;
/**
* Fetching a filter from the registry is a really expensive operation,
* because it blocks all the threads. Cache the filter if possible.
*/
if (direction == KisImportExportManager::Export &&
d->cachedExportFilter &&
d->cachedExportFilterMimeType == typeName) {
filter = d->cachedExportFilter;
} else {
filter = toQShared(filterForMimeType(typeName, direction));
if (direction == Export) {
d->cachedExportFilter = filter;
d->cachedExportFilterMimeType = typeName;
}
}
if (!filter) {
return KisImportExportFilter::FilterCreationError;
}
filter->setFilename(location);
filter->setRealFilename(realLocation);
filter->setBatchMode(batchMode());
filter->setMimeType(typeName);
if (!d->updater.isNull()) {
// WARNING: The updater is not guaranteed to be persistent! If you ever want
// to add progress reporting to "Save also as .kra", make sure you create
// a separate KoProgressUpdater for that!
// WARNING2: the failsafe completion of the updater happens in the destructor
// the filter.
filter->setUpdater(d->updater);
}
QByteArray from, to;
if (direction == Export) {
from = m_document->nativeFormatMimeType();
to = typeName.toLatin1();
}
else {
from = typeName.toLatin1();
to = m_document->nativeFormatMimeType();
}
KIS_ASSERT_RECOVER_RETURN_VALUE(
direction == Import || direction == Export,
KisImportExportFilter::BadConversionGraph);
ConversionResult result = KisImportExportFilter::OK;
if (direction == Import) {
// async importing is not yet supported!
KIS_SAFE_ASSERT_RECOVER_NOOP(!isAsync);
if (0 && !batchMode()) {
KisAsyncActionFeedback f(i18n("Opening document..."), 0);
result = f.runAction(std::bind(&KisImportExportManager::doImport, this, location, filter));
} else {
result = doImport(location, filter);
}
}
else /* if (direction == Export) */ {
if (!exportConfiguration) {
exportConfiguration = filter->lastSavedConfiguration(from, to);
}
if (exportConfiguration) {
fillStaticExportConfigurationProperties(exportConfiguration);
}
bool alsoAsKra = false;
if (!batchMode() && !askUserAboutExportConfiguration(filter, exportConfiguration,
from, to,
batchMode(), showWarnings,
&alsoAsKra)) {
return KisImportExportFilter::UserCancelled;
}
if (isAsync) {
result = QtConcurrent::run(std::bind(&KisImportExportManager::doExport, this, location, filter, exportConfiguration, alsoAsKra));
} 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) {
KisConfig().setExportConfiguration(typeName, exportConfiguration);
}
}
+ result.setStatus(KisImportExportFilter::OK);
return result;
}
void KisImportExportManager::fillStaticExportConfigurationProperties(KisPropertiesConfigurationSP exportConfiguration)
{
// Fill with some meta information about the image
KisImageSP image = m_document->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);
}
bool
KisImportExportManager::askUserAboutExportConfiguration(
QSharedPointer filter,
KisPropertiesConfigurationSP exportConfiguration,
const QByteArray &from,
const QByteArray &to,
const bool batchMode, const bool showWarnings,
bool *alsoAsKra)
{
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 = filter->createConfigurationWidget(0, from, to);
// Extra checks that cannot be done by the checker, because the checker only has access to the image.
if (!m_document->assistants().isEmpty() && to != m_document->nativeFormatMimeType()) {
warnings.append(i18nc("image conversion warning", "The image contains assistants. The assistants will not be saved."));
}
if (m_document->guidesConfig().hasGuides() && to != m_document->nativeFormatMimeType()) {
warnings.append(i18nc("image conversion warning", "The image contains guides. 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 custom grid configuration. The configuration will not be saved."));
}
if (!batchMode && !errors.isEmpty()) {
QString error = ""
+ i18n("Error: cannot save this image as a %1.", mimeUserDescription)
+ " Reasons:
"
+ "";
Q_FOREACH(const QString &w, errors) {
error += "\n- " + w + "
";
}
error += "
";
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 = ""
+ i18n("You will lose information when saving this image as a %1.", mimeUserDescription);
if (warnings.size() == 1) {
warning += " Reason:
";
}
else {
warning += " Reasons:
";
}
warning += "";
Q_FOREACH(const QString &w, warnings) {
warning += "\n- " + w + "
";
}
warning += "
";
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().readEntry("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().writeEntry("AlsoSaveAsKra", chkAlsoAsKra->isChecked());
*alsoAsKra = chkAlsoAsKra->isChecked();
}
if (wdg) {
*exportConfiguration = *wdg->configuration();
}
}
return true;
}
KisImportExportFilter::ConversionStatus KisImportExportManager::doImport(const QString &location, QSharedPointer filter)
{
QFile file(location);
if (!file.exists()) {
return KisImportExportFilter::FileNotFound;
}
if (filter->supportsIO() && !file.open(QFile::ReadOnly)) {
return KisImportExportFilter::FileNotFound;
}
KisImportExportFilter::ConversionStatus status =
filter->convert(m_document, &file, KisPropertiesConfigurationSP());
if (file.isOpen()) {
file.close();
}
return status;
}
KisImportExportFilter::ConversionStatus KisImportExportManager::doExport(const QString &location, QSharedPointer filter, KisPropertiesConfigurationSP exportConfiguration, bool alsoAsKra)
{
KisImportExportFilter::ConversionStatus status =
doExportImpl(location, filter, exportConfiguration);
if (alsoAsKra && status == KisImportExportFilter::OK) {
QString kraLocation = location + ".kra";
QByteArray mime = m_document->nativeFormatMimeType();
QSharedPointer filter(
filterForMimeType(QString::fromLatin1(mime), Export));
KIS_SAFE_ASSERT_RECOVER_NOOP(filter);
if (filter) {
filter->setFilename(kraLocation);
KisPropertiesConfigurationSP kraExportConfiguration =
filter->lastSavedConfiguration(mime, mime);
status = doExportImpl(kraLocation, filter, kraExportConfiguration);
} else {
status = KisImportExportFilter::FilterCreationError;
}
}
return status;
}
KisImportExportFilter::ConversionStatus KisImportExportManager::doExportImpl(const QString &location, QSharedPointer filter, KisPropertiesConfigurationSP exportConfiguration)
{
QSaveFile file(location);
file.setDirectWriteFallback(true);
if (filter->supportsIO() && !file.open(QFile::WriteOnly)) {
file.cancelWriting();
return KisImportExportFilter::CreationError;
}
KisImportExportFilter::ConversionStatus status =
filter->convert(m_document, &file, exportConfiguration);
if (status != KisImportExportFilter::OK) {
file.cancelWriting();
} else {
file.commit();
}
return status;
}
#include
diff --git a/libs/ui/KisImportExportManager.h b/libs/ui/KisImportExportManager.h
index 600ded4ab2..275b57bbce 100644
--- a/libs/ui/KisImportExportManager.h
+++ b/libs/ui/KisImportExportManager.h
@@ -1,161 +1,161 @@
/*
* Copyright (C) 2016 Boudewijn Rempt
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KIS_IMPORT_EXPORT_MANAGER_H
#define KIS_IMPORT_EXPORT_MANAGER_H
#include
#include
#include
#include
#include "KisImportExportFilter.h"
#include "kritaui_export.h"
class KisDocument;
class KoProgressUpdater;
template
class QFuture;
/**
* @brief The class managing all the filters.
*
* This class manages all filters for a %Calligra application. Normally
* you will not have to use it, since KisMainWindow takes care of loading
* and saving documents.
*
* @ref KisFilter
*
* @author Kalle Dalheimer
* @author Torben Weis
* @author Werner Trobin
*/
class KRITAUI_EXPORT KisImportExportManager : public QObject
{
Q_OBJECT
public:
/**
* This enum is used to distinguish the import/export cases
*/
enum Direction { Import = 1, Export = 2 };
/**
* Create a filter manager for a document
*/
explicit KisImportExportManager(KisDocument *document);
public:
~KisImportExportManager() override;
/**
* Imports the specified document and returns the resultant filename
* (most likely some file in /tmp).
* @p path can be either a URL or a filename.
* @p documentMimeType gives importDocument a hint about what type
* the document may be. It can be left empty.
*
* @return status signals the success/error of the conversion.
* If the QString which is returned isEmpty() and the status is OK,
* then we imported the file directly into the document.
*/
KisImportExportFilter::ConversionStatus importDocument(const QString &location, const QString &mimeType);
/**
* @brief Exports the given file/document to the specified URL/mimetype.
*
* If @p mimeType is empty, then the closest matching Calligra part is searched
* and when the method returns @p mimeType contains this mimetype.
* Oh, well, export is a C++ keyword ;)
*/
KisImportExportFilter::ConversionStatus exportDocument(const QString &location, const QString& realLocation, const QByteArray &mimeType, bool showWarnings = true, KisPropertiesConfigurationSP exportConfiguration = 0);
- QFuture exportDocumentAsyc(const QString &location, const QString& realLocation, const QByteArray &mimeType, bool showWarnings = true, KisPropertiesConfigurationSP exportConfiguration = 0);
+ QFuture exportDocumentAsyc(const QString &location, const QString& realLocation, const QByteArray &mimeType, KisImportExportFilter::ConversionStatus &status, bool showWarnings = true, KisPropertiesConfigurationSP exportConfiguration = 0);
///@name Static API
//@{
/**
* Suitable for passing to KoFileDialog::setMimeTypeFilters. The default mime
* gets set by the "users" of this method, as we do not have enough
* information here.
* Optionally, @p extraNativeMimeTypes are added after the native mimetype.
*/
static QStringList mimeFilter(Direction direction);
/**
* @brief filterForMimeType loads the relevant import/export plugin and returns it. The caller
* is responsible for deleting it!
* @param mimetype the mimetype we want to import/export. If there's more than one plugin, the one
* with the highest weight as defined in the json description will be taken
* @param direction import or export
* @return a pointer to the filter plugin or 0 if none could be found
*/
static KisImportExportFilter *filterForMimeType(const QString &mimetype, Direction direction);
/**
* Set the filter manager is batch mode (no dialog shown)
* instead of the interactive mode (dialog shown)
*/
void setBatchMode(const bool batch);
/**
* Get if the filter manager is batch mode (true)
* or in interactive mode (true)
*/
bool batchMode(void) const;
void setUpdater(KoUpdaterPtr updater);
static QString askForAudioFileName(const QString &defaultDir, QWidget *parent);
private Q_SLOTS:
private:
struct ConversionResult;
ConversionResult convert(Direction direction, const QString &location, const QString& realLocation, const QString &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration, bool isAsync);
void fillStaticExportConfigurationProperties(KisPropertiesConfigurationSP exportConfiguration);
bool askUserAboutExportConfiguration(QSharedPointer filter, KisPropertiesConfigurationSP exportConfiguration, const QByteArray &from, const QByteArray &to, bool batchMode, const bool showWarnings, bool *alsoAsKra);
KisImportExportFilter::ConversionStatus doImport(const QString &location, QSharedPointer filter);
KisImportExportFilter::ConversionStatus doExport(const QString &location, QSharedPointer filter, KisPropertiesConfigurationSP exportConfiguration, bool alsoAsKra);
KisImportExportFilter::ConversionStatus doExportImpl(const QString &location, QSharedPointer filter, KisPropertiesConfigurationSP exportConfiguration);
// Private API
KisImportExportManager(const KisImportExportManager& rhs);
KisImportExportManager &operator=(const KisImportExportManager& rhs);
KisDocument *m_document;
/// A static cache for the availability checks of filters
static QStringList m_importMimeTypes;
static QStringList m_exportMimeTypes;
class Private;
Private * const d;
};
#endif // __KO_FILTER_MANAGER_H__
diff --git a/libs/ui/KisViewManager.cpp b/libs/ui/KisViewManager.cpp
index 003ea429d3..2b0b8add6c 100644
--- a/libs/ui/KisViewManager.cpp
+++ b/libs/ui/KisViewManager.cpp
@@ -1,1378 +1,1379 @@
/*
* This file is part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 1999 Matthias Elter
* 1999 Michael Koch
* 1999 Carsten Pfeiffer
* 2002 Patrick Julien
* 2003-2011 Boudewijn Rempt
* 2004 Clarence Dang
* 2011 José Luis Vergara
* 2017 L. E. Segovia
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include
#include "KisViewManager.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "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
#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
#include
#include "kis_image_manager.h"
#include
#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 "kis_painting_assistants_manager.h"
#include
#include "kis_paintop_box.h"
#include
#include "KisPart.h"
#include "KisPrintJob.h"
#include
#include "kis_resource_server_provider.h"
#include "kis_selection.h"
#include "kis_selection_manager.h"
#include "kis_shape_controller.h"
#include "kis_shape_layer.h"
#include
#include "kis_statusbar.h"
#include
#include
#include "kis_tooltip_manager.h"
#include
#include "KisView.h"
#include "kis_zoom_manager.h"
#include "widgets/kis_floating_message.h"
#include "kis_signal_auto_connection.h"
#include "kis_script_manager.h"
#include "kis_icon_utils.h"
#include "kis_guides_manager.h"
#include "kis_derived_resources.h"
#include "dialogs/kis_delayed_save_dialog.h"
#include
class BlockingUserInputEventFilter : public QObject
{
bool eventFilter(QObject *watched, QEvent *event) override
{
Q_UNUSED(watched);
if(dynamic_cast(event)
|| dynamic_cast(event)
|| dynamic_cast(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)
, scriptManager(_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;
KisSelectionManager selectionManager;
KisGuidesManager guidesManager;
KisStatusBar statusBar;
QPointer persistentImageProgressUpdater;
QScopedPointer persistentUnthreadedProgressUpdaterRouter;
QPointer persistentUnthreadedProgressUpdater;
KisControlFrame controlFrame;
KisNodeManager nodeManager;
KisImageManager imageManager;
KisGridManager gridManager;
KisCanvasControlsManager canvasControlsManager;
KisPaintingAssistantsManager paintingAssistantsManager;
BlockingUserInputEventFilter blockingEventFilter;
KisActionManager actionManager;
QMainWindow* mainWindow;
QPointer savedFloatingMessage;
bool showFloatingMessage;
QPointer currentImageView;
KisCanvasResourceProvider canvasResourceProvider;
KoCanvasResourceManager canvasResourceManager;
KisSignalCompressor guiUpdateCompressor;
KActionCollection *actionCollection;
KisMirrorManager mirrorManager;
KisInputManager inputManager;
KisSignalAutoConnectionsStore viewConnections;
KisScriptManager scriptManager;
KSelectAction *actionAuthor; // Select action for author profile.
KisAction *showPixelGrid;
QByteArray canvasState;
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
QFlags 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(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 dummy(new KoDummyCanvasController(actionCollection()));
KoToolManager::instance()->registerToolActions(actionCollection(), dummy.data());
QTimer::singleShot(0, this, SLOT(initializeStatusBarVisibility()));
connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)),
d->controlFrame.paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice)));
connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)),
d->controlFrame.paintopBox(), SLOT(slotToolChanged(KoCanvasController*,int)));
connect(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)),
resourceProvider(), SLOT(slotNodeActivated(KisNodeSP)));
connect(resourceProvider()->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
d->controlFrame.paintopBox(), SLOT(slotCanvasResourceChanged(int,QVariant)));
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()));
KisInputProfileManager::instance()->loadProfiles();
KisConfig cfg;
d->showFloatingMessage = cfg.showCanvasMessages();
}
KisViewManager::~KisViewManager()
{
KisConfig cfg;
if (resourceProvider() && resourceProvider()->currentPreset()) {
cfg.writeEntry("LastPreset", resourceProvider()->currentPreset()->name());
cfg.writeKoColor("LastForeGroundColor",resourceProvider()->fgColor());
cfg.writeKoColor("LastBackGroundColor",resourceProvider()->bgColor());
}
cfg.writeEntry("baseLength", KoResourceItemChooserSync::instance()->baseLength());
delete d;
}
void KisViewManager::initializeResourceManager(KoCanvasResourceManager *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 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();
}
}
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 imageView = qobject_cast(view);
d->currentImageView = imageView;
if (imageView) {
d->softProof->setChecked(imageView->softProofing());
d->gamutCheck->setChecked(imageView->gamutCheck());
// Wait for the async image to have loaded
KisDocument* doc = view->document();
// connect(canvasController()->proxyObject, SIGNAL(documentMousePositionChanged(QPointF)), d->statusBar, SLOT(documentMousePositionChanged(QPointF)));
// Restore the last used brush preset, color and background color.
if (first) {
KisConfig cfg;
KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
QString lastPreset = cfg.readEntry("LastPreset", QString("Basic_tip_default"));
KisPaintOpPresetSP preset = rserver->resourceByName(lastPreset);
if (!preset) {
preset = rserver->resourceByName("Basic_tip_default");
}
if (!preset) {
preset = rserver->resources().first();
}
if (preset) {
paintOpBox()->restoreResource(preset.data());
}
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));
}
KisCanvasController *canvasController = dynamic_cast(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(const QPointF&, const QPointF&)),
resourceProvider(), SLOT(slotImageSizeChanged()));
d->viewConnections.addUniqueConnection(
image(), SIGNAL(sigResolutionChanged(double,double)),
resourceProvider(), SLOT(slotOnScreenResolutionChanged()));
d->viewConnections.addUniqueConnection(
image(), SIGNAL(sigNodeChanged(KisNodeSP)),
this, SLOT(updateGUI()));
d->viewConnections.addUniqueConnection(
d->currentImageView->zoomManager()->zoomController(),
SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)),
resourceProvider(), SLOT(slotOnScreenResolutionChanged()));
}
d->actionManager.updateGUI();
resourceProvider()->slotImageSizeChanged();
resourceProvider()->slotOnScreenResolutionChanged();
Q_EMIT viewChanged();
}
KoZoomController *KisViewManager::zoomController() const
{
if (d->currentImageView) {
return d->currentImageView->zoomController();
}
return 0;
}
KisImageWSP KisViewManager::image() const
{
if (document()) {
return document()->image();
}
return 0;
}
KisCanvasResourceProvider * KisViewManager::resourceProvider()
{
return &d->canvasResourceProvider;
}
KisCanvas2 * KisViewManager::canvasBase() const
{
if (d && d->currentImageView) {
return d->currentImageView->canvasBase();
}
return 0;
}
QWidget* KisViewManager::canvas() const
{
if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) {
return d->currentImageView->canvasBase()->canvasWidget();
}
return 0;
}
KisStatusBar * KisViewManager::statusBar() const
{
return &d->statusBar;
}
void KisViewManager::addStatusBarItem(QWidget *widget, int stretch, bool permanent)
{
d->statusBar.addStatusBarItem(widget, stretch, permanent);
}
void KisViewManager::removeStatusBarItem(QWidget *widget)
{
d->statusBar.removeStatusBarItem(widget);
}
KisPaintopBox* KisViewManager::paintOpBox() const
{
return d->controlFrame.paintopBox();
}
QPointer KisViewManager::createUnthreadedUpdater(const QString &name)
{
return d->persistentUnthreadedProgressUpdaterRouter->startSubtask(1, name, false);
}
QPointer 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) {
KoProperties properties;
QList masks = layer->childNodes(QStringList("KisSelectionMask"), properties);
if (masks.size() == 1) {
return masks[0]->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;
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(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(const QString &)), this, SLOT(changeAuthorProfile(const QString &)));
actionCollection()->addAction("settings_active_author", d->actionAuthor);
slotUpdateAuthorProfileActions();
d->showPixelGrid = actionManager()->createAction("view_pixel_grid");
d->showPixelGrid->setChecked(cfg.pixelGridEnabled());
}
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());
d->scriptManager.setup(actionCollection(), actionManager());
}
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;
}
KisScriptManager *KisViewManager::scriptManager() const
{
return &d->scriptManager;
}
int KisViewManager::viewCount() const
{
KisMainWindow *mw = qobject_cast(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(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(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;
bool foundVersion;
bool fileAlreadyExists;
bool isBackup;
QString version = "000";
QString newVersion;
QString letter;
QString fileName = document()->localFilePath();
// Find current version filenames
// v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well
// Considering our incremental version and backup scheme, format is filename_001~001.ext
QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
regex.indexIn(fileName); // Perform the search
QStringList matches = regex.capturedTexts();
foundVersion = matches.at(0).isEmpty() ? false : true;
// Ensure compatibility with Save Incremental Backup
// If this regex is not kept separate, the entire algorithm needs modification;
// It's simpler to just add this.
QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
regexAux.indexIn(fileName); // Perform the search
QStringList matchesAux = regexAux.capturedTexts();
isBackup = matchesAux.at(0).isEmpty() ? false : true;
// If the filename has a version, prepare it for incrementation
if (foundVersion) {
version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches
if (version.contains(QRegExp("[a-z]"))) {
version.chop(1); // Trim "."
letter = version.right(1); // Save letter
version.chop(1); // Trim letter
} else {
version.chop(1); // Trim "."
}
version.remove(0, 1); // Trim "_"
} else {
// TODO: this will not work with files extensions like jp2
// ...else, simply add a version to it so the next loop works
QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension
regex2.indexIn(fileName);
QStringList matches2 = regex2.capturedTexts();
QString extensionPlusVersion = matches2.at(0);
extensionPlusVersion.prepend(version);
extensionPlusVersion.prepend("_");
fileName.replace(regex2, extensionPlusVersion);
}
// Prepare the base for new version filename
int intVersion = version.toInt(0);
++intVersion;
QString baseNewVersion = QString::number(intVersion);
while (baseNewVersion.length() < version.length()) {
baseNewVersion.prepend("0");
}
// Check if the file exists under the new name and search until options are exhausted (test appending a to z)
do {
newVersion = baseNewVersion;
newVersion.prepend("_");
if (!letter.isNull()) newVersion.append(letter);
if (isBackup) {
newVersion.append("~");
} else {
newVersion.append(".");
}
fileName.replace(regex, newVersion);
fileAlreadyExists = QFile(fileName).exists();
if (fileAlreadyExists) {
if (!letter.isNull()) {
char letterCh = letter.at(0).toLatin1();
++letterCh;
letter = QString(QChar(letterCh));
} else {
letter = 'a';
}
}
} while (fileAlreadyExists && letter != "{"); // x, y, z, {...
if (letter == "{") {
QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number"));
return;
}
document()->setFileBatchMode(true);
document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true);
document()->setFileBatchMode(false);
if (mainWindow()) {
mainWindow()->updateCaption();
}
}
void KisViewManager::slotSaveIncrementalBackup()
{
if (!document()) return;
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;
cfg.setShowStatusBar(toggled);
}
}
void KisViewManager::switchCanvasOnly(bool toggled)
{
KisConfig cfg;
KisMainWindow* main = mainWindow();
if(!main) {
dbgUI << "Unable to switch to canvas-only mode, main window not found";
return;
}
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(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 toolBars = main->findChildren();
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 dockers = mainWindow()->dockWidgets();
Q_FOREACH (QDockWidget* dock, dockers) {
dbgKrita << "name " << dock->objectName();
KoDockWidgetTitleBar* titlebar = dynamic_cast(dock->titleBarWidget());
if (titlebar) {
titlebar->updateIcons();
}
QObjectList objects;
objects.append(dock);
while (!objects.isEmpty()) {
QObject* object = objects.takeFirst();
objects.append(object->children());
KisIconUtils::updateIconCommon(object);
}
}
}
}
void KisViewManager::initializeStatusBarVisibility()
{
KisConfig cfg;
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->showFloatingMessageImpl(message, icon, timeout, priority, alignment);
emit floatingMessageRequested(message, icon.name());
}
KisMainWindow *KisViewManager::mainWindow() const
{
return qobject_cast(d->mainWindow);
}
void KisViewManager::showHideScrollbars()
{
if (!d->currentImageView) return;
if (!d->currentImageView->canvasController()) return;
KisConfig cfg;
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;
cfg.setShowRulers(value);
}
void KisViewManager::slotSaveRulersTrackMouseState(bool value)
{
KisConfig cfg;
cfg.setRulersTrackMouse(value);
}
void KisViewManager::setShowFloatingMessage(bool show)
{
d->showFloatingMessage = show;
}
void KisViewManager::changeAuthorProfile(const QString &profileName)
{
KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "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(KoGlobal::calligraConfig(), "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(KoGlobal::calligraConfig(), "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);
}
}
diff --git a/libs/ui/KisViewManager.h b/libs/ui/KisViewManager.h
index 20b46b512e..b712e9b1b7 100644
--- a/libs/ui/KisViewManager.h
+++ b/libs/ui/KisViewManager.h
@@ -1,272 +1,273 @@
/*
* Copyright (c) 2006 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_GUI_CLIENT_H
#define KIS_GUI_CLIENT_H
#include
#include
#include
#include
#include
#include
#include
#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 KisPaintingAssistantsManager;
class KisPaintopBox;
class KisSelectionManager;
class KisStatusBar;
class KisUndoAdapter;
class KisZoomManager;
class KisPaintopBox;
class KisActionManager;
class KisScriptManager;
class KisInputManager;
class KoUpdater;
class KoProgressUpdater;
/**
* KisViewManager manages the collection of views shown in a single mainwindow.
*/
class KRITAUI_EXPORT KisViewManager : public QObject
{
Q_OBJECT
public:
/**
* Construct a new view on the krita document.
* @param document the document we show.
* @param parent a parent widget we show ourselves in.
*/
KisViewManager(QWidget *parent, KActionCollection *actionCollection);
~KisViewManager() override;
/**
* Retrieves the entire action collection.
*/
virtual KActionCollection* actionCollection() const;
public: // Krita specific interfaces
void setCurrentView(KisView *view);
/// Return the image this view is displaying
KisImageWSP image() const;
KoZoomController *zoomController() const;
/// The resource provider contains all per-view settings, such as
/// current color, current paint op etc.
KisCanvasResourceProvider *resourceProvider();
/// Return the canvasbase class
KisCanvas2 *canvasBase() const;
/// Return the actual widget that is displaying the current image
QWidget* canvas() const;
/// Return the wrapper class around the statusbar
KisStatusBar *statusBar() const;
/**
* This adds a widget to the statusbar for this view.
* If you use this method instead of using statusBar() directly,
* KisView will take care of removing the items when the view GUI is deactivated
* and readding them when it is reactivated.
* The parameters are the same as QStatusBar::addWidget().
- */
+ */
void addStatusBarItem(QWidget * widget, int stretch = 0, bool permanent = false);
+
/**
* Remove a widget from the statusbar for this view.
*/
void removeStatusBarItem(QWidget * widget);
KisPaintopBox* paintOpBox() const;
/// create a new progress updater
QPointer createUnthreadedUpdater(const QString &name);
QPointer 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;
KisScriptManager *scriptManager() 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(KoCanvasResourceManager *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);
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 changeAuthorProfile(const QString &profileName);
void slotUpdateAuthorProfileActions();
void slotSaveShowRulersState(bool value);
void slotSaveRulersTrackMouseState(bool value);
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/canvas/kis_guides_config.cpp b/libs/ui/canvas/kis_guides_config.cpp
index c88df8f739..4c492470e2 100644
--- a/libs/ui/canvas/kis_guides_config.cpp
+++ b/libs/ui/canvas/kis_guides_config.cpp
@@ -1,246 +1,296 @@
/* This file is part of the KDE project
Copyright (C) 2006 Laurent Montel
Copyright (C) 2008 Jan Hambrecht
Copyright (c) 2015 Dmitry Kazakov
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; 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_guides_config.h"
#include
#include
#include
#include "kis_config.h"
#include "kis_dom_utils.h"
class Q_DECL_HIDDEN KisGuidesConfig::Private
{
public:
Private()
: showGuides(false)
, snapToGuides(false)
, lockGuides(false)
+ , rulersMultiple2(false)
+ , unitType(KoUnit::Pixel)
{}
bool operator==(const Private &rhs) {
return horzGuideLines == rhs.horzGuideLines &&
vertGuideLines == rhs.vertGuideLines &&
showGuides == rhs.showGuides &&
snapToGuides == rhs.snapToGuides &&
lockGuides == rhs.lockGuides &&
guidesColor == rhs.guidesColor &&
- guidesLineType == rhs.guidesLineType;
+ guidesLineType == rhs.guidesLineType &&
+ rulersMultiple2 == rhs.rulersMultiple2 &&
+ unitType == rhs.unitType;
}
QList horzGuideLines;
QList vertGuideLines;
bool showGuides;
bool snapToGuides;
bool lockGuides;
+ bool rulersMultiple2;
+
+ KoUnit::Type unitType;
QColor guidesColor;
LineTypeInternal guidesLineType;
Qt::PenStyle toPenStyle(LineTypeInternal type);
};
KisGuidesConfig::KisGuidesConfig()
: d(new Private())
{
loadStaticData();
}
KisGuidesConfig::~KisGuidesConfig()
{
}
KisGuidesConfig::KisGuidesConfig(const KisGuidesConfig &rhs)
: d(new Private(*rhs.d))
{
}
KisGuidesConfig& KisGuidesConfig::operator=(const KisGuidesConfig &rhs)
{
if (&rhs != this) {
*d = *rhs.d;
}
return *this;
}
bool KisGuidesConfig::operator==(const KisGuidesConfig &rhs) const
{
return *d == *rhs.d;
}
void KisGuidesConfig::setHorizontalGuideLines(const QList &lines)
{
d->horzGuideLines = lines;
}
void KisGuidesConfig::setVerticalGuideLines(const QList &lines)
{
d->vertGuideLines = lines;
}
void KisGuidesConfig::addGuideLine(Qt::Orientation o, qreal pos)
{
if (o == Qt::Horizontal) {
d->horzGuideLines.append(pos);
} else {
d->vertGuideLines.append(pos);
}
}
bool KisGuidesConfig::showGuideLines() const
{
return d->showGuides;
}
void KisGuidesConfig::setShowGuideLines(bool show)
{
d->showGuides = show;
}
bool KisGuidesConfig::showGuides() const
{
return d->showGuides;
}
void KisGuidesConfig::setShowGuides(bool value)
{
d->showGuides = value;
}
bool KisGuidesConfig::lockGuides() const
{
return d->lockGuides;
}
void KisGuidesConfig::setLockGuides(bool value)
{
d->lockGuides = value;
}
bool KisGuidesConfig::snapToGuides() const
{
return d->snapToGuides;
}
void KisGuidesConfig::setSnapToGuides(bool value)
{
d->snapToGuides = value;
}
+bool KisGuidesConfig::rulersMultiple2() const
+{
+ return d->rulersMultiple2;
+}
+
+void KisGuidesConfig::setRulersMultiple2(bool value)
+{
+ d->rulersMultiple2 = value;
+}
+
+KoUnit::Type KisGuidesConfig::unitType() const
+{
+ return d->unitType;
+}
+
+void KisGuidesConfig::setUnitType(const KoUnit::Type type)
+{
+ d->unitType = type;
+}
+
KisGuidesConfig::LineTypeInternal
KisGuidesConfig::guidesLineType() const
{
return d->guidesLineType;
}
void KisGuidesConfig::setGuidesLineType(LineTypeInternal value)
{
d->guidesLineType = value;
}
QColor KisGuidesConfig::guidesColor() const
{
return d->guidesColor;
}
void KisGuidesConfig::setGuidesColor(const QColor &value)
{
d->guidesColor = value;
}
Qt::PenStyle KisGuidesConfig::Private::toPenStyle(LineTypeInternal type) {
return type == LINE_SOLID ? Qt::SolidLine :
type == LINE_DASHED ? Qt::DashLine :
type == LINE_DOTTED ? Qt::DotLine :
Qt::DashDotDotLine;
}
QPen KisGuidesConfig::guidesPen() const
{
return QPen(d->guidesColor, 0, d->toPenStyle(d->guidesLineType));
}
const QList& KisGuidesConfig::horizontalGuideLines() const
{
return d->horzGuideLines;
}
const QList& KisGuidesConfig::verticalGuideLines() const
{
return d->vertGuideLines;
}
bool KisGuidesConfig::hasGuides() const
{
return !d->horzGuideLines.isEmpty() || !d->vertGuideLines.isEmpty();
}
void KisGuidesConfig::loadStaticData()
{
KisConfig cfg;
d->guidesLineType = LineTypeInternal(cfg.guidesLineStyle());
d->guidesColor = cfg.guidesColor();
}
void KisGuidesConfig::saveStaticData() const
{
KisConfig cfg;
cfg.setGuidesLineStyle(d->guidesLineType);
cfg.setGuidesColor(d->guidesColor);
}
QDomElement KisGuidesConfig::saveToXml(QDomDocument& doc, const QString &tag) const
{
QDomElement guidesElement = doc.createElement(tag);
KisDomUtils::saveValue(&guidesElement, "showGuides", d->showGuides);
KisDomUtils::saveValue(&guidesElement, "snapToGuides", d->snapToGuides);
KisDomUtils::saveValue(&guidesElement, "lockGuides", d->lockGuides);
KisDomUtils::saveValue(&guidesElement, "horizontalGuides", d->horzGuideLines.toVector());
KisDomUtils::saveValue(&guidesElement, "verticalGuides", d->vertGuideLines.toVector());
+ KisDomUtils::saveValue(&guidesElement, "rulersMultiple2", d->rulersMultiple2);
+ KoUnit tmp(d->unitType);
+ KisDomUtils::saveValue(&guidesElement, "unit", tmp.symbol());
+
return guidesElement;
}
bool KisGuidesConfig::loadFromXml(const QDomElement &parent)
{
bool result = true;
result &= KisDomUtils::loadValue(parent, "showGuides", &d->showGuides);
result &= KisDomUtils::loadValue(parent, "snapToGuides", &d->snapToGuides);
result &= KisDomUtils::loadValue(parent, "lockGuides", &d->lockGuides);
QVector hGuides;
QVector vGuides;
result &= KisDomUtils::loadValue(parent, "horizontalGuides", &hGuides);
result &= KisDomUtils::loadValue(parent, "verticalGuides", &vGuides);
d->horzGuideLines = QList::fromVector(hGuides);
d->vertGuideLines = QList::fromVector(vGuides);
+ result &= KisDomUtils::loadValue(parent, "rulersMultiple2", &d->rulersMultiple2);
+ QString unit;
+ result &= KisDomUtils::loadValue(parent, "unit", &unit);
+ bool ok = false;
+ KoUnit tmp = KoUnit::fromSymbol(unit, &ok);
+ if (ok) {
+ d->unitType = tmp.type();
+ }
+ result &= ok;
+
+
return result;
}
+
+bool KisGuidesConfig::isDefault() const
+{
+ KisGuidesConfig defaultObject;
+ defaultObject.loadStaticData();
+
+ return *this == defaultObject;
+}
diff --git a/libs/ui/canvas/kis_guides_config.h b/libs/ui/canvas/kis_guides_config.h
index 4b5397dfbe..ed97a51333 100644
--- a/libs/ui/canvas/kis_guides_config.h
+++ b/libs/ui/canvas/kis_guides_config.h
@@ -1,121 +1,130 @@
/* This file is part of the KDE project
Copyright (C) 2006 Laurent Montel
Copyright (C) 2008 Jan Hambrecht
Copyright (c) 2015 Dmitry Kazakov
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; 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 KOGUIDESDATA_H
#define KOGUIDESDATA_H
#include "kritaui_export.h"
#include
#include
#include
+#include
class QDomElement;
class QDomDocument;
class QColor;
class QPen;
class KRITAUI_EXPORT KisGuidesConfig : boost::equality_comparable
{
public:
enum LineTypeInternal {
LINE_SOLID = 0,
LINE_DASHED,
LINE_DOTTED
};
public:
KisGuidesConfig();
~KisGuidesConfig();
KisGuidesConfig(const KisGuidesConfig &rhs);
KisGuidesConfig& operator=(const KisGuidesConfig &rhs);
bool operator==(const KisGuidesConfig &rhs) const;
/**
* @brief Set the positions of the horizontal guide lines
*
* @param lines a list of positions of the horizontal guide lines
*/
void setHorizontalGuideLines(const QList &lines);
/**
* @brief Set the positions of the vertical guide lines
*
* @param lines a list of positions of the vertical guide lines
*/
void setVerticalGuideLines(const QList &lines);
/**
* @brief Add a guide line to the canvas.
*
* @param orientation the orientation of the guide line
* @param position the position in document coordinates of the guide line
*/
void addGuideLine(Qt::Orientation orientation, qreal position);
/**
* @brief Display or not guide lines
*/
bool showGuideLines() const;
/**
* @param show display or not guide line
*/
void setShowGuideLines(bool show);
bool showGuides() const;
void setShowGuides(bool value);
bool lockGuides() const;
void setLockGuides(bool value);
bool snapToGuides() const;
void setSnapToGuides(bool value);
+ bool rulersMultiple2() const;
+ void setRulersMultiple2(bool value);
+
+ KoUnit::Type unitType() const;
+ void setUnitType(KoUnit::Type type);
+
LineTypeInternal guidesLineType() const;
void setGuidesLineType(LineTypeInternal value);
QColor guidesColor() const;
void setGuidesColor(const QColor &value);
QPen guidesPen() const;
/// Returns the list of horizontal guide lines.
const QList& horizontalGuideLines() const;
/// Returns the list of vertical guide lines.
const QList& verticalGuideLines() const;
bool hasGuides() const;
void loadStaticData();
void saveStaticData() const;
QDomElement saveToXml(QDomDocument& doc, const QString &tag) const;
bool loadFromXml(const QDomElement &parent);
+ bool isDefault() const;
+
private:
class Private;
const QScopedPointer d;
};
#endif
diff --git a/libs/ui/canvas/kis_guides_manager.cpp b/libs/ui/canvas/kis_guides_manager.cpp
index 2ebf8150ac..1d1d04e72b 100644
--- a/libs/ui/canvas/kis_guides_manager.cpp
+++ b/libs/ui/canvas/kis_guides_manager.cpp
@@ -1,765 +1,792 @@
/*
* Copyright (c) 2016 Dmitry Kazakov
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_guides_manager.h"
#include
#include
#include "kis_guides_decoration.h"
#include
#include "kis_guides_config.h"
#include "kis_action_manager.h"
#include "kis_action.h"
#include "kis_signals_blocker.h"
#include "input/kis_input_manager.h"
#include "kis_coordinates_converter.h"
#include "kis_zoom_manager.h"
#include "kis_signal_auto_connection.h"
#include "KisViewManager.h"
#include "KisDocument.h"
#include "kis_algebra_2d.h"
#include
#include "kis_snap_line_strategy.h"
#include "kis_change_guides_command.h"
#include "kis_snap_config.h"
#include "kis_coordinates_converter.h"
#include "kis_canvas2.h"
#include "kis_signal_compressor.h"
struct KisGuidesManager::Private
{
Private(KisGuidesManager *_q)
: q(_q),
decoration(0),
invalidGuide(Qt::Horizontal, -1),
currentGuide(invalidGuide),
cursorSwitched(false),
dragStartGuidePos(0),
updateDocumentCompressor(40, KisSignalCompressor::FIRST_ACTIVE),
shouldSetModified(false) {}
KisGuidesManager *q;
KisGuidesDecoration *decoration;
KisGuidesConfig guidesConfig;
KisSnapConfig snapConfig;
QPointer view;
typedef QPair GuideHandle;
GuideHandle findGuide(const QPointF &docPos);
bool isGuideValid(const GuideHandle &h);
qreal guideValue(const GuideHandle &h);
void setGuideValue(const GuideHandle &h, qreal value);
void deleteGuide(const GuideHandle &h);
const GuideHandle invalidGuide;
bool updateCursor(const QPointF &docPos);
void initDragStart(const GuideHandle &guide,
const QPointF &dragStart,
qreal guideValue,
bool snapToStart);
bool mouseMoveHandler(const QPointF &docPos, Qt::KeyboardModifiers modifiers);
bool mouseReleaseHandler(const QPointF &docPos);
void updateSnappingStatus(const KisGuidesConfig &value);
QPointF alignToPixels(const QPointF docPoint);
QPointF getDocPointFromEvent(QEvent *event);
Qt::MouseButton getButtonFromEvent(QEvent *event);
QAction* createShortenedAction(const QString &text, const QString &parentId, QObject *parent);
void syncAction(const QString &actionName, bool value);
GuideHandle currentGuide;
bool cursorSwitched;
QCursor oldCursor;
QPointF dragStartDoc;
QPointF dragPointerOffset;
qreal dragStartGuidePos;
KisSignalAutoConnectionsStore viewConnections;
KisSignalCompressor updateDocumentCompressor;
bool shouldSetModified;
};
KisGuidesManager::KisGuidesManager(QObject *parent)
: QObject(parent),
m_d(new Private(this))
{
connect(&m_d->updateDocumentCompressor, SIGNAL(timeout()), SLOT(slotUploadConfigToDocument()));
}
KisGuidesManager::~KisGuidesManager()
{
}
void KisGuidesManager::setGuidesConfig(const KisGuidesConfig &config)
{
if (config == m_d->guidesConfig) return;
setGuidesConfigImpl(config, true);
}
void KisGuidesManager::slotDocumentRequestedConfig(const KisGuidesConfig &config)
{
if (config == m_d->guidesConfig) return;
setGuidesConfigImpl(config, false);
}
void KisGuidesManager::slotUploadConfigToDocument()
{
const KisGuidesConfig &value = m_d->guidesConfig;
KisDocument *doc = m_d->view ? m_d->view->document() : 0;
if (doc) {
KisSignalsBlocker b(doc);
if (m_d->shouldSetModified) {
KUndo2Command *cmd = new KisChangeGuidesCommand(doc, value);
doc->addCommand(cmd);
} else {
doc->setGuidesConfig(value);
}
value.saveStaticData();
}
m_d->shouldSetModified = false;
}
void KisGuidesManager::setGuidesConfigImpl(const KisGuidesConfig &value, bool emitModified)
{
m_d->guidesConfig = value;
if (m_d->decoration && value != m_d->decoration->guidesConfig()) {
m_d->decoration->setVisible(value.showGuides());
m_d->decoration->setGuidesConfig(value);
}
m_d->shouldSetModified |= emitModified;
m_d->updateDocumentCompressor.start();
const bool shouldFilterEvent =
value.showGuides() && !value.lockGuides() && value.hasGuides();
attachEventFilterImpl(shouldFilterEvent);
syncActionsStatus();
if (!m_d->isGuideValid(m_d->currentGuide)) {
m_d->updateSnappingStatus(value);
}
+ if (m_d->view) {
+ m_d->view->document()->setUnit(KoUnit(m_d->guidesConfig.unitType()));
+ m_d->view->viewManager()->actionManager()->actionByName("ruler_pixel_multiple2")->setChecked(value.rulersMultiple2());
+ }
+
emit sigRequestUpdateGuidesConfig(m_d->guidesConfig);
}
void KisGuidesManager::attachEventFilterImpl(bool value)
{
if (!m_d->view) return;
KisInputManager *inputManager = m_d->view->globalInputManager();
if (inputManager) {
if (value) {
inputManager->attachPriorityEventFilter(this, 100);
} else {
inputManager->detachPriorityEventFilter(this);
}
}
}
void KisGuidesManager::Private::syncAction(const QString &actionName, bool value)
{
KisActionManager *actionManager = view->viewManager()->actionManager();
KisAction *action = actionManager->actionByName(actionName);
KIS_ASSERT_RECOVER_RETURN(action);
KisSignalsBlocker b(action);
action->setChecked(value);
}
void KisGuidesManager::syncActionsStatus()
{
if (!m_d->view) return;
m_d->syncAction("view_show_guides", m_d->guidesConfig.showGuides());
m_d->syncAction("view_lock_guides", m_d->guidesConfig.lockGuides());
m_d->syncAction("view_snap_to_guides", m_d->guidesConfig.snapToGuides());
m_d->syncAction("view_snap_orthogonal", m_d->snapConfig.orthogonal());
m_d->syncAction("view_snap_node", m_d->snapConfig.node());
m_d->syncAction("view_snap_extension", m_d->snapConfig.extension());
m_d->syncAction("view_snap_intersection", m_d->snapConfig.intersection());
m_d->syncAction("view_snap_bounding_box", m_d->snapConfig.boundingBox());
m_d->syncAction("view_snap_image_bounds", m_d->snapConfig.imageBounds());
m_d->syncAction("view_snap_image_center", m_d->snapConfig.imageCenter());
}
void KisGuidesManager::Private::updateSnappingStatus(const KisGuidesConfig &value)
{
if (!view) return;
KoSnapGuide *snapGuide = view->canvasBase()->snapGuide();
KisSnapLineStrategy *guidesSnap = 0;
if (value.snapToGuides()) {
guidesSnap = new KisSnapLineStrategy(KoSnapGuide::GuideLineSnapping);
guidesSnap->setHorizontalLines(value.horizontalGuideLines());
guidesSnap->setVerticalLines(value.verticalGuideLines());
}
snapGuide->overrideSnapStrategy(KoSnapGuide::GuideLineSnapping, guidesSnap);
snapGuide->enableSnapStrategy(KoSnapGuide::GuideLineSnapping, guidesSnap);
snapGuide->enableSnapStrategy(KoSnapGuide::OrthogonalSnapping, snapConfig.orthogonal());
snapGuide->enableSnapStrategy(KoSnapGuide::NodeSnapping, snapConfig.node());
snapGuide->enableSnapStrategy(KoSnapGuide::ExtensionSnapping, snapConfig.extension());
snapGuide->enableSnapStrategy(KoSnapGuide::IntersectionSnapping, snapConfig.intersection());
snapGuide->enableSnapStrategy(KoSnapGuide::BoundingBoxSnapping, snapConfig.boundingBox());
snapGuide->enableSnapStrategy(KoSnapGuide::DocumentBoundsSnapping, snapConfig.imageBounds());
snapGuide->enableSnapStrategy(KoSnapGuide::DocumentCenterSnapping, snapConfig.imageCenter());
snapConfig.saveStaticData();
}
bool KisGuidesManager::showGuides() const
{
return m_d->guidesConfig.showGuides();
}
void KisGuidesManager::setShowGuides(bool value)
{
m_d->guidesConfig.setShowGuides(value);
setGuidesConfigImpl(m_d->guidesConfig);
}
bool KisGuidesManager::lockGuides() const
{
return m_d->guidesConfig.lockGuides();
}
void KisGuidesManager::setLockGuides(bool value)
{
m_d->guidesConfig.setLockGuides(value);
setGuidesConfigImpl(m_d->guidesConfig);
}
bool KisGuidesManager::snapToGuides() const
{
return m_d->guidesConfig.snapToGuides();
}
void KisGuidesManager::setSnapToGuides(bool value)
{
m_d->guidesConfig.setSnapToGuides(value);
setGuidesConfigImpl(m_d->guidesConfig);
}
+bool KisGuidesManager::rulersMultiple2() const
+{
+ return m_d->guidesConfig.rulersMultiple2();
+}
+
+void KisGuidesManager::setRulersMultiple2(bool value)
+{
+ m_d->guidesConfig.setRulersMultiple2(value);
+ setGuidesConfigImpl(m_d->guidesConfig);
+}
+
+KoUnit::Type KisGuidesManager::unitType() const
+{
+ return m_d->guidesConfig.unitType();
+}
+
+void KisGuidesManager::setUnitType(const KoUnit::Type type)
+{
+ m_d->guidesConfig.setUnitType(type);
+ setGuidesConfigImpl(m_d->guidesConfig);
+}
+
void KisGuidesManager::setup(KisActionManager *actionManager)
{
KisAction *action = 0;
action = actionManager->createAction("view_show_guides");
connect(action, SIGNAL(toggled(bool)), this, SLOT(setShowGuides(bool)));
action = actionManager->createAction("view_lock_guides");
connect(action, SIGNAL(toggled(bool)), this, SLOT(setLockGuides(bool)));
action = actionManager->createAction("view_snap_to_guides");
connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapToGuides(bool)));
action = actionManager->createAction("show_snap_options_popup");
connect(action, SIGNAL(triggered()), this, SLOT(slotShowSnapOptions()));
action = actionManager->createAction("view_snap_orthogonal");
connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapOrthogonal(bool)));
action = actionManager->createAction("view_snap_node");
connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapNode(bool)));
action = actionManager->createAction("view_snap_extension");
connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapExtension(bool)));
action = actionManager->createAction("view_snap_intersection");
connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapIntersection(bool)));
action = actionManager->createAction("view_snap_bounding_box");
connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapBoundingBox(bool)));
action = actionManager->createAction("view_snap_image_bounds");
connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapImageBounds(bool)));
action = actionManager->createAction("view_snap_image_center");
connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapImageCenter(bool)));
m_d->updateSnappingStatus(m_d->guidesConfig);
syncActionsStatus();
}
void KisGuidesManager::setView(QPointer view)
{
if (m_d->view) {
KoSnapGuide *snapGuide = m_d->view->canvasBase()->snapGuide();
snapGuide->overrideSnapStrategy(KoSnapGuide::GuideLineSnapping, 0);
snapGuide->enableSnapStrategy(KoSnapGuide::GuideLineSnapping, false);
if (m_d->updateDocumentCompressor.isActive()) {
m_d->updateDocumentCompressor.stop();
slotUploadConfigToDocument();
}
m_d->decoration = 0;
m_d->viewConnections.clear();
attachEventFilterImpl(false);
}
m_d->view = view;
if (m_d->view) {
KisGuidesDecoration* decoration = qobject_cast(m_d->view->canvasBase()->decoration(GUIDES_DECORATION_ID).data());
if (!decoration) {
decoration = new KisGuidesDecoration(m_d->view);
m_d->view->canvasBase()->addDecoration(decoration);
}
m_d->decoration = decoration;
m_d->guidesConfig = m_d->view->document()->guidesConfig();
setGuidesConfigImpl(m_d->guidesConfig, false);
m_d->viewConnections.addUniqueConnection(
m_d->view->zoomManager()->horizontalRuler(), SIGNAL(guideCreationInProgress(Qt::Orientation, const QPoint&)),
this, SLOT(slotGuideCreationInProgress(Qt::Orientation, const QPoint&)));
m_d->viewConnections.addUniqueConnection(
m_d->view->zoomManager()->horizontalRuler(), SIGNAL(guideCreationFinished(Qt::Orientation, const QPoint&)),
this, SLOT(slotGuideCreationFinished(Qt::Orientation, const QPoint&)));
m_d->viewConnections.addUniqueConnection(
m_d->view->zoomManager()->verticalRuler(), SIGNAL(guideCreationInProgress(Qt::Orientation, const QPoint&)),
this, SLOT(slotGuideCreationInProgress(Qt::Orientation, const QPoint&)));
m_d->viewConnections.addUniqueConnection(
m_d->view->zoomManager()->verticalRuler(), SIGNAL(guideCreationFinished(Qt::Orientation, const QPoint&)),
this, SLOT(slotGuideCreationFinished(Qt::Orientation, const QPoint&)));
m_d->viewConnections.addUniqueConnection(
m_d->view->document(), SIGNAL(sigGuidesConfigChanged(const KisGuidesConfig &)),
this, SLOT(slotDocumentRequestedConfig(const KisGuidesConfig &)));
}
}
KisGuidesManager::Private::GuideHandle
KisGuidesManager::Private::findGuide(const QPointF &docPos)
{
const int snapRadius = 16;
GuideHandle nearestGuide = invalidGuide;
qreal nearestRadius = std::numeric_limits::max();
for (int i = 0; i < guidesConfig.horizontalGuideLines().size(); i++) {
const qreal guide = guidesConfig.horizontalGuideLines()[i];
const qreal radius = qAbs(docPos.y() - guide);
if (radius < snapRadius && radius < nearestRadius) {
nearestGuide = GuideHandle(Qt::Horizontal, i);
nearestRadius = radius;
}
}
for (int i = 0; i < guidesConfig.verticalGuideLines().size(); i++) {
const qreal guide = guidesConfig.verticalGuideLines()[i];
const qreal radius = qAbs(docPos.x() - guide);
if (radius < snapRadius && radius < nearestRadius) {
nearestGuide = GuideHandle(Qt::Vertical, i);
nearestRadius = radius;
}
}
return nearestGuide;
}
bool KisGuidesManager::Private::isGuideValid(const GuideHandle &h)
{
return h.second >= 0;
}
qreal KisGuidesManager::Private::guideValue(const GuideHandle &h)
{
return h.first == Qt::Horizontal ?
guidesConfig.horizontalGuideLines()[h.second] :
guidesConfig.verticalGuideLines()[h.second];
}
void KisGuidesManager::Private::setGuideValue(const GuideHandle &h, qreal value)
{
if (h.first == Qt::Horizontal) {
QList guides = guidesConfig.horizontalGuideLines();
guides[h.second] = value;
guidesConfig.setHorizontalGuideLines(guides);
} else {
QList guides = guidesConfig.verticalGuideLines();
guides[h.second] = value;
guidesConfig.setVerticalGuideLines(guides);
}
}
void KisGuidesManager::Private::deleteGuide(const GuideHandle &h)
{
if (h.first == Qt::Horizontal) {
QList guides = guidesConfig.horizontalGuideLines();
guides.removeAt(h.second);
guidesConfig.setHorizontalGuideLines(guides);
} else {
QList guides = guidesConfig.verticalGuideLines();
guides.removeAt(h.second);
guidesConfig.setVerticalGuideLines(guides);
}
}
bool KisGuidesManager::Private::updateCursor(const QPointF &docPos)
{
KisCanvas2 *canvas = view->canvasBase();
const GuideHandle guide = findGuide(docPos);
const bool guideValid = isGuideValid(guide);
if (guideValid && !cursorSwitched) {
oldCursor = canvas->canvasWidget()->cursor();
}
if (guideValid) {
cursorSwitched = true;
QCursor newCursor = guide.first == Qt::Horizontal ?
Qt::SizeVerCursor : Qt::SizeHorCursor;
canvas->canvasWidget()->setCursor(newCursor);
}
if (!guideValid && cursorSwitched) {
canvas->canvasWidget()->setCursor(oldCursor);
cursorSwitched = false;
}
return guideValid;
}
void KisGuidesManager::Private::initDragStart(const GuideHandle &guide,
const QPointF &dragStart,
qreal guideValue,
bool snapToStart)
{
currentGuide = guide;
dragStartDoc = dragStart;
dragStartGuidePos = guideValue;
dragPointerOffset =
guide.first == Qt::Horizontal ?
QPointF(0, dragStartGuidePos - dragStartDoc.y()) :
QPointF(dragStartGuidePos - dragStartDoc.x(), 0);
KoSnapGuide *snapGuide = view->canvasBase()->snapGuide();
snapGuide->reset();
if (snapToStart) {
KisSnapLineStrategy *strategy = new KisSnapLineStrategy();
strategy->addLine(guide.first, guideValue);
snapGuide->addCustomSnapStrategy(strategy);
}
}
QPointF KisGuidesManager::Private::alignToPixels(const QPointF docPoint)
{
KisCanvas2 *canvas = view->canvasBase();
const KisCoordinatesConverter *converter = canvas->coordinatesConverter();
QPoint imagePoint = converter->documentToImage(docPoint).toPoint();
return converter->imageToDocument(imagePoint);
}
bool KisGuidesManager::Private::mouseMoveHandler(const QPointF &docPos, Qt::KeyboardModifiers modifiers)
{
if (isGuideValid(currentGuide)) {
KoSnapGuide *snapGuide = view->canvasBase()->snapGuide();
const QPointF snappedPos = snapGuide->snap(docPos, dragPointerOffset, modifiers);
const QPointF offset = snappedPos - dragStartDoc;
const qreal newValue = dragStartGuidePos +
(currentGuide.first == Qt::Horizontal ?
offset.y() : offset.x());
setGuideValue(currentGuide, newValue);
q->setGuidesConfigImpl(guidesConfig);
}
return updateCursor(docPos);
}
bool KisGuidesManager::Private::mouseReleaseHandler(const QPointF &docPos)
{
bool result = false;
KisCanvas2 *canvas = view->canvasBase();
const KisCoordinatesConverter *converter = canvas->coordinatesConverter();
if (isGuideValid(currentGuide)) {
const QRectF docRect = converter->imageRectInDocumentPixels();
// TODO: enable work rect after we fix painting guides
// outside canvas in openGL mode
const QRectF workRect = KisAlgebra2D::blowRect(docRect, 0 /*0.2*/);
if (!workRect.contains(docPos)) {
deleteGuide(currentGuide);
q->setGuidesConfigImpl(guidesConfig);
/**
* When we delete a guide, it might happen that we are
* delting the last guide. Therefore we should eat the
* corresponding event so that the event filter would stop
* the filter processing.
*/
result = true;
}
currentGuide = invalidGuide;
dragStartDoc = QPointF();
dragPointerOffset = QPointF();
dragStartGuidePos = 0;
KoSnapGuide *snapGuide = view->canvasBase()->snapGuide();
snapGuide->reset();
updateSnappingStatus(guidesConfig);
}
return updateCursor(docPos) | result;
}
QPointF KisGuidesManager::Private::getDocPointFromEvent(QEvent *event)
{
QPointF result;
KisCanvas2 *canvas = view->canvasBase();
const KisCoordinatesConverter *converter = canvas->coordinatesConverter();
if (event->type() == QEvent::MouseMove ||
event->type() == QEvent::MouseButtonPress ||
event->type() == QEvent::MouseButtonRelease) {
QMouseEvent *mouseEvent = static_cast(event);
result = alignToPixels(converter->widgetToDocument(mouseEvent->pos()));
} else if (event->type() == QEvent::TabletMove ||
event->type() == QEvent::TabletPress ||
event->type() == QEvent::TabletRelease) {
QTabletEvent *tabletEvent = static_cast(event);
result = alignToPixels(converter->widgetToDocument(tabletEvent->pos()));
}
return result;
}
Qt::MouseButton KisGuidesManager::Private::getButtonFromEvent(QEvent *event)
{
Qt::MouseButton button = Qt::NoButton;
if (event->type() == QEvent::MouseMove ||
event->type() == QEvent::MouseButtonPress ||
event->type() == QEvent::MouseButtonRelease) {
QMouseEvent *mouseEvent = static_cast(event);
button = mouseEvent->button();
} else if (event->type() == QEvent::TabletMove ||
event->type() == QEvent::TabletPress ||
event->type() == QEvent::TabletRelease) {
QTabletEvent *tabletEvent = static_cast(event);
button = tabletEvent->button();
}
return button;
}
bool KisGuidesManager::eventFilter(QObject *obj, QEvent *event)
{
if (!m_d->view || obj != m_d->view->canvasBase()->canvasWidget()) return false;
bool retval = false;
switch (event->type()) {
case QEvent::Enter:
case QEvent::Leave:
case QEvent::TabletMove:
case QEvent::MouseMove: {
const QPointF docPos = m_d->getDocPointFromEvent(event);
const Qt::KeyboardModifiers modifiers = qApp->keyboardModifiers();
retval = m_d->mouseMoveHandler(docPos, modifiers);
break;
}
case QEvent::TabletPress:
case QEvent::MouseButtonPress: {
if (m_d->getButtonFromEvent(event) != Qt::LeftButton) break;
const QPointF docPos = m_d->getDocPointFromEvent(event);
const Private::GuideHandle guide = m_d->findGuide(docPos);
const bool guideValid = m_d->isGuideValid(guide);
if (guideValid) {
m_d->initDragStart(guide, docPos, m_d->guideValue(guide), true);
}
retval = m_d->updateCursor(docPos);
break;
}
case QEvent::TabletRelease:
case QEvent::MouseButtonRelease: {
if (m_d->getButtonFromEvent(event) != Qt::LeftButton) break;
const QPointF docPos = m_d->getDocPointFromEvent(event);
retval = m_d->mouseReleaseHandler(docPos);
break;
}
default:
break;
}
return !retval ? QObject::eventFilter(obj, event) : true;
}
void KisGuidesManager::slotGuideCreationInProgress(Qt::Orientation orientation, const QPoint &globalPos)
{
if (m_d->guidesConfig.lockGuides()) return;
KisCanvas2 *canvas = m_d->view->canvasBase();
const KisCoordinatesConverter *converter = canvas->coordinatesConverter();
const QPointF widgetPos = canvas->canvasWidget()->mapFromGlobal(globalPos);
const QPointF docPos = m_d->alignToPixels(converter->widgetToDocument(widgetPos));
if (m_d->isGuideValid(m_d->currentGuide)) {
const Qt::KeyboardModifiers modifiers = qApp->keyboardModifiers();
m_d->mouseMoveHandler(docPos, modifiers);
} else {
m_d->guidesConfig.setShowGuides(true);
if (orientation == Qt::Horizontal) {
QList guides = m_d->guidesConfig.horizontalGuideLines();
guides.append(docPos.y());
m_d->currentGuide.first = orientation;
m_d->currentGuide.second = guides.size() - 1;
m_d->guidesConfig.setHorizontalGuideLines(guides);
m_d->initDragStart(m_d->currentGuide, docPos, docPos.y(), false);
} else {
QList guides = m_d->guidesConfig.verticalGuideLines();
guides.append(docPos.x());
m_d->currentGuide.first = orientation;
m_d->currentGuide.second = guides.size() - 1;
m_d->guidesConfig.setVerticalGuideLines(guides);
m_d->initDragStart(m_d->currentGuide, docPos, docPos.x(), false);
}
setGuidesConfigImpl(m_d->guidesConfig);
}
}
void KisGuidesManager::slotGuideCreationFinished(Qt::Orientation orientation, const QPoint &globalPos)
{
Q_UNUSED(orientation);
if (m_d->guidesConfig.lockGuides()) return;
KisCanvas2 *canvas = m_d->view->canvasBase();
const KisCoordinatesConverter *converter = canvas->coordinatesConverter();
const QPointF widgetPos = canvas->canvasWidget()->mapFromGlobal(globalPos);
const QPointF docPos = m_d->alignToPixels(converter->widgetToDocument(widgetPos));
m_d->mouseReleaseHandler(docPos);
}
QAction* KisGuidesManager::Private::createShortenedAction(const QString &text, const QString &parentId, QObject *parent)
{
KisActionManager *actionManager = view->viewManager()->actionManager();
QAction *action = 0;
KisAction *parentAction = 0;
action = new QAction(text, parent);
action->setCheckable(true);
parentAction = actionManager->actionByName(parentId);
action->setChecked(parentAction->isChecked());
connect(action, SIGNAL(toggled(bool)), parentAction, SLOT(setChecked(bool)));
return action;
}
void KisGuidesManager::slotShowSnapOptions()
{
const QPoint pos = QCursor::pos();
QMenu menu;
menu.addSection(i18n("Snap to:"));
menu.addAction(m_d->createShortenedAction(i18n("Grid"), "view_snap_to_grid", &menu));
menu.addAction(m_d->createShortenedAction(i18n("Guides"), "view_snap_to_guides", &menu));
menu.addAction(m_d->createShortenedAction(i18n("Orthogonal"), "view_snap_orthogonal", &menu));
menu.addAction(m_d->createShortenedAction(i18n("Node"), "view_snap_node", &menu));
menu.addAction(m_d->createShortenedAction(i18n("Extension"), "view_snap_extension", &menu));
menu.addAction(m_d->createShortenedAction(i18n("Intersection"), "view_snap_intersection", &menu));
menu.addAction(m_d->createShortenedAction(i18n("Bounding Box"), "view_snap_bounding_box", &menu));
menu.addAction(m_d->createShortenedAction(i18n("Image Bounds"), "view_snap_image_bounds", &menu));
menu.addAction(m_d->createShortenedAction(i18n("Image Center"), "view_snap_image_center", &menu));
menu.exec(pos);
}
void KisGuidesManager::setSnapOrthogonal(bool value)
{
m_d->snapConfig.setOrthogonal(value);
m_d->updateSnappingStatus(m_d->guidesConfig);
}
void KisGuidesManager::setSnapNode(bool value)
{
m_d->snapConfig.setNode(value);
m_d->updateSnappingStatus(m_d->guidesConfig);
}
void KisGuidesManager::setSnapExtension(bool value)
{
m_d->snapConfig.setExtension(value);
m_d->updateSnappingStatus(m_d->guidesConfig);
}
void KisGuidesManager::setSnapIntersection(bool value)
{
m_d->snapConfig.setIntersection(value);
m_d->updateSnappingStatus(m_d->guidesConfig);
}
void KisGuidesManager::setSnapBoundingBox(bool value)
{
m_d->snapConfig.setBoundingBox(value);
m_d->updateSnappingStatus(m_d->guidesConfig);
}
void KisGuidesManager::setSnapImageBounds(bool value)
{
m_d->snapConfig.setImageBounds(value);
m_d->updateSnappingStatus(m_d->guidesConfig);
}
void KisGuidesManager::setSnapImageCenter(bool value)
{
m_d->snapConfig.setImageCenter(value);
m_d->updateSnappingStatus(m_d->guidesConfig);
}
diff --git a/libs/ui/canvas/kis_guides_manager.h b/libs/ui/canvas/kis_guides_manager.h
index 3e670587bc..9c7aadea61 100644
--- a/libs/ui/canvas/kis_guides_manager.h
+++ b/libs/ui/canvas/kis_guides_manager.h
@@ -1,84 +1,90 @@
/*
* Copyright (c) 2016 Dmitry Kazakov
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_GUIDES_MANAGER_H
#define __KIS_GUIDES_MANAGER_H
#include
#include
#include "kritaui_export.h"
+#include
class KisView;
class KisActionManager;
class KisCanvasDecoration;
class KisGuidesConfig;
class KRITAUI_EXPORT KisGuidesManager : public QObject
{
Q_OBJECT
public:
KisGuidesManager(QObject *parent = 0);
~KisGuidesManager() override;
void setup(KisActionManager *actionManager);
void setView(QPointer view);
bool showGuides() const;
bool lockGuides() const;
bool snapToGuides() const;
+ bool rulersMultiple2() const;
+
+ KoUnit::Type unitType() const;
bool eventFilter(QObject *obj, QEvent *event) override;
Q_SIGNALS:
void sigRequestUpdateGuidesConfig(const KisGuidesConfig &config);
public Q_SLOTS:
void setGuidesConfig(const KisGuidesConfig &config);
void slotDocumentRequestedConfig(const KisGuidesConfig &config);
void setShowGuides(bool value);
void setLockGuides(bool value);
void setSnapToGuides(bool value);
+ void setRulersMultiple2(bool value);
+ void setUnitType(KoUnit::Type type);
void slotGuideCreationInProgress(Qt::Orientation orientation, const QPoint &globalPos);
void slotGuideCreationFinished(Qt::Orientation orientation, const QPoint &globalPos);
void slotShowSnapOptions();
void setSnapOrthogonal(bool value);
void setSnapNode(bool value);
void setSnapExtension(bool value);
void setSnapIntersection(bool value);
void setSnapBoundingBox(bool value);
void setSnapImageBounds(bool value);
void setSnapImageCenter(bool value);
void slotUploadConfigToDocument();
private:
void setGuidesConfigImpl(const KisGuidesConfig &value, bool emitModified = true);
void attachEventFilterImpl(bool value);
void syncActionsStatus();
private:
struct Private;
const QScopedPointer m_d;
};
#endif /* __KIS_GUIDES_MANAGER_H */
diff --git a/libs/ui/input/kis_zoom_action.cpp b/libs/ui/input/kis_zoom_action.cpp
index 97d3d5de08..a61857359c 100644
--- a/libs/ui/input/kis_zoom_action.cpp
+++ b/libs/ui/input/kis_zoom_action.cpp
@@ -1,304 +1,304 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* 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_zoom_action.h"
#include
#include
#include
#include
#include
#include
#include
#include "kis_cursor.h"
#include "KisViewManager.h"
#include "kis_input_manager.h"
#include "kis_config.h"
inline QPoint pointFromEvent(QEvent *event) {
if (!event) {
return QPoint();
} else if (QMouseEvent *mouseEvent = dynamic_cast(event)) {
return mouseEvent->pos();
} else if (QTabletEvent *tabletEvent = dynamic_cast(event)) {
return tabletEvent->pos();
} else if (QWheelEvent *wheelEvent = dynamic_cast(event)) {
return wheelEvent->pos();
}
return QPoint();
}
class KisZoomAction::Private
{
public:
Private(KisZoomAction *qq) : q(qq), distance(0), lastDistance(0.f) {}
QPointF centerPoint(QTouchEvent* event);
KisZoomAction *q;
int distance;
Shortcuts mode;
QPointF lastPosition;
float lastDistance;
QPoint startPoint;
void zoomTo(bool zoomIn, const QPoint &pos);
};
QPointF KisZoomAction::Private::centerPoint(QTouchEvent* event)
{
QPointF result;
int count = 0;
Q_FOREACH (QTouchEvent::TouchPoint point, event->touchPoints()) {
if (point.state() != Qt::TouchPointReleased) {
- result += point.screenPos();
+ result += point.pos();
count++;
}
}
if (count > 0) {
return result / count;
} else {
return QPointF();
}
}
void KisZoomAction::Private::zoomTo(bool zoomIn, const QPoint &point)
{
KoZoomAction *zoomAction = q->inputManager()->canvas()->viewManager()->zoomController()->zoomAction();
if (!point.isNull()) {
float oldZoom = zoomAction->effectiveZoom();
float newZoom = zoomIn ?
zoomAction->nextZoomLevel() : zoomAction->prevZoomLevel();
KoCanvasControllerWidget *controller =
dynamic_cast(
q->inputManager()->canvas()->canvasController());
controller->zoomRelativeToPoint(point, newZoom / oldZoom);
} else {
if (zoomIn) {
zoomAction->zoomIn();
} else {
zoomAction->zoomOut();
}
}
}
KisZoomAction::KisZoomAction()
: KisAbstractInputAction("Zoom Canvas")
, d(new Private(this))
{
setName(i18n("Zoom Canvas"));
setDescription(i18n("The Zoom Canvas action zooms the canvas."));
QHash< QString, int > shortcuts;
shortcuts.insert(i18n("Zoom Mode"), ZoomModeShortcut);
shortcuts.insert(i18n("Discrete Zoom Mode"), DiscreteZoomModeShortcut);
shortcuts.insert(i18n("Relative Zoom Mode"), RelativeZoomModeShortcut);
shortcuts.insert(i18n("Relative Discrete Zoom Mode"), RelativeDiscreteZoomModeShortcut);
shortcuts.insert(i18n("Zoom In"), ZoomInShortcut);
shortcuts.insert(i18n("Zoom Out"), ZoomOutShortcut);
shortcuts.insert(i18n("Reset Zoom to 100%"), ZoomResetShortcut);
shortcuts.insert(i18n("Fit to Page"), ZoomToPageShortcut);
shortcuts.insert(i18n("Fit to Width"), ZoomToWidthShortcut);
setShortcutIndexes(shortcuts);
}
KisZoomAction::~KisZoomAction()
{
delete d;
}
int KisZoomAction::priority() const
{
return 4;
}
void KisZoomAction::activate(int shortcut)
{
if (shortcut == DiscreteZoomModeShortcut ||
shortcut == RelativeDiscreteZoomModeShortcut) {
QApplication::setOverrideCursor(KisCursor::zoomDiscreteCursor());
} else /* if (shortcut == SmoothZoomModeShortcut) */ {
QApplication::setOverrideCursor(KisCursor::zoomSmoothCursor());
}
}
void KisZoomAction::deactivate(int shortcut)
{
Q_UNUSED(shortcut);
QApplication::restoreOverrideCursor();
}
void KisZoomAction::begin(int shortcut, QEvent *event)
{
KisAbstractInputAction::begin(shortcut, event);
d->lastDistance = 0.f;
switch(shortcut) {
case ZoomModeShortcut:
case RelativeZoomModeShortcut: {
d->startPoint = pointFromEvent(event);
d->mode = (Shortcuts)shortcut;
QTouchEvent *tevent = dynamic_cast(event);
if(tevent)
d->lastPosition = d->centerPoint(tevent);
break;
}
case DiscreteZoomModeShortcut:
case RelativeDiscreteZoomModeShortcut:
d->startPoint = pointFromEvent(event);
d->mode = (Shortcuts)shortcut;
d->distance = 0;
break;
case ZoomInShortcut:
d->zoomTo(true, pointFromEvent(event));
break;
case ZoomOutShortcut:
d->zoomTo(false, pointFromEvent(event));
break;
case ZoomResetShortcut:
inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, 1.0);
break;
case ZoomToPageShortcut:
inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_PAGE, 1.0);
break;
case ZoomToWidthShortcut:
inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_WIDTH, 1.0);
break;
}
}
void KisZoomAction::inputEvent( QEvent* event )
{
switch (event->type()) {
case QEvent::TouchUpdate: {
QTouchEvent *tevent = static_cast(event);
QPointF center = d->centerPoint(tevent);
int count = 0;
float dist = 0.0f;
Q_FOREACH (const QTouchEvent::TouchPoint &point, tevent->touchPoints()) {
if (point.state() != Qt::TouchPointReleased) {
count++;
- dist += (point.screenPos() - center).manhattanLength();
+ dist += (point.pos() - center).manhattanLength();
}
}
dist /= count;
float delta = qFuzzyCompare(1.0f, 1.0f + d->lastDistance) ? 1.f : dist / d->lastDistance;
if(qAbs(delta) > 0.1f) {
qreal zoom = inputManager()->canvas()->viewManager()->zoomController()->zoomAction()->effectiveZoom();
Q_UNUSED(zoom);
static_cast(inputManager()->canvas()->canvasController())->zoomRelativeToPoint(center.toPoint(), delta);
d->lastDistance = dist;
// Also do panning here, as doing it later requires a further check for validity
QPointF moveDelta = center - d->lastPosition;
inputManager()->canvas()->canvasController()->pan(-moveDelta.toPoint());
d->lastPosition = center;
}
return; // Don't try to update the cursor during a pinch-zoom
}
case QEvent::NativeGesture: {
QNativeGestureEvent *gevent = static_cast(event);
if (gevent->gestureType() == Qt::ZoomNativeGesture) {
KisCanvas2 *canvas = inputManager()->canvas();
KisCanvasController *controller = static_cast(canvas->canvasController());
const float delta = 1.0f + gevent->value();
controller->zoomRelativeToPoint(canvas->canvasWidget()->mapFromGlobal(gevent->globalPos()), delta);
} else if (gevent->gestureType() == Qt::SmartZoomNativeGesture) {
KisCanvas2 *canvas = inputManager()->canvas();
KoZoomController *controller = canvas->viewManager()->zoomController();
if (controller->zoomMode() != KoZoomMode::ZOOM_WIDTH) {
controller->setZoom(KoZoomMode::ZOOM_WIDTH, 1.0);
} else {
controller->setZoom(KoZoomMode::ZOOM_CONSTANT, 1.0);
}
}
return;
}
default:
break;
}
KisAbstractInputAction::inputEvent(event);
}
void KisZoomAction::cursorMoved(const QPointF &lastPos, const QPointF &pos)
{
QPointF diff = -(pos - lastPos);
const int stepCont = 100;
const int stepDisc = 20;
if (d->mode == ZoomModeShortcut ||
d->mode == RelativeZoomModeShortcut) {
KisConfig cfg;
float coeff;
if (cfg.readEntry("InvertMiddleClickZoom", false)) {
coeff = 1.0 - qreal(diff.y()) / stepCont;
}
else {
coeff = 1.0 + qreal(diff.y()) / stepCont;
}
if (d->mode == ZoomModeShortcut) {
float zoom = coeff * inputManager()->canvas()->viewManager()->zoomController()->zoomAction()->effectiveZoom();
inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, zoom);
} else {
KoCanvasControllerWidget *controller =
dynamic_cast(
inputManager()->canvas()->canvasController());
controller->zoomRelativeToPoint(d->startPoint, coeff);
}
} else if (d->mode == DiscreteZoomModeShortcut ||
d->mode == RelativeDiscreteZoomModeShortcut) {
d->distance += diff.y();
QPoint stillPoint = d->mode == RelativeDiscreteZoomModeShortcut ?
d->startPoint : QPoint();
bool zoomIn = d->distance > 0;
while (qAbs(d->distance) > stepDisc) {
d->zoomTo(zoomIn, stillPoint);
d->distance += zoomIn ? -stepDisc : stepDisc;
}
}
}
bool KisZoomAction::isShortcutRequired(int shortcut) const
{
return shortcut == ZoomModeShortcut;
}
diff --git a/libs/ui/input/wintab/kis_tablet_support_win8.cpp b/libs/ui/input/wintab/kis_tablet_support_win8.cpp
index 53bb88a43d..771e5af04f 100644
--- a/libs/ui/input/wintab/kis_tablet_support_win8.cpp
+++ b/libs/ui/input/wintab/kis_tablet_support_win8.cpp
@@ -1,994 +1,1081 @@
/*
* Copyright (c) 2017 Alvin Wong
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// Get Windows 8 API prototypes and types
#ifdef WINVER
# undef WINVER
#endif
#ifdef _WIN32_WINNT
# undef _WIN32_WINNT
#endif
#define WINVER 0x0602
#define _WIN32_WINNT 0x0602
#include "kis_tablet_support_win8.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef Q_OS_WIN
# error This file must not be compiled for non-Windows systems
#endif
namespace
{
class Win8PointerInputApi
{
#define WIN8_POINTER_INPUT_API_LIST(FUNC) \
/* Pointer Input Functions */ \
FUNC(GetPointerPenInfo) \
FUNC(GetPointerPenInfoHistory) \
FUNC(GetPointerType) \
/* Pointer Device Functions */ \
/*FUNC(GetPointerDevices)*/ \
/*FUNC(GetPointerDeviceProperties)*/ \
FUNC(GetPointerDevice) \
FUNC(GetPointerDeviceRects) \
/*FUNC(RegisterPointerDeviceNotifications)*/ \
/* end */
bool m_loaded;
public:
#define DEFINE_FP_FROM_WINAPI(func) \
public: using p ## func ## _t = std::add_pointer::type; \
private: p ## func ## _t m_p ## func = nullptr; \
public: const p ## func ## _t &func = m_p ## func; // const fp ref to member
WIN8_POINTER_INPUT_API_LIST(DEFINE_FP_FROM_WINAPI)
#undef DEFINE_FP_FROM_WINAPI
public:
Win8PointerInputApi()
: m_loaded(false)
{
}
bool init() {
if (m_loaded) {
return true;
}
QLibrary user32Lib("user32");
if (!user32Lib.load()) {
qWarning() << "Failed to load user32.dll! This really should not happen.";
return false;
}
#define LOAD_AND_CHECK_FP_FROM_WINAPI(func) \
m_p ## func = reinterpret_cast(user32Lib.resolve(#func)); \
if (!m_p ## func) { \
dbgTablet << "Failed to load function " #func " from user32.dll"; \
return false; \
}
WIN8_POINTER_INPUT_API_LIST(LOAD_AND_CHECK_FP_FROM_WINAPI)
#undef LOAD_AND_CHECK_FP_FROM_WINAPI
dbgTablet << "Loaded Windows 8 Pointer Input API functions";
m_loaded = true;
return true;
}
bool isLoaded() {
return m_loaded;
}
#undef WIN8_POINTER_INPUT_API_LIST
}; // class Win8PointerInputApi
Win8PointerInputApi api;
class PointerFlagsWrapper
{
const POINTER_FLAGS f;
public:
PointerFlagsWrapper(POINTER_FLAGS flags)
: f(flags)
{}
static PointerFlagsWrapper fromPointerInfo(const POINTER_INFO &pointerInfo) {
return PointerFlagsWrapper(pointerInfo.pointerFlags);
}
static PointerFlagsWrapper fromPenInfo(const POINTER_PEN_INFO &penInfo) {
return fromPointerInfo(penInfo.pointerInfo);
}
bool isNew() const {
return f & POINTER_FLAG_NEW;
}
bool isInRange() const {
return f & POINTER_FLAG_INRANGE;
}
bool isInContact() const {
return f & POINTER_FLAG_INCONTACT;
}
bool isFirstButtonDown() const {
return f & POINTER_FLAG_FIRSTBUTTON;
}
bool isSecondButtonDown() const {
return f & POINTER_FLAG_SECONDBUTTON;
}
bool isThirdButtonDown() const {
return f & POINTER_FLAG_THIRDBUTTON;
}
bool isForthButtonDown() const {
return f & POINTER_FLAG_FOURTHBUTTON;
}
bool isFifthButtonDown() const {
return f & POINTER_FLAG_FIFTHBUTTON;
}
bool isPrimary() const {
return f & POINTER_FLAG_PRIMARY;
}
bool isConfidence() const {
return f & POINTER_FLAG_CONFIDENCE;
}
bool isCancelled() const {
return f & POINTER_FLAG_CANCELED;
}
bool isDown() const {
return f & POINTER_FLAG_DOWN;
}
bool isUpdate() const {
return f & POINTER_FLAG_UPDATE;
}
bool isUp() const {
return f & POINTER_FLAG_UP;
}
bool isWheel() const {
return f & POINTER_FLAG_WHEEL;
}
bool isHWheel() const {
return f & POINTER_FLAG_HWHEEL;
}
bool isCaptureChanged() const {
return f & POINTER_FLAG_CAPTURECHANGED;
}
bool hasTransform() const {
// mingw-w64 headers is missing this flag
// return f & POINTER_FLAG_HASTRANSFORM;
return f & 0x00400000;
}
}; // class PointerFlagsWrapper
class PenFlagsWrapper
{
const PEN_FLAGS f;
public:
PenFlagsWrapper(PEN_FLAGS flags)
: f(flags)
{}
static PenFlagsWrapper fromPenInfo(const POINTER_PEN_INFO &penInfo) {
return PenFlagsWrapper(penInfo.penFlags);
}
bool isBarrelPressed() const {
return f & PEN_FLAG_BARREL;
}
bool isInverted() const {
return f & PEN_FLAG_INVERTED;
}
bool isEraserPressed() const {
return f & PEN_FLAG_ERASER;
}
}; // class PenFlagsWrapper
class PenMaskWrapper
{
const PEN_MASK f;
public:
PenMaskWrapper(PEN_MASK mask)
:f(mask)
{}
static PenMaskWrapper fromPenInfo(const POINTER_PEN_INFO &penInfo) {
return PenMaskWrapper(penInfo.penMask);
}
bool pressureValid() const {
return f & PEN_MASK_PRESSURE;
}
bool rotationValid() const {
return f & PEN_MASK_ROTATION;
}
bool tiltXValid() const {
return f & PEN_MASK_TILT_X;
}
bool tiltYValid() const {
return f & PEN_MASK_TILT_Y;
}
}; // class PenMaskWrapper
struct PointerDeviceItem
{
// HANDLE handle;
// RECT pointerDeviceRect;
// RECT displayRect;
qreal himetricToPixelX;
qreal himetricToPixelY;
qreal pixelOffsetX;
qreal pixelOffsetY;
DISPLAYCONFIG_ROTATION deviceOrientation; // This is needed to fix tilt
};
QHash penDevices;
struct PenPointerItem
{
// int pointerId;
// POINTER_PEN_INFO penInfo;
HWND hwnd;
HANDLE deviceHandle;
QPointer activeWidget; // Current widget receiving events
qreal oneOverDpr; // 1 / devicePixelRatio of activeWidget
bool widgetIsCaptured; // Current widget is capturing a pen cown event
bool widgetIsIgnored; // Pen events should be ignored until pen up
+ bool widgetAcceptsPenEvent; // Whether the widget accepts pen events
bool isCaptured() const {
return widgetIsCaptured;
}
};
QHash penPointers;
// int primaryPenPointerId;
bool handlePointerMsg(const MSG &msg);
// extern "C" {
//
// LRESULT CALLBACK pointerDeviceNotificationsWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
// {
// switch (uMsg) {
// case WM_POINTERDEVICECHANGE:
// dbgTablet << "I would want to handle this WM_POINTERDEVICECHANGE event, but ms just doesn't want me to use it";
// dbgTablet << " wParam:" << wParam;
// dbgTablet << " lParam:" << lParam;
// return 0;
// case WM_POINTERDEVICEINRANGE:
// dbgTablet << "I would want to handle this WM_POINTERDEVICEINRANGE event, but ms just doesn't want me to use it";
// dbgTablet << " wParam:" << wParam;
// dbgTablet << " lParam:" << lParam;
// return 0;
// case WM_POINTERDEVICEOUTOFRANGE:
// dbgTablet << "I would want to handle this WM_POINTERDEVICEOUTOFRANGE event, but ms just doesn't want me to use it";
// dbgTablet << " wParam:" << wParam;
// dbgTablet << " lParam:" << lParam;
// return 0;
// }
// return DefWindowProcW(hwnd, uMsg, wParam, lParam);
// }
//
// } // extern "C"
} // namespace
bool KisTabletSupportWin8::isAvailable()
{
// Just try loading the APIs
return api.init();
}
bool KisTabletSupportWin8::init()
{
return api.init();
}
// void KisTabletSupportWin8::registerPointerDeviceNotifications()
// {
// const wchar_t *className = L"w8PointerMsgWindow";
// HINSTANCE hInst = static_cast(GetModuleHandleW(nullptr));
// WNDCLASSEXW wc;
// wc.cbSize = sizeof(WNDCLASSEXW);
// wc.style = 0;
// wc.lpfnWndProc = pointerDeviceNotificationsWndProc;
// wc.cbClsExtra = 0;
// wc.cbWndExtra = 0;
// wc.hInstance = hInst;
// wc.hCursor = 0;
// wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
// wc.hIcon = 0;
// wc.hIconSm = 0;
// wc.lpszMenuName = 0;
// wc.lpszClassName = className;
//
// if (RegisterClassEx(&wc)) {
// HWND hwnd = CreateWindowEx(0, className, nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, hInst, nullptr);
// api.RegisterPointerDeviceNotifications(hwnd, TRUE);
// } else {
// dbgTablet << "Cannot register dummy window";
// }
// }
bool KisTabletSupportWin8::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
if (!result) {
// I don't know why this even happens, but it actually does
// And the same event is sent in again with result != nullptr
return false;
}
// This is only installed on Windows so there is no reason to check eventType
MSG &msg = *static_cast(message);
switch (msg.message) {
case WM_POINTERDOWN:
case WM_POINTERUP:
case WM_POINTERENTER:
case WM_POINTERLEAVE:
case WM_POINTERUPDATE:
case WM_POINTERCAPTURECHANGED:
{
bool handled = handlePointerMsg(msg);
if (handled) {
*result = 0;
return true;
}
break;
}
case WM_TABLET_QUERYSYSTEMGESTURESTATUS:
*result = 0;
return true;
}
Q_UNUSED(eventType)
return false;
}
namespace {
QDebug operator<<(QDebug debug, const POINT &pt)
{
QDebugStateSaver saver(debug);
debug.nospace() << '(' << pt.x << ", " << pt.y << ')';
return debug;
}
QDebug operator<<(QDebug debug, const RECT &rect)
{
QDebugStateSaver saver(debug);
debug.nospace() << '(' << rect.left << ", " << rect.top << ", " << rect.right << ", " << rect.bottom << ')';
return debug;
}
bool registerOrUpdateDevice(HANDLE deviceHandle, const RECT &pointerDeviceRect, const RECT &displayRect, const DISPLAYCONFIG_ROTATION deviceOrientation)
{
bool isPreviouslyRegistered = penDevices.contains(deviceHandle);
PointerDeviceItem &deviceItem = penDevices[deviceHandle];
PointerDeviceItem oldDeviceItem = deviceItem;
// deviceItem.handle = deviceHandle;
deviceItem.himetricToPixelX =
static_cast(displayRect.right - displayRect.left)
/ (pointerDeviceRect.right - pointerDeviceRect.left);
deviceItem.himetricToPixelY =
static_cast(displayRect.bottom - displayRect.top)
/ (pointerDeviceRect.bottom - pointerDeviceRect.top);
deviceItem.pixelOffsetX = static_cast(displayRect.left)
- deviceItem.himetricToPixelX * pointerDeviceRect.left;
deviceItem.pixelOffsetY = static_cast(displayRect.top)
- deviceItem.himetricToPixelY * pointerDeviceRect.top;
deviceItem.deviceOrientation = deviceOrientation;
if (!isPreviouslyRegistered) {
dbgTablet << "Registered pen device" << deviceHandle
<< "with displayRect" << displayRect
<< "and deviceRect" << pointerDeviceRect
<< "scale" << deviceItem.himetricToPixelX << deviceItem.himetricToPixelY
<< "offset" << deviceItem.pixelOffsetX << deviceItem.pixelOffsetY
<< "orientation" << deviceItem.deviceOrientation;
} else if (deviceItem.himetricToPixelX != oldDeviceItem.himetricToPixelX
|| deviceItem.himetricToPixelY != oldDeviceItem.himetricToPixelY
|| deviceItem.pixelOffsetX != oldDeviceItem.pixelOffsetX
|| deviceItem.pixelOffsetY != oldDeviceItem.pixelOffsetY
|| deviceItem.deviceOrientation != oldDeviceItem.deviceOrientation) {
dbgTablet << "Updated pen device" << deviceHandle
<< "with displayRect" << displayRect
<< "and deviceRect" << pointerDeviceRect
<< "scale" << deviceItem.himetricToPixelX << deviceItem.himetricToPixelY
<< "offset" << deviceItem.pixelOffsetX << deviceItem.pixelOffsetY
<< "orientation" << deviceItem.deviceOrientation;
}
return true;
}
bool registerOrUpdateDevice(HANDLE deviceHandle)
{
RECT pointerDeviceRect, displayRect;
if (!api.GetPointerDeviceRects(deviceHandle, &pointerDeviceRect, &displayRect)) {
dbgTablet << "GetPointerDeviceRects failed";
return false;
}
POINTER_DEVICE_INFO pointerDeviceInfo;
if (!api.GetPointerDevice(deviceHandle, &pointerDeviceInfo)) {
dbgTablet << "GetPointerDevice failed";
return false;
}
return registerOrUpdateDevice(deviceHandle, pointerDeviceRect, displayRect,
static_cast(pointerDeviceInfo.displayOrientation));
}
QTabletEvent makeProximityTabletEvent(const QEvent::Type eventType, const POINTER_PEN_INFO &penInfo)
{
PenFlagsWrapper penFlags = PenFlagsWrapper::fromPenInfo(penInfo);
QTabletEvent::PointerType pointerType = penFlags.isInverted() ? QTabletEvent::Eraser : QTabletEvent::Pen;
const QPointF emptyPoint;
return QTabletEvent(
eventType, // type
emptyPoint, // pos
emptyPoint, // globalPos
QTabletEvent::Stylus, // device
pointerType, // pointerType
0, // pressure
0, // xTilt
0, // yTilt
0, // tangentialPressure
0, // rotation
0, // z
Qt::NoModifier, // keyState
reinterpret_cast(penInfo.pointerInfo.sourceDevice), // uniqueID
Qt::NoButton, // button
(Qt::MouseButtons)0 // buttons
);
}
// void rotateTiltAngles(int &tiltX, int &tiltY, const DISPLAYCONFIG_ROTATION orientation) {
// int newTiltX, newTiltY;
// switch (orientation) {
// case DISPLAYCONFIG_ROTATION_ROTATE90:
// newTiltX = -tiltY;
// newTiltY = tiltX;
// break;
// case DISPLAYCONFIG_ROTATION_ROTATE180:
// newTiltX = -tiltX;
// newTiltY = -tiltY;
// break;
// case DISPLAYCONFIG_ROTATION_ROTATE270:
// newTiltX = tiltY;
// newTiltY = -tiltX;
// break;
// case DISPLAYCONFIG_ROTATION_IDENTITY:
// default:
// newTiltX = tiltX;
// newTiltY = tiltY;
// break;
// }
// tiltX = newTiltX;
// tiltY = newTiltY;
// }
QTabletEvent makePositionalTabletEvent(const QWidget *targetWidget, const QEvent::Type eventType, const POINTER_PEN_INFO &penInfo, const PointerDeviceItem &deviceItem, const PenPointerItem &penPointerItem)
{
PenFlagsWrapper penFlags = PenFlagsWrapper::fromPenInfo(penInfo);
PointerFlagsWrapper pointerFlags = PointerFlagsWrapper::fromPenInfo(penInfo);
PenMaskWrapper penMask = PenMaskWrapper::fromPenInfo(penInfo);
const QPointF globalPosF(
(deviceItem.himetricToPixelX * penInfo.pointerInfo.ptHimetricLocationRaw.x + deviceItem.pixelOffsetX) * penPointerItem.oneOverDpr,
(deviceItem.himetricToPixelY * penInfo.pointerInfo.ptHimetricLocationRaw.y + deviceItem.pixelOffsetY) * penPointerItem.oneOverDpr
);
const QPoint globalPos = globalPosF.toPoint();
const QPoint localPos = targetWidget->mapFromGlobal(globalPos);
const QPointF delta = globalPosF - globalPos;
const QPointF localPosF = localPos + delta;
const QTabletEvent::PointerType pointerType = penFlags.isInverted() ? QTabletEvent::Eraser : QTabletEvent::Pen;
Qt::MouseButton mouseButton;
if (eventType == QEvent::TabletPress) {
if (penInfo.pointerInfo.ButtonChangeType == POINTER_CHANGE_SECONDBUTTON_DOWN) {
mouseButton = Qt::RightButton;
} else {
KIS_SAFE_ASSERT_RECOVER(penInfo.pointerInfo.ButtonChangeType == POINTER_CHANGE_FIRSTBUTTON_DOWN) {
qWarning() << "WM_POINTER* sent unknown ButtonChangeType" << penInfo.pointerInfo.ButtonChangeType;
}
mouseButton = Qt::LeftButton;
}
} else if (eventType == QEvent::TabletRelease) {
if (penInfo.pointerInfo.ButtonChangeType == POINTER_CHANGE_SECONDBUTTON_UP) {
mouseButton = Qt::RightButton;
} else {
KIS_SAFE_ASSERT_RECOVER(penInfo.pointerInfo.ButtonChangeType == POINTER_CHANGE_FIRSTBUTTON_UP) {
qWarning() << "WM_POINTER* sent unknown ButtonChangeType" << penInfo.pointerInfo.ButtonChangeType;
}
mouseButton = Qt::LeftButton;
}
} else {
mouseButton = Qt::NoButton;
}
Qt::MouseButtons mouseButtons;
if (pointerFlags.isFirstButtonDown()) {
mouseButtons |= Qt::LeftButton;
}
if (pointerFlags.isSecondButtonDown()) {
mouseButtons |= Qt::RightButton;
}
int tiltX = 0, tiltY = 0;
if (penMask.tiltXValid()) {
tiltX = qBound(-60, penInfo.tiltX, 60);
}
if (penMask.tiltYValid()) {
tiltY = qBound(-60, penInfo.tiltY, 60);
}
// rotateTiltAngles(tiltX, tiltY, deviceItem.deviceOrientation);
int rotation = 0;
if (penMask.rotationValid()) {
rotation = 360 - penInfo.rotation; // Flip direction and convert to signed int
if (rotation > 180) {
rotation -= 360;
}
}
return QTabletEvent(
eventType, // type
localPosF, // pos
globalPosF, // globalPos
QTabletEvent::Stylus, // device
pointerType, // pointerType
penMask.pressureValid() ? static_cast(penInfo.pressure) / 1024 : 0, // pressure
tiltX, // xTilt
tiltY, // yTilt
0, // tangentialPressure
rotation, // rotation
0, // z
QApplication::queryKeyboardModifiers(), // keyState
reinterpret_cast(penInfo.pointerInfo.sourceDevice), // uniqueID
mouseButton, // button
mouseButtons // buttons
);
}
bool sendProximityTabletEvent(const QEvent::Type eventType, const POINTER_PEN_INFO &penInfo)
{
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(
eventType == QEvent::TabletEnterProximity || eventType == QEvent::TabletLeaveProximity,
false
);
QTabletEvent ev = makeProximityTabletEvent(eventType, penInfo);
ev.setAccepted(false);
ev.setTimestamp(penInfo.pointerInfo.dwTime);
QCoreApplication::sendEvent(qApp, &ev);
return ev.isAccepted();
}
-bool sendPositionalTabletEvent(QWidget *target, const QEvent::Type eventType, const POINTER_PEN_INFO &penInfo, const PointerDeviceItem &device, const PenPointerItem &penPointerItem)
+void synthesizeMouseEvent(const QTabletEvent &ev, const POINTER_PEN_INFO &penInfo)
+{
+ // Update the cursor position
+ BOOL result = SetCursorPos(penInfo.pointerInfo.ptPixelLocationRaw.x, penInfo.pointerInfo.ptPixelLocationRaw.y);
+ if (!result) {
+ dbgInput << "SetCursorPos failed, err" << GetLastError();
+ return;
+ }
+ // Send mousebutton down/up events. Windows stores the button state.
+ DWORD inputDataFlags = 0;
+ switch (ev.type()) {
+ case QEvent::TabletPress:
+ switch (ev.button()) {
+ case Qt::LeftButton:
+ inputDataFlags = MOUSEEVENTF_LEFTDOWN;
+ break;
+ case Qt::RightButton:
+ inputDataFlags = MOUSEEVENTF_RIGHTDOWN;
+ break;
+ default:
+ return;
+ }
+ break;
+ case QEvent::TabletRelease:
+ switch (ev.button()) {
+ case Qt::LeftButton:
+ inputDataFlags = MOUSEEVENTF_LEFTUP;
+ break;
+ case Qt::RightButton:
+ inputDataFlags = MOUSEEVENTF_RIGHTUP;
+ break;
+ default:
+ return;
+ }
+ break;
+ case QEvent::TabletMove:
+ default:
+ return;
+ }
+ INPUT inputData = {};
+ inputData.type = INPUT_MOUSE;
+ inputData.mi.dwFlags = inputDataFlags;
+ inputData.mi.dwExtraInfo = 0xFF515700 | 0x01; // https://msdn.microsoft.com/en-us/library/windows/desktop/ms703320%28v=vs.85%29.aspx
+ UINT result2 = SendInput(1, &inputData, sizeof(inputData));
+ if (result2 != 1) {
+ dbgInput << "SendInput failed, err" << GetLastError();
+ return;
+ }
+}
+
+bool sendPositionalTabletEvent(QWidget *target, const QEvent::Type eventType, const POINTER_PEN_INFO &penInfo, const PointerDeviceItem &device, const PenPointerItem &penPointerItem, const bool shouldSynthesizeMouseEvent)
{
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(
eventType == QEvent::TabletMove || eventType == QEvent::TabletPress || eventType == QEvent::TabletRelease,
false
);
QTabletEvent ev = makePositionalTabletEvent(target, eventType, penInfo, device, penPointerItem);
ev.setAccepted(false);
ev.setTimestamp(penInfo.pointerInfo.dwTime);
QCoreApplication::sendEvent(target, &ev);
- return ev.isAccepted();
+ if (!shouldSynthesizeMouseEvent) {
+ // For pen update with multiple updates, only the last update should
+ // be used to synthesize a mouse event.
+ return false;
+ }
+ // This is some specialized code to handle synthesizing of mouse events from
+ // the pen events. Issues being:
+ // 1. We must accept the pointer down/up and the intermediate update events
+ // to indicate that we want all the pen pointer events for painting,
+ // otherwise Windows may do weird stuff and skip passing pointer events.
+ // 2. Some Qt and Krita code uses QCursor::pos() which calls GetCursorPos to
+ // get the cursor position. This doesn't work nicely before ver 1709 and
+ // doesn't work at all on ver 1709 if the pen events are handled, so we
+ // need some way to nudge the cursor on the OS level.
+ // It appears that using the same way (as in synthesizeMouseEvent) to nudge
+ // the cursor does work fine for when painting on canvas (which handles
+ // the QTabletEvent), but not for other widgets because it introduces a lag
+ // with mouse move events on move start and immediately after mouse down.
+ // The resolution is to simulate mouse movement with our own code only for
+ // handled pen events, which is what the following code does.
+ if (ev.type() == QEvent::TabletMove && ev.buttons() == Qt::NoButton) {
+ // Let Windows synthesize mouse hover events
+ return false;
+ }
+ if (ev.type() == QEvent::TabletPress && !ev.isAccepted()) {
+ // On pen down event, if the widget doesn't handle the event, let
+ // Windows translate the event to touch, mouse or whatever
+ return false;
+ }
+ if (ev.type() != QEvent::TabletPress && !penPointerItem.widgetAcceptsPenEvent) {
+ // For other events, if the previous pen down event wasn't handled by
+ // the widget, continue to let Windows translate the event
+ return false;
+ }
+ // Otherwise, we synthesize our mouse events
+ synthesizeMouseEvent(ev, penInfo);
+ return true; // and tell Windows that we do want the pen events.
}
bool handlePenEnterMsg(const POINTER_PEN_INFO &penInfo)
{
PointerFlagsWrapper pointerFlags = PointerFlagsWrapper::fromPenInfo(penInfo);
if (!pointerFlags.isPrimary()) {
// Don't handle non-primary pointer messages for now
dbgTablet << "Pointer" << penInfo.pointerInfo.pointerId
<< "of device" << penInfo.pointerInfo.sourceDevice
<< "is not flagged PRIMARY";
return false;
}
// Update the device scaling factors here
// It doesn't cost much to recalculate anyway
// This ensures that the screen resolution changes are reflected
// WM_POINTERDEVICECHANGE might be useful for this, but its docs are too unclear to use
registerOrUpdateDevice(penInfo.pointerInfo.sourceDevice);
// TODO: Need a way to remove from device registration when devices are changed
// We now only handle one pointer at a time, so just clear the pointer registration
penPointers.clear();
int pointerId = penInfo.pointerInfo.pointerId;
PenPointerItem penPointerItem;
penPointerItem.hwnd = penInfo.pointerInfo.hwndTarget;
penPointerItem.deviceHandle = penInfo.pointerInfo.sourceDevice;
penPointerItem.activeWidget = nullptr;
penPointerItem.oneOverDpr = 1.0;
penPointerItem.widgetIsCaptured = false;
penPointerItem.widgetIsIgnored = false;
+ penPointerItem.widgetAcceptsPenEvent = false;
// penPointerItem.pointerId = pointerId;
penPointers.insert(pointerId, penPointerItem);
// primaryPenPointerId = pointerId;
// penEnter
sendProximityTabletEvent(QEvent::TabletEnterProximity, penInfo);
return false;
}
bool handlePenLeaveMsg(const POINTER_PEN_INFO &penInfo)
{
if (!penPointers.contains(penInfo.pointerInfo.pointerId)) {
dbgTablet << "Pointer" << penInfo.pointerInfo.pointerId << "wasn't being handled";
return false;
}
if (!penDevices.contains(penInfo.pointerInfo.sourceDevice)) {
dbgTablet << "Device is gone from the registration???";
// TODO: re-register device?
penPointers.remove(penInfo.pointerInfo.pointerId);
return false;
}
// penLeave
sendProximityTabletEvent(QEvent::TabletLeaveProximity, penInfo);
penPointers.remove(penInfo.pointerInfo.pointerId);
return false;
}
-bool handleSinglePenUpdate(PenPointerItem &penPointerItem, const POINTER_PEN_INFO &penInfo, const PointerDeviceItem &device)
+bool handleSinglePenUpdate(PenPointerItem &penPointerItem, const POINTER_PEN_INFO &penInfo, const PointerDeviceItem &device, const bool shouldSynthesizeMouseEvent)
{
QWidget *targetWidget;
if (penPointerItem.isCaptured()) {
if (penPointerItem.widgetIsIgnored) {
return false;
}
targetWidget = penPointerItem.activeWidget;
if (!targetWidget) {
return false;
}
} else {
QWidget *hwndWidget = QWidget::find(reinterpret_cast(penInfo.pointerInfo.hwndTarget));
if (!hwndWidget) {
dbgTablet << "HWND cannot be mapped to QWidget (what?)";
return false;
}
{
// Check popup / modal widget
QWidget *modalWidget = QApplication::activePopupWidget();
if (!modalWidget) {
modalWidget = QApplication::activeModalWidget();
}
if (modalWidget && modalWidget != hwndWidget && !modalWidget->isAncestorOf(hwndWidget)) {
return false;
}
}
{
QWindow *topLevelWindow = hwndWidget->windowHandle();
if (topLevelWindow) {
penPointerItem.oneOverDpr = 1.0 / topLevelWindow->devicePixelRatio();
} else {
penPointerItem.oneOverDpr = 1.0 / qApp->devicePixelRatio();
}
}
QPoint posInHwndWidget = hwndWidget->mapFromGlobal(QPoint(
static_cast(penInfo.pointerInfo.ptPixelLocationRaw.x * penPointerItem.oneOverDpr),
static_cast(penInfo.pointerInfo.ptPixelLocationRaw.y * penPointerItem.oneOverDpr)
));
targetWidget = hwndWidget->childAt(posInHwndWidget);
if (!targetWidget) {
// dbgTablet << "No childQWidget at cursor position";
targetWidget = hwndWidget;
}
// penPointerItem.activeWidget = targetWidget;
}
- bool handled = sendPositionalTabletEvent(targetWidget, QEvent::TabletMove, penInfo, device, penPointerItem);
- if (!handled) {
- // dbgTablet << "Target widget doesn't want pen events";
- }
+ bool handled = sendPositionalTabletEvent(targetWidget, QEvent::TabletMove, penInfo, device, penPointerItem, shouldSynthesizeMouseEvent);
return handled;
}
bool handlePenUpdateMsg(const POINTER_PEN_INFO &penInfo)
{
auto currentPointerIt = penPointers.find(penInfo.pointerInfo.pointerId);
if (currentPointerIt == penPointers.end()) {
// dbgTablet << "Pointer" << penInfo.pointerInfo.pointerId << "wasn't being handled";
return false;
}
const auto devIt = penDevices.find(penInfo.pointerInfo.sourceDevice);
if (devIt == penDevices.end()) {
dbgTablet << "Device not registered???";
return false;
}
// UINT32 entriesCount = 0;
// if (!api.GetPointerPenInfoHistory(penInfo.pointerInfo.pointerId, &entriesCount, nullptr)) {
// dbgTablet << "GetPointerPenInfoHistory (getting count) failed";
// return false;
// }
UINT32 entriesCount = penInfo.pointerInfo.historyCount;
// dbgTablet << "entriesCount:" << entriesCount;
+ bool handled = false;
if (entriesCount != 1) {
QVector penInfoArray(entriesCount);
if (!api.GetPointerPenInfoHistory(penInfo.pointerInfo.pointerId, &entriesCount, penInfoArray.data())) {
dbgTablet << "GetPointerPenInfoHistory failed";
return false;
}
bool handled = false;
// The returned array is in reverse chronological order
const auto rbegin = penInfoArray.rbegin();
const auto rend = penInfoArray.rend();
+ const auto rlast = rend - 1; // Only synthesize mouse event for the last one
for (auto it = rbegin; it != rend; ++it) {
- handled |= handleSinglePenUpdate(*currentPointerIt, *it, *devIt); // Bitwise OR doesn't short circuit
+ handled |= handleSinglePenUpdate(*currentPointerIt, *it, *devIt, it == rlast); // Bitwise OR doesn't short circuit
}
- return handled;
} else {
- return handleSinglePenUpdate(*currentPointerIt, penInfo, *devIt);
+ handled = handleSinglePenUpdate(*currentPointerIt, penInfo, *devIt, true);
}
+ return handled;
}
bool handlePenDownMsg(const POINTER_PEN_INFO &penInfo)
{
// PointerFlagsWrapper pointerFlags = PointerFlagsWrapper::fromPenInfo(penInfo);
// if (!pointerFlags.isPrimary()) {
// // Don't handle non-primary pointer messages for now
// return false;
// }
auto currentPointerIt = penPointers.find(penInfo.pointerInfo.pointerId);
if (currentPointerIt == penPointers.end()) {
dbgTablet << "Pointer" << penInfo.pointerInfo.pointerId << "wasn't being handled";
return false;
}
currentPointerIt->hwnd = penInfo.pointerInfo.hwndTarget; // They *should* be the same, but just in case
QWidget *hwndWidget = QWidget::find(reinterpret_cast(penInfo.pointerInfo.hwndTarget));
if (!hwndWidget) {
dbgTablet << "HWND cannot be mapped to QWidget (what?)";
return false;
}
{
QWindow *topLevelWindow = hwndWidget->windowHandle();
if (topLevelWindow) {
currentPointerIt->oneOverDpr = 1.0 / topLevelWindow->devicePixelRatio();
} else {
currentPointerIt->oneOverDpr = 1.0 / qApp->devicePixelRatio();
}
}
QPoint posInHwndWidget = hwndWidget->mapFromGlobal(QPoint(
static_cast(penInfo.pointerInfo.ptPixelLocationRaw.x * currentPointerIt->oneOverDpr),
static_cast(penInfo.pointerInfo.ptPixelLocationRaw.y * currentPointerIt->oneOverDpr)
));
QWidget *targetWidget = hwndWidget->childAt(posInHwndWidget);
if (!targetWidget) {
dbgTablet << "No childQWidget at cursor position";
targetWidget = hwndWidget;
}
currentPointerIt->activeWidget = targetWidget;
currentPointerIt->widgetIsCaptured = true;
// dbgTablet << "QWidget" << targetWidget->windowTitle() << "is capturing pointer" << penInfo.pointerInfo.pointerId;
{
// Check popup / modal widget
QWidget *modalWidget = QApplication::activePopupWidget();
if (!modalWidget) {
modalWidget = QApplication::activeModalWidget();
}
if (modalWidget && modalWidget != hwndWidget && !modalWidget->isAncestorOf(hwndWidget)) {
currentPointerIt->widgetIsIgnored = true;
dbgTablet << "Pointer" << penInfo.pointerInfo.pointerId << "is being captured but will be ignored";
return false;
}
}
// penDown
const auto devIt = penDevices.find(penInfo.pointerInfo.sourceDevice);
if (devIt == penDevices.end()) {
dbgTablet << "Device not registered???";
return false;
}
- bool handled = sendPositionalTabletEvent(targetWidget, QEvent::TabletPress, penInfo, *devIt, *currentPointerIt);
+ bool handled = sendPositionalTabletEvent(targetWidget, QEvent::TabletPress, penInfo, *devIt, *currentPointerIt, true);
+ currentPointerIt->widgetAcceptsPenEvent = handled;
if (!handled) {
// dbgTablet << "QWidget did not handle tablet down event";
}
return handled;
}
bool handlePenUpMsg(const POINTER_PEN_INFO &penInfo)
{
auto currentPointerIt = penPointers.find(penInfo.pointerInfo.pointerId);
if (currentPointerIt == penPointers.end()) {
dbgTablet << "Pointer" << penInfo.pointerInfo.pointerId << "wasn't being handled";
return false;
}
PenPointerItem &penPointerItem = *currentPointerIt;
if (!penPointerItem.isCaptured()) {
dbgTablet << "Pointer wasn't captured";
return false;
}
if (penPointerItem.widgetIsIgnored) {
penPointerItem.widgetIsCaptured = false;
penPointerItem.widgetIsIgnored = false;
return false;
}
// penUp
QWidget *targetWidget = penPointerItem.activeWidget;
if (!targetWidget) {
dbgTablet << "Previously captured target has been deleted";
penPointerItem.widgetIsCaptured = false;
return false;
}
const auto devIt = penDevices.find(penInfo.pointerInfo.sourceDevice);
if (devIt == penDevices.end()) {
dbgTablet << "Device not registered???";
return false;
}
- bool handled = sendPositionalTabletEvent(targetWidget, QEvent::TabletRelease, penInfo, *devIt, penPointerItem);
+ bool handled = sendPositionalTabletEvent(targetWidget, QEvent::TabletRelease, penInfo, *devIt, penPointerItem, true);
// dbgTablet << "QWidget" << currentPointerIt->activeWidget->windowTitle() << "is releasing capture to pointer" << penInfo.pointerInfo.pointerId;
penPointerItem.widgetIsCaptured = false;
+ penPointerItem.widgetAcceptsPenEvent = false;
return handled;
}
bool handlePointerMsg(const MSG &msg)
{
if (!api.isLoaded()) {
qWarning() << "Windows 8 Pointer Input API functions not loaded";
return false;
}
int pointerId = GET_POINTERID_WPARAM(msg.wParam);
POINTER_INPUT_TYPE pointerType;
if (!api.GetPointerType(pointerId, &pointerType)) {
dbgTablet << "GetPointerType failed";
return false;
}
if (pointerType != PT_PEN) {
// dbgTablet << "pointerType" << pointerType << "is not PT_PEN";
return false;
}
POINTER_PEN_INFO penInfo;
if (!api.GetPointerPenInfo(pointerId, &penInfo)) {
dbgTablet << "GetPointerPenInfo failed";
return false;
}
switch (msg.message) {
case WM_POINTERDOWN:
// dbgTablet << "WM_POINTERDOWN";
break;
case WM_POINTERUP:
// dbgTablet << "WM_POINTERUP";
break;
case WM_POINTERENTER:
// dbgTablet << "WM_POINTERENTER";
break;
case WM_POINTERLEAVE:
// dbgTablet << "WM_POINTERLEAVE";
break;
case WM_POINTERUPDATE:
// dbgTablet << "WM_POINTERUPDATE";
break;
case WM_POINTERCAPTURECHANGED:
// dbgTablet << "WM_POINTERCAPTURECHANGED";
break;
default:
dbgTablet << "I missed this message: " << msg.message;
break;
}
// dbgTablet << " hwnd: " << penInfo.pointerInfo.hwndTarget;
// dbgTablet << " msg hwnd: " << msg.hwnd;
// dbgTablet << " pointerId: " << pointerId;
// dbgTablet << " sourceDevice:" << penInfo.pointerInfo.sourceDevice;
// dbgTablet << " pointerFlags:" << penInfo.pointerInfo.pointerFlags;
// dbgTablet << " btnChgType: " << penInfo.pointerInfo.ButtonChangeType;
// dbgTablet << " penFlags: " << penInfo.penFlags;
// dbgTablet << " penMask: " << penInfo.penMask;
// dbgTablet << " pressure: " << penInfo.pressure;
// dbgTablet << " rotation: " << penInfo.rotation;
// dbgTablet << " tiltX: " << penInfo.tiltX;
// dbgTablet << " tiltY: " << penInfo.tiltY;
// dbgTablet << " ptPixelLocationRaw: " << penInfo.pointerInfo.ptPixelLocationRaw;
// dbgTablet << " ptHimetricLocationRaw:" << penInfo.pointerInfo.ptHimetricLocationRaw;
// RECT pointerDeviceRect, displayRect;
// if (!api.GetPointerDeviceRects(penInfo.pointerInfo.sourceDevice, &pointerDeviceRect, &displayRect)) {
// dbgTablet << "GetPointerDeviceRects failed";
// return false;
// }
// dbgTablet << " pointerDeviceRect:" << pointerDeviceRect;
// dbgTablet << " displayRect:" << displayRect;
// dbgTablet << " scaled X:" << static_cast(penInfo.pointerInfo.ptHimetricLocationRaw.x) / (pointerDeviceRect.right - pointerDeviceRect.left) * (displayRect.right - displayRect.left);
// dbgTablet << " scaled Y:" << static_cast(penInfo.pointerInfo.ptHimetricLocationRaw.y) / (pointerDeviceRect.bottom - pointerDeviceRect.top) * (displayRect.bottom - displayRect.top);
switch (msg.message) {
case WM_POINTERDOWN:
return handlePenDownMsg(penInfo);
case WM_POINTERUP:
return handlePenUpMsg(penInfo);
case WM_POINTERENTER:
return handlePenEnterMsg(penInfo);
case WM_POINTERLEAVE:
return handlePenLeaveMsg(penInfo);
case WM_POINTERUPDATE:
- // HACK: Force further processing to force Windows to generate mouse move events
- handlePenUpdateMsg(penInfo);
- return false;
+ return handlePenUpdateMsg(penInfo);
case WM_POINTERCAPTURECHANGED:
// TODO: Should this event be handled?
dbgTablet << "FIXME: WM_POINTERCAPTURECHANGED isn't handled";
break;
}
return false;
}
} // namespace
diff --git a/libs/ui/kis_painting_assistant.cc b/libs/ui/kis_painting_assistant.cc
index 7617852d40..04c949afe1 100644
--- a/libs/ui/kis_painting_assistant.cc
+++ b/libs/ui/kis_painting_assistant.cc
@@ -1,682 +1,747 @@
/*
* Copyright (c) 2008,2011 Cyrille Berger
* Copyright (c) 2010 Geoffry Song
+ * Copyright (c) 2017 Scott Petrovic
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include
#include
#include "kis_painting_assistant.h"
#include "kis_coordinates_converter.h"
#include "kis_debug.h"
#include
+#include "kis_tool.h"
#include
#include
#include
#include
#include
#include
#include
Q_GLOBAL_STATIC(KisPaintingAssistantFactoryRegistry, s_instance)
struct KisPaintingAssistantHandle::Private {
QList assistants;
char handle_type;
};
KisPaintingAssistantHandle::KisPaintingAssistantHandle(double x, double y) : QPointF(x, y), d(new Private)
{
}
+
KisPaintingAssistantHandle::KisPaintingAssistantHandle(QPointF p) : QPointF(p), d(new Private)
{
}
KisPaintingAssistantHandle::KisPaintingAssistantHandle(const KisPaintingAssistantHandle& rhs)
: QPointF(rhs)
, KisShared()
, d(new Private)
{
}
KisPaintingAssistantHandle& KisPaintingAssistantHandle::operator=(const QPointF & pt)
{
setX(pt.x());
setY(pt.y());
return *this;
}
void KisPaintingAssistantHandle::setType(char type)
{
d->handle_type = type;
}
char KisPaintingAssistantHandle::handleType()
{
return d->handle_type;
}
-
KisPaintingAssistantHandle::~KisPaintingAssistantHandle()
{
Q_ASSERT(d->assistants.empty());
delete d;
}
void KisPaintingAssistantHandle::registerAssistant(KisPaintingAssistant* assistant)
{
Q_ASSERT(!d->assistants.contains(assistant));
d->assistants.append(assistant);
}
void KisPaintingAssistantHandle::unregisterAssistant(KisPaintingAssistant* assistant)
{
d->assistants.removeOne(assistant);
Q_ASSERT(!d->assistants.contains(assistant));
}
bool KisPaintingAssistantHandle::containsAssistant(KisPaintingAssistant* assistant)
{
return d->assistants.contains(assistant);
}
void KisPaintingAssistantHandle::mergeWith(KisPaintingAssistantHandleSP handle)
{
- if(this->handleType()=='S' || handle.data()->handleType()== 'S')
+ if(this->handleType()== HandleType::NORMAL || handle.data()->handleType()== HandleType::SIDE) {
return;
+ }
+
+
Q_FOREACH (KisPaintingAssistant* assistant, handle->d->assistants) {
if (!assistant->handles().contains(this)) {
assistant->replaceHandle(handle, this);
}
}
}
void KisPaintingAssistantHandle::uncache()
{
Q_FOREACH (KisPaintingAssistant* assistant, d->assistants) {
assistant->uncache();
}
}
struct KisPaintingAssistant::Private {
QString id;
QString name;
- bool snapping;
+ bool isSnappingActive;
bool outlineVisible;
QList handles,sideHandles;
QPixmapCache::Key cached;
QRect cachedRect; // relative to boundingRect().topLeft()
KisPaintingAssistantHandleSP topLeft, bottomLeft, topRight, bottomRight, topMiddle, bottomMiddle, rightMiddle, leftMiddle;
+ KisCanvas2* m_canvas = 0;
+
+
struct TranslationInvariantTransform {
qreal m11, m12, m21, m22;
TranslationInvariantTransform() { }
TranslationInvariantTransform(const QTransform& t) : m11(t.m11()), m12(t.m12()), m21(t.m21()), m22(t.m22()) { }
bool operator==(const TranslationInvariantTransform& b) {
return m11 == b.m11 && m12 == b.m12 && m21 == b.m21 && m22 == b.m22;
}
} cachedTransform;
-};
-KisPaintingAssistant::KisPaintingAssistant(const QString& id, const QString& name) : d(new Private)
-{
- d->id = id;
- d->name = name;
- d->snapping=true;
- d->outlineVisible=true;
-}
+ QColor assistantColor;
+};
-bool KisPaintingAssistant::snapping() const
+void KisPaintingAssistant::setAssistantColor(QColor color)
{
- return d->snapping;
+ d->assistantColor = color;
}
-void KisPaintingAssistant::setSnapping(bool set)
+KisPaintingAssistant::KisPaintingAssistant(const QString& id, const QString& name) : d(new Private)
{
- d->snapping=set;
+ d->id = id;
+ d->name = name;
+ d->isSnappingActive = true;
+ d->outlineVisible = true;
}
-bool KisPaintingAssistant::outline() const
+bool KisPaintingAssistant::isSnappingActive() const
{
- return d->outlineVisible;
+ return d->isSnappingActive;
}
-void KisPaintingAssistant::setOutline(bool set)
+void KisPaintingAssistant::setSnappingActive(bool set)
{
- d->outlineVisible=set;
+ d->isSnappingActive = set;
}
-void KisPaintingAssistant::drawPath(QPainter& painter, const QPainterPath &path, bool drawActive)
+void KisPaintingAssistant::drawPath(QPainter& painter, const QPainterPath &path, bool isSnappingOn)
{
- int alpha=100;
- if (!drawActive) {
- alpha=20;
+ int alpha = d->assistantColor.alpha();
+ if (!isSnappingOn) {
+ alpha = d->assistantColor.alpha() *0.2;
}
+
painter.save();
- QPen pen_a(QColor(0, 0, 0, alpha), 2);
+ QPen pen_a(QColor(d->assistantColor.red(), d->assistantColor.green(), d->assistantColor.blue(), alpha), 2);
pen_a.setCosmetic(true);
painter.setPen(pen_a);
painter.drawPath(path);
- QPen pen_b(QColor(255, 255, 255, alpha), 0.9);
- pen_b.setCosmetic(true);
- painter.setPen(pen_b);
- painter.drawPath(path);
painter.restore();
}
void KisPaintingAssistant::drawPreview(QPainter& painter, const QPainterPath &path)
{
painter.save();
- QPen pen_a(QColor(0, 0, 0, 50), 1);
+ QPen pen_a(d->assistantColor, 1);
pen_a.setStyle(Qt::SolidLine);
pen_a.setCosmetic(true);
painter.setPen(pen_a);
painter.drawPath(path);
- QPen pen_b(QColor(255, 255, 255, 50), 1);
- pen_b.setStyle(Qt::DotLine);
- pen_b.setCosmetic(true);
- painter.setPen(pen_b);
- painter.drawPath(path);
painter.restore();
}
void KisPaintingAssistant::initHandles(QList _handles)
{
Q_ASSERT(d->handles.isEmpty());
d->handles = _handles;
Q_FOREACH (KisPaintingAssistantHandleSP handle, _handles) {
handle->registerAssistant(this);
}
}
KisPaintingAssistant::~KisPaintingAssistant()
{
Q_FOREACH (KisPaintingAssistantHandleSP handle, d->handles) {
handle->unregisterAssistant(this);
}
if(!d->sideHandles.isEmpty()) {
Q_FOREACH (KisPaintingAssistantHandleSP handle, d->sideHandles) {
handle->unregisterAssistant(this);
}
}
delete d;
}
const QString& KisPaintingAssistant::id() const
{
return d->id;
}
const QString& KisPaintingAssistant::name() const
{
return d->name;
}
void KisPaintingAssistant::replaceHandle(KisPaintingAssistantHandleSP _handle, KisPaintingAssistantHandleSP _with)
{
Q_ASSERT(d->handles.contains(_handle));
d->handles.replace(d->handles.indexOf(_handle), _with);
Q_ASSERT(!d->handles.contains(_handle));
_handle->unregisterAssistant(this);
_with->registerAssistant(this);
}
-void KisPaintingAssistant::addHandle(KisPaintingAssistantHandleSP handle)
+void KisPaintingAssistant::addHandle(KisPaintingAssistantHandleSP handle, HandleType type)
{
Q_ASSERT(!d->handles.contains(handle));
- d->handles.append(handle);
- handle->registerAssistant(this);
- handle.data()->setType('H');
-}
+ if (HandleType::SIDE == type) {
+ d->sideHandles.append(handle);
+ } else {
+ d->handles.append(handle);
+ }
-void KisPaintingAssistant::addSideHandle(KisPaintingAssistantHandleSP handle)
-{
- Q_ASSERT(!d->sideHandles.contains(handle));
- d->sideHandles.append(handle);
handle->registerAssistant(this);
- handle.data()->setType('S');
+ handle.data()->setType(type);
}
+
void KisPaintingAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool useCache,KisCanvas2* canvas, bool assistantVisible, bool previewVisible)
{
Q_UNUSED(updateRect);
- Q_UNUSED(canvas);
Q_UNUSED(previewVisible);
- findHandleLocation();
+
+ findPerspectiveAssistantHandleLocation();
+
if (!useCache) {
gc.save();
drawCache(gc, converter, assistantVisible);
gc.restore();
return;
}
+
const QRect bound = boundingRect();
- if (bound.isEmpty()) return;
+ if (bound.isEmpty()) {
+ return;
+ }
+
const QTransform transform = converter->documentToWidgetTransform();
const QRect widgetBound = transform.mapRect(bound);
const QRect paintRect = transform.mapRect(bound).intersected(gc.viewport());
if (paintRect.isEmpty()) return;
QPixmap cached;
bool found = QPixmapCache::find(d->cached, &cached);
- if (!(found &&
- d->cachedTransform == transform &&
- d->cachedRect.translated(widgetBound.topLeft()).contains(paintRect))) {
+ if (!(found && d->cachedTransform == transform && d->cachedRect.translated(widgetBound.topLeft()).contains(paintRect))) {
+
const QRect cacheRect = gc.viewport().adjusted(-100, -100, 100, 100).intersected(widgetBound);
Q_ASSERT(!cacheRect.isEmpty());
+
if (cached.isNull() || cached.size() != cacheRect.size()) {
cached = QPixmap(cacheRect.size());
}
+
cached.fill(Qt::transparent);
QPainter painter(&cached);
painter.setRenderHint(QPainter::Antialiasing);
painter.setWindow(cacheRect);
drawCache(painter, converter, assistantVisible);
painter.end();
d->cachedTransform = transform;
d->cachedRect = cacheRect.translated(-widgetBound.topLeft());
d->cached = QPixmapCache::insert(cached);
}
+
gc.drawPixmap(paintRect, cached, paintRect.translated(-widgetBound.topLeft() - d->cachedRect.topLeft()));
+
+
+ if (canvas) {
+ d->m_canvas = canvas;
+ }
}
void KisPaintingAssistant::uncache()
{
d->cached = QPixmapCache::Key();
}
QRect KisPaintingAssistant::boundingRect() const
{
QRectF r;
Q_FOREACH (KisPaintingAssistantHandleSP h, handles()) {
r = r.united(QRectF(*h, QSizeF(1,1)));
}
return r.adjusted(-2, -2, 2, 2).toAlignedRect();
}
+bool KisPaintingAssistant::isAssistantComplete() const
+{
+ return true;
+}
+
QByteArray KisPaintingAssistant::saveXml(QMap &handleMap)
{
- QByteArray data;
- QXmlStreamWriter xml(&data);
- xml.writeStartDocument();
- xml.writeStartElement("assistant");
- xml.writeAttribute("type",d->id);
- xml.writeStartElement("handles");
- Q_FOREACH (const KisPaintingAssistantHandleSP handle, d->handles) {
- int id = handleMap.size();
- if (!handleMap.contains(handle)){
- handleMap.insert(handle, id);
- }
- id = handleMap.value(handle);
- xml.writeStartElement("handle");
- xml.writeAttribute("id", QString::number(id));
- xml.writeAttribute("x", QString::number(double(handle->x()), 'f', 3));
- xml.writeAttribute("y", QString::number(double(handle->y()), 'f', 3));
- xml.writeEndElement();
- }
- xml.writeEndElement();
- xml.writeEndElement();
- xml.writeEndDocument();
- return data;
+ QByteArray data;
+ QXmlStreamWriter xml(&data);
+ xml.writeStartDocument();
+ xml.writeStartElement("assistant");
+ xml.writeAttribute("type",d->id);
+ xml.writeAttribute("active", QString::number(d->isSnappingActive));
+ xml.writeStartElement("handles");
+ Q_FOREACH (const KisPaintingAssistantHandleSP handle, d->handles) {
+ int id = handleMap.size();
+ if (!handleMap.contains(handle)){
+ handleMap.insert(handle, id);
+ }
+ id = handleMap.value(handle);
+ xml.writeStartElement("handle");
+ xml.writeAttribute("id", QString::number(id));
+ xml.writeAttribute("x", QString::number(double(handle->x()), 'f', 3));
+ xml.writeAttribute("y", QString::number(double(handle->y()), 'f', 3));
+ xml.writeEndElement();
+ }
+ xml.writeEndElement();
+ xml.writeEndElement();
+ xml.writeEndDocument();
+ return data;
}
void KisPaintingAssistant::loadXml(KoStore* store, QMap &handleMap, QString path)
{
int id;
double x,y ;
store->open(path);
QByteArray data = store->read(store->size());
QXmlStreamReader xml(data);
while (!xml.atEnd()) {
switch (xml.readNext()) {
case QXmlStreamReader::StartElement:
+ if (xml.name() == "assistant") {
+ QStringRef active = xml.attributes().value("active");
+ d->isSnappingActive = (active != "0");
+ }
+
if (xml.name() == "handle") {
QString strId = xml.attributes().value("id").toString(),
- strX = xml.attributes().value("x").toString(),
- strY = xml.attributes().value("y").toString();
+ strX = xml.attributes().value("x").toString(),
+ strY = xml.attributes().value("y").toString();
if (!strId.isEmpty() && !strX.isEmpty() && !strY.isEmpty()) {
id = strId.toInt();
x = strX.toDouble();
y = strY.toDouble();
if (!handleMap.contains(id)) {
handleMap.insert(id, new KisPaintingAssistantHandle(x, y));
}
}
- addHandle(handleMap.value(id));
+ addHandle(handleMap.value(id), HandleType::NORMAL);
}
break;
default:
break;
}
}
store->close();
}
void KisPaintingAssistant::saveXmlList(QDomDocument& doc, QDomElement& assistantsElement,int count)
{
if (d->id == "ellipse"){
QDomElement assistantElement = doc.createElement("assistant");
assistantElement.setAttribute("type", "ellipse");
assistantElement.setAttribute("filename", QString("ellipse%1.assistant").arg(count));
assistantsElement.appendChild(assistantElement);
}
else if (d->id == "spline"){
QDomElement assistantElement = doc.createElement("assistant");
assistantElement.setAttribute("type", "spline");
assistantElement.setAttribute("filename", QString("spline%1.assistant").arg(count));
assistantsElement.appendChild(assistantElement);
}
else if (d->id == "perspective"){
QDomElement assistantElement = doc.createElement("assistant");
assistantElement.setAttribute("type", "perspective");
assistantElement.setAttribute("filename", QString("perspective%1.assistant").arg(count));
assistantsElement.appendChild(assistantElement);
}
else if (d->id == "vanishing point"){
QDomElement assistantElement = doc.createElement("assistant");
assistantElement.setAttribute("type", "vanishing point");
assistantElement.setAttribute("filename", QString("vanishing point%1.assistant").arg(count));
assistantsElement.appendChild(assistantElement);
}
else if (d->id == "infinite ruler"){
QDomElement assistantElement = doc.createElement("assistant");
assistantElement.setAttribute("type", "infinite ruler");
assistantElement.setAttribute("filename", QString("infinite ruler%1.assistant").arg(count));
assistantsElement.appendChild(assistantElement);
}
else if (d->id == "parallel ruler"){
QDomElement assistantElement = doc.createElement("assistant");
assistantElement.setAttribute("type", "parallel ruler");
assistantElement.setAttribute("filename", QString("parallel ruler%1.assistant").arg(count));
assistantsElement.appendChild(assistantElement);
}
else if (d->id == "concentric ellipse"){
QDomElement assistantElement = doc.createElement("assistant");
assistantElement.setAttribute("type", "concentric ellipse");
assistantElement.setAttribute("filename", QString("concentric ellipse%1.assistant").arg(count));
assistantsElement.appendChild(assistantElement);
}
else if (d->id == "fisheye-point"){
QDomElement assistantElement = doc.createElement("assistant");
assistantElement.setAttribute("type", "fisheye-point");
assistantElement.setAttribute("filename", QString("fisheye-point%1.assistant").arg(count));
assistantsElement.appendChild(assistantElement);
}
else if (d->id == "ruler"){
QDomElement assistantElement = doc.createElement("assistant");
assistantElement.setAttribute("type", "ruler");
assistantElement.setAttribute("filename", QString("ruler%1.assistant").arg(count));
assistantsElement.appendChild(assistantElement);
}
}
-void KisPaintingAssistant::findHandleLocation() {
+void KisPaintingAssistant::findPerspectiveAssistantHandleLocation() {
QList hHandlesList;
QList