diff --git a/3rdparty/ext_gmic/CMakeLists.txt b/3rdparty/ext_gmic/CMakeLists.txt index 6117b07efe..7be84ac03a 100644 --- a/3rdparty/ext_gmic/CMakeLists.txt +++ b/3rdparty/ext_gmic/CMakeLists.txt @@ -1,45 +1,38 @@ SET(PREFIX_ext_gmic "${EXTPREFIX}" ) # Download the gmic sources -if (WIN32) - # gmic uses a shell script with wget to download some files which won't work - # on Windows, so we use CMake to download them instead - set(_GMIC_BUILD_COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/gmic-downloads.cmake) -else (WIN32) - set(_GMIC_BUILD_COMMAND make -C src CImg.h gmic_stdlib.h) -endif (WIN32) ExternalProject_Add( ext_gmic_base DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - GIT_REPOSITORY https://github.com/dtschump/gmic.git - GIT_TAG master - GIT_SHALLOW 1 + URL http://gmic.eu/files/source/gmic_2.1.3.tar.gz + URL_HASH SHA1=f0832766f009a74287bb9fdbe013f4a84db1ad76 SOURCE_DIR gmic CONFIGURE_COMMAND "" - BUILD_COMMAND ${_GMIC_BUILD_COMMAND} + BUILD_COMMAND "" INSTALL_COMMAND "" BUILD_IN_SOURCE 1 ) # Download and build gmic-qt # FIXME: Forcing CMAKE_BUILD_TYPE to Release ExternalProject_Add( ext_gmic_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL https://github.com/c-koi/gmic-qt/archive/v.211.tar.gz - URL_HASH SHA1=776db5f77a4044711a9c4a4b0c6533556f4b810d + URL https://github.com/c-koi/gmic-qt/archive/v.213.tar.gz + DOWNLOAD_NAME gmic-qt_2.1.3.tar.gz + URL_HASH SHA1=8c6489da2ce44ae3eae6ab7e9cf7b45716c0aacd SOURCE_DIR gmic-qt INSTALL_DIR ${PREFIX_ext_gmic} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_gmic} -DGMIC_QT_HOST=krita -DCMAKE_BUILD_TYPE=Release ${GLOBAL_PROFILE} UPDATE_COMMAND "" INSTALL_COMMAND ${CMAKE_COMMAND} -E copy /gmic_krita_qt${CMAKE_EXECUTABLE_SUFFIX} /bin/gmic_krita_qt${CMAKE_EXECUTABLE_SUFFIX} DEPENDS ext_gmic_base ) add_custom_target(ext_gmic) add_dependencies(ext_gmic ext_gmic_qt) diff --git a/3rdparty/ext_gmic/gmic-downloads.cmake b/3rdparty/ext_gmic/gmic-downloads.cmake deleted file mode 100644 index 7bd4116961..0000000000 --- a/3rdparty/ext_gmic/gmic-downloads.cmake +++ /dev/null @@ -1,7 +0,0 @@ -message(STATUS "> Retrieve G'MIC Standard Library...") -file(DOWNLOAD http://gmic.eu/gmic_stdlib.h ${CMAKE_CURRENT_BINARY_DIR}/src/gmic_stdlib.h) -message(STATUS " done!") - -message(STATUS "> Retrieve CImg Library...") -file(DOWNLOAD https://github.com/dtschump/CImg/raw/master/CImg.h ${CMAKE_CURRENT_BINARY_DIR}/src/CImg.h) -message(STATUS " done!") diff --git a/CMakeLists.txt b/CMakeLists.txt index 60cf858568..1709e59d59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,692 +1,692 @@ project(krita) message(STATUS "Using CMake version: ${CMAKE_VERSION}") cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(MIN_QT_VERSION 5.6.0) option(OVERRIDE_QT_VERSION "Use this to make it possible to build with Qt < 5.6.0. There will be bugs." OFF) if (OVERRIDE_QT_VERSION) set(MIN_QT_VERSION 5.4.0) endif() set(MIN_FRAMEWORKS_VERSION 5.7.0) if (POLICY CMP0002) cmake_policy(SET CMP0002 OLD) endif() if (POLICY CMP0017) cmake_policy(SET CMP0017 NEW) endif () if (POLICY CMP0022) cmake_policy(SET CMP0022 OLD) endif () if (POLICY CMP0026) cmake_policy(SET CMP0026 OLD) endif() if (POLICY CMP0042) cmake_policy(SET CMP0042 NEW) endif() if (POLICY CMP0046) cmake_policy(SET CMP0046 OLD) endif () if (POLICY CMP0059) cmake_policy(SET CMP0059 OLD) endif() if (POLICY CMP0063) cmake_policy(SET CMP0063 OLD) endif() if (POLICY CMP0054) cmake_policy(SET CMP0054 OLD) endif() if (POLICY CMP0064) cmake_policy(SET CMP0064 OLD) endif() if (APPLE) set(APPLE_SUPPRESS_X11_WARNING TRUE) set(KDE_SKIP_RPATH_SETTINGS TRUE) set(CMAKE_MACOSX_RPATH 1) set(BUILD_WITH_INSTALL_RPATH 1) add_definitions(-mmacosx-version-min=10.9 -Wno-macro-redefined -Wno-deprecated-register) endif() if (LINUX) if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 AND NOT WINDOWS) add_definitions(-Werror=delete-incomplete) endif() endif() ###################### ####################### ## Constants defines ## ####################### ###################### # define common versions of Krita applications, used to generate kritaversion.h # update these version for every release: set(KRITA_VERSION_STRING "4.0.0-pre-alpha") set(KRITA_STABLE_VERSION_MAJOR 4) # 3 for 3.x, 4 for 4.x, etc. set(KRITA_STABLE_VERSION_MINOR 0) # 0 for 3.0, 1 for 3.1, etc. set(KRITA_VERSION_RELEASE 0) # 88 for pre-alpha, 89 for Alpha, increase for next test releases, set 0 for first Stable, etc. set(KRITA_ALPHA 1) # uncomment only for Alpha #set(KRITA_BETA 1) # uncomment only for Beta #set(KRITA_RC 1) # uncomment only for RC set(KRITA_YEAR 2017) # update every year if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC) set(KRITA_STABLE 1) # do not edit endif() message(STATUS "Krita version: ${KRITA_VERSION_STRING}") # Define the generic version of the Krita libraries here # This makes it easy to advance it when the next Krita release comes. # 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series # (2.x) so we're starting with 15 in 3.x series, 16 in 4.x series if(KRITA_STABLE_VERSION_MAJOR EQUAL 4) math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 16") else() # let's make sure we won't forget to update the "16" message(FATAL_ERROR "Reminder: please update offset == 16 used to compute GENERIC_KRITA_LIB_VERSION_MAJOR to something bigger") endif() set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0") set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro") # fetch git revision for the current build set(KRITA_GIT_SHA1_STRING "") set(KRITA_GIT_BRANCH_STRING "") include(GetGitRevisionDescription) get_git_head_revision(GIT_REFSPEC GIT_SHA1) get_git_branch(GIT_BRANCH) if(GIT_SHA1 AND GIT_BRANCH) string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1) set(KRITA_GIT_SHA1_STRING ${GIT_SHA1}) set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH}) endif() if(NOT DEFINED RELEASE_BUILD) # estimate mode by CMAKE_BUILD_TYPE content if not set on cmdline string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_TOLOWER) set(RELEASE_BUILD_TYPES "release" "relwithdebinfo" "minsizerel") list(FIND RELEASE_BUILD_TYPES "${CMAKE_BUILD_TYPE_TOLOWER}" INDEX) if (INDEX EQUAL -1) set(RELEASE_BUILD FALSE) else() set(RELEASE_BUILD TRUE) endif() endif() message(STATUS "Release build: ${RELEASE_BUILD}") # create test make targets enable_testing() # collect list of broken tests, empty here to start fresh with each cmake run set(KRITA_BROKEN_TESTS "" CACHE INTERNAL "KRITA_BROKEN_TESTS") ############ ############# ## Options ## ############# ############ include(FeatureSummary) if (WIN32) option(USE_DRMINGW "Support the Dr. Mingw crash handler (only on windows)" ON) add_feature_info("Dr. Mingw" USE_DRMINGW "Enable the Dr. Mingw crash handler") if (MINGW) option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON) add_feature_info("Linker Security Flags" USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags") if (USE_MINGW_HARDENING_LINKER) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") # Enable high-entropy ASLR for 64-bit # The image base has to be >4GB for HEASLR to be enabled. # The values used here are kind of arbitrary. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") else (USE_MINGW_HARDENING_LINKER) message(WARNING "Linker Security Flags not enabled!") endif (USE_MINGW_HARDENING_LINKER) endif (MINGW) endif () option(HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal." ON) configure_file(config-hide-safe-asserts.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hide-safe-asserts.h) add_feature_info("Safe Asserts" HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal.") option(FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true." OFF) add_feature_info("Foundation Build" FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true.") option(KRITA_ENABLE_BROKEN_TESTS "Enable tests that are marked as broken" OFF) add_feature_info("Enable Broken Tests" KRITA_ENABLE_BROKEN_TESTS "Runs broken test when \"make test\" is invoked (use -DKRITA_ENABLE_BROKEN_TESTS=ON to enable).") include(MacroJPEG) ########################################################### ## Look for Python3. It is also searched by KF5, ## ## so we should request the correct version in advance ## ########################################################### function(TestCompileLinkPythonLibs OUTPUT_VARNAME) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES ${PYTHON_INCLUDE_PATH}) set(CMAKE_REQUIRED_LIBRARIES ${PYTHON_LIBRARIES}) if (MINGW) set(CMAKE_REQUIRED_DEFINITIONS -D_hypot=hypot) endif (MINGW) unset(${OUTPUT_VARNAME} CACHE) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char *argv[]) { Py_InitializeEx(0); }" ${OUTPUT_VARNAME}) endfunction() if(MINGW) # First get the Python library path to make sure the selected Python # can be used for Building, then try to get the Python interpreter again find_package(PythonInterp 3.6 EXACT) find_package(PythonLibs 3.6 EXACT) if (PYTHONLIBS_FOUND) # Use the same Python as the library get_filename_component(PYTHON_DIR ${PYTHON_LIBRARIES} DIRECTORY) get_filename_component(PYTHON_DIR ${PYTHON_DIR} DIRECTORY) set(PYTHON_EXECUTABLE "${PYTHON_DIR}/python.exe") message(STATUS "Set Python executable: ${PYTHON_EXECUTABLE}") find_package(PythonInterp 3.6 EXACT) if (PYTHONINTERP_FOUND) find_package(PythonLibrary 3.6) else (PYTHONINTERP_FOUND) # This shouldn't happen on Windows... message(FATAL_ERROR "Python library found but python.exe not found!") endif (PYTHONINTERP_FOUND) TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS) if (NOT CAN_USE_PYTHON_LIBS) message(WARNING "Compiling with Python library failed, please check whether the architecture is correct. Python will be disabled.") unset(PYTHONLIBS_FOUND CACHE) endif (NOT CAN_USE_PYTHON_LIBS) endif (PYTHONLIBS_FOUND) else(MINGW) find_package(PythonInterp 3.0) find_package(PythonLibrary 3.0) endif(MINGW) ######################## ######################### ## Look for KDE and Qt ## ######################### ######################## find_package(ECM 5.19 REQUIRED NOMODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(ECMOptionalAddSubdirectory) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(GenerateExportHeader) include(ECMMarkAsTest) include(ECMInstallIcons) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) # do not reorder to be alphabetical: this is the order in which the frameworks # depend on each other. find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS Archive Config WidgetsAddons Completion CoreAddons GuiAddons I18n ItemModels ItemViews WindowSystem ) # KConfig deprecated authorizeKAction. In order to be warning free, # compile with the updated function when the dependency is new enough. # Remove this (and the uses of the define) when the minimum KF5 # version is >= 5.24.0. if (${KF5Config_VERSION} VERSION_LESS "5.24.0" ) message("Old KConfig (< 5.24.0) found.") add_definitions(-DKCONFIG_BEFORE_5_24) endif() find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS Core Gui Widgets Xml Network PrintSupport Svg Test Concurrent ) include (MacroAddFileDependencies) include (MacroBoolTo01) include (MacroEnsureOutOfSourceBuild) macro_ensure_out_of_source_build("Compiling Krita inside the source directory is not possible. Please refer to the build instruction https://community.kde.org/Krita#Build_Instructions") # Note: OPTIONAL_COMPONENTS does not seem to be reliable # (as of ECM 5.15.0, CMake 3.2) find_package(Qt5Multimedia ${MIN_QT_VERSION}) set_package_properties(Qt5Multimedia PROPERTIES DESCRIPTION "Qt multimedia integration" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide sound support for animations") macro_bool_to_01(Qt5Multimedia_FOUND HAVE_QT_MULTIMEDIA) configure_file(config-qtmultimedia.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-qtmultimedia.h ) find_package(Qt5Quick ${MIN_QT_VERSION}) set_package_properties(Qt5Quick PROPERTIES DESCRIPTION "QtQuick" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") macro_bool_to_01(Qt5Quick_FOUND HAVE_QT_QUICK) find_package(Qt5QuickWidgets ${MIN_QT_VERSION}) set_package_properties(Qt5QuickWidgets PROPERTIES DESCRIPTION "QtQuickWidgets" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used for the touch gui for Krita") if (NOT WIN32 AND NOT APPLE) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras) find_package(Qt5DBus ${MIN_QT_VERSION}) set(HAVE_DBUS ${Qt5DBus_FOUND}) set_package_properties(Qt5DBus PROPERTIES DESCRIPTION "Qt DBUS integration" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide a dbus api on Linux") find_package(KF5KIO ${MIN_FRAMEWORKS_VERSION}) macro_bool_to_01(KF5KIO_FOUND HAVE_KIO) set_package_properties(KF5KIO PROPERTIES DESCRIPTION "KDE's KIO Framework" URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kio/html/index.html" TYPE OPTIONAL PURPOSE "Optionally used for recent document handling") find_package(KF5Crash ${MIN_FRAMEWORKS_VERSION}) macro_bool_to_01(KF5Crash_FOUND HAVE_KCRASH) set_package_properties(KF5Crash PROPERTIES DESCRIPTION "KDE's Crash Handler" URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kcrash/html/index.html" TYPE OPTIONAL PURPOSE "Optionally used to provide crash reporting on Linux") find_package(X11 REQUIRED COMPONENTS Xinput) set(HAVE_X11 TRUE) add_definitions(-DHAVE_X11) find_package(XCB COMPONENTS XCB ATOM) set(HAVE_XCB ${XCB_FOUND}) else() set(HAVE_DBUS FALSE) set(HAVE_X11 FALSE) set(HAVE_XCB FALSE) endif() add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_STRICT_ITERATORS -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_USE_FAST_OPERATOR_PLUS -DQT_USE_FAST_CONCATENATION -DQT_NO_URL_CAST_FROM_STRING - -DQT_DISABLE_DEPRECATED_BEFORE=0 + -DQT_DISABLE_DEPRECATED_BEFORE=0x50900 ) add_definitions(-DTRANSLATION_DOMAIN=\"krita\") # # The reason for this mode is that the Debug mode disable inlining # if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS_KRITADEVS "-O3 -g" CACHE STRING "" FORCE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals") endif() if(UNIX) set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m") endif() if(WIN32) if(MSVC) # C4522: 'class' : multiple assignment operators specified set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4522") endif() endif() # enable exceptions globally kde_enable_exceptions() # only with this definition will all the FOO_TEST_EXPORT macro do something # TODO: check if this can be moved to only those places which make use of it, # to reduce global compiler definitions that would trigger a recompile of # everything on a change (like adding/removing tests to/from the build) if(BUILD_TESTING) add_definitions(-DCOMPILING_TESTS) endif() set(KRITA_DEFAULT_TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/sdk/tests/data/) macro(macro_add_unittest_definitions) add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/") add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}") add_definitions(-DFILES_DEFAULT_DATA_DIR="${KRITA_DEFAULT_TEST_DATA_DIR}") add_definitions(-DSYSTEM_RESOURCES_DATA_DIR="${CMAKE_SOURCE_DIR}/krita/data/") endmacro() # overcome some platform incompatibilities if(WIN32) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif() # set custom krita plugin installdir set(KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins) ########################### ############################ ## Required dependencies ## ############################ ########################### find_package(PNG REQUIRED) if (APPLE) # this is not added correctly on OSX -- see http://forum.kde.org/viewtopic.php?f=139&t=101867&p=221242#p221242 include_directories(SYSTEM ${PNG_INCLUDE_DIR}) endif() add_definitions(-DBOOST_ALL_NO_LIB) find_package(Boost 1.55 REQUIRED COMPONENTS system) # for pigment and stage include_directories(${Boost_INCLUDE_DIRS}) ## ## Test for GNU Scientific Library ## find_package(GSL) set_package_properties(GSL PROPERTIES URL "http://www.gnu.org/software/gsl" TYPE RECOMMENDED PURPOSE "Required by Krita's Transform tool.") macro_bool_to_01(GSL_FOUND HAVE_GSL) configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h ) ########################### ############################ ## Optional dependencies ## ############################ ########################### ## ## Check for OpenEXR ## find_package(ZLIB) set_package_properties(ZLIB PROPERTIES DESCRIPTION "Compression library" URL "http://www.zlib.net/" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic and the PSD plugins") macro_bool_to_01(ZLIB_FOUND HAVE_ZLIB) find_package(OpenEXR) set_package_properties(OpenEXR PROPERTIES DESCRIPTION "High dynamic-range (HDR) image file format" URL "http://www.openexr.com" TYPE OPTIONAL PURPOSE "Required by the Krita OpenEXR filter") macro_bool_to_01(OPENEXR_FOUND HAVE_OPENEXR) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() find_package(TIFF) set_package_properties(TIFF PROPERTIES DESCRIPTION "TIFF Library and Utilities" URL "http://www.remotesensing.org/libtiff" TYPE OPTIONAL PURPOSE "Required by the Krita TIFF filter") find_package(JPEG) set_package_properties(JPEG PROPERTIES DESCRIPTION "Free library for JPEG image compression. Note: libjpeg8 is NOT supported." URL "http://www.libjpeg-turbo.org" TYPE OPTIONAL PURPOSE "Required by the Krita JPEG filter") set(LIBRAW_MIN_VERSION "0.16") find_package(LibRaw ${LIBRAW_MIN_VERSION}) set_package_properties(LibRaw PROPERTIES DESCRIPTION "Library to decode RAW images" URL "http://www.libraw.org" TYPE OPTIONAL PURPOSE "Required to build the raw import plugin") find_package(FFTW3) set_package_properties(FFTW3 PROPERTIES DESCRIPTION "A fast, free C FFT library" URL "http://www.fftw.org/" TYPE OPTIONAL PURPOSE "Required by the Krita for fast convolution operators and some G'Mic features") macro_bool_to_01(FFTW3_FOUND HAVE_FFTW3) find_package(OCIO) set_package_properties(OCIO PROPERTIES DESCRIPTION "The OpenColorIO Library" URL "http://www.opencolorio.org" TYPE OPTIONAL PURPOSE "Required by the Krita LUT docker") macro_bool_to_01(OCIO_FOUND HAVE_OCIO) ## ## Look for OpenGL ## # TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes) if(Qt5Gui_OPENGL_IMPLEMENTATION) message(STATUS "Found QtGui OpenGL support") else() message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.") endif() ## ## Test for eigen3 ## find_package(Eigen3 3.0 REQUIRED) set_package_properties(Eigen3 PROPERTIES DESCRIPTION "C++ template library for linear algebra" URL "http://eigen.tuxfamily.org" TYPE REQUIRED) ## ## Test for exiv2 ## find_package(Exiv2 0.16 REQUIRED) set_package_properties(Exiv2 PROPERTIES DESCRIPTION "Image metadata library and tools" URL "http://www.exiv2.org" PURPOSE "Required by Krita") ## ## Test for lcms ## find_package(LCMS2 2.4 REQUIRED) set_package_properties(LCMS2 PROPERTIES DESCRIPTION "LittleCMS Color management engine" URL "http://www.littlecms.com" TYPE REQUIRED PURPOSE "Will be used for color management and is necessary for Krita") if(LCMS2_FOUND) if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 ) set(HAVE_LCMS24 TRUE) endif() set(HAVE_REQUIRED_LCMS_VERSION TRUE) set(HAVE_LCMS2 TRUE) endif() ## ## Test for Vc ## set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ) set(HAVE_VC FALSE) if( NOT MSVC) find_package(Vc 1.1.0) set_package_properties(Vc PROPERTIES DESCRIPTION "Portable, zero-overhead SIMD library for C++" URL "https://github.com/VcDevel/Vc" TYPE OPTIONAL PURPOSE "Required by the Krita for vectorization") macro_bool_to_01(Vc_FOUND HAVE_VC) endif() configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h ) if(HAVE_VC) message(STATUS "Vc found!") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/vc") include (VcMacros) if(Vc_COMPILER_IS_CLANG) set(ADDITIONAL_VC_FLAGS "-Wabi -ffp-contract=fast -fPIC") elseif (NOT MSVC) set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast -fPIC") endif() #Handle Vc master if(Vc_COMPILER_IS_GCC OR Vc_COMPILER_IS_CLANG) AddCompilerFlag("-std=c++11" _ok) if(NOT _ok) AddCompilerFlag("-std=c++0x" _ok) endif() endif() macro(ko_compile_for_all_implementations_no_scalar _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() macro(ko_compile_for_all_implementations _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() endif() set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} ) add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) if(WIN32) set(LIB_INSTALL_DIR ${LIB_INSTALL_DIR} RUNTIME DESTINATION ${BIN_INSTALL_DIR} LIBRARY ${INSTALL_TARGETS_DEFAULT_ARGS} ARCHIVE ${INSTALL_TARGETS_DEFAULT_ARGS} ) endif() ## ## Test endianess ## include (TestBigEndian) test_big_endian(CMAKE_WORDS_BIGENDIAN) ## ## Test for qt-poppler ## find_package(Poppler COMPONENTS Qt5) set_package_properties(Poppler PROPERTIES DESCRIPTION "A PDF rendering library" URL "http://poppler.freedesktop.org" TYPE OPTIONAL PURPOSE "Required by the Krita PDF filter.") ############################ ############################# ## Add Krita helper macros ## ############################# ############################ include(MacroKritaAddBenchmark) #################### ##################### ## Define includes ## ##################### #################### # for config.h and includes (if any?) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/interfaces ) add_subdirectory(libs) add_subdirectory(plugins) add_subdirectory(benchmarks) add_subdirectory(krita) configure_file(KoConfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h ) configure_file(config_convolution.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_convolution.h) configure_file(config-ocio.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ocio.h ) check_function_exists(powf HAVE_POWF) configure_file(config-powf.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-powf.h) message("\nBroken tests:") foreach(tst ${KRITA_BROKEN_TESTS}) message(" * ${tst}") endforeach() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/krita/main.cc b/krita/main.cc index 86bfb4d0ae..5864cd875b 100644 --- a/krita/main.cc +++ b/krita/main.cc @@ -1,314 +1,314 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2015 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 #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include "data/splash/splash_screen.xpm" #include "data/splash/splash_holidays.xpm" #include "KisDocument.h" #include "kis_splash_screen.h" #include "KisPart.h" #include "KisApplicationArguments.h" #include #include "input/KisQtWidgetsTweaker.h" #if defined Q_OS_WIN #include #include #include #include #include #elif defined HAVE_X11 #include #include #endif #if defined HAVE_KCRASH #include #elif defined USE_DRMINGW namespace { void tryInitDrMingw() { wchar_t path[MAX_PATH]; QString pathStr = QCoreApplication::applicationDirPath().replace(L'/', L'\\') + QStringLiteral("\\exchndl.dll"); if (pathStr.size() > MAX_PATH - 1) { return; } int pathLen = pathStr.toWCharArray(path); path[pathLen] = L'\0'; // toWCharArray doesn't add NULL terminator HMODULE hMod = LoadLibraryW(path); if (!hMod) { return; } // No need to call ExcHndlInit since the crash handler is installed on DllMain auto myExcHndlSetLogFileNameA = reinterpret_cast(GetProcAddress(hMod, "ExcHndlSetLogFileNameA")); if (!myExcHndlSetLogFileNameA) { return; } // Set the log file path to %LocalAppData%\kritacrash.log QString logFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation).replace(L'/', L'\\') + QStringLiteral("\\kritacrash.log"); myExcHndlSetLogFileNameA(logFile.toLocal8Bit()); } } // namespace #endif extern "C" int main(int argc, char **argv) { // The global initialization of the random generator qsrand(time(0)); bool runningInKDE = !qgetenv("KDE_FULL_SESSION").isEmpty(); #if defined HAVE_X11 qputenv("QT_QPA_PLATFORM", "xcb"); #endif KisLoggingManager::initialize(); // A per-user unique string, without /, because QLocalServer cannot use names with a / in it - QString key = "Krita3" + QDesktopServices::storageLocation(QDesktopServices::HomeLocation).replace("/", "_"); + QString key = "Krita3" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation).replace("/", "_"); key = key.replace(":", "_").replace("\\","_"); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); bool singleApplication = true; bool enableOpenGLDebug = false; bool openGLDebugSynchronous = false; { QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); singleApplication = kritarc.value("EnableSingleApplication", true).toBool(); #if QT_VERSION >= 0x050600 if (kritarc.value("EnableHiDPI", false).toBool()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } if (!qgetenv("KRITA_HIDPI").isEmpty()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } #endif if (!qgetenv("KRITA_OPENGL_DEBUG").isEmpty()) { enableOpenGLDebug = true; } else { enableOpenGLDebug = kritarc.value("EnableOpenGLDebug", false).toBool(); } if (enableOpenGLDebug && (qgetenv("KRITA_OPENGL_DEBUG") == "sync" || kritarc.value("OpenGLDebugSynchronous", false).toBool())) { openGLDebugSynchronous = true; } KisOpenGL::setDefaultFormat(enableOpenGLDebug, openGLDebugSynchronous); #ifdef Q_OS_WIN QString preferredOpenGLRenderer = kritarc.value("OpenGLRenderer", "auto").toString(); // Force ANGLE to use Direct3D11. D3D9 doesn't support OpenGL ES 3 and WARP // might get weird crashes atm. qputenv("QT_ANGLE_PLATFORM", "d3d11"); // Probe QPA auto OpenGL detection KisOpenGL::probeWindowsQpaOpenGL(argc, argv, preferredOpenGLRenderer); #endif } KLocalizedString::setApplicationDomain("krita"); // first create the application so we can create a pixmap KisApplication app(key, argc, argv); #ifdef Q_OS_LINUX qputenv("XDG_DATA_DIRS", QFile::encodeName(KoResourcePaths::getApplicationRoot() + "share") + ":" + qgetenv("XDG_DATA_DIRS")); #else qputenv("XDG_DATA_DIRS", QFile::encodeName(KoResourcePaths::getApplicationRoot() + "share")); #endif qDebug() << "Setting XDG_DATA_DIRS" << qgetenv("XDG_DATA_DIRS"); qDebug() << "Available translations" << KLocalizedString::availableApplicationTranslations(); qDebug() << "Available domain translations" << KLocalizedString::availableDomainTranslations("krita"); // Now that the paths are set, set the language. First check the override from the langage // selection dialog. { QSettings languageoverride(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat); languageoverride.beginGroup(QStringLiteral("Language")); QString language = languageoverride.value(qAppName(), "").toString(); qDebug() << "Override language:" << language; if (!language.isEmpty()) { KLocalizedString::setLanguages(language.split(":")); // And override Qt's locale, too qputenv("LANG", language.split(":").first().toUtf8()); QLocale locale(language.split(":").first()); QLocale::setDefault(locale); qDebug() << "Qt ui languages" << locale.uiLanguages(); } else { // And if there isn't one, check the one set by the system. // XXX: This doesn't work, for some !@#$% reason. QLocale locale = QLocale::system(); if (locale.bcp47Name() != QStringLiteral("en")) { qputenv("LANG", locale.bcp47Name().toLatin1()); KLocalizedString::setLanguages(QStringList() << locale.bcp47Name()); } } } #ifdef Q_OS_WIN QDir appdir(KoResourcePaths::getApplicationRoot()); QString path = qgetenv("PATH"); qputenv("PATH", QFile::encodeName(appdir.absolutePath() + "/bin" + ";" + appdir.absolutePath() + "/lib" + ";" + appdir.absolutePath() + "/Frameworks" + ";" + appdir.absolutePath() + ";" + path)); qDebug() << "PATH" << qgetenv("PATH"); #endif if (qApp->applicationDirPath().contains(KRITA_BUILD_DIR)) { qFatal("FATAL: You're trying to run krita from the build location. You can only run Krita from the installation location."); } #if defined HAVE_KCRASH KCrash::initialize(); #elif defined USE_DRMINGW tryInitDrMingw(); #endif // If we should clear the config, it has to be done as soon as possible after // KisApplication has been created. Otherwise the config file may have been read // and stored in a KConfig object we have no control over. app.askClearConfig(); KisApplicationArguments args(app); if (singleApplication && app.isRunning()) { // only pass arguments to main instance if they are not for batch processing // any batch processing would be done in this separate instance const bool batchRun = (args.print() || args.exportAs() || args.exportAsPdf()); if (!batchRun) { QByteArray ba = args.serialize(); if (app.sendMessage(ba)) { return 0; } } } if (!runningInKDE) { // Icons in menus are ugly and distracting app.setAttribute(Qt::AA_DontShowIconsInMenus); } #if defined HAVE_X11 app.installNativeEventFilter(KisXi2EventFilter::instance()); #endif app.installEventFilter(KisQtWidgetsTweaker::instance()); if (!args.noSplash()) { // then create the pixmap from an xpm: we cannot get the // location of our datadir before we've started our components, // so use an xpm. QDate currentDate = QDate::currentDate(); QWidget *splash = 0; if (currentDate > QDate(currentDate.year(), 12, 4) || currentDate < QDate(currentDate.year(), 1, 9)) { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_holidays_xpm)); } else { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_screen_xpm)); } app.setSplashScreen(splash); } #if defined Q_OS_WIN { KisConfig cfg; bool isUsingWin8PointerInput = false; if (cfg.useWin8PointerInput()) { KisTabletSupportWin8 *penFilter = new KisTabletSupportWin8(); if (penFilter->init()) { // penFilter.registerPointerDeviceNotifications(); app.installNativeEventFilter(penFilter); isUsingWin8PointerInput = true; qDebug() << "Using Win8 Pointer Input for tablet support"; } else { qDebug() << "No Win8 Pointer Input available"; delete penFilter; } } if (!isUsingWin8PointerInput) { KisTabletSupportWin::init(); // app.installNativeEventFilter(new KisTabletSupportWin()); } } #endif if (!app.start(args)) { return 1; } #if QT_VERSION >= 0x050700 app.setAttribute(Qt::AA_CompressHighFrequencyEvents, false); #endif // Set up remote arguments. QObject::connect(&app, SIGNAL(messageReceived(QByteArray,QObject*)), &app, SLOT(remoteArguments(QByteArray,QObject*))); QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), &app, SLOT(fileOpenRequested(QString))); int state = app.exec(); { QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("canvasState", "OPENGL_SUCCESS"); } return state; } diff --git a/libs/image/brushengine/kis_paintop_config_widget.cpp b/libs/image/brushengine/kis_paintop_config_widget.cpp index e956181ff3..c220372b70 100644 --- a/libs/image/brushengine/kis_paintop_config_widget.cpp +++ b/libs/image/brushengine/kis_paintop_config_widget.cpp @@ -1,66 +1,66 @@ /* * Copyright (c) 2008 Boudewijn Rempt * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_paintop_config_widget.h" #include -KisPaintOpConfigWidget::KisPaintOpConfigWidget(QWidget * parent, Qt::WFlags f) +KisPaintOpConfigWidget::KisPaintOpConfigWidget(QWidget * parent, Qt::WindowFlags f) : KisConfigWidget(parent, f, 10), m_isInsideUpdateCall(0) { } KisPaintOpConfigWidget::~KisPaintOpConfigWidget() { } void KisPaintOpConfigWidget::writeConfigurationSafe(KisPropertiesConfigurationSP config) const { if (m_isInsideUpdateCall) return; m_isInsideUpdateCall++; writeConfiguration(config); m_isInsideUpdateCall--; } void KisPaintOpConfigWidget::setConfigurationSafe(const KisPropertiesConfigurationSP config) { if (m_isInsideUpdateCall) return; m_isInsideUpdateCall++; setConfiguration(config); m_isInsideUpdateCall--; } void KisPaintOpConfigWidget::setImage(KisImageWSP image) { m_image = image; } void KisPaintOpConfigWidget::setNode(KisNodeWSP node) { m_node = node; } bool KisPaintOpConfigWidget::presetIsValid() { return true; } bool KisPaintOpConfigWidget::supportScratchBox() { return true; } diff --git a/libs/image/brushengine/kis_paintop_config_widget.h b/libs/image/brushengine/kis_paintop_config_widget.h index b4f209297b..6f9dd7c9cb 100644 --- a/libs/image/brushengine/kis_paintop_config_widget.h +++ b/libs/image/brushengine/kis_paintop_config_widget.h @@ -1,79 +1,79 @@ /* * 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. */ #ifndef KIS_PAINTOP_CONFIG_WIDGET_H_ #define KIS_PAINTOP_CONFIG_WIDGET_H_ #include "kritaimage_export.h" #include "kis_config_widget.h" #include "kis_image.h" #include #include class KisPaintopLodLimitations; /** * Base class for widgets that are used to edit and display paintop settings. */ class KRITAIMAGE_EXPORT KisPaintOpConfigWidget : public KisConfigWidget { Q_OBJECT public: - KisPaintOpConfigWidget(QWidget * parent = 0, Qt::WFlags f = 0); + KisPaintOpConfigWidget(QWidget * parent = 0, Qt::WindowFlags f = 0); ~KisPaintOpConfigWidget() override; void writeConfigurationSafe(KisPropertiesConfigurationSP config) const; void setConfigurationSafe(const KisPropertiesConfigurationSP config); protected: friend class CompositeOpModel; void setConfiguration(const KisPropertiesConfigurationSP config) override = 0; virtual void writeConfiguration(KisPropertiesConfigurationSP config) const = 0; public: virtual KisPaintopLodLimitations lodLimitations() const = 0; virtual void setImage(KisImageWSP image); virtual void setNode(KisNodeWSP node); /** * This is true for all of the paintop widget except for the Custom brush tab in the Brush tip dialog */ virtual bool presetIsValid(); /** * Some paintops are more complicated and require full canvas with layers, projections and KisImage etc. * Example is duplicate paintop. In this case simple canvas like scratchbox does not work. * Every paintop supports the scratchbox by default, override and return false if paintop does not. */ virtual bool supportScratchBox(); protected: KisImageWSP m_image; KisNodeWSP m_node; mutable int m_isInsideUpdateCall; }; #endif diff --git a/libs/image/kis_config_widget.cpp b/libs/image/kis_config_widget.cpp index 336672acdc..88e47a4163 100644 --- a/libs/image/kis_config_widget.cpp +++ b/libs/image/kis_config_widget.cpp @@ -1,47 +1,47 @@ /* * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_config_widget.h" #include "kis_debug.h" #include -KisConfigWidget::KisConfigWidget(QWidget * parent, Qt::WFlags f, int delay) +KisConfigWidget::KisConfigWidget(QWidget * parent, Qt::WindowFlags f, int delay) : QWidget(parent, f) , m_compressor(delay, KisSignalCompressor::FIRST_ACTIVE) { connect(this, SIGNAL(sigConfigurationItemChanged()), SLOT(slotConfigChanged())); connect(&m_compressor, SIGNAL(timeout()), SIGNAL(sigConfigurationUpdated())); } KisConfigWidget::~KisConfigWidget() { } void KisConfigWidget::slotConfigChanged() { if (!signalsBlocked()) { m_compressor.start(); } } void KisConfigWidget::setView(KisViewManager *view) { if (!view) { warnKrita << "KisConfigWidget::setView has got view == 0. That's a bug! Please report it!"; } } diff --git a/libs/image/kis_config_widget.h b/libs/image/kis_config_widget.h index 8f4c2186e8..98dd7150f1 100644 --- a/libs/image/kis_config_widget.h +++ b/libs/image/kis_config_widget.h @@ -1,92 +1,92 @@ /* * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * Copyright (c) 2004-2006 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_CONFIG_WIDGET_H_ #define _KIS_CONFIG_WIDGET_H_ #include #include #include "kis_signal_compressor.h" #include class KisViewManager; /** * Empty base class. Configurable resources like filters, paintops etc. * can build their own configuration widgets that inherit this class. * The configuration widget should emit sigConfigurationItemChanged * when it wants a preview updated; there is a timer that * waits a little time to see if there are more changes coming * and then emits sigConfigurationUpdated. */ class KRITAIMAGE_EXPORT KisConfigWidget : public QWidget { Q_OBJECT protected: - KisConfigWidget(QWidget * parent = 0, Qt::WFlags f = 0, int delay = 200); + KisConfigWidget(QWidget * parent = 0, Qt::WindowFlags f = 0, int delay = 200); public: ~KisConfigWidget() override; /** * @param config the configuration for this configuration widget. */ virtual void setConfiguration(const KisPropertiesConfigurationSP config) = 0; /** * @return the configuration */ virtual KisPropertiesConfigurationSP configuration() const = 0; /** * Sets the view object that can be used by the configuration * widget for richer functionality */ virtual void setView(KisViewManager *view); Q_SIGNALS: /** * emitted whenever it makes sense to update the preview */ void sigConfigurationUpdated(); /** * Subclasses should emit this signal whenever the preview should be * be recalculated. This kicks of a timer, so it's perfectly fine * to connect this to the changed signals of the widgets in your configuration * widget. */ void sigConfigurationItemChanged(); void sigSaveLockedConfig(KisPropertiesConfigurationSP p); void sigDropLockedConfig(KisPropertiesConfigurationSP p); private Q_SLOTS: void slotConfigChanged(); private: KisSignalCompressor m_compressor; }; #endif diff --git a/libs/image/kis_convolution_worker_fft.h b/libs/image/kis_convolution_worker_fft.h index 32bd6aca9c..503f8f13fd 100644 --- a/libs/image/kis_convolution_worker_fft.h +++ b/libs/image/kis_convolution_worker_fft.h @@ -1,541 +1,541 @@ /* * Copyright (c) 2010 Edward Apap * Copyright (c) 2011 José Luis Vergara Toloza * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * 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_CONVOLUTION_WORKER_FFT_H #define KIS_CONVOLUTION_WORKER_FFT_H #include #include #include "kis_convolution_worker.h" #include "kis_math_toolbox.h" #include #include #include #include #include #include template class KisConvolutionWorkerFFT; class KisConvolutionWorkerFFTLock { private: static QMutex fftwMutex; template friend class KisConvolutionWorkerFFT; }; QMutex KisConvolutionWorkerFFTLock::fftwMutex; template class KisConvolutionWorkerFFT : public KisConvolutionWorker<_IteratorFactory_> { public: KisConvolutionWorkerFFT(KisPainter *painter, KoUpdater *progress) : KisConvolutionWorker<_IteratorFactory_>(painter, progress), m_currentProgress(0), m_kernelFFT(0) { } ~KisConvolutionWorkerFFT() { } virtual void execute(const KisConvolutionKernelSP kernel, const KisPaintDeviceSP src, QPoint srcPos, QPoint dstPos, QSize areaSize, const QRect& dataRect) { // Make the area we cover as small as possible if (this->m_painter->selection()) { - QRect r = this->m_painter->selection()->selectedRect().intersect(QRect(srcPos, areaSize)); + QRect r = this->m_painter->selection()->selectedRect().intersected(QRect(srcPos, areaSize)); dstPos += r.topLeft() - srcPos; srcPos = r.topLeft(); areaSize = r.size(); } if (areaSize.width() == 0 || areaSize.height() == 0) return; addToProgress(0); if (isInterrupted()) return; const quint32 halfKernelWidth = (kernel->width() - 1) / 2; const quint32 halfKernelHeight = (kernel->height() - 1) / 2; m_fftWidth = areaSize.width() + 4 * halfKernelWidth; m_fftHeight = areaSize.height() + 2 * halfKernelHeight; /** * FIXME: check whether this "optimization" is needed to * be uncommented. My tests showed about 30% better performance * when the line is commented out (DK). */ //optimumDimensions(m_fftWidth, m_fftHeight); m_fftLength = m_fftHeight * (m_fftWidth / 2 + 1); m_extraMem = (m_fftWidth % 2) ? 1 : 2; // create and fill kernel m_kernelFFT = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * m_fftLength); memset(m_kernelFFT, 0, sizeof(fftw_complex) * m_fftLength); fftFillKernelMatrix(kernel, m_kernelFFT); // find out which channels need convolving QList convChannelList = this->convolvableChannelList(src); m_channelFFT.resize(convChannelList.count()); for (auto i = m_channelFFT.begin(); i != m_channelFFT.end(); ++i) { *i = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * m_fftLength); } const double kernelFactor = kernel->factor() ? kernel->factor() : 1; const double fftScale = 1.0 / (m_fftHeight * m_fftWidth) / kernelFactor; FFTInfo info (fftScale, convChannelList, kernel, this->m_painter->device()->colorSpace()); int cacheRowStride = m_fftWidth + m_extraMem; fillCacheFromDevice(src, QRect(srcPos.x() - halfKernelWidth, srcPos.y() - halfKernelHeight, m_fftWidth, m_fftHeight), cacheRowStride, info, dataRect); addToProgress(10); if (isInterrupted()) return; // calculate number off fft operations required for progress reporting const float progressPerFFT = (100 - 30) / (double)(convChannelList.count() * 2 + 1); // perform FFT fftw_plan fftwPlanForward, fftwPlanBackward; KisConvolutionWorkerFFTLock::fftwMutex.lock(); fftwPlanForward = fftw_plan_dft_r2c_2d(m_fftHeight, m_fftWidth, (double*)m_kernelFFT, m_kernelFFT, FFTW_ESTIMATE); fftwPlanBackward = fftw_plan_dft_c2r_2d(m_fftHeight, m_fftWidth, m_kernelFFT, (double*)m_kernelFFT, FFTW_ESTIMATE); KisConvolutionWorkerFFTLock::fftwMutex.unlock(); fftw_execute(fftwPlanForward); addToProgress(progressPerFFT); if (isInterrupted()) return; for (auto k = m_channelFFT.begin(); k != m_channelFFT.end(); ++k) { fftw_execute_dft_r2c(fftwPlanForward, (double*)(*k), *k); addToProgress(progressPerFFT); if (isInterrupted()) return; fftMultiply(*k, m_kernelFFT); fftw_execute_dft_c2r(fftwPlanBackward, *k, (double*)*k); addToProgress(progressPerFFT); if (isInterrupted()) return; } KisConvolutionWorkerFFTLock::fftwMutex.lock(); fftw_destroy_plan(fftwPlanForward); fftw_destroy_plan(fftwPlanBackward); KisConvolutionWorkerFFTLock::fftwMutex.unlock(); writeResultToDevice(QRect(dstPos.x(), dstPos.y(), areaSize.width(), areaSize.height()), cacheRowStride, halfKernelWidth, halfKernelHeight, info, dataRect); addToProgress(20); cleanUp(); } struct FFTInfo { FFTInfo(qreal _fftScale, const QList &_convChannelList, const KisConvolutionKernelSP kernel, const KoColorSpace */*colorSpace*/) : fftScale(_fftScale), convChannelList(_convChannelList), alphaCachePos(-1), alphaRealPos(-1) { KisMathToolbox mathToolbox; for (int i = 0; i < convChannelList.count(); ++i) { minClamp.append(mathToolbox.minChannelValue(convChannelList[i])); maxClamp.append(mathToolbox.maxChannelValue(convChannelList[i])); absoluteOffset.append((maxClamp[i] - minClamp[i]) * kernel->offset()); if (convChannelList[i]->channelType() == KoChannelInfo::ALPHA) { alphaCachePos = i; alphaRealPos = convChannelList[i]->pos(); } } toDoubleFuncPtr.resize(convChannelList.count()); fromDoubleFuncPtr.resize(convChannelList.count()); bool result = mathToolbox.getToDoubleChannelPtr(convChannelList, toDoubleFuncPtr); result &= mathToolbox.getFromDoubleChannelPtr(convChannelList, fromDoubleFuncPtr); KIS_ASSERT(result); } inline int numChannels() const { return convChannelList.size(); } QVector minClamp; QVector maxClamp; QVector absoluteOffset; qreal fftScale; QList convChannelList; QVector toDoubleFuncPtr; QVector fromDoubleFuncPtr; int alphaCachePos; int alphaRealPos; }; void fillCacheFromDevice(KisPaintDeviceSP src, const QRect &rect, const int cacheRowStride, const FFTInfo &info, const QRect &dataRect) { typename _IteratorFactory_::HLineConstIterator hitSrc = _IteratorFactory_::createHLineConstIterator(src, rect.x(), rect.y(), rect.width(), dataRect); const int channelCount = info.numChannels(); QVector channelPtr(channelCount); const auto channelPtrBegin = channelPtr.begin(); const auto channelPtrEnd = channelPtr.end(); auto iFFt = m_channelFFT.constBegin(); for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++iFFt) { *i = (double*)*iFFt; } // prepare cache, reused in all loops QVector cacheRowStart(channelCount); const auto cacheRowStartBegin = cacheRowStart.begin(); for (int y = 0; y < rect.height(); ++y) { // cache current channelPtr in cacheRowStart memcpy(cacheRowStart.data(), channelPtr.data(), channelCount * sizeof(double*)); for (int x = 0; x < rect.width(); ++x) { const quint8 *data = hitSrc->oldRawData(); // no alpha is a rare case, so just multiply by 1.0 in that case double alphaValue = info.alphaRealPos >= 0 ? info.toDoubleFuncPtr[info.alphaCachePos](data, info.alphaRealPos) : 1.0; int k = 0; for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++k) { if (k != info.alphaCachePos) { const quint32 channelPos = info.convChannelList[k]->pos(); **i = info.toDoubleFuncPtr[k](data, channelPos) * alphaValue; } else { **i = alphaValue; } ++(*i); } hitSrc->nextPixel(); } auto iRowStart = cacheRowStartBegin; for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++iRowStart) { *i = *iRowStart + cacheRowStride; } hitSrc->nextRow(); } } inline void limitValue(qreal *value, qreal lowBound, qreal highBound) { if (*value > highBound) { *value = highBound; } else if (!(*value >= lowBound)) { // value < lowBound or value == NaN // IEEE compliant comparisons with NaN are always false *value = lowBound; } } template inline qreal writeOneChannelFromCache(quint8* dstPtr, const quint32 channel, const FFTInfo &info, double* channelValuePtr, const qreal additionalMultiplier = 0.0) { qreal channelPixelValue; if (additionalMultiplierActive) { channelPixelValue = (*channelValuePtr * info.fftScale + info.absoluteOffset[channel]) * additionalMultiplier; } else { channelPixelValue = *channelValuePtr * info.fftScale + info.absoluteOffset[channel]; } limitValue(&channelPixelValue, info.minClamp[channel], info.maxClamp[channel]); info.fromDoubleFuncPtr[channel](dstPtr, info.convChannelList[channel]->pos(), channelPixelValue); return channelPixelValue; } void writeResultToDevice(const QRect &rect, const int cacheRowStride, const int halfKernelWidth, const int halfKernelHeight, const FFTInfo &info, const QRect &dataRect) { typename _IteratorFactory_::HLineIterator hitDst = _IteratorFactory_::createHLineIterator(this->m_painter->device(), rect.x(), rect.y(), rect.width(), dataRect); int initialOffset = cacheRowStride * halfKernelHeight + halfKernelWidth; const int channelCount = info.numChannels(); QVector channelPtr(channelCount); const auto channelPtrBegin = channelPtr.begin(); const auto channelPtrEnd = channelPtr.end(); auto iFFt = m_channelFFT.constBegin(); for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++iFFt) { *i = (double*)*iFFt + initialOffset; } // prepare cache, reused in all loops QVector cacheRowStart(channelCount); const auto cacheRowStartBegin = cacheRowStart.begin(); for (int y = 0; y < rect.height(); ++y) { // cache current channelPtr in cacheRowStart memcpy(cacheRowStart.data(), channelPtr.data(), channelCount * sizeof(double*)); for (int x = 0; x < rect.width(); ++x) { quint8 *dstPtr = hitDst->rawData(); if (info.alphaCachePos >= 0) { qreal alphaValue = writeOneChannelFromCache(dstPtr, info.alphaCachePos, info, channelPtr.at(info.alphaCachePos)); if (alphaValue > std::numeric_limits::epsilon()) { qreal alphaValueInv = 1.0 / alphaValue; int k = 0; for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++k) { if (k != info.alphaCachePos) { writeOneChannelFromCache(dstPtr, k, info, *i, alphaValueInv); } ++(*i); } } else { int k = 0; for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++k) { if (k != info.alphaCachePos) { info.fromDoubleFuncPtr[k](dstPtr, info.convChannelList[k]->pos(), 0.0); } ++(*i); } } } else { int k = 0; for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++k) { writeOneChannelFromCache(dstPtr, k, info, *i); ++(*i); } } hitDst->nextPixel(); } auto iRowStart = cacheRowStartBegin; for (auto i = channelPtrBegin; i != channelPtrEnd; ++i, ++iRowStart) { *i = *iRowStart + cacheRowStride; } hitDst->nextRow(); } } private: void fftFillKernelMatrix(const KisConvolutionKernelSP kernel, fftw_complex *m_kernelFFT) { // find central item QPoint offset((kernel->width() - 1) / 2, (kernel->height() - 1) / 2); qint32 xShift = m_fftWidth - offset.x(); qint32 yShift = m_fftHeight - offset.y(); quint32 absXpos, absYpos; for (quint32 y = 0; y < kernel->height(); y++) { absYpos = y + yShift; if (absYpos >= m_fftHeight) absYpos -= m_fftHeight; for (quint32 x = 0; x < kernel->width(); x++) { absXpos = x + xShift; if (absXpos >= m_fftWidth) absXpos -= m_fftWidth; ((double*)m_kernelFFT)[(m_fftWidth + m_extraMem) * absYpos + absXpos] = kernel->data()->coeff(y, x); } } } void fftMultiply(fftw_complex* channel, fftw_complex* kernel) { // perform complex multiplication fftw_complex *channelPtr = channel; fftw_complex *kernelPtr = kernel; fftw_complex tmp; for (quint32 pixelPos = 0; pixelPos < m_fftLength; ++pixelPos) { tmp[0] = ((*channelPtr)[0] * (*kernelPtr)[0]) - ((*channelPtr)[1] * (*kernelPtr)[1]); tmp[1] = ((*channelPtr)[0] * (*kernelPtr)[1]) + ((*channelPtr)[1] * (*kernelPtr)[0]); (*channelPtr)[0] = tmp[0]; (*channelPtr)[1] = tmp[1]; ++channelPtr; ++kernelPtr; } } void optimumDimensions(quint32& w, quint32& h) { // FFTW is most efficient when array size is a factor of 2, 3, 5 or 7 quint32 optW = w, optH = h; while ((optW % 2 != 0) || (optW % 3 != 0) || (optW % 5 != 0) || (optW % 7 != 0)) ++optW; while ((optH % 2 != 0) || (optH % 3 != 0) || (optH % 5 != 0) || (optH % 7 != 0)) ++optH; quint32 optAreaW = optW * h; quint32 optAreaH = optH * w; if (optAreaW < optAreaH) { w = optW; } else { h = optH; } } void fftLogMatrix(double* channel, const QString &f) { KisConvolutionWorkerFFTLock::fftwMutex.lock(); QString filename(QDir::homePath() + "/log_" + f + ".txt"); dbgKrita << "Log File Name: " << filename; QFile file (filename); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { dbgKrita << "Failed"; KisConvolutionWorkerFFTLock::fftwMutex.unlock(); return; } QTextStream in(&file); for (quint32 y = 0; y < m_fftHeight; y++) { for (quint32 x = 0; x < m_fftWidth; x++) { QString num = QString::number(channel[y * m_fftWidth + x]); while (num.length() < 15) num += " "; in << num << " "; } in << "\n"; } KisConvolutionWorkerFFTLock::fftwMutex.unlock(); } void addToProgress(float amount) { m_currentProgress += amount; if (this->m_progress) { this->m_progress->setProgress((int)m_currentProgress); } } bool isInterrupted() { if (this->m_progress && this->m_progress->interrupted()) { cleanUp(); return true; } return false; } void cleanUp() { // free kernel fft data if (m_kernelFFT) { fftw_free(m_kernelFFT); } Q_FOREACH (fftw_complex *channel, m_channelFFT) { fftw_free(channel); } m_channelFFT.clear(); } private: quint32 m_fftWidth, m_fftHeight, m_fftLength, m_extraMem; float m_currentProgress; fftw_complex* m_kernelFFT; QVector m_channelFFT; }; #endif diff --git a/libs/image/kis_convolution_worker_spatial.h b/libs/image/kis_convolution_worker_spatial.h index 35946dc86e..5bb9fba760 100644 --- a/libs/image/kis_convolution_worker_spatial.h +++ b/libs/image/kis_convolution_worker_spatial.h @@ -1,386 +1,386 @@ /* * Copyright (c) 2005, 2008, 2010 Cyrille Berger * Copyright (c) 2009, 2010 Edward Apap * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * 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_CONVOLUTION_WORKER_SPATIAL_H #define KIS_CONVOLUTION_WORKER_SPATIAL_H #include "kis_convolution_worker.h" #include "kis_math_toolbox.h" template class KisConvolutionWorkerSpatial : public KisConvolutionWorker<_IteratorFactory_> { public: KisConvolutionWorkerSpatial(KisPainter *painter, KoUpdater *progress) : KisConvolutionWorker<_IteratorFactory_>(painter, progress) , m_alphaCachePos(-1) , m_alphaRealPos(-1) , m_pixelPtrCache(0) , m_pixelPtrCacheCopy(0) , m_minClamp(0) , m_maxClamp(0) , m_absoluteOffset(0) { } ~KisConvolutionWorkerSpatial() override { } inline void loadPixelToCache(qreal **cache, const quint8 *data, int index) { // no alpha is rare case, so just multiply by 1.0 in that case qreal alphaValue = m_alphaRealPos >= 0 ? m_toDoubleFuncPtr[m_alphaCachePos](data, m_alphaRealPos) : 1.0; for (quint32 k = 0; k < m_convolveChannelsNo; ++k) { if (k != (quint32)m_alphaCachePos) { const quint32 channelPos = m_convChannelList[k]->pos(); cache[index][k] = m_toDoubleFuncPtr[k](data, channelPos) * alphaValue; } else { cache[index][k] = alphaValue; } } } void execute(const KisConvolutionKernelSP kernel, const KisPaintDeviceSP src, QPoint srcPos, QPoint dstPos, QSize areaSize, const QRect& dataRect) override { // store some kernel characteristics m_kw = kernel->width(); m_kh = kernel->height(); m_khalfWidth = (m_kw - 1) / 2; m_khalfHeight = (m_kh - 1) / 2; m_cacheSize = m_kw * m_kh; m_pixelSize = src->colorSpace()->pixelSize(); quint32 channelCount = src->colorSpace()->channelCount(); m_kernelData = new qreal[m_cacheSize]; qreal *kernelDataPtr = m_kernelData; // fill in data for (quint32 r = 0; r < kernel->height(); r++) { for (quint32 c = 0; c < kernel->width(); c++) { *kernelDataPtr = (*(kernel->data()))(r, c); kernelDataPtr++; } } // Make the area we cover as small as possible if (this->m_painter->selection()) { - QRect r = this->m_painter->selection()->selectedRect().intersect(QRect(srcPos, areaSize)); + QRect r = this->m_painter->selection()->selectedRect().intersected(QRect(srcPos, areaSize)); dstPos += r.topLeft() - srcPos; srcPos = r.topLeft(); areaSize = r.size(); } if (areaSize.width() == 0 || areaSize.height() == 0) return; // Don't convolve with an even sized kernel Q_ASSERT((m_kw & 0x01) == 1 || (m_kh & 0x01) == 1 || kernel->factor() != 0); // find out which channels need be convolved m_convChannelList = this->convolvableChannelList(src); m_convolveChannelsNo = m_convChannelList.count(); for (int i = 0; i < m_convChannelList.size(); i++) { if (m_convChannelList[i]->channelType() == KoChannelInfo::ALPHA) { m_alphaCachePos = i; m_alphaRealPos = m_convChannelList[i]->pos(); } } bool hasProgressUpdater = this->m_progress; if (hasProgressUpdater) this->m_progress->setProgress(0); // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them. m_pixelPtrCache = new qreal*[m_cacheSize]; m_pixelPtrCacheCopy = new qreal*[m_cacheSize]; for (quint32 c = 0; c < m_cacheSize; ++c) { m_pixelPtrCache[c] = new qreal[channelCount]; m_pixelPtrCacheCopy[c] = new qreal[channelCount]; } // decide caching strategy enum TraversingDirection { Horizontal, Vertical }; TraversingDirection traversingDirection = Vertical; if (m_kw > m_kh) { traversingDirection = Horizontal; } KisMathToolbox mathToolbox; m_toDoubleFuncPtr = QVector(m_convolveChannelsNo); if (!mathToolbox.getToDoubleChannelPtr(m_convChannelList, m_toDoubleFuncPtr)) return; m_fromDoubleFuncPtr = QVector(m_convolveChannelsNo); if (!mathToolbox.getFromDoubleChannelPtr(m_convChannelList, m_fromDoubleFuncPtr)) return; m_kernelFactor = kernel->factor() ? 1.0 / kernel->factor() : 1; m_maxClamp = new qreal[m_convChannelList.count()]; m_minClamp = new qreal[m_convChannelList.count()]; m_absoluteOffset = new qreal[m_convChannelList.count()]; for (quint16 i = 0; i < m_convChannelList.count(); ++i) { m_minClamp[i] = mathToolbox.minChannelValue(m_convChannelList[i]); m_maxClamp[i] = mathToolbox.maxChannelValue(m_convChannelList[i]); m_absoluteOffset[i] = (m_maxClamp[i] - m_minClamp[i]) * kernel->offset(); } qint32 row = srcPos.y(); qint32 col = srcPos.x(); // populate pixelPtrCacheCopy for starting position (0, 0) qint32 i = 0; typename _IteratorFactory_::HLineConstIterator hitInitSrc = _IteratorFactory_::createHLineConstIterator(src, col - m_khalfWidth, row - m_khalfHeight, m_kw, dataRect); for (quint32 krow = 0; krow < m_kh; ++krow) { do { const quint8* data = hitInitSrc->oldRawData(); loadPixelToCache(m_pixelPtrCacheCopy, data, i); ++i; } while (hitInitSrc->nextPixel()); hitInitSrc->nextRow(); } if (traversingDirection == Horizontal) { if(hasProgressUpdater) { this->m_progress->setRange(0, areaSize.height()); } typename _IteratorFactory_::HLineIterator hitDst = _IteratorFactory_::createHLineIterator(this->m_painter->device(), dstPos.x(), dstPos.y(), areaSize.width(), dataRect); typename _IteratorFactory_::HLineConstIterator hitSrc = _IteratorFactory_::createHLineConstIterator(src, srcPos.x(), srcPos.y(), areaSize.width(), dataRect); typename _IteratorFactory_::HLineConstIterator khitSrc = _IteratorFactory_::createHLineConstIterator(src, col - m_khalfWidth, row + m_khalfHeight, m_kw, dataRect); for (int prow = 0; prow < areaSize.height(); ++prow) { // reload cache from copy for (quint32 i = 0; i < m_cacheSize; ++i) memcpy(m_pixelPtrCache[i], m_pixelPtrCacheCopy[i], channelCount * sizeof(qreal)); typename _IteratorFactory_::VLineConstIterator kitSrc = _IteratorFactory_::createVLineConstIterator(src, col + m_khalfWidth, row - m_khalfHeight, m_kh, dataRect); for (int pcol = 0; pcol < areaSize.width(); ++pcol) { // write original channel values memcpy(hitDst->rawData(), hitSrc->oldRawData(), m_pixelSize); convolveCache(hitDst->rawData()); ++col; kitSrc->nextColumn(); hitDst->nextPixel(); hitSrc->nextPixel(); moveKernelRight(kitSrc, m_pixelPtrCache); } row++; khitSrc->nextRow(); hitDst->nextRow(); hitSrc->nextRow(); col = srcPos.x(); moveKernelDown(khitSrc, m_pixelPtrCacheCopy); if (hasProgressUpdater) { this->m_progress->setValue(prow); if (this->m_progress->interrupted()) { cleanUp(); return; } } } } else if (traversingDirection == Vertical) { if(hasProgressUpdater) { this->m_progress->setRange(0, areaSize.width()); } typename _IteratorFactory_::VLineIterator vitDst = _IteratorFactory_::createVLineIterator(this->m_painter->device(), dstPos.x(), dstPos.y(), areaSize.height(), dataRect); typename _IteratorFactory_::VLineConstIterator vitSrc = _IteratorFactory_::createVLineConstIterator(src, srcPos.x(), srcPos.y(), areaSize.height(), dataRect); typename _IteratorFactory_::VLineConstIterator kitSrc = _IteratorFactory_::createVLineConstIterator(src, col + m_khalfWidth, row - m_khalfHeight, m_kh, dataRect); for (int pcol = 0; pcol < areaSize.width(); pcol++) { // reload cache from copy for (quint32 i = 0; i < m_cacheSize; ++i) memcpy(m_pixelPtrCache[i], m_pixelPtrCacheCopy[i], channelCount * sizeof(qreal)); typename _IteratorFactory_::HLineConstIterator khitSrc = _IteratorFactory_::createHLineConstIterator(src, col - m_khalfWidth, row + m_khalfHeight, m_kw, dataRect); for (int prow = 0; prow < areaSize.height(); prow++) { // write original channel values memcpy(vitDst->rawData(), vitSrc->oldRawData(), m_pixelSize); convolveCache(vitDst->rawData()); ++row; khitSrc->nextRow(); vitDst->nextPixel(); vitSrc->nextPixel(); moveKernelDown(khitSrc, m_pixelPtrCache); } ++col; kitSrc->nextColumn(); vitDst->nextColumn(); vitSrc->nextColumn(); row = srcPos.y(); moveKernelRight(kitSrc, m_pixelPtrCacheCopy); if (hasProgressUpdater) { this->m_progress->setValue(pcol); if (this->m_progress->interrupted()) { cleanUp(); return; } } } } cleanUp(); } inline void limitValue(qreal *value, qreal lowBound, qreal highBound) { if (*value > highBound) { *value = highBound; } else if (!(*value >= lowBound)) { // value < lowBound or value == NaN // IEEE compliant comparisons with NaN are always false *value = lowBound; } } template inline qreal convolveOneChannelFromCache(quint8* dstPtr, quint32 channel, qreal additionalMultiplier = 0.0) { qreal interimConvoResult = 0; for (quint32 pIndex = 0; pIndex < m_cacheSize; ++pIndex) { qreal cacheValue = m_pixelPtrCache[pIndex][channel]; interimConvoResult += m_kernelData[m_cacheSize - pIndex - 1] * cacheValue; } qreal channelPixelValue; if (additionalMultiplierActive) { channelPixelValue = (interimConvoResult * m_kernelFactor) * additionalMultiplier + m_absoluteOffset[channel]; } else { channelPixelValue = interimConvoResult * m_kernelFactor + m_absoluteOffset[channel]; } limitValue(&channelPixelValue, m_minClamp[channel], m_maxClamp[channel]); const quint32 channelPos = m_convChannelList[channel]->pos(); m_fromDoubleFuncPtr[channel](dstPtr, channelPos, channelPixelValue); return channelPixelValue; } inline void convolveCache(quint8* dstPtr) { if (m_alphaCachePos >= 0) { qreal alphaValue = convolveOneChannelFromCache(dstPtr, m_alphaCachePos); // TODO: we need a special case for applying LoG filter, // when the alpha i suniform and therefore should not be // filtered! //alphaValue = 255.0; if (alphaValue != 0.0) { qreal alphaValueInv = 1.0 / alphaValue; for (quint32 k = 0; k < m_convolveChannelsNo; ++k) { if (k == (quint32)m_alphaCachePos) continue; convolveOneChannelFromCache(dstPtr, k, alphaValueInv); } } else { for (quint32 k = 0; k < m_convolveChannelsNo; ++k) { if (k == (quint32)m_alphaCachePos) continue; const qreal zeroValue = 0.0; const quint32 channelPos = m_convChannelList[k]->pos(); m_fromDoubleFuncPtr[k](dstPtr, channelPos, zeroValue); } } } else { for (quint32 k = 0; k < m_convolveChannelsNo; ++k) { convolveOneChannelFromCache(dstPtr, k); } } } inline void moveKernelRight(typename _IteratorFactory_::VLineConstIterator& kitSrc, qreal **pixelPtrCache) { qreal** d = pixelPtrCache; for (quint32 krow = 0; krow < m_kh; ++krow) { qreal* first = *d; memmove(d, d + 1, (m_kw - 1) * sizeof(qreal *)); *(d + m_kw - 1) = first; d += m_kw; } qint32 i = m_kw - 1; do { const quint8* data = kitSrc->oldRawData(); loadPixelToCache(pixelPtrCache, data, i); i += m_kw; } while (kitSrc->nextPixel()); } inline void moveKernelDown(typename _IteratorFactory_::HLineConstIterator& kitSrc, qreal **pixelPtrCache) { quint8 **tmp = new quint8*[m_kw]; memcpy(tmp, pixelPtrCache, m_kw * sizeof(qreal *)); memmove(pixelPtrCache, pixelPtrCache + m_kw, (m_kw * m_kh - m_kw) * sizeof(quint8 *)); memcpy(pixelPtrCache + m_kw *(m_kh - 1), tmp, m_kw * sizeof(quint8 *)); delete[] tmp; qint32 i = m_kw * (m_kh - 1); do { const quint8* data = kitSrc->oldRawData(); loadPixelToCache(pixelPtrCache, data, i); i++; } while (kitSrc->nextPixel()); } void cleanUp() { for (quint32 c = 0; c < m_cacheSize; ++c) { delete[] m_pixelPtrCache[c]; delete[] m_pixelPtrCacheCopy[c]; } delete[] m_kernelData; delete[] m_pixelPtrCache; delete[] m_pixelPtrCacheCopy; delete[] m_minClamp; delete[] m_maxClamp; delete[] m_absoluteOffset; } private: quint32 m_kw, m_kh; quint32 m_khalfWidth, m_khalfHeight; quint32 m_convolveChannelsNo; quint32 m_cacheSize, m_pixelSize; int m_alphaCachePos; int m_alphaRealPos; qreal *m_kernelData; qreal** m_pixelPtrCache, ** m_pixelPtrCacheCopy; qreal* m_minClamp, *m_maxClamp, *m_absoluteOffset; qreal m_kernelFactor; QList m_convChannelList; QVector m_toDoubleFuncPtr; QVector m_fromDoubleFuncPtr; }; #endif diff --git a/libs/image/metadata/kis_meta_data_value.cc b/libs/image/metadata/kis_meta_data_value.cc index 408cbffe1c..37cc571a48 100644 --- a/libs/image/metadata/kis_meta_data_value.cc +++ b/libs/image/metadata/kis_meta_data_value.cc @@ -1,462 +1,462 @@ /* * Copyright (c) 2007,2010 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_meta_data_value.h" #include #include #include #include #include #include #include #include using namespace KisMetaData; struct Q_DECL_HIDDEN Value::Private { Private() : type(Invalid) {} union { QVariant* variant; QList* array; QMap* structure; KisMetaData::Rational* rational; } value; ValueType type; QMap propertyQualifiers; }; Value::Value() : d(new Private) { d->type = Invalid; } Value::Value(const QVariant& variant) : d(new Private) { d->type = Value::Variant; d->value.variant = new QVariant(variant); } Value::Value(const QList& array, ValueType type) : d(new Private) { Q_ASSERT(type == OrderedArray || type == UnorderedArray || type == AlternativeArray || type == LangArray); d->value.array = new QList(array); d->type = type; // TODO: I am hesitating about LangArray to keep them as array or convert them to maps } Value::Value(const QMap& structure) : d(new Private) { d->type = Structure; d->value.structure = new QMap(structure); } Value::Value(const KisMetaData::Rational& signedRational) : d(new Private) { d->type = Value::Rational; d->value.rational = new KisMetaData::Rational(signedRational); } Value::Value(const Value& v) : d(new Private) { d->type = Invalid; *this = v; } Value& Value::operator=(const Value & v) { d->type = v.d->type; d->propertyQualifiers = v.d->propertyQualifiers; switch (d->type) { case Invalid: break; case Variant: d->value.variant = new QVariant(*v.d->value.variant); break; case OrderedArray: case UnorderedArray: case AlternativeArray: case LangArray: d->value.array = new QList(*v.d->value.array); break; case Structure: d->value.structure = new QMap(*v.d->value.structure); break; case Rational: d->value.rational = new KisMetaData::Rational(*v.d->value.rational); } return *this; } Value::~Value() { delete d; } void Value::addPropertyQualifier(const QString& _name, const Value& _value) { d->propertyQualifiers[_name] = _value; } const QMap& Value::propertyQualifiers() const { return d->propertyQualifiers; } Value::ValueType Value::type() const { return d->type; } double Value::asDouble() const { switch (type()) { case Variant: return d->value.variant->toDouble(0); case Rational: return d->value.rational->numerator / (double)d->value.rational->denominator; default: return 0.0; } return 0.0; } int Value::asInteger() const { switch (type()) { case Variant: return d->value.variant->toInt(0); case Rational: return d->value.rational->numerator / d->value.rational->denominator; default: return 0; } return 0; } QVariant Value::asVariant() const { switch (type()) { case Variant: return *d->value.variant; case Rational: return QVariant(QString("%1 / %2").arg(d->value.rational->numerator).arg(d->value.rational->denominator)); default: break; } return QVariant(); } bool Value::setVariant(const QVariant& variant) { switch (type()) { case KisMetaData::Value::Invalid: *this = KisMetaData::Value(variant); return true; case Rational: { QRegExp rx("([^\\/]*)\\/([^\\/]*)"); rx.indexIn(variant.toString()); } case KisMetaData::Value::Variant: { if (d->value.variant->type() == variant.type()) { *d->value.variant = variant; return true; } } return true; default: break; } return false; } bool Value::setStructureVariant(const QString& fieldNAme, const QVariant& variant) { if (type() == Structure) { return (*d->value.structure)[fieldNAme].setVariant(variant); } return false; } bool Value::setArrayVariant(int index, const QVariant& variant) { if (isArray()) { for (int i = d->value.array->size(); i <= index; ++i) { d->value.array->append(Value()); } (*d->value.array)[index].setVariant(variant); } return false; } KisMetaData::Rational Value::asRational() const { if (d->type == Rational) { return *d->value.rational; } return KisMetaData::Rational(); } QList Value::asArray() const { if (isArray()) { return *d->value.array; } return QList(); } bool Value::isArray() const { return type() == OrderedArray || type() == UnorderedArray || type() == AlternativeArray; } QMap Value::asStructure() const { if (type() == Structure) { return *d->value.structure; } return QMap(); } QDebug operator<<(QDebug debug, const Value &v) { switch (v.type()) { case Value::Invalid: debug.nospace() << "invalid value"; break; case Value::Variant: debug.nospace() << "Variant: " << v.asVariant(); break; case Value::OrderedArray: case Value::UnorderedArray: case Value::AlternativeArray: case Value::LangArray: debug.nospace() << "Array: " << v.asArray(); break; case Value::Structure: debug.nospace() << "Structure: " << v.asStructure(); break; case Value::Rational: debug.nospace() << "Rational: " << v.asRational().numerator << " / " << v.asRational().denominator; break; } return debug.space(); } bool Value::operator==(const Value& rhs) const { if (d->type != rhs.d->type) return false; switch (d->type) { case Value::Invalid: return true; case Value::Variant: return asVariant() == rhs.asVariant(); case Value::OrderedArray: case Value::UnorderedArray: case Value::AlternativeArray: case Value::LangArray: return asArray() == rhs.asArray(); case Value::Structure: return asStructure() == rhs.asStructure(); case Value::Rational: return asRational() == rhs.asRational(); } return false; } Value& Value::operator+=(const Value & v) { switch (d->type) { case Value::Invalid: Q_ASSERT(v.type() == Value::Invalid); break; case Value::Variant: Q_ASSERT(v.type() == Value::Variant); { QVariant v1 = *d->value.variant; QVariant v2 = *v.d->value.variant; Q_ASSERT(v2.canConvert(v1.type())); switch (v1.type()) { default: break; case QVariant::Date: { QDate date; date.fromJulianDay(v1.toDate().toJulianDay() + v2.toDate().toJulianDay()); *d->value.variant = date; } break; case QVariant::DateTime: { QDateTime dt; - dt.fromTime_t( - v1.toDateTime().toTime_t() - + v2.toDateTime().toTime_t()); + dt.fromSecsSinceEpoch( + v1.toDateTime().toSecsSinceEpoch() + + v2.toDateTime().toSecsSinceEpoch()); *d->value.variant = dt; } break; case QVariant::Double: *d->value.variant = v1.toDouble() + v2.toDouble(); break; case QVariant::Int: *d->value.variant = v1.toInt() + v2.toInt(); break; case QVariant::List: *d->value.variant = v1.toList() + v2.toList(); break; case QVariant::LongLong: *d->value.variant = v1.toLongLong() + v2.toLongLong(); break; case QVariant::Point: *d->value.variant = v1.toPoint() + v2.toPoint(); break; case QVariant::PointF: *d->value.variant = v1.toPointF() + v2.toPointF(); break; case QVariant::String: *d->value.variant = QVariant(v1.toString() + v2.toString()); break; case QVariant::StringList: *d->value.variant = v1.toStringList() + v2.toStringList(); break; case QVariant::Time: { QTime t1 = v1.toTime(); QTime t2 = v2.toTime(); int h = t1.hour() + t2.hour(); int m = t1.minute() + t2.minute(); int s = t1.second() + t2.second(); int ms = t1.msec() + t2.msec(); if (ms > 999) { ms -= 999; s++; } if (s > 60) { s -= 60; m++; } if (m > 60) { m -= 60; h++; } if (h > 24) { h -= 24; } *d->value.variant = QTime(h, m, s, ms); } break; case QVariant::UInt: *d->value.variant = v1.toUInt() + v2.toUInt(); break; case QVariant::ULongLong: *d->value.variant = v1.toULongLong() + v2.toULongLong(); break; } } break; case Value::OrderedArray: case Value::UnorderedArray: case Value::AlternativeArray: { if (v.isArray()) { *(d->value.array) += *(v.d->value.array); } else { d->value.array->append(v); } } break; case Value::LangArray: { Q_ASSERT(v.type() == Value::LangArray); } break; case Value::Structure: { Q_ASSERT(v.type() == Value::Structure); break; } case Value::Rational: { Q_ASSERT(v.type() == Value::Rational); d->value.rational->numerator = (d->value.rational->numerator * v.d->value.rational->denominator) + (v.d->value.rational->numerator * d->value.rational->denominator); d->value.rational->denominator *= v.d->value.rational->denominator; break; } } return *this; } QMap Value::asLangArray() const { Q_ASSERT(d->type == LangArray); QMap langArray; Q_FOREACH (const KisMetaData::Value& val, *d->value.array) { Q_ASSERT(val.d->propertyQualifiers.contains("xml:lang")); // TODO propably worth to have an assert for this in the constructor as well KisMetaData::Value valKeyVal = val.d->propertyQualifiers.value("xml:lang"); Q_ASSERT(valKeyVal.type() == Variant); QVariant valKeyVar = valKeyVal.asVariant(); Q_ASSERT(valKeyVar.type() == QVariant::String); langArray[valKeyVar.toString()] = val; } return langArray; } QString Value::toString() const { switch (type()) { case Value::Invalid: return i18n("Invalid value."); case Value::Variant: return d->value.variant->toString(); break; case Value::OrderedArray: case Value::UnorderedArray: case Value::AlternativeArray: case Value::LangArray: { QString r = QString("[%1]{ ").arg(d->value.array->size()); for (int i = 0; i < d->value.array->size(); ++i) { const Value& val = d->value.array->at(i); r += val.toString(); if (i != d->value.array->size() - 1) { r += ','; } r += ' '; } r += '}'; return r; } case Value::Structure: { QString r = "{ "; QList fields = d->value.structure->keys(); for (int i = 0; i < fields.count(); ++i) { const QString& field = fields[i]; const Value& val = d->value.structure->value(field); r += field + " => " + val.toString(); if (i != d->value.array->size() - 1) { r += ','; } r += ' '; } r += '}'; return r; } break; case Value::Rational: return QString("%1 / %2").arg(d->value.rational->numerator).arg(d->value.rational->denominator); } return i18n("Invalid value."); } diff --git a/libs/image/tests/kis_paint_device_test.cpp b/libs/image/tests/kis_paint_device_test.cpp index 364cb31a84..a0072fcf9d 100644 --- a/libs/image/tests/kis_paint_device_test.cpp +++ b/libs/image/tests/kis_paint_device_test.cpp @@ -1,2269 +1,2269 @@ /* * Copyright (c) 2007 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_paint_device_test.h" #include #include #include #include #include #include #include "kis_paint_device_writer.h" #include "kis_painter.h" #include "kis_types.h" #include "kis_paint_device.h" #include "kis_layer.h" #include "kis_paint_layer.h" #include "kis_selection.h" #include "kis_datamanager.h" #include "kis_global.h" #include "testutil.h" #include "kis_transaction.h" #include "kis_image.h" class KisFakePaintDeviceWriter : public KisPaintDeviceWriter { public: KisFakePaintDeviceWriter(KoStore *store) : m_store(store) { } bool write(const QByteArray &data) override { return (m_store->write(data) == data.size()); } bool write(const char* data, qint64 length) override { return (m_store->write(data, length) == length); } KoStore *m_store; }; void KisPaintDeviceTest::testCreation() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QVERIFY(dev->objectName().isEmpty()); dev = new KisPaintDevice(cs); QVERIFY(*dev->colorSpace() == *cs); QVERIFY(dev->x() == 0); QVERIFY(dev->y() == 0); QVERIFY(dev->pixelSize() == cs->pixelSize()); QVERIFY(dev->channelCount() == cs->channelCount()); QVERIFY(dev->dataManager() != 0); KisImageSP image = new KisImage(0, 1000, 1000, cs, "merge test"); KisPaintLayerSP layer = new KisPaintLayer(image, "bla", 125); dev = new KisPaintDevice(layer.data(), cs); QVERIFY(*dev->colorSpace() == *cs); QVERIFY(dev->x() == 0); QVERIFY(dev->y() == 0); QVERIFY(dev->pixelSize() == cs->pixelSize()); QVERIFY(dev->channelCount() == cs->channelCount()); QVERIFY(dev->dataManager() != 0); // Let the layer go out of scope and see what happens { KisPaintLayerSP l2 = new KisPaintLayer(image, "blabla", 250); dev = new KisPaintDevice(l2.data(), cs); } } void KisPaintDeviceTest::testStore() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); KoStore * readStore = KoStore::createStore(QString(FILES_DATA_DIR) + QDir::separator() + "store_test.kra", KoStore::Read); readStore->open("built image/layers/layer0"); QVERIFY(dev->read(readStore->device())); readStore->close(); delete readStore; QVERIFY(dev->exactBounds() == QRect(0, 0, 100, 100)); KoStore * writeStore = KoStore::createStore(QString(FILES_OUTPUT_DIR) + QDir::separator() + "store_test_out.kra", KoStore::Write); KisFakePaintDeviceWriter fakeWriter(writeStore); writeStore->open("built image/layers/layer0"); QVERIFY(dev->write(fakeWriter)); writeStore->close(); delete writeStore; KisPaintDeviceSP dev2 = new KisPaintDevice(cs); readStore = KoStore::createStore(QString(FILES_OUTPUT_DIR) + QDir::separator() + "store_test_out.kra", KoStore::Read); readStore->open("built image/layers/layer0"); QVERIFY(dev2->read(readStore->device())); readStore->close(); delete readStore; QVERIFY(dev2->exactBounds() == QRect(0, 0, 100, 100)); QPoint pt; if (!TestUtil::comparePaintDevices(pt, dev, dev2)) { QFAIL(QString("Loading a saved image is not pixel perfect, first different pixel: %1,%2 ").arg(pt.x()).arg(pt.y()).toLatin1()); } } void KisPaintDeviceTest::testGeometry() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); quint8* pixel = new quint8[cs->pixelSize()]; cs->fromQColor(Qt::white, pixel); dev->fill(0, 0, 512, 512, pixel); QCOMPARE(dev->exactBounds(), QRect(0, 0, 512, 512)); QCOMPARE(dev->extent(), QRect(0, 0, 512, 512)); dev->moveTo(10, 10); QCOMPARE(dev->exactBounds(), QRect(10, 10, 512, 512)); QCOMPARE(dev->extent(), QRect(10, 10, 512, 512)); dev->crop(50, 50, 50, 50); QCOMPARE(dev->exactBounds(), QRect(50, 50, 50, 50)); QCOMPARE(dev->extent(), QRect(10, 10, 128, 128)); QColor c; dev->clear(QRect(50, 50, 50, 50)); dev->pixel(80, 80, &c); QVERIFY(c.alpha() == OPACITY_TRANSPARENT_U8); dev->fill(0, 0, 512, 512, pixel); dev->pixel(80, 80, &c); QVERIFY(c == Qt::white); QVERIFY(c.alpha() == OPACITY_OPAQUE_U8); dev->clear(); dev->pixel(80, 80, &c); QVERIFY(c.alpha() == OPACITY_TRANSPARENT_U8); QVERIFY(dev->extent().isEmpty()); QVERIFY(dev->exactBounds().isEmpty()); } void KisPaintDeviceTest::testClear() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QVERIFY(!dev->extent().isValid()); QVERIFY(!dev->exactBounds().isValid()); dev->clear(); QVERIFY(!dev->extent().isValid()); QVERIFY(!dev->exactBounds().isValid()); QRect fillRect1(50, 100, 150, 100); dev->fill(fillRect1, KoColor(Qt::red, cs)); QCOMPARE(dev->extent(), QRect(0, 64, 256, 192)); QCOMPARE(dev->exactBounds(), fillRect1); dev->clear(QRect(100, 100, 100, 100)); QCOMPARE(dev->extent(), QRect(0, 64, 256, 192)); QCOMPARE(dev->exactBounds(), QRect(50, 100, 50, 100)); dev->clear(); QVERIFY(!dev->extent().isValid()); QVERIFY(!dev->exactBounds().isValid()); } void KisPaintDeviceTest::testCrop() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); quint8* pixel = new quint8[cs->pixelSize()]; cs->fromQColor(Qt::white, pixel); dev->fill(-14, 8, 433, 512, pixel); QVERIFY(dev->exactBounds() == QRect(-14, 8, 433, 512)); // Crop inside dev->crop(50, 50, 150, 150); QVERIFY(dev->exactBounds() == QRect(50, 50, 150, 150)); // Crop outside, pd should not grow dev->crop(0, 0, 1000, 1000); QVERIFY(dev->exactBounds() == QRect(50, 50, 150, 150)); } void KisPaintDeviceTest::testRoundtripReadWrite() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png"); dev->convertFromQImage(image, 0); quint8* bytes = new quint8[cs->pixelSize() * image.width() * image.height()]; memset(bytes, 0, image.width() * image.height() * dev->pixelSize()); dev->readBytes(bytes, image.rect()); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->writeBytes(bytes, image.rect()); QVERIFY(dev2->exactBounds() == image.rect()); dev2->convertToQImage(0, 0, 0, image.width(), image.height()).save("readwrite.png"); QPoint pt; if (!TestUtil::comparePaintDevices(pt, dev, dev2)) { QFAIL(QString("Failed round trip using readBytes and writeBytes, first different pixel: %1,%2 ").arg(pt.x()).arg(pt.y()).toLatin1()); } } void logFailure(const QString & reason, const KoColorSpace * srcCs, const KoColorSpace * dstCs) { QString profile1("no profile"); QString profile2("no profile"); if (srcCs->profile()) profile1 = srcCs->profile()->name(); if (dstCs->profile()) profile2 = dstCs->profile()->name(); QWARN(QString("Failed %1 %2 -> %3 %4 %5") .arg(srcCs->name()) .arg(profile1) .arg(dstCs->name()) .arg(profile2) .arg(reason) .toLatin1()); } void KisPaintDeviceTest::testColorSpaceConversion() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png"); const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->rgb8(); const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->lab16(); KisPaintDeviceSP dev = new KisPaintDevice(srcCs); dev->convertFromQImage(image, 0); dev->moveTo(10, 10); // Unalign with tile boundaries KUndo2Command* cmd = dev->convertTo(dstCs); QCOMPARE(dev->exactBounds(), QRect(10, 10, image.width(), image.height())); QCOMPARE(dev->pixelSize(), dstCs->pixelSize()); QVERIFY(*dev->colorSpace() == *dstCs); cmd->redo(); cmd->undo(); QCOMPARE(dev->exactBounds(), QRect(10, 10, image.width(), image.height())); QCOMPARE(dev->pixelSize(), srcCs->pixelSize()); QVERIFY(*dev->colorSpace() == *srcCs); delete cmd; } void KisPaintDeviceTest::testRoundtripConversion() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); QImage result = dev->convertToQImage(0, 0, 0, 640, 441); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_paint_device_test_test_roundtrip_qimage.png"); result.save("kis_paint_device_test_test_roundtrip_result.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisPaintDeviceTest::testFastBitBlt() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dstDev = new KisPaintDevice(cs); KisPaintDeviceSP srcDev = new KisPaintDevice(cs); srcDev->convertFromQImage(image, 0); QRect cloneRect(100,100,200,200); QPoint errpoint; QVERIFY(dstDev->fastBitBltPossible(srcDev)); dstDev->fastBitBlt(srcDev, cloneRect); QImage srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); QImage dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); if (!TestUtil::compareQImages(errpoint, srcImage, dstImage)) { QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } // Test Rough version dstDev->clear(); dstDev->fastBitBltRough(srcDev, cloneRect); srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); if (!TestUtil::compareQImages(errpoint, srcImage, dstImage)) { QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } srcDev->moveTo(10,10); QVERIFY(!dstDev->fastBitBltPossible(srcDev)); } void KisPaintDeviceTest::testMakeClone() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP srcDev = new KisPaintDevice(cs); srcDev->convertFromQImage(image, 0); srcDev->moveTo(10,10); const KoColorSpace * weirdCS = KoColorSpaceRegistry::instance()->lab16(); KisPaintDeviceSP dstDev = new KisPaintDevice(weirdCS); dstDev->moveTo(1000,1000); QVERIFY(!dstDev->fastBitBltPossible(srcDev)); QRect cloneRect(100,100,200,200); QPoint errpoint; dstDev->makeCloneFrom(srcDev, cloneRect); QVERIFY(*dstDev->colorSpace() == *srcDev->colorSpace()); QCOMPARE(dstDev->pixelSize(), srcDev->pixelSize()); QCOMPARE(dstDev->x(), srcDev->x()); QCOMPARE(dstDev->y(), srcDev->y()); QCOMPARE(dstDev->exactBounds(), cloneRect); QImage srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); QImage dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(), cloneRect.width(), cloneRect.height()); if (!TestUtil::compareQImages(errpoint, dstImage, srcImage)) { QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisPaintDeviceTest::testThumbnail() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); { KisPaintDeviceSP thumb = dev->createThumbnailDevice(50, 50); QRect rc = thumb->exactBounds(); QVERIFY(rc.width() <= 50); QVERIFY(rc.height() <= 50); } { QImage thumb = dev->createThumbnail(50, 50); QVERIFY(!thumb.isNull()); QVERIFY(thumb.width() <= 50); QVERIFY(thumb.height() <= 50); image.save("kis_paint_device_test_test_thumbnail.png"); } } void KisPaintDeviceTest::testThumbnailDeviceWithOffset() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); dev->setX(10); dev->setY(10); QImage thumb = dev->createThumbnail(640,441,QRect(10,10,640,441)); image.save("oring.png"); thumb.save("thumb.png"); QPoint pt; QVERIFY(TestUtil::compareQImages(pt, thumb, image)); } void KisPaintDeviceTest::testCaching() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); quint8* whitePixel = new quint8[cs->pixelSize()]; cs->fromQColor(Qt::white, whitePixel); quint8* blackPixel = new quint8[cs->pixelSize()]; cs->fromQColor(Qt::black, blackPixel); dev->fill(0, 0, 512, 512, whitePixel); QImage thumb1 = dev->createThumbnail(50, 50); QRect exactBounds1 = dev->exactBounds(); dev->fill(0, 0, 768, 768, blackPixel); QImage thumb2 = dev->createThumbnail(50, 50); QRect exactBounds2 = dev->exactBounds(); dev->moveTo(10, 10); QImage thumb3 = dev->createThumbnail(50, 50); QRect exactBounds3 = dev->exactBounds(); dev->crop(50, 50, 50, 50); QImage thumb4 = dev->createThumbnail(50, 50); QRect exactBounds4 = dev->exactBounds(); QVERIFY(thumb1 != thumb2); QVERIFY(thumb2 == thumb3); // Cache miss, but image is the same QVERIFY(thumb3 != thumb4); QVERIFY(thumb4 != thumb1); QCOMPARE(exactBounds1, QRect(0,0,512,512)); QCOMPARE(exactBounds2, QRect(0,0,768,768)); QCOMPARE(exactBounds3, QRect(10,10,768,768)); QCOMPARE(exactBounds4, QRect(50,50,50,50)); } void KisPaintDeviceTest::testRegion() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); quint8* whitePixel = new quint8[cs->pixelSize()]; cs->fromQColor(Qt::white, whitePixel); dev->fill(0, 0, 10, 10, whitePixel); dev->fill(70, 70, 10, 10, whitePixel); dev->fill(129, 0, 10, 10, whitePixel); dev->fill(0, 1030, 10, 10, whitePixel); QRegion referenceRegion; referenceRegion += QRect(0,0,64,64); referenceRegion += QRect(64,64,64,64); referenceRegion += QRect(128,0,64,64); referenceRegion += QRect(0,1024,64,64); QCOMPARE(dev->exactBounds(), QRect(0,0,139,1040)); QCOMPARE(dev->region(), referenceRegion); } void KisPaintDeviceTest::testPixel() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QColor c = Qt::red; quint8 opacity = 125; c.setAlpha(opacity); dev->setPixel(5, 5, c); QColor c2; dev->pixel(5, 5, &c2); QVERIFY(c == c2); QVERIFY(opacity == c2.alpha()); } void KisPaintDeviceTest::testPlanarReadWrite() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); quint8* pixel = new quint8[cs->pixelSize()]; cs->fromQColor(QColor(255, 200, 155, 100), pixel); dev->fill(0, 0, 5000, 5000, pixel); delete[] pixel; QColor c1; dev->pixel(5, 5, &c1); QVector planes = dev->readPlanarBytes(500, 500, 100, 100); QVector swappedPlanes; QCOMPARE((int)planes.size(), (int)dev->channelCount()); for (int i = 0; i < 100*100; i++) { // BGRA encoded QVERIFY(planes.at(2)[i] == 255); QVERIFY(planes.at(1)[i] == 200); QVERIFY(planes.at(0)[i] == 155); QVERIFY(planes.at(3)[i] == 100); } for (uint i = 1; i < dev->channelCount() + 1; ++i) { swappedPlanes.append(planes[dev->channelCount() - i]); } dev->writePlanarBytes(swappedPlanes, 0, 0, 100, 100); dev->convertToQImage(0, 0, 0, 1000, 1000).save("planar.png"); dev->pixel(5, 5, &c1); QVERIFY(c1.red() == 200); QVERIFY(c1.green() == 255); QVERIFY(c1.blue() == 100); QVERIFY(c1.alpha() == 155); dev->pixel(75, 50, &c1); QVERIFY(c1.red() == 200); QVERIFY(c1.green() == 255); QVERIFY(c1.blue() == 100); QVERIFY(c1.alpha() == 155); // check if one of the planes is Null. Q_ASSERT(planes.size() == 4); delete [] planes[2]; planes[2] = 0; dev->writePlanarBytes(planes, 0, 0, 100, 100); dev->convertToQImage(0, 0, 0, 1000, 1000).save("planar_noR.png"); dev->pixel(75, 50, &c1); QCOMPARE(c1.red(), 200); QCOMPARE(c1.green(), 200); QCOMPARE(c1.blue(), 155); QCOMPARE(c1.alpha(), 100); QVector::iterator i; for (i = planes.begin(); i != planes.end(); ++i) { delete [] *i; } swappedPlanes.clear(); } void KisPaintDeviceTest::testBltPerformance() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP fdev = new KisPaintDevice(cs); fdev->convertFromQImage(image, 0); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data()); QTime t; t.start(); int x; for (x = 0; x < 1000; ++x) { KisPainter gc(dev); gc.bitBlt(QPoint(0, 0), fdev, image.rect()); } dbgKrita << x << "blits" << " done in " << t.elapsed() << "ms"; } void KisPaintDeviceTest::testDeviceDuplication() { QRect fillRect(0,0,64,64); quint8 fillPixel[4]={255,255,255,255}; QRect clearRect(10,10,20,20); QImage referenceImage; QImage resultImage; const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP device = new KisPaintDevice(cs); // dbgKrita<<"FILLING"; device->fill(fillRect.left(), fillRect.top(), fillRect.width(), fillRect.height(),fillPixel); referenceImage = device->convertToQImage(0); KisTransaction transaction1(device); // dbgKrita<<"CLEARING"; device->clear(clearRect); transaction1.revert(); resultImage = device->convertToQImage(0); QVERIFY(resultImage == referenceImage); KisPaintDeviceSP clone = new KisPaintDevice(*device); KisTransaction transaction(clone); // dbgKrita<<"CLEARING"; clone->clear(clearRect); transaction.revert(); resultImage = clone->convertToQImage(0); QVERIFY(resultImage == referenceImage); } void KisPaintDeviceTest::testTranslate() { QRect fillRect(0,0,64,64); quint8 fillPixel[4]={255,255,255,255}; const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP device = new KisPaintDevice(cs); device->fill(fillRect.left(), fillRect.top(), fillRect.width(), fillRect.height(),fillPixel); device->setX(-10); device->setY(10); QCOMPARE(device->exactBounds(), QRect(-10,10,64,64)); QCOMPARE(device->extent(), QRect(-10,10,64,64)); } void KisPaintDeviceTest::testOpacity() { // blt a semi-transparent image on a white paint device QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP fdev = new KisPaintDevice(cs); fdev->convertFromQImage(image, 0); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data()); KisPainter gc(dev); gc.bitBlt(QPoint(0, 0), fdev, image.rect()); QImage result = dev->convertToQImage(0, 0, 0, 640, 441); QImage checkResult(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent_result.png"); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, checkResult, result, 1)) { checkResult.save("kis_paint_device_test_test_blt_fixed_opactiy_expected.png"); result.save("kis_paint_device_test_test_blt_fixed_opacity_result.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisPaintDeviceTest::testExactBoundsWeirdNullAlphaCase() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QVERIFY(dev->exactBounds().isEmpty()); dev->fill(QRect(10,10,10,10), KoColor(Qt::white, cs)); QCOMPARE(dev->exactBounds(), QRect(10,10,10,10)); const quint8 weirdPixelData[4] = {0,10,0,0}; KoColor weirdColor(weirdPixelData, cs); dev->setPixel(6,6,weirdColor); // such weird pixels should not change our opinion about // device's size QCOMPARE(dev->exactBounds(), QRect(10,10,10,10)); } void KisPaintDeviceTest::benchmarkExactBoundsNullDefaultPixel() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QVERIFY(dev->exactBounds().isEmpty()); QRect fillRect(60,60, 1930, 1930); dev->fill(fillRect, KoColor(Qt::white, cs)); QRect measuredRect; QBENCHMARK { // invalidate the cache dev->setDirty(); measuredRect = dev->exactBounds(); } QCOMPARE(measuredRect, fillRect); } void KisPaintDeviceTest::testAmortizedExactBounds() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QVERIFY(dev->exactBounds().isEmpty()); QRect fillRect(60,60, 833, 833); QRect extent(0,0,896,896); dev->fill(fillRect, KoColor(Qt::white, cs)); QCOMPARE(dev->exactBounds(), fillRect); QCOMPARE(dev->extent(), extent); QCOMPARE(dev->exactBoundsAmortized(), fillRect); dev->setDirty(); QCOMPARE(dev->exactBoundsAmortized(), fillRect); dev->setDirty(); QCOMPARE(dev->exactBoundsAmortized(), extent); QTest::qSleep(1100); QCOMPARE(dev->exactBoundsAmortized(), fillRect); } void KisPaintDeviceTest::testNonDefaultPixelArea() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QVERIFY(dev->exactBounds().isEmpty()); QVERIFY(dev->nonDefaultPixelArea().isEmpty()); KoColor defPixel(Qt::red, cs); dev->setDefaultPixel(defPixel); QCOMPARE(dev->exactBounds(), KisDefaultBounds::infiniteRect); QVERIFY(dev->nonDefaultPixelArea().isEmpty()); QRect fillRect(10,11,18,14); dev->fill(fillRect, KoColor(Qt::white, cs)); QCOMPARE(dev->exactBounds(), KisDefaultBounds::infiniteRect); QCOMPARE(dev->nonDefaultPixelArea(), fillRect); // non-default pixel variant should also handle weird pixels const quint8 weirdPixelData[4] = {0,10,0,0}; KoColor weirdColor(weirdPixelData, cs); dev->setPixel(100,100,weirdColor); // such weird pixels should not change our opinion about // device's size QCOMPARE(dev->exactBounds(), KisDefaultBounds::infiniteRect); QCOMPARE(dev->nonDefaultPixelArea(), fillRect | QRect(100,100,1,1)); } void KisPaintDeviceTest::testExactBoundsNonTransparent() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisImageSP image = new KisImage(0, 1000, 1000, cs, "merge test"); KisPaintLayerSP layer = new KisPaintLayer(image, "bla", 125); KisPaintDeviceSP dev = layer->paintDevice(); QVERIFY(dev); QRect imageRect(0,0,1000,1000); KoColor defPixel(Qt::red, cs); dev->setDefaultPixel(defPixel); QCOMPARE(dev->exactBounds(), imageRect); QVERIFY(dev->nonDefaultPixelArea().isEmpty()); KoColor fillPixel(Qt::white, cs); dev->fill(imageRect, KoColor(Qt::white, cs)); QCOMPARE(dev->exactBounds(), imageRect); QCOMPARE(dev->nonDefaultPixelArea(), imageRect); dev->fill(QRect(1000,0, 1, 1000), KoColor(Qt::white, cs)); QCOMPARE(dev->exactBounds(), QRect(0,0,1001,1000)); QCOMPARE(dev->nonDefaultPixelArea(), QRect(0,0,1001,1000)); dev->fill(QRect(0,1000, 1000, 1), KoColor(Qt::white, cs)); QCOMPARE(dev->exactBounds(), QRect(0,0,1001,1001)); QCOMPARE(dev->nonDefaultPixelArea(), QRect(0,0,1001,1001)); dev->fill(QRect(0,-1, 1000, 1), KoColor(Qt::white, cs)); QCOMPARE(dev->exactBounds(), QRect(0,-1,1001,1002)); QCOMPARE(dev->nonDefaultPixelArea(), QRect(0,-1,1001,1002)); dev->fill(QRect(-1,0, 1, 1000), KoColor(Qt::white, cs)); QCOMPARE(dev->exactBounds(), QRect(-1,-1,1002,1002)); QCOMPARE(dev->nonDefaultPixelArea(), QRect(-1,-1,1002,1002)); } KisPaintDeviceSP createWrapAroundPaintDevice(const KoColorSpace *cs) { struct TestingDefaultBounds : public KisDefaultBoundsBase { QRect bounds() const override { return QRect(0,0,20,20); } bool wrapAroundMode() const override { return true; } int currentLevelOfDetail() const override { return 0; } int currentTime() const override { return 0; } bool externalFrameActive() const override { return false; } }; KisPaintDeviceSP dev = new KisPaintDevice(cs); KisDefaultBoundsBaseSP bounds = new TestingDefaultBounds(); dev->setDefaultBounds(bounds); return dev; } void checkReadWriteRoundTrip(KisPaintDeviceSP dev, const QRect &rc) { KisPaintDeviceSP deviceCopy = new KisPaintDevice(*dev.data()); QRect readRect(10, 10, 20, 20); int bufSize = rc.width() * rc.height() * dev->pixelSize(); QScopedArrayPointer buf1(new quint8[bufSize]); deviceCopy->readBytes(buf1.data(), rc); deviceCopy->clear(); QVERIFY(deviceCopy->extent().isEmpty()); QScopedArrayPointer buf2(new quint8[bufSize]); deviceCopy->writeBytes(buf1.data(), rc); deviceCopy->readBytes(buf2.data(), rc); QVERIFY(!memcmp(buf1.data(), buf2.data(), bufSize)); } void KisPaintDeviceTest::testReadBytesWrapAround() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs); KoColor c1(Qt::red, cs); KoColor c2(Qt::green, cs); dev->setPixel(3, 3, c1); dev->setPixel(18, 18, c2); const int pixelSize = dev->pixelSize(); { QRect readRect(10, 10, 20, 20); QScopedArrayPointer buf(new quint8[readRect.width() * readRect.height() * pixelSize]); dev->readBytes(buf.data(), readRect); //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final1.png"); QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize)); checkReadWriteRoundTrip(dev, readRect); } { // check weird case when the read rect is larger than wrap rect QRect readRect(10, 10, 30, 30); QScopedArrayPointer buf(new quint8[readRect.width() * readRect.height() * pixelSize]); dev->readBytes(buf.data(), readRect); //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final2.png"); QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize)); checkReadWriteRoundTrip(dev, readRect); } { // even more large QRect readRect(10, 10, 40, 40); QScopedArrayPointer buf(new quint8[readRect.width() * readRect.height() * pixelSize]); dev->readBytes(buf.data(), readRect); //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final3.png"); QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (32 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (33 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 32) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 33) * pixelSize, c1.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (32 + readRect.width() * 32) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (33 + readRect.width() * 33) * pixelSize, c1.data(), pixelSize)); checkReadWriteRoundTrip(dev, readRect); } { // check if the wrap rect contains the read rect entirely QRect readRect(1, 1, 10, 10); QScopedArrayPointer buf(new quint8[readRect.width() * readRect.height() * pixelSize]); dev->readBytes(buf.data(), readRect); //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final4.png"); QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize)); checkReadWriteRoundTrip(dev, readRect); } { // check if the wrap happens only on vertical side of the rect QRect readRect(1, 1, 29, 10); QScopedArrayPointer buf(new quint8[readRect.width() * readRect.height() * pixelSize]); dev->readBytes(buf.data(), readRect); //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final5.png"); QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (21 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (22 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize)); checkReadWriteRoundTrip(dev, readRect); } { // check if the wrap happens only on horizontal side of the rect QRect readRect(1, 1, 10, 29); QScopedArrayPointer buf(new quint8[readRect.width() * readRect.height() * pixelSize]); dev->readBytes(buf.data(), readRect); //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final6.png"); QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 21) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 22) * pixelSize, c1.data(), pixelSize)); checkReadWriteRoundTrip(dev, readRect); } } #include "kis_random_accessor_ng.h" void KisPaintDeviceTest::testWrappedRandomAccessor() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs); KoColor c1(Qt::red, cs); KoColor c2(Qt::green, cs); dev->setPixel(3, 3, c1); dev->setPixel(18, 18, c2); const int pixelSize = dev->pixelSize(); int x; int y; x = 3; y = 3; KisRandomAccessorSP dstIt = dev->createRandomAccessorNG(x, y); QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize)); QCOMPARE(dstIt->numContiguousColumns(x), 17); QCOMPARE(dstIt->numContiguousRows(y), 17); x = 23; y = 23; dstIt->moveTo(x, y); QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize)); QCOMPARE(dstIt->numContiguousColumns(x), 17); QCOMPARE(dstIt->numContiguousRows(y), 17); x = 3; y = 23; dstIt->moveTo(x, y); QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize)); QCOMPARE(dstIt->numContiguousColumns(x), 17); QCOMPARE(dstIt->numContiguousRows(y), 17); x = 23; y = 3; dstIt->moveTo(x, y); QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize)); QCOMPARE(dstIt->numContiguousColumns(x), 17); QCOMPARE(dstIt->numContiguousRows(y), 17); x = -17; y = 3; dstIt->moveTo(x, y); QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize)); QCOMPARE(dstIt->numContiguousColumns(x), 17); QCOMPARE(dstIt->numContiguousRows(y), 17); x = 3; y = -17; dstIt->moveTo(x, y); QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize)); QCOMPARE(dstIt->numContiguousColumns(x), 17); QCOMPARE(dstIt->numContiguousRows(y), 17); x = -17; y = -17; dstIt->moveTo(x, y); QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize)); QCOMPARE(dstIt->numContiguousColumns(x), 17); QCOMPARE(dstIt->numContiguousRows(y), 17); } #include "kis_iterator_ng.h" static bool nextRowGeneral(KisHLineIteratorSP it, int y, const QRect &rc) { it->nextRow(); return y < rc.height(); } static bool nextRowGeneral(KisVLineIteratorSP it, int y, const QRect &rc) { it->nextColumn(); return y < rc.width(); } template bool checkXY(const QPoint &pt, const QPoint &realPt) { Q_UNUSED(pt); Q_UNUSED(realPt); return false; } template <> bool checkXY(const QPoint &pt, const QPoint &realPt) { return pt == realPt; } template <> bool checkXY(const QPoint &pt, const QPoint &realPt) { return pt.x() == realPt.y() && pt.y() == realPt.x(); } #include template bool checkConseqPixels(int value, const QPoint &pt, const KisWrappedRect &wrappedRect) { Q_UNUSED(value); Q_UNUSED(pt); Q_UNUSED(wrappedRect); return false; } template <> bool checkConseqPixels(int value, const QPoint &pt, const KisWrappedRect &wrappedRect) { int x = KisWrappedRect::xToWrappedX(pt.x(), wrappedRect.wrapRect()); int borderX = wrappedRect.originalRect().x() + wrappedRect.wrapRect().width(); int conseq = x >= borderX ? wrappedRect.wrapRect().right() - x + 1 : borderX - x; conseq = qMin(conseq, wrappedRect.originalRect().right() - pt.x() + 1); return value == conseq; } template <> bool checkConseqPixels(int value, const QPoint &pt, const KisWrappedRect &wrappedRect) { Q_UNUSED(pt); Q_UNUSED(wrappedRect); return value == 1; } template IteratorSP createIterator(KisPaintDeviceSP dev, const QRect &rc) { Q_UNUSED(dev); Q_UNUSED(rc); return 0; } template <> KisHLineIteratorSP createIterator(KisPaintDeviceSP dev, const QRect &rc) { return dev->createHLineIteratorNG(rc.x(), rc.y(), rc.width()); } template <> KisVLineIteratorSP createIterator(KisPaintDeviceSP dev, const QRect &rc) { return dev->createVLineIteratorNG(rc.x(), rc.y(), rc.height()); } template void testWrappedLineIterator(QString testName, const QRect &rect) { testName = QString("%1_%2_%3_%4_%5") .arg(testName) .arg(rect.x()) .arg(rect.y()) .arg(rect.width()) .arg(rect.height()); const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs); // test rect fits the wrap rect in both dimensions IteratorSP it = createIterator(dev, rect); int y = 0; do { int x = 0; do { quint8 *data = it->rawData(); data[0] = 10 * x; data[1] = 10 * y; data[2] = 0; data[3] = 255; x++; } while (it->nextPixel()); } while (nextRowGeneral(it, ++y, rect)); QRect rc = dev->defaultBounds()->bounds() | dev->exactBounds(); QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QVERIFY(TestUtil::checkQImage(result, "paint_device_test", "wrapped_iterators", testName)); } template void testWrappedLineIterator(const QString &testName) { testWrappedLineIterator(testName, QRect(10,10,20,20)); testWrappedLineIterator(testName, QRect(10,10,10,20)); testWrappedLineIterator(testName, QRect(10,10,20,10)); testWrappedLineIterator(testName, QRect(10,10,10,10)); testWrappedLineIterator(testName, QRect(0,0,20,20)); } void KisPaintDeviceTest::testWrappedHLineIterator() { testWrappedLineIterator("hline_iterator"); } void KisPaintDeviceTest::testWrappedVLineIterator() { testWrappedLineIterator("vline_iterator"); } template void testWrappedLineIteratorReadMoreThanBounds(QString testName) { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs); KisPaintDeviceSP dst = new KisPaintDevice(cs); // fill device with a gradient QRect bounds = dev->defaultBounds()->bounds(); for (int y = bounds.y(); y < bounds.y() + bounds.height(); y++) { for (int x = bounds.x(); x < bounds.x() + bounds.width(); x++) { QColor c((10 * x) % 255, (10 * y) % 255, 0, 255); dev->setPixel(x, y, c); } } // test rect doesn't fit the wrap rect in both dimentions const QRect &rect(bounds.adjusted(-6,-6,8,8)); KisRandomAccessorSP dstIt = dst->createRandomAccessorNG(rect.x(), rect.y()); IteratorSP it = createIterator(dev, rect); for (int y = rect.y(); y < rect.y() + rect.height(); y++) { for (int x = rect.x(); x < rect.x() + rect.width(); x++) { quint8 *data = it->rawData(); QVERIFY(checkConseqPixels(it->nConseqPixels(), QPoint(x, y), KisWrappedRect(rect, bounds))); dstIt->moveTo(x, y); memcpy(dstIt->rawData(), data, cs->pixelSize()); QVERIFY(checkXY(QPoint(it->x(), it->y()), QPoint(x,y))); bool stepDone = it->nextPixel(); QCOMPARE(stepDone, x < rect.x() + rect.width() - 1); } if (!nextRowGeneral(it, y, rect)) break; } testName = QString("%1_%2_%3_%4_%5") .arg(testName) .arg(rect.x()) .arg(rect.y()) .arg(rect.width()) .arg(rect.height()); QRect rc = rect; QImage result = dst->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QImage ref = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QVERIFY(TestUtil::checkQImage(result, "paint_device_test", "wrapped_iterators_huge", testName, 1)); } void KisPaintDeviceTest::testWrappedHLineIteratorReadMoreThanBounds() { testWrappedLineIteratorReadMoreThanBounds("hline_iterator"); } void KisPaintDeviceTest::testWrappedVLineIteratorReadMoreThanBounds() { testWrappedLineIteratorReadMoreThanBounds("vline_iterator"); } void KisPaintDeviceTest::testMoveWrapAround() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs); KoColor c1(Qt::red, cs); KoColor c2(Qt::green, cs); dev->setPixel(3, 3, c1); dev->setPixel(18, 18, c2); // QRect rc = dev->defaultBounds()->bounds(); //dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()).save("move0.png"); QCOMPARE(dev->exactBounds(), QRect(3,3,16,16)); dev->moveTo(QPoint(10,10)); QCOMPARE(dev->exactBounds(), QRect(8,8,6,6)); //dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()).save("move1.png"); } #include "kis_lock_free_cache.h" #define NUM_TYPES 3 // high-concurrency #define NUM_CYCLES 500000 #define NUM_THREADS 4 struct TestingCache : KisLockFreeCache { int calculateNewValue() const override { return m_realValue; } QAtomicInt m_realValue; }; class CacheStressJob : public QRunnable { public: CacheStressJob(TestingCache &cache) : m_cache(cache), m_oldValue(0) { } void run() override { for(qint32 i = 0; i < NUM_CYCLES; i++) { qint32 type = i % NUM_TYPES; switch(type) { case 0: m_cache.m_realValue.ref(); m_oldValue = m_cache.m_realValue; m_cache.invalidate(); break; case 1: { int newValue = m_cache.getValue(); Q_ASSERT(newValue >= m_oldValue); Q_UNUSED(newValue); } break; case 3: QTest::qSleep(3); break; } } } private: TestingCache &m_cache; int m_oldValue; }; void KisPaintDeviceTest::testCacheState() { TestingCache cache; QList jobsList; CacheStressJob *job; for(qint32 i = 0; i < NUM_THREADS; i++) { //job = new CacheStressJob(value, cacheValue, cacheState); job = new CacheStressJob(cache); job->setAutoDelete(true); jobsList.append(job); } QThreadPool pool; pool.setMaxThreadCount(NUM_THREADS); Q_FOREACH (job, jobsList) { pool.start(job); } pool.waitForDone(); } struct TestingLodDefaultBounds : public KisDefaultBoundsBase { TestingLodDefaultBounds(const QRect &bounds = QRect(0,0,100,100)) : m_lod(0), m_bounds(bounds) {} QRect bounds() const override { return m_bounds; } bool wrapAroundMode() const override { return false; } int currentLevelOfDetail() const override { return m_lod; } int currentTime() const override { return 0; } bool externalFrameActive() const override { return false; } void testingSetLevelOfDetail(int lod) { m_lod = lod; } private: int m_lod; QRect m_bounds; }; void fillGradientDevice(KisPaintDeviceSP dev, const QRect &rect, bool flat = false) { if (flat) { dev->fill(rect, KoColor(Qt::red, dev->colorSpace())); } else { // fill device with a gradient KisSequentialIterator it(dev, rect); do { QColor c((10 * it.x()) & 0xFF, (10 * it.y()) & 0xFF, 0, 255); KoColor color(c, dev->colorSpace()); memcpy(it.rawData(), color.data(), dev->pixelSize()); } while (it.nextPixel()); } } #include "kis_lod_transform.h" void KisPaintDeviceTest::testLodTransform() { const int lod = 2; // round to 4 KisLodTransform t(lod); QRect rc1(-16, -16, 8, 8); QRect rc2(-16, -16, 7, 7); QRect rc3(-15, -15, 7, 7); QCOMPARE(t.alignedRect(rc1, lod), rc1); QCOMPARE(t.alignedRect(rc2, lod), rc1); QCOMPARE(t.alignedRect(rc3, lod), rc1); } #include "krita_utils.h" void syncLodCache(KisPaintDeviceSP dev, int levelOfDetail) { KisPaintDevice::LodDataStruct* s = dev->createLodDataStruct(levelOfDetail); QRegion region = dev->regionForLodSyncing(); Q_FOREACH(QRect rect2, KritaUtils::splitRegionIntoPatches(region, KritaUtils::optimalPatchSize())) { dev->updateLodDataStruct(s, rect2); } dev->uploadLodDataStruct(s); } void KisPaintDeviceTest::testLodDevice() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds(); dev->setDefaultBounds(bounds); // fill device with a gradient // QRect rect = dev->defaultBounds()->bounds(); // fillGradientDevice(dev, rect); fillGradientDevice(dev, QRect(50,50,30,30)); QCOMPARE(dev->exactBounds(), QRect(50,50,30,30)); QImage result; qDebug() << ppVar(dev->exactBounds()); result = dev->convertToQImage(0,0,0,100,100); /*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test", "lod", "initial")); bounds->testingSetLevelOfDetail(1); syncLodCache(dev, 1); QCOMPARE(dev->exactBounds(), QRect(25,25,15,15)); qDebug() << ppVar(dev->exactBounds()); result = dev->convertToQImage(0,0,0,100,100); /*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test", "lod", "lod1")); bounds->testingSetLevelOfDetail(2); QCOMPARE(dev->exactBounds(), QRect(25,25,15,15)); qDebug() << ppVar(dev->exactBounds()); result = dev->convertToQImage(0,0,0,100,100); /*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test", "lod", "lod1")); syncLodCache(dev, 2); QCOMPARE(dev->exactBounds(), QRect(12,12,8,8)); qDebug() << ppVar(dev->exactBounds()); result = dev->convertToQImage(0,0,0,100,100); /*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test", "lod", "lod2")); bounds->testingSetLevelOfDetail(0); dev->setX(20); dev->setY(10); bounds->testingSetLevelOfDetail(1); syncLodCache(dev, 1); QCOMPARE(dev->exactBounds(), QRect(35,30,15,15)); qDebug() << ppVar(dev->exactBounds()) << ppVar(dev->x()) << ppVar(dev->y()); result = dev->convertToQImage(0,0,0,100,100); /*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test", "lod", "lod1-offset-6-14")); } void KisPaintDeviceTest::benchmarkLod1Generation() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds(QRect(0,0,6000,4000)); dev->setDefaultBounds(bounds); // fill device with a gradient QRect rect = dev->defaultBounds()->bounds(); fillGradientDevice(dev, rect, true); QBENCHMARK { bounds->testingSetLevelOfDetail(1); syncLodCache(dev, 1); } } void KisPaintDeviceTest::benchmarkLod2Generation() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds(QRect(0,0,6000,4000)); dev->setDefaultBounds(bounds); // fill device with a gradient QRect rect = dev->defaultBounds()->bounds(); fillGradientDevice(dev, rect, true); QBENCHMARK { bounds->testingSetLevelOfDetail(2); syncLodCache(dev, 2); } } void KisPaintDeviceTest::benchmarkLod3Generation() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds(QRect(0,0,3000,2000)); dev->setDefaultBounds(bounds); // fill device with a gradient QRect rect = dev->defaultBounds()->bounds(); fillGradientDevice(dev, rect, true); QBENCHMARK { bounds->testingSetLevelOfDetail(3); syncLodCache(dev, 3); } } void KisPaintDeviceTest::benchmarkLod4Generation() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds(QRect(0,0,3000,2000)); dev->setDefaultBounds(bounds); // fill device with a gradient QRect rect = dev->defaultBounds()->bounds(); fillGradientDevice(dev, rect, true); QBENCHMARK { bounds->testingSetLevelOfDetail(4); syncLodCache(dev, 4); } } #include "kis_keyframe_channel.h" #include "kis_raster_keyframe_channel.h" #include "kis_paint_device_frames_interface.h" #include "testing_timed_default_bounds.h" void KisPaintDeviceTest::testFramesLeaking() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds(); dev->setDefaultBounds(bounds); KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content); QVERIFY(channel); KisPaintDeviceFramesInterface *i = dev->framesInterface(); QVERIFY(i); QCOMPARE(i->frames().size(), 1); KisPaintDeviceFramesInterface::TestingDataObjects o; // Itinial state: one frame, m_data is kept separate o = i->testingGetDataObjects(); QVERIFY(o.m_data); QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 1); QVERIFY(o.m_currentData == o.m_frames[0]); // add keyframe at position 10 channel->addKeyframe(10); // two frames, m_data has a default empty value o = i->testingGetDataObjects(); QVERIFY(o.m_data); QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 2); QVERIFY(o.m_currentData == o.m_frames[0]); // add keyframe at position 20 channel->addKeyframe(20); // three frames, m_data is default, current frame is 0 o = i->testingGetDataObjects(); QVERIFY(o.m_data); QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 3); QVERIFY(o.m_currentData == o.m_frames[0]); // switch to frame 10 bounds->testingSetTime(10); // three frames, m_data is default, current frame is 10 o = i->testingGetDataObjects(); QVERIFY(o.m_data); QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QVERIFY(o.m_currentData == o.m_frames[1]); QCOMPARE(o.m_frames.size(), 3); // switch to frame 20 bounds->testingSetTime(20); // three frames, m_data is default, current frame is 20 o = i->testingGetDataObjects(); QVERIFY(o.m_data); QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QVERIFY(o.m_currentData == o.m_frames[2]); QCOMPARE(o.m_frames.size(), 3); // switch to frame 15 bounds->testingSetTime(15); // three frames, m_data is default, current frame is 10 o = i->testingGetDataObjects(); QVERIFY(o.m_data); QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QVERIFY(o.m_currentData == o.m_frames[1]); QCOMPARE(o.m_frames.size(), 3); KisKeyframeSP key; // deletion of frame 0 is forbidden key = channel->keyframeAt(0); QVERIFY(key); QVERIFY(channel->deleteKeyframe(key)); // delete keyframe at position 11 key = channel->activeKeyframeAt(11); QVERIFY(key); QCOMPARE(key->time(), 10); QVERIFY(channel->deleteKeyframe(key)); // two frames, m_data is default, current frame is 0 o = i->testingGetDataObjects(); QVERIFY(o.m_data); QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); //QVERIFY(o.m_currentData == o.m_frames[0]); QCOMPARE(o.m_frames.size(), 2); // deletion of frame 0 is forbidden key = channel->activeKeyframeAt(11); QVERIFY(key); QCOMPARE(key->time(), 0); QVERIFY(channel->deleteKeyframe(key)); // nothing changed o = i->testingGetDataObjects(); QVERIFY(o.m_data); QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); //QVERIFY(o.m_currentData == o.m_frames[0]); QCOMPARE(o.m_frames.size(), 2); // delete keyframe at position 20 key = channel->activeKeyframeAt(20); QVERIFY(key); QCOMPARE(key->time(), 20); QVERIFY(channel->deleteKeyframe(key)); // one keyframe is left at position 0, m_data is default o = i->testingGetDataObjects(); QVERIFY(o.m_data); QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); //QVERIFY(o.m_currentData == o.m_frames[0]); QCOMPARE(o.m_frames.size(), 1); // ensure all the objects in the list of all objects are unique QList allObjects = i->testingGetDataObjectsList(); QSet uniqueObjects; Q_FOREACH (KisPaintDeviceData *obj, allObjects) { if (!obj) continue; QVERIFY(!uniqueObjects.contains(obj)); uniqueObjects.insert(obj); } } void KisPaintDeviceTest::testFramesUndoRedo() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds(); dev->setDefaultBounds(bounds); KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content); QVERIFY(channel); KisPaintDeviceFramesInterface *i = dev->framesInterface(); QVERIFY(i); QCOMPARE(i->frames().size(), 1); KisPaintDeviceFramesInterface::TestingDataObjects o; // Itinial state: one frame, m_data shared o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 1); QVERIFY(o.m_currentData == o.m_frames[0]); // add a keyframe KUndo2Command cmdAdd; int frameId = -1; const int time = 1; channel->addKeyframe(time, &cmdAdd); frameId = channel->frameIdAt(time); //int frameId = i->createFrame(false, 0, QPoint(), &cmdAdd); QCOMPARE(frameId, 1); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 2); QVERIFY(o.m_currentData == o.m_frames[0]); cmdAdd.undo(); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 1); QVERIFY(o.m_currentData == o.m_frames[0]); cmdAdd.redo(); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 2); QVERIFY(o.m_currentData == o.m_frames[0]); KUndo2Command cmdRemove; KisKeyframeSP keyframe = channel->keyframeAt(time); QVERIFY(keyframe); channel->deleteKeyframe(keyframe, &cmdRemove); //i->deleteFrame(1, &cmdRemove); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 1); QVERIFY(o.m_currentData == o.m_frames[0]); cmdRemove.undo(); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 2); QVERIFY(o.m_currentData == o.m_frames[0]); cmdRemove.redo(); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 1); QVERIFY(o.m_currentData == o.m_frames[0]); } void KisPaintDeviceTest::testFramesUndoRedoWithChannel() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds(); dev->setDefaultBounds(bounds); KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content); QVERIFY(channel); KisPaintDeviceFramesInterface *i = dev->framesInterface(); QVERIFY(i); QCOMPARE(i->frames().size(), 1); KisPaintDeviceFramesInterface::TestingDataObjects o; // Itinial state: one frame, m_data shared o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 1); QVERIFY(o.m_currentData == o.m_frames[0]); // add a keyframe KUndo2Command cmdAdd; KisKeyframeSP frame = channel->addKeyframe(10, &cmdAdd); QVERIFY(channel->keyframeAt(10)); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 2); QVERIFY(o.m_currentData == o.m_frames[0]); cmdAdd.undo(); QVERIFY(!channel->keyframeAt(10)); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 1); QVERIFY(o.m_currentData == o.m_frames[0]); cmdAdd.redo(); QVERIFY(channel->keyframeAt(10)); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 2); QVERIFY(o.m_currentData == o.m_frames[0]); KUndo2Command cmdRemove; channel->deleteKeyframe(frame, &cmdRemove); QVERIFY(!channel->keyframeAt(10)); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 1); QVERIFY(o.m_currentData == o.m_frames[0]); cmdRemove.undo(); QVERIFY(channel->keyframeAt(10)); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 2); QVERIFY(o.m_currentData == o.m_frames[0]); cmdRemove.redo(); QVERIFY(!channel->keyframeAt(10)); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 1); QVERIFY(o.m_currentData == o.m_frames[0]); cmdRemove.undo(); QVERIFY(channel->keyframeAt(10)); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 2); QVERIFY(o.m_currentData == o.m_frames[0]); KUndo2Command cmdMove; channel->moveKeyframe(frame, 12, &cmdMove); QVERIFY(!channel->keyframeAt(10)); QVERIFY(channel->keyframeAt(12)); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 2); QVERIFY(o.m_currentData == o.m_frames[0]); cmdMove.undo(); QVERIFY(channel->keyframeAt(10)); QVERIFY(!channel->keyframeAt(12)); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 2); QVERIFY(o.m_currentData == o.m_frames[0]); cmdMove.redo(); QVERIFY(!channel->keyframeAt(10)); QVERIFY(channel->keyframeAt(12)); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // default m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 2); QVERIFY(o.m_currentData == o.m_frames[0]); } void fillRect(KisPaintDeviceSP dev, int time, const QRect &rc, TestUtil::TestingTimedDefaultBounds *bounds) { KUndo2Command parentCommand; KisRasterKeyframeChannel *channel = dev->keyframeChannel(); KisKeyframeSP frame = channel->addKeyframe(time, &parentCommand); const int oldTime = bounds->currentTime(); bounds->testingSetTime(time); KoColor color(Qt::red, dev->colorSpace()); dev->fill(rc, color); bounds->testingSetTime(oldTime); } bool checkRect(KisPaintDeviceSP dev, int time, const QRect &rc, TestUtil::TestingTimedDefaultBounds *bounds) { const int oldTime = bounds->currentTime(); bounds->testingSetTime(time); bool result = dev->exactBounds() == rc; if (!result) { qDebug() << "Failed to check frame:" << ppVar(time) << ppVar(rc) << ppVar(dev->exactBounds()); } bounds->testingSetTime(oldTime); return result; } void testCrossDeviceFrameCopyImpl(bool useChannel) { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev1 = new KisPaintDevice(cs); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); const KoColorSpace *cs3 = KoColorSpaceRegistry::instance()->rgb16(); KisPaintDeviceSP dev3 = new KisPaintDevice(cs3); TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds(); dev1->setDefaultBounds(bounds); dev2->setDefaultBounds(bounds); dev3->setDefaultBounds(bounds); KisRasterKeyframeChannel *channel1 = dev1->createKeyframeChannel(KisKeyframeChannel::Content); KisPaintDeviceFramesInterface *i1 = dev1->framesInterface(); QVERIFY(channel1); QVERIFY(i1); KisRasterKeyframeChannel *channel2 = dev2->createKeyframeChannel(KisKeyframeChannel::Content); KisPaintDeviceFramesInterface *i2 = dev2->framesInterface(); QVERIFY(channel2); QVERIFY(i2); KisRasterKeyframeChannel *channel3 = dev3->createKeyframeChannel(KisKeyframeChannel::Content); KisPaintDeviceFramesInterface *i3 = dev3->framesInterface(); QVERIFY(channel3); QVERIFY(i3); fillRect(dev1, 10, QRect(100,100,100,100), bounds); fillRect(dev2, 20, QRect(200,200,100,100), bounds); fillRect(dev3, 30, QRect(300,300,100,100), bounds); QCOMPARE(dev1->exactBounds(), QRect()); const int dstFrameId1 = channel1->frameIdAt(10); const int srcFrameId2 = channel2->frameIdAt(20); const int srcFrameId3 = channel3->frameIdAt(30); KUndo2Command cmd1; if (!useChannel) { dev1->framesInterface()->uploadFrame(srcFrameId2, dstFrameId1, dev2); } else { KisKeyframeSP k = channel1->copyExternalKeyframe(channel2, 20, 10, &cmd1); } QCOMPARE(dev1->exactBounds(), QRect()); QVERIFY(checkRect(dev1, 10, QRect(200,200,100,100), bounds)); if (useChannel) { cmd1.undo(); QVERIFY(checkRect(dev1, 10, QRect(100,100,100,100), bounds)); } KUndo2Command cmd2; if (!useChannel) { dev1->framesInterface()->uploadFrame(srcFrameId3, dstFrameId1, dev3); } else { KisKeyframeSP k = channel1->copyExternalKeyframe(channel3, 30, 10, &cmd2); } QCOMPARE(dev1->exactBounds(), QRect()); QVERIFY(checkRect(dev1, 10, QRect(300,300,100,100), bounds)); if (useChannel) { cmd2.undo(); QVERIFY(checkRect(dev1, 10, QRect(100,100,100,100), bounds)); } } void KisPaintDeviceTest::testCrossDeviceFrameCopyDirect() { testCrossDeviceFrameCopyImpl(false); } void KisPaintDeviceTest::testCrossDeviceFrameCopyChannel() { testCrossDeviceFrameCopyImpl(true); } #include "kis_surrogate_undo_adapter.h" void KisPaintDeviceTest::testLazyFrameCreation() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds(); dev->setDefaultBounds(bounds); KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content); QVERIFY(channel); KisPaintDeviceFramesInterface *i = dev->framesInterface(); QVERIFY(i); QCOMPARE(i->frames().size(), 1); bounds->testingSetTime(10); QCOMPARE(i->frames().size(), 1); KisSurrogateUndoAdapter undoAdapter; { KisTransaction transaction1(dev); transaction1.commit(&undoAdapter); } QCOMPARE(i->frames().size(), 2); undoAdapter.undoAll(); QCOMPARE(i->frames().size(), 1); undoAdapter.redoAll(); QCOMPARE(i->frames().size(), 2); } void KisPaintDeviceTest::testCopyPaintDeviceWithFrames() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds(); dev->setDefaultBounds(bounds); KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content); QVERIFY(channel); KisPaintDeviceFramesInterface *i = dev->framesInterface(); QVERIFY(i); QCOMPARE(i->frames().size(), 1); KisPaintDeviceFramesInterface::TestingDataObjects o; // Itinial state: one frame, m_data shared o = i->testingGetDataObjects(); QVERIFY(o.m_data); // m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 1); QVERIFY(o.m_currentData == o.m_frames[0]); // add a keyframe KUndo2Command cmdAdd; KisKeyframeSP frame = channel->addKeyframe(10, &cmdAdd); QVERIFY(channel->keyframeAt(10)); o = i->testingGetDataObjects(); QVERIFY(o.m_data); // m_data should always be present QVERIFY(!o.m_lodData); QVERIFY(!o.m_externalFrameData); QCOMPARE(o.m_frames.size(), 2); //QVERIFY(o.m_currentData == o.m_frames[0]); KisPaintDeviceSP newDev = new KisPaintDevice(*dev, true, 0); QVERIFY(channel->keyframeAt(0)); QVERIFY(channel->keyframeAt(10)); } #include #include #include #include #include #include #include #include "KoCompositeOpRegistry.h" using namespace boost::accumulators; accumulator_set > accum; void KisPaintDeviceTest::testCompositionAssociativity() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); qsrand(500); boost::mt11213b _rnd0(qrand()); boost::mt11213b _rnd1(qrand()); boost::mt11213b _rnd2(qrand()); boost::mt11213b _rnd3(qrand()); boost::uniform_smallint rnd0(0, 255); boost::uniform_smallint rnd1(0, 255); boost::uniform_smallint rnd2(0, 255); boost::uniform_smallint rnd3(0, 255); QList allCompositeOps = cs->compositeOps(); Q_FOREACH (const KoCompositeOp *op, allCompositeOps) { accumulator_set > accum; const int numIterations = 10000; for (int j = 0; j < numIterations; j++) { KoColor c1(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs); KoColor c2(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs); KoColor c3(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs); //KoColor c4(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs); //KoColor c5(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs); KoColor r1(QColor(Qt::transparent), cs); KoColor r2(QColor(Qt::transparent), cs); KoColor r3(QColor(Qt::transparent), cs); op->composite(r1.data(), 0, c1.data(), 0, 0,0, 1,1, 255); op->composite(r1.data(), 0, c2.data(), 0, 0,0, 1,1, 255); op->composite(r1.data(), 0, c3.data(), 0, 0,0, 1,1, 255); //op->composite(r1.data(), 0, c4.data(), 0, 0,0, 1,1, 255); //op->composite(r1.data(), 0, c5.data(), 0, 0,0, 1,1, 255); op->composite(r3.data(), 0, c2.data(), 0, 0,0, 1,1, 255); op->composite(r3.data(), 0, c3.data(), 0, 0,0, 1,1, 255); //op->composite(r3.data(), 0, c4.data(), 0, 0,0, 1,1, 255); //op->composite(r3.data(), 0, c5.data(), 0, 0,0, 1,1, 255); op->composite(r2.data(), 0, c1.data(), 0, 0,0, 1,1, 255); op->composite(r2.data(), 0, r3.data(), 0, 0,0, 1,1, 255); const quint8 *p1 = r1.data(); const quint8 *p2 = r2.data(); if (memcmp(p1, p2, 4) != 0) { for (int i = 0; i < 4; i++) { accum(qAbs(p1[i] - p2[i])); } } } qDebug("Errors for op %25s err rate %7.2f var %7.2f max %7.2f", - op->id().toAscii().data(), + op->id().toLatin1().data(), (qreal(count(accum)) / (4 * numIterations)), variance(accum), count(accum) > 0 ? (max)(accum) : 0); } } QTEST_MAIN(KisPaintDeviceTest) diff --git a/libs/libkis/Document.cpp b/libs/libkis/Document.cpp index eebc07d58a..a171608279 100644 --- a/libs/libkis/Document.cpp +++ b/libs/libkis/Document.cpp @@ -1,700 +1,700 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "Document.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct Document::Private { Private() {} QPointer document; }; Document::Document(KisDocument *document, QObject *parent) : QObject(parent) , d(new Private) { d->document = document; } Document::~Document() { delete d; } bool Document::operator==(const Document &other) const { return (d->document == other.d->document); } bool Document::operator!=(const Document &other) const { return !(operator==(other)); } bool Document::batchmode() const { if (!d->document) return false; return d->document->fileBatchMode(); } void Document::setBatchmode(bool value) { if (!d->document) return; d->document->setFileBatchMode(value); } Node *Document::activeNode() const { QList activeNodes; Q_FOREACH(QPointer view, KisPart::instance()->views()) { if (view && view->document() == d->document) { activeNodes << view->currentNode(); } } if (activeNodes.size() > 0) { return new Node(d->document->image(), activeNodes.first()); } return new Node(d->document->image(), d->document->image()->root()->firstChild()); } void Document::setActiveNode(Node* value) { if (!value->node()) return; KisMainWindow *mainWin = KisPart::instance()->currentMainwindow(); if (!mainWin) return; KisViewManager *viewManager = mainWin->viewManager(); if (!viewManager) return; if (viewManager->document() != d->document) return; KisNodeManager *nodeManager = viewManager->nodeManager(); if (!nodeManager) return; KisNodeSelectionAdapter *selectionAdapter = nodeManager->nodeSelectionAdapter(); if (!selectionAdapter) return; selectionAdapter->setActiveNode(value->node()); } QList Document::topLevelNodes() const { if (!d->document) return QList(); Node n(d->document->image(), d->document->image()->rootLayer()); return n.childNodes(); } Node *Document::nodeByName(const QString &name) const { if (!d->document) return 0; KisNodeSP node = d->document->image()->rootLayer()->findChildByName(name); return new Node(d->document->image(), node); } QString Document::colorDepth() const { if (!d->document) return ""; return d->document->image()->colorSpace()->colorDepthId().id(); } QString Document::colorModel() const { if (!d->document) return ""; return d->document->image()->colorSpace()->colorModelId().id(); } QString Document::colorProfile() const { if (!d->document) return ""; return d->document->image()->colorSpace()->profile()->name(); } bool Document::setColorProfile(const QString &value) { if (!d->document) return false; if (!d->document->image()) return false; const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileByName(value); if (!profile) return false; bool retval = d->document->image()->assignImageProfile(profile); d->document->image()->setModified(); d->document->image()->initialRefreshGraph(); return retval; } bool Document::setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile) { if (!d->document) return false; if (!d->document->image()) return false; const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, colorProfile); if (!colorSpace) return false; d->document->image()->convertImageColorSpace(colorSpace, KoColorConversionTransformation::IntentPerceptual, KoColorConversionTransformation::HighQuality | KoColorConversionTransformation::NoOptimization); d->document->image()->setModified(); d->document->image()->initialRefreshGraph(); return true; } QString Document::documentInfo() const { QDomDocument doc = KisDocument::createDomDocument("document-info" /*DTD name*/, "document-info" /*tag name*/, "1.1"); doc = d->document->documentInfo()->save(doc); return doc.toString(); } void Document::setDocumentInfo(const QString &document) { KoXmlDocument doc; QString errorMsg; int errorLine, errorColumn; doc.setContent(document, &errorMsg, &errorLine, &errorColumn); d->document->documentInfo()->load(doc); } QString Document::fileName() const { - if (!d->document) return QString::null; + if (!d->document) return QString(); return d->document->url().toLocalFile(); } void Document::setFileName(QString value) { if (!d->document) return; d->document->setUrl(QUrl::fromLocalFile(value)); } int Document::height() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->height(); } void Document::setHeight(int value) { if (!d->document) return; if (!d->document->image()) return; resizeImage(d->document->image()->bounds().x(), d->document->image()->bounds().y(), d->document->image()->width(), value); } QString Document::name() const { if (!d->document) return ""; return d->document->documentInfo()->aboutInfo("title"); } void Document::setName(QString value) { if (!d->document) return; d->document->documentInfo()->setAboutInfo("title", value); } int Document::resolution() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return qRound(d->document->image()->xRes() * 72); } void Document::setResolution(int value) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; d->document->image()->setResolution(value / 72.0, value / 72.0); } Node *Document::rootNode() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return new Node(image, image->root()); } Selection *Document::selection() const { if (!d->document) return 0; if (!d->document->image()) return 0; if (!d->document->image()->globalSelection()) return 0; return new Selection(d->document->image()->globalSelection()); } void Document::setSelection(Selection* value) { if (!d->document) return; if (!d->document->image()) return; if (value) { d->document->image()->setGlobalSelection(value->selection()); } else { d->document->image()->setGlobalSelection(0); } } int Document::width() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->width(); } void Document::setWidth(int value) { if (!d->document) return; if (!d->document->image()) return; resizeImage(d->document->image()->bounds().x(), d->document->image()->bounds().y(), value, d->document->image()->height()); } int Document::xOffset() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->bounds().x(); } void Document::setXOffset(int x) { if (!d->document) return; if (!d->document->image()) return; resizeImage(x, d->document->image()->bounds().y(), d->document->image()->width(), d->document->image()->height()); } int Document::yOffset() const { if (!d->document) return 0; KisImageSP image = d->document->image(); if (!image) return 0; return image->bounds().y(); } void Document::setYOffset(int y) { if (!d->document) return; if (!d->document->image()) return; resizeImage(d->document->image()->bounds().x(), y, d->document->image()->width(), d->document->image()->height()); } double Document::xRes() const { if (!d->document) return 0.0; if (!d->document->image()) return 0.0; return d->document->image()->xRes(); } void Document::setXRes(double xRes) const { if (!d->document) return; if (!d->document->image()) return; d->document->image()->setResolution(xRes, d->document->image()->yRes()); } double Document::yRes() const { if (!d->document) return 0.0; if (!d->document->image()) return 0.0; return d->document->image()->yRes(); } void Document::setYRes(double yRes) const { if (!d->document) return; if (!d->document->image()) return; d->document->image()->setResolution(d->document->image()->xRes(), yRes); } QByteArray Document::pixelData(int x, int y, int w, int h) const { QByteArray ba; if (!d->document) return ba; KisImageSP image = d->document->image(); if (!image) return ba; KisPaintDeviceSP dev = image->projection(); ba.resize(w * h * dev->pixelSize()); dev->readBytes(reinterpret_cast(ba.data()), x, y, w, h); return ba; } bool Document::close() { bool retval = d->document->closeUrl(false); Q_FOREACH(KisView *view, KisPart::instance()->views()) { if (view->document() == d->document) { view->close(); view->deleteLater(); } } KisPart::instance()->removeDocument(d->document); d->document = 0; return retval; } void Document::crop(int x, int y, int w, int h) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; QRect rc(x, y, w, h); image->cropImage(rc); } bool Document::exportImage(const QString &filename, const InfoObject &exportConfiguration) { if (!d->document) return false; const QString outputFormatString = KisMimeDatabase::mimeTypeForFile(filename); const QByteArray outputFormat = outputFormatString.toLatin1(); return d->document->exportDocumentSync(QUrl::fromLocalFile(filename), outputFormat, exportConfiguration.configuration()); } void Document::flatten() { if (!d->document) return; if (!d->document->image()) return; d->document->image()->flatten(); } void Document::resizeImage(int x, int y, int w, int h) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; QRect rc; rc.setX(x); rc.setY(y); rc.setWidth(w); rc.setHeight(h); image->resizeImage(rc); } void Document::scaleImage(int w, int h, int xres, int yres, QString strategy) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; QRect rc = image->bounds(); rc.setWidth(w); rc.setHeight(h); KisFilterStrategy *actualStrategy = KisFilterStrategyRegistry::instance()->get(strategy); if (!actualStrategy) actualStrategy = KisFilterStrategyRegistry::instance()->get("Bicubic"); image->scaleImage(rc.size(), xres, yres, actualStrategy); } void Document::rotateImage(double radians) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; image->rotateImage(radians); } void Document::shearImage(double angleX, double angleY) { if (!d->document) return; KisImageSP image = d->document->image(); if (!image) return; image->shear(angleX, angleY); } bool Document::save() { if (!d->document) return false; bool retval = d->document->save(true, 0); d->document->waitForSavingToComplete(); return retval; } bool Document::saveAs(const QString &filename) { if (!d->document) return false; const QString outputFormatString = KisMimeDatabase::mimeTypeForFile(filename); const QByteArray outputFormat = outputFormatString.toLatin1(); bool retval = d->document->saveAs(QUrl::fromLocalFile(filename), outputFormat, true); d->document->waitForSavingToComplete(); return retval; } Node* Document::createNode(const QString &name, const QString &nodeType) { if (!d->document) return 0; if (!d->document->image()) return 0; KisImageSP image = d->document->image(); Node *node = 0; if (nodeType == "paintlayer") { node = new Node(image, new KisPaintLayer(image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "grouplayer") { node = new Node(image, new KisGroupLayer(image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "filelayer") { node = new Node(image, new KisFileLayer(image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "filterlayer") { node = new Node(image, new KisAdjustmentLayer(image, name, 0, 0)); } else if (nodeType == "filllayer") { node = new Node(image, new KisGeneratorLayer(image, name, 0, 0)); } else if (nodeType == "clonelayer") { node = new Node(image, new KisCloneLayer(0, image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "vectorlayer") { node = new Node(image, new KisShapeLayer(d->document->shapeController(), image, name, OPACITY_OPAQUE_U8)); } else if (nodeType == "transparencymask") { node = new Node(image, new KisTransparencyMask()); } else if (nodeType == "filtermask") { node = new Node(image, new KisFilterMask()); } else if (nodeType == "transformmask") { node = new Node(image, new KisTransformMask()); } else if (nodeType == "selectionmask") { node = new Node(image, new KisSelectionMask(image)); } return node; } QImage Document::projection(int x, int y, int w, int h) const { if (!d->document || !d->document->image()) return QImage(); return d->document->image()->convertToQImage(x, y, w, h, 0); } QImage Document::thumbnail(int w, int h) const { if (!d->document || !d->document->image()) return QImage(); return d->document->generatePreview(QSize(w, h)).toImage(); } void Document::lock() { if (!d->document || !d->document->image()) return; d->document->image()->barrierLock(); } void Document::unlock() { if (!d->document || !d->document->image()) return; d->document->image()->unlock(); } void Document::waitForDone() { if (!d->document || !d->document->image()) return; d->document->image()->waitForDone(); } bool Document::tryBarrierLock() { if (!d->document || !d->document->image()) return false; return d->document->image()->tryBarrierLock(); } bool Document::isIdle() { if (!d->document || !d->document->image()) return false; return d->document->image()->isIdle(); } void Document::refreshProjection() { if (!d->document || !d->document->image()) return; d->document->image()->refreshGraph(); } QList Document::horizontalGuides() const { QList lines; if (!d->document || !d->document->image()) return lines; KisCoordinatesConverter converter; converter.setImage(d->document->image()); QTransform transform = converter.imageToDocumentTransform().inverted(); QList untransformedLines = d->document->guidesConfig().horizontalGuideLines(); for (int i = 0; i< untransformedLines.size(); i++) { qreal line = untransformedLines[i]; lines.append(transform.map(QPointF(line, line)).x()); } return lines; } QList Document::verticalGuides() const { QList lines; if (!d->document || !d->document->image()) return lines; KisCoordinatesConverter converter; converter.setImage(d->document->image()); QTransform transform = converter.imageToDocumentTransform().inverted(); QList untransformedLines = d->document->guidesConfig().verticalGuideLines(); for (int i = 0; i< untransformedLines.size(); i++) { qreal line = untransformedLines[i]; lines.append(transform.map(QPointF(line, line)).y()); } return lines; } bool Document::guidesVisible() const { return d->document->guidesConfig().lockGuides(); } bool Document::guidesLocked() const { return d->document->guidesConfig().showGuides(); } void Document::setHorizontalGuides(const QList &lines) { if (!d->document) return; KisGuidesConfig config = d->document->guidesConfig(); KisCoordinatesConverter converter; converter.setImage(d->document->image()); QTransform transform = converter.imageToDocumentTransform(); QList transformedLines; for (int i = 0; i< lines.size(); i++) { qreal line = lines[i]; transformedLines.append(transform.map(QPointF(line, line)).x()); } config.setHorizontalGuideLines(transformedLines); d->document->setGuidesConfig(config); } void Document::setVerticalGuides(const QList &lines) { if (!d->document) return; KisGuidesConfig config = d->document->guidesConfig(); KisCoordinatesConverter converter; converter.setImage(d->document->image()); QTransform transform = converter.imageToDocumentTransform(); QList transformedLines; for (int i = 0; i< lines.size(); i++) { qreal line = lines[i]; transformedLines.append(transform.map(QPointF(line, line)).y()); } config.setVerticalGuideLines(transformedLines); d->document->setGuidesConfig(config); } void Document::setGuidesVisible(bool visible) { if (!d->document) return; KisGuidesConfig config = d->document->guidesConfig(); config.setShowGuides(visible); d->document->setGuidesConfig(config); } void Document::setGuidesLocked(bool locked) { if (!d->document) return; KisGuidesConfig config = d->document->guidesConfig(); config.setLockGuides(locked); d->document->setGuidesConfig(config); } QPointer Document::document() const { return d->document; } diff --git a/libs/libqml/DocumentListModel.cpp b/libs/libqml/DocumentListModel.cpp index 5a1af15ae8..eeef8bc785 100644 --- a/libs/libqml/DocumentListModel.cpp +++ b/libs/libqml/DocumentListModel.cpp @@ -1,182 +1,188 @@ /* This file is part of the KDE project * Copyright (C) 2012 KO GmbH. Contact: 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 "DocumentListModel.h" #include #include #include class DocumentListModel::Private { public: Private( DocumentListModel *qq) : q(qq), filter(DocumentListModel::UnknownType) { } void relayout(); DocumentListModel* q; QList allDocumentInfos; QList currentDocumentInfos; DocumentType filter; QString searchPattern; QTimer *timer; }; QHash DocumentListModel::sm_extensions = QHash(); DocumentListModel::DocumentListModel(QObject *parent) : QAbstractListModel(parent), d(new Private(this)) { qRegisterMetaType(); +} + +DocumentListModel::~DocumentListModel() +{ +} + +QHash DocumentListModel::roleNames() const +{ QHash roleNames = QAbstractListModel::roleNames(); roleNames[FileNameRole] = "fileName"; roleNames[FilePathRole] = "filePath"; roleNames[DocTypeRole] = "docType"; roleNames[FileSizeRole] = "fileSize"; roleNames[AuthorNameRole] = "authorName"; roleNames[AccessedTimeRole] = "accessedTime"; roleNames[ModifiedTimeRole] = "modifiedTime"; roleNames[UUIDRole] = "uuid"; - setRoleNames(roleNames); -} -DocumentListModel::~DocumentListModel() -{ + return roleNames; } void DocumentListModel::addDocument(const DocumentInfo &info) { if (d->allDocumentInfos.contains(info)) { return; } d->allDocumentInfos.append(info); } int DocumentListModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return d->currentDocumentInfos.count(); } int DocumentListModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return 1; } QVariant DocumentListModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } const int row = index.row(); const DocumentInfo &info = d->currentDocumentInfos[row]; switch (role) { case FileNameRole: // intentional fall through case Qt::DisplayRole: return info.fileName; case FilePathRole: return info.filePath; case DocTypeRole: return info.docType; case FileSizeRole: return info.fileSize; case AuthorNameRole: return info.authorName; case AccessedTimeRole: return prettyTime(info.accessedTime); case ModifiedTimeRole: return prettyTime(info.modifiedTime); case UUIDRole: return info.uuid; default: return QVariant(); } } QString DocumentListModel::prettyTime( const QDateTime& theTime) { return QLocale().toString(theTime, QLocale::LongFormat); } QVariant DocumentListModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(section) Q_UNUSED(orientation) Q_UNUSED(role) return QVariant(); } DocumentListModel::DocumentType DocumentListModel::filter() { return d->filter; } void DocumentListModel::setFilter( DocumentListModel::DocumentType newFilter) { d->filter = newFilter; d->relayout(); } DocumentListModel::DocumentType DocumentListModel::typeForFile ( const QString& file ) { if (sm_extensions.isEmpty()) { sm_extensions["odt"] = TextDocumentType; sm_extensions["fodt"] = TextDocumentType; sm_extensions["doc"] = TextDocumentType; sm_extensions["docx"] = TextDocumentType; sm_extensions["txt"] = TextDocumentType; sm_extensions["odp"] = PresentationType; sm_extensions["fodp"] = PresentationType; sm_extensions["ppt"] = PresentationType; sm_extensions["pptx"] = PresentationType; sm_extensions["ods"] = SpreadsheetType; sm_extensions["fods"] = SpreadsheetType; sm_extensions["xls"] = SpreadsheetType; sm_extensions["xlsx"] = SpreadsheetType; sm_extensions["pdf"] = PDFDocumentType; } QString ext = file.split('.').last().toLower(); if (sm_extensions.contains(ext)) { return sm_extensions.value(ext); } return UnknownType; } void DocumentListModel::Private::relayout() { emit q->layoutAboutToBeChanged(); QList newList; Q_FOREACH (const DocumentInfo &docInfo, allDocumentInfos) { if (filter == UnknownType || docInfo.docType == filter) { if (searchPattern.isEmpty() || docInfo.fileName.contains(searchPattern, Qt::CaseInsensitive)) { newList.append(docInfo); } } } currentDocumentInfos = newList; emit q->layoutChanged(); - q->reset(); // ## Required for <= Qt 4.7.2 + q->beginResetModel(); + q->endResetModel(); } diff --git a/libs/libqml/DocumentListModel.h b/libs/libqml/DocumentListModel.h index b9c448868f..afb34bfabf 100644 --- a/libs/libqml/DocumentListModel.h +++ b/libs/libqml/DocumentListModel.h @@ -1,100 +1,102 @@ /* This file is part of the KDE project * Copyright (C) 2012 KO GmbH. Contact: Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KRITA_SKETCH_DOCUMENTLISTMODEL_H #define KRITA_SKETCH_DOCUMENTLISTMODEL_H #include #include #include #include #include #include "krita_sketch_export.h" class KRITA_SKETCH_EXPORT DocumentListModel : public QAbstractListModel { Q_OBJECT Q_ENUMS(DocumentType) Q_PROPERTY(DocumentType filter READ filter WRITE setFilter) public: DocumentListModel(QObject *parent = 0); ~DocumentListModel(); + QHash roleNames() const; + enum CustomRoles { FileNameRole = Qt::UserRole + 1, FilePathRole, DocTypeRole, FileSizeRole, AuthorNameRole, AccessedTimeRole, ModifiedTimeRole, UUIDRole, }; enum DocumentType { UnknownType, TextDocumentType, PresentationType, SpreadsheetType, PDFDocumentType, }; struct DocumentInfo { bool operator==(const DocumentInfo &other) const { return filePath == other.filePath; } QString filePath; QString fileName; DocumentType docType; QString fileSize; QString authorName; QDateTime accessedTime; QDateTime modifiedTime; QString uuid; }; // reimp from QAbstractListModel int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; DocumentType filter(); static QString prettyTime(const QDateTime &theTime); static DocumentType typeForFile(const QString &file); public Q_SLOTS: void addDocument(const DocumentListModel::DocumentInfo &info); void setFilter(DocumentType newFilter); private: class Private; const QScopedPointer d; static QHash sm_extensions; }; Q_DECLARE_METATYPE(DocumentListModel::DocumentInfo); #endif // KRITA_SKETCH_DOCUMENTLISTMODEL_H diff --git a/libs/libqml/plugins/kritasketchplugin/models/ColorDepthModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/ColorDepthModel.cpp index e322344ecf..d9222bc5d7 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/ColorDepthModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/ColorDepthModel.cpp @@ -1,96 +1,100 @@ /* * This file is part of the KDE project * Copyright (C) 2014 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ColorDepthModel.h" #include class ColorDepthModel::Private { public: Private() { } QString colorModelId; QList colorDepths; }; ColorDepthModel::ColorDepthModel(QObject* parent) : QAbstractListModel(parent), d(new Private) { - QHash roleNames; - roleNames.insert(TextRole, "text"); - setRoleNames(roleNames); } ColorDepthModel::~ColorDepthModel() { delete d; } +QHash ColorDepthModel::roleNames() const +{ + QHash roleNames; + roleNames.insert(TextRole, "text"); + return roleNames; +} + int ColorDepthModel::rowCount(const QModelIndex& parent) const { Q_UNUSED(parent); return d->colorDepths.count(); } QVariant ColorDepthModel::data(const QModelIndex& index, int role) const { if(!index.isValid() || index.row() < 0 || index.row() >= d->colorDepths.count()) return QVariant(); if(role == TextRole) { return d->colorDepths.at(index.row()).name(); } return QVariant(); } QString ColorDepthModel::colorModelId() const { return d->colorModelId; } void ColorDepthModel::setColorModelId(const QString& id) { if(id != d->colorModelId) { d->colorModelId = id; if(d->colorDepths.count() > 0) { beginRemoveRows(QModelIndex(), 0, d->colorDepths.count() - 1); endRemoveRows(); } d->colorDepths = KoColorSpaceRegistry::instance()->colorDepthList(d->colorModelId, KoColorSpaceRegistry::OnlyUserVisible); if(d->colorDepths.count() > 0) { beginInsertRows(QModelIndex(), 0, d->colorDepths.count() - 1); endInsertRows(); } emit colorModelIdChanged(); } } QString ColorDepthModel::id(int index) { if(index < 0 || index >= d->colorDepths.count()) return QString(); return d->colorDepths.at(index).id(); } int ColorDepthModel::indexOf(const QString& id) { return d->colorDepths.indexOf(KoID(id)); } diff --git a/libs/libqml/plugins/kritasketchplugin/models/ColorDepthModel.h b/libs/libqml/plugins/kritasketchplugin/models/ColorDepthModel.h index 1397990d87..687d105dbd 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/ColorDepthModel.h +++ b/libs/libqml/plugins/kritasketchplugin/models/ColorDepthModel.h @@ -1,57 +1,57 @@ /* * This file is part of the KDE project * Copyright (C) 2014 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef COLORDEPTHMODEL_H #define COLORDEPTHMODEL_H #include class ColorDepthModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QString colorModelId READ colorModelId WRITE setColorModelId NOTIFY colorModelIdChanged) public: enum Roles { TextRole = Qt::UserRole + 1, }; explicit ColorDepthModel(QObject* parent = 0); virtual ~ColorDepthModel(); - + QHash roleNames() const; virtual QVariant data(const QModelIndex& index, int role) const; virtual int rowCount(const QModelIndex& parent) const; QString colorModelId() const; Q_INVOKABLE QString id(int index); Q_INVOKABLE int indexOf(const QString& id); public Q_SLOTS: void setColorModelId(const QString& id); Q_SIGNALS: void colorModelIdChanged(); private: class Private; Private * const d; }; #endif // COLORDEPTHMODEL_H diff --git a/libs/libqml/plugins/kritasketchplugin/models/ColorModelModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/ColorModelModel.cpp index 9efd3d2860..f8327e4f83 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/ColorModelModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/ColorModelModel.cpp @@ -1,76 +1,80 @@ /* * This file is part of the KDE project * Copyright (C) 2014 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ColorModelModel.h" #include class ColorModelModel::Private { public: Private() { } QList colorModels; }; ColorModelModel::ColorModelModel(QObject* parent) : QAbstractListModel(parent), d(new Private) { d->colorModels = KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::OnlyUserVisible); - - QHash roleNames; - roleNames.insert(TextRole, "text"); - setRoleNames(roleNames); } ColorModelModel::~ColorModelModel() { delete d; } +QHash ColorModelModel::roleNames() const +{ + QHash roleNames; + roleNames.insert(TextRole, "text"); + + return roleNames; +} + int ColorModelModel::rowCount(const QModelIndex& parent) const { Q_UNUSED(parent); return d->colorModels.count(); } QVariant ColorModelModel::data(const QModelIndex& index, int role) const { if(!index.isValid() || index.row() < 0 || index.row() >= d->colorModels.count()) { return QVariant(); } if( role == TextRole ) return d->colorModels.at(index.row()).name(); return QVariant(); } QString ColorModelModel::id(int index) { if(index < 0 || index >= d->colorModels.count()) return QString(); return d->colorModels.at(index).id(); } int ColorModelModel::indexOf(const QString& id) { return d->colorModels.indexOf(KoID(id)); } diff --git a/libs/libqml/plugins/kritasketchplugin/models/ColorModelModel.h b/libs/libqml/plugins/kritasketchplugin/models/ColorModelModel.h index 01768ebfb6..24e593e99b 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/ColorModelModel.h +++ b/libs/libqml/plugins/kritasketchplugin/models/ColorModelModel.h @@ -1,48 +1,48 @@ /* * This file is part of the KDE project * Copyright (C) 2014 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef COLORMODELMODEL_H #define COLORMODELMODEL_H #include class ColorModelModel : public QAbstractListModel { Q_OBJECT public: enum Roles { TextRole = Qt::UserRole + 1, }; ColorModelModel(QObject* parent = 0); ~ColorModelModel(); - + QHash roleNames() const; virtual int rowCount(const QModelIndex& parent) const; virtual QVariant data(const QModelIndex& index, int role) const; Q_INVOKABLE QString id(int index); Q_INVOKABLE int indexOf(const QString& id); private: class Private; Private * const d; }; #endif // COLORMODELMODEL_H diff --git a/libs/libqml/plugins/kritasketchplugin/models/ColorProfileModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/ColorProfileModel.cpp index 457ccaea99..7e87a5e530 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/ColorProfileModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/ColorProfileModel.cpp @@ -1,130 +1,135 @@ /* * This file is part of the KDE project * Copyright (C) 2014 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ColorProfileModel.h" #include #include class ColorProfileModel::Private { public: Private(ColorProfileModel* qq) : q(qq), defaultProfile(-1) { } void updateProfiles(); ColorProfileModel* q; QString colorModelId; QString colorDepthId; QString colorSpaceId; int defaultProfile; QList colorProfiles; }; ColorProfileModel::ColorProfileModel(QObject* parent) : QAbstractListModel(parent), d(new Private(this)) { - QHash roleNames; - roleNames.insert(TextRole, "text"); - setRoleNames(roleNames); } ColorProfileModel::~ColorProfileModel() { delete d; } +QHash ColorProfileModel::roleNames() const +{ + QHash roleNames; + roleNames.insert(TextRole, "text"); + + return roleNames; +} + int ColorProfileModel::rowCount(const QModelIndex& parent) const { Q_UNUSED(parent); return d->colorProfiles.count(); } QVariant ColorProfileModel::data(const QModelIndex& index, int role) const { if(!index.isValid() || index.row() < 0 || index.row() >= d->colorProfiles.count()) return QVariant(); if(role == TextRole) { return d->colorProfiles.at(index.row())->name(); } return QVariant(); } QString ColorProfileModel::colorModelId() const { return d->colorModelId; } void ColorProfileModel::setColorModelId(const QString& id) { if(id != d->colorModelId) { d->colorModelId = id; d->updateProfiles(); emit colorModelIdChanged(); } } QString ColorProfileModel::colorDepthId() const { return d->colorDepthId; } void ColorProfileModel::setColorDepthId(const QString& id) { if(id != d->colorDepthId) { d->colorDepthId = id; d->updateProfiles(); emit colorDepthIdChanged(); } } int ColorProfileModel::defaultProfile() const { return d->defaultProfile; } QString ColorProfileModel::id(int index) { return d->colorProfiles.at(index)->name(); } void ColorProfileModel::Private::updateProfiles() { if(colorDepthId.isEmpty() || colorModelId.isEmpty()) return; q->beginResetModel(); colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(colorModelId, colorDepthId); colorProfiles = KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId); QString profile = KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId); for(int i = 0; i < colorProfiles.count(); ++i) { if(colorProfiles.at(i)->name() == profile) { defaultProfile = i; break; } } q->endResetModel(); emit q->defaultProfileChanged(); } diff --git a/libs/libqml/plugins/kritasketchplugin/models/ColorProfileModel.h b/libs/libqml/plugins/kritasketchplugin/models/ColorProfileModel.h index eec6d1a95f..49205dadb1 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/ColorProfileModel.h +++ b/libs/libqml/plugins/kritasketchplugin/models/ColorProfileModel.h @@ -1,64 +1,64 @@ /* * This file is part of the KDE project * Copyright (C) 2014 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef COLORPROFILEMODEL_H #define COLORPROFILEMODEL_H #include class ColorProfileModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QString colorModelId READ colorModelId WRITE setColorModelId NOTIFY colorModelIdChanged) Q_PROPERTY(QString colorDepthId READ colorDepthId WRITE setColorDepthId NOTIFY colorDepthIdChanged) Q_PROPERTY(int defaultProfile READ defaultProfile NOTIFY defaultProfileChanged) public: enum Roles { TextRole = Qt::UserRole + 1, }; - + explicit ColorProfileModel(QObject* parent = 0); virtual ~ColorProfileModel(); - + QHash roleNames() const; virtual QVariant data(const QModelIndex& index, int role) const; virtual int rowCount(const QModelIndex& parent) const; QString colorModelId() const; QString colorDepthId() const; int defaultProfile() const; Q_INVOKABLE QString id(int index); public Q_SLOTS: void setColorModelId(const QString& id); void setColorDepthId(const QString& id); Q_SIGNALS: void colorModelIdChanged(); void colorDepthIdChanged(); void defaultProfileChanged(); private: class Private; Private * const d; }; #endif // COLORDEPTHMODEL_H diff --git a/libs/libqml/plugins/kritasketchplugin/models/CompositeOpModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/CompositeOpModel.cpp index 7c5cf2fb11..9ad9f70f2e 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/CompositeOpModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/CompositeOpModel.cpp @@ -1,478 +1,482 @@ /* Copyright (C) 2012 Dan Leinir Turthra Jensen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "CompositeOpModel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class CompositeOpModel::Private { public: Private(CompositeOpModel* qq) : q(qq) , model(KisCompositeOpListModel::sharedInstance()) , view(0) , eraserMode(0) , opacity(0) , opacityEnabled(false) , flow(0) , flowEnabled(false) , size(0) , sizeEnabled(false) , presetsEnabled(true) {}; CompositeOpModel* q; KisCompositeOpListModel* model; KisViewManager* view; QString currentCompositeOpID; QString prevCompositeOpID; bool eraserMode; QMap settingsWidgets; qreal opacity; bool opacityEnabled; qreal flow; bool flowEnabled; qreal size; bool sizeEnabled; bool presetsEnabled; KisPaintOpPresetSP currentPreset; void updateCompositeOp(QString compositeOpID) { if (!view) return; KisNodeSP node = view->resourceProvider()->currentNode(); if (node && node->paintDevice()) { if (!node->paintDevice()->colorSpace()->hasCompositeOp(compositeOpID)) compositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id(); if (compositeOpID != currentCompositeOpID) { q->setEraserMode(compositeOpID == COMPOSITE_ERASE); currentPreset->settings()->setProperty("CompositeOp", compositeOpID); //m_optionWidget->setConfiguration(m_activePreset->settings().data()); view->resourceProvider()->setCurrentCompositeOp(compositeOpID); prevCompositeOpID = currentCompositeOpID; currentCompositeOpID = compositeOpID; } } emit q->currentCompositeOpIDChanged(); } void ofsChanged() { if (presetsEnabled && !currentPreset.isNull() && !currentPreset->settings().isNull()) { // IMPORTANT: set the PaintOp size before setting the other properties // it wont work the other way qreal sizeDiff = size - currentPreset->settings()->paintOpSize(); //currentPreset->settings()->changePaintOpSize(sizeDiff, 0); if (currentPreset->settings()->hasProperty("OpacityValue")) currentPreset->settings()->setProperty("OpacityValue", opacity); if (currentPreset->settings()->hasProperty("FlowValue")) currentPreset->settings()->setProperty("FlowValue", flow); //m_optionWidget->setConfiguration(d->currentPreset->settings().data()); } if (view) { view->resourceProvider()->setOpacity(opacity); } } }; CompositeOpModel::CompositeOpModel(QObject* parent) : QAbstractListModel(parent) , d(new Private(this)) { connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), this, SLOT(slotToolChanged(KoCanvasController*,int))); - QHash roles; - roles[TextRole] = "text"; - roles[IsCategoryRole] = "isCategory"; - setRoleNames(roles); } CompositeOpModel::~CompositeOpModel() { delete d; } +QHash CompositeOpModel::roleNames() const +{ + QHash roles; + roles[TextRole] = "text"; + roles[IsCategoryRole] = "isCategory"; + return roles; +} + QVariant CompositeOpModel::data(const QModelIndex& index, int role) const { QVariant data; if (index.isValid()) { QModelIndex otherIndex = d->model->index(index.row(), index.column(), QModelIndex()); switch(role) { case TextRole: data = d->model->data(otherIndex, Qt::DisplayRole); break; case IsCategoryRole: data = d->model->data(otherIndex, __CategorizedListModelBase::IsHeaderRole); break; default: break; } } return data; } int CompositeOpModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; return d->model->rowCount(QModelIndex()); } void CompositeOpModel::activateItem(int index) { if (index > -1 && index < d->model->rowCount(QModelIndex())) { KoID compositeOp; if (d->model->entryAt(compositeOp, d->model->index(index))) d->updateCompositeOp(compositeOp.id()); } } QObject* CompositeOpModel::view() const { return d->view; } void CompositeOpModel::setView(QObject* newView) { if (d->view) { d->view->canvasBase()->disconnect(this); d->view->canvasBase()->globalInputManager()->disconnect(this); d->view->nodeManager()->disconnect(this); } d->view = qobject_cast( newView ); if (d->view) { if (d->view->canvasBase() && d->view->canvasBase()->resourceManager()) { connect(d->view->canvasBase()->resourceManager(), SIGNAL(canvasResourceChanged(int, const QVariant&)), this, SLOT(resourceChanged(int, const QVariant&))); } if (d->view->nodeManager()) { connect(d->view->nodeManager(), SIGNAL(sigLayerActivated(KisLayerSP)), this, SLOT(currentNodeChanged(KisLayerSP))); } slotToolChanged(0, 0); } emit viewChanged(); } bool CompositeOpModel::eraserMode() const { return d->eraserMode; } void CompositeOpModel::setEraserMode(bool newEraserMode) { if (d->eraserMode != newEraserMode) { d->eraserMode = newEraserMode; if (d->eraserMode) d->updateCompositeOp(COMPOSITE_ERASE); else d->updateCompositeOp(d->prevCompositeOpID); emit eraserModeChanged(); } } qreal CompositeOpModel::flow() const { return d->flow; } void CompositeOpModel::setFlow(qreal newFlow) { if (d->flow != newFlow) { d->flow = newFlow; d->ofsChanged(); emit flowChanged(); } } bool CompositeOpModel::flowEnabled() const { return d->flowEnabled; } void CompositeOpModel::setFlowEnabled(bool newFlowEnabled) { d->flowEnabled = newFlowEnabled; emit flowEnabledChanged(); } qreal CompositeOpModel::opacity() const { return d->opacity; } void CompositeOpModel::setOpacity(qreal newOpacity) { if (d->opacity != newOpacity) { d->opacity = newOpacity; d->ofsChanged(); emit opacityChanged(); } } bool CompositeOpModel::opacityEnabled() const { return d->opacityEnabled; } void CompositeOpModel::setOpacityEnabled(bool newOpacityEnabled) { d->opacityEnabled = newOpacityEnabled; emit opacityEnabledChanged(); } qreal CompositeOpModel::size() const { return d->size; } void CompositeOpModel::setSize(qreal newSize) { if (d->size != newSize) { d->size = newSize; d->ofsChanged(); emit sizeChanged(); } } bool CompositeOpModel::sizeEnabled() const { return d->sizeEnabled; } void CompositeOpModel::setSizeEnabled(bool newSizeEnabled) { d->sizeEnabled = newSizeEnabled; emit sizeEnabledChanged(); } void CompositeOpModel::changePaintopValue(QString propertyName, QVariant value) { if (propertyName == "size" && value.toReal() != d->size) setSize(value.toReal()); else if (propertyName == "opacity" && value.toReal() != d->opacity) setOpacity(value.toReal()); else if (propertyName == "flow" && value.toReal() != d->flow) setFlow(value.toReal()); } bool CompositeOpModel::mirrorHorizontally() const { if (d->view) return d->view->resourceProvider()->mirrorHorizontal(); return false; } void CompositeOpModel::setMirrorHorizontally(bool newMirrorHorizontally) { if (d->view && d->view->resourceProvider()->mirrorHorizontal() != newMirrorHorizontally) { d->view->resourceProvider()->setMirrorHorizontal(newMirrorHorizontally); emit mirrorHorizontallyChanged(); } } bool CompositeOpModel::mirrorVertically() const { if (d->view) return d->view->resourceProvider()->mirrorVertical(); return false; } void CompositeOpModel::setMirrorVertically(bool newMirrorVertically) { if (d->view && d->view->resourceProvider()->mirrorVertical() != newMirrorVertically) { d->view->resourceProvider()->setMirrorVertical(newMirrorVertically); emit mirrorVerticallyChanged(); } } void CompositeOpModel::slotToolChanged(KoCanvasController* canvas, int toolId) { Q_UNUSED(canvas); Q_UNUSED(toolId); if (!d->view) return; if (!d->view->canvasBase()) return; QString id = KoToolManager::instance()->activeToolId(); KisTool* tool = dynamic_cast(KoToolManager::instance()->toolById(d->view->canvasBase(), id)); if (tool) { int flags = tool->flags(); if (flags & KisTool::FLAG_USES_CUSTOM_COMPOSITEOP) { //setWidgetState(ENABLE_COMPOSITEOP|ENABLE_OPACITY); d->opacityEnabled = true; } else { //setWidgetState(DISABLE_COMPOSITEOP|DISABLE_OPACITY); d->opacityEnabled = false; } if (flags & KisTool::FLAG_USES_CUSTOM_PRESET) { d->flowEnabled = true; d->sizeEnabled = true; d->presetsEnabled = true; } else { d->flowEnabled = false; d->sizeEnabled = false; d->presetsEnabled = false; } } else { d->opacityEnabled = false; d->flowEnabled = false; d->sizeEnabled = false; } emit opacityEnabledChanged(); emit flowEnabledChanged(); emit sizeEnabledChanged(); } void CompositeOpModel::resourceChanged(int key, const QVariant& /*v*/) { if (d->view && d->view->canvasBase() && d->view->canvasBase()->resourceManager() && d->view->resourceProvider()) { if (key == KisCanvasResourceProvider::MirrorHorizontal) { emit mirrorHorizontallyChanged(); return; } else if(key == KisCanvasResourceProvider::MirrorVertical) { emit mirrorVerticallyChanged(); return; } KisPaintOpPresetSP preset = d->view->canvasBase()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); if (preset && d->currentPreset.data() != preset.data()) { d->currentPreset = preset; if (!d->settingsWidgets.contains(preset.data())) { d->settingsWidgets[preset.data()] = KisPaintOpRegistry::instance()->get(preset->paintOp().id())->createConfigWidget(0); d->settingsWidgets[preset.data()]->setImage(d->view->image()); d->settingsWidgets[preset.data()]->setConfiguration(preset->settings()); } if (d->settingsWidgets[preset.data()]) { preset->settings()->setOptionsWidget(d->settingsWidgets[preset.data()]); } d->size = preset->settings()->paintOpSize(); emit sizeChanged(); if (preset->settings()->hasProperty("OpacityValue")) { d->opacityEnabled = true; d->opacity = preset->settings()->getProperty("OpacityValue").toReal(); } else { d->opacityEnabled = false; d->opacity = 1; } d->view->resourceProvider()->setOpacity(d->opacity); emit opacityChanged(); emit opacityEnabledChanged(); if (preset->settings()->hasProperty("FlowValue")) { d->flowEnabled = true; d->flow = preset->settings()->getProperty("FlowValue").toReal(); } else { d->flowEnabled = false; d->flow = 1; } emit flowChanged(); emit flowEnabledChanged(); QString compositeOp = preset->settings()->getString("CompositeOp"); // This is a little odd, but the logic here is that the opposite of an eraser is a normal composite op (so we just select over, aka normal) // This means that you can switch your eraser over to being a painting tool by turning off the eraser again. if (compositeOp == COMPOSITE_ERASE) { d->currentCompositeOpID = COMPOSITE_OVER; d->eraserMode = true; } else { d->eraserMode = false; } emit eraserModeChanged(); d->updateCompositeOp(compositeOp); } } } void CompositeOpModel::currentNodeChanged(KisLayerSP newNode) { Q_UNUSED(newNode); if (d->eraserMode) { d->eraserMode = false; d->updateCompositeOp(d->prevCompositeOpID); emit eraserModeChanged(); } } int CompositeOpModel::indexOf(QString compositeOpId) { return d->model->indexOf(KoID(compositeOpId)).row(); } QString CompositeOpModel::currentCompositeOpID() const { return d->currentCompositeOpID; } diff --git a/libs/libqml/plugins/kritasketchplugin/models/CompositeOpModel.h b/libs/libqml/plugins/kritasketchplugin/models/CompositeOpModel.h index 546dc0e16b..57cdba623e 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/CompositeOpModel.h +++ b/libs/libqml/plugins/kritasketchplugin/models/CompositeOpModel.h @@ -1,114 +1,115 @@ /* Copyright (C) 2012 Dan Leinir Turthra Jensen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef COMPOSITEOPMODEL_H #define COMPOSITEOPMODEL_H #include #include #include class KoCanvasController; class CompositeOpModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QObject* view READ view WRITE setView NOTIFY viewChanged); Q_PROPERTY(bool eraserMode READ eraserMode WRITE setEraserMode NOTIFY eraserModeChanged); Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged); Q_PROPERTY(bool opacityEnabled READ opacityEnabled WRITE setOpacityEnabled NOTIFY opacityEnabledChanged); Q_PROPERTY(qreal flow READ flow WRITE setFlow NOTIFY flowChanged); Q_PROPERTY(bool flowEnabled READ flowEnabled WRITE setFlowEnabled NOTIFY flowEnabledChanged); Q_PROPERTY(qreal size READ size WRITE setSize NOTIFY sizeChanged); Q_PROPERTY(bool sizeEnabled READ sizeEnabled WRITE setSizeEnabled NOTIFY sizeEnabledChanged); Q_PROPERTY(bool mirrorHorizontally READ mirrorHorizontally WRITE setMirrorHorizontally NOTIFY mirrorHorizontallyChanged); Q_PROPERTY(bool mirrorVertically READ mirrorVertically WRITE setMirrorVertically NOTIFY mirrorVerticallyChanged); Q_PROPERTY(QString currentCompositeOpID READ currentCompositeOpID NOTIFY currentCompositeOpIDChanged); public: enum CompositeOpModelRoles { TextRole = Qt::UserRole + 1, IsCategoryRole }; explicit CompositeOpModel(QObject* parent = 0); virtual ~CompositeOpModel(); + QHash roleNames() const; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; Q_INVOKABLE void activateItem(int index); QObject* view() const; void setView(QObject* newView); bool eraserMode() const; void setEraserMode(bool newEraserMode); qreal opacity() const; void setOpacity(qreal newOpacity); bool opacityEnabled() const; void setOpacityEnabled(bool newOpacityEnabled); qreal flow() const; void setFlow(qreal newFlow); bool flowEnabled() const; void setFlowEnabled(bool newFlowEnabled); qreal size() const; void setSize(qreal newSize); bool sizeEnabled() const; void setSizeEnabled(bool newSizeEnabled); bool mirrorHorizontally() const; void setMirrorHorizontally(bool newMirrorHorizontally); bool mirrorVertically() const; void setMirrorVertically(bool newMirrorVertically); QString currentCompositeOpID() const; Q_INVOKABLE void changePaintopValue(QString propertyName, QVariant value); Q_INVOKABLE int indexOf(QString compositeOpId); Q_SIGNALS: void viewChanged(); void eraserModeChanged(); void opacityChanged(); void opacityEnabledChanged(); void flowChanged(); void flowEnabledChanged(); void sizeChanged(); void sizeEnabledChanged(); void mirrorHorizontallyChanged(); void mirrorVerticallyChanged(); void currentCompositeOpIDChanged(); private Q_SLOTS: void slotToolChanged(KoCanvasController* canvas, int toolId); void resourceChanged(int key, const QVariant& v); void currentNodeChanged(KisLayerSP newNode); private: class Private; Private* d; }; #endif // COMPOSITEOPMODEL_H diff --git a/libs/libqml/plugins/kritasketchplugin/models/FileSystemModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/FileSystemModel.cpp index d14149fab4..a13980fdd5 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/FileSystemModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/FileSystemModel.cpp @@ -1,162 +1,162 @@ /* This file is part of the KDE project * * Copyright (c) 2012 Arjen Hiemstra * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; 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 "FileSystemModel.h" #include #include #include #include -#include +#include #include class FileSystemModel::Private { public: QDir dir; QFileInfoList list; static const QString drivesPath; }; const QString FileSystemModel::Private::drivesPath("special://drives"); FileSystemModel::FileSystemModel(QObject* parent) : QAbstractListModel(parent), d(new Private) { d->dir.setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); d->dir.setSorting(QDir::DirsFirst | QDir::Name | QDir::IgnoreCase); } FileSystemModel::~FileSystemModel() { delete d; } QVariant FileSystemModel::data(const QModelIndex& index, int role) const { if (index.isValid()) { const QFileInfo &fileInfo = d->list.at(index.row()); switch(role) { case FileNameRole: return fileInfo.fileName(); break; case FilePathRole: return fileInfo.absoluteFilePath(); break; case FileIconRole: return fileInfo.isDir() ? "inode/directory" : QString("image://recentimage/%1").arg(fileInfo.absoluteFilePath()); break; case FileDateRole: return fileInfo.lastModified().toString(Qt::SystemLocaleShortDate); } } return QVariant(); } int FileSystemModel::rowCount(const QModelIndex&) const { return d->list.count(); } void FileSystemModel::classBegin() { } void FileSystemModel::componentComplete() { - setPath(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + setPath(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); } QString FileSystemModel::path() { QString path = d->dir.absolutePath(); if (path.isEmpty()) { return Private::drivesPath; } else { return d->dir.absolutePath(); } } void FileSystemModel::setPath(const QString& path) { if (path != d->dir.path()) { if (d->list.count() > 0) { beginRemoveRows(QModelIndex(), 0, d->list.count() - 1); endRemoveRows(); } if (path != Private::drivesPath) { d->dir.setPath(path); d->dir.refresh(); d->list = d->dir.entryInfoList(); if (d->list.count() > 0) { beginInsertRows(QModelIndex(), 0, d->list.count() - 1); endInsertRows(); } } else { d->dir.setPath(""); d->dir.refresh(); d->list = QDir::drives(); beginInsertRows(QModelIndex(), 0, d->list.count() - 1); endInsertRows(); } emit pathChanged(); } } QString FileSystemModel::parentFolder() { if (path() != Private::drivesPath) { if (QRegExp("^[A-Z]{1,3}:/$").exactMatch(path())) { return Private::drivesPath; } else { QDir root(path()); root.cdUp(); return root.path(); } } else { return Private::drivesPath; } } QString FileSystemModel::filter() { return d->dir.nameFilters().join(" "); } void FileSystemModel::setFilter(const QString& filter) { d->dir.setNameFilters(filter.split(" ")); } QHash FileSystemModel::roleNames() const { QHash roles; roles.insert(FileNameRole, "fileName"); roles.insert(FilePathRole, "path"); roles.insert(FileIconRole, "icon"); roles.insert(FileDateRole, "date"); return roles; } diff --git a/libs/libqml/plugins/kritasketchplugin/models/FiltersCategoryModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/FiltersCategoryModel.cpp index 486f141033..cd425b7122 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/FiltersCategoryModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/FiltersCategoryModel.cpp @@ -1,309 +1,316 @@ /* This file is part of the KDE project * Copyright (C) 2012 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "FiltersCategoryModel.h" #include "FiltersModel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include + bool categoryLessThan(const FiltersModel* s1, const FiltersModel* s2) { return s1->categoryName.toLower() < s2->categoryName.toLower(); } class FiltersCategoryModel::Private { public: Private(FiltersCategoryModel* qq) : q(qq) , currentCategory(-1) , view(0) , previewEnabled(false) , previewFilterID(-1) , previewTimer(new QTimer()) { previewTimer->setInterval(150); previewTimer->setSingleShot(true); connect(previewTimer, SIGNAL(timeout()), q, SLOT(updatePreview())); } FiltersCategoryModel* q; int currentCategory; KisViewManager* view; QList categories; FiltersModel* categoryByName(const QString& name) { FiltersModel* category = 0; for(int i = 0; i < categories.count(); ++i) { if (categories.at(i)->categoryId == name) { category = categories[i]; break; } } return category; } void refreshContents() { q->beginResetModel(); qDeleteAll(categories); categories.clear(); QList filters = KisFilterRegistry::instance()->values(); QList tmpCategoryIDs; Q_FOREACH (const KisFilterSP filter, filters) { Q_ASSERT(filter); FiltersModel* cat = 0; if (!tmpCategoryIDs.contains(filter->menuCategory().id())) { cat = new FiltersModel(q); cat->categoryId = filter->menuCategory().id(); cat->categoryName = filter->menuCategory().name(); cat->setView(view); categories << cat; tmpCategoryIDs << filter->menuCategory().id(); connect(cat, SIGNAL(configurationChanged(int)), q, SLOT(filterConfigurationChanged(int))); connect(cat, SIGNAL(filterActivated(int)), q, SLOT(filterActivated(int))); } else cat = categoryByName(filter->menuCategory().id()); cat->addFilter(filter); qApp->processEvents(); } - qSort(categories.begin(), categories.end(), categoryLessThan); + std::sort(categories.begin(), categories.end(), categoryLessThan); q->endResetModel(); } bool previewEnabled; KisFilterMaskSP mask; KisNodeSP node; int previewFilterID; KisFilterConfigurationSP newConfig; QTimer* previewTimer; }; FiltersCategoryModel::FiltersCategoryModel(QObject* parent) : QAbstractListModel(parent) , d(new Private(this)) { - QHash roles; - roles[TextRole] = "text"; - setRoleNames(roles); } FiltersCategoryModel::~FiltersCategoryModel() { delete d; } +QHash FiltersCategoryModel::roleNames() const +{ + QHash roles; + roles[TextRole] = "text"; + + return roles; +} + QVariant FiltersCategoryModel::data(const QModelIndex& index, int role) const { QVariant data; if (index.isValid()) { switch(role) { case TextRole: data = d->categories[index.row()]->categoryName; break; default: break; } } return data; } int FiltersCategoryModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; return d->categories.count(); } QObject* FiltersCategoryModel::filterModel() const { if (d->currentCategory == -1) return 0; return d->categories[d->currentCategory]; } void FiltersCategoryModel::activateItem(int index) { if (index > -1 && index < d->categories.count()) { d->currentCategory = index; emit filterModelChanged(); } } QObject* FiltersCategoryModel::view() const { return d->view; } void FiltersCategoryModel::setView(QObject* newView) { if (d->view) { setPreviewEnabled(false); d->view->nodeManager()->disconnect(this); d->view->selectionManager()->disconnect(this); } d->view = qobject_cast( newView ); if (d->view) { d->refreshContents(); connect(d->view->nodeManager(), SIGNAL(sigLayerActivated(KisLayerSP)), this, SLOT(activeLayerChanged(KisLayerSP))); connect(d->view->selectionManager(), SIGNAL(currentSelectionChanged()), this, SLOT(activeSelectionChanged())); } emit viewChanged(); } void FiltersCategoryModel::activeLayerChanged(KisLayerSP layer) { Q_UNUSED(layer); setPreviewEnabled(false); } void FiltersCategoryModel::activeSelectionChanged() { setPreviewEnabled(false); } void FiltersCategoryModel::filterActivated(int index) { Q_UNUSED(index); setPreviewEnabled(false); } void FiltersCategoryModel::filterConfigurationChanged(int index, FiltersModel* model) { d->previewFilterID = index; if (d->previewEnabled && index > -1) { if (!model) { model = qobject_cast(sender()); } if (!model) { return; } KisFilterConfigurationSP config; KisFilter* filter = model->filter(index); if (filter->showConfigurationWidget() && filter->id() != QLatin1String("colortransfer")) { KisConfigWidget* wdg = filter->createConfigurationWidget(0, d->view->activeNode()->original()); wdg->deleteLater(); config = KisFilterConfigurationSP(KisFilterRegistry::instance()->cloneConfiguration(dynamic_cast(wdg->configuration().data()))); } else { config = KisFilterConfigurationSP(KisFilterRegistry::instance()->cloneConfiguration(filter->defaultConfiguration())); } QObject* configuration = d->categories[d->currentCategory]->configuration(index); Q_FOREACH (const QByteArray& propName, configuration->dynamicPropertyNames()) { config->setProperty(QString(propName), configuration->property(propName)); } config->setCurve(qobject_cast(configuration)->curve()); config->setCurves(qobject_cast(configuration)->curves()); configuration->deleteLater(); d->newConfig = config; d->previewTimer->start(); } } void FiltersCategoryModel::updatePreview() { d->view->filterManager()->apply(d->newConfig); } bool FiltersCategoryModel::previewEnabled() const { return d->previewEnabled; } void FiltersCategoryModel::filterSelected(int index) { if (d->previewEnabled) filterConfigurationChanged(index, d->categories[d->currentCategory]); } void FiltersCategoryModel::setPreviewEnabled(bool enabled) { if (d->previewEnabled != enabled) { d->previewEnabled = enabled; emit previewEnabledChanged(); if (enabled) filterConfigurationChanged(d->previewFilterID, d->categories[d->currentCategory]); else d->view->filterManager()->cancel(); } } int FiltersCategoryModel::categoryIndexForConfig(QObject* config) { PropertyContainer* configuration = qobject_cast(config); if (!configuration) return -1; FiltersModel* model = 0; int i = 0; while(model == 0 && i < d->categories.count()) { FiltersModel* cat = d->categories.at(i); // i know there's no check here - but a category is not created unless there // is something to put in it for(int j = 0; j < cat->rowCount(); ++j) { if (cat->filter(j)->id() == configuration->name()) return i; } ++i; } return -1; } int FiltersCategoryModel::filterIndexForConfig(int categoryIndex, QObject* filterConfig) { PropertyContainer* configuration = qobject_cast(filterConfig); if (!configuration) return -1; if (categoryIndex < 0 || categoryIndex > d->categories.count() - 1) return -1; FiltersModel* cat = d->categories.at(categoryIndex); // i know there's no check here - but a category is not created unless there // is something to put in it for(int j = 0; j < cat->rowCount(); ++j) { if (cat->filter(j)->id() == configuration->name()) return j; } return -1; } diff --git a/libs/libqml/plugins/kritasketchplugin/models/FiltersCategoryModel.h b/libs/libqml/plugins/kritasketchplugin/models/FiltersCategoryModel.h index 157d4d6af3..4be4ad842c 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/FiltersCategoryModel.h +++ b/libs/libqml/plugins/kritasketchplugin/models/FiltersCategoryModel.h @@ -1,72 +1,73 @@ /* This file is part of the KDE project * Copyright (C) 2012 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FILTERSCATEGORYMODEL_H #define FILTERSCATEGORYMODEL_H #include #include class FiltersModel; class FiltersCategoryModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QObject* view READ view WRITE setView NOTIFY viewChanged) Q_PROPERTY(QObject* filterModel READ filterModel NOTIFY filterModelChanged); Q_PROPERTY(bool previewEnabled READ previewEnabled WRITE setPreviewEnabled NOTIFY previewEnabledChanged); public: enum FiltersCategoryModelRoles { TextRole = Qt::UserRole + 1 }; explicit FiltersCategoryModel(QObject* parent = 0); virtual ~FiltersCategoryModel(); + QHash roleNames() const; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; QObject* filterModel() const; Q_INVOKABLE void activateItem(int index); QObject* view() const; void setView(QObject* newView); bool previewEnabled() const; void setPreviewEnabled(bool enabled); Q_INVOKABLE void filterSelected(int index); Q_INVOKABLE int categoryIndexForConfig(QObject* config); Q_INVOKABLE int filterIndexForConfig(int categoryIndex, QObject* filterConfig); Q_SIGNALS: void viewChanged(); void filterModelChanged(); void previewEnabledChanged(); private Q_SLOTS: void activeLayerChanged(KisLayerSP layer); void activeSelectionChanged(); void filterConfigurationChanged(int index, FiltersModel* model = 0); void filterActivated(int index); void updatePreview(); private: class Private; Private* d; }; #endif // FILTERSCATEGORYMODEL_H diff --git a/libs/libqml/plugins/kritasketchplugin/models/FiltersModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/FiltersModel.cpp index fa246874d4..42c685cd91 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/FiltersModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/FiltersModel.cpp @@ -1,202 +1,207 @@ -/* This file is part of the KDE project +/* This file is part of the KDE project * Copyright (C) 2012 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "FiltersModel.h" #include #include #include #include #include #include class FiltersModel::Private { public: Private() : view(0) {}; KisViewManager* view; QList filters; QList configurations; }; FiltersModel::FiltersModel(QObject* parent) : QAbstractListModel(parent) , d(new Private) { - QHash roles; - roles[TextRole] = "text"; - setRoleNames(roles); } FiltersModel::~FiltersModel() { delete d; } +QHash FiltersModel::roleNames() const +{ + QHash roles; + roles[TextRole] = "text"; + + return roles; +} + QVariant FiltersModel::data(const QModelIndex& index, int role) const { QVariant data; if (index.isValid()) { switch(role) { case TextRole: data = d->filters[index.row()]->name(); break; default: break; } } return data; } int FiltersModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; return d->filters.count(); } bool FiltersModel::filterRequiresConfiguration(int index) { if (index > -1 && index < d->filters.count()) { return d->filters[index]->showConfigurationWidget(); } return false; } QString FiltersModel::filterID(int index) { if (index > -1 && index < d->filters.count()) { return d->filters[index]->id(); } return QLatin1String(""); } void FiltersModel::activateFilter(int index) { if (index > -1 && index < d->filters.count()) { if (d->configurations[index]) { d->view->filterManager()->apply(d->configurations[index]); } else { d->view->filterManager()->apply(KisFilterConfigurationSP(d->filters[index]->defaultConfiguration())); } d->view->filterManager()->finish(); emit filterActivated(index); } } KisFilter* FiltersModel::filter(int index) { if (index > -1 && index < d->filters.count()) { return d->filters[index].data(); } return 0; } void FiltersModel::addFilter(KisFilterSP filter) { if (!d->view || !d->view->activeNode()) return; if (!filter.isNull()) { int newRow = d->filters.count(); beginInsertRows(QModelIndex(), newRow, newRow); d->filters << filter; // We're not asking for the config widget config for color transfer // The reason for this is that the completion widget is VERY slow to destruct on // Windows. This can be removed once that bug has been alleviated at some later // point in time, but for now it has no side effects, as this filter's default // config is fine anyway. if (filter->showConfigurationWidget() && filter->id() != QLatin1String("colortransfer")) { KisConfigWidget* wdg = filter->createConfigurationWidget(0, d->view->activeNode()->original()); wdg->deleteLater(); d->configurations << KisFilterConfigurationSP(dynamic_cast(wdg->configuration().data())); } else { d->configurations << KisFilterConfigurationSP(filter->defaultConfiguration()); } endInsertRows(); } } QObject* FiltersModel::view() const { return d->view; } void FiltersModel::setView(QObject* newView) { d->view = qobject_cast( newView ); emit viewChanged(); } QObject* FiltersModel::configuration(int index) { // If index is out of bounds, return /something/ for the object work on at least. if (index < 0 || index > d->configurations.count() - 1) return new PropertyContainer("", this); PropertyContainer* config = new PropertyContainer(d->filters[index]->id(), this); if (!d->configurations[index]) { // if we have a config widget to show, reinitialise the configuration, just in case if(d->filters[index]->showConfigurationWidget() && d->filters[index]->id() != QLatin1String("colortransfer")) { KisConfigWidget* wdg = d->filters[index]->createConfigurationWidget(0, d->view->activeNode()->original()); wdg->deleteLater(); d->configurations[index] = KisFilterConfigurationSP(dynamic_cast(wdg->configuration().data())); } // If we've not got one already, assign the default configuration to the cache else { d->configurations[index] = KisFilterConfigurationSP(d->filters[index]->defaultConfiguration()); } } QMap props = d->configurations[index]->getProperties(); QMap::const_iterator i; for(i = props.constBegin(); i != props.constEnd(); ++i) { config->setProperty(i.key().toLatin1(), i.value()); } config->setCurve(d->configurations[index]->curve()); config->setCurves(d->configurations[index]->curves()); return config; } void FiltersModel::setConfiguration(int index, QObject* configuration) { if (qobject_cast< PropertyContainer* >(configuration) && index > -1 && index < d->configurations.count() - 1) { KisFilterConfigurationSP config = d->configurations[index]; Q_FOREACH (const QByteArray& propName, configuration->dynamicPropertyNames()) { config->setProperty(QString(propName), configuration->property(propName)); } config->setCurve(qobject_cast< PropertyContainer* >(configuration)->curve()); config->setCurves(qobject_cast< PropertyContainer* >(configuration)->curves()); d->configurations[index] = config; emit configurationChanged(index); } } diff --git a/libs/libqml/plugins/kritasketchplugin/models/FiltersModel.h b/libs/libqml/plugins/kritasketchplugin/models/FiltersModel.h index 381016385e..d4033ee63c 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/FiltersModel.h +++ b/libs/libqml/plugins/kritasketchplugin/models/FiltersModel.h @@ -1,63 +1,64 @@ /* This file is part of the KDE project * Copyright (C) 2012 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FILTERSMODEL_H #define FILTERSMODEL_H #include #include class FiltersModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QObject* view READ view WRITE setView NOTIFY viewChanged) public: enum FiltersModelRoles { TextRole = Qt::UserRole + 1 }; explicit FiltersModel(QObject* parent = 0); virtual ~FiltersModel(); + QHash roleNames() const; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; Q_INVOKABLE bool filterRequiresConfiguration(int index); Q_INVOKABLE QString filterID(int index); Q_INVOKABLE void activateFilter(int index); KisFilter* filter(int index); QString categoryId; QString categoryName; void addFilter(KisFilterSP filter); QObject* view() const; void setView(QObject* newView); Q_INVOKABLE QObject* configuration(int index); Q_INVOKABLE void setConfiguration(int index, QObject* configuration); Q_SIGNALS: void viewChanged(); void configurationChanged(int index); void filterActivated(int index); private: class Private; Private* d; }; #endif // FILTERSMODEL_H diff --git a/libs/libqml/plugins/kritasketchplugin/models/KeyboardModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/KeyboardModel.cpp index 222f91a687..cff4091cf0 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/KeyboardModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/KeyboardModel.cpp @@ -1,290 +1,295 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KeyboardModel.h" struct Key { explicit Key(QString keyText, KeyboardModel::KeyType key = KeyboardModel::NormalKey, float size = 1.0f) : text(keyText), keyType(key), width(size) { } QString text; KeyboardModel::KeyType keyType; float width; }; class KeyboardModel::Private { public: Private() : mode(NormalMode), currentKeys(&normalKeys), useBuiltIn(true) { #ifdef Q_OS_WIN useBuiltIn = false; #endif } KeyboardMode mode; QList *currentKeys; QList normalKeys; QList capitalKeys; QList numericKeys; bool useBuiltIn; }; KeyboardModel::KeyboardModel(QObject* parent) : QAbstractListModel(parent), d(new Private) { - QHash roleNames; - roleNames.insert(TextRole, "text"); - roleNames.insert(TypeRole, "keyType"); - roleNames.insert(WidthRole, "width"); - setRoleNames(roleNames); } KeyboardModel::~KeyboardModel() { delete d; } +QHash KeyboardModel::roleNames() const +{ + QHash roleNames; + roleNames.insert(TextRole, "text"); + roleNames.insert(TypeRole, "keyType"); + roleNames.insert(WidthRole, "width"); + + return roleNames; +} + void KeyboardModel::classBegin() { } void KeyboardModel::componentComplete() { //Set up keys //Normal mode //First row d->normalKeys.append(Key("q")); d->normalKeys.append(Key("w")); d->normalKeys.append(Key("e")); d->normalKeys.append(Key("r")); d->normalKeys.append(Key("t")); d->normalKeys.append(Key("y")); d->normalKeys.append(Key("u")); d->normalKeys.append(Key("i")); d->normalKeys.append(Key("o")); d->normalKeys.append(Key("p")); d->normalKeys.append(Key(QChar(0x2190), BackspaceKey, 2.0f)); //Second row d->normalKeys.append(Key("", SpacerKey, 0.5f)); d->normalKeys.append(Key("a")); d->normalKeys.append(Key("s")); d->normalKeys.append(Key("d")); d->normalKeys.append(Key("f")); d->normalKeys.append(Key("g")); d->normalKeys.append(Key("h")); d->normalKeys.append(Key("j")); d->normalKeys.append(Key("k")); d->normalKeys.append(Key("l")); d->normalKeys.append(Key("'")); d->normalKeys.append(Key("Enter", EnterKey, 1.5f)); //Third row d->normalKeys.append(Key(QChar(0x2191), ShiftKey)); d->normalKeys.append(Key("z")); d->normalKeys.append(Key("x")); d->normalKeys.append(Key("c")); d->normalKeys.append(Key("v")); d->normalKeys.append(Key("b")); d->normalKeys.append(Key("n")); d->normalKeys.append(Key("m")); d->normalKeys.append(Key(",")); d->normalKeys.append(Key(".")); d->normalKeys.append(Key("?")); d->normalKeys.append(Key(QChar(0x2191), ShiftKey)); //Fourth row d->normalKeys.append(Key("&123", NumericModeKey)); d->normalKeys.append(Key("Ctrl", SpacerKey)); d->normalKeys.append(Key(QChar(0x263A), SpacerKey)); d->normalKeys.append(Key(" ", NormalKey, 6.f)); d->normalKeys.append(Key("<", LeftArrowKey)); d->normalKeys.append(Key(">", RightArrowKey)); d->normalKeys.append(Key("Close", CloseKey)); //Capital mode //First row d->capitalKeys.append(Key("Q")); d->capitalKeys.append(Key("W")); d->capitalKeys.append(Key("E")); d->capitalKeys.append(Key("R")); d->capitalKeys.append(Key("T")); d->capitalKeys.append(Key("Y")); d->capitalKeys.append(Key("U")); d->capitalKeys.append(Key("I")); d->capitalKeys.append(Key("O")); d->capitalKeys.append(Key("P")); d->capitalKeys.append(Key(QChar(0x2190), BackspaceKey, 2.0f)); //Second row d->capitalKeys.append(Key("", SpacerKey, 0.5f)); d->capitalKeys.append(Key("A")); d->capitalKeys.append(Key("S")); d->capitalKeys.append(Key("D")); d->capitalKeys.append(Key("F")); d->capitalKeys.append(Key("G")); d->capitalKeys.append(Key("H")); d->capitalKeys.append(Key("J")); d->capitalKeys.append(Key("K")); d->capitalKeys.append(Key("L")); d->capitalKeys.append(Key("\"")); d->capitalKeys.append(Key("Enter", EnterKey, 1.5f)); //Third row d->capitalKeys.append(Key(QChar(0x2191), ShiftKey)); d->capitalKeys.append(Key("Z")); d->capitalKeys.append(Key("X")); d->capitalKeys.append(Key("C")); d->capitalKeys.append(Key("V")); d->capitalKeys.append(Key("B")); d->capitalKeys.append(Key("N")); d->capitalKeys.append(Key("M")); d->capitalKeys.append(Key(";")); d->capitalKeys.append(Key(":")); d->capitalKeys.append(Key("!")); d->capitalKeys.append(Key(QChar(0x2191), ShiftKey)); //Fourth row d->capitalKeys.append(Key("&123", NumericModeKey)); d->capitalKeys.append(Key("Ctrl", SpacerKey)); d->capitalKeys.append(Key(QChar(0x263A), SpacerKey)); d->capitalKeys.append(Key(" ", NormalKey, 6.f)); d->capitalKeys.append(Key("<", LeftArrowKey)); d->capitalKeys.append(Key(">", RightArrowKey)); d->capitalKeys.append(Key("Close", CloseKey)); //Capital mode //First row d->numericKeys.append(Key("Tab", SpacerKey)); d->numericKeys.append(Key("!")); d->numericKeys.append(Key("@")); d->numericKeys.append(Key("#")); d->numericKeys.append(Key("$")); d->numericKeys.append(Key("%")); d->numericKeys.append(Key("&")); d->numericKeys.append(Key("", SpacerKey, 0.5f)); d->numericKeys.append(Key("1")); d->numericKeys.append(Key("2")); d->numericKeys.append(Key("3")); d->numericKeys.append(Key("", SpacerKey, 0.5f)); d->numericKeys.append(Key(QChar(0x2190), BackspaceKey)); //Second row d->numericKeys.append(Key("<", SpacerKey)); d->numericKeys.append(Key("(")); d->numericKeys.append(Key(")")); d->numericKeys.append(Key("-")); d->numericKeys.append(Key("_")); d->numericKeys.append(Key("=")); d->numericKeys.append(Key("+")); d->numericKeys.append(Key("", SpacerKey, 0.5f)); d->numericKeys.append(Key("4")); d->numericKeys.append(Key("5")); d->numericKeys.append(Key("6")); d->numericKeys.append(Key("", SpacerKey, 0.5f)); d->numericKeys.append(Key("Enter", EnterKey)); //Third row d->numericKeys.append(Key(">", SpacerKey)); d->numericKeys.append(Key("\\")); d->numericKeys.append(Key(";")); d->numericKeys.append(Key(":")); d->numericKeys.append(Key("\"")); d->numericKeys.append(Key("*")); d->numericKeys.append(Key("/")); d->numericKeys.append(Key("", SpacerKey, 0.5f)); d->numericKeys.append(Key("7")); d->numericKeys.append(Key("9")); d->numericKeys.append(Key("9")); d->numericKeys.append(Key("", SpacerKey, 1.5f)); //Fourth row d->numericKeys.append(Key("&123", NumericModeKey)); d->numericKeys.append(Key("Ctrl", SpacerKey)); d->numericKeys.append(Key(QChar(0x263A), SpacerKey)); d->numericKeys.append(Key("<", LeftArrowKey)); d->numericKeys.append(Key(">", RightArrowKey)); d->numericKeys.append(Key(" ", NormalKey, 2.f)); d->numericKeys.append(Key("", SpacerKey, 0.5f)); d->numericKeys.append(Key("0", NormalKey, 2.f)); d->numericKeys.append(Key(".")); d->numericKeys.append(Key("", SpacerKey, 0.5f)); d->numericKeys.append(Key("Close", CloseKey)); } QVariant KeyboardModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); switch(role) { case TextRole: return d->currentKeys->at(index.row()).text; case TypeRole: return QVariant::fromValue(d->currentKeys->at(index.row()).keyType); case WidthRole: return d->currentKeys->at(index.row()).width; default: break; } return QVariant(); } int KeyboardModel::rowCount(const QModelIndex& parent) const { Q_UNUSED(parent) return d->currentKeys->count(); } KeyboardModel::KeyboardMode KeyboardModel::keyboardMode() const { return d->mode; } void KeyboardModel::setKeyboardMode(KeyboardModel::KeyboardMode mode) { if (mode != d->mode) { d->mode = mode; beginRemoveRows(QModelIndex(), 0, d->currentKeys->count() - 1); endRemoveRows(); switch(d->mode) { case NormalMode: d->currentKeys = &d->normalKeys; break; case CapitalMode: d->currentKeys = &d->capitalKeys; break; case NumericMode: d->currentKeys = &d->numericKeys; break; } beginInsertRows(QModelIndex(), 0, d->currentKeys->count() - 1); endInsertRows(); emit keyboardModeChanged(); } } bool KeyboardModel::useBuiltIn() const { return d->useBuiltIn; } diff --git a/libs/libqml/plugins/kritasketchplugin/models/KeyboardModel.h b/libs/libqml/plugins/kritasketchplugin/models/KeyboardModel.h index a7ebe3d5e0..fe9379d05e 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/KeyboardModel.h +++ b/libs/libqml/plugins/kritasketchplugin/models/KeyboardModel.h @@ -1,82 +1,84 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KEYBOARDMODEL_H #define KEYBOARDMODEL_H #include #include class KeyboardModel : public QAbstractListModel, public QQmlParserStatus { Q_OBJECT Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(KeyboardMode mode READ keyboardMode WRITE setKeyboardMode NOTIFY keyboardModeChanged) Q_PROPERTY(bool useBuiltIn READ useBuiltIn NOTIFY useBuiltInChanged) public: enum Roles { TextRole = Qt::UserRole + 1, TypeRole, WidthRole }; enum KeyboardMode { NormalMode, CapitalMode, NumericMode }; Q_ENUMS(KeyboardMode) enum KeyType { NormalKey, SpacerKey, ShiftKey, EnterKey, BackspaceKey, NumericModeKey, CloseKey, LeftArrowKey, RightArrowKey }; Q_ENUMS(KeyType) explicit KeyboardModel(QObject* parent = 0); virtual ~KeyboardModel(); + QHash roleNames() const; + virtual void classBegin(); virtual void componentComplete(); virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; KeyboardMode keyboardMode() const; void setKeyboardMode(KeyboardModel::KeyboardMode mode); bool useBuiltIn() const; Q_SIGNALS: void keyboardModeChanged(); bool useBuiltInChanged(); private: class Private; Private * const d; }; #endif // KEYBOARDMODEL_H diff --git a/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp index ff6493db84..be39cd2375 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp @@ -1,1048 +1,1055 @@ /* This file is part of the KDE project * Copyright (C) 2012 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "LayerModel.h" #include "LayerThumbProvider.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct LayerModelMetaInfo { LayerModelMetaInfo() : canMoveUp(false) , canMoveRight(false) , canMoveDown(false) , canMoveLeft(false) , depth(-1) {} bool canMoveUp; bool canMoveRight; bool canMoveDown; bool canMoveLeft; int depth; }; class LayerModel::Private { public: Private(LayerModel* qq) : q(qq) , nodeModel(new KisNodeModel(qq)) , aboutToRemoveRoots(false) , canvas(0) , nodeManager(0) , image(0) , activeNode(0) , declarativeEngine(0) , thumbProvider(0) , updateActiveLayerWithNewFilterConfigTimer(new QTimer(qq)) , imageChangedTimer(new QTimer(qq)) { QList tmpFilters = KisFilterRegistry::instance()->values(); Q_FOREACH (const KisFilterSP& filter, tmpFilters) { filters[filter.data()->id()] = filter.data(); } updateActiveLayerWithNewFilterConfigTimer->setInterval(0); updateActiveLayerWithNewFilterConfigTimer->setSingleShot(true); connect(updateActiveLayerWithNewFilterConfigTimer, SIGNAL(timeout()), qq, SLOT(updateActiveLayerWithNewFilterConfig())); imageChangedTimer->setInterval(250); imageChangedTimer->setSingleShot(true); connect(imageChangedTimer, SIGNAL(timeout()), qq, SLOT(imageHasChanged())); } LayerModel* q; QList layers; QHash layerMeta; KisNodeModel* nodeModel; bool aboutToRemoveRoots; KisViewManager* view; KisCanvas2* canvas; QPointer nodeManager; KisImageWSP image; KisNodeSP activeNode; QQmlEngine* declarativeEngine; LayerThumbProvider* thumbProvider; QHash filters; KisFilterConfigurationSP newConfig; QTimer* updateActiveLayerWithNewFilterConfigTimer; QTimer* imageChangedTimer; static int counter() { static int count = 0; return count++; } static QStringList layerClassNames() { QStringList list; list << "KisGroupLayer"; list << "KisPaintLayer"; list << "KisFilterMask"; list << "KisAdjustmentLayer"; return list; } int deepChildCount(KisNodeSP layer) { quint32 childCount = layer->childCount(); QList children = layer->childNodes(layerClassNames(), KoProperties()); for(quint32 i = 0; i < childCount; ++i) childCount += deepChildCount(children.at(i)); return childCount; } void rebuildLayerList(KisNodeSP layer = 0) { bool refreshingFromRoot = false; if (!image) { layers.clear(); return; } if (layer == 0) { refreshingFromRoot = true; layers.clear(); layer = image->rootLayer(); } // implementation node: The root node is not a visible node, and so // is never added to the list of layers QList children = layer->childNodes(layerClassNames(), KoProperties()); if (children.count() == 0) return; for(quint32 i = children.count(); i > 0; --i) { layers << children.at(i-1); rebuildLayerList(children.at(i-1)); } if (refreshingFromRoot) refreshLayerMovementAbilities(); } void refreshLayerMovementAbilities() { layerMeta.clear(); if (layers.count() == 0) return; for(int i = 0; i < layers.count(); ++i) { const KisNodeSP layer = layers.at(i); LayerModelMetaInfo ability; if (i > 0) ability.canMoveUp = true; if (i < layers.count() - 1) ability.canMoveDown = true; KisNodeSP parent = layer; while(parent) { ++ability.depth; parent = parent->parent(); } if (ability.depth > 1) ability.canMoveLeft = true; if (i < layers.count() - 1 && qobject_cast(layers.at(i + 1).constData())) ability.canMoveRight = true; layerMeta[layer] = ability; } } }; LayerModel::LayerModel(QObject* parent) : QAbstractListModel(parent) , d(new Private(this)) { - QHash roles; - roles[IconRole] = "icon"; - roles[NameRole] = "name"; - roles[ActiveLayerRole] = "activeLayer"; - roles[OpacityRole] = "opacity"; - roles[PercentOpacityRole] = "percentOpacity"; - roles[VisibleRole] = "visible"; - roles[LockedRole] = "locked"; - roles[CompositeDetailsRole] = "compositeDetails"; - roles[FilterRole] = "filter"; - roles[ChildCountRole] = "childCount"; - roles[DeepChildCountRole] = "deepChildCount"; - roles[DepthRole] = "depth"; - roles[PreviousItemDepthRole] = "previousItemDepth"; - roles[NextItemDepthRole] = "nextItemDepth"; - roles[CanMoveDownRole] = "canMoveDown"; - roles[CanMoveLeftRole] = "canMoveLeft"; - roles[CanMoveRightRole] = "canMoveRight"; - roles[CanMoveUpRole] = "canMoveUp"; - setRoleNames(roles); connect(d->nodeModel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(source_rowsAboutToBeInserted(QModelIndex, int, int))); connect(d->nodeModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(source_rowsInserted(QModelIndex, int, int))); connect(d->nodeModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(source_rowsAboutToBeRemoved(QModelIndex, int, int))); connect(d->nodeModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(source_rowsRemoved(QModelIndex, int, int))); connect(d->nodeModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(source_dataChanged(QModelIndex,QModelIndex))); connect(d->nodeModel, SIGNAL(modelReset()), this, SLOT(source_modelReset())); connect(d->nodeModel, SIGNAL(layoutAboutToBeChanged()), this, SIGNAL(layoutAboutToBeChanged())); connect(d->nodeModel, SIGNAL(layoutChanged()), this, SIGNAL(layoutChanged())); } LayerModel::~LayerModel() { delete d; } +QHash LayerModel::roleNames() const +{ + QHash roles; + roles[IconRole] = "icon"; + roles[NameRole] = "name"; + roles[ActiveLayerRole] = "activeLayer"; + roles[OpacityRole] = "opacity"; + roles[PercentOpacityRole] = "percentOpacity"; + roles[VisibleRole] = "visible"; + roles[LockedRole] = "locked"; + roles[CompositeDetailsRole] = "compositeDetails"; + roles[FilterRole] = "filter"; + roles[ChildCountRole] = "childCount"; + roles[DeepChildCountRole] = "deepChildCount"; + roles[DepthRole] = "depth"; + roles[PreviousItemDepthRole] = "previousItemDepth"; + roles[NextItemDepthRole] = "nextItemDepth"; + roles[CanMoveDownRole] = "canMoveDown"; + roles[CanMoveLeftRole] = "canMoveLeft"; + roles[CanMoveRightRole] = "canMoveRight"; + roles[CanMoveUpRole] = "canMoveUp"; + + return roles; +} + QObject* LayerModel::view() const { return d->view; } void LayerModel::setView(QObject *newView) { KisViewManager* view = qobject_cast(newView); // This has to happen very early, and we will be filling it back up again soon anyway... if (d->canvas) { d->canvas->disconnectCanvasObserver(this); disconnect(d->image, 0, this, 0); disconnect(d->nodeManager, 0, this, 0); disconnect(d->nodeModel, 0, d->nodeManager, 0); disconnect(d->nodeModel, SIGNAL(nodeActivated(KisNodeSP)), this, SLOT(currentNodeChanged(KisNodeSP))); d->image = 0; d->nodeManager = 0; d->layers.clear(); d->activeNode.clear(); d->canvas = 0; d->nodeModel->setDummiesFacade(0, 0, 0, 0, 0); } d->view = view; if (!d->view) { return; } d->canvas = view->canvasBase(); d->thumbProvider = new LayerThumbProvider(); d->thumbProvider->setLayerModel(this); d->thumbProvider->setLayerID(Private::counter()); // QT5TODO: results in a crash d->declarativeEngine->addImageProvider(QString("layerthumb%1").arg(d->thumbProvider->layerID()), d->thumbProvider); if (d->canvas) { d->image = d->canvas->imageView()->image(); d->nodeManager = d->canvas->viewManager()->nodeManager(); KisDummiesFacadeBase *kritaDummiesFacade = dynamic_cast(d->canvas->imageView()->document()->shapeController()); KisShapeController *shapeController = dynamic_cast(d->canvas->imageView()->document()->shapeController()); d->nodeModel->setDummiesFacade(kritaDummiesFacade, d->image, shapeController, d->nodeManager->nodeSelectionAdapter(), d->nodeManager->nodeInsertionAdapter()); connect(d->image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted())); connect(d->image, SIGNAL(sigNodeChanged(KisNodeSP)), SLOT(nodeChanged(KisNodeSP))); connect(d->image, SIGNAL(sigImageUpdated(QRect)), SLOT(imageChanged())); connect(d->image, SIGNAL(sigRemoveNodeAsync(KisNodeSP)), SLOT(aboutToRemoveNode(KisNodeSP))); // cold start currentNodeChanged(d->nodeManager->activeNode()); // Connection KisNodeManager -> KisLayerBox connect(d->nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)), this, SLOT(currentNodeChanged(KisNodeSP))); d->rebuildLayerList(); - reset(); + beginResetModel(); + endResetModel(); } } QObject* LayerModel::engine() const { return d->declarativeEngine; } void LayerModel::setEngine(QObject* newEngine) { d->declarativeEngine = qobject_cast(newEngine); emit engineChanged(); } QString LayerModel::fullImageThumbUrl() const { return QString("image://layerthumb%1/fullimage/%2").arg(d->thumbProvider->layerID()).arg(QDateTime::currentMSecsSinceEpoch()); } void LayerModel::currentNodeChanged(KisNodeSP newActiveNode) { if (!d->activeNode.isNull()) { QModelIndex oldIndex = d->nodeModel->indexFromNode(d->activeNode); source_dataChanged(oldIndex, oldIndex); } d->activeNode = newActiveNode; emitActiveChanges(); if (!d->activeNode.isNull()) { QModelIndex oldIndex = d->nodeModel->indexFromNode(d->activeNode); source_dataChanged(oldIndex, oldIndex); } } QVariant LayerModel::data(const QModelIndex& index, int role) const { QVariant data; if (index.isValid()) { index.internalPointer(); KisNodeSP node = d->layers.at(index.row()); if (node.isNull()) return data; KisNodeSP parent; switch(role) { case IconRole: if (dynamic_cast(node.constData())) data = QLatin1String("../images/svg/icon-layer_group-black.svg"); else if (dynamic_cast(node.constData())) data = QLatin1String("../images/svg/icon-layer_filter-black.svg"); else if (dynamic_cast(node.constData())) data = QLatin1String("../images/svg/icon-layer_filter-black.svg"); else // We add the currentMSecsSinceEpoch to ensure we force an update (even with cache turned // off, we still apparently get some caching behaviour on delegates in QML) data = QString("image://layerthumb%1/%2/%3").arg(d->thumbProvider->layerID()).arg(index.row()).arg(QDateTime::currentMSecsSinceEpoch()); break; case NameRole: data = node->name(); break; case ActiveLayerRole: data = (node == d->activeNode); break; case OpacityRole: data = node->opacity(); break; case PercentOpacityRole: data = node->percentOpacity(); break; case VisibleRole: data = node->visible(); break; case LockedRole: data = node->userLocked(); break; case CompositeDetailsRole: // composite op goes here... if (node->compositeOp()) data = node->compositeOp()->description(); break; case FilterRole: break; case ChildCountRole: data = node->childNodes(d->layerClassNames(), KoProperties()).count(); break; case DeepChildCountRole: data = d->deepChildCount(d->layers.at(index.row())); break; case DepthRole: data = d->layerMeta[node.data()].depth; break; case PreviousItemDepthRole: if (index.row() == 0) data = -1; else data = d->layerMeta[d->layers[index.row() - 1].data()].depth; break; case NextItemDepthRole: if (index.row() == d->layers.count() - 1) data = -1; else data = d->layerMeta[d->layers[index.row() + 1].data()].depth; break; case CanMoveDownRole: data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveDown; break; case CanMoveLeftRole: data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveLeft; break; case CanMoveRightRole: data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveRight; break; case CanMoveUpRole: data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveUp; break; default: break; } } return data; } int LayerModel::rowCount(const QModelIndex& parent) const { if ( parent.isValid() ) { return 0; } return d->layers.count(); } QVariant LayerModel::headerData(int section, Qt::Orientation orientation, int role) const { return QAbstractItemModel::headerData(section, orientation, role); } void LayerModel::setActive(int index) { if (index > -1 && index < d->layers.count()) { KisNodeSP newNode = d->layers.at(index); d->nodeManager->slotUiActivatedNode(newNode); currentNodeChanged(newNode); } } void LayerModel::moveUp() { KisNodeSP node = d->nodeManager->activeNode(); KisNodeSP parent = node->parent(); KisNodeSP grandParent = parent->parent(); if (!d->nodeManager->activeNode()->nextSibling()) { //dbgKrita << "Active node apparently has no next sibling, however that has happened..."; if (!grandParent) return; //dbgKrita << "Node has grandparent"; if (!grandParent->parent() && node->inherits("KisMask")) return; //dbgKrita << "Node isn't a mask"; d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1); } else { //dbgKrita << "Move node directly"; d->nodeManager->lowerNode(); } } void LayerModel::moveDown() { KisNodeSP node = d->nodeManager->activeNode(); KisNodeSP parent = node->parent(); KisNodeSP grandParent = parent->parent(); if (!d->nodeManager->activeNode()->prevSibling()) { //dbgKrita << "Active node apparently has no previous sibling, however that has happened..."; if (!grandParent) return; //dbgKrita << "Node has grandparent"; if (!grandParent->parent() && node->inherits("KisMask")) return; //dbgKrita << "Node isn't a mask"; d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent)); } else { //dbgKrita << "Move node directly"; d->nodeManager->raiseNode(); } } void LayerModel::moveLeft() { KisNodeSP node = d->nodeManager->activeNode(); KisNodeSP parent = node->parent(); KisNodeSP grandParent = parent->parent(); quint16 nodeIndex = parent->index(node); if (!grandParent) return; if (!grandParent->parent() && node->inherits("KisMask")) return; if (nodeIndex <= parent->childCount() / 2) { d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent)); } else { d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1); } } void LayerModel::moveRight() { KisNodeSP node = d->nodeManager->activeNode(); KisNodeSP parent = d->nodeManager->activeNode()->parent(); KisNodeSP newParent; int nodeIndex = parent->index(node); int indexAbove = nodeIndex + 1; int indexBelow = nodeIndex - 1; if (parent->at(indexBelow) && parent->at(indexBelow)->allowAsChild(node)) { newParent = parent->at(indexBelow); d->nodeManager->moveNodeAt(node, newParent, newParent->childCount()); } else if (parent->at(indexAbove) && parent->at(indexAbove)->allowAsChild(node)) { newParent = parent->at(indexAbove); d->nodeManager->moveNodeAt(node, newParent, 0); } else { return; } } void LayerModel::clear() { d->canvas->viewManager()->selectionManager()->clear(); } void LayerModel::clone() { d->nodeManager->duplicateActiveNode(); } void LayerModel::setLocked(int index, bool newLocked) { if (index > -1 && index < d->layers.count()) { if(d->layers[index]->userLocked() == newLocked) return; d->layers[index]->setUserLocked(newLocked); QModelIndex idx = createIndex(index, 0); dataChanged(idx, idx); } } void LayerModel::setOpacity(int index, float newOpacity) { if (index > -1 && index < d->layers.count()) { if(qFuzzyCompare(d->layers[index]->opacity() + 1, newOpacity + 1)) return; d->layers[index]->setOpacity(newOpacity); d->layers[index]->setDirty(); QModelIndex idx = createIndex(index, 0); dataChanged(idx, idx); } } void LayerModel::setVisible(int index, bool newVisible) { if (index > -1 && index < d->layers.count()) { KisBaseNode::PropertyList props = d->layers[index]->sectionModelProperties(); if(props[0].state == newVisible) return; KisBaseNode::Property prop = props[0]; prop.state = newVisible; props[0] = prop; d->nodeModel->setData( d->nodeModel->indexFromNode(d->layers[index]), QVariant::fromValue(props), KisNodeModel::PropertiesRole ); d->layers[index]->setDirty(d->layers[index]->extent()); QModelIndex idx = createIndex(index, 0); dataChanged(idx, idx); } } QImage LayerModel::layerThumbnail(QString layerID) const { // So, yeah, this is a complete cheatery hack. However, it ensures // we actually get updates when we want them (every time the image is supposed // to be changed). Had hoped we could avoid it, but apparently not. int index = layerID.section(QChar('/'), 0, 0).toInt(); QImage thumb; if (index > -1 && index < d->layers.count()) { if (d->thumbProvider) thumb = d->layers[index]->createThumbnail(120, 120); } return thumb; } void LayerModel::deleteCurrentLayer() { d->activeNode.clear(); d->nodeManager->removeNode(); } void LayerModel::deleteLayer(int index) { if (index > -1 && index < d->layers.count()) { if (d->activeNode == d->layers.at(index)) d->activeNode.clear(); d->nodeManager->slotUiActivatedNode(d->layers.at(index)); d->nodeManager->removeNode(); d->rebuildLayerList(); - reset(); + beginResetModel(); + endResetModel(); } } void LayerModel::addLayer(int layerType) { switch(layerType) { case 0: d->nodeManager->createNode("KisPaintLayer"); break; case 1: d->nodeManager->createNode("KisGroupLayer"); break; case 2: d->nodeManager->createNode("KisFilterMask", true); break; default: break; } } void LayerModel::source_rowsAboutToBeInserted(QModelIndex /*p*/, int /*from*/, int /*to*/) { beginResetModel(); } void LayerModel::source_rowsInserted(QModelIndex /*p*/, int, int) { d->rebuildLayerList(); emit countChanged(); endResetModel(); } void LayerModel::source_rowsAboutToBeRemoved(QModelIndex /*p*/, int /*from*/, int /*to*/) { beginResetModel(); } void LayerModel::source_rowsRemoved(QModelIndex, int, int) { d->rebuildLayerList(); emit countChanged(); endResetModel(); } void LayerModel::source_dataChanged(QModelIndex /*tl*/, QModelIndex /*br*/) { QModelIndex top = createIndex(0, 0); QModelIndex bottom = createIndex(d->layers.count() - 1, 0); dataChanged(top, bottom); } void LayerModel::source_modelReset() { beginResetModel(); d->rebuildLayerList(); d->activeNode.clear(); if (d->layers.count() > 0) { d->nodeManager->slotUiActivatedNode(d->layers.at(0)); currentNodeChanged(d->layers.at(0)); } emit countChanged(); endResetModel(); } void LayerModel::notifyImageDeleted() { } void LayerModel::nodeChanged(KisNodeSP node) { QModelIndex index = createIndex(d->layers.indexOf(node), 0); dataChanged(index, index); } void LayerModel::imageChanged() { d->imageChangedTimer->start(); } void LayerModel::imageHasChanged() { QModelIndex top = createIndex(0, 0); QModelIndex bottom = createIndex(d->layers.count() - 1, 0); dataChanged(top, bottom); } void LayerModel::aboutToRemoveNode(KisNodeSP node) { Q_UNUSED(node) QTimer::singleShot(0, this, SLOT(source_modelReset())); } void LayerModel::emitActiveChanges() { emit activeFilterConfigChanged(); emit activeNameChanged(); emit activeTypeChanged(); emit activeCompositeOpChanged(); emit activeOpacityChanged(); emit activeVisibleChanged(); emit activeLockedChanged(); emit activeRChannelActiveChanged(); emit activeGChannelActiveChanged(); emit activeBChannelActiveChanged(); emit activeAChannelActiveChanged(); emit activeRChannelLockedChanged(); emit activeGChannelLockedChanged(); emit activeBChannelLockedChanged(); emit activeAChannelLockedChanged(); } QString LayerModel::activeName() const { if (d->activeNode.isNull()) return QString(); return d->activeNode->name(); } void LayerModel::setActiveName(QString newName) { if (d->activeNode.isNull()) return; d->activeNode->setName(newName); emit activeNameChanged(); } QString LayerModel::activeType() const { return d->activeNode->metaObject()->className(); } int LayerModel::activeCompositeOp() const { if (d->activeNode.isNull()) return 0; KoID entry(d->activeNode->compositeOp()->id()); QModelIndex idx = KisCompositeOpListModel::sharedInstance()->indexOf(entry); if (idx.isValid()) return idx.row(); return 0; } void LayerModel::setActiveCompositeOp(int newOp) { if (d->activeNode.isNull()) return; KoID entry; if (KisCompositeOpListModel::sharedInstance()->entryAt(entry, KisCompositeOpListModel::sharedInstance()->index(newOp))) { d->activeNode->setCompositeOpId(entry.id()); d->activeNode->setDirty(); emit activeCompositeOpChanged(); } } int LayerModel::activeOpacity() const { if (d->activeNode.isNull()) return 0; return d->activeNode->opacity(); } void LayerModel::setActiveOpacity(int newOpacity) { d->activeNode->setOpacity(newOpacity); d->activeNode->setDirty(); emit activeOpacityChanged(); } bool LayerModel::activeVisible() const { if (d->activeNode.isNull()) return false; return d->activeNode->visible(); } void LayerModel::setActiveVisibile(bool newVisible) { if (d->activeNode.isNull()) return; setVisible(d->layers.indexOf(d->activeNode), newVisible); emit activeVisibleChanged(); } bool LayerModel::activeLocked() const { if (d->activeNode.isNull()) return false; return d->activeNode->userLocked(); } void LayerModel::setActiveLocked(bool newLocked) { if (d->activeNode.isNull()) return; d->activeNode->setUserLocked(newLocked); emit activeLockedChanged(); } bool LayerModel::activeAChannelActive() const { KisLayer* layer = qobject_cast(d->activeNode.data()); bool state = false; if (layer) state = !layer->alphaChannelDisabled(); return state; } void LayerModel::setActiveAChannelActive(bool newActive) { KisLayer* layer = qobject_cast(d->activeNode.data()); if (layer) { layer->disableAlphaChannel(!newActive); layer->setDirty(); emit activeAChannelActiveChanged(); } } bool LayerModel::activeAChannelLocked() const { KisPaintLayer* layer = qobject_cast(d->activeNode.data()); bool state = false; if (layer) state = layer->alphaLocked(); return state; } void LayerModel::setActiveAChannelLocked(bool newLocked) { KisPaintLayer* layer = qobject_cast(d->activeNode.data()); if (layer) { layer->setAlphaLocked(newLocked); emit activeAChannelLockedChanged(); } } bool getActiveChannel(KisNodeSP node, int channelIndex) { KisLayer* layer = qobject_cast(node.data()); bool flag = false; if (layer) { QBitArray flags = layer->channelFlags(); if (channelIndex < flags.size()) { flag = flags[channelIndex]; } } return flag; } bool getLockedChannel(KisNodeSP node, int channelIndex) { KisPaintLayer* layer = qobject_cast(node.data()); bool flag = false; if (layer) { QBitArray flags = layer->channelLockFlags(); flags = flags.isEmpty() ? layer->colorSpace()->channelFlags(true, true) : flags; flag = flags[channelIndex]; } return flag; } void setChannelActive(KisNodeSP node, int channelIndex, bool newActive) { KisLayer* layer = qobject_cast(node.data()); if (layer) { QBitArray flags = layer->channelFlags(); flags.setBit(channelIndex, newActive); layer->setChannelFlags(flags); layer->setDirty(); } } void setChannelLocked(KisNodeSP node, int channelIndex, bool newLocked) { KisPaintLayer* layer = qobject_cast(node.data()); if (layer) { QBitArray flags = layer->channelLockFlags(); flags = flags.isEmpty() ? layer->colorSpace()->channelFlags(true, true) : flags; flags.setBit(channelIndex, newLocked); layer->setChannelLockFlags(flags); } } bool LayerModel::activeBChannelActive() const { return getActiveChannel(d->activeNode, 2); } void LayerModel::setActiveBChannelActive(bool newActive) { setChannelActive(d->activeNode, 2, newActive); emit activeBChannelActiveChanged(); } bool LayerModel::activeBChannelLocked() const { return getLockedChannel(d->activeNode, 2); } void LayerModel::setActiveBChannelLocked(bool newLocked) { setChannelLocked(d->activeNode, 2, newLocked); emit activeBChannelLockedChanged(); } bool LayerModel::activeGChannelActive() const { return getActiveChannel(d->activeNode, 1); } void LayerModel::setActiveGChannelActive(bool newActive) { setChannelActive(d->activeNode, 1, newActive); emit activeGChannelActiveChanged(); } bool LayerModel::activeGChannelLocked() const { return getLockedChannel(d->activeNode, 1); } void LayerModel::setActiveGChannelLocked(bool newLocked) { setChannelLocked(d->activeNode, 1, newLocked); emit activeGChannelLockedChanged(); } bool LayerModel::activeRChannelActive() const { return getActiveChannel(d->activeNode, 0); } void LayerModel::setActiveRChannelActive(bool newActive) { setChannelActive(d->activeNode, 0, newActive); emit activeRChannelActiveChanged(); } bool LayerModel::activeRChannelLocked() const { return getLockedChannel(d->activeNode, 0); } void LayerModel::setActiveRChannelLocked(bool newLocked) { setChannelLocked(d->activeNode, 0, newLocked); emit activeRChannelLockedChanged(); } QObject* LayerModel::activeFilterConfig() const { QMap props; QString filterId; KisFilterMask* filterMask = qobject_cast(d->activeNode.data()); if (filterMask) { props = filterMask->filter()->getProperties(); filterId = filterMask->filter()->name(); } else { KisAdjustmentLayer* adjustmentLayer = qobject_cast(d->activeNode.data()); if (adjustmentLayer) { props = adjustmentLayer->filter()->getProperties(); filterId = adjustmentLayer->filter()->name(); } } PropertyContainer* config = new PropertyContainer(filterId, 0); QMap::const_iterator i; for(i = props.constBegin(); i != props.constEnd(); ++i) { config->setProperty(i.key().toLatin1(), i.value()); //dbgKrita << "Getting active config..." << i.key() << i.value(); } return config; } void LayerModel::setActiveFilterConfig(QObject* newConfig) { if (d->activeNode.isNull()) return; PropertyContainer* config = qobject_cast(newConfig); if (!config) return; //dbgKrita << "Attempting to set new config" << config->name(); KisFilterConfigurationSP realConfig = d->filters.value(config->name())->factoryConfiguration(); QMap::const_iterator i; for(i = realConfig->getProperties().constBegin(); i != realConfig->getProperties().constEnd(); ++i) { realConfig->setProperty(i.key(), config->property(i.key().toLatin1())); //dbgKrita << "Creating config..." << i.key() << i.value(); } // The following code causes sporadic crashes, and disabling causes leaks. So, leaks it must be, for now. // The cause is the lack of a smart pointer interface for passing filter configs around // Must be remedied, but for now... // if (d->newConfig) // delete(d->newConfig); d->newConfig = realConfig; //d->updateActiveLayerWithNewFilterConfigTimer->start(); updateActiveLayerWithNewFilterConfig(); } void LayerModel::updateActiveLayerWithNewFilterConfig() { if (!d->newConfig) return; //dbgKrita << "Setting new config..." << d->newConfig->name(); KisFilterMask* filterMask = qobject_cast(d->activeNode.data()); if (filterMask) { //dbgKrita << "Filter mask"; if (filterMask->filter() == d->newConfig) return; //dbgKrita << "Setting filter mask"; filterMask->setFilter(d->newConfig); } else { KisAdjustmentLayer* adjustmentLayer = qobject_cast(d->activeNode.data()); if (adjustmentLayer) { //dbgKrita << "Adjustment layer"; if (adjustmentLayer->filter() == d->newConfig) return; //dbgKrita << "Setting filter on adjustment layer"; adjustmentLayer->setFilter(d->newConfig); } else { //dbgKrita << "UNKNOWN, BAIL OUT!"; } } d->newConfig = 0; d->activeNode->setDirty(d->activeNode->extent()); d->image->setModified(); QTimer::singleShot(100, this, SIGNAL(activeFilterConfigChanged())); } diff --git a/libs/libqml/plugins/kritasketchplugin/models/LayerModel.h b/libs/libqml/plugins/kritasketchplugin/models/LayerModel.h index d801e5e690..e774f46838 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/LayerModel.h +++ b/libs/libqml/plugins/kritasketchplugin/models/LayerModel.h @@ -1,172 +1,172 @@ /* This file is part of the KDE project * Copyright (C) 2012 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LAYERMODEL_H #define LAYERMODEL_H #include #include #include class LayerModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QObject* view READ view WRITE setView NOTIFY viewChanged) Q_PROPERTY(QObject* engine READ engine WRITE setEngine NOTIFY engineChanged) // This might seem a slightly odd position, but think of it as the thumbnail of all the currently visible layers merged down Q_PROPERTY(QString fullImageThumbUrl READ fullImageThumbUrl NOTIFY viewChanged); Q_PROPERTY(int count READ rowCount NOTIFY countChanged); Q_PROPERTY(QString activeName READ activeName WRITE setActiveName NOTIFY activeNameChanged); Q_PROPERTY(QString activeType READ activeType NOTIFY activeTypeChanged); Q_PROPERTY(int activeCompositeOp READ activeCompositeOp WRITE setActiveCompositeOp NOTIFY activeCompositeOpChanged); Q_PROPERTY(int activeOpacity READ activeOpacity WRITE setActiveOpacity NOTIFY activeOpacityChanged); Q_PROPERTY(bool activeVisible READ activeVisible WRITE setActiveVisibile NOTIFY activeVisibleChanged); Q_PROPERTY(bool activeLocked READ activeLocked WRITE setActiveLocked NOTIFY activeLockedChanged); Q_PROPERTY(bool activeRChannelActive READ activeRChannelActive WRITE setActiveRChannelActive NOTIFY activeRChannelActiveChanged); Q_PROPERTY(bool activeGChannelActive READ activeGChannelActive WRITE setActiveGChannelActive NOTIFY activeGChannelActiveChanged); Q_PROPERTY(bool activeBChannelActive READ activeBChannelActive WRITE setActiveBChannelActive NOTIFY activeBChannelActiveChanged); Q_PROPERTY(bool activeAChannelActive READ activeAChannelActive WRITE setActiveAChannelActive NOTIFY activeAChannelActiveChanged); Q_PROPERTY(bool activeRChannelLocked READ activeRChannelLocked WRITE setActiveRChannelLocked NOTIFY activeRChannelLockedChanged); Q_PROPERTY(bool activeGChannelLocked READ activeGChannelLocked WRITE setActiveGChannelLocked NOTIFY activeGChannelLockedChanged); Q_PROPERTY(bool activeBChannelLocked READ activeBChannelLocked WRITE setActiveBChannelLocked NOTIFY activeBChannelLockedChanged); Q_PROPERTY(bool activeAChannelLocked READ activeAChannelLocked WRITE setActiveAChannelLocked NOTIFY activeAChannelLockedChanged); Q_PROPERTY(QObject* activeFilterConfig READ activeFilterConfig WRITE setActiveFilterConfig NOTIFY activeFilterConfigChanged); public: enum LayerRoles { IconRole = Qt::UserRole + 1, NameRole, ActiveLayerRole, OpacityRole, PercentOpacityRole, VisibleRole, LockedRole, CompositeDetailsRole, FilterRole, ChildCountRole, DeepChildCountRole, DepthRole, PreviousItemDepthRole, NextItemDepthRole, CanMoveLeftRole, CanMoveRightRole, CanMoveUpRole, CanMoveDownRole }; explicit LayerModel(QObject* parent = 0); virtual ~LayerModel(); - + QHash roleNames() const; QObject* view() const; void setView(QObject* newView); QObject* engine() const; void setEngine(QObject* newEngine); QString fullImageThumbUrl() const; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; Q_INVOKABLE virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; Q_INVOKABLE void setActive(int index); Q_INVOKABLE void moveUp(); Q_INVOKABLE void moveDown(); Q_INVOKABLE void moveLeft(); Q_INVOKABLE void moveRight(); void emitActiveChanges(); Q_INVOKABLE void setOpacity(int index, float newOpacity); Q_INVOKABLE void setVisible(int index, bool newVisible); Q_INVOKABLE void setLocked(int index, bool newLocked); QImage layerThumbnail(QString layerID) const; Q_INVOKABLE void clear(); Q_INVOKABLE void clone(); Q_INVOKABLE void deleteCurrentLayer(); Q_INVOKABLE void deleteLayer(int index); Q_INVOKABLE void addLayer(int layerType); QString activeName() const; void setActiveName(QString newName); QString activeType() const; int activeCompositeOp() const; void setActiveCompositeOp(int newOp); int activeOpacity() const; void setActiveOpacity(int newOpacity); bool activeVisible() const; void setActiveVisibile(bool newVisible); bool activeLocked() const; void setActiveLocked(bool newLocked); bool activeRChannelActive() const; void setActiveRChannelActive(bool newActive); bool activeGChannelActive() const; void setActiveGChannelActive(bool newActive); bool activeBChannelActive() const; void setActiveBChannelActive(bool newActive); bool activeAChannelActive() const; void setActiveAChannelActive(bool newActive); bool activeRChannelLocked() const; void setActiveRChannelLocked(bool newLocked); bool activeGChannelLocked() const; void setActiveGChannelLocked(bool newLocked); bool activeBChannelLocked() const; void setActiveBChannelLocked(bool newLocked); bool activeAChannelLocked() const; void setActiveAChannelLocked(bool newLocked); QObject* activeFilterConfig() const; void setActiveFilterConfig(QObject* newConfig); Q_SIGNALS: void viewChanged(); void engineChanged(); void countChanged(); void activeNameChanged(); void activeTypeChanged(); void activeCompositeOpChanged(); void activeOpacityChanged(); void activeVisibleChanged(); void activeLockedChanged(); void activeRChannelActiveChanged(); void activeGChannelActiveChanged(); void activeBChannelActiveChanged(); void activeAChannelActiveChanged(); void activeRChannelLockedChanged(); void activeGChannelLockedChanged(); void activeBChannelLockedChanged(); void activeAChannelLockedChanged(); void activeFilterConfigChanged(); private Q_SLOTS: void source_rowsAboutToBeInserted(QModelIndex, int, int); void source_rowsAboutToBeRemoved(QModelIndex, int, int); void source_rowsInserted(QModelIndex, int, int); void source_rowsRemoved(QModelIndex, int, int); void source_dataChanged(QModelIndex, QModelIndex); void source_modelReset(); void currentNodeChanged(KisNodeSP newActiveNode); void notifyImageDeleted(); void nodeChanged(KisNodeSP node); void imageChanged(); void imageHasChanged(); void aboutToRemoveNode(KisNodeSP node); void updateActiveLayerWithNewFilterConfig(); private: class Private; Private* d; }; #endif // LAYERMODEL_H diff --git a/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.cpp index 59cedee2ac..90af9b254d 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.cpp @@ -1,141 +1,147 @@ /* This file is part of the KDE project * Copyright (C) 2012 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "PaletteColorsModel.h" #include #include #include class PaletteColorsModel::Private { public: Private() : colorSet(0) , view(0) {} KoColorSet* colorSet; KisViewManager* view; }; PaletteColorsModel::PaletteColorsModel(QObject *parent) : QAbstractListModel(parent) , d(new Private) { - QHash roles; - roles[ImageRole] = "image"; - roles[TextRole] = "text"; - setRoleNames(roles); } PaletteColorsModel::~PaletteColorsModel() { delete d; } +QHash PaletteColorsModel::roleNames() const +{ + QHash roles; + roles[ImageRole] = "image"; + roles[TextRole] = "text"; + + return roles; +} + int PaletteColorsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; if (!d->colorSet) return 0; return d->colorSet->nColors(); } QVariant PaletteColorsModel::data(const QModelIndex &index, int role) const { QVariant result; QColor color; if (index.isValid() && d->colorSet) { switch(role) { case ImageRole: color = d->colorSet->getColorGlobal(index.row()).color.toQColor(); result = QString("image://color/%1,%2,%3,%4").arg(color.redF()).arg(color.greenF()).arg(color.blueF()).arg(color.alphaF()); break; case TextRole: result = d->colorSet->getColorGlobal(index.row()).name; break; default: break; } } return result; } QVariant PaletteColorsModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(orientation); QVariant result; if (section == 0) { switch(role) { case ImageRole: result = QString("Thumbnail"); break; case TextRole: result = QString("Name"); break; default: break; } } return result; } void PaletteColorsModel::setColorSet(QObject *newColorSet) { d->colorSet = qobject_cast(newColorSet); - reset(); + beginResetModel(); + endResetModel(); emit colorSetChanged(); } QObject* PaletteColorsModel::colorSet() const { return d->colorSet; } QObject* PaletteColorsModel::view() const { return d->view; } void PaletteColorsModel::setView(QObject* newView) { d->view = qobject_cast( newView ); emit viewChanged(); } void PaletteColorsModel::activateColor(int index, bool setBackgroundColor) { if ( !d->view ) return; if (index >= 0 && index < (int)d->colorSet->nColors()) { if (setBackgroundColor) d->view->resourceProvider()->setBGColor(d->colorSet->getColorGlobal(index).color); else d->view->resourceProvider()->setFGColor( d->colorSet->getColorGlobal(index).color); emit colorChanged(d->colorSet->getColorGlobal(index).color.toQColor(), setBackgroundColor); } } diff --git a/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.h b/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.h index b3a99c5060..cfd53e5504 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.h +++ b/libs/libqml/plugins/kritasketchplugin/models/PaletteColorsModel.h @@ -1,61 +1,61 @@ /* This file is part of the KDE project * Copyright (C) 2012 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PALETTECOLORSMODEL_H #define PALETTECOLORSMODEL_H #include #include class PaletteColorsModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QObject* colorSet READ colorSet WRITE setColorSet NOTIFY colorSetChanged) Q_PROPERTY(QObject* view READ view WRITE setView NOTIFY viewChanged) public: enum PaletteColorsRoles { ImageRole = Qt::UserRole + 1, TextRole }; explicit PaletteColorsModel(QObject *parent = 0); virtual ~PaletteColorsModel(); - + QHash roleNames() const; virtual int rowCount(const QModelIndex &parent) const; virtual QVariant data(const QModelIndex &index, int role) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; QObject* view() const; void setView(QObject* newView); QObject* colorSet() const; void setColorSet(QObject* newColorSet); Q_SIGNALS: void colorChanged(QColor newColor, bool backgroundChanged); void colorSetChanged(); void viewChanged(); public Q_SLOTS: void activateColor(int index, bool setBackgroundColor); private: class Private; Private* d; }; #endif // PALETTECOLORSMODEL_H diff --git a/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.cpp index a0929908b5..cbb18f3af5 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.cpp @@ -1,116 +1,121 @@ /* This file is part of the KDE project * Copyright (C) 2012 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "PaletteModel.h" #include #include #include #include class PaletteModel::Private { public: Private(QObject* q) : currentSet(0) { KoResourceServer* rServer = KoResourceServerProvider::instance()->paletteServer(); serverAdaptor = new KoResourceServerAdapter(rServer, q); serverAdaptor->connectToResourceServer(); } KoResourceServerAdapter* serverAdaptor; KoColorSet* currentSet; }; PaletteModel::PaletteModel(QObject *parent) : QAbstractListModel(parent) , d(new Private(this)) { - QHash roles; - roles[ImageRole] = "image"; - roles[TextRole] = "text"; - setRoleNames(roles); } PaletteModel::~PaletteModel() { delete d; } +QHash PaletteModel::roleNames() const +{ + QHash roles; + roles[ImageRole] = "image"; + roles[TextRole] = "text"; + + return roles; +} + int PaletteModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return d->serverAdaptor->resources().count(); } QVariant PaletteModel::data(const QModelIndex &index, int role) const { QVariant result; if (index.isValid()) { switch(role) { case ImageRole: result = "../images/help-about.png"; break; case TextRole: result = d->serverAdaptor->resources().at(index.row())->name(); break; default: break; } } return result; } QVariant PaletteModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(orientation); QVariant result; if (section == 0) { switch(role) { case ImageRole: result = QString("Thumbnail"); break; case TextRole: result = QString("Name"); break; default: break; } } return result; } void PaletteModel::itemActivated(int index) { QList resources = d->serverAdaptor->resources(); if (index >= 0 && index < resources.count()) { d->currentSet = dynamic_cast(resources.at(index)); emit colorSetChanged(); } } QObject* PaletteModel::colorSet() const { return d->currentSet; } diff --git a/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.h b/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.h index 05df5fbf00..db342afcd9 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.h +++ b/libs/libqml/plugins/kritasketchplugin/models/PaletteModel.h @@ -1,53 +1,53 @@ /* This file is part of the KDE project * Copyright (C) 2012 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PALETTEMODEL_H #define PALETTEMODEL_H #include class PaletteModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QObject* colorSet READ colorSet NOTIFY colorSetChanged) public: enum PaletteRoles { ImageRole = Qt::UserRole + 1, TextRole }; explicit PaletteModel(QObject *parent = 0); virtual ~PaletteModel(); - + QHash roleNames() const; virtual int rowCount(const QModelIndex &parent) const; virtual QVariant data(const QModelIndex &index, int role) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; QObject* colorSet() const; public Q_SLOTS: void itemActivated(int index); Q_SIGNALS: void colorSetChanged(); private: class Private; Private* d; }; #endif // PALETTEMODEL_H diff --git a/libs/libqml/plugins/kritasketchplugin/models/PresetModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/PresetModel.cpp index 3874ff7d57..6db3c2fd46 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/PresetModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/PresetModel.cpp @@ -1,214 +1,219 @@ /* This file is part of the KDE project * Copyright (C) 2012 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "PresetModel.h" #include #include #include #include #include #include #include #include #include #include #include class PresetModel::Private { public: Private() : view(0) { rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); } KisPaintOpPresetResourceServer * rserver; QString currentPreset; KisViewManager* view; KisPaintOpPresetSP defaultPreset(const KoID& paintOp) { QString defaultName = paintOp.id() + ".kpp"; QString path = KoResourcePaths::findResource("kis_defaultpresets", defaultName); KisPaintOpPresetSP preset = new KisPaintOpPreset(path); if (!preset->load()) preset = KisPaintOpRegistry::instance()->defaultPreset(paintOp); Q_ASSERT(preset); Q_ASSERT(preset->valid()); return preset; } void setCurrentPaintop(const KoID& paintop, KisPaintOpPresetSP preset) { preset = (!preset) ? defaultPreset(paintop) : preset; Q_ASSERT(preset && preset->settings()); // handle the settings and expose it through a a simple QObject property //m_optionWidget->setConfiguration(preset->settings()); #if 0 preset->settings()->setNode(view->resourceProvider()->currentNode()); #endif KisPaintOpFactory* paintOp = KisPaintOpRegistry::instance()->get(paintop.id()); QString pixFilename = KoResourcePaths::findResource("kis_images", paintOp->pixmap()); view->resourceProvider()->setPaintOpPreset(preset); } }; PresetModel::PresetModel(QObject *parent) : QAbstractListModel(parent) , d(new Private) { - QHash roles; - roles[ImageRole] = "image"; - roles[TextRole] = "text"; - roles[NameRole] = "name"; - setRoleNames(roles); } PresetModel::~PresetModel() { delete d; } +QHash PresetModel::roleNames() const +{ + QHash roles; + roles[ImageRole] = "image"; + roles[TextRole] = "text"; + roles[NameRole] = "name"; + + return roles; +} + int PresetModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return d->rserver->resources().count(); } QVariant PresetModel::data(const QModelIndex &index, int role) const { QVariant result; if (index.isValid()) { switch(role) { case ImageRole: result = QString("image://presetthumb/%1").arg(index.row()); break; case TextRole: result = d->rserver->resources().at(index.row())->name().replace(QLatin1String("_"), QLatin1String(" ")); break; case NameRole: result = d->rserver->resources().at(index.row())->name(); break; default: result = ""; break; } } return result; } QVariant PresetModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(orientation); QVariant result; if (section == 0) { switch(role) { case ImageRole: result = QString("Thumbnail"); break; case TextRole: result = QString("Name"); break; default: result = ""; break; } } return result; } QObject* PresetModel::view() const { return d->view; } void PresetModel::setView(QObject* newView) { d->view = qobject_cast( newView ); if (d->view && d->view->canvasBase()) { connect(d->view->canvasBase()->resourceManager(), SIGNAL(canvasResourceChanged(int, const QVariant&)), this, SLOT(resourceChanged(int, const QVariant&))); } emit viewChanged(); } QString PresetModel::currentPreset() const { return d->currentPreset; } void PresetModel::setCurrentPreset(QString presetName) { activatePreset(nameToIndex(presetName)); // not emitting here, as that happens when the resource changes... this is more // a polite request than an actual setter, due to the nature of the resource system. } int PresetModel::nameToIndex(QString presetName) const { int index = 0; QList resources = d->rserver->resources(); for(int i = 0; i < resources.count(); ++i) { if (resources.at(i)->name() == presetName || resources.at(i)->name().replace(QLatin1String("_"), QLatin1String(" ")) == presetName) { index = i; break; } } return index; } void PresetModel::activatePreset(int index) { if ( !d->view ) return; QList resources = d->rserver->resources(); if (index >= 0 && index < resources.count()) { KisPaintOpPresetSP preset = resources.at( index ); d->setCurrentPaintop(preset->paintOp(), preset); } } void PresetModel::resourceChanged(int /*key*/, const QVariant& /*v*/) { if (d->view) { KisPaintOpPresetSP preset = d->view->canvasBase()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); if (preset && d->currentPreset != preset->name()) { d->currentPreset = preset->name(); emit currentPresetChanged(); } } } diff --git a/libs/libqml/plugins/kritasketchplugin/models/PresetModel.h b/libs/libqml/plugins/kritasketchplugin/models/PresetModel.h index a91fb016f6..34dd4320c5 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/PresetModel.h +++ b/libs/libqml/plugins/kritasketchplugin/models/PresetModel.h @@ -1,64 +1,64 @@ /* This file is part of the KDE project * Copyright (C) 2012 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PRESETMODEL_H #define PRESETMODEL_H #include class PresetModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QObject* view READ view WRITE setView NOTIFY viewChanged) Q_PROPERTY(QString currentPreset READ currentPreset WRITE setCurrentPreset NOTIFY currentPresetChanged) public: enum PresetRoles { ImageRole = Qt::UserRole + 1, TextRole, NameRole }; explicit PresetModel(QObject *parent = 0); virtual ~PresetModel(); - + QHash roleNames() const; virtual int rowCount(const QModelIndex &parent) const; virtual QVariant data(const QModelIndex &index, int role) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; QObject* view() const; void setView(QObject* newView); QString currentPreset() const; void setCurrentPreset(QString presetName); Q_INVOKABLE int nameToIndex(QString presetName) const; Q_SIGNALS: void viewChanged(); void currentPresetChanged(); public Q_SLOTS: void activatePreset(int index); void resourceChanged(int key, const QVariant& v); private: class Private; Private* d; }; #endif // PRESETMODEL_H diff --git a/libs/libqml/plugins/kritasketchplugin/models/RecentImagesModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/RecentImagesModel.cpp index 1cbfcf3834..2911273c20 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/RecentImagesModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/RecentImagesModel.cpp @@ -1,149 +1,154 @@ /* This file is part of the KDE project * Copyright (C) 2012 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 "RecentImagesModel.h" #include "RecentFileManager.h" #include #include #include class RecentImagesModel::Private { public: RecentFileManager *recentFileManager; }; RecentImagesModel::RecentImagesModel(QObject *parent) : QAbstractListModel(parent) , d(new Private()) { d->recentFileManager = 0; +} + +RecentImagesModel::~RecentImagesModel() +{ + delete d; +} +QHash RecentImagesModel::roleNames() const +{ QHash roles; roles[ImageRole] = "image"; roles[TextRole] = "text"; roles[UrlRole] = "url"; roles[NameRole] = "name"; roles[DateRole] = "filedate"; - setRoleNames(roles); -} -RecentImagesModel::~RecentImagesModel() -{ - delete d; + return roles; } int RecentImagesModel::rowCount(const QModelIndex &/*parent*/) const { if (d->recentFileManager) return d->recentFileManager->size(); else return 0; } QVariant RecentImagesModel::data(const QModelIndex &index, int role) const { QVariant result; if (!d->recentFileManager) return result; if (index.isValid()) { Q_ASSERT(index.row() < d->recentFileManager->size()); QString key = d->recentFileManager->recentFileName(index.row()); QString value = d->recentFileManager->recentFile(index.row()); switch(role) { case ImageRole: result = QString("image://recentimage/%1").arg(value); break; case TextRole: result = QFileInfo(value).completeBaseName(); break; case UrlRole: result = value; break; case NameRole: result = key; case DateRole: { QFile f(value); if (f.exists()) { QFileInfo fi(value); result = fi.lastModified().toString("dd-mm-yyyy (hh:mm)"); } } default: result = ""; break; } } return result; } QVariant RecentImagesModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(orientation); QVariant result; if (section == 0) { switch(role) { case ImageRole: result = QString("Thumbnail"); break; case TextRole: result = QString("Name"); break; case UrlRole: case NameRole: case DateRole: default: result = ""; break; } } return result; } QObject *RecentImagesModel::recentFileManager() const { return d->recentFileManager; } void RecentImagesModel::setRecentFileManager(QObject *recentFileManager) { disconnect(d->recentFileManager); d->recentFileManager = qobject_cast(recentFileManager); connect(d->recentFileManager, SIGNAL(recentFilesListChanged()), SLOT(recentFilesListChanged())); emit recentFileManagerChanged(); } void RecentImagesModel::recentFilesListChanged() { - reset(); + beginResetModel(); + endResetModel(); } void RecentImagesModel::addRecent(const QString &_url) { if (d->recentFileManager) d->recentFileManager->addRecent(_url); } diff --git a/libs/libqml/plugins/kritasketchplugin/models/RecentImagesModel.h b/libs/libqml/plugins/kritasketchplugin/models/RecentImagesModel.h index 3ac80f2271..92e65f47dc 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/RecentImagesModel.h +++ b/libs/libqml/plugins/kritasketchplugin/models/RecentImagesModel.h @@ -1,62 +1,62 @@ /* This file is part of the KDE project * Copyright (C) 2012 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef RECENTIMAGESMODEL_H #define RECENTIMAGESMODEL_H #include class RecentImagesModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QObject* recentFileManager READ recentFileManager WRITE setRecentFileManager NOTIFY recentFileManagerChanged) public: enum PresetRoles { ImageRole = Qt::UserRole + 1, TextRole, UrlRole, NameRole, DateRole }; explicit RecentImagesModel(QObject *parent = 0); virtual ~RecentImagesModel(); - + QHash roleNames() const; virtual int rowCount(const QModelIndex &parent) const; virtual QVariant data(const QModelIndex &index, int role) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; QObject* recentFileManager() const; void setRecentFileManager(QObject* recentFileManager); Q_SIGNALS: void recentFileManagerChanged(); public Q_SLOTS: void addRecent(const QString &fileName); private: class Private; Private* d; private Q_SLOTS: void recentFilesListChanged(); }; #endif // RECENTIMAGESMODEL_H diff --git a/libs/libqml/plugins/kritasketchplugin/models/TemplatesModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/TemplatesModel.cpp index 1290f766ec..ddf1a28152 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/TemplatesModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/TemplatesModel.cpp @@ -1,180 +1,183 @@ /* * This file is part of the KDE project * Copyright (C) 2014 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "TemplatesModel.h" #include #include #include #include #include struct TemplatesModel::ItemData { public: ItemData() : favourite(false) , groupFolded(false) {}; QString name; QString description; QString file; QString icon; QString groupName; bool favourite; bool groupFolded; }; class TemplatesModel::Private { public: Private() {} ~Private() { qDeleteAll(items);} QList items; }; TemplatesModel::TemplatesModel(QObject* parent) : QAbstractListModel(parent) , d(new Private) { - QHash roleNames; - roleNames[NameRole] = "name"; - roleNames[DescriptionRole] = "description"; - roleNames[FileRole] = "file"; - roleNames[IconRole] = "icon"; - roleNames[GroupName] = "groupName"; - roleNames[GroupFolded] = "groupFolded"; - setRoleNames(roleNames); - - // Prefill a couple of + // Prefill a couple of ItemData* customItem = new ItemData(); customItem->name = "Custom Image"; customItem->file = "custom"; customItem->icon = "filenew-black"; d->items << customItem; ItemData* clipItem = new ItemData(); clipItem->name = "From Clipboard"; clipItem->file = "clip"; clipItem->icon = "fileclip-black"; d->items << clipItem; ItemData* screenItem = new ItemData(); screenItem->name = "Blank Image (Screen Size)"; screenItem->file = "screen"; screenItem->icon = "filenew-black"; d->items << screenItem; ItemData* a4pItem = new ItemData(); a4pItem->name = "Blank Image (A4 Portrait)"; a4pItem->file = "a4p"; a4pItem->icon = "A4portrait-black"; d->items << a4pItem; ItemData* a4lItem = new ItemData(); a4lItem->name = "Blank Image (A4 Landscape)"; a4lItem->file = "a4l"; a4lItem->icon = "A4landscape-black"; d->items << a4lItem; populate(); } TemplatesModel::~TemplatesModel() { delete d; } +QHash TemplatesModel::roleNames() const +{ + QHash roleNames; + roleNames[NameRole] = "name"; + roleNames[DescriptionRole] = "description"; + roleNames[FileRole] = "file"; + roleNames[IconRole] = "icon"; + roleNames[GroupName] = "groupName"; + roleNames[GroupFolded] = "groupFolded"; + return roleNames; +} + QVariant TemplatesModel::data(const QModelIndex& index, int role) const { QVariant data; if(index.isValid() && index.row() > -1 && index.row() < d->items.count()) { ItemData* item = d->items[index.row()]; switch(role) { case NameRole: data = item->name; break; case DescriptionRole: data = item->description; break; case FileRole: data = item->file; break; case IconRole: data = item->icon; break; case GroupName: data = item->groupName; break; case GroupFolded: data = item->groupFolded; break; default: break; } } return data; } int TemplatesModel::rowCount(const QModelIndex& parent) const { if(parent.isValid()) return 0; return d->items.count(); } QString TemplatesModel::groupNameOf(int index) const { if(index > 0 && index < d->items.count()) return d->items[index]->groupName; return QString(); } void TemplatesModel::toggleGroup(const QString& name) { Q_FOREACH (ItemData* item, d->items) { if(item->groupName == name) item->groupFolded = !item->groupFolded; } dataChanged(index(0), index(d->items.count() - 1)); } void TemplatesModel::populate() { KisTemplateTree templateTree( QStringLiteral("templates/"), true); Q_FOREACH (KisTemplateGroup *group, templateTree.groups()) { if (group->isHidden()) { continue; } Q_FOREACH (KisTemplate* t, group->templates()) { if (t->isHidden()) continue; ItemData* item = new ItemData(); item->name = t->name(); item->description = t->description(); item->file = QString("template://").append(t->file()); // QT5TODO: support custom pictures and icon symbol ids again // item->icon = KIconLoader::global()->iconPath(t->picture(), KIconLoader::Desktop); item->icon = "filenew-black"; item->groupName = group->name(); item->groupFolded = true; // default hide groups d->items << item; } } } diff --git a/libs/libqml/plugins/kritasketchplugin/models/TemplatesModel.h b/libs/libqml/plugins/kritasketchplugin/models/TemplatesModel.h index 09277a595e..ed3742ac02 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/TemplatesModel.h +++ b/libs/libqml/plugins/kritasketchplugin/models/TemplatesModel.h @@ -1,55 +1,56 @@ /* * This file is part of the KDE project * Copyright (C) 2014 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef TEMPLATESMODEL_H #define TEMPLATESMODEL_H #include class TemplatesModel : public QAbstractListModel { Q_OBJECT public: enum TemplateRoles { NameRole = Qt::UserRole + 1, DescriptionRole, FileRole, IconRole, GroupName, GroupFolded }; explicit TemplatesModel(QObject* parent = 0); virtual ~TemplatesModel(); + QHash roleNames() const; virtual QVariant data(const QModelIndex& index, int role) const; virtual int rowCount(const QModelIndex& parent) const; Q_INVOKABLE QString groupNameOf(int index) const; Q_INVOKABLE void toggleGroup(const QString& name); Q_SLOT void populate(); private: struct ItemData; class Private; Private* d; }; #endif // TEMPLATESMODEL_H diff --git a/libs/pigment/KoColorSpace.cpp b/libs/pigment/KoColorSpace.cpp index c240a0fb05..fce965ed3f 100644 --- a/libs/pigment/KoColorSpace.cpp +++ b/libs/pigment/KoColorSpace.cpp @@ -1,819 +1,820 @@ /* * Copyright (c) 2005 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoColorSpace.h" #include "KoColorSpace_p.h" #include "KoChannelInfo.h" #include "DebugPigment.h" #include "KoCompositeOp.h" #include "KoColorTransformation.h" #include "KoColorTransformationFactory.h" #include "KoColorTransformationFactoryRegistry.h" #include "KoColorConversionCache.h" #include "KoColorConversionSystem.h" #include "KoColorSpaceRegistry.h" #include "KoColorProfile.h" #include "KoCopyColorConversionTransformation.h" #include "KoFallBackColorTransformation.h" #include "KoUniqueNumberForIdServer.h" #include "KoMixColorsOp.h" #include "KoConvolutionOp.h" #include "KoCompositeOpRegistry.h" #include "KoColorSpaceEngine.h" #include #include #include #include #include #include KoColorSpace::KoColorSpace() : d(new Private()) { } KoColorSpace::KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp) : d(new Private()) { d->id = id; d->idNumber = KoUniqueNumberForIdServer::instance()->numberForId(d->id); d->name = name; d->mixColorsOp = mixColorsOp; d->convolutionOp = convolutionOp; d->transfoToRGBA16 = 0; d->transfoFromRGBA16 = 0; d->transfoToLABA16 = 0; d->transfoFromLABA16 = 0; d->gamutXYY = QPolygonF(); d->TRCXYY = QPolygonF(); d->colorants = QVector (0); d->lumaCoefficients = QVector (0); d->iccEngine = 0; d->deletability = NotOwnedByRegistry; } KoColorSpace::~KoColorSpace() { Q_ASSERT(d->deletability != OwnedByRegistryDoNotDelete); qDeleteAll(d->compositeOps); Q_FOREACH (KoChannelInfo * channel, d->channels) { delete channel; } if (d->deletability == NotOwnedByRegistry) { KoColorConversionCache* cache = KoColorSpaceRegistry::instance()->colorConversionCache(); if (cache) { cache->colorSpaceIsDestroyed(this); } } delete d->mixColorsOp; delete d->convolutionOp; delete d->transfoToRGBA16; delete d->transfoFromRGBA16; delete d->transfoToLABA16; delete d->transfoFromLABA16; delete d; } bool KoColorSpace::operator==(const KoColorSpace& rhs) const { const KoColorProfile* p1 = rhs.profile(); const KoColorProfile* p2 = profile(); return d->idNumber == rhs.d->idNumber && ((p1 == p2) || (*p1 == *p2)); } QString KoColorSpace::id() const { return d->id; } QString KoColorSpace::name() const { return d->name; } //Color space info stuff. QPolygonF KoColorSpace::gamutXYY() const { if (d->gamutXYY.empty()) { //now, let's decide on the boundary. This is a bit tricky because icc profiles can be both matrix-shaper and cLUT at once if the maker so pleases. //first make a list of colors. qreal max = 1.0; if ((colorModelId().id()=="CMYKA" || colorModelId().id()=="LABA") && colorDepthId().id()=="F32") { //boundaries for cmyka/laba have trouble getting the max values for Float, and are pretty awkward in general. max = this->channels()[0]->getUIMax(); } int samples = 5;//amount of samples in our color space. const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F32"); quint8 *data = new quint8[pixelSize()]; quint8 data2[16]; // xyza f32 is 4 floats, that is 16 bytes per pixel. //QVector sampleCoordinates(pow(colorChannelCount(),samples)); //sampleCoordinates.fill(0.0); // This is fixed to 5 since the maximum number of channels are 5 for CMYKA QVector channelValuesF(5);//for getting the coordinates. for(int x=0;xnormalisedChannelsValue(data2, channelValuesF); qreal x = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); qreal y = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->gamutXYY << QPointF(x,y); } else { for(int y=0;ynormalisedChannelsValue(data2, channelValuesF); qreal x = channelValuesF[0] / (channelValuesF[0] + channelValuesF[1] + channelValuesF[2]); qreal y = channelValuesF[1] / (channelValuesF[0] + channelValuesF[1] + channelValuesF[2]); d->gamutXYY<< QPointF(x,y); } } else { channelValuesF[0]=(max/(samples-1))*(x); channelValuesF[1]=(max/(samples-1))*(y); channelValuesF[2]=(max/(samples-1))*(z); channelValuesF[3]=max; if (colorModelId().id()!="XYZA") { //no need for conversion when using xyz. fromNormalisedChannelsValue(data, channelValuesF); convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags()); xyzColorSpace->normalisedChannelsValue(data2,channelValuesF); } qreal x = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); qreal y = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->gamutXYY<< QPointF(x,y); } } } } } delete[] data; //if we ever implement a boundary-checking thing I'd add it here. return d->gamutXYY; } else { return d->gamutXYY; } } QPolygonF KoColorSpace::estimatedTRCXYY() const { if (d->TRCXYY.empty()){ qreal max = 1.0; if ((colorModelId().id()=="CMYKA" || colorModelId().id()=="LABA") && colorDepthId().id()=="F32") { //boundaries for cmyka/laba have trouble getting the max values for Float, and are pretty awkward in general. max = this->channels()[0]->getUIMax(); } const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F32"); quint8 *data = new quint8[pixelSize()]; - quint8 data2[xyzColorSpace->pixelSize()]; + quint8 *data2 = new quint8[xyzColorSpace->pixelSize()]; // This is fixed to 5 since the maximum number of channels are 5 for CMYKA QVector channelValuesF(5);//for getting the coordinates. for (quint32 i=0; i0; j--){ channelValuesF.fill(0.0); channelValuesF[i] = ((max/4)*(5-j)); if (colorModelId().id()!="XYZA") { //no need for conversion when using xyz. fromNormalisedChannelsValue(data, channelValuesF); convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags()); xyzColorSpace->normalisedChannelsValue(data2,channelValuesF); } if (j==0) { colorantY = channelValuesF[1]; if (d->colorants.size()<2){ d->colorants.resize(3*colorChannelCount()); d->colorants[i] = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[i+1]= channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[i+2]= channelValuesF[1]; } } d->TRCXYY << QPointF(channelValuesF[1]/colorantY, ((1.0/4)*(5-j))); } } else { for (int j=0; j<5; j++){ channelValuesF.fill(0.0); channelValuesF[i] = ((max/4)*(j)); fromNormalisedChannelsValue(data, channelValuesF); convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags()); xyzColorSpace->normalisedChannelsValue(data2,channelValuesF); if (j==0) { colorantY = channelValuesF[1]; if (d->colorants.size()<2){ d->colorants.resize(3*colorChannelCount()); d->colorants[i] = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[i+1]= channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[i+2]= channelValuesF[1]; } } d->TRCXYY << QPointF(channelValuesF[1]/colorantY, ((1.0/4)*(j))); } } } delete[] data; + delete[] data2; return d->TRCXYY; } else { return d->TRCXYY; } } QVector KoColorSpace::colorants() const { if (d->colorants.size()>1){ return d->colorants; } else if (profile() && profile()->hasColorants()) { d->colorants.resize(3*colorChannelCount()); d->colorants = profile()->getColorantsxyY(); return d->colorants; } else { estimatedTRCXYY(); return d->colorants; } } QVector KoColorSpace::lumaCoefficients() const { if (d->lumaCoefficients.size()>1){ return d->lumaCoefficients; } else { d->lumaCoefficients.resize(3); if (colorModelId().id()!="RGBA") { d->lumaCoefficients.fill(0.33); } else { colorants(); if (d->colorants[2]<0 || d->colorants[5]<0 || d->colorants[8]<0) { d->lumaCoefficients[0]=0.2126; d->lumaCoefficients[1]=0.7152; d->lumaCoefficients[2]=0.0722; } else { d->lumaCoefficients[0]=d->colorants[2]; d->lumaCoefficients[1]=d->colorants[5]; d->lumaCoefficients[2]=d->colorants[8]; } } return d->lumaCoefficients; } } QList KoColorSpace::channels() const { return d->channels; } QBitArray KoColorSpace::channelFlags(bool color, bool alpha) const { QBitArray ba(d->channels.size()); if (!color && !alpha) return ba; for (int i = 0; i < d->channels.size(); ++i) { KoChannelInfo * channel = d->channels.at(i); if ((color && channel->channelType() == KoChannelInfo::COLOR) || (alpha && channel->channelType() == KoChannelInfo::ALPHA)) ba.setBit(i, true); } return ba; } void KoColorSpace::addChannel(KoChannelInfo * ci) { d->channels.push_back(ci); } bool KoColorSpace::hasCompositeOp(const QString& id) const { return d->compositeOps.contains(id); } QList KoColorSpace::compositeOps() const { return d->compositeOps.values(); } KoMixColorsOp* KoColorSpace::mixColorsOp() const { return d->mixColorsOp; } KoConvolutionOp* KoColorSpace::convolutionOp() const { return d->convolutionOp; } const KoCompositeOp * KoColorSpace::compositeOp(const QString & id) const { const QHash::ConstIterator it = d->compositeOps.constFind(id); if (it != d->compositeOps.constEnd()) { return it.value(); } else { warnPigment << "Asking for non-existent composite operation " << id << ", returning " << COMPOSITE_OVER; return d->compositeOps.value(COMPOSITE_OVER); } } void KoColorSpace::addCompositeOp(const KoCompositeOp * op) { if (op->colorSpace()->id() == id()) { d->compositeOps.insert(op->id(), const_cast(op)); } } const KoColorConversionTransformation* KoColorSpace::toLabA16Converter() const { if (!d->transfoToLABA16) { d->transfoToLABA16 = KoColorSpaceRegistry::instance()->createColorConverter(this, KoColorSpaceRegistry::instance()->lab16(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoToLABA16; } const KoColorConversionTransformation* KoColorSpace::fromLabA16Converter() const { if (!d->transfoFromLABA16) { d->transfoFromLABA16 = KoColorSpaceRegistry::instance()->createColorConverter(KoColorSpaceRegistry::instance()->lab16(), this, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoFromLABA16; } const KoColorConversionTransformation* KoColorSpace::toRgbA16Converter() const { if (!d->transfoToRGBA16) { d->transfoToRGBA16 = KoColorSpaceRegistry::instance()->createColorConverter(this, KoColorSpaceRegistry::instance()->rgb16(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoToRGBA16; } const KoColorConversionTransformation* KoColorSpace::fromRgbA16Converter() const { if (!d->transfoFromRGBA16) { d->transfoFromRGBA16 = KoColorSpaceRegistry::instance()->createColorConverter(KoColorSpaceRegistry::instance()->rgb16() , this, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoFromRGBA16; } void KoColorSpace::toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { toLabA16Converter()->transform(src, dst, nPixels); } void KoColorSpace::fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { fromLabA16Converter()->transform(src, dst, nPixels); } void KoColorSpace::toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { toRgbA16Converter()->transform(src, dst, nPixels); } void KoColorSpace::fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { fromRgbA16Converter()->transform(src, dst, nPixels); } KoColorConversionTransformation* KoColorSpace::createColorConverter(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { if (*this == *dstColorSpace) { return new KoCopyColorConversionTransformation(this); } else { return KoColorSpaceRegistry::instance()->createColorConverter(this, dstColorSpace, renderingIntent, conversionFlags); } } bool KoColorSpace::convertPixelsTo(const quint8 * src, quint8 * dst, const KoColorSpace * dstColorSpace, quint32 numPixels, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { if (*this == *dstColorSpace) { if (src != dst) { memcpy(dst, src, numPixels * sizeof(quint8) * pixelSize()); } } else { KoCachedColorConversionTransformation cct = KoColorSpaceRegistry::instance()->colorConversionCache()->cachedConverter(this, dstColorSpace, renderingIntent, conversionFlags); cct.transformation()->transform(src, dst, numPixels); } return true; } KoColorConversionTransformation * KoColorSpace::createProofingTransform(const KoColorSpace *dstColorSpace, const KoColorSpace *proofingSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::Intent proofingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, quint8 *gamutWarning, double adaptationState) const { if (!d->iccEngine) { d->iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); } if (!d->iccEngine) return 0; return d->iccEngine->createColorProofingTransformation(this, dstColorSpace, proofingSpace, renderingIntent, proofingIntent, conversionFlags, gamutWarning, adaptationState); } bool KoColorSpace::proofPixelsTo(const quint8 *src, quint8 *dst, quint32 numPixels, KoColorConversionTransformation *proofingTransform) const { proofingTransform->transform(src, dst, numPixels); //the transform is deleted in the destructor. return true; } void KoColorSpace::bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { Q_ASSERT_X(*op->colorSpace() == *this, "KoColorSpace::bitBlt", QString("Composite op is for color space %1 (%2) while this is %3 (%4)").arg(op->colorSpace()->id()).arg(op->colorSpace()->profile()->name()).arg(id()).arg(profile()->name()).toLatin1()); if(params.rows <= 0 || params.cols <= 0) return; if(!(*this == *srcSpace)) { if (preferCompositionInSourceColorSpace() && srcSpace->hasCompositeOp(op->id())) { quint32 conversionDstBufferStride = params.cols * srcSpace->pixelSize(); QVector * conversionDstCache = threadLocalConversionCache(params.rows * conversionDstBufferStride); quint8* conversionDstData = conversionDstCache->data(); for(qint32 row=0; rowcompositeOp(op->id()); KoCompositeOp::ParameterInfo paramInfo(params); paramInfo.dstRowStart = conversionDstData; paramInfo.dstRowStride = conversionDstBufferStride; otherOp->composite(paramInfo); for(qint32 row=0; rowconvertPixelsTo(conversionDstData + row * conversionDstBufferStride, params.dstRowStart + row * params.dstRowStride, this, params.cols, renderingIntent, conversionFlags); } } else { quint32 conversionBufferStride = params.cols * pixelSize(); QVector * conversionCache = threadLocalConversionCache(params.rows * conversionBufferStride); quint8* conversionData = conversionCache->data(); for(qint32 row=0; rowconvertPixelsTo(params.srcRowStart + row * params.srcRowStride, conversionData + row * conversionBufferStride, this, params.cols, renderingIntent, conversionFlags); } KoCompositeOp::ParameterInfo paramInfo(params); paramInfo.srcRowStart = conversionData; paramInfo.srcRowStride = conversionBufferStride; op->composite(paramInfo); } } else { op->composite(params); } } QVector * KoColorSpace::threadLocalConversionCache(quint32 size) const { QVector * ba = 0; if (!d->conversionCache.hasLocalData()) { ba = new QVector(size, '0'); d->conversionCache.setLocalData(ba); } else { ba = d->conversionCache.localData(); if ((quint8)ba->size() < size) ba->resize(size); } return ba; } KoColorTransformation* KoColorSpace::createColorTransformation(const QString & id, const QHash & parameters) const { KoColorTransformationFactory* factory = KoColorTransformationFactoryRegistry::instance()->get(id); if (!factory) return 0; QPair model(colorModelId(), colorDepthId()); QList< QPair > models = factory->supportedModels(); if (models.isEmpty() || models.contains(model)) { return factory->createTransformation(this, parameters); } else { // Find the best solution // TODO use the color conversion cache KoColorConversionTransformation* csToFallBack = 0; KoColorConversionTransformation* fallBackToCs = 0; KoColorSpaceRegistry::instance()->createColorConverters(this, models, csToFallBack, fallBackToCs); Q_ASSERT(csToFallBack); Q_ASSERT(fallBackToCs); KoColorTransformation* transfo = factory->createTransformation(fallBackToCs->srcColorSpace(), parameters); return new KoFallBackColorTransformation(csToFallBack, fallBackToCs, transfo); } } void KoColorSpace::increaseLuminosity(quint8 * pixel, qreal step) const{ int channelnumber = channelCount(); QVector channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ihasTRC()){ //only linearise and crunch the luma if there's a TRC profile()->linearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); luma = pow(luma, 1/2.2); luma = qMin(1.0, luma + step); luma = pow(luma, 2.2); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); } else { qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); luma = qMin(1.0, luma + step); channelValues = fromHSY(&hue, &sat, &luma); } for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ihasTRC()){ //only linearise and crunch the luma if there's a TRC profile()->linearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); luma = pow(luma, 1/2.2); if (luma-step<0.0) { luma=0.0; } else { luma -= step; } luma = pow(luma, 2.2); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); } else { qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); if (luma-step<0.0) { luma=0.0; } else { luma -= step; } channelValues = fromHSY(&hue, &sat, &luma); } for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); sat += step; sat = qBound(0.0, sat, 1.0); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); sat -= step; sat = qBound(0.0, sat, 1.0); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); if (hue+step>1.0){ hue=(hue+step)- 1.0; } else { hue += step; } channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); if (hue-step<0.0){ hue=1.0-(step-hue); } else { hue -= step; } channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); u += step; u = qBound(0.0, u, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); u -= step; u = qBound(0.0, u, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); v += step; v = qBound(0.0, v, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); v -= step; v = qBound(0.0, v, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;irgb8(dstProfile); if (data) this->convertPixelsTo(const_cast(data), img.bits(), dstCS, width * height, renderingIntent, conversionFlags); return img; } bool KoColorSpace::preferCompositionInSourceColorSpace() const { return false; } diff --git a/libs/psd/asl/kis_asl_reader_utils.h b/libs/psd/asl/kis_asl_reader_utils.h index 7dfc2ef2a8..469333f0a1 100644 --- a/libs/psd/asl/kis_asl_reader_utils.h +++ b/libs/psd/asl/kis_asl_reader_utils.h @@ -1,122 +1,122 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_ASL_READER_UTILS_H #define __KIS_ASL_READER_UTILS_H #include "psd_utils.h" #include #include #include /** * Default value for variable read from a file */ #define GARBAGE_VALUE_MARK 999 namespace KisAslReaderUtils { /** * Exception that is emitted when any parse error appear. * Thanks to KisOffsetOnExitVerifier parsing can be continued * most of the time, based on the offset values written in PSD. */ struct KRITAPSD_EXPORT ASLParseException : public std::runtime_error { ASLParseException(const QString &msg) - : std::runtime_error(msg.toAscii().data()) + : std::runtime_error(msg.toLatin1().data()) { } }; } #define SAFE_READ_EX(device, varname) \ if (!psdread(device, &varname)) { \ QString msg = QString("Failed to read \'%1\' tag!").arg(#varname); \ throw KisAslReaderUtils::ASLParseException(msg); \ } #define SAFE_READ_SIGNATURE_EX(device, varname, expected) \ if (!psdread(device, &varname) || varname != expected) { \ QString msg = QString("Failed to check signature \'%1\' tag!\n" \ "Value: \'%2\' Expected: \'%3\'") \ .arg(#varname).arg(varname).arg(expected); \ throw KisAslReaderUtils::ASLParseException(msg); \ } #define SAFE_READ_SIGNATURE_2OPS_EX(device, varname, expected1, expected2) \ if (!psdread(device, &varname) || (varname != expected1 && varname != expected2)) { \ QString msg = QString("Failed to check signature \'%1\' tag!\n" \ "Value: \'%2\' Expected1: \'%3\' Expected2: \'%4\'") \ .arg(#varname).arg(varname).arg(expected1).arg(expected2); \ throw KisAslReaderUtils::ASLParseException(msg); \ } template inline bool TRY_READ_SIGNATURE_2OPS_EX(QIODevice *device, T expected1, T expected2) { T var; qint64 bytesRead = device->peek((char*)&var, sizeof(T)); if (bytesRead != sizeof(T)) { return false; } var = qFromBigEndian(var); bool result = var == expected1 || var == expected2; // If read successfully, adjust current position of the io device if (result) { // read, not seek, to support sequential devices bytesRead = device->read((char*)&var, sizeof(T)); if (bytesRead != sizeof(T)) { return false; } } return result; } namespace KisAslReaderUtils { /** * String fetch functions * * ASL has 4 types of strings: * * - fixed length (4 bytes) * - variable length (length (4 bytes) + string (var)) * - pascal (length (1 byte) + string (var)) * - unicode string (length (4 bytes) + null-terminated unicode string (var) */ KRITAPSD_EXPORT QString readFixedString(QIODevice *device); KRITAPSD_EXPORT QString readVarString(QIODevice *device); KRITAPSD_EXPORT QString readPascalString(QIODevice *device); KRITAPSD_EXPORT QString readUnicodeString(QIODevice *device); } #endif /* __KIS_ASL_READER_UTILS_H */ diff --git a/libs/psd/asl/kis_asl_writer.cpp b/libs/psd/asl/kis_asl_writer.cpp index e3c29b7a26..7a4052e2a3 100644 --- a/libs/psd/asl/kis_asl_writer.cpp +++ b/libs/psd/asl/kis_asl_writer.cpp @@ -1,278 +1,278 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_asl_writer.h" #include #include #include "kis_dom_utils.h" #include "kis_debug.h" #include "psd_utils.h" #include "kis_asl_patterns_writer.h" #include "kis_asl_writer_utils.h" namespace Private { using namespace KisAslWriterUtils; void parseElement(const QDomElement &el, QIODevice *device, bool forceTypeInfo = false) { KIS_ASSERT_RECOVER_RETURN(el.tagName() == "node"); QString type = el.attribute("type", ""); QString key = el.attribute("key", ""); // should be filtered on a heigher level KIS_ASSERT_RECOVER_RETURN(key != "Patterns"); if (type == "Descriptor") { if (!key.isEmpty()) { writeVarString(key, device); } if (!key.isEmpty() || forceTypeInfo) { writeFixedString("Objc", device); } QString classId = el.attribute("classId", ""); QString name = el.attribute("name", ""); writeUnicodeString(name, device); writeVarString(classId, device); quint32 numChildren = el.childNodes().size(); SAFE_WRITE_EX(device, numChildren); QDomNode child = el.firstChild(); while (!child.isNull()) { parseElement(child.toElement(), device); child = child.nextSibling(); } } else if (type == "List") { writeVarString(key, device); writeFixedString("VlLs", device); quint32 numChildren = el.childNodes().size(); SAFE_WRITE_EX(device, numChildren); QDomNode child = el.firstChild(); while (!child.isNull()) { parseElement(child.toElement(), device, true); child = child.nextSibling(); } } else if (type == "Double") { double v = KisDomUtils::toDouble(el.attribute("value", "0")); writeVarString(key, device); writeFixedString("doub", device); SAFE_WRITE_EX(device, v); } else if (type == "UnitFloat") { double v = KisDomUtils::toDouble(el.attribute("value", "0")); QString unit = el.attribute("unit", "#Pxl"); writeVarString(key, device); writeFixedString("UntF", device); writeFixedString(unit, device); SAFE_WRITE_EX(device, v); } else if (type == "Text") { QString v = el.attribute("value", ""); writeVarString(key, device); writeFixedString("TEXT", device); writeUnicodeString(v, device); } else if (type == "Enum") { QString v = el.attribute("value", ""); QString typeId = el.attribute("typeId", "DEAD"); writeVarString(key, device); writeFixedString("enum", device); writeVarString(typeId, device); writeVarString(v, device); } else if (type == "Integer") { quint32 v = KisDomUtils::toInt(el.attribute("value", "0")); writeVarString(key, device); writeFixedString("long", device); SAFE_WRITE_EX(device, v); } else if (type == "Boolean") { quint8 v = KisDomUtils::toInt(el.attribute("value", "0")); writeVarString(key, device); writeFixedString("bool", device); SAFE_WRITE_EX(device, v); } else { warnKrita << "WARNING: XML (ASL) Unknown element type:" << type << ppVar(key); } } int calculateNumStyles(const QDomElement &root) { int numStyles = 0; QDomNode child = root.firstChild(); while (!child.isNull()) { QDomElement el = child.toElement(); QString classId = el.attribute("classId", ""); if (classId == "null") { numStyles++; } child = child.nextSibling(); } return numStyles; } void writeFileImpl(QIODevice *device, const QDomDocument &doc) { { quint16 stylesVersion = 2; SAFE_WRITE_EX(device, stylesVersion); } { QString signature("8BSL"); - if (!device->write(signature.toAscii().data(), 4)) { + if (!device->write(signature.toLatin1().data(), 4)) { throw ASLWriteException("Failed to write ASL signature"); } } { quint16 patternsVersion = 3; SAFE_WRITE_EX(device, patternsVersion); } { KisAslWriterUtils::OffsetStreamPusher patternsSizeField(device); KisAslPatternsWriter patternsWriter(doc, device); patternsWriter.writePatterns(); } QDomElement root = doc.documentElement(); KIS_ASSERT_RECOVER_RETURN(root.tagName() == "asl"); int numStyles = calculateNumStyles(root); KIS_ASSERT_RECOVER_RETURN(numStyles > 0); { quint32 numStylesTag = numStyles; SAFE_WRITE_EX(device, numStylesTag); } QDomNode child = root.firstChild(); for (int styleIndex = 0; styleIndex < numStyles; styleIndex++) { KisAslWriterUtils::OffsetStreamPusher theOnlyStyleSizeField(device); KIS_ASSERT_RECOVER_RETURN(!child.isNull()); { quint32 stylesFormatVersion = 16; SAFE_WRITE_EX(device, stylesFormatVersion); } while (!child.isNull()) { QDomElement el = child.toElement(); QString key = el.attribute("key", ""); if (key != "Patterns") break; child = child.nextSibling(); } parseElement(child.toElement(), device); child = child.nextSibling(); { quint32 stylesFormatVersion = 16; SAFE_WRITE_EX(device, stylesFormatVersion); } parseElement(child.toElement(), device); child = child.nextSibling(); // ASL files' size should be 4-bytes aligned const qint64 paddingSize = 4 - (device->pos() & 0x3); if (paddingSize != 4) { QByteArray padding(paddingSize, '\0'); device->write(padding); } } } void writePsdLfx2SectionImpl(QIODevice *device, const QDomDocument &doc) { QDomElement root = doc.documentElement(); KIS_ASSERT_RECOVER_RETURN(root.tagName() == "asl"); int numStyles = calculateNumStyles(root); KIS_ASSERT_RECOVER_RETURN(numStyles == 1); { quint32 objectEffectsVersion = 0; SAFE_WRITE_EX(device, objectEffectsVersion); } { quint32 descriptorVersion = 16; SAFE_WRITE_EX(device, descriptorVersion); } QDomNode child = root.firstChild(); while (!child.isNull()) { QDomElement el = child.toElement(); QString key = el.attribute("key", ""); if (key != "Patterns") break; child = child.nextSibling(); } parseElement(child.toElement(), device); child = child.nextSibling(); // ASL files' size should be 4-bytes aligned const qint64 paddingSize = 4 - (device->pos() & 0x3); if (paddingSize != 4) { QByteArray padding(paddingSize, '\0'); device->write(padding); } } } // namespace void KisAslWriter::writeFile(QIODevice *device, const QDomDocument &doc) { try { Private::writeFileImpl(device, doc); } catch (Private::ASLWriteException &e) { warnKrita << "WARNING: ASL:" << e.what(); } } void KisAslWriter::writePsdLfx2SectionEx(QIODevice *device, const QDomDocument &doc) { Private::writePsdLfx2SectionImpl(device, doc); } diff --git a/libs/psd/asl/kis_asl_writer_utils.cpp b/libs/psd/asl/kis_asl_writer_utils.cpp index e3f362ddf4..806314586f 100644 --- a/libs/psd/asl/kis_asl_writer_utils.cpp +++ b/libs/psd/asl/kis_asl_writer_utils.cpp @@ -1,111 +1,111 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_asl_writer_utils.h" #include #include namespace KisAslWriterUtils { void writeRect(const QRect &rect, QIODevice *device) { { const quint32 rectY0 = rect.y(); SAFE_WRITE_EX(device, rectY0); } { const quint32 rectX0 = rect.x(); SAFE_WRITE_EX(device, rectX0); } { const quint32 rectY1 = rect.y() + rect.height(); SAFE_WRITE_EX(device, rectY1); } { const quint32 rectX1 = rect.x() + rect.width(); SAFE_WRITE_EX(device, rectX1); } } void writeUnicodeString(const QString &value, QIODevice *device) { quint32 len = value.length() + 1; SAFE_WRITE_EX(device, len); const quint16 *ptr = value.utf16(); for (quint32 i = 0; i < len; i++) { SAFE_WRITE_EX(device, ptr[i]); } } void writeVarString(const QString &value, QIODevice *device) { quint32 lenTag = value.length() != 4 ? value.length() : 0; SAFE_WRITE_EX(device, lenTag); - if (!device->write(value.toAscii().data(), value.length())) { + if (!device->write(value.toLatin1().data(), value.length())) { warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value); return; } } void writePascalString(const QString &value, QIODevice *device) { quint8 lenTag = value.length(); SAFE_WRITE_EX(device, lenTag); - if (!device->write(value.toAscii().data(), value.length())) { + if (!device->write(value.toLatin1().data(), value.length())) { warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value); return; } } void writeFixedString(const QString &value, QIODevice *device) { KIS_ASSERT_RECOVER_RETURN(value.length() == 4); - if (!device->write(value.toAscii().data(), value.length())) { + if (!device->write(value.toLatin1().data(), value.length())) { warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value); return; } } // Write UUID fetched from the file name or generate QString getPatternUuidLazy(const KoPattern *pattern) { QUuid uuid; QString patternFileName = pattern->filename(); if (patternFileName.endsWith(".pat", Qt::CaseInsensitive)) { QString strUuid = patternFileName.left(patternFileName.size() - 4); uuid = QUuid(strUuid); } if (uuid.isNull()) { warnKrita << "WARNING: Saved pattern doesn't have a UUID, generating..."; warnKrita << ppVar(patternFileName) << ppVar(pattern->name()); uuid = QUuid::createUuid(); } return uuid.toString().mid(1, 36); } } diff --git a/libs/psd/asl/kis_asl_writer_utils.h b/libs/psd/asl/kis_asl_writer_utils.h index 4053dd348a..bd1c68dab7 100644 --- a/libs/psd/asl/kis_asl_writer_utils.h +++ b/libs/psd/asl/kis_asl_writer_utils.h @@ -1,137 +1,137 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_ASL_WRITER_UTILS_H #define __KIS_ASL_WRITER_UTILS_H #include #include #include #include "psd_utils.h" #include "kis_debug.h" #include "kritapsd_export.h" namespace KisAslWriterUtils { /** * Exception that is emitted when any write error appear. */ struct KRITAPSD_EXPORT ASLWriteException : public std::runtime_error { ASLWriteException(const QString &msg) - : std::runtime_error(msg.toAscii().data()) + : std::runtime_error(msg.toLatin1().data()) { } }; } #define SAFE_WRITE_EX(device, varname) \ if (!psdwrite(device, varname)) { \ QString msg = QString("Failed to write \'%1\' tag!").arg(#varname); \ throw KisAslWriterUtils::ASLWriteException(msg); \ } namespace KisAslWriterUtils { KRITAPSD_EXPORT void writeRect(const QRect &rect, QIODevice *device); KRITAPSD_EXPORT void writeUnicodeString(const QString &value, QIODevice *device); KRITAPSD_EXPORT void writeVarString(const QString &value, QIODevice *device); KRITAPSD_EXPORT void writePascalString(const QString &value, QIODevice *device); KRITAPSD_EXPORT void writeFixedString(const QString &value, QIODevice *device); KRITAPSD_EXPORT QString getPatternUuidLazy(const KoPattern *pattern); /** * Align the pointer \p pos by alignment. Grow the pointer * if needed. * * \return the lowest integer not smaller than \p pos that divides by * alignment */ inline qint64 alignOffsetCeil(qint64 pos, qint64 alignment) { qint64 mask = alignment - 1; return (pos + mask) & ~mask; } template class OffsetStreamPusher { public: OffsetStreamPusher(QIODevice *device, qint64 alignOnExit = 0, qint64 externalSizeTagOffset = -1) : m_device(device), m_alignOnExit(alignOnExit), m_externalSizeTagOffset(externalSizeTagOffset) { m_chunkStartPos = m_device->pos(); if (externalSizeTagOffset < 0) { const OffsetType fakeObjectSize = OffsetType(0xdeadbeef); SAFE_WRITE_EX(m_device, fakeObjectSize); } } ~OffsetStreamPusher() { try { if (m_alignOnExit) { qint64 currentPos = m_device->pos(); const qint64 alignedPos = alignOffsetCeil(currentPos, m_alignOnExit); for (; currentPos < alignedPos; currentPos++) { quint8 padding = 0; SAFE_WRITE_EX(m_device, padding); } } const qint64 currentPos = m_device->pos(); qint64 writtenDataSize = 0; qint64 sizeFiledOffset = 0; if (m_externalSizeTagOffset >= 0) { writtenDataSize = currentPos - m_chunkStartPos; sizeFiledOffset = m_externalSizeTagOffset; } else { writtenDataSize = currentPos - m_chunkStartPos - sizeof(OffsetType); sizeFiledOffset = m_chunkStartPos; } m_device->seek(sizeFiledOffset); const OffsetType realObjectSize = writtenDataSize; SAFE_WRITE_EX(m_device, realObjectSize); m_device->seek(currentPos); } catch(ASLWriteException &e) { warnKrita << PREPEND_METHOD(e.what()); } } private: qint64 m_chunkStartPos; QIODevice *m_device; qint64 m_alignOnExit; qint64 m_externalSizeTagOffset; }; } #endif /* __KIS_ASL_WRITER_UTILS_H */ diff --git a/libs/psd/asl/kis_asl_xml_parser.cpp b/libs/psd/asl/kis_asl_xml_parser.cpp index 7f53c00827..dde244badb 100644 --- a/libs/psd/asl/kis_asl_xml_parser.cpp +++ b/libs/psd/asl/kis_asl_xml_parser.cpp @@ -1,556 +1,556 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_asl_xml_parser.h" #include #include #include #include #include #include #include #include #include #include #include "kis_dom_utils.h" #include "kis_debug.h" #include "psd_utils.h" #include "psd.h" #include "compression.h" #include "kis_asl_object_catcher.h" namespace Private { void parseElement(const QDomElement &el, const QString &parentPath, KisAslObjectCatcher &catcher); class CurveObjectCatcher : public KisAslObjectCatcher { public: void addText(const QString &path, const QString &value) override { if (path == "/Nm ") { m_name = value; } else { warnKrita << "XML (ASL): failed to parse curve object" << path << value; } } void addPoint(const QString &path, const QPointF &value) override { if (!m_arrayMode) { warnKrita << "XML (ASL): failed to parse curve object (array fault)" << path << value << ppVar(m_arrayMode); } m_points.append(value); } public: QVector m_points; QString m_name; }; QColor parseRGBColorObject(QDomElement parent) { QColor color(Qt::black); QDomNode child = parent.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type != "Double") { warnKrita << "Unknown color component type:" << ppVar(type) << ppVar(key); return Qt::red; } double value = KisDomUtils::toDouble(childEl.attribute("value", "0")); if (key == "Rd ") { color.setRed(value); } else if (key == "Grn ") { color.setGreen(value); } else if (key == "Bl ") { color.setBlue(value); } else { warnKrita << "Unknown color key value:" << ppVar(key); return Qt::red; } child = child.nextSibling(); } return color; } void parseColorStopsList(QDomElement parent, QVector &startLocations, QVector &middleOffsets, QVector &colors) { QDomNode child = parent.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); QString classId = childEl.attribute("classId", ""); if (type == "Descriptor" && classId == "Clrt") { // sorry for naming... QDomNode child = childEl.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); QString classId = childEl.attribute("classId", ""); if (type == "Integer" && key == "Lctn") { int value = KisDomUtils::toInt(childEl.attribute("value", "0")); startLocations.append(qreal(value) / 4096.0); } else if (type == "Integer" && key == "Mdpn") { int value = KisDomUtils::toInt(childEl.attribute("value", "0")); middleOffsets.append(qreal(value) / 100.0); } else if (type == "Descriptor" && key == "Clr ") { colors.append(parseRGBColorObject(childEl)); } else if (type == "Enum" && key == "Type") { QString typeId = childEl.attribute("typeId", ""); if (typeId != "Clry") { warnKrita << "WARNING: Invalid typeId of a greadient stop type" << typeId; } QString value = childEl.attribute("value", ""); if (value == "BckC" || value == "FrgC") { warnKrita << "WARNING: Using foreground/background colors in ASL gradients is not yet supported"; } } child = child.nextSibling(); } } else { warnKrita << "WARNING: Unrecognized object in color stops list" << ppVar(type) << ppVar(key) << ppVar(classId); } child = child.nextSibling(); } } void parseTransparencyStopsList(QDomElement parent, QVector &startLocations, QVector &middleOffsets, QVector &transparencies) { QDomNode child = parent.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); QString classId = childEl.attribute("classId", ""); if (type == "Descriptor" && classId == "TrnS") { // sorry for naming again... QDomNode child = childEl.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Integer" && key == "Lctn") { int value = KisDomUtils::toInt(childEl.attribute("value", "0")); startLocations.append(qreal(value) / 4096.0); } else if (type == "Integer" && key == "Mdpn") { int value = KisDomUtils::toInt(childEl.attribute("value", "0")); middleOffsets.append(qreal(value) / 100.0); } else if (type == "UnitFloat" && key == "Opct") { QString unit = childEl.attribute("unit", ""); if (unit != "#Prc") { warnKrita << "WARNING: Invalid unit of a greadient stop transparency" << unit; } qreal value = KisDomUtils::toDouble(childEl.attribute("value", "100")); transparencies.append(value / 100.0); } child = child.nextSibling(); } } else { warnKrita << "WARNING: Unrecognized object in transparency stops list" << ppVar(type) << ppVar(key) << ppVar(classId); } child = child.nextSibling(); } } inline QString buildPath(const QString &parent, const QString &key) { return parent + "/" + key; } bool tryParseDescriptor(const QDomElement &el, const QString &path, const QString &classId, KisAslObjectCatcher &catcher) { bool retval = true; if (classId == "null") { catcher.newStyleStarted(); // here we just notify that a new style is started, we haven't // processed the whole block yet, so return false. retval = false; } else if (classId == "RGBC") { catcher.addColor(path, parseRGBColorObject(el)); } else if (classId == "ShpC") { CurveObjectCatcher curveCatcher; QDomNode child = el.firstChild(); while (!child.isNull()) { parseElement(child.toElement(), "", curveCatcher); child = child.nextSibling(); } catcher.addCurve(path, curveCatcher.m_name, curveCatcher.m_points); } else if (classId == "CrPt") { QPointF point; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Boolean" && key == "Cnty") { warnKrita << "WARNING: tryParseDescriptor: The points of the curve object contain \'Cnty\' flag which is unsupported by Krita"; warnKrita << " " << ppVar(type) << ppVar(key) << ppVar(path); child = child.nextSibling(); continue; } if (type != "Double") { warnKrita << "Unknown point component type:" << ppVar(type) << ppVar(key) << ppVar(path); return false; } double value = KisDomUtils::toDouble(childEl.attribute("value", "0")); if (key == "Hrzn") { point.setX(value); } else if (key == "Vrtc") { point.setY(value); } else { warnKrita << "Unknown point key value:" << ppVar(key) << ppVar(path); return false; } child = child.nextSibling(); } catcher.addPoint(path, point); } else if (classId == "Pnt ") { QPointF point; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); QString unit = childEl.attribute("unit", ""); if (type != "Double" && !(type == "UnitFloat" && unit == "#Prc")) { warnKrita << "Unknown point component type:" << ppVar(unit) << ppVar(type) << ppVar(key) << ppVar(path); return false; } double value = KisDomUtils::toDouble(childEl.attribute("value", "0")); if (key == "Hrzn") { point.setX(value); } else if (key == "Vrtc") { point.setY(value); } else { warnKrita << "Unknown point key value:" << ppVar(key) << ppVar(path); return false; } child = child.nextSibling(); } catcher.addPoint(path, point); } else if (classId == "KisPattern") { QByteArray patternData; QString patternUuid; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Text" && key == "Idnt") { patternUuid = childEl.attribute("value", ""); } if (type == "KisPatternData" && key == "Data") { QDomNode dataNode = child.firstChild(); if (!dataNode.isCDATASection()) { warnKrita << "WARNING: failed to parse KisPatternData XML section!"; continue; } QDomCDATASection dataSection = dataNode.toCDATASection(); - QByteArray data = dataSection.data().toAscii(); + QByteArray data = dataSection.data().toLatin1(); data = QByteArray::fromBase64(data); data = qUncompress(data); if (data.isEmpty()) { warnKrita << "WARNING: failed to parse KisPatternData XML section!"; continue; } patternData = data; } child = child.nextSibling(); } if (!patternUuid.isEmpty() && !patternData.isEmpty()) { QString fileName = QString("%1.pat").arg(patternUuid); QScopedPointer pattern(new KoPattern(fileName)); QBuffer buffer(&patternData); buffer.open(QIODevice::ReadOnly); pattern->loadPatFromDevice(&buffer); catcher.addPattern(path, pattern.data()); } else { warnKrita << "WARNING: failed to load KisPattern XML section!" << ppVar(patternUuid); } } else if (classId == "Ptrn") { // reference to an existing pattern QString patternUuid; QString patternName; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Text" && key == "Idnt") { patternUuid = childEl.attribute("value", ""); } else if (type == "Text" && key == "Nm ") { patternName = childEl.attribute("value", ""); } else { warnKrita << "WARNING: unrecognized pattern-ref section key:" << ppVar(type) << ppVar(key); } child = child.nextSibling(); } catcher.addPatternRef(path, patternUuid, patternName); } else if (classId == "Grdn") { QString gradientName; qreal gradientSmoothness = 100.0; QVector startLocations; QVector middleOffsets; QVector colors; QVector transpStartLocations; QVector transpMiddleOffsets; QVector transparencies; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Text" && key == "Nm ") { gradientName = childEl.attribute("value", ""); } else if (type == "Enum" && key == "GrdF") { QString typeId = childEl.attribute("typeId", ""); QString value = childEl.attribute("value", ""); if (typeId != "GrdF" || value != "CstS") { warnKrita << "WARNING: Unsupported gradient type (porbably, noise-based):" << value; return true; } } else if (type == "Double" && key == "Intr") { double value = KisDomUtils::toDouble(childEl.attribute("value", "4096")); gradientSmoothness = 100.0 * value / 4096.0; } else if (type == "List" && key == "Clrs") { parseColorStopsList(childEl, startLocations, middleOffsets, colors); } else if (type == "List" && key == "Trns") { parseTransparencyStopsList(childEl, transpStartLocations, transpMiddleOffsets, transparencies); } child = child.nextSibling(); } if (colors.size() < 2) { warnKrita << "WARNING: ASL gradient has too few stops" << ppVar(colors.size()); } if (colors.size() != transparencies.size()) { warnKrita << "WARNING: ASL gradient has inconsistent number of transparency stops. Dropping transparency..." << ppVar(colors.size()) << ppVar(transparencies.size()); transparencies.resize(colors.size()); for (int i = 0; i < colors.size(); i++) { transparencies[i] = 1.0; } } QString fileName = gradientName + ".ggr"; QSharedPointer gradient(new KoSegmentGradient(fileName)); Q_UNUSED(gradientSmoothness); gradient->setName(gradientName); for (int i = 1; i < colors.size(); i++) { QColor startColor = colors[i-1]; QColor endColor = colors[i]; startColor.setAlphaF(transparencies[i-1]); endColor.setAlphaF(transparencies[i]); qreal start = startLocations[i-1]; qreal end = startLocations[i]; qreal middle = start + middleOffsets[i-1] * (end - start); gradient->createSegment(INTERP_LINEAR, COLOR_INTERP_RGB, start, end, middle, startColor, endColor); } gradient->setValid(true); catcher.addGradient(path, gradient); } else { retval = false; } return retval; } void parseElement(const QDomElement &el, const QString &parentPath, KisAslObjectCatcher &catcher) { KIS_ASSERT_RECOVER_RETURN(el.tagName() == "node"); QString type = el.attribute("type", ""); QString key = el.attribute("key", ""); if (type == "Descriptor") { QString classId = el.attribute("classId", ""); QString containerName = key.isEmpty() ? classId : key; QString containerPath = buildPath(parentPath, containerName); if (!tryParseDescriptor(el, containerPath, classId, catcher)) { QDomNode child = el.firstChild(); while (!child.isNull()) { parseElement(child.toElement(), containerPath, catcher); child = child.nextSibling(); } } } else if (type == "List") { catcher.setArrayMode(true); QString containerName = key; QString containerPath = buildPath(parentPath, containerName); QDomNode child = el.firstChild(); while (!child.isNull()) { parseElement(child.toElement(), containerPath, catcher); child = child.nextSibling(); } catcher.setArrayMode(false); } else if (type == "Double") { double v = KisDomUtils::toDouble(el.attribute("value", "0")); catcher.addDouble(buildPath(parentPath, key), v); } else if (type == "UnitFloat") { QString unit = el.attribute("unit", ""); double v = KisDomUtils::toDouble(el.attribute("value", "0")); catcher.addUnitFloat(buildPath(parentPath, key), unit, v); } else if (type == "Text") { QString v = el.attribute("value", ""); catcher.addText(buildPath(parentPath, key), v); } else if (type == "Enum") { QString v = el.attribute("value", ""); QString typeId = el.attribute("typeId", ""); catcher.addEnum(buildPath(parentPath, key), typeId, v); } else if (type == "Integer") { int v = KisDomUtils::toInt(el.attribute("value", "0")); catcher.addInteger(buildPath(parentPath, key), v); } else if (type == "Boolean") { int v = KisDomUtils::toInt(el.attribute("value", "0")); catcher.addBoolean(buildPath(parentPath, key), v); } else { warnKrita << "WARNING: XML (ASL) Unknown element type:" << type << ppVar(parentPath) << ppVar(key); } } } // namespace void KisAslXmlParser::parseXML(const QDomDocument &doc, KisAslObjectCatcher &catcher) { QDomElement root = doc.documentElement(); if (root.tagName() != "asl") { return; } QDomNode child = root.firstChild(); while (!child.isNull()) { Private::parseElement(child.toElement(), "", catcher); child = child.nextSibling(); } } diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp index 9a6597a658..a3e5bf70fa 100644 --- a/libs/ui/KisApplication.cpp +++ b/libs/ui/KisApplication.cpp @@ -1,812 +1,812 @@ /* * 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_OSX #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 #include #include "KoGlobal.h" #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 namespace { const QTime appStartTime(QTime::currentTime()); } class KisApplicationPrivate { public: KisApplicationPrivate() : splashScreen(0) {} QPointer splashScreen; }; class KisApplication::ResetStarting { public: ResetStarting(KisSplashScreen *splash, int fileCount) : m_splash(splash) , m_fileCount(fileCount) { } ~ResetStarting() { if (m_splash) { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false); if (m_fileCount > 0 || hideSplash) { m_splash->hide(); } else { m_splash->setWindowFlags(Qt::Dialog); QRect r(QPoint(), m_splash->size()); m_splash->move(QApplication::desktop()->availableGeometry().center() - r.center()); m_splash->setWindowTitle(qAppName()); m_splash->setParent(0); Q_FOREACH (QObject *o, m_splash->children()) { QWidget *w = qobject_cast(o); if (w && w->isHidden()) { w->setVisible(true); } } m_splash->show(); m_splash->activateWindow(); } } } QPointer m_splash; int m_fileCount; }; KisApplication::KisApplication(const QString &key, int &argc, char **argv) : QtSingleApplication(key, argc, argv) , d(new KisApplicationPrivate) , m_autosaveDialog(0) , m_mainWindow(0) , m_batchRun(false) { #ifdef Q_OS_OSX setMouseCoalescingEnabled(false); #endif QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); setApplicationDisplayName("Krita"); setApplicationName("krita"); // Note: Qt docs suggest we set this, but if we do, we get resource paths of the form of krita/krita, which is weird. // setOrganizationName("krita"); setOrganizationDomain("krita.org"); QString version = KritaVersionWrapper::versionString(true); setApplicationVersion(version); setWindowIcon(KisIconUtils::loadIcon("calligrakrita")); if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) { QStringList styles = QStringList() << "breeze" << "fusion" << "plastique"; if (!styles.contains(style()->objectName().toLower())) { Q_FOREACH (const QString & style, styles) { if (!setStyle(style)) { qDebug() << "No" << style << "available."; } else { qDebug() << "Set style" << style; break; } } } } else { qDebug() << "Style override disabled, using" << style()->objectName(); } KisOpenGL::initialize(); qDebug() << "krita has opengl" << KisOpenGL::hasOpenGL(); } #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() { // All Krita's resource types KoResourcePaths::addResourceType("kis_pics", "data", "/pics/"); KoResourcePaths::addResourceType("kis_images", "data", "/images/"); KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/"); 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("psd_layer_style_collections", "data", "/asl"); KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/", true); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/"); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/", true); KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/", true); KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/"); KoResourcePaths::addResourceType("kis_actions", "data", "/actions"); KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc"); KoResourcePaths::addResourceType("ko_effects", "data", "/effects/"); KoResourcePaths::addResourceType("tags", "data", "/tags/"); KoResourcePaths::addResourceType("templates", "data", "/templates"); KoResourcePaths::addResourceType("pythonscripts", "data", "/pykrita"); KoResourcePaths::addResourceType("symbols", "data", "/symbols"); // // 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) + "/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/"); // Indicate that it is now safe for users of KoResourcePaths to load resources KoResourcePaths::setReady(); } void KisApplication::loadResources() { setSplashScreenLoadingText(i18n("Loading Gradients...")); processEvents(); KoResourceServerProvider::instance()->gradientServer(true); // Load base resources setSplashScreenLoadingText(i18n("Loading Patterns...")); processEvents(); KoResourceServerProvider::instance()->patternServer(true); setSplashScreenLoadingText(i18n("Loading Palettes...")); processEvents(); KoResourceServerProvider::instance()->paletteServer(false); setSplashScreenLoadingText(i18n("Loading Brushes...")); processEvents(); KisBrushServer::instance()->brushServer(true); // load paintop presets setSplashScreenLoadingText(i18n("Loading Paint Operations...")); processEvents(); KisResourceServerProvider::instance()->paintOpPresetServer(true); // load symbols setSplashScreenLoadingText(i18n("Loading SVG Symbol Collections...")); processEvents(); KoResourceServerProvider::instance()->svgSymbolCollectionServer(true); setSplashScreenLoadingText(i18n("Loading Resource Bundles...")); processEvents(); KisResourceServerProvider::instance()->resourceBundleServer(); } void KisApplication::loadPlugins() { KoShapeRegistry* r = KoShapeRegistry::instance(); r->add(new KisShapeSelectionFactory()); KisActionRegistry::instance(); KisFilterRegistry::instance(); KisGeneratorRegistry::instance(); KisPaintOpRegistry::instance(); KoColorSpaceRegistry::instance(); // Load the krita-specific tools setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Tool...")); processEvents(); KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Tool"), QString::fromLatin1("[X-Krita-Version] == 28")); // Load dockers setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Dock...")); processEvents(); KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Dock"), QString::fromLatin1("[X-Krita-Version] == 28")); // XXX_EXIV: make the exiv io backends real plugins setSplashScreenLoadingText(i18n("Loading Plugins Exiv/IO...")); processEvents(); KisExiv2::initialize(); } bool KisApplication::start(const KisApplicationArguments &args) { KisConfig cfg; #if defined(Q_OS_WIN) #ifdef ENV32BIT if (isWow64() && !cfg.readEntry("WarnedAbout32Bits", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You are running a 32 bits build on a 64 bits Windows.\n" "This is not recommended.\n" "Please download and install the x64 build instead.")); cfg.writeEntry("WarnedAbout32Bits", true); } #endif #endif QString opengl = cfg.canvasState(); if (opengl == "OPENGL_NOT_TRIED" ) { cfg.setCanvasState("TRY_OPENGL"); } else if (opengl != "OPENGL_SUCCESS") { cfg.setCanvasState("OPENGL_FAILED"); } setSplashScreenLoadingText(i18n("Initializing Globals")); processEvents(); initializeGlobals(args); const bool doNewImage = args.doNewImage(); const bool doTemplate = args.doTemplate(); const bool print = args.print(); const bool exportAs = args.exportAs(); const bool exportAsPdf = args.exportAsPdf(); const QString exportFileName = args.exportFileName(); m_batchRun = (print || exportAs || exportAsPdf || !exportFileName.isEmpty()); // print & exportAsPdf do user interaction ATM const bool needsMainWindow = !exportAs; // only show the mainWindow when no command-line mode option is passed // TODO: fix print & exportAsPdf to work without mainwindow shown const bool showmainWindow = !exportAs; // would be !batchRun; const bool showSplashScreen = !m_batchRun && qEnvironmentVariableIsEmpty("NOSPLASH"); if (showSplashScreen && d->splashScreen) { d->splashScreen->show(); d->splashScreen->repaint(); processEvents(); } KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator()); // Initialize all Krita directories etc. KoGlobal::initialize(); 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(); // now we're set up, and the LcmsEnginePlugin will have access to resource paths for color management, // we can finally initialize KoColor. KoColor::init(); // Load all resources and tags before the plugins do that loadResources(); // Load the plugins loadPlugins(); if (needsMainWindow) { // show a mainWindow asap, if we want that setSplashScreenLoadingText(i18n("Loading Main Window...")); processEvents(); m_mainWindow = KisPart::instance()->createMainWindow(); if (showmainWindow) { m_mainWindow->initializeGeometry(); if (!args.workspace().isEmpty()) { KoResourceServer * rserver = KisResourceServerProvider::instance()->workspaceServer(); KisWorkspaceResource* workspace = rserver->resourceByName(args.workspace()); if (workspace) { m_mainWindow->restoreWorkspace(workspace->dockerState()); } } if (args.canvasOnly()) { m_mainWindow->viewManager()->switchCanvasOnly(true); } if (args.fullScreen()) { m_mainWindow->showFullScreen(); } else { m_mainWindow->show(); } } } short int numberOfOpenDocuments = 0; // number of documents open // Check for autosave files that can be restored, if we're not running a batchrun (test, print, export to pdf) if (!m_batchRun) { checkAutosaveFiles(); } setSplashScreenLoadingText(QString()); // done loading, so clear out label processEvents(); //configure the unit manager KisSpinBoxUnitManagerFactory::setDefaultUnitManagerBuilder(new KisDocumentAwareSpinBoxUnitManagerBuilder()); connect(this, &KisApplication::aboutToQuit, &KisSpinBoxUnitManagerFactory::clearUnitManagerBuilder); //ensure the builder is destroyed when the application leave. //the new syntax slot syntax allow to connect to a non q_object static method. // Create a new image, if needed if (doNewImage) { KisDocument *doc = args.image(); if (doc) { KisPart::instance()->addDocument(doc); m_mainWindow->addViewAndNotifyLoadingCompleted(doc); } } // Get the command line arguments which we have to parse int argsCount = args.filenames().count(); if (argsCount > 0) { // Loop through arguments short int nPrinted = 0; 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 (m_batchRun) { continue; } if (createNewDocFromTemplate(fileName, m_mainWindow)) { ++numberOfOpenDocuments; } // now try to load } else { if (exportAs) { QString outputMimetype = KisMimeDatabase::mimeTypeForFile(exportFileName); if (outputMimetype == "application/octetstream") { dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl; return 1; } KisDocument *doc = KisPart::instance()->createDocument(); doc->setFileBatchMode(m_batchRun); doc->openUrl(QUrl::fromLocalFile(fileName)); qApp->processEvents(); // For vector layers to be updated doc->setFileBatchMode(true); if (!doc->exportDocumentSync(QUrl::fromLocalFile(exportFileName), outputMimetype.toLatin1())) { dbgKrita << "Could not export " << fileName << "to" << exportFileName << ":" << doc->errorMessage(); } nPrinted++; QTimer::singleShot(0, this, SLOT(quit())); } else if (m_mainWindow) { KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; if (m_mainWindow->openDocument(QUrl::fromLocalFile(fileName), flags)) { if (print) { m_mainWindow->slotFilePrint(); nPrinted++; // TODO: trigger closing of app once printing is done } else if (exportAsPdf) { KisPrintJob *job = m_mainWindow->exportToPdf(exportFileName); if (job) connect (job, SIGNAL(destroyed(QObject*)), m_mainWindow, SLOT(slotFileQuit()), Qt::QueuedConnection); nPrinted++; } else { // Normal case, success numberOfOpenDocuments++; } } else { // .... if failed // delete doc; done by openDocument } } } } if (m_batchRun) { return nPrinted > 0; } } // 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(); d->splashScreen->displayRecentFiles(); } // not calling this before since the program will quit there. return true; } KisApplication::~KisApplication() { delete d; } 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->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::remoteArguments(QByteArray message, QObject *socket) { Q_UNUSED(socket); // check if we have any mainwindow KisMainWindow *mw = qobject_cast(qApp->activeWindow()); if (!mw) { mw = KisPart::instance()->mainWindows().first(); } if (!mw) { return; } KisApplicationArguments args = KisApplicationArguments::deserialize(message); const bool doTemplate = args.doTemplate(); const int argsCount = args.filenames().count(); if (argsCount > 0) { // Loop through arguments for (int argNumber = 0; argNumber < argsCount; ++argNumber) { QString filename = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { createNewDocFromTemplate(filename, mw); } else if (QFile(filename).exists()) { KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; mw->openDocument(QUrl::fromLocalFile(filename), flags); } } } } void KisApplication::fileOpenRequested(const QString &url) { KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first(); if (mainWindow) { KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; mainWindow->openDocument(QUrl::fromLocalFile(url), flags); } } void KisApplication::checkAutosaveFiles() { if (m_batchRun) return; // Check for autosave files from a previous run. There can be several, and // we want to offer a restore for every one. Including a nice thumbnail! QStringList filters; filters << QString(".krita-*-*-autosave.kra"); #ifdef Q_OS_WIN QDir dir = QDir::temp(); #else QDir dir = QDir::home(); #endif // all autosave files for our application QStringList autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden); // Allow the user to make their selection if (autosaveFiles.size() > 0) { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } m_autosaveDialog = new KisAutoSaveRecoveryDialog(autosaveFiles, activeWindow()); QDialog::DialogCode result = (QDialog::DialogCode) m_autosaveDialog->exec(); if (result == QDialog::Accepted) { QStringList filesToRecover = m_autosaveDialog->recoverableFiles(); Q_FOREACH (const QString &autosaveFile, autosaveFiles) { if (!filesToRecover.contains(autosaveFile)) { QFile::remove(dir.absolutePath() + "/" + autosaveFile); } } autosaveFiles = filesToRecover; } else { autosaveFiles.clear(); } if (autosaveFiles.size() > 0) { QList autosaveUrls; Q_FOREACH (const QString &autoSaveFile, autosaveFiles) { const QUrl url = QUrl::fromLocalFile(dir.absolutePath() + QLatin1Char('/') + autoSaveFile); autosaveUrls << url; } if (m_mainWindow) { Q_FOREACH (const QUrl &url, autosaveUrls) { KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; m_mainWindow->openDocument(url, flags | KisMainWindow::RecoveryFile); } } } // cleanup delete m_autosaveDialog; m_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); KisMainWindow::OpenFlags batchFlags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; if (mainWindow->openDocument(templateURL, KisMainWindow::Import | batchFlags)) { dbgUI << "Template loaded..."; return true; } else { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Template %1 failed to load.", templateURL.toDisplayString())); } } return false; } void KisApplication::clearConfig() { KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread()); KSharedConfigPtr config = KSharedConfig::openConfig(); // find user settings file bool createDir = false; QString kritarcPath = KoResourcePaths::locateLocal("config", "kritarc", createDir); QFile configFile(kritarcPath); if (configFile.exists()) { // clear file if (configFile.open(QFile::WriteOnly)) { configFile.close(); } else { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Failed to clear %1\n\n" "Please make sure no other program is using the file and try again.", kritarcPath), QMessageBox::Ok, QMessageBox::Ok); } } // reload from disk; with the user file settings cleared, // this should load any default configuration files shipping with the program config->reparseConfiguration(); config->sync(); } void KisApplication::askClearConfig() { Qt::KeyboardModifiers mods = QApplication::queryKeyboardModifiers(); bool askClearConfig = (mods & Qt::ControlModifier) && (mods & Qt::ShiftModifier) && (mods & Qt::AltModifier); if (askClearConfig) { bool ok = QMessageBox::question(0, i18nc("@title:window", "Krita"), i18n("Do you want to clear the settings file?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes; if (ok) { clearConfig(); } } } diff --git a/libs/ui/KisDocument.cpp b/libs/ui/KisDocument.cpp index e2f7f7338c..1cecc03405 100644 --- a/libs/ui/KisDocument.cpp +++ b/libs/ui/KisDocument.cpp @@ -1,1656 +1,1656 @@ /* This file is part of the Krita project * * Copyright (C) 2014 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMainWindow.h" // XXX: remove #include // XXX: remove #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Krita Image #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_layer_utils.h" // Local #include "KisViewManager.h" #include "kis_clipboard.h" #include "widgets/kis_custom_image_widget.h" #include "canvas/kis_canvas2.h" #include "flake/kis_shape_controller.h" #include "kis_statusbar.h" #include "widgets/kis_progress_widget.h" #include "kis_canvas_resource_provider.h" #include "kis_resource_server_provider.h" #include "kis_node_manager.h" #include "KisPart.h" #include "KisApplication.h" #include "KisDocument.h" #include "KisImportExportManager.h" #include "KisPart.h" #include "KisView.h" #include "kis_grid_config.h" #include "kis_guides_config.h" #include "kis_image_barrier_lock_adapter.h" #include #include "kis_config_notifier.h" #include "kis_async_action_feedback.h" // Define the protocol used here for embedded documents' URL // This used to "store" but QUrl didn't like it, // so let's simply make it "tar" ! #define STORE_PROTOCOL "tar" // The internal path is a hack to make QUrl happy and for document children #define INTERNAL_PROTOCOL "intern" #define INTERNAL_PREFIX "intern:/" // Warning, keep it sync in koStore.cc #include using namespace std; namespace { constexpr int errorMessageTimeout = 5000; constexpr int successMessageTimeout = 1000; } /********************************************************** * * KisDocument * **********************************************************/ //static QString KisDocument::newObjectName() { static int s_docIFNumber = 0; QString name; name.setNum(s_docIFNumber++); name.prepend("document_"); return name; } class UndoStack : public KUndo2Stack { public: UndoStack(KisDocument *doc) : KUndo2Stack(doc), m_doc(doc) { } void setIndex(int idx) override { KisImageWSP image = this->image(); image->requestStrokeCancellation(); if(image->tryBarrierLock()) { KUndo2Stack::setIndex(idx); image->unlock(); } } void notifySetIndexChangedOneCommand() override { KisImageWSP image = this->image(); image->unlock(); /** * Some very weird commands may emit blocking signals to * the GUI (e.g. KisGuiContextCommand). Here is the best thing * we can do to avoid the deadlock */ while(!image->tryBarrierLock()) { QApplication::processEvents(); } } void undo() override { KisImageWSP image = this->image(); image->requestUndoDuringStroke(); if (image->tryUndoUnfinishedLod0Stroke() == UNDO_OK) { return; } if(image->tryBarrierLock()) { KUndo2Stack::undo(); image->unlock(); } } void redo() override { KisImageWSP image = this->image(); if(image->tryBarrierLock()) { KUndo2Stack::redo(); image->unlock(); } } private: KisImageWSP image() { KisImageWSP currentImage = m_doc->image(); Q_ASSERT(currentImage); return currentImage; } private: KisDocument *m_doc; }; class Q_DECL_HIDDEN KisDocument::Private { public: Private(KisDocument *q) : docInfo(new KoDocumentInfo(q)), // deleted by QObject importExportManager(new KisImportExportManager(q)), // deleted manually undoStack(new UndoStack(q)), // deleted by QObject m_bAutoDetectedMime(false), modified(false), readwrite(true), firstMod(QDateTime::currentDateTime()), lastMod(firstMod), nserver(new KisNameServer(1)), imageIdleWatcher(2000 /*ms*/), savingLock(&savingMutex) { if (QLocale().measurementSystem() == QLocale::ImperialSystem) { unit = KoUnit::Inch; } else { unit = KoUnit::Centimeter; } } Private(const Private &rhs, KisDocument *q) : docInfo(new KoDocumentInfo(*rhs.docInfo, q)), unit(rhs.unit), importExportManager(new KisImportExportManager(q)), mimeType(rhs.mimeType), outputMimeType(rhs.outputMimeType), undoStack(new UndoStack(q)), guidesConfig(rhs.guidesConfig), m_bAutoDetectedMime(rhs.m_bAutoDetectedMime), m_url(rhs.m_url), m_file(rhs.m_file), modified(rhs.modified), readwrite(rhs.readwrite), firstMod(rhs.firstMod), lastMod(rhs.lastMod), nserver(new KisNameServer(*rhs.nserver)), preActivatedNode(0), // the node is from another hierarchy! imageIdleWatcher(2000 /*ms*/), assistants(rhs.assistants), // WARNING: assistants should not store pointers to the document! gridConfig(rhs.gridConfig), savingLock(&savingMutex) { } ~Private() { // Don't delete m_d->shapeController because it's in a QObject hierarchy. delete nserver; } KoDocumentInfo *docInfo = 0; KoUnit unit; KisImportExportManager *importExportManager = 0; // The filter-manager to use when loading/saving [for the options] QByteArray mimeType; // The actual mimetype of the document QByteArray outputMimeType; // The mimetype to use when saving QTimer autoSaveTimer; QString lastErrorMessage; // see openFile() QString lastWarningMessage; int autoSaveDelay = 300; // in seconds, 0 to disable. bool modifiedAfterAutosave = false; bool isAutosaving = false; bool disregardAutosaveFailure = false; KUndo2Stack *undoStack = 0; KisGuidesConfig guidesConfig; bool m_bAutoDetectedMime = false; // whether the mimetype in the arguments was detected by the part itself QUrl m_url; // local url - the one displayed to the user. QString m_file; // Local file - the only one the part implementation should deal with. QMutex savingMutex; bool modified = false; bool readwrite = false; QDateTime firstMod; QDateTime lastMod; KisNameServer *nserver; KisImageSP image; KisImageSP savingImage; KisNodeWSP preActivatedNode; KisShapeController* shapeController = 0; KoShapeController* koShapeController = 0; KisIdleWatcher imageIdleWatcher; QScopedPointer imageIdleConnection; QList assistants; KisGridConfig gridConfig; StdLockableWrapper savingLock; bool modifiedWhileSaving = false; QScopedPointer backgroundSaveDocument; QPointer savingUpdater; QFuture childSavingFuture; KritaUtils::ExportFileJob backgroundSaveJob; bool isRecovered = false; void setImageAndInitIdleWatcher(KisImageSP _image) { image = _image; imageIdleWatcher.setTrackedImage(image); if (image) { imageIdleConnection.reset( new KisSignalAutoConnection( &imageIdleWatcher, SIGNAL(startedIdleMode()), image.data(), SLOT(explicitRegenerateLevelOfDetail()))); } } class StrippedSafeSavingLocker; }; class KisDocument::Private::StrippedSafeSavingLocker { public: StrippedSafeSavingLocker(QMutex *savingMutex, KisImageSP image) : m_locked(false) , m_image(image) , m_savingLock(savingMutex) , m_imageLock(image, true) { /** * Initial try to lock both objects. Locking the image guards * us from any image composition threads running in the * background, while savingMutex guards us from entering the * saving code twice by autosave and main threads. * * Since we are trying to lock multiple objects, so we should * do it in a safe manner. */ m_locked = std::try_lock(m_imageLock, m_savingLock) < 0; if (!m_locked) { m_image->requestStrokeEnd(); QApplication::processEvents(); // one more try... m_locked = std::try_lock(m_imageLock, m_savingLock) < 0; } } ~StrippedSafeSavingLocker() { if (m_locked) { m_imageLock.unlock(); m_savingLock.unlock(); } } bool successfullyLocked() const { return m_locked; } private: bool m_locked; KisImageSP m_image; StdLockableWrapper m_savingLock; KisImageBarrierLockAdapter m_imageLock; }; KisDocument::KisDocument() : d(new Private(this)) { connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); connect(d->undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(slotUndoStackCleanChanged(bool))); connect(&d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); setObjectName(newObjectName()); // preload the krita resources KisResourceServerProvider::instance(); d->shapeController = new KisShapeController(this, d->nserver), d->koShapeController = new KoShapeController(0, d->shapeController), slotConfigChanged(); } KisDocument::KisDocument(const KisDocument &rhs) : QObject(), d(new Private(*rhs.d, this)) { connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); connect(d->undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(slotUndoStackCleanChanged(bool))); connect(&d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); setObjectName(rhs.objectName()); d->shapeController = new KisShapeController(this, d->nserver), d->koShapeController = new KoShapeController(0, d->shapeController), slotConfigChanged(); // clone the image with keeping the GUIDs of the layers intact // NOTE: we expect the image to be locked! setCurrentImage(rhs.image()->clone(true)); if (rhs.d->preActivatedNode) { // since we clone uuid's, we can use them for lacating new // nodes. Otherwise we would need to use findSymmetricClone() d->preActivatedNode = KisLayerUtils::findNodeByUuid(d->image->root(), rhs.d->preActivatedNode->uuid()); } } KisDocument::~KisDocument() { // wait until all the pending operations are in progress waitForSavingToComplete(); /** * Push a timebomb, which will try to release the memory after * the document has been deleted */ KisPaintDevice::createMemoryReleaseObject()->deleteLater(); d->autoSaveTimer.disconnect(this); d->autoSaveTimer.stop(); delete d->importExportManager; // Despite being QObject they needs to be deleted before the image delete d->shapeController; delete d->koShapeController; if (d->image) { d->image->notifyAboutToBeDeleted(); /** * WARNING: We should wait for all the internal image jobs to * finish before entering KisImage's destructor. The problem is, * while execution of KisImage::~KisImage, all the weak shared * pointers pointing to the image enter an inconsistent * state(!). The shared counter is already zero and destruction * has started, but the weak reference doesn't know about it, * because KisShared::~KisShared hasn't been executed yet. So all * the threads running in background and having weak pointers will * enter the KisImage's destructor as well. */ d->image->requestStrokeCancellation(); d->image->waitForDone(); // clear undo commands that can still point to the image d->undoStack->clear(); d->image->waitForDone(); KisImageWSP sanityCheckPointer = d->image; Q_UNUSED(sanityCheckPointer); // The following line trigger the deletion of the image d->image.clear(); // check if the image has actually been deleted KIS_SAFE_ASSERT_RECOVER_NOOP(!sanityCheckPointer.isValid()); } delete d; } bool KisDocument::reload() { // XXX: reimplement! return false; } KisDocument *KisDocument::clone() { return new KisDocument(*this); } bool KisDocument::exportDocumentImpl(const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration) { QFileInfo filePathInfo(job.filePath); if (filePathInfo.exists() && !filePathInfo.isWritable()) { slotCompleteSavingDocument(job, KisImportExportFilter::CreationError, i18n("%1 cannot be written to. Please save under a different name.", job.filePath)); return false; } KisConfig cfg; if (cfg.backupFile() && filePathInfo.exists()) { KBackup::backupFile(job.filePath); } KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!job.mimeType.isEmpty(), false); const QString actionName = job.flags & KritaUtils::SaveIsExporting ? i18n("Exporting Document...") : i18n("Saving Document..."); bool started = initiateSavingInBackground(actionName, this, SLOT(slotCompleteSavingDocument(KritaUtils::ExportFileJob, KisImportExportFilter::ConversionStatus,QString)), job, exportConfiguration); if (!started) { emit canceled(QString()); } return started; } bool KisDocument::exportDocument(const QUrl &url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { using namespace KritaUtils; SaveFlags flags = SaveIsExporting; if (showWarnings) { flags |= SaveShowWarnings; } return exportDocumentImpl(KritaUtils::ExportFileJob(url.toLocalFile(), mimeType, flags), exportConfiguration); } bool KisDocument::saveAs(const QUrl &url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { using namespace KritaUtils; return exportDocumentImpl(ExportFileJob(url.toLocalFile(), mimeType, showWarnings ? SaveShowWarnings : SaveNone), exportConfiguration); } bool KisDocument::save(bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { return saveAs(url(), mimeType(), showWarnings, exportConfiguration); } QByteArray KisDocument::serializeToNativeByteArray() { QByteArray byteArray; QBuffer buffer(&byteArray); QScopedPointer filter(KisImportExportManager::filterForMimeType(nativeFormatMimeType(), KisImportExportManager::Export)); filter->setBatchMode(true); filter->setMimeType(nativeFormatMimeType()); Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image); if (!locker.successfullyLocked()) { return byteArray; } d->savingImage = d->image; if (filter->convert(this, &buffer) != KisImportExportFilter::OK) { qWarning() << "serializeToByteArray():: Could not export to our native format"; } return byteArray; } void KisDocument::slotCompleteSavingDocument(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage) { const QString fileName = QFileInfo(job.filePath).fileName(); if (status != KisImportExportFilter::OK) { emit statusBarMessage(i18nc("%1 --- failing file name, %2 --- error mesage", "Error during saving %1: %2", fileName, exportErrorToUserMessage(status, errorMessage)), errorMessageTimeout); if (!fileBatchMode()) { const QString filePath = job.filePath; QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save %1\nReason: %2", filePath, errorMessage)); } } else { if (!(job.flags & KritaUtils::SaveIsExporting)) { setUrl(QUrl::fromLocalFile(job.filePath)); setLocalFilePath(job.filePath); setMimeType(job.mimeType); updateEditingTime(true); if (!d->modifiedWhileSaving) { d->undoStack->setClean(); } setRecovered(false); removeAutoSaveFiles(); } emit completed(); emit sigSavingFinished(); emit statusBarMessage(i18n("Finished saving %1", fileName), successMessageTimeout); } } QByteArray KisDocument::mimeType() const { return d->mimeType; } void KisDocument::setMimeType(const QByteArray & mimeType) { d->mimeType = mimeType; } bool KisDocument::fileBatchMode() const { return d->importExportManager->batchMode(); } void KisDocument::setFileBatchMode(const bool batchMode) { d->importExportManager->setBatchMode(batchMode); } KisDocument* KisDocument::lockAndCloneForSaving() { Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image); if (!locker.successfullyLocked()) { return 0; } return new KisDocument(*this); } bool KisDocument::exportDocumentSync(const QUrl &url, const QByteArray &mimeType, KisPropertiesConfigurationSP exportConfiguration) { Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image); if (!locker.successfullyLocked()) { return false; } d->savingImage = d->image; const QString fileName = url.toLocalFile(); KisImportExportFilter::ConversionStatus status = d->importExportManager-> exportDocument(fileName, fileName, mimeType, false, exportConfiguration); d->savingImage = 0; return status == KisImportExportFilter::OK; } bool KisDocument::initiateSavingInBackground(const QString actionName, const QObject *receiverObject, const char *receiverMethod, const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration) { KIS_ASSERT_RECOVER_RETURN_VALUE(job.isValid(), false); QScopedPointer clonedDocument(lockAndCloneForSaving()); // we block saving until the current saving is finished! if (!clonedDocument || !d->savingMutex.tryLock()) { return false; } KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveDocument, false); KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveJob.isValid(), false); d->backgroundSaveDocument.reset(clonedDocument.take()); d->backgroundSaveJob = job; d->modifiedWhileSaving = false; if (d->backgroundSaveJob.flags & KritaUtils::SaveInAutosaveMode) { d->backgroundSaveDocument->d->isAutosaving = true; } connect(d->backgroundSaveDocument.data(), SIGNAL(sigBackgroundSavingFinished(KisImportExportFilter::ConversionStatus, const QString&)), this, SLOT(slotChildCompletedSavingInBackground(KisImportExportFilter::ConversionStatus, const QString&))); connect(this, SIGNAL(sigCompleteBackgroundSaving(KritaUtils::ExportFileJob,KisImportExportFilter::ConversionStatus,QString)), receiverObject, receiverMethod, Qt::UniqueConnection); bool started = d->backgroundSaveDocument->startExportInBackground(actionName, job.filePath, job.filePath, job.mimeType, job.flags & KritaUtils::SaveShowWarnings, exportConfiguration); return started; } void KisDocument::slotChildCompletedSavingInBackground(KisImportExportFilter::ConversionStatus status, const QString &errorMessage) { KIS_SAFE_ASSERT_RECOVER(!d->savingMutex.tryLock()) { d->savingMutex.unlock(); return; } KIS_SAFE_ASSERT_RECOVER_RETURN(d->backgroundSaveDocument); if (d->backgroundSaveJob.flags & KritaUtils::SaveInAutosaveMode) { d->backgroundSaveDocument->d->isAutosaving = false; } d->backgroundSaveDocument.take()->deleteLater(); d->savingMutex.unlock(); KIS_SAFE_ASSERT_RECOVER_RETURN(d->backgroundSaveJob.isValid()); const KritaUtils::ExportFileJob job = d->backgroundSaveJob; d->backgroundSaveJob = KritaUtils::ExportFileJob(); emit sigCompleteBackgroundSaving(job, status, errorMessage); } void KisDocument::slotAutoSave() { if (!d->modified || !d->modifiedAfterAutosave) return; const QString autoSaveFileName = generateAutoSaveFileName(localFilePath()); emit statusBarMessage(i18n("Autosaving... %1", autoSaveFileName), successMessageTimeout); bool started = initiateSavingInBackground(i18n("Autosaving..."), this, SLOT(slotCompleteAutoSaving(KritaUtils::ExportFileJob, KisImportExportFilter::ConversionStatus, const QString&)), KritaUtils::ExportFileJob(autoSaveFileName, nativeFormatMimeType(), KritaUtils::SaveIsExporting | KritaUtils::SaveInAutosaveMode), 0); if (!started) { const int emergencyAutoSaveInterval = 10; // sec setAutoSaveDelay(emergencyAutoSaveInterval); } else { d->modifiedAfterAutosave = false; } } void KisDocument::slotCompleteAutoSaving(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage) { Q_UNUSED(job); const QString fileName = QFileInfo(job.filePath).fileName(); if (status != KisImportExportFilter::OK) { const int emergencyAutoSaveInterval = 10; // sec setAutoSaveDelay(emergencyAutoSaveInterval); emit statusBarMessage(i18nc("%1 --- failing file name, %2 --- error mesage", "Error during autosaving %1: %2", fileName, exportErrorToUserMessage(status, errorMessage)), errorMessageTimeout); } else { KisConfig cfg; d->autoSaveDelay = cfg.autoSaveInterval(); if (!d->modifiedWhileSaving) { d->autoSaveTimer.stop(); // until the next change } else { setAutoSaveDelay(d->autoSaveDelay); // restart the timer } emit statusBarMessage(i18n("Finished autosaving %1", fileName), successMessageTimeout); } } bool KisDocument::startExportInBackground(const QString &actionName, const QString &location, const QString &realLocation, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { d->savingImage = d->image; KisMainWindow *window = KisPart::instance()->currentMainwindow(); if (window) { if (window->viewManager()) { d->savingUpdater = window->viewManager()->createThreadedUpdater(actionName); d->importExportManager->setUpdater(d->savingUpdater); } } d->childSavingFuture = d->importExportManager->exportDocumentAsyc(location, realLocation, mimeType, showWarnings, exportConfiguration); if (d->childSavingFuture.isCanceled()) return false; typedef QFutureWatcher StatusWatcher; StatusWatcher *watcher = new StatusWatcher(); watcher->setFuture(d->childSavingFuture); connect(watcher, SIGNAL(finished()), SLOT(finishExportInBackground())); connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater())); return true; } void KisDocument::finishExportInBackground() { KIS_SAFE_ASSERT_RECOVER(d->childSavingFuture.isFinished()) { emit sigBackgroundSavingFinished(KisImportExportFilter::InternalError, ""); return; } KisImportExportFilter::ConversionStatus status = d->childSavingFuture.result(); const QString errorMessage = this->errorMessage(); d->savingImage.clear(); d->childSavingFuture = QFuture(); d->lastErrorMessage.clear(); if (d->savingUpdater) { d->savingUpdater->setProgress(100); } emit sigBackgroundSavingFinished(status, errorMessage); } void KisDocument::setReadWrite(bool readwrite) { d->readwrite = readwrite; setAutoSaveDelay(d->autoSaveDelay); Q_FOREACH (KisMainWindow *mainWindow, KisPart::instance()->mainWindows()) { mainWindow->setReadWrite(readwrite); } } void KisDocument::setAutoSaveDelay(int delay) { //qDebug() << "setting autosave delay from" << d->autoSaveDelay << "to" << delay; d->autoSaveDelay = delay; if (isReadWrite() && d->autoSaveDelay > 0) { d->autoSaveTimer.start(d->autoSaveDelay * 1000); } else { d->autoSaveTimer.stop(); } } KoDocumentInfo *KisDocument::documentInfo() const { return d->docInfo; } bool KisDocument::isModified() const { return d->modified; } QPixmap KisDocument::generatePreview(const QSize& size) { KisImageSP image = d->image; if (d->savingImage) image = d->savingImage; if (image) { QRect bounds = image->bounds(); QSize newSize = bounds.size(); newSize.scale(size, Qt::KeepAspectRatio); QPixmap px = QPixmap::fromImage(image->convertToQImage(newSize, 0)); if (px.size() == QSize(0,0)) { px = QPixmap(newSize); QPainter gc(&px); QBrush checkBrush = QBrush(KisCanvasWidgetBase::createCheckersImage(newSize.width() / 5)); gc.fillRect(px.rect(), checkBrush); gc.end(); } return px; } return QPixmap(size); } QString KisDocument::generateAutoSaveFileName(const QString & path) const { QString retval; // Using the extension allows to avoid relying on the mime magic when opening const QString extension (".kra"); QRegularExpression autosavePattern("^\\..+-autosave.kra$"); QFileInfo fi(path); QString dir = fi.absolutePath(); QString filename = fi.fileName(); if (path.isEmpty() || autosavePattern.match(filename).hasMatch()) { // Never saved? #ifdef Q_OS_WIN // On Windows, use the temp location (https://bugs.kde.org/show_bug.cgi?id=314921) retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::tempPath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension); #else // On Linux, use a temp file in $HOME then. Mark it with the pid so two instances don't overwrite each other's autosave file retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::homePath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension); #endif } else { retval = QString("%1%2.%3-autosave%4").arg(dir).arg(QDir::separator()).arg(filename).arg(extension); } //qDebug() << "generateAutoSaveFileName() for path" << path << ":" << retval; return retval; } bool KisDocument::importDocument(const QUrl &_url) { bool ret; dbgUI << "url=" << _url.url(); // open... ret = openUrl(_url); // reset url & m_file (kindly? set by KisParts::openUrl()) to simulate a // File --> Import if (ret) { dbgUI << "success, resetting url"; resetURL(); setTitleModified(); } return ret; } bool KisDocument::openUrl(const QUrl &_url, OpenFlags flags) { if (!_url.isLocalFile()) { return false; } dbgUI << "url=" << _url.url(); d->lastErrorMessage.clear(); // Reimplemented, to add a check for autosave files and to improve error reporting if (!_url.isValid()) { d->lastErrorMessage = i18n("Malformed URL\n%1", _url.url()); // ## used anywhere ? return false; } QUrl url(_url); bool autosaveOpened = false; if (url.isLocalFile() && !fileBatchMode()) { QString file = url.toLocalFile(); QString asf = generateAutoSaveFileName(file); if (QFile::exists(asf)) { KisApplication *kisApp = static_cast(qApp); kisApp->hideSplashScreen(); //dbgUI <<"asf=" << asf; // ## TODO compare timestamps ? int res = QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("An autosaved file exists for this document.\nDo you want to open the autosaved file instead?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); switch (res) { case QMessageBox::Yes : url.setPath(asf); autosaveOpened = true; break; case QMessageBox::No : QFile::remove(asf); break; default: // Cancel return false; } } } bool ret = openUrlInternal(url); if (autosaveOpened || flags & RecoveryFile) { setReadWrite(true); // enable save button setModified(true); setRecovered(true); } else { if (!(flags & DontAddToRecent)) { KisPart::instance()->addRecentURLToAllMainWindows(_url); } if (ret) { // Detect readonly local-files; remote files are assumed to be writable QFileInfo fi(url.toLocalFile()); setReadWrite(fi.isWritable()); } setRecovered(false); } return ret; } class DlgLoadMessages : public KoDialog { public: DlgLoadMessages(const QString &title, const QString &message, const QStringList &warnings) { setWindowTitle(title); setWindowIcon(KisIconUtils::loadIcon("warning")); QWidget *page = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(page); QHBoxLayout *hlayout = new QHBoxLayout(); QLabel *labelWarning= new QLabel(); labelWarning->setPixmap(KisIconUtils::loadIcon("warning").pixmap(32, 32)); hlayout->addWidget(labelWarning); hlayout->addWidget(new QLabel(message)); layout->addLayout(hlayout); QTextBrowser *browser = new QTextBrowser(); QString warning = "

"; if (warnings.size() == 1) { warning += " Reason:

"; } else { warning += " Reasons:

"; } warning += "

    "; Q_FOREACH(const QString &w, warnings) { warning += "\n
  • " + w + "
  • "; } warning += "
"; browser->setHtml(warning); browser->setMinimumHeight(200); browser->setMinimumWidth(400); layout->addWidget(browser); setMainWidget(page); setButtons(KoDialog::Ok); resize(minimumSize()); } }; bool KisDocument::openFile() { //dbgUI <<"for" << localFilePath(); if (!QFile::exists(localFilePath())) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("File %1 does not exist.", localFilePath())); return false; } QString filename = localFilePath(); QString typeName = mimeType(); if (typeName.isEmpty()) { typeName = KisMimeDatabase::mimeTypeForFile(filename); } //qDebug() << "mimetypes 4:" << typeName; // Allow to open backup files, don't keep the mimetype application/x-trash. if (typeName == "application/x-trash") { QString path = filename; while (path.length() > 0) { path.chop(1); typeName = KisMimeDatabase::mimeTypeForFile(path); //qDebug() << "\t" << path << typeName; if (!typeName.isEmpty()) { break; } } //qDebug() << "chopped" << filename << "to" << path << "Was trash, is" << typeName; } dbgUI << localFilePath() << "type:" << typeName; KisMainWindow *window = KisPart::instance()->currentMainwindow(); if (window && window->viewManager()) { KoUpdaterPtr updater = window->viewManager()->createUnthreadedUpdater(i18n("Opening document")); d->importExportManager->setUpdater(updater); } KisImportExportFilter::ConversionStatus status; status = d->importExportManager->importDocument(localFilePath(), typeName); if (status != KisImportExportFilter::OK) { QString msg = KisImportExportFilter::conversionStatusString(status); if (!msg.isEmpty()) { DlgLoadMessages dlg(i18nc("@title:window", "Krita"), i18n("Could not open %2.\nReason: %1.", msg, prettyPathOrUrl()), errorMessage().split("\n") + warningMessage().split("\n")); dlg.exec(); } return false; } else if (!warningMessage().isEmpty()) { DlgLoadMessages dlg(i18nc("@title:window", "Krita"), i18n("There were problems opening %1.", prettyPathOrUrl()), warningMessage().split("\n")); dlg.exec(); setUrl(QUrl()); } setMimeTypeAfterLoading(typeName); emit sigLoadingFinished(); undoStack()->clear(); return true; } // shared between openFile and koMainWindow's "create new empty document" code void KisDocument::setMimeTypeAfterLoading(const QString& mimeType) { d->mimeType = mimeType.toLatin1(); d->outputMimeType = d->mimeType; } bool KisDocument::loadNativeFormat(const QString & file_) { return openUrl(QUrl::fromLocalFile(file_)); } void KisDocument::setModified() { d->modified = true; } void KisDocument::setModified(bool mod) { if (mod) { updateEditingTime(false); } if (d->isAutosaving) // ignore setModified calls due to autosaving return; if ( !d->readwrite && d->modified ) { errKrita << "Can't set a read-only document to 'modified' !" << endl; return; } //dbgUI<<" url:" << url.path(); //dbgUI<<" mod="<docInfo->aboutInfo("editing-time").toInt() + d->firstMod.secsTo(d->lastMod))); d->firstMod = now; } else if (firstModDelta > 60 || forceStoreElapsed) { d->docInfo->setAboutInfo("editing-time", QString::number(d->docInfo->aboutInfo("editing-time").toInt() + firstModDelta)); d->firstMod = now; } d->lastMod = now; } QString KisDocument::prettyPathOrUrl() const { QString _url(url().toDisplayString()); #ifdef Q_OS_WIN if (url().isLocalFile()) { _url = QDir::toNativeSeparators(_url); } #endif return _url; } // Get caption from document info (title(), in about page) QString KisDocument::caption() const { QString c; const QString _url(url().fileName()); // if URL is empty...it is probably an unsaved file if (_url.isEmpty()) { c = " [" + i18n("Not Saved") + "] "; } else { c = _url; // Fall back to document URL } return c; } void KisDocument::setTitleModified() { emit titleModified(caption(), isModified()); } QDomDocument KisDocument::createDomDocument(const QString& tagName, const QString& version) const { return createDomDocument("krita", tagName, version); } //static QDomDocument KisDocument::createDomDocument(const QString& appName, const QString& tagName, const QString& version) { QDomImplementation impl; QString url = QString("http://www.calligra.org/DTD/%1-%2.dtd").arg(appName).arg(version); QDomDocumentType dtype = impl.createDocumentType(tagName, QString("-//KDE//DTD %1 %2//EN").arg(appName).arg(version), url); // The namespace URN doesn't need to include the version number. QString namespaceURN = QString("http://www.calligra.org/DTD/%1").arg(appName); QDomDocument doc = impl.createDocument(namespaceURN, tagName, dtype); doc.insertBefore(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""), doc.documentElement()); return doc; } bool KisDocument::isNativeFormat(const QByteArray& mimetype) const { if (mimetype == nativeFormatMimeType()) return true; return extraNativeMimeTypes().contains(mimetype); } void KisDocument::setErrorMessage(const QString& errMsg) { d->lastErrorMessage = errMsg; } QString KisDocument::errorMessage() const { return d->lastErrorMessage; } void KisDocument::setWarningMessage(const QString& warningMsg) { d->lastWarningMessage = warningMsg; } QString KisDocument::warningMessage() const { return d->lastWarningMessage; } void KisDocument::removeAutoSaveFiles() { //qDebug() << "removeAutoSaveFiles"; // Eliminate any auto-save file QString asf = generateAutoSaveFileName(localFilePath()); // the one in the current dir //qDebug() << "\tfilename:" << asf << "exists:" << QFile::exists(asf); if (QFile::exists(asf)) { //qDebug() << "\tremoving autosavefile" << asf; QFile::remove(asf); } asf = generateAutoSaveFileName(QString()); // and the one in $HOME //qDebug() << "Autsavefile in $home" << asf; if (QFile::exists(asf)) { //qDebug() << "\tremoving autsavefile 2" << asf; QFile::remove(asf); } } KoUnit KisDocument::unit() const { return d->unit; } void KisDocument::setUnit(const KoUnit &unit) { if (d->unit != unit) { d->unit = unit; emit unitChanged(unit); } } KUndo2Stack *KisDocument::undoStack() { return d->undoStack; } KisImportExportManager *KisDocument::importExportManager() const { return d->importExportManager; } void KisDocument::addCommand(KUndo2Command *command) { if (command) d->undoStack->push(command); } void KisDocument::beginMacro(const KUndo2MagicString & text) { d->undoStack->beginMacro(text); } void KisDocument::endMacro() { d->undoStack->endMacro(); } void KisDocument::slotUndoStackCleanChanged(bool value) { setModified(!value); } void KisDocument::slotConfigChanged() { KisConfig cfg; d->undoStack->setUndoLimit(cfg.undoStackLimit()); setAutoSaveDelay(cfg.autoSaveInterval()); } void KisDocument::clearUndoHistory() { d->undoStack->clear(); } KisGridConfig KisDocument::gridConfig() const { return d->gridConfig; } void KisDocument::setGridConfig(const KisGridConfig &config) { d->gridConfig = config; } const KisGuidesConfig& KisDocument::guidesConfig() const { return d->guidesConfig; } void KisDocument::setGuidesConfig(const KisGuidesConfig &data) { if (d->guidesConfig == data) return; d->guidesConfig = data; emit sigGuidesConfigChanged(d->guidesConfig); } void KisDocument::resetURL() { setUrl(QUrl()); setLocalFilePath(QString()); } KoDocumentInfoDlg *KisDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const { return new KoDocumentInfoDlg(parent, docInfo); } bool KisDocument::isReadWrite() const { return d->readwrite; } QUrl KisDocument::url() const { return d->m_url; } bool KisDocument::closeUrl(bool promptToSave) { if (promptToSave) { if ( isReadWrite() && isModified()) { Q_FOREACH (KisView *view, KisPart::instance()->views()) { if (view && view->document() == this) { if (!view->queryClose()) { return false; } } } } } // Not modified => ok and delete temp file. d->mimeType = QByteArray(); // It always succeeds for a read-only part, // but the return value exists for reimplementations // (e.g. pressing cancel for a modified read-write part) return true; } void KisDocument::setUrl(const QUrl &url) { d->m_url = url; } QString KisDocument::localFilePath() const { return d->m_file; } void KisDocument::setLocalFilePath( const QString &localFilePath ) { d->m_file = localFilePath; } bool KisDocument::openUrlInternal(const QUrl &url) { if ( !url.isValid() ) return false; if (d->m_bAutoDetectedMime) { d->mimeType = QByteArray(); d->m_bAutoDetectedMime = false; } QByteArray mimetype = d->mimeType; if ( !closeUrl() ) return false; d->mimeType = mimetype; setUrl(url); d->m_file.clear(); if (d->m_url.isLocalFile()) { d->m_file = d->m_url.toLocalFile(); bool ret; // set the mimetype only if it was not already set (for example, by the host application) if (d->mimeType.isEmpty()) { // get the mimetype of the file // using findByUrl() to avoid another string -> url conversion QString mime = KisMimeDatabase::mimeTypeForFile(d->m_url.toLocalFile()); d->mimeType = mime.toLocal8Bit(); d->m_bAutoDetectedMime = true; } setUrl(d->m_url); ret = openFile(); if (ret) { emit completed(); } else { emit canceled(QString()); } return ret; } return false; } bool KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace* cs, const KoColor &bgColor, bool backgroundAsLayer, int numberOfLayers, const QString &description, const double imageResolution) { Q_ASSERT(cs); KisConfig cfg; KisImageSP image; KisPaintLayerSP layer; if (!cs) return false; QApplication::setOverrideCursor(Qt::BusyCursor); image = new KisImage(createUndoStore(), width, height, cs, name); Q_CHECK_PTR(image); connect(image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection); image->setResolution(imageResolution, imageResolution); image->assignImageProfile(cs->profile()); documentInfo()->setAboutInfo("title", name); if (name != i18n("Unnamed") && !name.isEmpty()) { - setUrl(QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation) + '/' + name + ".kra")); + setUrl(QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation) + '/' + name + ".kra")); } documentInfo()->setAboutInfo("abstract", description); layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, cs); Q_CHECK_PTR(layer); if (backgroundAsLayer) { image->setDefaultProjectionColor(KoColor(cs)); if (bgColor.opacityU8() == OPACITY_OPAQUE_U8) { layer->paintDevice()->setDefaultPixel(bgColor); } else { // Hack: with a semi-transparent background color, the projection isn't composited right if we just set the default pixel KisFillPainter painter; painter.begin(layer->paintDevice()); painter.fillRect(0, 0, width, height, bgColor, bgColor.opacityU8()); } } else { image->setDefaultProjectionColor(bgColor); } layer->setDirty(QRect(0, 0, width, height)); image->addNode(layer.data(), image->rootLayer().data()); setCurrentImage(image); for(int i = 1; i < numberOfLayers; ++i) { KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), OPACITY_OPAQUE_U8, cs); image->addNode(layer, image->root(), i); layer->setDirty(QRect(0, 0, width, height)); } cfg.defImageWidth(width); cfg.defImageHeight(height); cfg.defImageResolution(imageResolution); cfg.defColorModel(image->colorSpace()->colorModelId().id()); cfg.setDefaultColorDepth(image->colorSpace()->colorDepthId().id()); cfg.defColorProfile(image->colorSpace()->profile()->name()); QApplication::restoreOverrideCursor(); return true; } bool KisDocument::isSaving() const { const bool result = d->savingMutex.tryLock(); if (result) { d->savingMutex.unlock(); } return !result; } void KisDocument::waitForSavingToComplete() { KisAsyncActionFeedback f(i18nc("progress dialog message when the user closes the document that is being saved", "Waiting for saving to complete..."), 0); f.waitForMutex(&d->savingMutex); } KoShapeBasedDocumentBase *KisDocument::shapeController() const { return d->shapeController; } KoShapeLayer* KisDocument::shapeForNode(KisNodeSP layer) const { return d->shapeController->shapeForNode(layer); } QList KisDocument::assistants() const { return d->assistants; } void KisDocument::setAssistants(const QList value) { d->assistants = value; } void KisDocument::setPreActivatedNode(KisNodeSP activatedNode) { d->preActivatedNode = activatedNode; } KisNodeSP KisDocument::preActivatedNode() const { return d->preActivatedNode; } KisImageWSP KisDocument::image() const { return d->image; } KisImageSP KisDocument::savingImage() const { return d->savingImage; } void KisDocument::setCurrentImage(KisImageSP image) { if (d->image) { // Disconnect existing sig/slot connections d->image->disconnect(this); d->shapeController->setImage(0); d->image = 0; } if (!image) return; d->setImageAndInitIdleWatcher(image); d->shapeController->setImage(image); setModified(false); connect(d->image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection); d->image->initialRefreshGraph(); } void KisDocument::setImageModified() { setModified(true); } KisUndoStore* KisDocument::createUndoStore() { return new KisDocumentUndoStore(this); } bool KisDocument::isAutosaving() const { return d->isAutosaving; } QString KisDocument::exportErrorToUserMessage(KisImportExportFilter::ConversionStatus status, const QString &errorMessage) { return errorMessage.isEmpty() ? KisImportExportFilter::conversionStatusString(status) : errorMessage; } diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp index abec058dbc..27d7bd684d 100644 --- a/libs/ui/KisMainWindow.cpp +++ b/libs/ui/KisMainWindow.cpp @@ -1,2486 +1,2488 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2000-2006 David Faure Copyright (C) 2007, 2009 Thomas zander Copyright (C) 2010 Benjamin Port This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMainWindow.h" #include // qt includes #include #include #include +#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_KIO #include #endif #include #include #include #include #include #include #include #include #include #include #include "KoDockFactoryBase.h" #include "KoDockWidgetTitleBar.h" #include "KoDocumentInfoDlg.h" #include "KoDocumentInfo.h" #include "KoFileDialog.h" #include #include #include #include #include #include "KoToolDocker.h" #include #include #include #include #include #include #include #include #include "dialogs/kis_about_application.h" #include "dialogs/kis_delayed_save_dialog.h" #include "dialogs/kis_dlg_preferences.h" #include "kis_action.h" #include "kis_action_manager.h" #include "KisApplication.h" #include "kis_canvas2.h" #include "kis_canvas_controller.h" #include "kis_canvas_resource_provider.h" #include "kis_clipboard.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_custom_image_widget.h" #include #include "KisDocument.h" #include "KisDocument.h" #include "kis_group_layer.h" #include "kis_icon_utils.h" #include "kis_image_from_clipboard_widget.h" #include "kis_image.h" #include #include "KisImportExportManager.h" #include "kis_mainwindow_observer.h" #include "kis_memory_statistics_server.h" #include "kis_node.h" #include "KisOpenPane.h" #include "kis_paintop_box.h" #include "KisPart.h" #include "KisPrintJob.h" #include "kis_resource_server_provider.h" #include "kis_signal_compressor_with_param.h" #include "kis_statusbar.h" #include "KisView.h" #include "KisViewManager.h" #include "thememanager.h" #include "kis_animation_importer.h" #include "dialogs/kis_dlg_import_image_sequence.h" #include #include #ifdef Q_OS_WIN #include #endif class ToolDockerFactory : public KoDockFactoryBase { public: ToolDockerFactory() : KoDockFactoryBase() { } QString id() const override { return "sharedtooldocker"; } QDockWidget* createDockWidget() override { KoToolDocker* dockWidget = new KoToolDocker(); dockWidget->setTabEnabled(false); return dockWidget; } DockPosition defaultDockPosition() const override { return DockRight; } }; class Q_DECL_HIDDEN KisMainWindow::Private { public: Private(KisMainWindow *parent) : q(parent) , dockWidgetMenu(new KActionMenu(i18nc("@action:inmenu", "&Dockers"), parent)) , windowMenu(new KActionMenu(i18nc("@action:inmenu", "&Window"), parent)) , documentMenu(new KActionMenu(i18nc("@action:inmenu", "New &View"), parent)) , workspaceMenu(new KActionMenu(i18nc("@action:inmenu", "Wor&kspace"), parent)) , mdiArea(new QMdiArea(parent)) , windowMapper(new QSignalMapper(parent)) , documentMapper(new QSignalMapper(parent)) { } ~Private() { qDeleteAll(toolbarList); } KisMainWindow *q {0}; KisViewManager *viewManager {0}; QPointer activeView; QList toolbarList; bool firstTime {true}; bool windowSizeDirty {false}; bool readOnly {false}; bool noCleanup {false}; KisAction *showDocumentInfo {0}; KisAction *saveAction {0}; KisAction *saveActionAs {0}; // KisAction *printAction; // KisAction *printActionPreview; // KisAction *exportPdf {0}; KisAction *importAnimation {0}; KisAction *closeAll {0}; // KisAction *reloadFile; KisAction *importFile {0}; KisAction *exportFile {0}; KisAction *undo {0}; KisAction *redo {0}; KisAction *newWindow {0}; KisAction *close {0}; KisAction *mdiCascade {0}; KisAction *mdiTile {0}; KisAction *mdiNextWindow {0}; KisAction *mdiPreviousWindow {0}; KisAction *toggleDockers {0}; KisAction *toggleDockerTitleBars {0}; KisAction *expandingSpacers[2]; KActionMenu *dockWidgetMenu; KActionMenu *windowMenu; KActionMenu *documentMenu; KActionMenu *workspaceMenu; KHelpMenu *helpMenu {0}; KRecentFilesAction *recentFiles {0}; KoResourceModel *workspacemodel {0}; QString lastExportLocation; QMap dockWidgetsMap; QMap dockWidgetVisibilityMap; QByteArray dockerStateBeforeHiding; KoToolDocker *toolOptionsDocker {0}; QCloseEvent *deferredClosingEvent {0}; Digikam::ThemeManager *themeManager {0}; QMdiArea *mdiArea; QMdiSubWindow *activeSubWindow {0}; QSignalMapper *windowMapper; QSignalMapper *documentMapper; QByteArray lastExportedFormat; QScopedPointer > tabSwitchCompressor; QMutex savingEntryMutex; KisActionManager * actionManager() { return viewManager->actionManager(); } QTabBar* findTabBarHACK() { QObjectList objects = mdiArea->children(); Q_FOREACH (QObject *object, objects) { QTabBar *bar = qobject_cast(object); if (bar) { return bar; } } return 0; } }; KisMainWindow::KisMainWindow() : KXmlGuiWindow() , d(new Private(this)) { auto rserver = KisResourceServerProvider::instance()->workspaceServer(false); QSharedPointer adapter(new KoResourceServerAdapter(rserver)); d->workspacemodel = new KoResourceModel(adapter, this); connect(d->workspacemodel, &KoResourceModel::afterResourcesLayoutReset, this, [&]() { updateWindowMenu(); }); KisConfig cfg; d->viewManager = new KisViewManager(this, actionCollection()); KConfigGroup group( KSharedConfig::openConfig(), "theme"); d->themeManager = new Digikam::ThemeManager(group.readEntry("Theme", "Krita dark"), this); setAcceptDrops(true); setStandardToolBarMenuEnabled(true); setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); setDockNestingEnabled(true); qApp->setStartDragDistance(25); // 25 px is a distance that works well for Tablet and Mouse events #ifdef Q_OS_OSX setUnifiedTitleAndToolBarOnMac(true); #endif connect(this, SIGNAL(restoringDone()), this, SLOT(forceDockTabFonts())); connect(this, SIGNAL(themeChanged()), d->viewManager, SLOT(updateIcons())); connect(KisPart::instance(), SIGNAL(documentClosed(QString)), SLOT(updateWindowMenu())); connect(KisPart::instance(), SIGNAL(documentOpened(QString)), SLOT(updateWindowMenu())); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(configChanged())); actionCollection()->addAssociatedWidget(this); KoPluginLoader::instance()->load("Krita/ViewPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), d->viewManager); KoToolBoxFactory toolBoxFactory; QDockWidget *toolbox = createDockWidget(&toolBoxFactory); toolbox->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable); if (cfg.toolOptionsInDocker()) { ToolDockerFactory toolDockerFactory; d->toolOptionsDocker = qobject_cast(createDockWidget(&toolDockerFactory)); d->toolOptionsDocker->toggleViewAction()->setEnabled(true); } QMap dockwidgetActions; dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction(); Q_FOREACH (const QString & docker, KoDockRegistry::instance()->keys()) { KoDockFactoryBase *factory = KoDockRegistry::instance()->value(docker); QDockWidget *dw = createDockWidget(factory); dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction(); } if (d->toolOptionsDocker) { dockwidgetActions[d->toolOptionsDocker->toggleViewAction()->text()] = d->toolOptionsDocker->toggleViewAction(); } connect(KoToolManager::instance(), SIGNAL(toolOptionWidgetsChanged(KoCanvasController*, QList >)), this, SLOT(newOptionWidgets(KoCanvasController*, QList >))); Q_FOREACH (QString title, dockwidgetActions.keys()) { d->dockWidgetMenu->addAction(dockwidgetActions[title]); } Q_FOREACH (QDockWidget *wdg, dockWidgets()) { if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) { wdg->setVisible(true); } } Q_FOREACH (KoCanvasObserverBase* observer, canvasObservers()) { observer->setObservedCanvas(0); KisMainwindowObserver* mainwindowObserver = dynamic_cast(observer); if (mainwindowObserver) { mainwindowObserver->setMainWindow(d->viewManager); } } d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setTabPosition(QTabWidget::North); d->mdiArea->setTabsClosable(true); setCentralWidget(d->mdiArea); connect(d->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(subWindowActivated())); connect(d->windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); connect(d->documentMapper, SIGNAL(mapped(QObject*)), this, SLOT(newView(QObject*))); createActions(); setAutoSaveSettings("krita", false); subWindowActivated(); updateWindowMenu(); if (isHelpMenuEnabled() && !d->helpMenu) { // workaround for KHelpMenu (or rather KAboutData::applicationData()) internally // not using the Q*Application metadata ATM, which results e.g. in the bugreport wizard // not having the app version preset // fixed hopefully in KF5 5.22.0, patch pending QGuiApplication *app = qApp; KAboutData aboutData(app->applicationName(), app->applicationDisplayName(), app->applicationVersion()); aboutData.setOrganizationDomain(app->organizationDomain().toUtf8()); d->helpMenu = new KHelpMenu(this, aboutData, false); // workaround-less version: // d->helpMenu = new KHelpMenu(this, QString()/*unused*/, false); // The difference between using KActionCollection->addAction() is that // these actions do not get tied to the MainWindow. What does this all do? KActionCollection *actions = d->viewManager->actionCollection(); QAction *helpContentsAction = d->helpMenu->action(KHelpMenu::menuHelpContents); QAction *whatsThisAction = d->helpMenu->action(KHelpMenu::menuWhatsThis); QAction *reportBugAction = d->helpMenu->action(KHelpMenu::menuReportBug); QAction *switchLanguageAction = d->helpMenu->action(KHelpMenu::menuSwitchLanguage); QAction *aboutAppAction = d->helpMenu->action(KHelpMenu::menuAboutApp); QAction *aboutKdeAction = d->helpMenu->action(KHelpMenu::menuAboutKDE); if (helpContentsAction) { actions->addAction(helpContentsAction->objectName(), helpContentsAction); } if (whatsThisAction) { actions->addAction(whatsThisAction->objectName(), whatsThisAction); } if (reportBugAction) { actions->addAction(reportBugAction->objectName(), reportBugAction); } if (switchLanguageAction) { actions->addAction(switchLanguageAction->objectName(), switchLanguageAction); } if (aboutAppAction) { actions->addAction(aboutAppAction->objectName(), aboutAppAction); } if (aboutKdeAction) { actions->addAction(aboutKdeAction->objectName(), aboutKdeAction); } connect(d->helpMenu, SIGNAL(showAboutApplication()), SLOT(showAboutApplication())); } // KDE' libs 4''s help contents action is broken outside kde, for some reason... We can handle it just as easily ourselves QAction *helpAction = actionCollection()->action("help_contents"); helpAction->disconnect(); connect(helpAction, SIGNAL(triggered()), this, SLOT(showManual())); #if 0 //check for colliding shortcuts QSet existingShortcuts; Q_FOREACH (QAction* action, actionCollection()->actions()) { if(action->shortcut() == QKeySequence(0)) { continue; } dbgKrita << "shortcut " << action->text() << " " << action->shortcut(); Q_ASSERT(!existingShortcuts.contains(action->shortcut())); existingShortcuts.insert(action->shortcut()); } #endif configChanged(); // If we have customized the toolbars, load that first setLocalXMLFile(KoResourcePaths::locateLocal("data", "krita4.xmlgui")); setXMLFile(":/kxmlgui5/krita4.xmlgui"); guiFactory()->addClient(this); // Create and plug toolbar list for Settings menu QList toolbarList; Q_FOREACH (QWidget* it, guiFactory()->containers("ToolBar")) { KToolBar * toolBar = ::qobject_cast(it); if (toolBar) { if (toolBar->objectName() == "BrushesAndStuff") { toolBar->setEnabled(false); } KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this); actionCollection()->addAction(toolBar->objectName().toUtf8(), act); act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle()))); connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool))); act->setChecked(!toolBar->isHidden()); toolbarList.append(act); } else { warnUI << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!"; } } plugActionList("toolbarlist", toolbarList); d->toolbarList = toolbarList; applyToolBarLayout(); d->viewManager->updateGUI(); d->viewManager->updateIcons(); #ifdef Q_OS_WIN auto w = qApp->activeWindow(); if (w) QWindowsWindowFunctions::setHasBorderInFullScreen(w->windowHandle(), true); #endif QTimer::singleShot(1000, this, SLOT(checkSanity())); { using namespace std::placeholders; // For _1 placeholder std::function callback( std::bind(&KisMainWindow::switchTab, this, _1)); d->tabSwitchCompressor.reset( new KisSignalCompressorWithParam(500, callback, KisSignalCompressor::FIRST_INACTIVE)); } } void KisMainWindow::setNoCleanup(bool noCleanup) { d->noCleanup = noCleanup; } KisMainWindow::~KisMainWindow() { // Q_FOREACH (QAction *ac, actionCollection()->actions()) { // QAction *action = qobject_cast(ac); // if (action) { // dbgKrita << "", "").replace("", "") // << "iconText=" << action->iconText().replace("&", "&") // << "shortcut=" << action->shortcut(QAction::ActiveShortcut).toString() // << "defaultShortcut=" << action->shortcut(QAction::DefaultShortcut).toString() // << "isCheckable=" << QString((action->isChecked() ? "true" : "false")) // << "statusTip=" << action->statusTip() // << "/>" ; // } // else { // dbgKrita << "Got a QAction:" << ac->objectName(); // } // } // The doc and view might still exist (this is the case when closing the window) KisPart::instance()->removeMainWindow(this); if (d->noCleanup) return; delete d->viewManager; delete d; } void KisMainWindow::addView(KisView *view) { if (d->activeView == view) return; if (d->activeView) { d->activeView->disconnect(this); } // register the newly created view in the input manager viewManager()->inputManager()->addTrackedCanvas(view->canvasBase()); showView(view); updateCaption(); emit restoringDone(); if (d->activeView) { connect(d->activeView, SIGNAL(titleModified(QString,bool)), SLOT(slotDocumentTitleModified())); connect(d->viewManager->statusBar(), SIGNAL(memoryStatusUpdated()), this, SLOT(updateCaption())); } } void KisMainWindow::notifyChildViewDestroyed(KisView *view) { viewManager()->inputManager()->removeTrackedCanvas(view->canvasBase()); if (view->canvasBase() == viewManager()->canvasBase()) { viewManager()->setCurrentView(0); } } void KisMainWindow::showView(KisView *imageView) { if (imageView && activeView() != imageView) { // XXX: find a better way to initialize this! imageView->setViewManager(d->viewManager); imageView->canvasBase()->setFavoriteResourceManager(d->viewManager->paintOpBox()->favoriteResourcesManager()); imageView->slotLoadingFinished(); QMdiSubWindow *subwin = d->mdiArea->addSubWindow(imageView); subwin->setAttribute(Qt::WA_DeleteOnClose, true); connect(subwin, SIGNAL(destroyed()), SLOT(updateWindowMenu())); KisConfig cfg; subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setWindowIcon(qApp->windowIcon()); /** * Hack alert! * * Here we explicitly request KoToolManager to emit all the tool * activation signals, to reinitialize the tool options docker. * * That is needed due to a design flaw we have in the * initialization procedure. The tool in the KoToolManager is * initialized in KisView::setViewManager() calls, which * happens early enough. During this call the tool manager * requests KoCanvasControllerWidget to emit the signal to * update the widgets in the tool docker. *But* at that moment * of time the view is not yet connected to the main window, * because it happens in KisViewManager::setCurrentView a bit * later. This fact makes the widgets updating signals be lost * and never reach the tool docker. * * So here we just explicitly call the tool activation stub. */ KoToolManager::instance()->initializeCurrentToolForCanvas(); if (d->mdiArea->subWindowList().size() == 1) { imageView->showMaximized(); } else { imageView->show(); } // No, no, no: do not try to call this _before_ the show() has // been called on the view; only when that has happened is the // opengl context active, and very bad things happen if we tell // the dockers to update themselves with a view if the opengl // context is not active. setActiveView(imageView); updateWindowMenu(); updateCaption(); } } void KisMainWindow::slotPreferences() { if (KisDlgPreferences::editPreferences()) { KisConfigNotifier::instance()->notifyConfigChanged(); KisUpdateSchedulerConfigNotifier::instance()->notifyConfigChanged(); // XXX: should this be changed for the views in other windows as well? Q_FOREACH (QPointer koview, KisPart::instance()->views()) { KisViewManager *view = qobject_cast(koview); if (view) { // Update the settings for all nodes -- they don't query // KisConfig directly because they need the settings during // compositing, and they don't connect to the config notifier // because nodes are not QObjects (because only one base class // can be a QObject). KisNode* node = dynamic_cast(view->image()->rootLayer().data()); node->updateSettings(); } } d->viewManager->showHideScrollbars(); } } void KisMainWindow::slotThemeChanged() { // save theme changes instantly KConfigGroup group( KSharedConfig::openConfig(), "theme"); group.writeEntry("Theme", d->themeManager->currentThemeName()); // reload action icons! Q_FOREACH (QAction *action, actionCollection()->actions()) { KisIconUtils::updateIcon(action); } emit themeChanged(); } void KisMainWindow::updateReloadFileAction(KisDocument *doc) { Q_UNUSED(doc); // d->reloadFile->setEnabled(doc && !doc->url().isEmpty()); } void KisMainWindow::setReadWrite(bool readwrite) { d->saveAction->setEnabled(readwrite); d->importFile->setEnabled(readwrite); d->readOnly = !readwrite; updateCaption(); } void KisMainWindow::addRecentURL(const QUrl &url) { dbgUI << "KisMainWindow::addRecentURL url=" << url.toDisplayString(); // Add entry to recent documents list // (call coming from KisDocument because it must work with cmd line, template dlg, file/open, etc.) if (!url.isEmpty()) { bool ok = true; if (url.isLocalFile()) { QString path = url.adjusted(QUrl::StripTrailingSlash).toLocalFile(); const QStringList tmpDirs = KoResourcePaths::resourceDirs("tmp"); for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it) if (path.contains(*it)) ok = false; // it's in the tmp resource #ifdef HAVE_KIO if (ok) { KRecentDocument::add(QUrl::fromLocalFile(path)); } #endif } #ifdef HAVE_KIO else { KRecentDocument::add(url.adjusted(QUrl::StripTrailingSlash)); } #endif if (ok) { d->recentFiles->addUrl(url); } saveRecentFiles(); } } void KisMainWindow::saveRecentFiles() { // Save list of recent files KSharedConfigPtr config = KSharedConfig::openConfig(); d->recentFiles->saveEntries(config->group("RecentFiles")); config->sync(); // Tell all windows to reload their list, after saving // Doesn't work multi-process, but it's a start Q_FOREACH (KMainWindow* window, KMainWindow::memberList()) { /** * FIXME: this is a hacking approach of reloading the updated recent files list. * Sometimes, the result of reading from KConfig right after doing 'sync()' still * returns old values of the recent files. Reading the same files a bit later * returns correct "updated" files. I couldn't find the cause of it (DK). */ KisMainWindow *mw = static_cast(window); if (mw != this) { mw->reloadRecentFileList(); } } } void KisMainWindow::reloadRecentFileList() { d->recentFiles->loadEntries( KSharedConfig::openConfig()->group("RecentFiles")); } void KisMainWindow::updateCaption() { if (!d->mdiArea->activeSubWindow()) { updateCaption(QString(), false); } else if (d->activeView && d->activeView->document() && d->activeView->image()){ KisDocument *doc = d->activeView->document(); QString caption(doc->caption()); if (d->readOnly) { caption += " [" + i18n("Write Protected") + "] "; } if (doc->isRecovered()) { caption += " [" + i18n("Recovered") + "] "; } // new documents aren't saved yet, so we don't need to say it is modified // new files don't have a URL, so we are using that for the check if (!doc->url().isEmpty()) { if ( doc->isModified()) { caption += " [" + i18n("Modified") + "] "; } } // show the file size for the document KisMemoryStatisticsServer::Statistics m_fileSizeStats = KisMemoryStatisticsServer::instance()->fetchMemoryStatistics(d->activeView ? d->activeView->image() : 0); if (m_fileSizeStats.imageSize) { caption += QString(" (").append( KisStatusBar::formatSize(m_fileSizeStats.imageSize)).append( ")"); } d->activeView->setWindowTitle(caption); d->activeView->setWindowModified(doc->isModified()); updateCaption(caption, doc->isModified()); if (!doc->url().fileName().isEmpty()) d->saveAction->setToolTip(i18n("Save as %1", doc->url().fileName())); else d->saveAction->setToolTip(i18n("Save")); } } void KisMainWindow::updateCaption(const QString & caption, bool mod) { dbgUI << "KisMainWindow::updateCaption(" << caption << "," << mod << ")"; #ifdef KRITA_ALPHA setCaption(QString("ALPHA %1: %2").arg(KRITA_ALPHA).arg(caption), mod); return; #endif #ifdef KRITA_BETA setCaption(QString("BETA %1: %2").arg(KRITA_BETA).arg(caption), mod); return; #endif #ifdef KRITA_RC setCaption(QString("RELEASE CANDIDATE %1: %2").arg(KRITA_RC).arg(caption), mod); return; #endif setCaption(caption, mod); } KisView *KisMainWindow::activeView() const { if (d->activeView) { return d->activeView; } return 0; } bool KisMainWindow::openDocument(const QUrl &url, OpenFlags flags) { if (!QFile(url.toLocalFile()).exists()) { if (!flags && BatchMode) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("The file %1 does not exist.", url.url())); } d->recentFiles->removeUrl(url); //remove the file from the recent-opened-file-list saveRecentFiles(); return false; } return openDocumentInternal(url, flags); } bool KisMainWindow::openDocumentInternal(const QUrl &url, OpenFlags flags) { if (!url.isLocalFile()) { qWarning() << "KisMainWindow::openDocumentInternal. Not a local file:" << url; return false; } KisDocument *newdoc = KisPart::instance()->createDocument(); if (flags & BatchMode) { newdoc->setFileBatchMode(true); } d->firstTime = true; connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); connect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); KisDocument::OpenFlags openFlags = KisDocument::None; if (flags & RecoveryFile) { openFlags |= KisDocument::RecoveryFile; } bool openRet = !(flags & Import) ? newdoc->openUrl(url, openFlags) : newdoc->importDocument(url); if (!openRet) { delete newdoc; return false; } KisPart::instance()->addDocument(newdoc); updateReloadFileAction(newdoc); if (!QFileInfo(url.toLocalFile()).isWritable()) { setReadWrite(false); } return true; } KisView* KisMainWindow::addViewAndNotifyLoadingCompleted(KisDocument *document) { KisView *view = KisPart::instance()->createView(document, resourceManager(), actionCollection(), this); addView(view); emit guiLoadingFinished(); return view; } QStringList KisMainWindow::showOpenFileDialog(bool isImporting) { KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument"); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); dialog.setCaption(isImporting ? i18n("Import Images") : i18n("Open Images")); return dialog.filenames(); } // Separate from openDocument to handle async loading (remote URLs) void KisMainWindow::slotLoadCompleted() { KisDocument *newdoc = qobject_cast(sender()); if (newdoc && newdoc->image()) { addViewAndNotifyLoadingCompleted(newdoc); disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); emit loadCompleted(); } } void KisMainWindow::slotLoadCanceled(const QString & errMsg) { dbgUI << "KisMainWindow::slotLoadCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg); // ... can't delete the document, it's the one who emitted the signal... KisDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); } void KisMainWindow::slotSaveCanceled(const QString &errMsg) { dbgUI << "KisMainWindow::slotSaveCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg); slotSaveCompleted(); } void KisMainWindow::slotSaveCompleted() { dbgUI << "KisMainWindow::slotSaveCompleted"; KisDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); if (d->deferredClosingEvent) { KXmlGuiWindow::closeEvent(d->deferredClosingEvent); } } bool KisMainWindow::hackIsSaving() const { StdLockableWrapper wrapper(&d->savingEntryMutex); std::unique_lock> l(wrapper, std::try_to_lock); return !l.owns_lock(); } bool KisMainWindow::saveDocument(KisDocument *document, bool saveas, bool isExporting) { if (!document) { return true; } /** * Make sure that we cannot enter this method twice! * * The lower level functions may call processEvents() so * double-entry is quite possible to achieve. Here we try to lock * the mutex, and if it is failed, just cancel saving. */ StdLockableWrapper wrapper(&d->savingEntryMutex); std::unique_lock> l(wrapper, std::try_to_lock); if (!l.owns_lock()) return false; // no busy wait for saving because it is dangerous! KisDelayedSaveDialog dlg(document->image(), KisDelayedSaveDialog::SaveDialog, 0, this); dlg.blockIfImageIsBusy(); if (dlg.result() == KisDelayedSaveDialog::Rejected) { return false; } else if (dlg.result() == KisDelayedSaveDialog::Ignored) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("You are saving a file while the image is " "still rendering. The saved file may be " "incomplete or corrupted.\n\n" "Please select a location where the original " "file will not be overridden!")); saveas = true; } if (document->isRecovered()) { saveas = true; } bool reset_url; if (document->url().isEmpty()) { reset_url = true; saveas = true; } else { reset_url = false; } connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); connect(document, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); QUrl oldURL = document->url(); QString oldFile = document->localFilePath(); QByteArray nativeFormat = document->nativeFormatMimeType(); QByteArray oldMimeFormat = document->mimeType(); QUrl suggestedURL = document->url(); QStringList mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export); mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export); if (!mimeFilter.contains(oldMimeFormat)) { dbgUI << "KisMainWindow::saveDocument no export filter for" << oldMimeFormat; // --- don't setOutputMimeType in case the user cancels the Save As // dialog and then tries to just plain Save --- // suggest a different filename extension (yes, we fortunately don't all live in a world of magic :)) QString suggestedFilename = QFileInfo(suggestedURL.toLocalFile()).baseName(); if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name suggestedFilename = suggestedFilename + "." + KisMimeDatabase::suffixesForMimeType(KIS_MIME_TYPE).first(); suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename); suggestedURL.setPath(suggestedURL.path() + suggestedFilename); } // force the user to choose outputMimeType saveas = true; } bool ret = false; if (document->url().isEmpty() || isExporting || saveas) { // if you're just File/Save As'ing to change filter options you // don't want to be reminded about overwriting files etc. bool justChangingFilterOptions = false; KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveAs"); dialog.setCaption(isExporting ? i18n("Exporting") : i18n("Saving As")); //qDebug() << ">>>>>" << isExporting << d->lastExportLocation << d->lastExportedFormat << QString::fromLatin1(document->mimeType()); if (isExporting && !d->lastExportLocation.isEmpty()) { // Use the location where we last exported to, if it's set, as the opening location for the file dialog QString proposedPath = QFileInfo(d->lastExportLocation).absolutePath(); // If the document doesn't have a filename yet, use the title QString proposedFileName = suggestedURL.isEmpty() ? document->documentInfo()->aboutInfo("title") : QFileInfo(suggestedURL.toLocalFile()).baseName(); // Use the last mimetype we exported to by default QString proposedMimeType = d->lastExportedFormat.isEmpty() ? "" : d->lastExportedFormat; QString proposedExtension = KisMimeDatabase::suffixesForMimeType(proposedMimeType).first().remove("*,"); // Set the default dir: this overrides the one loaded from the config file, since we're exporting and the lastExportLocation is not empty dialog.setDefaultDir(proposedPath + "/" + proposedFileName + "." + proposedExtension, true); dialog.setMimeTypeFilters(mimeFilter, proposedMimeType); } else { // Get the last used location for saving KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString proposedPath = group.readEntry("SaveAs", ""); // if that is empty, get the last used location for loading if (proposedPath.isEmpty()) { proposedPath = group.readEntry("OpenDocument", ""); } // If that is empty, too, use the Pictures location. if (proposedPath.isEmpty()) { - proposedPath = QDesktopServices::storageLocation(QDesktopServices::PicturesLocation); + proposedPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); } // But only use that if the suggestedUrl, that is, the document's own url is empty, otherwise // open the location where the document currently is. dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : suggestedURL.toLocalFile(), true); // If exporting, default to all supported file types if user is exporting QByteArray default_mime_type = ""; if (!isExporting) { // otherwise use the document's mimetype, or if that is empty, kra, which is the savest. default_mime_type = document->mimeType().isEmpty() ? nativeFormat : document->mimeType(); } dialog.setMimeTypeFilters(mimeFilter, QString::fromLatin1(default_mime_type)); } QUrl newURL = QUrl::fromUserInput(dialog.filename()); if (newURL.isLocalFile()) { QString fn = newURL.toLocalFile(); if (QFileInfo(fn).completeSuffix().isEmpty()) { fn.append(KisMimeDatabase::suffixesForMimeType(nativeFormat).first()); newURL = QUrl::fromLocalFile(fn); } } if (document->documentInfo()->aboutInfo("title") == i18n("Unnamed")) { QString fn = newURL.toLocalFile(); QFileInfo info(fn); document->documentInfo()->setAboutInfo("title", info.baseName()); } QByteArray outputFormat = nativeFormat; QString outputFormatString = KisMimeDatabase::mimeTypeForFile(newURL.toLocalFile()); outputFormat = outputFormatString.toLatin1(); if (!isExporting) { justChangingFilterOptions = (newURL == document->url()) && (outputFormat == document->mimeType()); } else { QString path = QFileInfo(d->lastExportLocation).absolutePath(); QString filename = QFileInfo(document->url().toLocalFile()).baseName(); justChangingFilterOptions = (QFileInfo(newURL.toLocalFile()).absolutePath() == path) && (QFileInfo(newURL.toLocalFile()).baseName() == filename) && (outputFormat == d->lastExportedFormat); } bool bOk = true; if (newURL.isEmpty()) { bOk = false; } if (bOk) { bool wantToSave = true; // don't change this line unless you know what you're doing :) if (!justChangingFilterOptions) { if (!document->isNativeFormat(outputFormat)) wantToSave = true; } if (wantToSave) { if (!isExporting) { // Save As ret = document->saveAs(newURL, outputFormat, true); if (ret) { dbgUI << "Successful Save As!"; KisPart::instance()->addRecentURLToAllMainWindows(newURL); setReadWrite(true); } else { dbgUI << "Failed Save As!"; document->setUrl(oldURL); document->setLocalFilePath(oldFile); } } else { // Export ret = document->exportDocument(newURL, outputFormat); if (ret) { d->lastExportLocation = newURL.toLocalFile(); d->lastExportedFormat = outputFormat; } } } // if (wantToSave) { else ret = false; } // if (bOk) { else ret = false; } else { // saving // We cannot "export" into the currently // opened document. We are not Gimp. KIS_ASSERT_RECOVER_NOOP(!isExporting); // be sure document has the correct outputMimeType! if (document->isModified()) { ret = document->save(true, 0); } if (!ret) { dbgUI << "Failed Save!"; document->setUrl(oldURL); document->setLocalFilePath(oldFile); } } if (ret && !isExporting) { document->setRecovered(false); } if (!ret && reset_url) document->resetURL(); //clean the suggested filename as the save dialog was rejected updateReloadFileAction(document); updateCaption(); return ret; } void KisMainWindow::undo() { if (activeView()) { activeView()->undoAction()->trigger(); d->undo->setText(activeView()->undoAction()->text()); } } void KisMainWindow::redo() { if (activeView()) { activeView()->redoAction()->trigger(); d->redo->setText(activeView()->redoAction()->text()); } } void KisMainWindow::closeEvent(QCloseEvent *e) { d->mdiArea->closeAllSubWindows(); QAction *action= d->viewManager->actionCollection()->action("view_show_canvas_only"); if ((action) && (action->isChecked())) { action->setChecked(false); } KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); cfg.writeEntry("ko_geometry", saveGeometry().toBase64()); cfg.writeEntry("ko_windowstate", saveState().toBase64()); { KConfigGroup group( KSharedConfig::openConfig(), "theme"); group.writeEntry("Theme", d->themeManager->currentThemeName()); } QList childrenList = d->mdiArea->subWindowList(); if (childrenList.isEmpty()) { d->deferredClosingEvent = e; if (!d->dockerStateBeforeHiding.isEmpty()) { restoreState(d->dockerStateBeforeHiding); } statusBar()->setVisible(true); menuBar()->setVisible(true); saveWindowSettings(); if (d->noCleanup) return; if (!d->dockWidgetVisibilityMap.isEmpty()) { // re-enable dockers for persistency Q_FOREACH (QDockWidget* dockWidget, d->dockWidgetsMap) dockWidget->setVisible(d->dockWidgetVisibilityMap.value(dockWidget)); } } else { e->setAccepted(false); } } void KisMainWindow::saveWindowSettings() { KSharedConfigPtr config = KSharedConfig::openConfig(); if (d->windowSizeDirty ) { dbgUI << "KisMainWindow::saveWindowSettings"; KConfigGroup group = config->group("MainWindow"); KWindowConfig::saveWindowSize(windowHandle(), group); config->sync(); d->windowSizeDirty = false; } if (!d->activeView || d->activeView->document()) { // Save toolbar position into the config file of the app, under the doc's component name KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); // Save collapsable state of dock widgets for (QMap::const_iterator i = d->dockWidgetsMap.constBegin(); i != d->dockWidgetsMap.constEnd(); ++i) { if (i.value()->widget()) { KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key()); dockGroup.writeEntry("Collapsed", i.value()->widget()->isHidden()); dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool()); dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value())); dockGroup.writeEntry("xPosition", (int) i.value()->widget()->x()); dockGroup.writeEntry("yPosition", (int) i.value()->widget()->y()); dockGroup.writeEntry("width", (int) i.value()->widget()->width()); dockGroup.writeEntry("height", (int) i.value()->widget()->height()); } } } KSharedConfig::openConfig()->sync(); resetAutoSaveSettings(); // Don't let KMainWindow override the good stuff we wrote down } void KisMainWindow::resizeEvent(QResizeEvent * e) { d->windowSizeDirty = true; KXmlGuiWindow::resizeEvent(e); } void KisMainWindow::setActiveView(KisView* view) { d->activeView = view; updateCaption(); actionCollection()->action("edit_undo")->setText(activeView()->undoAction()->text()); actionCollection()->action("edit_redo")->setText(activeView()->redoAction()->text()); d->viewManager->setCurrentView(view); } void KisMainWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/x-krita-node") || event->mimeData()->hasFormat("application/x-qt-image")) { event->accept(); } } void KisMainWindow::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) { Q_FOREACH (const QUrl &url, event->mimeData()->urls()) { openDocument(url, None); } } } void KisMainWindow::dragMoveEvent(QDragMoveEvent * event) { QTabBar *tabBar = d->findTabBarHACK(); if (!tabBar && d->mdiArea->viewMode() == QMdiArea::TabbedView) { qWarning() << "WARNING!!! Cannot find QTabBar in the main window! Looks like Qt has changed behavior. Drag & Drop between multiple tabs might not work properly (tabs will not switch automatically)!"; } if (tabBar && tabBar->isVisible()) { QPoint pos = tabBar->mapFromGlobal(mapToGlobal(event->pos())); if (tabBar->rect().contains(pos)) { const int tabIndex = tabBar->tabAt(pos); if (tabIndex >= 0 && tabBar->currentIndex() != tabIndex) { d->tabSwitchCompressor->start(tabIndex); } } else if (d->tabSwitchCompressor->isActive()) { d->tabSwitchCompressor->stop(); } } } void KisMainWindow::dragLeaveEvent(QDragLeaveEvent * /*event*/) { if (d->tabSwitchCompressor->isActive()) { d->tabSwitchCompressor->stop(); } } void KisMainWindow::switchTab(int index) { QTabBar *tabBar = d->findTabBarHACK(); if (!tabBar) return; tabBar->setCurrentIndex(index); } void KisMainWindow::slotFileNew() { const QStringList mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Import); KisOpenPane *startupWidget = new KisOpenPane(this, mimeFilter, QStringLiteral("templates/")); startupWidget->setWindowModality(Qt::WindowModal); KisConfig cfg; int w = cfg.defImageWidth(); int h = cfg.defImageHeight(); const double resolution = cfg.defImageResolution(); const QString colorModel = cfg.defColorModel(); const QString colorDepth = cfg.defaultColorDepth(); const QString colorProfile = cfg.defColorProfile(); CustomDocumentWidgetItem item; item.widget = new KisCustomImageWidget(startupWidget, w, h, resolution, colorModel, colorDepth, colorProfile, i18n("Unnamed")); item.icon = "document-new"; startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon); QSize sz = KisClipboard::instance()->clipSize(); if (sz.isValid() && sz.width() != 0 && sz.height() != 0) { w = sz.width(); h = sz.height(); } item.widget = new KisImageFromClipboard(startupWidget, w, h, resolution, colorModel, colorDepth, colorProfile, i18n("Unnamed")); item.title = i18n("Create from Clipboard"); item.icon = "tab-new"; startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon); // calls deleteLater connect(startupWidget, SIGNAL(documentSelected(KisDocument*)), KisPart::instance(), SLOT(startCustomDocument(KisDocument*))); // calls deleteLater connect(startupWidget, SIGNAL(openTemplate(const QUrl&)), KisPart::instance(), SLOT(openTemplate(const QUrl&))); startupWidget->exec(); // Cancel calls deleteLater... } void KisMainWindow::slotImportFile() { dbgUI << "slotImportFile()"; slotFileOpen(true); } void KisMainWindow::slotFileOpen(bool isImporting) { QStringList urls = showOpenFileDialog(isImporting); if (urls.isEmpty()) return; Q_FOREACH (const QString& url, urls) { if (!url.isEmpty()) { OpenFlags flags = isImporting ? Import : None; bool res = openDocument(QUrl::fromLocalFile(url), flags); if (!res) { warnKrita << "Loading" << url << "failed"; } } } } void KisMainWindow::slotFileOpenRecent(const QUrl &url) { (void) openDocument(QUrl::fromLocalFile(url.toLocalFile()), None); } void KisMainWindow::slotFileSave() { if (saveDocument(d->activeView->document(), false, false)) { emit documentSaved(); } } void KisMainWindow::slotFileSaveAs() { if (saveDocument(d->activeView->document(), true, false)) { emit documentSaved(); } } void KisMainWindow::slotExportFile() { if (saveDocument(d->activeView->document(), true, true)) { emit documentSaved(); } } KoCanvasResourceManager *KisMainWindow::resourceManager() const { return d->viewManager->resourceProvider()->resourceManager(); } int KisMainWindow::viewCount() const { return d->mdiArea->subWindowList().size(); } bool KisMainWindow::restoreWorkspace(const QByteArray &state) { QByteArray oldState = saveState(); const bool showTitlebars = KisConfig().showDockerTitleBars(); // needed because otherwise the layout isn't correctly restored in some situations Q_FOREACH (QDockWidget *dock, dockWidgets()) { dock->hide(); dock->titleBarWidget()->setVisible(showTitlebars); } bool success = KXmlGuiWindow::restoreState(state); if (!success) { KXmlGuiWindow::restoreState(oldState); Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating()); } } return false; } Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget(); dock->titleBarWidget()->setVisible(showTitlebars || (dock->isFloating() && isCollapsed)); } } return success; } KisViewManager *KisMainWindow::viewManager() const { return d->viewManager; } void KisMainWindow::slotDocumentInfo() { if (!d->activeView->document()) return; KoDocumentInfo *docInfo = d->activeView->document()->documentInfo(); if (!docInfo) return; KoDocumentInfoDlg *dlg = d->activeView->document()->createDocumentInfoDialog(this, docInfo); if (dlg->exec()) { if (dlg->isDocumentSaved()) { d->activeView->document()->setModified(false); } else { d->activeView->document()->setModified(true); } d->activeView->document()->setTitleModified(); } delete dlg; } bool KisMainWindow::slotFileCloseAll() { Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { if (subwin) { if(!subwin->close()) return false; } } updateCaption(); return true; } void KisMainWindow::slotFileQuit() { if(!slotFileCloseAll()) return; close(); Q_FOREACH (QPointer mainWin, KisPart::instance()->mainWindows()) { if (mainWin != this) { if(!mainWin->slotFileCloseAll()) return; mainWin->close(); } } } void KisMainWindow::slotFilePrint() { if (!activeView()) return; KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return; applyDefaultSettings(printJob->printer()); QPrintDialog *printDialog = activeView()->createPrintDialog( printJob, this ); if (printDialog && printDialog->exec() == QDialog::Accepted) { printJob->printer().setPageMargins(0.0, 0.0, 0.0, 0.0, QPrinter::Point); printJob->printer().setPaperSize(QSizeF(activeView()->image()->width() / (72.0 * activeView()->image()->xRes()), activeView()->image()->height()/ (72.0 * activeView()->image()->yRes())), QPrinter::Inch); printJob->startPrinting(KisPrintJob::DeleteWhenDone); } else { delete printJob; } delete printDialog; } void KisMainWindow::slotFilePrintPreview() { if (!activeView()) return; KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return; /* Sets the startPrinting() slot to be blocking. The Qt print-preview dialog requires the printing to be completely blocking and only return when the full document has been printed. By default the KisPrintingDialog is non-blocking and multithreading, setting blocking to true will allow it to be used in the preview dialog */ printJob->setProperty("blocking", true); QPrintPreviewDialog *preview = new QPrintPreviewDialog(&printJob->printer(), this); printJob->setParent(preview); // will take care of deleting the job connect(preview, SIGNAL(paintRequested(QPrinter*)), printJob, SLOT(startPrinting())); preview->exec(); delete preview; } KisPrintJob* KisMainWindow::exportToPdf(QString pdfFileName) { if (!activeView()) return 0; if (!activeView()->document()) return 0; KoPageLayout pageLayout; pageLayout.width = 0; pageLayout.height = 0; pageLayout.topMargin = 0; pageLayout.bottomMargin = 0; pageLayout.leftMargin = 0; pageLayout.rightMargin = 0; if (pdfFileName.isEmpty()) { KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString defaultDir = group.readEntry("SavePdfDialog"); if (defaultDir.isEmpty()) - defaultDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); + defaultDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QUrl startUrl = QUrl::fromLocalFile(defaultDir); KisDocument* pDoc = d->activeView->document(); /** if document has a file name, take file name and replace extension with .pdf */ if (pDoc && pDoc->url().isValid()) { startUrl = pDoc->url(); QString fileName = startUrl.toLocalFile(); fileName = fileName.replace( QRegExp( "\\.\\w{2,5}$", Qt::CaseInsensitive ), ".pdf" ); startUrl = startUrl.adjusted(QUrl::RemoveFilename); startUrl.setPath(startUrl.path() + fileName ); } QPointer layoutDlg(new KoPageLayoutDialog(this, pageLayout)); layoutDlg->setWindowModality(Qt::WindowModal); if (layoutDlg->exec() != QDialog::Accepted || !layoutDlg) { delete layoutDlg; return 0; } pageLayout = layoutDlg->pageLayout(); delete layoutDlg; KoFileDialog dialog(this, KoFileDialog::SaveFile, "OpenDocument"); dialog.setCaption(i18n("Export as PDF")); dialog.setDefaultDir(startUrl.toLocalFile()); dialog.setMimeTypeFilters(QStringList() << "application/pdf"); QUrl url = QUrl::fromUserInput(dialog.filename()); pdfFileName = url.toLocalFile(); if (pdfFileName.isEmpty()) return 0; } KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return 0; if (isHidden()) { printJob->setProperty("noprogressdialog", true); } applyDefaultSettings(printJob->printer()); // TODO for remote files we have to first save locally and then upload. printJob->printer().setOutputFileName(pdfFileName); printJob->printer().setDocName(pdfFileName); printJob->printer().setColorMode(QPrinter::Color); if (pageLayout.format == KoPageFormat::CustomSize) { printJob->printer().setPaperSize(QSizeF(pageLayout.width, pageLayout.height), QPrinter::Millimeter); } else { printJob->printer().setPaperSize(KoPageFormat::printerPageSize(pageLayout.format)); } printJob->printer().setPageMargins(pageLayout.leftMargin, pageLayout.topMargin, pageLayout.rightMargin, pageLayout.bottomMargin, QPrinter::Millimeter); switch (pageLayout.orientation) { case KoPageFormat::Portrait: printJob->printer().setOrientation(QPrinter::Portrait); break; case KoPageFormat::Landscape: printJob->printer().setOrientation(QPrinter::Landscape); break; } //before printing check if the printer can handle printing if (!printJob->canPrint()) { QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Cannot export to the specified file")); } printJob->startPrinting(KisPrintJob::DeleteWhenDone); return printJob; } void KisMainWindow::importAnimation() { if (!activeView()) return; KisDocument *document = activeView()->document(); if (!document) return; KisDlgImportImageSequence dlg(this, document); if (dlg.exec() == QDialog::Accepted) { QStringList files = dlg.files(); int firstFrame = dlg.firstFrame(); int step = dlg.step(); KoUpdaterPtr updater = !document->fileBatchMode() ? viewManager()->createUnthreadedUpdater(i18n("Import frames")) : 0; KisAnimationImporter importer(document->image(), updater); KisImportExportFilter::ConversionStatus status = importer.import(files, firstFrame, step); if (status != KisImportExportFilter::OK && status != KisImportExportFilter::InternalError) { QString msg = KisImportExportFilter::conversionStatusString(status); if (!msg.isEmpty()) QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not finish import animation:\n%1", msg)); } activeView()->canvasBase()->refetchDataFromImage(); } } void KisMainWindow::slotConfigureToolbars() { KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); KEditToolBar edit(factory(), this); connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig())); (void) edit.exec(); applyToolBarLayout(); } void KisMainWindow::slotNewToolbarConfig() { applyMainWindowSettings(KSharedConfig::openConfig()->group("krita")); KXMLGUIFactory *factory = guiFactory(); Q_UNUSED(factory); // Check if there's an active view if (!d->activeView) return; plugActionList("toolbarlist", d->toolbarList); applyToolBarLayout(); } void KisMainWindow::slotToolbarToggled(bool toggle) { //dbgUI <<"KisMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true; // The action (sender) and the toolbar have the same name KToolBar * bar = toolBar(sender()->objectName()); if (bar) { if (toggle) { bar->show(); } else { bar->hide(); } if (d->activeView && d->activeView->document()) { KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); } } else warnUI << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!"; } void KisMainWindow::viewFullscreen(bool fullScreen) { KisConfig cfg; cfg.setFullscreenMode(fullScreen); if (fullScreen) { setWindowState(windowState() | Qt::WindowFullScreen); // set } else { setWindowState(windowState() & ~Qt::WindowFullScreen); // reset } } void KisMainWindow::setMaxRecentItems(uint _number) { d->recentFiles->setMaxItems(_number); } void KisMainWindow::slotReloadFile() { KisDocument* document = d->activeView->document(); if (!document || document->url().isEmpty()) return; if (document->isModified()) { bool ok = QMessageBox::question(this, i18nc("@title:window", "Krita"), i18n("You will lose all changes made since your last save\n" "Do you want to continue?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes; if (!ok) return; } QUrl url = document->url(); saveWindowSettings(); if (!document->reload()) { QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Error: Could not reload this document")); } return; } QDockWidget* KisMainWindow::createDockWidget(KoDockFactoryBase* factory) { QDockWidget* dockWidget = 0; if (!d->dockWidgetsMap.contains(factory->id())) { dockWidget = factory->createDockWidget(); // It is quite possible that a dock factory cannot create the dock; don't // do anything in that case. if (!dockWidget) { warnKrita << "Could not create docker for" << factory->id(); return 0; } KoDockWidgetTitleBar *titleBar = dynamic_cast(dockWidget->titleBarWidget()); // Check if the dock widget is supposed to be collapsable if (!dockWidget->titleBarWidget()) { titleBar = new KoDockWidgetTitleBar(dockWidget); dockWidget->setTitleBarWidget(titleBar); titleBar->setCollapsable(factory->isCollapsable()); } titleBar->setFont(KoDockRegistry::dockFont()); dockWidget->setObjectName(factory->id()); dockWidget->setParent(this); if (dockWidget->widget() && dockWidget->widget()->layout()) dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1); Qt::DockWidgetArea side = Qt::RightDockWidgetArea; bool visible = true; switch (factory->defaultDockPosition()) { case KoDockFactoryBase::DockTornOff: dockWidget->setFloating(true); // position nicely? break; case KoDockFactoryBase::DockTop: side = Qt::TopDockWidgetArea; break; case KoDockFactoryBase::DockLeft: side = Qt::LeftDockWidgetArea; break; case KoDockFactoryBase::DockBottom: side = Qt::BottomDockWidgetArea; break; case KoDockFactoryBase::DockRight: side = Qt::RightDockWidgetArea; break; case KoDockFactoryBase::DockMinimized: default: side = Qt::RightDockWidgetArea; visible = false; } KConfigGroup group = KSharedConfig::openConfig()->group("krita").group("DockWidget " + factory->id()); side = static_cast(group.readEntry("DockArea", static_cast(side))); if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea; addDockWidget(side, dockWidget); if (!visible) { dockWidget->hide(); } bool collapsed = factory->defaultCollapsed(); bool locked = false; group = KSharedConfig::openConfig()->group("krita").group("DockWidget " + factory->id()); collapsed = group.readEntry("Collapsed", collapsed); locked = group.readEntry("Locked", locked); //dbgKrita << "docker" << factory->id() << dockWidget << "collapsed" << collapsed << "locked" << locked << "titlebar" << titleBar; if (titleBar && collapsed) titleBar->setCollapsed(true); if (titleBar && locked) titleBar->setLocked(true); d->dockWidgetsMap.insert(factory->id(), dockWidget); } else { dockWidget = d->dockWidgetsMap[factory->id()]; } #ifdef Q_OS_OSX dockWidget->setAttribute(Qt::WA_MacSmallSize, true); #endif dockWidget->setFont(KoDockRegistry::dockFont()); connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts())); return dockWidget; } void KisMainWindow::forceDockTabFonts() { Q_FOREACH (QObject *child, children()) { if (child->inherits("QTabBar")) { ((QTabBar *)child)->setFont(KoDockRegistry::dockFont()); } } } QList KisMainWindow::dockWidgets() const { return d->dockWidgetsMap.values(); } QDockWidget* KisMainWindow::dockWidget(const QString &id) { if (!d->dockWidgetsMap.contains(id)) return 0; return d->dockWidgetsMap[id]; } QList KisMainWindow::canvasObservers() const { QList observers; Q_FOREACH (QDockWidget *docker, dockWidgets()) { KoCanvasObserverBase *observer = dynamic_cast(docker); if (observer) { observers << observer; } else { warnKrita << docker << "is not a canvas observer"; } } return observers; } void KisMainWindow::toggleDockersVisibility(bool visible) { if (!visible) { d->dockerStateBeforeHiding = saveState(); Q_FOREACH (QObject* widget, children()) { if (widget->inherits("QDockWidget")) { QDockWidget* dw = static_cast(widget); if (dw->isVisible()) { dw->hide(); } } } } else { restoreState(d->dockerStateBeforeHiding); } } void KisMainWindow::slotDocumentTitleModified() { updateCaption(); updateReloadFileAction(d->activeView ? d->activeView->document() : 0); } void KisMainWindow::subWindowActivated() { bool enabled = (activeKisView() != 0); d->mdiCascade->setEnabled(enabled); d->mdiNextWindow->setEnabled(enabled); d->mdiPreviousWindow->setEnabled(enabled); d->mdiTile->setEnabled(enabled); d->close->setEnabled(enabled); d->closeAll->setEnabled(enabled); setActiveSubWindow(d->mdiArea->activeSubWindow()); Q_FOREACH (QToolBar *tb, toolBars()) { if (tb->objectName() == "BrushesAndStuff") { tb->setEnabled(enabled); } } updateCaption(); d->actionManager()->updateGUI(); } void KisMainWindow::updateWindowMenu() { QMenu *menu = d->windowMenu->menu(); menu->clear(); menu->addAction(d->newWindow); menu->addAction(d->documentMenu); QMenu *docMenu = d->documentMenu->menu(); docMenu->clear(); Q_FOREACH (QPointer doc, KisPart::instance()->documents()) { if (doc) { QString title = doc->url().toDisplayString(); if (title.isEmpty() && doc->image()) { title = doc->image()->objectName(); } QAction *action = docMenu->addAction(title); action->setIcon(qApp->windowIcon()); connect(action, SIGNAL(triggered()), d->documentMapper, SLOT(map())); d->documentMapper->setMapping(action, doc); } } menu->addAction(d->workspaceMenu); QMenu *workspaceMenu = d->workspaceMenu->menu(); workspaceMenu->clear(); auto workspaces = KisResourceServerProvider::instance()->workspaceServer(false)->resources(); auto m_this = this; for (auto &w : workspaces) { auto action = workspaceMenu->addAction(w->name()); auto ds = w->dockerState(); connect(action, &QAction::triggered, this, [=]() { m_this->restoreWorkspace(ds); }); } workspaceMenu->addSeparator(); connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&Import Workspace...")), &QAction::triggered, this, [&]() { QString extensions = d->workspacemodel->extensions(); QStringList mimeTypes; for(const QString &suffix : extensions.split(":")) { mimeTypes << KisMimeDatabase::mimeTypeForSuffix(suffix); } KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); dialog.setMimeTypeFilters(mimeTypes); dialog.setCaption(i18nc("@title:window", "Choose File to Add")); QString filename = dialog.filename(); d->workspacemodel->importResourceFile(filename); }); connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&New Workspace...")), &QAction::triggered, [=]() { QString name = QInputDialog::getText(this, i18nc("@title:window", "New Workspace..."), i18nc("@label:textbox", "Name:")); if (name.isEmpty()) return; auto rserver = KisResourceServerProvider::instance()->workspaceServer(); KisWorkspaceResource* workspace = new KisWorkspaceResource(""); workspace->setDockerState(m_this->saveState()); d->viewManager->resourceProvider()->notifySavingWorkspace(workspace); workspace->setValid(true); QString saveLocation = rserver->saveLocation(); bool newName = false; if(name.isEmpty()) { newName = true; name = i18n("Workspace"); } QFileInfo fileInfo(saveLocation + name + workspace->defaultFileExtension()); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(saveLocation + name + QString("%1").arg(i) + workspace->defaultFileExtension()); i++; } workspace->setFilename(fileInfo.filePath()); if(newName) { name = i18n("Workspace %1", i); } workspace->setName(name); rserver->addResource(workspace); }); // TODO: What to do about delete? // workspaceMenu->addAction(i18nc("@action:inmenu", "&Delete Workspace...")); menu->addSeparator(); menu->addAction(d->close); menu->addAction(d->closeAll); if (d->mdiArea->viewMode() == QMdiArea::SubWindowView) { menu->addSeparator(); menu->addAction(d->mdiTile); menu->addAction(d->mdiCascade); } menu->addSeparator(); menu->addAction(d->mdiNextWindow); menu->addAction(d->mdiPreviousWindow); menu->addSeparator(); QList windows = d->mdiArea->subWindowList(); for (int i = 0; i < windows.size(); ++i) { QPointerchild = qobject_cast(windows.at(i)->widget()); if (child && child->document()) { QString text; if (i < 9) { text = i18n("&%1 %2", i + 1, child->document()->url().toDisplayString()); } else { text = i18n("%1 %2", i + 1, child->document()->url().toDisplayString()); } QAction *action = menu->addAction(text); action->setIcon(qApp->windowIcon()); action->setCheckable(true); action->setChecked(child == activeKisView()); connect(action, SIGNAL(triggered()), d->windowMapper, SLOT(map())); d->windowMapper->setMapping(action, windows.at(i)); } } updateCaption(); } void KisMainWindow::setActiveSubWindow(QWidget *window) { if (!window) return; QMdiSubWindow *subwin = qobject_cast(window); //dbgKrita << "setActiveSubWindow();" << subwin << d->activeSubWindow; if (subwin && subwin != d->activeSubWindow) { KisView *view = qobject_cast(subwin->widget()); //dbgKrita << "\t" << view << activeView(); if (view && view != activeView()) { d->mdiArea->setActiveSubWindow(subwin); setActiveView(view); } d->activeSubWindow = subwin; } updateWindowMenu(); d->actionManager()->updateGUI(); } void KisMainWindow::configChanged() { KisConfig cfg; QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView); d->mdiArea->setViewMode(viewMode); Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); /** * Dirty workaround for a bug in Qt (checked on Qt 5.6.1): * * If you make a window "Show on top" and then switch to the tabbed mode * the window will contiue to be painted in its initial "mid-screen" * position. It will persist here until you explicitly switch to its tab. */ if (viewMode == QMdiArea::TabbedView) { Qt::WindowFlags oldFlags = subwin->windowFlags(); Qt::WindowFlags flags = oldFlags; flags &= ~Qt::WindowStaysOnTopHint; flags &= ~Qt::WindowStaysOnBottomHint; if (flags != oldFlags) { subwin->setWindowFlags(flags); subwin->showMaximized(); } } } KConfigGroup group( KSharedConfig::openConfig(), "theme"); d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark")); d->actionManager()->updateGUI(); QBrush brush(cfg.getMDIBackgroundColor()); d->mdiArea->setBackground(brush); QString backgroundImage = cfg.getMDIBackgroundImage(); if (backgroundImage != "") { QImage image(backgroundImage); QBrush brush(image); d->mdiArea->setBackground(brush); } d->mdiArea->update(); } KisView* KisMainWindow::newView(QObject *document) { KisDocument *doc = qobject_cast(document); KisView *view = addViewAndNotifyLoadingCompleted(doc); d->actionManager()->updateGUI(); return view; } void KisMainWindow::newWindow() { KisPart::instance()->createMainWindow()->show(); } void KisMainWindow::closeCurrentWindow() { d->mdiArea->currentSubWindow()->close(); d->actionManager()->updateGUI(); } void KisMainWindow::checkSanity() { // print error if the lcms engine is not available if (!KoColorSpaceEngineRegistry::instance()->contains("icc")) { // need to wait 1 event since exiting here would not work. m_errorMessage = i18n("The Krita LittleCMS color management plugin is not installed. Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); if (rserver->resources().isEmpty()) { m_errorMessage = i18n("Krita cannot find any brush presets! Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } } void KisMainWindow::showErrorAndDie() { QMessageBox::critical(0, i18nc("@title:window", "Installation error"), m_errorMessage); if (m_dieOnError) { exit(10); } } void KisMainWindow::showAboutApplication() { KisAboutApplication dlg(this); dlg.exec(); } QPointerKisMainWindow::activeKisView() { if (!d->mdiArea) return 0; QMdiSubWindow *activeSubWindow = d->mdiArea->activeSubWindow(); //dbgKrita << "activeKisView" << activeSubWindow; if (!activeSubWindow) return 0; return qobject_cast(activeSubWindow->widget()); } void KisMainWindow::newOptionWidgets(KoCanvasController *controller, const QList > &optionWidgetList) { KIS_ASSERT_RECOVER_NOOP(controller == KoToolManager::instance()->activeCanvasController()); bool isOurOwnView = false; Q_FOREACH (QPointer view, KisPart::instance()->views()) { if (view && view->canvasController() == controller) { isOurOwnView = view->mainWindow() == this; } } if (!isOurOwnView) return; Q_FOREACH (QWidget *w, optionWidgetList) { #ifdef Q_OS_OSX w->setAttribute(Qt::WA_MacSmallSize, true); #endif w->setFont(KoDockRegistry::dockFont()); } if (d->toolOptionsDocker) { d->toolOptionsDocker->setOptionWidgets(optionWidgetList); } else { d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList); } } void KisMainWindow::applyDefaultSettings(QPrinter &printer) { if (!d->activeView) return; QString title = d->activeView->document()->documentInfo()->aboutInfo("title"); if (title.isEmpty()) { QFileInfo info(d->activeView->document()->url().fileName()); title = info.baseName(); } if (title.isEmpty()) { // #139905 title = i18n("%1 unsaved document (%2)", qApp->applicationDisplayName(), QLocale().toString(QDate::currentDate(), QLocale::ShortFormat)); } printer.setDocName(title); } void KisMainWindow::createActions() { KisActionManager *actionManager = d->actionManager(); KisConfig cfg; actionManager->createStandardAction(KStandardAction::New, this, SLOT(slotFileNew())); actionManager->createStandardAction(KStandardAction::Open, this, SLOT(slotFileOpen())); actionManager->createStandardAction(KStandardAction::Quit, this, SLOT(slotFileQuit())); actionManager->createStandardAction(KStandardAction::ConfigureToolbars, this, SLOT(slotConfigureToolbars())); actionManager->createStandardAction(KStandardAction::FullScreen, this, SLOT(viewFullscreen(bool))); d->recentFiles = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection()); connect(d->recentFiles, SIGNAL(recentListCleared()), this, SLOT(saveRecentFiles())); KSharedConfigPtr configPtr = KSharedConfig::openConfig(); d->recentFiles->loadEntries(configPtr->group("RecentFiles")); d->saveAction = actionManager->createStandardAction(KStandardAction::Save, this, SLOT(slotFileSave())); d->saveAction->setActivationFlags(KisAction::ACTIVE_IMAGE); d->saveActionAs = actionManager->createStandardAction(KStandardAction::SaveAs, this, SLOT(slotFileSaveAs())); d->saveActionAs->setActivationFlags(KisAction::ACTIVE_IMAGE); // d->printAction = actionManager->createStandardAction(KStandardAction::Print, this, SLOT(slotFilePrint())); // d->printAction->setActivationFlags(KisAction::ACTIVE_IMAGE); // d->printActionPreview = actionManager->createStandardAction(KStandardAction::PrintPreview, this, SLOT(slotFilePrintPreview())); // d->printActionPreview->setActivationFlags(KisAction::ACTIVE_IMAGE); d->undo = actionManager->createStandardAction(KStandardAction::Undo, this, SLOT(undo())); d->undo ->setActivationFlags(KisAction::ACTIVE_IMAGE); d->redo = actionManager->createStandardAction(KStandardAction::Redo, this, SLOT(redo())); d->redo->setActivationFlags(KisAction::ACTIVE_IMAGE); // d->exportPdf = actionManager->createAction("file_export_pdf"); // connect(d->exportPdf, SIGNAL(triggered()), this, SLOT(exportToPdf())); d->importAnimation = actionManager->createAction("file_import_animation"); connect(d->importAnimation, SIGNAL(triggered()), this, SLOT(importAnimation())); d->closeAll = actionManager->createAction("file_close_all"); connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll())); // d->reloadFile = actionManager->createAction("file_reload_file"); // d->reloadFile->setActivationFlags(KisAction::CURRENT_IMAGE_MODIFIED); // connect(d->reloadFile, SIGNAL(triggered(bool)), this, SLOT(slotReloadFile())); d->importFile = actionManager->createAction("file_import_file"); connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile())); d->exportFile = actionManager->createAction("file_export_file"); connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile())); /* The following entry opens the document information dialog. Since the action is named so it intends to show data this entry should not have a trailing ellipses (...). */ d->showDocumentInfo = actionManager->createAction("file_documentinfo"); connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo())); d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this)); d->themeManager->registerThemeActions(actionCollection()); connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); d->toggleDockers = actionManager->createAction("view_toggledockers"); cfg.showDockers(true); d->toggleDockers->setChecked(true); connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool))); d->toggleDockerTitleBars = actionManager->createAction("view_toggledockertitlebars"); d->toggleDockerTitleBars->setChecked(cfg.showDockerTitleBars()); connect(d->toggleDockerTitleBars, SIGNAL(toggled(bool)), SLOT(showDockerTitleBars(bool))); actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu); actionCollection()->addAction("window", d->windowMenu); d->mdiCascade = actionManager->createAction("windows_cascade"); connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows())); d->mdiTile = actionManager->createAction("windows_tile"); connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows())); d->mdiNextWindow = actionManager->createAction("windows_next"); connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow())); d->mdiPreviousWindow = actionManager->createAction("windows_previous"); connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow())); d->newWindow = actionManager->createAction("view_newwindow"); connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow())); d->close = actionManager->createAction("file_close"); connect(d->close, SIGNAL(triggered()), SLOT(closeCurrentWindow())); actionManager->createStandardAction(KStandardAction::Preferences, this, SLOT(slotPreferences())); for (int i = 0; i < 2; i++) { d->expandingSpacers[i] = new KisAction(i18n("Expanding Spacer")); d->expandingSpacers[i]->setDefaultWidget(new QWidget(this)); d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); actionManager->addAction(QString("expanding_spacer_%1").arg(i), d->expandingSpacers[i]); } } void KisMainWindow::applyToolBarLayout() { const bool isPlastiqueStyle = style()->objectName() == "plastique"; Q_FOREACH (KToolBar *toolBar, toolBars()) { toolBar->layout()->setSpacing(4); if (isPlastiqueStyle) { toolBar->setContentsMargins(0, 0, 0, 2); } //Hide text for buttons with an icon in the toolbar Q_FOREACH (QAction *ac, toolBar->actions()){ if (ac->icon().pixmap(QSize(1,1)).isNull() == false){ ac->setPriority(QAction::LowPriority); }else { ac->setIcon(QIcon()); } } } } void KisMainWindow::initializeGeometry() { // if the user didn's specify the geometry on the command line (does anyone do that still?), // we first figure out some good default size and restore the x,y position. See bug 285804Z. KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray())); if (!restoreGeometry(geom)) { const int scnum = QApplication::desktop()->screenNumber(parentWidget()); QRect desk = QApplication::desktop()->availableGeometry(scnum); // if the desktop is virtual then use virtual screen size if (QApplication::desktop()->isVirtualDesktop()) { desk = QApplication::desktop()->availableGeometry(QApplication::desktop()->screen(scnum)); } quint32 x = desk.x(); quint32 y = desk.y(); quint32 w = 0; quint32 h = 0; // Default size -- maximize on small screens, something useful on big screens const int deskWidth = desk.width(); if (deskWidth > 1024) { // a nice width, and slightly less than total available // height to componensate for the window decs w = (deskWidth / 3) * 2; h = (desk.height() / 3) * 2; } else { w = desk.width(); h = desk.height(); } x += (desk.width() - w) / 2; y += (desk.height() - h) / 2; move(x,y); setGeometry(geometry().x(), geometry().y(), w, h); } restoreWorkspace(QByteArray::fromBase64(cfg.readEntry("ko_windowstate", QByteArray()))); } void KisMainWindow::showManual() { QDesktopServices::openUrl(QUrl("https://docs.krita.org")); } void KisMainWindow::showDockerTitleBars(bool show) { Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget(); dock->titleBarWidget()->setVisible(show || (dock->isFloating() && isCollapsed)); } } KisConfig cfg; cfg.setShowDockerTitleBars(show); } void KisMainWindow::moveEvent(QMoveEvent *e) { if (qApp->desktop()->screenNumber(this) != qApp->desktop()->screenNumber(e->oldPos())) { KisConfigNotifier::instance()->notifyConfigChanged(); } } #include diff --git a/libs/ui/KisMultiFeedRSSModel.cpp b/libs/ui/KisMultiFeedRSSModel.cpp index 5a8c234c72..11028fdefe 100644 --- a/libs/ui/KisMultiFeedRSSModel.cpp +++ b/libs/ui/KisMultiFeedRSSModel.cpp @@ -1,209 +1,216 @@ /************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** **************************************************************************/ #include "KisMultiFeedRSSModel.h" #include #include #include #include #include #include #include #include QString shortenHtml(QString html) { html.replace(QLatin1String("")); uint firstParaEndHtml = (uint) html.indexOf(QLatin1String("

"), html.indexOf(QLatin1String("

"))+1); uint firstParaEndBr = (uint) html.indexOf(QLatin1String("request().url(); requestUrl = source.toString(); streamReader.setDevice(reply); RssItemList list; while (!streamReader.atEnd()) { switch (streamReader.readNext()) { case QXmlStreamReader::StartElement: if (streamReader.name() == QLatin1String("item")) list.append(parseItem()); else if (streamReader.name() == QLatin1String("title")) blogName = streamReader.readElementText(); else if (streamReader.name() == QLatin1String("link")) { if (!streamReader.namespaceUri().isEmpty()) break; QString favIconString(streamReader.readElementText()); QUrl favIconUrl(favIconString); favIconUrl.setPath(QLatin1String("favicon.ico")); blogIcon = favIconUrl.toString(); blogIcon = QString(); // XXX: fix the favicon on krita.org! } break; default: break; } } return list; } private: QXmlStreamReader streamReader; QString requestUrl; QString blogIcon; QString blogName; }; MultiFeedRssModel::MultiFeedRssModel(QObject *parent) : QAbstractListModel(parent), m_networkAccessManager(new KisNetworkAccessManager), m_articleCount(0) { connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(appendFeedData(QNetworkReply*)), Qt::QueuedConnection); +} + + + +MultiFeedRssModel::~MultiFeedRssModel() +{ +} + +QHash MultiFeedRssModel::roleNames() const +{ QHash roleNames; roleNames[TitleRole] = "title"; roleNames[DescriptionRole] = "description"; roleNames[PubDateRole] = "pubDate"; roleNames[LinkRole] = "link"; roleNames[BlogNameRole] = "blogName"; roleNames[BlogIconRole] = "blogIcon"; - setRoleNames(roleNames); -} - -MultiFeedRssModel::~MultiFeedRssModel() -{ + return roleNames; } void MultiFeedRssModel::addFeed(const QString& feed) { const QUrl feedUrl(feed); QMetaObject::invokeMethod(m_networkAccessManager, "getUrl", Qt::QueuedConnection, Q_ARG(QUrl, feedUrl)); } bool sortForPubDate(const RssItem& item1, const RssItem& item2) { return item1.pubDate > item2.pubDate; } void MultiFeedRssModel::appendFeedData(QNetworkReply *reply) { RssReader reader; m_aggregatedFeed.append(reader.parse(reply)); std::sort(m_aggregatedFeed.begin(), m_aggregatedFeed.end(), sortForPubDate); setArticleCount(m_aggregatedFeed.size()); - reset(); + beginResetModel(); + endResetModel(); } void MultiFeedRssModel::removeFeed(const QString &feed) { QMutableListIterator it(m_aggregatedFeed); while (it.hasNext()) { RssItem item = it.next(); if (item.source == feed) it.remove(); } setArticleCount(m_aggregatedFeed.size()); } int MultiFeedRssModel::rowCount(const QModelIndex &) const { return m_aggregatedFeed.size(); } QVariant MultiFeedRssModel::data(const QModelIndex &index, int role) const { RssItem item = m_aggregatedFeed.at(index.row()); switch (role) { case Qt::DisplayRole: // fall through case TitleRole: return item.title; case DescriptionRole: return item.description; case PubDateRole: return item.pubDate.toString("dd-MM-yyyy hh:mm"); case LinkRole: return item.link; case BlogNameRole: return item.blogName; case BlogIconRole: return item.blogIcon; } return QVariant(); } diff --git a/libs/ui/KisMultiFeedRSSModel.h b/libs/ui/KisMultiFeedRSSModel.h index d85db72e1b..c87773e46a 100644 --- a/libs/ui/KisMultiFeedRSSModel.h +++ b/libs/ui/KisMultiFeedRSSModel.h @@ -1,105 +1,106 @@ /************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** **************************************************************************/ #ifndef MULTIFEEDRSSMODEL_H #define MULTIFEEDRSSMODEL_H #include #include #include #include class QThread; class QNetworkReply; class QNetworkAccessManager; struct RssItem { QString source; QString title; QString link; QString description; QString blogName; QString blogIcon; QDateTime pubDate; }; typedef QList RssItemList; class KisNetworkAccessManager; enum RssRoles { TitleRole = Qt::UserRole + 1, DescriptionRole, LinkRole, PubDateRole, BlogNameRole, BlogIconRole }; class KRITAUI_EXPORT MultiFeedRssModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(int articleCount READ articleCount WRITE setArticleCount NOTIFY articleCountChanged) public: explicit MultiFeedRssModel(QObject *parent = 0); ~MultiFeedRssModel() override; + QHash roleNames() const; void addFeed(const QString& feed); void removeFeed(const QString& feed); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int articleCount() const { return m_articleCount; } public Q_SLOTS: void setArticleCount(int arg) { if (m_articleCount != arg) { m_articleCount = arg; emit articleCountChanged(arg); } } Q_SIGNALS: void articleCountChanged(int arg); private Q_SLOTS: void appendFeedData(QNetworkReply *reply); private: QStringList m_sites; RssItemList m_aggregatedFeed; QNetworkAccessManager *m_networkAccessManager; QThread *m_namThread; int m_articleCount; }; #endif // MULTIFEEDRSSMODEL_H diff --git a/libs/ui/KisOpenPane.cpp b/libs/ui/KisOpenPane.cpp index 0872b35fbb..1bafb75ba2 100644 --- a/libs/ui/KisOpenPane.cpp +++ b/libs/ui/KisOpenPane.cpp @@ -1,384 +1,382 @@ /* This file is part of the KDE project Copyright (C) 2005 Peter Simonsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; 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 "KisOpenPane.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 "KisTemplateTree.h" #include "KisTemplateGroup.h" #include "KisTemplate.h" #include "KisDetailsPane.h" #include "KisTemplatesPane.h" #include "ui_KisOpenPaneBase.h" #include #include #include class KoSectionListItem : public QTreeWidgetItem { public: KoSectionListItem(QTreeWidget* treeWidget, const QString& name, int sortWeight, int widgetIndex = -1) : QTreeWidgetItem(treeWidget, QStringList() << name), m_sortWeight(sortWeight), m_widgetIndex(widgetIndex) { Qt::ItemFlags newFlags = Qt::NoItemFlags; if(m_widgetIndex >= 0) newFlags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable; setFlags(newFlags); } bool operator<(const QTreeWidgetItem & other) const override { const KoSectionListItem* item = dynamic_cast(&other); if (!item) return 0; return ((item->sortWeight() - sortWeight()) < 0); } int sortWeight() const { return m_sortWeight; } int widgetIndex() const { return m_widgetIndex; } private: int m_sortWeight; int m_widgetIndex; }; class KoSectionListDelegate : public QStyledItemDelegate { public: KoSectionListDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) { } void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override { QStyledItemDelegate::paint(painter, option, index); if(!(option.state & (int)(QStyle::State_Active && QStyle::State_Enabled))) { int ypos = option.rect.y() + ((option.rect.height() - 2) / 2); QRect lineRect(option.rect.left(), ypos, option.rect.width(), 2); QLinearGradient gradient(option.rect.topLeft(), option.rect.bottomRight()); gradient.setColorAt(option.direction == Qt::LeftToRight ? 0 : 1, option.palette.color(QPalette::Text)); gradient.setColorAt(option.direction == Qt::LeftToRight ? 1 : 0, Qt::transparent); painter->fillRect(lineRect, gradient); } } }; class KisOpenPanePrivate : public Ui_KisOpenPaneBase { public: KisOpenPanePrivate() : Ui_KisOpenPaneBase() { m_templatesSeparator = 0; } int m_freeCustomWidgetIndex; KoSectionListItem* m_templatesSeparator; - + }; KisOpenPane::KisOpenPane(QWidget *parent, const QStringList& mimeFilter, const QString& templatesResourcePath) : QDialog(parent) , d(new KisOpenPanePrivate) { d->setupUi(this); m_mimeFilter = mimeFilter; KoSectionListDelegate* delegate = new KoSectionListDelegate(d->m_sectionList); d->m_sectionList->setItemDelegate(delegate); connect(d->m_sectionList, SIGNAL(itemSelectionChanged()), this, SLOT(updateSelectedWidget())); connect(d->m_sectionList, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(itemClicked(QTreeWidgetItem*))); connect(d->m_sectionList, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(itemClicked(QTreeWidgetItem*))); - + connect(d->cancelButton, SIGNAL(clicked()), this, SLOT(close())); connect(d->cancelButton, SIGNAL(clicked()), this, SLOT(deleteLater())); initTemplates(templatesResourcePath); d->m_freeCustomWidgetIndex = 4; if (!d->m_sectionList->selectedItems().isEmpty()) { KoSectionListItem* selectedItem = static_cast(d->m_sectionList->selectedItems().first()); if (selectedItem) { d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus(); } } QList sizes; // Set the sizes of the details pane splitters KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); sizes = cfgGrp.readEntry("DetailsPaneSplitterSizes", sizes); if (!sizes.isEmpty()) emit splitterResized(0, sizes); connect(this, SIGNAL(splitterResized(KisDetailsPane*, const QList&)), this, SLOT(saveSplitterSizes(KisDetailsPane*, const QList&))); setAcceptDrops(true); } KisOpenPane::~KisOpenPane() { if (!d->m_sectionList->selectedItems().isEmpty()) { KoSectionListItem* item = dynamic_cast(d->m_sectionList->selectedItems().first()); if (item) { if (!qobject_cast(d->m_widgetStack->widget(item->widgetIndex()))) { KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); cfgGrp.writeEntry("LastReturnType", item->text(0)); } } } delete d; } void KisOpenPane::openFileDialog() { KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocument"); dialog.setCaption(i18n("Open Existing Document")); - dialog.setDefaultDir(qApp->applicationName().contains("krita") || qApp->applicationName().contains("karbon") - ? QDesktopServices::storageLocation(QDesktopServices::PicturesLocation) - : QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(m_mimeFilter); Q_FOREACH (const QString &filename, dialog.filenames()) { emit openExistingFile(QUrl::fromUserInput(filename)); } } void KisOpenPane::initTemplates(const QString& templatesResourcePath) { QTreeWidgetItem* selectItem = 0; QTreeWidgetItem* firstItem = 0; const int templateOffset = 1000; if (!templatesResourcePath.isEmpty()) { KisTemplateTree templateTree(templatesResourcePath, true); Q_FOREACH (KisTemplateGroup *group, templateTree.groups()) { if (group->isHidden()) { continue; } if (!d->m_templatesSeparator) { d->m_templatesSeparator = new KoSectionListItem(d->m_sectionList, "", 999); } KisTemplatesPane* pane = new KisTemplatesPane(this, group->name(), group, templateTree.defaultTemplate()); connect(pane, SIGNAL(openUrl(const QUrl&)), this, SIGNAL(openTemplate(const QUrl&))); connect(pane, SIGNAL(alwaysUseChanged(KisTemplatesPane*, const QString&)), this, SIGNAL(alwaysUseChanged(KisTemplatesPane*, const QString&))); connect(this, SIGNAL(alwaysUseChanged(KisTemplatesPane*, const QString&)), pane, SLOT(changeAlwaysUseTemplate(KisTemplatesPane*, const QString&))); connect(pane, SIGNAL(splitterResized(KisDetailsPane*, const QList&)), this, SIGNAL(splitterResized(KisDetailsPane*, const QList&))); connect(this, SIGNAL(splitterResized(KisDetailsPane*, const QList&)), pane, SLOT(resizeSplitter(KisDetailsPane*, const QList&))); QTreeWidgetItem* item = addPane(group->name(), group->templates().first()->loadPicture(), pane, group->sortingWeight() + templateOffset); - + if (!firstItem) { firstItem = item; } if (group == templateTree.defaultGroup()) { firstItem = item; } if (pane->isSelected()) { selectItem = item; } } } else { firstItem = d->m_sectionList->topLevelItem(0); } KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); if (selectItem && (cfgGrp.readEntry("LastReturnType") == "Template")) { d->m_sectionList->setCurrentItem(selectItem, 0, QItemSelectionModel::ClearAndSelect); } else if (d->m_sectionList->selectedItems().isEmpty() && firstItem) { d->m_sectionList->setCurrentItem(firstItem, 0, QItemSelectionModel::ClearAndSelect); } } void KisOpenPane::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls()) { event->accept(); } } void KisOpenPane::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) { // XXX: when the MVC refactoring is done, this can open a bunch of // urls, but since the part/document combination is still 1:1 // that won't work for now. emit openExistingFile(event->mimeData()->urls().first()); } } void KisOpenPane::addCustomDocumentWidget(QWidget *widget, const QString& title, const QString& icon) { Q_ASSERT(widget); QString realtitle = title; if (realtitle.isEmpty()) realtitle = i18n("Custom Document"); QTreeWidgetItem* item = addPane(realtitle, icon, widget, d->m_freeCustomWidgetIndex); ++d->m_freeCustomWidgetIndex; KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); QString lastActiveItem = cfgGrp.readEntry("LastReturnType"); bool showCustomItemByDefault = cfgGrp.readEntry("ShowCustomDocumentWidgetByDefault", false); if (lastActiveItem == realtitle || (lastActiveItem.isEmpty() && showCustomItemByDefault)) { d->m_sectionList->setCurrentItem(item, 0, QItemSelectionModel::ClearAndSelect); KoSectionListItem* selectedItem = static_cast(item); d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus(); } } QTreeWidgetItem* KisOpenPane::addPane(const QString &title, const QString &iconName, QWidget *widget, int sortWeight) { if (!widget) { return 0; } int id = d->m_widgetStack->addWidget(widget); KoSectionListItem* listItem = new KoSectionListItem(d->m_sectionList, title, sortWeight, id); // resizes icons so they are a bit smaller QIcon icon = KisIconUtils::loadIcon(iconName); QPixmap iconPixmap = icon.pixmap(32, 32); QIcon finalIcon(iconPixmap); listItem->setIcon(0, finalIcon); return listItem; } QTreeWidgetItem* KisOpenPane::addPane(const QString& title, const QPixmap& icon, QWidget* widget, int sortWeight) { if (!widget) { return 0; } int id = d->m_widgetStack->addWidget(widget); int iconSize = 32; KoSectionListItem* listItem = new KoSectionListItem(d->m_sectionList, title, sortWeight, id); if (!icon.isNull()) { QImage image = icon.toImage(); if ((image.width() > iconSize) || (image.height() > iconSize)) { image = image.scaled(iconSize, iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); } image = image.convertToFormat(QImage::Format_ARGB32); image = image.copy((image.width() - iconSize) / 2, (image.height() - iconSize) / 2, iconSize, iconSize); listItem->setIcon(0, QIcon(QPixmap::fromImage(image))); } return listItem; } void KisOpenPane::updateSelectedWidget() { if(!d->m_sectionList->selectedItems().isEmpty()) { KoSectionListItem* section = dynamic_cast(d->m_sectionList->selectedItems().first()); if (!section) return; d->m_widgetStack->setCurrentIndex(section->widgetIndex()); } } void KisOpenPane::saveSplitterSizes(KisDetailsPane* sender, const QList& sizes) { Q_UNUSED(sender); KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); cfgGrp.writeEntry("DetailsPaneSplitterSizes", sizes); } void KisOpenPane::itemClicked(QTreeWidgetItem* item) { KoSectionListItem* selectedItem = static_cast(item); if (selectedItem && selectedItem->widgetIndex() >= 0) { d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus(); - } + } } diff --git a/libs/ui/KisPaletteModel.cpp b/libs/ui/KisPaletteModel.cpp index 35d0e30b62..78d9aaab74 100644 --- a/libs/ui/KisPaletteModel.cpp +++ b/libs/ui/KisPaletteModel.cpp @@ -1,632 +1,634 @@ /* * Copyright (c) 2013 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisPaletteModel.h" #include #include #include #include #include #include #include #include #include #include KisPaletteModel::KisPaletteModel(QObject* parent) : QAbstractTableModel(parent), m_colorSet(0), m_displayRenderer(KoDumbColorDisplayRenderer::instance()) { } KisPaletteModel::~KisPaletteModel() { } void KisPaletteModel::setDisplayRenderer(KoColorDisplayRendererInterface *displayRenderer) { if (displayRenderer) { if (m_displayRenderer) { disconnect(m_displayRenderer, 0, this, 0); } m_displayRenderer = displayRenderer; connect(m_displayRenderer, SIGNAL(displayConfigurationChanged()), SLOT(slotDisplayConfigurationChanged())); } else { m_displayRenderer = KoDumbColorDisplayRenderer::instance(); } } void KisPaletteModel::slotDisplayConfigurationChanged() { - reset(); + beginResetModel(); + endResetModel(); } QModelIndex KisPaletteModel::getLastEntryIndex() { int endRow = rowCount(); int endColumn = columnCount(); if (m_colorSet->nColors()>0) { QModelIndex i = this->index(endRow, endColumn, QModelIndex()); - while (qVariantValue(i.data(RetrieveEntryRole)).isEmpty()) { + while (qvariant_cast(i.data(RetrieveEntryRole)).isEmpty()) { i = this->index(endRow, endColumn); endColumn -=1; if (endColumn<0) { endColumn = columnCount(); endRow-=1; } } return i; } return QModelIndex(); } QVariant KisPaletteModel::data(const QModelIndex& index, int role) const { KoColorSetEntry entry; if (m_colorSet && m_displayRenderer) { //now to figure out whether we have a groupname row or not. bool groupNameRow = false; quint32 indexInGroup = 0; QString indexGroupName = QString(); int rowstotal = m_colorSet->nColorsGroup()/columnCount(); if (index.row()<=rowstotal && (quint32)(index.row()*columnCount()+index.column())nColorsGroup()) { indexInGroup = (quint32)(index.row()*columnCount()+index.column()); } if (m_colorSet->nColorsGroup()==0) { rowstotal+=1; //always add one for the default group when considering groups. } Q_FOREACH (QString groupName, m_colorSet->getGroupNames()){ //we make an int for the rows added by the current group. int newrows = 1+m_colorSet->nColorsGroup(groupName)/columnCount(); if (m_colorSet->nColorsGroup(groupName)%columnCount() > 0) { newrows+=1; } if (newrows==0) { newrows+=1; //always add one for the group when considering groups. } quint32 tempIndex = (quint32)((index.row()-(rowstotal+2))*columnCount()+index.column()); if (index.row() == rowstotal+1) { //rowstotal+1 is taken up by the groupname. indexGroupName = groupName; groupNameRow = true; } else if (index.row() > (rowstotal+1) && index.row() <= rowstotal+newrows && tempIndexnColorsGroup(groupName)){ //otherwise it's an index to the colors in the group. indexGroupName = groupName; indexInGroup = tempIndex; } //add the new rows to the totalrows we've looked at. rowstotal += newrows; } if (groupNameRow) { switch (role) { case Qt::ToolTipRole: case Qt::DisplayRole: { return indexGroupName; } case IsHeaderRole: { return true; } case RetrieveEntryRole: { QStringList entryList; entryList.append(indexGroupName); entryList.append(QString::number(0)); return entryList; } } } else { if (indexInGroup < m_colorSet->nColorsGroup(indexGroupName)) { entry = m_colorSet->getColorGroup(indexInGroup, indexGroupName); switch (role) { case Qt::ToolTipRole: case Qt::DisplayRole: { return entry.name; } case Qt::BackgroundRole: { QColor color = m_displayRenderer->toQColor(entry.color); return QBrush(color); } case IsHeaderRole: { return false; } case RetrieveEntryRole: { QStringList entryList; entryList.append(indexGroupName); entryList.append(QString::number(indexInGroup)); return entryList; } } } } } return QVariant(); } int KisPaletteModel::rowCount(const QModelIndex& /*parent*/) const { if (!m_colorSet) { return 0; } if (columnCount() > 0) { int countedrows = m_colorSet->nColorsGroup("")/columnCount(); if (m_colorSet->nColorsGroup()%columnCount() > 0) { countedrows+=1; } if (m_colorSet->nColorsGroup()==0) { countedrows+=1; } Q_FOREACH (QString groupName, m_colorSet->getGroupNames()) { countedrows += 1; //add one for the name; countedrows += 1+(m_colorSet->nColorsGroup(groupName)/ columnCount()); if (m_colorSet->nColorsGroup(groupName)%columnCount() > 0) { countedrows+=1; } if (m_colorSet->nColorsGroup(groupName)==0) { countedrows+=1; } } countedrows +=1; //Our code up till now doesn't take 0 into account. return countedrows; } return m_colorSet->nColors()/15 + 1; } int KisPaletteModel::columnCount(const QModelIndex& /*parent*/) const { if (m_colorSet && m_colorSet->columnCount() > 0) { return m_colorSet->columnCount(); } return 15; } Qt::ItemFlags KisPaletteModel::flags(const QModelIndex& index) const { if (index.isValid()) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } return Qt::ItemIsDropEnabled; } QModelIndex KisPaletteModel::index(int row, int column, const QModelIndex& parent) const { if (m_colorSet) { //make an int to hold the amount of rows we've looked at. The initial is the total rows in the default group. int rowstotal = m_colorSet->nColorsGroup()/columnCount(); if (row<=rowstotal && (quint32)(row*columnCount()+column)nColorsGroup()) { //if the total rows are in the default group, we just return an index. return QAbstractTableModel::index(row, column, parent); } else if(row<0 && column<0) { return QAbstractTableModel::index(0, 0, parent); } if (m_colorSet->nColorsGroup()==0) { rowstotal+=1; //always add one for the default group when considering groups. } Q_FOREACH (QString groupName, m_colorSet->getGroupNames()){ //we make an int for the rows added by the current group. int newrows = 1+m_colorSet->nColorsGroup(groupName)/columnCount(); if (m_colorSet->nColorsGroup(groupName)%columnCount() > 0) { newrows+=1; } if (m_colorSet->nColorsGroup(groupName)==0) { newrows+=1; //always add one for the group when considering groups. } if (rowstotal + newrows>rowCount()) { newrows = rowCount() - rowstotal; } quint32 tempIndex = (quint32)((row-(rowstotal+2))*columnCount()+column); if (row == rowstotal+1) { //rowstotal+1 is taken up by the groupname. return QAbstractTableModel::index(row, 0, parent); } else if (row > (rowstotal+1) && row <= rowstotal+newrows && tempIndexnColorsGroup(groupName)){ //otherwise it's an index to the colors in the group. return QAbstractTableModel::index(row, column, parent); } //add the new rows to the totalrows we've looked at. rowstotal += newrows; } } return QModelIndex(); } void KisPaletteModel::setColorSet(KoColorSet* colorSet) { m_colorSet = colorSet; - reset(); + beginResetModel(); + endResetModel(); } KoColorSet* KisPaletteModel::colorSet() const { return m_colorSet; } QModelIndex KisPaletteModel::indexFromId(int i) const { QModelIndex index = QModelIndex(); if (colorSet()->nColors()==0) { return index; } if (i > colorSet()->nColors()) { qWarning()<<"index is too big"<nColors(); index = this->index(0,0); } if (i < (int)colorSet()->nColorsGroup(0)) { index = QAbstractTableModel::index(i/columnCount(), i%columnCount()); if (!index.isValid()) { index = QAbstractTableModel::index(0,0,QModelIndex()); } return index; } else { int rowstotal = 1+m_colorSet->nColorsGroup()/columnCount(); if (m_colorSet->nColorsGroup()==0) { rowstotal +=1; } int totalIndexes = colorSet()->nColorsGroup(); Q_FOREACH (QString groupName, m_colorSet->getGroupNames()){ if (i+1<=totalIndexes+colorSet()->nColorsGroup(groupName) && i+1>totalIndexes) { int col = (i-totalIndexes)%columnCount(); int row = rowstotal+1+((i-totalIndexes)/columnCount()); index = this->index(row, col); return index; } else { rowstotal += 1+m_colorSet->nColorsGroup(groupName)/columnCount(); totalIndexes += colorSet()->nColorsGroup(groupName); if (m_colorSet->nColorsGroup(groupName)%columnCount() > 0) { rowstotal+=1; } if (m_colorSet->nColorsGroup(groupName)==0) { rowstotal+=1; //always add one for the group when considering groups. } } } } return index; } int KisPaletteModel::idFromIndex(const QModelIndex &index) const { if (index.isValid()==false) { return -1; qWarning()<<"invalid index"; } int i=0; - QStringList entryList = qVariantValue(data(index, RetrieveEntryRole)); + QStringList entryList = qvariant_cast(data(index, RetrieveEntryRole)); if (entryList.isEmpty()) { return -1; qWarning()<<"invalid index, there's no data to retreive here"; } if (entryList.at(0)==QString()) { return entryList.at(1).toUInt(); } i = colorSet()->nColorsGroup(""); //find at which position the group is. int groupIndex = colorSet()->getGroupNames().indexOf(entryList.at(0)); //add all the groupsizes onto it till we get to our group. for(int g=0; gnColorsGroup(colorSet()->getGroupNames().at(g)); } //then add the index. i += entryList.at(1).toUInt(); return i; } KoColorSetEntry KisPaletteModel::colorSetEntryFromIndex(const QModelIndex &index) const { KoColorSetEntry blank = KoColorSetEntry(); if (!index.isValid()) { return blank; } - QStringList entryList = qVariantValue(data(index, RetrieveEntryRole)); + QStringList entryList = qvariant_cast(data(index, RetrieveEntryRole)); if (entryList.isEmpty()) { return blank; } QString groupName = entryList.at(0); quint32 indexInGroup = entryList.at(1).toUInt(); return m_colorSet->getColorGroup(indexInGroup, groupName); } bool KisPaletteModel::addColorSetEntry(KoColorSetEntry entry, QString groupName) { int col = m_colorSet->nColorsGroup(groupName)%columnCount(); QModelIndex i = getLastEntryIndex(); if (col+1>columnCount()) { beginInsertRows(QModelIndex(), i.row(), i.row()+1); } if (m_colorSet->nColors()nColors(), m_colorSet->nColors()+1); } m_colorSet->add(entry, groupName); if (col+1>columnCount()) { endInsertRows(); } if (m_colorSet->nColors()(index.data(RetrieveEntryRole)); + QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); if (entryList.empty()) { return false; } QString groupName = entryList.at(0); quint32 indexInGroup = entryList.at(1).toUInt(); - if (qVariantValue(index.data(IsHeaderRole))==false) { + if (qvariant_cast(index.data(IsHeaderRole))==false) { if (index.column()-1<0 && m_colorSet->nColorsGroup(groupName)%columnCount() <1 && index.row()-1>0 && m_colorSet->nColorsGroup(groupName)/columnCount()>0) { beginRemoveRows(QModelIndex(), index.row(), index.row()-1); } m_colorSet->removeAt(indexInGroup, groupName); if (index.column()-1<0 && m_colorSet->nColorsGroup(groupName)%columnCount() <1 && index.row()-1>0 && m_colorSet->nColorsGroup(groupName)/columnCount()>0) { endRemoveRows(); } } else { beginRemoveRows(QModelIndex(), index.row(), index.row()-1); m_colorSet->removeGroup(groupName, keepColors); endRemoveRows(); } return true; } bool KisPaletteModel::addGroup(QString groupName) { QModelIndex i = getLastEntryIndex(); beginInsertRows(QModelIndex(), i.row(), i.row()+1); m_colorSet->addGroup(groupName); endInsertRows(); return true; } bool KisPaletteModel::removeRows(int row, int count, const QModelIndex &parent) { Q_ASSERT(!parent.isValid()); int beginRow = qMax(0, row); int endRow = qMin(row + count - 1, (int)m_colorSet->nColors() - 1); beginRemoveRows(parent, beginRow, endRow); // Find the palette entry at row, count, remove from KoColorSet endRemoveRows(); return true; } bool KisPaletteModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (!data->hasFormat("krita/x-colorsetentry") && !data->hasFormat("krita/x-colorsetgroup")) { return false; } if (action == Qt::IgnoreAction) { return false; } int endRow; int endColumn; if (!parent.isValid()) { if (row < 0) { endRow = indexFromId(m_colorSet->nColors()).row(); endColumn = indexFromId(m_colorSet->nColors()).column(); } else { endRow = qMin(row, indexFromId(m_colorSet->nColors()).row()); endColumn = qMin(column, m_colorSet->columnCount()); } } else { endRow = qMin(parent.row(), rowCount()); endColumn = qMin(parent.column(), columnCount()); } if (data->hasFormat("krita/x-colorsetgroup")) { QByteArray encodedData = data->data("krita/x-colorsetgroup"); QDataStream stream(&encodedData, QIODevice::ReadOnly); while (!stream.atEnd()) { QString groupName; stream >> groupName; QModelIndex index = this->index(endRow, 0); if (index.isValid()) { - QStringList entryList = qVariantValue(index.data(RetrieveEntryRole)); + QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); QString groupDroppedOn = QString(); if (!entryList.isEmpty()) { groupDroppedOn = entryList.at(0); } int groupIndex = colorSet()->getGroupNames().indexOf(groupName); beginMoveRows( QModelIndex(), groupIndex, groupIndex, QModelIndex(), endRow); m_colorSet->moveGroup(groupName, groupDroppedOn); m_colorSet->save(); endMoveRows(); ++endRow; } } } else { QByteArray encodedData = data->data("krita/x-colorsetentry"); QDataStream stream(&encodedData, QIODevice::ReadOnly); while (!stream.atEnd()) { KoColorSetEntry entry; QString oldGroupName; int indexInGroup; QString colorXml; stream >> entry.name >> entry.id >> entry.spotColor >> indexInGroup >> oldGroupName >> colorXml; QDomDocument doc; doc.setContent(colorXml); QDomElement e = doc.documentElement(); QDomElement c = e.firstChildElement(); if (!c.isNull()) { QString colorDepthId = c.attribute("bitdepth", Integer8BitsColorDepthID.id()); entry.color = KoColor::fromXML(c, colorDepthId); } QModelIndex index = this->index(endRow, endColumn); - if (qVariantValue(index.data(IsHeaderRole))){ + if (qvariant_cast(index.data(IsHeaderRole))){ endRow+=1; } if (index.isValid()) { /*this is to figure out the row of the old color. * That way we can in turn avoid moverows from complaining the * index is out of bounds when using index. * Makes me wonder if we shouldn't just insert the index of the * old color when requesting the mimetype... */ int i = indexInGroup; if (oldGroupName != QString()) { colorSet()->nColorsGroup(""); //find at which position the group is. int groupIndex = colorSet()->getGroupNames().indexOf(oldGroupName); //add all the groupsizes onto it till we get to our group. for(int g=0; gnColorsGroup(colorSet()->getGroupNames().at(g)); } } QModelIndex indexOld = indexFromId(i); if (action == Qt::MoveAction){ if (indexOld.row()!=qMax(endRow, 0) && indexOld.row()!=qMax(endRow+1,1)) { beginMoveRows(QModelIndex(), indexOld.row(), indexOld.row(), QModelIndex(), qMax(endRow+1,1)); } if (indexOld.column()!=qMax(endColumn, 0) && indexOld.column()!=qMax(endColumn+1,1)) { beginMoveColumns(QModelIndex(), indexOld.column(), indexOld.column(), QModelIndex(), qMax(endColumn+1,1)); } } else { beginInsertRows(QModelIndex(), endRow, endRow); } - QStringList entryList = qVariantValue(index.data(RetrieveEntryRole)); + QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); QString entryInGroup = "0"; QString groupName = QString(); if (!entryList.isEmpty()) { groupName = entryList.at(0); entryInGroup = entryList.at(1); } int location = entryInGroup.toInt(); // Insert the entry - if (groupName==oldGroupName && qVariantValue(index.data(IsHeaderRole))==true) { + if (groupName==oldGroupName && qvariant_cast(index.data(IsHeaderRole))==true) { groupName=QString(); location=m_colorSet->nColorsGroup(); } m_colorSet->insertBefore(entry, location, groupName); if (groupName==oldGroupName && locationremoveAt(indexInGroup, oldGroupName); } m_colorSet->save(); if (action == Qt::MoveAction){ if (indexOld.row()!=qMax(endRow, 0) && indexOld.row()!=qMax(endRow+1,1)) { endMoveRows(); } if (indexOld.column()!=qMax(endColumn, 0) && indexOld.column()!=qMax(endColumn+1,1)) { endMoveColumns(); } } else { endInsertRows(); } ++endRow; } } } return true; } QMimeData *KisPaletteModel::mimeData(const QModelIndexList &indexes) const { QMimeData *mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); QString mimeTypeName = "krita/x-colorsetentry"; //Q_FOREACH(const QModelIndex &index, indexes) { QModelIndex index = indexes.last(); if (index.isValid()) { - if (qVariantValue(index.data(IsHeaderRole))==false) { + if (qvariant_cast(index.data(IsHeaderRole))==false) { KoColorSetEntry entry = colorSetEntryFromIndex(index); - QStringList entryList = qVariantValue(index.data(RetrieveEntryRole)); + QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); QString groupName = QString(); int indexInGroup = 0; if (!entryList.isEmpty()) { groupName = entryList.at(0); QString iig = entryList.at(1); indexInGroup = iig.toInt(); } QDomDocument doc; QDomElement root = doc.createElement("Color"); root.setAttribute("bitdepth", entry.color.colorSpace()->colorDepthId().id()); doc.appendChild(root); entry.color.toXML(doc, root); stream << entry.name << entry.id << entry.spotColor << indexInGroup << groupName << doc.toString(); } else { mimeTypeName = "krita/x-colorsetgroup"; - QStringList entryList = qVariantValue(index.data(RetrieveEntryRole)); + QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); QString groupName = QString(); if (!entryList.isEmpty()) { groupName = entryList.at(0); } stream << groupName; } } mimeData->setData(mimeTypeName, encodedData); return mimeData; } QStringList KisPaletteModel::mimeTypes() const { return QStringList() << "krita/x-colorsetentry" << "krita/x-colorsetgroup"; } Qt::DropActions KisPaletteModel::supportedDropActions() const { return Qt::MoveAction; } diff --git a/libs/ui/KisResourceBundle.h b/libs/ui/KisResourceBundle.h index a0a2dd7443..5a5f26f3ad 100644 --- a/libs/ui/KisResourceBundle.h +++ b/libs/ui/KisResourceBundle.h @@ -1,159 +1,159 @@ /* * 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 "KisResourceBundleManifest.h" #include "kritaui_export.h" class KoStore; /** * @brief The ResourceBundle class * @details Describe the resource bundles as KoResources */ class KRITAUI_EXPORT KisResourceBundle : public KoResource { public: /** * @brief ResourceBundle : Ctor * @param bundlePath the path of the bundle */ KisResourceBundle(QString const& fileName); /** * @brief ~ResourceBundle : Dtor */ ~KisResourceBundle() override; /** * @brief defaultFileExtension * @return the default file extension which should be when saving the resource */ QString defaultFileExtension() const override; /** * @brief load : Load this resource. * @return true if succeed, false otherwise. */ bool load() override; bool loadFromDevice(QIODevice *dev) override; /** * @brief save : Save this resource. * @return true if succeed, false otherwise. */ bool save() override; bool saveToDevice(QIODevice* dev) const override; /** * @brief install : Install the contents of the resource bundle. */ bool install(); /** * @brief uninstall : Uninstall the resource bundle. */ bool uninstall(); /** * @brief addMeta : Add a Metadata to the resource * @param type type of the metadata * @param value value of the metadata */ void addMeta(const QString &type, const QString &value); const QString getMeta(const QString &type, const QString &defaultValue = QString()) const; /** * @brief addFile : Add a file to the bundle * @param fileType type of the resource file * @param filePath path of the resource file */ 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::null) 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; QString m_bundleVersion; }; #endif // KORESOURCEBUNDLE_H diff --git a/libs/ui/KisViewManager.cpp b/libs/ui/KisViewManager.cpp index 016cc53b2d..2f3a5b05be 100644 --- a/libs/ui/KisViewManager.cpp +++ b/libs/ui/KisViewManager.cpp @@ -1,1369 +1,1370 @@ /* * This file is part of KimageShop^WKrayon^WKrita * * Copyright (c) 1999 Matthias Elter * 1999 Michael Koch * 1999 Carsten Pfeiffer * 2002 Patrick Julien * 2003-2011 Boudewijn Rempt * 2004 Clarence Dang * 2011 José Luis Vergara * 2017 L. E. Segovia * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "KisViewManager.h" #include #include #include #include #include -#include +#include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "input/kis_input_manager.h" #include "canvas/kis_canvas2.h" #include "canvas/kis_canvas_controller.h" #include "canvas/kis_grid_manager.h" #include "dialogs/kis_dlg_blacklist_cleanup.h" #include "input/kis_input_profile_manager.h" #include "kis_action_manager.h" #include "kis_action.h" #include "kis_canvas_controls_manager.h" #include "kis_canvas_resource_provider.h" #include "kis_composite_progress_proxy.h" #include #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_control_frame.h" #include "kis_coordinates_converter.h" #include "KisDocument.h" #include "kis_favorite_resource_manager.h" #include "kis_filter_manager.h" #include "kis_group_layer.h" #include #include #include "kis_image_manager.h" #include #include "kis_mainwindow_observer.h" #include "kis_mask_manager.h" #include "kis_mimedata.h" #include "kis_mirror_manager.h" #include "kis_node_commands_adapter.h" #include "kis_node.h" #include "kis_node_manager.h" #include "kis_painting_assistants_manager.h" #include #include "kis_paintop_box.h" #include #include "KisPart.h" #include "KisPrintJob.h" #include #include "kis_resource_server_provider.h" #include "kis_selection.h" #include "kis_selection_manager.h" #include "kis_shape_controller.h" #include "kis_shape_layer.h" #include #include "kis_statusbar.h" #include #include #include "kis_tooltip_manager.h" #include #include "KisView.h" #include "kis_zoom_manager.h" #include "widgets/kis_floating_message.h" #include "kis_signal_auto_connection.h" #include "kis_script_manager.h" #include "kis_icon_utils.h" #include "kis_guides_manager.h" #include "kis_derived_resources.h" #include "dialogs/kis_delayed_save_dialog.h" #include class BlockingUserInputEventFilter : public QObject { bool eventFilter(QObject *watched, QEvent *event) override { Q_UNUSED(watched); if(dynamic_cast(event) || dynamic_cast(event) || dynamic_cast(event)) { return true; } else { return false; } } }; class KisViewManager::KisViewManagerPrivate { public: KisViewManagerPrivate(KisViewManager *_q, KActionCollection *_actionCollection, QWidget *_q_parent) : filterManager(_q) , createTemplate(0) , saveIncremental(0) , saveIncrementalBackup(0) , openResourcesDirectory(0) , rotateCanvasRight(0) , rotateCanvasLeft(0) , resetCanvasRotation(0) , wrapAroundAction(0) , levelOfDetailAction(0) , showRulersAction(0) , rulersTrackMouseAction(0) , zoomTo100pct(0) , zoomIn(0) , zoomOut(0) , selectionManager(_q) , statusBar(_q) , controlFrame(_q, _q_parent) , nodeManager(_q) , imageManager(_q) , gridManager(_q) , canvasControlsManager(_q) , paintingAssistantsManager(_q) , actionManager(_q, _actionCollection) , mainWindow(0) , showFloatingMessage(true) , currentImageView(0) , canvasResourceProvider(_q) , canvasResourceManager() , guiUpdateCompressor(30, KisSignalCompressor::POSTPONE, _q) , actionCollection(_actionCollection) , mirrorManager(_q) , inputManager(_q) , scriptManager(_q) , actionAuthor(0) , showPixelGrid(0) { canvasResourceManager.addDerivedResourceConverter(toQShared(new KisCompositeOpResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisEffectiveCompositeOpResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisOpacityResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisFlowResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisSizeResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisLodAvailabilityResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisEraserModeResourceConverter)); canvasResourceManager.addResourceUpdateMediator(toQShared(new KisPresetUpdateMediator)); } public: KisFilterManager filterManager; KisAction *createTemplate; KisAction *createCopy; KisAction *saveIncremental; KisAction *saveIncrementalBackup; KisAction *openResourcesDirectory; KisAction *rotateCanvasRight; KisAction *rotateCanvasLeft; KisAction *resetCanvasRotation; KisAction *wrapAroundAction; KisAction *levelOfDetailAction; KisAction *showRulersAction; KisAction *rulersTrackMouseAction; KisAction *zoomTo100pct; KisAction *zoomIn; KisAction *zoomOut; KisAction *softProof; KisAction *gamutCheck; KisSelectionManager selectionManager; KisGuidesManager guidesManager; KisStatusBar statusBar; QPointer persistentImageProgressUpdater; QScopedPointer persistentUnthreadedProgressUpdaterRouter; QPointer persistentUnthreadedProgressUpdater; KisControlFrame controlFrame; KisNodeManager nodeManager; KisImageManager imageManager; KisGridManager gridManager; KisCanvasControlsManager canvasControlsManager; KisPaintingAssistantsManager paintingAssistantsManager; BlockingUserInputEventFilter blockingEventFilter; KisActionManager actionManager; QMainWindow* mainWindow; QPointer savedFloatingMessage; bool showFloatingMessage; QPointer currentImageView; KisCanvasResourceProvider canvasResourceProvider; KoCanvasResourceManager canvasResourceManager; KisSignalCompressor guiUpdateCompressor; KActionCollection *actionCollection; KisMirrorManager mirrorManager; KisInputManager inputManager; KisSignalAutoConnectionsStore viewConnections; KisScriptManager scriptManager; KSelectAction *actionAuthor; // Select action for author profile. KisAction *showPixelGrid; QByteArray canvasState; #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) QFlags windowFlags; #endif bool blockUntilOperationsFinishedImpl(KisImageSP image, bool force); }; KisViewManager::KisViewManager(QWidget *parent, KActionCollection *_actionCollection) : d(new KisViewManagerPrivate(this, _actionCollection, parent)) { d->actionCollection = _actionCollection; d->mainWindow = dynamic_cast(parent); d->canvasResourceProvider.setResourceManager(&d->canvasResourceManager); connect(&d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout())); createActions(); setupManagers(); // These initialization functions must wait until KisViewManager ctor is complete. d->statusBar.setup(); d->persistentImageProgressUpdater = d->statusBar.progressUpdater()->startSubtask(1, "", true); // reset state to "completed" d->persistentImageProgressUpdater->setRange(0,100); d->persistentImageProgressUpdater->setValue(100); d->persistentUnthreadedProgressUpdater = d->statusBar.progressUpdater()->startSubtask(1, "", true); // reset state to "completed" d->persistentUnthreadedProgressUpdater->setRange(0,100); d->persistentUnthreadedProgressUpdater->setValue(100); d->persistentUnthreadedProgressUpdaterRouter.reset( new KoProgressUpdater(d->persistentUnthreadedProgressUpdater, KoProgressUpdater::Unthreaded)); d->persistentUnthreadedProgressUpdaterRouter->setAutoNestNames(true); d->controlFrame.setup(parent); //Check to draw scrollbars after "Canvas only mode" toggle is created. this->showHideScrollbars(); QScopedPointer dummy(new KoDummyCanvasController(actionCollection())); KoToolManager::instance()->registerToolActions(actionCollection(), dummy.data()); QTimer::singleShot(0, this, SLOT(initializeStatusBarVisibility())); connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)), d->controlFrame.paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice))); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), d->controlFrame.paintopBox(), SLOT(slotToolChanged(KoCanvasController*,int))); connect(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), resourceProvider(), SLOT(slotNodeActivated(KisNodeSP))); connect(resourceProvider()->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), d->controlFrame.paintopBox(), SLOT(slotCanvasResourceChanged(int,QVariant))); connect(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*))); connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions())); KisInputProfileManager::instance()->loadProfiles(); KisConfig cfg; d->showFloatingMessage = cfg.showCanvasMessages(); } KisViewManager::~KisViewManager() { KisConfig cfg; if (resourceProvider() && resourceProvider()->currentPreset()) { cfg.writeEntry("LastPreset", resourceProvider()->currentPreset()->name()); cfg.writeKoColor("LastForeGroundColor",resourceProvider()->fgColor()); cfg.writeKoColor("LastBackGroundColor",resourceProvider()->bgColor()); } cfg.writeEntry("baseLength", KoResourceItemChooserSync::instance()->baseLength()); delete d; } KActionCollection *KisViewManager::actionCollection() const { return d->actionCollection; } void KisViewManager::slotViewAdded(KisView *view) { // WARNING: this slot is called even when a view from another main windows is added! // Don't expect \p view be a child of this view manager! Q_UNUSED(view); if (viewCount() == 0) { d->statusBar.showAllStatusBarItems(); } } void KisViewManager::slotViewRemoved(KisView *view) { // WARNING: this slot is called even when a view from another main windows is removed! // Don't expect \p view be a child of this view manager! Q_UNUSED(view); if (viewCount() == 0) { d->statusBar.hideAllStatusBarItems(); } } void KisViewManager::setCurrentView(KisView *view) { bool first = true; if (d->currentImageView) { d->currentImageView->notifyCurrentStateChanged(false); d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor)); first = false; KisDocument* doc = d->currentImageView->document(); if (doc) { doc->image()->compositeProgressProxy()->removeProxy(d->persistentImageProgressUpdater); doc->disconnect(this); } d->currentImageView->canvasController()->proxyObject->disconnect(&d->statusBar); d->viewConnections.clear(); } QPointer imageView = qobject_cast(view); d->currentImageView = imageView; if (imageView) { d->softProof->setChecked(imageView->softProofing()); d->gamutCheck->setChecked(imageView->gamutCheck()); // Wait for the async image to have loaded KisDocument* doc = view->document(); // connect(canvasController()->proxyObject, SIGNAL(documentMousePositionChanged(QPointF)), d->statusBar, SLOT(documentMousePositionChanged(QPointF))); // Restore the last used brush preset, color and background color. if (first) { KisConfig cfg; KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); QString lastPreset = cfg.readEntry("LastPreset", QString("Basic_tip_default")); KisPaintOpPresetSP preset = rserver->resourceByName(lastPreset); if (!preset) { preset = rserver->resourceByName("Basic_tip_default"); } if (!preset) { preset = rserver->resources().first(); } if (preset) { paintOpBox()->restoreResource(preset.data()); } const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KoColor foreground(Qt::black, cs); d->canvasResourceProvider.setFGColor(cfg.readKoColor("LastForeGroundColor",foreground)); KoColor background(Qt::white, cs); d->canvasResourceProvider.setBGColor(cfg.readKoColor("LastBackGroundColor",background)); } KisCanvasController *canvasController = dynamic_cast(d->currentImageView->canvasController()); d->viewConnections.addUniqueConnection(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), doc->image(), SLOT(requestStrokeEndActiveNode())); d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15())); d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15())); d->viewConnections.addUniqueConnection(d->resetCanvasRotation, SIGNAL(triggered()),canvasController, SLOT(resetCanvasRotation())); d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool))); d->wrapAroundAction->setChecked(canvasController->wrapAroundMode()); d->viewConnections.addUniqueConnection(d->levelOfDetailAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleLevelOfDetailMode(bool))); d->levelOfDetailAction->setChecked(canvasController->levelOfDetailMode()); d->viewConnections.addUniqueConnection(d->currentImageView->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), d->controlFrame.paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*))); d->viewConnections.addUniqueConnection(d->showRulersAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setShowRulers(bool))); d->viewConnections.addUniqueConnection(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setRulersTrackMouse(bool))); d->viewConnections.addUniqueConnection(d->zoomTo100pct, SIGNAL(triggered()), imageView->zoomManager(), SLOT(zoomTo100())); d->viewConnections.addUniqueConnection(d->zoomIn, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomIn())); d->viewConnections.addUniqueConnection(d->zoomOut, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomOut())); d->viewConnections.addUniqueConnection(d->softProof, SIGNAL(toggled(bool)), view, SLOT(slotSoftProofing(bool)) ); d->viewConnections.addUniqueConnection(d->gamutCheck, SIGNAL(toggled(bool)), view, SLOT(slotGamutCheck(bool)) ); // set up progrress reporting doc->image()->compositeProgressProxy()->addProxy(d->persistentImageProgressUpdater); d->viewConnections.addUniqueConnection(&d->statusBar, SIGNAL(sigCancellationRequested()), doc->image(), SLOT(requestStrokeCancellation())); d->viewConnections.addUniqueConnection(d->showPixelGrid, SIGNAL(toggled(bool)), canvasController, SLOT(slotTogglePixelGrid(bool))); imageView->zoomManager()->setShowRulers(d->showRulersAction->isChecked()); imageView->zoomManager()->setRulersTrackMouse(d->rulersTrackMouseAction->isChecked()); showHideScrollbars(); } d->filterManager.setView(imageView); d->selectionManager.setView(imageView); d->guidesManager.setView(imageView); d->nodeManager.setView(imageView); d->imageManager.setView(imageView); d->canvasControlsManager.setView(imageView); d->actionManager.setView(imageView); d->gridManager.setView(imageView); d->statusBar.setView(imageView); d->paintingAssistantsManager.setView(imageView); d->mirrorManager.setView(imageView); if (d->currentImageView) { d->currentImageView->notifyCurrentStateChanged(true); d->currentImageView->canvasController()->activate(); d->currentImageView->canvasController()->setFocus(); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), resourceProvider(), SLOT(slotImageSizeChanged())); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigResolutionChanged(double,double)), resourceProvider(), SLOT(slotOnScreenResolutionChanged())); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigNodeChanged(KisNodeSP)), this, SLOT(updateGUI())); d->viewConnections.addUniqueConnection( d->currentImageView->zoomManager()->zoomController(), SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)), resourceProvider(), SLOT(slotOnScreenResolutionChanged())); } d->actionManager.updateGUI(); resourceProvider()->slotImageSizeChanged(); resourceProvider()->slotOnScreenResolutionChanged(); Q_EMIT viewChanged(); } KoZoomController *KisViewManager::zoomController() const { if (d->currentImageView) { return d->currentImageView->zoomController(); } return 0; } KisImageWSP KisViewManager::image() const { if (document()) { return document()->image(); } return 0; } KisCanvasResourceProvider * KisViewManager::resourceProvider() { return &d->canvasResourceProvider; } KisCanvas2 * KisViewManager::canvasBase() const { if (d && d->currentImageView) { return d->currentImageView->canvasBase(); } return 0; } QWidget* KisViewManager::canvas() const { if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) { return d->currentImageView->canvasBase()->canvasWidget(); } return 0; } KisStatusBar * KisViewManager::statusBar() const { return &d->statusBar; } void KisViewManager::addStatusBarItem(QWidget *widget, int stretch, bool permanent) { d->statusBar.addStatusBarItem(widget, stretch, permanent); } void KisViewManager::removeStatusBarItem(QWidget *widget) { d->statusBar.removeStatusBarItem(widget); } KisPaintopBox* KisViewManager::paintOpBox() const { return d->controlFrame.paintopBox(); } QPointer KisViewManager::createUnthreadedUpdater(const QString &name) { return d->persistentUnthreadedProgressUpdaterRouter->startSubtask(1, name, false); } QPointer KisViewManager::createThreadedUpdater(const QString &name) { return d->statusBar.progressUpdater()->startSubtask(1, name, false); } KisSelectionManager * KisViewManager::selectionManager() { return &d->selectionManager; } KisNodeSP KisViewManager::activeNode() { return d->nodeManager.activeNode(); } KisLayerSP KisViewManager::activeLayer() { return d->nodeManager.activeLayer(); } KisPaintDeviceSP KisViewManager::activeDevice() { return d->nodeManager.activePaintDevice(); } KisZoomManager * KisViewManager::zoomManager() { if (d->currentImageView) { return d->currentImageView->zoomManager(); } return 0; } KisFilterManager * KisViewManager::filterManager() { return &d->filterManager; } KisImageManager * KisViewManager::imageManager() { return &d->imageManager; } KisInputManager* KisViewManager::inputManager() const { return &d->inputManager; } KisSelectionSP KisViewManager::selection() { if (d->currentImageView) { return d->currentImageView->selection(); } return 0; } bool KisViewManager::selectionEditable() { KisLayerSP layer = activeLayer(); if (layer) { KoProperties properties; QList masks = layer->childNodes(QStringList("KisSelectionMask"), properties); if (masks.size() == 1) { return masks[0]->isEditable(); } } // global selection is always editable return true; } KisUndoAdapter * KisViewManager::undoAdapter() { if (!document()) return 0; KisImageWSP image = document()->image(); Q_ASSERT(image); return image->undoAdapter(); } void KisViewManager::createActions() { KisConfig cfg; d->saveIncremental = actionManager()->createAction("save_incremental_version"); connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental())); d->saveIncrementalBackup = actionManager()->createAction("save_incremental_backup"); connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup())); connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved())); d->saveIncremental->setEnabled(false); d->saveIncrementalBackup->setEnabled(false); KisAction *tabletDebugger = actionManager()->createAction("tablet_debugger"); connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger())); d->createTemplate = actionManager()->createAction("create_template"); connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate())); d->createCopy = actionManager()->createAction("create_copy"); connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy())); d->openResourcesDirectory = actionManager()->createAction("open_resources_directory"); connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory())); d->rotateCanvasRight = actionManager()->createAction("rotate_canvas_right"); d->rotateCanvasLeft = actionManager()->createAction("rotate_canvas_left"); d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation"); d->wrapAroundAction = actionManager()->createAction("wrap_around_mode"); d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode"); d->softProof = actionManager()->createAction("softProof"); d->gamutCheck = actionManager()->createAction("gamutCheck"); KisAction *tAction = actionManager()->createAction("showStatusBar"); tAction->setChecked(cfg.showStatusBar()); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool))); tAction = actionManager()->createAction("view_show_canvas_only"); tAction->setChecked(false); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(switchCanvasOnly(bool))); //Workaround, by default has the same shortcut as mirrorCanvas KisAction *a = dynamic_cast(actionCollection()->action("format_italic")); if (a) { a->setDefaultShortcut(QKeySequence()); } a = actionManager()->createAction("edit_blacklist_cleanup"); connect(a, SIGNAL(triggered()), this, SLOT(slotBlacklistCleanup())); d->showRulersAction = actionManager()->createAction("view_ruler"); d->showRulersAction->setChecked(cfg.showRulers()); connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool))); d->rulersTrackMouseAction = actionManager()->createAction("rulers_track_mouse"); d->rulersTrackMouseAction->setChecked(cfg.rulersTrackMouse()); connect(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), SLOT(slotSaveRulersTrackMouseState(bool))); d->zoomTo100pct = actionManager()->createAction("zoom_to_100pct"); d->zoomIn = actionManager()->createStandardAction(KStandardAction::ZoomIn, 0, ""); d->zoomOut = actionManager()->createStandardAction(KStandardAction::ZoomOut, 0, ""); d->actionAuthor = new KSelectAction(KisIconUtils::loadIcon("im-user"), i18n("Active Author Profile"), this); connect(d->actionAuthor, SIGNAL(triggered(const QString &)), this, SLOT(changeAuthorProfile(const QString &))); actionCollection()->addAction("settings_active_author", d->actionAuthor); slotUpdateAuthorProfileActions(); d->showPixelGrid = actionManager()->createAction("view_pixel_grid"); d->showPixelGrid->setChecked(cfg.pixelGridEnabled()); } void KisViewManager::setupManagers() { // Create the managers for filters, selections, layers etc. // XXX: When the currentlayer changes, call updateGUI on all // managers d->filterManager.setup(actionCollection(), actionManager()); d->selectionManager.setup(actionManager()); d->guidesManager.setup(actionManager()); d->nodeManager.setup(actionCollection(), actionManager()); d->imageManager.setup(actionManager()); d->gridManager.setup(actionManager()); d->paintingAssistantsManager.setup(actionManager()); d->canvasControlsManager.setup(actionManager()); d->mirrorManager.setup(actionCollection()); d->scriptManager.setup(actionCollection(), actionManager()); } void KisViewManager::updateGUI() { d->guiUpdateCompressor.start(); } void KisViewManager::slotBlacklistCleanup() { KisDlgBlacklistCleanup dialog; dialog.exec(); } KisNodeManager * KisViewManager::nodeManager() const { return &d->nodeManager; } KisActionManager* KisViewManager::actionManager() const { return &d->actionManager; } KisGridManager * KisViewManager::gridManager() const { return &d->gridManager; } KisGuidesManager * KisViewManager::guidesManager() const { return &d->guidesManager; } KisDocument *KisViewManager::document() const { if (d->currentImageView && d->currentImageView->document()) { return d->currentImageView->document(); } return 0; } KisScriptManager *KisViewManager::scriptManager() const { return &d->scriptManager; } int KisViewManager::viewCount() const { KisMainWindow *mw = qobject_cast(d->mainWindow); if (mw) { return mw->viewCount(); } return 0; } bool KisViewManager::KisViewManagerPrivate::blockUntilOperationsFinishedImpl(KisImageSP image, bool force) { const int busyWaitDelay = 1000; KisDelayedSaveDialog dialog(image, !force ? KisDelayedSaveDialog::GeneralDialog : KisDelayedSaveDialog::ForcedDialog, busyWaitDelay, mainWindow); dialog.blockIfImageIsBusy(); return dialog.result() == QDialog::Accepted; } bool KisViewManager::blockUntilOperationsFinished(KisImageSP image) { return d->blockUntilOperationsFinishedImpl(image, false); } void KisViewManager::blockUntilOperationsFinishedForced(KisImageSP image) { d->blockUntilOperationsFinishedImpl(image, true); } void KisViewManager::slotCreateTemplate() { if (!document()) return; KisTemplateCreateDia::createTemplate( QStringLiteral("templates/"), ".kra", document(), mainWindow()); } void KisViewManager::slotCreateCopy() { KisDocument *srcDoc = document(); if (!srcDoc) return; if (!this->blockUntilOperationsFinished(srcDoc->image())) return; KisDocument *doc = 0; { KisImageBarrierLocker l(srcDoc->image()); doc = srcDoc->clone(); } KIS_SAFE_ASSERT_RECOVER_RETURN(doc); QString name = srcDoc->documentInfo()->aboutInfo("name"); if (name.isEmpty()) { name = document()->url().toLocalFile(); } name = i18n("%1 (Copy)", name); doc->documentInfo()->setAboutInfo("title", name); KisPart::instance()->addDocument(doc); KisMainWindow *mw = qobject_cast(d->mainWindow); mw->addViewAndNotifyLoadingCompleted(doc); } QMainWindow* KisViewManager::qtMainWindow() const { if (d->mainWindow) return d->mainWindow; //Fallback for when we have not yet set the main window. QMainWindow* w = qobject_cast(qApp->activeWindow()); if(w) return w; return mainWindow(); } void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow) { d->mainWindow = newMainWindow; } void KisViewManager::slotDocumentSaved() { d->saveIncremental->setEnabled(true); d->saveIncrementalBackup->setEnabled(true); } void KisViewManager::slotSaveIncremental() { if (!document()) return; bool foundVersion; bool fileAlreadyExists; bool isBackup; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // Find current version filenames // v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well // Considering our incremental version and backup scheme, format is filename_001~001.ext QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); foundVersion = matches.at(0).isEmpty() ? false : true; // Ensure compatibility with Save Incremental Backup // If this regex is not kept separate, the entire algorithm needs modification; // It's simpler to just add this. QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regexAux.indexIn(fileName); // Perform the search QStringList matchesAux = regexAux.capturedTexts(); isBackup = matchesAux.at(0).isEmpty() ? false : true; // If the filename has a version, prepare it for incrementation if (foundVersion) { version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "_" } else { // TODO: this will not work with files extensions like jp2 // ...else, simply add a version to it so the next loop works QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(fileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(version); extensionPlusVersion.prepend("_"); fileName.replace(regex2, extensionPlusVersion); } // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("_"); if (!letter.isNull()) newVersion.append(letter); if (isBackup) { newVersion.append("~"); } else { newVersion.append("."); } fileName.replace(regex, newVersion); fileAlreadyExists = QFile(fileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } document()->setFileBatchMode(true); document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true); document()->setFileBatchMode(false); if (mainWindow()) { mainWindow()->updateCaption(); } } void KisViewManager::slotSaveIncrementalBackup() { if (!document()) return; bool workingOnBackup; bool fileAlreadyExists; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // First, discover if working on a backup file, or a normal file QRegExp regex("~\\d{1,4}[.]|~\\d{1,4}[a-z][.]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); workingOnBackup = matches.at(0).isEmpty() ? false : true; if (workingOnBackup) { // Try to save incremental version (of backup), use letter for alt versions version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "~" // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); QString backupFileName = document()->localFilePath(); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("~"); if (!letter.isNull()) newVersion.append(letter); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true); if (mainWindow()) mainWindow()->updateCaption(); } else { // if NOT working on a backup... // Navigate directory searching for latest backup version, ignore letters const quint8 HARDCODED_DIGIT_COUNT = 3; QString baseNewVersion = "000"; QString backupFileName = document()->localFilePath(); QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(backupFileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(baseNewVersion); extensionPlusVersion.prepend("~"); backupFileName.replace(regex2, extensionPlusVersion); // Save version with 1 number higher than the highest version found ignoring letters do { newVersion = baseNewVersion; newVersion.prepend("~"); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { // Prepare the base for new version filename, increment by 1 int intVersion = baseNewVersion.toInt(0); ++intVersion; baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) { baseNewVersion.prepend("0"); } } } while (fileAlreadyExists); // Save both as backup and on current file for interapplication workflow document()->setFileBatchMode(true); QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true); document()->setFileBatchMode(false); if (mainWindow()) mainWindow()->updateCaption(); } } void KisViewManager::disableControls() { // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel // this is for Bug 250944 // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool // see KisToolFreehand::initPaint() and endPaint() d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter); Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) { child->installEventFilter(&d->blockingEventFilter); } } void KisViewManager::enableControls() { d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter); Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) { child->removeEventFilter(&d->blockingEventFilter); } } void KisViewManager::showStatusBar(bool toggled) { KisMainWindow *mw = mainWindow(); if(mw && mw->statusBar()) { mw->statusBar()->setVisible(toggled); KisConfig cfg; cfg.setShowStatusBar(toggled); } } void KisViewManager::switchCanvasOnly(bool toggled) { KisConfig cfg; KisMainWindow* main = mainWindow(); if(!main) { dbgUI << "Unable to switch to canvas-only mode, main window not found"; return; } if (toggled) { d->canvasState = qtMainWindow()->saveState(); #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) d->windowFlags = main->windowState(); #endif } if (cfg.hideStatusbarFullscreen()) { if (main->statusBar()) { if (!toggled) { if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) { if (main->statusBar()->property("wasvisible").toBool()) { main->statusBar()->setVisible(true); } } } else { main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible()); main->statusBar()->setVisible(false); } } } if (cfg.hideDockersFullscreen()) { KisAction* action = qobject_cast(main->actionCollection()->action("view_toggledockers")); if (action) { action->setCheckable(true); if (toggled) { if (action->isChecked()) { cfg.setShowDockers(action->isChecked()); action->setChecked(false); } else { cfg.setShowDockers(false); } } else { action->setChecked(cfg.showDockers()); } } } // QT in windows does not return to maximized upon 4th tab in a row // https://bugreports.qt.io/browse/QTBUG-57882, https://bugreports.qt.io/browse/QTBUG-52555, https://codereview.qt-project.org/#/c/185016/ if (cfg.hideTitlebarFullscreen() && !cfg.fullscreenMode()) { if(toggled) { main->setWindowState( main->windowState() | Qt::WindowFullScreen); } else { main->setWindowState( main->windowState() & ~Qt::WindowFullScreen); #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) // If window was maximized prior to fullscreen, restore that if (d->windowFlags & Qt::WindowMaximized) { main->setWindowState( main->windowState() | Qt::WindowMaximized); } #endif } } if (cfg.hideMenuFullscreen()) { if (!toggled) { if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) { if (main->menuBar()->property("wasvisible").toBool()) { main->menuBar()->setVisible(true); } } } else { main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible()); main->menuBar()->setVisible(false); } } if (cfg.hideToolbarFullscreen()) { QList toolBars = main->findChildren(); Q_FOREACH (QToolBar* toolbar, toolBars) { if (!toggled) { if (toolbar->dynamicPropertyNames().contains("wasvisible")) { if (toolbar->property("wasvisible").toBool()) { toolbar->setVisible(true); } } } else { toolbar->setProperty("wasvisible", toolbar->isVisible()); toolbar->setVisible(false); } } } showHideScrollbars(); if (toggled) { // show a fading heads-up display about the shortcut to go back showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.", actionCollection()->action("view_show_canvas_only")->shortcut().toString()), QIcon()); } else { main->restoreState(d->canvasState); } } void KisViewManager::toggleTabletLogger() { d->inputManager.toggleTabletLogger(); } void KisViewManager::openResourcesDirectory() { QString dir = KoResourcePaths::locateLocal("data", ""); QDesktopServices::openUrl(QUrl::fromLocalFile(dir)); } void KisViewManager::updateIcons() { if (mainWindow()) { QList dockers = mainWindow()->dockWidgets(); Q_FOREACH (QDockWidget* dock, dockers) { dbgKrita << "name " << dock->objectName(); KoDockWidgetTitleBar* titlebar = dynamic_cast(dock->titleBarWidget()); if (titlebar) { titlebar->updateIcons(); } QObjectList objects; objects.append(dock); while (!objects.isEmpty()) { QObject* object = objects.takeFirst(); objects.append(object->children()); KisIconUtils::updateIconCommon(object); } } } } void KisViewManager::initializeStatusBarVisibility() { KisConfig cfg; d->mainWindow->statusBar()->setVisible(cfg.showStatusBar()); } void KisViewManager::guiUpdateTimeout() { d->nodeManager.updateGUI(); d->selectionManager.updateGUI(); d->filterManager.updateGUI(); if (zoomManager()) { zoomManager()->updateGUI(); } d->gridManager.updateGUI(); d->actionManager.updateGUI(); } void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment) { if (!d->currentImageView) return; d->currentImageView->showFloatingMessageImpl(message, icon, timeout, priority, alignment); emit floatingMessageRequested(message, icon.name()); } KisMainWindow *KisViewManager::mainWindow() const { return qobject_cast(d->mainWindow); } void KisViewManager::showHideScrollbars() { if (!d->currentImageView) return; if (!d->currentImageView->canvasController()) return; KisConfig cfg; bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked(); if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } } void KisViewManager::slotSaveShowRulersState(bool value) { KisConfig cfg; cfg.setShowRulers(value); } void KisViewManager::slotSaveRulersTrackMouseState(bool value) { KisConfig cfg; cfg.setRulersTrackMouse(value); } void KisViewManager::setShowFloatingMessage(bool show) { d->showFloatingMessage = show; } void KisViewManager::changeAuthorProfile(const QString &profileName) { KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author"); if (profileName.isEmpty()) { appAuthorGroup.writeEntry("active-profile", ""); } else if (profileName == i18nc("choice for author profile", "Anonymous")) { appAuthorGroup.writeEntry("active-profile", "anonymous"); } else { appAuthorGroup.writeEntry("active-profile", profileName); } appAuthorGroup.sync(); Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) { doc->documentInfo()->updateParameters(); } } void KisViewManager::slotUpdateAuthorProfileActions() { Q_ASSERT(d->actionAuthor); if (!d->actionAuthor) { return; } d->actionAuthor->clear(); d->actionAuthor->addAction(i18n("Default Author Profile")); d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous")); KConfigGroup authorGroup(KoGlobal::calligraConfig(), "Author"); QStringList profiles = authorGroup.readEntry("profile-names", QStringList()); Q_FOREACH (const QString &profile , profiles) { d->actionAuthor->addAction(profile); } KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author"); QString profileName = appAuthorGroup.readEntry("active-profile", ""); if (profileName == "anonymous") { d->actionAuthor->setCurrentItem(1); } else if (profiles.contains(profileName)) { d->actionAuthor->setCurrentAction(profileName); } else { d->actionAuthor->setCurrentItem(0); } } diff --git a/libs/ui/canvas/kis_tool_proxy.cpp b/libs/ui/canvas/kis_tool_proxy.cpp index 8e6916ada6..5c054df964 100644 --- a/libs/ui/canvas/kis_tool_proxy.cpp +++ b/libs/ui/canvas/kis_tool_proxy.cpp @@ -1,243 +1,243 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_proxy.h" #include "kis_canvas2.h" #include "input/kis_tablet_debugger.h" #include KisToolProxy::KisToolProxy(KoCanvasBase *canvas, QObject *parent) : KoToolProxy(canvas, parent), m_isActionActivated(false), m_lastAction(KisTool::Primary) { } void KisToolProxy::initializeImage(KisImageSP image) { connect(image, SIGNAL(sigUndoDuringStrokeRequested()), SLOT(requestUndoDuringStroke()), Qt::UniqueConnection); connect(image, SIGNAL(sigStrokeCancellationRequested()), SLOT(requestStrokeCancellation()), Qt::UniqueConnection); connect(image, SIGNAL(sigStrokeEndRequested()), SLOT(requestStrokeEnd()), Qt::UniqueConnection); } QPointF KisToolProxy::tabletToDocument(const QPointF &globalPos) { const QPointF pos = globalPos - QPointF(canvas()->canvasWidget()->mapToGlobal(QPoint(0, 0))); return widgetToDocument(pos); } QPointF KisToolProxy::widgetToDocument(const QPointF &widgetPoint) const { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->coordinatesConverter()->widgetToDocument(widgetPoint); } KoPointerEvent KisToolProxy::convertEventToPointerEvent(QEvent *event, const QPointF &docPoint, bool *result) { switch (event->type()) { case QEvent::TabletPress: case QEvent::TabletRelease: case QEvent::TabletMove: { *result = true; QTabletEvent *tabletEvent = static_cast(event); KoPointerEvent ev(tabletEvent, docPoint); ev.setTabletButton(Qt::LeftButton); return ev; } case QEvent::MouseButtonPress: case QEvent::MouseButtonDblClick: case QEvent::MouseButtonRelease: case QEvent::MouseMove: { *result = true; QMouseEvent *mouseEvent = static_cast(event); return KoPointerEvent(mouseEvent, docPoint); } default: ; } *result = false; QMouseEvent fakeEvent(QEvent::MouseMove, QPoint(), Qt::NoButton, Qt::NoButton, Qt::NoModifier); return KoPointerEvent(&fakeEvent, QPointF()); } void KisToolProxy::forwardHoverEvent(QEvent *event) { switch (event->type()) { case QEvent::TabletMove: { QTabletEvent *tabletEvent = static_cast(event); QPointF docPoint = widgetToDocument(tabletEvent->posF()); this->tabletEvent(tabletEvent, docPoint); return; } case QEvent::MouseMove: { QMouseEvent *mouseEvent = static_cast(event); - QPointF docPoint = widgetToDocument(mouseEvent->posF()); + QPointF docPoint = widgetToDocument(mouseEvent->localPos()); mouseMoveEvent(mouseEvent, docPoint); return; } default: { qWarning() << "forwardHoverEvent encountered unknown event type."; return; } } } bool KisToolProxy::forwardEvent(ActionState state, KisTool::ToolAction action, QEvent *event, QEvent *originalEvent) { bool retval = true; QTabletEvent *tabletEvent = dynamic_cast(event); QMouseEvent *mouseEvent = dynamic_cast(event); if (tabletEvent) { QPointF docPoint = widgetToDocument(tabletEvent->posF()); tabletEvent->accept(); this->tabletEvent(tabletEvent, docPoint); forwardToTool(state, action, tabletEvent, docPoint); retval = tabletEvent->isAccepted(); } else if (mouseEvent) { - QPointF docPoint = widgetToDocument(mouseEvent->posF()); + QPointF docPoint = widgetToDocument(mouseEvent->localPos()); mouseEvent->accept(); if (mouseEvent->type() == QEvent::MouseButtonPress) { mousePressEvent(mouseEvent, docPoint); } else if (mouseEvent->type() == QEvent::MouseButtonDblClick) { mouseDoubleClickEvent(mouseEvent, docPoint); } else if (mouseEvent->type() == QEvent::MouseButtonRelease) { mouseReleaseEvent(mouseEvent, docPoint); } else if (mouseEvent->type() == QEvent::MouseMove) { mouseMoveEvent(mouseEvent, docPoint); } forwardToTool(state, action, originalEvent, docPoint); retval = mouseEvent->isAccepted(); } else if (event && event->type() == QEvent::KeyPress) { QKeyEvent* kevent = static_cast(event); keyPressEvent(kevent); } else if (event && event->type() == QEvent::KeyRelease) { QKeyEvent* kevent = static_cast(event); keyReleaseEvent(kevent); } return retval; } void KisToolProxy::forwardToTool(ActionState state, KisTool::ToolAction action, QEvent *event, const QPointF &docPoint) { bool eventValid = false; KoPointerEvent ev = convertEventToPointerEvent(event, docPoint, &eventValid); KisTool *activeTool = dynamic_cast(priv()->activeTool); if (!eventValid || !activeTool) return; switch (state) { case BEGIN: if (action == KisTool::Primary) { if (event->type() == QEvent::MouseButtonDblClick) { activeTool->beginPrimaryDoubleClickAction(&ev); } else { activeTool->beginPrimaryAction(&ev); } } else { if (event->type() == QEvent::MouseButtonDblClick) { activeTool->beginAlternateDoubleClickAction(&ev, KisTool::actionToAlternateAction(action)); } else { activeTool->beginAlternateAction(&ev, KisTool::actionToAlternateAction(action)); } } break; case CONTINUE: if (action == KisTool::Primary) { activeTool->continuePrimaryAction(&ev); } else { activeTool->continueAlternateAction(&ev, KisTool::actionToAlternateAction(action)); } break; case END: if (action == KisTool::Primary) { activeTool->endPrimaryAction(&ev); } else { activeTool->endAlternateAction(&ev, KisTool::actionToAlternateAction(action)); } break; } } bool KisToolProxy::primaryActionSupportsHiResEvents() const { KisTool *activeTool = dynamic_cast(const_cast(this)->priv()->activeTool); return activeTool && activeTool->primaryActionSupportsHiResEvents(); } void KisToolProxy::setActiveTool(KoToolBase *tool) { if (!tool) return; if (m_isActionActivated) { deactivateToolAction(m_lastAction); KoToolProxy::setActiveTool(tool); activateToolAction(m_lastAction); } else { KoToolProxy::setActiveTool(tool); } } void KisToolProxy::activateToolAction(KisTool::ToolAction action) { KisTool *activeTool = dynamic_cast(const_cast(this)->priv()->activeTool); if (activeTool) { if (action == KisTool::Primary) { activeTool->activatePrimaryAction(); } else { activeTool->activateAlternateAction(KisTool::actionToAlternateAction(action)); } } m_isActionActivated = true; m_lastAction = action; } void KisToolProxy::deactivateToolAction(KisTool::ToolAction action) { KisTool *activeTool = dynamic_cast(const_cast(this)->priv()->activeTool); if (activeTool) { if (action == KisTool::Primary) { activeTool->deactivatePrimaryAction(); } else { activeTool->deactivateAlternateAction(KisTool::actionToAlternateAction(action)); } } m_isActionActivated = false; m_lastAction = action; } diff --git a/libs/ui/dialogs/kis_dlg_file_layer.cpp b/libs/ui/dialogs/kis_dlg_file_layer.cpp index 435d29caf1..07ba826b10 100644 --- a/libs/ui/dialogs/kis_dlg_file_layer.cpp +++ b/libs/ui/dialogs/kis_dlg_file_layer.cpp @@ -1,98 +1,98 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2013 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_dlg_file_layer.h" #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include KisDlgFileLayer::KisDlgFileLayer(const QString &basePath, const QString & name, QWidget * parent) : KoDialog(parent) , m_basePath(basePath) { setButtons(Ok | Cancel); setDefaultButton(Ok); QWidget * page = new QWidget(this); dlgWidget.setupUi(page); dlgWidget.wdgUrlRequester->setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); setMainWidget(page); //dlgWidget.wdgUrlRequester->setBasePath(m_basePath); dlgWidget.wdgUrlRequester->setStartDir(m_basePath); dlgWidget.txtLayerName->setText(name); connect(dlgWidget.wdgUrlRequester, SIGNAL(textChanged(const QString &)), SLOT(slotNameChanged(const QString &))); enableButtonOk(false); } void KisDlgFileLayer::slotNameChanged(const QString & text) { enableButtonOk(!text.isEmpty()); } QString KisDlgFileLayer::layerName() const { return dlgWidget.txtLayerName->text(); } KisFileLayer::ScalingMethod KisDlgFileLayer::scaleToImageResolution() const { if (dlgWidget.radioDontScale->isChecked()) { return KisFileLayer::None; } else if (dlgWidget.radioScaleToImageSize->isChecked()) { return KisFileLayer::ToImageSize; } else { return KisFileLayer::ToImagePPI; } } QString KisDlgFileLayer::fileName() const { QString path = dlgWidget.wdgUrlRequester->fileName(); QFileInfo fi(path); if (fi.isSymLink()) { path = fi.symLinkTarget(); fi = QFileInfo(path); } if (!m_basePath.isEmpty() && fi.isAbsolute()) { QDir directory(m_basePath); path = directory.relativeFilePath(path); } return path; } diff --git a/libs/ui/dialogs/kis_dlg_import_image_sequence.cpp b/libs/ui/dialogs/kis_dlg_import_image_sequence.cpp index d518cfbc09..5fa8ae5db9 100644 --- a/libs/ui/dialogs/kis_dlg_import_image_sequence.cpp +++ b/libs/ui/dialogs/kis_dlg_import_image_sequence.cpp @@ -1,163 +1,163 @@ /* * Copyright (c) 2016 Jouni Pentikäinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_dlg_import_image_sequence.h" #include "KisDocument.h" #include "KisMainWindow.h" #include "kis_image.h" #include "kis_image_animation_interface.h" #include "KisImportExportManager.h" #include "KoFileDialog.h" -#include +#include class KisDlgImportImageSequence::ListItem : QListWidgetItem { public: ListItem(const QString &text, QListWidget *view, QCollator *collator) : QListWidgetItem(text, view), collator(collator) {} bool operator <(const QListWidgetItem &other) const override { int cmp = collator->compare(this->text(), other.text()); return cmp < 0; } private: QCollator *collator; }; KisDlgImportImageSequence::KisDlgImportImageSequence(KisMainWindow *mainWindow, KisDocument *document) : KoDialog(mainWindow), m_mainWindow(mainWindow), m_document(document) { setButtons(Ok | Cancel); setDefaultButton(Ok); QWidget * page = new QWidget(this); m_ui.setupUi(page); setMainWidget(page); enableButtonOk(false); m_ui.cmbOrder->addItem(i18n("Ascending"), Ascending); m_ui.cmbOrder->addItem(i18n("Descending"), Descending); m_ui.cmbOrder->setCurrentIndex(0); m_ui.cmbSortMode->addItem(i18n("Alphabetical"), Natural); m_ui.cmbSortMode->addItem(i18n("Numerical"), Numerical); m_ui.cmbSortMode->setCurrentIndex(1); m_ui.lstFiles->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(m_ui.btnAddImages, &QAbstractButton::clicked, this, &KisDlgImportImageSequence::slotAddFiles); connect(m_ui.btnRemove, &QAbstractButton::clicked, this, &KisDlgImportImageSequence::slotRemoveFiles); connect(m_ui.spinStep, SIGNAL(valueChanged(int)), this, SLOT(slotSkipChanged(int))); connect(m_ui.cmbOrder, SIGNAL(currentIndexChanged(int)), this, SLOT(slotOrderOptionsChanged(int))); connect(m_ui.cmbSortMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotOrderOptionsChanged(int))); // cold initialization of the controls slotSkipChanged(m_ui.spinStep->value()); slotOrderOptionsChanged(m_ui.cmbOrder->currentIndex()); slotOrderOptionsChanged(m_ui.cmbSortMode->currentIndex()); } QStringList KisDlgImportImageSequence::files() { QStringList list; for (int i=0; i < m_ui.lstFiles->count(); i++) { list.append(m_ui.lstFiles->item(i)->text()); } return list; } int KisDlgImportImageSequence::firstFrame() { return m_ui.spinFirstFrame->value(); } int KisDlgImportImageSequence::step() { return m_ui.spinStep->value(); } void KisDlgImportImageSequence::slotAddFiles() { QStringList urls = showOpenFileDialog(); if (!urls.isEmpty()) { Q_FOREACH(QString url, urls) { new ListItem(url, m_ui.lstFiles, &m_collator); } sortFileList(); } enableButtonOk(m_ui.lstFiles->count() > 0); } QStringList KisDlgImportImageSequence::showOpenFileDialog() { KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument"); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); dialog.setCaption(i18n("Import Images")); return dialog.filenames(); } void KisDlgImportImageSequence::slotRemoveFiles() { QList selected = m_ui.lstFiles->selectedItems(); Q_FOREACH(QListWidgetItem *item, selected) { delete item; } enableButtonOk(m_ui.lstFiles->count() > 0); } void KisDlgImportImageSequence::slotSkipChanged(int) { int documentFps = m_document->image()->animationInterface()->framerate(); float sourceFps = 1.0f * documentFps / m_ui.spinStep->value(); m_ui.lblFramerate->setText(i18n("Source fps: %1", sourceFps)); } void KisDlgImportImageSequence::slotOrderOptionsChanged(int) { sortFileList(); } void KisDlgImportImageSequence::sortFileList() { int order = m_ui.cmbOrder->currentData().toInt(); bool numeric = m_ui.cmbSortMode->currentData().toInt() == Numerical; m_collator.setNumericMode(numeric); m_ui.lstFiles->sortItems((order == Ascending) ? Qt::AscendingOrder : Qt::DescendingOrder); } diff --git a/libs/ui/dialogs/kis_dlg_layer_properties.cc b/libs/ui/dialogs/kis_dlg_layer_properties.cc index 32d387c2ff..dc517694c9 100644 --- a/libs/ui/dialogs/kis_dlg_layer_properties.cc +++ b/libs/ui/dialogs/kis_dlg_layer_properties.cc @@ -1,296 +1,296 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (c) 2011 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_dlg_layer_properties.h" #include #include #include #include #include #include #include #include #include "KisViewManager.h" #include #include #include "widgets/kis_cmb_composite.h" #include "KoColorProfile.h" #include "kis_multinode_property.h" #include "kis_layer_utils.h" #include "kis_image.h" #include "kis_layer_properties_icons.h" #include "kis_signal_compressor.h" #include "commands_new/kis_saved_commands.h" #include "kis_post_execution_undo_adapter.h" struct KisDlgLayerProperties::Private { Private() : updatesCompressor(500, KisSignalCompressor::POSTPONE) {} KisNodeList nodes; const KoColorSpace *colorSpace; KisViewManager *view; WdgLayerProperties *page; QSharedPointer compositeOpProperty; QSharedPointer opacityProperty; QSharedPointer nameProperty; QSharedPointer colorLabelProperty; QList layerProperties; QList > layerPropCheckboxes; QList channelFlagsProps; QList > channelFlagsCheckboxes; KisSignalCompressor updatesCompressor; QList allProperties() const { QList props; props << compositeOpProperty; props << opacityProperty; props << nameProperty; props << layerProperties; props << channelFlagsProps; props << colorLabelProperty; return props; } }; -KisDlgLayerProperties::KisDlgLayerProperties(KisNodeList nodes, KisViewManager *view, QWidget *parent, const char *name, Qt::WFlags f) +KisDlgLayerProperties::KisDlgLayerProperties(KisNodeList nodes, KisViewManager *view, QWidget *parent, const char *name, Qt::WindowFlags f) : KoDialog(parent) , d(new Private()) { nodes = KisLayerUtils::sortMergableNodes(view->image()->root(), nodes); d->nodes = nodes; Q_UNUSED(f); setCaption(i18n("Layer Properties")); setButtons(Ok | Cancel); setDefaultButton(Ok); setModal(false); setObjectName(name); d->page = new WdgLayerProperties(this); setMainWidget(d->page); d->view = view; d->colorSpace = d->nodes.first()->colorSpace(); d->page->editName->setFocus(); d->nameProperty.reset(new KisMultinodeNameProperty(nodes)); d->nameProperty->connectIgnoreCheckBox(d->page->chkName); d->nameProperty->connectAutoEnableWidget(d->page->editName); d->nameProperty->connectValueChangedSignal(this, SLOT(slotNameValueChangedInternally())); connect(d->page->editName, SIGNAL(textChanged(const QString &)), SLOT(slotNameValueChangedExternally())); d->page->intOpacity->setRange(0, 100); d->page->intOpacity->setSuffix("%"); d->opacityProperty.reset(new KisMultinodeOpacityProperty(nodes)); d->opacityProperty->connectIgnoreCheckBox(d->page->chkOpacity); d->opacityProperty->connectAutoEnableWidget(d->page->intOpacity); d->opacityProperty->connectValueChangedSignal(this, SLOT(slotOpacityValueChangedInternally())); d->opacityProperty->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); connect(d->page->intOpacity, SIGNAL(valueChanged(int)), SLOT(slotOpacityValueChangedExternally())); d->compositeOpProperty.reset(new KisMultinodeCompositeOpProperty(nodes)); d->compositeOpProperty->connectIgnoreCheckBox(d->page->chkCompositeOp); d->compositeOpProperty->connectAutoEnableWidget(d->page->cmbComposite); d->compositeOpProperty->connectValueChangedSignal(this, SLOT(slotCompositeOpValueChangedInternally())); d->compositeOpProperty->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); connect(d->page->cmbComposite, SIGNAL(currentIndexChanged(int)), SLOT(slotCompositeOpValueChangedExternally())); d->page->colorLabelSelector->setFocusPolicy(Qt::StrongFocus); d->colorLabelProperty.reset(new KisMultinodeColorLabelProperty(nodes)); d->colorLabelProperty->connectIgnoreCheckBox(d->page->chkColorLabel); d->colorLabelProperty->connectAutoEnableWidget(d->page->colorLabelSelector); d->colorLabelProperty->connectValueChangedSignal(this, SLOT(slotColorLabelValueChangedInternally())); d->colorLabelProperty->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); connect(d->page->colorLabelSelector, SIGNAL(currentIndexChanged(int)), SLOT(slotColorLabelValueChangedExternally())); if (!KisLayerUtils::checkNodesDiffer(d->nodes, [](KisNodeSP node) { return node->colorSpace(); })) { d->page->lblColorSpace->setText(d->colorSpace->name()); if (const KoColorProfile* profile = d->colorSpace->profile()) { d->page->lblProfile->setText(profile->name()); } ChannelFlagAdapter::PropertyList props = ChannelFlagAdapter::adaptersList(nodes); if (!props.isEmpty()) { QVBoxLayout *vbox = new QVBoxLayout; Q_FOREACH (const ChannelFlagAdapter::Property &prop, props) { QCheckBox *chk = new QCheckBox(prop.name, this); vbox->addWidget(chk); KisMultinodePropertyInterface *multiprop = new KisMultinodeProperty( nodes, ChannelFlagAdapter(prop)); multiprop->connectIgnoreCheckBox(chk); multiprop->connectValueChangedSignal(this, SLOT(slotFlagsValueChangedInternally())); multiprop->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); d->channelFlagsCheckboxes << chk; d->channelFlagsProps << toQShared(multiprop); } d->page->grpActiveChannels->setLayout(vbox); } else { d->page->grpActiveChannels->setVisible(false); d->page->lineActiveChannels->setVisible(false); } } else { d->page->grpActiveChannels->setVisible(false); d->page->lineActiveChannels->setVisible(false); d->page->cmbComposite->setEnabled(false); d->page->chkCompositeOp->setEnabled(false); d->page->lblColorSpace->setText(i18n("*varies*")); d->page->lblProfile->setText(i18n("*varies*")); } { QVBoxLayout *vbox = new QVBoxLayout; KisBaseNode::PropertyList props = LayerPropertyAdapter::adaptersList(nodes); Q_FOREACH (const KisBaseNode::Property &prop, props) { QCheckBox *chk = new QCheckBox(prop.name, this); chk->setIcon(prop.onIcon); vbox->addWidget(chk); KisMultinodePropertyInterface *multiprop = new KisMultinodeProperty( nodes, LayerPropertyAdapter(prop.name)); multiprop->connectIgnoreCheckBox(chk); multiprop->connectValueChangedSignal(this, SLOT(slotPropertyValueChangedInternally())); multiprop->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); d->layerPropCheckboxes << chk; d->layerProperties << toQShared(multiprop); } d->page->grpProperties->setLayout(vbox); } connect(&d->updatesCompressor, SIGNAL(timeout()), SLOT(updatePreview())); } KisDlgLayerProperties::~KisDlgLayerProperties() { if (result() == QDialog::Accepted) { if (d->updatesCompressor.isActive()) { d->updatesCompressor.stop(); updatePreview(); } KisPostExecutionUndoAdapter *adapter = d->view->image()->postExecutionUndoAdapter(); KisSavedMacroCommand *macro = adapter->createMacro(kundo2_i18n("Change Layer Properties")); macro->addCommand(toQShared(new KisLayerUtils::KisSimpleUpdateCommand(d->nodes, false))); Q_FOREACH(auto prop, d->allProperties()) { if (!prop->isIgnored()) { macro->addCommand(toQShared(prop->createPostExecutionUndoCommand())); } } macro->addCommand(toQShared(new KisLayerUtils::KisSimpleUpdateCommand(d->nodes, true))); adapter->addMacro(macro); } else /* if (result() == QDialog::Rejected) */ { Q_FOREACH(auto prop, d->allProperties()) { prop->setIgnored(true); } updatePreview(); } } void KisDlgLayerProperties::slotCompositeOpValueChangedInternally() { d->page->cmbComposite->validate(d->colorSpace); d->page->cmbComposite->selectCompositeOp(KoID(d->compositeOpProperty->value())); d->page->cmbComposite->setEnabled(!d->compositeOpProperty->isIgnored()); } void KisDlgLayerProperties::slotCompositeOpValueChangedExternally() { if (d->compositeOpProperty->isIgnored()) return; d->compositeOpProperty->setValue(d->page->cmbComposite->selectedCompositeOp().id()); } void KisDlgLayerProperties::slotColorLabelValueChangedInternally() { d->page->colorLabelSelector->setCurrentIndex(d->colorLabelProperty->value()); d->page->colorLabelSelector->setEnabled(!d->colorLabelProperty->isIgnored()); } void KisDlgLayerProperties::slotColorLabelValueChangedExternally() { if (d->colorLabelProperty->isIgnored()) return; d->colorLabelProperty->setValue(d->page->colorLabelSelector->currentIndex()); } void KisDlgLayerProperties::slotOpacityValueChangedInternally() { d->page->intOpacity->setValue(d->opacityProperty->value()); d->page->intOpacity->setEnabled(!d->opacityProperty->isIgnored()); } void KisDlgLayerProperties::slotOpacityValueChangedExternally() { if (d->opacityProperty->isIgnored()) return; d->opacityProperty->setValue(d->page->intOpacity->value()); } void KisDlgLayerProperties::slotNameValueChangedInternally() { d->page->editName->setText(d->nameProperty->value()); d->page->editName->setEnabled(!d->nameProperty->isIgnored()); } void KisDlgLayerProperties::slotNameValueChangedExternally() { if (d->nameProperty->isIgnored()) return; d->nameProperty->setValue(d->page->editName->text()); } void KisDlgLayerProperties::slotPropertyValueChangedInternally() { Q_FOREACH (KisMultinodePropertyInterfaceSP prop, d->channelFlagsProps) { prop->rereadCurrentValue(); } } void KisDlgLayerProperties::slotFlagsValueChangedInternally() { Q_FOREACH (KisMultinodePropertyInterfaceSP prop, d->layerProperties) { prop->rereadCurrentValue(); } } void KisDlgLayerProperties::updatePreview() { KisLayerUtils::KisSimpleUpdateCommand::updateNodes(d->nodes); } diff --git a/libs/ui/dialogs/kis_dlg_layer_properties.h b/libs/ui/dialogs/kis_dlg_layer_properties.h index 2ba053f73c..4ed5bd1aeb 100644 --- a/libs/ui/dialogs/kis_dlg_layer_properties.h +++ b/libs/ui/dialogs/kis_dlg_layer_properties.h @@ -1,86 +1,86 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (c) 2011 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_DLG_LAYER_PROPERTIES_H_ #define KIS_DLG_LAYER_PROPERTIES_H_ #include #include #include #include "kis_types.h" #include #include "ui_wdglayerproperties.h" class QWidget; class QBitArray; class KisViewManager; class KisDocument; class WdgLayerProperties : public QWidget, public Ui::WdgLayerProperties { Q_OBJECT public: WdgLayerProperties(QWidget *parent) : QWidget(parent) { setupUi(this); } }; /** * KisDlgLayerProperties is a dialogue for displaying and modifying information on a KisLayer. * The dialog is non modal by default and uses a timer to check for user changes to the * configuration, showing a preview of them. */ class KisDlgLayerProperties : public KoDialog { Q_OBJECT public: - KisDlgLayerProperties(KisNodeList nodes, KisViewManager *view, QWidget *parent = 0, const char *name = 0, Qt::WFlags f = 0); + KisDlgLayerProperties(KisNodeList nodes, KisViewManager *view, QWidget *parent = 0, const char *name = 0, Qt::WindowFlags f = 0); ~KisDlgLayerProperties() override; protected Q_SLOTS: void updatePreview(); void slotCompositeOpValueChangedInternally(); void slotCompositeOpValueChangedExternally(); void slotColorLabelValueChangedInternally(); void slotColorLabelValueChangedExternally(); void slotOpacityValueChangedInternally(); void slotOpacityValueChangedExternally(); void slotNameValueChangedInternally(); void slotNameValueChangedExternally(); void slotPropertyValueChangedInternally(); void slotFlagsValueChangedInternally(); private: struct Private; const QScopedPointer d; }; #endif // KIS_DLG_LAYER_PROPERTIES_H_ diff --git a/libs/ui/dialogs/kis_dlg_preferences.cc b/libs/ui/dialogs/kis_dlg_preferences.cc index 20feb22b2b..bd16e5c117 100644 --- a/libs/ui/dialogs/kis_dlg_preferences.cc +++ b/libs/ui/dialogs/kis_dlg_preferences.cc @@ -1,1269 +1,1269 @@ /* * preferencesdlg.cc - part of KImageShop * * Copyright (c) 1999 Michael Koch * Copyright (c) 2003-2011 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_dlg_preferences.h" #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoID.h" #include #include #include #include #include #include "kis_action_registry.h" #include "widgets/squeezedcombobox.h" #include "kis_clipboard.h" #include "widgets/kis_cmb_idlist.h" #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" #include "KoColorConversionTransformation.h" #include "kis_cursor.h" #include "kis_config.h" #include "kis_canvas_resource_provider.h" #include "kis_preference_set_registry.h" #include "kis_color_manager.h" #include "KisProofingConfiguration.h" #include "kis_image_config.h" #include "slider_and_spin_box_sync.h" // for the performance update #include #include #include "input/config/kis_input_configuration_page.h" #ifdef Q_OS_WIN # include #endif GeneralTab::GeneralTab(QWidget *_parent, const char *_name) : WdgGeneralSettings(_parent, _name) { KisConfig cfg; m_cmbCursorShape->addItem(i18n("No Cursor")); m_cmbCursorShape->addItem(i18n("Tool Icon")); m_cmbCursorShape->addItem(i18n("Arrow")); m_cmbCursorShape->addItem(i18n("Small Circle")); m_cmbCursorShape->addItem(i18n("Crosshair")); m_cmbCursorShape->addItem(i18n("Triangle Righthanded")); m_cmbCursorShape->addItem(i18n("Triangle Lefthanded")); m_cmbCursorShape->addItem(i18n("Black Pixel")); m_cmbCursorShape->addItem(i18n("White Pixel")); m_cmbOutlineShape->addItem(i18n("No Outline")); m_cmbOutlineShape->addItem(i18n("Circle Outline")); m_cmbOutlineShape->addItem(i18n("Preview Outline")); m_cmbOutlineShape->addItem(i18n("Tilt Outline")); m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle()); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle()); chkShowRootLayer->setChecked(cfg.showRootLayer()); int autosaveInterval = cfg.autoSaveInterval(); //convert to minutes m_autosaveSpinBox->setValue(autosaveInterval / 60); m_autosaveCheckBox->setChecked(autosaveInterval > 0); m_undoStackSize->setValue(cfg.undoStackLimit()); m_backupFileCheckBox->setChecked(cfg.backupFile()); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting()); m_hideSplashScreen->setChecked(cfg.hideSplashScreen()); KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); m_chkNativeFileDialog->setChecked(!group.readEntry("DontUseNativeFileDialog", true)); intMaxBrushSize->setValue(cfg.readEntry("maximumBrushSize", 1000)); m_cmbMDIType->setCurrentIndex(cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView)); m_chkRubberBand->setChecked(cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets()); KoColor mdiColor; mdiColor.fromQColor(cfg.getMDIBackgroundColor()); m_mdiColor->setColor(mdiColor); m_backgroundimage->setText(cfg.getMDIBackgroundImage()); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages()); m_chkCompressKra->setChecked(cfg.compressKra()); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); m_chkHiDPI->setChecked(kritarc.value("EnableHiDPI", false).toBool()); m_chkSingleApplication->setChecked(kritarc.value("EnableSingleApplication", true).toBool()); m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker()); m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt()); chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas()); m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport()); m_chkCacheAnimatioInBackground->setChecked(cfg.calculateAnimationCacheInBackground()); KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8()); cursorColor.fromQColor(cfg.getCursorMainColor()); cursorColorBtutton->setColor(cursorColor); connect(m_bnFileName, SIGNAL(clicked()), SLOT(getBackgroundImage())); connect(clearBgImageButton, SIGNAL(clicked()), SLOT(clearBackgroundImage())); } void GeneralTab::setDefault() { KisConfig cfg; m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle(true)); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle(true)); chkShowRootLayer->setChecked(cfg.showRootLayer(true)); m_autosaveCheckBox->setChecked(cfg.autoSaveInterval(true) > 0); //convert to minutes m_autosaveSpinBox->setValue(cfg.autoSaveInterval(true) / 60); m_undoStackSize->setValue(cfg.undoStackLimit(true)); m_backupFileCheckBox->setChecked(cfg.backupFile(true)); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting(true)); m_hideSplashScreen->setChecked(cfg.hideSplashScreen(true)); m_chkNativeFileDialog->setChecked(false); intMaxBrushSize->setValue(1000); m_cmbMDIType->setCurrentIndex((int)QMdiArea::TabbedView); m_chkRubberBand->setChecked(cfg.useOpenGL(true)); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets(true)); KoColor mdiColor; mdiColor.fromQColor(cfg.getMDIBackgroundColor(true)); m_mdiColor->setColor(mdiColor); m_backgroundimage->setText(cfg.getMDIBackgroundImage(true)); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages(true)); m_chkCompressKra->setChecked(cfg.compressKra(true)); m_chkHiDPI->setChecked(false); m_chkSingleApplication->setChecked(true); m_chkHiDPI->setChecked(true); m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker(true)); m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt(true)); chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas(true)); m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport(true)); m_chkCacheAnimatioInBackground->setChecked(cfg.calculateAnimationCacheInBackground(true)); KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8()); cursorColor.fromQColor(cfg.getCursorMainColor(true)); cursorColorBtutton->setColor(cursorColor); } CursorStyle GeneralTab::cursorStyle() { return (CursorStyle)m_cmbCursorShape->currentIndex(); } OutlineStyle GeneralTab::outlineStyle() { return (OutlineStyle)m_cmbOutlineShape->currentIndex(); } bool GeneralTab::showRootLayer() { return chkShowRootLayer->isChecked(); } int GeneralTab::autoSaveInterval() { //convert to seconds return m_autosaveCheckBox->isChecked() ? m_autosaveSpinBox->value() * 60 : 0; } int GeneralTab::undoStackSize() { return m_undoStackSize->value(); } bool GeneralTab::showOutlineWhilePainting() { return m_showOutlinePainting->isChecked(); } bool GeneralTab::hideSplashScreen() { return m_hideSplashScreen->isChecked(); } int GeneralTab::mdiMode() { return m_cmbMDIType->currentIndex(); } int GeneralTab::favoritePresets() { return m_favoritePresetsSpinBox->value(); } bool GeneralTab::showCanvasMessages() { return m_chkCanvasMessages->isChecked(); } bool GeneralTab::compressKra() { return m_chkCompressKra->isChecked(); } bool GeneralTab::toolOptionsInDocker() { return m_radioToolOptionsInDocker->isChecked(); } bool GeneralTab::switchSelectionCtrlAlt() { return m_chkSwitchSelectionCtrlAlt->isChecked(); } bool GeneralTab::convertToImageColorspaceOnImport() { return m_chkConvertOnImport->isChecked(); } bool GeneralTab::calculateAnimationCacheInBackground() { return m_chkCacheAnimatioInBackground->isChecked(); } void GeneralTab::getBackgroundImage() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "BackgroundImages"); dialog.setCaption(i18n("Select a Background Image")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setImageFilters(); QString fn = dialog.filename(); // dialog box was canceled or somehow no file was selected if (fn.isEmpty()) { return; } QImage image(fn); if (image.isNull()) { QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("%1 is not a valid image file!", fn)); } else { m_backgroundimage->setText(fn); } } void GeneralTab::clearBackgroundImage() { // clearing the background image text will implicitly make the background color be used m_backgroundimage->setText(""); } #include "kactioncollection.h" #include "KisActionsSnapshot.h" ShortcutSettingsTab::ShortcutSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgShortcutSettings(this); l->addWidget(m_page, 0, 0); m_snapshot.reset(new KisActionsSnapshot); KActionCollection *collection = KisPart::instance()->currentMainwindow()->actionCollection(); Q_FOREACH (QAction *action, collection->actions()) { m_snapshot->addAction(action->objectName(), action); } QMap sortedCollections = m_snapshot->actionCollections(); for (auto it = sortedCollections.constBegin(); it != sortedCollections.constEnd(); ++it) { m_page->addCollection(it.value(), it.key()); } } ShortcutSettingsTab::~ShortcutSettingsTab() { } void ShortcutSettingsTab::setDefault() { m_page->allDefault(); } void ShortcutSettingsTab::saveChanges() { m_page->save(); KisActionRegistry::instance()->settingsPageSaved(); } void ShortcutSettingsTab::cancelChanges() { m_page->undo(); } ColorSettingsTab::ColorSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); // XXX: Make sure only profiles that fit the specified color model // are shown in the profile combos QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgColorSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg; m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile()); connect(m_page->chkUseSystemMonitorProfile, SIGNAL(toggled(bool)), this, SLOT(toggleAllowMonitorProfileSelection(bool))); m_page->cmbWorkingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys()); m_page->cmbWorkingColorSpace->setCurrent(cfg.workingColorSpace()); m_page->bnAddColorProfile->setIcon(KisIconUtils::loadIcon("document-open")); m_page->bnAddColorProfile->setToolTip( i18n("Open Color Profile") ); connect(m_page->bnAddColorProfile, SIGNAL(clicked()), SLOT(installProfile())); QFormLayout *monitorProfileGrid = new QFormLayout(m_page->monitorprofileholder); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { QLabel *lbl = new QLabel(i18nc("The number of the screen", "Screen %1:", i + 1)); m_monitorProfileLabels << lbl; SqueezedComboBox *cmb = new SqueezedComboBox(); cmb->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); monitorProfileGrid->addRow(lbl, cmb); m_monitorProfileWidgets << cmb; } refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation()); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization()); KisImageConfig cfgImage; KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration(); m_page->sldAdaptationState->setMaximum(20); m_page->sldAdaptationState->setMinimum(0); m_page->sldAdaptationState->setValue((int)proofingConfig->adaptationState*20); //probably this should become the screenprofile? KoColor ga(KoColorSpaceRegistry::instance()->rgb8()); ga.fromKoColor(proofingConfig->warningColor); m_page->gamutAlarm->setColor(ga); const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel, proofingConfig->proofingDepth, proofingConfig->proofingProfile); if (proofingSpace) { m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace); } m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent); m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation)); m_pasteBehaviourGroup.addButton(m_page->radioPasteWeb, PASTE_ASSUME_WEB); m_pasteBehaviourGroup.addButton(m_page->radioPasteMonitor, PASTE_ASSUME_MONITOR); m_pasteBehaviourGroup.addButton(m_page->radioPasteAsk, PASTE_ASK); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour()); Q_ASSERT(button); if (button) { button->setChecked(true); } m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent()); toggleAllowMonitorProfileSelection(cfg.useSystemMonitorProfile()); } void ColorSettingsTab::installProfile() { KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); dialog.setMimeTypeFilters(QStringList() << "application/vnd.iccprofile", "application/vnd.iccprofile"); QStringList profileNames = dialog.filenames(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KoResourcePaths::saveLocation("icc_profiles"); Q_FOREACH (const QString &profileName, profileNames) { if (!QFile::copy(profileName, saveLocation + QFileInfo(profileName).fileName())) { qWarning() << "Could not install profile!" << saveLocation + QFileInfo(profileName).fileName(); continue; } iccEngine->addProfile(saveLocation + QFileInfo(profileName).fileName()); } KisConfig cfg; refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } } void ColorSettingsTab::toggleAllowMonitorProfileSelection(bool useSystemProfile) { if (useSystemProfile) { KisConfig cfg; QStringList devices = KisColorManager::instance()->devices(); if (devices.size() == QApplication::desktop()->screenCount()) { for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); QString monitorForScreen = cfg.monitorForScreen(i, devices[i]); Q_FOREACH (const QString &device, devices) { m_monitorProfileLabels[i]->setText(i18nc("The display/screen we got from Qt", "Screen %1:", i + 1)); m_monitorProfileWidgets[i]->addSqueezedItem(KisColorManager::instance()->deviceName(device), device); if (devices[i] == monitorForScreen) { m_monitorProfileWidgets[i]->setCurrentIndex(i); } } } } } else { KisConfig cfg; refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } } } void ColorSettingsTab::setDefault() { m_page->cmbWorkingColorSpace->setCurrent("RGBA"); refillMonitorProfiles(KoID("RGBA", "")); KisConfig cfg; KisImageConfig cfgImage; KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration(); const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel,proofingConfig->proofingDepth,proofingConfig->proofingProfile); if (proofingSpace) { m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace); } m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent); m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation)); m_page->sldAdaptationState->setValue(0); //probably this should become the screenprofile? KoColor ga(KoColorSpaceRegistry::instance()->rgb8()); ga.fromKoColor(proofingConfig->warningColor); m_page->gamutAlarm->setColor(ga); m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation(true)); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization(true)); m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent(true)); m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile(true)); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour(true)); Q_ASSERT(button); if (button) { button->setChecked(true); } } void ColorSettingsTab::refillMonitorProfiles(const KoID & colorSpaceId) { for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); } QMap profileList; Q_FOREACH(const KoColorProfile *profile, KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId.id())) { profileList[profile->name()] = profile; } Q_FOREACH (const KoColorProfile *profile, profileList.values()) { //qDebug() << "Profile" << profile->name() << profile->isSuitableForDisplay() << csf->defaultProfile(); if (profile->isSuitableForDisplay()) { for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->addSqueezedItem(profile->name()); } } } for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileLabels[i]->setText(i18nc("The number of the screen", "Screen %1:", i + 1)); m_monitorProfileWidgets[i]->setCurrent(KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId.id())); } } //--------------------------------------------------------------------------------------------------- void TabletSettingsTab::setDefault() { KisCubicCurve curve; curve.fromString(DEFAULT_CURVE_STRING); m_page->pressureCurve->setCurve(curve); #ifdef Q_OS_WIN if (KisTabletSupportWin8::isAvailable()) { KisConfig cfg; m_page->radioWintab->setChecked(!cfg.useWin8PointerInput(true)); m_page->radioWin8PointerInput->setChecked(cfg.useWin8PointerInput(true)); } else { m_page->radioWintab->setChecked(true); m_page->radioWin8PointerInput->setChecked(false); } #endif } TabletSettingsTab::TabletSettingsTab(QWidget* parent, const char* name): QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgTabletSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg; KisCubicCurve curve; curve.fromString( cfg.pressureTabletCurve() ); m_page->pressureCurve->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); m_page->pressureCurve->setCurve(curve); #ifdef Q_OS_WIN if (KisTabletSupportWin8::isAvailable()) { m_page->radioWintab->setChecked(!cfg.useWin8PointerInput()); m_page->radioWin8PointerInput->setChecked(cfg.useWin8PointerInput()); } else { m_page->radioWintab->setChecked(true); m_page->radioWin8PointerInput->setChecked(false); m_page->grpTabletApi->setVisible(false); } #else m_page->grpTabletApi->setVisible(false); #endif } //--------------------------------------------------------------------------------------------------- #include "kis_acyclic_signal_connector.h" int getTotalRAM() { KisImageConfig cfg; return cfg.totalRAM(); } int PerformanceTab::realTilesRAM() { return intMemoryLimit->value() - intPoolLimit->value(); } PerformanceTab::PerformanceTab(QWidget *parent, const char *name) : WdgPerformanceSettings(parent, name) { KisImageConfig cfg; const int totalRAM = cfg.totalRAM(); lblTotalMemory->setText(i18n("%1 MiB", totalRAM)); sliderMemoryLimit->setSuffix(i18n(" %")); sliderMemoryLimit->setRange(1, 100, 2); sliderMemoryLimit->setSingleStep(0.01); sliderPoolLimit->setSuffix(i18n(" %")); sliderPoolLimit->setRange(0, 20, 2); sliderMemoryLimit->setSingleStep(0.01); sliderUndoLimit->setSuffix(i18n(" %")); sliderUndoLimit->setRange(0, 50, 2); sliderMemoryLimit->setSingleStep(0.01); intMemoryLimit->setMinimumWidth(80); intPoolLimit->setMinimumWidth(80); intUndoLimit->setMinimumWidth(80); SliderAndSpinBoxSync *sync1 = new SliderAndSpinBoxSync(sliderMemoryLimit, intMemoryLimit, getTotalRAM); sync1->slotParentValueChanged(); m_syncs << sync1; SliderAndSpinBoxSync *sync2 = new SliderAndSpinBoxSync(sliderPoolLimit, intPoolLimit, std::bind(&KisIntParseSpinBox::value, intMemoryLimit)); connect(intMemoryLimit, SIGNAL(valueChanged(int)), sync2, SLOT(slotParentValueChanged())); sync2->slotParentValueChanged(); m_syncs << sync2; SliderAndSpinBoxSync *sync3 = new SliderAndSpinBoxSync(sliderUndoLimit, intUndoLimit, std::bind(&PerformanceTab::realTilesRAM, this)); connect(intPoolLimit, SIGNAL(valueChanged(int)), sync3, SLOT(slotParentValueChanged())); sync3->slotParentValueChanged(); m_syncs << sync3; sliderSwapSize->setSuffix(i18n(" GiB")); sliderSwapSize->setRange(1, 64); intSwapSize->setRange(1, 64); KisAcyclicSignalConnector *swapSizeConnector = new KisAcyclicSignalConnector(this); swapSizeConnector->connectForwardInt(sliderSwapSize, SIGNAL(valueChanged(int)), intSwapSize, SLOT(setValue(int))); swapSizeConnector->connectBackwardInt(intSwapSize, SIGNAL(valueChanged(int)), sliderSwapSize, SLOT(setValue(int))); lblSwapFileLocation->setText(cfg.swapDir()); connect(bnSwapFile, SIGNAL(clicked()), SLOT(selectSwapDir())); sliderThreadsLimit->setRange(1, QThread::idealThreadCount()); sliderFrameClonesLimit->setRange(1, QThread::idealThreadCount()); sliderFpsLimit->setRange(20, 100); sliderFpsLimit->setSuffix(i18n(" fps")); connect(sliderThreadsLimit, SIGNAL(valueChanged(int)), SLOT(slotThreadsLimitChanged(int))); connect(sliderFrameClonesLimit, SIGNAL(valueChanged(int)), SLOT(slotFrameClonesLimitChanged(int))); connect(sliderFpsLimit, SIGNAL(valueChanged(int)), SLOT(slotFpsLimitChanged(int))); load(false); } PerformanceTab::~PerformanceTab() { qDeleteAll(m_syncs); } void PerformanceTab::load(bool requestDefault) { KisImageConfig cfg; sliderMemoryLimit->setValue(cfg.memoryHardLimitPercent(requestDefault)); sliderPoolLimit->setValue(cfg.memoryPoolLimitPercent(requestDefault)); sliderUndoLimit->setValue(cfg.memorySoftLimitPercent(requestDefault)); chkPerformanceLogging->setChecked(cfg.enablePerfLog(requestDefault)); chkProgressReporting->setChecked(cfg.enableProgressReporting(requestDefault)); sliderSwapSize->setValue(cfg.maxSwapSize(requestDefault) / 1024); lblSwapFileLocation->setText(cfg.swapDir(requestDefault)); m_lastUsedThreadsLimit = cfg.maxNumberOfThreads(requestDefault); m_lastUsedClonesLimit = cfg.frameRenderingClones(requestDefault); m_lastUsedFpsLimit = cfg.fpsLimit(requestDefault); sliderThreadsLimit->setValue(m_lastUsedThreadsLimit); sliderFrameClonesLimit->setValue(m_lastUsedClonesLimit); sliderFpsLimit->setValue(m_lastUsedFpsLimit); { KisConfig cfg2; chkOpenGLFramerateLogging->setChecked(cfg2.enableOpenGLFramerateLogging(requestDefault)); chkDisableVectorOptimizations->setChecked(cfg2.enableAmdVectorizationWorkaround(requestDefault)); } } void PerformanceTab::save() { KisImageConfig cfg; cfg.setMemoryHardLimitPercent(sliderMemoryLimit->value()); cfg.setMemorySoftLimitPercent(sliderUndoLimit->value()); cfg.setMemoryPoolLimitPercent(sliderPoolLimit->value()); cfg.setEnablePerfLog(chkPerformanceLogging->isChecked()); cfg.setEnableProgressReporting(chkProgressReporting->isChecked()); cfg.setMaxSwapSize(sliderSwapSize->value() * 1024); cfg.setSwapDir(lblSwapFileLocation->text()); cfg.setMaxNumberOfThreads(sliderThreadsLimit->value()); cfg.setFrameRenderingClones(sliderFrameClonesLimit->value()); cfg.setFpsLimit(sliderFpsLimit->value()); { KisConfig cfg2; cfg2.setEnableOpenGLFramerateLogging(chkOpenGLFramerateLogging->isChecked()); cfg2.setEnableAmdVectorizationWorkaround(chkDisableVectorOptimizations->isChecked()); } } void PerformanceTab::selectSwapDir() { KisImageConfig cfg; QString swapDir = cfg.swapDir(); swapDir = QFileDialog::getExistingDirectory(0, i18nc("@title:window", "Select a swap directory"), swapDir); if (swapDir.isEmpty()) { return; } lblSwapFileLocation->setText(swapDir); } void PerformanceTab::slotThreadsLimitChanged(int value) { KisSignalsBlocker b(sliderFrameClonesLimit); sliderFrameClonesLimit->setValue(qMin(m_lastUsedClonesLimit, value)); m_lastUsedThreadsLimit = value; } void PerformanceTab::slotFrameClonesLimitChanged(int value) { KisSignalsBlocker b(sliderThreadsLimit); sliderThreadsLimit->setValue(qMax(m_lastUsedThreadsLimit, value)); m_lastUsedClonesLimit = value; } void PerformanceTab::slotFpsLimitChanged(int value) { KisSignalsBlocker b(sliderFrameClonesLimit); sliderFrameClonesLimit->setValue(qMax(m_lastUsedFpsLimit, value)); m_lastUsedFpsLimit = value; } //--------------------------------------------------------------------------------------------------- #include "KoColor.h" DisplaySettingsTab::DisplaySettingsTab(QWidget *parent, const char *name) : WdgDisplaySettings(parent, name) { KisConfig cfg; const QString rendererOpenGLText = i18nc("canvas renderer", "OpenGL"); const QString rendererAngleText = i18nc("canvas renderer", "Direct3D 11 via ANGLE"); #ifdef Q_OS_WIN cmbRenderer->clear(); QString qtPreferredRendererText; if (KisOpenGL::getQtPreferredOpenGLRenderer() == KisOpenGL::RendererAngle) { qtPreferredRendererText = rendererAngleText; } else { qtPreferredRendererText = rendererOpenGLText; } cmbRenderer->addItem(i18nc("canvas renderer", "Auto (%1)", qtPreferredRendererText), KisOpenGL::RendererAuto); cmbRenderer->setCurrentIndex(0); if (KisOpenGL::getSupportedOpenGLRenderers() & KisOpenGL::RendererDesktopGL) { cmbRenderer->addItem(rendererOpenGLText, KisOpenGL::RendererDesktopGL); if (KisOpenGL::getNextUserOpenGLRendererConfig() == KisOpenGL::RendererDesktopGL) { cmbRenderer->setCurrentIndex(cmbRenderer->count() - 1); } } if (KisOpenGL::getSupportedOpenGLRenderers() & KisOpenGL::RendererAngle) { cmbRenderer->addItem(rendererAngleText, KisOpenGL::RendererAngle); if (KisOpenGL::getNextUserOpenGLRendererConfig() == KisOpenGL::RendererAngle) { cmbRenderer->setCurrentIndex(cmbRenderer->count() - 1); } } #else lblRenderer->setEnabled(false); cmbRenderer->setEnabled(false); cmbRenderer->clear(); cmbRenderer->addItem(rendererOpenGLText); cmbRenderer->setCurrentIndex(0); #endif #ifdef Q_OS_WIN if (!(KisOpenGL::getSupportedOpenGLRenderers() & (KisOpenGL::RendererDesktopGL | KisOpenGL::RendererAngle))) { #else if (!KisOpenGL::hasOpenGL()) { #endif grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL()); chkUseTextureBuffer->setEnabled(cfg.useOpenGL()); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer()); chkDisableVsync->setVisible(cfg.showAdvancedOpenGLSettings()); chkDisableVsync->setEnabled(cfg.useOpenGL()); chkDisableVsync->setChecked(cfg.disableVSync()); cmbFilterMode->setEnabled(cfg.useOpenGL()); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode()); // Don't show the high quality filtering mode if it's not available if (!KisOpenGL::supportsLoD()) { cmbFilterMode->removeItem(3); } } if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") { grpOpenGL->setVisible(false); grpOpenGL->setMaximumHeight(0); } KoColor c; c.fromQColor(cfg.selectionOverlayMaskColor()); c.setOpacity(1.0); btnSelectionOverlayColor->setColor(c); sldSelectionOverlayOpacity->setRange(0.0, 1.0, 2); sldSelectionOverlayOpacity->setSingleStep(0.05); sldSelectionOverlayOpacity->setValue(cfg.selectionOverlayMaskColor().alphaF()); intCheckSize->setValue(cfg.checkSize()); chkMoving->setChecked(cfg.scrollCheckers()); KoColor ck1(KoColorSpaceRegistry::instance()->rgb8()); ck1.fromQColor(cfg.checkersColor1()); colorChecks1->setColor(ck1); KoColor ck2(KoColorSpaceRegistry::instance()->rgb8()); ck2.fromQColor(cfg.checkersColor2()); colorChecks2->setColor(ck2); KoColor cb(KoColorSpaceRegistry::instance()->rgb8()); cb.fromQColor(cfg.canvasBorderColor()); canvasBorder->setColor(cb); hideScrollbars->setChecked(cfg.hideScrollbars()); chkCurveAntialiasing->setChecked(cfg.antialiasCurves()); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline()); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor()); chkHidePopups->setChecked(cfg.hidePopups()); connect(grpOpenGL, SIGNAL(toggled(bool)), SLOT(slotUseOpenGLToggled(bool))); KoColor gridColor(KoColorSpaceRegistry::instance()->rgb8()); gridColor.fromQColor(cfg.getPixelGridColor()); pixelGridColorButton->setColor(gridColor); pixelGridDrawingThresholdBox->setValue(cfg.getPixelGridDrawingThreshold() * 100); } void DisplaySettingsTab::setDefault() { KisConfig cfg; cmbRenderer->setCurrentIndex(0); #ifdef Q_OS_WIN if (!(KisOpenGL::getSupportedOpenGLRenderers() & (KisOpenGL::RendererDesktopGL | KisOpenGL::RendererAngle))) { #else if (!KisOpenGL::hasOpenGL()) { #endif grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL(true)); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer(true)); chkUseTextureBuffer->setEnabled(true); chkDisableVsync->setEnabled(true); chkDisableVsync->setChecked(cfg.disableVSync(true)); cmbFilterMode->setEnabled(true); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode(true)); } chkMoving->setChecked(cfg.scrollCheckers(true)); intCheckSize->setValue(cfg.checkSize(true)); KoColor ck1(KoColorSpaceRegistry::instance()->rgb8()); ck1.fromQColor(cfg.checkersColor1(true)); colorChecks1->setColor(ck1); KoColor ck2(KoColorSpaceRegistry::instance()->rgb8()); ck2.fromQColor(cfg.checkersColor2(true)); colorChecks2->setColor(ck2); KoColor cvb(KoColorSpaceRegistry::instance()->rgb8()); cvb.fromQColor(cfg.canvasBorderColor(true)); canvasBorder->setColor(cvb); hideScrollbars->setChecked(cfg.hideScrollbars(true)); chkCurveAntialiasing->setChecked(cfg.antialiasCurves(true)); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline(true)); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor(true)); chkHidePopups->setChecked(cfg.hidePopups(true)); KoColor gridColor(KoColorSpaceRegistry::instance()->rgb8()); gridColor.fromQColor(cfg.getPixelGridColor(true)); pixelGridColorButton->setColor(gridColor); pixelGridDrawingThresholdBox->setValue(cfg.getPixelGridDrawingThreshold(true) * 100); } void DisplaySettingsTab::slotUseOpenGLToggled(bool isChecked) { chkUseTextureBuffer->setEnabled(isChecked); chkDisableVsync->setEnabled(isChecked); cmbFilterMode->setEnabled(isChecked); } //--------------------------------------------------------------------------------------------------- FullscreenSettingsTab::FullscreenSettingsTab(QWidget* parent) : WdgFullscreenSettingsBase(parent) { KisConfig cfg; chkDockers->setChecked(cfg.hideDockersFullscreen()); chkMenu->setChecked(cfg.hideMenuFullscreen()); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen()); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen()); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen()); chkToolbar->setChecked(cfg.hideToolbarFullscreen()); } void FullscreenSettingsTab::setDefault() { KisConfig cfg; chkDockers->setChecked(cfg.hideDockersFullscreen(true)); chkMenu->setChecked(cfg.hideMenuFullscreen(true)); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen(true)); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen(true)); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen(true)); chkToolbar->setChecked(cfg.hideToolbarFullscreen(true)); } //--------------------------------------------------------------------------------------------------- KisDlgPreferences::KisDlgPreferences(QWidget* parent, const char* name) : KPageDialog(parent) { Q_UNUSED(name); setWindowTitle(i18n("Configure Krita")); setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults); button(QDialogButtonBox::Ok)->setDefault(true); setFaceType(KPageDialog::Tree); // General KoVBox *vbox = new KoVBox(); KPageWidgetItem *page = new KPageWidgetItem(vbox, i18n("General")); page->setObjectName("general"); page->setHeader(i18n("General")); page->setIcon(KisIconUtils::loadIcon("go-home")); addPage(page); m_general = new GeneralTab(vbox); // Shortcuts vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Keyboard Shortcuts")); page->setObjectName("shortcuts"); page->setHeader(i18n("Shortcuts")); page->setIcon(KisIconUtils::loadIcon("document-export")); addPage(page); m_shortcutSettings = new ShortcutSettingsTab(vbox); connect(this, SIGNAL(accepted()), m_shortcutSettings, SLOT(saveChanges())); connect(this, SIGNAL(rejected()), m_shortcutSettings, SLOT(cancelChanges())); // Canvas input settings m_inputConfiguration = new KisInputConfigurationPage(); page = addPage(m_inputConfiguration, i18n("Canvas Input Settings")); page->setHeader(i18n("Canvas Input")); page->setObjectName("canvasinput"); page->setIcon(KisIconUtils::loadIcon("configure")); // Display vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Display")); page->setObjectName("display"); page->setHeader(i18n("Display")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-display")); addPage(page); m_displaySettings = new DisplaySettingsTab(vbox); // Color vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Color Management")); page->setObjectName("colormanagement"); page->setHeader(i18n("Color")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-color")); addPage(page); m_colorSettings = new ColorSettingsTab(vbox); // Performance vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Performance")); page->setObjectName("performance"); page->setHeader(i18n("Performance")); page->setIcon(KisIconUtils::loadIcon("applications-system")); addPage(page); m_performanceSettings = new PerformanceTab(vbox); // Tablet vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Tablet settings")); page->setObjectName("tablet"); page->setHeader(i18n("Tablet")); page->setIcon(KisIconUtils::loadIcon("document-edit")); addPage(page); m_tabletSettings = new TabletSettingsTab(vbox); // full-screen mode vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Canvas-only settings")); page->setObjectName("canvasonly"); page->setHeader(i18n("Canvas-only")); page->setIcon(KisIconUtils::loadIcon("folder-pictures")); addPage(page); m_fullscreenSettings = new FullscreenSettingsTab(vbox); // Author profiles m_authorPage = new KoConfigAuthorPage(); page = addPage(m_authorPage, i18nc("@title:tab Author page", "Author" )); page->setObjectName("author"); page->setHeader(i18n("Author")); page->setIcon(KisIconUtils::loadIcon("im-user")); QPushButton *restoreDefaultsButton = button(QDialogButtonBox::RestoreDefaults); restoreDefaultsButton->setText("Restore Defaults"); connect(this, SIGNAL(accepted()), m_inputConfiguration, SLOT(saveChanges())); connect(this, SIGNAL(rejected()), m_inputConfiguration, SLOT(revertChanges())); KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); Q_FOREACH (KisAbstractPreferenceSetFactory *preferenceSetFactory, preferenceSetRegistry->values()) { KisPreferenceSet* preferenceSet = preferenceSetFactory->createPreferenceSet(); vbox = new KoVBox(); page = new KPageWidgetItem(vbox, preferenceSet->name()); page->setHeader(preferenceSet->header()); page->setIcon(preferenceSet->icon()); addPage(page); preferenceSet->setParent(vbox); preferenceSet->loadPreferences(); connect(restoreDefaultsButton, SIGNAL(clicked(bool)), preferenceSet, SLOT(loadDefaultPreferences()), Qt::UniqueConnection); connect(this, SIGNAL(accepted()), preferenceSet, SLOT(savePreferences()), Qt::UniqueConnection); } connect(restoreDefaultsButton, SIGNAL(clicked(bool)), this, SLOT(slotDefault())); } KisDlgPreferences::~KisDlgPreferences() { } void KisDlgPreferences::slotDefault() { if (currentPage()->objectName() == "general") { m_general->setDefault(); } else if (currentPage()->objectName() == "shortcuts") { m_shortcutSettings->setDefault(); } else if (currentPage()->objectName() == "display") { m_displaySettings->setDefault(); } else if (currentPage()->objectName() == "colormanagement") { m_colorSettings->setDefault(); } else if (currentPage()->objectName() == "performance") { m_performanceSettings->load(true); } else if (currentPage()->objectName() == "tablet") { m_tabletSettings->setDefault(); } else if (currentPage()->objectName() == "canvasonly") { m_fullscreenSettings->setDefault(); } else if (currentPage()->objectName() == "canvasinput") { m_inputConfiguration->setDefaults(); } } bool KisDlgPreferences::editPreferences() { KisDlgPreferences* dialog; dialog = new KisDlgPreferences(); bool baccept = (dialog->exec() == Accepted); if (baccept) { // General settings KisConfig cfg; cfg.setNewCursorStyle(dialog->m_general->cursorStyle()); cfg.setNewOutlineStyle(dialog->m_general->outlineStyle()); cfg.setShowRootLayer(dialog->m_general->showRootLayer()); cfg.setShowOutlineWhilePainting(dialog->m_general->showOutlineWhilePainting()); cfg.setHideSplashScreen(dialog->m_general->hideSplashScreen()); cfg.setCalculateAnimationCacheInBackground(dialog->m_general->calculateAnimationCacheInBackground()); KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); group.writeEntry("DontUseNativeFileDialog", !dialog->m_general->m_chkNativeFileDialog->isChecked()); cfg.writeEntry("maximumBrushSize", dialog->m_general->intMaxBrushSize->value()); cfg.writeEntry("mdi_viewmode", dialog->m_general->mdiMode()); cfg.setMDIBackgroundColor(dialog->m_general->m_mdiColor->color().toQColor()); cfg.setMDIBackgroundImage(dialog->m_general->m_backgroundimage->text()); cfg.setAutoSaveInterval(dialog->m_general->autoSaveInterval()); cfg.setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked()); cfg.setShowCanvasMessages(dialog->m_general->showCanvasMessages()); cfg.setCompressKra(dialog->m_general->compressKra()); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("EnableHiDPI", dialog->m_general->m_chkHiDPI->isChecked()); kritarc.setValue("EnableSingleApplication", dialog->m_general->m_chkSingleApplication->isChecked()); cfg.setToolOptionsInDocker(dialog->m_general->toolOptionsInDocker()); cfg.setSwitchSelectionCtrlAlt(dialog->m_general->switchSelectionCtrlAlt()); cfg.setDisableTouchOnCanvas(!dialog->m_general->chkEnableTouch->isChecked()); cfg.setConvertToImageColorspaceOnImport(dialog->m_general->convertToImageColorspaceOnImport()); cfg.setUndoStackLimit(dialog->m_general->undoStackSize()); cfg.setFavoritePresets(dialog->m_general->favoritePresets()); // Color settings cfg.setUseSystemMonitorProfile(dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()) { int currentIndex = dialog->m_colorSettings->m_monitorProfileWidgets[i]->currentIndex(); QString monitorid = dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemData(currentIndex).toString(); cfg.setMonitorForScreen(i, monitorid); } else { cfg.setMonitorProfile(i, dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemHighlighted(), dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); } } cfg.setWorkingColorSpace(dialog->m_colorSettings->m_page->cmbWorkingColorSpace->currentItem().id()); KisImageConfig cfgImage; cfgImage.setDefaultProofingConfig(dialog->m_colorSettings->m_page->proofingSpaceSelector->currentColorSpace(), dialog->m_colorSettings->m_page->cmbProofingIntent->currentIndex(), dialog->m_colorSettings->m_page->ckbProofBlackPoint->isChecked(), dialog->m_colorSettings->m_page->gamutAlarm->color(), (double)dialog->m_colorSettings->m_page->sldAdaptationState->value()/20); cfg.setUseBlackPointCompensation(dialog->m_colorSettings->m_page->chkBlackpoint->isChecked()); cfg.setAllowLCMSOptimization(dialog->m_colorSettings->m_page->chkAllowLCMSOptimization->isChecked()); cfg.setPasteBehaviour(dialog->m_colorSettings->m_pasteBehaviourGroup.checkedId()); cfg.setRenderIntent(dialog->m_colorSettings->m_page->cmbMonitorIntent->currentIndex()); // Tablet settings cfg.setPressureTabletCurve( dialog->m_tabletSettings->m_page->pressureCurve->curve().toString() ); #ifdef Q_OS_WIN if (KisTabletSupportWin8::isAvailable()) { cfg.setUseWin8PointerInput(dialog->m_tabletSettings->m_page->radioWin8PointerInput->isChecked()); } #endif dialog->m_performanceSettings->save(); #ifdef Q_OS_WIN { KisOpenGL::OpenGLRenderer renderer = static_cast( dialog->m_displaySettings->cmbRenderer->itemData( dialog->m_displaySettings->cmbRenderer->currentIndex()).toInt()); KisOpenGL::setNextUserOpenGLRendererConfig(renderer); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("OpenGLRenderer", KisOpenGL::convertOpenGLRendererToConfig(renderer)); } #endif if (!cfg.useOpenGL() && dialog->m_displaySettings->grpOpenGL->isChecked()) cfg.setCanvasState("TRY_OPENGL"); cfg.setUseOpenGL(dialog->m_displaySettings->grpOpenGL->isChecked()); cfg.setUseOpenGLTextureBuffer(dialog->m_displaySettings->chkUseTextureBuffer->isChecked()); cfg.setOpenGLFilteringMode(dialog->m_displaySettings->cmbFilterMode->currentIndex()); cfg.setDisableVSync(dialog->m_displaySettings->chkDisableVsync->isChecked()); cfg.setCheckSize(dialog->m_displaySettings->intCheckSize->value()); cfg.setScrollingCheckers(dialog->m_displaySettings->chkMoving->isChecked()); cfg.setCheckersColor1(dialog->m_displaySettings->colorChecks1->color().toQColor()); cfg.setCheckersColor2(dialog->m_displaySettings->colorChecks2->color().toQColor()); cfg.setCanvasBorderColor(dialog->m_displaySettings->canvasBorder->color().toQColor()); cfg.setHideScrollbars(dialog->m_displaySettings->hideScrollbars->isChecked()); KoColor c = dialog->m_displaySettings->btnSelectionOverlayColor->color(); c.setOpacity(dialog->m_displaySettings->sldSelectionOverlayOpacity->value()); cfg.setSelectionOverlayMaskColor(c.toQColor()); cfg.setAntialiasCurves(dialog->m_displaySettings->chkCurveAntialiasing->isChecked()); cfg.setAntialiasSelectionOutline(dialog->m_displaySettings->chkSelectionOutlineAntialiasing->isChecked()); cfg.setShowSingleChannelAsColor(dialog->m_displaySettings->chkChannelsAsColor->isChecked()); cfg.setHidePopups(dialog->m_displaySettings->chkHidePopups->isChecked()); cfg.setHideDockersFullscreen(dialog->m_fullscreenSettings->chkDockers->checkState()); cfg.setHideMenuFullscreen(dialog->m_fullscreenSettings->chkMenu->checkState()); cfg.setHideScrollbarsFullscreen(dialog->m_fullscreenSettings->chkScrollbars->checkState()); cfg.setHideStatusbarFullscreen(dialog->m_fullscreenSettings->chkStatusbar->checkState()); cfg.setHideTitlebarFullscreen(dialog->m_fullscreenSettings->chkTitlebar->checkState()); cfg.setHideToolbarFullscreen(dialog->m_fullscreenSettings->chkToolbar->checkState()); cfg.setCursorMainColor(dialog->m_general->cursorColorBtutton->color().toQColor()); cfg.setPixelGridColor(dialog->m_displaySettings->pixelGridColorButton->color().toQColor()); cfg.setPixelGridDrawingThreshold(dialog->m_displaySettings->pixelGridDrawingThresholdBox->value() / 100); dialog->m_authorPage->apply(); } delete dialog; return baccept; } diff --git a/libs/ui/input/config/kis_input_configuration_page_item.cpp b/libs/ui/input/config/kis_input_configuration_page_item.cpp index fc7a28c60f..9e5a10bb95 100644 --- a/libs/ui/input/config/kis_input_configuration_page_item.cpp +++ b/libs/ui/input/config/kis_input_configuration_page_item.cpp @@ -1,90 +1,90 @@ /* * This file is part of the KDE project * Copyright (C) 2013 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_input_configuration_page_item.h" #include "kis_icon_utils.h" #include "input/kis_abstract_input_action.h" #include "input/kis_input_profile_manager.h" #include "kis_action_shortcuts_model.h" #include "kis_input_type_delegate.h" #include "kis_input_mode_delegate.h" #include "kis_input_editor_delegate.h" #include "ui_kis_input_configuration_page_item.h" KisInputConfigurationPageItem::KisInputConfigurationPageItem(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { ui = new Ui::KisInputConfigurationPageItem; ui->setupUi(this); m_shortcutsModel = new KisActionShortcutsModel(this); ui->shortcutsView->setModel(m_shortcutsModel); ui->shortcutsView->setItemDelegateForColumn(0, new KisInputTypeDelegate(ui->shortcutsView)); ui->shortcutsView->setItemDelegateForColumn(1, new KisInputEditorDelegate(ui->shortcutsView)); ui->shortcutsView->setItemDelegateForColumn(2, new KisInputModeDelegate(ui->shortcutsView)); - ui->shortcutsView->header()->setResizeMode(QHeaderView::Stretch); + ui->shortcutsView->header()->setSectionResizeMode(QHeaderView::Stretch); setExpanded(false); QAction *deleteAction = new QAction(KisIconUtils::loadIcon("edit-delete"), i18n("Delete Shortcut"), ui->shortcutsView); connect(deleteAction, SIGNAL(triggered(bool)), SLOT(deleteShortcut())); ui->shortcutsView->addAction(deleteAction); ui->shortcutsView->setContextMenuPolicy(Qt::ActionsContextMenu); connect(ui->collapseButton, SIGNAL(clicked(bool)), SLOT(setExpanded(bool))); } KisInputConfigurationPageItem::~KisInputConfigurationPageItem() { delete ui; } void KisInputConfigurationPageItem::setAction(KisAbstractInputAction *action) { m_action = action; ui->collapseButton->setText(action->name()); ui->descriptionLabel->setText(action->description()); m_shortcutsModel->setProfile(KisInputProfileManager::instance()->currentProfile()); m_shortcutsModel->setAction(action); qobject_cast(ui->shortcutsView->itemDelegateForColumn(2))->setAction(action); } void KisInputConfigurationPageItem::setExpanded(bool expand) { if (expand) { ui->descriptionLabel->setVisible(true); ui->shortcutsView->setVisible(true); ui->collapseButton->setArrowType(Qt::DownArrow); } else { ui->descriptionLabel->setVisible(false); ui->shortcutsView->setVisible(false); ui->collapseButton->setArrowType(Qt::RightArrow); } } void KisInputConfigurationPageItem::deleteShortcut() { int row = ui->shortcutsView->selectionModel()->currentIndex().row(); if (m_shortcutsModel->canRemoveRow(row)) { m_shortcutsModel->removeRow(row, QModelIndex()); } } diff --git a/libs/ui/input/kis_abstract_input_action.cpp b/libs/ui/input/kis_abstract_input_action.cpp index 18e205c22b..3e525fec93 100644 --- a/libs/ui/input/kis_abstract_input_action.cpp +++ b/libs/ui/input/kis_abstract_input_action.cpp @@ -1,217 +1,217 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_abstract_input_action.h" #include #include #include #include class Q_DECL_HIDDEN KisAbstractInputAction::Private { public: QString id; QString name; QString description; QHash indexes; QPointF lastCursorPosition; static KisInputManager* inputManager; }; KisInputManager *KisAbstractInputAction::Private::inputManager = 0; KisAbstractInputAction::KisAbstractInputAction(const QString & id) : d(new Private) { d->id = id; d->indexes.insert(i18n("Activate"), 0); } KisAbstractInputAction::~KisAbstractInputAction() { delete d; } void KisAbstractInputAction::activate(int shortcut) { Q_UNUSED(shortcut); } void KisAbstractInputAction::deactivate(int shortcut) { Q_UNUSED(shortcut); } void KisAbstractInputAction::begin(int shortcut, QEvent *event) { Q_UNUSED(shortcut); if (event) { d->lastCursorPosition = eventPosF(event); } } void KisAbstractInputAction::inputEvent(QEvent* event) { if (event) { QPointF newPosition = eventPosF(event); cursorMoved(d->lastCursorPosition, newPosition); d->lastCursorPosition = newPosition; } } void KisAbstractInputAction::end(QEvent *event) { Q_UNUSED(event); } void KisAbstractInputAction::cursorMoved(const QPointF &lastPos, const QPointF &pos) { Q_UNUSED(lastPos); Q_UNUSED(pos); } bool KisAbstractInputAction::supportsHiResInputEvents() const { return false; } KisInputManager* KisAbstractInputAction::inputManager() const { return Private::inputManager; } QString KisAbstractInputAction::name() const { return d->name; } QString KisAbstractInputAction::description() const { return d->description; } int KisAbstractInputAction::priority() const { return 0; } bool KisAbstractInputAction::canIgnoreModifiers() const { return false; } QHash< QString, int > KisAbstractInputAction::shortcutIndexes() const { return d->indexes; } QString KisAbstractInputAction::id() const { return d->id; } void KisAbstractInputAction::setName(const QString& name) { d->name = name; } void KisAbstractInputAction::setDescription(const QString& description) { d->description = description; } void KisAbstractInputAction::setShortcutIndexes(const QHash< QString, int >& indexes) { d->indexes = indexes; } void KisAbstractInputAction::setInputManager(KisInputManager *manager) { Private::inputManager = manager; } bool KisAbstractInputAction::isShortcutRequired(int shortcut) const { Q_UNUSED(shortcut); return false; } QPoint KisAbstractInputAction::eventPos(const QEvent *event) { if(!event) { return QPoint(); } switch (event->type()) { case QEvent::MouseMove: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: return static_cast(event)->pos(); case QEvent::TabletMove: case QEvent::TabletPress: case QEvent::TabletRelease: return static_cast(event)->pos(); case QEvent::Wheel: return static_cast(event)->pos(); case QEvent::NativeGesture: return static_cast(event)->pos(); default: warnInput << "KisAbstractInputAction" << d->name << "tried to process event data from an unhandled event type" << event->type(); return QPoint(); } } QPointF KisAbstractInputAction::eventPosF(const QEvent *event) { switch (event->type()) { case QEvent::MouseMove: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: - return static_cast(event)->posF(); + return static_cast(event)->localPos(); case QEvent::TabletMove: case QEvent::TabletPress: case QEvent::TabletRelease: return static_cast(event)->posF(); case QEvent::Wheel: return static_cast(event)->posF(); case QEvent::NativeGesture: return QPointF(static_cast(event)->pos()); default: warnInput << "KisAbstractInputAction" << d->name << "tried to process event data from an unhandled event type" << event->type(); return QPointF(); } } bool KisAbstractInputAction::isAvailable() const { return true; } diff --git a/libs/ui/kis_histogram_view.cc b/libs/ui/kis_histogram_view.cc index 9a9b478b5c..8cd3b0c712 100644 --- a/libs/ui/kis_histogram_view.cc +++ b/libs/ui/kis_histogram_view.cc @@ -1,239 +1,239 @@ /* * Copyright (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * 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_histogram_view.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoChannelInfo.h" #include "KoBasicHistogramProducers.h" #include "KoColorSpace.h" #include "kis_global.h" #include "kis_layer.h" #include #include "kis_paint_device.h" -KisHistogramView::KisHistogramView(QWidget *parent, const char *name, Qt::WFlags f) +KisHistogramView::KisHistogramView(QWidget *parent, const char *name, Qt::WindowFlags f) : QLabel(parent, f), m_currentDev(nullptr), m_currentProducer(nullptr), m_smoothHistogram(false),m_histogram_type(LINEAR) { setObjectName(name); } KisHistogramView::~KisHistogramView() { } KoHistogramProducer *KisHistogramView::currentProducer() { return m_currentProducer; } void KisHistogramView::startUpdateCanvasProjection() { updateHistogramCalculation(); } void KisHistogramView::setChannels(QList & channels) { m_channels = channels; updateHistogramCalculation(); } void KisHistogramView::setProducer(KoHistogramProducer* producer) { m_currentProducer = producer; m_channels = m_currentProducer->channels(); if( !m_histogram.isNull() ){ m_histogram->setProducer( m_currentProducer ); } updateHistogramCalculation(); } void KisHistogramView::setPaintDevice(KisPaintDeviceSP dev, KoHistogramProducer* producer, const QRect &bounds) { m_currentProducer = producer; m_channels = m_currentProducer->channels(); m_currentDev = dev; m_currentBounds = bounds; m_histogram = new KisHistogram(m_currentDev, m_currentBounds, m_currentProducer, m_histogram_type); updateHistogramCalculation(); } void KisHistogramView::setView(double from, double size) { double m_from = from; double m_width = size; if (m_from + m_width > 1.0) m_from = 1.0 - m_width; m_histogram->producer()->setView(m_from, m_width); updateHistogramCalculation(); } bool KisHistogramView::hasColor() { return m_color; } void KisHistogramView::setColor(bool set) { if (set != m_color) { m_color = set; } update(); } void KisHistogramView::setHistogramType(enumHistogramType type) { m_histogram_type = type; updateHistogramCalculation(); } void KisHistogramView::updateHistogramCalculation() { if (!m_currentProducer || m_currentDev.isNull() || m_histogram.isNull() ) { // Something's very wrong: not initialized return; } m_histogram->updateHistogram(); update(); } void KisHistogramView::mousePressEvent(QMouseEvent * e) { if (e->button() == Qt::RightButton) emit rightClicked(e->globalPos()); else QLabel::mousePressEvent(e); } void KisHistogramView::setSmoothHistogram(bool smoothHistogram) { m_smoothHistogram = smoothHistogram; } void KisHistogramView::paintEvent(QPaintEvent *event) { QLabel::paintEvent(event); if( this->height() > 0 && this->width()>0 && !m_histogram.isNull() ){ qint32 height = this->height(); int selFrom, selTo; // from - to in bins qint32 bins = m_histogram->producer()->numberOfBins(); QPainter painter(this); painter.setPen(this->palette().light().color()); const int NGRID = 4; for(int i=0; i<=NGRID; ++i){ painter.drawLine(this->width()*i/NGRID,0.,this->width()*i/NGRID,this->height()); painter.drawLine(0.,this->height()*i/NGRID,this->width(),this->height()*i/NGRID); } // Draw the box of the selection, if any if (m_histogram->hasSelection()) { double width = m_histogram->selectionTo() - m_histogram->selectionFrom(); double factor = static_cast(bins) / m_histogram->producer()->viewWidth(); selFrom = static_cast(m_histogram->selectionFrom() * factor); selTo = selFrom + static_cast(width * factor); painter.drawRect(selFrom, 0, selTo - selFrom, height); } else { // We don't want the drawing to think we're in a selected area selFrom = -1; selTo = 2; } float highest = 0; if(m_channels.count()==0){ m_channels=m_currentProducer->channels(); } int nChannels = m_channels.size(); // First we iterate once, so that we have the overall maximum. This is a bit inefficient, // but not too much since the histogram caches the calculations for (int chan = 0; chan < m_channels.size(); chan++) { if( m_channels.at(chan)->channelType() != KoChannelInfo::ALPHA ){ m_histogram->setChannel(chan); if ((float)m_histogram->calculations().getHighest() > highest) highest = (float)m_histogram->calculations().getHighest(); } } highest = (m_histogram_type==LINEAR)? highest: std::log2(highest); painter.setWindow(QRect(-1,0,bins+1,highest)); painter.setCompositionMode(QPainter::CompositionMode_Plus); for (int chan = 0; chan < nChannels; chan++) { if( m_channels.at(chan)->channelType() != KoChannelInfo::ALPHA ){ auto color = m_channels.at(chan)->color(); auto fill_color = color; fill_color.setAlphaF(.25); painter.setBrush(fill_color); auto pen = QPen(color); pen.setWidth(0); painter.setPen(pen); if (m_smoothHistogram){ QPainterPath path; m_histogram->setChannel(chan); path.moveTo(QPointF(-1,highest)); for (qint32 i = 0; i < bins; ++i) { float v = (m_histogram_type==LINEAR)? highest-m_histogram->getValue(i): highest-std::log2(m_histogram->getValue(i)); path.lineTo(QPointF(i,v)); } path.lineTo(QPointF(bins+1,highest)); path.closeSubpath(); painter.drawPath(path); } else { pen.setWidth(1); painter.setPen(pen); m_histogram->setChannel(chan); for (qint32 i = 0; i < bins; ++i) { float v = (m_histogram_type==LINEAR)? highest-m_histogram->getValue(i): highest-std::log2(m_histogram->getValue(i)); painter.drawLine(QPointF(i,highest),QPointF(i,v)); } } } } } } diff --git a/libs/ui/kis_histogram_view.h b/libs/ui/kis_histogram_view.h index a710c9a85d..1c57c15c10 100644 --- a/libs/ui/kis_histogram_view.h +++ b/libs/ui/kis_histogram_view.h @@ -1,106 +1,106 @@ /* * Copyright (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * 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_HISTOGRAM_VIEW_ #define _KIS_HISTOGRAM_VIEW_ #include #include #include #include #include #include #include "kis_types.h" #include "kis_histogram.h" #include class KoChannelInfo; /** * This class displays a histogram. It has a list of channels it can * select. The easy way is to display channelStrings() to the user, * and then use a setActiveChannel with the integer the same as the * one the selected string in that stringlist has. If the selected one * is a producer, the histogram will automatically display all its * channels, and color them if that is possible. * * You can also set the channels manually, just don't forget that the * displayed channels all need to belong to the same producer! If you * set them manually, don't forget to set the (non)usage of color as * well. * * You can either set this to use a specific layer, or use a specific * histogram. With the latter, some functionality will disappear, like * listProducers(). Setting a histogram will discard info on the * layer, and setting a layer will discard info on the histogram. * **/ class KRITAUI_EXPORT KisHistogramView : public QLabel { Q_OBJECT public: - KisHistogramView(QWidget *parent = 0, const char *name = 0, Qt::WFlags f = 0); + KisHistogramView(QWidget *parent = 0, const char *name = 0, Qt::WindowFlags f = 0); ~KisHistogramView() override; void setPaintDevice(KisPaintDeviceSP dev, KoHistogramProducer *producer, const QRect &bounds); void setView(double from, double size); KoHistogramProducer *currentProducer(); bool hasColor(); void setColor(bool set); void setProducer(KoHistogramProducer* producer); void setChannels(QList & channels); void paintEvent(QPaintEvent* event) override; virtual void updateHistogramCalculation(); void setSmoothHistogram(bool smoothHistogram); public Q_SLOTS: virtual void setHistogramType(enumHistogramType type); virtual void startUpdateCanvasProjection(); Q_SIGNALS: void rightClicked(const QPoint& pos); protected: void mousePressEvent(QMouseEvent * e) override; private: void setChannels(void); void addProducerChannels(KoHistogramProducer *producer); KisHistogramSP m_histogram; KisPaintDeviceSP m_currentDev; QRect m_currentBounds; KoHistogramProducer *m_currentProducer; QList m_channels; bool m_color; bool m_smoothHistogram; enumHistogramType m_histogram_type; }; #endif // _KIS_HISTOGRAM_VIEW_ diff --git a/libs/ui/kis_image_manager.cc b/libs/ui/kis_image_manager.cc index 2f46118b2b..7ae7c670a0 100644 --- a/libs/ui/kis_image_manager.cc +++ b/libs/ui/kis_image_manager.cc @@ -1,211 +1,211 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_image_manager.h" #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include "kis_import_catcher.h" #include "KisViewManager.h" #include "KisDocument.h" #include "dialogs/kis_dlg_image_properties.h" #include "commands/kis_image_commands.h" #include "kis_action.h" #include "kis_action_manager.h" #include "kis_layer_utils.h" #include "kis_signal_compressor_with_param.h" KisImageManager::KisImageManager(KisViewManager * view) : m_view(view) { } void KisImageManager::setView(QPointerimageView) { Q_UNUSED(imageView); } void KisImageManager::setup(KisActionManager *actionManager) { KisAction *action = actionManager->createAction("import_layer_from_file"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerFromFile())); action = actionManager->createAction("image_properties"); connect(action, SIGNAL(triggered()), this, SLOT(slotImageProperties())); action = actionManager->createAction("import_layer_as_paint_layer"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerFromFile())); action = actionManager->createAction("import_layer_as_transparency_mask"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerAsTransparencyMask())); action = actionManager->createAction("import_layer_as_filter_mask"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerAsFilterMask())); action = actionManager->createAction("import_layer_as_selection_mask"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerAsSelectionMask())); action = actionManager->createAction("image_color"); connect(action, SIGNAL(triggered()), this, SLOT(slotImageColor())); } void KisImageManager::slotImportLayerFromFile() { importImage(QUrl(), "KisPaintLayer"); } void KisImageManager::slotImportLayerAsTransparencyMask() { importImage(QUrl(), "KisTransparencyMask"); } void KisImageManager::slotImportLayerAsFilterMask() { importImage(QUrl(), "KisFilterMask"); } void KisImageManager::slotImportLayerAsSelectionMask() { importImage(QUrl(), "KisSelectionMask"); } qint32 KisImageManager::importImage(const QUrl &urlArg, const QString &layerType) { KisImageWSP currentImage = m_view->image(); if (!currentImage) { return 0; } QList urls; qint32 rc = 0; if (urlArg.isEmpty()) { KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::OpenFiles, "OpenDocument"); dialog.setCaption(i18n("Import Image")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); QStringList fileNames = dialog.filenames(); Q_FOREACH (const QString &fileName, fileNames) { urls << QUrl::fromLocalFile(fileName); } } else { urls.push_back(urlArg); } if (urls.empty()) return 0; for (QList::iterator it = urls.begin(); it != urls.end(); ++it) { new KisImportCatcher(*it, m_view, layerType); } m_view->canvas()->update(); return rc; } void KisImageManager::resizeCurrentImage(qint32 w, qint32 h, qint32 xOffset, qint32 yOffset) { if (!m_view->image()) return; m_view->image()->resizeImage(QRect(-xOffset, -yOffset, w, h)); } void KisImageManager::scaleCurrentImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy) { if (!m_view->image()) return; m_view->image()->scaleImage(size, xres, yres, filterStrategy); } void KisImageManager::rotateCurrentImage(double radians) { if (!m_view->image()) return; m_view->image()->rotateImage(radians); } void KisImageManager::shearCurrentImage(double angleX, double angleY) { if (!m_view->image()) return; m_view->image()->shear(angleX, angleY); } void KisImageManager::slotImageProperties() { KisImageWSP image = m_view->image(); if (!image) return; QPointer dlg = new KisDlgImageProperties(image, m_view->mainWindow()); if (dlg->exec() == QDialog::Accepted) { image->convertProjectionColorSpace(dlg->colorSpace()); } delete dlg; } void updateImageBackgroundColor(KisImageSP image, const QColorDialog *dlg) { QColor newColor = dlg->currentColor(); KoColor bg = image->defaultProjectionColor(); bg.fromQColor(newColor); KisLayerUtils::changeImageDefaultProjectionColor(image, bg); } void KisImageManager::slotImageColor() { KisImageWSP image = m_view->image(); if (!image) return; QColorDialog dlg; dlg.setOption(QColorDialog::ShowAlphaChannel, true); KoColor bg = image->defaultProjectionColor(); dlg.setCurrentColor(bg.toQColor()); KisSignalCompressor compressor(200, KisSignalCompressor::FIRST_INACTIVE); std::function updateCall(std::bind(updateImageBackgroundColor, image, &dlg)); SignalToFunctionProxy proxy(updateCall); connect(&dlg, SIGNAL(currentColorChanged(QColor)), &compressor, SLOT(start())); connect(&compressor, SIGNAL(timeout()), &proxy, SLOT(start())); dlg.exec(); } diff --git a/libs/ui/kis_layer_manager.cc b/libs/ui/kis_layer_manager.cc index 9056afa0e1..994e73c010 100644 --- a/libs/ui/kis_layer_manager.cc +++ b/libs/ui/kis_layer_manager.cc @@ -1,843 +1,843 @@ /* * Copyright (C) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_layer_manager.h" #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisImportExportManager.h" #include "kis_config.h" #include "kis_cursor.h" #include "dialogs/kis_dlg_adj_layer_props.h" #include "dialogs/kis_dlg_adjustment_layer.h" #include "dialogs/kis_dlg_layer_properties.h" #include "dialogs/kis_dlg_generator_layer.h" #include "dialogs/kis_dlg_file_layer.h" #include "dialogs/kis_dlg_layer_style.h" #include "KisDocument.h" #include "kis_filter_manager.h" #include "kis_node_visitor.h" #include "kis_paint_layer.h" #include "commands/kis_image_commands.h" #include "commands/kis_layer_command.h" #include "commands/kis_node_commands.h" #include "kis_canvas_resource_provider.h" #include "kis_selection_manager.h" #include "kis_statusbar.h" #include "KisViewManager.h" #include "kis_zoom_manager.h" #include "canvas/kis_canvas2.h" #include "widgets/kis_meta_data_merge_strategy_chooser_widget.h" #include "widgets/kis_wdg_generator.h" #include "kis_progress_widget.h" #include "kis_node_commands_adapter.h" #include "kis_node_manager.h" #include "kis_action.h" #include "kis_action_manager.h" #include "KisPart.h" #include "kis_raster_keyframe_channel.h" #include "kis_signal_compressor_with_param.h" #include "kis_abstract_projection_plane.h" #include "commands_new/kis_set_layer_style_command.h" #include "kis_post_execution_undo_adapter.h" #include "kis_selection_mask.h" #include "kis_layer_utils.h" #include "lazybrush/kis_colorize_mask.h" #include "KisSaveGroupVisitor.h" KisLayerManager::KisLayerManager(KisViewManager * view) : m_view(view) , m_imageView(0) , m_imageFlatten(0) , m_imageMergeLayer(0) , m_groupLayersSave(0) , m_imageResizeToLayer(0) , m_flattenLayer(0) , m_rasterizeLayer(0) , m_commandsAdapter(new KisNodeCommandsAdapter(m_view)) , m_layerStyle(0) { } KisLayerManager::~KisLayerManager() { delete m_commandsAdapter; } void KisLayerManager::setView(QPointerview) { m_imageView = view; } KisLayerSP KisLayerManager::activeLayer() { if (m_imageView) { return m_imageView->currentLayer(); } return 0; } KisPaintDeviceSP KisLayerManager::activeDevice() { if (activeLayer()) { return activeLayer()->paintDevice(); } return 0; } void KisLayerManager::activateLayer(KisLayerSP layer) { if (m_imageView) { emit sigLayerActivated(layer); layersUpdated(); if (layer) { m_view->resourceProvider()->slotNodeActivated(layer.data()); } } } void KisLayerManager::setup(KisActionManager* actionManager) { m_imageFlatten = actionManager->createAction("flatten_image"); connect(m_imageFlatten, SIGNAL(triggered()), this, SLOT(flattenImage())); m_imageMergeLayer = actionManager->createAction("merge_layer"); connect(m_imageMergeLayer, SIGNAL(triggered()), this, SLOT(mergeLayer())); m_flattenLayer = actionManager->createAction("flatten_layer"); connect(m_flattenLayer, SIGNAL(triggered()), this, SLOT(flattenLayer())); m_rasterizeLayer = actionManager->createAction("rasterize_layer"); connect(m_rasterizeLayer, SIGNAL(triggered()), this, SLOT(rasterizeLayer())); m_groupLayersSave = actionManager->createAction("save_groups_as_images"); connect(m_groupLayersSave, SIGNAL(triggered()), this, SLOT(saveGroupLayers())); m_convertGroupAnimated = actionManager->createAction("convert_group_to_animated"); connect(m_convertGroupAnimated, SIGNAL(triggered()), this, SLOT(convertGroupToAnimated())); m_imageResizeToLayer = actionManager->createAction("resizeimagetolayer"); connect(m_imageResizeToLayer, SIGNAL(triggered()), this, SLOT(imageResizeToActiveLayer())); KisAction *action = actionManager->createAction("trim_to_image"); connect(action, SIGNAL(triggered()), this, SLOT(trimToImage())); m_layerStyle = actionManager->createAction("layer_style"); connect(m_layerStyle, SIGNAL(triggered()), this, SLOT(layerStyle())); } void KisLayerManager::updateGUI() { KisImageSP image = m_view->image(); KisLayerSP layer = activeLayer(); const bool isGroupLayer = layer && layer->inherits("KisGroupLayer"); m_imageMergeLayer->setText( isGroupLayer ? i18nc("@action:inmenu", "Merge Group") : i18nc("@action:inmenu", "Merge with Layer Below")); m_flattenLayer->setVisible(!isGroupLayer); if (m_view->statusBar()) m_view->statusBar()->setProfile(image); } void KisLayerManager::imageResizeToActiveLayer() { KisLayerSP layer; KisImageWSP image = m_view->image(); if (image && (layer = activeLayer())) { QRect cropRect = layer->projection()->nonDefaultPixelArea(); if (!cropRect.isEmpty()) { image->cropImage(cropRect); } else { m_view->showFloatingMessage( i18nc("floating message in layer manager", "Layer is empty "), QIcon(), 2000, KisFloatingMessage::Low); } } } void KisLayerManager::trimToImage() { KisImageWSP image = m_view->image(); if (image) { image->cropImage(image->bounds()); } } void KisLayerManager::layerProperties() { if (!m_view) return; if (!m_view->document()) return; KisLayerSP layer = activeLayer(); QList selectedNodes = m_view->nodeManager()->selectedNodes(); const bool multipleLayersSelected = selectedNodes.size() > 1; if (!layer) return; KisAdjustmentLayerSP alayer = KisAdjustmentLayerSP(dynamic_cast(layer.data())); KisGeneratorLayerSP glayer = KisGeneratorLayerSP(dynamic_cast(layer.data())); if (alayer && !multipleLayersSelected) { KisPaintDeviceSP dev = alayer->projection(); KisDlgAdjLayerProps dlg(alayer, alayer.data(), dev, m_view, alayer->filter().data(), alayer->name(), i18n("Filter Layer Properties"), m_view->mainWindow(), "dlgadjlayerprops"); dlg.resize(dlg.minimumSizeHint()); KisFilterConfigurationSP configBefore(alayer->filter()); KIS_ASSERT_RECOVER_RETURN(configBefore); QString xmlBefore = configBefore->toXML(); if (dlg.exec() == QDialog::Accepted) { alayer->setName(dlg.layerName()); KisFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { KisChangeFilterCmd *cmd = new KisChangeFilterCmd(alayer, configBefore->name(), xmlBefore, configAfter->name(), xmlAfter, false); // FIXME: check whether is needed cmd->redo(); m_view->undoAdapter()->addCommand(cmd); m_view->document()->setModified(true); } } else { KisFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { alayer->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data())); alayer->setDirty(); } } } else if (glayer && !multipleLayersSelected) { KisDlgGeneratorLayer dlg(glayer->name(), m_view, m_view->mainWindow()); dlg.setCaption(i18n("Fill Layer Properties")); KisFilterConfigurationSP configBefore(glayer->filter()); Q_ASSERT(configBefore); QString xmlBefore = configBefore->toXML(); dlg.setConfiguration(configBefore.data()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { glayer->setName(dlg.layerName()); KisFilterConfigurationSP configAfter(dlg.configuration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { KisChangeFilterCmd *cmd = new KisChangeFilterCmd(glayer, configBefore->name(), xmlBefore, configAfter->name(), xmlAfter, true); // FIXME: check whether is needed cmd->redo(); m_view->undoAdapter()->addCommand(cmd); m_view->document()->setModified(true); } } } else { // If layer == normal painting layer, vector layer, or group layer QList selectedNodes = m_view->nodeManager()->selectedNodes(); KisDlgLayerProperties *dialog = new KisDlgLayerProperties(selectedNodes, m_view); dialog->resize(dialog->minimumSizeHint()); dialog->setAttribute(Qt::WA_DeleteOnClose); Qt::WindowFlags flags = dialog->windowFlags(); dialog->setWindowFlags(flags | Qt::WindowStaysOnTopHint | Qt::Dialog); dialog->show(); } } void KisLayerManager::convertNodeToPaintLayer(KisNodeSP source) { KisImageWSP image = m_view->image(); if (!image) return; KisLayer *srcLayer = qobject_cast(source.data()); if (srcLayer && (srcLayer->inherits("KisGroupLayer") || srcLayer->layerStyle() || srcLayer->childCount() > 0)) { image->flattenLayer(srcLayer); return; } KisPaintDeviceSP srcDevice = source->paintDevice() ? source->projection() : source->original(); bool putBehind = false; QString newCompositeOp = source->compositeOpId(); KisColorizeMask *colorizeMask = dynamic_cast(source.data()); if (colorizeMask) { srcDevice = colorizeMask->coloringProjection(); putBehind = colorizeMask->compositeOpId() == COMPOSITE_BEHIND; if (putBehind) { newCompositeOp = COMPOSITE_OVER; } } if (!srcDevice) return; KisPaintDeviceSP clone; if (*srcDevice->colorSpace() != *srcDevice->compositionSourceColorSpace()) { clone = new KisPaintDevice(srcDevice->compositionSourceColorSpace()); QRect rc(srcDevice->extent()); KisPainter::copyAreaOptimized(rc.topLeft(), srcDevice, clone, rc); } else { clone = new KisPaintDevice(*srcDevice); } KisLayerSP layer = new KisPaintLayer(image, source->name(), source->opacity(), clone); layer->setCompositeOpId(newCompositeOp); KisNodeSP parent = source->parent(); KisNodeSP above = source; while (parent && !parent->allowAsChild(layer)) { above = above->parent(); parent = above ? above->parent() : 0; } if (putBehind && above == source->parent()) { above = above->prevSibling(); } m_commandsAdapter->beginMacro(kundo2_i18n("Convert to a Paint Layer")); m_commandsAdapter->addNode(layer, parent, above); m_commandsAdapter->removeNode(source); m_commandsAdapter->endMacro(); } void KisLayerManager::convertGroupToAnimated() { KisGroupLayerSP group = dynamic_cast(activeLayer().data()); if (group.isNull()) return; KisPaintLayerSP animatedLayer = new KisPaintLayer(m_view->image(), group->name(), OPACITY_OPAQUE_U8); animatedLayer->enableAnimation(); KisRasterKeyframeChannel *contentChannel = dynamic_cast( animatedLayer->getKeyframeChannel(KisKeyframeChannel::Content.id(), true)); KIS_ASSERT_RECOVER_RETURN(contentChannel); KisNodeSP child = group->firstChild(); int time = 0; while (child) { contentChannel->importFrame(time, child->projection(), NULL); time++; child = child->nextSibling(); } m_commandsAdapter->beginMacro(kundo2_i18n("Convert to an animated layer")); m_commandsAdapter->addNode(animatedLayer, group->parent(), group); m_commandsAdapter->removeNode(group); m_commandsAdapter->endMacro(); } void KisLayerManager::adjustLayerPosition(KisNodeSP node, KisNodeSP activeNode, KisNodeSP &parent, KisNodeSP &above) { Q_ASSERT(activeNode); parent = activeNode; above = parent->lastChild(); while (parent && (!parent->allowAsChild(node) || parent->userLocked())) { above = parent; parent = parent->parent(); } if (!parent) { warnKrita << "KisLayerManager::adjustLayerPosition:" << "No node accepted newly created node"; parent = m_view->image()->root(); above = parent->lastChild(); } } void KisLayerManager::addLayerCommon(KisNodeSP activeNode, KisLayerSP layer, bool updateImage) { KisNodeSP parent; KisNodeSP above; adjustLayerPosition(layer, activeNode, parent, above); KisGroupLayer *group = dynamic_cast(parent.data()); const bool parentForceUpdate = group && !group->projectionIsValid(); updateImage |= parentForceUpdate; m_commandsAdapter->addNode(layer, parent, above, updateImage, updateImage); } KisLayerSP KisLayerManager::addLayer(KisNodeSP activeNode) { KisLayerSP layer = KisLayerUtils::constructDefaultLayer(m_view->image()); addLayerCommon(activeNode, layer, false); return layer; } void KisLayerManager::addGroupLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); addLayerCommon(activeNode, new KisGroupLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8), false); } void KisLayerManager::addCloneLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); addLayerCommon(activeNode, new KisCloneLayer(activeLayer(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8)); } void KisLayerManager::addShapeLayer(KisNodeSP activeNode) { if (!m_view) return; if (!m_view->document()) return; KisImageWSP image = m_view->image(); KisShapeLayerSP layer = new KisShapeLayer(m_view->document()->shapeController(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8); addLayerCommon(activeNode, layer, false); } void KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); KisSelectionSP selection = m_view->selection(); KisAdjustmentLayerSP adjl = addAdjustmentLayer(activeNode, QString(), 0, selection); image->refreshGraph(); KisPaintDeviceSP previewDevice = new KisPaintDevice(*adjl->original()); KisDlgAdjustmentLayer dlg(adjl, adjl.data(), previewDevice, image->nextLayerName(), i18n("New Filter Layer"), m_view); dlg.resize(dlg.minimumSizeHint()); // ensure that the device may be free'd by the dialog // when it is not needed anymore previewDevice = 0; if (dlg.exec() != QDialog::Accepted || adjl->filter().isNull()) { // XXX: add messagebox warning if there's no filter set! m_commandsAdapter->undoLastCommand(); } else { adjl->setName(dlg.layerName()); } } KisAdjustmentLayerSP KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode, const QString & name, KisFilterConfigurationSP filter, KisSelectionSP selection) { KisImageWSP image = m_view->image(); KisAdjustmentLayerSP layer = new KisAdjustmentLayer(image, name, filter, selection); addLayerCommon(activeNode, layer); return layer; } void KisLayerManager::addGeneratorLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); KisDlgGeneratorLayer dlg(image->nextLayerName(), m_view, m_view->mainWindow()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { KisSelectionSP selection = m_view->selection(); KisFilterConfigurationSP generator = dlg.configuration(); QString name = dlg.layerName(); addLayerCommon(activeNode, new KisGeneratorLayer(image, name, generator, selection)); } } void KisLayerManager::flattenImage() { KisImageSP image = m_view->image(); if (!m_view->blockUntilOperationsFinished(image)) return; if (image) { bool doIt = true; if (image->nHiddenLayers() > 0) { int answer = QMessageBox::warning(m_view->mainWindow(), i18nc("@title:window", "Flatten Image"), i18n("The image contains hidden layers that will be lost. Do you want to flatten the image?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (answer != QMessageBox::Yes) { doIt = false; } } if (doIt) { image->flatten(); } } } inline bool isSelectionMask(KisNodeSP node) { return dynamic_cast(node.data()); } bool tryMergeSelectionMasks(KisNodeSP currentNode, KisImageSP image) { bool result = false; KisNodeSP prevNode = currentNode->prevSibling(); if (isSelectionMask(currentNode) && prevNode && isSelectionMask(prevNode)) { QList mergedNodes; mergedNodes.append(currentNode); mergedNodes.append(prevNode); image->mergeMultipleLayers(mergedNodes, currentNode); result = true; } return result; } bool tryFlattenGroupLayer(KisNodeSP currentNode, KisImageSP image) { bool result = false; if (currentNode->inherits("KisGroupLayer")) { KisGroupLayer *layer = qobject_cast(currentNode.data()); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(layer, false); image->flattenLayer(layer); result = true; } return result; } void KisLayerManager::mergeLayer() { KisImageSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; if (!m_view->blockUntilOperationsFinished(image)) return; QList selectedNodes = m_view->nodeManager()->selectedNodes(); if (selectedNodes.size() > 1) { image->mergeMultipleLayers(selectedNodes, m_view->activeNode()); } else if (tryMergeSelectionMasks(m_view->activeNode(), image)) { // already done! } else if (tryFlattenGroupLayer(m_view->activeNode(), image)) { // already done! } else { if (!layer->prevSibling()) return; KisLayer *prevLayer = qobject_cast(layer->prevSibling().data()); if (!prevLayer) return; if (prevLayer->userLocked()) { m_view->showFloatingMessage( i18nc("floating message in layer manager", "Layer is locked "), QIcon(), 2000, KisFloatingMessage::Low); } else if (layer->metaData()->isEmpty() && prevLayer->metaData()->isEmpty()) { image->mergeDown(layer, KisMetaData::MergeStrategyRegistry::instance()->get("Drop")); } else { const KisMetaData::MergeStrategy* strategy = KisMetaDataMergeStrategyChooserWidget::showDialog(m_view->mainWindow()); if (!strategy) return; image->mergeDown(layer, strategy); } } m_view->updateGUI(); } void KisLayerManager::flattenLayer() { KisImageSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; if (!m_view->blockUntilOperationsFinished(image)) return; convertNodeToPaintLayer(layer); m_view->updateGUI(); } void KisLayerManager::rasterizeLayer() { KisImageSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; if (!m_view->blockUntilOperationsFinished(image)) return; KisPaintLayerSP paintLayer = new KisPaintLayer(image, layer->name(), layer->opacity()); KisPainter gc(paintLayer->paintDevice()); QRect rc = layer->projection()->exactBounds(); gc.bitBlt(rc.topLeft(), layer->projection(), rc); m_commandsAdapter->beginMacro(kundo2_i18n("Rasterize Layer")); m_commandsAdapter->addNode(paintLayer.data(), layer->parent().data(), layer.data()); int childCount = layer->childCount(); for (int i = 0; i < childCount; i++) { m_commandsAdapter->moveNode(layer->firstChild(), paintLayer, paintLayer->lastChild()); } m_commandsAdapter->removeNode(layer); m_commandsAdapter->endMacro(); updateGUI(); } void KisLayerManager::layersUpdated() { KisLayerSP layer = activeLayer(); if (!layer) return; m_view->updateGUI(); } void KisLayerManager::saveGroupLayers() { QStringList listMimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export); KoDialog dlg; QWidget *page = new QWidget(&dlg); dlg.setMainWidget(page); QBoxLayout *layout = new QVBoxLayout(page); KisFileNameRequester *urlRequester = new KisFileNameRequester(page); urlRequester->setMode(KoFileDialog::SaveFile); if (m_view->document()->url().isLocalFile()) { urlRequester->setStartDir(QFileInfo(m_view->document()->url().toLocalFile()).absolutePath()); } urlRequester->setMimeTypeFilters(listMimeFilter); urlRequester->setFileName(m_view->document()->url().toLocalFile()); layout->addWidget(urlRequester); QCheckBox *chkInvisible = new QCheckBox(i18n("Convert Invisible Groups"), page); chkInvisible->setChecked(false); layout->addWidget(chkInvisible); QCheckBox *chkDepth = new QCheckBox(i18n("Export Only Toplevel Groups"), page); chkDepth->setChecked(true); layout->addWidget(chkDepth); if (!dlg.exec()) return; QString path = urlRequester->fileName(); if (path.isEmpty()) return; QFileInfo f(path); QString mimeType= KisMimeDatabase::mimeTypeForFile(f.fileName()); if (mimeType.isEmpty()) { mimeType = "image/png"; } QString extension = KisMimeDatabase::suffixesForMimeType(mimeType).first(); QString basename = f.baseName(); KisImageSP image = m_view->image(); if (!image) return; KisSaveGroupVisitor v(image, chkInvisible->isChecked(), chkDepth->isChecked(), f.absolutePath(), basename, extension, mimeType); image->rootLayer()->accept(v); } bool KisLayerManager::activeLayerHasSelection() { return (activeLayer()->selection() != 0); } void KisLayerManager::addFileLayer(KisNodeSP activeNode) { QString basePath; QUrl url = m_view->document()->url(); if (url.isLocalFile()) { basePath = QFileInfo(url.toLocalFile()).absolutePath(); } KisImageWSP image = m_view->image(); KisDlgFileLayer dlg(basePath, image->nextLayerName(), m_view->mainWindow()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { QString name = dlg.layerName(); QString fileName = dlg.fileName(); if(fileName.isEmpty()){ QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified")); return; } KisFileLayer::ScalingMethod scalingMethod = dlg.scaleToImageResolution(); addLayerCommon(activeNode, new KisFileLayer(image, basePath, fileName, scalingMethod, name, OPACITY_OPAQUE_U8)); } } void updateLayerStyles(KisLayerSP layer, KisDlgLayerStyle *dlg) { KisSetLayerStyleCommand::updateLayerStyle(layer, dlg->style()->clone()); } void KisLayerManager::layerStyle() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; if (!m_view->blockUntilOperationsFinished(image)) return; KisPSDLayerStyleSP oldStyle; if (layer->layerStyle()) { oldStyle = layer->layerStyle()->clone(); } else { oldStyle = toQShared(new KisPSDLayerStyle()); } KisDlgLayerStyle dlg(oldStyle->clone(), m_view->resourceProvider()); std::function updateCall(std::bind(updateLayerStyles, layer, &dlg)); SignalToFunctionProxy proxy(updateCall); connect(&dlg, SIGNAL(configChanged()), &proxy, SLOT(start())); if (dlg.exec() == QDialog::Accepted) { KisPSDLayerStyleSP newStyle = dlg.style(); KUndo2CommandSP command = toQShared( new KisSetLayerStyleCommand(layer, oldStyle, newStyle)); image->postExecutionUndoAdapter()->addCommand(command); } } diff --git a/libs/ui/kis_node_filter_proxy_model.cpp b/libs/ui/kis_node_filter_proxy_model.cpp index f01b6f6485..fc24ba62e0 100644 --- a/libs/ui/kis_node_filter_proxy_model.cpp +++ b/libs/ui/kis_node_filter_proxy_model.cpp @@ -1,165 +1,167 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_node_filter_proxy_model.h" #include #include "kis_node.h" #include "kis_node_model.h" #include "kis_node_manager.h" #include "kis_signal_compressor.h" #include "kis_image.h" struct KisNodeFilterProxyModel::Private { Private() : nodeModel(0), activeNodeCompressor(1000, KisSignalCompressor::FIRST_INACTIVE) {} KisNodeModel *nodeModel; KisNodeSP pendingActiveNode; KisNodeSP activeNode; QSet acceptedLabels; KisSignalCompressor activeNodeCompressor; bool isUpdatingFilter = false; bool checkIndexAllowedRecursively(QModelIndex srcIndex); }; KisNodeFilterProxyModel::KisNodeFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent), m_d(new Private) { connect(&m_d->activeNodeCompressor, SIGNAL(timeout()), SLOT(slotUpdateCurrentNodeFilter())); } KisNodeFilterProxyModel::~KisNodeFilterProxyModel() { } void KisNodeFilterProxyModel::setNodeModel(KisNodeModel *model) { m_d->nodeModel = model; setSourceModel(model); } bool KisNodeFilterProxyModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (m_d->isUpdatingFilter && role == KisNodeModel::ActiveRole) { return false; } return QSortFilterProxyModel::setData(index, value, role); } bool KisNodeFilterProxyModel::Private::checkIndexAllowedRecursively(QModelIndex srcIndex) { + if (!srcIndex.isValid()) return false; + KisNodeSP node = nodeModel->nodeFromIndex(srcIndex); if (node == activeNode || acceptedLabels.contains(node->colorLabelIndex())) { return true; } bool result = false; const int numChildren = srcIndex.model()->rowCount(srcIndex); for (int i = 0; i < numChildren; i++) { - QModelIndex child = srcIndex.child(i, 0); + QModelIndex child = nodeModel->index(i, 0, srcIndex); if (checkIndexAllowedRecursively(child)) { result = true; break; } } return result; } bool KisNodeFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { KIS_ASSERT_RECOVER(m_d->nodeModel) { return true; } const QModelIndex index = sourceModel()->index(source_row, 0, source_parent); KisNodeSP node = m_d->nodeModel->nodeFromIndex(index); return !node || m_d->acceptedLabels.isEmpty() || m_d->checkIndexAllowedRecursively(index); } KisNodeSP KisNodeFilterProxyModel::nodeFromIndex(const QModelIndex &index) const { KIS_ASSERT_RECOVER(m_d->nodeModel) { return 0; } QModelIndex srcIndex = mapToSource(index); return m_d->nodeModel->nodeFromIndex(srcIndex); } QModelIndex KisNodeFilterProxyModel::indexFromNode(KisNodeSP node) const { KIS_ASSERT_RECOVER(m_d->nodeModel) { return QModelIndex(); } QModelIndex srcIndex = m_d->nodeModel->indexFromNode(node); return mapFromSource(srcIndex); } void KisNodeFilterProxyModel::setAcceptedLabels(const QList &value) { m_d->acceptedLabels = QSet::fromList(value); invalidateFilter(); } void KisNodeFilterProxyModel::setActiveNode(KisNodeSP node) { // the new node may temporary become null when the last layer // of the document in removed m_d->pendingActiveNode = node; if (node && indexFromNode(node).isValid()) { m_d->activeNodeCompressor.start(); } else { slotUpdateCurrentNodeFilter(); } } void KisNodeFilterProxyModel::slotUpdateCurrentNodeFilter() { m_d->activeNode = m_d->pendingActiveNode; /** * During the filter update the model might emit "current changed" signals, * which (in their turn) will issue setData(..., KisNodeModel::ActiveRole) * call, leading to a double recursion. Which, obviously, crashes Krita. * * Right now, just blocking the KisNodeModel::ActiveRole call is the * most obvious solution for the problem. */ m_d->isUpdatingFilter = true; invalidateFilter(); m_d->isUpdatingFilter = false; } void KisNodeFilterProxyModel::unsetDummiesFacade() { m_d->nodeModel->setDummiesFacade(0, 0, 0, 0, 0); m_d->pendingActiveNode = 0; m_d->activeNode = 0; } diff --git a/libs/ui/kis_node_manager.cpp b/libs/ui/kis_node_manager.cpp index 78bf22aa67..e055b70e5d 100644 --- a/libs/ui/kis_node_manager.cpp +++ b/libs/ui/kis_node_manager.cpp @@ -1,1438 +1,1438 @@ /* * Copyright (C) 2007 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_node_manager.h" -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisPart.h" #include "canvas/kis_canvas2.h" #include "kis_shape_controller.h" #include "kis_canvas_resource_provider.h" #include "KisViewManager.h" #include "KisDocument.h" #include "kis_mask_manager.h" #include "kis_group_layer.h" #include "kis_layer_manager.h" #include "kis_selection_manager.h" #include "kis_node_commands_adapter.h" #include "kis_action.h" #include "kis_action_manager.h" #include "kis_processing_applicator.h" #include "kis_sequential_iterator.h" #include "kis_transaction.h" #include "kis_node_selection_adapter.h" #include "kis_node_insertion_adapter.h" #include "kis_node_juggler_compressed.h" #include "kis_clipboard.h" #include "kis_node_dummies_graph.h" #include "kis_mimedata.h" #include "kis_layer_utils.h" #include "krita_utils.h" #include "processing/kis_mirror_processing_visitor.h" #include "KisView.h" #include struct KisNodeManager::Private { Private(KisNodeManager *_q, KisViewManager *v) : q(_q) , view(v) , imageView(0) , layerManager(v) , maskManager(v) , commandsAdapter(v) , nodeSelectionAdapter(new KisNodeSelectionAdapter(q)) , nodeInsertionAdapter(new KisNodeInsertionAdapter(q)) { } KisNodeManager * q; KisViewManager * view; QPointerimageView; KisLayerManager layerManager; KisMaskManager maskManager; KisNodeCommandsAdapter commandsAdapter; QScopedPointer nodeSelectionAdapter; QScopedPointer nodeInsertionAdapter; KisAction *showInTimeline; KisNodeList selectedNodes; QPointer nodeJuggler; KisNodeWSP previouslyActiveNode; bool activateNodeImpl(KisNodeSP node); QSignalMapper nodeCreationSignalMapper; QSignalMapper nodeConversionSignalMapper; void saveDeviceAsImage(KisPaintDeviceSP device, const QString &defaultName, const QRect &bounds, qreal xRes, qreal yRes, quint8 opacity); void mergeTransparencyMaskAsAlpha(bool writeToLayers); KisNodeJugglerCompressed* lazyGetJuggler(const KUndo2MagicString &actionName); }; bool KisNodeManager::Private::activateNodeImpl(KisNodeSP node) { Q_ASSERT(view); Q_ASSERT(view->canvasBase()); Q_ASSERT(view->canvasBase()->globalShapeManager()); Q_ASSERT(imageView); if (node && node == q->activeNode()) { return false; } // Set the selection on the shape manager to the active layer // and set call KoSelection::setActiveLayer( KoShapeLayer* layer ) // with the parent of the active layer. KoSelection *selection = view->canvasBase()->globalShapeManager()->selection(); Q_ASSERT(selection); selection->deselectAll(); if (!node) { selection->setActiveLayer(0); imageView->setCurrentNode(0); maskManager.activateMask(0); layerManager.activateLayer(0); previouslyActiveNode = q->activeNode(); } else { previouslyActiveNode = q->activeNode(); KoShape * shape = view->document()->shapeForNode(node); //if (!shape) return false; KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape, false); selection->select(shape); KoShapeLayer * shapeLayer = dynamic_cast(shape); //if (!shapeLayer) return false; KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shapeLayer, false); // shapeLayer->setGeometryProtected(node->userLocked()); // shapeLayer->setVisible(node->visible()); selection->setActiveLayer(shapeLayer); imageView->setCurrentNode(node); if (KisLayerSP layer = qobject_cast(node.data())) { maskManager.activateMask(0); layerManager.activateLayer(layer); } else if (KisMaskSP mask = dynamic_cast(node.data())) { maskManager.activateMask(mask); // XXX_NODE: for now, masks cannot be nested. layerManager.activateLayer(static_cast(node->parent().data())); } } return true; } KisNodeManager::KisNodeManager(KisViewManager *view) : m_d(new Private(this, view)) { connect(&m_d->layerManager, SIGNAL(sigLayerActivated(KisLayerSP)), SIGNAL(sigLayerActivated(KisLayerSP))); } KisNodeManager::~KisNodeManager() { delete m_d; } void KisNodeManager::setView(QPointerimageView) { m_d->maskManager.setView(imageView); m_d->layerManager.setView(imageView); if (m_d->imageView) { KisShapeController *shapeController = dynamic_cast(m_d->imageView->document()->shapeController()); Q_ASSERT(shapeController); shapeController->disconnect(SIGNAL(sigActivateNode(KisNodeSP)), this); m_d->imageView->image()->disconnect(this); } m_d->imageView = imageView; if (m_d->imageView) { KisShapeController *shapeController = dynamic_cast(m_d->imageView->document()->shapeController()); Q_ASSERT(shapeController); connect(shapeController, SIGNAL(sigActivateNode(KisNodeSP)), SLOT(slotNonUiActivatedNode(KisNodeSP))); connect(m_d->imageView->image(), SIGNAL(sigIsolatedModeChanged()),this, SLOT(slotUpdateIsolateModeAction())); connect(m_d->imageView->image(), SIGNAL(sigRequestNodeReselection(KisNodeSP, const KisNodeList&)),this, SLOT(slotImageRequestNodeReselection(KisNodeSP, const KisNodeList&))); m_d->imageView->resourceProvider()->slotNodeActivated(m_d->imageView->currentNode()); } } #define NEW_LAYER_ACTION(id, layerType) \ { \ action = actionManager->createAction(id); \ m_d->nodeCreationSignalMapper.setMapping(action, layerType); \ connect(action, SIGNAL(triggered()), \ &m_d->nodeCreationSignalMapper, SLOT(map())); \ } #define CONVERT_NODE_ACTION_2(id, layerType, exclude) \ { \ action = actionManager->createAction(id); \ action->setExcludedNodeTypes(QStringList(exclude)); \ actionManager->addAction(id, action); \ m_d->nodeConversionSignalMapper.setMapping(action, layerType); \ connect(action, SIGNAL(triggered()), \ &m_d->nodeConversionSignalMapper, SLOT(map())); \ } #define CONVERT_NODE_ACTION(id, layerType) \ CONVERT_NODE_ACTION_2(id, layerType, layerType) void KisNodeManager::setup(KActionCollection * actionCollection, KisActionManager* actionManager) { m_d->layerManager.setup(actionManager); m_d->maskManager.setup(actionCollection, actionManager); KisAction * action = actionManager->createAction("mirrorNodeX"); connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeX())); action = actionManager->createAction("mirrorNodeY"); connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeY())); action = actionManager->createAction("activateNextLayer"); connect(action, SIGNAL(triggered()), this, SLOT(activateNextNode())); action = actionManager->createAction("activatePreviousLayer"); connect(action, SIGNAL(triggered()), this, SLOT(activatePreviousNode())); action = actionManager->createAction("switchToPreviouslyActiveNode"); connect(action, SIGNAL(triggered()), this, SLOT(switchToPreviouslyActiveNode())); action = actionManager->createAction("save_node_as_image"); connect(action, SIGNAL(triggered()), this, SLOT(saveNodeAsImage())); action = actionManager->createAction("duplicatelayer"); connect(action, SIGNAL(triggered()), this, SLOT(duplicateActiveNode())); action = actionManager->createAction("copy_layer_clipboard"); connect(action, SIGNAL(triggered()), this, SLOT(copyLayersToClipboard())); action = actionManager->createAction("cut_layer_clipboard"); connect(action, SIGNAL(triggered()), this, SLOT(cutLayersToClipboard())); action = actionManager->createAction("paste_layer_from_clipboard"); connect(action, SIGNAL(triggered()), this, SLOT(pasteLayersFromClipboard())); action = actionManager->createAction("create_quick_group"); connect(action, SIGNAL(triggered()), this, SLOT(createQuickGroup())); action = actionManager->createAction("create_quick_clipping_group"); connect(action, SIGNAL(triggered()), this, SLOT(createQuickClippingGroup())); action = actionManager->createAction("quick_ungroup"); connect(action, SIGNAL(triggered()), this, SLOT(quickUngroup())); action = actionManager->createAction("select_all_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectAllNodes())); action = actionManager->createAction("select_visible_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectVisibleNodes())); action = actionManager->createAction("select_locked_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectLockedNodes())); action = actionManager->createAction("select_invisible_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectInvisibleNodes())); action = actionManager->createAction("select_unlocked_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectUnlockedNodes())); action = actionManager->createAction("new_from_visible"); connect(action, SIGNAL(triggered()), this, SLOT(createFromVisible())); action = actionManager->createAction("show_in_timeline"); action->setCheckable(true); connect(action, SIGNAL(toggled(bool)), this, SLOT(slotShowHideTimeline(bool))); m_d->showInTimeline = action; NEW_LAYER_ACTION("add_new_paint_layer", "KisPaintLayer"); NEW_LAYER_ACTION("add_new_group_layer", "KisGroupLayer"); NEW_LAYER_ACTION("add_new_clone_layer", "KisCloneLayer"); NEW_LAYER_ACTION("add_new_shape_layer", "KisShapeLayer"); NEW_LAYER_ACTION("add_new_adjustment_layer", "KisAdjustmentLayer"); NEW_LAYER_ACTION("add_new_fill_layer", "KisGeneratorLayer"); NEW_LAYER_ACTION("add_new_file_layer", "KisFileLayer"); NEW_LAYER_ACTION("add_new_transparency_mask", "KisTransparencyMask"); NEW_LAYER_ACTION("add_new_filter_mask", "KisFilterMask"); NEW_LAYER_ACTION("add_new_colorize_mask", "KisColorizeMask"); NEW_LAYER_ACTION("add_new_transform_mask", "KisTransformMask"); NEW_LAYER_ACTION("add_new_selection_mask", "KisSelectionMask"); connect(&m_d->nodeCreationSignalMapper, SIGNAL(mapped(const QString &)), this, SLOT(createNode(const QString &))); CONVERT_NODE_ACTION("convert_to_paint_layer", "KisPaintLayer"); CONVERT_NODE_ACTION_2("convert_to_selection_mask", "KisSelectionMask", QStringList() << "KisSelectionMask" << "KisColorizeMask"); CONVERT_NODE_ACTION_2("convert_to_filter_mask", "KisFilterMask", QStringList() << "KisFilterMask" << "KisColorizeMask"); CONVERT_NODE_ACTION_2("convert_to_transparency_mask", "KisTransparencyMask", QStringList() << "KisTransparencyMask" << "KisColorizeMask"); CONVERT_NODE_ACTION("convert_to_animated", "animated"); connect(&m_d->nodeConversionSignalMapper, SIGNAL(mapped(const QString &)), this, SLOT(convertNode(const QString &))); action = actionManager->createAction("isolate_layer"); connect(action, SIGNAL(triggered(bool)), this, SLOT(toggleIsolateMode(bool))); action = actionManager->createAction("toggle_layer_visibility"); connect(action, SIGNAL(triggered()), this, SLOT(toggleVisibility())); action = actionManager->createAction("toggle_layer_lock"); connect(action, SIGNAL(triggered()), this, SLOT(toggleLock())); action = actionManager->createAction("toggle_layer_inherit_alpha"); connect(action, SIGNAL(triggered()), this, SLOT(toggleInheritAlpha())); action = actionManager->createAction("toggle_layer_alpha_lock"); connect(action, SIGNAL(triggered()), this, SLOT(toggleAlphaLock())); action = actionManager->createAction("split_alpha_into_mask"); connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaIntoMask())); action = actionManager->createAction("split_alpha_write"); connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaWrite())); // HINT: we can save even when the nodes are not editable action = actionManager->createAction("split_alpha_save_merged"); connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaSaveMerged())); connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(slotUpdateIsolateModeAction())); connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(slotTryFinishIsolatedMode())); } void KisNodeManager::updateGUI() { // enable/disable all relevant actions m_d->layerManager.updateGUI(); m_d->maskManager.updateGUI(); } KisNodeSP KisNodeManager::activeNode() { if (m_d->imageView) { return m_d->imageView->currentNode(); } return 0; } KisLayerSP KisNodeManager::activeLayer() { return m_d->layerManager.activeLayer(); } const KoColorSpace* KisNodeManager::activeColorSpace() { if (m_d->maskManager.activeDevice()) { return m_d->maskManager.activeDevice()->colorSpace(); } else { Q_ASSERT(m_d->layerManager.activeLayer()); if (m_d->layerManager.activeLayer()->parentLayer()) return m_d->layerManager.activeLayer()->parentLayer()->colorSpace(); else return m_d->view->image()->colorSpace(); } } void KisNodeManager::moveNodeAt(KisNodeSP node, KisNodeSP parent, int index) { if (parent->allowAsChild(node)) { if (node->inherits("KisSelectionMask") && parent->inherits("KisLayer")) { KisSelectionMask *m = dynamic_cast(node.data()); KisLayer *l = qobject_cast(parent.data()); KisSelectionMaskSP selMask = l->selectionMask(); if (m && m->active() && l && l->selectionMask()) selMask->setActive(false); } m_d->commandsAdapter.moveNode(node, parent, index); } } void KisNodeManager::moveNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis) { KUndo2MagicString actionName = kundo2_i18n("Move Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->moveNode(nodes, parent, aboveThis); } void KisNodeManager::copyNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis) { KUndo2MagicString actionName = kundo2_i18n("Copy Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->copyNode(nodes, parent, aboveThis); } void KisNodeManager::addNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis) { KUndo2MagicString actionName = kundo2_i18n("Add Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->addNode(nodes, parent, aboveThis); } void KisNodeManager::toggleIsolateActiveNode() { KisImageWSP image = m_d->view->image(); KisNodeSP activeNode = this->activeNode(); KIS_ASSERT_RECOVER_RETURN(activeNode); if (activeNode == image->isolatedModeRoot()) { toggleIsolateMode(false); } else { toggleIsolateMode(true); } } void KisNodeManager::toggleIsolateMode(bool checked) { KisImageWSP image = m_d->view->image(); if (checked) { KisNodeSP activeNode = this->activeNode(); // Transform and colorize masks don't have pixel data... if (activeNode->inherits("KisTransformMask") || activeNode->inherits("KisColorizeMask")) return; KIS_ASSERT_RECOVER_RETURN(activeNode); if (!image->startIsolatedMode(activeNode)) { KisAction *action = m_d->view->actionManager()->actionByName("isolate_layer"); action->setChecked(false); } } else { image->stopIsolatedMode(); } } void KisNodeManager::slotUpdateIsolateModeAction() { KisAction *action = m_d->view->actionManager()->actionByName("isolate_layer"); Q_ASSERT(action); KisNodeSP activeNode = this->activeNode(); KisNodeSP isolatedRootNode = m_d->view->image()->isolatedModeRoot(); action->setChecked(isolatedRootNode && isolatedRootNode == activeNode); } void KisNodeManager::slotTryFinishIsolatedMode() { KisNodeSP isolatedRootNode = m_d->view->image()->isolatedModeRoot(); if (!isolatedRootNode) return; this->toggleIsolateMode(true); } void KisNodeManager::createNode(const QString & nodeType, bool quiet, KisPaintDeviceSP copyFrom) { if (!m_d->view->blockUntilOperationsFinished(m_d->view->image())) { return; } KisNodeSP activeNode = this->activeNode(); if (!activeNode) { activeNode = m_d->view->image()->root(); } KIS_ASSERT_RECOVER_RETURN(activeNode); // XXX: make factories for this kind of stuff, // with a registry if (nodeType == "KisPaintLayer") { m_d->layerManager.addLayer(activeNode); } else if (nodeType == "KisGroupLayer") { m_d->layerManager.addGroupLayer(activeNode); } else if (nodeType == "KisAdjustmentLayer") { m_d->layerManager.addAdjustmentLayer(activeNode); } else if (nodeType == "KisGeneratorLayer") { m_d->layerManager.addGeneratorLayer(activeNode); } else if (nodeType == "KisShapeLayer") { m_d->layerManager.addShapeLayer(activeNode); } else if (nodeType == "KisCloneLayer") { m_d->layerManager.addCloneLayer(activeNode); } else if (nodeType == "KisTransparencyMask") { m_d->maskManager.createTransparencyMask(activeNode, copyFrom, false); } else if (nodeType == "KisFilterMask") { m_d->maskManager.createFilterMask(activeNode, copyFrom, quiet, false); } else if (nodeType == "KisColorizeMask") { m_d->maskManager.createColorizeMask(activeNode); } else if (nodeType == "KisTransformMask") { m_d->maskManager.createTransformMask(activeNode); } else if (nodeType == "KisSelectionMask") { m_d->maskManager.createSelectionMask(activeNode, copyFrom, false); } else if (nodeType == "KisFileLayer") { m_d->layerManager.addFileLayer(activeNode); } } void KisNodeManager::createFromVisible() { KisLayerUtils::newLayerFromVisible(m_d->view->image(), m_d->view->image()->root()->lastChild()); } void KisNodeManager::slotShowHideTimeline(bool value) { Q_FOREACH (KisNodeSP node, selectedNodes()) { node->setUseInTimeline(value); } } KisLayerSP KisNodeManager::createPaintLayer() { KisNodeSP activeNode = this->activeNode(); if (!activeNode) { activeNode = m_d->view->image()->root(); } return m_d->layerManager.addLayer(activeNode); } void KisNodeManager::convertNode(const QString &nodeType) { KisNodeSP activeNode = this->activeNode(); if (!activeNode) return; if (nodeType == "KisPaintLayer") { m_d->layerManager.convertNodeToPaintLayer(activeNode); } else if (nodeType == "KisSelectionMask" || nodeType == "KisFilterMask" || nodeType == "KisTransparencyMask") { KisPaintDeviceSP copyFrom = activeNode->paintDevice() ? activeNode->paintDevice() : activeNode->projection(); m_d->commandsAdapter.beginMacro(kundo2_i18n("Convert to a Selection Mask")); if (nodeType == "KisSelectionMask") { m_d->maskManager.createSelectionMask(activeNode, copyFrom, true); } else if (nodeType == "KisFilterMask") { m_d->maskManager.createFilterMask(activeNode, copyFrom, false, true); } else if (nodeType == "KisTransparencyMask") { m_d->maskManager.createTransparencyMask(activeNode, copyFrom, true); } m_d->commandsAdapter.removeNode(activeNode); m_d->commandsAdapter.endMacro(); } else { warnKrita << "Unsupported node conversion type:" << nodeType; } } void KisNodeManager::slotSomethingActivatedNodeImpl(KisNodeSP node) { KIS_ASSERT_RECOVER_RETURN(node != activeNode()); if (m_d->activateNodeImpl(node)) { emit sigUiNeedChangeActiveNode(node); emit sigNodeActivated(node); nodesUpdated(); if (node) { bool toggled = m_d->view->actionCollection()->action("view_show_canvas_only")->isChecked(); if (toggled) { m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine); } } } } void KisNodeManager::slotNonUiActivatedNode(KisNodeSP node) { if (node == activeNode()) return; slotSomethingActivatedNodeImpl(node); if (node) { bool toggled = m_d->view->actionCollection()->action("view_show_canvas_only")->isChecked(); if (toggled) { m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine); } } } void KisNodeManager::slotUiActivatedNode(KisNodeSP node) { if (node == activeNode()) return; slotSomethingActivatedNodeImpl(node); if (node) { QStringList vectorTools = QStringList() << "InteractionTool" << "KarbonPatternTool" << "KarbonGradientTool" << "KarbonCalligraphyTool" << "CreateShapesTool" << "PathTool"; QStringList pixelTools = QStringList() << "KritaShape/KisToolBrush" << "KritaShape/KisToolDyna" << "KritaShape/KisToolMultiBrush" << "KritaFill/KisToolFill" << "KritaFill/KisToolGradient"; if (node->inherits("KisShapeLayer")) { if (pixelTools.contains(KoToolManager::instance()->activeToolId())) { KoToolManager::instance()->switchToolRequested("InteractionTool"); } } else { if (vectorTools.contains(KoToolManager::instance()->activeToolId())) { KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush"); } } } } void KisNodeManager::nodesUpdated() { KisNodeSP node = activeNode(); if (!node) return; m_d->layerManager.layersUpdated(); m_d->maskManager.masksUpdated(); m_d->view->updateGUI(); m_d->view->selectionManager()->selectionChanged(); { KisSignalsBlocker b(m_d->showInTimeline); m_d->showInTimeline->setChecked(node->useInTimeline()); } } KisPaintDeviceSP KisNodeManager::activePaintDevice() { return m_d->maskManager.activeMask() ? m_d->maskManager.activeDevice() : m_d->layerManager.activeDevice(); } void KisNodeManager::nodeProperties(KisNodeSP node) { if (selectedNodes().size() > 1 || node->inherits("KisLayer")) { m_d->layerManager.layerProperties(); } else if (node->inherits("KisMask")) { m_d->maskManager.maskProperties(); } } qint32 KisNodeManager::convertOpacityToInt(qreal opacity) { /** * Scales opacity from the range 0...100 * to the integer range 0...255 */ return qMin(255, int(opacity * 2.55 + 0.5)); } void KisNodeManager::setNodeOpacity(KisNodeSP node, qint32 opacity, bool finalChange) { if (!node) return; if (node->opacity() == opacity) return; if (!finalChange) { node->setOpacity(opacity); node->setDirty(); } else { m_d->commandsAdapter.setOpacity(node, opacity); } } void KisNodeManager::setNodeCompositeOp(KisNodeSP node, const KoCompositeOp* compositeOp) { if (!node) return; if (node->compositeOp() == compositeOp) return; m_d->commandsAdapter.setCompositeOp(node, compositeOp); } void KisNodeManager::slotImageRequestNodeReselection(KisNodeSP activeNode, const KisNodeList &selectedNodes) { if (activeNode) { slotNonUiActivatedNode(activeNode); } if (!selectedNodes.isEmpty()) { slotSetSelectedNodes(selectedNodes); } } void KisNodeManager::slotSetSelectedNodes(const KisNodeList &nodes) { m_d->selectedNodes = nodes; emit sigUiNeedChangeSelectedNodes(nodes); } KisNodeList KisNodeManager::selectedNodes() { return m_d->selectedNodes; } KisNodeSelectionAdapter* KisNodeManager::nodeSelectionAdapter() const { return m_d->nodeSelectionAdapter.data(); } KisNodeInsertionAdapter* KisNodeManager::nodeInsertionAdapter() const { return m_d->nodeInsertionAdapter.data(); } void KisNodeManager::nodeOpacityChanged(qreal opacity, bool finalChange) { KisNodeSP node = activeNode(); setNodeOpacity(node, convertOpacityToInt(opacity), finalChange); } void KisNodeManager::nodeCompositeOpChanged(const KoCompositeOp* op) { KisNodeSP node = activeNode(); setNodeCompositeOp(node, op); } void KisNodeManager::duplicateActiveNode() { KUndo2MagicString actionName = kundo2_i18n("Duplicate Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->duplicateNode(selectedNodes()); } KisNodeJugglerCompressed* KisNodeManager::Private::lazyGetJuggler(const KUndo2MagicString &actionName) { KisImageWSP image = view->image(); if (!nodeJuggler || (nodeJuggler && !nodeJuggler->canMergeAction(actionName))) { nodeJuggler = new KisNodeJugglerCompressed(actionName, image, q, 750); nodeJuggler->setAutoDelete(true); } return nodeJuggler; } void KisNodeManager::raiseNode() { KUndo2MagicString actionName = kundo2_i18n("Raise Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->raiseNode(selectedNodes()); } void KisNodeManager::lowerNode() { KUndo2MagicString actionName = kundo2_i18n("Lower Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->lowerNode(selectedNodes()); } void KisNodeManager::removeSingleNode(KisNodeSP node) { if (!node || !node->parent()) { return; } KisNodeList nodes; nodes << node; removeSelectedNodes(nodes); } void KisNodeManager::removeSelectedNodes(KisNodeList nodes) { KUndo2MagicString actionName = kundo2_i18n("Remove Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->removeNode(nodes); } void KisNodeManager::removeNode() { removeSelectedNodes(selectedNodes()); } void KisNodeManager::mirrorNodeX() { KisNodeSP node = activeNode(); KUndo2MagicString commandName; if (node->inherits("KisLayer")) { commandName = kundo2_i18n("Mirror Layer X"); } else if (node->inherits("KisMask")) { commandName = kundo2_i18n("Mirror Mask X"); } mirrorNode(node, commandName, Qt::Horizontal); } void KisNodeManager::mirrorNodeY() { KisNodeSP node = activeNode(); KUndo2MagicString commandName; if (node->inherits("KisLayer")) { commandName = kundo2_i18n("Mirror Layer Y"); } else if (node->inherits("KisMask")) { commandName = kundo2_i18n("Mirror Mask Y"); } mirrorNode(node, commandName, Qt::Vertical); } inline bool checkForGlobalSelection(KisNodeSP node) { return dynamic_cast(node.data()) && node->parent() && !node->parent()->parent(); } void KisNodeManager::activateNextNode() { KisNodeSP activeNode = this->activeNode(); if (!activeNode) return; KisNodeSP node = activeNode->nextSibling(); while (node && node->childCount() > 0) { node = node->firstChild(); } if (!node && activeNode->parent() && activeNode->parent()->parent()) { node = activeNode->parent(); } while(node && checkForGlobalSelection(node)) { node = node->nextSibling(); } if (node) { slotNonUiActivatedNode(node); } } void KisNodeManager::activatePreviousNode() { KisNodeSP activeNode = this->activeNode(); if (!activeNode) return; KisNodeSP node; if (activeNode->childCount() > 0) { node = activeNode->lastChild(); } else { node = activeNode->prevSibling(); } while (!node && activeNode->parent()) { node = activeNode->parent()->prevSibling(); activeNode = activeNode->parent(); } while(node && checkForGlobalSelection(node)) { node = node->prevSibling(); } if (node) { slotNonUiActivatedNode(node); } } void KisNodeManager::switchToPreviouslyActiveNode() { if (m_d->previouslyActiveNode && m_d->previouslyActiveNode->parent()) { slotNonUiActivatedNode(m_d->previouslyActiveNode); } } void KisNodeManager::rotate(double radians) { if(!m_d->view->image()) return; KisNodeSP node = activeNode(); if (!node) return; if (!m_d->view->blockUntilOperationsFinished(m_d->view->image())) return; m_d->view->image()->rotateNode(node, radians); } void KisNodeManager::rotate180() { rotate(M_PI); } void KisNodeManager::rotateLeft90() { rotate(-M_PI / 2); } void KisNodeManager::rotateRight90() { rotate(M_PI / 2); } void KisNodeManager::shear(double angleX, double angleY) { if (!m_d->view->image()) return; KisNodeSP node = activeNode(); if (!node) return; if(!m_d->view->blockUntilOperationsFinished(m_d->view->image())) return; m_d->view->image()->shearNode(node, angleX, angleY); } void KisNodeManager::scale(double sx, double sy, KisFilterStrategy *filterStrategy) { KisNodeSP node = activeNode(); KIS_ASSERT_RECOVER_RETURN(node); m_d->view->image()->scaleNode(node, sx, sy, filterStrategy); nodesUpdated(); } void KisNodeManager::mirrorNode(KisNodeSP node, const KUndo2MagicString& actionName, Qt::Orientation orientation) { KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; KisProcessingApplicator applicator(m_d->view->image(), node, KisProcessingApplicator::RECURSIVE, emitSignals, actionName); KisProcessingVisitorSP visitor = new KisMirrorProcessingVisitor(m_d->view->image()->bounds(), orientation); applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT); applicator.end(); nodesUpdated(); } void KisNodeManager::Private::saveDeviceAsImage(KisPaintDeviceSP device, const QString &defaultName, const QRect &bounds, qreal xRes, qreal yRes, quint8 opacity) { KoFileDialog dialog(view->mainWindow(), KoFileDialog::SaveFile, "savenodeasimage"); dialog.setCaption(i18n("Export \"%1\"", defaultName)); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Export)); QString filename = dialog.filename(); if (filename.isEmpty()) return; QUrl url = QUrl::fromLocalFile(filename); if (url.isEmpty()) return; QString mimefilter = KisMimeDatabase::mimeTypeForFile(filename);; QScopedPointer doc(KisPart::instance()->createDocument()); KisImageSP dst = new KisImage(doc->createUndoStore(), bounds.width(), bounds.height(), device->compositionSourceColorSpace(), defaultName); dst->setResolution(xRes, yRes); doc->setCurrentImage(dst); KisPaintLayer* paintLayer = new KisPaintLayer(dst, "paint device", opacity); paintLayer->paintDevice()->makeCloneFrom(device, bounds); dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0)); dst->initialRefreshGraph(); doc->exportDocumentSync(url, mimefilter.toLatin1()); } void KisNodeManager::saveNodeAsImage() { KisNodeSP node = activeNode(); if (!node) { warnKrita << "BUG: Save Node As Image was called without any node selected"; return; } KisImageWSP image = m_d->view->image(); QRect saveRect = image->bounds() | node->exactBounds(); KisPaintDeviceSP device = node->paintDevice(); if (!device) { device = node->projection(); } m_d->saveDeviceAsImage(device, node->name(), saveRect, image->xRes(), image->yRes(), node->opacity()); } void KisNodeManager::slotSplitAlphaIntoMask() { KisNodeSP node = activeNode(); // guaranteed by KisActionManager KIS_ASSERT_RECOVER_RETURN(node->hasEditablePaintDevice()); KisPaintDeviceSP srcDevice = node->paintDevice(); const KoColorSpace *srcCS = srcDevice->colorSpace(); const QRect processRect = srcDevice->exactBounds() | srcDevice->defaultBounds()->bounds(); KisPaintDeviceSP selectionDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); m_d->commandsAdapter.beginMacro(kundo2_i18n("Split Alpha into a Mask")); KisTransaction transaction(kundo2_noi18n("__split_alpha_channel__"), srcDevice); KisSequentialIterator srcIt(srcDevice, processRect); KisSequentialIterator dstIt(selectionDevice, processRect); do { quint8 *srcPtr = srcIt.rawData(); quint8 *alpha8Ptr = dstIt.rawData(); *alpha8Ptr = srcCS->opacityU8(srcPtr); srcCS->setOpacity(srcPtr, OPACITY_OPAQUE_U8, 1); } while (srcIt.nextPixel() && dstIt.nextPixel()); m_d->commandsAdapter.addExtraCommand(transaction.endAndTake()); createNode("KisTransparencyMask", false, selectionDevice); m_d->commandsAdapter.endMacro(); } void KisNodeManager::Private::mergeTransparencyMaskAsAlpha(bool writeToLayers) { KisNodeSP node = q->activeNode(); KisNodeSP parentNode = node->parent(); // guaranteed by KisActionManager KIS_ASSERT_RECOVER_RETURN(node->inherits("KisTransparencyMask")); if (writeToLayers && !parentNode->hasEditablePaintDevice()) { QMessageBox::information(view->mainWindow(), i18nc("@title:window", "Layer %1 is not editable").arg(parentNode->name()), i18n("Cannot write alpha channel of " "the parent layer \"%1\".\n" "The operation will be cancelled.").arg(parentNode->name())); return; } KisPaintDeviceSP dstDevice; if (writeToLayers) { KIS_ASSERT_RECOVER_RETURN(parentNode->paintDevice()); dstDevice = parentNode->paintDevice(); } else { KisPaintDeviceSP copyDevice = parentNode->paintDevice(); if (!copyDevice) { copyDevice = parentNode->original(); } dstDevice = new KisPaintDevice(*copyDevice); } const KoColorSpace *dstCS = dstDevice->colorSpace(); KisPaintDeviceSP selectionDevice = node->paintDevice(); KIS_ASSERT_RECOVER_RETURN(selectionDevice->colorSpace()->pixelSize() == 1); const QRect processRect = selectionDevice->exactBounds() | dstDevice->exactBounds() | selectionDevice->defaultBounds()->bounds(); QScopedPointer transaction; if (writeToLayers) { commandsAdapter.beginMacro(kundo2_i18n("Write Alpha into a Layer")); transaction.reset(new KisTransaction(kundo2_noi18n("__write_alpha_channel__"), dstDevice)); } KisSequentialIterator srcIt(selectionDevice, processRect); KisSequentialIterator dstIt(dstDevice, processRect); do { quint8 *alpha8Ptr = srcIt.rawData(); quint8 *dstPtr = dstIt.rawData(); dstCS->setOpacity(dstPtr, *alpha8Ptr, 1); } while (srcIt.nextPixel() && dstIt.nextPixel()); if (writeToLayers) { commandsAdapter.addExtraCommand(transaction->endAndTake()); commandsAdapter.removeNode(node); commandsAdapter.endMacro(); } else { KisImageWSP image = view->image(); QRect saveRect = image->bounds(); saveDeviceAsImage(dstDevice, parentNode->name(), saveRect, image->xRes(), image->yRes(), OPACITY_OPAQUE_U8); } } void KisNodeManager::slotSplitAlphaWrite() { m_d->mergeTransparencyMaskAsAlpha(true); } void KisNodeManager::slotSplitAlphaSaveMerged() { m_d->mergeTransparencyMaskAsAlpha(false); } void KisNodeManager::toggleLock() { KisNodeList nodes = this->selectedNodes(); KisNodeSP active = activeNode(); if (nodes.isEmpty() || !active) return; bool isLocked = active->userLocked(); for (auto &node : nodes) { node->setUserLocked(!isLocked); } } void KisNodeManager::toggleVisibility() { KisNodeList nodes = this->selectedNodes(); KisNodeSP active = activeNode(); if (nodes.isEmpty() || !active) return; bool isVisible = active->visible(); for (auto &node : nodes) { node->setVisible(!isVisible); node->setDirty(); } } void KisNodeManager::toggleAlphaLock() { KisNodeList nodes = this->selectedNodes(); KisNodeSP active = activeNode(); if (nodes.isEmpty() || !active) return; auto layer = qobject_cast(active.data()); if (!layer) { return; } bool isAlphaLocked = layer->alphaLocked(); for (auto &node : nodes) { auto layer = qobject_cast(node.data()); if (layer) { layer->setAlphaLocked(!isAlphaLocked); } } } void KisNodeManager::toggleInheritAlpha() { KisNodeList nodes = this->selectedNodes(); KisNodeSP active = activeNode(); if (nodes.isEmpty() || !active) return; auto layer = qobject_cast(active.data()); if (!layer) { return; } bool isAlphaDisabled = layer->alphaChannelDisabled(); for (auto &node : nodes) { auto layer = qobject_cast(node.data()); if (layer) { layer->disableAlphaChannel(!isAlphaDisabled); node->setDirty(); } } } void KisNodeManager::cutLayersToClipboard() { KisNodeList nodes = this->selectedNodes(); if (nodes.isEmpty()) return; KisClipboard::instance()->setLayers(nodes, m_d->view->image(), false); KUndo2MagicString actionName = kundo2_i18n("Cut Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->removeNode(nodes); } void KisNodeManager::copyLayersToClipboard() { KisNodeList nodes = this->selectedNodes(); KisClipboard::instance()->setLayers(nodes, m_d->view->image(), true); } void KisNodeManager::pasteLayersFromClipboard() { const QMimeData *data = KisClipboard::instance()->layersMimeData(); if (!data) return; KisNodeSP activeNode = this->activeNode(); KisShapeController *shapeController = dynamic_cast(m_d->imageView->document()->shapeController()); Q_ASSERT(shapeController); KisDummiesFacadeBase *dummiesFacade = dynamic_cast(m_d->imageView->document()->shapeController()); Q_ASSERT(dummiesFacade); const bool copyNode = false; KisImageSP image = m_d->view->image(); KisNodeDummy *parentDummy = dummiesFacade->dummyForNode(activeNode); KisNodeDummy *aboveThisDummy = parentDummy ? parentDummy->lastChild() : 0; KisMimeData::insertMimeLayers(data, image, shapeController, parentDummy, aboveThisDummy, copyNode, nodeInsertionAdapter()); } void KisNodeManager::createQuickGroupImpl(KisNodeJugglerCompressed *juggler, const QString &overrideGroupName, KisNodeSP *newGroup, KisNodeSP *newLastChild) { KisNodeSP active = activeNode(); if (!active) return; KisImageSP image = m_d->view->image(); QString groupName = !overrideGroupName.isEmpty() ? overrideGroupName : image->nextLayerName(); KisGroupLayerSP group = new KisGroupLayer(image.data(), groupName, OPACITY_OPAQUE_U8); KisNodeList nodes1; nodes1 << group; KisNodeList nodes2; nodes2 = KisLayerUtils::sortMergableNodes(image->root(), selectedNodes()); KisLayerUtils::filterMergableNodes(nodes2); if (KisLayerUtils::checkIsChildOf(active, nodes2)) { active = nodes2.first(); } KisNodeSP parent = active->parent(); KisNodeSP aboveThis = active; juggler->addNode(nodes1, parent, aboveThis); juggler->moveNode(nodes2, group, 0); *newGroup = group; *newLastChild = nodes2.last(); } void KisNodeManager::createQuickGroup() { KUndo2MagicString actionName = kundo2_i18n("Quick Group"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); KisNodeSP parent; KisNodeSP above; createQuickGroupImpl(juggler, "", &parent, &above); } void KisNodeManager::createQuickClippingGroup() { KUndo2MagicString actionName = kundo2_i18n("Quick Clipping Group"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); KisNodeSP parent; KisNodeSP above; KisImageSP image = m_d->view->image(); createQuickGroupImpl(juggler, image->nextLayerName(i18nc("default name for a clipping group layer", "Clipping Group")), &parent, &above); KisPaintLayerSP maskLayer = new KisPaintLayer(image.data(), i18nc("default name for quick clip group mask layer", "Mask Layer"), OPACITY_OPAQUE_U8, image->colorSpace()); maskLayer->disableAlphaChannel(true); juggler->addNode(KisNodeList() << maskLayer, parent, above); } void KisNodeManager::quickUngroup() { KisNodeSP active = activeNode(); if (!active) return; KisNodeSP parent = active->parent(); KisNodeSP aboveThis = active; KUndo2MagicString actionName = kundo2_i18n("Quick Ungroup"); if (parent && dynamic_cast(active.data())) { KisNodeList nodes = active->childNodes(QStringList(), KoProperties()); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->moveNode(nodes, parent, active); juggler->removeNode(KisNodeList() << active); } else if (parent && parent->parent()) { KisNodeSP grandParent = parent->parent(); KisNodeList allChildNodes = parent->childNodes(QStringList(), KoProperties()); KisNodeList allSelectedNodes = selectedNodes(); const bool removeParent = KritaUtils::compareListsUnordered(allChildNodes, allSelectedNodes); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->moveNode(allSelectedNodes, grandParent, parent); if (removeParent) { juggler->removeNode(KisNodeList() << parent); } } } void KisNodeManager::selectLayersImpl(const KoProperties &props, const KoProperties &invertedProps) { KisImageSP image = m_d->view->image(); KisNodeList nodes = KisLayerUtils::findNodesWithProps(image->root(), props, true); KisNodeList selectedNodes = this->selectedNodes(); if (KritaUtils::compareListsUnordered(nodes, selectedNodes)) { nodes = KisLayerUtils::findNodesWithProps(image->root(), invertedProps, true); } if (!nodes.isEmpty()) { slotImageRequestNodeReselection(nodes.last(), nodes); } } void KisNodeManager::selectAllNodes() { KoProperties props; selectLayersImpl(props, props); } void KisNodeManager::selectVisibleNodes() { KoProperties props; props.setProperty("visible", true); KoProperties invertedProps; invertedProps.setProperty("visible", false); selectLayersImpl(props, invertedProps); } void KisNodeManager::selectLockedNodes() { KoProperties props; props.setProperty("locked", true); KoProperties invertedProps; invertedProps.setProperty("locked", false); selectLayersImpl(props, invertedProps); } void KisNodeManager::selectInvisibleNodes() { KoProperties props; props.setProperty("visible", false); KoProperties invertedProps; invertedProps.setProperty("visible", true); selectLayersImpl(props, invertedProps); } void KisNodeManager::selectUnlockedNodes() { KoProperties props; props.setProperty("locked", false); KoProperties invertedProps; invertedProps.setProperty("locked", true); selectLayersImpl(props, invertedProps); } diff --git a/libs/ui/kis_node_model.cpp b/libs/ui/kis_node_model.cpp index c7c5704dcb..c5d9d29db8 100644 --- a/libs/ui/kis_node_model.cpp +++ b/libs/ui/kis_node_model.cpp @@ -1,704 +1,706 @@ /* * Copyright (c) 2007 Boudewijn Rempt * Copyright (c) 2008 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_node_model.h" #include #include #include #include #include #include #include "kis_mimedata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_dummies_facade_base.h" #include "kis_node_dummies_graph.h" #include "kis_model_index_converter.h" #include "kis_model_index_converter_show_all.h" #include "kis_node_selection_adapter.h" #include "kis_node_insertion_adapter.h" #include "kis_config.h" #include "kis_config_notifier.h" #include struct KisNodeModel::Private { KisImageWSP image; KisShapeController *shapeController = 0; KisNodeSelectionAdapter *nodeSelectionAdapter = 0; KisNodeInsertionAdapter *nodeInsertionAdapter = 0; QList updateQueue; QTimer updateTimer; KisModelIndexConverterBase *indexConverter = 0; QPointer dummiesFacade = 0; bool needFinishRemoveRows = false; bool needFinishInsertRows = false; bool showRootLayer = false; bool showGlobalSelection = false; QPersistentModelIndex activeNodeIndex; QPointer parentOfRemovedNode = 0; QSet dropEnabled; }; KisNodeModel::KisNodeModel(QObject * parent) : QAbstractItemModel(parent) , m_d(new Private) { updateSettings(); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(updateSettings())); m_d->updateTimer.setSingleShot(true); connect(&m_d->updateTimer, SIGNAL(timeout()), SLOT(processUpdateQueue())); } KisNodeModel::~KisNodeModel() { delete m_d->indexConverter; delete m_d; } KisNodeSP KisNodeModel::nodeFromIndex(const QModelIndex &index) const { Q_ASSERT(index.isValid()); KisNodeDummy *dummy = m_d->indexConverter->dummyFromIndex(index); if (dummy) { return dummy->node(); } return 0; } QModelIndex KisNodeModel::indexFromNode(KisNodeSP node) const { KisNodeDummy *dummy = m_d->dummiesFacade->dummyForNode(node); if(dummy) return m_d->indexConverter->indexFromDummy(dummy); return QModelIndex(); } bool KisNodeModel::belongsToIsolatedGroup(KisImageSP image, KisNodeSP node, KisDummiesFacadeBase *dummiesFacade) { KisNodeSP isolatedRoot = image->isolatedModeRoot(); if (!isolatedRoot) return true; KisNodeDummy *isolatedRootDummy = dummiesFacade->dummyForNode(isolatedRoot); KisNodeDummy *dummy = dummiesFacade->dummyForNode(node); while (dummy) { if (dummy == isolatedRootDummy) { return true; } dummy = dummy->parent(); } return false; } bool KisNodeModel::belongsToIsolatedGroup(KisNodeSP node) const { return belongsToIsolatedGroup(m_d->image, node, m_d->dummiesFacade); } void KisNodeModel::resetIndexConverter() { delete m_d->indexConverter; m_d->indexConverter = 0; if(m_d->dummiesFacade) { m_d->indexConverter = createIndexConverter(); } } KisModelIndexConverterBase *KisNodeModel::createIndexConverter() { if(m_d->showRootLayer) { return new KisModelIndexConverterShowAll(m_d->dummiesFacade, this); } else { return new KisModelIndexConverter(m_d->dummiesFacade, this, m_d->showGlobalSelection); } } void KisNodeModel::regenerateItems(KisNodeDummy *dummy) { const QModelIndex &index = m_d->indexConverter->indexFromDummy(dummy); emit dataChanged(index, index); dummy = dummy->firstChild(); while (dummy) { regenerateItems(dummy); dummy = dummy->nextSibling(); } } void KisNodeModel::slotIsolatedModeChanged() { regenerateItems(m_d->dummiesFacade->rootDummy()); } bool KisNodeModel::showGlobalSelection() const { KisConfig cfg; return cfg.showGlobalSelection(); } void KisNodeModel::setShowGlobalSelection(bool value) { KisConfig cfg; cfg.setShowGlobalSelection(value); updateSettings(); } void KisNodeModel::updateSettings() { KisConfig cfg; bool oldShowRootLayer = m_d->showRootLayer; bool oldShowGlobalSelection = m_d->showGlobalSelection; m_d->showRootLayer = cfg.showRootLayer(); m_d->showGlobalSelection = cfg.showGlobalSelection(); if (m_d->showRootLayer != oldShowRootLayer || m_d->showGlobalSelection != oldShowGlobalSelection) { resetIndexConverter(); - reset(); + beginResetModel(); + endResetModel(); } } void KisNodeModel::progressPercentageChanged(int, const KisNodeSP node) { if(!m_d->dummiesFacade) return; // Need to check here as the node might already be removed, but there might // still be some signals arriving from another thread if (m_d->dummiesFacade->hasDummyForNode(node)) { QModelIndex index = indexFromNode(node); emit dataChanged(index, index); } } KisModelIndexConverterBase * KisNodeModel::indexConverter() const { return m_d->indexConverter; } KisDummiesFacadeBase *KisNodeModel::dummiesFacade() const { return m_d->dummiesFacade; } void KisNodeModel::connectDummy(KisNodeDummy *dummy, bool needConnect) { KisNodeSP node = dummy->node(); if (!node) { qWarning() << "Dummy node has no node!" << dummy << dummy->node(); return; } KisNodeProgressProxy *progressProxy = node->nodeProgressProxy(); if(progressProxy) { if(needConnect) { connect(progressProxy, SIGNAL(percentageChanged(int,KisNodeSP)), SLOT(progressPercentageChanged(int,KisNodeSP))); } else { progressProxy->disconnect(this); } } } void KisNodeModel::connectDummies(KisNodeDummy *dummy, bool needConnect) { connectDummy(dummy, needConnect); dummy = dummy->firstChild(); while(dummy) { connectDummies(dummy, needConnect); dummy = dummy->nextSibling(); } } void KisNodeModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImageWSP image, KisShapeController *shapeController, KisNodeSelectionAdapter *nodeSelectionAdapter, KisNodeInsertionAdapter *nodeInsertionAdapter) { QPointer oldDummiesFacade(m_d->dummiesFacade); KisShapeController *oldShapeController = m_d->shapeController; m_d->shapeController = shapeController; m_d->nodeSelectionAdapter = nodeSelectionAdapter; m_d->nodeInsertionAdapter = nodeInsertionAdapter; if (oldDummiesFacade && m_d->image) { m_d->image->disconnect(this); oldDummiesFacade->disconnect(this); connectDummies(m_d->dummiesFacade->rootDummy(), false); } m_d->image = image; m_d->dummiesFacade = dummiesFacade; m_d->parentOfRemovedNode = 0; resetIndexConverter(); if (m_d->dummiesFacade) { KisNodeDummy *rootDummy = m_d->dummiesFacade->rootDummy(); if (rootDummy) { connectDummies(rootDummy, true); } connect(m_d->dummiesFacade, SIGNAL(sigBeginInsertDummy(KisNodeDummy*,int,QString)), SLOT(slotBeginInsertDummy(KisNodeDummy*,int,QString))); connect(m_d->dummiesFacade, SIGNAL(sigEndInsertDummy(KisNodeDummy*)), SLOT(slotEndInsertDummy(KisNodeDummy*))); connect(m_d->dummiesFacade, SIGNAL(sigBeginRemoveDummy(KisNodeDummy*)), SLOT(slotBeginRemoveDummy(KisNodeDummy*))); connect(m_d->dummiesFacade, SIGNAL(sigEndRemoveDummy()), SLOT(slotEndRemoveDummy())); connect(m_d->dummiesFacade, SIGNAL(sigDummyChanged(KisNodeDummy*)), SLOT(slotDummyChanged(KisNodeDummy*))); if (m_d->image.isValid()) { connect(m_d->image, SIGNAL(sigIsolatedModeChanged()), SLOT(slotIsolatedModeChanged())); } } if (m_d->dummiesFacade != oldDummiesFacade || m_d->shapeController != oldShapeController) { - reset(); + beginResetModel(); + endResetModel(); } } void KisNodeModel::slotBeginInsertDummy(KisNodeDummy *parent, int index, const QString &metaObjectType) { int row = 0; QModelIndex parentIndex; bool willAdd = m_d->indexConverter->indexFromAddedDummy(parent, index, metaObjectType, parentIndex, row); if(willAdd) { beginInsertRows(parentIndex, row, row); m_d->needFinishInsertRows = true; } } void KisNodeModel::slotEndInsertDummy(KisNodeDummy *dummy) { if(m_d->needFinishInsertRows) { connectDummy(dummy, true); endInsertRows(); m_d->needFinishInsertRows = false; } } void KisNodeModel::slotBeginRemoveDummy(KisNodeDummy *dummy) { if (!dummy) return; // FIXME: is it really what we want? m_d->updateTimer.stop(); m_d->updateQueue.clear(); m_d->parentOfRemovedNode = dummy->parent(); QModelIndex parentIndex; if (m_d->parentOfRemovedNode) { parentIndex = m_d->indexConverter->indexFromDummy(m_d->parentOfRemovedNode); } QModelIndex itemIndex = m_d->indexConverter->indexFromDummy(dummy); if (itemIndex.isValid()) { connectDummy(dummy, false); beginRemoveRows(parentIndex, itemIndex.row(), itemIndex.row()); m_d->needFinishRemoveRows = true; } } void KisNodeModel::slotEndRemoveDummy() { if(m_d->needFinishRemoveRows) { endRemoveRows(); m_d->needFinishRemoveRows = false; } } void KisNodeModel::slotDummyChanged(KisNodeDummy *dummy) { if (!m_d->updateQueue.contains(dummy)) { m_d->updateQueue.append(dummy); } m_d->updateTimer.start(1000); } -void addChangedIndex(const QModelIndex &index, QSet *indexes) +void addChangedIndex(const QModelIndex &idx, QSet *indexes) { - if (!index.isValid() || indexes->contains(index)) return; + if (!idx.isValid() || indexes->contains(idx)) return; - indexes->insert(index); + indexes->insert(idx); - const int rowCount = index.model()->rowCount(index); + const int rowCount = idx.model()->rowCount(idx); for (int i = 0; i < rowCount; i++) { - addChangedIndex(index.child(i, 0), indexes); + addChangedIndex(idx.model()->index(i, 0, idx), indexes); } } void KisNodeModel::processUpdateQueue() { QSet indexes; Q_FOREACH (KisNodeDummy *dummy, m_d->updateQueue) { QModelIndex index = m_d->indexConverter->indexFromDummy(dummy); addChangedIndex(index, &indexes); } Q_FOREACH (const QModelIndex &index, indexes) { emit dataChanged(index, index); } m_d->updateQueue.clear(); } QModelIndex KisNodeModel::index(int row, int col, const QModelIndex &parent) const { if(!m_d->dummiesFacade || !hasIndex(row, col, parent)) return QModelIndex(); QModelIndex itemIndex; KisNodeDummy *dummy = m_d->indexConverter->dummyFromRow(row, parent); if(dummy) { itemIndex = m_d->indexConverter->indexFromDummy(dummy); } return itemIndex; } int KisNodeModel::rowCount(const QModelIndex &parent) const { if(!m_d->dummiesFacade) return 0; return m_d->indexConverter->rowCount(parent); } int KisNodeModel::columnCount(const QModelIndex&) const { return 1; } QModelIndex KisNodeModel::parent(const QModelIndex &index) const { if(!m_d->dummiesFacade || !index.isValid()) return QModelIndex(); KisNodeDummy *dummy = m_d->indexConverter->dummyFromIndex(index); KisNodeDummy *parentDummy = dummy->parent(); QModelIndex parentIndex; if(parentDummy) { parentIndex = m_d->indexConverter->indexFromDummy(parentDummy); } return parentIndex; } QVariant KisNodeModel::data(const QModelIndex &index, int role) const { if (!m_d->dummiesFacade || !index.isValid() || !m_d->image.isValid()) return QVariant(); KisNodeSP node = nodeFromIndex(index); switch (role) { case Qt::DisplayRole: return node->name(); case Qt::DecorationRole: return node->icon(); case Qt::EditRole: return node->name(); case Qt::SizeHintRole: return m_d->image->size(); // FIXME case Qt::TextColorRole: return belongsToIsolatedGroup(node) && !node->projectionLeaf()->isDroppedMask() ? QVariant() : QVariant(QColor(Qt::gray)); case Qt::FontRole: { QFont baseFont; if (node->projectionLeaf()->isDroppedMask()) { baseFont.setStrikeOut(true); } if (m_d->activeNodeIndex == index) { baseFont.setBold(true); } return baseFont; } case KisNodeModel::PropertiesRole: return QVariant::fromValue(node->sectionModelProperties()); case KisNodeModel::AspectRatioRole: return double(m_d->image->width()) / m_d->image->height(); case KisNodeModel::ProgressRole: { KisNodeProgressProxy *proxy = node->nodeProgressProxy(); return proxy ? proxy->percentage() : -1; } case KisNodeModel::ActiveRole: { return m_d->activeNodeIndex == index; } case KisNodeModel::ShouldGrayOutRole: { return !node->visible(true); } case KisNodeModel::ColorLabelIndexRole: { return node->colorLabelIndex(); } default: if (role >= int(KisNodeModel::BeginThumbnailRole) && belongsToIsolatedGroup(node)) { const int maxSize = role - int(KisNodeModel::BeginThumbnailRole); QSize size = node->extent().size(); size.scale(maxSize, maxSize, Qt::KeepAspectRatio); if (size.width() == 0 || size.height() == 0) { // No thumbnail can be shown if there isn't width or height... return QVariant(); } return node->createThumbnail(size.width(), size.height()); } else { return QVariant(); } } return QVariant(); } Qt::ItemFlags KisNodeModel::flags(const QModelIndex &index) const { if(!m_d->dummiesFacade || !index.isValid()) return Qt::ItemIsDropEnabled; Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEditable; if (m_d->dropEnabled.contains(index.internalId())) { flags |= Qt::ItemIsDropEnabled; } return flags; } bool KisNodeModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role == KisNodeModel::DropEnabled) { const QMimeData *mimeData = static_cast(value.value()); setDropEnabled(mimeData); return true; } if (role == KisNodeModel::ActiveRole || role == KisNodeModel::AlternateActiveRole) { QModelIndex parentIndex; if (!index.isValid() && m_d->parentOfRemovedNode && m_d->dummiesFacade && m_d->indexConverter) { parentIndex = m_d->indexConverter->indexFromDummy(m_d->parentOfRemovedNode); m_d->parentOfRemovedNode = 0; } KisNodeSP activatedNode; if (index.isValid() && value.toBool()) { activatedNode = nodeFromIndex(index); } else if (parentIndex.isValid() && value.toBool()) { activatedNode = nodeFromIndex(parentIndex); } else { activatedNode = 0; } QModelIndex newActiveNode = activatedNode ? indexFromNode(activatedNode) : QModelIndex(); if (role == KisNodeModel::ActiveRole && value.toBool() && m_d->activeNodeIndex == newActiveNode) { return true; } m_d->activeNodeIndex = newActiveNode; if (m_d->nodeSelectionAdapter) { m_d->nodeSelectionAdapter->setActiveNode(activatedNode); } if (role == KisNodeModel::AlternateActiveRole) { emit toggleIsolateActiveNode(); } emit dataChanged(index, index); return true; } if(!m_d->dummiesFacade || !index.isValid()) return false; bool result = true; bool shouldUpdateRecursively = false; KisNodeSP node = nodeFromIndex(index); switch (role) { case Qt::DisplayRole: case Qt::EditRole: node->setName(value.toString()); break; case KisNodeModel::PropertiesRole: { // don't record undo/redo for visibility, locked or alpha locked changes KisBaseNode::PropertyList proplist = value.value(); KisNodePropertyListCommand::setNodePropertiesNoUndo(node, m_d->image, proplist); shouldUpdateRecursively = true; break; } default: result = false; } if(result) { if (shouldUpdateRecursively) { QSet indexes; addChangedIndex(index, &indexes); Q_FOREACH (const QModelIndex &index, indexes) { emit dataChanged(index, index); } } else { emit dataChanged(index, index); } } return result; } Qt::DropActions KisNodeModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions KisNodeModel::supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction; } bool KisNodeModel::hasDummiesFacade() { return m_d->dummiesFacade != 0; } QStringList KisNodeModel::mimeTypes() const { QStringList types; types << QLatin1String("application/x-krita-node"); types << QLatin1String("application/x-qt-image"); return types; } QMimeData * KisNodeModel::mimeData(const QModelIndexList &indexes) const { KisNodeList nodes; Q_FOREACH (const QModelIndex &idx, indexes) { nodes << nodeFromIndex(idx); } return KisMimeData::mimeForLayers(nodes, m_d->image); } bool KisNodeModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) { Q_UNUSED(column); bool copyNode = (action == Qt::CopyAction); KisNodeDummy *parentDummy = 0; KisNodeDummy *aboveThisDummy = 0; parentDummy = parent.isValid() ? m_d->indexConverter->dummyFromIndex(parent) : m_d->dummiesFacade->rootDummy(); if (row == -1) { aboveThisDummy = parent.isValid() ? parentDummy->lastChild() : 0; } else { aboveThisDummy = row < m_d->indexConverter->rowCount(parent) ? m_d->indexConverter->dummyFromRow(row, parent) : 0; } return KisMimeData::insertMimeLayers(data, m_d->image, m_d->shapeController, parentDummy, aboveThisDummy, copyNode, m_d->nodeInsertionAdapter); } bool KisNodeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const { - if (parent.isValid()) { + if (parent.isValid()) { // drop occured on an item. always return true as returning false will mess up // QT5's drag handling (see KisNodeModel::setDropEnabled). return true; - } else { - return QAbstractItemModel::canDropMimeData(data, action, row, column, parent); - } + } else { + return QAbstractItemModel::canDropMimeData(data, action, row, column, parent); + } } void KisNodeModel::setDropEnabled(const QMimeData *data) { // what happens here should really happen in KisNodeModel::canDropMimeData(), but QT5 // will mess up if an item's Qt::ItemIsDropEnabled does not match what is returned by // canDropMimeData; specifically, if we set the flag, but decide in canDropMimeData() // later on that an "onto" drag is not allowed, QT will display an drop indicator for // insertion, but not perform any drop when the mouse is released. // the only robust implementation seems to set all flags correctly, which is done here. bool copyNode = false; KisNodeList nodes = KisMimeData::loadNodesFast(data, m_d->image, m_d->shapeController, copyNode); m_d->dropEnabled.clear(); updateDropEnabled(nodes); } void KisNodeModel::updateDropEnabled(const QList &nodes, QModelIndex parent) { for (int r = 0; r < rowCount(parent); r++) { QModelIndex idx = index(r, 0, parent); KisNodeSP target = nodeFromIndex(idx); bool dropEnabled = true; Q_FOREACH (const KisNodeSP &node, nodes) { if (!target->allowAsChild(node)) { dropEnabled = false; break; } } if (dropEnabled) { m_d->dropEnabled.insert(idx.internalId()); } emit dataChanged(idx, idx); // indicate to QT that flags() have changed if (hasChildren(idx)) { updateDropEnabled(nodes, idx); } } } diff --git a/libs/ui/kis_palette_delegate.cpp b/libs/ui/kis_palette_delegate.cpp index 689afb6a3e..72ee7e4bf9 100644 --- a/libs/ui/kis_palette_delegate.cpp +++ b/libs/ui/kis_palette_delegate.cpp @@ -1,89 +1,89 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_palette_delegate.h" #include #include #include #include "kis_debug.h" #include KisPaletteDelegate::KisPaletteDelegate(QObject *parent) : QAbstractItemDelegate(parent) { } KisPaletteDelegate::~KisPaletteDelegate() { } void KisPaletteDelegate::setCrossedKeyword(const QString &value) { m_crossedKeyword = value; } void KisPaletteDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { painter->save(); if (! index.isValid()) return; const bool isSelected = option.state & QStyle::State_Selected; const int minSize = qMin(option.rect.width(), option.rect.height()); const int maxWidth = qBound(2, minSize / 6, 4); const int width = isSelected ? maxWidth : 1; - if (qVariantValue(index.data(KisPaletteModel::IsHeaderRole))) { - QString name = qVariantValue(index.data(Qt::DisplayRole)); + if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { + QString name = qvariant_cast(index.data(Qt::DisplayRole)); if (isSelected) { painter->fillRect(option.rect, option.palette.highlight()); } QRect paintRect = kisGrowRect(option.rect, -width); painter->drawText(paintRect, name); } else { if (isSelected) { painter->fillRect(option.rect, option.palette.highlight()); } QRect paintRect = kisGrowRect(option.rect, -width); - QBrush brush = qVariantValue(index.data(Qt::BackgroundRole)); + QBrush brush = qvariant_cast(index.data(Qt::BackgroundRole)); painter->fillRect(paintRect, brush); } painter->restore(); - QString name = qVariantValue(index.data(Qt::DisplayRole)); + QString name = qvariant_cast(index.data(Qt::DisplayRole)); if (!m_crossedKeyword.isNull() && name.toLower().contains(m_crossedKeyword)) { QRect crossRect = kisGrowRect(option.rect, -maxWidth); painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); painter->setPen(QPen(Qt::white, 2.5)); painter->drawLine(crossRect.topLeft(), crossRect.bottomRight()); painter->setPen(QPen(Qt::red, 1.0)); painter->drawLine(crossRect.topLeft(), crossRect.bottomRight()); painter->restore(); } } QSize KisPaletteDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const { return option.decorationSize; } diff --git a/libs/ui/kis_palette_view.cpp b/libs/ui/kis_palette_view.cpp index e9e7788694..24c86b4d33 100644 --- a/libs/ui/kis_palette_view.cpp +++ b/libs/ui/kis_palette_view.cpp @@ -1,340 +1,340 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_palette_view.h" #include #include #include "kis_palette_delegate.h" #include "KisPaletteModel.h" #include "kis_config.h" #include #include #include #include #include #include #include #include struct KisPaletteView::Private { KisPaletteModel *model = 0; bool allowPaletteModification = true; }; KisPaletteView::KisPaletteView(QWidget *parent) : KoTableView(parent), m_d(new Private) { setShowGrid(false); horizontalHeader()->setVisible(false); verticalHeader()->setVisible(false); setItemDelegate(new KisPaletteDelegate()); setDragEnabled(true); setDragDropMode(QAbstractItemView::InternalMove); setDropIndicatorShown(true); KisConfig cfg; //QPalette pal(palette()); //pal.setColor(QPalette::Base, cfg.getMDIBackgroundColor()); //setAutoFillBackground(true); //setPalette(pal); int defaultSectionSize = cfg.paletteDockerPaletteViewSectionSize(); horizontalHeader()->setDefaultSectionSize(defaultSectionSize); verticalHeader()->setDefaultSectionSize(defaultSectionSize); connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(modifyEntry(QModelIndex))); } KisPaletteView::~KisPaletteView() { } void KisPaletteView::setCrossedKeyword(const QString &value) { KisPaletteDelegate *delegate = dynamic_cast(itemDelegate()); KIS_ASSERT_RECOVER_RETURN(delegate); delegate->setCrossedKeyword(value); } bool KisPaletteView::addEntryWithDialog(KoColor color) { KoDialog *window = new KoDialog(); window->setWindowTitle(i18nc("@title:window", "Add a new Colorset Entry")); QFormLayout *editableItems = new QFormLayout(); window->mainWidget()->setLayout(editableItems); QComboBox *cmbGroups = new QComboBox(); QString defaultGroupName = i18nc("Name for default group", "Default"); cmbGroups->addItem(defaultGroupName); cmbGroups->addItems(m_d->model->colorSet()->getGroupNames()); QLineEdit *lnIDName = new QLineEdit(); QLineEdit *lnName = new QLineEdit(); KisColorButton *bnColor = new KisColorButton(); QCheckBox *chkSpot = new QCheckBox(); chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); editableItems->addRow(i18n("Group"), cmbGroups); editableItems->addRow(i18n("ID"), lnIDName); editableItems->addRow(i18n("Name"), lnName); editableItems->addRow(i18n("Color"), bnColor); editableItems->addRow(i18n("Spot"), chkSpot); cmbGroups->setCurrentIndex(0); lnName->setText(i18nc("Part of a default name for a color","Color")+" "+QString::number(m_d->model->colorSet()->nColors()+1)); lnIDName->setText(QString::number(m_d->model->colorSet()->nColors()+1)); bnColor->setColor(color); chkSpot->setChecked(false); // if (window->exec() == KoDialog::Accepted) { QString groupName = cmbGroups->currentText(); if (groupName == defaultGroupName) { groupName = QString(); } KoColorSetEntry newEntry; newEntry.color = bnColor->color(); newEntry.name = lnName->text(); newEntry.id = lnIDName->text(); newEntry.spotColor = chkSpot->isChecked(); m_d->model->addColorSetEntry(newEntry, groupName); m_d->model->colorSet()->save(); return true; } return false; } bool KisPaletteView::addGroupWithDialog() { KoDialog *window = new KoDialog(); window->setWindowTitle(i18nc("@title:window","Add a new group")); QFormLayout *editableItems = new QFormLayout(); window->mainWidget()->setLayout(editableItems); QLineEdit *lnName = new QLineEdit(); editableItems->addRow(i18nc("Name for a group", "Name"), lnName); lnName->setText(i18nc("Part of default name for a new group", "Color Group")+""+QString::number(m_d->model->colorSet()->getGroupNames().size()+1)); if (window->exec() == KoDialog::Accepted) { QString groupName = lnName->text(); m_d->model->addGroup(groupName); m_d->model->colorSet()->save(); return true; } return false; } bool KisPaletteView::removeEntryWithDialog(QModelIndex index) { bool keepColors = true; - if (qVariantValue(index.data(KisPaletteModel::IsHeaderRole))) { + if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { KoDialog *window = new KoDialog(); window->setWindowTitle(i18nc("@title:window","Removing Group")); QFormLayout *editableItems = new QFormLayout(); QCheckBox *chkKeep = new QCheckBox(); window->mainWidget()->setLayout(editableItems); editableItems->addRow(i18nc("Shows up when deleting a group","Keep the Colors"), chkKeep); chkKeep->setChecked(keepColors); if (window->exec() == KoDialog::Accepted) { keepColors = chkKeep->isChecked(); m_d->model->removeEntry(index, keepColors); m_d->model->colorSet()->save(); } } else { m_d->model->removeEntry(index, keepColors); m_d->model->colorSet()->save(); } return true; } void KisPaletteView::trySelectClosestColor(KoColor color) { KoColorSet* color_set = m_d->model->colorSet(); if (!color_set) return; //also don't select if the color is the same as the current selection if (selectedIndexes().size()>0) { QModelIndex currentI = currentIndex(); if (!currentI.isValid()) { currentI = selectedIndexes().last(); } if (!currentI.isValid()) { currentI = selectedIndexes().first(); } if (currentI.isValid()) { if (m_d->model->colorSetEntryFromIndex(currentI).color==color) { return; } } } quint32 i = color_set->getIndexClosestColor(color); QModelIndex index = m_d->model->indexFromId(i); this->selectionModel()->clearSelection(); this->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select); } void KisPaletteView::mouseReleaseEvent(QMouseEvent *event) { bool foreground = false; if (event->button()== Qt::LeftButton) { foreground = true; } entrySelection(foreground); } void KisPaletteView::paletteModelChanged() { updateView(); updateRows(); } void KisPaletteView::setPaletteModel(KisPaletteModel *model) { if (m_d->model) { disconnect(m_d->model, 0, this, 0); } m_d->model = model; setModel(model); paletteModelChanged(); connect(m_d->model, SIGNAL(layoutChanged(QList,QAbstractItemModel::LayoutChangeHint)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(modelReset()), this, SLOT(paletteModelChanged())); } KisPaletteModel* KisPaletteView::paletteModel() const { return m_d->model; } void KisPaletteView::updateRows() { this->clearSpans(); if (m_d->model) { for (int r=0; r<=m_d->model->rowCount(); r++) { QModelIndex index = m_d->model->index(r, 0); - if (qVariantValue(index.data(KisPaletteModel::IsHeaderRole))) { + if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { setSpan(r, 0, 1, m_d->model->columnCount()); setRowHeight(r, this->fontMetrics().lineSpacing()+6); } else { this->setRowHeight(r, this->columnWidth(0)); } } } } void KisPaletteView::setAllowModification(bool allow) { m_d->allowPaletteModification = allow; } void KisPaletteView::wheelEvent(QWheelEvent *event) { if (event->modifiers() & Qt::ControlModifier) { int numDegrees = event->delta() / 8; int numSteps = numDegrees / 7; int curSize = horizontalHeader()->sectionSize(0); int setSize = numSteps + curSize; if ( (event->delta() <= 0) && (setSize <= 8) ) { // Ignore scroll-zooming down below a certain size } else { horizontalHeader()->setDefaultSectionSize(setSize); verticalHeader()->setDefaultSectionSize(setSize); KisConfig cfg; cfg.setPaletteDockerPaletteViewSectionSize(setSize); } event->accept(); } else { KoTableView::wheelEvent(event); } } void KisPaletteView::entrySelection(bool foreground) { QModelIndex index; if (selectedIndexes().size()<=0) { return; } if (selectedIndexes().last().isValid()) { index = selectedIndexes().last(); } else if (selectedIndexes().first().isValid()) { index = selectedIndexes().first(); } else { return; } - if (qVariantValue(index.data(KisPaletteModel::IsHeaderRole))==false) { + if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))==false) { KoColorSetEntry entry = m_d->model->colorSetEntryFromIndex(index); if (foreground) { emit(entrySelected(entry)); emit(indexEntrySelected(index)); } else { emit(entrySelectedBackGround(entry)); emit(indexEntrySelected(index)); } } } void KisPaletteView::modifyEntry(QModelIndex index) { if (m_d->allowPaletteModification) { KoDialog *group = new KoDialog(); QFormLayout *editableItems = new QFormLayout(); group->mainWidget()->setLayout(editableItems); QLineEdit *lnIDName = new QLineEdit(); QLineEdit *lnGroupName = new QLineEdit(); KisColorButton *bnColor = new KisColorButton(); QCheckBox *chkSpot = new QCheckBox(); - if (qVariantValue(index.data(KisPaletteModel::IsHeaderRole))) { - QString groupName = qVariantValue(index.data(Qt::DisplayRole)); + if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { + QString groupName = qvariant_cast(index.data(Qt::DisplayRole)); editableItems->addRow(i18nc("Name for a colorgroup","Name"), lnGroupName); lnGroupName->setText(groupName); if (group->exec() == KoDialog::Accepted) { m_d->model->colorSet()->changeGroupName(groupName, lnGroupName->text()); m_d->model->colorSet()->save(); updateRows(); } //rename the group. } else { KoColorSetEntry entry = m_d->model->colorSetEntryFromIndex(index); - QStringList entryList = qVariantValue(index.data(KisPaletteModel::RetrieveEntryRole)); + QStringList entryList = qvariant_cast(index.data(KisPaletteModel::RetrieveEntryRole)); chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); editableItems->addRow(i18n("ID"), lnIDName); editableItems->addRow(i18n("Name"), lnGroupName); editableItems->addRow(i18n("Color"), bnColor); editableItems->addRow(i18n("Spot"), chkSpot); lnGroupName->setText(entry.name); lnIDName->setText(entry.id); bnColor->setColor(entry.color); chkSpot->setChecked(entry.spotColor); if (group->exec() == KoDialog::Accepted) { entry.name = lnGroupName->text(); entry.id = lnIDName->text(); entry.color = bnColor->color(); entry.spotColor = chkSpot->isChecked(); m_d->model->colorSet()->changeColorSetEntry(entry, entryList.at(0), entryList.at(1).toUInt()); m_d->model->colorSet()->save(); } } } } diff --git a/libs/ui/kis_popup_palette.cpp b/libs/ui/kis_popup_palette.cpp index a17f76859f..a920ed6f8e 100644 --- a/libs/ui/kis_popup_palette.cpp +++ b/libs/ui/kis_popup_palette.cpp @@ -1,921 +1,921 @@ /* This file is part of the KDE project Copyright 2009 Vera Lukman Copyright 2011 Sven Langkamp Copyright 2016 Scott Petrovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_config.h" #include "kis_popup_palette.h" #include "kis_paintop_box.h" #include "kis_favorite_resource_manager.h" #include "kis_icon_utils.h" #include #include "kis_resource_server_provider.h" #include #include #include #include #include "KoColorSpaceRegistry.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_signal_compressor.h" #include #include "brushhud/kis_brush_hud.h" #include "brushhud/kis_round_hud_button.h" #include class PopupColorTriangle : public KoTriangleColorSelector { public: PopupColorTriangle(const KoColorDisplayRendererInterface *displayRenderer, QWidget* parent) : KoTriangleColorSelector(displayRenderer, parent) , m_dragging(false) { } ~PopupColorTriangle() override {} void tabletEvent(QTabletEvent* event) override { event->accept(); QMouseEvent* mouseEvent = 0; switch (event->type()) { case QEvent::TabletPress: mouseEvent = new QMouseEvent(QEvent::MouseButtonPress, event->pos(), Qt::LeftButton, Qt::LeftButton, event->modifiers()); m_dragging = true; mousePressEvent(mouseEvent); break; case QEvent::TabletMove: mouseEvent = new QMouseEvent(QEvent::MouseMove, event->pos(), (m_dragging) ? Qt::LeftButton : Qt::NoButton, (m_dragging) ? Qt::LeftButton : Qt::NoButton, event->modifiers()); mouseMoveEvent(mouseEvent); break; case QEvent::TabletRelease: mouseEvent = new QMouseEvent(QEvent::MouseButtonRelease, event->pos(), Qt::LeftButton, Qt::LeftButton, event->modifiers()); m_dragging = false; mouseReleaseEvent(mouseEvent); break; default: break; } delete mouseEvent; } private: bool m_dragging; }; KisPopupPalette::KisPopupPalette(KisViewManager* viewManager, KisCoordinatesConverter* coordinatesConverter ,KisFavoriteResourceManager* manager, const KoColorDisplayRendererInterface *displayRenderer, KisCanvasResourceProvider *provider, QWidget *parent) : QWidget(parent, Qt::FramelessWindowHint) , m_hoveredPreset(0) , m_hoveredColor(0) , m_selectedColor(0) , m_coordinatesConverter(coordinatesConverter) , m_actionManager(viewManager->actionManager()) , m_resourceManager(manager) , m_triangleColorSelector(0) , m_displayRenderer(displayRenderer) , m_colorChangeCompressor(new KisSignalCompressor(50, KisSignalCompressor::POSTPONE)) , m_actionCollection(viewManager->actionCollection()) , m_brushHud(0) , m_popupPaletteSize(385.0) , m_colorHistoryInnerRadius(72.0) , m_colorHistoryOuterRadius(92.0) , m_isOverCanvasRotationIndicator(false) , m_isRotatingCanvasIndicator(false) { // some UI controls are defined and created based off these variables const int borderWidth = 3; if (KisConfig().readEntry("popuppalette/usevisualcolorselector", false)) { m_triangleColorSelector = new KisVisualColorSelector(this); } else { m_triangleColorSelector = new PopupColorTriangle(displayRenderer, this); } m_triangleColorSelector->setDisplayRenderer(displayRenderer); m_triangleColorSelector->setConfig(true,false); m_triangleColorSelector->move(m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth, m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth); m_triangleColorSelector->resize(m_colorHistoryInnerRadius*2-borderWidth*2, m_colorHistoryInnerRadius*2-borderWidth*2); m_triangleColorSelector->setVisible(true); KoColor fgcolor(Qt::black, KoColorSpaceRegistry::instance()->rgb8()); if (m_resourceManager) { fgcolor = provider->fgColor(); } m_triangleColorSelector->slotSetColor(fgcolor); QRegion maskedRegion(0, 0, m_triangleColorSelector->width(), m_triangleColorSelector->height(), QRegion::Ellipse ); m_triangleColorSelector->setMask(maskedRegion); //setAttribute(Qt::WA_TranslucentBackground, true); connect(m_triangleColorSelector, SIGNAL(sigNewColor(const KoColor &)), m_colorChangeCompressor.data(), SLOT(start())); connect(m_colorChangeCompressor.data(), SIGNAL(timeout()), SLOT(slotEmitColorChanged())); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), m_triangleColorSelector, SLOT(configurationChanged())); connect(m_resourceManager, SIGNAL(sigChangeFGColorSelector(KoColor)), SLOT(slotExternalFgColorChanged(KoColor))); connect(this, SIGNAL(sigChangefGColor(KoColor)), m_resourceManager, SIGNAL(sigSetFGColor(KoColor))); connect(this, SIGNAL(sigChangeActivePaintop(int)), m_resourceManager, SLOT(slotChangeActivePaintop(int))); connect(this, SIGNAL(sigUpdateRecentColor(int)), m_resourceManager, SLOT(slotUpdateRecentColor(int))); connect(m_resourceManager, SIGNAL(setSelectedColor(int)), SLOT(slotSetSelectedColor(int))); connect(m_resourceManager, SIGNAL(updatePalettes()), SLOT(slotUpdate())); connect(m_resourceManager, SIGNAL(hidePalettes()), SLOT(slotHide())); // This is used to handle a bug: // If pop up palette is visible and a new colour is selected, the new colour // will be added when the user clicks on the canvas to hide the palette // In general, we want to be able to store recent color if the pop up palette // is not visible m_timer.setSingleShot(true); connect(this, SIGNAL(sigTriggerTimer()), this, SLOT(slotTriggerTimer())); connect(&m_timer, SIGNAL(timeout()), this, SLOT(slotEnableChangeFGColor())); connect(this, SIGNAL(sigEnableChangeFGColor(bool)), m_resourceManager, SIGNAL(sigEnableChangeColor(bool))); setCursor(Qt::ArrowCursor); setMouseTracking(true); setHoveredPreset(-1); setHoveredColor(-1); setSelectedColor(-1); m_brushHud = new KisBrushHud(provider, parent); m_brushHud->setMaximumHeight(m_popupPaletteSize); m_brushHud->setVisible(false); const int auxButtonSize = 35; m_settingsButton = new KisRoundHudButton(this); m_settingsButton->setIcon(KisIconUtils::loadIcon("configure")); m_settingsButton->setGeometry(m_popupPaletteSize - 2.2 * auxButtonSize, m_popupPaletteSize - auxButtonSize, auxButtonSize, auxButtonSize); connect(m_settingsButton, SIGNAL(clicked()), SLOT(slotShowTagsPopup())); KisConfig cfg; m_brushHudButton = new KisRoundHudButton(this); m_brushHudButton->setCheckable(true); m_brushHudButton->setOnOffIcons(KisIconUtils::loadIcon("arrow-left"), KisIconUtils::loadIcon("arrow-right")); m_brushHudButton->setGeometry(m_popupPaletteSize - 1.0 * auxButtonSize, m_popupPaletteSize - auxButtonSize, auxButtonSize, auxButtonSize); connect(m_brushHudButton, SIGNAL(toggled(bool)), SLOT(showHudWidget(bool))); m_brushHudButton->setChecked(cfg.showBrushHud()); // add some stuff below the pop-up palette that will make it easier to use for tablet people QVBoxLayout* vLayout = new QVBoxLayout(this); // main layout QSpacerItem* verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding); vLayout->addSpacerItem(verticalSpacer); // this should push the box to the bottom QHBoxLayout* hLayout = new QHBoxLayout(); vLayout->addLayout(hLayout); mirrorMode = new KisHighlightedToolButton(this); mirrorMode->setCheckable(true); mirrorMode->setFixedSize(35, 35); mirrorMode->setIcon(KisIconUtils::loadIcon("symmetry-horizontal")); mirrorMode->setToolTip(i18n("Mirror Canvas")); connect(mirrorMode, SIGNAL(clicked(bool)), this, SLOT(slotmirroModeClicked())); canvasOnlyButton = new KisHighlightedToolButton(this); canvasOnlyButton->setCheckable(true); canvasOnlyButton->setFixedSize(35, 35); canvasOnlyButton->setIcon(KisIconUtils::loadIcon("document-new")); canvasOnlyButton->setToolTip(i18n("Canvas Only")); connect(canvasOnlyButton, SIGNAL(clicked(bool)), this, SLOT(slotCanvasonlyModeClicked())); zoomToOneHundredPercentButton = new QPushButton(this); zoomToOneHundredPercentButton->setText(i18n("100%")); zoomToOneHundredPercentButton->setFixedHeight(35); zoomToOneHundredPercentButton->setIcon(KisIconUtils::loadIcon("zoom-original")); zoomToOneHundredPercentButton->setToolTip(i18n("Zoom to 100%")); connect(zoomToOneHundredPercentButton, SIGNAL(clicked(bool)), this, SLOT(slotZoomToOneHundredPercentClicked())); zoomCanvasSlider = new QSlider(Qt::Horizontal, this); zoomSliderMinValue = 10; // set in % zoomSliderMaxValue = 200; // set in % zoomCanvasSlider->setRange(zoomSliderMinValue, zoomSliderMaxValue); zoomCanvasSlider->setFixedHeight(35); zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent()); zoomCanvasSlider->setSingleStep(1); zoomCanvasSlider->setPageStep(1); connect(zoomCanvasSlider, SIGNAL(valueChanged(int)), this, SLOT(slotZoomSliderChanged(int))); hLayout->addWidget(mirrorMode); hLayout->addWidget(canvasOnlyButton); hLayout->addWidget(zoomToOneHundredPercentButton); hLayout->addWidget(zoomCanvasSlider); setVisible(true); setVisible(false); // Prevent tablet events from being captured by the canvas setAttribute(Qt::WA_NoMousePropagation, true); } void KisPopupPalette::slotExternalFgColorChanged(const KoColor &color) { //hack to get around cmyk for now. if (color.colorSpace()->colorChannelCount()>3) { KoColor c(KoColorSpaceRegistry::instance()->rgb8()); c.fromKoColor(color); m_triangleColorSelector->slotSetColor(c); } else { m_triangleColorSelector->slotSetColor(color); } } void KisPopupPalette::slotEmitColorChanged() { if (isVisible()) { update(); emit sigChangefGColor(m_triangleColorSelector->getCurrentColor()); } } //setting KisPopupPalette properties int KisPopupPalette::hoveredPreset() const { return m_hoveredPreset; } void KisPopupPalette::setHoveredPreset(int x) { m_hoveredPreset = x; } int KisPopupPalette::hoveredColor() const { return m_hoveredColor; } void KisPopupPalette::setHoveredColor(int x) { m_hoveredColor = x; } int KisPopupPalette::selectedColor() const { return m_selectedColor; } void KisPopupPalette::setSelectedColor(int x) { m_selectedColor = x; } void KisPopupPalette::slotTriggerTimer() { m_timer.start(750); } void KisPopupPalette::slotEnableChangeFGColor() { emit sigEnableChangeFGColor(true); } void KisPopupPalette::slotZoomSliderChanged(int zoom) { emit zoomLevelChanged(zoom); } void KisPopupPalette::adjustLayout(const QPoint &p) { KIS_ASSERT_RECOVER_RETURN(m_brushHud); if (isVisible() && parentWidget()) { float hudMargin = 30.0; const QRect fitRect = kisGrowRect(parentWidget()->rect(), -20.0); // -20 is widget margin const QPoint paletteCenterOffset(m_popupPaletteSize / 2, m_popupPaletteSize / 2); QRect paletteRect = rect(); paletteRect.moveTo(p - paletteCenterOffset); if (m_brushHudButton->isChecked()) { m_brushHud->updateGeometry(); paletteRect.adjust(0, 0, m_brushHud->width() + hudMargin, 0); } paletteRect = kisEnsureInRect(paletteRect, fitRect); move(paletteRect.topLeft()); m_brushHud->move(paletteRect.topLeft() + QPoint(m_popupPaletteSize + hudMargin, 0)); m_lastCenterPoint = p; } } void KisPopupPalette::showHudWidget(bool visible) { KIS_ASSERT_RECOVER_RETURN(m_brushHud); const bool reallyVisible = visible && m_brushHudButton->isChecked(); if (reallyVisible) { m_brushHud->updateProperties(); } m_brushHud->setVisible(reallyVisible); adjustLayout(m_lastCenterPoint); KisConfig cfg; cfg.setShowBrushHud(visible); } void KisPopupPalette::showPopupPalette(const QPoint &p) { showPopupPalette(!isVisible()); adjustLayout(p); } void KisPopupPalette::showPopupPalette(bool show) { if (show) { // don't set the zoom slider if we are outside of the zoom slider bounds. It will change the zoom level to within // the bounds and cause the canvas to jump between the slider's min and max if (m_coordinatesConverter->zoomInPercent() > zoomSliderMinValue && m_coordinatesConverter->zoomInPercent() < zoomSliderMaxValue ){ zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent()); // sync the zoom slider } emit sigEnableChangeFGColor(!show); } else { emit sigTriggerTimer(); } setVisible(show); m_brushHud->setVisible(show && m_brushHudButton->isChecked()); } //redefinition of setVariable function to change the scope to private void KisPopupPalette::setVisible(bool b) { QWidget::setVisible(b); } void KisPopupPalette::setParent(QWidget *parent) { m_brushHud->setParent(parent); QWidget::setParent(parent); } QSize KisPopupPalette::sizeHint() const { return QSize(m_popupPaletteSize, m_popupPaletteSize + 50); // last number is the space for the toolbar below } void KisPopupPalette::resizeEvent(QResizeEvent*) { } void KisPopupPalette::paintEvent(QPaintEvent* e) { Q_UNUSED(e); QPainter painter(this); QPen pen(palette().color(QPalette::Text)); pen.setWidth(3); painter.setPen(pen); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); //painting background color indicator QPainterPath bgColor; bgColor.addEllipse(QPoint( 50, 80), 30, 30); painter.fillPath(bgColor, m_displayRenderer->toQColor(m_resourceManager->bgColor())); painter.drawPath(bgColor); //painting foreground color indicator QPainterPath fgColor; fgColor.addEllipse(QPoint( 60, 50), 30, 30); painter.fillPath(fgColor, m_displayRenderer->toQColor(m_triangleColorSelector->getCurrentColor())); painter.drawPath(fgColor); // create a circle background that everything else will go into QPainterPath backgroundContainer; float shrinkCircleAmount = 3;// helps the circle when the stroke is put around it QRectF circleRect(shrinkCircleAmount, shrinkCircleAmount, m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2); backgroundContainer.addEllipse( circleRect ); painter.fillPath(backgroundContainer,palette().brush(QPalette::Background)); painter.drawPath(backgroundContainer); // create a path slightly inside the container circle. this will create a 'track' to indicate that we can rotate the canvas // with the indicator QPainterPath rotationTrackPath; shrinkCircleAmount = 18; QRectF circleRect2(shrinkCircleAmount, shrinkCircleAmount, m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2); rotationTrackPath.addEllipse( circleRect2 ); pen.setWidth(1); painter.setPen(pen); painter.drawPath(rotationTrackPath); // this thing will help indicate where the starting brush preset is at. // also what direction they go to give sor order to the presets populated /* pen.setWidth(6); pen.setCapStyle(Qt::RoundCap); painter.setPen(pen); painter.drawArc(circleRect, (16*90), (16*-30)); // span angle (last parameter) is in 16th of degrees QPainterPath brushDir; brushDir.arcMoveTo(circleRect, 60); brushDir.lineTo(brushDir.currentPosition().x()-5, brushDir.currentPosition().y() - 14); painter.drawPath(brushDir); brushDir.lineTo(brushDir.currentPosition().x()-2, brushDir.currentPosition().y() + 6); painter.drawPath(brushDir); */ // the following things needs to be based off the center, so let's translate the painter painter.translate(m_popupPaletteSize / 2, m_popupPaletteSize / 2); // create the canvas rotation handle QPainterPath rotationIndicator = drawRotationIndicator(m_coordinatesConverter->rotationAngle(), true); painter.fillPath(rotationIndicator,palette().brush(QPalette::Text)); // hover indicator for the canvas rotation if (m_isOverCanvasRotationIndicator == true) { painter.save(); QPen pen(palette().color(QPalette::Highlight)); pen.setWidth(2); painter.setPen(pen); painter.drawPath(rotationIndicator); painter.restore(); } // create a reset canvas rotation indicator to bring the canvas back to 0 degrees QPainterPath resetRotationIndicator = drawRotationIndicator(0, false); QPen resetPen(palette().color(QPalette::Text)); resetPen.setWidth(1); painter.save(); painter.setPen(resetPen); painter.drawPath(resetRotationIndicator); painter.restore(); //painting favorite brushes QList images(m_resourceManager->favoritePresetImages()); //painting favorite brushes pixmap/icon QPainterPath presetPath; for (int pos = 0; pos < numSlots(); pos++) { painter.save(); presetPath = createPathFromPresetIndex(pos); if (pos < images.size()) { painter.setClipPath(presetPath); QRect bounds = presetPath.boundingRect().toAlignedRect(); painter.drawImage(bounds.topLeft() , images.at(pos).scaled(bounds.size() , Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); } else { painter.fillPath(presetPath, palette().brush(QPalette::Window)); // brush slot that has no brush in it } QPen pen = painter.pen(); pen.setWidth(1); painter.setPen(pen); painter.drawPath(presetPath); painter.restore(); } if (hoveredPreset() > -1) { presetPath = createPathFromPresetIndex(hoveredPreset()); QPen pen(palette().color(QPalette::Highlight)); pen.setWidth(3); painter.setPen(pen); painter.drawPath(presetPath); } // paint recent colors area. painter.setPen(Qt::NoPen); float rotationAngle = -360.0 / m_resourceManager->recentColorsTotal(); // there might be no recent colors at the start, so paint a placeholder if (m_resourceManager->recentColorsTotal() == 0) { painter.setBrush(Qt::transparent); QPainterPath emptyRecentColorsPath(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.setPen(QPen(palette().color(QPalette::Background).lighter(150), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); painter.drawPath(emptyRecentColorsPath); } else { for (int pos = 0; pos < m_resourceManager->recentColorsTotal(); pos++) { QPainterPath recentColorsPath(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); //accessing recent color of index pos painter.fillPath(recentColorsPath, m_displayRenderer->toQColor( m_resourceManager->recentColorAt(pos) )); painter.drawPath(recentColorsPath); painter.rotate(rotationAngle); } } //painting hovered color if (hoveredColor() > -1) { painter.setPen(QPen(palette().color(QPalette::Highlight), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); if (m_resourceManager->recentColorsTotal() == 1) { QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.drawPath(path_ColorDonut); } else { painter.rotate((m_resourceManager->recentColorsTotal() + hoveredColor()) *rotationAngle); QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); painter.drawPath(path); painter.rotate(hoveredColor() * -1 * rotationAngle); } } //painting selected color if (selectedColor() > -1) { painter.setPen(QPen(palette().color(QPalette::Highlight).darker(130), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); if (m_resourceManager->recentColorsTotal() == 1) { QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.drawPath(path_ColorDonut); } else { painter.rotate((m_resourceManager->recentColorsTotal() + selectedColor()) *rotationAngle); QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); painter.drawPath(path); painter.rotate(selectedColor() * -1 * rotationAngle); } } } QPainterPath KisPopupPalette::drawDonutPathFull(int x, int y, int inner_radius, int outer_radius) { QPainterPath path; path.addEllipse(QPointF(x, y), outer_radius, outer_radius); path.addEllipse(QPointF(x, y), inner_radius, inner_radius); path.setFillRule(Qt::OddEvenFill); return path; } QPainterPath KisPopupPalette::drawDonutPathAngle(int inner_radius, int outer_radius, int limit) { QPainterPath path; path.moveTo(-0.999 * outer_radius * sin(M_PI / limit), 0.999 * outer_radius * cos(M_PI / limit)); path.arcTo(-1 * outer_radius, -1 * outer_radius, 2 * outer_radius, 2 * outer_radius, -90.0 - 180.0 / limit, 360.0 / limit); path.arcTo(-1 * inner_radius, -1 * inner_radius, 2 * inner_radius, 2 * inner_radius, -90.0 + 180.0 / limit, - 360.0 / limit); path.closeSubpath(); return path; } QPainterPath KisPopupPalette::drawRotationIndicator(qreal rotationAngle, bool canDrag) { // used for canvas rotation. This function gets called twice. Once by the canvas rotation indicator, // and another time by the reset canvas position float canvasRotationRadians = qDegreesToRadians(rotationAngle - 90); // -90 will make 0 degrees be at the top float rotationDialXPosition = qCos(canvasRotationRadians) * (m_popupPaletteSize/2 - 10); // m_popupPaletteSize/2 = radius float rotationDialYPosition = qSin(canvasRotationRadians) * (m_popupPaletteSize/2 - 10); QPainterPath canvasRotationIndicator; int canvasIndicatorSize = 15; float canvasIndicatorMiddle = canvasIndicatorSize/2; QRect indicatorRectangle = QRect( rotationDialXPosition - canvasIndicatorMiddle, rotationDialYPosition - canvasIndicatorMiddle, canvasIndicatorSize, canvasIndicatorSize ); if (canDrag) { m_canvasRotationIndicatorRect = indicatorRectangle; } else { m_resetCanvasRotationIndicatorRect = indicatorRectangle; } canvasRotationIndicator.addEllipse(indicatorRectangle.x(), indicatorRectangle.y(), indicatorRectangle.width(), indicatorRectangle.height() ); return canvasRotationIndicator; } void KisPopupPalette::mouseMoveEvent(QMouseEvent* event) { - QPointF point = event->posF(); + QPointF point = event->localPos(); event->accept(); setToolTip(QString()); setHoveredPreset(-1); setHoveredColor(-1); // calculate if we are over the canvas rotation knob // before we started painting, we moved the painter to the center of the widget, so the X/Y positions are offset. we need to // correct them first before looking for a click event intersection float rotationCorrectedXPos = m_canvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2); float rotationCorrectedYPos = m_canvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2); QRect correctedCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos, m_canvasRotationIndicatorRect.width(), m_canvasRotationIndicatorRect.height()); if (correctedCanvasRotationIndicator.contains(point.x(), point.y())) { m_isOverCanvasRotationIndicator = true; } else { m_isOverCanvasRotationIndicator = false; } if (m_isRotatingCanvasIndicator) { // we are rotating the canvas, so calculate the rotation angle based off the center // calculate the angle we are at first QPoint widgetCenterPoint = QPoint(m_popupPaletteSize/2, m_popupPaletteSize/2); float dX = point.x() - widgetCenterPoint.x(); float dY = point.y() - widgetCenterPoint.y(); float finalAngle = qAtan2(dY,dX) * 180 / M_PI; // what we need if we have two points, but don't know the angle finalAngle = finalAngle + 90; // add 90 degrees so 0 degree position points up float angleDifference = finalAngle - m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs, so find it out m_coordinatesConverter->rotate(m_coordinatesConverter->widgetCenterPoint(), angleDifference); emit sigUpdateCanvas(); } // don't highlight the presets if we are in the middle of rotating the canvas if (m_isRotatingCanvasIndicator == false) { QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); { int pos = calculatePresetIndex(point, m_resourceManager->numFavoritePresets()); if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()) { setToolTip(m_resourceManager->favoritePresetList().at(pos).data()->name()); setHoveredPreset(pos); } } if (pathColor.contains(point)) { int pos = calculateIndex(point, m_resourceManager->recentColorsTotal()); if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) { setHoveredColor(pos); } } } update(); } void KisPopupPalette::mousePressEvent(QMouseEvent* event) { - QPointF point = event->posF(); + QPointF point = event->localPos(); event->accept(); if (event->button() == Qt::LeftButton) { //in favorite brushes area int pos = calculateIndex(point, m_resourceManager->numFavoritePresets()); if (pos >= 0 && pos < m_resourceManager->numFavoritePresets() && isPointInPixmap(point, pos)) { //setSelectedBrush(pos); update(); } if (m_isOverCanvasRotationIndicator) { m_isRotatingCanvasIndicator = true; } // reset the canvas if we are over the reset canvas rotation indicator float rotationCorrectedXPos = m_resetCanvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2); float rotationCorrectedYPos = m_resetCanvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2); QRect correctedResetCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos, m_resetCanvasRotationIndicatorRect.width(), m_resetCanvasRotationIndicatorRect.height()); if (correctedResetCanvasRotationIndicator.contains(point.x(), point.y())) { float angleDifference = -m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs, so find it ou m_coordinatesConverter->rotate(m_coordinatesConverter->widgetCenterPoint(), angleDifference); emit sigUpdateCanvas(); } } } void KisPopupPalette::slotShowTagsPopup() { KisPaintOpPresetResourceServer* rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); QStringList tags = rServer->tagNamesList(); std::sort(tags.begin(), tags.end()); if (!tags.isEmpty()) { QMenu menu; Q_FOREACH (const QString& tag, tags) { menu.addAction(tag); } QAction* action = menu.exec(QCursor::pos()); if (action) { m_resourceManager->setCurrentTag(action->text()); } } else { QWhatsThis::showText(QCursor::pos(), i18n("There are no tags available to show in this popup. To add presets, you need to tag them and then select the tag here.")); } } void KisPopupPalette::slotmirroModeClicked() { QAction* action = m_actionCollection->action("mirror_canvas"); if (action) { action->trigger(); } } void KisPopupPalette::slotCanvasonlyModeClicked() { QAction* action = m_actionCollection->action("view_show_canvas_only"); if (action) { action->trigger(); } } void KisPopupPalette::slotZoomToOneHundredPercentClicked() { QAction* action = m_actionCollection->action("zoom_to_100pct"); if (action) { action->trigger(); } // also move the zoom slider to 100% position so they are in sync zoomCanvasSlider->setValue(100); } void KisPopupPalette::tabletEvent(QTabletEvent* event) { event->ignore(); } void KisPopupPalette::mouseReleaseEvent(QMouseEvent * event) { - QPointF point = event->posF(); + QPointF point = event->localPos(); event->accept(); m_isOverCanvasRotationIndicator = false; m_isRotatingCanvasIndicator = false; if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) { QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); //in favorite brushes area if (hoveredPreset() > -1) { //setSelectedBrush(hoveredBrush()); emit sigChangeActivePaintop(hoveredPreset()); } if (pathColor.contains(point)) { int pos = calculateIndex(point, m_resourceManager->recentColorsTotal()); if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) { emit sigUpdateRecentColor(pos); } } } } int KisPopupPalette::calculateIndex(QPointF point, int n) { calculatePresetIndex(point, n); //translate to (0,0) point.setX(point.x() - m_popupPaletteSize / 2); point.setY(point.y() - m_popupPaletteSize / 2); //rotate float smallerAngle = M_PI / 2 + M_PI / n - atan2(point.y(), point.x()); float radius = sqrt((float)point.x() * point.x() + point.y() * point.y()); point.setX(radius * cos(smallerAngle)); point.setY(radius * sin(smallerAngle)); //calculate brush index int pos = floor(acos(point.x() / radius) * n / (2 * M_PI)); if (point.y() < 0) pos = n - pos - 1; return pos; } bool KisPopupPalette::isPointInPixmap(QPointF& point, int pos) { if (createPathFromPresetIndex(pos).contains(point + QPointF(-m_popupPaletteSize / 2, -m_popupPaletteSize / 2))) { return true; } return false; } KisPopupPalette::~KisPopupPalette() { } QPainterPath KisPopupPalette::createPathFromPresetIndex(int index) { qreal angleSlice = 360.0 / numSlots() ; // how many degrees each slice will get // the starting angle of the slice we need to draw. the negative sign makes us go clockwise. // adding 90 degrees makes us start at the top. otherwise we would start at the right qreal startingAngle = -(index * angleSlice) + 90; // the radius will get smaller as the amount of presets shown increases. 10 slots == 41 qreal presetRadius = m_colorHistoryOuterRadius * qSin(qDegreesToRadians(angleSlice/2)) / (1-qSin(qDegreesToRadians(angleSlice/2))); QPainterPath path; float pathX = (m_colorHistoryOuterRadius + presetRadius) * qCos(qDegreesToRadians(startingAngle)) - presetRadius; float pathY = -(m_colorHistoryOuterRadius + presetRadius) * qSin(qDegreesToRadians(startingAngle)) - presetRadius; float pathDiameter = 2 * presetRadius; // distance is used to calculate the X/Y in addition to the preset circle size path.addEllipse(pathX, pathY, pathDiameter, pathDiameter); return path; } int KisPopupPalette::calculatePresetIndex(QPointF point, int /*n*/) { for(int i = 0; i < numSlots(); i++) { QPointF adujustedPoint = point - QPointF(m_popupPaletteSize/2, m_popupPaletteSize/2); if(createPathFromPresetIndex(i).contains(adujustedPoint)) { return i; } } return -1; } int KisPopupPalette::numSlots() { KisConfig config; return qMax(config.favoritePresets(), 10); } diff --git a/libs/ui/kis_splash_screen.cpp b/libs/ui/kis_splash_screen.cpp index fbdc19283f..d3d30fec16 100644 --- a/libs/ui/kis_splash_screen.cpp +++ b/libs/ui/kis_splash_screen.cpp @@ -1,204 +1,204 @@ /* * Copyright (c) 2014 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_splash_screen.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisSplashScreen::KisSplashScreen(const QString &version, const QPixmap &pixmap, bool themed, QWidget *parent, Qt::WindowFlags f) : QWidget(parent, Qt::SplashScreen | Qt::FramelessWindowHint #ifdef Q_OS_LINUX | Qt::WindowStaysOnTopHint #endif | f), m_themed(themed) { setupUi(this); setWindowIcon(KisIconUtils::loadIcon("calligrakrita")); // Maintain the aspect ratio on high DPI screens when scaling lblSplash->setPixmap(pixmap); setFixedWidth(pixmap.width()); QString color = colorString(); lblVersion->setText(i18n("Version: %1", version)); lblVersion->setStyleSheet("color:" + color); bnClose->hide(); connect(bnClose, SIGNAL(clicked()), this, SLOT(close())); chkShowAtStartup->hide(); connect(chkShowAtStartup, SIGNAL(toggled(bool)), this, SLOT(toggleShowAtStartup(bool))); KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false); chkShowAtStartup->setChecked(hideSplash); connect(lblRecent, SIGNAL(linkActivated(QString)), SLOT(linkClicked(QString))); connect(&m_timer, SIGNAL(timeout()), SLOT(raise())); // hide these labels by default lblLinks->setVisible(false); lblRecent->setVisible(false); line->setVisible(false); m_timer.setSingleShot(true); m_timer.start(10); } void KisSplashScreen::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); updateText(); } void KisSplashScreen::updateText() { QString color = colorString(); KConfigGroup cfg2( KSharedConfig::openConfig(), "RecentFiles"); int i = 1; QString recent = i18n("" "" "" "

Recent Files

", color); QString path; QStringList recentfiles; QFontMetrics metrics(lblRecent->font()); do { path = cfg2.readPathEntry(QString("File%1").arg(i), QString()); if (!path.isEmpty()) { QString name = cfg2.readPathEntry(QString("Name%1").arg(i), QString()); QUrl url(path); if (name.isEmpty()) { name = url.fileName(); } name = metrics.elidedText(name, Qt::ElideMiddle, lblRecent->width()); if (!url.isLocalFile() || QFile::exists(url.toLocalFile())) { recentfiles.insert(0, QString("

%2

").arg(path).arg(name).arg(color)); } } i++; } while (!path.isEmpty() || i <= 8); recent += recentfiles.join("\n"); recent += "" ""; lblRecent->setText(recent); } void KisSplashScreen::displayLinks() { QString color = colorString(); lblLinks->setTextFormat(Qt::RichText); lblLinks->setText(i18n("" "" "" "

Links

" "

Support Krita

" "

Getting Started

" "

Manual

" "

Krita Website

" "

User Community

" "

Source Code

" "

Krita on Steam

" "" "", color)); lblLinks->setVisible(true); updateText(); } void KisSplashScreen::displayRecentFiles() { lblRecent->setVisible(true); line->setVisible(true); } QString KisSplashScreen::colorString() const { QString color = "#FFFFFF"; if (m_themed && qApp->palette().background().color().value() > 100) { color = "#000000"; } return color; } void KisSplashScreen::repaint() { QWidget::repaint(); - QApplication::flush(); + qApp->sendPostedEvents(); } void KisSplashScreen::show() { QRect r(QPoint(), sizeHint()); resize(r.size()); move(QApplication::desktop()->availableGeometry().center() - r.center()); if (isVisible()) { repaint(); } m_timer.setSingleShot(true); m_timer.start(1); QWidget::show(); } void KisSplashScreen::toggleShowAtStartup(bool toggle) { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); cfg.writeEntry("HideSplashAfterStartup", toggle); } void KisSplashScreen::linkClicked(const QString &link) { KisPart::instance()->openExistingFile(QUrl::fromLocalFile(link)); if (isTopLevel()) { close(); } } diff --git a/libs/ui/kisexiv2/kis_exiv2.cpp b/libs/ui/kisexiv2/kis_exiv2.cpp index 98bb847326..b0cfab91c9 100644 --- a/libs/ui/kisexiv2/kis_exiv2.cpp +++ b/libs/ui/kisexiv2/kis_exiv2.cpp @@ -1,290 +1,290 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - + #include "kis_exiv2.h" #include #include "kis_iptc_io.h" #include "kis_exif_io.h" #include "kis_xmp_io.h" #include #include // ---- Generic convertion functions ---- // // Convert an exiv value to a KisMetaData value KisMetaData::Value exivValueToKMDValue(const Exiv2::Value::AutoPtr value, bool forceSeq, KisMetaData::Value::ValueType arrayType) { switch (value->typeId()) { case Exiv2::signedByte: case Exiv2::invalidTypeId: case Exiv2::lastTypeId: case Exiv2::directory: dbgFile << "Invalid value :" << value->typeId() << " value =" << value->toString().c_str(); return KisMetaData::Value(); case Exiv2::undefined: { dbgFile << "Undefined value :" << value->typeId() << " value =" << value->toString().c_str(); QByteArray array(value->count() , 0); value->copy((Exiv2::byte*)array.data(), Exiv2::invalidByteOrder); return KisMetaData::Value(QString(array.toBase64())); } case Exiv2::unsignedByte: case Exiv2::unsignedShort: case Exiv2::unsignedLong: case Exiv2::signedShort: case Exiv2::signedLong: { if (value->count() == 1 && !forceSeq) { return KisMetaData::Value((int)value->toLong()); } else { QList array; for (int i = 0; i < value->count(); i++) array.push_back(KisMetaData::Value((int)value->toLong(i))); return KisMetaData::Value(array, arrayType); } } case Exiv2::asciiString: case Exiv2::string: case Exiv2::comment: // look at kexiv2 for the problem about decoding correctly that tag return KisMetaData::Value(value->toString().c_str()); case Exiv2::unsignedRational: if(value->size() < 2) { dbgFile << "Invalid size :" << value->size() << " value =" << value->toString().c_str(); return KisMetaData::Value(); } return KisMetaData::Value(KisMetaData::Rational(value->toRational().first , value->toRational().second)); case Exiv2::signedRational: if(value->size() < 2) { dbgFile << "Invalid size :" << value->size() << " value =" << value->toString().c_str(); return KisMetaData::Value(); } return KisMetaData::Value(KisMetaData::Rational(value->toRational().first , value->toRational().second)); case Exiv2::date: case Exiv2::time: return KisMetaData::Value(QDateTime::fromString(value->toString().c_str(), Qt::ISODate)); case Exiv2::xmpText: case Exiv2::xmpAlt: case Exiv2::xmpBag: case Exiv2::xmpSeq: case Exiv2::langAlt: default: { dbgFile << "Unknown type id :" << value->typeId() << " value =" << value->toString().c_str(); //Q_ASSERT(false); // This point must never be reached ! return KisMetaData::Value(); } } dbgFile << "Unknown type id :" << value->typeId() << " value =" << value->toString().c_str(); //Q_ASSERT(false); // This point must never be reached ! return KisMetaData::Value(); } // Convert a QtVariant to an Exiv value Exiv2::Value* variantToExivValue(const QVariant& variant, Exiv2::TypeId type) { switch (type) { case Exiv2::undefined: { QByteArray arr = QByteArray::fromBase64(variant.toString().toLatin1()); return new Exiv2::DataValue((Exiv2::byte*)arr.data(), arr.size()); } case Exiv2::unsignedByte: return new Exiv2::ValueType(variant.toUInt(0)); case Exiv2::unsignedShort: return new Exiv2::ValueType(variant.toUInt(0)); case Exiv2::unsignedLong: return new Exiv2::ValueType(variant.toUInt(0)); case Exiv2::signedShort: return new Exiv2::ValueType(variant.toInt(0)); case Exiv2::signedLong: return new Exiv2::ValueType(variant.toInt(0)); case Exiv2::date: { QDate date = variant.toDate(); return new Exiv2::DateValue(date.year(), date.month(), date.day()); } case Exiv2::asciiString: if (variant.type() == QVariant::DateTime) { return new Exiv2::AsciiValue(qPrintable(variant.toDateTime().toString("yyyy:MM:dd hh:mm:ss"))); } else return new Exiv2::AsciiValue(qPrintable(variant.toString())); case Exiv2::string: { if (variant.type() == QVariant::DateTime) { return new Exiv2::StringValue(qPrintable(variant.toDateTime().toString("yyyy:MM:dd hh:mm:ss"))); } else return new Exiv2::StringValue(qPrintable(variant.toString())); } case Exiv2::comment: return new Exiv2::CommentValue(qPrintable(variant.toString())); default: dbgFile << "Unhandled type:" << type; //Q_ASSERT(false); return 0; } } template Exiv2::Value* arrayToExivValue(const KisMetaData::Value& value) { Exiv2::ValueType<_TYPE_>* ev = new Exiv2::ValueType<_TYPE_>(); for (int i = 0; i < value.asArray().size(); ++i) { - ev->value_.push_back(qVariantValue<_TYPE_>(value.asArray()[i].asVariant())); + ev->value_.push_back(qvariant_cast<_TYPE_>(value.asArray()[i].asVariant())); } return ev; } // Convert a KisMetaData to an Exiv value Exiv2::Value* kmdValueToExivValue(const KisMetaData::Value& value, Exiv2::TypeId type) { switch (value.type()) { case KisMetaData::Value::Invalid: return &*Exiv2::Value::create(Exiv2::invalidTypeId); case KisMetaData::Value::Variant: { return variantToExivValue(value.asVariant(), type); } case KisMetaData::Value::Rational: //Q_ASSERT(type == Exiv2::signedRational || type == Exiv2::unsignedRational); if (type == Exiv2::signedRational) { return new Exiv2::ValueType(Exiv2::Rational(value.asRational().numerator, value.asRational().denominator)); } else { return new Exiv2::ValueType(Exiv2::URational(value.asRational().numerator, value.asRational().denominator)); } case KisMetaData::Value::OrderedArray: case KisMetaData::Value::UnorderedArray: case KisMetaData::Value::AlternativeArray: { switch (type) { case Exiv2::unsignedByte: return arrayToExivValue(value); case Exiv2::unsignedShort: return arrayToExivValue(value); case Exiv2::unsignedLong: return arrayToExivValue(value); case Exiv2::signedShort: return arrayToExivValue(value); case Exiv2::signedLong: return arrayToExivValue(value); case Exiv2::string: { Exiv2::StringValue* ev = new Exiv2::StringValue(); for (int i = 0; i < value.asArray().size(); ++i) { - ev->value_ += qVariantValue(value.asArray()[i].asVariant()).toLatin1().constData(); + ev->value_ += qvariant_cast(value.asArray()[i].asVariant()).toLatin1().constData(); if (i != value.asArray().size() - 1) ev->value_ += ','; } return ev; } default: dbgFile << type << " " << value; //Q_ASSERT(false); } } default: dbgFile << type << " " << value; //Q_ASSERT(false); } return 0; } Exiv2::Value* kmdValueToExivXmpValue(const KisMetaData::Value& value) { //Q_ASSERT(value.type() != KisMetaData::Value::Structure); switch (value.type()) { case KisMetaData::Value::Invalid: return new Exiv2::DataValue(Exiv2::invalidTypeId); case KisMetaData::Value::Variant: { QVariant var = value.asVariant(); if (var.type() == QVariant::Bool) { if (var.toBool()) { return new Exiv2::XmpTextValue("True"); } else { return new Exiv2::XmpTextValue("False"); } } else { //Q_ASSERT(var.canConvert(QVariant::String)); return new Exiv2::XmpTextValue(var.toString().toLatin1().constData()); } } case KisMetaData::Value::Rational: { QString rat = "%1 / %2"; rat = rat.arg(value.asRational().numerator); rat = rat.arg(value.asRational().denominator); return new Exiv2::XmpTextValue(rat.toLatin1().constData()); } case KisMetaData::Value::AlternativeArray: case KisMetaData::Value::OrderedArray: case KisMetaData::Value::UnorderedArray: { Exiv2::XmpArrayValue* arrV = new Exiv2::XmpArrayValue; switch (value.type()) { case KisMetaData::Value::OrderedArray: arrV->setXmpArrayType(Exiv2::XmpValue::xaSeq); break; case KisMetaData::Value::UnorderedArray: arrV->setXmpArrayType(Exiv2::XmpValue::xaBag); break; case KisMetaData::Value::AlternativeArray: arrV->setXmpArrayType(Exiv2::XmpValue::xaAlt); break; default: // Cannot happen ; } Q_FOREACH (const KisMetaData::Value& v, value.asArray()) { Exiv2::Value* ev = kmdValueToExivXmpValue(v); if (ev) { arrV->read(ev->toString()); delete ev; } } return arrV; } case KisMetaData::Value::LangArray: { Exiv2::Value* arrV = new Exiv2::LangAltValue; QMap langArray = value.asLangArray(); for (QMap::iterator it = langArray.begin(); it != langArray.end(); ++it) { QString exivVal; if (it.key() != "x-default") { exivVal = "lang=" + it.key() + ' '; } //Q_ASSERT(it.value().type() == KisMetaData::Value::Variant); QVariant var = it.value().asVariant(); //Q_ASSERT(var.type() == QVariant::String); exivVal += var.toString(); arrV->read(exivVal.toLatin1().constData()); } return arrV; } case KisMetaData::Value::Structure: default: { warnKrita << "KisExiv2: Unhandled value type"; return 0; } } warnKrita << "KisExiv2: Unhandled value type"; return 0; } void KisExiv2::initialize() { // XXX_EXIV: make the exiv io backends real plugins KisMetaData::IOBackendRegistry* ioreg = KisMetaData::IOBackendRegistry::instance(); ioreg->add(new KisIptcIO); ioreg->add(new KisExifIO); ioreg->add(new KisXMPIO); } diff --git a/libs/ui/opengl/kis_opengl.cpp b/libs/ui/opengl/kis_opengl.cpp index 0c395031f2..41aa35db36 100644 --- a/libs/ui/opengl/kis_opengl.cpp +++ b/libs/ui/opengl/kis_opengl.cpp @@ -1,600 +1,600 @@ /* * Copyright (c) 2007 Adrian Page * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "opengl/kis_opengl.h" #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #ifndef GL_RENDERER # define GL_RENDERER 0x1F01 #endif namespace { bool defaultFormatIsSet = false; bool isDebugEnabled = false; bool isDebugSynchronous = false; struct OpenGLCheckResult { // bool contextValid = false; int glMajorVersion = 0; int glMinorVersion = 0; bool supportsDeprecatedFunctions = false; bool isOpenGLES = false; QString rendererString; QString driverVersionString; OpenGLCheckResult(QOpenGLContext &context) { if (!context.isValid()) { return; } QOpenGLFunctions *funcs = context.functions(); // funcs is ready to be used rendererString = QString(reinterpret_cast(funcs->glGetString(GL_RENDERER))); driverVersionString = QString(reinterpret_cast(funcs->glGetString(GL_VERSION))); glMajorVersion = context.format().majorVersion(); glMinorVersion = context.format().minorVersion(); supportsDeprecatedFunctions = (context.format().options() & QSurfaceFormat::DeprecatedFunctions); isOpenGLES = context.isOpenGLES(); } bool isSupportedVersion() const { return #ifdef Q_OS_OSX ((glMajorVersion * 100 + glMinorVersion) >= 302) #else (glMajorVersion >= 3 && (supportsDeprecatedFunctions || isOpenGLES)) || ((glMajorVersion * 100 + glMinorVersion) == 201) #endif ; } bool supportsLoD() const { return (glMajorVersion * 100 + glMinorVersion) >= 300; } bool hasOpenGL3() const { return (glMajorVersion * 100 + glMinorVersion) >= 302; } bool supportsFenceSync() const { return glMajorVersion >= 3; } #ifdef Q_OS_WIN // This is only for detecting whether ANGLE is being used. // For detecting generic OpenGL ES please check isOpenGLES bool isUsingAngle() const { return rendererString.startsWith("ANGLE", Qt::CaseInsensitive); } #endif }; boost::optional openGLCheckResult; bool NeedsFenceWorkaround = false; bool NeedsPixmapCacheWorkaround = false; QString debugText("OpenGL Info\n **OpenGL not initialized**"); void openglOnMessageLogged(const QOpenGLDebugMessage& debugMessage) { qDebug() << "OpenGL:" << debugMessage; } } #ifdef Q_OS_WIN namespace { struct WindowsOpenGLStatus { bool supportsDesktopGL = false; bool supportsAngleD3D11 = false; bool isQtPreferAngle = false; bool overridePreferAngle = false; // override Qt to force ANGLE to be preferred }; WindowsOpenGLStatus windowsOpenGLStatus = {}; KisOpenGL::OpenGLRenderer userRendererConfig; KisOpenGL::OpenGLRenderer nextUserRendererConfig; KisOpenGL::OpenGLRenderer currentRenderer; QStringList qpaDetectionLog; boost::optional checkQpaOpenGLStatus() { QWindow surface; surface.setSurfaceType(QSurface::OpenGLSurface); surface.create(); QOpenGLContext context; if (!context.create()) { qDebug() << "OpenGL context cannot be created"; return boost::none; } if (!context.isValid()) { qDebug() << "OpenGL context is not valid while checking Qt's OpenGL status"; return boost::none; } if (!context.makeCurrent(&surface)) { qDebug() << "OpenGL context cannot be made current"; return boost::none; } return OpenGLCheckResult(context); } bool checkIsSupportedDesktopGL(const OpenGLCheckResult &checkResult) { if (checkResult.isUsingAngle()) { qWarning() << "ANGLE was being used when desktop OpenGL was wanted, assuming no desktop OpenGL support"; return false; } if (checkResult.isOpenGLES) { qWarning() << "Got OpenGL ES instead of desktop OpenGL, this shouldn't happen!"; return false; } return checkResult.isSupportedVersion(); } bool checkIsSupportedAngleD3D11(const OpenGLCheckResult &checkResult) { if (!checkResult.isUsingAngle()) { qWarning() << "Desktop OpenGL was being used when ANGLE was wanted, assuming no ANGLE support"; return false; } if (!checkResult.isOpenGLES) { qWarning() << "Got desktop OpenGL instead of OpenGL ES, this shouldn't happen!"; return false; } // HACK: Block ANGLE with Direct3D9 // Direct3D9 does not give OpenGL ES 3.0 // Some versions of ANGLE returns OpenGL version 3.0 incorrectly if (checkResult.rendererString.contains("Direct3D9", Qt::CaseInsensitive)) { qWarning() << "ANGLE tried to use Direct3D9, Krita won't work with it"; return false; } return checkResult.isSupportedVersion(); } void specialOpenGLVendorFilter(WindowsOpenGLStatus &status, const OpenGLCheckResult &checkResult) { if (!status.supportsAngleD3D11) { return; } // HACK: Make ANGLE the preferred renderer for Intel driver versions // between build 4636 and 4729 (exclusive) due to an UI offset bug. // See https://communities.intel.com/thread/116003 // (Build 4636 is known to work from some test results) if (checkResult.rendererString.startsWith("Intel")) { QRegularExpression regex("\\b\\d{2}\\.\\d{2}\\.\\d{2}\\.(\\d{4})\\b"); QRegularExpressionMatch match = regex.match(checkResult.driverVersionString); if (match.hasMatch()) { int driverBuild = match.captured(1).toInt(); if (driverBuild > 4636 && driverBuild < 4729) { qDebug() << "Detected Intel driver build between 4636 and 4729, making ANGLE the preferred renderer"; status.overridePreferAngle = true; } } } } } // namespace /** * This function probes the Qt Platform Abstraction (QPA) for OpenGL diagnostics * information. The code works under the assumption that the bundled Qt is built * with `-opengl dynamic` and includes support for ANGLE. * * This function is written for Qt 5.9.1. On other versions it might not work * as well. */ void KisOpenGL::probeWindowsQpaOpenGL(int argc, char **argv, QString userRendererConfigString) { KIS_SAFE_ASSERT_RECOVER(defaultFormatIsSet) { qWarning() << "Default OpenGL format was not set before calling KisOpenGL::probeWindowsQpaOpenGL. This might be a BUG!"; setDefaultFormat(); } // Clear env var to prevent affecting tests qunsetenv("QT_OPENGL"); boost::optional qpaDetectionResult; qDebug() << "Probing Qt OpenGL detection:"; { KisLoggingManager::ScopedLogCapturer logCapturer( "qt.qpa.gl", [](QtMsgType type, const QMessageLogContext &context, const QString &msg) { Q_UNUSED(type) Q_UNUSED(context) qpaDetectionLog.append(msg); } ); { QGuiApplication app(argc, argv); qpaDetectionResult = checkQpaOpenGLStatus(); } } if (!qpaDetectionResult) { qWarning() << "Could not initialize OpenGL context!"; return; } qDebug() << "Done probing Qt OpenGL detection"; windowsOpenGLStatus.isQtPreferAngle = qpaDetectionResult->isUsingAngle(); boost::optional checkResultAngle, checkResultDesktopGL; if (qpaDetectionResult->isUsingAngle()) { checkResultAngle = qpaDetectionResult; // We already checked ANGLE, now check desktop OpenGL qputenv("QT_OPENGL", "desktop"); qDebug() << "Checking desktop OpenGL..."; { QGuiApplication app(argc, argv); checkResultDesktopGL = checkQpaOpenGLStatus(); } if (!checkResultDesktopGL) { qWarning() << "Could not initialize OpenGL context!"; } qDebug() << "Done checking desktop OpenGL"; qunsetenv("QT_OPENGL"); } else { checkResultDesktopGL = qpaDetectionResult; // We already checked desktop OpenGL, now check ANGLE qputenv("QT_OPENGL", "angle"); qDebug() << "Checking ANGLE..."; { QGuiApplication app(argc, argv); checkResultAngle = checkQpaOpenGLStatus(); } if (!checkResultAngle) { qWarning() << "Could not initialize OpenGL context!"; } qDebug() << "Done checking ANGLE"; qunsetenv("QT_OPENGL"); } windowsOpenGLStatus.supportsDesktopGL = checkResultDesktopGL && checkIsSupportedDesktopGL(*checkResultDesktopGL); windowsOpenGLStatus.supportsAngleD3D11 = checkResultAngle && checkIsSupportedAngleD3D11(*checkResultAngle); // HACK: Filter specific buggy drivers not handled by Qt OpenGL buglist if (checkResultDesktopGL) { specialOpenGLVendorFilter(windowsOpenGLStatus, *checkResultDesktopGL); } userRendererConfig = convertConfigToOpenGLRenderer(userRendererConfigString); if ((userRendererConfig == RendererDesktopGL && !windowsOpenGLStatus.supportsDesktopGL) || (userRendererConfig == RendererAngle && !windowsOpenGLStatus.supportsAngleD3D11)) { // Set it to auto so we won't get stuck userRendererConfig = RendererAuto; } nextUserRendererConfig = userRendererConfig; switch (userRendererConfig) { case RendererDesktopGL: QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL, true); currentRenderer = RendererDesktopGL; break; case RendererAngle: QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true); currentRenderer = RendererAngle; break; default: if (windowsOpenGLStatus.isQtPreferAngle && windowsOpenGLStatus.supportsAngleD3D11) { currentRenderer = RendererAngle; } else if (windowsOpenGLStatus.overridePreferAngle && windowsOpenGLStatus.supportsAngleD3D11) { QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true); currentRenderer = RendererAngle; } else if (!windowsOpenGLStatus.isQtPreferAngle && windowsOpenGLStatus.supportsDesktopGL) { currentRenderer = RendererDesktopGL; } else { currentRenderer = RendererNone; } break; } } KisOpenGL::OpenGLRenderer KisOpenGL::getCurrentOpenGLRenderer() { return currentRenderer; } KisOpenGL::OpenGLRenderer KisOpenGL::getQtPreferredOpenGLRenderer() { return (windowsOpenGLStatus.isQtPreferAngle || windowsOpenGLStatus.overridePreferAngle) ? RendererAngle : RendererDesktopGL; } KisOpenGL::OpenGLRenderers KisOpenGL::getSupportedOpenGLRenderers() { return RendererAuto | (windowsOpenGLStatus.supportsDesktopGL ? RendererDesktopGL : static_cast(0)) | (windowsOpenGLStatus.supportsAngleD3D11 ? RendererAngle : static_cast(0)); } KisOpenGL::OpenGLRenderer KisOpenGL::getUserOpenGLRendererConfig() { return userRendererConfig; } KisOpenGL::OpenGLRenderer KisOpenGL::getNextUserOpenGLRendererConfig() { return nextUserRendererConfig; } void KisOpenGL::setNextUserOpenGLRendererConfig(KisOpenGL::OpenGLRenderer renderer) { nextUserRendererConfig = renderer; } QString KisOpenGL::convertOpenGLRendererToConfig(KisOpenGL::OpenGLRenderer renderer) { switch (renderer) { case RendererDesktopGL: return QStringLiteral("desktop"); case RendererAngle: return QStringLiteral("angle"); default: return QStringLiteral("auto"); } } KisOpenGL::OpenGLRenderer KisOpenGL::convertConfigToOpenGLRenderer(QString renderer) { if (renderer == "desktop") { return RendererDesktopGL; } else if (renderer == "angle") { return RendererAngle; } else { return RendererAuto; } } #endif void KisOpenGL::initialize() { if (openGLCheckResult) return; KIS_SAFE_ASSERT_RECOVER(defaultFormatIsSet) { qWarning() << "Default OpenGL format was not set before calling KisOpenGL::initialize. This might be a BUG!"; setDefaultFormat(); } // we need a QSurface active to get our GL functions from the context QWindow surface; surface.setSurfaceType( QSurface::OpenGLSurface ); surface.create(); QOpenGLContext context; if (!context.create()) { qDebug() << "OpenGL context cannot be created"; return; } if (!context.isValid()) { qDebug() << "OpenGL context is not valid"; return; } if (!context.makeCurrent(&surface)) { qDebug() << "OpenGL context cannot be made current"; return; } QOpenGLFunctions *funcs = context.functions(); openGLCheckResult = OpenGLCheckResult(context); debugText.clear(); QDebug debugOut(&debugText); debugOut << "OpenGL Info"; debugOut << "\n Vendor: " << reinterpret_cast(funcs->glGetString(GL_VENDOR)); debugOut << "\n Renderer: " << openGLCheckResult->rendererString; debugOut << "\n Version: " << openGLCheckResult->driverVersionString; debugOut << "\n Shading language: " << reinterpret_cast(funcs->glGetString(GL_SHADING_LANGUAGE_VERSION)); debugOut << "\n Requested format: " << QSurfaceFormat::defaultFormat(); debugOut << "\n Current format: " << context.format(); debugOut.nospace(); debugOut << "\n Version: " << openGLCheckResult->glMajorVersion << "." << openGLCheckResult->glMinorVersion; debugOut.resetFormat(); debugOut << "\n Supports deprecated functions" << openGLCheckResult->supportsDeprecatedFunctions; debugOut << "\n is OpenGL ES:" << openGLCheckResult->isOpenGLES; #ifdef Q_OS_WIN debugOut << "\n\nQPA OpenGL Detection Info"; debugOut << "\n supportsDesktopGL:" << windowsOpenGLStatus.supportsDesktopGL; debugOut << "\n supportsAngleD3D11:" << windowsOpenGLStatus.supportsAngleD3D11; debugOut << "\n isQtPreferAngle:" << windowsOpenGLStatus.isQtPreferAngle; debugOut << "\n overridePreferAngle:" << windowsOpenGLStatus.overridePreferAngle; debugOut << "\n== log ==\n"; debugOut.noquote(); debugOut << qpaDetectionLog.join('\n'); debugOut.resetFormat(); debugOut << "\n== end log =="; #endif qDebug().noquote() << debugText; } void KisOpenGL::initializeContext(QOpenGLContext *ctx) { KisConfig cfg; initialize(); dbgUI << "OpenGL: Opening new context"; if (isDebugEnabled) { // Passing ctx for ownership management only, not specifying context. // QOpenGLDebugLogger only function on the current active context. // FIXME: Do we need to make sure ctx is the active context? QOpenGLDebugLogger* openglLogger = new QOpenGLDebugLogger(ctx); if (openglLogger->initialize()) { qDebug() << "QOpenGLDebugLogger is initialized. Check whether you get a message below."; QObject::connect(openglLogger, &QOpenGLDebugLogger::messageLogged, &openglOnMessageLogged); openglLogger->startLogging(isDebugSynchronous ? QOpenGLDebugLogger::SynchronousLogging : QOpenGLDebugLogger::AsynchronousLogging); openglLogger->logMessage(QOpenGLDebugMessage::createApplicationMessage(QStringLiteral("QOpenGLDebugLogger is logging."))); } else { qDebug() << "QOpenGLDebugLogger cannot be initialized."; delete openglLogger; } } // Double check we were given the version we requested QSurfaceFormat format = ctx->format(); QOpenGLFunctions *f = ctx->functions(); f->initializeOpenGLFunctions(); - QFile log(QDesktopServices::storageLocation(QDesktopServices::TempLocation) + "/krita-opengl.txt"); + QFile log(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/krita-opengl.txt"); log.open(QFile::WriteOnly); QString vendor((const char*)f->glGetString(GL_VENDOR)); log.write(vendor.toLatin1()); log.write(", "); log.write(openGLCheckResult->rendererString.toLatin1()); log.write(", "); QString version((const char*)f->glGetString(GL_VERSION)); log.write(version.toLatin1()); log.close(); // Check if we have a bugged driver that needs fence workaround bool isOnX11 = false; #ifdef HAVE_X11 isOnX11 = true; #endif if ((isOnX11 && openGLCheckResult->rendererString.startsWith("AMD")) || cfg.forceOpenGLFenceWorkaround()) { NeedsFenceWorkaround = true; } /** * NVidia + Qt's openGL don't play well together and one cannot * draw a pixmap on a widget more than once in one rendering cycle. * * It can be workarounded by drawing strictly via QPixmapCache and * only when the pixmap size in bigger than doubled size of the * display framebuffer. That is for 8-bit HD display, you should have * a cache bigger than 16 MiB. Don't ask me why. (DK) * * See bug: https://bugs.kde.org/show_bug.cgi?id=361709 * * TODO: check if this workaround is still needed after merging * Qt5+openGL3 branch. */ if (vendor.toUpper().contains("NVIDIA")) { NeedsPixmapCacheWorkaround = true; const QRect screenSize = QApplication::desktop()->screenGeometry(); const int minCacheSize = 20 * 1024; const int cacheSize = 2048 + 2 * 4 * screenSize.width() * screenSize.height() / 1024; //KiB QPixmapCache::setCacheLimit(qMax(minCacheSize, cacheSize)); } } const QString &KisOpenGL::getDebugText() { initialize(); return debugText; } // XXX Temporary function to allow LoD on OpenGL3 without triggering // all of the other 3.2 functionality, can be removed once we move to Qt5.7 bool KisOpenGL::supportsLoD() { initialize(); return openGLCheckResult->supportsLoD(); } bool KisOpenGL::hasOpenGL3() { initialize(); return openGLCheckResult->hasOpenGL3(); } bool KisOpenGL::hasOpenGLES() { initialize(); return openGLCheckResult->isOpenGLES; } bool KisOpenGL::supportsFenceSync() { initialize(); return openGLCheckResult->supportsFenceSync(); } bool KisOpenGL::needsFenceWorkaround() { initialize(); return NeedsFenceWorkaround; } bool KisOpenGL::needsPixmapCacheWorkaround() { initialize(); return NeedsPixmapCacheWorkaround; } void KisOpenGL::setDefaultFormat(bool enableDebug, bool debugSynchronous) { if (defaultFormatIsSet) { return; } defaultFormatIsSet = true; QSurfaceFormat format; #ifdef Q_OS_OSX format.setVersion(3, 2); format.setProfile(QSurfaceFormat::CoreProfile); #else // XXX This can be removed once we move to Qt5.7 format.setVersion(3, 0); format.setProfile(QSurfaceFormat::CompatibilityProfile); format.setOptions(QSurfaceFormat::DeprecatedFunctions); #endif format.setDepthBufferSize(24); format.setStencilBufferSize(8); format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); format.setSwapInterval(0); // Disable vertical refresh syncing isDebugEnabled = enableDebug; if (enableDebug) { format.setOption(QSurfaceFormat::DebugContext, true); isDebugSynchronous = debugSynchronous; qDebug() << "QOpenGLDebugLogger will be enabled, synchronous:" << debugSynchronous; } QSurfaceFormat::setDefaultFormat(format); } bool KisOpenGL::hasOpenGL() { return openGLCheckResult->isSupportedVersion(); } diff --git a/libs/ui/tests/modeltest.cpp b/libs/ui/tests/modeltest.cpp index 9db9840c50..fc5948f573 100644 --- a/libs/ui/tests/modeltest.cpp +++ b/libs/ui/tests/modeltest.cpp @@ -1,545 +1,545 @@ /**************************************************************************** ** ** Copyright (C) 2007 Trolltech ASA. All rights reserved. ** ** This file is part of the Qt Concurrent project on Trolltech Labs. ** ** This file may be used under the terms of the GNU General Public ** License version 2.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of ** this file. Please review the following information to ensure GNU ** General Public Licensing requirements will be met: ** http://www.trolltech.com/products/qt/opensource.html ** ** If you are unsure which license is appropriate for your use, please ** review the following information: ** http://www.trolltech.com/products/qt/licensing.html or contact the ** sales department at sales@trolltech.com. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ****************************************************************************/ #include #include "modeltest.h" Q_DECLARE_METATYPE(QModelIndex) /*! Connect to all of the models signals. Whenever anything happens recheck everything. */ ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) { Q_ASSERT(model); connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(runAllTests())); connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(runAllTests())); connect(model, SIGNAL(layoutChanged()), this, SLOT(runAllTests())); connect(model, SIGNAL(modelReset()), this, SLOT(runAllTests())); connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(runAllTests())); // Special checks for inserting/removing connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); connect(model, SIGNAL(layoutChanged()), this, SLOT(layoutChanged())); connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int))); connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int))); connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int))); runAllTests(); } void ModelTest::runAllTests() { if (fetchingMore) return; nonDestructiveBasicTest(); rowCount(); columnCount(); hasIndex(); index(); parent(); data(); } /*! nonDestructiveBasicTest tries to call a number of the basic functions (not all) to make sure the model doesn't outright segfault, testing the functions that makes sense. */ void ModelTest::nonDestructiveBasicTest() { Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); model->canFetchMore(QModelIndex()); Q_ASSERT(model->columnCount(QModelIndex()) >= 0); Q_ASSERT(model->data(QModelIndex()) == QVariant()); fetchingMore = true; model->fetchMore(QModelIndex()); fetchingMore = false; Qt::ItemFlags flags = model->flags(QModelIndex()); Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); Q_UNUSED(flags); model->hasChildren(QModelIndex()); model->hasIndex(0, 0); model->headerData(0, Qt::Horizontal); model->index(0, 0); model->itemData(QModelIndex()); QVariant cache; model->match(QModelIndex(), -1, cache); model->mimeTypes(); Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); Q_ASSERT(model->rowCount() >= 0); QVariant variant; model->setData(QModelIndex(), variant, -1); model->setHeaderData(-1, Qt::Horizontal, QVariant()); model->setHeaderData(0, Qt::Horizontal, QVariant()); model->setHeaderData(999999, Qt::Horizontal, QVariant()); QMap roles; model->sibling(0, 0, QModelIndex()); model->span(QModelIndex()); model->supportedDropActions(); } /*! Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() Models that are dynamically populated are not as fully tested here. */ void ModelTest::rowCount() { // check top row QModelIndex topIndex = model->index(0, 0, QModelIndex()); int rows = model->rowCount(topIndex); Q_ASSERT(rows >= 0); if (rows > 0) Q_ASSERT(model->hasChildren(topIndex) == true); QModelIndex secondLevelIndex = model->index(0, 0, topIndex); if (secondLevelIndex.isValid()) { // not the top level // check a row count where parent is valid rows = model->rowCount(secondLevelIndex); Q_ASSERT(rows >= 0); if (rows > 0) Q_ASSERT(model->hasChildren(secondLevelIndex) == true); } // The models rowCount() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() */ void ModelTest::columnCount() { // check top row QModelIndex topIndex = model->index(0, 0, QModelIndex()); Q_ASSERT(model->columnCount(topIndex) >= 0); // check a column count where parent is valid QModelIndex childIndex = model->index(0, 0, topIndex); if (childIndex.isValid()) Q_ASSERT(model->columnCount(childIndex) >= 0); // columnCount() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::hasIndex() */ void ModelTest::hasIndex() { // Make sure that invalid values returns an invalid index Q_ASSERT(model->hasIndex(-2, -2) == false); Q_ASSERT(model->hasIndex(-2, 0) == false); Q_ASSERT(model->hasIndex(0, -2) == false); int rows = model->rowCount(); int columns = model->columnCount(); Q_UNUSED(columns); // check out of bounds Q_ASSERT(model->hasIndex(rows, columns) == false); Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); if (rows > 0) Q_ASSERT(model->hasIndex(0, 0) == true); // hasIndex() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::index() */ void ModelTest::index() { // Make sure that invalid values returns an invalid index Q_ASSERT(model->index(-2, -2) == QModelIndex()); Q_ASSERT(model->index(-2, 0) == QModelIndex()); Q_ASSERT(model->index(0, -2) == QModelIndex()); int rows = model->rowCount(); int columns = model->columnCount(); Q_UNUSED(columns); if (rows == 0) return; // Catch off by one errors Q_ASSERT(model->index(rows, columns) == QModelIndex()); Q_ASSERT(model->index(0, 0).isValid() == true); // Make sure that the same index is *always* returned QModelIndex a = model->index(0, 0); QModelIndex b = model->index(0, 0); Q_ASSERT(a == b); // index() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::parent() */ void ModelTest::parent() { // Make sure the model wont crash and will return an invalid QModelIndex // when asked for the parent of an invalid index. Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); if (model->rowCount() == 0) return; // Column 0 | Column 1 | // QModelIndex() | | // \- topIndex | topIndex1 | // \- childIndex | childIndex1 | // Common error test #1, make sure that a top level index has a parent // that is a invalid QModelIndex. QModelIndex topIndex = model->index(0, 0, QModelIndex()); Q_ASSERT(model->parent(topIndex) == QModelIndex()); // Common error test #2, make sure that a second level index has a parent // that is the first level index. if (model->rowCount(topIndex) > 0) { QModelIndex childIndex = model->index(0, 0, topIndex); Q_ASSERT(model->parent(childIndex) == topIndex); } // Common error test #3, the second column should NOT have the same children // as the first column in a row. // Usually the second column shouldn't have children. QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); if (model->rowCount(topIndex1) > 0) { QModelIndex childIndex = model->index(0, 0, topIndex); QModelIndex childIndex1 = model->index(0, 0, topIndex1); Q_ASSERT(childIndex != childIndex1); } // Full test, walk n levels deep through the model making sure that all // parent's children correctly specify their parent. checkChildren(QModelIndex()); } /*! Called from the parent() test. A model that returns an index of parent X should also return X when asking for the parent of the index. This recursive function does pretty extensive testing on the whole model in an effort to catch edge cases. This function assumes that rowCount(), columnCount() and index() already work. If they have a bug it will point it out, but the above tests should have already found the basic bugs because it is easier to figure out the problem in those tests then this one. */ void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) { // First just try walking back up the tree. QModelIndex p = parent; while (p.isValid()) p = p.parent(); // For models that are dynamically populated if (model->canFetchMore(parent)) { fetchingMore = true; model->fetchMore(parent); fetchingMore = false; } int rows = model->rowCount(parent); int columns = model->columnCount(parent); if (rows > 0) Q_ASSERT(model->hasChildren(parent)); // Some further testing against rows(), columns(), and hasChildren() Q_ASSERT(rows >= 0); Q_ASSERT(columns >= 0); if (rows > 0) Q_ASSERT(model->hasChildren(parent) == true); //dbgKrita << "parent:" << model->data(parent).toString() << "rows:" << rows // << "columns:" << columns << "parent column:" << parent.column(); Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); for (int r = 0; r < rows; ++r) { if (model->canFetchMore(parent)) { fetchingMore = true; model->fetchMore(parent); fetchingMore = false; } Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); for (int c = 0; c < columns; ++c) { Q_ASSERT(model->hasIndex(r, c, parent) == true); QModelIndex index = model->index(r, c, parent); // rowCount() and columnCount() said that it existed... Q_ASSERT(index.isValid() == true); // index() should always return the same index when called twice in a row QModelIndex modifiedIndex = model->index(r, c, parent); Q_ASSERT(index == modifiedIndex); // Make sure we get the same index if we request it twice in a row QModelIndex a = model->index(r, c, parent); QModelIndex b = model->index(r, c, parent); Q_ASSERT(a == b); // Some basic checking on the index that is returned Q_ASSERT(index.model() == model); Q_ASSERT(index.row() == r); Q_ASSERT(index.column() == c); // While you can technically return a QVariant usually this is a sign // of an bug in data() Disable if this really is ok in your model. Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); // If the next test fails here is some somewhat useful debug you play with. /* if (model->parent(index) != parent) { dbgKrita << r << c << currentDepth << model->data(index).toString() << model->data(parent).toString(); dbgKrita << index << parent << model->parent(index); // And a view that you can even use to show the model. //QTreeView view; //view.setModel(model); //view.show(); }*/ // Check that we can get back our real parent. Q_ASSERT(model->parent(index) == parent); // recursively go down the children if (model->hasChildren(index) && currentDepth < 10) { //dbgKrita << r << c << "has children" << model->rowCount(index); checkChildren(index, ++currentDepth); }/* else { if (currentDepth >= 10) dbgKrita << "checked 10 deep"; };*/ // make sure that after testing the children that the index doesn't change. QModelIndex newerIndex = model->index(r, c, parent); Q_ASSERT(index == newerIndex); } } } /*! Tests model's implementation of QAbstractItemModel::data() */ void ModelTest::data() { // Invalid index should return an invalid qvariant Q_ASSERT(!model->data(QModelIndex()).isValid()); if (model->rowCount() == 0) return; // A valid index should have a valid QVariant data Q_ASSERT(model->index(0, 0).isValid()); // shouldn't be able to set data on an invalid index Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); // General Purpose roles that should return a QString QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); if (variant.isValid()) { - Q_ASSERT(qVariantCanConvert(variant)); + Q_ASSERT(variant.canConvert()); } variant = model->data(model->index(0, 0), Qt::StatusTipRole); if (variant.isValid()) { - Q_ASSERT(qVariantCanConvert(variant)); + Q_ASSERT(variant.canConvert()); } variant = model->data(model->index(0, 0), Qt::WhatsThisRole); if (variant.isValid()) { - Q_ASSERT(qVariantCanConvert(variant)); + Q_ASSERT(variant.canConvert()); } // General Purpose roles that should return a QSize variant = model->data(model->index(0, 0), Qt::SizeHintRole); if (variant.isValid()) { - Q_ASSERT(qVariantCanConvert(variant)); + Q_ASSERT(variant.canConvert()); } // General Purpose roles that should return a QFont QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); if (fontVariant.isValid()) { - Q_ASSERT(qVariantCanConvert(fontVariant)); + Q_ASSERT(fontVariant.canConvert()); } // Check that the alignment is one we know about QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); if (textAlignmentVariant.isValid()) { int alignment = textAlignmentVariant.toInt(); Q_UNUSED(alignment); Q_ASSERT(alignment == Qt::AlignLeft || alignment == Qt::AlignRight || alignment == Qt::AlignHCenter || alignment == Qt::AlignJustify || alignment == Qt::AlignTop || alignment == Qt::AlignBottom || alignment == Qt::AlignVCenter || alignment == Qt::AlignCenter || alignment == Qt::AlignAbsolute || alignment == Qt::AlignLeading || alignment == Qt::AlignTrailing); } // General Purpose roles that should return a QColor QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); if (colorVariant.isValid()) { - Q_ASSERT(qVariantCanConvert(colorVariant)); + Q_ASSERT(colorVariant.canConvert()); } colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); if (colorVariant.isValid()) { - Q_ASSERT(qVariantCanConvert(colorVariant)); + Q_ASSERT(colorVariant.canConvert()); } // Check that the "check state" is one we know about. QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); if (checkStateVariant.isValid()) { int state = checkStateVariant.toInt(); Q_UNUSED(state); Q_ASSERT(state == Qt::Unchecked || state == Qt::PartiallyChecked || state == Qt::Checked); } } /*! Store what is about to be inserted to make sure it actually happens \sa rowsInserted() */ void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) { Q_UNUSED(end); Changing c; c.parent = parent; c.oldSize = model->rowCount(parent); c.last = model->data(model->index(start - 1, 0, parent)); c.next = model->data(model->index(start, 0, parent)); insert.push(c); } /*! Confirm that what was said was going to happen actually did \sa rowsAboutToBeInserted() */ void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(start); Q_UNUSED(end); Changing c = insert.pop(); Q_ASSERT(c.parent == parent); Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); /* if (c.next != model->data(model->index(end + 1, 0, c.parent))) { dbgKrita << start << end; for (int i=0; i < model->rowCount(); ++i) dbgKrita << model->index(i, 0).data().toString(); dbgKrita << c.next << model->data(model->index(end + 1, 0, c.parent)); } */ Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); } void ModelTest::layoutAboutToBeChanged() { for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) changing.append(QPersistentModelIndex(model->index(i, 0))); } void ModelTest::layoutChanged() { for (int i = 0; i < changing.count(); ++i) { QPersistentModelIndex p = changing[i]; Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); } changing.clear(); } /*! Store what is about to be inserted to make sure it actually happens \sa rowsRemoved() */ void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { Changing c; c.parent = parent; c.oldSize = model->rowCount(parent); c.last = model->data(model->index(start - 1, 0, parent)); c.next = model->data(model->index(end + 1, 0, parent)); remove.push(c); } /*! Confirm that what was said was going to happen actually did \sa rowsAboutToBeRemoved() */ void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(start); Q_UNUSED(end); Changing c = remove.pop(); Q_ASSERT(c.parent == parent); Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); } diff --git a/libs/ui/widgets/kis_advanced_color_space_selector.cc b/libs/ui/widgets/kis_advanced_color_space_selector.cc index e98f7cb161..8a88b4889c 100644 --- a/libs/ui/widgets/kis_advanced_color_space_selector.cc +++ b/libs/ui/widgets/kis_advanced_color_space_selector.cc @@ -1,793 +1,793 @@ /* * Copyright (C) 2007 Cyrille Berger * Copyright (C) 2011 Boudewijn Rempt * Copyright (C) 2011 Srikanth Tiyyagura * Copyright (C) 2015 Wolthera van Hövell tot Westerflier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * 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_advanced_color_space_selector.h" #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include "ui_wdgcolorspaceselectoradvanced.h" #include struct KisAdvancedColorSpaceSelector::Private { Ui_WdgColorSpaceSelectorAdvanced* colorSpaceSelector; QString knsrcFile; }; KisAdvancedColorSpaceSelector::KisAdvancedColorSpaceSelector(QWidget* parent, const QString &caption) : QDialog(parent) , d(new Private) { setWindowTitle(caption); d->colorSpaceSelector = new Ui_WdgColorSpaceSelectorAdvanced; d->colorSpaceSelector->setupUi(this); d->colorSpaceSelector->cmbColorModels->setIDList(KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::OnlyUserVisible)); fillCmbDepths(d->colorSpaceSelector->cmbColorModels->currentItem()); d->colorSpaceSelector->bnInstallProfile->setIcon(KisIconUtils::loadIcon("document-open")); d->colorSpaceSelector->bnInstallProfile->setToolTip( i18n("Open Color Profile") ); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbDepths(const KoID &))); connect(d->colorSpaceSelector->cmbColorDepth, SIGNAL(activated(const KoID &)), this, SLOT(fillLstProfiles())); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillLstProfiles())); connect(d->colorSpaceSelector->lstProfile, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(colorSpaceChanged())); connect(this, SIGNAL(selectionChanged(bool)), this, SLOT(fillDescription())); connect(this, SIGNAL(selectionChanged(bool)), d->colorSpaceSelector->TongueWidget, SLOT(repaint())); connect(this, SIGNAL(selectionChanged(bool)), d->colorSpaceSelector->TRCwidget, SLOT(repaint())); connect(d->colorSpaceSelector->bnInstallProfile, SIGNAL(clicked()), this, SLOT(installProfile())); connect(d->colorSpaceSelector->bnOK, SIGNAL(accepted()), this, SLOT(accept())); connect(d->colorSpaceSelector->bnOK, SIGNAL(rejected()), this, SLOT(reject())); fillLstProfiles(); } KisAdvancedColorSpaceSelector::~KisAdvancedColorSpaceSelector() { delete d->colorSpaceSelector; delete d; } void KisAdvancedColorSpaceSelector::fillLstProfiles() { d->colorSpaceSelector->lstProfile->blockSignals(true); const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem()); const QString defaultProfileName = KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId); d->colorSpaceSelector->lstProfile->clear(); QList profileList = KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId); QStringList profileNames; Q_FOREACH (const KoColorProfile *profile, profileList) { profileNames.append(profile->name()); } std::sort(profileNames.begin(), profileNames.end()); QListWidgetItem *defaultProfile = new QListWidgetItem; defaultProfile->setText(defaultProfileName + " " + i18nc("This is appended to the color profile which is the default for the given colorspace and bit-depth","(Default)")); Q_FOREACH (QString stringName, profileNames) { if (stringName == defaultProfileName) { d->colorSpaceSelector->lstProfile->addItem(defaultProfile); } else { d->colorSpaceSelector->lstProfile->addItem(stringName); } } d->colorSpaceSelector->lstProfile->setCurrentItem(defaultProfile); d->colorSpaceSelector->lstProfile->blockSignals(false); colorSpaceChanged(); } void KisAdvancedColorSpaceSelector::fillCmbDepths(const KoID& id) { KoID activeDepth = d->colorSpaceSelector->cmbColorDepth->currentItem(); d->colorSpaceSelector->cmbColorDepth->clear(); QList depths = KoColorSpaceRegistry::instance()->colorDepthList(id, KoColorSpaceRegistry::OnlyUserVisible); QList sortedDepths; if (depths.contains(Integer8BitsColorDepthID)) { sortedDepths << Integer8BitsColorDepthID; } if (depths.contains(Integer16BitsColorDepthID)) { sortedDepths << Integer16BitsColorDepthID; } if (depths.contains(Float16BitsColorDepthID)) { sortedDepths << Float16BitsColorDepthID; } if (depths.contains(Float32BitsColorDepthID)) { sortedDepths << Float32BitsColorDepthID; } if (depths.contains(Float64BitsColorDepthID)) { sortedDepths << Float64BitsColorDepthID; } d->colorSpaceSelector->cmbColorDepth->setIDList(sortedDepths); if (sortedDepths.contains(activeDepth)) { d->colorSpaceSelector->cmbColorDepth->setCurrent(activeDepth); } } void KisAdvancedColorSpaceSelector::fillDescription() { QString notApplicable = i18nc("Not Applicable, used where there's no colorants or gamma curve found","N/A"); QString notApplicableTooltip = i18nc("@info:tooltip","This profile has no colorants."); QString profileName = i18nc("Shows up instead of the name when there's no profile","No Profile Found"); QString whatIsColorant = i18n("Colorant in d50-adapted xyY."); //set colorants const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem()); QList profileList = KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId); if (!profileList.isEmpty()) { profileName = currentColorSpace()->profile()->name(); if (currentColorSpace()->profile()->hasColorants()){ QVector colorants = currentColorSpace()->profile()->getColorantsxyY(); QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); //QString text = currentColorSpace()->profile()->info() + " =" + d->colorSpaceSelector->lblXYZ_W->setText(nameWhitePoint(whitepoint)); d->colorSpaceSelector->lblXYZ_W->setToolTip(QString::number(whitepoint[0], 'f', 4) + ", " + QString::number(whitepoint[1], 'f', 4) + ", " + QString::number(whitepoint[2], 'f', 4)); d->colorSpaceSelector->TongueWidget->setToolTip("
"+i18nc("@info:tooltip","This profile has the following xyY colorants:")+"
"+ i18n("Red:") +""+QString::number(colorants[0], 'f', 4) + "" + QString::number(colorants[1], 'f', 4) + "" + QString::number(colorants[2], 'f', 4)+"
"+ i18n("Green:")+""+QString::number(colorants[3], 'f', 4) + "" + QString::number(colorants[4], 'f', 4) + "" + QString::number(colorants[5], 'f', 4)+"
"+ i18n("Blue:") +""+QString::number(colorants[6], 'f', 4) + "" + QString::number(colorants[7], 'f', 4) + "" + QString::number(colorants[8], 'f', 4)+"
"); } else { QVector whitepoint2 = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->lblXYZ_W->setText(nameWhitePoint(whitepoint2)); d->colorSpaceSelector->lblXYZ_W->setToolTip(QString::number(whitepoint2[0], 'f', 4) + ", " + QString::number(whitepoint2[1], 'f', 4) + ", " + QString::number(whitepoint2[2], 'f', 4)); d->colorSpaceSelector->TongueWidget->setToolTip(notApplicableTooltip); } } else { d->colorSpaceSelector->lblXYZ_W->setText(notApplicable); d->colorSpaceSelector->lblXYZ_W->setToolTip(notApplicableTooltip); d->colorSpaceSelector->TongueWidget->setToolTip(notApplicableTooltip); } //set TRC QVector estimatedTRC(3); QString estimatedGamma = i18nc("Estimated Gamma indicates how the TRC (Tone Response Curve or Tone Reproduction Curve) is bent. A Gamma of 1.0 means linear.", "Estimated Gamma: "); QString estimatedsRGB = i18nc("This is for special Gamma types that LCMS cannot differentiate between", "Estimated Gamma: sRGB, L* or rec709 TRC"); QString whatissRGB = i18nc("@info:tooltip","The Tone Response Curve of this color space is either sRGB, L* or rec709 TRC."); QString currentModelStr = d->colorSpaceSelector->cmbColorModels->currentItem().id(); if (profileList.isEmpty()) { d->colorSpaceSelector->TongueWidget->setProfileDataAvailable(false); d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } else if (currentModelStr == "RGBA") { QVector colorants = currentColorSpace()->profile()->getColorantsxyY(); QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); if (currentColorSpace()->profile()->hasColorants()){ d->colorSpaceSelector->TongueWidget->setRGBData(whitepoint, colorants); } else { colorants.fill(0.0); d->colorSpaceSelector->TongueWidget->setRGBData(whitepoint, colorants); } d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QString estimatedCurve = " Estimated curve: "; QPolygonF redcurve; QPolygonF greencurve; QPolygonF bluecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); redcurve<colorSpaceSelector->TRCwidget->setRGBCurve(redcurve, greencurve, bluecurve); } else { QPolygonF curve = currentColorSpace()->estimatedTRCXYY(); redcurve << curve.at(0) << curve.at(1) << curve.at(2) << curve.at(3) << curve.at(4); greencurve << curve.at(5) << curve.at(6) << curve.at(7) << curve.at(8) << curve.at(9); bluecurve << curve.at(10) << curve.at(11) << curve.at(12) << curve.at(13) << curve.at(14); d->colorSpaceSelector->TRCwidget->setRGBCurve(redcurve, greencurve, bluecurve); } if (estimatedTRC[0] == -1) { d->colorSpaceSelector->TRCwidget->setToolTip(""+whatissRGB+"
"+estimatedCurve+""); } else { d->colorSpaceSelector->TRCwidget->setToolTip(""+estimatedGamma + QString::number(estimatedTRC[0]) + "," + QString::number(estimatedTRC[1]) + "," + QString::number(estimatedTRC[2])+"
"+estimatedCurve+""); } } else if (currentModelStr == "GRAYA") { QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setGrayData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } d->colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); if (estimatedTRC[0] == -1) { d->colorSpaceSelector->TRCwidget->setToolTip(""+whatissRGB+"
"+estimatedCurve+""); } else { d->colorSpaceSelector->TRCwidget->setToolTip(""+estimatedGamma + QString::number(estimatedTRC[0])+"
"+estimatedCurve+""); } } else if (currentModelStr == "CMYKA") { QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setCMYKData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; QPolygonF cyancurve; QPolygonF magentacurve; QPolygonF yellowcurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { QPolygonF curve = currentColorSpace()->estimatedTRCXYY(); cyancurve << curve.at(0) << curve.at(1) << curve.at(2) << curve.at(3) << curve.at(4); magentacurve << curve.at(5) << curve.at(6) << curve.at(7) << curve.at(8) << curve.at(9); yellowcurve << curve.at(10) << curve.at(11) << curve.at(12) << curve.at(13) << curve.at(14); tonecurve << curve.at(15) << curve.at(16) << curve.at(17) << curve.at(18) << curve.at(19); d->colorSpaceSelector->TRCwidget->setCMYKCurve(cyancurve, magentacurve, yellowcurve, tonecurve); } d->colorSpaceSelector->TRCwidget->setToolTip(i18nc("@info:tooltip","Estimated Gamma cannot be retrieved for CMYK.")); } else if (currentModelStr == "XYZA") { QString estimatedCurve = " Estimated curve: "; estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setXYZData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); d->colorSpaceSelector->TRCwidget->setToolTip(""+estimatedGamma + QString::number(estimatedTRC[0])+"< br />"+estimatedCurve+""); } else if (currentModelStr == "LABA") { estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setLABData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); d->colorSpaceSelector->TRCwidget->setToolTip(""+i18nc("@info:tooltip","This is assumed to be the L * TRC. ")+"
"+estimatedCurve+""); } else if (currentModelStr == "YCbCrA") { QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setYCbCrData(whitepoint); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); d->colorSpaceSelector->TRCwidget->setToolTip(i18nc("@info:tooltip","Estimated Gamma cannot be retrieved for YCrCb.")); } d->colorSpaceSelector->textProfileDescription->clear(); if (profileList.isEmpty()==false) { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("About ","About ") + currentColorSpace()->name() + "/" + profileName + "

"); d->colorSpaceSelector->textProfileDescription->append("

"+ i18nc("ICC profile version","ICC Version: ") + QString::number(currentColorSpace()->profile()->version()) + "

"); //d->colorSpaceSelector->textProfileDescription->append("

"+ i18nc("Who made the profile?","Manufacturer: ") + currentColorSpace()->profile()->manufacturer() + "

"); //This would work if people actually wrote the manufacturer into the manufacturer fiedl... d->colorSpaceSelector->textProfileDescription->append("

"+ i18nc("What is the copyright? These are from embedded strings from the icc profile, so they default to english.","Copyright: ") + currentColorSpace()->profile()->copyright() + "

"); } else { d->colorSpaceSelector->textProfileDescription->append("

" + profileName + "

"); } if (currentModelStr == "RGBA") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("If the selected model is RGB", "RGB (Red, Green, Blue), is the color model used by screens and other light-based media.
" "RGB is an additive color model: adding colors together makes them brighter. This color " "model is the most extensive of all color models, and is recommended as a model for painting," "that you can later convert to other spaces. RGB is also the recommended colorspace for HDR editing.")+"

"); } else if (currentModelStr == "CMYKA") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("If the selected model is CMYK", "CMYK (Cyan, Magenta, Yellow, Key), " "is the model used by printers and other ink-based media.
" "CMYK is a subtractive model, meaning that adding colors together will turn them darker. Because of CMYK " "profiles being very specific per printer, it is recommended to work in RGB space, and then later convert " "to a CMYK profile, preferably one delivered by your printer.
" "CMYK is not recommended for painting." "Unfortunately, Krita cannot retrieve colorants or the TRC for this space.")+"

"); } else if (currentModelStr == "XYZA") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("If the selected model is XYZ", "CIE XYZ" "is the space determined by the CIE as the space that encompasses all other colors, and used to " "convert colors between profiles. XYZ is an additive color model, meaning that adding colors together " "makes them brighter. XYZ is not recommended for painting, but can be useful to encode in. The Tone Response " "Curve is assumed to be linear.")+"

"); } else if (currentModelStr == "GRAYA") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("If the selected model is Grayscale", "Grayscale only allows for " "gray values and transparent values. Grayscale images use half " "the memory and disk space compared to an RGB image of the same bit-depth.
" "Grayscale is useful for inking and greyscale images. In " "Krita, you can mix Grayscale and RGB layers in the same image.")+"

"); } else if (currentModelStr == "LABA") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("If the selected model is LAB", "L*a*b. L stands for Lightness, " "the a and b components represent color channels.
" "L*a*b is a special model for color correction. It is based on human perception, meaning that it " "tries to encode the difference in lightness, red-green balance and yellow-blue balance. " "This makes it useful for color correction, but the vast majority of color maths in the blending " "modes do not work as expected here.
" "Similarly, Krita does not support HDR in LAB, meaning that HDR images converted to LAB lose color " "information. This colorspace is not recommended for painting, nor for export, " "but best as a space to do post-processing in. The TRC is assumed to be the L* TRC.")+"

"); } else if (currentModelStr == "YCbCrA") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("If the selected model is YCbCr", "YCbCr (Luma, Blue Chroma, Red Chroma), is a " "model designed for video encoding. It is based on human perception, meaning that it tries to " "encode the difference in lightness, red-green balance and yellow-blue balance. Chroma in " "this case is then a word indicating a special type of saturation, in these cases the saturation " "of Red and Blue, of which the desaturated equivalents are Green and Yellow respectively. It " "is available to open up certain images correctly, but Krita does not currently ship a profile for " "this due to lack of open source ICC profiles for YCrCb.")+"

"); } QString currentDepthStr = d->colorSpaceSelector->cmbColorDepth->currentItem().id(); if (currentDepthStr == "U8") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("When the selected Bitdepth is 8", "8 bit integer: The default amount of colors per channel. Each channel will have 256 values available, " "leading to a total amount of 256*amount of channels. Recommended to use for images intended for the web, " "or otherwise simple images.")+"

"); } else if (currentDepthStr == "U16") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("When the selected Bitdepth is 16", "16 bit integer: Also known as 'deep color'. 16 bit is ideal for editing images with a linear TRC, large " "color space, or just when you need more precise color blending. This does take twice as much space on " "the RAM and hard-drive than any given 8 bit image of the same properties, and for some devices it " "takes much more processing power. We recommend watching the RAM usage of the file carefully, or " "otherwise use 8 bit if your computer slows down. Take care to disable conversion optimization " "when converting from 16 bit/channel to 8 bit/channel.")+"

"); } else if (currentDepthStr == "F16") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("When the selected Bitdepth is 16 bit float", "16 bit floating point: Also known as 'Half Floating Point', and the standard in VFX industry images. " "16 bit float is ideal for editing images with a linear Tone Response Curve, large color space, or just when you need " "more precise color blending. It being floating point is an absolute requirement for Scene Referred " "(HDR) images. This does take twice as much space on the RAM and hard-drive than any given 8 bit image " "of the same properties, and for some devices it takes much more processing power. We recommend watching " "the RAM usage of the file carefully, or otherwise use 8 bit if your computer slows down.")+"

"); } else if (currentDepthStr == "F32") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("When the selected Bitdepth is 32bit float", "32 bit float point: Also known as 'Full Floating Point'. 32 bit float is ideal for editing images " "with a linear TRC, large color space, or just when you need more precise color blending. It being " "floating point is an absolute requirement for Scene Referred (HDR) images. This does take four times " "as much space on the RAM and hard-drive than any given 8 bit image of the same properties, and for " "some devices it takes much more processing power. We recommend watching the RAM usage of the file " "carefully, or otherwise use 8 bit if your computer slows down.")+"

"); } else if (currentDepthStr == "F64") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("When the selected Bitdepth is 64bit float, but this isn't actually available in Krita at the moment.",\ "64 bit float point: 64 bit float is as precise as it gets in current technology, and this depth is used " "most of the time for images that are generated or used as an input for software. It being floating point " "is an absolute requirement for Scene Referred (HDR) images. This does take eight times as much space on " "the RAM and hard-drive than any given 8 bit image of the same properties, and for some devices it takes " "much more processing power. We recommend watching the RAM usage of the file carefully, or otherwise use " "8 bit if your computer slows down.")+"

"); } if (profileList.isEmpty()==false) { QString possibleConversionIntents = "

"+i18n("The following conversion intents are possible: ")+"

    "; if (currentColorSpace()->profile()->supportsPerceptual()){ possibleConversionIntents += "
  • "+i18n("Perceptual")+"
  • "; } if (currentColorSpace()->profile()->supportsRelative()){ possibleConversionIntents += "
  • "+i18n("Relative Colorimetric")+"
  • "; } if (currentColorSpace()->profile()->supportsAbsolute()){ possibleConversionIntents += "
  • "+i18n("Absolute Colorimetric")+"
  • "; } if (currentColorSpace()->profile()->supportsSaturation()){ possibleConversionIntents += "
  • "+i18n("Saturation")+"
  • "; } possibleConversionIntents += "

"; d->colorSpaceSelector->textProfileDescription->append(possibleConversionIntents); } if (profileName.contains("-elle-")) { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("These are Elle Stone's notes on her profiles that we ship.", "

Extra notes on profiles by Elle Stone:

" "

Krita comes with a number of high quality profiles created by " "Elle Stone. This is a summary. Please check " "the full documentation as well.

")); if (profileName.contains("ACES-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

Quoting Wikipedia, 'Academy Color Encoding System (ACES) is a color image " "encoding system proposed by the Academy of Motion Picture Arts and Sciences that will allow for " "a fully encompassing color accurate workflow, with 'seamless interchange of high quality motion " "picture images regardless of source'.

")); } if (profileName.contains("ACEScg-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

The ACEScg color space is smaller than the ACES color space, but large enough to contain the 'Rec-2020 gamut " "and the DCI-P3 gamut', unlike the ACES color space it has no negative values and contains only few colors " "that fall just barely outside the area of real colors humans can see

")); } if (profileName.contains("ClayRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

To avoid possible copyright infringement issues, I used 'ClayRGB' (following ArgyllCMS) as the base name " "for these profiles. As used below, 'Compatible with Adobe RGB 1998' is terminology suggested in the preamble " "to the AdobeRGB 1998 color space specifications.

" "The Adobe RGB 1998 color gamut covers a higher " "percentage of real-world cyans, greens, and yellow-greens than sRGB, but still doesn't include all printable " "cyans, greens, yellow-greens, especially when printing using today's high-end, wider gamut, ink jet printers. " "BetaRGB (not included in the profile pack) and Rec.2020 are better matches for the color gamuts of today's " "wide gamut printers.

" "The Adobe RGB 1998 color gamut is a reasonable approximation to some of today's " "high-end wide gamut monitors.

")); } if (profileName.contains("AllColorsRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

This profile's color gamut is roughly the same size and shape as the ACES color space gamut, " "and like the ACES color space, AllColorsRGB holds all possible real colors. But AllColorsRGB " "actually has a slightly larger color gamut (to capture some fringe colors that barely qualify " "as real when viewed by the standard observer) and uses the D50 white point.

" "Just like the ACES color space, AllColorsRGB holds a high percentage of imaginary colors. See the Completely " "" "Painless Programmer's Guide to XYZ, RGB, ICC, xyY, and TRCs for more information about imaginary " "colors.

" "There is no particular reason why anyone would want to use this profile " "for editing, unless one needs to make sure your color space really does hold all " "possible real colors.

")); } if (profileName.contains("CIERGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

This profile is included mostly for its historical significance. " "It's the color space that was used in the original color matching experiments " "that led to the creation of the XYZ reference color space.

" "The ASTM E white point " "is probably the right E white point to use when making the CIERGB color space profile. " "It's not clear to me what the correct CIERGB primaries really are. " "Lindbloom gives one set. The LCMS version 1 tutorial gives a different set. " "Experts in the field contend that the real primaries " "should be calculated from the spectral wavelengths, so I did.

")); } if (profileName.contains("IdentityRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

The IdentityRGB working space is included in the profile pack because it's a mathematically " "obvious way to include all possible visible colors, though it has a higher percentage of " "imaginary colors than the ACES and AllColorsRGB color spaces. I cannot think of any reason " "why you'd ever want to actually edit images in the IdentityRGB working space.

")); } if (profileName.contains("LargeRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

To avoid possible copyright infringement issues, I used 'LargeRGB' (following RawTherapee) " "as the base name for these profiles.

" "Kodak designed the RIMM/ROMM (ProPhotoRGB) color " "gamut to include all printable and most real world colors. It includes some imaginary colors " "and excludes some of the real world blues and violet blues that can be captured by digital " "cameras. It also excludes some very saturated 'camera-captured' yellows as interpreted by " "some (and probably many) camera matrix input profiles.

" "The ProPhotoRGB primaries are " "hard-coded into Adobe products such as Lightroom and the Dng-DCP camera 'profiles'. However, " "other than being large enough to hold a lot of colors, ProPhotoRGB has no particular merit " "as an RGB working space. Personally and for most editing purposes, I recommend BetaRGB, Rec2020, " "or the ACEScg profiles ProPhotoRGB.

")); } if (profileName.contains("Rec2020-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

Rec.2020 is the up-and-coming replacement for the thoroughly outdated sRGB color space. As of " "June 2015, very few (if any) display devices (and certainly no affordable display devices) can " "display all of Rec.2020. However, display technology is closing in on Rec.2020, movies are " "already being made for Rec.2020, and various cameras offer support for Rec.2020. And in the " "digital darkroom Rec.2020 is much more suitable as a general RGB working space than the " "exceedingly small sRGB color space.

")); } if (profileName.contains("sRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

Hewlett-Packard and Microsoft designed sRGB to match the color gamut of consumer-grade CRTs " "from the 1990s. sRGB is the standard color space for the world wide web and is still the best " "choice for exporting images to the internet.

" "The sRGB color gamut was a good match to " "calibrated decent quality CRTs. But sRGB is not a good match to many consumer-grade LCD monitors, " "which often cannot display the more saturated sRGB blues and magentas (the good news: as technology " "progresses, wider gamuts are trickling down to consumer grade monitors).

" "Printer color gamuts can easily exceed the sRGB color gamut in cyans, greens, and yellow-greens. Colors from interpolated " "camera raw files also often exceed the sRGB color gamut.

" "As a very relevant aside, using perceptual " "intent when converting to sRGB does not magically makes otherwise out of gamut colors fit inside the " "sRGB color gamut! The standard sRGB color space (along with all the other the RGB profiles provided " "in my profile pack) is a matrix profile, and matrix profiles don't have perceptual intent tables.

")); } if (profileName.contains("WideRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

To avoid possible copyright infringement issues, I used 'WideRGB' as the base name for these profiles.

" "WideGamutRGB was designed by Adobe to be a wide gamut color space that uses spectral colors " "as its primaries. Pascale's primary values produce a profile that matches old V2 Widegamut profiles " "from Adobe and Canon. It is an interesting color space, but shortly after its introduction, Adobe " "switched their emphasis to the ProPhotoRGB color space.

")); } if (profileName.contains("Gray-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

These profiles are for use with RGB images that have been converted to monotone gray (black and white). " "The main reason to convert from RGB to Gray is to save the file space needed to encode the image. " "Google places a premium on fast-loading web pages, and images are one of the slower-loading elements " "of a web page. So converting black and white images to Grayscale images does save some kilobytes. " " For grayscale images uploaded to the internet, convert the image to the V2 Gray profile with the sRGB TRC.

")); } if (profileName.contains("-g10")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

The profiles that end in '-g10.icc' are linear gamma (gamma=1.0, 'linear light', etc) profiles and " "should only be used when editing at high bit depths (16-bit floating point, 16-bit integer, 32-bit " "floating point, 32-bit integer). Many editing operations produce better results in linear gamma color " "spaces.

")); } if (profileName.contains("-labl")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

The profiles that end in '-labl.icc' have perceptually uniform TRCs. A few editing operations really " "should be done on perceptually uniform RGB. Make sure you use the V4 versions for editing high bit depth " "images.

")); } if (profileName.contains("-srgbtrc") || profileName.contains("-g22") || profileName.contains("-g18") || profileName.contains("-bt709")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

The profiles that end in '-srgbtrc.icc', '-g22.icc', and '-bt709.icc' have approximately but not exactly " "perceptually uniform TRCs. ProPhotoRGB's gamma=1.8 TRC is not quite as close to being perceptually uniform.

")); } if (d->colorSpaceSelector->cmbColorDepth->currentItem().id()=="U8") { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

When editing 8-bit images, you should use a profile with a small color gamut and an approximately or " "exactly perceptually uniform TRC. Of the profiles supplied in my profile pack, only the sRGB and AdobeRGB1998 " "(ClayRGB) color spaces are small enough for 8-bit editing. Even with the AdobeRGB1998 color space you need to " "be careful to not cause posterization. And of course you cannot use the linear gamma versions of these profiles " "for 8-bit editing.

")); } if (profileName.contains("-V4-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

Use V4 profiles for editing images using high bit depth image editors that use LCMS as the Color Management Module. " "This includes Krita, digiKam/showFoto, and GIMP 2.9.

")); } if (profileName.contains("-V2-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

Use V2 profiles for exporting finished images to be uploaded to the web or for use with imaging software that " "cannot read V4 profiles.

")); } } d->colorSpaceSelector->textProfileDescription->moveCursor(QTextCursor::Start); } QString KisAdvancedColorSpaceSelector::nameWhitePoint(QVector whitePoint) { QString name=(QString::number(whitePoint[0]) + ", " + QString::number(whitePoint[1], 'f', 4)); //A (0.451170, 0.40594) (2856K)(tungsten) if ((whitePoint[0]>0.451170-0.005 && whitePoint[0]<0.451170 + 0.005) && (whitePoint[1]>0.40594-0.005 && whitePoint[1]<0.40594 + 0.005)){ name="A"; return name; } //B (0.34980, 0.35270) (4874K) (Direct Sunlight at noon)(obsolete) //C (0.31039, 0.31905) (6774K) (avarage/north sky daylight)(obsolete) //D50 (0.34773, 0.35952) (5003K) (Horizon Light, default color of white paper, ICC profile standard illuminant) if ((whitePoint[0]>0.34773-0.005 && whitePoint[0]<0.34773 + 0.005) && (whitePoint[1]>0.35952-0.005 && whitePoint[1]<0.35952 + 0.005)){ name="D50"; return name; } //D55 (0.33411, 0.34877) (5503K) (Mid-morning / Mid-afternoon Daylight) if ((whitePoint[0]>0.33411-0.001 && whitePoint[0]<0.33411 + 0.001) && (whitePoint[1]>0.34877-0.005 && whitePoint[1]<0.34877 + 0.005)){ name="D55"; return name; } //D60 (0.3217, 0.3378) (~6000K) (ACES colorspace default) if ((whitePoint[0]>0.3217-0.001 && whitePoint[0]<0.3217 + 0.001) && (whitePoint[1]>0.3378-0.005 && whitePoint[1]<0.3378 + 0.005)){ name="D60"; return name; } //D65 (0.31382, 0.33100) (6504K) (Noon Daylight, default for computer and tv screens, sRGB default) //Elle's are old school with 0.3127 and 0.3289 if ((whitePoint[0]>0.31382-0.002 && whitePoint[0]<0.31382 + 0.002) && (whitePoint[1]>0.33100-0.005 && whitePoint[1]<0.33100 + 0.002)){ name="D65"; return name; } //D75 (0.29968, 0.31740) (7504K) (North sky Daylight) if ((whitePoint[0]>0.29968-0.001 && whitePoint[0]<0.29968 + 0.001) && (whitePoint[1]>0.31740-0.005 && whitePoint[1]<0.31740 + 0.005)){ name="D75"; return name; } //E (1/3, 1/3) (5454K) (Equal Energy. CIERGB default) if ((whitePoint[0]>(1.0/3.0)-0.001 && whitePoint[0]<(1.0/3.0) + 0.001) && (whitePoint[1]>(1.0/3.0)-0.001 && whitePoint[1]<(1.0/3.0) + 0.001)){ name="E"; return name; } //The F series seems to sorta overlap with the D series, so I'll just leave them in comment here.// //F1 (0.31811, 0.33559) (6430K) (Daylight Fluorescent) //F2 (0.37925, 0.36733) (4230K) (Cool White Fluorescent) //F3 (0.41761, 0.38324) (3450K) (White Florescent) //F4 (0.44920, 0.39074) (2940K) (Warm White Fluorescent) //F5 (0.31975, 0.34246) (6350K) (Daylight Fluorescent) //F6 (0.38660, 0.37847) (4150K) (Lite White Fluorescent) //F7 (0.31569, 0.32960) (6500K) (D65 simulator, Daylight simulator) //F8 (0.34902, 0.35939) (5000K) (D50 simulator) //F9 (0.37829, 0.37045) (4150K) (Cool White Deluxe Fluorescent) //F10 (0.35090, 0.35444) (5000K) (Philips TL85, Ultralume 50) //F11 (0.38541, 0.37123) (4000K) (Philips TL84, Ultralume 40) //F12 (0.44256, 0.39717) (3000K) (Philips TL83, Ultralume 30) return name; } const KoColorSpace* KisAdvancedColorSpaceSelector::currentColorSpace() { QString check = ""; if (d->colorSpaceSelector->lstProfile->currentItem()) { check = d->colorSpaceSelector->lstProfile->currentItem()->text(); } else if (d->colorSpaceSelector->lstProfile->item(0)) { check = d->colorSpaceSelector->lstProfile->item(0)->text(); } return KoColorSpaceRegistry::instance()->colorSpace(d->colorSpaceSelector->cmbColorModels->currentItem().id(), d->colorSpaceSelector->cmbColorDepth->currentItem().id(), check); } void KisAdvancedColorSpaceSelector::setCurrentColorModel(const KoID& id) { d->colorSpaceSelector->cmbColorModels->setCurrent(id); fillLstProfiles(); fillCmbDepths(id); } void KisAdvancedColorSpaceSelector::setCurrentColorDepth(const KoID& id) { d->colorSpaceSelector->cmbColorDepth->setCurrent(id); fillLstProfiles(); } void KisAdvancedColorSpaceSelector::setCurrentProfile(const QString& name) { QList Items= d->colorSpaceSelector->lstProfile->findItems(name, Qt::MatchStartsWith); d->colorSpaceSelector->lstProfile->setCurrentItem(Items.at(0)); } void KisAdvancedColorSpaceSelector::setCurrentColorSpace(const KoColorSpace* colorSpace) { if (!colorSpace) { return; } setCurrentColorModel(colorSpace->colorModelId()); setCurrentColorDepth(colorSpace->colorDepthId()); setCurrentProfile(colorSpace->profile()->name()); } void KisAdvancedColorSpaceSelector::colorSpaceChanged() { bool valid = d->colorSpaceSelector->lstProfile->count() != 0; emit(selectionChanged(valid)); if (valid) { emit colorSpaceChanged(currentColorSpace()); } } void KisAdvancedColorSpaceSelector::installProfile() { KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); dialog.setMimeTypeFilters(QStringList() << "application/vnd.iccprofile", "application/vnd.iccprofile"); QStringList profileNames = dialog.filenames(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KoResourcePaths::saveLocation("icc_profiles"); Q_FOREACH (const QString &profileName, profileNames) { QUrl file(profileName); if (!QFile::copy(profileName, saveLocation + file.fileName())) { dbgKrita << "Could not install profile!"; return; } iccEngine->addProfile(saveLocation + file.fileName()); } fillLstProfiles(); } diff --git a/libs/ui/widgets/kis_color_space_selector.cc b/libs/ui/widgets/kis_color_space_selector.cc index 2f508b769b..8aaf38c79c 100644 --- a/libs/ui/widgets/kis_color_space_selector.cc +++ b/libs/ui/widgets/kis_color_space_selector.cc @@ -1,274 +1,274 @@ /* * Copyright (C) 2007 Cyrille Berger * Copyright (C) 2011 Boudewijn Rempt * Copyright (C) 2011 Srikanth Tiyyagura * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_space_selector.h" #include "kis_advanced_color_space_selector.h" #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include "ui_wdgcolorspaceselector.h" struct KisColorSpaceSelector::Private { Ui_WdgColorSpaceSelector* colorSpaceSelector; QString knsrcFile; bool profileValid; QString defaultsuffix; }; KisColorSpaceSelector::KisColorSpaceSelector(QWidget* parent) : QWidget(parent), m_advancedSelector(0), d(new Private) { setObjectName("KisColorSpaceSelector"); d->colorSpaceSelector = new Ui_WdgColorSpaceSelector; d->colorSpaceSelector->setupUi(this); d->colorSpaceSelector->cmbColorModels->setIDList(KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::OnlyUserVisible)); fillCmbDepths(d->colorSpaceSelector->cmbColorModels->currentItem()); d->colorSpaceSelector->bnInstallProfile->setIcon(KisIconUtils::loadIcon("document-open")); d->colorSpaceSelector->bnInstallProfile->setToolTip( i18n("Open Color Profile") ); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbDepths(const KoID &))); connect(d->colorSpaceSelector->cmbColorDepth, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbProfiles())); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbProfiles())); connect(d->colorSpaceSelector->cmbProfile, SIGNAL(activated(const QString &)), this, SLOT(colorSpaceChanged())); connect(d->colorSpaceSelector->bnInstallProfile, SIGNAL(clicked()), this, SLOT(installProfile())); d->defaultsuffix = " "+i18nc("This is appended to the color profile which is the default for the given colorspace and bit-depth","(Default)"); connect(d->colorSpaceSelector->bnAdvanced, SIGNAL(clicked()), this, SLOT(slotOpenAdvancedSelector())); fillCmbProfiles(); } KisColorSpaceSelector::~KisColorSpaceSelector() { delete d->colorSpaceSelector; delete d; } void KisColorSpaceSelector::fillCmbProfiles() { const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem()); const QString defaultProfileName = KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId); d->colorSpaceSelector->cmbProfile->clear(); QList profileList = KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId); QStringList profileNames; Q_FOREACH (const KoColorProfile *profile, profileList) { profileNames.append(profile->name()); } std::sort(profileNames.begin(), profileNames.end()); Q_FOREACH (QString stringName, profileNames) { if (stringName == defaultProfileName) { d->colorSpaceSelector->cmbProfile->addSqueezedItem(stringName + d->defaultsuffix); } else { d->colorSpaceSelector->cmbProfile->addSqueezedItem(stringName); } } d->colorSpaceSelector->cmbProfile->setCurrent(defaultProfileName + d->defaultsuffix); colorSpaceChanged(); } void KisColorSpaceSelector::fillCmbDepths(const KoID& id) { KoID activeDepth = d->colorSpaceSelector->cmbColorDepth->currentItem(); d->colorSpaceSelector->cmbColorDepth->clear(); QList depths = KoColorSpaceRegistry::instance()->colorDepthList(id, KoColorSpaceRegistry::OnlyUserVisible); // order the depth by name std::sort(depths.begin(), depths.end(), sortBitDepthsComparer); d->colorSpaceSelector->cmbColorDepth->setIDList(depths); if (depths.contains(activeDepth)) { d->colorSpaceSelector->cmbColorDepth->setCurrent(activeDepth); } } bool KisColorSpaceSelector::sortBitDepthsComparer(KoID depthOne, KoID depthTwo) { // to order these right, we need to first order by bit depth, then by if it is floating or not QString bitDepthOne = depthOne.name().split(" ")[0]; QString bitDepthTwo = depthTwo.name().split(" ")[0]; if (bitDepthOne.toInt() > bitDepthTwo.toInt()) { return false; } if (bitDepthOne.toInt() == bitDepthTwo.toInt()) { // bit depth number is the same, so now we need to compare if it is a floating type or not // the second value [1], just says 'bits', so that is why we look for [2] which has the float word QString bitDepthOneType = ""; QString bitDepthTwoType = ""; if (depthOne.name().split(" ").length() > 2) { bitDepthOneType = depthOne.name().split(" ")[2]; } if (depthTwo.name().split(" ").length() > 2) { bitDepthTwoType = depthTwo.name().split(" ")[2]; } if (bitDepthOneType.length() > bitDepthTwoType.length()) { return false; } } return true; } const KoColorSpace* KisColorSpaceSelector::currentColorSpace() { QString profilenamestring = d->colorSpaceSelector->cmbProfile->itemHighlighted(); if (profilenamestring.contains(d->defaultsuffix)) { profilenamestring.remove(d->defaultsuffix); return KoColorSpaceRegistry::instance()->colorSpace( d->colorSpaceSelector->cmbColorModels->currentItem().id(), d->colorSpaceSelector->cmbColorDepth->currentItem().id(), profilenamestring); } else { return KoColorSpaceRegistry::instance()->colorSpace( d->colorSpaceSelector->cmbColorModels->currentItem().id(), d->colorSpaceSelector->cmbColorDepth->currentItem().id(), profilenamestring); } } void KisColorSpaceSelector::setCurrentColorModel(const KoID& id) { d->colorSpaceSelector->cmbColorModels->setCurrent(id); fillCmbDepths(id); } void KisColorSpaceSelector::setCurrentColorDepth(const KoID& id) { d->colorSpaceSelector->cmbColorDepth->setCurrent(id); fillCmbProfiles(); } void KisColorSpaceSelector::setCurrentProfile(const QString& name) { d->colorSpaceSelector->cmbProfile->setCurrent(name); } void KisColorSpaceSelector::setCurrentColorSpace(const KoColorSpace* colorSpace) { if (!colorSpace) { return; } setCurrentColorModel(colorSpace->colorModelId()); setCurrentColorDepth(colorSpace->colorDepthId()); setCurrentProfile(colorSpace->profile()->name()); } void KisColorSpaceSelector::showColorBrowserButton(bool showButton) { d->colorSpaceSelector->bnAdvanced->setVisible(showButton); } void KisColorSpaceSelector::colorSpaceChanged() { bool valid = d->colorSpaceSelector->cmbProfile->count() != 0; d->profileValid = valid; emit(selectionChanged(valid)); if(valid) { emit colorSpaceChanged(currentColorSpace()); QString text = currentColorSpace()->profile()->name(); } } void KisColorSpaceSelector::installProfile() { QStringList mime; KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); dialog.setMimeTypeFilters(QStringList() << "application/vnd.iccprofile", "application/vnd.iccprofile"); QStringList profileNames = dialog.filenames(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KoResourcePaths::saveLocation("icc_profiles"); Q_FOREACH (const QString &profileName, profileNames) { QUrl file(profileName); if (!QFile::copy(profileName, saveLocation + file.fileName())) { dbgKrita << "Could not install profile!"; return; } iccEngine->addProfile(saveLocation + file.fileName()); } fillCmbProfiles(); } void KisColorSpaceSelector::slotOpenAdvancedSelector() { if (!m_advancedSelector) { m_advancedSelector = new KisAdvancedColorSpaceSelector(this, "Select a Colorspace"); m_advancedSelector->setModal(true); if (currentColorSpace()) { m_advancedSelector->setCurrentColorSpace(currentColorSpace()); } connect(m_advancedSelector, SIGNAL(selectionChanged(bool)), this, SLOT(slotProfileValid(bool)) ); } QDialog::DialogCode result = (QDialog::DialogCode)m_advancedSelector->exec(); if (result) { if (d->profileValid==true) { setCurrentColorSpace(m_advancedSelector->currentColorSpace()); } } } void KisColorSpaceSelector::slotProfileValid(bool valid) { d->profileValid = valid; } diff --git a/libs/ui/widgets/kis_curve_widget.cpp b/libs/ui/widgets/kis_curve_widget.cpp index efff154e95..036bc959c8 100644 --- a/libs/ui/widgets/kis_curve_widget.cpp +++ b/libs/ui/widgets/kis_curve_widget.cpp @@ -1,502 +1,502 @@ /* * Copyright (c) 2005 C. Boemann * Copyright (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // C++ includes. #include #include // Qt includes. #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes. #include #include #include // Local includes. #include "widgets/kis_curve_widget.h" #define bounds(x,a,b) (xb ? b :x)) #define MOUSE_AWAY_THRES 15 #define POINT_AREA 1E-4 #define CURVE_AREA 1E-4 #include "kis_curve_widget_p.h" -KisCurveWidget::KisCurveWidget(QWidget *parent, Qt::WFlags f) +KisCurveWidget::KisCurveWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f), d(new KisCurveWidget::Private(this)) { setObjectName("KisCurveWidget"); d->m_grab_point_index = -1; d->m_readOnlyMode = false; d->m_guideVisible = false; d->m_pixmapDirty = true; d->m_pixmapCache = 0; d->setState(ST_NORMAL); d->m_intIn = 0; d->m_intOut = 0; setMouseTracking(true); setAutoFillBackground(false); setAttribute(Qt::WA_OpaquePaintEvent); setMinimumSize(150, 50); setMaximumSize(250, 250); d->setCurveModified(); setFocusPolicy(Qt::StrongFocus); } KisCurveWidget::~KisCurveWidget() { delete d->m_pixmapCache; delete d; } void KisCurveWidget::setupInOutControls(QSpinBox *in, QSpinBox *out, int min, int max) { d->m_intIn = in; d->m_intOut = out; if (!d->m_intIn || !d->m_intOut) return; d->m_inOutMin = min; d->m_inOutMax = max; d->m_intIn->setRange(d->m_inOutMin, d->m_inOutMax); d->m_intOut->setRange(d->m_inOutMin, d->m_inOutMax); connect(d->m_intIn, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int))); connect(d->m_intOut, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int))); d->syncIOControls(); } void KisCurveWidget::dropInOutControls() { if (!d->m_intIn || !d->m_intOut) return; disconnect(d->m_intIn, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int))); disconnect(d->m_intOut, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int))); d->m_intIn = d->m_intOut = 0; } void KisCurveWidget::inOutChanged(int) { QPointF pt; Q_ASSERT(d->m_grab_point_index >= 0); pt.setX(d->io2sp(d->m_intIn->value())); pt.setY(d->io2sp(d->m_intOut->value())); if (d->jumpOverExistingPoints(pt, d->m_grab_point_index)) { d->m_curve.setPoint(d->m_grab_point_index, pt); d->m_grab_point_index = d->m_curve.points().indexOf(pt); emit pointSelectedChanged(); } else pt = d->m_curve.points()[d->m_grab_point_index]; d->m_intIn->blockSignals(true); d->m_intOut->blockSignals(true); d->m_intIn->setValue(d->sp2io(pt.x())); d->m_intOut->setValue(d->sp2io(pt.y())); d->m_intIn->blockSignals(false); d->m_intOut->blockSignals(false); d->setCurveModified(); } void KisCurveWidget::reset(void) { d->m_grab_point_index = -1; emit pointSelectedChanged(); d->m_guideVisible = false; d->setCurveModified(); } void KisCurveWidget::setCurveGuide(const QColor & color) { d->m_guideVisible = true; d->m_colorGuide = color; } void KisCurveWidget::setPixmap(const QPixmap & pix) { d->m_pix = pix; d->m_pixmapDirty = true; d->setCurveRepaint(); } QPixmap KisCurveWidget::getPixmap() { return d->m_pix; } void KisCurveWidget::setBasePixmap(const QPixmap &pix) { d->m_pixmapBase = pix; } QPixmap KisCurveWidget::getBasePixmap() { return d->m_pixmapBase; } bool KisCurveWidget::pointSelected() const { return d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_curve.points().count() - 1; } void KisCurveWidget::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace) { if (d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_curve.points().count() - 1) { //x() find closest point to get focus afterwards double grab_point_x = d->m_curve.points()[d->m_grab_point_index].x(); int left_of_grab_point_index = d->m_grab_point_index - 1; int right_of_grab_point_index = d->m_grab_point_index + 1; int new_grab_point_index; if (fabs(d->m_curve.points()[left_of_grab_point_index].x() - grab_point_x) < fabs(d->m_curve.points()[right_of_grab_point_index].x() - grab_point_x)) { new_grab_point_index = left_of_grab_point_index; } else { new_grab_point_index = d->m_grab_point_index; } d->m_curve.removePoint(d->m_grab_point_index); d->m_grab_point_index = new_grab_point_index; emit pointSelectedChanged(); setCursor(Qt::ArrowCursor); d->setState(ST_NORMAL); } e->accept(); d->setCurveModified(); } else if (e->key() == Qt::Key_Escape && d->state() != ST_NORMAL) { d->m_curve.setPoint(d->m_grab_point_index, QPointF(d->m_grabOriginalX, d->m_grabOriginalY) ); setCursor(Qt::ArrowCursor); d->setState(ST_NORMAL); e->accept(); d->setCurveModified(); } else if ((e->key() == Qt::Key_A || e->key() == Qt::Key_Insert) && d->state() == ST_NORMAL) { /* FIXME: Lets user choose the hotkeys */ addPointInTheMiddle(); e->accept(); } else QWidget::keyPressEvent(e); } void KisCurveWidget::addPointInTheMiddle() { QPointF pt(0.5, d->m_curve.value(0.5)); if (!d->jumpOverExistingPoints(pt, -1)) return; d->m_grab_point_index = d->m_curve.addPoint(pt); emit pointSelectedChanged(); if (d->m_intIn) d->m_intIn->setFocus(Qt::TabFocusReason); d->setCurveModified(); } void KisCurveWidget::resizeEvent(QResizeEvent *e) { d->m_pixmapDirty = true; QWidget::resizeEvent(e); } void KisCurveWidget::paintEvent(QPaintEvent *) { int wWidth = width() - 1; int wHeight = height() - 1; QPainter p(this); QPalette appPalette = QApplication::palette(); // Antialiasing is not a good idea here, because // the grid will drift one pixel to any side due to rounding of int // FIXME: let's user tell the last word (in config) //p.setRenderHint(QPainter::Antialiasing); // fill with color to show widget bounds p.fillRect(rect(), appPalette.color(QPalette::Base)); // draw background if (!d->m_pix.isNull()) { if (d->m_pixmapDirty || !d->m_pixmapCache) { delete d->m_pixmapCache; d->m_pixmapCache = new QPixmap(width(), height()); QPainter cachePainter(d->m_pixmapCache); cachePainter.scale(1.0*width() / d->m_pix.width(), 1.0*height() / d->m_pix.height()); cachePainter.drawPixmap(0, 0, d->m_pix); d->m_pixmapDirty = false; } p.drawPixmap(0, 0, *d->m_pixmapCache); } d->drawGrid(p, wWidth, wHeight); KisConfig cfg; if (cfg.antialiasCurves()) p.setRenderHint(QPainter::Antialiasing); // Draw curve. double curY; double normalizedX; int x; QPolygonF poly; p.setPen(QPen(appPalette.color(QPalette::Text), 2, Qt::SolidLine)); for (x = 0 ; x < wWidth ; x++) { normalizedX = double(x) / wWidth; curY = wHeight - d->m_curve.value(normalizedX) * wHeight; /** * Keep in mind that QLineF rounds doubles * to ints mathematically, not just rounds down * like in C */ poly.append(QPointF(x, curY)); } poly.append(QPointF(x, wHeight - d->m_curve.value(1.0) * wHeight)); p.drawPolyline(poly); QPainterPath fillCurvePath; QPolygonF fillPoly = poly; fillPoly.append(QPoint(rect().width(), rect().height())); fillPoly.append(QPointF(0,rect().height())); // add a couple points to the edges so it fills in below always QColor fillColor = appPalette.color(QPalette::Text); fillColor.setAlphaF(0.2); fillCurvePath.addPolygon(fillPoly); p.fillPath(fillCurvePath, fillColor); // Drawing curve handles. double curveX; double curveY; if (!d->m_readOnlyMode) { for (int i = 0; i < d->m_curve.points().count(); ++i) { curveX = d->m_curve.points().at(i).x(); curveY = d->m_curve.points().at(i).y(); if (i == d->m_grab_point_index) { p.setPen(QPen(appPalette.color(QPalette::Text), 6, Qt::SolidLine)); p.drawEllipse(QRectF(curveX * wWidth - 2, wHeight - 2 - curveY * wHeight, 4, 4)); } else { p.setPen(QPen(appPalette.color(QPalette::Text), 2, Qt::SolidLine)); p.drawEllipse(QRectF(curveX * wWidth - 3, wHeight - 3 - curveY * wHeight, 6, 6)); } } } // add border around widget to help contain everything QPainterPath widgetBoundsPath; widgetBoundsPath.addRect(rect()); p.strokePath(widgetBoundsPath, appPalette.color(QPalette::Text)); } void KisCurveWidget::mousePressEvent(QMouseEvent * e) { if (d->m_readOnlyMode) return; if (e->button() != Qt::LeftButton) return; double x = e->pos().x() / (double)(width() - 1); double y = 1.0 - e->pos().y() / (double)(height() - 1); int closest_point_index = d->nearestPointInRange(QPointF(x, y), width(), height()); if (closest_point_index < 0) { QPointF newPoint(x, y); if (!d->jumpOverExistingPoints(newPoint, -1)) return; d->m_grab_point_index = d->m_curve.addPoint(newPoint); emit pointSelectedChanged(); } else { d->m_grab_point_index = closest_point_index; emit pointSelectedChanged(); } d->m_grabOriginalX = d->m_curve.points()[d->m_grab_point_index].x(); d->m_grabOriginalY = d->m_curve.points()[d->m_grab_point_index].y(); d->m_grabOffsetX = d->m_curve.points()[d->m_grab_point_index].x() - x; d->m_grabOffsetY = d->m_curve.points()[d->m_grab_point_index].y() - y; d->m_curve.setPoint(d->m_grab_point_index, QPointF(x + d->m_grabOffsetX, y + d->m_grabOffsetY)); d->m_draggedAwayPointIndex = -1; d->setState(ST_DRAG); d->setCurveModified(); } void KisCurveWidget::mouseReleaseEvent(QMouseEvent *e) { if (d->m_readOnlyMode) return; if (e->button() != Qt::LeftButton) return; setCursor(Qt::ArrowCursor); d->setState(ST_NORMAL); d->setCurveModified(); } void KisCurveWidget::mouseMoveEvent(QMouseEvent * e) { if (d->m_readOnlyMode) return; double x = e->pos().x() / (double)(width() - 1); double y = 1.0 - e->pos().y() / (double)(height() - 1); if (d->state() == ST_NORMAL) { // If no point is selected set the cursor shape if on top int nearestPointIndex = d->nearestPointInRange(QPointF(x, y), width(), height()); if (nearestPointIndex < 0) setCursor(Qt::ArrowCursor); else setCursor(Qt::CrossCursor); } else { // Else, drag the selected point bool crossedHoriz = e->pos().x() - width() > MOUSE_AWAY_THRES || e->pos().x() < -MOUSE_AWAY_THRES; bool crossedVert = e->pos().y() - height() > MOUSE_AWAY_THRES || e->pos().y() < -MOUSE_AWAY_THRES; bool removePoint = (crossedHoriz || crossedVert); if (!removePoint && d->m_draggedAwayPointIndex >= 0) { // point is no longer dragged away so reinsert it QPointF newPoint(d->m_draggedAwayPoint); d->m_grab_point_index = d->m_curve.addPoint(newPoint); d->m_draggedAwayPointIndex = -1; } if (removePoint && (d->m_draggedAwayPointIndex >= 0)) return; setCursor(Qt::CrossCursor); x += d->m_grabOffsetX; y += d->m_grabOffsetY; double leftX; double rightX; if (d->m_grab_point_index == 0) { leftX = 0.0; if (d->m_curve.points().count() > 1) rightX = d->m_curve.points()[d->m_grab_point_index + 1].x() - POINT_AREA; else rightX = 1.0; } else if (d->m_grab_point_index == d->m_curve.points().count() - 1) { leftX = d->m_curve.points()[d->m_grab_point_index - 1].x() + POINT_AREA; rightX = 1.0; } else { Q_ASSERT(d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_curve.points().count() - 1); // the 1E-4 addition so we can grab the dot later. leftX = d->m_curve.points()[d->m_grab_point_index - 1].x() + POINT_AREA; rightX = d->m_curve.points()[d->m_grab_point_index + 1].x() - POINT_AREA; } x = bounds(x, leftX, rightX); y = bounds(y, 0., 1.); d->m_curve.setPoint(d->m_grab_point_index, QPointF(x, y)); if (removePoint && d->m_curve.points().count() > 2) { d->m_draggedAwayPoint = d->m_curve.points()[d->m_grab_point_index]; d->m_draggedAwayPointIndex = d->m_grab_point_index; d->m_curve.removePoint(d->m_grab_point_index); d->m_grab_point_index = bounds(d->m_grab_point_index, 0, d->m_curve.points().count() - 1); emit pointSelectedChanged(); } d->setCurveModified(); } } KisCubicCurve KisCurveWidget::curve() { return d->m_curve; } void KisCurveWidget::setCurve(KisCubicCurve inlist) { d->m_curve = inlist; d->m_grab_point_index = qBound(0, d->m_grab_point_index, d->m_curve.points().count() - 1); emit pointSelectedChanged(); d->setCurveModified(); } void KisCurveWidget::leaveEvent(QEvent *) { } diff --git a/libs/ui/widgets/kis_curve_widget.h b/libs/ui/widgets/kis_curve_widget.h index a03225540e..4ee4330fa2 100644 --- a/libs/ui/widgets/kis_curve_widget.h +++ b/libs/ui/widgets/kis_curve_widget.h @@ -1,160 +1,160 @@ /* * Copyright (c) 2005 C. Boemann * Copyright (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_CURVE_WIDGET_H #define KIS_CURVE_WIDGET_H // Qt includes. #include #include #include #include #include #include #include #include #include #include class QSpinBox; class KisCubicCurve; /** * KisCurveWidget is a widget that shows a single curve that can be edited * by the user. The user can grab the curve and move it; this creates * a new control point. Control points can be deleted by selecting a point * and pressing the delete key. * * (From: http://techbase.kde.org/Projects/Widgets_and_Classes#KisCurveWidget) * KisCurveWidget allows editing of spline based y=f(x) curves. Handy for cases * where you want the user to control such things as tablet pressure * response, color transformations, acceleration by time, aeroplane lift *by angle of attack. */ class KRITAUI_EXPORT KisCurveWidget : public QWidget { Q_OBJECT Q_PROPERTY(bool pointSelected READ pointSelected NOTIFY pointSelectedChanged); public: friend class CurveEditorItem; /** * Create a new curve widget with a default curve, that is a straight * line from bottom-left to top-right. */ - KisCurveWidget(QWidget *parent = 0, Qt::WFlags f = 0); + KisCurveWidget(QWidget *parent = 0, Qt::WindowFlags f = 0); ~KisCurveWidget() override; /** * Reset the curve to the default shape */ void reset(void); /** * Enable the guide and set the guide color to the specified color. * * XXX: it seems that the guide feature isn't actually implemented yet? */ void setCurveGuide(const QColor & color); /** * Set a background pixmap. The background pixmap will be drawn under * the grid and the curve. * * XXX: or is the pixmap what is drawn to the left and bottom of the curve * itself? */ void setPixmap(const QPixmap & pix); QPixmap getPixmap(); void setBasePixmap(const QPixmap & pix); QPixmap getBasePixmap(); /** * Whether or not there is a point selected * This does NOT include the first and last points */ bool pointSelected() const; Q_SIGNALS: /** * Emitted whenever a control point has changed position. */ void modified(void); /** * Emitted whenever the status of whether a control point is selected or not changes */ void pointSelectedChanged(); protected Q_SLOTS: void inOutChanged(int); protected: void keyPressEvent(QKeyEvent *) override; void paintEvent(QPaintEvent *) override; void mousePressEvent(QMouseEvent * e) override; void mouseReleaseEvent(QMouseEvent * e) override; void mouseMoveEvent(QMouseEvent * e) override; void leaveEvent(QEvent *) override; void resizeEvent(QResizeEvent *e) override; public: /** * @return get a list with all defined points. If you want to know what the * y value for a given x is on the curve defined by these points, use getCurveValue(). * @see getCurveValue */ KisCubicCurve curve(); /** * Replace the current curve with a curve specified by the curve defined by the control * points in @param inlist. */ void setCurve(KisCubicCurve inlist); /** * Connect/disconnect external spinboxes to the curve * @min/@max - is the range for their values */ void setupInOutControls(QSpinBox *in, QSpinBox *out, int min, int max); void dropInOutControls(); /** * Handy function that creates new point in the middle * of the curve and sets focus on the m_intIn field, * so the user can move this point anywhere in a moment */ void addPointInTheMiddle(); private: class Private; Private * const d; }; #endif /* KIS_CURVE_WIDGET_H */ diff --git a/libs/ui/widgets/kis_gradient_slider_widget.cc b/libs/ui/widgets/kis_gradient_slider_widget.cc index f504dd178c..8f84608f55 100644 --- a/libs/ui/widgets/kis_gradient_slider_widget.cc +++ b/libs/ui/widgets/kis_gradient_slider_widget.cc @@ -1,225 +1,225 @@ /* * Copyright (c) 2004 Cyrille Berger * 2004 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "widgets/kis_gradient_slider_widget.h" #include #include #include #include #include #include #include #include #include #include #include #define MARGIN 5 #define HANDLE_SIZE 10 -KisGradientSliderWidget::KisGradientSliderWidget(QWidget *parent, const char* name, Qt::WFlags f) +KisGradientSliderWidget::KisGradientSliderWidget(QWidget *parent, const char* name, Qt::WindowFlags f) : QWidget(parent, f), m_currentSegment(0), m_selectedSegment(0), m_drag(0) { setObjectName(name); setMinimumHeight(30); m_segmentMenu = new QMenu(); m_segmentMenu->addAction(i18n("Split Segment"), this, SLOT(slotSplitSegment())); m_segmentMenu->addAction(i18n("Duplicate Segment"), this, SLOT(slotDuplicateSegment())); m_segmentMenu->addAction(i18n("Mirror Segment"), this, SLOT(slotMirrorSegment())); m_removeSegmentAction = new QAction(i18n("Remove Segment"), this); connect(m_removeSegmentAction, SIGNAL(triggered()), this, SLOT(slotRemoveSegment())); m_segmentMenu->addAction(m_removeSegmentAction); } void KisGradientSliderWidget::setGradientResource(KoSegmentGradient* agr) { m_autogradientResource = agr; m_selectedSegment = m_autogradientResource->segmentAt(0.0); emit sigSelectedSegment(m_selectedSegment); } void KisGradientSliderWidget::paintEvent(QPaintEvent* pe) { QWidget::paintEvent(pe); QPainter painter(this); painter.fillRect(rect(), palette().background()); painter.setPen(Qt::black); painter.drawRect(MARGIN, MARGIN, width() - 2 * MARGIN, height() - 2 * MARGIN - HANDLE_SIZE); if (m_autogradientResource) { QImage image = m_autogradientResource->generatePreview(width() - 2 * MARGIN - 2, height() - 2 * MARGIN - HANDLE_SIZE - 2); QPixmap pixmap(image.width(), image.height()); if (!image.isNull()) { painter.drawImage(MARGIN + 1, MARGIN + 1, image); } painter.fillRect(MARGIN + 1, height() - MARGIN - HANDLE_SIZE, width() - 2 * MARGIN, HANDLE_SIZE, QBrush(Qt::white)); if (m_selectedSegment) { QRect selection(qRound(m_selectedSegment->startOffset()*(double)(width() - 2 * MARGIN - 2)) + 6, height() - HANDLE_SIZE - MARGIN, qRound((m_selectedSegment->endOffset() - m_selectedSegment->startOffset())*(double)(width() - 12)), HANDLE_SIZE); painter.fillRect(selection, QBrush(palette().highlight())); } QPolygon triangle(3); QList handlePositions = m_autogradientResource->getHandlePositions(); int position; painter.setBrush(QBrush(Qt::black)); for (int i = 0; i < handlePositions.count(); i++) { position = qRound(handlePositions[i] * (double)(width() - 12)) + 6; triangle[0] = QPoint(position, height() - HANDLE_SIZE - MARGIN); triangle[1] = QPoint(position + (HANDLE_SIZE / 2 - 1), height() - MARGIN); triangle[2] = QPoint(position - (HANDLE_SIZE / 2 - 1), height() - MARGIN); painter.drawPolygon(triangle); } painter.setBrush(QBrush(Qt::white)); QList middleHandlePositions = m_autogradientResource->getMiddleHandlePositions(); for (int i = 0; i < middleHandlePositions.count(); i++) { position = qRound(middleHandlePositions[i] * (double)(width() - 12)) + 6; triangle[0] = QPoint(position, height() - HANDLE_SIZE - MARGIN); triangle[1] = QPoint(position + (HANDLE_SIZE / 2 - 2), height() - MARGIN); triangle[2] = QPoint(position - (HANDLE_SIZE / 2 - 2), height() - MARGIN); painter.drawPolygon(triangle); } } } void KisGradientSliderWidget::mousePressEvent(QMouseEvent * e) { if ((e->y() < MARGIN || e->y() > height() - MARGIN) || (e->x() < MARGIN || e->x() > width() - MARGIN) || e-> button() != Qt::LeftButton) { QWidget::mousePressEvent(e); return; } double t = (double)(e->x() - MARGIN) / (double)(width() - 2 * MARGIN); KoGradientSegment* segment = 0; segment = m_autogradientResource->segmentAt(t); if (segment != 0) { m_currentSegment = segment; QRect leftHandle(qRound(m_currentSegment->startOffset() *(double)(width() - 2*MARGIN - 2) + MARGIN - (HANDLE_SIZE / 2 - 1)), height() - HANDLE_SIZE, HANDLE_SIZE - 1, HANDLE_SIZE); QRect middleHandle(qRound(m_currentSegment->middleOffset() *(double)(width() - 2*MARGIN - 2) + MARGIN - (HANDLE_SIZE / 2 - 2)), height() - HANDLE_SIZE - MARGIN, HANDLE_SIZE - 1, HANDLE_SIZE); QRect rightHandle(qRound(m_currentSegment->endOffset() *(double)(width() - 2*MARGIN - 2) + MARGIN - (HANDLE_SIZE / 2 - 1)), height() - HANDLE_SIZE, HANDLE_SIZE - 1, HANDLE_SIZE); // Change the activation order of the handles to avoid deadlocks if (t > 0.5) { if (leftHandle.contains(e->pos())) m_drag = LEFT_DRAG; else if (middleHandle.contains(e->pos())) m_drag = MIDDLE_DRAG; else if (rightHandle.contains(e->pos())) m_drag = RIGHT_DRAG; } else { if (rightHandle.contains(e->pos())) m_drag = RIGHT_DRAG; else if (middleHandle.contains(e->pos())) m_drag = MIDDLE_DRAG; else if (leftHandle.contains(e->pos())) m_drag = LEFT_DRAG; } if (m_drag == NO_DRAG) { m_selectedSegment = m_currentSegment; emit sigSelectedSegment(m_selectedSegment); } } repaint(); } void KisGradientSliderWidget::mouseReleaseEvent(QMouseEvent * e) { Q_UNUSED(e); m_drag = NO_DRAG; } void KisGradientSliderWidget::mouseMoveEvent(QMouseEvent * e) { if ((e->y() < MARGIN || e->y() > height() - MARGIN) || (e->x() < MARGIN || e->x() > width() - MARGIN)) { QWidget::mouseMoveEvent(e); return; } double t = (double)(e->x() - MARGIN) / (double)(width() - 2 * MARGIN); switch (m_drag) { case RIGHT_DRAG: m_autogradientResource->moveSegmentEndOffset(m_currentSegment, t); break; case LEFT_DRAG: m_autogradientResource->moveSegmentStartOffset(m_currentSegment, t); break; case MIDDLE_DRAG: m_autogradientResource->moveSegmentMiddleOffset(m_currentSegment, t); break; } if (m_drag != NO_DRAG) emit sigChangedSegment(m_currentSegment); repaint(); } void KisGradientSliderWidget::contextMenuEvent(QContextMenuEvent * e) { m_removeSegmentAction->setEnabled(m_autogradientResource->removeSegmentPossible()); m_segmentMenu->popup(e->globalPos()); } void KisGradientSliderWidget::slotSplitSegment() { m_autogradientResource->splitSegment(m_selectedSegment); emit sigSelectedSegment(m_selectedSegment); repaint(); } void KisGradientSliderWidget::slotDuplicateSegment() { m_autogradientResource->duplicateSegment(m_selectedSegment); emit sigSelectedSegment(m_selectedSegment); repaint(); } void KisGradientSliderWidget::slotMirrorSegment() { m_autogradientResource->mirrorSegment(m_selectedSegment); emit sigSelectedSegment(m_selectedSegment); repaint(); } void KisGradientSliderWidget::slotRemoveSegment() { m_selectedSegment = m_autogradientResource->removeSegment(m_selectedSegment); emit sigSelectedSegment(m_selectedSegment); repaint(); } diff --git a/libs/ui/widgets/kis_gradient_slider_widget.h b/libs/ui/widgets/kis_gradient_slider_widget.h index e8b5b9aa11..533a62d87e 100644 --- a/libs/ui/widgets/kis_gradient_slider_widget.h +++ b/libs/ui/widgets/kis_gradient_slider_widget.h @@ -1,86 +1,86 @@ /* * Copyright (c) 2004 Cyrille Berger * 2004 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_GRADIENT_SLIDER_WIDGET_H_ #define _KIS_GRADIENT_SLIDER_WIDGET_H_ #include #include #include class QAction; class QMenu; class KoGradientSegment; class KoSegmentGradient; class KisGradientSliderWidget : public QWidget { Q_OBJECT public: - KisGradientSliderWidget(QWidget *parent = 0, const char* name = 0, Qt::WFlags f = 0); + KisGradientSliderWidget(QWidget *parent = 0, const char* name = 0, Qt::WindowFlags f = 0); public: void paintEvent(QPaintEvent *) override; void setGradientResource(KoSegmentGradient* agr); KoGradientSegment* selectedSegment() { return m_selectedSegment; } Q_SIGNALS: void sigSelectedSegment(KoGradientSegment*); void sigChangedSegment(KoGradientSegment*); protected: void mousePressEvent(QMouseEvent * e) override; void mouseReleaseEvent(QMouseEvent * e) override; void mouseMoveEvent(QMouseEvent * e) override; void contextMenuEvent(QContextMenuEvent * e) override; private Q_SLOTS: void slotSplitSegment(); void slotDuplicateSegment(); void slotMirrorSegment(); void slotRemoveSegment(); private: enum { NO_DRAG, LEFT_DRAG, RIGHT_DRAG, MIDDLE_DRAG }; enum { SPLIT_SEGMENT, DUPLICATE_SEGMENT, MIRROR_SEGMENT, REMOVE_SEGMENT }; KoSegmentGradient* m_autogradientResource; KoGradientSegment* m_currentSegment; KoGradientSegment* m_selectedSegment; QMenu* m_segmentMenu; int m_drag; QAction *m_removeSegmentAction; }; #endif diff --git a/libs/ui/widgets/kis_paintop_presets_save.cpp b/libs/ui/widgets/kis_paintop_presets_save.cpp index 9f6e807ec4..174b6a46cf 100644 --- a/libs/ui/widgets/kis_paintop_presets_save.cpp +++ b/libs/ui/widgets/kis_paintop_presets_save.cpp @@ -1,228 +1,228 @@ /* This file is part of the KDE project * Copyright (C) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "widgets/kis_paintop_presets_save.h" #include #include #include #include #include "KisImportExportManager.h" #include "QDesktopServices" #include "kis_resource_server_provider.h" KisPresetSaveWidget::KisPresetSaveWidget(QWidget * parent) : KisPaintOpPresetSaveDialog(parent) { // this is setting the area we will "capture" for saving the brush preset. It can potentially be a different // area that the entire scratchpad brushPresetThumbnailWidget->setCutoutOverlayRect(QRect(0, 0, brushPresetThumbnailWidget->height(), brushPresetThumbnailWidget->width())); // we will default to reusing the previous preset thumbnail // have that checked by default, hide the other elements, and load the last preset image connect(clearBrushPresetThumbnailButton, SIGNAL(clicked(bool)), brushPresetThumbnailWidget, SLOT(fillDefault())); connect(loadImageIntoThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadImageFromFile())); connect(loadScratchPadThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadScratchpadThumbnail())); connect(loadExistingThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadExistingThumbnail())); connect(savePresetButton, SIGNAL(clicked(bool)), this, SLOT(savePreset())); connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(close())); } KisPresetSaveWidget::~KisPresetSaveWidget() { } void KisPresetSaveWidget::scratchPadSetup(KisCanvasResourceProvider* resourceProvider) { m_resourceProvider = resourceProvider; brushPresetThumbnailWidget->setupScratchPad(m_resourceProvider, Qt::white); } void KisPresetSaveWidget::showDialog() { setModal(true); // set the name of the current brush preset area. KisPaintOpPresetSP preset = m_resourceProvider->currentPreset(); // UI will look a bit different if we are saving a new brush if (m_isSavingNewBrush) { setWindowTitle(i18n("Save New Brush Preset")); newBrushNameTexField->setVisible(true); clearBrushPresetThumbnailButton->setVisible(true); loadImageIntoThumbnailButton->setVisible(true); currentBrushNameLabel->setVisible(false); if (preset) { newBrushNameTexField->setText(preset->name().append(" ").append(i18n("Copy"))); } } else { setWindowTitle(i18n("Save Brush Preset")); if (preset) { currentBrushNameLabel->setText(preset->name()); } newBrushNameTexField->setVisible(false); currentBrushNameLabel->setVisible(true); } brushPresetThumbnailWidget->paintPresetImage(); show(); } void KisPresetSaveWidget::loadImageFromFile() { // create a dialog to retrieve an image file. KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); QString filename = dialog.filename(); // the filename() returns the entire path & file name, not just the file name if (filename != "") { // empty if "cancel" is pressed // take that file and load it into the thumbnail are const QImage imageToLoad(filename); brushPresetThumbnailWidget->fillTransparent(); // clear the background in case our new image has transparency brushPresetThumbnailWidget->paintCustomImage(imageToLoad); } } void KisPresetSaveWidget::loadScratchpadThumbnail() { brushPresetThumbnailWidget->paintCustomImage(scratchPadThumbnailArea); } void KisPresetSaveWidget::loadExistingThumbnail() { brushPresetThumbnailWidget->paintPresetImage(); } void KisPresetSaveWidget::setFavoriteResourceManager(KisFavoriteResourceManager * favManager) { m_favoriteResourceManager = favManager; } void KisPresetSaveWidget::savePreset() { KisPaintOpPresetSP curPreset = m_resourceProvider->currentPreset(); if (!curPreset) return; m_favoriteResourceManager->setBlockUpdates(true); KisPaintOpPresetSP oldPreset = curPreset->clone(); oldPreset->load(); KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); QString saveLocation = rServer->saveLocation(); // if we are saving a new brush, use what we type in for the input QString presetName = m_isSavingNewBrush ? newBrushNameTexField->text() : curPreset->name(); QString currentPresetFileName = saveLocation + presetName + curPreset->defaultFileExtension(); // if the preset already exists, make a back up of it if (rServer->resourceByName(presetName)) { QString currentDate = QDate::currentDate().toString(Qt::ISODate); QString currentTime = QTime::currentTime().toString(Qt::ISODate); QString presetFilename = saveLocation + presetName + "_backup_" + currentDate + "-" + currentTime + oldPreset->defaultFileExtension(); oldPreset->setFilename(presetFilename); oldPreset->setName(presetName); oldPreset->setPresetDirty(false); oldPreset->setValid(true); // add resource to the blacklist rServer->addResource(oldPreset); rServer->removeResourceAndBlacklist(oldPreset.data()); QStringList tags; tags = rServer->assignedTagsList(curPreset.data()); Q_FOREACH (const QString & tag, tags) { rServer->addTag(oldPreset.data(), tag); } } if (m_isSavingNewBrush) { KisPaintOpPresetSP newPreset = curPreset->clone(); newPreset->setFilename(currentPresetFileName); newPreset->setName(presetName); newPreset->setImage(brushPresetThumbnailWidget->cutoutOverlay()); newPreset->setPresetDirty(false); newPreset->setValid(true); rServer->addResource(newPreset); // trying to get brush preset to load after it is created emit resourceSelected(newPreset.data()); } else { if (curPreset->filename().contains(saveLocation)==false || curPreset->filename().contains(presetName)==false) { rServer->removeResourceAndBlacklist(curPreset.data()); curPreset->setFilename(currentPresetFileName); curPreset->setName(presetName); } if (!rServer->resourceByFilename(curPreset->filename())){ //this is necessary so that we can get the preset afterwards. rServer->addResource(curPreset, false, false); rServer->removeFromBlacklist(curPreset.data()); } curPreset->setImage(brushPresetThumbnailWidget->cutoutOverlay()); curPreset->save(); curPreset->load(); } // HACK ALERT! the server does not notify the observers // automatically, so we need to call theupdate manually! rServer->tagCategoryMembersChanged(); m_favoriteResourceManager->setBlockUpdates(false); close(); // we are done... so close the save brush dialog } void KisPresetSaveWidget::saveScratchPadThumbnailArea(QImage image) { scratchPadThumbnailArea = image; } void KisPresetSaveWidget::isSavingNewBrush(bool newBrush) { m_isSavingNewBrush = newBrush; } #include "moc_kis_paintop_presets_save.cpp" diff --git a/libs/ui/widgets/kis_scratch_pad_event_filter.cpp b/libs/ui/widgets/kis_scratch_pad_event_filter.cpp index 50b637a797..00321060ab 100644 --- a/libs/ui/widgets/kis_scratch_pad_event_filter.cpp +++ b/libs/ui/widgets/kis_scratch_pad_event_filter.cpp @@ -1,112 +1,112 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_scratch_pad_event_filter.h" #include "kis_scratch_pad.h" #include #include KisScratchPadEventFilter::KisScratchPadEventFilter(QWidget *parent) : QObject(parent), m_tabletPressed(false) { parent->installEventFilter(this); m_scratchPad = qobject_cast(parent); } void KisScratchPadEventFilter::setWidgetToDocumentTransform(const QTransform &transform) { m_widgetToDocument = transform; } QWidget* KisScratchPadEventFilter::parentWidget() { return static_cast(parent()); } KoPointerEvent* KisScratchPadEventFilter::createMouseEvent(QEvent *event) { QMouseEvent *mouseEvent = static_cast(event); return new KoPointerEvent(mouseEvent, m_widgetToDocument.map(mouseEvent->pos())); } KoPointerEvent* KisScratchPadEventFilter::createTabletEvent(QEvent *event) { QTabletEvent *tabletEvent = static_cast(event); - const QPointF pos = tabletEvent->hiResGlobalPos(); + const QPointF pos = tabletEvent->posF(); KoPointerEvent *ev = new KoPointerEvent(tabletEvent, m_widgetToDocument.map(pos)); ev->setTabletButton(Qt::LeftButton); return ev; } bool KisScratchPadEventFilter::eventFilter(QObject *obj, QEvent *event) { Q_UNUSED(obj); bool result = true; KoPointerEvent *ev = 0; switch(event->type()) { case QEvent::MouseButtonPress: if(m_tabletPressed) break; ev = createMouseEvent(event); m_scratchPad->pointerPress(ev); break; case QEvent::MouseButtonRelease: if(m_tabletPressed) break; ev = createMouseEvent(event); m_scratchPad->pointerRelease(ev); break; case QEvent::MouseMove: if(m_tabletPressed) break; ev = createMouseEvent(event); m_scratchPad->pointerMove(ev); break; case QEvent::TabletPress: // if(m_tabletPressed) break; m_tabletPressed = true; ev = createTabletEvent(event); m_scratchPad->pointerPress(ev); break; case QEvent::TabletRelease: // if(!m_tabletPressed) break; m_tabletPressed = false; ev = createTabletEvent(event); m_scratchPad->pointerRelease(ev); break; case QEvent::TabletMove: // if(!m_tabletPressed) break; ev = createTabletEvent(event); m_scratchPad->pointerMove(ev); break; default: result = false; } if(ev) { result = ev->isAccepted(); event->setAccepted(result); delete ev; } return result; } diff --git a/libs/ui/widgets/kis_stopgradient_slider_widget.cpp b/libs/ui/widgets/kis_stopgradient_slider_widget.cpp index 9ffcb95bed..fc5b52cf74 100644 --- a/libs/ui/widgets/kis_stopgradient_slider_widget.cpp +++ b/libs/ui/widgets/kis_stopgradient_slider_widget.cpp @@ -1,360 +1,360 @@ /* * Copyright (c) 2004 Cyrille Berger * 2016 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "widgets/kis_stopgradient_slider_widget.h" #include #include #include #include #include #include #include #include #include #include #include "kis_global.h" #include "kis_debug.h" #include "krita_utils.h" -KisStopGradientSliderWidget::KisStopGradientSliderWidget(QWidget *parent, Qt::WFlags f) +KisStopGradientSliderWidget::KisStopGradientSliderWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) , m_selectedStop(0) , m_drag(0) { QLinearGradient defaultGradient; m_defaultGradient.reset(KoStopGradient::fromQGradient(&defaultGradient)); setGradientResource(0); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); setMouseTracking(true); QWindow *window = this->window()->windowHandle(); if (window) { connect(window, SIGNAL(screenChanged(QScreen*)), SLOT(updateHandleSize())); } updateHandleSize(); } void KisStopGradientSliderWidget::updateHandleSize() { QFontMetrics fm(font()); const int h = fm.height(); m_handleSize = QSize(0.34 * h, h); } int KisStopGradientSliderWidget::handleClickTolerance() const { // the size of the default text! return m_handleSize.height(); } void KisStopGradientSliderWidget::setGradientResource(KoStopGradient* gradient) { m_gradient = gradient ? gradient : m_defaultGradient.data(); if (m_selectedStop >= 0) { m_selectedStop = qBound(0, m_selectedStop, m_gradient->stops().size() - 1); emit sigSelectedStop(m_selectedStop); } } void KisStopGradientSliderWidget::paintHandle(qreal position, const QColor &color, bool isSelected, QPainter *painter) { const QRect handlesRect = this->handlesStipeRect(); const int handleCenter = handlesRect.left() + position * handlesRect.width(); const int handlesHalfWidth = handlesRect.height() * 0.26; // = 1.0 / 0.66 * 0.34 / 2.0 <-- golden ratio QPolygon triangle(3); triangle[0] = QPoint(handleCenter, handlesRect.top()); triangle[1] = QPoint(handleCenter - handlesHalfWidth, handlesRect.bottom()); triangle[2] = QPoint(handleCenter + handlesHalfWidth, handlesRect.bottom()); const qreal lineWidth = 1.0; if (!isSelected) { painter->setPen(QPen(palette().text(), lineWidth)); painter->setBrush(QBrush(color)); painter->setRenderHint(QPainter::Antialiasing); painter->drawPolygon(triangle); } else { painter->setPen(QPen(palette().highlight(), 1.5 * lineWidth)); painter->setBrush(QBrush(color)); painter->setRenderHint(QPainter::Antialiasing); painter->drawPolygon(triangle); } } void KisStopGradientSliderWidget::paintEvent(QPaintEvent* pe) { QWidget::paintEvent(pe); QPainter painter(this); painter.setPen(Qt::black); const QRect previewRect = gradientStripeRect(); KritaUtils::renderExactRect(&painter, kisGrowRect(previewRect, 1)); painter.drawRect(previewRect); if (m_gradient) { QImage image = m_gradient->generatePreview(previewRect.width(), previewRect.height()); if (!image.isNull()) { painter.drawImage(previewRect.topLeft(), image); } QList handlePositions = m_gradient->stops(); for (int i = 0; i < handlePositions.count(); i++) { if (i == m_selectedStop) continue; paintHandle(handlePositions[i].first, handlePositions[i].second.toQColor(), false, &painter); } if (m_selectedStop >= 0) { paintHandle(handlePositions[m_selectedStop].first, handlePositions[m_selectedStop].second.toQColor(), true, &painter); } } } int findNearestHandle(qreal t, const qreal tolerance, const QList &stops) { int result = -1; qreal minDistance = tolerance; for (int i = 0; i < stops.size(); i++) { const KoGradientStop &stop = stops[i]; const qreal distance = qAbs(t - stop.first); if (distance < minDistance) { minDistance = distance; result = i; } } return result; } void KisStopGradientSliderWidget::mousePressEvent(QMouseEvent * e) { if (!allowedClickRegion(handleClickTolerance()).contains(e->pos())) { QWidget::mousePressEvent(e); return; } const QRect handlesRect = this->handlesStipeRect(); const qreal t = (qreal(e->x()) - handlesRect.x()) / handlesRect.width(); const QList stops = m_gradient->stops(); const int clickedStop = findNearestHandle(t, qreal(handleClickTolerance()) / handlesRect.width(), stops); if (clickedStop >= 0) { if (m_selectedStop != clickedStop) { m_selectedStop = clickedStop; emit sigSelectedStop(m_selectedStop); } m_drag = true; } else { insertStop(qBound(0.0, t, 1.0)); m_drag = true; } update(); updateCursor(e->pos()); } void KisStopGradientSliderWidget::mouseReleaseEvent(QMouseEvent * e) { Q_UNUSED(e); m_drag = false; updateCursor(e->pos()); } int getNewInsertPosition(const KoGradientStop &stop, const QList &stops) { int result = 0; for (int i = 0; i < stops.size(); i++) { if (stop.first <= stops[i].first) break; result = i + 1; } return result; } void KisStopGradientSliderWidget::mouseMoveEvent(QMouseEvent * e) { updateCursor(e->pos()); if (m_drag) { const QRect handlesRect = this->handlesStipeRect(); double t = (qreal(e->x()) - handlesRect.x()) / handlesRect.width(); QList stops = m_gradient->stops(); if (t < -0.1 || t > 1.1) { if (stops.size() > 2 && m_selectedStop >= 0) { m_removedStop = stops[m_selectedStop]; stops.removeAt(m_selectedStop); m_selectedStop = -1; } } else { if (m_selectedStop < 0) { m_removedStop.first = qBound(0.0, t, 1.0); const int newPos = getNewInsertPosition(m_removedStop, stops); stops.insert(newPos, m_removedStop); m_selectedStop = newPos; } else { KoGradientStop draggedStop = stops[m_selectedStop]; draggedStop.first = qBound(0.0, t, 1.0); stops.removeAt(m_selectedStop); const int newPos = getNewInsertPosition(draggedStop, stops); stops.insert(newPos, draggedStop); m_selectedStop = newPos; } } m_gradient->setStops(stops); emit sigSelectedStop(m_selectedStop); update(); } else { QWidget::mouseMoveEvent(e); } } void KisStopGradientSliderWidget::updateCursor(const QPoint &pos) { const bool isInAllowedRegion = allowedClickRegion(handleClickTolerance()).contains(pos); QCursor currentCursor; if (isInAllowedRegion) { const QRect handlesRect = this->handlesStipeRect(); const qreal t = (qreal(pos.x()) - handlesRect.x()) / handlesRect.width(); const QList stops = m_gradient->stops(); const int clickedStop = findNearestHandle(t, qreal(handleClickTolerance()) / handlesRect.width(), stops); if (clickedStop >= 0) { currentCursor = m_drag ? Qt::ClosedHandCursor : Qt::OpenHandCursor; } } if (currentCursor.shape() != Qt::ArrowCursor) { setCursor(currentCursor); } else { unsetCursor(); } } void KisStopGradientSliderWidget::insertStop(double t) { KIS_ASSERT_RECOVER(t >= 0 && t <= 1.0 ) { t = qBound(0.0, t, 1.0); } QList stops = m_gradient->stops(); KoColor color; m_gradient->colorAt(color, t); const KoGradientStop stop(t, color); const int newPos = getNewInsertPosition(stop, stops); stops.insert(newPos, stop); m_gradient->setStops(stops); m_selectedStop = newPos; emit sigSelectedStop(m_selectedStop); } QRect KisStopGradientSliderWidget::sliderRect() const { return QRect(QPoint(), size()).adjusted(m_handleSize.width(), 1, -m_handleSize.width(), -1); } QRect KisStopGradientSliderWidget::gradientStripeRect() const { const QRect rc = sliderRect(); return rc.adjusted(0, 0, 0, -m_handleSize.height()); } QRect KisStopGradientSliderWidget::handlesStipeRect() const { const QRect rc = sliderRect(); return rc.adjusted(0, rc.height() - m_handleSize.height(), 0, 0); } QRegion KisStopGradientSliderWidget::allowedClickRegion(int tolerance) const { QRegion result; result += sliderRect(); result += handlesStipeRect().adjusted(-tolerance, 0, tolerance, 0); return result; } int KisStopGradientSliderWidget::selectedStop() { return m_selectedStop; } void KisStopGradientSliderWidget::setSelectedStop(int selected) { m_selectedStop = selected; emit sigSelectedStop(m_selectedStop); update(); } int KisStopGradientSliderWidget::minimalHeight() const { QFontMetrics fm(font()); const int h = fm.height(); QStyleOptionToolButton opt; QSize sz = (style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(h, h), this). expandedTo(QApplication::globalStrut())); return sz.height() + m_handleSize.height(); } QSize KisStopGradientSliderWidget::sizeHint() const { const int h = minimalHeight(); return QSize(2 * h, h); } QSize KisStopGradientSliderWidget::minimumSizeHint() const { const int h = minimalHeight(); return QSize(h, h); } diff --git a/libs/ui/widgets/kis_stopgradient_slider_widget.h b/libs/ui/widgets/kis_stopgradient_slider_widget.h index b7d5c91f87..f00d7f83c4 100644 --- a/libs/ui/widgets/kis_stopgradient_slider_widget.h +++ b/libs/ui/widgets/kis_stopgradient_slider_widget.h @@ -1,82 +1,82 @@ /* * Copyright (c) 2004 Cyrille Berger * 2016 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_STOP_GRADIENT_SLIDER_WIDGET_H_ #define _KIS_STOP_GRADIENT_SLIDER_WIDGET_H_ #include #include #include #include #include class KisStopGradientSliderWidget : public QWidget { Q_OBJECT public: - KisStopGradientSliderWidget(QWidget *parent = 0, Qt::WFlags f = 0); + KisStopGradientSliderWidget(QWidget *parent = 0, Qt::WindowFlags f = 0); public: void paintEvent(QPaintEvent *) override; void setGradientResource(KoStopGradient* gradient); int selectedStop(); void setSelectedStop(int selected); QSize sizeHint() const override; QSize minimumSizeHint() const override; Q_SIGNALS: void sigSelectedStop(int stop); protected: void mousePressEvent(QMouseEvent * e) override; void mouseReleaseEvent(QMouseEvent * e) override; void mouseMoveEvent(QMouseEvent * e) override; private Q_SLOTS: void updateHandleSize(); private: void insertStop(double t); QRect sliderRect() const; QRect gradientStripeRect() const; QRect handlesStipeRect() const; QRegion allowedClickRegion(int tolerance) const; void updateCursor(const QPoint &pos); void paintHandle(qreal position, const QColor &color, bool isSelected, QPainter *painter); int handleClickTolerance() const; int minimalHeight() const; private: QScopedPointer m_defaultGradient; KoStopGradient* m_gradient; int m_selectedStop; KoGradientStop m_removedStop; bool m_drag; QSize m_handleSize; }; #endif diff --git a/libs/vectorimage/libwmf/WmfParser.cpp b/libs/vectorimage/libwmf/WmfParser.cpp index f2f31e328c..6539f5cd7e 100644 --- a/libs/vectorimage/libwmf/WmfParser.cpp +++ b/libs/vectorimage/libwmf/WmfParser.cpp @@ -1,1695 +1,1695 @@ /* This file is part of the KDE libraries * * Copyright (c) 1998 Stefan Taferner * 2001/2003 thierry lorthiois (lorthioist@wanadoo.fr) * 2007-2008 Jan Hambrecht * 2009-2011 Inge Wallin * With the help of WMF documentation by Caolan Mc Namara * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "WmfParser.h" #include "WmfAbstractBackend.h" #include #include #include #include #include #include #include #include #define DEBUG_BBOX 0 #define DEBUG_RECORDS 0 /** Namespace for Windows Metafile (WMF) classes */ namespace Libwmf { #if defined(DEBUG_RECORDS) // Used for debugging of records static const struct KoWmfFunc { const char *name; } koWmfFunc[] = { // index metafunc { "end" }, // 0 0x00 { "setBkColor" }, // 1 0x01 { "setBkMode" }, // 2 0x02 { "setMapMode" }, // 3 0x03 { "setRop" }, // 4 0x04 { "setRelAbs" }, // 5 0x05 { "setPolyFillMode" }, // 6 0x06 { "setStretchBltMode" }, // 7 0x07 { "setTextCharExtra" }, // 8 0x08 { "setTextColor" }, // 9 0x09 { "setTextJustification" }, // 10 0x0a { "setWindowOrg" }, // 11 0x0b { "setWindowExt" }, // 12 0x0c { "setViewportOrg" }, // 13 0x0d { "setViewportExt" }, // 14 0x0e { "offsetWindowOrg" }, // 15 0x0f { "scaleWindowExt" }, // 16 0x10 { "offsetViewportOrg" }, // 17 0x11 { "scaleViewportExt" }, // 18 0x12 { "lineTo" }, // 19 0x13 { "moveTo" }, // 20 0x14 { "excludeClipRect" }, // 21 0x15 { "intersectClipRect" }, // 22 0x16 { "arc" }, // 23 0x17 { "ellipse" }, // 24 0x18 { "floodfill" }, // 25 0x19 floodfill { "pie" }, // 26 0x1a { "rectangle" }, // 27 0x1b { "roundRect" }, // 28 0x1c { "patBlt" }, // 29 0x1d { "saveDC" }, // 30 0x1e { "setPixel" }, // 31 0x1f { "offsetClipRegion" }, // 32 0x20 { "textOut" }, // 33 0x21 { "bitBlt" }, // 34 0x22 { "stretchBlt" }, // 35 0x23 { "polygon" }, // 36 0x24 { "polyline" }, // 37 0x25 { "escape" }, // 38 0x26 { "restoreDC" }, // 39 0x27 { "fillRegion" }, // 40 0x28 { "frameRegion" }, // 41 0x29 { "invertRegion" }, // 42 0x2a { "paintRegion" }, // 43 0x2b { "selectClipRegion" }, // 44 0x2c { "selectObject" }, // 45 0x2d { "setTextAlign" }, // 46 0x2e { "noSuchRecord" }, // 47 0x2f { "chord" }, // 48 0x30 { "setMapperFlags" }, // 49 0x31 { "extTextOut" }, // 50 0x32 { "setDibToDev" }, // 51 0x33 { "selectPalette" }, // 52 0x34 { "realizePalette" }, // 53 0x35 { "animatePalette" }, // 54 0x36 { "setPalEntries" }, // 55 0x37 { "polyPolygon" }, // 56 0x38 { "resizePalette" }, // 57 0x39 { "noSuchRecord" }, // 58 0x3a { "noSuchRecord" }, // 59 0x3b { "noSuchRecord" }, // 60 0x3c { "noSuchRecord" }, // 61 0x3d { "noSuchRecord" }, // 62 0x3e { "unimplemented" }, // 63 0x3f { "dibBitBlt" }, // 64 0x40 { "dibStretchBlt" }, // 65 0x41 { "dibCreatePatternBrush" }, // 66 0x42 { "stretchDib" }, // 67 0x43 { "noSuchRecord" }, // 68 0x44 { "noSuchRecord" }, // 69 0x45 { "noSuchRecord" }, // 70 0x46 { "noSuchRecord" }, // 71 0x47 { "extFloodFill" }, // 72 0x48 { "setLayout" }, // 73 0x49 { "unimplemented" }, // 74 0x4a { "unimplemented" }, // 75 0x4b { "resetDC" }, // 76 0x4c { "startDoc" }, // 77 0x4d { "unimplemented" }, // 78 0x4e { "startPage" }, // 79 0x4f { "endPage" }, // 80 0x50 { "unimplemented" }, // 81 0x51 { "unimplemented" }, // 82 0x52 { "unimplemented" }, // 83 0x53 { "unimplemented" }, // 84 0x54 { "unimplemented" }, // 85 0x55 { "unimplemented" }, // 86 0x56 { "unimplemented" }, // 87 0x57 { "unimplemented" }, // 88 0x58 { "unimplemented" }, // 89 0x59 { "unimplemented" }, // 90 0x5a { "unimplemented" }, // 91 0x5b { "unimplemented" }, // 92 0x5c { "unimplemented" }, // 93 0x5d { "endDoc" }, // 94 0x5e { "unimplemented" }, // 95 0x5f { "deleteObject" }, // 96 0xf0 { "noSuchRecord" }, // 97 0xf1 { "noSuchRecord" }, // 98 0xf2 { "noSuchRecord" }, // 99 0xf3 { "noSuchRecord" }, // 100 0xf4 { "noSuchRecord" }, // 101 0xf5 { "noSuchRecord" }, // 102 0xf6 { "createPalette" }, // 103 0xf7 { "createBrush" }, // 104 0xf8 { "createPatternBrush" }, // 105 0xf9 { "createPenIndirect" }, // 106 0xfa { "createFontIndirect" }, // 107 0xfb { "createBrushIndirect" }, //108 0xfc { "createBitmapIndirect" }, //109 0xfd { "createBitmap" }, // 110 0xfe { "createRegion" } // 111 0xff }; #endif WmfParser::WmfParser() { mNbrFunc = 0; mValid = false; mStandard = false; mPlaceable = false; mEnhanced = false; mBuffer = 0; mObjHandleTab = 0; } WmfParser::~WmfParser() { if (mObjHandleTab != 0) { for (int i = 0 ; i < mNbrObject ; i++) { if (mObjHandleTab[i] != 0) delete mObjHandleTab[i]; } delete[] mObjHandleTab; } if (mBuffer != 0) { mBuffer->close(); delete mBuffer; } } bool WmfParser::load(const QByteArray& array) { // delete previous buffer if (mBuffer != 0) { mBuffer->close(); delete mBuffer; mBuffer = 0; } if (array.size() == 0) return false; // load into buffer mBuffer = new QBuffer(); mBuffer->setData(array); mBuffer->open(QIODevice::ReadOnly); // read and check the header WmfMetaHeader header; WmfEnhMetaHeader eheader; WmfPlaceableHeader pheader; // Contains a bounding box unsigned short checksum; int filePos; QDataStream stream(mBuffer); stream.setByteOrder(QDataStream::LittleEndian); mStackOverflow = false; mLayout = LAYOUT_LTR; mTextColor = Qt::black; mMapMode = MM_ANISOTROPIC; mValid = false; mStandard = false; mPlaceable = false; mEnhanced = false; // Initialize the bounding box. //mBBoxTop = 0; // The default origin is (0, 0). //mBBoxLeft = 0; mBBoxTop = 32767; mBBoxLeft = 32767; mBBoxRight = -32768; mBBoxBottom = -32768; mMaxWidth = 0; mMaxHeight = 0; #if DEBUG_RECORDS debugVectorImage << "--------------------------- Starting parsing WMF ---------------------------"; #endif stream >> pheader.key; if (pheader.key == (quint32)APMHEADER_KEY) { //----- Read placeable metafile header mPlaceable = true; #if DEBUG_RECORDS debugVectorImage << "Placeable header! Yessss!"; #endif stream >> pheader.handle; stream >> pheader.left; stream >> pheader.top; stream >> pheader.right; stream >> pheader.bottom; stream >> pheader.inch; stream >> pheader.reserved; stream >> pheader.checksum; checksum = calcCheckSum(&pheader); if (pheader.checksum != checksum) { warnVectorImage << "Checksum for placeable metafile header is incorrect ( actual checksum" << pheader.checksum << ", expected checksum" << checksum << ")"; return false; } stream >> header.fileType; stream >> header.headerSize; stream >> header.version; stream >> header.fileSize; stream >> header.numOfObjects; stream >> header.maxRecordSize; stream >> header.numOfParameters; mNbrObject = header.numOfObjects; // The bounding box of the WMF mBBoxLeft = pheader.left; mBBoxTop = pheader.top; mBBoxRight = pheader.right; mBBoxBottom = pheader.bottom; #if DEBUG_RECORDS debugVectorImage << "bounding box in header: " << mBBoxLeft << mBBoxTop << mBBoxRight << mBBoxBottom << "width, height: " << mBBoxRight - mBBoxLeft << mBBoxBottom - mBBoxTop; #endif mMaxWidth = abs(pheader.right - pheader.left); mMaxHeight = abs(pheader.bottom - pheader.top); mDpi = pheader.inch; } else { mBuffer->reset(); //----- Read as enhanced metafile header filePos = mBuffer->pos(); stream >> eheader.recordType; stream >> eheader.recordSize; stream >> eheader.boundsLeft; stream >> eheader.boundsTop; stream >> eheader.boundsRight; stream >> eheader.boundsBottom; stream >> eheader.frameLeft; stream >> eheader.frameTop; stream >> eheader.frameRight; stream >> eheader.frameBottom; stream >> eheader.signature; if (eheader.signature == ENHMETA_SIGNATURE) { mEnhanced = true; stream >> eheader.version; stream >> eheader.size; stream >> eheader.numOfRecords; stream >> eheader.numHandles; stream >> eheader.reserved; stream >> eheader.sizeOfDescription; stream >> eheader.offsetOfDescription; stream >> eheader.numPaletteEntries; stream >> eheader.widthDevicePixels; stream >> eheader.heightDevicePixels; stream >> eheader.widthDeviceMM; stream >> eheader.heightDeviceMM; } else { //----- Read as standard metafile header mStandard = true; mBuffer->seek(filePos); stream >> header.fileType; stream >> header.headerSize; stream >> header.version; stream >> header.fileSize; stream >> header.numOfObjects; stream >> header.maxRecordSize; stream >> header.numOfParameters; mNbrObject = header.numOfObjects; } } mOffsetFirstRecord = mBuffer->pos(); //----- Test header validity if (((header.headerSize == 9) && (header.numOfParameters == 0)) || (mPlaceable)) { // valid wmf file mValid = true; } else { debugVectorImage << "WmfParser : incorrect file format !"; } // check bounding rectangle for standard meta file if (mStandard && mValid) { // Note that this call can change mValid. createBoundingBox(stream); #if DEBUG_RECORDS debugVectorImage << "bounding box created by going through all records: " << mBBoxLeft << mBBoxTop << mBBoxRight << mBBoxBottom << "width, height: " << mBBoxRight - mBBoxLeft << mBBoxBottom - mBBoxTop; #endif } return mValid; } bool WmfParser::play(WmfAbstractBackend* backend) { if (!(mValid)) { debugVectorImage << "WmfParser::play : invalid WMF file"; return false; } if (mNbrFunc) { #if DEBUG_RECORDS if ((mStandard)) { debugVectorImage << "Standard :" << mBBoxLeft << "" << mBBoxTop << "" << mBBoxRight - mBBoxLeft << "" << mBBoxBottom - mBBoxTop; } else { debugVectorImage << "DPI :" << mDpi; debugVectorImage << "size (inch):" << (mBBoxRight - mBBoxLeft) / mDpi << "" << (mBBoxBottom - mBBoxTop) / mDpi; debugVectorImage << "size (mm):" << (mBBoxRight - mBBoxLeft) * 25.4 / mDpi << "" << (mBBoxBottom - mBBoxTop) * 25.4 / mDpi; } debugVectorImage << mValid << "" << mStandard << "" << mPlaceable; #endif } // Stack of handles mObjHandleTab = new KoWmfHandle* [ mNbrObject ]; for (int i = 0; i < mNbrObject ; i++) { mObjHandleTab[ i ] = 0; } mDeviceContext.reset(); quint16 recordType; quint32 size; int bufferOffset; // Create a stream from which the records will be read. QDataStream stream(mBuffer); stream.setByteOrder(QDataStream::LittleEndian); // Set the output backend. m_backend = backend; // Set some initial values. mDeviceContext.windowOrg = QPoint(0, 0); mDeviceContext.windowExt = QSize(1, 1); QRect bbox(QPoint(mBBoxLeft,mBBoxTop), QSize(mBBoxRight - mBBoxLeft, mBBoxBottom - mBBoxTop)); if (m_backend->begin(bbox)) { // Play WMF functions. mBuffer->seek(mOffsetFirstRecord); recordType = 1; while ((recordType) && (!mStackOverflow)) { int j = 1; bufferOffset = mBuffer->pos(); stream >> size; stream >> recordType; - + // mapping between n function and index of table 'metaFuncTab' // lower 8 digits of the function => entry in the table quint16 index = recordType & 0xff; if (index > 0x5F) { index -= 0x90; } #if defined(DEBUG_RECORDS) debugVectorImage << "Record = " << koWmfFunc[ index ].name << " (" << hex << recordType << ", index" << dec << index << ")"; #endif if (mNbrFunc) { // debug mode if ((j + 12) > mNbrFunc) { // output last 12 functions int offBuff = mBuffer->pos(); quint16 param; debugVectorImage << j << " :" << index << " :"; for (quint16 i = 0 ; i < (size - 3) ; i++) { stream >> param; debugVectorImage << param << ""; } debugVectorImage; mBuffer->seek(offBuff); } if (j >= mNbrFunc) { break; } j++; } // Execute the function and parse the record. switch (recordType & 0xff) { case (META_EOF & 0xff): // Don't need to do anything here. break; case (META_SETBKCOLOR & 0xff): { quint32 color; stream >> color; mDeviceContext.backgroundColor = qtColor(color); mDeviceContext.changedItems |= DCBgTextColor; } break; case (META_SETBKMODE & 0xff): { quint16 bkMode; stream >> bkMode; //debugVectorImage << "New bkMode: " << bkMode; mDeviceContext.bgMixMode = bkMode; mDeviceContext.changedItems |= DCBgMixMode; } break; case (META_SETMAPMODE & 0xff): { stream >> mMapMode; //debugVectorImage << "New mapmode: " << mMapMode; //mDeviceContext.FontMapMode = mMapMode;Not defined yet mDeviceContext.changedItems |= DCFontMapMode; } break; case (META_SETROP2 & 0xff): { quint16 rop; stream >> rop; m_backend->setCompositionMode(winToQtComposition(rop)); mDeviceContext.rop = rop; mDeviceContext.changedItems |= DCFgMixMode; } break; case (META_SETRELABS & 0xff): break; case (META_SETPOLYFILLMODE & 0xff): { stream >> mDeviceContext.polyFillMode; mDeviceContext.changedItems |= DCPolyFillMode; } break; case (META_SETSTRETCHBLTMODE & 0xff): case (META_SETTEXTCHAREXTRA & 0xff): break; case (META_SETTEXTCOLOR & 0xff): { quint32 color; stream >> color; mDeviceContext.foregroundTextColor = qtColor(color); mDeviceContext.changedItems |= DCFgTextColor; } break; case (META_SETTEXTJUSTIFICATION & 0xff): break; case (META_SETWINDOWORG & 0xff): { qint16 top, left; stream >> top >> left; m_backend->setWindowOrg(left, top); mDeviceContext.windowOrg = QPoint(left, top); #if DEBUG_RECORDS debugVectorImage <<"Org: (" << left <<"," << top <<")"; #endif } break; case (META_SETWINDOWEXT & 0xff): { qint16 width, height; // negative value allowed for width and height stream >> height >> width; #if DEBUG_RECORDS debugVectorImage <<"Ext: (" << width <<"," << height <<")"; #endif m_backend->setWindowExt(width, height); mDeviceContext.windowExt = QSize(width, height); } break; case (META_SETVIEWPORTORG & 0xff): { qint16 top, left; stream >> top >> left; m_backend->setViewportOrg(left, top); mDeviceContext.viewportOrg = QPoint(left, top); #if DEBUG_RECORDS debugVectorImage <<"Org: (" << left <<"," << top <<")"; #endif } break; case (META_SETVIEWPORTEXT & 0xff): { qint16 width, height; // Negative value allowed for width and height stream >> height >> width; #if DEBUG_RECORDS debugVectorImage <<"Ext: (" << width <<"," << height <<")"; #endif m_backend->setViewportExt(width, height); mDeviceContext.viewportExt = QSize(width, height); } break; case (META_OFFSETWINDOWORG & 0xff): { qint16 offTop, offLeft; stream >> offTop >> offLeft; m_backend->setWindowOrg(mDeviceContext.windowOrg.x() + offLeft, mDeviceContext.windowOrg.y() + offTop); mDeviceContext.windowOrg += QPoint(offLeft, offTop); } break; case (META_SCALEWINDOWEXT & 0xff): { // Use 32 bits in the calculations to not lose precision. qint32 width, height; qint16 heightDenum, heightNum, widthDenum, widthNum; stream >> heightDenum >> heightNum >> widthDenum >> widthNum; if ((widthDenum != 0) && (heightDenum != 0)) { width = (qint32(mDeviceContext.windowExt.width()) * widthNum) / widthDenum; height = (qint32(mDeviceContext.windowExt.height()) * heightNum) / heightDenum; m_backend->setWindowExt(width, height); mDeviceContext.windowExt = QSize(width, height); } //debugVectorImage <<"WmfParser::ScaleWindowExt :" << widthDenum <<"" << heightDenum; } break; case (META_OFFSETVIEWPORTORG & 0xff): { qint16 offTop, offLeft; stream >> offTop >> offLeft; m_backend->setViewportOrg(mDeviceContext.windowOrg.x() + offLeft, mDeviceContext.windowOrg.y() + offTop); mDeviceContext.viewportOrg += QPoint(offLeft, offTop); } break; case (META_SCALEVIEWPORTEXT & 0xff): { // Use 32 bits in the calculations to not lose precision. qint32 width, height; qint16 heightDenum, heightNum, widthDenum, widthNum; stream >> heightDenum >> heightNum >> widthDenum >> widthNum; if ((widthDenum != 0) && (heightDenum != 0)) { width = (qint32(mDeviceContext.windowExt.width()) * widthNum) / widthDenum; height = (qint32(mDeviceContext.windowExt.height()) * heightNum) / heightDenum; m_backend->setViewportExt(width, height); mDeviceContext.viewportExt = QSize(width, height); } //debugVectorImage <<"WmfParser::ScaleWindowExt :" << widthDenum <<"" << heightDenum; } break; // ---------------------------------------------------------------- // Drawing records case (META_LINETO & 0xff): { qint16 top, left; stream >> top >> left; m_backend->lineTo(mDeviceContext, left, top); } break; case (META_MOVETO & 0xff): { qint16 top, left; stream >> top >> left; mDeviceContext.currentPosition = QPoint(left, top); } break; case (META_EXCLUDECLIPRECT & 0xff): { qint16 top, left, right, bottom; stream >> bottom >> right >> top >> left; - + QRegion region = mDeviceContext.clipRegion; QRegion newRegion(left, top, right - left, bottom - top); if (region.isEmpty()) { // FIXME: I doubt that if the region is previously empty, // it should be set to the new region. /iw region = newRegion; } else { - region = region.subtract(newRegion); + region = region.subtracted(newRegion); } mDeviceContext.clipRegion = region; mDeviceContext.changedItems |= DCClipRegion; } break; case (META_INTERSECTCLIPRECT & 0xff): { qint16 top, left, right, bottom; stream >> bottom >> right >> top >> left; QRegion region = mDeviceContext.clipRegion; QRegion newRegion(left, top, right - left, bottom - top); if (region.isEmpty()) { // FIXME: I doubt that if the region is previously empty, // it should be set to the new region. /iw region = newRegion; } else { - region = region.intersect(newRegion); + region = region.intersected(newRegion); } mDeviceContext.clipRegion = region; mDeviceContext.changedItems |= DCClipRegion; } break; case (META_ARC & 0xff): { int xCenter, yCenter, angleStart, aLength; qint16 topEnd, leftEnd, topStart, leftStart; qint16 top, left, right, bottom; stream >> topEnd >> leftEnd >> topStart >> leftStart; stream >> bottom >> right >> top >> left; xCenter = left + ((right - left) / 2); yCenter = top + ((bottom - top) / 2); xyToAngle(leftStart - xCenter, yCenter - topStart, leftEnd - xCenter, yCenter - topEnd, angleStart, aLength); m_backend->drawArc(mDeviceContext, left, top, right - left, bottom - top, angleStart, aLength); } break; case (META_ELLIPSE & 0xff): { qint16 top, left, right, bottom; stream >> bottom >> right >> top >> left; m_backend->drawEllipse(mDeviceContext, left, top, right - left, bottom - top); } break; case (META_FLOODFILL & 0xff): break; case (META_PIE & 0xff): { int xCenter, yCenter, angleStart, aLength; qint16 topEnd, leftEnd, topStart, leftStart; qint16 top, left, right, bottom; stream >> topEnd >> leftEnd >> topStart >> leftStart; stream >> bottom >> right >> top >> left; xCenter = left + ((right - left) / 2); yCenter = top + ((bottom - top) / 2); xyToAngle(leftStart - xCenter, yCenter - topStart, leftEnd - xCenter, yCenter - topEnd, angleStart, aLength); m_backend->drawPie(mDeviceContext, left, top, right - left, bottom - top, angleStart, aLength); } break; case (META_RECTANGLE & 0xff): { qint16 top, left, right, bottom; stream >> bottom >> right >> top >> left; //debugVectorImage << left << top << right << bottom; m_backend->drawRect(mDeviceContext, left, top, right - left, bottom - top); } break; case (META_ROUNDRECT & 0xff): { int xRnd = 0, yRnd = 0; quint16 widthCorner, heightCorner; qint16 top, left, right, bottom; stream >> heightCorner >> widthCorner; stream >> bottom >> right >> top >> left; // convert (widthCorner, heightCorner) in percentage if ((right - left) != 0) xRnd = (widthCorner * 100) / (right - left); if ((bottom - top) != 0) yRnd = (heightCorner * 100) / (bottom - top); m_backend->drawRoundRect(mDeviceContext, left, top, right - left, bottom - top, xRnd, yRnd); } break; case (META_PATBLT & 0xff): { quint32 rasterOperation; quint16 height, width; qint16 y, x; stream >> rasterOperation; stream >> height >> width; stream >> y >> x; //debugVectorImage << "patBlt record" << hex << rasterOperation << dec // << x << y << width << height; m_backend->patBlt(mDeviceContext, x, y, width, height, rasterOperation); } break; case (META_SAVEDC & 0xff): m_backend->save(); break; case (META_SETPIXEL & 0xff): { qint16 left, top; quint32 color; stream >> color >> top >> left; m_backend->setPixel(mDeviceContext, left, top, qtColor(color)); } break; case (META_OFFSETCLIPRGN & 0xff): break; case (META_TEXTOUT & 0xff): { quint16 textLength; qint16 x, y; stream >> textLength; QByteArray text; text.resize(textLength); stream.readRawData(text.data(), textLength); // The string is always of even length, so if the actual data is // of uneven length, read an extra byte. if (textLength & 0x01) { quint8 dummy; stream >> dummy; } stream >> y; stream >> x; m_backend->drawText(mDeviceContext, x, y, text); } break; case (META_BITBLT & 0xff): case (META_STRETCHBLT & 0xff): break; case (META_POLYGON & 0xff): { quint16 num; stream >> num; QPolygon pa(num); pointArray(stream, pa); m_backend->drawPolygon(mDeviceContext, pa); } break; case (META_POLYLINE & 0xff): { quint16 num; stream >> num; QPolygon pa(num); pointArray(stream, pa); m_backend->drawPolyline(mDeviceContext, pa); } break; case (META_ESCAPE & 0xff): break; case (META_RESTOREDC & 0xff): { qint16 num; stream >> num; for (int i = 0; i > num ; i--) m_backend->restore(); } break; case (META_FILLREGION & 0xff): case (META_FRAMEREGION & 0xff): case (META_INVERTREGION & 0xff): case (META_PAINTREGION & 0xff): case (META_SELECTCLIPREGION & 0xff): break; case (META_SELECTOBJECT & 0xff): { quint16 idx; stream >> idx; if ((idx < mNbrObject) && (mObjHandleTab[ idx ] != 0)) mObjHandleTab[ idx ]->apply(&mDeviceContext); else debugVectorImage << "WmfParser::selectObject : selection of an empty object"; } break; case (META_SETTEXTALIGN & 0xff): stream >> mDeviceContext.textAlign; mDeviceContext.changedItems |= DCTextAlignMode; break; case (META_CHORD & 0xff): { int xCenter, yCenter, angleStart, aLength; qint16 topEnd, leftEnd, topStart, leftStart; qint16 top, left, right, bottom; stream >> topEnd >> leftEnd >> topStart >> leftStart; stream >> bottom >> right >> top >> left; xCenter = left + ((right - left) / 2); yCenter = top + ((bottom - top) / 2); xyToAngle(leftStart - xCenter, yCenter - topStart, leftEnd - xCenter, yCenter - topEnd, angleStart, aLength); m_backend->drawChord(mDeviceContext, left, top, right - left, bottom - top, angleStart, aLength); } break; case (META_SETMAPPERFLAGS & 0xff): break; case (META_EXTTEXTOUT & 0xff): { qint16 y, x; qint16 stringLength; quint16 fwOpts; qint16 top, left, right, bottom; // optional cliprect stream >> y; stream >> x; stream >> stringLength; stream >> fwOpts; // ETO_CLIPPED flag adds 4 parameters if (fwOpts & (ETO_CLIPPED | ETO_OPAQUE)) { // read the optional clip rect stream >> bottom >> right >> top >> left; } // Read the string. Note that it's padded to 16 bits. QByteArray text; text.resize(stringLength); stream.readRawData(text.data(), stringLength); if (stringLength & 0x01) { quint8 padding; stream >> padding; } #if DEBUG_RECORDS debugVectorImage << "text at" << x << y << "length" << stringLength << ':' << text; //debugVectorImage << "flags:" << hex << fwOpts << dec; debugVectorImage << "flags:" << fwOpts; debugVectorImage << "record length:" << size; #endif m_backend->drawText(mDeviceContext, x, y, text); } break; case (META_SETDIBTODEV & 0xff): case (META_SELECTPALETTE & 0xff): case (META_REALIZEPALETTE & 0xff): case (META_ANIMATEPALETTE & 0xff): case (META_SETPALENTRIES & 0xff): break; case (META_POLYPOLYGON & 0xff): { quint16 numberPoly; quint16 sizePoly; QList listPa; stream >> numberPoly; for (int i = 0 ; i < numberPoly ; i++) { stream >> sizePoly; listPa.append(QPolygon(sizePoly)); } // list of point array for (int i = 0; i < numberPoly; i++) { pointArray(stream, listPa[i]); } // draw polygon's m_backend->drawPolyPolygon(mDeviceContext, listPa); listPa.clear(); } break; case (META_RESIZEPALETTE & 0xff): break; case (META_DIBBITBLT & 0xff): { quint32 raster; qint16 topSrc, leftSrc, widthSrc, heightSrc; qint16 topDst, leftDst; stream >> raster; stream >> topSrc >> leftSrc >> heightSrc >> widthSrc; stream >> topDst >> leftDst; if (size > 11) { // DIB image QImage bmpSrc; if (dibToBmp(bmpSrc, stream, (size - 11) * 2)) { m_backend->setCompositionMode(winToQtComposition(raster)); m_backend->save(); if (widthSrc < 0) { // negative width => horizontal flip QMatrix m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F); m_backend->setMatrix(mDeviceContext, m, true); } if (heightSrc < 0) { // negative height => vertical flip QMatrix m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F); m_backend->setMatrix(mDeviceContext, m, true); } m_backend->drawImage(mDeviceContext, leftDst, topDst, bmpSrc, leftSrc, topSrc, widthSrc, heightSrc); m_backend->restore(); } } else { debugVectorImage << "WmfParser::dibBitBlt without image not implemented"; } } break; case (META_DIBSTRETCHBLT & 0xff): { quint32 raster; qint16 topSrc, leftSrc, widthSrc, heightSrc; qint16 topDst, leftDst, widthDst, heightDst; QImage bmpSrc; stream >> raster; stream >> heightSrc >> widthSrc >> topSrc >> leftSrc; stream >> heightDst >> widthDst >> topDst >> leftDst; if (dibToBmp(bmpSrc, stream, (size - 13) * 2)) { m_backend->setCompositionMode(winToQtComposition(raster)); m_backend->save(); if (widthDst < 0) { // negative width => horizontal flip QMatrix m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F); m_backend->setMatrix(mDeviceContext, m, true); } if (heightDst < 0) { // negative height => vertical flip QMatrix m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F); m_backend->setMatrix(mDeviceContext, m, true); } bmpSrc = bmpSrc.copy(leftSrc, topSrc, widthSrc, heightSrc); // TODO: scale the bitmap : QImage::scale(widthDst, heightDst) // is actually too slow m_backend->drawImage(mDeviceContext, leftDst, topDst, bmpSrc); m_backend->restore(); } } break; case (META_DIBCREATEPATTERNBRUSH & 0xff): { KoWmfPatternBrushHandle* handle = new KoWmfPatternBrushHandle; if (addHandle(handle)) { quint32 arg; QImage bmpSrc; stream >> arg; if (dibToBmp(bmpSrc, stream, (size - 5) * 2)) { handle->image = bmpSrc; handle->brush.setTextureImage(handle->image); } else { debugVectorImage << "WmfParser::dibCreatePatternBrush : incorrect DIB image"; } } } break; case (META_STRETCHDIB & 0xff): { quint32 raster; qint16 arg, topSrc, leftSrc, widthSrc, heightSrc; qint16 topDst, leftDst, widthDst, heightDst; QImage bmpSrc; stream >> raster >> arg; stream >> heightSrc >> widthSrc >> topSrc >> leftSrc; stream >> heightDst >> widthDst >> topDst >> leftDst; if (dibToBmp(bmpSrc, stream, (size - 14) * 2)) { m_backend->setCompositionMode(winToQtComposition(raster)); m_backend->save(); if (widthDst < 0) { // negative width => horizontal flip QMatrix m(-1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F); m_backend->setMatrix(mDeviceContext, m, true); } if (heightDst < 0) { // negative height => vertical flip QMatrix m(1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F); m_backend->setMatrix(mDeviceContext, m, true); } bmpSrc = bmpSrc.copy(leftSrc, topSrc, widthSrc, heightSrc); // TODO: scale the bitmap ( QImage::scale(param[ 8 ], param[ 7 ]) is actually too slow ) m_backend->drawImage(mDeviceContext, leftDst, topDst, bmpSrc); m_backend->restore(); } } break; case (META_EXTFLOODFILL & 0xff): break; case (META_SETLAYOUT & 0xff): { quint16 layout; quint16 reserved; // negative value allowed for width and height stream >> layout >> reserved; #if DEBUG_RECORDS debugVectorImage << "layout=" << layout; #endif mLayout = (WmfLayout)layout; mDeviceContext.layoutMode = mLayout; mDeviceContext.changedItems |= DCLayoutMode; } break; case (META_DELETEOBJECT & 0xff): { quint16 idx; stream >> idx; deleteHandle(idx); } break; case (META_CREATEPALETTE & 0xff): // Unimplemented createEmptyObject(); break; case (META_CREATEBRUSH & 0xff): case (META_CREATEPATTERNBRUSH & 0xff): break; case (META_CREATEPENINDIRECT & 0xff): { // TODO: userStyle and alternateStyle quint32 color; quint16 style, width, arg; KoWmfPenHandle* handle = new KoWmfPenHandle; if (addHandle(handle)) { stream >> style >> width >> arg >> color; // set the style defaults handle->pen.setStyle(Qt::SolidLine); handle->pen.setCapStyle(Qt::RoundCap); handle->pen.setJoinStyle(Qt::RoundJoin); const int PenStyleMask = 0x0000000F; const int PenCapMask = 0x00000F00; const int PenJoinMask = 0x0000F000; quint16 penStyle = style & PenStyleMask; if (penStyle < 7) handle->pen.setStyle(koWmfStylePen[ penStyle ]); else debugVectorImage << "WmfParser::createPenIndirect: invalid pen" << style; quint16 capStyle = (style & PenCapMask) >> 8; if (capStyle < 3) handle->pen.setCapStyle(koWmfCapStylePen[ capStyle ]); else debugVectorImage << "WmfParser::createPenIndirect: invalid pen cap style" << style; quint16 joinStyle = (style & PenJoinMask) >> 12; if (joinStyle < 3) handle->pen.setJoinStyle(koWmfJoinStylePen[ joinStyle ]); else debugVectorImage << "WmfParser::createPenIndirect: invalid pen join style" << style; handle->pen.setColor(qtColor(color)); handle->pen.setWidth(width); debugVectorImage << "Creating pen" << handle->pen; } } break; case (META_CREATEFONTINDIRECT & 0xff): { qint16 height; // Height of the character cell qint16 width; // Average width (not used) qint16 escapement; // The rotation of the text in 1/10th degrees qint16 orientation; // The rotation of each character quint16 weight, property, fixedPitch, arg; KoWmfFontHandle* handle = new KoWmfFontHandle; if (addHandle(handle)) { stream >> height >> width; stream >> escapement >> orientation; stream >> weight >> property >> arg >> arg; stream >> fixedPitch; //debugVectorImage << height << width << weight << property; // text rotation (in 1/10 degree) handle->font.setFixedPitch(((fixedPitch & 0x01) == 0)); handle->escapement = escapement; handle->orientation = orientation; // A negative height means to use device units. //debugVectorImage << "Font height:" << height; handle->height = height; // FIXME: For some reason this value needs to be multiplied by // a factor. 0.6 seems to give a good result, but why?? // ANSWER(?): The doc says the height is the height of the character cell. // But normally the font height is only the height above the // baseline, isn't it? handle->font.setPointSize(qAbs(height) * 6 / 10); if (weight == 0) weight = QFont::Normal; else { // Linear transform between MS weights to Qt weights // MS: 400=normal, 700=bold // Qt: 50=normal, 75=bold // This makes the line cross x=0 at y=50/3. (x=MS weight, y=Qt weight) // // FIXME: Is this a linear relationship? weight = (50 + 3 * ((weight * (75-50))/(700-400))) / 3; } handle->font.setWeight(weight); handle->font.setItalic((property & 0x01)); handle->font.setUnderline((property & 0x100)); // TODO: Strikethrough // font name int maxChar = (size - 12) * 2; char* nameFont = new char[maxChar]; stream.readRawData(nameFont, maxChar); handle->font.setFamily(nameFont); delete[] nameFont; } } break; case (META_CREATEBRUSHINDIRECT & 0xff): { Qt::BrushStyle style; quint16 sty, arg2; quint32 color; KoWmfBrushHandle* handle = new KoWmfBrushHandle; if (addHandle(handle)) { stream >> sty >> color >> arg2; if (sty == 2) { if (arg2 < 6) style = koWmfHatchedStyleBrush[ arg2 ]; else { debugVectorImage << "WmfParser::createBrushIndirect: invalid hatched brush" << arg2; style = Qt::SolidPattern; } } else { if (sty < 9) style = koWmfStyleBrush[ sty ]; else { debugVectorImage << "WmfParser::createBrushIndirect: invalid brush" << sty; style = Qt::SolidPattern; } } handle->brush.setStyle(style); handle->brush.setColor(qtColor(color)); } } break; #if 0 - UNSPECIFIED in the Spec: + UNSPECIFIED in the Spec: { &WmfParser::createBitmapIndirect, "createBitmapIndirect" }, //109 0xfd { &WmfParser::createBitmap, "createBitmap" }, // 110 0xfe #endif case (META_CREATEREGION & 0xff): // FIXME: Unimplemented createEmptyObject(); break; default: // function outside WMF specification errorVectorImage << "BROKEN WMF file: Record number" << hex << recordType << dec << " index " << index; mValid = false; break; } mBuffer->seek(bufferOffset + (size << 1)); } // Let the backend clean up it's internal state. m_backend->end(); } for (int i = 0 ; i < mNbrObject ; i++) { if (mObjHandleTab[ i ] != 0) delete mObjHandleTab[ i ]; } delete[] mObjHandleTab; mObjHandleTab = 0; return true; } //----------------------------------------------------------------------------- void WmfParser::createBoundingBox(QDataStream &stream) { // Check bounding rectangle for standard meta file. // This calculation is done in device coordinates. - if (!mStandard || !mValid) + if (!mStandard || !mValid) return; bool windowExtIsSet = false; bool viewportExtIsSet = false; quint16 recordType = 1; quint32 size; int filePos; // Search for records setWindowOrg and setWindowExt to // determine what the total bounding box of this WMF is. // This initialization assumes that setWindowOrg comes before setWindowExt. qint16 windowOrgX = 0; qint16 windowOrgY = 0; qint16 windowWidth = 0; qint16 windowHeight = 0; qint16 viewportOrgX = 0; qint16 viewportOrgY = 0; qint16 viewportWidth = 0; qint16 viewportHeight = 0; bool bboxRecalculated = false; while (recordType) { filePos = mBuffer->pos(); stream >> size >> recordType; if (size == 0) { debugVectorImage << "WmfParser: incorrect file!"; mValid = 0; return; } bool doRecalculateBBox = false; qint16 orgX = 0; qint16 orgY = 0; qint16 extX = 0; qint16 extY = 0; switch (recordType &= 0xFF) { case 11: // setWindowOrg { stream >> windowOrgY >> windowOrgX; #if DEBUG_BBOX debugVectorImage << "setWindowOrg" << windowOrgX << windowOrgY; #endif if (!windowExtIsSet) break; // The bounding box doesn't change just because we get // a new window. Remember we are working in device // (viewport) coordinates when deciding the bounding // box. if (viewportExtIsSet) break; // If there is no viewport, then use the window ext as // size, and (0, 0) as origin. // // FIXME: Handle the case where the window is defined // first and then the viewport, without any // drawing in between. If that happens, I // don't think that the window definition // should influence the bounding box. orgX = 0; orgY = 0; extX = windowWidth; extY = windowHeight; } break; case 12: // setWindowExt { stream >> windowHeight >> windowWidth; windowExtIsSet = true; bboxRecalculated = false; #if DEBUG_BBOX debugVectorImage << "setWindowExt" << windowWidth << windowHeight << "(viewportOrg = " << viewportOrgX << viewportOrgY << ")"; #endif // If the viewport is set, then a changed window // changes nothing in the bounding box. if (viewportExtIsSet) break; bboxRecalculated = false; // Collect the maximum width and height. if (abs(windowWidth - windowOrgX) > mMaxWidth) mMaxWidth = abs(windowWidth - windowOrgX); if (abs(windowHeight - windowOrgY) > mMaxHeight) mMaxHeight = abs(windowHeight - windowOrgY); orgX = 0; orgY = 0; extX = windowWidth; extY = windowHeight; } break; case 13: //setViewportOrg { stream >> viewportOrgY >> viewportOrgX; bboxRecalculated = false; #if DEBUG_BBOX debugVectorImage << "setViewportOrg" << viewportOrgX << viewportOrgY; #endif orgX = viewportOrgX; orgY = viewportOrgY; if (viewportExtIsSet) { extX = viewportWidth; extY = viewportHeight; } else { // If the viewportExt is not set, then either a // subsequent setViewportExt will set it, or the - // windowExt will be used instead. + // windowExt will be used instead. extX = windowWidth; extY = windowHeight; } break; // FIXME: Handle the case where the org changes but // there is no subsequent Ext change (should be // rather uncommon). } break; case 14: //setViewportExt { stream >> viewportHeight >> viewportWidth; viewportExtIsSet = true; bboxRecalculated = false; #if DEBUG_BBOX debugVectorImage << "setViewportExt" << viewportWidth << viewportHeight; #endif orgX = viewportOrgX; orgY = viewportOrgY; extX = viewportWidth; extY = viewportHeight; } break; // FIXME: Also support: - // ScaleWindowExt, ScaleViewportExt, + // ScaleWindowExt, ScaleViewportExt, // OffsetWindowOrg, OffsetViewportOrg // The following are drawing commands. It is only when // there is an actual drawing command that we should check // the bounding box. It seems that some WMF files have // lots of changes of the window or viewports but without // any drawing commands in between. These changes should // not affect the bounding box. case 19: // lineTo //case 20: // moveTo case 23: // arc case 24: // ellipse case 26: // pie case 27: // rectangle case 28: // roundRect case 29: // patBlt case 31: // setPixel case 33: // textOut case 34: // bitBlt case 36: // polygon case 37: // polyline //case 38: // escape FIXME: is this a drawing command? case 40: // fillRegion case 41: case 42: case 43: case 44: case 48: // chord case 50: // extTextOut case 56: // polyPolygon case 64: // dibBitBlt case 65: // dibStretchBlt case 67: // stretchDib case 72: // extFloodFill #if DEBUG_BBOX debugVectorImage << "drawing record: " << (recordType & 0xff); #endif doRecalculateBBox = true; break; default: ; } // Recalculate the BBox if it was indicated above that it should be. if (doRecalculateBBox && !bboxRecalculated) { #if DEBUG_BBOX debugVectorImage << "Recalculating BBox"; #endif // If we have a viewport, always use that one. if (viewportExtIsSet) { orgX = viewportOrgX; orgY = viewportOrgY; extX = viewportWidth; extY = viewportHeight; } else { // If there is no defined viewport, then use the // window as the fallback viewport. But only the size, // the origin is always (0, 0). orgX = 0; orgY = 0; extX = qAbs(windowWidth); extY = qAbs(windowHeight); } // If ext < 0, switch the org and org+ext if (extX < 0) { orgX += extX; extX = -extX; } if (extY < 0) { orgY += extY; extY = -extY; } // At this point, the ext is always >= 0, i.e. org <= org+ext #if DEBUG_BBOX debugVectorImage << orgX << orgY << extX << extY; #endif if (orgX < mBBoxLeft) mBBoxLeft = orgX; if (orgY < mBBoxTop) mBBoxTop = orgY; if (orgX + extX > mBBoxRight) mBBoxRight = orgX + extX; if (orgY + extY > mBBoxBottom) mBBoxBottom = orgY + extY; bboxRecalculated = true; } #if DEBUG_BBOX if (isOrgOrExt) { debugVectorImage << " mBBoxTop = " << mBBoxTop; debugVectorImage << "mBBoxLeft = " << mBBoxLeft << " mBBoxRight = " << mBBoxRight; debugVectorImage << " MBBoxBotton = " << mBBoxBottom; debugVectorImage << "Max width,height = " << mMaxWidth << mMaxHeight; } #endif mBuffer->seek(filePos + (size << 1)); } } //----------------------------------------------------------------------------- // Object handle void WmfParser::createEmptyObject() { // allocation of an empty object (to keep object counting in sync) KoWmfPenHandle* handle = new KoWmfPenHandle; addHandle(handle); } //----------------------------------------------------------------------------- // Misc functions quint16 WmfParser::calcCheckSum(WmfPlaceableHeader* apmfh) { quint16* lpWord; quint16 wResult, i; // Start with the first word wResult = *(lpWord = (quint16*)(apmfh)); // XOR in each of the other 9 words for (i = 1; i <= 9; i++) { wResult ^= lpWord[ i ]; } return wResult; } //----------------------------------------------------------------------------- // Utilities and conversion Wmf -> Qt bool WmfParser::addHandle(KoWmfHandle* handle) { int idx; for (idx = 0; idx < mNbrObject ; idx++) { if (mObjHandleTab[ idx ] == 0) break; } if (idx < mNbrObject) { mObjHandleTab[ idx ] = handle; return true; } else { delete handle; mStackOverflow = true; debugVectorImage << "WmfParser::addHandle : stack overflow = broken file !"; return false; } } void WmfParser::deleteHandle(int idx) { if ((idx < mNbrObject) && (mObjHandleTab[idx] != 0)) { delete mObjHandleTab[ idx ]; mObjHandleTab[ idx ] = 0; } else { debugVectorImage << "WmfParser::deletehandle() : bad index number"; } } void WmfParser::pointArray(QDataStream& stream, QPolygon& pa) { qint16 left, top; int i, max; for (i = 0, max = pa.size() ; i < max ; i++) { stream >> left >> top; pa.setPoint(i, left, top); } } void WmfParser::xyToAngle(int xStart, int yStart, int xEnd, int yEnd, int& angleStart, int& angleLength) { double aStart, aLength; aStart = atan2((double)yStart, (double)xStart); aLength = atan2((double)yEnd, (double)xEnd) - aStart; angleStart = (int)((aStart * 2880) / 3.14166); angleLength = (int)((aLength * 2880) / 3.14166); if (angleLength < 0) angleLength = 5760 + angleLength; } QPainter::CompositionMode WmfParser::winToQtComposition(quint16 param) const { if (param < 17) return koWmfOpTab16[ param ]; else return QPainter::CompositionMode_Source; } QPainter::CompositionMode WmfParser::winToQtComposition(quint32 param) const { /* TODO: Ternary raster operations 0x00C000CA dest = (source AND pattern) 0x00F00021 dest = pattern 0x00FB0A09 dest = DPSnoo 0x005A0049 dest = pattern XOR dest */ int i; for (i = 0 ; i < 15 ; i++) { if (koWmfOpTab32[ i ].winRasterOp == param) break; } if (i < 15) return koWmfOpTab32[ i ].qtRasterOp; else return QPainter::CompositionMode_SourceOver; } bool WmfParser::dibToBmp(QImage& bmp, QDataStream& stream, quint32 size) { typedef struct _BMPFILEHEADER { quint16 bmType; quint32 bmSize; quint16 bmReserved1; quint16 bmReserved2; quint32 bmOffBits; } BMPFILEHEADER; int sizeBmp = size + 14; QByteArray pattern; // BMP header and DIB data pattern.resize(sizeBmp); pattern.fill(0); stream.readRawData(pattern.data() + 14, size); // add BMP header BMPFILEHEADER* bmpHeader; bmpHeader = (BMPFILEHEADER*)(pattern.data()); bmpHeader->bmType = 0x4D42; bmpHeader->bmSize = sizeBmp; // if ( !bmp.loadFromData( (const uchar*)bmpHeader, pattern.size(), "BMP" ) ) { if (!bmp.loadFromData(pattern, "BMP")) { debugVectorImage << "WmfParser::dibToBmp: invalid bitmap"; return false; } else { return true; } } } diff --git a/libs/widgets/KoLineStyleModel.cpp b/libs/widgets/KoLineStyleModel.cpp index 6260d17b74..eb030806f2 100644 --- a/libs/widgets/KoLineStyleModel.cpp +++ b/libs/widgets/KoLineStyleModel.cpp @@ -1,101 +1,107 @@ /* This file is part of the KDE project * Copyright (C) 2007 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoLineStyleModel_p.h" #include KoLineStyleModel::KoLineStyleModel(QObject *parent) : QAbstractListModel(parent), m_hasTempStyle(false) { // add standard dash patterns for (int i = Qt::NoPen; i < Qt::CustomDashLine; i++) { QPen pen(static_cast(i)); m_styles << pen.dashPattern(); } } int KoLineStyleModel::rowCount(const QModelIndex &/*parent*/) const { return m_styles.count() + (m_hasTempStyle ? 1 : 0); } QVariant KoLineStyleModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); switch(role) { case Qt::DecorationRole: { QPen pen(Qt::black); pen.setWidth(2); if (index.row() < Qt::CustomDashLine) pen.setStyle(static_cast(index.row())); else if (index.row() < m_styles.count()) pen.setDashPattern(m_styles[index.row()]); else if (m_hasTempStyle) pen.setDashPattern(m_tempStyle); else pen.setStyle(Qt::NoPen); return QVariant(pen); } case Qt::SizeHintRole: return QSize(100, 15); default: return QVariant(); } } bool KoLineStyleModel::addCustomStyle(const QVector &style) { if (m_styles.contains(style)) return false; m_styles.append(style); return true; } int KoLineStyleModel::setLineStyle(Qt::PenStyle style, const QVector &dashes) { // check if we select a standard or custom style if (style < Qt::CustomDashLine) { // a standard style m_hasTempStyle = false; - reset(); + beginResetModel(); + endResetModel(); + return style; } else if (style == Qt::CustomDashLine) { // a custom style -> check if already added int index = m_styles.indexOf(dashes, Qt::CustomDashLine); if (index < 0) { // not already added -> add temporarly m_tempStyle = dashes; m_hasTempStyle = true; - reset(); + beginResetModel(); + endResetModel(); + return m_styles.count(); } else { // already added -> return index m_hasTempStyle = false; - reset(); + beginResetModel(); + endResetModel(); + return index; } } return -1; } diff --git a/libs/widgets/KoResourceModel.cpp b/libs/widgets/KoResourceModel.cpp index a423abde35..194e90af38 100644 --- a/libs/widgets/KoResourceModel.cpp +++ b/libs/widgets/KoResourceModel.cpp @@ -1,325 +1,327 @@ /* This file is part of the KDE project * Copyright (C) 2008-2009 Jan Hambrecht * Copyright (c) 2013 Sascha Suelzer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoResourceModel.h" #include #include #include #include #include KoResourceModel::KoResourceModel(QSharedPointer resourceAdapter, QObject * parent) : KoResourceModelBase(parent) , m_resourceAdapter(resourceAdapter) , m_columnCount(4) { Q_ASSERT(m_resourceAdapter); m_resourceAdapter->connectToResourceServer(); connect(m_resourceAdapter.data(), SIGNAL(resourceAdded(KoResource*)), this, SLOT(resourceAdded(KoResource*))); connect(m_resourceAdapter.data(), SIGNAL(removingResource(KoResource*)), this, SLOT(resourceRemoved(KoResource*))); connect(m_resourceAdapter.data(), SIGNAL(resourceChanged(KoResource*)), this, SLOT(resourceChanged(KoResource*))); connect(m_resourceAdapter.data(), SIGNAL(tagsWereChanged()), this, SLOT(tagBoxEntryWasModified())); connect(m_resourceAdapter.data(), SIGNAL(tagCategoryWasAdded(QString)), this, SLOT(tagBoxEntryWasAdded(QString))); connect(m_resourceAdapter.data(), SIGNAL(tagCategoryWasRemoved(QString)), this, SLOT(tagBoxEntryWasRemoved(QString))); } KoResourceModel::~KoResourceModel() { if (!m_currentTag.isEmpty()) { KConfigGroup group = KSharedConfig::openConfig()->group("SelectedTags"); group.writeEntry(serverType(), m_currentTag); } } int KoResourceModel::rowCount( const QModelIndex &/*parent*/ ) const { int resourceCount = m_resourceAdapter->resources().count(); if (!resourceCount) return 0; return static_cast(ceil(static_cast(resourceCount) / m_columnCount)); } int KoResourceModel::columnCount ( const QModelIndex & ) const { return m_columnCount; } QVariant KoResourceModel::data( const QModelIndex &index, int role ) const { if( ! index.isValid() ) return QVariant(); switch( role ) { case Qt::DisplayRole: { KoResource * resource = static_cast(index.internalPointer()); if( ! resource ) return QVariant(); QString resName = i18n( resource->name().toUtf8().data()); return QVariant( resName ); } case KoResourceModel::TagsRole: { KoResource * resource = static_cast(index.internalPointer()); if( ! resource ) return QVariant(); if (m_resourceAdapter->assignedTagsList(resource).count()) { QString taglist = m_resourceAdapter->assignedTagsList(resource).join("
  • "); return QString("
  • %2
  • ").arg(taglist); } else { return QString(); } } case Qt::DecorationRole: { KoResource * resource = static_cast(index.internalPointer()); if( ! resource ) return QVariant(); return QVariant( resource->image() ); } case KoResourceModel::LargeThumbnailRole: { KoResource * resource = static_cast(index.internalPointer()); if( ! resource ) return QVariant(); QSize imageSize = resource->image().size(); QSize thumbSize( 100, 100 ); if(imageSize.height() > thumbSize.height() || imageSize.width() > thumbSize.width()) { qreal scaleW = static_cast( thumbSize.width() ) / static_cast( imageSize.width() ); qreal scaleH = static_cast( thumbSize.height() ) / static_cast( imageSize.height() ); qreal scale = qMin( scaleW, scaleH ); int thumbW = static_cast( imageSize.width() * scale ); int thumbH = static_cast( imageSize.height() * scale ); return QVariant(resource->image().scaled( thumbW, thumbH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); } else return QVariant(resource->image()); } default: return QVariant(); } } QModelIndex KoResourceModel::index ( int row, int column, const QModelIndex & ) const { int index = row * m_columnCount + column; const QList resources = m_resourceAdapter->resources(); if( index >= resources.count() || index < 0) return QModelIndex(); return createIndex( row, column, resources[index] ); } void KoResourceModel::doSafeLayoutReset(KoResource *activateAfterReformat) { emit beforeResourcesLayoutReset(activateAfterReformat); - reset(); + beginResetModel(); + endResetModel(); emit afterResourcesLayoutReset(); } void KoResourceModel::setColumnCount( int columnCount ) { if (columnCount != m_columnCount) { emit beforeResourcesLayoutReset(0); m_columnCount = columnCount; - reset(); + beginResetModel(); + endResetModel(); emit afterResourcesLayoutReset(); } } void KoResourceModel::resourceAdded(KoResource *resource) { int newIndex = m_resourceAdapter->resources().indexOf(resource); if (newIndex >= 0) { doSafeLayoutReset(0); } } void KoResourceModel::resourceRemoved(KoResource *resource) { Q_UNUSED(resource); doSafeLayoutReset(0); } void KoResourceModel::resourceChanged(KoResource* resource) { int resourceIndex = m_resourceAdapter->resources().indexOf(resource); int row = resourceIndex / columnCount(); int column = resourceIndex % columnCount(); QModelIndex modelIndex = index(row, column); if (!modelIndex.isValid()) { return; } emit dataChanged(modelIndex, modelIndex); } void KoResourceModel::tagBoxEntryWasModified() { m_resourceAdapter->updateServer(); emit tagBoxEntryModified(); } void KoResourceModel::tagBoxEntryWasAdded(const QString& tag) { emit tagBoxEntryAdded(tag); } void KoResourceModel::tagBoxEntryWasRemoved(const QString& tag) { emit tagBoxEntryRemoved(tag); } QModelIndex KoResourceModel::indexFromResource(KoResource* resource) const { int resourceIndex = m_resourceAdapter->resources().indexOf(resource); if (columnCount() > 0) { int row = resourceIndex / columnCount(); int column = resourceIndex % columnCount(); return index(row, column); } return QModelIndex(); } QString KoResourceModel::extensions() const { return m_resourceAdapter->extensions(); } void KoResourceModel::importResourceFile(const QString &filename) { m_resourceAdapter->importResourceFile(filename); } void KoResourceModel::importResourceFile(const QString & filename, bool fileCreation) { m_resourceAdapter->importResourceFile(filename, fileCreation); } bool KoResourceModel::removeResource(KoResource* resource) { return m_resourceAdapter->removeResource(resource); } void KoResourceModel::removeResourceFile(const QString &filename) { m_resourceAdapter->removeResourceFile(filename); } QStringList KoResourceModel::assignedTagsList(KoResource *resource) const { return m_resourceAdapter->assignedTagsList(resource); } void KoResourceModel::addTag(KoResource* resource, const QString& tag) { m_resourceAdapter->addTag(resource, tag); emit tagBoxEntryAdded(tag); } void KoResourceModel::deleteTag(KoResource *resource, const QString &tag) { m_resourceAdapter->deleteTag(resource, tag); } QStringList KoResourceModel::tagNamesList() const { return m_resourceAdapter->tagNamesList(); } QStringList KoResourceModel::searchTag(const QString& lineEditText) { return m_resourceAdapter->searchTag(lineEditText); } void KoResourceModel::searchTextChanged(const QString& searchString) { m_resourceAdapter->searchTextChanged(searchString); } void KoResourceModel::enableResourceFiltering(bool enable) { m_resourceAdapter->enableResourceFiltering(enable); } void KoResourceModel::setCurrentTag(const QString& currentTag) { m_currentTag = currentTag; m_resourceAdapter->setCurrentTag(currentTag); } void KoResourceModel::updateServer() { m_resourceAdapter->updateServer(); } int KoResourceModel::resourcesCount() const { return m_resourceAdapter->resources().count(); } QList KoResourceModel::currentlyVisibleResources() const { return m_resourceAdapter->resources(); } void KoResourceModel::tagCategoryMembersChanged() { m_resourceAdapter->tagCategoryMembersChanged(); } void KoResourceModel::tagCategoryAdded(const QString& tag) { m_resourceAdapter->tagCategoryAdded(tag); } void KoResourceModel::tagCategoryRemoved(const QString& tag) { m_resourceAdapter->tagCategoryRemoved(tag); } QString KoResourceModel::serverType() const { return m_resourceAdapter->serverType(); } QList< KoResource* > KoResourceModel::serverResources() const { return m_resourceAdapter->serverResources(); } diff --git a/libs/widgets/kis_file_name_requester.cpp b/libs/widgets/kis_file_name_requester.cpp index db21da770e..966cc46999 100644 --- a/libs/widgets/kis_file_name_requester.cpp +++ b/libs/widgets/kis_file_name_requester.cpp @@ -1,103 +1,103 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_file_name_requester.h" #include "ui_wdg_file_name_requester.h" -#include +#include #include #include "KoIcon.h" KisFileNameRequester::KisFileNameRequester(QWidget *parent) : QWidget(parent) , m_ui(new Ui::WdgFileNameRequester) , m_mode(KoFileDialog::OpenFile) { m_ui->setupUi(this); m_ui->btnSelectFile->setIcon(kisIcon("folder")); connect(m_ui->btnSelectFile, SIGNAL(clicked()), SLOT(slotSelectFile())); connect(m_ui->txtFileName, SIGNAL(textChanged(const QString&)), SIGNAL(textChanged(const QString&))); } KisFileNameRequester::~KisFileNameRequester() { } void KisFileNameRequester::setStartDir(const QString &path) { m_basePath = path; } void KisFileNameRequester::setFileName(const QString &path) { m_ui->txtFileName->setText(path); emit fileSelected(path); } QString KisFileNameRequester::fileName() const { return m_ui->txtFileName->text(); } void KisFileNameRequester::setMode(KoFileDialog::DialogType mode) { m_mode = mode; } KoFileDialog::DialogType KisFileNameRequester::mode() const { return m_mode; } void KisFileNameRequester::setMimeTypeFilters(const QStringList &filterList, QString defaultFilter) { m_mime_filter_list = filterList; m_mime_default_filter = defaultFilter; } void KisFileNameRequester::slotSelectFile() { KoFileDialog dialog(this, m_mode, "OpenDocument"); if (m_mode == KoFileDialog::OpenFile) { dialog.setCaption(i18n("Select a file to load...")); } else if (m_mode == KoFileDialog::OpenDirectory) { dialog.setCaption(i18n("Select a directory to load...")); } if (m_basePath.isEmpty()) { - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); } else { dialog.setDefaultDir(m_basePath); } Q_ASSERT(!m_mime_filter_list.isEmpty()); dialog.setMimeTypeFilters(m_mime_filter_list, m_mime_default_filter); QString newFileName = dialog.filename(); if (!newFileName.isEmpty()) { setFileName(newFileName); } } diff --git a/libs/widgetutils/config/khelpclient.cpp b/libs/widgetutils/config/khelpclient.cpp index 53a331e954..ba9ea43937 100644 --- a/libs/widgetutils/config/khelpclient.cpp +++ b/libs/widgetutils/config/khelpclient.cpp @@ -1,78 +1,79 @@ /* This file is part of the KDE libraries * Copyright 2012 David Faure * * 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 of the License or ( at * your option ) version 3 or, at the discretion of KDE e.V. ( which shall * act as a proxy as in section 14 of the GPLv3 ), any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "khelpclient.h" #include "kdesktopfile.h" #include #include #include #include +#include #include void KHelpClient::invokeHelp(const QString &anchor, const QString &_appname) { QString appname; if (_appname.isEmpty()) { appname = QCoreApplication::instance()->applicationName(); } else { appname = _appname; } // Look for the .desktop file of the application // was: //KService::Ptr service(KService::serviceByDesktopName(appname)); //if (service) // docPath = service->docPath(); // but we don't want to depend on KService here. QString docPath; const QStringList desktopDirs = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation); Q_FOREACH (const QString &dir, desktopDirs) { QDirIterator it(dir, QStringList() << appname + QLatin1String(".desktop"), QDir::NoFilter, QDirIterator::Subdirectories); while (it.hasNext()) { const QString desktopPath(it.next()); KDesktopFile desktopFile(desktopPath); docPath = desktopFile.readDocPath(); break; } } // docPath could be a path or a full URL, I think. QUrl url; if (!docPath.isEmpty()) { url = QUrl(QLatin1String("help:/")).resolved(QUrl(docPath)); } else { url = QUrl(QString::fromLatin1("help:/%1/index.html").arg(appname)); } if (!anchor.isEmpty()) { QUrlQuery query(url); query.addQueryItem(QString::fromLatin1("anchor"), anchor); url.setQuery(query); } // launch khelpcenter, or a browser for URIs not handled by khelpcenter QDesktopServices::openUrl(url); } diff --git a/libs/widgetutils/xmlgui/kbugreport.cpp b/libs/widgetutils/xmlgui/kbugreport.cpp index 95e91473b7..3931011c24 100644 --- a/libs/widgetutils/xmlgui/kbugreport.cpp +++ b/libs/widgetutils/xmlgui/kbugreport.cpp @@ -1,226 +1,227 @@ /* This file is part of the KDE project Copyright (C) 1999 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; 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 "kbugreport.h" #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include "systeminformation_p.h" #include "config-xmlgui.h" #include class KBugReportPrivate { public: KBugReportPrivate(KBugReport *q): q(q), m_aboutData(KAboutData::applicationData()) {} void _k_updateUrl(); KBugReport *q; QProcess *m_process; KAboutData m_aboutData; QTextEdit *m_lineedit; QLineEdit *m_subject; QLabel *m_version; QString m_strVersion; QGroupBox *m_bgSeverity; QComboBox *appcombo; QString lastError; QString appname; QString os; QUrl url; QList severityButtons; int currentSeverity() { for (int i = 0; i < severityButtons.count(); i++) if (severityButtons[i]->isChecked()) { return i; } return -1; } }; KBugReport::KBugReport(const KAboutData &aboutData, QWidget *_parent) : QDialog(_parent), d(new KBugReportPrivate(this)) { setWindowTitle(i18n("Submit Bug Report")); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); d->m_aboutData = aboutData; d->m_process = 0; KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::close()); QLabel *tmpLabel; QVBoxLayout *lay = new QVBoxLayout; setLayout(lay); KTitleWidget *title = new KTitleWidget(this); title->setText(i18n("Submit Bug Report")); title->setPixmap(KisIconUtils::loadIcon(QStringLiteral("tools-report-bug")).pixmap(32)); lay->addWidget(title); QGridLayout *glay = new QGridLayout(); lay->addLayout(glay); int row = 0; // Program name QString qwtstr = i18n("The application for which you wish to submit a bug report - if incorrect, please use the Report Bug menu item of the correct application"); tmpLabel = new QLabel(i18n("Application: "), this); glay->addWidget(tmpLabel, row, 0); tmpLabel->setWhatsThis(qwtstr); d->appcombo = new QComboBox(this); d->appcombo->setWhatsThis(qwtstr); QStringList packageList = QStringList() << "krita"; d->appcombo->addItems(packageList); connect(d->appcombo, SIGNAL(activated(int)), SLOT(_k_appChanged(int))); d->appname = d->m_aboutData.productName(); glay->addWidget(d->appcombo, row, 1); int index = 0; for (; index < d->appcombo->count(); index++) { if (d->appcombo->itemText(index) == d->appname) { break; } } if (index == d->appcombo->count()) { // not present d->appcombo->addItem(d->appname); } d->appcombo->setCurrentIndex(index); tmpLabel->setWhatsThis(qwtstr); // Version qwtstr = i18n("The version of this application - please make sure that no newer version is available before sending a bug report"); tmpLabel = new QLabel(i18n("Version:"), this); glay->addWidget(tmpLabel, ++row, 0); tmpLabel->setWhatsThis(qwtstr); d->m_strVersion = d->m_aboutData.version(); if (d->m_strVersion.isEmpty()) { d->m_strVersion = i18n("no version set (programmer error)"); } d->m_version = new QLabel(d->m_strVersion, this); d->m_version->setTextInteractionFlags(Qt::TextBrowserInteraction); //glay->addWidget( d->m_version, row, 1 ); glay->addWidget(d->m_version, row, 1, 1, 2); d->m_version->setWhatsThis(qwtstr); tmpLabel = new QLabel(i18n("OS:"), this); glay->addWidget(tmpLabel, ++row, 0); d->os = SystemInformation::operatingSystemVersion(); tmpLabel = new QLabel(d->os, this); tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); glay->addWidget(tmpLabel, row, 1, 1, 2); tmpLabel = new QLabel(i18n("Compiler:"), this); glay->addWidget(tmpLabel, ++row, 0); tmpLabel = new QLabel(QString::fromLatin1(XMLGUI_COMPILER_VERSION), this); tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); glay->addWidget(tmpLabel, row, 1, 1, 2); // Point to the web form lay->addSpacing(10); QString text = i18n("To submit a bug report, click on the button below. This will open a web browser " "window on
    http://bugs.kde.org where you will find " "a form to fill in. The information displayed above will be transferred to that server."); QLabel *label = new QLabel(text, this); label->setOpenExternalLinks(true); label->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); label->setWordWrap(true); lay->addWidget(label); lay->addSpacing(10); d->appcombo->setFocus(); d->_k_updateUrl(); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setText(i18n("&Launch Bug Report Wizard")); okButton->setIcon(KisIconUtils::loadIcon(QStringLiteral("tools-report-bug"))); lay->addWidget(buttonBox); setMinimumHeight(sizeHint().height() + 20); // WORKAROUND: prevent "cropped" qcombobox } KBugReport::~KBugReport() { delete d; } void KBugReportPrivate::_k_updateUrl() { url = QUrl(QStringLiteral("https://bugs.kde.org/enter_bug.cgi")); QUrlQuery query; query.addQueryItem(QStringLiteral("format"), QLatin1String("guided")); // use the guided form // the string format is product/component, where component is optional QStringList list = appcombo->currentText().split(QLatin1Char('/')); query.addQueryItem(QStringLiteral("product"), list[0]); if (list.size() == 2) { query.addQueryItem(QStringLiteral("component"), list[1]); } query.addQueryItem(QStringLiteral("version"), m_strVersion); url.setQuery(query); // TODO: guess and fill OS(sys_os) and Platform(rep_platform) fields } void KBugReport::accept() { QDesktopServices::openUrl(d->url); } #include "moc_kbugreport.cpp" diff --git a/libs/widgetutils/xmlgui/khelpmenu.cpp b/libs/widgetutils/xmlgui/khelpmenu.cpp index 29ca437f00..7a8d7b4e96 100644 --- a/libs/widgetutils/xmlgui/khelpmenu.cpp +++ b/libs/widgetutils/xmlgui/khelpmenu.cpp @@ -1,303 +1,303 @@ /* * This file is part of the KDE Libraries * Copyright (C) 1999-2000 Espen Sand (espen@kde.org) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ // I (espen) prefer that header files are included alphabetically #include "khelpmenu.h" #include "config-xmlgui.h" #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include +#include #include #include "kaboutkdedialog_p.h" #include "kbugreport.h" #include "kswitchlanguagedialog_p.h" #include #include #include #include using namespace KDEPrivate; class KHelpMenuPrivate { public: KHelpMenuPrivate() : mSwitchApplicationLanguage(0), mActionsCreated(false), mSwitchApplicationLanguageAction(0), mAboutData(KAboutData::applicationData()) { mMenu = 0; mAboutApp = 0; mAboutKDE = 0; mBugReport = 0; mHandBookAction = 0; mWhatsThisAction = 0; mReportBugAction = 0; mAboutAppAction = 0; mAboutKDEAction = 0; } ~KHelpMenuPrivate() { delete mMenu; delete mAboutApp; delete mAboutKDE; delete mBugReport; delete mSwitchApplicationLanguage; } void createActions(KHelpMenu *q); QMenu *mMenu; QDialog *mAboutApp; KAboutKdeDialog *mAboutKDE; KBugReport *mBugReport; KSwitchLanguageDialog *mSwitchApplicationLanguage; // TODO evaluate if we use static_cast(parent()) instead of mParent to win that bit of memory QWidget *mParent; QString mAboutAppText; bool mShowWhatsThis; bool mActionsCreated; QAction *mHandBookAction, *mWhatsThisAction; QAction *mReportBugAction, *mSwitchApplicationLanguageAction, *mAboutAppAction, *mAboutKDEAction; KAboutData mAboutData; }; KHelpMenu::KHelpMenu(QWidget *parent, const QString &aboutAppText, bool showWhatsThis) : QObject(parent), d(new KHelpMenuPrivate) { d->mAboutAppText = aboutAppText; d->mShowWhatsThis = showWhatsThis; d->mParent = parent; d->createActions(this); } KHelpMenu::KHelpMenu(QWidget *parent, const KAboutData &aboutData, bool showWhatsThis) : QObject(parent), d(new KHelpMenuPrivate) { d->mShowWhatsThis = showWhatsThis; d->mParent = parent; d->mAboutData = aboutData; d->createActions(this); } KHelpMenu::~KHelpMenu() { delete d; } void KHelpMenuPrivate::createActions(KHelpMenu *q) { if (mActionsCreated) { return; } mActionsCreated = true; mHandBookAction = KStandardAction::helpContents(q, SLOT(appHelpActivated()), q); if (mShowWhatsThis) { mWhatsThisAction = KStandardAction::whatsThis(q, SLOT(contextHelpActivated()), q); } if (!mAboutData.bugAddress().isEmpty()) { mReportBugAction = KStandardAction::reportBug(q, SLOT(reportBug()), q); } mSwitchApplicationLanguageAction = KStandardAction::create(KStandardAction::SwitchApplicationLanguage, q, SLOT(switchApplicationLanguage()), q); mAboutAppAction = KStandardAction::aboutApp(q, SLOT(aboutApplication()), q); mAboutKDEAction = KStandardAction::aboutKDE(q, SLOT(aboutKDE()), q); } // Used in the non-xml-gui case, like kfind or ksnapshot's help button. QMenu *KHelpMenu::menu() { if (!d->mMenu) { d->mMenu = new QMenu(); connect(d->mMenu, SIGNAL(destroyed()), this, SLOT(menuDestroyed())); d->mMenu->setTitle(i18n("&Help")); d->createActions(this); bool need_separator = false; if (d->mHandBookAction) { d->mMenu->addAction(d->mHandBookAction); need_separator = true; } if (d->mWhatsThisAction) { d->mMenu->addAction(d->mWhatsThisAction); need_separator = true; } if (d->mReportBugAction) { if (need_separator) { d->mMenu->addSeparator(); } d->mMenu->addAction(d->mReportBugAction); need_separator = true; } if (d->mSwitchApplicationLanguageAction) { if (need_separator) { d->mMenu->addSeparator(); } d->mMenu->addAction(d->mSwitchApplicationLanguageAction); need_separator = true; } if (need_separator) { d->mMenu->addSeparator(); } if (d->mAboutAppAction) { d->mMenu->addAction(d->mAboutAppAction); } if (d->mAboutKDEAction) { d->mMenu->addAction(d->mAboutKDEAction); } } return d->mMenu; } QAction *KHelpMenu::action(MenuId id) const { switch (id) { case menuHelpContents: return d->mHandBookAction; break; case menuWhatsThis: return d->mWhatsThisAction; break; case menuReportBug: return d->mReportBugAction; break; case menuSwitchLanguage: return d->mSwitchApplicationLanguageAction; break; case menuAboutApp: return d->mAboutAppAction; break; case menuAboutKDE: return d->mAboutKDEAction; break; } return 0; } void KHelpMenu::appHelpActivated() { QDesktopServices::openUrl(QUrl(QStringLiteral("help:/"))); } void KHelpMenu::aboutApplication() { if (receivers(SIGNAL(showAboutApplication())) > 0) { emit showAboutApplication(); } } void KHelpMenu::aboutKDE() { if (!d->mAboutKDE) { d->mAboutKDE = new KAboutKdeDialog(d->mParent); connect(d->mAboutKDE, SIGNAL(finished(int)), this, SLOT(dialogFinished())); } d->mAboutKDE->show(); } void KHelpMenu::reportBug() { if (!d->mBugReport) { d->mBugReport = new KBugReport(d->mAboutData, d->mParent); connect(d->mBugReport, SIGNAL(finished(int)), this, SLOT(dialogFinished())); } d->mBugReport->show(); } void KHelpMenu::switchApplicationLanguage() { if (!d->mSwitchApplicationLanguage) { d->mSwitchApplicationLanguage = new KSwitchLanguageDialog(d->mParent); connect(d->mSwitchApplicationLanguage, SIGNAL(finished(int)), this, SLOT(dialogFinished())); } d->mSwitchApplicationLanguage->show(); } void KHelpMenu::dialogFinished() { QTimer::singleShot(0, this, SLOT(timerExpired())); } void KHelpMenu::timerExpired() { if (d->mAboutKDE && !d->mAboutKDE->isVisible()) { delete d->mAboutKDE; d->mAboutKDE = 0; } if (d->mBugReport && !d->mBugReport->isVisible()) { delete d->mBugReport; d->mBugReport = 0; } if (d->mSwitchApplicationLanguage && !d->mSwitchApplicationLanguage->isVisible()) { delete d->mSwitchApplicationLanguage; d->mSwitchApplicationLanguage = 0; } if (d->mAboutApp && !d->mAboutApp->isVisible()) { delete d->mAboutApp; d->mAboutApp = 0; } } void KHelpMenu::menuDestroyed() { d->mMenu = 0; } void KHelpMenu::contextHelpActivated() { QWhatsThis::enterWhatsThisMode(); } diff --git a/plugins/assistants/RulerAssistant/kis_ruler_assistant_tool.cc b/plugins/assistants/RulerAssistant/kis_ruler_assistant_tool.cc index 70894493a7..9300d8a5d3 100644 --- a/plugins/assistants/RulerAssistant/kis_ruler_assistant_tool.cc +++ b/plugins/assistants/RulerAssistant/kis_ruler_assistant_tool.cc @@ -1,913 +1,913 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_global.h" #include KisRulerAssistantTool::KisRulerAssistantTool(KoCanvasBase * canvas) : KisTool(canvas, KisCursor::arrowCursor()), m_canvas(dynamic_cast(canvas)), m_assistantDrag(0), m_newAssistant(0), m_optionsWidget(0), m_handleSize(32), m_handleHalfSize(16) { Q_ASSERT(m_canvas); setObjectName("tool_rulerassistanttool"); } KisRulerAssistantTool::~KisRulerAssistantTool() { } QPointF adjustPointF(const QPointF& _pt, const QRectF& _rc) { return QPointF(qBound(_rc.left(), _pt.x(), _rc.right()), qBound(_rc.top(), _pt.y(), _rc.bottom())); } void KisRulerAssistantTool::activate(ToolActivation toolActivation, const QSet &shapes) { // Add code here to initialize your tool when it got activated KisTool::activate(toolActivation, shapes); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->paintingAssistantsDecoration()->setVisible(true); m_canvas->updateCanvas(); m_handleDrag = 0; m_internalMode = MODE_CREATION; m_assistantHelperYOffset = 10; } void KisRulerAssistantTool::deactivate() { // Add code here to initialize your tool when it got deactivated m_canvas->updateCanvas(); KisTool::deactivate(); } bool KisRulerAssistantTool::mouseNear(const QPointF& mousep, const QPointF& point) { QRectF handlerect(point-QPointF(m_handleHalfSize,m_handleHalfSize), QSizeF(m_handleSize, m_handleSize)); return handlerect.contains(mousep); } KisPaintingAssistantHandleSP KisRulerAssistantTool::nodeNearPoint(KisPaintingAssistantSP grid, QPointF point) { if (mouseNear(point, pixelToView(*grid->topLeft()))) { return grid->topLeft(); } else if (mouseNear(point, pixelToView(*grid->topRight()))) { return grid->topRight(); } else if (mouseNear(point, pixelToView(*grid->bottomLeft()))) { return grid->bottomLeft(); } else if (mouseNear(point, pixelToView(*grid->bottomRight()))) { return grid->bottomRight(); } return 0; } inline double norm2(const QPointF& p) { return p.x() * p.x() + p.y() * p.y(); } void KisRulerAssistantTool::beginPrimaryAction(KoPointerEvent *event) { setMode(KisTool::PAINT_MODE); bool newAssistantAllowed = true; if (m_newAssistant) { m_internalMode = MODE_CREATION; *m_newAssistant->handles().back() = snapToGuide(event, QPointF(), false); if (m_newAssistant->handles().size() == m_newAssistant->numHandles()) { addAssistant(); } else { m_newAssistant->addHandle(new KisPaintingAssistantHandle(snapToGuide(event, QPointF(), false))); } m_canvas->updateCanvas(); return; } m_handleDrag = 0; double minDist = 81.0; QPointF mousePos = m_canvas->viewConverter()->documentToView(snapToGuide(event, QPointF(), false));//m_canvas->viewConverter()->documentToView(event->point); Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) { double dist = norm2(mousePos - m_canvas->viewConverter()->documentToView(*handle)); if (dist < minDist) { minDist = dist; m_handleDrag = handle; } } if(m_handleDrag && assistant->id() == "perspective") { // Look for the handle which was pressed if (m_handleDrag == assistant->topLeft()) { double dist = norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag)); if (dist < minDist) { minDist = dist; } m_dragStart = QPointF(assistant->topRight().data()->x(),assistant->topRight().data()->y()); m_internalMode = MODE_DRAGGING_NODE; } else if (m_handleDrag == assistant->topRight()) { double dist = norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag)); if (dist < minDist) { minDist = dist; } m_internalMode = MODE_DRAGGING_NODE; m_dragStart = QPointF(assistant->topLeft().data()->x(),assistant->topLeft().data()->y()); } else if (m_handleDrag == assistant->bottomLeft()) { double dist = norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag)); if (dist < minDist) { minDist = dist; } m_internalMode = MODE_DRAGGING_NODE; m_dragStart = QPointF(assistant->bottomRight().data()->x(),assistant->bottomRight().data()->y()); } else if (m_handleDrag == assistant->bottomRight()) { double dist = norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag)); if (dist < minDist) { minDist = dist; } m_internalMode = MODE_DRAGGING_NODE; m_dragStart = QPointF(assistant->bottomLeft().data()->x(),assistant->bottomLeft().data()->y()); } else if (m_handleDrag == assistant->leftMiddle()) { m_internalMode = MODE_DRAGGING_TRANSLATING_TWONODES; m_dragStart = QPointF((assistant->bottomLeft().data()->x()+assistant->topLeft().data()->x())*0.5, (assistant->bottomLeft().data()->y()+assistant->topLeft().data()->y())*0.5); m_selectedNode1 = new KisPaintingAssistantHandle(assistant->topLeft().data()->x(),assistant->topLeft().data()->y()); m_selectedNode2 = new KisPaintingAssistantHandle(assistant->bottomLeft().data()->x(),assistant->bottomLeft().data()->y()); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant()); m_newAssistant->addHandle(assistant->topLeft()); m_newAssistant->addHandle(m_selectedNode1); m_newAssistant->addHandle(m_selectedNode2); m_newAssistant->addHandle(assistant->bottomLeft()); m_dragEnd = event->point; m_handleDrag = 0; m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } else if (m_handleDrag == assistant->rightMiddle()) { m_dragStart = QPointF((assistant->topRight().data()->x()+assistant->bottomRight().data()->x())*0.5, (assistant->topRight().data()->y()+assistant->bottomRight().data()->y())*0.5); m_internalMode = MODE_DRAGGING_TRANSLATING_TWONODES; m_selectedNode1 = new KisPaintingAssistantHandle(assistant->topRight().data()->x(),assistant->topRight().data()->y()); m_selectedNode2 = new KisPaintingAssistantHandle(assistant->bottomRight().data()->x(),assistant->bottomRight().data()->y()); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant()); m_newAssistant->addHandle(assistant->topRight()); m_newAssistant->addHandle(m_selectedNode1); m_newAssistant->addHandle(m_selectedNode2); m_newAssistant->addHandle(assistant->bottomRight()); m_dragEnd = event->point; m_handleDrag = 0; m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } else if (m_handleDrag == assistant->topMiddle()) { m_dragStart = QPointF((assistant->topLeft().data()->x()+assistant->topRight().data()->x())*0.5, (assistant->topLeft().data()->y()+assistant->topRight().data()->y())*0.5); m_internalMode = MODE_DRAGGING_TRANSLATING_TWONODES; m_selectedNode1 = new KisPaintingAssistantHandle(assistant->topLeft().data()->x(),assistant->topLeft().data()->y()); m_selectedNode2 = new KisPaintingAssistantHandle(assistant->topRight().data()->x(),assistant->topRight().data()->y()); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant()); m_newAssistant->addHandle(m_selectedNode1); m_newAssistant->addHandle(m_selectedNode2); m_newAssistant->addHandle(assistant->topRight()); m_newAssistant->addHandle(assistant->topLeft()); m_dragEnd = event->point; m_handleDrag = 0; m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } else if (m_handleDrag == assistant->bottomMiddle()) { m_dragStart = QPointF((assistant->bottomLeft().data()->x()+assistant->bottomRight().data()->x())*0.5, (assistant->bottomLeft().data()->y()+assistant->bottomRight().data()->y())*0.5); m_internalMode = MODE_DRAGGING_TRANSLATING_TWONODES; m_selectedNode1 = new KisPaintingAssistantHandle(assistant->bottomLeft().data()->x(),assistant->bottomLeft().data()->y()); m_selectedNode2 = new KisPaintingAssistantHandle(assistant->bottomRight().data()->x(),assistant->bottomRight().data()->y()); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant()); m_newAssistant->addHandle(assistant->bottomLeft()); m_newAssistant->addHandle(assistant->bottomRight()); m_newAssistant->addHandle(m_selectedNode2); m_newAssistant->addHandle(m_selectedNode1); m_dragEnd = event->point; m_handleDrag = 0; m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } m_snapIsRadial = false; } else if (m_handleDrag && assistant->handles().size()>1 && (assistant->id() == "ruler" || assistant->id() == "parallel ruler" || assistant->id() == "infinite ruler" || assistant->id() == "spline")){ if (m_handleDrag == assistant->handles()[0]) { m_dragStart = *assistant->handles()[1]; } else if (m_handleDrag == assistant->handles()[1]) { m_dragStart = *assistant->handles()[0]; } else if(assistant->handles().size()==4){ if (m_handleDrag == assistant->handles()[2]) { m_dragStart = *assistant->handles()[0]; } else if (m_handleDrag == assistant->handles()[3]) { m_dragStart = *assistant->handles()[1]; } } m_snapIsRadial = false; } else if (m_handleDrag && assistant->handles().size()>2 && (assistant->id() == "ellipse" || assistant->id() == "concentric ellipse" || assistant->id() == "fisheye-point")){ m_snapIsRadial = false; if (m_handleDrag == assistant->handles()[0]) { m_dragStart = *assistant->handles()[1]; } else if (m_handleDrag == assistant->handles()[1]) { m_dragStart = *assistant->handles()[0]; } else if (m_handleDrag == assistant->handles()[2]) { m_dragStart = assistant->buttonPosition(); m_radius = QLineF(m_dragStart, *assistant->handles()[0]); m_snapIsRadial = true; } } else { m_dragStart = assistant->buttonPosition(); m_snapIsRadial = false; } } if (m_handleDrag) { // TODO: Shift-press should now be handled using the alternate actions // if (event->modifiers() & Qt::ShiftModifier) { // m_handleDrag->uncache(); // m_handleDrag = m_handleDrag->split()[0]; // m_handles = m_canvas->view()->paintingAssistantsDecoration()->handles(); // } m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } m_assistantDrag.clear(); Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { // This code contains the click event behavior. The actual display of the icons are done at the bottom // of the paint even. Make sure the rectangles positions are the same between the two. // TODO: These 6 lines are duplicated below in the paint layer. It shouldn't be done like this. QPointF actionsPosition = m_canvas->viewConverter()->documentToView(assistant->buttonPosition()); QPointF iconDeletePosition(actionsPosition + QPointF(78, m_assistantHelperYOffset + 7)); QPointF iconSnapPosition(actionsPosition + QPointF(54, m_assistantHelperYOffset + 7)); QPointF iconMovePosition(actionsPosition + QPointF(15, m_assistantHelperYOffset)); QRectF deleteRect(iconDeletePosition, QSizeF(16, 16)); QRectF visibleRect(iconSnapPosition, QSizeF(16, 16)); QRectF moveRect(iconMovePosition, QSizeF(32, 32)); if (moveRect.contains(mousePos)) { m_assistantDrag = assistant; m_cursorStart = event->point; m_currentAdjustment = QPointF(); m_internalMode = MODE_EDITING; return; } if (deleteRect.contains(mousePos)) { removeAssistant(assistant); if(m_canvas->paintingAssistantsDecoration()->assistants().isEmpty()) { m_internalMode = MODE_CREATION; } else m_internalMode = MODE_EDITING; m_canvas->updateCanvas(); return; } if (visibleRect.contains(mousePos)) { newAssistantAllowed = false; if (assistant->snapping()==true){ snappingOff(assistant); outlineOff(assistant); } else{ snappingOn(assistant); outlineOn(assistant); } assistant->uncache();//this updates the chache of the assistant, very important. } } if (newAssistantAllowed==true){//don't make a new assistant when I'm just toogling visiblity// QString key = m_options.comboBox->model()->index( m_options.comboBox->currentIndex(), 0 ).data(Qt::UserRole).toString(); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get(key)->createPaintingAssistant()); m_internalMode = MODE_CREATION; m_newAssistant->addHandle(new KisPaintingAssistantHandle(snapToGuide(event, QPointF(), false))); if (m_newAssistant->numHandles() <= 1) { if (key == "vanishing point"){ m_newAssistant->addSideHandle(new KisPaintingAssistantHandle(event->point+QPointF(-70,0))); m_newAssistant->addSideHandle(new KisPaintingAssistantHandle(event->point+QPointF(-140,0))); m_newAssistant->addSideHandle(new KisPaintingAssistantHandle(event->point+QPointF(70,0))); m_newAssistant->addSideHandle(new KisPaintingAssistantHandle(event->point+QPointF(140,0))); } addAssistant(); } else { m_newAssistant->addHandle(new KisPaintingAssistantHandle(snapToGuide(event, QPointF(), false))); } } m_canvas->updateCanvas(); } void KisRulerAssistantTool::continuePrimaryAction(KoPointerEvent *event) { if (m_handleDrag) { *m_handleDrag = event->point; //ported from the gradient tool... we need to think about this more in the future. if (event->modifiers() == Qt::ShiftModifier && m_snapIsRadial) { QLineF dragRadius = QLineF(m_dragStart, event->point); dragRadius.setLength(m_radius.length()); *m_handleDrag = dragRadius.p2(); } else if (event->modifiers() == Qt::ShiftModifier ) { QPointF move = snapToClosestAxis(event->point - m_dragStart); *m_handleDrag = m_dragStart + move; } else { *m_handleDrag = snapToGuide(event, QPointF(), false); } m_handleDrag->uncache(); m_handleCombine = 0; if (!(event->modifiers() & Qt::ShiftModifier)) { double minDist = 49.0; QPointF mousePos = m_canvas->viewConverter()->documentToView(event->point); Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) { if (handle == m_handleDrag) continue; double dist = norm2(mousePos - m_canvas->viewConverter()->documentToView(*handle)); if (dist < minDist) { minDist = dist; m_handleCombine = handle; } } } m_canvas->updateCanvas(); } else if (m_assistantDrag) { QPointF newAdjustment = snapToGuide(event, QPointF(), false) - m_cursorStart; if (event->modifiers() == Qt::ShiftModifier ) { newAdjustment = snapToClosestAxis(newAdjustment); } Q_FOREACH (KisPaintingAssistantHandleSP handle, m_assistantDrag->handles()) { *handle += (newAdjustment - m_currentAdjustment); } if (m_assistantDrag->id()== "vanishing point"){ Q_FOREACH (KisPaintingAssistantHandleSP handle, m_assistantDrag->sideHandles()) { *handle += (newAdjustment - m_currentAdjustment); } } m_currentAdjustment = newAdjustment; m_canvas->updateCanvas(); } else { event->ignore(); } bool wasHiglightedNode = m_higlightedNode != 0; QPointF mousep = m_canvas->viewConverter()->documentToView(event->point); QList pAssistant= m_canvas->paintingAssistantsDecoration()->assistants(); Q_FOREACH (KisPaintingAssistantSP assistant, pAssistant) { if(assistant->id() == "perspective") { if ((m_higlightedNode = nodeNearPoint(assistant, mousep))) { if (m_higlightedNode == m_selectedNode1 || m_higlightedNode == m_selectedNode2) { m_higlightedNode = 0; } else { m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas break; } } } //this following bit sets the translations for the vanishing-point handles. if(m_handleDrag && assistant->id() == "vanishing point" && assistant->sideHandles().size()==4) { //for inner handles, the outer handle gets translated. if (m_handleDrag == assistant->sideHandles()[0]){ QLineF perspectiveline = QLineF(*assistant->handles()[0], *assistant->sideHandles()[0]); qreal length = QLineF(*assistant->sideHandles()[0], *assistant->sideHandles()[1]).length(); if (length<2.0){length=2.0;} length +=perspectiveline.length(); perspectiveline.setLength(length); *assistant->sideHandles()[1] = perspectiveline.p2(); } else if (m_handleDrag == assistant->sideHandles()[2]){ QLineF perspectiveline = QLineF(*assistant->handles()[0], *assistant->sideHandles()[2]); qreal length = QLineF(*assistant->sideHandles()[2], *assistant->sideHandles()[3]).length(); if (length<2.0){length=2.0;} length +=perspectiveline.length(); perspectiveline.setLength(length); *assistant->sideHandles()[3] = perspectiveline.p2(); } //for outer handles, only the vanishing point is translated, but only if there's an intersection. else if (m_handleDrag == assistant->sideHandles()[1]|| m_handleDrag == assistant->sideHandles()[3]){ QPointF vanishingpoint(0,0); QLineF perspectiveline = QLineF(*assistant->sideHandles()[0], *assistant->sideHandles()[1]); QLineF perspectiveline2 = QLineF(*assistant->sideHandles()[2], *assistant->sideHandles()[3]); if (QLineF(perspectiveline2).intersect(QLineF(perspectiveline), &vanishingpoint) != QLineF::NoIntersection){ *assistant->handles()[0] = vanishingpoint;} }//and for the vanishing point itself, only the outer handles get translated. else if (m_handleDrag == assistant->handles()[0]){ QLineF perspectiveline = QLineF(*assistant->handles()[0], *assistant->sideHandles()[0]); QLineF perspectiveline2 = QLineF(*assistant->handles()[0], *assistant->sideHandles()[2]); qreal length = QLineF(*assistant->sideHandles()[0], *assistant->sideHandles()[1]).length(); qreal length2 = QLineF(*assistant->sideHandles()[2], *assistant->sideHandles()[3]).length(); if (length<2.0){length=2.0;} if (length2<2.0){length2=2.0;} length +=perspectiveline.length(); length2 +=perspectiveline2.length(); perspectiveline.setLength(length); perspectiveline2.setLength(length2); *assistant->sideHandles()[1] = perspectiveline.p2(); *assistant->sideHandles()[3] = perspectiveline2.p2(); } } } if (wasHiglightedNode && !m_higlightedNode) { m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas } } void KisRulerAssistantTool::endPrimaryAction(KoPointerEvent *event) { setMode(KisTool::HOVER_MODE); if (m_handleDrag) { if (!(event->modifiers() & Qt::ShiftModifier) && m_handleCombine) { m_handleCombine->mergeWith(m_handleDrag); m_handleCombine->uncache(); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); } m_handleDrag = m_handleCombine = 0; m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas } else if (m_assistantDrag) { m_assistantDrag.clear(); m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas } else if(m_internalMode == MODE_DRAGGING_TRANSLATING_TWONODES) { addAssistant(); m_internalMode = MODE_CREATION; m_canvas->updateCanvas(); } else { event->ignore(); } } void KisRulerAssistantTool::addAssistant() { m_canvas->paintingAssistantsDecoration()->addAssistant(m_newAssistant); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); KisAbstractPerspectiveGrid* grid = dynamic_cast(m_newAssistant.data()); if (grid) { m_canvas->viewManager()->resourceProvider()->addPerspectiveGrid(grid); } m_newAssistant.clear(); } void KisRulerAssistantTool::removeAssistant(KisPaintingAssistantSP assistant) { KisAbstractPerspectiveGrid* grid = dynamic_cast(assistant.data()); if (grid) { m_canvas->viewManager()->resourceProvider()->removePerspectiveGrid(grid); } m_canvas->paintingAssistantsDecoration()->removeAssistant(assistant); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); } void KisRulerAssistantTool::snappingOn(KisPaintingAssistantSP assistant) { assistant->setSnapping(true); } void KisRulerAssistantTool::snappingOff(KisPaintingAssistantSP assistant) { assistant->setSnapping(false); } void KisRulerAssistantTool::outlineOn(KisPaintingAssistantSP assistant) { assistant->setOutline(true); } void KisRulerAssistantTool::outlineOff(KisPaintingAssistantSP assistant) { assistant->setOutline(false); } #include QPointF KisRulerAssistantTool::snapToGuide(KoPointerEvent *e, const QPointF &offset, bool useModifiers) { if (!m_canvas->currentImage()) return e->point; KoSnapGuide *snapGuide = m_canvas->snapGuide(); QPointF pos = snapGuide->snap(e->point, offset, useModifiers ? e->modifiers() : Qt::NoModifier); //return m_canvas->currentImage()->documentToPixel(pos); return pos; } QPointF KisRulerAssistantTool::snapToGuide(const QPointF& pt, const QPointF &offset) { if (!m_canvas) return pt; KoSnapGuide *snapGuide = m_canvas->snapGuide(); QPointF pos = snapGuide->snap(pt, offset, Qt::NoModifier); return pos; } void KisRulerAssistantTool::mouseMoveEvent(KoPointerEvent *event) { if (m_newAssistant && m_internalMode == MODE_CREATION) { *m_newAssistant->handles().back() = event->point; m_canvas->updateCanvas(); } else if (m_newAssistant && m_internalMode == MODE_DRAGGING_TRANSLATING_TWONODES) { QPointF translate = event->point - m_dragEnd;; m_dragEnd = event->point; m_selectedNode1.data()->operator =(QPointF(m_selectedNode1.data()->x(),m_selectedNode1.data()->y()) + translate); m_selectedNode2.data()->operator = (QPointF(m_selectedNode2.data()->x(),m_selectedNode2.data()->y()) + translate); m_canvas->updateCanvas(); } } void KisRulerAssistantTool::paint(QPainter& _gc, const KoViewConverter &_converter) { QPixmap iconDelete = KisIconUtils::loadIcon("dialog-cancel").pixmap(16, 16); QPixmap iconSnapOn = KisIconUtils::loadIcon("visible").pixmap(16, 16); QPixmap iconSnapOff = KisIconUtils::loadIcon("novisible").pixmap(16, 16); QPixmap iconMove = KisIconUtils::loadIcon("transform-move").pixmap(32, 32); QColor handlesColor(0, 0, 0, 125); if (m_newAssistant) { m_newAssistant->drawAssistant(_gc, QRectF(QPointF(0, 0), QSizeF(m_canvas->image()->size())), m_canvas->coordinatesConverter(), false,m_canvas, true, false); Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_newAssistant->handles()) { QPainterPath path; path.addEllipse(QRectF(_converter.documentToView(*handle) - QPointF(6, 6), QSizeF(12, 12))); KisPaintingAssistant::drawPath(_gc, path); } } // TODO: too many Q_FOREACH loops going through all assistants. Condense this to one to be a little more performant // Draw corner and middle perspective nodes Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) { QRectF ellipse(_converter.documentToView(*handle) - QPointF(6, 6), QSizeF(12, 12)); // render handles when they are being dragged and moved if (handle == m_handleDrag || handle == m_handleCombine) { _gc.save(); _gc.setPen(Qt::transparent); _gc.setBrush(handlesColor); _gc.drawEllipse(ellipse); _gc.restore(); } if ( assistant->id() =="vanishing point") { if (assistant->handles().at(0) == handle ) { // vanishing point handle ellipse = QRectF(_converter.documentToView(*handle) - QPointF(10, 10), QSizeF(20, 20)); // TODO: change this to be smaller, but fill in with a color } //TODO: render outside handles a little bigger than rotation anchor handles } QPainterPath path; path.addEllipse(ellipse); KisPaintingAssistant::drawPath(_gc, path); } } Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { // Draw middle perspective handles if(assistant->id()=="perspective") { assistant->findHandleLocation(); QPointF topMiddle, bottomMiddle, rightMiddle, leftMiddle; topMiddle = (_converter.documentToView(*assistant->topLeft()) + _converter.documentToView(*assistant->topRight()))*0.5; bottomMiddle = (_converter.documentToView(*assistant->bottomLeft()) + _converter.documentToView(*assistant->bottomRight()))*0.5; rightMiddle = (_converter.documentToView(*assistant->topRight()) + _converter.documentToView(*assistant->bottomRight()))*0.5; leftMiddle = (_converter.documentToView(*assistant->topLeft()) + _converter.documentToView(*assistant->bottomLeft()))*0.5; QPainterPath path; path.addEllipse(QRectF(leftMiddle-QPointF(6,6),QSizeF(12,12))); path.addEllipse(QRectF(topMiddle-QPointF(6,6),QSizeF(12,12))); path.addEllipse(QRectF(rightMiddle-QPointF(6,6),QSizeF(12,12))); path.addEllipse(QRectF(bottomMiddle-QPointF(6,6),QSizeF(12,12))); KisPaintingAssistant::drawPath(_gc, path); } if(assistant->id()=="vanishing point") { if (assistant->sideHandles().size() == 4) { // Draw the line QPointF p0 = _converter.documentToView(*assistant->handles()[0]); QPointF p1 = _converter.documentToView(*assistant->sideHandles()[0]); QPointF p2 = _converter.documentToView(*assistant->sideHandles()[1]); QPointF p3 = _converter.documentToView(*assistant->sideHandles()[2]); QPointF p4 = _converter.documentToView(*assistant->sideHandles()[3]); _gc.setPen(QColor(0, 0, 0, 75)); // Draw control lines QPen penStyle(QColor(120, 120, 120, 60), 2.0, Qt::DashDotDotLine); _gc.setPen(penStyle); _gc.drawLine(p0, p1); _gc.drawLine(p0, p3); _gc.drawLine(p1, p2); _gc.drawLine(p3, p4); } } } // Draw the assistant widget Q_FOREACH (const KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { // We are going to put all of the assistant actions below the bounds of the assistant // so they are out of the way // assistant->buttonPosition() gets the center X/Y position point QPointF actionsPosition = m_canvas->viewConverter()->documentToView(assistant->buttonPosition()); QPointF iconDeletePosition(actionsPosition + QPointF(78, m_assistantHelperYOffset + 7)); QPointF iconSnapPosition(actionsPosition + QPointF(54, m_assistantHelperYOffset + 7)); QPointF iconMovePosition(actionsPosition + QPointF(15, m_assistantHelperYOffset )); // Background container for helpers QBrush backgroundColor = m_canvas->viewManager()->mainWindow()->palette().window(); QPointF actionsBGRectangle(actionsPosition + QPointF(25, m_assistantHelperYOffset)); _gc.setRenderHint(QPainter::Antialiasing); QPainterPath bgPath; bgPath.addRoundedRect(QRectF(actionsBGRectangle.x(), actionsBGRectangle.y(), 80, 30), 6, 6); QPen stroke(QColor(60, 60, 60, 80), 2); _gc.setPen(stroke); _gc.fillPath(bgPath, backgroundColor); _gc.drawPath(bgPath); QPainterPath movePath; // render circle behind by move helper _gc.setPen(stroke); movePath.addEllipse(iconMovePosition.x()-5, iconMovePosition.y()-5, 40, 40);// background behind icon _gc.fillPath(movePath, backgroundColor); _gc.drawPath(movePath); // Preview/Snap Tool helper _gc.drawPixmap(iconDeletePosition, iconDelete); if (assistant->snapping()==true) { _gc.drawPixmap(iconSnapPosition, iconSnapOn); } else { _gc.drawPixmap(iconSnapPosition, iconSnapOff); } // Move Assistant Tool helper _gc.drawPixmap(iconMovePosition, iconMove); } } void KisRulerAssistantTool::removeAllAssistants() { m_canvas->viewManager()->resourceProvider()->clearPerspectiveGrids(); m_canvas->paintingAssistantsDecoration()->removeAll(); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->updateCanvas(); } void KisRulerAssistantTool::loadAssistants() { KoFileDialog dialog(m_canvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenAssistant"); dialog.setCaption(i18n("Select an Assistant")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(QStringList() << "application/x-krita-assistant", "application/x-krita-assistant"); QString filename = dialog.filename(); if (filename.isEmpty()) return; if (!QFileInfo(filename).exists()) return; QFile file(filename); file.open(QIODevice::ReadOnly); QByteArray data = file.readAll(); QXmlStreamReader xml(data); QMap handleMap; KisPaintingAssistantSP assistant; bool errors = false; while (!xml.atEnd()) { switch (xml.readNext()) { case QXmlStreamReader::StartElement: if (xml.name() == "handle") { if (assistant && !xml.attributes().value("ref").isEmpty()) { KisPaintingAssistantHandleSP handle = handleMap.value(xml.attributes().value("ref").toString().toInt()); if (handle) { assistant->addHandle(handle); } else { errors = true; } } else { QString strId = xml.attributes().value("id").toString(), strX = xml.attributes().value("x").toString(), strY = xml.attributes().value("y").toString(); if (!strId.isEmpty() && !strX.isEmpty() && !strY.isEmpty()) { int id = strId.toInt(); double x = strX.toDouble(), y = strY.toDouble(); if (!handleMap.contains(id)) { handleMap.insert(id, new KisPaintingAssistantHandle(x, y)); } else { errors = true; } } else { errors = true; } } } else if (xml.name() == "assistant") { const KisPaintingAssistantFactory* factory = KisPaintingAssistantFactoryRegistry::instance()->get(xml.attributes().value("type").toString()); if (factory) { if (assistant) { errors = true; assistant.clear(); } assistant = toQShared(factory->createPaintingAssistant()); } else { errors = true; } } break; case QXmlStreamReader::EndElement: if (xml.name() == "assistant") { if (assistant) { if (assistant->handles().size() == assistant->numHandles()) { if (assistant->id() == "vanishing point"){ //ideally we'd save and load side-handles as well, but this is all I've got// QPointF pos = *assistant->handles()[0]; assistant->addSideHandle(new KisPaintingAssistantHandle(pos+QPointF(-70,0))); assistant->addSideHandle(new KisPaintingAssistantHandle(pos+QPointF(-140,0))); assistant->addSideHandle(new KisPaintingAssistantHandle(pos+QPointF(70,0))); assistant->addSideHandle(new KisPaintingAssistantHandle(pos+QPointF(140,0))); } m_canvas->paintingAssistantsDecoration()->addAssistant(assistant); KisAbstractPerspectiveGrid* grid = dynamic_cast(assistant.data()); if (grid) { m_canvas->viewManager()->resourceProvider()->addPerspectiveGrid(grid); } } else { errors = true; } assistant.clear(); } } break; default: break; } } if (assistant) { errors = true; assistant.clear(); } if (xml.hasError()) { QMessageBox::warning(0, i18nc("@title:window", "Krita"), xml.errorString()); } if (errors) { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Errors were encountered. Not all assistants were successfully loaded.")); } m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->updateCanvas(); } void KisRulerAssistantTool::saveAssistants() { if (m_handles.isEmpty()) return; QByteArray data; QXmlStreamWriter xml(&data); xml.writeStartDocument(); xml.writeStartElement("paintingassistant"); xml.writeStartElement("handles"); QMap handleMap; Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) { int id = handleMap.size(); handleMap.insert(handle, id); xml.writeStartElement("handle"); //xml.writeAttribute("type", handle->handleType()); xml.writeAttribute("id", QString::number(id)); xml.writeAttribute("x", QString::number(double(handle->x()), 'f', 3)); xml.writeAttribute("y", QString::number(double(handle->y()), 'f', 3)); xml.writeEndElement(); } xml.writeEndElement(); xml.writeStartElement("assistants"); Q_FOREACH (const KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { xml.writeStartElement("assistant"); xml.writeAttribute("type", assistant->id()); xml.writeStartElement("handles"); Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->handles()) { xml.writeStartElement("handle"); xml.writeAttribute("ref", QString::number(handleMap.value(handle))); xml.writeEndElement(); } xml.writeEndElement(); xml.writeEndElement(); } xml.writeEndElement(); xml.writeEndElement(); xml.writeEndDocument(); KoFileDialog dialog(m_canvas->viewManager()->mainWindow(), KoFileDialog::SaveFile, "OpenAssistant"); dialog.setCaption(i18n("Save Assistant")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(QStringList() << "application/x-krita-assistant", "application/x-krita-assistant"); QString filename = dialog.filename(); if (filename.isEmpty()) return; QFile file(filename); file.open(QIODevice::WriteOnly); file.write(data); } QWidget *KisRulerAssistantTool::createOptionWidget() { if (!m_optionsWidget) { m_optionsWidget = new QWidget; m_options.setupUi(m_optionsWidget); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(m_optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); m_optionsWidget->layout()->addWidget(specialSpacer); m_options.loadButton->setIcon(KisIconUtils::loadIcon("document-open")); m_options.saveButton->setIcon(KisIconUtils::loadIcon("document-save")); m_options.deleteButton->setIcon(KisIconUtils::loadIcon("edit-delete")); QList assistants; Q_FOREACH (const QString& key, KisPaintingAssistantFactoryRegistry::instance()->keys()) { QString name = KisPaintingAssistantFactoryRegistry::instance()->get(key)->name(); assistants << KoID(key, name); } std::sort(assistants.begin(), assistants.end(), KoID::compareNames); Q_FOREACH(const KoID &id, assistants) { m_options.comboBox->addItem(id.name(), id.id()); } connect(m_options.saveButton, SIGNAL(clicked()), SLOT(saveAssistants())); connect(m_options.loadButton, SIGNAL(clicked()), SLOT(loadAssistants())); connect(m_options.deleteButton, SIGNAL(clicked()), SLOT(removeAllAssistants())); } return m_optionsWidget; } diff --git a/plugins/dockers/animation/kis_time_based_item_model.cpp b/plugins/dockers/animation/kis_time_based_item_model.cpp index 55b158cdee..b34e9dd393 100644 --- a/plugins/dockers/animation/kis_time_based_item_model.cpp +++ b/plugins/dockers/animation/kis_time_based_item_model.cpp @@ -1,436 +1,437 @@ /* * Copyright (c) 2016 Jouni Pentikäinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_time_based_item_model.h" #include #include #include "kis_animation_frame_cache.h" #include "kis_animation_player.h" #include "kis_signal_compressor_with_param.h" #include "kis_image.h" #include "kis_image_animation_interface.h" #include "kis_time_range.h" #include "kis_animation_utils.h" #include "kis_keyframe_channel.h" #include "kis_processing_applicator.h" #include "KisImageBarrierLockerWithFeedback.h" struct KisTimeBasedItemModel::Private { Private() : animationPlayer(0) , numFramesOverride(0) , activeFrameIndex(0) , scrubInProgress(false) , scrubStartFrame(-1) {} KisImageWSP image; KisAnimationFrameCacheWSP framesCache; QPointer animationPlayer; QVector cachedFrames; int numFramesOverride; int activeFrameIndex; bool scrubInProgress; int scrubStartFrame; QScopedPointer > scrubbingCompressor; int baseNumFrames() const { if (image.isNull()) return 0; KisImageAnimationInterface *i = image->animationInterface(); if (!i) return 1; return i->totalLength(); } int effectiveNumFrames() const { if (image.isNull()) return 0; return qMax(baseNumFrames(), numFramesOverride); } int framesPerSecond() { return image->animationInterface()->framerate(); } }; KisTimeBasedItemModel::KisTimeBasedItemModel(QObject *parent) : QAbstractTableModel(parent) , m_d(new Private()) { KisConfig cfg; using namespace std::placeholders; std::function callback( std::bind(&KisTimeBasedItemModel::slotInternalScrubPreviewRequested, this, _1)); m_d->scrubbingCompressor.reset( new KisSignalCompressorWithParam(cfg.scrubbingUpdatesDelay(), callback, KisSignalCompressor::FIRST_ACTIVE)); } KisTimeBasedItemModel::~KisTimeBasedItemModel() {} void KisTimeBasedItemModel::setImage(KisImageWSP image) { KisImageWSP oldImage = m_d->image; m_d->image = image; if (image) { KisImageAnimationInterface *ai = image->animationInterface(); slotCurrentTimeChanged(ai->currentUITime()); connect(ai, SIGNAL(sigFramerateChanged()), SLOT(slotFramerateChanged())); connect(ai, SIGNAL(sigUiTimeChanged(int)), SLOT(slotCurrentTimeChanged(int))); } if (image != oldImage) { - reset(); + beginResetModel(); + endResetModel(); } } void KisTimeBasedItemModel::setFrameCache(KisAnimationFrameCacheSP cache) { if (KisAnimationFrameCacheSP(m_d->framesCache) == cache) return; if (m_d->framesCache) { m_d->framesCache->disconnect(this); } m_d->framesCache = cache; if (m_d->framesCache) { connect(m_d->framesCache, SIGNAL(changed()), SLOT(slotCacheChanged())); } } void KisTimeBasedItemModel::setAnimationPlayer(KisAnimationPlayer *player) { if (m_d->animationPlayer == player) return; if (m_d->animationPlayer) { m_d->animationPlayer->disconnect(this); } m_d->animationPlayer = player; if (m_d->animationPlayer) { connect(m_d->animationPlayer, SIGNAL(sigPlaybackStopped()), SLOT(slotPlaybackStopped())); connect(m_d->animationPlayer, SIGNAL(sigFrameChanged()), SLOT(slotPlaybackFrameChanged())); } } void KisTimeBasedItemModel::setLastVisibleFrame(int time) { const int growThreshold = m_d->effectiveNumFrames() - 3; const int growValue = time + 8; const int shrinkThreshold = m_d->effectiveNumFrames() - 12; const int shrinkValue = qMax(m_d->baseNumFrames(), qMin(growValue, shrinkThreshold)); const bool canShrink = m_d->effectiveNumFrames() > m_d->baseNumFrames(); if (time >= growThreshold) { beginInsertColumns(QModelIndex(), m_d->effectiveNumFrames(), growValue - 1); m_d->numFramesOverride = growValue; endInsertColumns(); } else if (time < shrinkThreshold && canShrink) { beginRemoveColumns(QModelIndex(), shrinkValue, m_d->effectiveNumFrames() - 1); m_d->numFramesOverride = shrinkValue; endRemoveColumns(); } } int KisTimeBasedItemModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_d->effectiveNumFrames(); } QVariant KisTimeBasedItemModel::data(const QModelIndex &index, int role) const { switch (role) { case ActiveFrameRole: { return index.column() == m_d->activeFrameIndex; } } return QVariant(); } bool KisTimeBasedItemModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) return false; switch (role) { case ActiveFrameRole: { setHeaderData(index.column(), Qt::Horizontal, value, role); break; } } return false; } QVariant KisTimeBasedItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { switch (role) { case ActiveFrameRole: return section == m_d->activeFrameIndex; case FrameCachedRole: return m_d->cachedFrames.size() > section ? m_d->cachedFrames[section] : false; case FramesPerSecondRole: return m_d->framesPerSecond(); } } return QVariant(); } bool KisTimeBasedItemModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) { if (orientation == Qt::Horizontal) { switch (role) { case ActiveFrameRole: if (value.toBool() && section != m_d->activeFrameIndex) { int prevFrame = m_d->activeFrameIndex; m_d->activeFrameIndex = section; scrubTo(m_d->activeFrameIndex, m_d->scrubInProgress); /** * Optimization Hack Alert: * * ideally, we should emit all four signals, but... The * point is this code is used in a tight loop during * playback, so it should run as fast as possible. To tell * the story short, commenting out these three lines makes * playback run 15% faster ;) */ if (m_d->scrubInProgress) { //emit dataChanged(this->index(0, prevFrame), this->index(rowCount() - 1, prevFrame)); emit dataChanged(this->index(0, m_d->activeFrameIndex), this->index(rowCount() - 1, m_d->activeFrameIndex)); //emit headerDataChanged (Qt::Horizontal, prevFrame, prevFrame); //emit headerDataChanged (Qt::Horizontal, m_d->activeFrameIndex, m_d->activeFrameIndex); } else { emit dataChanged(this->index(0, prevFrame), this->index(rowCount() - 1, prevFrame)); emit dataChanged(this->index(0, m_d->activeFrameIndex), this->index(rowCount() - 1, m_d->activeFrameIndex)); emit headerDataChanged (Qt::Horizontal, prevFrame, prevFrame); emit headerDataChanged (Qt::Horizontal, m_d->activeFrameIndex, m_d->activeFrameIndex); } } } } return false; } bool KisTimeBasedItemModel::removeFrames(const QModelIndexList &indexes) { KisAnimationUtils::FrameItemList frameItems; { KisImageBarrierLockerWithFeedback locker(m_d->image); Q_FOREACH (const QModelIndex &index, indexes) { int time = index.column(); Q_FOREACH(KisKeyframeChannel *channel, channelsAt(index)) { if (channel->keyframeAt(time)) { frameItems << KisAnimationUtils::FrameItem(channel->node(), channel->id(), index.column()); } } } } if (frameItems.isEmpty()) return false; KisAnimationUtils::removeKeyframes(m_d->image, frameItems); return true; } KUndo2Command* KisTimeBasedItemModel::createOffsetFramesCommand(QModelIndexList srcIndexes, const QPoint &offset, bool copyFrames, KUndo2Command *parentCommand) { if (srcIndexes.isEmpty()) return 0; if (offset.isNull()) return 0; KisAnimationUtils::sortPointsForSafeMove(&srcIndexes, offset); KisAnimationUtils::FrameItemList srcFrameItems; KisAnimationUtils::FrameItemList dstFrameItems; Q_FOREACH (const QModelIndex &srcIndex, srcIndexes) { QModelIndex dstIndex = index( srcIndex.row() + offset.y(), srcIndex.column() + offset.x()); KisNodeSP srcNode = nodeAt(srcIndex); KisNodeSP dstNode = nodeAt(dstIndex); if (!srcNode || !dstNode) { return 0; } Q_FOREACH(KisKeyframeChannel *channel, channelsAt(srcIndex)) { if (channel->keyframeAt(srcIndex.column())) { srcFrameItems << KisAnimationUtils::FrameItem(srcNode, channel->id(), srcIndex.column()); dstFrameItems << KisAnimationUtils::FrameItem(dstNode, channel->id(), dstIndex.column()); } } } KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(srcFrameItems.size() == dstFrameItems.size(), 0); if (srcFrameItems.isEmpty()) return 0; return KisAnimationUtils::createMoveKeyframesCommand(srcFrameItems, dstFrameItems, copyFrames, parentCommand); } bool KisTimeBasedItemModel::offsetFrames(QModelIndexList srcIndexes, const QPoint &offset, bool copyFrames) { KUndo2Command *cmd = 0; { KisImageBarrierLockerWithFeedback locker(m_d->image); cmd = createOffsetFramesCommand(srcIndexes, offset, copyFrames); } if (cmd) { KisProcessingApplicator::runSingleCommandStroke(m_d->image, cmd, KisStrokeJobData::BARRIER); } return cmd; } void KisTimeBasedItemModel::slotInternalScrubPreviewRequested(int time) { if (m_d->animationPlayer && !m_d->animationPlayer->isPlaying()) { m_d->animationPlayer->displayFrame(time); } } void KisTimeBasedItemModel::setScrubState(bool active) { if (!m_d->scrubInProgress && active) { m_d->scrubStartFrame = m_d->activeFrameIndex; m_d->scrubInProgress = true; } if (m_d->scrubInProgress && !active) { m_d->scrubInProgress = false; if (m_d->scrubStartFrame >= 0 && m_d->scrubStartFrame != m_d->activeFrameIndex) { scrubTo(m_d->activeFrameIndex, false); } m_d->scrubStartFrame = -1; } } void KisTimeBasedItemModel::scrubTo(int time, bool preview) { if (m_d->animationPlayer && m_d->animationPlayer->isPlaying()) return; KIS_ASSERT_RECOVER_RETURN(m_d->image); if (preview) { if (m_d->animationPlayer) { m_d->scrubbingCompressor->start(time); } } else { m_d->image->animationInterface()->requestTimeSwitchWithUndo(time); } } void KisTimeBasedItemModel::slotFramerateChanged() { emit headerDataChanged(Qt::Horizontal, 0, columnCount() - 1); } void KisTimeBasedItemModel::slotCurrentTimeChanged(int time) { if (time != m_d->activeFrameIndex) { setHeaderData(time, Qt::Horizontal, true, ActiveFrameRole); } } void KisTimeBasedItemModel::slotCacheChanged() { const int numFrames = columnCount(); m_d->cachedFrames.resize(numFrames); for (int i = 0; i < numFrames; i++) { m_d->cachedFrames[i] = m_d->framesCache->frameStatus(i) == KisAnimationFrameCache::Cached; } emit headerDataChanged(Qt::Horizontal, 0, numFrames); } void KisTimeBasedItemModel::slotPlaybackFrameChanged() { if (!m_d->animationPlayer->isPlaying()) return; setData(index(0, m_d->animationPlayer->currentTime()), true, ActiveFrameRole); } void KisTimeBasedItemModel::slotPlaybackStopped() { setData(index(0, m_d->image->animationInterface()->currentUITime()), true, ActiveFrameRole); } void KisTimeBasedItemModel::setPlaybackRange(const KisTimeRange &range) { if (m_d->image.isNull()) return; KisImageAnimationInterface *i = m_d->image->animationInterface(); i->setPlaybackRange(range); } bool KisTimeBasedItemModel::isPlaybackActive() const { return m_d->animationPlayer && m_d->animationPlayer->isPlaying(); } int KisTimeBasedItemModel::currentTime() const { return m_d->image->animationInterface()->currentUITime(); } KisImageWSP KisTimeBasedItemModel::image() const { return m_d->image; } diff --git a/plugins/dockers/animation/timeline_frames_model.cpp b/plugins/dockers/animation/timeline_frames_model.cpp index e42bb122f5..732d3a6ada 100644 --- a/plugins/dockers/animation/timeline_frames_model.cpp +++ b/plugins/dockers/animation/timeline_frames_model.cpp @@ -1,717 +1,718 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "timeline_frames_model.h" #include #include #include #include #include #include #include "kis_layer.h" #include "kis_config.h" #include "kis_global.h" #include "kis_debug.h" #include "kis_image.h" #include "kis_image_animation_interface.h" #include "kis_undo_adapter.h" #include "kis_node_dummies_graph.h" #include "kis_dummies_facade_base.h" #include "kis_signal_compressor.h" #include "kis_signal_compressor_with_param.h" #include "kis_keyframe_channel.h" #include "kundo2command.h" #include "kis_post_execution_undo_adapter.h" #include #include "kis_animation_utils.h" #include "timeline_color_scheme.h" #include "kis_node_model.h" #include "kis_projection_leaf.h" #include "kis_time_range.h" #include "kis_node_view_color_scheme.h" #include "krita_utils.h" #include struct TimelineFramesModel::Private { Private() : activeLayerIndex(0), dummiesFacade(0), needFinishInsertRows(false), needFinishRemoveRows(false), updateTimer(200, KisSignalCompressor::FIRST_INACTIVE), parentOfRemovedNode(0) {} int activeLayerIndex; QPointer dummiesFacade; KisImageWSP image; bool needFinishInsertRows; bool needFinishRemoveRows; QList updateQueue; KisSignalCompressor updateTimer; KisNodeDummy* parentOfRemovedNode; QScopedPointer converter; QScopedPointer nodeInterface; QPersistentModelIndex lastClickedIndex; QVariant layerName(int row) const { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return QVariant(); return dummy->node()->name(); } bool layerEditable(int row) const { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return true; return dummy->node()->visible() && !dummy->node()->userLocked(); } bool frameExists(int row, int column) const { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return false; KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id()); return (primaryChannel && primaryChannel->keyframeAt(column)); } bool specialKeyframeExists(int row, int column) { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return false; Q_FOREACH(KisKeyframeChannel *channel, dummy->node()->keyframeChannels()) { if (channel->id() != KisKeyframeChannel::Content.id() && channel->keyframeAt(column)) { return true; } } return false; } int frameColorLabel(int row, int column) { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return -1; KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id()); if (!primaryChannel) return -1; KisKeyframeSP frame = primaryChannel->keyframeAt(column); if (!frame) return -1; return frame->colorLabel(); } void setFrameColorLabel(int row, int column, int color) { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return; KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id()); if (!primaryChannel) return; KisKeyframeSP frame = primaryChannel->keyframeAt(column); if (!frame) return; frame->setColorLabel(color); } int layerColorLabel(int row) const { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return -1; return dummy->node()->colorLabelIndex(); } QVariant layerProperties(int row) const { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return QVariant(); PropertyList props = dummy->node()->sectionModelProperties(); return QVariant::fromValue(props); } bool setLayerProperties(int row, PropertyList props) { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return false; KisNodePropertyListCommand::setNodePropertiesNoUndo(dummy->node(), image, props); return true; } bool addKeyframe(int row, int column, bool copy) { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return false; KisNodeSP node = dummy->node(); if (!KisAnimationUtils::supportsContentFrames(node)) return false; KisAnimationUtils::createKeyframeLazy(image, node, KisKeyframeChannel::Content.id(), column, copy); return true; } bool addNewLayer(int row) { Q_UNUSED(row); if (nodeInterface) { KisLayerSP layer = nodeInterface->addPaintLayer(); layer->setUseInTimeline(true); } return true; } bool removeLayer(int row) { KisNodeDummy *dummy = converter->dummyFromRow(row); if (!dummy) return false; if (nodeInterface) { nodeInterface->removeNode(dummy->node()); } return true; } }; TimelineFramesModel::TimelineFramesModel(QObject *parent) : ModelWithExternalNotifications(parent), m_d(new Private) { connect(&m_d->updateTimer, SIGNAL(timeout()), SLOT(processUpdateQueue())); } TimelineFramesModel::~TimelineFramesModel() { } bool TimelineFramesModel::hasConnectionToCanvas() const { return m_d->dummiesFacade; } void TimelineFramesModel::setNodeManipulationInterface(NodeManipulationInterface *iface) { m_d->nodeInterface.reset(iface); } KisNodeSP TimelineFramesModel::nodeAt(QModelIndex index) const { /** * The dummy might not exist because the user could (quickly) change * active layer and the list of the nodes in m_d->converter will change. */ KisNodeDummy *dummy = m_d->converter->dummyFromRow(index.row()); return dummy ? dummy->node() : 0; } QMap TimelineFramesModel::channelsAt(QModelIndex index) const { KisNodeDummy *srcDummy = m_d->converter->dummyFromRow(index.row()); return srcDummy->node()->keyframeChannels(); } void TimelineFramesModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImageSP image) { KisDummiesFacadeBase *oldDummiesFacade = m_d->dummiesFacade; if (m_d->dummiesFacade && m_d->image) { m_d->image->animationInterface()->disconnect(this); m_d->image->disconnect(this); m_d->dummiesFacade->disconnect(this); } m_d->image = image; KisTimeBasedItemModel::setImage(image); m_d->dummiesFacade = dummiesFacade; m_d->converter.reset(); if (m_d->dummiesFacade) { m_d->converter.reset(new TimelineNodeListKeeper(this, m_d->dummiesFacade)); connect(m_d->dummiesFacade, SIGNAL(sigDummyChanged(KisNodeDummy*)), SLOT(slotDummyChanged(KisNodeDummy*))); connect(m_d->image->animationInterface(), SIGNAL(sigFullClipRangeChanged()), SIGNAL(sigInfiniteTimelineUpdateNeeded())); connect(m_d->image->animationInterface(), SIGNAL(sigAudioChannelChanged()), SIGNAL(sigAudioChannelChanged())); connect(m_d->image->animationInterface(), SIGNAL(sigAudioVolumeChanged()), SIGNAL(sigAudioChannelChanged())); } if (m_d->dummiesFacade != oldDummiesFacade) { - reset(); + beginResetModel(); + endResetModel(); } if (m_d->dummiesFacade) { emit sigInfiniteTimelineUpdateNeeded(); emit sigAudioChannelChanged(); } } void TimelineFramesModel::slotDummyChanged(KisNodeDummy *dummy) { if (!m_d->updateQueue.contains(dummy)) { m_d->updateQueue.append(dummy); } m_d->updateTimer.start(); } void TimelineFramesModel::processUpdateQueue() { Q_FOREACH (KisNodeDummy *dummy, m_d->updateQueue) { int row = m_d->converter->rowForDummy(dummy); if (row >= 0) { emit headerDataChanged (Qt::Vertical, row, row); emit dataChanged(this->index(row, 0), this->index(row, columnCount() - 1)); } } m_d->updateQueue.clear(); } void TimelineFramesModel::slotCurrentNodeChanged(KisNodeSP node) { if (!node) { m_d->activeLayerIndex = -1; return; } KisNodeDummy *dummy = m_d->dummiesFacade->dummyForNode(node); KIS_ASSERT_RECOVER_RETURN(dummy); m_d->converter->updateActiveDummy(dummy); const int row = m_d->converter->rowForDummy(dummy); if (row < 0) { qWarning() << "WARNING: TimelineFramesModel::slotCurrentNodeChanged: node not found!"; } if (row >= 0 && m_d->activeLayerIndex != row) { setData(index(row, 0), true, ActiveLayerRole); } } int TimelineFramesModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); if(!m_d->dummiesFacade) return 0; return m_d->converter->rowCount(); } QVariant TimelineFramesModel::data(const QModelIndex &index, int role) const { if(!m_d->dummiesFacade) return QVariant(); switch (role) { case ActiveLayerRole: { return index.row() == m_d->activeLayerIndex; } case FrameEditableRole: { return m_d->layerEditable(index.row()); } case FrameExistsRole: { return m_d->frameExists(index.row(), index.column()); } case SpecialKeyframeExists: { return m_d->specialKeyframeExists(index.row(), index.column()); } case FrameColorLabelIndexRole: { int label = m_d->frameColorLabel(index.row(), index.column()); return label > 0 ? label : QVariant(); } case Qt::DisplayRole: { return m_d->layerName(index.row()); } case Qt::TextAlignmentRole: { return QVariant(Qt::AlignHCenter | Qt::AlignVCenter); } case KoResourceModel::LargeThumbnailRole: { KisNodeDummy *dummy = m_d->converter->dummyFromRow(index.row()); if (!dummy) { return QVariant(); } const int maxSize = 200; QSize size = dummy->node()->extent().size(); size.scale(maxSize, maxSize, Qt::KeepAspectRatio); if (size.width() == 0 || size.height() == 0) { // No thumbnail can be shown if there isn't width or height... return QVariant(); } QImage image(dummy->node()->createThumbnailForFrame(size.width(), size.height(), index.column())); return image; } } return ModelWithExternalNotifications::data(index, role); } bool TimelineFramesModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || !m_d->dummiesFacade) return false; switch (role) { case ActiveLayerRole: { if (value.toBool() && index.row() != m_d->activeLayerIndex) { int prevLayer = m_d->activeLayerIndex; m_d->activeLayerIndex = index.row(); emit dataChanged(this->index(prevLayer, 0), this->index(prevLayer, columnCount() - 1)); emit dataChanged(this->index(m_d->activeLayerIndex, 0), this->index(m_d->activeLayerIndex, columnCount() - 1)); emit headerDataChanged(Qt::Vertical, prevLayer, prevLayer); emit headerDataChanged(Qt::Vertical, m_d->activeLayerIndex, m_d->activeLayerIndex); KisNodeDummy *dummy = m_d->converter->dummyFromRow(m_d->activeLayerIndex); KIS_ASSERT_RECOVER(dummy) { return true; } emit requestCurrentNodeChanged(dummy->node()); emit sigEnsureRowVisible(m_d->activeLayerIndex); } break; } case FrameColorLabelIndexRole: { m_d->setFrameColorLabel(index.row(), index.column(), value.toInt()); } break; } return ModelWithExternalNotifications::setData(index, value, role); } QVariant TimelineFramesModel::headerData(int section, Qt::Orientation orientation, int role) const { if(!m_d->dummiesFacade) return QVariant(); if (orientation == Qt::Vertical) { switch (role) { case ActiveLayerRole: return section == m_d->activeLayerIndex; case Qt::DisplayRole: { QVariant value = headerData(section, orientation, Qt::ToolTipRole); if (!value.isValid()) return value; QString name = value.toString(); const int maxNameSize = 13; if (name.size() > maxNameSize) { name = QString("%1...").arg(name.left(maxNameSize)); } return name; } case Qt::TextColorRole: { // WARNING: this role doesn't work for header views! Use // bold font to show isolated mode instead! return QVariant(); } case Qt::FontRole: { KisNodeDummy *dummy = m_d->converter->dummyFromRow(section); if (!dummy) return QVariant(); KisNodeSP node = dummy->node(); QFont baseFont; if (node->projectionLeaf()->isDroppedMask()) { baseFont.setStrikeOut(true); } else if (m_d->image && m_d->image->isolatedModeRoot() && KisNodeModel::belongsToIsolatedGroup(m_d->image, node, m_d->dummiesFacade)) { baseFont.setBold(true); } return baseFont; } case Qt::ToolTipRole: { return m_d->layerName(section); } case TimelinePropertiesRole: { return QVariant::fromValue(m_d->layerProperties(section)); } case OtherLayersRole: { TimelineNodeListKeeper::OtherLayersList list = m_d->converter->otherLayersList(); return QVariant::fromValue(list); } case LayerUsedInTimelineRole: { KisNodeDummy *dummy = m_d->converter->dummyFromRow(section); if (!dummy) return QVariant(); return dummy->node()->useInTimeline(); } case Qt::BackgroundRole: { int label = m_d->layerColorLabel(section); if (label > 0) { KisNodeViewColorScheme scm; QColor color = scm.colorLabel(label); QPalette pal = qApp->palette(); color = KritaUtils::blendColors(color, pal.color(QPalette::Button), 0.3); return QBrush(color); } else { return QVariant(); } } } } return ModelWithExternalNotifications::headerData(section, orientation, role); } bool TimelineFramesModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) { if (!m_d->dummiesFacade) return false; if (orientation == Qt::Vertical) { switch (role) { case ActiveLayerRole: { setData(index(section, 0), value, role); break; } case TimelinePropertiesRole: { TimelineFramesModel::PropertyList props = value.value(); int result = m_d->setLayerProperties(section, props); emit headerDataChanged (Qt::Vertical, section, section); return result; } case LayerUsedInTimelineRole: { KisNodeDummy *dummy = m_d->converter->dummyFromRow(section); if (!dummy) return false; dummy->node()->setUseInTimeline(value.toBool()); return true; } } } return ModelWithExternalNotifications::setHeaderData(section, orientation, value, role); } Qt::DropActions TimelineFramesModel::supportedDragActions() const { return Qt::MoveAction | Qt::CopyAction; } Qt::DropActions TimelineFramesModel::supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction; } QStringList TimelineFramesModel::mimeTypes() const { QStringList types; types << QLatin1String("application/x-krita-frame"); return types; } void TimelineFramesModel::setLastClickedIndex(const QModelIndex &index) { m_d->lastClickedIndex = index; } QMimeData* TimelineFramesModel::mimeData(const QModelIndexList &indexes) const { QMimeData *data = new QMimeData(); QByteArray encoded; QDataStream stream(&encoded, QIODevice::WriteOnly); const int baseRow = m_d->lastClickedIndex.row(); const int baseColumn = m_d->lastClickedIndex.column(); stream << indexes.size(); stream << baseRow << baseColumn; Q_FOREACH (const QModelIndex &index, indexes) { stream << index.row() - baseRow << index.column() - baseColumn; } data->setData("application/x-krita-frame", encoded); return data; } inline void decodeBaseIndex(QByteArray *encoded, int *row, int *col) { int size_UNUSED = 0; QDataStream stream(encoded, QIODevice::ReadOnly); stream >> size_UNUSED >> *row >> *col; } bool TimelineFramesModel::canDropFrameData(const QMimeData */*data*/, const QModelIndex &index) { if (!index.isValid()) return false; /** * Now we support D&D around any layer, so just return 'true' all * the time. */ return true; } bool TimelineFramesModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(row); Q_UNUSED(column); bool result = false; if ((action != Qt::MoveAction && action != Qt::CopyAction) || !parent.isValid()) return result; const bool copyFrames = action == Qt::CopyAction; QByteArray encoded = data->data("application/x-krita-frame"); QDataStream stream(&encoded, QIODevice::ReadOnly); int size, baseRow, baseColumn; stream >> size >> baseRow >> baseColumn; QModelIndexList srcIndexes; for (int i = 0; i < size; i++) { int relRow, relColumn; stream >> relRow >> relColumn; int srcRow = baseRow + relRow; int srcColumn = baseColumn + relColumn; srcIndexes << index(srcRow, srcColumn); } const QPoint offset(parent.column() - baseColumn, parent.row() - baseRow); return offsetFrames(srcIndexes, offset, copyFrames); } Qt::ItemFlags TimelineFramesModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = ModelWithExternalNotifications::flags(index); if (!index.isValid()) return flags; if (m_d->frameExists(index.row(), index.column()) || m_d->specialKeyframeExists(index.row(), index.column())) { if (data(index, FrameEditableRole).toBool()) { flags |= Qt::ItemIsDragEnabled; } } /** * Basically we should forbid overrides only if we D&D a single frame * and allow it when we D&D multiple frames. But we cannot distinguish * it here... So allow all the time. */ flags |= Qt::ItemIsDropEnabled; return flags; } bool TimelineFramesModel::insertRows(int row, int count, const QModelIndex &parent) { Q_UNUSED(parent); KIS_ASSERT_RECOVER(count == 1) { return false; } if (row < 0 || row > rowCount()) return false; bool result = m_d->addNewLayer(row); return result; } bool TimelineFramesModel::removeRows(int row, int count, const QModelIndex &parent) { Q_UNUSED(parent); KIS_ASSERT_RECOVER(count == 1) { return false; } if (row < 0 || row >= rowCount()) return false; bool result = m_d->removeLayer(row); return result; } bool TimelineFramesModel::insertOtherLayer(int index, int dstRow) { Q_UNUSED(dstRow); TimelineNodeListKeeper::OtherLayersList list = m_d->converter->otherLayersList(); if (index < 0 || index >= list.size()) return false; list[index].dummy->node()->setUseInTimeline(true); dstRow = m_d->converter->rowForDummy(list[index].dummy); setData(this->index(dstRow, 0), true, ActiveLayerRole); return true; } int TimelineFramesModel::activeLayerRow() const { return m_d->activeLayerIndex; } bool TimelineFramesModel::createFrame(const QModelIndex &dstIndex) { if (!dstIndex.isValid()) return false; return m_d->addKeyframe(dstIndex.row(), dstIndex.column(), false); } bool TimelineFramesModel::copyFrame(const QModelIndex &dstIndex) { if (!dstIndex.isValid()) return false; return m_d->addKeyframe(dstIndex.row(), dstIndex.column(), true); } QString TimelineFramesModel::audioChannelFileName() const { return m_d->image ? m_d->image->animationInterface()->audioChannelFileName() : QString(); } void TimelineFramesModel::setAudioChannelFileName(const QString &fileName) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->image); m_d->image->animationInterface()->setAudioChannelFileName(fileName); } bool TimelineFramesModel::isAudioMuted() const { return m_d->image ? m_d->image->animationInterface()->isAudioMuted() : false; } void TimelineFramesModel::setAudioMuted(bool value) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->image); m_d->image->animationInterface()->setAudioMuted(value); } qreal TimelineFramesModel::audioVolume() const { return m_d->image ? m_d->image->animationInterface()->audioVolume() : 0.5; } void TimelineFramesModel::setAudioVolume(qreal value) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->image); m_d->image->animationInterface()->setAudioVolume(value); } diff --git a/plugins/dockers/animation/timeline_frames_view.cpp b/plugins/dockers/animation/timeline_frames_view.cpp index 3f9ee73af7..a5759a4664 100644 --- a/plugins/dockers/animation/timeline_frames_view.cpp +++ b/plugins/dockers/animation/timeline_frames_view.cpp @@ -1,1098 +1,1098 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "timeline_frames_view.h" #include "timeline_frames_model.h" #include "timeline_ruler_header.h" #include "timeline_layers_header.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_debug.h" #include "timeline_frames_item_delegate.h" #include "kis_zoom_button.h" #include "kis_icon_utils.h" #include "kis_animation_utils.h" #include "kis_custom_modifiers_catcher.h" #include "kis_action.h" #include "kis_signal_compressor.h" #include "kis_time_range.h" #include "kis_color_label_selector_widget.h" #include "kis_slider_spin_box.h" #include #include #include -#include +#include #include #include "config-qtmultimedia.h" typedef QPair QItemViewPaintPair; typedef QList QItemViewPaintPairs; struct TimelineFramesView::Private { Private(TimelineFramesView *_q) : q(_q), fps(1), zoomStillPointIndex(-1), zoomStillPointOriginalOffset(0), dragInProgress(false), dragWasSuccessful(false), modifiersCatcher(0), selectionChangedCompressor(300, KisSignalCompressor::FIRST_INACTIVE) {} TimelineFramesView *q; TimelineFramesModel *model; TimelineRulerHeader *horizontalRuler; TimelineLayersHeader *layersHeader; int fps; int zoomStillPointIndex; int zoomStillPointOriginalOffset; QPoint initialDragPanValue; QPoint initialDragPanPos; QToolButton *addLayersButton; KisAction *showHideLayerAction; QToolButton *audioOptionsButton; KisColorLabelSelectorWidget *colorSelector; QWidgetAction *colorSelectorAction; KisColorLabelSelectorWidget *multiframeColorSelector; QWidgetAction *multiframeColorSelectorAction; QMenu *audioOptionsMenu; QAction *openAudioAction; QAction *audioMuteAction; KisSliderSpinBox *volumeSlider; QMenu *layerEditingMenu; QMenu *existingLayersMenu; QMenu *frameCreationMenu; QMenu *frameEditingMenu; QMenu *multipleFrameEditingMenu; QMap globalActions; KisZoomButton *zoomDragButton; bool dragInProgress; bool dragWasSuccessful; KisCustomModifiersCatcher *modifiersCatcher; QPoint lastPressedPosition; Qt::KeyboardModifiers lastPressedModifier; KisSignalCompressor selectionChangedCompressor; QStyleOptionViewItem viewOptionsV4() const; QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const; QPixmap renderToPixmap(const QModelIndexList &indexes, QRect *r) const; KoIconToolTip tip; }; TimelineFramesView::TimelineFramesView(QWidget *parent) : QTableView(parent), m_d(new Private(this)) { m_d->modifiersCatcher = new KisCustomModifiersCatcher(this); m_d->modifiersCatcher->addModifier("pan-zoom", Qt::Key_Space); m_d->modifiersCatcher->addModifier("offset-frame", Qt::Key_Alt); setCornerButtonEnabled(false); setSelectionBehavior(QAbstractItemView::SelectItems); setSelectionMode(QAbstractItemView::ExtendedSelection); setItemDelegate(new TimelineFramesItemDelegate(this)); setDragEnabled(true); setDragDropMode(QAbstractItemView::DragDrop); setAcceptDrops(true); setDropIndicatorShown(true); setDefaultDropAction(Qt::MoveAction); m_d->horizontalRuler = new TimelineRulerHeader(this); this->setHorizontalHeader(m_d->horizontalRuler); m_d->layersHeader = new TimelineLayersHeader(this); m_d->layersHeader->setSectionResizeMode(QHeaderView::Fixed); m_d->layersHeader->setDefaultSectionSize(24); m_d->layersHeader->setMinimumWidth(60); m_d->layersHeader->setHighlightSections(true); this->setVerticalHeader(m_d->layersHeader); connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), SLOT(slotUpdateInfiniteFramesCount())); connect(horizontalScrollBar(), SIGNAL(sliderReleased()), SLOT(slotUpdateInfiniteFramesCount())); /********** New Layer Menu ***********************************************************/ m_d->addLayersButton = new QToolButton(this); m_d->addLayersButton->setAutoRaise(true); m_d->addLayersButton->setIcon(KisIconUtils::loadIcon("addlayer")); m_d->addLayersButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); m_d->addLayersButton->setPopupMode(QToolButton::InstantPopup); m_d->layerEditingMenu = new QMenu(this); m_d->layerEditingMenu->addAction(KisAnimationUtils::newLayerActionName, this, SLOT(slotAddNewLayer())); m_d->existingLayersMenu = m_d->layerEditingMenu->addMenu(KisAnimationUtils::addExistingLayerActionName); m_d->layerEditingMenu->addSeparator(); m_d->layerEditingMenu->addAction(KisAnimationUtils::removeLayerActionName, this, SLOT(slotRemoveLayer())); connect(m_d->existingLayersMenu, SIGNAL(aboutToShow()), SLOT(slotUpdateLayersMenu())); connect(m_d->existingLayersMenu, SIGNAL(triggered(QAction*)), SLOT(slotAddExistingLayer(QAction*))); connect(m_d->layersHeader, SIGNAL(sigRequestContextMenu(const QPoint&)), SLOT(slotLayerContextMenuRequested(const QPoint&))); m_d->addLayersButton->setMenu(m_d->layerEditingMenu); /********** Audio Channel Menu *******************************************************/ m_d->audioOptionsButton = new QToolButton(this); m_d->audioOptionsButton->setAutoRaise(true); m_d->audioOptionsButton->setIcon(KisIconUtils::loadIcon("audio-none")); m_d->audioOptionsButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); m_d->audioOptionsButton->setPopupMode(QToolButton::InstantPopup); m_d->audioOptionsMenu = new QMenu(this); #ifndef HAVE_QT_MULTIMEDIA m_d->audioOptionsMenu->addSection(i18nc("@item:inmenu", "Audio playback is not supported in this build!")); #endif m_d->openAudioAction= new QAction("XXX", this); connect(m_d->openAudioAction, SIGNAL(triggered()), this, SLOT(slotSelectAudioChannelFile())); m_d->audioOptionsMenu->addAction(m_d->openAudioAction); m_d->audioMuteAction = new QAction(i18nc("@item:inmenu", "Mute"), this); m_d->audioMuteAction->setCheckable(true); connect(m_d->audioMuteAction, SIGNAL(triggered(bool)), SLOT(slotAudioChannelMute(bool))); m_d->audioOptionsMenu->addAction(m_d->audioMuteAction); m_d->audioOptionsMenu->addAction(i18nc("@item:inmenu", "Remove audio"), this, SLOT(slotAudioChannelRemove())); m_d->audioOptionsMenu->addSeparator(); m_d->volumeSlider = new KisSliderSpinBox(this); m_d->volumeSlider->setRange(0, 100); m_d->volumeSlider->setSuffix("%"); m_d->volumeSlider->setPrefix(i18nc("@item:inmenu, slider", "Volume:")); m_d->volumeSlider->setSingleStep(1); m_d->volumeSlider->setPageStep(10); m_d->volumeSlider->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); connect(m_d->volumeSlider, SIGNAL(valueChanged(int)), SLOT(slotAudioVolumeChanged(int))); QWidgetAction *volumeAction = new QWidgetAction(m_d->audioOptionsMenu); volumeAction->setDefaultWidget(m_d->volumeSlider); m_d->audioOptionsMenu->addAction(volumeAction); m_d->audioOptionsButton->setMenu(m_d->audioOptionsMenu); /********** Frame Editing Context Menu ***********************************************/ m_d->frameCreationMenu = new QMenu(this); m_d->frameCreationMenu->addAction(KisAnimationUtils::addFrameActionName, this, SLOT(slotNewFrame())); m_d->frameCreationMenu->addAction(KisAnimationUtils::duplicateFrameActionName, this, SLOT(slotCopyFrame())); m_d->colorSelector = new KisColorLabelSelectorWidget(this); m_d->colorSelectorAction = new QWidgetAction(this); m_d->colorSelectorAction->setDefaultWidget(m_d->colorSelector); connect(m_d->colorSelector, &KisColorLabelSelectorWidget::currentIndexChanged, this, &TimelineFramesView::slotColorLabelChanged); m_d->frameEditingMenu = new QMenu(this); m_d->frameEditingMenu->addAction(KisAnimationUtils::removeFrameActionName, this, SLOT(slotRemoveFrame())); m_d->frameEditingMenu->addAction(m_d->colorSelectorAction); m_d->multiframeColorSelector = new KisColorLabelSelectorWidget(this); m_d->multiframeColorSelectorAction = new QWidgetAction(this); m_d->multiframeColorSelectorAction->setDefaultWidget(m_d->multiframeColorSelector); connect(m_d->multiframeColorSelector, &KisColorLabelSelectorWidget::currentIndexChanged, this, &TimelineFramesView::slotColorLabelChanged); m_d->multipleFrameEditingMenu = new QMenu(this); m_d->multipleFrameEditingMenu->addAction(KisAnimationUtils::removeFramesActionName, this, SLOT(slotRemoveFrame())); m_d->multipleFrameEditingMenu->addAction(m_d->multiframeColorSelectorAction); /********** Zoom Button **************************************************************/ m_d->zoomDragButton = new KisZoomButton(this); m_d->zoomDragButton->setAutoRaise(true); m_d->zoomDragButton->setIcon(KisIconUtils::loadIcon("zoom-horizontal")); m_d->zoomDragButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); m_d->zoomDragButton->setToolTip(i18nc("@info:tooltip", "Zoom Timeline. Hold down and drag left or right.")); m_d->zoomDragButton->setPopupMode(QToolButton::InstantPopup); connect(m_d->zoomDragButton, SIGNAL(zoomLevelChanged(qreal)), SLOT(slotZoomButtonChanged(qreal))); connect(m_d->zoomDragButton, SIGNAL(zoomStarted(qreal)), SLOT(slotZoomButtonPressed(qreal))); setFramesPerSecond(12); setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); connect(&m_d->selectionChangedCompressor, SIGNAL(timeout()), SLOT(slotSelectionChanged())); } TimelineFramesView::~TimelineFramesView() { } QMap TimelineFramesView::globalActions() const { return m_d->globalActions; } void TimelineFramesView::setShowInTimeline(KisAction* action) { m_d->showHideLayerAction = action; m_d->layerEditingMenu->addAction(m_d->showHideLayerAction); } void resizeToMinimalSize(QAbstractButton *w, int minimalSize) { QSize buttonSize = w->sizeHint(); if (buttonSize.height() > minimalSize) { buttonSize = QSize(minimalSize, minimalSize); } w->resize(buttonSize); } void TimelineFramesView::updateGeometries() { QTableView::updateGeometries(); const int availableHeight = m_d->horizontalRuler->height(); const int margin = 2; const int minimalSize = availableHeight - 2 * margin; resizeToMinimalSize(m_d->addLayersButton, minimalSize); resizeToMinimalSize(m_d->audioOptionsButton, minimalSize); resizeToMinimalSize(m_d->zoomDragButton, minimalSize); int x = 2 * margin; int y = (availableHeight - minimalSize) / 2; m_d->addLayersButton->move(x, 2 * y); m_d->audioOptionsButton->move(x + minimalSize + 2 * margin, 2 * y); const int availableWidth = m_d->layersHeader->width(); x = availableWidth - margin - minimalSize; m_d->zoomDragButton->move(x, 2 * y); } void TimelineFramesView::setModel(QAbstractItemModel *model) { TimelineFramesModel *framesModel = qobject_cast(model); m_d->model = framesModel; QTableView::setModel(model); connect(m_d->model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), this, SLOT(slotHeaderDataChanged(Qt::Orientation, int, int))); connect(m_d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotDataChanged(QModelIndex,QModelIndex))); connect(m_d->model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), this, SLOT(slotReselectCurrentIndex())); connect(m_d->model, SIGNAL(sigInfiniteTimelineUpdateNeeded()), this, SLOT(slotUpdateInfiniteFramesCount())); connect(m_d->model, SIGNAL(sigAudioChannelChanged()), this, SLOT(slotUpdateAudioActions())); connect(selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), &m_d->selectionChangedCompressor, SLOT(start())); connect(m_d->model, SIGNAL(sigEnsureRowVisible(int)), SLOT(slotEnsureRowVisible(int))); slotUpdateAudioActions(); } void TimelineFramesView::setFramesPerSecond(int fps) { m_d->fps = fps; m_d->horizontalRuler->setFramePerSecond(fps); // For some reason simple update sometimes doesn't work here, so // reset the whole header // // m_d->horizontalRuler->reset(); } void TimelineFramesView::slotZoomButtonPressed(qreal staticPoint) { m_d->zoomStillPointIndex = qIsNaN(staticPoint) ? currentIndex().column() : staticPoint; const int w = m_d->horizontalRuler->defaultSectionSize(); m_d->zoomStillPointOriginalOffset = w * m_d->zoomStillPointIndex - horizontalScrollBar()->value(); } void TimelineFramesView::slotZoomButtonChanged(qreal zoomLevel) { if (m_d->horizontalRuler->setZoom(zoomLevel)) { slotUpdateInfiniteFramesCount(); const int w = m_d->horizontalRuler->defaultSectionSize(); horizontalScrollBar()->setValue(w * m_d->zoomStillPointIndex - m_d->zoomStillPointOriginalOffset); viewport()->update(); } } void TimelineFramesView::slotColorLabelChanged(int label) { Q_FOREACH(QModelIndex index, selectedIndexes()) { m_d->model->setData(index, label, TimelineFramesModel::FrameColorLabelIndexRole); } KisImageConfig config; config.setDefaultFrameColorLabel(label); } void TimelineFramesView::slotSelectAudioChannelFile() { if (!m_d->model) return; - QString defaultDir = QDesktopServices::storageLocation(QDesktopServices::MusicLocation); + QString defaultDir = QStandardPaths::writableLocation(QStandardPaths::MusicLocation); const QString currentFile = m_d->model->audioChannelFileName(); QDir baseDir = QFileInfo(currentFile).absoluteDir(); if (baseDir.exists()) { defaultDir = baseDir.absolutePath(); } const QString result = KisImportExportManager::askForAudioFileName(defaultDir, this); const QFileInfo info(result); if (info.exists()) { m_d->model->setAudioChannelFileName(info.absoluteFilePath()); } } void TimelineFramesView::slotAudioChannelMute(bool value) { if (!m_d->model) return; if (value != m_d->model->isAudioMuted()) { m_d->model->setAudioMuted(value); } } void TimelineFramesView::slotAudioChannelRemove() { if (!m_d->model) return; m_d->model->setAudioChannelFileName(QString()); } void TimelineFramesView::slotUpdateAudioActions() { if (!m_d->model) return; const QString currentFile = m_d->model->audioChannelFileName(); if (currentFile.isEmpty()) { m_d->openAudioAction->setText(i18nc("@item:inmenu", "Open audio...")); } else { QFileInfo info(currentFile); m_d->openAudioAction->setText(i18nc("@item:inmenu", "Change audio (%1)...", info.fileName())); } m_d->audioMuteAction->setChecked(m_d->model->isAudioMuted()); QIcon audioIcon; if (currentFile.isEmpty()) { audioIcon = KisIconUtils::loadIcon("audio-none"); } else { if (m_d->model->isAudioMuted()) { audioIcon = KisIconUtils::loadIcon("audio-volume-mute"); } else { audioIcon = KisIconUtils::loadIcon("audio-volume-high"); } } m_d->audioOptionsButton->setIcon(audioIcon); m_d->volumeSlider->setEnabled(!m_d->model->isAudioMuted()); KisSignalsBlocker b(m_d->volumeSlider); m_d->volumeSlider->setValue(qRound(m_d->model->audioVolume() * 100.0)); } void TimelineFramesView::slotAudioVolumeChanged(int value) { m_d->model->setAudioVolume(qreal(value) / 100.0); } void TimelineFramesView::slotUpdateInfiniteFramesCount() { if (horizontalScrollBar()->isSliderDown()) return; const int sectionWidth = m_d->horizontalRuler->defaultSectionSize(); const int calculatedIndex = (horizontalScrollBar()->value() + m_d->horizontalRuler->width() - 1) / sectionWidth; m_d->model->setLastVisibleFrame(calculatedIndex); } void TimelineFramesView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { QTableView::currentChanged(current, previous); if (previous.column() != current.column()) { m_d->model->setData(previous, false, TimelineFramesModel::ActiveFrameRole); m_d->model->setData(current, true, TimelineFramesModel::ActiveFrameRole); } } QItemSelectionModel::SelectionFlags TimelineFramesView::selectionCommand(const QModelIndex &index, const QEvent *event) const { // WARNING: Copy-pasted from KisNodeView! Please keep in sync! /** * Qt has a bug: when we Ctrl+click on an item, the item's * selections gets toggled on mouse *press*, whereas usually it is * done on mouse *release*. Therefore the user cannot do a * Ctrl+D&D with the default configuration. This code fixes the * problem by manually returning QItemSelectionModel::NoUpdate * flag when the user clicks on an item and returning * QItemSelectionModel::Toggle on release. */ if (event && (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) && index.isValid()) { const QMouseEvent *mevent = static_cast(event); if (mevent->button() == Qt::RightButton && selectionModel()->selectedIndexes().contains(index)) { // Allow calling context menu for multiple layers return QItemSelectionModel::NoUpdate; } if (event->type() == QEvent::MouseButtonPress && (mevent->modifiers() & Qt::ControlModifier)) { return QItemSelectionModel::NoUpdate; } if (event->type() == QEvent::MouseButtonRelease && (mevent->modifiers() & Qt::ControlModifier)) { return QItemSelectionModel::Toggle; } } return QAbstractItemView::selectionCommand(index, event); } void TimelineFramesView::slotSelectionChanged() { int minColumn = std::numeric_limits::max(); int maxColumn = std::numeric_limits::min(); foreach (const QModelIndex &idx, selectedIndexes()) { if (idx.column() > maxColumn) { maxColumn = idx.column(); } if (idx.column() < minColumn) { minColumn = idx.column(); } } KisTimeRange range; if (maxColumn > minColumn) { range = KisTimeRange(minColumn, maxColumn - minColumn + 1); } m_d->model->setPlaybackRange(range); } void TimelineFramesView::slotReselectCurrentIndex() { QModelIndex index = currentIndex(); currentChanged(index, index); } void TimelineFramesView::slotEnsureRowVisible(int row) { QModelIndex index = currentIndex(); if (!index.isValid() || row < 0) return; index = m_d->model->index(row, index.column()); scrollTo(index); } void TimelineFramesView::slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { if (m_d->model->isPlaybackActive()) return; int selectedColumn = -1; for (int j = topLeft.column(); j <= bottomRight.column(); j++) { QVariant value = m_d->model->data( m_d->model->index(topLeft.row(), j), TimelineFramesModel::ActiveFrameRole); if (value.isValid() && value.toBool()) { selectedColumn = j; break; } } QModelIndex index = currentIndex(); if (!index.isValid() && selectedColumn < 0) { return; } if (selectedColumn == -1) { selectedColumn = index.column(); } if (selectedColumn != index.column() && !m_d->dragInProgress) { int row= index.isValid() ? index.row() : 0; setCurrentIndex(m_d->model->index(row, selectedColumn)); } } void TimelineFramesView::slotHeaderDataChanged(Qt::Orientation orientation, int first, int last) { Q_UNUSED(first); Q_UNUSED(last); if (orientation == Qt::Horizontal) { const int newFps = m_d->model->headerData(0, Qt::Horizontal, TimelineFramesModel::FramesPerSecondRole).toInt(); if (newFps != m_d->fps) { setFramesPerSecond(newFps); } } } void TimelineFramesView::rowsInserted(const QModelIndex& parent, int start, int end) { QTableView::rowsInserted(parent, start, end); } inline bool isIndexDragEnabled(QAbstractItemModel *model, const QModelIndex &index) { return (model->flags(index) & Qt::ItemIsDragEnabled); } QStyleOptionViewItem TimelineFramesView::Private::viewOptionsV4() const { QStyleOptionViewItem option = q->viewOptions(); option.locale = q->locale(); option.locale.setNumberOptions(QLocale::OmitGroupSeparator); option.widget = q; return option; } QItemViewPaintPairs TimelineFramesView::Private::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const { Q_ASSERT(r); QRect &rect = *r; const QRect viewportRect = q->viewport()->rect(); QItemViewPaintPairs ret; for (int i = 0; i < indexes.count(); ++i) { const QModelIndex &index = indexes.at(i); const QRect current = q->visualRect(index); if (current.intersects(viewportRect)) { ret += qMakePair(current, index); rect |= current; } } rect &= viewportRect; return ret; } QPixmap TimelineFramesView::Private::renderToPixmap(const QModelIndexList &indexes, QRect *r) const { Q_ASSERT(r); QItemViewPaintPairs paintPairs = draggablePaintPairs(indexes, r); if (paintPairs.isEmpty()) return QPixmap(); QPixmap pixmap(r->size()); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); QStyleOptionViewItem option = viewOptionsV4(); option.state |= QStyle::State_Selected; for (int j = 0; j < paintPairs.count(); ++j) { option.rect = paintPairs.at(j).first.translated(-r->topLeft()); const QModelIndex ¤t = paintPairs.at(j).second; //adjustViewOptionsForIndex(&option, current); q->itemDelegate(current)->paint(&painter, option, current); } return pixmap; } void TimelineFramesView::startDrag(Qt::DropActions supportedActions) { QModelIndexList indexes = selectionModel()->selectedIndexes(); if (!indexes.isEmpty() && m_d->modifiersCatcher->modifierPressed("offset-frame")) { QVector rows; int leftmostColumn = std::numeric_limits::max(); Q_FOREACH (const QModelIndex &index, indexes) { leftmostColumn = qMin(leftmostColumn, index.column()); if (!rows.contains(index.row())) { rows.append(index.row()); } } const int lastColumn = m_d->model->columnCount() - 1; selectionModel()->clear(); Q_FOREACH (const int row, rows) { QItemSelection sel(m_d->model->index(row, leftmostColumn), m_d->model->index(row, lastColumn)); selectionModel()->select(sel, QItemSelectionModel::Select); } supportedActions = Qt::MoveAction; { QModelIndexList indexes = selectedIndexes(); for(int i = indexes.count() - 1 ; i >= 0; --i) { if (!isIndexDragEnabled(m_d->model, indexes.at(i))) indexes.removeAt(i); } selectionModel()->clear(); if (indexes.count() > 0) { QMimeData *data = m_d->model->mimeData(indexes); if (!data) return; QRect rect; QPixmap pixmap = m_d->renderToPixmap(indexes, &rect); rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); QDrag *drag = new QDrag(this); drag->setPixmap(pixmap); drag->setMimeData(data); drag->setHotSpot(m_d->lastPressedPosition - rect.topLeft()); drag->exec(supportedActions, Qt::MoveAction); setCurrentIndex(currentIndex()); } } } else { /** * Workaround for Qt5's bug: if we start a dragging action right during * Shift-selection, Qt will get crazy. We cannot workaround it easily, * because we would need to fork mouseMoveEvent() for that (where the * decision about drag state is done). So we just abort dragging in that * case. * * BUG:373067 */ if (m_d->lastPressedModifier & Qt::ShiftModifier) { return; } /** * Workaround for Qt5's bugs: * * 1) Qt doesn't treat selection the selection on D&D * correctly, so we save it in advance and restore * afterwards. * * 2) There is a private variable in QAbstractItemView: * QAbstractItemView::Private::currentSelectionStartIndex. * It is initialized *only* when the setCurrentIndex() is called * explicitly on the view object, not on the selection model. * Therefore we should explicitly call setCurrentIndex() after * D&D, even if it already has *correct* value! * * 2) We should also call selectionModel()->select() * explicitly. There are two reasons for it: 1) Qt doesn't * maintain selection over D&D; 2) when reselecting single * element after D&D, Qt goes crazy, because it tries to * read *global* keyboard modifiers. Therefore if we are * dragging with Shift or Ctrl pressed it'll get crazy. So * just reset it explicitly. */ QModelIndexList selectionBefore = selectionModel()->selectedIndexes(); QModelIndex currentBefore = selectionModel()->currentIndex(); // initialize a global status variable m_d->dragWasSuccessful = false; QAbstractItemView::startDrag(supportedActions); QModelIndex newCurrent; QPoint selectionOffset; if (m_d->dragWasSuccessful) { newCurrent = currentIndex(); selectionOffset = QPoint(newCurrent.column() - currentBefore.column(), newCurrent.row() - currentBefore.row()); } else { newCurrent = currentBefore; selectionOffset = QPoint(); } setCurrentIndex(newCurrent); selectionModel()->clearSelection(); Q_FOREACH (const QModelIndex &idx, selectionBefore) { QModelIndex newIndex = model()->index(idx.row() + selectionOffset.y(), idx.column() + selectionOffset.x()); selectionModel()->select(newIndex, QItemSelectionModel::Select); } } } void TimelineFramesView::dragEnterEvent(QDragEnterEvent *event) { m_d->dragInProgress = true; m_d->model->setScrubState(true); QTableView::dragEnterEvent(event); } void TimelineFramesView::dragMoveEvent(QDragMoveEvent *event) { m_d->dragInProgress = true; m_d->model->setScrubState(true); QTableView::dragMoveEvent(event); if (event->isAccepted()) { QModelIndex index = indexAt(event->pos()); if (!m_d->model->canDropFrameData(event->mimeData(), index)) { event->ignore(); } else { selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); } } } void TimelineFramesView::dropEvent(QDropEvent *event) { m_d->dragInProgress = false; m_d->model->setScrubState(false); QAbstractItemView::dropEvent(event); m_d->dragWasSuccessful = event->isAccepted(); } void TimelineFramesView::dragLeaveEvent(QDragLeaveEvent *event) { m_d->dragInProgress = false; m_d->model->setScrubState(false); QAbstractItemView::dragLeaveEvent(event); } void TimelineFramesView::mousePressEvent(QMouseEvent *event) { QPersistentModelIndex index = indexAt(event->pos()); if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) { if (event->button() == Qt::RightButton) { // TODO: try calculate index under mouse cursor even when // it is outside any visible row qreal staticPoint = index.isValid() ? index.column() : currentIndex().column(); m_d->zoomDragButton->beginZoom(event->pos(), staticPoint); } else if (event->button() == Qt::LeftButton) { m_d->initialDragPanPos = event->pos(); m_d->initialDragPanValue = QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()); } event->accept(); } else if (event->button() == Qt::RightButton) { int numSelectedItems = selectionModel()->selectedIndexes().size(); if (index.isValid() && numSelectedItems <= 1 && m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { model()->setData(index, true, TimelineFramesModel::ActiveLayerRole); model()->setData(index, true, TimelineFramesModel::ActiveFrameRole); setCurrentIndex(index); if (model()->data(index, TimelineFramesModel::FrameExistsRole).toBool() || model()->data(index, TimelineFramesModel::SpecialKeyframeExists).toBool()) { { KisSignalsBlocker b(m_d->colorSelector); QVariant colorLabel = index.data(TimelineFramesModel::FrameColorLabelIndexRole); int labelIndex = colorLabel.isValid() ? colorLabel.toInt() : 0; m_d->colorSelector->setCurrentIndex(labelIndex); } m_d->frameEditingMenu->exec(event->globalPos()); } else { m_d->frameCreationMenu->exec(event->globalPos()); } } else if (numSelectedItems > 1) { int labelIndex = -1; bool haveFrames = false; Q_FOREACH(QModelIndex index, selectedIndexes()) { haveFrames |= index.data(TimelineFramesModel::FrameExistsRole).toBool(); QVariant colorLabel = index.data(TimelineFramesModel::FrameColorLabelIndexRole); if (colorLabel.isValid()) { if (labelIndex == -1) { // First label labelIndex = colorLabel.toInt(); } else if (labelIndex != colorLabel.toInt()) { // Mixed colors in selection labelIndex = -1; break; } } } if (!haveFrames) { m_d->multiframeColorSelectorAction->setVisible(false); } else { KisSignalsBlocker b(m_d->multiframeColorSelector); m_d->multiframeColorSelector->setCurrentIndex(labelIndex); m_d->multiframeColorSelectorAction->setVisible(true); } m_d->multipleFrameEditingMenu->exec(event->globalPos()); } } else if (event->button() == Qt::MidButton) { QModelIndex index = model()->buddy(indexAt(event->pos())); if (index.isValid()) { QStyleOptionViewItem option = viewOptions(); option.rect = visualRect(index); // The offset of the headers is needed to get the correct position inside the view. m_d->tip.showTip(this, event->pos() + QPoint(verticalHeader()->width(), horizontalHeader()->height()), option, index); } event->accept(); } else { if (index.isValid()) { m_d->model->setLastClickedIndex(index); } m_d->lastPressedPosition = QPoint(horizontalOffset(), verticalOffset()) + event->pos(); m_d->lastPressedModifier = event->modifiers(); QAbstractItemView::mousePressEvent(event); } } void TimelineFramesView::mouseMoveEvent(QMouseEvent *e) { if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) { if (e->buttons() & Qt::RightButton) { m_d->zoomDragButton->continueZoom(e->pos()); } else if (e->buttons() & Qt::LeftButton) { QPoint diff = e->pos() - m_d->initialDragPanPos; QPoint offset = QPoint(m_d->initialDragPanValue.x() - diff.x(), m_d->initialDragPanValue.y() - diff.y()); const int height = m_d->layersHeader->defaultSectionSize(); horizontalScrollBar()->setValue(offset.x()); verticalScrollBar()->setValue(offset.y() / height); } e->accept(); } else if (e->buttons() == Qt::MidButton) { QModelIndex index = model()->buddy(indexAt(e->pos())); if (index.isValid()) { QStyleOptionViewItem option = viewOptions(); option.rect = visualRect(index); // The offset of the headers is needed to get the correct position inside the view. m_d->tip.showTip(this, e->pos() + QPoint(verticalHeader()->width(), horizontalHeader()->height()), option, index); } e->accept(); } else { m_d->model->setScrubState(true); QTableView::mouseMoveEvent(e); } } void TimelineFramesView::mouseReleaseEvent(QMouseEvent *e) { if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) { e->accept(); } else { m_d->model->setScrubState(false); QTableView::mouseReleaseEvent(e); } } void TimelineFramesView::wheelEvent(QWheelEvent *e) { QModelIndex index = currentIndex(); int column= -1; if (index.isValid()) { column= index.column() + ((e->delta() > 0) ? 1 : -1); } if (column >= 0 && !m_d->dragInProgress) { setCurrentIndex(m_d->model->index(index.row(), column)); } } void TimelineFramesView::slotUpdateLayersMenu() { QAction *action = 0; m_d->existingLayersMenu->clear(); QVariant value = model()->headerData(0, Qt::Vertical, TimelineFramesModel::OtherLayersRole); if (value.isValid()) { TimelineFramesModel::OtherLayersList list = value.value(); int i = 0; Q_FOREACH (const TimelineFramesModel::OtherLayer &l, list) { action = m_d->existingLayersMenu->addAction(l.name); action->setData(i++); } } } void TimelineFramesView::slotLayerContextMenuRequested(const QPoint &globalPos) { m_d->layerEditingMenu->exec(globalPos); } void TimelineFramesView::slotAddNewLayer() { QModelIndex index = currentIndex(); const int newRow = index.isValid() ? index.row() : 0; model()->insertRow(newRow); } void TimelineFramesView::slotAddExistingLayer(QAction *action) { QVariant value = action->data(); if (value.isValid()) { QModelIndex index = currentIndex(); const int newRow = index.isValid() ? index.row() + 1 : 0; m_d->model->insertOtherLayer(value.toInt(), newRow); } } void TimelineFramesView::slotRemoveLayer() { QModelIndex index = currentIndex(); if (!index.isValid()) return; model()->removeRow(index.row()); } void TimelineFramesView::slotNewFrame() { QModelIndex index = currentIndex(); if (!index.isValid() || !m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { return; } m_d->model->createFrame(index); } void TimelineFramesView::slotCopyFrame() { QModelIndex index = currentIndex(); if (!index.isValid() || !m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { return; } m_d->model->copyFrame(index); } void TimelineFramesView::slotRemoveFrame() { QModelIndexList indexes = selectionModel()->selectedIndexes(); for (auto it = indexes.begin(); it != indexes.end(); /*noop*/) { if (!m_d->model->data(*it, TimelineFramesModel::FrameEditableRole).toBool()) { it = indexes.erase(it); } else { ++it; } } if (!indexes.isEmpty()) { m_d->model->removeFrames(indexes); } } bool TimelineFramesView::viewportEvent(QEvent *event) { if (event->type() == QEvent::ToolTip && model()) { QHelpEvent *he = static_cast(event); QModelIndex index = model()->buddy(indexAt(he->pos())); if (index.isValid()) { QStyleOptionViewItem option = viewOptions(); option.rect = visualRect(index); // The offset of the headers is needed to get the correct position inside the view. m_d->tip.showTip(this, he->pos() + QPoint(verticalHeader()->width(), horizontalHeader()->height()), option, index); return true; } } return QTableView::viewportEvent(event); } diff --git a/plugins/dockers/artisticcolorselector/kis_color_selector.cpp b/plugins/dockers/artisticcolorselector/kis_color_selector.cpp index 717531fcda..8c479ba6cf 100644 --- a/plugins/dockers/artisticcolorselector/kis_color_selector.cpp +++ b/plugins/dockers/artisticcolorselector/kis_color_selector.cpp @@ -1,717 +1,717 @@ /* Copyright (C) 2011 Silvio Heinrich This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License 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 #include #include "kis_color_selector.h" static const int MIN_NUM_HUE_PIECES = 1; static const int MAX_NUM_HUE_PIECES = 48; static const int MIN_NUM_LIGHT_PIECES = 1; static const int MAX_NUM_LIGHT_PIECES = 30; static const int MIN_NUM_SATURATION_RINGS = 1; static const int MAX_NUM_SATURATION_RINGS = 20; KisColorSelector::KisColorSelector(QWidget* parent, KisColor::Type type): QWidget(parent), m_colorSpace(type), m_inverseSaturation(false), m_relativeLight(false), m_light(0.5f), m_selectedColorRole(Acs::Foreground), m_clickedRing(-1) { recalculateRings(9, 12); recalculateAreas(9); selectColor(KisColor(Qt::red, KisColor::HSY)); using namespace std::placeholders; // For _1 placeholder auto function = std::bind(&KisColorSelector::slotUpdateColorAndPreview, this, _1); m_updateColorCompressor.reset(new ColorCompressorType(20 /* ms */, function)); } void KisColorSelector::setColorSpace(KisColor::Type type) { m_colorSpace = type; m_selectedColor = KisColor(m_selectedColor, m_colorSpace); update(); } void KisColorSelector::setNumLightPieces(int num) { num = qBound(MIN_NUM_LIGHT_PIECES, num, MAX_NUM_LIGHT_PIECES); - + recalculateAreas(quint8(num)); - + if (m_selectedLightPiece >= 0) m_selectedLightPiece = getLightIndex(m_selectedColor.getX()); - + update(); } void KisColorSelector::setNumPieces(int num) { num = qBound(MIN_NUM_HUE_PIECES, num, MAX_NUM_HUE_PIECES); - + recalculateRings(quint8(getNumRings()), quint8(num)); - + if (m_selectedPiece >= 0) m_selectedPiece = getHueIndex(m_selectedColor.getH() * PI2); - + update(); } void KisColorSelector::setNumRings(int num) { num = qBound(MIN_NUM_SATURATION_RINGS, num, MAX_NUM_SATURATION_RINGS); - + recalculateRings(quint8(num), quint8(getNumPieces())); - + if (m_selectedRing >= 0) m_selectedRing = getSaturationIndex(m_selectedColor.getS()); - + update(); } void KisColorSelector::selectColor(const KisColor& color) { m_selectedColor = KisColor(color, m_colorSpace); m_selectedPiece = getHueIndex(m_selectedColor.getH() * PI2); m_selectedRing = getSaturationIndex(m_selectedColor.getS()); m_selectedLightPiece = getLightIndex(m_selectedColor.getX()); update(); } void KisColorSelector::setFgColor(const KisColor& fgColor) { m_fgColor = KisColor(fgColor, m_colorSpace); update(); } void KisColorSelector::setBgColor(const KisColor& bgColor) { m_bgColor = KisColor(bgColor, m_colorSpace); update(); } void KisColorSelector::resetRings() { for(int i=0; i= 0) { m_colorRings[m_selectedRing].angle = 0.0f; update(); } } void KisColorSelector::setLight(float light, bool relative) { m_light = qBound(0.0f, light, 1.0f); - + m_selectedColor.setX(getLight(m_light, m_selectedColor.getH(), relative)); m_relativeLight = relative; m_selectedLightPiece = getLightIndex(m_selectedColor.getX()); update(); } void KisColorSelector::setInverseSaturation(bool inverse) { if (m_inverseSaturation != inverse) { m_selectedRing = (getNumRings()-1) - m_selectedRing; m_inverseSaturation = inverse; recalculateRings(quint8(getNumRings()), quint8(getNumPieces())); update(); } } QPointF KisColorSelector::mapCoord(const QPointF& pt, const QRectF& rect) const { qreal w = rect.width() / 2.0; qreal h = rect.height() / 2.0; qreal x = pt.x() - (rect.x() + w); qreal y = pt.y() - (rect.y() + h); return QPointF(x/w, y/h); } qint8 KisColorSelector::getLightIndex(const QPointF& pt) const { if (m_lightStripArea.contains(pt.toPoint(), true)) { qreal t = (pt.x() - m_lightStripArea.x()) / qreal(m_lightStripArea.width()); t = (pt.y() - m_lightStripArea.y()) / qreal(m_lightStripArea.height()); return qint8(t * getNumLightPieces()); } - + return -1; } qint8 KisColorSelector::getLightIndex(qreal light) const { light = qreal(1) - qBound(qreal(0), light, qreal(1)); return qint8(qRound(light * (getNumLightPieces()-1))); } qreal KisColorSelector::getLight(qreal light, qreal hue, bool relative) const { if (relative) { KisColor color(hue, 1.0f, m_colorSpace); qreal cl = color.getX(); light = (light * 2.0f) - 1.0f; return (light < 0.0f) ? (cl + cl*light) : (cl + (1.0f-cl)*light); } - + return light; } qreal KisColorSelector::getLight(const QPointF& pt) const { qint8 clickedLightPiece = getLightIndex(pt); - + if (clickedLightPiece >= 0) { if (getNumLightPieces() > 1) { return 1.0 - (qreal(clickedLightPiece) / qreal(getNumLightPieces()-1)); } return 1.0 - (qreal(pt.y()) / qreal(m_lightStripArea.height())); } - + return qreal(0); } qint8 KisColorSelector::getHueIndex(Radian hue, Radian shift) const { hue -= shift; qreal partSize = 1.0 / qreal(getNumPieces()); return qint8(qRound(hue.scaled(0.0f, 1.0f) / partSize) % getNumPieces()); } qreal KisColorSelector::getHue(int hueIdx, Radian shift) const { Radian hue = (qreal(hueIdx) / qreal(getNumPieces())) * PI2; hue += shift; return hue.scaled(0.0f, 1.0f); } qint8 KisColorSelector::getSaturationIndex(qreal saturation) const { saturation = qBound(qreal(0), saturation, qreal(1)); saturation = m_inverseSaturation ? (qreal(1) - saturation) : saturation; return qint8(saturation * qreal(getNumRings() - 1)); } qint8 KisColorSelector::getSaturationIndex(const QPointF& pt) const { qreal length = std::sqrt(pt.x()*pt.x() + pt.y()*pt.y()); - + for(int i=0; i= m_colorRings[i].innerRadius && length < m_colorRings[i].outerRadius) return qint8(i); } return -1; } qreal KisColorSelector::getSaturation(int saturationIdx) const { qreal sat = qreal(saturationIdx) / qreal(getNumRings()-1); return m_inverseSaturation ? (1.0 - sat) : sat; } void KisColorSelector::recalculateAreas(quint8 numLightPieces) { const qreal LIGHT_STRIP_RATIO = 0.075; - + int width = QWidget::width(); int height = QWidget::height(); int size = qMin(width, height); int stripThick = int(size * LIGHT_STRIP_RATIO); - + width -= stripThick; size = qMin(width, height); - + int x = (width - size) / 2; int y = (height - size) / 2; - + m_renderArea = QRect(x+stripThick, y, size, size); m_lightStripArea = QRect(0, 0, stripThick, QWidget::height()); m_renderBuffer = QImage(size, size, QImage::Format_ARGB32); m_numLightPieces = numLightPieces; } void KisColorSelector::recalculateRings(quint8 numRings, quint8 numPieces) { m_colorRings.resize(numRings); m_numPieces = numPieces; - + for(int i=0; i(numPieces, 1); ring.innerRadius = innerRadius; ring.outerRadius = outerRadius; ring.pieced.resize(numParts); - + qreal partSize = 360.0 / qreal(numParts); QRectF outerRect(-outerRadius, -outerRadius, outerRadius*2.0, outerRadius*2.0); QRectF innerRect(-innerRadius, -innerRadius, innerRadius*2.0, innerRadius*2.0); - + for(int i=0; istart(qMakePair(color, role)); } void KisColorSelector::slotUpdateColorAndPreview(QPair color) { const bool selectAsFgColor = color.second == Acs::Foreground; if (selectAsFgColor) { m_fgColor = color.first; } else { m_bgColor = color.first; } m_selectedColor = color.first; m_selectedColorRole = color.second; if (selectAsFgColor) { emit sigFgColorChanged(m_selectedColor); } else { emit sigBgColorChanged(m_selectedColor); } } void KisColorSelector::drawRing(QPainter& painter, KisColorSelector::ColorRing& ring, const QRect& rect) { painter.setRenderHint(QPainter::Antialiasing, false); painter.resetTransform(); painter.translate(rect.width()/2, rect.height()/2); - + if (ring.pieced.size() > 1) { painter.rotate(-ring.getShift().degrees()); painter.scale(rect.width()/2, rect.height()/2); painter.setPen(Qt::NoPen); - + QBrush brush(Qt::SolidPattern); - + for(int i=0; i= 1.0f) ? (hue - 1.0f) : hue; hue = (hue < 0.0f) ? (hue + 1.0f) : hue; - + KisColor color(hue, 1.0f, m_colorSpace); color.setS(ring.saturation); color.setX(getLight(m_light, hue, m_relativeLight)); - + brush.setColor(color.getQColor()); painter.fillPath(ring.pieced[i], brush); } } else { KisColor colors[7] = { KisColor(Qt::red , m_colorSpace), KisColor(Qt::yellow , m_colorSpace), KisColor(Qt::green , m_colorSpace), KisColor(Qt::cyan , m_colorSpace), KisColor(Qt::blue , m_colorSpace), KisColor(Qt::magenta, m_colorSpace), KisColor(Qt::red , m_colorSpace) }; - + QConicalGradient gradient(0, 0, 0); - + for(int i=0; i<=6; ++i) { qreal hue = float(i) / 6.0f; colors[i].setS(ring.saturation); colors[i].setX(getLight(m_light, hue, m_relativeLight)); gradient.setColorAt(hue, colors[i].getQColor()); } - + painter.scale(rect.width()/2, rect.height()/2); painter.fillPath(ring.pieced[0], QBrush(gradient)); } - + painter.resetTransform(); } void KisColorSelector::drawOutline(QPainter& painter, const QRect& rect) { painter.setRenderHint(QPainter::Antialiasing, true); painter.resetTransform(); painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2); painter.scale(rect.width()/2, rect.height()/2); painter.setPen(QPen(QBrush(Qt::gray), 0.005)); - + if (getNumPieces() > 1) { for(int i=0; i= 0 && m_selectedPiece >= 0) { painter.resetTransform(); painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2); painter.rotate(-m_colorRings[m_selectedRing].getShift().degrees()); painter.scale(rect.width()/2, rect.height()/2); - + painter.setPen(QPen(QBrush(Qt::red), 0.01)); painter.drawPath(m_colorRings[m_selectedRing].pieced[m_selectedPiece]); } } else { for(int i=0; i= 0) { qreal iRad = m_colorRings[m_selectedRing].innerRadius; qreal oRad = m_colorRings[m_selectedRing].outerRadius; - + painter.setPen(QPen(QBrush(Qt::red), 0.005)); painter.drawEllipse(QRectF(-iRad, -iRad, iRad*2.0, iRad*2.0)); painter.drawEllipse(QRectF(-oRad, -oRad, oRad*2.0, oRad*2.0)); - + if (getNumPieces() <= 1) { float c = std::cos(-m_selectedColor.getH() * PI2); float s = std::sin(-m_selectedColor.getH() * PI2); painter.drawLine(QPointF(c*iRad, s*iRad), QPointF(c*oRad, s*oRad)); } } } void KisColorSelector::drawLightStrip(QPainter& painter, const QRect& rect) { bool isVertical = true; qreal penSize = qreal(qMin(QWidget::width(), QWidget::height())) / 200.0; KisColor color(m_selectedColor); - + painter.resetTransform(); - + if (getNumLightPieces() > 1) { painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(QPen(QBrush(Qt::red), penSize)); - + QTransform matrix; matrix.translate(rect.x(), rect.y()); matrix.scale(rect.width(), rect.height()); - + for(int i=0; i (1,0,0) // 1 yellow -> (1,1,0) // 2 green -> (0,1,0) // 3 cyan -> (0,1,1) // 4 blue -> (0,0,1) // 5 maenta -> (1,0,1) // 6 red -> (1,0,0) - + m_renderBuffer.fill(0); - + QPainter imgPainter(&m_renderBuffer); QPainter wdgPainter(this); - + QRect fgRect(0, 0 , QWidget::width(), QWidget::height()/2); QRect bgRect(0, QWidget::height()/2, QWidget::width(), QWidget::height()/2); wdgPainter.fillRect(fgRect, m_fgColor.getQColor()); wdgPainter.fillRect(bgRect, m_bgColor.getQColor()); - + for(int i=0; iposF(), m_renderArea); + m_clickPos = mapCoord(event->localPos(), m_renderArea); m_mouseMoved = false; m_pressedButtons = event->buttons(); m_clickedRing = getSaturationIndex(m_clickPos); - - qint8 clickedLightPiece = getLightIndex(event->posF()); - + + qint8 clickedLightPiece = getLightIndex(event->localPos()); + if (clickedLightPiece >= 0) { - setLight(getLight(event->posF()), m_relativeLight); + setLight(getLight(event->localPos()), m_relativeLight); m_selectedLightPiece = clickedLightPiece; requestUpdateColorAndPreview(m_selectedColor, Acs::buttonsToRole(Qt::NoButton, m_pressedButtons)); m_mouseMoved = true; } else if (m_clickedRing >= 0) { if (getNumPieces() > 1) { for(int i=0; iposF(), m_renderArea); - qint8 clickedLightPiece = getLightIndex(event->posF()); - + QPointF dragPos = mapCoord(event->localPos(), m_renderArea); + qint8 clickedLightPiece = getLightIndex(event->localPos()); + if (clickedLightPiece >= 0) { - setLight(getLight(event->posF()), m_relativeLight); + setLight(getLight(event->localPos()), m_relativeLight); m_selectedLightPiece = clickedLightPiece; requestUpdateColorAndPreview(m_selectedColor, m_selectedColorRole); } - + if (m_clickedRing < 0) return; - + if (getNumPieces() > 1) { float angle = std::atan2(dragPos.x(), dragPos.y()) - std::atan2(m_clickPos.x(), m_clickPos.y()); float dist = std::sqrt(dragPos.x()*dragPos.x() + dragPos.y()*dragPos.y()) * 0.80f; float threshold = 5.0f * (1.0f-(dist*dist)); - + if (qAbs(angle * TO_DEG) >= threshold || m_mouseMoved) { bool selectedRingMoved = true; - + if (m_pressedButtons & Qt::RightButton) { selectedRingMoved = m_clickedRing == m_selectedRing; m_colorRings[m_clickedRing].angle = m_colorRings[m_clickedRing].tmpAngle + angle; } else for(int i=0; i= 0) { Radian angle = std::atan2(m_clickPos.x(), m_clickPos.y()) - RAD_90; - + m_selectedRing = m_clickedRing; m_selectedPiece = getHueIndex(angle, m_colorRings[m_clickedRing].getShift()); - + if (getNumPieces() > 1) m_selectedColor.setH(getHue(m_selectedPiece, m_colorRings[m_clickedRing].getShift())); else m_selectedColor.setH(angle.scaled(0.0f, 1.0f)); - + m_selectedColor.setS(getSaturation(m_selectedRing)); m_selectedColor.setX(getLight(m_light, m_selectedColor.getH(), m_relativeLight)); - + requestUpdateColorAndPreview(m_selectedColor, Acs::buttonsToRole(Qt::NoButton, m_pressedButtons)); } else if (m_mouseMoved) requestUpdateColorAndPreview(m_selectedColor, m_selectedColorRole); - + m_clickedRing = -1; update(); } void KisColorSelector::resizeEvent(QResizeEvent* /*event*/) { recalculateAreas(quint8(getNumLightPieces())); } void KisColorSelector::saveSettings() { KisConfig cfg; cfg.writeEntry("ArtColorSel.ColorSpace" , qint32(m_colorSpace)); cfg.writeEntry("ArtColorSel.NumRings" , m_colorRings.size()); cfg.writeEntry("ArtColorSel.RingPieces" , qint32(m_numPieces)); cfg.writeEntry("ArtColorSel.LightPieces", qint32(m_numLightPieces)); - + cfg.writeEntry("ArtColorSel.InversedSaturation", m_inverseSaturation); cfg.writeEntry("ArtColorSel.RelativeLight" , m_relativeLight); cfg.writeEntry("ArtColorSel.Light" , m_light); - + cfg.writeEntry("ArtColorSel.SelColorH", m_selectedColor.getH()); cfg.writeEntry("ArtColorSel.SelColorS", m_selectedColor.getS()); cfg.writeEntry("ArtColorSel.SelColorX", m_selectedColor.getX()); cfg.writeEntry("ArtColorSel.SelColorA", m_selectedColor.getA()); - + QList angles; - + for(int i=0; i("ArtColorSel.ColorSpace" , KisColor::HSY))); - + setNumLightPieces(cfg.readEntry("ArtColorSel.LightPieces", 19)); - + m_selectedColor.setH(cfg.readEntry("ArtColorSel.SelColorH", 0.0f)); m_selectedColor.setS(cfg.readEntry("ArtColorSel.SelColorS", 0.0f)); m_selectedColor.setX(cfg.readEntry("ArtColorSel.SelColorX", 0.0f)); m_selectedColor.setA(1.0f); - + setInverseSaturation(cfg.readEntry("ArtColorSel.InversedSaturation", false)); setLight(cfg.readEntry("ArtColorSel.Light", 0.5f), cfg.readEntry("ArtColorSel.RelativeLight", false)); - + recalculateRings( cfg.readEntry("ArtColorSel.NumRings" , 11), cfg.readEntry("ArtColorSel.RingPieces", 12) ); - + QList angles = cfg.readList("ArtColorSel.RingAngles"); for (int i = 0; i < m_colorRings.size(); ++i) { if (i < angles.size() && i < m_colorRings.size()) { m_colorRings[i].angle = angles[i]; } } - + selectColor(m_selectedColor); update(); } diff --git a/plugins/dockers/compositiondocker/compositiondocker_dock.cpp b/plugins/dockers/compositiondocker/compositiondocker_dock.cpp index 25b24757c2..91788aa87e 100644 --- a/plugins/dockers/compositiondocker/compositiondocker_dock.cpp +++ b/plugins/dockers/compositiondocker/compositiondocker_dock.cpp @@ -1,304 +1,304 @@ /* * Copyright (c) 2012 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; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "compositiondocker_dock.h" #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "compositionmodel.h" CompositionDockerDock::CompositionDockerDock( ) : QDockWidget(i18n("Compositions")), m_canvas(0) { QWidget* widget = new QWidget(this); setupUi(widget); m_model = new CompositionModel(this); compositionView->setModel(m_model); compositionView->installEventFilter(this); deleteButton->setIcon(KisIconUtils::loadIcon("edit-delete")); saveButton->setIcon(KisIconUtils::loadIcon("list-add")); exportButton->setIcon(KisIconUtils::loadIcon("document-export")); deleteButton->setToolTip(i18n("Delete Composition")); saveButton->setToolTip(i18n("New Composition")); exportButton->setToolTip(i18n("Export Composition")); setWidget(widget); connect( compositionView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(activated ( const QModelIndex & ) ) ); compositionView->setContextMenuPolicy(Qt::CustomContextMenu); connect( compositionView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint))); connect( deleteButton, SIGNAL(clicked(bool)), this, SLOT(deleteClicked())); connect( saveButton, SIGNAL(clicked(bool)), this, SLOT(saveClicked())); connect( exportButton, SIGNAL(clicked(bool)), this, SLOT(exportClicked())); saveNameEdit->setPlaceholderText(i18n("Insert Name")); } CompositionDockerDock::~CompositionDockerDock() { } void CompositionDockerDock::setCanvas(KoCanvasBase * canvas) { if (m_canvas && m_canvas->viewManager()) { Q_FOREACH (KisAction *action, m_actions) { m_canvas->viewManager()->actionManager()->takeAction(action); } } unsetCanvas(); setEnabled(canvas != 0); m_canvas = dynamic_cast(canvas); if (m_canvas && m_canvas->viewManager()) { if (m_actions.isEmpty()) { KisAction *updateAction = m_canvas->viewManager()->actionManager()->createAction("update_composition"); connect(updateAction, SIGNAL(triggered()), this, SLOT(updateComposition())); m_actions.append(updateAction); KisAction *renameAction = m_canvas->viewManager()->actionManager()->createAction("rename_composition"); connect(renameAction, SIGNAL(triggered()), this, SLOT(renameComposition())); m_actions.append(renameAction); } else { Q_FOREACH (KisAction *action, m_actions) { m_canvas->viewManager()->actionManager()->addAction(action->objectName(), action); } } updateModel(); } } void CompositionDockerDock::unsetCanvas() { setEnabled(false); m_canvas = 0; m_model->setCompositions(QList()); } void CompositionDockerDock::activated(const QModelIndex& index) { KisLayerCompositionSP composition = m_model->compositionFromIndex(index); composition->apply(); } void CompositionDockerDock::deleteClicked() { QModelIndex index = compositionView->currentIndex(); if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image() && index.isValid()) { KisLayerCompositionSP composition = m_model->compositionFromIndex(index); m_canvas->viewManager()->image()->removeComposition(composition); updateModel(); } } void CompositionDockerDock::saveClicked() { KisImageWSP image = m_canvas->viewManager()->image(); if (!image) return; // format as 001, 002 ... QString name = saveNameEdit->text(); if (name.isEmpty()) { bool found = false; int i = 1; do { name = QString("%1").arg(i, 3, 10, QChar('0')); found = false; Q_FOREACH (KisLayerCompositionSP composition, m_canvas->viewManager()->image()->compositions()) { if (composition->name() == name) { found = true; break; } } i++; } while(found && i < 1000); } KisLayerCompositionSP composition(new KisLayerComposition(image, name)); composition->store(); image->addComposition(composition); saveNameEdit->clear(); updateModel(); compositionView->setCurrentIndex(m_model->index(image->compositions().count()-1, 0)); image->setModified(); } void CompositionDockerDock::updateModel() { if (m_model && m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image()) { m_model->setCompositions(m_canvas->viewManager()->image()->compositions()); } } void CompositionDockerDock::exportClicked() { if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image()) { QString path; KoFileDialog dialog(0, KoFileDialog::OpenDirectory, "compositiondockerdock"); dialog.setCaption(i18n("Select a Directory")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); path = dialog.filename(); if (path.isNull()) return; if (!path.endsWith('/')) { path.append('/'); } KisImageWSP image = m_canvas->viewManager()->image(); QString filename = m_canvas->viewManager()->document()->localFilePath(); if (!filename.isEmpty()) { QFileInfo info(filename); path += info.baseName() + '_'; } Q_FOREACH (KisLayerCompositionSP composition, m_canvas->viewManager()->image()->compositions()) { if (!composition->isExportEnabled()) { continue; } composition->apply(); image->refreshGraph(); image->lock(); #if 0 image->rootLayer()->projection()->convertToQImage(0, 0, 0, image->width(), image->height()).save(path + composition->name() + ".png"); #else QRect r = image->bounds(); KisDocument *d = KisPart::instance()->createDocument(); KisImageWSP dst = new KisImage(d->createUndoStore(), r.width(), r.height(), image->colorSpace(), composition->name()); dst->setResolution(image->xRes(), image->yRes()); d->setCurrentImage(dst); KisPaintLayer* paintLayer = new KisPaintLayer(dst, "projection", OPACITY_OPAQUE_U8); KisPainter gc(paintLayer->paintDevice()); gc.bitBlt(QPoint(0, 0), image->rootLayer()->projection(), r); dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0)); dst->refreshGraph(); d->setFileBatchMode(true); const QByteArray outputFormat("image/png"); d->exportDocumentSync(QUrl::fromLocalFile(path + composition->name() + ".png"), outputFormat); delete d; #endif image->unlock(); } } } bool CompositionDockerDock::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::KeyPress ) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down) { // new index will be set after the method is called QTimer::singleShot(0, this, SLOT(activateCurrentIndex())); } return false; } else { return QObject::eventFilter(obj, event); } } void CompositionDockerDock::activateCurrentIndex() { QModelIndex index = compositionView->currentIndex(); if (index.isValid()) { activated(index); } } void CompositionDockerDock::customContextMenuRequested(QPoint pos) { if (m_actions.isEmpty()) return; QMenu menu; Q_FOREACH (KisAction *action, m_actions) { menu.addAction(action); } menu.exec(compositionView->mapToGlobal(pos)); } void CompositionDockerDock::updateComposition() { QModelIndex index = compositionView->currentIndex(); if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image() && index.isValid()) { KisLayerCompositionSP composition = m_model->compositionFromIndex(index); composition->store(); m_canvas->image()->setModified(); } } void CompositionDockerDock::renameComposition() { dbgKrita << "rename"; QModelIndex index = compositionView->currentIndex(); if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image() && index.isValid()) { KisLayerCompositionSP composition = m_model->compositionFromIndex(index); bool ok; QString name = QInputDialog::getText(this, i18n("Rename Composition"), i18n("New Name:"), QLineEdit::Normal, composition->name(), &ok); if (ok && !name.isEmpty()) { composition->setName(name); m_canvas->image()->setModified(); } } } diff --git a/plugins/dockers/compositiondocker/compositionmodel.cpp b/plugins/dockers/compositiondocker/compositionmodel.cpp index abe4796ffa..b8561b4aef 100644 --- a/plugins/dockers/compositiondocker/compositionmodel.cpp +++ b/plugins/dockers/compositiondocker/compositionmodel.cpp @@ -1,109 +1,110 @@ /* * Copyright (c) 2012 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; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "compositionmodel.h" #include #include #include CompositionModel::CompositionModel(QObject* parent): QAbstractTableModel(parent) { } CompositionModel::~CompositionModel() { } QVariant CompositionModel::data(const QModelIndex& index, int role) const { if (index.isValid()) { switch (role) { case Qt::DisplayRole: { return m_compositions.at(index.row())->name(); } case Qt::DecorationRole: { return KisIconUtils::loadIcon("tools-wizard"); } case Qt::CheckStateRole: { return m_compositions.at(index.row())->isExportEnabled() ? Qt::Checked : Qt::Unchecked; } } } return QVariant(); } bool CompositionModel::setData ( const QModelIndex& index, const QVariant& value, int role ) { if (index.isValid()) { if (role == Qt::CheckStateRole) { Q_ASSERT(index.row() < rowCount()); Q_ASSERT(index.column() < columnCount()); if (index.column() == 0) { bool exportEnabled = value.toInt() == Qt::Checked; KisLayerCompositionSP layerComposition = m_compositions.at(index.row()); if (layerComposition) { layerComposition->setExportEnabled(exportEnabled); } } } return true; } return false; } QVariant CompositionModel::headerData(int /*section*/, Qt::Orientation /*orientation*/, int /*role*/) const { return i18n("Composition"); } int CompositionModel::rowCount(const QModelIndex& /*parent*/) const { return m_compositions.count(); } int CompositionModel::columnCount(const QModelIndex& /*parent*/) const { return 2; } Qt::ItemFlags CompositionModel::flags(const QModelIndex& /*index*/) const { Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; return flags; } KisLayerCompositionSP CompositionModel::compositionFromIndex(const QModelIndex& index) { if(index.isValid()) { return m_compositions.at(index.row()); } return KisLayerCompositionSP(); } void CompositionModel::setCompositions(QList< KisLayerCompositionSP > compositions) { m_compositions = compositions; - reset(); + beginResetModel(); + endResetModel(); } diff --git a/plugins/dockers/historydocker/KisUndoModel.cpp b/plugins/dockers/historydocker/KisUndoModel.cpp index 89d56f914e..ae1ceb5a0d 100644 --- a/plugins/dockers/historydocker/KisUndoModel.cpp +++ b/plugins/dockers/historydocker/KisUndoModel.cpp @@ -1,282 +1,283 @@ /* This file is part of the KDE project * Copyright (C) 2010 Matus Talcik * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "KisUndoModel.h" #include KisUndoModel::KisUndoModel(QObject *parent) : QAbstractItemModel(parent) { m_blockOutgoingHistoryChange = false; m_stack = 0; m_canvas = 0; m_sel_model = new QItemSelectionModel(this, this); connect(m_sel_model, SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(setStackCurrentIndex(QModelIndex))); m_empty_label = i18n(""); } QItemSelectionModel *KisUndoModel::selectionModel() const { return m_sel_model; } KUndo2QStack *KisUndoModel::stack() const { return m_stack; } void KisUndoModel::setStack(KUndo2QStack *stack) { if (m_stack == stack) return; if (m_stack != 0) { disconnect(m_stack, SIGNAL(canRedoChanged(bool)), this, SLOT(stackChanged())); disconnect(m_stack, SIGNAL(cleanChanged(bool)), this, SLOT(stackChanged())); disconnect(m_stack, SIGNAL(indexChanged(int)), this, SLOT(stackChanged())); disconnect(m_stack, SIGNAL(destroyed(QObject*)), this, SLOT(stackDestroyed(QObject*))); disconnect(m_stack, SIGNAL(indexChanged(int)), this, SLOT(addImage(int))); } m_stack = stack; if (m_stack != 0) { connect(m_stack, SIGNAL(canRedoChanged(bool)), this, SLOT(stackChanged())); connect(m_stack, SIGNAL(cleanChanged(bool)), this, SLOT(stackChanged())); connect(m_stack, SIGNAL(indexChanged(int)), this, SLOT(stackChanged())); connect(m_stack, SIGNAL(destroyed(QObject*)), this, SLOT(stackDestroyed(QObject*))); connect(m_stack, SIGNAL(indexChanged(int)), this, SLOT(addImage(int))); } stackChanged(); } void KisUndoModel::stackDestroyed(QObject *obj) { if (obj != m_stack) return; m_stack = 0; stackChanged(); } void KisUndoModel::stackChanged() { - reset(); + beginResetModel(); + endResetModel(); m_blockOutgoingHistoryChange = true; m_sel_model->setCurrentIndex(selectedIndex(), QItemSelectionModel::ClearAndSelect); m_blockOutgoingHistoryChange = false; } void KisUndoModel::setStackCurrentIndex(const QModelIndex &index) { if (m_blockOutgoingHistoryChange) return; if (m_stack == 0) return; if (index == selectedIndex()) return; if (index.column() != 0) return; m_stack->setIndex(index.row()); } QModelIndex KisUndoModel::selectedIndex() const { return m_stack == 0 ? QModelIndex() : createIndex(m_stack->index(), 0); } QModelIndex KisUndoModel::index(int row, int column, const QModelIndex &parent) const { if (m_stack == 0) return QModelIndex(); if (parent.isValid()) return QModelIndex(); if (column != 0) return QModelIndex(); if (row < 0 || row > m_stack->count()) return QModelIndex(); return createIndex(row, column); } QModelIndex KisUndoModel::parent(const QModelIndex&) const { return QModelIndex(); } int KisUndoModel::rowCount(const QModelIndex &parent) const { if (m_stack == 0) return 0; if (parent.isValid()) return 0; return m_stack->count() + 1; } int KisUndoModel::columnCount(const QModelIndex&) const { return 1; } QVariant KisUndoModel::data(const QModelIndex &index, int role) const { if (m_stack == 0){ return QVariant(); } if (index.column() != 0){ return QVariant(); } if (index.row() < 0 || index.row() > m_stack->count()){ return QVariant(); } if (role == Qt::DisplayRole) { if (index.row() == 0){ return m_empty_label; } KUndo2Command* currentCommand = const_cast(m_stack->command(index.row() - 1)); return currentCommand->isMerged()?m_stack->text(index.row() - 1)+"(Merged)":m_stack->text(index.row() - 1); } else if (role == Qt::DecorationRole) { if (index.row() > 0) { const KUndo2Command* currentCommand = m_stack->command(index.row() - 1); if (m_imageMap.contains(currentCommand)) { return m_imageMap[currentCommand]; } } } return QVariant(); } QString KisUndoModel::emptyLabel() const { return m_empty_label; } void KisUndoModel::setEmptyLabel(const QString &label) { m_empty_label = label; stackChanged(); } void KisUndoModel::setCleanIcon(const QIcon &icon) { m_clean_icon = icon; stackChanged(); } QIcon KisUndoModel::cleanIcon() const { return m_clean_icon; } void KisUndoModel::setCanvas(KisCanvas2 *canvas) { m_canvas = canvas; } void KisUndoModel::addImage(int idx) { if (m_stack == 0 || m_stack->count() == 0) { return; } const KUndo2Command* currentCommand = m_stack->command(idx-1); if (m_stack->count() == idx && !m_imageMap.contains(currentCommand)) { KisImageWSP historyImage = m_canvas->viewManager()->image(); KisPaintDeviceSP paintDevice = historyImage->projection(); QImage image = paintDevice->createThumbnail(32, 32, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); m_imageMap[currentCommand] = image; } QList list; for (int i = 0; i < m_stack->count(); ++i) { list << m_stack->command(i); } for (QMap:: iterator it = m_imageMap.begin(); it != m_imageMap.end();) { if (!list.contains(it.key())) { it = m_imageMap.erase(it); } else { ++it; } } } bool KisUndoModel::checkMergedCommand(int index) { Q_UNUSED(index) return false; } diff --git a/plugins/dockers/imagedocker/image_strip_scene.cpp b/plugins/dockers/imagedocker/image_strip_scene.cpp index 48ad8838e4..534db68aed 100644 --- a/plugins/dockers/imagedocker/image_strip_scene.cpp +++ b/plugins/dockers/imagedocker/image_strip_scene.cpp @@ -1,199 +1,199 @@ /* * Copyright (c) 2011 Silvio Heinrich * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "image_strip_scene.h" #include #include #include #include #include #include #include #include #include #include ///////////////////////////////////////////////////////////////////////////////////////////// // ------------- ImageLoader ---------------------------------------------------------- // ImageLoader::ImageLoader(float size) : m_size(size) , m_run(true) { connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(stopExecution())); } void ImageLoader::run() { typedef QHash::iterator Iterator; for (Iterator data = m_data.begin(); data != m_data.end() && m_run; ++data) { QImage img = QImage(data->path); if (!img.isNull()) { data->image = img.scaled(m_size, m_size, Qt::KeepAspectRatio, Qt::SmoothTransformation); } //dbgKrita << "Loaded" << data->path; data->isLoaded = true; emit sigItemContentChanged(data.key()); } } void ImageLoader::stopExecution() { m_run = false; } ///////////////////////////////////////////////////////////////////////////////////////////// // ------------- ImageItem ------------------------------------------------------------ // void ImageItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(option); Q_UNUSED(widget); if (m_loader->isImageLoaded(this)) { QImage image = m_loader->getImage(this); if (!image.isNull()) { QPointF offset((m_size-image.width()) / 2.0, (m_size-image.height()) / 2.0); painter->drawImage(offset, image); } else { QIcon icon = KisIconUtils::loadIcon("edit-delete"); QRect rect = boundingRect().toRect(); QPixmap img = icon.pixmap(rect.size()); painter->drawPixmap(rect, img, img.rect()); } } else { QIcon icon = KisIconUtils::loadIcon("folder-pictures"); QRect rect = boundingRect().toRect(); QPixmap img = icon.pixmap(rect.size()); painter->drawPixmap(rect, img, img.rect()); } if (isSelected()) { painter->setCompositionMode(QPainter::CompositionMode_HardLight); painter->setOpacity(0.50); painter->fillRect(boundingRect().toRect(), palette().color(QPalette::Active, QPalette::Highlight)); painter->setCompositionMode(QPainter::CompositionMode_SourceOver); QPen pen(palette().color(QPalette::Active, QPalette::Highlight), 3); painter->setPen(pen); } painter->drawRect(boundingRect()); } QSizeF ImageItem::sizeHint(Qt::SizeHint /*which*/, const QSizeF& /*constraint*/) const { return QSizeF(m_size, m_size); } ///////////////////////////////////////////////////////////////////////////////////////////// // ------------- ImageStripScene ------------------------------------------------------ // ImageStripScene::ImageStripScene(): m_imgSize(80) , m_loader(0) { } ImageStripScene::~ImageStripScene() { delete m_loader; } bool ImageStripScene::setCurrentDirectory(const QString& path) { m_path = path; QMutexLocker locker(&m_mutex); QDir directory(path); QImageReader reader; if (directory.exists()) { clear(); if (m_loader) { m_loader->disconnect(this); m_loader->stopExecution(); if (!m_loader->wait(500)) { m_loader->terminate(); m_loader->wait(); } } delete m_loader; m_numItems = 0; m_loader = new ImageLoader(m_imgSize); connect(m_loader, SIGNAL(sigItemContentChanged(ImageItem*)), SLOT(slotItemContentChanged(ImageItem*))); QStringList files = directory.entryList(QDir::Files); QGraphicsLinearLayout* layout = new QGraphicsLinearLayout(); for (QStringList::iterator name=files.begin(); name!=files.end(); ++name) { QString path = directory.absoluteFilePath(*name); QString fileExtension = QFileInfo(path).suffix(); if (!fileExtension.compare("DNG", Qt::CaseInsensitive)) { warnKrita << "WARNING: Qt is known to crash when trying to open a DNG file. Skip it"; continue; } reader.setFileName(path); if(reader.canRead()) { ImageItem* item = new ImageItem(m_imgSize, path, m_loader); m_loader->addPath(item, path); layout->addItem(item); ++m_numItems; } } QGraphicsWidget* widget = new QGraphicsWidget(); widget->setLayout(layout); widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); addItem(widget); setSceneRect(widget->boundingRect()); QTimer::singleShot(0, m_loader, SLOT(start())); return true; } return false; } void ImageStripScene::slotItemContentChanged(ImageItem* item) { QMutexLocker locker(&m_mutex); item->update(); } void ImageStripScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) { - ImageItem* item = static_cast(itemAt(event->scenePos())); + ImageItem* item = static_cast(itemAt(event->scenePos().x(), event->scenePos().y(), QTransform())); if (item) emit sigImageActivated(item->path()); } diff --git a/plugins/dockers/imagedocker/imagedocker_dock.cpp b/plugins/dockers/imagedocker/imagedocker_dock.cpp index 7c98b23996..7060aaba4f 100644 --- a/plugins/dockers/imagedocker/imagedocker_dock.cpp +++ b/plugins/dockers/imagedocker/imagedocker_dock.cpp @@ -1,593 +1,593 @@ /* * Copyright (c) 2011 Silvio Heinrich * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "imagedocker_dock.h" #include "image_strip_scene.h" #include "image_view.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include "ui_wdgimagedocker.h" #include "ui_wdgImageViewPopup.h" /////////////////////////////////////////////////////////////////////////////// // --------- ImageFilter --------------------------------------------------- // class ImageFilter: public QSortFilterProxyModel { bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override { QFileSystemModel* model = static_cast(sourceModel()); QModelIndex index = sourceModel()->index(source_row, 0, source_parent); if(model->isDir(index)) return true; QString ext = model->fileInfo(index).suffix().toLower(); if(s_supportedImageFormats.isEmpty()) { s_supportedImageFormats = QImageReader::supportedImageFormats(); } //QImageReader::supportedImageFormats return a list with mixed-case ByteArrays so //iterate over it manually to make it possible to do toLower(). Q_FOREACH (const QByteArray& format, s_supportedImageFormats) { if(format.toLower() == ext.toUtf8()) { return true; } } return false; } bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const override { Q_UNUSED(source_parent); return source_column == 0; } static QList s_supportedImageFormats; }; QList ImageFilter::s_supportedImageFormats; /////////////////////////////////////////////////////////////////////////////// // --------- ImageListModel ------------------------------------------------ // class ImageListModel: public QAbstractListModel { struct Data { QPixmap icon; QString text; qint64 id; }; public: void addImage(const QPixmap& pixmap, const QString& text, qint64 id) { Data data; data.icon = pixmap.scaled(70, 70, Qt::KeepAspectRatio, Qt::SmoothTransformation); data.text = text; data.id = id; emit layoutAboutToBeChanged(); m_data.push_back(data); emit layoutChanged(); } qint64 imageID(int index) const { return m_data[index].id; } void removeImage(qint64 id) { typedef QList::iterator Iterator; for(Iterator data=m_data.begin(); data!=m_data.end(); ++data) { if(data->id == id) { emit layoutAboutToBeChanged(); m_data.erase(data); emit layoutChanged(); return; } } } int indexFromID(qint64 id) { for(int i=0; i m_data; }; /////////////////////////////////////////////////////////////////////////////// // --------- ImageDockerUI ------------------------------------------------- // struct ImageDockerUI: public QWidget, public Ui_wdgImageDocker { ImageDockerUI() { setupUi(this); } }; /////////////////////////////////////////////////////////////////////////////// // --------- PopupWidgetUI ------------------------------------------------- // struct PopupWidgetUI: public QWidget, public Ui_wdgImageViewPopup { PopupWidgetUI() { setupUi(this); } }; /////////////////////////////////////////////////////////////////////////////// // --------- ImageDockerDock ----------------------------------------------- // ImageDockerDock::ImageDockerDock(): QDockWidget(i18n("Reference Images")), m_canvas(0), m_currImageID(-1) { m_ui = new ImageDockerUI(); m_popupUi = new PopupWidgetUI(); m_zoomButtons = new QButtonGroup(); m_imgListModel = new ImageListModel(); m_imageStripScene = new ImageStripScene(); m_model = new QFileSystemModel(); m_proxyModel = new ImageFilter(); m_proxyModel->setSourceModel(m_model); m_proxyModel->setDynamicSortFilter(true); m_ui->bnBack->setIcon(KisIconUtils::loadIcon("arrow-left")); m_ui->bnUp->setIcon(KisIconUtils::loadIcon("arrow-up")); m_ui->bnHome->setIcon(KisIconUtils::loadIcon("go-home")); m_ui->bnImgPrev->setIcon(KisIconUtils::loadIcon("arrow-left")); m_ui->bnImgNext->setIcon(KisIconUtils::loadIcon("arrow-right")); m_ui->bnImgClose->setIcon(KisIconUtils::loadIcon("window-close")); m_ui->thumbView->setScene(m_imageStripScene); m_ui->treeView->setModel(m_proxyModel); m_ui->cmbImg->setModel(m_imgListModel); m_ui->bnPopup->setIcon(KisIconUtils::loadIcon("zoom-original")); m_ui->bnPopup->setPopupWidget(m_popupUi); m_popupUi->zoomSlider->setRange(5, 500); m_popupUi->zoomSlider->setValue(100); m_zoomButtons->addButton(m_popupUi->bnZoomFit , ImageView::VIEW_MODE_FIT); m_zoomButtons->addButton(m_popupUi->bnZoomAdjust, ImageView::VIEW_MODE_ADJUST); m_zoomButtons->addButton(m_popupUi->bnZoom25 , 25); m_zoomButtons->addButton(m_popupUi->bnZoom50 , 50); m_zoomButtons->addButton(m_popupUi->bnZoom75 , 75); m_zoomButtons->addButton(m_popupUi->bnZoom100 , 100); installEventFilter(this); - m_ui->cmbPath->addItem(KisIconUtils::loadIcon("folder-pictures"), QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); - m_ui->cmbPath->addItem(KisIconUtils::loadIcon("folder-documents"), QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)); - m_ui->cmbPath->addItem(KisIconUtils::loadIcon("go-home"), QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + m_ui->cmbPath->addItem(KisIconUtils::loadIcon("folder-pictures"), QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); + m_ui->cmbPath->addItem(KisIconUtils::loadIcon("folder-documents"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); + m_ui->cmbPath->addItem(KisIconUtils::loadIcon("go-home"), QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); Q_FOREACH (const QFileInfo &info, QDir::drives()) { m_ui->cmbPath->addItem(KisIconUtils::loadIcon("drive-harddisk"), info.absolutePath()); } connect(m_ui->cmbPath, SIGNAL(activated(const QString&)), SLOT(slotChangeRoot(const QString&))); connect(m_ui->treeView , SIGNAL(doubleClicked(const QModelIndex&)) , SLOT(slotItemDoubleClicked(const QModelIndex&))); connect(m_ui->bnBack , SIGNAL(clicked(bool)) , SLOT(slotBackButtonClicked())); connect(m_ui->bnHome , SIGNAL(clicked(bool)) , SLOT(slotHomeButtonClicked())); connect(m_ui->bnUp , SIGNAL(clicked(bool)) , SLOT(slotUpButtonClicked())); connect(m_imageStripScene , SIGNAL(sigImageActivated(const QString&)) , SLOT(slotOpenImage(QString))); connect(m_ui->bnImgNext , SIGNAL(clicked(bool)) , SLOT(slotNextImage())); connect(m_ui->bnImgPrev , SIGNAL(clicked(bool)) , SLOT(slotPrevImage())); connect(m_ui->bnImgClose , SIGNAL(clicked(bool)) , SLOT(slotCloseCurrentImage())); connect(m_ui->cmbImg , SIGNAL(activated(int)) , SLOT(slotImageChoosenFromComboBox(int))); connect(m_ui->imgView , SIGNAL(sigColorSelected(const QColor&)) , SLOT(slotColorSelected(const QColor))); connect(m_ui->imgView , SIGNAL(sigViewModeChanged(int, qreal)) , SLOT(slotViewModeChanged(int, qreal))); connect(m_popupUi->zoomSlider , SIGNAL(valueChanged(int)) , SLOT(slotZoomChanged(int))); connect(m_zoomButtons , SIGNAL(buttonClicked(int)) , SLOT(slotZoomChanged(int))); connect(m_zoomButtons , SIGNAL(buttonClicked(int)) , SLOT(slotCloseZoomPopup())); setWidget(m_ui); setAcceptDrops(true); } ImageDockerDock::~ImageDockerDock() { saveConfigState(); delete m_proxyModel; delete m_model; delete m_imageStripScene; delete m_imgListModel; delete m_zoomButtons; qDeleteAll(m_temporaryFiles); } void ImageDockerDock::dragEnterEvent(QDragEnterEvent *event) { event->setAccepted(event->mimeData()->hasImage() || event->mimeData()->hasUrls()); } void ImageDockerDock::dropEvent(QDropEvent *event) { QImage image; if (event->mimeData()->hasImage()) { image = qvariant_cast(event->mimeData()->imageData()); } if (!image.isNull()) { QTemporaryFile *file = new QTemporaryFile(QDir::tempPath () + QDir::separator() + "krita_reference_dnd_XXXXXX.png"); m_temporaryFiles.append(file); file->open(); image.save(file, "PNG"); file->close(); slotOpenImage(file->fileName()); } else if (event->mimeData()->hasUrls()) { QList urls = event->mimeData()->urls(); Q_FOREACH (const QUrl &url, urls) { QString path = url.path(); QFileInfo info(path); if (info.exists() && !QImageReader::imageFormat(path).isEmpty()) { slotOpenImage(path); } } } } void ImageDockerDock::showEvent(QShowEvent *) { loadConfigState(); } void ImageDockerDock::hideEvent(QHideEvent *event) { saveConfigState(); } void ImageDockerDock::setCanvas(KoCanvasBase* canvas) { // Intentionally not disabled if there's no canvas // "Every connection you make emits a signal, so duplicate connections emit two signals" if(m_canvas) { m_canvas->disconnectCanvasObserver(this); } m_canvas = canvas; } void ImageDockerDock::addCurrentPathToHistory() { m_history.push_back(m_model->filePath(m_proxyModel->mapToSource(m_ui->treeView->rootIndex()))); } void ImageDockerDock::updatePath(const QString& path) { m_ui->bnBack->setDisabled(m_history.empty()); m_imageStripScene->setCurrentDirectory(path); } qint64 ImageDockerDock::generateImageID() const { static qint64 id = 0; return ++id; } void ImageDockerDock::setCurrentImage(qint64 imageID) { if(m_imgInfoMap.contains(m_currImageID)) m_imgInfoMap[m_currImageID].scrollPos = m_ui->imgView->getScrollPos(); m_ui->bnImgClose->setDisabled(imageID < 0); m_ui->bnPopup->setDisabled(imageID < 0); if(imageID < 0) { m_currImageID = -1; m_ui->imgView->setPixmap(QPixmap()); } else if(m_imgInfoMap.contains(imageID)) { ImageInfoIter info = m_imgInfoMap.find(imageID); m_ui->imgView->blockSignals(true); m_ui->imgView->setPixmap(info->pixmap); setZoom(*info); m_ui->imgView->blockSignals(false); m_ui->bnImgPrev->setDisabled(info == m_imgInfoMap.begin()); m_ui->bnImgNext->setDisabled((info+1) == m_imgInfoMap.end()); m_ui->cmbImg->blockSignals(true); m_ui->cmbImg->setCurrentIndex(m_imgListModel->indexFromID(imageID)); m_ui->cmbImg->blockSignals(false); m_currImageID = imageID; } } void ImageDockerDock::setZoom(const ImageInfo& info) { m_ui->imgView->setViewMode(info.viewMode, info.scale); m_ui->imgView->setScrollPos(info.scrollPos); int zoom = qRound(m_ui->imgView->getScale() * 100.0f); m_popupUi->zoomSlider->blockSignals(true); m_popupUi->zoomSlider->setValue(zoom); m_popupUi->zoomSlider->blockSignals(false); } void ImageDockerDock::saveConfigState() { const QString lastUsedDirectory = m_model->filePath(m_proxyModel->mapToSource(m_ui->treeView->rootIndex())); KConfigGroup cfg = KSharedConfig::openConfig()->group("referenceImageDocker"); cfg.writeEntry("lastUsedDirectory", lastUsedDirectory); } void ImageDockerDock::loadConfigState() { - const QString defaultLocation = QDesktopServices::storageLocation(QDesktopServices::PicturesLocation); + const QString defaultLocation = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); KConfigGroup cfg = KSharedConfig::openConfig()->group("referenceImageDocker"); QString lastUsedDirectory = cfg.readEntry("lastUsedDirectory", defaultLocation); if (!QFileInfo(lastUsedDirectory).exists()) { lastUsedDirectory = defaultLocation; } m_model->setRootPath(lastUsedDirectory); m_ui->treeView->setRootIndex(m_proxyModel->mapFromSource(m_model->index(lastUsedDirectory))); updatePath(lastUsedDirectory); } // ------------ slots ------------------------------------------------- // void ImageDockerDock::slotItemDoubleClicked(const QModelIndex& index) { QModelIndex mappedIndex = m_proxyModel->mapToSource(index); mappedIndex = m_model->index(mappedIndex.row(), 0, mappedIndex.parent()); QString path(m_model->filePath(mappedIndex)); if(m_model->isDir(mappedIndex)) { addCurrentPathToHistory(); updatePath(path); m_ui->treeView->setRootIndex(m_proxyModel->mapFromSource(mappedIndex)); } else slotOpenImage(path); } void ImageDockerDock::slotBackButtonClicked() { if(!m_history.empty()) { QString path = m_history.last(); QModelIndex index = m_proxyModel->mapFromSource(m_model->index(path)); m_ui->treeView->setRootIndex(index); m_history.pop_back(); updatePath(path); } } void ImageDockerDock::slotHomeButtonClicked() { addCurrentPathToHistory(); QModelIndex index = m_proxyModel->mapFromSource(m_model->index(QDir::homePath())); m_ui->treeView->setRootIndex(index); updatePath(QDir::homePath()); } void ImageDockerDock::slotUpButtonClicked() { addCurrentPathToHistory(); QModelIndex index = m_proxyModel->mapToSource(m_ui->treeView->rootIndex()); QDir dir(m_model->filePath(index)); dir.makeAbsolute(); if(dir.cdUp()) { index = m_proxyModel->mapFromSource(m_model->index(dir.path())); m_ui->treeView->setRootIndex(index); updatePath(dir.path()); } } void ImageDockerDock::slotOpenImage(const QString& path) { QPixmap pixmap(path); if(!pixmap.isNull()) { QFileInfo fileInfo(path); ImageInfo imgInfo; imgInfo.id = generateImageID(); imgInfo.name = fileInfo.fileName(); imgInfo.path = fileInfo.absoluteFilePath(); imgInfo.viewMode = ImageView::VIEW_MODE_FIT; imgInfo.scale = 1.0f; imgInfo.pixmap = pixmap; imgInfo.scrollPos = QPoint(0, 0); m_imgInfoMap[imgInfo.id] = imgInfo; m_imgListModel->addImage(imgInfo.pixmap, imgInfo.name, imgInfo.id); setCurrentImage(imgInfo.id); m_ui->tabWidget->setCurrentIndex(1); } } void ImageDockerDock::slotCloseCurrentImage() { ImageInfoIter info = m_imgInfoMap.find(m_currImageID); if(info != m_imgInfoMap.end()) { ImageInfoIter next = info + 1; ImageInfoIter prev = info - 1; qint64 id = -1; if(next != m_imgInfoMap.end()) id = next->id; else if(info != m_imgInfoMap.begin()) id = prev->id; m_imgListModel->removeImage(info->id); m_imgInfoMap.erase(info); setCurrentImage(id); if(id < 0) m_ui->tabWidget->setCurrentIndex(0); } } void ImageDockerDock::slotNextImage() { ImageInfoIter info = m_imgInfoMap.find(m_currImageID); if(info != m_imgInfoMap.end()) { ++info; if(info != m_imgInfoMap.end()) setCurrentImage(info->id); } } void ImageDockerDock::slotPrevImage() { ImageInfoIter info = m_imgInfoMap.find(m_currImageID); if(info != m_imgInfoMap.end() && info != m_imgInfoMap.begin()) { --info; setCurrentImage(info->id); } } void ImageDockerDock::slotImageChoosenFromComboBox(int index) { setCurrentImage(m_imgListModel->imageID(index)); } void ImageDockerDock::slotZoomChanged(int zoom) { if(isImageLoaded()) { ImageInfoIter info = m_imgInfoMap.find(m_currImageID); switch(zoom) { case ImageView::VIEW_MODE_FIT: case ImageView::VIEW_MODE_ADJUST: info->viewMode = zoom; break; default: info->viewMode = ImageView::VIEW_MODE_FREE; info->scale = float(zoom) / 100.0f; break; } setZoom(*info); } } void ImageDockerDock::slotColorSelected(const QColor& color) { if (m_canvas) { m_canvas->resourceManager()->setForegroundColor(KoColor(color, KoColorSpaceRegistry::instance()->rgb8())); } } void ImageDockerDock::slotViewModeChanged(int viewMode, qreal scale) { if(isImageLoaded()) { m_imgInfoMap[m_currImageID].viewMode = viewMode; m_imgInfoMap[m_currImageID].scale = scale; int zoom = qRound(scale * 100.0); m_popupUi->zoomSlider->blockSignals(true); m_popupUi->zoomSlider->setValue(zoom); m_popupUi->zoomSlider->blockSignals(false); } } void ImageDockerDock::slotCloseZoomPopup() { m_ui->bnPopup->hidePopupWidget(); } void ImageDockerDock::slotChangeRoot(const QString &path) { m_model->setRootPath(path); m_ui->treeView->setRootIndex(m_proxyModel->mapFromSource(m_model->index(path))); updatePath(path); } bool ImageDockerDock::eventFilter(QObject *obj, QEvent *event) { Q_UNUSED(obj); if (event->type() == QEvent::Resize) { m_ui->treeView->setColumnWidth(0, width()); return true; } return false; } diff --git a/plugins/dockers/shapedockers/CollectionItemModel.cpp b/plugins/dockers/shapedockers/CollectionItemModel.cpp index f437b67c6f..9dbb287ac1 100644 --- a/plugins/dockers/shapedockers/CollectionItemModel.cpp +++ b/plugins/dockers/shapedockers/CollectionItemModel.cpp @@ -1,129 +1,134 @@ /* This file is part of the KDE project * Copyright (C) 2008 Peter Simonsson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; 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 "CollectionItemModel.h" #include #include #include #include CollectionItemModel::CollectionItemModel(QObject *parent) : QAbstractListModel(parent) { - setSupportedDragActions(Qt::CopyAction); } QVariant CollectionItemModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() > m_shapeTemplateList.count()) { return QVariant(); } switch (role) { case Qt::ToolTipRole: return m_shapeTemplateList[index.row()].toolTip; case Qt::DecorationRole: return m_shapeTemplateList[index.row()].icon; case Qt::UserRole: return m_shapeTemplateList[index.row()].id; case Qt::DisplayRole: return m_shapeTemplateList[index.row()].name; default: return QVariant(); } return QVariant(); } int CollectionItemModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_shapeTemplateList.count(); } void CollectionItemModel::setShapeTemplateList(const QList &newlist) { m_shapeTemplateList = newlist; - reset(); + beginResetModel(); + endResetModel(); } QMimeData *CollectionItemModel::mimeData(const QModelIndexList &indexes) const { if (indexes.isEmpty()) { return 0; } QModelIndex index = indexes.first(); if (!index.isValid()) { return 0; } if (m_shapeTemplateList.isEmpty()) { return 0; } QByteArray itemData; QDataStream dataStream(&itemData, QIODevice::WriteOnly); dataStream << m_shapeTemplateList[index.row()].id; const KoProperties *props = m_shapeTemplateList[index.row()].properties; if (props) { dataStream << props->store("shapes"); } else { dataStream << QString(); } QMimeData *mimeData = new QMimeData; mimeData->setData(SHAPETEMPLATE_MIMETYPE, itemData); return mimeData; } QStringList CollectionItemModel::mimeTypes() const { QStringList mimetypes; mimetypes << SHAPETEMPLATE_MIMETYPE; return mimetypes; } Qt::ItemFlags CollectionItemModel::flags(const QModelIndex &index) const { if (index.isValid()) { return QAbstractListModel::flags(index) | Qt::ItemIsDragEnabled; } return QAbstractListModel::flags(index); } const KoProperties *CollectionItemModel::properties(const QModelIndex &index) const { if (!index.isValid() || index.row() > m_shapeTemplateList.count()) { return 0; } return m_shapeTemplateList[index.row()].properties; } + +Qt::DropActions CollectionItemModel::supportedDragActions() const +{ + return Qt::CopyAction; +} diff --git a/plugins/dockers/shapedockers/CollectionItemModel.h b/plugins/dockers/shapedockers/CollectionItemModel.h index 7f0731854e..ccb887b0c8 100644 --- a/plugins/dockers/shapedockers/CollectionItemModel.h +++ b/plugins/dockers/shapedockers/CollectionItemModel.h @@ -1,75 +1,77 @@ /* This file is part of the KDE project * Copyright (C) 2008 Peter Simonsson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; 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 KIVIOSHAPETEMPLATEMODEL_H #define KIVIOSHAPETEMPLATEMODEL_H #include #include #include #include #include class KoProperties; /** * Struct containing the information stored in CollectionItemModel item */ struct KoCollectionItem { KoCollectionItem() { properties = 0; }; QString id; QString name; QString toolTip; QIcon icon; const KoProperties *properties; }; class CollectionItemModel : public QAbstractListModel { Q_OBJECT public: explicit CollectionItemModel(QObject *parent = 0); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QMimeData *mimeData(const QModelIndexList &indexes) const override; QStringList mimeTypes() const override; Qt::ItemFlags flags(const QModelIndex &index) const override; /** * Set the list of KoCollectionItem to be stored in the model */ void setShapeTemplateList(const QList &newlist); QList shapeTemplateList() const { return m_shapeTemplateList; } const KoProperties *properties(const QModelIndex &index) const; + Qt::DropActions supportedDragActions() const; + private: QList m_shapeTemplateList; QString m_family; }; #endif //KIVIOSHAPETEMPLATEMODEL_H diff --git a/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.cpp b/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.cpp index 1429cc0847..752264d14b 100644 --- a/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.cpp +++ b/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.cpp @@ -1,208 +1,212 @@ /* This file is part of the KDE project * Copyright (C) 2008 Peter Simonsson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "SvgSymbolCollectionDocker.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui_WdgSvgCollection.h" #include // // SvgCollectionModel // SvgCollectionModel::SvgCollectionModel(QObject *parent) : QAbstractListModel(parent) { - setSupportedDragActions(Qt::CopyAction); } QVariant SvgCollectionModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() > m_symbolCollection->symbols().count()) { return QVariant(); } switch (role) { case Qt::ToolTipRole: return m_symbolCollection->symbols()[index.row()]->title; case Qt::DecorationRole: { QPixmap px = QPixmap::fromImage(m_symbolCollection->symbols()[index.row()]->icon()); QIcon icon(px); return icon; } case Qt::UserRole: return m_symbolCollection->symbols()[index.row()]->id; case Qt::DisplayRole: return m_symbolCollection->symbols()[index.row()]->title; default: return QVariant(); } return QVariant(); } int SvgCollectionModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_symbolCollection->symbols().count(); } QMimeData *SvgCollectionModel::mimeData(const QModelIndexList &indexes) const { if (indexes.isEmpty()) { return 0; } QModelIndex index = indexes.first(); if (!index.isValid()) { return 0; } if (m_symbolCollection->symbols().isEmpty()) { return 0; } QList shapes; shapes.append(m_symbolCollection->symbols()[index.row()]->shape); KoDrag drag; drag.setSvg(shapes); QMimeData *mimeData = drag.mimeData(); return mimeData; } QStringList SvgCollectionModel::mimeTypes() const { return QStringList() << SHAPETEMPLATE_MIMETYPE << "image/svg+xml"; } Qt::ItemFlags SvgCollectionModel::flags(const QModelIndex &index) const { if (index.isValid()) { return QAbstractListModel::flags(index) | Qt::ItemIsDragEnabled; } return QAbstractListModel::flags(index); } +Qt::DropActions SvgCollectionModel::supportedDragActions() const +{ + return Qt::CopyAction; +} + void SvgCollectionModel::setSvgSymbolCollectionResource(KoSvgSymbolCollectionResource *resource) { m_symbolCollection = resource; } // // SvgSymbolCollectionDockerFactory // SvgSymbolCollectionDockerFactory::SvgSymbolCollectionDockerFactory() : KoDockFactoryBase() { } QString SvgSymbolCollectionDockerFactory::id() const { return QString("SvgSymbolCollectionDocker"); } QDockWidget *SvgSymbolCollectionDockerFactory::createDockWidget() { SvgSymbolCollectionDocker *docker = new SvgSymbolCollectionDocker(); return docker; } // // SvgSymbolCollectionDocker // SvgSymbolCollectionDocker::SvgSymbolCollectionDocker(QWidget *parent) : QDockWidget(parent) , m_wdgSvgCollection(new Ui_WdgSvgCollection()) { setWindowTitle(i18n("Vector Libraries")); QWidget* mainWidget = new QWidget(this); setWidget(mainWidget); m_wdgSvgCollection->setupUi(mainWidget); connect(m_wdgSvgCollection->cmbCollections, SIGNAL(activated(int)), SLOT(collectionActivated(int))); KoResourceServer *svgCollectionProvider = KoResourceServerProvider::instance()->svgSymbolCollectionServer(); Q_FOREACH(KoSvgSymbolCollectionResource *r, svgCollectionProvider->resources()) { m_wdgSvgCollection->cmbCollections->addSqueezedItem(r->name()); SvgCollectionModel *model = new SvgCollectionModel(); model->setSvgSymbolCollectionResource(r); m_models.append(model); } m_wdgSvgCollection->listCollection->setDragEnabled(true); m_wdgSvgCollection->listCollection->setDragDropMode(QAbstractItemView::DragOnly); m_wdgSvgCollection->listCollection->setSelectionMode(QListView::SingleSelection); KConfigGroup cfg = KSharedConfig::openConfig()->group("SvgSymbolCollection"); int i = cfg.readEntry("currentCollection", 0); if (i > m_wdgSvgCollection->cmbCollections->count()) { i = 0; } m_wdgSvgCollection->cmbCollections->setCurrentIndex(i); collectionActivated(i); } void SvgSymbolCollectionDocker::setCanvas(KoCanvasBase *canvas) { setEnabled(canvas != 0); } void SvgSymbolCollectionDocker::unsetCanvas() { setEnabled(false); } void SvgSymbolCollectionDocker::collectionActivated(int index) { if (index < m_models.size()) { KConfigGroup cfg = KSharedConfig::openConfig()->group("SvgSymbolCollection"); cfg.writeEntry("currentCollection", index); m_wdgSvgCollection->listCollection->setModel(m_models[index]); } } diff --git a/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.h b/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.h index bb349bc0ac..817d2df48a 100644 --- a/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.h +++ b/plugins/dockers/shapedockers/SvgSymbolCollectionDocker.h @@ -1,86 +1,87 @@ /* This file is part of the KDE project * Copyright (C) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef SVGSYMBOLCOLLECTIONDOCKER_H #define SVGSYMBOLCOLLECTIONDOCKER_H #include #include #include #include #include #include #include #include "ui_WdgSvgCollection.h" class KoSvgSymbolCollectionResource; class SvgCollectionModel : public QAbstractListModel { Q_OBJECT public: explicit SvgCollectionModel(QObject *parent = 0); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QMimeData *mimeData(const QModelIndexList &indexes) const override; QStringList mimeTypes() const override; Qt::ItemFlags flags(const QModelIndex &index) const override; + Qt::DropActions supportedDragActions() const; public: void setSvgSymbolCollectionResource(KoSvgSymbolCollectionResource *resource); private: KoSvgSymbolCollectionResource *m_symbolCollection; }; class SvgSymbolCollectionDockerFactory : public KoDockFactoryBase { public: SvgSymbolCollectionDockerFactory(); QString id() const override; QDockWidget *createDockWidget() override; DockPosition defaultDockPosition() const override { return DockRight; } }; class SvgSymbolCollectionDocker : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: explicit SvgSymbolCollectionDocker(QWidget *parent = 0); /// reimplemented void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; private Q_SLOTS: void collectionActivated(int index); private: Ui_WdgSvgCollection *m_wdgSvgCollection; QVector m_models; }; #endif //KOSHAPECOLLECTIONDOCKER_H diff --git a/plugins/dockers/tasksetdocker/tasksetmodel.cpp b/plugins/dockers/tasksetdocker/tasksetmodel.cpp index 8595866a64..26661e1301 100644 --- a/plugins/dockers/tasksetdocker/tasksetmodel.cpp +++ b/plugins/dockers/tasksetdocker/tasksetmodel.cpp @@ -1,100 +1,102 @@ /* * Copyright (c) 2011 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; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tasksetmodel.h" #include #include #include TasksetModel::TasksetModel(QObject* parent): QAbstractTableModel(parent) { } TasksetModel::~TasksetModel() { } QVariant TasksetModel::data(const QModelIndex& index, int role) const { if (index.isValid()) { switch (role) { case Qt::DisplayRole: { return m_actions.at(index.row())->iconText(); } case Qt::DecorationRole: { const QIcon icon = m_actions.at(index.row())->icon(); if (icon.isNull()) { return KisIconUtils::loadIcon("tools-wizard"); } return icon; } } } return QVariant(); } QVariant TasksetModel::headerData(int /*section*/, Qt::Orientation /*orientation*/, int /*role*/) const { return i18n("Task"); } int TasksetModel::rowCount(const QModelIndex& /*parent*/) const { return m_actions.count(); } int TasksetModel::columnCount(const QModelIndex& /*parent*/) const { return 1; } Qt::ItemFlags TasksetModel::flags(const QModelIndex& /*index*/) const { Qt::ItemFlags flags = /*Qt::ItemIsSelectable |*/ Qt::ItemIsEnabled; return flags; } void TasksetModel::addAction(QAction* action) { m_actions.append(action); - reset(); + beginResetModel(); + endResetModel(); } QVector< QAction* > TasksetModel::actions() { return m_actions; } QAction* TasksetModel::actionFromIndex(const QModelIndex& index) { if(index.isValid()) { return m_actions.at(index.row()); } return 0; } void TasksetModel::clear() { m_actions.clear(); - reset(); + beginResetModel(); + endResetModel(); } diff --git a/plugins/extensions/bigbrother/bigbrother.cc b/plugins/extensions/bigbrother/bigbrother.cc index 0586196030..2df5da991c 100644 --- a/plugins/extensions/bigbrother/bigbrother.cc +++ b/plugins/extensions/bigbrother/bigbrother.cc @@ -1,238 +1,238 @@ /* * Copyright (c) 2007 Cyrille Berger (cberger@cberger.net) * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "bigbrother.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "actionseditor/kis_actions_editor.h" #include "actionseditor/kis_actions_editor_dialog.h" -#include +#include #include K_PLUGIN_FACTORY_WITH_JSON(BigBrotherPluginFactory, "kritabigbrother.json", registerPlugin();) class RecordedActionSaveContext : public KisRecordedActionSaveContext { public: void saveGradient(const KoAbstractGradient* ) override {} void savePattern(const KoPattern* ) override {} }; class RecordedActionLoadContext : public KisRecordedActionLoadContext { public: KoAbstractGradient* gradient(const QString& name) const override { return KoResourceServerProvider::instance()->gradientServer()->resourceByName(name); } KoPattern* pattern(const QString& name) const override { return KoResourceServerProvider::instance()->patternServer()->resourceByName(name); } }; BigBrotherPlugin::BigBrotherPlugin(QObject *parent, const QVariantList &) : KisViewPlugin(parent) , m_recorder(0) { if (parent->inherits("KisViewManager")) { m_view = (KisViewManager*) parent; // Open and play action KisAction* action = createAction("Macro_Open_Play"); connect(action, SIGNAL(triggered()), this, SLOT(slotOpenPlay())); // Open and edit action action = createAction("Macro_Open_Edit"); connect(action, SIGNAL(triggered()), this, SLOT(slotOpenEdit())); // Start recording action m_startRecordingMacroAction = createAction("Recording_Start_Recording_Macro"); connect(m_startRecordingMacroAction, SIGNAL(triggered()), this, SLOT(slotStartRecordingMacro())); // Save recorded action m_stopRecordingMacroAction = createAction("Recording_Stop_Recording_Macro"); connect(m_stopRecordingMacroAction, SIGNAL(triggered()), this, SLOT(slotStopRecordingMacro())); m_stopRecordingMacroAction->setEnabled(false); } } BigBrotherPlugin::~BigBrotherPlugin() { m_view = 0; delete m_recorder; } void BigBrotherPlugin::slotOpenPlay() { KisMacro* m = openMacro(); dbgKrita << m; if (!m) return; dbgPlugins << "Play the macro"; KoUpdaterPtr updater = m_view->createUnthreadedUpdater(i18n("Playing back macro")); KisMacroPlayer player(m, KisPlayInfo(m_view->image(), m_view->activeNode()), updater); player.start(); while(player.isRunning()) { QApplication::processEvents(); } dbgPlugins << "Finished"; delete m; } void BigBrotherPlugin::slotOpenEdit() { KisMacro *macro = openMacro(); if (!macro) return; KisActionsEditorDialog aed(m_view->mainWindow()); aed.actionsEditor()->setMacro(macro); if (aed.exec() == QDialog::Accepted) { saveMacro(macro); } delete macro; } void BigBrotherPlugin::slotStartRecordingMacro() { dbgPlugins << "Start recording macro"; if (m_recorder) return; // Alternate actions m_startRecordingMacroAction->setEnabled(false); m_stopRecordingMacroAction->setEnabled(true); // Create recorder m_recorder = new KisMacro(); connect(m_view->image()->actionRecorder(), SIGNAL(addedAction(const KisRecordedAction&)), m_recorder, SLOT(addAction(const KisRecordedAction&))); } void BigBrotherPlugin::slotStopRecordingMacro() { dbgPlugins << "Stop recording macro"; if (!m_recorder) return; // Alternate actions m_startRecordingMacroAction->setEnabled(true); m_stopRecordingMacroAction->setEnabled(false); // Save the macro saveMacro(m_recorder); // Delete recorder delete m_recorder; m_recorder = 0; } KisMacro* BigBrotherPlugin::openMacro() { QStringList mimeFilter; mimeFilter << "*.krarec|Recorded actions (*.krarec)"; KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::OpenFile, "OpenDocument"); dialog.setCaption(i18n("Open Macro")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(QStringList() << "application/krita-recorded-macro", "application/krita-recorded-macro"); QString filename = dialog.filename(); RecordedActionLoadContext loadContext; if (!filename.isNull()) { QDomDocument doc; QFile f(filename); if (f.exists()) { dbgPlugins << f.open(QIODevice::ReadOnly); QString err; int line, col; if (!doc.setContent(&f, &err, &line, &col)) { // TODO error message dbgPlugins << err << " line = " << line << " col = " << col; f.close(); return 0; } f.close(); QDomElement docElem = doc.documentElement(); if (!docElem.isNull() && docElem.tagName() == "RecordedActions") { dbgPlugins << "Load the macro"; KisMacro* m = new KisMacro(); m->fromXML(docElem, &loadContext); return m; } else { // TODO error message } } else { dbgPlugins << "Unexistant file : " << filename; } } return 0; } void BigBrotherPlugin::saveMacro(const KisMacro* macro) { KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::SaveFile, "bigbrother"); dialog.setCaption(i18n("Save Macro")); dialog.setMimeTypeFilters(QStringList() << "application/krita-recorded-macro", "application/krita-recorded-macro"); QString filename = dialog.filename(); if (!filename.isNull()) { QDomDocument doc; QDomElement e = doc.createElement("RecordedActions"); RecordedActionSaveContext context; macro->toXML(doc, e, &context); doc.appendChild(e); QFile f(filename); f.open(QIODevice::WriteOnly); QTextStream stream(&f); stream.setCodec("UTF-8"); doc.save(stream, 2); f.close(); } } #include "bigbrother.moc" diff --git a/plugins/extensions/imagesplit/imagesplit.cpp b/plugins/extensions/imagesplit/imagesplit.cpp index 884d0b34a6..a0817223f4 100644 --- a/plugins/extensions/imagesplit/imagesplit.cpp +++ b/plugins/extensions/imagesplit/imagesplit.cpp @@ -1,207 +1,207 @@ /* * imagesplit.cc -- Part of Krita * * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * Copyright (c) 2011 Srikanth Tiyyagura * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "imagesplit.h" #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dlg_imagesplit.h" K_PLUGIN_FACTORY_WITH_JSON(ImagesplitFactory, "kritaimagesplit.json", registerPlugin();) Imagesplit::Imagesplit(QObject *parent, const QVariantList &) : KisViewPlugin(parent) { KisAction *action = createAction("imagesplit"); connect(action, SIGNAL(triggered()), this, SLOT(slotImagesplit())); } Imagesplit::~Imagesplit() { } bool Imagesplit::saveAsImage(const QRect &imgSize, const QString &mimeType, const QString &url) { KisImageSP image = m_view->image(); KisDocument *document = KisPart::instance()->createDocument(); KisImageSP dst = new KisImage(document->createUndoStore(), imgSize.width(), imgSize.height(), image->colorSpace(), image->objectName()); dst->setResolution(image->xRes(), image->yRes()); document->setCurrentImage(dst); KisPaintLayer* paintLayer = new KisPaintLayer(dst, dst->nextLayerName(), 255); KisPainter gc(paintLayer->paintDevice()); gc.bitBlt(QPoint(0, 0), image->projection(), imgSize); dst->addNode(paintLayer, KisNodeSP(0)); dst->refreshGraph(); document->setFileBatchMode(true); if (!document->exportDocumentSync(QUrl::fromLocalFile(url), mimeType.toLatin1())) { if (document->errorMessage().isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save\n%1", document->localFilePath())); } else { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save %1\nReason: %2", document->localFilePath(), document->errorMessage())); } return false; } delete document; return true; } void Imagesplit::slotImagesplit() { // Taking the title - url from caption function and removing file extension QStringList strList = ((m_view->document())->caption()).split('.'); QString suffix = strList.at(0); // Getting all mime types and converting them into names which are displayed at combo box QStringList listMimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export); QString defaultMime = QString::fromLatin1(m_view->document()->mimeType()); int defaultMimeIndex = 0; listMimeFilter.sort(); QStringList filteredMimeTypes; QStringList listFileType; int i = 0; Q_FOREACH (const QString & mimeType, listMimeFilter) { listFileType.append(KisMimeDatabase::descriptionForMimeType(mimeType)); filteredMimeTypes.append(mimeType); if (mimeType == defaultMime) { defaultMimeIndex = i; } i++; } listMimeFilter = filteredMimeTypes; Q_ASSERT(listMimeFilter.size() == listFileType.size()); DlgImagesplit *dlgImagesplit = new DlgImagesplit(m_view, suffix, listFileType, defaultMimeIndex); dlgImagesplit->setObjectName("Imagesplit"); Q_CHECK_PTR(dlgImagesplit); KisImageWSP image = m_view->image(); if (dlgImagesplit->exec() == QDialog::Accepted) { int numHorizontalLines = dlgImagesplit->horizontalLines(); int numVerticalLines = dlgImagesplit->verticalLines(); int img_width = image->width() / (numVerticalLines + 1); int img_height = image->height() / (numHorizontalLines + 1); bool stop = false; if (dlgImagesplit->autoSave()) { KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::OpenDirectory, "OpenDocument"); dialog.setCaption(i18n("Save Image on Split")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); QStringList mimeFilter = m_view->document()->importExportManager()->mimeFilter(KisImportExportManager::Export); QString defaultMime = QString::fromLatin1(m_view->document()->mimeType()); dialog.setMimeTypeFilters(mimeFilter, defaultMime); QUrl directory = QUrl::fromUserInput(dialog.filename()); if (directory.isEmpty()) return; for (int i = 0, k = 1; i < (numVerticalLines + 1); i++) { for (int j = 0; j < (numHorizontalLines + 1); j++, k++) { QString mimeTypeSelected = listMimeFilter.at(dlgImagesplit->cmbIndex); QString homepath = directory.toLocalFile(); QString suffix = KisMimeDatabase::suffixesForMimeType(mimeTypeSelected).first(); qDebug() << "suffix" << suffix; if (suffix.startsWith("*.")) { suffix = suffix.remove(0, 1); } qDebug() << "\tsuffix" << suffix; if (!suffix.startsWith(".")) { suffix = suffix.prepend('.'); } qDebug() << "\tsuffix" << suffix; QString fileName = dlgImagesplit->suffix() + '_' + QString::number(k) + suffix; QString url = homepath + '/' + fileName; if (!saveAsImage(QRect((i * img_width), (j * img_height), img_width, img_height), listMimeFilter.at(dlgImagesplit->cmbIndex), url)) { stop = true; break; } } if (stop) { break; } } } else { for (int i = 0; i < (numVerticalLines + 1); i++) { for (int j = 0; j < (numHorizontalLines + 1); j++) { KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::SaveFile, "OpenDocument"); dialog.setCaption(i18n("Save Image on Split")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(listMimeFilter, defaultMime); QUrl url = QUrl::fromUserInput(dialog.filename()); QString mimefilter = KisMimeDatabase::mimeTypeForFile(url.toLocalFile()); if (url.isEmpty()) return; if (!saveAsImage(QRect((i * img_width), (j * img_height), img_width, img_height), mimefilter, url.toLocalFile())) { stop = true; break; } } if (stop) { break; } } } } delete dlgImagesplit; } #include "imagesplit.moc" diff --git a/plugins/extensions/pykrita/plugin/version_checker.h b/plugins/extensions/pykrita/plugin/version_checker.h index bfb9c2c5fb..33a32d292a 100644 --- a/plugins/extensions/pykrita/plugin/version_checker.h +++ b/plugins/extensions/pykrita/plugin/version_checker.h @@ -1,279 +1,279 @@ // This file is part of PyKrita, Krita' Python scripting plugin. // // Copyright (C) 2013 Alex Turbov // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) version 3. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with this library; 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 __VERSION_CHECKER_H__ # define __VERSION_CHECKER_H__ # include # include # include namespace PyKrita { /** * \brief Class \c version */ class version { enum type { undefined = -1 , zero = 0 }; public: /// Default constructor explicit version( const int major = zero , const int minor = zero , const int patch = zero ) : m_major(major) , m_minor(minor) , m_patch(patch) { } int major() const { return m_major; } int minor() const { return m_minor; } int patch() const { return m_patch; } bool isValid() const { return major() != undefined && minor() != undefined && patch() != undefined; } operator QString() const { return QString("%1.%2.%3").arg(major()).arg(minor()).arg(patch()); } static version fromString(const QString& version_str) { int tmp[3] = {zero, zero, zero}; QStringList parts = version_str.split('.'); for ( unsigned long i = 0 ; i < qMin(static_cast(sizeof(tmp) / sizeof(int)), static_cast(parts.size())) ; ++i ) { bool ok; const int num = parts[i].toInt(&ok); if (ok) tmp[i] = num; else { tmp[i] = undefined; break; } } return version(tmp[0], tmp[1], tmp[2]); }; static version invalid() { static version s_bad(undefined, undefined, undefined); return s_bad; } private: int m_major; int m_minor; int m_patch; }; inline bool operator==(const version& left, const version& right) { return left.major() == right.major() && left.minor() == right.minor() && left.patch() == right.patch() ; } inline bool operator!=(const version& left, const version& right) { return !(left == right); } inline bool operator<(const version& left, const version& right) { return left.major() < right.major() || (left.major() == right.major() && left.minor() < right.minor()) || (left.major() == right.major() && left.minor() == right.minor() && left.patch() < right.patch()) ; } inline bool operator>(const version& left, const version& right) { return left.major() > right.major() || (left.major() == right.major() && left.minor() > right.minor()) || (left.major() == right.major() && left.minor() == right.minor() && left.patch() > right.patch()) ; } inline bool operator<=(const version& left, const version& right) { return left == right || left < right; } inline bool operator>=(const version& left, const version& right) { return left == right || left > right; } /** * \brief Class \c version_checker */ class version_checker { public: enum operation { invalid , undefined , less , less_or_equal , greather , greather_or_equal , not_equal , equal , last__ }; /// Default constructor explicit version_checker(const operation op = invalid) : m_op(op) { } bool isValid() const { return m_op != invalid; } bool isEmpty() const { return m_op == undefined; } void bind_second(const version& rhs) { m_rhs = rhs; } bool operator()(const version& left) { switch (m_op) { case less: return left < m_rhs; case greather: return left > m_rhs; case equal: return left == m_rhs; case not_equal: return left != m_rhs; case less_or_equal: return left <= m_rhs; case greather_or_equal: return left >= m_rhs; default: Q_ASSERT(!"Sanity check"); break; } return false; } version required() const { return m_rhs; } QString operationToString() const { QString result; switch (m_op) { case less: result = " < "; break; case greather: result = " > "; break; case equal: result = " = "; break; case not_equal: result = " != "; break; case less_or_equal: result = " <= "; break; case greather_or_equal: result = " >= "; break; default: Q_ASSERT(!"Sanity check"); break; } return result; } static version_checker fromString(const QString& version_info) { version_checker checker(invalid); if (version_info.isEmpty()) return checker; bool lookup_next_char = false; int strip_lead_pos = 0; - switch (version_info.at(0).toAscii()) { + switch (version_info.at(0).toLatin1()) { case '<': checker.m_op = less; lookup_next_char = true; break; case '>': checker.m_op = greather; lookup_next_char = true; break; case '=': strip_lead_pos = 1; checker.m_op = equal; break; default: strip_lead_pos = 0; checker.m_op = equal; break; } if (lookup_next_char) { - if (version_info.at(1).toAscii() == '=') { + if (version_info.at(1).toLatin1() == '=') { // NOTE Shift state checker.m_op = operation(int(checker.m_op) + 1); strip_lead_pos = 2; } else { strip_lead_pos = 1; } } // QString rhs_str = version_info.mid(strip_lead_pos).trimmed(); version rhs = version::fromString(rhs_str); if (rhs.isValid()) checker.bind_second(rhs); else checker.m_op = invalid; return checker; } private: operation m_op; version m_rhs; }; } // namespace PyKrita #endif // __VERSION_CHECKER_H__ diff --git a/plugins/extensions/qmic/QMic.cpp b/plugins/extensions/qmic/QMic.cpp index ea27fbec84..ff26f78eb2 100644 --- a/plugins/extensions/qmic/QMic.cpp +++ b/plugins/extensions/qmic/QMic.cpp @@ -1,460 +1,460 @@ /* * Copyright (c) 2017 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 "QMic.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_input_output_mapper.h" #include "kis_qmic_simple_convertor.h" #include "kis_import_qmic_processing_visitor.h" #include #include "kis_qmic_applicator.h" static const char ack[] = "ack"; K_PLUGIN_FACTORY_WITH_JSON(QMicFactory, "kritaqmic.json", registerPlugin();) QMic::QMic(QObject *parent, const QVariantList &) : KisViewPlugin(parent) , m_gmicApplicator(0) { #ifndef Q_OS_MAC // KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); // PluginSettingsFactory* settingsFactory = new PluginSettingsFactory(); // preferenceSetRegistry->add("QMicPluginSettingsFactory", settingsFactory); m_qmicAction = createAction("QMic"); m_qmicAction->setActivationFlags(KisAction::ACTIVE_DEVICE); connect(m_qmicAction , SIGNAL(triggered()), this, SLOT(slotQMic())); m_againAction = createAction("QMicAgain"); m_againAction->setActivationFlags(KisAction::ACTIVE_DEVICE); m_againAction->setEnabled(false); connect(m_againAction, SIGNAL(triggered()), this, SLOT(slotQMicAgain())); m_gmicApplicator = new KisQmicApplicator(); connect(m_gmicApplicator, SIGNAL(gmicFinished(bool, int, QString)), this, SLOT(slotGmicFinished(bool, int, QString))); #endif } QMic::~QMic() { Q_FOREACH(QSharedMemory *memorySegment, m_sharedMemorySegments) { qDebug() << "detaching" << memorySegment->key(); memorySegment->detach(); } qDeleteAll(m_sharedMemorySegments); m_sharedMemorySegments.clear(); if (m_pluginProcess) { m_pluginProcess->close(); } delete m_gmicApplicator; delete m_localServer; } void QMic::slotQMicAgain() { slotQMic(true); } void QMic::slotQMic(bool again) { m_qmicAction->setEnabled(false); m_againAction->setEnabled(false); // find the krita-gmic-qt plugin QString pluginPath = PluginSettings::gmicQtPath(); if (pluginPath.isEmpty() || !QFileInfo(pluginPath).exists() || !QFileInfo(pluginPath).isFile()) { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Krita cannot find the gmic-qt plugin.")); return; } m_key = QUuid::createUuid().toString(); m_localServer = new QLocalServer(); m_localServer->listen(m_key); connect(m_localServer, SIGNAL(newConnection()), SLOT(connected())); m_pluginProcess = new QProcess(this); m_pluginProcess->setProcessChannelMode(QProcess::ForwardedChannels); connect(m_pluginProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(pluginFinished(int,QProcess::ExitStatus))); connect(m_pluginProcess, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(pluginStateChanged(QProcess::ProcessState))); - m_pluginProcess->start(pluginPath, QStringList() << m_key << (again ? QString(" reapply") : QString::null)); + m_pluginProcess->start(pluginPath, QStringList() << m_key << (again ? QString(" reapply") : QString())); bool r = m_pluginProcess->waitForStarted(); while (m_pluginProcess->waitForFinished(10)) { qApp->processEvents(QEventLoop::ExcludeUserInputEvents); } qDebug() << "Plugin started" << r << m_pluginProcess->state(); } void QMic::connected() { qDebug() << "connected"; QLocalSocket *socket = m_localServer->nextPendingConnection(); if (!socket) { return; } while (socket->bytesAvailable() < static_cast(sizeof(quint32))) { if (!socket->isValid()) { // stale request return; } socket->waitForReadyRead(1000); } QDataStream ds(socket); QByteArray msg; quint32 remaining; ds >> remaining; msg.resize(remaining); int got = 0; char* uMsgBuf = msg.data(); // FIXME: Should use read transaction for Qt >= 5.7: // https://doc.qt.io/qt-5/qdatastream.html#using-read-transactions do { got = ds.readRawData(uMsgBuf, remaining); remaining -= got; uMsgBuf += got; } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); if (got < 0) { qWarning() << "Message reception failed" << socket->errorString(); delete socket; m_localServer->close(); delete m_localServer; m_localServer = 0; return; } QString message = QString::fromUtf8(msg); qDebug() << "Received" << message; // Check the message: we can get three different ones QMultiMap messageMap; Q_FOREACH(QString line, message.split('\n', QString::SkipEmptyParts)) { QList kv = line.split('=', QString::SkipEmptyParts); if (kv.size() == 2) { messageMap.insert(kv[0], kv[1]); } else { qWarning() << "line" << line << "is invalid."; } } if (!messageMap.contains("command")) { qWarning() << "Message did not contain a command"; return; } int mode = 0; if (messageMap.contains("mode")) { mode = messageMap.values("mode").first().toInt(); } QByteArray ba; QString messageBoxWarningText; if (messageMap.values("command").first() == "gmic_qt_get_image_size") { KisSelectionSP selection = m_view->image()->globalSelection(); if (selection) { QRect selectionRect = selection->selectedExactRect(); ba = QByteArray::number(selectionRect.width()) + "," + QByteArray::number(selectionRect.height()); } else { ba = QByteArray::number(m_view->image()->width()) + "," + QByteArray::number(m_view->image()->height()); } } else if (messageMap.values("command").first() == "gmic_qt_get_cropped_images") { // Parse the message, create the shared memory segments, and create a new message to send back and waid for ack QRectF cropRect(0.0, 0.0, 1.0, 1.0); if (!messageMap.contains("croprect") || messageMap.values("croprect").first().split(',', QString::SkipEmptyParts).size() != 4) { qWarning() << "gmic-qt didn't send a croprect or not a valid croprect"; } else { QStringList cr = messageMap.values("croprect").first().split(',', QString::SkipEmptyParts); cropRect.setX(cr[0].toFloat()); cropRect.setY(cr[1].toFloat()); cropRect.setWidth(cr[2].toFloat()); cropRect.setHeight(cr[3].toFloat()); } if (!prepareCroppedImages(&ba, cropRect, mode)) { qWarning() << "Failed to prepare images for gmic-qt"; } } else if (messageMap.values("command").first() == "gmic_qt_output_images") { // Parse the message. read the shared memory segments, fix up the current image and send an ack qDebug() << "gmic_qt_output_images"; QStringList layers = messageMap.values("layer"); m_outputMode = (OutputMode)mode; if (m_outputMode != IN_PLACE) { messageBoxWarningText = i18n("Sorry, this output mode is not implemented yet."); m_outputMode = IN_PLACE; } slotStartApplicator(layers); } else if (messageMap.values("command").first() == "gmic_qt_detach") { Q_FOREACH(QSharedMemory *memorySegment, m_sharedMemorySegments) { qDebug() << "detaching" << memorySegment->key() << memorySegment->isAttached(); if (memorySegment->isAttached()) { if (!memorySegment->detach()) { qDebug() << "\t" << memorySegment->error() << memorySegment->errorString(); } } } qDeleteAll(m_sharedMemorySegments); m_sharedMemorySegments.clear(); } else { qWarning() << "Received unknown command" << messageMap.values("command"); } qDebug() << "Sending" << QString::fromUtf8(ba); // HACK: Make sure QDataStream does not refuse to write! // Proper fix: Change the above read to use read transaction ds.resetStatus(); ds.writeBytes(ba.constData(), ba.length()); // Flush the socket because we might not return to the event loop! if (!socket->waitForBytesWritten(2000)) { qWarning() << "Failed to write response:" << socket->error(); } // Wait for the ack bool r = true; r &= socket->waitForReadyRead(2000); // wait for ack r &= (socket->read(qstrlen(ack)) == ack); if (!socket->waitForDisconnected(2000)) { qWarning() << "Remote not disconnected:" << socket->error(); // Wait again socket->disconnectFromServer(); if (socket->waitForDisconnected(2000)) { qWarning() << "Disconnect timed out:" << socket->error(); } } if (!messageBoxWarningText.isEmpty()) { // Defer the message box to the event loop QTimer::singleShot(0, [messageBoxWarningText]() { QMessageBox::warning(KisPart::instance()->currentMainwindow(), i18nc("@title:window", "Krita"), messageBoxWarningText); }); } } void QMic::pluginStateChanged(QProcess::ProcessState state) { qDebug() << "stateChanged" << state; } void QMic::pluginFinished(int exitCode, QProcess::ExitStatus exitStatus) { qDebug() << "pluginFinished" << exitCode << exitStatus; delete m_pluginProcess; m_pluginProcess = 0; delete m_localServer; m_localServer = 0; m_qmicAction->setEnabled(true); m_againAction->setEnabled(true); } void QMic::slotGmicFinished(bool successfully, int milliseconds, const QString &msg) { qDebug() << "slotGmicFinished();" << successfully << milliseconds << msg; if (successfully) { m_gmicApplicator->finish(); } else { m_gmicApplicator->cancel(); QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("G'Mic failed, reason:") + msg); } } void QMic::slotStartApplicator(QStringList gmicImages) { qDebug() << "slotStartApplicator();" << gmicImages; // Create a vector of gmic images QVector *> images; Q_FOREACH(const QString &image, gmicImages) { QStringList parts = image.split(',', QString::SkipEmptyParts); Q_ASSERT(parts.size() == 4); QString key = parts[0]; QString layerName = QByteArray::fromHex(parts[1].toLatin1()); int spectrum = parts[2].toInt(); int width = parts[3].toInt(); int height = parts[4].toInt(); qDebug() << key << layerName << width << height; QSharedMemory m(key); if (!m.attach(QSharedMemory::ReadOnly)) { qWarning() << "Could not attach to shared memory area." << m.error() << m.errorString(); } if (m.isAttached()) { if (!m.lock()) { qDebug() << "Could not lock memeory segment" << m.error() << m.errorString(); } qDebug() << "Memory segment" << key << m.size() << m.constData() << m.data(); gmic_image *gimg = new gmic_image(); gimg->assign(width, height, 1, spectrum); gimg->name = layerName; gimg->_data = new float[width * height * spectrum * sizeof(float)]; qDebug() << "width" << width << "height" << height << "size" << width * height * spectrum * sizeof(float) << "shared memory size" << m.size(); memcpy(gimg->_data, m.constData(), width * height * spectrum * sizeof(float)); qDebug() << "created gmic image" << gimg->name << gimg->_width << gimg->_height; if (!m.unlock()) { qDebug() << "Could not unlock memeory segment" << m.error() << m.errorString(); } if (!m.detach()) { qDebug() << "Could not detach from memeory segment" << m.error() << m.errorString(); } images.append(gimg); } } qDebug() << "Got" << images.size() << "gmic images"; // Start the applicator KUndo2MagicString actionName = kundo2_i18n("Gmic filter"); KisNodeSP rootNode = m_view->image()->root(); KisInputOutputMapper mapper(m_view->image(), m_view->activeNode()); KisNodeListSP layers = mapper.inputNodes(m_inputMode); m_gmicApplicator->setProperties(m_view->image(), rootNode, images, actionName, layers); m_gmicApplicator->preview(); m_gmicApplicator->finish(); } bool QMic::prepareCroppedImages(QByteArray *message, QRectF &rc, int inputMode) { m_view->image()->lock(); m_inputMode = (InputLayerMode)inputMode; qDebug() << "prepareCroppedImages()" << QString::fromUtf8(*message) << rc << inputMode; KisInputOutputMapper mapper(m_view->image(), m_view->activeNode()); KisNodeListSP nodes = mapper.inputNodes(m_inputMode); if (nodes->isEmpty()) { m_view->image()->unlock(); return false; } for (int i = 0; i < nodes->size(); ++i) { KisNodeSP node = nodes->at(i); if (node && node->paintDevice()) { QRect cropRect; KisSelectionSP selection = m_view->image()->globalSelection(); if (selection) { cropRect = selection->selectedExactRect(); } else { cropRect = m_view->image()->bounds(); } qDebug() << "Converting node" << node->name() << cropRect; const QRectF mappedRect = KisAlgebra2D::mapToRect(cropRect).mapRect(rc); const QRect resultRect = mappedRect.toAlignedRect(); QSharedMemory *m = new QSharedMemory(QString("key_%1").arg(QUuid::createUuid().toString())); m_sharedMemorySegments.append(m); if (!m->create(resultRect.width() * resultRect.height() * 4 * sizeof(float))) { //buf.size())) { qWarning() << "Could not create shared memory segment" << m->error() << m->errorString(); return false; } m->lock(); gmic_image img; img.assign(resultRect.width(), resultRect.height(), 1, 4); img._data = reinterpret_cast(m->data()); KisQmicSimpleConvertor::convertToGmicImageFast(node->paintDevice(), &img, resultRect); message->append(m->key().toUtf8()); m->unlock(); message->append(","); message->append(node->name().toUtf8().toHex()); message->append(","); message->append(QByteArray::number(resultRect.width())); message->append(","); message->append(QByteArray::number(resultRect.height())); message->append("\n"); } } qDebug() << QString::fromUtf8(*message); m_view->image()->unlock(); return true; } #include "QMic.moc" diff --git a/plugins/extensions/resourcemanager/dlg_create_bundle.cpp b/plugins/extensions/resourcemanager/dlg_create_bundle.cpp index daa5cc8abf..3f2ca1ca21 100644 --- a/plugins/extensions/resourcemanager/dlg_create_bundle.cpp +++ b/plugins/extensions/resourcemanager/dlg_create_bundle.cpp @@ -1,438 +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_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 #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 { setCaption(i18n("Create Resource Bundle")); KisConfig cfg; 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", QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation))); + 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("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; 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()); } } 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()); } } 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); } } } } void DlgCreateBundle::getPreviewImage() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "BundlePreviewImage"); dialog.setCaption(i18n("Select file to use as bundle icon")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(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/resourcemanager.cpp b/plugins/extensions/resourcemanager/resourcemanager.cpp index 67fc7091e3..fc121c8071 100644 --- a/plugins/extensions/resourcemanager/resourcemanager.cpp +++ b/plugins/extensions/resourcemanager/resourcemanager.cpp @@ -1,324 +1,324 @@ /* * 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 "dlg_bundle_manager.h" #include "dlg_create_bundle.h" class ResourceManager::Private { public: Private() { brushServer = KisBrushServer::instance()->brushServer(false); paintopServer = KisResourceServerProvider::instance()->paintOpPresetServer(false); gradientServer = KoResourceServerProvider::instance()->gradientServer(false); patternServer = KoResourceServerProvider::instance()->patternServer(false); paletteServer = KoResourceServerProvider::instance()->paletteServer(false); workspaceServer = KisResourceServerProvider::instance()->workspaceServer(false); } KisBrushResourceServer* brushServer; KisPaintOpPresetResourceServer * paintopServer; KoResourceServer* gradientServer; KoResourceServer *patternServer; KoResourceServer* paletteServer; KoResourceServer* workspaceServer; }; K_PLUGIN_FACTORY_WITH_JSON(ResourceManagerFactory, "kritaresourcemanager.json", registerPlugin();) ResourceManager::ResourceManager(QObject *parent, const QVariantList &) : KisViewPlugin(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(); if (settings->hasProperty("requiredBrushFile")) { QString brushFile = settings->getString("requiredBrushFile"); 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()); } newBundle->addMeta("fileName", bundlePath); newBundle->addMeta("created", QDate::currentDate().toString("dd/MM/yyyy")); if (!newBundle->save()) { QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("Could not create the new bundle.")); } else { newBundle->setValid(true); if (QDir(KisResourceServerProvider::instance()->resourceBundleServer()->saveLocation()) != QDir(QFileInfo(bundlePath).path())) { newBundle->setFilename(KisResourceServerProvider::instance()->resourceBundleServer()->saveLocation() + "/" + dlgCreateBundle.bundleName() + ".bundle"); } if (KisResourceServerProvider::instance()->resourceBundleServer()->resourceByName(newBundle->name())) { KisResourceServerProvider::instance()->resourceBundleServer()->removeResourceFromServer( KisResourceServerProvider::instance()->resourceBundleServer()->resourceByName(newBundle->name())); } KisResourceServerProvider::instance()->resourceBundleServer()->addResource(newBundle, true); newBundle->load(); } return newBundle; } void ResourceManager::slotManageBundles() { DlgBundleManager* dlg = new DlgBundleManager(this, m_view->actionManager()); if (dlg->exec() != QDialog::Accepted) { return; } } QStringList ResourceManager::importResources(const QString &title, const QStringList &mimes) const { KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::OpenFiles, "krita_resources"); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + 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" << "applicaition/x-karbon-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 = KisResourceServerProvider::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.").arg(res)); } } else { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Could not load bundle %1.").arg(res)); } QFileInfo fi(res); QString newFilename = KisResourceServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + bundle->defaultFileExtension(); QFileInfo fileInfo(newFilename); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(KisResourceServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + QString("%1").arg(i) + bundle->defaultFileExtension()); i++; } bundle->setFilename(fileInfo.filePath()); QFile::copy(res, newFilename); KisResourceServerProvider::instance()->resourceBundleServer()->addResource(bundle, false); } } void ResourceManager::slotImportPatterns() { QStringList resources = importResources(i18n("Import Patterns"), QStringList() << "image/png" << "image/svg+xml" << "application/x-gimp-pattern" << "image/jpeg" << "image/tiff" << "image/bmp" << "image/xpg"); Q_FOREACH (const QString &res, resources) { d->patternServer->importResourceFile(res); } } void ResourceManager::slotImportPalettes() { QStringList resources = importResources(i18n("Import Palettes"), QStringList() << "image/x-gimp-color-palette"); Q_FOREACH (const QString &res, resources) { d->paletteServer->importResourceFile(res); } } void ResourceManager::slotImportWorkspaces() { QStringList resources = importResources(i18n("Import Workspaces"), QStringList() << "application/x-krita-workspace"); Q_FOREACH (const QString &res, resources) { d->workspaceServer->importResourceFile(res); } } #include "resourcemanager.moc" diff --git a/plugins/extensions/separate_channels/kis_channel_separator.cc b/plugins/extensions/separate_channels/kis_channel_separator.cc index 32859b9ead..e5b88d88d0 100644 --- a/plugins/extensions/separate_channels/kis_channel_separator.cc +++ b/plugins/extensions/separate_channels/kis_channel_separator.cc @@ -1,272 +1,272 @@ /* * This file is part of Krita * * Copyright (c) 2005 Michael Thaler * * ported from Gimp, Copyright (C) 1997 Eiichi Takamori * original pixelize.c for GIMP 0.54 by Tracy Scott * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_channel_separator.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 "kis_iterator_ng.h" #include #include #include #include #include #include KisChannelSeparator::KisChannelSeparator(KisViewManager * view) : m_view(view) { } void KisChannelSeparator::separate(KoUpdater * progressUpdater, enumSepAlphaOptions alphaOps, enumSepSource sourceOps, enumSepOutput outputOps, bool downscale, bool toColor) { KisImageSP image = m_view->image(); if (!image) return; KisPaintDeviceSP src; // Use the flattened image, if required switch (sourceOps) { case ALL_LAYERS: // the content will be locked later src = image->projection(); break; case CURRENT_LAYER: src = m_view->activeDevice(); break; default: break; } if (!src) return; progressUpdater->setProgress(1); const KoColorSpace * dstCs = 0; quint32 numberOfChannels = src->channelCount(); const KoColorSpace * srcCs = src->colorSpace(); QList channels = srcCs->channels(); vKisPaintDeviceSP layers; QList::const_iterator begin = channels.constBegin(); QList::const_iterator end = channels.constEnd(); QRect rect = src->exactBounds(); image->lock(); int i = 0; for (QList::const_iterator it = begin; it != end; ++it) { KoChannelInfo * ch = (*it); if (ch->channelType() == KoChannelInfo::ALPHA && alphaOps != CREATE_ALPHA_SEPARATION) { continue; } qint32 channelSize = ch->size(); qint32 channelPos = ch->pos(); qint32 destSize = 1; KisPaintDeviceSP dev; if (toColor) { // We don't downscale if we separate to color channels dev = new KisPaintDevice(srcCs); } else { if (channelSize == 1 || downscale) { dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), 0)); } else { dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), 0)); destSize = 2; } } dstCs = dev->colorSpace(); layers.push_back(dev); KisHLineConstIteratorSP srcIt = src->createHLineConstIteratorNG(rect.x(), rect.y(), rect.width()); KisHLineIteratorSP dstIt = dev->createHLineIteratorNG(rect.x(), rect.y(), rect.width()); for (qint32 row = 0; row < rect.height(); ++row) { do { if (toColor) { dstCs->singleChannelPixel(dstIt->rawData(), srcIt->oldRawData(), channelPos); if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) { //dstCs->setAlpha(dstIt->rawData(), srcIt->oldRawData()[srcAlphaPos], 1); dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1); } else { dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); } } else { // To grayscale // Decide whether we need downscaling if (channelSize == 1 && destSize == 1) { // Both 8-bit channels dstIt->rawData()[0] = srcIt->oldRawData()[channelPos]; if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) { dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1); } else { dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); } } else if (channelSize == 2 && destSize == 2) { // Both 16-bit dstIt->rawData()[0] = srcIt->oldRawData()[channelPos]; dstIt->rawData()[1] = srcIt->oldRawData()[channelPos + 1]; if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) { dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1); } else { dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); } } else if (channelSize != 1 && destSize == 1) { // Downscale memset(dstIt->rawData(), srcCs->scaleToU8(srcIt->oldRawData(), channelPos), 1); // XXX: Do alpha dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); } else if (channelSize != 2 && destSize == 2) { // Upscale dstIt->rawData()[0] = srcCs->scaleToU8(srcIt->oldRawData(), channelPos); // XXX: Do alpha dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1); } } } while (dstIt->nextPixel() && srcIt->nextPixel()); dstIt->nextRow(); srcIt->nextRow(); } ++i; progressUpdater->setProgress((i * 100) / numberOfChannels); if (progressUpdater->interrupted()) { break; } } vKisPaintDeviceSP_it deviceIt = layers.begin(); progressUpdater->setProgress(100); if (!progressUpdater->interrupted()) { KisUndoAdapter * undo = image->undoAdapter(); if (outputOps == TO_LAYERS) { undo->beginMacro(kundo2_i18n("Separate Image")); } // Flatten the image if required switch (sourceOps) { case(ALL_LAYERS): image->flatten(); break; default: break; } KisNodeCommandsAdapter adapter(m_view); for (QList::const_iterator it = begin; it != end; ++it) { KoChannelInfo * ch = (*it); if (ch->channelType() == KoChannelInfo::ALPHA && alphaOps != CREATE_ALPHA_SEPARATION) { // Don't make an separate separation of the alpha channel if the user didn't ask for it. continue; } if (outputOps == TO_LAYERS) { KisPaintLayerSP l = KisPaintLayerSP(new KisPaintLayer(image.data(), ch->name(), OPACITY_OPAQUE_U8, *deviceIt)); adapter.addNode(l.data(), image->rootLayer(), 0); } else { KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::SaveFile, "OpenDocument"); dialog.setCaption(i18n("Export Layer") + '(' + ch->name() + ')'); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Export)); QUrl url = QUrl::fromUserInput(dialog.filename()); if (url.isEmpty()) return; const QString mimeType = KisMimeDatabase::mimeTypeForFile(url.toLocalFile()); KisPaintLayerSP l = KisPaintLayerSP(new KisPaintLayer(image.data(), ch->name(), OPACITY_OPAQUE_U8, *deviceIt)); QRect r = l->exactBounds(); KisDocument *d = KisPart::instance()->createDocument(); KisImageWSP dst = KisImageWSP(new KisImage(d->createUndoStore(), r.width(), r.height(), (*deviceIt)->colorSpace(), l->name())); d->setCurrentImage(dst); dst->addNode(l->clone().data(), dst->rootLayer()); d->exportDocumentSync(url, mimeType.toLatin1()); delete d; } ++deviceIt; } if (outputOps == TO_LAYERS) { undo->endMacro(); } image->unlock(); image->setModified(); } } diff --git a/plugins/filters/colorsfilters/kis_brightness_contrast_filter.cpp b/plugins/filters/colorsfilters/kis_brightness_contrast_filter.cpp index d6c6d04a1a..c8f9da8d15 100644 --- a/plugins/filters/colorsfilters/kis_brightness_contrast_filter.cpp +++ b/plugins/filters/colorsfilters/kis_brightness_contrast_filter.cpp @@ -1,302 +1,302 @@ /* * This file is part of Krita * * Copyright (c) 2004 Cyrille Berger * Copyright (c) 2005 C. Boemann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * 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_brightness_contrast_filter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "KoBasicHistogramProducers.h" #include "KoColorSpace.h" #include "KoColorTransformation.h" #include "KoCompositeOp.h" #include #include "kis_config_widget.h" #include "kis_bookmarked_configuration_manager.h" #include "kis_paint_device.h" #include "widgets/kis_curve_widget.h" #include "kis_histogram.h" #include "kis_painter.h" #include #include #include KisBrightnessContrastFilterConfiguration::KisBrightnessContrastFilterConfiguration() : KisColorTransformationConfiguration("brightnesscontrast", 1) { } KisBrightnessContrastFilterConfiguration::~KisBrightnessContrastFilterConfiguration() { } void KisBrightnessContrastFilterConfiguration::fromLegacyXML(const QDomElement& root) { fromXML(root); } void KisBrightnessContrastFilterConfiguration::updateTransfer() { m_transfer = m_curve.uint16Transfer(); } void KisBrightnessContrastFilterConfiguration::setCurve(const KisCubicCurve &curve) { m_curve = curve; updateTransfer(); } const QVector& KisBrightnessContrastFilterConfiguration::transfer() const { return m_transfer; } const KisCubicCurve& KisBrightnessContrastFilterConfiguration::curve() const { return m_curve; } void KisBrightnessContrastFilterConfiguration::fromXML(const QDomElement& root) { KisCubicCurve curve; int version; version = root.attribute("version").toInt(); QDomElement e = root.firstChild().toElement(); QString attributeName; while (!e.isNull()) { if ((attributeName = e.attribute("name")) != "nTransfers") { QRegExp rx("curve(\\d+)"); if (rx.indexIn(attributeName, 0) != -1) { quint16 index = rx.cap(1).toUShort(); if (index == 0 && !e.text().isEmpty()) { /** * We are going to use first curve only */ curve.fromString(e.text()); } } } e = e.nextSiblingElement(); } setVersion(version); setCurve(curve); } /** * Inherited from KisPropertiesConfiguration */ //void KisPerChannelFilterConfiguration::fromXML(const QString& s) void KisBrightnessContrastFilterConfiguration::toXML(QDomDocument& doc, QDomElement& root) const { /** * * 1 * 0,0;0.5,0.5;1,1; * */ /* This is a constant for Brightness/Contranst filter */ const qint32 numTransfers = 1; root.setAttribute("version", version()); QDomElement t = doc.createElement("param"); QDomText text = doc.createTextNode(QString::number(numTransfers)); t.setAttribute("name", "nTransfers"); t.appendChild(text); root.appendChild(t); t = doc.createElement("param"); t.setAttribute("name", "curve0"); text = doc.createTextNode(m_curve.toString()); t.appendChild(text); root.appendChild(t); } /** * Inherited from KisPropertiesConfiguration */ //QString KisPerChannelFilterConfiguration::toXML() KisBrightnessContrastFilter::KisBrightnessContrastFilter() : KisColorTransformationFilter(id(), categoryAdjust(), i18n("&Brightness/Contrast curve...")) { setSupportsPainting(false); setColorSpaceIndependence(TO_LAB16); } KisConfigWidget * KisBrightnessContrastFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev) const { return new KisBrightnessContrastConfigWidget(parent, dev); } KisFilterConfigurationSP KisBrightnessContrastFilter::factoryConfiguration() const { return new KisBrightnessContrastFilterConfiguration(); } KoColorTransformation* KisBrightnessContrastFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const { const KisBrightnessContrastFilterConfiguration* configBC = dynamic_cast(config.data()); if (!configBC) return 0; KoColorTransformation * adjustment = cs->createBrightnessContrastAdjustment(configBC->transfer().constData()); return adjustment; } -KisBrightnessContrastConfigWidget::KisBrightnessContrastConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WFlags f) +KisBrightnessContrastConfigWidget::KisBrightnessContrastConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f) : KisConfigWidget(parent, f) { int i; int height; m_page = new WdgBrightnessContrast(this); QHBoxLayout * l = new QHBoxLayout(this); Q_CHECK_PTR(l); //Hide these buttons and labels as they are not implemented in 1.5 m_page->pb_more_contrast->hide(); m_page->pb_less_contrast->hide(); m_page->pb_more_brightness->hide(); m_page->pb_less_brightness->hide(); m_page->textLabelBrightness->hide(); m_page->textLabelContrast->hide(); l->addWidget(m_page, 1, Qt::AlignTop); l->setContentsMargins(0,0,0,0); height = 256; connect(m_page->curveWidget, SIGNAL(modified()), SIGNAL(sigConfigurationItemChanged())); // Create the horizontal gradient label QPixmap hgradientpix(256, 1); QPainter hgp(&hgradientpix); hgp.setPen(QPen(QColor(0, 0, 0), 1, Qt::SolidLine)); for (i = 0; i < 256; ++i) { hgp.setPen(QColor(i, i, i)); hgp.drawPoint(i, 0); } m_page->hgradient->setPixmap(hgradientpix); // Create the vertical gradient label QPixmap vgradientpix(1, 256); QPainter vgp(&vgradientpix); vgp.setPen(QPen(QColor(0, 0, 0), 1, Qt::SolidLine)); for (i = 0; i < 256; ++i) { vgp.setPen(QColor(i, i, i)); vgp.drawPoint(0, 255 - i); } m_page->vgradient->setPixmap(vgradientpix); KoHistogramProducer *producer = new KoGenericLabHistogramProducer(); KisHistogram histogram(dev, dev->exactBounds(), producer, LINEAR); QPalette appPalette = QApplication::palette(); QPixmap pix(256, height); pix.fill(QColor(appPalette.color(QPalette::Base))); QPainter p(&pix); p.setPen(QPen(Qt::gray, 1, Qt::SolidLine)); double highest = (double)histogram.calculations().getHighest(); qint32 bins = histogram.producer()->numberOfBins(); if (histogram.getHistogramType() == LINEAR) { double factor = (double)height / highest; for (i = 0; i < bins; ++i) { p.drawLine(i, height, i, height - int(histogram.getValue(i) * factor)); } } else { double factor = (double)height / (double)log(highest); for (i = 0; i < bins; ++i) { p.drawLine(i, height, i, height - int(log((double)histogram.getValue(i)) * factor)); } } m_page->curveWidget->setPixmap(pix); m_page->curveWidget->setBasePixmap(pix); } KisBrightnessContrastConfigWidget::~KisBrightnessContrastConfigWidget() { KoToolManager::instance()->switchBackRequested(); delete m_page; } KisPropertiesConfigurationSP KisBrightnessContrastConfigWidget::configuration() const { KisBrightnessContrastFilterConfiguration * cfg = new KisBrightnessContrastFilterConfiguration(); cfg->setCurve(m_page->curveWidget->curve()); return cfg; } void KisBrightnessContrastConfigWidget::slotDrawLine(const KoColor &color) { QColor colorNew = color.toQColor(); int i = (colorNew.red() + colorNew.green() + colorNew.blue())/3 ; QPixmap pix = m_page->curveWidget->getBasePixmap(); QPainter p(&pix); p.setPen(QPen(Qt::black, 1, Qt::SolidLine)); p.drawLine(i,0,i,255); QString label = "x:"; label.insert(2,QString(QString::number(i))); p.drawText(i,250,label); m_page->curveWidget->setPixmap(pix); } void KisBrightnessContrastConfigWidget::setView(KisViewManager *view) { connect(view->resourceProvider(), SIGNAL(sigFGColorChanged(const KoColor&)), this, SLOT(slotDrawLine(const KoColor&))); KoToolManager::instance()->switchToolTemporaryRequested("KritaSelected/KisToolColorPicker"); } void KisBrightnessContrastConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config) { const KisBrightnessContrastFilterConfiguration * cfg = dynamic_cast(config.data()); Q_ASSERT(cfg); m_page->curveWidget->setCurve(cfg->curve()); } diff --git a/plugins/filters/colorsfilters/kis_brightness_contrast_filter.h b/plugins/filters/colorsfilters/kis_brightness_contrast_filter.h index b92dddac7c..955d9cfc7c 100644 --- a/plugins/filters/colorsfilters/kis_brightness_contrast_filter.h +++ b/plugins/filters/colorsfilters/kis_brightness_contrast_filter.h @@ -1,116 +1,116 @@ /* * This file is part of Krita * * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * 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_BRIGHTNESS_CONTRAST_FILTER_H_ #define _KIS_BRIGHTNESS_CONTRAST_FILTER_H_ #include #include "filter/kis_color_transformation_filter.h" #include "kis_config_widget.h" #include "ui_wdg_brightness_contrast.h" #include #include #include #include #include class QWidget; class KoColorTransformation; class WdgBrightnessContrast : public QWidget, public Ui::WdgBrightnessContrast { Q_OBJECT public: WdgBrightnessContrast(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class KisBrightnessContrastFilterConfiguration : public KisColorTransformationConfiguration { public: using KisFilterConfiguration::fromXML; using KisFilterConfiguration::toXML; using KisFilterConfiguration::fromLegacyXML; void fromLegacyXML(const QDomElement& root) override; void fromXML(const QDomElement& e) override; void toXML(QDomDocument& doc, QDomElement& root) const override; KisBrightnessContrastFilterConfiguration(); ~KisBrightnessContrastFilterConfiguration() override; void setCurve(const KisCubicCurve &curve) override; const QVector& transfer() const; const KisCubicCurve& curve() const override; private: void updateTransfer(); private: KisCubicCurve m_curve; QVector m_transfer; }; /** * This class affect Intensity Y of the image */ class KisBrightnessContrastFilter : public KisColorTransformationFilter { public: KisBrightnessContrastFilter(); public: KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const override; static inline KoID id() { return KoID("brightnesscontrast", i18n("Brightness / Contrast")); } KisFilterConfigurationSP factoryConfiguration() const override; KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev) const override; }; class KisBrightnessContrastConfigWidget : public KisConfigWidget { Q_OBJECT public: - KisBrightnessContrastConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WFlags f = 0); + KisBrightnessContrastConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f = 0); ~KisBrightnessContrastConfigWidget() override; KisPropertiesConfigurationSP configuration() const override; void setConfiguration(const KisPropertiesConfigurationSP config) override; WdgBrightnessContrast * m_page; void setView(KisViewManager *view) override; public Q_SLOTS: void slotDrawLine(const KoColor &color); }; #endif diff --git a/plugins/filters/colorsfilters/kis_desaturate_filter.cpp b/plugins/filters/colorsfilters/kis_desaturate_filter.cpp index 30a48acdbf..6e3821fbae 100644 --- a/plugins/filters/colorsfilters/kis_desaturate_filter.cpp +++ b/plugins/filters/colorsfilters/kis_desaturate_filter.cpp @@ -1,121 +1,121 @@ /* * This file is part of Krita * * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_desaturate_filter.h" #include #include #include #include #include #include #include #include #include #include "KoBasicHistogramProducers.h" #include #include #include #include #include #include #include #include #include #include #include #include "filter/kis_filter_registry.h" #include #include #include #include KisDesaturateFilter::KisDesaturateFilter() : KisColorTransformationFilter(id(), categoryAdjust(), i18n("&Desaturate...")) { setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_U)); setSupportsPainting(true); } KisDesaturateFilter::~KisDesaturateFilter() { } KisConfigWidget *KisDesaturateFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev) const { Q_UNUSED(dev); return new KisDesaturateConfigWidget(parent); } KoColorTransformation* KisDesaturateFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const { QHash params; if (config) { params["type"] = config->getInt("type", 0); } return cs->createColorTransformation("desaturate_adjustment", params); } KisFilterConfigurationSP KisDesaturateFilter::factoryConfiguration() const { KisColorTransformationConfigurationSP config = new KisColorTransformationConfiguration(id().id(), 1); config->setProperty("type", 0); return config; } -KisDesaturateConfigWidget::KisDesaturateConfigWidget(QWidget * parent, Qt::WFlags f) : KisConfigWidget(parent, f) +KisDesaturateConfigWidget::KisDesaturateConfigWidget(QWidget * parent, Qt::WindowFlags f) : KisConfigWidget(parent, f) { m_page = new Ui_WdgDesaturate(); m_page->setupUi(this); m_group = new QButtonGroup(this); m_group->addButton(m_page->radioLightness, 0); m_group->addButton(m_page->radioLuminosityBT709, 1); m_group->addButton(m_page->radioLuminosityBT601, 2); m_group->addButton(m_page->radioAverage, 3); m_group->addButton(m_page->radioMin, 4); m_group->addButton(m_page->radioMax, 5); m_group->setExclusive(true); connect(m_group, SIGNAL(buttonClicked(int)), SIGNAL(sigConfigurationItemChanged())); } KisDesaturateConfigWidget::~KisDesaturateConfigWidget() { delete m_page; } KisPropertiesConfigurationSP KisDesaturateConfigWidget::configuration() const { KisColorTransformationConfigurationSP c = new KisColorTransformationConfiguration(KisDesaturateFilter::id().id(), 0); c->setProperty("type", m_group->checkedId()); return c; } void KisDesaturateConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config) { m_group->button(config->getInt("type", 0))->setChecked(true); emit sigConfigurationItemChanged(); } diff --git a/plugins/filters/colorsfilters/kis_desaturate_filter.h b/plugins/filters/colorsfilters/kis_desaturate_filter.h index a2e058052b..59bf312011 100644 --- a/plugins/filters/colorsfilters/kis_desaturate_filter.h +++ b/plugins/filters/colorsfilters/kis_desaturate_filter.h @@ -1,70 +1,70 @@ /* * This file is part of Krita * * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_DESATURATE_FILTER_H #define KIS_DESATURATE_FILTER_H #include #include #include #include #include "ui_wdg_desaturate.h" class KoColorSpace; class KoColorTransformation; class KisDesaturateFilter : public KisColorTransformationFilter { public: KisDesaturateFilter(); ~KisDesaturateFilter() override; public: KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev) const override; KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const override; static inline KoID id() { return KoID("desaturate", i18n("Desaturate")); } KisFilterConfigurationSP factoryConfiguration() const override; }; class KisDesaturateConfigWidget : public KisConfigWidget { Q_OBJECT public: - KisDesaturateConfigWidget(QWidget * parent, Qt::WFlags f = 0); + KisDesaturateConfigWidget(QWidget * parent, Qt::WindowFlags f = 0); ~KisDesaturateConfigWidget() override; KisPropertiesConfigurationSP configuration() const override; void setConfiguration(const KisPropertiesConfigurationSP config) override; Ui_WdgDesaturate *m_page; QButtonGroup *m_group; }; #endif diff --git a/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.cpp b/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.cpp index f197ce3eb9..44277ed32c 100644 --- a/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.cpp +++ b/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.cpp @@ -1,207 +1,207 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; version 2 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_hsv_adjustment_filter.h" #include #include #include #include #include namespace { struct SliderConfig { QString m_text; int m_minimum; int m_maximum; inline void apply(QSpinBox* spinBox, QSlider* slider, QLabel* label) const { label->setText(m_text); slider->setMinimum(m_minimum); slider->setMaximum(m_maximum); spinBox->setMinimum(m_minimum); spinBox->setMaximum(m_maximum); int sliderValue = slider->value(); if (sliderValue < m_minimum || sliderValue > m_maximum) { slider->setValue((m_minimum + m_maximum) / 2); } } inline double normalize(int value) const { return (double)value / (double)m_maximum; } inline void resetSlider( QSlider* slider) const { slider->setValue(0); } }; struct WidgetSlidersConfig { SliderConfig m_sliders[3]; }; #define PERCENT_FIELD_REL(x) {x, -100, 100} #define PERCENT_FIELD_ABS(x) {x, 0, 100} #define DEGREES_FIELD_REL(x) {x, -180, 180} #define DEGREES_FIELD_ABS(x) {x, 0, 360} #define HSX_CONFIGS(x) { \ { {DEGREES_FIELD_REL(i18n("Hue:")), PERCENT_FIELD_REL(i18n("Saturation:")), PERCENT_FIELD_REL(x)} }, \ { {DEGREES_FIELD_ABS(i18n("Hue:")), PERCENT_FIELD_ABS(i18n("Saturation:")), PERCENT_FIELD_REL(x)} } \ } const WidgetSlidersConfig WIDGET_CONFIGS[][2] = { // Hue/Saturation/Value HSX_CONFIGS(i18n("Value:")), // Hue/Saturation/Lightness HSX_CONFIGS(i18n("Lightness:")), // Hue/Saturation/Intensity HSX_CONFIGS(i18n("Intensity:")), // Hue/Saturation/Luminosity HSX_CONFIGS(i18n("Luma:")), // Blue Chroma/Red Chroma/Luma {{ {PERCENT_FIELD_REL(i18n("Yellow-Blue:")), PERCENT_FIELD_REL(i18n("Green-Red:")), PERCENT_FIELD_REL(i18n("Luma:"))} }, { {PERCENT_FIELD_ABS(i18n("Yellow-Blue:")), PERCENT_FIELD_ABS(i18n("Green-Red:")), PERCENT_FIELD_REL(i18n("Luma:"))} }} }; inline const WidgetSlidersConfig& getCurrentWidgetConfig(int type, bool colorize) { return WIDGET_CONFIGS[type][colorize ? 1 : 0]; } } KisHSVAdjustmentFilter::KisHSVAdjustmentFilter() : KisColorTransformationFilter(id(), categoryAdjust(), i18n("&HSV Adjustment...")) { setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U)); setSupportsPainting(true); } KisConfigWidget * KisHSVAdjustmentFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev) const { Q_UNUSED(dev); return new KisHSVConfigWidget(parent); } KoColorTransformation* KisHSVAdjustmentFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const { QHash params; if (config) { int type = config->getInt("type", 1); bool colorize = config->getBool("colorize", false); const WidgetSlidersConfig& widgetConfig = getCurrentWidgetConfig(type, colorize); params["h"] = widgetConfig.m_sliders[0].normalize(config->getInt("h", 0)); params["s"] = widgetConfig.m_sliders[1].normalize(config->getInt("s", 0)); params["v"] = widgetConfig.m_sliders[2].normalize(config->getInt("v", 0)); params["type"] = type; params["colorize"] = colorize; params["lumaRed"] = cs->lumaCoefficients()[0]; params["lumaGreen"] = cs->lumaCoefficients()[1]; params["lumaBlue"] = cs->lumaCoefficients()[2]; } return cs->createColorTransformation("hsv_adjustment", params); } KisFilterConfigurationSP KisHSVAdjustmentFilter::factoryConfiguration() const { KisColorTransformationConfigurationSP config = new KisColorTransformationConfiguration(id().id(), 1); config->setProperty("h", 0); config->setProperty("s", 0); config->setProperty("v", 0); config->setProperty("type", 1); config->setProperty("colorize", false); return config; } -KisHSVConfigWidget::KisHSVConfigWidget(QWidget * parent, Qt::WFlags f) : KisConfigWidget(parent, f) +KisHSVConfigWidget::KisHSVConfigWidget(QWidget * parent, Qt::WindowFlags f) : KisConfigWidget(parent, f) { m_page = new Ui_WdgHSVAdjustment(); m_page->setupUi(this); connect(m_page->cmbType, SIGNAL(activated(int)), this, SLOT(configureSliderLimitsAndLabels())); connect(m_page->chkColorize, SIGNAL(toggled(bool)), this, SLOT(configureSliderLimitsAndLabels())); connect(m_page->reset,SIGNAL(clicked(bool)),this,SLOT(resetFilter())); // connect horizontal sliders connect(m_page->hueSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->saturationSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->valueSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->hueSpinBox, SIGNAL(valueChanged(int)), m_page->hueSlider, SLOT(setValue(int))); connect(m_page->saturationSpinBox, SIGNAL(valueChanged(int)), m_page->saturationSlider, SLOT(setValue(int))); connect(m_page->valueSpinBox, SIGNAL(valueChanged(int)), m_page->valueSlider, SLOT(setValue(int))); connect(m_page->hueSlider, SIGNAL(valueChanged(int)), m_page->hueSpinBox, SLOT(setValue(int))); connect(m_page->saturationSlider, SIGNAL(valueChanged(int)), m_page->saturationSpinBox, SLOT(setValue(int))); connect(m_page->valueSlider, SIGNAL(valueChanged(int)), m_page->valueSpinBox, SLOT(setValue(int))); } KisHSVConfigWidget::~KisHSVConfigWidget() { delete m_page; } KisPropertiesConfigurationSP KisHSVConfigWidget::configuration() const { KisColorTransformationConfigurationSP c = new KisColorTransformationConfiguration(KisHSVAdjustmentFilter::id().id(), 0); c->setProperty("h", m_page->hueSlider->value()); c->setProperty("s", m_page->saturationSlider->value()); c->setProperty("v", m_page->valueSlider->value()); c->setProperty("type", m_page->cmbType->currentIndex()); c->setProperty("colorize", m_page->chkColorize->isChecked()); return c; } void KisHSVConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config) { m_page->cmbType->setCurrentIndex(config->getInt("type", 1)); m_page->chkColorize->setChecked(config->getBool("colorize", false)); m_page->hueSlider->setValue(config->getInt("h", 0)); m_page->saturationSlider->setValue(config->getInt("s", 0)); m_page->valueSlider->setValue(config->getInt("v", 0)); configureSliderLimitsAndLabels(); } void KisHSVConfigWidget::configureSliderLimitsAndLabels() { const WidgetSlidersConfig& widget = getCurrentWidgetConfig(m_page->cmbType->currentIndex(), m_page->chkColorize->isChecked()); widget.m_sliders[0].apply(m_page->hueSpinBox, m_page->hueSlider, m_page->label); widget.m_sliders[1].apply(m_page->saturationSpinBox, m_page->saturationSlider, m_page->label_2); widget.m_sliders[2].apply(m_page->valueSpinBox, m_page->valueSlider, m_page->label_3); emit sigConfigurationItemChanged(); } void KisHSVConfigWidget::resetFilter() { const WidgetSlidersConfig& widget = getCurrentWidgetConfig(m_page->cmbType->currentIndex(), m_page->chkColorize->isChecked()); widget.m_sliders[0].resetSlider(m_page->hueSlider); widget.m_sliders[1].resetSlider(m_page->saturationSlider); widget.m_sliders[2].resetSlider(m_page->valueSlider); } diff --git a/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.h b/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.h index 3afb7c14e2..2e007d42df 100644 --- a/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.h +++ b/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.h @@ -1,78 +1,78 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; version 2 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KIS_HSV_ADJUSTMENT_FILTER_H_ #define _KIS_HSV_ADJUSTMENT_FILTER_H_ #include #include "filter/kis_filter.h" #include "kis_config_widget.h" #include "ui_wdg_hsv_adjustment.h" #include "filter/kis_color_transformation_filter.h" class QWidget; class KoColorTransformation; /** * This class affect Intensity Y of the image */ class KisHSVAdjustmentFilter : public KisColorTransformationFilter { public: KisHSVAdjustmentFilter(); public: KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev) const override; KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const override; static inline KoID id() { return KoID("hsvadjustment", i18n("HSV/HSL Adjustment")); } KisFilterConfigurationSP factoryConfiguration() const override; }; class KisHSVConfigWidget : public KisConfigWidget { Q_OBJECT public: - KisHSVConfigWidget(QWidget * parent, Qt::WFlags f = 0); + KisHSVConfigWidget(QWidget * parent, Qt::WindowFlags f = 0); ~KisHSVConfigWidget() override; KisPropertiesConfigurationSP configuration() const override; void setConfiguration(const KisPropertiesConfigurationSP config) override; Ui_WdgHSVAdjustment * m_page; private Q_SLOTS: void configureSliderLimitsAndLabels(); void resetFilter(); }; #endif diff --git a/plugins/filters/colorsfilters/kis_perchannel_filter.cpp b/plugins/filters/colorsfilters/kis_perchannel_filter.cpp index dc31bfd439..a27240ffc1 100644 --- a/plugins/filters/colorsfilters/kis_perchannel_filter.cpp +++ b/plugins/filters/colorsfilters/kis_perchannel_filter.cpp @@ -1,613 +1,613 @@ /* * This file is part of Krita * * Copyright (c) 2005 C. Boemann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_perchannel_filter.h" #include #include #include #include #include #include #include #include #include "KoChannelInfo.h" #include "KoBasicHistogramProducers.h" #include "KoColorModelStandardIds.h" #include "KoColorSpace.h" #include "KoColorTransformation.h" #include "KoCompositeColorTransformation.h" #include "KoCompositeOp.h" #include "KoID.h" #include "kis_signals_blocker.h" #include "kis_bookmarked_configuration_manager.h" #include "kis_config_widget.h" #include #include #include #include #include "kis_histogram.h" #include "kis_painter.h" #include "widgets/kis_curve_widget.h" QVector getVirtualChannels(const KoColorSpace *cs) { const bool supportsLightness = cs->colorModelId() != LABAColorModelID && cs->colorModelId() != GrayAColorModelID && cs->colorModelId() != GrayColorModelID && cs->colorModelId() != AlphaColorModelID; QVector vchannels; QList sortedChannels = KoChannelInfo::displayOrderSorted(cs->channels()); if (supportsLightness) { vchannels << VirtualChannelInfo(VirtualChannelInfo::ALL_COLORS, -1, 0, cs); } Q_FOREACH (KoChannelInfo *channel, sortedChannels) { int pixelIndex = KoChannelInfo::displayPositionToChannelIndex(channel->displayPosition(), sortedChannels); vchannels << VirtualChannelInfo(VirtualChannelInfo::REAL, pixelIndex, channel, cs); } if (supportsLightness) { vchannels << VirtualChannelInfo(VirtualChannelInfo::LIGHTNESS, -1, 0, cs); } return vchannels; } -KisPerChannelConfigWidget::KisPerChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WFlags f) +KisPerChannelConfigWidget::KisPerChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f) : KisConfigWidget(parent, f), m_histogram(0) { Q_ASSERT(dev); m_page = new WdgPerChannel(this); QHBoxLayout * layout = new QHBoxLayout(this); Q_CHECK_PTR(layout); layout->setContentsMargins(0,0,0,0); layout->addWidget(m_page); m_dev = dev; m_activeVChannel = 0; const KoColorSpace *targetColorSpace = dev->compositionSourceColorSpace(); // fill in the channel chooser, in the display order, but store the pixel index as well. m_virtualChannels = getVirtualChannels(targetColorSpace); const int virtualChannelCount = m_virtualChannels.size(); KisPerChannelFilterConfiguration::initDefaultCurves(m_curves, virtualChannelCount); for (int i = 0; i < virtualChannelCount; i++) { const VirtualChannelInfo &info = m_virtualChannels[i]; m_page->cmbChannel->addItem(info.name(), info.pixelIndex()); m_curves[i].setName(info.name()); } connect(m_page->cmbChannel, SIGNAL(activated(int)), this, SLOT(setActiveChannel(int))); connect((QObject*)(m_page->chkLogarithmic), SIGNAL(toggled(bool)), this, SLOT(logHistView())); // create the horizontal and vertical gradient labels m_page->hgradient->setPixmap(createGradient(Qt::Horizontal)); m_page->vgradient->setPixmap(createGradient(Qt::Vertical)); // init histogram calculator QList keys = KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(targetColorSpace); if(keys.size() > 0) { KoHistogramProducerFactory *hpf; hpf = KoHistogramProducerFactoryRegistry::instance()->get(keys.at(0)); m_histogram = new KisHistogram(m_dev, m_dev->exactBounds(), hpf->generate(), LINEAR); } connect(m_page->curveWidget, SIGNAL(modified()), this, SIGNAL(sigConfigurationItemChanged())); m_page->curveWidget->setupInOutControls(m_page->intIn, m_page->intOut, 0, 100); { KisSignalsBlocker b(m_page->curveWidget); m_page->curveWidget->setCurve(m_curves[0]); setActiveChannel(0); } } KisPerChannelConfigWidget::~KisPerChannelConfigWidget() { delete m_histogram; } inline QPixmap KisPerChannelConfigWidget::createGradient(Qt::Orientation orient /*, int invert (not used yet) */) { int width; int height; int *i, inc, col; int x = 0, y = 0; if (orient == Qt::Horizontal) { i = &x; inc = 1; col = 0; width = 256; height = 1; } else { i = &y; inc = -1; col = 255; width = 1; height = 256; } QPixmap gradientpix(width, height); QPainter p(&gradientpix); p.setPen(QPen(QColor(0, 0, 0), 1, Qt::SolidLine)); for (; *i < 256; (*i)++, col += inc) { p.setPen(QColor(col, col, col)); p.drawPoint(x, y); } return gradientpix; } inline QPixmap KisPerChannelConfigWidget::getHistogram() { int i; int height = 256; QPixmap pix(256, height); bool logarithmic = m_page->chkLogarithmic->isChecked(); if (logarithmic) m_histogram->setHistogramType(LOGARITHMIC); else m_histogram->setHistogramType(LINEAR); QPalette appPalette = QApplication::palette(); pix.fill(QColor(appPalette.color(QPalette::Base))); QPainter p(&pix); p.setPen(QColor(appPalette.color(QPalette::Text))); p.save(); p.setOpacity(0.2); const VirtualChannelInfo &info = m_virtualChannels[m_activeVChannel]; if (m_histogram && info.type() == VirtualChannelInfo::REAL) { m_histogram->setChannel(info.pixelIndex()); double highest = (double)m_histogram->calculations().getHighest(); qint32 bins = m_histogram->producer()->numberOfBins(); if (m_histogram->getHistogramType() == LINEAR) { double factor = (double)height / highest; for (i = 0; i < bins; ++i) { p.drawLine(i, height, i, height - int(m_histogram->getValue(i) * factor)); } } else { double factor = (double)height / (double)log(highest); for (i = 0; i < bins; ++i) { p.drawLine(i, height, i, height - int(log((double)m_histogram->getValue(i)) * factor)); } } } p.restore(); return pix; } #define BITS_PER_BYTE 8 #define pwr2(p) (1<curveWidget->curve(); m_activeVChannel = ch; m_page->curveWidget->setCurve(m_curves[m_activeVChannel]); m_page->curveWidget->setPixmap(getHistogram()); m_page->cmbChannel->setCurrentIndex(m_activeVChannel); // Getting range accepted by channel VirtualChannelInfo ¤tVChannel = m_virtualChannels[m_activeVChannel]; KoChannelInfo::enumChannelValueType valueType = currentVChannel.valueType(); int order = BITS_PER_BYTE * currentVChannel.channelSize(); int maxValue = pwr2(order); int min; int max; m_page->curveWidget->dropInOutControls(); switch (valueType) { case KoChannelInfo::UINT8: case KoChannelInfo::UINT16: case KoChannelInfo::UINT32: m_shift = 0; m_scale = double(maxValue); min = 0; max = maxValue - 1; break; case KoChannelInfo::INT8: case KoChannelInfo::INT16: m_shift = 0.5; m_scale = double(maxValue); min = -maxValue / 2; max = maxValue / 2 - 1; break; case KoChannelInfo::FLOAT16: case KoChannelInfo::FLOAT32: case KoChannelInfo::FLOAT64: default: m_shift = 0; m_scale = 100.0; //Hack Alert: should be changed to float min = 0; max = 100; break; } m_page->curveWidget->setupInOutControls(m_page->intIn, m_page->intOut, min, max); } KisPropertiesConfigurationSP KisPerChannelConfigWidget::configuration() const { int numChannels = m_virtualChannels.size(); KisPropertiesConfigurationSP cfg = new KisPerChannelFilterConfiguration(numChannels); KIS_ASSERT_RECOVER(m_activeVChannel < m_curves.size()) { return cfg; } m_curves[m_activeVChannel] = m_page->curveWidget->curve(); static_cast(cfg.data())->setCurves(m_curves); return cfg; } void KisPerChannelConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config) { const KisPerChannelFilterConfiguration * cfg = dynamic_cast(config.data()); if (!cfg) return; if (cfg->curves().size() == 0) { /** * HACK ALERT: our configuration factory generates * default configuration with nTransfers==0. * Catching it here. Just reset all the transfers. */ const int virtualChannelCount = m_virtualChannels.size(); KisPerChannelFilterConfiguration::initDefaultCurves(m_curves, virtualChannelCount); for (int i = 0; i < virtualChannelCount; i++) { const VirtualChannelInfo &info = m_virtualChannels[i]; m_curves[i].setName(info.name()); } } else if (cfg->curves().size() != int(m_virtualChannels.size())) { warnKrita << "WARNING: trying to load a curve with incorrect number of channels!"; warnKrita << "WARNING: expected:" << m_virtualChannels.size(); warnKrita << "WARNING: got:" << cfg->curves().size(); return; } else { for (int ch = 0; ch < cfg->curves().size(); ch++) m_curves[ch] = cfg->curves()[ch]; } // HACK: we save the previous curve in setActiveChannel, so just copy it m_page->curveWidget->setCurve(m_curves[m_activeVChannel]); setActiveChannel(0); } KisPerChannelFilterConfiguration::KisPerChannelFilterConfiguration(int nCh) : KisColorTransformationConfiguration("perchannel", 1) { initDefaultCurves(m_curves, nCh); updateTransfers(); } KisPerChannelFilterConfiguration::~KisPerChannelFilterConfiguration() { } bool KisPerChannelFilterConfiguration::isCompatible(const KisPaintDeviceSP dev) const { return (int)dev->compositionSourceColorSpace()->channelCount() == m_curves.size(); } void KisPerChannelFilterConfiguration::setCurves(QList &curves) { m_curves.clear(); m_curves = curves; updateTransfers(); } void KisPerChannelFilterConfiguration::initDefaultCurves(QList &curves, int nCh) { curves.clear(); for (int i = 0; i < nCh; i++) { curves.append(KisCubicCurve()); } } void KisPerChannelFilterConfiguration::updateTransfers() { m_transfers.resize(m_curves.size()); for (int i = 0; i < m_curves.size(); i++) { m_transfers[i] = m_curves[i].uint16Transfer(); } } const QVector >& KisPerChannelFilterConfiguration::transfers() const { return m_transfers; } const QList& KisPerChannelFilterConfiguration::curves() const { return m_curves; } void KisPerChannelFilterConfiguration::fromLegacyXML(const QDomElement& root) { fromXML(root); } void KisPerChannelFilterConfiguration::fromXML(const QDomElement& root) { QList curves; quint16 numTransfers = 0; int version; version = root.attribute("version").toInt(); QDomElement e = root.firstChild().toElement(); QString attributeName; KisCubicCurve curve; quint16 index; while (!e.isNull()) { if ((attributeName = e.attribute("name")) == "nTransfers") { numTransfers = e.text().toUShort(); } else { QRegExp rx("curve(\\d+)"); if (rx.indexIn(attributeName, 0) != -1) { index = rx.cap(1).toUShort(); index = qMin(index, quint16(curves.count())); if (!e.text().isEmpty()) { curve.fromString(e.text()); } curves.insert(index, curve); } } e = e.nextSiblingElement(); } if (!numTransfers) return; setVersion(version); setCurves(curves); } /** * Inherited from KisPropertiesConfiguration */ //void KisPerChannelFilterConfiguration::fromXML(const QString& s) void addParamNode(QDomDocument& doc, QDomElement& root, const QString &name, const QString &value) { QDomText text = doc.createTextNode(value); QDomElement t = doc.createElement("param"); t.setAttribute("name", name); t.appendChild(text); root.appendChild(t); } void KisPerChannelFilterConfiguration::toXML(QDomDocument& doc, QDomElement& root) const { /** * * 3 * 0,0;0.5,0.5;1,1; * 0,0;1,1; * 0,0;1,1; * */ root.setAttribute("version", version()); QDomText text; QDomElement t; addParamNode(doc, root, "nTransfers", QString::number(m_curves.size())); KisCubicCurve curve; QString paramName; for (int i = 0; i < m_curves.size(); ++i) { QString name = QLatin1String("curve") + QString::number(i); QString value = m_curves[i].toString(); addParamNode(doc, root, name, value); } } /** * Inherited from KisPropertiesConfiguration */ //QString KisPerChannelFilterConfiguration::toXML() KisPerChannelFilter::KisPerChannelFilter() : KisColorTransformationFilter(id(), categoryAdjust(), i18n("&Color Adjustment curves...")) { setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M)); setSupportsPainting(true); setColorSpaceIndependence(TO_LAB16); } KisConfigWidget * KisPerChannelFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev) const { return new KisPerChannelConfigWidget(parent, dev); } KisFilterConfigurationSP KisPerChannelFilter::factoryConfiguration() const { return new KisPerChannelFilterConfiguration(0); } KoColorTransformation* KisPerChannelFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const { const KisPerChannelFilterConfiguration* configBC = dynamic_cast(config.data()); // Somehow, this shouldn't happen Q_ASSERT(configBC); const QVector > &originalTransfers = configBC->transfers(); const QList &originalCurves = configBC->curves(); /** * TODO: What about the order of channels? (DK) * * Virtual channels are sorted in display order, does Lcms accepts * transforms in display order? Why on Earth it works?! Is it * documented anywhere? */ const QVector virtualChannels = getVirtualChannels(cs); if (originalTransfers.size() != int(virtualChannels.size())) { // We got an illegal number of colorchannels :( return 0; } bool colorsNull = true; bool lightnessNull = true; bool allColorsNull = true; int alphaIndexInReal = -1; QVector > realTransfers; QVector lightnessTransfer; QVector allColorsTransfer; for (int i = 0; i < virtualChannels.size(); i++) { if (virtualChannels[i].type() == VirtualChannelInfo::REAL) { realTransfers << originalTransfers[i]; if (virtualChannels[i].isAlpha()) { alphaIndexInReal = realTransfers.size() - 1; } if (colorsNull && !originalCurves[i].isNull()) { colorsNull = false; } } else if (virtualChannels[i].type() == VirtualChannelInfo::LIGHTNESS) { KIS_ASSERT_RECOVER_NOOP(lightnessTransfer.isEmpty()); lightnessTransfer = originalTransfers[i]; if (lightnessNull && !originalCurves[i].isNull()) { lightnessNull = false; } } else if (virtualChannels[i].type() == VirtualChannelInfo::ALL_COLORS) { KIS_ASSERT_RECOVER_NOOP(allColorsTransfer.isEmpty()); allColorsTransfer = originalTransfers[i]; if (allColorsNull && !originalCurves[i].isNull()) { allColorsNull = false; } } } KoColorTransformation *lightnessTransform = 0; KoColorTransformation *allColorsTransform = 0; KoColorTransformation *colorTransform = 0; if (!colorsNull) { const quint16** transfers = new const quint16*[realTransfers.size()]; for(int i = 0; i < realTransfers.size(); ++i) { transfers[i] = realTransfers[i].constData(); /** * createPerChannelAdjustment() expects alpha channel to * be the last channel in the list, so just it here */ KIS_ASSERT_RECOVER_NOOP(i != alphaIndexInReal || alphaIndexInReal == (realTransfers.size() - 1)); } colorTransform = cs->createPerChannelAdjustment(transfers); delete [] transfers; } if (!lightnessNull) { lightnessTransform = cs->createBrightnessContrastAdjustment(lightnessTransfer.constData()); } if (!allColorsNull) { const quint16** allColorsTransfers = new const quint16*[realTransfers.size()]; for(int i = 0; i < realTransfers.size(); ++i) { allColorsTransfers[i] = (i != alphaIndexInReal) ? allColorsTransfer.constData() : 0; /** * createPerChannelAdjustment() expects alpha channel to * be the last channel in the list, so just it here */ KIS_ASSERT_RECOVER_NOOP(i != alphaIndexInReal || alphaIndexInReal == (realTransfers.size() - 1)); } allColorsTransform = cs->createPerChannelAdjustment(allColorsTransfers); delete[] allColorsTransfers; } QVector allTransforms; allTransforms << colorTransform; allTransforms << allColorsTransform; allTransforms << lightnessTransform; return KoCompositeColorTransformation::createOptimizedCompositeTransform(allTransforms); } bool KisPerChannelFilter::needsTransparentPixels(const KisFilterConfigurationSP config, const KoColorSpace *cs) const { Q_UNUSED(config); return cs->colorModelId() == AlphaColorModelID; } void KisPerChannelConfigWidget::logHistView() { m_page->curveWidget->setPixmap(getHistogram()); } diff --git a/plugins/filters/colorsfilters/kis_perchannel_filter.h b/plugins/filters/colorsfilters/kis_perchannel_filter.h index 4844503100..044d618621 100644 --- a/plugins/filters/colorsfilters/kis_perchannel_filter.h +++ b/plugins/filters/colorsfilters/kis_perchannel_filter.h @@ -1,136 +1,136 @@ /* * This file is part of Krita * * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_PERCHANNEL_FILTER_H_ #define _KIS_PERCHANNEL_FILTER_H_ #include #include #include #include #include #include #include "ui_wdg_perchannel.h" #include "virtual_channel_info.h" class WdgPerChannel : public QWidget, public Ui::WdgPerChannel { Q_OBJECT public: WdgPerChannel(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class KisPerChannelFilterConfiguration : public KisColorTransformationConfiguration { public: KisPerChannelFilterConfiguration(int n); ~KisPerChannelFilterConfiguration() override; using KisFilterConfiguration::fromXML; using KisFilterConfiguration::toXML; using KisFilterConfiguration::fromLegacyXML; void fromLegacyXML(const QDomElement& root) override; void fromXML(const QDomElement& e) override; void toXML(QDomDocument& doc, QDomElement& root) const override; void setCurves(QList &curves) override; static inline void initDefaultCurves(QList &curves, int nCh); bool isCompatible(const KisPaintDeviceSP) const override; const QVector >& transfers() const; const QList& curves() const override; private: QList m_curves; private: void updateTransfers(); private: QVector > m_transfers; }; /** * This class is generic for filters that affect channel separately */ class KisPerChannelFilter : public KisColorTransformationFilter { public: KisPerChannelFilter(); public: KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev) const override; KisFilterConfigurationSP factoryConfiguration() const override; KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const override; bool needsTransparentPixels(const KisFilterConfigurationSP config, const KoColorSpace *cs) const override; static inline KoID id() { return KoID("perchannel", i18n("Color Adjustment")); } private: }; class KisPerChannelConfigWidget : public KisConfigWidget { Q_OBJECT public: - KisPerChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WFlags f = 0); + KisPerChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f = 0); ~KisPerChannelConfigWidget() override; void setConfiguration(const KisPropertiesConfigurationSP config) override; KisPropertiesConfigurationSP configuration() const override; private Q_SLOTS: virtual void setActiveChannel(int ch); void logHistView(); private: QVector m_virtualChannels; int m_activeVChannel; // private routines inline QPixmap getHistogram(); inline QPixmap createGradient(Qt::Orientation orient /*, int invert (not used now) */); // members WdgPerChannel * m_page; KisPaintDeviceSP m_dev; KisHistogram *m_histogram; mutable QList m_curves; // scales for displaying color numbers double m_scale; double m_shift; }; #endif diff --git a/plugins/filters/gradientmap/gradientmap.cpp b/plugins/filters/gradientmap/gradientmap.cpp index 983ccbfae4..e9e74cb7f4 100644 --- a/plugins/filters/gradientmap/gradientmap.cpp +++ b/plugins/filters/gradientmap/gradientmap.cpp @@ -1,126 +1,126 @@ /* * This file is part of the KDE project * * Copyright (c) 2016 Spencer Brown * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "QObject" #include "gradientmap.h" #include #include #include "krita_filter_gradient_map.h" #include "KoResourceServerProvider.h" #include "kis_config_widget.h" #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KritaGradientMapFactory, "kritagradientmap.json", registerPlugin();) -KritaGradientMapConfigWidget::KritaGradientMapConfigWidget(QWidget *parent, KisPaintDeviceSP dev, Qt::WFlags f) +KritaGradientMapConfigWidget::KritaGradientMapConfigWidget(QWidget *parent, KisPaintDeviceSP dev, Qt::WindowFlags f) : KisConfigWidget(parent, f) { Q_UNUSED(dev) m_page = new WdgGradientMap(this); QHBoxLayout *l = new QHBoxLayout(this); Q_CHECK_PTR(l); l->addWidget(m_page); l->setContentsMargins(0, 0, 0, 0); KoResourceServerProvider *serverProvider = KoResourceServerProvider::instance(); QSharedPointer gradientResourceAdapter( new KoResourceServerAdapter(serverProvider->gradientServer())); m_gradientChangedCompressor = new KisSignalCompressor(100, KisSignalCompressor::FIRST_ACTIVE); m_gradientPopUp = new KoResourcePopupAction(gradientResourceAdapter, m_page->btnGradientChooser); m_activeGradient = KoStopGradient::fromQGradient(dynamic_cast(gradientResourceAdapter->resources().first())->toQGradient()); m_page->gradientEditor->setGradient(m_activeGradient); m_page->gradientEditor->setCompactMode(true); m_page->gradientEditor->setEnabled(true); m_page->btnGradientChooser->setDefaultAction(m_gradientPopUp); m_page->btnGradientChooser->setPopupMode(QToolButton::InstantPopup); connect(m_gradientPopUp, SIGNAL(resourceSelected(QSharedPointer)), this, SLOT(setAbstractGradientToEditor())); connect(m_page->gradientEditor, SIGNAL(sigGradientChanged()), m_gradientChangedCompressor, SLOT(start())); connect(m_gradientChangedCompressor, SIGNAL(timeout()), this, SIGNAL(sigConfigurationItemChanged())); } KritaGradientMapConfigWidget::~KritaGradientMapConfigWidget() { delete m_page; } void KritaGradientMapConfigWidget::setAbstractGradientToEditor() { QSharedPointer bg = qSharedPointerDynamicCast( m_gradientPopUp->currentBackground()); m_activeGradient = KoStopGradient::fromQGradient(bg->gradient()); m_page->gradientEditor->setGradient(m_activeGradient); } KisPropertiesConfigurationSP KritaGradientMapConfigWidget::configuration() const { KisFilterConfigurationSP cfg = new KisFilterConfiguration("gradientmap", 2); if (m_activeGradient) { QDomDocument doc; QDomElement elt = doc.createElement("gradient"); m_activeGradient->toXML(doc, elt); doc.appendChild(elt); cfg->setProperty("gradientXML", doc.toString()); } return cfg; } //----------------------------- void KritaGradientMapConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config) { Q_ASSERT(config); QDomDocument doc; if (config->hasProperty("gradientXML")) { doc.setContent(config->getString("gradientXML", "")); qDebug()<getString("gradientXML", ""); KoStopGradient gradient = KoStopGradient::fromXML(doc.firstChildElement()); if (gradient.stops().size()>0) { m_activeGradient->setStops(gradient.stops()); } } } void KritaGradientMapConfigWidget::setView(KisViewManager *view) { Q_UNUSED(view) } //------------------------------ KritaGradientMap::KritaGradientMap(QObject *parent, const QVariantList &) : QObject(parent) { KisFilterRegistry::instance()->add(KisFilterSP(new KritaFilterGradientMap())); } KritaGradientMap::~KritaGradientMap() { } //----------------------------- #include "gradientmap.moc" diff --git a/plugins/filters/gradientmap/gradientmap.h b/plugins/filters/gradientmap/gradientmap.h index fa5fcbebdb..bcaf3eaa29 100644 --- a/plugins/filters/gradientmap/gradientmap.h +++ b/plugins/filters/gradientmap/gradientmap.h @@ -1,83 +1,83 @@ /* * This file is part of Krita * * Copyright (c) 2016 Spencer Brown * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once #include "QObject" #include "ui_wdg_gradientmap.h" #include "kis_properties_configuration.h" #include "filter/kis_color_transformation_configuration.h" #include "kis_config_widget.h" #include #include #include class WdgGradientMap : public QWidget, public Ui::WdgGradientMap { Q_OBJECT public: WdgGradientMap(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class KritaGradientMapFilterConfiguration : public KisColorTransformationConfiguration { public: KritaGradientMapFilterConfiguration(); ~KritaGradientMapFilterConfiguration() override; virtual void setGradient(const KoResource* gradient); virtual const KoResource* gradient() const; private: KoResource const* m_gradient; }; class KritaGradientMapConfigWidget : public KisConfigWidget { Q_OBJECT public: - KritaGradientMapConfigWidget(QWidget *parent, KisPaintDeviceSP dev, Qt::WFlags f = 0); + KritaGradientMapConfigWidget(QWidget *parent, KisPaintDeviceSP dev, Qt::WindowFlags f = 0); ~KritaGradientMapConfigWidget() override; KisPropertiesConfigurationSP configuration() const override; void setConfiguration(const KisPropertiesConfigurationSP config) override; WdgGradientMap *m_page; KoResourcePopupAction *m_gradientPopUp; KisSignalCompressor *m_gradientChangedCompressor; KoStopGradient *m_activeGradient; void setView(KisViewManager *view) override; private Q_SLOTS: void setAbstractGradientToEditor(); }; class KritaGradientMap : public QObject { Q_OBJECT public: KritaGradientMap(QObject *parent, const QVariantList &); ~KritaGradientMap() override; }; diff --git a/plugins/filters/indexcolors/kiswdgindexcolors.cpp b/plugins/filters/indexcolors/kiswdgindexcolors.cpp index fd2659e8aa..51ce63d45b 100644 --- a/plugins/filters/indexcolors/kiswdgindexcolors.cpp +++ b/plugins/filters/indexcolors/kiswdgindexcolors.cpp @@ -1,192 +1,192 @@ /* * Copyright 2014 Manuel Riecke * * Permission to use, copy, modify, and distribute this software * and its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and this * permission notice and warranty disclaimer appear in supporting * documentation, and that the name of the author not be used in * advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * The author disclaim all warranties with regard to this * software, including all implied warranties of merchantability * and fitness. In no event shall the author be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether * in an action of contract, negligence or other tortious action, * arising out of or in connection with the use or performance of * this software. */ #include "filter/kis_color_transformation_configuration.h" #include "kiswdgindexcolors.h" #include "palettegeneratorconfig.h" #include "ui_kiswdgindexcolors.h" #include "kis_int_parse_spin_box.h" #include -KisWdgIndexColors::KisWdgIndexColors(QWidget* parent, Qt::WFlags f, int delay): KisConfigWidget(parent, f, delay) +KisWdgIndexColors::KisWdgIndexColors(QWidget* parent, Qt::WindowFlags f, int delay): KisConfigWidget(parent, f, delay) { ui = new Ui::KisWdgIndexColors; ui->setupUi(this); connect(ui->diagCheck, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged())); connect(ui->inbetweenSpinBox, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(ui->alphaStepsSpinBox, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(ui->colorLimit, SIGNAL(valueChanged(int)), SLOT(slotColorLimitChanged(int))); connect(ui->colorLimit, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(ui->colorLimitCheck, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged())); connect(ui->luminanceSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(ui->aSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(ui->bSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); } void KisWdgIndexColors::slotColorLimitChanged(int value) { ui->colorLimit->setSuffix(i18ncp("suffix for a spinbox", " color", " colors", value)); } void KisWdgIndexColors::setup(QStringList shadesLabels, int ramps) { int rows = shadesLabels.length(); int collumns = ramps; m_colorSelectors.resize(rows); m_stepSpinners.resize(rows-1); // Labels for the shades for(int row = 0; row < rows; ++row) { QLabel* l = new QLabel(shadesLabels[row], ui->colorsBox); ui->layoutColors->addWidget(l, row+1, 0); m_colorSelectors[row].resize(collumns); } // Labels for the ramps /*for(int col = 0; col < collumns; ++col) { QLabel* l = new QLabel(rampsLabels[col], ui->colorsBox); l->setAlignment(Qt::AlignRight | Qt::AlignVCenter); ui->layoutColors->addWidget(l, 0, col+1); }*/ // Step selectors for the shade gradients for(int row = 0; row < (rows-1); ++row) { QLabel* l0 = new QLabel(shadesLabels[row+1]); QLabel* l1 = new QLabel(QString::fromUtf8("↔")); QLabel* l2 = new QLabel(shadesLabels[row]); QSpinBox* s = new KisIntParseSpinBox(); s->setMinimum(0); s->setMaximum(32); s->setValue(2); connect(s, SIGNAL(valueChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); m_stepSpinners[row] = s; ui->layoutRowSteps->addWidget(l0, row, 0); ui->layoutRowSteps->addWidget(l1, row, 1); ui->layoutRowSteps->addWidget(l2, row, 2); ui->layoutRowSteps->addWidget(s, row, 3); } // Color selectors for(int y = 0; y < rows; ++y) for(int x = 0; x < collumns; ++x) { KisColorButton* b = new KisColorButton; QCheckBox* c = new QCheckBox; c->setChecked(false); b->setEnabled(false); b->setMaximumWidth(50); c->setMaximumWidth(21); // Ugh. I hope this won't be causing any issues. Trying to get rid of the unnecessary spacing after it. connect(c, SIGNAL(toggled(bool)), b, SLOT(setEnabled(bool))); connect(c, SIGNAL(toggled(bool)), this, SIGNAL(sigConfigurationItemChanged())); connect(b, SIGNAL(changed(KoColor)), this, SIGNAL(sigConfigurationItemChanged())); QHBoxLayout* cell = new QHBoxLayout(); cell->setSpacing(0); cell->setContentsMargins(0, 0, 0, 0); cell->addWidget(c); cell->addWidget(b); ui->layoutColors->addLayout(cell, 1+y, 1+x); m_colorSelectors[y][x].button = b; m_colorSelectors[y][x].checkbox = c; } } KisPropertiesConfigurationSP KisWdgIndexColors::configuration() const { KisColorTransformationConfigurationSP config = new KisColorTransformationConfiguration("indexcolors", 1); PaletteGeneratorConfig palCfg; for(int y = 0; y < 4; ++y) for(int x = 0; x < 4; ++x) { palCfg.colors[y][x] = m_colorSelectors[y][x].button->color().toQColor(); palCfg.colorsEnabled[y][x] = m_colorSelectors[y][x].button->isEnabled(); } for(int y = 0; y < 3; ++y) palCfg.gradientSteps[y] = m_stepSpinners[y]->value(); palCfg.diagonalGradients = ui->diagCheck->isChecked(); palCfg.inbetweenRampSteps = ui->inbetweenSpinBox->value(); IndexColorPalette pal = palCfg.generate(); ui->colorCount->setText(QString::number(pal.numColors())); config->setProperty("paletteGen", palCfg.toByteArray()); config->setProperty("LFactor", ui->luminanceSlider->value() / 100.f); config->setProperty("aFactor", ui->aSlider->value() / 100.f); config->setProperty("bFactor", ui->bSlider->value() / 100.f); config->setProperty("reduceColorsEnabled", ui->colorLimitCheck->isChecked()); config->setProperty("colorLimit", ui->colorLimit->value()); config->setProperty("alphaSteps", ui->alphaStepsSpinBox->value()); return config; } void KisWdgIndexColors::setConfiguration(const KisPropertiesConfigurationSP config) { PaletteGeneratorConfig palCfg; palCfg.fromByteArray(config->getProperty("paletteGen").toByteArray()); ui->luminanceSlider->setValue(config->getFloat("LFactor")*100); ui->aSlider->setValue(config->getFloat("aFactor")*100); ui->bSlider->setValue(config->getFloat("bFactor")*100); ui->alphaStepsSpinBox->setValue(config->getInt("alphaSteps")); ui->colorLimitCheck->setChecked(config->getBool("reduceColorsEnabled")); ui->colorLimit->setEnabled(config->getBool("reduceColorsEnabled")); ui->colorLimit->setValue(config->getInt("colorLimit")); ui->diagCheck->setChecked(palCfg.diagonalGradients); ui->inbetweenSpinBox->setValue(palCfg.inbetweenRampSteps); for(int y = 0; y < 4; ++y) for(int x = 0; x < 4; ++x) { m_colorSelectors[y][x].checkbox->setChecked(palCfg.colorsEnabled[y][x]); m_colorSelectors[y][x].button->setEnabled(palCfg.colorsEnabled[y][x]); KoColor c; c.fromQColor(palCfg.colors[y][x]); m_colorSelectors[y][x].button->setColor(c); } for(int y = 0; y < 3; ++y) m_stepSpinners[y]->setValue(palCfg.gradientSteps[y]); IndexColorPalette pal = palCfg.generate(); ui->colorCount->setText(QString::number(pal.numColors())); } diff --git a/plugins/filters/indexcolors/kiswdgindexcolors.h b/plugins/filters/indexcolors/kiswdgindexcolors.h index 902f9a73af..bb997290f3 100644 --- a/plugins/filters/indexcolors/kiswdgindexcolors.h +++ b/plugins/filters/indexcolors/kiswdgindexcolors.h @@ -1,59 +1,59 @@ /* * Copyright 2014 Manuel Riecke * * Permission to use, copy, modify, and distribute this software * and its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and this * permission notice and warranty disclaimer appear in supporting * documentation, and that the name of the author not be used in * advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * The author disclaim all warranties with regard to this * software, including all implied warranties of merchantability * and fitness. In no event shall the author be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether * in an action of contract, negligence or other tortious action, * arising out of or in connection with the use or performance of * this software. */ #ifndef KISWDGINDEXCOLORS_H #define KISWDGINDEXCOLORS_H #include #include class QCheckBox; class KisColorButton; namespace Ui { class KisWdgIndexColors; } class KisWdgIndexColors : public KisConfigWidget { Q_OBJECT public: - KisWdgIndexColors(QWidget* parent = 0, Qt::WFlags f = 0, int delay = 500); + KisWdgIndexColors(QWidget* parent = 0, Qt::WindowFlags f = 0, int delay = 500); KisPropertiesConfigurationSP configuration() const override; void setConfiguration(const KisPropertiesConfigurationSP config) override; void setup(QStringList shadesLabels, int ramps); private Q_SLOTS: void slotColorLimitChanged(int value); private: struct ColorWidgets { KisColorButton* button; QCheckBox* checkbox; }; QVector< QVector > m_colorSelectors; QVector< QSpinBox* > m_stepSpinners; Ui::KisWdgIndexColors* ui; }; #endif // KISWDGINDEXCOLORS_H diff --git a/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.cpp b/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.cpp index de3399d0cd..d0011fe093 100644 --- a/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.cpp +++ b/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.cpp @@ -1,186 +1,186 @@ /* * Copyright (c) 2010-2011 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_phong_bumpmap_config_widget.h" #include #include #include "phong_bumpmap_constants.h" #include "KoChannelInfo.h" #include "KoColorSpace.h" -KisPhongBumpmapConfigWidget::KisPhongBumpmapConfigWidget(const KisPaintDeviceSP dev, QWidget *parent, Qt::WFlags f) +KisPhongBumpmapConfigWidget::KisPhongBumpmapConfigWidget(const KisPaintDeviceSP dev, QWidget *parent, Qt::WindowFlags f) : KisConfigWidget(parent, f) , m_device(dev) { Q_ASSERT(m_device); m_page = new KisPhongBumpmapWidget(this); KisSizeGroup *matPropLabelsGroup = new KisSizeGroup(this); matPropLabelsGroup->addWidget(m_page->lblAmbientReflectivity); matPropLabelsGroup->addWidget(m_page->lblDiffuseReflectivity); matPropLabelsGroup->addWidget(m_page->lblSpecularReflectivity); matPropLabelsGroup->addWidget(m_page->lblSpecularShinyExp); // Connect widgets to each other connect(m_page->azimuthDial1, SIGNAL(valueChanged(int)), m_page->azimuthSpinBox1, SLOT(setValue(int))); connect(m_page->azimuthDial2, SIGNAL(valueChanged(int)), m_page->azimuthSpinBox2, SLOT(setValue(int))); connect(m_page->azimuthDial3, SIGNAL(valueChanged(int)), m_page->azimuthSpinBox3, SLOT(setValue(int))); connect(m_page->azimuthDial4, SIGNAL(valueChanged(int)), m_page->azimuthSpinBox4, SLOT(setValue(int))); connect(m_page->azimuthSpinBox1, SIGNAL(valueChanged(int)), m_page->azimuthDial1, SLOT(setValue(int))); connect(m_page->azimuthSpinBox2, SIGNAL(valueChanged(int)), m_page->azimuthDial2, SLOT(setValue(int))); connect(m_page->azimuthSpinBox3, SIGNAL(valueChanged(int)), m_page->azimuthDial3, SLOT(setValue(int))); connect(m_page->azimuthSpinBox4, SIGNAL(valueChanged(int)), m_page->azimuthDial4, SLOT(setValue(int))); //Let widgets warn the preview of when they are updated connect(m_page->azimuthDial1, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->azimuthDial2, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->azimuthDial3, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->azimuthDial4, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->lightKColorCombo1, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->lightKColorCombo2, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->lightKColorCombo3, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->lightKColorCombo4, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->inclinationSpinBox1, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->inclinationSpinBox2, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->inclinationSpinBox3, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->inclinationSpinBox4, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->useNormalMap, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->diffuseReflectivityGroup, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->specularReflectivityGroup, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->ambientReflectivityKisDoubleSliderSpinBox, SIGNAL(valueChanged(qreal)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->diffuseReflectivityKisDoubleSliderSpinBox, SIGNAL(valueChanged(qreal)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->specularReflectivityKisDoubleSliderSpinBox, SIGNAL(valueChanged(qreal)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->shinynessExponentKisSliderSpinBox, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->heightChannelComboBox, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->lightSourceGroupBox1, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->lightSourceGroupBox2, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->lightSourceGroupBox3, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->lightSourceGroupBox4, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged())); QVBoxLayout *l = new QVBoxLayout(this); Q_CHECK_PTR(l); l->addWidget(m_page); /* fill in the channel chooser */ QList channels = m_device->colorSpace()->channels(); for (quint8 ch = 0; ch < m_device->colorSpace()->colorChannelCount(); ch++) m_page->heightChannelComboBox->addItem(channels.at(ch)->name()); connect(m_page->useNormalMap, SIGNAL(toggled(bool)), this, SLOT(slotDisableHeightChannelCombobox(bool) ) ); } void KisPhongBumpmapConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config) { if (!config) return; QVariant tempcolor; if (config->getBool(USE_NORMALMAP_IS_ENABLED)) { m_page->heightChannelComboBox->setEnabled(false); } else { m_page->heightChannelComboBox->setEnabled(true); } m_page->ambientReflectivityKisDoubleSliderSpinBox->setValue( config->getDouble(PHONG_AMBIENT_REFLECTIVITY) ); m_page->diffuseReflectivityKisDoubleSliderSpinBox->setValue( config->getDouble(PHONG_DIFFUSE_REFLECTIVITY) ); m_page->specularReflectivityKisDoubleSliderSpinBox->setValue( config->getDouble(PHONG_SPECULAR_REFLECTIVITY) ); m_page->shinynessExponentKisSliderSpinBox->setValue( config->getInt(PHONG_SHINYNESS_EXPONENT) ); m_page->useNormalMap->setChecked( config->getBool(USE_NORMALMAP_IS_ENABLED) ); m_page->diffuseReflectivityGroup->setChecked( config->getBool(PHONG_DIFFUSE_REFLECTIVITY_IS_ENABLED) ); m_page->specularReflectivityGroup->setChecked( config->getBool(PHONG_SPECULAR_REFLECTIVITY_IS_ENABLED) ); // NOTE: Indexes are off by 1 simply because arrays start at 0 and the GUI naming scheme started at 1 m_page->lightSourceGroupBox1->setChecked( config->getBool(PHONG_ILLUMINANT_IS_ENABLED[0]) ); m_page->lightSourceGroupBox2->setChecked( config->getBool(PHONG_ILLUMINANT_IS_ENABLED[1]) ); m_page->lightSourceGroupBox3->setChecked( config->getBool(PHONG_ILLUMINANT_IS_ENABLED[2]) ); m_page->lightSourceGroupBox4->setChecked( config->getBool(PHONG_ILLUMINANT_IS_ENABLED[3]) ); config->getProperty(PHONG_ILLUMINANT_COLOR[0], tempcolor); m_page->lightKColorCombo1->setColor(tempcolor.value()); config->getProperty(PHONG_ILLUMINANT_COLOR[1], tempcolor); m_page->lightKColorCombo2->setColor(tempcolor.value()); config->getProperty(PHONG_ILLUMINANT_COLOR[2], tempcolor); m_page->lightKColorCombo3->setColor(tempcolor.value()); config->getProperty(PHONG_ILLUMINANT_COLOR[3], tempcolor); m_page->lightKColorCombo4->setColor(tempcolor.value()); m_page->azimuthSpinBox1->setValue( config->getDouble(PHONG_ILLUMINANT_AZIMUTH[0]) ); m_page->azimuthSpinBox2->setValue( config->getDouble(PHONG_ILLUMINANT_AZIMUTH[1]) ); m_page->azimuthSpinBox3->setValue( config->getDouble(PHONG_ILLUMINANT_AZIMUTH[2]) ); m_page->azimuthSpinBox4->setValue( config->getDouble(PHONG_ILLUMINANT_AZIMUTH[3]) ); m_page->inclinationSpinBox1->setValue( config->getDouble(PHONG_ILLUMINANT_INCLINATION[0]) ); m_page->inclinationSpinBox2->setValue( config->getDouble(PHONG_ILLUMINANT_INCLINATION[1]) ); m_page->inclinationSpinBox3->setValue( config->getDouble(PHONG_ILLUMINANT_INCLINATION[2]) ); m_page->inclinationSpinBox4->setValue( config->getDouble(PHONG_ILLUMINANT_INCLINATION[3]) ); } KisPropertiesConfigurationSP KisPhongBumpmapConfigWidget::configuration() const { KisFilterConfigurationSP config = new KisFilterConfiguration("phongbumpmap", 2); config->setProperty(PHONG_HEIGHT_CHANNEL, m_page->heightChannelComboBox->currentText()); config->setProperty(USE_NORMALMAP_IS_ENABLED, m_page->useNormalMap->isChecked()); config->setProperty(PHONG_AMBIENT_REFLECTIVITY, m_page->ambientReflectivityKisDoubleSliderSpinBox->value()); config->setProperty(PHONG_DIFFUSE_REFLECTIVITY, m_page->diffuseReflectivityKisDoubleSliderSpinBox->value()); config->setProperty(PHONG_SPECULAR_REFLECTIVITY, m_page->specularReflectivityKisDoubleSliderSpinBox->value()); config->setProperty(PHONG_SHINYNESS_EXPONENT, m_page->shinynessExponentKisSliderSpinBox->value()); config->setProperty(PHONG_DIFFUSE_REFLECTIVITY_IS_ENABLED, m_page->diffuseReflectivityGroup->isChecked()); config->setProperty(PHONG_SPECULAR_REFLECTIVITY_IS_ENABLED, m_page->specularReflectivityGroup->isChecked()); //config->setProperty(PHONG_SHINYNESS_EXPONENT_IS_ENABLED, m_page->specularReflectivityCheckBox->isChecked()); // Indexes are off by 1 simply because arrays start at 0 and the GUI naming scheme started at 1 config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[0], m_page->lightSourceGroupBox1->isChecked()); config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[1], m_page->lightSourceGroupBox2->isChecked()); config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[2], m_page->lightSourceGroupBox3->isChecked()); config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[3], m_page->lightSourceGroupBox4->isChecked()); config->setProperty(PHONG_ILLUMINANT_COLOR[0], m_page->lightKColorCombo1->color()); config->setProperty(PHONG_ILLUMINANT_COLOR[1], m_page->lightKColorCombo2->color()); config->setProperty(PHONG_ILLUMINANT_COLOR[2], m_page->lightKColorCombo3->color()); config->setProperty(PHONG_ILLUMINANT_COLOR[3], m_page->lightKColorCombo4->color()); config->setProperty(PHONG_ILLUMINANT_AZIMUTH[0], m_page->azimuthSpinBox1->value()); config->setProperty(PHONG_ILLUMINANT_AZIMUTH[1], m_page->azimuthSpinBox2->value()); config->setProperty(PHONG_ILLUMINANT_AZIMUTH[2], m_page->azimuthSpinBox3->value()); config->setProperty(PHONG_ILLUMINANT_AZIMUTH[3], m_page->azimuthSpinBox4->value()); config->setProperty(PHONG_ILLUMINANT_INCLINATION[0], m_page->inclinationSpinBox1->value()); config->setProperty(PHONG_ILLUMINANT_INCLINATION[1], m_page->inclinationSpinBox2->value()); config->setProperty(PHONG_ILLUMINANT_INCLINATION[2], m_page->inclinationSpinBox3->value()); config->setProperty(PHONG_ILLUMINANT_INCLINATION[3], m_page->inclinationSpinBox4->value()); // Read configuration /* QMap rofl = QMap(config->getProperties()); QMap::const_iterator i; for (i = rofl.constBegin(); i != rofl.constEnd(); ++i) dbgKrita << i.key() << ":" << i.value(); */ return config; } void KisPhongBumpmapConfigWidget::slotDisableHeightChannelCombobox(bool normalmapchecked) { if (normalmapchecked) { m_page->heightChannelComboBox->setEnabled(false); } else { m_page->heightChannelComboBox->setEnabled(true); } } diff --git a/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.h b/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.h index aed4fe678b..7367ca4b5d 100644 --- a/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.h +++ b/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.h @@ -1,66 +1,66 @@ /* * Copyright (c) 2010-2011 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_PHONG_BUMPMAP_CONFIG_WIDGET_H #define KIS_PHONG_BUMPMAP_CONFIG_WIDGET_H #include "ui_wdgphongbumpmap.h" #include "kis_paint_device.h" #include "kis_config_widget.h" #include "kis_image.h" class KisPhongBumpmapWidget : public QWidget, public Ui::WdgPhongBumpmap { Q_OBJECT public: KisPhongBumpmapWidget(QWidget *parent) : QWidget(parent) { setupUi(this); ambientReflectivityKisDoubleSliderSpinBox -> setRange(0, 1, 2); diffuseReflectivityKisDoubleSliderSpinBox -> setRange(0, 1, 2); specularReflectivityKisDoubleSliderSpinBox -> setRange(0, 1, 2); shinynessExponentKisSliderSpinBox -> setRange(1, 200); ambientReflectivityKisDoubleSliderSpinBox -> setValue(0.1); diffuseReflectivityKisDoubleSliderSpinBox -> setValue(0.5); specularReflectivityKisDoubleSliderSpinBox -> setValue(0.5); shinynessExponentKisSliderSpinBox -> setValue(40); } }; class KisPhongBumpmapConfigWidget : public KisConfigWidget { Q_OBJECT public: - KisPhongBumpmapConfigWidget(const KisPaintDeviceSP dev, QWidget *parent, Qt::WFlags f = 0); + KisPhongBumpmapConfigWidget(const KisPaintDeviceSP dev, QWidget *parent, Qt::WindowFlags f = 0); ~KisPhongBumpmapConfigWidget() override {} void setConfiguration(const KisPropertiesConfigurationSP config) override; KisPropertiesConfigurationSP configuration() const override; KisPhongBumpmapWidget *m_page; private: KisPaintDeviceSP m_device; private Q_SLOTS: void slotDisableHeightChannelCombobox(bool normalmapchecked); }; #endif //KIS_PHONG_BUMPMAP_CONFIG_WIDGET_H diff --git a/plugins/flake/textshape/CMakeLists.txt b/plugins/flake/textshape/CMakeLists.txt index c0999ed2fc..fe40faaa7e 100644 --- a/plugins/flake/textshape/CMakeLists.txt +++ b/plugins/flake/textshape/CMakeLists.txt @@ -1,160 +1,170 @@ project( textPlugin) +remove_definitions( + -DQT_DISABLE_DEPRECATED_BEFORE=0x50900 +) + +add_definitions( + -DQT_DISABLE_DEPRECATED_BEFORE=0 +) + add_subdirectory( textlayout ) add_subdirectory( kotext ) + + set ( textshape_SRCS TextPlugin.cpp TextShape.cpp TextShapeFactory.cpp TextTool.cpp TextEditingPluginContainer.cpp TextToolFactory.cpp ShrinkToFitShapeContainer.cpp SimpleRootAreaProvider.cpp AnnotationTextShape.cpp AnnotationTextShapeFactory.cpp ChangeTracker.cpp ReviewTool.cpp ReviewToolFactory.cpp TextChanges.cpp TextChange.cpp FontSizeAction.cpp FontFamilyAction.cpp ReferencesTool.cpp ReferencesToolFactory.cpp # dialogs/StylesWidget.cpp # dialogs/SpecialButton.cpp dialogs/StylesCombo.cpp dialogs/StylesComboPreview.cpp dialogs/DockerStylesComboModel.cpp dialogs/SimpleCharacterWidget.cpp dialogs/SimpleParagraphWidget.cpp dialogs/SimpleTableWidget.cpp dialogs/SimpleInsertWidget.cpp dialogs/LinkInsertionDialog.cpp dialogs/SimpleTableOfContentsWidget.cpp dialogs/SimpleCitationBibliographyWidget.cpp dialogs/SimpleLinksWidget.cpp dialogs/SimpleSpellCheckingWidget.cpp dialogs/CitationInsertionDialog.cpp dialogs/InsertBibliographyDialog.cpp dialogs/SimpleFootEndNotesWidget.cpp dialogs/NotesConfigurationDialog.cpp dialogs/SimpleCaptionsWidget.cpp dialogs/ParagraphLayout.cpp dialogs/ParagraphIndentSpacing.cpp dialogs/ParagraphDecorations.cpp dialogs/ParagraphBulletsNumbers.cpp dialogs/ParagraphSettingsDialog.cpp dialogs/ParagraphDropCaps.cpp dialogs/ListsSpinBox.cpp dialogs/StylesModel.cpp dialogs/StylesManagerModel.cpp dialogs/StylesSortFilterProxyModel.cpp dialogs/AbstractStylesModel.cpp dialogs/StylesFilteredModelBase.cpp dialogs/ValidParentStylesProxyModel.cpp dialogs/StylesDelegate.cpp dialogs/StyleManager.cpp dialogs/StyleManagerDialog.cpp dialogs/ParagraphGeneral.cpp dialogs/CharacterGeneral.cpp dialogs/CharacterHighlighting.cpp dialogs/InsertCharacter.cpp dialogs/FontDia.cpp dialogs/FontDecorations.cpp dialogs/LanguageTab.cpp dialogs/FormattingPreview.cpp dialogs/StyleManagerWelcome.cpp dialogs/TableDialog.cpp dialogs/QuickTableButton.cpp dialogs/FormattingButton.cpp dialogs/ChangeConfigureDialog.cpp dialogs/AcceptRejectChangeDialog.cpp dialogs/TrackedChangeModel.cpp dialogs/TrackedChangeManager.cpp dialogs/BibliographyConfigureDialog.cpp dialogs/TableOfContentsConfigure.cpp dialogs/TableOfContentsStyleConfigure.cpp dialogs/TableOfContentsStyleModel.cpp dialogs/TableOfContentsStyleDelegate.cpp dialogs/TableOfContentsPreview.cpp dialogs/TableOfContentsEntryDelegate.cpp dialogs/TableOfContentsEntryModel.cpp dialogs/TableOfContentsTemplate.cpp dialogs/BibliographyTemplate.cpp dialogs/BibliographyPreview.cpp dialogs/ListLevelChooser.cpp dialogs/SimpleAnnotationWidget.cpp dialogs/ManageBookmarkDialog.cpp dialogs/SectionFormatDialog.cpp dialogs/SectionsSplitDialog.cpp commands/ChangeListLevelCommand.cpp commands/ShowChangesCommand.cpp commands/AcceptChangeCommand.cpp commands/RejectChangeCommand.cpp commands/AutoResizeCommand.cpp ) ki18n_wrap_ui(textshape_SRCS dialogs/SimpleCharacterWidget.ui dialogs/SimpleParagraphWidget.ui dialogs/SimpleTableWidget.ui dialogs/SimpleInsertWidget.ui dialogs/SimpleTableOfContentsWidget.ui dialogs/SimpleCitationBibliographyWidget.ui dialogs/SimpleSpellCheckingWidget.ui dialogs/CitationInsertionDialog.ui dialogs/InsertBibliographyDialog.ui dialogs/SimpleFootEndNotesWidget.ui dialogs/NotesConfigurationDialog.ui dialogs/SimpleCaptionsWidget.ui dialogs/StylesWidget.ui dialogs/ParagraphLayout.ui dialogs/ParagraphIndentSpacing.ui dialogs/ParagraphDecorations.ui dialogs/ParagraphBulletsNumbers.ui dialogs/ParagraphDropCaps.ui dialogs/StyleManager.ui dialogs/CharacterGeneral.ui dialogs/CharacterHighlighting.ui dialogs/StyleManagerWelcome.ui dialogs/TableDialog.ui dialogs/BibliographyConfigureDialog.ui dialogs/TableOfContentsConfigure.ui dialogs/SimpleLinksWidget.ui dialogs/TableOfContentsStyleConfigure.ui dialogs/SimpleAnnotationWidget.ui dialogs/FontDecorations.ui dialogs/LanguageTab.ui dialogs/ChangeConfigureDialog.ui dialogs/AcceptRejectChangeDialog.ui dialogs/TrackedChangeManager.ui dialogs/LinkInsertionDialog.ui dialogs/ManageBookmark.ui dialogs/SectionFormatDialog.ui dialogs/SectionsSplitDialog.ui ) qt5_add_resources(textshape_SRCS textshape.qrc) add_library(krita_shape_text MODULE ${textshape_SRCS}) target_link_libraries(krita_shape_text kritatext kritatextlayout kritawidgetutils kritawidgets Qt5::Network KF5::Completion KF5::ItemViews KF5::WidgetsAddons ) if( SHOULD_BUILD_FEATURE_RDF ) target_link_libraries(krita_shape_text ${SOPRANO_LIBRARIES}) endif() install(TARGETS krita_shape_text DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/flake/vectorshape/VectorShape.cpp b/plugins/flake/vectorshape/VectorShape.cpp index 2a80a5cb20..0d847d54ed 100644 --- a/plugins/flake/vectorshape/VectorShape.cpp +++ b/plugins/flake/vectorshape/VectorShape.cpp @@ -1,498 +1,490 @@ /* This file is part of the KDE project * * Copyright (C) 2009 - 2011 Inge Wallin * Copyright (C) 2011 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. */ // Own #include "VectorShape.h" // Posix #include // Qt #include #include #include #include #include #include #include #include // Calligra #include "KoUnit.h" #include "KoStore.h" #include "KoXmlNS.h" #include "KoXmlReader.h" #include "KoXmlWriter.h" #include #include #include #include #include // Wmf support #include "WmfPainterBackend.h" // Vector shape #include "EmfParser.h" #include "EmfOutputPainterStrategy.h" #include "EmfOutputDebugStrategy.h" #include "SvmParser.h" #include "SvmPainterBackend.h" #include "kis_painting_tweaks.h" // Comment out to get uncached painting, which is good for debugging //#define VECTORSHAPE_PAINT_UNCACHED // Comment out to get unthreaded painting, which is good for debugging //#define VECTORSHAPE_PAINT_UNTHREADED VectorShape::VectorShape() : KoFrameShape(KoXmlNS::draw, "image") , m_type(VectorTypeNone) , m_isRendering(false) { setShapeId(VectorShape_SHAPEID); // Default size of the shape. KoShape::setSize(QSizeF(CM_TO_POINT(8), CM_TO_POINT(5))); m_cache.setMaxCost(3); } VectorShape::~VectorShape() { // Wait for the render-thread to finish before the shape is allowed to be // destroyed so we can make sure to prevent crashes or unwanted // side-effects. Maybe as alternate we could just kill the render-thread... QMutexLocker locker(&m_mutex); } // Methods specific to the vector shape. QByteArray VectorShape::compressedContents() const { return m_contents; } VectorShape::VectorType VectorShape::vectorType() const { return m_type; } void VectorShape::setCompressedContents(const QByteArray &newContents, VectorType vectorType) { QMutexLocker locker(&m_mutex); m_contents = newContents; m_type = vectorType; m_cache.clear(); update(); } RenderThread::RenderThread(const QByteArray &contents, VectorShape::VectorType type, const QSizeF &size, const QSize &boundingSize, qreal zoomX, qreal zoomY) : QObject(), QRunnable(), m_contents(contents), m_type(type), m_size(size), m_boundingSize(boundingSize), m_zoomX(zoomX), m_zoomY(zoomY) { setAutoDelete(true); } RenderThread::~RenderThread() { } void RenderThread::run() { QImage *image = new QImage(m_boundingSize, QImage::Format_ARGB32); image->fill(0); QPainter painter; if (!painter.begin(image)) { //kWarning(31000) << "Failed to create image-cache"; delete image; image = 0; } else { painter.scale(m_zoomX, m_zoomY); draw(painter); painter.end(); } emit finished(m_boundingSize, image); } void RenderThread::draw(QPainter &painter) { // If the data is uninitialized, e.g. because loading failed, draw the null shape. if (m_contents.isEmpty()) { drawNull(painter); return; } // Actually draw the contents switch (m_type) { case VectorShape::VectorTypeWmf: drawWmf(painter); break; case VectorShape::VectorTypeEmf: drawEmf(painter); break; case VectorShape::VectorTypeSvm: drawSvm(painter); break; case VectorShape::VectorTypeSvg: drawSvg(painter); break; case VectorShape::VectorTypeNone: default: drawNull(painter); } } void RenderThread::drawNull(QPainter &painter) const { QRectF rect(QPointF(0, 0), m_size); painter.save(); // Draw a simple cross in a rectangle just to indicate that there is something here. painter.setPen(QPen(QColor(172, 196, 206))); painter.drawRect(rect); painter.drawLine(rect.topLeft(), rect.bottomRight()); painter.drawLine(rect.bottomLeft(), rect.topRight()); painter.restore(); } void RenderThread::drawWmf(QPainter &painter) const { Libwmf::WmfPainterBackend wmfPainter(&painter, m_size); if (!wmfPainter.load(m_contents)) { drawNull(painter); return; } painter.save(); // Actually paint the WMF. wmfPainter.play(); painter.restore(); } void RenderThread::drawEmf(QPainter &painter) const { // FIXME: Make emfOutput use QSizeF QSize shapeSizeInt(m_size.width(), m_size.height()); //kDebug(31000) << "-------------------------------------------"; //kDebug(31000) << "size: " << shapeSizeInt << m_size; //kDebug(31000) << "position: " << position(); //kDebug(31000) << "-------------------------------------------"; Libemf::Parser emfParser; #if 1 // Set to 0 to get debug output // Create a new painter output strategy. Last param = true means keep aspect ratio. Libemf::OutputPainterStrategy emfPaintOutput(painter, shapeSizeInt, true); emfParser.setOutput(&emfPaintOutput); #else Libemf::OutputDebugStrategy emfDebugOutput; emfParser.setOutput(&emfDebugOutput); #endif emfParser.load(m_contents); } void RenderThread::drawSvm(QPainter &painter) const { QSize shapeSizeInt(m_size.width(), m_size.height()); Libsvm::SvmParser svmParser; // Create a new painter backend. Libsvm::SvmPainterBackend svmPaintOutput(&painter, shapeSizeInt); svmParser.setBackend(&svmPaintOutput); svmParser.parse(m_contents); } void RenderThread::drawSvg(QPainter &painter) const { QSvgRenderer renderer(m_contents); renderer.render(&painter, QRectF(0, 0, m_size.width(), m_size.height())); } void VectorShape::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &) { #ifdef VECTORSHAPE_PAINT_UNCACHED bool useCache = false; #else bool useCache = true; #endif -#ifdef VECTORSHAPE_PAINT_UNTHREADED - bool asynchronous = false; -#else - // Since the backends may use QPainter::drawText we need to make sure to only - // use threads if the font-backend supports that what is in most cases. - bool asynchronous = QFontDatabase::supportsThreadedFontRendering(); -#endif - - QImage *cache = render(converter, asynchronous, useCache); + QImage *cache = render(converter, true, useCache); if (cache) { // paint cached image Q_ASSERT(!cache->isNull()); QVector clipRects = KisPaintingTweaks::safeClipRegion(painter).rects(); foreach (const QRect &rc, clipRects) { painter.drawImage(rc.topLeft(), *cache, rc); } } } void VectorShape::renderFinished(QSize boundingSize, QImage *image) { if (image) { m_cache.insert(boundingSize.height(), image); update(); } m_isRendering = false; } // ---------------------------------------------------------------- // Loading and Saving void VectorShape::saveOdf(KoShapeSavingContext &context) const { QMutexLocker locker(&m_mutex); KoEmbeddedDocumentSaver &fileSaver = context.embeddedSaver(); KoXmlWriter &xmlWriter = context.xmlWriter(); QString fileName = fileSaver.getFilename("VectorImages/Image"); QByteArray mimeType; switch (m_type) { case VectorTypeWmf: mimeType = "image/x-wmf"; break; case VectorTypeEmf: mimeType = "image/x-emf"; break; case VectorTypeSvm: mimeType = "image/x-svm"; // mimetype as used inside LO/AOO break; case VectorTypeSvg: mimeType = "image/svg+xml"; default: // FIXME: What here? mimeType = "application/x-what"; break; } xmlWriter.startElement("draw:frame"); saveOdfAttributes(context, OdfAllAttributes); fileSaver.embedFile(xmlWriter, "draw:image", fileName, mimeType, qUncompress(m_contents)); xmlWriter.endElement(); // draw:frame } bool VectorShape::loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) { //kDebug(31000) <<"Loading ODF frame in the vector shape. Element = " << element.tagName(); loadOdfAttributes(element, context, OdfAllAttributes); return loadOdfFrame(element, context); } inline static int read32(const char *buffer, const int offset) { // little endian int result = (int) buffer[offset]; result |= (int) buffer[offset + 1] << 8; result |= (int) buffer[offset + 2] << 16; result |= (int) buffer[offset + 3] << 24; return result; } // Load the actual contents within the vector shape. bool VectorShape::loadOdfFrameElement(const KoXmlElement &element, KoShapeLoadingContext &context) { //kDebug(31000) <<"Loading ODF element: " << element.tagName(); QMutexLocker locker(&m_mutex); // Get the reference to the vector file. If there is no href, then just return. const QString href = element.attribute("href"); if (href.isEmpty()) { return false; } // Try to open the embedded file. KoStore *store = context.odfLoadingContext().store(); bool result = store->open(href); if (!result) { return false; } int size = store->size(); if (size < 88) { store->close(); return false; } m_contents = store->read(size); store->close(); if (m_contents.count() < size) { //kDebug(31000) << "Too few bytes read: " << m_contents.count() << " instead of " << size; return false; } // Try to recognize the type. We should do this before the // compression below, because that's a semi-expensive operation. m_type = vectorType(m_contents); // Return false if we didn't manage to identify the type. if (m_type == VectorTypeNone) { return false; } // Compress for biiiig memory savings. m_contents = qCompress(m_contents); return true; } void VectorShape::waitUntilReady(const KoViewConverter &converter, bool asynchronous) const { render(converter, asynchronous, true); } QImage *VectorShape::render(const KoViewConverter &converter, bool asynchronous, bool useCache) const { QRectF rect = converter.documentToView(boundingRect()); int id = rect.size().toSize().height(); QImage *cache = useCache ? m_cache[id] : 0; if (!cache || cache->isNull()) { // recreate the cached image cache = 0; if (!m_isRendering) { m_isRendering = true; qreal zoomX, zoomY; converter.zoom(&zoomX, &zoomY); QMutexLocker locker(&m_mutex); const QByteArray uncompressedContents = m_type != VectorShape::VectorTypeNone ? qUncompress(m_contents) : QByteArray(); RenderThread *t = new RenderThread(uncompressedContents, m_type, size(), rect.size().toSize(), zoomX, zoomY); connect(t, SIGNAL(finished(QSize,QImage*)), this, SLOT(renderFinished(QSize,QImage*))); if (asynchronous) { // render and paint the image threaded QThreadPool::globalInstance()->start(t); } else { // non-threaded rendering and painting of the image t->run(); cache = m_cache[id]; } } } return cache; } VectorShape::VectorType VectorShape::vectorType(const QByteArray &newContents) { VectorType vectorType; if (isWmf(newContents)) { vectorType = VectorShape::VectorTypeWmf; } else if (isEmf(newContents)) { vectorType = VectorShape::VectorTypeEmf; } else if (isSvm(newContents)) { vectorType = VectorShape::VectorTypeSvm; } else if (isSvg(newContents)) { vectorType = VectorShape::VectorTypeSvg; } else { vectorType = VectorShape::VectorTypeNone; } return vectorType; } bool VectorShape::isWmf(const QByteArray &bytes) { //kDebug(31000) << "Check for WMF"; const char *data = bytes.constData(); const int size = bytes.count(); if (size < 10) { return false; } // This is how the 'file' command identifies a WMF. if (data[0] == '\327' && data[1] == '\315' && data[2] == '\306' && data[3] == '\232') { // FIXME: Is this a compressed wmf? Check it up. //kDebug(31000) << "WMF identified: header 1"; return true; } if (data[0] == '\002' && data[1] == '\000' && data[2] == '\011' && data[3] == '\000') { //kDebug(31000) << "WMF identified: header 2"; return true; } if (data[0] == '\001' && data[1] == '\000' && data[2] == '\011' && data[3] == '\000') { //kDebug(31000) << "WMF identified: header 3"; return true; } return false; } bool VectorShape::isEmf(const QByteArray &bytes) { //kDebug(31000) << "Check for EMF"; const char *data = bytes.constData(); const int size = bytes.count(); // This is how the 'file' command identifies an EMF. // 1. Check type qint32 mark = read32(data, 0); if (mark != 0x00000001) { //kDebug(31000) << "Not an EMF: mark = " << mark << " instead of 0x00000001"; return false; } // 2. An EMF has the string " EMF" at the start + offset 40. if (size > 44 && data[40] == ' ' && data[41] == 'E' && data[42] == 'M' && data[43] == 'F') { //kDebug(31000) << "EMF identified"; return true; } return false; } bool VectorShape::isSvm(const QByteArray &bytes) { //kDebug(31000) << "Check for SVM"; // Check the SVM signature. if (bytes.startsWith("VCLMTF")) { //kDebug(31000) << "SVM identified"; return true; } return false; } bool VectorShape::isSvg(const QByteArray &bytes) { //kDebug(31000) << "Check for SVG"; return (bytes.contains("svg")); } diff --git a/plugins/flake/vectorshape/VectorTool.cpp b/plugins/flake/vectorshape/VectorTool.cpp index 026cfffc4a..8b3aff4cdb 100644 --- a/plugins/flake/vectorshape/VectorTool.cpp +++ b/plugins/flake/vectorshape/VectorTool.cpp @@ -1,117 +1,117 @@ /* This file is part of the KDE project Copyright 2007 Montel Laurent Copyright 2011 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 "VectorTool.h" #include "VectorShape.h" #include "ChangeVectorDataCommand.h" #include #include -#include +#include #include #include #include #include #include #include #include #include VectorTool::VectorTool(KoCanvasBase *canvas) : KoToolBase(canvas) , m_shape(0) { } void VectorTool::activate(ToolActivation activation, const QSet &shapes) { KoToolBase::activate(activation, shapes); foreach (KoShape *shape, shapes) { m_shape = dynamic_cast(shape); if (m_shape) { break; } } if (!m_shape) { emit done(); return; } useCursor(Qt::ArrowCursor); } void VectorTool::deactivate() { m_shape = 0; KoToolBase::deactivate(); } QWidget *VectorTool::createOptionWidget() { QWidget *optionWidget = new QWidget(); QGridLayout *layout = new QGridLayout(optionWidget); QToolButton *button = 0; button = new QToolButton(optionWidget); button->setIcon(koIcon("document-open")); button->setToolTip(i18n("Open Vector Image (EMF/WMF/SVM)")); layout->addWidget(button, 0, 0); connect(button, SIGNAL(clicked(bool)), this, SLOT(changeUrlPressed())); return optionWidget; } void VectorTool::changeUrlPressed() { if (m_shape == 0) { return; } KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); dialog.setCaption(i18n("Select a Vector Image")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(QString("image/x-emf,image/x-wmf,image/x-svm,image/svg+xml").split(',')); QString fn = dialog.filename(); if (!fn.isEmpty()) { QFile f(fn); if (f.exists()) { f.open(QFile::ReadOnly); QByteArray ba = f.readAll(); f.close(); if (!ba.isEmpty()) { const VectorShape::VectorType vectorType = VectorShape::vectorType(ba); ChangeVectorDataCommand *cmd = new ChangeVectorDataCommand(m_shape, qCompress(ba), vectorType); canvas()->addCommand(cmd); } } } } void VectorTool::mouseDoubleClickEvent(KoPointerEvent *event) { if (canvas()->shapeManager()->shapeAt(event->point) != m_shape) { event->ignore(); // allow the event to be used by another return; } changeUrlPressed(); } diff --git a/plugins/impex/kra/kra_converter.cpp b/plugins/impex/kra/kra_converter.cpp index 79b5976bfa..8724b36ae2 100644 --- a/plugins/impex/kra/kra_converter.cpp +++ b/plugins/impex/kra/kra_converter.cpp @@ -1,353 +1,352 @@ /* * 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 "kra_converter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char CURRENT_DTD_VERSION[] = "2.0"; KraConverter::KraConverter(KisDocument *doc) : m_doc(doc) , m_image(doc->savingImage()) { } KraConverter::~KraConverter() { delete m_store; delete m_kraSaver; delete m_kraLoader; } KisImageBuilder_Result KraConverter::buildImage(QIODevice *io) { m_store = KoStore::createStore(io, KoStore::Read, "", KoStore::Zip); if (m_store->bad()) { m_doc->setErrorMessage(i18n("Not a valid Krita file")); return KisImageBuilder_RESULT_FAILURE; } bool success; { if (m_store->hasFile("root") || m_store->hasFile("maindoc.xml")) { // Fallback to "old" file format (maindoc.xml) KoXmlDocument doc; bool ok = oldLoadAndParse(m_store, "root", doc); if (ok) ok = loadXML(doc, m_store); if (!ok) { return KisImageBuilder_RESULT_FAILURE; } } else { errUI << "ERROR: No maindoc.xml" << endl; m_doc->setErrorMessage(i18n("Invalid document: no file 'maindoc.xml'.")); return KisImageBuilder_RESULT_FAILURE; } if (m_store->hasFile("documentinfo.xml")) { KoXmlDocument doc; if (oldLoadAndParse(m_store, "documentinfo.xml", doc)) { m_doc->documentInfo()->load(doc); } } success = completeLoading(m_store); } return success ? KisImageBuilder_RESULT_OK : KisImageBuilder_RESULT_FAILURE; } KisImageSP KraConverter::image() { return m_image; } vKisNodeSP KraConverter::activeNodes() { return m_activeNodes; } QList KraConverter::assistants() { return m_assistants; } KisImageBuilder_Result KraConverter::buildFile(QIODevice *io) { m_store = KoStore::createStore(io, KoStore::Write, m_doc->nativeFormatMimeType(), KoStore::Zip); if (m_store->bad()) { m_doc->setErrorMessage(i18n("Could not create the file for saving")); return KisImageBuilder_RESULT_FAILURE; } bool result = false; m_kraSaver = new KisKraSaver(m_doc); result = saveRootDocuments(m_store); if (!result) { return KisImageBuilder_RESULT_FAILURE; } result = m_kraSaver->saveKeyframes(m_store, m_doc->url().toLocalFile(), true); if (!result) { qWarning() << "saving key frames failed"; } result = m_kraSaver->saveBinaryData(m_store, m_image, m_doc->url().toLocalFile(), true, m_doc->isAutosaving()); if (!result) { qWarning() << "saving binary data failed"; } if (!m_store->finalize()) { return KisImageBuilder_RESULT_FAILURE; } if (!m_kraSaver->errorMessages().isEmpty()) { m_doc->setErrorMessage(m_kraSaver->errorMessages().join(".\n")); return KisImageBuilder_RESULT_FAILURE; } return KisImageBuilder_RESULT_OK; } bool KraConverter::saveRootDocuments(KoStore *store) { dbgFile << "Saving root"; if (store->open("root")) { KoStoreDevice dev(store); if (!saveToStream(&dev) || !store->close()) { dbgUI << "saveToStream failed"; return false; } } else { m_doc->setErrorMessage(i18n("Not able to write '%1'. Partition full?", QString("maindoc.xml"))); return false; } bool success = false; if (store->open("documentinfo.xml")) { QDomDocument doc = KisDocument::createDomDocument("document-info" /*DTD name*/, "document-info" /*tag name*/, "1.1"); doc = m_doc->documentInfo()->save(doc); KoStoreDevice dev(store); QByteArray s = doc.toByteArray(); // this is already Utf8! success = dev.write(s.data(), s.size()); store->close(); } if (store->open("preview.png")) { // ### TODO: missing error checking (The partition could be full!) savePreview(store); (void)store->close(); } dbgUI << "Saving done of url:" << m_doc->url().toLocalFile(); // Success return success; } bool KraConverter::saveToStream(QIODevice *dev) { QDomDocument doc = createDomDocument(); // Save to buffer QByteArray s = doc.toByteArray(); // utf8 already dev->open(QIODevice::WriteOnly); int nwritten = dev->write(s.data(), s.size()); if (nwritten != (int)s.size()) { warnUI << "wrote " << nwritten << "- expected" << s.size(); } return nwritten == (int)s.size(); } QDomDocument KraConverter::createDomDocument() { QDomDocument doc = m_doc->createDomDocument("DOC", CURRENT_DTD_VERSION); QDomElement root = doc.documentElement(); root.setAttribute("editor", "Krita"); root.setAttribute("syntaxVersion", "2"); root.setAttribute("kritaVersion", KritaVersionWrapper::versionString(false)); root.appendChild(m_kraSaver->saveXML(doc, m_image)); if (!m_kraSaver->errorMessages().isEmpty()) { m_doc->setErrorMessage(m_kraSaver->errorMessages().join(".\n")); } return doc; } bool KraConverter::savePreview(KoStore *store) { QPixmap pix = m_doc->generatePreview(QSize(256, 256)); QImage preview(pix.toImage().convertToFormat(QImage::Format_ARGB32, Qt::ColorOnly)); if (preview.size() == QSize(0,0)) { QSize newSize = m_doc->savingImage()->bounds().size(); newSize.scale(QSize(256, 256), Qt::KeepAspectRatio); preview = QImage(newSize, QImage::Format_ARGB32); preview.fill(QColor(0, 0, 0, 0)); } KoStoreDevice io(store); if (!io.open(QIODevice::WriteOnly)) { return false; } bool ret = preview.save(&io, "PNG"); io.close(); return ret; } bool KraConverter::oldLoadAndParse(KoStore *store, const QString &filename, KoXmlDocument &xmldoc) { //dbgUI <<"Trying to open" << filename; if (!store->open(filename)) { warnUI << "Entry " << filename << " not found!"; m_doc->setErrorMessage(i18n("Could not find %1", filename)); return false; } // Error variables for QDomDocument::setContent QString errorMsg; int errorLine, errorColumn; bool ok = xmldoc.setContent(store->device(), &errorMsg, &errorLine, &errorColumn); store->close(); if (!ok) { errUI << "Parsing error in " << filename << "! Aborting!" << endl << " In line: " << errorLine << ", column: " << errorColumn << endl << " Error message: " << errorMsg << endl; m_doc->setErrorMessage(i18n("Parsing error in %1 at line %2, column %3\nError message: %4" , filename , errorLine, errorColumn , - QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0, - QCoreApplication::UnicodeUTF8))); + QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0))); return false; } dbgUI << "File" << filename << " loaded and parsed"; return true; } bool KraConverter::loadXML(const KoXmlDocument &doc, KoStore *store) { Q_UNUSED(store); KoXmlElement root; KoXmlNode node; if (doc.doctype().name() != "DOC") { m_doc->setErrorMessage(i18n("The format is not supported or the file is corrupted")); return false; } root = doc.documentElement(); int syntaxVersion = root.attribute("syntaxVersion", "3").toInt(); if (syntaxVersion > 2) { m_doc->setErrorMessage(i18n("The file is too new for this version of Krita (%1).", syntaxVersion)); return false; } if (!root.hasChildNodes()) { m_doc->setErrorMessage(i18n("The file has no layers.")); return false; } m_kraLoader = new KisKraLoader(m_doc, syntaxVersion); // Legacy from the multi-image .kra file period. for (node = root.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isElement()) { if (node.nodeName() == "IMAGE") { KoXmlElement elem = node.toElement(); if (!(m_image = m_kraLoader->loadXML(elem))) { if (m_kraLoader->errorMessages().isEmpty()) { m_doc->setErrorMessage(i18n("Unknown error.")); } else { m_doc->setErrorMessage(m_kraLoader->errorMessages().join("\n")); } return false; } return true; } else { if (m_kraLoader->errorMessages().isEmpty()) { m_doc->setErrorMessage(i18n("The file does not contain an image.")); } return false; } } } return false; } bool KraConverter::completeLoading(KoStore* store) { if (!m_image) { if (m_kraLoader->errorMessages().isEmpty()) { m_doc->setErrorMessage(i18n("Unknown error.")); } else { m_doc->setErrorMessage(m_kraLoader->errorMessages().join("\n")); } return false; } m_image->blockUpdates(); m_kraLoader->loadBinaryData(store, m_image, m_doc->localFilePath(), true); m_image->unblockUpdates(); bool retval = true; if (!m_kraLoader->warningMessages().isEmpty()) { m_doc->setWarningMessage(m_kraLoader->warningMessages().join("\n")); retval = true; } if (retval) { m_activeNodes = m_kraLoader->selectedNodes(); m_assistants = m_kraLoader->assistants(); } return retval; } void KraConverter::cancel() { m_stop = true; } diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.cpp index 7ed4cb4b59..8157920dbf 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/dcrawsettingswidget.cpp @@ -1,1324 +1,1324 @@ /** =========================================================== * @file * * This file is a part of digiKam project * http://www.digikam.org * * @date 2006-09-13 * @brief LibRaw settings widgets * * @author Copyright (C) 2006-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2006-2011 by Marcel Wiesweg * marcel dot wiesweg at gmx dot de * @author Copyright (C) 2007-2008 by Guillaume Castagnino * casta at xwing dot info * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dcrawsettingswidget.h" // C++ includes #include // Qt includes #include #include #include #include #include #include #include -#include +#include // KDE includes #include // Local includes #include "kdcraw.h" #include "rnuminput.h" #include "rcombobox.h" #include "rexpanderbox.h" #include "libkdcraw_debug.h" #include namespace KDcrawIface { class Q_DECL_HIDDEN DcrawSettingsWidget::Private { public: Private() { autoBrightnessBox = 0; sixteenBitsImage = 0; fourColorCheckBox = 0; brightnessLabel = 0; brightnessSpinBox = 0; blackPointCheckBox = 0; blackPointSpinBox = 0; whitePointCheckBox = 0; whitePointSpinBox = 0; whiteBalanceComboBox = 0; whiteBalanceLabel = 0; customWhiteBalanceSpinBox = 0; customWhiteBalanceLabel = 0; customWhiteBalanceGreenSpinBox = 0; customWhiteBalanceGreenLabel = 0; unclipColorLabel = 0; dontStretchPixelsCheckBox = 0; RAWQualityComboBox = 0; RAWQualityLabel = 0; noiseReductionComboBox = 0; NRSpinBox1 = 0; NRSpinBox2 = 0; NRLabel1 = 0; NRLabel2 = 0; enableCACorrectionBox = 0; autoCACorrectionBox = 0; caRedMultSpinBox = 0; caBlueMultSpinBox = 0; caRedMultLabel = 0; caBlueMultLabel = 0; unclipColorComboBox = 0; reconstructLabel = 0; reconstructSpinBox = 0; outputColorSpaceLabel = 0; outputColorSpaceComboBox = 0; demosaicingSettings = 0; whiteBalanceSettings = 0; correctionsSettings = 0; colormanSettings = 0; medianFilterPassesSpinBox = 0; medianFilterPassesLabel = 0; inIccUrlEdit = 0; outIccUrlEdit = 0; inputColorSpaceLabel = 0; inputColorSpaceComboBox = 0; fixColorsHighlightsBox = 0; refineInterpolationBox = 0; noiseReductionLabel = 0; expoCorrectionBox = 0; expoCorrectionShiftSpinBox = 0; expoCorrectionHighlightSpinBox = 0; expoCorrectionShiftLabel = 0; expoCorrectionHighlightLabel = 0; } /** Convert Exposure correction shift E.V value from GUI to Linear value needs by libraw decoder. */ double shiftExpoFromEvToLinear(double ev) const { // From GUI : -2.0EV => 0.25 // +3.0EV => 8.00 return (1.55*ev + 3.35); } /** Convert Exposure correction shift Linear value from liraw decoder to E.V value needs by GUI. */ double shiftExpoFromLinearToEv(double lin) const { // From GUI : 0.25 => -2.0EV // 8.00 => +3.0EV return ((lin-3.35) / 1.55); } public: QWidget* demosaicingSettings; QWidget* whiteBalanceSettings; QWidget* correctionsSettings; QWidget* colormanSettings; QLabel* whiteBalanceLabel; QLabel* customWhiteBalanceLabel; QLabel* customWhiteBalanceGreenLabel; QLabel* brightnessLabel; QLabel* RAWQualityLabel; QLabel* NRLabel1; QLabel* NRLabel2; QLabel* caRedMultLabel; QLabel* caBlueMultLabel; QLabel* unclipColorLabel; QLabel* reconstructLabel; QLabel* inputColorSpaceLabel; QLabel* outputColorSpaceLabel; QLabel* medianFilterPassesLabel; QLabel* noiseReductionLabel; QLabel* expoCorrectionShiftLabel; QLabel* expoCorrectionHighlightLabel; QCheckBox* blackPointCheckBox; QCheckBox* whitePointCheckBox; QCheckBox* sixteenBitsImage; QCheckBox* autoBrightnessBox; QCheckBox* fourColorCheckBox; QCheckBox* dontStretchPixelsCheckBox; QCheckBox* enableCACorrectionBox; QCheckBox* autoCACorrectionBox; QCheckBox* fixColorsHighlightsBox; QCheckBox* refineInterpolationBox; QCheckBox* expoCorrectionBox; RFileSelector* inIccUrlEdit; RFileSelector* outIccUrlEdit; RComboBox* noiseReductionComboBox; RComboBox* whiteBalanceComboBox; RComboBox* RAWQualityComboBox; RComboBox* unclipColorComboBox; RComboBox* inputColorSpaceComboBox; RComboBox* outputColorSpaceComboBox; RIntNumInput* customWhiteBalanceSpinBox; RIntNumInput* reconstructSpinBox; RIntNumInput* blackPointSpinBox; RIntNumInput* whitePointSpinBox; RIntNumInput* NRSpinBox1; RIntNumInput* NRSpinBox2; RIntNumInput* medianFilterPassesSpinBox; RDoubleNumInput* customWhiteBalanceGreenSpinBox; RDoubleNumInput* caRedMultSpinBox; RDoubleNumInput* caBlueMultSpinBox; RDoubleNumInput* brightnessSpinBox; RDoubleNumInput* expoCorrectionShiftSpinBox; RDoubleNumInput* expoCorrectionHighlightSpinBox; }; DcrawSettingsWidget::DcrawSettingsWidget(QWidget* const parent, int advSettings) : RExpanderBox(parent), d(new Private) { setup(advSettings); } void DcrawSettingsWidget::setup(int advSettings) { setObjectName( QLatin1String("DCRawSettings Expander" )); // --------------------------------------------------------------- // DEMOSAICING Settings panel d->demosaicingSettings = new QWidget(this); QGridLayout* const demosaicingLayout = new QGridLayout(d->demosaicingSettings); int line = 0; d->sixteenBitsImage = new QCheckBox(i18nc("@option:check", "16 bits color depth"), d->demosaicingSettings); d->sixteenBitsImage->setToolTip(i18nc("@info:whatsthis", "

    If enabled, all RAW files will " "be decoded in 16-bit color depth using a linear gamma curve. To " "prevent dark picture rendering in the editor, it is recommended to " "use Color Management in this mode.

    " "

    If disabled, all RAW files will be decoded in 8-bit color " "depth with a BT.709 gamma curve and a 99th-percentile white point. " "This mode is faster than 16-bit decoding.

    ")); demosaicingLayout->addWidget(d->sixteenBitsImage, 0, 0, 1, 2); if (advSettings & SIXTEENBITS) { d->sixteenBitsImage->show(); line = 1; } else { d->sixteenBitsImage->hide(); } d->fourColorCheckBox = new QCheckBox(i18nc("@option:check", "Interpolate RGB as four colors"), d->demosaicingSettings); d->fourColorCheckBox->setToolTip(i18nc("@info:whatsthis", "Interpolate RGB as four " "colors" "

    The default is to assume that all green pixels are the same. " "If even-row green pixels are more sensitive to ultraviolet light " "than odd-row this difference causes a mesh pattern in the output; " "using this option solves this problem with minimal loss of detail.

    " "

    To resume, this option blurs the image a little, but it " "eliminates false 2x2 mesh patterns with VNG quality method or " "mazes with AHD quality method.

    ")); demosaicingLayout->addWidget(d->fourColorCheckBox, line, 0, 1, line == 0 ? 2 : 3); line++; QLabel* const dcrawVersion = new QLabel(d->demosaicingSettings); dcrawVersion->setAlignment(Qt::AlignRight); dcrawVersion->setToolTip(i18nc("@info:tooltip", "Visit LibRaw project website")); dcrawVersion->setOpenExternalLinks(true); dcrawVersion->setTextFormat(Qt::RichText); dcrawVersion->setTextInteractionFlags(Qt::LinksAccessibleByMouse); dcrawVersion->setText(QString::fromLatin1("%2") .arg(QLatin1String("http://www.libraw.org")) .arg(QString::fromLatin1("libraw %1").arg(KDcraw::librawVersion()))); demosaicingLayout->addWidget(dcrawVersion, 0, 2, 1, 1); d->dontStretchPixelsCheckBox = new QCheckBox(i18nc("@option:check", "Do not stretch or rotate pixels"), d->demosaicingSettings); d->dontStretchPixelsCheckBox->setToolTip(i18nc("@info:whatsthis", "Do not stretch or rotate pixels" "

    For Fuji Super CCD cameras, show the image tilted 45 degrees. " "For cameras with non-square pixels, do not stretch the image to " "its correct aspect ratio. In any case, this option guarantees that " "each output pixel corresponds to one RAW pixel.

    ")); demosaicingLayout->addWidget(d->dontStretchPixelsCheckBox, line, 0, 1, 3); line++; d->RAWQualityLabel = new QLabel(i18nc("@label:listbox", "Quality:"), d->demosaicingSettings); d->RAWQualityComboBox = new RComboBox(d->demosaicingSettings); // Original dcraw demosaicing methods d->RAWQualityComboBox->insertItem(RawDecodingSettings::BILINEAR, i18nc("@item:inlistbox Quality", "Bilinear")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::VNG, i18nc("@item:inlistbox Quality", "VNG")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::PPG, i18nc("@item:inlistbox Quality", "PPG")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::AHD, i18nc("@item:inlistbox Quality", "AHD")); // Extended demosaicing method from GPL2 pack d->RAWQualityComboBox->insertItem(RawDecodingSettings::DCB, i18nc("@item:inlistbox Quality", "DCB")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::PL_AHD, i18nc("@item:inlistbox Quality", "AHD v2")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::AFD, i18nc("@item:inlistbox Quality", "AFD")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::VCD, i18nc("@item:inlistbox Quality", "VCD")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::VCD_AHD, i18nc("@item:inlistbox Quality", "VCD & AHD")); d->RAWQualityComboBox->insertItem(RawDecodingSettings::LMMSE, i18nc("@item:inlistbox Quality", "LMMSE")); // Extended demosaicing method from GPL3 pack d->RAWQualityComboBox->insertItem(RawDecodingSettings::AMAZE, i18nc("@item:inlistbox Quality", "AMaZE")); // If Libraw do not support GPL2 pack, disable entries relevant. if (!KDcraw::librawUseGPL2DemosaicPack()) { for (int i=RawDecodingSettings::DCB ; i <=RawDecodingSettings::LMMSE ; ++i) d->RAWQualityComboBox->combo()->setItemData(i, false, Qt::UserRole-1); } // If Libraw do not support GPL3 pack, disable entries relevant. if (!KDcraw::librawUseGPL3DemosaicPack()) { d->RAWQualityComboBox->combo()->setItemData(RawDecodingSettings::AMAZE, false, Qt::UserRole-1); } d->RAWQualityComboBox->setDefaultIndex(RawDecodingSettings::BILINEAR); d->RAWQualityComboBox->setCurrentIndex(RawDecodingSettings::BILINEAR); d->RAWQualityComboBox->setToolTip(i18nc("@info:whatsthis", "Quality (interpolation)" "

    Select here the demosaicing method to use when decoding RAW " "images. A demosaicing algorithm is a digital image process used to " "interpolate a complete image from the partial raw data received " "from the color-filtered image sensor, internal to many digital " "cameras, in form of a matrix of colored pixels. Also known as CFA " "interpolation or color reconstruction, another common spelling is " "demosaicing. The following methods are available for demosaicing " "RAW images:

    " // Original dcraw demosaicing methods "

    • Bilinear: use " "high-speed but low-quality bilinear interpolation (default - for " "slow computers). In this method, the red value of a non-red pixel " "is computed as the average of the adjacent red pixels, and similarly " "for blue and green.
    • " "
    • VNG: use Variable Number " "of Gradients interpolation. This method computes gradients near " "the pixel of interest and uses the lower gradients (representing " "smoother and more similar parts of the image) to make an estimate.
    • " "
    • PPG: use Patterned-Pixel-" "Grouping interpolation. Pixel Grouping uses assumptions about " "natural scenery in making estimates. It has fewer color artifacts " "on natural images than the Variable Number of Gradients method.
    • " "
    • AHD: use Adaptive " "Homogeneity-Directed interpolation. This method selects the " "direction of interpolation so as to maximize a homogeneity metric, " "thus typically minimizing color artifacts.
    • " // Extended demosaicing method "
    • DCB: DCB interpolation from " "linuxphoto.org project.
    • " "
    • AHD v2: modified AHD " "interpolation using Variance of Color Differences method.
    • " "
    • AFD: Adaptive Filtered " "Demosaicing interpolation through 5 pass median filter from PerfectRaw " "project.
    • " "
    • VCD: Variance of Color " "Differences interpolation.
    • " "
    • VCD & AHD: Mixed demosaicing " "between VCD and AHD.
    • " "
    • LMMSE: color demosaicing via " "directional linear minimum mean-square error estimation interpolation " "from PerfectRaw.
    • " "
    • AMaZE: Aliasing Minimization " "interpolation and Zipper Elimination to apply color aberration removal " "from RawTherapee project.

    " "

    Note: some methods can be unavailable if RAW decoder have been built " "without extension packs.

    ")); demosaicingLayout->addWidget(d->RAWQualityLabel, line, 0, 1, 1); demosaicingLayout->addWidget(d->RAWQualityComboBox, line, 1, 1, 2); line++; d->medianFilterPassesSpinBox = new RIntNumInput(d->demosaicingSettings); d->medianFilterPassesSpinBox->setRange(0, 10, 1); d->medianFilterPassesSpinBox->setDefaultValue(0); d->medianFilterPassesLabel = new QLabel(i18nc("@label:slider", "Pass:"), d->whiteBalanceSettings); d->medianFilterPassesSpinBox->setToolTip( i18nc("@info:whatsthis", "Pass" "

    Set here the passes used by the median filter applied after " "interpolation to Red-Green and Blue-Green channels.

    " "

    This setting is only available for specific Quality options: " "Bilinear, " "VNG, PPG, " "AHD, " "DCB, and VCD & AHD.

    ")); demosaicingLayout->addWidget(d->medianFilterPassesLabel, line, 0, 1, 1); demosaicingLayout->addWidget(d->medianFilterPassesSpinBox, line, 1, 1, 2); line++; d->refineInterpolationBox = new QCheckBox(i18nc("@option:check", "Refine interpolation"), d->demosaicingSettings); d->refineInterpolationBox->setToolTip(i18nc("@info:whatsthis", "Refine interpolation" "

    This setting is available only for few Quality options:

    " "

    • DCB: turn on " "the enhance interpolated colors filter.
    • " "
    • VCD & AHD: turn on the " "enhanced effective color interpolation (EECI) refine to improve " "sharpness.

    ")); demosaicingLayout->addWidget(d->refineInterpolationBox, line, 0, 1, 2); // If Libraw do not support GPL2 pack, disable options relevant. if (!KDcraw::librawUseGPL2DemosaicPack()) { d->medianFilterPassesLabel->setEnabled(false); d->medianFilterPassesSpinBox->setEnabled(false); d->refineInterpolationBox->setEnabled(false); } addItem(d->demosaicingSettings, KisIconUtils::loadIcon("kdcraw").pixmap(16, 16), i18nc("@label", "Demosaicing"), QString("demosaicing"), true); // --------------------------------------------------------------- // WHITE BALANCE Settings Panel d->whiteBalanceSettings = new QWidget(this); QGridLayout* const whiteBalanceLayout = new QGridLayout(d->whiteBalanceSettings); d->whiteBalanceLabel = new QLabel(i18nc("@label:listbox", "Method:"), d->whiteBalanceSettings); d->whiteBalanceComboBox = new RComboBox(d->whiteBalanceSettings); d->whiteBalanceComboBox->insertItem(RawDecodingSettings::NONE, i18nc("@item:inlistbox", "Default D65")); d->whiteBalanceComboBox->insertItem(RawDecodingSettings::CAMERA, i18nc("@item:inlistbox", "Camera")); d->whiteBalanceComboBox->insertItem(RawDecodingSettings::AUTO, i18nc("@item:inlistbox set while balance automatically", "Automatic")); d->whiteBalanceComboBox->insertItem(RawDecodingSettings::CUSTOM, i18nc("@item:inlistbox set white balance manually", "Manual")); d->whiteBalanceComboBox->setDefaultIndex(RawDecodingSettings::CAMERA); d->whiteBalanceComboBox->setToolTip(i18nc("@info:whatsthis", "White Balance" "

    Configure the raw white balance:

    " "

    • Default D65: " "Use a standard daylight D65 white balance.
    • " "
    • Camera: Use the white " "balance specified by the camera. If not available, reverts to " "default neutral white balance.
    • " "
    • Automatic: Calculates an " "automatic white balance averaging the entire image.
    • " "
    • Manual: Set a custom " "temperature and green level values.

    ")); d->customWhiteBalanceSpinBox = new RIntNumInput(d->whiteBalanceSettings); d->customWhiteBalanceSpinBox->setRange(2000, 12000, 10); d->customWhiteBalanceSpinBox->setDefaultValue(6500); d->customWhiteBalanceLabel = new QLabel(i18nc("@label:slider", "T(K):"), d->whiteBalanceSettings); d->customWhiteBalanceSpinBox->setToolTip( i18nc("@info:whatsthis", "Temperature" "

    Set here the color temperature in Kelvin.

    ")); d->customWhiteBalanceGreenSpinBox = new RDoubleNumInput(d->whiteBalanceSettings); d->customWhiteBalanceGreenSpinBox->setDecimals(2); d->customWhiteBalanceGreenSpinBox->setRange(0.2, 2.5, 0.01); d->customWhiteBalanceGreenSpinBox->setDefaultValue(1.0); d->customWhiteBalanceGreenLabel = new QLabel(i18nc("@label:slider Green component", "Green:"), d->whiteBalanceSettings); d->customWhiteBalanceGreenSpinBox->setToolTip(i18nc("@info:whatsthis", "

    Set here the " "green component to set magenta color cast removal level.

    ")); d->unclipColorLabel = new QLabel(i18nc("@label:listbox", "Highlights:"), d->whiteBalanceSettings); d->unclipColorComboBox = new RComboBox(d->whiteBalanceSettings); d->unclipColorComboBox->insertItem(0, i18nc("@item:inlistbox", "Solid white")); d->unclipColorComboBox->insertItem(1, i18nc("@item:inlistbox", "Unclip")); d->unclipColorComboBox->insertItem(2, i18nc("@item:inlistbox", "Blend")); d->unclipColorComboBox->insertItem(3, i18nc("@item:inlistbox", "Rebuild")); d->unclipColorComboBox->setDefaultIndex(0); d->unclipColorComboBox->setToolTip(i18nc("@info:whatsthis", "Highlights" "

    Select here the highlight clipping method:

    " "

    • Solid white: " "clip all highlights to solid white
    • " "
    • Unclip: leave highlights " "unclipped in various shades of pink
    • " "
    • Blend:Blend clipped and " "unclipped values together for a gradual fade to white
    • " "
    • Rebuild: reconstruct " "highlights using a level value

    ")); d->reconstructLabel = new QLabel(i18nc("@label:slider Highlight reconstruct level", "Level:"), d->whiteBalanceSettings); d->reconstructSpinBox = new RIntNumInput(d->whiteBalanceSettings); d->reconstructSpinBox->setRange(0, 6, 1); d->reconstructSpinBox->setDefaultValue(0); d->reconstructSpinBox->setToolTip(i18nc("@info:whatsthis", "Level" "

    Specify the reconstruct highlight level. Low values favor " "whites and high values favor colors.

    ")); d->expoCorrectionBox = new QCheckBox(i18nc("@option:check", "Exposure Correction (E.V)"), d->whiteBalanceSettings); d->expoCorrectionBox->setToolTip(i18nc("@info:whatsthis", "

    Turn on the exposure " "correction before interpolation.

    ")); d->expoCorrectionShiftLabel = new QLabel(i18nc("@label:slider", "Linear Shift:"), d->whiteBalanceSettings); d->expoCorrectionShiftSpinBox = new RDoubleNumInput(d->whiteBalanceSettings); d->expoCorrectionShiftSpinBox->setDecimals(2); d->expoCorrectionShiftSpinBox->setRange(-2.0, 3.0, 0.01); d->expoCorrectionShiftSpinBox->setDefaultValue(0.0); d->expoCorrectionShiftSpinBox->setToolTip(i18nc("@info:whatsthis", "Shift" "

    Linear Shift of exposure correction before interpolation in E.V

    ")); d->expoCorrectionHighlightLabel = new QLabel(i18nc("@label:slider", "Highlight:"), d->whiteBalanceSettings); d->expoCorrectionHighlightSpinBox = new RDoubleNumInput(d->whiteBalanceSettings); d->expoCorrectionHighlightSpinBox->setDecimals(2); d->expoCorrectionHighlightSpinBox->setRange(0.0, 1.0, 0.01); d->expoCorrectionHighlightSpinBox->setDefaultValue(0.0); d->expoCorrectionHighlightSpinBox->setToolTip(i18nc("@info:whatsthis", "Highlight" "

    Amount of highlight preservation for exposure correction " "before interpolation in E.V. Only take effect if Shift Correction is > 1.0 E.V

    ")); d->fixColorsHighlightsBox = new QCheckBox(i18nc("@option:check", "Correct false colors in highlights"), d->whiteBalanceSettings); d->fixColorsHighlightsBox->setToolTip(i18nc("@info:whatsthis", "

    If enabled, images with " "overblown channels are processed much more accurately, without " "'pink clouds' (and blue highlights under tungsten lamps).

    ")); d->autoBrightnessBox = new QCheckBox(i18nc("@option:check", "Auto Brightness"), d->whiteBalanceSettings); d->autoBrightnessBox->setToolTip(i18nc("@info:whatsthis", "

    If disable, use a fixed white level " "and ignore the image histogram to adjust brightness.

    ")); d->brightnessLabel = new QLabel(i18nc("@label:slider", "Brightness:"), d->whiteBalanceSettings); d->brightnessSpinBox = new RDoubleNumInput(d->whiteBalanceSettings); d->brightnessSpinBox->setDecimals(2); d->brightnessSpinBox->setRange(0.0, 10.0, 0.01); d->brightnessSpinBox->setDefaultValue(1.0); d->brightnessSpinBox->setToolTip(i18nc("@info:whatsthis", "Brightness" "

    Specify the brightness level of output image. The default " "value is 1.0 (works in 8-bit mode only).

    ")); if (! (advSettings & POSTPROCESSING)) { d->brightnessLabel->hide(); d->brightnessSpinBox->hide(); } d->blackPointCheckBox = new QCheckBox(i18nc("@option:check Black point", "Black:"), d->whiteBalanceSettings); d->blackPointCheckBox->setToolTip(i18nc("@info:whatsthis", "Black point" "

    Use a specific black point value to decode RAW pictures. If " "you set this option to off, the Black Point value will be " "automatically computed.

    ")); d->blackPointSpinBox = new RIntNumInput(d->whiteBalanceSettings); d->blackPointSpinBox->setRange(0, 1000, 1); d->blackPointSpinBox->setDefaultValue(0); d->blackPointSpinBox->setToolTip(i18nc("@info:whatsthis", "Black point value" "

    Specify specific black point value of the output image.

    ")); d->whitePointCheckBox = new QCheckBox(i18nc("@option:check White point", "White:"), d->whiteBalanceSettings); d->whitePointCheckBox->setToolTip(i18nc("@info:whatsthis", "White point" "

    Use a specific white point value to decode RAW pictures. If " "you set this option to off, the White Point value will be " "automatically computed.

    ")); d->whitePointSpinBox = new RIntNumInput(d->whiteBalanceSettings); d->whitePointSpinBox->setRange(0, 20000, 1); d->whitePointSpinBox->setDefaultValue(0); d->whitePointSpinBox->setToolTip(i18nc("@info:whatsthis", "White point value" "

    Specify specific white point value of the output image.

    ")); if (! (advSettings & BLACKWHITEPOINTS)) { d->blackPointCheckBox->hide(); d->blackPointSpinBox->hide(); d->whitePointCheckBox->hide(); d->whitePointSpinBox->hide(); } whiteBalanceLayout->addWidget(d->whiteBalanceLabel, 0, 0, 1, 1); whiteBalanceLayout->addWidget(d->whiteBalanceComboBox, 0, 1, 1, 2); whiteBalanceLayout->addWidget(d->customWhiteBalanceLabel, 1, 0, 1, 1); whiteBalanceLayout->addWidget(d->customWhiteBalanceSpinBox, 1, 1, 1, 2); whiteBalanceLayout->addWidget(d->customWhiteBalanceGreenLabel, 2, 0, 1, 1); whiteBalanceLayout->addWidget(d->customWhiteBalanceGreenSpinBox, 2, 1, 1, 2); whiteBalanceLayout->addWidget(d->unclipColorLabel, 3, 0, 1, 1); whiteBalanceLayout->addWidget(d->unclipColorComboBox, 3, 1, 1, 2); whiteBalanceLayout->addWidget(d->reconstructLabel, 4, 0, 1, 1); whiteBalanceLayout->addWidget(d->reconstructSpinBox, 4, 1, 1, 2); whiteBalanceLayout->addWidget(d->expoCorrectionBox, 5, 0, 1, 2); whiteBalanceLayout->addWidget(d->expoCorrectionShiftLabel, 6, 0, 1, 1); whiteBalanceLayout->addWidget(d->expoCorrectionShiftSpinBox, 6, 1, 1, 2); whiteBalanceLayout->addWidget(d->expoCorrectionHighlightLabel, 7, 0, 1, 1); whiteBalanceLayout->addWidget(d->expoCorrectionHighlightSpinBox, 7, 1, 1, 2); whiteBalanceLayout->addWidget(d->fixColorsHighlightsBox, 8, 0, 1, 3); whiteBalanceLayout->addWidget(d->autoBrightnessBox, 9, 0, 1, 3); whiteBalanceLayout->addWidget(d->brightnessLabel, 10, 0, 1, 1); whiteBalanceLayout->addWidget(d->brightnessSpinBox, 10, 1, 1, 2); whiteBalanceLayout->addWidget(d->blackPointCheckBox, 11, 0, 1, 1); whiteBalanceLayout->addWidget(d->blackPointSpinBox, 11, 1, 1, 2); whiteBalanceLayout->addWidget(d->whitePointCheckBox, 12, 0, 1, 1); whiteBalanceLayout->addWidget(d->whitePointSpinBox, 12, 1, 1, 2); whiteBalanceLayout->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); whiteBalanceLayout->setMargin(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); addItem(d->whiteBalanceSettings, KisIconUtils::loadIcon("kdcraw").pixmap(16, 16), i18nc("@label", "White Balance"), QString("whitebalance"), true); // --------------------------------------------------------------- // CORRECTIONS Settings panel d->correctionsSettings = new QWidget(this); QGridLayout* const correctionsLayout = new QGridLayout(d->correctionsSettings); d->noiseReductionLabel = new QLabel(i18nc("@label:listbox", "Noise reduction:"), d->correctionsSettings); d->noiseReductionComboBox = new RComboBox(d->colormanSettings); d->noiseReductionComboBox->insertItem(RawDecodingSettings::NONR, i18nc("@item:inlistbox Noise Reduction", "None")); d->noiseReductionComboBox->insertItem(RawDecodingSettings::WAVELETSNR, i18nc("@item:inlistbox Noise Reduction", "Wavelets")); d->noiseReductionComboBox->insertItem(RawDecodingSettings::FBDDNR, i18nc("@item:inlistbox Noise Reduction", "FBDD")); d->noiseReductionComboBox->insertItem(RawDecodingSettings::LINENR, i18nc("@item:inlistbox Noise Reduction", "CFA Line Denoise")); d->noiseReductionComboBox->insertItem(RawDecodingSettings::IMPULSENR, i18nc("@item:inlistbox Noise Reduction", "Impulse Denoise")); d->noiseReductionComboBox->setDefaultIndex(RawDecodingSettings::NONR); d->noiseReductionComboBox->setToolTip(i18nc("@info:whatsthis", "Noise Reduction" "

    Select here the noise reduction method to apply during RAW " "decoding.

    " "

    • None: no " "noise reduction.
    • " "
    • Wavelets: wavelets " "correction to erase noise while preserving real detail. It's " "applied after interpolation.
    • " "
    • FBDD: Fake Before " "Demosaicing Denoising noise reduction. It's applied before " "interpolation.
    • " "
    • CFA Line Denoise: Banding " "noise suppression. It's applied after interpolation.
    • " "
    • Impulse Denoise: Impulse " "noise suppression. It's applied after interpolation.

    ")); d->NRSpinBox1 = new RIntNumInput(d->correctionsSettings); d->NRSpinBox1->setRange(100, 1000, 1); d->NRSpinBox1->setDefaultValue(100); d->NRLabel1 = new QLabel(d->correctionsSettings); d->NRSpinBox2 = new RIntNumInput(d->correctionsSettings); d->NRSpinBox2->setRange(100, 1000, 1); d->NRSpinBox2->setDefaultValue(100); d->NRLabel2 = new QLabel(d->correctionsSettings); d->enableCACorrectionBox = new QCheckBox(i18nc("@option:check", "Enable Chromatic Aberration correction"), d->correctionsSettings); d->enableCACorrectionBox->setToolTip(i18nc("@info:whatsthis", "Enable Chromatic " "Aberration correction" "

    Enlarge the raw red-green and blue-yellow axis by the given " "factors (automatic by default).

    ")); d->autoCACorrectionBox = new QCheckBox(i18nc("@option:check", "Automatic color axis adjustments"), d->correctionsSettings); d->autoCACorrectionBox->setToolTip(i18nc("@info:whatsthis", "Automatic Chromatic " "Aberration correction" "

    If this option is turned on, it will try to shift image " "channels slightly and evaluate Chromatic Aberration change. Note " "that if you shot blue-red pattern, the method may fail. In this " "case, disable this option and tune manually color factors.

    ")); d->caRedMultLabel = new QLabel(i18nc("@label:slider", "Red-Green:"), d->correctionsSettings); d->caRedMultSpinBox = new RDoubleNumInput(d->correctionsSettings); d->caRedMultSpinBox->setDecimals(1); d->caRedMultSpinBox->setRange(-4.0, 4.0, 0.1); d->caRedMultSpinBox->setDefaultValue(0.0); d->caRedMultSpinBox->setToolTip(i18nc("@info:whatsthis", "Red-Green multiplier" "

    Set here the amount of correction on red-green axis

    ")); d->caBlueMultLabel = new QLabel(i18nc("@label:slider", "Blue-Yellow:"), d->correctionsSettings); d->caBlueMultSpinBox = new RDoubleNumInput(d->correctionsSettings); d->caBlueMultSpinBox->setDecimals(1); d->caBlueMultSpinBox->setRange(-4.0, 4.0, 0.1); d->caBlueMultSpinBox->setDefaultValue(0.0); d->caBlueMultSpinBox->setToolTip(i18nc("@info:whatsthis", "Blue-Yellow multiplier" "

    Set here the amount of correction on blue-yellow axis

    ")); correctionsLayout->addWidget(d->noiseReductionLabel, 0, 0, 1, 1); correctionsLayout->addWidget(d->noiseReductionComboBox, 0, 1, 1, 2); correctionsLayout->addWidget(d->NRLabel1, 1, 0, 1, 1); correctionsLayout->addWidget(d->NRSpinBox1, 1, 1, 1, 2); correctionsLayout->addWidget(d->NRLabel2, 2, 0, 1, 1); correctionsLayout->addWidget(d->NRSpinBox2, 2, 1, 1, 2); correctionsLayout->addWidget(d->enableCACorrectionBox, 3, 0, 1, 3); correctionsLayout->addWidget(d->autoCACorrectionBox, 4, 0, 1, 3); correctionsLayout->addWidget(d->caRedMultLabel, 5, 0, 1, 1); correctionsLayout->addWidget(d->caRedMultSpinBox, 5, 1, 1, 2); correctionsLayout->addWidget(d->caBlueMultLabel, 6, 0, 1, 1); correctionsLayout->addWidget(d->caBlueMultSpinBox, 6, 1, 1, 2); correctionsLayout->setRowStretch(7, 10); correctionsLayout->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); correctionsLayout->setMargin(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); addItem(d->correctionsSettings, KisIconUtils::loadIcon("kdcraw").pixmap(16, 16), i18nc("@label", "Corrections"), QString("corrections"), false); // --------------------------------------------------------------- // COLOR MANAGEMENT Settings panel d->colormanSettings = new QWidget(this); QGridLayout* const colormanLayout = new QGridLayout(d->colormanSettings); d->inputColorSpaceLabel = new QLabel(i18nc("@label:listbox", "Camera Profile:"), d->colormanSettings); d->inputColorSpaceComboBox = new RComboBox(d->colormanSettings); d->inputColorSpaceComboBox->insertItem(RawDecodingSettings::NOINPUTCS, i18nc("@item:inlistbox Camera Profile", "None")); d->inputColorSpaceComboBox->insertItem(RawDecodingSettings::EMBEDDED, i18nc("@item:inlistbox Camera Profile", "Embedded")); d->inputColorSpaceComboBox->insertItem(RawDecodingSettings::CUSTOMINPUTCS, i18nc("@item:inlistbox Camera Profile", "Custom")); d->inputColorSpaceComboBox->setDefaultIndex(RawDecodingSettings::NOINPUTCS); d->inputColorSpaceComboBox->setToolTip(i18nc("@info:whatsthis", "Camera Profile" "

    Select here the input color space used to decode RAW data.

    " "

    • None: no " "input color profile is used during RAW decoding.
    • " "
    • Embedded: use embedded " "color profile from RAW file, if it exists.
    • " "
    • Custom: use a custom " "input color space profile.

    ")); d->inIccUrlEdit = new RFileSelector(d->colormanSettings); d->inIccUrlEdit->setFileDlgMode(QFileDialog::ExistingFile); d->inIccUrlEdit->setFileDlgFilter(i18n("ICC Files (*.icc; *.icm)")); d->outputColorSpaceLabel = new QLabel(i18nc("@label:listbox", "Workspace:"), d->colormanSettings); d->outputColorSpaceComboBox = new RComboBox( d->colormanSettings ); d->outputColorSpaceComboBox->insertItem(RawDecodingSettings::RAWCOLOR, i18nc("@item:inlistbox Workspace", "Raw (no profile)")); d->outputColorSpaceComboBox->insertItem(RawDecodingSettings::SRGB, i18nc("@item:inlistbox Workspace", "sRGB")); d->outputColorSpaceComboBox->insertItem(RawDecodingSettings::ADOBERGB, i18nc("@item:inlistbox Workspace", "Adobe RGB")); d->outputColorSpaceComboBox->insertItem(RawDecodingSettings::WIDEGAMMUT, i18nc("@item:inlistbox Workspace", "Wide Gamut")); d->outputColorSpaceComboBox->insertItem(RawDecodingSettings::PROPHOTO, i18nc("@item:inlistbox Workspace", "Pro-Photo")); d->outputColorSpaceComboBox->insertItem(RawDecodingSettings::CUSTOMOUTPUTCS, i18nc("@item:inlistbox Workspace", "Custom")); d->outputColorSpaceComboBox->setDefaultIndex(RawDecodingSettings::SRGB); d->outputColorSpaceComboBox->setToolTip(i18nc("@info:whatsthis", "Workspace" "

    Select here the output color space used to decode RAW data.

    " "

    • Raw (linear): " "in this mode, no output color space is used during RAW decoding.
    • " "
    • sRGB: this is an RGB " "color space, created cooperatively by Hewlett-Packard and " "Microsoft. It is the best choice for images destined for the Web " "and portrait photography.
    • " "
    • Adobe RGB: this color " "space is an extended RGB color space, developed by Adobe. It is " "used for photography applications such as advertising and fine " "art.
    • " "
    • Wide Gamut: this color " "space is an expanded version of the Adobe RGB color space.
    • " "
    • Pro-Photo: this color " "space is an RGB color space, developed by Kodak, that offers an " "especially large gamut designed for use with photographic outputs " "in mind.
    • " "
    • Custom: use a custom " "output color space profile.

    ")); d->outIccUrlEdit = new RFileSelector(d->colormanSettings); d->outIccUrlEdit->setFileDlgMode(QFileDialog::ExistingFile); d->outIccUrlEdit->setFileDlgFilter(i18n("ICC Files (*.icc; *.icm)")); colormanLayout->addWidget(d->inputColorSpaceLabel, 0, 0, 1, 1); colormanLayout->addWidget(d->inputColorSpaceComboBox, 0, 1, 1, 2); colormanLayout->addWidget(d->inIccUrlEdit, 1, 0, 1, 3); colormanLayout->addWidget(d->outputColorSpaceLabel, 2, 0, 1, 1); colormanLayout->addWidget(d->outputColorSpaceComboBox, 2, 1, 1, 2); colormanLayout->addWidget(d->outIccUrlEdit, 3, 0, 1, 3); colormanLayout->setRowStretch(4, 10); colormanLayout->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); colormanLayout->setMargin(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); addItem(d->colormanSettings, KisIconUtils::loadIcon("kdcraw").pixmap(16, 16), i18nc("@label", "Color Management"), QString("colormanagement"), false); if (! (advSettings & COLORSPACE)) removeItem(COLORMANAGEMENT); addStretch(); // --------------------------------------------------------------- connect(d->unclipColorComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::slotUnclipColorActivated); connect(d->whiteBalanceComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::slotWhiteBalanceToggled); connect(d->noiseReductionComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::slotNoiseReductionChanged); connect(d->enableCACorrectionBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::slotCACorrectionToggled); connect(d->autoCACorrectionBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::slotAutoCAToggled); connect(d->blackPointCheckBox, SIGNAL(toggled(bool)), d->blackPointSpinBox, SLOT(setEnabled(bool))); connect(d->whitePointCheckBox, SIGNAL(toggled(bool)), d->whitePointSpinBox, SLOT(setEnabled(bool))); connect(d->sixteenBitsImage, &QCheckBox::toggled, this, &DcrawSettingsWidget::slotsixteenBitsImageToggled); connect(d->inputColorSpaceComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::slotInputColorSpaceChanged); connect(d->outputColorSpaceComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::slotOutputColorSpaceChanged); connect(d->expoCorrectionBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::slotExposureCorrectionToggled); connect(d->expoCorrectionShiftSpinBox, &RDoubleNumInput::valueChanged, this, &DcrawSettingsWidget::slotExpoCorrectionShiftChanged); // Wrapper to emit signal when something is changed in settings. connect(d->inIccUrlEdit->lineEdit(), &QLineEdit::textChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->outIccUrlEdit->lineEdit(), &QLineEdit::textChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->whiteBalanceComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->RAWQualityComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::slotRAWQualityChanged); connect(d->unclipColorComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->inputColorSpaceComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->outputColorSpaceComboBox, static_cast(&RComboBox::activated), this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->blackPointCheckBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->whitePointCheckBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->sixteenBitsImage, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->fixColorsHighlightsBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->autoBrightnessBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->fourColorCheckBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->dontStretchPixelsCheckBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->refineInterpolationBox, &QCheckBox::toggled, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->customWhiteBalanceSpinBox, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->reconstructSpinBox, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->blackPointSpinBox, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->whitePointSpinBox, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->NRSpinBox1, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->NRSpinBox2, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->medianFilterPassesSpinBox, &RIntNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->customWhiteBalanceGreenSpinBox, &RDoubleNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->caRedMultSpinBox, &RDoubleNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->caBlueMultSpinBox, &RDoubleNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->brightnessSpinBox, &RDoubleNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); connect(d->expoCorrectionHighlightSpinBox, &RDoubleNumInput::valueChanged, this, &DcrawSettingsWidget::signalSettingsChanged); } DcrawSettingsWidget::~DcrawSettingsWidget() { delete d; } void DcrawSettingsWidget::updateMinimumWidth() { int width = 0; for (int i = 0; i < count(); i++) { if (widget(i)->width() > width) width = widget(i)->width(); } setMinimumWidth(width); } RFileSelector* DcrawSettingsWidget::inputProfileUrlEdit() const { return d->inIccUrlEdit; } RFileSelector* DcrawSettingsWidget::outputProfileUrlEdit() const { return d->outIccUrlEdit; } void DcrawSettingsWidget::resetToDefault() { setSettings(RawDecodingSettings()); } void DcrawSettingsWidget::slotsixteenBitsImageToggled(bool b) { setEnabledBrightnessSettings(!b); emit signalSixteenBitsImageToggled(d->sixteenBitsImage->isChecked()); } void DcrawSettingsWidget::slotWhiteBalanceToggled(int v) { if (v == 3) { d->customWhiteBalanceSpinBox->setEnabled(true); d->customWhiteBalanceGreenSpinBox->setEnabled(true); d->customWhiteBalanceLabel->setEnabled(true); d->customWhiteBalanceGreenLabel->setEnabled(true); } else { d->customWhiteBalanceSpinBox->setEnabled(false); d->customWhiteBalanceGreenSpinBox->setEnabled(false); d->customWhiteBalanceLabel->setEnabled(false); d->customWhiteBalanceGreenLabel->setEnabled(false); } } void DcrawSettingsWidget::slotUnclipColorActivated(int v) { if (v == 3) // Reconstruct Highlight method { d->reconstructLabel->setEnabled(true); d->reconstructSpinBox->setEnabled(true); } else { d->reconstructLabel->setEnabled(false); d->reconstructSpinBox->setEnabled(false); } } void DcrawSettingsWidget::slotNoiseReductionChanged(int item) { d->NRSpinBox1->setEnabled(true); d->NRLabel1->setEnabled(true); d->NRSpinBox2->setEnabled(true); d->NRLabel2->setEnabled(true); d->NRLabel1->setText(i18nc("@label", "Threshold:")); d->NRSpinBox1->setToolTip(i18nc("@info:whatsthis", "Threshold" "

    Set here the noise reduction threshold value to use.

    ")); switch(item) { case RawDecodingSettings::WAVELETSNR: case RawDecodingSettings::FBDDNR: case RawDecodingSettings::LINENR: d->NRSpinBox2->setVisible(false); d->NRLabel2->setVisible(false); break; case RawDecodingSettings::IMPULSENR: d->NRLabel1->setText(i18nc("@label", "Luminance:")); d->NRSpinBox1->setToolTip(i18nc("@info:whatsthis", "Luminance" "

    Amount of Luminance impulse noise reduction.

    ")); d->NRLabel2->setText(i18nc("@label", "Chrominance:")); d->NRSpinBox2->setToolTip(i18nc("@info:whatsthis", "Chrominance" "

    Amount of Chrominance impulse noise reduction.

    ")); d->NRSpinBox2->setVisible(true); d->NRLabel2->setVisible(true); break; default: d->NRSpinBox1->setEnabled(false); d->NRLabel1->setEnabled(false); d->NRSpinBox2->setEnabled(false); d->NRLabel2->setEnabled(false); d->NRSpinBox2->setVisible(false); d->NRLabel2->setVisible(false); break; } emit signalSettingsChanged(); } void DcrawSettingsWidget::slotCACorrectionToggled(bool b) { d->autoCACorrectionBox->setEnabled(b); slotAutoCAToggled(d->autoCACorrectionBox->isChecked()); } void DcrawSettingsWidget::slotAutoCAToggled(bool b) { if (b) { d->caRedMultSpinBox->setValue(0.0); d->caBlueMultSpinBox->setValue(0.0); } bool mult = (!b) && (d->autoCACorrectionBox->isEnabled()); d->caRedMultSpinBox->setEnabled(mult); d->caBlueMultSpinBox->setEnabled(mult); d->caRedMultLabel->setEnabled(mult); d->caBlueMultLabel->setEnabled(mult); emit signalSettingsChanged(); } void DcrawSettingsWidget::slotExposureCorrectionToggled(bool b) { d->expoCorrectionShiftLabel->setEnabled(b); d->expoCorrectionShiftSpinBox->setEnabled(b); d->expoCorrectionHighlightLabel->setEnabled(b); d->expoCorrectionHighlightSpinBox->setEnabled(b); slotExpoCorrectionShiftChanged(d->expoCorrectionShiftSpinBox->value()); } void DcrawSettingsWidget::slotExpoCorrectionShiftChanged(double ev) { // Only enable Highligh exposure correction if Shift correction is >= 1.0, else this settings do not take effect. bool b = (ev >= 1.0); d->expoCorrectionHighlightLabel->setEnabled(b); d->expoCorrectionHighlightSpinBox->setEnabled(b); emit signalSettingsChanged(); } void DcrawSettingsWidget::slotInputColorSpaceChanged(int item) { d->inIccUrlEdit->setEnabled(item == RawDecodingSettings::CUSTOMINPUTCS); } void DcrawSettingsWidget::slotOutputColorSpaceChanged(int item) { d->outIccUrlEdit->setEnabled(item == RawDecodingSettings::CUSTOMOUTPUTCS); } void DcrawSettingsWidget::slotRAWQualityChanged(int quality) { switch(quality) { case RawDecodingSettings::DCB: case RawDecodingSettings::VCD_AHD: // These options can be only available if Libraw use GPL2 pack. d->medianFilterPassesLabel->setEnabled(KDcraw::librawUseGPL2DemosaicPack()); d->medianFilterPassesSpinBox->setEnabled(KDcraw::librawUseGPL2DemosaicPack()); d->refineInterpolationBox->setEnabled(KDcraw::librawUseGPL2DemosaicPack()); break; case RawDecodingSettings::PL_AHD: case RawDecodingSettings::AFD: case RawDecodingSettings::VCD: case RawDecodingSettings::LMMSE: case RawDecodingSettings::AMAZE: d->medianFilterPassesLabel->setEnabled(false); d->medianFilterPassesSpinBox->setEnabled(false); d->refineInterpolationBox->setEnabled(false); break; default: // BILINEAR, VNG, PPG, AHD d->medianFilterPassesLabel->setEnabled(true); d->medianFilterPassesSpinBox->setEnabled(true); d->refineInterpolationBox->setEnabled(false); break; } emit signalSettingsChanged(); } void DcrawSettingsWidget::setEnabledBrightnessSettings(bool b) { d->brightnessLabel->setEnabled(b); d->brightnessSpinBox->setEnabled(b); } bool DcrawSettingsWidget::brightnessSettingsIsEnabled() const { return d->brightnessSpinBox->isEnabled(); } void DcrawSettingsWidget::setSettings(const RawDecodingSettings& settings) { d->sixteenBitsImage->setChecked(settings.sixteenBitsImage); switch(settings.whiteBalance) { case RawDecodingSettings::CAMERA: d->whiteBalanceComboBox->setCurrentIndex(1); break; case RawDecodingSettings::AUTO: d->whiteBalanceComboBox->setCurrentIndex(2); break; case RawDecodingSettings::CUSTOM: d->whiteBalanceComboBox->setCurrentIndex(3); break; default: d->whiteBalanceComboBox->setCurrentIndex(0); break; } slotWhiteBalanceToggled(d->whiteBalanceComboBox->currentIndex()); d->customWhiteBalanceSpinBox->setValue(settings.customWhiteBalance); d->customWhiteBalanceGreenSpinBox->setValue(settings.customWhiteBalanceGreen); d->fourColorCheckBox->setChecked(settings.RGBInterpolate4Colors); d->autoBrightnessBox->setChecked(settings.autoBrightness); d->fixColorsHighlightsBox->setChecked(settings.fixColorsHighlights); switch(settings.unclipColors) { case 0: d->unclipColorComboBox->setCurrentIndex(0); break; case 1: d->unclipColorComboBox->setCurrentIndex(1); break; case 2: d->unclipColorComboBox->setCurrentIndex(2); break; default: // Reconstruct Highlight method d->unclipColorComboBox->setCurrentIndex(3); d->reconstructSpinBox->setValue(settings.unclipColors-3); break; } slotUnclipColorActivated(d->unclipColorComboBox->currentIndex()); d->dontStretchPixelsCheckBox->setChecked(settings.DontStretchPixels); d->brightnessSpinBox->setValue(settings.brightness); d->blackPointCheckBox->setChecked(settings.enableBlackPoint); d->blackPointSpinBox->setEnabled(settings.enableBlackPoint); d->blackPointSpinBox->setValue(settings.blackPoint); d->whitePointCheckBox->setChecked(settings.enableWhitePoint); d->whitePointSpinBox->setEnabled(settings.enableWhitePoint); d->whitePointSpinBox->setValue(settings.whitePoint); int q = settings.RAWQuality; // If Libraw do not support GPL2 pack, reset to BILINEAR. if (!KDcraw::librawUseGPL2DemosaicPack()) { for (int i=RawDecodingSettings::DCB ; i <=RawDecodingSettings::LMMSE ; ++i) { if (q == i) { q = RawDecodingSettings::BILINEAR; qCDebug(LIBKDCRAW_LOG) << "Libraw GPL2 pack not available. Raw quality set to Bilinear"; break; } } } // If Libraw do not support GPL3 pack, reset to BILINEAR. if (!KDcraw::librawUseGPL3DemosaicPack() && (q == RawDecodingSettings::AMAZE)) { q = RawDecodingSettings::BILINEAR; qCDebug(LIBKDCRAW_LOG) << "Libraw GPL3 pack not available. Raw quality set to Bilinear"; } d->RAWQualityComboBox->setCurrentIndex(q); switch(q) { case RawDecodingSettings::DCB: d->medianFilterPassesSpinBox->setValue(settings.dcbIterations); d->refineInterpolationBox->setChecked(settings.dcbEnhanceFl); break; case RawDecodingSettings::VCD_AHD: d->medianFilterPassesSpinBox->setValue(settings.eeciRefine); d->refineInterpolationBox->setChecked(settings.eeciRefine); break; default: d->medianFilterPassesSpinBox->setValue(settings.medianFilterPasses); d->refineInterpolationBox->setChecked(false); // option not used. break; } slotRAWQualityChanged(q); d->inputColorSpaceComboBox->setCurrentIndex((int)settings.inputColorSpace); slotInputColorSpaceChanged((int)settings.inputColorSpace); d->outputColorSpaceComboBox->setCurrentIndex((int)settings.outputColorSpace); slotOutputColorSpaceChanged((int)settings.outputColorSpace); d->noiseReductionComboBox->setCurrentIndex(settings.NRType); slotNoiseReductionChanged(settings.NRType); d->NRSpinBox1->setValue(settings.NRThreshold); d->NRSpinBox2->setValue(settings.NRChroThreshold); d->enableCACorrectionBox->setChecked(settings.enableCACorrection); d->caRedMultSpinBox->setValue(settings.caMultiplier[0]); d->caBlueMultSpinBox->setValue(settings.caMultiplier[1]); d->autoCACorrectionBox->setChecked((settings.caMultiplier[0] == 0.0) && (settings.caMultiplier[1] == 0.0)); slotCACorrectionToggled(settings.enableCACorrection); d->expoCorrectionBox->setChecked(settings.expoCorrection); slotExposureCorrectionToggled(settings.expoCorrection); d->expoCorrectionShiftSpinBox->setValue(d->shiftExpoFromLinearToEv(settings.expoCorrectionShift)); d->expoCorrectionHighlightSpinBox->setValue(settings.expoCorrectionHighlight); d->inIccUrlEdit->lineEdit()->setText(settings.inputProfile); d->outIccUrlEdit->lineEdit()->setText(settings.outputProfile); } RawDecodingSettings DcrawSettingsWidget::settings() const { RawDecodingSettings prm; prm.sixteenBitsImage = d->sixteenBitsImage->isChecked(); switch(d->whiteBalanceComboBox->currentIndex()) { case 1: prm.whiteBalance = RawDecodingSettings::CAMERA; break; case 2: prm.whiteBalance = RawDecodingSettings::AUTO; break; case 3: prm.whiteBalance = RawDecodingSettings::CUSTOM; break; default: prm.whiteBalance = RawDecodingSettings::NONE; break; } prm.customWhiteBalance = d->customWhiteBalanceSpinBox->value(); prm.customWhiteBalanceGreen = d->customWhiteBalanceGreenSpinBox->value(); prm.RGBInterpolate4Colors = d->fourColorCheckBox->isChecked(); prm.autoBrightness = d->autoBrightnessBox->isChecked(); prm.fixColorsHighlights = d->fixColorsHighlightsBox->isChecked(); switch(d->unclipColorComboBox->currentIndex()) { case 0: prm.unclipColors = 0; break; case 1: prm.unclipColors = 1; break; case 2: prm.unclipColors = 2; break; default: // Reconstruct Highlight method prm.unclipColors = d->reconstructSpinBox->value()+3; break; } prm.DontStretchPixels = d->dontStretchPixelsCheckBox->isChecked(); prm.brightness = d->brightnessSpinBox->value(); prm.enableBlackPoint = d->blackPointCheckBox->isChecked(); prm.blackPoint = d->blackPointSpinBox->value(); prm.enableWhitePoint = d->whitePointCheckBox->isChecked(); prm.whitePoint = d->whitePointSpinBox->value(); prm.RAWQuality = (RawDecodingSettings::DecodingQuality)d->RAWQualityComboBox->currentIndex(); switch(prm.RAWQuality) { case RawDecodingSettings::DCB: prm.dcbIterations = d->medianFilterPassesSpinBox->value(); prm.dcbEnhanceFl = d->refineInterpolationBox->isChecked(); break; case RawDecodingSettings::VCD_AHD: prm.esMedPasses = d->medianFilterPassesSpinBox->value(); prm.eeciRefine = d->refineInterpolationBox->isChecked(); break; default: prm.medianFilterPasses = d->medianFilterPassesSpinBox->value(); break; } prm.NRType = (RawDecodingSettings::NoiseReduction)d->noiseReductionComboBox->currentIndex(); switch (prm.NRType) { case RawDecodingSettings::NONR: { prm.NRThreshold = 0; prm.NRChroThreshold = 0; break; } case RawDecodingSettings::WAVELETSNR: case RawDecodingSettings::FBDDNR: case RawDecodingSettings::LINENR: { prm.NRThreshold = d->NRSpinBox1->value(); prm.NRChroThreshold = 0; break; } default: // IMPULSENR { prm.NRThreshold = d->NRSpinBox1->value(); prm.NRChroThreshold = d->NRSpinBox2->value(); break; } } prm.enableCACorrection = d->enableCACorrectionBox->isChecked(); prm.caMultiplier[0] = d->caRedMultSpinBox->value(); prm.caMultiplier[1] = d->caBlueMultSpinBox->value(); prm.expoCorrection = d->expoCorrectionBox->isChecked(); prm.expoCorrectionShift = d->shiftExpoFromEvToLinear(d->expoCorrectionShiftSpinBox->value()); prm.expoCorrectionHighlight = d->expoCorrectionHighlightSpinBox->value(); prm.inputColorSpace = (RawDecodingSettings::InputColorSpace)(d->inputColorSpaceComboBox->currentIndex()); prm.outputColorSpace = (RawDecodingSettings::OutputColorSpace)(d->outputColorSpaceComboBox->currentIndex()); prm.inputProfile = d->inIccUrlEdit->lineEdit()->text(); prm.outputProfile = d->outIccUrlEdit->lineEdit()->text(); return prm; } void DcrawSettingsWidget::writeSettings(KConfigGroup& group) { RawDecodingSettings prm = settings(); prm.writeSettings(group); RExpanderBox::writeSettings(group); } void DcrawSettingsWidget::readSettings(KConfigGroup& group) { RawDecodingSettings prm; prm.readSettings(group); setSettings(prm); RExpanderBox::readSettings(group); } } // NameSpace KDcrawIface diff --git a/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw_p.cpp b/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw_p.cpp index 38fdc2b2b7..cf5ffa482d 100644 --- a/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw_p.cpp +++ b/plugins/impex/raw/3rdparty/libkdcraw/src/kdcraw_p.cpp @@ -1,690 +1,690 @@ /** =========================================================== * @file * * This file is a part of digiKam project * http://www.digikam.org * * @date 2008-10-09 * @brief internal private container for KDcraw * * @author Copyright (C) 2008-2015 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "kdcraw.h" #include "kdcraw_p.h" // Qt includes #include #include // Local includes #include "libkdcraw_debug.h" namespace KDcrawIface { int callbackForLibRaw(void* data, enum LibRaw_progress p, int iteration, int expected) { if (data) { KDcraw::Private* const d = static_cast(data); if (d) { return d->progressCallback(p, iteration, expected); } } return 0; } // -------------------------------------------------------------------------------------------------- KDcraw::Private::Private(KDcraw* const p) { m_progress = 0.0; m_parent = p; } KDcraw::Private::~Private() { } void KDcraw::Private::createPPMHeader(QByteArray& imgData, libraw_processed_image_t* const img) { QString header = QString("P%1\n%2 %3\n%4\n").arg(img->colors == 3 ? "6" : "5") .arg(img->width) .arg(img->height) .arg((1 << img->bits)-1); imgData.append(header.toLatin1()); imgData.append(QByteArray((const char*)img->data, (int)img->data_size)); } int KDcraw::Private::progressCallback(enum LibRaw_progress p, int iteration, int expected) { qCDebug(LIBKDCRAW_LOG) << "LibRaw progress: " << libraw_strprogress(p) << " pass " << iteration << " of " << expected; // post a little change in progress indicator to show raw processor activity. setProgress(progressValue()+0.01); // Clean processing termination by user... if (m_parent->checkToCancelWaitingData()) { qCDebug(LIBKDCRAW_LOG) << "LibRaw process terminaison invoked..."; m_parent->m_cancel = true; m_progress = 0.0; return 1; } // Return 0 to continue processing... return 0; } void KDcraw::Private::setProgress(double value) { m_progress = value; m_parent->setWaitingDataProgress(m_progress); } double KDcraw::Private::progressValue() const { return m_progress; } void KDcraw::Private::fillIndentifyInfo(LibRaw* const raw, DcrawInfoContainer& identify) { - identify.dateTime.setTime_t(raw->imgdata.other.timestamp); + identify.dateTime.setSecsSinceEpoch(raw->imgdata.other.timestamp); identify.make = QString(raw->imgdata.idata.make); identify.model = QString(raw->imgdata.idata.model); identify.owner = QString(raw->imgdata.other.artist); identify.DNGVersion = QString::number(raw->imgdata.idata.dng_version); identify.sensitivity = raw->imgdata.other.iso_speed; identify.exposureTime = raw->imgdata.other.shutter; identify.aperture = raw->imgdata.other.aperture; identify.focalLength = raw->imgdata.other.focal_len; identify.imageSize = QSize(raw->imgdata.sizes.width, raw->imgdata.sizes.height); identify.fullSize = QSize(raw->imgdata.sizes.raw_width, raw->imgdata.sizes.raw_height); identify.outputSize = QSize(raw->imgdata.sizes.iwidth, raw->imgdata.sizes.iheight); identify.thumbSize = QSize(raw->imgdata.thumbnail.twidth, raw->imgdata.thumbnail.theight); identify.topMargin = raw->imgdata.sizes.top_margin; identify.leftMargin = raw->imgdata.sizes.left_margin; identify.hasIccProfile = raw->imgdata.color.profile ? true : false; identify.isDecodable = true; identify.pixelAspectRatio = raw->imgdata.sizes.pixel_aspect; identify.rawColors = raw->imgdata.idata.colors; identify.rawImages = raw->imgdata.idata.raw_count; identify.blackPoint = raw->imgdata.color.black; for (int ch = 0; ch < 4; ch++) { identify.blackPointCh[ch] = raw->imgdata.color.cblack[ch]; } identify.whitePoint = raw->imgdata.color.maximum; identify.orientation = (DcrawInfoContainer::ImageOrientation)raw->imgdata.sizes.flip; memcpy(&identify.cameraColorMatrix1, &raw->imgdata.color.cmatrix, sizeof(raw->imgdata.color.cmatrix)); memcpy(&identify.cameraColorMatrix2, &raw->imgdata.color.rgb_cam, sizeof(raw->imgdata.color.rgb_cam)); memcpy(&identify.cameraXYZMatrix, &raw->imgdata.color.cam_xyz, sizeof(raw->imgdata.color.cam_xyz)); if (raw->imgdata.idata.filters) { if (!raw->imgdata.idata.cdesc[3]) { raw->imgdata.idata.cdesc[3] = 'G'; } for (int i=0; i < 16; i++) { identify.filterPattern.append(raw->imgdata.idata.cdesc[raw->COLOR(i >> 1,i & 1)]); } identify.colorKeys = raw->imgdata.idata.cdesc; } for(int c = 0 ; c < raw->imgdata.idata.colors ; c++) { identify.daylightMult[c] = raw->imgdata.color.pre_mul[c]; } if (raw->imgdata.color.cam_mul[0] > 0) { for(int c = 0 ; c < 4 ; c++) { identify.cameraMult[c] = raw->imgdata.color.cam_mul[c]; } } } bool KDcraw::Private::loadFromLibraw(const QString& filePath, QByteArray& imageData, int& width, int& height, int& rgbmax) { m_parent->m_cancel = false; LibRaw raw; // Set progress call back function. raw.set_progress_handler(callbackForLibRaw, this); QByteArray deadpixelPath = QFile::encodeName(m_parent->m_rawDecodingSettings.deadPixelMap); QByteArray cameraProfile = QFile::encodeName(m_parent->m_rawDecodingSettings.inputProfile); QByteArray outputProfile = QFile::encodeName(m_parent->m_rawDecodingSettings.outputProfile); if (!m_parent->m_rawDecodingSettings.autoBrightness) { // Use a fixed white level, ignoring the image histogram. raw.imgdata.params.no_auto_bright = 1; } if (m_parent->m_rawDecodingSettings.sixteenBitsImage) { // (-4) 16bit ppm output raw.imgdata.params.output_bps = 16; } if (m_parent->m_rawDecodingSettings.halfSizeColorImage) { // (-h) Half-size color image (3x faster than -q). raw.imgdata.params.half_size = 1; } if (m_parent->m_rawDecodingSettings.RGBInterpolate4Colors) { // (-f) Interpolate RGB as four colors. raw.imgdata.params.four_color_rgb = 1; } if (m_parent->m_rawDecodingSettings.DontStretchPixels) { // (-j) Do not stretch the image to its correct aspect ratio. raw.imgdata.params.use_fuji_rotate = 1; } // (-H) Unclip highlight color. raw.imgdata.params.highlight = m_parent->m_rawDecodingSettings.unclipColors; if (m_parent->m_rawDecodingSettings.brightness != 1.0) { // (-b) Set Brightness value. raw.imgdata.params.bright = m_parent->m_rawDecodingSettings.brightness; } if (m_parent->m_rawDecodingSettings.enableBlackPoint) { // (-k) Set Black Point value. raw.imgdata.params.user_black = m_parent->m_rawDecodingSettings.blackPoint; } if (m_parent->m_rawDecodingSettings.enableWhitePoint) { // (-S) Set White Point value (saturation). raw.imgdata.params.user_sat = m_parent->m_rawDecodingSettings.whitePoint; } if (m_parent->m_rawDecodingSettings.medianFilterPasses > 0) { // (-m) After interpolation, clean up color artifacts by repeatedly applying a 3x3 median filter to the R-G and B-G channels. raw.imgdata.params.med_passes = m_parent->m_rawDecodingSettings.medianFilterPasses; } if (!m_parent->m_rawDecodingSettings.deadPixelMap.isEmpty()) { // (-P) Read the dead pixel list from this file. raw.imgdata.params.bad_pixels = deadpixelPath.data(); } switch (m_parent->m_rawDecodingSettings.whiteBalance) { case RawDecodingSettings::NONE: { break; } case RawDecodingSettings::CAMERA: { // (-w) Use camera white balance, if possible. raw.imgdata.params.use_camera_wb = 1; break; } case RawDecodingSettings::AUTO: { // (-a) Use automatic white balance. raw.imgdata.params.use_auto_wb = 1; break; } case RawDecodingSettings::CUSTOM: { /* Convert between Temperature and RGB. */ double T; double RGB[3]; double xD, yD, X, Y, Z; DcrawInfoContainer identify; T = m_parent->m_rawDecodingSettings.customWhiteBalance; /* Here starts the code picked and adapted from ufraw (0.12.1) to convert Temperature + green multiplier to RGB multipliers */ /* Convert between Temperature and RGB. * Base on information from http://www.brucelindbloom.com/ * The fit for D-illuminant between 4000K and 12000K are from CIE * The generalization to 2000K < T < 4000K and the blackbody fits * are my own and should be taken with a grain of salt. */ const double XYZ_to_RGB[3][3] = { { 3.24071, -0.969258, 0.0556352 }, {-1.53726, 1.87599, -0.203996 }, {-0.498571, 0.0415557, 1.05707 } }; // Fit for CIE Daylight illuminant if (T <= 4000) { xD = 0.27475e9/(T*T*T) - 0.98598e6/(T*T) + 1.17444e3/T + 0.145986; } else if (T <= 7000) { xD = -4.6070e9/(T*T*T) + 2.9678e6/(T*T) + 0.09911e3/T + 0.244063; } else { xD = -2.0064e9/(T*T*T) + 1.9018e6/(T*T) + 0.24748e3/T + 0.237040; } yD = -3*xD*xD + 2.87*xD - 0.275; X = xD/yD; Y = 1; Z = (1-xD-yD)/yD; RGB[0] = X*XYZ_to_RGB[0][0] + Y*XYZ_to_RGB[1][0] + Z*XYZ_to_RGB[2][0]; RGB[1] = X*XYZ_to_RGB[0][1] + Y*XYZ_to_RGB[1][1] + Z*XYZ_to_RGB[2][1]; RGB[2] = X*XYZ_to_RGB[0][2] + Y*XYZ_to_RGB[1][2] + Z*XYZ_to_RGB[2][2]; /* End of the code picked to ufraw */ RGB[1] = RGB[1] / m_parent->m_rawDecodingSettings.customWhiteBalanceGreen; /* By default, decraw override his default D65 WB We need to keep it as a basis : if not, colors with some DSLR will have a high dominant of color that will lead to a completely wrong WB */ if (rawFileIdentify(identify, filePath)) { RGB[0] = identify.daylightMult[0] / RGB[0]; RGB[1] = identify.daylightMult[1] / RGB[1]; RGB[2] = identify.daylightMult[2] / RGB[2]; } else { RGB[0] = 1.0 / RGB[0]; RGB[1] = 1.0 / RGB[1]; RGB[2] = 1.0 / RGB[2]; qCDebug(LIBKDCRAW_LOG) << "Warning: cannot get daylight multipliers"; } // (-r) set Raw Color Balance Multipliers. raw.imgdata.params.user_mul[0] = RGB[0]; raw.imgdata.params.user_mul[1] = RGB[1]; raw.imgdata.params.user_mul[2] = RGB[2]; raw.imgdata.params.user_mul[3] = RGB[1]; break; } case RawDecodingSettings::AERA: { // (-A) Calculate the white balance by averaging a rectangular area from image. raw.imgdata.params.greybox[0] = m_parent->m_rawDecodingSettings.whiteBalanceArea.left(); raw.imgdata.params.greybox[1] = m_parent->m_rawDecodingSettings.whiteBalanceArea.top(); raw.imgdata.params.greybox[2] = m_parent->m_rawDecodingSettings.whiteBalanceArea.width(); raw.imgdata.params.greybox[3] = m_parent->m_rawDecodingSettings.whiteBalanceArea.height(); break; } } // (-q) Use an interpolation method. raw.imgdata.params.user_qual = m_parent->m_rawDecodingSettings.RAWQuality; switch (m_parent->m_rawDecodingSettings.NRType) { case RawDecodingSettings::WAVELETSNR: { // (-n) Use wavelets to erase noise while preserving real detail. raw.imgdata.params.threshold = m_parent->m_rawDecodingSettings.NRThreshold; break; } case RawDecodingSettings::FBDDNR: { // (100 - 1000) => (1 - 10) conversion raw.imgdata.params.fbdd_noiserd = lround(m_parent->m_rawDecodingSettings.NRThreshold / 100.0); break; } case RawDecodingSettings::LINENR: { // (100 - 1000) => (0.001 - 0.02) conversion. raw.imgdata.params.linenoise = m_parent->m_rawDecodingSettings.NRThreshold * 2.11E-5 + 0.00111111; raw.imgdata.params.cfaline = true; break; } case RawDecodingSettings::IMPULSENR: { // (100 - 1000) => (0.005 - 0.05) conversion. raw.imgdata.params.lclean = m_parent->m_rawDecodingSettings.NRThreshold * 5E-5; raw.imgdata.params.cclean = m_parent->m_rawDecodingSettings.NRChroThreshold * 5E-5; raw.imgdata.params.cfa_clean = true; break; } default: // No Noise Reduction { raw.imgdata.params.threshold = 0; raw.imgdata.params.fbdd_noiserd = 0; raw.imgdata.params.linenoise = 0; raw.imgdata.params.cfaline = false; raw.imgdata.params.lclean = 0; raw.imgdata.params.cclean = 0; raw.imgdata.params.cfa_clean = false; break; } } // Chromatic aberration correction. raw.imgdata.params.ca_correc = m_parent->m_rawDecodingSettings.enableCACorrection; raw.imgdata.params.cared = m_parent->m_rawDecodingSettings.caMultiplier[0]; raw.imgdata.params.cablue = m_parent->m_rawDecodingSettings.caMultiplier[1]; // Exposure Correction before interpolation. raw.imgdata.params.exp_correc = m_parent->m_rawDecodingSettings.expoCorrection; raw.imgdata.params.exp_shift = m_parent->m_rawDecodingSettings.expoCorrectionShift; raw.imgdata.params.exp_preser = m_parent->m_rawDecodingSettings.expoCorrectionHighlight; switch (m_parent->m_rawDecodingSettings.inputColorSpace) { case RawDecodingSettings::EMBEDDED: { // (-p embed) Use input profile from RAW file to define the camera's raw colorspace. raw.imgdata.params.camera_profile = (char*)"embed"; break; } case RawDecodingSettings::CUSTOMINPUTCS: { if (!m_parent->m_rawDecodingSettings.inputProfile.isEmpty()) { // (-p) Use input profile file to define the camera's raw colorspace. raw.imgdata.params.camera_profile = cameraProfile.data(); } break; } default: { // No input profile break; } } switch (m_parent->m_rawDecodingSettings.outputColorSpace) { case RawDecodingSettings::CUSTOMOUTPUTCS: { if (!m_parent->m_rawDecodingSettings.outputProfile.isEmpty()) { // (-o) Use ICC profile file to define the output colorspace. raw.imgdata.params.output_profile = outputProfile.data(); } break; } default: { // (-o) Define the output colorspace. raw.imgdata.params.output_color = m_parent->m_rawDecodingSettings.outputColorSpace; break; } } //-- Extended demosaicing settings ---------------------------------------------------------- raw.imgdata.params.dcb_iterations = m_parent->m_rawDecodingSettings.dcbIterations; raw.imgdata.params.dcb_enhance_fl = m_parent->m_rawDecodingSettings.dcbEnhanceFl; raw.imgdata.params.eeci_refine = m_parent->m_rawDecodingSettings.eeciRefine; raw.imgdata.params.es_med_passes = m_parent->m_rawDecodingSettings.esMedPasses; //------------------------------------------------------------------------------------------- setProgress(0.1); qCDebug(LIBKDCRAW_LOG) << filePath; qCDebug(LIBKDCRAW_LOG) << m_parent->m_rawDecodingSettings; int ret = raw.open_file((const char*)(QFile::encodeName(filePath)).constData()); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret); raw.recycle(); return false; } if (m_parent->m_cancel) { raw.recycle(); return false; } setProgress(0.2); ret = raw.unpack(); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run unpack: " << libraw_strerror(ret); raw.recycle(); return false; } if (m_parent->m_cancel) { raw.recycle(); return false; } setProgress(0.25); if (m_parent->m_rawDecodingSettings.fixColorsHighlights) { qCDebug(LIBKDCRAW_LOG) << "Applying LibRaw highlights adjustments"; // 1.0 is fallback to default value raw.imgdata.params.adjust_maximum_thr = 1.0; } else { qCDebug(LIBKDCRAW_LOG) << "Disabling LibRaw highlights adjustments"; // 0.0 disables this feature raw.imgdata.params.adjust_maximum_thr = 0.0; } ret = raw.dcraw_process(); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run dcraw_process: " << libraw_strerror(ret); raw.recycle(); return false; } if (m_parent->m_cancel) { raw.recycle(); return false; } setProgress(0.3); libraw_processed_image_t* img = raw.dcraw_make_mem_image(&ret); if(!img) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run dcraw_make_mem_image: " << libraw_strerror(ret); raw.recycle(); return false; } if (m_parent->m_cancel) { // Clear memory allocation. Introduced with LibRaw 0.11.0 raw.dcraw_clear_mem(img); raw.recycle(); return false; } setProgress(0.35); width = img->width; height = img->height; rgbmax = (1 << img->bits)-1; if (img->colors == 3) { imageData = QByteArray((const char*)img->data, (int)img->data_size); } else { // img->colors == 1 (Grayscale) : convert to RGB imageData = QByteArray(); for (int i = 0 ; i < (int)img->data_size ; ++i) { for (int j = 0 ; j < 3 ; ++j) { imageData.append(img->data[i]); } } } // Clear memory allocation. Introduced with LibRaw 0.11.0 raw.dcraw_clear_mem(img); raw.recycle(); if (m_parent->m_cancel) { return false; } setProgress(0.4); qCDebug(LIBKDCRAW_LOG) << "LibRaw: data info: width=" << width << " height=" << height << " rgbmax=" << rgbmax; return true; } bool KDcraw::Private::loadEmbeddedPreview(QByteArray& imgData, LibRaw& raw) { int ret = raw.unpack_thumb(); if (ret != LIBRAW_SUCCESS) { raw.recycle(); qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run unpack_thumb: " << libraw_strerror(ret); raw.recycle(); return false; } libraw_processed_image_t* const thumb = raw.dcraw_make_mem_thumb(&ret); if(!thumb) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run dcraw_make_mem_thumb: " << libraw_strerror(ret); raw.recycle(); return false; } if(thumb->type == LIBRAW_IMAGE_BITMAP) { createPPMHeader(imgData, thumb); } else { imgData = QByteArray((const char*)thumb->data, (int)thumb->data_size); } // Clear memory allocation. Introduced with LibRaw 0.11.0 raw.dcraw_clear_mem(thumb); raw.recycle(); if ( imgData.isEmpty() ) { qCDebug(LIBKDCRAW_LOG) << "Failed to load JPEG thumb from LibRaw!"; return false; } return true; } bool KDcraw::Private::loadHalfPreview(QImage& image, LibRaw& raw) { raw.imgdata.params.use_auto_wb = 1; // Use automatic white balance. raw.imgdata.params.use_camera_wb = 1; // Use camera white balance, if possible. raw.imgdata.params.half_size = 1; // Half-size color image (3x faster than -q). QByteArray imgData; int ret = raw.unpack(); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run unpack: " << libraw_strerror(ret); raw.recycle(); return false; } ret = raw.dcraw_process(); if (ret != LIBRAW_SUCCESS) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run dcraw_process: " << libraw_strerror(ret); raw.recycle(); return false; } libraw_processed_image_t* halfImg = raw.dcraw_make_mem_image(&ret); if(!halfImg) { qCDebug(LIBKDCRAW_LOG) << "LibRaw: failed to run dcraw_make_mem_image: " << libraw_strerror(ret); raw.recycle(); return false; } Private::createPPMHeader(imgData, halfImg); // Clear memory allocation. Introduced with LibRaw 0.11.0 raw.dcraw_clear_mem(halfImg); raw.recycle(); if ( imgData.isEmpty() ) { qCDebug(LIBKDCRAW_LOG) << "Failed to load half preview from LibRaw!"; return false; } if (!image.loadFromData(imgData)) { qCDebug(LIBKDCRAW_LOG) << "Failed to load PPM data from LibRaw!"; return false; } return true; } } // namespace KDcrawIface diff --git a/plugins/paintops/libpaintop/kis_multi_sensors_model_p.cpp b/plugins/paintops/libpaintop/kis_multi_sensors_model_p.cpp index f7cd7237e2..8616b67f88 100644 --- a/plugins/paintops/libpaintop/kis_multi_sensors_model_p.cpp +++ b/plugins/paintops/libpaintop/kis_multi_sensors_model_p.cpp @@ -1,125 +1,124 @@ /* * Copyright (c) 2011 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_multi_sensors_model_p.h" #include "kis_dynamic_sensor.h" #include "kis_curve_option.h" KisMultiSensorsModel::KisMultiSensorsModel(QObject* parent) : QAbstractListModel(parent) , m_curveOption(0) { } KisMultiSensorsModel::~KisMultiSensorsModel() { } void KisMultiSensorsModel::setCurveOption(KisCurveOption *curveOption) { beginResetModel(); m_curveOption = curveOption; endResetModel(); } int KisMultiSensorsModel::rowCount(const QModelIndex &/*parent*/) const { if (m_curveOption) { return m_curveOption->sensors().size(); } else { return 0; } } QVariant KisMultiSensorsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole) { return KisDynamicSensor::sensorsIds()[index.row()].name(); } else if (role == Qt::CheckStateRole) { QString selectedSensorId = KisDynamicSensor::sensorsIds()[index.row()].id(); KisDynamicSensorSP sensor = m_curveOption->sensor(KisDynamicSensor::id2Type(selectedSensorId), false); if (sensor) { //dbgKrita << sensor->id() << sensor->isActive(); return QVariant(sensor->isActive() ? Qt::Checked : Qt::Unchecked); } else { return QVariant(Qt::Unchecked); } } return QVariant(); } bool KisMultiSensorsModel::setData(const QModelIndex &index, const QVariant &value, int role) { bool result = false; if (role == Qt::CheckStateRole) { bool checked = (value.toInt() == Qt::Checked); if (checked || m_curveOption->activeSensors().size() != 1) { // Don't uncheck the last sensor (but why not?) KisDynamicSensorSP sensor = m_curveOption->sensor(KisDynamicSensor::id2Type(KisDynamicSensor::sensorsIds()[index.row()].id()), false); if (!sensor) { sensor = KisDynamicSensor::id2Sensor(KisDynamicSensor::sensorsIds()[index.row()].id()); m_curveOption->replaceSensor(sensor); } sensor->setActive(checked); emit(parametersChanged()); result = true; } } return result; } Qt::ItemFlags KisMultiSensorsModel::flags(const QModelIndex & /*index */) const { return Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled; } KisDynamicSensorSP KisMultiSensorsModel::getSensor(const QModelIndex& index) { if (!index.isValid()) return 0; QString id = KisDynamicSensor::sensorsIds()[index.row()].id(); return m_curveOption->sensor(KisDynamicSensor::id2Type(id), false); } void KisMultiSensorsModel::setCurrentCurve(const QModelIndex& currentIndex, const KisCubicCurve& curve, bool useSameCurve) { if (!currentIndex.isValid()) return; QString selectedSensorId = KisDynamicSensor::sensorsIds()[currentIndex.row()].id(); m_curveOption->setCurve(KisDynamicSensor::id2Type(selectedSensorId), useSameCurve, curve); } QModelIndex KisMultiSensorsModel::sensorIndex(KisDynamicSensorSP arg1) { return index(KisDynamicSensor::sensorsIds().indexOf(KoID(KisDynamicSensor::id(arg1->sensorType())))); } void KisMultiSensorsModel::resetCurveOption() { beginResetModel(); - reset(); endResetModel(); } diff --git a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectConfigWidget.cpp b/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectConfigWidget.cpp index b1880cd462..a97c78abe7 100644 --- a/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectConfigWidget.cpp +++ b/plugins/tools/karbonplugins/filtereffects/ColorMatrixEffectConfigWidget.cpp @@ -1,181 +1,181 @@ /* This file is part of the KDE project * Copyright (c) 2009-2010 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "ColorMatrixEffectConfigWidget.h" #include "ColorMatrixEffect.h" #include "KoFilterEffect.h" #include "MatrixDataModel.h" #include #include #include #include #include #include #include #include #include "kis_double_parse_spin_box.h" ColorMatrixEffectConfigWidget::ColorMatrixEffectConfigWidget(QWidget *parent) : KoFilterEffectConfigWidgetBase(parent) , m_effect(0) { QGridLayout *g = new QGridLayout(this); m_type = new KComboBox(this); m_type->addItem(i18n("Apply color matrix")); m_type->addItem(i18n("Saturate colors")); m_type->addItem(i18n("Rotate hue")); m_type->addItem(i18n("Luminance to alpha")); g->addWidget(m_type, 0, 0); m_stack = new QStackedWidget(this); m_stack->setContentsMargins(0, 0, 0, 0); g->addWidget(m_stack, 1, 0); m_matrixModel = new MatrixDataModel(this); QTableView *matrixWidget = new QTableView(m_stack); matrixWidget->setModel(m_matrixModel); matrixWidget->horizontalHeader()->hide(); - matrixWidget->horizontalHeader()->setResizeMode(QHeaderView::Stretch); + matrixWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); matrixWidget->verticalHeader()->hide(); - matrixWidget->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents); + matrixWidget->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); m_stack->addWidget(matrixWidget); QWidget *saturateWidget = new QWidget(m_stack); QGridLayout *saturateLayout = new QGridLayout(saturateWidget); saturateLayout->addWidget(new QLabel(i18n("Saturate value"), saturateWidget), 0, 0); m_saturate = new KisDoubleParseSpinBox(saturateWidget); m_saturate->setRange(0.0, 1.0); m_saturate->setSingleStep(0.05); saturateLayout->addWidget(m_saturate, 0, 1); saturateLayout->addItem(new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 1, 0); saturateWidget->setLayout(saturateLayout); m_stack->addWidget(saturateWidget); QWidget *hueRotateWidget = new QWidget(m_stack); QGridLayout *hueRotateLayout = new QGridLayout(hueRotateWidget); hueRotateLayout->addWidget(new QLabel(i18n("Angle"), hueRotateWidget), 0, 0); m_hueRotate = new KisDoubleParseSpinBox(hueRotateWidget); m_hueRotate->setRange(0.0, 360.0); m_hueRotate->setSingleStep(1.0); hueRotateLayout->addWidget(m_hueRotate, 0, 1); hueRotateLayout->addItem(new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 1, 0); hueRotateWidget->setLayout(hueRotateLayout); m_stack->addWidget(hueRotateWidget); QWidget *luminanceWidget = new QWidget(m_stack); m_stack->addWidget(luminanceWidget); setLayout(g); connect(m_type, SIGNAL(currentIndexChanged(int)), m_stack, SLOT(setCurrentIndex(int))); connect(m_type, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged(int))); connect(m_saturate, SIGNAL(valueChanged(double)), this, SLOT(saturateChanged(double))); connect(m_hueRotate, SIGNAL(valueChanged(double)), this, SLOT(hueRotateChanged(double))); connect(m_matrixModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(matrixChanged())); } bool ColorMatrixEffectConfigWidget::editFilterEffect(KoFilterEffect *filterEffect) { m_effect = dynamic_cast(filterEffect); if (!m_effect) { return false; } m_type->blockSignals(true); switch (m_effect->type()) { case ColorMatrixEffect::Matrix: m_type->setCurrentIndex(0); m_matrixModel->setMatrix(m_effect->colorMatrix(), m_effect->colorMatrixRowCount(), m_effect->colorMatrixColumnCount()); break; case ColorMatrixEffect::Saturate: m_type->setCurrentIndex(1); m_saturate->blockSignals(true); m_saturate->setValue(m_effect->saturate()); m_saturate->blockSignals(false); break; case ColorMatrixEffect::HueRotate: m_type->setCurrentIndex(2); m_hueRotate->blockSignals(true); m_hueRotate->setValue(m_effect->hueRotate()); m_hueRotate->blockSignals(false); break; case ColorMatrixEffect::LuminanceAlpha: m_type->setCurrentIndex(3); break; } m_type->blockSignals(false); m_stack->setCurrentIndex(m_type->currentIndex()); return true; } void ColorMatrixEffectConfigWidget::matrixChanged() { if (!m_effect) { return; } m_effect->setColorMatrix(m_matrixModel->matrix()); emit filterChanged(); } void ColorMatrixEffectConfigWidget::saturateChanged(double saturate) { if (!m_effect) { return; } m_effect->setSaturate(saturate); emit filterChanged(); } void ColorMatrixEffectConfigWidget::hueRotateChanged(double angle) { if (!m_effect) { return; } m_effect->setHueRotate(angle); emit filterChanged(); } void ColorMatrixEffectConfigWidget::typeChanged(int index) { if (!m_effect) { return; } if (index == ColorMatrixEffect::Matrix) { m_effect->setColorMatrix(m_matrixModel->matrix()); } else if (index == ColorMatrixEffect::Saturate) { m_effect->setSaturate(m_saturate->value()); } else if (index == ColorMatrixEffect::HueRotate) { m_effect->setHueRotate(m_hueRotate->value()); } else { m_effect->setLuminanceAlpha(); } emit filterChanged(); } diff --git a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectConfigWidget.cpp b/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectConfigWidget.cpp index 6bacd30a5a..7d8a7e8c2f 100644 --- a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectConfigWidget.cpp +++ b/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffectConfigWidget.cpp @@ -1,262 +1,262 @@ /* This file is part of the KDE project * Copyright (c) 2010 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "ConvolveMatrixEffectConfigWidget.h" #include "ConvolveMatrixEffect.h" #include "KoFilterEffect.h" #include "MatrixDataModel.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_double_parse_spin_box.h" #include "kis_int_parse_spin_box.h" ConvolveMatrixEffectConfigWidget::ConvolveMatrixEffectConfigWidget(QWidget *parent) : KoFilterEffectConfigWidgetBase(parent) , m_effect(0) { QGridLayout *g = new QGridLayout(this); m_edgeMode = new KComboBox(this); m_edgeMode->addItem(i18n("Duplicate")); m_edgeMode->addItem(i18n("Wrap")); m_edgeMode->addItem(i18n("None")); g->addWidget(new QLabel(i18n("Edge mode:"), this), 0, 0); g->addWidget(m_edgeMode, 0, 1, 1, 3); m_orderX = new KisIntParseSpinBox(this); m_orderX->setRange(1, 30); m_orderY = new KisIntParseSpinBox(this); m_orderY->setRange(1, 30); g->addWidget(new QLabel(i18n("Kernel size:"), this), 1, 0); g->addWidget(m_orderX, 1, 1); g->addWidget(new QLabel("X", this), 1, 2, Qt::AlignHCenter); g->addWidget(m_orderY, 1, 3); m_targetX = new KisIntParseSpinBox(this); m_targetX->setRange(0, 30); m_targetY = new KisIntParseSpinBox(this); m_targetY->setRange(0, 30); g->addWidget(new QLabel(i18n("Target point:"), this), 2, 0); g->addWidget(m_targetX, 2, 1); g->addWidget(new QLabel("X", this), 2, 2, Qt::AlignHCenter); g->addWidget(m_targetY, 2, 3); m_divisor = new KisDoubleParseSpinBox(this); m_bias = new KisDoubleParseSpinBox(this); g->addWidget(new QLabel(i18n("Divisor:"), this), 3, 0); g->addWidget(m_divisor, 3, 1); g->addWidget(new QLabel(i18n("Bias:"), this), 3, 2); g->addWidget(m_bias, 3, 3); m_preserveAlpha = new QCheckBox(i18n("Preserve alpha"), this); g->addWidget(m_preserveAlpha, 4, 1, 1, 3); QPushButton *kernelButton = new QPushButton(i18n("Edit kernel"), this); g->addWidget(kernelButton, 5, 0, 1, 4); setLayout(g); connect(m_edgeMode, SIGNAL(currentIndexChanged(int)), this, SLOT(edgeModeChanged(int))); connect(m_orderX, SIGNAL(valueChanged(int)), this, SLOT(orderChanged(int))); connect(m_orderY, SIGNAL(valueChanged(int)), this, SLOT(orderChanged(int))); connect(m_targetX, SIGNAL(valueChanged(int)), this, SLOT(targetChanged(int))); connect(m_targetY, SIGNAL(valueChanged(int)), this, SLOT(targetChanged(int))); connect(m_divisor, SIGNAL(valueChanged(double)), this, SLOT(divisorChanged(double))); connect(m_bias, SIGNAL(valueChanged(double)), this, SLOT(biasChanged(double))); connect(kernelButton, SIGNAL(clicked(bool)), this, SLOT(editKernel())); connect(m_preserveAlpha, SIGNAL(toggled(bool)), this, SLOT(preserveAlphaChanged(bool))); m_matrixModel = new MatrixDataModel(this); } bool ConvolveMatrixEffectConfigWidget::editFilterEffect(KoFilterEffect *filterEffect) { m_effect = dynamic_cast(filterEffect); if (!m_effect) { return false; } m_edgeMode->blockSignals(true); m_edgeMode->setCurrentIndex(m_effect->edgeMode()); m_edgeMode->blockSignals(false); m_orderX->blockSignals(true); m_orderX->setValue(m_effect->order().x()); m_orderX->blockSignals(false); m_orderY->blockSignals(true); m_orderY->setValue(m_effect->order().y()); m_orderY->blockSignals(false); m_targetX->blockSignals(true); m_targetX->setMaximum(m_orderX->value()); m_targetX->setValue(m_effect->target().x() + 1); m_targetX->blockSignals(false); m_targetY->blockSignals(true); m_targetY->setMaximum(m_orderY->value()); m_targetY->setValue(m_effect->target().y() + 1); m_targetY->blockSignals(false); m_divisor->blockSignals(true); m_divisor->setValue(m_effect->divisor()); m_divisor->blockSignals(false); m_bias->blockSignals(true); m_bias->setValue(m_effect->bias()); m_bias->blockSignals(false); m_preserveAlpha->blockSignals(true); m_preserveAlpha->setChecked(m_effect->isPreserveAlphaEnabled()); m_preserveAlpha->blockSignals(false); return true; } void ConvolveMatrixEffectConfigWidget::edgeModeChanged(int id) { if (!m_effect) { return; } switch (id) { case ConvolveMatrixEffect::Duplicate: m_effect->setEdgeMode(ConvolveMatrixEffect::Duplicate); break; case ConvolveMatrixEffect::Wrap: m_effect->setEdgeMode(ConvolveMatrixEffect::Wrap); break; case ConvolveMatrixEffect::None: m_effect->setEdgeMode(ConvolveMatrixEffect::None); break; } emit filterChanged(); } void ConvolveMatrixEffectConfigWidget::orderChanged(int) { if (!m_effect) { return; } QPoint newOrder(m_orderX->value(), m_orderY->value()); QPoint oldOrder = m_effect->order(); if (newOrder != oldOrder) { m_effect->setOrder(newOrder); emit filterChanged(); } m_targetX->setMaximum(newOrder.x()); m_targetY->setMaximum(newOrder.y()); } void ConvolveMatrixEffectConfigWidget::targetChanged(int) { if (!m_effect) { return; } QPoint newTarget(m_targetX->value() - 1, m_targetY->value() - 1); QPoint oldTarget = m_effect->target(); if (newTarget != oldTarget) { m_effect->setTarget(newTarget); emit filterChanged(); } } void ConvolveMatrixEffectConfigWidget::divisorChanged(double divisor) { if (!m_effect) { return; } if (divisor != m_effect->divisor()) { m_effect->setDivisor(divisor); emit filterChanged(); } } void ConvolveMatrixEffectConfigWidget::biasChanged(double bias) { if (!m_effect) { return; } if (bias != m_effect->bias()) { m_effect->setBias(bias); emit filterChanged(); } } void ConvolveMatrixEffectConfigWidget::preserveAlphaChanged(bool checked) { if (!m_effect) { return; } m_effect->enablePreserveAlpha(checked); emit filterChanged(); } void ConvolveMatrixEffectConfigWidget::editKernel() { if (!m_effect) { return; } QVector oldKernel = m_effect->kernel(); QPoint kernelSize = m_effect->order(); m_matrixModel->setMatrix(oldKernel, kernelSize.y(), kernelSize.x()); connect(m_matrixModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(kernelChanged())); QPointer dlg = new QDialog(this); QTableView *table = new QTableView(dlg); table->setModel(m_matrixModel); table->horizontalHeader()->hide(); - table->horizontalHeader()->setResizeMode(QHeaderView::Stretch); + table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); table->verticalHeader()->hide(); - table->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents); + table->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); QVBoxLayout *mainLayout = new QVBoxLayout; dlg->setLayout(mainLayout); mainLayout->addWidget(table); if (dlg->exec() == QDialog::Accepted) { m_effect->setKernel(m_matrixModel->matrix()); emit filterChanged(); } else { m_effect->setKernel(oldKernel); } delete dlg; disconnect(m_matrixModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(kernelChanged())); } void ConvolveMatrixEffectConfigWidget::kernelChanged() { if (!m_effect) { return; } m_effect->setKernel(m_matrixModel->matrix()); emit filterChanged(); } diff --git a/plugins/tools/karbonplugins/filtereffects/MatrixDataModel.cpp b/plugins/tools/karbonplugins/filtereffects/MatrixDataModel.cpp index ace47513cf..6c0a699657 100644 --- a/plugins/tools/karbonplugins/filtereffects/MatrixDataModel.cpp +++ b/plugins/tools/karbonplugins/filtereffects/MatrixDataModel.cpp @@ -1,86 +1,87 @@ /* This file is part of the KDE project * Copyright (c) 2010 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "MatrixDataModel.h" #include "kis_num_parser.h" MatrixDataModel::MatrixDataModel(QObject *parent) : QAbstractTableModel(parent) , m_rows(0) , m_cols(0) { } void MatrixDataModel::setMatrix(const QVector &matrix, int rows, int cols) { m_matrix = matrix; m_rows = rows; m_cols = cols; Q_ASSERT(m_rows); Q_ASSERT(m_cols); Q_ASSERT(m_matrix.count() == m_rows * m_cols); - reset(); + beginResetModel(); + endResetModel(); } QVector MatrixDataModel::matrix() const { return m_matrix; } int MatrixDataModel::rowCount(const QModelIndex &/*parent*/) const { return m_rows; } int MatrixDataModel::columnCount(const QModelIndex &/*parent*/) const { return m_cols; } QVariant MatrixDataModel::data(const QModelIndex &index, int role) const { int element = index.row() * m_cols + index.column(); switch (role) { case Qt::DisplayRole: case Qt::EditRole: return QVariant(QString("%1").arg(m_matrix[element], 2)); break; default: return QVariant(); } } bool MatrixDataModel::setData(const QModelIndex &index, const QVariant &value, int /*role*/) { int element = index.row() * m_cols + index.column(); bool valid = false; qreal elementValue = KisNumericParser::parseSimpleMathExpr(value.toString(), &valid); if (!valid) { return false; } m_matrix[element] = elementValue; emit dataChanged(index, index); return true; } Qt::ItemFlags MatrixDataModel::flags(const QModelIndex &/*index*/) const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; } diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectSceneItems.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectSceneItems.cpp index 871a4f8003..b633cc4ef5 100644 --- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectSceneItems.cpp +++ b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectSceneItems.cpp @@ -1,321 +1,321 @@ /* This file is part of the KDE project * Copyright (c) 2009 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "FilterEffectSceneItems.h" #include "KoFilterEffect.h" #include #include #include #include #include const QSizeF ConnectorSize = QSize(20, 20); const qreal ItemWidth = 15 * ConnectorSize.height(); const qreal FontSize = 0.8 * ConnectorSize.height(); ConnectorItem::ConnectorItem(ConnectorType type, int index, QGraphicsItem *parent) : QGraphicsEllipseItem(parent) , m_type(type) , m_index(index) { if (m_type == Output) { setBrush(QBrush(Qt::red)); } else if (m_type == Input) { setBrush(QBrush(Qt::green)); } setAcceptDrops(true); setRect(QRectF(QPointF(), ConnectorSize)); } void ConnectorItem::setCenter(const QPointF &position) { QRectF r = rect(); r.moveCenter(position); setRect(r); } ConnectorItem::ConnectorType ConnectorItem::connectorType() { return m_type; } int ConnectorItem::connectorIndex() const { return m_index; } KoFilterEffect *ConnectorItem::effect() const { if (!parentItem()) { return 0; } EffectItemBase *effectItem = dynamic_cast(parentItem()); if (!effectItem) { return 0; } return effectItem->effect(); } ConnectorMimeData::ConnectorMimeData(ConnectorItem *connector) : m_connector(connector) { } ConnectorItem *ConnectorMimeData::connector() const { return m_connector; } EffectItemBase::EffectItemBase(KoFilterEffect *effect) : QGraphicsRectItem(0), m_effect(effect) { setZValue(1); setFlags(QGraphicsItem::ItemIsSelectable); setAcceptDrops(true); setHandlesChildEvents(true); } void EffectItemBase::createText(const QString &text) { QGraphicsSimpleTextItem *textItem = new QGraphicsSimpleTextItem(text, this); QFont font = textItem->font(); font.setPointSize(FontSize); textItem->setFont(font); QRectF textBox = textItem->boundingRect(); QPointF offset = rect().center() - textBox.center(); - textItem->translate(offset.x(), offset.y()); + setTransform(QTransform::fromTranslate(offset.x(), offset.y()), true); } void EffectItemBase::createOutput(const QPointF &position, const QString &name) { ConnectorItem *connector = new ConnectorItem(ConnectorItem::Output, 0, this); connector->setCenter(position); m_outputPosition = position; m_outputName = name; } void EffectItemBase::createInput(const QPointF &position) { int inputCount = m_inputPositions.count(); ConnectorItem *connector = new ConnectorItem(ConnectorItem::Input, inputCount, this); connector->setCenter(position); m_inputPositions.append(position); } QPointF EffectItemBase::outputPosition() const { return m_outputPosition; } QPointF EffectItemBase::inputPosition(int index) const { if (index < 0 || index >= m_inputPositions.count()) { return QPointF(); } return m_inputPositions[index]; } QString EffectItemBase::outputName() const { return m_outputName; } QSizeF EffectItemBase::connectorSize() const { return ConnectorSize; } KoFilterEffect *EffectItemBase::effect() const { return m_effect; } void EffectItemBase::mousePressEvent(QGraphicsSceneMouseEvent *event) { ConnectorItem *connector = connectorAtPosition(event->scenePos()); if (!connector) { return; } ConnectorMimeData *data = new ConnectorMimeData(connector); QDrag *drag = new QDrag(event->widget()); drag->setMimeData(data); drag->start(); } void EffectItemBase::dragMoveEvent(QGraphicsSceneDragDropEvent *event) { event->ignore(); ConnectorItem *targetItem = connectorAtPosition(event->scenePos()); if (!targetItem) { return; } const ConnectorMimeData *data = dynamic_cast(event->mimeData()); if (!data) { return; } ConnectorItem *sourceItem = data->connector(); int sourceItemType = sourceItem->connectorType(); int targetItemType = targetItem->connectorType(); if (sourceItemType == targetItemType) { return; } // do not accept connection within single effect item if (sourceItem->parentItem() == targetItem->parentItem()) { return; } if (sourceItemType == ConnectorItem::Input) { // we can only connect input with output above if (sourceItem->scenePos().y() < targetItem->scenePos().y()) { return; } } if (sourceItemType == ConnectorItem::Output) { // we can only connect output with input below if (sourceItem->scenePos().y() > targetItem->scenePos().y()) { return; } } event->accept(); } void EffectItemBase::dropEvent(QGraphicsSceneDragDropEvent *event) { ConnectorItem *connector = connectorAtPosition(event->scenePos()); if (!connector) { return; } const ConnectorMimeData *data = dynamic_cast(event->mimeData()); if (!data) { return; } } ConnectorItem *EffectItemBase::connectorAtPosition(const QPointF &scenePosition) { Q_FOREACH (QGraphicsItem *childItem, childItems()) { ConnectorItem *connector = dynamic_cast(childItem); if (!connector) { continue; } if (connector->contains(connector->mapFromScene(scenePosition))) { return connector; } } return 0; } DefaultInputItem::DefaultInputItem(const QString &name, KoFilterEffect *effect) : EffectItemBase(effect), m_name(name) { setRect(0, 0, ItemWidth, 2 * ConnectorSize.height()); createOutput(QPointF(ItemWidth, 0.5 * rect().height()), name); createText(name); QLinearGradient g(QPointF(0, 0), QPointF(1, 1)); g.setCoordinateMode(QGradient::ObjectBoundingMode); g.setColorAt(0, Qt::white); g.setColorAt(1, QColor(255, 168, 88)); setBrush(QBrush(g)); } EffectItem::EffectItem(KoFilterEffect *effect) : EffectItemBase(effect) { Q_ASSERT(effect); QRectF circle(QPointF(), ConnectorSize); QPointF position(ItemWidth, ConnectorSize.height()); // create input connectors int requiredInputCount = effect->requiredInputCount(); int usedInputCount = qMax(requiredInputCount, effect->inputs().count()); for (int i = 0; i < usedInputCount; ++i) { createInput(position); position.ry() += 1.5 * ConnectorSize.height(); } // create a new input connector when maximal input count in not reached yet if (usedInputCount < effect->maximalInputCount()) { createInput(position); position.ry() += 1.5 * ConnectorSize.height(); } // create output connector position.ry() += 0.5 * ConnectorSize.height(); createOutput(position, effect->output()); setRect(0, 0, ItemWidth, position.y() + ConnectorSize.height()); createText(effect->id()); QLinearGradient g(QPointF(0, 0), QPointF(1, 1)); g.setCoordinateMode(QGradient::ObjectBoundingMode); g.setColorAt(0, Qt::white); g.setColorAt(1, QColor(0, 192, 192)); setBrush(QBrush(g)); } ConnectionItem::ConnectionItem(EffectItemBase *source, EffectItemBase *target, int targetInput) : QGraphicsPathItem(0) , m_source(source) , m_target(target) , m_targetInput(targetInput) { setPen(QPen(Qt::black)); } EffectItemBase *ConnectionItem::sourceItem() const { return m_source; } EffectItemBase *ConnectionItem::targetItem() const { return m_target; } int ConnectionItem::targetInput() const { return m_targetInput; } void ConnectionItem::setSourceItem(EffectItemBase *source) { m_source = source; } void ConnectionItem::setTargetItem(EffectItemBase *target, int targetInput) { m_target = target; m_targetInput = targetInput; }