diff --git a/3rdparty/README.md b/3rdparty/README.md index 489a0793c0..072a98241a 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -1,254 +1,255 @@ = CMake external projects to build krita's dependencies on Linux, Windows or OSX = If you need to build Krita's dependencies for the following reasons: * you develop on Windows and aren't using Craft * you develop on OSX and aren't using Homebrew, or are on OSX and want to make distributable app bundles * you want to build a generic, distro-agnostic version of Krita for Linux * you develop on Linux, but some dependencies aren't available for your distribution and you know what you're doing, you can use the following guide to build the dependencies that Krita needs. If you develop on Linux and your distribution has all dependencies available, YOU DO NOT NEED THIS GUIDE AND YOU SHOULD STOP READING NOW Otherwise you risk major confusion. == Prerequisites == Note: on all operating systems the entire procedure is done in a terminal window. 1. git: https://git-scm.com/downloads. Make sure git is in your path 2. CMake 3.3.2 or later: https://cmake.org/download/. Make sure cmake is in your path. * CMake 3.9 does not build Krita properly at the moment, please use 3.8 instead. 3. Make sure you have a compiler: * Linux: gcc, minimum version 4.8 * OSX: clang, you need to install xcode for this * Windows: mingw-w64 7.3 (by mingw-builds): https://sourceforge.net/projects/mingw-w64/ Install mingw to something like c:\mingw; not to a folder with a space in its path. For threading, select posix, for exceptions seh. Make sure mingw's bin folder is in your path. It might be a good idea to create a batch file which sets the path and start cmd. MSVC is *not* supported at the moment. 4. On Windows, you will also need Python 3.6.2 (technically any versions of 3.6 is fine, but it's not tested): https://www.python.org. Make sure to have that version of python.exe in your path. This version of Python will be used for two things: to configure Qt and to build the Python scripting module. Make sure the version you download is exactly python-3.6.2. Make sure that this version of Python comes first in your path. Do not set PYTHONHOME or PYTHONPATH. Make sure that your Python will have the correct architecture for the version you are trying to build: https://www.python.org/downloads/windows/ 5. On Windows, if you want to compile Qt with ANGLE support, you will need to install Windows 10 SDK and have the environment variable `WindowsSdkDir` set to it (typically `C:\Program Files (x86)\Windows Kits\10`) == Setup your environment == == Prepare your directory layout == 1. Make a toplevel build directory, say $HOME/dev or c:\dev. We'll refer to this directory as BUILDROOT. You can use a variable for this, on WINDOWS %BUILDROOT%, on OSX and Linux $BUILDROOT. You will have to replace BUILDROOT with $BUILDROOT or %BUILDROOT whenever you copy and paste a command, depending on your operating system. 2. Checkout krita in BUILDROOT cd BUILDROOT git clone git://anongit.kde.org/krita.git 3. Create the build directory mkdir BUILDROOT/b 4. Create the downloads directory mkdir BUILDROOT/d 5. Create the install directory mkdir BUILDROOT/i == Prepare the externals build == 1. enter the BUILDROOT/b directory 2. run cmake: * Linux: export PATH=$BUILDROOT/i/bin:$PATH export PYTHONHOME=$BUILDROOT/i (only if you want to build your own python) cmake ../krita/3rdparty \ -DINSTALL_ROOT=$BUILDROOT/i \ -DEXTERNALS_DOWNLOAD_DIR=$BUILDROOT/d \ -DCMAKE_INSTALL_PREFIX=BUILDROOT/i * OSX: export PATH=$BUILDROOT/i/bin:$PATH export PYTHONHOME=$BUILDROOT/i (only if you want to build your own python) cmake ../krita/3rdparty/ \ -DCMAKE_INSTALL_PREFIX=$BUILDROOT/i \ -DEXTERNALS_DOWNLOAD_DIR=$BUILDROOT/d \ -DINSTALL_ROOT=$BUILDROOT/i * Windows 32-bit / 64-bit: Note that the cmake command needs to point to your BUILDROOT like /dev/d, not c:\dev\d. set PATH=%BUILDROOT%\i\bin\;%BUILDROOT%\i\lib;%PATH% set PATH=BUILDROOT\i\bin\;BUILDROOT\i\lib;%PATH% cmake ..\krita\3rdparty -DEXTERNALS_DOWNLOAD_DIR=/dev/d -DINSTALL_ROOT=/dev/i -G "MinGW Makefiles" - If you want to build Qt and some other dependencies with parallel jobs, add - `-DSUBMAKE_JOBS=` to the cmake command where is the number of jobs to - run (if your PC has 4 CPU cores, you might want to set it to 5). - + `-DSUBMAKE_JOBS=` to this cmake command where is the number of jobs to + run (if your PC has 4 CPU cores, you might want to set it to 5). For other jobs, + you might need to manually add a -- -j N option, where N is the number of jobs. + - If you don't have Windows 10 SDK and don't want to build Qt with ANGLE, add `-DQT_ENABLE_DYNAMIC_OPENGL=OFF` to the CMake command line args. 3. build the packages: With a judicious application of DEPENDS statements, it's possible to build it all in one go, but in my experience that fails always, so it's better to build the dependencies independently. On Windows: cmake --build . --config RelWithDebInfo --target ext_patch cmake --build . --config RelWithDebInfo --target ext_png2ico On OSX and Windows: cmake --build . --config RelWithDebInfo --target ext_gettext On all operating systems: cmake --build . --config RelWithDebInfo --target ext_qt cmake --build . --config RelWithDebInfo --target ext_zlib cmake --build . --config RelWithDebInfo --target ext_boost Note about boost: check if the headers are installed into i/include/boost, but not into i/include/boost-1.61/boost cmake --build . --config RelWithDebInfo --target ext_eigen3 cmake --build . --config RelWithDebInfo --target ext_exiv2 cmake --build . --config RelWithDebInfo --target ext_fftw3 cmake --build . --config RelWithDebInfo --target ext_ilmbase cmake --build . --config RelWithDebInfo --target ext_jpeg cmake --build . --config RelWithDebInfo --target ext_lcms2 cmake --build . --config RelWithDebInfo --target ext_ocio cmake --build . --config RelWithDebInfo --target ext_openexr OSX Note: You need to first build openexr; that will fail; then you need to set the rpath for the two utilities correctly, then try to build openexr again. install_name_tool -add_rpath $BUILD_ROOT/i/lib $BUILD_ROOT/b/ext_openexr/ext_openexr-prefix/src/ext_openexr-build/IlmImf/./b44ExpLogTable install_name_tool -add_rpath $BUILD_ROOT/i/lib $BUILD_ROOT/b/ext_openexr/ext_openexr-prefix/src/ext_openexr-build/IlmImf/./dwaLookups On All operating systems cmake --build . --config RelWithDebInfo --target ext_png cmake --build . --config RelWithDebInfo --target ext_tiff cmake --build . --config RelWithDebInfo --target ext_gsl cmake --build . --config RelWithDebInfo --target ext_vc cmake --build . --config RelWithDebInfo --target ext_libraw cmake --build . --config RelWithDebInfo --target ext_giflib On Linux (if you want to build your own SIP and PyQt instead of the system one) cmake --build . --config RelWithDebInfo --target ext_sip cmake --build . --config RelWithDebInfo --target ext_pyqt On Windows cmake --build . --config RelWithDebInfo --target ext_freetype cmake --build . --config RelWithDebInfo --target ext_poppler On Linux cmake --build . --config RelWithDebInfo --target ext_kcrash On Windows (if you want to include DrMingw for dumping backtrace on crash) cmake --build . --config RelWithDebInfo --target ext_drmingw On Windows (if you want to include Python scripting) cmake --build . --config RelWithDebInfo --target ext_python cmake --build . --config RelWithDebInfo --target ext_sip cmake --build . --config RelWithDebInfo --target ext_pyqt On Windows and Linux (if you want to include gmic-qt) cmake --build . --config RelWithDebInfo --target ext_gmic Linux Note: poppler should be buildable on Linux as well with a home-built freetype and fontconfig, but I don't know how to make fontconfig find freetype, and on Linux, fontconfig is needed for poppler. Poppler is needed for PDF import. OSX Note: In order to build fontconfig on macOS, you need to have pkg-config installed. You probably need homebrew for that... See http://macappstore.org/pkg-config/ . archives from: files.kde.org/krita/build/dependencies: On Windows, download the approporiate zip file and put in in your i folder http://files.kde.org/krita/build/dependencies/gettext0.19.8.1-iconv1.14-shared-32.zip http://files.kde.org/krita/build/dependencies/gettext0.19.8.1-iconv1.14-shared-64.zip - Take care, these zips contain a libstdc++-6.dll that you don't want in your path when building. On Windows and OSX cmake --build . --config RelWithDebInfo --target ext_kwindowsystem == Build Krita == 1. Make a krita build directory: mkdir BUILDROOT/build 2. Enter the BUILDROOT/build 3. Run On Windows Depending on what you want to use, run this command for MSBuild: cmake ..\krita -G "MinGW Makefiles" -DBoost_DEBUG=OFF -DBOOST_INCLUDEDIR=c:\dev\i\include -DBOOST_DEBUG=ON -DBOOST_ROOT=c:\dev\i -DBOOST_LIBRARYDIR=c:\dev\i\lib -DCMAKE_INSTALL_PREFIX=c:\dev\i -DCMAKE_PREFIX_PATH=c:\dev\i -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DHAVE_MEMORY_LEAK_TRACKER=OFF -Wno-dev -DDEFINE_NO_DEPRECATED=1 Or this to use jom (faster compiling, uses all cores, ships with QtCreator/pre-built Qt binaries): cmake ..\krita -G "MinGW Makefiles" -DBoost_DEBUG=OFF -DBOOST_INCLUDEDIR=c:\dev\i\include -DBOOST_DEBUG=ON -DBOOST_ROOT=c:\dev\i -DBOOST_LIBRARYDIR=c:\dev\i\lib -DCMAKE_INSTALL_PREFIX=c:\dev\i -DCMAKE_PREFIX_PATH=c:\dev\i -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DHAVE_MEMORY_LEAK_TRACKER=OFF -Wno-dev -DDEFINE_NO_DEPRECATED=1 On Linux cmake ../krita -DCMAKE_INSTALL_PREFIX=BUILDROOT/i -DDEFINE_NO_DEPRECATED=1 -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo # Troubleshooting: if you built your own SIP and CMake fails to find it, please set # the following environment variable to the SIP installation directory: export PYTHONPATH=$BUILDROOT/i/sip/ # If you also have KIO installed in the system, don't forget to disable it by bassing to cmake: # cmake -DCMAKE_DISABLE_FIND_PACKAGE_KF5KIO=true . On OSX cmake ../krita -DCMAKE_INSTALL_PREFIX=$BUILDROOT/i -DDEFINE_NO_DEPRECATED=1 -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DBUNDLE_INSTALL_DIR=$BUILDROOT/i/bin -DCMAKE_BUILD_TYPE=RelWithDebInfo 4. Run On Linux and OSX make make install On Windows (replace 4 with the number of jobs to run in parallel) cmake --build . --target install -- -j4 6. Run krita: On Linux BUILDROOT/i/bin/krita On Windows BUILDROOT\i\bin\krita.exe On OSX BUILDROOT/i/bin/krita.app/Contents/MacOS/krita == Packaging a Windows Build == If you want to create a stripped down version of Krita to distribute, after building everything just copy the package_2.cmd file from the "windows" folder inside krita root source folder to BUILDROOT and run it (most likely C:\dev\). That script will copy the necessary files into the specified folder and leave out developer related files. After the script runs there will be two new ZIP files that contain a small portable version of Krita and a separate portable debug version. diff --git a/krita/data/kritarc b/krita/data/kritarc index 16ce4b018a..13cc8372ae 100644 --- a/krita/data/kritarc +++ b/krita/data/kritarc @@ -1,478 +1,478 @@ favoriteCompositeOps=normal,erase,multiply,burn,darken,add,dodge,screen,overlay,soft_light_svg,luminize,lighten,saturation,color ArtColorSel.ColorSpace=0 ArtColorSel.InversedSaturation=false ArtColorSel.Light=0.5 ArtColorSel.LightPieces=11 ArtColorSel.NumRings=7 ArtColorSel.RingAngles=0,0,0,0,0,0,0,0,0,0,0 ArtColorSel.RingPieces=12 ArtColorSel.SelColorA=1 ArtColorSel.SelColorH=0 ArtColorSel.SelColorS=0 ArtColorSel.SelColorX=0.5 BackgroundColorForNewImage=255,255,255 BackgroundOpacityForNewImage=255 BackgroundStyleForNewImage=0 Krita/Ocio/OcioColorManagementMode=0 Krita/Ocio/OcioLockColorVisualRepresentation=false Krita/Ocio/UseOcio=false LastBackGroundColor=\n\n \n\n LastForeGroundColor=\n\n \n\n LastPreset=Basic_circle LastPreset_-1=Basic_circle LineSmoothingDelayDistance=50 LineSmoothingDistance=50 LineSmoothingFinishStabilizedCurve=true LineSmoothingStabilizeSensors=true LineSmoothingTailAggressiveness=0.14999999999999999 LineSmoothingType=1 LineSmoothingUseDelayDistance=true NumberOfLayersForNewImage=2 PaintopPopupDetached=false SpecificColorSelector/ShowColorSpaceSelector=false baseLength=50 colorDepthDef=U8 colorModelDef=RGBA colorProfileDef=sRGB-elle-V2-srgbtrc.icc favoritePresetsTag=★ My Favorites internal_selector_active_color_set=Default globalSnapBoundingBox=false globalSnapExtension=false globalSnapImageBounds=true globalSnapImageCenter=true globalSnapIntersection=false globalSnapNode=false globalSnapOrthogonal=false gridmaincolor=99,99,99 gridmainstyle=0 gridsubdivisioncolor=150,150,150 gridsubdivisionstyle=1 guidesColor=99,99,99 guidesLineStyle=0 imageHeightDef=1200 imageResolutionDef=300 imageWidthDef=1600 levelOfDetailEnabled=true numberOfOnionSkins=10 oninSkinTintColorForward=0,255,0 onionSkinOpacity_-1=173 onionSkinOpacity_-10=22 onionSkinOpacity_-2=163 onionSkinOpacity_-3=147 onionSkinOpacity_-4=127 onionSkinOpacity_-5=107 onionSkinOpacity_-6=84 onionSkinOpacity_-7=63 onionSkinOpacity_-8=48 onionSkinOpacity_-9=33 onionSkinOpacity_0=175 onionSkinOpacity_1=173 onionSkinOpacity_10=22 onionSkinOpacity_2=163 onionSkinOpacity_3=147 onionSkinOpacity_4=127 onionSkinOpacity_5=107 onionSkinOpacity_6=84 onionSkinOpacity_7=63 onionSkinOpacity_8=48 onionSkinOpacity_9=33 onionSkinState_-1=true onionSkinState_-10=false onionSkinState_-2=true onionSkinState_-3=false onionSkinState_-4=false onionSkinState_-5=false onionSkinState_-6=false onionSkinState_-7=false onionSkinState_-8=false onionSkinState_-9=false onionSkinState_0=true onionSkinState_1=true onionSkinState_10=false onionSkinState_2=true onionSkinState_3=false onionSkinState_4=false onionSkinState_5=false onionSkinState_6=false onionSkinState_7=false onionSkinState_8=false onionSkinState_9=false onionSkinTintColorBackward=255,0,0 onionSkinTintFactor=191 presethistory=Basic_tip_default showAdditionalOnionSkinsSettings=true toolbarslider_1=opacity toolbarslider_2=size toolbarslider_3=flow [advancedColorSelector] allowHorizontalLayout=true colorSelectorConfiguration=3|0|5|0 commonColorsAlignment=false commonColorsAutoUpdate=false commonColorsCount=12 commonColorsHeight=16 commonColorsNumCols=1 commonColorsNumRows=1 -commonColorsScrolling=false +commonColorsScrolling=true commonColorsShow=true commonColorsWidth=16 customColorSpaceDepthID=U8 customColorSpaceModel=RGBA customColorSpaceProfile=sRGB built-in lastUsedColorsAlignment=true lastUsedColorsCount=20 lastUsedColorsHeight=16 lastUsedColorsNumCols=1 lastUsedColorsNumRows=1 lastUsedColorsScrolling=true lastUsedColorsShow=true lastUsedColorsWidth=16 minimalShadeSelectorAsGradient=true minimalShadeSelectorLineConfig=0|0.2|0|0|0|0|0;1|0|1|1|0|0|0;2|0|-1|1|0|0|0; minimalShadeSelectorLineHeight=10 minimalShadeSelectorPatchCount=10 popupOnMouseClick=true popupOnMouseOver=false shadeSelectorHideable=false shadeSelectorType=Minimal shadeSelectorUpdateOnBackground=true shadeSelectorUpdateOnForeground=true shadeSelectorUpdateOnLeftClick=false shadeSelectorUpdateOnRightClick=false useCustomColorSpace=false zoomSize=280 [DockWidget sharedtooldocker] TabbedMode=false [KisToolTransform] filterId=Bicubic [MainWindow] State=AAAA/wAAAAD9AAAABAAAAAAAAABJAAADzfwCAAAAA/sAAAAOAFQAbwBvAGwAQgBvAHgBAAAAPwAAA80AAAAxAP////sAAAAkAEYAbABvAHcAUwBoAGEAcABlAEIAbwB4AEQAbwBjAGsAZQByAAAAA2oAAADHAAAAAAAAAAD7AAAAKABGAGwAbwB3AFMAdABlAG4AYwBpAGwAQgBvAHgARABvAGMAawBlAHIAAAADfQAAAMcAAAAAAAAAAAAAAAEAAAEGAAADzfwCAAAAQPsAAAAaAEsAaQBzAEIAaQByAGQAZQB5AGUAQgBvAHgAAAAAAP////8AAAAAAAAAAPsAAAAgAEsAaQBzAFAAYQBsAGUAdAB0AGUARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAaAEsAbwBDAG8AbABvAHIARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAwAEsAaQBzAFQAcgBpAGEAbgBnAGwAZQBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAAAAAAD/////AAAAAAAAAAD7AAAAIgBTAGgAYQBkAG8AdwAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAAgAFMAaABhAHAAZQAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAAaAFMAaABhAHAAZQBTAGUAbABlAGMAdABvAHIAAAAASAAAAEQAAAAAAAAAAPsAAAAkAFMAaQBtAHAAbABlACAAVABlAHgAdAAgAEUAZABpAHQAbwByAAAAAAD/////AAAAAAAAAAD8AAAAPwAAAOIAAACEAQAAHfoAAAAAAQAAAAf7AAAAHgBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAE4AZwEAAAAA/////wAAADoA////+wAAACAAcwBoAGEAcgBlAGQAdABvAG8AbABkAG8AYwBrAGUAcgEAAAAA/////wAAAFMA////+wAAABwATwB2AGUAcgB2AGkAZQB3AEQAbwBjAGsAZQByAQAAAAD/////AAAA2gD////7AAAAKgBTAHAAZQBjAGkAZgBpAGMAQwBvAGwAbwByAFMAZQBsAGUAYwB0AG8AcgAAAAAA/////wAAAL4A////+wAAABYAQwBvAGwAbwByAFMAbABpAGQAZQByAAAAAAD/////AAAAkQD////7AAAAFgBJAG0AYQBnAGUARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAqAFMAaABhAHAAZQBDAG8AbABsAGUAYwB0AGkAbwBuAEQAbwBjAGsAZQByAAAABkgAAAEoAAAAAAAAAAD7AAAARgBLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABEAHkAbgBhAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAAUgAAABIAAAAAAAAAAPsAAAAsAEsAcgBpAHQAYQBTAGgAYQBwAGUALwBLAGkAcwBUAG8AbwBsAEwAaQBuAGUBAAAAPAAAAGkAAAAAAAAAAPsAAAAyAEsAcgBpAHQAYQBTAGgAYQBwAGUALwBLAGkAcwBUAG8AbwBsAEUAbABsAGkAcABzAGUBAAAAkQAAABIAAAAAAAAAAPsAAAAcAEsAaQBzAFQAbwBvAGwAUABvAGwAeQBnAG8AbgEAAACmAAAAEgAAAAAAAAAA+wAAAB4ASwBpAHMAVABvAG8AbABQAG8AbAB5AGwAaQBuAGUBAAAAuwAAABIAAAAAAAAAAPsAAAAWAEsAaQBzAFQAbwBvAGwAUwB0AGEAcgEAAADQAAAAEwAAAAAAAAAA+wAAACoAUwBuAGEAcABHAHUAaQBkAGUAQwBvAG4AZgBpAGcAVwBpAGQAZwBlAHQAAAAA7wAAAHEAAAAAAAAAAPsAAAAyAEsAaQBzAFQAbwBvAGwAQwByAG8AcAAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAA+wAAABIAAAAAAAAAAPsAAABQAEsAcgBpAHQAYQBUAHIAYQBuAHMAZgBvAHIAbQAvAEsAaQBzAFQAbwBvAGwATQBvAHYAZQAgAE8AcAB0AGkAbwBuACAAVwBpAGQAZwBlAHQBAAABEAAAABIAAAAAAAAAAPsAAAA8AEsAaQBzAFQAbwBvAGwAVAByAGEAbgBzAGYAbwByAG0AIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAADwAAAAvAAAAAAAAAAD7AAAATgBLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABNAGUAYQBzAHUAcgBlACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAA8AAAAQgAAAAAAAAAA+wAAAFwASwByAGkAdABhAFMAZQBsAGUAYwB0AGUAZAAvAEsAaQBzAFQAbwBvAGwAQwBvAGwAbwByAFAAaQBjAGsAZQByACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAA8AAAA/wAAAAAAAAAA+wAAAEYASwBpAHMAUgB1AGwAZQByAEEAcwBzAGkAcwB0AGEAbgB0AFQAbwBvAGwAIABPAHAAdABpAG8AbgAgAFcAaQBkAGcAZQB0AQAAADwAAAASAAAAAAAAAAD7AAAASABLAGkAcwBUAG8AbwBsAFAAZQByAHMAcABlAGMAdABpAHYAZQBHAHIAaQBkACAATwBwAHQAaQBvAG4AIABXAGkAZABnAGUAdAEAAAGjAAAAEgAAAAAAAAAA+wAAADIASwBpAHMAVABvAG8AbABHAHIAaQBkACAATwBwAHQAaQBvAG4AIABXAGkAZABnAGUAdAEAAAG4AAAAEwAAAAAAAAAA+wAAAEwASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABSAGUAYwB0AGEAbgBnAHUAbABhAHIAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAAc4AAAASAAAAAAAAAAD7AAAASgBLAGkAcwBUAG8AbwBsAFMAZQBsAGUAYwB0AEUAbABsAGkAcAB0AGkAYwBhAGwAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAAeMAAAASAAAAAAAAAAD7AAAASABLAGkAcwBUAG8AbwBsAFMAZQBsAGUAYwB0AFAAbwBsAHkAZwBvAG4AYQBsACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAH4AAAAEgAAAAAAAAAA+wAAAEQASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABPAHUAdABsAGkAbgBlACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAINAAAAEgAAAAAAAAAA+wAAAEoASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABDAG8AbgB0AGkAZwB1AG8AdQBzACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAIiAAAAEgAAAAAAAAAA+wAAAEQASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABTAGkAbQBpAGwAYQByACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAI3AAAAEgAAAAAAAAAA/AAAAbYAAABaAAAAAAD////6AAAAAAEAAAAC+wAAAC4ASwBvAFMAaABhAHAAZQBDAG8AbABsAGUAYwB0AGkAbwBuAEQAbwBjAGsAZQByAQAAAAD/////AAAAAAAAAAD7AAAAJABTAG0AYQBsAGwAQwBvAGwAbwByAFMAZQBsAGUAYwB0AG8AcgAAAANuAAABBAAAADoA/////AAAASgAAAEwAAAA1QEAAB36AAAAAAEAAAAD+wAAABYASwBpAHMATABhAHkAZQByAEIAbwB4AQAAAAD/////AAABAgD////7AAAAGgBDAGgAYQBuAG4AZQBsAEQAbwBjAGsAZQByAQAAAAD/////AAAAVQD////7AAAALgBLAGkAcwBQAGEAaQBuAHQAZQByAGwAeQBNAGkAeABlAHIARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPwAAAJfAAABrQAAAJcBAAAd+gAAAAABAAAAAvsAAAAYAFAAcgBlAHMAZQB0AEQAbwBjAGsAZQByAQAAAAD/////AAAAZgD////7AAAAGgBQAHIAZQBzAGUAdABIAGkAcwB0AG8AcgB5AQAACPoAAAEGAAAAVQD////7AAAASABLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABCAHIAdQBzAGgAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAPcAAAAaAAAAAAAAAAA+wAAACIAUwB0AHIAbwBrAGUAIABQAHIAbwBwAGUAcgB0AGkAZQBzAAAAAAD/////AAAAAAAAAAD7AAAAFgBTAHQAeQBsAGUARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAgAEsAaQBzAEgAaQBzAHQAbwBnAHIAYQBtAEQAbwBjAGsAAAAAAP////8AAAAAAAAAAPsAAAASAFMAYwByAGkAcAB0AGkAbgBnAAAAAAD/////AAAAAAAAAAD7AAAAMABEAGUAZgBhAHUAbAB0AFQAbwBvAGwAQQByAHIAYQBuAGcAZQBXAGkAZABnAGUAdAAAAAK8AAAAUgAAAAAAAAAA+wAAACIARABlAGYAYQB1AGwAdABUAG8AbwBsAFcAaQBkAGcAZQB0AAAAAxEAAABbAAAAAAAAAAD7AAAAJABLAGkAcwBIAGkAcwB0AG8AZwByAGEAbQBEAG8AYwBrAGUAcgAAAAJCAAAAewAAAAAAAAAA+wAAABgARABpAGcAaQB0AGEAbABNAGkAeABlAHIAAAAAAP////8AAACTAP////sAAAAOAEgAaQBzAHQAbwByAHkAAAADkAAAALQAAACuAP////sAAABOAEsAcgBpAHQAYQBGAGkAbABsAC8ASwBpAHMAVABvAG8AbABHAHIAYQBkAGkAZQBuAHQAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AAAABCgAAAAcAAAAAAAAAAD7AAAARgBLAHIAaQB0AGEARgBpAGwAbAAvAEsAaQBzAFQAbwBvAGwARgBpAGwAbAAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQAAAADUAAAABwAAAAAAAAAAPsAAAA2AEsAcgBpAHQAYQBTAGgAYQBwAGUALwBLAGkAcwBUAG8AbwBsAFIAZQBjAHQAYQBuAGcAbABlAAAAAwUAAABnAAAAAAAAAAD7AAAAIgBDAG8AbQBwAG8AcwBpAHQAaQBvAG4ARABvAGMAawBlAHIAAAAAAP////8AAACMAP////sAAAAqAEEAcgB0AGkAcwB0AGkAYwBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAAAAAAD/////AAAAdwD////7AAAAGgBQAGEAdAB0AGUAcgBuAEQAbwBjAGsAZQByAAAAAtkAAAFJAAAAswD////7AAAAGgBUAGEAcwBrAHMAZQB0AEQAbwBjAGsAZQByAAAAAAD/////AAAAjAD////7AAAAKABTAG4AYQBwAEcAdQBpAGQAZQAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAA4AFQAZQB4AHQARABvAGMAdQBtAGUAbgB0AEkAbgBzAHAAZQBjAHQAaQBvAG4ARABvAGMAawBlAHICAAAEmgAAAhUAAAEqAAAArvsAAAASAEwAdQB0AEQAbwBjAGsAZQByAAAAAAD/////AAABXQD////7AAAAGgBQAGEAbABlAHQAdABlAEQAbwBjAGsAZQByAAAAAAD/////AAAAQgD////7AAAAFABHAHIAaQBkAEQAbwBjAGsAZQByAAAAAAD/////AAABLQD////7AAAAHgBIAGkAcwB0AG8AZwByAGEAbQBEAG8AYwBrAGUAcgAAAAAA/////wAAAEcA////+wAAACoAQQBuAGkAbQBhAHQAaQBvAG4AQwB1AHIAdgBlAHMARABvAGMAawBlAHIAAAAAAP////8AAACMAP////sAAAAyAFMAdgBnAFMAeQBtAGIAbwBsAEMAbwBsAGwAZQBjAHQAaQBvAG4ARABvAGMAawBlAHIAAAAAAP////8AAACMAP////sAAAAWAFQAbwB1AGMAaABEAG8AYwBrAGUAcgAAAAJMAAABMQAAABMA////+wAAABoAQQByAHIAYQBuAGcAZQBEAG8AYwBrAGUAcgAAAAAA/////wAAAHoA////+wAAADoAYwBvAG0AaQBjAHMAXwBwAHIAbwBqAGUAYwB0AF8AbQBhAG4AYQBnAGUAcgBfAGQAbwBjAGsAZQByAAAAAAD/////AAAAuQD////7AAAAKgBxAHUAaQBjAGsAXwBzAGUAdAB0AGkAbgBnAHMAXwBkAG8AYwBrAGUAcgAAAAAA/////wAAAIwA////+wAAABYAUABhAGcAZQByAEQAbwBjAGsAZQByAAAAAAD/////AAAALQD////7AAAAJgBsAGEAcwB0AGQAbwBjAHUAbQBlAG4AdABzAGQAbwBjAGsAZQByAAAAAAD/////AAAAiQD///8AAAACAAAKAAAAALz8AQAAAAH7AAAAGgBUAG8AbwBsAEIAYQByAEQAbwBjAGsAZQByAAAAAAD/////AAAAAAAAAAAAAAADAAAAAAAAAAD8AQAAAAT7AAAAHABGAGwAaQBwAGIAbwBvAGsARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAeAEEAbgBpAG0AYQB0AGkAbwBuAEQAbwBjAGsAZQByAAAAAAD/////AAABBQD////7AAAAIABPAG4AaQBvAG4AUwBrAGkAbgBzAEQAbwBjAGsAZQByAAAAAAD/////AAABNgD////7AAAAHABUAGkAbQBlAGwAaQBuAGUARABvAGMAawBlAHIAAAAAAP////8AAABVAP///wAABU0AAAPNAAAABAAAAAQAAAAIAAAACPwAAAABAAAAAgAAAAIAAAAWAG0AYQBpAG4AVABvAG8AbABCAGEAcgEAAAAA/////wAAAAAAAAAAAAAAHgBCAHIAdQBzAGgAZQBzAEEAbgBkAFMAdAB1AGYAZgEAAAC5/////wAAAAAAAAAA [advancedColorSelector] gamma=2.2000000000000002 hidePopupOnClickCheck=false hsxSettingType=0 lumaB=0.0722 lumaG=0.71519999999999995 lumaR=0.21260000000000001 onDockerResize=0 shadeMyPaintType=HSV zoomSelectorOptions=0 [calligra] ColorSpaceExtensionsPlugins=\\0 ColorSpaceExtensionsPluginsDisabled= ColorSpacePlugins=\\0 ColorSpacePluginsDisabled= DockerPlugins=\\0 DockerPluginsDisabled=textdocumentinspection FlakePlugins=, ShapePlugins=, ToolsBlacklist=CreatePathTool,KoPencilTool,ConnectionTool,KarbonFilterEffectsTool,KritaShape/KisToolText,ArtisticTextTool,TextTool ToolPlugins=,, ToolPluginsDisabled= [KoShapeCollection] QuickShapes=ArtisticText,TextShapeID,EllipseShape,RectangleShape [colorhotkeys] steps_blueyellow=10 steps_hue=36 steps_lightness=10 steps_redgreen=10 steps_saturation=10 [crashprevention] CreatingCanvas=false [hsxColorSlider] hsiH=false hsiI=false hsiS=false hslH=true hslL=true hslS=true hsvH=false hsvS=false hsvV=false hsyH=false hsyS=false hsyY=false [krita] State=AAAA/wAAAAD9AAAABAAAAAAAAABEAAAE6PwCAAAAA/sAAAAOAFQAbwBvAGwAQgBvAHgBAAAARAAABOgAAAAdAQAAA/sAAAAkAEYAbABvAHcAUwBoAGEAcABlAEIAbwB4AEQAbwBjAGsAZQByAAAAA2oAAADHAAAAAAAAAAD7AAAAKABGAGwAbwB3AFMAdABlAG4AYwBpAGwAQgBvAHgARABvAGMAawBlAHIAAAADfQAAAMcAAAAAAAAAAAAAAAEAAAEZAAAE6PwCAAAAO/sAAAAaAEsAaQBzAEIAaQByAGQAZQB5AGUAQgBvAHgAAAAAAP////8AAAAAAAAAAPsAAAAgAEsAaQBzAFAAYQBsAGUAdAB0AGUARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAaAEsAbwBDAG8AbABvAHIARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAwAEsAaQBzAFQAcgBpAGEAbgBnAGwAZQBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAAAAAAD/////AAAAAAAAAAD7AAAAIgBTAGgAYQBkAG8AdwAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAAgAFMAaABhAHAAZQAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAAaAFMAaABhAHAAZQBTAGUAbABlAGMAdABvAHIAAAAASAAAAEQAAAAAAAAAAPsAAAAkAFMAaQBtAHAAbABlACAAVABlAHgAdAAgAEUAZABpAHQAbwByAAAAAAD/////AAAAAAAAAAD8AAAARAAAAKUAAAAAAP////r/////AQAAAAL7AAAAFgBDAG8AbABvAHIAUwBsAGkAZABlAHIAAAAAAP////8AAACuAQAAA/sAAAAaAFAAYQBsAGUAdAB0AGUARABvAGMAawBlAHIAAAAAAP////8AAADtAQAAA/wAAABEAAABMgAAAIgBAAAb+gAAAAIBAAAABvsAAAAcAE8AdgBlAHIAdgBpAGUAdwBEAG8AYwBrAGUAcgEAAAAA/////wAAAKMBAAAD+wAAACAAcwBoAGEAcgBlAGQAdABvAG8AbABkAG8AYwBrAGUAcgEAAAAA/////wAAAJgBAAAD+wAAAB4AQwBvAGwAbwByAFMAZQBsAGUAYwB0AG8AcgBOAGcBAAAAAP////8AAAD7AQAAA/sAAAAqAFMAcABlAGMAaQBmAGkAYwBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAAAAAAD/////AAAA5gEAAAP7AAAAFgBJAG0AYQBnAGUARABvAGMAawBlAHIAAAAAAP////8AAADbAQAAA/sAAAAqAFMAaABhAHAAZQBDAG8AbABsAGUAYwB0AGkAbwBuAEQAbwBjAGsAZQByAAAABkgAAAEoAAAAAAAAAAD7AAAARgBLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABEAHkAbgBhAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAAUgAAABIAAAAAAAAAAPsAAAAsAEsAcgBpAHQAYQBTAGgAYQBwAGUALwBLAGkAcwBUAG8AbwBsAEwAaQBuAGUBAAAAPAAAAGkAAAAAAAAAAPsAAAAyAEsAcgBpAHQAYQBTAGgAYQBwAGUALwBLAGkAcwBUAG8AbwBsAEUAbABsAGkAcABzAGUBAAAAkQAAABIAAAAAAAAAAPsAAAAcAEsAaQBzAFQAbwBvAGwAUABvAGwAeQBnAG8AbgEAAACmAAAAEgAAAAAAAAAA+wAAAB4ASwBpAHMAVABvAG8AbABQAG8AbAB5AGwAaQBuAGUBAAAAuwAAABIAAAAAAAAAAPsAAAAWAEsAaQBzAFQAbwBvAGwAUwB0AGEAcgEAAADQAAAAEwAAAAAAAAAA+wAAACoAUwBuAGEAcABHAHUAaQBkAGUAQwBvAG4AZgBpAGcAVwBpAGQAZwBlAHQAAAAA7wAAAHEAAAAAAAAAAPsAAAAyAEsAaQBzAFQAbwBvAGwAQwByAG8AcAAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAA+wAAABIAAAAAAAAAAPsAAABQAEsAcgBpAHQAYQBUAHIAYQBuAHMAZgBvAHIAbQAvAEsAaQBzAFQAbwBvAGwATQBvAHYAZQAgAE8AcAB0AGkAbwBuACAAVwBpAGQAZwBlAHQBAAABEAAAABIAAAAAAAAAAPsAAAA8AEsAaQBzAFQAbwBvAGwAVAByAGEAbgBzAGYAbwByAG0AIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAADwAAAAvAAAAAAAAAAD7AAAATgBLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABNAGUAYQBzAHUAcgBlACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAA8AAAAQgAAAAAAAAAA+wAAAFwASwByAGkAdABhAFMAZQBsAGUAYwB0AGUAZAAvAEsAaQBzAFQAbwBvAGwAQwBvAGwAbwByAFAAaQBjAGsAZQByACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAA8AAAA/wAAAAAAAAAA+wAAAEYASwBpAHMAUgB1AGwAZQByAEEAcwBzAGkAcwB0AGEAbgB0AFQAbwBvAGwAIABPAHAAdABpAG8AbgAgAFcAaQBkAGcAZQB0AQAAADwAAAASAAAAAAAAAAD7AAAASABLAGkAcwBUAG8AbwBsAFAAZQByAHMAcABlAGMAdABpAHYAZQBHAHIAaQBkACAATwBwAHQAaQBvAG4AIABXAGkAZABnAGUAdAEAAAGjAAAAEgAAAAAAAAAA+wAAADIASwBpAHMAVABvAG8AbABHAHIAaQBkACAATwBwAHQAaQBvAG4AIABXAGkAZABnAGUAdAEAAAG4AAAAEwAAAAAAAAAA+wAAAEwASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABSAGUAYwB0AGEAbgBnAHUAbABhAHIAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAAc4AAAASAAAAAAAAAAD7AAAASgBLAGkAcwBUAG8AbwBsAFMAZQBsAGUAYwB0AEUAbABsAGkAcAB0AGkAYwBhAGwAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAAeMAAAASAAAAAAAAAAD7AAAASABLAGkAcwBUAG8AbwBsAFMAZQBsAGUAYwB0AFAAbwBsAHkAZwBvAG4AYQBsACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAH4AAAAEgAAAAAAAAAA+wAAAEQASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABPAHUAdABsAGkAbgBlACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAINAAAAEgAAAAAAAAAA+wAAAEoASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABDAG8AbgB0AGkAZwB1AG8AdQBzACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAIiAAAAEgAAAAAAAAAA+wAAAEQASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABTAGkAbQBpAGwAYQByACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAI3AAAAEgAAAAAAAAAA/AAAAbYAAABaAAAAAAD////6AAAAAAEAAAAC+wAAAC4ASwBvAFMAaABhAHAAZQBDAG8AbABsAGUAYwB0AGkAbwBuAEQAbwBjAGsAZQByAQAAAAD/////AAAAAAAAAAD7AAAAJABTAG0AYQBsAGwAQwBvAGwAbwByAFMAZQBsAGUAYwB0AG8AcgAAAANuAAABBAAAANkBAAAD/AAAAXcAAAGjAAAA3gEAABv6AAAAAAEAAAAF+wAAABYASwBpAHMATABhAHkAZQByAEIAbwB4AQAAAAD/////AAABBgEAAAP7AAAAIgBDAG8AbQBwAG8AcwBpAHQAaQBvAG4ARABvAGMAawBlAHIAAAAAAP////8AAAC0AQAAA/sAAAAOAEgAaQBzAHQAbwByAHkAAAAAAP////8AAACxAQAAA/sAAAAaAEMAaABhAG4AbgBlAGwARABvAGMAawBlAHIBAAAAAP////8AAACjAQAAA/sAAAAuAEsAaQBzAFAAYQBpAG4AdABlAHIAbAB5AE0AaQB4AGUAcgBEAG8AYwBrAGUAcgAAAAAA/////wAAAAAAAAAA+wAAABgAUAByAGUAcwBlAHQARABvAGMAawBlAHIBAAADGwAAAhEAAACCAQAAA/sAAABIAEsAcgBpAHQAYQBTAGgAYQBwAGUALwBLAGkAcwBUAG8AbwBsAEIAcgB1AHMAaABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAA9wAAABoAAAAAAAAAAD7AAAAIgBTAHQAcgBvAGsAZQAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAAWAFMAdAB5AGwAZQBEAG8AYwBrAGUAcgAAAAAA/////wAAAAAAAAAA+wAAACAASwBpAHMASABpAHMAdABvAGcAcgBhAG0ARABvAGMAawAAAAAA/////wAAAAAAAAAA+wAAABIAUwBjAHIAaQBwAHQAaQBuAGcAAAAAAP////8AAAAAAAAAAPsAAAAwAEQAZQBmAGEAdQBsAHQAVABvAG8AbABBAHIAcgBhAG4AZwBlAFcAaQBkAGcAZQB0AAAAArwAAABSAAAAAAAAAAD7AAAAIgBEAGUAZgBhAHUAbAB0AFQAbwBvAGwAVwBpAGQAZwBlAHQAAAADEQAAAFsAAAAAAAAAAPsAAAAkAEsAaQBzAEgAaQBzAHQAbwBnAHIAYQBtAEQAbwBjAGsAZQByAAAAAkIAAAB7AAAAAAAAAAD7AAAAGABEAGkAZwBpAHQAYQBsAE0AaQB4AGUAcgAAAAAA/////wAAAKEBAAAD+wAAAE4ASwByAGkAdABhAEYAaQBsAGwALwBLAGkAcwBUAG8AbwBsAEcAcgBhAGQAaQBlAG4AdAAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQAAAAEKAAAABwAAAAAAAAAAPsAAABGAEsAcgBpAHQAYQBGAGkAbABsAC8ASwBpAHMAVABvAG8AbABGAGkAbABsACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAAAAANQAAAAHAAAAAAAAAAA+wAAADYASwByAGkAdABhAFMAaABhAHAAZQAvAEsAaQBzAFQAbwBvAGwAUgBlAGMAdABhAG4AZwBsAGUAAAADBQAAAGcAAAAAAAAAAPsAAAAqAEEAcgB0AGkAcwB0AGkAYwBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAAAAAAD/////AAAAgAEAAAP7AAAAGgBQAGEAdAB0AGUAcgBuAEQAbwBjAGsAZQByAAAAAtkAAAFJAAAAvAEAAAP7AAAAGgBUAGEAcwBrAHMAZQB0AEQAbwBjAGsAZQByAAAAAAD/////AAAAmAEAAAP7AAAAKABTAG4AYQBwAEcAdQBpAGQAZQAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAA4AFQAZQB4AHQARABvAGMAdQBtAGUAbgB0AEkAbgBzAHAAZQBjAHQAaQBvAG4ARABvAGMAawBlAHICAAAEmgAAAhUAAAEqAAAArvsAAAASAEwAdQB0AEQAbwBjAGsAZQByAAAAA3wAAAEuAAABsQEAAAP7AAAAFABHAHIAaQBkAEQAbwBjAGsAZQByAAAAAAD/////AAABNgEAAAP7AAAAHgBIAGkAcwB0AG8AZwByAGEAbQBEAG8AYwBrAGUAcgAAAAAA/////wAAAFABAAAD+wAAABoAUAByAGUAcwBlAHQASABpAHMAdABvAHIAeQAAAAAA/////wAAAHABAAAD+wAAADIAUwB2AGcAUwB5AG0AYgBvAGwAQwBvAGwAbABlAGMAdABpAG8AbgBEAG8AYwBrAGUAcgAAAAAA/////wAAAAAAAAAA+wAAABYAVABvAHUAYwBoAEQAbwBjAGsAZQByAAAAAAD/////AAAAHAEAAAP7AAAAGgBBAHIAcgBhAG4AZwBlAEQAbwBjAGsAZQByAAAAAAD/////AAAAkAEAAAP7AAAAKgBBAG4AaQBtAGEAdABpAG8AbgBDAHUAcgB2AGUAcwBEAG8AYwBrAGUAcgAAAAAA/////wAAAJgBAAADAAAAAgAAB4AAAAC8/AEAAAAB+wAAABoAVABvAG8AbABCAGEAcgBEAG8AYwBrAGUAcgAAAAAA/////wAAAAAAAAAAAAAAAwAAAAAAAAAA/AEAAAAE+wAAABwARgBsAGkAcABiAG8AbwBrAEQAbwBjAGsAZQByAAAAAAD/////AAAAAAAAAAD7AAAAIABPAG4AaQBvAG4AUwBrAGkAbgBzAEQAbwBjAGsAZQByAAAAAAD/////AAABSAEAAAP7AAAAHgBBAG4AaQBtAGEAdABpAG8AbgBEAG8AYwBrAGUAcgAAAAAA/////wAAASUBAAAD+wAAABwAVABpAG0AZQBsAGkAbgBlAEQAbwBjAGsAZQByAAAAAAD/////AAAAlgEAAAMAAAihAAAE6AAAAAQAAAAEAAAACAAAAAj8AAAAAQAAAAIAAAACAAAAFgBtAGEAaQBuAFQAbwBvAGwAQgBhAHIBAAAAAP////8AAAAAAAAAAAAAAB4AQgByAHUAcwBoAGUAcwBBAG4AZABTAHQAdQBmAGYBAAAA1P////8AAAAAAAAAAA== ToolBarsMovable=Enabled [krita][DockWidget AnimationCurvesDocker] Collapsed=false DockArea=2 Locked=false height=421 width=448 xPosition=0 yPosition=0 [krita][DockWidget AnimationDocker] Collapsed=false DockArea=8 Locked=false height=160 width=280 xPosition=0 yPosition=0 [krita][DockWidget ArtisticColorSelector] Collapsed=false DockArea=2 Locked=false height=294 width=337 xPosition=0 yPosition=0 [krita][DockWidget ChannelDocker] Collapsed=false DockArea=2 Locked=false height=30 width=100 xPosition=0 yPosition=0 [krita][DockWidget ColorSelectorNg] Collapsed=false DockArea=2 Locked=false height=176 width=281 xPosition=0 yPosition=20 [krita][DockWidget ColorSlider] Collapsed=false DockArea=2 Locked=false height=460 width=640 xPosition=0 yPosition=20 [krita][DockWidget CompositionDocker] Collapsed=false DockArea=2 Locked=false height=300 width=400 xPosition=0 yPosition=0 [krita][DockWidget DigitalMixer] Collapsed=false DockArea=2 Locked=false height=30 width=100 xPosition=0 yPosition=0 [krita][DockWidget GridDocker] Collapsed=false DockArea=2 Locked=false height=342 width=441 xPosition=0 yPosition=0 [krita][DockWidget HistogramDocker] Collapsed=false DockArea=2 Locked=false height=91 width=281 xPosition=0 yPosition=20 [krita][DockWidget History] Collapsed=false DockArea=2 Locked=false height=460 width=640 xPosition=0 yPosition=20 [krita][DockWidget ImageDocker] Collapsed=false DockArea=2 Locked=false height=300 width=399 xPosition=0 yPosition=0 [krita][DockWidget KisLayerBox] DockArea=2 Locked=false height=358 width=281 xPosition=0 yPosition=20 [krita][DockWidget LutDocker] Collapsed=false DockArea=2 Locked=false height=286 width=357 xPosition=0 yPosition=0 [krita][DockWidget OnionSkinsDocker] Collapsed=false DockArea=8 Locked=false height=210 width=356 xPosition=0 yPosition=0 [krita][DockWidget OverviewDocker] Collapsed=false DockArea=2 Locked=false height=30 width=100 xPosition=0 yPosition=0 [krita][DockWidget PaletteDocker] Collapsed=false DockArea=2 Locked=false height=219 width=256 xPosition=0 yPosition=0 [krita][DockWidget PatternDocker] Collapsed=false DockArea=2 Locked=false height=30 width=100 xPosition=0 yPosition=0 [krita][DockWidget PresetDocker] Collapsed=false DockArea=2 Locked=false height=460 width=640 xPosition=0 yPosition=20 [krita][DockWidget PresetHistory] Collapsed=false DockArea=2 Locked=false height=30 width=100 xPosition=0 yPosition=0 [krita][DockWidget Shape Properties] DockArea=2 Locked=false height=480 width=640 xPosition=0 yPosition=0 [krita][DockWidget ShapeCollectionDocker] Collapsed=false DockArea=2 Locked=false height=0 width=0 xPosition=0 yPosition=20 [krita][DockWidget SmallColorSelector] DockArea=2 Locked=false height=460 width=640 xPosition=0 yPosition=20 [krita][DockWidget SpecificColorSelector] DockArea=2 Locked=false height=460 width=640 xPosition=0 yPosition=20 [krita][DockWidget TasksetDocker] Collapsed=false DockArea=2 Locked=false height=300 width=400 xPosition=0 yPosition=0 [krita][DockWidget TimelineDocker] Collapsed=false DockArea=8 Locked=false height=30 width=100 xPosition=0 yPosition=0 [krita][DockWidget ToolBox] DockArea=1 Locked=false height=610 width=63 xPosition=0 yPosition=20 [krita][DockWidget sharedtooldocker] Collapsed=false DockArea=2 Locked=false height=460 width=640 xPosition=0 yPosition=20 [krita][Toolbar mainToolBar] ToolButtonStyle=IconOnly [TemplateChooserDialog] ShowCustomDocumentWidgetByDefault=true LastReturnType=Custom Document [theme] Theme=Krita dark [python] enable_colorspace=true enable_comics_project_management_tools=true enable_documenttools=true enable_exportlayers=true enable_filtermanager=true enable_lastdocumentsdocker=true enable_quick_settings_docker=true enable_scripter=true enable_tenbrushes=true enable_tenscripts=true diff --git a/krita/krita.action b/krita/krita.action index 96aa1b66e8..c6b06cc039 100644 --- a/krita/krita.action +++ b/krita/krita.action @@ -1,3602 +1,3592 @@ General Open Resources Folder Opens a file browser at the location Krita saves resources such as brushes to. Opens a file browser at the location Krita saves resources such as brushes to. Open Resources Folder 0 0 false Cleanup removed files... Cleanup removed files Cleanup removed files 0 0 false C&ascade Cascade Cascade 10 0 false &Tile Tile Tile 10 0 false Create Resource Bundle... Create Resource Bundle Create Resource Bundle 0 0 false Show File Toolbar Show File Toolbar Show File Toolbar false Show color selector Show color selector Show color selector Shift+I false Show MyPaint shade selector Show MyPaint shade selector Show MyPaint shade selector Shift+M false Show minimal shade selector Show minimal shade selector Show minimal shade selector Shift+N false Show color history Show color history Show color history H false Show common colors Show common colors Show common colors U false Show Tool Options Show Tool Options Show Tool Options \ false Show Brush Editor Show Brush Editor Show Brush Editor F5 false Show Brush Presets Show Brush Presets Show Brush Presets F6 false Toggle Tablet Debugger Toggle Tablet Debugger Toggle Tablet Debugger 0 0 Ctrl+Shift+T false Show system information for bug reports. Show system information for bug reports. Show system information for bug reports. false Rename Composition... Rename Composition Rename Composition 0 0 false Update Composition Update Composition Update Composition 0 0 false Use multiple of 2 for pixel scale Use multiple of 2 for pixel scale Use multiple of 2 for pixel scale Use multiple of 2 for pixel scale 1 0 true &Invert Selection Invert current selection Invert Selection 10000000000 100 Ctrl+Shift+I false Painting lightness-increase Make brush color lighter Make brush color lighter Make brush color lighter 0 0 L false lightness-decrease Make brush color darker Make brush color darker Make brush color darker 0 0 K false Make brush color more saturated Make brush color more saturated Make brush color more saturated false Make brush color more desaturated Make brush color more desaturated Make brush color more desaturated false Shift brush color hue clockwise Shift brush color hue clockwise Shift brush color hue clockwise false Shift brush color hue counter-clockwise Shift brush color hue counter-clockwise Shift brush color hue counter-clockwise false Make brush color more red Make brush color more red Make brush color more red false Make brush color more green Make brush color more green Make brush color more green false Make brush color more blue Make brush color more blue Make brush color more blue false Make brush color more yellow Make brush color more yellow Make brush color more yellow false opacity-increase Increase opacity Increase opacity Increase opacity 0 0 O false opacity-decrease Decrease opacity Decrease opacity Decrease opacity 0 0 I false draw-eraser Set eraser mode Set eraser mode Set eraser mode 10000 0 E true view-refresh Reload Original Preset Reload Original Preset Reload Original Preset 10000 false transparency-unlocked Preserve Alpha Preserve Alpha Preserve Alpha 10000 true transform_icons_penPressure Use Pen Pressure Use Pen Pressure Use Pen Pressure 10000 true symmetry-horizontal Horizontal Mirror Tool Horizontal Mirror Tool Horizontal Mirror Tool 0 true symmetry-vertical Vertical Mirror Tool Vertical Mirror Tool Vertical Mirror Tool 0 true Hide Mirror X Line Hide Mirror X Line Hide Mirror X Line 10000 true Hide Mirror Y Line Hide Mirror Y Line Hide Mirror Y Line 10000 true Lock Lock X Line Lock X Line 10000 true Lock Y Line Lock Y Line Lock Y Line 10000 true Move to Canvas Center Move to Canvas Center X Move to Canvas Center X 10000 false Move to Canvas Center Y Move to Canvas Center Y Move to Canvas Center Y 10000 false &Toggle Selection Display Mode Toggle Selection Display Mode Toggle Selection Display Mode 0 0 false Next Favourite Preset Next Favourite Preset Next Favourite Preset , false Previous Favourite Preset Previous Favourite Preset Previous Favourite Preset . false preset-switcher Switch to Previous Preset Switch to Previous Preset Switch to Previous Preset / false Hide Brushes and Stuff Toolbar Hide Brushes and Stuff Toolbar Hide Brushes and Stuff Toolbar true Reset Foreground and Background Color Reset Foreground and Background Color Reset Foreground and Background Color D false Swap Foreground and Background Color Swap Foreground and Background Color Swap Foreground and Background Color X false Selection Mode: Add Selection Mode: Add Selection Mode: Add A false Selection Mode: Subtract Selection Mode: Subtract Selection Mode: Subtract S false Selection Mode: Intersect Selection Mode: Intersect Selection Mode: Intersect false Selection Mode: Replace Selection Mode: Replace Selection Mode: Replace R false smoothing-weighted Brush Smoothing: Weighted Brush Smoothing: Weighted Brush Smoothing: Weighted false smoothing-no Brush Smoothing: Disabled Brush Smoothing: Disabled Brush Smoothing: Disabled false smoothing-stabilizer Brush Smoothing: Stabilizer Brush Smoothing: Stabilizer Brush Smoothing: Stabilizer false brushsize-decrease Decrease Brush Size Decrease Brush Size Decrease Brush Size 0 0 [ false smoothing-basic Brush Smoothing: Basic Brush Smoothing: Basic Brush Smoothing: Basic false brushsize-increase Increase Brush Size Increase Brush Size Increase Brush Size 0 0 ] false Toggle Assistant Toggle Assistant ToggleAssistant Ctrl+Shift+L true Undo Polygon Selection Points Undo Polygon Selection Points Undo Polygon Selection Points Shift+Z false Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) 10000 1 Ctrl+Shift+Backspace false Fill with Background Color (Opacity) Fill with Background Color (Opacity) Fill with Background Color (Opacity) 10000 1 Ctrl+Backspace false Fill with Pattern (Opacity) Fill with Pattern (Opacity) Fill with Pattern (Opacity) 10000 1 false Convert &to Shape Convert to Shape Convert to Shape 10000000000 0 false &Show Global Selection Mask Shows global selection as a usual selection mask in <interface>Layers</interface> docker Show Global Selection Mask 100000 100 true Filters color-to-alpha &Color to Alpha... Color to Alpha Color to Alpha 10000 0 false &Top Edge Detection Top Edge Detection Top Edge Detection 10000 0 false &Index Colors... Index Colors Index Colors 10000 0 false Emboss Horizontal &Only Emboss Horizontal Only Emboss Horizontal Only 10000 0 false D&odge Dodge Dodge 10000 0 false &Sharpen Sharpen Sharpen 10000 0 false B&urn Burn Burn 10000 0 false &Mean Removal Mean Removal Mean Removal 10000 0 false &Gaussian Blur... Gaussian Blur Gaussian Blur 10000 0 false Emboss &in All Directions Emboss in All Directions Emboss in All Directions 10000 0 false &Small Tiles... Small Tiles Small Tiles 10000 0 false &Levels... Levels Levels 10000 0 Ctrl+L false &Sobel... Sobel Sobel 10000 0 false &Wave... Wave Wave 10000 0 false &Motion Blur... Motion Blur Motion Blur 10000 0 false &Invert Invert Invert 10000 0 Ctrl+I false &Color Adjustment curves... Color Adjustment curves Color Adjustment curves 10000 0 Ctrl+M false Pi&xelize... Pixelize Pixelize 10000 0 false Emboss (&Laplacian) Emboss (Laplacian) Emboss (Laplacian) 10000 0 false &Left Edge Detection Left Edge Detection Left Edge Detection 10000 0 false &Blur... Blur Blur 10000 0 false &Raindrops... Raindrops Raindrops 10000 0 false &Bottom Edge Detection Bottom Edge Detection Bottom Edge Detection 10000 0 false &Random Noise... Random Noise Random Noise 10000 0 false &Brightness/Contrast curve... Brightness/Contrast curve Brightness/Contrast curve 10000 0 false Colo&r Balance... Color Balance Color Balance 10000 0 Ctrl+B false &Phong Bumpmap... Phong Bumpmap Phong Bumpmap 10000 0 false &Desaturate Desaturate Desaturate 10000 0 Ctrl+Shift+U false Color &Transfer... Color Transfer Color Transfer 10000 0 false Emboss &Vertical Only Emboss Vertical Only Emboss Vertical Only 10000 0 false &Lens Blur... Lens Blur Lens Blur 10000 0 false M&inimize Channel Minimize Channel Minimize Channel 10000 0 false M&aximize Channel Maximize Channel Maximize Channel 10000 0 false &Oilpaint... Oilpaint Oilpaint 10000 0 false &Right Edge Detection Right Edge Detection Right Edge Detection 10000 0 false &Auto Contrast Auto Contrast Auto Contrast 10000 0 false &Round Corners... Round Corners Round Corners 10000 0 false &Unsharp Mask... Unsharp Mask Unsharp Mask 10000 0 false &Emboss with Variable Depth... Emboss with Variable Depth Emboss with Variable Depth 10000 0 false Emboss &Horizontal && Vertical Emboss Horizontal & Vertical Emboss Horizontal & Vertical 10000 0 false Random &Pick... Random Pick Random Pick 10000 0 false &Gaussian Noise Reduction... Gaussian Noise Reduction Gaussian Noise Reduction 10000 0 false &Posterize... Posterize Posterize 10000 0 false &Wavelet Noise Reducer... Wavelet Noise Reducer Wavelet Noise Reducer 10000 0 false &HSV Adjustment... HSV Adjustment HSV Adjustment 10000 0 Ctrl+U false Tool Shortcuts Dynamic Brush Tool Dynamic Brush Tool Dynamic Brush Tool false Crop Tool Crop the image to an area Crop the image to an area C false Polygon Tool Polygon Tool. Shift-mouseclick ends the polygon. Polygon Tool. Shift-mouseclick ends the polygon. false Rectangle Tool Rectangle Tool Rectangle Tool false Multibrush Tool Multibrush Tool Multibrush Tool Q false Colorize Mask Tool Colorize Mask Tool Colorize Mask Tool Smart Patch Tool Smart Patch Tool Smart Patch Tool Pan Tool Pan Tool Pan Tool Select Shapes Tool Select Shapes Tool Select Shapes Tool false Color Picker Select a color from the image or current layer Select a color from the image or current layer P false Outline Selection Tool Outline Selection Tool Outline Selection Tool false Bezier Curve Selection Tool Select a Bezier Curve Selection Tool false Similar Color Selection Tool Select a Similar Color Selection Tool false Fill Tool Fill a contiguous area of color with a color, or fill a selection. Fill a contiguous area of color with a color, or fill a selection. F false Line Tool Line Tool Line Tool false Freehand Path Tool Freehand Path Tool Freehand Path Tool false Bezier Curve Tool Bezier Curve Tool. Shift-mouseclick or double-click ends the curve. Bezier Curve Tool. Shift-mouseclick or double-click ends the curve. false Ellipse Tool Ellipse Tool Ellipse Tool false Freehand Brush Tool Freehand Brush Tool Freehand Brush Tool B false Create object Create object Create object false Elliptical Selection Tool Elliptical Selection Tool Elliptical Selection Tool J false Contiguous Selection Tool Contiguous Selection Tool Contiguous Selection Tool false Pattern editing Pattern editing Pattern editing false Review Review Review false Draw a gradient. Draw a gradient. Draw a gradient. G false Polygonal Selection Tool Polygonal Selection Tool Polygonal Selection Tool false Measurement Tool Measure the distance between two points Measure the distance between two points false Rectangular Selection Tool Rectangular Selection Tool Rectangular Selection Tool Ctrl+R false Move Tool Move a layer Move a layer T false Vector Image Tool Vector Image (EMF/WMF/SVM/SVG) tool Vector Image (EMF/WMF/SVM/SVG) tool false Calligraphy Calligraphy Calligraphy false Path editing Path editing Path editing false Zoom Tool Zoom Tool Zoom Tool false Polyline Tool Polyline Tool. Shift-mouseclick ends the polyline. Polyline Tool. Shift-mouseclick ends the polyline. false Transform Tool Transform a layer or a selection Transform a layer or a selection Ctrl+T false Assistant Tool Assistant Tool Assistant Tool false - - - Text tool - - Text tool - Text tool - - false - - Gradient Editing Tool Gradient editing Gradient editing false Reference Images Tool Reference Images Tool Reference Images Tool false Blending Modes Select Normal Blending Mode Select Normal Blending Mode Select Normal Blending Mode 0 0 Alt+Shift+N false Select Dissolve Blending Mode Select Dissolve Blending Mode Select Dissolve Blending Mode 0 0 Alt+Shift+I false Select Behind Blending Mode Select Behind Blending Mode Select Behind Blending Mode 0 0 Alt+Shift+Q false Select Clear Blending Mode Select Clear Blending Mode Select Clear Blending Mode 0 0 Alt+Shift+R false Select Darken Blending Mode Select Darken Blending Mode Select Darken Blending Mode 0 0 Alt+Shift+K false Select Multiply Blending Mode Select Multiply Blending Mode Select Multiply Blending Mode 0 0 Alt+Shift+M false Select Color Burn Blending Mode Select Color Burn Blending Mode Select Color Burn Blending Mode 0 0 Alt+Shift+B false Select Linear Burn Blending Mode Select Linear Burn Blending Mode Select Linear Burn Blending Mode 0 0 Alt+Shift+A false Select Lighten Blending Mode Select Lighten Blending Mode Select Lighten Blending Mode 0 0 Alt+Shift+G false Select Screen Blending Mode Select Screen Blending Mode Select Screen Blending Mode 0 0 Alt+Shift+S false Select Color Dodge Blending Mode Select Color Dodge Blending Mode Select Color Dodge Blending Mode 0 0 Alt+Shift+D false Select Linear Dodge Blending Mode Select Linear Dodge Blending Mode Select Linear Dodge Blending Mode 0 0 Alt+Shift+W false Select Overlay Blending Mode Select Overlay Blending Mode Select Overlay Blending Mode 0 0 Alt+Shift+O false Select Hard Overlay Blending Mode Select Hard Overlay Blending Mode Select Hard Overlay Blending Mode 0 0 Alt+Shift+P false Select Soft Light Blending Mode Select Soft Light Blending Mode Select Soft Light Blending Mode 0 0 Alt+Shift+F false Select Hard Light Blending Mode Select Hard Light Blending Mode Select Hard Light Blending Mode 0 0 Alt+Shift+H false Select Vivid Light Blending Mode Select Vivid Light Blending Mode Select Vivid Light Blending Mode 0 0 Alt+Shift+V false Select Linear Light Blending Mode Select Linear Light Blending Mode Select Linear Light Blending Mode 0 0 Alt+Shift+J false Select Pin Light Blending Mode Select Pin Light Blending Mode Select Pin Light Blending Mode 0 0 Alt+Shift+Z false Select Hard Mix Blending Mode Select Hard Mix Blending Mode Select Hard Mix Blending Mode 0 0 Alt+Shift+L false Select Difference Blending Mode Select Difference Blending Mode Select Difference Blending Mode 0 0 Alt+Shift+E false Select Exclusion Blending Mode Select Exclusion Blending Mode Select Exclusion Blending Mode 0 0 Alt+Shift+X false Select Hue Blending Mode Select Hue Blending Mode Select Hue Blending Mode 0 0 Alt+Shift+U false Select Saturation Blending Mode Select Saturation Blending Mode Select Saturation Blending Mode 0 0 Alt+Shift+T false Select Color Blending Mode Select Color Blending Mode Select Color Blending Mode 0 0 Alt+Shift+C false Select Luminosity Blending Mode Select Luminosity Blending Mode Select Luminosity Blending Mode 0 0 Alt+Shift+Y false Animation Previous frame Move to previous frame Move to previous frame 1 0 false Next frame Move to next frame Move to next frame 1 0 false Play / pause animation Play / pause animation Play / pause animation 1 0 false addblankframe Create Blank Frame Add blank frame Add blank frame 100000 0 false addduplicateframe Create Duplicate Frame Add duplicate frame Add duplicate frame 100000 0 false Toggle onion skin Toggle onion skin Toggle onion skin 100000 0 false Previous Keyframe false Next Keyframe false First Frame false Last Frame false Auto Frame Mode true true Show in Timeline true Insert Keyframe Left Insert keyframes to the left of selection, moving the tail of animation to the right. 100000 0 false Insert Keyframe Right Insert keyframes to the right of selection, moving the tail of animation to the right. 100000 0 false Insert Multiple Keyframes Insert several keyframes based on user parameters. 100000 0 false Remove Frame and Pull Remove keyframes moving the tail of animation to the left 100000 0 false deletekeyframe Remove Keyframe Remove keyframes without moving anything around 100000 0 false Insert Column Left Insert column to the left of selection, moving the tail of animation to the right 100000 0 false Insert Column Right Insert column to the right of selection, moving the tail of animation to the right 100000 0 false Insert Multiple Columns Insert several columns based on user parameters. 100000 0 false Remove Column and Pull Remove columns moving the tail of animation to the left 100000 0 false Remove Column Remove columns without moving anything around 100000 0 false Insert Hold Frame Insert a hold frame after every keyframe 100000 0 false Insert Multiple Hold Frames Insert N hold frames after every keyframe 100000 0 false Remove Hold Frame Remove a hold frame after every keyframe 100000 0 false Remove Multiple Hold Frames Remove N hold frames after every keyframe 100000 0 false Insert Hold Column Insert a hold column into the frame at the current position 100000 0 false Insert Multiple Hold Columns Insert N hold columns into the frame at the current position 100000 0 false Remove Hold Column Remove a hold column from the frame at the current position 100000 0 false Remove Multiple Hold Columns Remove N hold columns from the frame at the current position 100000 0 false Mirror Frames Mirror frames' position 100000 0 false Mirror Columns Mirror columns' position 100000 0 false Copy to Clipboard Copy frames to clipboard 100000 0 false Cut to Clipboard Cut frames to clipboard 100000 0 false Paste from Clipboard Paste frames from clipboard 100000 0 false Copy Columns to Clipboard Copy columns to clipboard 100000 0 false Cut Columns to Clipboard Cut columns to clipboard 100000 0 false Paste Columns from Clipboard Paste columns from clipboard 100000 0 false Set Start Time 100000 0 false Set End Time 100000 0 false Update Playback Range 100000 0 false Layers Activate next layer Activate next layer Activate next layer 1000 0 PgUp false Activate previous layer Activate previous layer Activate previous layer 1000 0 PgDown false Activate previously selected layer Activate previously selected layer Activate previously selected layer 1000 0 ; false groupLayer &Group Layer Group Layer Group Layer 1000 0 false cloneLayer &Clone Layer Clone Layer Clone Layer 1000 0 false vectorLayer &Vector Layer Vector Layer Vector Layer 1000 0 false filterLayer &Filter Layer... Filter Layer Filter Layer 1000 0 false fillLayer &Fill Layer... Fill Layer Fill Layer 1000 0 false fileLayer &File Layer... File Layer File Layer 1000 0 false transparencyMask &Transparency Mask Transparency Mask Transparency Mask 100000 0 false filterMask &Filter Mask... Filter Mask Filter Mask 100000 0 false filterMask &Colorize Mask Colorize Mask Colorize Mask 100000 0 false transformMask &Transform Mask... Transform Mask Transform Mask 100000 0 false selectionMask &Local Selection Local Selection Local Selection 100000 0 false view-filter &Isolate Layer Isolate Layer Isolate Layer 1000 0 true layer-locked &Toggle layer lock Toggle layer lock Toggle layer lock 1000 0 false visible Toggle layer &visibility Toggle layer visibility Toggle layer visibility 1000 0 false transparency-locked Toggle layer &alpha Toggle layer alpha Toggle layer alpha 1000 0 false transparency-enabled Toggle layer alpha &inheritance Toggle layer alpha inheritance Toggle layer alpha inheritance 1000 0 false paintLayer &Paint Layer Paint Layer Paint Layer 1000 0 Insert false &New Layer From Visible New layer from visible New layer from visible 1000 0 false duplicatelayer &Duplicate Layer or Mask Duplicate Layer or Mask Duplicate Layer or Mask 1000 0 Ctrl+J false &Cut Selection to New Layer Cut Selection to New Layer Cut Selection to New Layer 100000000 1 Ctrl+Shift+J false Copy &Selection to New Layer Copy Selection to New Layer Copy Selection to New Layer 100000000 0 Ctrl+Alt+J false Copy Layer Copy layer to clipboard Copy layer to clipboard 1000 0 false Cut Layer Cut layer to clipboard Cut layer to clipboard 1000 0 false Paste Layer Paste layer from clipboard Paste layer from clipboard 1000 0 false Quick Group Create a group layer containing selected layers Quick Group 1000 0 Ctrl+G false Quick Ungroup Remove grouping of the layers or remove one layer out of the group Quick Ungroup 100000 0 Ctrl+Alt+G false Quick Clipping Group Group selected layers and add a layer with clipped alpha channel Quick Clipping Group 100000 0 Ctrl+Shift+G false All Layers Select all layers Select all layers 10000 0 false Visible Layers Select all visible layers Select all visible layers 10000 0 false Locked Layers Select all locked layers Select all locked layers 10000 0 false Invisible Layers Select all invisible layers Select all invisible layers 10000 0 false Unlocked Layers Select all unlocked layers Select all unlocked layers 10000 0 false document-save &Save Layer/Mask... Save Layer/Mask Save Layer/Mask 1000 0 false document-save Save Vector Layer as SVG... Save Vector Layer as SVG Save Vector Layer as SVG 1000 0 false document-save Save &Group Layers... Save Group Layers Save Group Layers 100000 0 false Convert group to &animated layer Convert child layers into animation frames Convert child layers into animation frames 100000 0 false fileLayer to &File Layer Saves out the layers into a new image and then references that image. Convert to File Layer 100000 0 false I&mport Layer... Import Layer Import Layer 100000 0 false paintLayer &as Paint Layer... as Paint Layer as Paint Layer 1000 0 false transparencyMask as &Transparency Mask... as Transparency Mask as Transparency Mask 1000 0 false filterMask as &Filter Mask... as Filter Mask as Filter Mask 1000 0 false selectionMask as &Selection Mask... as Selection Mask as Selection Mask 1000 0 false paintLayer to &Paint Layer to Paint Layer to Paint Layer 1000 0 false transparencyMask to &Transparency Mask to Transparency Mask to Transparency Mask 1000 0 false filterMask to &Filter Mask... to Filter Mask to Filter Mask 1000 0 false selectionMask to &Selection Mask to Selection Mask to Selection Mask 1000 0 false transparencyMask &Alpha into Mask Alpha into Mask Alpha into Mask 100000 10 false transparency-enabled &Write as Alpha Write as Alpha Write as Alpha 1000000 1 false document-save &Save Merged... Save Merged Save Merged 1000000 0 false split-layer Split Layer... Split Layer Split Layer 1000 0 false Wavelet Decompose ... Wavelet Decompose Wavelet Decompose 1000 1 false symmetry-horizontal Mirror Layer Hori&zontally Mirror Layer Horizontally Mirror Layer Horizontally 1000 1 false symmetry-vertical Mirror Layer &Vertically Mirror Layer Vertically Mirror Layer Vertically 1000 1 false &Rotate Layer... Rotate Layer Rotate Layer 1000 1 false object-rotate-right Rotate &Layer 90° to the Right Rotate Layer 90° to the Right Rotate Layer 90° to the Right 1000 1 false object-rotate-left Rotate Layer &90° to the Left Rotate Layer 90° to the Left Rotate Layer 90° to the Left 1000 1 false Rotate Layer &180° Rotate Layer 180° Rotate Layer 180° 1000 1 false Scale &Layer to new Size... Scale Layer to new Size Scale Layer to new Size 100000 1 false &Shear Layer... Shear Layer Shear Layer 1000 1 false symmetry-horizontal Mirror All Layers Hori&zontally Mirror All Layers Horizontally Mirror All Layers Horizontally 1000 1 false symmetry-vertical Mirror All Layers &Vertically Mirror All Layers Vertically Mirror All Layers Vertically 1000 1 false &Rotate All Layers... Rotate All Layers Rotate All Layers 1000 1 false object-rotate-right Rotate All &Layers 90° to the Right Rotate All Layers 90° to the Right Rotate All Layers 90° to the Right 1000 1 false object-rotate-left Rotate All Layers &90° to the Left Rotate All Layers 90° to the Left Rotate All Layers 90° to the Left 1000 1 false Rotate All Layers &180° Rotate All Layers 180° Rotate All Layers 180° 1000 1 false Scale All &Layers to new Size... Scale All Layers to new Size Scale All Layers to new Size 100000 1 false &Shear All Layers... Shear All Layers Shear All Layers 1000 1 false &Offset Layer... Offset Layer Offset Layer 100000 1 false Clones &Array... Clones Array Clones Array 100000 0 false &Edit metadata... Edit metadata Edit metadata 100000 1 false &Histogram... Histogram Histogram 100000 0 false &Convert Layer Color Space... Convert Layer Color Space Convert Layer Color Space 100000 1 false merge-layer-below &Merge with Layer Below Merge with Layer Below Merge with Layer Below 100000 0 Ctrl+E false &Flatten Layer Flatten Layer Flatten Layer 100000 0 false Ras&terize Layer Rasterize Layer Rasterize Layer 10000000 1 false Flatten ima&ge Flatten image Flatten image 100000 0 Ctrl+Shift+E false La&yer Style... Layer Style Layer Style 100000 1 false Move into previous group Move into previous group Move into previous group 0 0 false Move into next group Move into next group Move into next group 0 0 false Rename current layer Rename current layer Rename current layer 100000 0 F2 false deletelayer &Remove Layer Remove Layer Remove Layer 1000 1 Shift+Delete false arrowupblr Move Layer or Mask Up Move Layer or Mask Up Ctrl+PgUp false arrowdown Move Layer or Mask Down Move Layer or Mask Down Ctrl+PgDown false properties &Properties... Properties Properties 1000 1 F3 false diff --git a/krita/kritamenu.action b/krita/kritamenu.action index 1807315d41..2652cb9784 100644 --- a/krita/kritamenu.action +++ b/krita/kritamenu.action @@ -1,1818 +1,1806 @@ File document-new &New Create new document New 0 0 Ctrl+N false document-open &Open... Open an existing document Open 0 0 Ctrl+O false document-open-recent Open &Recent Open a document which was recently opened Open Recent 1 0 false document-save &Save Save Save 1 0 Ctrl+S false document-save-as Save &As... Save document under a new name Save As 1 0 Ctrl+Shift+S false Sessions... Open session manager Sessions 0 0 false document-import Open ex&isting Document as Untitled Document... Open existing Document as Untitled Document Open existing Document as Untitled Document 0 0 false document-export E&xport... Export Export 1 0 false Import animation frames... Import animation frames Import animation frames 1 0 false &Render Animation... Render Animation to GIF, Image Sequence or Video Render Animation 1000 0 false &Render Image Sequence Again Render Animation to Image Sequence Again Render Animation 1000 0 false Save Incremental &Version Save Incremental Version Save Incremental Version 1 0 Ctrl+Alt+S false Save Incremental &Backup Save Incremental Backup Save Incremental Backup 1 0 F4 false &Create Template From Image... Create Template From Image Create Template From Image 1 0 false Create Copy &From Current Image Create Copy From Current Image Create Copy From Current Image 1 0 false document-print &Print... Print document Print 1 0 Ctrl+P false document-print-preview Print Previe&w Show a print preview of document Print Preview 1 0 false configure &Document Information Document Information Document Information 1 0 false &Close All Close All Close All 1 0 Ctrl+Shift+W false C&lose Close Close 1 0 Ctrl+W false &Quit Quit application Quit 0 0 Ctrl+Q false Edit edit-undo Undo Undo last action Undo 1 0 Ctrl+Z false edit-redo Redo Redo last undone action Redo 1 0 Ctrl+Shift+Z false edit-cut Cu&t Cut selection to clipboard Cut 0 0 Ctrl+X false edit-copy &Copy Copy selection to clipboard Copy 0 0 Ctrl+C false C&opy (sharp) Copy (sharp) Copy (sharp) 100000000 0 false Cut (&sharp) Cut (sharp) Cut (sharp) 100000000 0 false Copy &merged Copy merged Copy merged 100000000 0 Ctrl+Shift+C false edit-paste &Paste Paste clipboard content Paste 0 0 Ctrl+V false Paste at Cursor Paste at cursor Paste at cursor 0 0 Ctrl+Alt+V false Paste into &New Image Paste into New Image Paste into New Image 0 0 Ctrl+Shift+N false edit-clear C&lear Clear Clear 1 0 Del false &Fill with Foreground Color Fill with Foreground Color Fill with Foreground Color 10000 1 Shift+Backspace false Fill &with Background Color Fill with Background Color Fill with Background Color 10000 1 Backspace false F&ill with Pattern Fill with Pattern Fill with Pattern 10000 1 false Fill Special Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) 10000 1 Ctrl+Shift+Backspace false Fill with Background Color (Opacity) Fill with Background Color (Opacity) Fill with Background Color (Opacity) 10000 1 Ctrl+Backspace false Fill with Pattern (Opacity) Fill with Pattern (Opacity) Fill with Pattern (Opacity) 10000 1 false Stro&ke selected shapes Stroke selected shapes Stroke selected shapes 1000000000 0 false Stroke Selec&tion... Stroke selection Stroke selection 10000000000 0 false Delete keyframe Delete keyframe Delete keyframe 100000 0 false Window window-new &New Window New Window New Window 0 0 false N&ext Next Next 10 0 false Previous Previous Previous false View &Show Canvas Only Show just the canvas or the whole window Show Canvas Only 0 0 Tab true view-fullscreen F&ull Screen Mode Display the window in full screen Full Screen Mode 0 0 Ctrl+Shift+F true &Wrap Around Mode Wrap Around Mode Wrap Around Mode 1 0 true &Instant Preview Mode Instant Preview Mode Instant Preview Mode 1 0 Shift+L true Soft Proofing Turns on Soft Proofing Turns on Soft Proofing Ctrl+Y true Out of Gamut Warnings Turns on warnings for colors out of proofed gamut, needs soft proofing to be turned on. Turns on warnings for colors out of proofed gamut, needs soft proofing to be turned on. Ctrl+Shift+Y true mirror-view Mirror View Mirror View Mirror View M false zoom-original &Reset zoom Reset zoom Reset zoom 1 0 Ctrl+0 false zoom-in Zoom &In Zoom In 0 0 Ctrl++ false zoom-out Zoom &Out Zoom Out 0 0 Ctrl+- false rotate-canvas-right Rotate &Canvas Right Rotate Canvas Right Rotate Canvas Right 1 0 Ctrl+] false rotate-canvas-left Rotate Canvas &Left Rotate Canvas Left Rotate Canvas Left 1 0 Ctrl+[ false rotation-reset Reset Canvas Rotation Reset Canvas Rotation Reset Canvas Rotation 1 0 false Show &Rulers The rulers show the horizontal and vertical positions of the mouse on the image and can be used to position your mouse at the right place on the canvas. <p>Uncheck this to hide the rulers.</p> Show Rulers Show Rulers 1 0 true Rulers Track Pointer The rulers will track current mouse position and show it on screen. It can cause suptle performance slowdown Rulers Track Pointer Rulers Track Pointer 1 0 true Show Guides Show or hide guides Show Guides 1 0 true Lock Guides Lock or unlock guides Lock Guides 1 0 true Snap to Guides Snap cursor to guides position Snap to Guides 1 0 true Show Status &Bar Show or hide the status bar Show Status Bar 0 0 true Show Pixel Grid Show Pixel Grid Show Pixel Grid 1000 1000 true view-grid Show &Grid Show Grid Show Grid 1000 0 Ctrl+Shift+' true Snap To Grid Snap To Grid Snap To Grid 1000 Ctrl+Shift+; true Show Snap Options Popup Show Snap Options Popup Show Snap Options Popup 1000 Shift+s false Snap Orthogonal Snap Orthogonal Snap Orthogonal 1000 true Snap Node Snap Node Snap Node 1000 true Snap Extension Snap Extension Snap Extension 1000 true Snap Intersection Snap Intersection Snap Intersection 1000 true Snap Bounding Box Snap Bounding Box Snap Bounding Box 1000 true Snap Image Bounds Snap Image Bounds Snap Image Bounds 1000 true Snap Image Center Snap Image Center Snap Image Center 1000 true S&how Painting Assistants Show Painting Assistants Show Painting Assistants 1000 0 true Show &Assistant Previews Show Assistant Previews Show Assistant Previews 1000 0 true S&how Reference Images Show Reference Images Show Reference Images 1000 0 true Image document-properties &Properties... Properties Properties 1000 0 false format-stroke-color &Image Background Color and Transparency... Change the background color of the image Image Background Color and Transparency 1000 0 false &Convert Image Color Space... Convert Image Color Space Convert Image Color Space 1000 0 false trim-to-image &Trim to Image Size Trim to Image Size Trim to Image Size 1 0 false Trim to Current &Layer Trim to Current Layer Trim to Current Layer 100000 0 false Trim to S&election Trim to Selection Trim to Selection 100000000 0 false &Rotate Image... Rotate Image Rotate Image 1000 0 false object-rotate-right Rotate &Image 90° to the Right Rotate Image 90° to the Right Rotate Image 90° to the Right 1000 0 false object-rotate-left Rotate Image &90° to the Left Rotate Image 90° to the Left Rotate Image 90° to the Left 1000 0 false Rotate Image &180° Rotate Image 180° Rotate Image 180° 1000 0 false &Shear Image... Shear Image Shear Image 1000 0 false symmetry-horizontal &Mirror Image Horizontally Mirror Image Horizontally Mirror Image Horizontally 1000 0 false symmetry-vertical Mirror Image &Vertically Mirror Image Vertically Mirror Image Vertically 1000 0 false Scale Image To &New Size... Scale Image To New Size Scale Image To New Size 1000 0 Ctrl+Alt+I false &Offset Image... Offset Image Offset Image 1000 0 false R&esize Canvas... Resize Canvas Resize Canvas 1000 0 Ctrl+Alt+C false Im&age Split Image Split Image Split 1000 0 false Separate Ima&ge... Separate Image Separate Image 1000 0 false Select edit-select-all Select &All Select All Select All 0 0 Ctrl+A false edit-select-all &Deselect Deselect Deselect 1100000000 0 Ctrl+Shift+A false &Reselect Reselect Reselect 0 0 Ctrl+Shift+D false - - - &Invert Selection - - Invert Selection - Invert Selection - 10000 - 0 - Ctrl+I - false - - &Convert to Vector Selection Convert to Vector Selection Convert to Vector Selection 100000000000000000 0 false &Convert to Raster Selection Convert to Raster Selection Convert to Raster Selection 10000000000000000 0 false Edit Selection Edit Selection Edit Selection 10000000000 100 false Convert Shapes to &Vector Selection Convert Shapes to Vector Selection Convert Shapes to Vector Selection 1000000000 0 false &Feather Selection... Feather Selection Feather Selection 10000000000 100 Shift+F6 false Dis&play Selection Display Selection Display Selection 1000 0 Ctrl+H true Sca&le... Scale Scale 100000000 100 false S&elect from Color Range... Select from Color Range Select from Color Range 10000 100 false Select &Opaque (Replace) Select Opaque Select Opaque 10000 100 false Select Opaque (&Add) Select Opaque (Add) Select Opaque (Add) 10000 100 false Select Opaque (&Subtract) Select Opaque (Subtract) Select Opaque (Subtract) 10000 100 false Select Opaque (&Intersect) Select Opaque (Intersect) Select Opaque (Intersect) 10000 100 false &Grow Selection... Grow Selection Grow Selection 10000000000 100 false S&hrink Selection... Shrink Selection Shrink Selection 10000000000 100 false &Border Selection... Border Selection Border Selection 10000000000 100 false S&mooth Smooth Smooth 10000000000 100 false Filter &Apply Filter Again Apply Filter Again Apply Filter Again 0 0 Ctrl+F false Adjust Adjust Adjust false Artistic Artistic Artistic false Blur Blur Blur false Colors Colors Colors false Edge Detection Edge Detection Edge Detection false Enhance Enhance Enhance false Emboss Emboss Emboss false Map Map Map false Other Other Other false gmic Start G'MIC-Qt Start G'Mic-Qt Start G'Mic-Qt false gmic Re-apply the last G'MIC filter Apply the last G'Mic-Qt action again Apply the last G'Mic-Qt action again false Settings configure &Configure Krita... Configure Krita Configure Krita 0 0 false &Manage Resources... Manage Resources Manage Resources 0 0 false preferences-desktop-locale Switch Application &Language... Switch Application Language Switch Application Language false &Show Dockers Show Dockers Show Dockers 0 0 true configure Configure Tool&bars... Configure Toolbars Configure Toolbars 0 0 false Dockers Dockers Dockers false &Themes Themes Themes false im-user Active Author Profile Active Author Profile Active Author Profile configure-shortcuts Configure S&hortcuts... Configure Shortcuts Configure Shortcuts 0 0 false &Window Window Window false Help help-contents Krita &Handbook Krita Handbook Krita Handbook F1 false tools-report-bug &Report Bug... Report Bug Report Bug false calligrakrita &About Krita About Krita About Krita false kde About &KDE About KDE About KDE false Brushes and Stuff &Gradients Gradients Gradients false &Patterns Patterns Patterns false &Color Color Color false &Painter's Tools Painter's Tools Painter's Tools false Brush composite Brush composite Brush composite false Brush option slider 1 Brush option slider 1 Brush option slider 1 false Brush option slider 2 Brush option slider 2 Brush option slider 2 false Brush option slider 3 Brush option slider 3 Brush option slider 3 false Mirror Mirror Mirror false Layouts Select layout false Workspaces Workspaces Workspaces false diff --git a/krita/main.cc b/krita/main.cc index 1cd6a3a611..c7c03a657f 100644 --- a/krita/main.cc +++ b/krita/main.cc @@ -1,464 +1,467 @@ /* * 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 #if QT_VERSION >= 0x050900 #include #endif #include #include #include #include #include #include "data/splash/splash_screen.xpm" #include "data/splash/splash_holidays.xpm" #include "data/splash/splash_screen_x2.xpm" #include "data/splash/splash_holidays_x2.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 #elif defined HAVE_X11 #include "config_use_qt_xcb.h" #ifndef USE_QT_XCB #include #endif #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()); } typedef enum ORIENTATION_PREFERENCE { ORIENTATION_PREFERENCE_NONE = 0x0, ORIENTATION_PREFERENCE_LANDSCAPE = 0x1, ORIENTATION_PREFERENCE_PORTRAIT = 0x2, ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED = 0x4, ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED = 0x8 } ORIENTATION_PREFERENCE; typedef BOOL WINAPI (*pSetDisplayAutoRotationPreferences_t)( ORIENTATION_PREFERENCE orientation ); void resetRotation() { QLibrary user32Lib("user32"); if (!user32Lib.load()) { qWarning() << "Failed to load user32.dll! This really should not happen."; return; } pSetDisplayAutoRotationPreferences_t pSetDisplayAutoRotationPreferences = reinterpret_cast(user32Lib.resolve("SetDisplayAutoRotationPreferences")); if (!pSetDisplayAutoRotationPreferences) { dbgKrita << "Failed to load function SetDisplayAutoRotationPreferences"; return; } bool result = pSetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE); dbgKrita << "SetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE) returned" << result; } } // 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 + // Workaround a bug in QNetworkManager + qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1)); + // A per-user unique string, without /, because QLocalServer cannot use names with a / in it QString key = "Krita4" + 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); #if QT_VERSION >= 0x050900 QCoreApplication::setAttribute(Qt::AA_DisableShaderDiskCache, true); #endif 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()) { + if (kritarc.value("EnableHiDPI", true).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 char *fakeArgv[2] = { argv[0], nullptr }; // Prevents QCoreApplication from modifying the real argc/argv KisOpenGL::probeWindowsQpaOpenGL(1, fakeArgv, preferredOpenGLRenderer); // HACK: https://bugs.kde.org/show_bug.cgi?id=390651 resetRotation(); #endif } QString root; QString language; { // Create a temporary application to get the root QCoreApplication app(argc, argv); Q_UNUSED(app); root = KoResourcePaths::getApplicationRoot(); QSettings languageoverride(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat); languageoverride.beginGroup(QStringLiteral("Language")); language = languageoverride.value(qAppName(), "").toString(); } #ifdef Q_OS_LINUX { QByteArray originalXdgDataDirs = qgetenv("XDG_DATA_DIRS"); if (originalXdgDataDirs.isEmpty()) { // We don't want to completely override the default originalXdgDataDirs = "/usr/local/share/:/usr/share/"; } qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share") + ":" + originalXdgDataDirs); } #else qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share")); #endif dbgKrita << "Setting XDG_DATA_DIRS" << qgetenv("XDG_DATA_DIRS"); // Now that the paths are set, set the language. First check the override from the language // selection dialog. dbgKrita << "Override language:" << language; bool rightToLeft = false; if (!language.isEmpty()) { KLocalizedString::setLanguages(language.split(":")); // And override Qt's locale, too qputenv("LANG", language.split(":").first().toLocal8Bit()); QLocale locale(language.split(":").first()); QLocale::setDefault(locale); const QStringList rtlLanguages = QStringList() << "ar" << "dv" << "he" << "ha" << "ku" << "fa" << "ps" << "ur" << "yi"; if (rtlLanguages.contains(language.split(':').first())) { rightToLeft = true; } } else { dbgKrita << "Qt UI languages:" << QLocale::system().uiLanguages() << qgetenv("LANG"); // And if there isn't one, check the one set by the system. QLocale locale = QLocale::system(); if (locale.name() != QStringLiteral("en")) { QStringList uiLanguages = locale.uiLanguages(); for (QString &uiLanguage : uiLanguages) { // This list of language codes that can have a specifier should // be extended whenever we have translations that need it; right // now, only en, pt, zh are in this situation. if (uiLanguage.startsWith("en") || uiLanguage.startsWith("pt")) { uiLanguage.replace(QChar('-'), QChar('_')); } else if (uiLanguage.startsWith("zh-Hant") || uiLanguage.startsWith("zh-TW")) { uiLanguage = "zh_TW"; } else if (uiLanguage.startsWith("zh-Hans") || uiLanguage.startsWith("zh-CN")) { uiLanguage = "zh_CN"; } } for (int i = 0; i < uiLanguages.size(); i++) { QString uiLanguage = uiLanguages[i]; // Strip the country code int idx = uiLanguage.indexOf(QChar('-')); if (idx != -1) { uiLanguage = uiLanguage.left(idx); uiLanguages.replace(i, uiLanguage); } } dbgKrita << "Converted ui languages:" << uiLanguages; qputenv("LANG", uiLanguages.first().toLocal8Bit()); #ifdef Q_OS_MAC // See https://bugs.kde.org/show_bug.cgi?id=396370 KLocalizedString::setLanguages(QStringList() << uiLanguages.first()); #else KLocalizedString::setLanguages(QStringList() << uiLanguages); #endif } } // first create the application so we can create a pixmap KisApplication app(key, argc, argv); if (!language.isEmpty()) { if (rightToLeft) { app.setLayoutDirection(Qt::RightToLeft); } else { app.setLayoutDirection(Qt::LeftToRight); } } KLocalizedString::setApplicationDomain("krita"); dbgKrita << "Available translations" << KLocalizedString::availableApplicationTranslations(); dbgKrita << "Available domain translations" << KLocalizedString::availableDomainTranslations("krita"); #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)); dbgKrita << "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.exportAs(); 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 #ifndef USE_QT_XCB app.installNativeEventFilter(KisXi2EventFilter::instance()); #endif #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), QPixmap(splash_holidays_x2_xpm)); } else { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_screen_xpm), QPixmap(splash_screen_x2_xpm)); } app.setSplashScreen(splash); } #if defined Q_OS_WIN KisConfig cfg(false); bool supportedWindowsVersion = true; #if QT_VERSION >= 0x050900 QOperatingSystemVersion osVersion = QOperatingSystemVersion::current(); if (osVersion.type() == QOperatingSystemVersion::Windows) { if (osVersion.majorVersion() >= QOperatingSystemVersion::Windows7.majorVersion()) { supportedWindowsVersion = true; } else { supportedWindowsVersion = false; if (cfg.readEntry("WarnedAboutUnsupportedWindows", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You are running an unsupported version of Windows: %1.\n" "This is not recommended. Do not report any bugs.\n" "Please update to a supported version of Windows: Windows 7, 8, 8.1 or 10.", osVersion.name())); cfg.writeEntry("WarnedAboutUnsupportedWindows", true); } } } #endif { if (cfg.useWin8PointerInput() && !KisTabletSupportWin8::isAvailable()) { cfg.setUseWin8PointerInput(false); } if (!cfg.useWin8PointerInput()) { bool hasWinTab = KisTabletSupportWin::init(); if (!hasWinTab && supportedWindowsVersion) { if (KisTabletSupportWin8::isPenDeviceAvailable()) { // Use WinInk automatically cfg.setUseWin8PointerInput(true); } else if (!cfg.readEntry("WarnedAboutMissingWinTab", false)) { if (KisTabletSupportWin8::isAvailable()) { QMessageBox::information(nullptr, i18n("Krita Tablet Support"), i18n("Cannot load WinTab driver and no Windows Ink pen devices are found. If you have a drawing tablet, please make sure the tablet driver is properly installed."), QMessageBox::Ok, QMessageBox::Ok); } else { QMessageBox::information(nullptr, i18n("Krita Tablet Support"), i18n("Cannot load WinTab driver. If you have a drawing tablet, please make sure the tablet driver is properly installed."), QMessageBox::Ok, QMessageBox::Ok); } cfg.writeEntry("WarnedAboutMissingWinTab", true); } } } if (cfg.useWin8PointerInput()) { KisTabletSupportWin8 *penFilter = new KisTabletSupportWin8(); if (penFilter->init()) { // penFilter.registerPointerDeviceNotifications(); app.installNativeEventFilter(penFilter); dbgKrita << "Using Win8 Pointer Input for tablet support"; } else { dbgKrita << "No Win8 Pointer Input available"; delete penFilter; } } } #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/krita/org.kde.krita.appdata.xml b/krita/org.kde.krita.appdata.xml index 48c3eda6e3..d5ce8dd1ef 100644 --- a/krita/org.kde.krita.appdata.xml +++ b/krita/org.kde.krita.appdata.xml @@ -1,257 +1,258 @@ org.kde.krita org.kde.krita.desktop CC0-1.0 GPL-3.0-only Krita Foundation Fundació Krita Fundació Krita Krita Foundation Krita Foundation Krita Foundation Fundación Krita Krita Fundazioa La Fondation Krita Fundación Krita Fondazione Krita Krita Foundation Fundacja Krity Fundação do Krita Krita Foundation Krita-stiftelsen Фундація Krita xxKrita Foundationxx Krita 基金会 + Krita 基金會 foundation@krita.org Krita كريتا Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita xxKritaxx Krita Krita Digital Painting, Creative Freedom رسم رقميّ، حريّة إبداعيّة Digitalno crtanje, kreativna sloboda Dibuix digital, Llibertat creativa Dibuix digital, Llibertat creativa Digitální malování, svoboda tvorby Digital tegning, kunstnerisk frihed Digitales Malen, kreative Freiheit Ψηφιακή ζωγραφική, δημιουργική ελευθερία Digital Painting, Creative Freedom Pintura digital, libertad creativa Digitaalne joonistamine, loominguline vabadus Margolan digitala, sormen askatasuna Digitaalimaalaus, luova vapaus Peinture numérique, liberté créatrice Debuxo dixital, liberdade creativa Pictura digital, Libertate creative Pelukisan Digital, Kebebasan Berkreatif Pittura digitale, libertà creativa Digital Painting, Creative Freedom Cyfrowe malowanie, Wolność Twórcza Pintura Digital, Liberdade Criativa Pintura digital, liberdade criativa Цифровое рисование. Творческая свобода Digitálne maľovanie, kreatívna sloboda Digital målning, kreativ frihet Sayısal Boyama, Yaratıcı Özgürlük Цифрове малювання, творча свобода xxDigital Painting, Creative Freedomxx 自由挥洒数字绘画的无限创意 數位繪畫,創作自由

Krita is the full-featured digital art studio.

Krita je potpuni digitalni umjetnički studio.

Krita és l'estudi d'art digital ple de funcionalitats.

Krita és l'estudi d'art digital ple de funcionalitats.

Krita ist ein digitales Designstudio mit umfangreichen Funktionen.

Το Krita είναι ένα πλήρες χαρακτηριστικών ψηφιακό ατελιέ.

Krita is the full-featured digital art studio.

Krita es un estudio de arte digital completo

Krita on rohkete võimalustega digitaalkunstistuudio.

Krita arte lantegi digital osoa da.

Krita on täyspiirteinen digitaiteen ateljee.

Krita est le studio d'art numérique complet.

Krita é un estudio completo de arte dixital.

Krita es le studio de arte digital complete.

Krita adalah studio seni digital yang penuh dengan fitur.

Krita è uno studio d'arte digitale completo.

Krita は、フル機能を備えたデジタルなアートスタジオです。

Krita is de digitale kunststudio vol mogelijkheden.

Krita jest pełnowymiarowym, cyfrowym studiem artystycznym

O Krita é o estúdio de arte digital completo.

O Krita é o estúdio de arte digital completo.

Krita — полнофункциональный инструмент для создания цифровой графики.

Krita je plne vybavené digitálne umelecké štúdio.

Krita är den fullfjädrade digitala konststudion.

Krita, tam özellikli dijital sanat stüdyosudur.

Krita — повноцінний комплекс для створення цифрових художніх творів.

xxKrita is the full-featured digital art studio.xx

Krita 是一款功能齐全的数字绘画工作室软件。

Krita 是全功能的數位藝術工作室。

It is perfect for sketching and painting, and presents an end–to–end solution for creating digital painting files from scratch by masters.

On je savršen za skiciranje i slikanje i predstavlja finalno rješenje za kreiranje digitalnih slika od nule s majstorima

És perfecte per fer esbossos i pintar, i presenta una solució final per crear fitxers de dibuix digital des de zero per a mestres.

És perfecte per fer esbossos i pintar, i presenta una solució final per crear fitxers de dibuix digital des de zero per a mestres.

Είναι ιδανικό για σκιτσογραφία και ζωγραφική, και παρουσιάζει μια από άκρη σε άκρη λύση για τη δημιουργία από το μηδέν αρχείων ψηφιακης ζωγραφικής από τους δασκάλους της τέχνης.

It is perfect for sketching and painting, and presents an end–to–end solution for creating digital painting files from scratch by masters.

Es perfecto para diseñar y pintar, y ofrece una solución completa para crear desde cero archivos de pintura digital apta para profesionales.

See on suurepärane töövahend visandite ja joonistuste valmistamiseks ning annab andekatele kunstnikele võimaluse luua digitaalpilt algusest lõpuni just oma käe järgi.

Zirriborratzeko eta margotzeko ezin hobea da, eta margolan digitalen fitxategiak hutsetik sortzeko muturretik-muturrera konponbide bat aurkezten du, maisuentzako mailakoa.

Se on täydellinen luonnosteluun ja maalaukseen ja tarjoaa kokonaisratkaisun digitaalisten kuvatiedostojen luomiseen alusta alkaen.

Il est parfait pour crayonner et peindre, et constitue une solution de bout en bout pour créer des fichier de peinture numérique depuis la feuille blanche jusqu'au épreuves finales.

Resulta perfecto para debuxar e pintar, e presenta unha solución completa que permite aos mestres crear ficheiros de debuxo dixital desde cero.

Illo es perfecte pro schizzar e pinger, e presenta un solution ab fin al fin pro crear files de pictura digital ab grattamentos per maestros.

Ini adalah sempurna untuk mensketsa dan melukis, dan menghadirkan sebuah solusi untuk menciptakan file-file pelukisan digital dari goresan si pelukis ulung.

Perfetto per fare schizzi e dipingere, prevede una soluzione completa che consente agli artisti di creare file di dipinti digitali partendo da zero.

Het is perfect voor schetsen en schilderen en zet een end–to–end oplossing voor het maken van digitale bestanden voor schilderingen vanuit het niets door meesters.

Nadaje się perfekcyjnie do szkicowania i malowania i dostarcza zupełnego rozwiązania dla tworzenia plików malowideł cyfrowych od zalążka.

É perfeito para desenhos e pinturas, oferecendo uma solução final para criar ficheiros de pintura digital do zero por mestres.

É perfeito para desenhos e pinturas, oferecendo uma solução final para criar arquivos de desenho digital feitos a partir do zero por mestres.

Она превосходно подходит для набросков и рисования, предоставляя мастерам самодостаточный инструмент для создания цифровой живописи с нуля.

Je ideálna na skicovanie a maľovanie a poskytuje end-to-end riešenie na vytváranie súborov digitálneho maľovania od základu od profesionálov.

Den är perfekt för att skissa och måla, samt erbjuder en helomfattande lösning för att skapa digitala målningsfiler från grunden av mästare.

Eskiz ve boyama için mükemmeldir ve ustaların sıfırdan dijital boyama dosyaları oluşturmak için uçtan-uca bir çözüm sunar.

Цей комплекс чудово пасує для створення ескізів та художніх зображень і є самодостатнім набором для створення файлів цифрових полотен «з нуля» для справжніх художників.

xxIt is perfect for sketching and painting, and presents an end–to–end solution for creating digital painting files from scratch by masters.xx

它专门为数字绘画设计,为美术工作者提供了一个从起草、上色到完成作品等整个创作流程的完整解决方案。

它是素描和繪畫的完美選擇,並提供了一個從零開始建立數位繪畫檔的端到端解決方案。

Krita is a great choice for creating concept art, comics, textures for rendering and matte paintings. Krita supports many colorspaces like RGB and CMYK at 8 and 16 bits integer channels, as well as 16 and 32 bits floating point channels.

Krita je odličan izbor za kreiranje konceptualne umjetnosti, stripove, teksture za obradu i mat slike. Krita podržava mnoge prostore boja kao RGB i CMIK na 8 i 16 bitnim cjelobrojnim kanalimaa, kao i 16 i 32 bita floating point kanalima.

El Krita és una gran elecció per crear art conceptual, còmics, textures per renderitzar i pintures «matte». El Krita permet molts espais de color com el RGB i el CMYK a 8 i 16 bits de canals sencers, així com 16 i 32 bits de canals de coma flotant.

El Krita és una gran elecció per crear art conceptual, còmics, textures per renderitzar i pintures «matte». El Krita permet molts espais de color com el RGB i el CMYK a 8 i 16 bits de canals sencers, així com 16 i 32 bits de canals de coma flotant.

Το Krita είναι μια εξαιρετική επιλογή για τη δημιουργία αφηρημένης τέχνης, ιστοριών με εικόνες, υφής για ζωγραφική αποτύπωσης και διάχυσης φωτός. Το Krita υποστηρίζει πολλούς χρωματικούς χώρους όπως τα RGB και CMYK σε 8 και 16 bit κανάλια ακεραίων καθώς επίσης και σε 16 και 32 bit κανάλια κινητής υποδιαστολής,

Krita is a great choice for creating concept art, comics, textures for rendering and matte paintings. Krita supports many colourspaces like RGB and CMYK at 8 and 16 bits integer channels, as well as 16 and 32 bits floating point channels.

Krita es una gran elección para crear arte conceptual, cómics, texturas para renderizar y «matte paintings». Krita permite el uso de muchos espacios de color, como, por ejemplo, RGB y CMYK, tanto en canales de enteros de 8 y 16 bits, así como en canales de coma flotante de 16 y 32 bits.

Krita on üks paremaid valikuid kontseptuaalkunsti, koomiksite, tekstuuride ja digitaalmaalide loomiseks. Krita toetab paljusid värviruume, näiteks RGB ja CMYK 8 ja 16 täisarvulise bitiga kanali kohta, samuti 16 ja 32 ujukomabitiga kanali kohta.

Krita aukera bikaina da kontzeptuzko artea, komikiak, errendatzeko ehundurak eta «matte» margolanak sortzeko. Kritak kolore-espazio ugari onartzen ditu hala nola GBU eta CMYK, 8 eta 16 biteko osoko kanaletan, baita 16 eta 32 biteko koma-higikorreko kanaletan.

Krita on hyvä valinta konseptikuvituksen, sarjakuvien, pintakuvioiden ja maalausten luomiseen. Krita tukee useita väriavaruuksia kuten RGB:tä ja CMYK:ta 8 ja 16 bitin kokonaisluku- samoin kuin 16 ja 32 bitin liukulukukanavin.

Krita est un très bon choix pour créer des concepts arts, des bandes-dessinées, des textures de rendu et des peintures. Krita prend en charge plusieurs espaces de couleurs comme RVB et CMJN avec les canaux de 8 et 16 bits entiers ainsi que les canaux de 16 et 32 bits flottants.

Krita é unha gran opción para crear arte conceptual, texturas para renderización e pinturas mate. Krita permite usar moitos espazos de cores como RGB e CMYK con canles de 8 e 16 bits, así como canles de coma flotante de 16 e 32 bits.

Krita es un grande selection pro crear arte de concepto, comics, texturas pro rendering e picturas opac. Krita supporta multe spatios de colores como RGB e CMYK con canales de integer a 8 e 16 bits, como anque canales floating point a 16 e 32 bits.

Krita adalah pilihan yang cocok untuk menciptakan konsep seni, komik, tekstur untuk rendering dan lukisan matte. Krita mendukung banyak ruang warna seperti RGB dan CMYK pada channel integer 8 dan 16 bit, serta 16 dan 32 bit channel titik mengambang.

Krita rappresenta una scelta ottimale per la creazione di arte concettuale, fumetti e texture per il rendering e il matte painting. Krita supporta molti spazi colori come RGB e CMYK a 8 e 16 bit per canali interi e 16 e 32 bit per canali a virgola mobile.

コンセプトアート、コミック、3DCG 用テクスチャ、マットペイントを制作する方にとって、Krita は最適な選択です。Krita は、8/16 ビット整数/チャンネル、および 16/32 ビット浮動小数点/チャンネルの RGB や CMYK をはじめ、さまざまな色空間をサポートしています。

Krita is een goede keuze voor het maken van kunstconcepten, strips, textuur voor weergeven en matte schilderijen. Krita ondersteunt vele kleurruimten zoals RGB en CMYK in 8 en 16 bits kanalen met gehele getallen, evenals 16 en 32 bits kanalen met drijvende komma.

Krita jest świetnym wyborem przy tworzeniu koncepcyjnej sztuki, komiksów, tekstur do wyświetlania i kaszet. Krita obsługuje wiele przestrzeni barw takich jak RGB oraz CMYK dla kanałów 8 oraz 16 bitowych wyrażonych w l. całkowitych, a także 16 oraz 32 bitowych wyrażonych w l. zmiennoprzecinkowych.

O Krita é uma óptima escolha para criar arte conceptual, banda desenhada, texturas para desenho e pinturas. O Krita suporta diversos espaços de cores como o RGB e o CMYK com canais de cores inteiros a 8 e 16 bits, assim como canais de vírgula flutuante a 16 e a 32 bits.

O Krita é uma ótima escolha para criação de arte conceitual, histórias em quadrinhos, texturas para desenhos e pinturas. O Krita tem suporte a diversos espaços de cores como RGB e CMYK com canais de cores inteiros de 8 e 16 bits, assim como canais de ponto flutuante de 16 e 32 bits.

Krita — отличный выбор для создания концепт-артов, комиксов, текстур для рендеринга и рисования. Она поддерживает множество цветовых пространств включая RGB и CMYK с 8 и 16 целыми битами на канал, а также 16 и 32 битами с плавающей запятой на канал.

Krita je výborná voľba pre vytváranie konceptového umenia, textúr na renderovanie a matné kresby. Krita podporuje mnoho farebných priestorov ako RGB a CMYK na 8 a 16 bitových celočíselných kanáloch ako aj 16 a 32 bitových reálnych kanáloch.

Krita är ett utmärkt val för att skapa concept art, serier, strukturer för återgivning och bakgrundsmålningar. Krita stöder många färgrymder som RGB och CMYK med 8- och 16-bitars heltal, samt 16- och 32-bitars flyttal.

Krita, konsept sanat, çizgi roman, kaplama ve mat resimler için dokular oluşturmak için mükemmel bir seçimdir. Krita, 8 ve 16 bit tamsayı kanallarında RGB ve CMYK gibi birçok renk alanını ve 16 ve 32 bit kayan nokta kanallarını desteklemektedir.

Krita — чудовий інструмент для створення концептуального живопису, коміксів, текстур для моделей та декорацій. У Krita передбачено підтримку багатьох просторів кольорів, зокрема RGB та CMYK з 8-бітовими та 16-бітовими цілими значеннями, а також 16-бітовими та 32-бітовими значеннями з рухомою крапкою для каналів кольорів.

xxKrita is a great choice for creating concept art, comics, textures for rendering and matte paintings. Krita supports many colorspaces like RGB and CMYK at 8 and 16 bits integer channels, as well as 16 and 32 bits floating point channels.xx

Krita 是绘制概念美术、漫画、纹理和电影布景的理想选择。Krita 支持多种色彩空间,如 8 位和 16 位整数及 16 位和 32 位浮点的 RGB 和 CMYK 颜色模型。

Krita 是創造概念藝術、漫畫、彩現紋理和場景繪畫的絕佳選擇。Krita 在 8 位元和 16 位元整數色版,以及 16 位元和 32 位元浮點色版中支援 RGB 和 CMYK 等多種色彩空間。

Have fun painting with the advanced brush engines, amazing filters and many handy features that make Krita enormously productive.

Zabavite se kreirajući napredne pogone četki, filtere i mnoge praktične osobine koje čine Krita vrlo produktivnim.

Gaudiu pintant amb els motors de pinzells avançats, els filtres impressionants i moltes funcionalitats útils que fan el Krita molt productiu.

Gaudiu pintant amb els motors de pinzells avançats, els filtres impressionants i moltes funcionalitats útils que fan el Krita molt productiu.

Διασκεδάστε ζωγραφίζοντας με τις προηγμένες μηχανές πινέλων, με εκπληκτικά φίλτρα και πολλά εύκολης χρήσης χαρακτηριστικά που παρέχουν στο Krita εξαιρετικά αυξημένη παραγωγικότητα.

Have fun painting with the advanced brush engines, amazing filters and many handy features that make Krita enormously productive.

Diviértase pintando con los avanzados motores de pinceles, los espectaculares filtros y muchas funcionalidades prácticas que hacen que Krita sea enormemente productivo.

Joonistamise muudavad tunduvalt lõbusamaks võimsad pintslimootorid, imetabased filtrid ja veel paljud käepärased võimalused, mis muudavad Krita kasutaja tohutult tootlikuks.

Marrazten ondo pasa ezazu, isipu motor aurreratuekin, iragazki txundigarriekin eta eginbide praktiko ugariekin, zeintzuek Krita ikaragarri emankorra egiten duten.

Pidä hauskaa maalatessasi edistyneillä sivellinmoottoreilla, hämmästyttävillä suotimilla ja monilla muilla kätevillä ominaisuuksilla, jotka tekevät Kritasta tavattoman tehokkaan.

Amusez-vous à peindre avec les outils de brosse avancés, les filtres incroyables et les nombreuses fonctionnalités pratiques qui rendent Krita extrêmement productif.

Goza debuxando con motores de pincel avanzados, filtros fantásticos e moitas outras funcionalidades útiles que fan de Krita un programa extremadamente produtivo.

Amusa te a pinger con le motores de pincel avantiate, filtros stupende e multe characteristicas amical que face Krita enormemente productive.

Bersenang-senanglah melukis dengan mesin kuas canggih, filter luar biasa dan banyak fitur berguna yang membuat Krita sangat produktif.

Divertiti a dipingere con gli avanzati sistemi di pennelli, i sorprendenti filtri e molte altre utili caratteristiche che fanno di Krita un software enormemente produttivo.

Krita のソフトウェアとしての生産性を高めている先進的なブラシエンジンや素晴らしいフィルタのほか、便利な機能の数々をお楽しみください。

Veel plezier met schilderen met the geavanceerde penseel-engines, filters vol verbazing en vele handige mogelijkheden die maken dat Krita enorm productief is.

Baw się przy malowaniu przy użyciu zaawansowanych silników pędzli, zadziwiających filtrów i wielu innych przydatnych cech, które czynią z Krity bardzo produktywną.

Divirta-se a pintar com os motores de pincéis avançados, os filtros espantosos e muitas outras funcionalidades úteis que tornam o Krita altamente produtivo.

Divirta-se pintando com os mecanismos de pincéis avançados, filtros maravilhosos e muitas outras funcionalidades úteis que tornam o Krita altamente produtivo.

Получайте удовольствие от использования особых кистевых движков, впечатляющих фильтров и множества других функций, делающих Krita сверхпродуктивной.

Užívajte si maľovanie s pokročilými kresliacimi enginmi, úžasnými filtrami a mnohými užitočnými funkciami, ktoré robia Kritu veľmi produktívnu.

Ha det så kul vid målning med de avancerade penselfunktionerna, fantastiska filtren och många praktiska funktioner som gör Krita så enormt produktiv.

Gelişmiş fırça motorları, şaşırtıcı filtreler ve Krita'yı son derece üretken yapan bir çok kullanışlı özellikli boya ile iyi eğlenceler.

Отримуйте задоволення від малювання за допомогою пензлів з найширшими можливостями, чудових фільтрів та багатьох зручних можливостей, які роблять Krita надзвичайно продуктивним засобом малювання.

xxHave fun painting with the advanced brush engines, amazing filters and many handy features that make Krita enormously productive.xx

Krita 具有功能强大的笔刷引擎、种类繁多的滤镜以及便于操作的交互设计,可让你尽情、高效地挥洒无限创意。

使用先進的筆刷引擎、驚人的濾鏡和許多方便的功能來開心地繪畫,讓 Krita 擁有巨大的生產力。

https://www.krita.org/ https://krita.org/about/faq/ https://krita.org/support-us/donations/ https://docs.krita.org/Category:Tutorials https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_001.png https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_002.png https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_003.png https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_004.png https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_005.png https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_006.png none none none none none none none none none none none none none none none none none none none none Graphics KDE krita
diff --git a/krita/pics/misc-dark/dark_ox16-action-object-align-horizontal-left-calligra.svg b/krita/pics/misc-dark/dark_ox16-action-object-align-horizontal-left-calligra.svg index 4cb22b1539..bf8efc1e13 100644 --- a/krita/pics/misc-dark/dark_ox16-action-object-align-horizontal-left-calligra.svg +++ b/krita/pics/misc-dark/dark_ox16-action-object-align-horizontal-left-calligra.svg @@ -1,187 +1,192 @@ image/svg+xml Timothée Giet 2015 Based on Breeze icons - - - + + + + + diff --git a/krita/pics/misc-dark/dark_ox16-action-object-align-horizontal-right-calligra.svg b/krita/pics/misc-dark/dark_ox16-action-object-align-horizontal-right-calligra.svg index bf8efc1e13..4cb22b1539 100644 --- a/krita/pics/misc-dark/dark_ox16-action-object-align-horizontal-right-calligra.svg +++ b/krita/pics/misc-dark/dark_ox16-action-object-align-horizontal-right-calligra.svg @@ -1,192 +1,187 @@ image/svg+xml Timothée Giet 2015 Based on Breeze icons - - - - - + + + diff --git a/krita/pics/misc-dark/misc-dark-icons.qrc b/krita/pics/misc-dark/misc-dark-icons.qrc index 0f999d6a3f..f72b960e74 100644 --- a/krita/pics/misc-dark/misc-dark-icons.qrc +++ b/krita/pics/misc-dark/misc-dark-icons.qrc @@ -1,67 +1,67 @@ dark_draw-eraser.svg dark_geometry.svg dark_ox16-action-object-align-horizontal-center-calligra.svg - dark_ox16-action-object-align-horizontal-left-calligra.svg - dark_ox16-action-object-align-horizontal-right-calligra.svg dark_ox16-action-object-align-vertical-bottom-calligra.svg dark_ox16-action-object-align-vertical-center-calligra.svg dark_ox16-action-object-align-vertical-top-calligra.svg dark_ox16-action-object-order-back-calligra.svg dark_ox16-action-object-order-front-calligra.svg dark_ox16-action-object-order-lower-calligra.svg dark_ox16-action-object-order-raise-calligra.svg dark_ox16-action-object-group-calligra.svg dark_ox16-action-object-ungroup-calligra.svg dark_distribute-horizontal-center.svg dark_distribute-horizontal-left.svg dark_distribute-horizontal-right.svg dark_distribute-horizontal.svg dark_distribute-vertical-bottom.svg dark_distribute-vertical-center.svg dark_distribute-vertical-top.svg dark_distribute-vertical.svg dark_paintop_settings_01.svg dark_paintop_settings_02.svg dark_pivot-point.svg dark_stroke-cap-butt.svg dark_stroke-cap-round.svg dark_stroke-cap-square.svg dark_stroke-join-bevel.svg dark_stroke-join-miter.svg dark_stroke-join-round.svg dark_symmetry-horizontal.svg dark_symmetry-vertical.svg dark_onionOff.svg dark_onionOn.svg dark_onion_skin_options.svg dark_path-break-point.svg dark_path-break-segment.svg dark_pathpoint-corner.svg dark_pathpoint-curve.svg dark_pathpoint-insert.svg dark_pathpoint-join.svg dark_pathpoint-line.svg dark_pathpoint-merge.svg dark_pathpoint-remove.svg dark_pathpoint-smooth.svg dark_pathpoint-symmetric.svg dark_pathsegment-curve.svg dark_pathsegment-line.svg dark_opacity-increase.svg dark_opacity-decrease.svg dark_lightness-increase.svg dark_lightness-decrease.svg dark_brushsize-increase.svg dark_brushsize-decrease.svg dark_curve-preset-u.svg dark_curve-preset-s.svg dark_curve-preset-s-reverse.svg dark_curve-preset-linear.svg dark_curve-preset-linear-reverse.svg dark_curve-preset-l.svg dark_curve-preset-j.svg dark_curve-preset-arch.svg + dark_ox16-action-object-align-horizontal-right-calligra.svg + dark_ox16-action-object-align-horizontal-left-calligra.svg diff --git a/krita/pics/misc-light/light_ox16-action-object-align-horizontal-left-calligra.svg b/krita/pics/misc-light/light_ox16-action-object-align-horizontal-left-calligra.svg index 0043ded962..6a2cb4faa5 100644 --- a/krita/pics/misc-light/light_ox16-action-object-align-horizontal-left-calligra.svg +++ b/krita/pics/misc-light/light_ox16-action-object-align-horizontal-left-calligra.svg @@ -1,187 +1,191 @@ image/svg+xml Timothée Giet 2015 Based on Breeze icons - - - + + + + + diff --git a/krita/pics/misc-light/light_ox16-action-object-align-horizontal-right-calligra.svg b/krita/pics/misc-light/light_ox16-action-object-align-horizontal-right-calligra.svg index 6a2cb4faa5..0043ded962 100644 --- a/krita/pics/misc-light/light_ox16-action-object-align-horizontal-right-calligra.svg +++ b/krita/pics/misc-light/light_ox16-action-object-align-horizontal-right-calligra.svg @@ -1,191 +1,187 @@ image/svg+xml Timothée Giet 2015 Based on Breeze icons - - - - - + + + diff --git a/krita/pics/misc-light/misc-light-icons.qrc b/krita/pics/misc-light/misc-light-icons.qrc index d3d1264663..dc7b2ed33a 100644 --- a/krita/pics/misc-light/misc-light-icons.qrc +++ b/krita/pics/misc-light/misc-light-icons.qrc @@ -1,67 +1,67 @@ light_draw-eraser.svg light_geometry.svg light_ox16-action-object-align-horizontal-center-calligra.svg - light_ox16-action-object-align-horizontal-left-calligra.svg - light_ox16-action-object-align-horizontal-right-calligra.svg light_ox16-action-object-align-vertical-bottom-calligra.svg light_ox16-action-object-align-vertical-center-calligra.svg light_ox16-action-object-align-vertical-top-calligra.svg light_ox16-action-object-order-back-calligra.svg light_ox16-action-object-order-front-calligra.svg light_ox16-action-object-order-lower-calligra.svg light_ox16-action-object-order-raise-calligra.svg light_ox16-action-object-group-calligra.svg light_ox16-action-object-ungroup-calligra.svg light_distribute-horizontal-center.svg light_distribute-horizontal-left.svg light_distribute-horizontal-right.svg light_distribute-horizontal.svg light_distribute-vertical-bottom.svg light_distribute-vertical-center.svg light_distribute-vertical-top.svg light_distribute-vertical.svg light_paintop_settings_01.svg light_paintop_settings_02.svg light_pivot-point.svg light_stroke-cap-butt.svg light_stroke-cap-round.svg light_stroke-cap-square.svg light_stroke-join-bevel.svg light_stroke-join-miter.svg light_stroke-join-round.svg light_symmetry-horizontal.svg light_symmetry-vertical.svg light_onionOff.svg light_onionOn.svg light_onion_skin_options.svg light_path-break-point.svg light_path-break-segment.svg light_pathpoint-corner.svg light_pathpoint-curve.svg light_pathpoint-insert.svg light_pathpoint-join.svg light_pathpoint-line.svg light_pathpoint-merge.svg light_pathpoint-remove.svg light_pathpoint-smooth.svg light_pathpoint-symmetric.svg light_pathsegment-curve.svg light_pathsegment-line.svg light_opacity-increase.svg light_opacity-decrease.svg light_lightness-increase.svg light_lightness-decrease.svg light_brushsize-increase.svg light_brushsize-decrease.svg light_curve-preset-u.svg light_curve-preset-s.svg light_curve-preset-s-reverse.svg light_curve-preset-linear.svg light_curve-preset-linear-reverse.svg light_curve-preset-l.svg light_curve-preset-j.svg light_curve-preset-arch.svg + light_ox16-action-object-align-horizontal-left-calligra.svg + light_ox16-action-object-align-horizontal-right-calligra.svg diff --git a/libs/flake/KoCanvasController.cpp b/libs/flake/KoCanvasController.cpp index 32a8058d88..f971fb633c 100644 --- a/libs/flake/KoCanvasController.cpp +++ b/libs/flake/KoCanvasController.cpp @@ -1,132 +1,129 @@ /* This file is part of the KDE project * * Copyright (C) 2010 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 "KoCanvasController.h" #include "KoToolManager.h" #include #include class Q_DECL_HIDDEN KoCanvasController::Private { public: Private() : margin(0) , preferredCenterFractionX(0.5) , preferredCenterFractionY(0.5) , actionCollection(0) { } int margin; QSize documentSize; QPoint documentOffset; qreal preferredCenterFractionX; qreal preferredCenterFractionY; KActionCollection* actionCollection; }; KoCanvasController::KoCanvasController(KActionCollection* actionCollection) : d(new Private()) { proxyObject = new KoCanvasControllerProxyObject(this); d->actionCollection = actionCollection; } KoCanvasController::~KoCanvasController() { KoToolManager::instance()->removeCanvasController(this); delete d; delete proxyObject; } void KoCanvasController::setMargin(int margin) { d->margin = margin; } int KoCanvasController::margin() const { return d->margin; } KoCanvasBase* KoCanvasController::canvas() const { return 0; } void KoCanvasController::setDocumentSize(const QSize &sz) { d->documentSize = sz; } QSize KoCanvasController::documentSize() const { return d->documentSize; } void KoCanvasController::setPreferredCenterFractionX(qreal x) { d->preferredCenterFractionX = x; } qreal KoCanvasController::preferredCenterFractionX() const { return d->preferredCenterFractionX; } void KoCanvasController::setPreferredCenterFractionY(qreal y) { d->preferredCenterFractionY = y; } qreal KoCanvasController::preferredCenterFractionY() const { return d->preferredCenterFractionY; } void KoCanvasController::setDocumentOffset(QPoint &offset) { d->documentOffset = offset; } QPoint KoCanvasController::documentOffset() const { return d->documentOffset; } - - - KoCanvasControllerProxyObject::KoCanvasControllerProxyObject(KoCanvasController *controller, QObject *parent) : QObject(parent) , m_canvasController(controller) { } void KoCanvasControllerProxyObject::updateDocumentSize(const QSize &newSize, bool recalculateCenter) { m_canvasController->updateDocumentSize(newSize, recalculateCenter); } KActionCollection* KoCanvasController::actionCollection() const { return d->actionCollection; } diff --git a/libs/flake/KoCanvasController.h b/libs/flake/KoCanvasController.h index 81e105fcd8..78c40e1b71 100644 --- a/libs/flake/KoCanvasController.h +++ b/libs/flake/KoCanvasController.h @@ -1,482 +1,482 @@ /* This file is part of the KDE project * Copyright (C) 2006, 2008 Thomas Zander * Copyright (C) 2007-2010 Boudewijn Rempt * Copyright (C) 2007-2008 C. Boemann * Copyright (C) 2006-2007 Jan Hambrecht * Copyright (C) 2009 Thorsten Zachmann * * 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 KOCANVASCONTROLLER_H #define KOCANVASCONTROLLER_H #include "kritaflake_export.h" #include #include #include #include #include class KActionCollection; class QRect; class QRectF; class KoShape; class KoCanvasBase; class KoCanvasControllerProxyObject; /** * KoCanvasController is the base class for wrappers around your canvas * that provides scrolling and zooming for your canvas. * * Flake does not provide a canvas, the application will have to * implement a canvas themselves. You canvas can be QWidget-based * or something we haven't invented yet -- as long the class that holds the canvas * implements KoCanvasController, tools, scrolling and zooming will work. * * A KoCanvasController implementation acts as a decorator around the canvas widget * and provides a way to scroll the canvas, allows the canvas to be centered * in the viewArea and manages tool activation. * *

The using application can instantiate this class and add its * canvas using the setCanvas() call. Which is designed so it can be * called multiple times if you need to exchange one canvas * widget for another, for instance, switching between a plain QWidget or a QOpenGLWidget. * *

There is _one_ KoCanvasController per canvas in your * application. * *

The canvas widget is at most as big as the viewport of the scroll * area, and when the view on the document is near its edges, smaller. * In your canvas widget code, you can find the right place in your * document in view coordinates (pixels) by adding the documentOffset */ class KRITAFLAKE_EXPORT KoCanvasController { public: // proxy QObject: use this to connect to slots and signals. QPointer proxyObject; /** * Constructor. * @param actionCollection the action collection for this canvas */ explicit KoCanvasController(KActionCollection* actionCollection); virtual ~KoCanvasController(); public: /** * Returns the current margin that is used to pad the canvas with. * This value is read from the KConfig property "canvasmargin" */ virtual int margin() const; /** * Set the new margin to pad the canvas with. */ virtual void setMargin(int margin); /** * compatibility with QAbstractScrollArea */ virtual void scrollContentsBy(int dx, int dy) = 0; /** * @return the size of the viewport */ virtual QSize viewportSize() const = 0; /** * Set the new canvas to be shown as a child * Calling this will emit canvasRemoved() if there was a canvas before, and will emit * canvasSet() with the new canvas. * @param canvas the new canvas. The KoCanvasBase::canvas() will be called to retrieve the * actual widget which will then be added as child of this one. */ virtual void setCanvas(KoCanvasBase *canvas) = 0; /** * Return the currently set canvas. The default implementation will return Null * @return the currently set canvas */ virtual KoCanvasBase *canvas() const; /** * return the amount of pixels vertically visible of the child canvas. * @return the amount of pixels vertically visible of the child canvas. */ virtual int visibleHeight() const = 0; /** * return the amount of pixels horizontally visible of the child canvas. * @return the amount of pixels horizontally visible of the child canvas. */ virtual int visibleWidth() const = 0; /** * return the amount of pixels that are not visible on the left side of the canvas. * The leftmost pixel that is shown is returned. */ virtual int canvasOffsetX() const = 0; /** * return the amount of pixels that are not visible on the top side of the canvas. * The topmost pixel that is shown is returned. */ virtual int canvasOffsetY() const = 0; /** * @brief Scrolls the content of the canvas so that the given rect is visible. * * The rect is to be specified in view coordinates (pixels). The scrollbar positions * are changed so that the centerpoint of the rectangle is centered if possible. * * @param rect the rectangle to make visible * @param smooth if true the viewport translation will make be just enough to ensure visibility, no more. * @see KoViewConverter::documentToView() */ virtual void ensureVisible(const QRectF &rect, bool smooth = false) = 0; /** * @brief Scrolls the content of the canvas so that the given shape is visible. * * This is just a wrapper function of the above function. * * @param shape the shape to make visible */ virtual void ensureVisible(KoShape *shape) = 0; /** * @brief zooms in around the center. * * The center must be specified in view coordinates (pixels). The scrollbar positions * are changed so that the center becomes center if possible. * * @param center the position to zoom in on */ virtual void zoomIn(const QPoint ¢er) = 0; /** * @brief zooms out around the center. * * The center must be specified in view coordinates (pixels). The scrollbar positions * are changed so that the center becomes center if possible. * * @param center the position to zoom out around */ virtual void zoomOut(const QPoint ¢er) = 0; /** * @brief zooms around the center. * * The center must be specified in view coordinates (pixels). The scrollbar positions * are changed so that the center becomes center if possible. * * @param center the position to zoom around * @param zoom the zoom to apply */ virtual void zoomBy(const QPoint ¢er, qreal zoom) = 0; /** * @brief zoom so that rect is exactly visible (as close as possible) * * The rect must be specified in view coordinates (pixels). The scrollbar positions * are changed so that the center of the rect becomes center if possible. * * @param rect the rect in view coordinates (pixels) that should fit the view afterwards */ virtual void zoomTo(const QRect &rect) = 0; /** * @brief repositions the scrollbars so previous center is once again center * * The previous center is cached from when the user uses the scrollbars or zoomTo * are called. zoomTo is mostly used when a zoom tool of sorts have marked an area * to zoom in on * * The success of this method is limited by the size of thing. But we try our best. */ virtual void recenterPreferred() = 0; /** * Sets the preferred center point in view coordinates (pixels). * @param viewPoint the new preferred center */ virtual void setPreferredCenter(const QPointF &viewPoint) = 0; /// Returns the currently set preferred center point in view coordinates (pixels) virtual QPointF preferredCenter() const = 0; /** * Move the canvas over the x and y distance of the parameter distance * @param distance the distance in view coordinates (pixels). A positive distance means moving the canvas up/left. */ virtual void pan(const QPoint &distance) = 0; /** * Move the canvas up. This behaves the same as \sa pan() with a positive y coordinate. */ virtual void panUp() = 0; /** * Move the canvas down. This behaves the same as \sa pan() with a negative y coordinate. */ virtual void panDown() = 0; /** * Move the canvas to the left. This behaves the same as \sa pan() with a positive x coordinate. */ virtual void panLeft() = 0; /** * Move the canvas to the right. This behaves the same as \sa pan() with a negative x coordinate. */ virtual void panRight() = 0; /** * Get the position of the scrollbar */ virtual QPoint scrollBarValue() const = 0; /** * Set the position of the scrollbar * @param value the new values of the scroll bars */ virtual void setScrollBarValue(const QPoint &value) = 0; /** * Update the range of scroll bars */ virtual void resetScrollBars() = 0; /** * Called when the size of your document in view coordinates (pixels) changes, for instance when zooming. * * @param newSize the new size, in view coordinates (pixels), of the document. * @param recalculateCenter if true the offset in the document we center on after calling * recenterPreferred() will be recalculated for the new document size so the visual offset stays the same. */ virtual void updateDocumentSize(const QSize &sz, bool recalculateCenter) = 0; /** * Set mouse wheel to zoom behaviour * @param zoom if true wheel will zoom instead of scroll, control modifier will scroll */ virtual void setZoomWithWheel(bool zoom) = 0; /** * Set scroll area to be bigger than actual document. * It allows the user to move the corner of the document * to e.g. the center of the screen * * @param factor the coefficient, defining how much we can scroll out, * measured in parts of the widget size. Null value means vast * scrolling is disabled. */ virtual void setVastScrolling(qreal factor) = 0; /** - * Returns the action collection for the canvas - * @returns action collection for this canvas, can be 0 + * Returns the action collection for the window + * @returns action collection for this window, can be 0 */ - virtual KActionCollection* actionCollection() const; + KActionCollection* actionCollection() const; QPoint documentOffset() const; /** * @return the current position of the cursor fetched from QCursor::pos() and * converted into document coordinates */ virtual QPointF currentCursorPosition() const = 0; protected: void setDocumentSize(const QSize &sz); QSize documentSize() const; void setPreferredCenterFractionX(qreal); qreal preferredCenterFractionX() const; void setPreferredCenterFractionY(qreal); qreal preferredCenterFractionY() const; void setDocumentOffset( QPoint &offset); private: class Private; Private * const d; }; /** * Workaround class for the problem that Qt does not allow two QObject base classes. * KoCanvasController can be implemented by for instance QWidgets, so it cannot be * a QObject directly. The interface of this class should be considered public interface * for KoCanvasController. */ class KRITAFLAKE_EXPORT KoCanvasControllerProxyObject : public QObject { Q_OBJECT Q_DISABLE_COPY(KoCanvasControllerProxyObject) public: explicit KoCanvasControllerProxyObject(KoCanvasController *canvasController, QObject *parent = 0); public: // Convenience methods to invoke the signals from subclasses void emitCanvasRemoved(KoCanvasController *canvasController) { emit canvasRemoved(canvasController); } void emitCanvasSet(KoCanvasController *canvasController) { emit canvasSet(canvasController); } void emitCanvasOffsetXChanged(int offset) { emit canvasOffsetXChanged(offset); } void emitCanvasOffsetYChanged(int offset) { emit canvasOffsetYChanged(offset); } void emitCanvasMousePositionChanged(const QPoint &position) { emit canvasMousePositionChanged(position); } void emitDocumentMousePositionChanged(const QPointF &position) { emit documentMousePositionChanged(position); } void emitSizeChanged(const QSize &size) { emit sizeChanged(size); } void emitMoveDocumentOffset(const QPoint &point) { emit moveDocumentOffset(point); } void emitZoomRelative(const qreal factor, const QPointF &stillPoint) { emit zoomRelative(factor, stillPoint); } // Convenience method to retrieve the canvas controller for who needs to use QPointer KoCanvasController *canvasController() const { return m_canvasController; } Q_SIGNALS: /** * Emitted when a previously added canvas is about to be removed. * @param canvasController this object */ void canvasRemoved(KoCanvasController *canvasController); /** * Emitted when a canvas is set on this widget * @param canvasController this object */ void canvasSet(KoCanvasController *canvasController); /** * Emitted when canvasOffsetX() changes * @param offset the new canvas offset */ void canvasOffsetXChanged(int offset); /** * Emitted when canvasOffsetY() changes * @param offset the new canvas offset */ void canvasOffsetYChanged(int offset); /** * Emitted when the cursor is moved over the canvas widget. * @param position the position in view coordinates (pixels). */ void canvasMousePositionChanged(const QPoint &position); /** * Emitted when the cursor is moved over the canvas widget. * @param position the position in document coordinates. * * Use \ref canvasMousePositionChanged to get the position * in view coordinates. */ void documentMousePositionChanged(const QPointF &position); /** * Emitted when the entire controller size changes * @param size the size in widget pixels. */ void sizeChanged(const QSize &size); /** * Emitted whenever the document is scrolled. * * @param point the new top-left point from which the document should * be drawn. */ void moveDocumentOffset(const QPoint &point); /** * Emitted when zoomRelativeToPoint have calculated a factor by which * the zoom should change and the point which should stand still * on screen. * Someone needs to connect to this and take action * * @param factor by how much the zoom needs to change. * @param stillPoint the point which will not change its position * in widget during the zooming. It is measured in * view coordinate system *before* zoom. */ void zoomRelative(const qreal factor, const QPointF &stillPoint); public Q_SLOTS: /** * Call this slot whenever the size of your document in view coordinates (pixels) * changes, for instance when zooming. * @param newSize the new size, in view coordinates (pixels), of the document. * @param recalculateCenter if true the offset in the document we center on after calling * recenterPreferred() will be recalculated for the new document size so the visual offset stays the same. */ void updateDocumentSize(const QSize &newSize, bool recalculateCenter = true); private: KoCanvasController *m_canvasController; }; class KRITAFLAKE_EXPORT KoDummyCanvasController : public KoCanvasController { public: explicit KoDummyCanvasController(KActionCollection* actionCollection) : KoCanvasController(actionCollection) {} ~KoDummyCanvasController() override {} void scrollContentsBy(int /*dx*/, int /*dy*/) override {} QSize viewportSize() const override { return QSize(); } void setCanvas(KoCanvasBase *canvas) override {Q_UNUSED(canvas)} KoCanvasBase *canvas() const override {return 0;} int visibleHeight() const override {return 0;} int visibleWidth() const override {return 0;} int canvasOffsetX() const override {return 0;} int canvasOffsetY() const override {return 0;} void ensureVisible(const QRectF &/*rect*/, bool /*smooth */ = false) override {} void ensureVisible(KoShape *shape) override {Q_UNUSED(shape)} void zoomIn(const QPoint &/*center*/) override {} void zoomOut(const QPoint &/*center*/) override {} void zoomBy(const QPoint &/*center*/, qreal /*zoom*/) override {} void zoomTo(const QRect &/*rect*/) override {} void recenterPreferred() override {} void setPreferredCenter(const QPointF &/*viewPoint*/) override {} QPointF preferredCenter() const override {return QPointF();} void pan(const QPoint &/*distance*/) override {} void panUp() override {} void panDown() override {} void panLeft() override {} void panRight() override {} QPoint scrollBarValue() const override {return QPoint();} void setScrollBarValue(const QPoint &/*value*/) override {} void resetScrollBars() override {} void updateDocumentSize(const QSize &/*sz*/, bool /*recalculateCenter*/) override {} void setZoomWithWheel(bool /*zoom*/) override {} void setVastScrolling(qreal /*factor*/) override {} QPointF currentCursorPosition() const override { return QPointF(); } }; #endif diff --git a/libs/flake/KoToolBase.cpp b/libs/flake/KoToolBase.cpp index 431bf8d89e..7d1f94c20c 100644 --- a/libs/flake/KoToolBase.cpp +++ b/libs/flake/KoToolBase.cpp @@ -1,429 +1,373 @@ /* This file is part of the KDE project * Copyright (C) 2006, 2010 Thomas Zander * Copyright (C) 2011 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include "KoToolBase.h" #include "KoToolBase_p.h" #include "KoCanvasBase.h" #include "KoPointerEvent.h" #include "KoDocumentResourceManager.h" #include "KoCanvasResourceProvider.h" #include "KoViewConverter.h" #include "KoShapeController.h" #include "KoShapeControllerBase.h" #include "KoToolSelection.h" +#include "KoCanvasController.h" #include #include #include #include #include #include KoToolBase::KoToolBase(KoCanvasBase *canvas) : d_ptr(new KoToolBasePrivate(this, canvas)) { Q_D(KoToolBase); d->connectSignals(); } KoToolBase::KoToolBase(KoToolBasePrivate &dd) : d_ptr(&dd) { Q_D(KoToolBase); d->connectSignals(); } KoToolBase::~KoToolBase() { -// Enable this to easily generate action files for tools - - // if (actions().size() > 0) { - - // QDomDocument doc; - // QDomElement e = doc.createElement("Actions"); - // e.setAttribute("name", toolId()); - // e.setAttribute("version", "2"); - // doc.appendChild(e); - - // Q_FOREACH (QAction *action, actions().values()) { - // QDomElement a = doc.createElement("Action"); - // a.setAttribute("name", action->objectName()); - - // // But seriously, XML is the worst format ever designed - // auto addElement = [&](QString title, QString content) { - // QDomElement newNode = doc.createElement(title); - // QDomText newText = doc.createTextNode(content); - // newNode.appendChild(newText); - // a.appendChild(newNode); - // }; - - // addElement("icon", action->icon().name()); - // addElement("text", action->text()); - // addElement("whatsThis" , action->whatsThis()); - // addElement("toolTip" , action->toolTip()); - // addElement("iconText" , action->iconText()); - // addElement("shortcut" , action->shortcut().toString()); - // addElement("isCheckable" , QString((action->isChecked() ? "true" : "false"))); - // addElement("statusTip", action->statusTip()); - // e.appendChild(a); - // } - // QFile f(toolId() + ".action"); - // f.open(QFile::WriteOnly); - // f.write(doc.toString().toUtf8()); - // f.close(); - - // } - -// else { -// debugFlake << "Tool" << toolId() << "has no actions"; -// } qDeleteAll(d_ptr->optionWidgets); delete d_ptr; } bool KoToolBase::isActivated() const { Q_D(const KoToolBase); return d->isActivated; } void KoToolBase::activate(KoToolBase::ToolActivation toolActivation, const QSet &shapes) { Q_UNUSED(toolActivation); Q_UNUSED(shapes); Q_D(KoToolBase); d->isActivated = true; } void KoToolBase::deactivate() { Q_D(KoToolBase); d->isActivated = false; } void KoToolBase::canvasResourceChanged(int key, const QVariant & res) { Q_UNUSED(key); Q_UNUSED(res); } void KoToolBase::documentResourceChanged(int key, const QVariant &res) { Q_UNUSED(key); Q_UNUSED(res); } bool KoToolBase::wantsAutoScroll() const { return true; } void KoToolBase::mouseDoubleClickEvent(KoPointerEvent *event) { event->ignore(); } void KoToolBase::mouseTripleClickEvent(KoPointerEvent *event) { event->ignore(); } void KoToolBase::keyPressEvent(QKeyEvent *e) { e->ignore(); } void KoToolBase::keyReleaseEvent(QKeyEvent *e) { e->ignore(); } void KoToolBase::explicitUserStrokeEndRequest() { } QVariant KoToolBase::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &) const { Q_D(const KoToolBase); if (d->canvas->canvasWidget() == 0) return QVariant(); switch (query) { case Qt::ImMicroFocus: return QRect(d->canvas->canvasWidget()->width() / 2, 0, 1, d->canvas->canvasWidget()->height()); case Qt::ImFont: return d->canvas->canvasWidget()->font(); default: return QVariant(); } } void KoToolBase::inputMethodEvent(QInputMethodEvent * event) { if (! event->commitString().isEmpty()) { QKeyEvent ke(QEvent::KeyPress, -1, 0, event->commitString()); keyPressEvent(&ke); } event->accept(); } void KoToolBase::customPressEvent(KoPointerEvent * event) { event->ignore(); } void KoToolBase::customReleaseEvent(KoPointerEvent * event) { event->ignore(); } void KoToolBase::customMoveEvent(KoPointerEvent * event) { event->ignore(); } void KoToolBase::useCursor(const QCursor &cursor) { Q_D(KoToolBase); d->currentCursor = cursor; emit cursorChanged(d->currentCursor); } QList > KoToolBase::optionWidgets() { Q_D(KoToolBase); if (d->optionWidgets.empty()) { d->optionWidgets = createOptionWidgets(); } return d->optionWidgets; } -void KoToolBase::addAction(const QString &name, QAction *action) -{ - Q_D(KoToolBase); - if (action->objectName().isEmpty()) { - action->setObjectName(name); - } - d->actions.insert(name, action); -} - -QHash KoToolBase::actions() const -{ - Q_D(const KoToolBase); - return d->actions; -} - QAction *KoToolBase::action(const QString &name) const { Q_D(const KoToolBase); - return d->actions.value(name); + return d->canvas->canvasController()->actionCollection()->action(name); } QWidget * KoToolBase::createOptionWidget() { return 0; } QList > KoToolBase::createOptionWidgets() { QList > ow; if (QWidget *widget = createOptionWidget()) { if (widget->objectName().isEmpty()) { widget->setObjectName(toolId()); } ow.append(widget); } return ow; } void KoToolBase::setToolId(const QString &id) { Q_D(KoToolBase); d->toolId = id; } QString KoToolBase::toolId() const { Q_D(const KoToolBase); return d->toolId; } QCursor KoToolBase::cursor() const { Q_D(const KoToolBase); return d->currentCursor; } void KoToolBase::deleteSelection() { } void KoToolBase::cut() { copy(); deleteSelection(); } QMenu *KoToolBase::popupActionsMenu() { return 0; } KoCanvasBase * KoToolBase::canvas() const { Q_D(const KoToolBase); return d->canvas; } void KoToolBase::setStatusText(const QString &statusText) { emit statusTextChanged(statusText); } uint KoToolBase::handleRadius() const { Q_D(const KoToolBase); if(d->canvas->shapeController()->resourceManager()) { return d->canvas->shapeController()->resourceManager()->handleRadius(); } else { return 3; } } uint KoToolBase::grabSensitivity() const { Q_D(const KoToolBase); if(d->canvas->shapeController()->resourceManager()) { return d->canvas->shapeController()->resourceManager()->grabSensitivity(); } else { return 3; } } QRectF KoToolBase::handleGrabRect(const QPointF &position) const { Q_D(const KoToolBase); const KoViewConverter * converter = d->canvas->viewConverter(); uint handleSize = 2*grabSensitivity(); QRectF r = converter->viewToDocument(QRectF(0, 0, handleSize, handleSize)); r.moveCenter(position); return r; } QRectF KoToolBase::handlePaintRect(const QPointF &position) const { Q_D(const KoToolBase); const KoViewConverter * converter = d->canvas->viewConverter(); uint handleSize = 2*handleRadius(); QRectF r = converter->viewToDocument(QRectF(0, 0, handleSize, handleSize)); r.moveCenter(position); return r; } void KoToolBase::setTextMode(bool value) { Q_D(KoToolBase); d->isInTextMode=value; } bool KoToolBase::paste() { return false; } void KoToolBase::copy() const { } void KoToolBase::dragMoveEvent(QDragMoveEvent *event, const QPointF &point) { Q_UNUSED(event); Q_UNUSED(point); } void KoToolBase::dragLeaveEvent(QDragLeaveEvent *event) { Q_UNUSED(event); } void KoToolBase::dropEvent(QDropEvent *event, const QPointF &point) { Q_UNUSED(event); Q_UNUSED(point); } bool KoToolBase::hasSelection() { KoToolSelection *sel = selection(); return (sel && sel->hasSelection()); } KoToolSelection *KoToolBase::selection() { return 0; } void KoToolBase::repaintDecorations() { } bool KoToolBase::isInTextMode() const { Q_D(const KoToolBase); return d->isInTextMode; } void KoToolBase::requestUndoDuringStroke() { /** * Default implementation just cancels the stroke */ requestStrokeCancellation(); } void KoToolBase::requestStrokeCancellation() { } void KoToolBase::requestStrokeEnd() { } bool KoToolBase::maskSyntheticEvents() const { Q_D(const KoToolBase); return d->maskSyntheticEvents; } void KoToolBase::setMaskSyntheticEvents(bool value) { Q_D(KoToolBase); d->maskSyntheticEvents = value; } diff --git a/libs/flake/KoToolBase.h b/libs/flake/KoToolBase.h index 6eebb6bddf..0afb6a641e 100644 --- a/libs/flake/KoToolBase.h +++ b/libs/flake/KoToolBase.h @@ -1,544 +1,523 @@ /* This file is part of the KDE project * Copyright (C) 2006 Thomas Zander * Copyright (C) 2011 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOTOOLBASE_H #define KOTOOLBASE_H #include #include #include #include #include #include "kritaflake_export.h" class KoShape; class KoCanvasBase; class KoPointerEvent; class KoViewConverter; class KoToolSelection; class KoToolBasePrivate; class KoShapeControllerBase; class QAction; class QKeyEvent; class QWidget; class QCursor; class QPainter; class QString; class QStringList; class QRectF; class QPointF; class QInputMethodEvent; class QDragMoveEvent; class QDragLeaveEvent; class QDropEvent; class QTouchEvent; class QMenu; /** * Abstract base class for all tools. Tools can create or manipulate * flake shapes, canvas state or any other thing that a user may wish * to do to his document or his view on a document with a pointing * device. * * There exists an instance of every tool for every pointer device. * These instances are managed by the toolmanager.. */ class KRITAFLAKE_EXPORT KoToolBase : public QObject { Q_OBJECT public: /// Option for activate() enum ToolActivation { TemporaryActivation, ///< The tool is activated temporarily and works 'in-place' of another one. DefaultActivation ///< The tool is activated normally and emitting 'done' goes to the defaultTool }; /** * Constructor, normally only called by the factory (see KoToolFactoryBase) * @param canvas the canvas interface this tool will work for. */ explicit KoToolBase(KoCanvasBase *canvas); ~KoToolBase() override; /** * request a repaint of the decorations to be made. This triggers * an update call on the canvas, but does not paint directly. */ virtual void repaintDecorations(); /** * Return if dragging (moving with the mouse down) to the edge of a canvas should scroll the * canvas (default is true). * @return if this tool wants mouse events to cause scrolling of canvas. */ virtual bool wantsAutoScroll() const; /** * Called by the canvas to paint any decorations that the tool deems needed. * The painter has the top left of the canvas as its origin. * @param painter used for painting the shape * @param converter to convert between internal and view coordinates. */ virtual void paint(QPainter &painter, const KoViewConverter &converter) = 0; /** * Return the option widgets for this tool. Create them if they * do not exist yet. If the tool does not have an option widget, * this method return an empty list. (After discussion with Thomas, who prefers * the toolmanager to handle that case.) * * @see m_optionWidgets */ QList > optionWidgets(); - /** - * Retrieves the entire collection of actions for the tool. - */ - QHash actions() const; - /** * Retrieve an action by name. */ QAction *action(const QString &name) const; - /** * Called when (one of) the mouse or stylus buttons is pressed. * Implementors should call event->ignore() if they do not actually use the event. * @param event state and reason of this mouse or stylus press */ virtual void mousePressEvent(KoPointerEvent *event) = 0; /** * Called when (one of) the mouse or stylus buttons is double clicked. * Implementors should call event->ignore() if they do not actually use the event. * Default implementation ignores this event. * @param event state and reason of this mouse or stylus press */ virtual void mouseDoubleClickEvent(KoPointerEvent *event); /** * Called when (one of) the mouse or stylus buttons is triple clicked. * Implementors should call event->ignore() if they do not actually use the event. * Default implementation ignores this event. * @param event state and reason of this mouse or stylus press */ virtual void mouseTripleClickEvent(KoPointerEvent *event); /** * Called when the mouse or stylus moved over the canvas. * Implementors should call event->ignore() if they do not actually use the event. * @param event state and reason of this mouse or stylus move */ virtual void mouseMoveEvent(KoPointerEvent *event) = 0; /** * Called when (one of) the mouse or stylus buttons is released. * Implementors should call event->ignore() if they do not actually use the event. * @param event state and reason of this mouse or stylus release */ virtual void mouseReleaseEvent(KoPointerEvent *event) = 0; /** * Called when a key is pressed. * Implementors should call event->ignore() if they do not actually use the event. * Default implementation ignores this event. * @param event state and reason of this key press */ virtual void keyPressEvent(QKeyEvent *event); /** * Called when a key is released * Implementors should call event->ignore() if they do not actually use the event. * Default implementation ignores this event. * @param event state and reason of this key release */ virtual void keyReleaseEvent(QKeyEvent *event); /** * @brief explicitUserStrokeEndRequest is called by the input manager * when the user presses Enter key or any equivalent. This callback * comes before requestStrokeEnd(), which comes from a different source. */ virtual void explicitUserStrokeEndRequest(); /** * This method is used to query a set of properties of the tool to be * able to support complex input method operations as support for surrounding * text and reconversions. * Default implementation returns simple defaults, for tools that want to provide * a more responsive text entry experience for CJK languages it would be good to reimplement. * @param query specifies which property is queried. * @param converter the view converter for the current canvas. */ virtual QVariant inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const; /** * Text entry of complex text, like CJK, can be made more interactive if a tool * implements this and the InputMethodQuery() methods. * Reimplementing this only provides the user with a more responsive text experience, since the * default implementation forwards the typed text as key pressed events. * @param event the input method event. */ virtual void inputMethodEvent(QInputMethodEvent *event); /** * Called when (one of) a custom device buttons is pressed. * Implementors should call event->ignore() if they do not actually use the event. * @param event state and reason of this custom device press */ virtual void customPressEvent(KoPointerEvent *event); /** * Called when (one of) a custom device buttons is released. * Implementors should call event->ignore() if they do not actually use the event. * @param event state and reason of this custom device release */ virtual void customReleaseEvent(KoPointerEvent *event); /** * Called when a custom device moved over the canvas. * Implementors should call event->ignore() if they do not actually use the event. * @param event state and reason of this custom device move */ virtual void customMoveEvent(KoPointerEvent *event); /** * @return true if synthetic mouse events on the canvas should be eaten. * * For example, the guides tool should allow click and drag with touch, * while the same touch events should be rejected by the freehand tool. * * These events are sent by the OS in Windows */ bool maskSyntheticEvents() const; /** * get the identifier code from the KoToolFactoryBase that created this tool. * @return the toolId. * @see KoToolFactoryBase::id() */ Q_INVOKABLE QString toolId() const; /// return the last emitted cursor QCursor cursor() const; /** * Returns the internal selection object of this tool. * Each tool can have a selection which is private to that tool and the specified shape that it comes with. * The default returns 0. */ virtual KoToolSelection *selection(); /** * @returns true if the tool has selected data. */ virtual bool hasSelection(); /** * copies the tools selection to the clipboard. * The default implementation is empty to aid tools that don't have any selection. * @see selection() */ virtual void copy() const; /** * Delete the tools selection. * The default implementation is empty to aid tools that don't have any selection. * @see selection() */ virtual void deleteSelection(); /** * Cut the tools selection and copy it to the clipboard. * The default implementation calls copy() and then deleteSelection() * @see copy() * @see deleteSelection() */ virtual void cut(); /** * Paste the clipboard selection. * A tool typically has one or more shapes selected and pasting should do something meaningful * for this specific shape and tool combination. Inserting text in a text tool, for example. * @return will return true if pasting succeeded. False if nothing happened. */ virtual bool paste(); /** * Handle the dragMoveEvent * A tool typically has one or more shapes selected and dropping into should do * something meaningful for this specific shape and tool combination. For example * dropping text in a text tool. * The tool should Accept the event if it is meaningful; Default implementation does not. */ virtual void dragMoveEvent(QDragMoveEvent *event, const QPointF &point); /** * Handle the dragLeaveEvent * Basically just a noticification that the drag is no long relevant * The tool should Accept the event if it is meaningful; Default implementation does not. */ virtual void dragLeaveEvent(QDragLeaveEvent *event); /** * Handle the dropEvent * A tool typically has one or more shapes selected and dropping into should do * something meaningful for this specific shape and tool combination. For example * dropping text in a text tool. * The tool should Accept the event if it is meaningful; Default implementation does not. */ virtual void dropEvent(QDropEvent *event, const QPointF &point); /** * @return a menu with context-aware actions for the current selection. If * the returned value is null, no context menu is shown. */ virtual QMenu* popupActionsMenu(); /// Returns the canvas the tool is working on KoCanvasBase *canvas() const; /** * This method can be reimplemented in a subclass. * @return returns true, if the tool is in text mode. that means, that there is * any kind of textual input and all single key shortcuts should be eaten. */ bool isInTextMode() const; public Q_SLOTS: /** * Called when the user requested undo while the stroke is * active. If you tool supports undo of the part of its actions, * override this method and do the needed work there. * * NOTE: Default implementation forwards this request to * requestStrokeCancellation() method, so that the stroke * would be cancelled. */ virtual void requestUndoDuringStroke(); /** * Called when the user requested the cancellation of the current * stroke. If you tool supports cancelling, override this method * and do the needed work there */ virtual void requestStrokeCancellation(); /** * Called when the image decided that the stroke should better be * ended. If you tool supports long strokes, override this method * and do the needed work there */ virtual void requestStrokeEnd(); /** * This method is called when this tool instance is activated. * For any main window there is only one tool active at a time, which then gets all * user input. Switching between tools will call deactivate on one and activate on the * new tool allowing the tool to flush items (like a selection) * when it is not in use. * *

There is one case where two tools are activated at the same. This is the case * where one tool delegates work to another temporarily. For example, while shift is * being held down. The second tool will get activated with temporary=true and * it should emit done() when the state that activated it is ended. *

One of the important tasks of activate is to call useCursor() * * @param shapes the set of shapes that are selected or suggested for editing by a * selected shape for the tool to work on. Not all shapes will be meant for this * tool. * @param toolActivation if TemporaryActivation, this tool is only temporarily activated * and should emit done when it is done. * @see deactivate() */ virtual void activate(ToolActivation toolActivation, const QSet &shapes); /** * This method is called whenever this tool is no longer the * active tool * @see activate() */ virtual void deactivate(); /** * This method is called whenever a property in the resource * provider associated with the canvas this tool belongs to * changes. An example is currently selected foreground color. */ virtual void canvasResourceChanged(int key, const QVariant &res); /** * This method is called whenever a property in the resource * provider associated with the document this tool belongs to * changes. An example is the handle radius */ virtual void documentResourceChanged(int key, const QVariant &res); /** * This method just relays the given text via the tools statusTextChanged signal. * @param statusText the new status text */ void setStatusText(const QString &statusText); Q_SIGNALS: /** * Emitted when this tool wants itself to be replaced by another tool. * * @param id the identification of the desired tool * @see toolId(), KoToolFactoryBase::id() */ void activateTool(const QString &id); /** * Emitted when this tool wants itself to temporarily be replaced by another tool. * For instance, a paint tool could desire to be * temporarily replaced by a pan tool which could be temporarily * replaced by a colorpicker. * @param id the identification of the desired tool */ void activateTemporary(const QString &id); /** * Emitted when the tool has been temporarily activated and wants * to notify the world that it's done. */ void done(); /** * Emitted by useCursor() when the cursor to display on the canvas is changed. * The KoToolManager should connect to this signal to handle cursors further. */ void cursorChanged(const QCursor &cursor); /** * A tool can have a selection that is copy-able, this signal is emitted when that status changes. * @param hasSelection is true when the tool holds selected data. */ void selectionChanged(bool hasSelection); /** * Emitted when the tool wants to display a different status text * @param statusText the new status text */ void statusTextChanged(const QString &statusText); protected: /** * Classes inheriting from this one can call this method to signify which cursor * the tool wants to display at this time. Logical place to call it is after an * incoming event has been handled. * @param cursor the new cursor. */ void useCursor(const QCursor &cursor); /** * Reimplement this if your tool actually has an option widget. * Sets the option widget to 0 by default. */ virtual QWidget *createOptionWidget(); virtual QList > createOptionWidgets(); - /** - * Add an action under the given name to the collection. - * - * Inserting an action under a name that is already used for another action will replace - * the other action in the collection. - * - * @param name The name by which the action be retrieved again from the collection. - * @param action The action to add. - * @param readWrite set this to ReadOnlyAction to keep the action available on - * read-only documents - */ - void addAction(const QString &name, QAction *action); - /// Convenience function to get the current handle radius uint handleRadius() const; /// Convencience function to get the current grab sensitivity uint grabSensitivity() const; /** * Returns a handle grab rect at the given position. * * The position is expected to be in document coordinates. The grab sensitivity * canvas resource is used for the dimension of the rectangle. * * @return the handle rectangle in document coordinates */ QRectF handleGrabRect(const QPointF &position) const; /** * Returns a handle paint rect at the given position. * * The position is expected to be in document coordinates. The handle radius * canvas resource is used for the dimension of the rectangle. * * @return the handle rectangle in document coordinates */ QRectF handlePaintRect(const QPointF &position) const; /** * You should set the text mode to true in subclasses, if this tool is in text input mode, eg if the users * are able to type. If you don't set it, then single key shortcuts will get the key event and not this tool. */ void setTextMode(bool value); /** * Allows subclasses to specify whether synthetic mouse events should be accepted. */ void setMaskSyntheticEvents(bool value); /** * Returns true if activate() has been called (more times than deactivate :) ) */ bool isActivated() const; protected: KoToolBase(KoToolBasePrivate &dd); KoToolBasePrivate *d_ptr; private: friend class ToolHelper; /** * Set the identifier code from the KoToolFactoryBase that created this tool. * @param id the identifier code * @see KoToolFactoryBase::id() */ void setToolId(const QString &id); - - KoToolBase(); KoToolBase(const KoToolBase&); KoToolBase& operator=(const KoToolBase&); Q_DECLARE_PRIVATE(KoToolBase) }; #endif /* KOTOOL_H */ diff --git a/libs/flake/KoToolBase_p.h b/libs/flake/KoToolBase_p.h index 91cf2b5089..4e60c8b0d0 100644 --- a/libs/flake/KoToolBase_p.h +++ b/libs/flake/KoToolBase_p.h @@ -1,89 +1,88 @@ /* This file is part of the KDE project * Copyright (C) 2006-2010 Thomas Zander * Copyright (C) 2010 KO GmbH * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOTOOLBASE_P_H #define KOTOOLBASE_P_H #include "KoDocumentResourceManager.h" #include "KoCanvasResourceProvider.h" #include "KoCanvasBase.h" #include "KoShapeController.h" #include #include #include #include #include // for the qt version check class QAction; class KoToolBase; class KoToolBasePrivate { public: KoToolBasePrivate(KoToolBase *qq, KoCanvasBase *canvas_) : currentCursor(Qt::ArrowCursor), q(qq), canvas(canvas_), isInTextMode(false), isActivated(false) { } ~KoToolBasePrivate() { Q_FOREACH (QPointer optionWidget, optionWidgets) { if (optionWidget) { optionWidget->setParent(0); delete optionWidget; } } optionWidgets.clear(); } void connectSignals() { if (canvas) { // in the case of KoToolManagers dummytool it can be zero :( KoCanvasResourceProvider * crp = canvas->resourceManager(); Q_ASSERT_X(crp, "KoToolBase::KoToolBase", "No Canvas KoResourceManager"); if (crp) q->connect(crp, SIGNAL(canvasResourceChanged(int, const QVariant &)), SLOT(canvasResourceChanged(int, const QVariant &))); // can be 0 in the case of Calligra Sheets KoDocumentResourceManager *scrm = canvas->shapeController()->resourceManager(); if (scrm) { q->connect(scrm, SIGNAL(resourceChanged(int, const QVariant &)), SLOT(documentResourceChanged(int, const QVariant &))); } } } QList > optionWidgets; ///< the optionwidgets associated with this tool QCursor currentCursor; - QHash actions; QString toolId; KoToolBase *q; KoCanvasBase *canvas; ///< the canvas interface this tool will work for. bool isInTextMode; bool maskSyntheticEvents{false}; ///< Whether this tool masks synthetic events bool isActivated; }; #endif diff --git a/libs/flake/KoToolFactoryBase.cpp b/libs/flake/KoToolFactoryBase.cpp index e037f2e480..63ff044007 100644 --- a/libs/flake/KoToolFactoryBase.cpp +++ b/libs/flake/KoToolFactoryBase.cpp @@ -1,122 +1,213 @@ /* This file is part of the KDE project * Copyright (C) 2006 Thomas Zander * * 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 "KoToolFactoryBase.h" #include "KoToolBase.h" +#include + +#include #include +#include +#include class Q_DECL_HIDDEN KoToolFactoryBase::Private { public: Private(const QString &i) - : priority(100), - id(i) + : priority(100), + id(i) { } int priority; QString section; QString tooltip; QString activationId; QString iconName; const QString id; QKeySequence shortcut; }; KoToolFactoryBase::KoToolFactoryBase(const QString &id) - : d(new Private(id)) + : d(new Private(id)) { } KoToolFactoryBase::~KoToolFactoryBase() { delete d; } +QList KoToolFactoryBase::createActions(KActionCollection *actionCollection) +{ +// qDebug() << "creating actions for" << id(); + QList toolActions; + Q_FOREACH(QAction *action, createActionsImpl()) { + if (action->objectName().isEmpty()) { + qWarning() << "Tool" << id() << "tries to add an action without a name"; + continue; + } + QAction *existingAction = actionCollection->action(action->objectName()); + if (existingAction) { +// qDebug() << "\tFound existing action" << action->objectName() << existingAction->property("tool_action"); + delete action; + action = existingAction; + } + + QStringList tools; + if (action->property("tool_action").isValid()) { + tools = action->property("tool_action").toStringList(); + } + tools << id(); + action->setProperty("tool_action", tools); + if (!existingAction) { +// qDebug() << "\tAdding new action" << action->objectName() << "Associated with" << tools; + actionCollection->addAction(action->objectName(), action); + } + toolActions << action; + } + + // Enable this to easily generate action files for tools + #if 0 + if (toolActions.size() > 0) { + + QDomDocument doc; + QDomElement e = doc.createElement("Actions"); + e.setAttribute("name", id); + e.setAttribute("version", "2"); + doc.appendChild(e); + + Q_FOREACH (QAction *action, toolActions) { + QDomElement a = doc.createElement("Action"); + a.setAttribute("name", action->objectName()); + + // But seriously, XML is the worst format ever designed + auto addElement = [&](QString title, QString content) { + QDomElement newNode = doc.createElement(title); + QDomText newText = doc.createTextNode(content); + newNode.appendChild(newText); + a.appendChild(newNode); + }; + + addElement("icon", action->icon().name()); + addElement("text", action->text()); + addElement("whatsThis" , action->whatsThis()); + addElement("toolTip" , action->toolTip()); + addElement("iconText" , action->iconText()); + addElement("shortcut" , action->shortcut().toString()); + addElement("isCheckable" , QString((action->isChecked() ? "true" : "false"))); + addElement("statusTip", action->statusTip()); + e.appendChild(a); + } + QFile f(id()z + ".action"); + f.open(QFile::WriteOnly); + f.write(doc.toString().toUtf8()); + f.close(); + + } + + else { + debugFlake << "Tool" << id() << "has no actions"; + } +#endif + +// qDebug() << "Generated actions for tool factory" << id(); +// Q_FOREACH(QAction *action, toolActions) { +// qDebug() << "\taction:" << action->objectName() << "shortcut" << action->shortcuts() << "tools" << action->property("tool_action").toStringList(); +// } + return toolActions; +} + QString KoToolFactoryBase::id() const { return d->id; } int KoToolFactoryBase::priority() const { return d->priority; } QString KoToolFactoryBase::section() const { return d->section; } QString KoToolFactoryBase::toolTip() const { return d->tooltip; } QString KoToolFactoryBase::iconName() const { return d->iconName; } QString KoToolFactoryBase::activationShapeId() const { return d->activationId; } QKeySequence KoToolFactoryBase::shortcut() const { return d->shortcut; } void KoToolFactoryBase::setActivationShapeId(const QString &activationShapeId) { d->activationId = activationShapeId; } void KoToolFactoryBase::setToolTip(const QString & tooltip) { d->tooltip = tooltip; } void KoToolFactoryBase::setSection(const QString & section) { d->section = section; } void KoToolFactoryBase::setIconName(const char *iconName) { d->iconName = QLatin1String(iconName); } void KoToolFactoryBase::setIconName(const QString &iconName) { d->iconName = iconName; } void KoToolFactoryBase::setPriority(int newPriority) { d->priority = newPriority; } void KoToolFactoryBase::setShortcut(const QKeySequence &shortcut) { d->shortcut = shortcut; } + +QList KoToolFactoryBase::createActionsImpl() +{ + return QList(); +} + diff --git a/libs/flake/KoToolFactoryBase.h b/libs/flake/KoToolFactoryBase.h index 3673bad272..db827364a8 100644 --- a/libs/flake/KoToolFactoryBase.h +++ b/libs/flake/KoToolFactoryBase.h @@ -1,240 +1,262 @@ /* This file is part of the KDE project * Copyright (c) 2004 Boudewijn Rempt * Copyright (C) 2006 Thomas Zander * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KO_TOOL_FACTORY_H #define KO_TOOL_FACTORY_H #include "kritaflake_export.h" #include +#include class KoCanvasBase; class KoToolBase; class QKeySequence; +class KActionCollection; +class QAction; /** * A factory for KoToolBase objects. * * The baseclass for all tool plugins. Each plugin that ships a KoToolBase should also * ship a factory. That factory will extend this class and set variable data like * a toolTip and icon in the constructor of that extending class. * * An example usage would be:

  * class MyToolFactory : public KoToolFactoryBase {
  * public:
  *   MyToolFactory(const QStringList&)
  *       : KoToolFactoryBase("MyTool") {
  *       setToolTip(i18n("Create object"));
  *       setToolType("dynamic");
  *       setPriority(5);
  *   }
  *   ~MyToolFactory() {}
  *   KoToolBase *createTool(KoCanvasBase *canvas);
  * };
  * K_PLUGIN_FACTORY_WITH_JSON((MyToolFactoryFactory, "mytool.json", registerPlugin();)
 
*/ class KRITAFLAKE_EXPORT KoToolFactoryBase { public: /** * Create the new factory * @param id a string that will be used internally for referencing the tool, for * example for use by the KoToolBase::activateTemporary. */ explicit KoToolFactoryBase(const QString &id); virtual ~KoToolFactoryBase(); + /** + * Create the actions for this tool. Actions are unique per window, not per + * tool instance; tool instances are unique per view/canvas. + */ + QList createActions(KActionCollection *actionCollection); + /** * Instantiate a new tool * @param canvas the canvas that the new tool will work on. Should be passed * to the constructor of the tool. * @return a new KoToolBase instance, or zero if the tool doesn't want to show up. */ virtual KoToolBase *createTool(KoCanvasBase *canvas) = 0; /** * return the id for the tool this factory creates. * @return the id for the tool this factory creates. */ QString id() const; /** * Returns The priority of this tool in its section in the toolbox * @return The priority of this tool. */ int priority() const; /** * returns the type of tool, used to group tools in the toolbox * @return the type of tool */ QString section() const; /** * return a translated tooltip Text * @return a translated tooltip Text */ QString toolTip() const; /** * return the basename of the icon for this tool * @return the basename of the icon for this tool */ QString iconName() const; /** * Return the id of the shape we can process. * This is the shape Id the tool we create is associated with. So a TextTool for a TextShape. * In combination with the toolType the following situations can occur;
TypeshapeIdResult
'main' Foo Tool will always be visible, but only active when shape with shapeId 'Foo' is in the selection.
'main' '' Tool will always be visible, but only active when at least one shape is selected
'main' 'flake/always' Tool will always be visible and enabled.
'main' 'flake/edit' Tool will be visible no matter which shape is selected (if any), but only be enabled when the current layer is editable.
'dynamic' Foo Tool will only be visible when shape with shapeId 'Foo' is in the selection.
'dynamic' '' Tool will always be visible. We recommend you don't use this one.
"comma separated list of application names" see main type Similar to the 'main' item if the application name matches with the current application. Otherwise it's similar to 'dynamic', but segmented in its own section. If the list includes 'dynamic' it's even added to the dynamic section, when not matching the application name
'other' any similar to the 'dynamic' items, but segmented in its own section.
n/a /always An activation shape id ending with '/always' will make the tool always visible and enabled.
* @see KoShapeFactoryBase::shapeId() * @see setActivationShapeId() * @return the id of a shape, or an empty string for all shapes. */ QString activationShapeId() const; /** * Return the default keyboard shortcut for activation of this tool (if * the shape this tool belongs to is active). * * See KoToolManager for use. * * @return the shortcut */ QKeySequence shortcut() const; /** * Returns the main toolType * Each tool has a toolType which it uses to be grouped in the toolbox. * The predefined areas are main and dynamic. "main" tools are always * shown. * * @see toolType() * @see setToolType() */ static QString mainToolType() { return "main"; } /** * Returns the navigation toolType * Each tool has a toolType which it uses to be grouped in the toolbox. * The predefined areas are main and dynamic. "navigation" tools are always * shown and are for tools that change the settings of the canvas, zoom, pan... * * @see toolType() * @see setToolType() */ static QString navigationToolType() { return "navigation"; } /** * Returns the dynamic toolType * Each tool has a toolType which it uses to be grouped in the toolbox. * The predefined areas are main and dynamic. Dynamic tools are hidden * until the shape they belong to is activated. * * @see toolType() * @see setToolType() */ static QString dynamicToolType() { return "dynamic"; } /** * Set the default shortcut for activation of this tool. */ void setShortcut(const QKeySequence & shortcut); protected: + /** * Set the tooltip to be used for this tool * @param tooltip the tooltip */ void setToolTip(const QString &tooltip); + /** * Set the toolType. used to group tools in the toolbox * @param toolType the toolType */ void setSection(const QString §ion); + /** * Set an icon to be used in the toolBox. * @param iconName the basename (without extension) of the icon */ void setIconName(const char *iconName); void setIconName(const QString &iconName); + /** * Set the priority of this tool, as it is shown in the toolBox; lower number means * it will be show more to the front of the list. * @param newPriority the priority */ void setPriority(int newPriority); + /** * Set the id of the shape we can process. * This is the Id, as passed to the constructor of a KoShapeFactoryBase, that the tool * we create is associated with. This means that if a KoTextShape is selected, then * all tools that have its id set here will be added to the dynamic part of the toolbox. * @param activationShapeId the Id of the shape * @see activationShapeId() */ void setActivationShapeId(const QString &activationShapeId); + /** + * @brief createActionsImpl should be reimplemented if the tool needs any actions. + * The actions should have a valid objectName(). + * + * @return the list of actions this tool wishes to be available. + */ + virtual QList createActionsImpl(); + private: class Private; Private * const d; }; #endif diff --git a/libs/flake/KoToolManager.cpp b/libs/flake/KoToolManager.cpp index 4050652412..66a04bc0c9 100644 --- a/libs/flake/KoToolManager.cpp +++ b/libs/flake/KoToolManager.cpp @@ -1,1009 +1,975 @@ /* This file is part of the KDE project * * Copyright (c) 2005-2010 Boudewijn Rempt * Copyright (C) 2006-2008 Thomas Zander * Copyright (C) 2006 Thorsten Zachmann * Copyright (C) 2008 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. */ // flake #include "KoToolManager.h" #include "KoToolManager_p.h" #include "KoToolRegistry.h" #include "KoToolProxy.h" #include "KoToolProxy_p.h" #include "KoSelection.h" #include "KoCanvasController.h" #include "KoCanvasControllerWidget.h" #include "KoShape.h" #include "KoShapeLayer.h" #include "KoShapeRegistry.h" #include "KoShapeManager.h" #include "KoSelectedShapesProxy.h" #include "KoCanvasBase.h" #include "KoInputDeviceHandlerRegistry.h" #include "KoInputDeviceHandlerEvent.h" #include "KoPointerEvent.h" #include "tools/KoCreateShapesTool.h" #include "tools/KoZoomTool.h" #include "kis_action_registry.h" #include "KoToolFactoryBase.h" #include "kis_assert.h" #include // Qt + kde #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_GLOBAL_STATIC(KoToolManager, s_instance) class CanvasData { public: CanvasData(KoCanvasController *cc, const KoInputDevice &id) - : activeTool(0), - canvas(cc), - inputDevice(id), - dummyToolWidget(0), - dummyToolLabel(0) + : activeTool(0), + canvas(cc), + inputDevice(id), + dummyToolWidget(0), + dummyToolLabel(0) { } ~CanvasData() { // the dummy tool widget does not necessarily have a parent and we create it, so we delete it. delete dummyToolWidget; } void activateToolActions() { - disabledDisabledActions.clear(); - disabledActions.clear(); - disabledCanvasShortcuts.clear(); - // we do several things here - // 1. enable the actions of the active tool - // 2. disable conflicting actions - // 3. replace conflicting actions in the action collection - KActionCollection *canvasActionCollection = canvas->actionCollection(); - - QHash toolActions = activeTool->actions(); - QHash::const_iterator it(toolActions.constBegin()); - - for (; it != toolActions.constEnd(); ++it) { - if (canvasActionCollection) { - - QString toolActionID = it.key(); - QAction *toolAction = it.value(); - - QAction * action = qobject_cast(canvasActionCollection->action(it.key())); - if (action) { - canvasActionCollection->takeAction(action); - if (action != it.value()) { - if (action->isEnabled()) { - action->setEnabled(false); - disabledActions.append(action); - } else { - disabledDisabledActions.append(action); - } + toolActions.clear(); + disabledGlobalActions.clear(); + + KActionCollection *windowActionCollection = canvas->actionCollection(); + + if (!windowActionCollection) { + qWarning() << "We haven't got an action collection"; + return; + } + + QStringList globalActions; + + QMap shortcutMap; + + //qDebug() << "................... activating tool" << activeToolId; + + Q_FOREACH(QAction *action, windowActionCollection->actions()) { + + //qDebug() << "Action" << action->objectName() << "shortcuts" << action->shortcuts(); + if (action->property("tool_action").isValid()) { + QStringList tools = action->property("tool_action").toStringList(); + //qDebug() << "\tassociated with" << tools; + if (tools.contains(activeToolId)) { + //qDebug() << "\t\tenabling"; + action->setEnabled(true); + toolActions << action->objectName(); + } + else { + action->setDisabled(true); + } + } + else { + globalActions << action->objectName(); + } + + Q_FOREACH(QKeySequence keySequence, action->shortcuts()) { + // After loading a custom shortcut profile, shortcuts can be defined as an empty string, which is not an empty shortcut + if (keySequence.toString() != "") { + if (shortcutMap.contains(keySequence)) { + shortcutMap[keySequence].append(action->objectName()); + } + else { + shortcutMap[keySequence] = QStringList() << action->objectName(); } } - Q_FOREACH (QAction *a, canvasActionCollection->actions()) { - QAction *canvasAction = dynamic_cast(a); - if (canvasAction && canvasAction->shortcut().toString() != "" && canvasAction->shortcut() == toolAction->shortcut()) { - warnFlake << activeToolId << ": action" << toolActionID << "conflicts with canvas action" << canvasAction->objectName() << "shortcut:" << canvasAction->shortcut().toString(); - disabledCanvasShortcuts[canvasAction] = canvasAction->shortcut().toString(); - canvasAction->setShortcut(QKeySequence()); + } + } + + // Make sure the tool's actions override the global actions that aren't associated with the tool. + Q_FOREACH(const QKeySequence &k, shortcutMap.keys()) { + if (shortcutMap[k].size() > 1) { + QStringList actions = shortcutMap[k]; + //qDebug() << k << actions; + bool toolActionFound = false; + Q_FOREACH(const QString &action, actions) { + if (toolActions.contains(action)) { + toolActionFound = true; } } - canvasActionCollection->addAction(toolActionID, toolAction); + Q_FOREACH(const QString &action, actions) { + if (toolActionFound && globalActions.contains(action)) { + //qDebug() << "\tdisabling global action" << action; + windowActionCollection->action(action)->setEnabled(false); + disabledGlobalActions << action; + } + } + //qDebug() << k << shortcutMap[k]; } - it.value()->setEnabled(true); } - canvasActionCollection->readSettings(); // The shortcuts might have been configured in the meantime. + + windowActionCollection->readSettings(); // The shortcuts might have been configured in the meantime. } void deactivateToolActions() { - if (!activeTool) return; - // disable actions of active tool - Q_FOREACH (QAction *action, activeTool->actions()) { - action->setEnabled(false); - } - // enable actions which where disabled on activating the active tool - // and re-add them to the action collection - KActionCollection *ac = canvas->actionCollection(); - Q_FOREACH (QPointer action, disabledDisabledActions) { - if (action) { - if (ac) { - ac->addAction(action->objectName(), action); - } - } - } - disabledDisabledActions.clear(); + //qDebug() << "............... deactivating previous tool because activating" << activeToolId; - Q_FOREACH (QPointer action, disabledActions) { - if (action) { - action->setEnabled(true); - if(ac) { - ac->addAction(action->objectName(), action); - } - } - } - disabledActions.clear(); + KActionCollection *windowActionCollection = canvas->actionCollection(); - QMap, QString>::const_iterator it(disabledCanvasShortcuts.constBegin()); - for (; it != disabledCanvasShortcuts.constEnd(); ++it) { - QAction *action = it.key(); - QString shortcut = it.value(); - action->setShortcut(shortcut); + Q_FOREACH(const QString &action, toolActions) { + //qDebug() << "disabling" << action; + windowActionCollection->action(action)->setDisabled(true); + } + Q_FOREACH(const QString &action, disabledGlobalActions) { + //qDebug() << "enabling" << action; + windowActionCollection->action(action)->setEnabled(true); } - disabledCanvasShortcuts.clear(); } KoToolBase *activeTool; // active Tool QString activeToolId; // the id of the active Tool QString activationShapeId; // the shape-type (KoShape::shapeId()) the activeTool 'belongs' to. QHash allTools; // all the tools that are created for this canvas. QStack stack; // stack of temporary tools KoCanvasController *const canvas; const KoInputDevice inputDevice; QWidget *dummyToolWidget; // the widget shown in the toolDocker. QLabel *dummyToolLabel; - QList > disabledActions; ///< disabled conflicting actions - QList > disabledDisabledActions; ///< disabled conflicting actions that were already disabled - QMap, QString> disabledCanvasShortcuts; ///< Shortcuts that were temporarily removed from canvas actions because the tool overrides + QStringList toolActions; + QStringList disabledGlobalActions; }; // ******** KoToolManager ********** KoToolManager::KoToolManager() : QObject(), - d(new Private(this)) + d(new Private(this)) { connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*,QWidget*)), this, SLOT(movedFocus(QWidget*,QWidget*))); } KoToolManager::~KoToolManager() { delete d; } QList KoToolManager::toolActionList() const { QList answer; answer.reserve(d->tools.count()); Q_FOREACH (ToolHelper *tool, d->tools) { if (tool->id() == KoCreateShapesTool_ID) continue; // don't show this one. answer.append(tool->toolAction()); } return answer; } void KoToolManager::requestToolActivation(KoCanvasController * controller) { if (d->canvasses.contains(controller)) { QString activeToolId = d->canvasses.value(controller).first()->activeToolId; Q_FOREACH (ToolHelper * th, d->tools) { if (th->id() == activeToolId) { d->toolActivated(th); break; } } } } KoInputDevice KoToolManager::currentInputDevice() const { return d->inputDevice; } void KoToolManager::registerToolActions(KActionCollection *ac, KoCanvasController *controller) { Q_ASSERT(controller); Q_ASSERT(ac); d->setup(); if (!d->canvasses.contains(controller)) { return; } - // Actions available during the use of individual tools - CanvasData *cd = d->canvasses.value(controller).first(); - Q_FOREACH (KoToolBase *tool, cd->allTools) { - QHash actions = tool->actions(); - QHash::const_iterator action(actions.constBegin()); - for (; action != actions.constEnd(); ++action) { - if (!ac->action(action.key())) - ac->addAction(action.key(), action.value()); - } - } - // Actions used to switch tools via shortcuts Q_FOREACH (ToolHelper * th, d->tools) { if (ac->action(th->id())) { continue; } ShortcutToolAction* action = th->createShortcutToolAction(ac); ac->addCategorizedAction(th->id(), action, "tool-shortcuts"); } } void KoToolManager::addController(KoCanvasController *controller) { Q_ASSERT(controller); if (d->canvasses.contains(controller)) return; d->setup(); d->attachCanvas(controller); connect(controller->proxyObject, SIGNAL(destroyed(QObject*)), this, SLOT(attemptCanvasControllerRemoval(QObject*))); connect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*))); connect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*))); } void KoToolManager::removeCanvasController(KoCanvasController *controller) { Q_ASSERT(controller); disconnect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*))); disconnect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*))); d->detachCanvas(controller); } void KoToolManager::attemptCanvasControllerRemoval(QObject* controller) { KoCanvasControllerProxyObject* controllerActual = qobject_cast(controller); if (controllerActual) { removeCanvasController(controllerActual->canvasController()); } } void KoToolManager::switchToolRequested(const QString & id) { Q_ASSERT(d->canvasData); if (!d->canvasData) return; while (!d->canvasData->stack.isEmpty()) // switching means to flush the stack d->canvasData->stack.pop(); d->switchTool(id, false); } void KoToolManager::switchInputDeviceRequested(const KoInputDevice &id) { if (!d->canvasData) return; d->switchInputDevice(id); } void KoToolManager::switchToolTemporaryRequested(const QString &id) { d->switchTool(id, true); } void KoToolManager::switchBackRequested() { if (!d->canvasData) return; if (d->canvasData->stack.isEmpty()) { // default to changing to the interactionTool d->switchTool(KoInteractionTool_ID, false); return; } d->switchTool(d->canvasData->stack.pop(), false); } KoCreateShapesTool * KoToolManager::shapeCreatorTool(KoCanvasBase *canvas) const { Q_ASSERT(canvas); Q_FOREACH (KoCanvasController *controller, d->canvasses.keys()) { if (controller->canvas() == canvas) { KoCreateShapesTool *createTool = dynamic_cast - (d->canvasData->allTools.value(KoCreateShapesTool_ID)); + (d->canvasData->allTools.value(KoCreateShapesTool_ID)); Q_ASSERT(createTool /* ID changed? */); return createTool; } } Q_ASSERT(0); // this should not happen return 0; } KoToolBase *KoToolManager::toolById(KoCanvasBase *canvas, const QString &id) const { Q_ASSERT(canvas); Q_FOREACH (KoCanvasController *controller, d->canvasses.keys()) { if (controller->canvas() == canvas) return d->canvasData->allTools.value(id); } return 0; } KoCanvasController *KoToolManager::activeCanvasController() const { if (! d->canvasData) return 0; return d->canvasData->canvas; } QString KoToolManager::preferredToolForSelection(const QList &shapes) { QSet shapeTypes; Q_FOREACH (KoShape *shape, shapes) { shapeTypes << shape->shapeId(); } //KritaUtils::makeContainerUnique(types); QString toolType = KoInteractionTool_ID; int prio = INT_MAX; Q_FOREACH (ToolHelper *helper, d->tools) { if (helper->id() == KoCreateShapesTool_ID) continue; if (helper->priority() >= prio) continue; bool toolWillWork = false; foreach (const QString &type, shapeTypes) { if (helper->activationShapeId().split(',').contains(type)) { toolWillWork = true; break; } } if (toolWillWork) { toolType = helper->id(); prio = helper->priority(); } } return toolType; } QPair KoToolManager::createTools(KoCanvasController *controller, ToolHelper *tool) { // XXX: maybe this method should go into the private class? QHash origHash; if (d->canvasses.contains(controller)) { origHash = d->canvasses.value(controller).first()->allTools; } if (origHash.contains(tool->id())) { return QPair(tool->id(), origHash.value(tool->id())); } debugFlake << "Creating tool" << tool->id() << ". Activated on:" << tool->activationShapeId() << ", prio:" << tool->priority(); KoToolBase *tl = tool->createTool(controller->canvas()); if (tl) { d->uniqueToolIds.insert(tl, tool->uniqueId()); - tl->setObjectName(tool->id()); - - Q_FOREACH (QAction *action, tl->actions()) { - action->setEnabled(false); - } - } KoZoomTool *zoomTool = dynamic_cast(tl); if (zoomTool) { zoomTool->setCanvasController(controller); } return QPair(tool->id(), tl); } -// NOT IMPLEMENTED -void KoToolManager::updateToolShortcuts() -{ - // auto actionRegistry = KisActionRegistry::instance(); - // foreach (KoToolBase *t, allTools) { - // for (auto it = t->actions().constBegin(); - // it != t->actions().constEnd(); - // ++it;) { - // actionRegistry->updateShortcut(it.key(), it.value()); - // } - // } -} - void KoToolManager::initializeCurrentToolForCanvas() { d->postSwitchTool(false); } KoToolManager* KoToolManager::instance() { return s_instance; } QString KoToolManager::activeToolId() const { if (!d->canvasData) return QString(); return d->canvasData->activeToolId; } KoToolManager::Private *KoToolManager::priv() { return d; } /**** KoToolManager::Private ****/ KoToolManager::Private::Private(KoToolManager *qq) : q(qq), - canvasData(0), - layerExplicitlyDisabled(false) + canvasData(0), + layerExplicitlyDisabled(false) { } KoToolManager::Private::~Private() { qDeleteAll(tools); } // helper method. CanvasData *KoToolManager::Private::createCanvasData(KoCanvasController *controller, const KoInputDevice &device) { QHash toolsHash; Q_FOREACH (ToolHelper *tool, tools) { QPair toolPair = q->createTools(controller, tool); if (toolPair.second) { // only if a real tool was created toolsHash.insert(toolPair.first, toolPair.second); } } KoCreateShapesTool *createShapesTool = dynamic_cast(toolsHash.value(KoCreateShapesTool_ID)); KIS_ASSERT(createShapesTool); QString id = KoShapeRegistry::instance()->keys()[0]; createShapesTool->setShapeId(id); CanvasData *cd = new CanvasData(controller, device); cd->allTools = toolsHash; return cd; } void KoToolManager::Private::setup() { if (tools.size() > 0) return; KoShapeRegistry::instance(); KoToolRegistry *registry = KoToolRegistry::instance(); Q_FOREACH (const QString & id, registry->keys()) { ToolHelper *t = new ToolHelper(registry->value(id)); tools.append(t); } // connect to all tools so we can hear their button-clicks Q_FOREACH (ToolHelper *tool, tools) connect(tool, SIGNAL(toolActivated(ToolHelper*)), q, SLOT(toolActivated(ToolHelper*))); // load pluggable input devices KoInputDeviceHandlerRegistry::instance(); } void KoToolManager::Private::connectActiveTool() { if (canvasData->activeTool) { connect(canvasData->activeTool, SIGNAL(cursorChanged(QCursor)), q, SLOT(updateCursor(QCursor))); connect(canvasData->activeTool, SIGNAL(activateTool(QString)), q, SLOT(switchToolRequested(QString))); connect(canvasData->activeTool, SIGNAL(activateTemporary(QString)), q, SLOT(switchToolTemporaryRequested(QString))); connect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested())); connect(canvasData->activeTool, SIGNAL(statusTextChanged(QString)), q, SIGNAL(changedStatusText(QString))); } // we expect the tool to emit a cursor on activation. updateCursor(Qt::ForbiddenCursor); } void KoToolManager::Private::disconnectActiveTool() { if (canvasData->activeTool) { canvasData->deactivateToolActions(); // repaint the decorations before we deactivate the tool as it might deleted // data needed for the repaint emit q->aboutToChangeTool(canvasData->canvas); canvasData->activeTool->deactivate(); disconnect(canvasData->activeTool, SIGNAL(cursorChanged(QCursor)), q, SLOT(updateCursor(QCursor))); disconnect(canvasData->activeTool, SIGNAL(activateTool(QString)), q, SLOT(switchToolRequested(QString))); disconnect(canvasData->activeTool, SIGNAL(activateTemporary(QString)), q, SLOT(switchToolTemporaryRequested(QString))); disconnect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested())); disconnect(canvasData->activeTool, SIGNAL(statusTextChanged(QString)), q, SIGNAL(changedStatusText(QString))); } // emit a empty status text to clear status text from last active tool emit q->changedStatusText(QString()); } void KoToolManager::Private::switchTool(KoToolBase *tool, bool temporary) { Q_ASSERT(tool); if (canvasData == 0) return; if (canvasData->activeTool == tool && tool->toolId() != KoInteractionTool_ID) return; disconnectActiveTool(); canvasData->activeTool = tool; connectActiveTool(); postSwitchTool(temporary); } void KoToolManager::Private::switchTool(const QString &id, bool temporary) { Q_ASSERT(canvasData); if (!canvasData) return; if (canvasData->activeTool && temporary) canvasData->stack.push(canvasData->activeToolId); canvasData->activeToolId = id; KoToolBase *tool = canvasData->allTools.value(id); if (! tool) { return; } Q_FOREACH (ToolHelper *th, tools) { if (th->id() == id) { canvasData->activationShapeId = th->activationShapeId(); break; } } switchTool(tool, temporary); } void KoToolManager::Private::postSwitchTool(bool temporary) { #ifndef NDEBUG int canvasCount = 1; Q_FOREACH (QList list, canvasses) { bool first = true; Q_FOREACH (CanvasData *data, list) { if (first) { debugFlake << "Canvas" << canvasCount++; } debugFlake << " +- Tool:" << data->activeToolId << (data == canvasData ? " *" : ""); first = false; } } #endif Q_ASSERT(canvasData); if (!canvasData) return; KoToolBase::ToolActivation toolActivation; if (temporary) toolActivation = KoToolBase::TemporaryActivation; else toolActivation = KoToolBase::DefaultActivation; QSet shapesToOperateOn; if (canvasData->activeTool && canvasData->activeTool->canvas() && canvasData->activeTool->canvas()->shapeManager()) { KoSelection *selection = canvasData->activeTool->canvas()->shapeManager()->selection(); Q_ASSERT(selection); shapesToOperateOn = QSet::fromList(selection->selectedEditableShapesAndDelegates()); } if (canvasData->canvas->canvas()) { // Caller of postSwitchTool expect this to be called to update the selected tool updateToolForProxy(); canvasData->activeTool->activate(toolActivation, shapesToOperateOn); KoCanvasBase *canvas = canvasData->canvas->canvas(); canvas->updateInputMethodInfo(); } else { canvasData->activeTool->activate(toolActivation, shapesToOperateOn); } QList > optionWidgetList = canvasData->activeTool->optionWidgets(); if (optionWidgetList.empty()) { // no option widget. QWidget *toolWidget; QString title; Q_FOREACH (ToolHelper *tool, tools) { if (tool->id() == canvasData->activeTool->toolId()) { title = tool->toolTip(); break; } } toolWidget = canvasData->dummyToolWidget; if (toolWidget == 0) { toolWidget = new QWidget(); toolWidget->setObjectName("DummyToolWidget"); QVBoxLayout *layout = new QVBoxLayout(toolWidget); layout->setMargin(3); canvasData->dummyToolLabel = new QLabel(toolWidget); layout->addWidget(canvasData->dummyToolLabel); layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); toolWidget->setLayout(layout); canvasData->dummyToolWidget = toolWidget; } canvasData->dummyToolLabel->setText(i18n("Active tool: %1", title)); optionWidgetList.append(toolWidget); } // Activate the actions for the currently active tool canvasData->activateToolActions(); emit q->changedTool(canvasData->canvas, uniqueToolIds.value(canvasData->activeTool)); emit q->toolOptionWidgetsChanged(canvasData->canvas, optionWidgetList); } void KoToolManager::Private::switchCanvasData(CanvasData *cd) { Q_ASSERT(cd); KoCanvasBase *oldCanvas = 0; KoInputDevice oldInputDevice; if (canvasData) { oldCanvas = canvasData->canvas->canvas(); oldInputDevice = canvasData->inputDevice; if (canvasData->activeTool) { disconnectActiveTool(); } KoToolProxy *proxy = proxies.value(oldCanvas); Q_ASSERT(proxy); proxy->setActiveTool(0); } canvasData = cd; inputDevice = canvasData->inputDevice; if (canvasData->activeTool) { connectActiveTool(); postSwitchTool(false); } if (oldInputDevice != canvasData->inputDevice) { emit q->inputDeviceChanged(canvasData->inputDevice); } if (oldCanvas != canvasData->canvas->canvas()) { emit q->changedCanvas(canvasData->canvas->canvas()); } } void KoToolManager::Private::toolActivated(ToolHelper *tool) { Q_ASSERT(tool); Q_ASSERT(canvasData); if (!canvasData) return; KoToolBase *t = canvasData->allTools.value(tool->id()); Q_ASSERT(t); canvasData->activeToolId = tool->id(); canvasData->activationShapeId = tool->activationShapeId(); switchTool(t, false); } void KoToolManager::Private::detachCanvas(KoCanvasController *controller) { Q_ASSERT(controller); // check if we are removing the active canvas controller if (canvasData && canvasData->canvas == controller) { KoCanvasController *newCanvas = 0; // try to find another canvas controller beside the one we are removing Q_FOREACH (KoCanvasController* canvas, canvasses.keys()) { if (canvas != controller) { // yay found one newCanvas = canvas; break; } } if (newCanvas) { switchCanvasData(canvasses.value(newCanvas).first()); } else { emit q->toolOptionWidgetsChanged(controller, QList >()); // as a last resort just set a blank one canvasData = 0; } } KoToolProxy *proxy = proxies.value(controller->canvas()); if (proxy) proxy->setActiveTool(0); QList tools; Q_FOREACH (CanvasData *canvasData, canvasses.value(controller)) { Q_FOREACH (KoToolBase *tool, canvasData->allTools) { if (! tools.contains(tool)) { tools.append(tool); } } delete canvasData; } Q_FOREACH (KoToolBase *tool, tools) { uniqueToolIds.remove(tool); delete tool; } canvasses.remove(controller); emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0); } void KoToolManager::Private::attachCanvas(KoCanvasController *controller) { Q_ASSERT(controller); CanvasData *cd = createCanvasData(controller, KoInputDevice::mouse()); // switch to new canvas as the active one. switchCanvasData(cd); inputDevice = cd->inputDevice; QList canvasses_; canvasses_.append(cd); canvasses[controller] = canvasses_; KoToolProxy *tp = proxies[controller->canvas()]; if (tp) tp->priv()->setCanvasController(controller); if (cd->activeTool == 0) { // no active tool, so we activate the highest priority main tool int highestPriority = INT_MAX; ToolHelper * helper = 0; Q_FOREACH (ToolHelper * th, tools) { if (th->section() == KoToolFactoryBase::mainToolType()) { if (th->priority() < highestPriority) { highestPriority = qMin(highestPriority, th->priority()); helper = th; } } } if (helper) toolActivated(helper); } Connector *connector = new Connector(controller->canvas()->shapeManager()); connect(connector, SIGNAL(selectionChanged(QList)), q, SLOT(selectionChanged(QList))); connect(controller->canvas()->selectedShapesProxy(), SIGNAL(currentLayerChanged(const KoShapeLayer*)), q, SLOT(currentLayerChanged(const KoShapeLayer*))); emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0); } void KoToolManager::Private::movedFocus(QWidget *from, QWidget *to) { Q_UNUSED(from); // no canvas anyway or no focus set anyway? if (!canvasData || to == 0) { return; } // Check if this app is about QWidget-based KoCanvasControllerWidget canvasses // XXX: Focus handling for non-qwidget based canvases! KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast(canvasData->canvas); if (!canvasControllerWidget) { return; } // canvasWidget is set as focusproxy for KoCanvasControllerWidget, // so all focus checks are to be done against canvasWidget objects // focus returned to current canvas? if (to == canvasData->canvas->canvas()->canvasWidget()) { // nothing to do return; } // if the 'to' is one of our canvasWidgets, then switch. // for code simplicity the current canvas will be checked again, // but would have been caught already in the lines above, so no issue KoCanvasController *newCanvas = 0; Q_FOREACH (KoCanvasController* canvas, canvasses.keys()) { if (canvas->canvas()->canvasWidget() == to) { newCanvas = canvas; break; } } // none of our canvasWidgets got focus? if (newCanvas == 0) { return; } // switch to canvasdata matching inputdevice used last with this app instance Q_FOREACH (CanvasData *data, canvasses.value(newCanvas)) { if (data->inputDevice == inputDevice) { switchCanvasData(data); return; } } // if no such inputDevice for this canvas, then simply fallback to first one switchCanvasData(canvasses.value(newCanvas).first()); } void KoToolManager::Private::updateCursor(const QCursor &cursor) { Q_ASSERT(canvasData); Q_ASSERT(canvasData->canvas); Q_ASSERT(canvasData->canvas->canvas()); canvasData->canvas->canvas()->setCursor(cursor); } void KoToolManager::Private::selectionChanged(const QList &shapes) { QList types; Q_FOREACH (KoShape *shape, shapes) { QSet delegates = shape->toolDelegates(); if (delegates.isEmpty()) { // no delegates, just the orig shape delegates << shape; } foreach (KoShape *shape2, delegates) { Q_ASSERT(shape2); if (! types.contains(shape2->shapeId())) { types.append(shape2->shapeId()); } } } // check if there is still a shape selected the active tool can work on // there needs to be at least one shape for a tool without an activationShapeId // to work // if not change the current tool to the default tool const QStringList activationShapeIds = canvasData->activationShapeId.split(','); if (!(canvasData->activationShapeId.isNull() && shapes.size() > 0) - && !activationShapeIds.contains("flake/always") - && !activationShapeIds.contains("flake/edit")) { + && !activationShapeIds.contains("flake/always") + && !activationShapeIds.contains("flake/edit")) { bool currentToolWorks = false; foreach (const QString &type, types) { if (activationShapeIds.contains(type)) { currentToolWorks = true; break; } } if (!currentToolWorks) { switchTool(KoInteractionTool_ID, false); } } emit q->toolCodesSelected(types); } void KoToolManager::Private::currentLayerChanged(const KoShapeLayer *layer) { emit q->currentLayerChanged(canvasData->canvas, layer); layerExplicitlyDisabled = layer && !layer->isShapeEditable(); updateToolForProxy(); debugFlake << "Layer changed to" << layer << "explicitly disabled:" << layerExplicitlyDisabled; } void KoToolManager::Private::updateToolForProxy() { KoToolProxy *proxy = proxies.value(canvasData->canvas->canvas()); if(!proxy) return; bool canUseTool = !layerExplicitlyDisabled || canvasData->activationShapeId.endsWith(QLatin1String("/always")); proxy->setActiveTool(canUseTool ? canvasData->activeTool : 0); } void KoToolManager::Private::switchInputDevice(const KoInputDevice &device) { Q_ASSERT(canvasData); if (!canvasData) return; if (inputDevice == device) return; if (inputDevice.isMouse() && device.isMouse()) return; if (device.isMouse() && !inputDevice.isMouse()) { // we never switch back to mouse from a tablet input device, so the user can use the // mouse to edit the settings for a tool activated by a tablet. See bugs // https://bugs.kde.org/show_bug.cgi?id=283130 and https://bugs.kde.org/show_bug.cgi?id=285501. // We do continue to switch between tablet devices, thought. return; } QList items = canvasses[canvasData->canvas]; - // disable all actions for all tools in the all canvasdata objects for this canvas. - Q_FOREACH (CanvasData *cd, items) { - Q_FOREACH (KoToolBase* tool, cd->allTools) { - Q_FOREACH (QAction * action, tool->actions()) { - action->setEnabled(false); - } - } - } - // search for a canvasdata object for the current input device Q_FOREACH (CanvasData *cd, items) { if (cd->inputDevice == device) { switchCanvasData(cd); if (!canvasData->activeTool) { switchTool(KoInteractionTool_ID, false); } return; } } // still here? That means we need to create a new CanvasData instance with the current InputDevice. CanvasData *cd = createCanvasData(canvasData->canvas, device); // switch to new canvas as the active one. QString oldTool = canvasData->activeToolId; items.append(cd); canvasses[cd->canvas] = items; switchCanvasData(cd); q->switchToolRequested(oldTool); } void KoToolManager::Private::registerToolProxy(KoToolProxy *proxy, KoCanvasBase *canvas) { proxies.insert(canvas, proxy); Q_FOREACH (KoCanvasController *controller, canvasses.keys()) { if (controller->canvas() == canvas) { proxy->priv()->setCanvasController(controller); break; } } } //have to include this because of Q_PRIVATE_SLOT #include "moc_KoToolManager.cpp" diff --git a/libs/flake/KoToolManager.h b/libs/flake/KoToolManager.h index 417e11fdbd..f99dc52d34 100644 --- a/libs/flake/KoToolManager.h +++ b/libs/flake/KoToolManager.h @@ -1,338 +1,335 @@ /* This file is part of the KDE project * Copyright (c) 2005-2006 Boudewijn Rempt * Copyright (C) 2006, 2008 Thomas Zander * Copyright (C) 2006 Thorsten Zachmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KO_TOOL_MANAGER #define KO_TOOL_MANAGER #include "KoInputDevice.h" #include "kritaflake_export.h" #include #include class KoCanvasController; class KoShapeControllerBase; class KoToolFactoryBase; class KoCanvasBase; class KoToolBase; class KoCreateShapesTool; class KActionCollection; class KoShape; class KoInputDeviceHandlerEvent; class KoShapeLayer; class ToolHelper; class QKeySequence; class QCursor; /** * This class serves as a QAction-like control object for activation of a tool. * * It allows to implement a custom UI to control the activation of tools. * See KoToolBox & KoModeBox in the kowidgets library. * * KoToolAction objects are indirectly owned by the KoToolManager singleton * and live until the end of its lifetime. */ class KRITAFLAKE_EXPORT KoToolAction : public QObject { Q_OBJECT public: // toolHelper takes over ownership, and those live till the end of KoToolManager. explicit KoToolAction(ToolHelper *toolHelper); ~KoToolAction() override; public: QString id() const; ///< The id of the tool QString iconText() const; ///< The icontext of the tool QString toolTip() const; ///< The tooltip of the tool QString iconName() const; ///< The icon name of the tool QKeySequence shortcut() const; ///< The shortcut to activate the tool QString section() const; ///< The section the tool wants to be in. int priority() const; ///< Lower number (higher priority) means coming first in the section. int buttonGroupId() const; ///< A unique ID for this tool as passed by changedTool(), >= 0 QString visibilityCode() const; ///< This tool should become visible when we emit this string in toolCodesSelected() public Q_SLOTS: void trigger(); ///< Request the activation of the tool Q_SIGNALS: void changed(); ///< Emitted when a property changes (shortcut ATM) private: friend class ToolHelper; class Private; Private *const d; }; /** * This class manages the activation and deactivation of tools for * each input device. * * Managing the active tool and switching tool based on various variables. * * The state of the toolbox will be the same for all views in the process so practically * you can say we have one toolbox per application instance (process). Implementation * does not allow one widget to be in more then one view, so we just make sure the toolbox * is hidden in not-in-focus views. * * The ToolManager is a singleton and will manage all views in all applications that * are loaded in this process. This means you will have to register and unregister your view. * When creating your new view you should use a KoCanvasController() and register that * with the ToolManager like this: @code MyGuiWidget::MyGuiWidget() { m_canvasController = new KoCanvasController(this); m_canvasController->setCanvas(m_canvas); KoToolManager::instance()->addControllers(m_canvasController)); } MyGuiWidget::~MyGuiWidget() { KoToolManager::instance()->removeCanvasController(m_canvasController); } @endcode * * For a new view that extends KoView all you need to do is implement KoView::createToolBox() * * KoToolManager also keeps track of the current tool based on a complex set of conditions and heuristics: - there is one active tool per KoCanvasController (and there is one KoCanvasController per view, because this is a class with scrollbars and a zoomlevel and so on) - for every pointing device (determined by the unique id of tablet, or 0 for mice -- you may have more than one mouse attached, but Qt cannot distinguish between them, there is an associated tool. - depending on things like tablet leave/enter proximity, incoming mouse or tablet events and a little timer (that gets stopped when we know what is what), the active pointing device is determined, and the active tool is set accordingly. Nota bene: if you use KoToolManager and register your canvases with it you no longer have to manually implement methods to route mouse, tablet, key or wheel events to the active tool. In fact, it's no longer interesting to you which tool is active; you can safely route the paint event through KoToolProxy::paint(). (The reason the input events are handled completely by the toolmanager and the paint events not is that, generally speaking, it's okay if the tools get the input events first, but you want to paint your shapes or other canvas stuff first and only then paint the tool stuff.) */ class KRITAFLAKE_EXPORT KoToolManager : public QObject { Q_OBJECT public: KoToolManager(); /// Return the toolmanager singleton static KoToolManager* instance(); ~KoToolManager() override; /** * Register actions for switching to tools at the actionCollection parameter. * The actions will have the text / shortcut as stated by the toolFactory. * If the application calls this in their KoView extending class they will have all the benefits * from allowing this in the menus and to allow the use to configure the shortcuts used. * @param ac the actionCollection that will be the parent of the actions. * @param controller tools registered with this controller will have all their actions added as well. */ void registerToolActions(KActionCollection *ac, KoCanvasController *controller); /** * Register a new canvas controller * @param controller the view controller that this toolmanager will manage the tools for */ void addController(KoCanvasController *controller); /** * Remove a set of controllers * When the controller is no longer used it should be removed so all tools can be * deleted and stop eating memory. * @param controller the controller that is removed */ void removeCanvasController(KoCanvasController *controller); /** * Attempt to remove a controller. * This is automatically called when a controller's proxy object is deleted, and * it ensures that the controller is, in fact, removed, even if the creator forgot * to do so. * @param controller the proxy object of the controller to be removed */ Q_SLOT void attemptCanvasControllerRemoval(QObject *controller); /// @return the active canvas controller KoCanvasController *activeCanvasController() const; /** * Return the tool that is able to create shapes for this param canvas. * This is typically used by the KoShapeSelector to set which shape to create next. * @param canvas the canvas that is a child of a previously registered controller * who's tool you want. * @see addController() */ KoCreateShapesTool *shapeCreatorTool(KoCanvasBase *canvas) const; /** * Returns the tool for the given tool id. The tool may be 0 * @param canvas the canvas that is a child of a previously registered controller * who's tool you want. * @see addController() */ KoToolBase *toolById(KoCanvasBase *canvas, const QString &id) const; /// @return the currently active pointing device KoInputDevice currentInputDevice() const; /** * For the list of shapes find out which tool is the highest priority tool that can handle it. * @returns the toolId for the shapes. * @param shapes a list of shapes, a selection for example, that is used to look for the tool. */ QString preferredToolForSelection(const QList &shapes); /** * Returns the list of toolActions for the current tools. * @returns lists of toolActions for the current tools. */ QList toolActionList() const; - /// Update the internal shortcuts of each tool. (Activation shortcuts are exposed already.) - void updateToolShortcuts(); - /// Request tool activation for the given canvas controller void requestToolActivation(KoCanvasController *controller); /// Returns the toolId of the currently active tool QString activeToolId() const; void initializeCurrentToolForCanvas(); class Private; /** * \internal return the private object for the toolmanager. */ KoToolManager::Private *priv(); public Q_SLOTS: /** * Request switching tool * @param id the id of the tool */ void switchToolRequested(const QString &id); /** * Request change input device * @param id the id of the input device */ void switchInputDeviceRequested(const KoInputDevice &id); /** * Request for temporary switching the tools. * This switch can be later reverted with switchBackRequested(). * @param id the id of the tool * * @see switchBackRequested() */ void switchToolTemporaryRequested(const QString &id); /** * Switches back to the original tool after the temporary switch * has been done. It the user changed the tool manually on the way, * then it switches to the interaction tool */ void switchBackRequested(); Q_SIGNALS: /** * Emitted when a new tool is going to override the current tool * @param canvas the currently active canvas. */ void aboutToChangeTool(KoCanvasController *canvas); /** * Emitted when a new tool was selected or became active. * @param canvas the currently active canvas. * @param uniqueToolId a random but unique code for the new tool. */ void changedTool(KoCanvasController *canvas, int uniqueToolId); /** * Emitted after the selection changed to state which unique shape-types are now * in the selection. * @param canvas the currently active canvas. * @param types a list of string that are the shape types of the selected objects. */ void toolCodesSelected(const QList &types); /** * Emitted after the current layer changed either its properties or to a new layer. * @param canvas the currently active canvas. * @param layer the layer that is selected. */ void currentLayerChanged(const KoCanvasController *canvas, const KoShapeLayer *layer); /** * Every time a new input device gets used by a tool, this event is emitted. * @param device the new input device that the user picked up. */ void inputDeviceChanged(const KoInputDevice &device); /** * Emitted whenever the active canvas changed. * @param canvas the new activated canvas (might be 0) */ void changedCanvas(const KoCanvasBase *canvas); /** * Emitted whenever the active tool changes the status text. * @param statusText the new status text */ void changedStatusText(const QString &statusText); /** * emitted whenever a new tool is dynamically added for the given canvas */ void addedTool(KoToolAction *toolAction, KoCanvasController *canvas); /** * Emit the new tool option widgets to be used with this canvas. */ void toolOptionWidgetsChanged(KoCanvasController *controller, const QList > &widgets); private: KoToolManager(const KoToolManager&); KoToolManager operator=(const KoToolManager&); Q_PRIVATE_SLOT(d, void toolActivated(ToolHelper *tool)) Q_PRIVATE_SLOT(d, void detachCanvas(KoCanvasController *controller)) Q_PRIVATE_SLOT(d, void attachCanvas(KoCanvasController *controller)) Q_PRIVATE_SLOT(d, void movedFocus(QWidget *from, QWidget *to)) Q_PRIVATE_SLOT(d, void updateCursor(const QCursor &cursor)) Q_PRIVATE_SLOT(d, void selectionChanged(const QList &shapes)) Q_PRIVATE_SLOT(d, void currentLayerChanged(const KoShapeLayer *layer)) QPair createTools(KoCanvasController *controller, ToolHelper *tool); Private *const d; }; #endif diff --git a/libs/flake/KoToolProxy.cpp b/libs/flake/KoToolProxy.cpp index a3eebfc35d..3cd9d4c37b 100644 --- a/libs/flake/KoToolProxy.cpp +++ b/libs/flake/KoToolProxy.cpp @@ -1,484 +1,479 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007 Thomas Zander * Copyright (c) 2006-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 "KoToolProxy.h" #include "KoToolProxy_p.h" #include #include #include #include #include #include #include #include #include #include #include "KoToolBase.h" #include "KoPointerEvent.h" #include "KoInputDevice.h" #include "KoToolManager_p.h" #include "KoToolSelection.h" #include "KoCanvasBase.h" #include "KoCanvasController.h" #include "KoShapeManager.h" #include "KoSelection.h" #include "KoShapeLayer.h" #include "KoShapeRegistry.h" #include "KoShapeController.h" #include "KoOdf.h" #include "KoViewConverter.h" #include "KoShapeFactoryBase.h" KoToolProxyPrivate::KoToolProxyPrivate(KoToolProxy *p) : activeTool(0), tabletPressed(false), hasSelection(false), controller(0), parent(p) { scrollTimer.setInterval(100); mouseLeaveWorkaround = false; multiClickCount = 0; } void KoToolProxyPrivate::timeout() // Auto scroll the canvas { Q_ASSERT(controller); QPoint offset = QPoint(controller->canvasOffsetX(), controller->canvasOffsetY()); QPoint origin = controller->canvas()->documentOrigin(); QPoint viewPoint = widgetScrollPoint - origin - offset; QRectF mouseArea(viewPoint, QSizeF(10, 10)); mouseArea.setTopLeft(mouseArea.center()); controller->ensureVisible(mouseArea, true); QPoint newOffset = QPoint(controller->canvasOffsetX(), controller->canvasOffsetY()); QPoint moved = offset - newOffset; if (moved.isNull()) return; widgetScrollPoint += moved; QPointF documentPoint = parent->widgetToDocument(widgetScrollPoint); QMouseEvent event(QEvent::MouseMove, widgetScrollPoint, Qt::LeftButton, Qt::LeftButton, 0); KoPointerEvent ev(&event, documentPoint); activeTool->mouseMoveEvent(&ev); } void KoToolProxyPrivate::checkAutoScroll(const KoPointerEvent &event) { if (controller == 0) return; if (!activeTool) return; if (!activeTool->wantsAutoScroll()) return; if (!event.isAccepted()) return; if (!isToolPressed) return; if (event.buttons() != Qt::LeftButton) return; widgetScrollPoint = event.pos(); if (! scrollTimer.isActive()) scrollTimer.start(); } void KoToolProxyPrivate::selectionChanged(bool newSelection) { if (hasSelection == newSelection) return; hasSelection = newSelection; emit parent->selectionChanged(hasSelection); } bool KoToolProxyPrivate::isActiveLayerEditable() { if (!activeTool) return false; KoShapeManager * shapeManager = activeTool->canvas()->shapeManager(); KoShapeLayer * activeLayer = shapeManager->selection()->activeLayer(); if (activeLayer && !activeLayer->isShapeEditable()) return false; return true; } KoToolProxy::KoToolProxy(KoCanvasBase *canvas, QObject *parent) : QObject(parent), d(new KoToolProxyPrivate(this)) { KoToolManager::instance()->priv()->registerToolProxy(this, canvas); connect(&d->scrollTimer, SIGNAL(timeout()), this, SLOT(timeout())); } KoToolProxy::~KoToolProxy() { delete d; } void KoToolProxy::paint(QPainter &painter, const KoViewConverter &converter) { if (d->activeTool) d->activeTool->paint(painter, converter); } void KoToolProxy::repaintDecorations() { if (d->activeTool) d->activeTool->repaintDecorations(); } QPointF KoToolProxy::widgetToDocument(const QPointF &widgetPoint) const { QPoint offset = QPoint(d->controller->canvasOffsetX(), d->controller->canvasOffsetY()); QPoint origin = d->controller->canvas()->documentOrigin(); QPointF viewPoint = widgetPoint.toPoint() - QPointF(origin - offset); return d->controller->canvas()->viewConverter()->viewToDocument(viewPoint); } KoCanvasBase* KoToolProxy::canvas() const { return d->controller->canvas(); } void KoToolProxy::tabletEvent(QTabletEvent *event, const QPointF &point) { // We get these events exclusively from KisToolProxy - accept them event->accept(); KoInputDevice id(event->device(), event->pointerType(), event->uniqueId()); KoToolManager::instance()->priv()->switchInputDevice(id); KoPointerEvent ev(event, point); switch (event->type()) { case QEvent::TabletPress: ev.setTabletButton(Qt::LeftButton); if (!d->tabletPressed && d->activeTool) d->activeTool->mousePressEvent(&ev); d->tabletPressed = true; break; case QEvent::TabletRelease: ev.setTabletButton(Qt::LeftButton); d->tabletPressed = false; d->scrollTimer.stop(); if (d->activeTool) d->activeTool->mouseReleaseEvent(&ev); break; case QEvent::TabletMove: if (d->tabletPressed) ev.setTabletButton(Qt::LeftButton); if (d->activeTool) d->activeTool->mouseMoveEvent(&ev); d->checkAutoScroll(ev); default: ; // ignore the rest. } d->mouseLeaveWorkaround = true; } void KoToolProxy::mousePressEvent(KoPointerEvent *ev) { d->mouseLeaveWorkaround = false; KoInputDevice id; KoToolManager::instance()->priv()->switchInputDevice(id); d->mouseDownPoint = ev->pos(); // this tries to make sure another mouse press event doesn't happen // before a release event happens if (d->isToolPressed) { mouseReleaseEvent(ev); d->tabletPressed = false; d->scrollTimer.stop(); if (d->activeTool) { d->activeTool->mouseReleaseEvent(ev); } d->isToolPressed = false; return; } QPointF globalPoint = ev->globalPos(); if (d->multiClickGlobalPoint != globalPoint) { if (qAbs(globalPoint.x() - d->multiClickGlobalPoint.x()) > 5|| qAbs(globalPoint.y() - d->multiClickGlobalPoint.y()) > 5) { d->multiClickCount = 0; } d->multiClickGlobalPoint = globalPoint; } if (d->multiClickCount && d->multiClickTimeStamp.elapsed() < QApplication::doubleClickInterval()) { // One more multiclick; d->multiClickCount++; } else { d->multiClickTimeStamp.start(); d->multiClickCount = 1; } if (d->activeTool) { switch (d->multiClickCount) { case 0: case 1: d->activeTool->mousePressEvent(ev); break; case 2: d->activeTool->mouseDoubleClickEvent(ev); break; case 3: default: d->activeTool->mouseTripleClickEvent(ev); break; } } else { d->multiClickCount = 0; ev->ignore(); } d->isToolPressed = true; } void KoToolProxy::mousePressEvent(QMouseEvent *event, const QPointF &point) { KoPointerEvent ev(event, point); mousePressEvent(&ev); } void KoToolProxy::mouseDoubleClickEvent(QMouseEvent *event, const QPointF &point) { KoPointerEvent ev(event, point); mouseDoubleClickEvent(&ev); } void KoToolProxy::mouseDoubleClickEvent(KoPointerEvent *event) { // let us handle it as any other mousepress (where we then detect multi clicks mousePressEvent(event); } void KoToolProxy::mouseMoveEvent(QMouseEvent *event, const QPointF &point) { KoPointerEvent ev(event, point); mouseMoveEvent(&ev); } void KoToolProxy::mouseMoveEvent(KoPointerEvent *event) { if (d->mouseLeaveWorkaround) { d->mouseLeaveWorkaround = false; return; } KoInputDevice id; KoToolManager::instance()->priv()->switchInputDevice(id); if (d->activeTool == 0) { event->ignore(); return; } d->activeTool->mouseMoveEvent(event); d->checkAutoScroll(*event); } void KoToolProxy::mouseReleaseEvent(QMouseEvent *event, const QPointF &point) { KoPointerEvent ev(event, point); mouseReleaseEvent(&ev); } void KoToolProxy::mouseReleaseEvent(KoPointerEvent* event) { d->mouseLeaveWorkaround = false; KoInputDevice id; KoToolManager::instance()->priv()->switchInputDevice(id); d->scrollTimer.stop(); if (d->activeTool) { d->activeTool->mouseReleaseEvent(event); } else { event->ignore(); } d->isToolPressed = false; } void KoToolProxy::keyPressEvent(QKeyEvent *event) { if (d->activeTool) d->activeTool->keyPressEvent(event); else event->ignore(); } void KoToolProxy::keyReleaseEvent(QKeyEvent *event) { if (d->activeTool) d->activeTool->keyReleaseEvent(event); else event->ignore(); d->isToolPressed = false; } void KoToolProxy::explicitUserStrokeEndRequest() { if (d->activeTool) { d->activeTool->explicitUserStrokeEndRequest(); } } QVariant KoToolProxy::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const { if (d->activeTool) return d->activeTool->inputMethodQuery(query, converter); return QVariant(); } void KoToolProxy::inputMethodEvent(QInputMethodEvent *event) { if (d->activeTool) d->activeTool->inputMethodEvent(event); } QMenu *KoToolProxy::popupActionsMenu() { return d->activeTool ? d->activeTool->popupActionsMenu() : 0; } void KoToolProxy::setActiveTool(KoToolBase *tool) { if (d->activeTool) disconnect(d->activeTool, SIGNAL(selectionChanged(bool)), this, SLOT(selectionChanged(bool))); d->activeTool = tool; if (tool) { connect(d->activeTool, SIGNAL(selectionChanged(bool)), this, SLOT(selectionChanged(bool))); d->selectionChanged(hasSelection()); emit toolChanged(tool->toolId()); } } void KoToolProxyPrivate::setCanvasController(KoCanvasController *c) { controller = c; } -QHash KoToolProxy::actions() const -{ - return d->activeTool ? d->activeTool->actions() : QHash(); -} - bool KoToolProxy::hasSelection() const { return d->activeTool ? d->activeTool->hasSelection() : false; } void KoToolProxy::cut() { if (d->activeTool && d->isActiveLayerEditable()) d->activeTool->cut(); } void KoToolProxy::copy() const { if (d->activeTool) d->activeTool->copy(); } bool KoToolProxy::paste() { bool success = false; if (d->activeTool && d->isActiveLayerEditable()) { success = d->activeTool->paste(); } return success; } void KoToolProxy::dragMoveEvent(QDragMoveEvent *event, const QPointF &point) { if (d->activeTool) d->activeTool->dragMoveEvent(event, point); } void KoToolProxy::dragLeaveEvent(QDragLeaveEvent *event) { if (d->activeTool) d->activeTool->dragLeaveEvent(event); } void KoToolProxy::dropEvent(QDropEvent *event, const QPointF &point) { if (d->activeTool) d->activeTool->dropEvent(event, point); } void KoToolProxy::deleteSelection() { if (d->activeTool) d->activeTool->deleteSelection(); } void KoToolProxy::processEvent(QEvent *e) const { if(e->type()==QEvent::ShortcutOverride && d->activeTool && d->activeTool->isInTextMode() && (static_cast(e)->modifiers()==Qt::NoModifier || static_cast(e)->modifiers()==Qt::ShiftModifier)) { e->accept(); } } void KoToolProxy::requestUndoDuringStroke() { if (d->activeTool) { d->activeTool->requestUndoDuringStroke(); } } void KoToolProxy::requestStrokeCancellation() { if (d->activeTool) { d->activeTool->requestStrokeCancellation(); } } void KoToolProxy::requestStrokeEnd() { if (d->activeTool) { d->activeTool->requestStrokeEnd(); } } KoToolProxyPrivate *KoToolProxy::priv() { return d; } //have to include this because of Q_PRIVATE_SLOT #include "moc_KoToolProxy.cpp" diff --git a/libs/flake/KoToolProxy.h b/libs/flake/KoToolProxy.h index 2513ae8def..64999d31f5 100644 --- a/libs/flake/KoToolProxy.h +++ b/libs/flake/KoToolProxy.h @@ -1,190 +1,184 @@ /* This file is part of the KDE project * * Copyright (c) 2006, 2010 Boudewijn Rempt * Copyright (C) 2006-2010 Thomas Zander * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KO_TOOL_PROXY_H_ #define _KO_TOOL_PROXY_H_ #include "kritaflake_export.h" #include #include class QAction; class QAction; class QMouseEvent; class QKeyEvent; class QWheelEvent; class QTabletEvent; class KoCanvasBase; class KoViewConverter; class KoToolBase; class KoToolProxyPrivate; class QInputMethodEvent; class KoPointerEvent; class QDragMoveEvent; class QDragLeaveEvent; class QDropEvent; class QTouchEvent; class QPainter; class QPointF; class QMenu; /** * Tool proxy object which allows an application to address the current tool. * * Applications typically have a canvas and a canvas requires a tool for * the user to do anything. Since the flake system is responsible for handling * tools and also to change the active tool when needed we provide one class * that can be used by an application canvas to route all the native events too * which will transparently be routed to the active tool. Without the application * having to bother about which tool is active. */ class KRITAFLAKE_EXPORT KoToolProxy : public QObject { Q_OBJECT public: /** * Constructor * @param canvas Each canvas has 1 toolProxy. Pass the parent here. * @param parent a parent QObject for memory management purposes. */ explicit KoToolProxy(KoCanvasBase *canvas, QObject *parent = 0); ~KoToolProxy() override; /// Forwarded to the current KoToolBase void paint(QPainter &painter, const KoViewConverter &converter); /// Forwarded to the current KoToolBase void repaintDecorations(); /// Forwarded to the current KoToolBase void tabletEvent(QTabletEvent *event, const QPointF &point); /// Forwarded to the current KoToolBase void mousePressEvent(QMouseEvent *event, const QPointF &point); void mousePressEvent(KoPointerEvent *event); /// Forwarded to the current KoToolBase void mouseDoubleClickEvent(QMouseEvent *event, const QPointF &point); void mouseDoubleClickEvent(KoPointerEvent *event); /// Forwarded to the current KoToolBase void mouseMoveEvent(QMouseEvent *event, const QPointF &point); void mouseMoveEvent(KoPointerEvent *event); /// Forwarded to the current KoToolBase void mouseReleaseEvent(QMouseEvent *event, const QPointF &point); void mouseReleaseEvent(KoPointerEvent *event); /// Forwarded to the current KoToolBase void keyPressEvent(QKeyEvent *event); /// Forwarded to the current KoToolBase void keyReleaseEvent(QKeyEvent *event); /// Forwarded to the current KoToolBase void explicitUserStrokeEndRequest(); /// Forwarded to the current KoToolBase QVariant inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const; /// Forwarded to the current KoToolBase void inputMethodEvent(QInputMethodEvent *event); /// Forwarded to the current KoToolBase QMenu* popupActionsMenu(); /// Forwarded to the current KoToolBase void deleteSelection(); /// This method gives the proxy a chance to do things. for example it is need to have working singlekey /// shortcuts. call it from the canvas' event function and forward it to QWidget::event() later. void processEvent(QEvent *) const; - /** - * Retrieves the entire collection of actions for the active tool - * or an empty hash if there is no active tool yet. - */ - QHash actions() const; - /// returns true if the current tool holds a selection bool hasSelection() const; /// Forwarded to the current KoToolBase void cut(); /// Forwarded to the current KoToolBase void copy() const; /// Forwarded to the current KoToolBase bool paste(); /// Forwarded to the current KoToolBase void dragMoveEvent(QDragMoveEvent *event, const QPointF &point); /// Forwarded to the current KoToolBase void dragLeaveEvent(QDragLeaveEvent *event); /// Forwarded to the current KoToolBase void dropEvent(QDropEvent *event, const QPointF &point); /// Set the new active tool. virtual void setActiveTool(KoToolBase *tool); /// \internal KoToolProxyPrivate *priv(); protected Q_SLOTS: /// Forwarded to the current KoToolBase void requestUndoDuringStroke(); /// Forwarded to the current KoToolBase void requestStrokeCancellation(); /// Forwarded to the current KoToolBase void requestStrokeEnd(); Q_SIGNALS: /** * A tool can have a selection that is copy-able, this signal is emitted when that status changes. * @param hasSelection is true when the tool holds selected data. */ void selectionChanged(bool hasSelection); /** * Emitted every time a tool is changed. * @param toolId the id of the tool. * @see KoToolBase::toolId() */ void toolChanged(const QString &toolId); protected: virtual QPointF widgetToDocument(const QPointF &widgetPoint) const; KoCanvasBase* canvas() const; private: Q_PRIVATE_SLOT(d, void timeout()) Q_PRIVATE_SLOT(d, void selectionChanged(bool)) friend class KoToolProxyPrivate; KoToolProxyPrivate * const d; }; #endif // _KO_TOOL_PROXY_H_ diff --git a/libs/flake/tools/KoPathTool.cpp b/libs/flake/tools/KoPathTool.cpp index 07e62e7b15..20ba9d5955 100644 --- a/libs/flake/tools/KoPathTool.cpp +++ b/libs/flake/tools/KoPathTool.cpp @@ -1,1313 +1,1303 @@ /* This file is part of the KDE project * Copyright (C) 2006-2012 Jan Hambrecht * Copyright (C) 2006,2007 Thorsten Zachmann * Copyright (C) 2007, 2010 Thomas Zander * Copyright (C) 2007 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 "KoPathTool.h" #include "KoToolBase_p.h" #include "KoPathShape_p.h" #include "KoPathToolHandle.h" #include "KoCanvasBase.h" #include "KoShapeManager.h" #include "KoSelectedShapesProxy.h" #include "KoDocumentResourceManager.h" #include "KoViewConverter.h" #include "KoSelection.h" #include "KoPointerEvent.h" #include "commands/KoPathPointTypeCommand.h" #include "commands/KoPathPointInsertCommand.h" #include "commands/KoPathPointRemoveCommand.h" #include "commands/KoPathSegmentTypeCommand.h" #include "commands/KoPathBreakAtPointCommand.h" #include "commands/KoPathSegmentBreakCommand.h" #include "commands/KoParameterToPathCommand.h" #include "commands/KoSubpathJoinCommand.h" #include #include #include #include "KoParameterShape.h" #include #include "KoPathPoint.h" #include "KoPathPointRubberSelectStrategy.h" #include "KoPathSegmentChangeStrategy.h" #include "KoPathConnectionPointStrategy.h" #include "KoParameterChangeStrategy.h" #include "PathToolOptionWidget.h" #include "KoConnectionShape.h" #include "KoSnapGuide.h" #include "KoShapeController.h" #include "kis_action_registry.h" #include #include #include "kis_command_utils.h" #include "kis_pointer_utils.h" #include #include #include #include #include #include #include #include #include static const unsigned char needle_bits[] = { 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x60, 0x00, 0xc0, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x80, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x3e, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x00 }; static const unsigned char needle_move_bits[] = { 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x60, 0x00, 0xc0, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x80, 0x07, 0x10, 0x0f, 0x38, 0x1f, 0x54, 0x3e, 0xfe, 0x7e, 0x54, 0x7c, 0x38, 0x1c, 0x10, 0x18, 0x00, 0x00 }; // helper function to calculate the squared distance between two points qreal squaredDistance(const QPointF& p1, const QPointF &p2) { qreal dx = p1.x()-p2.x(); qreal dy = p1.y()-p2.y(); return dx*dx + dy*dy; } struct KoPathTool::PathSegment { PathSegment() : path(0), segmentStart(0), positionOnSegment(0) { } bool isValid() { return path && segmentStart; } KoPathShape *path; KoPathPoint *segmentStart; qreal positionOnSegment; }; KoPathTool::KoPathTool(KoCanvasBase *canvas) : KoToolBase(canvas) , m_pointSelection(this) , m_activeHandle(0) , m_handleRadius(3) , m_activeSegment(0) , m_currentStrategy(0) , m_activatedTemporarily(false) { - QActionGroup *points = new QActionGroup(this); + m_points = new QActionGroup(this); // m_pointTypeGroup->setExclusive(true); - KisActionRegistry *actionRegistry = KisActionRegistry::instance(); - m_actionPathPointCorner = actionRegistry->makeQAction("pathpoint-corner", this); - addAction("pathpoint-corner", m_actionPathPointCorner); + + m_actionPathPointCorner = action("pathpoint-corner"); m_actionPathPointCorner->setData(KoPathPointTypeCommand::Corner); - points->addAction(m_actionPathPointCorner); + m_points->addAction(m_actionPathPointCorner); - m_actionPathPointSmooth = actionRegistry->makeQAction("pathpoint-smooth", this); - addAction("pathpoint-smooth", m_actionPathPointSmooth); + m_actionPathPointSmooth = action("pathpoint-smooth"); m_actionPathPointSmooth->setData(KoPathPointTypeCommand::Smooth); - points->addAction(m_actionPathPointSmooth); + m_points->addAction(m_actionPathPointSmooth); - m_actionPathPointSymmetric = actionRegistry->makeQAction("pathpoint-symmetric", this); - addAction("pathpoint-symmetric", m_actionPathPointSymmetric); + m_actionPathPointSymmetric = action("pathpoint-symmetric"); m_actionPathPointSymmetric->setData(KoPathPointTypeCommand::Symmetric); - points->addAction(m_actionPathPointSymmetric); - - m_actionCurvePoint = actionRegistry->makeQAction("pathpoint-curve", this); - addAction("pathpoint-curve", m_actionCurvePoint); - connect(m_actionCurvePoint, SIGNAL(triggered()), this, SLOT(pointToCurve())); - - m_actionLinePoint = actionRegistry->makeQAction("pathpoint-line", this); - addAction("pathpoint-line", m_actionLinePoint); - connect(m_actionLinePoint, SIGNAL(triggered()), this, SLOT(pointToLine())); - - m_actionLineSegment = actionRegistry->makeQAction("pathsegment-line", this); - addAction("pathsegment-line", m_actionLineSegment); - connect(m_actionLineSegment, SIGNAL(triggered()), this, SLOT(segmentToLine())); - - m_actionCurveSegment = actionRegistry->makeQAction("pathsegment-curve", this); - addAction("pathsegment-curve", m_actionCurveSegment); - connect(m_actionCurveSegment, SIGNAL(triggered()), this, SLOT(segmentToCurve())); - - m_actionAddPoint = actionRegistry->makeQAction("pathpoint-insert", this); - addAction("pathpoint-insert", m_actionAddPoint); - connect(m_actionAddPoint, SIGNAL(triggered()), this, SLOT(insertPoints())); - - m_actionRemovePoint = actionRegistry->makeQAction("pathpoint-remove", this); - addAction("pathpoint-remove", m_actionRemovePoint); - connect(m_actionRemovePoint, SIGNAL(triggered()), this, SLOT(removePoints())); - - m_actionBreakPoint = actionRegistry->makeQAction("path-break-point", this); - addAction("path-break-point", m_actionBreakPoint); - connect(m_actionBreakPoint, SIGNAL(triggered()), this, SLOT(breakAtPoint())); - - m_actionBreakSegment = actionRegistry->makeQAction("path-break-segment", this); - addAction("path-break-segment", m_actionBreakSegment); - connect(m_actionBreakSegment, SIGNAL(triggered()), this, SLOT(breakAtSegment())); - - m_actionJoinSegment = actionRegistry->makeQAction("pathpoint-join", this); - addAction("pathpoint-join", m_actionJoinSegment); - connect(m_actionJoinSegment, SIGNAL(triggered()), this, SLOT(joinPoints())); - - m_actionMergePoints = actionRegistry->makeQAction("pathpoint-merge", this); - addAction("pathpoint-merge", m_actionMergePoints); - connect(m_actionMergePoints, SIGNAL(triggered()), this, SLOT(mergePoints())); - - m_actionConvertToPath = actionRegistry->makeQAction("convert-to-path", this); - addAction("convert-to-path", m_actionConvertToPath); - connect(m_actionConvertToPath, SIGNAL(triggered()), this, SLOT(convertToPath())); + m_points->addAction(m_actionPathPointSymmetric); + + m_actionCurvePoint = action("pathpoint-curve"); + m_actionLinePoint = action("pathpoint-line"); + m_actionLineSegment = action("pathsegment-line"); + m_actionCurveSegment = action("pathsegment-curve"); + m_actionAddPoint = action("pathpoint-insert"); + m_actionRemovePoint = action("pathpoint-remove"); + m_actionBreakPoint = action("path-break-point"); + m_actionBreakSegment = action("path-break-segment"); + m_actionJoinSegment = action("pathpoint-join"); + m_actionMergePoints = action("pathpoint-merge"); + m_actionConvertToPath = action("convert-to-path"); m_contextMenu.reset(new QMenu()); - - connect(points, SIGNAL(triggered(QAction*)), this, SLOT(pointTypeChanged(QAction*))); - connect(&m_pointSelection, SIGNAL(selectionChanged()), this, SLOT(pointSelectionChanged())); - QBitmap b = QBitmap::fromData(QSize(16, 16), needle_bits); QBitmap m = b.createHeuristicMask(false); m_selectCursor = QCursor(b, m, 2, 0); b = QBitmap::fromData(QSize(16, 16), needle_move_bits); m = b.createHeuristicMask(false); m_moveCursor = QCursor(b, m, 2, 0); } KoPathTool::~KoPathTool() { delete m_activeHandle; delete m_activeSegment; delete m_currentStrategy; } QList > KoPathTool::createOptionWidgets() { QList > list; PathToolOptionWidget * toolOptions = new PathToolOptionWidget(this); connect(this, SIGNAL(typeChanged(int)), toolOptions, SLOT(setSelectionType(int))); connect(this, SIGNAL(singleShapeChanged(KoPathShape*)), toolOptions, SLOT(setCurrentShape(KoPathShape*))); connect(toolOptions, SIGNAL(sigRequestUpdateActions()), this, SLOT(updateActions())); updateOptionsWidget(); toolOptions->setWindowTitle(i18n("Edit Shape")); list.append(toolOptions); return list; } void KoPathTool::pointTypeChanged(QAction *type) { Q_D(KoToolBase); if (m_pointSelection.hasSelection()) { QList selectedPoints = m_pointSelection.selectedPointsData(); KUndo2Command *initialConversionCommand = createPointToCurveCommand(selectedPoints); // conversion should happen before the c-tor // of KoPathPointTypeCommand is executed! if (initialConversionCommand) { initialConversionCommand->redo(); } KUndo2Command *command = new KoPathPointTypeCommand(selectedPoints, static_cast(type->data().toInt())); if (initialConversionCommand) { using namespace KisCommandUtils; CompositeCommand *parent = new CompositeCommand(); parent->setText(command->text()); parent->addCommand(new SkipFirstRedoWrapper(initialConversionCommand)); parent->addCommand(command); command = parent; } d->canvas->addCommand(command); } } void KoPathTool::insertPoints() { Q_D(KoToolBase); QList segments(m_pointSelection.selectedSegmentsData()); if (segments.size() == 1) { qreal positionInSegment = 0.5; if (m_activeSegment && m_activeSegment->isValid()) { positionInSegment = m_activeSegment->positionOnSegment; } KoPathPointInsertCommand *cmd = new KoPathPointInsertCommand(segments, positionInSegment); d->canvas->addCommand(cmd); // TODO: this construction is dangerous. The canvas can remove the command right after // it has been added to it! m_pointSelection.clear(); foreach (KoPathPoint * p, cmd->insertedPoints()) { m_pointSelection.add(p, false); } } } void KoPathTool::removePoints() { Q_D(KoToolBase); if (m_pointSelection.size() > 0) { KUndo2Command *cmd = KoPathPointRemoveCommand::createCommand(m_pointSelection.selectedPointsData(), d->canvas->shapeController()); PointHandle *pointHandle = dynamic_cast(m_activeHandle); if (pointHandle && m_pointSelection.contains(pointHandle->activePoint())) { delete m_activeHandle; m_activeHandle = 0; } clearActivePointSelectionReferences(); d->canvas->addCommand(cmd); } } void KoPathTool::pointToLine() { Q_D(KoToolBase); if (m_pointSelection.hasSelection()) { QList selectedPoints = m_pointSelection.selectedPointsData(); QList pointToChange; QList::const_iterator it(selectedPoints.constBegin()); for (; it != selectedPoints.constEnd(); ++it) { KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex); if (point && (point->activeControlPoint1() || point->activeControlPoint2())) pointToChange.append(*it); } if (! pointToChange.isEmpty()) { d->canvas->addCommand(new KoPathPointTypeCommand(pointToChange, KoPathPointTypeCommand::Line)); } } } void KoPathTool::pointToCurve() { Q_D(KoToolBase); if (m_pointSelection.hasSelection()) { QList selectedPoints = m_pointSelection.selectedPointsData(); KUndo2Command *command = createPointToCurveCommand(selectedPoints); if (command) { d->canvas->addCommand(command); } } } KUndo2Command* KoPathTool::createPointToCurveCommand(const QList &points) { KUndo2Command *command = 0; QList pointToChange; QList::const_iterator it(points.constBegin()); for (; it != points.constEnd(); ++it) { KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex); if (point && (! point->activeControlPoint1() || ! point->activeControlPoint2())) pointToChange.append(*it); } if (!pointToChange.isEmpty()) { command = new KoPathPointTypeCommand(pointToChange, KoPathPointTypeCommand::Curve); } return command; } void KoPathTool::segmentToLine() { Q_D(KoToolBase); if (m_pointSelection.size() > 1) { QList segments(m_pointSelection.selectedSegmentsData()); if (segments.size() > 0) { d->canvas->addCommand(new KoPathSegmentTypeCommand(segments, KoPathSegmentTypeCommand::Line)); } } } void KoPathTool::segmentToCurve() { Q_D(KoToolBase); if (m_pointSelection.size() > 1) { QList segments(m_pointSelection.selectedSegmentsData()); if (segments.size() > 0) { d->canvas->addCommand(new KoPathSegmentTypeCommand(segments, KoPathSegmentTypeCommand::Curve)); } } } void KoPathTool::convertToPath() { Q_D(KoToolBase); KoSelection *selection = canvas()->selectedShapesProxy()->selection(); QList parameterShapes; Q_FOREACH (KoShape *shape, m_pointSelection.selectedShapes()) { KoParameterShape * parameteric = dynamic_cast(shape); if (parameteric && parameteric->isParametricShape()) { parameterShapes.append(parameteric); } } if (!parameterShapes.isEmpty()) { d->canvas->addCommand(new KoParameterToPathCommand(parameterShapes)); } QList textShapes; Q_FOREACH (KoShape *shape, selection->selectedEditableShapes()) { if (KoSvgTextShape *text = dynamic_cast(shape)) { textShapes.append(text); } } if (!textShapes.isEmpty()) { KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Convert to Path")); // TODO: reuse the text from KoParameterToPathCommand const QList oldSelectedShapes = implicitCastList(textShapes); new KoKeepShapesSelectedCommand(oldSelectedShapes, {}, canvas()->selectedShapesProxy(), KisCommandUtils::FlipFlopCommand::State::INITIALIZING, cmd); QList newSelectedShapes; Q_FOREACH (KoSvgTextShape *shape, textShapes) { const QPainterPath path = shape->textOutline(); if (path.isEmpty()) continue; KoPathShape *pathShape = KoPathShape::createShapeFromPainterPath(path); pathShape->setBackground(shape->background()); pathShape->setStroke(shape->stroke()); pathShape->setZIndex(shape->zIndex()); pathShape->setTransformation(shape->transformation()); KoShapeContainer *parent = shape->parent(); canvas()->shapeController()->addShapeDirect(pathShape, parent, cmd); newSelectedShapes << pathShape; } canvas()->shapeController()->removeShapes(oldSelectedShapes, cmd); new KoKeepShapesSelectedCommand({}, newSelectedShapes, canvas()->selectedShapesProxy(), KisCommandUtils::FlipFlopCommand::State::FINALIZING, cmd); canvas()->addCommand(cmd); } updateOptionsWidget(); } namespace { bool checkCanJoinToPoints(const KoPathPointData & pd1, const KoPathPointData & pd2) { const KoPathPointIndex & index1 = pd1.pointIndex; const KoPathPointIndex & index2 = pd2.pointIndex; KoPathShape *path1 = pd1.pathShape; KoPathShape *path2 = pd2.pathShape; // check if subpaths are already closed if (path1->isClosedSubpath(index1.first) || path2->isClosedSubpath(index2.first)) return false; // check if first point is an endpoint if (index1.second != 0 && index1.second != path1->subpathPointCount(index1.first)-1) return false; // check if second point is an endpoint if (index2.second != 0 && index2.second != path2->subpathPointCount(index2.first)-1) return false; return true; } } void KoPathTool::mergePointsImpl(bool doJoin) { Q_D(KoToolBase); if (m_pointSelection.size() != 2) return; QList pointData = m_pointSelection.selectedPointsData(); if (pointData.size() != 2) return; const KoPathPointData & pd1 = pointData.at(0); const KoPathPointData & pd2 = pointData.at(1); if (!checkCanJoinToPoints(pd1, pd2)) { return; } clearActivePointSelectionReferences(); KUndo2Command *cmd = 0; if (doJoin) { cmd = new KoMultiPathPointJoinCommand(pd1, pd2, d->canvas->shapeController()->documentBase(), d->canvas->shapeManager()->selection()); } else { cmd = new KoMultiPathPointMergeCommand(pd1, pd2, d->canvas->shapeController()->documentBase(), d->canvas->shapeManager()->selection()); } d->canvas->addCommand(cmd); } void KoPathTool::joinPoints() { mergePointsImpl(true); } void KoPathTool::mergePoints() { mergePointsImpl(false); } void KoPathTool::breakAtPoint() { Q_D(KoToolBase); if (m_pointSelection.hasSelection()) { d->canvas->addCommand(new KoPathBreakAtPointCommand(m_pointSelection.selectedPointsData())); } } void KoPathTool::breakAtSegment() { Q_D(KoToolBase); // only try to break a segment when 2 points of the same object are selected if (m_pointSelection.objectCount() == 1 && m_pointSelection.size() == 2) { QList segments(m_pointSelection.selectedSegmentsData()); if (segments.size() == 1) { d->canvas->addCommand(new KoPathSegmentBreakCommand(segments.at(0))); } } } void KoPathTool::paint(QPainter &painter, const KoViewConverter &converter) { Q_D(KoToolBase); Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) { KisHandlePainterHelper helper = KoShape::createHandlePainterHelper(&painter, shape, converter, m_handleRadius); helper.setHandleStyle(KisHandleStyle::primarySelection()); KoParameterShape * parameterShape = dynamic_cast(shape); if (parameterShape && parameterShape->isParametricShape()) { parameterShape->paintHandles(helper); } else { shape->paintPoints(helper); } if (!shape->stroke() || !shape->stroke()->isVisible()) { helper.setHandleStyle(KisHandleStyle::secondarySelection()); helper.drawPath(shape->outline()); } } if (m_currentStrategy) { painter.save(); m_currentStrategy->paint(painter, converter); painter.restore(); } m_pointSelection.paint(painter, converter, m_handleRadius); if (m_activeHandle) { if (m_activeHandle->check(m_pointSelection.selectedShapes())) { m_activeHandle->paint(painter, converter, m_handleRadius); } else { delete m_activeHandle; m_activeHandle = 0; } } else if (m_activeSegment && m_activeSegment->isValid()) { KoPathShape *shape = m_activeSegment->path; // if the stroke is invisible, then we already painted the outline of the shape! if (shape->stroke() && shape->stroke()->isVisible()) { KoPathPointIndex index = shape->pathPointIndex(m_activeSegment->segmentStart); KoPathSegment segment = shape->segmentByIndex(index).toCubic(); KIS_SAFE_ASSERT_RECOVER_RETURN(segment.isValid()); KisHandlePainterHelper helper = KoShape::createHandlePainterHelper(&painter, shape, converter, m_handleRadius); helper.setHandleStyle(KisHandleStyle::secondarySelection()); QPainterPath path; path.moveTo(segment.first()->point()); path.cubicTo(segment.first()->controlPoint2(), segment.second()->controlPoint1(), segment.second()->point()); helper.drawPath(path); } } if (m_currentStrategy) { painter.save(); KoShape::applyConversion(painter, converter); d->canvas->snapGuide()->paint(painter, converter); painter.restore(); } } void KoPathTool::repaintDecorations() { Q_FOREACH (KoShape *shape, m_pointSelection.selectedShapes()) { repaint(shape->boundingRect()); } m_pointSelection.repaint(); updateOptionsWidget(); } void KoPathTool::mousePressEvent(KoPointerEvent *event) { // we are moving if we hit a point and use the left mouse button event->ignore(); if (m_activeHandle) { m_currentStrategy = m_activeHandle->handleMousePress(event); event->accept(); } else { if (event->button() & Qt::LeftButton) { // check if we hit a path segment if (m_activeSegment && m_activeSegment->isValid()) { KoPathShape *shape = m_activeSegment->path; KoPathPointIndex index = shape->pathPointIndex(m_activeSegment->segmentStart); KoPathSegment segment = shape->segmentByIndex(index); m_pointSelection.add(segment.first(), !(event->modifiers() & Qt::ShiftModifier)); m_pointSelection.add(segment.second(), false); KoPathPointData data(shape, index); m_currentStrategy = new KoPathSegmentChangeStrategy(this, event->point, data, m_activeSegment->positionOnSegment); event->accept(); } else { KoShapeManager *shapeManager = canvas()->shapeManager(); KoSelection *selection = shapeManager->selection(); KoShape *shape = shapeManager->shapeAt(event->point, KoFlake::ShapeOnTop); if (shape && !selection->isSelected(shape)) { if (!(event->modifiers() & Qt::ShiftModifier)) { selection->deselectAll(); } selection->select(shape); } else { KIS_ASSERT_RECOVER_RETURN(m_currentStrategy == 0); m_currentStrategy = new KoPathPointRubberSelectStrategy(this, event->point); event->accept(); } } } } } void KoPathTool::mouseMoveEvent(KoPointerEvent *event) { if (event->button() & Qt::RightButton) return; if (m_currentStrategy) { m_lastPoint = event->point; m_currentStrategy->handleMouseMove(event->point, event->modifiers()); // repaint new handle positions m_pointSelection.repaint(); if (m_activeHandle) { m_activeHandle->repaint(); } if (m_activeSegment) { repaintSegment(m_activeSegment); } return; } if (m_activeSegment) { KoPathPointIndex index = m_activeSegment->path->pathPointIndex(m_activeSegment->segmentStart); KoPathSegment segment = m_activeSegment->path->segmentByIndex(index); repaint(segment.boundingRect()); delete m_activeSegment; m_activeSegment = 0; } Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) { QRectF roi = handleGrabRect(shape->documentToShape(event->point)); KoParameterShape * parameterShape = dynamic_cast(shape); if (parameterShape && parameterShape->isParametricShape()) { int handleId = parameterShape->handleIdAt(roi); if (handleId != -1) { useCursor(m_moveCursor); emit statusTextChanged(i18n("Drag to move handle.")); if (m_activeHandle) m_activeHandle->repaint(); delete m_activeHandle; if (KoConnectionShape * connectionShape = dynamic_cast(parameterShape)) { //debugFlake << "handleId" << handleId; m_activeHandle = new ConnectionHandle(this, connectionShape, handleId); m_activeHandle->repaint(); return; } else { //debugFlake << "handleId" << handleId; m_activeHandle = new ParameterHandle(this, parameterShape, handleId); m_activeHandle->repaint(); return; } } } else { QList points = shape->pointsAt(roi); if (! points.empty()) { // find the nearest control point from all points within the roi KoPathPoint * bestPoint = 0; KoPathPoint::PointType bestPointType = KoPathPoint::Node; qreal minDistance = HUGE_VAL; Q_FOREACH (KoPathPoint *p, points) { // the node point must be hit if the point is not selected yet if (! m_pointSelection.contains(p) && ! roi.contains(p->point())) continue; // check for the control points first as otherwise it is no longer // possible to change the control points when they are the same as the point if (p->activeControlPoint1() && roi.contains(p->controlPoint1())) { qreal dist = squaredDistance(roi.center(), p->controlPoint1()); if (dist < minDistance) { bestPoint = p; bestPointType = KoPathPoint::ControlPoint1; minDistance = dist; } } if (p->activeControlPoint2() && roi.contains(p->controlPoint2())) { qreal dist = squaredDistance(roi.center(), p->controlPoint2()); if (dist < minDistance) { bestPoint = p; bestPointType = KoPathPoint::ControlPoint2; minDistance = dist; } } // check the node point at last qreal dist = squaredDistance(roi.center(), p->point()); if (dist < minDistance) { bestPoint = p; bestPointType = KoPathPoint::Node; minDistance = dist; } } if (! bestPoint) return; useCursor(m_moveCursor); if (bestPointType == KoPathPoint::Node) emit statusTextChanged(i18n("Drag to move point. Shift click to change point type.")); else emit statusTextChanged(i18n("Drag to move control point.")); PointHandle *prev = dynamic_cast(m_activeHandle); if (prev && prev->activePoint() == bestPoint && prev->activePointType() == bestPointType) return; // no change; if (m_activeHandle) m_activeHandle->repaint(); delete m_activeHandle; m_activeHandle = new PointHandle(this, bestPoint, bestPointType); m_activeHandle->repaint(); return; } } } useCursor(m_selectCursor); if (m_activeHandle) { m_activeHandle->repaint(); } delete m_activeHandle; m_activeHandle = 0; PathSegment *hoveredSegment = segmentAtPoint(event->point); if(hoveredSegment) { useCursor(Qt::PointingHandCursor); emit statusTextChanged(i18n("Drag to change curve directly. Double click to insert new path point.")); m_activeSegment = hoveredSegment; repaintSegment(m_activeSegment); } else { uint selectedPointCount = m_pointSelection.size(); if (selectedPointCount == 0) emit statusTextChanged(QString()); else if (selectedPointCount == 1) emit statusTextChanged(i18n("Press B to break path at selected point.")); else emit statusTextChanged(i18n("Press B to break path at selected segments.")); } } void KoPathTool::repaintSegment(PathSegment *pathSegment) { if (!pathSegment || !pathSegment->isValid()) return; KoPathPointIndex index = pathSegment->path->pathPointIndex(pathSegment->segmentStart); KoPathSegment segment = pathSegment->path->segmentByIndex(index); repaint(segment.boundingRect()); } void KoPathTool::mouseReleaseEvent(KoPointerEvent *event) { Q_D(KoToolBase); if (m_currentStrategy) { const bool hadNoSelection = !m_pointSelection.hasSelection(); m_currentStrategy->finishInteraction(event->modifiers()); KUndo2Command *command = m_currentStrategy->createCommand(); if (command) d->canvas->addCommand(command); if (hadNoSelection && dynamic_cast(m_currentStrategy) && !m_pointSelection.hasSelection()) { // the click didn't do anything at all. Allow it to be used by others. event->ignore(); } delete m_currentStrategy; m_currentStrategy = 0; } } void KoPathTool::keyPressEvent(QKeyEvent *event) { if (m_currentStrategy) { switch (event->key()) { case Qt::Key_Control: case Qt::Key_Alt: case Qt::Key_Shift: case Qt::Key_Meta: if (! event->isAutoRepeat()) { m_currentStrategy->handleMouseMove(m_lastPoint, event->modifiers()); } break; case Qt::Key_Escape: m_currentStrategy->cancelInteraction(); delete m_currentStrategy; m_currentStrategy = 0; break; default: event->ignore(); return; } } else { switch (event->key()) { #ifndef NDEBUG case Qt::Key_D: if (m_pointSelection.objectCount() == 1) { QList selectedPoints = m_pointSelection.selectedPointsData(); KoPathShapePrivate *p = static_cast(selectedPoints[0].pathShape->priv()); p->debugPath(); } break; #endif case Qt::Key_B: if (m_pointSelection.size() == 1) breakAtPoint(); else if (m_pointSelection.size() >= 2) breakAtSegment(); break; default: event->ignore(); return; } } event->accept(); } void KoPathTool::keyReleaseEvent(QKeyEvent *event) { if (m_currentStrategy) { switch (event->key()) { case Qt::Key_Control: case Qt::Key_Alt: case Qt::Key_Shift: case Qt::Key_Meta: if (! event->isAutoRepeat()) { m_currentStrategy->handleMouseMove(m_lastPoint, Qt::NoModifier); } break; default: break; } } event->accept(); } void KoPathTool::mouseDoubleClickEvent(KoPointerEvent *event) { Q_D(KoToolBase); event->ignore(); // check if we are doing something else at the moment if (m_currentStrategy) return; if (!m_activeHandle && m_activeSegment && m_activeSegment->isValid()) { QList segments; segments.append( KoPathPointData(m_activeSegment->path, m_activeSegment->path->pathPointIndex(m_activeSegment->segmentStart))); KoPathPointInsertCommand *cmd = new KoPathPointInsertCommand(segments, m_activeSegment->positionOnSegment); d->canvas->addCommand(cmd); m_pointSelection.clear(); foreach (KoPathPoint * p, cmd->insertedPoints()) { m_pointSelection.add(p, false); } updateActions(); event->accept(); } else if (!m_activeHandle && !m_activeSegment && m_activatedTemporarily) { emit done(); event->accept(); } else if (!m_activeHandle && !m_activeSegment) { KoShapeManager *shapeManager = canvas()->shapeManager(); KoSelection *selection = shapeManager->selection(); selection->deselectAll(); event->accept(); } } KoPathTool::PathSegment* KoPathTool::segmentAtPoint(const QPointF &point) { // the max allowed distance from a segment const QRectF grabRoi = handleGrabRect(point); const qreal distanceThreshold = 0.5 * KisAlgebra2D::maxDimension(grabRoi); QScopedPointer segment(new PathSegment); Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) { KoParameterShape * parameterShape = dynamic_cast(shape); if (parameterShape && parameterShape->isParametricShape()) continue; // convert document point to shape coordinates const QPointF p = shape->documentToShape(point); // our region of interest, i.e. a region around our mouse position const QRectF roi = shape->documentToShape(grabRoi); qreal minDistance = std::numeric_limits::max(); // check all segments of this shape which intersect the region of interest const QList segments = shape->segmentsAt(roi); foreach (const KoPathSegment &s, segments) { const qreal nearestPointParam = s.nearestPoint(p); const QPointF nearestPoint = s.pointAt(nearestPointParam); const qreal distance = kisDistance(p, nearestPoint); // are we within the allowed distance ? if (distance > distanceThreshold) continue; // are we closer to the last closest point ? if (distance < minDistance) { segment->path = shape; segment->segmentStart = s.first(); segment->positionOnSegment = nearestPointParam; } } } if (!segment->isValid()) { segment.reset(); } return segment.take(); } void KoPathTool::activate(ToolActivation activation, const QSet &shapes) { KoToolBase::activate(activation, shapes); Q_D(KoToolBase); m_activatedTemporarily = activation == TemporaryActivation; // retrieve the actual global handle radius m_handleRadius = handleRadius(); d->canvas->snapGuide()->reset(); useCursor(m_selectCursor); m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged())); m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), this, SLOT(updateActions())); m_shapeFillResourceConnector.connectToCanvas(d->canvas); initializeWithShapes(shapes.toList()); + + connect(m_actionCurvePoint, SIGNAL(triggered()), this, SLOT(pointToCurve()), Qt::UniqueConnection); + connect(m_actionLinePoint, SIGNAL(triggered()), this, SLOT(pointToLine()), Qt::UniqueConnection); + connect(m_actionLineSegment, SIGNAL(triggered()), this, SLOT(segmentToLine()), Qt::UniqueConnection); + connect(m_actionCurveSegment, SIGNAL(triggered()), this, SLOT(segmentToCurve()), Qt::UniqueConnection); + connect(m_actionAddPoint, SIGNAL(triggered()), this, SLOT(insertPoints()), Qt::UniqueConnection); + connect(m_actionRemovePoint, SIGNAL(triggered()), this, SLOT(removePoints()), Qt::UniqueConnection); + connect(m_actionBreakPoint, SIGNAL(triggered()), this, SLOT(breakAtPoint()), Qt::UniqueConnection); + connect(m_actionBreakSegment, SIGNAL(triggered()), this, SLOT(breakAtSegment()), Qt::UniqueConnection); + connect(m_actionJoinSegment, SIGNAL(triggered()), this, SLOT(joinPoints()), Qt::UniqueConnection); + connect(m_actionMergePoints, SIGNAL(triggered()), this, SLOT(mergePoints()), Qt::UniqueConnection); + connect(m_actionConvertToPath, SIGNAL(triggered()), this, SLOT(convertToPath()), Qt::UniqueConnection); + connect(m_points, SIGNAL(triggered(QAction*)), this, SLOT(pointTypeChanged(QAction*)), Qt::UniqueConnection); + connect(&m_pointSelection, SIGNAL(selectionChanged()), this, SLOT(pointSelectionChanged()), Qt::UniqueConnection); + } void KoPathTool::slotSelectionChanged() { Q_D(KoToolBase); QList shapes = d->canvas->selectedShapesProxy()->selection()->selectedEditableShapesAndDelegates(); initializeWithShapes(shapes); } void KoPathTool::notifyPathPointsChanged(KoPathShape *shape) { Q_UNUSED(shape); // active handle and selection might have already become invalid, so just // delete them without dereferencing anything... delete m_activeHandle; m_activeHandle = 0; delete m_activeSegment; m_activeSegment = 0; } void KoPathTool::clearActivePointSelectionReferences() { delete m_activeHandle; m_activeHandle = 0; delete m_activeSegment; m_activeSegment = 0; m_pointSelection.clear(); } void KoPathTool::initializeWithShapes(const QList shapes) { QList selectedShapes; Q_FOREACH (KoShape *shape, shapes) { KoPathShape *pathShape = dynamic_cast(shape); if (pathShape && pathShape->isShapeEditable()) { selectedShapes.append(pathShape); } } const QRectF oldBoundingRect = KoShape::boundingRect(implicitCastList(m_pointSelection.selectedShapes())); if (selectedShapes != m_pointSelection.selectedShapes()) { clearActivePointSelectionReferences(); m_pointSelection.setSelectedShapes(selectedShapes); repaintDecorations(); } Q_FOREACH (KoPathShape *shape, selectedShapes) { // as the tool is just in activation repaintDecorations does not yet get called // so we need to use repaint of the tool and it is only needed to repaint the // current canvas repaint(shape->boundingRect()); } repaint(oldBoundingRect); updateOptionsWidget(); updateActions(); } void KoPathTool::updateOptionsWidget() { PathToolOptionWidget::Types type; QList selectedShapes = m_pointSelection.selectedShapes(); Q_FOREACH (KoPathShape *shape, selectedShapes) { KoParameterShape * parameterShape = dynamic_cast(shape); type |= parameterShape && parameterShape->isParametricShape() ? PathToolOptionWidget::ParametricShape : PathToolOptionWidget::PlainPath; } emit singleShapeChanged(selectedShapes.size() == 1 ? selectedShapes.first() : 0); emit typeChanged(type); } void KoPathTool::updateActions() { QList pointData = m_pointSelection.selectedPointsData(); bool canBreakAtPoint = false; bool hasNonSmoothPoints = false; bool hasNonSymmetricPoints = false; bool hasNonSplitPoints = false; bool hasNonLinePoints = false; bool hasNonCurvePoints = false; bool canJoinSubpaths = false; if (!pointData.isEmpty()) { Q_FOREACH (const KoPathPointData &pd, pointData) { const int subpathIndex = pd.pointIndex.first; const int pointIndex = pd.pointIndex.second; canBreakAtPoint |= pd.pathShape->isClosedSubpath(subpathIndex) || (pointIndex > 0 && pointIndex < pd.pathShape->subpathPointCount(subpathIndex) - 1); KoPathPoint *point = pd.pathShape->pointByIndex(pd.pointIndex); hasNonSmoothPoints |= !(point->properties() & KoPathPoint::IsSmooth); hasNonSymmetricPoints |= !(point->properties() & KoPathPoint::IsSymmetric); hasNonSplitPoints |= point->properties() & KoPathPoint::IsSymmetric || point->properties() & KoPathPoint::IsSmooth; hasNonLinePoints |= point->activeControlPoint1() || point->activeControlPoint2(); hasNonCurvePoints |= !point->activeControlPoint1() && !point->activeControlPoint2(); } if (pointData.size() == 2) { const KoPathPointData & pd1 = pointData.at(0); const KoPathPointData & pd2 = pointData.at(1); canJoinSubpaths = checkCanJoinToPoints(pd1, pd2); } } m_actionPathPointCorner->setEnabled(hasNonSplitPoints); m_actionPathPointSmooth->setEnabled(hasNonSmoothPoints); m_actionPathPointSymmetric->setEnabled(hasNonSymmetricPoints); m_actionRemovePoint->setEnabled(!pointData.isEmpty()); m_actionBreakPoint->setEnabled(canBreakAtPoint); m_actionCurvePoint->setEnabled(hasNonCurvePoints); m_actionLinePoint->setEnabled(hasNonLinePoints); m_actionJoinSegment->setEnabled(canJoinSubpaths); m_actionMergePoints->setEnabled(canJoinSubpaths); QList segments(m_pointSelection.selectedSegmentsData()); bool canSplitAtSegment = false; bool canConvertSegmentToLine = false; bool canConvertSegmentToCurve= false; if (!segments.isEmpty()) { canSplitAtSegment = segments.size() == 1; bool hasLines = false; bool hasCurves = false; Q_FOREACH (const KoPathPointData &pd, segments) { KoPathSegment segment = pd.pathShape->segmentByIndex(pd.pointIndex); hasLines |= segment.degree() == 1; hasCurves |= segment.degree() > 1; } canConvertSegmentToLine = !segments.isEmpty() && hasCurves; canConvertSegmentToCurve= !segments.isEmpty() && hasLines; } m_actionAddPoint->setEnabled(canSplitAtSegment); m_actionLineSegment->setEnabled(canConvertSegmentToLine); m_actionCurveSegment->setEnabled(canConvertSegmentToCurve); m_actionBreakSegment->setEnabled(canSplitAtSegment); KoSelection *selection = canvas()->selectedShapesProxy()->selection(); bool haveConvertibleShapes = false; Q_FOREACH (KoShape *shape, selection->selectedEditableShapes()) { KoParameterShape * parameterShape = dynamic_cast(shape); KoSvgTextShape *textShape = dynamic_cast(shape); if (textShape || (parameterShape && parameterShape->isParametricShape())) { haveConvertibleShapes = true; break; } } m_actionConvertToPath->setEnabled(haveConvertibleShapes); } void KoPathTool::deactivate() { Q_D(KoToolBase); m_shapeFillResourceConnector.disconnect(); m_canvasConnections.clear(); m_pointSelection.clear(); m_pointSelection.setSelectedShapes(QList()); delete m_activeHandle; m_activeHandle = 0; delete m_activeSegment; m_activeSegment = 0; delete m_currentStrategy; m_currentStrategy = 0; d->canvas->snapGuide()->reset(); + disconnect(m_actionCurvePoint, 0, this, 0); + disconnect(m_actionLinePoint, 0, this, 0); + disconnect(m_actionLineSegment, 0, this, 0); + disconnect(m_actionCurveSegment, 0, this, 0); + disconnect(m_actionAddPoint, 0, this, 0); + disconnect(m_actionRemovePoint, 0, this, 0); + disconnect(m_actionBreakPoint, 0, this, 0); + disconnect(m_actionBreakSegment, 0, this, 0); + disconnect(m_actionJoinSegment, 0, this, 0); + disconnect(m_actionMergePoints, 0, this, 0); + disconnect(m_actionConvertToPath, 0, this, 0); + disconnect(m_points, 0, this, 0); + disconnect(&m_pointSelection, 0, this, 0); + KoToolBase::deactivate(); } void KoPathTool::documentResourceChanged(int key, const QVariant & res) { if (key == KoDocumentResourceManager::HandleRadius) { int oldHandleRadius = m_handleRadius; m_handleRadius = res.toUInt(); // repaint with the bigger of old and new handle radius int maxRadius = qMax(m_handleRadius, oldHandleRadius); Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) { QRectF controlPointRect = shape->absoluteTransformation(0).map(shape->outline()).controlPointRect(); repaint(controlPointRect.adjusted(-maxRadius, -maxRadius, maxRadius, maxRadius)); } } } void KoPathTool::pointSelectionChanged() { Q_D(KoToolBase); updateActions(); d->canvas->snapGuide()->setIgnoredPathPoints(m_pointSelection.selectedPoints().toList()); emit selectionChanged(m_pointSelection.hasSelection()); } void KoPathTool::repaint(const QRectF &repaintRect) { Q_D(KoToolBase); //debugFlake <<"KoPathTool::repaint(" << repaintRect <<")" << m_handleRadius; // widen border to take antialiasing into account qreal radius = m_handleRadius + 1; d->canvas->updateCanvas(repaintRect.adjusted(-radius, -radius, radius, radius)); } namespace { void addActionsGroupIfEnabled(QMenu *menu, QAction *a1, QAction *a2) { if (a1->isEnabled() || a2->isEnabled()) { menu->addAction(a1); menu->addAction(a2); menu->addSeparator(); } } void addActionsGroupIfEnabled(QMenu *menu, QAction *a1, QAction *a2, QAction *a3) { if (a1->isEnabled() || a2->isEnabled()) { menu->addAction(a1); menu->addAction(a2); menu->addAction(a3); menu->addSeparator(); } } } QMenu *KoPathTool::popupActionsMenu() { if (m_activeHandle) { m_activeHandle->trySelectHandle(); } if (m_activeSegment && m_activeSegment->isValid()) { KoPathShape *shape = m_activeSegment->path; KoPathSegment segment = shape->segmentByIndex(shape->pathPointIndex(m_activeSegment->segmentStart)); m_pointSelection.add(segment.first(), true); m_pointSelection.add(segment.second(), false); } if (m_contextMenu) { m_contextMenu->clear(); addActionsGroupIfEnabled(m_contextMenu.data(), m_actionPathPointCorner, m_actionPathPointSmooth, m_actionPathPointSymmetric); addActionsGroupIfEnabled(m_contextMenu.data(), m_actionCurvePoint, m_actionLinePoint); addActionsGroupIfEnabled(m_contextMenu.data(), m_actionAddPoint, m_actionRemovePoint); addActionsGroupIfEnabled(m_contextMenu.data(), m_actionLineSegment, m_actionCurveSegment); addActionsGroupIfEnabled(m_contextMenu.data(), m_actionBreakPoint, m_actionBreakSegment); addActionsGroupIfEnabled(m_contextMenu.data(), m_actionJoinSegment, m_actionMergePoints); m_contextMenu->addAction(m_actionConvertToPath); m_contextMenu->addSeparator(); } return m_contextMenu.data(); } void KoPathTool::deleteSelection() { removePoints(); } KoToolSelection * KoPathTool::selection() { return &m_pointSelection; } void KoPathTool::requestUndoDuringStroke() { // noop! } void KoPathTool::requestStrokeCancellation() { explicitUserStrokeEndRequest(); } void KoPathTool::requestStrokeEnd() { // noop! } void KoPathTool::explicitUserStrokeEndRequest() { if (m_activatedTemporarily) { emit done(); } } diff --git a/libs/flake/tools/KoPathTool.h b/libs/flake/tools/KoPathTool.h index 2ad56b0487..9e6757ed8b 100644 --- a/libs/flake/tools/KoPathTool.h +++ b/libs/flake/tools/KoPathTool.h @@ -1,161 +1,162 @@ /* This file is part of the KDE project * Copyright (C) 2006-2012 Jan Hambrecht * Copyright (C) 2006,2007 Thorsten Zachmann * Copyright (C) 2007 Thomas Zander * Copyright (C) 2007 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 KOPATHTOOL_H #define KOPATHTOOL_H #include "KoPathShape.h" #include "KoToolBase.h" #include "KoPathToolSelection.h" #include "kis_signal_auto_connection.h" #include #include #include +class QActionGroup; class QButtonGroup; class KoCanvasBase; class KoInteractionStrategy; class KoPathToolHandle; class KoParameterShape; class KUndo2Command; class QAction; class QMenu; /// The tool for editing a KoPathShape or a KoParameterShape. /// See KoCreatePathTool for code handling the initial path creation. class KRITAFLAKE_EXPORT KoPathTool : public KoToolBase { Q_OBJECT public: explicit KoPathTool(KoCanvasBase *canvas); ~KoPathTool() override; void paint(QPainter &painter, const KoViewConverter &converter) override; void repaintDecorations() override; void mousePressEvent(KoPointerEvent *event) override; void mouseMoveEvent(KoPointerEvent *event) override; void mouseReleaseEvent(KoPointerEvent *event) override; void keyPressEvent(QKeyEvent *event) override; void keyReleaseEvent(QKeyEvent *event) override; void mouseDoubleClickEvent(KoPointerEvent *event) override; void activate(ToolActivation activation, const QSet &shapes) override; void deactivate() override; void deleteSelection() override; KoToolSelection* selection() override; void requestUndoDuringStroke() override; void requestStrokeCancellation() override; void requestStrokeEnd() override; void explicitUserStrokeEndRequest() override; /// repaints the specified rect void repaint(const QRectF &repaintRect); QMenu* popupActionsMenu() override; // for KoPathToolSelection void notifyPathPointsChanged(KoPathShape *shape); public Q_SLOTS: void documentResourceChanged(int key, const QVariant & res) override; Q_SIGNALS: void typeChanged(int types); void singleShapeChanged(KoPathShape* path); protected: /// reimplemented QList > createOptionWidgets() override; private: struct PathSegment; void updateOptionsWidget(); PathSegment* segmentAtPoint(const QPointF &point); private Q_SLOTS: void pointTypeChanged(QAction *type); void insertPoints(); void removePoints(); void segmentToLine(); void segmentToCurve(); void convertToPath(); void joinPoints(); void mergePoints(); void breakAtPoint(); void breakAtSegment(); void pointSelectionChanged(); void updateActions(); void pointToLine(); void pointToCurve(); void slotSelectionChanged(); private: void clearActivePointSelectionReferences(); void initializeWithShapes(const QList shapes); KUndo2Command* createPointToCurveCommand(const QList &points); void repaintSegment(PathSegment *pathSegment); void mergePointsImpl(bool doJoin); protected: KoPathToolSelection m_pointSelection; ///< the point selection QCursor m_selectCursor; private: KoPathToolHandle * m_activeHandle; ///< the currently active handle int m_handleRadius; ///< the radius of the control point handles uint m_grabSensitivity; ///< the grab sensitivity QPointF m_lastPoint; ///< needed for interaction strategy PathSegment *m_activeSegment; // make a frind so that it can test private member/methods friend class TestPathTool; KoInteractionStrategy *m_currentStrategy; ///< the rubber selection strategy QButtonGroup *m_pointTypeGroup; - + QActionGroup *m_points; QAction *m_actionPathPointCorner; QAction *m_actionPathPointSmooth; QAction *m_actionPathPointSymmetric; QAction *m_actionCurvePoint; QAction *m_actionLinePoint; QAction *m_actionLineSegment; QAction *m_actionCurveSegment; QAction *m_actionAddPoint; QAction *m_actionRemovePoint; QAction *m_actionBreakPoint; QAction *m_actionBreakSegment; QAction *m_actionJoinSegment; QAction *m_actionMergePoints; QAction *m_actionConvertToPath; QCursor m_moveCursor; bool m_activatedTemporarily; QScopedPointer m_contextMenu; KisSignalAutoConnectionsStore m_canvasConnections; KoShapeFillResourceConnector m_shapeFillResourceConnector; Q_DECLARE_PRIVATE(KoToolBase) }; #endif diff --git a/libs/flake/tools/KoPathToolFactory.cpp b/libs/flake/tools/KoPathToolFactory.cpp index aa82b36d16..f2e0527931 100644 --- a/libs/flake/tools/KoPathToolFactory.cpp +++ b/libs/flake/tools/KoPathToolFactory.cpp @@ -1,44 +1,66 @@ /* This file is part of the KDE project * Copyright (C) 2006 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 "KoPathToolFactory.h" #include "KoPathTool.h" #include "KoPathShape.h" +#include #include #include KoPathToolFactory::KoPathToolFactory() : KoToolFactoryBase("PathTool") { setToolTip(i18n("Edit Shapes Tool")); setSection(mainToolType()); setIconName(koIconNameCStr("shape_handling")); setPriority(2); setActivationShapeId("flake/always,KoPathShape"); } KoPathToolFactory::~KoPathToolFactory() { } KoToolBase * KoPathToolFactory::createTool(KoCanvasBase *canvas) { return new KoPathTool(canvas); } + +QList KoPathToolFactory::createActionsImpl() +{ + KisActionRegistry *actionRegistry = KisActionRegistry::instance(); + QList actions; + actions << actionRegistry->makeQAction("pathpoint-corner"); + actions << actionRegistry->makeQAction("pathpoint-smooth"); + actions << actionRegistry->makeQAction("pathpoint-symmetric"); + actions << actionRegistry->makeQAction("pathpoint-curve"); + actions << actionRegistry->makeQAction("pathpoint-line"); + actions << actionRegistry->makeQAction("pathsegment-line"); + actions << actionRegistry->makeQAction("pathsegment-curve"); + actions << actionRegistry->makeQAction("pathpoint-insert"); + actions << actionRegistry->makeQAction("pathpoint-remove"); + actions << actionRegistry->makeQAction("path-break-point"); + actions << actionRegistry->makeQAction("path-break-segment"); + actions << actionRegistry->makeQAction("pathpoint-join"); + actions << actionRegistry->makeQAction("pathpoint-merge"); + actions << actionRegistry->makeQAction("convert-to-path"); + return actions; +} diff --git a/libs/flake/tools/KoPathToolFactory.h b/libs/flake/tools/KoPathToolFactory.h index e5b6804e7c..55b72d3abb 100644 --- a/libs/flake/tools/KoPathToolFactory.h +++ b/libs/flake/tools/KoPathToolFactory.h @@ -1,35 +1,36 @@ /* This file is part of the KDE project * Copyright (C) 2006 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOPATHTOOLFACTORY_H #define KOPATHTOOLFACTORY_H #include /// Factory for the KoPathTool class KoPathToolFactory : public KoToolFactoryBase { public: KoPathToolFactory(); ~KoPathToolFactory() override; KoToolBase *createTool(KoCanvasBase *canvas) override; + QList createActionsImpl() override; }; #endif diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index 137b3455ac..f5c42e403e 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -1,592 +1,594 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile ${EXIV2_INCLUDE_DIR} ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ${OCIO_INCLUDE_DIR} ) add_subdirectory( tests ) if (APPLE) find_library(FOUNDATION_LIBRARY Foundation) find_library(APPKIT_LIBRARY AppKit) endif () set(kritaui_LIB_SRCS canvas/kis_canvas_widget_base.cpp canvas/kis_canvas2.cpp canvas/kis_canvas_updates_compressor.cpp canvas/kis_canvas_controller.cpp canvas/kis_paintop_transformation_connector.cpp canvas/kis_display_color_converter.cpp canvas/kis_display_filter.cpp canvas/kis_exposure_gamma_correction_interface.cpp canvas/kis_tool_proxy.cpp canvas/kis_canvas_decoration.cc canvas/kis_coordinates_converter.cpp canvas/kis_grid_manager.cpp canvas/kis_grid_decoration.cpp canvas/kis_grid_config.cpp canvas/kis_prescaled_projection.cpp canvas/kis_qpainter_canvas.cpp canvas/kis_projection_backend.cpp canvas/kis_update_info.cpp canvas/kis_image_patch.cpp canvas/kis_image_pyramid.cpp canvas/kis_infinity_manager.cpp canvas/kis_change_guides_command.cpp canvas/kis_guides_decoration.cpp canvas/kis_guides_manager.cpp canvas/kis_guides_config.cpp canvas/kis_snap_config.cpp canvas/kis_snap_line_strategy.cpp canvas/KisSnapPointStrategy.cpp dialogs/kis_about_application.cpp dialogs/kis_dlg_adj_layer_props.cc dialogs/kis_dlg_adjustment_layer.cc dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_generator_layer.cpp dialogs/kis_dlg_file_layer.cpp dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_stroke_selection_properties.cpp dialogs/kis_dlg_image_properties.cc dialogs/kis_dlg_layer_properties.cc dialogs/kis_dlg_preferences.cc dialogs/slider_and_spin_box_sync.cpp dialogs/kis_dlg_blacklist_cleanup.cpp dialogs/kis_dlg_layer_style.cpp dialogs/kis_dlg_png_import.cpp dialogs/kis_dlg_import_image_sequence.cpp dialogs/kis_delayed_save_dialog.cpp dialogs/KisSessionManagerDialog.cpp dialogs/KisNewWindowLayoutDialog.cpp flake/kis_node_dummies_graph.cpp flake/kis_dummies_facade_base.cpp flake/kis_dummies_facade.cpp flake/kis_node_shapes_graph.cpp flake/kis_node_shape.cpp flake/kis_shape_controller.cpp flake/kis_shape_layer.cc flake/kis_shape_layer_canvas.cpp flake/kis_shape_selection.cpp flake/kis_shape_selection_canvas.cpp flake/kis_shape_selection_model.cpp flake/kis_take_all_shapes_command.cpp brushhud/kis_uniform_paintop_property_widget.cpp brushhud/kis_brush_hud.cpp brushhud/kis_round_hud_button.cpp brushhud/kis_dlg_brush_hud_config.cpp brushhud/kis_brush_hud_properties_list.cpp brushhud/kis_brush_hud_properties_config.cpp kis_aspect_ratio_locker.cpp kis_autogradient.cc kis_bookmarked_configurations_editor.cc kis_bookmarked_configurations_model.cc kis_bookmarked_filter_configurations_model.cc KisPaintopPropertiesBase.cpp kis_canvas_resource_provider.cpp kis_derived_resources.cpp kis_categories_mapper.cpp kis_categorized_list_model.cpp kis_categorized_item_delegate.cpp kis_clipboard.cc kis_config.cc kis_control_frame.cpp kis_composite_ops_model.cc kis_paint_ops_model.cpp kis_cursor.cc kis_cursor_cache.cpp kis_custom_pattern.cc kis_file_layer.cpp kis_change_file_layer_command.h kis_safe_document_loader.cpp kis_splash_screen.cpp kis_filter_manager.cc kis_filters_model.cc kis_histogram_view.cc KisImageBarrierLockerWithFeedback.cpp kis_image_manager.cc kis_image_view_converter.cpp kis_import_catcher.cc kis_layer_manager.cc kis_mask_manager.cc kis_mimedata.cpp kis_node_commands_adapter.cpp kis_node_manager.cpp kis_node_juggler_compressed.cpp kis_node_selection_adapter.cpp kis_node_insertion_adapter.cpp KisNodeDisplayModeAdapter.cpp kis_node_model.cpp kis_node_filter_proxy_model.cpp kis_model_index_converter_base.cpp kis_model_index_converter.cpp kis_model_index_converter_show_all.cpp kis_painting_assistant.cc kis_painting_assistants_decoration.cpp KisDecorationsManager.cpp kis_paintop_box.cc kis_paintop_option.cpp kis_paintop_options_model.cpp kis_paintop_settings_widget.cpp kis_popup_palette.cpp kis_png_converter.cpp kis_preference_set_registry.cpp KisResourceServerProvider.cpp KisResourceBundleServerProvider.cpp KisSelectedShapesProxy.cpp kis_selection_decoration.cc kis_selection_manager.cc KisSelectionActionsAdapter.cpp kis_statusbar.cc kis_zoom_manager.cc kis_favorite_resource_manager.cpp kis_workspace_resource.cpp kis_action.cpp kis_action_manager.cpp KisActionPlugin.cpp kis_canvas_controls_manager.cpp kis_tooltip_manager.cpp kis_multinode_property.cpp kis_stopgradient_editor.cpp KisWelcomePageWidget.cpp kisexiv2/kis_exif_io.cpp kisexiv2/kis_exiv2.cpp kisexiv2/kis_iptc_io.cpp kisexiv2/kis_xmp_io.cpp opengl/kis_opengl.cpp opengl/kis_opengl_canvas2.cpp opengl/kis_opengl_canvas_debugger.cpp opengl/kis_opengl_image_textures.cpp opengl/kis_texture_tile.cpp opengl/kis_opengl_shader_loader.cpp opengl/kis_texture_tile_info_pool.cpp opengl/KisOpenGLUpdateInfoBuilder.cpp kis_fps_decoration.cpp + tool/KisToolChangesTracker.cpp tool/KisToolChangesTrackerData.cpp tool/kis_selection_tool_helper.cpp tool/kis_selection_tool_config_widget_helper.cpp tool/kis_rectangle_constraint_widget.cpp tool/kis_shape_tool_helper.cpp tool/kis_tool.cc tool/kis_delegated_tool_policies.cpp tool/kis_tool_freehand.cc tool/kis_speed_smoother.cpp tool/kis_painting_information_builder.cpp tool/kis_stabilized_events_sampler.cpp tool/kis_tool_freehand_helper.cpp tool/kis_tool_multihand_helper.cpp tool/kis_figure_painting_tool_helper.cpp tool/kis_tool_paint.cc tool/kis_tool_shape.cc tool/kis_tool_ellipse_base.cpp tool/kis_tool_rectangle_base.cpp tool/kis_tool_polyline_base.cpp tool/kis_tool_utils.cpp tool/kis_resources_snapshot.cpp tool/kis_smoothing_options.cpp tool/KisStabilizerDelayedPaintHelper.cpp tool/KisStrokeSpeedMonitor.cpp tool/strokes/freehand_stroke.cpp tool/strokes/KisStrokeEfficiencyMeasurer.cpp tool/strokes/kis_painter_based_stroke_strategy.cpp tool/strokes/kis_filter_stroke_strategy.cpp tool/strokes/kis_color_picker_stroke_strategy.cpp tool/strokes/KisFreehandStrokeInfo.cpp tool/strokes/KisMaskedFreehandStrokePainter.cpp tool/strokes/KisMaskingBrushRenderer.cpp tool/strokes/KisMaskingBrushCompositeOpFactory.cpp tool/strokes/move_stroke_strategy.cpp - + tool/KisSelectionToolFactoryBase.cpp + tool/KisToolPaintFactoryBase.cpp widgets/kis_cmb_composite.cc widgets/kis_cmb_contour.cpp widgets/kis_cmb_gradient.cpp widgets/kis_paintop_list_widget.cpp widgets/kis_cmb_idlist.cc widgets/kis_color_space_selector.cc widgets/kis_advanced_color_space_selector.cc widgets/kis_cie_tongue_widget.cpp widgets/kis_tone_curve_widget.cpp widgets/kis_curve_widget.cpp widgets/kis_custom_image_widget.cc widgets/kis_image_from_clipboard_widget.cpp widgets/kis_double_widget.cc widgets/kis_filter_selector_widget.cc widgets/kis_gradient_chooser.cc widgets/kis_iconwidget.cc widgets/kis_mask_widgets.cpp widgets/kis_meta_data_merge_strategy_chooser_widget.cc widgets/kis_multi_bool_filter_widget.cc widgets/kis_multi_double_filter_widget.cc widgets/kis_multi_integer_filter_widget.cc widgets/kis_multipliers_double_slider_spinbox.cpp widgets/kis_paintop_presets_popup.cpp widgets/kis_tool_options_popup.cpp widgets/kis_paintop_presets_chooser_popup.cpp widgets/kis_paintop_presets_save.cpp widgets/kis_paintop_preset_icon_library.cpp widgets/kis_pattern_chooser.cc widgets/kis_preset_chooser.cpp widgets/kis_progress_widget.cpp widgets/kis_selection_options.cc widgets/kis_scratch_pad.cpp widgets/kis_scratch_pad_event_filter.cpp widgets/kis_preset_selector_strip.cpp widgets/kis_slider_spin_box.cpp widgets/KisSelectionPropertySlider.cpp widgets/kis_size_group.cpp widgets/kis_size_group_p.cpp widgets/kis_wdg_generator.cpp widgets/kis_workspace_chooser.cpp widgets/kis_categorized_list_view.cpp widgets/kis_widget_chooser.cpp widgets/kis_tool_button.cpp widgets/kis_floating_message.cpp widgets/kis_lod_availability_widget.cpp widgets/kis_color_label_selector_widget.cpp widgets/kis_color_filter_combo.cpp widgets/kis_elided_label.cpp widgets/kis_stopgradient_slider_widget.cpp widgets/kis_preset_live_preview_view.cpp widgets/KisScreenColorPicker.cpp widgets/KoDualColorButton.cpp widgets/KoStrokeConfigWidget.cpp widgets/KoFillConfigWidget.cpp KisPaletteEditor.cpp dialogs/KisDlgPaletteEditor.cpp widgets/KisNewsWidget.cpp widgets/KisGamutMaskToolbar.cpp utils/kis_document_aware_spin_box_unit_manager.cpp input/kis_input_manager.cpp input/kis_input_manager_p.cpp input/kis_extended_modifiers_mapper.cpp input/kis_abstract_input_action.cpp input/kis_tool_invocation_action.cpp input/kis_pan_action.cpp input/kis_alternate_invocation_action.cpp input/kis_rotate_canvas_action.cpp input/kis_zoom_action.cpp input/kis_change_frame_action.cpp input/kis_gamma_exposure_action.cpp input/kis_show_palette_action.cpp input/kis_change_primary_setting_action.cpp input/kis_abstract_shortcut.cpp input/kis_native_gesture_shortcut.cpp input/kis_single_action_shortcut.cpp input/kis_stroke_shortcut.cpp input/kis_shortcut_matcher.cpp input/kis_select_layer_action.cpp input/KisQtWidgetsTweaker.cpp input/KisInputActionGroup.cpp operations/kis_operation.cpp operations/kis_operation_configuration.cpp operations/kis_operation_registry.cpp operations/kis_operation_ui_factory.cpp operations/kis_operation_ui_widget.cpp operations/kis_filter_selection_operation.cpp actions/kis_selection_action_factories.cpp actions/KisPasteActionFactory.cpp actions/KisTransformToolActivationCommand.cpp input/kis_touch_shortcut.cpp kis_document_undo_store.cpp kis_gui_context_command.cpp kis_gui_context_command_p.cpp input/kis_tablet_debugger.cpp input/kis_input_profile_manager.cpp input/kis_input_profile.cpp input/kis_shortcut_configuration.cpp input/config/kis_input_configuration_page.cpp input/config/kis_edit_profiles_dialog.cpp input/config/kis_input_profile_model.cpp input/config/kis_input_configuration_page_item.cpp input/config/kis_action_shortcuts_model.cpp input/config/kis_input_type_delegate.cpp input/config/kis_input_mode_delegate.cpp input/config/kis_input_button.cpp input/config/kis_input_editor_delegate.cpp input/config/kis_mouse_input_editor.cpp input/config/kis_wheel_input_editor.cpp input/config/kis_key_input_editor.cpp processing/fill_processing_visitor.cpp kis_asl_layer_style_serializer.cpp kis_psd_layer_style_resource.cpp canvas/kis_mirror_axis.cpp kis_abstract_perspective_grid.cpp KisApplication.cpp KisAutoSaveRecoveryDialog.cpp KisDetailsPane.cpp KisDocument.cpp KisCloneDocumentStroke.cpp KisNodeDelegate.cpp kis_node_view_visibility_delegate.cpp KisNodeToolTip.cpp KisNodeView.cpp kis_node_view_color_scheme.cpp KisImportExportFilter.cpp KisFilterEntry.cpp KisImportExportManager.cpp KisImportExportUtils.cpp kis_async_action_feedback.cpp KisMainWindow.cpp KisOpenPane.cpp KisPart.cpp KisPrintJob.cpp KisTemplate.cpp KisTemplateCreateDia.cpp KisTemplateGroup.cpp KisTemplates.cpp KisTemplatesPane.cpp KisTemplateTree.cpp KisUndoActionsUpdateManager.cpp KisView.cpp thememanager.cpp kis_mainwindow_observer.cpp KisViewManager.cpp kis_mirror_manager.cpp qtlockedfile/qtlockedfile.cpp qtsingleapplication/qtlocalpeer.cpp qtsingleapplication/qtsingleapplication.cpp KisResourceBundle.cpp KisResourceBundleManifest.cpp kis_md5_generator.cpp KisApplicationArguments.cpp KisNetworkAccessManager.cpp KisMultiFeedRSSModel.cpp KisRemoteFileFetcher.cpp KisSaveGroupVisitor.cpp KisWindowLayoutResource.cpp KisWindowLayoutManager.cpp KisSessionResource.cpp KisReferenceImagesDecoration.cpp KisReferenceImage.cpp flake/KisReferenceImagesLayer.cpp flake/KisReferenceImagesLayer.h ) if(WIN32) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/wintab/kis_tablet_support_win.cpp input/wintab/kis_screen_size_choice_dialog.cpp qtlockedfile/qtlockedfile_win.cpp input/wintab/kis_tablet_support_win8.cpp opengl/kis_opengl_win.cpp ) endif() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} kis_animation_frame_cache.cpp kis_animation_cache_populator.cpp KisAsyncAnimationRendererBase.cpp KisAsyncAnimationCacheRenderer.cpp KisAsyncAnimationFramesSavingRenderer.cpp dialogs/KisAsyncAnimationRenderDialogBase.cpp dialogs/KisAsyncAnimationCacheRenderDialog.cpp dialogs/KisAsyncAnimationFramesSaveDialog.cpp canvas/kis_animation_player.cpp kis_animation_importer.cpp KisSyncedAudioPlayback.cpp KisFrameDataSerializer.cpp KisFrameCacheStore.cpp KisFrameCacheSwapper.cpp KisAbstractFrameCacheSwapper.cpp KisInMemoryFrameCacheSwapper.cpp input/wintab/drawpile_tablettester/tablettester.cpp input/wintab/drawpile_tablettester/tablettest.cpp ) if (UNIX) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} qtlockedfile/qtlockedfile_unix.cpp ) if(NOT USE_QT_XCB) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/wintab/kis_tablet_support.cpp ) endif() if(NOT APPLE AND NOT USE_QT_XCB) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/wintab/qxcbconnection_xi2.cpp input/wintab/qxcbconnection.cpp input/wintab/kis_xi2_event_filter.cpp ) endif() endif() if(APPLE) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} osx.mm ) endif() ki18n_wrap_ui(kritaui_LIB_SRCS widgets/KoFillConfigWidget.ui widgets/KoStrokeConfigWidget.ui forms/wdgdlgpngimport.ui forms/wdgfullscreensettings.ui forms/wdgautogradient.ui forms/wdggeneralsettings.ui forms/wdgperformancesettings.ui forms/wdggenerators.ui forms/wdgbookmarkedconfigurationseditor.ui forms/wdgapplyprofile.ui forms/wdgcustompattern.ui forms/wdglayerproperties.ui forms/wdgcolorsettings.ui forms/wdgtabletsettings.ui forms/wdgcolorspaceselector.ui forms/wdgcolorspaceselectoradvanced.ui forms/wdgdisplaysettings.ui forms/kis_previewwidgetbase.ui forms/kis_matrix_widget.ui forms/wdgselectionoptions.ui forms/wdggeometryoptions.ui forms/wdgnewimage.ui forms/wdgimageproperties.ui forms/wdgmaskfromselection.ui forms/wdgmasksource.ui forms/wdgfilterdialog.ui forms/wdgmetadatamergestrategychooser.ui forms/wdgpaintoppresets.ui forms/wdgpaintopsettings.ui forms/wdgdlggeneratorlayer.ui forms/wdgdlgfilelayer.ui forms/wdgfilterselector.ui forms/wdgfilternodecreation.ui forms/wdgmultipliersdoublesliderspinbox.ui forms/wdgnodequerypatheditor.ui forms/wdgpresetselectorstrip.ui forms/wdgsavebrushpreset.ui forms/wdgpreseticonlibrary.ui forms/wdgdlgblacklistcleanup.ui forms/wdgrectangleconstraints.ui forms/wdgimportimagesequence.ui forms/wdgstrokeselectionproperties.ui forms/KisDetailsPaneBase.ui forms/KisOpenPaneBase.ui forms/wdgstopgradienteditor.ui forms/wdgsessionmanager.ui forms/wdgnewwindowlayout.ui forms/KisWelcomePage.ui forms/WdgDlgPaletteEditor.ui forms/KisNewsPage.ui forms/wdgGamutMaskToolbar.ui brushhud/kis_dlg_brush_hud_config.ui dialogs/kis_delayed_save_dialog.ui input/config/kis_input_configuration_page.ui input/config/kis_edit_profiles_dialog.ui input/config/kis_input_configuration_page_item.ui input/config/kis_mouse_input_editor.ui input/config/kis_wheel_input_editor.ui input/config/kis_key_input_editor.ui layerstyles/wdgBevelAndEmboss.ui layerstyles/wdgblendingoptions.ui layerstyles/WdgColorOverlay.ui layerstyles/wdgContour.ui layerstyles/wdgdropshadow.ui layerstyles/WdgGradientOverlay.ui layerstyles/wdgInnerGlow.ui layerstyles/wdglayerstyles.ui layerstyles/WdgPatternOverlay.ui layerstyles/WdgSatin.ui layerstyles/WdgStroke.ui layerstyles/wdgstylesselector.ui layerstyles/wdgTexture.ui wdgsplash.ui input/wintab/kis_screen_size_choice_dialog.ui input/wintab/drawpile_tablettester/tablettest.ui ) QT5_WRAP_CPP(kritaui_HEADERS_MOC KisNodePropertyAction_p.h) add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} ) generate_export_header(kritaui BASE_NAME kritaui) target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils kritaresources ${PNG_LIBRARIES} ${EXIV2_LIBRARIES} ) if (HAVE_QT_MULTIMEDIA) target_link_libraries(kritaui Qt5::Multimedia) endif() if (NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${X11_X11_LIB} ${X11_Xinput_LIB} ${XCB_LIBRARIES}) endif() if(APPLE) target_link_libraries(kritaui ${FOUNDATION_LIBRARY}) target_link_libraries(kritaui ${APPKIT_LIBRARY}) endif () target_link_libraries(kritaui ${OPENEXR_LIBRARIES}) # Add VSync disable workaround if(NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras) endif() if(X11_FOUND) target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES}) endif() target_include_directories(kritaui PUBLIC $ $ $ $ $ $ $ ) set_target_properties(kritaui PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS}) if (APPLE) install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita) endif () diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp index be04e2ee91..229e6c53b1 100644 --- a/libs/ui/KisMainWindow.cpp +++ b/libs/ui/KisMainWindow.cpp @@ -1,2663 +1,2667 @@ /* 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 #include #include #include "kis_selection_manager.h" #include "kis_icon_utils.h" - #include -#include #include #include #include #include #include #include #include #include #include +#include +#include +#include #include "KoDockFactoryBase.h" #include "KoDocumentInfoDlg.h" #include "KoDocumentInfo.h" #include "KoFileDialog.h" #include #include #include #include #include #include "KoToolDocker.h" #include "KoToolBoxDocker_p.h" #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 "kis_group_layer.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 "KisResourceServerProvider.h" #include "kis_signal_compressor_with_param.h" #include "kis_statusbar.h" #include "KisView.h" #include "KisViewManager.h" #include "thememanager.h" #include "kis_animation_importer.h" #include "dialogs/kis_dlg_import_image_sequence.h" #include #include "KisWindowLayoutManager.h" #include #include "KisWelcomePageWidget.h" #include #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(); return dockWidget; } DockPosition defaultDockPosition() const override { return DockRight; } }; class Q_DECL_HIDDEN KisMainWindow::Private { public: Private(KisMainWindow *parent, QUuid id) : q(parent) , id(id) , dockWidgetMenu(new KActionMenu(i18nc("@action:inmenu", "&Dockers"), parent)) , windowMenu(new KActionMenu(i18nc("@action:inmenu", "&Window"), parent)) , documentMenu(new KActionMenu(i18nc("@action:inmenu", "New &View"), parent)) , workspaceMenu(new KActionMenu(i18nc("@action:inmenu", "Wor&kspace"), parent)) , welcomePage(new KisWelcomePageWidget(parent)) , widgetStack(new QStackedWidget(parent)) , mdiArea(new QMdiArea(parent)) , windowMapper(new QSignalMapper(parent)) , documentMapper(new QSignalMapper(parent)) { if (id.isNull()) this->id = QUuid::createUuid(); widgetStack->addWidget(welcomePage); widgetStack->addWidget(mdiArea); mdiArea->setTabsMovable(true); mdiArea->setActivationOrder(QMdiArea::ActivationHistoryOrder); } ~Private() { qDeleteAll(toolbarList); } KisMainWindow *q {0}; QUuid id; KisViewManager *viewManager {0}; QPointer activeView; QList toolbarList; bool firstTime {true}; bool windowSizeDirty {false}; bool readOnly {false}; KisAction *showDocumentInfo {0}; KisAction *saveAction {0}; KisAction *saveActionAs {0}; // KisAction *printAction; // KisAction *printActionPreview; // KisAction *exportPdf {0}; KisAction *importAnimation {0}; KisAction *closeAll {0}; // KisAction *reloadFile; KisAction *importFile {0}; KisAction *exportFile {0}; KisAction *undo {0}; KisAction *redo {0}; KisAction *newWindow {0}; KisAction *close {0}; KisAction *mdiCascade {0}; KisAction *mdiTile {0}; KisAction *mdiNextWindow {0}; KisAction *mdiPreviousWindow {0}; KisAction *toggleDockers {0}; KisAction *toggleDockerTitleBars {0}; KisAction *fullScreenMode {0}; KisAction *showSessionManager {0}; KisAction *expandingSpacers[2]; KActionMenu *dockWidgetMenu; KActionMenu *windowMenu; KActionMenu *documentMenu; KActionMenu *workspaceMenu; KHelpMenu *helpMenu {0}; KRecentFilesAction *recentFiles {0}; KoLegacyResourceModel *workspacemodel {0}; QScopedPointer undoActionsUpdateManager; QString lastExportLocation; QMap dockWidgetsMap; QByteArray dockerStateBeforeHiding; KoToolDocker *toolOptionsDocker {0}; QCloseEvent *deferredClosingEvent {0}; Digikam::ThemeManager *themeManager {0}; KisWelcomePageWidget *welcomePage {0}; QStackedWidget *widgetStack {0}; QMdiArea *mdiArea; QMdiSubWindow *activeSubWindow {0}; QSignalMapper *windowMapper; QSignalMapper *documentMapper; QByteArray lastExportedFormat; QScopedPointer > tabSwitchCompressor; QMutex savingEntryMutex; KConfigGroup windowStateConfig; QUuid workspaceBorrowedBy; KisSignalAutoConnectionsStore screenConnectionsStore; 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(QUuid uuid) : KXmlGuiWindow() , d(new Private(this, uuid)) { auto rserver = KisResourceServerProvider::instance()->workspaceServer(); QSharedPointer adapter(new KoResourceServerAdapter(rserver)); d->workspacemodel = new KoLegacyResourceModel(adapter, this); connect(d->workspacemodel, &KoLegacyResourceModel::afterResourcesLayoutReset, this, [&]() { updateWindowMenu(); }); d->viewManager = new KisViewManager(this, actionCollection()); KConfigGroup group( KSharedConfig::openConfig(), "theme"); d->themeManager = new Digikam::ThemeManager(group.readEntry("Theme", "Krita dark"), this); d->windowStateConfig = KSharedConfig::openConfig()->group("MainWindow"); 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, false); // Load the per-application plugins (Right now, only Python) We do this only once, when the first mainwindow is being created. KoPluginLoader::instance()->load("Krita/ApplicationPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), qApp, true); KoToolBoxFactory toolBoxFactory; QDockWidget *toolbox = createDockWidget(&toolBoxFactory); toolbox->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable); KisConfig cfg(true); if (cfg.toolOptionsInDocker()) { ToolDockerFactory toolDockerFactory; d->toolOptionsDocker = qobject_cast(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->setViewManager(d->viewManager); } } + // Load all the actions from the tool plugins + Q_FOREACH(KoToolFactoryBase *toolFactory, KoToolRegistry::instance()->values()) { + toolFactory->createActions(actionCollection()); + } + d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setTabPosition(QTabWidget::North); d->mdiArea->setTabsClosable(true); // Tab close button override // Windows just has a black X, and Ubuntu has a dark x that is hard to read // just switch this icon out for all OSs so it is easier to see d->mdiArea->setStyleSheet("QTabBar::close-button { image: url(:/pics/broken-preset.png) }"); setCentralWidget(d->widgetStack); d->widgetStack->setCurrentIndex(0); connect(d->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(subWindowActivated())); connect(d->windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); connect(d->documentMapper, SIGNAL(mapped(QObject*)), this, SLOT(newView(QObject*))); createActions(); // the welcome screen needs to grab actions...so make sure this line goes after the createAction() so they exist d->welcomePage->setMainWindow(this); setAutoSaveSettings(d->windowStateConfig, false); subWindowActivated(); updateWindowMenu(); if (isHelpMenuEnabled() && !d->helpMenu) { // workaround for KHelpMenu (or rather KAboutData::applicationData()) internally // not using the Q*Application metadata ATM, which results e.g. in the bugreport wizard // not having the app version preset // fixed hopefully in KF5 5.22.0, patch pending QGuiApplication *app = qApp; KAboutData aboutData(app->applicationName(), app->applicationDisplayName(), app->applicationVersion()); aboutData.setOrganizationDomain(app->organizationDomain().toUtf8()); d->helpMenu = new KHelpMenu(this, aboutData, false); // workaround-less version: // d->helpMenu = new KHelpMenu(this, QString()/*unused*/, false); // The difference between using KActionCollection->addAction() is that // these actions do not get tied to the MainWindow. What does this all do? KActionCollection *actions = d->viewManager->actionCollection(); QAction *helpContentsAction = d->helpMenu->action(KHelpMenu::menuHelpContents); QAction *whatsThisAction = d->helpMenu->action(KHelpMenu::menuWhatsThis); QAction *reportBugAction = d->helpMenu->action(KHelpMenu::menuReportBug); QAction *switchLanguageAction = d->helpMenu->action(KHelpMenu::menuSwitchLanguage); QAction *aboutAppAction = d->helpMenu->action(KHelpMenu::menuAboutApp); QAction *aboutKdeAction = d->helpMenu->action(KHelpMenu::menuAboutKDE); if (helpContentsAction) { actions->addAction(helpContentsAction->objectName(), helpContentsAction); } if (whatsThisAction) { actions->addAction(whatsThisAction->objectName(), whatsThisAction); } if (reportBugAction) { actions->addAction(reportBugAction->objectName(), reportBugAction); } if (switchLanguageAction) { actions->addAction(switchLanguageAction->objectName(), switchLanguageAction); } if (aboutAppAction) { actions->addAction(aboutAppAction->objectName(), aboutAppAction); } if (aboutKdeAction) { actions->addAction(aboutKdeAction->objectName(), aboutKdeAction); } connect(d->helpMenu, SIGNAL(showAboutApplication()), SLOT(showAboutApplication())); } // KDE' libs 4''s help contents action is broken outside kde, for some reason... We can handle it just as easily ourselves QAction *helpAction = actionCollection()->action("help_contents"); helpAction->disconnect(); connect(helpAction, SIGNAL(triggered()), this, SLOT(showManual())); #if 0 //check for colliding shortcuts QSet 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); + toolBar->setMovable(KisConfig(true).readEntry("LockAllDockerPanels", false)); if (toolBar) { if (toolBar->objectName() == "BrushesAndStuff") { toolBar->setEnabled(false); } KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this); actionCollection()->addAction(toolBar->objectName().toUtf8(), act); act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle()))); connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool))); act->setChecked(!toolBar->isHidden()); toolbarList.append(act); } else { warnUI << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!"; } } + + KToolBar::setToolBarsLocked(KisConfig(true).readEntry("LockAllDockerPanels", false)); 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)); } } 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() -// << "/>" ; +// qDebug() << "", "").replace("", "") +// << "\n\ticonText=" << action->iconText().replace("&", "&") +// << "\n\tshortcut=" << action->shortcut().toString() +// << "\n\tisCheckable=" << QString((action->isChecked() ? "true" : "false")) +// << "\n\tstatusTip=" << action->statusTip() +// << "\n/>\n" ; // } // else { -// dbgKrita << "Got a QAction:" << ac->objectName(); +// dbgKrita << "Got a non-qaction:" << ac->objectName(); // } - // } // The doc and view might still exist (this is the case when closing the window) KisPart::instance()->removeMainWindow(this); delete d->viewManager; delete d; } QUuid KisMainWindow::id() const { return d->id; } void KisMainWindow::addView(KisView *view) { 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); imageView->setSubWindow(subwin); subwin->setAttribute(Qt::WA_DeleteOnClose, true); connect(subwin, SIGNAL(destroyed()), SLOT(updateWindowMenu())); KisConfig cfg(true); 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(); KisConfigNotifier::instance()->notifyPixelGridModeChanged(); KisImageConfigNotifier::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) { // Add entry to recent documents list // (call coming from KisDocument because it must work with cmd line, template dlg, file/open, etc.) if (!url.isEmpty()) { bool ok = true; if (url.isLocalFile()) { QString path = url.adjusted(QUrl::StripTrailingSlash).toLocalFile(); const QStringList tmpDirs = KoResourcePaths::resourceDirs("tmp"); for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it) { if (path.contains(*it)) { ok = false; // it's in the tmp resource } } const QStringList templateDirs = KoResourcePaths::findDirs("templates"); for (QStringList::ConstIterator it = templateDirs.begin() ; ok && it != templateDirs.end() ; ++it) { if (path.contains(*it)) { ok = false; // it's in the templates directory. break; } } } if (ok) { d->recentFiles->addUrl(url); } saveRecentFiles(); } } void KisMainWindow::saveRecentFiles() { // Save list of recent files KSharedConfigPtr config = KSharedConfig::openConfig(); d->recentFiles->saveEntries(config->group("RecentFiles")); config->sync(); // Tell all windows to reload their list, after saving // Doesn't work multi-process, but it's a start Q_FOREACH (KisMainWindow *mw, KisPart::instance()->mainWindows()) { if (mw != this) { mw->reloadRecentFileList(); } } } QList KisMainWindow::recentFilesUrls() { return d->recentFiles->urls(); } void KisMainWindow::clearRecentFiles() { d->recentFiles->clear(); } 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") + "] "; } // show the file size for the document KisMemoryStatisticsServer::Statistics m_fileSizeStats = KisMemoryStatisticsServer::instance()->fetchMemoryStatistics(d->activeView ? d->activeView->image() : 0); if (m_fileSizeStats.imageSize) { caption += QString(" (").append( KFormat().formatByteSize(m_fileSizeStats.imageSize)).append( ")"); } 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 << ")"; QString versionString = KritaVersionWrapper::versionString(true); #if defined(KRITA_ALPHA) || defined (KRITA_BETA) || defined (KRITA_RC) setCaption(QString("%1: %2").arg(versionString).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(QString)), this, SLOT(slotLoadCanceled(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; } void KisMainWindow::showDocument(KisDocument *document) { Q_FOREACH(QMdiSubWindow *subwindow, d->mdiArea->subWindowList()) { KisView *view = qobject_cast(subwindow->widget()); KIS_SAFE_ASSERT_RECOVER_NOOP(view); if (view) { if (view->document() == document) { setActiveSubWindow(subwindow); return; } } } addViewAndNotifyLoadingCompleted(document); } KisView* KisMainWindow::addViewAndNotifyLoadingCompleted(KisDocument *document) { showWelcomeScreen(false); // see workaround in function header 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(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import)); dialog.setCaption(isImporting ? i18n("Import Images") : i18n("Open Images")); return dialog.filenames(); } // Separate from openDocument to handle async loading (remote URLs) void KisMainWindow::slotLoadCompleted() { KisDocument *newdoc = qobject_cast(sender()); if (newdoc && newdoc->image()) { addViewAndNotifyLoadingCompleted(newdoc); disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(newdoc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString))); emit loadCompleted(); } } void KisMainWindow::slotLoadCanceled(const QString & errMsg) { 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(QString)), this, SLOT(slotLoadCanceled(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(QString)), this, SLOT(slotSaveCanceled(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::installBundle(const QString &fileName) const { QFileInfo from(fileName); QFileInfo to(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/" + from.fileName()); if (to.exists()) { QFile::remove(to.canonicalFilePath()); } return QFile::copy(fileName, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/" + from.fileName()); } 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; } if (document->url().isEmpty()) { saveas = true; } connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); connect(document, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString))); QByteArray nativeFormat = document->nativeFormatMimeType(); QByteArray oldMimeFormat = document->mimeType(); QUrl suggestedURL = document->url(); QStringList mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export); mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export); if (!mimeFilter.contains(oldMimeFormat)) { dbgUI << "KisMainWindow::saveDocument no export filter for" << oldMimeFormat; // --- don't setOutputMimeType in case the user cancels the Save As // dialog and then tries to just plain Save --- // suggest a different filename extension (yes, we fortunately don't all live in a world of magic :)) QString suggestedFilename = QFileInfo(suggestedURL.toLocalFile()).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 = 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(), false); outputFormat = outputFormatString.toLatin1(); if (!isExporting) { justChangingFilterOptions = (newURL == document->url()) && (outputFormat == document->mimeType()); } else { QString path = QFileInfo(d->lastExportLocation).absolutePath(); QString filename = QFileInfo(document->url().toLocalFile()).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!"; } } else { // Export ret = document->exportDocument(newURL, outputFormat); if (ret) { d->lastExportLocation = newURL.toLocalFile(); d->lastExportedFormat = outputFormat; } } } // if (wantToSave) { else ret = false; } // if (bOk) { else ret = false; } else { // saving // We cannot "export" into the currently // opened document. We are not Gimp. KIS_ASSERT_RECOVER_NOOP(!isExporting); // be sure document has the correct outputMimeType! if (document->isModified()) { ret = document->save(true, 0); } if (!ret) { dbgUI << "Failed Save!"; } } updateReloadFileAction(document); updateCaption(); return ret; } void KisMainWindow::undo() { if (activeView()) { activeView()->document()->undoStack()->undo(); } } void KisMainWindow::redo() { if (activeView()) { activeView()->document()->undoStack()->redo(); } } void KisMainWindow::closeEvent(QCloseEvent *e) { if (!KisPart::instance()->closingSession()) { QAction *action= d->viewManager->actionCollection()->action("view_show_canvas_only"); if ((action) && (action->isChecked())) { action->setChecked(false); } // Save session when last window is closed if (KisPart::instance()->mainwindowCount() == 1) { bool closeAllowed = KisPart::instance()->closeSession(); if (!closeAllowed) { e->setAccepted(false); return; } } } d->mdiArea->closeAllSubWindows(); QList childrenList = d->mdiArea->subWindowList(); if (childrenList.isEmpty()) { d->deferredClosingEvent = e; saveWindowState(true); } else { e->setAccepted(false); } } void KisMainWindow::saveWindowSettings() { KSharedConfigPtr config = KSharedConfig::openConfig(); if (d->windowSizeDirty ) { dbgUI << "KisMainWindow::saveWindowSettings"; KConfigGroup group = d->windowStateConfig; KWindowConfig::saveWindowSize(windowHandle(), group); config->sync(); d->windowSizeDirty = false; } if (!d->activeView || d->activeView->document()) { // Save toolbar position into the config file of the app, under the doc's component name KConfigGroup group = d->windowStateConfig; saveMainWindowSettings(group); // Save collapsible state of dock widgets for (QMap::const_iterator i = d->dockWidgetsMap.constBegin(); i != d->dockWidgetsMap.constEnd(); ++i) { if (i.value()->widget()) { KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key()); dockGroup.writeEntry("Collapsed", i.value()->widget()->isHidden()); dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool()); dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value())); dockGroup.writeEntry("xPosition", (int) i.value()->widget()->x()); dockGroup.writeEntry("yPosition", (int) i.value()->widget()->y()); dockGroup.writeEntry("width", (int) i.value()->widget()->width()); dockGroup.writeEntry("height", (int) i.value()->widget()->height()); } } } KSharedConfig::openConfig()->sync(); resetAutoSaveSettings(); // Don't let KMainWindow override the good stuff we wrote down } void KisMainWindow::resizeEvent(QResizeEvent * e) { d->windowSizeDirty = true; KXmlGuiWindow::resizeEvent(e); } void KisMainWindow::setActiveView(KisView* view) { d->activeView = view; updateCaption(); if (d->undoActionsUpdateManager) { d->undoActionsUpdateManager->setCurrentDocument(view ? view->document() : 0); } d->viewManager->setCurrentView(view); KisWindowLayoutManager::instance()->activeDocumentChanged(view->document()); } void KisMainWindow::dragEnterEvent(QDragEnterEvent *event) { d->welcomePage->showDropAreaIndicator(true); 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) { d->welcomePage->showDropAreaIndicator(false); if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) { Q_FOREACH (const QUrl &url, event->mimeData()->urls()) { if (url.toLocalFile().endsWith(".bundle")) { bool r = installBundle(url.toLocalFile()); if (!r) { qWarning() << "Could not install bundle" << url.toLocalFile(); } } else { 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*/) { d->welcomePage->showDropAreaIndicator(false); if (d->tabSwitchCompressor->isActive()) { d->tabSwitchCompressor->stop(); } } void KisMainWindow::switchTab(int index) { QTabBar *tabBar = d->findTabBarHACK(); if (!tabBar) return; tabBar->setCurrentIndex(index); } void KisMainWindow::showWelcomeScreen(bool show) { d->widgetStack->setCurrentIndex(!show); } void KisMainWindow::slotFileNew() { const QStringList mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import); KisOpenPane *startupWidget = new KisOpenPane(this, mimeFilter, QStringLiteral("templates/")); startupWidget->setWindowModality(Qt::WindowModal); startupWidget->setWindowTitle(i18n("Create new document")); KisConfig cfg(true); int w = cfg.defImageWidth(); int h = cfg.defImageHeight(); const double resolution = cfg.defImageResolution(); const QString colorModel = cfg.defColorModel(); const QString colorDepth = cfg.defaultColorDepth(); const QString colorProfile = cfg.defColorProfile(); CustomDocumentWidgetItem item; item.widget = new KisCustomImageWidget(startupWidget, w, h, resolution, colorModel, colorDepth, colorProfile, i18n("Unnamed")); item.icon = "document-new"; 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(QUrl)), KisPart::instance(), SLOT(openTemplate(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(); } } void KisMainWindow::slotShowSessionManager() { KisPart::instance()->showSessionManager(); } KoCanvasResourceProvider *KisMainWindow::resourceManager() const { return d->viewManager->resourceProvider()->resourceManager(); } int KisMainWindow::viewCount() const { return d->mdiArea->subWindowList().size(); } const KConfigGroup &KisMainWindow::windowStateConfig() const { return d->windowStateConfig; } void KisMainWindow::saveWindowState(bool restoreNormalState) { if (restoreNormalState) { QAction *showCanvasOnly = d->viewManager->actionCollection()->action("view_show_canvas_only"); if (showCanvasOnly && showCanvasOnly->isChecked()) { showCanvasOnly->setChecked(false); } d->windowStateConfig.writeEntry("ko_geometry", saveGeometry().toBase64()); d->windowStateConfig.writeEntry("State", saveState().toBase64()); if (!d->dockerStateBeforeHiding.isEmpty()) { restoreState(d->dockerStateBeforeHiding); } statusBar()->setVisible(true); menuBar()->setVisible(true); saveWindowSettings(); } else { saveMainWindowSettings(d->windowStateConfig); } } bool KisMainWindow::restoreWorkspaceState(const QByteArray &state) { QByteArray oldState = saveState(); // needed because otherwise the layout isn't correctly restored in some situations Q_FOREACH (QDockWidget *dock, dockWidgets()) { dock->toggleViewAction()->setEnabled(true); dock->hide(); } bool success = KXmlGuiWindow::restoreState(state); if (!success) { KXmlGuiWindow::restoreState(oldState); return false; } return success; } bool KisMainWindow::restoreWorkspace(KisWorkspaceResourceSP workspace) { bool success = restoreWorkspaceState(workspace->dockerState()); if (activeKisView()) { activeKisView()->resourceProvider()->notifyLoadingWorkspace(workspace); } return success; } QByteArray KisMainWindow::borrowWorkspace(KisMainWindow *other) { QByteArray currentWorkspace = saveState(); if (!d->workspaceBorrowedBy.isNull()) { if (other->id() == d->workspaceBorrowedBy) { // We're swapping our original workspace back d->workspaceBorrowedBy = QUuid(); return currentWorkspace; } else { // Get our original workspace back before swapping with a third window KisMainWindow *borrower = KisPart::instance()->windowById(d->workspaceBorrowedBy); if (borrower) { QByteArray originalLayout = borrower->borrowWorkspace(this); borrower->restoreWorkspaceState(currentWorkspace); d->workspaceBorrowedBy = other->id(); return originalLayout; } } } d->workspaceBorrowedBy = other->id(); return currentWorkspace; } void KisMainWindow::swapWorkspaces(KisMainWindow *a, KisMainWindow *b) { QByteArray workspaceA = a->borrowWorkspace(b); QByteArray workspaceB = b->borrowWorkspace(a); a->restoreWorkspaceState(workspaceB); b->restoreWorkspaceState(workspaceA); } KisViewManager *KisMainWindow::viewManager() const { return d->viewManager; } void KisMainWindow::slotDocumentInfo() { if (!d->activeView->document()) return; KoDocumentInfo *docInfo = d->activeView->document()->documentInfo(); if (!docInfo) return; KoDocumentInfoDlg *dlg = d->activeView->document()->createDocumentInfoDialog(this, docInfo); if (dlg->exec()) { if (dlg->isDocumentSaved()) { d->activeView->document()->setModified(false); } else { d->activeView->document()->setModified(true); } d->activeView->document()->setTitleModified(); } delete dlg; } bool KisMainWindow::slotFileCloseAll() { Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { if (subwin) { if(!subwin->close()) return false; } } updateCaption(); return true; } void KisMainWindow::slotFileQuit() { KisPart::instance()->closeSession(); } void KisMainWindow::slotFilePrint() { if (!activeView()) return; KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return; applyDefaultSettings(printJob->printer()); QPrintDialog *printDialog = activeView()->createPrintDialog( printJob, this ); if (printDialog && printDialog->exec() == QDialog::Accepted) { printJob->printer().setPageMargins(0.0, 0.0, 0.0, 0.0, QPrinter::Point); printJob->printer().setPaperSize(QSizeF(activeView()->image()->width() / (72.0 * activeView()->image()->xRes()), activeView()->image()->height()/ (72.0 * activeView()->image()->yRes())), QPrinter::Inch); printJob->startPrinting(KisPrintJob::DeleteWhenDone); } else { delete printJob; } delete printDialog; } void KisMainWindow::slotFilePrintPreview() { if (!activeView()) return; KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return; /* Sets the startPrinting() slot to be blocking. The Qt print-preview dialog requires the printing to be completely blocking and only return when the full document has been printed. By default the KisPrintingDialog is non-blocking and multithreading, setting blocking to true will allow it to be used in the preview dialog */ printJob->setProperty("blocking", true); QPrintPreviewDialog *preview = new QPrintPreviewDialog(&printJob->printer(), this); printJob->setParent(preview); // will take care of deleting the job connect(preview, SIGNAL(paintRequested(QPrinter*)), printJob, SLOT(startPrinting())); preview->exec(); delete preview; } KisPrintJob* KisMainWindow::exportToPdf(QString pdfFileName) { if (!activeView()) return 0; if (!activeView()->document()) return 0; KoPageLayout pageLayout; pageLayout.width = 0; pageLayout.height = 0; pageLayout.topMargin = 0; pageLayout.bottomMargin = 0; pageLayout.leftMargin = 0; pageLayout.rightMargin = 0; if (pdfFileName.isEmpty()) { KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString defaultDir = group.readEntry("SavePdfDialog"); if (defaultDir.isEmpty()) defaultDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QUrl startUrl = QUrl::fromLocalFile(defaultDir); KisDocument* pDoc = d->activeView->document(); /** if document has a file name, take file name and replace extension with .pdf */ if (pDoc && pDoc->url().isValid()) { startUrl = pDoc->url(); QString fileName = startUrl.toLocalFile(); fileName = fileName.replace( QRegExp( "\\.\\w{2,5}$", Qt::CaseInsensitive ), ".pdf" ); startUrl = startUrl.adjusted(QUrl::RemoveFilename); startUrl.setPath(startUrl.path() + fileName ); } QPointer 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() { saveWindowState(); KEditToolBar edit(factory(), this); connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig())); (void) edit.exec(); applyToolBarLayout(); } void KisMainWindow::slotNewToolbarConfig() { applyMainWindowSettings(d->windowStateConfig); KXMLGUIFactory *factory = guiFactory(); Q_UNUSED(factory); // Check if there's an active view if (!d->activeView) return; plugActionList("toolbarlist", d->toolbarList); applyToolBarLayout(); } void KisMainWindow::slotToolbarToggled(bool toggle) { //dbgUI <<"KisMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true; // The action (sender) and the toolbar have the same name KToolBar * bar = toolBar(sender()->objectName()); if (bar) { if (toggle) { bar->show(); } else { bar->hide(); } if (d->activeView && d->activeView->document()) { saveWindowState(); } } else warnUI << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!"; } void KisMainWindow::viewFullscreen(bool fullScreen) { KisConfig cfg(false); cfg.setFullscreenMode(fullScreen); if (fullScreen) { setWindowState(windowState() | Qt::WindowFullScreen); // set } else { setWindowState(windowState() & ~Qt::WindowFullScreen); // reset } } void KisMainWindow::setMaxRecentItems(uint _number) { d->recentFiles->setMaxItems(_number); } void KisMainWindow::slotReloadFile() { KisDocument* document = d->activeView->document(); if (!document || document->url().isEmpty()) return; if (document->isModified()) { bool ok = QMessageBox::question(this, i18nc("@title:window", "Krita"), i18n("You will lose all changes made since your last save\n" "Do you want to continue?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes; if (!ok) return; } QUrl url = document->url(); saveWindowSettings(); if (!document->reload()) { QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Error: Could not reload this document")); } return; } QDockWidget* KisMainWindow::createDockWidget(KoDockFactoryBase* factory) { QDockWidget* dockWidget = 0; bool lockAllDockers = KisConfig(true).readEntry("LockAllDockerPanels", false); - if (!d->dockWidgetsMap.contains(factory->id())) { dockWidget = factory->createDockWidget(); // It is quite possible that a dock factory cannot create the dock; don't // do anything in that case. if (!dockWidget) { warnKrita << "Could not create docker for" << factory->id(); return 0; } dockWidget->setFont(KoDockRegistry::dockFont()); dockWidget->setObjectName(factory->id()); dockWidget->setParent(this); if (lockAllDockers) { if (dockWidget->titleBarWidget()) { dockWidget->titleBarWidget()->setVisible(false); } dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures); } if (dockWidget->widget() && dockWidget->widget()->layout()) dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1); Qt::DockWidgetArea side = Qt::RightDockWidgetArea; bool visible = true; switch (factory->defaultDockPosition()) { case KoDockFactoryBase::DockTornOff: dockWidget->setFloating(true); // position nicely? break; case KoDockFactoryBase::DockTop: side = Qt::TopDockWidgetArea; break; case KoDockFactoryBase::DockLeft: side = Qt::LeftDockWidgetArea; break; case KoDockFactoryBase::DockBottom: side = Qt::BottomDockWidgetArea; break; case KoDockFactoryBase::DockRight: side = Qt::RightDockWidgetArea; break; case KoDockFactoryBase::DockMinimized: default: side = Qt::RightDockWidgetArea; visible = false; } KConfigGroup group = d->windowStateConfig.group("DockWidget " + factory->id()); side = static_cast(group.readEntry("DockArea", static_cast(side))); if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea; addDockWidget(side, dockWidget); if (!visible) { dockWidget->hide(); } d->dockWidgetsMap.insert(factory->id(), dockWidget); } else { dockWidget = d->dockWidgetsMap[factory->id()]; } #ifdef Q_OS_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); } } /** * Qt has a weirdness, it has hardcoded shortcuts added to an action * in the window menu. We need to reset the shortcuts for that menu * to nothing, otherwise the shortcuts cannot be made configurable. * * See: https://bugs.kde.org/show_bug.cgi?id=352205 * https://bugs.kde.org/show_bug.cgi?id=375524 * https://bugs.kde.org/show_bug.cgi?id=398729 */ QMdiSubWindow *subWindow = d->mdiArea->currentSubWindow(); if (subWindow) { QMenu *menu = subWindow->systemMenu(); if (menu && menu->actions().size() == 8) { Q_FOREACH (QAction *action, menu->actions()) { action->setShortcut(QKeySequence()); } menu->actions().last()->deleteLater(); } } updateCaption(); d->actionManager()->updateGUI(); } void KisMainWindow::windowFocused() { /** * Notify selection manager so that it could update selection mask overlay */ if (viewManager() && viewManager()->selectionManager()) { viewManager()->selectionManager()->selectionChanged(); } KisPart *kisPart = KisPart::instance(); KisWindowLayoutManager *layoutManager = KisWindowLayoutManager::instance(); if (!layoutManager->primaryWorkspaceFollowsFocus()) return; QUuid primary = layoutManager->primaryWindowId(); if (primary.isNull()) return; if (d->id == primary) { if (!d->workspaceBorrowedBy.isNull()) { KisMainWindow *borrower = kisPart->windowById(d->workspaceBorrowedBy); if (!borrower) return; swapWorkspaces(this, borrower); } } else { if (d->workspaceBorrowedBy == primary) return; KisMainWindow *primaryWindow = kisPart->windowById(primary); if (!primaryWindow) return; swapWorkspaces(this, primaryWindow); } } void KisMainWindow::updateWindowMenu() { QMenu *menu = d->windowMenu->menu(); menu->clear(); menu->addAction(d->newWindow); menu->addAction(d->documentMenu); QMenu *docMenu = d->documentMenu->menu(); docMenu->clear(); 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()->resources(); auto m_this = this; for (auto &w : workspaces) { auto action = workspaceMenu->addAction(w->name()); connect(action, &QAction::triggered, this, [=]() { m_this->restoreWorkspace(w); }); } 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(); KisWorkspaceResourceSP 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)); } } bool showMdiArea = windows.count( ) > 0; if (!showMdiArea) { showWelcomeScreen(true); // see workaround in function in header // keep the recent file list updated when going back to welcome screen reloadRecentFileList(); d->welcomePage->populateRecentDocuments(); } // enable/disable the toolbox docker if there are no documents open Q_FOREACH (QObject* widget, children()) { if (widget->inherits("QDockWidget")) { QDockWidget* dw = static_cast(widget); if ( dw->objectName() == "ToolBox") { dw->setEnabled(showMdiArea); } } } 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(true); 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() { KisMainWindow *mainWindow = KisPart::instance()->createMainWindow(); mainWindow->initializeGeometry(); mainWindow->show(); } void KisMainWindow::closeCurrentWindow() { if (d->mdiArea->currentSubWindow()) { d->mdiArea->currentSubWindow()->close(); d->actionManager()->updateGUI(); } } void KisMainWindow::checkSanity() { // print error if the lcms engine is not available if (!KoColorSpaceEngineRegistry::instance()->contains("icc")) { // need to wait 1 event since exiting here would not work. m_errorMessage = i18n("The Krita LittleCMS color management plugin is not installed. Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); if (rserver->resources().isEmpty()) { m_errorMessage = i18n("Krita cannot find any brush presets! Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } } void KisMainWindow::showErrorAndDie() { QMessageBox::critical(0, i18nc("@title:window", "Installation error"), m_errorMessage); if (m_dieOnError) { exit(10); } } void KisMainWindow::showAboutApplication() { KisAboutApplication dlg(this); dlg.exec(); } QPointer KisMainWindow::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(); actionManager->createStandardAction(KStandardAction::New, this, SLOT(slotFileNew())); actionManager->createStandardAction(KStandardAction::Open, this, SLOT(slotFileOpen())); actionManager->createStandardAction(KStandardAction::Quit, this, SLOT(slotFileQuit())); actionManager->createStandardAction(KStandardAction::ConfigureToolbars, this, SLOT(slotConfigureToolbars())); d->fullScreenMode = actionManager->createStandardAction(KStandardAction::FullScreen, this, SLOT(viewFullscreen(bool))); d->recentFiles = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection()); connect(d->recentFiles, SIGNAL(recentListCleared()), this, SLOT(saveRecentFiles())); KSharedConfigPtr configPtr = KSharedConfig::openConfig(); d->recentFiles->loadEntries(configPtr->group("RecentFiles")); d->saveAction = actionManager->createStandardAction(KStandardAction::Save, this, SLOT(slotFileSave())); d->saveAction->setActivationFlags(KisAction::ACTIVE_IMAGE); d->saveActionAs = actionManager->createStandardAction(KStandardAction::SaveAs, this, SLOT(slotFileSaveAs())); d->saveActionAs->setActivationFlags(KisAction::ACTIVE_IMAGE); // d->printAction = actionManager->createStandardAction(KStandardAction::Print, this, SLOT(slotFilePrint())); // d->printAction->setActivationFlags(KisAction::ACTIVE_IMAGE); // d->printActionPreview = actionManager->createStandardAction(KStandardAction::PrintPreview, this, SLOT(slotFilePrintPreview())); // d->printActionPreview->setActivationFlags(KisAction::ACTIVE_IMAGE); d->undo = actionManager->createStandardAction(KStandardAction::Undo, this, SLOT(undo())); d->undo->setActivationFlags(KisAction::ACTIVE_IMAGE); d->redo = actionManager->createStandardAction(KStandardAction::Redo, this, SLOT(redo())); d->redo->setActivationFlags(KisAction::ACTIVE_IMAGE); d->undoActionsUpdateManager.reset(new KisUndoActionsUpdateManager(d->undo, d->redo)); d->undoActionsUpdateManager->setCurrentDocument(d->activeView ? d->activeView->document() : 0); // d->exportPdf = actionManager->createAction("file_export_pdf"); // connect(d->exportPdf, SIGNAL(triggered()), this, SLOT(exportToPdf())); d->importAnimation = actionManager->createAction("file_import_animation"); connect(d->importAnimation, SIGNAL(triggered()), this, SLOT(importAnimation())); d->closeAll = actionManager->createAction("file_close_all"); connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll())); // d->reloadFile = actionManager->createAction("file_reload_file"); // d->reloadFile->setActivationFlags(KisAction::CURRENT_IMAGE_MODIFIED); // connect(d->reloadFile, SIGNAL(triggered(bool)), this, SLOT(slotReloadFile())); d->importFile = actionManager->createAction("file_import_file"); connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile())); d->exportFile = actionManager->createAction("file_export_file"); connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile())); /* The following entry opens the document information dialog. Since the action is named so it intends to show data this entry should not have a trailing ellipses (...). */ d->showDocumentInfo = actionManager->createAction("file_documentinfo"); connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo())); d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this)); d->themeManager->registerThemeActions(actionCollection()); connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); connect(d->themeManager, SIGNAL(signalThemeChanged()), d->welcomePage, SLOT(slotUpdateThemeColors())); d->toggleDockers = actionManager->createAction("view_toggledockers"); KisConfig(true).showDockers(true); d->toggleDockers->setChecked(true); connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool))); actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu); actionCollection()->addAction("window", d->windowMenu); d->mdiCascade = actionManager->createAction("windows_cascade"); connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows())); d->mdiTile = actionManager->createAction("windows_tile"); connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows())); d->mdiNextWindow = actionManager->createAction("windows_next"); connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow())); d->mdiPreviousWindow = actionManager->createAction("windows_previous"); connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow())); d->newWindow = actionManager->createAction("view_newwindow"); connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow())); d->close = actionManager->createStandardAction(KStandardAction::Close, this, SLOT(closeCurrentWindow())); d->showSessionManager = actionManager->createAction("file_sessions"); connect(d->showSessionManager, SIGNAL(triggered(bool)), this, SLOT(slotShowSessionManager())); actionManager->createStandardAction(KStandardAction::Preferences, this, SLOT(slotPreferences())); for (int i = 0; i < 2; i++) { d->expandingSpacers[i] = new KisAction(i18n("Expanding Spacer")); d->expandingSpacers[i]->setDefaultWidget(new QWidget(this)); d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); actionManager->addAction(QString("expanding_spacer_%1").arg(i), d->expandingSpacers[i]); } } void KisMainWindow::applyToolBarLayout() { const bool isPlastiqueStyle = style()->objectName() == "plastique"; Q_FOREACH (KToolBar *toolBar, toolBars()) { toolBar->layout()->setSpacing(4); if (isPlastiqueStyle) { toolBar->setContentsMargins(0, 0, 0, 2); } //Hide text for buttons with an icon in the toolbar Q_FOREACH (QAction *ac, toolBar->actions()){ if (ac->icon().pixmap(QSize(1,1)).isNull() == false){ ac->setPriority(QAction::LowPriority); }else { ac->setIcon(QIcon()); } } } } void KisMainWindow::initializeGeometry() { // if the user didn's specify the geometry on the command line (does anyone do that still?), // we first figure out some good default size and restore the x,y position. See bug 285804Z. KConfigGroup cfg = d->windowStateConfig; QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray())); if (!restoreGeometry(geom)) { const int scnum = QApplication::desktop()->screenNumber(parentWidget()); QRect desk = 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 compensate for the window decs w = (deskWidth / 3) * 2; h = (desk.height() / 3) * 2; } else { w = desk.width(); h = desk.height(); } x += (desk.width() - w) / 2; y += (desk.height() - h) / 2; move(x,y); setGeometry(geometry().x(), geometry().y(), w, h); } d->fullScreenMode->setChecked(isFullScreen()); } void KisMainWindow::showManual() { QDesktopServices::openUrl(QUrl("https://docs.krita.org")); } void KisMainWindow::moveEvent(QMoveEvent *e) { /** * For checking if the display number has changed or not we should always use * positional overload, not using QWidget overload. Otherwise we might get * inconsistency, because screenNumber(widget) can return -1, but screenNumber(pos) * will always return the nearest screen. */ const int oldScreen = qApp->desktop()->screenNumber(e->oldPos()); const int newScreen = qApp->desktop()->screenNumber(e->pos()); if (oldScreen != newScreen) { emit screenChanged(); } if (d->screenConnectionsStore.isEmpty() || oldScreen != newScreen) { d->screenConnectionsStore.clear(); QScreen *newScreenObject = 0; #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) newScreenObject = qApp->screenAt(e->pos()); #else // TODO: i'm not sure if this pointer already has a correct value // by the moment we get the event. It might not work on older // versions of Qt newScreenObject = qApp->primaryScreen(); #endif if (newScreenObject) { d->screenConnectionsStore.addConnection(newScreenObject, SIGNAL(physicalDotsPerInchChanged(qreal)), this, SIGNAL(screenChanged())); } } } #include diff --git a/libs/ui/KisPart.cpp b/libs/ui/KisPart.cpp index b0c569cf13..2bd8b94caf 100644 --- a/libs/ui/KisPart.cpp +++ b/libs/ui/KisPart.cpp @@ -1,567 +1,563 @@ /* This file is part of the KDE project * Copyright (C) 1998-1999 Torben Weis * Copyright (C) 2000-2005 David Faure * Copyright (C) 2007-2008 Thorsten Zachmann * Copyright (C) 2010-2012 Boudewijn Rempt * Copyright (C) 2011 Inge Wallin * Copyright (C) 2015 Michael Abrahams * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisPart.h" #include "KoProgressProxy.h" #include #include #include #include #include #include #include #include #include "KisApplication.h" #include "KisMainWindow.h" #include "KisDocument.h" #include "KisView.h" #include "KisViewManager.h" #include "KisImportExportManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_config.h" #include "kis_shape_controller.h" #include "KisResourceServerProvider.h" #include "kis_animation_cache_populator.h" #include "kis_idle_watcher.h" #include "kis_image.h" #include "KisOpenPane.h" #include "kis_color_manager.h" #include "kis_action.h" #include "kis_action_registry.h" #include "KisSessionResource.h" Q_GLOBAL_STATIC(KisPart, s_instance) class Q_DECL_HIDDEN KisPart::Private { public: Private(KisPart *_part) : part(_part) , idleWatcher(2500) , animationCachePopulator(_part) { } ~Private() { } KisPart *part; QList > views; QList > mainWindows; QList > documents; KActionCollection *actionCollection{0}; KisIdleWatcher idleWatcher; KisAnimationCachePopulator animationCachePopulator; KisSessionResourceSP currentSession; bool closingSession{false}; QScopedPointer sessionManager; bool queryCloseDocument(KisDocument *document) { Q_FOREACH(auto view, views) { if (view && view->isVisible() && view->document() == document) { return view->queryClose(); } } return true; } }; KisPart* KisPart::instance() { return s_instance; } KisPart::KisPart() : d(new Private(this)) { // Preload all the resources in the background Q_UNUSED(KoResourceServerProvider::instance()); Q_UNUSED(KisResourceServerProvider::instance()); Q_UNUSED(KisColorManager::instance()); connect(this, SIGNAL(documentOpened(QString)), this, SLOT(updateIdleWatcherConnections())); connect(this, SIGNAL(documentClosed(QString)), this, SLOT(updateIdleWatcherConnections())); connect(KisActionRegistry::instance(), SIGNAL(shortcutsUpdated()), this, SLOT(updateShortcuts())); connect(&d->idleWatcher, SIGNAL(startedIdleMode()), &d->animationCachePopulator, SLOT(slotRequestRegeneration())); d->animationCachePopulator.slotRequestRegeneration(); } KisPart::~KisPart() { while (!d->documents.isEmpty()) { delete d->documents.takeFirst(); } while (!d->views.isEmpty()) { delete d->views.takeFirst(); } while (!d->mainWindows.isEmpty()) { delete d->mainWindows.takeFirst(); } delete d; } void KisPart::updateIdleWatcherConnections() { QVector images; Q_FOREACH (QPointer document, documents()) { if (document->image()) { images << document->image(); } } d->idleWatcher.setTrackedImages(images); } void KisPart::addDocument(KisDocument *document) { //dbgUI << "Adding document to part list" << document; Q_ASSERT(document); if (!d->documents.contains(document)) { d->documents.append(document); emit documentOpened('/'+objectName()); emit sigDocumentAdded(document); connect(document, SIGNAL(sigSavingFinished()), SLOT(slotDocumentSaved())); } } QList > KisPart::documents() const { return d->documents; } KisDocument *KisPart::createDocument() const { KisDocument *doc = new KisDocument(); return doc; } int KisPart::documentCount() const { return d->documents.size(); } void KisPart::removeDocument(KisDocument *document) { d->documents.removeAll(document); emit documentClosed('/'+objectName()); emit sigDocumentRemoved(document->url().toLocalFile()); document->deleteLater(); } KisMainWindow *KisPart::createMainWindow(QUuid id) { KisMainWindow *mw = new KisMainWindow(id); dbgUI <<"mainWindow" << (void*)mw << "added to view" << this; d->mainWindows.append(mw); emit sigWindowAdded(mw); return mw; } KisView *KisPart::createView(KisDocument *document, KoCanvasResourceProvider *resourceManager, KActionCollection *actionCollection, QWidget *parent) { // If creating the canvas fails, record this and disable OpenGL next time KisConfig cfg(false); KConfigGroup grp( KSharedConfig::openConfig(), "crashprevention"); if (grp.readEntry("CreatingCanvas", false)) { cfg.setUseOpenGL(false); } if (cfg.canvasState() == "OPENGL_FAILED") { cfg.setUseOpenGL(false); } grp.writeEntry("CreatingCanvas", true); grp.sync(); QApplication::setOverrideCursor(Qt::WaitCursor); KisView *view = new KisView(document, resourceManager, actionCollection, parent); QApplication::restoreOverrideCursor(); // Record successful canvas creation grp.writeEntry("CreatingCanvas", false); grp.sync(); addView(view); return view; } void KisPart::addView(KisView *view) { if (!view) return; if (!d->views.contains(view)) { d->views.append(view); } emit sigViewAdded(view); } void KisPart::removeView(KisView *view) { if (!view) return; /** * HACK ALERT: we check here explicitly if the document (or main * window), is saving the stuff. If we close the * document *before* the saving is completed, a crash * will happen. */ KIS_ASSERT_RECOVER_RETURN(!view->mainWindow()->hackIsSaving()); emit sigViewRemoved(view); QPointer doc = view->document(); d->views.removeAll(view); if (doc) { bool found = false; Q_FOREACH (QPointer view, d->views) { if (view && view->document() == doc) { found = true; break; } } if (!found) { removeDocument(doc); } } } QList > KisPart::views() const { return d->views; } int KisPart::viewCount(KisDocument *doc) const { if (!doc) { return d->views.count(); } else { int count = 0; Q_FOREACH (QPointer view, d->views) { if (view && view->isVisible() && view->document() == doc) { count++; } } return count; } } bool KisPart::closingSession() const { return d->closingSession; } bool KisPart::closeSession(bool keepWindows) { d->closingSession = true; Q_FOREACH(auto document, d->documents) { if (!d->queryCloseDocument(document.data())) { d->closingSession = false; return false; } } if (d->currentSession) { KisConfig kisCfg(false); if (kisCfg.saveSessionOnQuit(false)) { d->currentSession->storeCurrentWindows(); d->currentSession->save(); KConfigGroup cfg = KSharedConfig::openConfig()->group("session"); cfg.writeEntry("previousSession", d->currentSession->name()); } d->currentSession = nullptr; } if (!keepWindows) { Q_FOREACH (auto window, d->mainWindows) { window->close(); } if (d->sessionManager) { d->sessionManager->close(); } } d->closingSession = false; return true; } void KisPart::slotDocumentSaved() { KisDocument *doc = qobject_cast(sender()); emit sigDocumentSaved(doc->url().toLocalFile()); } void KisPart::removeMainWindow(KisMainWindow *mainWindow) { dbgUI <<"mainWindow" << (void*)mainWindow <<"removed from doc" << this; if (mainWindow) { d->mainWindows.removeAll(mainWindow); } } const QList > &KisPart::mainWindows() const { return d->mainWindows; } int KisPart::mainwindowCount() const { return d->mainWindows.count(); } KisMainWindow *KisPart::currentMainwindow() const { QWidget *widget = qApp->activeWindow(); KisMainWindow *mainWindow = qobject_cast(widget); while (!mainWindow && widget) { widget = widget->parentWidget(); mainWindow = qobject_cast(widget); } if (!mainWindow && mainWindows().size() > 0) { mainWindow = mainWindows().first(); } return mainWindow; } KisMainWindow * KisPart::windowById(QUuid id) const { Q_FOREACH(QPointer mainWindow, d->mainWindows) { if (mainWindow->id() == id) { return mainWindow; } } return nullptr; } KisIdleWatcher* KisPart::idleWatcher() const { return &d->idleWatcher; } KisAnimationCachePopulator* KisPart::cachePopulator() const { return &d->animationCachePopulator; } void KisPart::openExistingFile(const QUrl &url) { // TODO: refactor out this method! KisMainWindow *mw = currentMainwindow(); KIS_SAFE_ASSERT_RECOVER_RETURN(mw); mw->openDocument(url, KisMainWindow::None); } void KisPart::updateShortcuts() { // Update any non-UI actionCollections. That includes: - // - Shortcuts called inside of tools - // - Perhaps other things? - KoToolManager::instance()->updateToolShortcuts(); - // Now update the UI actions. Q_FOREACH (KisMainWindow *mainWindow, d->mainWindows) { KActionCollection *ac = mainWindow->actionCollection(); ac->updateShortcuts(); // Loop through mainWindow->actionCollections() to modify tooltips // so that they list shortcuts at the end in parentheses Q_FOREACH ( QAction* action, ac->actions()) { // Remove any existing suffixes from the tooltips. // Note this regexp starts with a space, e.g. " (Ctrl-a)" QString strippedTooltip = action->toolTip().remove(QRegExp("\\s\\(.*\\)")); // Now update the tooltips with the new shortcut info. - if(action->shortcut() == QKeySequence(0)) + if (action->shortcut() == QKeySequence(0)) action->setToolTip(strippedTooltip); else action->setToolTip( strippedTooltip + " (" + action->shortcut().toString() + ")"); } } } void KisPart::openTemplate(const QUrl &url) { qApp->setOverrideCursor(Qt::BusyCursor); KisDocument *document = createDocument(); bool ok = document->loadNativeFormat(url.toLocalFile()); document->setModified(false); document->undoStack()->clear(); if (ok) { QString mimeType = KisMimeDatabase::mimeTypeForFile(url.toLocalFile()); // in case this is a open document template remove the -template from the end mimeType.remove( QRegExp( "-template$" ) ); document->setMimeTypeAfterLoading(mimeType); document->resetURL(); } else { if (document->errorMessage().isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not create document from template\n%1", document->localFilePath())); } else { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not create document from template\n%1\nReason: %2", document->localFilePath(), document->errorMessage())); } delete document; return; } addDocument(document); KisMainWindow *mw = currentMainwindow(); mw->addViewAndNotifyLoadingCompleted(document); KisOpenPane *pane = qobject_cast(sender()); if (pane) { pane->hide(); pane->deleteLater(); } qApp->restoreOverrideCursor(); } void KisPart::addRecentURLToAllMainWindows(QUrl url) { // Add to recent actions list in our mainWindows Q_FOREACH (KisMainWindow *mainWindow, d->mainWindows) { mainWindow->addRecentURL(url); } } void KisPart::startCustomDocument(KisDocument* doc) { addDocument(doc); KisMainWindow *mw = currentMainwindow(); KisOpenPane *pane = qobject_cast(sender()); if (pane) { pane->hide(); pane->deleteLater(); } mw->addViewAndNotifyLoadingCompleted(doc); } KisInputManager* KisPart::currentInputManager() { KisMainWindow *mw = currentMainwindow(); KisViewManager *manager = mw ? mw->viewManager() : 0; return manager ? manager->inputManager() : 0; } void KisPart::showSessionManager() { if (d->sessionManager.isNull()) { d->sessionManager.reset(new KisSessionManagerDialog()); } d->sessionManager->show(); d->sessionManager->activateWindow(); } void KisPart::startBlankSession() { KisMainWindow *window = createMainWindow(); window->initializeGeometry(); window->show(); } bool KisPart::restoreSession(const QString &sessionName) { if (sessionName.isNull()) return false; KoResourceServer *rserver = KisResourceServerProvider::instance()->sessionServer(); KisSessionResourceSP session = rserver->resourceByName(sessionName); if (!session || !session->valid()) return false; session->restore(); return true; } void KisPart::setCurrentSession(KisSessionResourceSP session) { d->currentSession = session; } diff --git a/libs/ui/KisViewManager.cpp b/libs/ui/KisViewManager.cpp index 9c57461f1c..7ab68dc6df 100644 --- a/libs/ui/KisViewManager.cpp +++ b/libs/ui/KisViewManager.cpp @@ -1,1409 +1,1443 @@ /* * 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 "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 "KisDecorationsManager.h" #include #include "kis_paintop_box.h" #include #include "KisPart.h" #include "KisPrintJob.h" #include #include "KisResourceServerProvider.h" #include "kis_selection.h" #include "kis_selection_mask.h" #include "kis_selection_manager.h" #include "kis_shape_controller.h" #include "kis_shape_layer.h" #include #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_icon_utils.h" #include "kis_guides_manager.h" #include "kis_derived_resources.h" #include "dialogs/kis_delayed_save_dialog.h" #include #include "kis_signals_blocker.h" 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) , actionAuthor(0) , showPixelGrid(0) { KisViewManager::initializeResourceManager(&canvasResourceManager); } public: KisFilterManager filterManager; KisAction *createTemplate; KisAction *createCopy; KisAction *saveIncremental; KisAction *saveIncrementalBackup; KisAction *openResourcesDirectory; KisAction *rotateCanvasRight; KisAction *rotateCanvasLeft; KisAction *resetCanvasRotation; KisAction *wrapAroundAction; KisAction *levelOfDetailAction; KisAction *showRulersAction; KisAction *rulersTrackMouseAction; KisAction *zoomTo100pct; KisAction *zoomIn; KisAction *zoomOut; KisAction *softProof; KisAction *gamutCheck; + KisAction *toggleFgBg; + KisAction *resetFgBg; KisSelectionManager selectionManager; KisGuidesManager guidesManager; KisStatusBar statusBar; QPointer persistentImageProgressUpdater; QScopedPointer persistentUnthreadedProgressUpdaterRouter; QPointer persistentUnthreadedProgressUpdater; KisControlFrame controlFrame; KisNodeManager nodeManager; KisImageManager imageManager; KisGridManager gridManager; KisCanvasControlsManager canvasControlsManager; KisDecorationsManager paintingAssistantsManager; BlockingUserInputEventFilter blockingEventFilter; KisActionManager actionManager; QMainWindow* mainWindow; QPointer savedFloatingMessage; bool showFloatingMessage; QPointer currentImageView; KisCanvasResourceProvider canvasResourceProvider; KoCanvasResourceProvider canvasResourceManager; KisSignalCompressor guiUpdateCompressor; KActionCollection *actionCollection; KisMirrorManager mirrorManager; KisInputManager inputManager; KisSignalAutoConnectionsStore viewConnections; KSelectAction *actionAuthor; // Select action for author profile. KisAction *showPixelGrid; QByteArray canvasState; #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) QFlags 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(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*))); connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions())); connect(KisConfigNotifier::instance(), SIGNAL(pixelGridModeChanged()), SLOT(slotUpdatePixelGridAction())); KisInputProfileManager::instance()->loadProfiles(); KisConfig cfg(true); d->showFloatingMessage = cfg.showCanvasMessages(); const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KoColor foreground(Qt::black, cs); d->canvasResourceProvider.setFGColor(cfg.readKoColor("LastForeGroundColor",foreground)); KoColor background(Qt::white, cs); d->canvasResourceProvider.setBGColor(cfg.readKoColor("LastBackGroundColor",background)); } KisViewManager::~KisViewManager() { KisConfig cfg(false); if (resourceProvider() && resourceProvider()->currentPreset()) { cfg.writeKoColor("LastForeGroundColor",resourceProvider()->fgColor()); cfg.writeKoColor("LastBackGroundColor",resourceProvider()->bgColor()); } cfg.writeEntry("baseLength", KoResourceItemChooserSync::instance()->baseLength()); delete d; } void KisViewManager::initializeResourceManager(KoCanvasResourceProvider *resourceManager) { resourceManager->addDerivedResourceConverter(toQShared(new KisCompositeOpResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisEffectiveCompositeOpResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisOpacityResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisFlowResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisSizeResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisLodAvailabilityResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisLodSizeThresholdResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisLodSizeThresholdSupportedResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisEraserModeResourceConverter)); resourceManager->addResourceUpdateMediator(toQShared(new KisPresetUpdateMediator)); } KActionCollection *KisViewManager::actionCollection() const { return d->actionCollection; } void KisViewManager::slotViewAdded(KisView *view) { // WARNING: this slot is called even when a view from another main windows is added! // Don't expect \p view be a child of this view manager! Q_UNUSED(view); if (viewCount() == 0) { d->statusBar.showAllStatusBarItems(); } } void KisViewManager::slotViewRemoved(KisView *view) { // WARNING: this slot is called even when a view from another main windows is removed! // Don't expect \p view be a child of this view manager! Q_UNUSED(view); if (viewCount() == 0) { d->statusBar.hideAllStatusBarItems(); } KisConfig cfg(false); if (resourceProvider() && resourceProvider()->currentPreset()) { cfg.writeEntry("LastPreset", resourceProvider()->currentPreset()->name()); } } void KisViewManager::setCurrentView(KisView *view) { bool first = true; if (d->currentImageView) { d->currentImageView->notifyCurrentStateChanged(false); d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor)); first = false; KisDocument* doc = d->currentImageView->document(); if (doc) { doc->image()->compositeProgressProxy()->removeProxy(d->persistentImageProgressUpdater); doc->disconnect(this); } d->currentImageView->canvasController()->proxyObject->disconnect(&d->statusBar); d->viewConnections.clear(); } QPointer 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(); if (KisConfig(true).readEntry("EnablePositionLabel", false)) { connect(d->currentImageView->canvasController()->proxyObject, SIGNAL(documentMousePositionChanged(QPointF)), &d->statusBar, SLOT(documentMousePositionChanged(QPointF))); } // Restore the last used brush preset, color and background color. if (first) { KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); QString defaultPresetName = "basic_tip_default"; bool foundTip = false; for (int i=0; iresourceCount(); i++) { KisPaintOpPresetSP resource = rserver->resources().at(i); if (resource->name().toLower().contains("basic_tip_default")) { defaultPresetName = resource->name(); foundTip = true; } else if (foundTip == false && (resource->name().toLower().contains("default") || resource->filename().toLower().contains("default"))) { defaultPresetName = resource->name(); foundTip = true; } } KisConfig cfg(true); QString lastPreset = cfg.readEntry("LastPreset", defaultPresetName); KisPaintOpPresetSP preset = rserver->resourceByName(lastPreset); if (!preset) { preset = rserver->resourceByName(defaultPresetName); } if (!preset && !rserver->resources().isEmpty()) { preset = rserver->resources().first(); } if (preset) { paintOpBox()->restoreResource(preset); } } 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(QPointF,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; } 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) { KisSelectionMaskSP mask = layer->selectionMask(); if (mask) { return mask->isEditable(); } } // global selection is always editable return true; } KisUndoAdapter * KisViewManager::undoAdapter() { if (!document()) return 0; KisImageWSP image = document()->image(); Q_ASSERT(image); return image->undoAdapter(); } void KisViewManager::createActions() { KisConfig cfg(true); d->saveIncremental = actionManager()->createAction("save_incremental_version"); connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental())); d->saveIncrementalBackup = actionManager()->createAction("save_incremental_backup"); connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup())); connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved())); d->saveIncremental->setEnabled(false); d->saveIncrementalBackup->setEnabled(false); KisAction *tabletDebugger = actionManager()->createAction("tablet_debugger"); connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger())); d->createTemplate = actionManager()->createAction("create_template"); connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate())); d->createCopy = actionManager()->createAction("create_copy"); connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy())); d->openResourcesDirectory = actionManager()->createAction("open_resources_directory"); connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory())); d->rotateCanvasRight = actionManager()->createAction("rotate_canvas_right"); d->rotateCanvasLeft = actionManager()->createAction("rotate_canvas_left"); d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation"); d->wrapAroundAction = actionManager()->createAction("wrap_around_mode"); d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode"); d->softProof = actionManager()->createAction("softProof"); d->gamutCheck = actionManager()->createAction("gamutCheck"); KisAction *tAction = actionManager()->createAction("showStatusBar"); tAction->setChecked(cfg.showStatusBar()); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool))); tAction = actionManager()->createAction("view_show_canvas_only"); tAction->setChecked(false); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(switchCanvasOnly(bool))); //Workaround, by default has the same shortcut as mirrorCanvas KisAction *a = dynamic_cast(actionCollection()->action("format_italic")); if (a) { a->setDefaultShortcut(QKeySequence()); } a = actionManager()->createAction("edit_blacklist_cleanup"); connect(a, SIGNAL(triggered()), this, SLOT(slotBlacklistCleanup())); actionManager()->createAction("ruler_pixel_multiple2"); d->showRulersAction = actionManager()->createAction("view_ruler"); d->showRulersAction->setChecked(cfg.showRulers()); connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool))); d->rulersTrackMouseAction = actionManager()->createAction("rulers_track_mouse"); d->rulersTrackMouseAction->setChecked(cfg.rulersTrackMouse()); connect(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), SLOT(slotSaveRulersTrackMouseState(bool))); d->zoomTo100pct = actionManager()->createAction("zoom_to_100pct"); d->zoomIn = actionManager()->createStandardAction(KStandardAction::ZoomIn, 0, ""); d->zoomOut = actionManager()->createStandardAction(KStandardAction::ZoomOut, 0, ""); d->actionAuthor = new KSelectAction(KisIconUtils::loadIcon("im-user"), i18n("Active Author Profile"), this); connect(d->actionAuthor, SIGNAL(triggered(QString)), this, SLOT(changeAuthorProfile(QString))); actionCollection()->addAction("settings_active_author", d->actionAuthor); slotUpdateAuthorProfileActions(); d->showPixelGrid = actionManager()->createAction("view_pixel_grid"); slotUpdatePixelGridAction(); + + d->toggleFgBg = actionManager()->createAction("toggle_fg_bg"); + connect(d->toggleFgBg, SIGNAL(triggered(bool)), this, SLOT(slotToggleFgBg())); + + d->resetFgBg = actionManager()->createAction("reset_fg_bg"); + connect(d->resetFgBg, SIGNAL(triggered(bool)), this, SLOT(slotResetFgBg())); + } void KisViewManager::setupManagers() { // Create the managers for filters, selections, layers etc. // XXX: When the currentlayer changes, call updateGUI on all // managers d->filterManager.setup(actionCollection(), actionManager()); d->selectionManager.setup(actionManager()); d->guidesManager.setup(actionManager()); d->nodeManager.setup(actionCollection(), actionManager()); d->imageManager.setup(actionManager()); d->gridManager.setup(actionManager()); d->paintingAssistantsManager.setup(actionManager()); d->canvasControlsManager.setup(actionManager()); d->mirrorManager.setup(actionCollection()); } void KisViewManager::updateGUI() { d->guiUpdateCompressor.start(); } void KisViewManager::slotBlacklistCleanup() { KisDlgBlacklistCleanup dialog; dialog.exec(); } KisNodeManager * KisViewManager::nodeManager() const { return &d->nodeManager; } KisActionManager* KisViewManager::actionManager() const { return &d->actionManager; } KisGridManager * KisViewManager::gridManager() const { return &d->gridManager; } KisGuidesManager * KisViewManager::guidesManager() const { return &d->guidesManager; } KisDocument *KisViewManager::document() const { if (d->currentImageView && d->currentImageView->document()) { return d->currentImageView->document(); } return 0; } int KisViewManager::viewCount() const { KisMainWindow *mw = qobject_cast(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; if (document()->url().isEmpty()) { KisMainWindow *mw = qobject_cast(d->mainWindow); mw->saveDocument(document(), true, false); return; } bool foundVersion; bool fileAlreadyExists; bool isBackup; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // Find current version filenames // v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well // Considering our incremental version and backup scheme, format is filename_001~001.ext QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); foundVersion = matches.at(0).isEmpty() ? false : true; // Ensure compatibility with Save Incremental Backup // If this regex is not kept separate, the entire algorithm needs modification; // It's simpler to just add this. QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regexAux.indexIn(fileName); // Perform the search QStringList matchesAux = regexAux.capturedTexts(); isBackup = matchesAux.at(0).isEmpty() ? false : true; // If the filename has a version, prepare it for incrementation if (foundVersion) { version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "_" } else { // TODO: this will not work with files extensions like jp2 // ...else, simply add a version to it so the next loop works QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(fileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(version); extensionPlusVersion.prepend("_"); fileName.replace(regex2, extensionPlusVersion); } // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("_"); if (!letter.isNull()) newVersion.append(letter); if (isBackup) { newVersion.append("~"); } else { newVersion.append("."); } fileName.replace(regex, newVersion); fileAlreadyExists = QFile(fileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } document()->setFileBatchMode(true); document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true); document()->setFileBatchMode(false); if (mainWindow()) { mainWindow()->updateCaption(); } } void KisViewManager::slotSaveIncrementalBackup() { if (!document()) return; if (document()->url().isEmpty()) { KisMainWindow *mw = qobject_cast(d->mainWindow); mw->saveDocument(document(), true, false); return; } bool workingOnBackup; bool fileAlreadyExists; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // First, discover if working on a backup file, or a normal file QRegExp regex("~\\d{1,4}[.]|~\\d{1,4}[a-z][.]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); workingOnBackup = matches.at(0).isEmpty() ? false : true; if (workingOnBackup) { // Try to save incremental version (of backup), use letter for alt versions version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "~" // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); QString backupFileName = document()->localFilePath(); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("~"); if (!letter.isNull()) newVersion.append(letter); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true); if (mainWindow()) mainWindow()->updateCaption(); } else { // if NOT working on a backup... // Navigate directory searching for latest backup version, ignore letters const quint8 HARDCODED_DIGIT_COUNT = 3; QString baseNewVersion = "000"; QString backupFileName = document()->localFilePath(); QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(backupFileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(baseNewVersion); extensionPlusVersion.prepend("~"); backupFileName.replace(regex2, extensionPlusVersion); // Save version with 1 number higher than the highest version found ignoring letters do { newVersion = baseNewVersion; newVersion.prepend("~"); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { // Prepare the base for new version filename, increment by 1 int intVersion = baseNewVersion.toInt(0); ++intVersion; baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) { baseNewVersion.prepend("0"); } } } while (fileAlreadyExists); // Save both as backup and on current file for interapplication workflow document()->setFileBatchMode(true); QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true); document()->setFileBatchMode(false); if (mainWindow()) mainWindow()->updateCaption(); } } void KisViewManager::disableControls() { // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel // this is for Bug 250944 // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool // see KisToolFreehand::initPaint() and endPaint() d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter); Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) { child->installEventFilter(&d->blockingEventFilter); } } void KisViewManager::enableControls() { d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter); Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) { child->removeEventFilter(&d->blockingEventFilter); } } void KisViewManager::showStatusBar(bool toggled) { KisMainWindow *mw = mainWindow(); if(mw && mw->statusBar()) { mw->statusBar()->setVisible(toggled); KisConfig cfg(false); cfg.setShowStatusBar(toggled); } } void KisViewManager::switchCanvasOnly(bool toggled) { KisConfig cfg(false); KisMainWindow* main = mainWindow(); if(!main) { dbgUI << "Unable to switch to canvas-only mode, main window not found"; return; } 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) { QObjectList objects; objects.append(dock); while (!objects.isEmpty()) { QObject* object = objects.takeFirst(); objects.append(object->children()); KisIconUtils::updateIconCommon(object); } } } } void KisViewManager::initializeStatusBarVisibility() { KisConfig cfg(true); d->mainWindow->statusBar()->setVisible(cfg.showStatusBar()); } void KisViewManager::guiUpdateTimeout() { d->nodeManager.updateGUI(); d->selectionManager.updateGUI(); d->filterManager.updateGUI(); if (zoomManager()) { zoomManager()->updateGUI(); } d->gridManager.updateGUI(); d->actionManager.updateGUI(); } void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment) { if (!d->currentImageView) return; d->currentImageView->showFloatingMessage(message, icon, timeout, priority, alignment); emit floatingMessageRequested(message, icon.name()); } KisMainWindow *KisViewManager::mainWindow() const { return qobject_cast(d->mainWindow); } void KisViewManager::showHideScrollbars() { if (!d->currentImageView) return; if (!d->currentImageView->canvasController()) return; KisConfig cfg(true); bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked(); if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } } void KisViewManager::slotSaveShowRulersState(bool value) { KisConfig cfg(false); cfg.setShowRulers(value); } void KisViewManager::slotSaveRulersTrackMouseState(bool value) { KisConfig cfg(false); cfg.setRulersTrackMouse(value); } void KisViewManager::setShowFloatingMessage(bool show) { d->showFloatingMessage = show; } void KisViewManager::changeAuthorProfile(const QString &profileName) { KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author"); if (profileName.isEmpty() || profileName == i18nc("choice for author profile", "Anonymous")) { appAuthorGroup.writeEntry("active-profile", ""); } else { appAuthorGroup.writeEntry("active-profile", profileName); } appAuthorGroup.sync(); Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) { doc->documentInfo()->updateParameters(); } } void KisViewManager::slotUpdateAuthorProfileActions() { Q_ASSERT(d->actionAuthor); if (!d->actionAuthor) { return; } d->actionAuthor->clear(); d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous")); KConfigGroup authorGroup(KSharedConfig::openConfig(), "Author"); QStringList profiles = authorGroup.readEntry("profile-names", QStringList()); QString authorInfo = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/authorinfo/"; QStringList filters = QStringList() << "*.authorinfo"; QDir dir(authorInfo); Q_FOREACH(QString entry, dir.entryList(filters)) { int ln = QString(".authorinfo").size(); entry.chop(ln); if (!profiles.contains(entry)) { profiles.append(entry); } } Q_FOREACH (const QString &profile , profiles) { d->actionAuthor->addAction(profile); } KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author"); QString profileName = appAuthorGroup.readEntry("active-profile", ""); if (profileName == "anonymous" || profileName.isEmpty()) { d->actionAuthor->setCurrentItem(0); } else if (profiles.contains(profileName)) { d->actionAuthor->setCurrentAction(profileName); } } void KisViewManager::slotUpdatePixelGridAction() { KIS_SAFE_ASSERT_RECOVER_RETURN(d->showPixelGrid); KisSignalsBlocker b(d->showPixelGrid); KisConfig cfg(true); d->showPixelGrid->setChecked(cfg.pixelGridEnabled() && cfg.useOpenGL()); } void KisViewManager::slotActivateTransformTool() { if(KoToolManager::instance()->activeToolId() == "KisToolTransform") { KoToolBase* tool = KoToolManager::instance()->toolById(canvasBase(), "KisToolTransform"); QSet dummy; // Start a new stroke tool->deactivate(); tool->activate(KoToolBase::DefaultActivation, dummy); } KoToolManager::instance()->switchToolRequested("KisToolTransform"); } + +void KisViewManager::slotToggleFgBg() +{ + + KoColor newFg = d->canvasResourceManager.backgroundColor(); + KoColor newBg = d->canvasResourceManager.foregroundColor(); + + /** + * NOTE: Some of color selectors do not differentiate foreground + * and background colors, so if one wants them to end up + * being set up to foreground color, it should be set the + * last. + */ + d->canvasResourceManager.setBackgroundColor(newBg); + d->canvasResourceManager.setForegroundColor(newFg); +} + +void KisViewManager::slotResetFgBg() +{ + // see a comment in slotToggleFgBg() + d->canvasResourceManager.setBackgroundColor(KoColor(Qt::white, KoColorSpaceRegistry::instance()->rgb8())); + d->canvasResourceManager.setForegroundColor(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8())); +} + + diff --git a/libs/ui/KisViewManager.h b/libs/ui/KisViewManager.h index bf5766f599..030aeac59e 100644 --- a/libs/ui/KisViewManager.h +++ b/libs/ui/KisViewManager.h @@ -1,260 +1,261 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_GUI_CLIENT_H #define KIS_GUI_CLIENT_H #include #include #include #include #include #include #include #include "kis_floating_message.h" class QPoint; class KisView; class KisCanvas2; class KisCanvasResourceProvider; class KisDocument; class KisFilterManager; class KisGridManager; class KisGuidesManager; class KisImageManager; class KisNodeManager; class KisDecorationsManager; class KisPaintopBox; class KisSelectionManager; class KisStatusBar; class KisUndoAdapter; class KisZoomManager; class KisPaintopBox; class KisActionManager; class KisInputManager; class KoUpdater; class KoProgressUpdater; /** * KisViewManager manages the collection of views shown in a single mainwindow. */ class KRITAUI_EXPORT KisViewManager : public QObject { Q_OBJECT public: /** * Construct a new view on the krita document. * @param document the document we show. * @param parent a parent widget we show ourselves in. */ KisViewManager(QWidget *parent, KActionCollection *actionCollection); ~KisViewManager() override; /** * Retrieves the entire action collection. */ virtual KActionCollection* actionCollection() const; public: // Krita specific interfaces void setCurrentView(KisView *view); /// Return the image this view is displaying KisImageWSP image() const; KoZoomController *zoomController() const; /// The resource provider contains all per-view settings, such as /// current color, current paint op etc. KisCanvasResourceProvider *resourceProvider(); /// Return the canvasbase class KisCanvas2 *canvasBase() const; /// Return the actual widget that is displaying the current image QWidget* canvas() const; /// Return the wrapper class around the statusbar KisStatusBar *statusBar() const; KisPaintopBox* paintOpBox() const; /// create a new progress updater QPointer createUnthreadedUpdater(const QString &name); QPointer createThreadedUpdater(const QString &name); /// The selection manager handles everything action related to /// selections. KisSelectionManager *selectionManager(); /// The node manager handles everything about nodes KisNodeManager *nodeManager() const; KisActionManager *actionManager() const; /** * Convenience method to get at the active node, which may be * a layer or a mask or a selection */ KisNodeSP activeNode(); /// Convenience method to get at the active layer KisLayerSP activeLayer(); /// Convenience method to get at the active paint device KisPaintDeviceSP activeDevice(); /// The filtermanager handles everything action-related to filters KisFilterManager *filterManager(); /// The image manager handles everything action-related to the /// current image KisImageManager *imageManager(); /// Filters events and sends them to canvas actions KisInputManager *inputManager() const; /// Convenience method to get at the active selection (the /// selection of the current layer, or, if that does not exist, /// the global selection. KisSelectionSP selection(); /// Checks if the current global or local selection is editable bool selectionEditable(); /// The undo adapter is used to add commands to the undo stack KisUndoAdapter *undoAdapter(); KisDocument *document() const; int viewCount() const; /** * @brief blockUntilOperationsFinished blocks the GUI of the application until execution * of actions on \p image is finished * @param image the image which we should wait for * @return true if the image has finished execution of the actions, false if * the user cancelled operation */ bool blockUntilOperationsFinished(KisImageSP image); /** * @brief blockUntilOperationsFinished blocks the GUI of the application until execution * of actions on \p image is finished. Does *not* provide a "Cancel" button. So the * user is forced to wait. * @param image the image which we should wait for */ void blockUntilOperationsFinishedForced(KisImageSP image); public: KisGridManager * gridManager() const; KisGuidesManager * guidesManager() const; /// disable and enable toolbar controls. used for disabling them during painting. void enableControls(); void disableControls(); /// shows a floating message in the top right corner of the canvas void showFloatingMessage(const QString &message, const QIcon& icon, int timeout = 4500, KisFloatingMessage::Priority priority = KisFloatingMessage::Medium, int alignment = Qt::AlignCenter | Qt::TextWordWrap); /// @return the KoMaindow this view is in, or 0 KisMainWindow *mainWindow() const; /// The QMainWindow associated with this view. This is most likely going to be shell(), but /// when running as Gemini or Sketch, this will be set to the applications' own QMainWindow. /// This can be checked by qobject_casting to KisMainWindow to check the difference. QMainWindow* qtMainWindow() const; /// The mainWindow function will return the shell() value, unless this function is called /// with a non-null value. To make it return shell() again, simply pass null to this function. void setQtMainWindow(QMainWindow* newMainWindow); static void initializeResourceManager(KoCanvasResourceProvider *resourceManager); public Q_SLOTS: void switchCanvasOnly(bool toggled); void setShowFloatingMessage(bool show); void showHideScrollbars(); /// Visit all managers to update gui elements, e.g. enable / disable actions. /// This is heavy-duty call, so it uses a compressor. void updateGUI(); /// Update the style of all the icons void updateIcons(); void slotViewAdded(KisView *view); void slotViewRemoved(KisView *view); void slotActivateTransformTool(); // Change and update author void changeAuthorProfile(const QString &profileName); void slotUpdateAuthorProfileActions(); Q_SIGNALS: void floatingMessageRequested(const QString &message, const QString &iconName); /** * @brief viewChanged * sent out when the view has changed. */ void viewChanged(); private Q_SLOTS: void slotBlacklistCleanup(); void slotCreateTemplate(); void slotCreateCopy(); void slotDocumentSaved(); void slotSaveIncremental(); void slotSaveIncrementalBackup(); void showStatusBar(bool toggled); void toggleTabletLogger(); void openResourcesDirectory(); void initializeStatusBarVisibility(); void guiUpdateTimeout(); void slotUpdatePixelGridAction(); void slotSaveShowRulersState(bool value); void slotSaveRulersTrackMouseState(bool value); - + void slotToggleFgBg(); + void slotResetFgBg(); private: void createActions(); void setupManagers(); /// The zoommanager handles everything action-related to zooming KisZoomManager * zoomManager(); private: class KisViewManagerPrivate; KisViewManagerPrivate * const d; }; #endif diff --git a/libs/ui/dialogs/kis_dlg_preferences.cc b/libs/ui/dialogs/kis_dlg_preferences.cc index db8d69c67c..667adae9f0 100644 --- a/libs/ui/dialogs/kis_dlg_preferences.cc +++ b/libs/ui/dialogs/kis_dlg_preferences.cc @@ -1,1468 +1,1468 @@ /* * 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 #include #include "KisProofingConfiguration.h" #include "KoColorConversionTransformation.h" #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" #include "kis_action_registry.h" #include "kis_canvas_resource_provider.h" #include "kis_clipboard.h" #include "kis_color_manager.h" #include "kis_config.h" #include "kis_cursor.h" #include "kis_image_config.h" #include "kis_preference_set_registry.h" #include "widgets/kis_cmb_idlist.h" #include #include "kis_file_name_requester.h" #include #include #include #include "slider_and_spin_box_sync.h" // for the performance update #include #include #include "input/config/kis_input_configuration_page.h" #include "input/wintab/drawpile_tablettester/tablettester.h" #ifdef Q_OS_WIN # include #endif GeneralTab::GeneralTab(QWidget *_parent, const char *_name) : WdgGeneralSettings(_parent, _name) { KisConfig cfg(true); // // Cursor Tab // m_cmbCursorShape->addItem(i18n("No Cursor")); m_cmbCursorShape->addItem(i18n("Tool Icon")); m_cmbCursorShape->addItem(i18n("Arrow")); m_cmbCursorShape->addItem(i18n("Small Circle")); m_cmbCursorShape->addItem(i18n("Crosshair")); m_cmbCursorShape->addItem(i18n("Triangle Righthanded")); m_cmbCursorShape->addItem(i18n("Triangle Lefthanded")); m_cmbCursorShape->addItem(i18n("Black Pixel")); m_cmbCursorShape->addItem(i18n("White Pixel")); m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle()); m_cmbOutlineShape->addItem(i18n("No Outline")); m_cmbOutlineShape->addItem(i18n("Circle Outline")); m_cmbOutlineShape->addItem(i18n("Preview Outline")); m_cmbOutlineShape->addItem(i18n("Tilt Outline")); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle()); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting()); m_changeBrushOutline->setChecked(!cfg.forceAlwaysFullSizedOutline()); KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8()); cursorColor.fromQColor(cfg.getCursorMainColor()); cursorColorBtutton->setColor(cursorColor); // // Window Tab // m_cmbMDIType->setCurrentIndex(cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView)); m_backgroundimage->setText(cfg.getMDIBackgroundImage()); connect(m_bnFileName, SIGNAL(clicked()), SLOT(getBackgroundImage())); connect(clearBgImageButton, SIGNAL(clicked()), SLOT(clearBackgroundImage())); KoColor mdiColor; mdiColor.fromQColor(cfg.getMDIBackgroundColor()); m_mdiColor->setColor(mdiColor); m_chkRubberBand->setChecked(cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages()); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); - m_chkHiDPI->setChecked(kritarc.value("EnableHiDPI", false).toBool()); + m_chkHiDPI->setChecked(kritarc.value("EnableHiDPI", true).toBool()); m_chkSingleApplication->setChecked(kritarc.value("EnableSingleApplication", true).toBool()); // // Tools tab // m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker()); m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt()); chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas()); chkEnableTranformToolAfterPaste->setChecked(cfg.activateTransformToolAfterPaste()); m_groupBoxKineticScrollingSettings->setChecked(cfg.kineticScrollingEnabled()); m_cmbKineticScrollingGesture->addItem(i18n("On Touch Drag")); m_cmbKineticScrollingGesture->addItem(i18n("On Click Drag")); m_cmbKineticScrollingGesture->addItem(i18n("On Middle-Click Drag")); //m_cmbKineticScrollingGesture->addItem(i18n("On Right Click Drag")); m_cmbKineticScrollingGesture->setCurrentIndex(cfg.kineticScrollingGesture()); m_kineticScrollingSensitivitySlider->setRange(0, 100); m_kineticScrollingSensitivitySlider->setValue(cfg.kineticScrollingSensitivity()); m_chkKineticScrollingHideScrollbars->setChecked(cfg.kineticScrollingHiddenScrollbars()); // // Miscellaneous // cmbStartupSession->addItem(i18n("Open default window")); cmbStartupSession->addItem(i18n("Load previous session")); cmbStartupSession->addItem(i18n("Show session manager")); cmbStartupSession->setCurrentIndex(cfg.sessionOnStartup()); chkSaveSessionOnQuit->setChecked(cfg.saveSessionOnQuit(false)); int autosaveInterval = cfg.autoSaveInterval(); //convert to minutes m_autosaveSpinBox->setValue(autosaveInterval / 60); m_autosaveCheckBox->setChecked(autosaveInterval > 0); m_chkCompressKra->setChecked(cfg.compressKra()); m_backupFileCheckBox->setChecked(cfg.backupFile()); m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport()); m_undoStackSize->setValue(cfg.undoStackLimit()); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets()); chkShowRootLayer->setChecked(cfg.showRootLayer()); KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); bool dontUseNative = true; #ifdef Q_OS_UNIX if (qgetenv("XDG_CURRENT_DESKTOP") == "KDE") { dontUseNative = false; } #endif #ifdef Q_OS_WIN dontUseNative = false; #endif m_chkNativeFileDialog->setChecked(!group.readEntry("DontUseNativeFileDialog", dontUseNative)); intMaxBrushSize->setValue(cfg.readEntry("maximumBrushSize", 1000)); // // Resources // m_urlCacheDbLocation->setMode(KoFileDialog::OpenDirectory); m_urlCacheDbLocation->setConfigurationName("cachedb_location"); m_urlCacheDbLocation->setFileName(cfg.readEntry(KisResourceCacheDb::dbLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))); m_urlResourceFolder->setMode(KoFileDialog::OpenDirectory); m_urlResourceFolder->setConfigurationName("resource_directory"); m_urlResourceFolder->setFileName(cfg.readEntry(KisResourceLocator::resourceLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))); } void GeneralTab::setDefault() { KisConfig cfg(true); m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle(true)); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle(true)); chkShowRootLayer->setChecked(cfg.showRootLayer(true)); m_autosaveCheckBox->setChecked(cfg.autoSaveInterval(true) > 0); //convert to minutes m_autosaveSpinBox->setValue(cfg.autoSaveInterval(true) / 60); m_undoStackSize->setValue(cfg.undoStackLimit(true)); m_backupFileCheckBox->setChecked(cfg.backupFile(true)); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting(true)); m_changeBrushOutline->setChecked(!cfg.forceAlwaysFullSizedOutline(true)); m_chkNativeFileDialog->setChecked(false); intMaxBrushSize->setValue(1000); m_cmbMDIType->setCurrentIndex((int)QMdiArea::TabbedView); m_chkRubberBand->setChecked(cfg.useOpenGL(true)); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets(true)); KoColor mdiColor; mdiColor.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_groupBoxKineticScrollingSettings->setChecked(cfg.kineticScrollingEnabled(true)); m_cmbKineticScrollingGesture->setCurrentIndex(cfg.kineticScrollingGesture(true)); m_kineticScrollingSensitivitySlider->setValue(cfg.kineticScrollingSensitivity(true)); m_chkKineticScrollingHideScrollbars->setChecked(cfg.kineticScrollingHiddenScrollbars(true)); m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt(true)); chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas(true)); chkEnableTranformToolAfterPaste->setChecked(cfg.activateTransformToolAfterPaste(true)); m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport(true)); KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8()); cursorColor.fromQColor(cfg.getCursorMainColor(true)); cursorColorBtutton->setColor(cursorColor); m_urlCacheDbLocation->setFileName(cfg.readEntry(KisResourceCacheDb::dbLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))); m_urlResourceFolder->setFileName(cfg.readEntry(KisResourceLocator::resourceLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))); } CursorStyle GeneralTab::cursorStyle() { return (CursorStyle)m_cmbCursorShape->currentIndex(); } OutlineStyle GeneralTab::outlineStyle() { return (OutlineStyle)m_cmbOutlineShape->currentIndex(); } KisConfig::SessionOnStartup GeneralTab::sessionOnStartup() const { return (KisConfig::SessionOnStartup)cmbStartupSession->currentIndex(); } bool GeneralTab::saveSessionOnQuit() const { return chkSaveSessionOnQuit->isChecked(); } bool GeneralTab::showRootLayer() { return chkShowRootLayer->isChecked(); } int GeneralTab::autoSaveInterval() { //convert to seconds return m_autosaveCheckBox->isChecked() ? m_autosaveSpinBox->value() * 60 : 0; } int GeneralTab::undoStackSize() { return m_undoStackSize->value(); } bool GeneralTab::showOutlineWhilePainting() { return m_showOutlinePainting->isChecked(); } int GeneralTab::mdiMode() { return m_cmbMDIType->currentIndex(); } int GeneralTab::favoritePresets() { return m_favoritePresetsSpinBox->value(); } bool GeneralTab::showCanvasMessages() { return m_chkCanvasMessages->isChecked(); } bool GeneralTab::compressKra() { return m_chkCompressKra->isChecked(); } bool GeneralTab::toolOptionsInDocker() { return m_radioToolOptionsInDocker->isChecked(); } bool GeneralTab::kineticScrollingEnabled() { return m_groupBoxKineticScrollingSettings->isChecked(); } int GeneralTab::kineticScrollingGesture() { return m_cmbKineticScrollingGesture->currentIndex(); } int GeneralTab::kineticScrollingSensitivity() { return m_kineticScrollingSensitivitySlider->value(); } bool GeneralTab::kineticScrollingHiddenScrollbars() { return m_chkKineticScrollingHideScrollbars->isChecked(); } bool GeneralTab::switchSelectionCtrlAlt() { return m_chkSwitchSelectionCtrlAlt->isChecked(); } bool GeneralTab::convertToImageColorspaceOnImport() { return m_chkConvertOnImport->isChecked(); } void GeneralTab::getBackgroundImage() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "BackgroundImages"); dialog.setCaption(i18n("Select a Background Image")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setImageFilters(); QString fn = dialog.filename(); // dialog box was canceled or somehow no file was selected if (fn.isEmpty()) { return; } QImage image(fn); if (image.isNull()) { QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("%1 is not a valid image file!", fn)); } else { m_backgroundimage->setText(fn); } } void GeneralTab::clearBackgroundImage() { // clearing the background image text will implicitly make the background color be used m_backgroundimage->setText(""); } #include "kactioncollection.h" #include "KisActionsSnapshot.h" ShortcutSettingsTab::ShortcutSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgShortcutSettings(this); l->addWidget(m_page, 0, 0); m_snapshot.reset(new KisActionsSnapshot); KActionCollection *collection = KisPart::instance()->currentMainwindow()->actionCollection(); Q_FOREACH (QAction *action, collection->actions()) { m_snapshot->addAction(action->objectName(), action); } QMap sortedCollections = m_snapshot->actionCollections(); for (auto it = sortedCollections.constBegin(); it != sortedCollections.constEnd(); ++it) { m_page->addCollection(it.value(), it.key()); } } ShortcutSettingsTab::~ShortcutSettingsTab() { } void ShortcutSettingsTab::setDefault() { m_page->allDefault(); } void ShortcutSettingsTab::saveChanges() { m_page->save(); KisActionRegistry::instance()->settingsPageSaved(); } void ShortcutSettingsTab::cancelChanges() { m_page->undo(); } ColorSettingsTab::ColorSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); // XXX: Make sure only profiles that fit the specified color model // are shown in the profile combos QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgColorSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg(true); m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile()); connect(m_page->chkUseSystemMonitorProfile, SIGNAL(toggled(bool)), this, SLOT(toggleAllowMonitorProfileSelection(bool))); m_page->cmbWorkingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys()); m_page->cmbWorkingColorSpace->setCurrent(cfg.workingColorSpace()); m_page->bnAddColorProfile->setIcon(KisIconUtils::loadIcon("document-open")); m_page->bnAddColorProfile->setToolTip( i18n("Open Color Profile") ); connect(m_page->bnAddColorProfile, SIGNAL(clicked()), SLOT(installProfile())); QFormLayout *monitorProfileGrid = new QFormLayout(m_page->monitorprofileholder); for(int i = 0; i < 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()); m_page->chkForcePaletteColor->setChecked(cfg.forcePaletteColors()); KisImageConfig cfgImage(true); KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration(); m_page->sldAdaptationState->setMaximum(20); m_page->sldAdaptationState->setMinimum(0); m_page->sldAdaptationState->setValue((int)proofingConfig->adaptationState*20); //probably this should become the screenprofile? KoColor ga(KoColorSpaceRegistry::instance()->rgb8()); ga.fromKoColor(proofingConfig->warningColor); m_page->gamutAlarm->setColor(ga); const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel, proofingConfig->proofingDepth, proofingConfig->proofingProfile); if (proofingSpace) { m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace); } m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent); m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation)); m_pasteBehaviourGroup.addButton(m_page->radioPasteWeb, PASTE_ASSUME_WEB); m_pasteBehaviourGroup.addButton(m_page->radioPasteMonitor, PASTE_ASSUME_MONITOR); m_pasteBehaviourGroup.addButton(m_page->radioPasteAsk, PASTE_ASK); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour()); Q_ASSERT(button); if (button) { button->setChecked(true); } m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent()); toggleAllowMonitorProfileSelection(cfg.useSystemMonitorProfile()); } void ColorSettingsTab::installProfile() { KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); dialog.setMimeTypeFilters(QStringList() << "application/vnd.iccprofile", "application/vnd.iccprofile"); QStringList profileNames = dialog.filenames(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KoResourcePaths::saveLocation("icc_profiles"); Q_FOREACH (const QString &profileName, profileNames) { if (!QFile::copy(profileName, saveLocation + QFileInfo(profileName).fileName())) { qWarning() << "Could not install profile!" << saveLocation + QFileInfo(profileName).fileName(); continue; } iccEngine->addProfile(saveLocation + QFileInfo(profileName).fileName()); } KisConfig cfg(true); refillMonitorProfiles(KoID("RGBA")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } } void ColorSettingsTab::toggleAllowMonitorProfileSelection(bool useSystemProfile) { KisConfig cfg(true); if (useSystemProfile) { QStringList devices = KisColorManager::instance()->devices(); if (devices.size() == QApplication::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 { 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(true); KisImageConfig cfgImage(true); KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration(); const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel,proofingConfig->proofingDepth,proofingConfig->proofingProfile); if (proofingSpace) { m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace); } m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent); m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation)); m_page->sldAdaptationState->setValue(0); //probably this should become the screenprofile? KoColor ga(KoColorSpaceRegistry::instance()->rgb8()); ga.fromKoColor(proofingConfig->warningColor); m_page->gamutAlarm->setColor(ga); m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation(true)); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization(true)); m_page->chkForcePaletteColor->setChecked(cfg.forcePaletteColors(true)); m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent(true)); m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile(true)); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour(true)); Q_ASSERT(button); if (button) { button->setChecked(true); } } void ColorSettingsTab::refillMonitorProfiles(const KoID & colorSpaceId) { for (int i = 0; i < QApplication::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(true); m_page->radioWintab->setChecked(!cfg.useWin8PointerInput(true)); m_page->radioWin8PointerInput->setChecked(cfg.useWin8PointerInput(true)); } else { m_page->radioWintab->setChecked(true); m_page->radioWin8PointerInput->setChecked(false); } #endif } TabletSettingsTab::TabletSettingsTab(QWidget* parent, const char* name): QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgTabletSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg(true); KisCubicCurve curve; curve.fromString( cfg.pressureTabletCurve() ); m_page->pressureCurve->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); m_page->pressureCurve->setCurve(curve); #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 connect(m_page->btnTabletTest, SIGNAL(clicked()), SLOT(slotTabletTest())); } void TabletSettingsTab::slotTabletTest() { TabletTestDialog tabletTestDialog(this); tabletTestDialog.exec(); } //--------------------------------------------------------------------------------------------------- #include "kis_acyclic_signal_connector.h" int getTotalRAM() { return KisImageConfig(true).totalRAM(); } int PerformanceTab::realTilesRAM() { return intMemoryLimit->value() - intPoolLimit->value(); } PerformanceTab::PerformanceTab(QWidget *parent, const char *name) : WdgPerformanceSettings(parent, name) { KisImageConfig cfg(true); const double totalRAM = cfg.totalRAM(); lblTotalMemory->setText(KFormat().formatByteSize(totalRAM * 1024 * 1024, 0, KFormat::IECBinaryDialect, KFormat::UnitMegaByte)); sliderMemoryLimit->setSuffix(i18n(" %")); sliderMemoryLimit->setRange(1, 100, 2); sliderMemoryLimit->setSingleStep(0.01); sliderPoolLimit->setSuffix(i18n(" %")); sliderPoolLimit->setRange(0, 20, 2); sliderMemoryLimit->setSingleStep(0.01); sliderUndoLimit->setSuffix(i18n(" %")); sliderUndoLimit->setRange(0, 50, 2); sliderMemoryLimit->setSingleStep(0.01); intMemoryLimit->setMinimumWidth(80); intPoolLimit->setMinimumWidth(80); intUndoLimit->setMinimumWidth(80); SliderAndSpinBoxSync *sync1 = new SliderAndSpinBoxSync(sliderMemoryLimit, intMemoryLimit, getTotalRAM); sync1->slotParentValueChanged(); m_syncs << sync1; SliderAndSpinBoxSync *sync2 = new SliderAndSpinBoxSync(sliderPoolLimit, intPoolLimit, std::bind(&KisIntParseSpinBox::value, intMemoryLimit)); connect(intMemoryLimit, SIGNAL(valueChanged(int)), sync2, SLOT(slotParentValueChanged())); sync2->slotParentValueChanged(); m_syncs << sync2; SliderAndSpinBoxSync *sync3 = new SliderAndSpinBoxSync(sliderUndoLimit, intUndoLimit, std::bind(&PerformanceTab::realTilesRAM, this)); connect(intPoolLimit, SIGNAL(valueChanged(int)), sync3, SLOT(slotParentValueChanged())); 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))); intCachedFramesSizeLimit->setRange(1, 10000); intCachedFramesSizeLimit->setSuffix(i18n(" px")); intCachedFramesSizeLimit->setSingleStep(1); intCachedFramesSizeLimit->setPageStep(1000); intRegionOfInterestMargin->setRange(1, 100); intRegionOfInterestMargin->setSuffix(i18n(" %")); intRegionOfInterestMargin->setSingleStep(1); intRegionOfInterestMargin->setPageStep(10); connect(chkCachedFramesSizeLimit, SIGNAL(toggled(bool)), intCachedFramesSizeLimit, SLOT(setEnabled(bool))); connect(chkUseRegionOfInterest, SIGNAL(toggled(bool)), intRegionOfInterestMargin, SLOT(setEnabled(bool))); load(false); } PerformanceTab::~PerformanceTab() { qDeleteAll(m_syncs); } void PerformanceTab::load(bool requestDefault) { KisImageConfig cfg(true); sliderMemoryLimit->setValue(cfg.memoryHardLimitPercent(requestDefault)); sliderPoolLimit->setValue(cfg.memoryPoolLimitPercent(requestDefault)); sliderUndoLimit->setValue(cfg.memorySoftLimitPercent(requestDefault)); chkPerformanceLogging->setChecked(cfg.enablePerfLog(requestDefault)); chkProgressReporting->setChecked(cfg.enableProgressReporting(requestDefault)); sliderSwapSize->setValue(cfg.maxSwapSize(requestDefault) / 1024); lblSwapFileLocation->setText(cfg.swapDir(requestDefault)); m_lastUsedThreadsLimit = cfg.maxNumberOfThreads(requestDefault); m_lastUsedClonesLimit = cfg.frameRenderingClones(requestDefault); sliderThreadsLimit->setValue(m_lastUsedThreadsLimit); sliderFrameClonesLimit->setValue(m_lastUsedClonesLimit); sliderFpsLimit->setValue(cfg.fpsLimit(requestDefault)); { KisConfig cfg2(true); chkOpenGLFramerateLogging->setChecked(cfg2.enableOpenGLFramerateLogging(requestDefault)); chkBrushSpeedLogging->setChecked(cfg2.enableBrushSpeedLogging(requestDefault)); chkDisableVectorOptimizations->setChecked(cfg2.enableAmdVectorizationWorkaround(requestDefault)); chkBackgroundCacheGeneration->setChecked(cfg2.calculateAnimationCacheInBackground(requestDefault)); } if (cfg.useOnDiskAnimationCacheSwapping(requestDefault)) { optOnDisk->setChecked(true); } else { optInMemory->setChecked(true); } chkCachedFramesSizeLimit->setChecked(cfg.useAnimationCacheFrameSizeLimit(requestDefault)); intCachedFramesSizeLimit->setValue(cfg.animationCacheFrameSizeLimit(requestDefault)); intCachedFramesSizeLimit->setEnabled(chkCachedFramesSizeLimit->isChecked()); chkUseRegionOfInterest->setChecked(cfg.useAnimationCacheRegionOfInterest(requestDefault)); intRegionOfInterestMargin->setValue(cfg.animationCacheRegionOfInterestMargin(requestDefault) * 100.0); intRegionOfInterestMargin->setEnabled(chkUseRegionOfInterest->isChecked()); } void PerformanceTab::save() { KisImageConfig cfg(false); cfg.setMemoryHardLimitPercent(sliderMemoryLimit->value()); cfg.setMemorySoftLimitPercent(sliderUndoLimit->value()); cfg.setMemoryPoolLimitPercent(sliderPoolLimit->value()); cfg.setEnablePerfLog(chkPerformanceLogging->isChecked()); cfg.setEnableProgressReporting(chkProgressReporting->isChecked()); cfg.setMaxSwapSize(sliderSwapSize->value() * 1024); cfg.setSwapDir(lblSwapFileLocation->text()); cfg.setMaxNumberOfThreads(sliderThreadsLimit->value()); cfg.setFrameRenderingClones(sliderFrameClonesLimit->value()); cfg.setFpsLimit(sliderFpsLimit->value()); { KisConfig cfg2(true); cfg2.setEnableOpenGLFramerateLogging(chkOpenGLFramerateLogging->isChecked()); cfg2.setEnableBrushSpeedLogging(chkBrushSpeedLogging->isChecked()); cfg2.setEnableAmdVectorizationWorkaround(chkDisableVectorOptimizations->isChecked()); cfg2.setCalculateAnimationCacheInBackground(chkBackgroundCacheGeneration->isChecked()); } cfg.setUseOnDiskAnimationCacheSwapping(optOnDisk->isChecked()); cfg.setUseAnimationCacheFrameSizeLimit(chkCachedFramesSizeLimit->isChecked()); cfg.setAnimationCacheFrameSizeLimit(intCachedFramesSizeLimit->value()); cfg.setUseAnimationCacheRegionOfInterest(chkUseRegionOfInterest->isChecked()); cfg.setAnimationCacheRegionOfInterestMargin(intRegionOfInterestMargin->value() / 100.0); } void PerformanceTab::selectSwapDir() { KisImageConfig cfg(true); QString swapDir = cfg.swapDir(); swapDir = QFileDialog::getExistingDirectory(0, i18nc("@title:window", "Select a swap directory"), swapDir); if (swapDir.isEmpty()) { return; } lblSwapFileLocation->setText(swapDir); } void PerformanceTab::slotThreadsLimitChanged(int value) { KisSignalsBlocker b(sliderFrameClonesLimit); sliderFrameClonesLimit->setValue(qMin(m_lastUsedClonesLimit, value)); m_lastUsedThreadsLimit = value; } void PerformanceTab::slotFrameClonesLimitChanged(int value) { KisSignalsBlocker b(sliderThreadsLimit); sliderThreadsLimit->setValue(qMax(m_lastUsedThreadsLimit, value)); m_lastUsedClonesLimit = value; } //--------------------------------------------------------------------------------------------------- #include "KoColor.h" DisplaySettingsTab::DisplaySettingsTab(QWidget *parent, const char *name) : WdgDisplaySettings(parent, name) { KisConfig cfg(true); const QString rendererOpenGLText = i18nc("canvas renderer", "OpenGL"); #ifdef Q_OS_WIN const QString rendererAngleText = i18nc("canvas renderer", "Direct3D 11 via ANGLE"); 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); } } const QStringList openglWarnings = KisOpenGL::getOpenGLWarnings(); if (openglWarnings.isEmpty()) { lblOpenGLWarnings->setVisible(false); } else { QString text(" "); text.append(i18n("Warning(s):")); text.append("
    "); Q_FOREACH (const QString &warning, openglWarnings) { text.append("
  • "); text.append(warning.toHtmlEscaped()); text.append("
  • "); } text.append("
"); lblOpenGLWarnings->setText(text); lblOpenGLWarnings->setVisible(true); } if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") { grpOpenGL->setVisible(false); grpOpenGL->setMaximumHeight(0); } KisImageConfig imageCfg(false); KoColor c; c.fromQColor(imageCfg.selectionOverlayMaskColor()); c.setOpacity(1.0); btnSelectionOverlayColor->setColor(c); sldSelectionOverlayOpacity->setRange(0.0, 1.0, 2); sldSelectionOverlayOpacity->setSingleStep(0.05); sldSelectionOverlayOpacity->setValue(imageCfg.selectionOverlayMaskColor().alphaF()); intCheckSize->setValue(cfg.checkSize()); chkMoving->setChecked(cfg.scrollCheckers()); KoColor ck1(KoColorSpaceRegistry::instance()->rgb8()); ck1.fromQColor(cfg.checkersColor1()); colorChecks1->setColor(ck1); KoColor ck2(KoColorSpaceRegistry::instance()->rgb8()); ck2.fromQColor(cfg.checkersColor2()); colorChecks2->setColor(ck2); KoColor cb(KoColorSpaceRegistry::instance()->rgb8()); cb.fromQColor(cfg.canvasBorderColor()); canvasBorder->setColor(cb); hideScrollbars->setChecked(cfg.hideScrollbars()); chkCurveAntialiasing->setChecked(cfg.antialiasCurves()); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline()); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor()); chkHidePopups->setChecked(cfg.hidePopups()); connect(grpOpenGL, SIGNAL(toggled(bool)), SLOT(slotUseOpenGLToggled(bool))); KoColor gridColor(KoColorSpaceRegistry::instance()->rgb8()); gridColor.fromQColor(cfg.getPixelGridColor()); pixelGridColorButton->setColor(gridColor); pixelGridDrawingThresholdBox->setValue(cfg.getPixelGridDrawingThreshold() * 100); } void DisplaySettingsTab::setDefault() { KisConfig cfg(true); 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(true); chkDockers->setChecked(cfg.hideDockersFullscreen()); chkMenu->setChecked(cfg.hideMenuFullscreen()); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen()); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen()); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen()); chkToolbar->setChecked(cfg.hideToolbarFullscreen()); } void FullscreenSettingsTab::setDefault() { KisConfig cfg(true); chkDockers->setChecked(cfg.hideDockersFullscreen(true)); chkMenu->setChecked(cfg.hideMenuFullscreen(true)); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen(true)); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen(true)); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen(true)); chkToolbar->setChecked(cfg.hideToolbarFullscreen(true)); } //--------------------------------------------------------------------------------------------------- KisDlgPreferences::KisDlgPreferences(QWidget* parent, const char* name) : KPageDialog(parent) { Q_UNUSED(name); setWindowTitle(i18n("Configure Krita")); setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults); setFaceType(KPageDialog::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")); m_pages << page; addPage(page); m_general = new GeneralTab(vbox); // Shortcuts vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Keyboard Shortcuts")); page->setObjectName("shortcuts"); page->setHeader(i18n("Shortcuts")); page->setIcon(KisIconUtils::loadIcon("document-export")); m_pages << page; addPage(page); m_shortcutSettings = new ShortcutSettingsTab(vbox); connect(this, SIGNAL(accepted()), m_shortcutSettings, SLOT(saveChanges())); connect(this, SIGNAL(rejected()), m_shortcutSettings, SLOT(cancelChanges())); // Canvas input settings m_inputConfiguration = new KisInputConfigurationPage(); page = addPage(m_inputConfiguration, i18n("Canvas Input Settings")); page->setHeader(i18n("Canvas Input")); page->setObjectName("canvasinput"); page->setIcon(KisIconUtils::loadIcon("configure")); m_pages << page; // Display vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Display")); page->setObjectName("display"); page->setHeader(i18n("Display")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-display")); m_pages << page; addPage(page); m_displaySettings = new DisplaySettingsTab(vbox); // Color vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Color Management")); page->setObjectName("colormanagement"); page->setHeader(i18n("Color")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-color")); m_pages << page; addPage(page); m_colorSettings = new ColorSettingsTab(vbox); // Performance vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Performance")); page->setObjectName("performance"); page->setHeader(i18n("Performance")); page->setIcon(KisIconUtils::loadIcon("applications-system")); m_pages << page; addPage(page); m_performanceSettings = new PerformanceTab(vbox); // Tablet vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Tablet settings")); page->setObjectName("tablet"); page->setHeader(i18n("Tablet")); page->setIcon(KisIconUtils::loadIcon("document-edit")); m_pages << page; addPage(page); m_tabletSettings = new TabletSettingsTab(vbox); // full-screen mode vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Canvas-only settings")); page->setObjectName("canvasonly"); page->setHeader(i18n("Canvas-only")); page->setIcon(KisIconUtils::loadIcon("folder-pictures")); m_pages << page; addPage(page); m_fullscreenSettings = new FullscreenSettingsTab(vbox); // Author profiles m_authorPage = new KoConfigAuthorPage(); page = addPage(m_authorPage, i18nc("@title:tab Author page", "Author" )); page->setObjectName("author"); page->setHeader(i18n("Author")); page->setIcon(KisIconUtils::loadIcon("im-user")); m_pages << page; QPushButton *restoreDefaultsButton = button(QDialogButtonBox::RestoreDefaults); restoreDefaultsButton->setText(i18nc("@action:button", "Restore Defaults")); connect(this, SIGNAL(accepted()), m_inputConfiguration, SLOT(saveChanges())); connect(this, SIGNAL(rejected()), m_inputConfiguration, SLOT(revertChanges())); KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); 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())); KisConfig cfg(true); QString currentPageName = cfg.readEntry("KisDlgPreferences/CurrentPage"); Q_FOREACH(KPageWidgetItem *page, m_pages) { if (page->objectName() == currentPageName) { setCurrentPage(page); break; } } } KisDlgPreferences::~KisDlgPreferences() { KisConfig cfg(true); cfg.writeEntry("KisDlgPreferences/CurrentPage", currentPage()->objectName()); } void KisDlgPreferences::showEvent(QShowEvent *event){ KPageDialog::showEvent(event); button(QDialogButtonBox::Cancel)->setAutoDefault(false); button(QDialogButtonBox::Ok)->setAutoDefault(false); button(QDialogButtonBox::RestoreDefaults)->setAutoDefault(false); button(QDialogButtonBox::Cancel)->setDefault(false); button(QDialogButtonBox::Ok)->setDefault(false); button(QDialogButtonBox::RestoreDefaults)->setDefault(false); } void KisDlgPreferences::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(false); 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.setForceAlwaysFullSizedOutline(!dialog->m_general->m_changeBrushOutline->isChecked()); cfg.setSessionOnStartup(dialog->m_general->sessionOnStartup()); cfg.setSaveSessionOnQuit(dialog->m_general->saveSessionOnQuit()); 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.setKineticScrollingEnabled(dialog->m_general->kineticScrollingEnabled()); cfg.setKineticScrollingGesture(dialog->m_general->kineticScrollingGesture()); cfg.setKineticScrollingSensitivity(dialog->m_general->kineticScrollingSensitivity()); cfg.setKineticScrollingHideScrollbars(dialog->m_general->kineticScrollingHiddenScrollbars()); cfg.setSwitchSelectionCtrlAlt(dialog->m_general->switchSelectionCtrlAlt()); cfg.setDisableTouchOnCanvas(!dialog->m_general->chkEnableTouch->isChecked()); cfg.setActivateTransformToolAfterPaste(dialog->m_general->chkEnableTranformToolAfterPaste->isChecked()); cfg.setConvertToImageColorspaceOnImport(dialog->m_general->convertToImageColorspaceOnImport()); cfg.setUndoStackLimit(dialog->m_general->undoStackSize()); cfg.setFavoritePresets(dialog->m_general->favoritePresets()); cfg.writeEntry(KisResourceCacheDb::dbLocationKey, dialog->m_general->m_urlCacheDbLocation->fileName()); cfg.writeEntry(KisResourceLocator::resourceLocationKey, dialog->m_general->m_urlResourceFolder->fileName()); // 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(false); 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.setForcePaletteColors(dialog->m_colorSettings->m_page->chkForcePaletteColor->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()); cfgImage.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/wintab/qxcbconnection.cpp b/libs/ui/input/wintab/qxcbconnection.cpp index 19d650cabe..c062d05e0b 100644 --- a/libs/ui/input/wintab/qxcbconnection.cpp +++ b/libs/ui/input/wintab/qxcbconnection.cpp @@ -1,793 +1,795 @@ + /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** 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 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qxcbconnection_xi2.h" #include #include #include #include #include #include #include #include Q_LOGGING_CATEGORY(lcQpaXInput, "qt.qpa.input") Q_LOGGING_CATEGORY(lcQpaXInputDevices, "qt.qpa.input.devices") Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen") QXcbConnection::QXcbConnection(bool canGrabServer, const char *displayName) : m_connection(0) , m_canGrabServer(canGrabServer) , m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY")) -#ifdef XCB_USE_XLIB + #ifdef XCB_USE_XLIB , m_xlib_display(0) -#endif + #endif { m_connection = QX11Info::connection(); m_xlib_display = QX11Info::display(); if (!m_connection || xcb_connection_has_error(m_connection)) { qFatal("QXcbConnection: Could not connect to display %s", m_displayName.constData()); } initializeAllAtoms(); #if defined(XCB_USE_XINPUT2) initializeXInput2(); #endif } QXcbConnection::~QXcbConnection() { #if defined(XCB_USE_XINPUT2) finalizeXInput2(); #endif } QXcbAtom::Atom QXcbConnection::qatom(xcb_atom_t xatom) const { return static_cast(std::find(m_allAtoms, m_allAtoms + QXcbAtom::NAtoms, xatom) - m_allAtoms); } void *QXcbConnection::xlib_display() const { return m_xlib_display; } QByteArray QXcbConnection::atomName(xcb_atom_t atom) { if (!atom) return QByteArray(); xcb_generic_error_t *error = 0; xcb_get_atom_name_cookie_t cookie = Q_XCB_CALL(xcb_get_atom_name(xcb_connection(), atom)); xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(xcb_connection(), cookie, &error); if (error) { qWarning() << "QXcbConnection::atomName: bad Atom" << atom; free(error); } if (reply) { QByteArray result(xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply)); free(reply); return result; } return QByteArray(); } static const char * xcb_atomnames = { // window-manager <-> client protocols "WM_PROTOCOLS\0" "WM_DELETE_WINDOW\0" "WM_TAKE_FOCUS\0" "_NET_WM_PING\0" "_NET_WM_CONTEXT_HELP\0" "_NET_WM_SYNC_REQUEST\0" "_NET_WM_SYNC_REQUEST_COUNTER\0" "MANAGER\0" "_NET_SYSTEM_TRAY_OPCODE\0" // ICCCM window state "WM_STATE\0" "WM_CHANGE_STATE\0" "WM_CLASS\0" "WM_NAME\0" // Session management "WM_CLIENT_LEADER\0" "WM_WINDOW_ROLE\0" "SM_CLIENT_ID\0" // Clipboard "CLIPBOARD\0" "INCR\0" "TARGETS\0" "MULTIPLE\0" "TIMESTAMP\0" "SAVE_TARGETS\0" "CLIP_TEMPORARY\0" "_QT_SELECTION\0" "_QT_CLIPBOARD_SENTINEL\0" "_QT_SELECTION_SENTINEL\0" "CLIPBOARD_MANAGER\0" "RESOURCE_MANAGER\0" "_XSETROOT_ID\0" "_QT_SCROLL_DONE\0" "_QT_INPUT_ENCODING\0" "_QT_CLOSE_CONNECTION\0" "_MOTIF_WM_HINTS\0" "DTWM_IS_RUNNING\0" "ENLIGHTENMENT_DESKTOP\0" "_DT_SAVE_MODE\0" "_SGI_DESKS_MANAGER\0" // EWMH (aka NETWM) "_NET_SUPPORTED\0" "_NET_VIRTUAL_ROOTS\0" "_NET_WORKAREA\0" "_NET_MOVERESIZE_WINDOW\0" "_NET_WM_MOVERESIZE\0" "_NET_WM_NAME\0" "_NET_WM_ICON_NAME\0" "_NET_WM_ICON\0" "_NET_WM_PID\0" "_NET_WM_WINDOW_OPACITY\0" "_NET_WM_STATE\0" "_NET_WM_STATE_ABOVE\0" "_NET_WM_STATE_BELOW\0" "_NET_WM_STATE_FULLSCREEN\0" "_NET_WM_STATE_MAXIMIZED_HORZ\0" "_NET_WM_STATE_MAXIMIZED_VERT\0" "_NET_WM_STATE_MODAL\0" "_NET_WM_STATE_STAYS_ON_TOP\0" "_NET_WM_STATE_DEMANDS_ATTENTION\0" "_NET_WM_USER_TIME\0" "_NET_WM_USER_TIME_WINDOW\0" "_NET_WM_FULL_PLACEMENT\0" "_NET_WM_WINDOW_TYPE\0" "_NET_WM_WINDOW_TYPE_DESKTOP\0" "_NET_WM_WINDOW_TYPE_DOCK\0" "_NET_WM_WINDOW_TYPE_TOOLBAR\0" "_NET_WM_WINDOW_TYPE_MENU\0" "_NET_WM_WINDOW_TYPE_UTILITY\0" "_NET_WM_WINDOW_TYPE_SPLASH\0" "_NET_WM_WINDOW_TYPE_DIALOG\0" "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0" "_NET_WM_WINDOW_TYPE_POPUP_MENU\0" "_NET_WM_WINDOW_TYPE_TOOLTIP\0" "_NET_WM_WINDOW_TYPE_NOTIFICATION\0" "_NET_WM_WINDOW_TYPE_COMBO\0" "_NET_WM_WINDOW_TYPE_DND\0" "_NET_WM_WINDOW_TYPE_NORMAL\0" "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0" "_KDE_NET_WM_FRAME_STRUT\0" "_NET_FRAME_EXTENTS\0" "_NET_STARTUP_INFO\0" "_NET_STARTUP_INFO_BEGIN\0" "_NET_SUPPORTING_WM_CHECK\0" "_NET_WM_CM_S0\0" "_NET_SYSTEM_TRAY_VISUAL\0" "_NET_ACTIVE_WINDOW\0" // Property formats "TEXT\0" "UTF8_STRING\0" "CARDINAL\0" // xdnd "XdndEnter\0" "XdndPosition\0" "XdndStatus\0" "XdndLeave\0" "XdndDrop\0" "XdndFinished\0" "XdndTypeList\0" "XdndActionList\0" "XdndSelection\0" "XdndAware\0" "XdndProxy\0" "XdndActionCopy\0" "XdndActionLink\0" "XdndActionMove\0" "XdndActionPrivate\0" // Motif DND "_MOTIF_DRAG_AND_DROP_MESSAGE\0" "_MOTIF_DRAG_INITIATOR_INFO\0" "_MOTIF_DRAG_RECEIVER_INFO\0" "_MOTIF_DRAG_WINDOW\0" "_MOTIF_DRAG_TARGETS\0" "XmTRANSFER_SUCCESS\0" "XmTRANSFER_FAILURE\0" // Xkb "_XKB_RULES_NAMES\0" // XEMBED "_XEMBED\0" "_XEMBED_INFO\0" // XInput2 "Button Left\0" "Button Middle\0" "Button Right\0" "Button Wheel Up\0" "Button Wheel Down\0" "Button Horiz Wheel Left\0" "Button Horiz Wheel Right\0" "Abs MT Position X\0" "Abs MT Position Y\0" "Abs MT Touch Major\0" "Abs MT Touch Minor\0" "Abs MT Pressure\0" "Abs MT Tracking ID\0" "Max Contacts\0" "Rel X\0" "Rel Y\0" // XInput2 tablet "Abs X\0" "Abs Y\0" "Abs Pressure\0" "Abs Tilt X\0" "Abs Tilt Y\0" "Abs Wheel\0" "Abs Distance\0" "Wacom Serial IDs\0" "INTEGER\0" "Rel Horiz Wheel\0" "Rel Vert Wheel\0" "Rel Horiz Scroll\0" "Rel Vert Scroll\0" "_XSETTINGS_SETTINGS\0" "_COMPIZ_DECOR_PENDING\0" "_COMPIZ_DECOR_REQUEST\0" "_COMPIZ_DECOR_DELETE_PIXMAP\0" // \0\0 terminates loop. }; void QXcbConnection::initializeAllAtoms() { const char *names[QXcbAtom::NAtoms]; const char *ptr = xcb_atomnames; int i = 0; while (*ptr) { names[i++] = ptr; while (*ptr) ++ptr; ++ptr; } Q_ASSERT(i == QXcbAtom::NPredefinedAtoms); QByteArray settings_atom_name("_QT_SETTINGS_TIMESTAMP_"); settings_atom_name += m_displayName; names[i++] = settings_atom_name; xcb_intern_atom_cookie_t cookies[QXcbAtom::NAtoms]; Q_ASSERT(i == QXcbAtom::NAtoms); for (i = 0; i < QXcbAtom::NAtoms; ++i) cookies[i] = xcb_intern_atom(xcb_connection(), false, strlen(names[i]), names[i]); for (i = 0; i < QXcbAtom::NAtoms; ++i) { xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0); m_allAtoms[i] = reply->atom; free(reply); } } bool QXcbConnection::xi2MouseEvents() const { static bool mouseViaXI2 = !qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE"); return mouseViaXI2; } void QXcbConnection::notifyEnterEvent(xcb_enter_notify_event_t *event) { xcb_window_t window; // first cleaning up deleted windows: assuming 0 is not a valid window id while ((window = m_windowMapper.key(0,0)) != 0) { m_windowMapper.remove(window); } addWindowFromXi2Id(event->event); } void QXcbConnection::addWindowFromXi2Id(xcb_window_t id) { if (!m_windowMapper.contains(id)) { QWidget *widget = QWidget::find(id); if (widget) { QWindow *windowHandle = widget->windowHandle(); m_windowMapper.insert(id, windowHandle); // we should also cold-init the window events mask // (Qt's own initialization got broken in Qt 5.10) xi2Select(id); } } } QWindow* QXcbConnection::windowFromId(xcb_window_t id) { QWindow *window = m_windowMapper.value(id, 0); // Try to fetch the window Id lazily. It is needed when the cursor gets under // a popup window or a popup dialog, which doesn't produce any enter event on // some systems if (!window) { addWindowFromXi2Id(id); window = m_windowMapper.value(id, 0); } return window; } static int xi2ValuatorOffset(unsigned char *maskPtr, int maskLen, int number) { int offset = 0; for (int i = 0; i < maskLen; i++) { if (number < 8) { if ((maskPtr[i] & (1 << number)) == 0) return -1; } for (int j = 0; j < 8; j++) { if (j == number) return offset; if (maskPtr[i] & (1 << j)) offset++; } number -= 8; } return -1; } bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value) { xXIDeviceEvent *xideviceevent = static_cast(event); unsigned char *buttonsMaskAddr = (unsigned char*)&xideviceevent[1]; unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4; FP3232 *valuatorsValuesAddr = (FP3232*)(valuatorsMaskAddr + xideviceevent->valuators_len * 4); int valuatorOffset = xi2ValuatorOffset(valuatorsMaskAddr, xideviceevent->valuators_len, valuatorNum); if (valuatorOffset < 0) return false; *value = valuatorsValuesAddr[valuatorOffset].integral; *value += ((double)valuatorsValuesAddr[valuatorOffset].frac / (1 << 16) / (1 << 16)); return true; } // Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed: // - "pad0" became "extension" // - "pad1" and "pad" became "pad0" // New and old version of this struct share the following fields: // NOTE: API might change again in the next release of xcb in which case this comment will // need to be updated to reflect the reality. typedef struct qt_xcb_ge_event_t { uint8_t response_type; uint8_t extension; uint16_t sequence; uint32_t length; uint16_t event_type; } qt_xcb_ge_event_t; bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *ev, int opCode) { qt_xcb_ge_event_t *event = (qt_xcb_ge_event_t *)ev; // xGenericEvent has "extension" on the second byte, the same is true for xcb_ge_event_t starting from // the xcb version 1.9.3, prior to that it was called "pad0". if (event->extension == opCode) { // xcb event structs contain stuff that wasn't on the wire, the full_sequence field // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes. // Move this data back to have the same layout in memory as it was on the wire // and allow casting, overwriting the full_sequence field. memmove((char*) event + 32, (char*) event + 36, event->length * 4); return true; } return false; } class Q_GUI_EXPORT QWindowSystemInterfacePrivate { public: enum EventType { UserInputEvent = 0x100, Close = UserInputEvent | 0x01, GeometryChange = 0x02, Enter = UserInputEvent | 0x03, Leave = UserInputEvent | 0x04, ActivatedWindow = 0x05, WindowStateChanged = 0x06, Mouse = UserInputEvent | 0x07, FrameStrutMouse = UserInputEvent | 0x08, Wheel = UserInputEvent | 0x09, Key = UserInputEvent | 0x0a, Touch = UserInputEvent | 0x0b, ScreenOrientation = 0x0c, ScreenGeometry = 0x0d, ScreenAvailableGeometry = 0x0e, ScreenLogicalDotsPerInch = 0x0f, ScreenRefreshRate = 0x10, ThemeChange = 0x11, Expose_KRITA_XXX = 0x12, FileOpen = UserInputEvent | 0x13, Tablet = UserInputEvent | 0x14, TabletEnterProximity = UserInputEvent | 0x15, TabletLeaveProximity = UserInputEvent | 0x16, PlatformPanel = UserInputEvent | 0x17, ContextMenu = UserInputEvent | 0x18, EnterWhatsThisMode = UserInputEvent | 0x19, #ifndef QT_NO_GESTURES Gesture = UserInputEvent | 0x1a, #endif ApplicationStateChanged = 0x19, FlushEvents = 0x20, WindowScreenChanged = 0x21 }; class WindowSystemEvent { public: enum { Synthetic = 0x1, NullWindow = 0x2 }; explicit WindowSystemEvent(EventType t) : type(t), flags(0) { } virtual ~WindowSystemEvent() { } bool synthetic() const { return flags & Synthetic; } bool nullWindow() const { return flags & NullWindow; } EventType type; int flags; }; class UserEvent : public WindowSystemEvent { public: UserEvent(QWindow * w, ulong time, EventType t) : WindowSystemEvent(t), window(w), timestamp(time) { if (!w) flags |= NullWindow; } QPointer window; unsigned long timestamp; }; class InputEvent: public UserEvent { public: InputEvent(QWindow * w, ulong time, EventType t, Qt::KeyboardModifiers mods) : UserEvent(w, time, t), modifiers(mods) {} Qt::KeyboardModifiers modifiers; }; class TabletEvent : public InputEvent { public: static void handleTabletEvent(QWindow *w, const QPointF &local, const QPointF &global, int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers modifiers = Qt::NoModifier); TabletEvent(QWindow *w, ulong time, const QPointF &local, const QPointF &global, int device, int pointerType, Qt::MouseButtons b, qreal pressure, int xTilt, int yTilt, qreal tpressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers mods) : InputEvent(w, time, Tablet, mods), buttons(b), local(local), global(global), device(device), pointerType(pointerType), pressure(pressure), xTilt(xTilt), yTilt(yTilt), tangentialPressure(tpressure), rotation(rotation), z(z), uid(uid) { } Qt::MouseButtons buttons; QPointF local; QPointF global; int device; int pointerType; qreal pressure; int xTilt; int yTilt; qreal tangentialPressure; qreal rotation; int z; qint64 uid; }; class WheelEvent : public InputEvent { public: #if QT_VERSION >= 0x050700 WheelEvent(QWindow *w, ulong time, const QPointF & local, const QPointF & global, QPoint pixelD, QPoint angleD, int qt4D, Qt::Orientation qt4O, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase = Qt::NoScrollPhase, Qt::MouseEventSource src = Qt::MouseEventNotSynthesized) #else WheelEvent(QWindow *w, ulong time, const QPointF & local, const QPointF & global, QPoint pixelD, QPoint angleD, int qt4D, Qt::Orientation qt4O, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase = Qt::ScrollUpdate, Qt::MouseEventSource src = Qt::MouseEventNotSynthesized) #endif : InputEvent(w, time, Wheel, mods), pixelDelta(pixelD), angleDelta(angleD), qt4Delta(qt4D), qt4Orientation(qt4O), localPos(local), globalPos(global), phase(phase), source(src) { } QPoint pixelDelta; QPoint angleDelta; int qt4Delta; Qt::Orientation qt4Orientation; QPointF localPos; QPointF globalPos; Qt::ScrollPhase phase; Qt::MouseEventSource source; }; }; void processTabletEvent(QWindowSystemInterfacePrivate::TabletEvent *e); static QElapsedTimer g_eventTimer; struct EventTimerStaticInitializer { EventTimerStaticInitializer() { g_eventTimer.start(); } }; EventTimerStaticInitializer __timerStaticInitializer; Qt::MouseButtons tabletState = Qt::NoButton; QPointer tabletPressWidget = 0; void QWindowSystemInterface::handleTabletEvent(QWindow *w, const QPointF &local, const QPointF &global, int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers modifiers) { qint64 timestamp = g_eventTimer.msecsSinceReference() + g_eventTimer.elapsed(); QWindowSystemInterfacePrivate::TabletEvent *e = - new QWindowSystemInterfacePrivate::TabletEvent(w, timestamp, local, global, device, pointerType, buttons, pressure, - xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers); + new QWindowSystemInterfacePrivate::TabletEvent(w, timestamp, local, global, device, pointerType, buttons, pressure, + xTilt, yTilt, tangentialPressure, rotation, z, uid, modifiers); processTabletEvent(e); } void processTabletEvent(QWindowSystemInterfacePrivate::TabletEvent *e) { #ifndef QT_NO_TABLETEVENT QEvent::Type type = QEvent::TabletMove; if (e->buttons != tabletState) type = (e->buttons > tabletState) ? QEvent::TabletPress : QEvent::TabletRelease; bool localValid = true; // It can happen that we got no tablet release event. Just catch it here // and clean up the state. if (type == QEvent::TabletMove && e->buttons == Qt::NoButton) { tabletPressWidget = 0; } QWidget *targetWidget = 0; if (tabletPressWidget) { targetWidget = tabletPressWidget; localValid = false; } else if (e->window) { /** * Here we use a weird way of converting QWindow into a * QWidget. The problem is that the Qt itself does it by just * converting QWindow into QWidgetWindow. But the latter one * is private, so we cannot use it. * * We also cannot use QApplication::widegtAt(). We *MUST NOT*! * There is some but in XCB: if we call * QApplication::topLevelAt() during the event processing, the * Enter/Leave events stop arriving. Or, more precisely, they * start to errive at random points in time. Which makes * KisShortcutMatcher go crazy of course. * * So instead of just fetching the toplevel window we decrypt * the pointer using WinId mapping. */ targetWidget = QWidget::find(e->window->winId()); if (targetWidget) { QWidget *childWidget = targetWidget->childAt(e->local.toPoint()); if (childWidget) { targetWidget = childWidget; localValid = false; } } } if (!targetWidget) { targetWidget = QApplication::widgetAt(e->global.toPoint()); localValid = false; if (!targetWidget) return; } if (type == QEvent::TabletPress) { tabletPressWidget = targetWidget; } else if (type == QEvent::TabletRelease) { tabletPressWidget = 0; } QPointF local = e->local; if (!localValid) { QPointF delta = e->global - e->global.toPoint(); local = targetWidget->mapFromGlobal(e->global.toPoint()) + delta; } Qt::MouseButtons stateChange = e->buttons ^ tabletState; Qt::MouseButton button = Qt::NoButton; for (int check = Qt::LeftButton; check <= int(Qt::MaxMouseButton); check = check << 1) { if (check & stateChange) { button = Qt::MouseButton(check); break; } } QTabletEvent ev(type, local, e->global, e->device, e->pointerType, e->pressure, e->xTilt, e->yTilt, e->tangentialPressure, e->rotation, e->z, e->modifiers, e->uid, button, e->buttons); ev.setTimestamp(e->timestamp); QGuiApplication::sendEvent(targetWidget, &ev); tabletState = e->buttons; #else Q_UNUSED(e) #endif } void QWindowSystemInterface::handleTabletEnterProximityEvent(int device, int pointerType, qint64 uid) { qint64 timestamp = g_eventTimer.msecsSinceReference() + g_eventTimer.elapsed(); QTabletEvent ev(QEvent::TabletEnterProximity, QPointF(), QPointF(), device, pointerType, 0, 0, 0, 0, 0, 0, Qt::NoModifier, uid, Qt::NoButton, tabletState); ev.setTimestamp(timestamp); QGuiApplication::sendEvent(qGuiApp, &ev); } void QWindowSystemInterface::handleTabletLeaveProximityEvent(int device, int pointerType, qint64 uid) { qint64 timestamp = g_eventTimer.msecsSinceReference() + g_eventTimer.elapsed(); QTabletEvent ev(QEvent::TabletLeaveProximity, QPointF(), QPointF(), device, pointerType, 0, 0, 0, 0, 0, 0, Qt::NoModifier, uid, Qt::NoButton, tabletState); ev.setTimestamp(timestamp); QGuiApplication::sendEvent(qGuiApp, &ev); } void processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e); void QWindowSystemInterface::handleWheelEvent(QWindow *tlw, ulong timestamp, const QPointF & local, const QPointF & global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase, Qt::MouseEventSource source) { // Qt 4 sends two separate wheel events for horizontal and vertical // deltas. For Qt 5 we want to send the deltas in one event, but at the // same time preserve source and behavior compatibility with Qt 4. // // In addition high-resolution pixel-based deltas are also supported. // Platforms that does not support these may pass a null point here. // Angle deltas must always be sent in addition to pixel deltas. QScopedPointer e; // Pass Qt::ScrollBegin and Qt::ScrollEnd through // even if the wheel delta is null. if (angleDelta.isNull() && phase == Qt::ScrollUpdate) return; // Simple case: vertical deltas only: if (angleDelta.y() != 0 && angleDelta.x() == 0) { e.reset(new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical, mods, phase, source)); processWheelEvent(e.data()); return; } // Simple case: horizontal deltas only: if (angleDelta.y() == 0 && angleDelta.x() != 0) { e.reset(new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, pixelDelta, angleDelta, angleDelta.x(), Qt::Horizontal, mods, phase, source)); processWheelEvent(e.data()); return; } // Both horizontal and vertical deltas: Send two wheel events. // The first event contains the Qt 5 pixel and angle delta as points, // and in addition the Qt 4 compatibility vertical angle delta. e.reset(new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, pixelDelta, angleDelta, angleDelta.y(), Qt::Vertical, mods, phase, source)); processWheelEvent(e.data()); // The second event contains null pixel and angle points and the // Qt 4 compatibility horizontal angle delta. e.reset(new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, QPoint(), QPoint(), angleDelta.x(), Qt::Horizontal, mods, phase, source)); processWheelEvent(e.data()); } void processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e) { #ifndef QT_NO_WHEELEVENT QWindow *window = e->window.data(); QPointF globalPoint = e->globalPos; QPointF localPoint = e->localPos; if (e->nullWindow()) { window = QGuiApplication::topLevelAt(globalPoint.toPoint()); if (window) { QPointF delta = globalPoint - globalPoint.toPoint(); localPoint = window->mapFromGlobal(globalPoint.toPoint()) + delta; } } if (!window) return; // Cut off in Krita... // // QGuiApplicationPrivate::lastCursorPosition = globalPoint; // modifier_buttons = e->modifiers; //if (window->d_func()->blockedByModalWindow) { if (QGuiApplication::modalWindow() && - QGuiApplication::modalWindow() != window) { + QGuiApplication::modalWindow() != window && + QGuiApplication::modalWindow() != window->transientParent()) { // a modal window is blocking this window, don't allow wheel events through return; } #if QT_VERSION >= 0x050500 QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta, e->qt4Delta, e->qt4Orientation, QGuiApplication::mouseButtons(), e->modifiers, e->phase, e->source); #else QWheelEvent ev(localPoint, globalPoint, e->pixelDelta, e->angleDelta, e->qt4Delta, e->qt4Orientation, QGuiApplication::mouseButtons(), e->modifiers, e->phase); #endif ev.setTimestamp(e->timestamp); QGuiApplication::sendEvent(window, &ev); #endif /* ifndef QT_NO_WHEELEVENT */ } diff --git a/libs/ui/kis_popup_palette.cpp b/libs/ui/kis_popup_palette.cpp index b8b7864368..c792573a88 100644 --- a/libs/ui/kis_popup_palette.cpp +++ b/libs/ui/kis_popup_palette.cpp @@ -1,916 +1,941 @@ /* 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_canvas2.h" #include "kis_config.h" #include "kis_popup_palette.h" #include "kis_favorite_resource_manager.h" #include "kis_icon_utils.h" #include "KisResourceServerProvider.h" #include #include #include #include #include #include #include #include #include #include "kis_signal_compressor.h" #include "brushhud/kis_brush_hud.h" #include "brushhud/kis_round_hud_button.h" #include "kis_signals_blocker.h" #include "kis_canvas_controller.h" #include "kis_acyclic_signal_connector.h" 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_coordinatesConverter(coordinatesConverter) , m_viewManager(viewManager) , m_actionManager(viewManager->actionManager()) , m_resourceManager(manager) , m_displayRenderer(displayRenderer) , m_colorChangeCompressor(new KisSignalCompressor(50, KisSignalCompressor::POSTPONE)) , m_actionCollection(viewManager->actionCollection()) , m_acyclicConnector(new KisAcyclicSignalConnector(this)) { // some UI controls are defined and created based off these variables const int borderWidth = 3; if (KisConfig(true).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(KoColor)), m_colorChangeCompressor.data(), SLOT(start())); connect(m_colorChangeCompressor.data(), SIGNAL(timeout()), SLOT(slotEmitColorChanged())); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), m_triangleColorSelector, SLOT(configurationChanged())); m_acyclicConnector->connectForwardKoColor(m_resourceManager, SIGNAL(sigChangeFGColorSelector(KoColor)), this, SLOT(slotExternalFgColorChanged(KoColor))); m_acyclicConnector->connectBackwardKoColor(this, SIGNAL(sigChangefGColor(KoColor)), m_resourceManager, SIGNAL(sigSetFGColor(KoColor))); connect(this, SIGNAL(sigChangeActivePaintop(int)), m_resourceManager, SLOT(slotChangeActivePaintop(int))); connect(this, SIGNAL(sigUpdateRecentColor(int)), m_resourceManager, SLOT(slotUpdateRecentColor(int))); connect(m_resourceManager, SIGNAL(setSelectedColor(int)), SLOT(slotSetSelectedColor(int))); connect(m_resourceManager, SIGNAL(updatePalettes()), SLOT(slotUpdate())); connect(m_resourceManager, SIGNAL(hidePalettes()), SLOT(slotHide())); // This is used to handle a bug: // If pop up palette is visible and a new colour is selected, the new colour // will be added when the user clicks on the canvas to hide the palette // In general, we want to be able to store recent color if the pop up palette // is not visible m_timer.setSingleShot(true); connect(this, SIGNAL(sigTriggerTimer()), this, SLOT(slotTriggerTimer())); connect(&m_timer, SIGNAL(timeout()), this, SLOT(slotEnableChangeFGColor())); connect(this, SIGNAL(sigEnableChangeFGColor(bool)), m_resourceManager, SIGNAL(sigEnableChangeColor(bool))); setCursor(Qt::ArrowCursor); setMouseTracking(true); setHoveredPreset(-1); setHoveredColor(-1); setSelectedColor(-1); m_brushHud = new KisBrushHud(provider, parent); m_brushHud->setMaximumHeight(m_popupPaletteSize); m_brushHud->setVisible(false); const int auxButtonSize = 35; m_settingsButton = new KisRoundHudButton(this); m_settingsButton->setGeometry(m_popupPaletteSize - 2.2 * auxButtonSize, m_popupPaletteSize - auxButtonSize, auxButtonSize, auxButtonSize); connect(m_settingsButton, SIGNAL(clicked()), SLOT(slotShowTagsPopup())); KisConfig cfg(true); m_brushHudButton = new KisRoundHudButton(this); m_brushHudButton->setCheckable(true); m_brushHudButton->setGeometry(m_popupPaletteSize - 1.0 * auxButtonSize, m_popupPaletteSize - auxButtonSize, auxButtonSize, auxButtonSize); connect(m_brushHudButton, SIGNAL(toggled(bool)), SLOT(showHudWidget(bool))); m_brushHudButton->setChecked(cfg.showBrushHud()); // add some stuff below the pop-up palette that will make it easier to use for tablet people QVBoxLayout* vLayout = new QVBoxLayout(this); // main layout QSpacerItem* verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding); vLayout->addSpacerItem(verticalSpacer); // this should push the box to the bottom QHBoxLayout* hLayout = new QHBoxLayout(); vLayout->addLayout(hLayout); mirrorMode = new KisHighlightedToolButton(this); mirrorMode->setCheckable(true); mirrorMode->setFixedSize(35, 35); 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->setToolTip(i18n("Canvas Only")); connect(canvasOnlyButton, SIGNAL(clicked(bool)), this, SLOT(slotCanvasonlyModeClicked())); zoomToOneHundredPercentButton = new QPushButton(this); zoomToOneHundredPercentButton->setText(i18n("100%")); zoomToOneHundredPercentButton->setFixedHeight(35); zoomToOneHundredPercentButton->setToolTip(i18n("Zoom to 100%")); connect(zoomToOneHundredPercentButton, SIGNAL(clicked(bool)), this, SLOT(slotZoomToOneHundredPercentClicked())); zoomCanvasSlider = new QSlider(Qt::Horizontal, this); zoomSliderMinValue = 10; // set in % zoomSliderMaxValue = 200; // set in % zoomCanvasSlider->setRange(zoomSliderMinValue, zoomSliderMaxValue); zoomCanvasSlider->setFixedHeight(35); zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent()); zoomCanvasSlider->setSingleStep(1); zoomCanvasSlider->setPageStep(1); connect(zoomCanvasSlider, SIGNAL(valueChanged(int)), this, SLOT(slotZoomSliderChanged(int))); + connect(zoomCanvasSlider, SIGNAL(sliderPressed()), this, SLOT(slotZoomSliderPressed())); + connect(zoomCanvasSlider, SIGNAL(sliderReleased()), this, SLOT(slotZoomSliderReleased())); slotUpdateIcons(); hLayout->addWidget(mirrorMode); hLayout->addWidget(canvasOnlyButton); hLayout->addWidget(zoomToOneHundredPercentButton); hLayout->addWidget(zoomCanvasSlider); setVisible(true); setVisible(false); + opacityChange = new QGraphicsOpacityEffect(this); + setGraphicsEffect(opacityChange); + // Prevent tablet events from being captured by the canvas setAttribute(Qt::WA_NoMousePropagation, true); } void KisPopupPalette::slotExternalFgColorChanged(const KoColor &color) { //hack to get around cmyk for now. if (color.colorSpace()->colorChannelCount()>3) { KoColor c(KoColorSpaceRegistry::instance()->rgb8()); c.fromKoColor(color); m_triangleColorSelector->slotSetColor(c); } else { m_triangleColorSelector->slotSetColor(color); } } void KisPopupPalette::slotEmitColorChanged() { if (isVisible()) { update(); emit sigChangefGColor(m_triangleColorSelector->getCurrentColor()); } } //setting KisPopupPalette properties int KisPopupPalette::hoveredPreset() const { return m_hoveredPreset; } void KisPopupPalette::setHoveredPreset(int x) { m_hoveredPreset = x; } int KisPopupPalette::hoveredColor() const { return m_hoveredColor; } void KisPopupPalette::setHoveredColor(int x) { m_hoveredColor = x; } int KisPopupPalette::selectedColor() const { return m_selectedColor; } void KisPopupPalette::setSelectedColor(int x) { m_selectedColor = x; } void KisPopupPalette::slotTriggerTimer() { m_timer.start(750); } void KisPopupPalette::slotEnableChangeFGColor() { emit sigEnableChangeFGColor(true); } void KisPopupPalette::slotZoomSliderChanged(int zoom) { emit zoomLevelChanged(zoom); } +void KisPopupPalette::slotZoomSliderPressed() +{ + m_isZoomingCanvas = true; +} + +void KisPopupPalette::slotZoomSliderReleased() +{ + m_isZoomingCanvas = false; +} + void KisPopupPalette::adjustLayout(const QPoint &p) { KIS_ASSERT_RECOVER_RETURN(m_brushHud); if (isVisible() && parentWidget()) { float hudMargin = 30.0; const QRect fitRect = kisGrowRect(parentWidget()->rect(), -20.0); // -20 is widget margin const QPoint paletteCenterOffset(m_popupPaletteSize / 2, m_popupPaletteSize / 2); QRect paletteRect = rect(); paletteRect.moveTo(p - paletteCenterOffset); if (m_brushHudButton->isChecked()) { m_brushHud->updateGeometry(); paletteRect.adjust(0, 0, m_brushHud->width() + hudMargin, 0); } paletteRect = kisEnsureInRect(paletteRect, fitRect); move(paletteRect.topLeft()); m_brushHud->move(paletteRect.topLeft() + QPoint(m_popupPaletteSize + hudMargin, 0)); m_lastCenterPoint = p; } } void KisPopupPalette::slotUpdateIcons() { zoomToOneHundredPercentButton->setIcon(KisIconUtils::loadIcon("zoom-original")); canvasOnlyButton->setIcon(KisIconUtils::loadIcon("document-new")); mirrorMode->setIcon(KisIconUtils::loadIcon("symmetry-horizontal")); m_settingsButton->setIcon(KisIconUtils::loadIcon("configure")); m_brushHud->updateIcons(); m_brushHudButton->setOnOffIcons(KisIconUtils::loadIcon("arrow-left"), KisIconUtils::loadIcon("arrow-right")); } void KisPopupPalette::showHudWidget(bool visible) { KIS_ASSERT_RECOVER_RETURN(m_brushHud); const bool reallyVisible = visible && m_brushHudButton->isChecked(); if (reallyVisible) { m_brushHud->updateProperties(); } m_brushHud->setVisible(reallyVisible); adjustLayout(m_lastCenterPoint); KisConfig cfg(false); cfg.setShowBrushHud(visible); } void KisPopupPalette::showPopupPalette(const QPoint &p) { showPopupPalette(!isVisible()); adjustLayout(p); } void KisPopupPalette::showPopupPalette(bool show) { if (show) { m_hadMousePressSinceOpening = false; m_timeSinceOpening.start(); // don't set the zoom slider if we are outside of the zoom slider bounds. It will change the zoom level to within // the bounds and cause the canvas to jump between the slider's min and max if (m_coordinatesConverter->zoomInPercent() > zoomSliderMinValue && m_coordinatesConverter->zoomInPercent() < zoomSliderMaxValue ){ KisSignalsBlocker b(zoomCanvasSlider); zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent()); // sync the zoom slider } emit sigEnableChangeFGColor(!show); } else { emit sigTriggerTimer(); } setVisible(show); m_brushHud->setVisible(show && m_brushHudButton->isChecked()); } //redefinition of setVariable function to change the scope to private void KisPopupPalette::setVisible(bool b) { QWidget::setVisible(b); } void KisPopupPalette::setParent(QWidget *parent) { m_brushHud->setParent(parent); QWidget::setParent(parent); } QSize KisPopupPalette::sizeHint() const { return QSize(m_popupPaletteSize, m_popupPaletteSize + 50); // last number is the space for the toolbar below } void KisPopupPalette::resizeEvent(QResizeEvent*) { } void KisPopupPalette::paintEvent(QPaintEvent* e) { Q_UNUSED(e); QPainter painter(this); QPen pen(palette().color(QPalette::Text)); pen.setWidth(3); painter.setPen(pen); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); // painting background color indicator QPainterPath bgColor; bgColor.addEllipse(QPoint( 50, 80), 30, 30); painter.fillPath(bgColor, m_displayRenderer->toQColor(m_resourceManager->bgColor())); painter.drawPath(bgColor); // painting foreground color indicator QPainterPath fgColor; fgColor.addEllipse(QPoint( 60, 50), 30, 30); painter.fillPath(fgColor, m_displayRenderer->toQColor(m_triangleColorSelector->getCurrentColor())); painter.drawPath(fgColor); // create a circle background that everything else will go into QPainterPath backgroundContainer; float shrinkCircleAmount = 3;// helps the circle when the stroke is put around it QRectF circleRect(shrinkCircleAmount, shrinkCircleAmount, m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2); backgroundContainer.addEllipse( circleRect ); painter.fillPath(backgroundContainer,palette().brush(QPalette::Background)); painter.drawPath(backgroundContainer); // create a path slightly inside the container circle. this will create a 'track' to indicate that we can rotate the canvas // with the indicator QPainterPath rotationTrackPath; shrinkCircleAmount = 18; QRectF circleRect2(shrinkCircleAmount, shrinkCircleAmount, m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2); rotationTrackPath.addEllipse( circleRect2 ); pen.setWidth(1); painter.setPen(pen); painter.drawPath(rotationTrackPath); // this thing will help indicate where the starting brush preset is at. // also what direction they go to give sor order to the presets populated /* pen.setWidth(6); pen.setCapStyle(Qt::RoundCap); painter.setPen(pen); painter.drawArc(circleRect, (16*90), (16*-30)); // span angle (last parameter) is in 16th of degrees QPainterPath brushDir; brushDir.arcMoveTo(circleRect, 60); brushDir.lineTo(brushDir.currentPosition().x()-5, brushDir.currentPosition().y() - 14); painter.drawPath(brushDir); brushDir.lineTo(brushDir.currentPosition().x()-2, brushDir.currentPosition().y() + 6); painter.drawPath(brushDir); */ // the following things needs to be based off the center, so let's translate the painter painter.translate(m_popupPaletteSize / 2, m_popupPaletteSize / 2); // create the canvas rotation handle QPainterPath rotationIndicator = drawRotationIndicator(m_coordinatesConverter->rotationAngle(), true); painter.fillPath(rotationIndicator,palette().brush(QPalette::Text)); // hover indicator for the canvas rotation if (m_isOverCanvasRotationIndicator == true) { painter.save(); QPen pen(palette().color(QPalette::Highlight)); pen.setWidth(2); painter.setPen(pen); painter.drawPath(rotationIndicator); painter.restore(); } // create a reset canvas rotation indicator to bring the canvas back to 0 degrees QPainterPath resetRotationIndicator = drawRotationIndicator(0, false); QPen resetPen(palette().color(QPalette::Text)); resetPen.setWidth(1); painter.save(); painter.setPen(resetPen); painter.drawPath(resetRotationIndicator); painter.restore(); // painting favorite brushes QList images(m_resourceManager->favoritePresetImages()); // painting favorite brushes pixmap/icon QPainterPath presetPath; for (int pos = 0; pos < numSlots(); pos++) { painter.save(); presetPath = createPathFromPresetIndex(pos); if (pos < images.size()) { painter.setClipPath(presetPath); QRect bounds = presetPath.boundingRect().toAlignedRect(); painter.drawImage(bounds.topLeft() , images.at(pos).scaled(bounds.size() , Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); } else { painter.fillPath(presetPath, palette().brush(QPalette::Window)); // brush slot that has no brush in it } QPen pen = painter.pen(); pen.setWidth(1); painter.setPen(pen); painter.drawPath(presetPath); painter.restore(); } if (hoveredPreset() > -1) { presetPath = createPathFromPresetIndex(hoveredPreset()); QPen pen(palette().color(QPalette::Highlight)); pen.setWidth(3); painter.setPen(pen); painter.drawPath(presetPath); } // paint recent colors area. painter.setPen(Qt::NoPen); float rotationAngle = -360.0 / m_resourceManager->recentColorsTotal(); // there might be no recent colors at the start, so paint a placeholder if (m_resourceManager->recentColorsTotal() == 0) { painter.setBrush(Qt::transparent); QPainterPath emptyRecentColorsPath(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.setPen(QPen(palette().color(QPalette::Background).lighter(150), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); painter.drawPath(emptyRecentColorsPath); } else { for (int pos = 0; pos < m_resourceManager->recentColorsTotal(); pos++) { QPainterPath recentColorsPath(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); //accessing recent color of index pos painter.fillPath(recentColorsPath, m_displayRenderer->toQColor( m_resourceManager->recentColorAt(pos) )); painter.drawPath(recentColorsPath); painter.rotate(rotationAngle); } } // painting hovered color if (hoveredColor() > -1) { painter.setPen(QPen(palette().color(QPalette::Highlight), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); if (m_resourceManager->recentColorsTotal() == 1) { QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.drawPath(path_ColorDonut); } else { painter.rotate((m_resourceManager->recentColorsTotal() + hoveredColor()) *rotationAngle); QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); painter.drawPath(path); painter.rotate(hoveredColor() * -1 * rotationAngle); } } // painting selected color if (selectedColor() > -1) { painter.setPen(QPen(palette().color(QPalette::Highlight).darker(130), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); if (m_resourceManager->recentColorsTotal() == 1) { QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.drawPath(path_ColorDonut); } else { painter.rotate((m_resourceManager->recentColorsTotal() + selectedColor()) *rotationAngle); QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); painter.drawPath(path); painter.rotate(selectedColor() * -1 * rotationAngle); } } + + + // if we are actively rotating the canvas or zooming, make the panel slightly transparent to see the canvas better + if(m_isRotatingCanvasIndicator || m_isZoomingCanvas) { + opacityChange->setOpacity(0.4); + } else { + opacityChange->setOpacity(1.0); + } + } QPainterPath KisPopupPalette::drawDonutPathFull(int x, int y, int inner_radius, int outer_radius) { QPainterPath path; path.addEllipse(QPointF(x, y), outer_radius, outer_radius); path.addEllipse(QPointF(x, y), inner_radius, inner_radius); path.setFillRule(Qt::OddEvenFill); return path; } QPainterPath KisPopupPalette::drawDonutPathAngle(int inner_radius, int outer_radius, int limit) { QPainterPath path; path.moveTo(-0.999 * outer_radius * sin(M_PI / limit), 0.999 * outer_radius * cos(M_PI / limit)); path.arcTo(-1 * outer_radius, -1 * outer_radius, 2 * outer_radius, 2 * outer_radius, -90.0 - 180.0 / limit, 360.0 / limit); path.arcTo(-1 * inner_radius, -1 * inner_radius, 2 * inner_radius, 2 * inner_radius, -90.0 + 180.0 / limit, - 360.0 / limit); path.closeSubpath(); return path; } QPainterPath KisPopupPalette::drawRotationIndicator(qreal rotationAngle, bool canDrag) { // used for canvas rotation. This function gets called twice. Once by the canvas rotation indicator, // and another time by the reset canvas position float canvasRotationRadians = qDegreesToRadians(rotationAngle - 90); // -90 will make 0 degrees be at the top float rotationDialXPosition = qCos(canvasRotationRadians) * (m_popupPaletteSize/2 - 10); // m_popupPaletteSize/2 = radius float rotationDialYPosition = qSin(canvasRotationRadians) * (m_popupPaletteSize/2 - 10); QPainterPath canvasRotationIndicator; int canvasIndicatorSize = 15; 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->localPos(); event->accept(); setToolTip(QString()); setHoveredPreset(-1); setHoveredColor(-1); // calculate if we are over the canvas rotation knob // before we started painting, we moved the painter to the center of the widget, so the X/Y positions are offset. we need to // correct them first before looking for a click event intersection float rotationCorrectedXPos = m_canvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2); float rotationCorrectedYPos = m_canvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2); QRect correctedCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos, m_canvasRotationIndicatorRect.width(), m_canvasRotationIndicatorRect.height()); if (correctedCanvasRotationIndicator.contains(point.x(), point.y())) { m_isOverCanvasRotationIndicator = true; } else { m_isOverCanvasRotationIndicator = false; } if (m_isRotatingCanvasIndicator) { // we are rotating the canvas, so calculate the rotation angle based off the center // calculate the angle we are at first QPoint widgetCenterPoint = QPoint(m_popupPaletteSize/2, m_popupPaletteSize/2); float dX = point.x() - widgetCenterPoint.x(); float dY = point.y() - widgetCenterPoint.y(); float finalAngle = qAtan2(dY,dX) * 180 / M_PI; // what we need if we have two points, but don't know the angle finalAngle = finalAngle + 90; // add 90 degrees so 0 degree position points up float angleDifference = finalAngle - m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs, so find it out KisCanvasController *canvasController = dynamic_cast(m_viewManager->canvasBase()->canvasController()); canvasController->rotateCanvas(angleDifference); + emit sigUpdateCanvas(); } // don't highlight the presets if we are in the middle of rotating the canvas if (m_isRotatingCanvasIndicator == false) { QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); { int pos = calculatePresetIndex(point, m_resourceManager->numFavoritePresets()); if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()) { setToolTip(m_resourceManager->favoritePresetList().at(pos).data()->name()); setHoveredPreset(pos); } } if (pathColor.contains(point)) { int pos = calculateIndex(point, m_resourceManager->recentColorsTotal()); if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) { setHoveredColor(pos); } } } update(); } void KisPopupPalette::mousePressEvent(QMouseEvent *event) { QPointF point = event->localPos(); event->accept(); /** * Tablet support code generates a spurious right-click right after opening * the window, so we should ignore it. Next right-click will be used for * closing the popup palette */ if (!m_hadMousePressSinceOpening && m_timeSinceOpening.elapsed() > 100) { m_hadMousePressSinceOpening = true; } if (event->button() == Qt::LeftButton) { //in favorite brushes area int pos = calculateIndex(point, m_resourceManager->numFavoritePresets()); if (pos >= 0 && pos < m_resourceManager->numFavoritePresets() && isPointInPixmap(point, pos)) { //setSelectedBrush(pos); update(); } if (m_isOverCanvasRotationIndicator) { m_isRotatingCanvasIndicator = true; } // reset the canvas if we are over the reset canvas rotation indicator float rotationCorrectedXPos = m_resetCanvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2); float rotationCorrectedYPos = m_resetCanvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2); QRect correctedResetCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos, m_resetCanvasRotationIndicatorRect.width(), m_resetCanvasRotationIndicatorRect.height()); if (correctedResetCanvasRotationIndicator.contains(point.x(), point.y())) { float angleDifference = -m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs KisCanvasController *canvasController = dynamic_cast(m_viewManager->canvasBase()->canvasController()); canvasController->rotateCanvas(angleDifference); emit sigUpdateCanvas(); } } } void KisPopupPalette::slotShowTagsPopup() { KisPaintOpPresetResourceServer *rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); QStringList tags = rServer->tagNamesList(); std::sort(tags.begin(), tags.end()); 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->localPos(); event->accept(); // see a comment in KisPopupPalette::mousePressEvent if (m_hadMousePressSinceOpening && event->buttons() == Qt::NoButton && event->button() == Qt::RightButton) { showPopupPalette(false); return; } m_isOverCanvasRotationIndicator = false; m_isRotatingCanvasIndicator = false; if (event->button() == Qt::LeftButton) { QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); //in favorite brushes area if (hoveredPreset() > -1) { //setSelectedBrush(hoveredBrush()); emit sigChangeActivePaintop(hoveredPreset()); } if (pathColor.contains(point)) { int pos = calculateIndex(point, m_resourceManager->recentColorsTotal()); if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) { emit sigUpdateRecentColor(pos); } } } } int KisPopupPalette::calculateIndex(QPointF point, int n) { calculatePresetIndex(point, n); //translate to (0,0) point.setX(point.x() - m_popupPaletteSize / 2); point.setY(point.y() - m_popupPaletteSize / 2); //rotate float smallerAngle = M_PI / 2 + M_PI / n - atan2(point.y(), point.x()); float radius = sqrt((float)point.x() * point.x() + point.y() * point.y()); point.setX(radius * cos(smallerAngle)); point.setY(radius * sin(smallerAngle)); //calculate brush index int pos = floor(acos(point.x() / radius) * n / (2 * M_PI)); if (point.y() < 0) pos = n - pos - 1; return pos; } bool KisPopupPalette::isPointInPixmap(QPointF &point, int pos) { if (createPathFromPresetIndex(pos).contains(point + QPointF(-m_popupPaletteSize / 2, -m_popupPaletteSize / 2))) { return true; } return false; } KisPopupPalette::~KisPopupPalette() { } QPainterPath KisPopupPalette::createPathFromPresetIndex(int index) { qreal angleSlice = 360.0 / numSlots() ; // how many degrees each slice will get // the starting angle of the slice we need to draw. the negative sign makes us go clockwise. // adding 90 degrees makes us start at the top. otherwise we would start at the right qreal startingAngle = -(index * angleSlice) + 90; // the radius will get smaller as the amount of presets shown increases. 10 slots == 41 qreal 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(true); return qMax(config.favoritePresets(), 10); } diff --git a/libs/ui/kis_popup_palette.h b/libs/ui/kis_popup_palette.h index 3864c079c3..5935c1d52d 100644 --- a/libs/ui/kis_popup_palette.h +++ b/libs/ui/kis_popup_palette.h @@ -1,180 +1,190 @@ /* This file is part of the KDE project Copyright 2009 Vera Lukman 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. */ #ifndef KIS_POPUP_PALETTE_H #define KIS_POPUP_PALETTE_H #include #include #include +#include #include "KisViewManager.h" #include "kactioncollection.h" #include "kis_tool_button.h" #include "kis_highlighted_button.h" #include class KisFavoriteResourceManager; class QWidget; class KoColor; class KoTriangleColorSelector; class KisSignalCompressor; class KisBrushHud; class KisRoundHudButton; class KisCanvasResourceProvider; class KisVisualColorSelector; class KisAcyclicSignalConnector; class KisPopupPalette : public QWidget { Q_OBJECT Q_PROPERTY(int hoveredPreset READ hoveredPreset WRITE setHoveredPreset) Q_PROPERTY(int hoveredColor READ hoveredColor WRITE setHoveredColor) Q_PROPERTY(int selectedColor READ selectedColor WRITE setSelectedColor) public: KisPopupPalette(KisViewManager*, KisCoordinatesConverter* ,KisFavoriteResourceManager*, const KoColorDisplayRendererInterface *displayRenderer, KisCanvasResourceProvider *provider, QWidget *parent = 0); ~KisPopupPalette() override; QSize sizeHint() const override; void showPopupPalette(const QPoint&); void showPopupPalette(bool b); //functions to set up selectedBrush void setSelectedBrush(int x); int selectedBrush() const; //functions to set up selectedColor void setSelectedColor(int x); int selectedColor() const; void setParent(QWidget *parent); void tabletEvent(QTabletEvent *event) override; protected: void paintEvent(QPaintEvent*) override; void resizeEvent(QResizeEvent*) override; void mouseReleaseEvent(QMouseEvent*) override; void mouseMoveEvent(QMouseEvent*) override; void mousePressEvent(QMouseEvent*) override; //functions to calculate index of favorite brush or recent color in array //n is the total number of favorite brushes or recent colors int calculateIndex(QPointF, int n); int calculatePresetIndex(QPointF, int n); //functions to set up hoveredBrush void setHoveredPreset(int x); int hoveredPreset() const; //functions to set up hoveredColor void setHoveredColor(int x); int hoveredColor() const; private: void setVisible(bool b) override; QPainterPath drawDonutPathFull(int, int, int, int); QPainterPath drawDonutPathAngle(int, int, int); QPainterPath drawRotationIndicator(qreal rotationAngle, bool canDrag); bool isPointInPixmap(QPointF&, int pos); QPainterPath createPathFromPresetIndex(int index); int numSlots(); void adjustLayout(const QPoint &p); private: int m_hoveredPreset {0}; int m_hoveredColor {0}; int m_selectedColor {0}; QElapsedTimer m_timeSinceOpening; bool m_hadMousePressSinceOpening {false}; KisCoordinatesConverter *m_coordinatesConverter; KisViewManager *m_viewManager; KisActionManager *m_actionManager; KisFavoriteResourceManager *m_resourceManager; KisColorSelectorInterface *m_triangleColorSelector {0}; const KoColorDisplayRendererInterface *m_displayRenderer; QScopedPointer m_colorChangeCompressor; KActionCollection *m_actionCollection; QTimer m_timer; KisBrushHud *m_brushHud {0}; float m_popupPaletteSize {385.0}; float m_colorHistoryInnerRadius {72.0}; float m_colorHistoryOuterRadius {92.0}; KisRoundHudButton *m_settingsButton {0}; KisRoundHudButton *m_brushHudButton {0}; QPoint m_lastCenterPoint; QRect m_canvasRotationIndicatorRect; QRect m_resetCanvasRotationIndicatorRect; bool m_isOverCanvasRotationIndicator {false}; bool m_isRotatingCanvasIndicator {false}; + bool m_isZoomingCanvas {false}; + KisHighlightedToolButton *mirrorMode {0}; KisHighlightedToolButton *canvasOnlyButton {0}; QPushButton *zoomToOneHundredPercentButton {0}; QSlider *zoomCanvasSlider {0}; int zoomSliderMinValue {10}; int zoomSliderMaxValue {200}; KisAcyclicSignalConnector *m_acyclicConnector = 0; + // updates the transparency and effects of the whole widget + QGraphicsOpacityEffect *opacityChange {0}; + Q_SIGNALS: void sigChangeActivePaintop(int); void sigUpdateRecentColor(int); void sigChangefGColor(const KoColor&); void sigUpdateCanvas(); void zoomLevelChanged(int); // These are 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 void sigEnableChangeFGColor(bool); void sigTriggerTimer(); public Q_SLOTS: void slotUpdateIcons(); private Q_SLOTS: void slotExternalFgColorChanged(const KoColor &color); void slotEmitColorChanged(); void slotSetSelectedColor(int x) { setSelectedColor(x); update(); } void slotTriggerTimer(); void slotEnableChangeFGColor(); void slotUpdate() { update(); } void slotHide() { showPopupPalette(false); } void slotShowTagsPopup(); void showHudWidget(bool visible); void slotmirroModeClicked(); void slotCanvasonlyModeClicked(); void slotZoomToOneHundredPercentClicked(); void slotZoomSliderChanged(int zoom); + + void slotZoomSliderPressed(); + void slotZoomSliderReleased(); + }; #endif // KIS_POPUP_PALETTE_H diff --git a/libs/ui/tool/KisSelectionToolFactoryBase.cpp b/libs/ui/tool/KisSelectionToolFactoryBase.cpp new file mode 100644 index 0000000000..2e33e31593 --- /dev/null +++ b/libs/ui/tool/KisSelectionToolFactoryBase.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018 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 "KisSelectionToolFactoryBase.h" + +#include + +KisSelectionToolFactoryBase::KisSelectionToolFactoryBase(const QString &id) + : KisToolPaintFactoryBase(id) +{ +} + +KisSelectionToolFactoryBase::~KisSelectionToolFactoryBase() +{ +} + +QList KisSelectionToolFactoryBase::createActionsImpl() +{ + KisActionRegistry *actionRegistry = KisActionRegistry::instance(); + QList actions = KisToolPaintFactoryBase::createActionsImpl(); + + actions << actionRegistry->makeQAction("selection_tool_mode_add"); + actions << actionRegistry->makeQAction("selection_tool_mode_replace"); + actions << actionRegistry->makeQAction("selection_tool_mode_subtract"); + actions << actionRegistry->makeQAction("selection_tool_mode_intersect"); + + return actions; +} + +KisToolPolyLineFactoryBase::KisToolPolyLineFactoryBase(const QString &id) + : KisSelectionToolFactoryBase(id) +{ + +} + +KisToolPolyLineFactoryBase::~KisToolPolyLineFactoryBase() +{ + +} + +QList KisToolPolyLineFactoryBase::createActionsImpl() +{ + KisActionRegistry *actionRegistry = KisActionRegistry::instance(); + QList actions = KisSelectionToolFactoryBase::createActionsImpl(); + + actions << actionRegistry->makeQAction("undo_polygon_selection"); + actions << actionRegistry->makeQAction("selection_tool_mode_add"); + + return actions; +} diff --git a/libs/ui/tool/KisSelectionToolFactoryBase.h b/libs/ui/tool/KisSelectionToolFactoryBase.h new file mode 100644 index 0000000000..a2e73a0ff9 --- /dev/null +++ b/libs/ui/tool/KisSelectionToolFactoryBase.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 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. + */ +#ifndef KISSELECTIONTOOLFACTORYBASE_H +#define KISSELECTIONTOOLFACTORYBASE_H + +#include "KisToolPaintFactoryBase.h" + +#include "kritaui_export.h" + +class KRITAUI_EXPORT KisSelectionToolFactoryBase : public KisToolPaintFactoryBase +{ +public: + explicit KisSelectionToolFactoryBase(const QString &id); + ~KisSelectionToolFactoryBase() override; +protected: + QList createActionsImpl() override; +}; + +class KRITAUI_EXPORT KisToolPolyLineFactoryBase : public KisSelectionToolFactoryBase +{ +public: + explicit KisToolPolyLineFactoryBase(const QString &id); + ~KisToolPolyLineFactoryBase() override; +protected: + QList createActionsImpl() override; +}; + + +#endif diff --git a/libs/ui/tool/KisToolPaintFactoryBase.cpp b/libs/ui/tool/KisToolPaintFactoryBase.cpp new file mode 100644 index 0000000000..227c846c2a --- /dev/null +++ b/libs/ui/tool/KisToolPaintFactoryBase.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 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 "KisToolPaintFactoryBase.h" + +#include +#include + +#include + +KisToolPaintFactoryBase::KisToolPaintFactoryBase(const QString &id) + : KoToolFactoryBase(id) +{ +} + +KisToolPaintFactoryBase::~KisToolPaintFactoryBase() +{ +} + +QList KisToolPaintFactoryBase::createActionsImpl() +{ + KisActionRegistry *actionRegistry = KisActionRegistry::instance(); + QList actions; + + KisAction *increaseBrushSize = new KisAction(i18n("Increase Brush Size")); + increaseBrushSize->setObjectName("increase_brush_size"); + increaseBrushSize->setShortcut(Qt::Key_BracketRight); + actionRegistry->propertizeAction("increase_brush_size", increaseBrushSize); + + actions << increaseBrushSize; + + KisAction *decreaseBrushSize = new KisAction(i18n("Decrease Brush Size")); + decreaseBrushSize->setShortcut(Qt::Key_BracketLeft); + decreaseBrushSize->setObjectName("decrease_brush_size"); + actionRegistry->propertizeAction("decrease_brush_size", decreaseBrushSize); + + actions << decreaseBrushSize; + + return actions; +} diff --git a/libs/ui/tool/KisToolPaintFactoryBase.h b/libs/ui/tool/KisToolPaintFactoryBase.h new file mode 100644 index 0000000000..8187615ae8 --- /dev/null +++ b/libs/ui/tool/KisToolPaintFactoryBase.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 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. + */ +#ifndef KISTOOLPAINTFACTORYBASE_H +#define KISTOOLPAINTFACTORYBASE_H + +#include + +#include "kritaui_export.h" + +class KRITAUI_EXPORT KisToolPaintFactoryBase : public KoToolFactoryBase +{ +public: + explicit KisToolPaintFactoryBase(const QString &id); + ~KisToolPaintFactoryBase() override; +protected: + QList createActionsImpl() override; + +}; + +#endif // KISTOOLPAINTFACTORYBASE_H diff --git a/libs/ui/tool/kis_tool.cc b/libs/ui/tool/kis_tool.cc index 827e592733..6008074ea9 100644 --- a/libs/ui/tool/kis_tool.cc +++ b/libs/ui/tool/kis_tool.cc @@ -1,709 +1,655 @@ /* * Copyright (c) 2006, 2010 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_tool.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_node_manager.h" #include #include #include #include #include #include #include #include #include #include #include #include "opengl/kis_opengl_canvas2.h" #include "kis_canvas_resource_provider.h" #include "canvas/kis_canvas2.h" #include "kis_coordinates_converter.h" #include "filter/kis_filter_configuration.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_cursor.h" #include #include "kis_resources_snapshot.h" #include #include "kis_action_registry.h" #include "kis_tool_utils.h" struct Q_DECL_HIDDEN KisTool::Private { QCursor cursor; // the cursor that should be shown on tool activation. // From the canvas resources KoPatternSP currentPattern; KoAbstractGradientSP currentGradient; KoColor currentFgColor; KoColor currentBgColor; float currentExposure{1.0}; KisFilterConfigurationSP currentGenerator; QWidget* optionWidget{0}; ToolMode m_mode{HOVER_MODE}; bool m_isActive{false}; }; KisTool::KisTool(KoCanvasBase * canvas, const QCursor & cursor) : KoToolBase(canvas) , d(new Private) { d->cursor = cursor; connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle())); connect(this, SIGNAL(isActiveChanged(bool)), SLOT(resetCursorStyle())); - - KActionCollection *collection = this->canvas()->canvasController()->actionCollection(); - - if (!collection->action("toggle_fg_bg")) { - QAction *toggleFgBg = KisActionRegistry::instance()->makeQAction("toggle_fg_bg", collection); - collection->addAction("toggle_fg_bg", toggleFgBg); - } - - if (!collection->action("reset_fg_bg")) { - QAction *toggleFgBg = KisActionRegistry::instance()->makeQAction("reset_fg_bg", collection); - collection->addAction("reset_fg_bg", toggleFgBg); - } - - addAction("toggle_fg_bg", dynamic_cast(collection->action("toggle_fg_bg"))); - addAction("reset_fg_bg", dynamic_cast(collection->action("reset_fg_bg"))); - } KisTool::~KisTool() { delete d; } void KisTool::activate(ToolActivation activation, const QSet &shapes) { KoToolBase::activate(activation, shapes); resetCursorStyle(); if (!canvas()) return; if (!canvas()->resourceManager()) return; d->currentFgColor = canvas()->resourceManager()->resource(KoCanvasResourceProvider::ForegroundColor).value(); d->currentBgColor = canvas()->resourceManager()->resource(KoCanvasResourceProvider::BackgroundColor).value(); if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentPattern)) { d->currentPattern = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPattern).value(); } if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGradient)) { d->currentGradient = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGradient).value(); } KisPaintOpPresetSP preset = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); if (preset && preset->settings()) { preset->settings()->activate(); } if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::HdrExposure)) { d->currentExposure = static_cast(canvas()->resourceManager()->resource(KisCanvasResourceProvider::HdrExposure).toDouble()); } if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGeneratorConfiguration)) { d->currentGenerator = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value(); } - connect(action("toggle_fg_bg"), SIGNAL(triggered()), SLOT(slotToggleFgBg()), Qt::UniqueConnection); - connect(action("reset_fg_bg"), SIGNAL(triggered()), SLOT(slotResetFgBg()), Qt::UniqueConnection); - d->m_isActive = true; emit isActiveChanged(true); } void KisTool::deactivate() { - bool result = true; - - result &= disconnect(action("toggle_fg_bg"), 0, this, 0); - result &= disconnect(action("reset_fg_bg"), 0, this, 0); - - if (!result) { - warnKrita << "WARNING: KisTool::deactivate() failed to disconnect" - << "some signal connections. Your actions might be executed twice!"; - } - d->m_isActive = false; emit isActiveChanged(false); KoToolBase::deactivate(); } void KisTool::canvasResourceChanged(int key, const QVariant & v) { QString formattedBrushName; if (key == KisCanvasResourceProvider::CurrentPaintOpPreset) { formattedBrushName = v.value()->name().replace("_", " "); } switch (key) { case(KoCanvasResourceProvider::ForegroundColor): d->currentFgColor = v.value(); break; case(KoCanvasResourceProvider::BackgroundColor): d->currentBgColor = v.value(); break; case(KisCanvasResourceProvider::CurrentPattern): d->currentPattern = v.value(); break; case(KisCanvasResourceProvider::CurrentGradient): d->currentGradient = v.value(); break; case(KisCanvasResourceProvider::HdrExposure): d->currentExposure = static_cast(v.toDouble()); break; case(KisCanvasResourceProvider::CurrentGeneratorConfiguration): d->currentGenerator = static_cast(v.value()); break; case(KisCanvasResourceProvider::CurrentPaintOpPreset): emit statusTextChanged(formattedBrushName); break; case(KisCanvasResourceProvider::CurrentKritaNode): resetCursorStyle(); break; default: break; // Do nothing }; } void KisTool::updateSettingsViews() { } QPointF KisTool::widgetCenterInWidgetPixels() { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); const KisCoordinatesConverter *converter = kritaCanvas->coordinatesConverter(); return converter->flakeToWidget(converter->flakeCenterPoint()); } QPointF KisTool::convertDocumentToWidget(const QPointF& pt) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->coordinatesConverter()->documentToWidget(pt); } QPointF KisTool::convertToPixelCoord(KoPointerEvent *e) { if (!image()) return e->point; return image()->documentToPixel(e->point); } QPointF KisTool::convertToPixelCoord(const QPointF& pt) { if (!image()) return pt; return image()->documentToPixel(pt); } QPointF KisTool::convertToPixelCoordAndSnap(KoPointerEvent *e, const QPointF &offset, bool useModifiers) { if (!image()) return e->point; KoSnapGuide *snapGuide = canvas()->snapGuide(); QPointF pos = snapGuide->snap(e->point, offset, useModifiers ? e->modifiers() : Qt::NoModifier); return image()->documentToPixel(pos); } QPointF KisTool::convertToPixelCoordAndSnap(const QPointF& pt, const QPointF &offset) { if (!image()) return pt; KoSnapGuide *snapGuide = canvas()->snapGuide(); QPointF pos = snapGuide->snap(pt, offset, Qt::NoModifier); return image()->documentToPixel(pos); } QPoint KisTool::convertToImagePixelCoordFloored(KoPointerEvent *e) { if (!image()) return e->point.toPoint(); return image()->documentToImagePixelFloored(e->point); } QPointF KisTool::viewToPixel(const QPointF &viewCoord) const { if (!image()) return viewCoord; return image()->documentToPixel(canvas()->viewConverter()->viewToDocument(viewCoord)); } QRectF KisTool::convertToPt(const QRectF &rect) { if (!image()) return rect; QRectF r; //We add 1 in the following to the extreme coords because a pixel always has size r.setCoords(int(rect.left()) / image()->xRes(), int(rect.top()) / image()->yRes(), int(rect.right()) / image()->xRes(), int( rect.bottom()) / image()->yRes()); return r; } qreal KisTool::convertToPt(qreal value) { const qreal avgResolution = 0.5 * (image()->xRes() + image()->yRes()); return value / avgResolution; } QPointF KisTool::pixelToView(const QPoint &pixelCoord) const { if (!image()) return pixelCoord; QPointF documentCoord = image()->pixelToDocument(pixelCoord); return canvas()->viewConverter()->documentToView(documentCoord); } QPointF KisTool::pixelToView(const QPointF &pixelCoord) const { if (!image()) return pixelCoord; QPointF documentCoord = image()->pixelToDocument(pixelCoord); return canvas()->viewConverter()->documentToView(documentCoord); } QRectF KisTool::pixelToView(const QRectF &pixelRect) const { if (!image()) return pixelRect; QPointF topLeft = pixelToView(pixelRect.topLeft()); QPointF bottomRight = pixelToView(pixelRect.bottomRight()); return QRectF(topLeft, bottomRight); } QPainterPath KisTool::pixelToView(const QPainterPath &pixelPolygon) const { QTransform matrix; qreal zoomX, zoomY; canvas()->viewConverter()->zoom(&zoomX, &zoomY); matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes()); return matrix.map(pixelPolygon); } QPolygonF KisTool::pixelToView(const QPolygonF &pixelPath) const { QTransform matrix; qreal zoomX, zoomY; canvas()->viewConverter()->zoom(&zoomX, &zoomY); matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes()); return matrix.map(pixelPath); } void KisTool::updateCanvasPixelRect(const QRectF &pixelRect) { canvas()->updateCanvas(convertToPt(pixelRect)); } void KisTool::updateCanvasViewRect(const QRectF &viewRect) { canvas()->updateCanvas(canvas()->viewConverter()->viewToDocument(viewRect)); } KisImageWSP KisTool::image() const { // For now, krita tools only work in krita, not for a krita shape. Krita shapes are for 2.1 KisCanvas2 * kisCanvas = dynamic_cast(canvas()); if (kisCanvas) { return kisCanvas->currentImage(); } return 0; } QCursor KisTool::cursor() const { return d->cursor; } void KisTool::notifyModified() const { if (image()) { image()->setModified(); } } KoPatternSP KisTool::currentPattern() { return d->currentPattern; } KoAbstractGradientSP KisTool::currentGradient() { return d->currentGradient; } KisPaintOpPresetSP KisTool::currentPaintOpPreset() { return canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); } KisNodeSP KisTool::currentNode() const { KisNodeSP node = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value(); return node; } KisNodeList KisTool::selectedNodes() const { KisCanvas2 * kiscanvas = static_cast(canvas()); KisViewManager* viewManager = kiscanvas->viewManager(); return viewManager->nodeManager()->selectedNodes(); } KoColor KisTool::currentFgColor() { return d->currentFgColor; } KoColor KisTool::currentBgColor() { return d->currentBgColor; } KisImageWSP KisTool::currentImage() { return image(); } KisFilterConfigurationSP KisTool::currentGenerator() { return d->currentGenerator; } void KisTool::setMode(ToolMode mode) { d->m_mode = mode; } KisTool::ToolMode KisTool::mode() const { return d->m_mode; } void KisTool::setCursor(const QCursor &cursor) { d->cursor = cursor; } KisTool::AlternateAction KisTool::actionToAlternateAction(ToolAction action) { KIS_ASSERT_RECOVER_RETURN_VALUE(action != Primary, Secondary); return (AlternateAction)action; } void KisTool::activatePrimaryAction() { resetCursorStyle(); } void KisTool::deactivatePrimaryAction() { resetCursorStyle(); } void KisTool::beginPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::beginPrimaryDoubleClickAction(KoPointerEvent *event) { beginPrimaryAction(event); } void KisTool::continuePrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::endPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); } bool KisTool::primaryActionSupportsHiResEvents() const { return false; } void KisTool::activateAlternateAction(AlternateAction action) { Q_UNUSED(action); } void KisTool::deactivateAlternateAction(AlternateAction action) { Q_UNUSED(action); } void KisTool::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(event); Q_UNUSED(action); } void KisTool::beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action) { beginAlternateAction(event, action); } void KisTool::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(event); Q_UNUSED(action); } void KisTool::endAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(event); Q_UNUSED(action); } void KisTool::mouseDoubleClickEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::mouseTripleClickEvent(KoPointerEvent *event) { mouseDoubleClickEvent(event); } void KisTool::mousePressEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::mouseReleaseEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::mouseMoveEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::deleteSelection() { KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager()); if (!blockUntilOperationsFinished()) { return; } if (!KisToolUtils::clearImage(image(), resources->currentNode(), resources->activeSelection())) { KoToolBase::deleteSelection(); } } QWidget* KisTool::createOptionWidget() { d->optionWidget = new QLabel(i18n("No options")); d->optionWidget->setObjectName("SpecialSpacer"); return d->optionWidget; } #define NEAR_VAL -1000.0 #define FAR_VAL 1000.0 #define PROGRAM_VERTEX_ATTRIBUTE 0 void KisTool::paintToolOutline(QPainter* painter, const QPainterPath &path) { KisOpenGLCanvas2 *canvasWidget = dynamic_cast(canvas()->canvasWidget()); if (canvasWidget) { painter->beginNativePainting(); canvasWidget->paintToolOutline(path); painter->endNativePainting(); } else { painter->save(); painter->setCompositionMode(QPainter::RasterOp_SourceXorDestination); painter->setPen(QColor(128, 255, 128)); painter->drawPath(path); painter->restore(); } } void KisTool::resetCursorStyle() { useCursor(d->cursor); } bool KisTool::overrideCursorIfNotEditable() { // override cursor for canvas iff this tool is active // and we can't paint on the active layer if (isActive()) { KisNodeSP node = currentNode(); if (node && !node->isEditable()) { canvas()->setCursor(Qt::ForbiddenCursor); return true; } } return false; } bool KisTool::blockUntilOperationsFinished() { KisCanvas2 * kiscanvas = static_cast(canvas()); KisViewManager* viewManager = kiscanvas->viewManager(); return viewManager->blockUntilOperationsFinished(image()); } void KisTool::blockUntilOperationsFinishedForced() { KisCanvas2 * kiscanvas = static_cast(canvas()); KisViewManager* viewManager = kiscanvas->viewManager(); viewManager->blockUntilOperationsFinishedForced(image()); } bool KisTool::isActive() const { return d->m_isActive; } -void KisTool::slotToggleFgBg() -{ - KoCanvasResourceProvider* resourceManager = canvas()->resourceManager(); - KoColor newFg = resourceManager->backgroundColor(); - KoColor newBg = resourceManager->foregroundColor(); - - /** - * NOTE: Some of color selectors do not differentiate foreground - * and background colors, so if one wants them to end up - * being set up to foreground color, it should be set the - * last. - */ - resourceManager->setBackgroundColor(newBg); - resourceManager->setForegroundColor(newFg); -} - -void KisTool::slotResetFgBg() -{ - KoCanvasResourceProvider* resourceManager = canvas()->resourceManager(); - - // see a comment in slotToggleFgBg() - resourceManager->setBackgroundColor(KoColor(Qt::white, KoColorSpaceRegistry::instance()->rgb8())); - resourceManager->setForegroundColor(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8())); -} - bool KisTool::nodeEditable() { KisNodeSP node = currentNode(); if (!node) { return false; } bool blockedNoIndirectPainting = false; const bool presetUsesIndirectPainting = !currentPaintOpPreset()->settings()->paintIncremental(); if (!presetUsesIndirectPainting) { const KisIndirectPaintingSupport *indirectPaintingLayer = dynamic_cast(node.data()); if (indirectPaintingLayer) { blockedNoIndirectPainting = !indirectPaintingLayer->supportsNonIndirectPainting(); } } bool nodeEditable = node->isEditable() && !blockedNoIndirectPainting; if (!nodeEditable) { KisCanvas2 * kiscanvas = static_cast(canvas()); QString message; if (!node->visible() && node->userLocked()) { message = i18n("Layer is locked and invisible."); } else if (node->userLocked()) { message = i18n("Layer is locked."); } else if(!node->visible()) { message = i18n("Layer is invisible."); } else if (blockedNoIndirectPainting) { message = i18n("Layer can be painted in Wash Mode only."); } else { message = i18n("Group not editable."); } kiscanvas->viewManager()->showFloatingMessage(message, KisIconUtils::loadIcon("object-locked")); } return nodeEditable; } bool KisTool::selectionEditable() { KisCanvas2 * kisCanvas = static_cast(canvas()); KisViewManager * view = kisCanvas->viewManager(); bool editable = view->selectionEditable(); if (!editable) { KisCanvas2 * kiscanvas = static_cast(canvas()); kiscanvas->viewManager()->showFloatingMessage(i18n("Local selection is locked."), KisIconUtils::loadIcon("object-locked")); } return editable; } void KisTool::listenToModifiers(bool listen) { Q_UNUSED(listen); } bool KisTool::listeningToModifiers() { return false; } diff --git a/libs/ui/tool/kis_tool.h b/libs/ui/tool/kis_tool.h index f8aba280b9..218926159e 100644 --- a/libs/ui/tool/kis_tool.h +++ b/libs/ui/tool/kis_tool.h @@ -1,322 +1,318 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_H_ #define KIS_TOOL_H_ #include #include #include #include #include #include #include #include #ifdef __GNUC__ #define WARN_WRONG_MODE(_mode) warnKrita << "Unexpected tool event has come to" << __func__ << "while being mode" << _mode << "!" #else #define WARN_WRONG_MODE(_mode) warnKrita << "Unexpected tool event has come while being mode" << _mode << "!" #endif #define CHECK_MODE_SANITY_OR_RETURN(_mode) if (mode() != _mode) { WARN_WRONG_MODE(mode()); return; } class KoCanvasBase; class KisFilterConfiguration; class QPainter; class QPainterPath; class QPolygonF; /// Definitions of the toolgroups of Krita static const QString TOOL_TYPE_SHAPE = "0 Krita/Shape"; // Geometric shapes like ellipses and lines static const QString TOOL_TYPE_TRANSFORM = "2 Krita/Transform"; // Tools that transform the layer; static const QString TOOL_TYPE_FILL = "3 Krita/Fill"; // Tools that fill parts of the canvas static const QString TOOL_TYPE_VIEW = "4 Krita/View"; // Tools that affect the canvas: pan, zoom, etc. static const QString TOOL_TYPE_SELECTION = "5 Krita/Select"; // Tools that select pixels //activation id for Krita tools, Krita tools are always active and handle locked and invisible layers by themself static const QString KRITA_TOOL_ACTIVATION_ID = "flake/always"; #include class KRITAUI_EXPORT KisTool : public KoToolBase { Q_OBJECT Q_PROPERTY(bool isActive READ isActive NOTIFY isActiveChanged) public: enum { FLAG_USES_CUSTOM_PRESET=0x01, FLAG_USES_CUSTOM_COMPOSITEOP=0x02, FLAG_USES_CUSTOM_SIZE=0x04 }; KisTool(KoCanvasBase * canvas, const QCursor & cursor); ~KisTool() override; virtual int flags() const { return 0; } void deleteSelection() override; // KoToolBase Implementation. public: /** * Called by KisToolProxy when the primary action of the tool is * going to be started now, that is when all the modifiers are * pressed and the only thing left is just to press the mouse * button. On coming of this callback the tool is supposed to * prepare the cursor and/or the outline to show the user shat is * going to happen next */ virtual void activatePrimaryAction(); /** * Called by KisToolProxy when the primary is no longer possible * to be started now, e.g. when its modifiers and released. The * tool is supposed revert all the preparetions it has doen in * activatePrimaryAction(). */ virtual void deactivatePrimaryAction(); /** * Called by KisToolProxy when a primary action for the tool is * started. The \p event stores the original event that * started the stroke. The \p event is _accepted_ by default. If * the tool decides to ignore this particular action (e.g. when * the node is not editable), it should call event->ignore(). Then * no further continuePrimaryAction() or endPrimaryAction() will * be called until the next user action. */ virtual void beginPrimaryAction(KoPointerEvent *event); /** * Called by KisToolProxy when the primary action is in progress * of pointer movement. If the tool has ignored the event in * beginPrimaryAction(), this method will not be called. */ virtual void continuePrimaryAction(KoPointerEvent *event); /** * Called by KisToolProxy when the primary action is being * finished, that is while mouseRelease or tabletRelease event. * If the tool has ignored the event in beginPrimaryAction(), this * method will not be called. */ virtual void endPrimaryAction(KoPointerEvent *event); /** * The same as beginPrimaryAction(), but called when the stroke is * started by a double-click * * \see beginPrimaryAction() */ virtual void beginPrimaryDoubleClickAction(KoPointerEvent *event); /** * Returns true if the tool can handle (and wants to handle) a * very tight flow of input events from the tablet */ virtual bool primaryActionSupportsHiResEvents() const; enum ToolAction { Primary, AlternateChangeSize, AlternatePickFgNode, AlternatePickBgNode, AlternatePickFgImage, AlternatePickBgImage, AlternateSecondary, AlternateThird, AlternateFourth, AlternateFifth, Alternate_NONE = 10000 }; // Technically users are allowed to configure this, but nobody ever would do that. // So these can basically be thought of as aliases to ctrl+click, etc. enum AlternateAction { ChangeSize = AlternateChangeSize, // Default: Shift+Left click PickFgNode = AlternatePickFgNode, // Default: Ctrl+Alt+Left click PickBgNode = AlternatePickBgNode, // Default: Ctrl+Alt+Right click PickFgImage = AlternatePickFgImage, // Default: Ctrl+Left click PickBgImage = AlternatePickBgImage, // Default: Ctrl+Right click Secondary = AlternateSecondary, Third = AlternateThird, Fourth = AlternateFourth, Fifth = AlternateFifth, NONE = 10000 }; static AlternateAction actionToAlternateAction(ToolAction action); virtual void activateAlternateAction(AlternateAction action); virtual void deactivateAlternateAction(AlternateAction action); virtual void beginAlternateAction(KoPointerEvent *event, AlternateAction action); virtual void continueAlternateAction(KoPointerEvent *event, AlternateAction action); virtual void endAlternateAction(KoPointerEvent *event, AlternateAction action); virtual void beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action); void mousePressEvent(KoPointerEvent *event) override; void mouseDoubleClickEvent(KoPointerEvent *event) override; void mouseTripleClickEvent(KoPointerEvent *event) override; void mouseReleaseEvent(KoPointerEvent *event) override; void mouseMoveEvent(KoPointerEvent *event) override; bool isActive() const; public Q_SLOTS: void activate(ToolActivation activation, const QSet &shapes) override; void deactivate() override; void canvasResourceChanged(int key, const QVariant & res) override; // Implement this slot in case there are any widgets or properties which need // to be updated after certain operations, to reflect the inner state correctly. // At the moment this is used for smoothing options in the freehand brush, but // this will likely be expanded. virtual void updateSettingsViews(); Q_SIGNALS: void isActiveChanged(bool isActivated); protected: // conversion methods are also needed by the paint information builder friend class KisToolPaintingInformationBuilder; /// Convert from native (postscript points) to image pixel /// coordinates. QPointF convertToPixelCoord(KoPointerEvent *e); QPointF convertToPixelCoord(const QPointF& pt); QPointF convertToPixelCoordAndSnap(KoPointerEvent *e, const QPointF &offset = QPointF(), bool useModifiers = true); QPointF convertToPixelCoordAndSnap(const QPointF& pt, const QPointF &offset = QPointF()); protected: QPointF widgetCenterInWidgetPixels(); QPointF convertDocumentToWidget(const QPointF& pt); /// Convert from native (postscript points) to integer image pixel /// coordinates. This rounds down (not truncate) the pixel coordinates and /// should be used in preference to QPointF::toPoint(), which rounds, /// to ensure the cursor acts on the pixel it is visually over. QPoint convertToImagePixelCoordFloored(KoPointerEvent *e); QRectF convertToPt(const QRectF &rect); qreal convertToPt(qreal value); QPointF viewToPixel(const QPointF &viewCoord) const; /// Convert an integer pixel coordinate into a view coordinate. /// The view coordinate is at the centre of the pixel. QPointF pixelToView(const QPoint &pixelCoord) const; /// Convert a floating point pixel coordinate into a view coordinate. QPointF pixelToView(const QPointF &pixelCoord) const; /// Convert a pixel rectangle into a view rectangle. QRectF pixelToView(const QRectF &pixelRect) const; /// Convert a pixel path into a view path QPainterPath pixelToView(const QPainterPath &pixelPath) const; /// Convert a pixel polygon into a view path QPolygonF pixelToView(const QPolygonF &pixelPolygon) const; /// Update the canvas for the given rectangle in image pixel coordinates. void updateCanvasPixelRect(const QRectF &pixelRect); /// Update the canvas for the given rectangle in view coordinates. void updateCanvasViewRect(const QRectF &viewRect); QWidget* createOptionWidget() override; /** * To determine whether this tool will change its behavior when * modifier keys are pressed */ virtual bool listeningToModifiers(); /** * Request that this tool no longer listen to modifier keys * (Responding to the request is optional) */ virtual void listenToModifiers(bool listen); protected: KisImageWSP image() const; QCursor cursor() const; /// Call this to set the document modified void notifyModified() const; KisImageWSP currentImage(); KoPatternSP currentPattern(); KoAbstractGradientSP currentGradient(); KisNodeSP currentNode() const; KisNodeList selectedNodes() const; KoColor currentFgColor(); KoColor currentBgColor(); KisPaintOpPresetSP currentPaintOpPreset(); KisFilterConfigurationSP currentGenerator(); /// paint the path which is in view coordinates, default paint mode is XOR_MODE, BW_MODE is also possible /// never apply transformations to the painter, they would be useless, if drawing in OpenGL mode. The coordinates in the path should be in view coordinates. void paintToolOutline(QPainter * painter, const QPainterPath &path); /// Checks checks if the current node is editable bool nodeEditable(); /// Checks checks if the selection is editable, only applies to local selection as global selection is always editable bool selectionEditable(); /// Override the cursor appropriately if current node is not editable bool overrideCursorIfNotEditable(); bool blockUntilOperationsFinished(); void blockUntilOperationsFinishedForced(); protected: enum ToolMode { HOVER_MODE, PAINT_MODE, SECONDARY_PAINT_MODE, MIRROR_AXIS_SETUP_MODE, GESTURE_MODE, PAN_MODE, OTHER // not used now }; virtual void setMode(ToolMode mode); virtual ToolMode mode() const; void setCursor(const QCursor &cursor); protected Q_SLOTS: /** * Called whenever the configuration settings change. */ virtual void resetCursorStyle(); -private Q_SLOTS: - void slotToggleFgBg(); - void slotResetFgBg(); - private: struct Private; Private* const d; }; #endif // KIS_TOOL_H_ diff --git a/libs/ui/tool/kis_tool_paint.cc b/libs/ui/tool/kis_tool_paint.cc index b6c4ae5079..9a3e72b9b1 100644 --- a/libs/ui/tool/kis_tool_paint.cc +++ b/libs/ui/tool/kis_tool_paint.cc @@ -1,801 +1,782 @@ /* * Copyright (c) 2003-2009 Boudewijn Rempt * Copyright (c) 2015 Moritz Molch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * 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_paint.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_display_color_converter.h" #include #include #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_cursor.h" #include "widgets/kis_cmb_composite.h" #include "widgets/kis_slider_spin_box.h" #include "kis_canvas_resource_provider.h" #include "kis_tool_utils.h" #include #include #include #include #include "strokes/kis_color_picker_stroke_strategy.h" KisToolPaint::KisToolPaint(KoCanvasBase *canvas, const QCursor &cursor) : KisTool(canvas, cursor), m_showColorPreview(false), m_colorPreviewShowComparePlate(false), m_colorPickerDelayTimer(), m_isOutlineEnabled(true) { m_specialHoverModifier = false; m_optionsWidgetLayout = 0; m_opacity = OPACITY_OPAQUE_U8; m_supportOutline = false; { int maxSize = KisConfig(true).readEntry("maximumBrushSize", 1000); int brushSize = 1; do { m_standardBrushSizes.push_back(brushSize); int increment = qMax(1, int(std::ceil(qreal(brushSize) / 15))); brushSize += increment; } while (brushSize < maxSize); m_standardBrushSizes.push_back(maxSize); } KisCanvas2 *kiscanvas = dynamic_cast(canvas); - KisActionManager *actionManager = kiscanvas->viewManager()->actionManager(); - - // XXX: Perhaps a better place for these? - if (!actionManager->actionByName("increase_brush_size")) { - KisAction *increaseBrushSize = new KisAction(i18n("Increase Brush Size")); - increaseBrushSize->setShortcut(Qt::Key_BracketRight); - actionManager->addAction("increase_brush_size", increaseBrushSize); - } - - if (!actionManager->actionByName("decrease_brush_size")) { - KisAction *decreaseBrushSize = new KisAction(i18n("Decrease Brush Size")); - decreaseBrushSize->setShortcut(Qt::Key_BracketLeft); - actionManager->addAction("decrease_brush_size", decreaseBrushSize); - } - - addAction("increase_brush_size", dynamic_cast(actionManager->actionByName("increase_brush_size"))); - addAction("decrease_brush_size", dynamic_cast(actionManager->actionByName("decrease_brush_size"))); connect(this, SIGNAL(sigPaintingFinished()), kiscanvas->viewManager()->resourceProvider(), SLOT(slotPainting())); m_colorPickerDelayTimer.setSingleShot(true); connect(&m_colorPickerDelayTimer, SIGNAL(timeout()), this, SLOT(activatePickColorDelayed())); using namespace std::placeholders; // For _1 placeholder std::function callback = std::bind(&KisToolPaint::addPickerJob, this, _1); m_colorPickingCompressor.reset( new PickingCompressor(100, callback, KisSignalCompressor::FIRST_ACTIVE)); } KisToolPaint::~KisToolPaint() { } int KisToolPaint::flags() const { return KisTool::FLAG_USES_CUSTOM_COMPOSITEOP; } void KisToolPaint::canvasResourceChanged(int key, const QVariant& v) { KisTool::canvasResourceChanged(key, v); switch(key) { case(KisCanvasResourceProvider::Opacity): setOpacity(v.toDouble()); break; default: //nothing break; } connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle()), Qt::UniqueConnection); } void KisToolPaint::activate(ToolActivation toolActivation, const QSet &shapes) { if (currentPaintOpPreset()) { QString formattedBrushName = currentPaintOpPreset()->name().replace("_", " "); emit statusTextChanged(formattedBrushName); } KisTool::activate(toolActivation, shapes); if (flags() & KisTool::FLAG_USES_CUSTOM_SIZE) { connect(action("increase_brush_size"), SIGNAL(triggered()), SLOT(increaseBrushSize()), Qt::UniqueConnection); connect(action("decrease_brush_size"), SIGNAL(triggered()), SLOT(decreaseBrushSize()), Qt::UniqueConnection); } KisCanvasResourceProvider *provider = qobject_cast(canvas())->viewManager()->resourceProvider(); m_oldOpacity = provider->opacity(); provider->setOpacity(m_localOpacity); } void KisToolPaint::deactivate() { if (flags() & KisTool::FLAG_USES_CUSTOM_SIZE) { disconnect(action("increase_brush_size"), 0, this, 0); disconnect(action("decrease_brush_size"), 0, this, 0); } KisCanvasResourceProvider *provider = qobject_cast(canvas())->viewManager()->resourceProvider(); m_localOpacity = provider->opacity(); provider->setOpacity(m_oldOpacity); KisTool::deactivate(); } QPainterPath KisToolPaint::tryFixBrushOutline(const QPainterPath &originalOutline) { KisConfig cfg(true); if (cfg.newOutlineStyle() == OUTLINE_NONE) return originalOutline; const qreal minThresholdSize = cfg.outlineSizeMinimum(); /** * If the brush outline is bigger than the canvas itself (which * would make it invisible for a user in most of the cases) just * add a cross in the center of it */ QSize widgetSize = canvas()->canvasWidget()->size(); const int maxThresholdSum = widgetSize.width() + widgetSize.height(); QPainterPath outline = originalOutline; QRectF boundingRect = outline.boundingRect(); const qreal sum = boundingRect.width() + boundingRect.height(); QPointF center = boundingRect.center(); if (sum > maxThresholdSum) { const int hairOffset = 7; outline.moveTo(center.x(), center.y() - hairOffset); outline.lineTo(center.x(), center.y() + hairOffset); outline.moveTo(center.x() - hairOffset, center.y()); outline.lineTo(center.x() + hairOffset, center.y()); } else if (sum < minThresholdSize && !outline.isEmpty()) { outline = QPainterPath(); outline.addEllipse(center, 0.5 * minThresholdSize, 0.5 * minThresholdSize); } return outline; } void KisToolPaint::paint(QPainter &gc, const KoViewConverter &converter) { Q_UNUSED(converter); QPainterPath path = tryFixBrushOutline(pixelToView(m_currentOutline)); paintToolOutline(&gc, path); if (m_showColorPreview) { QRectF viewRect = converter.documentToView(m_oldColorPreviewRect); gc.fillRect(viewRect, m_colorPreviewCurrentColor); if (m_colorPreviewShowComparePlate) { QRectF baseColorRect = viewRect.translated(viewRect.width(), 0); gc.fillRect(baseColorRect, m_colorPreviewBaseColor); } } } void KisToolPaint::setMode(ToolMode mode) { if(this->mode() == KisTool::PAINT_MODE && mode != KisTool::PAINT_MODE) { // Let's add history information about recently used colors emit sigPaintingFinished(); } KisTool::setMode(mode); } void KisToolPaint::activatePickColor(AlternateAction action) { m_showColorPreview = true; requestUpdateOutline(m_outlineDocPoint, 0); int resource = colorPreviewResourceId(action); KoColor color = canvas()->resourceManager()->koColorResource(resource); KisCanvas2 * kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas); m_colorPreviewCurrentColor = kisCanvas->displayColorConverter()->toQColor(color); if (!m_colorPreviewBaseColor.isValid()) { m_colorPreviewBaseColor = m_colorPreviewCurrentColor; } } void KisToolPaint::deactivatePickColor(AlternateAction action) { Q_UNUSED(action); m_showColorPreview = false; m_oldColorPreviewRect = QRect(); m_oldColorPreviewUpdateRect = QRect(); m_colorPreviewCurrentColor = QColor(); } void KisToolPaint::pickColorWasOverridden() { m_colorPreviewShowComparePlate = false; m_colorPreviewBaseColor = QColor(); } void KisToolPaint::activateAlternateAction(AlternateAction action) { switch (action) { case PickFgNode: /* Falls through */ case PickBgNode: /* Falls through */ case PickFgImage: /* Falls through */ case PickBgImage: delayedAction = action; m_colorPickerDelayTimer.start(100); /* Falls through */ default: pickColorWasOverridden(); KisTool::activateAlternateAction(action); }; } void KisToolPaint::activatePickColorDelayed() { switch (delayedAction) { case PickFgNode: useCursor(KisCursor::pickerLayerForegroundCursor()); activatePickColor(delayedAction); break; case PickBgNode: useCursor(KisCursor::pickerLayerBackgroundCursor()); activatePickColor(delayedAction); break; case PickFgImage: useCursor(KisCursor::pickerImageForegroundCursor()); activatePickColor(delayedAction); break; case PickBgImage: useCursor(KisCursor::pickerImageBackgroundCursor()); activatePickColor(delayedAction); break; default: break; }; repaintDecorations(); } bool KisToolPaint::isPickingAction(AlternateAction action) { return action == PickFgNode || action == PickBgNode || action == PickFgImage || action == PickBgImage; } void KisToolPaint::deactivateAlternateAction(AlternateAction action) { if (!isPickingAction(action)) { KisTool::deactivateAlternateAction(action); return; } delayedAction = KisTool::NONE; m_colorPickerDelayTimer.stop(); resetCursorStyle(); deactivatePickColor(action); } void KisToolPaint::addPickerJob(const PickingJob &pickingJob) { /** * The actual picking is delayed by a compressor, so we can get this * event when the stroke is already closed */ if (!m_pickerStrokeId) return; KIS_ASSERT_RECOVER_RETURN(isPickingAction(pickingJob.action)); const QPoint imagePoint = image()->documentToImagePixelFloored(pickingJob.documentPixel); const bool fromCurrentNode = pickingJob.action == PickFgNode || pickingJob.action == PickBgNode; m_pickingResource = colorPreviewResourceId(pickingJob.action); if (!fromCurrentNode) { auto *kisCanvas = dynamic_cast(canvas()); KIS_SAFE_ASSERT_RECOVER_RETURN(kisCanvas); KisSharedPtr referencesLayer = kisCanvas->imageView()->document()->referenceImagesLayer(); if (referencesLayer && kisCanvas->referenceImagesDecoration()->visible()) { QColor color = referencesLayer->getPixel(imagePoint); if (color.isValid() && color.alpha() != 0) { slotColorPickingFinished(KoColor(color, image()->colorSpace())); return; } } } KisPaintDeviceSP device = fromCurrentNode ? currentNode()->colorPickSourceDevice() : image()->projection(); // Used for color picker blending. KoColor currentColor = canvas()->resourceManager()->foregroundColor(); if( pickingJob.action == PickBgNode || pickingJob.action == PickBgImage ){ currentColor = canvas()->resourceManager()->backgroundColor(); } image()->addJob(m_pickerStrokeId, new KisColorPickerStrokeStrategy::Data(device, imagePoint, currentColor)); } void KisToolPaint::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { if (isPickingAction(action)) { KIS_ASSERT_RECOVER_RETURN(!m_pickerStrokeId); setMode(SECONDARY_PAINT_MODE); KisColorPickerStrokeStrategy *strategy = new KisColorPickerStrokeStrategy(); connect(strategy, &KisColorPickerStrokeStrategy::sigColorUpdated, this, &KisToolPaint::slotColorPickingFinished); m_pickerStrokeId = image()->startStroke(strategy); m_colorPickingCompressor->start(PickingJob(event->point, action)); requestUpdateOutline(event->point, event); } else { KisTool::beginAlternateAction(event, action); } } void KisToolPaint::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { if (isPickingAction(action)) { KIS_ASSERT_RECOVER_RETURN(m_pickerStrokeId); m_colorPickingCompressor->start(PickingJob(event->point, action)); requestUpdateOutline(event->point, event); } else { KisTool::continueAlternateAction(event, action); } } void KisToolPaint::endAlternateAction(KoPointerEvent *event, AlternateAction action) { if (isPickingAction(action)) { KIS_ASSERT_RECOVER_RETURN(m_pickerStrokeId); image()->endStroke(m_pickerStrokeId); m_pickerStrokeId.clear(); requestUpdateOutline(event->point, event); setMode(HOVER_MODE); } else { KisTool::endAlternateAction(event, action); } } int KisToolPaint::colorPreviewResourceId(AlternateAction action) { bool toForegroundColor = action == PickFgNode || action == PickFgImage; int resource = toForegroundColor ? KoCanvasResourceProvider::ForegroundColor : KoCanvasResourceProvider::BackgroundColor; return resource; } void KisToolPaint::slotColorPickingFinished(const KoColor &color) { canvas()->resourceManager()->setResource(m_pickingResource, color); if (!m_showColorPreview) return; KisCanvas2 * kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas); QColor previewColor = kisCanvas->displayColorConverter()->toQColor(color); m_colorPreviewShowComparePlate = true; m_colorPreviewCurrentColor = previewColor; requestUpdateOutline(m_outlineDocPoint, 0); } void KisToolPaint::mousePressEvent(KoPointerEvent *event) { KisTool::mousePressEvent(event); if (mode() == KisTool::HOVER_MODE) { requestUpdateOutline(event->point, event); } } void KisToolPaint::mouseMoveEvent(KoPointerEvent *event) { KisTool::mouseMoveEvent(event); if (mode() == KisTool::HOVER_MODE) { requestUpdateOutline(event->point, event); } } void KisToolPaint::mouseReleaseEvent(KoPointerEvent *event) { KisTool::mouseReleaseEvent(event); if (mode() == KisTool::HOVER_MODE) { requestUpdateOutline(event->point, event); } } QWidget * KisToolPaint::createOptionWidget() { QWidget *optionWidget = new QWidget(); optionWidget->setObjectName(toolId()); QVBoxLayout *verticalLayout = new QVBoxLayout(optionWidget); verticalLayout->setObjectName("KisToolPaint::OptionWidget::VerticalLayout"); verticalLayout->setContentsMargins(0,0,0,0); verticalLayout->setSpacing(5); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(optionWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); verticalLayout->addWidget(specialSpacer); verticalLayout->addWidget(specialSpacer); m_optionsWidgetLayout = new QGridLayout(); m_optionsWidgetLayout->setColumnStretch(1, 1); - verticalLayout->addLayout(m_optionsWidgetLayout); m_optionsWidgetLayout->setContentsMargins(0,0,0,0); m_optionsWidgetLayout->setSpacing(5); if (!quickHelp().isEmpty()) { QPushButton *push = new QPushButton(KisIconUtils::loadIcon("help-contents"), QString(), optionWidget); connect(push, SIGNAL(clicked()), this, SLOT(slotPopupQuickHelp())); - - QHBoxLayout *hLayout = new QHBoxLayout(optionWidget); + QHBoxLayout *hLayout = new QHBoxLayout(); hLayout->addWidget(push); hLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed)); verticalLayout->addLayout(hLayout); } return optionWidget; } QWidget* findLabelWidget(QGridLayout *layout, QWidget *control) { QWidget *result = 0; int index = layout->indexOf(control); int row, col, rowSpan, colSpan; layout->getItemPosition(index, &row, &col, &rowSpan, &colSpan); if (col > 0) { QLayoutItem *item = layout->itemAtPosition(row, col - 1); if (item) { result = item->widget(); } } else { QLayoutItem *item = layout->itemAtPosition(row, col + 1); if (item) { result = item->widget(); } } return result; } void KisToolPaint::showControl(QWidget *control, bool value) { control->setVisible(value); QWidget *label = findLabelWidget(m_optionsWidgetLayout, control); if (label) { label->setVisible(value); } } void KisToolPaint::enableControl(QWidget *control, bool value) { control->setEnabled(value); QWidget *label = findLabelWidget(m_optionsWidgetLayout, control); if (label) { label->setEnabled(value); } } void KisToolPaint::addOptionWidgetLayout(QLayout *layout) { Q_ASSERT(m_optionsWidgetLayout != 0); int rowCount = m_optionsWidgetLayout->rowCount(); m_optionsWidgetLayout->addLayout(layout, rowCount, 0, 1, 2); } void KisToolPaint::addOptionWidgetOption(QWidget *control, QWidget *label) { Q_ASSERT(m_optionsWidgetLayout != 0); if (label) { m_optionsWidgetLayout->addWidget(label, m_optionsWidgetLayout->rowCount(), 0); m_optionsWidgetLayout->addWidget(control, m_optionsWidgetLayout->rowCount() - 1, 1); } else { m_optionsWidgetLayout->addWidget(control, m_optionsWidgetLayout->rowCount(), 0, 1, 2); } } void KisToolPaint::setOpacity(qreal opacity) { m_opacity = quint8(opacity * OPACITY_OPAQUE_U8); } const KoCompositeOp* KisToolPaint::compositeOp() { if (currentNode()) { KisPaintDeviceSP device = currentNode()->paintDevice(); if (device) { QString op = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentCompositeOp).toString(); return device->colorSpace()->compositeOp(op); } } return 0; } void KisToolPaint::slotPopupQuickHelp() { QWhatsThis::showText(QCursor::pos(), quickHelp()); } KisToolPaint::NodePaintAbility KisToolPaint::nodePaintAbility() { KisNodeSP node = currentNode(); if (!node) { return NONE; } if (node->inherits("KisShapeLayer")) { return VECTOR; } if (node->inherits("KisCloneLayer")) { return CLONE; } if (node->paintDevice()) { return PAINT; } return NONE; } void KisToolPaint::activatePrimaryAction() { pickColorWasOverridden(); setOutlineEnabled(true); KisTool::activatePrimaryAction(); } void KisToolPaint::deactivatePrimaryAction() { setOutlineEnabled(false); KisTool::deactivatePrimaryAction(); } bool KisToolPaint::isOutlineEnabled() const { return m_isOutlineEnabled; } void KisToolPaint::setOutlineEnabled(bool value) { m_isOutlineEnabled = value; requestUpdateOutline(m_outlineDocPoint, 0); } void KisToolPaint::increaseBrushSize() { qreal paintopSize = currentPaintOpPreset()->settings()->paintOpSize(); std::vector::iterator result = std::upper_bound(m_standardBrushSizes.begin(), m_standardBrushSizes.end(), qRound(paintopSize)); int newValue = result != m_standardBrushSizes.end() ? *result : m_standardBrushSizes.back(); currentPaintOpPreset()->settings()->setPaintOpSize(newValue); requestUpdateOutline(m_outlineDocPoint, 0); } void KisToolPaint::decreaseBrushSize() { qreal paintopSize = currentPaintOpPreset()->settings()->paintOpSize(); std::vector::reverse_iterator result = std::upper_bound(m_standardBrushSizes.rbegin(), m_standardBrushSizes.rend(), (int)paintopSize, std::greater()); int newValue = result != m_standardBrushSizes.rend() ? *result : m_standardBrushSizes.front(); currentPaintOpPreset()->settings()->setPaintOpSize(newValue); requestUpdateOutline(m_outlineDocPoint, 0); } QRectF KisToolPaint::colorPreviewDocRect(const QPointF &outlineDocPoint) { if (!m_showColorPreview) return QRect(); KisConfig cfg(true); const QRectF colorPreviewViewRect = cfg.colorPreviewRect(); const QRectF colorPreviewDocumentRect = canvas()->viewConverter()->viewToDocument(colorPreviewViewRect); return colorPreviewDocumentRect.translated(outlineDocPoint); } void KisToolPaint::requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event) { if (!m_supportOutline) return; KisConfig cfg(true); KisPaintOpSettings::OutlineMode outlineMode; if (isOutlineEnabled() && (mode() == KisTool::GESTURE_MODE || ((cfg.newOutlineStyle() == OUTLINE_FULL || cfg.newOutlineStyle() == OUTLINE_CIRCLE || cfg.newOutlineStyle() == OUTLINE_TILT) && ((mode() == HOVER_MODE) || (mode() == PAINT_MODE && cfg.showOutlineWhilePainting()))))) { // lisp forever! outlineMode.isVisible = true; if (cfg.newOutlineStyle() == OUTLINE_CIRCLE) { outlineMode.forceCircle = true; } else if(cfg.newOutlineStyle() == OUTLINE_TILT) { outlineMode.forceCircle = true; outlineMode.showTiltDecoration = true; } else { // noop } } outlineMode.forceFullSize = cfg.forceAlwaysFullSizedOutline(); m_outlineDocPoint = outlineDocPoint; m_currentOutline = getOutlinePath(m_outlineDocPoint, event, outlineMode); QRectF outlinePixelRect = m_currentOutline.boundingRect(); QRectF outlineDocRect = currentImage()->pixelToDocument(outlinePixelRect); // This adjusted call is needed as we paint with a 3 pixel wide brush and the pen is outside the bounds of the path // Pen uses view coordinates so we have to zoom the document value to match 2 pixel in view coordinates // See BUG 275829 qreal zoomX; qreal zoomY; canvas()->viewConverter()->zoom(&zoomX, &zoomY); qreal xoffset = 2.0/zoomX; qreal yoffset = 2.0/zoomY; if (!outlineDocRect.isEmpty()) { outlineDocRect.adjust(-xoffset,-yoffset,xoffset,yoffset); } QRectF colorPreviewDocRect = this->colorPreviewDocRect(m_outlineDocPoint); QRectF colorPreviewDocUpdateRect; if (!colorPreviewDocRect.isEmpty()) { colorPreviewDocUpdateRect.adjust(-xoffset,-yoffset,xoffset,yoffset); } // DIRTY HACK ALERT: we should fetch the assistant's dirty rect when requesting // the update, instead of just dumbly update the entire canvas! KisCanvas2 * kiscanvas = dynamic_cast(canvas()); KisPaintingAssistantsDecorationSP decoration = kiscanvas->paintingAssistantsDecoration(); if (decoration && decoration->visible()) { kiscanvas->updateCanvas(); } else { // TODO: only this branch should be present! if (!m_oldColorPreviewUpdateRect.isEmpty()) { canvas()->updateCanvas(m_oldColorPreviewUpdateRect); } if (!m_oldOutlineRect.isEmpty()) { canvas()->updateCanvas(m_oldOutlineRect); } if (!outlineDocRect.isEmpty()) { canvas()->updateCanvas(outlineDocRect); } if (!colorPreviewDocUpdateRect.isEmpty()) { canvas()->updateCanvas(colorPreviewDocUpdateRect); } } m_oldOutlineRect = outlineDocRect; m_oldColorPreviewRect = colorPreviewDocRect; m_oldColorPreviewUpdateRect = colorPreviewDocUpdateRect; } QPainterPath KisToolPaint::getOutlinePath(const QPointF &documentPos, const KoPointerEvent *event, KisPaintOpSettings::OutlineMode outlineMode) { Q_UNUSED(event); QPointF imagePos = currentImage()->documentToPixel(documentPos); QPainterPath path = currentPaintOpPreset()->settings()-> brushOutline(KisPaintInformation(imagePos), outlineMode); return path; } diff --git a/libs/ui/tool/kis_tool_polyline_base.cpp b/libs/ui/tool/kis_tool_polyline_base.cpp index ac052a2599..7df6b666ae 100644 --- a/libs/ui/tool/kis_tool_polyline_base.cpp +++ b/libs/ui/tool/kis_tool_polyline_base.cpp @@ -1,270 +1,267 @@ /* This file is part of the KDE project * Copyright (C) 2009 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 #include #include #include #include #include #include "kis_tool_polyline_base.h" #include "kis_canvas2.h" #include #include #include #include "kis_action_registry.h" #define SNAPPING_THRESHOLD 10 #define SNAPPING_HANDLE_RADIUS 8 #define PREVIEW_LINE_WIDTH 1 KisToolPolylineBase::KisToolPolylineBase(KoCanvasBase * canvas, KisToolPolylineBase::ToolType type, const QCursor & cursor) : KisToolShape(canvas, cursor), m_dragging(false), m_type(type), m_closeSnappingActivated(false) { - QAction *undo_polygon_selection = - KisActionRegistry::instance()->makeQAction("undo_polygon_selection", this); - addAction("undo_polygon_selection", undo_polygon_selection); } void KisToolPolylineBase::activate(KoToolBase::ToolActivation activation, const QSet &shapes) { KisToolShape::activate(activation, shapes); connect(action("undo_polygon_selection"), SIGNAL(triggered()), SLOT(undoSelection()), Qt::UniqueConnection); } void KisToolPolylineBase::deactivate() { disconnect(action("undo_polygon_selection"), 0, this, 0); cancelStroke(); KisToolShape::deactivate(); } void KisToolPolylineBase::requestStrokeEnd() { endStroke(); } void KisToolPolylineBase::requestStrokeCancellation() { cancelStroke(); } bool KisToolPolylineBase::hasUserInteractionRunning() const { return !m_points.isEmpty(); } void KisToolPolylineBase::beginPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); if ((m_type == PAINT && (!nodeEditable() || nodePaintAbility() == NONE)) || (m_type == SELECT && !selectionEditable())) { event->ignore(); return; } setMode(KisTool::PAINT_MODE); if(m_dragging && m_closeSnappingActivated) { m_points.append(m_points.first()); endStroke(); } else { m_dragging = true; } } void KisToolPolylineBase::endPrimaryAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); setMode(KisTool::HOVER_MODE); if(m_dragging) { m_dragStart = convertToPixelCoordAndSnap(event); m_dragEnd = m_dragStart; m_points.append(m_dragStart); } } void KisToolPolylineBase::beginPrimaryDoubleClickAction(KoPointerEvent *event) { endStroke(); // this action will have no continuation event->ignore(); } void KisToolPolylineBase::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { if (action != ChangeSize || !m_dragging) { KisToolPaint::beginAlternateAction(event, action); } if (m_closeSnappingActivated) { m_points.append(m_points.first()); } endStroke(); } void KisToolPolylineBase::mouseMoveEvent(KoPointerEvent *event) { if (m_dragging && !m_points.empty()) { // erase old lines on canvas QRectF updateRect = dragBoundingRect(); // get current mouse position m_dragEnd = convertToPixelCoordAndSnap(event); // draw new lines on canvas updateRect |= dragBoundingRect(); updateCanvasViewRect(updateRect); QPointF basePoint = pixelToView(m_points.first()); m_closeSnappingActivated = m_points.size() > 1 && (basePoint - pixelToView(m_dragEnd)).manhattanLength() < SNAPPING_THRESHOLD; updateCanvasViewRect(QRectF(basePoint, 2 * QSize(SNAPPING_HANDLE_RADIUS + PREVIEW_LINE_WIDTH, SNAPPING_HANDLE_RADIUS + PREVIEW_LINE_WIDTH)).translated(-SNAPPING_HANDLE_RADIUS + PREVIEW_LINE_WIDTH,-SNAPPING_HANDLE_RADIUS + PREVIEW_LINE_WIDTH)); KisToolPaint::requestUpdateOutline(event->point, event); } else { KisToolPaint::mouseMoveEvent(event); } } void KisToolPolylineBase::undoSelection() { if(m_dragging) { //Update canvas for drag before undo QRectF updateRect = dragBoundingRect(); updateRect |= dragBoundingRect(); updateCanvasViewRect(updateRect); //Update canvas for last segment QRectF rect; if (m_points.size() > 2) { rect = pixelToView(QRectF(m_points.last(), m_points.at(m_points.size()-2)).normalized()); rect.adjust(-PREVIEW_LINE_WIDTH, -PREVIEW_LINE_WIDTH, PREVIEW_LINE_WIDTH, PREVIEW_LINE_WIDTH); rect |= rect; updateCanvasViewRect(rect); } if (m_points.size() > 0) { m_points.pop_back(); } if (m_points.size() > 0) { m_dragStart = m_points.last(); } } } void KisToolPolylineBase::paint(QPainter& gc, const KoViewConverter &converter) { Q_UNUSED(converter); if (!canvas() || !currentImage()) return; QPointF start, end; QPointF startPos; QPointF endPos; QPainterPath path; if (m_dragging && !m_points.empty()) { startPos = pixelToView(m_dragStart); endPos = pixelToView(m_dragEnd); path.moveTo(startPos); path.lineTo(endPos); } for (vQPointF::iterator it = m_points.begin(); it != m_points.end(); ++it) { if (it == m_points.begin()) { start = (*it); } else { end = (*it); startPos = pixelToView(start); endPos = pixelToView(end); path.moveTo(startPos); path.lineTo(endPos); start = end; } } if (m_closeSnappingActivated) { QPointF basePoint = pixelToView(m_points.first()); path.addEllipse(basePoint, SNAPPING_HANDLE_RADIUS, SNAPPING_HANDLE_RADIUS); } paintToolOutline(&gc, path); KisToolPaint::paint(gc,converter); } void KisToolPolylineBase::updateArea() { updateCanvasPixelRect(image()->bounds()); } void KisToolPolylineBase::endStroke() { if (!m_dragging) return; m_dragging = false; if(m_points.count() > 1) { finishPolyline(m_points); } m_points.clear(); m_closeSnappingActivated = false; updateArea(); } void KisToolPolylineBase::cancelStroke() { if (!m_dragging) return; m_dragging = false; m_points.clear(); m_closeSnappingActivated = false; updateArea(); } QRectF KisToolPolylineBase::dragBoundingRect() { QRectF rect = pixelToView(QRectF(m_dragStart, m_dragEnd).normalized()); rect.adjust(-PREVIEW_LINE_WIDTH, -PREVIEW_LINE_WIDTH, PREVIEW_LINE_WIDTH, PREVIEW_LINE_WIDTH); return rect; } void KisToolPolylineBase::listenToModifiers(bool listen) { Q_UNUSED(listen) } bool KisToolPolylineBase::listeningToModifiers() { //Never grab modifier keys return false; } diff --git a/libs/ui/tool/kis_tool_select_base.h b/libs/ui/tool/kis_tool_select_base.h index fe169c8d90..697df68dc6 100644 --- a/libs/ui/tool/kis_tool_select_base.h +++ b/libs/ui/tool/kis_tool_select_base.h @@ -1,426 +1,402 @@ /* This file is part of the KDE project * Copyright (C) 2009 Boudewijn Rempt * Copyright (C) 2015 Michael Abrahams * * 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 KISTOOLSELECTBASE_H #define KISTOOLSELECTBASE_H #include "KoPointerEvent.h" #include "kis_tool.h" #include "kis_canvas2.h" #include "kis_selection.h" #include "kis_selection_options.h" #include "kis_selection_tool_config_widget_helper.h" #include "KisViewManager.h" #include "kis_selection_manager.h" #include "kis_selection_modifier_mapper.h" #include "strokes/move_stroke_strategy.h" #include "kis_image.h" #include "kis_cursor.h" #include "kis_action_manager.h" #include "kis_action.h" #include "kis_signal_auto_connection.h" #include "kis_selection_tool_helper.h" /** * This is a basic template to create selection tools from basic path based drawing tools. * The template overrides the ability to execute alternate actions correctly. * The default behavior for the modifier keys is as follows: * * Shift: add to selection * Alt: subtract from selection * Shift+Alt: intersect current selection * Ctrl: replace selection * * The mapping itself is done in KisSelectionModifierMapper. * * Certain tools also use modifier keys to alter their behavior, e.g. forcing square proportions with the rectangle tool. * The template enables the following rules for forwarding keys: * 1) Any modifier keys held *when the tool is first activated* will determine * the new selection method. This is recorded in m_selectionActionAlternate. A * value of m_selectionActionAlternate = SELECTION_DEFAULT means no modifier was * being pressed when the tool was activated. * * 2) If the underlying tool *does not take modifier keys*, pressing modifier * keys in the middle of a stroke will change the selection method. This is * recorded in m_selectionAction. A value of SELECTION_DEFAULT means no modifier * is being pressed. Applies to the lasso tool and polygon tool. * * 3) If the underlying tool *takes modifier keys,* they will always be * forwarded to the underlying tool, and it is not possible to change the * selection method in the middle of a stroke. */ template class KisToolSelectBase : public BaseClass { public: KisToolSelectBase(KoCanvasBase* canvas, const QString toolName) - :BaseClass(canvas), - m_widgetHelper(toolName), - m_selectionActionAlternate(SELECTION_DEFAULT) + : BaseClass(canvas) + , m_widgetHelper(toolName) + , m_selectionActionAlternate(SELECTION_DEFAULT) { KisSelectionModifierMapper::instance(); - initShortcuts(); } KisToolSelectBase(KoCanvasBase* canvas, const QCursor cursor, const QString toolName) - :BaseClass(canvas, cursor), - m_widgetHelper(toolName), - m_selectionActionAlternate(SELECTION_DEFAULT) + : BaseClass(canvas, cursor) + , m_widgetHelper(toolName) + , m_selectionActionAlternate(SELECTION_DEFAULT) { KisSelectionModifierMapper::instance(); - initShortcuts(); } KisToolSelectBase(KoCanvasBase* canvas, QCursor cursor, QString toolName, KisTool *delegateTool) - :BaseClass(canvas, cursor, delegateTool), - m_widgetHelper(toolName), - m_selectionActionAlternate(SELECTION_DEFAULT) + : BaseClass(canvas, cursor, delegateTool) + , m_widgetHelper(toolName) + , m_selectionActionAlternate(SELECTION_DEFAULT) { KisSelectionModifierMapper::instance(); - initShortcuts(); - } - - void initShortcuts() - { - KisCanvas2 * kiscanvas = static_cast(canvas()); - KisViewManager* viewManager = kiscanvas->viewManager(); - KisActionManager *manager = viewManager->actionManager(); - - KisAction *action = 0; - - action = manager->createAction("selection_tool_mode_add"); - this->addAction(action->objectName(), action); - - action = manager->createAction("selection_tool_mode_replace"); - this->addAction(action->objectName(), action); - - action = manager->createAction("selection_tool_mode_subtract"); - this->addAction(action->objectName(), action); - - action = manager->createAction("selection_tool_mode_intersect"); - this->addAction(action->objectName(), action); } void updateActionShortcutToolTips() { KisSelectionOptions *widget = m_widgetHelper.optionWidget(); if (widget) { widget->updateActionButtonToolTip( SELECTION_REPLACE, this->action("selection_tool_mode_replace")->shortcut()); widget->updateActionButtonToolTip( SELECTION_ADD, this->action("selection_tool_mode_add")->shortcut()); widget->updateActionButtonToolTip( SELECTION_SUBTRACT, this->action("selection_tool_mode_subtract")->shortcut()); widget->updateActionButtonToolTip( SELECTION_INTERSECT, this->action("selection_tool_mode_intersect")->shortcut()); } } void activate(KoToolBase::ToolActivation activation, const QSet &shapes) { BaseClass::activate(activation, shapes); m_modeConnections.addUniqueConnection( this->action("selection_tool_mode_replace"), SIGNAL(triggered()), &m_widgetHelper, SLOT(slotReplaceModeRequested())); m_modeConnections.addUniqueConnection( this->action("selection_tool_mode_add"), SIGNAL(triggered()), &m_widgetHelper, SLOT(slotAddModeRequested())); m_modeConnections.addUniqueConnection( this->action("selection_tool_mode_subtract"), SIGNAL(triggered()), &m_widgetHelper, SLOT(slotSubtractModeRequested())); m_modeConnections.addUniqueConnection( this->action("selection_tool_mode_intersect"), SIGNAL(triggered()), &m_widgetHelper, SLOT(slotIntersectModeRequested())); updateActionShortcutToolTips(); } void deactivate() { BaseClass::deactivate(); m_modeConnections.clear(); } QWidget* createOptionWidget() { KisCanvas2* canvas = dynamic_cast(this->canvas()); Q_ASSERT(canvas); m_widgetHelper.createOptionWidget(canvas, this->toolId()); this->connect(this, SIGNAL(isActiveChanged(bool)), &m_widgetHelper, SLOT(slotToolActivatedChanged(bool))); this->connect(&m_widgetHelper, SIGNAL(selectionActionChanged(int)), this, SLOT(resetCursorStyle())); updateActionShortcutToolTips(); return m_widgetHelper.optionWidget(); } SelectionMode selectionMode() const { return m_widgetHelper.selectionMode(); } SelectionAction selectionAction() const { if (alternateSelectionAction() == SELECTION_DEFAULT) { return m_widgetHelper.selectionAction(); } return alternateSelectionAction(); } bool antiAliasSelection() const { return m_widgetHelper.antiAliasSelection(); } SelectionAction alternateSelectionAction() const { return m_selectionActionAlternate; } KisSelectionOptions* selectionOptionWidget() { return m_widgetHelper.optionWidget(); } virtual void setAlternateSelectionAction(SelectionAction action) { m_selectionActionAlternate = action; dbgKrita << "Changing to selection action" << m_selectionActionAlternate; } void activateAlternateAction(KisTool::AlternateAction action) { Q_UNUSED(action); BaseClass::activatePrimaryAction(); } void deactivateAlternateAction(KisTool::AlternateAction action) { Q_UNUSED(action); BaseClass::deactivatePrimaryAction(); } void beginAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) { Q_UNUSED(action); beginPrimaryAction(event); } void continueAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) { Q_UNUSED(action); continuePrimaryAction(event); } void endAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) { Q_UNUSED(action); endPrimaryAction(event); } KisNodeSP locateSelectionMaskUnderCursor(const QPointF &pos, Qt::KeyboardModifiers modifiers) { if (modifiers != Qt::NoModifier) return 0; KisCanvas2* canvas = dynamic_cast(this->canvas()); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(canvas, 0); KisSelectionSP selection = canvas->viewManager()->selection(); if (selection && selection->outlineCacheValid()) { const qreal handleRadius = qreal(this->handleRadius()) / canvas->coordinatesConverter()->effectiveZoom(); QPainterPath samplePath; samplePath.addEllipse(pos, handleRadius, handleRadius); const QPainterPath selectionPath = selection->outlineCache(); if (selectionPath.intersects(samplePath) && !selectionPath.contains(samplePath)) { KisNodeSP parent = selection->parentNode(); if (parent && parent->isEditable()) { return parent; } } } return 0; } void keyPressEvent(QKeyEvent *event) { if (this->mode() != KisTool::PAINT_MODE) { setAlternateSelectionAction(KisSelectionModifierMapper::map(event->modifiers())); this->resetCursorStyle(); } BaseClass::keyPressEvent(event); } void keyReleaseEvent(QKeyEvent *event) { if (this->mode() != KisTool::PAINT_MODE) { setAlternateSelectionAction(KisSelectionModifierMapper::map(event->modifiers())); this->resetCursorStyle(); } BaseClass::keyPressEvent(event); } void mouseMoveEvent(KoPointerEvent *event) { if (!this->hasUserInteractionRunning() && (m_moveStrokeId || this->mode() != KisTool::PAINT_MODE)) { const QPointF pos = this->convertToPixelCoord(event->point); KisNodeSP selectionMask = locateSelectionMaskUnderCursor(pos, event->modifiers()); if (selectionMask) { this->useCursor(KisCursor::moveSelectionCursor()); } else { setAlternateSelectionAction(KisSelectionModifierMapper::map(event->modifiers())); this->resetCursorStyle(); } } BaseClass::mouseMoveEvent(event); } virtual void beginPrimaryAction(KoPointerEvent *event) { if (!this->hasUserInteractionRunning()) { const QPointF pos = this->convertToPixelCoord(event->point); KisCanvas2* canvas = dynamic_cast(this->canvas()); KIS_SAFE_ASSERT_RECOVER_RETURN(canvas); KisNodeSP selectionMask = locateSelectionMaskUnderCursor(pos, event->modifiers()); if (selectionMask) { KisStrokeStrategy *strategy = new MoveStrokeStrategy({selectionMask}, this->image().data(), this->image().data()); m_moveStrokeId = this->image()->startStroke(strategy); m_dragStartPos = pos; return; } } keysAtStart = event->modifiers(); setAlternateSelectionAction(KisSelectionModifierMapper::map(keysAtStart)); if (alternateSelectionAction() != SELECTION_DEFAULT) { BaseClass::listenToModifiers(false); } BaseClass::beginPrimaryAction(event); } virtual void continuePrimaryAction(KoPointerEvent *event) { if (m_moveStrokeId) { const QPointF pos = this->convertToPixelCoord(event->point); const QPoint offset((pos - m_dragStartPos).toPoint()); this->image()->addJob(m_moveStrokeId, new MoveStrokeStrategy::Data(offset)); return; } //If modifier keys have changed, tell the base tool it can start capturing modifiers if ((keysAtStart != event->modifiers()) && !BaseClass::listeningToModifiers()) { BaseClass::listenToModifiers(true); } //Always defer to the base class if it signals it is capturing modifier keys if (!BaseClass::listeningToModifiers()) { setAlternateSelectionAction(KisSelectionModifierMapper::map(event->modifiers())); } BaseClass::continuePrimaryAction(event); } void endPrimaryAction(KoPointerEvent *event) { if (m_moveStrokeId) { this->image()->endStroke(m_moveStrokeId); m_moveStrokeId.clear(); return; } keysAtStart = Qt::NoModifier; //reset this with each action BaseClass::endPrimaryAction(event); } bool selectionDragInProgress() const { return m_moveStrokeId; } QMenu* popupActionsMenu() { KisCanvas2 * kisCanvas = dynamic_cast(canvas()); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(kisCanvas, 0); return KisSelectionToolHelper::getSelectionContextMenu(kisCanvas); } protected: using BaseClass::canvas; KisSelectionToolConfigWidgetHelper m_widgetHelper; SelectionAction m_selectionActionAlternate; private: Qt::KeyboardModifiers keysAtStart; QPointF m_dragStartPos; KisStrokeId m_moveStrokeId; KisSignalAutoConnectionsStore m_modeConnections; }; struct FakeBaseTool : KisTool { FakeBaseTool(KoCanvasBase* canvas) : KisTool(canvas, QCursor()) { } FakeBaseTool(KoCanvasBase* canvas, const QString &toolName) : KisTool(canvas, QCursor()) { Q_UNUSED(toolName); } FakeBaseTool(KoCanvasBase* canvas, const QCursor &cursor) : KisTool(canvas, cursor) { } bool hasUserInteractionRunning() const { return false; } }; typedef KisToolSelectBase KisToolSelect; #endif // KISTOOLSELECTBASE_H diff --git a/libs/ui/tool/kis_tool_shape.h b/libs/ui/tool/kis_tool_shape.h index 95d93ca9f2..2cbc72a163 100644 --- a/libs/ui/tool/kis_tool_shape.h +++ b/libs/ui/tool/kis_tool_shape.h @@ -1,93 +1,94 @@ /* * Copyright (c) 2005 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. */ #ifndef KIS_TOOL_SHAPE_H_ #define KIS_TOOL_SHAPE_H_ #include #include #include -#include "kis_tool_paint.h" -#include "kis_painter.h" -#include "ui_wdggeometryoptions.h" +#include +#include "kis_tool_paint.h" +#include "KisSelectionToolFactoryBase.h" +#include "ui_wdggeometryoptions.h" class KoCanvasBase; class KoPathShape; class WdgGeometryOptions : public QWidget, public Ui::WdgGeometryOptions { Q_OBJECT public: WdgGeometryOptions(QWidget *parent) : QWidget(parent) { setupUi(this); } }; /** * Base for tools specialized in drawing shapes */ class KRITAUI_EXPORT KisToolShape : public KisToolPaint { Q_OBJECT public: KisToolShape(KoCanvasBase * canvas, const QCursor & cursor); ~KisToolShape() override; int flags() const override; WdgGeometryOptions *m_shapeOptionsWidget; public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; virtual void outlineSettingChanged(int value); virtual void fillSettingChanged(int value); protected: QWidget* createOptionWidget() override; virtual KisPainter::FillStyle fillStyle(); KisPainter::StrokeStyle strokeStyle(); qreal currentStrokeWidth() const; struct KRITAUI_EXPORT ShapeAddInfo { bool shouldAddShape = false; bool shouldAddSelectionShape = false; void markAsSelectionShapeIfNeeded(KoShape *shape) const; }; ShapeAddInfo shouldAddShape(KisNodeSP currentNode) const; void addShape(KoShape* shape); void addPathShape(KoPathShape* pathShape, const KUndo2MagicString& name); KConfigGroup m_configGroup; }; #endif // KIS_TOOL_SHAPE_H_ diff --git a/libs/widgetutils/kis_action_registry.cpp b/libs/widgetutils/kis_action_registry.cpp index ac681e0566..6708199a9a 100644 --- a/libs/widgetutils/kis_action_registry.cpp +++ b/libs/widgetutils/kis_action_registry.cpp @@ -1,419 +1,420 @@ /* * Copyright (c) 2015 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include "kis_debug.h" #include "KoResourcePaths.h" #include "kis_icon_utils.h" #include "kis_action_registry.h" #include "kshortcutschemeshelper_p.h" namespace { /** * We associate several pieces of information with each shortcut. The first * piece of information is a QDomElement, containing the raw data from the * .action XML file. The second and third are QKeySequences, the first of * which is the default shortcut, the last of which is any custom shortcut. * The last two are the KActionCollection and KActionCategory used to * organize the shortcut editor. */ struct ActionInfoItem { QDomElement xmlData; QString collectionName; QString categoryName; inline QList defaultShortcuts() const { return m_defaultShortcuts; } inline void setDefaultShortcuts(const QList &value) { m_defaultShortcuts = value; } inline QList customShortcuts() const { return m_customShortcuts; } inline void setCustomShortcuts(const QList &value, bool explicitlyReset) { m_customShortcuts = value; m_explicitlyReset = explicitlyReset; } inline QList effectiveShortcuts() const { return m_customShortcuts.isEmpty() && !m_explicitlyReset ? m_defaultShortcuts : m_customShortcuts; } private: QList m_defaultShortcuts; QList m_customShortcuts; bool m_explicitlyReset = false; }; // Convenience macros to extract text of a child node. QString getChildContent(QDomElement xml, QString node) { return xml.firstChildElement(node).text(); } // Use Krita debug logging categories instead of KDE's default qDebug() for // harmless empty strings and translations QString quietlyTranslate(const QString &s) { if (s.isEmpty()) { return s; } QString translatedString = i18nc("action", s.toUtf8()); if (translatedString == s) { translatedString = i18n(s.toUtf8()); } if (translatedString.isEmpty()) { dbgAction << "No translation found for" << s; return s; } return translatedString; } } class Q_DECL_HIDDEN KisActionRegistry::Private { public: Private(KisActionRegistry *_q) : q(_q) {} // This is the main place containing ActionInfoItems. QMap actionInfoList; void loadActionFiles(); void loadCustomShortcuts(QString filename = QStringLiteral("kritashortcutsrc")); // XXX: this adds a default item for the given name to the list of actioninfo objects! ActionInfoItem &actionInfo(const QString &name) { if (!actionInfoList.contains(name)) { dbgAction << "Tried to look up info for unknown action" << name; } return actionInfoList[name]; } KisActionRegistry *q; QSet sanityPropertizedShortcuts; }; Q_GLOBAL_STATIC(KisActionRegistry, s_instance) KisActionRegistry *KisActionRegistry::instance() { if (!s_instance.exists()) { dbgRegistry << "initializing KoActionRegistry"; } return s_instance; } bool KisActionRegistry::hasAction(const QString &name) const { return d->actionInfoList.contains(name); } KisActionRegistry::KisActionRegistry() : d(new KisActionRegistry::Private(this)) { KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes"); QString schemeName = cg.readEntry("Current Scheme", "Default"); loadShortcutScheme(schemeName); loadCustomShortcuts(); } KisActionRegistry::ActionCategory KisActionRegistry::fetchActionCategory(const QString &name) const { if (!d->actionInfoList.contains(name)) return ActionCategory(); const ActionInfoItem info = d->actionInfoList.value(name); return ActionCategory(info.collectionName, info.categoryName); } void KisActionRegistry::notifySettingsUpdated() { d->loadCustomShortcuts(); } void KisActionRegistry::loadCustomShortcuts() { d->loadCustomShortcuts(); } void KisActionRegistry::loadShortcutScheme(const QString &schemeName) { // Load scheme file if (schemeName != QStringLiteral("Default")) { QString schemeFileName = KShortcutSchemesHelper::schemeFileLocations().value(schemeName); if (schemeFileName.isEmpty()) { return; } KConfig schemeConfig(schemeFileName, KConfig::SimpleConfig); applyShortcutScheme(&schemeConfig); } else { // Apply default scheme, updating KisActionRegistry data applyShortcutScheme(); } } QAction * KisActionRegistry::makeQAction(const QString &name, QObject *parent) { QAction * a = new QAction(parent); if (!d->actionInfoList.contains(name)) { - dbgAction << "Warning: requested data for unknown action" << name; + qWarning() << "Warning: requested data for unknown action" << name; + a->setObjectName(name); return a; } propertizeAction(name, a); return a; } void KisActionRegistry::settingsPageSaved() { // For now, custom shortcuts are dealt with by writing to file and reloading. loadCustomShortcuts(); // Announce UI should reload current shortcuts. emit shortcutsUpdated(); } void KisActionRegistry::applyShortcutScheme(const KConfigBase *config) { // First, update the things in KisActionRegistry d->actionInfoList.clear(); d->loadActionFiles(); if (config == 0) { // Use default shortcut scheme. Simplest just to reload everything. loadCustomShortcuts(); } else { const auto schemeEntries = config->group(QStringLiteral("Shortcuts")).entryMap(); // Load info item for each shortcut, reset custom shortcuts auto it = schemeEntries.constBegin(); while (it != schemeEntries.end()) { ActionInfoItem &info = d->actionInfo(it.key()); info.setDefaultShortcuts(QKeySequence::listFromString(it.value())); it++; } } } void KisActionRegistry::updateShortcut(const QString &name, QAction *action) { const ActionInfoItem &info = d->actionInfo(name); action->setShortcuts(info.effectiveShortcuts()); action->setProperty("defaultShortcuts", qVariantFromValue(info.defaultShortcuts())); d->sanityPropertizedShortcuts.insert(name); } bool KisActionRegistry::sanityCheckPropertized(const QString &name) { return d->sanityPropertizedShortcuts.contains(name); } QList KisActionRegistry::registeredShortcutIds() const { return d->actionInfoList.keys(); } bool KisActionRegistry::propertizeAction(const QString &name, QAction * a) { if (!d->actionInfoList.contains(name)) { warnAction << "propertizeAction: No XML data found for action" << name; return false; } const ActionInfoItem info = d->actionInfo(name); QDomElement actionXml = info.xmlData; if (!actionXml.text().isEmpty()) { // i18n requires converting format from QString. auto getChildContent_i18n = [=](QString node){return quietlyTranslate(getChildContent(actionXml, node));}; // Note: the fields in the .action documents marked for translation are determined by extractrc. QString icon = getChildContent(actionXml, "icon"); QString text = getChildContent_i18n("text"); QString whatsthis = getChildContent_i18n("whatsThis"); QString toolTip = getChildContent_i18n("toolTip"); QString statusTip = getChildContent_i18n("statusTip"); QString iconText = getChildContent_i18n("iconText"); bool isCheckable = getChildContent(actionXml, "isCheckable") == QString("true"); a->setObjectName(name); // This is helpful, should be added more places in Krita a->setIcon(KisIconUtils::loadIcon(icon.toLatin1())); a->setText(text); a->setObjectName(name); a->setWhatsThis(whatsthis); a->setToolTip(toolTip); a->setStatusTip(statusTip); a->setIconText(iconText); a->setCheckable(isCheckable); } updateShortcut(name, a); return true; } QString KisActionRegistry::getActionProperty(const QString &name, const QString &property) { ActionInfoItem info = d->actionInfo(name); QDomElement actionXml = info.xmlData; if (actionXml.text().isEmpty()) { dbgAction << "getActionProperty: No XML data found for action" << name; return QString(); } return getChildContent(actionXml, property); } void KisActionRegistry::Private::loadActionFiles() { QStringList actionDefinitions = KoResourcePaths::findAllResources("kis_actions", "*.action", KoResourcePaths::Recursive); dbgAction << "Action Definitions" << actionDefinitions; // Extract actions all XML .action files. Q_FOREACH (const QString &actionDefinition, actionDefinitions) { dbgAction << "\tLoading Action File" << actionDefinition; QDomDocument doc; QFile f(actionDefinition); f.open(QFile::ReadOnly); doc.setContent(f.readAll()); QDomElement base = doc.documentElement(); // "ActionCollection" outer group QString collectionName = base.attribute("name"); QString version = base.attribute("version"); if (version != "2") { errAction << ".action XML file" << actionDefinition << "has incorrect version; skipping."; continue; } // Loop over nodes. Each of these corresponds to a // KActionCategory, producing a group of actions in the shortcut dialog. QDomElement actions = base.firstChild().toElement(); while (!actions.isNull()) { // field QDomElement categoryTextNode = actions.firstChild().toElement(); QString categoryName = quietlyTranslate(categoryTextNode.text()); // tags QDomElement actionXml = categoryTextNode.nextSiblingElement(); // Loop over individual actions while (!actionXml.isNull()) { if (actionXml.tagName() == "Action") { // Read name from format QString name = actionXml.attribute("name"); dbgAction << "\t\tloading xml data for action" << name; // Bad things if (name.isEmpty()) { errAction << "Unnamed action in definitions file " << actionDefinition; } else if (actionInfoList.contains(name)) { errAction << "NOT COOL: Duplicated action name from xml data: " << name; } else { ActionInfoItem info; info.xmlData = actionXml; // Use empty list to signify no shortcut QString shortcutText = getChildContent(actionXml, "shortcut"); if (!shortcutText.isEmpty()) { info.setDefaultShortcuts(QKeySequence::listFromString(shortcutText)); } info.categoryName = categoryName; info.collectionName = collectionName; actionInfoList.insert(name,info); } } actionXml = actionXml.nextSiblingElement(); } actions = actions.nextSiblingElement(); } } } void KisActionRegistry::Private::loadCustomShortcuts(QString filename) { const KConfigGroup localShortcuts(KSharedConfig::openConfig(filename), QStringLiteral("Shortcuts")); if (!localShortcuts.exists()) { return; } // Distinguish between two "null" states for custom shortcuts. for (auto i = actionInfoList.begin(); i != actionInfoList.end(); ++i) { if (localShortcuts.hasKey(i.key())) { QString entry = localShortcuts.readEntry(i.key(), QString()); if (entry == QStringLiteral("none")) { i.value().setCustomShortcuts(QList(), true); } else { i.value().setCustomShortcuts(QKeySequence::listFromString(entry), false); } } else { i.value().setCustomShortcuts(QList(), false); } } } KisActionRegistry::ActionCategory::ActionCategory() { } KisActionRegistry::ActionCategory::ActionCategory(const QString &_componentName, const QString &_categoryName) : componentName(_componentName), categoryName(_categoryName), m_isValid(true) { } bool KisActionRegistry::ActionCategory::isValid() const { return m_isValid && !categoryName.isEmpty() && !componentName.isEmpty(); } diff --git a/libs/widgetutils/kis_action_registry.h b/libs/widgetutils/kis_action_registry.h index ddfef9cf8c..fecb591eee 100644 --- a/libs/widgetutils/kis_action_registry.h +++ b/libs/widgetutils/kis_action_registry.h @@ -1,150 +1,150 @@ /* * Copyright (c) 2015 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_ACTION_REGISTRY_H #define KIS_ACTION_REGISTRY_H #include #include #include #include #include #include "kritawidgetutils_export.h" class KActionCollection; class QDomElement; class KConfigBase; class KisShortcutsDialog; /** * KisActionRegistry is intended to manage the global action configuration data * for Krita. The data come from four sources: * - .action files, containing static action configuration data in XML format, * - .rc configuration files, originally from XMLGUI and now in WidgetUtils, * - kritashortcutsrc, containing temporary shortcut configuration, and * - .shortcuts scheme files providing sets of default shortcuts, also from XMLGUI * * This class can be used as a factory by calling makeQAction. It can be used to * add standard properties such as default shortcuts and default tooltip to an * existing action with propertizeAction. If you have a custom action class * which needs to add other properties, you can use propertizeAction to add any * sort of data you wish to the .action configuration file. * * This class is also in charge of displaying the shortcut configuration dialog. * The interplay between this class, KActionCollection, KisShortcutsEditor and * so on can be complex, and is sometimes synchronized by file I/O by reading * and writing the configuration files mentioned above. * * It is a global static. Grab an ::instance(). */ class KRITAWIDGETUTILS_EXPORT KisActionRegistry : public QObject { Q_OBJECT public: static KisActionRegistry *instance(); /** * @return true if the given action exists */ bool hasAction(const QString &name) const; /** * @return value @p property for an action @p name. * * Allow flexible info structure for KisActions, etc. */ QString getActionProperty(const QString &name, const QString &property); /** * Produces a new QAction based on the .action data files. * * N.B. this action will not be saved in the registry. */ - QAction *makeQAction(const QString &name, QObject *parent); + QAction *makeQAction(const QString &name, QObject *parent = 0); /** * Fills the standard QAction properties of an action. * * @return true if the action was loaded successfully. */ bool propertizeAction(const QString &name, QAction *a); /** * Called when "OK" button is pressed in settings dialog. */ void settingsPageSaved(); /** * Reload custom shortcuts from kritashortcutsrc */ void loadCustomShortcuts(); /** * Call after settings are changed. */ void notifySettingsUpdated(); // If config == 0, reload defaults void applyShortcutScheme(const KConfigBase *config = 0); struct ActionCategory { ActionCategory(); ActionCategory(const QString &_componentName, const QString &_categoryName); QString componentName; QString categoryName; bool isValid() const; private: bool m_isValid = false; }; ActionCategory fetchActionCategory(const QString &name) const; /** * Constructor. Please don't touch! */ KisActionRegistry(); /** * @brief loadShortcutScheme * @param schemeName */ void loadShortcutScheme(const QString &schemeName); // Undocumented void updateShortcut(const QString &name, QAction *ac); bool sanityCheckPropertized(const QString &name); QList registeredShortcutIds() const; Q_SIGNALS: void shortcutsUpdated(); private: class Private; Private * const d; }; #endif /* KIS_ACTION_REGISTRY_H */ diff --git a/libs/widgetutils/xmlgui/kactionconflictdetector.cpp b/libs/widgetutils/xmlgui/kactionconflictdetector.cpp index 4d0a8046cb..60334c9db6 100644 --- a/libs/widgetutils/xmlgui/kactionconflictdetector.cpp +++ b/libs/widgetutils/xmlgui/kactionconflictdetector.cpp @@ -1,68 +1,68 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2002 Joseph Wenninger (C) 2005-2006 Hamish Rodda 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 #include #include #include #include class KActionConflictDetector : public QObject { public: explicit KActionConflictDetector(QObject *parent = 0) : QObject(parent) { } bool eventFilter(QObject *watched, QEvent *event) override { if (qobject_cast(watched) && (event->type() == QEvent::Shortcut)) { QShortcutEvent *se = static_cast(event); if (se->isAmbiguous()) { KMessageBox::information( 0, // No widget to be seen around here - i18n("The key sequence '%1' is ambiguous. Use the 'Keyboard Shortcuts'\n" + i18n("XXXX The key sequence '%1' is ambiguous. Use the 'Keyboard Shortcuts'\n" "tab in 'Settings->Configure Krita...' dialog to solve the ambiguity.\n" - "No action will be triggered.", - se->key().toString(QKeySequence::NativeText)), + "No action will be triggered. %2", + se->key().toString(QKeySequence::NativeText), se->shortcutId()), i18n("Ambiguous shortcut detected")); return true; } } return QObject::eventFilter(watched, event); } }; void _k_installConflictDetector() { QCoreApplication *app = QCoreApplication::instance(); app->installEventFilter(new KActionConflictDetector(app)); } Q_COREAPP_STARTUP_FUNCTION(_k_installConflictDetector) diff --git a/plugins/assistants/Assistants/kis_assistant_tool.cc b/plugins/assistants/Assistants/kis_assistant_tool.cc index 4774587588..8cd038d6b9 100644 --- a/plugins/assistants/Assistants/kis_assistant_tool.cc +++ b/plugins/assistants/Assistants/kis_assistant_tool.cc @@ -1,1041 +1,1041 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * 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 "kis_dom_utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_global.h" #include "VanishingPointAssistant.h" #include KisAssistantTool::KisAssistantTool(KoCanvasBase * canvas) : KisTool(canvas, KisCursor::arrowCursor()), m_canvas(dynamic_cast(canvas)), m_assistantDrag(0), m_newAssistant(0), m_optionsWidget(0) { Q_ASSERT(m_canvas); setObjectName("tool_assistanttool"); } KisAssistantTool::~KisAssistantTool() { } void KisAssistantTool::activate(ToolActivation toolActivation, const QSet &shapes) { KisTool::activate(toolActivation, shapes); m_canvas->paintingAssistantsDecoration()->activateAssistantsEditor(); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_handleDrag = 0; m_internalMode = MODE_CREATION; m_assistantHelperYOffset = 10; m_handleSize = 17; m_canvas->paintingAssistantsDecoration()->setHandleSize(m_handleSize); if (m_optionsWidget) { m_canvas->paintingAssistantsDecoration()->deselectAssistant(); updateToolOptionsUI(); } m_canvas->updateCanvas(); } void KisAssistantTool::deactivate() { m_canvas->paintingAssistantsDecoration()->deactivateAssistantsEditor(); m_canvas->updateCanvas(); KisTool::deactivate(); } void KisAssistantTool::beginPrimaryAction(KoPointerEvent *event) { setMode(KisTool::PAINT_MODE); bool newAssistantAllowed = true; KisPaintingAssistantsDecorationSP canvasDecoration = m_canvas->paintingAssistantsDecoration(); if (m_newAssistant) { m_internalMode = MODE_CREATION; *m_newAssistant->handles().back() = canvasDecoration->snapToGuide(event, QPointF(), false); if (m_newAssistant->handles().size() == m_newAssistant->numHandles()) { addAssistant(); } else { m_newAssistant->addHandle(new KisPaintingAssistantHandle(canvasDecoration->snapToGuide(event, QPointF(), false)), HandleType::NORMAL); } m_canvas->updateCanvas(); return; } m_handleDrag = 0; double minDist = 81.0; QPointF mousePos = m_canvas->viewConverter()->documentToView(canvasDecoration->snapToGuide(event, QPointF(), false));//m_canvas->viewConverter()->documentToView(event->point); // syncs the assistant handles to the handles reference we store in this tool // they can get out of sync with the way the actions and paintevents occur // we probably need to stop storing a reference in m_handles and call the assistants directly m_handles = m_canvas->paintingAssistantsDecoration()->handles(); Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { // find out which handle on all assistants is closest to the mouse position // vanishing points have "side handles", so make sure to include that { QList allAssistantHandles; allAssistantHandles.append(assistant->handles()); allAssistantHandles.append(assistant->sideHandles()); Q_FOREACH (const KisPaintingAssistantHandleSP handle, allAssistantHandles) { double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*handle)); if (dist < minDist) { minDist = dist; m_handleDrag = handle; assistantSelected(assistant); // whatever handle is the closest contains the selected assistant } } } if(m_handleDrag && assistant->id() == "perspective") { // Look for the handle which was pressed if (m_handleDrag == assistant->topLeft()) { double dist = KisPaintingAssistant::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 = KisPaintingAssistant::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 = KisPaintingAssistant::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 = KisPaintingAssistant::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(), HandleType::NORMAL ); m_newAssistant->addHandle(m_selectedNode1, HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode2, HandleType::NORMAL); m_newAssistant->addHandle(assistant->bottomLeft(), HandleType::NORMAL); 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(), HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode1, HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode2, HandleType::NORMAL); m_newAssistant->addHandle(assistant->bottomRight(), HandleType::NORMAL); 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, HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode2, HandleType::NORMAL); m_newAssistant->addHandle(assistant->topRight(), HandleType::NORMAL); m_newAssistant->addHandle(assistant->topLeft(), HandleType::NORMAL); 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(), HandleType::NORMAL); m_newAssistant->addHandle(assistant->bottomRight(), HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode2, HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode1, HandleType::NORMAL); 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. QTransform initialTransform = m_canvas->coordinatesConverter()->documentToWidgetTransform(); QPointF actionsPosition = initialTransform.map(assistant->buttonPosition()); // for UI editor widget controls with move, show, and delete -- disregard document transforms like rotating and mirroring. // otherwise the UI controls get awkward to use when they are at 45 degree angles or the order of controls gets flipped backwards QPointF uiMousePosition = initialTransform.map( canvasDecoration->snapToGuide(event, QPointF(), false)); AssistantEditorData editorShared; // shared position data between assistant tool and decoration QPointF iconMovePosition(actionsPosition + editorShared.moveIconPosition); QPointF iconSnapPosition(actionsPosition + editorShared.snapIconPosition); QPointF iconDeletePosition(actionsPosition + editorShared.deleteIconPosition); QRectF deleteRect(iconDeletePosition, QSizeF(editorShared.deleteIconSize, editorShared.deleteIconSize)); QRectF visibleRect(iconSnapPosition, QSizeF(editorShared.snapIconSize, editorShared.snapIconSize)); QRectF moveRect(iconMovePosition, QSizeF(editorShared.moveIconSize, editorShared.moveIconSize)); if (moveRect.contains(uiMousePosition)) { m_assistantDrag = assistant; m_cursorStart = event->point; m_currentAdjustment = QPointF(); m_internalMode = MODE_EDITING; assistantSelected(assistant); // whatever handle is the closest contains the selected assistant return; } if (deleteRect.contains(uiMousePosition)) { removeAssistant(assistant); if(m_canvas->paintingAssistantsDecoration()->assistants().isEmpty()) { m_internalMode = MODE_CREATION; } else m_internalMode = MODE_EDITING; m_canvas->updateCanvas(); return; } if (visibleRect.contains(uiMousePosition)) { newAssistantAllowed = false; assistant->setSnappingActive(!assistant->isSnappingActive()); // toggle assistant->uncache();//this updates the chache of the assistant, very important. assistantSelected(assistant); // whatever handle is the closest contains the selected assistant } } if (newAssistantAllowed==true){//don't make a new assistant when I'm just toogling visibility// QString key = m_options.availableAssistantsComboBox->model()->index( m_options.availableAssistantsComboBox->currentIndex(), 0 ).data(Qt::UserRole).toString(); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get(key)->createPaintingAssistant()); m_internalMode = MODE_CREATION; m_newAssistant->addHandle(new KisPaintingAssistantHandle(canvasDecoration->snapToGuide(event, QPointF(), false)), HandleType::NORMAL); if (m_newAssistant->numHandles() <= 1) { addAssistant(); } else { m_newAssistant->addHandle(new KisPaintingAssistantHandle(canvasDecoration->snapToGuide(event, QPointF(), false)), HandleType::NORMAL); } } if (m_newAssistant) { m_newAssistant->setAssistantGlobalColorCache(m_canvas->paintingAssistantsDecoration()->globalAssistantsColor()); } m_canvas->updateCanvas(); } void KisAssistantTool::continuePrimaryAction(KoPointerEvent *event) { KisPaintingAssistantsDecorationSP canvasDecoration = m_canvas->paintingAssistantsDecoration(); 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 = canvasDecoration->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 = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*handle)); if (dist < minDist) { minDist = dist; m_handleCombine = handle; } } } m_canvas->updateCanvas(); } else if (m_assistantDrag) { QPointF newAdjustment = canvasDecoration->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 = assistant->closestCornerHandleFromPoint(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 KisAssistantTool::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; } else if (m_assistantDrag) { m_assistantDrag.clear(); } else if(m_internalMode == MODE_DRAGGING_TRANSLATING_TWONODES) { addAssistant(); m_internalMode = MODE_CREATION; } else { event->ignore(); } m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas } void KisAssistantTool::addAssistant() { m_canvas->paintingAssistantsDecoration()->addAssistant(m_newAssistant); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->paintingAssistantsDecoration()->setSelectedAssistant(m_newAssistant); updateToolOptionsUI(); // vanishing point assistant will get an extra option KisAbstractPerspectiveGrid* grid = dynamic_cast(m_newAssistant.data()); if (grid) { m_canvas->viewManager()->resourceProvider()->addPerspectiveGrid(grid); } m_newAssistant.clear(); } void KisAssistantTool::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(); m_canvas->paintingAssistantsDecoration()->deselectAssistant(); updateToolOptionsUI(); } void KisAssistantTool::assistantSelected(KisPaintingAssistantSP assistant) { m_canvas->paintingAssistantsDecoration()->setSelectedAssistant(assistant); updateToolOptionsUI(); } void KisAssistantTool::updateToolOptionsUI() { KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); bool hasActiveAssistant = m_selectedAssistant ? true : false; if (m_selectedAssistant) { bool isVanishingPointAssistant = m_selectedAssistant->id() == "vanishing point"; m_options.vanishingPointAngleSpinbox->setVisible(isVanishingPointAssistant); if (isVanishingPointAssistant) { QSharedPointer assis = qSharedPointerCast(m_selectedAssistant); m_options.vanishingPointAngleSpinbox->setValue(assis->referenceLineDensity()); } // load custom color settings from assistant (this happens when changing assistant m_options.useCustomAssistantColor->setChecked(m_selectedAssistant->useCustomColor()); m_options.customAssistantColorButton->setColor(m_selectedAssistant->assistantCustomColor()); float opacity = (float)m_selectedAssistant->assistantCustomColor().alpha()/255.0 * 100.0 ; m_options.customColorOpacitySlider->setValue(opacity); } else { m_options.vanishingPointAngleSpinbox->setVisible(false); // } // show/hide elements if an assistant is selected or not m_options.assistantsGlobalOpacitySlider->setVisible(hasActiveAssistant); m_options.assistantsColor->setVisible(hasActiveAssistant); m_options.globalColorLabel->setVisible(hasActiveAssistant); m_options.useCustomAssistantColor->setVisible(hasActiveAssistant); // hide custom color options if use custom color is not selected bool showCustomColorSettings = m_options.useCustomAssistantColor->isChecked() && hasActiveAssistant; m_options.customColorOpacitySlider->setVisible(showCustomColorSettings); m_options.customAssistantColorButton->setVisible(showCustomColorSettings); // disable global color settings if we are using the custom color m_options.assistantsGlobalOpacitySlider->setEnabled(!showCustomColorSettings); m_options.assistantsColor->setEnabled(!showCustomColorSettings); m_options.globalColorLabel->setEnabled(!showCustomColorSettings); } void KisAssistantTool::slotChangeVanishingPointAngle(double value) { if ( m_canvas->paintingAssistantsDecoration()->assistants().length() == 0) { return; } // get the selected assistant and change the angle value KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); if (m_selectedAssistant) { bool isVanishingPointAssistant = m_selectedAssistant->id() == "vanishing point"; if (isVanishingPointAssistant) { QSharedPointer assis = qSharedPointerCast(m_selectedAssistant); assis->setReferenceLineDensity((float)value); } } m_canvas->canvasWidget()->update(); } void KisAssistantTool::mouseMoveEvent(KoPointerEvent *event) { if (m_newAssistant && m_internalMode == MODE_CREATION) { *m_newAssistant->handles().back() = event->point; } 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 KisAssistantTool::paint(QPainter& _gc, const KoViewConverter &_converter) { QRectF canvasSize = QRectF(QPointF(0, 0), QSizeF(m_canvas->image()->size())); // show special display while a new assistant is in the process of being created if (m_newAssistant) { QColor assistantColor = m_newAssistant->effectiveAssistantColor(); assistantColor.setAlpha(80); m_newAssistant->drawAssistant(_gc, canvasSize, 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(m_handleSize * 0.5, m_handleSize * 0.5), QSizeF(m_handleSize, m_handleSize))); _gc.save(); _gc.setPen(Qt::NoPen); _gc.setBrush(assistantColor); _gc.drawPath(path); _gc.restore(); } } Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { QColor assistantColor = assistant->effectiveAssistantColor(); assistantColor.setAlpha(80); Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) { QRectF ellipse(_converter.documentToView(*handle) - QPointF(m_handleSize * 0.5, m_handleSize * 0.5), QSizeF(m_handleSize, m_handleSize)); // render handles differently if it is the one being dragged. if (handle == m_handleDrag || handle == m_handleCombine) { QPen stroke(assistantColor, 4); _gc.save(); _gc.setPen(stroke); _gc.setBrush(Qt::NoBrush); _gc.drawEllipse(ellipse); _gc.restore(); } } } } void KisAssistantTool::removeAllAssistants() { m_canvas->viewManager()->resourceProvider()->clearPerspectiveGrids(); m_canvas->paintingAssistantsDecoration()->removeAll(); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->updateCanvas(); m_canvas->paintingAssistantsDecoration()->deselectAssistant(); updateToolOptionsUI(); } void KisAssistantTool::loadAssistants() { KoFileDialog dialog(m_canvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenAssistant"); dialog.setCaption(i18n("Select an Assistant")); 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, HandleType::NORMAL); } 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; } // load custom shared assistant properties if ( xml.attributes().hasAttribute("useCustomColor")) { QStringRef useCustomColor = xml.attributes().value("useCustomColor"); bool usingColor = false; if (useCustomColor.toString() == "1") { usingColor = true; } assistant->setUseCustomColor(usingColor); } if ( xml.attributes().hasAttribute("useCustomColor")) { QStringRef customColor = xml.attributes().value("customColor"); assistant->setAssistantCustomColor( KisDomUtils::qStringToQColor(customColor.toString()) ); } } if (assistant) { assistant->loadCustomXml(&xml); } 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->addHandle(new KisPaintingAssistantHandle(pos+QPointF(-70,0)), HandleType::SIDE); assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(-140,0)), HandleType::SIDE); assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(70,0)), HandleType::SIDE); assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(140,0)), HandleType::SIDE); } 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 KisAssistantTool::saveAssistants() { if (m_handles.isEmpty()) return; QByteArray data; QXmlStreamWriter xml(&data); xml.writeStartDocument(); xml.writeStartElement("paintingassistant"); xml.writeAttribute("color", KisDomUtils::qColorToQString( m_canvas->paintingAssistantsDecoration()->globalAssistantsColor())); // global color if no custom color used 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.writeAttribute("useCustomColor", QString::number(assistant->useCustomColor())); xml.writeAttribute("customColor", KisDomUtils::qColorToQString(assistant->assistantCustomColor())); // custom assistant properties like angle density on vanishing point assistant->saveCustomXml(&xml); // handle information 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(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 *KisAssistantTool::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.loadAssistantButton->setIcon(KisIconUtils::loadIcon("document-open")); m_options.saveAssistantButton->setIcon(KisIconUtils::loadIcon("document-save")); m_options.deleteAllAssistantsButton->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.availableAssistantsComboBox->addItem(id.name(), id.id()); } connect(m_options.saveAssistantButton, SIGNAL(clicked()), SLOT(saveAssistants())); connect(m_options.loadAssistantButton, SIGNAL(clicked()), SLOT(loadAssistants())); connect(m_options.deleteAllAssistantsButton, SIGNAL(clicked()), SLOT(removeAllAssistants())); connect(m_options.assistantsColor, SIGNAL(changed(QColor)), SLOT(slotGlobalAssistantsColorChanged(QColor))); connect(m_options.assistantsGlobalOpacitySlider, SIGNAL(valueChanged(int)), SLOT(slotGlobalAssistantOpacityChanged())); connect(m_options.vanishingPointAngleSpinbox, SIGNAL(valueChanged(double)), this, SLOT(slotChangeVanishingPointAngle(double))); - ENTER_FUNCTION() << ppVar(m_canvas) << ppVar(m_canvas && m_canvas->paintingAssistantsDecoration()); + //ENTER_FUNCTION() << ppVar(m_canvas) << ppVar(m_canvas && m_canvas->paintingAssistantsDecoration()); // initialize UI elements with existing data if possible if (m_canvas && m_canvas->paintingAssistantsDecoration()) { const QColor color = m_canvas->paintingAssistantsDecoration()->globalAssistantsColor(); QColor opaqueColor = color; opaqueColor.setAlpha(255); - ENTER_FUNCTION() << ppVar(opaqueColor); + //ENTER_FUNCTION() << ppVar(opaqueColor); m_options.assistantsColor->setColor(opaqueColor); m_options.customAssistantColorButton->setColor(opaqueColor); m_options.assistantsGlobalOpacitySlider->setValue(color.alphaF() * 100.0); } else { m_options.assistantsColor->setColor(QColor(176, 176, 176, 255)); // grey default for all assistants m_options.assistantsGlobalOpacitySlider->setValue(100); // 100% } m_options.assistantsGlobalOpacitySlider->setPrefix(i18n("Opacity: ")); m_options.assistantsGlobalOpacitySlider->setSuffix(" %"); // custom color of selected assistant m_options.customColorOpacitySlider->setValue(100); // 100% m_options.customColorOpacitySlider->setPrefix(i18n("Opacity: ")); m_options.customColorOpacitySlider->setSuffix(" %"); connect(m_options.useCustomAssistantColor, SIGNAL(clicked(bool)), this, SLOT(slotUpdateCustomColor())); connect(m_options.customAssistantColorButton, SIGNAL(changed(QColor)), this, SLOT(slotUpdateCustomColor())); connect(m_options.customColorOpacitySlider, SIGNAL(valueChanged(int)), SLOT(slotCustomOpacityChanged())); m_options.vanishingPointAngleSpinbox->setPrefix(i18n("Density: ")); m_options.vanishingPointAngleSpinbox->setSuffix(QChar(Qt::Key_degree)); m_options.vanishingPointAngleSpinbox->setRange(1.0, 180.0); m_options.vanishingPointAngleSpinbox->setSingleStep(0.5); m_options.vanishingPointAngleSpinbox->setVisible(false); } updateToolOptionsUI(); return m_optionsWidget; } void KisAssistantTool::slotGlobalAssistantsColorChanged(const QColor& setColor) { // color and alpha are stored separately, so we need to merge the values before sending it on int oldAlpha = m_canvas->paintingAssistantsDecoration()->globalAssistantsColor().alpha(); QColor newColor = setColor; newColor.setAlpha(oldAlpha); m_canvas->paintingAssistantsDecoration()->setGlobalAssistantsColor(newColor); m_canvas->paintingAssistantsDecoration()->uncache(); m_canvas->canvasWidget()->update(); } void KisAssistantTool::slotGlobalAssistantOpacityChanged() { QColor newColor = m_canvas->paintingAssistantsDecoration()->globalAssistantsColor(); qreal newOpacity = m_options.assistantsGlobalOpacitySlider->value() * 0.01 * 255.0; newColor.setAlpha(int(newOpacity)); m_canvas->paintingAssistantsDecoration()->setGlobalAssistantsColor(newColor); m_canvas->paintingAssistantsDecoration()->uncache(); m_canvas->canvasWidget()->update(); } void KisAssistantTool::slotUpdateCustomColor() { // get the selected assistant and change the angle value KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); if (m_selectedAssistant) { m_selectedAssistant->setUseCustomColor(m_options.useCustomAssistantColor->isChecked()); // changing color doesn't keep alpha, so update that before we send it on QColor newColor = m_options.customAssistantColorButton->color(); newColor.setAlpha(m_selectedAssistant->assistantCustomColor().alpha()); m_selectedAssistant->setAssistantCustomColor(newColor); m_selectedAssistant->uncache(); } updateToolOptionsUI(); m_canvas->canvasWidget()->update(); } void KisAssistantTool::slotCustomOpacityChanged() { KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); if (m_selectedAssistant) { QColor newColor = m_selectedAssistant->assistantCustomColor(); qreal newOpacity = m_options.customColorOpacitySlider->value() * 0.01 * 255.0; newColor.setAlpha(int(newOpacity)); m_selectedAssistant->setAssistantCustomColor(newColor); m_selectedAssistant->uncache(); } // this forces the canvas to refresh to see the changes immediately m_canvas->paintingAssistantsDecoration()->uncache(); m_canvas->canvasWidget()->update(); } diff --git a/plugins/color/colorspaceextensions/kis_desaturate_adjustment.cpp b/plugins/color/colorspaceextensions/kis_desaturate_adjustment.cpp index e601e5ee96..7091e446c5 100644 --- a/plugins/color/colorspaceextensions/kis_desaturate_adjustment.cpp +++ b/plugins/color/colorspaceextensions/kis_desaturate_adjustment.cpp @@ -1,193 +1,193 @@ /* * Copyright (c) 2013 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; 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_desaturate_adjustment.h" #include #include #include #include #include #include #include #include #include #define SCALE_TO_FLOAT( v ) KoColorSpaceMaths< _channel_type_, float>::scaleToA( v ) #define SCALE_FROM_FLOAT( v ) KoColorSpaceMaths< float, _channel_type_>::scaleToA( v ) template class KisDesaturateAdjustment : public KoColorTransformation { typedef traits RGBTrait; typedef typename RGBTrait::Pixel RGBPixel; public: KisDesaturateAdjustment() { } public: void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const override { const RGBPixel* src = reinterpret_cast(srcU8); RGBPixel* dst = reinterpret_cast(dstU8); float r, g, b, gray; while (nPixels > 0) { r = SCALE_TO_FLOAT(src->red); g = SCALE_TO_FLOAT(src->green); b = SCALE_TO_FLOAT(src->blue); // http://www.tannerhelland.com/3643/grayscale-image-algorithm-vb6/ switch(m_type) { case 0: // lightness { gray = (qMax(qMax(r, g), b) + qMin(qMin(r, g), b)) / 2; break; } case 1: // luminosity BT 709 { gray = r * 0.2126 + g * 0.7152 + b * 0.0722; break; } case 2: // luminosity BT 601 { gray = r * 0.299 + g * 0.587 + b * 0.114; break; } case 3: // average { - gray = (r + g + b) / 2; + gray = (r + g + b) / 3; break; } case 4: // min { gray = qMin(qMin(r, g), b); break; } case 5: // min { gray = qMax(qMax(r, g), b); break; } default: gray = 0; } dst->red = SCALE_FROM_FLOAT(gray); dst->green = SCALE_FROM_FLOAT(gray); dst->blue = SCALE_FROM_FLOAT(gray); dst->alpha = src->alpha; --nPixels; ++src; ++dst; } } QList parameters() const override { QList list; list << "type"; return list; } int parameterId(const QString& name) const override { if (name == "type") { return 0; } return -1; } /** * name - "type": * 0: lightness * 1: luminosity * 2: average */ void setParameter(int id, const QVariant& parameter) override { switch(id) { case 0: m_type = parameter.toDouble(); break; default: ; } } private: int m_type; }; KisDesaturateAdjustmentFactory::KisDesaturateAdjustmentFactory() : KoColorTransformationFactory("desaturate_adjustment") { } QList< QPair< KoID, KoID > > KisDesaturateAdjustmentFactory::supportedModels() const { QList< QPair< KoID, KoID > > l; l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID)); l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID)); l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID)); l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID)); return l; } KoColorTransformation* KisDesaturateAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash parameters) const { KoColorTransformation * adj; if (colorSpace->colorModelId() != RGBAColorModelID) { dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisDesaturateAdjustmentFactory::createTransformation"; return 0; } if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) { adj = new KisDesaturateAdjustment< quint8, KoBgrTraits < quint8 > >(); } else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) { adj = new KisDesaturateAdjustment< quint16, KoBgrTraits < quint16 > >(); } #ifdef HAVE_OPENEXR else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) { adj = new KisDesaturateAdjustment< half, KoRgbTraits < half > >(); } #endif else if (colorSpace->colorDepthId() == Float32BitsColorDepthID) { adj = new KisDesaturateAdjustment< float, KoRgbTraits < float > >(); } else { dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisDesaturateAdjustmentFactory::createTransformation"; return 0; } adj->setParameters(parameters); return adj; } diff --git a/plugins/dockers/advancedcolorselector/kis_color_patches.cpp b/plugins/dockers/advancedcolorselector/kis_color_patches.cpp index 4c5261e7f4..f54557494c 100644 --- a/plugins/dockers/advancedcolorselector/kis_color_patches.cpp +++ b/plugins/dockers/advancedcolorselector/kis_color_patches.cpp @@ -1,350 +1,352 @@ /* * Copyright (c) 2010 Adam Celarek * * 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 "kis_color_patches.h" #include #include #include #include #include #include #include #include #include #include "kis_canvas2.h" #include "KoCanvasResourceProvider.h" #include "kis_display_color_converter.h" KisColorPatches::KisColorPatches(QString configPrefix, QWidget *parent) : KisColorSelectorBase(parent), m_allowColorListChangeGuard(true), m_scrollValue(0), m_configPrefix(configPrefix) { resize(1, 1); updateSettings(); } void KisColorPatches::setColors(QListcolors) { if (m_allowColorListChangeGuard) { m_colors = colors; m_allowColorListChangeGuard=false; KisColorPatches* parent = dynamic_cast(m_parent); if (parent) parent->setColors(colors); KisColorPatches* popup = dynamic_cast(m_popup); if (popup) popup->setColors(colors); m_allowColorListChangeGuard=true; update(); } } void KisColorPatches::paintEvent(QPaintEvent* e) { QPainter painter(this); if(m_allowScrolling) { if(m_direction == Vertical) painter.translate(0, m_scrollValue); else painter.translate(m_scrollValue, 0); } int widgetWidth = width(); int numPatchesInARow = qMax(widgetWidth/m_patchWidth, 1); int widgetHeight = height(); int numPatchesInACol = qMax(widgetHeight/m_patchHeight, 1); for(int i = m_buttonList.size(); i < qMin(fieldCount(), m_colors.size() + m_buttonList.size()); i++) { int row; int col; if(m_direction == Vertical) { row = i /numPatchesInARow; col = i % numPatchesInARow; } else { row= i % numPatchesInACol; col = i / numPatchesInACol; } QColor qcolor = converter()->toQColor(m_colors.at(i - m_buttonList.size())); painter.fillRect(col*m_patchWidth, row*m_patchHeight, m_patchWidth, m_patchHeight, qcolor); } QWidget::paintEvent(e); + } void KisColorPatches::wheelEvent(QWheelEvent* event) { m_scrollValue+=event->delta()/2; if(m_direction == Vertical) { if(m_scrollValue < -1*(heightOfAllPatches()-height())) m_scrollValue = -1*(heightOfAllPatches()-height()); } else { if(m_scrollValue < -1*(widthOfAllPatches()-width())) m_scrollValue = -1*(widthOfAllPatches()-width()); } if(m_scrollValue>0) m_scrollValue=0; update(); } void KisColorPatches::resizeEvent(QResizeEvent* event) { if(size()==QSize(1, 1)) return; QWheelEvent dummyWheelEvent(QPoint(), 0, Qt::NoButton, Qt::NoModifier); wheelEvent(&dummyWheelEvent); if(parentWidget()==0) { // this instance is a popup setMinimumWidth(m_patchWidth*(m_patchCount/4)); setMaximumWidth(minimumWidth()); } - if(m_allowScrolling == false && event->oldSize() != event->size()) { + + if (m_allowScrolling == false && event->oldSize() != event->size()) { if(m_direction == Horizontal) { setMaximumHeight(heightForWidth(width())); setMinimumHeight(heightForWidth(width())); } else { setMaximumWidth(widthForHeight(height())); setMinimumWidth(widthForHeight(height())); } } } void KisColorPatches::mouseReleaseEvent(QMouseEvent* event) { KisColorSelectorBase::mouseReleaseEvent(event); event->setAccepted(false); KisColorSelectorBase::mouseReleaseEvent(event); if (event->isAccepted() || !rect().contains(event->pos())) return; if (!m_canvas) return; KoColor color; if(colorAt(event->pos(), &color)) { if (event->button()==Qt::LeftButton) m_canvas->resourceManager()->setForegroundColor(color); else if (event->button()==Qt::RightButton) m_canvas->resourceManager()->setBackgroundColor(color); } } void KisColorPatches::mousePressEvent(QMouseEvent *event) { KoColor koColor; if(!colorAt(event->pos(), &koColor)) return; KisColorSelectorBase::mousePressEvent(event); if(event->isAccepted()) return; updateColorPreview(koColor); if (event->button() == Qt::LeftButton) m_dragStartPos = event->pos(); } void KisColorPatches::mouseMoveEvent(QMouseEvent *event) { event->ignore(); KisColorSelectorBase::mouseMoveEvent(event); if(event->isAccepted()) return; if (!(event->buttons() & Qt::LeftButton)) return; if ((event->pos() - m_dragStartPos).manhattanLength() < QApplication::startDragDistance()) return; KoColor koColor; if(!colorAt(m_dragStartPos, &koColor)) return; QDrag *drag = new QDrag(this); QMimeData *mimeData = new QMimeData; QColor color = converter()->toQColor(koColor); mimeData->setColorData(color); mimeData->setText(color.name()); drag->setMimeData(mimeData); drag->exec(Qt::CopyAction); event->accept(); } int KisColorPatches::patchCount() const { return m_patchCount; } bool KisColorPatches::colorAt(const QPoint &pos, KoColor *result) const { if(!rect().contains(pos)) return false; int scrollX = m_direction==Horizontal?m_scrollValue:0; int scrollY = m_direction==Vertical?m_scrollValue:0; int column = (pos.x()-scrollX)/m_patchWidth; int row = (pos.y()-scrollY)/m_patchHeight; int patchNr; if(m_direction == Vertical) { int patchesInARow = width()/m_patchWidth; patchNr=row*patchesInARow+column; } else { // Vertical int patchesInAColumn = height()/m_patchHeight; patchNr=column*patchesInAColumn+row; } patchNr-=m_buttonList.size(); if(patchNr>=0 && patchNr buttonList) { for(int i=0; isetParent(this); } m_buttonList = buttonList; } void KisColorPatches::updateSettings() { KisColorSelectorBase::updateSettings(); KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); if(cfg.readEntry(m_configPrefix+"Alignment", false)) m_direction=Vertical; else m_direction=Horizontal; - m_allowScrolling=cfg.readEntry(m_configPrefix+"Scrolling", true); m_numCols=cfg.readEntry(m_configPrefix+"NumCols", 1); m_numRows=cfg.readEntry(m_configPrefix+"NumRows", 1); m_patchCount=cfg.readEntry(m_configPrefix+"Count", 15); m_patchWidth=cfg.readEntry(m_configPrefix+"Width", 20); m_patchHeight=cfg.readEntry(m_configPrefix+"Height", 20); if(m_patchHeight == 0) { m_patchHeight = 1; } if(parentWidget()==0) { // this instance is a popup m_allowScrolling = false; m_direction = Horizontal; m_patchWidth*=2; m_patchHeight*=2; } for(int i=0; isetGeometry(0, i*m_patchHeight, m_patchWidth, m_patchHeight); } setMaximumWidth(QWIDGETSIZE_MAX); setMinimumWidth(1); setMaximumHeight(QWIDGETSIZE_MAX); setMinimumHeight(1); if(m_allowScrolling && m_direction == Horizontal) { setMaximumHeight(m_numRows*m_patchHeight); setMinimumHeight(m_numRows*m_patchHeight); } if(m_allowScrolling && m_direction == Vertical) { setMaximumWidth(m_numCols*m_patchWidth); setMinimumWidth(m_numCols*m_patchWidth); } if(m_allowScrolling == false) { m_scrollValue = 0; } + QResizeEvent dummy(size(), QSize(-1,-1)); resizeEvent(&dummy); setPopupBehaviour(false, false); update(); } int KisColorPatches::widthOfAllPatches() { return (fieldCount()/m_numRows)*m_patchWidth; } int KisColorPatches::heightOfAllPatches() { return (fieldCount()/m_numCols)*m_patchHeight; } int KisColorPatches::heightForWidth(int width) const { int numPatchesInARow = width / m_patchWidth; int numRows = qMax((fieldCount() - 1), 1) / qMax(numPatchesInARow + 1, 1); - return numRows * m_patchHeight; + return qMax(numRows * m_patchHeight, m_patchHeight); } int KisColorPatches::widthForHeight(int height) const { if (height == 0) { return 0; } if (m_patchHeight == 0) { return 0; } int numPatchesInACol = height / m_patchHeight; int numCols = (fieldCount() - 1) / (numPatchesInACol + 1); - return numCols*m_patchWidth; + return qMax(numCols*m_patchWidth, m_patchWidth); } int KisColorPatches::fieldCount() const { return m_patchCount+m_buttonList.size(); } diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_settings.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_settings.cpp index 229e9f3a6e..c648ec5ed4 100644 --- a/plugins/dockers/advancedcolorselector/kis_color_selector_settings.cpp +++ b/plugins/dockers/advancedcolorselector/kis_color_selector_settings.cpp @@ -1,632 +1,632 @@ /* * Copyright (C) 2010 Celarek Adam * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_selector_settings.h" #include "ui_wdg_color_selector_settings.h" #include #include #include #include #include #include #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" #include "KoColorProfile.h" #include "kis_color_selector_combo_box.h" #include "kis_color_selector.h" #include "kis_config.h" KisColorSelectorSettings::KisColorSelectorSettings(QWidget *parent) : KisPreferenceSet(parent), ui(new Ui::KisColorSelectorSettings) { ui->setupUi(this); resize(minimumSize()); ui->colorSelectorConfiguration->setColorSpace(ui->colorSpace->currentColorSpace()); ui->useDifferentColorSpaceCheckbox->setChecked(false); connect(ui->useDifferentColorSpaceCheckbox, SIGNAL(clicked(bool)), this, SLOT(useDifferentColorSpaceChecked(bool))); /* color docker selector drop down */ ui->dockerColorSettingsComboBox->addItem(i18n("Advanced Color Selector")); //ui->dockerColorSettingsComboBox->addItem(i18n("Color Sliders")); ui->dockerColorSettingsComboBox->addItem(i18n("Color Hotkeys")); ui->dockerColorSettingsComboBox->setCurrentIndex(0); // start off seeing advanced color selector properties connect( ui->dockerColorSettingsComboBox, SIGNAL(currentIndexChanged(int)),this, SLOT(changedColorDocker(int))); changedColorDocker(0); /* advanced color docker options */ ui->dockerResizeOptionsComboBox->addItem(i18n("Change to a Horizontal Layout")); ui->dockerResizeOptionsComboBox->addItem(i18n("Hide Shade Selector")); ui->dockerResizeOptionsComboBox->addItem(i18n("Do Nothing")); ui->dockerResizeOptionsComboBox->setCurrentIndex(0); ui->zoomSelectorOptionComboBox->addItem(i18n("When Pressing Middle Mouse Button")); ui->zoomSelectorOptionComboBox->addItem(i18n("On Mouse Over")); ui->zoomSelectorOptionComboBox->addItem(i18n("Never")); ui->zoomSelectorOptionComboBox->setCurrentIndex(0); ui->colorSelectorTypeComboBox->addItem(i18n("HSV")); ui->colorSelectorTypeComboBox->addItem(i18n("HSL")); ui->colorSelectorTypeComboBox->addItem(i18n("HSI")); ui->colorSelectorTypeComboBox->addItem(i18n("HSY'")); ui->colorSelectorTypeComboBox->setCurrentIndex(0); connect( ui->colorSelectorTypeComboBox, SIGNAL(currentIndexChanged(int)),this, SLOT(changedACSColorSelectorType(int))); changedACSColorSelectorType(0); // initialize everything to HSV at the start ui->ACSshadeSelectorMyPaintColorModelComboBox->addItem(i18n("HSV")); ui->ACSshadeSelectorMyPaintColorModelComboBox->addItem(i18n("HSL")); ui->ACSshadeSelectorMyPaintColorModelComboBox->addItem(i18n("HSI")); ui->ACSshadeSelectorMyPaintColorModelComboBox->addItem(i18n("HSY'")); ui->ACSshadeSelectorMyPaintColorModelComboBox->setCurrentIndex(0); ui->ACSShadeSelectorTypeComboBox->addItem(i18n("MyPaint")); ui->ACSShadeSelectorTypeComboBox->addItem(i18n("Minimal")); ui->ACSShadeSelectorTypeComboBox->addItem(i18n("Do Not Show")); ui->ACSShadeSelectorTypeComboBox->setCurrentIndex(0); changedACSShadeSelectorType(0); // show/hide UI elements for MyPaint settings connect( ui->ACSShadeSelectorTypeComboBox, SIGNAL(currentIndexChanged(int)),this, SLOT(changedACSShadeSelectorType(int))); ui->commonColorsAlignVertical->setChecked(true); ui->commonColorsAlignHorizontal->setChecked(true); connect( ui->commonColorsAlignHorizontal, SIGNAL(toggled(bool)), this, SLOT(changedACSColorAlignment(bool))); connect( ui->lastUsedColorsAlignHorizontal, SIGNAL(toggled(bool)), this, SLOT(changedACSLastUsedColorAlignment(bool))); changedACSColorAlignment(ui->commonColorsAlignHorizontal->isChecked()); changedACSLastUsedColorAlignment(ui->lastUsedColorsAlignHorizontal->isChecked()); connect(ui->colorSpace, SIGNAL(colorSpaceChanged(const KoColorSpace*)), ui->colorSelectorConfiguration, SLOT(setColorSpace(const KoColorSpace*))); connect(this, SIGNAL(hsxchanged(int)), ui->colorSelectorConfiguration, SLOT(setList(int))); connect(ui->minimalShadeSelectorLineCount, SIGNAL(valueChanged(int)), ui->minimalShadeSelectorLineSettings, SLOT(setLineCount(int))); connect(ui->minimalShadeSelectorLineSettings, SIGNAL(lineCountChanged(int)), ui->minimalShadeSelectorLineCount, SLOT(setValue(int))); connect(ui->minimalShadeSelectorAsGradient, SIGNAL(toggled(bool)), ui->minimalShadeSelectorLineSettings, SIGNAL(setGradient(bool))); connect(ui->minimalShadeSelectorAsColorPatches, SIGNAL(toggled(bool)), ui->minimalShadeSelectorLineSettings, SIGNAL(setPatches(bool))); connect(ui->minimalShadeSelectorLineHeight, SIGNAL(valueChanged(int)), ui->minimalShadeSelectorLineSettings, SIGNAL(setLineHeight(int))); connect(ui->minimalShadeSelectorPatchesPerLine, SIGNAL(valueChanged(int)), ui->minimalShadeSelectorLineSettings, SIGNAL(setPatchCount(int))); } KisColorSelectorSettings::~KisColorSelectorSettings() { delete ui; } QString KisColorSelectorSettings::id() { return QString("advancedColorSelector"); } QString KisColorSelectorSettings::name() { return header(); } QString KisColorSelectorSettings::header() { return QString(i18n("Color Selector Settings")); } QIcon KisColorSelectorSettings::icon() { return KisIconUtils::loadIcon("extended_color_selector"); } void KisColorSelectorSettings::savePreferences() const { // write cfg KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); KConfigGroup hsxcfg = KSharedConfig::openConfig()->group("hsxColorSlider"); KConfigGroup hotkeycfg = KSharedConfig::openConfig()->group("colorhotkeys"); // advanced color selector cfg.writeEntry("onDockerResize", ui->dockerResizeOptionsComboBox->currentIndex()); cfg.writeEntry("zoomSelectorOptions", ui->zoomSelectorOptionComboBox->currentIndex() ); cfg.writeEntry("zoomSize", ui->popupSize->value()); bool useCustomColorSpace = ui->useDifferentColorSpaceCheckbox->isChecked(); const KoColorSpace* colorSpace = useCustomColorSpace ? ui->colorSpace->currentColorSpace() : 0; KisConfig kisconfig(false); kisconfig.setCustomColorSelectorColorSpace(colorSpace); //color patches cfg.writeEntry("lastUsedColorsShow", ui->lastUsedColorsShow->isChecked()); cfg.writeEntry("lastUsedColorsAlignment", ui->lastUsedColorsAlignVertical->isChecked()); cfg.writeEntry("lastUsedColorsScrolling", ui->lastUsedColorsAllowScrolling->isChecked()); cfg.writeEntry("lastUsedColorsNumCols", ui->lastUsedColorsNumCols->value()); cfg.writeEntry("lastUsedColorsNumRows", ui->lastUsedColorsNumRows->value()); cfg.writeEntry("lastUsedColorsCount", ui->lastUsedColorsPatchCount->value()); cfg.writeEntry("lastUsedColorsWidth", ui->lastUsedColorsWidth->value()); cfg.writeEntry("lastUsedColorsHeight", ui->lastUsedColorsHeight->value()); cfg.writeEntry("commonColorsShow", ui->commonColorsShow->isChecked()); cfg.writeEntry("commonColorsAlignment", ui->commonColorsAlignVertical->isChecked()); cfg.writeEntry("commonColorsScrolling", ui->commonColorsAllowScrolling->isChecked()); cfg.writeEntry("commonColorsNumCols", ui->commonColorsNumCols->value()); cfg.writeEntry("commonColorsNumRows", ui->commonColorsNumRows->value()); cfg.writeEntry("commonColorsCount", ui->commonColorsPatchCount->value()); cfg.writeEntry("commonColorsWidth", ui->commonColorsWidth->value()); cfg.writeEntry("commonColorsHeight", ui->commonColorsHeight->value()); cfg.writeEntry("commonColorsAutoUpdate", ui->commonColorsAutoUpdate->isChecked()); //shade selector int shadeSelectorTypeIndex = ui->ACSShadeSelectorTypeComboBox->currentIndex(); if(shadeSelectorTypeIndex == 0) { cfg.writeEntry("shadeSelectorType", "MyPaint"); } else if (shadeSelectorTypeIndex == 1) { cfg.writeEntry("shadeSelectorType", "Minimal"); } else { cfg.writeEntry("shadeSelectorType", "Hidden"); } cfg.writeEntry("shadeSelectorUpdateOnRightClick", ui->shadeSelectorUpdateOnRightClick->isChecked()); cfg.writeEntry("shadeSelectorUpdateOnForeground", ui->shadeSelectorUpdateOnForeground->isChecked()); cfg.writeEntry("shadeSelectorUpdateOnLeftClick", ui->shadeSelectorUpdateOnLeftClick->isChecked()); cfg.writeEntry("shadeSelectorUpdateOnBackground", ui->shadeSelectorUpdateOnBackground->isChecked()); cfg.writeEntry("hidePopupOnClickCheck", ui->hidePopupOnClickCheck->isChecked()); //mypaint model int shadeMyPaintComboBoxIndex = ui->ACSshadeSelectorMyPaintColorModelComboBox->currentIndex(); if (shadeMyPaintComboBoxIndex == 0 ) { cfg.writeEntry("shadeMyPaintType", "HSV"); } else if (shadeMyPaintComboBoxIndex == 1 ) { cfg.writeEntry("shadeMyPaintType", "HSL"); } else if (shadeMyPaintComboBoxIndex == 2 ) { cfg.writeEntry("shadeMyPaintType", "HSI"); } else { // HSY cfg.writeEntry("shadeMyPaintType", "HSY"); } cfg.writeEntry("minimalShadeSelectorAsGradient", ui->minimalShadeSelectorAsGradient->isChecked()); cfg.writeEntry("minimalShadeSelectorPatchCount", ui->minimalShadeSelectorPatchesPerLine->value()); cfg.writeEntry("minimalShadeSelectorLineConfig", ui->minimalShadeSelectorLineSettings->toString()); cfg.writeEntry("minimalShadeSelectorLineHeight", ui->minimalShadeSelectorLineHeight->value()); //color selector KisColorSelectorComboBox* cstw = dynamic_cast(ui->colorSelectorConfiguration); cfg.writeEntry("colorSelectorConfiguration", cstw->configuration().toString()); cfg.writeEntry("hsxSettingType", ui->colorSelectorTypeComboBox->currentIndex()); //luma// cfg.writeEntry("lumaR", ui->l_lumaR->value()); cfg.writeEntry("lumaG", ui->l_lumaG->value()); cfg.writeEntry("lumaB", ui->l_lumaB->value()); cfg.writeEntry("gamma", ui->SP_Gamma->value()); //slider// hsxcfg.writeEntry("hsvH", ui->csl_hsvH->isChecked()); hsxcfg.writeEntry("hsvS", ui->csl_hsvS->isChecked()); hsxcfg.writeEntry("hsvV", ui->csl_hsvV->isChecked()); hsxcfg.writeEntry("hslH", ui->csl_hslH->isChecked()); hsxcfg.writeEntry("hslS", ui->csl_hslS->isChecked()); hsxcfg.writeEntry("hslL", ui->csl_hslL->isChecked()); hsxcfg.writeEntry("hsiH", ui->csl_hsiH->isChecked()); hsxcfg.writeEntry("hsiS", ui->csl_hsiS->isChecked()); hsxcfg.writeEntry("hsiI", ui->csl_hsiI->isChecked()); hsxcfg.writeEntry("hsyH", ui->csl_hsyH->isChecked()); hsxcfg.writeEntry("hsyS", ui->csl_hsyS->isChecked()); hsxcfg.writeEntry("hsyY", ui->csl_hsyY->isChecked()); //hotkeys// hotkeycfg.writeEntry("steps_lightness", ui->sb_lightness->value()); hotkeycfg.writeEntry("steps_saturation", ui->sb_saturation->value()); hotkeycfg.writeEntry("steps_hue", ui->sb_hue->value()); hotkeycfg.writeEntry("steps_redgreen", ui->sb_rg->value()); hotkeycfg.writeEntry("steps_blueyellow", ui->sb_by->value()); emit settingsChanged(); } //void KisColorSelectorSettings::changeEvent(QEvent *e) //{ // QDialog::changeEvent(e); // switch (e->type()) { // case QEvent::LanguageChange: // ui->retranslateUi(this); // break; // default: // break; // } //} void KisColorSelectorSettings::changedColorDocker(int index) { // having a situation where too many sections are visible makes the window too large. turn all off before turning more on ui->colorSliderOptions->hide(); ui->advancedColorSelectorOptions->hide(); ui->hotKeyOptions->hide(); if (index == 0) { // advanced color selector options selected ui->advancedColorSelectorOptions->show(); ui->colorSliderOptions->hide(); ui->hotKeyOptions->hide(); } // else if (index == 1) { // color slider options selected // ui->advancedColorSelectorOptions->hide(); // ui->hotKeyOptions->hide(); // ui->colorSliderOptions->show(); // } else { ui->colorSliderOptions->hide(); ui->advancedColorSelectorOptions->hide(); ui->hotKeyOptions->show(); } } void KisColorSelectorSettings::changedACSColorSelectorType(int index) { ui->lumaCoefficientGroupbox->setVisible(false); if (index == 0) { // HSV ui->ACSTypeDescriptionLabel->setText(i18n("Values goes from black to white, or black to the most saturated colour. Saturation, in turn, goes from the most saturated colour to white, grey or black.")); } else if (index == 1) { // HSL ui->ACSTypeDescriptionLabel->setText(i18n("Lightness goes from black to white, with middle grey being equal to the most saturated colour.")); } else if (index == 2) { // HSI ui->ACSTypeDescriptionLabel->setText(i18n("Intensity maps to the sum of rgb components")); } else { // HSY' ui->ACSTypeDescriptionLabel->setText(i18n("Luma(Y') is weighted by its coefficients which are configurable. Default values are set to 'rec 709'.")); ui->lumaCoefficientGroupbox->setVisible(true); } ui->colorSelectorConfiguration->update(); emit hsxchanged(index); } void KisColorSelectorSettings::changedACSColorAlignment(bool toggled) { // this slot is tied to the horizontal radio button's state being changed // you can infer the vertical state ui->lbl_commonColorsNumCols->setDisabled(toggled); ui->commonColorsNumCols->setDisabled(toggled); ui->lbl_commonColorsNumRows->setEnabled(toggled); ui->commonColorsNumRows->setEnabled(toggled); } void KisColorSelectorSettings::changedACSLastUsedColorAlignment(bool toggled) { // this slot is tied to the horizontal radio button's state being changed // you can infer the vertical state ui->lbl_lastUsedNumCols->setDisabled(toggled); ui->lastUsedColorsNumCols->setDisabled(toggled); ui->lbl_lastUsedNumRows->setEnabled(toggled); ui->lastUsedColorsNumRows->setEnabled(toggled); } void KisColorSelectorSettings::changedACSShadeSelectorType(int index) { if (index == 0) { // MyPaint ui->minimalShadeSelectorGroup->hide(); ui->myPaintColorModelLabel->show(); ui->ACSshadeSelectorMyPaintColorModelComboBox->show(); } else if (index == 1) { // Minimal ui->minimalShadeSelectorGroup->show(); ui->myPaintColorModelLabel->hide(); ui->ACSshadeSelectorMyPaintColorModelComboBox->hide(); }else { // do not show ui->minimalShadeSelectorGroup->hide(); ui->myPaintColorModelLabel->hide(); ui->ACSshadeSelectorMyPaintColorModelComboBox->hide(); } } void KisColorSelectorSettings::useDifferentColorSpaceChecked(bool enabled) { ui->colorSpace->setEnabled(enabled); } void KisColorSelectorSettings::loadPreferences() { //read cfg //don't forget to also add a new entry to the default preferences KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); KConfigGroup hsxcfg = KSharedConfig::openConfig()->group("hsxColorSlider"); KConfigGroup hotkeycfg = KSharedConfig::openConfig()->group("colorhotkeys"); // Advanced color selector ui->dockerResizeOptionsComboBox->setCurrentIndex( (int)cfg.readEntry("onDockerResize", 0) ); ui->zoomSelectorOptionComboBox->setCurrentIndex( (int) cfg.readEntry("zoomSelectorOptions", 0) ); ui->popupSize->setValue(cfg.readEntry("zoomSize", 280)); { KisConfig kisconfig(true); const KoColorSpace *cs = kisconfig.customColorSelectorColorSpace(); if (cs) { ui->useDifferentColorSpaceCheckbox->setChecked(true); ui->colorSpace->setEnabled(true); ui->colorSpace->setCurrentColorSpace(cs); } else { ui->useDifferentColorSpaceCheckbox->setChecked(false); ui->colorSpace->setEnabled(false); } } //color patches ui->lastUsedColorsShow->setChecked(cfg.readEntry("lastUsedColorsShow", true)); bool a = cfg.readEntry("lastUsedColorsAlignment", true); ui->lastUsedColorsAlignVertical->setChecked(a); ui->lastUsedColorsAlignHorizontal->setChecked(!a); ui->lastUsedColorsAllowScrolling->setChecked(cfg.readEntry("lastUsedColorsScrolling", true)); ui->lastUsedColorsNumCols->setValue(cfg.readEntry("lastUsedColorsNumCols", 1)); ui->lastUsedColorsNumRows->setValue(cfg.readEntry("lastUsedColorsNumRows", 1)); ui->lastUsedColorsPatchCount->setValue(cfg.readEntry("lastUsedColorsCount", 20)); ui->lastUsedColorsWidth->setValue(cfg.readEntry("lastUsedColorsWidth", 16)); ui->lastUsedColorsHeight->setValue(cfg.readEntry("lastUsedColorsHeight", 16)); ui->commonColorsShow->setChecked(cfg.readEntry("commonColorsShow", true)); a = cfg.readEntry("commonColorsAlignment", false); ui->commonColorsAlignVertical->setChecked(a); ui->commonColorsAlignHorizontal->setChecked(!a); - ui->commonColorsAllowScrolling->setChecked(cfg.readEntry("commonColorsScrolling", false)); + ui->commonColorsAllowScrolling->setChecked(cfg.readEntry("commonColorsScrolling", true)); ui->commonColorsNumCols->setValue(cfg.readEntry("commonColorsNumCols", 1)); ui->commonColorsNumRows->setValue(cfg.readEntry("commonColorsNumRows", 1)); ui->commonColorsPatchCount->setValue(cfg.readEntry("commonColorsCount", 12)); ui->commonColorsWidth->setValue(cfg.readEntry("commonColorsWidth", 16)); ui->commonColorsHeight->setValue(cfg.readEntry("commonColorsHeight", 16)); ui->commonColorsAutoUpdate->setChecked(cfg.readEntry("commonColorsAutoUpdate", false)); //shade selector QString shadeSelectorType=cfg.readEntry("shadeSelectorType", "Minimal"); if ( shadeSelectorType == "MyPaint") { ui->ACSShadeSelectorTypeComboBox->setCurrentIndex(0); } else if (shadeSelectorType == "Minimal") { ui->ACSShadeSelectorTypeComboBox->setCurrentIndex(1); } else { // Hidden ui->ACSShadeSelectorTypeComboBox->setCurrentIndex(2); } ui->shadeSelectorUpdateOnRightClick->setChecked(cfg.readEntry("shadeSelectorUpdateOnRightClick", false)); ui->shadeSelectorUpdateOnLeftClick->setChecked(cfg.readEntry("shadeSelectorUpdateOnLeftClick", false)); ui->shadeSelectorUpdateOnForeground->setChecked(cfg.readEntry("shadeSelectorUpdateOnForeground", true)); ui->shadeSelectorUpdateOnBackground->setChecked(cfg.readEntry("shadeSelectorUpdateOnBackground", true)); ui->hidePopupOnClickCheck->setChecked(cfg.readEntry("hidePopupOnClickCheck", false)); QString shadeMyPaintType = cfg.readEntry("shadeMyPaintType", "HSV"); if (shadeMyPaintType == "HSV" ) { ui->ACSshadeSelectorMyPaintColorModelComboBox->setCurrentIndex(0); } else if (shadeMyPaintType == "HSL" ) { ui->ACSshadeSelectorMyPaintColorModelComboBox->setCurrentIndex(1); } else if (shadeMyPaintType == "HSI" ) { ui->ACSshadeSelectorMyPaintColorModelComboBox->setCurrentIndex(2); } else { // HSY ui->ACSshadeSelectorMyPaintColorModelComboBox->setCurrentIndex(3); } bool asGradient = cfg.readEntry("minimalShadeSelectorAsGradient", true); if(asGradient) ui->minimalShadeSelectorAsGradient->setChecked(true); else ui->minimalShadeSelectorAsColorPatches->setChecked(true); ui->minimalShadeSelectorPatchesPerLine->setValue(cfg.readEntry("minimalShadeSelectorPatchCount", 10)); ui->minimalShadeSelectorLineSettings->fromString(cfg.readEntry("minimalShadeSelectorLineConfig", "0|0.2|0|0|0|0|0;1|0|1|1|0|0|0;2|0|-1|1|0|0|0;")); ui->minimalShadeSelectorLineHeight->setValue(cfg.readEntry("minimalShadeSelectorLineHeight", 10)); int hsxSettingType= (int)cfg.readEntry("hsxSettingType", 0); ui->colorSelectorTypeComboBox->setCurrentIndex(hsxSettingType); //color selector KisColorSelectorComboBox* cstw = dynamic_cast(ui->colorSelectorConfiguration); cstw->setConfiguration(KisColorSelectorConfiguration::fromString(cfg.readEntry("colorSelectorConfiguration", "3|0|5|0"))); // triangle selector //luma values// ui->l_lumaR->setValue(cfg.readEntry("lumaR", 0.2126)); ui->l_lumaG->setValue(cfg.readEntry("lumaG", 0.7152)); ui->l_lumaB->setValue(cfg.readEntry("lumaB", 0.0722)); ui->SP_Gamma->setValue(cfg.readEntry("gamma", 2.2)); //color sliders// ui->csl_hsvH->setChecked(hsxcfg.readEntry("hsvH", false)); ui->csl_hsvS->setChecked(hsxcfg.readEntry("hsvS", false)); ui->csl_hsvV->setChecked(hsxcfg.readEntry("hsvV", false)); ui->csl_hslH->setChecked(hsxcfg.readEntry("hslH", true)); ui->csl_hslS->setChecked(hsxcfg.readEntry("hslS", true)); ui->csl_hslL->setChecked(hsxcfg.readEntry("hslL", true)); ui->csl_hsiH->setChecked(hsxcfg.readEntry("hsiH", false)); ui->csl_hsiS->setChecked(hsxcfg.readEntry("hsiS", false)); ui->csl_hsiI->setChecked(hsxcfg.readEntry("hsiI", false)); ui->csl_hsyH->setChecked(hsxcfg.readEntry("hsyH", false)); ui->csl_hsyS->setChecked(hsxcfg.readEntry("hsyS", false)); ui->csl_hsyY->setChecked(hsxcfg.readEntry("hsyY", false)); //hotkeys// ui->sb_lightness->setValue(hotkeycfg.readEntry("steps_lightness", 10)); ui->sb_saturation->setValue(hotkeycfg.readEntry("steps_saturation", 10)); ui->sb_hue->setValue(hotkeycfg.readEntry("steps_hue", 36)); ui->sb_rg->setValue(hotkeycfg.readEntry("steps_redgreen", 10)); ui->sb_by->setValue(hotkeycfg.readEntry("steps_blueyellow", 10)); } void KisColorSelectorSettings::loadDefaultPreferences() { //set defaults //if you change something, don't forget that loadPreferences should be kept in sync // advanced color selector docker ui->dockerResizeOptionsComboBox->setCurrentIndex(0); ui->zoomSelectorOptionComboBox->setCurrentIndex(0); ui->popupSize->setValue(280); ui->useDifferentColorSpaceCheckbox->setChecked(false); ui->colorSpace->setCurrentColorModel(KoID("RGBA")); ui->colorSpace->setCurrentColorDepth(KoID("U8")); ui->colorSpace->setCurrentProfile(KoColorSpaceRegistry::instance()->rgb8()->profile()->name()); //color patches ui->lastUsedColorsShow->setChecked(true); ui->lastUsedColorsAlignVertical->setChecked(true); ui->lastUsedColorsAlignHorizontal->setChecked(false); ui->lastUsedColorsAllowScrolling->setChecked(true); ui->lastUsedColorsNumCols->setValue(1); ui->lastUsedColorsNumRows->setValue(1); ui->lastUsedColorsPatchCount->setValue(20); ui->lastUsedColorsWidth->setValue(16); ui->lastUsedColorsHeight->setValue(16); ui->commonColorsShow->setChecked(true); ui->commonColorsAlignVertical->setChecked(false); ui->commonColorsAlignHorizontal->setChecked(true); - ui->commonColorsAllowScrolling->setChecked(false); + ui->commonColorsAllowScrolling->setChecked(true); ui->commonColorsNumCols->setValue(1); ui->commonColorsNumRows->setValue(1); ui->commonColorsPatchCount->setValue(12); ui->commonColorsWidth->setValue(16); ui->commonColorsHeight->setValue(16); ui->commonColorsAutoUpdate->setChecked(false); //shade selector ui->ACSShadeSelectorTypeComboBox->setCurrentIndex(1); // Minimal ui->ACSshadeSelectorMyPaintColorModelComboBox->setCurrentIndex(0); ui->shadeSelectorUpdateOnRightClick->setChecked(false); ui->shadeSelectorUpdateOnLeftClick->setChecked(false); ui->shadeSelectorUpdateOnForeground->setChecked(true); ui->shadeSelectorUpdateOnBackground->setChecked(true); bool asGradient = true; if(asGradient) ui->minimalShadeSelectorAsGradient->setChecked(true); else ui->minimalShadeSelectorAsColorPatches->setChecked(true); ui->minimalShadeSelectorPatchesPerLine->setValue(10); ui->minimalShadeSelectorLineSettings->fromString("0|0.2|0|0|0|0|0;1|0|1|1|0|0|0;2|0|-1|1|0|0|0;"); ui->minimalShadeSelectorLineHeight->setValue(10); // set advanced color selector to use HSV ui->colorSelectorTypeComboBox->setCurrentIndex(0); KisColorSelectorComboBox* cstw = dynamic_cast(ui->colorSelectorConfiguration); cstw->setConfiguration(KisColorSelectorConfiguration("3|0|5|0")); // triangle selector //luma// ui->l_lumaR->setValue(0.2126); ui->l_lumaG->setValue(0.7152); ui->l_lumaB->setValue(0.0722); ui->SP_Gamma->setValue(2.2); //color sliders// ui->csl_hsvH->setChecked(false); ui->csl_hsvS->setChecked(false); ui->csl_hsvV->setChecked(false); ui->csl_hslH->setChecked(true); ui->csl_hslS->setChecked(true); ui->csl_hslL->setChecked(true); ui->csl_hsiH->setChecked(false); ui->csl_hsiS->setChecked(false); ui->csl_hsiI->setChecked(false); ui->csl_hsyH->setChecked(false); ui->csl_hsyS->setChecked(false); ui->csl_hsyY->setChecked(false); //hotkeys// ui->sb_lightness->setValue(10); ui->sb_saturation->setValue(10); ui->sb_hue->setValue(36); ui->sb_rg->setValue(10); ui->sb_by->setValue(10); } KisColorSelectorSettingsDialog::KisColorSelectorSettingsDialog(QWidget *parent) : QDialog(parent), m_widget(new KisColorSelectorSettings(this)) { QLayout* l = new QVBoxLayout(this); l->addWidget(m_widget); m_widget->loadPreferences(); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::RestoreDefaults, Qt::Horizontal, this); l->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), m_widget, SLOT(savePreferences())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), m_widget, SLOT(loadDefaultPreferences())); } diff --git a/plugins/dockers/advancedcolorselector/wdg_color_selector_settings.ui b/plugins/dockers/advancedcolorselector/wdg_color_selector_settings.ui index 2397ed6b47..1228962083 100644 --- a/plugins/dockers/advancedcolorselector/wdg_color_selector_settings.ui +++ b/plugins/dockers/advancedcolorselector/wdg_color_selector_settings.ui @@ -1,1557 +1,1560 @@ KisColorSelectorSettings 0 0 721 1108 0 0 Color Selector Settings Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 6 20 Docker: 0 0 0 30 0 0 0 280 0 Color Selector 0 0 Qt::Vertical QSizePolicy::MinimumExpanding 20 5 6 12 6 6 0 0 Qt::LeftToRight Color &Model Type: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter colorSelectorConfiguration 0 0 0 0 Type Description goes here Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true 0 0 Luma Coefficients 0 0 6 0 0 Red': Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 50 0 4 0 0 Green': 0 0 Blue': 0 0 50 0 4 0 0 50 0 4 <html><head/><body><p>This sets the gamma value that the linearised HSY Luminosity is crunched with. 1 makes the selector fully linear, 2.2 is a practical default value.</p></body></html> 1 -3.000000000000000 3.000000000000000 0.100000000000000 2.200000000000000 Gamma: 0 0 Color Selector Uses Different Color Space than Ima&ge true false 0 0 Qt::Vertical QSizePolicy::MinimumExpanding 20 5 Behavior 0 0 Qt::LeftToRight When Docker Resizes: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Show Zoom Selector UI: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Zoom Selector Size: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 px 100 1000 10 260 true 0 0 Hide Popup on click. Qt::Vertical QSizePolicy::MinimumExpanding 20 40 Shade Selector Type: Color model: Qt::Horizontal 20 20 0 0 0 Update Selector When: Right clicking on shade selector Left clicking on shade selector this doesn't include a color change by the shade selector Foreground color changes false this doesn't include a color change by the shade selector Background color change true 0 0 Minimal Shade Selector 0 0 Display: &Gradient Colo&r Patches 0 0 Qt::Horizontal 59 20 Line Count: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 1 10 3 Line Height: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter px 8 99 16 Patches Per Line: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical QSizePolicy::MinimumExpanding 20 10 true 0 0 Color History 0 0 Show Color Histor&y true Patch Options Height: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter px 16 Width: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter px 16 Max Patches: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 1 30 Qt::Horizontal 40 20 Layout 0 &Vertical true Colu&mns: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter lastUsedColorsNumCols 1 20 2 Hori&zontal &Rows: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter lastUsedColorsNumRows 1 20 3 Allow scrolling true Qt::Vertical QSizePolicy::MinimumExpanding 20 5 Colors from Image true Show Colors from the ima&ge true Patch Options Height: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter px 16 Width: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter px 16 Max Patches: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 1 30 Qt::Horizontal 40 20 Layout 6 0 &Vertical Colu&mns: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter commonColorsNumCols 1 20 2 Hori&zontal true &Rows: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter commonColorsNumRows 1 20 3 this can be slow on big images Update after every stroke Allow scrolling + + true + Qt::Vertical QSizePolicy::MinimumExpanding 20 10 HSV Sliders to Show Hue Saturation Value HSL Sliders to Show Hue Saturation Lightness HSI Sliders to Show Hue Saturation Intensity HSY' Sliders to Show Hue Saturation Luma Lightness, Saturation and Hue hotkey steps Lightness: steps Qt::Horizontal 40 20 Saturation: steps Qt::Horizontal 40 20 Hue: steps YUV Redder/Greener/Bluer/Yellower hotkey steps Redder/Greener: steps Qt::Horizontal 40 20 Bluer/Yellower: steps Qt::Vertical 20 10 KisDoubleParseSpinBox QDoubleSpinBox
kis_double_parse_spin_box.h
KisColorSpaceSelector QWidget
widgets/kis_color_space_selector.h
1
KisIntParseSpinBox QSpinBox
kis_int_parse_spin_box.h
KisColorSelectorComboBox QComboBox
kis_color_selector_combo_box.h
KisShadeSelectorLinesSettings QComboBox
kis_shade_selector_lines_settings.h
minimalShadeSelectorAsColorPatches toggled(bool) minimalShadeSelectorPatchesPerLine setEnabled(bool) 194 393 520 463
diff --git a/plugins/dockers/arrangedocker/arrange_docker_widget.cpp b/plugins/dockers/arrangedocker/arrange_docker_widget.cpp index b0020342fa..760ff036da 100644 --- a/plugins/dockers/arrangedocker/arrange_docker_widget.cpp +++ b/plugins/dockers/arrangedocker/arrange_docker_widget.cpp @@ -1,91 +1,102 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * 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 "arrange_docker_widget.h" #include "ui_arrange_docker_widget.h" #include "kis_debug.h" #include "kactioncollection.h" #include #include struct ArrangeDockerWidget::Private { }; ArrangeDockerWidget::ArrangeDockerWidget(QWidget *parent) : QWidget(parent), ui(new Ui::ArrangeDockerWidget), m_d(new Private) { ui->setupUi(this); } ArrangeDockerWidget::~ArrangeDockerWidget() { } void replaceAction(QToolButton *button, QAction *newAction) { Q_FOREACH (QAction *action, button->actions()) { button->removeAction(action); } if (newAction) { button->setDefaultAction(newAction); } } void ArrangeDockerWidget::setActionCollection(KActionCollection *collection) { const bool enabled = collection->action("object_order_front"); if (enabled) { replaceAction(ui->bringToFront, collection->action("object_order_front")); replaceAction(ui->raiseLevel, collection->action("object_order_raise")); replaceAction(ui->lowerLevel, collection->action("object_order_lower")); replaceAction(ui->sendBack, collection->action("object_order_back")); replaceAction(ui->leftAlign, collection->action("object_align_horizontal_left")); replaceAction(ui->hCenterAlign, collection->action("object_align_horizontal_center")); replaceAction(ui->rightAlign, collection->action("object_align_horizontal_right")); replaceAction(ui->topAlign, collection->action("object_align_vertical_top")); replaceAction(ui->vCenterAlign, collection->action("object_align_vertical_center")); replaceAction(ui->bottomAlign, collection->action("object_align_vertical_bottom")); replaceAction(ui->hDistributeLeft, collection->action("object_distribute_horizontal_left")); replaceAction(ui->hDistributeCenter, collection->action("object_distribute_horizontal_center")); replaceAction(ui->hDistributeRight, collection->action("object_distribute_horizontal_right")); replaceAction(ui->hDistributeGaps, collection->action("object_distribute_horizontal_gaps")); replaceAction(ui->vDistributeTop, collection->action("object_distribute_vertical_top")); replaceAction(ui->vDistributeCenter, collection->action("object_distribute_vertical_center")); replaceAction(ui->vDistributeBottom, collection->action("object_distribute_vertical_bottom")); replaceAction(ui->vDistributeGaps, collection->action("object_distribute_vertical_gaps")); replaceAction(ui->group, collection->action("object_group")); replaceAction(ui->ungroup, collection->action("object_ungroup")); } setEnabled(enabled); } +void ArrangeDockerWidget::switchState(bool enabled) +{ + if (enabled) { + ui->buttons->show(); + ui->disabledLabel->hide(); + } else { + ui->buttons->hide(); + ui->disabledLabel->show(); + } +} + diff --git a/plugins/dockers/arrangedocker/arrange_docker_widget.h b/plugins/dockers/arrangedocker/arrange_docker_widget.h index 88ce957f74..4054dbc5b9 100644 --- a/plugins/dockers/arrangedocker/arrange_docker_widget.h +++ b/plugins/dockers/arrangedocker/arrange_docker_widget.h @@ -1,48 +1,49 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * 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. */ #ifndef ARRANGE_DOCKER_WIDGET_H #define ARRANGE_DOCKER_WIDGET_H #include #include #include "kactioncollection.h" namespace Ui { class ArrangeDockerWidget; } class ArrangeDockerWidget : public QWidget { Q_OBJECT public: explicit ArrangeDockerWidget(QWidget *parent = 0); ~ArrangeDockerWidget() override; void setActionCollection(KActionCollection *collection); + void switchState(bool enabled); private: Ui::ArrangeDockerWidget *ui; struct Private; const QScopedPointer m_d; }; #endif // ARRANGE_DOCKER_WIDGET_H diff --git a/plugins/dockers/arrangedocker/arrange_docker_widget.ui b/plugins/dockers/arrangedocker/arrange_docker_widget.ui index efdc308b02..4090b5ef1d 100644 --- a/plugins/dockers/arrangedocker/arrange_docker_widget.ui +++ b/plugins/dockers/arrangedocker/arrange_docker_widget.ui @@ -1,303 +1,533 @@ ArrangeDockerWidget 0 0 - 838 - 448 + 303 + 426 - - - - - 2 + + + + + QFrame::NoFrame - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - QFrame::VLine - - - QFrame::Raised - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Minimum - - - - 6 - 6 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 6 - 6 - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - QFrame::VLine - - - QFrame::Raised - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - - - - Qt::NoFocus - - - - - - - + + QFrame::Raised + + + + 4 + + + 5 + + + 5 + + + 5 + + + + + Activate the Select Shapes Tool to arrange objects. + + + true + + + + + - - - - Qt::Horizontal + + + + + 0 + 0 + - - - 631 - 20 - + + QFrame::NoFrame - + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 4 + + + 7 + + + 7 + + + 5 + + + + + Align + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Order + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Spacing + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + Group + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Qt::NoFocus + + + + + + + 22 + 22 + + + + + + + + Distribute + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + + 0 + 15 + + + + Qt::Horizontal + + + + + + + + 0 + 15 + + + + Qt::Horizontal + + + + + + + + 0 + 15 + + + + Qt::Horizontal + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + - + Qt::Vertical 20 - 323 + 40 diff --git a/plugins/dockers/arrangedocker/arrangedocker_dock.cpp b/plugins/dockers/arrangedocker/arrangedocker_dock.cpp index c414dbfa66..0208d8e25c 100644 --- a/plugins/dockers/arrangedocker/arrangedocker_dock.cpp +++ b/plugins/dockers/arrangedocker/arrangedocker_dock.cpp @@ -1,82 +1,90 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * 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 "arrangedocker_dock.h" #include #include "kis_canvas2.h" #include #include "arrange_docker_widget.h" #include #include ArrangeDockerDock::ArrangeDockerDock( ) : QDockWidget(i18n("Arrange")) , m_canvas(0) { m_configWidget = new ArrangeDockerWidget(this); + m_configWidget->switchState(false); setWidget(m_configWidget); setEnabled(m_canvas); } ArrangeDockerDock::~ArrangeDockerDock() { } void ArrangeDockerDock::setCanvas(KoCanvasBase * canvas) { if(canvas && m_canvas == canvas) return; if (m_canvas) { m_canvasConnections.clear(); m_canvas->disconnectCanvasObserver(this); m_canvas->image()->disconnect(this); } m_canvas = canvas ? dynamic_cast(canvas) : 0; setEnabled(m_canvas); if (m_canvas) { m_canvasConnections.addConnection( m_canvas->toolProxy(), SIGNAL(toolChanged(QString)), this, - SLOT(slotToolChanged())); + SLOT(slotToolChanged(QString))); m_canvasConnections.addConnection( m_canvas->shapeManager(), SIGNAL(selectionChanged()), this, SLOT(slotToolChanged())); slotToolChanged(); } } void ArrangeDockerDock::unsetCanvas() { setCanvas(0); } void ArrangeDockerDock::slotToolChanged() { KActionCollection *collection = m_canvas->viewManager()->actionCollection(); m_configWidget->setActionCollection(collection); } + +void ArrangeDockerDock::slotToolChanged(QString toolId) +{ + bool enableWidget = (toolId == "InteractionTool") ? true : false; + m_configWidget->switchState(enableWidget); + slotToolChanged(); +} diff --git a/plugins/dockers/arrangedocker/arrangedocker_dock.h b/plugins/dockers/arrangedocker/arrangedocker_dock.h index b8243d9330..ffcb2eac08 100644 --- a/plugins/dockers/arrangedocker/arrangedocker_dock.h +++ b/plugins/dockers/arrangedocker/arrangedocker_dock.h @@ -1,48 +1,49 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * 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. */ #ifndef _GRID_DOCK_H_ #define _GRID_DOCK_H_ #include #include #include "kis_signal_auto_connection.h" class KisCanvas2; class ArrangeDockerWidget; class KisSignalAutoConnection; class ArrangeDockerDock : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: ArrangeDockerDock(); ~ArrangeDockerDock() override; QString observerName() override { return "ArrangeDockerDock"; } void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; private Q_SLOTS: void slotToolChanged(); + void slotToolChanged(QString toolId); private: ArrangeDockerWidget *m_configWidget; QPointer m_canvas; KisSignalAutoConnectionsStore m_canvasConnections; }; #endif diff --git a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp index 8357e546b6..02532c1b78 100644 --- a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp +++ b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp @@ -1,459 +1,459 @@ /* * Copyright (c) 2009 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.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 #include #include #include "artisticcolorselector_dock.h" #include #include #include #include #include "ui_wdgArtisticColorSelector.h" #include "ui_wdgARCSSettings.h" #include "ui_wdgWheelPreferencesPopup.h" class KisMainWindow; struct ArtisticColorSelectorUI: public QWidget, public Ui_wdgArtisticColorSelector { ArtisticColorSelectorUI() { setupUi(this); } }; struct ARCSSettingsUI: public QWidget, public Ui_wdgARCSSettings { ARCSSettingsUI() { setupUi(this); } }; struct WheelPreferencesPopupUI: public QWidget, public Ui_wdgWheelPreferencesPopup { WheelPreferencesPopupUI() { setupUi(this); } }; ArtisticColorSelectorDock::ArtisticColorSelectorDock() : QDockWidget(i18n("Artistic Color Selector")) , m_canvas(nullptr) , m_resourceProvider(0) , m_selectedMask(nullptr) { setEnabled(false); m_hsxButtons = new QButtonGroup(); m_preferencesUI = new ARCSSettingsUI(); m_wheelPrefsUI = new WheelPreferencesPopupUI(); m_selectorUI = new ArtisticColorSelectorUI(); QPixmap hueStepsPixmap = KisIconUtils::loadIcon("wheel-sectors").pixmap(16,16); QPixmap saturationStepsPixmap = KisIconUtils::loadIcon("wheel-rings").pixmap(16,16); QPixmap valueScaleStepsPixmap = KisIconUtils::loadIcon("wheel-light").pixmap(16,16); QIcon infinityIcon = KisIconUtils::loadIcon("infinity"); m_infinityPixmap = infinityIcon.pixmap(16,16); m_selectorUI->colorSelector->loadSettings(); m_selectorUI->bnWheelPrefs->setIcon(KisIconUtils::loadIcon("wheel-sectors")); m_selectorUI->bnWheelPrefs->setPopupWidget(m_wheelPrefsUI); m_selectorUI->bnDockerPrefs->setPopupWidget(m_preferencesUI); m_selectorUI->bnDockerPrefs->setIcon(KisIconUtils::loadIcon("configure")); //preferences m_hsxButtons->addButton(m_preferencesUI->bnHsy, KisColor::HSY); m_hsxButtons->addButton(m_preferencesUI->bnHsi, KisColor::HSI); m_hsxButtons->addButton(m_preferencesUI->bnHsl, KisColor::HSL); m_hsxButtons->addButton(m_preferencesUI->bnHsv, KisColor::HSV); m_wheelPrefsUI->bnInverseSat->setChecked(m_selectorUI->colorSelector->isSaturationInverted()); m_wheelPrefsUI->labelHueSteps->setPixmap(hueStepsPixmap); m_wheelPrefsUI->labelSaturationSteps->setPixmap(saturationStepsPixmap); m_wheelPrefsUI->labelValueScaleSteps->setPixmap(valueScaleStepsPixmap); m_wheelPrefsUI->numHueSteps->setRange(MIN_NUM_UI_HUE_PIECES, MAX_NUM_HUE_PIECES); m_wheelPrefsUI->numSaturationSteps->setRange(MIN_NUM_SATURATION_RINGS, MAX_NUM_SATURATION_RINGS); m_wheelPrefsUI->numValueScaleSteps->setRange(MIN_NUM_UI_LIGHT_PIECES, MAX_NUM_LIGHT_PIECES); m_wheelPrefsUI->bnInfHueSteps->setIcon(infinityIcon); m_wheelPrefsUI->bnInfValueScaleSteps->setIcon(infinityIcon); m_wheelPrefsUI->bnInfHueSteps->setToolTip(i18n("Continuous Mode")); m_wheelPrefsUI->bnInfValueScaleSteps->setToolTip(i18n("Continuous Mode")); int selectorHueSteps = m_selectorUI->colorSelector->getNumPieces(); if (selectorHueSteps == 1) { m_wheelPrefsUI->bnInfHueSteps->setChecked(true); } else { m_wheelPrefsUI->bnInfHueSteps->setChecked(false); } m_wheelPrefsUI->numHueSteps->setValue(selectorHueSteps); m_wheelPrefsUI->numSaturationSteps->setValue(m_selectorUI->colorSelector->getNumRings()); int selectorValueScaleSteps = m_selectorUI->colorSelector->getNumLightPieces(); if (selectorValueScaleSteps == 1) { m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(true); } else { m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(false); } m_wheelPrefsUI->numValueScaleSteps->setValue(m_selectorUI->colorSelector->getNumLightPieces()); m_preferencesUI->bnDefInfHueSteps->setIcon(infinityIcon); m_preferencesUI->bnDefInfValueScaleSteps->setIcon(infinityIcon); m_preferencesUI->labelDefHueSteps->setPixmap(hueStepsPixmap); m_preferencesUI->labelDefSaturationSteps->setPixmap(saturationStepsPixmap); m_preferencesUI->labelDefValueScaleSteps->setPixmap(valueScaleStepsPixmap); m_preferencesUI->defaultHueSteps->setRange(MIN_NUM_HUE_PIECES, MAX_NUM_HUE_PIECES); m_preferencesUI->defaultSaturationSteps->setRange(MIN_NUM_SATURATION_RINGS, MAX_NUM_SATURATION_RINGS); m_preferencesUI->defaultValueScaleSteps->setRange(MIN_NUM_LIGHT_PIECES, MAX_NUM_LIGHT_PIECES); m_preferencesUI->defaultHueSteps->setValue(m_selectorUI->colorSelector->getDefaultHueSteps()); m_preferencesUI->defaultSaturationSteps->setValue(m_selectorUI->colorSelector->getDefaultSaturationSteps()); m_preferencesUI->defaultValueScaleSteps->setValue(m_selectorUI->colorSelector->getDefaultValueScaleSteps()); m_preferencesUI->showBgColor->setChecked(m_selectorUI->colorSelector->getShowBgColor()); m_preferencesUI->showValueScaleNumbers->setChecked(m_selectorUI->colorSelector->getShowValueScaleNumbers()); m_preferencesUI->enforceGamutMask->setChecked(m_selectorUI->colorSelector->enforceGamutMask()); m_preferencesUI->permissiveGamutMask->setChecked(!m_selectorUI->colorSelector->enforceGamutMask()); m_preferencesUI->spLumaR->setValue(m_selectorUI->colorSelector->lumaR()); m_preferencesUI->spLumaG->setValue(m_selectorUI->colorSelector->lumaG()); m_preferencesUI->spLumaB->setValue(m_selectorUI->colorSelector->lumaB()); m_preferencesUI->spLumaGamma->setValue(m_selectorUI->colorSelector->lumaGamma()); switch(m_selectorUI->colorSelector->getColorSpace()) { case KisColor::HSV: { m_preferencesUI->bnHsv->setChecked(true); } break; case KisColor::HSI: { m_preferencesUI->bnHsi->setChecked(true); } break; case KisColor::HSL: { m_preferencesUI->bnHsl->setChecked(true); } break; case KisColor::HSY: { m_preferencesUI->bnHsy->setChecked(true); } break; } if (m_selectorUI->colorSelector->getColorSpace() == KisColor::HSY) { m_preferencesUI->lumaCoefficientBox->show(); } else { m_preferencesUI->lumaCoefficientBox->hide(); } connect(m_wheelPrefsUI->numValueScaleSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); connect(m_wheelPrefsUI->numHueSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); connect(m_wheelPrefsUI->numSaturationSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); connect(m_wheelPrefsUI->bnInverseSat , SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); connect(m_wheelPrefsUI->bnInfHueSteps , SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); connect(m_wheelPrefsUI->bnInfValueScaleSteps, SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); connect(m_wheelPrefsUI->bnDefault , SIGNAL(clicked(bool)) , SLOT(slotResetDefaultSettings())); connect(m_preferencesUI->defaultHueSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->defaultSaturationSteps, SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->defaultValueScaleSteps, SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->bnDefInfHueSteps , SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->bnDefInfValueScaleSteps, SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->showBgColor , SIGNAL(toggled(bool)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->showValueScaleNumbers, SIGNAL(toggled(bool)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->enforceGamutMask , SIGNAL(toggled(bool)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->spLumaR , SIGNAL(valueChanged(qreal)), SLOT(slotColorSpaceSelected())); connect(m_preferencesUI->spLumaG , SIGNAL(valueChanged(qreal)), SLOT(slotColorSpaceSelected())); connect(m_preferencesUI->spLumaB , SIGNAL(valueChanged(qreal)), SLOT(slotColorSpaceSelected())); connect(m_preferencesUI->spLumaGamma , SIGNAL(valueChanged(qreal)), SLOT(slotColorSpaceSelected())); connect(m_selectorUI->colorSelector , SIGNAL(sigFgColorChanged(KisColor)) , SLOT(slotFgColorChanged(KisColor))); connect(m_selectorUI->colorSelector , SIGNAL(sigBgColorChanged(KisColor)) , SLOT(slotBgColorChanged(KisColor))); // gamut mask connections connect(m_selectorUI->gamutMaskToolbar, SIGNAL(sigGamutMaskToggle(bool)), SLOT(slotGamutMaskToggle(bool))); connect(m_hsxButtons , SIGNAL(buttonClicked(int)) , SLOT(slotColorSpaceSelected())); setWidget(m_selectorUI); } ArtisticColorSelectorDock::~ArtisticColorSelectorDock() { m_selectorUI->colorSelector->saveSettings(); delete m_hsxButtons; } void ArtisticColorSelectorDock::setViewManager(KisViewManager* kisview) { m_resourceProvider = kisview->resourceProvider(); m_selectorUI->colorSelector->setFgColor(m_resourceProvider->resourceManager()->foregroundColor()); m_selectorUI->colorSelector->setBgColor(m_resourceProvider->resourceManager()->backgroundColor()); connect(m_resourceProvider, SIGNAL(sigGamutMaskChanged(KoGamutMaskSP)), this, SLOT(slotGamutMaskSet(KoGamutMaskSP))); connect(m_resourceProvider, SIGNAL(sigGamutMaskUnset()), this, SLOT(slotGamutMaskUnset())); connect(m_resourceProvider, SIGNAL(sigGamutMaskPreviewUpdate()), this, SLOT(slotGamutMaskPreviewUpdate())); m_selectorUI->gamutMaskToolbar->connectMaskSignals(m_resourceProvider); } void ArtisticColorSelectorDock::slotCanvasResourceChanged(int key, const QVariant& value) { if(key == KoCanvasResourceProvider::ForegroundColor) m_selectorUI->colorSelector->setFgColor(value.value()); if(key == KoCanvasResourceProvider::BackgroundColor) m_selectorUI->colorSelector->setBgColor(value.value()); } void ArtisticColorSelectorDock::slotFgColorChanged(const KisColor& color) { m_resourceProvider->resourceManager()->setForegroundColor( KoColor(color.toKoColor(), m_resourceProvider->resourceManager()->foregroundColor().colorSpace()) ); } void ArtisticColorSelectorDock::slotBgColorChanged(const KisColor& color) { m_resourceProvider->resourceManager()->setBackgroundColor( KoColor(color.toKoColor(), m_resourceProvider->resourceManager()->backgroundColor().colorSpace()) ); } void ArtisticColorSelectorDock::slotColorSpaceSelected() { KisColor::Type type = static_cast( m_hsxButtons->id(m_hsxButtons->checkedButton())); m_selectorUI->colorSelector->setColorSpace(type); if (type == KisColor::HSY) { m_preferencesUI->lumaCoefficientBox->show(); } else { m_preferencesUI->lumaCoefficientBox->hide(); } m_selectorUI->colorSelector->setLumaCoefficients( m_preferencesUI->spLumaR->value(), m_preferencesUI->spLumaG->value(), m_preferencesUI->spLumaB->value(), m_preferencesUI->spLumaGamma->value() ); } void ArtisticColorSelectorDock::slotPreferenceChanged() { int hueSteps = DEFAULT_HUE_STEPS; if (m_wheelPrefsUI->bnInfHueSteps->isChecked()) { m_wheelPrefsUI->numHueSteps->setEnabled(false); hueSteps = 1; } else { m_wheelPrefsUI->numHueSteps->setEnabled(true); hueSteps = m_wheelPrefsUI->numHueSteps->value(); } m_selectorUI->colorSelector->setNumPieces(hueSteps); m_selectorUI->colorSelector->setNumRings(m_wheelPrefsUI->numSaturationSteps->value()); int valueScaleSteps; if (m_wheelPrefsUI->bnInfValueScaleSteps->isChecked()) { m_wheelPrefsUI->numValueScaleSteps->setEnabled(false); valueScaleSteps = 1; } else { valueScaleSteps = m_wheelPrefsUI->numValueScaleSteps->value(); m_wheelPrefsUI->numValueScaleSteps->setEnabled(true); } m_selectorUI->colorSelector->setNumLightPieces(valueScaleSteps); int defHueSteps; if (m_preferencesUI->bnDefInfHueSteps->isChecked()) { m_preferencesUI->defaultHueSteps->setEnabled(false); defHueSteps = 1; } else { m_preferencesUI->defaultHueSteps->setEnabled(true); defHueSteps = m_preferencesUI->defaultHueSteps->value(); } m_selectorUI->colorSelector->setDefaultHueSteps(defHueSteps); m_selectorUI->colorSelector->setDefaultSaturationSteps(m_preferencesUI->defaultSaturationSteps->value()); int defValueScaleSteps; if (m_preferencesUI->bnDefInfValueScaleSteps->isChecked()) { m_preferencesUI->defaultValueScaleSteps->setEnabled(false); defValueScaleSteps = 1; } else { m_preferencesUI->defaultValueScaleSteps->setEnabled(true); defValueScaleSteps = m_preferencesUI->defaultValueScaleSteps->value(); } m_selectorUI->colorSelector->setDefaultValueScaleSteps(defValueScaleSteps); m_selectorUI->colorSelector->setShowBgColor(m_preferencesUI->showBgColor->isChecked()); m_selectorUI->colorSelector->setShowValueScaleNumbers(m_preferencesUI->showValueScaleNumbers->isChecked()); m_selectorUI->colorSelector->setEnforceGamutMask(m_preferencesUI->enforceGamutMask->isChecked()); m_selectorUI->colorSelector->setInverseSaturation(m_wheelPrefsUI->bnInverseSat->isChecked()); } void ArtisticColorSelectorDock::slotResetDefaultSettings() { quint32 hueSteps = m_selectorUI->colorSelector->getDefaultHueSteps(); quint32 saturationSteps = m_selectorUI->colorSelector->getDefaultSaturationSteps(); quint32 valueScaleSteps = m_selectorUI->colorSelector->getDefaultValueScaleSteps(); m_selectorUI->colorSelector->setNumRings(saturationSteps); m_wheelPrefsUI->numSaturationSteps->blockSignals(true); m_wheelPrefsUI->numSaturationSteps->setValue(saturationSteps); m_wheelPrefsUI->numSaturationSteps->blockSignals(false); m_selectorUI->colorSelector->setNumPieces(hueSteps); m_wheelPrefsUI->numHueSteps->blockSignals(true); m_wheelPrefsUI->numHueSteps->setValue(hueSteps); m_wheelPrefsUI->numHueSteps->blockSignals(false); if (hueSteps == 1) { m_wheelPrefsUI->numHueSteps->setEnabled(false); m_wheelPrefsUI->bnInfHueSteps->setChecked(true); } else { m_wheelPrefsUI->numHueSteps->setEnabled(true); m_wheelPrefsUI->bnInfHueSteps->setChecked(false); } m_selectorUI->colorSelector->setNumLightPieces(valueScaleSteps); m_wheelPrefsUI->numValueScaleSteps->blockSignals(true); m_wheelPrefsUI->numValueScaleSteps->setValue(valueScaleSteps); m_wheelPrefsUI->numValueScaleSteps->blockSignals(false); if (valueScaleSteps == 1) { m_wheelPrefsUI->numValueScaleSteps->setEnabled(false); m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(true); } else { m_wheelPrefsUI->numValueScaleSteps->setEnabled(true); m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(false); } } void ArtisticColorSelectorDock::slotGamutMaskToggle(bool checked) { bool b = (!m_selectedMask) ? false : checked; if (b == true) { m_selectorUI->colorSelector->setGamutMask(m_selectedMask); } m_selectorUI->colorSelector->setGamutMaskOn(b); } void ArtisticColorSelectorDock::setCanvas(KoCanvasBase *canvas) { if (!canvas) { return; } m_canvas = dynamic_cast(canvas); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); } if (m_canvas) { connect(m_canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), SLOT(slotCanvasResourceChanged(int,QVariant))); connect(m_canvas->displayColorConverter(), SIGNAL(displayConfigurationChanged()), SLOT(slotSelectorSettingsChanged())); m_selectorUI->colorSelector->setColorConverter(m_canvas->displayColorConverter()); setEnabled(true); } } void ArtisticColorSelectorDock::unsetCanvas() { setEnabled(false); m_canvas = nullptr; m_selectorUI->colorSelector->setColorConverter(KisDisplayColorConverter::dumbConverterInstance()); } void ArtisticColorSelectorDock::slotGamutMaskSet(KoGamutMaskSP mask) { if (!mask) { return; } m_selectedMask = mask; if (m_selectedMask) { m_selectorUI->colorSelector->setGamutMask(m_selectedMask); slotGamutMaskToggle(true); } else { slotGamutMaskToggle(false); } } void ArtisticColorSelectorDock::slotGamutMaskUnset() { if (!m_selectedMask) { return; } m_selectedMask = nullptr; slotGamutMaskToggle(false); m_selectorUI->colorSelector->setGamutMask(m_selectedMask); } void ArtisticColorSelectorDock::slotGamutMaskPreviewUpdate() { - m_selectorUI->colorSelector->update(); + m_selectorUI->colorSelector->setDirty(); } void ArtisticColorSelectorDock::slotSelectorSettingsChanged() { m_selectorUI->colorSelector->setDirty(); } diff --git a/plugins/dockers/compositiondocker/compositiondocker_dock.cpp b/plugins/dockers/compositiondocker/compositiondocker_dock.cpp index 8c8147d110..7b7eaacfa7 100644 --- a/plugins/dockers/compositiondocker/compositiondocker_dock.cpp +++ b/plugins/dockers/compositiondocker/compositiondocker_dock.cpp @@ -1,309 +1,309 @@ /* * 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(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")); QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(compositionView); if (scroller) { connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChanged(QScroller::State))); } } 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(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()); + KisImageSP 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/defaultdockers/kis_layer_box.cpp b/plugins/dockers/defaultdockers/kis_layer_box.cpp index 853661293e..fed744d849 100644 --- a/plugins/dockers/defaultdockers/kis_layer_box.cpp +++ b/plugins/dockers/defaultdockers/kis_layer_box.cpp @@ -1,1083 +1,1083 @@ /* * kis_layer_box.cc - part of Krita aka Krayon aka KimageShop * * Copyright (c) 2002 Patrick Julien * Copyright (C) 2006 Gábor Lehel * Copyright (C) 2007 Thomas Zander * Copyright (C) 2007 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_layer_box.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 "kis_action.h" #include "kis_action_manager.h" #include "widgets/kis_cmb_composite.h" #include "widgets/kis_slider_spin_box.h" #include "KisViewManager.h" #include "kis_node_manager.h" #include "kis_node_model.h" #include "canvas/kis_canvas2.h" #include "kis_dummies_facade_base.h" #include "kis_shape_controller.h" #include "kis_selection_mask.h" #include "kis_config.h" #include "KisView.h" #include "krita_utils.h" #include "sync_button_and_action.h" #include "kis_color_label_selector_widget.h" #include "kis_signals_blocker.h" #include "kis_color_filter_combo.h" #include "kis_node_filter_proxy_model.h" #include "kis_selection.h" #include "kis_processing_applicator.h" #include "commands/kis_set_global_selection_command.h" #include "KisSelectionActionsAdapter.h" #include "kis_layer_utils.h" #include "ui_wdglayerbox.h" #include class KisLayerBoxStyle : public QProxyStyle { public: KisLayerBoxStyle(QStyle *baseStyle = 0) : QProxyStyle(baseStyle) {} void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { if (element == QStyle::PE_IndicatorItemViewItemDrop) { QColor color(widget->palette().color(QPalette::Highlight).lighter()); if (option->rect.height() == 0) { QBrush brush(color); QRect r(option->rect); r.setTop(r.top() - 2); r.setBottom(r.bottom() + 2); painter->fillRect(r, brush); } else { color.setAlpha(200); QBrush brush(color); painter->fillRect(option->rect, brush); } } else { QProxyStyle::drawPrimitive(element, option, painter, widget); } } }; inline void KisLayerBox::connectActionToButton(KisViewManager* viewManager, QAbstractButton *button, const QString &id) { if (!viewManager || !button) return; KisAction *action = viewManager->actionManager()->actionByName(id); if (!action) return; connect(button, SIGNAL(clicked()), action, SLOT(trigger())); connect(action, SIGNAL(sigEnableSlaves(bool)), button, SLOT(setEnabled(bool))); connect(viewManager->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateIcons())); } inline void KisLayerBox::addActionToMenu(QMenu *menu, const QString &id) { if (m_canvas) { menu->addAction(m_canvas->viewManager()->actionManager()->actionByName(id)); } } KisLayerBox::KisLayerBox() : QDockWidget(i18n("Layers")) , m_canvas(0) , m_wdgLayerBox(new Ui_WdgLayerBox) , m_thumbnailCompressor(500, KisSignalCompressor::FIRST_INACTIVE) , m_colorLabelCompressor(900, KisSignalCompressor::FIRST_INACTIVE) , m_thumbnailSizeCompressor(100, KisSignalCompressor::FIRST_INACTIVE) { KisConfig cfg(false); QWidget* mainWidget = new QWidget(this); setWidget(mainWidget); m_opacityDelayTimer.setSingleShot(true); m_wdgLayerBox->setupUi(mainWidget); m_wdgLayerBox->listLayers->setStyle(new KisLayerBoxStyle(m_wdgLayerBox->listLayers->style())); connect(m_wdgLayerBox->listLayers, SIGNAL(contextMenuRequested(QPoint,QModelIndex)), this, SLOT(slotContextMenuRequested(QPoint,QModelIndex))); connect(m_wdgLayerBox->listLayers, SIGNAL(collapsed(QModelIndex)), SLOT(slotCollapsed(QModelIndex))); connect(m_wdgLayerBox->listLayers, SIGNAL(expanded(QModelIndex)), SLOT(slotExpanded(QModelIndex))); connect(m_wdgLayerBox->listLayers, SIGNAL(selectionChanged(QModelIndexList)), SLOT(selectionChanged(QModelIndexList))); slotUpdateIcons(); m_wdgLayerBox->bnDelete->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnRaise->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnLower->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnProperties->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnDuplicate->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnLower->setEnabled(false); m_wdgLayerBox->bnRaise->setEnabled(false); if (cfg.sliderLabels()) { m_wdgLayerBox->opacityLabel->hide(); m_wdgLayerBox->doubleOpacity->setPrefix(QString("%1: ").arg(i18n("Opacity"))); } m_wdgLayerBox->doubleOpacity->setRange(0, 100, 0); m_wdgLayerBox->doubleOpacity->setSuffix("%"); connect(m_wdgLayerBox->doubleOpacity, SIGNAL(valueChanged(qreal)), SLOT(slotOpacitySliderMoved(qreal))); connect(&m_opacityDelayTimer, SIGNAL(timeout()), SLOT(slotOpacityChanged())); connect(m_wdgLayerBox->cmbComposite, SIGNAL(activated(int)), SLOT(slotCompositeOpChanged(int))); m_newLayerMenu = new QMenu(this); m_wdgLayerBox->bnAdd->setMenu(m_newLayerMenu); m_wdgLayerBox->bnAdd->setPopupMode(QToolButton::MenuButtonPopup); m_nodeModel = new KisNodeModel(this); m_filteringModel = new KisNodeFilterProxyModel(this); m_filteringModel->setNodeModel(m_nodeModel); /** * Connect model updateUI() to enable/disable controls. * Note: nodeActivated() is connected separately in setImage(), because * it needs particular order of calls: first the connection to the * node manager should be called, then updateUI() */ connect(m_nodeModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(modelReset()), SLOT(slotModelReset())); KisAction *showGlobalSelectionMask = new KisAction(i18n("&Show Global Selection Mask"), this); showGlobalSelectionMask->setObjectName("show-global-selection-mask"); showGlobalSelectionMask->setActivationFlags(KisAction::ACTIVE_IMAGE); showGlobalSelectionMask->setToolTip(i18nc("@info:tooltip", "Shows global selection as a usual selection mask in Layers docker")); showGlobalSelectionMask->setCheckable(true); connect(showGlobalSelectionMask, SIGNAL(triggered(bool)), SLOT(slotEditGlobalSelection(bool))); m_actions.append(showGlobalSelectionMask); showGlobalSelectionMask->setChecked(cfg.showGlobalSelection()); m_colorSelector = new KisColorLabelSelectorWidget(this); connect(m_colorSelector, SIGNAL(currentIndexChanged(int)), SLOT(slotColorLabelChanged(int))); m_colorSelectorAction = new QWidgetAction(this); m_colorSelectorAction->setDefaultWidget(m_colorSelector); connect(m_nodeModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), &m_colorLabelCompressor, SLOT(start())); m_wdgLayerBox->listLayers->setModel(m_filteringModel); // this connection should be done *after* the setModel() call to // happen later than the internal selection model connect(m_filteringModel.data(), &KisNodeFilterProxyModel::rowsAboutToBeRemoved, this, &KisLayerBox::slotAboutToRemoveRows); connect(m_wdgLayerBox->cmbFilter, SIGNAL(selectedColorsChanged()), SLOT(updateLayerFiltering())); setEnabled(false); connect(&m_thumbnailCompressor, SIGNAL(timeout()), SLOT(updateThumbnail())); connect(&m_colorLabelCompressor, SIGNAL(timeout()), SLOT(updateAvailableLabels())); // set up the configure menu for changing thumbnail size QMenu* configureMenu = new QMenu(this); configureMenu->setStyleSheet("margin: 6px"); configureMenu->addSection(i18n("Thumbnail Size")); m_wdgLayerBox->configureLayerDockerToolbar->setMenu(configureMenu); m_wdgLayerBox->configureLayerDockerToolbar->setIcon(KisIconUtils::loadIcon("configure")); m_wdgLayerBox->configureLayerDockerToolbar->setIconSize(QSize(13, 13)); m_wdgLayerBox->configureLayerDockerToolbar->setPopupMode(QToolButton::InstantPopup); // add horizontal slider thumbnailSizeSlider = new QSlider(this); thumbnailSizeSlider->setOrientation(Qt::Horizontal); thumbnailSizeSlider->setRange(20, 80); thumbnailSizeSlider->setValue(cfg.layerThumbnailSize(false)); // grab this from the kritarc thumbnailSizeSlider->setMinimumHeight(20); thumbnailSizeSlider->setMinimumWidth(40); thumbnailSizeSlider->setTickInterval(5); QWidgetAction *sliderAction= new QWidgetAction(this); sliderAction->setDefaultWidget(thumbnailSizeSlider); configureMenu->addAction(sliderAction); connect(thumbnailSizeSlider, SIGNAL(sliderMoved(int)), &m_thumbnailSizeCompressor, SLOT(start())); connect(&m_thumbnailSizeCompressor, SIGNAL(timeout()), SLOT(slotUpdateThumbnailIconSize())); } KisLayerBox::~KisLayerBox() { delete m_wdgLayerBox; } void expandNodesRecursively(KisNodeSP root, QPointer filteringModel, KisNodeView *nodeView) { if (!root) return; if (filteringModel.isNull()) return; if (!nodeView) return; nodeView->blockSignals(true); KisNodeSP node = root->firstChild(); while (node) { QModelIndex idx = filteringModel->indexFromNode(node); if (idx.isValid()) { nodeView->setExpanded(idx, !node->collapsed()); } - if (node->childCount() > 0) { + if (!node->collapsed() && node->childCount() > 0) { expandNodesRecursively(node, filteringModel, nodeView); } node = node->nextSibling(); } nodeView->blockSignals(false); } void KisLayerBox::slotAddLayerBnClicked() { if (m_canvas) { KisNodeList nodes = m_nodeManager->selectedNodes(); if (nodes.size() == 1) { KisAction *action = m_canvas->viewManager()->actionManager()->actionByName("add_new_paint_layer"); action->trigger(); } else { KisAction *action = m_canvas->viewManager()->actionManager()->actionByName("create_quick_group"); action->trigger(); } } } void KisLayerBox::setViewManager(KisViewManager* kisview) { m_nodeManager = kisview->nodeManager(); Q_FOREACH (KisAction *action, m_actions) { kisview->actionManager()-> addAction(action->objectName(), action); } connect(m_wdgLayerBox->bnAdd, SIGNAL(clicked()), this, SLOT(slotAddLayerBnClicked())); connectActionToButton(kisview, m_wdgLayerBox->bnDuplicate, "duplicatelayer"); KisActionManager *actionManager = kisview->actionManager(); KisAction *action = actionManager->createAction("RenameCurrentLayer"); Q_ASSERT(action); connect(action, SIGNAL(triggered()), this, SLOT(slotRenameCurrentNode())); m_propertiesAction = actionManager->createAction("layer_properties"); Q_ASSERT(m_propertiesAction); new SyncButtonAndAction(m_propertiesAction, m_wdgLayerBox->bnProperties, this); connect(m_propertiesAction, SIGNAL(triggered()), this, SLOT(slotPropertiesClicked())); m_removeAction = actionManager->createAction("remove_layer"); Q_ASSERT(m_removeAction); new SyncButtonAndAction(m_removeAction, m_wdgLayerBox->bnDelete, this); connect(m_removeAction, SIGNAL(triggered()), this, SLOT(slotRmClicked())); action = actionManager->createAction("move_layer_up"); Q_ASSERT(action); new SyncButtonAndAction(action, m_wdgLayerBox->bnRaise, this); connect(action, SIGNAL(triggered()), this, SLOT(slotRaiseClicked())); action = actionManager->createAction("move_layer_down"); Q_ASSERT(action); new SyncButtonAndAction(action, m_wdgLayerBox->bnLower, this); connect(action, SIGNAL(triggered()), this, SLOT(slotLowerClicked())); } void KisLayerBox::setCanvas(KoCanvasBase *canvas) { if (m_canvas == canvas) return; setEnabled(canvas != 0); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); m_nodeModel->setDummiesFacade(0, 0, 0, 0, 0, 0, 0); m_selectionActionsAdapter.reset(); if (m_image) { KisImageAnimationInterface *animation = m_image->animationInterface(); animation->disconnect(this); } disconnect(m_image, 0, this, 0); disconnect(m_nodeManager, 0, this, 0); disconnect(m_nodeModel, 0, m_nodeManager, 0); m_nodeManager->slotSetSelectedNodes(KisNodeList()); } m_canvas = dynamic_cast(canvas); if (m_canvas) { m_image = m_canvas->image(); connect(m_image, SIGNAL(sigImageUpdated(QRect)), &m_thumbnailCompressor, SLOT(start())); KisDocument* doc = static_cast(m_canvas->imageView()->document()); KisShapeController *kritaShapeController = dynamic_cast(doc->shapeController()); KisDummiesFacadeBase *kritaDummiesFacade = static_cast(kritaShapeController); m_selectionActionsAdapter.reset(new KisSelectionActionsAdapter(m_canvas->viewManager()->selectionManager())); m_nodeModel->setDummiesFacade(kritaDummiesFacade, m_image, kritaShapeController, m_nodeManager->nodeSelectionAdapter(), m_nodeManager->nodeInsertionAdapter(), m_selectionActionsAdapter.data(), m_nodeManager->nodeDisplayModeAdapter()); connect(m_image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted())); connect(m_image, SIGNAL(sigNodeCollapsedChanged()), SLOT(slotNodeCollapsedChanged())); // cold start if (m_nodeManager) { setCurrentNode(m_nodeManager->activeNode()); // Connection KisNodeManager -> KisLayerBox connect(m_nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)), this, SLOT(setCurrentNode(KisNodeSP))); connect(m_nodeManager, SIGNAL(sigUiNeedChangeSelectedNodes(QList)), SLOT(slotNodeManagerChangedSelection(QList))); } else { setCurrentNode(m_canvas->imageView()->currentNode()); } // Connection KisLayerBox -> KisNodeManager (isolate layer) connect(m_nodeModel, SIGNAL(toggleIsolateActiveNode()), m_nodeManager, SLOT(toggleIsolateActiveNode())); KisImageAnimationInterface *animation = m_image->animationInterface(); connect(animation, &KisImageAnimationInterface::sigUiTimeChanged, this, &KisLayerBox::slotImageTimeChanged); expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers); m_wdgLayerBox->listLayers->scrollTo(m_wdgLayerBox->listLayers->currentIndex()); updateAvailableLabels(); addActionToMenu(m_newLayerMenu, "add_new_paint_layer"); addActionToMenu(m_newLayerMenu, "add_new_group_layer"); addActionToMenu(m_newLayerMenu, "add_new_clone_layer"); addActionToMenu(m_newLayerMenu, "add_new_shape_layer"); addActionToMenu(m_newLayerMenu, "add_new_adjustment_layer"); addActionToMenu(m_newLayerMenu, "add_new_fill_layer"); addActionToMenu(m_newLayerMenu, "add_new_file_layer"); m_newLayerMenu->addSeparator(); addActionToMenu(m_newLayerMenu, "add_new_transparency_mask"); addActionToMenu(m_newLayerMenu, "add_new_filter_mask"); addActionToMenu(m_newLayerMenu, "add_new_colorize_mask"); addActionToMenu(m_newLayerMenu, "add_new_transform_mask"); addActionToMenu(m_newLayerMenu, "add_new_selection_mask"); } } void KisLayerBox::unsetCanvas() { setEnabled(false); if (m_canvas) { m_newLayerMenu->clear(); } m_filteringModel->unsetDummiesFacade(); disconnect(m_image, 0, this, 0); disconnect(m_nodeManager, 0, this, 0); disconnect(m_nodeModel, 0, m_nodeManager, 0); m_nodeManager->slotSetSelectedNodes(KisNodeList()); m_canvas = 0; } void KisLayerBox::notifyImageDeleted() { setCanvas(0); } void KisLayerBox::updateUI() { if (!m_canvas) return; if (!m_nodeManager) return; KisNodeSP activeNode = m_nodeManager->activeNode(); if (activeNode != m_activeNode) { if( !m_activeNode.isNull() ) m_activeNode->disconnect(this); m_activeNode = activeNode; if (activeNode) { KisKeyframeChannel *opacityChannel = activeNode->getKeyframeChannel(KisKeyframeChannel::Opacity.id(), false); if (opacityChannel) { watchOpacityChannel(opacityChannel); } else { watchOpacityChannel(0); connect(activeNode.data(), &KisNode::keyframeChannelAdded, this, &KisLayerBox::slotKeyframeChannelAdded); } } } m_wdgLayerBox->bnRaise->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->nextSibling() || (activeNode->parent() && activeNode->parent() != m_image->root()))); m_wdgLayerBox->bnLower->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->prevSibling() || (activeNode->parent() && activeNode->parent() != m_image->root()))); m_wdgLayerBox->doubleOpacity->setEnabled(activeNode && activeNode->isEditable(false)); m_wdgLayerBox->cmbComposite->setEnabled(activeNode && activeNode->isEditable(false)); m_wdgLayerBox->cmbComposite->validate(m_image->colorSpace()); if (activeNode) { if (activeNode->inherits("KisColorizeMask") || activeNode->inherits("KisLayer")) { m_wdgLayerBox->doubleOpacity->setEnabled(true); slotSetOpacity(activeNode->opacity() * 100.0 / 255); const KoCompositeOp* compositeOp = activeNode->compositeOp(); if (compositeOp) { slotSetCompositeOp(compositeOp); } else { m_wdgLayerBox->cmbComposite->setEnabled(false); } const KisGroupLayer *group = qobject_cast(activeNode.data()); bool compositeSelectionActive = !(group && group->passThroughMode()); m_wdgLayerBox->cmbComposite->setEnabled(compositeSelectionActive); } else if (activeNode->inherits("KisMask")) { m_wdgLayerBox->cmbComposite->setEnabled(false); m_wdgLayerBox->doubleOpacity->setEnabled(false); } } } /** * This method is called *only* when non-GUI code requested the * change of the current node */ void KisLayerBox::setCurrentNode(KisNodeSP node) { m_filteringModel->setActiveNode(node); QModelIndex index = node ? m_filteringModel->indexFromNode(node) : QModelIndex(); m_filteringModel->setData(index, true, KisNodeModel::ActiveRole); updateUI(); } void KisLayerBox::slotModelReset() { if(m_nodeModel->hasDummiesFacade()) { QItemSelection selection; Q_FOREACH (const KisNodeSP node, m_nodeManager->selectedNodes()) { const QModelIndex &idx = m_filteringModel->indexFromNode(node); if(idx.isValid()){ QItemSelectionRange selectionRange(idx); selection << selectionRange; } } m_wdgLayerBox->listLayers->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect); } updateUI(); } void KisLayerBox::slotSetCompositeOp(const KoCompositeOp* compositeOp) { KoID opId = KoCompositeOpRegistry::instance().getKoID(compositeOp->id()); m_wdgLayerBox->cmbComposite->blockSignals(true); m_wdgLayerBox->cmbComposite->selectCompositeOp(opId); m_wdgLayerBox->cmbComposite->blockSignals(false); } // range: 0-100 void KisLayerBox::slotSetOpacity(double opacity) { Q_ASSERT(opacity >= 0 && opacity <= 100); m_wdgLayerBox->doubleOpacity->blockSignals(true); m_wdgLayerBox->doubleOpacity->setValue(opacity); m_wdgLayerBox->doubleOpacity->blockSignals(false); } void KisLayerBox::slotContextMenuRequested(const QPoint &pos, const QModelIndex &index) { KisNodeList nodes = m_nodeManager->selectedNodes(); KisNodeSP activeNode = m_nodeManager->activeNode(); if (nodes.isEmpty() || !activeNode) return; if (m_canvas) { QMenu menu; const bool singleLayer = nodes.size() == 1; if (index.isValid()) { menu.addAction(m_propertiesAction); if (singleLayer) { addActionToMenu(&menu, "layer_style"); } { KisSignalsBlocker b(m_colorSelector); m_colorSelector->setCurrentIndex(singleLayer ? activeNode->colorLabelIndex() : -1); } menu.addAction(m_colorSelectorAction); menu.addSeparator(); addActionToMenu(&menu, "cut_layer_clipboard"); addActionToMenu(&menu, "copy_layer_clipboard"); addActionToMenu(&menu, "paste_layer_from_clipboard"); menu.addAction(m_removeAction); addActionToMenu(&menu, "duplicatelayer"); addActionToMenu(&menu, "merge_layer"); if (singleLayer) { addActionToMenu(&menu, "flatten_image"); addActionToMenu(&menu, "flatten_layer"); } menu.addSeparator(); QMenu *selectMenu = menu.addMenu(i18n("&Select")); addActionToMenu(selectMenu, "select_all_layers"); addActionToMenu(selectMenu, "select_visible_layers"); addActionToMenu(selectMenu, "select_invisible_layers"); addActionToMenu(selectMenu, "select_locked_layers"); addActionToMenu(selectMenu, "select_unlocked_layers"); QMenu *groupMenu = menu.addMenu(i18n("&Group")); addActionToMenu(groupMenu, "create_quick_group"); addActionToMenu(groupMenu, "create_quick_clipping_group"); addActionToMenu(groupMenu, "quick_ungroup"); QMenu *locksMenu = menu.addMenu(i18n("&Toggle Locks && Visibility")); addActionToMenu(locksMenu, "toggle_layer_visibility"); addActionToMenu(locksMenu, "toggle_layer_lock"); addActionToMenu(locksMenu, "toggle_layer_inherit_alpha"); addActionToMenu(locksMenu, "toggle_layer_alpha_lock"); if (singleLayer) { QMenu *addLayerMenu = menu.addMenu(i18n("&Add")); addActionToMenu(addLayerMenu, "add_new_transparency_mask"); addActionToMenu(addLayerMenu, "add_new_filter_mask"); addActionToMenu(addLayerMenu, "add_new_colorize_mask"); addActionToMenu(addLayerMenu, "add_new_transform_mask"); addActionToMenu(addLayerMenu, "add_new_selection_mask"); QMenu *convertToMenu = menu.addMenu(i18n("&Convert")); addActionToMenu(convertToMenu, "convert_to_paint_layer"); addActionToMenu(convertToMenu, "convert_to_transparency_mask"); addActionToMenu(convertToMenu, "convert_to_filter_mask"); addActionToMenu(convertToMenu, "convert_to_selection_mask"); addActionToMenu(convertToMenu, "convert_to_file_layer"); QMenu *splitAlphaMenu = menu.addMenu(i18n("S&plit Alpha")); addActionToMenu(splitAlphaMenu, "split_alpha_into_mask"); addActionToMenu(splitAlphaMenu, "split_alpha_write"); addActionToMenu(splitAlphaMenu, "split_alpha_save_merged"); } menu.addSeparator(); addActionToMenu(&menu, "show_in_timeline"); if (singleLayer) { KisNodeSP node = m_filteringModel->nodeFromIndex(index); if (node && !node->inherits("KisTransformMask")) { addActionToMenu(&menu, "isolate_layer"); } addActionToMenu(&menu, "selectopaque"); } } menu.exec(pos); } } void KisLayerBox::slotMinimalView() { m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::MinimalMode); } void KisLayerBox::slotDetailedView() { m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::DetailedMode); } void KisLayerBox::slotThumbnailView() { m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::ThumbnailMode); } void KisLayerBox::slotRmClicked() { if (!m_canvas) return; m_nodeManager->removeNode(); } void KisLayerBox::slotRaiseClicked() { if (!m_canvas) return; m_nodeManager->raiseNode(); } void KisLayerBox::slotLowerClicked() { if (!m_canvas) return; m_nodeManager->lowerNode(); } void KisLayerBox::slotPropertiesClicked() { if (!m_canvas) return; if (KisNodeSP active = m_nodeManager->activeNode()) { m_nodeManager->nodeProperties(active); } } void KisLayerBox::slotCompositeOpChanged(int index) { Q_UNUSED(index); if (!m_canvas) return; QString compositeOp = m_wdgLayerBox->cmbComposite->selectedCompositeOp().id(); m_nodeManager->nodeCompositeOpChanged(m_nodeManager->activeColorSpace()->compositeOp(compositeOp)); } void KisLayerBox::slotOpacityChanged() { if (!m_canvas) return; m_blockOpacityUpdate = true; m_nodeManager->nodeOpacityChanged(m_newOpacity, true); m_blockOpacityUpdate = false; } void KisLayerBox::slotOpacitySliderMoved(qreal opacity) { m_newOpacity = opacity; m_opacityDelayTimer.start(200); } void KisLayerBox::slotCollapsed(const QModelIndex &index) { KisNodeSP node = m_filteringModel->nodeFromIndex(index); if (node) { node->setCollapsed(true); } } void KisLayerBox::slotExpanded(const QModelIndex &index) { KisNodeSP node = m_filteringModel->nodeFromIndex(index); if (node) { node->setCollapsed(false); } } void KisLayerBox::slotSelectOpaque() { if (!m_canvas) return; QAction *action = m_canvas->viewManager()->actionManager()->actionByName("selectopaque"); if (action) { action->trigger(); } } void KisLayerBox::slotNodeCollapsedChanged() { expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers); } inline bool isSelectionMask(KisNodeSP node) { return dynamic_cast(node.data()); } KisNodeSP KisLayerBox::findNonHidableNode(KisNodeSP startNode) { if (KisNodeManager::isNodeHidden(startNode, true) && startNode->parent() && !startNode->parent()->parent()) { KisNodeSP node = startNode->prevSibling(); while (node && KisNodeManager::isNodeHidden(node, true)) { node = node->prevSibling(); } if (!node) { node = startNode->nextSibling(); while (node && KisNodeManager::isNodeHidden(node, true)) { node = node->nextSibling(); } } if (!node) { node = m_image->root()->lastChild(); while (node && KisNodeManager::isNodeHidden(node, true)) { node = node->prevSibling(); } } KIS_ASSERT_RECOVER_NOOP(node && "cannot activate any node!"); startNode = node; } return startNode; } void KisLayerBox::slotEditGlobalSelection(bool showSelections) { KisNodeSP lastActiveNode = m_nodeManager->activeNode(); KisNodeSP activateNode = lastActiveNode; KisSelectionMaskSP globalSelectionMask; if (!showSelections) { activateNode = findNonHidableNode(activateNode); } m_nodeModel->setShowGlobalSelection(showSelections); globalSelectionMask = m_image->rootLayer()->selectionMask(); // try to find deactivated, but visible masks if (!globalSelectionMask) { KoProperties properties; properties.setProperty("visible", true); QList masks = m_image->rootLayer()->childNodes(QStringList("KisSelectionMask"), properties); if (!masks.isEmpty()) { globalSelectionMask = dynamic_cast(masks.first().data()); } } // try to find at least any selection mask if (!globalSelectionMask) { KoProperties properties; QList masks = m_image->rootLayer()->childNodes(QStringList("KisSelectionMask"), properties); if (!masks.isEmpty()) { globalSelectionMask = dynamic_cast(masks.first().data()); } } if (globalSelectionMask) { if (showSelections) { activateNode = globalSelectionMask; } } if (activateNode != lastActiveNode) { m_nodeManager->slotNonUiActivatedNode(activateNode); } else if (lastActiveNode) { setCurrentNode(lastActiveNode); } if (showSelections && !globalSelectionMask) { KisProcessingApplicator applicator(m_image, 0, KisProcessingApplicator::NONE, KisImageSignalVector() << ModifiedSignal, kundo2_i18n("Quick Selection Mask")); applicator.applyCommand( new KisLayerUtils::KeepNodesSelectedCommand( m_nodeManager->selectedNodes(), KisNodeList(), lastActiveNode, 0, m_image, false), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.applyCommand(new KisSetEmptyGlobalSelectionCommand(m_image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.applyCommand(new KisLayerUtils::SelectGlobalSelectionMask(m_image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.end(); } else if (!showSelections && globalSelectionMask && globalSelectionMask->selection()->selectedRect().isEmpty()) { KisProcessingApplicator applicator(m_image, 0, KisProcessingApplicator::NONE, KisImageSignalVector() << ModifiedSignal, kundo2_i18n("Cancel Quick Selection Mask")); applicator.applyCommand(new KisSetGlobalSelectionCommand(m_image, 0), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.end(); } } void KisLayerBox::selectionChanged(const QModelIndexList selection) { if (!m_nodeManager) return; /** * When the user clears the extended selection by clicking on the * empty area of the docker, the selection should be reset on to * the active layer, which might be even unselected(!). */ if (selection.isEmpty() && m_nodeManager->activeNode()) { QModelIndex selectedIndex = m_filteringModel->indexFromNode(m_nodeManager->activeNode()); m_wdgLayerBox->listLayers->selectionModel()-> setCurrentIndex(selectedIndex, QItemSelectionModel::ClearAndSelect); return; } QList selectedNodes; Q_FOREACH (const QModelIndex &idx, selection) { selectedNodes << m_filteringModel->nodeFromIndex(idx); } m_nodeManager->slotSetSelectedNodes(selectedNodes); updateUI(); } void KisLayerBox::slotAboutToRemoveRows(const QModelIndex &parent, int start, int end) { /** * Qt has changed its behavior when deleting an item. Previously * the selection priority was on the next item in the list, and * now it has shanged to the previous item. Here we just adjust * the selected item after the node removal. Please take care that * this method overrides what was done by the corresponding method * of QItemSelectionModel, which *has already done* its work. That * is why we use (start - 1) and (end + 1) in the activation * condition. * * See bug: https://bugs.kde.org/show_bug.cgi?id=345601 */ QModelIndex currentIndex = m_wdgLayerBox->listLayers->currentIndex(); QAbstractItemModel *model = m_filteringModel; if (currentIndex.isValid() && parent == currentIndex.parent() && currentIndex.row() >= start - 1 && currentIndex.row() <= end + 1) { QModelIndex old = currentIndex; if (model && end < model->rowCount(parent) - 1) // there are rows left below the change currentIndex = model->index(end + 1, old.column(), parent); else if (start > 0) // there are rows left above the change currentIndex = model->index(start - 1, old.column(), parent); else // there are no rows left in the table currentIndex = QModelIndex(); if (currentIndex.isValid() && currentIndex != old) { m_wdgLayerBox->listLayers->setCurrentIndex(currentIndex); } } } void KisLayerBox::slotNodeManagerChangedSelection(const KisNodeList &nodes) { if (!m_nodeManager) return; QModelIndexList newSelection; Q_FOREACH(KisNodeSP node, nodes) { newSelection << m_filteringModel->indexFromNode(node); } QItemSelectionModel *model = m_wdgLayerBox->listLayers->selectionModel(); if (KritaUtils::compareListsUnordered(newSelection, model->selectedIndexes())) { return; } QItemSelection selection; Q_FOREACH(const QModelIndex &idx, newSelection) { selection.select(idx, idx); } model->select(selection, QItemSelectionModel::ClearAndSelect); } void KisLayerBox::updateThumbnail() { m_wdgLayerBox->listLayers->updateNode(m_wdgLayerBox->listLayers->currentIndex()); } void KisLayerBox::slotRenameCurrentNode() { m_wdgLayerBox->listLayers->edit(m_wdgLayerBox->listLayers->currentIndex()); } void KisLayerBox::slotColorLabelChanged(int label) { KisNodeList nodes = m_nodeManager->selectedNodes(); Q_FOREACH(KisNodeSP node, nodes) { auto applyLabelFunc = [label](KisNodeSP node) { node->setColorLabelIndex(label); }; KisLayerUtils::recursiveApplyNodes(node, applyLabelFunc); } } void KisLayerBox::updateAvailableLabels() { if (!m_image) return; m_wdgLayerBox->cmbFilter->updateAvailableLabels(m_image->root()); } void KisLayerBox::updateLayerFiltering() { m_filteringModel->setAcceptedLabels(m_wdgLayerBox->cmbFilter->selectedColors()); } void KisLayerBox::slotKeyframeChannelAdded(KisKeyframeChannel *channel) { if (channel->id() == KisKeyframeChannel::Opacity.id()) { watchOpacityChannel(channel); } } void KisLayerBox::watchOpacityChannel(KisKeyframeChannel *channel) { if (m_opacityChannel) { m_opacityChannel->disconnect(this); } m_opacityChannel = channel; if (m_opacityChannel) { connect(m_opacityChannel, SIGNAL(sigKeyframeAdded(KisKeyframeSP)), this, SLOT(slotOpacityKeyframeChanged(KisKeyframeSP))); connect(m_opacityChannel, SIGNAL(sigKeyframeRemoved(KisKeyframeSP)), this, SLOT(slotOpacityKeyframeChanged(KisKeyframeSP))); connect(m_opacityChannel, SIGNAL(sigKeyframeMoved(KisKeyframeSP)), this, SLOT(slotOpacityKeyframeMoved(KisKeyframeSP))); connect(m_opacityChannel, SIGNAL(sigKeyframeChanged(KisKeyframeSP)), this, SLOT(slotOpacityKeyframeChanged(KisKeyframeSP))); } } void KisLayerBox::slotOpacityKeyframeChanged(KisKeyframeSP keyframe) { Q_UNUSED(keyframe); if (m_blockOpacityUpdate) return; updateUI(); } void KisLayerBox::slotOpacityKeyframeMoved(KisKeyframeSP keyframe, int fromTime) { Q_UNUSED(fromTime); slotOpacityKeyframeChanged(keyframe); } void KisLayerBox::slotImageTimeChanged(int time) { Q_UNUSED(time); updateUI(); } void KisLayerBox::slotUpdateIcons() { m_wdgLayerBox->bnAdd->setIcon(KisIconUtils::loadIcon("addlayer")); m_wdgLayerBox->bnRaise->setIcon(KisIconUtils::loadIcon("arrowupblr")); m_wdgLayerBox->bnDelete->setIcon(KisIconUtils::loadIcon("deletelayer")); m_wdgLayerBox->bnLower->setIcon(KisIconUtils::loadIcon("arrowdown")); m_wdgLayerBox->bnProperties->setIcon(KisIconUtils::loadIcon("properties")); m_wdgLayerBox->bnDuplicate->setIcon(KisIconUtils::loadIcon("duplicatelayer")); // call child function about needing to update icons m_wdgLayerBox->listLayers->slotUpdateIcons(); } void KisLayerBox::slotUpdateThumbnailIconSize() { KisConfig cfg(false); cfg.setLayerThumbnailSize(thumbnailSizeSlider->value()); // this is a hack to force the layers list to update its display and // re-layout all the layers with the new thumbnail size resize(this->width()+1, this->height()+1); resize(this->width()-1, this->height()-1); } #include "moc_kis_layer_box.cpp" diff --git a/plugins/extensions/colorrange/dlg_colorrange.cc b/plugins/extensions/colorrange/dlg_colorrange.cc index 755856120d..2ab9836660 100644 --- a/plugins/extensions/colorrange/dlg_colorrange.cc +++ b/plugins/extensions/colorrange/dlg_colorrange.cc @@ -1,274 +1,275 @@ /* * dlg_colorrange.cc - part of KimageShop^WKrayon^WKrita * * Copyright (c) 2004 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 "dlg_colorrange.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 "kis_iterator_ng.h" #include "kis_selection_tool_helper.h" #include DlgColorRange::DlgColorRange(KisViewManager *viewManager, QWidget *parent) : KoDialog(parent) , m_selectionCommandsAdded(0) , m_viewManager(viewManager) { setCaption(i18n("Color Range")); setButtons(Ok | Cancel); setDefaultButton(Ok); m_page = new WdgColorRange(this); Q_CHECK_PTR(m_page); m_page->setObjectName("color_range"); setCaption(i18n("Color Range")); setMainWidget(m_page); resize(m_page->sizeHint()); m_page->intFuzziness->setObjectName("fuzziness"); m_page->intFuzziness->setRange(0, 200); m_page->intFuzziness->setSingleStep(10); m_page->intFuzziness->setValue(100); m_invert = false; m_mode = SELECTION_ADD; m_currentAction = REDS; connect(this, SIGNAL(okClicked()), this, SLOT(okClicked())); connect(this, SIGNAL(cancelClicked()), this, SLOT(cancelClicked())); connect(m_page->chkInvert, SIGNAL(clicked()), this, SLOT(slotInvertClicked())); connect(m_page->cmbSelect, SIGNAL(activated(int)), this, SLOT(slotSelectionTypeChanged(int))); connect(m_page->radioAdd, SIGNAL(toggled(bool)), this, SLOT(slotAdd(bool))); connect(m_page->radioSubtract, SIGNAL(toggled(bool)), this, SLOT(slotSubtract(bool))); connect(m_page->bnSelect, SIGNAL(clicked()), this, SLOT(slotSelectClicked())); connect(m_page->bnDeselect, SIGNAL(clicked()), this, SLOT(slotDeselectClicked())); m_page->bnDeselect->setEnabled(false); } DlgColorRange::~DlgColorRange() { delete m_page; } void DlgColorRange::okClicked() { accept(); } void DlgColorRange::cancelClicked() { if (!m_viewManager) return; if (!m_viewManager->image()) return; for (int i = 0; i < m_selectionCommandsAdded; i++) { m_viewManager->undoAdapter()->undoLastCommand(); } m_viewManager->canvas()->update(); reject(); } void DlgColorRange::slotInvertClicked() { m_invert = m_page->chkInvert->isChecked(); } void DlgColorRange::slotSelectionTypeChanged(int index) { m_currentAction = (enumAction)index; } void DlgColorRange::slotSubtract(bool on) { if (on) m_mode = SELECTION_SUBTRACT; } void DlgColorRange::slotAdd(bool on) { if (on) m_mode = SELECTION_ADD; } void DlgColorRange::slotSelectClicked() { KisPaintDeviceSP device = m_viewManager->activeDevice(); KIS_ASSERT_RECOVER_RETURN(device); - QRect rc = device->exactBounds(); + QRect rc = m_viewManager->image()->bounds(); + if (rc.isEmpty()) return; QApplication::setOverrideCursor(KisCursor::waitCursor()); qint32 x, y, w, h; rc.getRect(&x, &y, &w, &h); const KoColorSpace *cs = m_viewManager->activeDevice()->colorSpace(); const KoColorSpace *lab = KoColorSpaceRegistry::instance()->lab16(); KoColor match; switch (m_currentAction) { case REDS: match = KoColor(QColor(Qt::red), cs); break; case YELLOWS: match = KoColor(QColor(Qt::yellow), cs); break; case GREENS: match = KoColor(QColor(Qt::green), cs); break; case CYANS: match = KoColor(QColor(Qt::cyan), cs); break; case BLUES: match = KoColor(QColor(Qt::blue), cs); break; case MAGENTAS: match = KoColor(QColor(Qt::magenta), cs); break; default: ; }; int fuzziness = m_page->intFuzziness->value(); KisSelectionSP selection = new KisSelection(new KisSelectionDefaultBounds(m_viewManager->activeDevice(), m_viewManager->image())); KisHLineConstIteratorSP hiter = m_viewManager->activeDevice()->createHLineConstIteratorNG(x, y, w); KisHLineIteratorSP selIter = selection->pixelSelection()->createHLineIteratorNG(x, y, w); for (int row = y; row < h - y; ++row) { do { // Don't try to select transparent pixels. if (cs->opacityU8(hiter->oldRawData()) > OPACITY_TRANSPARENT_U8) { bool selected = false; KoColor c(hiter->oldRawData(), cs); if (m_currentAction > MAGENTAS) { c.convertTo(lab); quint8 L = lab->scaleToU8(c.data(), 0); switch (m_currentAction) { case HIGHLIGHTS: selected = (L > MAX_SELECTED - fuzziness); break; case MIDTONES: selected = (L > MAX_SELECTED / 2 - fuzziness && L < MAX_SELECTED / 2 + fuzziness); break; case SHADOWS: selected = (L < MIN_SELECTED + fuzziness); break; default: ; } } else { quint8 difference = cs->difference(match.data(), c.data()); selected = (difference <= fuzziness); } if (selected) { if (!m_invert) { if (m_mode == SELECTION_ADD) { *(selIter->rawData()) = MAX_SELECTED; } else if (m_mode == SELECTION_SUBTRACT) { *(selIter->rawData()) = MIN_SELECTED; } } else { if (m_mode == SELECTION_ADD) { *(selIter->rawData()) = MIN_SELECTED; } else if (m_mode == SELECTION_SUBTRACT) { *(selIter->rawData()) = MAX_SELECTED; } } } } } while (hiter->nextPixel() && selIter->nextPixel()); hiter->nextRow(); selIter->nextRow(); } selection->pixelSelection()->invalidateOutlineCache(); KisSelectionToolHelper helper(m_viewManager->canvasBase(), kundo2_i18n("Color Range Selection")); helper.selectPixelSelection(selection->pixelSelection(), m_mode); m_page->bnDeselect->setEnabled(true); m_selectionCommandsAdded++; QApplication::restoreOverrideCursor(); } void DlgColorRange::slotDeselectClicked() { if (!m_viewManager) return; m_viewManager->undoAdapter()->undoLastCommand(); m_selectionCommandsAdded--; if (!m_selectionCommandsAdded) { m_page->bnDeselect->setEnabled(false); } } diff --git a/plugins/flake/artistictextshape/ArtisticTextTool.cpp b/plugins/flake/artistictextshape/ArtisticTextTool.cpp index ca278a084c..5f8d81e4c2 100644 --- a/plugins/flake/artistictextshape/ArtisticTextTool.cpp +++ b/plugins/flake/artistictextshape/ArtisticTextTool.cpp @@ -1,1007 +1,1007 @@ /* This file is part of the KDE project * Copyright (C) 2007,2011 Jan Hambrecht * Copyright (C) 2008 Rob Buis * * 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 "ArtisticTextTool.h" #include "ArtisticTextToolSelection.h" #include "AttachTextToPathCommand.h" #include "DetachTextFromPathCommand.h" #include "AddTextRangeCommand.h" #include "RemoveTextRangeCommand.h" #include "ArtisticTextShapeConfigWidget.h" #include "ArtisticTextShapeOnPathWidget.h" #include "MoveStartOffsetStrategy.h" #include "SelectTextStrategy.h" #include "ChangeTextOffsetCommand.h" #include "ChangeTextFontCommand.h" #include "ChangeTextAnchorCommand.h" #include "ReplaceTextRangeCommand.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_action_registry.h" #include #include #include #include #include #include #include #include #include #include #include #include const int BlinkInterval = 500; static bool hit(const QKeySequence &input, KStandardShortcut::StandardShortcut shortcut) { foreach (const QKeySequence &ks, KStandardShortcut::shortcut(shortcut)) { if (input == ks) { return true; } } return false; } ArtisticTextTool::ArtisticTextTool(KoCanvasBase *canvas) : KoToolBase(canvas) , m_selection(canvas, this) , m_currentShape(0) , m_hoverText(0) , m_hoverPath(0) , m_hoverHandle(false) , m_textCursor(-1) , m_showCursor(true) , m_currentStrategy(0) { KisActionRegistry *actionRegistry = KisActionRegistry::instance(); m_detachPath = actionRegistry->makeQAction("artistictext_detach_from_path", this); m_detachPath->setEnabled(false); connect(m_detachPath, SIGNAL(triggered()), this, SLOT(detachPath())); - addAction("artistictext_detach_from_path", m_detachPath); +// addAction("artistictext_detach_from_path", m_detachPath); m_convertText = actionRegistry->makeQAction("artistictext_convert_to_path", this); m_convertText->setEnabled(false); connect(m_convertText, SIGNAL(triggered()), this, SLOT(convertText())); - addAction("artistictext_convert_to_path", m_convertText); +// addAction("artistictext_convert_to_path", m_convertText); m_fontBold = actionRegistry->makeQAction("artistictext_font_bold", this); connect(m_fontBold, SIGNAL(toggled(bool)), this, SLOT(toggleFontBold(bool))); - addAction("artistictext_font_bold", m_fontBold); +// addAction("artistictext_font_bold", m_fontBold); m_fontItalic = actionRegistry->makeQAction("artistictext_font_italic", this); connect(m_fontItalic, SIGNAL(toggled(bool)), this, SLOT(toggleFontItalic(bool))); - addAction("artistictext_font_italic", m_fontItalic); +// addAction("artistictext_font_italic", m_fontItalic); m_superScript = actionRegistry->makeQAction("artistictext_superscript", this); connect(m_superScript, SIGNAL(triggered()), this, SLOT(setSuperScript())); - addAction("artistictext_superscript", m_superScript); +// addAction("artistictext_superscript", m_superScript); m_subScript = actionRegistry->makeQAction("artistictext_subscript", this); connect(m_subScript, SIGNAL(triggered()), this, SLOT(setSubScript())); - addAction("artistictext_subscript", m_subScript); +// addAction("artistictext_subscript", m_subScript); QAction *anchorStart = actionRegistry->makeQAction("artistictext_anchor_start", this); anchorStart->setData(ArtisticTextShape::AnchorStart); - addAction("artistictext_anchor_start", anchorStart); +// addAction("artistictext_anchor_start", anchorStart); QAction *anchorMiddle = actionRegistry->makeQAction("artistictext_anchor_middle", this); anchorMiddle->setData(ArtisticTextShape::AnchorMiddle); - addAction("artistictext_anchor_middle", anchorMiddle); +// addAction("artistictext_anchor_middle", anchorMiddle); QAction *anchorEnd = actionRegistry->makeQAction("artistictext_anchor_end", this); anchorEnd->setData(ArtisticTextShape::AnchorEnd); - addAction("artistictext_anchor_end", anchorEnd); +// addAction("artistictext_anchor_end", anchorEnd); m_anchorGroup = new QActionGroup(this); m_anchorGroup->setExclusive(true); m_anchorGroup->addAction(anchorStart); m_anchorGroup->addAction(anchorMiddle); m_anchorGroup->addAction(anchorEnd); connect(m_anchorGroup, SIGNAL(triggered(QAction*)), this, SLOT(anchorChanged(QAction*))); connect(canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), this, SLOT(textChanged())); - addAction("edit_select_all", KStandardAction::selectAll(this, SLOT(selectAll()), this)); - addAction("edit_deselect_all", KStandardAction::deselect(this, SLOT(deselectAll()), this)); +// addAction("edit_select_all", KStandardAction::selectAll(this, SLOT(selectAll()), this)); +// addAction("edit_deselect_all", KStandardAction::deselect(this, SLOT(deselectAll()), this)); setTextMode(true); } ArtisticTextTool::~ArtisticTextTool() { delete m_currentStrategy; } QTransform ArtisticTextTool::cursorTransform() const { if (!m_currentShape) { return QTransform(); } QTransform transform; const int textLength = m_currentShape->plainText().length(); if (m_textCursor <= textLength) { const QPointF pos = m_currentShape->charPositionAt(m_textCursor); const qreal angle = m_currentShape->charAngleAt(m_textCursor); QFontMetrics metrics(m_currentShape->fontAt(m_textCursor)); transform.translate(pos.x() - 1, pos.y()); transform.rotate(360. - angle); transform.translate(0, metrics.descent()); } else if (m_textCursor <= textLength + m_linefeedPositions.size()) { const QPointF pos = m_linefeedPositions.value(m_textCursor - textLength - 1); QFontMetrics metrics(m_currentShape->fontAt(textLength - 1)); transform.translate(pos.x(), pos.y()); transform.translate(0, metrics.descent()); } return transform * m_currentShape->absoluteTransformation(0); } void ArtisticTextTool::paint(QPainter &painter, const KoViewConverter &converter) { if (! m_currentShape) { return; } if (m_showCursor && m_blinkingCursor.isActive() && !m_currentStrategy) { painter.save(); m_currentShape->applyConversion(painter, converter); painter.setBrush(Qt::black); painter.setWorldTransform(cursorTransform(), true); painter.setClipping(false); painter.drawPath(m_textCursorShape); painter.restore(); } m_showCursor = !m_showCursor; if (m_currentShape->isOnPath()) { painter.save(); m_currentShape->applyConversion(painter, converter); if (!m_currentShape->baselineShape()) { painter.setPen(Qt::DotLine); painter.setBrush(Qt::NoBrush); painter.drawPath(m_currentShape->baseline()); } painter.setPen(Qt::blue); painter.setBrush(m_hoverHandle ? Qt::red : Qt::white); painter.drawPath(offsetHandleShape()); painter.restore(); } if (m_selection.hasSelection()) { painter.save(); m_selection.paint(painter, converter); painter.restore(); } } void ArtisticTextTool::repaintDecorations() { canvas()->updateCanvas(offsetHandleShape().boundingRect()); if (m_currentShape && m_currentShape->isOnPath()) { if (!m_currentShape->baselineShape()) { canvas()->updateCanvas(m_currentShape->baseline().boundingRect()); } } m_selection.repaintDecoration(); } int ArtisticTextTool::cursorFromMousePosition(const QPointF &mousePosition) { if (!m_currentShape) { return -1; } const QPointF pos = m_currentShape->documentToShape(mousePosition); const int len = m_currentShape->plainText().length(); int hit = -1; qreal mindist = DBL_MAX; for (int i = 0; i <= len; ++i) { QPointF center = pos - m_currentShape->charPositionAt(i); if ((fabs(center.x()) + fabs(center.y())) < mindist) { hit = i; mindist = fabs(center.x()) + fabs(center.y()); } } return hit; } void ArtisticTextTool::mousePressEvent(KoPointerEvent *event) { if (m_hoverHandle) { m_currentStrategy = new MoveStartOffsetStrategy(this, m_currentShape); } if (m_hoverText) { KoSelection *selection = canvas()->selectedShapesProxy()->selection(); if (m_hoverText != m_currentShape) { // if we hit another text shape, select that shape selection->deselectAll(); setCurrentShape(m_hoverText); selection->select(m_currentShape); } // change the text cursor position int hitCursorPos = cursorFromMousePosition(event->point); if (hitCursorPos >= 0) { setTextCursorInternal(hitCursorPos); m_selection.clear(); } m_currentStrategy = new SelectTextStrategy(this, m_textCursor); } event->ignore(); } void ArtisticTextTool::mouseMoveEvent(KoPointerEvent *event) { m_hoverPath = 0; m_hoverText = 0; if (m_currentStrategy) { m_currentStrategy->handleMouseMove(event->point, event->modifiers()); return; } const bool textOnPath = m_currentShape && m_currentShape->isOnPath(); if (textOnPath) { QPainterPath handle = offsetHandleShape(); QPointF handleCenter = handle.boundingRect().center(); if (handleGrabRect(event->point).contains(handleCenter)) { // mouse is on offset handle if (!m_hoverHandle) { canvas()->updateCanvas(handle.boundingRect()); } m_hoverHandle = true; } else { if (m_hoverHandle) { canvas()->updateCanvas(handle.boundingRect()); } m_hoverHandle = false; } } if (!m_hoverHandle) { // find text or path shape at cursor position QList shapes = canvas()->shapeManager()->shapesAt(handleGrabRect(event->point)); if (shapes.contains(m_currentShape)) { m_hoverText = m_currentShape; } else { Q_FOREACH (KoShape *shape, shapes) { ArtisticTextShape *text = dynamic_cast(shape); if (text && !m_hoverText) { m_hoverText = text; } KoPathShape *path = dynamic_cast(shape); if (path && !m_hoverPath) { m_hoverPath = path; } if (m_hoverPath && m_hoverText) { break; } } } } const bool hoverOnBaseline = textOnPath && m_currentShape && m_currentShape->baselineShape() == m_hoverPath; // update cursor and status text if (m_hoverText) { useCursor(QCursor(Qt::IBeamCursor)); if (m_hoverText == m_currentShape) { emit statusTextChanged(i18n("Click to change cursor position.")); } else { emit statusTextChanged(i18n("Click to select text shape.")); } } else if (m_hoverPath && m_currentShape && !hoverOnBaseline) { useCursor(QCursor(Qt::PointingHandCursor)); emit statusTextChanged(i18n("Double click to put text on path.")); } else if (m_hoverHandle) { useCursor(QCursor(Qt::ArrowCursor)); emit statusTextChanged(i18n("Drag handle to change start offset.")); } else { useCursor(QCursor(Qt::ArrowCursor)); if (m_currentShape) { emit statusTextChanged(i18n("Press escape to finish editing.")); } else { emit statusTextChanged(QString()); } } } void ArtisticTextTool::mouseReleaseEvent(KoPointerEvent *event) { if (m_currentStrategy) { m_currentStrategy->finishInteraction(event->modifiers()); KUndo2Command *cmd = m_currentStrategy->createCommand(); if (cmd) { canvas()->addCommand(cmd); } delete m_currentStrategy; m_currentStrategy = 0; } updateActions(); } void ArtisticTextTool::shortcutOverrideEvent(QKeyEvent *event) { QKeySequence item(event->key() | ((Qt::ControlModifier | Qt::AltModifier) & event->modifiers())); if (hit(item, KStandardShortcut::Begin) || hit(item, KStandardShortcut::End)) { event->accept(); } } void ArtisticTextTool::mouseDoubleClickEvent(KoPointerEvent */*event*/) { if (m_hoverPath && m_currentShape) { if (!m_currentShape->isOnPath() || m_currentShape->baselineShape() != m_hoverPath) { m_blinkingCursor.stop(); m_showCursor = false; updateTextCursorArea(); canvas()->addCommand(new AttachTextToPathCommand(m_currentShape, m_hoverPath)); m_blinkingCursor.start(BlinkInterval); updateActions(); m_hoverPath = 0; m_linefeedPositions.clear(); return; } } } void ArtisticTextTool::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { event->ignore(); return; } event->accept(); if (m_currentShape && textCursor() > -1) { switch (event->key()) { case Qt::Key_Delete: if (m_selection.hasSelection()) { removeFromTextCursor(m_selection.selectionStart(), m_selection.selectionCount()); } else if (textCursor() >= 0 && textCursor() < m_currentShape->plainText().length()) { removeFromTextCursor(textCursor(), 1); } break; case Qt::Key_Backspace: if (m_selection.hasSelection()) { removeFromTextCursor(m_selection.selectionStart(), m_selection.selectionCount()); } else { removeFromTextCursor(textCursor() - 1, 1); } break; case Qt::Key_Right: if (event->modifiers() & Qt::ShiftModifier) { int selectionStart, selectionEnd; if (m_selection.hasSelection()) { selectionStart = m_selection.selectionStart(); selectionEnd = selectionStart + m_selection.selectionCount(); if (textCursor() == selectionStart) { selectionStart = textCursor() + 1; } else if (textCursor() == selectionEnd) { selectionEnd = textCursor() + 1; } } else { selectionStart = textCursor(); selectionEnd = textCursor() + 1; } m_selection.selectText(selectionStart, selectionEnd); } else { m_selection.clear(); } setTextCursor(m_currentShape, textCursor() + 1); break; case Qt::Key_Left: if (event->modifiers() & Qt::ShiftModifier) { int selectionStart, selectionEnd; if (m_selection.hasSelection()) { selectionStart = m_selection.selectionStart(); selectionEnd = selectionStart + m_selection.selectionCount(); if (textCursor() == selectionStart) { selectionStart = textCursor() - 1; } else if (textCursor() == selectionEnd) { selectionEnd = textCursor() - 1; } } else { selectionEnd = textCursor(); selectionStart = textCursor() - 1; } m_selection.selectText(selectionStart, selectionEnd); } else { m_selection.clear(); } setTextCursor(m_currentShape, textCursor() - 1); break; case Qt::Key_Home: if (event->modifiers() & Qt::ShiftModifier) { const int selectionStart = 0; const int selectionEnd = m_selection.hasSelection() ? m_selection.selectionStart() + m_selection.selectionCount() : m_textCursor; m_selection.selectText(selectionStart, selectionEnd); } else { m_selection.clear(); } setTextCursor(m_currentShape, 0); break; case Qt::Key_End: if (event->modifiers() & Qt::ShiftModifier) { const int selectionStart = m_selection.hasSelection() ? m_selection.selectionStart() : m_textCursor; const int selectionEnd = m_currentShape->plainText().length(); m_selection.selectText(selectionStart, selectionEnd); } else { m_selection.clear(); } setTextCursor(m_currentShape, m_currentShape->plainText().length()); break; case Qt::Key_Return: case Qt::Key_Enter: { const int textLength = m_currentShape->plainText().length(); if (m_textCursor >= textLength) { // get font metrics for last character QFontMetrics metrics(m_currentShape->fontAt(textLength - 1)); const qreal offset = m_currentShape->size().height() + (m_linefeedPositions.size() + 1) * metrics.height(); m_linefeedPositions.append(QPointF(0, offset)); setTextCursor(m_currentShape, textCursor() + 1); } break; } default: if (event->text().isEmpty()) { event->ignore(); return; } if (m_selection.hasSelection()) { removeFromTextCursor(m_selection.selectionStart(), m_selection.selectionCount()); } addToTextCursor(event->text()); } } else { event->ignore(); } } void ArtisticTextTool::activate(ToolActivation activation, const QSet &shapes) { KoToolBase::activate(activation, shapes); foreach (KoShape *shape, shapes) { ArtisticTextShape *text = dynamic_cast(shape); if (text) { setCurrentShape(text); break; } } if (!m_currentShape) { // none found emit done(); return; } m_hoverText = 0; m_hoverPath = 0; updateActions(); emit statusTextChanged(i18n("Press return to finish editing.")); repaintDecorations(); connect(canvas()->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(shapeSelectionChanged())); } void ArtisticTextTool::blinkCursor() { updateTextCursorArea(); } void ArtisticTextTool::deactivate() { if (m_currentShape) { if (m_currentShape->plainText().isEmpty()) { canvas()->addCommand(canvas()->shapeController()->removeShape(m_currentShape)); } setCurrentShape(0); } m_hoverPath = 0; m_hoverText = 0; disconnect(canvas()->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(shapeSelectionChanged())); KoToolBase::deactivate(); } void ArtisticTextTool::updateActions() { if (m_currentShape) { const QFont font = m_currentShape->fontAt(textCursor()); const CharIndex index = m_currentShape->indexOfChar(textCursor()); ArtisticTextRange::BaselineShift baselineShift = ArtisticTextRange::None; if (index.first >= 0) { baselineShift = m_currentShape->text().at(index.first).baselineShift(); } m_fontBold->blockSignals(true); m_fontBold->setChecked(font.bold()); m_fontBold->blockSignals(false); m_fontBold->setEnabled(true); m_fontItalic->blockSignals(true); m_fontItalic->setChecked(font.italic()); m_fontItalic->blockSignals(false); m_fontItalic->setEnabled(true); m_detachPath->setEnabled(m_currentShape->isOnPath()); m_convertText->setEnabled(true); m_anchorGroup->blockSignals(true); Q_FOREACH (QAction *action, m_anchorGroup->actions()) { if (action->data().toInt() == m_currentShape->textAnchor()) { action->setChecked(true); } } m_anchorGroup->blockSignals(false); m_anchorGroup->setEnabled(true); m_superScript->blockSignals(true); m_superScript->setChecked(baselineShift == ArtisticTextRange::Super); m_superScript->blockSignals(false); m_subScript->blockSignals(true); m_subScript->setChecked(baselineShift == ArtisticTextRange::Sub); m_subScript->blockSignals(false); m_superScript->setEnabled(true); m_subScript->setEnabled(true); } else { m_detachPath->setEnabled(false); m_convertText->setEnabled(false); m_fontBold->setEnabled(false); m_fontItalic->setEnabled(false); m_anchorGroup->setEnabled(false); m_superScript->setEnabled(false); m_subScript->setEnabled(false); } } void ArtisticTextTool::detachPath() { if (m_currentShape && m_currentShape->isOnPath()) { canvas()->addCommand(new DetachTextFromPathCommand(m_currentShape)); updateActions(); } } void ArtisticTextTool::convertText() { if (! m_currentShape) { return; } KoPathShape *path = KoPathShape::createShapeFromPainterPath(m_currentShape->outline()); path->setZIndex(m_currentShape->zIndex()); path->setStroke(m_currentShape->stroke()); path->setBackground(m_currentShape->background()); path->setTransformation(m_currentShape->transformation()); path->setShapeId(KoPathShapeId); KUndo2Command *cmd = canvas()->shapeController()->addShapeDirect(path, 0); cmd->setText(kundo2_i18n("Convert to Path")); canvas()->shapeController()->removeShape(m_currentShape, cmd); canvas()->addCommand(cmd); emit done(); } QList > ArtisticTextTool::createOptionWidgets() { QList > widgets; ArtisticTextShapeConfigWidget *configWidget = new ArtisticTextShapeConfigWidget(this); configWidget->setObjectName("ArtisticTextConfigWidget"); configWidget->setWindowTitle(i18n("Text Properties")); connect(configWidget, SIGNAL(fontFamilyChanged(QFont)), this, SLOT(setFontFamiliy(QFont))); connect(configWidget, SIGNAL(fontSizeChanged(int)), this, SLOT(setFontSize(int))); connect(this, SIGNAL(shapeSelected()), configWidget, SLOT(updateWidget())); connect(canvas()->selectedShapesProxy(), SIGNAL(selectionContentChanged()), configWidget, SLOT(updateWidget())); widgets.append(configWidget); ArtisticTextShapeOnPathWidget *pathWidget = new ArtisticTextShapeOnPathWidget(this); pathWidget->setObjectName("ArtisticTextPathWidget"); pathWidget->setWindowTitle(i18n("Text On Path")); connect(pathWidget, SIGNAL(offsetChanged(int)), this, SLOT(setStartOffset(int))); connect(this, SIGNAL(shapeSelected()), pathWidget, SLOT(updateWidget())); connect(canvas()->selectedShapesProxy(), SIGNAL(selectionContentChanged()), pathWidget, SLOT(updateWidget())); widgets.append(pathWidget); if (m_currentShape) { pathWidget->updateWidget(); configWidget->updateWidget(); } return widgets; } KoToolSelection *ArtisticTextTool::selection() { return &m_selection; } void ArtisticTextTool::enableTextCursor(bool enable) { if (enable) { if (m_currentShape) { setTextCursorInternal(m_currentShape->plainText().length()); } connect(&m_blinkingCursor, SIGNAL(timeout()), this, SLOT(blinkCursor())); m_blinkingCursor.start(BlinkInterval); } else { m_blinkingCursor.stop(); disconnect(&m_blinkingCursor, SIGNAL(timeout()), this, SLOT(blinkCursor())); setTextCursorInternal(-1); m_showCursor = false; } } void ArtisticTextTool::setTextCursor(ArtisticTextShape *textShape, int textCursor) { if (!m_currentShape || textShape != m_currentShape) { return; } if (m_textCursor == textCursor || textCursor < 0) { return; } const int textLength = m_currentShape->plainText().length() + m_linefeedPositions.size(); if (textCursor > textLength) { return; } setTextCursorInternal(textCursor); } int ArtisticTextTool::textCursor() const { return m_textCursor; } void ArtisticTextTool::updateTextCursorArea() const { if (! m_currentShape || m_textCursor < 0) { return; } QRectF bbox = cursorTransform().mapRect(m_textCursorShape.boundingRect()); canvas()->updateCanvas(bbox); } void ArtisticTextTool::setCurrentShape(ArtisticTextShape *currentShape) { if (m_currentShape == currentShape) { return; } enableTextCursor(false); m_currentShape = currentShape; m_selection.setSelectedShape(m_currentShape); if (m_currentShape) { enableTextCursor(true); } emit shapeSelected(); } void ArtisticTextTool::setTextCursorInternal(int textCursor) { updateTextCursorArea(); m_textCursor = textCursor; createTextCursorShape(); updateTextCursorArea(); updateActions(); emit shapeSelected(); } void ArtisticTextTool::createTextCursorShape() { if (m_textCursor < 0 || ! m_currentShape) { return; } const QRectF extents = m_currentShape->charExtentsAt(m_textCursor); m_textCursorShape = QPainterPath(); m_textCursorShape.addRect(0, 0, 1, -extents.height()); m_textCursorShape.closeSubpath(); } void ArtisticTextTool::removeFromTextCursor(int from, unsigned int count) { if (from >= 0) { if (m_selection.hasSelection()) { // clear selection before text is removed, or else selection will be invalid m_selection.clear(); } KUndo2Command *cmd = new RemoveTextRangeCommand(this, m_currentShape, from, count); canvas()->addCommand(cmd); } } void ArtisticTextTool::addToTextCursor(const QString &str) { if (!str.isEmpty() && m_textCursor > -1) { QString printable; for (int i = 0; i < str.length(); i++) { if (str[i].isPrint()) { printable.append(str[i]); } } unsigned int len = printable.length(); if (len) { const int textLength = m_currentShape->plainText().length(); if (m_textCursor <= textLength) { KUndo2Command *cmd = new AddTextRangeCommand(this, m_currentShape, printable, m_textCursor); canvas()->addCommand(cmd); } else if (m_textCursor <= textLength + m_linefeedPositions.size()) { const QPointF pos = m_linefeedPositions.value(m_textCursor - textLength - 1); ArtisticTextRange newLine(printable, m_currentShape->fontAt(textLength - 1)); newLine.setXOffsets(QList() << pos.x(), ArtisticTextRange::AbsoluteOffset); newLine.setYOffsets(QList() << pos.y() - m_currentShape->baselineOffset(), ArtisticTextRange::AbsoluteOffset); KUndo2Command *cmd = new AddTextRangeCommand(this, m_currentShape, newLine, m_textCursor); canvas()->addCommand(cmd); m_linefeedPositions.clear(); } } } } void ArtisticTextTool::textChanged() { if (!m_currentShape) { return; } const QString currentText = m_currentShape->plainText(); if (m_textCursor > currentText.length()) { setTextCursorInternal(currentText.length()); } } void ArtisticTextTool::shapeSelectionChanged() { KoSelection *selection = canvas()->selectedShapesProxy()->selection(); if (selection->isSelected(m_currentShape)) { return; } foreach (KoShape *shape, selection->selectedShapes()) { ArtisticTextShape *text = dynamic_cast(shape); if (text) { setCurrentShape(text); break; } } } QPainterPath ArtisticTextTool::offsetHandleShape() { QPainterPath handle; if (!m_currentShape || !m_currentShape->isOnPath()) { return handle; } const QPainterPath baseline = m_currentShape->baseline(); const qreal offset = m_currentShape->startOffset(); QPointF offsetPoint = baseline.pointAtPercent(offset); QSizeF paintSize = handlePaintRect(QPointF()).size(); handle.moveTo(0, 0); handle.lineTo(0.5 * paintSize.width(), paintSize.height()); handle.lineTo(-0.5 * paintSize.width(), paintSize.height()); handle.closeSubpath(); QTransform transform; transform.translate(offsetPoint.x(), offsetPoint.y()); transform.rotate(360. - baseline.angleAtPercent(offset)); return transform.map(handle); } void ArtisticTextTool::setStartOffset(int offset) { if (!m_currentShape || !m_currentShape->isOnPath()) { return; } const qreal newOffset = static_cast(offset) / 100.0; if (newOffset != m_currentShape->startOffset()) { canvas()->addCommand(new ChangeTextOffsetCommand(m_currentShape, m_currentShape->startOffset(), newOffset)); } } void ArtisticTextTool::changeFontProperty(FontProperty property, const QVariant &value) { if (!m_currentShape || !m_selection.hasSelection()) { return; } // build font ranges const int selectedCharCount = m_selection.selectionCount(); const int selectedCharStart = m_selection.selectionStart(); QList ranges = m_currentShape->text(); CharIndex index = m_currentShape->indexOfChar(selectedCharStart); if (index.first < 0) { return; } KUndo2Command *cmd = new KUndo2Command; int collectedCharCount = 0; while (collectedCharCount < selectedCharCount) { ArtisticTextRange &range = ranges[index.first]; QFont font = range.font(); switch (property) { case BoldProperty: font.setBold(value.toBool()); break; case ItalicProperty: font.setItalic(value.toBool()); break; case FamiliyProperty: font.setFamily(value.toString()); break; case SizeProperty: font.setPointSize(value.toInt()); break; } const int changeCount = qMin(selectedCharCount - collectedCharCount, range.text().count() - index.second); const int changeStart = selectedCharStart + collectedCharCount; new ChangeTextFontCommand(m_currentShape, changeStart, changeCount, font, cmd); index.first++; index.second = 0; collectedCharCount += changeCount; } canvas()->addCommand(cmd); } void ArtisticTextTool::toggleFontBold(bool enabled) { changeFontProperty(BoldProperty, QVariant(enabled)); } void ArtisticTextTool::toggleFontItalic(bool enabled) { changeFontProperty(ItalicProperty, QVariant(enabled)); } void ArtisticTextTool::anchorChanged(QAction *action) { if (!m_currentShape) { return; } ArtisticTextShape::TextAnchor newAnchor = static_cast(action->data().toInt()); if (newAnchor != m_currentShape->textAnchor()) { canvas()->addCommand(new ChangeTextAnchorCommand(m_currentShape, newAnchor)); } } void ArtisticTextTool::setFontFamiliy(const QFont &font) { changeFontProperty(FamiliyProperty, QVariant(font.family())); } void ArtisticTextTool::setFontSize(int size) { changeFontProperty(SizeProperty, QVariant(size)); } void ArtisticTextTool::setSuperScript() { toggleSubSuperScript(ArtisticTextRange::Super); } void ArtisticTextTool::setSubScript() { toggleSubSuperScript(ArtisticTextRange::Sub); } void ArtisticTextTool::toggleSubSuperScript(ArtisticTextRange::BaselineShift mode) { if (!m_currentShape || !m_selection.hasSelection()) { return; } const int from = m_selection.selectionStart(); const int count = m_selection.selectionCount(); QList ranges = m_currentShape->copyText(from, count); const int rangeCount = ranges.count(); if (!rangeCount) { return; } // determine if we want to disable the specified mode const bool disableMode = ranges.first().baselineShift() == mode; const qreal fontSize = m_currentShape->defaultFont().pointSizeF(); for (int i = 0; i < rangeCount; ++i) { ArtisticTextRange ¤tRange = ranges[i]; QFont font = currentRange.font(); if (disableMode) { currentRange.setBaselineShift(ArtisticTextRange::None); font.setPointSizeF(fontSize); } else { currentRange.setBaselineShift(mode); font.setPointSizeF(fontSize * ArtisticTextRange::subAndSuperScriptSizeFactor()); } currentRange.setFont(font); } canvas()->addCommand(new ReplaceTextRangeCommand(m_currentShape, ranges, from, count, this)); } void ArtisticTextTool::selectAll() { if (m_currentShape) { m_selection.selectText(0, m_currentShape->plainText().count()); } } void ArtisticTextTool::deselectAll() { if (m_currentShape) { m_selection.clear(); } } QVariant ArtisticTextTool::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const { if (!m_currentShape) { return QVariant(); } switch (query) { case Qt::ImMicroFocus: { // The rectangle covering the area of the input cursor in widget coordinates. QRectF rect = m_textCursorShape.boundingRect(); rect.moveTop(rect.bottom()); QTransform shapeMatrix = m_currentShape->absoluteTransformation(&converter); qreal zoomX, zoomY; converter.zoom(&zoomX, &zoomY); shapeMatrix.scale(zoomX, zoomY); rect = shapeMatrix.mapRect(rect); return rect.toRect(); } case Qt::ImFont: // The currently used font for text input. return m_currentShape->fontAt(m_textCursor); case Qt::ImCursorPosition: // The logical position of the cursor within the text surrounding the input area (see ImSurroundingText). return m_currentShape->charPositionAt(m_textCursor); case Qt::ImSurroundingText: // The plain text around the input area, for example the current paragraph. return m_currentShape->plainText(); case Qt::ImCurrentSelection: // The currently selected text. return QVariant(); default: ; // Qt 4.6 adds ImMaximumTextLength and ImAnchorPosition } return QVariant(); } diff --git a/plugins/flake/textshape/TextTool.cpp b/plugins/flake/textshape/TextTool.cpp index de076864ec..7733038861 100644 --- a/plugins/flake/textshape/TextTool.cpp +++ b/plugins/flake/textshape/TextTool.cpp @@ -1,3140 +1,3140 @@ /* This file is part of the KDE project * Copyright (C) 2006-2010 Thomas Zander * Copyright (C) 2008 Thorsten Zachmann * Copyright (C) 2008 Girish Ramakrishnan * Copyright (C) 2008, 2012 Pierre Stirnweiss * Copyright (C) 2009 KO GmbH * Copyright (C) 2011 Mojtaba Shahi Senobari * Copyright (C) 2014 Denis Kuplyakov * * 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 "TextTool.h" #include "TextEditingPluginContainer.h" #include "dialogs/SimpleCharacterWidget.h" #include "dialogs/SimpleParagraphWidget.h" #include "dialogs/SimpleTableWidget.h" #include "dialogs/SimpleInsertWidget.h" #include "dialogs/ParagraphSettingsDialog.h" #include "dialogs/StyleManagerDialog.h" #include "dialogs/InsertCharacter.h" #include "dialogs/FontDia.h" #include "dialogs/TableDialog.h" #include "dialogs/SectionFormatDialog.h" #include "dialogs/SectionsSplitDialog.h" #include "commands/AutoResizeCommand.h" #include "commands/ChangeListLevelCommand.h" #include "FontSizeAction.h" #include "FontFamilyAction.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_action_registry.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoShapeControllerBase.h" #include #include #include #include class TextToolSelection : public KoToolSelection { public: TextToolSelection(QWeakPointer editor) : KoToolSelection(0) , m_editor(editor) { } bool hasSelection() override { if (!m_editor.isNull()) { return m_editor.data()->hasSelection(); } return false; } QWeakPointer m_editor; }; static bool hit(const QKeySequence &input, KStandardShortcut::StandardShortcut shortcut) { foreach (const QKeySequence &ks, KStandardShortcut::shortcut(shortcut)) { if (input == ks) { return true; } } return false; } TextTool::TextTool(KoCanvasBase *canvas) : KoToolBase(canvas) , m_textShape(0) , m_textShapeData(0) , m_changeTracker(0) , m_allowActions(true) , m_allowAddUndoCommand(true) , m_allowResourceManagerUpdates(true) , m_prevCursorPosition(-1) , m_caretTimer(this) , m_caretTimerState(true) , m_currentCommand(0) , m_currentCommandHasChildren(false) , m_specialCharacterDocker(0) , m_textTyping(false) , m_textDeleting(false) , m_editTipTimer(this) , m_delayedEnsureVisible(false) , m_toolSelection(0) , m_tableDraggedOnce(false) , m_tablePenMode(false) , m_lastImMicroFocus(QRectF(0, 0, 0, 0)) , m_drag(0) { setTextMode(true); createActions(); m_unit = canvas->resourceManager()->unitResource(KoCanvasResourceProvider::Unit); foreach (KoTextEditingPlugin *plugin, textEditingPluginContainer()->values()) { connect(plugin, SIGNAL(startMacro(QString)), this, SLOT(startMacro(QString))); connect(plugin, SIGNAL(stopMacro()), this, SLOT(stopMacro())); QHash actions = plugin->actions(); QHash::iterator i = actions.begin(); while (i != actions.end()) { - addAction(i.key(), i.value()); +// addAction(i.key(), i.value()); ++i; } } m_contextMenu.reset(new QMenu()); // setup the context list. QSignalMapper *signalMapper = new QSignalMapper(this); connect(signalMapper, SIGNAL(mapped(QString)), this, SLOT(startTextEditingPlugin(QString))); m_contextMenu->addAction(this->action("format_font")); foreach (const QString &key, KoTextEditingRegistry::instance()->keys()) { KoTextEditingFactory *factory = KoTextEditingRegistry::instance()->value(key); if (factory->showInMenu()) { QAction *a = new QAction(factory->title(), this); connect(a, SIGNAL(triggered()), signalMapper, SLOT(map())); signalMapper->setMapping(a, factory->id()); m_contextMenu->addAction(a); - addAction(QString("apply_%1").arg(factory->id()), a); +// addAction(QString("apply_%1").arg(factory->id()), a); } } connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(shapeAddedToCanvas())); m_caretTimer.setInterval(500); connect(&m_caretTimer, SIGNAL(timeout()), this, SLOT(blinkCaret())); m_editTipTimer.setInterval(500); m_editTipTimer.setSingleShot(true); connect(&m_editTipTimer, SIGNAL(timeout()), this, SLOT(showEditTip())); } void TextTool::createActions() { bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceProvider::ApplicationSpeciality) & KoCanvasResourceProvider::NoAdvancedText); KisActionRegistry *actionRegistry = KisActionRegistry::instance(); // FIXME: find new icons for these m_actionConfigureSection = actionRegistry->makeQAction("configure_section", this); - addAction("configure_section", m_actionConfigureSection); +// addAction("configure_section", m_actionConfigureSection); connect(m_actionConfigureSection, SIGNAL(triggered(bool)), this, SLOT(configureSection())); m_actionInsertSection = actionRegistry->makeQAction("insert_section", this); - addAction("insert_section", m_actionInsertSection); +// addAction("insert_section", m_actionInsertSection); connect(m_actionInsertSection, SIGNAL(triggered(bool)), this, SLOT(insertNewSection())); m_actionSplitSections = actionRegistry->makeQAction("split_sections", this); - addAction("split_sections", m_actionSplitSections); +// addAction("split_sections", m_actionSplitSections); connect(m_actionSplitSections, SIGNAL(triggered(bool)), this, SLOT(splitSections())); m_actionPasteAsText = actionRegistry->makeQAction("edit_paste_text", this); - addAction("edit_paste_text", m_actionPasteAsText); +// addAction("edit_paste_text", m_actionPasteAsText); connect(m_actionPasteAsText, SIGNAL(triggered(bool)), this, SLOT(pasteAsText())); m_actionFormatBold = actionRegistry->makeQAction("format_bold", this); - addAction("format_bold", m_actionFormatBold); +// addAction("format_bold", m_actionFormatBold); m_actionFormatBold->setCheckable(true); connect(m_actionFormatBold, SIGNAL(triggered(bool)), this, SLOT(bold(bool))); m_actionFormatItalic = actionRegistry->makeQAction("format_italic", this); m_actionFormatItalic->setCheckable(true); - addAction("format_italic", m_actionFormatItalic); +// addAction("format_italic", m_actionFormatItalic); connect(m_actionFormatItalic, SIGNAL(triggered(bool)), this, SLOT(italic(bool))); m_actionFormatUnderline = actionRegistry->makeQAction("format_underline", this); m_actionFormatUnderline->setCheckable(true); - addAction("format_underline", m_actionFormatUnderline); +// addAction("format_underline", m_actionFormatUnderline); connect(m_actionFormatUnderline, SIGNAL(triggered(bool)), this, SLOT(underline(bool))); m_actionFormatStrikeOut = actionRegistry->makeQAction("format_strike", this); m_actionFormatStrikeOut->setCheckable(true); - addAction("format_strike", m_actionFormatStrikeOut); +// addAction("format_strike", m_actionFormatStrikeOut); connect(m_actionFormatStrikeOut, SIGNAL(triggered(bool)), this, SLOT(strikeOut(bool))); QActionGroup *alignmentGroup = new QActionGroup(this); m_actionAlignLeft = actionRegistry->makeQAction("format_alignleft", this); m_actionAlignLeft->setCheckable(true); alignmentGroup->addAction(m_actionAlignLeft); - addAction("format_alignleft", m_actionAlignLeft); +// addAction("format_alignleft", m_actionAlignLeft); connect(m_actionAlignLeft, SIGNAL(triggered(bool)), this, SLOT(alignLeft())); m_actionAlignRight = actionRegistry->makeQAction("format_alignright", this); m_actionAlignRight->setCheckable(true); alignmentGroup->addAction(m_actionAlignRight); - addAction("format_alignright", m_actionAlignRight); +// addAction("format_alignright", m_actionAlignRight); connect(m_actionAlignRight, SIGNAL(triggered(bool)), this, SLOT(alignRight())); m_actionAlignCenter = actionRegistry->makeQAction("format_aligncenter", this); m_actionAlignCenter->setCheckable(true); - addAction("format_aligncenter", m_actionAlignCenter); +// addAction("format_aligncenter", m_actionAlignCenter); alignmentGroup->addAction(m_actionAlignCenter); connect(m_actionAlignCenter, SIGNAL(triggered(bool)), this, SLOT(alignCenter())); m_actionAlignBlock = actionRegistry->makeQAction("format_alignblock", this); m_actionAlignBlock->setCheckable(true); alignmentGroup->addAction(m_actionAlignBlock); - addAction("format_alignblock", m_actionAlignBlock); +// addAction("format_alignblock", m_actionAlignBlock); connect(m_actionAlignBlock, SIGNAL(triggered(bool)), this, SLOT(alignBlock())); m_actionChangeDirection = actionRegistry->makeQAction("change_text_direction", this); m_actionChangeDirection->setCheckable(true); - addAction("change_text_direction", m_actionChangeDirection); +// addAction("change_text_direction", m_actionChangeDirection); connect(m_actionChangeDirection, SIGNAL(triggered()), this, SLOT(textDirectionChanged())); m_actionFormatSuper = actionRegistry->makeQAction("format_super", this); m_actionFormatSuper->setCheckable(true); - addAction("format_super", m_actionFormatSuper); +// addAction("format_super", m_actionFormatSuper); connect(m_actionFormatSuper, SIGNAL(triggered(bool)), this, SLOT(superScript(bool))); m_actionFormatSub = actionRegistry->makeQAction("format_sub", this); m_actionFormatSub->setCheckable(true); - addAction("format_sub", m_actionFormatSub); +// addAction("format_sub", m_actionFormatSub); connect(m_actionFormatSub, SIGNAL(triggered(bool)), this, SLOT(subScript(bool))); // TODO: check these rtl-things work properly m_actionFormatIncreaseIndent = actionRegistry->makeQAction("format_increaseindent", this); - addAction("format_increaseindent", m_actionFormatIncreaseIndent); +// addAction("format_increaseindent", m_actionFormatIncreaseIndent); connect(m_actionFormatIncreaseIndent, SIGNAL(triggered()), this, SLOT(increaseIndent())); m_actionFormatDecreaseIndent = actionRegistry->makeQAction("format_decreaseindent", this); - addAction("format_decreaseindent", m_actionFormatDecreaseIndent); +// addAction("format_decreaseindent", m_actionFormatDecreaseIndent); connect(m_actionFormatDecreaseIndent, SIGNAL(triggered()), this, SLOT(decreaseIndent())); const char *const increaseIndentActionIconName = QApplication::isRightToLeft() ? koIconNameCStr("format-indent-less") : koIconNameCStr("format-indent-more"); m_actionFormatIncreaseIndent->setIcon(koIcon(increaseIndentActionIconName)); const char *const decreaseIndentActionIconName = QApplication::isRightToLeft() ? koIconNameCStr("format_decreaseindent") : koIconNameCStr("format-indent-less"); m_actionFormatIncreaseIndent->setIcon(koIcon(decreaseIndentActionIconName)); QAction *action = actionRegistry->makeQAction("format_bulletlist", this); - addAction("format_bulletlist", action); +// addAction("format_bulletlist", action); action = actionRegistry->makeQAction("format_numberlist", this); - addAction("format_numberlist", action); +// addAction("format_numberlist", action); action = actionRegistry->makeQAction("fontsizeup", this); - addAction("fontsizeup", action); +// addAction("fontsizeup", action); connect(action, SIGNAL(triggered()), this, SLOT(increaseFontSize())); action = actionRegistry->makeQAction("fontsizedown", this); - addAction("fontsizedown", action); +// addAction("fontsizedown", action); connect(action, SIGNAL(triggered()), this, SLOT(decreaseFontSize())); m_actionFormatFontFamily = new KoFontFamilyAction(this); m_actionFormatFontFamily->setText(i18n("Font Family")); - addAction("format_fontfamily", m_actionFormatFontFamily); +// addAction("format_fontfamily", m_actionFormatFontFamily); connect(m_actionFormatFontFamily, SIGNAL(triggered(QString)), this, SLOT(setFontFamily(QString))); m_variableMenu = new KActionMenu(i18n("Variable"), this); - addAction("insert_variable", m_variableMenu); +// addAction("insert_variable", m_variableMenu); // ------------------- Actions with a key binding and no GUI item action = actionRegistry->makeQAction("nonbreaking_space", this); - addAction("nonbreaking_space", action); +// addAction("nonbreaking_space", action); connect(action, SIGNAL(triggered()), this, SLOT(nonbreakingSpace())); action = actionRegistry->makeQAction("nonbreaking_hyphen", this); - addAction("nonbreaking_hyphen", action); +// addAction("nonbreaking_hyphen", action); connect(action, SIGNAL(triggered()), this, SLOT(nonbreakingHyphen())); action = actionRegistry->makeQAction("insert_index", this); - addAction("insert_index", action); +// addAction("insert_index", action); connect(action, SIGNAL(triggered()), this, SLOT(insertIndexMarker())); action = actionRegistry->makeQAction("soft_hyphen", this); // TODO: double check this one works, conflicts with "zoom out" - addAction("soft_hyphen", action); +// addAction("soft_hyphen", action); connect(action, SIGNAL(triggered()), this, SLOT(softHyphen())); if (useAdvancedText) { action = actionRegistry->makeQAction("line_break", this); - addAction("line_break", action); +// addAction("line_break", action); connect(action, SIGNAL(triggered()), this, SLOT(lineBreak())); action = actionRegistry->makeQAction("insert_framebreak", this); - addAction("insert_framebreak", action); +// addAction("insert_framebreak", action); connect(action, SIGNAL(triggered()), this, SLOT(insertFrameBreak())); } action = actionRegistry->makeQAction("format_font", this); - addAction("format_font", action); +// addAction("format_font", action); connect(action, SIGNAL(triggered()), this, SLOT(selectFont())); m_actionFormatFontSize = new FontSizeAction(i18n("Font Size"), this); - addAction("format_fontsize", m_actionFormatFontSize); +// addAction("format_fontsize", m_actionFormatFontSize); connect(m_actionFormatFontSize, SIGNAL(fontSizeChanged(qreal)), this, SLOT(setFontSize(qreal))); m_actionFormatTextColor = new KoColorPopupAction(this); - addAction("format_textcolor", m_actionFormatTextColor); +// addAction("format_textcolor", m_actionFormatTextColor); connect(m_actionFormatTextColor, SIGNAL(colorChanged(KoColor)), this, SLOT(setTextColor(KoColor))); m_actionFormatBackgroundColor = new KoColorPopupAction(this); - addAction("format_backgroundcolor", m_actionFormatBackgroundColor); +// addAction("format_backgroundcolor", m_actionFormatBackgroundColor); connect(m_actionFormatBackgroundColor, SIGNAL(colorChanged(KoColor)), this, SLOT(setBackgroundColor(KoColor))); m_growWidthAction = actionRegistry->makeQAction("grow_to_fit_width", this); - addAction("grow_to_fit_width", m_growWidthAction); +// addAction("grow_to_fit_width", m_growWidthAction); connect(m_growWidthAction, SIGNAL(triggered(bool)), this, SLOT(setGrowWidthToFit(bool))); m_growHeightAction = actionRegistry->makeQAction("grow_to_fit_height", this); - addAction("grow_to_fit_height", m_growHeightAction); +// addAction("grow_to_fit_height", m_growHeightAction); connect(m_growHeightAction, SIGNAL(triggered(bool)), this, SLOT(setGrowHeightToFit(bool))); m_shrinkToFitAction = actionRegistry->makeQAction("shrink_to_fit", this); - addAction("shrink_to_fit", m_shrinkToFitAction); +// addAction("shrink_to_fit", m_shrinkToFitAction); connect(m_shrinkToFitAction, SIGNAL(triggered(bool)), this, SLOT(setShrinkToFit(bool))); if (useAdvancedText) { action = actionRegistry->makeQAction("insert_table", this); - addAction("insert_table", action); +// addAction("insert_table", action); connect(action, SIGNAL(triggered()), this, SLOT(insertTable())); action = actionRegistry->makeQAction("insert_tablerow_above", this); - addAction("insert_tablerow_above", action); +// addAction("insert_tablerow_above", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableRowAbove())); action = actionRegistry->makeQAction("insert_tablerow_below", this); - addAction("insert_tablerow_below", action); +// addAction("insert_tablerow_below", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableRowBelow())); action = actionRegistry->makeQAction("insert_tablecolumn_left", this); - addAction("insert_tablecolumn_left", action); +// addAction("insert_tablecolumn_left", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableColumnLeft())); action = actionRegistry->makeQAction("insert_tablecolumn_right", this); - addAction("insert_tablecolumn_right", action); +// addAction("insert_tablecolumn_right", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableColumnRight())); action = actionRegistry->makeQAction("delete_tablecolumn", this); - addAction("delete_tablecolumn", action); +// addAction("delete_tablecolumn", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteTableColumn())); action = actionRegistry->makeQAction("delete_tablerow", this); - addAction("delete_tablerow", action); +// addAction("delete_tablerow", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteTableRow())); action = actionRegistry->makeQAction("merge_tablecells", this); - addAction("merge_tablecells", action); +// addAction("merge_tablecells", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(mergeTableCells())); action = actionRegistry->makeQAction("split_tablecells", this); - addAction("split_tablecells", action); +// addAction("split_tablecells", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(splitTableCells())); action = actionRegistry->makeQAction("activate_borderpainter", this); - addAction("activate_borderpainter", action); +// addAction("activate_borderpainter", action); } action = actionRegistry->makeQAction("format_paragraph", this); - addAction("format_paragraph", action); +// addAction("format_paragraph", action); connect(action, SIGNAL(triggered()), this, SLOT(formatParagraph())); action = actionRegistry->makeQAction("format_stylist", this); - addAction("format_stylist", action); +// addAction("format_stylist", action); connect(action, SIGNAL(triggered()), this, SLOT(showStyleManager())); action = KStandardAction::selectAll(this, SLOT(selectAll()), this); - addAction("edit_select_all", action); +// addAction("edit_select_all", action); action = actionRegistry->makeQAction("insert_specialchar", this); - addAction("insert_specialchar", action); +// addAction("insert_specialchar", action); connect(action, SIGNAL(triggered()), this, SLOT(insertSpecialCharacter())); action = actionRegistry->makeQAction("repaint", this); - addAction("repaint", action); +// addAction("repaint", action); connect(action, SIGNAL(triggered()), this, SLOT(relayoutContent())); action = actionRegistry->makeQAction("insert_annotation", this); - addAction("insert_annotation", action); +// addAction("insert_annotation", action); connect(action, SIGNAL(triggered()), this, SLOT(insertAnnotation())); #ifndef NDEBUG action = actionRegistry->makeQAction("detailed_debug_paragraphs", this); - addAction("detailed_debug_paragraphs", action); +// addAction("detailed_debug_paragraphs", action); connect(action, SIGNAL(triggered()), this, SLOT(debugTextDocument())); action = actionRegistry->makeQAction("detailed_debug_styles", this); - addAction("detailed_debug_styles", action); +// addAction("detailed_debug_styles", action); connect(action, SIGNAL(triggered()), this, SLOT(debugTextStyles())); #endif } #ifndef NDEBUG #include "tests/MockShapes.h" #include #include TextTool::TextTool(MockCanvas *canvas) // constructor for our unit tests; : KoToolBase(canvas), m_textShape(0), m_textShapeData(0), m_changeTracker(0), m_allowActions(true), m_allowAddUndoCommand(true), m_allowResourceManagerUpdates(true), m_prevCursorPosition(-1), m_caretTimer(this), m_caretTimerState(true), m_currentCommand(0), m_currentCommandHasChildren(false), m_specialCharacterDocker(0), m_textEditingPlugins(0) , m_editTipTimer(this) , m_delayedEnsureVisible(false) , m_tableDraggedOnce(false) , m_tablePenMode(false) { // we could init some vars here, but we probably don't have to QLocale::setDefault(QLocale("en")); QTextDocument *document = new QTextDocument(); // this document is leaked KoInlineTextObjectManager *inlineManager = new KoInlineTextObjectManager(); KoTextDocument(document).setInlineTextObjectManager(inlineManager); KoTextRangeManager *locationManager = new KoTextRangeManager(); KoTextDocument(document).setTextRangeManager(locationManager); m_textEditor = new KoTextEditor(document); KoTextDocument(document).setTextEditor(m_textEditor.data()); m_toolSelection = new TextToolSelection(m_textEditor); m_changeTracker = new KoChangeTracker(); KoTextDocument(document).setChangeTracker(m_changeTracker); KoTextDocument(document).setUndoStack(new KUndo2Stack()); } #endif TextTool::~TextTool() { delete m_toolSelection; KIS_SAFE_ASSERT_RECOVER (!m_currentCommand) { delete m_currentCommand; } } void TextTool::showEditTip() { if (!m_textShapeData || m_editTipPointedAt.position == -1) { return; } QTextCursor c(m_textShapeData->document()); c.setPosition(m_editTipPointedAt.position); QString text = "

"; int toolTipWidth = 0; if (m_changeTracker && m_changeTracker->containsInlineChanges(c.charFormat()) && m_changeTracker->displayChanges()) { KoChangeTrackerElement *element = m_changeTracker->elementById(c.charFormat().property(KoCharacterStyle::ChangeTrackerId).toInt()); if (element->isEnabled()) { QString changeType; if (element->getChangeType() == KoGenChange::InsertChange) { changeType = i18n("Insertion"); } else if (element->getChangeType() == KoGenChange::DeleteChange) { changeType = i18n("Deletion"); } else { changeType = i18n("Formatting"); } text += "" + changeType + "
"; QString date = element->getDate(); //Remove the T which separates the Data and Time. date[10] = QLatin1Char(' '); date = element->getCreator() + QLatin1Char(' ') + date; text += date + "

"; toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(date).width(); } } if (m_editTipPointedAt.bookmark || !m_editTipPointedAt.externalHRef.isEmpty()) { QString help = i18n("Ctrl+click to go to link "); help += m_editTipPointedAt.externalHRef; text += help + "

"; toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width(); } if (m_editTipPointedAt.note) { QString help = i18n("Ctrl+click to go to the note "); text += help + "

"; toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width(); } if (m_editTipPointedAt.noteReference > 0) { QString help = i18n("Ctrl+click to go to the note reference"); text += help + "

"; toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width(); } QToolTip::hideText(); if (toolTipWidth) { QRect keepRect(m_editTipPos - QPoint(3, 3), QSize(6, 6)); QToolTip::showText(m_editTipPos - QPoint(toolTipWidth / 2, 0), text, canvas()->canvasWidget(), keepRect); } } void TextTool::blinkCaret() { if (!(canvas()->canvasWidget() && canvas()->canvasWidget()->hasFocus())) { m_caretTimer.stop(); m_caretTimerState = false; // not visible. } else { m_caretTimerState = !m_caretTimerState; } repaintCaret(); } void TextTool::relayoutContent() { KoTextDocumentLayout *lay = qobject_cast(m_textShapeData->document()->documentLayout()); Q_ASSERT(lay); foreach (KoTextLayoutRootArea *rootArea, lay->rootAreas()) { rootArea->setDirty(); } lay->emitLayoutIsDirty(); } void TextTool::paint(QPainter &painter, const KoViewConverter &converter) { if (m_textEditor.isNull()) { return; } if (canvas() && (canvas()->canvasWidget() && canvas()->canvasWidget()->hasFocus()) && !m_caretTimer.isActive()) { // make sure we blink m_caretTimer.start(); m_caretTimerState = true; } if (!m_caretTimerState) { m_caretTimer.setInterval(500); // we set it lower during typing, so set it back to normal } if (!m_textShapeData) { return; } if (m_textShapeData->isDirty()) { return; } qreal zoomX, zoomY; converter.zoom(&zoomX, &zoomY); painter.save(); QTransform shapeMatrix = m_textShape->absoluteTransformation(&converter); shapeMatrix.scale(zoomX, zoomY); shapeMatrix.translate(0, -m_textShapeData->documentOffset()); // Possibly draw table dragging visual cues const qreal boxHeight = 20; if (m_tableDragInfo.tableHit == KoPointedAt::ColumnDivider) { QPointF anchorPos = m_tableDragInfo.tableDividerPos - QPointF(m_dx, 0.0); if (m_tableDragInfo.tableColumnDivider > 0) { //let's draw left qreal w = m_tableDragInfo.tableLeadSize - m_dx; QRectF rect(anchorPos - QPointF(w, 0.0), QSizeF(w, 0.0)); QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight())); drawRect.setHeight(boxHeight); drawRect.moveTop(drawRect.top() - 1.5 * boxHeight); QString label = m_unit.toUserStringValue(w); int labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width(); painter.fillRect(drawRect, QColor(64, 255, 64, 196)); painter.setPen(QColor(0, 0, 0, 196)); if (labelWidth + 10 < drawRect.width()) { QPointF centerLeft(drawRect.left(), drawRect.center().y()); QPointF centerRight(drawRect.right(), drawRect.center().y()); painter.drawLine(centerLeft, drawRect.center() - QPointF(labelWidth / 2 + 5, 0.0)); painter.drawLine(centerLeft, centerLeft + QPointF(7, -5)); painter.drawLine(centerLeft, centerLeft + QPointF(7, 5)); painter.drawLine(drawRect.center() + QPointF(labelWidth / 2 + 5, 0.0), centerRight); painter.drawLine(centerRight, centerRight + QPointF(-7, -5)); painter.drawLine(centerRight, centerRight + QPointF(-7, 5)); painter.drawText(drawRect, Qt::AlignCenter, label); } } if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns()) { //let's draw right qreal w = m_tableDragInfo.tableTrailSize + m_dx; QRectF rect(anchorPos, QSizeF(w, 0.0)); QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight())); drawRect.setHeight(boxHeight); drawRect.moveTop(drawRect.top() - 1.5 * boxHeight); QString label; int labelWidth; if (m_tableDragWithShift) { label = i18n("follows along"); labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width(); drawRect.setWidth(2 * labelWidth); QLinearGradient g(drawRect.topLeft(), drawRect.topRight()); g.setColorAt(0.6, QColor(255, 64, 64, 196)); g.setColorAt(1.0, QColor(255, 64, 64, 0)); QBrush brush(g); painter.fillRect(drawRect, brush); } else { label = m_unit.toUserStringValue(w); labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width(); drawRect.setHeight(boxHeight); painter.fillRect(drawRect, QColor(64, 255, 64, 196)); } painter.setPen(QColor(0, 0, 0, 196)); if (labelWidth + 10 < drawRect.width()) { QPointF centerLeft(drawRect.left(), drawRect.center().y()); QPointF centerRight(drawRect.right(), drawRect.center().y()); painter.drawLine(centerLeft, drawRect.center() - QPointF(labelWidth / 2 + 5, 0.0)); painter.drawLine(centerLeft, centerLeft + QPointF(7, -5)); painter.drawLine(centerLeft, centerLeft + QPointF(7, 5)); if (!m_tableDragWithShift) { painter.drawLine(drawRect.center() + QPointF(labelWidth / 2 + 5, 0.0), centerRight); painter.drawLine(centerRight, centerRight + QPointF(-7, -5)); painter.drawLine(centerRight, centerRight + QPointF(-7, 5)); } painter.drawText(drawRect, Qt::AlignCenter, label); } if (!m_tableDragWithShift) { // let's draw a helper text too label = i18n("Press shift to not resize this"); labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width(); labelWidth += 10; //if (labelWidth < drawRect.width()) { drawRect.moveTop(drawRect.top() + boxHeight); drawRect.moveLeft(drawRect.left() + (drawRect.width() - labelWidth) / 2); drawRect.setWidth(labelWidth); painter.fillRect(drawRect, QColor(64, 255, 64, 196)); painter.drawText(drawRect, Qt::AlignCenter, label); } } } } // Possibly draw table dragging visual cues if (m_tableDragInfo.tableHit == KoPointedAt::RowDivider) { QPointF anchorPos = m_tableDragInfo.tableDividerPos - QPointF(0.0, m_dy); if (m_tableDragInfo.tableRowDivider > 0) { qreal h = m_tableDragInfo.tableLeadSize - m_dy; QRectF rect(anchorPos - QPointF(0.0, h), QSizeF(0.0, h)); QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight())); drawRect.setWidth(boxHeight); drawRect.moveLeft(drawRect.left() - 1.5 * boxHeight); QString label = m_unit.toUserStringValue(h); QRectF labelRect = QFontMetrics(QToolTip::font()).boundingRect(label); labelRect.setHeight(boxHeight); labelRect.setWidth(labelRect.width() + 10); labelRect.moveTopLeft(drawRect.center() - QPointF(labelRect.width(), labelRect.height()) / 2); painter.fillRect(drawRect, QColor(64, 255, 64, 196)); painter.fillRect(labelRect, QColor(64, 255, 64, 196)); painter.setPen(QColor(0, 0, 0, 196)); if (labelRect.height() + 10 < drawRect.height()) { QPointF centerTop(drawRect.center().x(), drawRect.top()); QPointF centerBottom(drawRect.center().x(), drawRect.bottom()); painter.drawLine(centerTop, drawRect.center() - QPointF(0.0, labelRect.height() / 2 + 5)); painter.drawLine(centerTop, centerTop + QPointF(-5, 7)); painter.drawLine(centerTop, centerTop + QPointF(5, 7)); painter.drawLine(drawRect.center() + QPointF(0.0, labelRect.height() / 2 + 5), centerBottom); painter.drawLine(centerBottom, centerBottom + QPointF(-5, -7)); painter.drawLine(centerBottom, centerBottom + QPointF(5, -7)); } painter.drawText(labelRect, Qt::AlignCenter, label); } } if (m_caretTimerState) { // Lets draw the caret ourselves, as the Qt method doesn't take cursor // charFormat into consideration. QTextBlock block = m_textEditor.data()->block(); if (block.isValid()) { int posInParag = m_textEditor.data()->position() - block.position(); if (posInParag <= block.layout()->preeditAreaPosition()) { posInParag += block.layout()->preeditAreaText().length(); } QTextLine tl = block.layout()->lineForTextPosition(m_textEditor.data()->position() - block.position()); if (tl.isValid()) { painter.setRenderHint(QPainter::Antialiasing, false); QRectF rect = caretRect(m_textEditor.data()->cursor()); QPointF baselinePoint; if (tl.ascent() > 0) { QFontMetricsF fm(m_textEditor.data()->charFormat().font(), painter.device()); rect.setY(rect.y() + tl.ascent() - qMin(tl.ascent(), fm.ascent())); rect.setHeight(qMin(tl.ascent(), fm.ascent()) + qMin(tl.descent(), fm.descent())); baselinePoint = QPoint(rect.x(), rect.y() + tl.ascent()); } else { //line only filled with characters-without-size (eg anchors) // layout will make sure line has height of block font QFontMetricsF fm(block.charFormat().font(), painter.device()); rect.setHeight(fm.ascent() + fm.descent()); baselinePoint = QPoint(rect.x(), rect.y() + fm.ascent()); } QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomLeft())); drawRect.setWidth(2); painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination); if (m_textEditor.data()->isEditProtected(true)) { QRectF circleRect(shapeMatrix.map(baselinePoint), QSizeF(14, 14)); circleRect.translate(-6.5, -6.5); QPen pen(QColor(16, 255, 255)); pen.setWidthF(2.0); painter.setPen(pen); painter.setRenderHint(QPainter::Antialiasing, true); painter.drawEllipse(circleRect); painter.drawLine(circleRect.topLeft() + QPointF(4.5, 4.5), circleRect.bottomRight() - QPointF(4.5, 4.5)); } else { painter.fillRect(drawRect, QColor(128, 255, 128)); } } } } painter.restore(); } void TextTool::updateSelectedShape(const QPointF &point, bool noDocumentChange) { QRectF area(point, QSizeF(1, 1)); if (m_textEditor.data()->hasSelection()) { repaintSelection(); } else { repaintCaret(); } QList sortedShapes = canvas()->shapeManager()->shapesAt(area, true); std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex); for (int count = sortedShapes.count() - 1; count >= 0; count--) { KoShape *shape = sortedShapes.at(count); if (shape->isContentProtected()) { continue; } TextShape *textShape = dynamic_cast(shape); if (textShape) { if (textShape != m_textShape) { if (static_cast(textShape->userData())->document() != m_textShapeData->document()) { //we should only change to another document if allowed if (noDocumentChange) { return; } // if we change to another textdocument we need to remove selection in old document // or it would continue to be painted etc m_textEditor.data()->setPosition(m_textEditor.data()->position()); } m_textShape = textShape; setShapeData(static_cast(m_textShape->userData())); // This is how we inform the rulers of the active range // For now we will not consider table cells, but just give the shape dimensions QVariant v; QRectF rect(QPoint(), m_textShape->size()); rect = m_textShape->absoluteTransformation(0).mapRect(rect); v.setValue(rect); canvas()->resourceManager()->setResource(KoCanvasResourceProvider::ActiveRange, v); } return; } } } void TextTool::mousePressEvent(KoPointerEvent *event) { if (m_textEditor.isNull()) { return; } // request the software keyboard, if any if (event->button() == Qt::LeftButton && qApp->autoSipEnabled()) { QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel(qApp->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel)); // the two following bools just make it all a lot easier to read in the following if() // basically, we require a widget for this to work (passing 0 to QApplication::sendEvent // crashes) and there are three tests any one of which can be true to trigger the event const bool hasWidget = canvas()->canvasWidget(); if ((behavior == QStyle::RSIP_OnMouseClick && hasWidget) || (hasWidget && canvas()->canvasWidget()->hasFocus())) { QEvent event(QEvent::RequestSoftwareInputPanel); if (hasWidget) { QApplication::sendEvent(canvas()->canvasWidget(), &event); } } } bool shiftPressed = event->modifiers() & Qt::ShiftModifier; updateSelectedShape(event->point, shiftPressed); KoSelection *selection = canvas()->selectedShapesProxy()->selection(); if (m_textShape && !selection->isSelected(m_textShape) && m_textShape->isSelectable()) { selection->deselectAll(); selection->select(m_textShape); } KoPointedAt pointedAt = hitTest(event->point); m_tableDraggedOnce = false; m_clickWithinSelection = false; if (pointedAt.position != -1) { m_tablePenMode = false; if ((event->button() == Qt::LeftButton) && !shiftPressed && m_textEditor.data()->hasSelection() && m_textEditor.data()->isWithinSelection(pointedAt.position)) { m_clickWithinSelection = true; m_draggingOrigin = event->pos(); //we store the pixel pos } else if (!(event->button() == Qt::RightButton && m_textEditor.data()->hasSelection() && m_textEditor.data()->isWithinSelection(pointedAt.position))) { m_textEditor.data()->setPosition(pointedAt.position, shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); useCursor(Qt::IBeamCursor); } m_tableDragInfo.tableHit = KoPointedAt::None; if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw) m_caretTimer.stop(); m_caretTimer.setInterval(50); m_caretTimer.start(); m_caretTimerState = true; // turn caret instantly on on click } } else { if (event->button() == Qt::RightButton) { m_tablePenMode = false; KoTextEditingPlugin *plugin = textEditingPluginContainer()->spellcheck(); if (plugin) { plugin->setCurrentCursorPosition(m_textShapeData->document(), -1); } event->ignore(); } else if (m_tablePenMode) { m_textEditor.data()->beginEditBlock(kundo2_i18n("Change Border Formatting")); if (pointedAt.tableHit == KoPointedAt::ColumnDivider) { if (pointedAt.tableColumnDivider < pointedAt.table->columns()) { m_textEditor.data()->setTableBorderData(pointedAt.table, pointedAt.tableRowDivider, pointedAt.tableColumnDivider, KoBorder::LeftBorder, m_tablePenBorderData); } if (pointedAt.tableColumnDivider > 0) { m_textEditor.data()->setTableBorderData(pointedAt.table, pointedAt.tableRowDivider, pointedAt.tableColumnDivider - 1, KoBorder::RightBorder, m_tablePenBorderData); } } else if (pointedAt.tableHit == KoPointedAt::RowDivider) { if (pointedAt.tableRowDivider < pointedAt.table->rows()) { m_textEditor.data()->setTableBorderData(pointedAt.table, pointedAt.tableRowDivider, pointedAt.tableColumnDivider, KoBorder::TopBorder, m_tablePenBorderData); } if (pointedAt.tableRowDivider > 0) { m_textEditor.data()->setTableBorderData(pointedAt.table, pointedAt.tableRowDivider - 1, pointedAt.tableColumnDivider, KoBorder::BottomBorder, m_tablePenBorderData); } } m_textEditor.data()->endEditBlock(); } else { m_tableDragInfo = pointedAt; m_tablePenMode = false; } return; } if (shiftPressed) { // altered selection. repaintSelection(); } else { repaintCaret(); } updateSelectionHandler(); updateStyleManager(); updateActions(); //activate context-menu for spelling-suggestions if (event->button() == Qt::RightButton) { KoTextEditingPlugin *plugin = textEditingPluginContainer()->spellcheck(); if (plugin) { plugin->setCurrentCursorPosition(m_textShapeData->document(), m_textEditor.data()->position()); } event->ignore(); } if (event->button() == Qt::MidButton) { // Paste const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Selection); // on windows we do not have data if we try to paste this selection if (data) { m_prevCursorPosition = m_textEditor.data()->position(); m_textEditor.data()->paste(canvas(), data, canvas()->resourceManager()); editingPluginEvents(); } } } void TextTool::setShapeData(KoTextShapeData *data) { bool docChanged = !data || !m_textShapeData || m_textShapeData->document() != data->document(); if (m_textShapeData) { disconnect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved())); } m_textShapeData = data; if (!m_textShapeData) { return; } connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved())); if (docChanged) { if (!m_textEditor.isNull()) { disconnect(m_textEditor.data(), SIGNAL(textFormatChanged()), this, SLOT(updateActions())); } m_textEditor = KoTextDocument(m_textShapeData->document()).textEditor(); Q_ASSERT(m_textEditor.data()); if (!m_toolSelection) { m_toolSelection = new TextToolSelection(m_textEditor.data()); } else { m_toolSelection->m_editor = m_textEditor.data(); } m_variableMenu->menu()->clear(); KoTextDocument document(m_textShapeData->document()); foreach (QAction *action, document.inlineTextObjectManager()->createInsertVariableActions(canvas())) { m_variableMenu->addAction(action); connect(action, SIGNAL(triggered()), this, SLOT(returnFocusToCanvas())); } connect(m_textEditor.data(), SIGNAL(textFormatChanged()), this, SLOT(updateActions())); updateActions(); } } void TextTool::updateSelectionHandler() { if (m_textEditor) { emit selectionChanged(m_textEditor.data()->hasSelection()); if (m_textEditor.data()->hasSelection()) { QClipboard *clipboard = QApplication::clipboard(); if (clipboard->supportsSelection()) { clipboard->setText(m_textEditor.data()->selectedText(), QClipboard::Selection); } } } KoCanvasResourceProvider *p = canvas()->resourceManager(); m_allowResourceManagerUpdates = false; if (m_textEditor && m_textShapeData) { p->setResource(KoText::CurrentTextPosition, m_textEditor.data()->position()); p->setResource(KoText::CurrentTextAnchor, m_textEditor.data()->anchor()); QVariant variant; variant.setValue(m_textShapeData->document()); p->setResource(KoText::CurrentTextDocument, variant); } else { p->clearResource(KoText::CurrentTextPosition); p->clearResource(KoText::CurrentTextAnchor); p->clearResource(KoText::CurrentTextDocument); } m_allowResourceManagerUpdates = true; } QMimeData *TextTool::generateMimeData() const { if (!m_textShapeData || m_textEditor.isNull() || !m_textEditor.data()->hasSelection()) { return 0; } int from = m_textEditor.data()->position(); int to = m_textEditor.data()->anchor(); KoTextOdfSaveHelper saveHelper(m_textShapeData->document(), from, to); KoTextDrag drag; #ifdef SHOULD_BUILD_RDF KoDocumentResourceManager *rm = 0; if (canvas()->shapeController()) { rm = canvas()->shapeController()->resourceManager(); } if (rm && rm->hasResource(KoText::DocumentRdf)) { KoDocumentRdfBase *rdf = qobject_cast(rm->resource(KoText::DocumentRdf).value()); if (rdf) { saveHelper.setRdfModel(rdf->model()); } } #endif drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper); QTextDocumentFragment fragment = m_textEditor.data()->selection(); drag.setData("text/html", fragment.toHtml("utf-8").toUtf8()); drag.setData("text/plain", fragment.toPlainText().toUtf8()); return drag.takeMimeData(); } TextEditingPluginContainer *TextTool::textEditingPluginContainer() { m_textEditingPlugins = canvas()->resourceManager()-> resource(TextEditingPluginContainer::ResourceId).value(); if (m_textEditingPlugins == 0) { m_textEditingPlugins = new TextEditingPluginContainer(canvas()->resourceManager()); QVariant variant; variant.setValue(m_textEditingPlugins.data()); canvas()->resourceManager()->setResource(TextEditingPluginContainer::ResourceId, variant); foreach (KoTextEditingPlugin *plugin, m_textEditingPlugins->values()) { connect(plugin, SIGNAL(startMacro(QString)), this, SLOT(startMacro(QString))); connect(plugin, SIGNAL(stopMacro()), this, SLOT(stopMacro())); QHash actions = plugin->actions(); QHash::iterator i = actions.begin(); while (i != actions.end()) { - addAction(i.key(), i.value()); +// addAction(i.key(), i.value()); ++i; } } } return m_textEditingPlugins; } void TextTool::copy() const { QMimeData *mimeData = generateMimeData(); if (mimeData) { QApplication::clipboard()->setMimeData(mimeData); } } void TextTool::deleteSelection() { m_textEditor.data()->deleteChar(); editingPluginEvents(); } bool TextTool::paste() { const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Clipboard); // on windows we do not have data if we try to paste the selection if (!data) { return false; } // since this is not paste-as-text we will not paste in urls, but instead let KoToolProxy solve it if (data->hasUrls()) { return false; } if (data->hasFormat(KoOdf::mimeType(KoOdf::Text)) || data->hasText()) { m_prevCursorPosition = m_textEditor.data()->position(); m_textEditor.data()->paste(canvas(), data); editingPluginEvents(); return true; } return false; } void TextTool::cut() { if (m_textEditor.data()->hasSelection()) { copy(); KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Cut")); m_textEditor.data()->deleteChar(false, topCmd); m_textEditor.data()->endEditBlock(); } } void TextTool::dragMoveEvent(QDragMoveEvent *event, const QPointF &point) { if (event->mimeData()->hasFormat(KoOdf::mimeType(KoOdf::Text)) || event->mimeData()->hasFormat(KoOdf::mimeType(KoOdf::OpenOfficeClipboard)) || event->mimeData()->hasText()) { if (m_drag) { event->setDropAction(Qt::MoveAction); event->accept(); } else if (event->proposedAction() == Qt::CopyAction) { event->acceptProposedAction(); } else { event->ignore(); return; } KoPointedAt pointedAt = hitTest(point); if (pointedAt.position == -1) { event->ignore(); } if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw) m_caretTimer.stop(); m_caretTimer.setInterval(50); m_caretTimer.start(); m_caretTimerState = true; // turn caret instantly on on click } if (m_preDragSelection.cursor.isNull()) { repaintSelection(); m_preDragSelection.cursor = QTextCursor(*m_textEditor.data()->cursor()); if (m_drag) { // Make a selection that looks like the current cursor selection // so we can move the real carent around freely QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections(); m_preDragSelection.format = QTextCharFormat(); m_preDragSelection.format.setBackground(qApp->palette().brush(QPalette::Highlight)); m_preDragSelection.format.setForeground(qApp->palette().brush(QPalette::HighlightedText)); sels.append(m_preDragSelection); KoTextDocument(m_textShapeData->document()).setSelections(sels); } // else we wantt the selection ot disappaear } repaintCaret(); // will erase caret m_textEditor.data()->setPosition(pointedAt.position); repaintCaret(); // will paint caret in new spot // Selection has visually not appeared at a new spot so no need to repaint it } } void TextTool::dragLeaveEvent(QDragLeaveEvent *event) { if (m_drag) { // restore the old selections QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections(); sels.pop_back(); KoTextDocument(m_textShapeData->document()).setSelections(sels); } repaintCaret(); // will erase caret in old spot m_textEditor.data()->setPosition(m_preDragSelection.cursor.anchor()); m_textEditor.data()->setPosition(m_preDragSelection.cursor.position(), QTextCursor::KeepAnchor); repaintCaret(); // will paint caret in new spot if (!m_drag) { repaintSelection(); // will paint selection again } // mark that we now are back to normal selection m_preDragSelection.cursor = QTextCursor(); event->accept(); } void TextTool::dropEvent(QDropEvent *event, const QPointF &) { if (m_drag) { // restore the old selections QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections(); sels.pop_back(); KoTextDocument(m_textShapeData->document()).setSelections(sels); } QTextCursor insertCursor(*m_textEditor.data()->cursor()); m_textEditor.data()->setPosition(m_preDragSelection.cursor.anchor()); m_textEditor.data()->setPosition(m_preDragSelection.cursor.position(), QTextCursor::KeepAnchor); repaintSelection(); // will erase the selection in new spot if (m_drag) { m_textEditor.data()->deleteChar(); } m_prevCursorPosition = insertCursor.position(); m_textEditor.data()->setPosition(m_prevCursorPosition); m_textEditor.data()->paste(canvas(), event->mimeData()); m_textEditor.data()->setPosition(m_prevCursorPosition); //since the paste made insertCursor we can now use that for the end position m_textEditor.data()->setPosition(insertCursor.position(), QTextCursor::KeepAnchor); // mark that we no are back to normal selection m_preDragSelection.cursor = QTextCursor(); event->accept(); } KoPointedAt TextTool::hitTest(const QPointF &point) const { if (!m_textShape || !m_textShapeData) { return KoPointedAt(); } QPointF p = m_textShape->convertScreenPos(point); KoTextLayoutRootArea *rootArea = m_textShapeData->rootArea(); return rootArea ? rootArea->hitTest(p, Qt::FuzzyHit) : KoPointedAt(); } void TextTool::mouseDoubleClickEvent(KoPointerEvent *event) { if (canvas()->shapeManager()->shapeAt(event->point) != m_textShape) { event->ignore(); // allow the event to be used by another return; } if (event->modifiers() & Qt::ShiftModifier) { // When whift is pressed we behave as a single press return mousePressEvent(event); } m_textEditor.data()->select(QTextCursor::WordUnderCursor); m_clickWithinSelection = false; repaintSelection(); updateSelectionHandler(); } void TextTool::mouseTripleClickEvent(KoPointerEvent *event) { if (canvas()->shapeManager()->shapeAt(event->point) != m_textShape) { event->ignore(); // allow the event to be used by another return; } if (event->modifiers() & Qt::ShiftModifier) { // When whift is pressed we behave as a single press return mousePressEvent(event); } m_textEditor.data()->clearSelection(); m_textEditor.data()->movePosition(QTextCursor::StartOfBlock); m_textEditor.data()->movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); m_clickWithinSelection = false; repaintSelection(); updateSelectionHandler(); } void TextTool::mouseMoveEvent(KoPointerEvent *event) { m_editTipPos = event->globalPos(); if (event->buttons()) { updateSelectedShape(event->point, true); } m_editTipTimer.stop(); if (QToolTip::isVisible()) { QToolTip::hideText(); } KoPointedAt pointedAt = hitTest(event->point); if (event->buttons() == Qt::NoButton) { if (m_tablePenMode) { if (pointedAt.tableHit == KoPointedAt::ColumnDivider || pointedAt.tableHit == KoPointedAt::RowDivider) { useTableBorderCursor(); } else { useCursor(Qt::IBeamCursor); } // do nothing else return; } if (!m_textShapeData || pointedAt.position < 0) { if (pointedAt.tableHit == KoPointedAt::ColumnDivider) { useCursor(Qt::SplitHCursor); m_draggingOrigin = event->point; } else if (pointedAt.tableHit == KoPointedAt::RowDivider) { if (pointedAt.tableRowDivider > 0) { useCursor(Qt::SplitVCursor); m_draggingOrigin = event->point; } else { useCursor(Qt::IBeamCursor); } } else { useCursor(Qt::IBeamCursor); } return; } QTextCursor mouseOver(m_textShapeData->document()); mouseOver.setPosition(pointedAt.position); if (m_changeTracker && m_changeTracker->containsInlineChanges(mouseOver.charFormat())) { m_editTipPointedAt = pointedAt; if (QToolTip::isVisible()) { QTimer::singleShot(0, this, SLOT(showEditTip())); } else { m_editTipTimer.start(); } } if ((pointedAt.bookmark || !pointedAt.externalHRef.isEmpty()) || pointedAt.note || (pointedAt.noteReference > 0)) { if (event->modifiers() & Qt::ControlModifier) { useCursor(Qt::PointingHandCursor); } m_editTipPointedAt = pointedAt; if (QToolTip::isVisible()) { QTimer::singleShot(0, this, SLOT(showEditTip())); } else { m_editTipTimer.start(); } return; } // check if mouse pointer is over shape with hyperlink KoShape *selectedShape = canvas()->shapeManager()->shapeAt(event->point); if (selectedShape != 0 && selectedShape != m_textShape && selectedShape->hyperLink().size() != 0) { useCursor(Qt::PointingHandCursor); return; } useCursor(Qt::IBeamCursor); // Set Arrow Cursor when mouse is on top of annotation shape. if (selectedShape) { if (selectedShape->shapeId() == "AnnotationTextShapeID") { QPointF point(event->point); if (point.y() <= (selectedShape->position().y() + 25)) { useCursor(Qt::ArrowCursor); } } } return; } else { if (m_tableDragInfo.tableHit == KoPointedAt::ColumnDivider) { m_tableDragWithShift = event->modifiers() & Qt::ShiftModifier; if (m_tableDraggedOnce) { canvas()->shapeController()->resourceManager()->undoStack()->undo(); } KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Adjust Column Width")); m_dx = m_draggingOrigin.x() - event->point.x(); if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns() && m_tableDragInfo.tableTrailSize + m_dx < 0) { m_dx = -m_tableDragInfo.tableTrailSize; } if (m_tableDragInfo.tableColumnDivider > 0) { if (m_tableDragInfo.tableLeadSize - m_dx < 0) { m_dx = m_tableDragInfo.tableLeadSize; } m_textEditor.data()->adjustTableColumnWidth(m_tableDragInfo.table, m_tableDragInfo.tableColumnDivider - 1, m_tableDragInfo.tableLeadSize - m_dx, topCmd); } else { m_textEditor.data()->adjustTableWidth(m_tableDragInfo.table, -m_dx, 0.0); } if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns()) { if (!m_tableDragWithShift) { m_textEditor.data()->adjustTableColumnWidth(m_tableDragInfo.table, m_tableDragInfo.tableColumnDivider, m_tableDragInfo.tableTrailSize + m_dx, topCmd); } } else { m_tableDragWithShift = true; // act like shift pressed } if (m_tableDragWithShift) { m_textEditor.data()->adjustTableWidth(m_tableDragInfo.table, 0.0, m_dx); } m_textEditor.data()->endEditBlock(); m_tableDragInfo.tableDividerPos.setY(m_textShape->convertScreenPos(event->point).y()); if (m_tableDraggedOnce) { //we need to redraw like this so we update outside the textshape too if (canvas()->canvasWidget()) { canvas()->canvasWidget()->update(); } } m_tableDraggedOnce = true; } else if (m_tableDragInfo.tableHit == KoPointedAt::RowDivider) { if (m_tableDraggedOnce) { canvas()->shapeController()->resourceManager()->undoStack()->undo(); } if (m_tableDragInfo.tableRowDivider > 0) { KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Adjust Row Height")); m_dy = m_draggingOrigin.y() - event->point.y(); if (m_tableDragInfo.tableLeadSize - m_dy < 0) { m_dy = m_tableDragInfo.tableLeadSize; } m_textEditor.data()->adjustTableRowHeight(m_tableDragInfo.table, m_tableDragInfo.tableRowDivider - 1, m_tableDragInfo.tableLeadSize - m_dy, topCmd); m_textEditor.data()->endEditBlock(); m_tableDragInfo.tableDividerPos.setX(m_textShape->convertScreenPos(event->point).x()); if (m_tableDraggedOnce) { //we need to redraw like this so we update outside the textshape too if (canvas()->canvasWidget()) { canvas()->canvasWidget()->update(); } } m_tableDraggedOnce = true; } } else if (m_tablePenMode) { // do nothing } else if (m_clickWithinSelection) { if (!m_drag && (event->pos() - m_draggingOrigin).manhattanLength() >= QApplication::startDragDistance()) { QMimeData *mimeData = generateMimeData(); if (mimeData) { m_drag = new QDrag(canvas()->canvasWidget()); m_drag->setMimeData(mimeData); m_drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::CopyAction); m_drag = 0; } } } else { useCursor(Qt::IBeamCursor); if (pointedAt.position == m_textEditor.data()->position()) { return; } if (pointedAt.position >= 0) { if (m_textEditor.data()->hasSelection()) { repaintSelection(); // will erase selection } else { repaintCaret(); } m_textEditor.data()->setPosition(pointedAt.position, QTextCursor::KeepAnchor); if (m_textEditor.data()->hasSelection()) { repaintSelection(); } else { repaintCaret(); } } } updateSelectionHandler(); } } void TextTool::mouseReleaseEvent(KoPointerEvent *event) { event->ignore(); editingPluginEvents(); m_tableDragInfo.tableHit = KoPointedAt::None; if (m_tableDraggedOnce) { m_tableDraggedOnce = false; //we need to redraw like this so we update outside the textshape too if (canvas()->canvasWidget()) { canvas()->canvasWidget()->update(); } } if (!m_textShapeData) { return; } // check if mouse pointer is not over some shape with hyperlink KoShape *selectedShape = canvas()->shapeManager()->shapeAt(event->point); if (selectedShape != 0 && selectedShape != m_textShape && selectedShape->hyperLink().size() != 0) { QString url = selectedShape->hyperLink(); runUrl(event, url); return; } KoPointedAt pointedAt = hitTest(event->point); if (m_clickWithinSelection && !m_drag) { if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw) m_caretTimer.stop(); m_caretTimer.setInterval(50); m_caretTimer.start(); m_caretTimerState = true; // turn caret instantly on on click } repaintCaret(); // will erase caret repaintSelection(); // will erase selection m_textEditor.data()->setPosition(pointedAt.position); repaintCaret(); // will paint caret in new spot } // Is there an anchor here ? if ((event->modifiers() & Qt::ControlModifier) && !m_textEditor.data()->hasSelection()) { if (pointedAt.bookmark) { m_textEditor.data()->setPosition(pointedAt.bookmark->rangeStart()); ensureCursorVisible(); event->accept(); return; } if (pointedAt.note) { m_textEditor.data()->setPosition(pointedAt.note->textFrame()->firstPosition()); ensureCursorVisible(); event->accept(); return; } if (pointedAt.noteReference > 0) { m_textEditor.data()->setPosition(pointedAt.noteReference); ensureCursorVisible(); event->accept(); return; } if (!pointedAt.externalHRef.isEmpty()) { runUrl(event, pointedAt.externalHRef); } } } void TextTool::keyPressEvent(QKeyEvent *event) { int destinationPosition = -1; // for those cases where the moveOperation is not relevant; QTextCursor::MoveOperation moveOperation = QTextCursor::NoMove; KoTextEditor *textEditor = m_textEditor.data(); m_tablePenMode = false; // keypress always stops the table (border) pen mode Q_ASSERT(textEditor); if (event->key() == Qt::Key_Backspace) { if (!textEditor->hasSelection() && textEditor->block().textList() && (textEditor->position() == textEditor->block().position()) && !(m_changeTracker && m_changeTracker->recordChanges())) { if (!textEditor->blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) { // backspace at beginning of numbered list item, makes it unnumbered textEditor->toggleListNumbering(false); } else { KoListLevelProperties llp; llp.setStyle(KoListStyle::None); llp.setLevel(0); // backspace on numbered, empty parag, removes numbering. textEditor->setListProperties(llp); } } else if (textEditor->position() > 0 || textEditor->hasSelection()) { if (!textEditor->hasSelection() && event->modifiers() & Qt::ControlModifier) { // delete prev word. textEditor->movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor); } textEditor->deletePreviousChar(); editingPluginEvents(); } } else if ((event->key() == Qt::Key_Tab) && ((!textEditor->hasSelection() && (textEditor->position() == textEditor->block().position())) || (textEditor->block().document()->findBlock(textEditor->anchor()) != textEditor->block().document()->findBlock(textEditor->position()))) && textEditor->block().textList()) { ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::IncreaseLevel; ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, 1); textEditor->addCommand(cll); editingPluginEvents(); } else if ((event->key() == Qt::Key_Backtab) && ((!textEditor->hasSelection() && (textEditor->position() == textEditor->block().position())) || (textEditor->block().document()->findBlock(textEditor->anchor()) != textEditor->block().document()->findBlock(textEditor->position()))) && textEditor->block().textList() && !(m_changeTracker && m_changeTracker->recordChanges())) { ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::DecreaseLevel; ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, 1); textEditor->addCommand(cll); editingPluginEvents(); } else if (event->key() == Qt::Key_Delete) { if (!textEditor->hasSelection() && event->modifiers() & Qt::ControlModifier) {// delete next word. textEditor->movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor); } // the event only gets through when the Del is not used in the app // if the app forwards Del then deleteSelection is used textEditor->deleteChar(); editingPluginEvents(); } else if ((event->key() == Qt::Key_Left) && (event->modifiers() & Qt::ControlModifier) == 0) { moveOperation = QTextCursor::Left; } else if ((event->key() == Qt::Key_Right) && (event->modifiers() & Qt::ControlModifier) == 0) { moveOperation = QTextCursor::Right; } else if ((event->key() == Qt::Key_Up) && (event->modifiers() & Qt::ControlModifier) == 0) { moveOperation = QTextCursor::Up; } else if ((event->key() == Qt::Key_Down) && (event->modifiers() & Qt::ControlModifier) == 0) { moveOperation = QTextCursor::Down; } else { // check for shortcuts. QKeySequence item(event->key() | ((Qt::ControlModifier | Qt::AltModifier) & event->modifiers())); if (hit(item, KStandardShortcut::Begin)) // Goto beginning of the document. Default: Ctrl-Home { destinationPosition = 0; } else if (hit(item, KStandardShortcut::End)) { // Goto end of the document. Default: Ctrl-End if (m_textShapeData) { QTextBlock last = m_textShapeData->document()->lastBlock(); destinationPosition = last.position() + last.length() - 1; } } else if (hit(item, KStandardShortcut::Prior)) { // page up // Scroll up one page. Default: Prior event->ignore(); // let app level actions handle it return; } else if (hit(item, KStandardShortcut::Next)) { // Scroll down one page. Default: Next event->ignore(); // let app level actions handle it return; } else if (hit(item, KStandardShortcut::BeginningOfLine)) // Goto beginning of current line. Default: Home { moveOperation = QTextCursor::StartOfLine; } else if (hit(item, KStandardShortcut::EndOfLine)) // Goto end of current line. Default: End { moveOperation = QTextCursor::EndOfLine; } else if (hit(item, KStandardShortcut::BackwardWord)) { moveOperation = QTextCursor::WordLeft; } else if (hit(item, KStandardShortcut::ForwardWord)) { moveOperation = QTextCursor::WordRight; } #ifdef Q_OS_OSX // Don't reject "alt" key, it may be used for typing text on Mac OS else if ((event->modifiers() & Qt::ControlModifier) || event->text().length() == 0) { #else else if ((event->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) || event->text().length() == 0) { #endif event->ignore(); return; } else if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)) { m_prevCursorPosition = textEditor->position(); textEditor->newLine(); updateActions(); editingPluginEvents(); } else if ((event->key() == Qt::Key_Tab || !(event->text().length() == 1 && !event->text().at(0).isPrint()))) { // insert the text m_prevCursorPosition = textEditor->position(); startingSimpleEdit(); //signal editing plugins that this is a simple edit textEditor->insertText(event->text()); editingPluginEvents(); } } if (moveOperation != QTextCursor::NoMove || destinationPosition != -1) { useCursor(Qt::BlankCursor); bool shiftPressed = event->modifiers() & Qt::ShiftModifier; if (textEditor->hasSelection()) { repaintSelection(); // will erase selection } else { repaintCaret(); } QTextBlockFormat format = textEditor->blockFormat(); KoText::Direction dir = static_cast(format.intProperty(KoParagraphStyle::TextProgressionDirection)); bool isRtl; if (dir == KoText::AutoDirection) { isRtl = textEditor->block().text().isRightToLeft(); } else { isRtl = dir == KoText::RightLeftTopBottom; } if (isRtl) { // if RTL toggle direction of cursor movement. switch (moveOperation) { case QTextCursor::Left: moveOperation = QTextCursor::Right; break; case QTextCursor::Right: moveOperation = QTextCursor::Left; break; case QTextCursor::WordRight: moveOperation = QTextCursor::WordLeft; break; case QTextCursor::WordLeft: moveOperation = QTextCursor::WordRight; break; default: break; } } int prevPosition = textEditor->position(); if (moveOperation != QTextCursor::NoMove) textEditor->movePosition(moveOperation, shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); else textEditor->setPosition(destinationPosition, shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); if (moveOperation == QTextCursor::Down && prevPosition == textEditor->position()) { // change behavior a little big from Qt; at the bottom of the doc we go to the end of the doc textEditor->movePosition(QTextCursor::End, shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); } if (shiftPressed) { // altered selection. repaintSelection(); } else { repaintCaret(); } updateActions(); editingPluginEvents(); } if (m_caretTimer.isActive()) { // make the caret not blink but decide on the action if its visible or not. m_caretTimer.stop(); m_caretTimer.setInterval(50); m_caretTimer.start(); m_caretTimerState = true; // turn caret on while typing } if (moveOperation != QTextCursor::NoMove) // this difference in handling is need to prevent leaving a trail of old cursors onscreen { ensureCursorVisible(); } else { m_delayedEnsureVisible = true; } updateActions(); updateSelectionHandler(); } QVariant TextTool::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor || !m_textShapeData) { return QVariant(); } switch (query) { case Qt::ImMicroFocus: { // The rectangle covering the area of the input cursor in widget coordinates. QRectF rect = caretRect(textEditor->cursor()); rect.moveTop(rect.top() - m_textShapeData->documentOffset()); QTransform shapeMatrix = m_textShape->absoluteTransformation(&converter); qreal zoomX, zoomY; converter.zoom(&zoomX, &zoomY); shapeMatrix.scale(zoomX, zoomY); rect = shapeMatrix.mapRect(rect); return rect.toRect(); } case Qt::ImFont: // The currently used font for text input. return textEditor->charFormat().font(); case Qt::ImCursorPosition: // The logical position of the cursor within the text surrounding the input area (see ImSurroundingText). return textEditor->position() - textEditor->block().position(); case Qt::ImSurroundingText: // The plain text around the input area, for example the current paragraph. return textEditor->block().text(); case Qt::ImCurrentSelection: // The currently selected text. return textEditor->selectedText(); default: ; // Qt 4.6 adds ImMaximumTextLength and ImAnchorPosition } return QVariant(); } void TextTool::inputMethodEvent(QInputMethodEvent *event) { KoTextEditor *textEditor = m_textEditor.data(); if (textEditor == 0) { return; } if (event->replacementLength() > 0) { textEditor->setPosition(textEditor->position() + event->replacementStart()); for (int i = event->replacementLength(); i > 0; --i) { textEditor->deleteChar(); } } if (!event->commitString().isEmpty()) { QKeyEvent ke(QEvent::KeyPress, -1, 0, event->commitString()); keyPressEvent(&ke); // The cursor may reside in a different block before vs. after keyPressEvent. QTextBlock block = textEditor->block(); QTextLayout *layout = block.layout(); Q_ASSERT(layout); layout->setPreeditArea(-1, QString()); } else { QTextBlock block = textEditor->block(); QTextLayout *layout = block.layout(); Q_ASSERT(layout); layout->setPreeditArea(textEditor->position() - block.position(), event->preeditString()); const_cast(textEditor->document())->markContentsDirty(textEditor->position(), event->preeditString().length()); } event->accept(); } void TextTool::ensureCursorVisible(bool moveView) { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor || !m_textShapeData) { return; } bool upToDate; QRectF cRect = caretRect(textEditor->cursor(), &upToDate); KoTextDocumentLayout *lay = qobject_cast(m_textShapeData->document()->documentLayout()); Q_ASSERT(lay); KoTextLayoutRootArea *rootArea = lay->rootAreaForPoint(cRect.center()); if (rootArea && rootArea->associatedShape() && m_textShapeData->rootArea() != rootArea) { // If we have changed root area we need to update m_textShape and m_textShapeData m_textShape = static_cast(rootArea->associatedShape()); Q_ASSERT(m_textShape); disconnect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved())); m_textShapeData = static_cast(m_textShape->userData()); Q_ASSERT(m_textShapeData); connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved())); } if (!moveView) { return; } if (!upToDate) { // paragraph is not yet layouted. // The number one usecase for this is when the user pressed enter. // try to do it on next caret blink m_delayedEnsureVisible = true; return; // we shouldn't move to an obsolete position } cRect.moveTop(cRect.top() - m_textShapeData->documentOffset()); canvas()->ensureVisible(m_textShape->absoluteTransformation(0).mapRect(cRect)); } void TextTool::keyReleaseEvent(QKeyEvent *event) { event->accept(); } void TextTool::updateActions() { bool notInAnnotation = true; // no annotation shape anymore! KoTextEditor *textEditor = m_textEditor.data(); if (textEditor == 0) { return; } m_allowActions = false; //Update the characterStyle related GUI elements QTextCharFormat cf = textEditor->charFormat(); m_actionFormatBold->setChecked(cf.fontWeight() > QFont::Normal); m_actionFormatItalic->setChecked(cf.fontItalic()); m_actionFormatUnderline->setChecked(cf.intProperty(KoCharacterStyle::UnderlineType) != KoCharacterStyle::NoLineType); m_actionFormatStrikeOut->setChecked(cf.intProperty(KoCharacterStyle::StrikeOutType) != KoCharacterStyle::NoLineType); bool super = false, sub = false; switch (cf.verticalAlignment()) { case QTextCharFormat::AlignSuperScript: super = true; break; case QTextCharFormat::AlignSubScript: sub = true; break; default:; } m_actionFormatSuper->setChecked(super); m_actionFormatSub->setChecked(sub); m_actionFormatFontSize->setFontSize(cf.font().pointSizeF()); m_actionFormatFontFamily->setFont(cf.font().family()); KoTextShapeData::ResizeMethod resizemethod = KoTextShapeData::AutoResize; if (m_textShapeData) { resizemethod = m_textShapeData->resizeMethod(); } m_shrinkToFitAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation); m_shrinkToFitAction->setChecked(resizemethod == KoTextShapeData::ShrinkToFitResize); m_growWidthAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation); m_growWidthAction->setChecked(resizemethod == KoTextShapeData::AutoGrowWidth || resizemethod == KoTextShapeData::AutoGrowWidthAndHeight); m_growHeightAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation); m_growHeightAction->setChecked(resizemethod == KoTextShapeData::AutoGrowHeight || resizemethod == KoTextShapeData::AutoGrowWidthAndHeight); //update paragraphStyle GUI element QTextBlockFormat bf = textEditor->blockFormat(); if (bf.hasProperty(KoParagraphStyle::TextProgressionDirection)) { switch (bf.intProperty(KoParagraphStyle::TextProgressionDirection)) { case KoText::RightLeftTopBottom: m_actionChangeDirection->setChecked(true); break; case KoText::LeftRightTopBottom: default: m_actionChangeDirection->setChecked(false); break; } } else { m_actionChangeDirection->setChecked(textEditor->block().text().isRightToLeft()); } if (bf.alignment() == Qt::AlignLeading || bf.alignment() == Qt::AlignTrailing) { bool revert = (textEditor->block().layout()->textOption().textDirection() == Qt::RightToLeft); if ((bf.alignment() == Qt::AlignLeading) ^ revert) { m_actionAlignLeft->setChecked(true); } else { m_actionAlignRight->setChecked(true); } } else if (bf.alignment() == Qt::AlignHCenter) { m_actionAlignCenter->setChecked(true); } if (bf.alignment() == Qt::AlignJustify) { m_actionAlignBlock->setChecked(true); } else if (bf.alignment() == (Qt::AlignLeft | Qt::AlignAbsolute)) { m_actionAlignLeft->setChecked(true); } else if (bf.alignment() == (Qt::AlignRight | Qt::AlignAbsolute)) { m_actionAlignRight->setChecked(true); } if (textEditor->block().textList()) { QTextListFormat listFormat = textEditor->block().textList()->format(); if (listFormat.intProperty(KoListStyle::Level) > 1) { m_actionFormatDecreaseIndent->setEnabled(true); } else { m_actionFormatDecreaseIndent->setEnabled(false); } if (listFormat.intProperty(KoListStyle::Level) < 10) { m_actionFormatIncreaseIndent->setEnabled(true); } else { m_actionFormatIncreaseIndent->setEnabled(false); } } else { m_actionFormatDecreaseIndent->setEnabled(textEditor->blockFormat().leftMargin() > 0.); } m_allowActions = true; bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceProvider::ApplicationSpeciality) & KoCanvasResourceProvider::NoAdvancedText); if (useAdvancedText) { action("insert_table")->setEnabled(notInAnnotation); bool hasTable = textEditor->currentTable(); action("insert_tablerow_above")->setEnabled(hasTable && notInAnnotation); action("insert_tablerow_below")->setEnabled(hasTable && notInAnnotation); action("insert_tablecolumn_left")->setEnabled(hasTable && notInAnnotation); action("insert_tablecolumn_right")->setEnabled(hasTable && notInAnnotation); action("delete_tablerow")->setEnabled(hasTable && notInAnnotation); action("delete_tablecolumn")->setEnabled(hasTable && notInAnnotation); action("merge_tablecells")->setEnabled(hasTable && notInAnnotation); action("split_tablecells")->setEnabled(hasTable && notInAnnotation); action("activate_borderpainter")->setEnabled(hasTable && notInAnnotation); } action("insert_annotation")->setEnabled(notInAnnotation); ///TODO if selection contains several different format emit blockChanged(textEditor->block()); emit charFormatChanged(cf, textEditor->blockCharFormat()); emit blockFormatChanged(bf); } QMenu *TextTool::popupActionsMenu() { return m_contextMenu.data(); } void TextTool::updateStyleManager() { if (!m_textShapeData) { return; } KoStyleManager *styleManager = KoTextDocument(m_textShapeData->document()).styleManager(); emit styleManagerChanged(styleManager); //TODO move this to its own method m_changeTracker = KoTextDocument(m_textShapeData->document()).changeTracker(); } void TextTool::activate(ToolActivation activation, const QSet &shapes) { KoToolBase::activate(activation, shapes); m_caretTimer.start(); m_caretTimerState = true; foreach (KoShape *shape, shapes) { m_textShape = dynamic_cast(shape); if (m_textShape) { break; } } if (!m_textShape) { // none found emit done(); // This is how we inform the rulers of the active range // No shape means no active range canvas()->resourceManager()->setResource(KoCanvasResourceProvider::ActiveRange, QVariant(QRectF())); return; } // This is how we inform the rulers of the active range // For now we will not consider table cells, but just give the shape dimensions QVariant v; QRectF rect(QPoint(), m_textShape->size()); rect = m_textShape->absoluteTransformation(0).mapRect(rect); v.setValue(rect); canvas()->resourceManager()->setResource(KoCanvasResourceProvider::ActiveRange, v); if ((!m_oldTextEditor.isNull()) && m_oldTextEditor.data()->document() != static_cast(m_textShape->userData())->document()) { m_oldTextEditor.data()->setPosition(m_oldTextEditor.data()->position()); //we need to redraw like this so we update the old textshape wherever it may be if (canvas()->canvasWidget()) { canvas()->canvasWidget()->update(); } } setShapeData(static_cast(m_textShape->userData())); useCursor(Qt::IBeamCursor); updateStyleManager(); repaintSelection(); updateSelectionHandler(); updateActions(); if (m_specialCharacterDocker) { m_specialCharacterDocker->setEnabled(true); } } void TextTool::deactivate() { m_caretTimer.stop(); m_caretTimerState = false; repaintCaret(); m_textShape = 0; // This is how we inform the rulers of the active range // No shape means no active range canvas()->resourceManager()->setResource(KoCanvasResourceProvider::ActiveRange, QVariant(QRectF())); m_oldTextEditor = m_textEditor; setShapeData(0); updateSelectionHandler(); if (m_specialCharacterDocker) { m_specialCharacterDocker->setEnabled(false); m_specialCharacterDocker->setVisible(false); } KoToolBase::deactivate(); } void TextTool::repaintDecorations() { if (m_textShapeData) { repaintSelection(); } } void TextTool::repaintCaret() { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor || !m_textShapeData) { return; } KoTextDocumentLayout *lay = qobject_cast(m_textShapeData->document()->documentLayout()); Q_ASSERT(lay); Q_UNUSED(lay); // If we have changed root area we need to update m_textShape and m_textShapeData if (m_delayedEnsureVisible) { m_delayedEnsureVisible = false; ensureCursorVisible(); return; } ensureCursorVisible(false); // ensures the various vars are updated bool upToDate; QRectF repaintRect = caretRect(textEditor->cursor(), &upToDate); repaintRect.moveTop(repaintRect.top() - m_textShapeData->documentOffset()); if (repaintRect.isValid()) { repaintRect = m_textShape->absoluteTransformation(0).mapRect(repaintRect); // Make sure there is enough space to show an icon QRectF iconSize = canvas()->viewConverter()->viewToDocument(QRect(0, 0, 18, 18)); repaintRect.setX(repaintRect.x() - iconSize.width() / 2); repaintRect.setRight(repaintRect.right() + iconSize.width() / 2); repaintRect.setTop(repaintRect.y() - iconSize.height() / 2); repaintRect.setBottom(repaintRect.bottom() + iconSize.height() / 2); canvas()->updateCanvas(repaintRect); } } void TextTool::repaintSelection() { KoTextEditor *textEditor = m_textEditor.data(); if (textEditor == 0) { return; } QTextCursor cursor = *textEditor->cursor(); QList shapes; KoTextDocumentLayout *lay = qobject_cast(textEditor->document()->documentLayout()); Q_ASSERT(lay); foreach (KoShape *shape, lay->shapes()) { TextShape *textShape = dynamic_cast(shape); if (textShape == 0) { // when the shape is being deleted its no longer a TextShape but a KoShape continue; } //Q_ASSERT(!shapes.contains(textShape)); if (!shapes.contains(textShape)) { shapes.append(textShape); } } // loop over all shapes that contain the text and update per shape. QRectF repaintRect = textRect(cursor); foreach (TextShape *ts, shapes) { QRectF rect = repaintRect; rect.moveTop(rect.y() - ts->textShapeData()->documentOffset()); rect = ts->absoluteTransformation(0).mapRect(rect); QRectF r = ts->boundingRect().intersected(rect); canvas()->updateCanvas(r); } } QRectF TextTool::caretRect(QTextCursor *cursor, bool *upToDate) const { QTextCursor tmpCursor(*cursor); tmpCursor.setPosition(cursor->position()); // looses the anchor QRectF rect = textRect(tmpCursor); if (rect.size() == QSizeF(0, 0)) { if (upToDate) { *upToDate = false; } rect = m_lastImMicroFocus; // prevent block changed but layout not done } else { if (upToDate) { *upToDate = true; } m_lastImMicroFocus = rect; } return rect; } QRectF TextTool::textRect(QTextCursor &cursor) const { if (!m_textShapeData) { return QRectF(); } KoTextEditor *textEditor = m_textEditor.data(); KoTextDocumentLayout *lay = qobject_cast(textEditor->document()->documentLayout()); return lay->selectionBoundingBox(cursor); } KoToolSelection *TextTool::selection() { return m_toolSelection; } QList > TextTool::createOptionWidgets() { QList > widgets; SimpleCharacterWidget *scw = new SimpleCharacterWidget(this, 0); SimpleParagraphWidget *spw = new SimpleParagraphWidget(this, 0); if (m_textEditor.data()) { // connect(m_textEditor.data(), SIGNAL(paragraphStyleApplied(KoParagraphStyle*)), spw, SLOT(slotParagraphStyleApplied(KoParagraphStyle*))); // connect(m_textEditor.data(), SIGNAL(characterStyleApplied(KoCharacterStyle*)), scw, SLOT(slotCharacterStyleApplied(KoCharacterStyle*))); //initialise the char- and par- widgets with the current block and formats. scw->setCurrentBlockFormat(m_textEditor.data()->blockFormat()); scw->setCurrentFormat(m_textEditor.data()->charFormat(), m_textEditor.data()-> blockCharFormat()); spw->setCurrentBlock(m_textEditor.data()->block()); spw->setCurrentFormat(m_textEditor.data()->blockFormat()); } SimpleTableWidget *stw = new SimpleTableWidget(this, 0); SimpleInsertWidget *siw = new SimpleInsertWidget(this, 0); /* We do not use these for now. Let's see if they become useful at a certain point in time. If not, we can remove the whole chain (SimpleCharWidget, SimpleParWidget, DockerStyleComboModel) if (m_textShapeData && KoTextDocument(m_textShapeData->document()).styleManager()) { scw->setInitialUsedStyles(KoTextDocument(m_textShapeData->document()).styleManager()->usedCharacterStyles()); spw->setInitialUsedStyles(KoTextDocument(m_textShapeData->document()).styleManager()->usedParagraphStyles()); } */ // Connect to/with simple character widget (docker) connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), scw, SLOT(setStyleManager(KoStyleManager*))); connect(this, SIGNAL(charFormatChanged(QTextCharFormat,QTextCharFormat)), scw, SLOT(setCurrentFormat(QTextCharFormat,QTextCharFormat))); connect(this, SIGNAL(blockFormatChanged(QTextBlockFormat)), scw, SLOT(setCurrentBlockFormat(QTextBlockFormat))); connect(scw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas())); connect(scw, SIGNAL(characterStyleSelected(KoCharacterStyle*)), this, SLOT(setStyle(KoCharacterStyle*))); connect(scw, SIGNAL(newStyleRequested(QString)), this, SLOT(createStyleFromCurrentCharFormat(QString))); connect(scw, SIGNAL(showStyleManager(int)), this, SLOT(showStyleManager(int))); // Connect to/with simple paragraph widget (docker) connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), spw, SLOT(setStyleManager(KoStyleManager*))); connect(this, SIGNAL(blockChanged(QTextBlock)), spw, SLOT(setCurrentBlock(QTextBlock))); connect(this, SIGNAL(blockFormatChanged(QTextBlockFormat)), spw, SLOT(setCurrentFormat(QTextBlockFormat))); connect(spw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas())); connect(spw, SIGNAL(paragraphStyleSelected(KoParagraphStyle*)), this, SLOT(setStyle(KoParagraphStyle*))); connect(spw, SIGNAL(newStyleRequested(QString)), this, SLOT(createStyleFromCurrentBlockFormat(QString))); connect(spw, SIGNAL(showStyleManager(int)), this, SLOT(showStyleManager(int))); // Connect to/with simple table widget (docker) connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), stw, SLOT(setStyleManager(KoStyleManager*))); connect(stw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas())); connect(stw, SIGNAL(tableBorderDataUpdated(KoBorder::BorderData)), this, SLOT(setTableBorderData(KoBorder::BorderData))); // Connect to/with simple insert widget (docker) connect(siw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas())); connect(siw, SIGNAL(insertTableQuick(int,int)), this, SLOT(insertTableQuick(int,int))); updateStyleManager(); if (m_textShape) { updateActions(); } scw->setWindowTitle(i18n("Character")); widgets.append(scw); spw->setWindowTitle(i18n("Paragraph")); widgets.append(spw); bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceProvider::ApplicationSpeciality) & KoCanvasResourceProvider::NoAdvancedText); if (useAdvancedText) { stw->setWindowTitle(i18n("Table")); widgets.append(stw); siw->setWindowTitle(i18n("Insert")); widgets.append(siw); } return widgets; } void TextTool::returnFocusToCanvas() { canvas()->canvasWidget()->setFocus(); } void TextTool::startEditing(KUndo2Command *command) { m_currentCommand = command; m_currentCommandHasChildren = true; } void TextTool::stopEditing() { m_currentCommand = 0; m_currentCommandHasChildren = false; } void TextTool::insertNewSection() { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor) { return; } textEditor->newSection(); } void TextTool::configureSection() { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor) { return; } SectionFormatDialog *dia = new SectionFormatDialog(0, m_textEditor.data()); dia->exec(); delete dia; returnFocusToCanvas(); updateActions(); } void TextTool::splitSections() { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor) { return; } SectionsSplitDialog *dia = new SectionsSplitDialog(0, m_textEditor.data()); dia->exec(); delete dia; returnFocusToCanvas(); updateActions(); } void TextTool::pasteAsText() { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor) { return; } const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Clipboard); // on windows we do not have data if we try to paste this selection if (!data) { return; } if (data->hasFormat(KoOdf::mimeType(KoOdf::Text)) || data->hasText()) { m_prevCursorPosition = m_textEditor.data()->position(); m_textEditor.data()->paste(canvas(), data, true); editingPluginEvents(); } } void TextTool::bold(bool bold) { m_textEditor.data()->bold(bold); } void TextTool::italic(bool italic) { m_textEditor.data()->italic(italic); } void TextTool::underline(bool underline) { m_textEditor.data()->underline(underline); } void TextTool::strikeOut(bool strikeOut) { m_textEditor.data()->strikeOut(strikeOut); } void TextTool::nonbreakingSpace() { if (!m_allowActions || !m_textEditor.data()) { return; } m_textEditor.data()->insertText(QString(QChar(Qt::Key_nobreakspace))); } void TextTool::nonbreakingHyphen() { if (!m_allowActions || !m_textEditor.data()) { return; } m_textEditor.data()->insertText(QString(QChar(0x2013))); } void TextTool::softHyphen() { if (!m_allowActions || !m_textEditor.data()) { return; } m_textEditor.data()->insertText(QString(QChar(Qt::Key_hyphen))); } void TextTool::lineBreak() { if (!m_allowActions || !m_textEditor.data()) { return; } m_textEditor.data()->insertText(QString(QChar(0x2028))); } void TextTool::alignLeft() { if (!m_allowActions || !m_textEditor.data()) { return; } m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignLeft | Qt::AlignAbsolute); } void TextTool::alignRight() { if (!m_allowActions || !m_textEditor.data()) { return; } m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignRight | Qt::AlignAbsolute); } void TextTool::alignCenter() { if (!m_allowActions || !m_textEditor.data()) { return; } m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignHCenter); } void TextTool::alignBlock() { if (!m_allowActions || !m_textEditor.data()) { return; } m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignJustify); } void TextTool::superScript(bool on) { if (!m_allowActions || !m_textEditor.data()) { return; } if (on) { m_actionFormatSub->setChecked(false); } m_textEditor.data()->setVerticalTextAlignment(on ? Qt::AlignTop : Qt::AlignVCenter); } void TextTool::subScript(bool on) { if (!m_allowActions || !m_textEditor.data()) { return; } if (on) { m_actionFormatSuper->setChecked(false); } m_textEditor.data()->setVerticalTextAlignment(on ? Qt::AlignBottom : Qt::AlignVCenter); } void TextTool::increaseIndent() { if (!m_allowActions || !m_textEditor.data()) { return; } if (m_textEditor.data()->block().textList()) { ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::IncreaseLevel; ChangeListLevelCommand *cll = new ChangeListLevelCommand(*(m_textEditor.data()->cursor()), type, 1); m_textEditor.data()->addCommand(cll); editingPluginEvents(); } else { m_textEditor.data()->increaseIndent(); } updateActions(); } void TextTool::decreaseIndent() { if (!m_allowActions || !m_textEditor.data()) { return; } if (m_textEditor.data()->block().textList()) { ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::DecreaseLevel; ChangeListLevelCommand *cll = new ChangeListLevelCommand(*(m_textEditor.data()->cursor()), type, 1); m_textEditor.data()->addCommand(cll); editingPluginEvents(); } else { m_textEditor.data()->decreaseIndent(); } updateActions(); } void TextTool::decreaseFontSize() { if (!m_allowActions || !m_textEditor.data()) { return; } m_textEditor.data()->decreaseFontSize(); } void TextTool::increaseFontSize() { if (!m_allowActions || !m_textEditor.data()) { return; } m_textEditor.data()->increaseFontSize(); } void TextTool::setFontFamily(const QString &font) { if (!m_allowActions || !m_textEditor.data()) { return; } m_textEditor.data()->setFontFamily(font); } void TextTool::setFontSize(qreal size) { if (!m_allowActions || !m_textEditor.data() || m_textEditor.isNull()) { return; } m_textEditor.data()->setFontSize(size); } void TextTool::insertIndexMarker() { // TODO handle result when we figure out how to report errors from a tool. m_textEditor.data()->insertIndexMarker(); } void TextTool::insertFrameBreak() { m_textEditor.data()->insertFrameBreak(); ensureCursorVisible(); m_delayedEnsureVisible = true; } void TextTool::setStyle(KoCharacterStyle *style) { KoCharacterStyle *charStyle = style; //if the given KoCharacterStyle is null, set the KoParagraphStyle character properties if (!charStyle) { charStyle = static_cast(KoTextDocument(m_textShapeData->document()).styleManager()->paragraphStyle(m_textEditor.data()->blockFormat().intProperty(KoParagraphStyle::StyleId))); } if (charStyle) { m_textEditor.data()->setStyle(charStyle); updateActions(); } } void TextTool::setStyle(KoParagraphStyle *style) { m_textEditor.data()->setStyle(style); updateActions(); } void TextTool::insertTable() { TableDialog *dia = new TableDialog(0); if (dia->exec() == TableDialog::Accepted) { m_textEditor.data()->insertTable(dia->rows(), dia->columns()); } delete dia; updateActions(); } void TextTool::insertTableQuick(int rows, int columns) { m_textEditor.data()->insertTable(rows, columns); updateActions(); } void TextTool::insertTableRowAbove() { m_textEditor.data()->insertTableRowAbove(); } void TextTool::insertTableRowBelow() { m_textEditor.data()->insertTableRowBelow(); } void TextTool::insertTableColumnLeft() { m_textEditor.data()->insertTableColumnLeft(); } void TextTool::insertTableColumnRight() { m_textEditor.data()->insertTableColumnRight(); } void TextTool::deleteTableColumn() { m_textEditor.data()->deleteTableColumn(); } void TextTool::deleteTableRow() { m_textEditor.data()->deleteTableRow(); } void TextTool::mergeTableCells() { m_textEditor.data()->mergeTableCells(); } void TextTool::splitTableCells() { m_textEditor.data()->splitTableCells(); } void TextTool::useTableBorderCursor() { static const unsigned char data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x80, 0x7e, 0x00, 0x00, 0x40, 0x3f, 0x00, 0x00, 0xa0, 0x1f, 0x00, 0x00, 0xd0, 0x0f, 0x00, 0x00, 0xe8, 0x07, 0x00, 0x00, 0xf4, 0x03, 0x00, 0x00, 0xe4, 0x01, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x40, 0x32, 0x00, 0x00, 0xa0, 0x0f, 0x00, 0x00, 0xd0, 0x0f, 0x00, 0x00, 0xd0, 0x0f, 0x00, 0x00, 0xe8, 0x07, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; QBitmap result(32, 32); result.fill(Qt::color0); QPainter painter(&result); painter.drawPixmap(0, 0, QBitmap::fromData(QSize(25, 23), data)); QBitmap brushMask = result.createHeuristicMask(false); useCursor(QCursor(result, brushMask, 1, 21)); } void TextTool::setTableBorderData(const KoBorder::BorderData &data) { m_tablePenMode = true; m_tablePenBorderData = data; } void TextTool::formatParagraph() { ParagraphSettingsDialog *dia = new ParagraphSettingsDialog(this, m_textEditor.data()); dia->setUnit(canvas()->unit()); dia->setImageCollection(m_textShape->imageCollection()); dia->exec(); delete dia; returnFocusToCanvas(); } void TextTool::testSlot(bool on) { qDebug() << "signal received. bool:" << on; } void TextTool::selectAll() { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor || !m_textShapeData) { return; } const int selectionLength = qAbs(textEditor->position() - textEditor->anchor()); textEditor->movePosition(QTextCursor::End); textEditor->setPosition(0, QTextCursor::KeepAnchor); repaintSelection(); if (selectionLength != qAbs(textEditor->position() - textEditor->anchor())) { // it actually changed emit selectionChanged(true); } } void TextTool::startMacro(const QString &title) { if (title != i18n("Key Press") && title != i18n("Autocorrection")) { //dirty hack while waiting for refactor of text editing m_textTyping = false; } else { m_textTyping = true; } if (title != i18n("Delete") && title != i18n("Autocorrection")) { //same dirty hack as above m_textDeleting = false; } else { m_textDeleting = true; } if (m_currentCommand) { return; } class MacroCommand : public KUndo2Command { public: MacroCommand(const KUndo2MagicString &title) : KUndo2Command(title), m_first(true) {} void redo() override { if (!m_first) { KUndo2Command::redo(); } m_first = false; } bool mergeWith(const KUndo2Command *) override { return false; } bool m_first; }; /** * FIXME: The messages generated by the Text Tool might not be * properly translated, since we don't control it in * type-safe way. * * The title is already translated string, we just don't * have any type control over it. */ KUndo2MagicString title_workaround = kundo2_noi18n(title); m_currentCommand = new MacroCommand(title_workaround); m_currentCommandHasChildren = false; } void TextTool::stopMacro() { if (!m_currentCommand) { return; } if (!m_currentCommandHasChildren) { delete m_currentCommand; } m_currentCommand = 0; } void TextTool::showStyleManager(int styleId) { if (!m_textShapeData) { return; } KoStyleManager *styleManager = KoTextDocument(m_textShapeData->document()).styleManager(); Q_ASSERT(styleManager); if (!styleManager) { return; //don't crash } StyleManagerDialog *dia = new StyleManagerDialog(canvas()->canvasWidget()); dia->setStyleManager(styleManager); dia->setUnit(canvas()->unit()); KoParagraphStyle *paragraphStyle = styleManager->paragraphStyle(styleId); if (paragraphStyle) { dia->setParagraphStyle(paragraphStyle); } KoCharacterStyle *characterStyle = styleManager->characterStyle(styleId); if (characterStyle) { dia->setCharacterStyle(characterStyle); } dia->show(); } void TextTool::startTextEditingPlugin(const QString &pluginId) { KoTextEditingPlugin *plugin = textEditingPluginContainer()->plugin(pluginId); if (plugin) { if (m_textEditor.data()->hasSelection()) { plugin->checkSection(m_textShapeData->document(), m_textEditor.data()->selectionStart(), m_textEditor.data()->selectionEnd()); } else { plugin->finishedWord(m_textShapeData->document(), m_textEditor.data()->position()); } } } void TextTool::canvasResourceChanged(int key, const QVariant &var) { if (m_textEditor.isNull()) { return; } if (!m_textShapeData) { return; } if (m_allowResourceManagerUpdates == false) { return; } if (key == KoText::CurrentTextPosition) { repaintSelection(); m_textEditor.data()->setPosition(var.toInt()); ensureCursorVisible(); } else if (key == KoText::CurrentTextAnchor) { repaintSelection(); int pos = m_textEditor.data()->position(); m_textEditor.data()->setPosition(var.toInt()); m_textEditor.data()->setPosition(pos, QTextCursor::KeepAnchor); } else if (key == KoCanvasResourceProvider::Unit) { m_unit = var.value(); } else { return; } repaintSelection(); } void TextTool::insertSpecialCharacter() { if (m_specialCharacterDocker == 0) { m_specialCharacterDocker = new InsertCharacter(canvas()->canvasWidget()); connect(m_specialCharacterDocker, SIGNAL(insertCharacter(QString)), this, SLOT(insertString(QString))); } m_specialCharacterDocker->show(); } void TextTool::insertString(const QString &string) { m_textEditor.data()->insertText(string); returnFocusToCanvas(); } void TextTool::selectFont() { FontDia *fontDlg = new FontDia(m_textEditor.data()); fontDlg->exec(); delete fontDlg; returnFocusToCanvas(); } void TextTool::shapeAddedToCanvas() { qDebug(); if (m_textShape) { KoSelection *selection = canvas()->selectedShapesProxy()->selection(); KoShape *shape = selection->firstSelectedShape(); if (shape != m_textShape && canvas()->shapeManager()->shapes().contains(m_textShape)) { // this situation applies when someone, not us, changed the selection by selecting another // text shape. Possibly by adding one. // Deselect the new shape again, so we can keep editing what we were already editing selection->select(m_textShape); selection->deselect(shape); } } } void TextTool::shapeDataRemoved() { m_textShapeData = 0; m_textShape = 0; if (!m_textEditor.isNull() && !m_textEditor.data()->cursor()->isNull()) { const QTextDocument *doc = m_textEditor.data()->document(); Q_ASSERT(doc); KoTextDocumentLayout *lay = qobject_cast(doc->documentLayout()); if (!lay || lay->shapes().isEmpty()) { emit done(); return; } m_textShape = static_cast(lay->shapes().first()); m_textShapeData = static_cast(m_textShape->userData()); connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved())); } } void TextTool::createStyleFromCurrentBlockFormat(const QString &name) { KoTextDocument document(m_textShapeData->document()); KoStyleManager *styleManager = document.styleManager(); KoParagraphStyle *paragraphStyle = new KoParagraphStyle(m_textEditor.data()->blockFormat(), m_textEditor.data()->charFormat()); paragraphStyle->setName(name); styleManager->add(paragraphStyle); m_textEditor.data()->setStyle(paragraphStyle); emit charFormatChanged(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat()); emit blockFormatChanged(m_textEditor.data()->blockFormat()); } void TextTool::createStyleFromCurrentCharFormat(const QString &name) { KoCharacterStyle blankStyle; KoTextDocument document(m_textShapeData->document()); KoStyleManager *styleManager = document.styleManager(); KoCharacterStyle *originalCharStyle = styleManager->characterStyle(m_textEditor.data()->charFormat().intProperty(KoCharacterStyle::StyleId)); KoCharacterStyle *autoStyle; if (!originalCharStyle) { originalCharStyle = &blankStyle; autoStyle = originalCharStyle->autoStyle(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat()); autoStyle->setParentStyle(0); } else { autoStyle = originalCharStyle->autoStyle(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat()); } autoStyle->setName(name); styleManager->add(autoStyle); m_textEditor.data()->setStyle(autoStyle); emit charFormatChanged(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat()); } // ---------- editing plugins methods. void TextTool::editingPluginEvents() { if (m_prevCursorPosition == -1 || m_prevCursorPosition == m_textEditor.data()->position()) { qDebug() << "m_prevCursorPosition=" << m_prevCursorPosition << "m_textEditor.data()->position()=" << m_textEditor.data()->position(); return; } QTextBlock block = m_textEditor.data()->block(); if (!block.contains(m_prevCursorPosition)) { qDebug() << "m_prevCursorPosition=" << m_prevCursorPosition; finishedWord(); finishedParagraph(); m_prevCursorPosition = -1; } else { int from = m_prevCursorPosition; int to = m_textEditor.data()->position(); if (from > to) { std::swap(from, to); } QString section = block.text().mid(from - block.position(), to - from); qDebug() << "from=" << from << "to=" << to; if (section.contains(' ')) { finishedWord(); m_prevCursorPosition = -1; } } } void TextTool::finishedWord() { if (m_textShapeData && textEditingPluginContainer()) { foreach (KoTextEditingPlugin *plugin, textEditingPluginContainer()->values()) { plugin->finishedWord(m_textShapeData->document(), m_prevCursorPosition); } } } void TextTool::finishedParagraph() { if (m_textShapeData && textEditingPluginContainer()) { foreach (KoTextEditingPlugin *plugin, textEditingPluginContainer()->values()) { plugin->finishedParagraph(m_textShapeData->document(), m_prevCursorPosition); } } } void TextTool::startingSimpleEdit() { if (m_textShapeData && textEditingPluginContainer()) { foreach (KoTextEditingPlugin *plugin, textEditingPluginContainer()->values()) { plugin->startingSimpleEdit(m_textShapeData->document(), m_prevCursorPosition); } } } void TextTool::setTextColor(const KoColor &color) { m_textEditor.data()->setTextColor(color.toQColor()); } void TextTool::setBackgroundColor(const KoColor &color) { m_textEditor.data()->setTextBackgroundColor(color.toQColor()); } void TextTool::setGrowWidthToFit(bool enabled) { m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::AutoGrowWidth, enabled)); updateActions(); } void TextTool::setGrowHeightToFit(bool enabled) { m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::AutoGrowHeight, enabled)); updateActions(); } void TextTool::setShrinkToFit(bool enabled) { m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::ShrinkToFitResize, enabled)); updateActions(); } void TextTool::runUrl(KoPointerEvent *event, QString &url) { QUrl _url = QUrl::fromUserInput(url); if (!_url.isLocalFile()) { QDesktopServices::openUrl(_url); } event->accept(); } void TextTool::debugTextDocument() { #ifndef NDEBUG if (!m_textShapeData) { return; } const int CHARSPERLINE = 80; // TODO Make configurable using ENV var? const int CHARPOSITION = 278301935; KoTextDocument document(m_textShapeData->document()); KoStyleManager *styleManager = document.styleManager(); KoInlineTextObjectManager *inlineManager = document.inlineTextObjectManager(); QTextBlock block = m_textShapeData->document()->begin(); for (; block.isValid(); block = block.next()) { QVariant var = block.blockFormat().property(KoParagraphStyle::StyleId); if (!var.isNull()) { KoParagraphStyle *ps = styleManager->paragraphStyle(var.toInt()); qDebug() << "--- Paragraph Style:" << (ps ? ps->name() : QString()) << var.toInt(); } var = block.charFormat().property(KoCharacterStyle::StyleId); if (!var.isNull()) { KoCharacterStyle *cs = styleManager->characterStyle(var.toInt()); qDebug() << "--- Character Style:" << (cs ? cs->name() : QString()) << var.toInt(); } int lastPrintedChar = -1; QTextBlock::iterator it; QString fragmentText; QList inlineCharacters; for (it = block.begin(); !it.atEnd(); ++it) { QTextFragment fragment = it.fragment(); if (!fragment.isValid()) { continue; } QTextCharFormat fmt = fragment.charFormat(); qDebug() << "changeId: " << fmt.property(KoCharacterStyle::ChangeTrackerId); const int fragmentStart = fragment.position() - block.position(); for (int i = fragmentStart; i < fragmentStart + fragment.length(); i += CHARSPERLINE) { if (lastPrintedChar == fragmentStart - 1) { fragmentText += '|'; } if (lastPrintedChar < fragmentStart || i > fragmentStart) { QString debug = block.text().mid(lastPrintedChar, CHARSPERLINE); lastPrintedChar += CHARSPERLINE; if (lastPrintedChar > block.length()) { debug += "\\n"; } qDebug() << debug; } var = fmt.property(KoCharacterStyle::StyleId); QString charStyleLong, charStyleShort; if (!var.isNull()) { // named style charStyleShort = QString::number(var.toInt()); KoCharacterStyle *cs = styleManager->characterStyle(var.toInt()); if (cs) { charStyleLong = cs->name(); } } if (inlineManager && fmt.hasProperty(KoCharacterStyle::InlineInstanceId)) { QTextCharFormat inlineFmt = fmt; inlineFmt.setProperty(CHARPOSITION, fragmentStart); inlineCharacters << inlineFmt; } if (fragment.length() > charStyleLong.length()) { fragmentText += charStyleLong; } else if (fragment.length() > charStyleShort.length()) { fragmentText += charStyleShort; } else if (fragment.length() >= 2) { fragmentText += QChar(8230); // ellipses } int rest = fragmentStart - (lastPrintedChar - CHARSPERLINE) + fragment.length() - fragmentText.length(); rest = qMin(rest, CHARSPERLINE - fragmentText.length()); if (rest >= 2) { fragmentText = QString("%1%2").arg(fragmentText).arg(' ', rest); } if (rest >= 0) { fragmentText += '|'; } if (fragmentText.length() >= CHARSPERLINE) { qDebug() << fragmentText; fragmentText.clear(); } } } if (!fragmentText.isEmpty()) { qDebug() << fragmentText; } else if (block.length() == 1) { // no actual tet qDebug() << "\\n"; } foreach (const QTextCharFormat &cf, inlineCharacters) { KoInlineObject *object = inlineManager->inlineTextObject(cf); qDebug() << "At pos:" << cf.intProperty(CHARPOSITION) << object; // qDebug() << "-> id:" << cf.intProperty(577297549); } QTextList *list = block.textList(); if (list) { if (list->format().hasProperty(KoListStyle::StyleId)) { KoListStyle *ls = styleManager->listStyle(list->format().intProperty(KoListStyle::StyleId)); qDebug() << " List style applied:" << ls->styleId() << ls->name(); } else { qDebug() << " +- is a list..." << list; } } } #endif } void TextTool::debugTextStyles() { #ifndef NDEBUG if (!m_textShapeData) { return; } KoTextDocument document(m_textShapeData->document()); KoStyleManager *styleManager = document.styleManager(); QSet seenStyles; foreach (KoParagraphStyle *style, styleManager->paragraphStyles()) { qDebug() << style->styleId() << style->name() << (styleManager->defaultParagraphStyle() == style ? "[Default]" : ""); KoListStyle *ls = style->listStyle(); if (ls) { // optional ;) qDebug() << " +- ListStyle: " << ls->styleId() << ls->name() << (ls == styleManager->defaultListStyle() ? "[Default]" : ""); foreach (int level, ls->listLevels()) { KoListLevelProperties llp = ls->levelProperties(level); qDebug() << " | level" << llp.level() << " style (enum):" << llp.style(); if (llp.bulletCharacter().unicode() != 0) { qDebug() << " | bullet" << llp.bulletCharacter(); } } seenStyles << ls->styleId(); } } bool first = true; foreach (KoCharacterStyle *style, styleManager->characterStyles()) { if (seenStyles.contains(style->styleId())) { continue; } if (first) { qDebug() << "--- Character styles ---"; first = false; } qDebug() << style->styleId() << style->name(); qDebug() << style->font(); } first = true; foreach (KoListStyle *style, styleManager->listStyles()) { if (seenStyles.contains(style->styleId())) { continue; } if (first) { qDebug() << "--- List styles ---"; first = false; } qDebug() << style->styleId() << style->name() << (style == styleManager->defaultListStyle() ? "[Default]" : ""); } #endif } void TextTool::textDirectionChanged() { if (!m_allowActions || !m_textEditor.data()) { return; } QTextBlockFormat blockFormat; if (m_actionChangeDirection->isChecked()) { blockFormat.setProperty(KoParagraphStyle::TextProgressionDirection, KoText::RightLeftTopBottom); } else { blockFormat.setProperty(KoParagraphStyle::TextProgressionDirection, KoText::LeftRightTopBottom); } m_textEditor.data()->mergeBlockFormat(blockFormat); } void TextTool::setListLevel(int level) { if (level < 1 || level > 10) { return; } KoTextEditor *textEditor = m_textEditor.data(); if (textEditor->block().textList()) { ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::SetLevel; ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, level); textEditor->addCommand(cll); editingPluginEvents(); } } void TextTool::insertAnnotation() { // no annotations anymore, sorry :( } diff --git a/plugins/impex/brush/krita_brush.desktop b/plugins/impex/brush/krita_brush.desktop index 19dd8f621d..13d926cfff 100644 --- a/plugins/impex/brush/krita_brush.desktop +++ b/plugins/impex/brush/krita_brush.desktop @@ -1,73 +1,73 @@ [Desktop Entry] Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita -Exec=krita %f +Exec=krita %F MimeType=image/x-gimp-brush; Type=Application Icon=calligrakrita Categories=Qt;KDE;Office;Graphics; StartupNotify=true X-DBUS-StartupType=Multi X-DBUS-ServiceName=org.krita.krita NoDisplay=true diff --git a/plugins/impex/csv/krita_csv.desktop b/plugins/impex/csv/krita_csv.desktop index 7ae9f5af2e..18837058dd 100644 --- a/plugins/impex/csv/krita_csv.desktop +++ b/plugins/impex/csv/krita_csv.desktop @@ -1,71 +1,71 @@ [Desktop Entry] Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita -Exec=krita %f +Exec=krita %F MimeType=text/csv; Type=Application Icon=calligrakrita Categories=Qt;KDE;Office;Graphics; StartupNotify=true NoDisplay=true diff --git a/plugins/impex/exr/krita_exr.desktop b/plugins/impex/exr/krita_exr.desktop index cd268909ad..ed2565b3eb 100644 --- a/plugins/impex/exr/krita_exr.desktop +++ b/plugins/impex/exr/krita_exr.desktop @@ -1,124 +1,124 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; -Exec=krita %f +Exec=krita %F GenericName=Application for Drawing and Handling of Images GenericName[ar]=تطبيق لرسم الصور والتعامل معها GenericName[bg]=Приложение за рисуване и обработка на изображения GenericName[bs]=Aplikacija za crtanje i upravljanje slikom GenericName[ca]=Aplicació per a dibuix i modificació d'imatges GenericName[ca@valencia]=Aplicació per a dibuix i modificació d'imatges GenericName[da]=Tegne- og billedbehandlingsprogram GenericName[de]=Programm zum Zeichnen und Bearbeiten von Bildern GenericName[el]=Εφαρμογή για επεξεργασία και χειρισμό εικόνων GenericName[en_GB]=Application for Drawing and Handling of Images GenericName[eo]=Aplikaĵo por Desegnado kaj Mastrumado de Bildoj GenericName[es]=Aplicación para dibujo y manipulación de imágenes GenericName[et]=Joonistamise ja pilditöötluse rakendus GenericName[eu]=Irudiak marrazteko eta manipulatzeko aplikazioa GenericName[fa]=کاربرد برای ترسیم و به کار بردن تصاویر GenericName[fi]=Ohjelma kuvien piirtämiseen ja käsittelyyn GenericName[fr]=Application pour dessiner et manipuler des images GenericName[fy]=Aplikaasje om ôfbyldings mei te tekenjen en te bewurkjen GenericName[ga]=Feidhmchlár le haghaidh Líníochta agus Láimhseála Íomhánna GenericName[gl]=Aplicativo de debuxo e edición de imaxes GenericName[he]=יישום לצביעה וניהול תמונות GenericName[hi]=छवियों को ड्रा करने तथा उन्हें प्रबन्धित करने का अनुप्रयोग GenericName[hne]=फोटू मन ल ड्रा करे अउ ओ मन ल प्रबन्धित करे के अनुपरयोग GenericName[hu]=Rajzoló és képkezelő GenericName[is]=Teikni og myndvinnsluforrit GenericName[it]=Applicazione di disegno e gestione di immagini GenericName[ja]=描画と画像操作のためのアプリケーション GenericName[kk]=Кескінді салу және өңдеу бағдарламасы GenericName[ko]=그림 그리기 및 처리 프로그램 GenericName[lv]=Programma zīmēšanai un attēlu apstrādei GenericName[nb]=Program for tegning og bildehåndtering GenericName[nds]=Programm för't Teken un Bildhanteren GenericName[ne]=रेखाचित्र बनाउन र छविको ह्यान्डल गर्नका लागि अनुप्रयोग GenericName[nl]=Toepassing om afbeeldingen te tekenen en te bewerken GenericName[pl]=Program do rysowania i obróbki obrazów GenericName[pt]=Aplicação de Desenho e Manipulação de Imagens GenericName[pt_BR]=Aplicativo de desenho e manipulação de imagens GenericName[ru]=Приложение для рисования и редактирования изображений GenericName[sk]=Aplikácia na kresnenie a manilupáciu s obrázkami GenericName[sl]=Program za risanje in rokovanje s slikami GenericName[sv]=Program för att rita och hantera bilder GenericName[ta]=பிம்பங்களை கையாளுதல் மற்றும் வரைதலுக்கான பயன்னாடு GenericName[tr]=Çizim ve Resim İşleme Uygulaması GenericName[uk]=Програма для малювання і обробки зображень GenericName[uz]=Rasm chizish dasturi GenericName[uz@cyrillic]=Расм чизиш дастури GenericName[wa]=Programe po dessiner et apougnî des imådjes GenericName[x-test]=xxApplication for Drawing and Handling of Imagesxx GenericName[zh_CN]=绘制和处理图像的应用程序 GenericName[zh_TW]=繪圖與影像處理的應用程式 Icon=calligrakrita MimeType=image/exr; Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita StartupNotify=true Terminal=false Type=Application X-KDE-SubstituteUID=false X-KDE-Username= NoDisplay=true diff --git a/plugins/impex/gif/krita_gif.desktop b/plugins/impex/gif/krita_gif.desktop index 80afd40c5a..65dbdfdba6 100644 --- a/plugins/impex/gif/krita_gif.desktop +++ b/plugins/impex/gif/krita_gif.desktop @@ -1,71 +1,71 @@ [Desktop Entry] Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita -Exec=krita %f +Exec=krita %F MimeType=image/gif; Type=Application Icon=calligrakrita Categories=Qt;KDE;Office;Graphics; StartupNotify=true NoDisplay=true diff --git a/plugins/impex/heif/krita_heif.desktop b/plugins/impex/heif/krita_heif.desktop index ddb12c86f0..6da0d4b9e4 100644 --- a/plugins/impex/heif/krita_heif.desktop +++ b/plugins/impex/heif/krita_heif.desktop @@ -1,124 +1,124 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; -Exec=krita %f +Exec=krita %F GenericName=Application for Drawing and Handling of Images GenericName[ar]=تطبيق لرسم الصور والتعامل معها GenericName[bg]=Приложение за рисуване и обработка на изображения GenericName[bs]=Aplikacija za crtanje i upravljanje slikom GenericName[ca]=Aplicació per a dibuix i modificació d'imatges GenericName[ca@valencia]=Aplicació per a dibuix i modificació d'imatges GenericName[da]=Tegne- og billedbehandlingsprogram GenericName[de]=Programm zum Zeichnen und Bearbeiten von Bildern GenericName[el]=Εφαρμογή για επεξεργασία και χειρισμό εικόνων GenericName[en_GB]=Application for Drawing and Handling of Images GenericName[eo]=Aplikaĵo por Desegnado kaj Mastrumado de Bildoj GenericName[es]=Aplicación para dibujo y manipulación de imágenes GenericName[et]=Joonistamise ja pilditöötluse rakendus GenericName[eu]=Irudiak marrazteko eta manipulatzeko aplikazioa GenericName[fa]=کاربرد برای ترسیم و به کار بردن تصاویر GenericName[fi]=Ohjelma kuvien piirtämiseen ja käsittelyyn GenericName[fr]=Application pour dessiner et manipuler des images GenericName[fy]=Aplikaasje om ôfbyldings mei te tekenjen en te bewurkjen GenericName[ga]=Feidhmchlár le haghaidh Líníochta agus Láimhseála Íomhánna GenericName[gl]=Aplicativo de debuxo e edición de imaxes GenericName[he]=יישום לצביעה וניהול תמונות GenericName[hi]=छवियों को ड्रा करने तथा उन्हें प्रबन्धित करने का अनुप्रयोग GenericName[hne]=फोटू मन ल ड्रा करे अउ ओ मन ल प्रबन्धित करे के अनुपरयोग GenericName[hu]=Rajzoló és képkezelő GenericName[is]=Teikni og myndvinnsluforrit GenericName[it]=Applicazione di disegno e gestione di immagini GenericName[ja]=描画と画像操作のためのアプリケーション GenericName[kk]=Кескінді салу және өңдеу бағдарламасы GenericName[ko]=그림 그리기 및 처리 프로그램 GenericName[lv]=Programma zīmēšanai un attēlu apstrādei GenericName[nb]=Program for tegning og bildehåndtering GenericName[nds]=Programm för't Teken un Bildhanteren GenericName[ne]=रेखाचित्र बनाउन र छविको ह्यान्डल गर्नका लागि अनुप्रयोग GenericName[nl]=Toepassing om afbeeldingen te tekenen en te bewerken GenericName[pl]=Program do rysowania i obróbki obrazów GenericName[pt]=Aplicação de Desenho e Manipulação de Imagens GenericName[pt_BR]=Aplicativo de desenho e manipulação de imagens GenericName[ru]=Приложение для рисования и редактирования изображений GenericName[sk]=Aplikácia na kresnenie a manilupáciu s obrázkami GenericName[sl]=Program za risanje in rokovanje s slikami GenericName[sv]=Program för att rita och hantera bilder GenericName[ta]=பிம்பங்களை கையாளுதல் மற்றும் வரைதலுக்கான பயன்னாடு GenericName[tr]=Çizim ve Resim İşleme Uygulaması GenericName[uk]=Програма для малювання і обробки зображень GenericName[uz]=Rasm chizish dasturi GenericName[uz@cyrillic]=Расм чизиш дастури GenericName[wa]=Programe po dessiner et apougnî des imådjes GenericName[x-test]=xxApplication for Drawing and Handling of Imagesxx GenericName[zh_CN]=绘制和处理图像的应用程序 GenericName[zh_TW]=繪圖與影像處理的應用程式 Icon=calligrakrita MimeType=image/heic; Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita StartupNotify=true Terminal=false Type=Application X-KDE-SubstituteUID=false X-KDE-Username= NoDisplay=true diff --git a/plugins/impex/heightmap/krita_heightmap.desktop b/plugins/impex/heightmap/krita_heightmap.desktop index 0c254dc52a..374e50ec34 100644 --- a/plugins/impex/heightmap/krita_heightmap.desktop +++ b/plugins/impex/heightmap/krita_heightmap.desktop @@ -1,75 +1,75 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; -Exec=krita %f +Exec=krita %F GenericName= Icon=calligrakrita MimeType=image/x-r16; Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita NoDisplay=true StartupNotify=true Terminal=false Type=Application X-KDE-SubstituteUID=false X-KDE-Username= diff --git a/plugins/impex/jpeg/krita_jpeg.desktop b/plugins/impex/jpeg/krita_jpeg.desktop index c94f6e6e54..e84fc9823c 100644 --- a/plugins/impex/jpeg/krita_jpeg.desktop +++ b/plugins/impex/jpeg/krita_jpeg.desktop @@ -1,74 +1,74 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; -Exec=krita %f +Exec=krita %F Icon=calligrakrita MimeType=image/jpeg; Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita StartupNotify=true Terminal=false Type=Application X-KDE-SubstituteUID=false X-KDE-Username= NoDisplay=true diff --git a/plugins/impex/kra/krita_kra.desktop b/plugins/impex/kra/krita_kra.desktop index 213e9c3660..c03de11d25 100644 --- a/plugins/impex/kra/krita_kra.desktop +++ b/plugins/impex/kra/krita_kra.desktop @@ -1,75 +1,75 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Comment= -Exec=krita %f +Exec=krita %F Icon=calligrakrita MimeType=application/x-krita; Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita StartupNotify=true Terminal=false Type=Application X-KDE-SubstituteUID=false X-KDE-Username= NoDisplay=true diff --git a/plugins/impex/ora/krita_ora.desktop b/plugins/impex/ora/krita_ora.desktop index d58255fa96..9071d6cd94 100644 --- a/plugins/impex/ora/krita_ora.desktop +++ b/plugins/impex/ora/krita_ora.desktop @@ -1,75 +1,75 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Comment= -Exec=krita %f +Exec=krita %F Icon=calligrakrita MimeType=image/openraster; Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita StartupNotify=true Terminal=false Type=Application X-KDE-SubstituteUID=false X-KDE-Username= NoDisplay=true diff --git a/plugins/impex/pdf/krita_pdf.desktop b/plugins/impex/pdf/krita_pdf.desktop index f443c612b6..b59200fb0c 100644 --- a/plugins/impex/pdf/krita_pdf.desktop +++ b/plugins/impex/pdf/krita_pdf.desktop @@ -1,74 +1,74 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; -Exec=krita %f +Exec=krita %F Icon=calligrakrita MimeType=application/pdf; Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita StartupNotify=true Terminal=false Type=Application X-KDE-SubstituteUID=false X-KDE-Username= NoDisplay=true diff --git a/plugins/impex/png/krita_png.desktop b/plugins/impex/png/krita_png.desktop index 6ff582fd54..0ce304736c 100644 --- a/plugins/impex/png/krita_png.desktop +++ b/plugins/impex/png/krita_png.desktop @@ -1,71 +1,71 @@ [Desktop Entry] Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita -Exec=krita %f +Exec=krita %F MimeType=image/png; Type=Application Icon=calligrakrita Categories=Qt;KDE;Office;Graphics; StartupNotify=true NoDisplay=true diff --git a/plugins/impex/ppm/krita_ppm.desktop b/plugins/impex/ppm/krita_ppm.desktop index a3b0a16396..a009f10bef 100644 --- a/plugins/impex/ppm/krita_ppm.desktop +++ b/plugins/impex/ppm/krita_ppm.desktop @@ -1,75 +1,75 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; -Exec=krita %f +Exec=krita %F GenericName= Icon=calligrakrita MimeType=image/x-portable-pixmap;image/x-portable-graymap;image/x-portable-bitmap; Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita NoDisplay=true StartupNotify=true Terminal=false Type=Application X-KDE-SubstituteUID=false X-KDE-Username= diff --git a/plugins/impex/psd/krita_psd.desktop b/plugins/impex/psd/krita_psd.desktop index 53620b5837..1981f6eac6 100644 --- a/plugins/impex/psd/krita_psd.desktop +++ b/plugins/impex/psd/krita_psd.desktop @@ -1,74 +1,74 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; -Exec=krita %f +Exec=krita %F Icon=calligrakrita MimeType=image/x-psd;image/photoshop;image/x-photoshop;image/vnd.adobe.photoshop; Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita NoDisplay=true StartupNotify=true Terminal=false Type=Application X-KDE-SubstituteUID=false X-KDE-Username= diff --git a/plugins/impex/qimageio/krita_qimageio.desktop b/plugins/impex/qimageio/krita_qimageio.desktop index af366fef07..a4ae12d0f4 100644 --- a/plugins/impex/qimageio/krita_qimageio.desktop +++ b/plugins/impex/qimageio/krita_qimageio.desktop @@ -1,71 +1,71 @@ [Desktop Entry] Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita -Exec=krita %f +Exec=krita %F MimeType=image/bmp;image/x-xpixmap;image/x-xbitmap;image/webp;image/vnd.microsoft.icon; Type=Application Icon=calligrakrita Categories=Qt;KDE;Office;Graphics; StartupNotify=true NoDisplay=true diff --git a/plugins/impex/raw/krita_raw.desktop b/plugins/impex/raw/krita_raw.desktop index 26b97dd8d1..4d5290dcb3 100644 --- a/plugins/impex/raw/krita_raw.desktop +++ b/plugins/impex/raw/krita_raw.desktop @@ -1,71 +1,71 @@ [Desktop Entry] Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita -Exec=krita %f +Exec=krita %F MimeType=image/x-nikon-nef;image/x-canon-cr2;image/x-sony-sr2;image/x-canon-crw;image/x-pentax-pef;image/x-sigma-x3f;image/x-kodak-kdc;image/x-minolta-mrw;image/x-sony-arw;image/x-kodak-k25;image/x-kodak-dcr;image/x-olympus-orf;image/x-panasonic-raw;image/x-panasonic-raw2;image/x-fuji-raf;image/x-sony-srf;image/x-adobe-dng; Icon=calligrakrita Categories=Qt;KDE;Office;Graphics; StartupNotify=true NoDisplay=true Type=Application diff --git a/plugins/impex/spriter/krita_spriter.desktop b/plugins/impex/spriter/krita_spriter.desktop index 0f64b14bbc..d86212012b 100644 --- a/plugins/impex/spriter/krita_spriter.desktop +++ b/plugins/impex/spriter/krita_spriter.desktop @@ -1,71 +1,71 @@ [Desktop Entry] Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita -Exec=krita %f +Exec=krita %F MimeType=image/x-scml; Type=Application Icon=calligrakrita Categories=Qt;KDE;Office;Graphics; StartupNotify=true NoDisplay=true diff --git a/plugins/impex/svg/krita_svg.desktop b/plugins/impex/svg/krita_svg.desktop index ace1cc9290..f72e2b4d45 100644 --- a/plugins/impex/svg/krita_svg.desktop +++ b/plugins/impex/svg/krita_svg.desktop @@ -1,71 +1,71 @@ [Desktop Entry] Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita -Exec=krita %f +Exec=krita %F MimeType=image/svg+xml; Type=Application Icon=calligrakrita Categories=Qt;KDE;Office;Graphics; StartupNotify=true NoDisplay=true diff --git a/plugins/impex/tga/krita_tga.desktop b/plugins/impex/tga/krita_tga.desktop index 21d0856f6e..e7a36f29c3 100644 --- a/plugins/impex/tga/krita_tga.desktop +++ b/plugins/impex/tga/krita_tga.desktop @@ -1,71 +1,71 @@ [Desktop Entry] Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita -Exec=krita %f +Exec=krita %F MimeType=image/x-tga; Type=Application Icon=calligrakrita Categories=Qt;KDE;Office;Graphics; StartupNotify=true NoDisplay=true diff --git a/plugins/impex/tiff/krita_tiff.desktop b/plugins/impex/tiff/krita_tiff.desktop index 0eef0c8d4a..f7c0c9a68a 100644 --- a/plugins/impex/tiff/krita_tiff.desktop +++ b/plugins/impex/tiff/krita_tiff.desktop @@ -1,74 +1,74 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; -Exec=krita %f +Exec=krita %F Icon=calligrakrita MimeType=image/tiff; Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita NoDisplay=true StartupNotify=true Terminal=false Type=Application X-KDE-SubstituteUID=false X-KDE-Username= diff --git a/plugins/impex/xcf/krita_xcf.desktop b/plugins/impex/xcf/krita_xcf.desktop index d72ceb7daf..af4d682997 100644 --- a/plugins/impex/xcf/krita_xcf.desktop +++ b/plugins/impex/xcf/krita_xcf.desktop @@ -1,74 +1,74 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; -Exec=krita %f +Exec=krita %F Icon=calligrakrita MimeType=image/x-xcf; Name=Krita Name[af]=Krita Name[ar]=كريتا Name[bg]=Krita Name[br]=Krita Name[bs]=Krita Name[ca]=Krita Name[ca@valencia]=Krita Name[cs]=Krita Name[cy]=Krita Name[da]=Krita Name[de]=Krita Name[el]=Krita Name[en_GB]=Krita Name[eo]=Krita Name[es]=Krita Name[et]=Krita Name[eu]=Krita Name[fi]=Krita Name[fr]=Krita Name[fy]=Krita Name[ga]=Krita Name[gl]=Krita Name[he]=Krita Name[hi]=केरिता Name[hne]=केरिता Name[hr]=Krita Name[hu]=Krita Name[ia]=Krita Name[is]=Krita Name[it]=Krita Name[ja]=Krita Name[kk]=Krita Name[ko]=Krita Name[lt]=Krita Name[lv]=Krita Name[mr]=क्रिटा Name[ms]=Krita Name[nb]=Krita Name[nds]=Krita Name[ne]=क्रिता Name[nl]=Krita Name[pl]=Krita Name[pt]=Krita Name[pt_BR]=Krita Name[ro]=Krita Name[ru]=Krita Name[se]=Krita Name[sk]=Krita Name[sl]=Krita Name[sv]=Krita Name[ta]=கிரிட்டா Name[tg]=Krita Name[tr]=Krita Name[ug]=Krita Name[uk]=Krita Name[uz]=Krita Name[uz@cyrillic]=Krita Name[wa]=Krita Name[xh]=Krita Name[x-test]=xxKritaxx Name[zh_CN]=Krita Name[zh_TW]=Krita Type=Application NoDisplay=true StartupNotify=true Terminal=false X-KDE-SubstituteUID=false X-KDE-Username= diff --git a/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop b/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop index c8656b77db..f9f11aa2ac 100644 --- a/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop +++ b/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop @@ -1,37 +1,39 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=krita_script_starter X-Python-2-Compatible=false X-Krita-Manual=Manual.html Name=Krita Script Starter Name[ca]=Iniciador de scripts del Krita Name[ca@valencia]=Iniciador de scripts del Krita Name[cs]=Spouštěč skriptů Krita Name[en_GB]=Krita Script Starter Name[es]=Iniciador de guiones de Krita Name[eu]=Krita-ren script abiarazlea Name[gl]=Iniciador de scripts de Krita Name[it]=Iniziatore di script per Krita Name[nl]=Script-starter van Krita Name[pl]=Starter skryptów Krity Name[pt]=Inicialização do Programa do Krita Name[sv]=Krita skriptstart Name[uk]=Створення скрипту Krita Name[x-test]=xxKrita Script Starterxx Name[zh_CN]=Krita 空脚本生成器 +Name[zh_TW]=Krita 指令啟動器 Comment=Create the metadata and file structure for a new Krita script Comment[ca]=Crea les metadades i l'estructura de fitxers d'un script nou del Krita Comment[ca@valencia]=Crea les metadades i l'estructura de fitxers d'un script nou del Krita Comment[en_GB]=Create the metadata and file structure for a new Krita script Comment[es]=Crear los metadatos y la estructura de archivos para un nuevo guion de Krita Comment[eu]=Sortu Krita-script berri baterako meta-datuak eta fitxategi egitura Comment[gl]=Crear os metadatos e a estrutura de ficheiros para un novo script de Krita. Comment[it]=Crea i metadati e la struttura dei file per un nuovo script di Krita Comment[nl]=Maak de metagegevens en bestandsstructuur voor een nieuw Krita-script Comment[pl]=Utwórz metadane i strukturę plików dla nowego skryptu Krity Comment[pt]=Cria os meta-dados e a estrutura de ficheiros para um novo programa do Krita Comment[sv]=Skapa metadata och filstruktur för ett nytt Krita-skript Comment[uk]=Створення метаданих і структури файлів для нового скрипту Krita Comment[x-test]=xxCreate the metadata and file structure for a new Krita scriptxx Comment[zh_CN]=给一个新 Krita 脚本创建元数据及文件结构 +Comment[zh_TW]=為新的 Krita 腳本建立中繼資料和檔案建構體 diff --git a/plugins/tools/basictools/kis_tool_brush.cc b/plugins/tools/basictools/kis_tool_brush.cc index 8a0aed7e31..e1117114e3 100644 --- a/plugins/tools/basictools/kis_tool_brush.cc +++ b/plugins/tools/basictools/kis_tool_brush.cc @@ -1,473 +1,477 @@ /* * kis_tool_brush.cc - part of Krita * * Copyright (c) 2003-2004 Boudewijn Rempt * Copyright (c) 2015 Moritz Molch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * 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_brush.h" #include #include #include #include #include #include #include #include #include #include #include "kis_cursor.h" #include "kis_config.h" #include "kis_slider_spin_box.h" #include "kundo2magicstring.h" #define MAXIMUM_SMOOTHNESS_DISTANCE 1000.0 // 0..1000.0 == weight in gui #define MAXIMUM_MAGNETISM 1000 -void KisToolBrush::addSmoothingAction(int enumId, const QString &id, const QString &name, const QIcon &icon, KActionCollection *globalCollection) +void KisToolBrush::addSmoothingAction(int enumId, const QString &id) { /** * KisToolBrush is the base of several tools, but the actions * should be unique, so let's be careful with them */ - if (!globalCollection->action(id)) { - QAction *action = new QAction(name, globalCollection); - action->setIcon(icon); - globalCollection->addAction(id, action); - } - - QAction *action = dynamic_cast(globalCollection->action(id)); - addAction(id, action); - - connect(action, SIGNAL(triggered()), &m_signalMapper, SLOT(map())); - m_signalMapper.setMapping(action, enumId); + QAction *a = action(id); + connect(a, SIGNAL(triggered()), &m_signalMapper, SLOT(map())); + m_signalMapper.setMapping(a, enumId); } KisToolBrush::KisToolBrush(KoCanvasBase * canvas) : KisToolFreehand(canvas, KisCursor::load("tool_freehand_cursor.png", 5, 5), kundo2_i18n("Freehand Brush Stroke")) { setObjectName("tool_brush"); - connect(this, SIGNAL(smoothingTypeChanged()), this, SLOT(resetCursorStyle())); - KActionCollection *collection = this->canvas()->canvasController()->actionCollection(); + createOptionWidget(); - addSmoothingAction(KisSmoothingOptions::NO_SMOOTHING, "set_no_brush_smoothing", i18nc("@action", "Brush Smoothing: Disabled"), KisIconUtils::loadIcon("smoothing-no"), collection); - addSmoothingAction(KisSmoothingOptions::SIMPLE_SMOOTHING, "set_simple_brush_smoothing", i18nc("@action", "Brush Smoothing: Basic"), KisIconUtils::loadIcon("smoothing-basic"), collection); - addSmoothingAction(KisSmoothingOptions::WEIGHTED_SMOOTHING, "set_weighted_brush_smoothing", i18nc("@action", "Brush Smoothing: Weighted"), KisIconUtils::loadIcon("smoothing-weighted"), collection); - addSmoothingAction(KisSmoothingOptions::STABILIZER, "set_stabilizer_brush_smoothing", i18nc("@action", "Brush Smoothing: Stabilizer"), KisIconUtils::loadIcon("smoothing-stabilizer"), collection); + connect(this, SIGNAL(smoothingTypeChanged()), this, SLOT(resetCursorStyle())); + + addSmoothingAction(KisSmoothingOptions::NO_SMOOTHING, "set_no_brush_smoothing"); + addSmoothingAction(KisSmoothingOptions::SIMPLE_SMOOTHING, "set_simple_brush_smoothing"); + addSmoothingAction(KisSmoothingOptions::WEIGHTED_SMOOTHING, "set_weighted_brush_smoothing"); + addSmoothingAction(KisSmoothingOptions::STABILIZER, "set_stabilizer_brush_smoothing"); } KisToolBrush::~KisToolBrush() { } void KisToolBrush::activate(ToolActivation activation, const QSet &shapes) { KisToolFreehand::activate(activation, shapes); connect(&m_signalMapper, SIGNAL(mapped(int)), SLOT(slotSetSmoothingType(int)), Qt::UniqueConnection); + QAction *toggleaction = action("toggle_assistant"); + connect(toggleaction, SIGNAL(triggered(bool)), m_chkAssistant, SLOT(toggle()), Qt::UniqueConnection); + m_configGroup = KSharedConfig::openConfig()->group(toolId()); } void KisToolBrush::deactivate() { disconnect(&m_signalMapper, 0, this, 0); + QAction *toggleaction = action("toggle_assistant"); + disconnect(toggleaction, 0, m_chkAssistant, 0); + KisToolFreehand::deactivate(); } int KisToolBrush::smoothingType() const { return smoothingOptions()->smoothingType(); } bool KisToolBrush::smoothPressure() const { return smoothingOptions()->smoothPressure(); } int KisToolBrush::smoothnessQuality() const { return smoothingOptions()->smoothnessDistance(); } qreal KisToolBrush::smoothnessFactor() const { return smoothingOptions()->tailAggressiveness(); } void KisToolBrush::slotSetSmoothingType(int index) { /** * The slot can also be called from smoothing-type-switching * action that would mean the combo box will not be synchronized */ if (m_cmbSmoothingType->currentIndex() != index) { m_cmbSmoothingType->setCurrentIndex(index); } switch (index) { case 0: smoothingOptions()->setSmoothingType(KisSmoothingOptions::NO_SMOOTHING); showControl(m_sliderSmoothnessDistance, false); showControl(m_sliderTailAggressiveness, false); showControl(m_chkSmoothPressure, false); showControl(m_chkUseScalableDistance, false); showControl(m_sliderDelayDistance, false); showControl(m_chkFinishStabilizedCurve, false); showControl(m_chkStabilizeSensors, false); break; case 1: smoothingOptions()->setSmoothingType(KisSmoothingOptions::SIMPLE_SMOOTHING); showControl(m_sliderSmoothnessDistance, false); showControl(m_sliderTailAggressiveness, false); showControl(m_chkSmoothPressure, false); showControl(m_chkUseScalableDistance, false); showControl(m_sliderDelayDistance, false); showControl(m_chkFinishStabilizedCurve, false); showControl(m_chkStabilizeSensors, false); break; case 2: smoothingOptions()->setSmoothingType(KisSmoothingOptions::WEIGHTED_SMOOTHING); showControl(m_sliderSmoothnessDistance, true); showControl(m_sliderTailAggressiveness, true); showControl(m_chkSmoothPressure, true); showControl(m_chkUseScalableDistance, true); showControl(m_sliderDelayDistance, false); showControl(m_chkFinishStabilizedCurve, false); showControl(m_chkStabilizeSensors, false); break; case 3: default: smoothingOptions()->setSmoothingType(KisSmoothingOptions::STABILIZER); showControl(m_sliderSmoothnessDistance, true); showControl(m_sliderTailAggressiveness, false); showControl(m_chkSmoothPressure, false); showControl(m_chkUseScalableDistance, true); showControl(m_sliderDelayDistance, true); showControl(m_chkFinishStabilizedCurve, true); showControl(m_chkStabilizeSensors, true); } emit smoothingTypeChanged(); } void KisToolBrush::slotSetSmoothnessDistance(qreal distance) { smoothingOptions()->setSmoothnessDistance(distance); emit smoothnessQualityChanged(); } void KisToolBrush::slotSetTailAgressiveness(qreal argh_rhhrr) { smoothingOptions()->setTailAggressiveness(argh_rhhrr); emit smoothnessFactorChanged(); } // used with weighted smoothing void KisToolBrush::setSmoothPressure(bool value) { smoothingOptions()->setSmoothPressure(value); } void KisToolBrush::slotSetMagnetism(int magnetism) { m_magnetism = expf(magnetism / (double)MAXIMUM_MAGNETISM) / expf(1.0); } bool KisToolBrush::useScalableDistance() const { return smoothingOptions()->useScalableDistance(); } // used with weighted smoothing void KisToolBrush::setUseScalableDistance(bool value) { smoothingOptions()->setUseScalableDistance(value); emit useScalableDistanceChanged(); } void KisToolBrush::resetCursorStyle() { KisConfig cfg(true); CursorStyle cursorStyle = cfg.newCursorStyle(); // When the stabilizer is in use, we avoid using the brush outline cursor, // because it would hide the real position of the cursor to the user, // yielding unexpected results. if (smoothingOptions()->smoothingType() == KisSmoothingOptions::STABILIZER && - smoothingOptions()->useDelayDistance() && - cursorStyle == CURSOR_STYLE_NO_CURSOR) { + smoothingOptions()->useDelayDistance() && + cursorStyle == CURSOR_STYLE_NO_CURSOR) { useCursor(KisCursor::roundCursor()); } else { KisToolFreehand::resetCursorStyle(); } overrideCursorIfNotEditable(); } // stabilizer brush settings bool KisToolBrush::useDelayDistance() const { return smoothingOptions()->useDelayDistance(); } qreal KisToolBrush::delayDistance() const { return smoothingOptions()->delayDistance(); } void KisToolBrush::setUseDelayDistance(bool value) { smoothingOptions()->setUseDelayDistance(value); m_sliderDelayDistance->setEnabled(value); enableControl(m_chkFinishStabilizedCurve, !value); emit useDelayDistanceChanged(); } void KisToolBrush::setDelayDistance(qreal value) { smoothingOptions()->setDelayDistance(value); emit delayDistanceChanged(); } void KisToolBrush::setFinishStabilizedCurve(bool value) { smoothingOptions()->setFinishStabilizedCurve(value); emit finishStabilizedCurveChanged(); } bool KisToolBrush::finishStabilizedCurve() const { return smoothingOptions()->finishStabilizedCurve(); } void KisToolBrush::setStabilizeSensors(bool value) { smoothingOptions()->setStabilizeSensors(value); emit stabilizeSensorsChanged(); } bool KisToolBrush::stabilizeSensors() const { return smoothingOptions()->stabilizeSensors(); } void KisToolBrush::updateSettingsViews() { m_cmbSmoothingType->setCurrentIndex(smoothingOptions()->smoothingType()); m_sliderSmoothnessDistance->setValue(smoothingOptions()->smoothnessDistance()); m_chkDelayDistance->setChecked(smoothingOptions()->useDelayDistance()); m_sliderDelayDistance->setValue(smoothingOptions()->delayDistance()); m_sliderTailAggressiveness->setValue(smoothingOptions()->tailAggressiveness()); m_chkSmoothPressure->setChecked(smoothingOptions()->smoothPressure()); m_chkUseScalableDistance->setChecked(smoothingOptions()->useScalableDistance()); m_cmbSmoothingType->setCurrentIndex((int)smoothingOptions()->smoothingType()); m_chkStabilizeSensors->setChecked(smoothingOptions()->stabilizeSensors()); emit smoothnessQualityChanged(); emit smoothnessFactorChanged(); emit smoothPressureChanged(); emit smoothingTypeChanged(); emit useScalableDistanceChanged(); emit useDelayDistanceChanged(); emit delayDistanceChanged(); emit finishStabilizedCurveChanged(); emit stabilizeSensorsChanged(); KisTool::updateSettingsViews(); } QWidget * KisToolBrush::createOptionWidget() { QWidget *optionsWidget = KisToolFreehand::createOptionWidget(); optionsWidget->setObjectName(toolId() + "option widget"); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); optionsWidget->layout()->addWidget(specialSpacer); // Line smoothing configuration m_cmbSmoothingType = new QComboBox(optionsWidget); m_cmbSmoothingType->addItems(QStringList() - << i18n("None") - << i18n("Basic") - << i18n("Weighted") - << i18n("Stabilizer")); + << i18n("None") + << i18n("Basic") + << i18n("Weighted") + << i18n("Stabilizer")); connect(m_cmbSmoothingType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetSmoothingType(int))); addOptionWidgetOption(m_cmbSmoothingType, new QLabel(i18n("Brush Smoothing:"))); m_sliderSmoothnessDistance = new KisDoubleSliderSpinBox(optionsWidget); m_sliderSmoothnessDistance->setRange(3.0, MAXIMUM_SMOOTHNESS_DISTANCE, 1); m_sliderSmoothnessDistance->setExponentRatio(3.0); // help pick smaller values m_sliderSmoothnessDistance->setEnabled(true); connect(m_sliderSmoothnessDistance, SIGNAL(valueChanged(qreal)), SLOT(slotSetSmoothnessDistance(qreal))); m_sliderSmoothnessDistance->setValue(smoothingOptions()->smoothnessDistance()); addOptionWidgetOption(m_sliderSmoothnessDistance, new QLabel(i18n("Distance:"))); // Finish stabilizer curve m_chkFinishStabilizedCurve = new QCheckBox(optionsWidget); m_chkFinishStabilizedCurve->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3, m_chkFinishStabilizedCurve->sizeHint().height())); connect(m_chkFinishStabilizedCurve, SIGNAL(toggled(bool)), this, SLOT(setFinishStabilizedCurve(bool))); m_chkFinishStabilizedCurve->setChecked(smoothingOptions()->finishStabilizedCurve()); // Delay Distance for Stabilizer QWidget* delayWidget = new QWidget(optionsWidget); QHBoxLayout* delayLayout = new QHBoxLayout(delayWidget); delayLayout->setContentsMargins(0,0,0,0); delayLayout->setSpacing(1); QLabel* delayLabel = new QLabel(i18n("Delay:"), optionsWidget); delayLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); delayLayout->addWidget(delayLabel); m_chkDelayDistance = new QCheckBox(optionsWidget); m_chkDelayDistance->setLayoutDirection(Qt::RightToLeft); delayWidget->setToolTip(i18n("Delay the brush stroke to make the line smoother")); connect(m_chkDelayDistance, SIGNAL(toggled(bool)), this, SLOT(setUseDelayDistance(bool))); delayLayout->addWidget(m_chkDelayDistance); m_sliderDelayDistance = new KisDoubleSliderSpinBox(optionsWidget); m_sliderDelayDistance->setToolTip(i18n("Radius where the brush is blocked")); m_sliderDelayDistance->setRange(0, 500); m_sliderDelayDistance->setExponentRatio(3.0); // help pick smaller values m_sliderDelayDistance->setSuffix(i18n(" px")); connect(m_sliderDelayDistance, SIGNAL(valueChanged(qreal)), SLOT(setDelayDistance(qreal))); addOptionWidgetOption(m_sliderDelayDistance, delayWidget); addOptionWidgetOption(m_chkFinishStabilizedCurve, new QLabel(i18n("Finish line:"))); m_sliderDelayDistance->setValue(smoothingOptions()->delayDistance()); m_chkDelayDistance->setChecked(smoothingOptions()->useDelayDistance()); // if the state is not flipped, then the previous line doesn't generate any signals setUseDelayDistance(m_chkDelayDistance->isChecked()); // Stabilize sensors m_chkStabilizeSensors = new QCheckBox(optionsWidget); m_chkStabilizeSensors->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3, m_chkStabilizeSensors->sizeHint().height())); connect(m_chkStabilizeSensors, SIGNAL(toggled(bool)), this, SLOT(setStabilizeSensors(bool))); m_chkStabilizeSensors->setChecked(smoothingOptions()->stabilizeSensors()); addOptionWidgetOption(m_chkStabilizeSensors, new QLabel(i18n("Stabilize Sensors:"))); m_sliderTailAggressiveness = new KisDoubleSliderSpinBox(optionsWidget); m_sliderTailAggressiveness->setRange(0.0, 1.0, 2); m_sliderTailAggressiveness->setEnabled(true); connect(m_sliderTailAggressiveness, SIGNAL(valueChanged(qreal)), SLOT(slotSetTailAgressiveness(qreal))); m_sliderTailAggressiveness->setValue(smoothingOptions()->tailAggressiveness()); addOptionWidgetOption(m_sliderTailAggressiveness, new QLabel(i18n("Stroke Ending:"))); m_chkSmoothPressure = new QCheckBox(optionsWidget); m_chkSmoothPressure->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3, m_chkSmoothPressure->sizeHint().height())); m_chkSmoothPressure->setChecked(smoothingOptions()->smoothPressure()); connect(m_chkSmoothPressure, SIGNAL(toggled(bool)), this, SLOT(setSmoothPressure(bool))); addOptionWidgetOption(m_chkSmoothPressure, new QLabel(QString("%1:").arg(i18n("Smooth Pressure")))); m_chkUseScalableDistance = new QCheckBox(optionsWidget); m_chkUseScalableDistance->setChecked(smoothingOptions()->useScalableDistance()); m_chkUseScalableDistance->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3, m_chkUseScalableDistance->sizeHint().height())); m_chkUseScalableDistance->setToolTip(i18nc("@info:tooltip", "Scalable distance takes zoom level " "into account and makes the distance " "be visually constant whatever zoom " "level is chosen")); connect(m_chkUseScalableDistance, SIGNAL(toggled(bool)), this, SLOT(setUseScalableDistance(bool))); addOptionWidgetOption(m_chkUseScalableDistance, new QLabel(QString("%1:").arg(i18n("Scalable Distance")))); // add a line spacer so we know that the next set of options are for different settings QFrame* line = new QFrame(optionsWidget); line->setObjectName(QString::fromUtf8("line")); line->setFrameShape(QFrame::HLine); addOptionWidgetOption(line); - - // Drawing assistant configuration QWidget* assistantWidget = new QWidget(optionsWidget); QGridLayout* assistantLayout = new QGridLayout(assistantWidget); assistantLayout->setContentsMargins(10,0,0,0); assistantLayout->setSpacing(5); - m_chkAssistant = new QCheckBox(optionsWidget); m_chkAssistant->setText(i18n("Snap to Assistants")); assistantWidget->setToolTip(i18n("You need to add Assistants before this tool will work.")); connect(m_chkAssistant, SIGNAL(toggled(bool)), this, SLOT(setAssistant(bool))); addOptionWidgetOption(assistantWidget, m_chkAssistant); m_sliderMagnetism = new KisSliderSpinBox(optionsWidget); m_sliderMagnetism->setToolTip(i18n("Assistant Magnetism")); m_sliderMagnetism->setRange(0, MAXIMUM_MAGNETISM); m_sliderMagnetism->setValue(m_magnetism * MAXIMUM_MAGNETISM); connect(m_sliderMagnetism, SIGNAL(valueChanged(int)), SLOT(slotSetMagnetism(int))); - QAction *toggleaction = KisActionRegistry::instance()->makeQAction("toggle_assistant", this); - addAction("toggle_assistant", toggleaction); - toggleaction->setShortcut(QKeySequence(Qt::ControlModifier + Qt::ShiftModifier + Qt::Key_L)); - connect(toggleaction, SIGNAL(triggered(bool)), m_chkAssistant, SLOT(toggle())); - QLabel* magnetismLabel = new QLabel(i18n("Magnetism:")); addOptionWidgetOption(m_sliderMagnetism, magnetismLabel); QLabel* snapSingleLabel = new QLabel(i18n("Snap Single:")); m_chkOnlyOneAssistant = new QCheckBox(optionsWidget); m_chkOnlyOneAssistant->setToolTip(i18nc("@info:tooltip","Make it only snap to a single assistant, prevents snapping mess while using the infinite assistants.")); m_chkOnlyOneAssistant->setCheckState(Qt::Checked);//turn on by default. connect(m_chkOnlyOneAssistant, SIGNAL(toggled(bool)), this, SLOT(setOnlyOneAssistantSnap(bool))); addOptionWidgetOption(m_chkOnlyOneAssistant, snapSingleLabel); - // set the assistant snapping options to hidden by default and toggle their visibility based based off snapping checkbox m_sliderMagnetism->setVisible(false); m_chkOnlyOneAssistant->setVisible(false); snapSingleLabel->setVisible(false); magnetismLabel->setVisible(false); - connect(m_chkAssistant, SIGNAL(toggled(bool)), m_sliderMagnetism, SLOT(setVisible(bool))); - connect(m_chkAssistant, SIGNAL(toggled(bool)), m_chkOnlyOneAssistant, SLOT(setVisible(bool))); - connect(m_chkAssistant, SIGNAL(toggled(bool)), snapSingleLabel, SLOT(setVisible(bool))); + connect(m_chkAssistant, SIGNAL(toggled(bool)), m_sliderMagnetism, SLOT(setVisible(bool))); + connect(m_chkAssistant, SIGNAL(toggled(bool)), m_chkOnlyOneAssistant, SLOT(setVisible(bool))); + connect(m_chkAssistant, SIGNAL(toggled(bool)), snapSingleLabel, SLOT(setVisible(bool))); connect(m_chkAssistant, SIGNAL(toggled(bool)), magnetismLabel, SLOT(setVisible(bool))); KisConfig cfg(true); slotSetSmoothingType(cfg.lineSmoothingType()); return optionsWidget; } +QList KisToolBrushFactory::createActionsImpl() +{ + KisActionRegistry *actionRegistry = KisActionRegistry::instance(); + QList actions = KisToolPaintFactoryBase::createActionsImpl(); + + actions << actionRegistry->makeQAction("set_no_brush_smoothing"); + actions << actionRegistry->makeQAction("set_simple_brush_smoothing"); + actions << actionRegistry->makeQAction("set_weighted_brush_smoothing"); + actions << actionRegistry->makeQAction("set_stabilizer_brush_smoothing"); + actions << actionRegistry->makeQAction("toggle_assistant"); + + return actions; + +} diff --git a/plugins/tools/basictools/kis_tool_brush.h b/plugins/tools/basictools/kis_tool_brush.h index 577f4b05a2..8acbc44001 100644 --- a/plugins/tools/basictools/kis_tool_brush.h +++ b/plugins/tools/basictools/kis_tool_brush.h @@ -1,165 +1,167 @@ /* * Copyright (c) 2003-2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_BRUSH_H_ #define KIS_TOOL_BRUSH_H_ #include "kis_tool_freehand.h" #include -#include "KoToolFactoryBase.h" +#include "KisToolPaintFactoryBase.h" #include #include #include #include #include #include class QCheckBox; class QComboBox; class KActionCollection; class KoCanvasBase; class KisSliderSpinBox; class KisDoubleSliderSpinBox; class KisToolBrush : public KisToolFreehand { Q_OBJECT Q_PROPERTY(int smoothnessQuality READ smoothnessQuality WRITE slotSetSmoothnessDistance NOTIFY smoothnessQualityChanged) Q_PROPERTY(qreal smoothnessFactor READ smoothnessFactor WRITE slotSetTailAgressiveness NOTIFY smoothnessFactorChanged) Q_PROPERTY(bool smoothPressure READ smoothPressure WRITE setSmoothPressure NOTIFY smoothPressureChanged) Q_PROPERTY(int smoothingType READ smoothingType WRITE slotSetSmoothingType NOTIFY smoothingTypeChanged) Q_PROPERTY(bool useScalableDistance READ useScalableDistance WRITE setUseScalableDistance NOTIFY useScalableDistanceChanged) Q_PROPERTY(bool useDelayDistance READ useDelayDistance WRITE setUseDelayDistance NOTIFY useDelayDistanceChanged) Q_PROPERTY(qreal delayDistance READ delayDistance WRITE setDelayDistance NOTIFY delayDistanceChanged) Q_PROPERTY(bool finishStabilizedCurve READ finishStabilizedCurve WRITE setFinishStabilizedCurve NOTIFY finishStabilizedCurveChanged) Q_PROPERTY(bool stabilizeSensors READ stabilizeSensors WRITE setStabilizeSensors NOTIFY stabilizeSensorsChanged) public: KisToolBrush(KoCanvasBase * canvas); ~KisToolBrush() override; QWidget * createOptionWidget() override; int smoothnessQuality() const; qreal smoothnessFactor() const; bool smoothPressure() const; int smoothingType() const; bool useScalableDistance() const; bool useDelayDistance() const; qreal delayDistance() const; bool finishStabilizedCurve() const; bool stabilizeSensors() const; protected: KConfigGroup m_configGroup; // only used in the multihand tool for now protected Q_SLOTS: void resetCursorStyle() override; public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; void slotSetSmoothnessDistance(qreal distance); void slotSetMagnetism(int magnetism); void slotSetSmoothingType(int index); void slotSetTailAgressiveness(qreal argh_rhhrr); void setSmoothPressure(bool value); void setUseScalableDistance(bool value); void setUseDelayDistance(bool value); void setDelayDistance(qreal value); void setStabilizeSensors(bool value); void setFinishStabilizedCurve(bool value); void updateSettingsViews() override; Q_SIGNALS: void smoothnessQualityChanged(); void smoothnessFactorChanged(); void smoothPressureChanged(); void smoothingTypeChanged(); void useScalableDistanceChanged(); void useDelayDistanceChanged(); void delayDistanceChanged(); void finishStabilizedCurveChanged(); void stabilizeSensorsChanged(); private: - void addSmoothingAction(int enumId, const QString &id, const QString &name, const QIcon &icon, KActionCollection *globalCollection); + void addSmoothingAction(int enumId, const QString &id); private: - QComboBox *m_cmbSmoothingType; + QComboBox *m_cmbSmoothingType {0}; - QCheckBox *m_chkAssistant; - KisSliderSpinBox *m_sliderMagnetism; - QCheckBox *m_chkOnlyOneAssistant; - KisDoubleSliderSpinBox *m_sliderSmoothnessDistance; - KisDoubleSliderSpinBox *m_sliderTailAggressiveness; - QCheckBox *m_chkSmoothPressure; - QCheckBox *m_chkUseScalableDistance; + QCheckBox *m_chkAssistant {0}; + KisSliderSpinBox *m_sliderMagnetism {0}; + QCheckBox *m_chkOnlyOneAssistant {0}; + KisDoubleSliderSpinBox *m_sliderSmoothnessDistance {0}; + KisDoubleSliderSpinBox *m_sliderTailAggressiveness {0}; + QCheckBox *m_chkSmoothPressure {0}; + QCheckBox *m_chkUseScalableDistance {0}; - QCheckBox *m_chkStabilizeSensors; - QCheckBox *m_chkDelayDistance; - KisDoubleSliderSpinBox *m_sliderDelayDistance; + QCheckBox *m_chkStabilizeSensors {0}; + QCheckBox *m_chkDelayDistance {0}; + KisDoubleSliderSpinBox *m_sliderDelayDistance {0}; - QCheckBox *m_chkFinishStabilizedCurve; + QCheckBox *m_chkFinishStabilizedCurve {0}; QSignalMapper m_signalMapper; }; -class KisToolBrushFactory : public KoToolFactoryBase +class KisToolBrushFactory : public KisToolPaintFactoryBase { public: KisToolBrushFactory() - : KoToolFactoryBase("KritaShape/KisToolBrush") { + : KisToolPaintFactoryBase("KritaShape/KisToolBrush") { setToolTip(i18n("Freehand Brush Tool")); // Temporarily setSection(TOOL_TYPE_SHAPE); setIconName(koIconNameCStr("krita_tool_freehand")); setShortcut(QKeySequence(Qt::Key_B)); setPriority(0); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); } ~KisToolBrushFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolBrush(canvas); } + QList createActionsImpl() override; + }; #endif // KIS_TOOL_BRUSH_H_ diff --git a/plugins/tools/basictools/kis_tool_ellipse.h b/plugins/tools/basictools/kis_tool_ellipse.h index f655aa662c..79fd0c9460 100644 --- a/plugins/tools/basictools/kis_tool_ellipse.h +++ b/plugins/tools/basictools/kis_tool_ellipse.h @@ -1,75 +1,74 @@ /* * kis_tool_ellipse.h - part of Krayon * * Copyright (c) 2000 John Califf * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Clarence Dang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_TOOL_ELLIPSE_H__ #define __KIS_TOOL_ELLIPSE_H__ #include "kis_tool_shape.h" #include "kis_types.h" -#include "KoToolFactoryBase.h" +#include "KisSelectionToolFactoryBase.h" #include "flake/kis_node_shape.h" #include #include - class KoCanvasBase; class KisToolEllipse : public KisToolEllipseBase { Q_OBJECT public: KisToolEllipse(KoCanvasBase * canvas); ~KisToolEllipse() override; protected Q_SLOTS: void resetCursorStyle() override; protected: void finishRect(const QRectF& rect, qreal roundCornersX, qreal roundCornersY) override; }; -class KisToolEllipseFactory : public KoToolFactoryBase +class KisToolEllipseFactory : public KisSelectionToolFactoryBase { public: KisToolEllipseFactory() - : KoToolFactoryBase("KritaShape/KisToolEllipse") { + : KisSelectionToolFactoryBase("KritaShape/KisToolEllipse") { setToolTip(i18n("Ellipse Tool")); setSection(TOOL_TYPE_SHAPE); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setIconName(koIconNameCStr("krita_tool_ellipse")); setPriority(3); } ~KisToolEllipseFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolEllipse(canvas); } }; #endif //__KIS_TOOL_ELLIPSE_H__ diff --git a/plugins/tools/basictools/kis_tool_fill.h b/plugins/tools/basictools/kis_tool_fill.h index 393a70ae11..8274863113 100644 --- a/plugins/tools/basictools/kis_tool_fill.h +++ b/plugins/tools/basictools/kis_tool_fill.h @@ -1,118 +1,118 @@ /* * kis_tool_fill.h - part of Krayon^Krita * * Copyright (c) 2004 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_TOOL_FILL_H_ #define KIS_TOOL_FILL_H_ #include "kis_tool_paint.h" #include #include #include #include #include #include class QWidget; class QCheckBox; class KisSliderSpinBox; class KoCanvasBase; class KisToolFill : public KisToolPaint { Q_OBJECT public: KisToolFill(KoCanvasBase * canvas); ~KisToolFill() override; void beginPrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; QWidget * createOptionWidget() override; public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void slotSetUseFastMode(bool); void slotSetThreshold(int); void slotSetUsePattern(bool); void slotSetSampleMerged(bool); void slotSetFillSelection(bool); void slotSetSizemod(int); void slotSetFeather(int); protected Q_SLOTS: void resetCursorStyle() override; protected: bool wantsAutoScroll() const override { return false; } private: void updateGUI(); private: int m_feather; int m_sizemod; QPoint m_startPos; int m_threshold; bool m_unmerged; bool m_usePattern; bool m_fillOnlySelection; QCheckBox *m_useFastMode; KisSliderSpinBox *m_slThreshold; KisSliderSpinBox *m_sizemodWidget; KisSliderSpinBox *m_featherWidget; QCheckBox *m_checkUsePattern; QCheckBox *m_checkSampleMerged; QCheckBox *m_checkFillSelection; KConfigGroup m_configGroup; }; -#include "KoToolFactoryBase.h" +#include "KisToolPaintFactoryBase.h" -class KisToolFillFactory : public KoToolFactoryBase +class KisToolFillFactory : public KisToolPaintFactoryBase { public: KisToolFillFactory() - : KoToolFactoryBase("KritaFill/KisToolFill") { + : KisToolPaintFactoryBase("KritaFill/KisToolFill") { setToolTip(i18n("Fill Tool")); setSection(TOOL_TYPE_FILL); setPriority(0); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setIconName(koIconNameCStr("krita_tool_color_fill")); setShortcut( QKeySequence( Qt::Key_F ) ); setPriority(14); } ~KisToolFillFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolFill(canvas); } }; #endif //__filltool_h__ diff --git a/plugins/tools/basictools/kis_tool_gradient.h b/plugins/tools/basictools/kis_tool_gradient.h index 700297d57b..e3a620e6ee 100644 --- a/plugins/tools/basictools/kis_tool_gradient.h +++ b/plugins/tools/basictools/kis_tool_gradient.h @@ -1,131 +1,131 @@ /* * kis_tool_line.h - part of Krayon * * Copyright (c) 2000 John Califf * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 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. */ #ifndef KIS_TOOL_GRADIENT_H_ #define KIS_TOOL_GRADIENT_H_ #include -#include +#include #include #include #include #include #include #include #include #include class QLabel; class QPoint; class QWidget; class QCheckBox; class KComboBox; class KisDoubleSliderSpinBox; class KisToolGradient : public KisToolPaint { Q_OBJECT public: KisToolGradient(KoCanvasBase * canvas); ~KisToolGradient() override; void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; void paint(QPainter &painter, const KoViewConverter &converter) override; QWidget* createOptionWidget() override; public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; private Q_SLOTS: void slotSetShape(int); void slotSetRepeat(int); void slotSetReverse(bool); void slotSetAntiAliasThreshold(qreal); void setOpacity(qreal opacity); protected Q_SLOTS: void resetCursorStyle() override; private Q_SLOTS: void areaDone(const QRect & rc) { currentNode()->setDirty(rc); // Starts computing the projection for the area we've done. } private: void paintLine(QPainter& gc); QPointF straightLine(QPointF point); QPointF m_startPos; QPointF m_endPos; KisGradientPainter::enumGradientShape m_shape; KisGradientPainter::enumGradientRepeat m_repeat; bool m_reverse; double m_antiAliasThreshold; QLabel *m_lbShape; QLabel *m_lbRepeat; QCheckBox *m_ckReverse; KComboBox *m_cmbShape; KComboBox *m_cmbRepeat; QLabel *m_lbAntiAliasThreshold; KisDoubleSliderSpinBox *m_slAntiAliasThreshold; KConfigGroup m_configGroup; }; -class KisToolGradientFactory : public KoToolFactoryBase +class KisToolGradientFactory : public KisToolPaintFactoryBase { public: KisToolGradientFactory() - : KoToolFactoryBase("KritaFill/KisToolGradient") { + : KisToolPaintFactoryBase("KritaFill/KisToolGradient") { setToolTip(i18n("Gradient Tool")); setSection(TOOL_TYPE_FILL); setIconName(koIconNameCStr("krita_tool_gradient")); setShortcut(QKeySequence(Qt::Key_G)); setPriority(1); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); } ~KisToolGradientFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolGradient(canvas); } }; #endif //KIS_TOOL_GRADIENT_H_ diff --git a/plugins/tools/basictools/kis_tool_move.cc b/plugins/tools/basictools/kis_tool_move.cc index b3ef5ae296..ee6bc8f467 100644 --- a/plugins/tools/basictools/kis_tool_move.cc +++ b/plugins/tools/basictools/kis_tool_move.cc @@ -1,628 +1,665 @@ /* * Copyright (c) 1999 Matthias Elter * 1999 Michael Koch * 2002 Patrick Julien * 2004 Boudewijn Rempt * 2016 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_move.h" #include #include "kis_cursor.h" #include "kis_selection.h" #include "kis_canvas2.h" #include "kis_image.h" #include "kis_tool_utils.h" #include "kis_paint_layer.h" #include "strokes/move_stroke_strategy.h" #include "kis_tool_movetooloptionswidget.h" #include "strokes/move_selection_stroke_strategy.h" #include "kis_resources_snapshot.h" #include "kis_action_registry.h" #include "krita_utils.h" #include #include #include "kis_node_manager.h" #include "kis_signals_blocker.h" #include struct KisToolMoveState : KisToolChangesTrackerData, boost::equality_comparable { KisToolMoveState(QPoint _accumulatedOffset) : accumulatedOffset(_accumulatedOffset) {} KisToolChangesTrackerData* clone() const { return new KisToolMoveState(*this); } bool operator ==(const KisToolMoveState &rhs) { return accumulatedOffset == rhs.accumulatedOffset; } QPoint accumulatedOffset; }; KisToolMove::KisToolMove(KoCanvasBase * canvas) : KisTool(canvas, KisCursor::moveCursor()), m_updateCursorCompressor(100, KisSignalCompressor::FIRST_ACTIVE) { m_canvas = dynamic_cast(canvas); - setObjectName("tool_move"); - m_optionsWidget = 0; - QAction *a; - - KisActionRegistry *actionRegistry = KisActionRegistry::instance(); - a = actionRegistry->makeQAction("movetool-move-up", this); - addAction("movetool-move-up", a); - connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Up, false);}); - - a = actionRegistry->makeQAction("movetool-move-down", this); - addAction("movetool-move-down", a); - connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Down, false);}); - - a = actionRegistry->makeQAction("movetool-move-left", this); - addAction("movetool-move-left", a); - connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Left, false);}); - - a = actionRegistry->makeQAction("movetool-move-right", this); - addAction("movetool-move-right", a); - connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Right, false);}); - - a = actionRegistry->makeQAction("movetool-move-up-more", this); - addAction("movetool-move-up-more", a); - connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Up, true);}); - - a = actionRegistry->makeQAction("movetool-move-down-more", this); - addAction("movetool-move-down-more", a); - connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Down, true);}); - - a = actionRegistry->makeQAction("movetool-move-left-more", this); - addAction("movetool-move-left-more", a); - connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Left, true);}); + m_showCoordinatesAction = action("movetool-show-coordinates"); + createOptionWidget(); - a = actionRegistry->makeQAction("movetool-move-right-more", this); - addAction("movetool-move-right-more", a); - connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Right, true);}); - - m_showCoordinatesAction = actionRegistry->makeQAction("movetool-show-coordinates", this); - addAction("movetool-show-coordinates", m_showCoordinatesAction); + setObjectName("tool_move"); - connect(&m_changesTracker, - SIGNAL(sigConfigChanged(KisToolChangesTrackerDataSP)), - SLOT(slotTrackerChangedConfig(KisToolChangesTrackerDataSP))); + m_showCoordinatesAction = action("movetool-show-coordinates"); connect(&m_updateCursorCompressor, SIGNAL(timeout()), this, SLOT(resetCursorStyle())); } KisToolMove::~KisToolMove() { endStroke(); } void KisToolMove::resetCursorStyle() { KisTool::resetCursorStyle(); if (!isActive()) return; KisImageSP image = this->image(); KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image, currentNode(), this->canvas()->resourceManager()); KisSelectionSP selection = resources->activeSelection(); KisNodeList nodes = fetchSelectedNodes(moveToolMode(), &m_lastCursorPos, selection); if (nodes.isEmpty()) { canvas()->setCursor(Qt::ForbiddenCursor); } } KisNodeList KisToolMove::fetchSelectedNodes(MoveToolMode mode, const QPoint *pixelPoint, KisSelectionSP selection) { KisNodeList nodes; KisImageSP image = this->image(); if (mode != MoveSelectedLayer && pixelPoint) { const bool wholeGroup = !selection && mode == MoveGroup; KisNodeSP node = KisToolUtils::findNode(image->root(), *pixelPoint, wholeGroup); if (node) { nodes = {node}; } } if (nodes.isEmpty()) { nodes = this->selectedNodes(); KritaUtils::filterContainer(nodes, [](KisNodeSP node) { return node->isEditable(); }); } return nodes; } bool KisToolMove::startStrokeImpl(MoveToolMode mode, const QPoint *pos) { KisNodeSP node; KisImageSP image = this->image(); KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image, currentNode(), this->canvas()->resourceManager()); KisSelectionSP selection = resources->activeSelection(); KisNodeList nodes = fetchSelectedNodes(mode, pos, selection); if (nodes.size() == 1) { node = nodes.first(); } if (nodes.isEmpty()) { return false; } /** * If the target node has changed, the stroke should be * restarted. Otherwise just continue processing current node. */ if (m_strokeId && !tryEndPreviousStroke(nodes)) { return true; } initHandles(nodes); KisStrokeStrategy *strategy; KisPaintLayerSP paintLayer = node ? dynamic_cast(node.data()) : 0; if (paintLayer && selection && !selection->isTotallyUnselected(image->bounds())) { strategy = new MoveSelectionStrokeStrategy(paintLayer, selection, image.data(), image.data()); } else { strategy = new MoveStrokeStrategy(nodes, image.data(), image.data()); } m_strokeId = image->startStroke(strategy); m_currentlyProcessingNodes = nodes; m_accumulatedOffset = QPoint(); KIS_SAFE_ASSERT_RECOVER(m_changesTracker.isEmpty()) { m_changesTracker.reset(); } commitChanges(); return true; } QPoint KisToolMove::currentOffset() const { return m_accumulatedOffset + m_dragPos - m_dragStart; } void KisToolMove::notifyGuiAfterMove(bool showFloatingMessage) { if (!m_optionsWidget) return; const QPoint currentTopLeft = m_handlesRect.topLeft() + currentOffset(); KisSignalsBlocker b(m_optionsWidget); emit moveInNewPosition(currentTopLeft); // TODO: fetch this info not from options widget, but from config const bool showCoordinates = m_optionsWidget->showCoordinates(); if (showCoordinates && showFloatingMessage) { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); kisCanvas->viewManager()-> showFloatingMessage( i18nc("floating message in move tool", "X: %1 px, Y: %2 px", currentTopLeft.x(), currentTopLeft.y()), QIcon(), 1000, KisFloatingMessage::High); } } bool KisToolMove::tryEndPreviousStroke(KisNodeList nodes) { if (!m_strokeId) return false; bool strokeEnded = false; if (!KritaUtils::compareListsUnordered(nodes, m_currentlyProcessingNodes)) { endStroke(); strokeEnded = true; } return strokeEnded; } void KisToolMove::commitChanges() { KIS_SAFE_ASSERT_RECOVER_RETURN(m_strokeId); QSharedPointer newState(new KisToolMoveState(m_accumulatedOffset)); KisToolMoveState *lastState = dynamic_cast(m_changesTracker.lastState().data()); if (lastState && *lastState == *newState) return; m_changesTracker.commitConfig(newState); } void KisToolMove::moveDiscrete(MoveDirection direction, bool big) { if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging if (!currentNode()->isEditable()) return; // Don't move invisible nodes if (startStrokeImpl(MoveSelectedLayer, 0)) { setMode(KisTool::PAINT_MODE); } // Larger movement if "shift" key is pressed. qreal scale = big ? m_optionsWidget->moveScale() : 1.0; qreal moveStep = m_optionsWidget->moveStep() * scale; const QPoint offset = direction == Up ? QPoint( 0, -moveStep) : direction == Down ? QPoint( 0, moveStep) : direction == Left ? QPoint(-moveStep, 0) : QPoint( moveStep, 0) ; m_accumulatedOffset += offset; image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(); commitChanges(); setMode(KisTool::HOVER_MODE); } void KisToolMove::activate(ToolActivation toolActivation, const QSet &shapes) { KisTool::activate(toolActivation, shapes); - QRect totalBounds; + + QAction *a = action("movetool-move-up"); + connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Up, false);}); + + a = action("movetool-move-down"); + connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Down, false);}); + + a = action("movetool-move-left"); + connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Left, false);}); + + a = action("movetool-move-right"); + connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Right, false);}); + + a = action("movetool-move-up-more"); + connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Up, true);}); + + a = action("movetool-move-down-more"); + connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Down, true);}); + + a = action("movetool-move-left-more"); + connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Left, true);}); + + a = action("movetool-move-right-more"); + connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Right, true);}); + + connect(m_showCoordinatesAction, SIGNAL(triggered(bool)), m_optionsWidget, SLOT(setShowCoordinates(bool)), Qt::UniqueConnection); + connect(m_optionsWidget, SIGNAL(showCoordinatesChanged(bool)), m_showCoordinatesAction, SLOT(setChecked(bool)), Qt::UniqueConnection); + + connect(&m_changesTracker, + SIGNAL(sigConfigChanged(KisToolChangesTrackerDataSP)), + SLOT(slotTrackerChangedConfig(KisToolChangesTrackerDataSP))); + slotNodeChanged(this->selectedNodes()); } void KisToolMove::paint(QPainter& gc, const KoViewConverter &converter) { Q_UNUSED(converter); if (m_strokeId) { QPainterPath handles; handles.addRect(m_handlesRect.translated(currentOffset())); QPainterPath path = pixelToView(handles); paintToolOutline(&gc, path); } } void KisToolMove::initHandles(const KisNodeList &nodes) { /** * The handles should be initialized only once, **before** the start of * the stroke. If the nodes change, we should restart the stroke. */ KIS_SAFE_ASSERT_RECOVER_NOOP(!m_strokeId); m_handlesRect = QRect(); for (KisNodeSP node : nodes) { node->exactBounds(); m_handlesRect |= node->exactBounds(); } if (image()->globalSelection()) { m_handlesRect &= image()->globalSelection()->selectedExactRect(); } } void KisToolMove::deactivate() { + QAction *a = action("movetool-move-up"); + disconnect(a, 0, this, 0); + + a = action("movetool-move-down"); + disconnect(a, 0, this, 0); + + a = action("movetool-move-left"); + disconnect(a, 0, this, 0); + + a = action("movetool-move-right"); + disconnect(a, 0, this, 0); + + a = action("movetool-move-up-more"); + disconnect(a, 0, this, 0); + + a = action("movetool-move-down-more"); + disconnect(a, 0, this, 0); + + a = action("movetool-move-left-more"); + disconnect(a, 0, this, 0); + + a = action("movetool-move-right-more"); + disconnect(a, 0, this, 0); + + disconnect(m_showCoordinatesAction, 0, this, 0); + disconnect(m_optionsWidget, 0, this, 0); + endStroke(); KisTool::deactivate(); } void KisToolMove::requestStrokeEnd() { endStroke(); } void KisToolMove::requestStrokeCancellation() { cancelStroke(); } void KisToolMove::requestUndoDuringStroke() { if (!m_strokeId) return; if (m_changesTracker.isEmpty()) { cancelStroke(); } else { m_changesTracker.requestUndo(); } } void KisToolMove::beginPrimaryAction(KoPointerEvent *event) { startAction(event, moveToolMode()); } void KisToolMove::continuePrimaryAction(KoPointerEvent *event) { continueAction(event); } void KisToolMove::endPrimaryAction(KoPointerEvent *event) { endAction(event); } void KisToolMove::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { // Ctrl+Right click toggles between moving current layer and moving layer w/ content if (action == PickFgNode || action == PickBgImage) { MoveToolMode mode = moveToolMode(); if (mode == MoveSelectedLayer) { mode = MoveFirstLayer; } else if (mode == MoveFirstLayer) { mode = MoveSelectedLayer; } startAction(event, mode); } else { startAction(event, MoveGroup); } } void KisToolMove::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(action) continueAction(event); } void KisToolMove::endAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(action) endAction(event); } void KisToolMove::mouseMoveEvent(KoPointerEvent *event) { m_lastCursorPos = convertToPixelCoord(event).toPoint(); KisTool::mouseMoveEvent(event); if (moveToolMode() == MoveFirstLayer) { m_updateCursorCompressor.start(); } } void KisToolMove::startAction(KoPointerEvent *event, MoveToolMode mode) { QPoint pos = convertToPixelCoordAndSnap(event).toPoint(); m_dragStart = pos; m_dragPos = pos; if (startStrokeImpl(mode, &pos)) { setMode(KisTool::PAINT_MODE); } else { event->ignore(); m_dragPos = QPoint(); m_dragStart = QPoint(); } m_canvas->updateCanvas(); } void KisToolMove::continueAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); if (!m_strokeId) return; QPoint pos = convertToPixelCoordAndSnap(event).toPoint(); pos = applyModifiers(event->modifiers(), pos); m_dragPos = pos; drag(pos); notifyGuiAfterMove(); m_canvas->updateCanvas(); } void KisToolMove::endAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); setMode(KisTool::HOVER_MODE); if (!m_strokeId) return; QPoint pos = convertToPixelCoordAndSnap(event).toPoint(); pos = applyModifiers(event->modifiers(), pos); drag(pos); m_accumulatedOffset += pos - m_dragStart; m_dragStart = QPoint(); m_dragPos = QPoint(); commitChanges(); notifyGuiAfterMove(); m_canvas->updateCanvas(); } void KisToolMove::drag(const QPoint& newPos) { KisImageWSP image = currentImage(); QPoint offset = m_accumulatedOffset + newPos - m_dragStart; image->addJob(m_strokeId, new MoveStrokeStrategy::Data(offset)); } void KisToolMove::endStroke() { if (!m_strokeId) return; KisImageSP image = currentImage(); image->endStroke(m_strokeId); m_strokeId.clear(); m_changesTracker.reset(); m_currentlyProcessingNodes.clear(); m_accumulatedOffset = QPoint(); m_canvas->updateCanvas(); } void KisToolMove::slotTrackerChangedConfig(KisToolChangesTrackerDataSP state) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_strokeId); KisToolMoveState *newState = dynamic_cast(state.data()); KIS_SAFE_ASSERT_RECOVER_RETURN(newState); if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging m_accumulatedOffset = newState->accumulatedOffset; image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(); } void KisToolMove::cancelStroke() { if (!m_strokeId) return; KisImageSP image = currentImage(); image->cancelStroke(m_strokeId); m_strokeId.clear(); m_changesTracker.reset(); m_currentlyProcessingNodes.clear(); m_accumulatedOffset = QPoint(); notifyGuiAfterMove(); m_canvas->updateCanvas(); } QWidget* KisToolMove::createOptionWidget() { if (!currentImage()) return 0; m_optionsWidget = new MoveToolOptionsWidget(0, currentImage()->xRes(), toolId()); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(m_optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); m_optionsWidget->layout()->addWidget(specialSpacer); m_optionsWidget->setFixedHeight(m_optionsWidget->sizeHint().height()); - connect(m_showCoordinatesAction, SIGNAL(triggered(bool)), m_optionsWidget, SLOT(setShowCoordinates(bool))); - connect(m_optionsWidget, SIGNAL(showCoordinatesChanged(bool)), m_showCoordinatesAction, SLOT(setChecked(bool))); - m_showCoordinatesAction->setChecked(m_optionsWidget->showCoordinates()); m_optionsWidget->slotSetTranslate(m_handlesRect.topLeft() + currentOffset()); connect(m_optionsWidget, SIGNAL(sigSetTranslateX(int)), SLOT(moveBySpinX(int))); connect(m_optionsWidget, SIGNAL(sigSetTranslateY(int)), SLOT(moveBySpinY(int))); connect(m_optionsWidget, SIGNAL(sigRequestCommitOffsetChanges()), this, SLOT(commitChanges())); connect(this, SIGNAL(moveInNewPosition(QPoint)), m_optionsWidget, SLOT(slotSetTranslate(QPoint))); KisCanvas2 *kisCanvas = dynamic_cast(canvas()); connect(kisCanvas->viewManager()->nodeManager(), SIGNAL(sigUiNeedChangeSelectedNodes(KisNodeList)), this, SLOT(slotNodeChanged(KisNodeList))); return m_optionsWidget; } KisToolMove::MoveToolMode KisToolMove::moveToolMode() const { if (m_optionsWidget) return m_optionsWidget->mode(); return MoveSelectedLayer; } QPoint KisToolMove::applyModifiers(Qt::KeyboardModifiers modifiers, QPoint pos) { QPoint move = pos - m_dragStart; // Snap to axis if (modifiers & Qt::ShiftModifier) { move = snapToClosestAxis(move); } // "Precision mode" - scale down movement by 1/5 if (modifiers & Qt::AltModifier) { const qreal SCALE_FACTOR = .2; move = SCALE_FACTOR * move; } return m_dragStart + move; } void KisToolMove::moveBySpinX(int newX) { if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging if (!currentNode()->isEditable()) return; // Don't move invisible nodes if (startStrokeImpl(MoveSelectedLayer, 0)) { setMode(KisTool::PAINT_MODE); } m_accumulatedOffset.rx() = newX - m_handlesRect.x(); image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(false); setMode(KisTool::HOVER_MODE); } void KisToolMove::moveBySpinY(int newY) { if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging if (!currentNode()->isEditable()) return; // Don't move invisible nodes if (startStrokeImpl(MoveSelectedLayer, 0)) { setMode(KisTool::PAINT_MODE); } m_accumulatedOffset.ry() = newY - m_handlesRect.y(); image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset)); notifyGuiAfterMove(false); setMode(KisTool::HOVER_MODE); } void KisToolMove::slotNodeChanged(KisNodeList nodes) { if (m_strokeId && !tryEndPreviousStroke(nodes)) { return; } initHandles(nodes); notifyGuiAfterMove(false); } + +QList KisToolMoveFactory::createActionsImpl() +{ + KisActionRegistry *actionRegistry = KisActionRegistry::instance(); + QList actions = KisToolPaintFactoryBase::createActionsImpl(); + + actions << actionRegistry->makeQAction("movetool-move-up"); + actions << actionRegistry->makeQAction("movetool-move-down"); + actions << actionRegistry->makeQAction("movetool-move-left"); + actions << actionRegistry->makeQAction("movetool-move-right"); + actions << actionRegistry->makeQAction("movetool-move-up-more"); + actions << actionRegistry->makeQAction("movetool-move-down-more"); + actions << actionRegistry->makeQAction("movetool-move-left-more"); + actions << actionRegistry->makeQAction("movetool-move-right-more"); + actions << actionRegistry->makeQAction("movetool-show-coordinates"); + + return actions; + +} diff --git a/plugins/tools/basictools/kis_tool_move.h b/plugins/tools/basictools/kis_tool_move.h index e7c0c29f73..4bdf2dde41 100644 --- a/plugins/tools/basictools/kis_tool_move.h +++ b/plugins/tools/basictools/kis_tool_move.h @@ -1,192 +1,193 @@ /* * Copyright (c) 1999 Matthias Elter * 1999 Michael Koch * 2003 Patrick Julien * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_MOVE_H_ #define KIS_TOOL_MOVE_H_ -#include +#include #include #include #include #include #include #include #include #include #include "KisToolChangesTracker.h" #include "kis_signal_compressor.h" #include "kis_canvas2.h" class KoCanvasBase; class MoveToolOptionsWidget; class KisDocument; class KisToolMove : public KisTool { Q_OBJECT Q_ENUMS(MoveToolMode); public: KisToolMove(KoCanvasBase * canvas); ~KisToolMove() override; /** * @brief wantsAutoScroll * reimplemented from KoToolBase * there's an issue where autoscrolling with this tool never makes the * stroke end, so we return false here so that users don't get stuck with * the tool. See bug 362659 * @return false */ bool wantsAutoScroll() const override { return false; } public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; public Q_SLOTS: void requestStrokeEnd() override; void requestStrokeCancellation() override; void requestUndoDuringStroke() override; protected Q_SLOTS: void resetCursorStyle() override; public: enum MoveToolMode { MoveSelectedLayer, MoveFirstLayer, MoveGroup }; enum MoveDirection { Up, Down, Left, Right }; void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override; void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override; void endAlternateAction(KoPointerEvent *event, AlternateAction action) override; void mouseMoveEvent(KoPointerEvent *event) override; void startAction(KoPointerEvent *event, MoveToolMode mode); void continueAction(KoPointerEvent *event); void endAction(KoPointerEvent *event); void paint(QPainter& gc, const KoViewConverter &converter) override; void initHandles(const KisNodeList &nodes); - QWidget* createOptionWidget() override; + QWidget *createOptionWidget() override; void updateUIUnit(int newUnit); MoveToolMode moveToolMode() const; void setShowCoordinates(bool value); public Q_SLOTS: void moveDiscrete(MoveDirection direction, bool big); void moveBySpinX(int newX); void moveBySpinY(int newY); void slotNodeChanged(KisNodeList nodes); void commitChanges(); Q_SIGNALS: void moveToolModeChanged(); void moveInNewPosition(QPoint); private: void drag(const QPoint& newPos); void cancelStroke(); QPoint applyModifiers(Qt::KeyboardModifiers modifiers, QPoint pos); bool startStrokeImpl(MoveToolMode mode, const QPoint *pos); QPoint currentOffset() const; void notifyGuiAfterMove(bool showFloatingMessage = true); bool tryEndPreviousStroke(KisNodeList nodes); KisNodeList fetchSelectedNodes(MoveToolMode mode, const QPoint *pixelPoint, KisSelectionSP selection); private Q_SLOTS: void endStroke(); void slotTrackerChangedConfig(KisToolChangesTrackerDataSP state); private: - MoveToolOptionsWidget* m_optionsWidget; + MoveToolOptionsWidget* m_optionsWidget {0}; QPoint m_dragStart; ///< Point where current cursor dragging began QPoint m_accumulatedOffset; ///< Total offset including multiple clicks, up/down/left/right keys, etc. added together KisStrokeId m_strokeId; KisNodeList m_currentlyProcessingNodes; int m_resolution; - QAction *m_showCoordinatesAction; + QAction *m_showCoordinatesAction {0}; - KisCanvas2* m_canvas; + KisCanvas2 *m_canvas {0}; QPoint m_dragPos; QRect m_handlesRect; KisToolChangesTracker m_changesTracker; QPoint m_lastCursorPos; KisSignalCompressor m_updateCursorCompressor; }; -class KisToolMoveFactory : public KoToolFactoryBase +class KisToolMoveFactory : public KisToolPaintFactoryBase { public: KisToolMoveFactory() - : KoToolFactoryBase("KritaTransform/KisToolMove") { + : KisToolPaintFactoryBase("KritaTransform/KisToolMove") { setToolTip(i18n("Move Tool")); setSection(TOOL_TYPE_TRANSFORM); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setPriority(3); setIconName(koIconNameCStr("krita_tool_move")); setShortcut(QKeySequence( Qt::Key_T)); } ~KisToolMoveFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolMove(canvas); } + QList createActionsImpl() override; }; #endif // KIS_TOOL_MOVE_H_ diff --git a/plugins/tools/basictools/kis_tool_multihand.cpp b/plugins/tools/basictools/kis_tool_multihand.cpp index 059d558a53..79e4928469 100644 --- a/plugins/tools/basictools/kis_tool_multihand.cpp +++ b/plugins/tools/basictools/kis_tool_multihand.cpp @@ -1,421 +1,421 @@ /* * Copyright (c) 2011 Lukáš Tvrdý * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_multihand.h" #include #include #include #include #include #include #include #include "kis_canvas2.h" #include "kis_cursor.h" #include "kis_tool_multihand_helper.h" static const int MAXIMUM_BRUSHES = 50; #include #ifdef Q_OS_WIN // quoting DRAND48(3) man-page: // These functions are declared obsolete by SVID 3, // which states that rand(3) should be used instead. #define drand48() (static_cast(qrand()) / static_cast(RAND_MAX)) #endif KisToolMultihand::KisToolMultihand(KoCanvasBase *canvas) : KisToolBrush(canvas), m_transformMode(SYMMETRY), m_angle(0), m_handsCount(6), m_mirrorVertically(false), m_mirrorHorizontally(false), m_showAxes(false), m_translateRadius(100), m_setupAxesFlag(false) , customUI(0) { m_helper = new KisToolMultihandHelper(paintingInformationBuilder(), kundo2_i18n("Multibrush Stroke")); resetHelper(m_helper); if (image()) { m_axesPoint = QPointF(0.5 * image()->width(), 0.5 * image()->height()); } } KisToolMultihand::~KisToolMultihand() { } void KisToolMultihand::beginPrimaryAction(KoPointerEvent *event) { if(m_setupAxesFlag) { setMode(KisTool::OTHER); m_axesPoint = convertToPixelCoord(event->point); requestUpdateOutline(event->point, 0); updateCanvas(); } else { initTransformations(); KisToolFreehand::beginPrimaryAction(event); } } void KisToolMultihand::continuePrimaryAction(KoPointerEvent *event) { if(mode() == KisTool::OTHER) { m_axesPoint = convertToPixelCoord(event->point); requestUpdateOutline(event->point, 0); updateCanvas(); } else { KisToolFreehand::continuePrimaryAction(event); } } void KisToolMultihand::endPrimaryAction(KoPointerEvent *event) { if(mode() == KisTool::OTHER) { setMode(KisTool::HOVER_MODE); requestUpdateOutline(event->point, 0); finishAxesSetup(); } else { KisToolFreehand::endPrimaryAction(event); } } void KisToolMultihand::paint(QPainter& gc, const KoViewConverter &converter) { if(m_setupAxesFlag) { int diagonal = (currentImage()->height() + currentImage()->width()); QPainterPath path; path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle), m_axesPoint.y()-diagonal*sin(m_angle)); path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle), m_axesPoint.y()+diagonal*sin(m_angle)); path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()-diagonal*sin(m_angle+M_PI_2)); path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()+diagonal*sin(m_angle+M_PI_2)); paintToolOutline(&gc, pixelToView(path)); } else { KisToolFreehand::paint(gc, converter); if(m_showAxes){ int diagonal = (currentImage()->height() + currentImage()->width()); QPainterPath path; path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle), m_axesPoint.y()-diagonal*sin(m_angle)); path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle), m_axesPoint.y()+diagonal*sin(m_angle)); path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()-diagonal*sin(m_angle+M_PI_2)); path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()+diagonal*sin(m_angle+M_PI_2)); paintToolOutline(&gc, pixelToView(path)); } } } void KisToolMultihand::initTransformations() { QVector transformations; QTransform m; if(m_transformMode == SYMMETRY) { qreal angle = 0; qreal angleStep = (2 * M_PI) / m_handsCount; for(int i = 0; i < m_handsCount; i++) { m.translate(m_axesPoint.x(), m_axesPoint.y()); m.rotateRadians(angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); angle += angleStep; } } else if(m_transformMode == MIRROR) { transformations << m; if (m_mirrorHorizontally) { m.translate(m_axesPoint.x(),m_axesPoint.y()); m.rotateRadians(m_angle); m.scale(-1,1); m.rotateRadians(-m_angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); } if (m_mirrorVertically) { m.translate(m_axesPoint.x(),m_axesPoint.y()); m.rotateRadians(m_angle); m.scale(1,-1); m.rotateRadians(-m_angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); } if (m_mirrorVertically && m_mirrorHorizontally){ m.translate(m_axesPoint.x(),m_axesPoint.y()); m.rotateRadians(m_angle); m.scale(-1,-1); m.rotateRadians(-m_angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); } } else if(m_transformMode == SNOWFLAKE) { qreal angle = 0; qreal angleStep = (2 * M_PI) / m_handsCount/4; for(int i = 0; i < m_handsCount*4; i++) { if ((i%2)==1) { m.translate(m_axesPoint.x(), m_axesPoint.y()); m.rotateRadians(m_angle-angleStep); m.rotateRadians(angle); m.scale(-1,1); m.rotateRadians(-m_angle+angleStep); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); angle += angleStep*2; } else { m.translate(m_axesPoint.x(), m_axesPoint.y()); m.rotateRadians(m_angle-angleStep); m.rotateRadians(angle); m.rotateRadians(-m_angle+angleStep); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); angle += angleStep*2; } } } else /* if(m_transformationNode == TRANSLATE) */ { /** * TODO: currently, the seed is the same for all the * strokes */ for (int i = 0; i < m_handsCount; i++){ qreal angle = drand48() * M_PI * 2; qreal length = drand48(); // convert the Polar coordinates to Cartesian coordinates qreal nx = (m_translateRadius * cos(angle) * length); qreal ny = (m_translateRadius * sin(angle) * length); m.translate(m_axesPoint.x(),m_axesPoint.y()); m.rotateRadians(m_angle); m.translate(nx,ny); m.rotateRadians(-m_angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); } } m_helper->setupTransformations(transformations); } QWidget* KisToolMultihand::createOptionWidget() { QWidget *widget = KisToolBrush::createOptionWidget(); customUI = new KisToolMultiHandConfigWidget(); // brush smoothing option. customUI->layout()->addWidget(widget); customUI->smoothingOptionsLayout->addWidget(widget); // setup common parameters that all of the modes will see connect(customUI->showAxesCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetAxesVisible(bool))); customUI->showAxesCheckbox->setChecked((bool)m_configGroup.readEntry("showAxes", false)); - connect(image(), SIGNAL(sigSizeChanged(QPointF,QPointF)), this, SLOT(resetAxes(QPointF,QPointF))); + connect(image(), SIGNAL(sigSizeChanged(QPointF,QPointF)), this, SLOT(resetAxes())); customUI->moveOriginButton->setCheckable(true); connect(customUI->moveOriginButton, SIGNAL(clicked(bool)),this, SLOT(activateAxesPointModeSetup())); connect(customUI->resetOriginButton, SIGNAL(released()), this, SLOT(resetAxes())); customUI->multihandTypeCombobox->addItem(i18n("Symmetry"),int(SYMMETRY)); // axis mode customUI->multihandTypeCombobox->addItem(i18n("Mirror"),int(MIRROR)); customUI->multihandTypeCombobox->addItem(i18n("Translate"),int(TRANSLATE)); customUI->multihandTypeCombobox->addItem(i18n("Snowflake"),int(SNOWFLAKE)); connect(customUI->multihandTypeCombobox,SIGNAL(currentIndexChanged(int)),this, SLOT(slotSetTransformMode(int))); customUI->multihandTypeCombobox->setCurrentIndex(m_configGroup.readEntry("transformMode", 0)); slotSetTransformMode(customUI->multihandTypeCombobox->currentIndex()); customUI->axisRotationSpinbox->setSuffix(QChar(Qt::Key_degree)); // origin rotation customUI->axisRotationSpinbox->setSingleStep(0.5); customUI->axisRotationSpinbox->setRange(0.0, 90.0, 1); customUI->axisRotationSpinbox->setValue(m_configGroup.readEntry("axesAngle", 0.0)); connect( customUI->axisRotationSpinbox, SIGNAL(valueChanged(qreal)),this, SLOT(slotSetAxesAngle(qreal))); // symmetry mode options customUI->brushCountSpinBox->setRange(1, MAXIMUM_BRUSHES); connect(customUI->brushCountSpinBox, SIGNAL(valueChanged(int)),this, SLOT(slotSetHandsCount(int))); customUI->brushCountSpinBox->setValue(m_configGroup.readEntry("handsCount", 4)); // mirror mode specific options connect(customUI->horizontalCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetMirrorHorizontally(bool))); customUI->horizontalCheckbox->setChecked((bool)m_configGroup.readEntry("mirrorHorizontally", false)); connect(customUI->verticalCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetMirrorVertically(bool))); customUI->verticalCheckbox->setChecked((bool)m_configGroup.readEntry("mirrorVertically", false)); // translate mode options customUI->translationRadiusSpinbox->setRange(0, 200); customUI->translationRadiusSpinbox->setSuffix(i18n(" px")); customUI->translationRadiusSpinbox->setValue(m_configGroup.readEntry("translateRadius", 0)); connect(customUI->translationRadiusSpinbox,SIGNAL(valueChanged(int)),this,SLOT(slotSetTranslateRadius(int))); // snowflake re-uses the existing options, so there is no special parameters for that... return static_cast(customUI); // keeping it in the native class until the end allows us to access the UI components } void KisToolMultihand::activateAxesPointModeSetup() { if (customUI->moveOriginButton->isChecked()){ m_setupAxesFlag = true; useCursor(KisCursor::crossCursor()); updateCanvas(); } else { finishAxesSetup(); } } void KisToolMultihand::resetAxes() { m_axesPoint = QPointF(0.5 * image()->width(), 0.5 * image()->height()); finishAxesSetup(); } void KisToolMultihand::finishAxesSetup() { m_setupAxesFlag = false; customUI->moveOriginButton->setChecked(false); resetCursorStyle(); updateCanvas(); } void KisToolMultihand::updateCanvas() { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); Q_ASSERT(kisCanvas); kisCanvas->updateCanvas(); } void KisToolMultihand::slotSetHandsCount(int count) { m_handsCount = count; m_configGroup.writeEntry("handsCount", count); } void KisToolMultihand::slotSetAxesAngle(qreal angle) { //negative so axes rotates counter clockwise m_angle = -angle*M_PI/180; updateCanvas(); m_configGroup.writeEntry("axesAngle", angle); } void KisToolMultihand::slotSetTransformMode(int index) { m_transformMode = enumTransforModes(customUI->multihandTypeCombobox->itemData(index).toInt()); m_configGroup.writeEntry("transformMode", index); // hide all of the UI elements by default customUI->horizontalCheckbox->setVisible(false); customUI->verticalCheckbox->setVisible(false); customUI->translationRadiusSpinbox->setVisible(false); customUI->radiusLabel->setVisible(false); customUI->brushCountSpinBox->setVisible(false); customUI->brushesLabel->setVisible(false); // turn on what we need if (index == int(MIRROR)) { customUI->horizontalCheckbox->setVisible(true); customUI->verticalCheckbox->setVisible(true); } if (index == int(TRANSLATE)) { customUI->translationRadiusSpinbox->setVisible(true); customUI->radiusLabel->setVisible(true); } if (index == int(SYMMETRY) || index == int(SNOWFLAKE) || index == int(TRANSLATE) ) { customUI->brushCountSpinBox->setVisible(true); customUI->brushesLabel->setVisible(true); } } void KisToolMultihand::slotSetAxesVisible(bool vis) { m_showAxes = vis; updateCanvas(); m_configGroup.writeEntry("showAxes", vis); } void KisToolMultihand::slotSetMirrorVertically(bool mirror) { m_mirrorVertically = mirror; m_configGroup.writeEntry("mirrorVertically", mirror); } void KisToolMultihand::slotSetMirrorHorizontally(bool mirror) { m_mirrorHorizontally = mirror; m_configGroup.writeEntry("mirrorHorizontally", mirror); } void KisToolMultihand::slotSetTranslateRadius(int radius) { m_translateRadius = radius; m_configGroup.writeEntry("translateRadius", radius); } diff --git a/plugins/tools/basictools/kis_tool_multihand.h b/plugins/tools/basictools/kis_tool_multihand.h index a475506d2f..2f1f640f6e 100644 --- a/plugins/tools/basictools/kis_tool_multihand.h +++ b/plugins/tools/basictools/kis_tool_multihand.h @@ -1,121 +1,120 @@ /* * Copyright (c) 2011 Lukáš Tvrdý * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_TOOL_MULTIHAND_H #define __KIS_TOOL_MULTIHAND_H #include "kis_tool_brush.h" #include #include "kis_tool_multihand_config.h" class QPushButton; class QCheckBox; class QComboBox; class QStackedWidget; class KisSliderSpinBox; class KisToolMultihandHelper; - class KisToolMultihand : public KisToolBrush { Q_OBJECT public: KisToolMultihand(KoCanvasBase *canvas); ~KisToolMultihand() override; void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; protected: void paint(QPainter& gc, const KoViewConverter &converter) override; QWidget* createOptionWidget() override; private: void initTransformations(); void finishAxesSetup(); void updateCanvas(); private Q_SLOTS: void activateAxesPointModeSetup(); void resetAxes(); void slotSetHandsCount(int count); void slotSetAxesAngle(qreal angle); void slotSetTransformMode(int qcomboboxIndex); void slotSetAxesVisible(bool vis); void slotSetMirrorVertically(bool mirror); void slotSetMirrorHorizontally(bool mirror); void slotSetTranslateRadius(int radius); private: KisToolMultihandHelper *m_helper; enum enumTransforModes { SYMMETRY, MIRROR, TRANSLATE, SNOWFLAKE }; enumTransforModes m_transformMode; QPointF m_axesPoint; qreal m_angle; int m_handsCount; bool m_mirrorVertically; bool m_mirrorHorizontally; bool m_showAxes; int m_translateRadius; bool m_setupAxesFlag; QComboBox * m_transformModesComboBox; KisSliderSpinBox *m_handsCountSlider; KisDoubleSliderSpinBox *m_axesAngleSlider; QCheckBox *m_axesChCkBox; QStackedWidget *m_modeCustomOption; QCheckBox *m_mirrorVerticallyChCkBox; QCheckBox *m_mirrorHorizontallyChCkBox; KisSliderSpinBox *m_translateRadiusSlider; QPushButton *m_axesPointBtn; KisToolMultiHandConfigWidget* customUI; }; -class KisToolMultiBrushFactory : public KoToolFactoryBase +class KisToolMultiBrushFactory : public KisToolPaintFactoryBase { public: KisToolMultiBrushFactory() - : KoToolFactoryBase("KritaShape/KisToolMultiBrush") { + : KisToolPaintFactoryBase("KritaShape/KisToolMultiBrush") { setToolTip(i18n("Multibrush Tool")); // Temporarily setSection(TOOL_TYPE_SHAPE); setIconName(koIconNameCStr("krita_tool_multihand")); setShortcut(QKeySequence(Qt::Key_Q)); setPriority(11); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); } ~KisToolMultiBrushFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolMultihand(canvas); } }; #endif /* __KIS_TOOL_MULTIHAND_H */ diff --git a/plugins/tools/basictools/kis_tool_rectangle.h b/plugins/tools/basictools/kis_tool_rectangle.h index 5798e8841c..c12392b584 100644 --- a/plugins/tools/basictools/kis_tool_rectangle.h +++ b/plugins/tools/basictools/kis_tool_rectangle.h @@ -1,79 +1,79 @@ /* * kis_tool_rectangle.h - part of KImageShop^WKrayon^WKrita * * Copyright (c) 1999 Michael Koch * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_TOOL_RECTANGLE_H__ #define __KIS_TOOL_RECTANGLE_H__ #include "kis_tool_shape.h" #include "kis_types.h" -#include "KoToolFactoryBase.h" +#include "KisSelectionToolFactoryBase.h" #include "flake/kis_node_shape.h" #include #include class QRect; class KoCanvasBase; class KisToolRectangle : public KisToolRectangleBase { Q_OBJECT public: KisToolRectangle(KoCanvasBase * canvas); ~KisToolRectangle() override; protected: void finishRect(const QRectF& rect, qreal roundCornersX, qreal roundCornersY) override; protected Q_SLOTS: void resetCursorStyle() override; }; -class KisToolRectangleFactory : public KoToolFactoryBase +class KisToolRectangleFactory : public KisSelectionToolFactoryBase { public: KisToolRectangleFactory() - : KoToolFactoryBase("KritaShape/KisToolRectangle") { + : KisSelectionToolFactoryBase("KritaShape/KisToolRectangle") { setToolTip(i18n("Rectangle Tool")); setSection(TOOL_TYPE_SHAPE); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setIconName(koIconNameCStr("krita_tool_rectangle")); //setShortcut( Qt::Key_F6 ); setPriority(2); } ~KisToolRectangleFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolRectangle(canvas); } }; #endif // __KIS_TOOL_RECTANGLE_H__ diff --git a/plugins/tools/defaulttool/connectionTool/ConnectionTool.cpp b/plugins/tools/defaulttool/connectionTool/ConnectionTool.cpp index ae07841abd..c1666338da 100644 --- a/plugins/tools/defaulttool/connectionTool/ConnectionTool.cpp +++ b/plugins/tools/defaulttool/connectionTool/ConnectionTool.cpp @@ -1,987 +1,987 @@ /* This file is part of the KDE project * * Copyright (C) 2009 Thorsten Zachmann * Copyright (C) 2009 Jean-Nicolas Artaud * Copyright (C) 2011 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "ConnectionTool.h" #include #include #include #include "AddConnectionPointCommand.h" #include "RemoveConnectionPointCommand.h" #include "ChangeConnectionPointCommand.h" #include "MoveConnectionPointStrategy.h" #include "ConnectionPointWidget.h" #define TextShape_SHAPEID "TextShapeID" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_document_aware_spin_box_unit_manager.h" #include #include "kis_action_registry.h" #include #include #include #include #include #include ConnectionTool::ConnectionTool(KoCanvasBase *canvas) : KoToolBase(canvas) , m_editMode(Idle) , m_connectionType(KoConnectionShape::Standard) , m_currentShape(0) , m_activeHandle(-1) , m_currentStrategy(0) , m_oldSnapStrategies(0) , m_resetPaint(true) { QPixmap connectPixmap; connectPixmap.load(":/cursor_connect.png"); m_connectCursor = QCursor(connectPixmap, 4, 1); KisActionRegistry *actionRegistry = KisActionRegistry::instance(); m_editConnectionPoint = actionRegistry->makeQAction("toggle-edit-mode", this); m_editConnectionPoint->setCheckable(true); - addAction("toggle-edit-mode", m_editConnectionPoint); +// addAction("toggle-edit-mode", m_editConnectionPoint); m_alignPercent = actionRegistry->makeQAction("align-relative", this); m_alignPercent->setCheckable(true); - addAction("align-relative", m_alignPercent); +// addAction("align-relative", m_alignPercent); m_alignLeft = actionRegistry->makeQAction("align-left", this); m_alignLeft->setCheckable(true); - addAction("align-left", m_alignLeft); +// addAction("align-left", m_alignLeft); m_alignCenterH = actionRegistry->makeQAction("align-centerh", this); m_alignCenterH->setCheckable(true); - addAction("align-centerh", m_alignCenterH); +// addAction("align-centerh", m_alignCenterH); m_alignRight = actionRegistry->makeQAction("align-right", this); m_alignRight->setCheckable(true); - addAction("align-right", m_alignRight); +// addAction("align-right", m_alignRight); m_alignTop = actionRegistry->makeQAction("align-top", this); m_alignTop->setCheckable(true); - addAction("align-top", m_alignTop); +// addAction("align-top", m_alignTop); m_alignCenterV = actionRegistry->makeQAction("align-centerv", this); m_alignCenterV->setCheckable(true); - addAction("align-centerv", m_alignCenterV); +// addAction("align-centerv", m_alignCenterV); m_alignBottom = actionRegistry->makeQAction("align-bottom", this); m_alignBottom->setCheckable(true); - addAction("align-bottom", m_alignBottom); +// addAction("align-bottom", m_alignBottom); m_escapeAll = actionRegistry->makeQAction("escape-all", this); m_escapeAll->setCheckable(true); - addAction("escape-all", m_escapeAll); +// addAction("escape-all", m_escapeAll); m_escapeHorizontal = actionRegistry->makeQAction("escape-horizontal", this); m_escapeHorizontal->setCheckable(true); - addAction("escape-horizontal", m_escapeHorizontal); +// addAction("escape-horizontal", m_escapeHorizontal); m_escapeVertical = actionRegistry->makeQAction("escape-vertical", this); m_escapeVertical->setCheckable(true); - addAction("escape-vertical", m_escapeVertical); +// addAction("escape-vertical", m_escapeVertical); m_escapeLeft = actionRegistry->makeQAction("escape-left", this); m_escapeLeft->setCheckable(true); - addAction("escape-left", m_escapeLeft); +// addAction("escape-left", m_escapeLeft); m_escapeRight = actionRegistry->makeQAction("escape-right", this); m_escapeRight->setCheckable(true); - addAction("escape-right", m_escapeRight); +// addAction("escape-right", m_escapeRight); m_escapeUp = actionRegistry->makeQAction("escape-up", this); m_escapeUp->setCheckable(true); - addAction("escape-up", m_escapeUp); +// addAction("escape-up", m_escapeUp); m_escapeDown = actionRegistry->makeQAction("escape-down", this); m_escapeDown->setCheckable(true); - addAction("escape-down", m_escapeDown); +// addAction("escape-down", m_escapeDown); m_alignHorizontal = new QActionGroup(this); m_alignHorizontal->setExclusive(true); m_alignHorizontal->addAction(m_alignLeft); m_alignHorizontal->addAction(m_alignCenterH); m_alignHorizontal->addAction(m_alignRight); connect(m_alignHorizontal, SIGNAL(triggered(QAction*)), this, SLOT(horizontalAlignChanged())); m_alignVertical = new QActionGroup(this); m_alignVertical->setExclusive(true); m_alignVertical->addAction(m_alignTop); m_alignVertical->addAction(m_alignCenterV); m_alignVertical->addAction(m_alignBottom); connect(m_alignVertical, SIGNAL(triggered(QAction*)), this, SLOT(verticalAlignChanged())); m_alignRelative = new QActionGroup(this); m_alignRelative->setExclusive(true); m_alignRelative->addAction(m_alignPercent); connect(m_alignRelative, SIGNAL(triggered(QAction*)), this, SLOT(relativeAlignChanged())); m_escapeDirections = new QActionGroup(this); m_escapeDirections->setExclusive(true); m_escapeDirections->addAction(m_escapeAll); m_escapeDirections->addAction(m_escapeHorizontal); m_escapeDirections->addAction(m_escapeVertical); m_escapeDirections->addAction(m_escapeLeft); m_escapeDirections->addAction(m_escapeRight); m_escapeDirections->addAction(m_escapeUp); m_escapeDirections->addAction(m_escapeDown); connect(m_escapeDirections, SIGNAL(triggered(QAction*)), this, SLOT(escapeDirectionChanged())); connect(this, SIGNAL(connectionPointEnabled(bool)), m_alignHorizontal, SLOT(setEnabled(bool))); connect(this, SIGNAL(connectionPointEnabled(bool)), m_alignVertical, SLOT(setEnabled(bool))); connect(this, SIGNAL(connectionPointEnabled(bool)), m_alignRelative, SLOT(setEnabled(bool))); connect(this, SIGNAL(connectionPointEnabled(bool)), m_escapeDirections, SLOT(setEnabled(bool))); resetEditMode(); } ConnectionTool::~ConnectionTool() { } void ConnectionTool::paint(QPainter &painter, const KoViewConverter &converter) { // get the correctly sized rect for painting handles QRectF handleRect = handlePaintRect(QPointF()); painter.setRenderHint(QPainter::Antialiasing, true); if (m_currentStrategy) { painter.save(); m_currentStrategy->paint(painter, converter); painter.restore(); } QList shapes = canvas()->shapeManager()->shapes(); for (QList::const_iterator end = shapes.constBegin(); end != shapes.constEnd(); ++end) { KoShape *shape = *end; if (!dynamic_cast(shape)) { // only paint connection points of textShapes not inside a tos container and other shapes if (shape->shapeId() == TextShape_SHAPEID && dynamic_cast(shape->parent())) { continue; } painter.save(); painter.setPen(Qt::black); QTransform transform = shape->absoluteTransformation(0); KoShape::applyConversion(painter, converter); // Draw all the connection points of the shape KoConnectionPoints connectionPoints = shape->connectionPoints(); KoConnectionPoints::const_iterator cp = connectionPoints.constBegin(); KoConnectionPoints::const_iterator lastCp = connectionPoints.constEnd(); for (; cp != lastCp; ++cp) { if (shape == findNonConnectionShapeAtPosition(transform.map(cp.value().position))) { handleRect.moveCenter(transform.map(cp.value().position)); painter.setBrush(cp.key() == m_activeHandle && shape == m_currentShape ? Qt::red : Qt::white); painter.drawRect(handleRect); } } painter.restore(); } } // paint connection points or connection handles depending // on the shape the mouse is currently if (m_currentShape && m_editMode == EditConnection) { KoConnectionShape *connectionShape = dynamic_cast(m_currentShape); if (connectionShape) { int radius = handleRadius() + 1; int handleCount = connectionShape->handleCount(); for (int i = 0; i < handleCount; ++i) { KisHandlePainterHelper helper = KoShape::createHandlePainterHelper(&painter, connectionShape, converter, radius); helper.setHandleStyle(i == m_activeHandle ? KisHandleStyle::highlightedPrimaryHandles() : KisHandleStyle::primarySelection()); connectionShape->paintHandle(helper, i); } } } } void ConnectionTool::repaintDecorations() { const qreal radius = handleRadius(); QRectF repaintRect; if (m_currentShape) { repaintRect = m_currentShape->boundingRect(); canvas()->updateCanvas(repaintRect.adjusted(-radius, -radius, radius, radius)); KoConnectionShape *connectionShape = dynamic_cast(m_currentShape); if (!m_resetPaint && m_currentShape->isVisible() && !connectionShape) { // only paint connection points of textShapes not inside a tos container and other shapes if (!(m_currentShape->shapeId() == TextShape_SHAPEID && dynamic_cast(m_currentShape->parent()))) { KoConnectionPoints connectionPoints = m_currentShape->connectionPoints(); KoConnectionPoints::const_iterator cp = connectionPoints.constBegin(); KoConnectionPoints::const_iterator lastCp = connectionPoints.constEnd(); for (; cp != lastCp; ++cp) { repaintRect = handleGrabRect(m_currentShape->shapeToDocument(cp.value().position)); canvas()->updateCanvas(repaintRect.adjusted(-radius, -radius, radius, radius)); } } } if (m_editMode == EditConnection) { if (connectionShape) { QPointF handlePos = connectionShape->handlePosition(m_activeHandle); handlePos = connectionShape->shapeToDocument(handlePos); repaintRect = handlePaintRect(handlePos); canvas()->updateCanvas(repaintRect.adjusted(-radius, -radius, radius, radius)); } } } if (m_resetPaint) { QList shapes = canvas()->shapeManager()->shapes(); for (QList::const_iterator end = shapes.constBegin(); end != shapes.constEnd(); ++end) { KoShape *shape = *end; if (!dynamic_cast(shape)) { // only paint connection points of textShapes not inside a tos container and other shapes if (shape->shapeId() == TextShape_SHAPEID && dynamic_cast(shape->parent())) { continue; } KoConnectionPoints connectionPoints = shape->connectionPoints(); KoConnectionPoints::const_iterator cp = connectionPoints.constBegin(); KoConnectionPoints::const_iterator lastCp = connectionPoints.constEnd(); for (; cp != lastCp; ++cp) { repaintRect = handleGrabRect(shape->shapeToDocument(cp.value().position)); canvas()->updateCanvas(repaintRect.adjusted(-radius, -radius, radius, radius)); } } } } m_resetPaint = false; } void ConnectionTool::mousePressEvent(KoPointerEvent *event) { if (!m_currentShape) { return; } KoShape *hitShape = findShapeAtPosition(event->point); int hitHandle = handleAtPoint(m_currentShape, event->point); if (m_editMode == EditConnection && hitHandle >= 0) { // create connection handle change strategy m_currentStrategy = new KoPathConnectionPointStrategy(this, dynamic_cast(m_currentShape), hitHandle); } else if (m_editMode == EditConnectionPoint) { if (hitHandle >= KoConnectionPoint::FirstCustomConnectionPoint) { // start moving custom connection point m_currentStrategy = new MoveConnectionPointStrategy(m_currentShape, hitHandle, this); } } else if (m_editMode == CreateConnection) { // create new connection shape, connect it to the active connection point // and start editing the new connection // create the new connection shape KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value("KoConnectionShape"); KoShape *shape = factory->createDefaultShape(canvas()->shapeController()->resourceManager()); KoConnectionShape *connectionShape = dynamic_cast(shape); if (!connectionShape) { delete shape; resetEditMode(); return; } //set connection type connectionShape->setType(m_connectionType); // get the position of the connection point we start our connection from QPointF cp = m_currentShape->shapeToDocument(m_currentShape->connectionPoint(m_activeHandle).position); // move both handles to that point connectionShape->moveHandle(0, cp); connectionShape->moveHandle(1, cp); // connect the first handle of the connection shape to our connection point if (!connectionShape->connectFirst(m_currentShape, m_activeHandle)) { delete shape; resetEditMode(); return; } //add connector label connectionShape->createTextShape(canvas()->shapeController()->resourceManager()); connectionShape->setPlainText(QString()); // create the connection edit strategy from the path tool m_currentStrategy = new KoPathConnectionPointStrategy(this, connectionShape, 1); if (!m_currentStrategy) { delete shape; resetEditMode(); return; } // update our handle data setEditMode(m_editMode, shape, 1); // add connection shape to the shape manager so it gets painted canvas()->shapeManager()->addShape(connectionShape); } else { // pressing on a shape in idle mode switches to corresponding edit mode if (hitShape) { if (dynamic_cast(hitShape)) { int hitHandle = handleAtPoint(hitShape, event->point); setEditMode(EditConnection, hitShape, hitHandle); if (hitHandle >= 0) { // start editing connection shape m_currentStrategy = new KoPathConnectionPointStrategy(this, dynamic_cast(m_currentShape), m_activeHandle); } } } else { resetEditMode(); } } } void ConnectionTool::mouseMoveEvent(KoPointerEvent *event) { if (m_currentStrategy) { repaintDecorations(); if (m_editMode != EditConnection && m_editMode != CreateConnection) { QPointF snappedPos = canvas()->snapGuide()->snap(event->point, event->modifiers()); m_currentStrategy->handleMouseMove(snappedPos, event->modifiers()); } else { m_currentStrategy->handleMouseMove(event->point, event->modifiers()); } repaintDecorations(); } else if (m_editMode == EditConnectionPoint) { KoShape *hoverShape = findNonConnectionShapeAtPosition(event->point);//TODO exclude connectors, need snap guide maybe? if (hoverShape) { m_currentShape = hoverShape; Q_ASSERT(m_currentShape); // check if we should highlight another connection point int handle = handleAtPoint(m_currentShape, event->point); if (handle >= 0) { setEditMode(m_editMode, m_currentShape, handle); useCursor(handle >= KoConnectionPoint::FirstCustomConnectionPoint ? Qt::SizeAllCursor : Qt::ArrowCursor); } else { updateStatusText(); useCursor(Qt::CrossCursor); } } else { m_currentShape = 0; useCursor(Qt::ArrowCursor); } } else if (m_editMode == EditConnection) { Q_ASSERT(m_currentShape); KoShape *hoverShape = findShapeAtPosition(event->point); // check if we should highlight another connection handle int handle = handleAtPoint(m_currentShape, event->point); setEditMode(m_editMode, m_currentShape, handle); if (m_activeHandle == KoConnectionShape::StartHandle || m_activeHandle == KoConnectionShape::EndHandle) { useCursor(Qt::SizeAllCursor); } else if (m_activeHandle >= KoConnectionShape::ControlHandle_1) { } else if (hoverShape && hoverShape != m_currentShape) { useCursor(Qt::PointingHandCursor); } else { useCursor(Qt::ArrowCursor); } } else {// Idle and no current strategy KoShape *hoverShape = findShapeAtPosition(event->point); int hoverHandle = -1; if (hoverShape) { KoConnectionShape *connectionShape = dynamic_cast(hoverShape); if (!connectionShape) { QPointF snappedPos = canvas()->snapGuide()->snap(event->point, event->modifiers()); hoverHandle = handleAtPoint(hoverShape, snappedPos); setEditMode(hoverHandle >= 0 ? CreateConnection : Idle, hoverShape, hoverHandle); } useCursor(hoverHandle >= 0 ? m_connectCursor : Qt::PointingHandCursor); } else { useCursor(Qt::ArrowCursor); } } } void ConnectionTool::mouseReleaseEvent(KoPointerEvent *event) { if (m_currentStrategy) { if (m_editMode == CreateConnection) { // check if connection handles have a minimal distance KoConnectionShape *connectionShape = dynamic_cast(m_currentShape); Q_ASSERT(connectionShape); // get both handle positions in document coordinates QPointF p1 = connectionShape->shapeToDocument(connectionShape->handlePosition(0)); QPointF p2 = connectionShape->shapeToDocument(connectionShape->handlePosition(1)); int grabDistance = grabSensitivity(); // use grabbing sensitivity as minimal distance threshold if (squareDistance(p1, p2) < grabDistance * grabDistance) { // minimal distance was not reached, so we have to undo the started work: // - cleanup and delete the strategy // - remove connection shape from shape manager and delete it // - reset edit mode to last state delete m_currentStrategy; m_currentStrategy = 0; repaintDecorations(); canvas()->shapeManager()->remove(m_currentShape); setEditMode(m_editMode, connectionShape->firstShape(), connectionShape->firstConnectionId()); repaintDecorations(); delete connectionShape; return; } else { // finalize adding the new connection shape with an undo command KUndo2Command *cmd = canvas()->shapeController()->addShape(m_currentShape, 0); canvas()->addCommand(cmd); setEditMode(EditConnection, m_currentShape, KoConnectionShape::StartHandle); } } m_currentStrategy->finishInteraction(event->modifiers()); // TODO: Add parent command to KoInteractionStrategy::createCommand // so that we can have a single command to undo for the user KUndo2Command *command = m_currentStrategy->createCommand(); if (command) { canvas()->addCommand(command); } delete m_currentStrategy; m_currentStrategy = 0; } updateStatusText(); } void ConnectionTool::mouseDoubleClickEvent(KoPointerEvent *event) { if (m_editMode == EditConnectionPoint) { repaintDecorations(); //quit EditConnectionPoint mode when double click blank region on canvas if (!m_currentShape) { resetEditMode(); return; } //add connection point when double click a shape //remove connection point when double click a existed connection point int handleId = handleAtPoint(m_currentShape, event->point); if (handleId < 0) { QPointF mousePos = canvas()->snapGuide()->snap(event->point, event->modifiers()); QPointF point = m_currentShape->documentToShape(mousePos); canvas()->addCommand(new AddConnectionPointCommand(m_currentShape, point)); } else { canvas()->addCommand(new RemoveConnectionPointCommand(m_currentShape, handleId)); } setEditMode(m_editMode, m_currentShape, -1); } else { //deactivate connection tool when double click blank region on canvas KoShape *hitShape = findShapeAtPosition(event->point); if (!hitShape) { deactivate(); emit done(); } else if (dynamic_cast(hitShape)) { repaintDecorations(); setEditMode(EditConnection, m_currentShape, -1); //TODO: temporarily activate text tool to edit connection path } } } void ConnectionTool::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { deactivate(); emit done(); } else if (event->key() == Qt::Key_Backspace) { deleteSelection(); event->accept(); } } void ConnectionTool::activate(ToolActivation activation, const QSet &shapes) { KoToolBase::activate(activation, shapes); // save old enabled snap strategies, set bounding box snap strategy m_oldSnapStrategies = canvas()->snapGuide()->enabledSnapStrategies(); canvas()->snapGuide()->enableSnapStrategies(KoSnapGuide::BoundingBoxSnapping); canvas()->snapGuide()->reset(); m_resetPaint = true; repaintDecorations(); } void ConnectionTool::deactivate() { // Put everything to 0 to be able to begin a new shape properly delete m_currentStrategy; m_currentStrategy = 0; resetEditMode(); m_resetPaint = true; repaintDecorations(); // restore previously set snap strategies canvas()->snapGuide()->enableSnapStrategies(m_oldSnapStrategies); canvas()->snapGuide()->reset(); KoToolBase::deactivate(); } qreal ConnectionTool::squareDistance(const QPointF &p1, const QPointF &p2) const { // Square of the distance const qreal dx = p2.x() - p1.x(); const qreal dy = p2.y() - p1.y(); return dx * dx + dy * dy; } KoShape *ConnectionTool::findShapeAtPosition(const QPointF &position) const { QList shapes = canvas()->shapeManager()->shapesAt(handleGrabRect(position)); if (!shapes.isEmpty()) { std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex); // we want to priorize connection shape handles, even if the connection shape // is not at the top of the shape stack at the mouse position KoConnectionShape *connectionShape = nearestConnectionShape(shapes, position); // use best connection shape or first shape from stack (last in the list) if not found if (connectionShape) { return connectionShape; } else { for (QList::const_iterator end = shapes.constEnd() - 1; end >= shapes.constBegin(); --end) { KoShape *shape = *end; if (!dynamic_cast(shape) && shape->shapeId() != TextShape_SHAPEID) { return shape; } } } } return 0; } KoShape *ConnectionTool::findNonConnectionShapeAtPosition(const QPointF &position) const { QList shapes = canvas()->shapeManager()->shapesAt(handleGrabRect(position)); if (!shapes.isEmpty()) { std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex); for (QList::const_iterator end = shapes.constEnd() - 1; end >= shapes.constBegin(); --end) { KoShape *shape = *end; if (!dynamic_cast(shape) && shape->shapeId() != TextShape_SHAPEID) { return shape; } } } return 0; } int ConnectionTool::handleAtPoint(KoShape *shape, const QPointF &mousePoint) const { if (!shape) { return -1; } const QPointF shapePoint = shape->documentToShape(mousePoint); KoConnectionShape *connectionShape = dynamic_cast(shape); if (connectionShape) { // check connection shape handles return connectionShape->handleIdAt(handleGrabRect(shapePoint)); } else { // check connection points int grabDistance = grabSensitivity(); qreal minDistance = HUGE_VAL; int handleId = -1; KoConnectionPoints connectionPoints = shape->connectionPoints(); KoConnectionPoints::const_iterator cp = connectionPoints.constBegin(); KoConnectionPoints::const_iterator lastCp = connectionPoints.constEnd(); for (; cp != lastCp; ++cp) { qreal d = squareDistance(shapePoint, cp.value().position); if (d <= grabDistance && d < minDistance) { handleId = cp.key(); minDistance = d; } } return handleId; } } KoConnectionShape *ConnectionTool::nearestConnectionShape(const QList &shapes, const QPointF &mousePos) const { int grabDistance = grabSensitivity(); KoConnectionShape *nearestConnectionShape = 0; qreal minSquaredDistance = HUGE_VAL; const qreal maxSquaredDistance = grabDistance * grabDistance; Q_FOREACH (KoShape *shape, shapes) { KoConnectionShape *connectionShape = dynamic_cast(shape); if (!connectionShape || !connectionShape->isParametricShape()) { continue; } // convert document point to shape coordinates QPointF p = connectionShape->documentToShape(mousePos); // our region of interest, i.e. a region around our mouse position QRectF roi = handleGrabRect(p); // check all segments of this shape which intersect the region of interest QList segments = connectionShape->segmentsAt(roi); foreach (const KoPathSegment &s, segments) { qreal nearestPointParam = s.nearestPoint(p); QPointF nearestPoint = s.pointAt(nearestPointParam); QPointF diff = p - nearestPoint; qreal squaredDistance = diff.x() * diff.x() + diff.y() * diff.y(); // are we within the allowed distance ? if (squaredDistance > maxSquaredDistance) { continue; } // are we closer to the last closest point ? if (squaredDistance < minSquaredDistance) { nearestConnectionShape = connectionShape; minSquaredDistance = squaredDistance; } } } return nearestConnectionShape; } void ConnectionTool::setEditMode(EditMode mode, KoShape *currentShape, int handle) { repaintDecorations(); m_editMode = mode; if (m_currentShape != currentShape) { KoConnectionShape *connectionShape = dynamic_cast(currentShape); foreach (KoShapeConfigWidgetBase *cw, m_connectionShapeWidgets) { if (connectionShape) { cw->open(currentShape); } } } if (mode == Idle) { emit sendConnectionType(m_connectionType); } m_currentShape = currentShape; m_activeHandle = handle; repaintDecorations(); updateActions(); updateStatusText(); } void ConnectionTool::resetEditMode() { m_connectionType = KoConnectionShape::Standard; setEditMode(Idle, 0, -1); emit sendConnectionPointEditState(false); } void ConnectionTool::updateActions() { const bool connectionPointSelected = m_editMode == EditConnectionPoint && m_activeHandle >= 0; if (connectionPointSelected) { KoConnectionPoint cp = m_currentShape->connectionPoint(m_activeHandle); m_alignPercent->setChecked(false); Q_FOREACH (QAction *action, m_alignHorizontal->actions()) { action->setChecked(false); } Q_FOREACH (QAction *action, m_alignVertical->actions()) { action->setChecked(false); } switch (cp.alignment) { case KoConnectionPoint::AlignNone: m_alignPercent->setChecked(true); break; case KoConnectionPoint::AlignTopLeft: m_alignLeft->setChecked(true); m_alignTop->setChecked(true); break; case KoConnectionPoint::AlignTop: m_alignCenterH->setChecked(true); m_alignTop->setChecked(true); break; case KoConnectionPoint::AlignTopRight: m_alignRight->setChecked(true); m_alignTop->setChecked(true); break; case KoConnectionPoint::AlignLeft: m_alignLeft->setChecked(true); m_alignCenterV->setChecked(true); break; case KoConnectionPoint::AlignCenter: m_alignCenterH->setChecked(true); m_alignCenterV->setChecked(true); break; case KoConnectionPoint::AlignRight: m_alignRight->setChecked(true); m_alignCenterV->setChecked(true); break; case KoConnectionPoint::AlignBottomLeft: m_alignLeft->setChecked(true); m_alignBottom->setChecked(true); break; case KoConnectionPoint::AlignBottom: m_alignCenterH->setChecked(true); m_alignBottom->setChecked(true); break; case KoConnectionPoint::AlignBottomRight: m_alignRight->setChecked(true); m_alignBottom->setChecked(true); break; } Q_FOREACH (QAction *action, m_escapeDirections->actions()) { action->setChecked(false); } switch (cp.escapeDirection) { case KoConnectionPoint::AllDirections: m_escapeAll->setChecked(true); break; case KoConnectionPoint::HorizontalDirections: m_escapeHorizontal->setChecked(true); break; case KoConnectionPoint::VerticalDirections: m_escapeVertical->setChecked(true); break; case KoConnectionPoint::LeftDirection: m_escapeLeft->setChecked(true); break; case KoConnectionPoint::RightDirection: m_escapeRight->setChecked(true); break; case KoConnectionPoint::UpDirection: m_escapeUp->setChecked(true); break; case KoConnectionPoint::DownDirection: m_escapeDown->setChecked(true); break; } } emit connectionPointEnabled(connectionPointSelected); } void ConnectionTool::updateStatusText() { switch (m_editMode) { case Idle: if (m_currentShape) { if (dynamic_cast(m_currentShape)) { if (m_activeHandle >= 0) { emit statusTextChanged(i18n("Drag to edit connection.")); } else { emit statusTextChanged(i18n("Double click connection or press delete to remove it.")); } } else if (m_activeHandle < 0) { emit statusTextChanged(i18n("Click to edit connection points.")); } } else { emit statusTextChanged(QString()); } break; case EditConnection: if (m_activeHandle >= 0) { emit statusTextChanged(i18n("Drag to edit connection.")); } else { emit statusTextChanged(i18n("Double click connection or press delete to remove it.")); } break; case EditConnectionPoint: if (m_activeHandle >= KoConnectionPoint::FirstCustomConnectionPoint) { emit statusTextChanged(i18n("Drag to move connection point. Double click connection or press delete to remove it.")); } else if (m_activeHandle >= 0) { emit statusTextChanged(i18n("Double click connection point or press delete to remove it.")); } else { emit statusTextChanged(i18n("Double click to add connection point.")); } break; case CreateConnection: emit statusTextChanged(i18n("Drag to create new connection.")); break; default: emit statusTextChanged(QString()); } } QList > ConnectionTool::createOptionWidgets() { QList > list; m_connectionShapeWidgets.clear(); KoShapeFactoryBase *factory = KoShapeRegistry::instance()->get(KOCONNECTIONSHAPEID); if (factory) { QList widgets = factory->createShapeOptionPanels(); Q_FOREACH (KoShapeConfigWidgetBase *cw, widgets) { if (cw->showOnShapeCreate() || !cw->showOnShapeSelect()) { delete cw; continue; } connect(cw, SIGNAL(propertyChanged()), this, SLOT(connectionChanged())); KoConnectionShapeConfigWidget *cw2 = (KoConnectionShapeConfigWidget *)cw; if (cw2) { connect(cw2, SIGNAL(connectionTypeChanged(int)), this, SLOT(getConnectionType(int))); connect(this, SIGNAL(sendConnectionType(int)), cw2, SLOT(setConnectionType(int))); } m_connectionShapeWidgets.append(cw); cw->setWindowTitle(i18n("Connection")); list.append(cw); } } KoStrokeConfigWidget *strokeWidget = new KoStrokeConfigWidget(canvas(), 0); KisDocumentAwareSpinBoxUnitManager* managerLineWidth = new KisDocumentAwareSpinBoxUnitManager(strokeWidget); KisDocumentAwareSpinBoxUnitManager* managerMitterLimit = new KisDocumentAwareSpinBoxUnitManager(strokeWidget); managerLineWidth->setApparentUnitFromSymbol("px"); managerMitterLimit->setApparentUnitFromSymbol("px"); strokeWidget->setUnitManagers(managerLineWidth, managerMitterLimit); strokeWidget->setWindowTitle(i18n("Line")); list.append(strokeWidget); ConnectionPointWidget *connectPoint = new ConnectionPointWidget(this); connectPoint->setWindowTitle(i18n("Connection Point")); list.append(connectPoint); return list; } void ConnectionTool::horizontalAlignChanged() { if (m_alignPercent->isChecked()) { m_alignPercent->setChecked(false); m_alignTop->setChecked(true); } updateConnectionPoint(); } void ConnectionTool::verticalAlignChanged() { if (m_alignPercent->isChecked()) { m_alignPercent->setChecked(false); m_alignLeft->setChecked(true); } updateConnectionPoint(); } void ConnectionTool::relativeAlignChanged() { Q_FOREACH (QAction *action, m_alignHorizontal->actions()) { action->setChecked(false); } Q_FOREACH (QAction *action, m_alignVertical->actions()) { action->setChecked(false); } m_alignPercent->setChecked(true); updateConnectionPoint(); } void ConnectionTool::updateConnectionPoint() { if (m_editMode == EditConnectionPoint && m_currentShape && m_activeHandle >= 0) { KoConnectionPoint oldPoint = m_currentShape->connectionPoint(m_activeHandle); KoConnectionPoint newPoint = oldPoint; if (m_alignPercent->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignNone; } else if (m_alignLeft->isChecked() && m_alignTop->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignTopLeft; } else if (m_alignCenterH->isChecked() && m_alignTop->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignTop; } else if (m_alignRight->isChecked() && m_alignTop->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignTopRight; } else if (m_alignLeft->isChecked() && m_alignCenterV->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignLeft; } else if (m_alignCenterH->isChecked() && m_alignCenterV->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignCenter; } else if (m_alignRight->isChecked() && m_alignCenterV->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignRight; } else if (m_alignLeft->isChecked() && m_alignBottom->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignBottomLeft; } else if (m_alignCenterH->isChecked() && m_alignBottom->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignBottom; } else if (m_alignRight->isChecked() && m_alignBottom->isChecked()) { newPoint.alignment = KoConnectionPoint::AlignBottomRight; } canvas()->addCommand(new ChangeConnectionPointCommand(m_currentShape, m_activeHandle, oldPoint, newPoint)); } } void ConnectionTool::escapeDirectionChanged() { if (m_editMode == EditConnectionPoint && m_currentShape && m_activeHandle >= 0) { KoConnectionPoint oldPoint = m_currentShape->connectionPoint(m_activeHandle); KoConnectionPoint newPoint = oldPoint; QAction *checkedAction = m_escapeDirections->checkedAction(); if (checkedAction == m_escapeAll) { newPoint.escapeDirection = KoConnectionPoint::AllDirections; } else if (checkedAction == m_escapeHorizontal) { newPoint.escapeDirection = KoConnectionPoint::HorizontalDirections; } else if (checkedAction == m_escapeVertical) { newPoint.escapeDirection = KoConnectionPoint::VerticalDirections; } else if (checkedAction == m_escapeLeft) { newPoint.escapeDirection = KoConnectionPoint::LeftDirection; } else if (checkedAction == m_escapeRight) { newPoint.escapeDirection = KoConnectionPoint::RightDirection; } else if (checkedAction == m_escapeUp) { newPoint.escapeDirection = KoConnectionPoint::UpDirection; } else if (checkedAction == m_escapeDown) { newPoint.escapeDirection = KoConnectionPoint::DownDirection; } canvas()->addCommand(new ChangeConnectionPointCommand(m_currentShape, m_activeHandle, oldPoint, newPoint)); } } void ConnectionTool::connectionChanged() { if (m_editMode != EditConnection) { return; } KoConnectionShape *connectionShape = dynamic_cast(m_currentShape); if (!connectionShape) { return; } Q_FOREACH (KoShapeConfigWidgetBase *cw, m_connectionShapeWidgets) { canvas()->addCommand(cw->createCommand()); } } void ConnectionTool::deleteSelection() { if (m_editMode == EditConnectionPoint && m_currentShape && m_activeHandle >= 0) { repaintDecorations(); canvas()->addCommand(new RemoveConnectionPointCommand(m_currentShape, m_activeHandle)); setEditMode(m_editMode, m_currentShape, -1); } else if (m_editMode == EditConnection && m_currentShape) { repaintDecorations(); canvas()->addCommand(canvas()->shapeController()->removeShape(m_currentShape)); resetEditMode(); } } void ConnectionTool::getConnectionType(int type) { if (m_editMode == Idle) { m_connectionType = (KoConnectionShape::Type)type; } } void ConnectionTool::toggleConnectionPointEditMode(int state) { if (state == Qt::Checked) { setEditMode(EditConnectionPoint, 0, -1); } else if (state == Qt::Unchecked) { setEditMode(Idle, 0, -1); } else { return; } } diff --git a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp index abe62b910d..a1e7df8e4e 100644 --- a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp +++ b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp @@ -1,1714 +1,1727 @@ /* This file is part of the KDE project Copyright (C) 2006-2008 Thorsten Zachmann Copyright (C) 2006-2010 Thomas Zander Copyright (C) 2008-2009 Jan Hambrecht Copyright (C) 2008 C. Boemann 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 "DefaultTool.h" #include "DefaultToolGeometryWidget.h" #include "DefaultToolTabbedWidget.h" #include "SelectionDecorator.h" #include "ShapeMoveStrategy.h" #include "ShapeRotateStrategy.h" #include "ShapeShearStrategy.h" #include "ShapeResizeStrategy.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 "kis_action_registry.h" #include "kis_node.h" #include "kis_node_manager.h" #include "KisViewManager.h" #include "kis_canvas2.h" #include "kis_canvas_resource_provider.h" #include #include "kis_document_aware_spin_box_unit_manager.h" #include #include #include #include #include #include #include #include #include #include #include "kis_assert.h" #include "kis_global.h" #include "kis_debug.h" #include #define HANDLE_DISTANCE 10 #define HANDLE_DISTANCE_SQ (HANDLE_DISTANCE * HANDLE_DISTANCE) #define INNER_HANDLE_DISTANCE_SQ 16 namespace { static const QString EditFillGradientFactoryId = "edit_fill_gradient"; static const QString EditStrokeGradientFactoryId = "edit_stroke_gradient"; enum TransformActionType { TransformRotate90CW, TransformRotate90CCW, TransformRotate180, TransformMirrorX, TransformMirrorY, TransformReset }; enum BooleanOp { BooleanUnion, BooleanIntersection, BooleanSubtraction }; } class NopInteractionStrategy : public KoInteractionStrategy { public: explicit NopInteractionStrategy(KoToolBase *parent) : KoInteractionStrategy(parent) { } KUndo2Command *createCommand() override { return 0; } void handleMouseMove(const QPointF & /*mouseLocation*/, Qt::KeyboardModifiers /*modifiers*/) override {} void finishInteraction(Qt::KeyboardModifiers /*modifiers*/) override {} void paint(QPainter &painter, const KoViewConverter &converter) override { Q_UNUSED(painter); Q_UNUSED(converter); } }; class SelectionInteractionStrategy : public KoShapeRubberSelectStrategy { public: explicit SelectionInteractionStrategy(KoToolBase *parent, const QPointF &clicked, bool useSnapToGrid) : KoShapeRubberSelectStrategy(parent, clicked, useSnapToGrid) { } void paint(QPainter &painter, const KoViewConverter &converter) override { KoShapeRubberSelectStrategy::paint(painter, converter); } void finishInteraction(Qt::KeyboardModifiers modifiers = 0) override { Q_UNUSED(modifiers); DefaultTool *defaultTool = dynamic_cast(tool()); KIS_SAFE_ASSERT_RECOVER_RETURN(defaultTool); KoSelection * selection = defaultTool->koSelection(); const bool useContainedMode = currentMode() == CoveringSelection; QList shapes = defaultTool->shapeManager()-> shapesAt(selectedRectangle(), true, useContainedMode); Q_FOREACH (KoShape * shape, shapes) { if (!shape->isSelectable()) continue; selection->select(shape); } defaultTool->repaintDecorations(); defaultTool->canvas()->updateCanvas(selectedRectangle()); } }; #include #include "KoShapeGradientHandles.h" #include "ShapeGradientEditStrategy.h" class DefaultTool::MoveGradientHandleInteractionFactory : public KoInteractionStrategyFactory { public: MoveGradientHandleInteractionFactory(KoFlake::FillVariant fillVariant, int priority, const QString &id, DefaultTool *_q) : KoInteractionStrategyFactory(priority, id), q(_q), m_fillVariant(fillVariant) { } KoInteractionStrategy* createStrategy(KoPointerEvent *ev) override { m_currentHandle = handleAt(ev->point); if (m_currentHandle.type != KoShapeGradientHandles::Handle::None) { KoShape *shape = onlyEditableShape(); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape, 0); return new ShapeGradientEditStrategy(q, m_fillVariant, shape, m_currentHandle.type, ev->point); } return 0; } bool hoverEvent(KoPointerEvent *ev) override { m_currentHandle = handleAt(ev->point); return false; } bool paintOnHover(QPainter &painter, const KoViewConverter &converter) override { Q_UNUSED(painter); Q_UNUSED(converter); return false; } bool tryUseCustomCursor() override { if (m_currentHandle.type != KoShapeGradientHandles::Handle::None) { q->useCursor(Qt::OpenHandCursor); } return m_currentHandle.type != KoShapeGradientHandles::Handle::None; } private: KoShape* onlyEditableShape() const { KoSelection *selection = q->koSelection(); QList shapes = selection->selectedEditableShapes(); KoShape *shape = 0; if (shapes.size() == 1) { shape = shapes.first(); } return shape; } KoShapeGradientHandles::Handle handleAt(const QPointF &pos) { KoShapeGradientHandles::Handle result; KoShape *shape = onlyEditableShape(); if (shape) { KoFlake::SelectionHandle globalHandle = q->handleAt(pos); const qreal distanceThresholdSq = globalHandle == KoFlake::NoHandle ? HANDLE_DISTANCE_SQ : 0.25 * HANDLE_DISTANCE_SQ; const KoViewConverter *converter = q->canvas()->viewConverter(); const QPointF viewPoint = converter->documentToView(pos); qreal minDistanceSq = std::numeric_limits::max(); KoShapeGradientHandles sh(m_fillVariant, shape); Q_FOREACH (const KoShapeGradientHandles::Handle &handle, sh.handles()) { const QPointF handlePoint = converter->documentToView(handle.pos); const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint); if (distanceSq < distanceThresholdSq && distanceSq < minDistanceSq) { result = handle; minDistanceSq = distanceSq; } } } return result; } private: DefaultTool *q; KoFlake::FillVariant m_fillVariant; KoShapeGradientHandles::Handle m_currentHandle; }; class SelectionHandler : public KoToolSelection { public: SelectionHandler(DefaultTool *parent) : KoToolSelection(parent) , m_selection(parent->koSelection()) { } bool hasSelection() override { if (m_selection) { return m_selection->count(); } return false; } private: QPointer m_selection; }; DefaultTool::DefaultTool(KoCanvasBase *canvas) : KoInteractionTool(canvas) , m_lastHandle(KoFlake::NoHandle) , m_hotPosition(KoFlake::TopLeft) , m_mouseWasInsideHandles(false) , m_decorator(0) , m_selectionHandler(new SelectionHandler(this)) , m_tabbedOptionWidget(0) { setupActions(); QPixmap rotatePixmap, shearPixmap; rotatePixmap.load(":/cursor_rotate.png"); Q_ASSERT(!rotatePixmap.isNull()); shearPixmap.load(":/cursor_shear.png"); Q_ASSERT(!shearPixmap.isNull()); m_rotateCursors[0] = QCursor(rotatePixmap.transformed(QTransform().rotate(45))); m_rotateCursors[1] = QCursor(rotatePixmap.transformed(QTransform().rotate(90))); m_rotateCursors[2] = QCursor(rotatePixmap.transformed(QTransform().rotate(135))); m_rotateCursors[3] = QCursor(rotatePixmap.transformed(QTransform().rotate(180))); m_rotateCursors[4] = QCursor(rotatePixmap.transformed(QTransform().rotate(225))); m_rotateCursors[5] = QCursor(rotatePixmap.transformed(QTransform().rotate(270))); m_rotateCursors[6] = QCursor(rotatePixmap.transformed(QTransform().rotate(315))); m_rotateCursors[7] = QCursor(rotatePixmap); /* m_rotateCursors[0] = QCursor(Qt::RotateNCursor); m_rotateCursors[1] = QCursor(Qt::RotateNECursor); m_rotateCursors[2] = QCursor(Qt::RotateECursor); m_rotateCursors[3] = QCursor(Qt::RotateSECursor); m_rotateCursors[4] = QCursor(Qt::RotateSCursor); m_rotateCursors[5] = QCursor(Qt::RotateSWCursor); m_rotateCursors[6] = QCursor(Qt::RotateWCursor); m_rotateCursors[7] = QCursor(Qt::RotateNWCursor); */ m_shearCursors[0] = QCursor(shearPixmap); m_shearCursors[1] = QCursor(shearPixmap.transformed(QTransform().rotate(45))); m_shearCursors[2] = QCursor(shearPixmap.transformed(QTransform().rotate(90))); m_shearCursors[3] = QCursor(shearPixmap.transformed(QTransform().rotate(135))); m_shearCursors[4] = QCursor(shearPixmap.transformed(QTransform().rotate(180))); m_shearCursors[5] = QCursor(shearPixmap.transformed(QTransform().rotate(225))); m_shearCursors[6] = QCursor(shearPixmap.transformed(QTransform().rotate(270))); m_shearCursors[7] = QCursor(shearPixmap.transformed(QTransform().rotate(315))); m_sizeCursors[0] = Qt::SizeVerCursor; m_sizeCursors[1] = Qt::SizeBDiagCursor; m_sizeCursors[2] = Qt::SizeHorCursor; m_sizeCursors[3] = Qt::SizeFDiagCursor; m_sizeCursors[4] = Qt::SizeVerCursor; m_sizeCursors[5] = Qt::SizeBDiagCursor; m_sizeCursors[6] = Qt::SizeHorCursor; m_sizeCursors[7] = Qt::SizeFDiagCursor; connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(updateActions())); } DefaultTool::~DefaultTool() { } void DefaultTool::slotActivateEditFillGradient(bool value) { if (value) { addInteractionFactory( new MoveGradientHandleInteractionFactory(KoFlake::Fill, 1, EditFillGradientFactoryId, this)); } else { removeInteractionFactory(EditFillGradientFactoryId); } repaintDecorations(); } void DefaultTool::slotActivateEditStrokeGradient(bool value) { if (value) { addInteractionFactory( new MoveGradientHandleInteractionFactory(KoFlake::StrokeFill, 0, EditStrokeGradientFactoryId, this)); } else { removeInteractionFactory(EditStrokeGradientFactoryId); } repaintDecorations(); } bool DefaultTool::wantsAutoScroll() const { return true; } void DefaultTool::addMappedAction(QSignalMapper *mapper, const QString &actionId, int commandType) { - KisActionRegistry *actionRegistry = KisActionRegistry::instance(); - - QAction *action = actionRegistry->makeQAction(actionId, this); - addAction(actionId, action); - connect(action, SIGNAL(triggered()), mapper, SLOT(map())); - mapper->setMapping(action, commandType); + QAction *a =action(actionId); + connect(a, SIGNAL(triggered()), mapper, SLOT(map())); + mapper->setMapping(a, commandType); } void DefaultTool::setupActions() { - KisActionRegistry *actionRegistry = KisActionRegistry::instance(); - - QAction *actionBringToFront = actionRegistry->makeQAction("object_order_front", this); - addAction("object_order_front", actionBringToFront); - connect(actionBringToFront, SIGNAL(triggered()), this, SLOT(selectionBringToFront())); - - QAction *actionRaise = actionRegistry->makeQAction("object_order_raise", this); - addAction("object_order_raise", actionRaise); - connect(actionRaise, SIGNAL(triggered()), this, SLOT(selectionMoveUp())); - - QAction *actionLower = actionRegistry->makeQAction("object_order_lower", this); - addAction("object_order_lower", actionLower); - connect(actionLower, SIGNAL(triggered()), this, SLOT(selectionMoveDown())); - - QAction *actionSendToBack = actionRegistry->makeQAction("object_order_back", this); - addAction("object_order_back", actionSendToBack); - connect(actionSendToBack, SIGNAL(triggered()), this, SLOT(selectionSendToBack())); - - - QSignalMapper *alignSignalsMapper = new QSignalMapper(this); - connect(alignSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionAlign(int))); + m_alignSignalsMapper = new QSignalMapper(this); - addMappedAction(alignSignalsMapper, "object_align_horizontal_left", KoShapeAlignCommand::HorizontalLeftAlignment); - addMappedAction(alignSignalsMapper, "object_align_horizontal_center", KoShapeAlignCommand::HorizontalCenterAlignment); - addMappedAction(alignSignalsMapper, "object_align_horizontal_right", KoShapeAlignCommand::HorizontalRightAlignment); - addMappedAction(alignSignalsMapper, "object_align_vertical_top", KoShapeAlignCommand::VerticalTopAlignment); - addMappedAction(alignSignalsMapper, "object_align_vertical_center", KoShapeAlignCommand::VerticalCenterAlignment); - addMappedAction(alignSignalsMapper, "object_align_vertical_bottom", KoShapeAlignCommand::VerticalBottomAlignment); + addMappedAction(m_alignSignalsMapper, "object_align_horizontal_left", KoShapeAlignCommand::HorizontalLeftAlignment); + addMappedAction(m_alignSignalsMapper, "object_align_horizontal_center", KoShapeAlignCommand::HorizontalCenterAlignment); + addMappedAction(m_alignSignalsMapper, "object_align_horizontal_right", KoShapeAlignCommand::HorizontalRightAlignment); + addMappedAction(m_alignSignalsMapper, "object_align_vertical_top", KoShapeAlignCommand::VerticalTopAlignment); + addMappedAction(m_alignSignalsMapper, "object_align_vertical_center", KoShapeAlignCommand::VerticalCenterAlignment); + addMappedAction(m_alignSignalsMapper, "object_align_vertical_bottom", KoShapeAlignCommand::VerticalBottomAlignment); - QSignalMapper *distributeSignalsMapper = new QSignalMapper(this); - connect(distributeSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionDistribute(int))); + m_distributeSignalsMapper = new QSignalMapper(this); - addMappedAction(distributeSignalsMapper, "object_distribute_horizontal_left", KoShapeDistributeCommand::HorizontalLeftDistribution); - addMappedAction(distributeSignalsMapper, "object_distribute_horizontal_center", KoShapeDistributeCommand::HorizontalCenterDistribution); - addMappedAction(distributeSignalsMapper, "object_distribute_horizontal_right", KoShapeDistributeCommand::HorizontalRightDistribution); - addMappedAction(distributeSignalsMapper, "object_distribute_horizontal_gaps", KoShapeDistributeCommand::HorizontalGapsDistribution); + addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_left", KoShapeDistributeCommand::HorizontalLeftDistribution); + addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_center", KoShapeDistributeCommand::HorizontalCenterDistribution); + addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_right", KoShapeDistributeCommand::HorizontalRightDistribution); + addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_gaps", KoShapeDistributeCommand::HorizontalGapsDistribution); - addMappedAction(distributeSignalsMapper, "object_distribute_vertical_top", KoShapeDistributeCommand::VerticalTopDistribution); - addMappedAction(distributeSignalsMapper, "object_distribute_vertical_center", KoShapeDistributeCommand::VerticalCenterDistribution); - addMappedAction(distributeSignalsMapper, "object_distribute_vertical_bottom", KoShapeDistributeCommand::VerticalBottomDistribution); - addMappedAction(distributeSignalsMapper, "object_distribute_vertical_gaps", KoShapeDistributeCommand::VerticalGapsDistribution); + addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_top", KoShapeDistributeCommand::VerticalTopDistribution); + addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_center", KoShapeDistributeCommand::VerticalCenterDistribution); + addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_bottom", KoShapeDistributeCommand::VerticalBottomDistribution); + addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_gaps", KoShapeDistributeCommand::VerticalGapsDistribution); - QAction *actionGroupBottom = actionRegistry->makeQAction("object_group", this); - addAction("object_group", actionGroupBottom); - connect(actionGroupBottom, SIGNAL(triggered()), this, SLOT(selectionGroup())); + m_transformSignalsMapper = new QSignalMapper(this); - QAction *actionUngroupBottom = actionRegistry->makeQAction("object_ungroup", this); - addAction("object_ungroup", actionUngroupBottom); - connect(actionUngroupBottom, SIGNAL(triggered()), this, SLOT(selectionUngroup())); + addMappedAction(m_transformSignalsMapper, "object_transform_rotate_90_cw", TransformRotate90CW); + addMappedAction(m_transformSignalsMapper, "object_transform_rotate_90_ccw", TransformRotate90CCW); + addMappedAction(m_transformSignalsMapper, "object_transform_rotate_180", TransformRotate180); + addMappedAction(m_transformSignalsMapper, "object_transform_mirror_horizontally", TransformMirrorX); + addMappedAction(m_transformSignalsMapper, "object_transform_mirror_vertically", TransformMirrorY); + addMappedAction(m_transformSignalsMapper, "object_transform_reset", TransformReset); - QSignalMapper *transformSignalsMapper = new QSignalMapper(this); - connect(transformSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionTransform(int))); + m_booleanSignalsMapper = new QSignalMapper(this); - addMappedAction(transformSignalsMapper, "object_transform_rotate_90_cw", TransformRotate90CW); - addMappedAction(transformSignalsMapper, "object_transform_rotate_90_ccw", TransformRotate90CCW); - addMappedAction(transformSignalsMapper, "object_transform_rotate_180", TransformRotate180); - addMappedAction(transformSignalsMapper, "object_transform_mirror_horizontally", TransformMirrorX); - addMappedAction(transformSignalsMapper, "object_transform_mirror_vertically", TransformMirrorY); - addMappedAction(transformSignalsMapper, "object_transform_reset", TransformReset); - - QSignalMapper *booleanSignalsMapper = new QSignalMapper(this); - connect(booleanSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionBooleanOp(int))); - - addMappedAction(booleanSignalsMapper, "object_unite", BooleanUnion); - addMappedAction(booleanSignalsMapper, "object_intersect", BooleanIntersection); - addMappedAction(booleanSignalsMapper, "object_subtract", BooleanSubtraction); - - QAction *actionSplit = actionRegistry->makeQAction("object_split", this); - addAction("object_split", actionSplit); - connect(actionSplit, SIGNAL(triggered()), this, SLOT(selectionSplitShapes())); + addMappedAction(m_booleanSignalsMapper, "object_unite", BooleanUnion); + addMappedAction(m_booleanSignalsMapper, "object_intersect", BooleanIntersection); + addMappedAction(m_booleanSignalsMapper, "object_subtract", BooleanSubtraction); m_contextMenu.reset(new QMenu()); } qreal DefaultTool::rotationOfHandle(KoFlake::SelectionHandle handle, bool useEdgeRotation) { QPointF selectionCenter = koSelection()->absolutePosition(); QPointF direction; switch (handle) { case KoFlake::TopMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::TopLeft); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeft); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::TopRight) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::TopRightHandle: direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::TopLeft)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::BottomRight)).normalized()).toPointF(); break; case KoFlake::RightMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::TopRight); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopRight); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRight) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::BottomRightHandle: direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::BottomLeft)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::TopRight)).normalized()).toPointF(); break; case KoFlake::BottomMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::BottomRight); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::BottomLeft); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRight) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::BottomLeftHandle: direction = koSelection()->absolutePosition(KoFlake::BottomLeft) - selectionCenter; direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::BottomRight)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::TopLeft)).normalized()).toPointF(); break; case KoFlake::LeftMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::BottomLeft); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeft); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomLeft) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::TopLeftHandle: direction = koSelection()->absolutePosition(KoFlake::TopLeft) - selectionCenter; direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::TopRight)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::BottomLeft)).normalized()).toPointF(); break; case KoFlake::NoHandle: return 0.0; break; } qreal rotation = atan2(direction.y(), direction.x()) * 180.0 / M_PI; switch (handle) { case KoFlake::TopMiddleHandle: if (useEdgeRotation) { rotation -= 0.0; } else { rotation -= 270.0; } break; case KoFlake::TopRightHandle: rotation -= 315.0; break; case KoFlake::RightMiddleHandle: if (useEdgeRotation) { rotation -= 90.0; } else { rotation -= 0.0; } break; case KoFlake::BottomRightHandle: rotation -= 45.0; break; case KoFlake::BottomMiddleHandle: if (useEdgeRotation) { rotation -= 180.0; } else { rotation -= 90.0; } break; case KoFlake::BottomLeftHandle: rotation -= 135.0; break; case KoFlake::LeftMiddleHandle: if (useEdgeRotation) { rotation -= 270.0; } else { rotation -= 180.0; } break; case KoFlake::TopLeftHandle: rotation -= 225.0; break; case KoFlake::NoHandle: break; } if (rotation < 0.0) { rotation += 360.0; } return rotation; } void DefaultTool::updateCursor() { if (tryUseCustomCursor()) return; QCursor cursor = Qt::ArrowCursor; QString statusText; KoSelection *selection = koSelection(); if (selection && selection->count() > 0) { // has a selection bool editable = !selection->selectedEditableShapes().isEmpty(); if (!m_mouseWasInsideHandles) { m_angle = rotationOfHandle(m_lastHandle, true); int rotOctant = 8 + int(8.5 + m_angle / 45); bool rotateHandle = false; bool shearHandle = false; switch (m_lastHandle) { case KoFlake::TopMiddleHandle: cursor = m_shearCursors[(0 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::TopRightHandle: cursor = m_rotateCursors[(1 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::RightMiddleHandle: cursor = m_shearCursors[(2 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::BottomRightHandle: cursor = m_rotateCursors[(3 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::BottomMiddleHandle: cursor = m_shearCursors[(4 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::BottomLeftHandle: cursor = m_rotateCursors[(5 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::LeftMiddleHandle: cursor = m_shearCursors[(6 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::TopLeftHandle: cursor = m_rotateCursors[(7 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::NoHandle: cursor = Qt::ArrowCursor; break; } if (rotateHandle) { statusText = i18n("Left click rotates around center, right click around highlighted position."); } if (shearHandle) { statusText = i18n("Click and drag to shear selection."); } } else { statusText = i18n("Click and drag to resize selection."); m_angle = rotationOfHandle(m_lastHandle, false); int rotOctant = 8 + int(8.5 + m_angle / 45); bool cornerHandle = false; switch (m_lastHandle) { case KoFlake::TopMiddleHandle: cursor = m_sizeCursors[(0 + rotOctant) % 8]; break; case KoFlake::TopRightHandle: cursor = m_sizeCursors[(1 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::RightMiddleHandle: cursor = m_sizeCursors[(2 + rotOctant) % 8]; break; case KoFlake::BottomRightHandle: cursor = m_sizeCursors[(3 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::BottomMiddleHandle: cursor = m_sizeCursors[(4 + rotOctant) % 8]; break; case KoFlake::BottomLeftHandle: cursor = m_sizeCursors[(5 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::LeftMiddleHandle: cursor = m_sizeCursors[(6 + rotOctant) % 8]; break; case KoFlake::TopLeftHandle: cursor = m_sizeCursors[(7 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::NoHandle: cursor = Qt::SizeAllCursor; statusText = i18n("Click and drag to move selection."); break; } if (cornerHandle) { statusText = i18n("Click and drag to resize selection. Middle click to set highlighted position."); } } if (!editable) { cursor = Qt::ArrowCursor; } } else { // there used to be guides... :'''( } useCursor(cursor); if (currentStrategy() == 0) { emit statusTextChanged(statusText); } } void DefaultTool::paint(QPainter &painter, const KoViewConverter &converter) { KoSelection *selection = koSelection(); if (selection) { this->m_decorator = new SelectionDecorator(canvas()->resourceManager()); { /** * Selection masks don't render the outline of the shapes, so we should * do that explicitly when rendering them via selection */ KisCanvas2 *kisCanvas = static_cast(canvas()); KisNodeSP node = kisCanvas->viewManager()->nodeManager()->activeNode(); const bool isSelectionMask = node && node->inherits("KisSelectionMask"); m_decorator->setForceShapeOutlines(isSelectionMask); } m_decorator->setSelection(selection); m_decorator->setHandleRadius(handleRadius()); m_decorator->setShowFillGradientHandles(hasInteractioFactory(EditFillGradientFactoryId)); m_decorator->setShowStrokeFillGradientHandles(hasInteractioFactory(EditStrokeGradientFactoryId)); m_decorator->paint(painter, converter); } KoInteractionTool::paint(painter, converter); painter.save(); KoShape::applyConversion(painter, converter); canvas()->snapGuide()->paint(painter, converter); painter.restore(); } bool DefaultTool::isValidForCurrentLayer() const { // if the currently active node has a shape manager, then it is // probably our client :) KisCanvas2 *kisCanvas = static_cast(canvas()); return bool(kisCanvas->localShapeManager()); } KoShapeManager *DefaultTool::shapeManager() const { return canvas()->shapeManager(); } void DefaultTool::mousePressEvent(KoPointerEvent *event) { // this tool only works on a vector layer right now, so give a warning if another layer type is trying to use it if (!isValidForCurrentLayer()) { KisCanvas2 *kiscanvas = static_cast(canvas()); kiscanvas->viewManager()->showFloatingMessage( i18n("This tool only works on vector layers. You probably want the move tool."), QIcon(), 2000, KisFloatingMessage::Medium, Qt::AlignCenter); return; } KoInteractionTool::mousePressEvent(event); updateCursor(); } void DefaultTool::mouseMoveEvent(KoPointerEvent *event) { KoInteractionTool::mouseMoveEvent(event); if (currentStrategy() == 0 && koSelection() && koSelection()->count() > 0) { QRectF bound = handlesSize(); if (bound.contains(event->point)) { bool inside; KoFlake::SelectionHandle newDirection = handleAt(event->point, &inside); if (inside != m_mouseWasInsideHandles || m_lastHandle != newDirection) { m_lastHandle = newDirection; m_mouseWasInsideHandles = inside; //repaintDecorations(); } } else { /*if (m_lastHandle != KoFlake::NoHandle) repaintDecorations(); */ m_lastHandle = KoFlake::NoHandle; m_mouseWasInsideHandles = false; // there used to be guides... :'''( } } else { // there used to be guides... :'''( } updateCursor(); } QRectF DefaultTool::handlesSize() { KoSelection *selection = koSelection(); if (!selection || !selection->count()) return QRectF(); recalcSelectionBox(selection); QRectF bound = m_selectionOutline.boundingRect(); // expansion Border if (!canvas() || !canvas()->viewConverter()) { return bound; } QPointF border = canvas()->viewConverter()->viewToDocument(QPointF(HANDLE_DISTANCE, HANDLE_DISTANCE)); bound.adjust(-border.x(), -border.y(), border.x(), border.y()); return bound; } void DefaultTool::mouseReleaseEvent(KoPointerEvent *event) { KoInteractionTool::mouseReleaseEvent(event); updateCursor(); // This makes sure the decorations that are shown are refreshed. especally the "T" icon canvas()->updateCanvas(QRectF(0,0,canvas()->canvasWidget()->width(), canvas()->canvasWidget()->height())); } void DefaultTool::mouseDoubleClickEvent(KoPointerEvent *event) { KoSelection *selection = koSelection(); KoShape *shape = shapeManager()->shapeAt(event->point, KoFlake::ShapeOnTop); if (shape && selection && !selection->isSelected(shape)) { if (!(event->modifiers() & Qt::ShiftModifier)) { selection->deselectAll(); } selection->select(shape); } explicitUserStrokeEndRequest(); } bool DefaultTool::moveSelection(int direction, Qt::KeyboardModifiers modifiers) { bool result = false; qreal x = 0.0, y = 0.0; if (direction == Qt::Key_Left) { x = -5; } else if (direction == Qt::Key_Right) { x = 5; } else if (direction == Qt::Key_Up) { y = -5; } else if (direction == Qt::Key_Down) { y = 5; } if (x != 0.0 || y != 0.0) { // actually move if ((modifiers & Qt::ShiftModifier) != 0) { x *= 10; y *= 10; } else if ((modifiers & Qt::AltModifier) != 0) { // more precise x /= 5; y /= 5; } QList shapes = koSelection()->selectedEditableShapes(); if (!shapes.isEmpty()) { canvas()->addCommand(new KoShapeMoveCommand(shapes, QPointF(x, y))); result = true; } } return result; } void DefaultTool::keyPressEvent(QKeyEvent *event) { KoInteractionTool::keyPressEvent(event); if (currentStrategy() == 0) { switch (event->key()) { case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Up: case Qt::Key_Down: if (moveSelection(event->key(), event->modifiers())) { event->accept(); } break; case Qt::Key_1: case Qt::Key_2: case Qt::Key_3: case Qt::Key_4: case Qt::Key_5: canvas()->resourceManager()->setResource(HotPosition, event->key() - Qt::Key_1); event->accept(); break; default: return; } } } void DefaultTool::repaintDecorations() { if (koSelection() && koSelection()->count() > 0) { canvas()->updateCanvas(handlesSize()); } } void DefaultTool::copy() const { // all the selected shapes, not only editable! QList shapes = koSelection()->selectedShapes(); if (!shapes.isEmpty()) { KoDrag drag; drag.setSvg(shapes); drag.addToClipboard(); } } void DefaultTool::deleteSelection() { QList shapes; foreach (KoShape *s, koSelection()->selectedShapes()) { if (s->isGeometryProtected()) { continue; } shapes << s; } if (!shapes.empty()) { canvas()->addCommand(canvas()->shapeController()->removeShapes(shapes)); } } bool DefaultTool::paste() { // we no longer have to do anything as tool Proxy will do it for us return false; } KoSelection *DefaultTool::koSelection() const { Q_ASSERT(canvas()); Q_ASSERT(canvas()->selectedShapesProxy()); return canvas()->selectedShapesProxy()->selection(); } KoFlake::SelectionHandle DefaultTool::handleAt(const QPointF &point, bool *innerHandleMeaning) { // check for handles in this order; meaning that when handles overlap the one on top is chosen static const KoFlake::SelectionHandle handleOrder[] = { KoFlake::BottomRightHandle, KoFlake::TopLeftHandle, KoFlake::BottomLeftHandle, KoFlake::TopRightHandle, KoFlake::BottomMiddleHandle, KoFlake::RightMiddleHandle, KoFlake::LeftMiddleHandle, KoFlake::TopMiddleHandle, KoFlake::NoHandle }; const KoViewConverter *converter = canvas()->viewConverter(); KoSelection *selection = koSelection(); if (!selection || !selection->count() || !converter) { return KoFlake::NoHandle; } recalcSelectionBox(selection); if (innerHandleMeaning) { QPainterPath path; path.addPolygon(m_selectionOutline); *innerHandleMeaning = path.contains(point) || path.intersects(handlePaintRect(point)); } const QPointF viewPoint = converter->documentToView(point); for (int i = 0; i < KoFlake::NoHandle; ++i) { KoFlake::SelectionHandle handle = handleOrder[i]; const QPointF handlePoint = converter->documentToView(m_selectionBox[handle]); const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint); // if just inside the outline if (distanceSq < HANDLE_DISTANCE_SQ) { if (innerHandleMeaning) { if (distanceSq < INNER_HANDLE_DISTANCE_SQ) { *innerHandleMeaning = true; } } return handle; } } return KoFlake::NoHandle; } void DefaultTool::recalcSelectionBox(KoSelection *selection) { KIS_ASSERT_RECOVER_RETURN(selection->count()); QTransform matrix = selection->absoluteTransformation(0); m_selectionOutline = matrix.map(QPolygonF(selection->outlineRect())); m_angle = 0.0; QPolygonF outline = m_selectionOutline; //shorter name in the following :) m_selectionBox[KoFlake::TopMiddleHandle] = (outline.value(0) + outline.value(1)) / 2; m_selectionBox[KoFlake::TopRightHandle] = outline.value(1); m_selectionBox[KoFlake::RightMiddleHandle] = (outline.value(1) + outline.value(2)) / 2; m_selectionBox[KoFlake::BottomRightHandle] = outline.value(2); m_selectionBox[KoFlake::BottomMiddleHandle] = (outline.value(2) + outline.value(3)) / 2; m_selectionBox[KoFlake::BottomLeftHandle] = outline.value(3); m_selectionBox[KoFlake::LeftMiddleHandle] = (outline.value(3) + outline.value(0)) / 2; m_selectionBox[KoFlake::TopLeftHandle] = outline.value(0); if (selection->count() == 1) { #if 0 // TODO detect mirroring KoShape *s = koSelection()->firstSelectedShape(); if (s->scaleX() < 0) { // vertically mirrored: swap left / right std::swap(m_selectionBox[KoFlake::TopLeftHandle], m_selectionBox[KoFlake::TopRightHandle]); std::swap(m_selectionBox[KoFlake::LeftMiddleHandle], m_selectionBox[KoFlake::RightMiddleHandle]); std::swap(m_selectionBox[KoFlake::BottomLeftHandle], m_selectionBox[KoFlake::BottomRightHandle]); } if (s->scaleY() < 0) { // vertically mirrored: swap top / bottom std::swap(m_selectionBox[KoFlake::TopLeftHandle], m_selectionBox[KoFlake::BottomLeftHandle]); std::swap(m_selectionBox[KoFlake::TopMiddleHandle], m_selectionBox[KoFlake::BottomMiddleHandle]); std::swap(m_selectionBox[KoFlake::TopRightHandle], m_selectionBox[KoFlake::BottomRightHandle]); } #endif } } void DefaultTool::activate(ToolActivation activation, const QSet &shapes) { KoToolBase::activate(activation, shapes); + QAction *actionBringToFront = action("object_order_front"); + connect(actionBringToFront, SIGNAL(triggered()), this, SLOT(selectionBringToFront()), Qt::UniqueConnection); + + QAction *actionRaise = action("object_order_raise"); + connect(actionRaise, SIGNAL(triggered()), this, SLOT(selectionMoveUp()), Qt::UniqueConnection); + + QAction *actionLower = action("object_order_lower"); + connect(actionLower, SIGNAL(triggered()), this, SLOT(selectionMoveDown())); + + QAction *actionSendToBack = action("object_order_back"); + connect(actionSendToBack, SIGNAL(triggered()), this, SLOT(selectionSendToBack()), Qt::UniqueConnection); + + QAction *actionGroupBottom = action("object_group"); + connect(actionGroupBottom, SIGNAL(triggered()), this, SLOT(selectionGroup()), Qt::UniqueConnection); + + QAction *actionUngroupBottom = action("object_ungroup"); + connect(actionUngroupBottom, SIGNAL(triggered()), this, SLOT(selectionUngroup()), Qt::UniqueConnection); + + QAction *actionSplit = action("object_split"); + connect(actionSplit, SIGNAL(triggered()), this, SLOT(selectionSplitShapes()), Qt::UniqueConnection); + + connect(m_alignSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionAlign(int))); + connect(m_distributeSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionDistribute(int))); + connect(m_transformSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionTransform(int))); + connect(m_booleanSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionBooleanOp(int))); + m_mouseWasInsideHandles = false; m_lastHandle = KoFlake::NoHandle; useCursor(Qt::ArrowCursor); repaintDecorations(); updateActions(); if (m_tabbedOptionWidget) { m_tabbedOptionWidget->activate(); } } void DefaultTool::deactivate() { KoToolBase::deactivate(); + QAction *actionBringToFront = action("object_order_front"); + disconnect(actionBringToFront, 0, this, 0); + + QAction *actionRaise = action("object_order_raise"); + disconnect(actionRaise, 0, this, 0); + + QAction *actionLower = action("object_order_lower"); + disconnect(actionLower, 0, this, 0); + + QAction *actionSendToBack = action("object_order_back"); + disconnect(actionSendToBack, 0, this, 0); + + QAction *actionGroupBottom = action("object_group"); + disconnect(actionGroupBottom, 0, this, 0); + + QAction *actionUngroupBottom = action("object_ungroup"); + disconnect(actionUngroupBottom, 0, this, 0); + + QAction *actionSplit = action("object_split"); + disconnect(actionSplit, 0, this, 0); + + disconnect(m_alignSignalsMapper, 0, this, 0); + disconnect(m_distributeSignalsMapper, 0, this, 0); + disconnect(m_transformSignalsMapper, 0, this, 0); + disconnect(m_booleanSignalsMapper, 0, this, 0); + + if (m_tabbedOptionWidget) { m_tabbedOptionWidget->deactivate(); } } void DefaultTool::selectionGroup() { KoSelection *selection = koSelection(); if (!selection) return; QList selectedShapes = selection->selectedEditableShapes(); std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex); if (selectedShapes.isEmpty()) return; const int groupZIndex = selectedShapes.last()->zIndex(); KoShapeGroup *group = new KoShapeGroup(); group->setZIndex(groupZIndex); // TODO what if only one shape is left? KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Group shapes")); new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, cmd); canvas()->shapeController()->addShapeDirect(group, 0, cmd); new KoShapeGroupCommand(group, selectedShapes, true, cmd); new KoKeepShapesSelectedCommand({}, {group}, canvas()->selectedShapesProxy(), true, cmd); canvas()->addCommand(cmd); // update selection so we can ungroup immediately again selection->deselectAll(); selection->select(group); } void DefaultTool::selectionUngroup() { KoSelection *selection = koSelection(); if (!selection) return; QList selectedShapes = selection->selectedEditableShapes(); std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex); KUndo2Command *cmd = 0; QList newShapes; // add a ungroup command for each found shape container to the macro command Q_FOREACH (KoShape *shape, selectedShapes) { KoShapeGroup *group = dynamic_cast(shape); if (group) { if (!cmd) { cmd = new KUndo2Command(kundo2_i18n("Ungroup shapes")); new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, cmd); } newShapes << group->shapes(); new KoShapeUngroupCommand(group, group->shapes(), group->parent() ? QList() : shapeManager()->topLevelShapes(), cmd); canvas()->shapeController()->removeShape(group, cmd); } } if (cmd) { new KoKeepShapesSelectedCommand({}, newShapes, canvas()->selectedShapesProxy(), true, cmd); canvas()->addCommand(cmd); } } void DefaultTool::selectionTransform(int transformAction) { KoSelection *selection = koSelection(); if (!selection) return; QList editableShapes = selection->selectedEditableShapes(); if (editableShapes.isEmpty()) { return; } QTransform applyTransform; bool shouldReset = false; KUndo2MagicString actionName = kundo2_noi18n("BUG: No transform action"); switch (TransformActionType(transformAction)) { case TransformRotate90CW: applyTransform.rotate(90.0); actionName = kundo2_i18n("Rotate Object 90° CW"); break; case TransformRotate90CCW: applyTransform.rotate(-90.0); actionName = kundo2_i18n("Rotate Object 90° CCW"); break; case TransformRotate180: applyTransform.rotate(180.0); actionName = kundo2_i18n("Rotate Object 180°"); break; case TransformMirrorX: applyTransform.scale(-1.0, 1.0); actionName = kundo2_i18n("Mirror Object Horizontally"); break; case TransformMirrorY: applyTransform.scale(1.0, -1.0); actionName = kundo2_i18n("Mirror Object Vertically"); break; case TransformReset: shouldReset = true; actionName = kundo2_i18n("Reset Object Transformations"); break; } if (!shouldReset && applyTransform.isIdentity()) return; QList oldTransforms; QList newTransforms; const QRectF outlineRect = KoShape::absoluteOutlineRect(editableShapes); const QPointF centerPoint = outlineRect.center(); const QTransform centerTrans = QTransform::fromTranslate(centerPoint.x(), centerPoint.y()); const QTransform centerTransInv = QTransform::fromTranslate(-centerPoint.x(), -centerPoint.y()); // we also add selection to the list of transformed shapes, so that its outline is updated correctly QList transformedShapes = editableShapes; transformedShapes << selection; Q_FOREACH (KoShape *shape, transformedShapes) { oldTransforms.append(shape->transformation()); QTransform t; if (!shouldReset) { const QTransform world = shape->absoluteTransformation(0); t = world * centerTransInv * applyTransform * centerTrans * world.inverted() * shape->transformation(); } else { const QPointF center = shape->outlineRect().center(); const QPointF offset = shape->transformation().map(center) - center; t = QTransform::fromTranslate(offset.x(), offset.y()); } newTransforms.append(t); } KoShapeTransformCommand *cmd = new KoShapeTransformCommand(transformedShapes, oldTransforms, newTransforms); cmd->setText(actionName); canvas()->addCommand(cmd); } void DefaultTool::selectionBooleanOp(int booleanOp) { KoSelection *selection = koSelection(); if (!selection) return; QList editableShapes = selection->selectedEditableShapes(); if (editableShapes.isEmpty()) { return; } QVector srcOutlines; QPainterPath dstOutline; KUndo2MagicString actionName = kundo2_noi18n("BUG: boolean action name"); // TODO: implement a reference shape selection dialog! const int referenceShapeIndex = 0; KoShape *referenceShape = editableShapes[referenceShapeIndex]; Q_FOREACH (KoShape *shape, editableShapes) { srcOutlines << shape->absoluteTransformation(0).map(shape->outline()); } if (booleanOp == BooleanUnion) { Q_FOREACH (const QPainterPath &path, srcOutlines) { dstOutline |= path; } actionName = kundo2_i18n("Unite Shapes"); } else if (booleanOp == BooleanIntersection) { for (int i = 0; i < srcOutlines.size(); i++) { if (i == 0) { dstOutline = srcOutlines[i]; } else { dstOutline &= srcOutlines[i]; } } // there is a bug in Qt, sometimes it leaves the resulting // outline open, so just close it explicitly. dstOutline.closeSubpath(); actionName = kundo2_i18n("Intersect Shapes"); } else if (booleanOp == BooleanSubtraction) { for (int i = 0; i < srcOutlines.size(); i++) { dstOutline = srcOutlines[referenceShapeIndex]; if (i != referenceShapeIndex) { dstOutline -= srcOutlines[i]; } } actionName = kundo2_i18n("Subtract Shapes"); } KoShape *newShape = 0; if (!dstOutline.isEmpty()) { newShape = KoPathShape::createShapeFromPainterPath(dstOutline); } KUndo2Command *cmd = new KUndo2Command(actionName); new KoKeepShapesSelectedCommand(editableShapes, {}, canvas()->selectedShapesProxy(), false, cmd); QList newSelectedShapes; if (newShape) { newShape->setBackground(referenceShape->background()); newShape->setStroke(referenceShape->stroke()); newShape->setZIndex(referenceShape->zIndex()); KoShapeContainer *parent = referenceShape->parent(); canvas()->shapeController()->addShapeDirect(newShape, parent, cmd); newSelectedShapes << newShape; } canvas()->shapeController()->removeShapes(editableShapes, cmd); new KoKeepShapesSelectedCommand({}, newSelectedShapes, canvas()->selectedShapesProxy(), true, cmd); canvas()->addCommand(cmd); } void DefaultTool::selectionSplitShapes() { KoSelection *selection = koSelection(); if (!selection) return; QList editableShapes = selection->selectedEditableShapes(); if (editableShapes.isEmpty()) { return; } KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Split Shapes")); new KoKeepShapesSelectedCommand(editableShapes, {}, canvas()->selectedShapesProxy(), false, cmd); QList newShapes; Q_FOREACH (KoShape *shape, editableShapes) { KoPathShape *pathShape = dynamic_cast(shape); if (!pathShape) return; QList splitShapes; if (pathShape->separate(splitShapes)) { QList normalShapes = implicitCastList(splitShapes); KoShapeContainer *parent = shape->parent(); canvas()->shapeController()->addShapesDirect(normalShapes, parent, cmd); canvas()->shapeController()->removeShape(shape, cmd); newShapes << normalShapes; } } new KoKeepShapesSelectedCommand({}, newShapes, canvas()->selectedShapesProxy(), true, cmd); canvas()->addCommand(cmd); } void DefaultTool::selectionAlign(int _align) { KoShapeAlignCommand::Align align = static_cast(_align); KoSelection *selection = koSelection(); if (!selection) return; QList editableShapes = selection->selectedEditableShapes(); if (editableShapes.isEmpty()) { return; } // TODO add an option to the widget so that one can align to the page // with multiple selected shapes too QRectF bb; // single selected shape is automatically aligned to document rect if (editableShapes.count() == 1) { if (!canvas()->resourceManager()->hasResource(KoCanvasResourceProvider::PageSize)) { return; } bb = QRectF(QPointF(0, 0), canvas()->resourceManager()->sizeResource(KoCanvasResourceProvider::PageSize)); } else { bb = KoShape::absoluteOutlineRect(editableShapes); } KoShapeAlignCommand *cmd = new KoShapeAlignCommand(editableShapes, align, bb); canvas()->addCommand(cmd); } void DefaultTool::selectionDistribute(int _distribute) { KoShapeDistributeCommand::Distribute distribute = static_cast(_distribute); KoSelection *selection = koSelection(); if (!selection) return; QList editableShapes = selection->selectedEditableShapes(); if (editableShapes.size() < 3) { return; } QRectF bb = KoShape::absoluteOutlineRect(editableShapes); KoShapeDistributeCommand *cmd = new KoShapeDistributeCommand(editableShapes, distribute, bb); canvas()->addCommand(cmd); } void DefaultTool::selectionBringToFront() { selectionReorder(KoShapeReorderCommand::BringToFront); } void DefaultTool::selectionMoveUp() { selectionReorder(KoShapeReorderCommand::RaiseShape); } void DefaultTool::selectionMoveDown() { selectionReorder(KoShapeReorderCommand::LowerShape); } void DefaultTool::selectionSendToBack() { selectionReorder(KoShapeReorderCommand::SendToBack); } void DefaultTool::selectionReorder(KoShapeReorderCommand::MoveShapeType order) { KoSelection *selection = koSelection(); if (!selection) { return; } QList selectedShapes = selection->selectedEditableShapes(); if (selectedShapes.isEmpty()) { return; } KUndo2Command *cmd = KoShapeReorderCommand::createCommand(selectedShapes, shapeManager(), order); if (cmd) { canvas()->addCommand(cmd); } } QList > DefaultTool::createOptionWidgets() { QList > widgets; m_tabbedOptionWidget = new DefaultToolTabbedWidget(this); if (isActivated()) { m_tabbedOptionWidget->activate(); } widgets.append(m_tabbedOptionWidget); connect(m_tabbedOptionWidget, SIGNAL(sigSwitchModeEditFillGradient(bool)), SLOT(slotActivateEditFillGradient(bool))); connect(m_tabbedOptionWidget, SIGNAL(sigSwitchModeEditStrokeGradient(bool)), SLOT(slotActivateEditStrokeGradient(bool))); return widgets; } void DefaultTool::canvasResourceChanged(int key, const QVariant &res) { if (key == HotPosition) { m_hotPosition = KoFlake::AnchorPosition(res.toInt()); repaintDecorations(); } } KoInteractionStrategy *DefaultTool::createStrategy(KoPointerEvent *event) { KoSelection *selection = koSelection(); if (!selection) return nullptr; bool insideSelection = false; KoFlake::SelectionHandle handle = handleAt(event->point, &insideSelection); bool editableShape = !selection->selectedEditableShapes().isEmpty(); const bool selectMultiple = event->modifiers() & Qt::ShiftModifier; const bool selectNextInStack = event->modifiers() & Qt::ControlModifier; const bool avoidSelection = event->modifiers() & Qt::AltModifier; if (selectNextInStack) { // change the hot selection position when middle clicking on a handle KoFlake::AnchorPosition newHotPosition = m_hotPosition; switch (handle) { case KoFlake::TopMiddleHandle: newHotPosition = KoFlake::Top; break; case KoFlake::TopRightHandle: newHotPosition = KoFlake::TopRight; break; case KoFlake::RightMiddleHandle: newHotPosition = KoFlake::Right; break; case KoFlake::BottomRightHandle: newHotPosition = KoFlake::BottomRight; break; case KoFlake::BottomMiddleHandle: newHotPosition = KoFlake::Bottom; break; case KoFlake::BottomLeftHandle: newHotPosition = KoFlake::BottomLeft; break; case KoFlake::LeftMiddleHandle: newHotPosition = KoFlake::Left; break; case KoFlake::TopLeftHandle: newHotPosition = KoFlake::TopLeft; break; case KoFlake::NoHandle: default: // check if we had hit the center point const KoViewConverter *converter = canvas()->viewConverter(); QPointF pt = converter->documentToView(event->point); // TODO: use calculated values instead! QPointF centerPt = converter->documentToView(selection->absolutePosition()); if (kisSquareDistance(pt, centerPt) < HANDLE_DISTANCE_SQ) { newHotPosition = KoFlake::Center; } break; } if (m_hotPosition != newHotPosition) { canvas()->resourceManager()->setResource(HotPosition, newHotPosition); return new NopInteractionStrategy(this); } } if (!avoidSelection && editableShape) { // manipulation of selected shapes goes first if (handle != KoFlake::NoHandle) { // resizing or shearing only with left mouse button if (insideSelection) { bool forceUniformScaling = m_tabbedOptionWidget && m_tabbedOptionWidget->useUniformScaling(); return new ShapeResizeStrategy(this, selection, event->point, handle, forceUniformScaling); } if (handle == KoFlake::TopMiddleHandle || handle == KoFlake::RightMiddleHandle || handle == KoFlake::BottomMiddleHandle || handle == KoFlake::LeftMiddleHandle) { return new ShapeShearStrategy(this, selection, event->point, handle); } // rotating is allowed for right mouse button too if (handle == KoFlake::TopLeftHandle || handle == KoFlake::TopRightHandle || handle == KoFlake::BottomLeftHandle || handle == KoFlake::BottomRightHandle) { return new ShapeRotateStrategy(this, selection, event->point, event->buttons()); } } if (!selectMultiple && !selectNextInStack) { if (insideSelection) { return new ShapeMoveStrategy(this, selection, event->point); } } } KoShape *shape = shapeManager()->shapeAt(event->point, selectNextInStack ? KoFlake::NextUnselected : KoFlake::ShapeOnTop); if (avoidSelection || (!shape && handle == KoFlake::NoHandle)) { if (!selectMultiple) { repaintDecorations(); selection->deselectAll(); } return new SelectionInteractionStrategy(this, event->point, false); } if (selection->isSelected(shape)) { if (selectMultiple) { repaintDecorations(); selection->deselect(shape); } } else if (handle == KoFlake::NoHandle) { // clicked on shape which is not selected repaintDecorations(); if (!selectMultiple) { selection->deselectAll(); } selection->select(shape); repaintDecorations(); // tablet selection isn't precise and may lead to a move, preventing that if (event->isTabletEvent()) { return new NopInteractionStrategy(this); } return new ShapeMoveStrategy(this, selection, event->point); } return 0; } void DefaultTool::updateActions() { QList editableShapes; if (koSelection()) { editableShapes = koSelection()->selectedEditableShapes(); } const bool hasEditableShapes = !editableShapes.isEmpty(); action("object_order_front")->setEnabled(hasEditableShapes); action("object_order_raise")->setEnabled(hasEditableShapes); action("object_order_lower")->setEnabled(hasEditableShapes); action("object_order_back")->setEnabled(hasEditableShapes); action("object_transform_rotate_90_cw")->setEnabled(hasEditableShapes); action("object_transform_rotate_90_ccw")->setEnabled(hasEditableShapes); action("object_transform_rotate_180")->setEnabled(hasEditableShapes); action("object_transform_mirror_horizontally")->setEnabled(hasEditableShapes); action("object_transform_mirror_vertically")->setEnabled(hasEditableShapes); action("object_transform_reset")->setEnabled(hasEditableShapes); const bool multipleSelected = editableShapes.size() > 1; const bool alignmentEnabled = multipleSelected || (!editableShapes.isEmpty() && canvas()->resourceManager()->hasResource(KoCanvasResourceProvider::PageSize)); action("object_align_horizontal_left")->setEnabled(alignmentEnabled); action("object_align_horizontal_center")->setEnabled(alignmentEnabled); action("object_align_horizontal_right")->setEnabled(alignmentEnabled); action("object_align_vertical_top")->setEnabled(alignmentEnabled); action("object_align_vertical_center")->setEnabled(alignmentEnabled); action("object_align_vertical_bottom")->setEnabled(alignmentEnabled); const bool distributionEnabled = editableShapes.size() > 2; action("object_distribute_horizontal_left")->setEnabled(distributionEnabled); action("object_distribute_horizontal_center")->setEnabled(distributionEnabled); action("object_distribute_horizontal_right")->setEnabled(distributionEnabled); action("object_distribute_horizontal_gaps")->setEnabled(distributionEnabled); action("object_distribute_vertical_top")->setEnabled(distributionEnabled); action("object_distribute_vertical_center")->setEnabled(distributionEnabled); action("object_distribute_vertical_bottom")->setEnabled(distributionEnabled); action("object_distribute_vertical_gaps")->setEnabled(distributionEnabled); updateDistinctiveActions(editableShapes); emit selectionChanged(editableShapes.size()); } void DefaultTool::updateDistinctiveActions(const QList &editableShapes) { const bool multipleSelected = editableShapes.size() > 1; action("object_group")->setEnabled(multipleSelected); action("object_unite")->setEnabled(multipleSelected); action("object_intersect")->setEnabled(multipleSelected); action("object_subtract")->setEnabled(multipleSelected); bool hasShapesWithMultipleSegments = false; Q_FOREACH (KoShape *shape, editableShapes) { KoPathShape *pathShape = dynamic_cast(shape); if (pathShape && pathShape->subpathCount() > 1) { hasShapesWithMultipleSegments = true; break; } } action("object_split")->setEnabled(hasShapesWithMultipleSegments); bool hasGroupShape = false; foreach (KoShape *shape, editableShapes) { if (dynamic_cast(shape)) { hasGroupShape = true; break; } } action("object_ungroup")->setEnabled(hasGroupShape); } KoToolSelection *DefaultTool::selection() { return m_selectionHandler; } QMenu* DefaultTool::popupActionsMenu() { if (m_contextMenu) { m_contextMenu->clear(); - KActionCollection *collection = this->canvas()->canvasController()->actionCollection(); - - m_contextMenu->addAction(collection->action("edit_cut")); - m_contextMenu->addAction(collection->action("edit_copy")); - m_contextMenu->addAction(collection->action("edit_paste")); + m_contextMenu->addAction(action("edit_cut")); + m_contextMenu->addAction(action("edit_copy")); + m_contextMenu->addAction(action("edit_paste")); m_contextMenu->addSeparator(); m_contextMenu->addAction(action("object_order_front")); m_contextMenu->addAction(action("object_order_raise")); m_contextMenu->addAction(action("object_order_lower")); m_contextMenu->addAction(action("object_order_back")); if (action("object_group")->isEnabled() || action("object_ungroup")->isEnabled()) { m_contextMenu->addSeparator(); m_contextMenu->addAction(action("object_group")); m_contextMenu->addAction(action("object_ungroup")); } m_contextMenu->addSeparator(); QMenu *transform = m_contextMenu->addMenu(i18n("Transform")); transform->addAction(action("object_transform_rotate_90_cw")); transform->addAction(action("object_transform_rotate_90_ccw")); transform->addAction(action("object_transform_rotate_180")); transform->addSeparator(); transform->addAction(action("object_transform_mirror_horizontally")); transform->addAction(action("object_transform_mirror_vertically")); transform->addSeparator(); transform->addAction(action("object_transform_reset")); if (action("object_unite")->isEnabled() || action("object_intersect")->isEnabled() || action("object_subtract")->isEnabled() || action("object_split")->isEnabled()) { QMenu *transform = m_contextMenu->addMenu(i18n("Logical Operations")); transform->addAction(action("object_unite")); transform->addAction(action("object_intersect")); transform->addAction(action("object_subtract")); transform->addAction(action("object_split")); } } return m_contextMenu.data(); } void DefaultTool::addTransformActions(QMenu *menu) const { menu->addAction(action("object_transform_rotate_90_cw")); menu->addAction(action("object_transform_rotate_90_ccw")); menu->addAction(action("object_transform_rotate_180")); menu->addSeparator(); menu->addAction(action("object_transform_mirror_horizontally")); menu->addAction(action("object_transform_mirror_vertically")); menu->addSeparator(); menu->addAction(action("object_transform_reset")); } void DefaultTool::explicitUserStrokeEndRequest() { QList shapes = koSelection()->selectedEditableShapesAndDelegates(); emit activateTemporary(KoToolManager::instance()->preferredToolForSelection(shapes)); } diff --git a/plugins/tools/defaulttool/defaulttool/DefaultTool.h b/plugins/tools/defaulttool/defaulttool/DefaultTool.h index 19888d2b4e..b6d698f995 100644 --- a/plugins/tools/defaulttool/defaulttool/DefaultTool.h +++ b/plugins/tools/defaulttool/defaulttool/DefaultTool.h @@ -1,193 +1,199 @@ /* This file is part of the KDE project Copyright (C) 2006-2008 Thorsten Zachmann Copyright (C) 2006-2008 Thomas Zander 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 DEFAULTTOOL_H #define DEFAULTTOOL_H #include #include #include #include #include "SelectionDecorator.h" #include #include class QSignalMapper; class KoInteractionStrategy; class KoShapeMoveCommand; class KoSelection; class DefaultToolTabbedWidget; class KisViewManager; /** * The default tool (associated with the arrow icon) implements the default * interactions you have with flake objects.
* The tool provides scaling, moving, selecting, rotation and soon skewing of * any number of shapes. *

Note that the implementation of those different strategies are delegated * to the InteractionStrategy class and its subclasses. */ class DefaultTool : public KoInteractionTool { Q_OBJECT public: /** * Constructor for basic interaction tool where user actions are translated * and handled by interaction strategies of type KoInteractionStrategy. * @param canvas the canvas this tool will be working for. */ explicit DefaultTool(KoCanvasBase *canvas); ~DefaultTool() override; enum CanvasResource { HotPosition = 1410100299 }; public: bool wantsAutoScroll() const override; void paint(QPainter &painter, const KoViewConverter &converter) override; void repaintDecorations() override; ///reimplemented void copy() const override; ///reimplemented void deleteSelection() override; ///reimplemented bool paste() override; ///reimplemented KoToolSelection *selection() override; QMenu* popupActionsMenu() override; /** * Returns which selection handle is at params point (or NoHandle if none). * @return which selection handle is at params point (or NoHandle if none). * @param point the location (in pt) where we should look for a handle * @param innerHandleMeaning this boolean is altered to true if the point * is inside the selection rectangle and false if it is just outside. * The value of innerHandleMeaning is undefined if the handle location is NoHandle */ KoFlake::SelectionHandle handleAt(const QPointF &point, bool *innerHandleMeaning = 0); public Q_SLOTS: void activate(ToolActivation activation, const QSet &shapes) override; void deactivate() override; private Q_SLOTS: void selectionAlign(int _align); void selectionDistribute(int _distribute); void selectionBringToFront(); void selectionSendToBack(); void selectionMoveUp(); void selectionMoveDown(); void selectionGroup(); void selectionUngroup(); void selectionTransform(int transformAction); void selectionBooleanOp(int booleanOp); void selectionSplitShapes(); void slotActivateEditFillGradient(bool value); void slotActivateEditStrokeGradient(bool value); protected Q_SLOTS: /// Update actions on selection change void updateActions(); public: // Events void mousePressEvent(KoPointerEvent *event) override; void mouseMoveEvent(KoPointerEvent *event) override; void mouseReleaseEvent(KoPointerEvent *event) override; void mouseDoubleClickEvent(KoPointerEvent *event) override; void keyPressEvent(QKeyEvent *event) override; void explicitUserStrokeEndRequest() override; protected: QList > createOptionWidgets() override; KoInteractionStrategy *createStrategy(KoPointerEvent *event) override; protected: friend class SelectionInteractionStrategy; virtual bool isValidForCurrentLayer() const; virtual KoShapeManager *shapeManager() const; virtual KoSelection *koSelection() const; /** * Enable/disable actions specific to the tool (vector vs. reference images) */ virtual void updateDistinctiveActions(const QList &editableShapes); void addTransformActions(QMenu *menu) const; QScopedPointer m_contextMenu; private: class MoveGradientHandleInteractionFactory; private: void setupActions(); void recalcSelectionBox(KoSelection *selection); void updateCursor(); /// Returns rotation angle of given handle of the current selection qreal rotationOfHandle(KoFlake::SelectionHandle handle, bool useEdgeRotation); void addMappedAction(QSignalMapper *mapper, const QString &actionId, int type); void selectionReorder(KoShapeReorderCommand::MoveShapeType order); bool moveSelection(int direction, Qt::KeyboardModifiers modifiers); /// Returns selection rectangle adjusted by handle proximity threshold QRectF handlesSize(); void canvasResourceChanged(int key, const QVariant &res) override; KoFlake::SelectionHandle m_lastHandle; KoFlake::AnchorPosition m_hotPosition; bool m_mouseWasInsideHandles; QPointF m_selectionBox[8]; QPolygonF m_selectionOutline; QPointF m_lastPoint; SelectionDecorator *m_decorator; // TODO alter these 3 arrays to be static const instead QCursor m_sizeCursors[8]; QCursor m_rotateCursors[8]; QCursor m_shearCursors[8]; qreal m_angle; KoToolSelection *m_selectionHandler; friend class SelectionHandler; DefaultToolTabbedWidget *m_tabbedOptionWidget; + + QSignalMapper *m_alignSignalsMapper {0}; + QSignalMapper *m_distributeSignalsMapper {0}; + QSignalMapper *m_transformSignalsMapper {0}; + QSignalMapper *m_booleanSignalsMapper {0}; }; + #endif diff --git a/plugins/tools/defaulttool/defaulttool/DefaultToolFactory.cpp b/plugins/tools/defaulttool/defaulttool/DefaultToolFactory.cpp index cabe7e0cd2..3b245543c7 100644 --- a/plugins/tools/defaulttool/defaulttool/DefaultToolFactory.cpp +++ b/plugins/tools/defaulttool/defaulttool/DefaultToolFactory.cpp @@ -1,44 +1,91 @@ /* This file is part of the KDE project * * Copyright (C) 2006-2007 Thomas Zander * * 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 "DefaultToolFactory.h" #include "DefaultTool.h" +#include + #include #include DefaultToolFactory::DefaultToolFactory() : KoToolFactoryBase(KoInteractionTool_ID) { setToolTip(i18n("Select Shapes Tool")); setSection(mainToolType()); setPriority(0); setIconName(koIconNameCStr("select")); setActivationShapeId("flake/always"); } +DefaultToolFactory::DefaultToolFactory(const QString &id) + : KoToolFactoryBase(id) +{ +} + DefaultToolFactory::~DefaultToolFactory() { } KoToolBase *DefaultToolFactory::createTool(KoCanvasBase *canvas) { return new DefaultTool(canvas); } + +QList DefaultToolFactory::createActionsImpl() +{ + KisActionRegistry *actionRegistry = KisActionRegistry::instance(); + + QList actions; + actions << actionRegistry->makeQAction("object_order_front"); + actions << actionRegistry->makeQAction("object_order_raise"); + actions << actionRegistry->makeQAction("object_order_lower"); + actions << actionRegistry->makeQAction("object_order_back"); + actions << actionRegistry->makeQAction("object_align_horizontal_left"); + actions << actionRegistry->makeQAction("object_align_horizontal_center"); + actions << actionRegistry->makeQAction("object_align_horizontal_right"); + actions << actionRegistry->makeQAction("object_align_vertical_top"); + actions << actionRegistry->makeQAction("object_align_vertical_center"); + actions << actionRegistry->makeQAction("object_align_vertical_bottom"); + actions << actionRegistry->makeQAction("object_distribute_horizontal_left"); + actions << actionRegistry->makeQAction("object_distribute_horizontal_center"); + actions << actionRegistry->makeQAction("object_distribute_horizontal_right"); + actions << actionRegistry->makeQAction("object_distribute_horizontal_gaps"); + actions << actionRegistry->makeQAction("object_distribute_vertical_top"); + actions << actionRegistry->makeQAction("object_distribute_vertical_center"); + actions << actionRegistry->makeQAction("object_distribute_vertical_bottom"); + actions << actionRegistry->makeQAction("object_distribute_vertical_gaps"); + actions << actionRegistry->makeQAction("object_group"); + actions << actionRegistry->makeQAction("object_ungroup"); + actions << actionRegistry->makeQAction("object_transform_rotate_90_cw"); + actions << actionRegistry->makeQAction("object_transform_rotate_90_ccw"); + actions << actionRegistry->makeQAction("object_transform_rotate_180"); + actions << actionRegistry->makeQAction("object_transform_mirror_horizontally"); + actions << actionRegistry->makeQAction("object_transform_mirror_vertically"); + actions << actionRegistry->makeQAction("object_transform_reset"); + actions << actionRegistry->makeQAction("object_unite"); + actions << actionRegistry->makeQAction("object_intersect"); + actions << actionRegistry->makeQAction("object_subtract"); + actions << actionRegistry->makeQAction("object_split"); + + return actions; + +} diff --git a/plugins/tools/defaulttool/defaulttool/DefaultToolFactory.h b/plugins/tools/defaulttool/defaulttool/DefaultToolFactory.h index 869b218d86..815a8de024 100644 --- a/plugins/tools/defaulttool/defaulttool/DefaultToolFactory.h +++ b/plugins/tools/defaulttool/defaulttool/DefaultToolFactory.h @@ -1,36 +1,38 @@ /* This file is part of the KDE project * * Copyright (C) 2006-2007 Thomas Zander * * 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 DEFAULTTOOLFACTORY_H #define DEFAULTTOOLFACTORY_H #include /// Factory for the KoInteractionTool class DefaultToolFactory : public KoToolFactoryBase { public: /// constructor DefaultToolFactory(); + DefaultToolFactory(const QString &id); ~DefaultToolFactory() override; KoToolBase *createTool(KoCanvasBase *canvas) override; + QList createActionsImpl() override; }; #endif diff --git a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp index 5ba3214d44..99afee138a 100644 --- a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp +++ b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp @@ -1,264 +1,289 @@ /* * Copyright (c) 2017 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; 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 "ToolReferenceImages.h" #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include "ToolReferenceImagesWidget.h" #include "KisReferenceImageCollection.h" ToolReferenceImages::ToolReferenceImages(KoCanvasBase * canvas) : DefaultTool(canvas) { setObjectName("ToolReferenceImages"); } ToolReferenceImages::~ToolReferenceImages() { } void ToolReferenceImages::activate(ToolActivation toolActivation, const QSet &shapes) { DefaultTool::activate(toolActivation, shapes); auto kisCanvas = dynamic_cast(canvas()); connect(kisCanvas->image(), SIGNAL(sigNodeAddedAsync(KisNodeSP)), this, SLOT(slotNodeAdded(KisNodeSP))); auto referenceImageLayer = document()->referenceImagesLayer(); if (referenceImageLayer) { setReferenceImageLayer(referenceImageLayer); } } void ToolReferenceImages::deactivate() { DefaultTool::deactivate(); } void ToolReferenceImages::slotNodeAdded(KisNodeSP node) { auto *referenceImagesLayer = dynamic_cast(node.data()); if (referenceImagesLayer) { setReferenceImageLayer(referenceImagesLayer); } } void ToolReferenceImages::setReferenceImageLayer(KisSharedPtr layer) { m_layer = layer; connect(layer.data(), SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged())); } void ToolReferenceImages::addReferenceImage() { auto kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas) KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenReferenceImage"); dialog.setCaption(i18n("Select a Reference Image")); QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); if (!locations.isEmpty()) { dialog.setDefaultDir(locations.first()); } QString filename = dialog.filename(); if (filename.isEmpty()) return; if (!QFileInfo(filename).exists()) return; auto *reference = KisReferenceImage::fromFile(filename, *kisCanvas->coordinatesConverter(), canvas()->canvasWidget()); if (reference) { KisDocument *doc = document(); doc->addCommand(KisReferenceImagesLayer::addReferenceImages(doc, {reference})); } } void ToolReferenceImages::removeAllReferenceImages() { auto layer = m_layer.toStrongRef(); if (!layer) return; canvas()->addCommand(layer->removeReferenceImages(document(), layer->shapes())); } void ToolReferenceImages::loadReferenceImages() { auto kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas) KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenReferenceImageCollection"); dialog.setMimeTypeFilters(QStringList() << "application/x-krita-reference-images"); dialog.setCaption(i18n("Load Reference Images")); QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); if (!locations.isEmpty()) { dialog.setDefaultDir(locations.first()); } QString filename = dialog.filename(); if (filename.isEmpty()) return; if (!QFileInfo(filename).exists()) return; QFile file(filename); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Could not open '%1'.", filename)); return; } KisReferenceImageCollection collection; if (collection.load(&file)) { QList shapes; Q_FOREACH(auto *reference, collection.referenceImages()) { shapes.append(reference); } KisDocument *doc = document(); doc->addCommand(KisReferenceImagesLayer::addReferenceImages(doc, shapes)); } else { QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Could not load reference images from '%1'.", filename)); } file.close(); } void ToolReferenceImages::saveReferenceImages() { auto layer = m_layer.toStrongRef(); if (!layer || layer->shapeCount() == 0) return; auto kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas) KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::SaveFile, "SaveReferenceImageCollection"); dialog.setMimeTypeFilters(QStringList() << "application/x-krita-reference-images"); dialog.setCaption(i18n("Save Reference Images")); QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); if (!locations.isEmpty()) { dialog.setDefaultDir(locations.first()); } QString filename = dialog.filename(); if (filename.isEmpty()) return; QFile file(filename); if (!file.open(QIODevice::WriteOnly)) { QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Could not open '%1' for saving.", filename)); return; } KisReferenceImageCollection collection(layer->referenceImages()); bool ok = collection.save(&file); file.close(); if (!ok) { QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Failed to save reference images.")); } } void ToolReferenceImages::slotSelectionChanged() { auto layer = m_layer.toStrongRef(); if (!layer) return; m_optionsWidget->selectionChanged(layer->shapeManager()->selection()); updateActions(); } QList> ToolReferenceImages::createOptionWidgets() { // Instead of inheriting DefaultTool's multi-tab implementation, inherit straight from KoToolBase return KoToolBase::createOptionWidgets(); } QWidget *ToolReferenceImages::createOptionWidget() { if (!m_optionsWidget) { m_optionsWidget = new ToolReferenceImagesWidget(this); // 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); } return m_optionsWidget; } bool ToolReferenceImages::isValidForCurrentLayer() const { return true; } KoShapeManager *ToolReferenceImages::shapeManager() const { auto layer = m_layer.toStrongRef(); return layer ? layer->shapeManager() : nullptr; } KoSelection *ToolReferenceImages::koSelection() const { auto manager = shapeManager(); return manager ? manager->selection() : nullptr; } void ToolReferenceImages::updateDistinctiveActions(const QList &) { action("object_group")->setEnabled(false); action("object_unite")->setEnabled(false); action("object_intersect")->setEnabled(false); action("object_subtract")->setEnabled(false); action("object_split")->setEnabled(false); action("object_ungroup")->setEnabled(false); } void ToolReferenceImages::deleteSelection() { auto layer = m_layer.toStrongRef(); if (!layer) return; QList shapes = koSelection()->selectedShapes(); if (!shapes.empty()) { canvas()->addCommand(layer->removeReferenceImages(document(), shapes)); } } KisDocument *ToolReferenceImages::document() const { auto kisCanvas = dynamic_cast(canvas()); return kisCanvas->imageView()->document(); } + +QList ToolReferenceImagesFactory::createActionsImpl() +{ + KisActionRegistry *actionRegistry = KisActionRegistry::instance(); + QList actions = DefaultToolFactory::createActionsImpl(); + + actions << actionRegistry->makeQAction("object_order_front"); + actions << actionRegistry->makeQAction("object_order_raise"); + actions << actionRegistry->makeQAction("object_order_lower"); + actions << actionRegistry->makeQAction("object_order_back"); + actions << actionRegistry->makeQAction("object_group"); + actions << actionRegistry->makeQAction("object_ungroup"); + actions << actionRegistry->makeQAction("object_transform_rotate_90_cw"); + actions << actionRegistry->makeQAction("object_transform_rotate_90_ccw"); + actions << actionRegistry->makeQAction("object_transform_rotate_180"); + actions << actionRegistry->makeQAction("object_transform_mirror_horizontally"); + actions << actionRegistry->makeQAction("object_transform_mirror_vertically"); + actions << actionRegistry->makeQAction("object_transform_reset"); + actions << actionRegistry->makeQAction("object_unite"); + actions << actionRegistry->makeQAction("object_intersect"); + actions << actionRegistry->makeQAction("object_subtract"); + actions << actionRegistry->makeQAction("object_split"); + return actions; +} diff --git a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.h b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.h index 3b3ab30fe5..33adb33ece 100644 --- a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.h +++ b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.h @@ -1,107 +1,110 @@ /* * Copyright (c) 2017 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; 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. */ #ifndef TOOL_REFERENCE_IMAGES_H #define TOOL_REFERENCE_IMAGES_H #include #include #include #include #include "kis_painting_assistant.h" #include #include #include +#include class ToolReferenceImagesWidget; class KisReferenceImagesLayer; class ToolReferenceImages : public DefaultTool { Q_OBJECT public: ToolReferenceImages(KoCanvasBase * canvas); ~ToolReferenceImages() override; virtual quint32 priority() { return 3; } void mouseDoubleClickEvent(KoPointerEvent */*event*/) override {} void deleteSelection() override; protected: QList> createOptionWidgets() override; QWidget *createOptionWidget() override; bool isValidForCurrentLayer() const override; KoShapeManager *shapeManager() const override; KoSelection *koSelection() const override; void updateDistinctiveActions(const QList &editableShapes) override; public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; void addReferenceImage(); void removeAllReferenceImages(); void saveReferenceImages(); void loadReferenceImages(); void slotNodeAdded(KisNodeSP node); void slotSelectionChanged(); private: friend class ToolReferenceImagesWidget; ToolReferenceImagesWidget *m_optionsWidget = nullptr; KisWeakSharedPtr m_layer; KisDocument *document() const; void setReferenceImageLayer(KisSharedPtr layer); }; -class ToolReferenceImagesFactory : public KoToolFactoryBase +class ToolReferenceImagesFactory : public DefaultToolFactory { public: ToolReferenceImagesFactory() - : KoToolFactoryBase("ToolReferenceImages") { + : DefaultToolFactory("ToolReferenceImages") { setToolTip(i18n("Reference Images Tool")); setSection(TOOL_TYPE_VIEW); setIconName(koIconNameCStr("krita_tool_reference_images")); setPriority(2); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); }; ~ToolReferenceImagesFactory() override {} KoToolBase * createTool(KoCanvasBase * canvas) override { return new ToolReferenceImages(canvas); } + QList createActionsImpl() override; + }; #endif diff --git a/plugins/tools/karbonplugins/tools/CMakeLists.txt b/plugins/tools/karbonplugins/tools/CMakeLists.txt index 3f5610caee..4a1e71f054 100644 --- a/plugins/tools/karbonplugins/tools/CMakeLists.txt +++ b/plugins/tools/karbonplugins/tools/CMakeLists.txt @@ -1,47 +1,48 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/filterEffectTool ) ########### next target ############### set(karbon_tools_SOURCES KarbonToolsPlugin.cpp KarbonCursor.cpp CalligraphyTool/KarbonCalligraphyTool.cpp CalligraphyTool/KarbonCalligraphyOptionWidget.cpp CalligraphyTool/KarbonCalligraphyToolFactory.cpp CalligraphyTool/KarbonCalligraphicShape.cpp CalligraphyTool/KarbonCalligraphicShapeFactory.cpp CalligraphyTool/KarbonSimplifyPath.cpp KarbonPatternTool.cpp KarbonPatternToolFactory.cpp KarbonPatternEditStrategy.cpp filterEffectTool/KarbonFilterEffectsTool.cpp filterEffectTool/KarbonFilterEffectsToolFactory.cpp filterEffectTool/FilterEffectEditWidget.cpp filterEffectTool/FilterEffectScene.cpp filterEffectTool/FilterEffectSceneItems.cpp filterEffectTool/FilterInputChangeCommand.cpp filterEffectTool/FilterAddCommand.cpp filterEffectTool/FilterRemoveCommand.cpp filterEffectTool/FilterStackSetCommand.cpp filterEffectTool/FilterRegionChangeCommand.cpp filterEffectTool/FilterEffectResource.cpp filterEffectTool/FilterResourceServerProvider.cpp filterEffectTool/FilterRegionEditStrategy.cpp KarbonPatternOptionsWidget.cpp ) ki18n_wrap_ui(karbon_tools_SOURCES filterEffectTool/FilterEffectEditWidget.ui KarbonPatternOptionsWidget.ui ) qt5_add_resources(karbon_tools_SOURCES karbontools.qrc) add_library(krita_karbontools MODULE ${karbon_tools_SOURCES}) target_link_libraries(krita_karbontools kritaui kritawidgets KF5::Completion) install(TARGETS krita_karbontools DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) +install(FILES CalligraphyTool/KarbonCalligraphyTool.action DESTINATION ${DATA_INSTALL_DIR}/krita/actions) diff --git a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.action b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.action new file mode 100644 index 0000000000..7184ae1979 --- /dev/null +++ b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.action @@ -0,0 +1,46 @@ + + + +General + + + Calligraphy: increase width + + Calligraphy: increase width + Calligraphy: increase width + + false + + + + + Calligraphy: increase angle + + Calligraphy: increase angle + Calligraphy: increase angle + + false + + + + + Calligraphy: decrease width + + Calligraphy: decrease width + Calligraphy: decrease width + + false + + + + + Calligraphy: decrease angle + + Calligraphy: decrease angle + Calligraphy: decrease angle + + false + + + + diff --git a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.cpp b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.cpp index 266a92cde0..af512b2a32 100644 --- a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.cpp +++ b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.cpp @@ -1,516 +1,520 @@ /* This file is part of the KDE project * Copyright (C) 2008 Fela Winkelmolen * * 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 "KarbonCalligraphyTool.h" #include "KarbonCalligraphicShape.h" #include "KarbonCalligraphyOptionWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef M_PI const qreal M_PI = 3.1415927; using std::pow; using std::sqrt; KarbonCalligraphyTool::KarbonCalligraphyTool(KoCanvasBase *canvas) : KoToolBase(canvas) , m_shape(0) , m_angle(0) , m_selectedPath(0) , m_isDrawing(false) , m_speed(0, 0) , m_lastShape(0) { connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), SLOT(updateSelectedPath())); updateSelectedPath(); } KarbonCalligraphyTool::~KarbonCalligraphyTool() { } void KarbonCalligraphyTool::paint(QPainter &painter, const KoViewConverter &converter) { if (m_selectedPath) { painter.save(); painter.setRenderHints(QPainter::Antialiasing, false); painter.setPen(Qt::red); // TODO make configurable QRectF rect = m_selectedPath->boundingRect(); QPointF p1 = converter.documentToView(rect.topLeft()); QPointF p2 = converter.documentToView(rect.bottomRight()); painter.drawRect(QRectF(p1, p2)); painter.restore(); } if (!m_shape) { return; } painter.save(); painter.setTransform(m_shape->absoluteTransformation(&converter) * painter.transform()); KoShapePaintingContext paintContext; //FIXME m_shape->paint(painter, converter, paintContext); painter.restore(); } void KarbonCalligraphyTool::mousePressEvent(KoPointerEvent *event) { if (m_isDrawing) { return; } m_lastPoint = event->point; m_speed = QPointF(0, 0); m_isDrawing = true; m_pointCount = 0; m_shape = new KarbonCalligraphicShape(m_caps); m_shape->setBackground(QSharedPointer(new KoColorBackground(canvas()->resourceManager()->foregroundColor().toQColor()))); //addPoint( event ); } void KarbonCalligraphyTool::mouseMoveEvent(KoPointerEvent *event) { if (!m_isDrawing) { return; } addPoint(event); } void KarbonCalligraphyTool::mouseReleaseEvent(KoPointerEvent *event) { if (!m_isDrawing) { return; } if (m_pointCount == 0) { // handle click: select shape (if any) if (event->point == m_lastPoint) { KoShapeManager *shapeManager = canvas()->shapeManager(); KoShape *selectedShape = shapeManager->shapeAt(event->point); if (selectedShape != 0) { shapeManager->selection()->deselectAll(); shapeManager->selection()->select(selectedShape); } } delete m_shape; m_shape = 0; m_isDrawing = false; return; } else { m_endOfPath = false; // allow last point being added addPoint(event); // add last point m_isDrawing = false; } m_shape->simplifyGuidePath(); KUndo2Command *cmd = canvas()->shapeController()->addShape(m_shape, 0); if (cmd) { m_lastShape = m_shape; canvas()->addCommand(cmd); canvas()->updateCanvas(m_shape->boundingRect()); } else { // don't leak shape when command could not be created delete m_shape; } m_shape = 0; } void KarbonCalligraphyTool::addPoint(KoPointerEvent *event) { if (m_pointCount == 0) { if (m_usePath && m_selectedPath) { m_selectedPathOutline = m_selectedPath->outline(); } m_pointCount = 1; m_endOfPath = false; m_followPathPosition = 0; m_lastMousePos = event->point; m_lastPoint = calculateNewPoint(event->point, &m_speed); m_deviceSupportsTilt = (event->xTilt() != 0 || event->yTilt() != 0); return; } if (m_endOfPath) { return; } ++m_pointCount; setAngle(event); QPointF newSpeed; QPointF newPoint = calculateNewPoint(event->point, &newSpeed); qreal width = calculateWidth(event->pressure()); qreal angle = calculateAngle(m_speed, newSpeed); // add the previous point m_shape->appendPoint(m_lastPoint, angle, width); m_speed = newSpeed; m_lastPoint = newPoint; canvas()->updateCanvas(m_shape->lastPieceBoundingRect()); if (m_usePath && m_selectedPath) { m_speed = QPointF(0, 0); // following path } } void KarbonCalligraphyTool::setAngle(KoPointerEvent *event) { if (!m_useAngle) { m_angle = (360 - m_customAngle + 90) / 180.0 * M_PI; return; } // setting m_angle to the angle of the device if (event->xTilt() != 0 || event->yTilt() != 0) { m_deviceSupportsTilt = false; } if (m_deviceSupportsTilt) { if (event->xTilt() == 0 && event->yTilt() == 0) { return; // leave as is } qDebug() << "using tilt" << m_angle; if (event->x() == 0) { m_angle = M_PI / 2; return; } // y is inverted in qt painting m_angle = std::atan(static_cast(-event->yTilt() / event->xTilt())) + M_PI / 2; } else { m_angle = event->rotation() + M_PI / 2; qDebug() << "using rotation" << m_angle; } } QPointF KarbonCalligraphyTool::calculateNewPoint(const QPointF &mousePos, QPointF *speed) { if (!m_usePath || !m_selectedPath) { // don't follow path QPointF force = mousePos - m_lastPoint; QPointF dSpeed = force / m_mass; *speed = m_speed * (1.0 - m_drag) + dSpeed; return m_lastPoint + *speed; } QPointF sp = mousePos - m_lastMousePos; m_lastMousePos = mousePos; // follow selected path qreal step = QLineF(QPointF(0, 0), sp).length(); m_followPathPosition += step; qreal t; if (m_followPathPosition >= m_selectedPathOutline.length()) { t = 1.0; m_endOfPath = true; } else { t = m_selectedPathOutline.percentAtLength(m_followPathPosition); } QPointF res = m_selectedPathOutline.pointAtPercent(t) + m_selectedPath->position(); *speed = res - m_lastPoint; return res; } qreal KarbonCalligraphyTool::calculateWidth(qreal pressure) { // calculate the modulo of the speed qreal speed = std::sqrt(pow(m_speed.x(), 2) + pow(m_speed.y(), 2)); qreal thinning = m_thinning * (speed + 1) / 10.0; // can be negative if (thinning > 1) { thinning = 1; } if (!m_usePressure) { pressure = 1.0; } qreal strokeWidth = m_strokeWidth * pressure * (1 - thinning); const qreal MINIMUM_STROKE_WIDTH = 1.0; if (strokeWidth < MINIMUM_STROKE_WIDTH) { strokeWidth = MINIMUM_STROKE_WIDTH; } return strokeWidth; } qreal KarbonCalligraphyTool::calculateAngle(const QPointF &oldSpeed, const QPointF &newSpeed) { // calculate the average of the speed (sum of the normalized values) qreal oldLength = QLineF(QPointF(0, 0), oldSpeed).length(); qreal newLength = QLineF(QPointF(0, 0), newSpeed).length(); QPointF oldSpeedNorm = !qFuzzyCompare(oldLength + 1, 1) ? oldSpeed / oldLength : QPointF(0, 0); QPointF newSpeedNorm = !qFuzzyCompare(newLength + 1, 1) ? newSpeed / newLength : QPointF(0, 0); QPointF speed = oldSpeedNorm + newSpeedNorm; // angle solely based on the speed qreal speedAngle = 0; if (speed.x() != 0) { // avoid division by zero speedAngle = std::atan(speed.y() / speed.x()); } else if (speed.y() > 0) { // x == 0 && y != 0 speedAngle = M_PI / 2; } else if (speed.y() < 0) { // x == 0 && y != 0 speedAngle = -M_PI / 2; } if (speed.x() < 0) { speedAngle += M_PI; } // move 90 degrees speedAngle += M_PI / 2; qreal fixedAngle = m_angle; // check if the fixed angle needs to be flipped qreal diff = fixedAngle - speedAngle; while (diff >= M_PI) { // normalize diff between -180 and 180 diff -= 2 * M_PI; } while (diff < -M_PI) { diff += 2 * M_PI; } if (std::abs(diff) > M_PI / 2) { // if absolute value < 90 fixedAngle += M_PI; // += 180 } qreal dAngle = speedAngle - fixedAngle; // normalize dAngle between -90 and +90 while (dAngle >= M_PI / 2) { dAngle -= M_PI; } while (dAngle < -M_PI / 2) { dAngle += M_PI; } qreal angle = fixedAngle + dAngle * (1.0 - m_fixation); return angle; } void KarbonCalligraphyTool::activate(ToolActivation activation, const QSet &shapes) { KoToolBase::activate(activation, shapes); + QAction *a = action("calligraphy_increase_width"); + connect(a, SIGNAL(triggered()), m_widget, SLOT(increaseWidth()), Qt::UniqueConnection); + + a = action("calligraphy_decrease_width"); + connect(a, SIGNAL(triggered()), m_widget, SLOT(decreaseWidth()), Qt::UniqueConnection); + + a = action("calligraphy_increase_angle"); + connect(a, SIGNAL(triggered()), m_widget, SLOT(increaseAngle()), Qt::UniqueConnection); + + a = action("calligraphy_decrease_angle"); + connect(a, SIGNAL(triggered()), m_widget, SLOT(decreaseAngle()), Qt::UniqueConnection); + + useCursor(Qt::CrossCursor); m_lastShape = 0; } void KarbonCalligraphyTool::deactivate() { + QAction *a = action("calligraphy_increase_width"); + disconnect(a, 0, this, 0); + + a = action("calligraphy_decrease_width"); + disconnect(a, 0, this, 0); + + a = action("calligraphy_increase_angle"); + disconnect(a, 0, this, 0); + + a = action("calligraphy_decrease_angle"); + disconnect(a, 0, this, 0); + if (m_lastShape && canvas()->shapeManager()->shapes().contains(m_lastShape)) { KoSelection *selection = canvas()->shapeManager()->selection(); selection->deselectAll(); selection->select(m_lastShape); } KoToolBase::deactivate(); } QList > KarbonCalligraphyTool::createOptionWidgets() { // if the widget don't exists yet create it QList > widgets; //KoFillConfigWidget *fillWidget = new KoFillConfigWidget(0); //fillWidget->setWindowTitle(i18n("Fill")); //widgets.append(fillWidget); - KarbonCalligraphyOptionWidget *widget = new KarbonCalligraphyOptionWidget; - connect(widget, SIGNAL(usePathChanged(bool)), + m_widget = new KarbonCalligraphyOptionWidget(); + connect(m_widget, SIGNAL(usePathChanged(bool)), this, SLOT(setUsePath(bool))); - connect(widget, SIGNAL(usePressureChanged(bool)), + connect(m_widget, SIGNAL(usePressureChanged(bool)), this, SLOT(setUsePressure(bool))); - connect(widget, SIGNAL(useAngleChanged(bool)), + connect(m_widget, SIGNAL(useAngleChanged(bool)), this, SLOT(setUseAngle(bool))); - connect(widget, SIGNAL(widthChanged(double)), + connect(m_widget, SIGNAL(widthChanged(double)), this, SLOT(setStrokeWidth(double))); - connect(widget, SIGNAL(thinningChanged(double)), + connect(m_widget, SIGNAL(thinningChanged(double)), this, SLOT(setThinning(double))); - connect(widget, SIGNAL(angleChanged(int)), + connect(m_widget, SIGNAL(angleChanged(int)), this, SLOT(setAngle(int))); - connect(widget, SIGNAL(fixationChanged(double)), + connect(m_widget, SIGNAL(fixationChanged(double)), this, SLOT(setFixation(double))); - connect(widget, SIGNAL(capsChanged(double)), + connect(m_widget, SIGNAL(capsChanged(double)), this, SLOT(setCaps(double))); - connect(widget, SIGNAL(massChanged(double)), + connect(m_widget, SIGNAL(massChanged(double)), this, SLOT(setMass(double))); - connect(widget, SIGNAL(dragChanged(double)), + connect(m_widget, SIGNAL(dragChanged(double)), this, SLOT(setDrag(double))); connect(this, SIGNAL(pathSelectedChanged(bool)), - widget, SLOT(setUsePathEnabled(bool))); - - // add shortcuts - QAction *action = new QAction(i18n("Calligraphy: increase width"), this); - action->setShortcut(Qt::Key_Right); - connect(action, SIGNAL(triggered()), widget, SLOT(increaseWidth())); - addAction("calligraphy_increase_width", action); - - action = new QAction(i18n("Calligraphy: decrease width"), this); - action->setShortcut(Qt::Key_Left); - connect(action, SIGNAL(triggered()), widget, SLOT(decreaseWidth())); - addAction("calligraphy_decrease_width", action); - - action = new QAction(i18n("Calligraphy: increase angle"), this); - action->setShortcut(Qt::Key_Up); - connect(action, SIGNAL(triggered()), widget, SLOT(increaseAngle())); - addAction("calligraphy_increase_angle", action); - - action = new QAction(i18n("Calligraphy: decrease angle"), this); - action->setShortcut(Qt::Key_Down); - connect(action, SIGNAL(triggered()), widget, SLOT(decreaseAngle())); - addAction("calligraphy_decrease_angle", action); + m_widget, SLOT(setUsePathEnabled(bool))); // sync all parameters with the loaded profile - widget->emitAll(); - widget->setObjectName(i18n("Calligraphy")); - widget->setWindowTitle(i18n("Calligraphy")); - widgets.append(widget); + m_widget->emitAll(); + m_widget->setObjectName(i18n("Calligraphy")); + m_widget->setWindowTitle(i18n("Calligraphy")); + widgets.append(m_widget); return widgets; } void KarbonCalligraphyTool::setStrokeWidth(double width) { m_strokeWidth = width; } void KarbonCalligraphyTool::setThinning(double thinning) { m_thinning = thinning; } void KarbonCalligraphyTool::setAngle(int angle) { m_customAngle = angle; } void KarbonCalligraphyTool::setFixation(double fixation) { m_fixation = fixation; } void KarbonCalligraphyTool::setMass(double mass) { m_mass = mass * mass + 1; } void KarbonCalligraphyTool::setDrag(double drag) { m_drag = drag; } void KarbonCalligraphyTool::setUsePath(bool usePath) { m_usePath = usePath; } void KarbonCalligraphyTool::setUsePressure(bool usePressure) { m_usePressure = usePressure; } void KarbonCalligraphyTool::setUseAngle(bool useAngle) { m_useAngle = useAngle; } void KarbonCalligraphyTool::setCaps(double caps) { m_caps = caps; } void KarbonCalligraphyTool::updateSelectedPath() { KoPathShape *oldSelectedPath = m_selectedPath; // save old value KoSelection *selection = canvas()->shapeManager()->selection(); if (selection) { // null pointer if it the selection isn't a KoPathShape // or if the selection is empty m_selectedPath = dynamic_cast(selection->firstSelectedShape()); // or if it's a KoPathShape but with no or more than one subpaths if (m_selectedPath && m_selectedPath->subpathCount() != 1) { m_selectedPath = 0; } // or if there ora none or more than 1 shapes selected if (selection->count() != 1) { m_selectedPath = 0; } // emit signal it there wasn't a selected path and now there is // or the other way around if ((m_selectedPath != 0) != (oldSelectedPath != 0)) { emit pathSelectedChanged(m_selectedPath != 0); } } } diff --git a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.h b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.h index 76c3669925..5bf3a8d320 100644 --- a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.h +++ b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.h @@ -1,110 +1,114 @@ /* This file is part of the KDE project * Copyright (C) 2008 Fela Winkelmolen * * 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 KARBONCALLIGRAPHYTOOL_H #define KARBONCALLIGRAPHYTOOL_H #include #include #include +#include "KarbonCalligraphyOptionWidget.h" + class KoPathShape; class KarbonCalligraphicShape; class KarbonCalligraphyTool : public KoToolBase { Q_OBJECT public: explicit KarbonCalligraphyTool(KoCanvasBase *canvas); ~KarbonCalligraphyTool() override; void paint(QPainter &painter, const KoViewConverter &converter) override; void mousePressEvent(KoPointerEvent *event) override; void mouseMoveEvent(KoPointerEvent *event) override; void mouseReleaseEvent(KoPointerEvent *event) override; QList > createOptionWidgets() override; void activate(ToolActivation activation, const QSet &shapes) override; void deactivate() override; Q_SIGNALS: void pathSelectedChanged(bool selection); private Q_SLOTS: void setUsePath(bool usePath); void setUsePressure(bool usePressure); void setUseAngle(bool useAngle); void setStrokeWidth(double width); void setThinning(double thinning); void setAngle(int angle); // set theangle in degrees void setFixation(double fixation); void setCaps(double caps); void setMass(double mass); // set the mass in user friendly format void setDrag(double drag); void updateSelectedPath(); private: void addPoint(KoPointerEvent *event); // auxiliary function that sets m_angle void setAngle(KoPointerEvent *event); // auxiliary functions to calculate the dynamic parameters // returns the new point and sets speed to the speed QPointF calculateNewPoint(const QPointF &mousePos, QPointF *speed); qreal calculateWidth(qreal pressure); qreal calculateAngle(const QPointF &oldSpeed, const QPointF &newSpeed); QPointF m_lastPoint; KarbonCalligraphicShape *m_shape; // used to determine if the device supports tilt bool m_deviceSupportsTilt; bool m_usePath; // follow selected path bool m_usePressure; // use tablet pressure bool m_useAngle; // use tablet angle qreal m_strokeWidth; qreal m_lastWidth; qreal m_customAngle; // angle set by the user qreal m_angle; // angle to use, may use the device angle, in radians!!! qreal m_fixation; qreal m_thinning; qreal m_caps; qreal m_mass; // in raw format (not user friendly) qreal m_drag; // from 0.0 to 1.0 KoPathShape *m_selectedPath; QPainterPath m_selectedPathOutline; qreal m_followPathPosition; bool m_endOfPath; QPointF m_lastMousePos; bool m_isDrawing; int m_pointCount; // dynamic parameters QPointF m_speed; // used as a vector // last calligraphic shape drawn, if any KarbonCalligraphicShape *m_lastShape; + + KarbonCalligraphyOptionWidget *m_widget {0}; }; #endif // KARBONCALLIGRAPHYTOOL_H diff --git a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyToolFactory.cpp b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyToolFactory.cpp index 69288a567b..a342b427a7 100644 --- a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyToolFactory.cpp +++ b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyToolFactory.cpp @@ -1,46 +1,60 @@ /* 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 "KarbonCalligraphyToolFactory.h" #include "KarbonCalligraphyTool.h" #include +#include #include #include #include KarbonCalligraphyToolFactory::KarbonCalligraphyToolFactory() : KoToolFactoryBase("KarbonCalligraphyTool") { setToolTip(i18n("Calligraphy")); setSection(mainToolType()); setIconName(koIconNameCStr("calligraphy")); setPriority(6); setActivationShapeId("flake/edit"); } KarbonCalligraphyToolFactory::~KarbonCalligraphyToolFactory() { } KoToolBase *KarbonCalligraphyToolFactory::createTool(KoCanvasBase *canvas) { return new KarbonCalligraphyTool(canvas); } + +QList KarbonCalligraphyToolFactory::createActionsImpl() +{ + KisActionRegistry *actionRegistry = KisActionRegistry::instance(); + QList actions; + + actions << actionRegistry->makeQAction("calligraphy_increase_width"); + actions << actionRegistry->makeQAction("calligraphy_decrease_width"); + actions << actionRegistry->makeQAction("calligraphy_increase_angle"); + actions << actionRegistry->makeQAction("calligraphy_decrease_angle"); + + return actions; +} diff --git a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyToolFactory.h b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyToolFactory.h index 8290f26aa6..307a48bd5f 100644 --- a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyToolFactory.h +++ b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyToolFactory.h @@ -1,34 +1,36 @@ /* 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. */ #ifndef _KARBONCALLIGRAPHYTOOLFACTORY_H_ #define _KARBONCALLIGRAPHYTOOLFACTORY_H_ #include class KarbonCalligraphyToolFactory : public KoToolFactoryBase { public: KarbonCalligraphyToolFactory(); ~KarbonCalligraphyToolFactory() override; KoToolBase *createTool(KoCanvasBase *canvas) override; + + QList createActionsImpl() override; }; #endif // _KARBONCALLIGRAPHYTOOLFACTORY_H_ diff --git a/plugins/tools/svgtexttool/SvgTextTool.action b/plugins/tools/svgtexttool/SvgTextTool.action index 2e2466dcee..f4f417f9ce 100644 --- a/plugins/tools/svgtexttool/SvgTextTool.action +++ b/plugins/tools/svgtexttool/SvgTextTool.action @@ -1,245 +1,255 @@ - + SVG Text Tool + + + Text Tool + + Text Tool + Text Tool + CTRL+T + false + + Text Color Text Color... format-text-color false Text Color Background Background Color... format-fill-color false Background Font Size Font Size false Font Size Font Ctrl+Alt+F Change character size, font, boldface, italics etc. Change the attributes of the currently selected characters. false Font... Special Character Alt+Shift+C Insert one or more symbols or characters not found on the keyboard character-set Insert one or more symbols or characters not found on the keyboard. false Special Character... Align Right Ctrl+Alt+R Align Right format-justify-right false Align Right Align Left Align Left format-justify-left false Align Left Align Block Ctrl+Alt+R Align Block format-justify-fill false Align Block Align Center Ctrl+Alt+C Align Center format-justify-center false Align Center Decrease Font Size Ctrl+< Decrease Font Size false Decrease Font Size Increase Font Size Ctrl+> Increase Font Size false Increase Font Size Subscript Ctrl+Shift+B Subscript format-text-subscript false Subscript Superscript Ctrl+Shift+P Superscript format-text-superscript false Superscript Underline Ctrl+U Underline format-text-underline false Underline Strikethrough Strikethrough format-text-strikethrough false Strikethrough Bold Ctrl+B Bold format-text-bold true Bold Italic Ctrl+I Italic format-text-italic true Italic Normal Ctrl+N Normal format-text-normal false Normal Demi Demi format-text-demi false Demi Black Black format-text-black false Black Light Ctrl+L Light format-text-light false Light Line Height Ctrl+H Line Height in em false Line Height Settings Text Editor Settings false Settings... Colorpicker Pick a color from the screen false Color Picker diff --git a/plugins/tools/svgtexttool/SvgTextToolFactory.cpp b/plugins/tools/svgtexttool/SvgTextToolFactory.cpp index 078660d055..459c42a33c 100644 --- a/plugins/tools/svgtexttool/SvgTextToolFactory.cpp +++ b/plugins/tools/svgtexttool/SvgTextToolFactory.cpp @@ -1,47 +1,48 @@ /* This file is part of the KDE project Copyright 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. */ #include "SvgTextToolFactory.h" #include "KoSvgTextShape.h" #include "SvgTextTool.h" #include #include SvgTextToolFactory::SvgTextToolFactory() : KoToolFactoryBase("SvgTextTool") { setToolTip(i18n("SVG Text Tool")); setIconName(koIconNameCStr("draw-text")); setSection(mainToolType()); setPriority(1); setActivationShapeId(QString("flake/always,%1").arg(KoSvgTextShape_SHAPEID)); + setShortcut(QKeySequence("CTRL+T")); } SvgTextToolFactory::~SvgTextToolFactory() { } KoToolBase *SvgTextToolFactory::createTool(KoCanvasBase *canvas) { return new SvgTextTool(canvas); } diff --git a/plugins/tools/tool_dyna/kis_tool_dyna.cpp b/plugins/tools/tool_dyna/kis_tool_dyna.cpp index b7d4ab2726..1e2a90d9cd 100644 --- a/plugins/tools/tool_dyna/kis_tool_dyna.cpp +++ b/plugins/tools/tool_dyna/kis_tool_dyna.cpp @@ -1,350 +1,349 @@ /* * kis_tool_dyna.cpp - part of Krita * * Copyright (c) 2009-2011 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * 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_dyna.h" #include #include #include #include #include #include "KoPointerEvent.h" #include "kundo2magicstring.h" #include "kis_cursor.h" #include #define MAXIMUM_SMOOTHNESS 1000 #define MAXIMUM_MAGNETISM 1000 #define MIN_MASS 1.0 #define MAX_MASS 160.0 #define MIN_DRAG 0.0 #define MAX_DRAG 0.5 #define MIN_ACC 0.000001 #define MIN_VEL 0.000001 KisToolDyna::KisToolDyna(KoCanvasBase * canvas) : KisToolFreehand(canvas, KisCursor::load("tool_freehand_cursor.png", 5, 5), kundo2_i18n("Dynamic Brush Stroke")) { setObjectName("tool_dyna"); initDyna(); } void KisToolDyna::initDyna() { /* dynadraw init */ m_curmass = 0.5; m_curdrag = 0.15; m_mouse.fixedangle = false; m_width = 1.5; m_xangle = 0.60; m_yangle = 0.20; m_widthRange = 0.05; } KisToolDyna::~KisToolDyna() { } void KisToolDyna::resetCursorStyle() { KisToolFreehand::resetCursorStyle(); overrideCursorIfNotEditable(); } void KisToolDyna::activate(ToolActivation toolActivation, const QSet &shapes) { KisToolPaint::activate(toolActivation, shapes); m_configGroup = KSharedConfig::openConfig()->group(toolId()); } void KisToolDyna::initStroke(KoPointerEvent *event) { QRectF imageSize = QRectF(QPointF(0.0,0.0),currentImage()->size()); QRectF documentSize = currentImage()->pixelToDocument(imageSize); m_surfaceWidth = documentSize.width(); m_surfaceHeight = documentSize.height(); setMousePosition(event->point); m_mouse.init(m_mousePos.x(), m_mousePos.y()); KisToolFreehand::initStroke(event); } void KisToolDyna::beginPrimaryAction(KoPointerEvent *event) { setMousePosition(event->point); m_mouse.init(m_mousePos.x(), m_mousePos.y()); m_odelx = m_mousePos.x(); m_odely = m_mousePos.y(); KisToolFreehand::beginPrimaryAction(event); } void KisToolDyna::continuePrimaryAction(KoPointerEvent *event) { setMousePosition(event->point); if (applyFilter(m_mousePos.x(), m_mousePos.y())) { KoPointerEvent newEvent = filterEvent(event); KisToolFreehand::continuePrimaryAction(&newEvent); } } // dyna algorithm int KisToolDyna::applyFilter(qreal mx, qreal my) { /* calculate mass and drag */ qreal mass = flerp(MIN_MASS, MAX_MASS, m_curmass); qreal drag = flerp(MIN_DRAG, MAX_DRAG, m_curdrag * m_curdrag); /* calculate force and acceleration */ qreal fx = mx - m_mouse.curx; qreal fy = my - m_mouse.cury; m_mouse.acc = sqrt(fx * fx + fy * fy); if (m_mouse.acc < MIN_ACC) { return 0; } m_mouse.accx = fx / mass; m_mouse.accy = fy / mass; /* calculate new velocity */ m_mouse.velx += m_mouse.accx; m_mouse.vely += m_mouse.accy; m_mouse.vel = sqrt(m_mouse.velx * m_mouse.velx + m_mouse.vely * m_mouse.vely); m_mouse.angx = -m_mouse.vely; m_mouse.angy = m_mouse.velx; if (m_mouse.vel < MIN_VEL) { return 0; } /* calculate angle of drawing tool */ if (m_mouse.fixedangle) { m_mouse.angx = m_xangle; m_mouse.angy = m_yangle; } else { m_mouse.angx /= m_mouse.vel; m_mouse.angy /= m_mouse.vel; } m_mouse.velx = m_mouse.velx * (1.0 - drag); m_mouse.vely = m_mouse.vely * (1.0 - drag); m_mouse.lastx = m_mouse.curx; m_mouse.lasty = m_mouse.cury; m_mouse.curx = m_mouse.curx + m_mouse.velx; m_mouse.cury = m_mouse.cury + m_mouse.vely; return 1; } KoPointerEvent KisToolDyna::filterEvent(KoPointerEvent* event) { qreal wid = m_widthRange - m_mouse.vel; wid = wid * m_width; if (wid < 0.00001) { wid = 0.00001; } qreal delx = m_mouse.angx * wid; qreal dely = m_mouse.angy * wid; qreal px = m_mouse.lastx; qreal py = m_mouse.lasty; qreal nx = m_mouse.curx; qreal ny = m_mouse.cury; QPointF prev(px , py); // previous position QPointF now(nx , ny); // new position QPointF prevr(px + m_odelx , py + m_odely); QPointF prevl(px - m_odelx , py - m_odely); QPointF nowl(nx - delx , ny - dely); QPointF nowr(nx + delx , ny + dely); // transform coords from float points into image points prev.rx() *= m_surfaceWidth; prevr.rx() *= m_surfaceWidth; prevl.rx() *= m_surfaceWidth; now.rx() *= m_surfaceWidth; nowl.rx() *= m_surfaceWidth; nowr.rx() *= m_surfaceWidth; prev.ry() *= m_surfaceHeight; prevr.ry() *= m_surfaceHeight; prevl.ry() *= m_surfaceHeight; now.ry() *= m_surfaceHeight; nowl.ry() *= m_surfaceHeight; nowr.ry() *= m_surfaceHeight; #if 0 qreal xTilt, yTilt; qreal m_rotation; qreal m_tangentialPressure; // some funny debugging dbgPlugins << "m_mouse.vel: " << m_mouse.vel; dbgPlugins << "m_mouse.velx: " << m_mouse.velx; dbgPlugins << "m_mouse.vely: " << m_mouse.vely; dbgPlugins << "m_mouse.accx: " << m_mouse.accx; dbgPlugins << "m_mouse.accy: " << m_mouse.accy; dbgPlugins << "fixed: " << m_mouse.fixedangle; dbgPlugins << "drag: " << m_curdrag; dbgPlugins << "mass: " << m_curmass; dbgPlugins << "xAngle: " << m_xangle; dbgPlugins << "yAngle: " << m_yangle; #endif m_odelx = delx; m_odely = dely; // how to change pressure in the KoPointerEvent??? return KoPointerEvent(event,now); } void KisToolDyna::slotSetDrag(qreal drag) { m_curdrag = drag; m_configGroup.writeEntry("dragAmount", drag); } void KisToolDyna::slotSetMass(qreal mass) { m_curmass = mass; m_configGroup.writeEntry("massAmount", mass); } void KisToolDyna::slotSetDynaWidth(double width) { m_width = width; m_configGroup.writeEntry("initWidth", width); } void KisToolDyna::slotSetWidthRange(double widthRange) { m_widthRange = widthRange; m_configGroup.writeEntry("initWidthRange", widthRange); } void KisToolDyna::slotSetFixedAngle(bool fixedAngle) { m_mouse.fixedangle = fixedAngle; m_angleDSSBox->setEnabled(fixedAngle); m_configGroup.writeEntry("useFixedAngle", fixedAngle); } QWidget * KisToolDyna::createOptionWidget() { QWidget * optionsWidget = KisToolFreehand::createOptionWidget(); - optionsWidget->setObjectName(toolId() + "option widget"); + optionsWidget->setObjectName(toolId() + " option widget"); - m_optionLayout = new QGridLayout(optionsWidget); - Q_CHECK_PTR(m_optionLayout); + m_optionLayout = new QGridLayout(); m_optionLayout->setMargin(0); m_optionLayout->setSpacing(2); KisToolFreehand::addOptionWidgetLayout(m_optionLayout); QLabel* massLbl = new QLabel(i18n("Mass:"), optionsWidget); m_massSPBox = new KisDoubleSliderSpinBox(optionsWidget); m_massSPBox->setRange(0.0,1.0,2); connect(m_massSPBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetMass(qreal))); KisToolFreehand::addOptionWidgetOption(m_massSPBox,massLbl); QLabel* dragLbl = new QLabel(i18n("Drag:"), optionsWidget); m_dragSPBox = new KisDoubleSliderSpinBox(optionsWidget); m_dragSPBox->setRange(0.0,1.0,2); connect(m_dragSPBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetDrag(qreal))); KisToolFreehand::addOptionWidgetOption(m_dragSPBox,dragLbl); //NOTE: so far unused, waiting for the changes to propagate rotation/pressure to freehand tool // fixed angle might be for 2.4, but the later one for 2.5 m_chkFixedAngle = new QCheckBox(i18n("Fixed angle:"), optionsWidget); m_chkFixedAngle->setEnabled(false); connect(m_chkFixedAngle, SIGNAL(toggled(bool)), this, SLOT(slotSetFixedAngle(bool))); m_angleDSSBox = new KisDoubleSliderSpinBox(optionsWidget); m_angleDSSBox->setRange(0,360,0); m_angleDSSBox->setSuffix(QChar(Qt::Key_degree)); m_angleDSSBox->setEnabled(false); connect(m_angleDSSBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetAngle(qreal))); KisToolFreehand::addOptionWidgetOption(m_angleDSSBox,m_chkFixedAngle); // read settings in from config m_massSPBox->setValue(m_configGroup.readEntry("massAmount", 0.01)); m_dragSPBox->setValue(m_configGroup.readEntry("dragAmount", .98)); m_chkFixedAngle->setChecked((bool)m_configGroup.readEntry("useFixedAngle", false)); m_angleDSSBox->setValue(m_configGroup.readEntry("angleAmount", 20)); #if 0 QLabel* initWidthLbl = new QLabel(i18n("Initial width:"), optionWidget); m_initWidthSPBox = new QDoubleSpinBox(optionWidget); connect(m_initWidthSPBox, SIGNAL(valueChanged(double)), this, SLOT(slotSetDynaWidth(double))); KisToolFreehand::addOptionWidgetOption(m_initWidthSPBox,initWidthLbl); QLabel* widthRangeLbl = new QLabel(i18n("Width range:"), optionWidget); m_widthRangeSPBox = new QDoubleSpinBox(optionWidget); connect(m_widthRangeSPBox, SIGNAL(valueChanged(double)), this, SLOT(slotSetWidthRange(double))); //KisToolFreehand::addOptionWidgetOption(m_widthRangeSPBox,widthRangeLbl); m_initWidthSPBox->setValue(m_configGroup.readEntry("initWidth", 10)); m_widthRangeSPBox->setValue(m_configGroup.readEntry("initWidthRange", 20)); #endif return optionsWidget; } void KisToolDyna::slotSetAngle(qreal angle) { m_xangle = cos(angle * M_PI/180.0); m_yangle = sin(angle * M_PI/180.0); m_configGroup.writeEntry("angleAmount", angle); } diff --git a/plugins/tools/tool_dyna/kis_tool_dyna.h b/plugins/tools/tool_dyna/kis_tool_dyna.h index 8c3c7345a1..480566f5c1 100644 --- a/plugins/tools/tool_dyna/kis_tool_dyna.h +++ b/plugins/tools/tool_dyna/kis_tool_dyna.h @@ -1,171 +1,168 @@ /* * Copyright (c) 2009-2011 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_DYNA_H_ #define KIS_TOOL_DYNA_H_ #include "kis_tool_freehand.h" -#include "KoToolFactoryBase.h" +#include "KisToolPaintFactoryBase.h" #include "KoPointerEvent.h" #include #include #include #include class KisDoubleSliderSpinBox; class QCheckBox; class QGridLayout; class KoCanvasBase; class DynaFilter { public: DynaFilter() {} void init(qreal x, qreal y) { curx = x; cury = y; lastx = x; lasty = y; velx = 0.0; vely = 0.0; accx = 0.0; accy = 0.0; } ~DynaFilter() {} public: qreal curx {0.0}, cury {0.0}; qreal velx {0.0}, vely {0.0}, vel {0.0}; qreal accx {0.0}, accy {0.0}, acc {0.0}; qreal angx {0.0}, angy {0.0}; qreal mass {0.0}, drag {0.0}; qreal lastx {0.0}, lasty {0.0}; bool fixedangle {false}; }; class KisToolDyna : public KisToolFreehand { Q_OBJECT public: KisToolDyna(KoCanvasBase * canvas); ~KisToolDyna() override; QWidget * createOptionWidget() override; void activate(ToolActivation toolActivation, const QSet &shapes) override; void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; protected: void initStroke(KoPointerEvent *event) override; protected Q_SLOTS: void resetCursorStyle() override; private Q_SLOTS: void slotSetDynaWidth(double width); void slotSetMass(qreal mass); void slotSetDrag(qreal drag); void slotSetAngle(qreal angle); void slotSetWidthRange(double widthRange); void slotSetFixedAngle(bool fixedAngle); private: QGridLayout* m_optionLayout; // dyna gui QCheckBox * m_chkFixedAngle; KisDoubleSliderSpinBox * m_massSPBox; KisDoubleSliderSpinBox * m_dragSPBox; KisDoubleSliderSpinBox * m_angleDSSBox; // dyna algorithm QVector m_prevPosition; qreal m_odelx, m_odely; // mouse info QPointF m_mousePos; qreal m_surfaceWidth; qreal m_surfaceHeight; // settings variables KConfigGroup m_configGroup; qreal m_width; qreal m_curmass; qreal m_curdrag; DynaFilter m_mouse; qreal m_xangle; qreal m_yangle; qreal m_widthRange; // methods qreal flerp(qreal f0, qreal f1, qreal p) { return ((f0 *(1.0 - p)) + (f1 * p)); } void setMousePosition(const QPointF &point) { m_mousePos.setX(point.x() / m_surfaceWidth ); m_mousePos.setY(point.y() / m_surfaceHeight); } void initDyna(); int applyFilter(qreal mx, qreal my); KoPointerEvent filterEvent(KoPointerEvent * event); }; -class KisToolDynaFactory : public KoToolFactoryBase +class KisToolDynaFactory : public KisToolPaintFactoryBase { public: KisToolDynaFactory() - : KoToolFactoryBase("KritaShape/KisToolDyna") { + : KisToolPaintFactoryBase("KritaShape/KisToolDyna") { setToolTip(i18n("Dynamic Brush Tool")); - - // Temporarily setSection(TOOL_TYPE_SHAPE); setIconName(koIconNameCStr("krita_tool_dyna")); - // TODO //setShortcut(QKeySequence(Qt::Key_F)); setPriority(10); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); } ~KisToolDynaFactory() override {} - KoToolBase * createTool(KoCanvasBase *canvas) override { + KoToolBase *createTool(KoCanvasBase *canvas) override { return new KisToolDyna(canvas); } }; #endif // KIS_TOOL_DYNA_H_ diff --git a/plugins/tools/tool_lazybrush/kis_tool_lazy_brush.h b/plugins/tools/tool_lazybrush/kis_tool_lazy_brush.h index 3a1ccc749e..3935efce07 100644 --- a/plugins/tools/tool_lazybrush/kis_tool_lazy_brush.h +++ b/plugins/tools/tool_lazybrush/kis_tool_lazy_brush.h @@ -1,115 +1,115 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_LAZY_BRUSH_H_ #define KIS_TOOL_LAZY_BRUSH_H_ #include #include "kis_tool_freehand.h" -#include "KoToolFactoryBase.h" +#include "KisToolPaintFactoryBase.h" #include #include #include #include #include #include class KActionCollection; class KoCanvasBase; class KisToolLazyBrush : public KisToolFreehand { Q_OBJECT public: KisToolLazyBrush(KoCanvasBase * canvas); ~KisToolLazyBrush() override; QWidget * createOptionWidget() override; void activatePrimaryAction() override; void deactivatePrimaryAction() override; void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; void activateAlternateAction(AlternateAction action) override; void deactivateAlternateAction(AlternateAction action) override; void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override; void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override; void endAlternateAction(KoPointerEvent *event, AlternateAction action) override; void explicitUserStrokeEndRequest() override; protected Q_SLOTS: void resetCursorStyle() override; public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; private Q_SLOTS: void slotCurrentNodeChanged(KisNodeSP node); Q_SIGNALS: private: bool colorizeMaskActive() const; bool canCreateColorizeMask() const; bool shouldActivateKeyStrokes() const; void tryCreateColorizeMask(); void tryDisableKeyStrokesOnMask(); private: struct Private; const QScopedPointer m_d; }; -class KisToolLazyBrushFactory : public KoToolFactoryBase +class KisToolLazyBrushFactory : public KisToolPaintFactoryBase { public: KisToolLazyBrushFactory() - : KoToolFactoryBase("KritaShape/KisToolLazyBrush") { + : KisToolPaintFactoryBase("KritaShape/KisToolLazyBrush") { setToolTip(i18n("Colorize Mask Editing Tool")); // Temporarily setSection(TOOL_TYPE_FILL); setIconName(koIconNameCStr("krita_tool_lazybrush")); //setShortcut(QKeySequence(Qt::Key_Shift + Qt::Key_B)); setPriority(3); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); } ~KisToolLazyBrushFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolLazyBrush(canvas); } }; #endif // KIS_TOOL_LAZY_BRUSH_H_ diff --git a/plugins/tools/tool_polygon/kis_tool_polygon.h b/plugins/tools/tool_polygon/kis_tool_polygon.h index fb7a5db095..ca94c7675d 100644 --- a/plugins/tools/tool_polygon/kis_tool_polygon.h +++ b/plugins/tools/tool_polygon/kis_tool_polygon.h @@ -1,68 +1,68 @@ /* * kis_tool_polygon.h - part of Krita * * Copyright (c) 2004 Michael Thaler * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_POLYGON_H_ #define KIS_TOOL_POLYGON_H_ #include "kis_tool_shape.h" #include "flake/kis_node_shape.h" #include #include class KoCanvasBase; class KisToolPolygon : public KisToolPolylineBase { Q_OBJECT public: KisToolPolygon(KoCanvasBase *canvas); ~KisToolPolygon() override; protected: void finishPolyline(const QVector& points) override; protected Q_SLOTS: void resetCursorStyle() override; }; #include "KoToolFactoryBase.h" -class KisToolPolygonFactory : public KoToolFactoryBase +class KisToolPolygonFactory : public KisToolPolyLineFactoryBase { public: KisToolPolygonFactory() - : KoToolFactoryBase("KisToolPolygon") { + : KisToolPolyLineFactoryBase("KisToolPolygon") { setToolTip(i18n("Polygon Tool: Shift-mouseclick ends the polygon.")); setSection(TOOL_TYPE_SHAPE); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setIconName(koIconNameCStr("krita_tool_polygon")); setPriority(4); } ~KisToolPolygonFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolPolygon(canvas); } }; #endif //__KIS_TOOL_POLYGON_H__ diff --git a/plugins/tools/tool_polyline/kis_tool_polyline.h b/plugins/tools/tool_polyline/kis_tool_polyline.h index 731e3e416a..d5af3482ce 100644 --- a/plugins/tools/tool_polyline/kis_tool_polyline.h +++ b/plugins/tools/tool_polyline/kis_tool_polyline.h @@ -1,70 +1,70 @@ /* * kis_tool_polyline.h - part of Krita * * Copyright (c) 2004 Michael Thaler * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_POLYLINE_H_ #define KIS_TOOL_POLYLINE_H_ #include "kis_tool_polyline_base.h" //#include "flake/kis_node_shape.h" #include class KisToolPolyline : public KisToolPolylineBase { Q_OBJECT public: KisToolPolyline(KoCanvasBase * canvas); ~KisToolPolyline() override; protected: QWidget* createOptionWidget() override; void finishPolyline(const QVector& points) override; protected Q_SLOTS: void resetCursorStyle() override; }; #include "KoToolFactoryBase.h" -class KisToolPolylineFactory : public KoToolFactoryBase +class KisToolPolylineFactory : public KisToolPolyLineFactoryBase { public: KisToolPolylineFactory() - : KoToolFactoryBase("KisToolPolyline") { + : KisToolPolyLineFactoryBase("KisToolPolyline") { setToolTip(i18n("Polyline Tool: Shift-mouseclick ends the polyline.")); setSection(TOOL_TYPE_SHAPE); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setIconName(koIconNameCStr("polyline")); setPriority(5); } ~KisToolPolylineFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolPolyline(canvas); } }; #endif //__KIS_TOOL_POLYLINE_H__ diff --git a/plugins/tools/tool_smart_patch/kis_tool_smart_patch.h b/plugins/tools/tool_smart_patch/kis_tool_smart_patch.h index 91579a7b72..0b1ca6072b 100644 --- a/plugins/tools/tool_smart_patch/kis_tool_smart_patch.h +++ b/plugins/tools/tool_smart_patch/kis_tool_smart_patch.h @@ -1,109 +1,109 @@ /* * Copyright (c) 2017 Eugene Ingerman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_SMART_PATCH_H_ #define KIS_TOOL_SMART_PATCH_H_ #include #include #include "kis_tool_paint.h" -#include "KoToolFactoryBase.h" +#include "KisToolPaintFactoryBase.h" #include #include #include #include #include #include class KActionCollection; class KoCanvasBase; class KisPaintInformation; class KisSpacingInfomation; class KisToolSmartPatch : public KisToolPaint { Q_OBJECT public: KisToolSmartPatch(KoCanvasBase * canvas); ~KisToolSmartPatch() override; QWidget * createOptionWidget() override; void activatePrimaryAction() override; void deactivatePrimaryAction() override; void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; void paint(QPainter &painter, const KoViewConverter &converter) override; int flags() const override { return KisTool::FLAG_USES_CUSTOM_SIZE | KisTool::FLAG_USES_CUSTOM_PRESET; } protected Q_SLOTS: void resetCursorStyle() override; public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; private: //QRect inpaintImage(KisPaintDeviceSP maskDev, KisPaintDeviceSP imageDev); QPainterPath getBrushOutlinePath(const QPointF &documentPos, const KoPointerEvent *event); QPainterPath brushOutline(); void requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event) override; private: struct Private; class InpaintCommand; const QScopedPointer m_d; void addMaskPath(KoPointerEvent *event); }; -class KisToolSmartPatchFactory : public KoToolFactoryBase +class KisToolSmartPatchFactory : public KisToolPaintFactoryBase { public: KisToolSmartPatchFactory() - : KoToolFactoryBase("KritaShape/KisToolSmartPatch") + : KisToolPaintFactoryBase("KritaShape/KisToolSmartPatch") { setToolTip(i18n("Smart Patch Tool")); setSection(TOOL_TYPE_FILL); setIconName(koIconNameCStr("krita_tool_smart_patch")); setPriority(4); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); } ~KisToolSmartPatchFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolSmartPatch(canvas); } }; #endif // KIS_TOOL_SMART_PATCH_H_ diff --git a/winquirks/README b/winquirks/README index b61d70cb63..a9d65567ca 100644 --- a/winquirks/README +++ b/winquirks/README @@ -1,6 +1,6 @@ Without introducing some artificial compatibility layers, a native windows development environment (e.g. Visual Studio 2010) does not know about some includes known in the UNIX world. For this reason the winquirks/ directory shall provide a minimal -approach to such an compatibility layer. +approach to such a compatibility layer.