diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 93843fa8e6..06e92003a4 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -1,237 +1,238 @@ project (krita-and-all-its-deps) # # Build all dependencies for Krita and finally Krita itself. # Parameters: EXTERNALS_DOWNLOAD_DIR place to download all packages # INSTALL_ROOT place to install everything to # MXE_TOOLCHAIN: the toolchain file to cross-compile using MXE # # Example usage: cmake ..\kritadeposx -DEXTERNALS_DOWNLOAD_DIR=/dev2/d -DINSTALL_ROOT=/dev2/i -DWIN64_BUILD=TRUE -DBOOST_LIBRARYDIR=/dev2/i/lib -G "Visual Studio 11 Win64" cmake_minimum_required(VERSION 2.8.6) if(NOT SUBMAKE_JOBS) set(SUBMAKE_JOBS 1) endif() if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) message(FATAL_ERROR "Compiling in the source directory is not supported. Use for example 'mkdir build; cd build; cmake ..'.") endif (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) # Tools must be obtained to work with: include (ExternalProject) # allow specification of a directory with pre-downloaded # requirements if(NOT IS_DIRECTORY ${EXTERNALS_DOWNLOAD_DIR}) message(FATAL_ERROR "No externals download dir set. Use -DEXTERNALS_DOWNLOAD_DIR") endif() if(NOT IS_DIRECTORY ${INSTALL_ROOT}) message(FATAL_ERROR "No install dir set. Use -DINSTALL_ROOT") endif() set(TOP_INST_DIR ${INSTALL_ROOT}) set(EXTPREFIX "${TOP_INST_DIR}") set(CMAKE_PREFIX_PATH "${EXTPREFIX}") if (${CMAKE_GENERATOR} STREQUAL "Visual Studio 14 2015 Win64") SET(GLOBAL_PROFILE -DCMAKE_MODULE_LINKER_FLAGS=/machine:x64 -DCMAKE_EXE_LINKER_FLAGS=/machine:x64 -DCMAKE_SHARED_LINKER_FLAGS=/machine:x64 -DCMAKE_STATIC_LINKER_FLAGS=/machine:x64 ) endif () message( STATUS "CMAKE_GENERATOR: ${CMAKE_GENERATOR}") message( STATUS "CMAKE_CL_64: ${CMAKE_CL_64}") set(GLOBAL_BUILD_TYPE RelWithDebInfo) set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DBUILD_TESTING=false) if (MINGW) option(QT_ENABLE_DEBUG_INFO "Build Qt with debug info included" OFF) option(QT_ENABLE_DYNAMIC_OPENGL "Build Qt with dynamic ANGLE support '-opengl dynamic -angle' (needs env var 'WindowsSdkDir' set to path of Windows 10 SDK)" ON) if (QT_ENABLE_DYNAMIC_OPENGL) if (DEFINED ENV{WindowsSdkDir}) message(STATUS "WindowsSdkDir is set to '$ENV{WindowsSdkDir}'") else (DEFINED ENV{WindowsSdkDir}) message(FATAL_ERROR "Environment variable 'WindowsSdkDir' not set! Please set it to path of Windows 10 SDK or disable QT_ENABLE_DYNAMIC_OPENGL") endif () endif () endif (MINGW) if (WIN32) option(USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt." ON) endif () set(SECURITY_EXE_LINKER_FLAGS "") set(SECURITY_SHARED_LINKER_FLAGS "") set(SECURITY_MODULE_LINKER_FLAGS "") if (MINGW) option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON) if (USE_MINGW_HARDENING_LINKER) set(SECURITY_EXE_LINKER_FLAGS "-Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(SECURITY_SHARED_LINKER_FLAGS "-Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(SECURITY_MODULE_LINKER_FLAGS "-Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") # Enable high-entropy ASLR for 64-bit # The image base has to be >4GB for HEASLR to be enabled. # The values used here are kind of arbitrary. set(SECURITY_EXE_LINKER_FLAGS "${SECURITY_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000") set(SECURITY_SHARED_LINKER_FLAGS "${SECURITY_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(SECURITY_MODULE_LINKER_FLAGS "${SECURITY_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DCMAKE_EXE_LINKER_FLAGS=${SECURITY_EXE_LINKER_FLAGS} -DCMAKE_SHARED_LINKER_FLAGS=${SECURITY_SHARED_LINKER_FLAGS} -DCMAKE_MODULE_LINKER_FLAGS=${SECURITY_MODULE_LINKER_FLAGS} ) endif () else () message(WARNING "Linker Security Flags not enabled!") endif () endif () if (DEFINED EP_PREFIX) set_directory_properties(PROPERTIES EP_PREFIX ${EP_PREFIX}) endif () if (MSVC) message(FATAL_ERROR "Krita cannot be built with MSVC. See the README.md file!") endif() if (MINGW) set(PATCH_COMMAND myptch) endif() if (MSYS) set(PATCH_COMMAND patch) set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DCMAKE_TOOLCHAIN_FILE=${MXE_TOOLCHAIN} -DCMAKE_FIND_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_SYSTEM_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include -DCMAKE_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include -DCMAKE_LIBRARY_PATH=${CMAKE_PREFIX_PATH}/lib -DZLIB_ROOT=${CMAKE_PREFIX_PATH} ) set(GLOBAL_AUTOMAKE_PROFILE --host=i686-pc-mingw32 ) endif() if (APPLE) set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DCMAKE_PREFIX_PATH:PATH=${CMAKE_PREFIX_PATH} -DCMAKE_INCLUDE_PATH:PATH=${CMAKE_PREFIX_PATH}/include -DCMAKE_LIBRARY_PATH:PATH=${CMAKE_PREFIX_PATH}/lib -DCMAKE_MACOSX_RPATH=ON -DKDE_SKIP_RPATH_SETTINGS=ON -DBUILD_WITH_INSTALL_RPATH=ON -DAPPLE_SUPPRESS_X11_WARNING=ON) set(PATCH_COMMAND patch) endif () if (ANDROID) set (GLOBAL_PROFILE ${GLOBAL_PROFILE} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} -DANDROID_PLATFORM=${ANDROID_PLATFORM} -DANDROID_ABI=${ANDROID_ABI}) endif() if (UNIX AND NOT APPLE) set(LINUX true) set(PATCH_COMMAND patch) endif () function(TestCompileLinkPythonLibs OUTPUT_VARNAME) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES ${PYTHON_INCLUDE_PATH}) set(CMAKE_REQUIRED_LIBRARIES ${PYTHON_LIBRARIES}) if (MINGW) set(CMAKE_REQUIRED_DEFINITIONS -D_hypot=hypot) endif () unset(${OUTPUT_VARNAME} CACHE) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { Py_InitializeEx(0); }" ${OUTPUT_VARNAME}) endfunction() if (MINGW) option(ENABLE_PYTHON_DEPS "Enable Python deps (sip, pyqt)" ON) if (ENABLE_PYTHON_DEPS) if (ENABLE_PYTHON_2) message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.") else(ENABLE_PYTHON_2) find_package(PythonInterp 3.8 EXACT) find_package(PythonLibs 3.8 EXACT) endif() if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) message(STATUS "Python requirements met.") TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS) if (NOT CAN_USE_PYTHON_LIBS) message(FATAL_ERROR "Compiling with Python library failed, please check whether the architecture is correct!") endif () else (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) message(FATAL_ERROR "Python requirements not met. To disable Python deps, set ENABLE_PYTHON_DEPS to OFF.") endif () endif () endif () # this list must be dependency-ordered if (ENABLE_PYTHON_DEPS OR NOT MINGW) add_subdirectory( ext_python ) endif () if (MINGW) add_subdirectory( ext_patch ) add_subdirectory( ext_png2ico ) endif () add_subdirectory( ext_lzma ) add_subdirectory( ext_iconv ) add_subdirectory( ext_gettext ) add_subdirectory( ext_zlib ) add_subdirectory( ext_boost ) add_subdirectory( ext_jpeg ) add_subdirectory( ext_tiff ) add_subdirectory( ext_png ) add_subdirectory( ext_eigen3 ) add_subdirectory( ext_expat ) # for exiv2 add_subdirectory( ext_exiv2 ) add_subdirectory( ext_ilmbase ) add_subdirectory( ext_lcms2 ) add_subdirectory( ext_openexr ) add_subdirectory( ext_openssl ) add_subdirectory( ext_vc ) add_subdirectory( ext_gsl ) add_subdirectory( ext_fftw3 ) add_subdirectory( ext_ocio ) add_subdirectory( ext_openjpeg ) if (MSVC) add_subdirectory( ext_pthreads ) endif (MSVC) add_subdirectory( ext_fontconfig) add_subdirectory( ext_freetype) add_subdirectory( ext_qt ) add_subdirectory( ext_poppler ) add_subdirectory( ext_libraw ) add_subdirectory( ext_frameworks ) if (ENABLE_PYTHON_DEPS OR NOT MINGW) add_subdirectory( ext_sip ) add_subdirectory( ext_pyqt ) endif () if (MINGW) add_subdirectory( ext_drmingw ) # add_subdirectory( ext_ffmpeg ) endif () if (NOT APPLE) add_subdirectory( ext_gmic ) endif () if(UNIX) add_subdirectory( ext_pkgconfig ) endif() add_subdirectory( ext_heif ) add_subdirectory(ext_giflib) add_subdirectory(ext_quazip) +add_subdirectory(ext_seexpr) diff --git a/3rdparty/ext_gsl/CMakeLists.txt b/3rdparty/ext_gsl/CMakeLists.txt index 3bd8182f47..57eb443d34 100644 --- a/3rdparty/ext_gsl/CMakeLists.txt +++ b/3rdparty/ext_gsl/CMakeLists.txt @@ -1,30 +1,30 @@ # # From https://github.com/ampl/gsl. which adds CMake support to gsl # -if (ANDROID) +if (UNIX) SET(EXTPREFIX_gsl "${EXTPREFIX}" ) ExternalProject_Add( ext_gsl DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/gsl-2.3.0.tar.gz URL_MD5 7e0478f7c5e62696fef480b9a46f708c PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/gsl-android.patch INSTALL_DIR ${EXTPREFIX_gsl} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_gsl} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF UPDATE_COMMAND "" ) else() SET(EXTPREFIX_gsl "${EXTPREFIX}" ) ExternalProject_Add( ext_gsl DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/gsl-2.3.0.tar.gz URL_MD5 7e0478f7c5e62696fef480b9a46f708c INSTALL_DIR ${EXTPREFIX_gsl} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_gsl} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DBUILD_TESTING=OFF UPDATE_COMMAND "" ) endif() diff --git a/3rdparty/ext_heif/CMakeLists.txt b/3rdparty/ext_heif/CMakeLists.txt index dbdfdafff8..55877cf1b0 100644 --- a/3rdparty/ext_heif/CMakeLists.txt +++ b/3rdparty/ext_heif/CMakeLists.txt @@ -1,148 +1,148 @@ SET(EXTPREFIX_heif "${EXTPREFIX}" ) if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") ExternalProject_Add( ext_libde265 DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://github.com/strukturag/libde265/releases/download/v1.0.5/libde265-1.0.5.tar.gz URL_MD5 385c88166cb255a068a8c936d0ab23ef PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/01-fix-sse41.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_heif} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} UPDATE_COMMAND "" ) else ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") ExternalProject_Add( ext_libde265 DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://github.com/strukturag/libde265/releases/download/v1.0.5/libde265-1.0.5.tar.gz URL_MD5 385c88166cb255a068a8c936d0ab23ef PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/01-fix-sse41.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_heif} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DDISABLE_SSE=TRUE UPDATE_COMMAND "" ) endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") if (WIN32) if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") ExternalProject_Add( ext_nasm DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://www.nasm.us/pub/nasm/releasebuilds/2.14.03rc2/win64/nasm-2.14.03rc2-win64.zip URL_MD5 f12d14ca46cf397a1d27ac0edde548fb BUILD_COMMAND ${CMAKE_COMMAND} -E echo Deploying nasm 64-bit dependencies CONFIGURE_COMMAND "" INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /rdoff ${EXTPREFIX_heif}/bin/rdoff COMMAND ${CMAKE_COMMAND} -E copy /nasm.exe ${EXTPREFIX_heif}/bin/nasm.exe COMMAND ${CMAKE_COMMAND} -E copy /ndisasm.exe ${EXTPREFIX_heif}/bin/ndisasm.exe UPDATE_COMMAND "" ) else("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") ExternalProject_Add( ext_nasm DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://www.nasm.us/pub/nasm/releasebuilds/2.14.03rc2/win32/nasm-2.14.03rc2-win32.zip URL_MD5 9ec22ac06a6b9d993ef503ea2fe074ab BUILD_COMMAND ${CMAKE_COMMAND} -E echo Deploying nasm 64-bit dependencies CONFIGURE_COMMAND "" INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /rdoff ${EXTPREFIX_heif}/bin/rdoff COMMAND ${CMAKE_COMMAND} -E copy /nasm.exe ${EXTPREFIX_heif}/bin/nasm.exe COMMAND ${CMAKE_COMMAND} -E copy /ndisasm.exe ${EXTPREFIX_heif}/bin/ndisasm.exe UPDATE_COMMAND "" ) endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") else (WIN32) ExternalProject_Add( ext_nasm DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://www.nasm.us/pub/nasm/releasebuilds/2.14.03rc2/nasm-2.14.03rc2.tar.gz URL_MD5 4cd1fe6788cd15d08c1a8f18b6d2428e CONFIGURE_COMMAND /configure --prefix=${EXTPREFIX_heif} ${GLOBAL_AUTOMAKE_PROFILE} BUILD_COMMAND make INSTALL_COMMAND make install UPDATE_COMMAND "" ) endif(WIN32) IF ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") ExternalProject_Add(ext_libx265_10bit DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://ftp.videolan.org/pub/videolan/x265/x265_3.2.1.tar.gz URL_MD5 94808045a34d88a857e5eaf3f68f4bca SOURCE_SUBDIR source/ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_heif} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DHIGH_BIT_DEPTH=TRUE -DEXPORT_C_API=FALSE -DENABLE_CLI=FALSE -DENABLE_SHARED=FALSE INSTALL_COMMAND ${CMAKE_COMMAND} -E copy libx265.a ${EXTPREFIX_heif}/lib/libx265_main10.a ) ExternalProject_Add(ext_libx265_12bit DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://ftp.videolan.org/pub/videolan/x265/x265_3.2.1.tar.gz URL_MD5 94808045a34d88a857e5eaf3f68f4bca SOURCE_SUBDIR source/ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_heif} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DHIGH_BIT_DEPTH=TRUE -DMAIN12=TRUE -DEXPORT_C_API=FALSE -DENABLE_CLI=FALSE -DENABLE_SHARED=FALSE INSTALL_COMMAND ${CMAKE_COMMAND} -E copy libx265.a ${EXTPREFIX_heif}/lib/libx265_main12.a ) ExternalProject_Add( ext_libx265 DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://ftp.videolan.org/pub/videolan/x265/x265_3.2.1.tar.gz URL_MD5 94808045a34d88a857e5eaf3f68f4bca SOURCE_SUBDIR source/ PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/02-skip-pdb-in-mingw.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_heif} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DENABLE_SHARED=TRUE -DENABLE_HDR10_PLUS=TRUE -DEXTRA_LIB:String=x265_main10.a$x265_main12.a -DEXTRA_LINK_FLAGS:String=-L${EXTPREFIX_heif}/lib -DLINKED_10BIT=TRUE -DLINKED_12BIT=TRUE UPDATE_COMMAND "" DEPENDS ext_nasm ext_libx265_10bit ext_libx265_12bit ) else ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") # x265 in 32-bit platforms cannot support 10-bit and 12-bit images # because they either crash on malloc or require SSE4.1+ extensions ExternalProject_Add( ext_libx265 DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://ftp.videolan.org/pub/videolan/x265/x265_3.2.1.tar.gz URL_MD5 94808045a34d88a857e5eaf3f68f4bca SOURCE_SUBDIR source/ PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/02-skip-pdb-in-mingw.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_heif} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DENABLE_SHARED=TRUE -DENABLE_CLI=FALSE UPDATE_COMMAND "" DEPENDS ext_nasm ) endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") ExternalProject_Add( ext_libheif DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://github.com/strukturag/libheif/releases/download/v1.6.2/libheif-1.6.2.tar.gz URL_HASH SHA256=bb229e855621deb374f61bee95c4642f60c2a2496bded35df3d3c42cc6d8eefc PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/03-fix-build-macos-windows.diff - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_heif} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} -DBUILD_EXAMPLES=ON ${GLOBAL_PROFILE} + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_heif} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} -DBUILD_EXAMPLES=OFF ${GLOBAL_PROFILE} UPDATE_COMMAND "" DEPENDS ext_libde265 ext_libx265 ext_jpeg ext_png ) diff --git a/3rdparty/ext_seexpr/CMakeLists.txt b/3rdparty/ext_seexpr/CMakeLists.txt new file mode 100644 index 0000000000..6557127d16 --- /dev/null +++ b/3rdparty/ext_seexpr/CMakeLists.txt @@ -0,0 +1,12 @@ +SET(PREFIX_ext_seexpr "${EXTPREFIX}" ) +ExternalProject_Add( + ext_seexpr + GIT_REPOSITORY https://invent.kde.org/lsegovia/SeExpr.git + GIT_TAG d9a7c387d1caeef675e1ff78cd8011003e703239 + + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_seexpr} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} -DFLAVOR=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DBOOST_INCLUDE_DIR=${PREFIX_ext_seexpr}/include -DBOOST_ROOT=${PREFIX_ext_seexpr} -DBOOST_LIB_DIR=${PREFIX_ext_seexpr}/lib -DENABLE_LLVM_BACKEND=OFF -DENABLE_SSE4=OFF -DBUILD_DOC=OFF -DBUILD_TESTS=OFF -DBUILD_UTILS=OFF -DUSE_PYTHON=OFF + + UPDATE_COMMAND "" + # TODO when moving this library to a packaged release, please add ext_extra_cmake_modules so that translations can be built in + DEPENDS ext_qt +) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e9bea3780..ae2d6e3b0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,923 +1,935 @@ project(krita) message(STATUS "Using CMake version: ${CMAKE_VERSION}") cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) set(MIN_QT_VERSION 5.9.0) set(MIN_FRAMEWORKS_VERSION 5.44.0) set( CMAKE_CXX_STANDARD 11 ) set( CMAKE_CXX_STANDARD_REQUIRED ON ) if (POLICY CMP0002) cmake_policy(SET CMP0002 OLD) endif() if (POLICY CMP0017) cmake_policy(SET CMP0017 NEW) endif () if (POLICY CMP0022) cmake_policy(SET CMP0022 OLD) endif () if (POLICY CMP0026) cmake_policy(SET CMP0026 OLD) endif() if (POLICY CMP0042) cmake_policy(SET CMP0042 NEW) endif() if (POLICY CMP0046) cmake_policy(SET CMP0046 OLD) endif () if (POLICY CMP0059) cmake_policy(SET CMP0059 OLD) endif() if (POLICY CMP0063) cmake_policy(SET CMP0063 OLD) endif() if (POLICY CMP0054) cmake_policy(SET CMP0054 OLD) endif() if (POLICY CMP0064) cmake_policy(SET CMP0064 OLD) endif() if (POLICY CMP0071) cmake_policy(SET CMP0071 OLD) endif() if (APPLE) set(APPLE_SUPPRESS_X11_WARNING TRUE) set(KDE_SKIP_RPATH_SETTINGS TRUE) set(CMAKE_MACOSX_RPATH 1) set(BUILD_WITH_INSTALL_RPATH 1) add_definitions(-mmacosx-version-min=10.12 -Wno-macro-redefined -Wno-deprecated-register) endif() if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 AND NOT WIN32) add_compile_options($<$:-Wno-suggest-override> -Wextra -Wno-class-memaccess) endif() ###################### ####################### ## Constants defines ## ####################### ###################### # define common versions of Krita applications, used to generate kritaversion.h # update these version for every release: set(KRITA_VERSION_STRING "4.3.1-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 3) # Bugfix release version, or 0 for before the first stable release set(KRITA_VERSION_RELEASE 1) # the 4th digit, really only used for the Windows installer: # - [Pre-]Alpha: Starts from 0, increment 1 per release # - Beta: Starts from 50, increment 1 per release # - Stable: Set to 100, bump to 101 if emergency update is needed set(KRITA_VERSION_REVISION 0) set(KRITA_ALPHA 1) # uncomment only for Alpha #set(KRITA_BETA 1) # uncomment only for Beta #set(KRITA_RC 1) # uncomment only for RC if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC) set(KRITA_STABLE 1) # do not edit endif() message(STATUS "Krita version: ${KRITA_VERSION_STRING}") # Define the generic version of the Krita libraries here # This makes it easy to advance it when the next Krita release comes. # 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series # (2.x) so we're starting with 15 in 3.x series, 16 in 4.x series if(KRITA_STABLE_VERSION_MAJOR EQUAL 4) math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 16") else() # let's make sure we won't forget to update the "16" message(FATAL_ERROR "Reminder: please update offset == 16 used to compute GENERIC_KRITA_LIB_VERSION_MAJOR to something bigger") endif() set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0") set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro") # fetch git revision for the current build set(KRITA_GIT_SHA1_STRING "") set(KRITA_GIT_BRANCH_STRING "") include(GetGitRevisionDescription) get_git_head_hash(GIT_SHA1) get_git_branch(GIT_BRANCH) if(GIT_SHA1) string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1) set(KRITA_GIT_SHA1_STRING ${GIT_SHA1}) if(GIT_BRANCH) set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH}) else() set(KRITA_GIT_BRANCH_STRING "(detached HEAD)") endif() endif() # create test make targets enable_testing() # collect list of broken tests, empty here to start fresh with each cmake run set(KRITA_BROKEN_TESTS "" CACHE INTERNAL "KRITA_BROKEN_TESTS") ############ ############# ## Options ## ############# ############ include(FeatureSummary) if (WIN32) option(USE_DRMINGW "Support the Dr. Mingw crash handler (only on windows)" ON) add_feature_info("Dr. Mingw" USE_DRMINGW "Enable the Dr. Mingw crash handler") if (MINGW) option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON) add_feature_info("Linker Security Flags" USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags") if (USE_MINGW_HARDENING_LINKER) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") # Enable high-entropy ASLR for 64-bit # The image base has to be >4GB for HEASLR to be enabled. # The values used here are kind of arbitrary. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") else (USE_MINGW_HARDENING_LINKER) message(WARNING "Linker Security Flags not enabled!") endif (USE_MINGW_HARDENING_LINKER) endif (MINGW) endif () option(HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal." ON) configure_file(config-hide-safe-asserts.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hide-safe-asserts.h) add_feature_info("Hide Safe Asserts" HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal.") option(USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking." ON) configure_file(config-hash-table-implementaion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hash-table-implementaion.h) add_feature_info("Lock free hash table" USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking.") option(FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true." OFF) add_feature_info("Foundation Build" FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true.") option(KRITA_ENABLE_BROKEN_TESTS "Enable tests that are marked as broken" OFF) add_feature_info("Enable Broken Tests" KRITA_ENABLE_BROKEN_TESTS "Runs broken test when \"make test\" is invoked (use -DKRITA_ENABLE_BROKEN_TESTS=ON to enable).") option(LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode" ON) configure_file(config-limit-long-tests.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-limit-long-tests.h) add_feature_info("Limit long tests" LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode") option(ENABLE_PYTHON_2 "Enables the compiler to look for Python 2.7 instead of Python 3. Some packaged scripts are not compatible with Python 2 and this should only be used if you really have to use 2.7." OFF) option(BUILD_KRITA_QT_DESIGNER_PLUGINS "Build Qt Designer plugins for Krita widgets" OFF) add_feature_info("Build Qt Designer plugins" BUILD_KRITA_QT_DESIGNER_PLUGINS "Builds Qt Designer plugins for Krita widgets (use -DBUILD_KRITA_QT_DESIGNER_PLUGINS=ON to enable).") include(MacroJPEG) ######################################################### ## Look for Python3 It is also searched by KF5, ## ## so we should request the correct version in advance ## ######################################################### function(TestCompileLinkPythonLibs OUTPUT_VARNAME) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES ${PYTHON_INCLUDE_PATH}) set(CMAKE_REQUIRED_LIBRARIES ${PYTHON_LIBRARIES}) if (MINGW) set(CMAKE_REQUIRED_DEFINITIONS -D_hypot=hypot) endif (MINGW) unset(${OUTPUT_VARNAME} CACHE) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { Py_InitializeEx(0); }" ${OUTPUT_VARNAME}) endfunction() if(MINGW) if(ENABLE_PYTHON_2) message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.") else(ENABLE_PYTHON_2) find_package(PythonInterp 3.8 EXACT) find_package(PythonLibs 3.8 EXACT) endif(ENABLE_PYTHON_2) if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) if(ENABLE_PYTHON_2) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonLibrary 3.8) endif(ENABLE_PYTHON_2) TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS) if (NOT CAN_USE_PYTHON_LIBS) message(FATAL_ERROR "Compiling with Python library failed, please check whether the architecture is correct. Python will be disabled.") endif (NOT CAN_USE_PYTHON_LIBS) endif (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND) else(MINGW) if(ENABLE_PYTHON_2) find_package(PythonInterp 2.7) find_package(PythonLibrary 2.7) else(ENABLE_PYTHON_2) find_package(PythonInterp 3.0) find_package(PythonLibrary 3.0) endif(ENABLE_PYTHON_2) endif(MINGW) ######################## ######################### ## Look for KDE and Qt ## ######################### ######################## find_package(ECM 5.22 REQUIRED NOMODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(ECMOptionalAddSubdirectory) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(GenerateExportHeader) include(ECMMarkAsTest) include(ECMInstallIcons) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) # do not reorder to be alphabetical: this is the order in which the frameworks # depend on each other. find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS Config WidgetsAddons Completion CoreAddons GuiAddons I18n ItemModels ItemViews WindowSystem ) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS Core Gui Widgets Xml Network PrintSupport Svg Test Concurrent ) if (ANDROID) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS AndroidExtras ) endif() if (WIN32) set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES}) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_MSWindowsUseWinTabAPI); } " QT_HAS_WINTAB_SWITCH ) unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_LIBRARIES) option(USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt." ON) add_feature_info("Use Qt's Windows Tablet Support" USE_QT_TABLET_WINDOWS "Do not use Krita's forked Wintab and Windows Ink support on Windows, but leave everything to Qt.") configure_file(config_use_qt_tablet_windows.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_use_qt_tablet_windows.h) endif () set(CMAKE_REQUIRED_INCLUDES ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${Qt5Core_LIBRARIES} ${Qt5Gui_LIBRARIES}) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QSurfaceFormat fmt; fmt.setColorSpace(QSurfaceFormat::scRGBColorSpace); fmt.setColorSpace(QSurfaceFormat::bt2020PQColorSpace); } " HAVE_HDR ) configure_file(config-hdr.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hdr.h) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round); QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); } " HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY ) configure_file(config-high-dpi-scale-factor-rounding-policy.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-high-dpi-scale-factor-rounding-policy.h) if (WIN32) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { QWindowsWindowFunctions::setHasBorderInFullScreenDefault(true); } " HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT ) configure_file(config-set-has-border-in-full-screen-default.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-set-has-border-in-full-screen-default.h) endif (WIN32) unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_LIBRARIES) include (MacroAddFileDependencies) include (MacroBoolTo01) include (MacroEnsureOutOfSourceBuild) macro_ensure_out_of_source_build("Compiling Krita inside the source directory is not possible. Please refer to the build instruction https://community.kde.org/Krita#Build_Instructions") # Note: OPTIONAL_COMPONENTS does not seem to be reliable # (as of ECM 5.15.0, CMake 3.2) find_package(Qt5Multimedia ${MIN_QT_VERSION}) set_package_properties(Qt5Multimedia PROPERTIES DESCRIPTION "Qt multimedia integration" URL "https://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide sound support for animations") macro_bool_to_01(Qt5Multimedia_FOUND HAVE_QT_MULTIMEDIA) configure_file(config-qtmultimedia.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-qtmultimedia.h ) if (NOT APPLE) find_package(Qt5Quick ${MIN_QT_VERSION}) set_package_properties(Qt5Quick PROPERTIES DESCRIPTION "QtQuick" URL "https://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") macro_bool_to_01(Qt5Quick_FOUND HAVE_QT_QUICK) find_package(Qt5QuickWidgets ${MIN_QT_VERSION}) set_package_properties(Qt5QuickWidgets PROPERTIES DESCRIPTION "QtQuickWidgets" URL "https://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") endif() if (NOT WIN32 AND NOT APPLE AND NOT ANDROID) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras) find_package(Qt5DBus ${MIN_QT_VERSION}) set(HAVE_DBUS ${Qt5DBus_FOUND}) set_package_properties(Qt5DBus PROPERTIES DESCRIPTION "Qt DBUS integration" URL "https://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide a dbus api on Linux") find_package(KF5Crash ${MIN_FRAMEWORKS_VERSION}) macro_bool_to_01(KF5Crash_FOUND HAVE_KCRASH) set_package_properties(KF5Crash PROPERTIES DESCRIPTION "KDE's Crash Handler" URL "https://api.kde.org/frameworks-api/frameworks5-apidocs/kcrash/html/index.html" TYPE OPTIONAL PURPOSE "Optionally used to provide crash reporting on Linux") find_package(X11 REQUIRED COMPONENTS Xinput) set(HAVE_X11 TRUE) add_definitions(-DHAVE_X11) else() set(HAVE_DBUS FALSE) set(HAVE_X11 FALSE) endif() add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_STRICT_ITERATORS -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_NO_URL_CAST_FROM_STRING -DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS ) #if (${Qt5_VERSION} VERSION_GREATER "5.14.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50F00) #elseif (${Qt5_VERSION} VERSION_GREATER "5.13.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50E00) #elseif (${Qt5_VERSION} VERSION_GREATER "5.12.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50D00) #elseif (${Qt5_VERSION} VERSION_GREATER "5.11.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50C00) #if(${Qt5_VERSION} VERSION_GREATER "5.10.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50B00) #if(${Qt5_VERSION} VERSION_GREATER "5.9.0" ) # add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50A00) #else() add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50900) #endif() add_definitions(-DQT_DEPRECATED_WARNINGS) add_definitions(-DTRANSLATION_DOMAIN=\"krita\") # # The reason for this mode is that the Debug mode disable inlining # if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals") endif() option(KRITA_DEVS "For Krita developers. This modifies the DEBUG build type to use -O3 -g, while still enabling Q_ASSERT. This is necessary because the Qt5 cmake modules normally append QT_NO_DEBUG to any build type that is not labeled Debug") if (KRITA_DEVS) set(CMAKE_CXX_FLAGS_DEBUG "-O3 -g" CACHE STRING "" FORCE) endif() if(UNIX) set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m") endif() if(WIN32) if(MSVC) # C4522: 'class' : multiple assignment operators specified set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4522") endif() endif() # KDECompilerSettings adds the `--export-all-symbols` linker flag. # We don't really need it. if(MINGW) string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") endif(MINGW) if(MINGW) # Hack CMake's variables to tell AR to create thin archives to reduce unnecessary writes. # Source of definition: https://github.com/Kitware/CMake/blob/v3.14.1/Modules/Platform/Windows-GNU.cmake#L128 # Thin archives: https://sourceware.org/binutils/docs/binutils/ar.html#index-thin-archives macro(mingw_use_thin_archive lang) foreach(rule CREATE_SHARED_MODULE CREATE_SHARED_LIBRARY LINK_EXECUTABLE) string(REGEX REPLACE "( [^ T]+) " "\\1T " CMAKE_${lang}_${rule} "${CMAKE_${lang}_${rule}}") endforeach() endmacro() mingw_use_thin_archive(CXX) endif(MINGW) # enable exceptions globally kde_enable_exceptions() set(KRITA_DEFAULT_TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/sdk/tests/data/) macro(macro_add_unittest_definitions) add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/") add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}") add_definitions(-DFILES_DEFAULT_DATA_DIR="${KRITA_DEFAULT_TEST_DATA_DIR}") add_definitions(-DSYSTEM_RESOURCES_DATA_DIR="${CMAKE_SOURCE_DIR}/krita/data/") endmacro() # overcome some platform incompatibilities if(WIN32) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif() # set custom krita plugin installdir if (ANDROID) # use default ABI if (NOT ANDROID_ABI) set (ANDROID_ABI armeabi-v7a) endif() set (ANDROID_SDK_ROOT $ENV{ANDROID_SDK_ROOT}) set (KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}) # set (DATA_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/assets) else() set (KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins) endif() ########################### ############################ ## Required dependencies ## ############################ ########################### # FIXME: Still hardcoded if (ANDROID) set (Boost_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/i/${ANDROID_ABI}/include/boost-1_69) set (Boost_LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/i/${ANDROID_ABI}/lib) set (KF5_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/kf5/kde/install/lib) endif() find_package(PNG REQUIRED) list (APPEND ANDROID_EXTRA_LIBS ${PNG_LIBRARY}) if (APPLE) # this is not added correctly on OSX -- see https://forum.kde.org/viewtopic.php?f=139&t=101867&p=221242#p221242 include_directories(SYSTEM ${PNG_INCLUDE_DIR}) endif() add_definitions(-DBOOST_ALL_NO_LIB) find_package(Boost 1.55 REQUIRED COMPONENTS system) include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) ## ## Test for GNU Scientific Library ## find_package(GSL) set_package_properties(GSL PROPERTIES URL "https://www.gnu.org/software/gsl" TYPE RECOMMENDED PURPOSE "Required by Krita's Transform tool.") macro_bool_to_01(GSL_FOUND HAVE_GSL) configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h ) if (GSL_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES}) endif() ########################### ############################ ## Optional dependencies ## ############################ ########################### +## +## Test for SeExpr +## +find_package(SeExpr2) +set_package_properties(SeExpr2 PROPERTIES + URL "https://wdas.github.io/SeExpr/" + TYPE OPTIONAL + PURPOSE "Required by the SeExpr generator layer" +) +macro_bool_to_01(SeExpr2_FOUND HAVE_SEEXPR) +configure_file(config-seexpr.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-seexpr.h ) + find_package(ZLIB) set_package_properties(ZLIB PROPERTIES DESCRIPTION "Compression library" URL "https://www.zlib.net/" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic and the PSD plugins") macro_bool_to_01(ZLIB_FOUND HAVE_ZLIB) find_package(OpenEXR) set_package_properties(OpenEXR PROPERTIES DESCRIPTION "High dynamic-range (HDR) image file format" URL "https://www.openexr.com" TYPE OPTIONAL PURPOSE "Required by the Krita OpenEXR filter") macro_bool_to_01(OPENEXR_FOUND HAVE_OPENEXR) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(SYSTEM ${OPENEXR_INCLUDE_DIRS}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() find_package(TIFF) set_package_properties(TIFF PROPERTIES DESCRIPTION "TIFF Library and Utilities" URL "http://www.libtiff.org" TYPE OPTIONAL PURPOSE "Required by the Krita TIFF filter") if (TIFF_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${TIFF_LIBRARY}) endif() find_package(JPEG) set_package_properties(JPEG PROPERTIES DESCRIPTION "Free library for JPEG image compression. Note: libjpeg8 is NOT supported." URL "https://www.libjpeg-turbo.org" TYPE OPTIONAL PURPOSE "Required by the Krita JPEG filter") if (JPEG_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${JPEG_LIBRARY}) endif() find_package(GIF) set_package_properties(GIF PROPERTIES DESCRIPTION "Library for loading and saving gif files." URL "http://giflib.sourceforge.net/" TYPE OPTIONAL PURPOSE "Required by the Krita GIF filter") if (GIF_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${GIF_LIBRARY}) endif() find_package(HEIF "1.3.0") set_package_properties(HEIF PROPERTIES DESCRIPTION "Library for loading and saving heif files." URL "https://github.com/strukturag/libheif" TYPE OPTIONAL PURPOSE "Required by the Krita HEIF filter") find_package(OpenJPEG "2.3.0") set_package_properties(OpenJPEG PROPERTIES DESCRIPTION "Library for loading and saving jp2000 files." URL "https://www.openjpeg.org/" TYPE OPTIONAL PURPOSE "Required by the Krita JP2000 filter") set(LIBRAW_MIN_VERSION "0.16") find_package(LibRaw ${LIBRAW_MIN_VERSION}) set_package_properties(LibRaw PROPERTIES DESCRIPTION "Library to decode RAW images" URL "https://www.libraw.org/" TYPE OPTIONAL PURPOSE "Required to build the raw import plugin") find_package(FFTW3) set_package_properties(FFTW3 PROPERTIES DESCRIPTION "A fast, free C FFT library" URL "http://www.fftw.org/" TYPE OPTIONAL PURPOSE "Required by the Krita for fast convolution operators and some G'Mic features") macro_bool_to_01(FFTW3_FOUND HAVE_FFTW3) if (FFTW3_FOUND) list (APPEND ANDROID_EXTRA_LIBS ${FFTW3_LIBRARY}) endif() find_package(OCIO) set_package_properties(OCIO PROPERTIES DESCRIPTION "The OpenColorIO Library" URL "https://www.opencolorio.org" TYPE OPTIONAL PURPOSE "Required by the Krita LUT docker") macro_bool_to_01(OCIO_FOUND HAVE_OCIO) set_package_properties(PythonLibrary PROPERTIES DESCRIPTION "Python Library" URL "https://www.python.org" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYTHONLIBS_FOUND HAVE_PYTHONLIBS) find_package(SIP "4.19.13") set_package_properties(SIP PROPERTIES DESCRIPTION "Support for generating SIP Python bindings" URL "https://www.riverbankcomputing.com/software/sip/download" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(SIP_FOUND HAVE_SIP) find_package(PyQt5 "5.6.0") set_package_properties(PyQt5 PROPERTIES DESCRIPTION "Python bindings for Qt5." URL "https://www.riverbankcomputing.com/software/pyqt/download5" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYQT5_FOUND HAVE_PYQT5) ## ## Look for OpenGL ## # TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes) if(Qt5Gui_OPENGL_IMPLEMENTATION) message(STATUS "Found QtGui OpenGL support") else() message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.") endif() ## ## Test for eigen3 ## find_package(Eigen3 3.0 REQUIRED) set_package_properties(Eigen3 PROPERTIES DESCRIPTION "C++ template library for linear algebra" URL "http://eigen.tuxfamily.org" TYPE REQUIRED) ## ## Test for exiv2 ## find_package(LibExiv2 0.16 REQUIRED) if (ANDROID) list (APPEND ANDROID_EXTRA_LIBS ${LibExiv2_LIBRARIES}) # because libexiv2 depends on libexpat and it is installed in the same folder get_filename_component (_base_dir ${LibExiv2_LIBRARIES} DIRECTORY) list (APPEND ANDROID_EXTRA_LIBS ${_base_dir}/libexpat.so) endif() ## ## Test for lcms ## find_package(LCMS2 2.4 REQUIRED) set_package_properties(LCMS2 PROPERTIES DESCRIPTION "LittleCMS Color management engine" URL "http://www.littlecms.com" TYPE REQUIRED PURPOSE "Will be used for color management and is necessary for Krita") if(LCMS2_FOUND) if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 ) set(HAVE_LCMS24 TRUE) endif() set(HAVE_REQUIRED_LCMS_VERSION TRUE) set(HAVE_LCMS2 TRUE) endif() list (APPEND ANDROID_EXTRA_LIBS ${LCMS2_LIBRARIES}) ## ## Test for Vc ## set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ) set(HAVE_VC FALSE) if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") if(NOT MSVC) find_package(Vc 1.1.0) set_package_properties(Vc PROPERTIES DESCRIPTION "Portable, zero-overhead SIMD library for C++" URL "https://github.com/VcDevel/Vc" TYPE OPTIONAL PURPOSE "Required by the Krita for vectorization") macro_bool_to_01(Vc_FOUND HAVE_VC) endif() endif() configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h ) if(HAVE_VC) message(STATUS "Vc found!") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/vc") include (VcMacros) if(Vc_COMPILER_IS_CLANG) set(ADDITIONAL_VC_FLAGS "-ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() elseif (NOT MSVC) set(ADDITIONAL_VC_FLAGS "-fabi-version=0 -ffp-contract=fast") if(NOT WIN32) set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC") endif() endif() macro(ko_compile_for_all_implementations_no_scalar _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() macro(ko_compile_for_all_implementations _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() endif() set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} ) add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) ## ## Test endianness ## include (TestBigEndian) test_big_endian(CMAKE_WORDS_BIGENDIAN) ## ## Test for qt-poppler ## find_package(Poppler COMPONENTS Qt5) set_package_properties(Poppler PROPERTIES DESCRIPTION "A PDF rendering library" URL "https://poppler.freedesktop.org/" TYPE OPTIONAL PURPOSE "Required by the Krita PDF filter.") ## ## Test for quazip ## find_package(QuaZip 0.6) set_package_properties(QuaZip PROPERTIES DESCRIPTION "A library for reading and writing zip files" URL "https://stachenov.github.io/quazip/" TYPE REQUIRED PURPOSE "Needed for reading and writing KRA and ORA files" ) # FIXME: better way to do this? list (APPEND ANDROID_EXTRA_LIBS ${QUAZIP_LIBRARIES} ${EXPAT_LIBRARY} ${KF5_LIBRARIES}/libKF5Completion.so ${KF5_LIBRARIES}/libKF5WindowSystem.so ${KF5_LIBRARIES}/libKF5WidgetsAddons.so ${KF5_LIBRARIES}/libKF5ItemViews.so ${KF5_LIBRARIES}/libKF5ItemModels.so ${KF5_LIBRARIES}/libKF5GuiAddons.so ${KF5_LIBRARIES}/libKF5I18n.so ${KF5_LIBRARIES}/libKF5CoreAddons.so ${KF5_LIBRARIES}/libKF5ConfigGui.so ${KF5_LIBRARIES}/libKF5ConfigCore.so) ## ## 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) if (BUILD_TESTING) add_subdirectory(benchmarks) endif() add_subdirectory(krita) configure_file(KoConfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h ) configure_file(config_convolution.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_convolution.h) configure_file(config-ocio.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ocio.h ) check_function_exists(powf HAVE_POWF) configure_file(config-powf.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-powf.h) if(WIN32) include(${CMAKE_CURRENT_LIST_DIR}/packaging/windows/installer/ConfigureInstallerNsis.cmake) endif() message("\nBroken tests:") foreach(tst ${KRITA_BROKEN_TESTS}) message(" * ${tst}") endforeach() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/po OR EXISTS ${CMAKE_CURRENT_BINARY_DIR}/po ) find_package(KF5I18n CONFIG REQUIRED) ki18n_install(po) endif() if(DEFINED QTANDROID_EXPORTED_TARGET AND NOT TARGET "create-apk") set (_CMAKE_ANDROID_DIR "${ECM_DIR}/../toolchain") list(LENGTH QTANDROID_EXPORTED_TARGET targetsCount) include(${_CMAKE_ANDROID_DIR}/ECMAndroidDeployQt.cmake) math(EXPR last "${targetsCount}-1") foreach(idx RANGE 0 ${last}) list(GET QTANDROID_EXPORTED_TARGET ${idx} exportedTarget) list(GET ANDROID_APK_DIR ${idx} APK_DIR) if(APK_DIR AND NOT EXISTS "${ANDROID_APK_DIR}/AndroidManifest.xml" AND IS_ABSOLUTE ANDROID_APK_DIR) message(FATAL_ERROR "Cannot find ${APK_DIR}/AndroidManifest.xml according to ANDROID_APK_DIR. ${ANDROID_APK_DIR} ${exportedTarget}") elseif(NOT APK_DIR) get_filename_component(_qt5Core_install_prefix "${Qt5Core_DIR}/../../../" ABSOLUTE) set(APK_DIR "${_qt5Core_install_prefix}/src/android/templates/") endif() ecm_androiddeployqt("${exportedTarget}" "${ECM_ADDITIONAL_FIND_ROOT_PATH}") set_target_properties(create-apk-${exportedTarget} PROPERTIES ANDROID_APK_DIR "${APK_DIR}") endforeach() elseif(ANDROID) message(STATUS "You can export a target by specifying -DQTANDROID_EXPORTED_TARGET= and -DANDROID_APK_DIR=") endif() diff --git a/build-tools/windows/build.cmd b/build-tools/windows/build.cmd index 0c3f57f830..a5c9190e12 100644 --- a/build-tools/windows/build.cmd +++ b/build-tools/windows/build.cmd @@ -1,819 +1,820 @@ @echo off setlocal enabledelayedexpansion goto begin :: Subroutines :find_on_path out_variable file_name set %1=%~f$PATH:2 goto :EOF :get_dir_path out_variable file_path set %1=%~dp2 goto :EOF :get_full_path out_variable file_path setlocal set FULL_PATH=%~f2 if not exist "%FULL_PATH%" ( set FULL_PATH= ) else ( if exist "%FULL_PATH%\" ( set FULL_PATH= ) ) endlocal & set "%1=%FULL_PATH%" goto :EOF :get_full_path_dir out_variable file_path setlocal set FULL_PATH=%~dp2 if not exist "%FULL_PATH%" ( set FULL_PATH= ) endlocal & set "%1=%FULL_PATH%" goto :EOF :prompt_for_string out_variable prompt set /p %1=%~2^> goto :EOF :prompt_for_positive_integer out_variable prompt setlocal call :prompt_for_string USER_INPUT "%~2" if "%USER_INPUT%" == "" set USER_INPUT=0 set /a RESULT=%USER_INPUT% if not %RESULT% GTR 0 ( set RESULT= ) endlocal & set "%1=%RESULT%" goto :EOF :prompt_for_file out_variable prompt setlocal :prompt_for_file__retry call :prompt_for_string USER_INPUT "%~2" if "%USER_INPUT%" == "" ( endlocal set %1= goto :EOF ) call :get_full_path RESULT "%USER_INPUT%" if "%RESULT%" == "" ( echo Input does not point to valid file! set USER_INPUT= goto prompt_for_file__retry ) endlocal & set "%1=%RESULT%" goto :EOF :prompt_for_dir out_variable prompt setlocal :prompt_for_dir__retry call :prompt_for_string USER_INPUT "%~2" if "%USER_INPUT%" == "" ( endlocal set %1= goto :EOF ) call :get_full_path_dir RESULT "%USER_INPUT%\" if "%RESULT%" == "" ( echo Input does not point to valid dir! set USER_INPUT= goto prompt_for_dir__retry ) endlocal & set "%1=%RESULT%" goto :EOF :usage echo Usage: echo %~n0 [--no-interactive] [ OPTIONS ... ] echo. echo Basic options: echo --no-interactive Run without interactive prompts echo When not specified, the script will prompt echo for some of the parameters. echo --jobs ^ Set parallel jobs count when building echo Defaults to no. of logical cores echo --skip-deps Skips (re)building of deps echo --skip-krita Skips (re)building of Krita echo --cmd Launch a cmd prompt instead of building. echo The environment is set up like the build echo environment with some helper command macros. echo. echo Path options: echo --src-dir ^ Specify Krita source dir echo If unspecified, this will be determined from echo the script location. echo --download-dir ^ Specify deps download dir echo Can be omitted if --skip-deps is used echo --deps-build-dir ^ Specify deps build dir echo Can be omitted if --skip-deps is used echo --deps-install-dir ^ Specify deps install dir echo --krita-build-dir ^ Specify Krita build dir echo Can be omitted if --skip-krita is used echo --krita-install-dir ^ Specify Krita install dir echo Can be omitted if --skip-krita is used echo. goto :EOF :usage_and_exit call :usage exit /b :usage_and_fail call :usage exit /b 100 :: ---------------------------- :begin echo Krita build script for Windows echo. :: command-line args parsing set ARG_NO_INTERACTIVE= set ARG_JOBS= set ARG_SKIP_DEPS= set ARG_SKIP_KRITA= set ARG_SRC_DIR= set ARG_DOWNLOAD_DIR= set ARG_DEPS_BUILD_DIR= set ARG_DEPS_INSTALL_DIR= set ARG_KRITA_BUILD_DIR= set ARG_KRITA_INSTALL_DIR= set ARG_CMD= :args_parsing_loop set CURRENT_MATCHED= if not "%1" == "" ( if "%1" == "--no-interactive" ( set ARG_NO_INTERACTIVE=1 set CURRENT_MATCHED=1 ) if "%1" == "--jobs" ( if not "%ARG_JOBS%" == "" ( echo ERROR: Arg --jobs specified more than once 1>&2 echo. goto usage_and_fail ) set /a "ARG_JOBS=%2" if not !ARG_JOBS! GTR 0 ( echo ERROR: Arg --jobs is not a positive integer 1>&2 echo. goto usage_and_fail ) shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--skip-deps" ( set ARG_SKIP_DEPS=1 set CURRENT_MATCHED=1 ) if "%1" == "--skip-krita" ( set ARG_SKIP_KRITA=1 set CURRENT_MATCHED=1 ) if "%1" == "--src-dir" ( if not "%ARG_SRC_DIR%" == "" ( echo ERROR: Arg --src-dir specified more than once 1>&2 echo. goto usage_and_fail ) if not exist "%~f2\" ( echo ERROR: Arg --src-dir does not point to a directory 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_SRC_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--download-dir" ( if not "%ARG_DOWNLOAD_DIR%" == "" ( echo ERROR: Arg --download-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --download-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_DOWNLOAD_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--deps-build-dir" ( if not "%ARG_DEPS_BUILD_DIR%" == "" ( echo ERROR: Arg --deps-build-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --deps-build-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_DEPS_BUILD_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--deps-install-dir" ( if not "%ARG_DEPS_INSTALL_DIR%" == "" ( echo ERROR: Arg --deps-install-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --deps-install-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_DEPS_INSTALL_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--krita-build-dir" ( if not "%ARG_KRITA_BUILD_DIR%" == "" ( echo ERROR: Arg --krita-build-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --krita-build-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_KRITA_BUILD_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--krita-install-dir" ( if not "%ARG_KRITA_INSTALL_DIR%" == "" ( echo ERROR: Arg --krita-install-dir specified more than once 1>&2 echo. goto usage_and_fail ) if "%~f2" == "" ( echo ERROR: Arg --krita-install-dir does not point to a valid path 1>&2 echo. goto usage_and_fail ) call :get_dir_path ARG_KRITA_INSTALL_DIR "%~f2\" shift /2 set CURRENT_MATCHED=1 ) if "%1" == "--cmd" ( set ARG_CMD=1 set CURRENT_MATCHED=1 ) if "%1" == "--help" ( goto usage_and_exit ) if not "!CURRENT_MATCHED!" == "1" ( echo ERROR: Unknown option %1 1>&2 echo. goto usage_and_fail ) shift /1 goto args_parsing_loop ) if "%ARG_NO_INTERACTIVE%" == "1" ( echo Non-interactive mode ) else ( echo Interactive mode :: Trick to pause on exit call :real_begin pause exit /b !ERRORLEVEL! ) :real_begin echo. if "%ARG_SKIP_DEPS%" == "1" ( if "%ARG_SKIP_KRITA%" == "1" ( echo ERROR: You cannot skip both deps and Krita 1>&2 echo. exit /b 102 ) echo Building of deps will be skipped. ) else ( if "%ARG_SKIP_KRITA%" == "1" ( echo Building of Krita will be skipped. ) else ( echo Both deps and Krita will be built. ) ) :: Check environment config if "%CMAKE_EXE%" == "" ( call :find_on_path CMAKE_EXE cmake.exe if "!CMAKE_EXE!" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_file CMAKE_EXE "Provide path to cmake.exe" ) if "!CMAKE_EXE!" == "" ( echo ERROR: CMake not found! 1>&2 exit /b 102 ) ) else ( echo Found CMake on PATH: !CMAKE_EXE! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this correct? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_file CMAKE_EXE "Provide path to cmake.exe" if "!CMAKE_EXE!" == "" ( echo ERROR: CMake not found! 1>&2 exit /b 102 ) ) ) ) ) echo CMake: %CMAKE_EXE% if "%SEVENZIP_EXE%" == "" ( call :find_on_path SEVENZIP_EXE 7z.exe if "!SEVENZIP_EXE!" == "" ( set "SEVENZIP_EXE=%ProgramFiles%\7-Zip\7z.exe" if "!SEVENZIP_EXE!" == "" ( set "SEVENZIP_EXE=%ProgramFiles(x86)%\7-Zip\7z.exe" ) if "!SEVENZIP_EXE!" == "" ( echo 7-Zip not found ) ) ) if "%SEVENZIP_EXE%" == "" ( echo 7-Zip: %SEVENZIP_EXE% ) if "%MINGW_BIN_DIR%" == "" ( call :find_on_path MINGW_BIN_DIR_MAKE_EXE mingw32-make.exe if "!MINGW_BIN_DIR_MAKE_EXE!" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_file MINGW_BIN_DIR_MAKE_EXE "Provide path to mingw32-make.exe of mingw-w64" ) if "!MINGW_BIN_DIR_MAKE_EXE!" == "" ( echo ERROR: mingw-w64 not found! 1>&2 exit /b 102 ) call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!" ) else ( call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!" echo Found mingw on PATH: !MINGW_BIN_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this correct? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_file MINGW_BIN_DIR_MAKE_EXE "Provide path to mingw32-make.exe of mingw-w64" if "!MINGW_BIN_DIR_MAKE_EXE!" == "" ( echo ERROR: mingw-w64 not found! 1>&2 exit /b 102 ) call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!" ) ) ) ) echo mingw-w64: %MINGW_BIN_DIR% if "%PYTHON_BIN_DIR%" == "" ( call :find_on_path PYTHON_BIN_DIR_PYTHON_EXE python.exe if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_file PYTHON_BIN_DIR_PYTHON_EXE "Provide path to python.exe of Python 3.6.2" ) if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" ( echo ERROR: Python not found! 1>&2 exit /b 102 ) call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!" ) else ( call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!" echo Found Python on PATH: !PYTHON_BIN_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this correct? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_file PYTHON_BIN_DIR_PYTHON_EXE "Provide path to python.exe of Python 3.6.2" if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" ( echo ERROR: Python not found! 1>&2 exit /b 102 ) call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!" ) ) ) ) echo Python: %PYTHON_BIN_DIR% if "%ARG_SKIP_DEPS%" == "1" goto skip_windows_sdk_dir_check if "%WindowsSdkDir%" == "" if not "%ProgramFiles(x86)%" == "" set "WindowsSdkDir=%ProgramFiles(x86)%\Windows Kits\10" if "%WindowsSdkDir%" == "" set "WindowsSdkDir=%ProgramFiles(x86)%\Windows Kits\10" if exist "%WindowsSdkDir%\" ( pushd "%WindowsSdkDir%" if exist "bin\x64\fxc.exe" ( set HAVE_FXC_EXE=1 if "%WindowsSdkVerBinPath%" == "" set "WindowsSdkVerBinPath=%WindowsSdkDir%" ) else ( for /f "delims=" %%a in ('dir /a:d /b "bin\10.*"') do ( if exist "bin\%%a\x64\fxc.exe" ( set HAVE_FXC_EXE=1 if "%WindowsSdkVerBinPath%" == "" set "WindowsSdkVerBinPath=%WindowsSdkDir%\bin\%%a\" ) ) ) popd ) set QT_ENABLE_DYNAMIC_OPENGL=ON if not "%HAVE_FXC_EXE%" == "1" ( set WindowsSdkDir= echo Windows SDK 10 with fxc.exe not found echo Qt will *not* be built with ANGLE ^(dynamic OpenGL^) support. if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( exit /b 102 ) ) set QT_ENABLE_DYNAMIC_OPENGL=OFF ) else echo Windows SDK 10 with fxc.exe found on %WindowsSdkDir% :skip_windows_sdk_dir_check if not "%ARG_JOBS%" == "" ( set "PARALLEL_JOBS=%ARG_JOBS%" ) if "%PARALLEL_JOBS%" == "" ( echo Number of logical CPU cores detected: %NUMBER_OF_PROCESSORS% echo Enabling %NUMBER_OF_PROCESSORS% parallel jobs set PARALLEL_JOBS=%NUMBER_OF_PROCESSORS% if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this correct? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_positive_integer PARALLEL_JOBS "Provide no. of parallel jobs" if "!PARALLEL_JOBS!" == "" ( echo ERROR: Invalid job count! 1>&2 exit /b 102 ) ) ) ) echo Parallel jobs count: %PARALLEL_JOBS% if not "%ARG_SRC_DIR%" == "" ( set "KRITA_SRC_DIR=%ARG_SRC_DIR%" ) if "%KRITA_SRC_DIR%" == "" ( :: Check whether this looks like to be in the source tree set "_temp=%~dp0" if "!_temp:~-21!" == "\build-tools\windows\" ( if exist "!_temp:~0,-21!\CMakeLists.txt" ( if exist "!_temp:~0,-21!\3rdparty\CMakeLists.txt" ( set "KRITA_SRC_DIR=!_temp:~0,-21!\" echo Script is running inside Krita src dir ) ) ) ) if "%KRITA_SRC_DIR%" == "" ( if not "%ARG_NO_INTERACTIVE%" == "1" ( call :prompt_for_dir KRITA_SRC_DIR "Provide path of Krita src dir" ) if "!KRITA_SRC_DIR!" == "" ( echo ERROR: Krita src dir not found! 1>&2 exit /b 102 ) ) echo Krita src: %KRITA_SRC_DIR% if "%ARG_SKIP_DEPS%" == "1" goto skip_deps_args_check if not "%ARG_DOWNLOAD_DIR%" == "" ( set "DEPS_DOWNLOAD_DIR=%ARG_DOWNLOAD_DIR%" ) if "%DEPS_DOWNLOAD_DIR%" == "" ( set DEPS_DOWNLOAD_DIR=%CD%\d\ echo Using default deps download dir: !DEPS_DOWNLOAD_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir DEPS_DOWNLOAD_DIR "Provide path of depps download dir" ) ) if "!DEPS_DOWNLOAD_DIR!" == "" ( echo ERROR: Deps download dir not set! 1>&2 exit /b 102 ) ) echo Deps download dir: %DEPS_DOWNLOAD_DIR% if not "%ARG_DEPS_BUILD_DIR%" == "" ( set "DEPS_BUILD_DIR=%ARG_DEPS_BUILD_DIR%" ) if "%DEPS_BUILD_DIR%" == "" ( set DEPS_BUILD_DIR=%CD%\b_deps\ echo Using default deps build dir: !DEPS_BUILD_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir DEPS_BUILD_DIR "Provide path of deps build dir" ) ) if "!DEPS_BUILD_DIR!" == "" ( echo ERROR: Deps build dir not set! 1>&2 exit /b 102 ) ) echo Deps build dir: %DEPS_BUILD_DIR% :skip_deps_args_check if not "%ARG_DEPS_INSTALL_DIR%" == "" ( set "DEPS_INSTALL_DIR=%ARG_DEPS_INSTALL_DIR%" ) if "%DEPS_INSTALL_DIR%" == "" ( set DEPS_INSTALL_DIR=%CD%\i_deps\ echo Using default deps install dir: !DEPS_INSTALL_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir DEPS_INSTALL_DIR "Provide path of deps install dir" ) ) if "!DEPS_INSTALL_DIR!" == "" ( echo ERROR: Deps install dir not set! 1>&2 exit /b 102 ) ) echo Deps install dir: %DEPS_INSTALL_DIR% if "%ARG_SKIP_KRITA%" == "1" goto skip_krita_args_check if not "%ARG_KRITA_BUILD_DIR%" == "" ( set "KRITA_BUILD_DIR=%ARG_KRITA_BUILD_DIR%" ) if "%KRITA_BUILD_DIR%" == "" ( set KRITA_BUILD_DIR=%CD%\b\ echo Using default Krita build dir: !KRITA_BUILD_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir KRITA_BUILD_DIR "Provide path of Krita build dir" ) ) if "!KRITA_BUILD_DIR!" == "" ( echo ERROR: Krita build dir not set! 1>&2 exit /b 102 ) ) echo Krita build dir: %KRITA_BUILD_DIR% if not "%ARG_KRITA_INSTALL_DIR%" == "" ( set "KRITA_INSTALL_DIR=%ARG_KRITA_INSTALL_DIR%" ) if "%KRITA_INSTALL_DIR%" == "" ( set KRITA_INSTALL_DIR=%CD%\i\ echo Using default Krita install dir: !KRITA_INSTALL_DIR! if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is this ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( call :prompt_for_dir KRITA_INSTALL_DIR "Provide path of Krita install dir" ) ) if "!KRITA_INSTALL_DIR!" == "" ( echo ERROR: Krita install dir not set! 1>&2 exit /b 102 ) ) echo Krita install dir: %KRITA_INSTALL_DIR% :skip_krita_args_check echo. if not "%ARG_NO_INTERACTIVE%" == "1" ( choice /c ny /n /m "Is the above ok? [y/n] " if errorlevel 3 exit 255 if not errorlevel 2 ( exit /b 1 ) echo. ) :: Initialize clean PATH set PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\ set PATH=%MINGW_BIN_DIR%;%PYTHON_BIN_DIR%;%PATH% echo Creating dirs... if NOT "%ARG_SKIP_DEPS%" == "1" ( mkdir %DEPS_DOWNLOAD_DIR% if errorlevel 1 ( if not exist "%DEPS_DOWNLOAD_DIR%\" ( echo ERROR: Cannot create deps download dir! 1>&2 exit /b 103 ) ) mkdir %DEPS_BUILD_DIR% if errorlevel 1 ( if not exist "%DEPS_BUILD_DIR%\" ( echo ERROR: Cannot create deps build dir! 1>&2 exit /b 103 ) ) mkdir %DEPS_INSTALL_DIR% if errorlevel 1 ( if not exist "%DEPS_INSTALL_DIR%\" ( echo ERROR: Cannot create deps install dir! 1>&2 exit /b 103 ) ) ) if NOT "%ARG_SKIP_KRITA%" == "1" ( mkdir %KRITA_BUILD_DIR% if errorlevel 1 ( if not exist "%KRITA_BUILD_DIR%\" ( echo ERROR: Cannot create Krita build dir! 1>&2 exit /b 103 ) ) mkdir %KRITA_INSTALL_DIR% if errorlevel 1 ( if not exist "%KRITA_INSTALL_DIR%\" ( echo ERROR: Cannot create Krita install dir! 1>&2 exit /b 103 ) ) ) echo. set CMAKE_BUILD_TYPE=RelWithDebInfo set QT_ENABLE_DEBUG_INFO=OFF :: Paths for CMake set "BUILDDIR_DOWNLOAD_CMAKE=%DEPS_DOWNLOAD_DIR:\=/%" set "BUILDDIR_DOWNLOAD_CMAKE=%BUILDDIR_DOWNLOAD_CMAKE: =\ %" set "BUILDDIR_DEPS_INSTALL_CMAKE=%DEPS_INSTALL_DIR:\=/%" set "BUILDDIR_DEPS_INSTALL_CMAKE=%BUILDDIR_DEPS_INSTALL_CMAKE: =\ %" set "BUILDDIR_KRITA_INSTALL_CMAKE=%KRITA_INSTALL_DIR:\=/%" set "BUILDDIR_KRITA_INSTALL_CMAKE=%BUILDDIR_KRITA_INSTALL_CMAKE: =\ %" set PATH=%DEPS_INSTALL_DIR%\bin;%PATH% if not "%GETTEXT_SEARCH_PATH%" == "" ( set PATH=!PATH!;!GETTEXT_SEARCH_PATH! ) :: Prepare the CMake command lines set CMDLINE_CMAKE_DEPS="%CMAKE_EXE%" "%KRITA_SRC_DIR%\3rdparty" ^ -DSUBMAKE_JOBS=%PARALLEL_JOBS% ^ -DQT_ENABLE_DEBUG_INFO=%QT_ENABLE_DEBUG_INFO% ^ -DQT_ENABLE_DYNAMIC_OPENGL=%QT_ENABLE_DYNAMIC_OPENGL% ^ -DEXTERNALS_DOWNLOAD_DIR=%BUILDDIR_DOWNLOAD_CMAKE% ^ -DINSTALL_ROOT=%BUILDDIR_DEPS_INSTALL_CMAKE% ^ -G "MinGW Makefiles" ^ -DUSE_QT_TABLET_WINDOWS=ON ^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% set CMDLINE_CMAKE_KRITA="%CMAKE_EXE%" "%KRITA_SRC_DIR%\." ^ -DBoost_DEBUG=OFF ^ -DBOOST_INCLUDEDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/include ^ -DBOOST_ROOT=%BUILDDIR_DEPS_INSTALL_CMAKE% ^ -DBOOST_LIBRARYDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/lib ^ -DCMAKE_PREFIX_PATH=%BUILDDIR_DEPS_INSTALL_CMAKE% ^ -DCMAKE_INSTALL_PREFIX=%BUILDDIR_KRITA_INSTALL_CMAKE% ^ -DBUILD_TESTING=OFF ^ -DHAVE_MEMORY_LEAK_TRACKER=OFF ^ -DFOUNDATION_BUILD=ON ^ -DUSE_QT_TABLET_WINDOWS=ON ^ -DHIDE_SAFE_ASSERTS=ON ^ -Wno-dev ^ -G "MinGW Makefiles" ^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% :: Launch CMD prompt if requested if "%ARG_CMD%" == "1" ( doskey cmake-deps=cmd /c "pushd %DEPS_BUILD_DIR% && %CMDLINE_CMAKE_DEPS%" doskey cmake-krita=cmd /c "pushd %KRITA_BUILD_DIR% && %CMDLINE_CMAKE_KRITA%" doskey make-deps=cmd /c "pushd %DEPS_BUILD_DIR% && "%CMAKE_EXE%" --build . --config %CMAKE_BUILD_TYPE% --target $*" doskey make-krita=cmd /c "pushd %KRITA_BUILD_DIR% && "%CMAKE_EXE%" --build . --config %CMAKE_BUILD_TYPE% --target install -- -j%PARALLEL_JOBS%" echo. title Krita build - %KRITA_SRC_DIR% ^(deps: %DEPS_BUILD_DIR%, krita: %KRITA_BUILD_DIR%^) echo You're now in the build environment. echo The following macros are available: echo cmake-deps echo -- Run CMake for the deps. echo make-deps ^ echo -- Run build for the specified deps target. The target name should echo include the `ext_` prefix, e.g. `ext_qt`. echo cmake-krita echo -- Run CMake for Krita. echo make-krita echo -- Run build for Krita's `install` target. echo. echo For more info, type `doskey /macros` to view the macro commands. cmd /k exit ) if "%ARG_SKIP_DEPS%" == "1" goto skip_build_deps pushd %DEPS_BUILD_DIR% if errorlevel 1 ( echo ERROR: Cannot enter deps build dir! 1>&2 exit /b 104 ) echo Running CMake for deps... @echo on %CMDLINE_CMAKE_DEPS% @if errorlevel 1 ( @echo ERROR: CMake configure failed! 1>&2 @exit /b 104 ) @echo off echo. set EXT_TARGETS=patch png2ico zlib gettext openssl boost exiv2 fftw3 eigen3 set EXT_TARGETS=%EXT_TARGETS% jpeg lcms2 ocio ilmbase openexr png tiff gsl vc libraw set EXT_TARGETS=%EXT_TARGETS% giflib qt kwindowsystem drmingw gmic freetype poppler set EXT_TARGETS=%EXT_TARGETS% python sip pyqt set EXT_TARGETS=%EXT_TARGETS% lzma quazip openjpeg libheif +set EXT_TARGETS=%EXT_TARGETS% seexpr for %%a in (%EXT_TARGETS%) do ( echo Building ext_%%a... - "%CMAKE_EXE%" --build . --config %CMAKE_BUILD_TYPE% --target ext_%%a + "%CMAKE_EXE%" --build . --config %CMAKE_BUILD_TYPE% --target ext_%%a -- -j%PARALLEL_JOBS% if errorlevel 1 ( echo ERROR: Building of ext_%%a failed! 1>&2 exit /b 105 ) ) echo. echo ******** Built deps ******** popd :skip_build_deps if "%ARG_SKIP_KRITA%" == "1" goto skip_build_krita pushd %KRITA_BUILD_DIR% if errorlevel 1 ( echo ERROR: Cannot enter Krita build dir! 1>&2 exit /b 104 ) echo Running CMake for Krita... @echo on %CMDLINE_CMAKE_KRITA% @if errorlevel 1 ( @echo ERROR: CMake configure failed! 1>&2 @exit /b 104 ) @echo off echo. echo Building Krita... "%CMAKE_EXE%" --build . --config %CMAKE_BUILD_TYPE% --target install -- -j%PARALLEL_JOBS% if errorlevel 1 ( echo ERROR: Building of Krita failed! 1>&2 exit /b 105 ) echo. echo ******** Built Krita ******** popd :skip_build_krita echo Krita build completed! diff --git a/config-seexpr.h.cmake b/config-seexpr.h.cmake new file mode 100644 index 0000000000..639849d179 --- /dev/null +++ b/config-seexpr.h.cmake @@ -0,0 +1,4 @@ +/* config-seexpr.h. Generated by cmake from config-seexpr.h.cmake */ + +/* Define if you have SeExpr, Disney Animation's expression language library */ +#cmakedefine HAVE_SEEXPR 1 diff --git a/libs/flake/CMakeLists.txt b/libs/flake/CMakeLists.txt index cbdac265d2..a43227dabc 100644 --- a/libs/flake/CMakeLists.txt +++ b/libs/flake/CMakeLists.txt @@ -1,249 +1,255 @@ project(kritaflake) include_directories( ${CMAKE_SOURCE_DIR}/libs/flake/commands ${CMAKE_SOURCE_DIR}/libs/flake/tools ${CMAKE_SOURCE_DIR}/libs/flake/svg ${CMAKE_SOURCE_DIR}/libs/flake/text ${CMAKE_BINARY_DIR}/libs/flake ) add_subdirectory(styles) add_subdirectory(tests) add_subdirectory(resources/tests) set(kritaflake_SRCS KoGradientHelper.cpp KoFlake.cpp KoCanvasBase.cpp KoResourceManager_p.cpp KoDerivedResourceConverter.cpp KoResourceUpdateMediator.cpp KoCanvasResourceProvider.cpp KoDocumentResourceManager.cpp KoCanvasObserverBase.cpp KoCanvasSupervisor.cpp KoDockFactoryBase.cpp KoDockRegistry.cpp KoDataCenterBase.cpp KoInsets.cpp KoPathShape.cpp KoPathPoint.cpp KoPathSegment.cpp KoSelection.cpp KoSelectedShapesProxy.cpp KoSelectedShapesProxySimple.cpp KoShape.cpp KoShapeAnchor.cpp KoShapeControllerBase.cpp KoShapeApplicationData.cpp KoShapeContainer.cpp KoShapeContainerModel.cpp KoShapeGroup.cpp KoShapeManager.cpp KoShapePaintingContext.cpp KoFrameShape.cpp KoMarker.cpp KoMarkerCollection.cpp KoToolBase.cpp KoCanvasController.cpp KoCanvasControllerWidget.cpp KoCanvasControllerWidgetViewport_p.cpp KoShapeRegistry.cpp KoDeferredShapeFactoryBase.cpp KoToolFactoryBase.cpp KoPathShapeFactory.cpp KoShapeFactoryBase.cpp KoShapeUserData.cpp KoParameterShape.cpp KoPointerEvent.cpp KoShapeController.cpp KoToolSelection.cpp KoShapeLayer.cpp KoPostscriptPaintDevice.cpp KoInputDevice.cpp KoToolManager_p.cpp KoToolManager.cpp KoToolRegistry.cpp KoToolProxy.cpp KoShapeSavingContext.cpp KoShapeLoadingContext.cpp KoLoadingShapeUpdater.cpp KoPathShapeLoader.cpp KoShapeStrokeModel.cpp KoShapeStroke.cpp KoShapeBackground.cpp KoColorBackground.cpp KoGradientBackground.cpp KoOdfGradientBackground.cpp KoHatchBackground.cpp KoPatternBackground.cpp KoVectorPatternBackground.cpp KoShapeFillWrapper.cpp KoShapeFillResourceConnector.cpp KoShapeConfigWidgetBase.cpp KoDrag.cpp KoSvgPaste.cpp KoDragOdfSaveHelper.cpp KoShapeOdfSaveHelper.cpp KoConnectionPoint.cpp KoConnectionShape.cpp KoConnectionShapeLoadingUpdater.cpp KoConnectionShapeFactory.cpp KoConnectionShapeConfigWidget.cpp KoSnapGuide.cpp KoSnapProxy.cpp KoSnapStrategy.cpp KoSnapData.cpp KoShapeShadow.cpp KoSharedLoadingData.cpp KoSharedSavingData.cpp KoViewConverter.cpp KoInputDeviceHandler.cpp KoInputDeviceHandlerEvent.cpp KoInputDeviceHandlerRegistry.cpp KoImageData.cpp KoImageData_p.cpp KoImageCollection.cpp KoOdfWorkaround.cpp KoFilterEffect.cpp KoFilterEffectStack.cpp KoFilterEffectFactoryBase.cpp KoFilterEffectRegistry.cpp KoFilterEffectConfigWidgetBase.cpp KoFilterEffectRenderContext.cpp KoFilterEffectLoadingContext.cpp KoTextShapeDataBase.cpp KoTosContainer.cpp KoTosContainerModel.cpp KoClipPath.cpp KoClipMask.cpp KoClipMaskPainter.cpp KoCurveFit.cpp commands/KoShapeGroupCommand.cpp commands/KoShapeAlignCommand.cpp commands/KoShapeBackgroundCommand.cpp commands/KoShapeCreateCommand.cpp commands/KoShapeDeleteCommand.cpp commands/KoShapeDistributeCommand.cpp commands/KoShapeLockCommand.cpp commands/KoShapeMoveCommand.cpp commands/KoShapeResizeCommand.cpp commands/KoShapeShearCommand.cpp commands/KoShapeSizeCommand.cpp commands/KoShapeStrokeCommand.cpp commands/KoShapeUngroupCommand.cpp commands/KoShapeReorderCommand.cpp commands/KoShapeKeepAspectRatioCommand.cpp commands/KoPathBaseCommand.cpp commands/KoPathPointMoveCommand.cpp commands/KoPathControlPointMoveCommand.cpp commands/KoPathPointTypeCommand.cpp commands/KoPathPointRemoveCommand.cpp commands/KoPathPointInsertCommand.cpp commands/KoPathSegmentBreakCommand.cpp commands/KoPathBreakAtPointCommand.cpp commands/KoPathSegmentTypeCommand.cpp commands/KoPathCombineCommand.cpp commands/KoSubpathRemoveCommand.cpp commands/KoSubpathJoinCommand.cpp commands/KoParameterHandleMoveCommand.cpp commands/KoParameterToPathCommand.cpp commands/KoShapeTransformCommand.cpp commands/KoPathFillRuleCommand.cpp commands/KoConnectionShapeTypeCommand.cpp commands/KoShapeShadowCommand.cpp commands/KoPathReverseCommand.cpp commands/KoShapeRenameCommand.cpp commands/KoShapeRunAroundCommand.cpp commands/KoPathPointMergeCommand.cpp commands/KoShapeTransparencyCommand.cpp commands/KoShapeClipCommand.cpp commands/KoShapeUnclipCommand.cpp commands/KoPathShapeMarkerCommand.cpp commands/KoShapeConnectionChangeCommand.cpp commands/KoMultiPathPointMergeCommand.cpp commands/KoMultiPathPointJoinCommand.cpp commands/KoKeepShapesSelectedCommand.cpp commands/KoPathMergeUtils.cpp commands/KoAddRemoveShapeCommands.cpp html/HtmlSavingContext.cpp html/HtmlWriter.cpp tools/KoPathToolFactory.cpp tools/KoPathTool.cpp tools/KoPathToolSelection.cpp tools/KoPathToolHandle.cpp tools/PathToolOptionWidget.cpp tools/KoPathPointRubberSelectStrategy.cpp tools/KoPathPointMoveStrategy.cpp tools/KoPathConnectionPointStrategy.cpp tools/KoPathControlPointMoveStrategy.cpp tools/KoParameterChangeStrategy.cpp tools/KoZoomTool.cpp tools/KoZoomToolFactory.cpp tools/KoZoomToolWidget.cpp tools/KoZoomStrategy.cpp tools/KoInteractionTool.cpp tools/KoInteractionStrategy.cpp tools/KoInteractionStrategyFactory.cpp tools/KoShapeRubberSelectStrategy.cpp tools/KoPathSegmentChangeStrategy.cpp svg/KoShapePainter.cpp svg/SvgUtil.cpp svg/SvgGraphicContext.cpp svg/SvgSavingContext.cpp svg/SvgWriter.cpp svg/SvgStyleWriter.cpp svg/SvgShape.cpp svg/SvgParser.cpp svg/SvgStyleParser.cpp svg/SvgGradientHelper.cpp svg/SvgFilterHelper.cpp svg/SvgCssHelper.cpp svg/SvgClipPathHelper.cpp svg/SvgLoadingContext.cpp svg/SvgShapeFactory.cpp svg/parsers/SvgTransformParser.cpp text/KoSvgText.cpp text/KoSvgTextProperties.cpp text/KoSvgTextChunkShape.cpp text/KoSvgTextShape.cpp text/KoSvgTextShapeMarkupConverter.cpp resources/KoSvgSymbolCollectionResource.cpp resources/KoGamutMask.cpp FlakeDebug.cpp tests/MockShapes.cpp ) +if (SeExpr2_FOUND) + set(kritaflake_SRCS ${kritaflake_SRCS} + resources/KisSeExprScript.cpp + ) +endif() + ki18n_wrap_ui(kritaflake_SRCS tools/PathToolOptionWidgetBase.ui KoConnectionShapeConfigWidget.ui tools/KoZoomToolWidget.ui ) add_library(kritaflake SHARED ${kritaflake_SRCS}) generate_export_header(kritaflake BASE_NAME kritaflake) target_include_directories(kritaflake PUBLIC $ $ $ $ ) target_link_libraries(kritaflake kritapigment kritawidgetutils kritaodf kritacommand KF5::WidgetsAddons Qt5::Svg) set_target_properties(kritaflake PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaflake ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/flake/resources/KisSeExprScript.cpp b/libs/flake/resources/KisSeExprScript.cpp new file mode 100644 index 0000000000..c6ea6648b9 --- /dev/null +++ b/libs/flake/resources/KisSeExprScript.cpp @@ -0,0 +1,242 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "KisSeExprScript.h" + +struct KisSeExprScript::Private +{ + QString script; + QByteArray data; + bool dirtyPreset = false; +}; + +KisSeExprScript::KisSeExprScript(const QString &filename) + : KoResource(filename), d(new Private) +{ +} + +KisSeExprScript::KisSeExprScript(const QImage &image, const QString &script, const QString &name, const QString &folderName) + : KoResource(QString()), d(new Private) +{ + setScript(script); + setImage(image); + setName(name); + + QFileInfo fileInfo(folderName + QDir::separator() + name + defaultFileExtension()); + + int i = 1; + while (fileInfo.exists()) + { + fileInfo.setFile(folderName + QDir::separator() + + name + QString::number(i) + defaultFileExtension()); + i++; + } + + setFilename(fileInfo.filePath()); +} + +KisSeExprScript::~KisSeExprScript() +{ + delete d; +} + +bool KisSeExprScript::load() +{ + QFile file(filename()); + if (file.size() == 0) + return false; + + if (!file.open(QIODevice::ReadOnly)) + { + warnFlake << "Can't open file " << filename(); + return false; + } + bool result = loadFromDevice(&file); + file.close(); + + return result; +} + +bool KisSeExprScript::loadFromDevice(QIODevice *dev) +{ + if (!dev->isOpen()) + dev->open(QIODevice::ReadOnly); + + d->data = dev->readAll(); + + // TODO: test + KIS_ASSERT_RECOVER_RETURN_VALUE(d->data.size() != 0, false); + + if (filename().isNull()) + { + warnFlake << "Cannot load SeExpr script" << name() << ", there is no filename set"; + return false; + } + + if (d->data.isNull()) + { + QFile file(filename()); + if (file.size() == 0) + { + warnFlake << "Cannot load SeExpr script" << name() << "there is no data available"; + return false; + } + + file.open(QIODevice::ReadOnly); + d->data = file.readAll(); + file.close(); + } + + QBuffer buf(&d->data); + buf.open(QBuffer::ReadOnly); + + QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "application/x-krita-seexpr-script", KoStore::Zip)); + if (!store || store->bad()) + return false; + + bool storeOpened = store->open("script.se"); + if (!storeOpened) + { + return false; + } + + d->script = QString(store->read(store->size())); + store->close(); + + if (store->open("preview.png")) + { + KoStoreDevice previewDev(store.data()); + previewDev.open(QIODevice::ReadOnly); + + QImage preview = QImage(); + preview.load(&previewDev, "PNG"); + setImage(preview); + + (void)store->close(); + } + + buf.close(); + + QFileInfo fileinfo(filename()); + // The name of a SeExpr script is its basename + // KoResourceServer uses its filename -- amyspark + setName(fileinfo.baseName()); + setValid(true); + setDirty(false); + + return true; +} + +bool KisSeExprScript::save() +{ + QFile file(filename()); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + { + return false; + } + saveToDevice(&file); + file.close(); + + return true; +} + +bool KisSeExprScript::saveToDevice(QIODevice *dev) const +{ + KoStore *store(KoStore::createStore(dev, KoStore::Write, "application/x-krita-seexpr-script", KoStore::Zip)); + if (!store || store->bad()) + return false; + + if (!store->open("script.se")) + { + return false; + } + + KoStoreDevice storeDev(store); + storeDev.open(QIODevice::WriteOnly); + + storeDev.write(d->script.toUtf8()); + + if (!store->close()) + { + return false; + } + + if (!store->open("preview.png")) + { + return false; + } + + KoStoreDevice previewDev(store); + previewDev.open(QIODevice::WriteOnly); + + image().save(&previewDev, "PNG"); + if (!store->close()) + { + return false; + } + + return store->finalize(); +} + +QString KisSeExprScript::defaultFileExtension() const +{ + return QString(".kse"); +} + +QString KisSeExprScript::script() const +{ + return d->script; +} + +void KisSeExprScript::setScript(const QString &script) +{ + d->script = script; +} + +KisSeExprScript* KisSeExprScript::clone() const +{ + KisSeExprScript* scr = new KisSeExprScript(filename()); + scr->setScript(script()); + scr->setImage(image()); + scr->setName(name()); + scr->setValid(valid()); + scr->setDirty(isDirty()); + + return scr; +} + +void KisSeExprScript::setDirty(bool value) +{ + d->dirtyPreset = value; +} + +bool KisSeExprScript::isDirty() const +{ + return d->dirtyPreset; +} diff --git a/libs/flake/resources/KisSeExprScript.h b/libs/flake/resources/KisSeExprScript.h new file mode 100644 index 0000000000..1ae6258aea --- /dev/null +++ b/libs/flake/resources/KisSeExprScript.h @@ -0,0 +1,103 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_SEEXPR_SCRIPT_H +#define KIS_SEEXPR_SCRIPT_H + +#include +#include +#include +#include +#include + +class KisSeExprScript; + +typedef KisSharedPtr KisSeExprScriptSP; + +/** + * KoResource container for SeExpr scripts. + */ +class KRITAFLAKE_EXPORT KisSeExprScript : public KoResource, public KisShared +{ + +public: + + /** + * Creates a new KisSeExprScript object using @p filename. No file is opened + * in the constructor, you have to call load. + * + * @param filename the file name to save and load from. + */ + KisSeExprScript(const QString &filename); + + /** + * Creates a new SeExpr script resource with the given @p image thumbnail, + * @p script, @p name, @p and folder name. + * + * @param image the thumbnail of the texture this script will render + * @param script the actual script's contents + * @param name the name of the script + * @param folderName the folder name + */ + KisSeExprScript(const QImage &image, const QString &script, const QString &name, const QString &folderName); + + ~KisSeExprScript(); + +public: + /** + * Load this resource. + * @return true if loading the resource succeeded. + */ + bool load() override; + bool loadFromDevice(QIODevice *dev) override; + + /** + * Save this resource. + * @return true if saving the resource succeeded. + */ + bool save() override; + bool saveToDevice(QIODevice* dev) const override; + + QString defaultFileExtension() const override; + + /** + * @brief script the actual script + * @return a valid SeExpr script. It is guaranteed to be in UTF-8. + */ + QString script() const; + + /** + * @brief set SeExpr expression script + */ + void setScript(const QString &script); + + KisSeExprScript* clone() const; + + bool isDirty() const; + void setDirty(bool value); + +private: + struct Private; + Private *const d; +}; + +Q_DECLARE_METATYPE(KisSeExprScript*) + +#endif // KIS_SEEXPR_SCRIPT_H diff --git a/libs/image/generator/kis_generator_layer.cpp b/libs/image/generator/kis_generator_layer.cpp index d2cea64545..5f35d67c25 100644 --- a/libs/image/generator/kis_generator_layer.cpp +++ b/libs/image/generator/kis_generator_layer.cpp @@ -1,189 +1,191 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_generator_layer.h" #include #include "kis_debug.h" #include #include #include "kis_selection.h" #include "filter/kis_filter_configuration.h" #include "kis_processing_information.h" #include "generator/kis_generator_registry.h" #include "generator/kis_generator.h" #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_thread_safe_signal_compressor.h" #include "kis_recalculate_generator_layer_job.h" #define UPDATE_DELAY 100 /*ms */ struct Q_DECL_HIDDEN KisGeneratorLayer::Private { Private() : updateSignalCompressor(UPDATE_DELAY, KisSignalCompressor::FIRST_INACTIVE) { } KisThreadSafeSignalCompressor updateSignalCompressor; QRect preparedRect; KisFilterConfigurationSP preparedForFilter; }; KisGeneratorLayer::KisGeneratorLayer(KisImageWSP image, const QString &name, KisFilterConfigurationSP kfc, KisSelectionSP selection) : KisSelectionBasedLayer(image, name, selection, kfc, true), m_d(new Private) { connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate())); } KisGeneratorLayer::KisGeneratorLayer(const KisGeneratorLayer& rhs) : KisSelectionBasedLayer(rhs), m_d(new Private) { connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate())); } KisGeneratorLayer::~KisGeneratorLayer() { } void KisGeneratorLayer::setFilter(KisFilterConfigurationSP filterConfig) { KisSelectionBasedLayer::setFilter(filterConfig); m_d->preparedRect = QRect(); slotDelayedStaticUpdate(); } void KisGeneratorLayer::slotDelayedStaticUpdate() { /** * The mask might have been deleted from the layers stack in the * meanwhile. Just ignore the updates in the case. */ KisLayerSP parentLayer(qobject_cast(parent().data())); if (!parentLayer) return; KisImageSP image = parentLayer->image(); if (image) { image->addSpontaneousJob(new KisRecalculateGeneratorLayerJob(KisGeneratorLayerSP(this))); } } void KisGeneratorLayer::update() { KisImageSP image = this->image().toStrongRef(); const QRect updateRect = extent() | image->bounds(); KisFilterConfigurationSP filterConfig = filter(); KIS_SAFE_ASSERT_RECOVER_RETURN(filterConfig); if (filterConfig != m_d->preparedForFilter) { resetCache(); } const QRegion processRegion(QRegion(updateRect) - m_d->preparedRect); if (processRegion.isEmpty()) return; KisGeneratorSP f = KisGeneratorRegistry::instance()->value(filterConfig->name()); KIS_SAFE_ASSERT_RECOVER_RETURN(f); + KisProcessingVisitor::ProgressHelper helper(this); + KisPaintDeviceSP originalDevice = original(); QVector dirtyRegion; auto rc = processRegion.begin(); while (rc != processRegion.end()) { KisProcessingInformation dstCfg(originalDevice, rc->topLeft(), KisSelectionSP()); - f->generate(dstCfg, rc->size(), filterConfig.data()); + f->generate(dstCfg, rc->size(), filterConfig.data(), helper.updater()); dirtyRegion << *rc; rc++; } m_d->preparedRect = updateRect; m_d->preparedForFilter = filterConfig; // HACK ALERT!!! // this avoids cyclic loop with KisRecalculateGeneratorLayerJob::run() KisSelectionBasedLayer::setDirty(dirtyRegion); } bool KisGeneratorLayer::accept(KisNodeVisitor & v) { return v.visit(this); } void KisGeneratorLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } QIcon KisGeneratorLayer::icon() const { return KisIconUtils::loadIcon("fillLayer"); } KisBaseNode::PropertyList KisGeneratorLayer::sectionModelProperties() const { KisFilterConfigurationSP filterConfig = filter(); KisBaseNode::PropertyList l = KisLayer::sectionModelProperties(); l << KisBaseNode::Property(KoID("generator", i18n("Generator")), KisGeneratorRegistry::instance()->value(filterConfig->name())->name()); return l; } void KisGeneratorLayer::setX(qint32 x) { KisSelectionBasedLayer::setX(x); m_d->preparedRect = QRect(); m_d->updateSignalCompressor.start(); } void KisGeneratorLayer::setY(qint32 y) { KisSelectionBasedLayer::setY(y); m_d->preparedRect = QRect(); m_d->updateSignalCompressor.start(); } void KisGeneratorLayer::resetCache() { KisSelectionBasedLayer::resetCache(); m_d->preparedRect = QRect(); m_d->updateSignalCompressor.start(); } void KisGeneratorLayer::setDirty(const QVector &rects) { KisSelectionBasedLayer::setDirty(rects); m_d->updateSignalCompressor.start(); } diff --git a/libs/koplugin/KisMimeDatabase.cpp b/libs/koplugin/KisMimeDatabase.cpp index 9ca4ab82a8..091f8b2c70 100644 --- a/libs/koplugin/KisMimeDatabase.cpp +++ b/libs/koplugin/KisMimeDatabase.cpp @@ -1,297 +1,302 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMimeDatabase.h" #include #include #include #include #include QList KisMimeDatabase::s_mimeDatabase; QString KisMimeDatabase::mimeTypeForFile(const QString &file, bool checkExistingFiles) { fillMimeData(); QFileInfo fi(file); QString suffix = fi.suffix().toLower(); Q_FOREACH(const KisMimeDatabase::KisMimeType &mimeType, s_mimeDatabase) { if (mimeType.suffixes.contains(suffix)) { debugPlugin << "mimeTypeForFile(). KisMimeDatabase returned" << mimeType.mimeType << "for" << file; return mimeType.mimeType; } } QMimeDatabase db; QMimeType mime; if (checkExistingFiles && fi.size() > 0) { mime = db.mimeTypeForFile(file, QMimeDatabase::MatchContent); if (mime.name() != "application/octet-stream" && mime.name() != "application/zip") { debugPlugin << "mimeTypeForFile(). QMimeDatabase returned" << mime.name() << "for" << file; return mime.name(); } } mime = db.mimeTypeForFile(file); if (mime.name() != "application/octet-stream") { debugPlugin << "mimeTypeForFile(). QMimeDatabase returned" << mime.name() << "for" << file; return mime.name(); } return ""; } QString KisMimeDatabase::mimeTypeForSuffix(const QString &suffix) { fillMimeData(); QMimeDatabase db; QString s = suffix.toLower(); Q_FOREACH(const KisMimeDatabase::KisMimeType &mimeType, s_mimeDatabase) { if (mimeType.suffixes.contains(s)) { debugPlugin << "mimeTypeForSuffix(). KisMimeDatabase returned" << mimeType.mimeType << "for" << s; return mimeType.mimeType; } } // make the file look like a file so Qt would recognize it s = "file." + s; return mimeTypeForFile(s); } QString KisMimeDatabase::mimeTypeForData(const QByteArray ba) { QMimeDatabase db; QMimeType mtp = db.mimeTypeForData(ba); debugPlugin << "mimeTypeForData(). QMimeDatabase returned" << mtp.name(); return mtp.name(); } QString KisMimeDatabase::descriptionForMimeType(const QString &mimeType) { fillMimeData(); Q_FOREACH(const KisMimeDatabase::KisMimeType &m, s_mimeDatabase) { if (m.mimeType == mimeType) { debugPlugin << "descriptionForMimeType. KisMimeDatabase returned" << m.description << "for" << mimeType; return m.description; } } QMimeDatabase db; QMimeType mime = db.mimeTypeForName(mimeType); if (mime.name() != "application/octet-stream") { debugPlugin << "descriptionForMimeType. QMimeDatabase returned" << mime.comment() << "for" << mimeType; return mime.comment(); } return mimeType; } QStringList KisMimeDatabase::suffixesForMimeType(const QString &mimeType) { fillMimeData(); Q_FOREACH(const KisMimeDatabase::KisMimeType &m, s_mimeDatabase) { if (m.mimeType == mimeType) { debugPlugin << "suffixesForMimeType. KisMimeDatabase returned" << m.suffixes; return m.suffixes; } } QMimeDatabase db; QMimeType mime = db.mimeTypeForName(mimeType); if (mime.name() != "application/octet-stream" && !mime.suffixes().isEmpty()) { QString preferredSuffix = mime.preferredSuffix(); if (mimeType == "image/x-tga") { preferredSuffix = "tga"; } if (mimeType == "image/jpeg") { preferredSuffix = "jpg"; } QStringList suffixes = mime.suffixes(); if (preferredSuffix != suffixes.first()) { suffixes.removeAll(preferredSuffix); suffixes.prepend(preferredSuffix); } debugPlugin << "suffixesForMimeType. QMimeDatabase returned" << suffixes; return suffixes; } return QStringList(); } QString KisMimeDatabase::iconNameForMimeType(const QString &mimeType) { QMimeDatabase db; QMimeType mime = db.mimeTypeForName(mimeType); debugPlugin << "iconNameForMimeType" << mime.iconName(); return mime.iconName(); } void KisMimeDatabase::fillMimeData() { // This should come from the import/export plugins, but the json files aren't translated, // which is bad for the description field if (s_mimeDatabase.isEmpty()) { KisMimeType mimeType; mimeType.mimeType = "image/x-gimp-brush"; mimeType.description = i18nc("description of a file type", "Gimp Brush"); mimeType.suffixes = QStringList() << "gbr" << "vbr"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-gimp-brush-animated"; mimeType.description = i18nc("description of a file type", "Gimp Image Hose Brush"); mimeType.suffixes = QStringList() << "gih"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-adobe-brushlibrary"; mimeType.description = i18nc("description of a file type", "Adobe Brush Library"); mimeType.suffixes = QStringList() << "abr"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-paintoppreset"; mimeType.description = i18nc("description of a file type", "Krita Brush Preset"); mimeType.suffixes = QStringList() << "kpp"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-assistant"; mimeType.description = i18nc("description of a file type", "Krita Assistant"); mimeType.suffixes = QStringList() << "paintingassistant"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-r32"; mimeType.description = i18nc("description of a file type", "R32 Heightmap"); mimeType.suffixes = QStringList() << "r32"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-r16"; mimeType.description = i18nc("description of a file type", "R16 Heightmap"); mimeType.suffixes = QStringList() << "r16"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-r8"; mimeType.description = i18nc("description of a file type", "R8 Heightmap"); mimeType.suffixes = QStringList() << "r8"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-spriter"; mimeType.description = i18nc("description of a file type", "Spriter SCML"); mimeType.suffixes = QStringList() << "scml"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-svm"; mimeType.description = i18nc("description of a file type", "Starview Metafile"); mimeType.suffixes = QStringList() << "svm"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/openraster"; mimeType.description = i18nc("description of a file type", "OpenRaster Image"); mimeType.suffixes = QStringList() << "ora"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-photoshop-style-library"; mimeType.description = i18nc("description of a file type", "Photoshop Layer Style Library"); mimeType.suffixes = QStringList() << "asl"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-gimp-color-palette"; mimeType.description = i18nc("description of a file type", "Color Palette"); mimeType.suffixes = QStringList() << "gpl" << "pal" << "act" << "aco" << "colors" << "xml" << "sbz"; s_mimeDatabase << mimeType; mimeType.mimeType = "krita/x-colorset"; mimeType.description = i18nc("description of a file type", "Krita Color Palette"); mimeType.suffixes = QStringList() << "kpl"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-opencolorio-configuration"; mimeType.description = i18nc("description of a file type", "OpenColorIO Configuration"); mimeType.suffixes = QStringList() << "ocio"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-gimp-gradient"; mimeType.description = i18nc("description of a file type", "GIMP Gradients"); mimeType.suffixes = QStringList() << "ggr"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-gimp-pattern"; mimeType.description = i18nc("description of a file type", "GIMP Patterns"); mimeType.suffixes = QStringList() << "pat"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-bundle"; mimeType.description = i18nc("description of a file type", "Krita Resource Bundle"); mimeType.suffixes = QStringList() << "bundle"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-workspace"; mimeType.description = i18nc("description of a file type", "Krita Workspace"); mimeType.suffixes = QStringList() << "kws"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-taskset"; mimeType.description = i18nc("description of a file type", "Krita Taskset"); mimeType.suffixes = QStringList() << "kts"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-reference-images"; mimeType.description = i18nc("description of a file type", "Krita Reference Image Collection"); mimeType.suffixes = QStringList() << "krf"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-krita-shortcuts"; mimeType.description = i18nc("description of a file type", "Krita Shortcut Scheme"); mimeType.suffixes = QStringList() << "shortcuts"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-krita-raw"; mimeType.description = i18nc("description of a file type", "Camera Raw Files"); mimeType.suffixes = QStringList() << "bay" << "bmq" << "cr2" << "crw" << "cs1" << "dc2" << "dcr" << "dng" << "erf" << "fff" << "hdr" << "k25" << "kdc" << "mdc" << "mos" << "mrw" << "nef" << "orf" << "pef" << "pxn" << "raf" << "raw" << "rdc" << "sr2" << "srf" << "x3f" << "arw" << "3fr" << "cine" << "ia" << "kc2" << "mef" << "nrw" << "qtk" << "rw2" << "sti" << "rwl" << "srw"; s_mimeDatabase << mimeType; mimeType.mimeType = "application/x-extension-exr"; mimeType.description = i18nc("description of a file type", "OpenEXR (Extended)"); mimeType.suffixes = QStringList() << "exr"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/x-psb"; mimeType.description = i18nc("description of a file type", "Photoshop Image (Large)"); mimeType.suffixes = QStringList() << "psb"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/heic"; mimeType.description = i18nc("description of a file type", "HEIC/HEIF Image"); mimeType.suffixes = QStringList() << "heic" << "heif"; s_mimeDatabase << mimeType; mimeType.mimeType = "image/jp2"; mimeType.description = i18nc("description of a file type", "JP2 Image"); mimeType.suffixes = QStringList() << "jp2" << "j2k"; s_mimeDatabase << mimeType; + mimeType.mimeType = "application/x-krita-seexpr-script"; + mimeType.description = i18nc("description of a file type", "SeExpr script package"); + mimeType.suffixes = QStringList() << "kse"; + s_mimeDatabase << mimeType; + debugPlugin << "Filled mimedatabase with" << s_mimeDatabase.count() << "special mimetypes"; } } diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index d273bf548a..e0db1e75ee 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -1,636 +1,642 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ${OCIO_INCLUDE_DIR} ) if (ANDROID) add_definitions(-DQT_OPENGL_ES_3) add_definitions(-DHAS_ONLY_OPENGL_ES) include_directories (${Qt5AndroidExtras_INCLUDE_DIRS}) endif() add_subdirectory( tests ) if (APPLE) find_library(FOUNDATION_LIBRARY Foundation) find_library(APPKIT_LIBRARY AppKit) endif () set(kritaui_LIB_SRCS canvas/kis_canvas_widget_base.cpp canvas/kis_canvas2.cpp canvas/kis_canvas_updates_compressor.cpp canvas/kis_canvas_controller.cpp canvas/kis_display_color_converter.cpp canvas/kis_display_filter.cpp canvas/kis_exposure_gamma_correction_interface.cpp canvas/kis_tool_proxy.cpp canvas/kis_canvas_decoration.cc canvas/kis_coordinates_converter.cpp canvas/kis_grid_manager.cpp canvas/kis_grid_decoration.cpp canvas/kis_grid_config.cpp canvas/kis_prescaled_projection.cpp canvas/kis_qpainter_canvas.cpp canvas/kis_projection_backend.cpp canvas/kis_update_info.cpp canvas/kis_image_patch.cpp canvas/kis_image_pyramid.cpp canvas/kis_infinity_manager.cpp canvas/kis_change_guides_command.cpp canvas/kis_guides_decoration.cpp canvas/kis_guides_manager.cpp canvas/kis_guides_config.cpp canvas/kis_snap_config.cpp canvas/kis_snap_line_strategy.cpp canvas/KisSnapPointStrategy.cpp canvas/KisSnapPixelStrategy.cpp canvas/KisMirrorAxisConfig.cpp dialogs/kis_about_application.cpp dialogs/kis_dlg_adj_layer_props.cc dialogs/kis_dlg_adjustment_layer.cc dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_generator_layer.cpp dialogs/kis_dlg_file_layer.cpp dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_stroke_selection_properties.cpp dialogs/kis_dlg_image_properties.cc dialogs/kis_dlg_layer_properties.cc dialogs/kis_dlg_preferences.cc dialogs/slider_and_spin_box_sync.cpp dialogs/kis_dlg_blacklist_cleanup.cpp dialogs/kis_dlg_layer_style.cpp dialogs/kis_dlg_png_import.cpp dialogs/kis_dlg_import_image_sequence.cpp dialogs/kis_delayed_save_dialog.cpp dialogs/KisSessionManagerDialog.cpp dialogs/KisNewWindowLayoutDialog.cpp dialogs/KisDlgChangeCloneSource.cpp dialogs/KisRecoverNamedAutosaveDialog.cpp flake/kis_node_dummies_graph.cpp flake/kis_dummies_facade_base.cpp flake/kis_dummies_facade.cpp flake/kis_node_shapes_graph.cpp flake/kis_node_shape.cpp flake/kis_shape_controller.cpp flake/kis_shape_layer.cc flake/kis_shape_layer_canvas.cpp flake/kis_shape_selection.cpp flake/kis_shape_selection_canvas.cpp flake/kis_shape_selection_model.cpp flake/kis_take_all_shapes_command.cpp brushhud/kis_uniform_paintop_property_widget.cpp brushhud/kis_brush_hud.cpp brushhud/kis_round_hud_button.cpp brushhud/kis_dlg_brush_hud_config.cpp brushhud/kis_brush_hud_properties_list.cpp brushhud/kis_brush_hud_properties_config.cpp kis_aspect_ratio_locker.cpp kis_autogradient.cc kis_bookmarked_configurations_editor.cc kis_bookmarked_configurations_model.cc kis_bookmarked_filter_configurations_model.cc KisPaintopPropertiesBase.cpp kis_canvas_resource_provider.cpp kis_derived_resources.cpp kis_categories_mapper.cpp kis_categorized_list_model.cpp kis_categorized_item_delegate.cpp kis_clipboard.cc kis_config.cc KisOcioConfiguration.cpp kis_control_frame.cpp kis_composite_ops_model.cc kis_paint_ops_model.cpp kis_custom_pattern.cc kis_file_layer.cpp kis_change_file_layer_command.h kis_safe_document_loader.cpp kis_splash_screen.cpp kis_filter_manager.cc kis_filters_model.cc KisImageBarrierLockerWithFeedback.cpp kis_image_manager.cc kis_image_view_converter.cpp kis_import_catcher.cc kis_layer_manager.cc kis_mask_manager.cc kis_mimedata.cpp kis_node_commands_adapter.cpp kis_node_manager.cpp kis_node_juggler_compressed.cpp kis_node_selection_adapter.cpp kis_node_insertion_adapter.cpp KisNodeDisplayModeAdapter.cpp kis_node_model.cpp kis_node_filter_proxy_model.cpp kis_model_index_converter_base.cpp kis_model_index_converter.cpp kis_model_index_converter_show_all.cpp kis_painting_assistant.cc kis_painting_assistants_decoration.cpp KisDecorationsManager.cpp kis_paintop_box.cc kis_paintop_option.cpp kis_paintop_options_model.cpp kis_paintop_settings_widget.cpp kis_popup_palette.cpp kis_png_converter.cpp kis_preference_set_registry.cpp KisResourceServerProvider.cpp KisResourceBundleServerProvider.cpp KisSelectedShapesProxy.cpp kis_selection_decoration.cc kis_selection_manager.cc KisSelectionActionsAdapter.cpp kis_statusbar.cc kis_zoom_manager.cc kis_favorite_resource_manager.cpp kis_workspace_resource.cpp kis_action.cpp kis_action_manager.cpp KisActionPlugin.cpp kis_canvas_controls_manager.cpp kis_tooltip_manager.cpp kis_multinode_property.cpp kis_stopgradient_editor.cpp KisWelcomePageWidget.cpp KisChangeCloneLayersCommand.cpp kisexiv2/kis_exif_io.cpp kisexiv2/kis_exiv2.cpp kisexiv2/kis_iptc_io.cpp kisexiv2/kis_xmp_io.cpp opengl/kis_opengl.cpp opengl/kis_opengl_canvas2.cpp opengl/kis_opengl_canvas_debugger.cpp opengl/kis_opengl_image_textures.cpp opengl/kis_texture_tile.cpp opengl/kis_opengl_shader_loader.cpp opengl/kis_texture_tile_info_pool.cpp opengl/KisOpenGLUpdateInfoBuilder.cpp opengl/KisOpenGLModeProber.cpp opengl/KisScreenInformationAdapter.cpp kis_fps_decoration.cpp tool/KisToolChangesTracker.cpp tool/KisToolChangesTrackerData.cpp tool/kis_selection_tool_helper.cpp tool/kis_selection_tool_config_widget_helper.cpp tool/kis_rectangle_constraint_widget.cpp tool/kis_shape_tool_helper.cpp tool/kis_tool.cc tool/kis_delegated_tool_policies.cpp tool/kis_tool_freehand.cc tool/kis_speed_smoother.cpp tool/kis_painting_information_builder.cpp tool/kis_stabilized_events_sampler.cpp tool/kis_tool_freehand_helper.cpp tool/kis_tool_multihand_helper.cpp tool/kis_figure_painting_tool_helper.cpp tool/KisAsyncronousStrokeUpdateHelper.cpp tool/kis_tool_paint.cc tool/kis_tool_shape.cc tool/kis_tool_ellipse_base.cpp tool/kis_tool_rectangle_base.cpp tool/kis_tool_polyline_base.cpp tool/kis_tool_utils.cpp tool/kis_resources_snapshot.cpp tool/kis_smoothing_options.cpp tool/KisStabilizerDelayedPaintHelper.cpp tool/KisStrokeSpeedMonitor.cpp tool/strokes/freehand_stroke.cpp tool/strokes/KisStrokeEfficiencyMeasurer.cpp tool/strokes/kis_painter_based_stroke_strategy.cpp tool/strokes/kis_filter_stroke_strategy.cpp tool/strokes/kis_color_picker_stroke_strategy.cpp tool/strokes/KisFreehandStrokeInfo.cpp tool/strokes/KisMaskedFreehandStrokePainter.cpp tool/strokes/KisMaskingBrushRenderer.cpp tool/strokes/KisMaskingBrushCompositeOpFactory.cpp tool/strokes/move_stroke_strategy.cpp tool/strokes/KisNodeSelectionRecipe.cpp tool/KisSelectionToolFactoryBase.cpp tool/KisToolPaintFactoryBase.cpp widgets/kis_cmb_composite.cc widgets/kis_cmb_contour.cpp widgets/kis_cmb_gradient.cpp widgets/kis_paintop_list_widget.cpp widgets/kis_cmb_idlist.cc widgets/kis_color_space_selector.cc widgets/kis_advanced_color_space_selector.cc widgets/kis_cie_tongue_widget.cpp widgets/kis_tone_curve_widget.cpp widgets/kis_curve_widget.cpp widgets/kis_custom_image_widget.cc widgets/kis_image_from_clipboard_widget.cpp widgets/kis_double_widget.cc widgets/kis_filter_selector_widget.cc widgets/kis_gradient_chooser.cc widgets/kis_iconwidget.cc widgets/kis_mask_widgets.cpp widgets/kis_meta_data_merge_strategy_chooser_widget.cc widgets/kis_multi_bool_filter_widget.cc widgets/kis_multi_double_filter_widget.cc widgets/kis_multi_integer_filter_widget.cc widgets/kis_multipliers_double_slider_spinbox.cpp widgets/kis_paintop_presets_popup.cpp widgets/kis_tool_options_popup.cpp widgets/kis_paintop_presets_chooser_popup.cpp widgets/kis_paintop_presets_save.cpp widgets/kis_paintop_preset_icon_library.cpp widgets/kis_pattern_chooser.cc widgets/kis_preset_chooser.cpp widgets/kis_progress_widget.cpp widgets/kis_selection_options.cc widgets/kis_scratch_pad.cpp widgets/kis_scratch_pad_event_filter.cpp widgets/kis_preset_selector_strip.cpp widgets/KisSelectionPropertySlider.cpp widgets/kis_size_group.cpp widgets/kis_size_group_p.cpp widgets/kis_wdg_generator.cpp widgets/kis_workspace_chooser.cpp widgets/kis_categorized_list_view.cpp widgets/kis_widget_chooser.cpp widgets/kis_tool_button.cpp widgets/kis_floating_message.cpp widgets/kis_lod_availability_widget.cpp widgets/kis_color_label_selector_widget.cpp widgets/kis_color_filter_combo.cpp widgets/kis_elided_label.cpp widgets/kis_stopgradient_slider_widget.cpp widgets/kis_preset_live_preview_view.cpp widgets/KisScreenColorPicker.cpp widgets/KoDualColorButton.cpp widgets/KoStrokeConfigWidget.cpp widgets/KoFillConfigWidget.cpp widgets/KisLayerStyleAngleSelector.cpp widgets/KisMemoryReportButton.cpp widgets/KisDitherWidget.cpp KisPaletteEditor.cpp dialogs/KisDlgPaletteEditor.cpp widgets/KisNewsWidget.cpp widgets/KisGamutMaskToolbar.cpp utils/kis_document_aware_spin_box_unit_manager.cpp utils/KisSpinBoxSplineUnitConverter.cpp utils/KisClipboardUtil.cpp utils/KisDitherUtil.cpp utils/KisFileIconCreator.cpp input/kis_input_manager.cpp input/kis_input_manager_p.cpp input/kis_extended_modifiers_mapper.cpp input/kis_abstract_input_action.cpp input/kis_tool_invocation_action.cpp input/kis_pan_action.cpp input/kis_alternate_invocation_action.cpp input/kis_rotate_canvas_action.cpp input/kis_zoom_action.cpp input/kis_change_frame_action.cpp input/kis_gamma_exposure_action.cpp input/kis_show_palette_action.cpp input/kis_change_primary_setting_action.cpp input/kis_abstract_shortcut.cpp input/kis_native_gesture_shortcut.cpp input/kis_single_action_shortcut.cpp input/kis_stroke_shortcut.cpp input/kis_shortcut_matcher.cpp input/kis_select_layer_action.cpp input/KisQtWidgetsTweaker.cpp input/KisInputActionGroup.cpp input/kis_zoom_and_rotate_action.cpp operations/kis_operation.cpp operations/kis_operation_configuration.cpp operations/kis_operation_registry.cpp operations/kis_operation_ui_factory.cpp operations/kis_operation_ui_widget.cpp operations/kis_filter_selection_operation.cpp actions/kis_selection_action_factories.cpp actions/KisPasteActionFactories.cpp actions/KisTransformToolActivationCommand.cpp input/kis_touch_shortcut.cpp kis_document_undo_store.cpp kis_gui_context_command.cpp kis_gui_context_command_p.cpp input/kis_tablet_debugger.cpp input/kis_input_profile_manager.cpp input/kis_input_profile.cpp input/kis_shortcut_configuration.cpp input/config/kis_input_configuration_page.cpp input/config/kis_edit_profiles_dialog.cpp input/config/kis_input_profile_model.cpp input/config/kis_input_configuration_page_item.cpp input/config/kis_action_shortcuts_model.cpp input/config/kis_input_type_delegate.cpp input/config/kis_input_mode_delegate.cpp input/config/kis_input_button.cpp input/config/kis_input_editor_delegate.cpp input/config/kis_mouse_input_editor.cpp input/config/kis_wheel_input_editor.cpp input/config/kis_key_input_editor.cpp processing/fill_processing_visitor.cpp kis_asl_layer_style_serializer.cpp kis_psd_layer_style_resource.cpp canvas/kis_mirror_axis.cpp kis_abstract_perspective_grid.cpp KisApplication.cpp KisAutoSaveRecoveryDialog.cpp KisDetailsPane.cpp KisDocument.cpp KisCloneDocumentStroke.cpp kis_node_view_color_scheme.cpp KisImportExportFilter.cpp KisImportExportManager.cpp KisImportExportUtils.cpp kis_async_action_feedback.cpp KisMainWindow.cpp KisOpenPane.cpp KisPart.cpp KisPrintJob.cpp KisTemplate.cpp KisTemplateCreateDia.cpp KisTemplateGroup.cpp KisTemplates.cpp KisTemplatesPane.cpp KisTemplateTree.cpp KisUndoActionsUpdateManager.cpp KisView.cpp KisCanvasWindow.cpp KisImportExportErrorCode.cpp KisImportExportAdditionalChecks.cpp thememanager.cpp kis_mainwindow_observer.cpp KisViewManager.cpp kis_mirror_manager.cpp qtlockedfile/qtlockedfile.cpp qtsingleapplication/qtlocalpeer.cpp qtsingleapplication/qtsingleapplication.cpp KisResourceBundle.cpp KisResourceBundleManifest.cpp kis_md5_generator.cpp KisApplicationArguments.cpp KisNetworkAccessManager.cpp KisMultiFeedRSSModel.cpp KisRemoteFileFetcher.cpp KisSaveGroupVisitor.cpp KisWindowLayoutResource.cpp KisWindowLayoutManager.cpp KisSessionResource.cpp KisReferenceImagesDecoration.cpp KisReferenceImage.cpp flake/KisReferenceImagesLayer.cpp flake/KisReferenceImagesLayer.h KisMouseClickEater.cpp KisDecorationsWrapperLayer.cpp ) +if (SeExpr2_FOUND) + set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} + widgets/kis_seexpr_script_chooser.cpp + ) +endif() + if(WIN32) # Private headers are needed for: # * KisDlgCustomTabletResolution # * KisScreenInformationAdapter include_directories(SYSTEM ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} qtlockedfile/qtlockedfile_win.cpp ) if (NOT USE_QT_TABLET_WINDOWS) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/wintab/kis_tablet_support_win.cpp input/wintab/kis_screen_size_choice_dialog.cpp input/wintab/kis_tablet_support_win8.cpp ) else() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} dialogs/KisDlgCustomTabletResolution.cpp ) endif() endif() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} kis_animation_frame_cache.cpp kis_animation_cache_populator.cpp KisAsyncAnimationRendererBase.cpp KisAsyncAnimationCacheRenderer.cpp KisAsyncAnimationFramesSavingRenderer.cpp dialogs/KisAsyncAnimationRenderDialogBase.cpp dialogs/KisAsyncAnimationCacheRenderDialog.cpp dialogs/KisAsyncAnimationFramesSaveDialog.cpp canvas/kis_animation_player.cpp kis_animation_importer.cpp KisSyncedAudioPlayback.cpp KisFrameDataSerializer.cpp KisFrameCacheStore.cpp KisFrameCacheSwapper.cpp KisAbstractFrameCacheSwapper.cpp KisInMemoryFrameCacheSwapper.cpp input/wintab/drawpile_tablettester/tablettester.cpp input/wintab/drawpile_tablettester/tablettest.cpp ) if (UNIX) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} qtlockedfile/qtlockedfile_unix.cpp ) endif() if(APPLE) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/kis_extended_modifiers_mapper_osx.mm osx.mm ) endif() if (ANDROID) set (kritaui_LIB_SRCS ${kritaui_LIB_SRCS} KisAndroidFileManager.cpp) endif() ki18n_wrap_ui(kritaui_LIB_SRCS widgets/KoFillConfigWidget.ui widgets/KoStrokeConfigWidget.ui widgets/KisDitherWidget.ui forms/wdgdlgpngimport.ui forms/wdgfullscreensettings.ui forms/wdgautogradient.ui forms/wdggeneralsettings.ui forms/wdgperformancesettings.ui forms/wdggenerators.ui forms/wdgbookmarkedconfigurationseditor.ui forms/wdgapplyprofile.ui forms/wdgcustompattern.ui forms/wdglayerproperties.ui forms/wdgcolorsettings.ui forms/wdgtabletsettings.ui forms/wdgcolorspaceselector.ui forms/wdgcolorspaceselectoradvanced.ui forms/wdgdisplaysettings.ui forms/kis_previewwidgetbase.ui forms/kis_matrix_widget.ui forms/wdgselectionoptions.ui forms/wdggeometryoptions.ui forms/wdgnewimage.ui forms/wdgimageproperties.ui forms/wdgmaskfromselection.ui forms/wdgmasksource.ui forms/wdgfilterdialog.ui forms/wdgmetadatamergestrategychooser.ui forms/wdgpaintoppresets.ui forms/wdgpaintopsettings.ui forms/wdgdlggeneratorlayer.ui forms/wdgdlgfilelayer.ui forms/wdgfilterselector.ui forms/wdgfilternodecreation.ui forms/wdgmultipliersdoublesliderspinbox.ui forms/wdgnodequerypatheditor.ui forms/wdgpresetselectorstrip.ui forms/wdgsavebrushpreset.ui forms/wdgpreseticonlibrary.ui forms/wdgdlgblacklistcleanup.ui forms/wdgrectangleconstraints.ui forms/wdgimportimagesequence.ui forms/wdgstrokeselectionproperties.ui forms/KisDetailsPaneBase.ui forms/KisOpenPaneBase.ui forms/wdgstopgradienteditor.ui forms/wdgsessionmanager.ui forms/wdgnewwindowlayout.ui forms/KisWelcomePage.ui forms/WdgDlgPaletteEditor.ui forms/KisNewsPage.ui forms/wdgGamutMaskToolbar.ui forms/wdgchangeclonesource.ui brushhud/kis_dlg_brush_hud_config.ui dialogs/kis_delayed_save_dialog.ui dialogs/KisRecoverNamedAutosaveDialog.ui input/config/kis_input_configuration_page.ui input/config/kis_edit_profiles_dialog.ui input/config/kis_input_configuration_page_item.ui input/config/kis_mouse_input_editor.ui input/config/kis_wheel_input_editor.ui input/config/kis_key_input_editor.ui layerstyles/wdgBevelAndEmboss.ui layerstyles/wdgblendingoptions.ui layerstyles/WdgColorOverlay.ui layerstyles/wdgContour.ui layerstyles/wdgdropshadow.ui layerstyles/WdgGradientOverlay.ui layerstyles/wdgInnerGlow.ui layerstyles/wdglayerstyles.ui layerstyles/WdgPatternOverlay.ui layerstyles/WdgSatin.ui layerstyles/WdgStroke.ui layerstyles/wdgstylesselector.ui layerstyles/wdgTexture.ui layerstyles/wdgKisLayerStyleAngleSelector.ui wdgsplash.ui input/wintab/kis_screen_size_choice_dialog.ui input/wintab/drawpile_tablettester/tablettest.ui ) if(WIN32) if(USE_QT_TABLET_WINDOWS) ki18n_wrap_ui(kritaui_LIB_SRCS dialogs/KisDlgCustomTabletResolution.ui ) else() ki18n_wrap_ui(kritaui_LIB_SRCS input/wintab/kis_screen_size_choice_dialog.ui ) endif() endif() add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} ) generate_export_header(kritaui BASE_NAME kritaui) target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} LibExiv2::LibExiv2 ) if (ANDROID) target_link_libraries(kritaui GLESv3) target_link_libraries(kritaui Qt5::Gui) target_link_libraries(kritaui Qt5::AndroidExtras) endif() if (HAVE_QT_MULTIMEDIA) target_link_libraries(kritaui Qt5::Multimedia) endif() if (NOT WIN32 AND NOT APPLE AND NOT ANDROID) target_link_libraries(kritaui ${X11_X11_LIB} ${X11_Xinput_LIB}) endif() if(APPLE) target_link_libraries(kritaui ${FOUNDATION_LIBRARY}) target_link_libraries(kritaui ${APPKIT_LIBRARY}) endif () target_link_libraries(kritaui ${OPENEXR_LIBRARIES}) # Add VSync disable workaround if(NOT WIN32 AND NOT APPLE AND NOT ANDROID) target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras) endif() if(X11_FOUND) target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES}) endif() target_include_directories(kritaui PUBLIC $ $ $ $ $ $ $ ) set_target_properties(kritaui PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS}) if (APPLE) install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita) endif () diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp index e269e61ac6..0da4572978 100644 --- a/libs/ui/KisApplication.cpp +++ b/libs/ui/KisApplication.cpp @@ -1,972 +1,980 @@ /* * Copyright (C) 1998, 1999 Torben Weis * Copyright (C) 2012 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisApplication.h" #include #ifdef Q_OS_WIN #include #include #endif #ifdef Q_OS_MACOS #include "osx.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoConfig.h" #include #include #include #include "thememanager.h" #include "KisPrintJob.h" #include "KisDocument.h" #include "KisMainWindow.h" #include "KisAutoSaveRecoveryDialog.h" #include "KisPart.h" #include #include "kis_md5_generator.h" #include "kis_splash_screen.h" #include "kis_config.h" #include "flake/kis_shape_selection.h" #include #include #include #include #include #include #include #include "kisexiv2/kis_exiv2.h" #include "KisApplicationArguments.h" #include #include "kis_action_registry.h" #include #include #include #include "kis_image_barrier_locker.h" #include "opengl/kis_opengl.h" #include "kis_spin_box_unit_manager.h" #include "kis_document_aware_spin_box_unit_manager.h" #include "KisViewManager.h" #include "kis_workspace_resource.h" #include #include #include #include "widgets/KisScreenColorPicker.h" #include "KisDlgInternalColorSelector.h" #include #include #include "kis_file_layer.h" #include "kis_group_layer.h" #include "kis_node_commands_adapter.h" +#include + namespace { const QTime appStartTime(QTime::currentTime()); } class KisApplication::Private { public: Private() {} QPointer splashScreen; KisAutoSaveRecoveryDialog *autosaveDialog {0}; QPointer mainWindow; // The first mainwindow we create on startup bool batchRun {false}; QVector earlyRemoteArguments; }; class KisApplication::ResetStarting { public: ResetStarting(KisSplashScreen *splash, int fileCount) : m_splash(splash) , m_fileCount(fileCount) { } ~ResetStarting() { if (m_splash) { m_splash->hide(); } } QPointer m_splash; int m_fileCount; }; KisApplication::KisApplication(const QString &key, int &argc, char **argv) : QtSingleApplication(key, argc, argv) , d(new Private) { #ifdef Q_OS_MACOS setMouseCoalescingEnabled(false); #endif QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); setApplicationDisplayName("Krita"); setApplicationName("krita"); // Note: Qt docs suggest we set this, but if we do, we get resource paths of the form of krita/krita, which is weird. // setOrganizationName("krita"); setOrganizationDomain("krita.org"); QString version = KritaVersionWrapper::versionString(true); setApplicationVersion(version); setWindowIcon(KisIconUtils::loadIcon("krita")); if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) { QStringList styles = QStringList() << "breeze" << "fusion" << "plastique"; if (!styles.contains(style()->objectName().toLower())) { Q_FOREACH (const QString & style, styles) { if (!setStyle(style)) { qDebug() << "No" << style << "available."; } else { qDebug() << "Set style" << style; break; } } } } else { qDebug() << "Style override disabled, using" << style()->objectName(); } } #if defined(Q_OS_WIN) && defined(ENV32BIT) typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process; BOOL isWow64() { BOOL bIsWow64 = FALSE; //IsWow64Process is not available on all supported versions of Windows. //Use GetModuleHandle to get a handle to the DLL that contains the function //and GetProcAddress to get a pointer to the function if available. fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress( GetModuleHandle(TEXT("kernel32")),"IsWow64Process"); if(0 != fnIsWow64Process) { if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64)) { //handle error } } return bIsWow64; } #endif void KisApplication::initializeGlobals(const KisApplicationArguments &args) { int dpiX = args.dpiX(); int dpiY = args.dpiY(); if (dpiX > 0 && dpiY > 0) { KoDpi::setDPI(dpiX, dpiY); } } void KisApplication::addResourceTypes() { // qDebug() << "addResourceTypes();"; // All Krita's resource types KoResourcePaths::addResourceType("markers", "data", "/styles/"); KoResourcePaths::addResourceType("kis_pics", "data", "/pics/"); KoResourcePaths::addResourceType("kis_images", "data", "/images/"); KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/"); KoResourcePaths::addResourceType("kis_brushes", "data", "/brushes/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/"); KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/"); KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/"); KoResourcePaths::addResourceType("kis_paintoppresets", "data", "/paintoppresets/"); KoResourcePaths::addResourceType("kis_workspaces", "data", "/workspaces/"); KoResourcePaths::addResourceType("kis_windowlayouts", "data", "/windowlayouts/"); KoResourcePaths::addResourceType("kis_sessions", "data", "/sessions/"); KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl"); KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/", true); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/"); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/", true); KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/", true); KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/"); KoResourcePaths::addResourceType("kis_actions", "data", "/actions"); KoResourcePaths::addResourceType("kis_actions", "data", "/pykrita"); KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc"); KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/"); KoResourcePaths::addResourceType("ko_effects", "data", "/effects/"); KoResourcePaths::addResourceType("tags", "data", "/tags/"); KoResourcePaths::addResourceType("templates", "data", "/templates"); KoResourcePaths::addResourceType("pythonscripts", "data", "/pykrita"); KoResourcePaths::addResourceType("symbols", "data", "/symbols"); KoResourcePaths::addResourceType("preset_icons", "data", "/preset_icons"); KoResourcePaths::addResourceType("ko_gamutmasks", "data", "/gamutmasks/", true); +#if defined HAVE_SEEXPR + KoResourcePaths::addResourceType("kis_seexpr_scripts", "data", "/seexpr_scripts/", true); +#endif // // Extra directories to look for create resources. (Does anyone actually use that anymore?) // KoResourcePaths::addResourceDir("ko_gradients", "/usr/share/create/gradients/gimp"); // KoResourcePaths::addResourceDir("ko_gradients", QDir::homePath() + QString("/.create/gradients/gimp")); // KoResourcePaths::addResourceDir("ko_patterns", "/usr/share/create/patterns/gimp"); // KoResourcePaths::addResourceDir("ko_patterns", QDir::homePath() + QString("/.create/patterns/gimp")); // KoResourcePaths::addResourceDir("kis_brushes", "/usr/share/create/brushes/gimp"); // KoResourcePaths::addResourceDir("kis_brushes", QDir::homePath() + QString("/.create/brushes/gimp")); // KoResourcePaths::addResourceDir("ko_palettes", "/usr/share/create/swatches"); // KoResourcePaths::addResourceDir("ko_palettes", QDir::homePath() + QString("/.create/swatches")); // Make directories for all resources we can save, and tags QDir d; d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tags/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/asl/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/brushes/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gradients/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/paintoppresets/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/palettes/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patterns/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/workspaces/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/input/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/pykrita/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/symbols/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/color-schemes/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/tool_icons/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/emblem_icons/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gamutmasks/"); +#if defined HAVE_SEEXPR + d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/seexpr_scripts/"); +#endif } void KisApplication::loadResources() { // qDebug() << "loadResources();"; setSplashScreenLoadingText(i18n("Loading Resources...")); processEvents(); KoResourceServerProvider::instance(); setSplashScreenLoadingText(i18n("Loading Brush Presets...")); processEvents(); KisResourceServerProvider::instance(); setSplashScreenLoadingText(i18n("Loading Brushes...")); processEvents(); KisBrushServer::instance()->brushServer(); setSplashScreenLoadingText(i18n("Loading Bundles...")); processEvents(); KisResourceBundleServerProvider::instance(); } void KisApplication::loadResourceTags() { // qDebug() << "loadResourceTags()"; KoResourceServerProvider::instance()->patternServer()->loadTags(); KoResourceServerProvider::instance()->gradientServer()->loadTags(); KoResourceServerProvider::instance()->paletteServer()->loadTags(); KoResourceServerProvider::instance()->svgSymbolCollectionServer()->loadTags(); KisBrushServer::instance()->brushServer()->loadTags(); KisResourceServerProvider::instance()->workspaceServer()->loadTags(); KisResourceServerProvider::instance()->layerStyleCollectionServer()->loadTags(); KisResourceBundleServerProvider::instance()->resourceBundleServer()->loadTags(); KisResourceServerProvider::instance()->paintOpPresetServer()->loadTags(); KisResourceServerProvider::instance()->paintOpPresetServer()->clearOldSystemTags(); } void KisApplication::loadPlugins() { // qDebug() << "loadPlugins();"; KoShapeRegistry* r = KoShapeRegistry::instance(); r->add(new KisShapeSelectionFactory()); KoColorSpaceRegistry::instance(); KisActionRegistry::instance(); KisFilterRegistry::instance(); KisGeneratorRegistry::instance(); KisPaintOpRegistry::instance(); KoToolRegistry::instance(); KoDockRegistry::instance(); } void KisApplication::loadGuiPlugins() { // XXX_EXIV: make the exiv io backends real plugins setSplashScreenLoadingText(i18n("Loading Plugins Exiv/IO...")); processEvents(); // qDebug() << "loading exiv2"; KisExiv2::initialize(); } bool KisApplication::start(const KisApplicationArguments &args) { KisConfig cfg(false); #if defined(Q_OS_WIN) #ifdef ENV32BIT if (isWow64() && !cfg.readEntry("WarnedAbout32Bits", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You are running a 32 bits build on a 64 bits Windows.\n" "This is not recommended.\n" "Please download and install the x64 build instead.")); cfg.writeEntry("WarnedAbout32Bits", true); } #endif #endif QString opengl = cfg.canvasState(); if (opengl == "OPENGL_NOT_TRIED" ) { cfg.setCanvasState("TRY_OPENGL"); } else if (opengl != "OPENGL_SUCCESS" && opengl != "TRY_OPENGL") { cfg.setCanvasState("OPENGL_FAILED"); } setSplashScreenLoadingText(i18n("Initializing Globals")); processEvents(); initializeGlobals(args); const bool doNewImage = args.doNewImage(); const bool doTemplate = args.doTemplate(); const bool exportAs = args.exportAs(); const bool exportSequence = args.exportSequence(); const QString exportFileName = args.exportFileName(); d->batchRun = (exportAs || exportSequence || !exportFileName.isEmpty()); const bool needsMainWindow = (!exportAs && !exportSequence); // only show the mainWindow when no command-line mode option is passed bool showmainWindow = (!exportAs && !exportSequence); // would be !batchRun; const bool showSplashScreen = !d->batchRun && qEnvironmentVariableIsEmpty("NOSPLASH"); if (showSplashScreen && d->splashScreen) { d->splashScreen->show(); d->splashScreen->repaint(); processEvents(); } KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator()); KConfigGroup group(KSharedConfig::openConfig(), "theme"); Digikam::ThemeManager themeManager; themeManager.setCurrentTheme(group.readEntry("Theme", "Krita dark")); ResetStarting resetStarting(d->splashScreen, args.filenames().count()); // remove the splash when done Q_UNUSED(resetStarting); // Make sure we can save resources and tags setSplashScreenLoadingText(i18n("Adding resource types")); processEvents(); addResourceTypes(); // Load the plugins loadPlugins(); // Load all resources loadResources(); // Load all the tags loadResourceTags(); // Load the gui plugins loadGuiPlugins(); KisPart *kisPart = KisPart::instance(); if (needsMainWindow) { // show a mainWindow asap, if we want that setSplashScreenLoadingText(i18n("Loading Main Window...")); processEvents(); bool sessionNeeded = true; auto sessionMode = cfg.sessionOnStartup(); if (!args.session().isEmpty()) { sessionNeeded = !kisPart->restoreSession(args.session()); } else if (sessionMode == KisConfig::SOS_ShowSessionManager) { showmainWindow = false; sessionNeeded = false; kisPart->showSessionManager(); } else if (sessionMode == KisConfig::SOS_PreviousSession) { KConfigGroup sessionCfg = KSharedConfig::openConfig()->group("session"); const QString &sessionName = sessionCfg.readEntry("previousSession"); sessionNeeded = !kisPart->restoreSession(sessionName); } if (sessionNeeded) { kisPart->startBlankSession(); } if (!args.windowLayout().isEmpty()) { KoResourceServer * rserver = KisResourceServerProvider::instance()->windowLayoutServer(); KisWindowLayoutResource* windowLayout = rserver->resourceByName(args.windowLayout()); if (windowLayout) { windowLayout->applyLayout(); } } if (showmainWindow) { d->mainWindow = kisPart->currentMainwindow(); if (!args.workspace().isEmpty()) { KoResourceServer * rserver = KisResourceServerProvider::instance()->workspaceServer(); KisWorkspaceResource* workspace = rserver->resourceByName(args.workspace()); if (workspace) { d->mainWindow->restoreWorkspace(workspace); } } if (args.canvasOnly()) { d->mainWindow->viewManager()->switchCanvasOnly(true); } if (args.fullScreen()) { d->mainWindow->showFullScreen(); } } else { d->mainWindow = kisPart->createMainWindow(); } } short int numberOfOpenDocuments = 0; // number of documents open // Check for autosave files that can be restored, if we're not running a batchrun (test) if (!d->batchRun) { checkAutosaveFiles(); } setSplashScreenLoadingText(QString()); // done loading, so clear out label processEvents(); //configure the unit manager KisSpinBoxUnitManagerFactory::setDefaultUnitManagerBuilder(new KisDocumentAwareSpinBoxUnitManagerBuilder()); connect(this, &KisApplication::aboutToQuit, &KisSpinBoxUnitManagerFactory::clearUnitManagerBuilder); //ensure the builder is destroyed when the application leave. //the new syntax slot syntax allow to connect to a non q_object static method. // Create a new image, if needed if (doNewImage) { KisDocument *doc = args.createDocumentFromArguments(); if (doc) { kisPart->addDocument(doc); d->mainWindow->addViewAndNotifyLoadingCompleted(doc); } } // Get the command line arguments which we have to parse int argsCount = args.filenames().count(); if (argsCount > 0) { // Loop through arguments for (int argNumber = 0; argNumber < argsCount; argNumber++) { QString fileName = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { // called in mix with batch options? ignore and silently skip if (d->batchRun) { continue; } if (createNewDocFromTemplate(fileName, d->mainWindow)) { ++numberOfOpenDocuments; } // now try to load } else { if (exportAs) { QString outputMimetype = KisMimeDatabase::mimeTypeForFile(exportFileName, false); if (outputMimetype == "application/octetstream") { dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl; return false; } KisDocument *doc = kisPart->createDocument(); doc->setFileBatchMode(d->batchRun); bool result = doc->openUrl(QUrl::fromLocalFile(fileName)); if (!result) { errKrita << "Could not load " << fileName << ":" << doc->errorMessage(); QTimer::singleShot(0, this, SLOT(quit())); return false; } if (exportFileName.isEmpty()) { errKrita << "Export destination is not specified for" << fileName << "Please specify export destination with --export-filename option"; QTimer::singleShot(0, this, SLOT(quit())); return false; } qApp->processEvents(); // For vector layers to be updated doc->setFileBatchMode(true); if (!doc->exportDocumentSync(QUrl::fromLocalFile(exportFileName), outputMimetype.toLatin1())) { errKrita << "Could not export " << fileName << "to" << exportFileName << ":" << doc->errorMessage(); } QTimer::singleShot(0, this, SLOT(quit())); return true; } else if (exportSequence) { KisDocument *doc = kisPart->createDocument(); doc->setFileBatchMode(d->batchRun); doc->openUrl(QUrl::fromLocalFile(fileName)); qApp->processEvents(); // For vector layers to be updated if (!doc->image()->animationInterface()->hasAnimation()) { errKrita << "This file has no animation." << endl; QTimer::singleShot(0, this, SLOT(quit())); return false; } doc->setFileBatchMode(true); int sequenceStart = 0; KisAsyncAnimationFramesSaveDialog exporter(doc->image(), doc->image()->animationInterface()->fullClipRange(), exportFileName, sequenceStart, false, 0); exporter.setBatchMode(d->batchRun); KisAsyncAnimationFramesSaveDialog::Result result = exporter.regenerateRange(0); if (result == KisAsyncAnimationFramesSaveDialog::RenderFailed) { errKrita << i18n("Failed to render animation frames!") << endl; } QTimer::singleShot(0, this, SLOT(quit())); return true; } else if (d->mainWindow) { if (fileName.endsWith(".bundle")) { d->mainWindow->installBundle(fileName); } else { KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; if (d->mainWindow->openDocument(QUrl::fromLocalFile(fileName), flags)) { // Normal case, success numberOfOpenDocuments++; } } } } } } //add an image as file-layer if (!args.fileLayer().isEmpty()){ if (d->mainWindow->viewManager()->image()){ KisFileLayer *fileLayer = new KisFileLayer(d->mainWindow->viewManager()->image(), "", args.fileLayer(), KisFileLayer::None, d->mainWindow->viewManager()->image()->nextLayerName(), OPACITY_OPAQUE_U8); QFileInfo fi(fileLayer->path()); if (fi.exists()){ KisNodeCommandsAdapter adapter(d->mainWindow->viewManager()); adapter.addNode(fileLayer, d->mainWindow->viewManager()->activeNode()->parent(), d->mainWindow->viewManager()->activeNode()); } else{ QMessageBox::warning(nullptr, i18nc("@title:window", "Krita:Warning"), i18n("Cannot add %1 as a file layer: the file does not exist.", fileLayer->path())); } } else if (this->isRunning()){ QMessageBox::warning(nullptr, i18nc("@title:window", "Krita:Warning"), i18n("Cannot add the file layer: no document is open.\n\n" "You can create a new document using the --new-image option, or you can open an existing file.\n\n" "If you instead want to add the file layer to a document in an already running instance of Krita, check the \"Allow only one instance of Krita\" checkbox in the settings (Settings -> General -> Window).")); } else { QMessageBox::warning(nullptr, i18nc("@title:window", "Krita: Warning"), i18n("Cannot add the file layer: no document is open.\n" "You can either create a new file using the --new-image option, or you can open an existing file.")); } } // fixes BUG:369308 - Krita crashing on splash screen when loading. // trying to open a file before Krita has loaded can cause it to hang and crash if (d->splashScreen) { d->splashScreen->displayLinks(true); d->splashScreen->displayRecentFiles(true); } Q_FOREACH(const QByteArray &message, d->earlyRemoteArguments) { executeRemoteArguments(message, d->mainWindow); } KisUsageLogger::writeSysInfo(KisUsageLogger::screenInformation()); // not calling this before since the program will quit there. return true; } KisApplication::~KisApplication() { } void KisApplication::setSplashScreen(QWidget *splashScreen) { d->splashScreen = qobject_cast(splashScreen); } void KisApplication::setSplashScreenLoadingText(QString textToLoad) { if (d->splashScreen) { //d->splashScreen->loadingLabel->setText(textToLoad); d->splashScreen->setLoadingText(textToLoad); d->splashScreen->repaint(); } } void KisApplication::hideSplashScreen() { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } } bool KisApplication::notify(QObject *receiver, QEvent *event) { try { return QApplication::notify(receiver, event); } catch (std::exception &e) { qWarning("Error %s sending event %i to object %s", e.what(), event->type(), qPrintable(receiver->objectName())); } catch (...) { qWarning("Error sending event %i to object %s", event->type(), qPrintable(receiver->objectName())); } return false; } void KisApplication::executeRemoteArguments(QByteArray message, KisMainWindow *mainWindow) { KisApplicationArguments args = KisApplicationArguments::deserialize(message); const bool doTemplate = args.doTemplate(); const bool doNewImage = args.doNewImage(); const int argsCount = args.filenames().count(); bool documentCreated = false; // Create a new image, if needed if (doNewImage) { KisDocument *doc = args.createDocumentFromArguments(); if (doc) { KisPart::instance()->addDocument(doc); d->mainWindow->addViewAndNotifyLoadingCompleted(doc); } } if (argsCount > 0) { // Loop through arguments for (int argNumber = 0; argNumber < argsCount; ++argNumber) { QString filename = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { documentCreated |= createNewDocFromTemplate(filename, mainWindow); } else if (QFile(filename).exists()) { KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; documentCreated |= mainWindow->openDocument(QUrl::fromLocalFile(filename), flags); } } } //add an image as file-layer if called in another process and singleApplication is enabled if (!args.fileLayer().isEmpty()){ if (argsCount > 0 && !documentCreated){ //arg was passed but document was not created so don't add the file layer. QMessageBox::warning(mainWindow, i18nc("@title:window", "Krita:Warning"), i18n("Couldn't open file %1",args.filenames().at(argsCount - 1))); } else if (mainWindow->viewManager()->image()){ KisFileLayer *fileLayer = new KisFileLayer(mainWindow->viewManager()->image(), "", args.fileLayer(), KisFileLayer::None, mainWindow->viewManager()->image()->nextLayerName(), OPACITY_OPAQUE_U8); QFileInfo fi(fileLayer->path()); if (fi.exists()){ KisNodeCommandsAdapter adapter(d->mainWindow->viewManager()); adapter.addNode(fileLayer, d->mainWindow->viewManager()->activeNode()->parent(), d->mainWindow->viewManager()->activeNode()); } else{ QMessageBox::warning(mainWindow, i18nc("@title:window", "Krita:Warning"), i18n("Cannot add %1 as a file layer: the file does not exist.", fileLayer->path())); } } else { QMessageBox::warning(mainWindow, i18nc("@title:window", "Krita:Warning"), i18n("Cannot add the file layer: no document is open.")); } } } void KisApplication::remoteArguments(QByteArray message, QObject *socket) { Q_UNUSED(socket); // check if we have any mainwindow KisMainWindow *mw = qobject_cast(qApp->activeWindow()); if (!mw && KisPart::instance()->mainWindows().size() > 0) { mw = KisPart::instance()->mainWindows().first(); } if (!mw) { d->earlyRemoteArguments << message; return; } executeRemoteArguments(message, mw); } void KisApplication::fileOpenRequested(const QString &url) { KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first(); if (mainWindow) { KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; mainWindow->openDocument(QUrl::fromLocalFile(url), flags); } } void KisApplication::checkAutosaveFiles() { if (d->batchRun) return; #ifdef Q_OS_WIN QDir dir = QDir::temp(); #else QDir dir = QDir::home(); #endif // Check for autosave files from a previous run. There can be several, and // we want to offer a restore for every one. Including a nice thumbnail! // Hidden autosave files QStringList filters = QStringList() << QString(".krita-*-*-autosave.kra"); // all autosave files for our application QStringList autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden); // Visible autosave files filters = QStringList() << QString("krita-*-*-autosave.kra"); autosaveFiles += dir.entryList(filters, QDir::Files); // Allow the user to make their selection if (autosaveFiles.size() > 0) { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } d->autosaveDialog = new KisAutoSaveRecoveryDialog(autosaveFiles, activeWindow()); QDialog::DialogCode result = (QDialog::DialogCode) d->autosaveDialog->exec(); if (result == QDialog::Accepted) { QStringList filesToRecover = d->autosaveDialog->recoverableFiles(); Q_FOREACH (const QString &autosaveFile, autosaveFiles) { if (!filesToRecover.contains(autosaveFile)) { KisUsageLogger::log(QString("Removing autosave file %1").arg(dir.absolutePath() + "/" + autosaveFile)); QFile::remove(dir.absolutePath() + "/" + autosaveFile); } } autosaveFiles = filesToRecover; } else { autosaveFiles.clear(); } if (autosaveFiles.size() > 0) { QList autosaveUrls; Q_FOREACH (const QString &autoSaveFile, autosaveFiles) { const QUrl url = QUrl::fromLocalFile(dir.absolutePath() + QLatin1Char('/') + autoSaveFile); autosaveUrls << url; } if (d->mainWindow) { Q_FOREACH (const QUrl &url, autosaveUrls) { KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; d->mainWindow->openDocument(url, flags | KisMainWindow::RecoveryFile); } } } // cleanup delete d->autosaveDialog; d->autosaveDialog = nullptr; } } bool KisApplication::createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow) { QString templatePath; const QUrl templateUrl = QUrl::fromLocalFile(fileName); if (QFile::exists(fileName)) { templatePath = templateUrl.toLocalFile(); dbgUI << "using full path..."; } else { QString desktopName(fileName); const QString templatesResourcePath = QStringLiteral("templates/"); QStringList paths = KoResourcePaths::findAllResources("data", templatesResourcePath + "*/" + desktopName); if (paths.isEmpty()) { paths = KoResourcePaths::findAllResources("data", templatesResourcePath + desktopName); } if (paths.isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("No template found for: %1", desktopName)); } else if (paths.count() > 1) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Too many templates found for: %1", desktopName)); } else { templatePath = paths.at(0); } } if (!templatePath.isEmpty()) { QUrl templateBase; templateBase.setPath(templatePath); KDesktopFile templateInfo(templatePath); QString templateName = templateInfo.readUrl(); QUrl templateURL; templateURL.setPath(templateBase.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() + '/' + templateName); if (templateURL.scheme().isEmpty()) { templateURL.setScheme("file"); } KisMainWindow::OpenFlags batchFlags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; if (mainWindow->openDocument(templateURL, KisMainWindow::Import | batchFlags)) { dbgUI << "Template loaded..."; return true; } else { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Template %1 failed to load.", templateURL.toDisplayString())); } } return false; } void KisApplication::resetConfig() { KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread()); KSharedConfigPtr config = KSharedConfig::openConfig(); config->markAsClean(); // find user settings file const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QString kritarcPath = configPath + QStringLiteral("/kritarc"); QFile kritarcFile(kritarcPath); if (kritarcFile.exists()) { if (kritarcFile.open(QFile::ReadWrite)) { QString backupKritarcPath = kritarcPath + QStringLiteral(".backup"); QFile backupKritarcFile(backupKritarcPath); if (backupKritarcFile.exists()) { backupKritarcFile.remove(); } QMessageBox::information(0, i18nc("@title:window", "Krita"), i18n("Krita configurations reset!\n\n" "Backup file was created at: %1\n\n" "Restart Krita for changes to take effect.", backupKritarcPath), QMessageBox::Ok, QMessageBox::Ok); // clear file kritarcFile.rename(backupKritarcPath); kritarcFile.close(); } else { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Failed to clear %1\n\n" "Please make sure no other program is using the file and try again.", kritarcPath), QMessageBox::Ok, QMessageBox::Ok); } } // reload from disk; with the user file settings cleared, // this should load any default configuration files shipping with the program config->reparseConfiguration(); config->sync(); // Restore to default workspace KConfigGroup cfg = KSharedConfig::openConfig()->group("MainWindow"); QString currentWorkspace = cfg.readEntry("CurrentWorkspace", "Default"); KoResourceServer *rserver = KisResourceServerProvider::instance()->workspaceServer(); KisWorkspaceResource *workspace = rserver->resourceByName(currentWorkspace); if (workspace) { d->mainWindow->restoreWorkspace(workspace); } } void KisApplication::askresetConfig() { bool ok = QMessageBox::question(0, i18nc("@title:window", "Krita"), i18n("Do you want to clear the settings file?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes; if (ok) { resetConfig(); } } diff --git a/libs/ui/KisResourceBundle.cpp b/libs/ui/KisResourceBundle.cpp index fb4bc40cc3..ac3accd8a7 100644 --- a/libs/ui/KisResourceBundle.cpp +++ b/libs/ui/KisResourceBundle.cpp @@ -1,1098 +1,1200 @@ /* * Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisResourceBundle.h" #include "KisResourceBundleManifest.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisResourceBundle::KisResourceBundle(QString const& fileName) : KoResource(fileName), m_bundleVersion("1") { setName(QFileInfo(fileName).completeBaseName()); m_metadata["generator"] = "Krita (" + KritaVersionWrapper::versionString(true) + ")"; } KisResourceBundle::~KisResourceBundle() { } QString KisResourceBundle::defaultFileExtension() const { return QString(".bundle"); } bool KisResourceBundle::load() { if (filename().isEmpty()) return false; QScopedPointer resourceStore(KoStore::createStore(filename(), KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip)); if (!resourceStore || resourceStore->bad()) { warnKrita << "Could not open store on bundle" << filename(); m_installed = false; setValid(false); return false; } else { m_metadata.clear(); bool toRecreate = false; if (resourceStore->open("META-INF/manifest.xml")) { if (!m_manifest.load(resourceStore->device())) { warnKrita << "Could not open manifest for bundle" << filename(); return false; } resourceStore->close(); Q_FOREACH (KisResourceBundleManifest::ResourceReference ref, m_manifest.files()) { if (!resourceStore->open(ref.resourcePath)) { warnKrita << "Bundle is broken. File" << ref.resourcePath << "is missing"; toRecreate = true; } else { resourceStore->close(); } } if(toRecreate) { warnKrita << "Due to missing files and wrong entries in the manifest, " << filename() << " will be recreated."; } } else { warnKrita << "Could not load META-INF/manifest.xml"; return false; } bool versionFound = false; if (resourceStore->open("meta.xml")) { KoXmlDocument doc; if (!doc.setContent(resourceStore->device())) { warnKrita << "Could not parse meta.xml for" << filename(); return false; } // First find the manifest:manifest node. KoXmlNode n = doc.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { if (!n.isElement()) { continue; } if (n.toElement().tagName() == "meta:meta") { break; } } if (n.isNull()) { warnKrita << "Could not find manifest node for bundle" << filename(); return false; } const KoXmlElement metaElement = n.toElement(); for (n = metaElement.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.isElement()) { KoXmlElement e = n.toElement(); if (e.tagName() == "meta:generator") { m_metadata.insert("generator", e.firstChild().toText().data()); } else if (e.tagName() == "dc:author") { m_metadata.insert("author", e.firstChild().toText().data()); } else if (e.tagName() == "dc:title") { m_metadata.insert("title", e.firstChild().toText().data()); } else if (e.tagName() == "dc:description") { m_metadata.insert("description", e.firstChild().toText().data()); } else if (e.tagName() == "meta:initial-creator") { m_metadata.insert("author", e.firstChild().toText().data()); } else if (e.tagName() == "dc:creator") { m_metadata.insert("author", e.firstChild().toText().data()); } else if (e.tagName() == "meta:creation-date") { m_metadata.insert("created", e.firstChild().toText().data()); } else if (e.tagName() == "meta:dc-date") { m_metadata.insert("updated", e.firstChild().toText().data()); } else if (e.tagName() == "meta:meta-userdefined") { if (e.attribute("meta:name") == "tag") { m_bundletags << e.attribute("meta:value"); } else { m_metadata.insert(e.attribute("meta:name"), e.attribute("meta:value")); } } else if(e.tagName() == "meta:bundle-version") { m_metadata.insert("bundle-version", e.firstChild().toText().data()); versionFound = true; } } } resourceStore->close(); } else { warnKrita << "Could not load meta.xml"; return false; } if (resourceStore->open("preview.png")) { // Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice // fails with "libpng error: IDAT: CRC error" QByteArray data = resourceStore->device()->readAll(); QBuffer buffer(&data); m_thumbnail.load(&buffer, "PNG"); resourceStore->close(); } else { warnKrita << "Could not open preview.png"; } /* * If no version is found it's an old bundle with md5 hashes to fix, or if some manifest resource entry * doesn't not correspond to a file the bundle is "broken", in both cases we need to recreate the bundle. */ if (!versionFound) { m_metadata.insert("bundle-version", "1"); warnKrita << filename() << " has an old version and possibly wrong resources md5, so it will be recreated."; toRecreate = true; } if (toRecreate) { recreateBundle(resourceStore); } m_installed = true; setValid(true); setImage(m_thumbnail); } return true; } bool KisResourceBundle::loadFromDevice(QIODevice *) { return false; } bool saveResourceToStore(KoResource *resource, KoStore *store, const QString &resType) { if (!resource) { warnKrita << "No Resource"; return false; } if (!resource->valid()) { warnKrita << "Resource is not valid"; return false; } if (!store || store->bad()) { warnKrita << "No Store or Store is Bad"; return false; } QByteArray ba; QBuffer buf; QFileInfo fi(resource->filename()); if (fi.exists() && fi.isReadable()) { QFile f(resource->filename()); if (!f.open(QFile::ReadOnly)) { warnKrita << "Could not open resource" << resource->filename(); return false; } ba = f.readAll(); if (ba.size() == 0) { warnKrita << "Resource is empty" << resource->filename(); return false; } f.close(); buf.setBuffer(&ba); } else { warnKrita << "Could not find the resource " << resource->filename() << " or it isn't readable"; return false; } if (!buf.open(QBuffer::ReadOnly)) { warnKrita << "Could not open buffer"; return false; } Q_ASSERT(!store->hasFile(resType + "/" + resource->shortFilename())); if (!store->open(resType + "/" + resource->shortFilename())) { warnKrita << "Could not open file in store for resource"; return false; } bool res = (store->write(buf.data()) == buf.size()); store->close(); return res; } bool KisResourceBundle::save() { if (filename().isEmpty()) return false; addMeta("updated", QDateTime::currentDateTime().toOffsetFromUtc(0).toString(Qt::ISODate)); QDir bundleDir = KoResourcePaths::saveLocation("data", "bundles"); bundleDir.cdUp(); QScopedPointer store(KoStore::createStore(filename(), KoStore::Write, "application/x-krita-resourcebundle", KoStore::Zip)); if (!store || store->bad()) return false; Q_FOREACH (const QString &resType, m_manifest.types()) { if (resType == "ko_gradients") { KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KoResource *res = gradientServer->resourceByMD5(ref.md5sum); if (!res) res = gradientServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); if (!saveResourceToStore(res, store.data(), "gradients")) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } else if (resType == "ko_patterns") { KoResourceServer* patternServer = KoResourceServerProvider::instance()->patternServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KoResource *res = patternServer->resourceByMD5(ref.md5sum); if (!res) res = patternServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); if (!saveResourceToStore(res, store.data(), "patterns")) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } else if (resType == "kis_brushes") { KisBrushResourceServer* brushServer = KisBrushServer::instance()->brushServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KisBrushSP brush = brushServer->resourceByMD5(ref.md5sum); if (!brush) brush = brushServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); KoResource *res = brush.data(); if (!saveResourceToStore(res, store.data(), "brushes")) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } else if (resType == "ko_palettes") { KoResourceServer* paletteServer = KoResourceServerProvider::instance()->paletteServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KoResource *res = paletteServer->resourceByMD5(ref.md5sum); if (!res) res = paletteServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); if (!saveResourceToStore(res, store.data(), "palettes")) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } else if (resType == "kis_workspaces") { KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KoResource *res = workspaceServer->resourceByMD5(ref.md5sum); if (!res) res = workspaceServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); if (!saveResourceToStore(res, store.data(), "workspaces")) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } else if (resType == "kis_paintoppresets") { KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(ref.md5sum); if (!res) res = paintoppresetServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); if (!saveResourceToStore(res.data(), store.data(), "paintoppresets")) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } else if (resType == "ko_gamutmasks") { KoResourceServer* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { KoResource *res = gamutMaskServer->resourceByMD5(ref.md5sum); if (!res) res = gamutMaskServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); if (!saveResourceToStore(res, store.data(), "gamutmasks")) { if (res) { warnKrita << "Could not save resource" << resType << res->name(); } else { warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); } } } } +#if defined HAVE_SEEXPR + else if (resType == "kis_seexpr_scripts") + { + KoResourceServer *seExprScriptServer = KoResourceServerProvider::instance()->seExprScriptServer(); + Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) + { + KoResource *res = seExprScriptServer->resourceByMD5(ref.md5sum); + if (!res) + res = seExprScriptServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName()); + if (!saveResourceToStore(res, store.data(), "seexpr_scripts")) + { + if (res) + { + warnKrita << "Could not save resource" << resType << res->name(); + } + else + { + warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName(); + } + } + } + } +#endif } if (!m_thumbnail.isNull()) { QByteArray byteArray; QBuffer buffer(&byteArray); m_thumbnail.save(&buffer, "PNG"); if (!store->open("preview.png")) warnKrita << "Could not open preview.png"; if (store->write(byteArray) != buffer.size()) warnKrita << "Could not write preview.png"; store->close(); } saveManifest(store); saveMetadata(store); store->finalize(); return true; } bool KisResourceBundle::saveToDevice(QIODevice */*dev*/) const { return false; } bool KisResourceBundle::install() { QStringList md5Mismatch; if (filename().isEmpty()) { warnKrita << "Cannot install bundle: no file name" << this; return false; } QScopedPointer resourceStore(KoStore::createStore(filename(), KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip)); if (!resourceStore || resourceStore->bad()) { warnKrita << "Cannot open the resource bundle: invalid zip file?"; return false; } Q_FOREACH (const QString &resType, m_manifest.types()) { dbgResources << "Installing resource type" << resType; if (resType == "gradients") { KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { if (resourceStore->isOpen()) resourceStore->close(); dbgResources << "\tInstalling" << ref.resourcePath; KoAbstractGradient *res = gradientServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); if (!res) { warnKrita << "Could not create resource for" << ref.resourcePath; continue; } if (!resourceStore->open(ref.resourcePath)) { warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); continue; } if (!res->loadFromDevice(resourceStore->device())) { warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); continue; } dbgResources << "\t\tresource:" << res->name(); KoAbstractGradient *res2 = gradientServer->resourceByName(res->name()); if (!res2) {//if it doesn't exist... gradientServer->addResource(res, false);//add it! if (!m_gradientsMd5Installed.contains(res->md5())) { m_gradientsMd5Installed.append(res->md5()); } if (ref.md5sum!=res->md5()) { md5Mismatch.append(res->name()); } Q_FOREACH (const QString &tag, ref.tagList) { gradientServer->addTag(res, tag); } //gradientServer->addTag(res, name()); } else { //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; } } } else if (resType == "patterns") { KoResourceServer* patternServer = KoResourceServerProvider::instance()->patternServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { if (resourceStore->isOpen()) resourceStore->close(); dbgResources << "\tInstalling" << ref.resourcePath; KoPattern *res = patternServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); if (!res) { warnKrita << "Could not create resource for" << ref.resourcePath; continue; } if (!resourceStore->open(ref.resourcePath)) { warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); continue; } if (!res->loadFromDevice(resourceStore->device())) { warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); continue; } dbgResources << "\t\tresource:" << res->name(); KoPattern *res2 = patternServer->resourceByName(res->name()); if (!res2) {//if it doesn't exist... patternServer->addResource(res, false);//add it! if (!m_patternsMd5Installed.contains(res->md5())) { m_patternsMd5Installed.append(res->md5()); } if (ref.md5sum!=res->md5()) { md5Mismatch.append(res->name()); } Q_FOREACH (const QString &tag, ref.tagList) { patternServer->addTag(res, tag); } //patternServer->addTag(res, name()); } } } else if (resType == "brushes") { KisBrushResourceServer *brushServer = KisBrushServer::instance()->brushServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { if (resourceStore->isOpen()) resourceStore->close(); dbgResources << "\tInstalling" << ref.resourcePath; KisBrushSP res = brushServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); if (!res) { warnKrita << "Could not create resource for" << ref.resourcePath; continue; } if (!resourceStore->open(ref.resourcePath)) { warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); continue; } if (!res->loadFromDevice(resourceStore->device())) { warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); continue; } dbgResources << "\t\tresource:" << res->name(); //find the resource on the server KisBrushSP res2 = brushServer->resourceByName(res->name()); if (res2) { res->setName(res->name()+"("+res->shortFilename()+")"); } // file name is more important than the regular name because the // it is the way how it is called up from the brushpreset settings. // Therefore just adjust the resource name and only refuse to load // when the filename is different. res2 = brushServer->resourceByFilename(res->shortFilename()); if (!res2) {//if it doesn't exist... brushServer->addResource(res, false);//add it! if (!m_brushesMd5Installed.contains(res->md5())) { m_brushesMd5Installed.append(res->md5()); } if (ref.md5sum!=res->md5()) { md5Mismatch.append(res->name()); } Q_FOREACH (const QString &tag, ref.tagList) { brushServer->addTag(res.data(), tag); } //brushServer->addTag(res.data(), name()); } else { //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; } } } else if (resType == "palettes") { KoResourceServer* paletteServer = KoResourceServerProvider::instance()->paletteServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { if (resourceStore->isOpen()) resourceStore->close(); dbgResources << "\tInstalling" << ref.resourcePath; KoColorSet *res = paletteServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); if (!res) { warnKrita << "Could not create resource for" << ref.resourcePath; continue; } if (!resourceStore->open(ref.resourcePath)) { warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); continue; } if (!res->loadFromDevice(resourceStore->device())) { warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); continue; } dbgResources << "\t\tresource:" << res->name(); //find the resource on the server KoColorSet *res2 = paletteServer->resourceByName(res->name()); if (!res2) {//if it doesn't exist... paletteServer->addResource(res, false);//add it! if (!m_palettesMd5Installed.contains(res->md5())) { m_palettesMd5Installed.append(res->md5()); } if (ref.md5sum!=res->md5()) { md5Mismatch.append(res->name()); } Q_FOREACH (const QString &tag, ref.tagList) { paletteServer->addTag(res, tag); } //paletteServer->addTag(res, name()); } else { //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; } } } else if (resType == "workspaces") { KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { if (resourceStore->isOpen()) resourceStore->close(); dbgResources << "\tInstalling" << ref.resourcePath; KisWorkspaceResource *res = workspaceServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); if (!res) { warnKrita << "Could not create resource for" << ref.resourcePath; continue; } if (!resourceStore->open(ref.resourcePath)) { warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); continue; } if (!res->loadFromDevice(resourceStore->device())) { warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); continue; } dbgResources << "\t\tresource:" << res->name(); //the following tries to find the resource by name. KisWorkspaceResource *res2 = workspaceServer->resourceByName(res->name()); if (!res2) {//if it doesn't exist... workspaceServer->addResource(res, false);//add it! if (!m_workspacesMd5Installed.contains(res->md5())) { m_workspacesMd5Installed.append(res->md5()); } if (ref.md5sum!=res->md5()) { md5Mismatch.append(res->name()); } Q_FOREACH (const QString &tag, ref.tagList) { workspaceServer->addTag(res, tag); } //workspaceServer->addTag(res, name()); } else { //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; } } } else if (resType == "paintoppresets") { KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { if (resourceStore->isOpen()) resourceStore->close(); dbgResources << "\tInstalling" << ref.resourcePath; KisPaintOpPresetSP res = paintoppresetServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); if (!res) { warnKrita << "Could not create resource for" << ref.resourcePath; continue; } if (!resourceStore->open(ref.resourcePath)) { warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); continue; } // Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice // fails with "libpng error: IDAT: CRC error" QByteArray data = resourceStore->device()->readAll(); QBuffer buffer(&data); if (!res->loadFromDevice(&buffer)) { warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); continue; } dbgResources << "\t\tresource:" << res->name() << "File:" << res->filename(); //the following tries to find the resource by name. KisPaintOpPresetSP res2 = paintoppresetServer->resourceByName(res->name()); if (!res2) {//if it doesn't exist... paintoppresetServer->addResource(res, false);//add it! if (!m_presetsMd5Installed.contains(res->md5())){ m_presetsMd5Installed.append(res->md5()); } if (ref.md5sum!=res->md5()) { md5Mismatch.append(res->name()); } Q_FOREACH (const QString &tag, ref.tagList) { paintoppresetServer->addTag(res.data(), tag); } //paintoppresetServer->addTag(res.data(), name()); } else { //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; } } } else if (resType == "gamutmasks") { KoResourceServer* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) { if (resourceStore->isOpen()) resourceStore->close(); dbgResources << "\tInstalling" << ref.resourcePath; KoGamutMask *res = gamutMaskServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); if (!res) { warnKrita << "Could not create resource for" << ref.resourcePath; continue; } if (!resourceStore->open(ref.resourcePath)) { warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); continue; } if (!res->loadFromDevice(resourceStore->device())) { warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); continue; } dbgResources << "\t\tresource:" << res->name(); //find the resource on the server KoGamutMask *res2 = gamutMaskServer->resourceByName(res->name()); if (!res2) {//if it doesn't exist... gamutMaskServer->addResource(res, false);//add it! if (!m_gamutMasksMd5Installed.contains(res->md5())) { m_gamutMasksMd5Installed.append(res->md5()); } if (ref.md5sum!=res->md5()) { md5Mismatch.append(res->name()); } Q_FOREACH (const QString &tag, ref.tagList) { gamutMaskServer->addTag(res, tag); } //gamutMaskServer->addTag(res, name()); } else { //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; } } } +#if defined HAVE_SEEXPR + else if (resType == "seexpr_scripts") + { + KoResourceServer *seExprScriptServer = KoResourceServerProvider::instance()->seExprScriptServer(); + Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) + { + + if (resourceStore->isOpen()) + resourceStore->close(); + + dbgResources << "\tInstalling" << ref.resourcePath; + KisSeExprScript *res = seExprScriptServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath)); + + if (!res) + { + warnKrita << "Could not create resource for" << ref.resourcePath; + continue; + } + if (!resourceStore->open(ref.resourcePath)) + { + warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename(); + continue; + } + if (!res->loadFromDevice(resourceStore->device())) + { + warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename(); + continue; + } + dbgResources << "\t\tresource:" << res->name(); + + //find the resource on the server + KisSeExprScript *res2 = seExprScriptServer->resourceByName(res->name()); + if (!res2) + { + //if it doesn't exist... + seExprScriptServer->addResource(res, false); //add it! + + if (!m_seExprScriptsMd5Installed.contains(res->md5())) + { + m_seExprScriptsMd5Installed.append(res->md5()); + } + if (ref.md5sum != res->md5()) + { + md5Mismatch.append(res->name()); + } + + Q_FOREACH (const QString &tag, ref.tagList) + { + seExprScriptServer->addTag(res, tag); + } + //seExprScriptServer->addTag(res, name()); + } + else + { + //warnKrita << "Didn't install" << res->name()<<"It already exists on the server"; + } + } + } +#endif } m_installed = true; if(!md5Mismatch.isEmpty()){ QString message = i18n("The following resources had mismatching MD5 sums. They may have gotten corrupted, for example, during download."); QMessageBox bundleFeedback; bundleFeedback.setIcon(QMessageBox::Warning); Q_FOREACH (QString name, md5Mismatch) { message.append("\n"); message.append(name); } bundleFeedback.setText(message); bundleFeedback.exec(); } return true; } bool KisResourceBundle::uninstall() { m_installed = false; QStringList tags = getTagsList(); tags << m_manifest.tags(); //tags << name(); KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("gradients")) { Q_FOREACH (const QByteArray md5, m_gradientsMd5Installed) { KoAbstractGradient *res = gradientServer->resourceByMD5(md5); if (res) { gradientServer->removeResourceFromServer(res); } } KoResourceServer* patternServer = KoResourceServerProvider::instance()->patternServer(); //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("patterns")) { Q_FOREACH (const QByteArray md5, m_patternsMd5Installed) { KoPattern *res = patternServer->resourceByMD5(md5); if (res) { patternServer->removeResourceFromServer(res); } } KisBrushResourceServer *brushServer = KisBrushServer::instance()->brushServer(); //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("brushes")) { Q_FOREACH (const QByteArray md5, m_brushesMd5Installed) { KisBrushSP res = brushServer->resourceByMD5(md5); if (res) { brushServer->removeResourceFromServer(res); } } KoResourceServer* paletteServer = KoResourceServerProvider::instance()->paletteServer(); //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("palettes")) { Q_FOREACH (const QByteArray md5, m_palettesMd5Installed) { KoColorSet *res = paletteServer->resourceByMD5(md5); if (res) { paletteServer->removeResourceFromServer(res); } } KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("workspaces")) { Q_FOREACH (const QByteArray md5, m_workspacesMd5Installed) { KisWorkspaceResource *res = workspaceServer->resourceByMD5(md5); if (res) { workspaceServer->removeResourceFromServer(res); } } KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer(); //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("paintoppresets")) { Q_FOREACH (const QByteArray md5, m_presetsMd5Installed) { KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(md5); if (res) { paintoppresetServer->removeResourceFromServer(res); } } KoResourceServer* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("gamutmasks")) { Q_FOREACH (const QByteArray md5, m_gamutMasksMd5Installed) { KoGamutMask *res = gamutMaskServer->resourceByMD5(md5); if (res) { gamutMaskServer->removeResourceFromServer(res); } } +#if defined HAVE_SEEXPR + KoResourceServer *seExprScriptServer = KoResourceServerProvider::instance()->seExprScriptServer(); + //Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, m_manifest.files("seexpr_scripts")) { + Q_FOREACH (const QByteArray md5, m_seExprScriptsMd5Installed) + { + KisSeExprScript *res = seExprScriptServer->resourceByMD5(md5); + if (res) + { + seExprScriptServer->removeResourceFromServer(res); + } + } +#endif + Q_FOREACH(const QString &tag, tags) { paintoppresetServer->tagCategoryRemoved(tag); workspaceServer->tagCategoryRemoved(tag); paletteServer->tagCategoryRemoved(tag); brushServer->tagCategoryRemoved(tag); patternServer->tagCategoryRemoved(tag); gradientServer->tagCategoryRemoved(tag); gamutMaskServer->tagCategoryRemoved(tag); } return true; } void KisResourceBundle::addMeta(const QString &type, const QString &value) { m_metadata.insert(type, value); } const QString KisResourceBundle::getMeta(const QString &type, const QString &defaultValue) const { if (m_metadata.contains(type)) { return m_metadata[type]; } else { return defaultValue; } } void KisResourceBundle::addResource(QString fileType, QString filePath, QStringList fileTagList, const QByteArray md5sum) { m_manifest.addResource(fileType, filePath, fileTagList, md5sum); } QList KisResourceBundle::getTagsList() { return QList::fromSet(m_bundletags); } bool KisResourceBundle::isInstalled() { return m_installed; } QStringList KisResourceBundle::resourceTypes() const { return m_manifest.types(); } QList KisResourceBundle::resources(const QString &resType) const { QList references = m_manifest.files(resType); QList ret; Q_FOREACH (const KisResourceBundleManifest::ResourceReference &ref, references) { if (resType == "gradients") { KoResourceServer* gradientServer = KoResourceServerProvider::instance()->gradientServer(); KoResource *res = gradientServer->resourceByMD5(ref.md5sum); if (res) ret << res; } else if (resType == "patterns") { KoResourceServer* patternServer = KoResourceServerProvider::instance()->patternServer(); KoResource *res = patternServer->resourceByMD5(ref.md5sum); if (res) ret << res; } else if (resType == "brushes") { KisBrushResourceServer *brushServer = KisBrushServer::instance()->brushServer(); KoResource *res = brushServer->resourceByMD5(ref.md5sum).data(); if (res) ret << res; } else if (resType == "palettes") { KoResourceServer* paletteServer = KoResourceServerProvider::instance()->paletteServer(); KoResource *res = paletteServer->resourceByMD5(ref.md5sum); if (res) ret << res; } else if (resType == "workspaces") { KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); KoResource *res = workspaceServer->resourceByMD5(ref.md5sum); if (res) ret << res; } else if (resType == "paintoppresets") { KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer(); KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(ref.md5sum); if (res) ret << res.data(); } else if (resType == "gamutmasks") { KoResourceServer* gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); KoResource *res = gamutMaskServer->resourceByMD5(ref.md5sum); if (res) ret << res; } +#if defined HAVE_SEEXPR + else if (resType == "seexpr_scripts") { + KoResourceServer *seExprScriptServer = KoResourceServerProvider::instance()->seExprScriptServer(); + KoResource *res = seExprScriptServer->resourceByMD5(ref.md5sum); + if (res) ret << res; + } +#endif } return ret; } void KisResourceBundle::setThumbnail(QString filename) { if (QFileInfo(filename).exists()) { m_thumbnail = QImage(filename); m_thumbnail = m_thumbnail.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation); } else { m_thumbnail = QImage(256, 256, QImage::Format_ARGB32); QPainter gc(&m_thumbnail); gc.fillRect(0, 0, 256, 256, Qt::red); gc.end(); } setImage(m_thumbnail); } void KisResourceBundle::writeMeta(const char *metaTag, const QString &metaKey, KoXmlWriter *writer) { if (m_metadata.contains(metaKey)) { writer->startElement(metaTag); writer->addTextNode(m_metadata[metaKey].toUtf8()); writer->endElement(); } } void KisResourceBundle::writeUserDefinedMeta(const QString &metaKey, KoXmlWriter *writer) { if (m_metadata.contains(metaKey)) { writer->startElement("meta:meta-userdefined"); writer->addAttribute("meta:name", metaKey); writer->addAttribute("meta:value", m_metadata[metaKey]); writer->endElement(); } } void KisResourceBundle::setInstalled(bool install) { m_installed = install; } void KisResourceBundle::saveMetadata(QScopedPointer &store) { QBuffer buf; store->open("meta.xml"); buf.open(QBuffer::WriteOnly); KoXmlWriter metaWriter(&buf); metaWriter.startDocument("office:document-meta"); metaWriter.startElement("meta:meta"); writeMeta("meta:generator", "generator", &metaWriter); metaWriter.startElement("meta:bundle-version"); metaWriter.addTextNode(m_bundleVersion.toUtf8()); metaWriter.endElement(); writeMeta("dc:author", "author", &metaWriter); writeMeta("dc:title", "filename", &metaWriter); writeMeta("dc:description", "description", &metaWriter); writeMeta("meta:initial-creator", "author", &metaWriter); writeMeta("dc:creator", "author", &metaWriter); writeMeta("meta:creation-date", "created", &metaWriter); writeMeta("meta:dc-date", "updated", &metaWriter); writeUserDefinedMeta("email", &metaWriter); writeUserDefinedMeta("license", &metaWriter); writeUserDefinedMeta("website", &metaWriter); Q_FOREACH (const QString &tag, m_bundletags) { metaWriter.startElement("meta:meta-userdefined"); metaWriter.addAttribute("meta:name", "tag"); metaWriter.addAttribute("meta:value", tag); metaWriter.endElement(); } metaWriter.endElement(); // meta:meta metaWriter.endDocument(); buf.close(); store->write(buf.data()); store->close(); } void KisResourceBundle::saveManifest(QScopedPointer &store) { store->open("META-INF/manifest.xml"); QBuffer buf; buf.open(QBuffer::WriteOnly); m_manifest.save(&buf); buf.close(); store->write(buf.data()); store->close(); } void KisResourceBundle::recreateBundle(QScopedPointer &oldStore) { // Save a copy of the unmodified bundle, so that if anything goes bad the user doesn't lose it QFile file(filename()); file.copy(filename() + ".old"); QString newStoreName = filename() + ".tmp"; { QScopedPointer store(KoStore::createStore(newStoreName, KoStore::Write, "application/x-krita-resourcebundle", KoStore::Zip)); KoHashGenerator *generator = KoHashGeneratorProvider::instance()->getGenerator("MD5"); KisResourceBundleManifest newManifest; addMeta("updated", QDateTime::currentDateTime().toString(Qt::ISODate)); Q_FOREACH (KisResourceBundleManifest::ResourceReference ref, m_manifest.files()) { // Wrong manifest entry found, skip it if(!oldStore->open(ref.resourcePath)) continue; store->open(ref.resourcePath); QByteArray data = oldStore->device()->readAll(); oldStore->close(); store->write(data); store->close(); QByteArray result = generator->generateHash(data); newManifest.addResource(ref.fileTypeName, ref.resourcePath, ref.tagList, result); } m_manifest = newManifest; if (!m_thumbnail.isNull()) { QByteArray byteArray; QBuffer buffer(&byteArray); m_thumbnail.save(&buffer, "PNG"); if (!store->open("preview.png")) warnKrita << "Could not open preview.png"; if (store->write(byteArray) != buffer.size()) warnKrita << "Could not write preview.png"; store->close(); } saveManifest(store); saveMetadata(store); store->finalize(); } // Remove the current bundle and then move the tmp one to be the correct one file.setFileName(filename()); if (!file.remove()) { qWarning() << "Could not remove" << filename() << file.errorString(); } QFile f(newStoreName); Q_ASSERT(f.exists()); if (!f.copy(filename())) { qWarning() << "Could not copy the tmp file to the store" << filename() << newStoreName << QFile(newStoreName).exists() << f.errorString(); } } int KisResourceBundle::resourceCount() const { return m_manifest.files().count(); } diff --git a/libs/ui/KisResourceBundle.h b/libs/ui/KisResourceBundle.h index bd167bf4e7..6a66aea05a 100644 --- a/libs/ui/KisResourceBundle.h +++ b/libs/ui/KisResourceBundle.h @@ -1,163 +1,166 @@ /* * Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KORESOURCEBUNDLE_H #define KORESOURCEBUNDLE_H #include #include #include - +#include #include #include "KisResourceBundleManifest.h" #include "kritaui_export.h" class KoStore; /** * @brief The ResourceBundle class * @details Describe the resource bundles as KoResources */ class KRITAUI_EXPORT KisResourceBundle : public KoResource { public: /** * @brief ResourceBundle : Ctor * * @param fileName the path of the bundle */ KisResourceBundle(QString const& fileName); /** * @brief ~ResourceBundle : Dtor */ ~KisResourceBundle() override; /** * @brief defaultFileExtension * @return the default file extension which should be when saving the resource */ QString defaultFileExtension() const override; /** * @brief load : Load this resource. * @return true if succeed, false otherwise. */ bool load() override; bool loadFromDevice(QIODevice *dev) override; /** * @brief save : Save this resource. * @return true if succeed, false otherwise. */ bool save() override; bool saveToDevice(QIODevice* dev) const override; /** * @brief install : Install the contents of the resource bundle. */ bool install(); /** * @brief uninstall : Uninstall the resource bundle. */ bool uninstall(); /** * @brief addMeta : Add a Metadata to the resource * @param type type of the metadata * @param value value of the metadata */ void addMeta(const QString &type, const QString &value); const QString getMeta(const QString &type, const QString &defaultValue = QString()) const; /** * @brief Add a file to the bundle * @param fileType type of the resource file * @param filePath path of the resource file * @param fileTagList the list of tags * @param md5sum the file MD5 checksum */ void addResource(QString fileType, QString filePath, QStringList fileTagList, const QByteArray md5sum); QList getTagsList(); /** * @brief isInstalled * @return true if the bundle is installed, false otherwise. */ bool isInstalled(); /** * @brief setInstalled * This allows you to set installed or uninstalled upon loading. This is used with blacklists. */ void setInstalled(bool install); void setThumbnail(QString); /** * @brief saveMetadata: saves bundle metadata * @param store bundle where to save the metadata */ void saveMetadata(QScopedPointer &store); /** * @brief saveManifest: saves bundle manifest * @param store bundle where to save the manifest */ void saveManifest(QScopedPointer &store); /** * @brief recreateBundle * It recreates the bundle by copying the old bundle information to a new store * and recalculating the md5 of each resource. * @param oldStore the old store to be recreated. */ void recreateBundle(QScopedPointer &oldStore); QStringList resourceTypes() const; QList resources(const QString &resType = QString()) const; int resourceCount() const; private: void writeMeta(const char *metaTag, const QString &metaKey, KoXmlWriter *writer); void writeUserDefinedMeta(const QString &metaKey, KoXmlWriter *writer); private: QImage m_thumbnail; KisResourceBundleManifest m_manifest; QMap m_metadata; QSet m_bundletags; bool m_installed; QList m_gradientsMd5Installed; QList m_patternsMd5Installed; QList m_brushesMd5Installed; QList m_palettesMd5Installed; QList m_workspacesMd5Installed; QList m_presetsMd5Installed; QList m_gamutMasksMd5Installed; +#if defined HAVE_SEEXPR + QList m_seExprScriptsMd5Installed; +#endif QString m_bundleVersion; }; #endif // KORESOURCEBUNDLE_H diff --git a/libs/ui/forms/wdgdlggeneratorlayer.ui b/libs/ui/forms/wdgdlggeneratorlayer.ui index b903c4daea..5f5348732d 100644 --- a/libs/ui/forms/wdgdlggeneratorlayer.ui +++ b/libs/ui/forms/wdgdlggeneratorlayer.ui @@ -1,59 +1,71 @@ WdgDlgGeneratorLayer 0 0 - 500 - 300 + 700 + 430 + + + 700 + 430 + + + + + 0 + 0 + + 8 Layer Name: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter txtLayerName 0 0 KisWdgGenerator QWidget
widgets/kis_wdg_generator.h
1
diff --git a/libs/ui/forms/wdggenerators.ui b/libs/ui/forms/wdggenerators.ui index fb4a160efc..087924d46d 100644 --- a/libs/ui/forms/wdggenerators.ui +++ b/libs/ui/forms/wdggenerators.ui @@ -1,74 +1,74 @@ WdgGenerators 0 0 - 500 - 324 + 700 + 430 0 0 - 500 + 700 0 0 0 0 0 0 0 150 0 150 16777215 6 0 diff --git a/libs/ui/widgets/kis_seexpr_script_chooser.cpp b/libs/ui/widgets/kis_seexpr_script_chooser.cpp new file mode 100644 index 0000000000..3a389f9e7b --- /dev/null +++ b/libs/ui/widgets/kis_seexpr_script_chooser.cpp @@ -0,0 +1,111 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_seexpr_script_chooser.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "kis_signals_blocker.h" + +#include "kis_global.h" +#include +#include + +#include + +KisSeExprScriptChooser::KisSeExprScriptChooser(QWidget *parent) + : QFrame(parent) +{ + m_lblName = new KSqueezedTextLabel(this); + m_lblName->setTextElideMode(Qt::ElideLeft); + + KoResourceServer *rserver = KoResourceServerProvider::instance()->seExprScriptServer(); + QSharedPointer adapter (new KoResourceServerAdapter(rserver)); + m_itemChooser = new KoResourceItemChooser(adapter, this, true); + m_itemChooser->setPreviewTiled(true); + m_itemChooser->setPreviewOrientation(Qt::Horizontal); + m_itemChooser->showTaggingBar(true); + m_itemChooser->setSynced(true); + + connect(m_itemChooser, SIGNAL(resourceSelected(KoResource*)), + this, SLOT(update(KoResource*))); + + connect(m_itemChooser, SIGNAL(resourceSelected(KoResource*)), + this, SIGNAL(resourceSelected(KoResource*))); + + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); + mainLayout->setMargin(0); + mainLayout->addWidget(m_lblName); + mainLayout->addWidget(m_itemChooser, 10); + + setLayout(mainLayout); +} + +KisSeExprScriptChooser::~KisSeExprScriptChooser() +{ +} + +KoResource * KisSeExprScriptChooser::currentResource() +{ + if (!m_itemChooser->currentResource()) { + KoResourceServer *rserver = KoResourceServerProvider::instance()->seExprScriptServer(); + if (rserver->resources().size() > 0) { + KisSignalsBlocker blocker(m_itemChooser); + m_itemChooser->setCurrentResource(rserver->resources().first()); + } + } + return m_itemChooser->currentResource(); +} + +void KisSeExprScriptChooser::setCurrentScript(KoResource *resource) +{ + m_itemChooser->setCurrentResource(resource); +} + +void KisSeExprScriptChooser::setCurrentItem(int row, int column) +{ + m_itemChooser->setCurrentItem(row, column); + if (currentResource()) { + update(currentResource()); + } +} + +void KisSeExprScriptChooser::setPreviewOrientation(Qt::Orientation orientation) +{ + m_itemChooser->setPreviewOrientation(orientation); +} + +void KisSeExprScriptChooser::update(KoResource * resource) +{ + m_lblName->setFixedWidth(m_itemChooser->width()); + KisSeExprScript *pattern = static_cast(resource); + m_lblName->setText(QString("%1").arg(i18n(pattern->name().toUtf8()))); +} diff --git a/libs/ui/widgets/kis_seexpr_script_chooser.h b/libs/ui/widgets/kis_seexpr_script_chooser.h new file mode 100644 index 0000000000..e1d75c7339 --- /dev/null +++ b/libs/ui/widgets/kis_seexpr_script_chooser.h @@ -0,0 +1,63 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_SEEXPR_SCRIPT_CHOOSER_H_ +#define KIS_SEEXPR_SCRIPT_CHOOSER_H_ + +#include +#include +#include +#include + +class KRITAUI_EXPORT KisSeExprScriptChooser : public QFrame +{ + + Q_OBJECT + +public: + KisSeExprScriptChooser(QWidget *parent = 0); + ~KisSeExprScriptChooser() override; + + /// Gets the currently selected resource + /// @returns the selected resource, 0 is no resource is selected + KoResource *currentResource(); + void setCurrentScript(KoResource *resource); + void setCurrentItem(int row, int column); + /// determines whether the preview right or below the splitter + void setPreviewOrientation(Qt::Orientation orientation); + +Q_SIGNALS: + + /// Emitted when a resource was selected + void resourceSelected(KoResource *resource); + void updateItemSize(); + +private Q_SLOTS: + + void update(KoResource *resource); + +private: + + KSqueezedTextLabel *m_lblName; + KoResourceItemChooser *m_itemChooser; +}; + +#endif // KIS_SEEXPR_SCRIPT_CHOOSER_H_ + diff --git a/libs/widgets/KoResourceServerProvider.cpp b/libs/widgets/KoResourceServerProvider.cpp index dc187ef957..cf8ac143f7 100644 --- a/libs/widgets/KoResourceServerProvider.cpp +++ b/libs/widgets/KoResourceServerProvider.cpp @@ -1,201 +1,217 @@ /* This file is part of the KDE project Copyright (c) 1999 Matthias Elter Copyright (c) 2003 Patrick Julien Copyright (c) 2005 Sven Langkamp Copyright (C) 2011 Srikanth Tiyyagura This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "KoResourceServerProvider.h" #include #include #include #include #include #include #include #include #include "KoColorSpaceRegistry.h" #include "KoResourcePaths.h" #include "klocalizedstring.h" #include using namespace std; class GradientResourceServer : public KoResourceServer { public: GradientResourceServer(const QString& type, const QString& extensions) : KoResourceServer(type, extensions) , m_foregroundToTransparent(0) , m_foregroundToBackground(0) { insertSpecialGradients(); } void insertSpecialGradients() { const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8(); QList stops; KoStopGradient* gradient = new KoStopGradient(); gradient->setType(QGradient::LinearGradient); gradient->setName(i18n("Foreground to Transparent")); stops << KoGradientStop(0.0, KoColor(Qt::black, cs), FOREGROUNDSTOP); stops << KoGradientStop(1.0, KoColor(QColor(0, 0, 0, 0), cs), COLORSTOP); gradient->setStops(stops); gradient->setValid(true); gradient->setPermanent(true); addResource(gradient, false, true); m_foregroundToTransparent = gradient; gradient = new KoStopGradient(); gradient->setType(QGradient::LinearGradient); gradient->setName(i18n("Foreground to Background")); stops.clear(); stops << KoGradientStop(0.0, KoColor(Qt::black, cs), FOREGROUNDSTOP); stops << KoGradientStop(1.0, KoColor(Qt::white, cs), BACKGROUNDSTOP); gradient->setStops(stops); gradient->setValid(true); gradient->setPermanent(true); addResource(gradient, false, true); m_foregroundToBackground = gradient; } private: friend class KoResourceBundle; KoAbstractGradient* createResource( const QString & filename ) override { QString fileExtension; int index = filename.lastIndexOf('.'); if (index != -1) fileExtension = filename.mid(index).toLower(); KoAbstractGradient* grad = 0; if(fileExtension == ".svg" || fileExtension == ".kgr") grad = new KoStopGradient(filename); else if(fileExtension == ".ggr" ) grad = new KoSegmentGradient(filename); return grad; } QList< KoAbstractGradient* > sortedResources() override { QList< KoAbstractGradient* > resources = KoResourceServer::sortedResources(); QList< KoAbstractGradient* > sorted; if (m_foregroundToTransparent && resources.contains(m_foregroundToTransparent)) { sorted.append(resources.takeAt(resources.indexOf(m_foregroundToTransparent))); } if (m_foregroundToBackground && resources.contains(m_foregroundToBackground)) { sorted.append(resources.takeAt(resources.indexOf(m_foregroundToBackground))); } return sorted + resources; } KoAbstractGradient* m_foregroundToTransparent; KoAbstractGradient* m_foregroundToBackground; }; struct Q_DECL_HIDDEN KoResourceServerProvider::Private { KoResourceServer* patternServer; KoResourceServer* gradientServer; KoResourceServer* paletteServer; KoResourceServer *svgSymbolCollectionServer; KoResourceServer* gamutMaskServer; +#if defined HAVE_SEEXPR + KoResourceServer* seExprScriptServer; +#endif }; KoResourceServerProvider::KoResourceServerProvider() : d(new Private) { d->patternServer = new KoResourceServerSimpleConstruction("ko_patterns", "*.pat:*.jpg:*.gif:*.png:*.tif:*.xpm:*.bmp" ); d->patternServer->loadResources(blacklistFileNames(d->patternServer->fileNames(), d->patternServer->blackListedFiles())); d->gradientServer = new GradientResourceServer("ko_gradients", "*.svg:*.ggr"); d->gradientServer->loadResources(blacklistFileNames(d->gradientServer->fileNames(), d->gradientServer->blackListedFiles())); d->paletteServer = new KoResourceServerSimpleConstruction("ko_palettes", "*.kpl:*.gpl:*.pal:*.act:*.aco:*.css:*.colors:*.xml:*.sbz"); d->paletteServer->loadResources(blacklistFileNames(d->paletteServer->fileNames(), d->paletteServer->blackListedFiles())); d->svgSymbolCollectionServer = new KoResourceServerSimpleConstruction("symbols", "*.svg"); d->svgSymbolCollectionServer->loadResources(blacklistFileNames(d->svgSymbolCollectionServer->fileNames(), d->svgSymbolCollectionServer->blackListedFiles())); d->gamutMaskServer = new KoResourceServerSimpleConstruction("ko_gamutmasks", "*.kgm"); d->gamutMaskServer->loadResources(blacklistFileNames(d->gamutMaskServer->fileNames(), d->gamutMaskServer->blackListedFiles())); + +#if defined HAVE_SEEXPR + d->seExprScriptServer = new KoResourceServerSimpleConstruction("kis_seexpr_scripts", "*.kse"); + d->seExprScriptServer->loadResources(blacklistFileNames(d->seExprScriptServer->fileNames(), d->seExprScriptServer->blackListedFiles())); +#endif } KoResourceServerProvider::~KoResourceServerProvider() { delete d->patternServer; delete d->gradientServer; delete d->paletteServer; delete d->svgSymbolCollectionServer; delete d->gamutMaskServer; +#if defined HAVE_SEEXPR + delete d->seExprScriptServer; +#endif delete d; } Q_GLOBAL_STATIC(KoResourceServerProvider, s_instance) KoResourceServerProvider* KoResourceServerProvider::instance() { return s_instance; } QStringList KoResourceServerProvider::blacklistFileNames(QStringList fileNames, const QStringList &blacklistedFileNames) { if (!blacklistedFileNames.isEmpty()) { foreach (const QString &s, blacklistedFileNames) { fileNames.removeAll(s); } } return fileNames; } KoResourceServer* KoResourceServerProvider::patternServer() { return d->patternServer; } KoResourceServer* KoResourceServerProvider::gradientServer() { return d->gradientServer; } KoResourceServer* KoResourceServerProvider::paletteServer() { return d->paletteServer; } KoResourceServer *KoResourceServerProvider::svgSymbolCollectionServer() { return d->svgSymbolCollectionServer; } KoResourceServer* KoResourceServerProvider::gamutMaskServer() { return d->gamutMaskServer; } - +#if defined HAVE_SEEXPR +KoResourceServer *KoResourceServerProvider::seExprScriptServer() +{ + return d->seExprScriptServer; +} +#endif diff --git a/libs/widgets/KoResourceServerProvider.h b/libs/widgets/KoResourceServerProvider.h index a9f2798c32..7db7a618de 100644 --- a/libs/widgets/KoResourceServerProvider.h +++ b/libs/widgets/KoResourceServerProvider.h @@ -1,76 +1,82 @@ /* This file is part of the KDE project Copyright (c) 1999 Matthias Elter Copyright (c) 2003 Patrick Julien Copyright (c) 2005 Sven Langkamp This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KORESOURCESERVERPROVIDER_H #define KORESOURCESERVERPROVIDER_H #include #include #include #include "KoResourceServer.h" #include #include #include #include #include - +#include +#if defined HAVE_SEEXPR +#include +#endif /** * Provides default resource servers for gradients, patterns and palettes */ class KRITAWIDGETS_EXPORT KoResourceServerProvider : public QObject { Q_OBJECT public: KoResourceServerProvider(); ~KoResourceServerProvider() override; static KoResourceServerProvider* instance(); /** * @brief blacklistFileNames filters the filenames with the list of blacklisted file names * @param fileNames all files * @param blacklistedFileNames the files we don't want * @return the result */ static QStringList blacklistFileNames(QStringList fileNames, const QStringList &blacklistedFileNames); KoResourceServer* patternServer(); KoResourceServer* gradientServer(); KoResourceServer* paletteServer(); KoResourceServer* svgSymbolCollectionServer(); KoResourceServer* gamutMaskServer(); +#if defined HAVE_SEEXPR + KoResourceServer *seExprScriptServer(); +#endif private: KoResourceServerProvider(const KoResourceServerProvider&); KoResourceServerProvider operator=(const KoResourceServerProvider&); private: struct Private; Private* const d; }; #endif // KORESOURCESERVERPROVIDER_H diff --git a/libs/widgetutils/KisDialogStateSaver.cpp b/libs/widgetutils/KisDialogStateSaver.cpp index f01212080f..459e3236a2 100644 --- a/libs/widgetutils/KisDialogStateSaver.cpp +++ b/libs/widgetutils/KisDialogStateSaver.cpp @@ -1,209 +1,220 @@ /* * Copyright (c) 2019 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisDialogStateSaver.h" #include #include #include #include #include #include +#include #include #include #include #include #include #include "kis_int_parse_spin_box.h" #include "kis_double_parse_spin_box.h" #include "kis_double_parse_unit_spin_box.h" #include "kis_slider_spin_box.h" void KisDialogStateSaver::saveState(QWidget *parent, const QString &dialogName) { Q_ASSERT(parent); Q_ASSERT(!dialogName.isEmpty()); KConfigGroup group(KSharedConfig::openConfig(), dialogName); Q_FOREACH(QWidget *widget, parent->findChildren(QString())) { if (!widget->objectName().isEmpty() ) { if (qobject_cast(widget)) { group.writeEntry(widget->objectName(), qobject_cast(widget)->value()); } else if (qobject_cast(widget)) { group.writeEntry(widget->objectName(), qobject_cast(widget)->value()); } else if (qobject_cast(widget)) { // XXX: also save the unit group.writeEntry(widget->objectName(), qobject_cast(widget)->value()); } else if (qobject_cast(widget)) { group.writeEntry(widget->objectName(), qobject_cast(widget)->isChecked()); } else if (qobject_cast(widget)) { group.writeEntry(widget->objectName(), qobject_cast(widget)->currentIndex()); } else if (qobject_cast(widget)) { group.writeEntry(widget->objectName(), qobject_cast(widget)->text()); } + else if (qobject_cast(widget)) { + group.writeEntry(widget->objectName(), qobject_cast(widget)->toPlainText()); + } else if (qobject_cast(widget)) { group.writeEntry(widget->objectName(), qobject_cast(widget)->value()); } else if (qobject_cast(widget)) { group.writeEntry(widget->objectName(), qobject_cast(widget)->value()); } else if (qobject_cast(widget)) { group.writeEntry(widget->objectName(), qobject_cast(widget)->value()); } else if (qobject_cast(widget)) { group.writeEntry(widget->objectName(), qobject_cast(widget)->isChecked()); } else if (qobject_cast(widget)){ group.writeEntry(widget->objectName(), qobject_cast(widget)->value()); } else { //qWarning() << "Cannot save state for object" << widget; } } else { qWarning() << "Dialog" << dialogName << "has a widget without an objectname:" << widget; } } } void KisDialogStateSaver::restoreState(QWidget *parent, const QString &dialogName, const QMap &defaults) { Q_ASSERT(parent); Q_ASSERT(!dialogName.isEmpty()); KConfigGroup group( KSharedConfig::openConfig(), dialogName); Q_FOREACH(QWidget *widget, parent->findChildren(QString())) { if (!widget->objectName().isEmpty()) { QString widgetName = widget->objectName(); QVariant defaultValue; if (defaults.contains(widgetName)) { defaultValue = defaults[widgetName]; } if (qobject_cast(widget)) { if (defaultValue.isValid()) { qobject_cast(widget)->setValue(defaultValue.toInt()); } else { qobject_cast(widget)->setValue(group.readEntry(widgetName, qobject_cast(widget)->value())); } } else if (qobject_cast(widget)) { if (defaultValue.isValid()) { qobject_cast(widget)->setValue(defaultValue.toInt()); } else { qobject_cast(widget)->setValue(group.readEntry(widgetName, qobject_cast(widget)->value())); } } else if (qobject_cast(widget)) { if (defaultValue.isValid()) { qobject_cast(widget)->setValue(defaultValue.toInt()); } else { qobject_cast(widget)->setValue(group.readEntry(widgetName, qobject_cast(widget)->value())); } } else if (qobject_cast(widget)) { if (defaultValue.isValid()) { qobject_cast(widget)->setChecked(defaultValue.toBool()); } else { qobject_cast(widget)->setChecked(group.readEntry(widgetName, qobject_cast(widget)->isChecked())); } } else if (qobject_cast(widget)) { if (defaultValue.isValid()) { qobject_cast(widget)->setCurrentIndex(defaultValue.toInt()); } else { qobject_cast(widget)->setCurrentIndex(group.readEntry(widgetName, qobject_cast(widget)->currentIndex())); } } else if (qobject_cast(widget)) { if (defaultValue.isValid()) { qobject_cast(widget)->setText(defaultValue.toString()); } else { qobject_cast(widget)->setText(group.readEntry(widgetName, qobject_cast(widget)->text())); } } + else if (qobject_cast(widget)) { + if (defaultValue.isValid()) { + qobject_cast(widget)->setPlainText(defaultValue.toString()); + } else { + qobject_cast(widget)->setPlainText(group.readEntry(widgetName, qobject_cast(widget)->toPlainText())); + } + } else if (qobject_cast(widget)) { if (defaultValue.isValid()) { qobject_cast(widget)->setValue(defaultValue.toInt()); } else { qobject_cast(widget)->setValue(group.readEntry(widgetName, qobject_cast(widget)->value())); } } else if (qobject_cast(widget)) { if (defaultValue.isValid()) { qobject_cast(widget)->setValue(defaultValue.toInt()); } else { qobject_cast(widget)->setValue(group.readEntry(widgetName, qobject_cast(widget)->value())); } } else if (qobject_cast(widget)) { if (defaultValue.isValid()) { qobject_cast(widget)->setValue(defaultValue.toDouble()); } else { qobject_cast(widget)->setValue(group.readEntry(widgetName, qobject_cast(widget)->value())); } } else if (qobject_cast(widget)) { if (defaultValue.isValid()) { qobject_cast(widget)->setChecked(defaultValue.toBool()); } else { qobject_cast(widget)->setChecked(group.readEntry(widgetName, qobject_cast(widget)->isChecked())); } } else if (qobject_cast(widget)) { if (defaultValue.isValid()) { qobject_cast(widget)->setValue(defaultValue.toInt()); } else { qobject_cast(widget)->setValue(group.readEntry(widgetName, qobject_cast(widget)->value())); } } else { //qWarning() << "Cannot restore state for object" << widget; } } else { qWarning() << "Dialog" << dialogName << "has a widget without an object name:" << widget; } } } diff --git a/packaging/linux/appimage/build-deps.sh b/packaging/linux/appimage/build-deps.sh index 4e7ff53125..b82bcede03 100755 --- a/packaging/linux/appimage/build-deps.sh +++ b/packaging/linux/appimage/build-deps.sh @@ -1,85 +1,91 @@ #!/bin/bash # # Build all Krita's dependencies on Ubuntu 14.04. # # Prerequisites: cmake git build-essential libxcb-keysyms1-dev plus all deps for Qt5 # # Halt on errors and be verbose about what we are doing set -e set -x # Read in our parameters export BUILD_PREFIX=$1 export KRITA_SOURCES=$2 # qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment. # That's not always the case, so make sure it is export LC_ALL=en_US.UTF-8 export LANG=en_us.UTF-8 # We want to use $prefix/deps/usr/ for all our dependencies export DEPS_INSTALL_PREFIX=$BUILD_PREFIX/deps/usr/ export DOWNLOADS_DIR=$BUILD_PREFIX/downloads/ # Setup variables needed to help everything find what we build export LD_LIBRARY_PATH=$DEPS_INSTALL_PREFIX/lib:$LD_LIBRARY_PATH export PATH=$DEPS_INSTALL_PREFIX/bin:$PATH export PKG_CONFIG_PATH=$DEPS_INSTALL_PREFIX/share/pkgconfig:$DEPS_INSTALL_PREFIX/lib/pkgconfig:/usr/lib/pkgconfig:$PKG_CONFIG_PATH export CMAKE_PREFIX_PATH=$DEPS_INSTALL_PREFIX:$CMAKE_PREFIX_PATH # A krita build layout looks like this: # krita/ -- the source directory # downloads/ -- downloads of the dependencies from files.kde.org # deps-build/ -- build directory for the dependencies # deps/ -- the location for the built dependencies # build/ -- build directory for krita itself # krita.appdir/ -- install directory for krita and the dependencies # Make sure our downloads directory exists if [ ! -d $DOWNLOADS_DIR ] ; then mkdir -p $DOWNLOADS_DIR fi # Make sure our build directory exists if [ ! -d $BUILD_PREFIX/deps-build/ ] ; then mkdir -p $BUILD_PREFIX/deps-build/ fi # The 3rdparty dependency handling in Krita also requires the install directory to be pre-created if [ ! -d $DEPS_INSTALL_PREFIX ] ; then mkdir -p $DEPS_INSTALL_PREFIX fi # Switch to our build directory as we're basically ready to start building... cd $BUILD_PREFIX/deps-build/ +# Determine how many CPUs we have +export MAKEFLAGS="-j`grep processor /proc/cpuinfo | wc -l`" + # Configure the dependencies for building cmake $KRITA_SOURCES/3rdparty -DCMAKE_INSTALL_PREFIX=$DEPS_INSTALL_PREFIX -DINSTALL_ROOT=$DEPS_INSTALL_PREFIX -DEXTERNALS_DOWNLOAD_DIR=$DOWNLOADS_DIR # Now start building everything we need, in the appropriate order -#cmake --build . --config RelWithDebInfo --target ext_png -#cmake --build . --config RelWithDebInfo --target ext_tiff -#cmake --build . --config RelWithDebInfo --target ext_jpeg +cmake --build . --config RelWithDebInfo --target ext_png +cmake --build . --config RelWithDebInfo --target ext_tiff +cmake --build . --config RelWithDebInfo --target ext_jpeg cmake --build . --config RelWithDebInfo --target ext_boost cmake --build . --config RelWithDebInfo --target ext_fftw3 cmake --build . --config RelWithDebInfo --target ext_eigen3 cmake --build . --config RelWithDebInfo --target ext_expat cmake --build . --config RelWithDebInfo --target ext_exiv2 cmake --build . --config RelWithDebInfo --target ext_lcms2 cmake --build . --config RelWithDebInfo --target ext_ocio cmake --build . --config RelWithDebInfo --target ext_openexr cmake --build . --config RelWithDebInfo --target ext_vc cmake --build . --config RelWithDebInfo --target ext_libraw cmake --build . --config RelWithDebInfo --target ext_giflib -#cmake --build . --config RelWithDebInfo --target ext_gsl +cmake --build . --config RelWithDebInfo --target ext_gsl cmake --build . --config RelWithDebInfo --target ext_python #cmake --build . --config RelWithDebInfo --target ext_freetype #cmake --build . --config RelWithDebInfo --target ext_fontconfig cmake --build . --config RelWithDebInfo --target ext_qt cmake --build . --config RelWithDebInfo --target ext_poppler cmake --build . --config RelWithDebInfo --target ext_kcrash cmake --build . --config RelWithDebInfo --target ext_gmic cmake --build . --config RelWithDebInfo --target ext_sip cmake --build . --config RelWithDebInfo --target ext_pyqt cmake --build . --config RelWithDebInfo --target ext_quazip +cmake --build . --config RelWithDebInfo --target ext_openjpeg +cmake --build . --config RelWithDebInfo --target ext_libheif +cmake --build . --config RelWithDebInfo --target ext_seexpr diff --git a/packaging/macos/osxbuild.sh b/packaging/macos/osxbuild.sh index 9be3d9299f..5d6ac36a1b 100755 --- a/packaging/macos/osxbuild.sh +++ b/packaging/macos/osxbuild.sh @@ -1,699 +1,703 @@ #!/usr/bin/env bash # osxbuild.sh automates building and installing of krita and krita dependencies # for OSX, the script only needs you to set BUILDROOT environment to work # properly. # # Run with no args for a short help about each command. # builddeps: Attempts to build krita dependencies in the necessary order, # intermediate steps for creating symlinks and fixing rpath of some # packages midway is also managed. Order goes from top to bottom, to add # new steps just place them in the proper place. # rebuilddeps: This re-runs all make and make install of dependencies 3rdparty # this was needed as deleting the entire install directory an rerunning build # step for dependencies does not install if they are already built. This step # forces installation. Have not tested it lately so it might not be needed anymore # build: Runs cmake build and make step for krita sources. It always run cmake step, so # it might take a bit longer than a pure on the source tree. The script tries # to set the make flag -jN to a proper N. # install: Runs install step for krita sources. # fixboost: Search for all libraries using boost and sets a proper @rpath for boost as by # default it fails to set a proper @rpath # buildinstall: Runs build, install and fixboost steps.# if test -z $BUILDROOT; then echo "ERROR: BUILDROOT env not set, exiting!" echo "\t Must point to the root of the buildfiles as stated in 3rdparty Readme" exit fi BUILDROOT="${BUILDROOT%/}" echo "BUILDROOT set to ${BUILDROOT}" # Set some global variables. OSXBUILD_TYPE="RelWithDebInfo" OSXBUILD_TESTING="OFF" # -- Parse input args for arg in "${@}"; do if [[ "${arg}" = --dirty ]]; then OSXBUILD_CLEAN="keep dirty" elif [[ "${arg}" = --debug ]]; then OSXBUILD_TYPE="Debug" OSXBUILD_TESTING="ON" elif [[ "${arg}" = --install_tarball ]]; then OSXBUILD_TARBALLINSTALL="TRUE" else parsed_args="${parsed_args} ${arg}" fi done export KIS_SRC_DIR=${BUILDROOT}/krita export KIS_TBUILD_DIR=${BUILDROOT}/depbuild export KIS_TDEPINSTALL_DIR=${BUILDROOT}/depinstall export KIS_DOWN_DIR=${BUILDROOT}/down export KIS_BUILD_DIR=${BUILDROOT}/kisbuild export KIS_INSTALL_DIR=${BUILDROOT}/i # flags for OSX environment # Qt only supports from 10.12 up, and https://doc.qt.io/qt-5/macos.html#target-platforms warns against setting it lower export MACOSX_DEPLOYMENT_TARGET=10.12 export QMAKE_MACOSX_DEPLOYMENT_TARGET=10.12 export PATH=${KIS_INSTALL_DIR}/bin:$PATH export PKG_CONFIG_PATH=${KIS_INSTALL_DIR}/share/pkgconfig:${KIS_INSTALL_DIR}/lib/pkgconfig export CMAKE_PREFIX_PATH=${KIS_INSTALL_DIR} export C_INCLUDE_PATH=${KIS_INSTALL_DIR}/include:/usr/include:${C_INCLUDE_PATH} export CPLUS_INCLUDE_PATH=${KIS_INSTALL_DIR}/include:/usr/include:${CPLUS_INCLUDE_PATH} export LIBRARY_PATH=${KIS_INSTALL_DIR}/lib:/usr/lib:${LIBRARY_PATH} # export CPPFLAGS=-I${KIS_INSTALL_DIR}/include # export LDFLAGS=-L${KIS_INSTALL_DIR}/lib export FRAMEWORK_PATH=${KIS_INSTALL_DIR}/lib/ # export PYTHONHOME=${KIS_INSTALL_DIR} # export PYTHONPATH=${KIS_INSTALL_DIR}/sip:${KIS_INSTALL_DIR}/lib/python3.8/site-packages:${KIS_INSTALL_DIR}/lib/python3.8 # This will make the debug output prettier export KDE_COLOR_DEBUG=1 export QTEST_COLORED=1 export OUPUT_LOG="${BUILDROOT}/osxbuild.log" printf "" > "${OUPUT_LOG}" # Build time variables if test -z $(which cmake); then echo "ERROR: cmake not found, exiting!" exit fi # configure max core for make compile ((MAKE_THREADS=1)) if test ${OSTYPE} == "darwin*"; then ((MAKE_THREADS = $(sysctl -n hw.logicalcpu))) fi # Prints stderr and stdout to log files # >(tee) works but breaks sigint log_cmd () { "$@" 1>> ${OUPUT_LOG} osxbuild_error="${?}" } # Log messages to logfile log () { printf "%s\n" "${@}" | tee -a ${OUPUT_LOG} } # if previous command gives error # print msg print_if_error() { if [ "${osxbuild_error}" -ne 0 ]; then printf "\nERROR: Printing last lines of log ouput\n\n" tail ${OUPUT_LOG} printf "\e[31m%s %s\e[0m\n" "Error:" "${1}" fi } # print status messages print_msg() { printf "\e[32m%s\e[0m\n" "${1}" printf "%s\n" "${1}" >> ${OUPUT_LOG} } check_dir_path () { printf "%s" "Checking if ${1} exists and is dir... " if test -d ${1}; then echo -e "OK" elif test -e ${1}; then echo -e "\n\tERROR: file ${1} exists but is not a directory!" >&2 return 1 else echo -e "Creating ${1}" mkdir ${1} fi return 0 } waiting_fixed() { local message="${1}" local waitTime=${2} for i in $(seq ${waitTime}); do sleep 1 printf -v dots '%*s' ${i} printf -v spaces '%*s' $((${waitTime} - $i)) printf "\r%s [%s%s]" "${message}" "${dots// /.}" "${spaces}" done printf "\n" } dir_clean() { if [[ -d "${1}" ]]; then log "Default cleaning build dirs, use --dirty to keep them..." waiting_fixed "Erase of ${1} in 5 sec" 5 rm -rf "${1}" fi } # builds dependencies for the first time cmake_3rdparty () { cd ${KIS_TBUILD_DIR} local build_pkgs=("${@}") # convert to array local error="false" if [[ ${2} = "1" ]]; then local nofix="true" local build_pkgs=(${build_pkgs[@]:0:1}) fi for package in ${build_pkgs[@]} ; do if [[ ${package:0:3} != "ext" ]]; then continue fi print_msg "Building ${package}" - log_cmd cmake --build . --config RelWithDebInfo --target ${package} + log_cmd cmake --build . --config RelWithDebInfo --target ${package} --quiet print_if_error "Failed build ${package}" if [[ ! ${osxbuild_error} -ne 0 ]]; then print_msg "Build Success! ${package}" else log "${pkg} build fail, attempting known fixes..." error="true" fi # fixes does not depend on failure if [[ ! ${nofix} ]]; then build_3rdparty_fixes ${package} ${error} elif [[ "${error}" = "true" ]]; then log "ERROR: ${pkg} failed a second time, time to check the logs" log "stoping..." fi done } build_3rdparty_fixes(){ local pkg=${1} local error=${2} if [[ "${pkg}" = "ext_qt" && -e "${KIS_INSTALL_DIR}/bin/qmake" ]]; then ln -sf qmake "${KIS_INSTALL_DIR}/bin/qmake-qt5" # build macdeployqt log_cmd cd "${BUILDROOT}/depbuild/ext_qt/ext_qt-prefix/src/ext_qt/qttools/src" print_if_error "macdeployqt source dir was not found, it will be missing for deployment!" if [[ ! ${osxbuild_error} -ne 0 && ! -e "${KIS_INSTALL_DIR}/bin/macdeployqt" ]]; then make sub-macdeployqt-all make sub-macdeployqt-install_subtargets make install fi cd "${KIS_TBUILD_DIR}" error="false" elif [[ "${pkg}" = "ext_openexr" ]]; then # open exr will fail the first time is called # rpath needs to be fixed an build rerun log "Fixing rpath on openexr file: b44ExpLogTable" log "Fixing rpath on openexr file: dwaLookups" log_cmd install_name_tool -add_rpath ${KIS_INSTALL_DIR}/lib $(find ${KIS_TBUILD_DIR}/ext_openexr/ext_openexr-prefix/src/ext_openexr-build -name b44ExpLogTable) log_cmd install_name_tool -add_rpath ${KIS_INSTALL_DIR}/lib $(find ${KIS_TBUILD_DIR}/ext_openexr/ext_openexr-prefix/src/ext_openexr-build -name dwaLookups) # we must rerun build! cmake_3rdparty ext_openexr "1" error="false" elif [[ "${pkg}" = "ext_fontconfig" ]]; then log "fixing rpath on fc-cache" log_cmd install_name_tool -add_rpath ${KIS_INSTALL_DIR}/lib ${KIS_TBUILD_DIR}/ext_fontconfig/ext_fontconfig-prefix/src/ext_fontconfig-build/fc-cache/.libs/fc-cache # rerun rebuild cmake_3rdparty ext_fontconfig "1" error="false" elif [[ "${pkg}" = "ext_poppler" && "${error}" = "true" ]]; then log "re-running poppler to avoid possible glitch" cmake_3rdparty ext_poppler "1" error="false" fi if [[ "${error}" = "true" ]]; then log "Error building package ${pkg}, stopping..." exit fi } build_3rdparty () { print_msg "building in ${KIS_TBUILD_DIR}" log "$(check_dir_path ${KIS_TBUILD_DIR})" log "$(check_dir_path ${KIS_DOWN_DIR})" log "$(check_dir_path ${KIS_INSTALL_DIR})" cd ${KIS_TBUILD_DIR} log_cmd cmake ${KIS_SRC_DIR}/3rdparty/ \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 \ -DCMAKE_INSTALL_PREFIX=${KIS_INSTALL_DIR} \ -DCMAKE_PREFIX_PATH:PATH=${KIS_INSTALL_DIR} \ -DEXTERNALS_DOWNLOAD_DIR=${KIS_DOWN_DIR} \ -DINSTALL_ROOT=${KIS_INSTALL_DIR} # -DCPPFLAGS=-I${KIS_INSTALL_DIR}/include \ # -DLDFLAGS=-L${KIS_INSTALL_DIR}/lib print_msg "finished 3rdparty build setup" if [[ -n ${1} ]]; then cmake_3rdparty "${@}" # log "Syncing install backup..." # rsync -a --delete "${KIS_INSTALL_DIR}" "${KIS_INSTALL_DIR}.onlydeps" exit fi # build 3rdparty tools # The order must not be changed! cmake_3rdparty \ ext_pkgconfig \ ext_gettext \ ext_openssl \ ext_qt \ ext_zlib \ ext_boost \ ext_eigen3 \ ext_exiv2 \ ext_fftw3 \ ext_ilmbase \ ext_jpeg \ ext_lcms2 \ ext_ocio \ ext_openexr \ ext_openjpeg cmake_3rdparty \ ext_png \ ext_tiff \ ext_gsl \ ext_vc \ ext_libraw \ ext_giflib \ ext_freetype \ ext_fontconfig \ ext_poppler # Stop if qmake link was not created # this meant qt build fail and further builds will # also fail. log_cmd test -L "${KIS_INSTALL_DIR}/bin/qmake-qt5" print_if_error "qmake link missing!" if [[ ${osxbuild_error} -ne 0 ]]; then printf " link: ${KIS_INSTALL_DIR}/bin/qmake-qt5 missing! It probably means ext_qt failed!! check, fix and rerun!\n" exit fi # for python cmake_3rdparty \ ext_python \ ext_sip \ ext_pyqt cmake_3rdparty ext_libheif cmake_3rdparty \ ext_extra_cmake_modules \ ext_kconfig \ ext_kwidgetsaddons \ ext_kcompletion \ ext_kcoreaddons \ ext_kguiaddons \ ext_ki18n \ ext_kitemmodels \ ext_kitemviews \ ext_kimageformats \ ext_kwindowsystem \ ext_quazip + cmake_3rdparty ext_seexpr ## All builds done, creating a new install onlydeps install dir dir_clean "${KIS_INSTALL_DIR}.onlydeps" log "Copying ${KIS_INSTALL_DIR} to ${KIS_INSTALL_DIR}.onlydeps" cp -aP "${KIS_INSTALL_DIR}" "${KIS_INSTALL_DIR}.onlydeps" print_msg "Build Finished!" } # Recall cmake for all 3rd party packages # make is only on target first run # subsequent runs only call make install rebuild_3rdparty () { print_msg "starting rebuild of 3rdparty packages" build_install_ext() { for pkg in ${@:1:${#@}}; do { cd ${KIS_TBUILD_DIR}/${pkg}/${pkg}-prefix/src/${pkg}-stamp } || { cd ${KIS_TBUILD_DIR}/ext_frameworks/${pkg}-prefix/src/${pkg}-stamp } || { cd ${KIS_TBUILD_DIR}/ext_heif/${pkg}-prefix/src/${pkg}-stamp } log "Installing ${pkg} files..." rm ${pkg}-configure ${pkg}-build ${pkg}-install cmake_3rdparty ${pkg} cd ${KIS_TBUILD_DIR} done } # Do not process complete list only pkgs given. if ! test -z ${1}; then build_install_ext ${@} exit fi build_install_ext \ ext_pkgconfig \ ext_iconv \ ext_gettext \ ext_openssl \ ext_qt \ ext_zlib \ ext_boost \ ext_eigen3 \ ext_expat \ ext_exiv2 \ ext_fftw3 \ ext_ilmbase \ ext_jpeg \ ext_patch \ ext_lcms2 \ ext_ocio \ ext_ilmbase \ ext_openexr #ext_openjpeg build_install_ext \ ext_png \ ext_tiff \ ext_gsl \ ext_vc \ ext_libraw \ ext_giflib \ ext_fontconfig \ ext_freetype \ ext_poppler \ ext_python \ ext_sip \ ext_pyqt \ build_install_ext \ ext_nasm \ ext_libx265 \ ext_libde265 \ ext_libheif # Build kde_frameworks build_install_ext \ ext_extra_cmake_modules \ ext_kconfig \ ext_kwidgetsaddons \ ext_kcompletion \ ext_kcoreaddons \ ext_kguiaddons \ ext_ki18n \ ext_kitemmodels \ ext_kitemviews \ ext_kimageformats \ ext_kwindowsystem \ ext_quazip + + build_install_ext \ + ext_seexpr } #not tested set_krita_dirs() { if [[ -n ${1} ]]; then KIS_BUILD_DIR=${BUILDROOT}/b_${1} KIS_INSTALL_DIR=${BUILDROOT}/i_${1} KIS_SRC_DIR=${BUILDROOT}/src_${1} fi } # build_krita # run cmake krita build_krita () { if [[ -z ${OSXBUILD_CLEAN} ]]; then log "Deleting ${KIS_BUILD_DIR}" dir_clean "${KIS_BUILD_DIR}" else if [[ -e "${KIS_INSTALL_DIR}.onlydeps" && -d "${KIS_INSTALL_DIR}.onlydeps" ]]; then print_msg "Found ${KIS_INSTALL_DIR}.onlydeps" log "==== manually copy onlydeps to ${KIS_INSTALL_DIR} if you need a fresh build" fi fi export DYLD_FRAMEWORK_PATH=${FRAMEWORK_PATH} echo ${KIS_BUILD_DIR} echo ${KIS_INSTALL_DIR} log_cmd check_dir_path ${KIS_BUILD_DIR} cd ${KIS_BUILD_DIR} cmake ${KIS_SRC_DIR} \ -DFOUNDATION_BUILD=ON \ -DBoost_INCLUDE_DIR=${KIS_INSTALL_DIR}/include \ -DCMAKE_INSTALL_PREFIX=${KIS_INSTALL_DIR} \ -DCMAKE_PREFIX_PATH=${KIS_INSTALL_DIR} \ -DDEFINE_NO_DEPRECATED=1 \ -DBUILD_TESTING=${OSXBUILD_TESTING} \ -DHIDE_SAFE_ASSERTS=ON \ -DKDE_INSTALL_BUNDLEDIR=${KIS_INSTALL_DIR}/bin \ -DPYQT_SIP_DIR_OVERRIDE=${KIS_INSTALL_DIR}/share/sip/ \ -DCMAKE_BUILD_TYPE=${OSXBUILD_TYPE} \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 \ -DPYTHON_INCLUDE_DIR=${KIS_INSTALL_DIR}/lib/Python.framework/Headers # copiling phase - make -j${MAKE_THREADS} + cmake --build . --target all -- -j${MAKE_THREADS} --quiet # compile integrations if test ${OSTYPE} == "darwin*"; then cd ${KIS_BUILD_DIR}/krita/integration/kritaquicklook make -j${MAKE_THREADS} fi } build_krita_tarball () { filename="$(basename ${1})" KIS_CUSTOM_BUILD="${BUILDROOT}/releases/${filename%.tar.gz}" print_msg "Tarball BUILDROOT is ${KIS_CUSTOM_BUILD}" filename_dir=$(dirname "${1}") cd "${filename_dir}" file_abspath="$(pwd)/${1##*/}" mkdir "${KIS_CUSTOM_BUILD}" 2> /dev/null cd "${KIS_CUSTOM_BUILD}" mkdir "src" "build" 2> /dev/null log_cmd tar -xzf "${file_abspath}" --strip-components=1 --directory "src" print_if_error "Failed untar of ${filename}" if [[ ${osxbuild_error} -ne 0 ]]; then exit fi KIS_BUILD_DIR="${KIS_CUSTOM_BUILD}/build" KIS_SRC_DIR="${KIS_CUSTOM_BUILD}/src" build_krita print_msg "Build done!" } install_krita () { # custom install provided if [[ -n "${1}" ]]; then KIS_BUILD_DIR="${1}" fi # Delete any "krita" named file in install # this helps avoid double versioned libraries log "About to delete krita libs from ${KIS_INSTALL_DIR}" waiting_fixed "Deleting files in 5 seconds" 5 find ${KIS_INSTALL_DIR} -type d -name "*krita*" | xargs -I FILE rm -rf FILE find ${KIS_INSTALL_DIR} \( -type f -or -type l \) -name "*krita*" | xargs -P4 -I FILE rm FILE print_msg "Install krita from ${KIS_BUILD_DIR}" log_cmd check_dir_path ${KIS_BUILD_DIR} cd "${KIS_BUILD_DIR}" osxbuild_error=$? print_if_error "could not cd to ${KIS_BUILD_DIR}" if [[ ${osxbuild_error} -ne 0 ]]; then exit fi make install # compile integrations if test ${OSTYPE} == "darwin*"; then cd ${KIS_BUILD_DIR}/krita/integration/kritaquicklook make install fi } # Runs all fixes for path and packages. # Historically only fixed boost @rpath fix_boost_rpath () { # helpers to define function only once fixboost_find () { for FILE in "${@}"; do if [[ -n "$(otool -L $FILE | grep boost)" ]]; then log "Fixing -- $FILE" log_cmd install_name_tool -change libboost_system.dylib @rpath/libboost_system.dylib $FILE fi done } batch_fixboost() { xargs -P4 -I FILE bash -c 'fixboost_find "FILE"' } export -f fixboost_find export -f log export -f log_cmd print_msg "Fixing boost in... ${KIS_INSTALL_DIR}" # install_name_tool -add_rpath ${KIS_INSTALL_DIR}/lib $BUILDROOT/$KRITA_INSTALL/bin/krita.app/Contents/MacOS/gmic_krita_qt log_cmd install_name_tool -add_rpath ${KIS_INSTALL_DIR}/lib ${KIS_INSTALL_DIR}/bin/krita.app/Contents/MacOS/krita # echo "Added rpath ${KIS_INSTALL_DIR}/lib to krita bin" # install_name_tool -add_rpath ${BUILDROOT}/deps/lib ${KIS_INSTALL_DIR}/bin/krita.app/Contents/MacOS/krita log_cmd install_name_tool -change libboost_system.dylib @rpath/libboost_system.dylib ${KIS_INSTALL_DIR}/bin/krita.app/Contents/MacOS/krita find -L "${KIS_INSTALL_DIR}" -name '*so' -o -name '*dylib' | batch_fixboost log "Fixing boost done!" } get_directory_fromargs() { local OSXBUILD_DIR="" for arg in "${@}"; do if [[ -d "${arg}" ]]; then OSXBUILD_DIR="${arg}" continue fi done echo "${OSXBUILD_DIR}" } print_usage () { printf "USAGE: osxbuild.sh [pkg|file]\n" printf "BUILDSTEPS:\t\t" printf "\n builddeps \t\t Run cmake step for 3rd party dependencies, optionally takes a [pkg] arg" printf "\n rebuilddeps \t\t Rerun make and make install step for 3rd party deps, optionally takes a [pkg] arg \t\t\t useful for cleaning install directory and quickly reinstall all deps." printf "\n fixboost \t\t Fixes broken boost \@rpath on OSX" printf "\n build \t\t\t Builds krita" printf "\n buildtarball \t\t Builds krita from provided [file] tarball" printf "\n clean \t\t\t Removes build and install directories to start fresh" printf "\n install \t\t Installs krita. Optionally accepts a [build dir] as argument \t\t\t this will install krita from given directory" printf "\n buildinstall \t\t Build and Installs krita, running fixboost after installing" printf "\n" printf "OPTIONS:\t\t" printf "\n \t --dirty \t [build] (old default) Keep old build directories before build to start fresh" printf "\n" } script_run() { if [[ ${#} -eq 0 ]]; then echo "ERROR: No option given!" print_usage exit 1 fi if [[ ${1} = "builddeps" ]]; then if [[ -z ${OSXBUILD_CLEAN} ]]; then dir_clean "${KIS_INSTALL_DIR}" dir_clean "${KIS_TBUILD_DIR}" fi build_3rdparty "${@:2}" exit elif [[ ${1} = "rebuilddeps" ]]; then rebuild_3rdparty "${@:2}" exit elif [[ ${1} = "fixboost" ]]; then if [[ -d ${1} ]]; then KIS_BUILD_DIR="${1}" fi fix_boost_rpath elif [[ ${1} = "build" ]]; then OSXBUILD_DIR=$(get_directory_fromargs "${@}") build_krita "${OSXBUILD_DIR}" exit elif [[ ${1} = "buildtarball" ]]; then # uncomment line to optionally change # install directory providing a third argument # This is not on by default as build success requires all # deps installed in the given dir beforehand. # KIS_INSTALL_DIR=${3} if [[ -f "${2}" && "${2:(-7)}" == ".tar.gz" ]]; then TARBALL_FILE="${2}" build_krita_tarball "${TARBALL_FILE}" if [[ -n "${OSXBUILD_TARBALLINSTALL}" ]]; then install_krita "${KIS_BUILD_DIR}" else print_msg "to install run osxbuild.sh install ${KIS_BUILD_DIR}" fi exit else log "File not a tarball tar.gz file" fi elif [[ ${1} = "clean" ]]; then # remove all build and install directories to start # a fresh install. this no different than using rm directly dir_clean "${KIS_TBUILD_DIR}" dir_clean "${$KIS_BUILD_DIR}" dir_clean "${KIS_INSTALL_DIR}" exit elif [[ ${1} = "install" ]]; then OSXBUILD_DIR=$(get_directory_fromargs "${@}") install_krita "${OSXBUILD_DIR}" fix_boost_rpath elif [[ ${1} = "buildinstall" ]]; then OSXBUILD_DIR=$(get_directory_fromargs "${@}") build_krita "${OSXBUILD_DIR}" install_krita "${OSXBUILD_DIR}" fix_boost_rpath "${OSXBUILD_DIR}" elif [[ ${1} = "test" ]]; then ${KIS_INSTALL_DIR}/bin/krita.app/Contents/MacOS/krita else echo "Option ${1} not supported" print_usage exit 1 fi } script_run ${parsed_args} diff --git a/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp b/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp index bdf4ddfca0..2154c6944f 100644 --- a/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp +++ b/plugins/extensions/resourcemanager/dlg_bundle_manager.cpp @@ -1,433 +1,438 @@ /* * Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "dlg_bundle_manager.h" #include "ui_wdgdlgbundlemanager.h" #include "resourcemanager.h" #include "dlg_create_bundle.h" #include #include #include #include #include #include #include #include "kis_action.h" #include #include #include #define ICON_SIZE 48 DlgBundleManager::DlgBundleManager(ResourceManager *resourceManager, KisActionManager* actionMgr, QWidget *parent) : KoDialog(parent) , m_page(new QWidget()) , m_ui(new Ui::WdgDlgBundleManager) , m_currentBundle(0) , m_resourceManager(resourceManager) { setCaption(i18n("Manage Resource Bundles")); m_ui->setupUi(m_page); setMainWidget(m_page); resize(m_page->sizeHint()); setButtons(Ok | Cancel); setDefaultButton(Ok); m_ui->listActive->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); m_ui->listActive->setSelectionMode(QAbstractItemView::SingleSelection); connect(m_ui->listActive, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*,QListWidgetItem*))); connect(m_ui->listActive, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*))); m_ui->listInactive->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); m_ui->listInactive->setSelectionMode(QAbstractItemView::SingleSelection); connect(m_ui->listInactive, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*,QListWidgetItem*))); connect(m_ui->listInactive, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*))); m_ui->bnAdd->setIcon(KisIconUtils::loadIcon("arrow-right")); connect(m_ui->bnAdd, SIGNAL(clicked()), SLOT(addSelected())); m_ui->bnRemove->setIcon(KisIconUtils::loadIcon("arrow-left")); connect(m_ui->bnRemove, SIGNAL(clicked()), SLOT(removeSelected())); m_ui->listBundleContents->setHeaderLabel(i18n("Resource")); m_ui->listBundleContents->setSelectionMode(QAbstractItemView::NoSelection); m_actionManager = actionMgr; refreshListData(); connect(m_ui->bnEditBundle, SIGNAL(clicked()), SLOT(editBundle())); connect(m_ui->bnImportBrushes, SIGNAL(clicked()), SLOT(slotImportResource())); connect(m_ui->bnImportGradients, SIGNAL(clicked()), SLOT(slotImportResource())); connect(m_ui->bnImportPalettes, SIGNAL(clicked()), SLOT(slotImportResource())); connect(m_ui->bnImportPatterns, SIGNAL(clicked()), SLOT(slotImportResource())); connect(m_ui->bnImportPresets, SIGNAL(clicked()), SLOT(slotImportResource())); connect(m_ui->bnImportWorkspaces, SIGNAL(clicked()), SLOT(slotImportResource())); connect(m_ui->bnImportBundles, SIGNAL(clicked()), SLOT(slotImportResource())); connect(m_ui->createBundleButton, SIGNAL(clicked()), SLOT(slotCreateBundle())); connect(m_ui->deleteBackupFilesButton, SIGNAL(clicked()), SLOT(slotDeleteBackupFiles())); connect(m_ui->openResourceFolderButton, SIGNAL(clicked()), SLOT(slotOpenResourceFolder())); } void DlgBundleManager::refreshListData() { KoResourceServer *bundleServer = KisResourceBundleServerProvider::instance()->resourceBundleServer(); m_ui->listInactive->clear(); m_ui->listActive->clear(); Q_FOREACH (const QString &f, bundleServer->blackListedFiles()) { KisResourceBundle *bundle = new KisResourceBundle(f); bundle->load(); if (bundle->valid()) { bundle->setInstalled(false); m_blacklistedBundles[f] = bundle; } } fillListWidget(m_blacklistedBundles.values(), m_ui->listInactive); Q_FOREACH (KisResourceBundle *bundle, bundleServer->resources()) { if (bundle->valid()) { m_activeBundles[bundle->filename()] = bundle; } } fillListWidget(m_activeBundles.values(), m_ui->listActive); } void DlgBundleManager::accept() { KoResourceServer *bundleServer = KisResourceBundleServerProvider::instance()->resourceBundleServer(); for (int i = 0; i < m_ui->listActive->count(); ++i) { QListWidgetItem *item = m_ui->listActive->item(i); QByteArray ba = item->data(Qt::UserRole).toByteArray(); QString name = item->text(); KisResourceBundle *bundle = bundleServer->resourceByMD5(ba); QMessageBox bundleFeedback; bundleFeedback.setIcon(QMessageBox::Warning); QString feedback = "bundlefeedback"; if (!bundle) { // Get it from the blacklisted bundles Q_FOREACH (KisResourceBundle *b2, m_blacklistedBundles.values()) { if (b2->md5() == ba) { bundle = b2; break; } } } if (bundle) { bool isKrita3Bundle = false; if (bundle->filename().endsWith("Krita_3_Default_Resources.bundle")) { isKrita3Bundle = true; KConfigGroup group = KSharedConfig::openConfig()->group("BundleHack"); group.writeEntry("HideKrita3Bundle", false); } else { if (!bundle->isInstalled()) { bundle->install(); //this removes the bundle from the blacklist and add it to the server without saving or putting it in front// if (!bundleServer->addResource(bundle, false, false)){ feedback = i18n("Couldn't add bundle \"%1\" to resource server", name); bundleFeedback.setText(feedback); bundleFeedback.exec(); } if (!isKrita3Bundle) { if (!bundleServer->removeFromBlacklist(bundle)) { feedback = i18n("Couldn't remove bundle \"%1\" from blacklist", name); bundleFeedback.setText(feedback); bundleFeedback.exec(); } } } else { if (!isKrita3Bundle) { bundleServer->removeFromBlacklist(bundle); } //let's assume that bundles that exist and are installed have to be removed from the blacklist, and if they were already this returns false, so that's not a problem. } } } else{ QString feedback = i18n("Bundle \"%1\" doesn't exist!", name); bundleFeedback.setText(feedback); bundleFeedback.exec(); } } for (int i = 0; i < m_ui->listInactive->count(); ++i) { QListWidgetItem *item = m_ui->listInactive->item(i); QByteArray ba = item->data(Qt::UserRole).toByteArray(); KisResourceBundle *bundle = bundleServer->resourceByMD5(ba); bool isKrits3Bundle = false; if (bundle) { if (bundle->filename().contains("Krita_3_Default_Resources.bundle")) { isKrits3Bundle = true; KConfigGroup group = KSharedConfig::openConfig()->group("BundleHack"); group.writeEntry("HideKrita3Bundle", true); } if (bundle->isInstalled()) { bundle->uninstall(); if (!isKrits3Bundle) { bundleServer->removeResourceAndBlacklist(bundle); } } } } KoDialog::accept(); } void DlgBundleManager::addSelected() { Q_FOREACH (QListWidgetItem *item, m_ui->listActive->selectedItems()) { m_ui->listInactive->addItem(m_ui->listActive->takeItem(m_ui->listActive->row(item))); } } void DlgBundleManager::removeSelected() { Q_FOREACH (QListWidgetItem *item, m_ui->listInactive->selectedItems()) { m_ui->listActive->addItem(m_ui->listInactive->takeItem(m_ui->listInactive->row(item))); } } void DlgBundleManager::itemSelected(QListWidgetItem *current, QListWidgetItem *) { if (!current) { m_ui->lblName->clear(); m_ui->lblAuthor->clear(); m_ui->lblEmail->clear(); m_ui->lblLicense->clear(); m_ui->lblWebsite->clear(); m_ui->lblDescription->clear(); m_ui->lblCreated->clear(); m_ui->lblUpdated->clear(); m_ui->lblPreview->setPixmap(QPixmap::fromImage(QImage())); m_ui->listBundleContents->clear(); m_ui->bnEditBundle->setEnabled(false); m_currentBundle = 0; } else { QByteArray ba = current->data(Qt::UserRole).toByteArray(); KoResourceServer *bundleServer = KisResourceBundleServerProvider::instance()->resourceBundleServer(); KisResourceBundle *bundle = bundleServer->resourceByMD5(ba); if (!bundle) { // Get it from the blacklisted bundles Q_FOREACH (KisResourceBundle *b2, m_blacklistedBundles.values()) { if (b2->md5() == ba) { bundle = b2; break; } } } if (bundle) { QFontMetrics metrics(this->font()); m_currentBundle = bundle; m_ui->bnEditBundle->setEnabled(true); m_ui->lblName->setText(bundle->name()); m_ui->lblAuthor->setText(metrics.elidedText(bundle->getMeta("author"), Qt::ElideRight, m_ui->lblAuthor->width())); m_ui->lblAuthor->setToolTip(bundle->getMeta("author")); m_ui->lblEmail->setText(metrics.elidedText(bundle->getMeta("email"), Qt::ElideRight, m_ui->lblEmail->width())); m_ui->lblEmail->setToolTip(bundle->getMeta("email")); m_ui->lblLicense->setText(metrics.elidedText(bundle->getMeta("license"), Qt::ElideRight, m_ui->lblLicense->width())); m_ui->lblLicense->setToolTip(bundle->getMeta("license")); m_ui->lblWebsite->setText(metrics.elidedText(bundle->getMeta("website"), Qt::ElideRight, m_ui->lblWebsite->width())); m_ui->lblWebsite->setToolTip(bundle->getMeta("website")); m_ui->lblDescription->setPlainText(bundle->getMeta("description")); if (QDateTime::fromString(bundle->getMeta("created"), Qt::ISODate).isValid()) { m_ui->lblCreated->setText(QDateTime::fromString(bundle->getMeta("created"), Qt::ISODate).toLocalTime().toString(Qt::DefaultLocaleShortDate)); } else { m_ui->lblCreated->setText(QDate::fromString(bundle->getMeta("created"), "dd/MM/yyyy").toString(Qt::DefaultLocaleShortDate)); } if (QDateTime::fromString(bundle->getMeta("updated"), Qt::ISODate).isValid()) { m_ui->lblUpdated->setText(QDateTime::fromString(bundle->getMeta("updated"), Qt::ISODate).toLocalTime().toString(Qt::DefaultLocaleShortDate)); } else { m_ui->lblUpdated->setText(QDate::fromString(bundle->getMeta("updated"), "dd/MM/yyyy").toString(Qt::DefaultLocaleShortDate)); } m_ui->lblPreview->setPixmap(QPixmap::fromImage(bundle->image().scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation))); m_ui->listBundleContents->clear(); Q_FOREACH (const QString & resType, bundle->resourceTypes()) { QTreeWidgetItem *toplevel = new QTreeWidgetItem(); if (resType == "gradients") { toplevel->setText(0, i18n("Gradients")); } else if (resType == "patterns") { toplevel->setText(0, i18n("Patterns")); } else if (resType == "brushes") { toplevel->setText(0, i18n("Brushes")); } else if (resType == "palettes") { toplevel->setText(0, i18n("Palettes")); } else if (resType == "workspaces") { toplevel->setText(0, i18n("Workspaces")); } else if (resType == "paintoppresets") { toplevel->setText(0, i18n("Brush Presets")); } else if (resType == "gamutmasks") { toplevel->setText(0, i18n("Gamut Masks")); } +#if defined HAVE_SEEXPR + else if (resType == "seexpr_scripts") { + toplevel->setText(0, i18n("SeExpr Scripts")); + } +#endif m_ui->listBundleContents->addTopLevelItem(toplevel); Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { QTreeWidgetItem *i = new QTreeWidgetItem(); i->setIcon(0, QIcon(QPixmap::fromImage(res->image()))); i->setText(0, res->name()); toplevel->addChild(i); } } } } else { m_currentBundle = 0; } } } void DlgBundleManager::itemSelected(QListWidgetItem *current) { itemSelected(current, 0); } void DlgBundleManager::editBundle() { if (m_currentBundle) { DlgCreateBundle dlg(m_currentBundle); m_activeBundles.remove(m_currentBundle->filename()); m_currentBundle = 0; if (dlg.exec() != QDialog::Accepted) { return; } m_currentBundle = m_resourceManager->saveBundle(dlg); refreshListData(); } } void DlgBundleManager::fillListWidget(QList bundles, QListWidget *w) { w->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); w->setSelectionMode(QAbstractItemView::MultiSelection); Q_FOREACH (KisResourceBundle *bundle, bundles) { QPixmap pixmap(ICON_SIZE, ICON_SIZE); pixmap.fill(Qt::gray); if (!bundle->image().isNull()) { QImage scaled = bundle->image().scaled(ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation); int x = (ICON_SIZE - scaled.width()) / 2; int y = (ICON_SIZE - scaled.height()) / 2; QPainter gc(&pixmap); gc.drawImage(x, y, scaled); gc.end(); } QListWidgetItem *item = new QListWidgetItem(pixmap, bundle->name()); item->setData(Qt::UserRole, bundle->md5()); w->addItem(item); } } void DlgBundleManager::slotImportResource() { if (m_actionManager) { QObject *button = sender(); QString buttonName = button->objectName(); KisAction *action = 0; if (buttonName == "bnImportBundles") { action = m_actionManager->actionByName("import_bundles"); } else if (buttonName == "bnImportBrushes") { action = m_actionManager->actionByName("import_brushes"); } else if (buttonName == "bnImportGradients") { action = m_actionManager->actionByName("import_gradients"); } else if (buttonName == "bnImportPalettes") { action = m_actionManager->actionByName("import_palettes"); } else if (buttonName == "bnImportPatterns") { action = m_actionManager->actionByName("import_patterns"); } else if (buttonName == "bnImportPresets") { action = m_actionManager->actionByName("import_presets"); } else if (buttonName == "bnImportWorkspaces") { action = m_actionManager->actionByName("import_workspaces"); } else { warnUI << "Unhandled bundle manager import button " << buttonName; return; } action->trigger(); refreshListData(); } } void DlgBundleManager::slotCreateBundle() { if (m_actionManager) { KisAction *action = m_actionManager->actionByName("create_bundle"); action->trigger(); refreshListData(); } } void DlgBundleManager::slotDeleteBackupFiles() { if (m_actionManager) { KisAction *action = m_actionManager->actionByName("edit_blacklist_cleanup"); action->trigger(); } } void DlgBundleManager::slotOpenResourceFolder() { if (m_actionManager) { KisAction *action = m_actionManager->actionByName("open_resources_directory"); action->trigger(); } } diff --git a/plugins/extensions/resourcemanager/dlg_create_bundle.cpp b/plugins/extensions/resourcemanager/dlg_create_bundle.cpp index 7e998437e9..c010f3ecaa 100644 --- a/plugins/extensions/resourcemanager/dlg_create_bundle.cpp +++ b/plugins/extensions/resourcemanager/dlg_create_bundle.cpp @@ -1,467 +1,505 @@ /* * Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "dlg_create_bundle.h" #include "ui_wdgdlgcreatebundle.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisResourceBundle.h" #define ICON_SIZE 48 DlgCreateBundle::DlgCreateBundle(KisResourceBundle *bundle, QWidget *parent) : KoDialog(parent) , m_ui(new Ui::WdgDlgCreateBundle) , m_bundle(bundle) { m_page = new QWidget(); m_ui->setupUi(m_page); setMainWidget(m_page); setFixedSize(m_page->sizeHint()); setButtons(Ok | Cancel); setDefaultButton(Ok); setButtonText(Ok, i18n("Save")); connect(m_ui->bnSelectSaveLocation, SIGNAL(clicked()), SLOT(selectSaveLocation())); KoDocumentInfo info; info.updateParameters(); if (bundle) { setCaption(i18n("Edit Resource Bundle")); m_ui->lblSaveLocation->setText(QFileInfo(bundle->filename()).absolutePath()); m_ui->editBundleName->setText(bundle->name()); m_ui->editAuthor->setText(bundle->getMeta("author")); m_ui->editEmail->setText(bundle->getMeta("email")); m_ui->editLicense->setText(bundle->getMeta("license")); m_ui->editWebsite->setText(bundle->getMeta("website")); m_ui->editDescription->document()->setPlainText(bundle->getMeta("description")); m_ui->lblPreview->setPixmap(QPixmap::fromImage(bundle->image().scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation))); Q_FOREACH (const QString & resType, bundle->resourceTypes()) { if (resType == "gradients") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedGradients << res->shortFilename(); } } } else if (resType == "patterns") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedPatterns << res->shortFilename(); } } } else if (resType == "brushes") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedBrushes << res->shortFilename(); } } } else if (resType == "palettes") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedPalettes << res->shortFilename(); } } } else if (resType == "workspaces") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedWorkspaces << res->shortFilename(); } } } else if (resType == "paintoppresets") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedPresets << res->shortFilename(); } } } else if (resType == "gamutmasks") { Q_FOREACH (const KoResource *res, bundle->resources(resType)) { if (res) { m_selectedGamutMasks << res->shortFilename(); } } } +#if defined HAVE_SEEXPR + else if (resType == "seexpr_scripts") { + Q_FOREACH (const KoResource *res, bundle->resources(resType)) { + if (res) { + m_selectedSeExprScripts << res->shortFilename(); + } + } + } +#endif } } else { setCaption(i18n("Create Resource Bundle")); KisConfig cfg(true); m_ui->editAuthor->setText(cfg.readEntry("BundleAuthorName", info.authorInfo("creator"))); m_ui->editEmail->setText(cfg.readEntry("BundleAuthorEmail", info.authorInfo("email"))); m_ui->editWebsite->setText(cfg.readEntry("BundleWebsite", "http://")); m_ui->editLicense->setText(cfg.readEntry("BundleLicense", "CC-BY-SA")); m_ui->lblSaveLocation->setText(cfg.readEntry("BundleExportLocation", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation))); } m_ui->bnAdd->setIcon(KisIconUtils::loadIcon("arrow-right")); connect(m_ui->bnAdd, SIGNAL(clicked()), SLOT(addSelected())); m_ui->bnRemove->setIcon(KisIconUtils::loadIcon("arrow-left")); connect(m_ui->bnRemove, SIGNAL(clicked()), SLOT(removeSelected())); m_ui->cmbResourceTypes->addItem(i18n("Brushes"), QString("brushes")); m_ui->cmbResourceTypes->addItem(i18n("Brush Presets"), QString("presets")); m_ui->cmbResourceTypes->addItem(i18n("Gradients"), QString("gradients")); m_ui->cmbResourceTypes->addItem(i18n("Gamut Masks"), QString("gamutmasks")); +#if defined HAVE_SEEXPR + m_ui->cmbResourceTypes->addItem(i18n("SeExpr Scripts"), QString("seexpr_scripts")); +#endif m_ui->cmbResourceTypes->addItem(i18n("Patterns"), QString("patterns")); m_ui->cmbResourceTypes->addItem(i18n("Palettes"), QString("palettes")); m_ui->cmbResourceTypes->addItem(i18n("Workspaces"), QString("workspaces")); connect(m_ui->cmbResourceTypes, SIGNAL(activated(int)), SLOT(resourceTypeSelected(int))); m_ui->tableAvailable->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); m_ui->tableAvailable->setSelectionMode(QAbstractItemView::ExtendedSelection); m_ui->tableSelected->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); m_ui->tableSelected->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(m_ui->bnGetPreview, SIGNAL(clicked()), SLOT(getPreviewImage())); resourceTypeSelected(0); } DlgCreateBundle::~DlgCreateBundle() { delete m_ui; } QString DlgCreateBundle::bundleName() const { return m_ui->editBundleName->text().replace(" ", "_"); } QString DlgCreateBundle::authorName() const { return m_ui->editAuthor->text(); } QString DlgCreateBundle::email() const { return m_ui->editEmail->text(); } QString DlgCreateBundle::website() const { return m_ui->editWebsite->text(); } QString DlgCreateBundle::license() const { return m_ui->editLicense->text(); } QString DlgCreateBundle::description() const { return m_ui->editDescription->document()->toPlainText(); } QString DlgCreateBundle::saveLocation() const { return m_ui->lblSaveLocation->text(); } QString DlgCreateBundle::previewImage() const { return m_previewImage; } void DlgCreateBundle::accept() { QString name = m_ui->editBundleName->text().remove(" "); if (name.isEmpty()) { m_ui->editBundleName->setStyleSheet(QString(" border: 1px solid red")); QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The resource bundle name cannot be empty.")); return; } else { QFileInfo fileInfo(m_ui->lblSaveLocation->text() + "/" + name + ".bundle"); if (fileInfo.exists() && !m_bundle) { m_ui->editBundleName->setStyleSheet("border: 1px solid red"); QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("A bundle with this name already exists.")); return; } else { if (!m_bundle) { KisConfig cfg(false); cfg.writeEntry("BunleExportLocation", m_ui->lblSaveLocation->text()); cfg.writeEntry("BundleAuthorName", m_ui->editAuthor->text()); cfg.writeEntry("BundleAuthorEmail", m_ui->editEmail->text()); cfg.writeEntry("BundleWebsite", m_ui->editWebsite->text()); cfg.writeEntry("BundleLicense", m_ui->editLicense->text()); } KoDialog::accept(); } } } void DlgCreateBundle::selectSaveLocation() { KoFileDialog dialog(this, KoFileDialog::OpenDirectory, "resourcebundlesavelocation"); dialog.setDefaultDir(m_ui->lblSaveLocation->text()); dialog.setCaption(i18n("Select a directory to save the bundle")); QString location = dialog.filename(); m_ui->lblSaveLocation->setText(location); } void DlgCreateBundle::addSelected() { int row = m_ui->tableAvailable->currentRow(); Q_FOREACH (QListWidgetItem *item, m_ui->tableAvailable->selectedItems()) { m_ui->tableSelected->addItem(m_ui->tableAvailable->takeItem(m_ui->tableAvailable->row(item))); QString resourceType = m_ui->cmbResourceTypes->itemData(m_ui->cmbResourceTypes->currentIndex()).toString(); if (resourceType == "brushes") { m_selectedBrushes.append(item->data(Qt::UserRole).toString()); } else if (resourceType == "presets") { m_selectedPresets.append(item->data(Qt::UserRole).toString()); } else if (resourceType == "gradients") { m_selectedGradients.append(item->data(Qt::UserRole).toString()); } else if (resourceType == "patterns") { m_selectedPatterns.append(item->data(Qt::UserRole).toString()); } else if (resourceType == "palettes") { m_selectedPalettes.append(item->data(Qt::UserRole).toString()); } else if (resourceType == "workspaces") { m_selectedWorkspaces.append(item->data(Qt::UserRole).toString()); } else if (resourceType == "gamutmasks") { m_selectedGamutMasks.append(item->data(Qt::UserRole).toString()); } +#if defined HAVE_SEEXPR + else if (resourceType == "seexpr_scripts") { + m_selectedSeExprScripts.append(item->data(Qt::UserRole).toString()); + } +#endif } m_ui->tableAvailable->setCurrentRow(row); } void DlgCreateBundle::removeSelected() { int row = m_ui->tableSelected->currentRow(); Q_FOREACH (QListWidgetItem *item, m_ui->tableSelected->selectedItems()) { m_ui->tableAvailable->addItem(m_ui->tableSelected->takeItem(m_ui->tableSelected->row(item))); QString resourceType = m_ui->cmbResourceTypes->itemData(m_ui->cmbResourceTypes->currentIndex()).toString(); if (resourceType == "brushes") { m_selectedBrushes.removeAll(item->data(Qt::UserRole).toString()); } else if (resourceType == "presets") { m_selectedPresets.removeAll(item->data(Qt::UserRole).toString()); } else if (resourceType == "gradients") { m_selectedGradients.removeAll(item->data(Qt::UserRole).toString()); } else if (resourceType == "patterns") { m_selectedPatterns.removeAll(item->data(Qt::UserRole).toString()); } else if (resourceType == "palettes") { m_selectedPalettes.removeAll(item->data(Qt::UserRole).toString()); } else if (resourceType == "workspaces") { m_selectedWorkspaces.removeAll(item->data(Qt::UserRole).toString()); } else if (resourceType == "gamutmasks") { m_selectedGamutMasks.removeAll(item->data(Qt::UserRole).toString()); } +#if defined HAVE_SEEXPR + else if (resourceType == "seexpr_scripts") { + m_selectedSeExprScripts.removeAll(item->data(Qt::UserRole).toString()); + } +#endif } m_ui->tableSelected->setCurrentRow(row); } QPixmap imageToIcon(const QImage &img) { QPixmap pixmap(ICON_SIZE, ICON_SIZE); pixmap.fill(); QImage scaled = img.scaled(ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation); int x = (ICON_SIZE - scaled.width()) / 2; int y = (ICON_SIZE - scaled.height()) / 2; QPainter gc(&pixmap); gc.drawImage(x, y, scaled); gc.end(); return pixmap; } void DlgCreateBundle::resourceTypeSelected(int idx) { QString resourceType = m_ui->cmbResourceTypes->itemData(idx).toString(); m_ui->tableAvailable->clear(); m_ui->tableSelected->clear(); if (resourceType == "brushes") { KisBrushResourceServer *server = KisBrushServer::instance()->brushServer(); Q_FOREACH (KisBrushSP res, server->resources()) { QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedBrushes.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } else if (resourceType == "presets") { KisPaintOpPresetResourceServer* server = KisResourceServerProvider::instance()->paintOpPresetServer(); Q_FOREACH (KisPaintOpPresetSP res, server->resources()) { QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedPresets.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } else if (resourceType == "gradients") { KoResourceServer* server = KoResourceServerProvider::instance()->gradientServer(); Q_FOREACH (KoResource *res, server->resources()) { if (res->filename()!="Foreground to Transparent" && res->filename()!="Foreground to Background") { //technically we should read from the file-name whether or not the file can be opened, but this works for now. The problem is making sure that bundle-resource know where they are stored.// //dbgKrita<filename(); QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedGradients.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } } else if (resourceType == "patterns") { KoResourceServer* server = KoResourceServerProvider::instance()->patternServer(); Q_FOREACH (KoResource *res, server->resources()) { QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedPatterns.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } else if (resourceType == "palettes") { KoResourceServer* server = KoResourceServerProvider::instance()->paletteServer(); Q_FOREACH (KoResource *res, server->resources()) { QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedPalettes.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } else if (resourceType == "workspaces") { KoResourceServer* server = KisResourceServerProvider::instance()->workspaceServer(); Q_FOREACH (KoResource *res, server->resources()) { QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedWorkspaces.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } else if (resourceType == "gamutmasks") { KoResourceServer* server = KoResourceServerProvider::instance()->gamutMaskServer(); Q_FOREACH (KoResource *res, server->resources()) { QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); item->setData(Qt::UserRole, res->shortFilename()); if (m_selectedGamutMasks.contains(res->shortFilename())) { m_ui->tableSelected->addItem(item); } else { m_ui->tableAvailable->addItem(item); } } } +#if defined HAVE_SEEXPR + else if (resourceType == "seexpr_scripts") { + KoResourceServer* server = KoResourceServerProvider::instance()->seExprScriptServer(); + Q_FOREACH (KoResource *res, server->resources()) { + QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name()); + item->setData(Qt::UserRole, res->shortFilename()); + + if (m_selectedSeExprScripts.contains(res->shortFilename())) { + m_ui->tableSelected->addItem(item); + } + else { + m_ui->tableAvailable->addItem(item); + } + } + } +#endif } void DlgCreateBundle::getPreviewImage() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "BundlePreviewImage"); dialog.setCaption(i18n("Select file to use as bundle icon")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import)); m_previewImage = dialog.filename(); QImage img(m_previewImage); img = img.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation); m_ui->lblPreview->setPixmap(QPixmap::fromImage(img)); } diff --git a/plugins/extensions/resourcemanager/dlg_create_bundle.h b/plugins/extensions/resourcemanager/dlg_create_bundle.h index 9253ecd74e..b7a3c1c1b7 100644 --- a/plugins/extensions/resourcemanager/dlg_create_bundle.h +++ b/plugins/extensions/resourcemanager/dlg_create_bundle.h @@ -1,83 +1,91 @@ /* * Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOBUNDLECREATIONWIDGET_H #define KOBUNDLECREATIONWIDGET_H #include +#include "config-seexpr.h" + class KisResourceBundle; namespace Ui { class WdgDlgCreateBundle; } class DlgCreateBundle : public KoDialog { Q_OBJECT public: explicit DlgCreateBundle(KisResourceBundle *bundle = 0, QWidget *parent = 0); ~DlgCreateBundle() override; QString bundleName() const; QString authorName() const; QString email() const; QString website() const; QString license() const; QString description() const; QString saveLocation() const; QString previewImage() const; QStringList selectedBrushes() const { return m_selectedBrushes; } QStringList selectedPresets() const { return m_selectedPresets; } QStringList selectedGradients() const { return m_selectedGradients; } QStringList selectedPatterns() const { return m_selectedPatterns; } QStringList selectedPalettes() const { return m_selectedPalettes; } QStringList selectedWorkspaces() const { return m_selectedWorkspaces; } QStringList selectedGamutMasks() const { return m_selectedGamutMasks; } +#if defined HAVE_SEEXPR + QStringList selectedSeExprScripts() const { return m_selectedSeExprScripts; } +#endif private Q_SLOTS: void accept() override; void selectSaveLocation(); void addSelected(); void removeSelected(); void resourceTypeSelected(int idx); void getPreviewImage(); private: QWidget *m_page; Ui::WdgDlgCreateBundle *m_ui; QStringList m_selectedBrushes; QStringList m_selectedPresets; QStringList m_selectedGradients; QStringList m_selectedPatterns; QStringList m_selectedPalettes; QStringList m_selectedWorkspaces; QStringList m_selectedGamutMasks; +#if defined HAVE_SEEXPR + QStringList m_selectedSeExprScripts; +#endif QString m_previewImage; KisResourceBundle *m_bundle; }; #endif // KOBUNDLECREATIONWIDGET_H diff --git a/plugins/extensions/resourcemanager/resourcemanager.cpp b/plugins/extensions/resourcemanager/resourcemanager.cpp index 0fe45768f9..27dc9bf702 100644 --- a/plugins/extensions/resourcemanager/resourcemanager.cpp +++ b/plugins/extensions/resourcemanager/resourcemanager.cpp @@ -1,337 +1,353 @@ /* * resourcemanager.cc -- Part of Krita * * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "resourcemanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include "config-seexpr.h" + #include "dlg_bundle_manager.h" #include "dlg_create_bundle.h" class ResourceManager::Private { public: Private() { brushServer = KisBrushServer::instance()->brushServer(); paintopServer = KisResourceServerProvider::instance()->paintOpPresetServer(); gradientServer = KoResourceServerProvider::instance()->gradientServer(); patternServer = KoResourceServerProvider::instance()->patternServer(); paletteServer = KoResourceServerProvider::instance()->paletteServer(); workspaceServer = KisResourceServerProvider::instance()->workspaceServer(); gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer(); +#if defined HAVE_SEEXPR + seExprScriptServer = KoResourceServerProvider::instance() ->seExprScriptServer(); +#endif } KisBrushResourceServer* brushServer; KisPaintOpPresetResourceServer * paintopServer; KoResourceServer* gradientServer; KoResourceServer *patternServer; KoResourceServer* paletteServer; KoResourceServer* workspaceServer; KoResourceServer* gamutMaskServer; +#if defined HAVE_SEEXPR + KoResourceServer* seExprScriptServer; +#endif }; K_PLUGIN_FACTORY_WITH_JSON(ResourceManagerFactory, "kritaresourcemanager.json", registerPlugin();) ResourceManager::ResourceManager(QObject *parent, const QVariantList &) : KisActionPlugin(parent) , d(new Private()) { KisAction *action = new KisAction(i18n("Import Bundles..."), this); addAction("import_bundles", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportBundles())); action = new KisAction(i18n("Import Brushes..."), this); addAction("import_brushes", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportBrushes())); action = new KisAction(i18n("Import Gradients..."), this); addAction("import_gradients", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportGradients())); action = new KisAction(i18n("Import Palettes..."), this); addAction("import_palettes", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportPalettes())); action = new KisAction(i18n("Import Patterns..."), this); addAction("import_patterns", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportPatterns())); action = new KisAction(i18n("Import Presets..."), this); addAction("import_presets", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportPresets())); action = new KisAction(i18n("Import Workspaces..."), this); addAction("import_workspaces", action); connect(action, SIGNAL(triggered()), this, SLOT(slotImportWorkspaces())); action = new KisAction(i18n("Create Resource Bundle..."), this); addAction("create_bundle", action); connect(action, SIGNAL(triggered()), this, SLOT(slotCreateBundle())); action = new KisAction(i18n("Manage Resources..."), this); addAction("manage_bundles", action); connect(action, SIGNAL(triggered()), this, SLOT(slotManageBundles())); } ResourceManager::~ResourceManager() { } void ResourceManager::slotCreateBundle() { DlgCreateBundle dlgCreateBundle; if (dlgCreateBundle.exec() != QDialog::Accepted) { return; } saveBundle(dlgCreateBundle); } KisResourceBundle *ResourceManager::saveBundle(const DlgCreateBundle &dlgCreateBundle) { QString bundlePath = dlgCreateBundle.saveLocation() + "/" + dlgCreateBundle.bundleName() + ".bundle"; KisResourceBundle *newBundle = new KisResourceBundle(bundlePath); newBundle->addMeta("name", dlgCreateBundle.bundleName()); newBundle->addMeta("author", dlgCreateBundle.authorName()); newBundle->addMeta("email", dlgCreateBundle.email()); newBundle->addMeta("license", dlgCreateBundle.license()); newBundle->addMeta("website", dlgCreateBundle.website()); newBundle->addMeta("description", dlgCreateBundle.description()); newBundle->setThumbnail(dlgCreateBundle.previewImage()); QStringList res = dlgCreateBundle.selectedBrushes(); Q_FOREACH (const QString &r, res) { KoResource *res = d->brushServer->resourceByFilename(r).data(); newBundle->addResource("kis_brushes", res->filename(), d->brushServer->assignedTagsList(res), res->md5()); } res = dlgCreateBundle.selectedGradients(); Q_FOREACH (const QString &r, res) { KoResource *res = d->gradientServer->resourceByFilename(r); newBundle->addResource("ko_gradients", res->filename(), d->gradientServer->assignedTagsList(res), res->md5()); } res = dlgCreateBundle.selectedPalettes(); Q_FOREACH (const QString &r, res) { KoResource *res = d->paletteServer->resourceByFilename(r); newBundle->addResource("ko_palettes", res->filename(), d->paletteServer->assignedTagsList(res), res->md5()); } res = dlgCreateBundle.selectedPatterns(); Q_FOREACH (const QString &r, res) { KoResource *res = d->patternServer->resourceByFilename(r); newBundle->addResource("ko_patterns", res->filename(), d->patternServer->assignedTagsList(res), res->md5()); } res = dlgCreateBundle.selectedPresets(); Q_FOREACH (const QString &r, res) { KisPaintOpPresetSP preset = d->paintopServer->resourceByFilename(r); KoResource *res = preset.data(); newBundle->addResource("kis_paintoppresets", res->filename(), d->paintopServer->assignedTagsList(res), res->md5()); KisPaintOpSettingsSP settings = preset->settings(); QStringList requiredFiles = settings->getStringList(KisPaintOpUtils::RequiredBrushFilesListTag); requiredFiles << settings->getString(KisPaintOpUtils::RequiredBrushFileTag); KritaUtils::makeContainerUnique(requiredFiles); Q_FOREACH (const QString &brushFile, requiredFiles) { KisBrush *brush = d->brushServer->resourceByFilename(brushFile).data(); if (brush) { newBundle->addResource("kis_brushes", brushFile, d->brushServer->assignedTagsList(brush), brush->md5()); } else { qWarning() << "There is no brush with name" << brushFile; } } } res = dlgCreateBundle.selectedWorkspaces(); Q_FOREACH (const QString &r, res) { KoResource *res = d->workspaceServer->resourceByFilename(r); newBundle->addResource("kis_workspaces", res->filename(), d->workspaceServer->assignedTagsList(res), res->md5()); } res = dlgCreateBundle.selectedGamutMasks(); Q_FOREACH (const QString &r, res) { KoResource *res = d->gamutMaskServer->resourceByFilename(r); newBundle->addResource("ko_gamutmasks", res->filename(), d->gamutMaskServer->assignedTagsList(res), res->md5()); } +#if defined HAVE_SEEXPR + res = dlgCreateBundle.selectedSeExprScripts(); + Q_FOREACH (const QString &r, res) { + KoResource *res = d->seExprScriptServer->resourceByFilename(r); + newBundle->addResource("kis_seexpr_scripts", res->filename(), d->gamutMaskServer->assignedTagsList(res), res->md5()); + } +#endif + newBundle->addMeta("fileName", bundlePath); newBundle->addMeta("created", QDateTime::currentDateTime().toOffsetFromUtc(0).toString(Qt::ISODate)); if (!newBundle->save()) { QMessageBox::critical(viewManager()->mainWindow(), i18nc("@title:window", "Krita"), i18n("Could not create the new bundle.")); } else { newBundle->setValid(true); if (QDir(KisResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation()) != QDir(QFileInfo(bundlePath).path())) { newBundle->setFilename(KisResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + "/" + dlgCreateBundle.bundleName() + ".bundle"); } if (KisResourceBundleServerProvider::instance()->resourceBundleServer()->resourceByName(newBundle->name())) { KisResourceBundleServerProvider::instance()->resourceBundleServer()->removeResourceFromServer( KisResourceBundleServerProvider::instance()->resourceBundleServer()->resourceByName(newBundle->name())); } KisResourceBundleServerProvider::instance()->resourceBundleServer()->addResource(newBundle, true); newBundle->load(); } return newBundle; } void ResourceManager::slotManageBundles() { DlgBundleManager* dlg = new DlgBundleManager(this, viewManager()->actionManager(), KisPart::instance()->currentMainwindow()); if (dlg->exec() != QDialog::Accepted) { return; } } QStringList ResourceManager::importResources(const QString &title, const QStringList &mimes) const { KoFileDialog dialog(viewManager()->mainWindow(), KoFileDialog::OpenFiles, "krita_resources"); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); dialog.setCaption(title); dialog.setMimeTypeFilters(mimes); return dialog.filenames(); } void ResourceManager::slotImportBrushes() { QStringList resources = importResources(i18n("Import Brushes"), QStringList() << "image/x-gimp-brush" << "image/x-gimp-x-gimp-brush-animated" << "image/x-adobe-brushlibrary" << "image/png" << "image/svg+xml"); Q_FOREACH (const QString &res, resources) { d->brushServer->importResourceFile(res); } } void ResourceManager::slotImportPresets() { QStringList resources = importResources(i18n("Import Presets"), QStringList() << "application/x-krita-paintoppreset"); Q_FOREACH (const QString &res, resources) { d->paintopServer->importResourceFile(res); } } void ResourceManager::slotImportGradients() { QStringList resources = importResources(i18n("Import Gradients"), QStringList() << "image/svg+xml" << "application/x-gimp-gradient"); Q_FOREACH (const QString &res, resources) { d->gradientServer->importResourceFile(res); } } void ResourceManager::slotImportBundles() { QStringList resources = importResources(i18n("Import Bundles"), QStringList() << "application/x-krita-bundle"); Q_FOREACH (const QString &res, resources) { KisResourceBundle *bundle = KisResourceBundleServerProvider::instance()->resourceBundleServer()->createResource(res); bundle->load(); if (bundle->valid()) { if (!bundle->install()) { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Could not install the resources for bundle %1.", res)); } } else { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Could not load bundle %1.", res)); } QFileInfo fi(res); QString newFilename = KisResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.completeBaseName() + bundle->defaultFileExtension(); QFileInfo fileInfo(newFilename); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(KisResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.completeBaseName() + QString("%1").arg(i) + bundle->defaultFileExtension()); i++; } bundle->setFilename(fileInfo.filePath()); QFile::copy(res, newFilename); KisResourceBundleServerProvider::instance()->resourceBundleServer()->addResource(bundle, false); } } void ResourceManager::slotImportPatterns() { QStringList resources = importResources(i18n("Import Patterns"), QStringList() << "image/png" << "image/svg+xml" << "application/x-gimp-pattern" << "image/jpeg" << "image/tiff" << "image/bmp" << "image/xpg"); Q_FOREACH (const QString &res, resources) { d->patternServer->importResourceFile(res); } } void ResourceManager::slotImportPalettes() { QStringList resources = importResources(i18n("Import Palettes"), QStringList() << "image/x-gimp-color-palette"); Q_FOREACH (const QString &res, resources) { d->paletteServer->importResourceFile(res); } } void ResourceManager::slotImportWorkspaces() { QStringList resources = importResources(i18n("Import Workspaces"), QStringList() << "application/x-krita-workspace"); Q_FOREACH (const QString &res, resources) { d->workspaceServer->importResourceFile(res); } } #include "resourcemanager.moc" diff --git a/plugins/generators/CMakeLists.txt b/plugins/generators/CMakeLists.txt index 1a560d4b66..086392feb3 100644 --- a/plugins/generators/CMakeLists.txt +++ b/plugins/generators/CMakeLists.txt @@ -1,3 +1,6 @@ add_subdirectory(solid) add_subdirectory(pattern) add_subdirectory(simplexnoise) +if(SeExpr2_FOUND) + add_subdirectory(seexpr) +endif() diff --git a/plugins/generators/seexpr/CMakeLists.txt b/plugins/generators/seexpr/CMakeLists.txt new file mode 100644 index 0000000000..6cef25102a --- /dev/null +++ b/plugins/generators/seexpr/CMakeLists.txt @@ -0,0 +1,21 @@ +set(kritaseexprgenerator_SOURCES + SeExprVariable.cpp + SeExprExpressionContext.cpp + generator.cpp + kis_wdg_seexpr.cpp + kis_wdg_seexpr_presets_save.cpp + ) + + +ki18n_wrap_ui(kritaseexprgenerator_SOURCES + wdgseexpr.ui + wdgseexprsavepreset.ui + ) + +add_library(kritaseexprgenerator MODULE ${kritaseexprgenerator_SOURCES}) + +target_link_libraries(kritaseexprgenerator kritaui SeExpr2::SeExpr2 SeExpr2::SeExpr2Editor) + +install(TARGETS kritaseexprgenerator DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) + +add_subdirectory(tests) diff --git a/plugins/generators/seexpr/SeExprExpressionContext.cpp b/plugins/generators/seexpr/SeExprExpressionContext.cpp new file mode 100644 index 0000000000..4f6853680a --- /dev/null +++ b/plugins/generators/seexpr/SeExprExpressionContext.cpp @@ -0,0 +1,32 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "SeExprExpressionContext.h" + +SeExprExpressionContext::SeExprExpressionContext(const QString &expr) + : SeExpr2::Expression(expr.toStdString()), m_vars(VariableMap()) +{ + +} + +SeExpr2::ExprVarRef *SeExprExpressionContext::resolveVar(const std::string &name) const +{ + return m_vars.value(name, nullptr); +} diff --git a/plugins/generators/seexpr/SeExprExpressionContext.h b/plugins/generators/seexpr/SeExprExpressionContext.h new file mode 100644 index 0000000000..6367e4fad4 --- /dev/null +++ b/plugins/generators/seexpr/SeExprExpressionContext.h @@ -0,0 +1,38 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include "SeExprVariable.h" + +class SeExprExpressionContext : public SeExpr2::Expression +{ +public: + typedef QMap VariableMap; + + VariableMap m_vars; + + SeExprExpressionContext(const QString &expr); + + virtual SeExpr2::ExprVarRef *resolveVar(const std::string &name) const override; +}; diff --git a/plugins/generators/seexpr/SeExprVariable.cpp b/plugins/generators/seexpr/SeExprVariable.cpp new file mode 100644 index 0000000000..4cc2883e20 --- /dev/null +++ b/plugins/generators/seexpr/SeExprVariable.cpp @@ -0,0 +1,43 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "SeExprVariable.h" + +SeExprVariable::SeExprVariable() + : SeExpr2::ExprVarRef(SeExpr2::ExprType().FP(1).Varying()), m_value(0) +{ + +} + +SeExprVariable::SeExprVariable(const double v) + : SeExpr2::ExprVarRef(SeExpr2::ExprType().FP(1).Varying()), m_value(v) +{ + +} + +void SeExprVariable::eval(double *result) +{ + result[0] = m_value; +} + +void SeExprVariable::eval(const char **) +{ + Q_ASSERT(!"Sanity check"); +} diff --git a/plugins/generators/seexpr/SeExprVariable.h b/plugins/generators/seexpr/SeExprVariable.h new file mode 100644 index 0000000000..72e72fcf27 --- /dev/null +++ b/plugins/generators/seexpr/SeExprVariable.h @@ -0,0 +1,35 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +struct SeExprVariable : public SeExpr2::ExprVarRef +{ +public: + double m_value; + + SeExprVariable(); + SeExprVariable(const double v); + + void eval(double *result) override; + void eval(const char **result) override; +}; diff --git a/plugins/generators/seexpr/generator.cpp b/plugins/generators/seexpr/generator.cpp new file mode 100644 index 0000000000..303a093033 --- /dev/null +++ b/plugins/generators/seexpr/generator.cpp @@ -0,0 +1,132 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "generator.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SeExprExpressionContext.h" + +#include "kis_wdg_seexpr.h" +#include "ui_wdgseexpr.h" + +K_PLUGIN_FACTORY_WITH_JSON(KritaSeExprGeneratorFactory, "generator.json", registerPlugin();) + +KritaSeExprGenerator::KritaSeExprGenerator(QObject *parent, const QVariantList &) + : QObject(parent) +{ + KisGeneratorRegistry::instance()->add(new KisSeExprGenerator()); +} + +KritaSeExprGenerator::~KritaSeExprGenerator() +{ +} + +KisSeExprGenerator::KisSeExprGenerator() : KisGenerator(id(), KoID("basic"), i18n("&SeExpr...")) +{ + setColorSpaceIndependence(FULLY_INDEPENDENT); + setSupportsPainting(false); +} + +KisFilterConfigurationSP KisSeExprGenerator::defaultConfiguration() const +{ + KisFilterConfigurationSP config = factoryConfiguration(); + + QVariant script(QStringLiteral(BASE_SCRIPT)); + config->setProperty("script", script); + return config; +} + +KisConfigWidget *KisSeExprGenerator::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool) const +{ + Q_UNUSED(dev); + return new KisWdgSeExpr(parent); +} + +void KisSeExprGenerator::generate(KisProcessingInformation dstInfo, + const QSize &size, + const KisFilterConfigurationSP config, + KoUpdater *progressUpdater) const +{ + KisPaintDeviceSP device = dstInfo.paintDevice(); + + Q_ASSERT(!device.isNull()); + Q_ASSERT(config); + + if (config) + { + QString script = config->getString("script"); + + QRect bounds = QRect(dstInfo.topLeft(), size); + const KoColorSpace *cs = device->colorSpace(); + KisSequentialIteratorProgress it(device, bounds, progressUpdater); + + SeExprExpressionContext expression(script); + + expression.m_vars["u"] = new SeExprVariable(); + expression.m_vars["v"] = new SeExprVariable(); + expression.m_vars["w"] = new SeExprVariable(bounds.width()); + expression.m_vars["h"] = new SeExprVariable(bounds.height()); + + if (expression.isValid() && expression.returnType().isFP(3)) + { + double pixel_stride_x = 1. / bounds.width(); + double pixel_stride_y = 1. / bounds.height(); + double &u = expression.m_vars["u"]->m_value; + double &v = expression.m_vars["v"]->m_value; + + while(it.nextPixel()) + { + u = pixel_stride_x * (it.x() + .5); + v = pixel_stride_y * (it.y() + .5); + + const qreal* value = expression.evalFP(); + QColor color; + + // SeExpr already outputs normalized RGB + color.setRedF(value[0]); + color.setGreenF(value[1]); + color.setBlueF(value[2]); + + cs->fromQColor(color, it.rawData()); + } + } + } +} + +#include "generator.moc" diff --git a/plugins/generators/seexpr/generator.h b/plugins/generators/seexpr/generator.h new file mode 100644 index 0000000000..7549fa71bb --- /dev/null +++ b/plugins/generators/seexpr/generator.h @@ -0,0 +1,68 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef SEEXPR_GENERATOR_H +#define SEEXPR_GENERATOR_H + +#include +#include +#include "generator/kis_generator.h" + +#define BASE_SCRIPT "$val=voronoi(5*[$u,$v,.5],4,.6,.2); \n \ +$color=ccurve($val,\n\ + 0.000, [0.141, 0.059, 0.051], 4,\n\ + 0.185, [0.302, 0.176, 0.122], 4,\n\ + 0.301, [0.651, 0.447, 0.165], 4,\n\ + 0.462, [0.976, 0.976, 0.976], 4);\n\ +$color\n\ +" + +class KisConfigWidget; + +class KritaSeExprGenerator : public QObject +{ + Q_OBJECT +public: + KritaSeExprGenerator(QObject *parent, const QVariantList &); + ~KritaSeExprGenerator() override; +}; + +class KisSeExprGenerator : public KisGenerator +{ +public: + + KisSeExprGenerator(); + + using KisGenerator::generate; + + void generate(KisProcessingInformation dst, + const QSize& size, + const KisFilterConfigurationSP config, + KoUpdater* progressUpdater + ) const override; + + static inline KoID id() { + return KoID("seexpr", i18n("SeExpr")); + } + KisFilterConfigurationSP defaultConfiguration() const override; + KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override; +}; + +#endif diff --git a/plugins/generators/seexpr/generator.json b/plugins/generators/seexpr/generator.json new file mode 100644 index 0000000000..9b09250462 --- /dev/null +++ b/plugins/generators/seexpr/generator.json @@ -0,0 +1,9 @@ +{ + "Id": "SeExpr Generator", + "Type": "Service", + "X-KDE-Library": "kritaseexprgenerator", + "X-KDE-ServiceTypes": [ + "Krita/Generator" + ], + "X-Krita-Version": "28" +} diff --git a/plugins/generators/seexpr/kis_wdg_seexpr.cpp b/plugins/generators/seexpr/kis_wdg_seexpr.cpp new file mode 100644 index 0000000000..5dbaa999fa --- /dev/null +++ b/plugins/generators/seexpr/kis_wdg_seexpr.cpp @@ -0,0 +1,320 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "generator.h" +#include "kis_wdg_seexpr.h" +#include "SeExprExpressionContext.h" +#include "ui_wdgseexpr.h" + +KisWdgSeExpr::KisWdgSeExpr(QWidget *parent) + : KisConfigWidget(parent), + updateCompressor(1000, KisSignalCompressor::Mode::POSTPONE), + m_currentPreset(new KisSeExprScript(i18n("Untitled"))), + m_saveDialog(new KisWdgSeExprPresetsSave(this)), + m_isCreatingPresetFromScratch(true) +{ + m_widget = new Ui_WdgSeExpr(); + m_widget->setupUi(this); + m_widget->txtEditor->setControlCollectionWidget(m_widget->wdgControls); + + m_widget->renameBrushPresetButton->setIcon(KisIconUtils::loadIcon("dirty-preset")); // edit icon + + m_widget->reloadPresetButton->setIcon(KisIconUtils::loadIcon("updateColorize")); + m_widget->reloadPresetButton->setToolTip(i18n("Reload the preset")); + m_widget->dirtyPresetIndicatorButton->setIcon(KisIconUtils::loadIcon("warning")); + m_widget->dirtyPresetIndicatorButton->setToolTip(i18n("The settings for this preset have changed from their default.")); + + KisDialogStateSaver::restoreState(m_widget->txtEditor, "krita/generators/seexpr"); + // Manually restore SeExpr state. KisDialogStateSaver uses setPlainText, not text itself + m_widget->txtEditor->setExpr(m_widget->txtEditor->exprTe->toPlainText()); + + m_widget->txtEditor->registerExtraVariable("$u", i18nc("SeExpr variable", "Normalized X axis coordinate of the image from its top-left corner")); + m_widget->txtEditor->registerExtraVariable("$v", i18nc("SeExpr variable", "Normalized Y axis coordinate of the image from its top-left corner")); + m_widget->txtEditor->registerExtraVariable("$w", i18nc("SeExpr variable", "Image width")); + m_widget->txtEditor->registerExtraVariable("$h", i18nc("SeExpr variable", "Image height")); + + m_widget->txtEditor->updateCompleter(); + + connect(m_widget->scriptSelectorWidget, SIGNAL(resourceSelected(KoResource*)), this, SLOT(slotResourceSelected(KoResource*))); + connect(m_saveDialog, SIGNAL(resourceSelected(KoResource*)), this, SLOT(slotResourceSelected(KoResource*))); + + connect(m_widget->renameBrushPresetButton, SIGNAL(clicked(bool)), + this, SLOT(slotRenamePresetActivated())); + connect(m_widget->cancelBrushNameUpdateButton, SIGNAL(clicked(bool)), + this, SLOT(slotRenamePresetDeactivated())); + connect(m_widget->updateBrushNameButton, SIGNAL(clicked(bool)), + this, SLOT(slotSaveRenameCurrentPreset())); + connect(m_widget->renameBrushNameTextField, SIGNAL(returnPressed()), + this, SLOT(slotSaveRenameCurrentPreset())); + + connect(m_widget->saveBrushPresetButton, SIGNAL(clicked()), + this, SLOT(slotSaveBrushPreset())); + connect(m_widget->saveNewBrushPresetButton, SIGNAL(clicked()), + this, SLOT(slotSaveNewBrushPreset())); + + connect(m_widget->reloadPresetButton, SIGNAL(clicked()), + this, SLOT(slotReloadPresetClicked())); + + connect(m_widget->txtEditor, SIGNAL(apply()), + &updateCompressor, SLOT(start())); + connect(m_widget->txtEditor, SIGNAL(preview()), + &updateCompressor, SLOT(start())); + + connect(&updateCompressor, SIGNAL(timeout()), this, SLOT(isValid())); + + togglePresetRenameUIActive(false); // reset the UI state of renaming a preset if we are changing presets + slotUpdatePresetSettings(); // disable everything until a preset is selected +} + +KisWdgSeExpr::~KisWdgSeExpr() +{ + KisDialogStateSaver::saveState(m_widget->txtEditor, "krita/generators/seexpr"); + delete m_saveDialog; + delete m_widget; +} + +inline const Ui_WdgSeExpr *KisWdgSeExpr::widget() const { + return m_widget; +} + +void KisWdgSeExpr::setConfiguration(const KisPropertiesConfigurationSP config) +{ + Q_ASSERT(!config->getString("script").isEmpty()); + QString script = config->getString("script"); + + m_widget->txtEditor->setExpr(script, true); +} + +KisPropertiesConfigurationSP KisWdgSeExpr::configuration() const +{ + KisFilterConfigurationSP config = new KisFilterConfiguration("seexpr", 1); + QVariant v(m_widget->txtEditor->getExpr()); + + config->setProperty("script", v); + + return config; +} + +void KisWdgSeExpr::slotResourceSelected(KoResource *r) +{ + KisSeExprScript *g = static_cast(r); + if (g) + { + // ALWAYS have a manageable copy of the preset + m_currentPreset = g->clone(); + + m_isCreatingPresetFromScratch = false; + + m_widget->txtEditor->setExpr(g->script(), true); + + QString formattedBrushName = g->name().replace("_", " "); + m_widget->currentBrushNameLabel->setText(formattedBrushName); + m_widget->renameBrushNameTextField->setText(g->name()); + // get the preset image and pop it into the thumbnail area on the top of the brush editor + m_widget->presetThumbnailicon->setPixmap(QPixmap::fromImage(g->image().scaled(55, 55, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + + togglePresetRenameUIActive(false); // reset the UI state of renaming a brush if we are changing brush presets + slotUpdatePresetSettings(); // check to see if the dirty preset icon needs to be shown + + updateCompressor.start(); + } +} + +void KisWdgSeExpr::slotRenamePresetActivated() +{ + togglePresetRenameUIActive(true); +} + +void KisWdgSeExpr::slotRenamePresetDeactivated() +{ + togglePresetRenameUIActive(false); +} + +void KisWdgSeExpr::togglePresetRenameUIActive(bool isRenaming) +{ + // This function doesn't really do anything except get the UI in a state to rename a brush preset + m_widget->renameBrushNameTextField->setVisible(isRenaming); + m_widget->updateBrushNameButton->setVisible(isRenaming); + m_widget->cancelBrushNameUpdateButton->setVisible(isRenaming); + + // hide these below areas while renaming + m_widget->currentBrushNameLabel->setVisible(!isRenaming); + m_widget->renameBrushPresetButton->setVisible(!isRenaming); + m_widget->saveBrushPresetButton->setEnabled(!isRenaming); + m_widget->saveBrushPresetButton->setVisible(!isRenaming); + m_widget->saveNewBrushPresetButton->setEnabled(!isRenaming); + m_widget->saveNewBrushPresetButton->setVisible(!isRenaming); +} + +void KisWdgSeExpr::slotSaveRenameCurrentPreset() +{ + slotReloadPresetClicked(); + + KisSeExprScriptSP curPreset = m_currentPreset; + + if (!curPreset) + return; + + KoResourceServer *rServer = KoResourceServerProvider::instance()->seExprScriptServer(); + QString saveLocation = rServer->saveLocation(); + + QString originalPresetName = curPreset->name(); + QString renamedPresetName = m_widget->renameBrushNameTextField->text(); + QString originalPresetPathAndFile = saveLocation + originalPresetName + curPreset->defaultFileExtension(); + QString renamedPresetPathAndFile = saveLocation + renamedPresetName + curPreset->defaultFileExtension(); + + KisSeExprScript* newPreset = curPreset->clone(); + newPreset->setFilename(renamedPresetPathAndFile); // this also contains the path + newPreset->setName(renamedPresetName); + newPreset->setImage(curPreset->image()); // use existing thumbnail (might not need to do this) + newPreset->setDirty(false); + rServer->addResource(newPreset); + + slotResourceSelected(newPreset); // refresh and select our freshly renamed resource + + // Now blacklist the original file + if (rServer->resourceByName(originalPresetName)) { + rServer->removeResourceAndBlacklist(curPreset.data()); + } + + togglePresetRenameUIActive(false); // this returns the UI to its original state after saving + + slotUpdatePresetSettings(); // update visibility of dirty preset and icon +} + +void KisWdgSeExpr::slotUpdatePresetSettings() +{ + // hide options on UI if we are creating a brush preset from scratch to prevent confusion + if (m_isCreatingPresetFromScratch) { + m_widget->presetThumbnailicon->setVisible(false); + m_widget->dirtyPresetIndicatorButton->setVisible(false); + m_widget->reloadPresetButton->setVisible(false); + m_widget->saveBrushPresetButton->setVisible(false); + m_widget->renameBrushPresetButton->setVisible(false); + } else { + // In SeExpr's case, there is never a default preset -- amyspark + if (!m_currentPreset) { + return; + } + + bool isPresetDirty = m_currentPreset->isDirty(); + + m_widget->presetThumbnailicon->setVisible(true); + // don't need to reload or overwrite a clean preset + m_widget->dirtyPresetIndicatorButton->setVisible(isPresetDirty); + m_widget->reloadPresetButton->setVisible(isPresetDirty); + m_widget->saveBrushPresetButton->setEnabled(isPresetDirty); + m_widget->renameBrushPresetButton->setVisible(true); + } +} + +void KisWdgSeExpr::slotSaveBrushPreset() +{ + KisFilterConfigurationSP currentConfiguration = static_cast(configuration().data()); + + m_saveDialog->useNewPresetDialog(false); // this mostly just makes sure we keep the existing brush preset name when saving + m_saveDialog->setCurrentPreset(m_currentPreset); + m_saveDialog->setCurrentRenderConfiguration(currentConfiguration); + m_saveDialog->loadExistingThumbnail(); // This makes sure we use the existing preset icon when updating the existing brush preset + m_saveDialog->savePreset(); + + // refresh the view settings so the brush doesn't appear dirty + slotUpdatePresetSettings(); +} + +void KisWdgSeExpr::slotSaveNewBrushPreset() +{ + KisFilterConfigurationSP currentConfiguration = static_cast(configuration().data()); + + m_saveDialog->useNewPresetDialog(true); + m_saveDialog->setCurrentPreset(m_currentPreset); + m_saveDialog->setCurrentRenderConfiguration(currentConfiguration); + m_saveDialog->showDialog(); +} + +void KisWdgSeExpr::slotReloadPresetClicked() +{ + auto *rserver = KoResourceServerProvider::instance()->seExprScriptServer(); + auto preset = rserver->resourceByName(m_currentPreset->name()); + if (preset) { + preset->load(); + + KIS_ASSERT(!m_currentPreset->isDirty()); + + slotResourceSelected(preset); + } +} + +void KisWdgSeExpr::isValid() +{ + QString script = m_widget->txtEditor->getExpr(); + SeExprExpressionContext expression(script); + + expression.setDesiredReturnType(SeExpr2::ExprType().FP(3)); + + expression.m_vars["u"] = new SeExprVariable(); + expression.m_vars["v"] = new SeExprVariable(); + expression.m_vars["w"] = new SeExprVariable(); + expression.m_vars["h"] = new SeExprVariable(); + + m_widget->txtEditor->clearErrors(); + + if (!expression.isValid()) + { + auto errors = expression.getErrors(); + + for (auto occurrence: errors) + { + QString message = ErrorMessages::message(occurrence.error); + for (auto arg : occurrence.ids) { + message = message.arg(QString::fromStdString(arg)); + } + m_widget->txtEditor->addError(occurrence.startPos, occurrence.endPos, message); + } + } + // Should not happen now, but I've left it for completeness's sake + else if (!expression.returnType().isFP(3)) + { + QString type = QString::fromStdString(expression.returnType().toString()); + m_widget->txtEditor->addError(1, 1, tr2i18n("Expected this script to output color, got '%1'").arg(type)); + } + else + { + m_widget->txtEditor->clearErrors(); + emit sigConfigurationUpdated(); + + if (m_currentPreset) + { + if (m_currentPreset->script() != m_widget->txtEditor->getExpr()) + { + m_currentPreset->setScript(m_widget->txtEditor->getExpr());m_currentPreset->setDirty(true); + } + slotUpdatePresetSettings(); + } + } +} diff --git a/plugins/generators/seexpr/kis_wdg_seexpr.h b/plugins/generators/seexpr/kis_wdg_seexpr.h new file mode 100644 index 0000000000..ecbd9a9878 --- /dev/null +++ b/plugins/generators/seexpr/kis_wdg_seexpr.h @@ -0,0 +1,67 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_WDG_SEEXPR_H +#define KIS_WDG_SEEXPR_H + +#include +#include +#include + +#include "kis_wdg_seexpr_presets_save.h" + +class Ui_WdgSeExpr; + +class KisWdgSeExpr : public KisConfigWidget +{ + Q_OBJECT +public: + KisWdgSeExpr(QWidget* parent = 0); + ~KisWdgSeExpr() override; + +public: + inline const Ui_WdgSeExpr* widget() const; + void setConfiguration(const KisPropertiesConfigurationSP) override; + KisPropertiesConfigurationSP configuration() const override; + +private Q_SLOTS: + void isValid(); + void slotResourceSelected(KoResource* resource); + void slotRenamePresetActivated(); + void slotRenamePresetDeactivated(); + void slotSaveRenameCurrentPreset(); + void slotUpdatePresetSettings(); + void slotSaveBrushPreset(); + void slotSaveNewBrushPreset(); + void slotReloadPresetClicked(); + +private: + Ui_WdgSeExpr *m_widget; + KisSignalCompressor updateCompressor; + KisSeExprScriptSP m_currentPreset; + KisWdgSeExprPresetsSave *m_saveDialog; + + bool m_isCreatingPresetFromScratch; + + void togglePresetRenameUIActive(bool isRenaming); +}; + +#endif + diff --git a/plugins/generators/seexpr/kis_wdg_seexpr_presets_save.cpp b/plugins/generators/seexpr/kis_wdg_seexpr_presets_save.cpp new file mode 100644 index 0000000000..c6f27dc775 --- /dev/null +++ b/plugins/generators/seexpr/kis_wdg_seexpr_presets_save.cpp @@ -0,0 +1,230 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "kis_wdg_seexpr_presets_save.h" + +KisWdgSeExprPresetsSave::KisWdgSeExprPresetsSave(QWidget *parent) + : KisWdgSeExprSavePreset(parent), + m_currentPreset(nullptr) +{ + // we will default to reusing the previous preset thumbnail + // have that checked by default, hide the other elements, and load the last preset image + connect(loadExistingThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadExistingThumbnail())); + connect(loadImageIntoThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadImageFromFile())); + connect(loadRenderFromScriptButton, SIGNAL(clicked(bool)), this, SLOT(renderScriptToThumbnail())); + connect(clearPresetThumbnailButton, SIGNAL(clicked(bool)), presetThumbnailWidget, SLOT(clear())); + + connect(buttons, SIGNAL(accepted()), this, SLOT(savePreset())); + connect(buttons, SIGNAL(rejected()), this, SLOT(close())); +} + +KisWdgSeExprPresetsSave::~KisWdgSeExprPresetsSave() +{ +} + +void KisWdgSeExprPresetsSave::setCurrentPreset(KisSeExprScriptSP resource) +{ + m_currentPreset = resource; +} + +void KisWdgSeExprPresetsSave::setCurrentRenderConfiguration(KisFilterConfigurationSP config) +{ + m_currentConfiguration = config; +} + +void KisWdgSeExprPresetsSave::showDialog() +{ + setModal(true); + + // set the name of the current preset area. + KisSeExprScriptSP preset = m_currentPreset; + + // UI will look a bit different if we are saving a new preset + if (m_useNewPresetDialog) { + setWindowTitle(i18n("Save New SeExpr Preset")); + newPresetNameTextField->setVisible(true); + clearPresetThumbnailButton->setVisible(true); + loadImageIntoThumbnailButton->setVisible(true); + + if (preset) { + if (!preset->name().isEmpty()) { + newPresetNameTextField->setText(preset->name().append(" ").append(i18n("Copy"))); + } + else { + newPresetNameTextField->clear(); + } + } + + } else { + setWindowTitle(i18n("Save SeExpr Preset")); + newPresetNameTextField->setVisible(false); + + if(preset) { + newPresetNameTextField->setText(preset->name()); + } + } + + if (preset) { + presetThumbnailWidget->setPixmap(QPixmap::fromImage(preset->image())); + } + + show(); +} + +void KisWdgSeExprPresetsSave::loadImageFromFile() +{ + // create a dialog to retrieve an image file. + KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); + dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); + QString filename = dialog.filename(); // the filename() returns the entire path & file name, not just the file name + + if (!filename.isEmpty()) { // empty if "cancel" is pressed + // take that file and load it into the thumbnail are + const QImage imageToLoad(filename); + + presetThumbnailWidget->clear(); // clear the background in case our new image has transparency + presetThumbnailWidget->setPixmap(QPixmap::fromImage(imageToLoad)); + } +} + +void KisWdgSeExprPresetsSave::loadExistingThumbnail() +{ + presetThumbnailWidget->setPixmap(QPixmap::fromImage(m_currentPreset->image())); +} + +void KisWdgSeExprPresetsSave::renderScriptToThumbnail() +{ + if (m_currentConfiguration) + { + // TODO add some sort of progress marker? + KisPaintDeviceSP src = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); + KisFillPainter fillPainter(src); + fillPainter.fillRect(0, 0, 256, 256, m_currentConfiguration); + + QImage thumbnail = src->convertToQImage(nullptr, 0, 0, 256, 256); + presetThumbnailWidget->setPixmap(QPixmap::fromImage(thumbnail)); + } +} + +void KisWdgSeExprPresetsSave::savePreset() +{ + KisSeExprScriptSP curPreset = m_currentPreset; + if (!curPreset) + return; + + KisSeExprScript* oldPreset = curPreset->clone(); // tags are not cloned with this + oldPreset->load(); + auto *rServer = KoResourceServerProvider::instance()->seExprScriptServer(); + QString saveLocation = rServer->saveLocation(); + + // if we are saving a new brush, use what we type in for the input + QString presetName = m_useNewPresetDialog ? newPresetNameTextField->text() : curPreset->name(); + QString currentPresetFileName = saveLocation + presetName.replace(" ", "_") + curPreset->defaultFileExtension(); + bool isSavingOverExistingPreset = rServer->resourceByName(presetName); + + // make a back up of the existing preset if we are saving over it + if (isSavingOverExistingPreset) { + QString currentDate = QDate::currentDate().toString(Qt::ISODate); + QString currentTime = QTime::currentTime().toString(Qt::ISODate).remove(QChar(':')); + QString presetFilename = saveLocation + presetName.replace(" ", "_") + "_backup_" + currentDate + "-" + currentTime + oldPreset->defaultFileExtension(); + oldPreset->setFilename(presetFilename); + oldPreset->setName(presetName); + oldPreset->setDirty(false); + oldPreset->setValid(true); + + // add backup resource to the blacklist + rServer->addResource(oldPreset); + rServer->removeResourceAndBlacklist(oldPreset); + + QStringList tags; + tags = rServer->assignedTagsList(curPreset.data()); + Q_FOREACH (const QString &tag, tags) { + rServer->addTag(oldPreset, tag); + } + } + + if (m_useNewPresetDialog) { + KisSeExprScript* newPreset = curPreset->clone(); + newPreset->setFilename(currentPresetFileName); + newPreset->setName(presetName); + newPreset->setImage(presetThumbnailWidget->pixmap()->toImage()); + newPreset->setScript(m_currentConfiguration->getString("script")); + newPreset->setDirty(false); + newPreset->setValid(true); + + // keep tags if we are saving over existing preset + if (isSavingOverExistingPreset) { + QStringList tags; + tags = rServer->assignedTagsList(curPreset.data()); + Q_FOREACH (const QString &tag, tags) { + rServer->addTag(newPreset, tag); + } + } + + rServer->addResource(newPreset); + + // trying to get brush preset to load after it is created + emit resourceSelected(newPreset); + } else { // saving a preset that is replacing an existing one + + if (curPreset->filename().contains(saveLocation) == false || curPreset->filename().contains(presetName) == false) { + rServer->removeResourceAndBlacklist(curPreset.data()); + curPreset->setFilename(currentPresetFileName); + curPreset->setName(presetName); + } + + if (!rServer->resourceByFilename(curPreset->filename())) { + // this is necessary so that we can get the preset afterwards. + rServer->addResource(curPreset.data(), false, false); + rServer->removeFromBlacklist(curPreset.data()); + } + if (curPreset->image().isNull()) { + curPreset->setImage(presetThumbnailWidget->pixmap()->toImage()); + } + + curPreset->setScript(m_currentConfiguration->getString("script")); + + // we should not load() the brush right after saving because it will reset all our saved + // eraser size and opacity values + curPreset->save(); + } + + // HACK ALERT! the server does not notify the observers + // automatically, so we need to call theupdate manually! + rServer->tagCategoryMembersChanged(); + + close(); // we are done... so close the save brush dialog +} + +void KisWdgSeExprPresetsSave::useNewPresetDialog(bool show) +{ + m_useNewPresetDialog = show; +} + +#include "moc_kis_wdg_seexpr_presets_save.cpp" diff --git a/plugins/generators/seexpr/kis_wdg_seexpr_presets_save.h b/plugins/generators/seexpr/kis_wdg_seexpr_presets_save.h new file mode 100644 index 0000000000..98a87fcc1a --- /dev/null +++ b/plugins/generators/seexpr/kis_wdg_seexpr_presets_save.h @@ -0,0 +1,77 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_WDG_SEEXPR_PRESETS_SAVE_H +#define KIS_WDG_SEEXPR_PRESETS_SAVE_H + +#include +#include + +#include "ui_wdgseexprsavepreset.h" + +/** + * Dialog for saving SeExpr presets. + * + * Based on Scott Petrovic's KisPaintOpPresetSaveDialog. + */ +class KisWdgSeExprSavePreset : public QDialog, public Ui::WdgSeExprSavePreset +{ + Q_OBJECT + +public: + KisWdgSeExprSavePreset(QWidget *parent) + : QDialog(parent) + { + setupUi(this); + } +}; + +class KisWdgSeExprPresetsSave : public KisWdgSeExprSavePreset +{ + Q_OBJECT + +public: + KisWdgSeExprPresetsSave(QWidget *parent); + virtual ~KisWdgSeExprPresetsSave(); + + void showDialog(); + + /// determines if we should show the save as dialog (true) or save in the background (false) + void useNewPresetDialog(bool show); + +Q_SIGNALS: + // Triggers after resource is saved + void resourceSelected(KoResource* resource); + +public Q_SLOTS: + void setCurrentPreset(KisSeExprScriptSP resource); + void setCurrentRenderConfiguration(KisFilterConfigurationSP config); + void loadImageFromFile(); + void savePreset(); + void loadExistingThumbnail(); + void renderScriptToThumbnail(); + +private: + bool m_useNewPresetDialog; + KisSeExprScriptSP m_currentPreset; + KisFilterConfigurationSP m_currentConfiguration; +}; + +#endif diff --git a/plugins/generators/seexpr/tests/CMakeLists.txt b/plugins/generators/seexpr/tests/CMakeLists.txt new file mode 100644 index 0000000000..a7e1acda3a --- /dev/null +++ b/plugins/generators/seexpr/tests/CMakeLists.txt @@ -0,0 +1,13 @@ +set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) + +include_directories( + ${CMAKE_SOURCE_DIR}/sdk/tests +) + +macro_add_unittest_definitions() + +ecm_add_tests( + kis_seexpr_generator_test.cpp + + NAME_PREFIX "plugins-generators-" + LINK_LIBRARIES kritaimage Qt5::Test) diff --git a/plugins/generators/seexpr/tests/data/noisecolor2.png b/plugins/generators/seexpr/tests/data/noisecolor2.png new file mode 100644 index 0000000000..0f7971f034 Binary files /dev/null and b/plugins/generators/seexpr/tests/data/noisecolor2.png differ diff --git a/plugins/generators/seexpr/tests/kis_seexpr_generator_test.cpp b/plugins/generators/seexpr/tests/kis_seexpr_generator_test.cpp new file mode 100644 index 0000000000..340036364d --- /dev/null +++ b/plugins/generators/seexpr/tests/kis_seexpr_generator_test.cpp @@ -0,0 +1,71 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kis_seexpr_generator_test.h" + +void KisSeExprGeneratorTest::initTestCase() +{ + KisGeneratorRegistry::instance(); +} + +void KisSeExprGeneratorTest::testGeneration() +{ + + KisGeneratorSP generator = KisGeneratorRegistry::instance()->get("seexpr"); + QVERIFY(generator); + + KisFilterConfigurationSP config = generator->defaultConfiguration(); + QVERIFY(config); + + const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); + KisPaintDeviceSP dev = new KisPaintDevice(cs); + KisProcessingInformation test(dev, QPoint(0, 0), KisSelectionSP()); + TestUtil::TestProgressBar *bar = new TestUtil::TestProgressBar(); + KoProgressUpdater *pu = new KoProgressUpdater(bar); + KoUpdaterPtr updater = pu->startSubtask(); + + QSize testSize(256, 256); + + generator->generate(test, testSize, config, updater); + + QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "noisecolor2.png"); + + QPoint errpoint; + if (!TestUtil::compareQImages(errpoint, qimage, dev->convertToQImage(0, 0, 0, testSize.width(), testSize.height()))) { + dev->convertToQImage(0, 0, 0, testSize.width(), testSize.height()).save("filtertest.png"); + QFAIL(QString("Failed to create image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); + } + delete pu; + delete bar; +} + +KISTEST_MAIN(KisSeExprGeneratorTest) diff --git a/plugins/generators/seexpr/tests/kis_seexpr_generator_test.h b/plugins/generators/seexpr/tests/kis_seexpr_generator_test.h new file mode 100644 index 0000000000..7b08c126e6 --- /dev/null +++ b/plugins/generators/seexpr/tests/kis_seexpr_generator_test.h @@ -0,0 +1,36 @@ +/* + * This file is part of Krita + * + * Copyright (c) 2020 L. E. Segovia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_SEEXPR_GENERATOR_TEST_H +#define KIS_SEEXPR_GENERATOR_TEST_H + +#include + +class KisSeExprGeneratorTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void testGeneration(); +}; + +#endif + diff --git a/plugins/generators/seexpr/wdgseexpr.ui b/plugins/generators/seexpr/wdgseexpr.ui new file mode 100644 index 0000000000..88a5a9da3d --- /dev/null +++ b/plugins/generators/seexpr/wdgseexpr.ui @@ -0,0 +1,420 @@ + + + WdgSeExpr + + + + 0 + 0 + 500 + 480 + + + + + 500 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + + Script + + + + + + + 0 + 0 + + + + + + + + + Options + + + + + + + + + + + 0 + 0 + + + + + 55 + 55 + + + + + + + true + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + 0 + + + QLayout::SetMinimumSize + + + + + 10 + + + QLayout::SetMinimumSize + + + + + + 10 + 75 + true + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + + + + + + + + + 180 + 0 + + + + + + + + + 0 + 0 + + + + Save + + + + + + + Cancel + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + + + + + 0 + + + + + Save New Brush Preset... + + + + + + + Overwrite Brush + + + + + + + 6 + + + 0 + + + 6 + + + + + Qt::Horizontal + + + + 0 + 23 + + + + + + + + + 0 + 0 + + + + + 23 + 23 + + + + + 23 + 23 + + + + + + + + + + + + 0 + 0 + + + + + 23 + 23 + + + + + 23 + 23 + + + + + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + + 0 + 5 + + + + true + + + + + 0 + 0 + 466 + 172 + + + + + 0 + 0 + + + + + + + + + + 0 + 4 + + + + + + + + + + + + + KisSeExprScriptChooser + QWidget +
kis_seexpr_script_chooser.h
+ 1 +
+ + ExprControlCollection + QWidget +
SeExpr2/UI/ExprControlCollection.h
+ 1 +
+ + ExprEditor + QWidget +
SeExpr2/UI/ExprEditor.h
+ 1 +
+
+ + +
diff --git a/plugins/generators/seexpr/wdgseexprsavepreset.ui b/plugins/generators/seexpr/wdgseexprsavepreset.ui new file mode 100644 index 0000000000..bce89dbb98 --- /dev/null +++ b/plugins/generators/seexpr/wdgseexprsavepreset.ui @@ -0,0 +1,161 @@ + + + WdgSeExprSavePreset + + + + 0 + 0 + 457 + 332 + + + + + 0 + 0 + + + + Save SeExpr Preset + + + + 13 + + + + + + + Preset name: + + + + + + + + 220 + 0 + + + + + + + + + + 18 + + + + + 2 + + + + + + 0 + 0 + + + + + 200 + 200 + + + + + 200 + 200 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 6 + + + + + Load Existing Thumbnail + + + + + + + Load Image + + + + + + + &Render Script to Thumbnail + + + + + + + Clear Thumbnail + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + + +