diff --git a/CMakeLists.txt b/CMakeLists.txt index 39e02593ed..db678ae89c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,750 +1,755 @@ project(krita) message(STATUS "Using CMake version: ${CMAKE_VERSION}") cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(MIN_QT_VERSION 5.6.0) set(MIN_FRAMEWORKS_VERSION 5.18.0) if (POLICY CMP0002) cmake_policy(SET CMP0002 OLD) endif() if (POLICY CMP0017) cmake_policy(SET CMP0017 NEW) endif () if (POLICY CMP0022) cmake_policy(SET CMP0022 OLD) endif () if (POLICY CMP0026) cmake_policy(SET CMP0026 OLD) endif() if (POLICY CMP0042) cmake_policy(SET CMP0042 NEW) endif() if (POLICY CMP0046) cmake_policy(SET CMP0046 OLD) endif () if (POLICY CMP0059) cmake_policy(SET CMP0059 OLD) endif() if (POLICY CMP0063) cmake_policy(SET CMP0063 OLD) endif() if (POLICY CMP0054) cmake_policy(SET CMP0054 OLD) endif() if (POLICY CMP0064) cmake_policy(SET CMP0064 OLD) endif() if (POLICY CMP0071) cmake_policy(SET CMP0071 OLD) endif() if (APPLE) set(APPLE_SUPPRESS_X11_WARNING TRUE) set(KDE_SKIP_RPATH_SETTINGS TRUE) set(CMAKE_MACOSX_RPATH 1) set(BUILD_WITH_INSTALL_RPATH 1) add_definitions(-mmacosx-version-min=10.11 -Wno-macro-redefined -Wno-deprecated-register) endif() if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 AND NOT WIN32) endif() ###################### ####################### ## Constants defines ## ####################### ###################### # define common versions of Krita applications, used to generate kritaversion.h # update these version for every release: set(KRITA_VERSION_STRING "4.2.0-pre-alpha") # Major version: 3 for 3.x, 4 for 4.x, etc. set(KRITA_STABLE_VERSION_MAJOR 4) # Minor version: 0 for 4.0, 1 for 4.1, etc. set(KRITA_STABLE_VERSION_MINOR 2) # Bugfix release version, or 0 for before the first stable release set(KRITA_VERSION_RELEASE 0) # the 4th digit, really only used for the Windows installer: # - [Pre-]Alpha: Starts from 0, increment 1 per release # - Beta: Starts from 50, increment 1 per release # - Stable: Set to 100, bump to 101 if emergency update is needed set(KRITA_VERSION_REVISION 0) set(KRITA_ALPHA 1) # uncomment only for Alpha #set(KRITA_BETA 1) # uncomment only for Beta #set(KRITA_RC 1) # uncomment only for RC set(KRITA_YEAR 2018) # update every year if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC) set(KRITA_STABLE 1) # do not edit endif() message(STATUS "Krita version: ${KRITA_VERSION_STRING}") # Define the generic version of the Krita libraries here # This makes it easy to advance it when the next Krita release comes. # 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series # (2.x) so we're starting with 15 in 3.x series, 16 in 4.x series if(KRITA_STABLE_VERSION_MAJOR EQUAL 4) math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 16") else() # let's make sure we won't forget to update the "16" message(FATAL_ERROR "Reminder: please update offset == 16 used to compute GENERIC_KRITA_LIB_VERSION_MAJOR to something bigger") endif() set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0") set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro") # fetch git revision for the current build set(KRITA_GIT_SHA1_STRING "") set(KRITA_GIT_BRANCH_STRING "") include(GetGitRevisionDescription) get_git_head_hash(GIT_SHA1) get_git_branch(GIT_BRANCH) if(GIT_SHA1) string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1) set(KRITA_GIT_SHA1_STRING ${GIT_SHA1}) if(GIT_BRANCH) set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH}) else() set(KRITA_GIT_BRANCH_STRING "(detached HEAD)") endif() endif() # create test make targets enable_testing() # collect list of broken tests, empty here to start fresh with each cmake run set(KRITA_BROKEN_TESTS "" CACHE INTERNAL "KRITA_BROKEN_TESTS") ############ ############# ## Options ## ############# ############ include(FeatureSummary) if (WIN32) option(USE_DRMINGW "Support the Dr. Mingw crash handler (only on windows)" ON) add_feature_info("Dr. Mingw" USE_DRMINGW "Enable the Dr. Mingw crash handler") if (MINGW) option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON) add_feature_info("Linker Security Flags" USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags") if (USE_MINGW_HARDENING_LINKER) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") # Enable high-entropy ASLR for 64-bit # The image base has to be >4GB for HEASLR to be enabled. # The values used here are kind of arbitrary. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") else (USE_MINGW_HARDENING_LINKER) message(WARNING "Linker Security Flags not enabled!") endif (USE_MINGW_HARDENING_LINKER) endif (MINGW) endif () if (UNIX AND NOT APPLE) option(USE_QT_XCB "Do not use Krita's forked XCB connection and tablet support on X11, but leave everything to Qt." OFF) configure_file(config_use_qt_xcb.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_use_qt_xcb.h) add_feature_info("Use Qt's XCB and Tablet support on X11" USE_QT_XCB "Do not use Krita's forked XCB connection and tablet support on X11, but leave everything to Qt.") endif() option(HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal." ON) configure_file(config-hide-safe-asserts.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hide-safe-asserts.h) add_feature_info("Hide Safe Asserts" HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal.") option(USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking." ON) configure_file(config-hash-table-implementaion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hash-table-implementaion.h) add_feature_info("Lock free hash table" USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking.") option(FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true." OFF) add_feature_info("Foundation Build" FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true.") option(KRITA_ENABLE_BROKEN_TESTS "Enable tests that are marked as broken" OFF) add_feature_info("Enable Broken Tests" KRITA_ENABLE_BROKEN_TESTS "Runs broken test when \"make test\" is invoked (use -DKRITA_ENABLE_BROKEN_TESTS=ON to enable).") option(LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode" ON) configure_file(config-limit-long-tests.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-limit-long-tests.h) add_feature_info("Limit long tests" LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode") option(ENABLE_PYTHON_2 "Enables the compiler to look for Python 2.7 instead of Python 3. Some packaged scripts are not compatible with Python 2 and this should only be used if you really have to use 2.7." OFF) option(BUILD_KRITA_QT_DESIGNER_PLUGINS "Build Qt Designer plugins for Krita widgets" OFF) add_feature_info("Build Qt Designer plugins" BUILD_KRITA_QT_DESIGNER_PLUGINS "Builds Qt Designer plugins for Krita widgets (use -DBUILD_KRITA_QT_DESIGNER_PLUGINS=ON to enable).") include(MacroJPEG) ######################################################### ## Look for Python3 It is also searched by KF5, ## ## so we should request the correct version in advance ## ######################################################### function(TestCompileLinkPythonLibs OUTPUT_VARNAME) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES ${PYTHON_INCLUDE_PATH}) set(CMAKE_REQUIRED_LIBRARIES ${PYTHON_LIBRARIES}) if (MINGW) set(CMAKE_REQUIRED_DEFINITIONS -D_hypot=hypot) endif (MINGW) unset(${OUTPUT_VARNAME} CACHE) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { Py_InitializeEx(0); }" ${OUTPUT_VARNAME}) endfunction() if(MINGW) if(ENABLE_PYTHON_2) message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.") else(ENABLE_PYTHON_2) find_package(PythonInterp 3.6 EXACT) find_package(PythonLibs 3.6 EXACT) endif(ENABLE_PYTHON_2) if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) if(ENABLE_PYTHON_2) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonLibrary 3.6) endif(ENABLE_PYTHON_2) TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS) if (NOT CAN_USE_PYTHON_LIBS) message(FATAL_ERROR "Compiling with Python library failed, please check whether the architecture is correct. Python will be disabled.") endif (NOT CAN_USE_PYTHON_LIBS) endif (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) else(MINGW) if(ENABLE_PYTHON_2) find_package(PythonInterp 2.7) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonInterp 3.0) find_package(PythonLibrary 3.0) endif(ENABLE_PYTHON_2) endif(MINGW) ######################## ######################### ## Look for KDE and Qt ## ######################### ######################## find_package(ECM 5.22 REQUIRED NOMODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(ECMOptionalAddSubdirectory) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(GenerateExportHeader) include(ECMMarkAsTest) include(ECMInstallIcons) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) # do not reorder to be alphabetical: this is the order in which the frameworks # depend on each other. find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS Archive Config WidgetsAddons Completion CoreAddons GuiAddons I18n ItemModels ItemViews WindowSystem ) # KConfig deprecated authorizeKAction. In order to be warning free, # compile with the updated function when the dependency is new enough. # Remove this (and the uses of the define) when the minimum KF5 # version is >= 5.24.0. if (${KF5Config_VERSION} VERSION_LESS "5.24.0" ) message("Old KConfig (< 5.24.0) found.") add_definitions(-DKCONFIG_BEFORE_5_24) endif() find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS Core Gui Widgets Xml Network PrintSupport Svg Test Concurrent Sql ) include (MacroAddFileDependencies) include (MacroBoolTo01) include (MacroEnsureOutOfSourceBuild) macro_ensure_out_of_source_build("Compiling Krita inside the source directory is not possible. Please refer to the build instruction https://community.kde.org/Krita#Build_Instructions") # Note: OPTIONAL_COMPONENTS does not seem to be reliable # (as of ECM 5.15.0, CMake 3.2) find_package(Qt5Multimedia ${MIN_QT_VERSION}) set_package_properties(Qt5Multimedia PROPERTIES DESCRIPTION "Qt multimedia integration" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide sound support for animations") macro_bool_to_01(Qt5Multimedia_FOUND HAVE_QT_MULTIMEDIA) configure_file(config-qtmultimedia.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-qtmultimedia.h ) if (NOT APPLE) find_package(Qt5Quick ${MIN_QT_VERSION}) set_package_properties(Qt5Quick PROPERTIES DESCRIPTION "QtQuick" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") macro_bool_to_01(Qt5Quick_FOUND HAVE_QT_QUICK) find_package(Qt5QuickWidgets ${MIN_QT_VERSION}) set_package_properties(Qt5QuickWidgets PROPERTIES DESCRIPTION "QtQuickWidgets" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") endif() if (NOT WIN32 AND NOT APPLE) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras) find_package(Qt5DBus ${MIN_QT_VERSION}) set(HAVE_DBUS ${Qt5DBus_FOUND}) set_package_properties(Qt5DBus PROPERTIES DESCRIPTION "Qt DBUS integration" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide a dbus api on Linux") find_package(KF5Crash ${MIN_FRAMEWORKS_VERSION}) macro_bool_to_01(KF5Crash_FOUND HAVE_KCRASH) set_package_properties(KF5Crash PROPERTIES DESCRIPTION "KDE's Crash Handler" URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kcrash/html/index.html" TYPE OPTIONAL PURPOSE "Optionally used to provide crash reporting on Linux") find_package(X11 REQUIRED COMPONENTS Xinput) set(HAVE_X11 TRUE) add_definitions(-DHAVE_X11) find_package(XCB COMPONENTS XCB ATOM) set(HAVE_XCB ${XCB_FOUND}) else() set(HAVE_DBUS FALSE) set(HAVE_X11 FALSE) set(HAVE_XCB FALSE) endif() add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_STRICT_ITERATORS -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_NO_URL_CAST_FROM_STRING ) if (${Qt5_VERSION} VERSION_GREATER "5.8.0" ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50900) elseif(${Qt5_VERSION} VERSION_GREATER "5.7.0" ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50800) elseif(${Qt5_VERSION} VERSION_GREATER "5.6.0" ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50700) else() add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50600) endif() add_definitions(-DTRANSLATION_DOMAIN=\"krita\") # # The reason for this mode is that the Debug mode disable inlining # if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals") endif() option(KRITA_DEVS "For Krita developers. This modifies the DEBUG build type to use -O3 -g, while still enabling Q_ASSERT. This is necessary because the Qt5 cmake modules normally append QT_NO_DEBUG to any build type that is not labeled Debug") if (KRITA_DEVS) set(CMAKE_CXX_FLAGS_DEBUG "-O3 -g" CACHE STRING "" FORCE) endif() if(UNIX) set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m") endif() if(WIN32) if(MSVC) # C4522: 'class' : multiple assignment operators specified set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4522") endif() endif() # KDECompilerSettings adds the `--export-all-symbols` linker flag. # We don't really need it. if(MINGW) string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") endif(MINGW) # enable exceptions globally kde_enable_exceptions() set(KRITA_DEFAULT_TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/sdk/tests/data/) macro(macro_add_unittest_definitions) add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/") add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}") add_definitions(-DFILES_DEFAULT_DATA_DIR="${KRITA_DEFAULT_TEST_DATA_DIR}") add_definitions(-DSYSTEM_RESOURCES_DATA_DIR="${CMAKE_SOURCE_DIR}/krita/data/") endmacro() # overcome some platform incompatibilities if(WIN32) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif() # set custom krita plugin installdir set(KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins) ########################### ############################ ## Required dependencies ## ############################ ########################### find_package(PNG REQUIRED) if (APPLE) # this is not added correctly on OSX -- see http://forum.kde.org/viewtopic.php?f=139&t=101867&p=221242#p221242 include_directories(SYSTEM ${PNG_INCLUDE_DIR}) endif() add_definitions(-DBOOST_ALL_NO_LIB) find_package(Boost 1.55 REQUIRED COMPONENTS system) include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) ## ## Test for GNU Scientific Library ## find_package(GSL) set_package_properties(GSL PROPERTIES URL "http://www.gnu.org/software/gsl" TYPE RECOMMENDED PURPOSE "Required by Krita's Transform tool.") macro_bool_to_01(GSL_FOUND HAVE_GSL) configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h ) ########################### ############################ ## Optional dependencies ## ############################ ########################### find_package(ZLIB) set_package_properties(ZLIB PROPERTIES DESCRIPTION "Compression library" URL "http://www.zlib.net/" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic and the PSD plugins") macro_bool_to_01(ZLIB_FOUND HAVE_ZLIB) find_package(OpenEXR) set_package_properties(OpenEXR PROPERTIES DESCRIPTION "High dynamic-range (HDR) image file format" URL "http://www.openexr.com" TYPE OPTIONAL PURPOSE "Required by the Krita OpenEXR filter") macro_bool_to_01(OPENEXR_FOUND HAVE_OPENEXR) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() find_package(TIFF) set_package_properties(TIFF PROPERTIES DESCRIPTION "TIFF Library and Utilities" URL "http://www.remotesensing.org/libtiff" TYPE OPTIONAL PURPOSE "Required by the Krita TIFF filter") find_package(JPEG) set_package_properties(JPEG PROPERTIES DESCRIPTION "Free library for JPEG image compression. Note: libjpeg8 is NOT supported." URL "http://www.libjpeg-turbo.org" TYPE OPTIONAL PURPOSE "Required by the Krita JPEG filter") find_package(GIF) set_package_properties(GIF PROPERTIES DESCRIPTION "Library for loading and saving gif files." URL "http://giflib.sourceforge.net/" TYPE OPTIONAL PURPOSE "Required by the Krita GIF filter") find_package(HEIF "1.3.0") set_package_properties(HEIF PROPERTIES DESCRIPTION "Library for loading and saving heif files." URL "https://github.com/strukturag/libheif" TYPE OPTIONAL PURPOSE "Required by the Krita HEIF filter") set(LIBRAW_MIN_VERSION "0.16") find_package(LibRaw ${LIBRAW_MIN_VERSION}) set_package_properties(LibRaw PROPERTIES DESCRIPTION "Library to decode RAW images" URL "http://www.libraw.org" TYPE OPTIONAL PURPOSE "Required to build the raw import plugin") find_package(FFTW3) set_package_properties(FFTW3 PROPERTIES DESCRIPTION "A fast, free C FFT library" URL "http://www.fftw.org/" TYPE OPTIONAL PURPOSE "Required by the Krita for fast convolution operators and some G'Mic features") macro_bool_to_01(FFTW3_FOUND HAVE_FFTW3) find_package(OCIO) set_package_properties(OCIO PROPERTIES DESCRIPTION "The OpenColorIO Library" URL "http://www.opencolorio.org" TYPE OPTIONAL PURPOSE "Required by the Krita LUT docker") macro_bool_to_01(OCIO_FOUND HAVE_OCIO) set_package_properties(PythonLibrary PROPERTIES DESCRIPTION "Python Library" URL "http://www.python.org" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYTHONLIBS_FOUND HAVE_PYTHONLIBS) find_package(SIP "4.18.0") set_package_properties(SIP PROPERTIES DESCRIPTION "Support for generating SIP Python bindings" URL "https://www.riverbankcomputing.com/software/sip/download" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(SIP_FOUND HAVE_SIP) find_package(PyQt5 "5.6.0") set_package_properties(PyQt5 PROPERTIES DESCRIPTION "Python bindings for Qt5." URL "https://www.riverbankcomputing.com/software/pyqt/download5" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYQT5_FOUND HAVE_PYQT5) ## ## Look for OpenGL ## # TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes) if(Qt5Gui_OPENGL_IMPLEMENTATION) message(STATUS "Found QtGui OpenGL support") else() message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.") endif() ## ## Test for eigen3 ## find_package(Eigen3 3.0 REQUIRED) set_package_properties(Eigen3 PROPERTIES DESCRIPTION "C++ template library for linear algebra" URL "http://eigen.tuxfamily.org" TYPE REQUIRED) ## ## Test for exiv2 ## find_package(LibExiv2 0.16 REQUIRED) ## ## Test for lcms ## find_package(LCMS2 2.4 REQUIRED) set_package_properties(LCMS2 PROPERTIES DESCRIPTION "LittleCMS Color management engine" URL "http://www.littlecms.com" TYPE REQUIRED PURPOSE "Will be used for color management and is necessary for Krita") if(LCMS2_FOUND) if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 ) set(HAVE_LCMS24 TRUE) endif() set(HAVE_REQUIRED_LCMS_VERSION TRUE) set(HAVE_LCMS2 TRUE) endif() ## ## Test for Vc ## set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ) set(HAVE_VC FALSE) if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") if(NOT MSVC) find_package(Vc 1.1.0) set_package_properties(Vc PROPERTIES DESCRIPTION "Portable, zero-overhead SIMD library for C++" URL "https://github.com/VcDevel/Vc" TYPE OPTIONAL PURPOSE "Required by the Krita for vectorization") macro_bool_to_01(Vc_FOUND HAVE_VC) endif() endif() configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h ) if(HAVE_VC) message(STATUS "Vc found!") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/vc") include (VcMacros) if(Vc_COMPILER_IS_CLANG) set(ADDITIONAL_VC_FLAGS "-Wabi -ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() elseif (NOT MSVC) set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() endif() #Handle Vc master if(Vc_COMPILER_IS_GCC OR Vc_COMPILER_IS_CLANG) AddCompilerFlag("-std=c++11" _ok) if(NOT _ok) AddCompilerFlag("-std=c++0x" _ok) endif() endif() macro(ko_compile_for_all_implementations_no_scalar _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() macro(ko_compile_for_all_implementations _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() endif() set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} ) add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) ## ## Test endianness ## include (TestBigEndian) test_big_endian(CMAKE_WORDS_BIGENDIAN) ## ## Test for qt-poppler ## find_package(Poppler COMPONENTS Qt5) set_package_properties(Poppler PROPERTIES DESCRIPTION "A PDF rendering library" URL "http://poppler.freedesktop.org" TYPE OPTIONAL PURPOSE "Required by the Krita PDF filter.") +## +## Test for Atomics +## +include(CheckAtomic) + ############################ ############################# ## 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) if(WIN32) include(${CMAKE_CURRENT_LIST_DIR}/packaging/windows/installer/ConfigureInstallerNsis.cmake) endif() message("\nBroken tests:") foreach(tst ${KRITA_BROKEN_TESTS}) message(" * ${tst}") endforeach() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/po OR EXISTS ${CMAKE_CURRENT_BINARY_DIR}/po ) find_package(KF5I18n CONFIG REQUIRED) ki18n_install(po) endif() diff --git a/cmake/modules/CheckAtomic.cmake b/cmake/modules/CheckAtomic.cmake new file mode 100644 index 0000000000..d72807f5ef --- /dev/null +++ b/cmake/modules/CheckAtomic.cmake @@ -0,0 +1,118 @@ +# ============================================================================== +# LLVM Release License +# ============================================================================== +# University of Illinois/NCSA +# Open Source License +# +# Copyright (c) 2003-2018 University of Illinois at Urbana-Champaign. +# All rights reserved. +# +# Developed by: +# +# LLVM Team +# +# University of Illinois at Urbana-Champaign +# +# http://llvm.org +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal with +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is furnished to do +# so, subject to the following conditions: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimers. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimers in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of the LLVM Team, University of Illinois at +# Urbana-Champaign, nor the names of its contributors may be used to +# endorse or promote products derived from this Software without specific +# prior written permission. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +# SOFTWARE. + +INCLUDE(CheckCXXSourceCompiles) +INCLUDE(CheckLibraryExists) + +# Sometimes linking against libatomic is required for atomic ops, if +# the platform doesn't support lock-free atomics. + +function(check_working_cxx_atomics varname) +set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) +set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11") +CHECK_CXX_SOURCE_COMPILES(" +#include +std::atomic x; +int main() { +return std::atomic_is_lock_free(&x); +} +" ${varname}) +set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) +endfunction(check_working_cxx_atomics) + +function(check_working_cxx_atomics64 varname) +set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) +set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}") +CHECK_CXX_SOURCE_COMPILES(" +#include +#include +std::atomic x (0); +int main() { +uint64_t i = x.load(std::memory_order_relaxed); +return std::atomic_is_lock_free(&x); +} +" ${varname}) +set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) +endfunction(check_working_cxx_atomics64) + + +# This isn't necessary on MSVC, so avoid command-line switch annoyance +# by only running on GCC-like hosts. +if (LLVM_COMPILER_IS_GCC_COMPATIBLE) + # First check if atomics work without the library. + check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB) + # If not, check if the library exists, and atomics work with it. + if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB) + check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC) + if( HAVE_LIBATOMIC ) + list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") + check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB) + if (NOT HAVE_CXX_ATOMICS_WITH_LIB) + message(FATAL_ERROR "Host compiler must support std::atomic!") + endif() + else() + message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.") + endif() + endif() +endif() +# Check for 64 bit atomic operations. +if(MSVC) + set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True) +else() + check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB) +endif() + +# If not, check if the library exists, and atomics work with it. +if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) + check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64) + if(HAVE_CXX_LIBATOMICS64) + list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") + check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) + if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) + message(FATAL_ERROR "Host compiler must support std::atomic!") + endif() + else() + message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.") + endif() +endif() diff --git a/krita/data/aboutdata/developers.txt b/krita/data/aboutdata/developers.txt index 988a1c6e5b..2712d4f763 100644 --- a/krita/data/aboutdata/developers.txt +++ b/krita/data/aboutdata/developers.txt @@ -1,198 +1,199 @@ Boudewijn Rempt Aaron J. Seigo Adam Celarek Adam Pigg Adriaan de Groot Adrian Page Adrian Schroeter Albert Astals Cid Alberto Villa Alexander Neundorf Alexander Potashev Alexis Ménard Alfredo Beaumont Sainz Allen Winter Ana Beatriz Guerrero López Andras Mantia Andreas Hartmetz Andreas Lundin André Marcelo Alvarenga Andrew Coles Andre Woebbeking Andrius da Costa Ribas Andy Fawcett Anne-Marie Mahfouf Ariya Hidayat Arjen Hiemstra Bart Coppens Ben Cooksley Benjamin K. Stuhl Benjamin Meyer Benjamin Reed Benoît Jacob Ben Schleimer Bernhard Rosenkraenzer Bo Thorsen Brad Hards Bram Schoenmakers Burkhard Lück Carlo Segato C. Boemann Christer Stenbrenden Christian Ehrlicher Christian Mueller Christoph Feck Chusslove Illich Clarence Dang Cyrille Berger Daniel M. Duley Daniel Molkentin Dan Leinir Turthra Jensen Dan Meltzer Danny Allen David Faure David Gowers Dirk Mueller Dirk Schönberger Dmitry Kazakov Edward Apap Elvis Stansvik Emanuele Tamponi Enrique Matías Sánchez Fabian Kosmale Frank Osterfeld Frederik Schwarzer Fredrik Edemar Fredy Yanardi Friedrich W. H. Kossebau Gábor Lehel Gary Cramblitt Geoffry Song Gioele Barabucci Giovanni Venturi Gopalakrishna Bhat A Hanna Scott Harald Sitter Hasso Tepper Helge Deller Helio Castro Hideki Saito Hoàng Đức Hiếu Hugo Pereira Da Costa Inge Wallin Ingo Klöcker İsmail Dönmez Ivan Yossi Jaime Jaime Torres Jaison Lee Jakob Petsovits Jakub Stachowski Jan Hambrecht Jarosław Staniek Jens Herden Jessica Hall Johannes Simon John Layt Jonathan Riddell Jonathan Singer José Luis Vergara Juan Luis Boya García Juan Palacios Jure Repinc Kai-Uwe Behrmann Kevin Krammer Kevin Ottens Kurt Pfeifle Laurent Montel Lauri Watts Leo Savernik Lukáš Tinkl Lukáš Tvrdý Maciej Mrozowski Malcolm Hunter Manuel Riecke manu tortosa Marc Pegon Marijn Kruisselbrink Martin Ellis Martin Gräßlin Matthew Woehlke Matthias Klumpp Matthias Kretz Matus Talcik Maximiliano Curia Melchior Franz Michael David Howell Michael Drueing Michael Thaler Michel Hermier Mohit Goyal Mojtaba Shahi Senobari Montel Laurent Moritz Molch Nick Shaforostoff Nicolas Goutte Olivier Goffart Patrick Julien Patrick Spendrin +Pavel Belskiy Pavel Heimlich Peter Simonsson Pierre Ducroquet Pierre Stirnweiss Pino Toscano Rafael Fernández López Raphael Langerhorst Rex Dieter Rob Buis Roopesh Chander Sahil Nagpal Salil Kapur Samuel Buttigieg Sander Koning Sascha Suelzer Scott Petrovic Scott Wheeler Sebastian Sauer Shivaraman Aiyer Siddharth Sharma Silvio Heinrich Somsubhra Bairi Spencer Brown Srikanth Tiyyagura Stefan Nikolaus Stephan Binner Stephan Kulow Stuart Dickson Sune Vuorela Sven Langkamp Thiago Macieira Thomas Capricelli Thomas Friedrichsmeier Thomas Klausner Thomas Nagy Thomas Zander Thorsten Staerk Thorsten Zachmann Tim Beaulen Timothée Giet Tobias Koenig Tom Burdick Torio Mlshi Torsten Rahn Unai Garro Urs Wolfer Vadim Zhukov Vera Lukman Victor Lafon Victor Wåhlström Volker Krause Waldo Bastian Werner Trobin Wilco Greven Will Entriken William Steidtmann Wolthera van Hovell Yann Bodson Yue Liu Yuri Chornoivan \ No newline at end of file diff --git a/libs/flake/KoToolBase.cpp b/libs/flake/KoToolBase.cpp index 7d1f94c20c..0e097ab5c5 100644 --- a/libs/flake/KoToolBase.cpp +++ b/libs/flake/KoToolBase.cpp @@ -1,373 +1,374 @@ /* This file is part of the KDE project * Copyright (C) 2006, 2010 Thomas Zander * Copyright (C) 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. */ #include #include #include "KoToolBase.h" #include "KoToolBase_p.h" #include "KoCanvasBase.h" #include "KoPointerEvent.h" #include "KoDocumentResourceManager.h" #include "KoCanvasResourceProvider.h" #include "KoViewConverter.h" #include "KoShapeController.h" #include "KoShapeControllerBase.h" #include "KoToolSelection.h" #include "KoCanvasController.h" #include #include #include #include #include #include KoToolBase::KoToolBase(KoCanvasBase *canvas) : d_ptr(new KoToolBasePrivate(this, canvas)) { Q_D(KoToolBase); d->connectSignals(); } KoToolBase::KoToolBase(KoToolBasePrivate &dd) : d_ptr(&dd) { Q_D(KoToolBase); d->connectSignals(); } KoToolBase::~KoToolBase() { qDeleteAll(d_ptr->optionWidgets); delete d_ptr; } bool KoToolBase::isActivated() const { Q_D(const KoToolBase); return d->isActivated; } void KoToolBase::activate(KoToolBase::ToolActivation toolActivation, const QSet &shapes) { Q_UNUSED(toolActivation); Q_UNUSED(shapes); Q_D(KoToolBase); d->isActivated = true; } void KoToolBase::deactivate() { Q_D(KoToolBase); d->isActivated = false; } void KoToolBase::canvasResourceChanged(int key, const QVariant & res) { Q_UNUSED(key); Q_UNUSED(res); } void KoToolBase::documentResourceChanged(int key, const QVariant &res) { Q_UNUSED(key); Q_UNUSED(res); } bool KoToolBase::wantsAutoScroll() const { return true; } void KoToolBase::mouseDoubleClickEvent(KoPointerEvent *event) { event->ignore(); } void KoToolBase::mouseTripleClickEvent(KoPointerEvent *event) { event->ignore(); } void KoToolBase::keyPressEvent(QKeyEvent *e) { e->ignore(); } void KoToolBase::keyReleaseEvent(QKeyEvent *e) { e->ignore(); } void KoToolBase::explicitUserStrokeEndRequest() { } QVariant KoToolBase::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &) const { Q_D(const KoToolBase); if (d->canvas->canvasWidget() == 0) return QVariant(); switch (query) { case Qt::ImMicroFocus: return QRect(d->canvas->canvasWidget()->width() / 2, 0, 1, d->canvas->canvasWidget()->height()); case Qt::ImFont: return d->canvas->canvasWidget()->font(); default: return QVariant(); } } void KoToolBase::inputMethodEvent(QInputMethodEvent * event) { if (! event->commitString().isEmpty()) { QKeyEvent ke(QEvent::KeyPress, -1, 0, event->commitString()); keyPressEvent(&ke); } event->accept(); } void KoToolBase::customPressEvent(KoPointerEvent * event) { event->ignore(); } void KoToolBase::customReleaseEvent(KoPointerEvent * event) { event->ignore(); } void KoToolBase::customMoveEvent(KoPointerEvent * event) { event->ignore(); } void KoToolBase::useCursor(const QCursor &cursor) { Q_D(KoToolBase); d->currentCursor = cursor; emit cursorChanged(d->currentCursor); } QList > KoToolBase::optionWidgets() { Q_D(KoToolBase); - if (d->optionWidgets.empty()) { + if (!d->optionWidgetsCreated) { d->optionWidgets = createOptionWidgets(); + d->optionWidgetsCreated = true; } return d->optionWidgets; } QAction *KoToolBase::action(const QString &name) const { Q_D(const KoToolBase); return d->canvas->canvasController()->actionCollection()->action(name); } QWidget * KoToolBase::createOptionWidget() { return 0; } QList > KoToolBase::createOptionWidgets() { QList > ow; if (QWidget *widget = createOptionWidget()) { if (widget->objectName().isEmpty()) { widget->setObjectName(toolId()); } ow.append(widget); } return ow; } void KoToolBase::setToolId(const QString &id) { Q_D(KoToolBase); d->toolId = id; } QString KoToolBase::toolId() const { Q_D(const KoToolBase); return d->toolId; } QCursor KoToolBase::cursor() const { Q_D(const KoToolBase); return d->currentCursor; } void KoToolBase::deleteSelection() { } void KoToolBase::cut() { copy(); deleteSelection(); } QMenu *KoToolBase::popupActionsMenu() { return 0; } KoCanvasBase * KoToolBase::canvas() const { Q_D(const KoToolBase); return d->canvas; } void KoToolBase::setStatusText(const QString &statusText) { emit statusTextChanged(statusText); } uint KoToolBase::handleRadius() const { Q_D(const KoToolBase); if(d->canvas->shapeController()->resourceManager()) { return d->canvas->shapeController()->resourceManager()->handleRadius(); } else { return 3; } } uint KoToolBase::grabSensitivity() const { Q_D(const KoToolBase); if(d->canvas->shapeController()->resourceManager()) { return d->canvas->shapeController()->resourceManager()->grabSensitivity(); } else { return 3; } } QRectF KoToolBase::handleGrabRect(const QPointF &position) const { Q_D(const KoToolBase); const KoViewConverter * converter = d->canvas->viewConverter(); uint handleSize = 2*grabSensitivity(); QRectF r = converter->viewToDocument(QRectF(0, 0, handleSize, handleSize)); r.moveCenter(position); return r; } QRectF KoToolBase::handlePaintRect(const QPointF &position) const { Q_D(const KoToolBase); const KoViewConverter * converter = d->canvas->viewConverter(); uint handleSize = 2*handleRadius(); QRectF r = converter->viewToDocument(QRectF(0, 0, handleSize, handleSize)); r.moveCenter(position); return r; } void KoToolBase::setTextMode(bool value) { Q_D(KoToolBase); d->isInTextMode=value; } bool KoToolBase::paste() { return false; } void KoToolBase::copy() const { } void KoToolBase::dragMoveEvent(QDragMoveEvent *event, const QPointF &point) { Q_UNUSED(event); Q_UNUSED(point); } void KoToolBase::dragLeaveEvent(QDragLeaveEvent *event) { Q_UNUSED(event); } void KoToolBase::dropEvent(QDropEvent *event, const QPointF &point) { Q_UNUSED(event); Q_UNUSED(point); } bool KoToolBase::hasSelection() { KoToolSelection *sel = selection(); return (sel && sel->hasSelection()); } KoToolSelection *KoToolBase::selection() { return 0; } void KoToolBase::repaintDecorations() { } bool KoToolBase::isInTextMode() const { Q_D(const KoToolBase); return d->isInTextMode; } void KoToolBase::requestUndoDuringStroke() { /** * Default implementation just cancels the stroke */ requestStrokeCancellation(); } void KoToolBase::requestStrokeCancellation() { } void KoToolBase::requestStrokeEnd() { } bool KoToolBase::maskSyntheticEvents() const { Q_D(const KoToolBase); return d->maskSyntheticEvents; } void KoToolBase::setMaskSyntheticEvents(bool value) { Q_D(KoToolBase); d->maskSyntheticEvents = value; } diff --git a/libs/flake/KoToolBase_p.h b/libs/flake/KoToolBase_p.h index 4e60c8b0d0..8e8d2c2251 100644 --- a/libs/flake/KoToolBase_p.h +++ b/libs/flake/KoToolBase_p.h @@ -1,88 +1,89 @@ /* This file is part of the KDE project * Copyright (C) 2006-2010 Thomas Zander * Copyright (C) 2010 KO GmbH * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOTOOLBASE_P_H #define KOTOOLBASE_P_H #include "KoDocumentResourceManager.h" #include "KoCanvasResourceProvider.h" #include "KoCanvasBase.h" #include "KoShapeController.h" #include #include #include #include #include // for the qt version check class QAction; class KoToolBase; class KoToolBasePrivate { public: KoToolBasePrivate(KoToolBase *qq, KoCanvasBase *canvas_) : currentCursor(Qt::ArrowCursor), q(qq), canvas(canvas_), isInTextMode(false), isActivated(false) { } ~KoToolBasePrivate() { Q_FOREACH (QPointer optionWidget, optionWidgets) { if (optionWidget) { optionWidget->setParent(0); delete optionWidget; } } optionWidgets.clear(); } void connectSignals() { if (canvas) { // in the case of KoToolManagers dummytool it can be zero :( KoCanvasResourceProvider * crp = canvas->resourceManager(); Q_ASSERT_X(crp, "KoToolBase::KoToolBase", "No Canvas KoResourceManager"); if (crp) q->connect(crp, SIGNAL(canvasResourceChanged(int, const QVariant &)), SLOT(canvasResourceChanged(int, const QVariant &))); // can be 0 in the case of Calligra Sheets KoDocumentResourceManager *scrm = canvas->shapeController()->resourceManager(); if (scrm) { q->connect(scrm, SIGNAL(resourceChanged(int, const QVariant &)), SLOT(documentResourceChanged(int, const QVariant &))); } } } QList > optionWidgets; ///< the optionwidgets associated with this tool + bool optionWidgetsCreated {false}; QCursor currentCursor; QString toolId; KoToolBase *q; KoCanvasBase *canvas; ///< the canvas interface this tool will work for. bool isInTextMode; bool maskSyntheticEvents{false}; ///< Whether this tool masks synthetic events bool isActivated; }; #endif diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt index ec6b486960..cf25e7365c 100644 --- a/libs/image/CMakeLists.txt +++ b/libs/image/CMakeLists.txt @@ -1,368 +1,374 @@ add_subdirectory( tests ) add_subdirectory( tiles3 ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty ${CMAKE_CURRENT_SOURCE_DIR}/brushengine ${CMAKE_CURRENT_SOURCE_DIR}/commands ${CMAKE_CURRENT_SOURCE_DIR}/commands_new ${CMAKE_CURRENT_SOURCE_DIR}/filter ${CMAKE_CURRENT_SOURCE_DIR}/floodfill ${CMAKE_CURRENT_SOURCE_DIR}/generator ${CMAKE_CURRENT_SOURCE_DIR}/layerstyles ${CMAKE_CURRENT_SOURCE_DIR}/processing ${CMAKE_SOURCE_DIR}/sdk/tests ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ) if(FFTW3_FOUND) include_directories(${FFTW3_INCLUDE_DIR}) endif() if(HAVE_VC) include_directories(SYSTEM ${Vc_INCLUDE_DIR} ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}) ko_compile_for_all_implementations(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp) else() set(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp) endif() set(kritaimage_LIB_SRCS tiles3/kis_tile.cc tiles3/kis_tile_data.cc tiles3/kis_tile_data_store.cc tiles3/kis_tile_data_pooler.cc tiles3/kis_tiled_data_manager.cc tiles3/KisTiledExtentManager.cpp tiles3/kis_memento_manager.cc tiles3/kis_hline_iterator.cpp tiles3/kis_vline_iterator.cpp tiles3/kis_random_accessor.cc tiles3/swap/kis_abstract_compression.cpp tiles3/swap/kis_lzf_compression.cpp tiles3/swap/kis_abstract_tile_compressor.cpp tiles3/swap/kis_legacy_tile_compressor.cpp tiles3/swap/kis_tile_compressor_2.cpp tiles3/swap/kis_chunk_allocator.cpp tiles3/swap/kis_memory_window.cpp tiles3/swap/kis_swapped_data_store.cpp tiles3/swap/kis_tile_data_swapper.cpp kis_distance_information.cpp kis_painter.cc kis_painter_blt_multi_fixed.cpp kis_marker_painter.cpp KisPrecisePaintDeviceWrapper.cpp kis_progress_updater.cpp brushengine/kis_paint_information.cc brushengine/kis_random_source.cpp brushengine/KisPerStrokeRandomSource.cpp brushengine/kis_stroke_random_source.cpp brushengine/kis_paintop.cc brushengine/kis_paintop_factory.cpp brushengine/kis_paintop_preset.cpp brushengine/kis_paintop_registry.cc brushengine/kis_paintop_settings.cpp brushengine/kis_paintop_settings_update_proxy.cpp brushengine/kis_paintop_utils.cpp brushengine/kis_no_size_paintop_settings.cpp brushengine/kis_locked_properties.cc brushengine/kis_locked_properties_proxy.cpp brushengine/kis_locked_properties_server.cpp brushengine/kis_paintop_config_widget.cpp brushengine/kis_uniform_paintop_property.cpp brushengine/kis_combo_based_paintop_property.cpp brushengine/kis_slider_based_paintop_property.cpp brushengine/kis_standard_uniform_properties_factory.cpp brushengine/KisStrokeSpeedMeasurer.cpp brushengine/KisPaintopSettingsIds.cpp commands/kis_deselect_global_selection_command.cpp commands/KisDeselectActiveSelectionCommand.cpp commands/kis_image_change_layers_command.cpp commands/kis_image_change_visibility_command.cpp commands/kis_image_command.cpp commands/kis_image_set_projection_color_space_command.cpp commands/kis_image_layer_add_command.cpp commands/kis_image_layer_move_command.cpp commands/kis_image_layer_remove_command.cpp commands/kis_image_layer_remove_command_impl.cpp commands/kis_image_lock_command.cpp commands/kis_node_command.cpp commands/kis_node_compositeop_command.cpp commands/kis_node_opacity_command.cpp commands/kis_node_property_list_command.cpp commands/kis_reselect_global_selection_command.cpp commands/KisReselectActiveSelectionCommand.cpp commands/kis_set_global_selection_command.cpp commands_new/kis_saved_commands.cpp commands_new/kis_processing_command.cpp commands_new/kis_image_resize_command.cpp commands_new/kis_image_set_resolution_command.cpp commands_new/kis_node_move_command2.cpp commands_new/kis_set_layer_style_command.cpp commands_new/kis_selection_move_command2.cpp commands_new/kis_update_command.cpp commands_new/kis_switch_current_time_command.cpp commands_new/kis_change_projection_color_command.cpp commands_new/kis_activate_selection_mask_command.cpp commands_new/kis_transaction_based_command.cpp processing/kis_do_nothing_processing_visitor.cpp processing/kis_simple_processing_visitor.cpp processing/kis_crop_processing_visitor.cpp processing/kis_crop_selections_processing_visitor.cpp processing/kis_transform_processing_visitor.cpp processing/kis_mirror_processing_visitor.cpp processing/KisSelectionBasedProcessingHelper.cpp filter/kis_filter.cc filter/kis_filter_category_ids.cpp filter/kis_filter_configuration.cc filter/kis_color_transformation_configuration.cc filter/kis_filter_registry.cc filter/kis_color_transformation_filter.cc generator/kis_generator.cpp generator/kis_generator_layer.cpp generator/kis_generator_registry.cpp floodfill/kis_fill_interval_map.cpp floodfill/kis_scanline_fill.cpp lazybrush/kis_min_cut_worker.cpp lazybrush/kis_lazy_fill_tools.cpp lazybrush/kis_multiway_cut.cpp lazybrush/KisWatershedWorker.cpp lazybrush/kis_colorize_mask.cpp lazybrush/kis_colorize_stroke_strategy.cpp KisDelayedUpdateNodeInterface.cpp kis_adjustment_layer.cc kis_selection_based_layer.cpp kis_node_filter_interface.cpp kis_base_accessor.cpp kis_base_node.cpp kis_base_processor.cpp kis_bookmarked_configuration_manager.cc kis_node_uuid_info.cpp kis_clone_layer.cpp kis_colorspace_convert_visitor.cpp kis_config_widget.cpp kis_convolution_kernel.cc kis_convolution_painter.cc kis_gaussian_kernel.cpp kis_edge_detection_kernel.cpp kis_cubic_curve.cpp kis_default_bounds.cpp kis_default_bounds_base.cpp kis_effect_mask.cc kis_fast_math.cpp kis_fill_painter.cc kis_filter_mask.cpp kis_filter_strategy.cc kis_transform_mask.cpp kis_transform_mask_params_interface.cpp kis_recalculate_transform_mask_job.cpp kis_recalculate_generator_layer_job.cpp kis_transform_mask_params_factory_registry.cpp kis_safe_transform.cpp kis_gradient_painter.cc kis_gradient_shape_strategy.cpp kis_cached_gradient_shape_strategy.cpp kis_polygonal_gradient_shape_strategy.cpp kis_iterator_ng.cpp kis_async_merger.cpp kis_merge_walker.cc kis_updater_context.cpp kis_update_job_item.cpp kis_stroke_strategy_undo_command_based.cpp kis_simple_stroke_strategy.cpp KisRunnableBasedStrokeStrategy.cpp KisRunnableStrokeJobDataBase.cpp KisRunnableStrokeJobData.cpp KisRunnableStrokeJobsInterface.cpp KisFakeRunnableStrokeJobsExecutor.cpp kis_stroke_job_strategy.cpp kis_stroke_strategy.cpp kis_stroke.cpp kis_strokes_queue.cpp KisStrokesQueueMutatedJobInterface.cpp kis_simple_update_queue.cpp kis_update_scheduler.cpp kis_queues_progress_updater.cpp kis_composite_progress_proxy.cpp kis_sync_lod_cache_stroke_strategy.cpp kis_lod_capable_layer_offset.cpp kis_update_time_monitor.cpp KisImageConfigNotifier.cpp kis_group_layer.cc kis_count_visitor.cpp kis_histogram.cc kis_image_interfaces.cpp kis_image_animation_interface.cpp kis_time_range.cpp kis_node_graph_listener.cpp kis_image.cc kis_image_signal_router.cpp KisImageSignals.cpp kis_image_config.cpp kis_projection_updates_filter.cpp kis_suspend_projection_updates_stroke_strategy.cpp kis_regenerate_frame_stroke_strategy.cpp kis_switch_time_stroke_strategy.cpp kis_crop_saved_extra_data.cpp kis_timed_signal_threshold.cpp kis_layer.cc kis_indirect_painting_support.cpp kis_abstract_projection_plane.cpp kis_layer_projection_plane.cpp kis_layer_utils.cpp kis_mask_projection_plane.cpp kis_projection_leaf.cpp kis_mask.cc kis_base_mask_generator.cpp kis_rect_mask_generator.cpp kis_circle_mask_generator.cpp kis_gauss_circle_mask_generator.cpp kis_gauss_rect_mask_generator.cpp ${__per_arch_circle_mask_generator_objs} kis_curve_circle_mask_generator.cpp kis_curve_rect_mask_generator.cpp kis_math_toolbox.cpp kis_memory_statistics_server.cpp kis_name_server.cpp kis_node.cpp kis_node_facade.cpp kis_node_progress_proxy.cpp kis_busy_progress_indicator.cpp kis_node_visitor.cpp kis_paint_device.cc kis_paint_device_debug_utils.cpp kis_fixed_paint_device.cpp KisOptimizedByteArray.cpp kis_paint_layer.cc kis_perspective_math.cpp kis_pixel_selection.cpp kis_processing_information.cpp kis_properties_configuration.cc kis_random_accessor_ng.cpp kis_random_generator.cc kis_random_sub_accessor.cpp kis_wrapped_random_accessor.cpp kis_selection.cc KisSelectionUpdateCompressor.cpp kis_selection_mask.cpp kis_update_outline_job.cpp kis_update_selection_job.cpp kis_serializable_configuration.cc kis_transaction_data.cpp kis_transform_worker.cc kis_perspectivetransform_worker.cpp bsplines/kis_bspline_1d.cpp bsplines/kis_bspline_2d.cpp bsplines/kis_nu_bspline_2d.cpp kis_warptransform_worker.cc kis_cage_transform_worker.cpp kis_liquify_transform_worker.cpp kis_green_coordinates_math.cpp kis_transparency_mask.cc kis_undo_adapter.cpp kis_macro_based_undo_store.cpp kis_surrogate_undo_adapter.cpp kis_legacy_undo_adapter.cpp kis_post_execution_undo_adapter.cpp kis_processing_visitor.cpp kis_processing_applicator.cpp krita_utils.cpp kis_outline_generator.cpp kis_layer_composition.cpp kis_selection_filters.cpp KisProofingConfiguration.h kis_keyframe.cpp kis_keyframe_channel.cpp kis_keyframe_commands.cpp kis_scalar_keyframe_channel.cpp kis_raster_keyframe_channel.cpp kis_onion_skin_compositor.cpp kis_onion_skin_cache.cpp kis_idle_watcher.cpp kis_psd_layer_style.cpp kis_layer_properties_icons.cpp layerstyles/kis_multiple_projection.cpp layerstyles/kis_layer_style_filter.cpp layerstyles/kis_layer_style_filter_environment.cpp layerstyles/kis_layer_style_filter_projection_plane.cpp layerstyles/kis_layer_style_projection_plane.cpp layerstyles/kis_ls_drop_shadow_filter.cpp layerstyles/kis_ls_satin_filter.cpp layerstyles/kis_ls_stroke_filter.cpp layerstyles/kis_ls_bevel_emboss_filter.cpp layerstyles/kis_ls_overlay_filter.cpp layerstyles/kis_ls_utils.cpp layerstyles/gimp_bump_map.cpp KisProofingConfiguration.cpp kis_node_query_path.cc ) set(einspline_SRCS 3rdparty/einspline/bspline_create.cpp 3rdparty/einspline/bspline_data.cpp 3rdparty/einspline/multi_bspline_create.cpp 3rdparty/einspline/nubasis.cpp 3rdparty/einspline/nubspline_create.cpp 3rdparty/einspline/nugrid.cpp ) add_library(kritaimage SHARED ${kritaimage_LIB_SRCS} ${einspline_SRCS}) generate_export_header(kritaimage BASE_NAME kritaimage) target_link_libraries(kritaimage PUBLIC kritaversion kritawidgets kritaglobal kritapsd kritaodf kritapigment kritacommand kritawidgetutils kritametadata Qt5::Concurrent ) target_link_libraries(kritaimage PUBLIC ${Boost_SYSTEM_LIBRARY}) +if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB) + if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) + target_link_libraries(kritaimage PUBLIC atomic) + endif() +endif() + if(OPENEXR_FOUND) target_link_libraries(kritaimage PUBLIC ${OPENEXR_LIBRARIES}) endif() if(FFTW3_FOUND) target_link_libraries(kritaimage PRIVATE ${FFTW3_LIBRARIES}) endif() if(HAVE_VC) target_link_libraries(kritaimage PUBLIC ${Vc_LIBRARIES}) endif() if (NOT GSL_FOUND) message (WARNING "KRITA WARNING! No GNU Scientific Library was found! Krita's Shaped Gradients might be non-normalized! Please install GSL library.") else () target_link_libraries(kritaimage PRIVATE ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES}) endif () target_include_directories(kritaimage PUBLIC $ $ $ $ $ ) set_target_properties(kritaimage PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaimage ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/image/kis_filter_strategy.h b/libs/image/kis_filter_strategy.h index 8c09af6797..1a037e2d50 100644 --- a/libs/image/kis_filter_strategy.h +++ b/libs/image/kis_filter_strategy.h @@ -1,206 +1,200 @@ /* * Copyright (c) 2004 Michael Thaler * Copyright (c) 2005 C. Boemann * Copyright (c) 2013 Juan Palacios * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_FILTER_STRATEGY_H_ #define KIS_FILTER_STRATEGY_H_ #include #include "KoGenericRegistry.h" #include "KoID.h" #include "kritaimage_export.h" class KRITAIMAGE_EXPORT KisFilterStrategy { public: KisFilterStrategy(KoID id) : m_id(id) {} virtual ~KisFilterStrategy() { } QString id() { return m_id.id(); } QString name() { return m_id.name(); } virtual qreal valueAt(qreal /*t*/) const { return 0; } virtual qint32 intValueAt(qint32 t) const { return qint32(255*valueAt(t / 256.0)); } qreal support() { return supportVal; } qint32 intSupport() { return intSupportVal; } - virtual bool boxSpecial() { - return false; - } virtual QString description() { return QString(); } protected: qreal supportVal; qint32 intSupportVal; KoID m_id; }; class KRITAIMAGE_EXPORT KisHermiteFilterStrategy : public KisFilterStrategy { public: KisHermiteFilterStrategy() : KisFilterStrategy(KoID("Hermite", i18n("Hermite"))) { supportVal = 1.0; intSupportVal = 256; } ~KisHermiteFilterStrategy() override {} qint32 intValueAt(qint32 t) const override; qreal valueAt(qreal t) const override; }; class KRITAIMAGE_EXPORT KisBicubicFilterStrategy : public KisFilterStrategy { public: KisBicubicFilterStrategy() : KisFilterStrategy(KoID("Bicubic", i18n("Bicubic"))) { supportVal = 2.0; intSupportVal = 512; } ~KisBicubicFilterStrategy() override {} QString description() override { return i18n("Adds pixels using the color of surrounding pixels. Produces smoother tonal gradations than Bilinear."); } qint32 intValueAt(qint32 t) const override; }; class KRITAIMAGE_EXPORT KisBoxFilterStrategy : public KisFilterStrategy { public: KisBoxFilterStrategy() : KisFilterStrategy(KoID("NearestNeighbor", i18n("Nearest Neighbor"))) { supportVal = 0.5; intSupportVal = 128; } ~KisBoxFilterStrategy() override {} QString description() override { return i18n("Replicate pixels in the image. Preserves all the original detail, but can produce jagged effects."); } qint32 intValueAt(qint32 t) const override; qreal valueAt(qreal t) const override; - bool boxSpecial() override { - return true; - } }; class KRITAIMAGE_EXPORT KisBilinearFilterStrategy : public KisFilterStrategy { public: KisBilinearFilterStrategy() : KisFilterStrategy(KoID("Bilinear", i18n("Bilinear"))) { supportVal = 1.0; intSupportVal = 256; } ~KisBilinearFilterStrategy() override {} QString description() override { return i18n("Adds pixels averaging the color values of surrounding pixels. Produces medium quality results when the image is scaled from half to two times the original size."); } qint32 intValueAt(qint32 t) const override; qreal valueAt(qreal t) const override; }; class KRITAIMAGE_EXPORT KisBellFilterStrategy : public KisFilterStrategy { public: KisBellFilterStrategy() : KisFilterStrategy(KoID("Bell", i18n("Bell"))) { supportVal = 1.5; intSupportVal = 128 + 256; } ~KisBellFilterStrategy() override {} qreal valueAt(qreal t) const override; }; class KRITAIMAGE_EXPORT KisBSplineFilterStrategy : public KisFilterStrategy { public: KisBSplineFilterStrategy() : KisFilterStrategy(KoID("BSpline", i18n("BSpline"))) { supportVal = 2.0; intSupportVal = 512; } ~KisBSplineFilterStrategy() override {} qreal valueAt(qreal t) const override; }; class KRITAIMAGE_EXPORT KisLanczos3FilterStrategy : public KisFilterStrategy { public: KisLanczos3FilterStrategy() : KisFilterStrategy(KoID("Lanczos3", i18n("Lanczos3"))) { supportVal = 3.0; intSupportVal = 768; } ~KisLanczos3FilterStrategy() override {} QString description() override { return i18n("Offers similar results than Bicubic, but maybe a little bit sharper. Can produce light and dark halos along strong edges."); } qreal valueAt(qreal t) const override; private: qreal sinc(qreal x) const; }; class KRITAIMAGE_EXPORT KisMitchellFilterStrategy : public KisFilterStrategy { public: KisMitchellFilterStrategy() : KisFilterStrategy(KoID("Mitchell", i18n("Mitchell"))) { supportVal = 2.0; intSupportVal = 256; } ~KisMitchellFilterStrategy() override {} qreal valueAt(qreal t) const override; }; class KRITAIMAGE_EXPORT KisFilterStrategyRegistry : public KoGenericRegistry { public: KisFilterStrategyRegistry(); ~KisFilterStrategyRegistry() override; static KisFilterStrategyRegistry* instance(); /** * This function return a list of all the keys in KoID format by using the name() method * on the objects stored in the registry. */ QList listKeys() const; /** * This function return a string formatted in HTML that contains the descriptions of all objects * (with a non empty description) stored in the registry. */ QString formattedDescriptions() const; private: KisFilterStrategyRegistry(const KisFilterStrategyRegistry&); KisFilterStrategyRegistry operator=(const KisFilterStrategyRegistry&); }; #endif // KIS_FILTER_STRATEGY_H_ diff --git a/libs/ui/tool/kis_tool.h b/libs/ui/tool/kis_tool.h index 218926159e..e41f0e1599 100644 --- a/libs/ui/tool/kis_tool.h +++ b/libs/ui/tool/kis_tool.h @@ -1,318 +1,319 @@ /* * 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_TOOL_H_ #define KIS_TOOL_H_ #include #include #include #include #include #include #include #include #ifdef __GNUC__ #define WARN_WRONG_MODE(_mode) warnKrita << "Unexpected tool event has come to" << __func__ << "while being mode" << _mode << "!" #else #define WARN_WRONG_MODE(_mode) warnKrita << "Unexpected tool event has come while being mode" << _mode << "!" #endif #define CHECK_MODE_SANITY_OR_RETURN(_mode) if (mode() != _mode) { WARN_WRONG_MODE(mode()); return; } class KoCanvasBase; class KisFilterConfiguration; class QPainter; class QPainterPath; class QPolygonF; /// Definitions of the toolgroups of Krita static const QString TOOL_TYPE_SHAPE = "0 Krita/Shape"; // Geometric shapes like ellipses and lines static const QString TOOL_TYPE_TRANSFORM = "2 Krita/Transform"; // Tools that transform the layer; static const QString TOOL_TYPE_FILL = "3 Krita/Fill"; // Tools that fill parts of the canvas static const QString TOOL_TYPE_VIEW = "4 Krita/View"; // Tools that affect the canvas: pan, zoom, etc. static const QString TOOL_TYPE_SELECTION = "5 Krita/Select"; // Tools that select pixels //activation id for Krita tools, Krita tools are always active and handle locked and invisible layers by themself static const QString KRITA_TOOL_ACTIVATION_ID = "flake/always"; #include class KRITAUI_EXPORT KisTool : public KoToolBase { Q_OBJECT Q_PROPERTY(bool isActive READ isActive NOTIFY isActiveChanged) public: enum { FLAG_USES_CUSTOM_PRESET=0x01, FLAG_USES_CUSTOM_COMPOSITEOP=0x02, FLAG_USES_CUSTOM_SIZE=0x04 }; KisTool(KoCanvasBase * canvas, const QCursor & cursor); ~KisTool() override; virtual int flags() const { return 0; } void deleteSelection() override; // KoToolBase Implementation. public: /** * Called by KisToolProxy when the primary action of the tool is * going to be started now, that is when all the modifiers are * pressed and the only thing left is just to press the mouse * button. On coming of this callback the tool is supposed to * prepare the cursor and/or the outline to show the user shat is * going to happen next */ virtual void activatePrimaryAction(); /** * Called by KisToolProxy when the primary is no longer possible * to be started now, e.g. when its modifiers and released. The * tool is supposed revert all the preparetions it has doen in * activatePrimaryAction(). */ virtual void deactivatePrimaryAction(); /** * Called by KisToolProxy when a primary action for the tool is * started. The \p event stores the original event that * started the stroke. The \p event is _accepted_ by default. If * the tool decides to ignore this particular action (e.g. when * the node is not editable), it should call event->ignore(). Then * no further continuePrimaryAction() or endPrimaryAction() will * be called until the next user action. */ virtual void beginPrimaryAction(KoPointerEvent *event); /** * Called by KisToolProxy when the primary action is in progress * of pointer movement. If the tool has ignored the event in * beginPrimaryAction(), this method will not be called. */ virtual void continuePrimaryAction(KoPointerEvent *event); /** * Called by KisToolProxy when the primary action is being * finished, that is while mouseRelease or tabletRelease event. * If the tool has ignored the event in beginPrimaryAction(), this * method will not be called. */ virtual void endPrimaryAction(KoPointerEvent *event); /** * The same as beginPrimaryAction(), but called when the stroke is * started by a double-click * * \see beginPrimaryAction() */ virtual void beginPrimaryDoubleClickAction(KoPointerEvent *event); /** * Returns true if the tool can handle (and wants to handle) a * very tight flow of input events from the tablet */ virtual bool primaryActionSupportsHiResEvents() const; enum ToolAction { Primary, AlternateChangeSize, AlternatePickFgNode, AlternatePickBgNode, AlternatePickFgImage, AlternatePickBgImage, AlternateSecondary, AlternateThird, AlternateFourth, AlternateFifth, Alternate_NONE = 10000 }; // Technically users are allowed to configure this, but nobody ever would do that. // So these can basically be thought of as aliases to ctrl+click, etc. enum AlternateAction { ChangeSize = AlternateChangeSize, // Default: Shift+Left click PickFgNode = AlternatePickFgNode, // Default: Ctrl+Alt+Left click PickBgNode = AlternatePickBgNode, // Default: Ctrl+Alt+Right click PickFgImage = AlternatePickFgImage, // Default: Ctrl+Left click PickBgImage = AlternatePickBgImage, // Default: Ctrl+Right click Secondary = AlternateSecondary, Third = AlternateThird, Fourth = AlternateFourth, Fifth = AlternateFifth, NONE = 10000 }; static AlternateAction actionToAlternateAction(ToolAction action); virtual void activateAlternateAction(AlternateAction action); virtual void deactivateAlternateAction(AlternateAction action); virtual void beginAlternateAction(KoPointerEvent *event, AlternateAction action); virtual void continueAlternateAction(KoPointerEvent *event, AlternateAction action); virtual void endAlternateAction(KoPointerEvent *event, AlternateAction action); virtual void beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action); void mousePressEvent(KoPointerEvent *event) override; void mouseDoubleClickEvent(KoPointerEvent *event) override; void mouseTripleClickEvent(KoPointerEvent *event) override; void mouseReleaseEvent(KoPointerEvent *event) override; void mouseMoveEvent(KoPointerEvent *event) override; bool isActive() const; public Q_SLOTS: void activate(ToolActivation activation, const QSet &shapes) override; void deactivate() override; void canvasResourceChanged(int key, const QVariant & res) override; // Implement this slot in case there are any widgets or properties which need // to be updated after certain operations, to reflect the inner state correctly. // At the moment this is used for smoothing options in the freehand brush, but // this will likely be expanded. virtual void updateSettingsViews(); Q_SIGNALS: void isActiveChanged(bool isActivated); protected: // conversion methods are also needed by the paint information builder friend class KisToolPaintingInformationBuilder; /// Convert from native (postscript points) to image pixel /// coordinates. QPointF convertToPixelCoord(KoPointerEvent *e); QPointF convertToPixelCoord(const QPointF& pt); QPointF convertToPixelCoordAndSnap(KoPointerEvent *e, const QPointF &offset = QPointF(), bool useModifiers = true); QPointF convertToPixelCoordAndSnap(const QPointF& pt, const QPointF &offset = QPointF()); protected: QPointF widgetCenterInWidgetPixels(); QPointF convertDocumentToWidget(const QPointF& pt); /// Convert from native (postscript points) to integer image pixel /// coordinates. This rounds down (not truncate) the pixel coordinates and /// should be used in preference to QPointF::toPoint(), which rounds, /// to ensure the cursor acts on the pixel it is visually over. QPoint convertToImagePixelCoordFloored(KoPointerEvent *e); QRectF convertToPt(const QRectF &rect); qreal convertToPt(qreal value); QPointF viewToPixel(const QPointF &viewCoord) const; /// Convert an integer pixel coordinate into a view coordinate. /// The view coordinate is at the centre of the pixel. QPointF pixelToView(const QPoint &pixelCoord) const; /// Convert a floating point pixel coordinate into a view coordinate. QPointF pixelToView(const QPointF &pixelCoord) const; /// Convert a pixel rectangle into a view rectangle. QRectF pixelToView(const QRectF &pixelRect) const; /// Convert a pixel path into a view path QPainterPath pixelToView(const QPainterPath &pixelPath) const; /// Convert a pixel polygon into a view path QPolygonF pixelToView(const QPolygonF &pixelPolygon) const; /// Update the canvas for the given rectangle in image pixel coordinates. void updateCanvasPixelRect(const QRectF &pixelRect); /// Update the canvas for the given rectangle in view coordinates. void updateCanvasViewRect(const QRectF &viewRect); QWidget* createOptionWidget() override; /** * To determine whether this tool will change its behavior when * modifier keys are pressed */ virtual bool listeningToModifiers(); /** * Request that this tool no longer listen to modifier keys * (Responding to the request is optional) */ virtual void listenToModifiers(bool listen); protected: KisImageWSP image() const; QCursor cursor() const; /// Call this to set the document modified void notifyModified() const; KisImageWSP currentImage(); KoPatternSP currentPattern(); KoAbstractGradientSP currentGradient(); KisNodeSP currentNode() const; KisNodeList selectedNodes() const; KoColor currentFgColor(); KoColor currentBgColor(); KisPaintOpPresetSP currentPaintOpPreset(); KisFilterConfigurationSP currentGenerator(); /// paint the path which is in view coordinates, default paint mode is XOR_MODE, BW_MODE is also possible /// never apply transformations to the painter, they would be useless, if drawing in OpenGL mode. The coordinates in the path should be in view coordinates. void paintToolOutline(QPainter * painter, const QPainterPath &path); /// Checks checks if the current node is editable bool nodeEditable(); /// Checks checks if the selection is editable, only applies to local selection as global selection is always editable bool selectionEditable(); /// Override the cursor appropriately if current node is not editable bool overrideCursorIfNotEditable(); bool blockUntilOperationsFinished(); void blockUntilOperationsFinishedForced(); protected: - enum ToolMode { + enum ToolMode: int { HOVER_MODE, PAINT_MODE, SECONDARY_PAINT_MODE, MIRROR_AXIS_SETUP_MODE, GESTURE_MODE, PAN_MODE, - OTHER // not used now + OTHER, // tool-specific modes, like multibrush's symmetry axis setup + OTHER_1 }; virtual void setMode(ToolMode mode); virtual ToolMode mode() const; void setCursor(const QCursor &cursor); protected Q_SLOTS: /** * Called whenever the configuration settings change. */ virtual void resetCursorStyle(); private: struct Private; Private* const d; }; #endif // KIS_TOOL_H_ diff --git a/plugins/tools/basictools/kis_tool_move.cc b/plugins/tools/basictools/kis_tool_move.cc index 2517a97744..5c95d1c10c 100644 --- a/plugins/tools/basictools/kis_tool_move.cc +++ b/plugins/tools/basictools/kis_tool_move.cc @@ -1,665 +1,655 @@ /* * Copyright (c) 1999 Matthias Elter * 1999 Michael Koch * 2002 Patrick Julien * 2004 Boudewijn Rempt * 2016 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_move.h" #include #include "kis_cursor.h" #include "kis_selection.h" #include "kis_canvas2.h" #include "kis_image.h" #include "kis_tool_utils.h" #include "kis_paint_layer.h" #include "strokes/move_stroke_strategy.h" #include "kis_tool_movetooloptionswidget.h" #include "strokes/move_selection_stroke_strategy.h" #include "kis_resources_snapshot.h" #include "kis_action_registry.h" #include "krita_utils.h" #include #include #include "kis_node_manager.h" #include "kis_signals_blocker.h" #include struct KisToolMoveState : KisToolChangesTrackerData, boost::equality_comparable { KisToolMoveState(QPoint _accumulatedOffset) : accumulatedOffset(_accumulatedOffset) {} KisToolChangesTrackerData* clone() const { return new KisToolMoveState(*this); } bool operator ==(const KisToolMoveState &rhs) { return accumulatedOffset == rhs.accumulatedOffset; } QPoint accumulatedOffset; }; -KisToolMove::KisToolMove(KoCanvasBase * canvas) - : KisTool(canvas, KisCursor::moveCursor()), - m_updateCursorCompressor(100, KisSignalCompressor::FIRST_ACTIVE) +KisToolMove::KisToolMove(KoCanvasBase *canvas) + : KisTool(canvas, KisCursor::moveCursor()) + , m_updateCursorCompressor(100, KisSignalCompressor::FIRST_ACTIVE) { - m_canvas = dynamic_cast(canvas); + setObjectName("tool_move"); m_showCoordinatesAction = action("movetool-show-coordinates"); - createOptionWidget(); + m_showCoordinatesAction = action("movetool-show-coordinates"); + connect(&m_updateCursorCompressor, SIGNAL(timeout()), this, SLOT(resetCursorStyle())); - setObjectName("tool_move"); + m_optionsWidget = new MoveToolOptionsWidget(0, currentImage()->xRes(), toolId()); - m_showCoordinatesAction = action("movetool-show-coordinates"); + // See https://bugs.kde.org/show_bug.cgi?id=316896 + QWidget *specialSpacer = new QWidget(m_optionsWidget); + specialSpacer->setObjectName("SpecialSpacer"); + specialSpacer->setFixedSize(0, 0); + m_optionsWidget->layout()->addWidget(specialSpacer); - connect(&m_updateCursorCompressor, SIGNAL(timeout()), this, SLOT(resetCursorStyle())); + m_optionsWidget->setFixedHeight(m_optionsWidget->sizeHint().height()); + + m_showCoordinatesAction->setChecked(m_optionsWidget->showCoordinates()); + + m_optionsWidget->slotSetTranslate(m_handlesRect.topLeft() + currentOffset()); + + connect(m_optionsWidget, SIGNAL(sigSetTranslateX(int)), SLOT(moveBySpinX(int)), Qt::UniqueConnection); + connect(m_optionsWidget, SIGNAL(sigSetTranslateY(int)), SLOT(moveBySpinY(int)), Qt::UniqueConnection); + connect(m_optionsWidget, SIGNAL(sigRequestCommitOffsetChanges()), this, SLOT(commitChanges()), Qt::UniqueConnection); + + connect(this, SIGNAL(moveInNewPosition(QPoint)), m_optionsWidget, SLOT(slotSetTranslate(QPoint)), Qt::UniqueConnection); + + connect(qobject_cast(canvas)->viewManager()->nodeManager(), SIGNAL(sigUiNeedChangeSelectedNodes(KisNodeList)), this, SLOT(slotNodeChanged(KisNodeList)), Qt::UniqueConnection); } KisToolMove::~KisToolMove() { endStroke(); } void KisToolMove::resetCursorStyle() { KisTool::resetCursorStyle(); if (!isActive()) return; KisImageSP image = this->image(); KisResourcesSnapshotSP resources = - new KisResourcesSnapshot(image, currentNode(), this->canvas()->resourceManager()); + new KisResourcesSnapshot(image, currentNode(), canvas()->resourceManager()); KisSelectionSP selection = resources->activeSelection(); KisNodeList nodes = fetchSelectedNodes(moveToolMode(), &m_lastCursorPos, selection); if (nodes.isEmpty()) { canvas()->setCursor(Qt::ForbiddenCursor); } } KisNodeList KisToolMove::fetchSelectedNodes(MoveToolMode mode, const QPoint *pixelPoint, KisSelectionSP selection) { KisNodeList nodes; KisImageSP image = this->image(); if (mode != MoveSelectedLayer && pixelPoint) { const bool wholeGroup = !selection && mode == MoveGroup; KisNodeSP node = KisToolUtils::findNode(image->root(), *pixelPoint, wholeGroup); if (node) { nodes = {node}; } } if (nodes.isEmpty()) { nodes = this->selectedNodes(); KritaUtils::filterContainer(nodes, [](KisNodeSP node) { return node->isEditable(); }); } return nodes; } bool KisToolMove::startStrokeImpl(MoveToolMode mode, const QPoint *pos) { KisNodeSP node; KisImageSP image = this->image(); KisResourcesSnapshotSP resources = - new KisResourcesSnapshot(image, currentNode(), this->canvas()->resourceManager()); + new KisResourcesSnapshot(image, currentNode(), canvas()->resourceManager()); KisSelectionSP selection = resources->activeSelection(); KisNodeList nodes = fetchSelectedNodes(mode, pos, selection); if (nodes.size() == 1) { node = nodes.first(); } if (nodes.isEmpty()) { return false; } /** * If the target node has changed, the stroke should be * restarted. Otherwise just continue processing current node. */ if (m_strokeId && !tryEndPreviousStroke(nodes)) { return true; } initHandles(nodes); KisStrokeStrategy *strategy; KisPaintLayerSP paintLayer = node ? dynamic_cast(node.data()) : 0; if (paintLayer && selection && !selection->isTotallyUnselected(image->bounds())) { strategy = new MoveSelectionStrokeStrategy(paintLayer, selection, image.data(), image.data()); } else { strategy = new MoveStrokeStrategy(nodes, image.data(), image.data()); } m_strokeId = image->startStroke(strategy); m_currentlyProcessingNodes = nodes; m_accumulatedOffset = QPoint(); KIS_SAFE_ASSERT_RECOVER(m_changesTracker.isEmpty()) { m_changesTracker.reset(); } commitChanges(); return true; } QPoint KisToolMove::currentOffset() const { return m_accumulatedOffset + m_dragPos - m_dragStart; } void KisToolMove::notifyGuiAfterMove(bool showFloatingMessage) { if (!m_optionsWidget) return; const QPoint currentTopLeft = m_handlesRect.topLeft() + currentOffset(); KisSignalsBlocker b(m_optionsWidget); emit moveInNewPosition(currentTopLeft); // TODO: fetch this info not from options widget, but from config const bool showCoordinates = m_optionsWidget->showCoordinates(); if (showCoordinates && showFloatingMessage) { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); kisCanvas->viewManager()-> showFloatingMessage( i18nc("floating message in move tool", "X: %1 px, Y: %2 px", currentTopLeft.x(), currentTopLeft.y()), QIcon(), 1000, KisFloatingMessage::High); } } bool KisToolMove::tryEndPreviousStroke(KisNodeList nodes) { if (!m_strokeId) return false; bool strokeEnded = false; if (!KritaUtils::compareListsUnordered(nodes, m_currentlyProcessingNodes)) { endStroke(); strokeEnded = true; } return strokeEnded; } void KisToolMove::commitChanges() { KIS_SAFE_ASSERT_RECOVER_RETURN(m_strokeId); QSharedPointer newState(new KisToolMoveState(m_accumulatedOffset)); KisToolMoveState *lastState = dynamic_cast(m_changesTracker.lastState().data()); if (lastState && *lastState == *newState) return; m_changesTracker.commitConfig(newState); } void KisToolMove::moveDiscrete(MoveDirection direction, bool big) { if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging if (!currentNode()->isEditable()) return; // Don't move invisible nodes if (startStrokeImpl(MoveSelectedLayer, 0)) { setMode(KisTool::PAINT_MODE); } // Larger movement if "shift" key is pressed. qreal scale = big ? m_optionsWidget->moveScale() : 1.0; qreal moveStep = m_optionsWidget->moveStep() * scale; const QPoint offset = direction == Up ? QPoint( 0, -moveStep) : direction == Down ? QPoint( 0, moveStep) : direction == Left ? QPoint(-moveStep, 0) : QPoint( moveStep, 0) ; m_accumulatedOffset += offset; image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(); commitChanges(); setMode(KisTool::HOVER_MODE); } void KisToolMove::activate(ToolActivation toolActivation, const QSet &shapes) { KisTool::activate(toolActivation, shapes); QAction *a = action("movetool-move-up"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Up, false);}); a = action("movetool-move-down"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Down, false);}); a = action("movetool-move-left"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Left, false);}); a = action("movetool-move-right"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Right, false);}); a = action("movetool-move-up-more"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Up, true);}); a = action("movetool-move-down-more"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Down, true);}); a = action("movetool-move-left-more"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Left, true);}); a = action("movetool-move-right-more"); connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Right, true);}); connect(m_showCoordinatesAction, SIGNAL(triggered(bool)), m_optionsWidget, SLOT(setShowCoordinates(bool)), Qt::UniqueConnection); connect(m_optionsWidget, SIGNAL(showCoordinatesChanged(bool)), m_showCoordinatesAction, SLOT(setChecked(bool)), Qt::UniqueConnection); connect(&m_changesTracker, SIGNAL(sigConfigChanged(KisToolChangesTrackerDataSP)), SLOT(slotTrackerChangedConfig(KisToolChangesTrackerDataSP))); slotNodeChanged(this->selectedNodes()); } void KisToolMove::paint(QPainter& gc, const KoViewConverter &converter) { Q_UNUSED(converter); if (m_strokeId) { QPainterPath handles; handles.addRect(m_handlesRect.translated(currentOffset())); QPainterPath path = pixelToView(handles); paintToolOutline(&gc, path); } } void KisToolMove::initHandles(const KisNodeList &nodes) { /** * The handles should be initialized only once, **before** the start of * the stroke. If the nodes change, we should restart the stroke. */ KIS_SAFE_ASSERT_RECOVER_NOOP(!m_strokeId); m_handlesRect = QRect(); for (KisNodeSP node : nodes) { node->exactBounds(); m_handlesRect |= node->exactBounds(); } if (image()->globalSelection()) { m_handlesRect &= image()->globalSelection()->selectedExactRect(); } } void KisToolMove::deactivate() { QAction *a = action("movetool-move-up"); disconnect(a, 0, this, 0); a = action("movetool-move-down"); disconnect(a, 0, this, 0); a = action("movetool-move-left"); disconnect(a, 0, this, 0); a = action("movetool-move-right"); disconnect(a, 0, this, 0); a = action("movetool-move-up-more"); disconnect(a, 0, this, 0); a = action("movetool-move-down-more"); disconnect(a, 0, this, 0); a = action("movetool-move-left-more"); disconnect(a, 0, this, 0); a = action("movetool-move-right-more"); disconnect(a, 0, this, 0); disconnect(m_showCoordinatesAction, 0, this, 0); disconnect(m_optionsWidget, 0, this, 0); endStroke(); KisTool::deactivate(); } void KisToolMove::requestStrokeEnd() { endStroke(); } void KisToolMove::requestStrokeCancellation() { cancelStroke(); } void KisToolMove::requestUndoDuringStroke() { if (!m_strokeId) return; if (m_changesTracker.isEmpty()) { cancelStroke(); } else { m_changesTracker.requestUndo(); } } void KisToolMove::beginPrimaryAction(KoPointerEvent *event) { startAction(event, moveToolMode()); } void KisToolMove::continuePrimaryAction(KoPointerEvent *event) { continueAction(event); } void KisToolMove::endPrimaryAction(KoPointerEvent *event) { endAction(event); } void KisToolMove::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { // Ctrl+Right click toggles between moving current layer and moving layer w/ content if (action == PickFgNode || action == PickBgImage) { MoveToolMode mode = moveToolMode(); if (mode == MoveSelectedLayer) { mode = MoveFirstLayer; } else if (mode == MoveFirstLayer) { mode = MoveSelectedLayer; } startAction(event, mode); } else { startAction(event, MoveGroup); } } void KisToolMove::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(action) continueAction(event); } void KisToolMove::endAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(action) endAction(event); } void KisToolMove::mouseMoveEvent(KoPointerEvent *event) { m_lastCursorPos = convertToPixelCoord(event).toPoint(); KisTool::mouseMoveEvent(event); if (moveToolMode() == MoveFirstLayer) { m_updateCursorCompressor.start(); } } void KisToolMove::startAction(KoPointerEvent *event, MoveToolMode mode) { QPoint pos = convertToPixelCoordAndSnap(event).toPoint(); m_dragStart = pos; m_dragPos = pos; if (startStrokeImpl(mode, &pos)) { setMode(KisTool::PAINT_MODE); } else { event->ignore(); m_dragPos = QPoint(); m_dragStart = QPoint(); } - m_canvas->updateCanvas(); + qobject_cast(canvas())->updateCanvas(); } void KisToolMove::continueAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); if (!m_strokeId) return; QPoint pos = convertToPixelCoordAndSnap(event).toPoint(); pos = applyModifiers(event->modifiers(), pos); m_dragPos = pos; drag(pos); notifyGuiAfterMove(); - m_canvas->updateCanvas(); + qobject_cast(canvas())->updateCanvas(); } void KisToolMove::endAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); setMode(KisTool::HOVER_MODE); if (!m_strokeId) return; QPoint pos = convertToPixelCoordAndSnap(event).toPoint(); pos = applyModifiers(event->modifiers(), pos); drag(pos); m_accumulatedOffset += pos - m_dragStart; m_dragStart = QPoint(); m_dragPos = QPoint(); commitChanges(); notifyGuiAfterMove(); - m_canvas->updateCanvas(); + qobject_cast(canvas())->updateCanvas(); } void KisToolMove::drag(const QPoint& newPos) { KisImageWSP image = currentImage(); QPoint offset = m_accumulatedOffset + newPos - m_dragStart; image->addJob(m_strokeId, new MoveStrokeStrategy::Data(offset)); } void KisToolMove::endStroke() { if (!m_strokeId) return; KisImageSP image = currentImage(); image->endStroke(m_strokeId); m_strokeId.clear(); m_changesTracker.reset(); m_currentlyProcessingNodes.clear(); m_accumulatedOffset = QPoint(); - m_canvas->updateCanvas(); + qobject_cast(canvas())->updateCanvas(); } void KisToolMove::slotTrackerChangedConfig(KisToolChangesTrackerDataSP state) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_strokeId); KisToolMoveState *newState = dynamic_cast(state.data()); KIS_SAFE_ASSERT_RECOVER_RETURN(newState); if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging m_accumulatedOffset = newState->accumulatedOffset; image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(); } void KisToolMove::cancelStroke() { if (!m_strokeId) return; KisImageSP image = currentImage(); image->cancelStroke(m_strokeId); m_strokeId.clear(); m_changesTracker.reset(); m_currentlyProcessingNodes.clear(); m_accumulatedOffset = QPoint(); notifyGuiAfterMove(); - m_canvas->updateCanvas(); + qobject_cast(canvas())->updateCanvas(); } QWidget* KisToolMove::createOptionWidget() { - if (!currentImage()) - return 0; - - m_optionsWidget = new MoveToolOptionsWidget(0, currentImage()->xRes(), toolId()); - // See https://bugs.kde.org/show_bug.cgi?id=316896 - QWidget *specialSpacer = new QWidget(m_optionsWidget); - specialSpacer->setObjectName("SpecialSpacer"); - specialSpacer->setFixedSize(0, 0); - m_optionsWidget->layout()->addWidget(specialSpacer); - - m_optionsWidget->setFixedHeight(m_optionsWidget->sizeHint().height()); - - m_showCoordinatesAction->setChecked(m_optionsWidget->showCoordinates()); - - m_optionsWidget->slotSetTranslate(m_handlesRect.topLeft() + currentOffset()); - - connect(m_optionsWidget, SIGNAL(sigSetTranslateX(int)), SLOT(moveBySpinX(int))); - connect(m_optionsWidget, SIGNAL(sigSetTranslateY(int)), SLOT(moveBySpinY(int))); - connect(m_optionsWidget, SIGNAL(sigRequestCommitOffsetChanges()), this, SLOT(commitChanges()), Qt::UniqueConnection); - - connect(this, SIGNAL(moveInNewPosition(QPoint)), m_optionsWidget, SLOT(slotSetTranslate(QPoint))); - - KisCanvas2 *kisCanvas = dynamic_cast(canvas()); - - connect(kisCanvas->viewManager()->nodeManager(), SIGNAL(sigUiNeedChangeSelectedNodes(KisNodeList)), - this, SLOT(slotNodeChanged(KisNodeList))); - return m_optionsWidget; } KisToolMove::MoveToolMode KisToolMove::moveToolMode() const { if (m_optionsWidget) return m_optionsWidget->mode(); return MoveSelectedLayer; } QPoint KisToolMove::applyModifiers(Qt::KeyboardModifiers modifiers, QPoint pos) { QPoint move = pos - m_dragStart; // Snap to axis if (modifiers & Qt::ShiftModifier) { move = snapToClosestAxis(move); } // "Precision mode" - scale down movement by 1/5 if (modifiers & Qt::AltModifier) { const qreal SCALE_FACTOR = .2; move = SCALE_FACTOR * move; } return m_dragStart + move; } void KisToolMove::moveBySpinX(int newX) { if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging if (!currentNode()->isEditable()) return; // Don't move invisible nodes if (startStrokeImpl(MoveSelectedLayer, 0)) { setMode(KisTool::PAINT_MODE); } m_accumulatedOffset.rx() = newX - m_handlesRect.x(); image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(false); setMode(KisTool::HOVER_MODE); } void KisToolMove::moveBySpinY(int newY) { if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging if (!currentNode()->isEditable()) return; // Don't move invisible nodes if (startStrokeImpl(MoveSelectedLayer, 0)) { setMode(KisTool::PAINT_MODE); } m_accumulatedOffset.ry() = newY - m_handlesRect.y(); image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(false); setMode(KisTool::HOVER_MODE); } void KisToolMove::slotNodeChanged(KisNodeList nodes) { if (m_strokeId && !tryEndPreviousStroke(nodes)) { return; } initHandles(nodes); notifyGuiAfterMove(false); } QList KisToolMoveFactory::createActionsImpl() { KisActionRegistry *actionRegistry = KisActionRegistry::instance(); QList actions = KisToolPaintFactoryBase::createActionsImpl(); actions << actionRegistry->makeQAction("movetool-move-up"); actions << actionRegistry->makeQAction("movetool-move-down"); actions << actionRegistry->makeQAction("movetool-move-left"); actions << actionRegistry->makeQAction("movetool-move-right"); actions << actionRegistry->makeQAction("movetool-move-up-more"); actions << actionRegistry->makeQAction("movetool-move-down-more"); actions << actionRegistry->makeQAction("movetool-move-left-more"); actions << actionRegistry->makeQAction("movetool-move-right-more"); actions << actionRegistry->makeQAction("movetool-show-coordinates"); return actions; } diff --git a/plugins/tools/basictools/kis_tool_move.h b/plugins/tools/basictools/kis_tool_move.h index 9bd7c3f613..920b229acc 100644 --- a/plugins/tools/basictools/kis_tool_move.h +++ b/plugins/tools/basictools/kis_tool_move.h @@ -1,193 +1,191 @@ /* * Copyright (c) 1999 Matthias Elter * 1999 Michael Koch * 2003 Patrick Julien * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_MOVE_H_ #define KIS_TOOL_MOVE_H_ #include #include #include #include #include #include #include #include #include #include "KisToolChangesTracker.h" #include "kis_signal_compressor.h" #include "kis_canvas2.h" class KoCanvasBase; class MoveToolOptionsWidget; class KisDocument; class KisToolMove : public KisTool { Q_OBJECT Q_ENUMS(MoveToolMode); public: KisToolMove(KoCanvasBase * canvas); ~KisToolMove() override; /** * @brief wantsAutoScroll * reimplemented from KoToolBase * there's an issue where autoscrolling with this tool never makes the * stroke end, so we return false here so that users don't get stuck with * the tool. See bug 362659 * @return false */ bool wantsAutoScroll() const override { return false; } public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; public Q_SLOTS: void requestStrokeEnd() override; void requestStrokeCancellation() override; void requestUndoDuringStroke() override; protected Q_SLOTS: void resetCursorStyle() override; public: enum MoveToolMode { MoveSelectedLayer, MoveFirstLayer, MoveGroup }; enum MoveDirection { Up, Down, Left, Right }; void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override; void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override; void endAlternateAction(KoPointerEvent *event, AlternateAction action) override; void mouseMoveEvent(KoPointerEvent *event) override; void startAction(KoPointerEvent *event, MoveToolMode mode); void continueAction(KoPointerEvent *event); void endAction(KoPointerEvent *event); void paint(QPainter& gc, const KoViewConverter &converter) override; void initHandles(const KisNodeList &nodes); QWidget *createOptionWidget() override; void updateUIUnit(int newUnit); MoveToolMode moveToolMode() const; void setShowCoordinates(bool value); public Q_SLOTS: void moveDiscrete(MoveDirection direction, bool big); void moveBySpinX(int newX); void moveBySpinY(int newY); void slotNodeChanged(KisNodeList nodes); void commitChanges(); Q_SIGNALS: void moveToolModeChanged(); void moveInNewPosition(QPoint); private: void drag(const QPoint& newPos); void cancelStroke(); QPoint applyModifiers(Qt::KeyboardModifiers modifiers, QPoint pos); bool startStrokeImpl(MoveToolMode mode, const QPoint *pos); QPoint currentOffset() const; void notifyGuiAfterMove(bool showFloatingMessage = true); bool tryEndPreviousStroke(KisNodeList nodes); KisNodeList fetchSelectedNodes(MoveToolMode mode, const QPoint *pixelPoint, KisSelectionSP selection); private Q_SLOTS: void endStroke(); void slotTrackerChangedConfig(KisToolChangesTrackerDataSP state); private: MoveToolOptionsWidget* m_optionsWidget {0}; QPoint m_dragStart; ///< Point where current cursor dragging began QPoint m_accumulatedOffset; ///< Total offset including multiple clicks, up/down/left/right keys, etc. added together KisStrokeId m_strokeId; KisNodeList m_currentlyProcessingNodes; int m_resolution; QAction *m_showCoordinatesAction {0}; - KisCanvas2 *m_canvas {0}; - QPoint m_dragPos; QRect m_handlesRect; KisToolChangesTracker m_changesTracker; QPoint m_lastCursorPos; KisSignalCompressor m_updateCursorCompressor; }; class KisToolMoveFactory : public KisToolPaintFactoryBase { public: KisToolMoveFactory() : KisToolPaintFactoryBase("KritaTransform/KisToolMove") { setToolTip(i18n("Move Tool")); setSection(TOOL_TYPE_TRANSFORM); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setPriority(3); setIconName(koIconNameCStr("krita_tool_move")); setShortcut(QKeySequence(Qt::Key_T)); } ~KisToolMoveFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolMove(canvas); } QList createActionsImpl() override; }; #endif // KIS_TOOL_MOVE_H_ diff --git a/plugins/tools/basictools/kis_tool_multihand.cpp b/plugins/tools/basictools/kis_tool_multihand.cpp index 79e4928469..c94bf9c84d 100644 --- a/plugins/tools/basictools/kis_tool_multihand.cpp +++ b/plugins/tools/basictools/kis_tool_multihand.cpp @@ -1,421 +1,525 @@ /* * Copyright (c) 2011 Lukáš Tvrdý * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - #include "kis_tool_multihand.h" #include #include #include #include #include #include #include #include "kis_canvas2.h" #include "kis_cursor.h" #include "kis_tool_multihand_helper.h" static const int MAXIMUM_BRUSHES = 50; #include #ifdef Q_OS_WIN // quoting DRAND48(3) man-page: // These functions are declared obsolete by SVID 3, // which states that rand(3) should be used instead. #define drand48() (static_cast(qrand()) / static_cast(RAND_MAX)) #endif KisToolMultihand::KisToolMultihand(KoCanvasBase *canvas) : KisToolBrush(canvas), m_transformMode(SYMMETRY), m_angle(0), m_handsCount(6), m_mirrorVertically(false), m_mirrorHorizontally(false), m_showAxes(false), m_translateRadius(100), - m_setupAxesFlag(false) + m_setupAxesFlag(false), + m_addSubbrushesMode(false) , customUI(0) { m_helper = new KisToolMultihandHelper(paintingInformationBuilder(), kundo2_i18n("Multibrush Stroke")); resetHelper(m_helper); if (image()) { m_axesPoint = QPointF(0.5 * image()->width(), 0.5 * image()->height()); } } KisToolMultihand::~KisToolMultihand() { } - - void KisToolMultihand::beginPrimaryAction(KoPointerEvent *event) { if(m_setupAxesFlag) { setMode(KisTool::OTHER); m_axesPoint = convertToPixelCoord(event->point); requestUpdateOutline(event->point, 0); updateCanvas(); } + else if (m_addSubbrushesMode){ + QPointF newPoint = convertToPixelCoord(event->point); + m_subbrOriginalLocations << newPoint; + requestUpdateOutline(event->point, 0); + updateCanvas(); + } else { initTransformations(); KisToolFreehand::beginPrimaryAction(event); } } void KisToolMultihand::continuePrimaryAction(KoPointerEvent *event) { if(mode() == KisTool::OTHER) { m_axesPoint = convertToPixelCoord(event->point); requestUpdateOutline(event->point, 0); updateCanvas(); } else { + requestUpdateOutline(event->point, 0); KisToolFreehand::continuePrimaryAction(event); } } void KisToolMultihand::endPrimaryAction(KoPointerEvent *event) { if(mode() == KisTool::OTHER) { setMode(KisTool::HOVER_MODE); requestUpdateOutline(event->point, 0); finishAxesSetup(); } else { KisToolFreehand::endPrimaryAction(event); } } +void KisToolMultihand::beginAlternateAction(KoPointerEvent* event, AlternateAction action) +{ + if (action != ChangeSize || m_transformMode != COPYTRANSLATE || !m_addSubbrushesMode) { + KisToolBrush::beginAlternateAction(event, action); + return; + } + setMode(KisTool::OTHER_1); + m_axesPoint = convertToPixelCoord(event->point); + requestUpdateOutline(event->point, 0); + updateCanvas(); +} + +void KisToolMultihand::continueAlternateAction(KoPointerEvent* event, AlternateAction action) +{ + if (action != ChangeSize || m_transformMode != COPYTRANSLATE || !m_addSubbrushesMode) { + KisToolBrush::continueAlternateAction(event, action); + return; + } + if (mode() == KisTool::OTHER_1) { + m_axesPoint = convertToPixelCoord(event->point); + requestUpdateOutline(event->point, 0); + updateCanvas(); + } +} + +void KisToolMultihand::endAlternateAction(KoPointerEvent* event, AlternateAction action) +{ + if (action != ChangeSize || m_transformMode != COPYTRANSLATE || !m_addSubbrushesMode) { + KisToolBrush::continueAlternateAction(event, action); + return; + } + if (mode() == KisTool::OTHER_1) { + setMode(KisTool::HOVER_MODE); + } +} + +void KisToolMultihand::mouseMoveEvent(KoPointerEvent* event) +{ + if (mode() == HOVER_MODE) { + m_lastToolPos=convertToPixelCoord(event->point); + } + KisToolBrush::mouseMoveEvent(event); +} + void KisToolMultihand::paint(QPainter& gc, const KoViewConverter &converter) { + QPainterPath path; + if (m_transformMode == COPYTRANSLATE) { + for (QPointF dPos : m_subbrOriginalLocations) { + if (m_addSubbrushesMode) { + path.addEllipse(dPos, 10, 10); // Show subbrush reference locations + path.moveTo(dPos.x() - 15, dPos.y()); + path.lineTo(dPos.x() + 15, dPos.y()); + path.moveTo(dPos.x(), dPos.y() - 15); + path.lineTo(dPos.x(), dPos.y() + 15); + } + else { + // Show where subbrush strokes are predicted to land + path += m_currentOutline.translated(dPos - m_axesPoint); + } + } + if (m_addSubbrushesMode) { + // add a cross shape to the main tool outline as well + + path.moveTo(m_lastToolPos.x() - 15, m_lastToolPos.y()); + path.lineTo(m_lastToolPos.x() + 15, m_lastToolPos.y()); + path.moveTo(m_lastToolPos.x(), m_lastToolPos.y() - 15); + path.lineTo(m_lastToolPos.x(), m_lastToolPos.y() + 15); + } + } if(m_setupAxesFlag) { int diagonal = (currentImage()->height() + currentImage()->width()); - QPainterPath path; path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle), m_axesPoint.y()-diagonal*sin(m_angle)); path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle), m_axesPoint.y()+diagonal*sin(m_angle)); path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()-diagonal*sin(m_angle+M_PI_2)); path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()+diagonal*sin(m_angle+M_PI_2)); - paintToolOutline(&gc, pixelToView(path)); } else { KisToolFreehand::paint(gc, converter); - if(m_showAxes){ + // Force paint axeslines of "origin" point when in COPYTRANSLATE addSubbrushes mode. + if(m_showAxes || (m_transformMode == COPYTRANSLATE && m_addSubbrushesMode)){ int diagonal = (currentImage()->height() + currentImage()->width()); - QPainterPath path; path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle), m_axesPoint.y()-diagonal*sin(m_angle)); path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle), m_axesPoint.y()+diagonal*sin(m_angle)); path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()-diagonal*sin(m_angle+M_PI_2)); path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()+diagonal*sin(m_angle+M_PI_2)); - paintToolOutline(&gc, pixelToView(path)); } } + paintToolOutline(&gc, pixelToView(path)); } void KisToolMultihand::initTransformations() { QVector transformations; QTransform m; if(m_transformMode == SYMMETRY) { qreal angle = 0; qreal angleStep = (2 * M_PI) / m_handsCount; for(int i = 0; i < m_handsCount; i++) { m.translate(m_axesPoint.x(), m_axesPoint.y()); m.rotateRadians(angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); angle += angleStep; } } else if(m_transformMode == MIRROR) { transformations << m; if (m_mirrorHorizontally) { m.translate(m_axesPoint.x(),m_axesPoint.y()); m.rotateRadians(m_angle); m.scale(-1,1); m.rotateRadians(-m_angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); } if (m_mirrorVertically) { m.translate(m_axesPoint.x(),m_axesPoint.y()); m.rotateRadians(m_angle); m.scale(1,-1); m.rotateRadians(-m_angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); } if (m_mirrorVertically && m_mirrorHorizontally){ m.translate(m_axesPoint.x(),m_axesPoint.y()); m.rotateRadians(m_angle); m.scale(-1,-1); m.rotateRadians(-m_angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); } } else if(m_transformMode == SNOWFLAKE) { qreal angle = 0; qreal angleStep = (2 * M_PI) / m_handsCount/4; for(int i = 0; i < m_handsCount*4; i++) { if ((i%2)==1) { m.translate(m_axesPoint.x(), m_axesPoint.y()); m.rotateRadians(m_angle-angleStep); m.rotateRadians(angle); m.scale(-1,1); m.rotateRadians(-m_angle+angleStep); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); angle += angleStep*2; } else { m.translate(m_axesPoint.x(), m_axesPoint.y()); m.rotateRadians(m_angle-angleStep); m.rotateRadians(angle); m.rotateRadians(-m_angle+angleStep); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); angle += angleStep*2; } } } - else /* if(m_transformationNode == TRANSLATE) */ { + else if(m_transformMode == TRANSLATE) { /** * TODO: currently, the seed is the same for all the * strokes */ for (int i = 0; i < m_handsCount; i++){ qreal angle = drand48() * M_PI * 2; qreal length = drand48(); // convert the Polar coordinates to Cartesian coordinates qreal nx = (m_translateRadius * cos(angle) * length); qreal ny = (m_translateRadius * sin(angle) * length); m.translate(m_axesPoint.x(),m_axesPoint.y()); m.rotateRadians(m_angle); m.translate(nx,ny); m.rotateRadians(-m_angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); } + } else if (m_transformMode == COPYTRANSLATE) { + transformations << m; + for (QPointF dPos : m_subbrOriginalLocations) { + QPointF resPos = dPos-m_axesPoint; // Calculate the difference between subbrush reference position and "origin" reference + m.translate(resPos.x(), resPos.y()); + transformations << m; + m.reset(); + } } m_helper->setupTransformations(transformations); } QWidget* KisToolMultihand::createOptionWidget() { QWidget *widget = KisToolBrush::createOptionWidget(); customUI = new KisToolMultiHandConfigWidget(); // brush smoothing option. - customUI->layout()->addWidget(widget); + //customUI->layout()->addWidget(widget); customUI->smoothingOptionsLayout->addWidget(widget); // setup common parameters that all of the modes will see connect(customUI->showAxesCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetAxesVisible(bool))); customUI->showAxesCheckbox->setChecked((bool)m_configGroup.readEntry("showAxes", false)); connect(image(), SIGNAL(sigSizeChanged(QPointF,QPointF)), this, SLOT(resetAxes())); customUI->moveOriginButton->setCheckable(true); connect(customUI->moveOriginButton, SIGNAL(clicked(bool)),this, SLOT(activateAxesPointModeSetup())); connect(customUI->resetOriginButton, SIGNAL(released()), this, SLOT(resetAxes())); customUI->multihandTypeCombobox->addItem(i18n("Symmetry"),int(SYMMETRY)); // axis mode customUI->multihandTypeCombobox->addItem(i18n("Mirror"),int(MIRROR)); customUI->multihandTypeCombobox->addItem(i18n("Translate"),int(TRANSLATE)); customUI->multihandTypeCombobox->addItem(i18n("Snowflake"),int(SNOWFLAKE)); + customUI->multihandTypeCombobox->addItem(i18n("Copy Translate"),int(COPYTRANSLATE)); connect(customUI->multihandTypeCombobox,SIGNAL(currentIndexChanged(int)),this, SLOT(slotSetTransformMode(int))); customUI->multihandTypeCombobox->setCurrentIndex(m_configGroup.readEntry("transformMode", 0)); slotSetTransformMode(customUI->multihandTypeCombobox->currentIndex()); customUI->axisRotationSpinbox->setSuffix(QChar(Qt::Key_degree)); // origin rotation customUI->axisRotationSpinbox->setSingleStep(0.5); customUI->axisRotationSpinbox->setRange(0.0, 90.0, 1); customUI->axisRotationSpinbox->setValue(m_configGroup.readEntry("axesAngle", 0.0)); connect( customUI->axisRotationSpinbox, SIGNAL(valueChanged(qreal)),this, SLOT(slotSetAxesAngle(qreal))); - - // symmetry mode options customUI->brushCountSpinBox->setRange(1, MAXIMUM_BRUSHES); connect(customUI->brushCountSpinBox, SIGNAL(valueChanged(int)),this, SLOT(slotSetHandsCount(int))); customUI->brushCountSpinBox->setValue(m_configGroup.readEntry("handsCount", 4)); // mirror mode specific options connect(customUI->horizontalCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetMirrorHorizontally(bool))); customUI->horizontalCheckbox->setChecked((bool)m_configGroup.readEntry("mirrorHorizontally", false)); connect(customUI->verticalCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetMirrorVertically(bool))); customUI->verticalCheckbox->setChecked((bool)m_configGroup.readEntry("mirrorVertically", false)); // translate mode options customUI->translationRadiusSpinbox->setRange(0, 200); customUI->translationRadiusSpinbox->setSuffix(i18n(" px")); customUI->translationRadiusSpinbox->setValue(m_configGroup.readEntry("translateRadius", 0)); connect(customUI->translationRadiusSpinbox,SIGNAL(valueChanged(int)),this,SLOT(slotSetTranslateRadius(int))); + // Copy translate mode options and actions + connect(customUI->addSubbrushButton, &QPushButton::clicked, this, &KisToolMultihand::slotAddSubbrushesMode); + connect(customUI->removeSubbrushButton, &QPushButton::clicked, this, &KisToolMultihand::slotRemoveAllSubbrushes); // snowflake re-uses the existing options, so there is no special parameters for that... return static_cast(customUI); // keeping it in the native class until the end allows us to access the UI components } void KisToolMultihand::activateAxesPointModeSetup() { if (customUI->moveOriginButton->isChecked()){ m_setupAxesFlag = true; useCursor(KisCursor::crossCursor()); updateCanvas(); } else { finishAxesSetup(); } } void KisToolMultihand::resetAxes() { m_axesPoint = QPointF(0.5 * image()->width(), 0.5 * image()->height()); finishAxesSetup(); } void KisToolMultihand::finishAxesSetup() { m_setupAxesFlag = false; customUI->moveOriginButton->setChecked(false); resetCursorStyle(); updateCanvas(); } void KisToolMultihand::updateCanvas() { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); Q_ASSERT(kisCanvas); kisCanvas->updateCanvas(); } void KisToolMultihand::slotSetHandsCount(int count) { m_handsCount = count; m_configGroup.writeEntry("handsCount", count); } void KisToolMultihand::slotSetAxesAngle(qreal angle) { //negative so axes rotates counter clockwise m_angle = -angle*M_PI/180; updateCanvas(); m_configGroup.writeEntry("axesAngle", angle); } void KisToolMultihand::slotSetTransformMode(int index) { m_transformMode = enumTransforModes(customUI->multihandTypeCombobox->itemData(index).toInt()); m_configGroup.writeEntry("transformMode", index); // hide all of the UI elements by default customUI->horizontalCheckbox->setVisible(false); customUI->verticalCheckbox->setVisible(false); customUI->translationRadiusSpinbox->setVisible(false); customUI->radiusLabel->setVisible(false); customUI->brushCountSpinBox->setVisible(false); customUI->brushesLabel->setVisible(false); + customUI->subbrushLabel->setVisible(false); + customUI->addSubbrushButton->setVisible(false); + customUI->removeSubbrushButton->setVisible(false); // turn on what we need - if (index == int(MIRROR)) { + if (index == MIRROR) { customUI->horizontalCheckbox->setVisible(true); customUI->verticalCheckbox->setVisible(true); } - if (index == int(TRANSLATE)) { - customUI->translationRadiusSpinbox->setVisible(true); - customUI->radiusLabel->setVisible(true); + else if (index == TRANSLATE) { + customUI->translationRadiusSpinbox->setVisible(true); + customUI->radiusLabel->setVisible(true); + customUI->brushCountSpinBox->setVisible(true); + customUI->brushesLabel->setVisible(true); } - if (index == int(SYMMETRY) || index == int(SNOWFLAKE) || index == int(TRANSLATE) ) { + else if (index == SYMMETRY || index == SNOWFLAKE || index == TRANSLATE ) { customUI->brushCountSpinBox->setVisible(true); customUI->brushesLabel->setVisible(true); } + + else if (index == COPYTRANSLATE) { + customUI->subbrushLabel->setVisible(true); + customUI->addSubbrushButton->setVisible(true); + customUI->removeSubbrushButton->setVisible(true); + } } void KisToolMultihand::slotSetAxesVisible(bool vis) { m_showAxes = vis; updateCanvas(); m_configGroup.writeEntry("showAxes", vis); } void KisToolMultihand::slotSetMirrorVertically(bool mirror) { m_mirrorVertically = mirror; m_configGroup.writeEntry("mirrorVertically", mirror); } void KisToolMultihand::slotSetMirrorHorizontally(bool mirror) { m_mirrorHorizontally = mirror; m_configGroup.writeEntry("mirrorHorizontally", mirror); } void KisToolMultihand::slotSetTranslateRadius(int radius) { m_translateRadius = radius; m_configGroup.writeEntry("translateRadius", radius); } +void KisToolMultihand::slotAddSubbrushesMode(bool checked) +{ + m_addSubbrushesMode = checked; + updateCanvas(); +} + +void KisToolMultihand::slotRemoveAllSubbrushes() +{ + m_subbrOriginalLocations.clear(); + updateCanvas(); +} + diff --git a/plugins/tools/basictools/kis_tool_multihand.h b/plugins/tools/basictools/kis_tool_multihand.h index 2f1f640f6e..a9d1cf7ca1 100644 --- a/plugins/tools/basictools/kis_tool_multihand.h +++ b/plugins/tools/basictools/kis_tool_multihand.h @@ -1,120 +1,121 @@ /* * Copyright (c) 2011 Lukáš Tvrdý * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_TOOL_MULTIHAND_H #define __KIS_TOOL_MULTIHAND_H #include "kis_tool_brush.h" #include #include "kis_tool_multihand_config.h" class QPushButton; class QCheckBox; class QComboBox; class QStackedWidget; class KisSliderSpinBox; class KisToolMultihandHelper; class KisToolMultihand : public KisToolBrush { Q_OBJECT public: KisToolMultihand(KoCanvasBase *canvas); ~KisToolMultihand() override; void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; + void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override; + void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override; + void endAlternateAction(KoPointerEvent *event, AlternateAction action) override; + + void mouseMoveEvent(KoPointerEvent* event) override; + protected: void paint(QPainter& gc, const KoViewConverter &converter) override; - + QWidget* createOptionWidget() override; private: void initTransformations(); void finishAxesSetup(); void updateCanvas(); private Q_SLOTS: void activateAxesPointModeSetup(); void resetAxes(); void slotSetHandsCount(int count); void slotSetAxesAngle(qreal angle); void slotSetTransformMode(int qcomboboxIndex); void slotSetAxesVisible(bool vis); void slotSetMirrorVertically(bool mirror); void slotSetMirrorHorizontally(bool mirror); void slotSetTranslateRadius(int radius); + void slotAddSubbrushesMode(bool checked); + void slotRemoveAllSubbrushes(); private: KisToolMultihandHelper *m_helper; - enum enumTransforModes { SYMMETRY, MIRROR, TRANSLATE, SNOWFLAKE }; + enum enumTransforModes:int { SYMMETRY=0, MIRROR, TRANSLATE, SNOWFLAKE, COPYTRANSLATE }; enumTransforModes m_transformMode; QPointF m_axesPoint; qreal m_angle; int m_handsCount; bool m_mirrorVertically; bool m_mirrorHorizontally; bool m_showAxes; int m_translateRadius; bool m_setupAxesFlag; - QComboBox * m_transformModesComboBox; - KisSliderSpinBox *m_handsCountSlider; - KisDoubleSliderSpinBox *m_axesAngleSlider; - QCheckBox *m_axesChCkBox; - QStackedWidget *m_modeCustomOption; - QCheckBox *m_mirrorVerticallyChCkBox; - QCheckBox *m_mirrorHorizontallyChCkBox; - KisSliderSpinBox *m_translateRadiusSlider; - QPushButton *m_axesPointBtn; - + bool m_addSubbrushesMode; + QPointF m_lastToolPos; + QVector m_subbrOriginalLocations; KisToolMultiHandConfigWidget* customUI; }; class KisToolMultiBrushFactory : public KisToolPaintFactoryBase { public: KisToolMultiBrushFactory() : KisToolPaintFactoryBase("KritaShape/KisToolMultiBrush") { setToolTip(i18n("Multibrush Tool")); // Temporarily setSection(TOOL_TYPE_SHAPE); setIconName(koIconNameCStr("krita_tool_multihand")); setShortcut(QKeySequence(Qt::Key_Q)); setPriority(11); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); } ~KisToolMultiBrushFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolMultihand(canvas); } }; #endif /* __KIS_TOOL_MULTIHAND_H */ diff --git a/plugins/tools/basictools/kis_tool_multihand_config.h b/plugins/tools/basictools/kis_tool_multihand_config.h index e67e4cc293..978bc0d657 100644 --- a/plugins/tools/basictools/kis_tool_multihand_config.h +++ b/plugins/tools/basictools/kis_tool_multihand_config.h @@ -1,44 +1,43 @@ /* Copyright (C) 2016 Scott Petrovic This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - #ifndef KISTOOLMULTIHANDCONFIG_H #define KISTOOLMULTIHANDCONFIG_H #include "ui_wdgmultihandtool.h" class KisToolMultiHandConfigWidget : public QWidget, public Ui::WdgMultiHandTool { Q_OBJECT public: KisToolMultiHandConfigWidget(QWidget *parent=0); ~KisToolMultiHandConfigWidget() override; //Q_SIGNALS: //public Q_SLOTS: //void cropTypeSelectableChanged(); //private: //KisToolCrop* m_cropTool; }; #endif // KISTOOLMULTIHANDCONFIG_H diff --git a/plugins/tools/basictools/wdgmultihandtool.ui b/plugins/tools/basictools/wdgmultihandtool.ui index 8e4e5622a4..3e3e11704e 100644 --- a/plugins/tools/basictools/wdgmultihandtool.ui +++ b/plugins/tools/basictools/wdgmultihandtool.ui @@ -1,264 +1,295 @@ WdgMultiHandTool 0 0 200 - 282 + 290 200 0 0 10 QFrame::Plain Qt::Horizontal 4 7 - - + + 0 0 - Type: + Brushes: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - true - - - - 0 - 0 - - - - Move - - - false - - - - - - - true - - - - 0 - 0 - - - - Reset - - - false - - - - + + - - + + + + Subbrushes: + + - - + + 0 0 - Rotation: + Radius: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + 0 0 - - - 0 - 15 - + + Horizontal - - - - + 0 0 Show Origin - - + + 0 0 - Brushes: + Rotation: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + 0 0 + + + 0 + 15 + + + + + + - Horizontal + Vertical - - + + 0 0 - - Qt::LeftToRight - - Origin: + Type: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - Vertical - - + + + + + + true + + + + 0 + 0 + + + + Move + + + false + + + + + + + true + + + + 0 + 0 + + + + Reset + + + false + + + + - - + + + + + 0 0 + + Qt::LeftToRight + - Radius: + Origin: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + + + + + Add + + + true + + + false + + + + + + + Remove All + + + + + + + Qt::Vertical 20 40 KisSliderSpinBox QWidget
kis_slider_spin_box.h
1
- + KisDoubleSliderSpinBox QWidget
kis_slider_spin_box.h
1