diff --git a/3rdparty/ext_gmic/CMakeLists.txt b/3rdparty/ext_gmic/CMakeLists.txt index 649c92594e..6194e8b6a5 100644 --- a/3rdparty/ext_gmic/CMakeLists.txt +++ b/3rdparty/ext_gmic/CMakeLists.txt @@ -1,38 +1,38 @@ SET(PREFIX_ext_gmic "${EXTPREFIX}" ) # Download the gmic sources ExternalProject_Add( ext_gmic_base DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://gmic.eu/files/source/gmic_2.1.8.tar.gz URL_HASH SHA1=9c5bc202c6e792620bdedd6f13d78cea21ef0552 SOURCE_DIR gmic CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" BUILD_IN_SOURCE 1 ) # Download and build gmic-qt # FIXME: Forcing CMAKE_BUILD_TYPE to Release ExternalProject_Add( ext_gmic_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://files.kde.org/krita/build/dependencies/gmic-qt-3868d25.tar.gz DOWNLOAD_NAME gmic-qt-3868d25.tar.gz URL_HASH SHA1=98ae2db9c45ee1c830011b4ed2574eb0839c7abe + PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/desktop_icon.diff SOURCE_DIR gmic-qt INSTALL_DIR ${PREFIX_ext_gmic} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_gmic} -DGMIC_QT_HOST=krita -DCMAKE_BUILD_TYPE=Release ${GLOBAL_PROFILE} UPDATE_COMMAND "" - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy /gmic_krita_qt${CMAKE_EXECUTABLE_SUFFIX} /bin/gmic_krita_qt${CMAKE_EXECUTABLE_SUFFIX} DEPENDS ext_gmic_base ) add_custom_target(ext_gmic) add_dependencies(ext_gmic ext_gmic_qt) diff --git a/3rdparty/ext_gmic/desktop_icon.diff b/3rdparty/ext_gmic/desktop_icon.diff new file mode 100644 index 0000000000..34ba700651 --- /dev/null +++ b/3rdparty/ext_gmic/desktop_icon.diff @@ -0,0 +1,64 @@ +commit f65b19713653c35707cf4c33c405802710fbce83 +Author: Boudewijn Rempt +Date: Thu Feb 8 13:30:47 2018 +0100 + + Install gmic and desktop file + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index c8106e2..ab071e9 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -70,10 +70,6 @@ if (NOT(${GMIC_VERSION} EQUAL ${CIMG_VERSION})) + message(FATAL_ERROR "\nVersion numbers of files 'gmic.h' (" ${GMIC_VERSION} ") and 'CImg.h' ("${CIMG_VERSION} ") mismatch") + endif() + +- +- +- +- + option(PRERELEASE "Set to ON makes this a prelease build") + if (${PRERELEASE}) + string(TIMESTAMP PRERELEASE_DATE %y%m%d) +@@ -86,8 +82,6 @@ if (${DRMINGW}) + add_definitions(-DDRMINGW) + endif() + +- +- + # Required packages + + # +@@ -416,6 +410,9 @@ elseif (${GMIC_QT_HOST} STREQUAL "krita") + ${gmic_qt_LIBRARIES} + ) + ++ install(TARGETS gmic_krita_qt RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) ++ install(FILES gmic_krita_qt.desktop gmic_krita_qt.png DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) ++ + elseif (${GMIC_QT_HOST} STREQUAL "none") + + set (gmic_qt_SRCS ${gmic_qt_SRCS} src/host_none.cpp include/standalone/ImageDialog.h src/standalone/ImageDialog.cpp) +@@ -432,3 +429,4 @@ else() + endif() + + feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) ++ +diff --git a/gmic_krita_qt.desktop b/gmic_krita_qt.desktop +new file mode 100644 +index 0000000..579d427 +--- /dev/null ++++ b/gmic_krita_qt.desktop +@@ -0,0 +1,9 @@ ++[Desktop Entry] ++Name=gmic_krita_qt ++Exec=gmic_krita_qt ++GenericName=G'Mic plugin for Krita ++Comment=G'Mic plugin for Krita ++Type=Application ++Icon=gmic_krita_qt ++Categories=Qt;KDE;Graphics; ++StartupNotify=false +diff --git a/gmic_krita_qt.png b/gmic_krita_qt.png +new file mode 100644 +index 0000000..cda8d5c +Binary files /dev/null and b/gmic_krita_qt.png differ diff --git a/README_PACKAGERS.md b/README_PACKAGERS.md index bef4cbb741..7cd8faa7e1 100644 --- a/README_PACKAGERS.md +++ b/README_PACKAGERS.md @@ -1,73 +1,71 @@ = Notes for Packagers = == Patching Qt == Qt 5.6 is currently the recommended version to build Krita with on all platforms. However, Qt 5.6 on Linux needs to be patched for https://bugreports.qt.io/browse/QTBUG-44964 . The patch in 3rdparty/ext_qt/qt-no-motion-compression.diff == Package Contents == We recommend that all of Krita packaged in one package: there is no need to split Krita up. In particular, do not make a separate package out of the plugins directory; without the plugins Krita will not even start. Krita does not install header files, so there is no need for a corresponding -dev(el) package. == Third Party Libraries == The top-level 3rd-party directory is not relevant for packaging: it only contains CMake projects for all of Krita's dependencies which are used for building Krita on Windows and OSX. It is not called from the top-level CMakeLists.txt project. There are four forks of 3rd party libraries that are relevant and cannot be replaced by system libraries: * plugins/impex/raw/3rdparty contains a fork of kdcraw. Upstread removed most functionality from this library and is in general unable to provide a stable API. The library has been renamed to avoid conflicts with upstream kdcraw. * plugins/impex/xcf/3rdparty contains the xcftools code. This has never been released as a library -* plugins/extensions/gmic/3rdparty contains G'Mic. This has never been released as a shared library. - -* libs/image/3rdparty contains einspline. This code is directly linke d into the kritaimage library and has never been released as a separate library. +* libs/image/3rdparty contains einspline. This code is directly linked into the kritaimage library and has never been released as a separate library. == Build flags == Krita no longer supports a build without OpenGL. For alpha and beta packages, please build with debug output enabled, but for production packages the -DCMAKE_CXX_FLAGS="-DKDE_NO_DEBUG_OUTPUT" is recommended. A significant performance increase will be the result. If you build Krita with RelWithDebInfo to be able to create a corresponding -dbg package, please define -DQT_NO_DEBUG=1 as well to disable asserts. == Dependencies == Krita depends on: * boost and the boost-system library * eigen3 * exiv2 * fftw3 * gsl * ilmbase * jpeg: Note that libjpeg-turbo is recommended. * lcms2 * libraw * opencolorio * openexr * png * poppler-qt5 * pthreads * qt-5: Note that Qt 5.6 is _strongly_ recommended. Qt 5.5 has bugs that interfere with proper handling of tablet events * tiff * vc: this is a build-time dependency only * zlib And the following KDE Frameworks: * Archive * Completion * Config * CoreAddons * GuiAddons * I18n * ItemModels * ItemViews * KCrash * WidgetsAddons * WindowSystem diff --git a/krita/data/actions/CMakeLists.txt b/krita/data/actions/CMakeLists.txt index 084820e55a..73f290a9cd 100644 --- a/krita/data/actions/CMakeLists.txt +++ b/krita/data/actions/CMakeLists.txt @@ -1,9 +1,8 @@ install( FILES ArtisticTextTool.action InteractionTool.action PathTool.action TextTool.action ConnectionTool.action - ReferencesTool.action MoveTool.action DESTINATION ${DATA_INSTALL_DIR}/krita/actions) diff --git a/krita/data/actions/ReferencesTool.action b/krita/data/actions/ReferencesTool.action deleted file mode 100644 index 3edc976500..0000000000 --- a/krita/data/actions/ReferencesTool.action +++ /dev/null @@ -1,596 +0,0 @@ - - - - References Tool - - - Insert footnote with auto number - - Insert footnote with auto number - Insert footnote with auto number - - false - - - - configure - Insert Custom... - - Insert a custom Table of Contents into the document. - Insert Custom - - false - - - - - Insert Labeled Footnote - - Insert Labeled Footnote - Insert Labeled Footnote - - false - - - - edit-paste - Paste - - - - - false - - - - configure - Settings... - - Settings - Settings - - false - - - - - Change text direction - - Change writing direction - Change text direction - Ctrl+Shift+D - false - - - - - Insert Paragraph between sections - - Insert Paragraph between sections - Insert Paragraph between sections - - false - - - - - Repaint - - Repaint - Repaint - - false - - - - - Insert Link - - Insert a weblink or link to a bookmark. - Insert Link - - false - - - - - Manage Bookmarks - - Manage your Bookmarks. Check where are they pointing to, Delete or Rename. - Manage Bookmarks - - false - - - - - Style Manager... - <p>Change font and paragraph attributes of styles.</p><p>Multiple styles can be changed using the dialog box.</p> - Change attributes of styles - Style Manager - Ctrl+Alt+S - false - - - - - Font... - Change the attributes of the currently selected characters. - Change character size, font, boldface, italics etc. - Font - Ctrl+Alt+F - false - - - - - Font Size - - Font Size - Font Size - - false - - - - - Text Color - - - - - false - - - - - Align Block - - Align Block - Align Block - Ctrl+Alt+R - false - - - - - Insert Bibliography - - Insert a bibliography into the document. - Insert Bibliography - - false - - - - - Insert endnote with auto number - - Insert endnote with auto number - Insert endnote with auto number - - false - - - - configure - Settings... - - Settings - Settings - - false - - - - - Bookmarks - - Display a pop up that hosts the options to add new Bookmark or handle existing Bookmarks - Bookmarks - - false - - - - - Numbered list - - Numbered list - Numbered list - - false - - - - - Subscript - - Subscript - Subscript - Ctrl+Shift+B - false - - - - - Decrease Font Size - - Decrease Font Size - Decrease Font Size - Ctrl+< - false - - - - - Insert Citation - - Insert a citation into the document. - Insert Citation - - false - - - - - Configure current section - - Configure current section - Configure current section - - false - - - - - Insert Index - - Insert Index - Insert Index - Ctrl+T - false - - - - - Italic - - Italic - Italic - Ctrl+I - false - - - - - Strikethrough - - Strikethrough - Strikethrough - - false - - - - - Insert Custom Bibliography - - Insert a custom Bibliography into the document. - Insert Custom Bibliography - - false - - - - - Styles Debug - - Styles Debug - Styles Debug - Ctrl+Alt+Shift+S - false - - - - - Bullet list - - Bullet list - Bullet list - - false - - - - - Underline - - Underline - Underline - Ctrl+U - false - - - - - Align Center - - Align Center - Align Center - Ctrl+Alt+C - false - - - - - Shrink To Fit - - Shrink To Fit - Shrink To Fit - - true - - - - - Select &All - - Select All - Select All - Ctrl+A - false - - - - - Increase Indent - - Increase Indent - Increase Indent - - false - - - - - Insert - - Insert a Table of Contents into the document. - Insert - - false - - - - - Paragraph Debug - - Paragraph Debug - Paragraph Debug - Ctrl+Alt+Shift+P - false - - - - - Insert Labeled Endnote - - Insert Labeled Endnote - Insert Labeled Endnote - - false - - - - - Paragraph... - <p>Change paragraph margins, text flow, borders, bullets, numbering etc.</p><p>Select text in multiple paragraphs to change the formatting of all selected paragraphs.</p><p>If no text is selected, the paragraph where the cursor is located will be changed.</p> - Change paragraph margins, text flow, borders, bullets, numbering etc. - Paragraph - Ctrl+Alt+P - false - - - - - Insert Non-Breaking Space - - Insert Non-Breaking Space - Insert Non-Breaking Space - Ctrl+Space - false - - - - - Insert Soft Hyphen - - Insert Soft Hyphen - Insert Soft Hyphen - - false - - - - - Align Right - - Align Right - Align Right - Ctrl+Alt+R - false - - - - - Insert Non-Breaking Hyphen - - Insert Non-Breaking Hyphen - Insert Non-Breaking Hyphen - Ctrl+Shift+- - false - - - - - Configure... - - Configure the Table of Contents - Configure - - false - - - - - Special Character... - Insert one or more symbols or characters not found on the keyboard. - Insert one or more symbols or characters not found on the keyboard - Special Character - Alt+Shift+C - false - - - - - Bold - - Bold - Bold - Ctrl+B - false - - - - - Superscript - - Superscript - Superscript - Ctrl+Shift+P - false - - - - - Configure - - Configure the bibliography - Configure - - false - - - - - Insert new section - - Insert new section - Insert new section - - false - - - - - Decrease Indent - - Decrease Indent - Decrease Indent - - false - - - - - Increase Font Size - - Increase Font Size - Increase Font Size - Ctrl+> - false - - - - - Grow To Fit Height - - Grow To Fit Height - Grow To Fit Height - - true - - - - - Align Left - - Align Left - Align Left - - false - - - - - Add Bookmark - - Insert a Bookmark. This is useful to create links that point to areas within the document - Add Bookmark - - false - - - - - Variable - - Variable - Variable - - false - - - - - Grow To Fit Width - - Grow To Fit Width - Grow To Fit Width - - true - - - - - Font Family - - Font Family - Font Family - - false - - - - - Insert Comment - - Insert Comment - Insert Comment - Ctrl+Shift+C - false - - - - - Background Color - - - - - false - - - - diff --git a/krita/data/brushes/3_brush.png b/krita/data/brushes/3_brush.png deleted file mode 100644 index 09e0719870..0000000000 Binary files a/krita/data/brushes/3_brush.png and /dev/null differ diff --git a/krita/data/brushes/3_dotted-flat.png b/krita/data/brushes/3_dotted-flat.png deleted file mode 100644 index 31267f478b..0000000000 Binary files a/krita/data/brushes/3_dotted-flat.png and /dev/null differ diff --git a/krita/data/brushes/3_eroded.gih b/krita/data/brushes/3_eroded.gih deleted file mode 100755 index 775679dfc5..0000000000 Binary files a/krita/data/brushes/3_eroded.gih and /dev/null differ diff --git a/krita/data/brushes/3_splat.png b/krita/data/brushes/3_splat.png deleted file mode 100644 index e7a5fce3e7..0000000000 Binary files a/krita/data/brushes/3_splat.png and /dev/null differ diff --git a/krita/data/brushes/A_bamboo-leaves.gih b/krita/data/brushes/A_bamboo-leaves.gih deleted file mode 100644 index 58e85fe54f..0000000000 Binary files a/krita/data/brushes/A_bamboo-leaves.gih and /dev/null differ diff --git a/krita/data/brushes/A_eroded-cercle-anim.gih b/krita/data/brushes/A_eroded-cercle-anim.gih deleted file mode 100644 index b28e28a479..0000000000 Binary files a/krita/data/brushes/A_eroded-cercle-anim.gih and /dev/null differ diff --git a/krita/data/brushes/A_eroded_circle.gih b/krita/data/brushes/A_eroded_circle.gih deleted file mode 100644 index 6e5fb3c3c3..0000000000 Binary files a/krita/data/brushes/A_eroded_circle.gih and /dev/null differ diff --git a/krita/data/brushes/A_smoke.gbr b/krita/data/brushes/A_smoke.gbr deleted file mode 100644 index e4425b289e..0000000000 Binary files a/krita/data/brushes/A_smoke.gbr and /dev/null differ diff --git a/krita/data/brushes/A_sparkle1.gbr b/krita/data/brushes/A_sparkle1.gbr deleted file mode 100644 index 42899c74ec..0000000000 Binary files a/krita/data/brushes/A_sparkle1.gbr and /dev/null differ diff --git a/krita/data/brushes/A_sparkle2.gbr b/krita/data/brushes/A_sparkle2.gbr deleted file mode 100644 index c2ea097d7a..0000000000 Binary files a/krita/data/brushes/A_sparkle2.gbr and /dev/null differ diff --git a/krita/data/brushes/A_sparkle3.gbr b/krita/data/brushes/A_sparkle3.gbr deleted file mode 100644 index ff7d27c781..0000000000 Binary files a/krita/data/brushes/A_sparkle3.gbr and /dev/null differ diff --git a/krita/data/brushes/A_splat2.gih b/krita/data/brushes/A_splat2.gih deleted file mode 100644 index 3a94572e23..0000000000 Binary files a/krita/data/brushes/A_splat2.gih and /dev/null differ diff --git a/krita/data/brushes/A_starfield2.gbr b/krita/data/brushes/A_starfield2.gbr deleted file mode 100644 index 4f7f1dcf47..0000000000 Binary files a/krita/data/brushes/A_starfield2.gbr and /dev/null differ diff --git a/krita/data/brushes/CMakeLists.txt b/krita/data/brushes/CMakeLists.txt index bb2ac97739..3cfcb1fe6f 100644 --- a/krita/data/brushes/CMakeLists.txt +++ b/krita/data/brushes/CMakeLists.txt @@ -1,52 +1,49 @@ install( FILES -3_brush.png -3_dotted-flat.png -3_eroded.gih -3_splat.png -3_texture.png -A_bamboo-leaves.gih -A_distant-mountain.gih -A_eroded-cercle-anim.gih -A_eroded_circle.gih -A_grass-floor.gih -A_pin.gih -A_smoke.gbr -A_snow-pack.gih -A_sparkle1.gbr -A_sparkle2.gbr -A_sparkle3.gbr -A_splat2.gih -A_starfield2.gbr -A_wall-texture.gih -cazu-chalk.gbr -cazu-freckles.gbr -cazu-reptile.gbr -cazu-smear-paint.gbr -cazu-spines.gbr -cazu_watercolor2.gih -double_special6.gih -dub1.gbr -dub6.gbr -Flat_textured1.gih -knife3.gbr -LJF_Water_Brush_02.gih +sparkle.gbr +abominable_snowman.png +bamboo_leaves_random.gih +brick.gih +bristles_chisel_dense.png +bristles_circle_dense.png +bristles_circle_medium.png +bristles_circle_random.gih +bristles_circle_sparse.png +bristles_circle_variable.svg +chalk_chisel.gih +chalk_chisel_random.gih +chalk.png +chalk_round_hard.png +chalk_sparse.png +chisel_bent_rough.gih +chisel_dense_smear.png +chisel_knife.svg +circle_hard_eroded.gih +freckles.png +gradient.png +graphite_grain.gih +grass.gih +grass_patch.gih +hearts.gih +leaves.png leaves-scattered.svg -M_Pipe_1.gbr -MZ_leaves.gbr -N_Grass_2.gih -oil_bristle.gbr -oil_knife.gbr -P_Graphite_Pencil_grain.gih -Rect_textured1.gih -Rect_textured2.gih -Rect_textured3.gih -round_textured1.gbr -R_Rake2.gbr -R_Rake4.gbr -rock2.gbr -S_splats_02.gih -scribbles.gbr -texture51.gbr -w_hearts.gih -z_study_pencil_2H.gih +mountains_distant.gih +oil_bristle.png +oil_knife.png +paint_splats.gih +rake_dense.png +rake_sparse.png +rock_light.gih +rock_pitted.gih +rock_scraped.gih +scales.png +scratches_rough.gih +scribbles.png +smear_paint.png +smoke.png +snow.gih +spines.png +square_rough.png +starfield.png +watercolor.gih +water_still.gih DESTINATION ${DATA_INSTALL_DIR}/krita/brushes) diff --git a/krita/data/brushes/MZ_leaves.gbr b/krita/data/brushes/MZ_leaves.gbr deleted file mode 100644 index a0623743cd..0000000000 Binary files a/krita/data/brushes/MZ_leaves.gbr and /dev/null differ diff --git a/krita/data/brushes/M_Pipe_1.gbr b/krita/data/brushes/M_Pipe_1.gbr deleted file mode 100644 index 4a03ebec58..0000000000 Binary files a/krita/data/brushes/M_Pipe_1.gbr and /dev/null differ diff --git a/krita/data/brushes/P_Graphite_Pencil_grain.gih b/krita/data/brushes/P_Graphite_Pencil_grain.gih deleted file mode 100644 index 2ac0de1121..0000000000 Binary files a/krita/data/brushes/P_Graphite_Pencil_grain.gih and /dev/null differ diff --git a/krita/data/brushes/R_Rake2.gbr b/krita/data/brushes/R_Rake2.gbr deleted file mode 100644 index e622c36773..0000000000 Binary files a/krita/data/brushes/R_Rake2.gbr and /dev/null differ diff --git a/krita/data/brushes/R_Rake4.gbr b/krita/data/brushes/R_Rake4.gbr deleted file mode 100644 index c6e835faaf..0000000000 Binary files a/krita/data/brushes/R_Rake4.gbr and /dev/null differ diff --git a/krita/data/brushes/Rect_textured2.gih b/krita/data/brushes/Rect_textured2.gih deleted file mode 100644 index bdfa2daa38..0000000000 Binary files a/krita/data/brushes/Rect_textured2.gih and /dev/null differ diff --git a/krita/data/brushes/Rect_textured3.gih b/krita/data/brushes/Rect_textured3.gih deleted file mode 100644 index 9af9d413fd..0000000000 Binary files a/krita/data/brushes/Rect_textured3.gih and /dev/null differ diff --git a/krita/data/brushes/abominable_snowman.png b/krita/data/brushes/abominable_snowman.png new file mode 100644 index 0000000000..359dc30405 Binary files /dev/null and b/krita/data/brushes/abominable_snowman.png differ diff --git a/krita/data/brushes/bamboo_leaves_random.gih b/krita/data/brushes/bamboo_leaves_random.gih new file mode 100644 index 0000000000..d311436c4e Binary files /dev/null and b/krita/data/brushes/bamboo_leaves_random.gih differ diff --git a/krita/data/brushes/A_wall-texture.gih b/krita/data/brushes/brick.gih similarity index 75% rename from krita/data/brushes/A_wall-texture.gih rename to krita/data/brushes/brick.gih index 863e3592e9..302aaa5e78 100644 Binary files a/krita/data/brushes/A_wall-texture.gih and b/krita/data/brushes/brick.gih differ diff --git a/krita/data/brushes/bristles_chisel_dense.png b/krita/data/brushes/bristles_chisel_dense.png new file mode 100644 index 0000000000..dca1303689 Binary files /dev/null and b/krita/data/brushes/bristles_chisel_dense.png differ diff --git a/krita/data/brushes/bristles_circle_dense.png b/krita/data/brushes/bristles_circle_dense.png new file mode 100644 index 0000000000..8674d60ca8 Binary files /dev/null and b/krita/data/brushes/bristles_circle_dense.png differ diff --git a/krita/data/brushes/bristles_circle_medium.png b/krita/data/brushes/bristles_circle_medium.png new file mode 100644 index 0000000000..87f7bd3374 Binary files /dev/null and b/krita/data/brushes/bristles_circle_medium.png differ diff --git a/krita/data/brushes/bristles_circle_random.gih b/krita/data/brushes/bristles_circle_random.gih new file mode 100644 index 0000000000..84988a21b4 Binary files /dev/null and b/krita/data/brushes/bristles_circle_random.gih differ diff --git a/krita/data/brushes/bristles_circle_sparse.png b/krita/data/brushes/bristles_circle_sparse.png new file mode 100644 index 0000000000..1e88002fe9 Binary files /dev/null and b/krita/data/brushes/bristles_circle_sparse.png differ diff --git a/krita/data/brushes/bristles_circle_variable.svg b/krita/data/brushes/bristles_circle_variable.svg new file mode 100644 index 0000000000..7f51a720e2 --- /dev/null +++ b/krita/data/brushes/bristles_circle_variable.svg @@ -0,0 +1,63 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/krita/data/brushes/cazu-chalk.gbr b/krita/data/brushes/cazu-chalk.gbr deleted file mode 100644 index 0864cb2570..0000000000 Binary files a/krita/data/brushes/cazu-chalk.gbr and /dev/null differ diff --git a/krita/data/brushes/cazu-freckles.gbr b/krita/data/brushes/cazu-freckles.gbr deleted file mode 100644 index ada6cc28e6..0000000000 Binary files a/krita/data/brushes/cazu-freckles.gbr and /dev/null differ diff --git a/krita/data/brushes/cazu-reptile.gbr b/krita/data/brushes/cazu-reptile.gbr deleted file mode 100644 index bc08d506d0..0000000000 Binary files a/krita/data/brushes/cazu-reptile.gbr and /dev/null differ diff --git a/krita/data/brushes/cazu-smear-paint.gbr b/krita/data/brushes/cazu-smear-paint.gbr deleted file mode 100644 index 9552e66516..0000000000 Binary files a/krita/data/brushes/cazu-smear-paint.gbr and /dev/null differ diff --git a/krita/data/brushes/cazu-spines.gbr b/krita/data/brushes/cazu-spines.gbr deleted file mode 100644 index b1640bb6c2..0000000000 Binary files a/krita/data/brushes/cazu-spines.gbr and /dev/null differ diff --git a/krita/data/brushes/chalk.png b/krita/data/brushes/chalk.png new file mode 100644 index 0000000000..dd288e5200 Binary files /dev/null and b/krita/data/brushes/chalk.png differ diff --git a/krita/data/brushes/Flat_textured1.gih b/krita/data/brushes/chalk_chisel.gih similarity index 94% rename from krita/data/brushes/Flat_textured1.gih rename to krita/data/brushes/chalk_chisel.gih index 162fc0ea7e..e4488439b7 100644 Binary files a/krita/data/brushes/Flat_textured1.gih and b/krita/data/brushes/chalk_chisel.gih differ diff --git a/krita/data/brushes/chalk_chisel_random.gih b/krita/data/brushes/chalk_chisel_random.gih new file mode 100644 index 0000000000..d9a3da5353 Binary files /dev/null and b/krita/data/brushes/chalk_chisel_random.gih differ diff --git a/krita/data/brushes/chalk_round_hard.png b/krita/data/brushes/chalk_round_hard.png new file mode 100644 index 0000000000..6b769b59a0 Binary files /dev/null and b/krita/data/brushes/chalk_round_hard.png differ diff --git a/krita/data/brushes/chalk_sparse.png b/krita/data/brushes/chalk_sparse.png new file mode 100644 index 0000000000..3331a0d8e7 Binary files /dev/null and b/krita/data/brushes/chalk_sparse.png differ diff --git a/krita/data/brushes/A_pin.gih b/krita/data/brushes/chisel_bent_rough.gih similarity index 74% copy from krita/data/brushes/A_pin.gih copy to krita/data/brushes/chisel_bent_rough.gih index b5de820a3d..ba01f77f1e 100644 Binary files a/krita/data/brushes/A_pin.gih and b/krita/data/brushes/chisel_bent_rough.gih differ diff --git a/krita/data/brushes/chisel_dense_smear.png b/krita/data/brushes/chisel_dense_smear.png new file mode 100644 index 0000000000..e4091f234a Binary files /dev/null and b/krita/data/brushes/chisel_dense_smear.png differ diff --git a/krita/data/brushes/chisel_knife.svg b/krita/data/brushes/chisel_knife.svg new file mode 100644 index 0000000000..40351cbd3f --- /dev/null +++ b/krita/data/brushes/chisel_knife.svg @@ -0,0 +1,63 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/krita/data/brushes/circle_hard_eroded.gih b/krita/data/brushes/circle_hard_eroded.gih new file mode 100644 index 0000000000..e8eae1e8be Binary files /dev/null and b/krita/data/brushes/circle_hard_eroded.gih differ diff --git a/krita/data/brushes/double_special6.gih b/krita/data/brushes/double_special6.gih deleted file mode 100644 index 5e1e7bdefe..0000000000 Binary files a/krita/data/brushes/double_special6.gih and /dev/null differ diff --git a/krita/data/brushes/dub1.gbr b/krita/data/brushes/dub1.gbr deleted file mode 100644 index 5b31354095..0000000000 Binary files a/krita/data/brushes/dub1.gbr and /dev/null differ diff --git a/krita/data/brushes/dub6.gbr b/krita/data/brushes/dub6.gbr deleted file mode 100644 index 240a5893d2..0000000000 Binary files a/krita/data/brushes/dub6.gbr and /dev/null differ diff --git a/krita/data/brushes/export.txt b/krita/data/brushes/export.txt new file mode 100644 index 0000000000..9b0a9c42df --- /dev/null +++ b/krita/data/brushes/export.txt @@ -0,0 +1,45 @@ +abominable_snowman.png +bamboo_leaves_random.gih +brick.gih +bristles_chisel_dense.png +bristles_circle_dense.png +bristles_circle_medium.png +bristles_circle_random.gih +bristles_circle_sparse.png +bristles_circle_variable.svg +chalk_chisel.gih +chalk_chisel_random.gih +chalk.png +chalk_round_hard.png +chalk_sparse.png +chisel_bent_rough.gih +chisel_dense_smear.png +chisel_knife.svg +circle_hard_eroded.gih +freckles.png +graphite_grain.gih +grass.gih +grass_patch.gih +hearts.gih +leaves.png +leaves-scattered.svg +mountains_distant.gih +oil_bristle.png +oil_knife.png +paint_splats.gih +rake_dense.png +rake_sparse.png +rock_light.gih +rock_pitted.gih +rock_scraped.gih +scales.png +scratches_rough.gih +scribbles.png +smear_paint.png +smoke.png +snow.gih +spines.png +square_rough.png +starfield.png +watercolor.gih +water_still.gih diff --git a/krita/data/brushes/freckles.png b/krita/data/brushes/freckles.png new file mode 100644 index 0000000000..63fbd770ce Binary files /dev/null and b/krita/data/brushes/freckles.png differ diff --git a/krita/data/brushes/gradient.png b/krita/data/brushes/gradient.png new file mode 100644 index 0000000000..bb252f58f8 Binary files /dev/null and b/krita/data/brushes/gradient.png differ diff --git a/krita/data/brushes/graphite_grain.gih b/krita/data/brushes/graphite_grain.gih new file mode 100644 index 0000000000..175532b552 Binary files /dev/null and b/krita/data/brushes/graphite_grain.gih differ diff --git a/krita/data/brushes/N_Grass_2.gih b/krita/data/brushes/grass.gih similarity index 72% rename from krita/data/brushes/N_Grass_2.gih rename to krita/data/brushes/grass.gih index 7714eec661..75baeca181 100644 Binary files a/krita/data/brushes/N_Grass_2.gih and b/krita/data/brushes/grass.gih differ diff --git a/krita/data/brushes/A_grass-floor.gih b/krita/data/brushes/grass_patch.gih similarity index 79% rename from krita/data/brushes/A_grass-floor.gih rename to krita/data/brushes/grass_patch.gih index 83898447f6..34987ed10d 100644 Binary files a/krita/data/brushes/A_grass-floor.gih and b/krita/data/brushes/grass_patch.gih differ diff --git a/krita/data/brushes/w_hearts.gih b/krita/data/brushes/hearts.gih similarity index 52% rename from krita/data/brushes/w_hearts.gih rename to krita/data/brushes/hearts.gih index 6b45830c52..b0a562114e 100644 Binary files a/krita/data/brushes/w_hearts.gih and b/krita/data/brushes/hearts.gih differ diff --git a/krita/data/brushes/knife3.gbr b/krita/data/brushes/knife3.gbr deleted file mode 100644 index b94ba45e53..0000000000 Binary files a/krita/data/brushes/knife3.gbr and /dev/null differ diff --git a/krita/data/brushes/leaves.png b/krita/data/brushes/leaves.png new file mode 100644 index 0000000000..8c32fb00ce Binary files /dev/null and b/krita/data/brushes/leaves.png differ diff --git a/krita/data/brushes/A_distant-mountain.gih b/krita/data/brushes/mountains_distant.gih similarity index 76% rename from krita/data/brushes/A_distant-mountain.gih rename to krita/data/brushes/mountains_distant.gih index 320f3ed222..3b598130ba 100644 Binary files a/krita/data/brushes/A_distant-mountain.gih and b/krita/data/brushes/mountains_distant.gih differ diff --git a/krita/data/brushes/oil_bristle.gbr b/krita/data/brushes/oil_bristle.gbr deleted file mode 100644 index 0c738d1b99..0000000000 Binary files a/krita/data/brushes/oil_bristle.gbr and /dev/null differ diff --git a/krita/data/brushes/oil_bristle.png b/krita/data/brushes/oil_bristle.png new file mode 100644 index 0000000000..38661fdabc Binary files /dev/null and b/krita/data/brushes/oil_bristle.png differ diff --git a/krita/data/brushes/oil_knife.gbr b/krita/data/brushes/oil_knife.gbr deleted file mode 100644 index a2cb048afa..0000000000 Binary files a/krita/data/brushes/oil_knife.gbr and /dev/null differ diff --git a/krita/data/brushes/oil_knife.png b/krita/data/brushes/oil_knife.png new file mode 100644 index 0000000000..2f4876ae34 Binary files /dev/null and b/krita/data/brushes/oil_knife.png differ diff --git a/krita/data/brushes/S_splats_02.gih b/krita/data/brushes/paint_splats.gih similarity index 74% rename from krita/data/brushes/S_splats_02.gih rename to krita/data/brushes/paint_splats.gih index 6294cd6d0d..2ffd9eea1e 100644 Binary files a/krita/data/brushes/S_splats_02.gih and b/krita/data/brushes/paint_splats.gih differ diff --git a/krita/data/brushes/rake_dense.png b/krita/data/brushes/rake_dense.png new file mode 100644 index 0000000000..0092a51c64 Binary files /dev/null and b/krita/data/brushes/rake_dense.png differ diff --git a/krita/data/brushes/rake_sparse.png b/krita/data/brushes/rake_sparse.png new file mode 100644 index 0000000000..8fea9805e2 Binary files /dev/null and b/krita/data/brushes/rake_sparse.png differ diff --git a/krita/data/brushes/rock2.gbr b/krita/data/brushes/rock2.gbr deleted file mode 100644 index 9fd98c40fe..0000000000 Binary files a/krita/data/brushes/rock2.gbr and /dev/null differ diff --git a/krita/data/brushes/Rect_textured1.gih b/krita/data/brushes/rock_light.gih similarity index 87% rename from krita/data/brushes/Rect_textured1.gih rename to krita/data/brushes/rock_light.gih index 61e54c6f34..44d365d7b1 100644 Binary files a/krita/data/brushes/Rect_textured1.gih and b/krita/data/brushes/rock_light.gih differ diff --git a/krita/data/brushes/rock_pitted.gih b/krita/data/brushes/rock_pitted.gih new file mode 100644 index 0000000000..0868203ea9 Binary files /dev/null and b/krita/data/brushes/rock_pitted.gih differ diff --git a/krita/data/brushes/rock_scraped.gih b/krita/data/brushes/rock_scraped.gih new file mode 100644 index 0000000000..7ce3b2220e Binary files /dev/null and b/krita/data/brushes/rock_scraped.gih differ diff --git a/krita/data/brushes/round_textured1.gbr b/krita/data/brushes/round_textured1.gbr deleted file mode 100644 index fcba8a189e..0000000000 Binary files a/krita/data/brushes/round_textured1.gbr and /dev/null differ diff --git a/krita/data/brushes/scales.png b/krita/data/brushes/scales.png new file mode 100644 index 0000000000..190b55cd9f Binary files /dev/null and b/krita/data/brushes/scales.png differ diff --git a/krita/data/brushes/scratches_rough.gih b/krita/data/brushes/scratches_rough.gih new file mode 100644 index 0000000000..5c3a956639 Binary files /dev/null and b/krita/data/brushes/scratches_rough.gih differ diff --git a/krita/data/brushes/scribbles.gbr b/krita/data/brushes/scribbles.gbr deleted file mode 100644 index 39c57480bb..0000000000 Binary files a/krita/data/brushes/scribbles.gbr and /dev/null differ diff --git a/krita/data/brushes/scribbles.png b/krita/data/brushes/scribbles.png new file mode 100644 index 0000000000..5b9720fac2 Binary files /dev/null and b/krita/data/brushes/scribbles.png differ diff --git a/krita/data/brushes/smear_paint.png b/krita/data/brushes/smear_paint.png new file mode 100644 index 0000000000..11ac102f37 Binary files /dev/null and b/krita/data/brushes/smear_paint.png differ diff --git a/krita/data/brushes/smoke.png b/krita/data/brushes/smoke.png new file mode 100644 index 0000000000..c5642850a9 Binary files /dev/null and b/krita/data/brushes/smoke.png differ diff --git a/krita/data/brushes/A_snow-pack.gih b/krita/data/brushes/snow.gih similarity index 82% rename from krita/data/brushes/A_snow-pack.gih rename to krita/data/brushes/snow.gih index c8a280e88a..e2f69ad9cb 100644 Binary files a/krita/data/brushes/A_snow-pack.gih and b/krita/data/brushes/snow.gih differ diff --git a/krita/data/brushes/A_pin.gih b/krita/data/brushes/sparkle.gbr similarity index 52% rename from krita/data/brushes/A_pin.gih rename to krita/data/brushes/sparkle.gbr index b5de820a3d..cc467d6c36 100644 Binary files a/krita/data/brushes/A_pin.gih and b/krita/data/brushes/sparkle.gbr differ diff --git a/krita/data/brushes/spines.png b/krita/data/brushes/spines.png new file mode 100644 index 0000000000..6586eb63c7 Binary files /dev/null and b/krita/data/brushes/spines.png differ diff --git a/krita/data/brushes/3_texture.png b/krita/data/brushes/square_rough.png similarity index 100% rename from krita/data/brushes/3_texture.png rename to krita/data/brushes/square_rough.png diff --git a/krita/data/brushes/starfield.png b/krita/data/brushes/starfield.png new file mode 100644 index 0000000000..cf372c6ca0 Binary files /dev/null and b/krita/data/brushes/starfield.png differ diff --git a/krita/data/brushes/texture51.gbr b/krita/data/brushes/texture51.gbr deleted file mode 100644 index d57a7f0747..0000000000 Binary files a/krita/data/brushes/texture51.gbr and /dev/null differ diff --git a/krita/data/brushes/LJF_Water_Brush_02.gih b/krita/data/brushes/water_still.gih similarity index 91% rename from krita/data/brushes/LJF_Water_Brush_02.gih rename to krita/data/brushes/water_still.gih index 4026e83ed9..4c29bcf4dc 100644 Binary files a/krita/data/brushes/LJF_Water_Brush_02.gih and b/krita/data/brushes/water_still.gih differ diff --git a/krita/data/brushes/cazu_watercolor2.gih b/krita/data/brushes/watercolor.gih similarity index 60% rename from krita/data/brushes/cazu_watercolor2.gih rename to krita/data/brushes/watercolor.gih index 82e1e9d1c6..1e7938693b 100644 Binary files a/krita/data/brushes/cazu_watercolor2.gih and b/krita/data/brushes/watercolor.gih differ diff --git a/krita/data/brushes/z_study_pencil_2H.gih b/krita/data/brushes/z_study_pencil_2H.gih deleted file mode 100644 index a4ed724f69..0000000000 Binary files a/krita/data/brushes/z_study_pencil_2H.gih and /dev/null differ diff --git a/krita/data/paintoppresets/Basic_Chisel_Smooth.kpp b/krita/data/paintoppresets/Basic_Chisel_Smooth.kpp index e2c79035b5..d885fbb467 100644 Binary files a/krita/data/paintoppresets/Basic_Chisel_Smooth.kpp and b/krita/data/paintoppresets/Basic_Chisel_Smooth.kpp differ diff --git a/krita/data/paintoppresets/Blender_Basic.kpp b/krita/data/paintoppresets/Blender_Basic.kpp index e84e2cdccb..96e71935cd 100644 Binary files a/krita/data/paintoppresets/Blender_Basic.kpp and b/krita/data/paintoppresets/Blender_Basic.kpp differ diff --git a/krita/data/paintoppresets/Blender_Blur.kpp b/krita/data/paintoppresets/Blender_Blur.kpp index f24252c616..e00e435781 100644 Binary files a/krita/data/paintoppresets/Blender_Blur.kpp and b/krita/data/paintoppresets/Blender_Blur.kpp differ diff --git a/krita/data/paintoppresets/Blender_Mover.kpp b/krita/data/paintoppresets/Blender_Mover.kpp index 3b51a3412a..9f86d029bc 100644 Binary files a/krita/data/paintoppresets/Blender_Mover.kpp and b/krita/data/paintoppresets/Blender_Mover.kpp differ diff --git a/krita/data/paintoppresets/Blender_Mover_Tendrils.kpp b/krita/data/paintoppresets/Blender_Mover_Tendrils.kpp new file mode 100644 index 0000000000..387778e55f Binary files /dev/null and b/krita/data/paintoppresets/Blender_Mover_Tendrils.kpp differ diff --git a/krita/data/paintoppresets/Blender_Rake.kpp b/krita/data/paintoppresets/Blender_Rake.kpp index 9505e1dbf4..8507d991b3 100644 Binary files a/krita/data/paintoppresets/Blender_Rake.kpp and b/krita/data/paintoppresets/Blender_Rake.kpp differ diff --git a/krita/data/paintoppresets/Blender_Textured.kpp b/krita/data/paintoppresets/Blender_Textured.kpp index 7b05aafd23..fb63c076ab 100644 Binary files a/krita/data/paintoppresets/Blender_Textured.kpp and b/krita/data/paintoppresets/Blender_Textured.kpp differ diff --git a/krita/data/paintoppresets/Blender_Textured_Marble.kpp b/krita/data/paintoppresets/Blender_Textured_Marble.kpp new file mode 100644 index 0000000000..4f1e890316 Binary files /dev/null and b/krita/data/paintoppresets/Blender_Textured_Marble.kpp differ diff --git a/krita/data/paintoppresets/Blender_Water.kpp b/krita/data/paintoppresets/Blender_Water.kpp index bae0c0fe42..ce430da706 100644 Binary files a/krita/data/paintoppresets/Blender_Water.kpp and b/krita/data/paintoppresets/Blender_Water.kpp differ diff --git a/krita/data/paintoppresets/Blender_Water_Sponge.kpp b/krita/data/paintoppresets/Blender_Water_Sponge.kpp new file mode 100644 index 0000000000..fe80fe85fd Binary files /dev/null and b/krita/data/paintoppresets/Blender_Water_Sponge.kpp differ diff --git a/krita/data/paintoppresets/CMakeLists.txt b/krita/data/paintoppresets/CMakeLists.txt index 06dac6e4f4..e611fe993d 100644 --- a/krita/data/paintoppresets/CMakeLists.txt +++ b/krita/data/paintoppresets/CMakeLists.txt @@ -1,79 +1,88 @@ install( FILES Airbrush_Soft.kpp Basic_Chisel_Smooth.kpp Basic_Flow.kpp Basic_tip_default.kpp Blender_Basic.kpp Blender_Blur.kpp Blender_Mover.kpp +Blender_Mover_Tendrils.kpp Blender_Rake.kpp Blender_Textured.kpp +Blender_Textured_Marble.kpp Blender_Water.kpp +Blender_Water_Sponge.kpp Clone_Tool.kpp Clone_Tool_Textured.kpp Distort_Grow.kpp Distort_Move.kpp Distort_Shrink.kpp Dry_Bristles_Eroded.kpp Dry_Bristles.kpp Dry_Bristles_Smooth.kpp Dry_Marker.kpp Dry_Textured_Creases.kpp Dry_Textured_Rough.kpp Dry_Textured_Scribbles.kpp Dry_Textured_Soft.kpp Eraser_Airbrush.kpp Eraser_Circle.kpp Eraser_Hard.kpp Eraser_Screentone.kpp Eraser_Soft.kpp Fill_Large_Quick.kpp Fill_Screentones.kpp Fill_Shape.kpp FX_Glow_Add.kpp FX_Image_Blur.kpp FX_Image_Sharpen.kpp +FX_Pixelize.kpp FX_Smoke_Particles.kpp FX_Starfield.kpp FX_Value_Burn.kpp FX_Value_Dodge.kpp +Ink_Bristle.kpp Ink_Brush.kpp Ink_Brush_Rough.kpp Ink_Fineliner.kpp +Ink_Leaky.kpp Ink_Precision.kpp Ink_Tough.kpp Normal_Map.kpp +Pencil_2B.kpp +Pencil_6B.kpp +Pencil_Chrome.kpp +Pencil_Hatch_Noisy.kpp +Pencil_Pattern_Shading.kpp +Pencil_Pattern_Tilt.kpp Pixel_Art.kpp Pixel_Art_Dithering.kpp -Sketch_2B.kpp -Sketch_6B.kpp -Sketch_Chrome.kpp -Sketch_Hatch_Noisy.kpp Spray_Smoke.kpp Spray_Watercolor.kpp Stamp_Clouds.kpp Stamp_Freckles.kpp Stamp_Grass.kpp Stamp_Grass_Patch.kpp Stamp_Hearts.kpp Stamp_Leaves.kpp Stamp_Mountains_Distant.kpp Stamp_Scales.kpp Stamp_Shoujo_Bubbles.kpp Stamp_Spines.kpp Stamp_Watercolor.kpp Stamp_Water.kpp Waterpaint_Hard_Edges.kpp Waterpaint_Soft_Edges.kpp Waterpaint_Soft.kpp Wet_Bristles.kpp Wet_Bristles_Rough.kpp +Wet_Bristles_Square.kpp Wet_Circle.kpp Wet_Soft_Tilted.kpp Wet_Smear.kpp DESTINATION ${DATA_INSTALL_DIR}/krita/paintoppresets) install( FILES kis_paintoppresets_tags.xml DESTINATION ${DATA_INSTALL_DIR}/krita/tags) diff --git a/krita/data/paintoppresets/Clone_Tool_Textured.kpp b/krita/data/paintoppresets/Clone_Tool_Textured.kpp index ff8331e31e..2661e44f1d 100644 Binary files a/krita/data/paintoppresets/Clone_Tool_Textured.kpp and b/krita/data/paintoppresets/Clone_Tool_Textured.kpp differ diff --git a/krita/data/paintoppresets/Distort_Move.kpp b/krita/data/paintoppresets/Distort_Move.kpp index 24a00152b3..4bd6c0b66d 100644 Binary files a/krita/data/paintoppresets/Distort_Move.kpp and b/krita/data/paintoppresets/Distort_Move.kpp differ diff --git a/krita/data/paintoppresets/Dry_Bristles.kpp b/krita/data/paintoppresets/Dry_Bristles.kpp index 5d68df99f2..d2c4285991 100644 Binary files a/krita/data/paintoppresets/Dry_Bristles.kpp and b/krita/data/paintoppresets/Dry_Bristles.kpp differ diff --git a/krita/data/paintoppresets/Dry_Bristles_Eroded.kpp b/krita/data/paintoppresets/Dry_Bristles_Eroded.kpp index 9682838417..4fef24342c 100644 Binary files a/krita/data/paintoppresets/Dry_Bristles_Eroded.kpp and b/krita/data/paintoppresets/Dry_Bristles_Eroded.kpp differ diff --git a/krita/data/paintoppresets/Dry_Bristles_Smooth.kpp b/krita/data/paintoppresets/Dry_Bristles_Smooth.kpp index e5b9182784..fc600edeb3 100644 Binary files a/krita/data/paintoppresets/Dry_Bristles_Smooth.kpp and b/krita/data/paintoppresets/Dry_Bristles_Smooth.kpp differ diff --git a/krita/data/paintoppresets/Dry_Marker.kpp b/krita/data/paintoppresets/Dry_Marker.kpp index cdbf998b90..347b6a424f 100644 Binary files a/krita/data/paintoppresets/Dry_Marker.kpp and b/krita/data/paintoppresets/Dry_Marker.kpp differ diff --git a/krita/data/paintoppresets/Dry_Textured_Creases.kpp b/krita/data/paintoppresets/Dry_Textured_Creases.kpp index 7eb5191f04..c3b102f46e 100644 Binary files a/krita/data/paintoppresets/Dry_Textured_Creases.kpp and b/krita/data/paintoppresets/Dry_Textured_Creases.kpp differ diff --git a/krita/data/paintoppresets/Dry_Textured_Rough.kpp b/krita/data/paintoppresets/Dry_Textured_Rough.kpp index e1de1643c3..4303cb9feb 100644 Binary files a/krita/data/paintoppresets/Dry_Textured_Rough.kpp and b/krita/data/paintoppresets/Dry_Textured_Rough.kpp differ diff --git a/krita/data/paintoppresets/Dry_Textured_Scribbles.kpp b/krita/data/paintoppresets/Dry_Textured_Scribbles.kpp index 3b438faf45..a3cb82c0f4 100644 Binary files a/krita/data/paintoppresets/Dry_Textured_Scribbles.kpp and b/krita/data/paintoppresets/Dry_Textured_Scribbles.kpp differ diff --git a/krita/data/paintoppresets/Dry_Textured_Soft.kpp b/krita/data/paintoppresets/Dry_Textured_Soft.kpp index ead00970fb..e816723c99 100644 Binary files a/krita/data/paintoppresets/Dry_Textured_Soft.kpp and b/krita/data/paintoppresets/Dry_Textured_Soft.kpp differ diff --git a/krita/data/paintoppresets/Eraser_Airbrush.kpp b/krita/data/paintoppresets/Eraser_Airbrush.kpp index 27f448e3d3..21760bbf07 100644 Binary files a/krita/data/paintoppresets/Eraser_Airbrush.kpp and b/krita/data/paintoppresets/Eraser_Airbrush.kpp differ diff --git a/krita/data/paintoppresets/Eraser_Circle.kpp b/krita/data/paintoppresets/Eraser_Circle.kpp index 0df932dd44..ad8b518296 100644 Binary files a/krita/data/paintoppresets/Eraser_Circle.kpp and b/krita/data/paintoppresets/Eraser_Circle.kpp differ diff --git a/krita/data/paintoppresets/Eraser_Hard.kpp b/krita/data/paintoppresets/Eraser_Hard.kpp index fa8fba4063..a37c650f8c 100644 Binary files a/krita/data/paintoppresets/Eraser_Hard.kpp and b/krita/data/paintoppresets/Eraser_Hard.kpp differ diff --git a/krita/data/paintoppresets/Eraser_Screentone.kpp b/krita/data/paintoppresets/Eraser_Screentone.kpp index 9c2e987707..c07de4ad06 100644 Binary files a/krita/data/paintoppresets/Eraser_Screentone.kpp and b/krita/data/paintoppresets/Eraser_Screentone.kpp differ diff --git a/krita/data/paintoppresets/Eraser_Soft.kpp b/krita/data/paintoppresets/Eraser_Soft.kpp index 6d8d06ea99..f742537146 100644 Binary files a/krita/data/paintoppresets/Eraser_Soft.kpp and b/krita/data/paintoppresets/Eraser_Soft.kpp differ diff --git a/krita/data/paintoppresets/FX_Pixelize.kpp b/krita/data/paintoppresets/FX_Pixelize.kpp new file mode 100644 index 0000000000..cccdb64228 Binary files /dev/null and b/krita/data/paintoppresets/FX_Pixelize.kpp differ diff --git a/krita/data/paintoppresets/Fill_Large_Quick.kpp b/krita/data/paintoppresets/Fill_Large_Quick.kpp index dcaf73bb3e..29db1ed54a 100644 Binary files a/krita/data/paintoppresets/Fill_Large_Quick.kpp and b/krita/data/paintoppresets/Fill_Large_Quick.kpp differ diff --git a/krita/data/paintoppresets/Fill_Screentones.kpp b/krita/data/paintoppresets/Fill_Screentones.kpp index 1b60f536e4..8bf47ddd08 100644 Binary files a/krita/data/paintoppresets/Fill_Screentones.kpp and b/krita/data/paintoppresets/Fill_Screentones.kpp differ diff --git a/krita/data/paintoppresets/Fill_Shape.kpp b/krita/data/paintoppresets/Fill_Shape.kpp index ca20c785de..68e869f4e5 100644 Binary files a/krita/data/paintoppresets/Fill_Shape.kpp and b/krita/data/paintoppresets/Fill_Shape.kpp differ diff --git a/krita/data/paintoppresets/Ink_Bristle.kpp b/krita/data/paintoppresets/Ink_Bristle.kpp new file mode 100644 index 0000000000..10d88990e8 Binary files /dev/null and b/krita/data/paintoppresets/Ink_Bristle.kpp differ diff --git a/krita/data/paintoppresets/Ink_Leaky.kpp b/krita/data/paintoppresets/Ink_Leaky.kpp new file mode 100644 index 0000000000..6b13750c0a Binary files /dev/null and b/krita/data/paintoppresets/Ink_Leaky.kpp differ diff --git a/krita/data/paintoppresets/Pencil_2B.kpp b/krita/data/paintoppresets/Pencil_2B.kpp new file mode 100644 index 0000000000..daff376e4b Binary files /dev/null and b/krita/data/paintoppresets/Pencil_2B.kpp differ diff --git a/krita/data/paintoppresets/Pencil_6B.kpp b/krita/data/paintoppresets/Pencil_6B.kpp new file mode 100644 index 0000000000..dbf80cf83b Binary files /dev/null and b/krita/data/paintoppresets/Pencil_6B.kpp differ diff --git a/krita/data/paintoppresets/Pencil_Chrome.kpp b/krita/data/paintoppresets/Pencil_Chrome.kpp new file mode 100644 index 0000000000..eace9a7ed7 Binary files /dev/null and b/krita/data/paintoppresets/Pencil_Chrome.kpp differ diff --git a/krita/data/paintoppresets/Pencil_Hatch_Noisy.kpp b/krita/data/paintoppresets/Pencil_Hatch_Noisy.kpp new file mode 100644 index 0000000000..af4283695f Binary files /dev/null and b/krita/data/paintoppresets/Pencil_Hatch_Noisy.kpp differ diff --git a/krita/data/paintoppresets/Pencil_Pattern_Shading.kpp b/krita/data/paintoppresets/Pencil_Pattern_Shading.kpp new file mode 100644 index 0000000000..eb5bf3335a Binary files /dev/null and b/krita/data/paintoppresets/Pencil_Pattern_Shading.kpp differ diff --git a/krita/data/paintoppresets/Pencil_Pattern_Tilt.kpp b/krita/data/paintoppresets/Pencil_Pattern_Tilt.kpp new file mode 100644 index 0000000000..64036a1e52 Binary files /dev/null and b/krita/data/paintoppresets/Pencil_Pattern_Tilt.kpp differ diff --git a/krita/data/paintoppresets/Pixel_Art.kpp b/krita/data/paintoppresets/Pixel_Art.kpp index 8da16dc0db..563293a31c 100644 Binary files a/krita/data/paintoppresets/Pixel_Art.kpp and b/krita/data/paintoppresets/Pixel_Art.kpp differ diff --git a/krita/data/paintoppresets/Pixel_Art_Dithering.kpp b/krita/data/paintoppresets/Pixel_Art_Dithering.kpp index e737d6e56b..1ff83b628c 100644 Binary files a/krita/data/paintoppresets/Pixel_Art_Dithering.kpp and b/krita/data/paintoppresets/Pixel_Art_Dithering.kpp differ diff --git a/krita/data/paintoppresets/Sketch_2B.kpp b/krita/data/paintoppresets/Sketch_2B.kpp deleted file mode 100644 index 810917be26..0000000000 Binary files a/krita/data/paintoppresets/Sketch_2B.kpp and /dev/null differ diff --git a/krita/data/paintoppresets/Sketch_6B.kpp b/krita/data/paintoppresets/Sketch_6B.kpp deleted file mode 100644 index 646e82fb9b..0000000000 Binary files a/krita/data/paintoppresets/Sketch_6B.kpp and /dev/null differ diff --git a/krita/data/paintoppresets/Sketch_Chrome.kpp b/krita/data/paintoppresets/Sketch_Chrome.kpp deleted file mode 100644 index 42901e5cb2..0000000000 Binary files a/krita/data/paintoppresets/Sketch_Chrome.kpp and /dev/null differ diff --git a/krita/data/paintoppresets/Sketch_Hatch_Noisy.kpp b/krita/data/paintoppresets/Sketch_Hatch_Noisy.kpp deleted file mode 100644 index ed18831557..0000000000 Binary files a/krita/data/paintoppresets/Sketch_Hatch_Noisy.kpp and /dev/null differ diff --git a/krita/data/paintoppresets/Stamp_Clouds.kpp b/krita/data/paintoppresets/Stamp_Clouds.kpp index 1f127f7f38..9a2db3f710 100644 Binary files a/krita/data/paintoppresets/Stamp_Clouds.kpp and b/krita/data/paintoppresets/Stamp_Clouds.kpp differ diff --git a/krita/data/paintoppresets/Stamp_Freckles.kpp b/krita/data/paintoppresets/Stamp_Freckles.kpp index 9d1ca4fff5..293c5d4f93 100644 Binary files a/krita/data/paintoppresets/Stamp_Freckles.kpp and b/krita/data/paintoppresets/Stamp_Freckles.kpp differ diff --git a/krita/data/paintoppresets/Stamp_Grass.kpp b/krita/data/paintoppresets/Stamp_Grass.kpp index 9d54fbdeeb..daab7324d8 100644 Binary files a/krita/data/paintoppresets/Stamp_Grass.kpp and b/krita/data/paintoppresets/Stamp_Grass.kpp differ diff --git a/krita/data/paintoppresets/Stamp_Grass_Patch.kpp b/krita/data/paintoppresets/Stamp_Grass_Patch.kpp index b7be94542c..78bc8ae1ed 100644 Binary files a/krita/data/paintoppresets/Stamp_Grass_Patch.kpp and b/krita/data/paintoppresets/Stamp_Grass_Patch.kpp differ diff --git a/krita/data/paintoppresets/Stamp_Hearts.kpp b/krita/data/paintoppresets/Stamp_Hearts.kpp index 1459a407b4..22badf4b5c 100644 Binary files a/krita/data/paintoppresets/Stamp_Hearts.kpp and b/krita/data/paintoppresets/Stamp_Hearts.kpp differ diff --git a/krita/data/paintoppresets/Stamp_Mountains_Distant.kpp b/krita/data/paintoppresets/Stamp_Mountains_Distant.kpp index a76d711b81..28d752e5a4 100644 Binary files a/krita/data/paintoppresets/Stamp_Mountains_Distant.kpp and b/krita/data/paintoppresets/Stamp_Mountains_Distant.kpp differ diff --git a/krita/data/paintoppresets/Stamp_Scales.kpp b/krita/data/paintoppresets/Stamp_Scales.kpp index 488c6b66b1..35514e0f2d 100644 Binary files a/krita/data/paintoppresets/Stamp_Scales.kpp and b/krita/data/paintoppresets/Stamp_Scales.kpp differ diff --git a/krita/data/paintoppresets/Stamp_Spines.kpp b/krita/data/paintoppresets/Stamp_Spines.kpp index aea4e539e1..c870fe4605 100644 Binary files a/krita/data/paintoppresets/Stamp_Spines.kpp and b/krita/data/paintoppresets/Stamp_Spines.kpp differ diff --git a/krita/data/paintoppresets/Stamp_Water.kpp b/krita/data/paintoppresets/Stamp_Water.kpp index 2a9bdecc07..6dae2a9ba7 100644 Binary files a/krita/data/paintoppresets/Stamp_Water.kpp and b/krita/data/paintoppresets/Stamp_Water.kpp differ diff --git a/krita/data/paintoppresets/Stamp_Watercolor.kpp b/krita/data/paintoppresets/Stamp_Watercolor.kpp index 2ffccd766d..d7c9d325b8 100644 Binary files a/krita/data/paintoppresets/Stamp_Watercolor.kpp and b/krita/data/paintoppresets/Stamp_Watercolor.kpp differ diff --git a/krita/data/paintoppresets/Waterpaint_Hard_Edges.kpp b/krita/data/paintoppresets/Waterpaint_Hard_Edges.kpp index 379b5f5ecf..5a66252cf5 100644 Binary files a/krita/data/paintoppresets/Waterpaint_Hard_Edges.kpp and b/krita/data/paintoppresets/Waterpaint_Hard_Edges.kpp differ diff --git a/krita/data/paintoppresets/Waterpaint_Soft.kpp b/krita/data/paintoppresets/Waterpaint_Soft.kpp index ecc8dd5afe..f0d9e7cb81 100644 Binary files a/krita/data/paintoppresets/Waterpaint_Soft.kpp and b/krita/data/paintoppresets/Waterpaint_Soft.kpp differ diff --git a/krita/data/paintoppresets/Waterpaint_Soft_Edges.kpp b/krita/data/paintoppresets/Waterpaint_Soft_Edges.kpp index 6b01f2e9d3..62f60c6909 100644 Binary files a/krita/data/paintoppresets/Waterpaint_Soft_Edges.kpp and b/krita/data/paintoppresets/Waterpaint_Soft_Edges.kpp differ diff --git a/krita/data/paintoppresets/Wet_Bristles.kpp b/krita/data/paintoppresets/Wet_Bristles.kpp index d3cb8b0ada..fb3fbc253d 100644 Binary files a/krita/data/paintoppresets/Wet_Bristles.kpp and b/krita/data/paintoppresets/Wet_Bristles.kpp differ diff --git a/krita/data/paintoppresets/Wet_Bristles_Rough.kpp b/krita/data/paintoppresets/Wet_Bristles_Rough.kpp index c3de6f8cd7..6e986cea85 100644 Binary files a/krita/data/paintoppresets/Wet_Bristles_Rough.kpp and b/krita/data/paintoppresets/Wet_Bristles_Rough.kpp differ diff --git a/krita/data/paintoppresets/Wet_Bristles_Square.kpp b/krita/data/paintoppresets/Wet_Bristles_Square.kpp new file mode 100644 index 0000000000..009ae92a2f Binary files /dev/null and b/krita/data/paintoppresets/Wet_Bristles_Square.kpp differ diff --git a/krita/data/paintoppresets/Wet_Circle.kpp b/krita/data/paintoppresets/Wet_Circle.kpp index dba34ae3a5..28f43f3fd4 100644 Binary files a/krita/data/paintoppresets/Wet_Circle.kpp and b/krita/data/paintoppresets/Wet_Circle.kpp differ diff --git a/krita/data/paintoppresets/Wet_Smear.kpp b/krita/data/paintoppresets/Wet_Smear.kpp index 5708462ce1..61a7ca5432 100644 Binary files a/krita/data/paintoppresets/Wet_Smear.kpp and b/krita/data/paintoppresets/Wet_Smear.kpp differ diff --git a/krita/data/paintoppresets/Wet_Soft_Tilted.kpp b/krita/data/paintoppresets/Wet_Soft_Tilted.kpp index c19deda7ce..a6701980fc 100644 Binary files a/krita/data/paintoppresets/Wet_Soft_Tilted.kpp and b/krita/data/paintoppresets/Wet_Soft_Tilted.kpp differ diff --git a/krita/data/preset_icons/background.png b/krita/data/preset_icons/background.png index e52baae2e2..2338ac0ff0 100644 Binary files a/krita/data/preset_icons/background.png and b/krita/data/preset_icons/background.png differ diff --git a/krita/data/preset_icons/emblem_icons/CMakeLists.txt b/krita/data/preset_icons/emblem_icons/CMakeLists.txt index caf9495977..b302d769b7 100644 --- a/krita/data/preset_icons/emblem_icons/CMakeLists.txt +++ b/krita/data/preset_icons/emblem_icons/CMakeLists.txt @@ -1,15 +1,22 @@ ########### install files ############### install(FILES color.png +emblem_tilt.png +emblem_angle_10.png +emblem_angle_15.png emblem_angle_30.png +emblem_angle_45.png +emblem_angle_60.png +emblem_angle_75.png +emblem_angle_90.png experimental.png favorite.png love.png mechanical.png multidirection.png sketch.png wet.png whirlpool.png DESTINATION ${DATA_INSTALL_DIR}/krita/preset_icons/emblem_icons) diff --git a/krita/data/preset_icons/emblem_icons/emblem_angle_10.png b/krita/data/preset_icons/emblem_icons/emblem_angle_10.png new file mode 100644 index 0000000000..704df1352e Binary files /dev/null and b/krita/data/preset_icons/emblem_icons/emblem_angle_10.png differ diff --git a/krita/data/preset_icons/emblem_icons/emblem_angle_15.png b/krita/data/preset_icons/emblem_icons/emblem_angle_15.png new file mode 100644 index 0000000000..c858116675 Binary files /dev/null and b/krita/data/preset_icons/emblem_icons/emblem_angle_15.png differ diff --git a/krita/data/preset_icons/emblem_icons/emblem_angle_30.png b/krita/data/preset_icons/emblem_icons/emblem_angle_30.png index b984492a52..223db5b296 100644 Binary files a/krita/data/preset_icons/emblem_icons/emblem_angle_30.png and b/krita/data/preset_icons/emblem_icons/emblem_angle_30.png differ diff --git a/krita/data/preset_icons/emblem_icons/emblem_angle_45.png b/krita/data/preset_icons/emblem_icons/emblem_angle_45.png new file mode 100644 index 0000000000..8e4dc6c730 Binary files /dev/null and b/krita/data/preset_icons/emblem_icons/emblem_angle_45.png differ diff --git a/krita/data/preset_icons/emblem_icons/emblem_angle_60.png b/krita/data/preset_icons/emblem_icons/emblem_angle_60.png new file mode 100644 index 0000000000..5553741d55 Binary files /dev/null and b/krita/data/preset_icons/emblem_icons/emblem_angle_60.png differ diff --git a/krita/data/preset_icons/emblem_icons/emblem_angle_75.png b/krita/data/preset_icons/emblem_icons/emblem_angle_75.png new file mode 100644 index 0000000000..b54eb1c986 Binary files /dev/null and b/krita/data/preset_icons/emblem_icons/emblem_angle_75.png differ diff --git a/krita/data/preset_icons/emblem_icons/emblem_angle_90.png b/krita/data/preset_icons/emblem_icons/emblem_angle_90.png new file mode 100644 index 0000000000..7adb5dc6ef Binary files /dev/null and b/krita/data/preset_icons/emblem_icons/emblem_angle_90.png differ diff --git a/krita/data/preset_icons/emblem_icons/emblem_tilt.png b/krita/data/preset_icons/emblem_icons/emblem_tilt.png new file mode 100644 index 0000000000..9a7a4626af Binary files /dev/null and b/krita/data/preset_icons/emblem_icons/emblem_tilt.png differ diff --git a/krita/data/preset_icons/tool_icons/CMakeLists.txt b/krita/data/preset_icons/tool_icons/CMakeLists.txt index 46bbcb788b..1454a8d204 100644 --- a/krita/data/preset_icons/tool_icons/CMakeLists.txt +++ b/krita/data/preset_icons/tool_icons/CMakeLists.txt @@ -1,77 +1,78 @@ ########### install files ############### install(FILES acrylic-angled.png acrylic-fan-used.png acrylic-flat-old.png acrylic-flat.png acrylic-mop.png acrylic-rounded-used.png adjustement-contrast.png adjustement-dark.png airbrush.png blending-stump.png blur_soft.png charcoal-block.png charcoal-pencil-large.png clone.png color-effect.png colored-pencil.png deform.png eraser-large.png experimental.png gel-pen.png +grid.png ink-brush.png ink-G-pen.png inking_brush_a.png inking_brush_blurry_a.png inking_dynamic_pen_a.png inking_dynamic_pen_small_a.png inking_dynamic_pen_though_a.png ink-pen-atom.png inking_brush.png inking_brush_blurry.png inking_dynamic_pen.png inking_dynamic_pen_though.png inking_fine-line_medium.png inking_fine-line_tiny.png inking_pen_bumpy.png inking_pen_bumpy_though.png inking_pen_calligraphic.png kneaded-eraser.png knife.png large-rounded-cut.png marker-old.png marker.png mechanical-eraser.png oil-filbert.png oil-flat-large.png oil-round-hard.png paint-roller.png pencil-black-stone.png pencil-double-ring.png rigger-brush.png round_marker_detail.png sketching_wide_47.png sketching_wide_bristles.png special-effect-used-brush.png stamp.png stylus-colored-ring.png stylus-white.png stylus.png synthetic-filbert.png synthetic-large-bright-used.png synthetic-large-bright.png synthetic-medium.png synthetic-thin.png synthetic-very-old.png technical-pen.png thin-stylus-tablet.png wand.png watercolor-brush-medium.png watercolor-flat.png wide_brush_big.png wide_brush_blurry.png wide_dull_round.png DESTINATION ${DATA_INSTALL_DIR}/krita/preset_icons/tool_icons) diff --git a/krita/data/preset_icons/tool_icons/grid.png b/krita/data/preset_icons/tool_icons/grid.png new file mode 100644 index 0000000000..c7f2cee7d2 Binary files /dev/null and b/krita/data/preset_icons/tool_icons/grid.png differ diff --git a/krita/data/templates/animation/.directory b/krita/data/templates/animation/.directory index 11a1ac8e02..0e5995b3e8 100644 --- a/krita/data/templates/animation/.directory +++ b/krita/data/templates/animation/.directory @@ -1,23 +1,24 @@ [Desktop Entry] Name=Animation Templates +Name[ar]=قوالب الحركات Name[ca]=Plantilles d'animació Name[ca@valencia]=Plantilles d'animació Name[cs]=Šablony animací: Name[de]=Animations-Vorlagen Name[el]=Πρότυπα εφέ κίνησης Name[en_GB]=Animation Templates Name[es]=Plantillas de animación Name[gl]=Modelos de animación Name[is]=Sniðmát fyrir hreyfimyndir Name[it]=Modelli di animazioni Name[nl]=Animatiesjablonen Name[pl]=Szablony animacji Name[pt]=Modelos de Animações Name[pt_BR]=Modelos de animação Name[sv]=Animeringsmallar Name[tr]=Canlandırma Şablonları Name[uk]=Шаблони анімацій Name[x-test]=xxAnimation Templatesxx Name[zh_CN]=动画模板 Name[zh_TW]=動畫範本 X-KDE-DefaultTab=true diff --git a/krita/data/templates/animation/Anim-Jp-EN.desktop b/krita/data/templates/animation/Anim-Jp-EN.desktop index 9afb1736cf..b2e1294130 100644 --- a/krita/data/templates/animation/Anim-Jp-EN.desktop +++ b/krita/data/templates/animation/Anim-Jp-EN.desktop @@ -1,28 +1,29 @@ [Desktop Entry] Type=Link URL=.source/Anim-Jp-EN.kra Icon=template_animation Name=Animation-Japanese-En +Name[ar]=حركة يابانيّة (إنجليزيّ) Name[ca]=Animació-Japonès-EN Name[ca@valencia]=Animació-Japonés-EN Name[de]=Animation-Japanisch-En Name[el]=Εφέ-κίνησης-Ιαπωνικό-En Name[en_GB]=Animation-Japanese-En Name[es]=Animación-Japonés-En Name[et]=Animation-Japanese-En Name[gl]=Animación-xaponesa-en-inglés Name[is]=Hreyfimynd-Japanska-En Name[it]=Animazione-Giapponese-EN Name[ja]=日本式アニメ(英語版) Name[nl]=Animatie-Japans-En Name[pl]=Animacja-Japońska-En Name[pt]=Animação-Japonês-EN Name[pt_BR]=Animation-Japanese-En Name[ru]=Анимация-японская-англ Name[sk]=Animation-Japanese-En Name[sv]=Animering-japanska-en Name[tr]=Canlandırma-Japonca-İngilizce Name[uk]=Японська анімація (англійською) Name[x-test]=xxAnimation-Japanese-Enxx Name[zh_CN]=日本动画 (英式) Name[zh_TW]=動畫-Japanese-En diff --git a/krita/data/templates/animation/Anim-Jp-JP.desktop b/krita/data/templates/animation/Anim-Jp-JP.desktop index e1be657423..404250f4e1 100644 --- a/krita/data/templates/animation/Anim-Jp-JP.desktop +++ b/krita/data/templates/animation/Anim-Jp-JP.desktop @@ -1,28 +1,29 @@ [Desktop Entry] Type=Link URL=.source/Anim-Jp-JP.kra Icon=template_animation Name=Animation-Japanese-JP +Name[ar]=حركة يابانيّة (يابانيّ) Name[ca]=Animació-Japonès-JP Name[ca@valencia]=Animació-Japonés-JP Name[de]=Animation-Japanisch-JP Name[el]=Εφέ-κίνησης-Ιαπωνικό-JP Name[en_GB]=Animation-Japanese-JP Name[es]=Animación-Japonés-JP Name[et]=Animation-Japanese-JP Name[gl]=Animación-xaponesa-en-xaponés Name[is]=Hreyfimynd-Japanska-JP Name[it]=Animazione-Giapponese-JP Name[ja]=日本式アニメ(日本語版) Name[nl]=Animatie-Japans-JP Name[pl]=Animacja-Japońska-JP Name[pt]=Animação-Japonês-JP Name[pt_BR]=Animation-Japanese-JP Name[ru]=Анимация-японская-японск Name[sk]=Animation-Japanese-JP Name[sv]=Animering-japanska-jp Name[tr]=Canlandırma-Japonca-JP Name[uk]=Японська анімація (японською) Name[x-test]=xxAnimation-Japanese-JPxx Name[zh_CN]=日本动画 (日式) Name[zh_TW]=動畫-Japanese-JP diff --git a/krita/data/templates/comics/.directory b/krita/data/templates/comics/.directory index 8798d03e12..cee998feb6 100644 --- a/krita/data/templates/comics/.directory +++ b/krita/data/templates/comics/.directory @@ -1,41 +1,42 @@ [Desktop Entry] Name=Comic Templates +Name[ar]=قوالب الهزليّات Name[bs]=Predlošci stripova Name[ca]=Plantilles per a còmics Name[ca@valencia]=Plantilles per a còmics Name[cs]=Šablony komixů Name[da]=Tegneserieskabeloner Name[de]=Comic-Vorlagen Name[el]=Πρότυπα κόμικ Name[en_GB]=Comic Templates Name[es]=Plantillas de cómic Name[et]=Koomiksimallid Name[eu]=Komiki-txantiloiak Name[fi]=Sarjakuvapohjat Name[fr]=Modèles de bandes dessinées Name[gl]=Modelos de banda deseñada Name[hu]=Képregénysablonok Name[ia]=Patronos de Comic Name[is]=Comic sniðmát Name[it]=Modelli di fumetti Name[ja]=コミックテンプレート Name[kk]=Комикс үлгілері Name[ko]=만화 서식 Name[lt]=Komiksų šablonai Name[nb]=Tegneseriemaler Name[nds]=Comic-Vörlagen Name[nl]=Stripverhaalsjabloon Name[pl]=Szablony komiksów Name[pt]=Modelos de Banda Desenhada Name[pt_BR]=Modelos de quadrinhos Name[ru]=Шаблоны комиксов Name[sk]=Komixové šablóny Name[sl]=Predloge za stripe Name[sv]=Seriemallar Name[tr]=Çizgi Roman Şablonu Name[uk]=Шаблони коміксів Name[wa]=Modeles di bindes d' imådjes Name[x-test]=xxComic Templatesxx Name[zh_CN]=漫画模板 Name[zh_TW]=漫畫範本 X-KDE-DefaultTab=true diff --git a/krita/data/templates/comics/Comics-USTemplate.desktop b/krita/data/templates/comics/Comics-USTemplate.desktop index 4a37d609d3..d6321c9353 100644 --- a/krita/data/templates/comics/Comics-USTemplate.desktop +++ b/krita/data/templates/comics/Comics-USTemplate.desktop @@ -1,80 +1,82 @@ [Desktop Entry] Type=Link URL=.source/Comics-USTemplate.kra Icon=template_comics_empty Name=US-style comics template +Name[ar]=قالب هزليّات بنمط أمريكيّ Name[bs]=Američki strip predložak Name[ca]=plantilla de còmics d'estil americà Name[ca@valencia]=plantilla de còmics d'estil americà Name[cs]=Šablona komixu v americkém stylu Name[da]=Tegneserieskabelon i amerikansk stil Name[de]=US-Design-Comicvorlage Name[el]=Πρότυπο κόμικς US-style Name[en_GB]=US-style comics template Name[es]=plantilla de cómic de estilo estadounidense Name[et]=USA stiilis koomiksi mall Name[eu]=AEBko estiloko komiki-txantiloia Name[fi]=Yhdysvaltalaistyylinen sarjakuvapohja Name[fr]=Modèle US de bande dessinée Name[gl]=Formato estadounidense (2×3 viñetas) Name[hu]=US-stílusú képregénysablon Name[is]=Bandarískt teiknimyndasögusniðmát Name[it]=Modello per fumetti in stile americano Name[ja]=アメリカ式コミックテンプレート Name[kk]=АҚШ-стильді комикс үлгісі Name[ko]=미국식 만화 서식 Name[lt]=JAV stiliaus komiksų šablonas Name[nb]=Tegneseriemal i USA-stil Name[nds]=Amerikaansch Comicvörlaag Name[nl]=sjabloon voor strips in US-stijl Name[pl]=Szablon komiksów Amerykańskiego stylu Name[pt]=Modelo de banda desenhada dos EUA Name[pt_BR]=Modelo de quadrinhos no estilo americano Name[ru]=Шаблон в американском стиле Name[sk]=šablóna pre americké komixy Name[sl]=Predloga za stripe v ameriškem slogu Name[sv]=Seriemall med amerikansk stil Name[tr]=US tarzı çizgi roman şablonu Name[uk]=Шаблон коміксів у американському стилі Name[wa]=Modele comics a l' amerikinnes Name[x-test]=xxUS-style comics templatexx Name[zh_CN]=美式漫画模板 Name[zh_TW]=美式漫畫範本 Comment=template for US-style comics +Comment[ar]=قالب للهزليّات بالنّمط الأمريكيّ Comment[bs]=predložak za stripove američkog stila Comment[ca]=plantilla per a còmics d'estil americà Comment[ca@valencia]=plantilla per a còmics d'estil americà Comment[cs]=šablona pro komiksy v americkém stylu Comment[da]=skabelon til tegneserier i amerikansk stil Comment[de]=Vorlage für Comics im US-Stil Comment[el]=πρότυπο για US-style κόμικς Comment[en_GB]=template for US-style comics Comment[es]=plantilla para cómics de estilo estadounidense Comment[et]=USA stiilis koomiksi mall Comment[eu]=AEBko estiloko komikietarako txantiloia Comment[fi]=yhdysvaltalaistyylisen sarjakuvan pohja Comment[fr]=Modèle US de bandes dessinées Comment[gl]=Páxina de banda deseñada de formato estadounidense, con 2×3 viñetas regulares. Comment[hu]=sablon a US-stílusú képregényekhez Comment[is]=Sniðmát fyrir bandarískar comics-teiknimyndir Comment[it]=modello per fumetti in stile americano Comment[ja]=アメリカ式コミック用テンプレート Comment[kk]=АҚШ-стильдегі комикс үлгісі Comment[ko]=미국식 만화 서식 Comment[nb]=mal for tegneserier i US-stil Comment[nds]=Vörlaag för amerikaansche Comics Comment[nl]=sjabloon voor strips in US-stijl Comment[pl]=szablon dla Amerykańskiego stylu komiksów Comment[pt]=modelo de banda desenhada do estilo dos EUA Comment[pt_BR]=Modelo de quadrinhos no estilo americano Comment[ru]=Шаблон комиксов в американском стиле Comment[sk]=šablóna pre americké komixy Comment[sl]=predloga za stripe v ameriškem slogu Comment[sv]=seriemall med amerikansk stil Comment[tr]=US tarzı çizgi romanlar için şablon Comment[uk]=шаблон для коміксів у американському стилі Comment[wa]=Modele di bindes d' imådje al môde des comics amerikins Comment[x-test]=xxtemplate for US-style comicsxx Comment[zh_CN]=美式漫画模板 Comment[zh_TW]=US-風格的連環漫畫範本 X-Krita-Version=28 diff --git a/krita/data/templates/comics/Manga-JpTemplate.desktop b/krita/data/templates/comics/Manga-JpTemplate.desktop index 71e8f73935..ea19a697f5 100644 --- a/krita/data/templates/comics/Manga-JpTemplate.desktop +++ b/krita/data/templates/comics/Manga-JpTemplate.desktop @@ -1,81 +1,83 @@ [Desktop Entry] Type=Link URL=.source/Manga-JpTemplate.kra Icon=template_comics_empty Name=Manga template +Name[ar]=قالب مانغا Name[bs]=Manga predložak Name[ca]=Plantilla per a manga Name[ca@valencia]=Plantilla per a manga Name[cs]=Šablona Mangy Name[da]=Manga-skabelon Name[de]=Manga-Vorlage Name[el]=Πρότυπο μάνγκα Name[en_GB]=Manga template Name[es]=Plantilla manga Name[et]=Manga mall Name[eu]=Manga-txantiloia Name[fi]=Mangapohja Name[fr]=Modèle de Manga Name[gl]=Formato Manga Name[hu]=Manga sablon Name[ia]=Patrono de Manga Name[is]=Manga sniðmát Name[it]=Modello manga Name[ja]=漫画テンプレート Name[kk]=Үлгіні басқару Name[ko]=일본식 만화 서식 Name[lt]=Manga šablonas Name[nb]=Manga-mal Name[nds]=Manga-Vörlaag Name[nl]=Manga-sjabloon Name[pl]=Szablon Mangi Name[pt]=Modelo Manga Name[pt_BR]=Modelo de mangá Name[ru]=Шаблон манги Name[sk]=Manga šablóna Name[sl]=Predloga Manga Name[sv]=Manga-mall Name[tr]=Manga şablonu Name[uk]=Шаблон манґи Name[wa]=Modele di manga Name[x-test]=xxManga templatexx Name[zh_CN]=漫画模板 Name[zh_TW]=日本漫畫範本 Comment=template for Japanese Manga-style comics +Comment[ar]=قالب للهزليّات بنمط المانغا اليابانيّة Comment[bs]=predložak za japanske Manga stripove Comment[ca]=plantilla per a còmics d'estil manga japonès Comment[ca@valencia]=plantilla per a còmics d'estil manga japonés Comment[cs]=šablona pro japonské komiksy ve stylu Manga Comment[da]=skabelon til tegneserier i japansk Manga-stil Comment[de]=Vorlage für Comics im Stil japanischer Mangas Comment[el]=Πρότυπο για Ιαπωνικά μάνγκα κόμικς Comment[en_GB]=template for Japanese Manga-style comics Comment[es]=plantilla para cómics de estilo manga japonés Comment[et]=Jaapani manga-stiilis koomiksi mall Comment[eu]=Japoniako Manga estiloko komikietarako txantiloia Comment[fi]=japanilaisen mangatyylisen sarjakuvan pohja Comment[fr]=Modèle de mangas japonais Comment[gl]=Páxina de banda deseñada de formato Manga, con 2×3 viñetas non regulares. Comment[hu]=sablon a japán Manga-stílusú képregényekhez Comment[is]=Sniðmát fyrir japanskar Manga-teiknimyndir Comment[it]=modello per fumetti in stile manga giapponese Comment[ja]=日本式漫画用テンプレート Comment[kk]=Жапондық манга-стильдегі комикс үлгісі Comment[ko]=일본식 만화 서식 Comment[nb]=mal for japanske tegneserier i Manga-stil Comment[nds]=Vörlaag för japaansche Manga-Comics Comment[nl]=sjabloon voor strips in Japanse Manga-stijl Comment[pl]=szablon dla Japońskiego stylu komiksów Mangi Comment[pt]=modelo de banda desenhada Manga do estilo Japonês Comment[pt_BR]=Modelo de quadrinhos no estilo mangá japonês Comment[ru]=Шаблон комиксов в японском стиле манга Comment[sk]=šablóna pre japonské manga komixy Comment[sl]=predloge za stripe v japonskem slogu Manga Comment[sv]=seriemall med japansk Manga-stil Comment[tr]=Japon Manga çizgi romanları için şablon Comment[uk]=шаблон японських коміксів у стилі манґа Comment[wa]=Modele di bindes d' imådje al môde des mangas djaponès Comment[x-test]=xxtemplate for Japanese Manga-style comicsxx Comment[zh_CN]=日式漫画模板 Comment[zh_TW]=日本 Manga-風格的連環漫畫範本 X-Krita-Version=28 diff --git a/krita/data/templates/design/.directory b/krita/data/templates/design/.directory index ea2e2a7455..6db049443c 100644 --- a/krita/data/templates/design/.directory +++ b/krita/data/templates/design/.directory @@ -1,39 +1,40 @@ [Desktop Entry] Name=Design Templates +Name[ar]=قوالب التّصميم Name[bs]=Predlošci dizajna Name[ca]=Plantilles de disseny Name[ca@valencia]=Plantilles de disseny Name[cs]=Návrhové šablony Name[da]=Designskabeloner Name[de]=Design-Vorlagen Name[el]=Πρότυπα σχεδίασης Name[en_GB]=Design Templates Name[es]=Plantillas de diseño Name[et]=Disainmallid Name[eu]=Diseinu-txantiloiak Name[fi]=Suunnittelupohjat Name[fr]=Modèles design Name[gl]=Modelos de deseño Name[hu]=Tervező sablonok Name[ia]=Patronos de dessigno Name[is]=Hönnunarsniðmát Name[it]=Modelli di stile Name[ja]=デザインテンプレート Name[kk]=Пішім үлгілері Name[ko]=디자인 서식 Name[lt]=Dizaino šablonai Name[nb]=Designmaler Name[nl]=Design-sjablonen Name[pl]=Szablony projekcyjne Name[pt]=Modelos de Desenho Name[pt_BR]=Modelos de design Name[ru]=Шаблоны для дизайна Name[sk]=Šablóny dizajnu Name[sl]=Oblikovalske predloge Name[sv]=Designmallar Name[tr]=Tasarım Şablonları Name[uk]=Шаблони компонування Name[x-test]=xxDesign Templatesxx Name[zh_CN]=设计模板 Name[zh_TW]=設計範本 X-KDE-DefaultTab=true diff --git a/krita/data/templates/design/Designcinema16_10_2484x1200_96dpiRGB_8bit_.desktop b/krita/data/templates/design/Designcinema16_10_2484x1200_96dpiRGB_8bit_.desktop index 4fae8d8672..aa23211122 100644 --- a/krita/data/templates/design/Designcinema16_10_2484x1200_96dpiRGB_8bit_.desktop +++ b/krita/data/templates/design/Designcinema16_10_2484x1200_96dpiRGB_8bit_.desktop @@ -1,37 +1,38 @@ [Desktop Entry] Icon=template_ratio_1610 Name=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] +Name[ar]=تصميم سينمائيّ ١٦:١٠ [ ٢٤٨٤×١٢٠٠ ، ٩٦ نقطة/بوصة ، ٨ بتّ ] Name[bs]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[ca]=Disseny de cine 16:10 [2484x1200 / 96ppp RGB / 8bit] Name[ca@valencia]=Disseny de cine 16:10 [2484x1200 / 96ppp RGB / 8bit] Name[cs]=Návrh kino 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[da]=Design-cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[de]=Design-Kino 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[el]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[en_GB]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[es]=Diseño de cine 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[et]=Disainkino 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[eu]=Zinema-diseinua 16:10 [2484 x 1200, 96 dpi GBU, 8 bit] Name[fr]=style cinéma 16:10 [ 2484x1200, 96dpi RGB, 8bit ] Name[gl]=Deseño de cine 16:10 (2484×1200, 96 dpi RGB, 8 bits) Name[hu]=Tervező mozi 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[is]=Hanna kvikmynd 16:10 [ 2484x1200 , 96pát RGB , 8bita ] Name[it]=Stile cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[ja]=映画 16:10 [ 2484x1200、96dpi RGB、8 ビット ] Name[kk]=Кино пішімі 106:1 [ 2484x1200 , 96 н/д RGB , 8бит ] Name[nb]=Designkino 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[nl]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[pl]=Kino projekcyjne 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[pt]=Desenho de cinema 16:10 [ 2484x1200 , 96ppp RGB , 8-bits ] Name[pt_BR]=Design de cinema 16:10 [ 2484x1200, 96dpi RGB, 8bits ] Name[ru]=Дизайн кино 16:10 [ 2484x1200 , 96dpi RGB , 8 бит ] Name[sk]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[sv]=Design film 16:10 [ 2484x1200, 96 punkter/tum RGB, 8 bitar ] Name[tr]=Sineme tasarla 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Name[uk]=Компонування кіноекрана 16:10 [2484⨯1200, 96 т./д., RGB, 8 бітів] Name[x-test]=xxDesign cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]xx Name[zh_CN]=设计影院 16:10 [ 2484x1200 像素, 96dpi RGB , 8 位 ] Name[zh_TW]=設計電影院 16:10 [ 2484x1200 , 96dpi RGB , 8bit ] Type=Link URL[$e]=.source/Designcinema16_10_2484x1200_96dpiRGB_8bit_.kra X-KDE-Hidden=false diff --git a/krita/data/templates/design/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.desktop b/krita/data/templates/design/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.desktop index b206d5f54a..2b8e41a138 100644 --- a/krita/data/templates/design/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.desktop +++ b/krita/data/templates/design/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.desktop @@ -1,37 +1,38 @@ [Desktop Entry] Icon=template_ratio_2391 Name=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] +Name[ar]=تصميم سينمائيّ ٢٫٣٩:١ [ ٢٤٨٤×١٠٤٠ ، ٩٦ نقطة/بوصة ، ٨ بتّ ] Name[bs]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[ca]=Disseny de cine 2,39:1 [2484x1040 / 96ppp RGB / 8bit] Name[ca@valencia]=Disseny de cine 2,39:1 [2484x1040 / 96ppp RGB / 8bit] Name[cs]=Návrh kino 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[da]=Design-cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[de]=Design-Kino 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[el]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[en_GB]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[es]=Diseño de cine 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[et]=Disainkino 2,39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[eu]=Zinema-diseinua 2.39:1 [2484 x 1040, 96 dpi GBU, 8 bit] Name[fr]=style cinéma 2.39:1 [ 2484x1040, 96dpi RGB, 8bit ] Name[gl]=Deseño de cine 2.39:1 (2484×1040, 96 dpi RGB, 8 bits) Name[hu]=Tervező mozi 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[is]=Hanna kvikmynd 2.39:1 [ 2484x1040 , 96pát RGB , 8bita ] Name[it]=Stile cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[ja]=映画 2.39:1 [ 2484x1040、96dpi RGB、8 ビット ] Name[kk]=Кино пішімі 2.39:1 [ 2484x1040 , 96 н/д RGB , 8бит ] Name[nb]=Designkino 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[nl]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[pl]=Kino projekcyjne 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[pt]=Desenho de cinema 2,39:1 [ 2484x1040 , 96ppp RGB , 8-bits ] Name[pt_BR]=Design de cinema 2.39:1 [ 2484x1040, 96dpi RGB, 8bits ] Name[ru]=Дизайн кино 2.39:1 [ 2484x1040 , 96dpi RGB , 8 бит ] Name[sk]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[sv]=Design film 2,39:1 [ 2484x1040, 96 punkter/tum RGB, 8 bitar ] Name[tr]=Sineme tasarla 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Name[uk]=Компонування кіноекрана 2,39:1 [2484⨯1040, 96 т./д., RGB, 8 бітів] Name[x-test]=xxDesign cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]xx Name[zh_CN]=设计影院 2.39:1 [ 2484x1040 像素, 96dpi RGB , 8 位] Name[zh_TW]=設計電影院 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ] Type=Link URL[$e]=.source/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.kra X-KDE-Hidden=false diff --git a/krita/data/templates/design/web_design.desktop b/krita/data/templates/design/web_design.desktop index 7e3bbcda05..8cdd01ef3b 100644 --- a/krita/data/templates/design/web_design.desktop +++ b/krita/data/templates/design/web_design.desktop @@ -1,34 +1,35 @@ [Desktop Entry] Icon=template_web_design Name=Web Design [ 2160x1440 , 72ppi RGB , 8bit ] +Name[ar]=تصميم وبّ [ ٢١٦٠×١٤٤٠ ، ٧٢ بكسل/بوصة ، ٨ بتّ ] Name[bs]=Web dizajn [ 2160x1440 , 72ppi RGB , 8bit ] Name[ca]=Disseny web [2160x1440 / 72ppi RGB / 8bit] Name[ca@valencia]=Disseny web [2160x1440 / 72ppi RGB / 8bit] Name[cs]=Návrh webu [ 2160x1440 , 72ppi RGB , 8bit ] Name[da]=Webdesign [ 2160x1440 , 72ppi RGB , 8bit ] Name[de]=Web-Design [ 2160x1440 , 72ppi RGB , 8bit ] Name[el]=Σχεδίαση διαδικτυακών τόπων [ 2160x1440 , 72ppi RGB , 8bit ] Name[en_GB]=Web Design [ 2160x1440 , 72ppi RGB , 8bit ] Name[es]=Diseño de web 4:3 [ 2160x1440 , 72ppi RGB , 8bit ] Name[et]=Veebidisain [ 2160x1440, 72ppi RGB, 8-bitine ] Name[fr]=Style écran [ 2160x1440, 72ppi RGB , 8bit ] Name[gl]=Deseño web (2160×1440, 72 ppi RGB, 8 bits) Name[is]=Vefhönnun [ 2160x1440 , 72pát RGB , 8bita ] Name[it]=Progettazione web [ 2160x1440 , 72ppi RGB , 8bit ] Name[ja]=ウェブデザイン [ 2160x1440、72ppi RGB、8 ビット ] Name[nb]=Web Design [ 2160x1440 , 72ppi RGB , 8bit ] Name[nl]=Webontwerp [ 2160x1440 , 72ppi RGB , 8bit ] Name[pl]=Projekt sieciowy [ 2160x1440 , 72ppi RGB , 8bit ] Name[pt]=Desenho na Web [ 2160x1440 , 72ppp RGB , 8-bits ] Name[pt_BR]=Web Design [ 2160x1440 , 72ppi RGB , 8bits ] Name[ru]=Веб-дизайн [ 2160x1440 , 72ppi RGB , 8 бит ] Name[sk]=Webový dizajn [ 2160x1440 , 72ppi RGB , 8bit ] Name[sv]=Webbdesign [ 2160x1440, 72 punkter/tum RGB, 8 bitar ] Name[tr]=Web Tasarımı [ 2160x1440 , 72ppi RGB , 8bit ] Name[uk]=Вебдизайн [2160⨯1440, 72 т./д., RGB, 8 бітів] Name[x-test]=xxWeb Design [ 2160x1440 , 72ppi RGB , 8bit ]xx Name[zh_CN]=网页设计 [ 2160x1440 像素, 72ppi RGB , 8 位 ] Name[zh_TW]=網頁設計 [ 2160x1440 , 72ppi RGB , 8bit ] Type=Link URL[$e]=.source/web_design.kra X-KDE-Hidden=false diff --git a/krita/data/templates/dslr/.directory b/krita/data/templates/dslr/.directory index 092ee614d6..a59afd77ea 100644 --- a/krita/data/templates/dslr/.directory +++ b/krita/data/templates/dslr/.directory @@ -1,39 +1,40 @@ [Desktop Entry] Name=DSLR Templates +Name[ar]=قوالب DSLR Name[bs]=DSLR Predlošci Name[ca]=Plantilles DSLR Name[ca@valencia]=Plantilles DSLR Name[cs]=Šablony DSLR Name[da]=DSLR-skabeloner Name[de]=DSLR-Vorlagen Name[el]=Πρότυπα DSLR Name[en_GB]=DSLR Templates Name[es]=Plantillas DSLR Name[et]=Digitaalpeegelkaamera (DSLR) mallid Name[eu]=DSLR txantiloiak Name[fi]=DSLR-pohjat Name[fr]=Modèles DSLR Name[gl]=Modelos DSLR Name[hu]=DSLR sablonok Name[ia]=Patronos de DSLR Name[is]=DSLR sniðmát Name[it]=Modelli DSLR Name[ja]=デジタル一眼レフテンプレート Name[kk]=DSLR үлгілері Name[ko]=DSLR 서식 Name[lt]=DSLR šablonai Name[nb]=DSLR-maler Name[nl]=DSLR-sjablonen Name[pl]=Szablony DSLR Name[pt]=Modelos de DSLR Name[pt_BR]=Modelos DSLR Name[ru]=Шаблоны для фотоаппаратов Name[sk]=Šablóny DSLR Name[sl]=Predloge DSLR Name[sv]=Mallar för digitala spegelreflexkameror Name[tr]=DSLR Şablonları Name[uk]=Шаблони DSLR Name[x-test]=xxDSLR Templatesxx Name[zh_CN]=DSLR 模板 Name[zh_TW]=數位單眼相機範本 X-KDE-DefaultTab=true diff --git a/krita/data/workspaces/Default.kws b/krita/data/workspaces/Default.kws index c7ba2fab69..e36b14374a 100644 --- a/krita/data/workspaces/Default.kws +++ b/krita/data/workspaces/Default.kws @@ -1,4 +1,4 @@ - + diff --git a/krita/krita.action b/krita/krita.action index 112631fdca..20b3b4030f 100644 --- a/krita/krita.action +++ b/krita/krita.action @@ -1,3114 +1,3104 @@ General Open Resources Folder Opens a file browser at the location Krita saves resources such as brushes to. Opens a file browser at the location Krita saves resources such as brushes to. Open Resources Folder 0 0 false Cleanup removed files... Cleanup removed files Cleanup removed files 0 0 false C&ascade Cascade Cascade 10 0 false &Tile Tile Tile 10 0 false Create Resource Bundle... Create Resource Bundle Create Resource Bundle 0 0 false Show File Toolbar Show File Toolbar Show File Toolbar false Show color selector Show color selector Show color selector Shift+I false Show MyPaint shade selector Show MyPaint shade selector Show MyPaint shade selector Shift+M false Show minimal shade selector Show minimal shade selector Show minimal shade selector Shift+N false Show color history Show color history Show color history H false Show common colors Show common colors Show common colors U false Show Tool Options Show Tool Options Show Tool Options \ false Show Brush Editor Show Brush Editor Show Brush Editor F5 false Show Brush Presets Show Brush Presets Show Brush Presets F6 false Toggle Tablet Debugger Toggle Tablet Debugger Toggle Tablet Debugger 0 0 Ctrl+Shift+T false Show system information for bug reports. Show system information for bug reports. Show system information for bug reports. false Rename Composition... Rename Composition Rename Composition 0 0 false Update Composition Update Composition Update Composition 0 0 false Use multiple of 2 for pixel scale Use multiple of 2 for pixel scale Use multiple of 2 for pixel scale Use multiple of 2 for pixel scale 1 0 true Painting lightness-increase Make brush color lighter Make brush color lighter Make brush color lighter 0 0 L false lightness-decrease Make brush color darker Make brush color darker Make brush color darker 0 0 K false Make brush color more saturated Make brush color more saturated Make brush color more saturated false Make brush color more desaturated Make brush color more desaturated Make brush color more desaturated false Shift brush color hue clockwise Shift brush color hue clockwise Shift brush color hue clockwise false Shift brush color hue counter-clockwise Shift brush color hue counter-clockwise Shift brush color hue counter-clockwise false Make brush color more red Make brush color more red Make brush color more red false Make brush color more green Make brush color more green Make brush color more green false Make brush color more blue Make brush color more blue Make brush color more blue false Make brush color more yellow Make brush color more yellow Make brush color more yellow false opacity-increase Increase opacity Increase opacity Increase opacity 0 0 O false opacity-decrease Decrease opacity Decrease opacity Decrease opacity 0 0 I false draw-eraser Set eraser mode Set eraser mode Set eraser mode 10000 0 E true view-refresh Reload Original Preset Reload Original Preset Reload Original Preset 10000 false transparency-unlocked Preserve Alpha Preserve Alpha Preserve Alpha 10000 true transform_icons_penPressure Use Pen Pressure Use Pen Pressure Use Pen Pressure 10000 true symmetry-horizontal Horizontal Mirror Tool Horizontal Mirror Tool Horizontal Mirror Tool 10000 true symmetry-vertical Vertical Mirror Tool Vertical Mirror Tool Vertical Mirror Tool 10000 true Hide Mirror X Line Hide Mirror X Line Hide Mirror X Line 10000 true Hide Mirror Y Line Hide Mirror Y Line Hide Mirror Y Line 10000 true Lock Lock X Line Lock X Line 10000 true Lock Y Line Lock Y Line Lock Y Line 10000 true Move to Canvas Center Move to Canvas Center X Move to Canvas Center X 10000 false Move to Canvas Center Y Move to Canvas Center Y Move to Canvas Center Y 10000 false &Invert Selection Invert current selection Invert Selection 10000000000 100 Ctrl+Shift+I false &Toggle Selection Display Mode Toggle Selection Display Mode Toggle Selection Display Mode 0 0 false Next Favourite Preset Next Favourite Preset Next Favourite Preset , false Previous Favourite Preset Previous Favourite Preset Previous Favourite Preset . false preset-switcher Switch to Previous Preset Switch to Previous Preset Switch to Previous Preset / false Hide Brushes and Stuff Toolbar Hide Brushes and Stuff Toolbar Hide Brushes and Stuff Toolbar true Reset Foreground and Background Color Reset Foreground and Background Color Reset Foreground and Background Color D false Swap Foreground and Background Color Swap Foreground and Background Color Swap Foreground and Background Color X false smoothing-weighted Brush Smoothing: Weighted Brush Smoothing: Weighted Brush Smoothing: Weighted false smoothing-no Brush Smoothing: Disabled Brush Smoothing: Disabled Brush Smoothing: Disabled false smoothing-stabilizer Brush Smoothing: Stabilizer Brush Smoothing: Stabilizer Brush Smoothing: Stabilizer false brushsize-decrease Decrease Brush Size Decrease Brush Size Decrease Brush Size 0 0 [ false smoothing-basic Brush Smoothing: Basic Brush Smoothing: Basic Brush Smoothing: Basic false brushsize-increase Increase Brush Size Increase Brush Size Increase Brush Size 0 0 ] false Toggle Assistant Toggle Assistant ToggleAssistant Ctrl+Shift+L true Undo Polygon Selection Points Undo Polygon Selection Points Undo Polygon Selection Points Shift+Z false Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) 10000 1 Ctrl+Shift+Backspace false Fill with Background Color (Opacity) Fill with Background Color (Opacity) Fill with Background Color (Opacity) 10000 1 Ctrl+Backspace false Fill with Pattern (Opacity) Fill with Pattern (Opacity) Fill with Pattern (Opacity) 10000 1 false Convert &to Shape Convert to Shape Convert to Shape 10000000000 0 false &Select Opaque Select Opaque Select Opaque 100000 100 false &Show Global Selection Mask Shows global selection as a usual selection mask in <interface>Layers</interface> docker Show Global Selection Mask 100000 100 true Filters color-to-alpha &Color to Alpha... Color to Alpha Color to Alpha 10000 0 false &Top Edge Detection Top Edge Detection Top Edge Detection 10000 0 false &Index Colors... Index Colors Index Colors 10000 0 false Emboss Horizontal &Only Emboss Horizontal Only Emboss Horizontal Only 10000 0 false D&odge Dodge Dodge 10000 0 false &Sharpen Sharpen Sharpen 10000 0 false B&urn Burn Burn 10000 0 false &Mean Removal Mean Removal Mean Removal 10000 0 false &Gaussian Blur... Gaussian Blur Gaussian Blur 10000 0 false Emboss &in All Directions Emboss in All Directions Emboss in All Directions 10000 0 false &Small Tiles... Small Tiles Small Tiles 10000 0 false &Levels... Levels Levels 10000 0 Ctrl+L false &Sobel... Sobel Sobel 10000 0 false &Wave... Wave Wave 10000 0 false &Motion Blur... Motion Blur Motion Blur 10000 0 false &Color Adjustment curves... Color Adjustment curves Color Adjustment curves 10000 0 Ctrl+M false Pi&xelize... Pixelize Pixelize 10000 0 false Emboss (&Laplacian) Emboss (Laplacian) Emboss (Laplacian) 10000 0 false &Left Edge Detection Left Edge Detection Left Edge Detection 10000 0 false &Blur... Blur Blur 10000 0 false &Raindrops... Raindrops Raindrops 10000 0 false &Bottom Edge Detection Bottom Edge Detection Bottom Edge Detection 10000 0 false &Random Noise... Random Noise Random Noise 10000 0 false &Brightness/Contrast curve... Brightness/Contrast curve Brightness/Contrast curve 10000 0 false Colo&r Balance.. Color Balance.. Color Balance.. 10000 0 Ctrl+B false &Phong Bumpmap... Phong Bumpmap Phong Bumpmap 10000 0 false &Desaturate Desaturate Desaturate 10000 0 Ctrl+Shift+U false Color &Transfer... Color Transfer Color Transfer 10000 0 false Emboss &Vertical Only Emboss Vertical Only Emboss Vertical Only 10000 0 false &Lens Blur... Lens Blur Lens Blur 10000 0 false M&inimize Channel Minimize Channel Minimize Channel 10000 0 false M&aximize Channel Maximize Channel Maximize Channel 10000 0 false &Oilpaint... Oilpaint Oilpaint 10000 0 false &Right Edge Detection Right Edge Detection Right Edge Detection 10000 0 false &Auto Contrast Auto Contrast Auto Contrast 10000 0 false &Round Corners... Round Corners Round Corners 10000 0 false &Unsharp Mask... Unsharp Mask Unsharp Mask 10000 0 false &Emboss with Variable Depth... Emboss with Variable Depth Emboss with Variable Depth 10000 0 false Emboss &Horizontal && Vertical Emboss Horizontal & Vertical Emboss Horizontal & Vertical 10000 0 false Random &Pick... Random Pick Random Pick 10000 0 false &Gaussian Noise Reduction... Gaussian Noise Reduction Gaussian Noise Reduction 10000 0 false &Posterize... Posterize Posterize 10000 0 false &Wavelet Noise Reducer... Wavelet Noise Reducer Wavelet Noise Reducer 10000 0 false &HSV Adjustment... HSV Adjustment HSV Adjustment 10000 0 Ctrl+U false Tool Shortcuts Dynamic Brush Tool Dynamic Brush Tool Dynamic Brush Tool false Crop Tool Crop the image to an area Crop the image to an area C false Polygon Tool Polygon Tool. Shift-mouseclick ends the polygon. Polygon Tool. Shift-mouseclick ends the polygon. false - - - References - - References - References - - false - - Rectangle Tool Rectangle Tool Rectangle Tool false Multibrush Tool Multibrush Tool Multibrush Tool Q false Lazy Brush Tool Lazy Brush Tool Lazy Brush Tool Smart Patch Tool Smart Patch Tool Smart Patch Tool Pan Tool Pan Tool Pan Tool Shape Manipulation Tool Shape Manipulation Tool Shape Manipulation Tool false Color Picker Select a color from the image or current layer Select a color from the image or current layer P false Text Editing Tool Text editing Text editing false Outline Selection Tool Outline Selection Tool Outline Selection Tool false Artistic Text Tool Artistic text editing Artistic text editing false Bezier Curve Selection Tool Select a Bezier Curve Selection Tool false Similar Color Selection Tool Select a Similar Color Selection Tool false Fill Tool Fill a contiguous area of color with a color, or fill a selection. Fill a contiguous area of color with a color, or fill a selection. F false Line Tool Line Tool Line Tool false Freehand Path Tool Freehand Path Tool Freehand Path Tool false Bezier Curve Tool Bezier Curve Tool. Shift-mouseclick ends the curve. Bezier Curve Tool. Shift-mouseclick ends the curve. false Ellipse Tool Ellipse Tool Ellipse Tool false Freehand Brush Tool Freehand Brush Tool Freehand Brush Tool B false Create object Create object Create object false Elliptical Selection Tool Elliptical Selection Tool Elliptical Selection Tool J false Contiguous Selection Tool Contiguous Selection Tool Contiguous Selection Tool false Pattern editing Pattern editing Pattern editing false Review Review Review false Draw a gradient. Draw a gradient. Draw a gradient. G false Polygonal Selection Tool Polygonal Selection Tool Polygonal Selection Tool false Measurement Tool Measure the distance between two points Measure the distance between two points false Rectangular Selection Tool Rectangular Selection Tool Rectangular Selection Tool Ctrl+R false Move Tool Move a layer Move a layer T false Vector Image Tool Vector Image (EMF/WMF/SVM/SVG) tool Vector Image (EMF/WMF/SVM/SVG) tool false Calligraphy Calligraphy Calligraphy false Path editing Path editing Path editing false Zoom Tool Zoom Tool Zoom Tool false Polyline Tool Polyline Tool. Shift-mouseclick ends the polyline. Polyline Tool. Shift-mouseclick ends the polyline. false Transform Tool Transform a layer or a selection Transform a layer or a selection Ctrl+T false Assistant Tool Assistant Tool Assistant Tool false Text tool Text tool Text tool false Gradient Editing Tool Gradient editing Gradient editing false Blending Modes Select Normal Blending Mode Select Normal Blending Mode Select Normal Blending Mode 0 0 Alt+Shift+N false Select Dissolve Blending Mode Select Dissolve Blending Mode Select Dissolve Blending Mode 0 0 Alt+Shift+I false Select Behind Blending Mode Select Behind Blending Mode Select Behind Blending Mode 0 0 Alt+Shift+Q false Select Clear Blending Mode Select Clear Blending Mode Select Clear Blending Mode 0 0 Alt+Shift+R false Select Darken Blending Mode Select Darken Blending Mode Select Darken Blending Mode 0 0 Alt+Shift+K false Select Multiply Blending Mode Select Multiply Blending Mode Select Multiply Blending Mode 0 0 Alt+Shift+M false Select Color Burn Blending Mode Select Color Burn Blending Mode Select Color Burn Blending Mode 0 0 Alt+Shift+B false Select Linear Burn Blending Mode Select Linear Burn Blending Mode Select Linear Burn Blending Mode 0 0 Alt+Shift+A false Select Lighten Blending Mode Select Lighten Blending Mode Select Lighten Blending Mode 0 0 Alt+Shift+G false Select Screen Blending Mode Select Screen Blending Mode Select Screen Blending Mode 0 0 Alt+Shift+S false Select Color Dodge Blending Mode Select Color Dodge Blending Mode Select Color Dodge Blending Mode 0 0 Alt+Shift+D false Select Linear Dodge Blending Mode Select Linear Dodge Blending Mode Select Linear Dodge Blending Mode 0 0 Alt+Shift+W false Select Overlay Blending Mode Select Overlay Blending Mode Select Overlay Blending Mode 0 0 Alt+Shift+O false Select Hard Overlay Blending Mode Select Hard Overlay Blending Mode Select Hard Overlay Blending Mode 0 0 Alt+Shift+P false Select Soft Light Blending Mode Select Soft Light Blending Mode Select Soft Light Blending Mode 0 0 Alt+Shift+F false Select Hard Light Blending Mode Select Hard Light Blending Mode Select Hard Light Blending Mode 0 0 Alt+Shift+H false Select Vivid Light Blending Mode Select Vivid Light Blending Mode Select Vivid Light Blending Mode 0 0 Alt+Shift+V false Select Linear Light Blending Mode Select Linear Light Blending Mode Select Linear Light Blending Mode 0 0 Alt+Shift+J false Select Pin Light Blending Mode Select Pin Light Blending Mode Select Pin Light Blending Mode 0 0 Alt+Shift+Z false Select Hard Mix Blending Mode Select Hard Mix Blending Mode Select Hard Mix Blending Mode 0 0 Alt+Shift+L false Select Difference Blending Mode Select Difference Blending Mode Select Difference Blending Mode 0 0 Alt+Shift+E false Select Exclusion Blending Mode Select Exclusion Blending Mode Select Exclusion Blending Mode 0 0 Alt+Shift+X false Select Hue Blending Mode Select Hue Blending Mode Select Hue Blending Mode 0 0 Alt+Shift+U false Select Saturation Blending Mode Select Saturation Blending Mode Select Saturation Blending Mode 0 0 Alt+Shift+T false Select Color Blending Mode Select Color Blending Mode Select Color Blending Mode 0 0 Alt+Shift+C false Select Luminosity Blending Mode Select Luminosity Blending Mode Select Luminosity Blending Mode 0 0 Alt+Shift+Y false Animation Previous frame Move to previous frame Move to previous frame 1 0 false Next frame Move to next frame Move to next frame 1 0 false Play / pause animation Play / pause animation Play / pause animation 1 0 false Add blank frame Add blank frame Add blank frame 100000 0 false Copy Frame Add duplicate frame Add duplicate frame 100000 0 false Toggle onion skin Toggle onion skin Toggle onion skin 100000 0 false Previous Keyframe false Next Keyframe false First Frame false Last Frame false Auto Frame Mode true true Add blank frame Add blank frame Add blank frame 100000 0 false Show in Timeline true Layers Activate next layer Activate next layer Activate next layer 1000 0 PgUp false Activate previous layer Activate previous layer Activate previous layer 1000 0 PgDown false Activate previously selected layer Activate previously selected layer Activate previously selected layer 1000 0 ; false groupLayer &Group Layer Group Layer Group Layer 1000 0 false cloneLayer &Clone Layer Clone Layer Clone Layer 1000 0 false vectorLayer &Vector Layer Vector Layer Vector Layer 1000 0 false filterLayer &Filter Layer... Filter Layer Filter Layer 1000 0 false fillLayer &Fill Layer... Fill Layer Fill Layer 1000 0 false fileLayer &File Layer... File Layer File Layer 1000 0 false transparencyMask &Transparency Mask Transparency Mask Transparency Mask 100000 0 false filterMask &Filter Mask... Filter Mask Filter Mask 100000 0 false filterMask &Colorize Mask Colorize Mask Colorize Mask 100000 0 false transformMask &Transform Mask... Transform Mask Transform Mask 100000 0 false selectionMask &Local Selection Local Selection Local Selection 100000 0 false view-filter &Isolate Layer Isolate Layer Isolate Layer 1000 0 true layer-locked &Toggle layer lock Toggle layer lock Toggle layer lock 1000 0 false visible Toggle layer &visibility Toggle layer visibility Toggle layer visibility 1000 0 false transparency-locked Toggle layer &alpha Toggle layer alpha Toggle layer alpha 1000 0 false transparency-enabled Toggle layer alpha &inheritance Toggle layer alpha inheritance Toggle layer alpha inheritance 1000 0 false paintLayer &Paint Layer Paint Layer Paint Layer 1000 0 Insert false &New Layer From Visible New layer from visible New layer from visible 1000 0 false duplicatelayer &Duplicate Layer or Mask Duplicate Layer or Mask Duplicate Layer or Mask 1000 0 Ctrl+J false &Cut Selection to New Layer Cut Selection to New Layer Cut Selection to New Layer 100000000 1 Ctrl+Shift+J false Copy &Selection to New Layer Copy Selection to New Layer Copy Selection to New Layer 100000000 0 Ctrl+Alt+J false Copy Layer Copy layer to clipboard Copy layer to clipboard 1000 0 false Cut Layer Cut layer to clipboard Cut layer to clipboard 1000 0 false Paste Layer Paste layer from clipboard Paste layer from clipboard 1000 0 false Quick Group Create a group layer containing selected layers Quick Group 100000 0 Ctrl+G false Quick Ungroup Remove grouping of the layers or remove one layer out of the group Quick Ungroup 100000 0 Ctrl+Alt+G false Quick Clipping Group Group selected layers and add a layer with clipped alpha channel Quick Clipping Group 100000 0 Ctrl+Shift+G false All Layers Select all layers Select all layers 10000 0 false Visible Layers Select all visible layers Select all visible layers 10000 0 false Locked Layers Select all locked layers Select all locked layers 10000 0 false Invisible Layers Select all invisible layers Select all invisible layers 10000 0 false Unlocked Layers Select all unlocked layers Select all unlocked layers 10000 0 false document-save &Save Layer/Mask... Save Layer/Mask Save Layer/Mask 1000 0 false document-save Save 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 &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/org.kde.krita.appdata.xml b/krita/org.kde.krita.appdata.xml index 0f28f6b516..3191aae9f1 100644 --- a/krita/org.kde.krita.appdata.xml +++ b/krita/org.kde.krita.appdata.xml @@ -1,193 +1,195 @@ org.kde.krita.desktop CC0-1.0 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 + رسم رقميّ، حريّة إبداعيّة Pintura dixital, llibertá creativa 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 Digitaalimaalaus, luova vapaus Peinture numérique, liberté créatrice Debuxo dixital, liberdade creativa Pictura digital, Libertate creative 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 ye l'estudiu completu d'arte dixital.

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 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 è 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.

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.

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 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 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.

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.

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/2016-05-24_screenshot_002.png https://cdn.kde.org/screenshots/krita/2016-05-24_screenshot_003.png https://cdn.kde.org/screenshots/krita/2016-05-24_screenshot_004.png https://cdn.kde.org/screenshots/krita/2016-05-24_screenshot_005.png foundation@krita.org KDE krita
diff --git a/krita/org.kde.krita.desktop b/krita/org.kde.krita.desktop index 2c6d44cfe5..2ba0b72ef0 100644 --- a/krita/org.kde.krita.desktop +++ b/krita/org.kde.krita.desktop @@ -1,148 +1,151 @@ [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 %U GenericName=Digital Painting +GenericName[ar]=رسم رقميّ GenericName[bs]=Digitalno Bojenje GenericName[ca]=Dibuix digital GenericName[ca@valencia]=Dibuix digital GenericName[cs]=Digitální malování GenericName[da]=Digital tegning GenericName[de]=Digitales Malen GenericName[el]=Ψηφιακή ζωγραφική GenericName[en_GB]=Digital Painting GenericName[es]=Pintura digital GenericName[et]=Digitaalne joonistamine GenericName[eu]=Pintura digitala GenericName[fi]=Digitaalimaalaus GenericName[fr]=Peinture numérique GenericName[gl]=Debuxo dixital GenericName[hu]=Digitális festészet GenericName[ia]=Pintura Digital GenericName[is]=Stafræn málun GenericName[it]=Pittura digitale GenericName[ja]=デジタルペインティング GenericName[kk]=Цифрлық сурет салу GenericName[lt]=Skaitmeninis piešimas GenericName[mr]=डिजिटल पेंटिंग GenericName[nb]=Digital maling GenericName[nl]=Digitaal schilderen GenericName[pl]=Cyfrowe malowanie GenericName[pt]=Pintura Digital GenericName[pt_BR]=Pintura digital GenericName[ru]=Цифровая живопись GenericName[sk]=Digitálne maľovanie GenericName[sl]=Digitalno slikanje GenericName[sv]=Digital målning GenericName[tr]=Sayısal Boyama GenericName[ug]=سىفىرلىق رەسىم سىزغۇ GenericName[uk]=Цифрове малювання GenericName[x-test]=xxDigital Paintingxx GenericName[zh_CN]=数字绘画 GenericName[zh_TW]=數位繪畫 MimeType=application/x-krita;image/openraster;application/x-krita-paintoppreset; Comment=Digital Painting +Comment[ar]=رسم رقميّ Comment[bs]=Digitalno Bojenje Comment[ca]=Dibuix digital Comment[ca@valencia]=Dibuix digital Comment[cs]=Digitální malování Comment[da]=Digital tegning Comment[de]=Digitales Malen Comment[el]=Ψηφιακή ζωγραφική Comment[en_GB]=Digital Painting Comment[es]=Pintura digital Comment[et]=Digitaalne joonistamine Comment[eu]=Pintura digitala Comment[fi]=Digitaalimaalaus Comment[fr]=Peinture numérique Comment[gl]=Debuxo dixital. Comment[hu]=Digitális festészet Comment[ia]=Pintura Digital Comment[is]=Stafræn málun Comment[it]=Pittura digitale Comment[ja]=デジタルペインティング Comment[kk]=Цифрлық сурет салу Comment[lt]=Skaitmeninis piešimas Comment[mr]=डिजिटल पेंटिंग Comment[nb]=Digital maling Comment[nl]=Digitaal schilderen Comment[pl]=Cyfrowe malowanie Comment[pt]=Pintura Digital Comment[pt_BR]=Pintura digital Comment[ru]=Цифровая живопись Comment[sk]=Digitálne maľovanie Comment[sl]=Digitalno slikanje Comment[sv]=Digitalt målningsverktyg Comment[tr]=Sayısal Boyama Comment[ug]=سىفىرلىق رەسىم سىزغۇ Comment[uk]=Цифрове малювання Comment[x-test]=xxDigital Paintingxx Comment[zh_CN]=数字绘画 Comment[zh_TW]=數位繪畫 Type=Application Icon=calligrakrita Categories=Qt;KDE;Graphics; X-KDE-NativeMimeType=application/x-krita X-KDE-ExtraNativeMimeTypes= StartupNotify=true X-Krita-Version=28 diff --git a/libs/flake/CMakeLists.txt b/libs/flake/CMakeLists.txt index 04201eb040..511f3c1f7c 100644 --- a/libs/flake/CMakeLists.txt +++ b/libs/flake/CMakeLists.txt @@ -1,247 +1,246 @@ project(kritaflake) include_directories( ${CMAKE_SOURCE_DIR}/libs/flake/commands ${CMAKE_SOURCE_DIR}/libs/flake/tools ${CMAKE_SOURCE_DIR}/libs/flake/svg ${CMAKE_SOURCE_DIR}/libs/flake/text ${CMAKE_BINARY_DIR}/libs/flake ) add_subdirectory(styles) add_subdirectory(tests) set(kritaflake_SRCS KoGradientHelper.cpp KoFlake.cpp KoCanvasBase.cpp KoResourceManager_p.cpp KoDerivedResourceConverter.cpp KoResourceUpdateMediator.cpp KoCanvasResourceManager.cpp KoDocumentResourceManager.cpp KoCanvasObserverBase.cpp KoCanvasSupervisor.cpp KoDockFactoryBase.cpp KoDockRegistry.cpp KoDataCenterBase.cpp KoInsets.cpp KoPathShape.cpp KoPathPoint.cpp KoPathSegment.cpp KoSelection.cpp KoSelectedShapesProxy.cpp KoSelectedShapesProxySimple.cpp KoShape.cpp KoShapeAnchor.cpp KoShapeBasedDocumentBase.cpp KoShapeApplicationData.cpp KoShapeContainer.cpp KoShapeContainerModel.cpp KoShapeGroup.cpp KoShapeManager.cpp KoShapePaintingContext.cpp KoFrameShape.cpp - KoUnavailShape.cpp KoMarker.cpp KoMarkerCollection.cpp KoToolBase.cpp KoCanvasController.cpp KoCanvasControllerWidget.cpp KoCanvasControllerWidgetViewport_p.cpp KoShapeRegistry.cpp KoDeferredShapeFactoryBase.cpp KoToolFactoryBase.cpp KoPathShapeFactory.cpp KoShapeFactoryBase.cpp KoShapeUserData.cpp KoParameterShape.cpp KoPointerEvent.cpp KoShapeController.cpp KoToolSelection.cpp KoShapeLayer.cpp KoPostscriptPaintDevice.cpp KoInputDevice.cpp KoToolManager_p.cpp KoToolManager.cpp KoToolRegistry.cpp KoToolProxy.cpp KoShapeSavingContext.cpp KoShapeLoadingContext.cpp KoLoadingShapeUpdater.cpp KoPathShapeLoader.cpp KoShapeStrokeModel.cpp KoShapeStroke.cpp KoShapeBackground.cpp KoColorBackground.cpp KoGradientBackground.cpp KoOdfGradientBackground.cpp KoHatchBackground.cpp KoPatternBackground.cpp KoVectorPatternBackground.cpp KoShapeConfigWidgetBase.cpp KoDrag.cpp KoSvgPaste.cpp KoDragOdfSaveHelper.cpp KoShapeOdfSaveHelper.cpp KoConnectionPoint.cpp KoConnectionShape.cpp KoConnectionShapeLoadingUpdater.cpp KoConnectionShapeFactory.cpp KoConnectionShapeConfigWidget.cpp KoSnapGuide.cpp KoSnapProxy.cpp KoSnapStrategy.cpp KoSnapData.cpp KoShapeShadow.cpp KoSharedLoadingData.cpp KoSharedSavingData.cpp KoViewConverter.cpp KoInputDeviceHandler.cpp KoInputDeviceHandlerEvent.cpp KoInputDeviceHandlerRegistry.cpp KoImageData.cpp KoImageData_p.cpp KoImageCollection.cpp KoOdfWorkaround.cpp KoFilterEffect.cpp KoFilterEffectStack.cpp KoFilterEffectFactoryBase.cpp KoFilterEffectRegistry.cpp KoFilterEffectConfigWidgetBase.cpp KoFilterEffectRenderContext.cpp KoFilterEffectLoadingContext.cpp KoTextShapeDataBase.cpp KoTosContainer.cpp KoTosContainerModel.cpp KoClipPath.cpp KoClipMask.cpp KoClipMaskPainter.cpp KoCurveFit.cpp commands/KoShapeGroupCommand.cpp commands/KoShapeAlignCommand.cpp commands/KoShapeBackgroundCommand.cpp commands/KoShapeCreateCommand.cpp commands/KoShapeDeleteCommand.cpp commands/KoShapeDistributeCommand.cpp commands/KoShapeLockCommand.cpp commands/KoShapeMoveCommand.cpp commands/KoShapeResizeCommand.cpp commands/KoShapeShearCommand.cpp commands/KoShapeSizeCommand.cpp commands/KoShapeStrokeCommand.cpp commands/KoShapeUngroupCommand.cpp commands/KoShapeReorderCommand.cpp commands/KoShapeKeepAspectRatioCommand.cpp commands/KoPathBaseCommand.cpp commands/KoPathPointMoveCommand.cpp commands/KoPathControlPointMoveCommand.cpp commands/KoPathPointTypeCommand.cpp commands/KoPathPointRemoveCommand.cpp commands/KoPathPointInsertCommand.cpp commands/KoPathSegmentBreakCommand.cpp commands/KoPathBreakAtPointCommand.cpp commands/KoPathSegmentTypeCommand.cpp commands/KoPathCombineCommand.cpp commands/KoSubpathRemoveCommand.cpp commands/KoSubpathJoinCommand.cpp commands/KoParameterHandleMoveCommand.cpp commands/KoParameterToPathCommand.cpp commands/KoShapeTransformCommand.cpp commands/KoPathFillRuleCommand.cpp commands/KoConnectionShapeTypeCommand.cpp commands/KoShapeShadowCommand.cpp commands/KoPathReverseCommand.cpp commands/KoShapeRenameCommand.cpp commands/KoShapeRunAroundCommand.cpp commands/KoPathPointMergeCommand.cpp commands/KoShapeTransparencyCommand.cpp commands/KoShapeClipCommand.cpp commands/KoShapeUnclipCommand.cpp commands/KoPathShapeMarkerCommand.cpp commands/KoShapeConnectionChangeCommand.cpp commands/KoMultiPathPointMergeCommand.cpp commands/KoMultiPathPointJoinCommand.cpp commands/KoKeepShapesSelectedCommand.cpp html/HtmlSavingContext.cpp html/HtmlWriter.cpp tools/KoCreateShapeStrategy.cpp tools/KoPathToolFactory.cpp tools/KoPathTool.cpp tools/KoPathToolSelection.cpp tools/KoPathToolHandle.cpp tools/PathToolOptionWidget.cpp tools/KoPathPointRubberSelectStrategy.cpp tools/KoPathPointMoveStrategy.cpp tools/KoPathConnectionPointStrategy.cpp tools/KoPathControlPointMoveStrategy.cpp tools/KoParameterChangeStrategy.cpp tools/KoZoomTool.cpp tools/KoZoomToolFactory.cpp tools/KoZoomToolWidget.cpp tools/KoZoomStrategy.cpp tools/KoInteractionTool.cpp tools/KoInteractionStrategy.cpp tools/KoInteractionStrategyFactory.cpp tools/KoCreateShapesTool.cpp tools/KoCreateShapesToolFactory.cpp tools/KoShapeRubberSelectStrategy.cpp tools/KoPathSegmentChangeStrategy.cpp svg/KoShapePainter.cpp svg/SvgUtil.cpp svg/SvgGraphicContext.cpp svg/SvgSavingContext.cpp svg/SvgWriter.cpp svg/SvgStyleWriter.cpp svg/SvgShape.cpp svg/SvgParser.cpp svg/SvgStyleParser.cpp svg/SvgGradientHelper.cpp svg/SvgFilterHelper.cpp svg/SvgCssHelper.cpp svg/SvgClipPathHelper.cpp svg/SvgLoadingContext.cpp svg/SvgShapeFactory.cpp svg/parsers/SvgTransformParser.cpp text/KoSvgText.cpp text/KoSvgTextProperties.cpp text/KoSvgTextChunkShape.cpp text/KoSvgTextShape.cpp text/KoSvgTextShapeMarkupConverter.cpp resources/KoSvgSymbolCollectionResource.cpp FlakeDebug.cpp tests/MockShapes.cpp ) ki18n_wrap_ui(kritaflake_SRCS tools/PathToolOptionWidgetBase.ui KoConnectionShapeConfigWidget.ui tools/KoZoomToolWidget.ui ) add_library(kritaflake SHARED ${kritaflake_SRCS}) generate_export_header(kritaflake BASE_NAME kritaflake) target_include_directories(kritaflake PUBLIC $ $ $ $ ) target_link_libraries(kritaflake kritapigment kritawidgetutils kritaodf kritacommand KF5::WidgetsAddons Qt5::Svg) set_target_properties(kritaflake PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaflake ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/flake/KoFrameShape.cpp b/libs/flake/KoFrameShape.cpp index 7229e118da..5fe3a2bde0 100644 --- a/libs/flake/KoFrameShape.cpp +++ b/libs/flake/KoFrameShape.cpp @@ -1,55 +1,64 @@ /* This file is part of the KDE project Copyright (C) 2008 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. */ #include "KoFrameShape.h" #include #include class Q_DECL_HIDDEN KoFrameShape::Private { public: Private(const QString &ns, const QString &tag) : ns(ns) , tag(tag) {} + Private(const Private &rhs) + : ns(rhs.ns) + , tag(rhs.tag) {} + const QString ns; const QString tag; }; KoFrameShape::KoFrameShape(const QString &ns, const QString &tag) - : d(new Private(ns, tag)) + : d(new Private(ns, tag)) +{ +} + +KoFrameShape::KoFrameShape(const KoFrameShape &rhs) + : d(new Private(*rhs.d)) { } KoFrameShape::~KoFrameShape() { delete d; } bool KoFrameShape::loadOdfFrame(const KoXmlElement & element, KoShapeLoadingContext &context) { const KoXmlElement & frameElement(KoXml::namedItemNS(element, d->ns, d->tag)); if (frameElement.isNull()) { errorFlake << "frame element" << d->tag << "not found"; return false; } return loadOdfFrameElement(frameElement, context); } diff --git a/libs/flake/KoFrameShape.h b/libs/flake/KoFrameShape.h index 466a85e1e4..e067e2b689 100644 --- a/libs/flake/KoFrameShape.h +++ b/libs/flake/KoFrameShape.h @@ -1,94 +1,99 @@ /* This file is part of the KDE project Copyright (C) 2008 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 KOFRAMESHAPE_H #define KOFRAMESHAPE_H #include "kritaflake_export.h" class KoShapeLoadingContext; #include class QString; /** * @brief Base class for shapes that are saved as a part of a draw:frame. * * Shapes like the TextShape or the PictureShape are implementing this * class to deal with frames and their attributes. * * What follows is a sample taken out of an ODT-file that shows how this works * together; * @code * * * * @endcode * * The loading code of the shape gets passed the draw:frame element. Out of this element the * odf attributes can be loaded. Then it calls loadOdfFrame which loads the correct frame element * the object supports. The loading of the frame element is done in the loadOdfFrameElement. * * @code * bool PictureShape::loadOdf( const KoXmlElement & element, KoShapeLoadingContext &context ) * { * loadOdfAttributes( element, context, OdfAllAttributes ); * return loadOdfFrame( element, context ); * } * @endcode */ class KRITAFLAKE_EXPORT KoFrameShape { public: /** * Constructor. * * \param ns The namespace. E.g. KoXmlNS::draw * \param element The tag-name. E.g. "image" */ KoFrameShape(const QString &ns, const QString &tag); + /** + * Copy contrustor + */ + KoFrameShape(const KoFrameShape &rhs); + /** * Destructor. */ virtual ~KoFrameShape(); /** * Loads the content of the draw:frame element and it's children. This * method calls the abstract loadOdfFrameElement() method. * * @return false if loading failed */ virtual bool loadOdfFrame(const KoXmlElement &element, KoShapeLoadingContext &context); protected: /** * Abstract method to handle loading of the defined inner element like * e.g. the draw:image element. * @return false if loading failed */ virtual bool loadOdfFrameElement(const KoXmlElement &element, KoShapeLoadingContext &context) = 0; private: class Private; Private * const d; }; #endif /* KOFRAMESHAPE_H */ diff --git a/libs/flake/KoShapeManager.cpp b/libs/flake/KoShapeManager.cpp index 4ca6172dc5..361157dacd 100644 --- a/libs/flake/KoShapeManager.cpp +++ b/libs/flake/KoShapeManager.cpp @@ -1,625 +1,622 @@ /* This file is part of the KDE project Copyright (C) 2006-2008 Thorsten Zachmann Copyright (C) 2006-2010 Thomas Zander Copyright (C) 2009-2010 Jan Hambrecht This library is free software; you can redistribute it and/or modify it under the terms of the GNU 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 "KoShapeManager.h" #include "KoShapeManager_p.h" #include "KoSelection.h" #include "KoToolManager.h" #include "KoPointerEvent.h" #include "KoShape.h" #include "KoShape_p.h" #include "KoCanvasBase.h" #include "KoShapeContainer.h" #include "KoShapeStrokeModel.h" #include "KoShapeGroup.h" #include "KoToolProxy.h" #include "KoShapeShadow.h" #include "KoShapeLayer.h" #include "KoFilterEffect.h" #include "KoFilterEffectStack.h" #include "KoFilterEffectRenderContext.h" #include "KoShapeBackground.h" #include #include "KoClipPath.h" #include "KoClipMaskPainter.h" #include "KoShapePaintingContext.h" #include "KoViewConverter.h" #include "KisQPainterStateSaver.h" #include "KoSvgTextChunkShape.h" #include "KoSvgTextShape.h" #include #include #include #include "kis_painting_tweaks.h" bool KoShapeManager::Private::shapeUsedInRenderingTree(KoShape *shape) { // FIXME: make more general! return !dynamic_cast(shape) && !dynamic_cast(shape) && !(dynamic_cast(shape) && !dynamic_cast(shape)); } void KoShapeManager::Private::updateTree() { // for detecting collisions between shapes. DetectCollision detector; bool selectionModified = false; bool anyModified = false; Q_FOREACH (KoShape *shape, aggregate4update) { if (shapeIndexesBeforeUpdate.contains(shape)) detector.detect(tree, shape, shapeIndexesBeforeUpdate[shape]); selectionModified = selectionModified || selection->isSelected(shape); anyModified = true; } foreach (KoShape *shape, aggregate4update) { if (!shapeUsedInRenderingTree(shape)) continue; tree.remove(shape); QRectF br(shape->boundingRect()); tree.insert(br, shape); } // do it again to see which shapes we intersect with _after_ moving. foreach (KoShape *shape, aggregate4update) { detector.detect(tree, shape, shapeIndexesBeforeUpdate[shape]); } aggregate4update.clear(); shapeIndexesBeforeUpdate.clear(); detector.fireSignals(); if (selectionModified) { emit q->selectionContentChanged(); } if (anyModified) { emit q->contentChanged(); } } void KoShapeManager::Private::paintGroup(KoShapeGroup *group, QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) { QList shapes = group->shapes(); std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex); Q_FOREACH (KoShape *child, shapes) { // we paint recursively here, so we do not have to check recursively for visibility if (!child->isVisible()) continue; KoShapeGroup *childGroup = dynamic_cast(child); if (childGroup) { paintGroup(childGroup, painter, converter, paintContext); } else { painter.save(); KoShapeManager::renderSingleShape(child, painter, converter, paintContext); painter.restore(); } } } KoShapeManager::KoShapeManager(KoCanvasBase *canvas, const QList &shapes) : d(new Private(this, canvas)) { Q_ASSERT(d->canvas); // not optional. connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged())); setShapes(shapes); } KoShapeManager::KoShapeManager(KoCanvasBase *canvas) : d(new Private(this, canvas)) { Q_ASSERT(d->canvas); // not optional. connect(d->selection, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged())); } void KoShapeManager::Private::unlinkFromShapesRecursively(const QList &shapes) { Q_FOREACH (KoShape *shape, shapes) { shape->priv()->removeShapeManager(q); KoShapeContainer *container = dynamic_cast(shape); if (container) { unlinkFromShapesRecursively(container->shapes()); } } } KoShapeManager::~KoShapeManager() { d->unlinkFromShapesRecursively(d->shapes); d->shapes.clear(); delete d; } void KoShapeManager::setShapes(const QList &shapes, Repaint repaint) { //clear selection d->selection->deselectAll(); d->unlinkFromShapesRecursively(d->shapes); d->aggregate4update.clear(); d->tree.clear(); d->shapes.clear(); Q_FOREACH (KoShape *shape, shapes) { addShape(shape, repaint); } } void KoShapeManager::addShape(KoShape *shape, Repaint repaint) { if (d->shapes.contains(shape)) return; shape->priv()->addShapeManager(this); d->shapes.append(shape); if (d->shapeUsedInRenderingTree(shape)) { QRectF br(shape->boundingRect()); d->tree.insert(br, shape); } if (repaint == PaintShapeOnAdd) { shape->update(); } // add the children of a KoShapeContainer KoShapeContainer *container = dynamic_cast(shape); if (container) { foreach (KoShape *containerShape, container->shapes()) { addShape(containerShape, repaint); } } Private::DetectCollision detector; detector.detect(d->tree, shape, shape->zIndex()); detector.fireSignals(); } void KoShapeManager::remove(KoShape *shape) { Private::DetectCollision detector; detector.detect(d->tree, shape, shape->zIndex()); detector.fireSignals(); shape->update(); shape->priv()->removeShapeManager(this); d->selection->deselect(shape); d->aggregate4update.remove(shape); if (d->shapeUsedInRenderingTree(shape)) { d->tree.remove(shape); } d->shapes.removeAll(shape); // remove the children of a KoShapeContainer KoShapeContainer *container = dynamic_cast(shape); if (container) { foreach (KoShape *containerShape, container->shapes()) { remove(containerShape); } } } KoShapeManager::ShapeInterface::ShapeInterface(KoShapeManager *_q) : q(_q) { } void KoShapeManager::ShapeInterface::notifyShapeDestructed(KoShape *shape) { q->d->selection->deselect(shape); q->d->aggregate4update.remove(shape); // we cannot access RTTI of the semi-destructed shape, so just // unlink it lazily if (q->d->tree.contains(shape)) { q->d->tree.remove(shape); } q->d->shapes.removeAll(shape); } KoShapeManager::ShapeInterface *KoShapeManager::shapeInterface() { return &d->shapeInterface; } void KoShapeManager::paint(QPainter &painter, const KoViewConverter &converter, bool forPrint) { d->updateTree(); painter.setPen(Qt::NoPen); // painters by default have a black stroke, lets turn that off. painter.setBrush(Qt::NoBrush); QList unsortedShapes; if (painter.hasClipping()) { QRectF rect = converter.viewToDocument(KisPaintingTweaks::safeClipBoundingRect(painter)); unsortedShapes = d->tree.intersects(rect); } else { unsortedShapes = shapes(); warnFlake << "KoShapeManager::paint Painting with a painter that has no clipping will lead to too much being painted!"; } // filter all hidden shapes from the list // also filter shapes with a parent which has filter effects applied QList sortedShapes; foreach (KoShape *shape, unsortedShapes) { if (!shape->isVisible(true)) continue; bool addShapeToList = true; // check if one of the shapes ancestors have filter effects KoShapeContainer *parent = shape->parent(); while (parent) { // parent must be part of the shape manager to be taken into account if (!d->shapes.contains(parent)) break; if (parent->filterEffectStack() && !parent->filterEffectStack()->isEmpty()) { addShapeToList = false; break; } parent = parent->parent(); } if (addShapeToList) { sortedShapes.append(shape); } else if (parent) { sortedShapes.append(parent); } } std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex); KoShapePaintingContext paintContext(d->canvas, forPrint); //FIXME foreach (KoShape *shape, sortedShapes) { renderSingleShape(shape, painter, converter, paintContext); } #ifdef CALLIGRA_RTREE_DEBUG // paint tree qreal zx = 0; qreal zy = 0; converter.zoom(&zx, &zy); painter.save(); painter.scale(zx, zy); d->tree.paint(painter); painter.restore(); #endif if (! forPrint) { KoShapePaintingContext paintContext(d->canvas, forPrint); //FIXME d->selection->paint(painter, converter, paintContext); } } void KoShapeManager::renderSingleShape(KoShape *shape, QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) { KisQPainterStateSaver saver(&painter); // apply shape clipping KoClipPath::applyClipping(shape, painter, converter); // apply transformation painter.setTransform(shape->absoluteTransformation(&converter) * painter.transform()); // paint the shape paintShape(shape, painter, converter, paintContext); } void KoShapeManager::paintShape(KoShape *shape, QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) { qreal transparency = shape->transparency(true); if (transparency > 0.0) { painter.setOpacity(1.0-transparency); } if (shape->shadow()) { painter.save(); shape->shadow()->paint(shape, painter, converter); painter.restore(); } if (!shape->filterEffectStack() || shape->filterEffectStack()->isEmpty()) { QScopedPointer clipMaskPainter; QPainter *shapePainter = &painter; KoClipMask *clipMask = shape->clipMask(); if (clipMask) { clipMaskPainter.reset(new KoClipMaskPainter(&painter, shape->boundingRect())); shapePainter = clipMaskPainter->shapePainter(); } /** * We expect the shape to save/restore the painter's state itself. Such design was not * not always here, so we need a period of sanity checks to ensure all the shapes are * ported correctly. */ const QTransform sanityCheckTransformSaved = shapePainter->transform(); shape->paint(*shapePainter, converter, paintContext); shape->paintStroke(*shapePainter, converter, paintContext); KIS_SAFE_ASSERT_RECOVER(shapePainter->transform() == sanityCheckTransformSaved) { shapePainter->setTransform(sanityCheckTransformSaved); } if (clipMask) { shape->clipMask()->drawMask(clipMaskPainter->maskPainter(), shape); clipMaskPainter->renderOnGlobalPainter(); } } else { // TODO: clipping mask is not implemented for this case! // There are filter effects, then we need to prerender the shape on an image, to filter it QRectF shapeBound(QPointF(), shape->size()); // First step, compute the rectangle used for the image QRectF clipRegion = shape->filterEffectStack()->clipRectForBoundingRect(shapeBound); // convert clip region to view coordinates QRectF zoomedClipRegion = converter.documentToView(clipRegion); // determine the offset of the clipping rect from the shapes origin QPointF clippingOffset = zoomedClipRegion.topLeft(); // Initialize the buffer image QImage sourceGraphic(zoomedClipRegion.size().toSize(), QImage::Format_ARGB32_Premultiplied); sourceGraphic.fill(qRgba(0,0,0,0)); QHash imageBuffers; QSet requiredStdInputs = shape->filterEffectStack()->requiredStandarsInputs(); if (requiredStdInputs.contains("SourceGraphic") || requiredStdInputs.contains("SourceAlpha")) { // Init the buffer painter QPainter imagePainter(&sourceGraphic); imagePainter.translate(-1.0f*clippingOffset); imagePainter.setPen(Qt::NoPen); imagePainter.setBrush(Qt::NoBrush); imagePainter.setRenderHint(QPainter::Antialiasing, painter.testRenderHint(QPainter::Antialiasing)); // Paint the shape on the image KoShapeGroup *group = dynamic_cast(shape); if (group) { // the childrens matrix contains the groups matrix as well // so we have to compensate for that before painting the children imagePainter.setTransform(group->absoluteTransformation(&converter).inverted(), true); Private::paintGroup(group, imagePainter, converter, paintContext); } else { imagePainter.save(); shape->paint(imagePainter, converter, paintContext); shape->paintStroke(imagePainter, converter, paintContext); imagePainter.restore(); imagePainter.end(); } } if (requiredStdInputs.contains("SourceAlpha")) { QImage sourceAlpha = sourceGraphic; sourceAlpha.fill(qRgba(0,0,0,255)); sourceAlpha.setAlphaChannel(sourceGraphic.alphaChannel()); imageBuffers.insert("SourceAlpha", sourceAlpha); } if (requiredStdInputs.contains("FillPaint")) { QImage fillPaint = sourceGraphic; if (shape->background()) { QPainter fillPainter(&fillPaint); QPainterPath fillPath; fillPath.addRect(fillPaint.rect().adjusted(-1,-1,1,1)); shape->background()->paint(fillPainter, converter, paintContext, fillPath); } else { fillPaint.fill(qRgba(0,0,0,0)); } imageBuffers.insert("FillPaint", fillPaint); } imageBuffers.insert("SourceGraphic", sourceGraphic); imageBuffers.insert(QString(), sourceGraphic); KoFilterEffectRenderContext renderContext(converter); renderContext.setShapeBoundingBox(shapeBound); QImage result; QList filterEffects = shape->filterEffectStack()->filterEffects(); // Filter foreach (KoFilterEffect *filterEffect, filterEffects) { QRectF filterRegion = filterEffect->filterRectForBoundingRect(shapeBound); filterRegion = converter.documentToView(filterRegion); QRect subRegion = filterRegion.translated(-clippingOffset).toRect(); // set current filter region renderContext.setFilterRegion(subRegion & sourceGraphic.rect()); if (filterEffect->maximalInputCount() <= 1) { QList inputs = filterEffect->inputs(); QString input = inputs.count() ? inputs.first() : QString(); // get input image from image buffers and apply the filter effect QImage image = imageBuffers.value(input); if (!image.isNull()) { result = filterEffect->processImage(imageBuffers.value(input), renderContext); } } else { QList inputImages; Q_FOREACH (const QString &input, filterEffect->inputs()) { QImage image = imageBuffers.value(input); if (!image.isNull()) inputImages.append(imageBuffers.value(input)); } // apply the filter effect if (filterEffect->inputs().count() == inputImages.count()) result = filterEffect->processImages(inputImages, renderContext); } // store result of effect imageBuffers.insert(filterEffect->output(), result); } KoFilterEffect *lastEffect = filterEffects.last(); // Paint the result painter.save(); painter.drawImage(clippingOffset, imageBuffers.value(lastEffect->output())); painter.restore(); } } KoShape *KoShapeManager::shapeAt(const QPointF &position, KoFlake::ShapeSelection selection, bool omitHiddenShapes) { d->updateTree(); QList sortedShapes(d->tree.contains(position)); std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex); KoShape *firstUnselectedShape = 0; for (int count = sortedShapes.count() - 1; count >= 0; count--) { KoShape *shape = sortedShapes.at(count); if (omitHiddenShapes && ! shape->isVisible(true)) continue; if (! shape->hitTest(position)) continue; switch (selection) { case KoFlake::ShapeOnTop: if (shape->isSelectable()) return shape; case KoFlake::Selected: if (d->selection->isSelected(shape)) return shape; break; case KoFlake::Unselected: if (! d->selection->isSelected(shape)) return shape; break; case KoFlake::NextUnselected: // we want an unselected shape if (d->selection->isSelected(shape)) continue; // memorize the first unselected shape if (! firstUnselectedShape) firstUnselectedShape = shape; // check if the shape above is selected if (count + 1 < sortedShapes.count() && d->selection->isSelected(sortedShapes.at(count + 1))) return shape; break; } } // if we want the next unselected below a selected but there was none selected, // return the first found unselected shape if (selection == KoFlake::NextUnselected && firstUnselectedShape) return firstUnselectedShape; if (d->selection->hitTest(position)) return d->selection; return 0; // missed everything } QList KoShapeManager::shapesAt(const QRectF &rect, bool omitHiddenShapes, bool containedMode) { d->updateTree(); QList shapes(containedMode ? d->tree.contained(rect) : d->tree.intersects(rect)); for (int count = shapes.count() - 1; count >= 0; count--) { KoShape *shape = shapes.at(count); if (omitHiddenShapes && !shape->isVisible(true)) { shapes.removeAt(count); } else { const QPainterPath outline = shape->absoluteTransformation(0).map(shape->outline()); if (!containedMode && !outline.intersects(rect) && !outline.contains(rect)) { shapes.removeAt(count); } else if (containedMode) { QPainterPath containingPath; containingPath.addRect(rect); if (!containingPath.contains(outline)) { shapes.removeAt(count); } } } } return shapes; } void KoShapeManager::update(const QRectF &rect, const KoShape *shape, bool selectionHandles) { d->canvas->updateCanvas(rect); if (selectionHandles && d->selection->isSelected(shape)) { if (d->canvas->toolProxy()) d->canvas->toolProxy()->repaintDecorations(); } } void KoShapeManager::notifyShapeChanged(KoShape *shape) { Q_ASSERT(shape); if (d->aggregate4update.contains(shape)) { return; } const bool wasEmpty = d->aggregate4update.isEmpty(); d->aggregate4update.insert(shape); d->shapeIndexesBeforeUpdate.insert(shape, shape->zIndex()); KoShapeContainer *container = dynamic_cast(shape); if (container) { Q_FOREACH (KoShape *child, container->shapes()) notifyShapeChanged(child); } - // TODO: change to a KisThreadSafeSignalCompressor! - if (wasEmpty && !d->aggregate4update.isEmpty()) - QTimer::singleShot(100, this, SLOT(updateTree())); - - // TODO: remove, it is not used anymore! - emit shapeChanged(shape); + if (wasEmpty && !d->aggregate4update.isEmpty()) { + d->updateTreeCompressor.start(); + } } QList KoShapeManager::shapes() const { return d->shapes; } QList KoShapeManager::topLevelShapes() const { QList shapes; // get all toplevel shapes Q_FOREACH (KoShape *shape, d->shapes) { if (!shape->parent() || dynamic_cast(shape->parent())) { shapes.append(shape); } } return shapes; } KoSelection *KoShapeManager::selection() const { return d->selection; } KoCanvasBase *KoShapeManager::canvas() { return d->canvas; } //have to include this because of Q_PRIVATE_SLOT #include "moc_KoShapeManager.cpp" diff --git a/libs/flake/KoShapeManager.h b/libs/flake/KoShapeManager.h index 48037bc6a7..f9bbb3ddb0 100644 --- a/libs/flake/KoShapeManager.h +++ b/libs/flake/KoShapeManager.h @@ -1,215 +1,213 @@ /* This file is part of the KDE project Copyright (C) 2006-2008 Thorsten Zachmann Copyright (C) 2007, 2009 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 KOSHAPEMANAGER_H #define KOSHAPEMANAGER_H #include #include #include #include "KoFlake.h" #include "kritaflake_export.h" class KoShape; class KoSelection; class KoViewConverter; class KoCanvasBase; class KoPointerEvent; class KoShapePaintingContext; class QPainter; class QPointF; class QRectF; /** * The shape manager hold a list of all shape which are in scope. * There is one shape manager per canvas. This makes the shape manager * different from QGraphicsScene, which contains the datamodel for all * graphics items: KoShapeManager only contains the subset of shapes * that are shown in its canvas. * * The selection in the different views can be different. */ class KRITAFLAKE_EXPORT KoShapeManager : public QObject { Q_OBJECT public: /// enum for add() enum Repaint { PaintShapeOnAdd, ///< Causes each shapes 'update()' to be called after being added to the shapeManager AddWithoutRepaint ///< Avoids each shapes 'update()' to be called for faster addition when its possible. }; /** * Constructor. */ explicit KoShapeManager(KoCanvasBase *canvas); /** * Constructor that takes a list of shapes, convenience version. * @param shapes the shapes to start out with, see also setShapes() * @param canvas the canvas this shape manager is working on. */ KoShapeManager(KoCanvasBase *canvas, const QList &shapes); ~KoShapeManager() override; /** * Remove all previously owned shapes and make the argument list the new shapes * to be managed by this manager. * @param shapes the new shapes to manage. * @param repaint if true it will trigger a repaint of the shapes */ void setShapes(const QList &shapes, Repaint repaint = PaintShapeOnAdd); /// returns the list of maintained shapes QList shapes() const; /** * Get a list of all shapes that don't have a parent. */ QList topLevelShapes() const; public Q_SLOTS: /** * Add a KoShape to be displayed and managed by this manager. * This will trigger a repaint of the shape. * @param shape the shape to add * @param repaint if true it will trigger a repaint of the shape */ void addShape(KoShape *shape, KoShapeManager::Repaint repaint = PaintShapeOnAdd); /** * Remove a KoShape from this manager * @param shape the shape to remove */ void remove(KoShape *shape); public: /// return the selection shapes for this shapeManager KoSelection *selection() const; /** * Paint all shapes and their selection handles etc. * @param painter the painter to paint to. * @param forPrint if true, make sure only actual content is drawn and no decorations. * @param converter to convert between document and view coordinates. */ void paint(QPainter &painter, const KoViewConverter &converter, bool forPrint); /** * Returns the shape located at a specific point in the document. * If more than one shape is located at the specific point, the given selection type * controls which of them is returned. * @param position the position in the document coordinate system. * @param selection controls which shape is returned when more than one shape is at the specific point * @param omitHiddenShapes if true, only visible shapes are considered */ KoShape *shapeAt(const QPointF &position, KoFlake::ShapeSelection selection = KoFlake::ShapeOnTop, bool omitHiddenShapes = true); /** * Returns the shapes which intersects the specific rect in the document. * @param rect the rectangle in the document coordinate system. * @param omitHiddenShapes if true, only visible shapes are considered */ QList shapesAt(const QRectF &rect, bool omitHiddenShapes = true, bool containedMode = false); /** * Request a repaint to be queued. * The repaint will be restricted to the parameters rectangle, which is expected to be * in points (the document coordinates system of KoShape) and it is expected to be * normalized and based in the global coordinates, not any local coordinates. *

This method will return immediately and only request a repaint. Successive calls * will be merged into an appropriate repaint action. * @param rect the rectangle (in pt) to queue for repaint. * @param shape the shape that is going to be redrawn; only needed when selectionHandles=true * @param selectionHandles if true; find out if the shape is selected and repaint its * selection handles at the same time. */ void update(const QRectF &rect, const KoShape *shape = 0, bool selectionHandles = false); /** * Update the tree for finding the shapes. * This will remove the shape from the tree and will reinsert it again. * The update to the tree will be posponed until it is needed so that successive calls * will be merged into one. * @param shape the shape to updated its position in the tree. */ void notifyShapeChanged(KoShape *shape); /** * Paint a shape * * @param shape the shape to paint * @param painter the painter to paint to. * @param converter to convert between document and view coordinates. */ static void paintShape(KoShape *shape, QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext); /** * @brief renderSingleShape renders a shape on \p painter. This method includes all the * needed steps for painting a single shape: setting transformations, clipping and masking. */ static void renderSingleShape(KoShape *shape, QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext); /** * A special interface for KoShape to use during shape destruction. Don't use this * interface directly unless you are KoShape. */ struct ShapeInterface { ShapeInterface(KoShapeManager *_q); /** * Called by a shape when it is destructed. Please note that you cannot access * any shape's method type or information during this call because the shape might be * semi-destroyed. */ void notifyShapeDestructed(KoShape *shape); protected: KoShapeManager *q; }; ShapeInterface* shapeInterface(); Q_SIGNALS: /// emitted when the selection is changed void selectionChanged(); /// emitted when an object in the selection is changed (moved/rotated etc) void selectionContentChanged(); /// emitted when any object changed (moved/rotated etc) void contentChanged(); - /// emitted when any shape changed. - void shapeChanged(KoShape *); private: KoCanvasBase *canvas(); class Private; Private * const d; Q_PRIVATE_SLOT(d, void updateTree()) }; #endif diff --git a/libs/flake/KoShapeManager_p.h b/libs/flake/KoShapeManager_p.h index 51caae9d42..654ace744c 100644 --- a/libs/flake/KoShapeManager_p.h +++ b/libs/flake/KoShapeManager_p.h @@ -1,119 +1,124 @@ /* This file is part of the KDE project Copyright (C) 2006-2008 Thorsten Zachmann Copyright (C) 2006-2010 Thomas Zander Copyright (C) 2009-2010 Jan Hambrecht This library is free software; you can redistribute it and/or modify it under the terms of the GNU 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 KoShapeManager_p_h #define KoShapeManager_p_h #include "KoSelection.h" #include "KoShape.h" #include "KoShape_p.h" #include "KoShapeContainer.h" #include "KoShapeManager.h" #include +#include "kis_thread_safe_signal_compressor.h" + class KoCanvasBase; class KoShapeGroup; class KoShapePaintingContext; class QPainter; class Q_DECL_HIDDEN KoShapeManager::Private { public: Private(KoShapeManager *shapeManager, KoCanvasBase *c) : selection(new KoSelection()), canvas(c), tree(4, 2), q(shapeManager), - shapeInterface(shapeManager) + shapeInterface(shapeManager), + updateTreeCompressor(100, KisSignalCompressor::FIRST_INACTIVE) { + connect(&updateTreeCompressor, SIGNAL(timeout()), q, SLOT(updateTree())); } ~Private() { delete selection; } /** * Update the tree when there are shapes in m_aggregate4update. This is done so not all * updates to the tree are done when they are asked for but when they are needed. */ void updateTree(); /** * Returns whether the shape should be added to the RTree for collision and ROI * detection. */ bool shapeUsedInRenderingTree(KoShape *shape); /** * Recursively detach the shapes from this shape manager */ void unlinkFromShapesRecursively(const QList &shapes); /** * Recursively paints the given group shape to the specified painter * This is needed for filter effects on group shapes where the filter effect * applies to all the children of the group shape at once */ static void paintGroup(KoShapeGroup *group, QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext); class DetectCollision { public: DetectCollision() {} void detect(KoRTree &tree, KoShape *s, int prevZIndex) { Q_FOREACH (KoShape *shape, tree.intersects(s->boundingRect())) { bool isChild = false; KoShapeContainer *parent = s->parent(); while (parent && !isChild) { if (parent == shape) isChild = true; parent = parent->parent(); } if (isChild) continue; if (s->zIndex() <= shape->zIndex() && prevZIndex <= shape->zIndex()) // Moving a shape will only make it collide with shapes below it. continue; if (shape->collisionDetection() && !shapesWithCollisionDetection.contains(shape)) shapesWithCollisionDetection.append(shape); } } void fireSignals() { Q_FOREACH (KoShape *shape, shapesWithCollisionDetection) shape->priv()->shapeChanged(KoShape::CollisionDetected); } private: QList shapesWithCollisionDetection; }; QList shapes; KoSelection *selection; KoCanvasBase *canvas; KoRTree tree; QSet aggregate4update; QHash shapeIndexesBeforeUpdate; KoShapeManager *q; KoShapeManager::ShapeInterface shapeInterface; + KisThreadSafeSignalCompressor updateTreeCompressor; }; #endif diff --git a/libs/flake/KoShapeRegistry.cpp b/libs/flake/KoShapeRegistry.cpp index 04767d4c36..7b55591cb4 100644 --- a/libs/flake/KoShapeRegistry.cpp +++ b/libs/flake/KoShapeRegistry.cpp @@ -1,352 +1,570 @@ /* This file is part of the KDE project * Copyright (c) 2006 Boudewijn Rempt (boud@valdyas.org) * Copyright (C) 2006-2007, 2010 Thomas Zander * Copyright (C) 2006,2008-2010 Thorsten Zachmann * Copyright (C) 2007 Jan Hambrecht * Copyright (C) 2010 Inge Wallin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // Own #include "KoShapeRegistry.h" #include "KoSvgTextShape.h" #include "KoPathShapeFactory.h" #include "KoConnectionShapeFactory.h" #include "KoShapeLoadingContext.h" #include "KoShapeSavingContext.h" #include "KoShapeGroup.h" #include "KoShapeLayer.h" -#include "KoUnavailShape.h" #include "SvgShapeFactory.h" #include #include #include #include #include #include #include #include #include #include #include Q_GLOBAL_STATIC(KoShapeRegistry, s_instance) class Q_DECL_HIDDEN KoShapeRegistry::Private { public: void insertFactory(KoShapeFactoryBase *factory); void init(KoShapeRegistry *q); KoShape *createShapeInternal(const KoXmlElement &fullElement, KoShapeLoadingContext &context, const KoXmlElement &element) const; // Map namespace,tagname to priority:factory QHash, QMultiMap > factoryMap; }; KoShapeRegistry::KoShapeRegistry() : d(new Private()) { } KoShapeRegistry::~KoShapeRegistry() { qDeleteAll(doubleEntries()); qDeleteAll(values()); delete d; } void KoShapeRegistry::Private::init(KoShapeRegistry *q) { KoPluginLoader::PluginsConfig config; config.whiteList = "FlakePlugins"; config.blacklist = "FlakePluginsDisabled"; config.group = "calligra"; KoPluginLoader::instance()->load(QString::fromLatin1("Calligra/Flake"), QString::fromLatin1("[X-Flake-PluginVersion] == 28"), config); config.whiteList = "ShapePlugins"; config.blacklist = "ShapePluginsDisabled"; KoPluginLoader::instance()->load(QString::fromLatin1("Calligra/Shape"), QString::fromLatin1("[X-Flake-PluginVersion] == 28"), config); // Also add our hard-coded basic shapes q->add(new KoSvgTextShapeFactory()); q->add(new KoPathShapeFactory(QStringList())); q->add(new KoConnectionShapeFactory()); // As long as there is no shape dealing with embedded svg images // we add the svg shape factory here by default q->add(new SvgShapeFactory); // Now all shape factories are registered with us, determine their // associated odf tagname & priority and prepare ourselves for // loading ODF. QList factories = q->values(); for (int i = 0; i < factories.size(); ++i) { insertFactory(factories[i]); } } KoShapeRegistry* KoShapeRegistry::instance() { if (!s_instance.exists()) { s_instance->d->init(s_instance); } return s_instance; } void KoShapeRegistry::addFactory(KoShapeFactoryBase * factory) { add(factory); d->insertFactory(factory); } void KoShapeRegistry::Private::insertFactory(KoShapeFactoryBase *factory) { const QList > odfElements(factory->odfElements()); if (odfElements.isEmpty()) { debugFlake << "Shape factory" << factory->id() << " does not have OdfNamespace defined, ignoring"; } else { int priority = factory->loadingPriority(); for (QList >::const_iterator it(odfElements.begin()); it != odfElements.end(); ++it) { foreach (const QString &elementName, (*it).second) { QPair p((*it).first, elementName); QMultiMap & priorityMap = factoryMap[p]; priorityMap.insert(priority, factory); debugFlake << "Inserting factory" << factory->id() << " for" << p << " with priority " << priority << " into factoryMap making " << priorityMap.size() << " entries. "; } } } } +#include +#include +#include +#include + +namespace { + +struct ObjectEntry { + ObjectEntry() + { + } + + ObjectEntry(const ObjectEntry &rhs) + : objectXmlContents(rhs.objectXmlContents), + objectName(rhs.objectName), + isDir(rhs.isDir) + { + } + + ~ObjectEntry() + { + } + + QByteArray objectXmlContents; // the XML tree in the object + QString objectName; // object name in the frame without "./" + // This is extracted from objectXmlContents. + bool isDir = false; +}; + +// A FileEntry is used to store information about embedded files +// inside (i.e. referred to by) an object. +struct FileEntry { + FileEntry() {} + FileEntry(const FileEntry &rhs) + : path(rhs.path), + mimeType(rhs.mimeType), + isDir(rhs.isDir), + contents(rhs.contents) + { + } + + QString path; // Normalized filename, i.e. without "./". + QString mimeType; + bool isDir; + QByteArray contents; +}; + +QByteArray loadFile(const QString &fileName, KoShapeLoadingContext &context) +{ + // Can't load a file which is a directory, return an invalid QByteArray + if (fileName.endsWith('/')) + return QByteArray(); + + KoStore *store = context.odfLoadingContext().store(); + QByteArray fileContent; + + if (!store->open(fileName)) { + store->close(); + return QByteArray(); + } + + int fileSize = store->size(); + fileContent = store->read(fileSize); + store->close(); + + //debugFlake << "File content: " << fileContent; + return fileContent; +} + + +boost::optional storeFile(const QString &fileName, KoShapeLoadingContext &context) +{ + debugFlake << "Saving file: " << fileName; + + boost::optional result; + + QByteArray fileContent = loadFile(fileName, context); + if (!fileContent.isNull()) { + + // Actually store the file in the list. + FileEntry entry; + entry.path = fileName; + if (entry.path.startsWith(QLatin1String("./"))) { + entry.path.remove(0, 2); + } + entry.mimeType = context.odfLoadingContext().mimeTypeForPath(entry.path); + entry.isDir = false; + entry.contents = fileContent; + + result = entry; + } + + return result; +} + +void storeXmlRecursive(const KoXmlElement &el, KoXmlWriter &writer, + ObjectEntry *object, QHash &unknownNamespaces) +{ + // Start the element; + // keep the name in a QByteArray so that it stays valid until end element is called. + const QByteArray name(el.nodeName().toLatin1()); + writer.startElement(name.constData()); + + // Child elements + // Loop through all the child elements of the draw:frame. + KoXmlNode n = el.firstChild(); + for (; !n.isNull(); n = n.nextSibling()) { + if (n.isElement()) { + storeXmlRecursive(n.toElement(), writer, object, unknownNamespaces); + } + else if (n.isText()) { + writer.addTextNode(n.toText().data()/*.toUtf8()*/); + } + } + + // End the element + writer.endElement(); +} + + +QVector storeObjects(const KoXmlElement &element) +{ + QVector result; + + // Loop through all the child elements of the draw:frame and save them. + KoXmlNode n = element.firstChild(); + for (; !n.isNull(); n = n.nextSibling()) { + debugFlake << "In draw:frame, node =" << n.nodeName(); + + // This disregards #text, but that's not in the spec anyway so + // it doesn't need to be saved. + if (!n.isElement()) + continue; + KoXmlElement el = n.toElement(); + + ObjectEntry object; + + QByteArray contentsTmp; + QBuffer buffer(&contentsTmp); // the member + KoXmlWriter writer(&buffer); + + // 1. Find out the objectName + // Save the normalized filename, i.e. without a starting "./". + // An empty string is saved if no name is found. + QString name = el.attributeNS(KoXmlNS::xlink, "href", QString()); + if (name.startsWith(QLatin1String("./"))) + name.remove(0, 2); + object.objectName = name; + + // 2. Copy the XML code. + QHash unknownNamespaces; + storeXmlRecursive(el, writer, &object, unknownNamespaces); + object.objectXmlContents = contentsTmp; + + // 3, 4: the isDir and manifestEntry members are not set here, + // but initialize them anyway. . + object.isDir = false; // Has to be initialized to something. + + result.append(object); + } + + return result; +} +} + +#include +#include "kis_debug.h" +#include +#include +#include +#include +#include + + KoShape * KoShapeRegistry::createShapeFromOdf(const KoXmlElement & e, KoShapeLoadingContext & context) const { debugFlake << "Going to check for" << e.namespaceURI() << ":" << e.tagName(); KoShape * shape = 0; // Handle the case where the element is a draw:frame differently from other cases. if (e.tagName() == "frame" && e.namespaceURI() == KoXmlNS::draw) { // If the element is in a frame, the frame is already added by the // application and we only want to create a shape from the // embedded element. The very first shape we create is accepted. // // FIXME: we might want to have some code to determine which is // the "best" of the creatable shapes. - // The logic is thus: - // First attempt to check whether we can in fact load the first child, - // and only use a Shape if the first child is accepted. If this is not the case, then - // use the UnavailShape which ensures data integrity and that the fallback views are not - // edited and subsequently saved back (as they would then no longer be a true - // representation of the data they are supposed to be views of). - // The reason is that all subsequent children will be fallbacks, in order of preference. - if (e.hasChildNodes()) { // if we don't ignore white spaces it can be that the first child is not a element so look for the first element KoXmlNode node = e.firstChild(); KoXmlElement element; while (!node.isNull() && element.isNull()) { element = node.toElement(); node = node.nextSibling(); } if (!element.isNull()) { // Check for draw:object if (element.tagName() == "object" && element.namespaceURI() == KoXmlNS::draw && element.hasChildNodes()) { // Loop through the elements and find the first one // that is handled by any shape. KoXmlNode n = element.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { if (n.isElement()) { debugFlake << "trying for element " << n.toElement().tagName(); shape = d->createShapeInternal(e, context, n.toElement()); break; } } if (shape) debugFlake << "Found a shape for draw:object"; else debugFlake << "Found NO shape shape for draw:object"; } else { // If not draw:object, e.g draw:image or draw:plugin shape = d->createShapeInternal(e, context, element); } } if (shape) { debugFlake << "A shape supporting the requested type was found."; } else { // If none of the registered shapes could handle the frame - // contents, create an UnavailShape. This should never fail. - debugFlake << "No shape found; Creating an unavail shape"; - - KoUnavailShape *uShape = new KoUnavailShape(); - uShape->setShapeId(KoUnavailShape_SHAPEID); - //FIXME: Add creating/setting the collection here(?) - uShape->loadOdf(e, context); - - // Check whether we can load a shape to fit the current object. - KoXmlElement child; - KoShape *childShape = 0; - bool first = true; - forEachElement(child, e) { - // no need to try to load the first element again as it was already tried before and we could not load it - if (first) { - first = false; - continue; - } - debugFlake << "--------------------------------------------------------"; - debugFlake << "Attempting to check if we can fall back ability to the item" - << child.nodeName(); - childShape = d->createShapeInternal(e, context, child); - if (childShape) { - debugFlake << "Shape was found! Adding as child of unavail shape and stopping search"; - uShape->addShape(childShape); - childShape->setPosition(QPointF(qreal(0.0), qreal(0.0))); - - // The embedded shape is just there to show the preview image. - // We don't want the user to be able to manipulate the picture - // in any way, so we disable the tools of the shape. This can - // be done in a hacky way (courtesy of Jaham) by setting its - // shapeID to "". - childShape->setShapeId(QString()); - break; + // contents, try to fetch SVG it from an embedded link + + const KoXmlElement &frameElement = e; + const int frameZIndex = SvgShapeFactory::calculateZIndex(frameElement, context); + + QList resultShapes; + + QVector objects = storeObjects(frameElement); + Q_FOREACH (const ObjectEntry &object, objects) { + if (object.objectName.isEmpty()) continue; + + boost::optional file = storeFile(object.objectName, context); + if (file && !file->contents.isEmpty()) { + QMimeDatabase db; + QMimeType mime = db.mimeTypeForData(file->contents); + + const int zIndex = SvgShapeFactory::calculateZIndex(element, context); + + if (mime.inherits("image/svg+xml")) { + + + KoXmlDocument xmlDoc; + + int line, col; + QString errormessage; + const bool parsed = xmlDoc.setContent(file->contents, &errormessage, &line, &col); + if (!parsed) continue; + + const QRectF bounds = context.documentResourceManager()->shapeController()->documentRectInPixels(); + + // WARNING: Krita 3.x expects all the embedded objects to + // be loaded in default resolution of 72.0 ppi. + // Don't change it to the correct data in the image, + // it will change back compatibility (and this code will + // be deprecated some time soon + const qreal pixelsPerInch = 72.0; + + QPointF pos; + pos.setX(KoUnit::parseValue(frameElement.attributeNS(KoXmlNS::svg, "x", QString::number(bounds.x())))); + pos.setY(KoUnit::parseValue(frameElement.attributeNS(KoXmlNS::svg, "y", QString::number(bounds.y())))); + + QSizeF size; + size.setWidth(KoUnit::parseValue(frameElement.attributeNS(KoXmlNS::svg, "width", QString::number(bounds.width())))); + size.setHeight(KoUnit::parseValue(frameElement.attributeNS(KoXmlNS::svg, "height", QString::number(bounds.height())))); + + KoShape *shape = SvgShapeFactory::createShapeFromSvgDirect(xmlDoc.documentElement(), + QRectF(pos, size), + pixelsPerInch, + zIndex, + context); + + if (shape) { + // NOTE: here we are expected to stretch the internal to the bounds of + // the frame! Sounds weird, but it is what Krita 3.x did. + + const QRectF shapeRect = shape->absoluteOutlineRect(0); + const QPointF offset = shapeRect.topLeft(); + const QSizeF fragmentSize = shapeRect.size(); + + if (fragmentSize.isValid()) { + /** + * Yes, you see what you see. The previous versions of Krita used + * QSvgRenderer to render the object, which allegedly truncated the + * object on sides. Even though we don't use pre-rendering now, + * we should still reproduce the old way... + */ + const QSizeF newSize = QSizeF(int(size.width()), int(size.height())); + + shape->applyAbsoluteTransformation( + QTransform::fromTranslate(-offset.x(), -offset.y()) * + QTransform::fromScale( + newSize.width() / fragmentSize.width(), + newSize.height() / fragmentSize.height()) * + QTransform::fromTranslate(pos.x(), pos.y())); + resultShapes.append(shape); + } + } + + } else { + // TODO: implement raster images? + } } } - if (!childShape) - debugFlake << "Failed to find fallback for the unavail shape named " - << e.tagName(); - shape = uShape; + + if (resultShapes.size() == 1) { + shape = resultShapes.takeLast(); + } else if (resultShapes.size() > 1) { + KoShapeGroup *groupShape = new KoShapeGroup; + KoShapeGroupCommand cmd(groupShape, resultShapes); + cmd.redo(); + groupShape->setZIndex(frameZIndex); + shape = groupShape; + } } } } // Hardwire the group shape into the loading as it should not appear // in the shape selector else if (e.localName() == "g" && e.namespaceURI() == KoXmlNS::draw) { KoShapeGroup * group = new KoShapeGroup(); context.odfLoadingContext().styleStack().save(); bool loaded = group->loadOdf(e, context); context.odfLoadingContext().styleStack().restore(); if (loaded) { shape = group; } else { delete group; } } else { shape = d->createShapeInternal(e, context, e); } if (shape) { context.shapeLoaded(shape); } return shape; } KoShape *KoShapeRegistry::Private::createShapeInternal(const KoXmlElement &fullElement, KoShapeLoadingContext &context, const KoXmlElement &element) const { // Pair of namespace, tagname QPair p = QPair(element.namespaceURI(), element.tagName()); // Remove duplicate lookup. if (!factoryMap.contains(p)) return 0; QMultiMap priorityMap = factoryMap.value(p); QList factories = priorityMap.values(); #ifndef NDEBUG debugFlake << "Supported factories for=" << p; foreach (KoShapeFactoryBase *f, factories) debugFlake << f->id() << f->name(); #endif // Loop through all shape factories. If any of them supports this // element, then we let the factory create a shape from it. This // may fail because the element itself is too generic to draw any // real conclusions from it - we actually have to try to load it. // An example of this is the draw:image element which have // potentially hundreds of different image formats to support, // including vector formats. // // If it succeeds, then we use this shape, if it fails, then just // try the next. // // Higher numbers are more specific, map is sorted by keys. for (int i = factories.size() - 1; i >= 0; --i) { KoShapeFactoryBase * factory = factories[i]; if (factory->supports(element, context)) { KoShape *shape = factory->createShapeFromOdf(fullElement, context); if (shape) { debugFlake << "Shape found for factory " << factory->id() << factory->name(); // we return the top-level most shape as that's the one that we'll have to // add to the KoShapeManager for painting later (and also to avoid memory leaks) // but don't go past a KoShapeLayer as KoShape adds those from the context // during loading and those are already added. while (shape->parent() && dynamic_cast(shape->parent()) == 0) shape = shape->parent(); return shape; } // Maybe a shape with a lower priority can load our // element, but this attempt has failed. } else { debugFlake << "No support for" << p << "by" << factory->id(); } } return 0; } QList KoShapeRegistry::factoriesForElement(const QString &nameSpace, const QString &elementName) { // Pair of namespace, tagname QPair p = QPair(nameSpace, elementName); QMultiMap priorityMap = d->factoryMap.value(p); QList shapeFactories; // sort list by priority Q_FOREACH (KoShapeFactoryBase *f, priorityMap.values()) { shapeFactories.prepend(f); } return shapeFactories; } diff --git a/libs/flake/KoUnavailShape.cpp b/libs/flake/KoUnavailShape.cpp deleted file mode 100644 index b4c87d9e30..0000000000 --- a/libs/flake/KoUnavailShape.cpp +++ /dev/null @@ -1,629 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2010-2011 Inge Wallin - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - - -// Own -#include "KoUnavailShape.h" - -// Qt -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Calligra -#include -#include -#include -#include -#include -#include -#include -#include -#include "KoShapeLoadingContext.h" -#include "KoShapeSavingContext.h" -#include "SimpleShapeContainerModel.h" -#include "KoShapeBackground.h" -#include "KisQPainterStateSaver.h" - -#include - - -// The XML of a frame looks something like this: -// -// 1. -// 2. -// 3. -// 4. -// -// or -// -// 1. -// 2. ...inline xml here... -// 3. -// 4. -// -// We define each Xml statement on lines 2 and 3 above as an "object". -// (Strictly only the first child element is an object in the ODF sense, -// but we have to have some terminology here.) -// -// In an ODF frame, only the first line, i.e. the first object -// contains the real contents. All the rest of the objects are used / -// shown if we cannot handle the first one. The most common cases are -// that there is only one object inside the frame OR that there are 2 -// and the 2nd is a picture. -// -// Sometimes, e.g. in the case of an embedded document, the reference -// points not to a file but to a directory structure inside the ODF -// store. -// -// When we load and save in the UnavailShape, we have to be general -// enough to cover all possible cases of references and inline XML, -// embedded files and embedded directory structures. -// -// We also have to be careful because we cannot reuse the object names -// that are in the original files when saving. Instead we need to -// create new object names because the ones that were used in the -// original file may already be used by other embedded files/objects -// that are saved by other shapes. -// -// FIXME: There should only be ONE place where new object / file names -// are generated, not 2(?) like there are now: -// KoEmbeddedDocumentSaver and the KoImageCollection. -// - - -// An ObjectEntry is used to store information about objects in the -// frame, as defined above. -struct ObjectEntry { - QByteArray objectXmlContents; // the XML tree in the object - QString objectName; // object name in the frame without "./" - // This is extracted from objectXmlContents. - bool isDir; - KoOdfManifestEntry *manifestEntry; // manifest entry for the above. -}; - -// A FileEntry is used to store information about embedded files -// inside (i.e. referred to by) an object. -struct FileEntry { - QString path; // Normalized filename, i.e. without "./". - QString mimeType; - bool isDir; - QByteArray contents; -}; - - -class KoUnavailShape::Private -{ -public: - Private(KoUnavailShape* qq); - ~Private(); - - void draw(QPainter &painter) const; - void drawNull(QPainter &painter) const; - - void storeObjects(const KoXmlElement &element); - void storeXmlRecursive(const KoXmlElement &el, KoXmlWriter &writer, - ObjectEntry *object, QHash &unknownNamespaces); - void storeFile(const QString &filename, KoShapeLoadingContext &context); - QByteArray loadFile(const QString &filename, KoShapeLoadingContext &context); - - // Objects inside the frame. For each file, we store: - // - The XML code for the object - // - Any embedded files (names, contents) that are referenced by xlink:href - // - Whether they are directories, i.e. if they contain a file tree and not just one file. - // - The manifest entries - QList objectEntries; - - // Embedded files - QList embeddedFiles; // List of embedded files. - - // Some cached values. - QPixmap questionMark; - QPixmap pixmapPreview; - QSvgRenderer *scalablePreview; - - KoUnavailShape* q; -}; - -KoUnavailShape::Private::Private(KoUnavailShape* qq) -: scalablePreview(new QSvgRenderer()) -, q(qq) -{ - // Get the question mark "icon". - questionMark.load(":/questionmark.png"); -} - -KoUnavailShape::Private::~Private() -{ - qDeleteAll(objectEntries); - qDeleteAll(embeddedFiles); - - // It's a QObject, but we haven't parented it. - delete(scalablePreview); -} - - -// ---------------------------------------------------------------- -// The main class - - -KoUnavailShape::KoUnavailShape() -: KoFrameShape( "", "" ) -, KoShapeContainer(new SimpleShapeContainerModel()) -, d(new Private(this)) -{ - setShapeId(KoUnavailShape_SHAPEID); - - // Default size of the shape. - KoShape::setSize( QSizeF( CM_TO_POINT( 5 ), CM_TO_POINT( 3 ) ) ); -} - -KoUnavailShape::~KoUnavailShape() -{ - delete d; -} - -void KoUnavailShape::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) -{ - KisQPainterStateSaver saver(&painter); - applyConversion(painter, converter); - - // If the frame is empty, just draw a background. - debugFlake << "Number of objects:" << d->objectEntries.size(); - if (d->objectEntries.isEmpty()) { - // But... only try to draw the background if there's one such - if (background()) { - QPainterPath p; - p.addRect(QRectF(QPointF(), size())); - background()->paint(painter, converter, paintContext, p); - } - } else { - if(shapes().isEmpty()) { - d->draw(painter); - } - } -} - -void KoUnavailShape::paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &) -{ - Q_UNUSED(painter); - Q_UNUSED(converter); -} - -void KoUnavailShape::Private::draw(QPainter &painter) const -{ - painter.save(); - painter.setRenderHint(QPainter::Antialiasing); - // Run through the previews in order of preference. Draw a placeholder - // questionmark if there is no preview available for rendering. - if (scalablePreview->isValid()) { - QRect bounds(0, 0, q->boundingRect().width(), q->boundingRect().height()); - scalablePreview->render(&painter, bounds); - } - else if (!pixmapPreview.isNull()) { - QRect bounds(0, 0, q->boundingRect().width(), q->boundingRect().height()); - painter.setRenderHint(QPainter::SmoothPixmapTransform); - painter.drawPixmap(bounds, pixmapPreview); - } - else if (q->shapes().isEmpty()) { - // Draw a nice question mark with a frame around it if there - // is no other preview image. If there is a contained image - // shape, we don't need to draw anything. - - // Get the question mark "icon". - // FIXME: We should be able to use d->questionMark here. - QPixmap questionMark; - questionMark.load(":/questionmark.png"); - - // The size of the image is: - // - the size of the shape if shapesize < 2cm - // - 2 cm if 2cm <= shapesize <= 8cm - // - shapesize / 4 if shapesize > 8cm - qreal width = q->size().width(); - qreal height = q->size().height(); - qreal picSize = CM_TO_POINT(2); // Default size is 2 cm. - if (width < CM_TO_POINT(2) || height < CM_TO_POINT(2)) - picSize = qMin(width, height); - else if (width > CM_TO_POINT(8) && height > CM_TO_POINT(8)) - picSize = qMin(width, height) / qreal(4.0); - - painter.drawPixmap((width - picSize) / qreal(2.0), (height - picSize) / qreal(2.0), - picSize, picSize, questionMark); - - // Draw a gray rectangle around the shape. - painter.setPen(QPen(QColor(172, 196, 206), 0)); - painter.drawRect(QRectF(QPointF(0,0), q->size())); - - } - painter.restore(); -} - -void KoUnavailShape::Private::drawNull(QPainter &painter) const -{ - QRectF rect(QPointF(0,0), q->size()); - painter.save(); - - // Draw a simple cross in a rectangle just to indicate that there is something here. - painter.drawLine(rect.topLeft(), rect.bottomRight()); - painter.drawLine(rect.bottomLeft(), rect.topRight()); - - painter.restore(); -} - - -// ---------------------------------------------------------------- -// Loading and Saving - - -void KoUnavailShape::saveOdf(KoShapeSavingContext & context) const -{ - debugFlake << "START SAVING ##################################################"; - - KoEmbeddedDocumentSaver &fileSaver = context.embeddedSaver(); - KoXmlWriter &writer = context.xmlWriter(); - - writer.startElement("draw:frame"); - - // See also loadOdf() in loadOdfAttributes. - saveOdfAttributes( context, OdfAllAttributes ); - - // Write the stored XML to the file, but don't reuse object names. - int lap = 0; - QString newName; - foreach (const ObjectEntry *object, d->objectEntries) { - QByteArray xmlArray(object->objectXmlContents); - QString objectName(object->objectName); // Possibly empty. - KoOdfManifestEntry *manifestEntry(object->manifestEntry); - - // Create a name for this object. If this is not the first - // object, i.e. a replacement object (most likely a picture), - // then reuse the name but put it in ReplacementObjects. - if (++lap == 1) { - // The first lap in the loop is the actual object. All - // other laps are replacement objects. - newName = fileSaver.getFilename("Object "); - } - else if (lap == 2) { - newName = "ObjectReplacements/" + newName; - } - else - // FIXME: what should replacement 2 and onwards be called? - newName = newName + "_"; - - // If there was a previous object name, replace it with the new one. - if (!objectName.isEmpty() && manifestEntry) { - // FIXME: We must make a copy of the byte array here because - // otherwise we won't be able to save > 1 time. - xmlArray.replace(objectName.toLatin1(), newName.toLatin1()); - } - - writer.addCompleteElement(xmlArray.data()); - - // If the objectName is empty, this may be inline XML. - // If so, we are done now. - if (objectName.isEmpty() || !manifestEntry) { - continue; - } - - // Save embedded files for this object. - foreach (FileEntry *entry, d->embeddedFiles) { - QString fileName(entry->path); - - // If we found a file for this object, we need to write it - // but with the new object name instead of the old one. - if (!fileName.startsWith(objectName)) - continue; - - debugFlake << "Object name: " << objectName << "newName: " << newName - << "filename: " << fileName << "isDir: " << entry->isDir; - - fileName.replace(objectName, newName); - fileName.prepend("./"); - debugFlake << "New filename: " << fileName; - - // FIXME: Check if we need special treatment of directories. - fileSaver.saveFile(fileName, entry->mimeType.toLatin1(), entry->contents); - } - - // Write the manifest entry for the object itself. If it's a - // file, the manifest is already written by saveFile, so skip - // it here. - if (object->isDir) { - fileSaver.saveManifestEntry(newName + '/', manifestEntry->mediaType(), - manifestEntry->version()); - } - } - - writer.endElement(); // draw:frame -} - - -bool KoUnavailShape::loadOdf(const KoXmlElement &frameElement, KoShapeLoadingContext &context) -{ - debugFlake << "START LOADING ##################################################"; - //debugFlake << "Loading ODF frame in the KoUnavailShape. Element = " - // << frameElement.tagName(); - - loadOdfAttributes(frameElement, context, OdfAllAttributes); - - // NOTE: We cannot use loadOdfFrame() because we want to save all - // the things inside the frame, not just one of them, like - // loadOdfFrame() provides. - - // Get the manifest. - QList manifest = context.odfLoadingContext().manifestEntries(); - -#if 0 // Enable to show all manifest entries. - debugFlake << "MANIFEST: "; - foreach (KoOdfManifestEntry *entry, manifest) { - debugFlake << entry->mediaType() << entry->fullPath() << entry->version(); - } -#endif - - // 1. Get the XML contents of the objects from the draw:frame. As - // a side effect, this extracts the object names from all - // xlink:href and stores them into d->objectNames. The saved - // xml contents itself is saved into d->objectXmlContents - // (QByteArray) so we can save it back from saveOdf(). - d->storeObjects(frameElement); - -#if 1 - // Debug only - debugFlake << "----------------------------------------------------------------"; - debugFlake << "After storeObjects():"; - foreach (ObjectEntry *object, d->objectEntries) { - debugFlake << "objectXmlContents: " << object->objectXmlContents - << "objectName: " << object->objectName; - // Note: at this point, isDir and manifestEntry are not set. -#endif - } - - // 2. Loop through the objects that were found in the frame and - // save all the files associated with them. Some of the - // objects are files, and some are directories. The - // directories are searched and the files within are saved as - // well. - // - // In this loop, isDir and manifestEntry of each ObjectEntry are set. - bool foundPreview = false; - foreach (ObjectEntry *object, d->objectEntries) { - QString objectName = object->objectName; - - if (objectName.isEmpty()) - continue; - - debugFlake << "Storing files for object named:" << objectName; - - // Try to find out if the entry is a directory. - // If the object is a directory, then save all the files - // inside it, otherwise save the file as it is. - QString dirName = objectName + '/'; - bool isDir = !context.odfLoadingContext().mimeTypeForPath(dirName).isEmpty(); - if (isDir) { - // A directory: the files can be found in the manifest. - foreach (KoOdfManifestEntry *entry, manifest) { - if (entry->fullPath() == dirName) - continue; - - if (entry->fullPath().startsWith(dirName)) { - d->storeFile(entry->fullPath(), context); - } - } - } - else { - // A file: save it. - d->storeFile(objectName, context); - } - - // Get the manifest entry for this object. - KoOdfManifestEntry *entry = 0; - QString entryName = isDir ? dirName : objectName; - for (int j = 0; j < manifest.size(); ++j) { - KoOdfManifestEntry *temp = manifest.value(j); - - if (temp->fullPath() == entryName) { - entry = new KoOdfManifestEntry(*temp); - break; - } - } - object->isDir = isDir; - object->manifestEntry = entry; - - // If we have not already found a preview in previous times - // through the loop, then see if this one may be a preview. - if (!foundPreview) { - debugFlake << "Attempting to load preview from " << objectName; - QByteArray previewData = d->loadFile(objectName, context); - // Check to see if we know the mimetype for this entry. Specifically: - // 1. Check to see if the item is a loadable SVG file - - // FIXME: Perhaps check in the manifest first? But this - // seems to work well. - d->scalablePreview->load(previewData); - if (d->scalablePreview->isValid()) { - debugFlake << "Found scalable preview image!"; - d->scalablePreview->setViewBox(d->scalablePreview->boundsOnElement("svg")); - foundPreview = true; - continue; - } - // 2. Otherwise check to see if it's a loadable pixmap file - d->pixmapPreview.loadFromData(previewData); - if (!d->pixmapPreview.isNull()) { - debugFlake << "Found pixel based preview image!"; - foundPreview = true; - } - } - } - -#if 0 // Enable to get more detailed debug messages - debugFlake << "Object manifest entries:"; - for (int i = 0; i < d->manifestEntries.size(); ++i) { - KoOdfManifestEntry *entry = d->manifestEntries.value(i); - debugFlake << i << ":" << entry; - if (entry) - debugFlake << entry->fullPath() << entry->mediaType() << entry->version(); - else - debugFlake << "--"; - } - debugFlake << "END LOADING ####################################################"; -#endif - - return true; -} - - -// Load the actual contents inside the frame. -bool KoUnavailShape::loadOdfFrameElement(const KoXmlElement & /*element*/, - KoShapeLoadingContext &/*context*/) -{ - return true; -} - - -// ---------------------------------------------------------------- -// Private functions - -void KoUnavailShape::Private::storeObjects(const KoXmlElement &element) -{ - // Loop through all the child elements of the draw:frame and save them. - KoXmlNode n = element.firstChild(); - for (; !n.isNull(); n = n.nextSibling()) { - debugFlake << "In draw:frame, node =" << n.nodeName(); - - // This disregards #text, but that's not in the spec anyway so - // it doesn't need to be saved. - if (!n.isElement()) - continue; - KoXmlElement el = n.toElement(); - - ObjectEntry *object = new ObjectEntry; - - QByteArray contentsTmp; - QBuffer buffer(&contentsTmp); // the member - KoXmlWriter writer(&buffer); - - // 1. Find out the objectName - // Save the normalized filename, i.e. without a starting "./". - // An empty string is saved if no name is found. - QString name = el.attributeNS(KoXmlNS::xlink, "href", QString()); - if (name.startsWith(QLatin1String("./"))) - name.remove(0, 2); - object->objectName = name; - - // 2. Copy the XML code. - QHash unknownNamespaces; - storeXmlRecursive(el, writer, object, unknownNamespaces); - object->objectXmlContents = contentsTmp; - - // 3, 4: the isDir and manifestEntry members are not set here, - // but initialize them anyway. . - object->isDir = false; // Has to be initialized to something. - object->manifestEntry = 0; - - objectEntries.append(object); - } -} - -void KoUnavailShape::Private::storeXmlRecursive(const KoXmlElement &el, KoXmlWriter &writer, - ObjectEntry *object, QHash &unknownNamespaces) -{ - // Start the element; - // keep the name in a QByteArray so that it stays valid until end element is called. - const QByteArray name(el.nodeName().toLatin1()); - writer.startElement(name.constData()); - - // Child elements - // Loop through all the child elements of the draw:frame. - KoXmlNode n = el.firstChild(); - for (; !n.isNull(); n = n.nextSibling()) { - if (n.isElement()) { - storeXmlRecursive(n.toElement(), writer, object, unknownNamespaces); - } - else if (n.isText()) { - writer.addTextNode(n.toText().data()/*.toUtf8()*/); - } - } - - // End the element - writer.endElement(); -} - -/** - * This function stores the embedded file in an internal store - it does not save files to disk, - * and thus it is named in this manner, to avoid the function being confused with functions which - * save files to disk. - */ -void KoUnavailShape::Private::storeFile(const QString &fileName, KoShapeLoadingContext &context) -{ - debugFlake << "Saving file: " << fileName; - - // Directories need to be saved too, but they don't have any file contents. - if (fileName.endsWith('/')) { - FileEntry *entry = new FileEntry; - entry->path = fileName; - entry->mimeType = context.odfLoadingContext().mimeTypeForPath(entry->path); - entry->isDir = true; - embeddedFiles.append(entry); - } - - QByteArray fileContent = loadFile(fileName, context); - if (fileContent.isNull()) - return; - - // Actually store the file in the list. - FileEntry *entry = new FileEntry; - entry->path = fileName; - if (entry->path.startsWith(QLatin1String("./"))) - entry->path.remove(0, 2); - entry->mimeType = context.odfLoadingContext().mimeTypeForPath(entry->path); - entry->isDir = false; - entry->contents = fileContent; - embeddedFiles.append(entry); - - debugFlake << "File length: " << fileContent.size(); -} - -QByteArray KoUnavailShape::Private::loadFile(const QString &fileName, KoShapeLoadingContext &context) -{ - // Can't load a file which is a directory, return an invalid QByteArray - if (fileName.endsWith('/')) - return QByteArray(); - - KoStore *store = context.odfLoadingContext().store(); - QByteArray fileContent; - - if (!store->open(fileName)) { - store->close(); - return QByteArray(); - } - - int fileSize = store->size(); - fileContent = store->read(fileSize); - store->close(); - - //debugFlake << "File content: " << fileContent; - return fileContent; -} diff --git a/libs/flake/KoUnavailShape.h b/libs/flake/KoUnavailShape.h deleted file mode 100644 index 16e5977aa2..0000000000 --- a/libs/flake/KoUnavailShape.h +++ /dev/null @@ -1,76 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2010 Inge Wallin - * - * 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 KOUNAVAILSHAPE_H -#define KOUNAVAILSHAPE_H - - -// Calligra -#include -#include - - -class QPainter; - -#define KoUnavailShape_SHAPEID "UnavailShapeID" - - -/** - * The KoUnavailShape is a frame shape that takes care of all frame - * based objects that are not handled by any of the shape plugins. - * - * The KoUnavailShape stores the data associated with the frame, even - * if this data is stored in embedded files inside the ODF container. - * To the user, it shows an empty frame with an indicator that there - * is an object here. If a preview of some type is available, the - * Unavail shape will attempt to load them read-only, in a fallback - * manner and show them in that frame. If no shape is found at all - * which supports any of the fallbacks, it will show a placeholder - * graphic, to indicate the fact that it is an unknown item. - * - * The KoUnavailShape always has to be present, and is the only shape - * that is not implemented as a plugin. - */ -class KoUnavailShape : public KoFrameShape, public KoShapeContainer { -public: - KoUnavailShape(); - ~KoUnavailShape() override; - - // Inherited methods - - /// reimplemented - void paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext) override; - /// reimplemented from KoShapeContainer - void paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) override; - /// reimplemented - void saveOdf(KoShapeSavingContext & context) const override; - /// reimplemented - bool loadOdf( const KoXmlElement & element, KoShapeLoadingContext &context ) override; - /// Load the real contents of the frame shape. - bool loadOdfFrameElement(const KoXmlElement& frameElement, - KoShapeLoadingContext& context) override; - -private: - class Private; - Private * const d; -}; - -#endif diff --git a/libs/flake/commands/KoShapeReorderCommand.cpp b/libs/flake/commands/KoShapeReorderCommand.cpp index b0ffdf56e8..e4bda3207d 100644 --- a/libs/flake/commands/KoShapeReorderCommand.cpp +++ b/libs/flake/commands/KoShapeReorderCommand.cpp @@ -1,225 +1,323 @@ /* 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 "KoShapeReorderCommand.h" #include "KoShape.h" #include "KoShape_p.h" #include "KoShapeManager.h" #include "KoShapeContainer.h" #include #include #include +KoShapeReorderCommand::IndexedShape::IndexedShape() +{ +} + +KoShapeReorderCommand::IndexedShape::IndexedShape(KoShape *_shape) + : zIndex(_shape->zIndex()), shape(_shape) +{ +} + class KoShapeReorderCommandPrivate { public: + KoShapeReorderCommandPrivate() {} KoShapeReorderCommandPrivate(const QList &s, QList &ni) : shapes(s), newIndexes(ni) { } QList shapes; QList previousIndexes; QList newIndexes; }; KoShapeReorderCommand::KoShapeReorderCommand(const QList &shapes, QList &newIndexes, KUndo2Command *parent) : KUndo2Command(parent), - d(new KoShapeReorderCommandPrivate(shapes, newIndexes)) + d(new KoShapeReorderCommandPrivate(shapes, newIndexes)) { Q_ASSERT(shapes.count() == newIndexes.count()); foreach (KoShape *shape, shapes) d->previousIndexes.append(shape->zIndex()); setText(kundo2_i18n("Reorder shapes")); } +KoShapeReorderCommand::KoShapeReorderCommand(const QList &shapes, KUndo2Command *parent) + : KUndo2Command(parent), + d(new KoShapeReorderCommandPrivate()) +{ + Q_FOREACH (const IndexedShape &index, shapes) { + d->shapes.append(index.shape); + d->newIndexes.append(index.zIndex); + d->previousIndexes.append(index.shape->zIndex()); + } + + setText(kundo2_i18n("Reorder shapes")); +} + KoShapeReorderCommand::~KoShapeReorderCommand() { delete d; } void KoShapeReorderCommand::redo() { KUndo2Command::redo(); for (int i = 0; i < d->shapes.count(); i++) { - d->shapes.at(i)->update(); + // z-index cannot chage the bounding rect of the shape, so + // no united updates needed d->shapes.at(i)->setZIndex(d->newIndexes.at(i)); d->shapes.at(i)->update(); } } void KoShapeReorderCommand::undo() { KUndo2Command::undo(); for (int i = 0; i < d->shapes.count(); i++) { - d->shapes.at(i)->update(); + // z-index cannot chage the bounding rect of the shape, so + // no united updates needed d->shapes.at(i)->setZIndex(d->previousIndexes.at(i)); d->shapes.at(i)->update(); } } static void prepare(KoShape *s, QMap > &newOrder, KoShapeManager *manager, KoShapeReorderCommand::MoveShapeType move) { KoShapeContainer *parent = s->parent(); QMap >::iterator it(newOrder.find(parent)); if (it == newOrder.end()) { QList children; if (parent != 0) { children = parent->shapes(); } else { // get all toplevel shapes children = manager->topLevelShapes(); } std::sort(children.begin(), children.end(), KoShape::compareShapeZIndex); // the append and prepend are needed so that the raise/lower of all shapes works as expected. children.append(0); children.prepend(0); it = newOrder.insert(parent, children); } QList & shapes(newOrder[parent]); int index = shapes.indexOf(s); if (index != -1) { shapes.removeAt(index); switch (move) { case KoShapeReorderCommand::BringToFront: index = shapes.size(); break; case KoShapeReorderCommand::RaiseShape: if (index < shapes.size()) { ++index; } break; case KoShapeReorderCommand::LowerShape: if (index > 0) { --index; } break; case KoShapeReorderCommand::SendToBack: index = 0; break; } shapes.insert(index,s); } } // static KoShapeReorderCommand *KoShapeReorderCommand::createCommand(const QList &shapes, KoShapeManager *manager, MoveShapeType move, KUndo2Command *parent) { /** * TODO: this method doesn't handle the case when one of the shapes * has maximum or minimum zIndex value (which is 16-bit in our case)! */ QList newIndexes; QList changedShapes; QMap > newOrder; QList sortedShapes(shapes); std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex); if (move == BringToFront || move == LowerShape) { for (int i = 0; i < sortedShapes.size(); ++i) { prepare(sortedShapes.at(i), newOrder, manager, move); } } else { for (int i = sortedShapes.size() - 1; i >= 0; --i) { prepare(sortedShapes.at(i), newOrder, manager, move); } } QMap >::iterator newIt(newOrder.begin()); for (; newIt!= newOrder.end(); ++newIt) { QList order(newIt.value()); order.removeAll(0); int index = -KoShape::maxZIndex - 1; // set minimum zIndex int pos = 0; for (; pos < order.size(); ++pos) { if (order[pos]->zIndex() > index) { index = order[pos]->zIndex(); } else { break; } } if (pos == order.size()) { //nothing needs to be done continue; } else if (pos <= order.size() / 2) { // new index for the front int startIndex = order[pos]->zIndex() - pos; for (int i = 0; i < pos; ++i) { changedShapes.append(order[i]); newIndexes.append(startIndex++); } } else { //new index for the end for (int i = pos; i < order.size(); ++i) { changedShapes.append(order[i]); newIndexes.append(++index); } } } Q_ASSERT(changedShapes.count() == newIndexes.count()); return changedShapes.isEmpty() ? 0: new KoShapeReorderCommand(changedShapes, newIndexes, parent); } KoShapeReorderCommand *KoShapeReorderCommand::mergeInShape(QList shapes, KoShape *newShape, KUndo2Command *parent) { std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex); QList reindexedShapes; QList reindexedIndexes; const int originalShapeZIndex = newShape->zIndex(); int newShapeZIndex = originalShapeZIndex; int lastOccupiedShapeZIndex = originalShapeZIndex + 1; Q_FOREACH (KoShape *shape, shapes) { if (shape == newShape) continue; const int zIndex = shape->zIndex(); if (newShapeZIndex == originalShapeZIndex) { if (zIndex == originalShapeZIndex) { newShapeZIndex = originalShapeZIndex + 1; lastOccupiedShapeZIndex = newShapeZIndex; reindexedShapes << newShape; reindexedIndexes << newShapeZIndex; } } else { if (newShapeZIndex != originalShapeZIndex && zIndex >= newShapeZIndex && zIndex <= lastOccupiedShapeZIndex) { lastOccupiedShapeZIndex = zIndex + 1; reindexedShapes << shape; reindexedIndexes << lastOccupiedShapeZIndex; } } } return !reindexedShapes.isEmpty() ? new KoShapeReorderCommand(reindexedShapes, reindexedIndexes, parent) : 0; } + +namespace { + +QList +homogenizeZIndexes(QList shapes) +{ + if (shapes.isEmpty()) return shapes; + + // the shapes are expected to be sorted, we just need to adjust the indexes + + int lastIndex = shapes.begin()->zIndex; + + auto it = shapes.begin() + 1; + while (it != shapes.end()) { + if (it->zIndex <= lastIndex) { + it->zIndex = lastIndex + 1; + } + lastIndex = it->zIndex; + ++it; + } + + const int overflowSize = shapes.last().zIndex - int(std::numeric_limits::max()); + + if (overflowSize > 0) { + if (shapes.first().zIndex - overflowSize > int(std::numeric_limits::min())) { + for (auto it = shapes.begin(); it != shapes.end(); ++it) { + it->zIndex -= overflowSize; + } + } else { + int index = shapes.size() < int(std::numeric_limits::max()) ? + 0 : + int(std::numeric_limits::max()) - shapes.size(); + + for (auto it = shapes.begin(); it != shapes.end(); ++it) { + it->zIndex = index; + index++; + } + } + } + + return shapes; +} + +} + +QList +KoShapeReorderCommand::mergeDownShapes(QList shapesBelow, QList shapesAbove) +{ + std::sort(shapesBelow.begin(), shapesBelow.end(), KoShape::compareShapeZIndex); + std::sort(shapesAbove.begin(), shapesAbove.end(), KoShape::compareShapeZIndex); + + QList shapes; + Q_FOREACH (KoShape *shape, shapesBelow) { + shapes.append(IndexedShape(shape)); + } + + Q_FOREACH (KoShape *shape, shapesAbove) { + shapes.append(IndexedShape(shape)); + } + + shapes = homogenizeZIndexes(shapes); + + // remove shapes that didn't change + for (auto it = shapes.begin(); it != shapes.end();) { + if (it->zIndex == it->shape->zIndex()) { + it = shapes.erase(it); + } else { + ++it; + } + } + + return shapes; +} diff --git a/libs/flake/commands/KoShapeReorderCommand.h b/libs/flake/commands/KoShapeReorderCommand.h index 1ea442c11d..1a04b59844 100644 --- a/libs/flake/commands/KoShapeReorderCommand.h +++ b/libs/flake/commands/KoShapeReorderCommand.h @@ -1,89 +1,106 @@ /* 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. */ #ifndef KOSHAPEREORDERCOMMAND_H #define KOSHAPEREORDERCOMMAND_H #include "kritaflake_export.h" #include #include class KoShape; class KoShapeManager; class KoShapeReorderCommandPrivate; /// This command allows you to change the zIndex of a number of shapes. class KRITAFLAKE_EXPORT KoShapeReorderCommand : public KUndo2Command { +public: + struct IndexedShape { + IndexedShape(); + IndexedShape(KoShape *_shape); + + int zIndex = 0; + KoShape *shape = 0; + }; + + public: /** * Constructor. * @param shapes the set of objects that are moved. * @param newIndexes the new indexes for the shapes. * this list naturally must have the same amount of items as the shapes set. * @param parent the parent command used for macro commands */ KoShapeReorderCommand(const QList &shapes, QList &newIndexes, KUndo2Command *parent = 0); + KoShapeReorderCommand(const QList &shapes, KUndo2Command *parent = 0); ~KoShapeReorderCommand() override; /// An enum for defining what kind of reordering to use. enum MoveShapeType { RaiseShape, ///< raise the selected shape to the level that it is above the shape that is on top of it. LowerShape, ///< Lower the selected shape to the level that it is below the shape that is below it. BringToFront, ///< Raise the selected shape to be on top of all shapes. SendToBack ///< Lower the selected shape to be below all other shapes. }; /** * Create a new KoShapeReorderCommand by calculating the new indexes required to move the shapes * according to the move parameter. * @param shapes all the shapes that should be moved. * @param manager the shapeManager that contains all the shapes that could have their indexes changed. * @param move the moving type. * @param parent the parent command for grouping purposes. * @return command for reording the shapes or 0 if no reordering happened */ static KoShapeReorderCommand *createCommand(const QList &shapes, KoShapeManager *manager, MoveShapeType move, KUndo2Command *parent = 0); /** * @brief mergeInShape adjust zIndex of all the \p shapes and \p newShape to * avoid collisions between \p shapes and \p newShape. * * Note1: \p newShape may or may not be contained in \p shapes, there * is no difference. * Note2: the collisions inside \p shapes are ignored. They are just * adjusted to avoid collisions with \p newShape only * @param parent the parent command for grouping purposes. * @return command for reording the shapes or 0 if no reordering happened */ static KoShapeReorderCommand *mergeInShape(QList shapes, KoShape *newShape, KUndo2Command *parent = 0); + /** + * Put all the shapes in \p shapesAbove above the shapes in \p shapesBelow, adjusting their + * z-index values. + */ + static QList mergeDownShapes(QList shapesBelow, QList shapesAbove); + /// redo the command void redo() override; /// revert the actions done in redo void undo() override; private: KoShapeReorderCommandPrivate * const d; }; #endif diff --git a/libs/flake/commands/KoShapeShadowCommand.cpp b/libs/flake/commands/KoShapeShadowCommand.cpp index 60fe2e8bd6..5f57e9a2fe 100644 --- a/libs/flake/commands/KoShapeShadowCommand.cpp +++ b/libs/flake/commands/KoShapeShadowCommand.cpp @@ -1,125 +1,135 @@ /* This file is part of the KDE project * 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. */ #include "KoShapeShadowCommand.h" #include "KoShape.h" #include "KoShapeShadow.h" #include class Q_DECL_HIDDEN KoShapeShadowCommand::Private { public: Private() {} ~Private() { Q_FOREACH (KoShapeShadow* shadow, oldShadows) { if (shadow && !shadow->deref()) delete shadow; } } void addOldShadow(KoShapeShadow * oldShadow) { if (oldShadow) oldShadow->ref(); oldShadows.append(oldShadow); } void addNewShadow(KoShapeShadow * newShadow) { if (newShadow) newShadow->ref(); newShadows.append(newShadow); } QList shapes; ///< the shapes to set shadow for QList oldShadows; ///< the old shadows, one for each shape QList newShadows; ///< the new shadows to set }; KoShapeShadowCommand::KoShapeShadowCommand(const QList &shapes, KoShapeShadow *shadow, KUndo2Command *parent) : KUndo2Command(parent) , d(new Private()) { d->shapes = shapes; // save old shadows Q_FOREACH (KoShape *shape, d->shapes) { d->addOldShadow(shape->shadow()); d->addNewShadow(shadow); } setText(kundo2_i18n("Set Shadow")); } KoShapeShadowCommand::KoShapeShadowCommand(const QList &shapes, const QList &shadows, KUndo2Command *parent) : KUndo2Command(parent) , d(new Private()) { Q_ASSERT(shapes.count() == shadows.count()); d->shapes = shapes; // save old shadows Q_FOREACH (KoShape *shape, shapes) d->addOldShadow(shape->shadow()); Q_FOREACH (KoShapeShadow * shadow, shadows) d->addNewShadow(shadow); setText(kundo2_i18n("Set Shadow")); } KoShapeShadowCommand::KoShapeShadowCommand(KoShape* shape, KoShapeShadow *shadow, KUndo2Command *parent) : KUndo2Command(parent) , d(new Private()) { d->shapes.append(shape); d->addNewShadow(shadow); d->addOldShadow(shape->shadow()); setText(kundo2_i18n("Set Shadow")); } KoShapeShadowCommand::~KoShapeShadowCommand() { delete d; } void KoShapeShadowCommand::redo() { KUndo2Command::redo(); int shapeCount = d->shapes.count(); for (int i = 0; i < shapeCount; ++i) { KoShape *shape = d->shapes[i]; - shape->update(); - shape->setShadow(d->newShadows[i]); - shape->update(); + + // TODO: implement comparison operator for KoShapeShadow + // (or just deprecate it entirely) + if (shape->shadow() || d->newShadows[i]) { + const QRectF oldBoundingRect = shape->boundingRect(); + shape->setShadow(d->newShadows[i]); + shape->updateAbsolute(oldBoundingRect | shape->boundingRect()); + } } } void KoShapeShadowCommand::undo() { KUndo2Command::undo(); int shapeCount = d->shapes.count(); for (int i = 0; i < shapeCount; ++i) { KoShape *shape = d->shapes[i]; - shape->update(); - shape->setShadow(d->oldShadows[i]); - shape->update(); + + // TODO: implement comparison operator for KoShapeShadow + // (or just deprecate it entirely) + if (shape->shadow() || d->oldShadows[i]) { + const QRectF oldBoundingRect = shape->boundingRect(); + shape->setShadow(d->oldShadows[i]); + shape->updateAbsolute(oldBoundingRect | shape->boundingRect()); + } } } diff --git a/libs/flake/svg/SvgShapeFactory.cpp b/libs/flake/svg/SvgShapeFactory.cpp index 65165a8cce..551977f8e6 100644 --- a/libs/flake/svg/SvgShapeFactory.cpp +++ b/libs/flake/svg/SvgShapeFactory.cpp @@ -1,148 +1,166 @@ /* This file is part of the KDE project * 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 "SvgShapeFactory.h" #include "SvgParser.h" #include "KoShapeGroup.h" #include "KoShapeGroupCommand.h" #include "KoShapeLoadingContext.h" #include "KoShapeRegistry.h" #include "FlakeDebug.h" #include #include #include #include #include #define SVGSHAPEFACTORYID "SvgShapeFactory" SvgShapeFactory::SvgShapeFactory() : KoShapeFactoryBase(SVGSHAPEFACTORYID, i18n("Embedded svg shape")) { setLoadingPriority(4); setXmlElementNames(QString(KoXmlNS::draw), QStringList("image")); // hide from add shapes docker as the shape is not able to be dragged onto // the canvas as createDefaultShape returns 0. setHidden(true); } SvgShapeFactory::~SvgShapeFactory() { } void SvgShapeFactory::addToRegistry() { KoShapeRegistry *registry = KoShapeRegistry::instance(); if (!registry->contains(QString(SVGSHAPEFACTORYID))) { registry->addFactory(new SvgShapeFactory); } } bool SvgShapeFactory::supports(const KoXmlElement &element, KoShapeLoadingContext &context) const { if (element.localName() == "image" && element.namespaceURI() == KoXmlNS::draw) { QString href = element.attribute("href"); if (href.isEmpty()) return false; // check the mimetype if (href.startsWith(QLatin1String("./"))) { href.remove(0,2); } QString mimetype = context.odfLoadingContext().mimeTypeForPath(href, true); return (mimetype == "image/svg+xml"); } return false; } KoShape *SvgShapeFactory::createShapeFromOdf(const KoXmlElement &element, KoShapeLoadingContext &context) { const KoXmlElement & imageElement(KoXml::namedItemNS(element, KoXmlNS::draw, "image")); if (imageElement.isNull()) { errorFlake << "svg image element not found"; return 0; } if (imageElement.tagName() == "image") { debugFlake << "trying to create shapes form svg image"; QString href = imageElement.attribute("href"); if (href.isEmpty()) return 0; // check the mimetype if (href.startsWith(QLatin1String("./"))) { href.remove(0,2); } QString mimetype = context.odfLoadingContext().mimeTypeForPath(href); debugFlake << mimetype; if (mimetype != "image/svg+xml") return 0; if (!context.odfLoadingContext().store()->open(href)) return 0; KoStoreDevice dev(context.odfLoadingContext().store()); KoXmlDocument xmlDoc; int line, col; QString errormessage; const bool parsed = xmlDoc.setContent(&dev, &errormessage, &line, &col); context.odfLoadingContext().store()->close(); if (! parsed) { errorFlake << "Error while parsing file: " << "at line " << line << " column: " << col << " message: " << errormessage << endl; return 0; } - SvgParser parser(context.documentResourceManager()); + const int zIndex = calculateZIndex(element, context); + return createShapeFromSvgDirect(xmlDoc.documentElement(), QRect(0,0,30,30), 72.0, zIndex, context); + } - QList shapes = parser.parseSvg(xmlDoc.documentElement()); - if (shapes.isEmpty()) - return 0; + return 0; +} - int zIndex = 0; - if (element.hasAttributeNS(KoXmlNS::draw, "z-index")) { - zIndex = element.attributeNS(KoXmlNS::draw, "z-index").toInt(); - } else { - zIndex = context.zIndex(); - } +int SvgShapeFactory::calculateZIndex(const KoXmlElement &element, KoShapeLoadingContext &context) +{ + int zIndex = 0; - if (shapes.count() == 1) { - KoShape *shape = shapes.first(); - shape->setZIndex(zIndex); - return shape; - } + if (element.hasAttributeNS(KoXmlNS::draw, "z-index")) { + zIndex = element.attributeNS(KoXmlNS::draw, "z-index").toInt(); + } else { + zIndex = context.zIndex(); + } - KoShapeGroup *svgGroup = new KoShapeGroup; - KoShapeGroupCommand cmd(svgGroup, shapes); - cmd.redo(); - svgGroup->setZIndex(zIndex); + return zIndex; +} + +KoShape *SvgShapeFactory::createShapeFromSvgDirect(const KoXmlElement &root, + const QRectF &boundsInPixels, + const qreal pixelsPerInch, + int zIndex, + KoShapeLoadingContext &context, + QSizeF *fragmentSize) +{ + SvgParser parser(context.documentResourceManager()); + parser.setResolution(boundsInPixels, pixelsPerInch); - return svgGroup; + QList shapes = parser.parseSvg(root, fragmentSize); + if (shapes.isEmpty()) + return 0; + + if (shapes.count() == 1) { + KoShape *shape = shapes.first(); + shape->setZIndex(zIndex); + return shape; } - return 0; + KoShapeGroup *svgGroup = new KoShapeGroup; + KoShapeGroupCommand cmd(svgGroup, shapes); + cmd.redo(); + svgGroup->setZIndex(zIndex); + + return svgGroup; } diff --git a/libs/flake/svg/SvgShapeFactory.h b/libs/flake/svg/SvgShapeFactory.h index 28a097b42d..d7ef75898a 100644 --- a/libs/flake/svg/SvgShapeFactory.h +++ b/libs/flake/svg/SvgShapeFactory.h @@ -1,42 +1,45 @@ /* This file is part of the KDE project * 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 SVGSHAPEFACTORY_H #define SVGSHAPEFACTORY_H #include "kritaflake_export.h" #include "KoShapeFactoryBase.h" /// Use this shape factory to load embedded svg files from odf class KRITAFLAKE_EXPORT SvgShapeFactory : public KoShapeFactoryBase { public: SvgShapeFactory(); ~SvgShapeFactory() override; // reimplemented from KoShapeFactoryBase bool supports(const KoXmlElement &element, KoShapeLoadingContext &context) const override; // reimplemented from KoShapeFactoryBase KoShape *createShapeFromOdf(const KoXmlElement &element, KoShapeLoadingContext &context) override; + static int calculateZIndex(const KoXmlElement &element, KoShapeLoadingContext &context); + static KoShape *createShapeFromSvgDirect(const KoXmlElement &root, const QRectF &boundsInPixels, const qreal pixelsPerInch, int zIndex, KoShapeLoadingContext &context, QSizeF *fragmentSize = 0); + /// Adds an instance of this factory to the shape registry, if not already registered static void addToRegistry(); }; #endif // SVGSHAPEFACTORY_H diff --git a/libs/flake/tests/TestKoShapeRegistry.cpp b/libs/flake/tests/TestKoShapeRegistry.cpp index 43857e522e..8bd1f74005 100644 --- a/libs/flake/tests/TestKoShapeRegistry.cpp +++ b/libs/flake/tests/TestKoShapeRegistry.cpp @@ -1,142 +1,213 @@ /* This file is part of the KDE project * Copyright (c) 2007 Boudewijn Rempt (boud@valdyas.org) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "TestKoShapeRegistry.h" #include #include #include #include #include #include #include #include #include #include #include "KoShapeRegistry.h" #include "KoShape.h" #include "KoPathShape.h" #include "KoShapeLoadingContext.h" #include #include +#include "kis_debug.h" + void TestKoShapeRegistry::testGetKoShapeRegistryInstance() { KoShapeRegistry * registry = KoShapeRegistry::instance(); QVERIFY(registry != 0); } void TestKoShapeRegistry::testCreateShapes() { QBuffer xmldevice; xmldevice.open(QIODevice::WriteOnly); QTextStream xmlstream(&xmldevice); xmlstream.setCodec("UTF-8"); xmlstream << ""; xmlstream << ""; xmlstream << ""; xmlstream << ""; xmlstream << ""; xmlstream << ""; xmlstream << ""; xmlstream << ""; xmldevice.close(); KoXmlDocument doc; QString errorMsg; int errorLine = 0; int errorColumn = 0; QCOMPARE(doc.setContent(&xmldevice, true, &errorMsg, &errorLine, &errorColumn), true); QCOMPARE(errorMsg.isEmpty(), true); QCOMPARE(errorLine, 0); QCOMPARE(errorColumn, 0); KoXmlElement contentElement = doc.documentElement(); KoXmlElement bodyElement = contentElement.firstChild().toElement(); KoShapeRegistry * registry = KoShapeRegistry::instance(); // XXX: When loading is implemented, these no doubt have to be // sensibly filled. KoOdfStylesReader stylesReader; KoOdfLoadingContext odfContext(stylesReader, 0); KoShapeLoadingContext shapeContext(odfContext, 0); KoShape * shape = registry->createShapeFromOdf(bodyElement, shapeContext); QVERIFY(shape == 0); KoXmlElement pathElement = bodyElement.firstChild().firstChild().toElement(); shape = registry->createShapeFromOdf(pathElement, shapeContext); QVERIFY(shape != 0); QVERIFY(shape->shapeId() == KoPathShapeId); } void TestKoShapeRegistry::testCreateFramedShapes() { QBuffer xmldevice; xmldevice.open(QIODevice::WriteOnly); QTextStream xmlstream(&xmldevice); xmlstream.setCodec("UTF-8"); xmlstream << ""; xmlstream << ""; xmlstream << ""; xmlstream << ""; xmlstream << ""; xmlstream << ""; xmlstream << ""; xmlstream << ""; xmldevice.close(); KoXmlDocument doc; QString errorMsg; int errorLine = 0; int errorColumn = 0; QCOMPARE(doc.setContent(&xmldevice, true, &errorMsg, &errorLine, &errorColumn), true); QCOMPARE(errorMsg.isEmpty(), true); QCOMPARE(errorLine, 0); QCOMPARE(errorColumn, 0); KoXmlElement contentElement = doc.documentElement(); KoXmlElement bodyElement = contentElement.firstChild().toElement(); KoShapeRegistry * registry = KoShapeRegistry::instance(); // XXX: When loading is implemented, these no doubt have to be // sensibly filled. KoOdfStylesReader stylesReader; KoOdfLoadingContext odfContext(stylesReader, 0); KoShapeLoadingContext shapeContext(odfContext, 0); KoShape * shape = registry->createShapeFromOdf(bodyElement, shapeContext); QVERIFY(shape == 0); KoXmlElement pathElement = bodyElement.firstChild().firstChild().toElement(); shape = registry->createShapeFromOdf(pathElement, shapeContext); QVERIFY(shape != 0); QVERIFY(shape->shapeId() == KoPathShapeId); } +#include +#include +#include +#include +#include "../../sdk/tests/qimage_test_util.h" + +void TestKoShapeRegistry::testFramedSvgShapes() +{ + QBuffer xmldevice; + xmldevice.open(QIODevice::WriteOnly); + QTextStream xmlstream(&xmldevice); + xmlstream.setCodec("UTF-8"); + + xmlstream << ""; + xmlstream << ""; + xmlstream << ""; + xmlstream << ""; + xmlstream << ""; + xmlstream << " "; + xmlstream << ""; + xmlstream << ""; + xmlstream << ""; + xmlstream << ""; + xmldevice.close(); + + KoXmlDocument doc; + QString errorMsg; + int errorLine = 0; + int errorColumn = 0; + + QCOMPARE(doc.setContent(&xmldevice, true, &errorMsg, &errorLine, &errorColumn), true); + QCOMPARE(errorMsg.isEmpty(), true); + QCOMPARE(errorLine, 0); + QCOMPARE(errorColumn, 0); + + KoXmlElement contentElement = doc.documentElement(); + KoXmlElement bodyElement = contentElement.firstChild().toElement(); + + KoShapeRegistry * registry = KoShapeRegistry::instance(); + + // XXX: When loading is implemented, these no doubt have to be + // sensibly filled. + KoOdfStylesReader stylesReader; + + const QString resourcesBlob = TestUtil::fetchDataFileLazy("odf_frame_resource_store.zip"); + QScopedPointer store(KoStore::createStore(resourcesBlob, KoStore::Read, "krita", KoStore::Zip)); + QScopedPointer resourceManager(new KoDocumentResourceManager()); + + + QScopedPointer document(new MockShapeController()); + QScopedPointer canvas(new MockCanvas(document.data())); + + QScopedPointer shapeController(new KoShapeController(canvas.data(), document.data())); + resourceManager->setShapeController(shapeController.data()); + + + KoOdfLoadingContext odfContext(stylesReader, store.data()); + KoShapeLoadingContext shapeContext(odfContext, resourceManager.data()); + + KoXmlElement frameElement = bodyElement.firstChild().firstChild().toElement(); + + QCOMPARE(frameElement.tagName(), QString("frame")); + + KoShape *shape = registry->createShapeFromOdf(frameElement, shapeContext); + + QVERIFY(shape); + QCOMPARE(shape->absoluteOutlineRect(0), QRectF(83, 41, 226,141)); +} + QTEST_MAIN(TestKoShapeRegistry) diff --git a/libs/flake/tests/TestKoShapeRegistry.h b/libs/flake/tests/TestKoShapeRegistry.h index 6bbbc5f462..c101380d1d 100644 --- a/libs/flake/tests/TestKoShapeRegistry.h +++ b/libs/flake/tests/TestKoShapeRegistry.h @@ -1,36 +1,36 @@ /* This file is part of the KDE project * Copyright (c) 2007 Boudewijn Rempt (boud@valdyas.org) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef TestKoShapeRegistry_H #define TestKoShapeRegistry_H #include class TestKoShapeRegistry : public QObject { Q_OBJECT private Q_SLOTS: // tests void testGetKoShapeRegistryInstance(); void testCreateShapes(); void testCreateFramedShapes(); - + void testFramedSvgShapes(); }; #endif diff --git a/libs/flake/tests/data/odf_frame_resource_store.zip b/libs/flake/tests/data/odf_frame_resource_store.zip new file mode 100644 index 0000000000..7a561706cb Binary files /dev/null and b/libs/flake/tests/data/odf_frame_resource_store.zip differ diff --git a/libs/global/CMakeLists.txt b/libs/global/CMakeLists.txt index 219c5148f4..e8823c59c2 100644 --- a/libs/global/CMakeLists.txt +++ b/libs/global/CMakeLists.txt @@ -1,51 +1,52 @@ add_subdirectory( tests ) include(CheckFunctionExists) check_function_exists(backtrace HAVE_BACKTRACE) configure_file(config-debug.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-debug.h) option(HAVE_MEMORY_LEAK_TRACKER "Enable memory leak tracker (always disabled in release build)" OFF) option(HAVE_BACKTRACE_SUPPORT "Enable recording of backtrace in memory leak tracker" OFF) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-memory-leak-tracker.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-memory-leak-tracker.h) ### WRONG PLACE??? set(kritaglobal_LIB_SRCS kis_assert.cpp kis_debug.cpp kis_algebra_2d.cpp kis_memory_leak_tracker.cpp kis_shared.cpp kis_dom_utils.cpp kis_painting_tweaks.cpp KisHandlePainterHelper.cpp KisHandleStyle.cpp kis_relaxed_timer.cpp kis_signal_compressor.cpp kis_signal_compressor_with_param.cpp + kis_thread_safe_signal_compressor.cpp kis_acyclic_signal_connector.cpp kis_latency_tracker.cpp KisQPainterStateSaver.cpp KisSharedThreadPoolAdapter.cpp KisSharedRunnable.cpp KisRollingMeanAccumulatorWrapper.cpp KisLoggingManager.cpp ) add_library(kritaglobal SHARED ${kritaglobal_LIB_SRCS} ) generate_export_header(kritaglobal BASE_NAME kritaglobal) target_link_libraries(kritaglobal PUBLIC Qt5::Concurrent Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Xml KF5::I18n ) set_target_properties(kritaglobal PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaglobal ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/global/kis_algebra_2d.h b/libs/global/kis_algebra_2d.h index 8ac904ee19..f9bc7f2327 100644 --- a/libs/global/kis_algebra_2d.h +++ b/libs/global/kis_algebra_2d.h @@ -1,587 +1,606 @@ /* * Copyright (c) 2014 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_ALGEBRA_2D_H #define __KIS_ALGEBRA_2D_H #include #include #include #include #include #include #include #include #include class QPainterPath; class QTransform; namespace KisAlgebra2D { template struct PointTypeTraits { }; template <> struct PointTypeTraits { typedef int value_type; typedef qreal calculation_type; typedef QRect rect_type; }; template <> struct PointTypeTraits { typedef qreal value_type; typedef qreal calculation_type; typedef QRectF rect_type; }; template typename PointTypeTraits::value_type dotProduct(const T &a, const T &b) { return a.x() * b.x() + a.y() * b.y(); } template typename PointTypeTraits::value_type crossProduct(const T &a, const T &b) { return a.x() * b.y() - a.y() * b.x(); } template qreal norm(const T &a) { return std::sqrt(pow2(a.x()) + pow2(a.y())); } template Point normalize(const Point &a) { const qreal length = norm(a); return (1.0 / length) * a; } /** * Usual sign() function with positive zero */ template T signPZ(T x) { return x >= T(0) ? T(1) : T(-1); } /** * Usual sign() function with zero returning zero */ template T signZZ(T x) { return x == T(0) ? T(0) : x > T(0) ? T(1) : T(-1); } /** * Copies the sign of \p y into \p x and returns the result */ template inline T copysign(T x, T y) { T strippedX = qAbs(x); return y >= T(0) ? strippedX : -strippedX; } +template +typename std::enable_if::value, T>::type +divideFloor(T a, T b) +{ + const bool a_neg = a < T(0); + const bool b_neg = b < T(0); + + if (a == T(0)) { + return 0; + } else if (a_neg == b_neg) { + return a / b; + } else { + const T a_abs = qAbs(a); + const T b_abs = qAbs(b); + + return - 1 - (a_abs - T(1)) / b_abs; + } +} + template T leftUnitNormal(const T &a) { T result = a.x() != 0 ? T(-a.y() / a.x(), 1) : T(-1, 0); qreal length = norm(result); result *= (crossProduct(a, result) >= 0 ? 1 : -1) / length; return -result; } template T rightUnitNormal(const T &a) { return -leftUnitNormal(a); } template T inwardUnitNormal(const T &a, int polygonDirection) { return polygonDirection * leftUnitNormal(a); } /** * \return 1 if the polygon is counterclockwise * -1 if the polygon is clockwise * * Note: the sign is flipped because our 0y axis * is reversed */ template int polygonDirection(const QVector &polygon) { typename PointTypeTraits::value_type doubleSum = 0; const int numPoints = polygon.size(); for (int i = 1; i <= numPoints; i++) { int prev = i - 1; int next = i == numPoints ? 0 : i; doubleSum += (polygon[next].x() - polygon[prev].x()) * (polygon[next].y() + polygon[prev].y()); } return doubleSum >= 0 ? 1 : -1; } template bool isInRange(T x, T a, T b) { T length = qAbs(a - b); return qAbs(x - a) <= length && qAbs(x - b) <= length; } void KRITAGLOBAL_EXPORT adjustIfOnPolygonBoundary(const QPolygonF &poly, int polygonDirection, QPointF *pt); /** * Let \p pt, \p base1 are two vectors. \p base1 is uniformly scaled * and then rotated into \p base2 using transformation matrix S * * R. The function applies the same transformation to \pt and returns * the result. **/ QPointF KRITAGLOBAL_EXPORT transformAsBase(const QPointF &pt, const QPointF &base1, const QPointF &base2); qreal KRITAGLOBAL_EXPORT angleBetweenVectors(const QPointF &v1, const QPointF &v2); /** * Computes an angle indicating the direction from p1 to p2. If p1 and p2 are too close together to * compute an angle, defaultAngle is returned. */ qreal KRITAGLOBAL_EXPORT directionBetweenPoints(const QPointF &p1, const QPointF &p2, qreal defaultAngle); namespace Private { inline void resetEmptyRectangle(const QPoint &pt, QRect *rc) { *rc = QRect(pt, QSize(1, 1)); } inline void resetEmptyRectangle(const QPointF &pt, QRectF *rc) { static const qreal eps = 1e-10; *rc = QRectF(pt, QSizeF(eps, eps)); } } template inline void accumulateBounds(const Point &pt, Rect *bounds) { if (bounds->isEmpty()) { Private::resetEmptyRectangle(pt, bounds); } if (pt.x() > bounds->right()) { bounds->setRight(pt.x()); } if (pt.x() < bounds->left()) { bounds->setLeft(pt.x()); } if (pt.y() > bounds->bottom()) { bounds->setBottom(pt.y()); } if (pt.y() < bounds->top()) { bounds->setTop(pt.y()); } } template

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

"; } else { warning += " Reasons:

"; } warning += "

    "; Q_FOREACH(const QString &w, warnings) { warning += "\n
  • " + w + "
  • "; } warning += "
"; browser->setHtml(warning); browser->setMinimumHeight(200); browser->setMinimumWidth(400); layout->addWidget(browser); setMainWidget(page); setButtons(KoDialog::Ok); resize(minimumSize()); } }; bool KisDocument::openFile() { //dbgUI <<"for" << localFilePath(); if (!QFile::exists(localFilePath())) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("File %1 does not exist.", localFilePath())); return false; } QString filename = localFilePath(); QString typeName = mimeType(); if (typeName.isEmpty()) { typeName = KisMimeDatabase::mimeTypeForFile(filename); } //qDebug() << "mimetypes 4:" << typeName; // Allow to open backup files, don't keep the mimetype application/x-trash. if (typeName == "application/x-trash") { QString path = filename; while (path.length() > 0) { path.chop(1); typeName = KisMimeDatabase::mimeTypeForFile(path); //qDebug() << "\t" << path << typeName; if (!typeName.isEmpty()) { break; } } //qDebug() << "chopped" << filename << "to" << path << "Was trash, is" << typeName; } dbgUI << localFilePath() << "type:" << typeName; KisMainWindow *window = KisPart::instance()->currentMainwindow(); if (window && window->viewManager()) { KoUpdaterPtr updater = window->viewManager()->createUnthreadedUpdater(i18n("Opening document")); d->importExportManager->setUpdater(updater); } KisImportExportFilter::ConversionStatus status; status = d->importExportManager->importDocument(localFilePath(), typeName); if (status != KisImportExportFilter::OK) { QString msg = KisImportExportFilter::conversionStatusString(status); if (!msg.isEmpty()) { DlgLoadMessages dlg(i18nc("@title:window", "Krita"), i18n("Could not open %2.\nReason: %1.", msg, prettyPathOrUrl()), errorMessage().split("\n") + warningMessage().split("\n")); dlg.exec(); } return false; } else if (!warningMessage().isEmpty()) { DlgLoadMessages dlg(i18nc("@title:window", "Krita"), i18n("There were problems opening %1.", prettyPathOrUrl()), warningMessage().split("\n")); dlg.exec(); setUrl(QUrl()); } setMimeTypeAfterLoading(typeName); emit sigLoadingFinished(); undoStack()->clear(); return true; } // shared between openFile and koMainWindow's "create new empty document" code void KisDocument::setMimeTypeAfterLoading(const QString& mimeType) { d->mimeType = mimeType.toLatin1(); d->outputMimeType = d->mimeType; } bool KisDocument::loadNativeFormat(const QString & file_) { return openUrl(QUrl::fromLocalFile(file_)); } void KisDocument::setModified() { d->modified = true; } void KisDocument::setModified(bool mod) { if (mod) { updateEditingTime(false); } if (d->isAutosaving) // ignore setModified calls due to autosaving return; if ( !d->readwrite && d->modified ) { errKrita << "Can't set a read-only document to 'modified' !" << endl; return; } //dbgUI<<" url:" << url.path(); //dbgUI<<" mod="<docInfo->aboutInfo("editing-time").toInt() + d->firstMod.secsTo(d->lastMod))); d->firstMod = now; } else if (firstModDelta > 60 || forceStoreElapsed) { d->docInfo->setAboutInfo("editing-time", QString::number(d->docInfo->aboutInfo("editing-time").toInt() + firstModDelta)); d->firstMod = now; } d->lastMod = now; } QString KisDocument::prettyPathOrUrl() const { QString _url(url().toDisplayString()); #ifdef Q_OS_WIN if (url().isLocalFile()) { _url = QDir::toNativeSeparators(_url); } #endif return _url; } // Get caption from document info (title(), in about page) QString KisDocument::caption() const { QString c; const QString _url(url().fileName()); // if URL is empty...it is probably an unsaved file if (_url.isEmpty()) { c = " [" + i18n("Not Saved") + "] "; } else { c = _url; // Fall back to document URL } return c; } void KisDocument::setTitleModified() { emit titleModified(caption(), isModified()); } QDomDocument KisDocument::createDomDocument(const QString& tagName, const QString& version) const { return createDomDocument("krita", tagName, version); } //static QDomDocument KisDocument::createDomDocument(const QString& appName, const QString& tagName, const QString& version) { QDomImplementation impl; QString url = QString("http://www.calligra.org/DTD/%1-%2.dtd").arg(appName).arg(version); QDomDocumentType dtype = impl.createDocumentType(tagName, QString("-//KDE//DTD %1 %2//EN").arg(appName).arg(version), url); // The namespace URN doesn't need to include the version number. QString namespaceURN = QString("http://www.calligra.org/DTD/%1").arg(appName); QDomDocument doc = impl.createDocument(namespaceURN, tagName, dtype); doc.insertBefore(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""), doc.documentElement()); return doc; } bool KisDocument::isNativeFormat(const QByteArray& mimetype) const { if (mimetype == nativeFormatMimeType()) return true; return extraNativeMimeTypes().contains(mimetype); } void KisDocument::setErrorMessage(const QString& errMsg) { d->lastErrorMessage = errMsg; } QString KisDocument::errorMessage() const { return d->lastErrorMessage; } void KisDocument::setWarningMessage(const QString& warningMsg) { d->lastWarningMessage = warningMsg; } QString KisDocument::warningMessage() const { return d->lastWarningMessage; } void KisDocument::removeAutoSaveFiles() { //qDebug() << "removeAutoSaveFiles"; // Eliminate any auto-save file QString asf = generateAutoSaveFileName(localFilePath()); // the one in the current dir //qDebug() << "\tfilename:" << asf << "exists:" << QFile::exists(asf); if (QFile::exists(asf)) { //qDebug() << "\tremoving autosavefile" << asf; QFile::remove(asf); } asf = generateAutoSaveFileName(QString()); // and the one in $HOME //qDebug() << "Autsavefile in $home" << asf; if (QFile::exists(asf)) { //qDebug() << "\tremoving autsavefile 2" << asf; QFile::remove(asf); } } KoUnit KisDocument::unit() const { return d->unit; } void KisDocument::setUnit(const KoUnit &unit) { if (d->unit != unit) { d->unit = unit; emit unitChanged(unit); } } KUndo2Stack *KisDocument::undoStack() { return d->undoStack; } KisImportExportManager *KisDocument::importExportManager() const { return d->importExportManager; } void KisDocument::addCommand(KUndo2Command *command) { if (command) d->undoStack->push(command); } void KisDocument::beginMacro(const KUndo2MagicString & text) { d->undoStack->beginMacro(text); } void KisDocument::endMacro() { d->undoStack->endMacro(); } void KisDocument::slotUndoStackCleanChanged(bool value) { setModified(!value); } void KisDocument::slotConfigChanged() { KisConfig cfg; d->undoStack->setUndoLimit(cfg.undoStackLimit()); setAutoSaveDelay(cfg.autoSaveInterval()); } void KisDocument::clearUndoHistory() { d->undoStack->clear(); } KisGridConfig KisDocument::gridConfig() const { return d->gridConfig; } void KisDocument::setGridConfig(const KisGridConfig &config) { d->gridConfig = config; } const KisGuidesConfig& KisDocument::guidesConfig() const { return d->guidesConfig; } void KisDocument::setGuidesConfig(const KisGuidesConfig &data) { if (d->guidesConfig == data) return; d->guidesConfig = data; emit sigGuidesConfigChanged(d->guidesConfig); } void KisDocument::resetURL() { setUrl(QUrl()); setLocalFilePath(QString()); } KoDocumentInfoDlg *KisDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const { return new KoDocumentInfoDlg(parent, docInfo); } bool KisDocument::isReadWrite() const { return d->readwrite; } QUrl KisDocument::url() const { return d->m_url; } bool KisDocument::closeUrl(bool promptToSave) { if (promptToSave) { if ( isReadWrite() && isModified()) { Q_FOREACH (KisView *view, KisPart::instance()->views()) { if (view && view->document() == this) { if (!view->queryClose()) { return false; } } } } } // Not modified => ok and delete temp file. d->mimeType = QByteArray(); // It always succeeds for a read-only part, // but the return value exists for reimplementations // (e.g. pressing cancel for a modified read-write part) return true; } void KisDocument::setUrl(const QUrl &url) { d->m_url = url; } QString KisDocument::localFilePath() const { return d->m_file; } void KisDocument::setLocalFilePath( const QString &localFilePath ) { d->m_file = localFilePath; } bool KisDocument::openUrlInternal(const QUrl &url) { if ( !url.isValid() ) return false; if (d->m_bAutoDetectedMime) { d->mimeType = QByteArray(); d->m_bAutoDetectedMime = false; } QByteArray mimetype = d->mimeType; if ( !closeUrl() ) return false; d->mimeType = mimetype; setUrl(url); d->m_file.clear(); if (d->m_url.isLocalFile()) { d->m_file = d->m_url.toLocalFile(); bool ret; // set the mimetype only if it was not already set (for example, by the host application) if (d->mimeType.isEmpty()) { // get the mimetype of the file // using findByUrl() to avoid another string -> url conversion QString mime = KisMimeDatabase::mimeTypeForFile(d->m_url.toLocalFile()); d->mimeType = mime.toLocal8Bit(); d->m_bAutoDetectedMime = true; } setUrl(d->m_url); ret = openFile(); if (ret) { emit completed(); } else { emit canceled(QString()); } return ret; } return false; } bool KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace* cs, const KoColor &bgColor, bool backgroundAsLayer, int numberOfLayers, const QString &description, const double imageResolution) { Q_ASSERT(cs); KisConfig cfg; KisImageSP image; KisPaintLayerSP layer; if (!cs) return false; QApplication::setOverrideCursor(Qt::BusyCursor); image = new KisImage(createUndoStore(), width, height, cs, name); Q_CHECK_PTR(image); connect(image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection); image->setResolution(imageResolution, imageResolution); image->assignImageProfile(cs->profile()); documentInfo()->setAboutInfo("title", name); if (name != i18n("Unnamed") && !name.isEmpty()) { setUrl(QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation) + '/' + name + ".kra")); } documentInfo()->setAboutInfo("abstract", description); layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, cs); Q_CHECK_PTR(layer); if (backgroundAsLayer) { image->setDefaultProjectionColor(KoColor(cs)); if (bgColor.opacityU8() == OPACITY_OPAQUE_U8) { layer->paintDevice()->setDefaultPixel(bgColor); } else { // Hack: with a semi-transparent background color, the projection isn't composited right if we just set the default pixel KisFillPainter painter; painter.begin(layer->paintDevice()); painter.fillRect(0, 0, width, height, bgColor, bgColor.opacityU8()); } } else { image->setDefaultProjectionColor(bgColor); } layer->setDirty(QRect(0, 0, width, height)); image->addNode(layer.data(), image->rootLayer().data()); setCurrentImage(image); for(int i = 1; i < numberOfLayers; ++i) { KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), OPACITY_OPAQUE_U8, cs); image->addNode(layer, image->root(), i); layer->setDirty(QRect(0, 0, width, height)); } cfg.defImageWidth(width); cfg.defImageHeight(height); cfg.defImageResolution(imageResolution); cfg.defColorModel(image->colorSpace()->colorModelId().id()); cfg.setDefaultColorDepth(image->colorSpace()->colorDepthId().id()); cfg.defColorProfile(image->colorSpace()->profile()->name()); QApplication::restoreOverrideCursor(); return true; } bool KisDocument::isSaving() const { const bool result = d->savingMutex.tryLock(); if (result) { d->savingMutex.unlock(); } return !result; } void KisDocument::waitForSavingToComplete() { KisAsyncActionFeedback f(i18nc("progress dialog message when the user closes the document that is being saved", "Waiting for saving to complete..."), 0); f.waitForMutex(&d->savingMutex); } KoShapeBasedDocumentBase *KisDocument::shapeController() const { return d->shapeController; } KoShapeLayer* KisDocument::shapeForNode(KisNodeSP layer) const { return d->shapeController->shapeForNode(layer); } QList KisDocument::assistants() const { return d->assistants; } void KisDocument::setAssistants(const QList &value) { d->assistants = value; } KisSharedPtr KisDocument::createReferenceImagesLayer(KisImageSP targetImage) { if (!d->referenceImagesLayer) { if (targetImage.isNull()) targetImage = d->image; d->referenceImagesLayer = new KisReferenceImagesLayer(shapeController(), targetImage); targetImage->addNode(d->referenceImagesLayer, targetImage->root()); } return d->referenceImagesLayer; } KisReferenceImagesLayer *KisDocument::referenceImagesLayer() const { return d->referenceImagesLayer.data(); } void KisDocument::setPreActivatedNode(KisNodeSP activatedNode) { d->preActivatedNode = activatedNode; } KisNodeSP KisDocument::preActivatedNode() const { return d->preActivatedNode; } KisImageWSP KisDocument::image() const { return d->image; } KisImageSP KisDocument::savingImage() const { return d->savingImage; } void KisDocument::setCurrentImage(KisImageSP image) { if (d->image) { // Disconnect existing sig/slot connections d->image->disconnect(this); d->shapeController->setImage(0); d->image = 0; } if (!image) return; d->setImageAndInitIdleWatcher(image); d->shapeController->setImage(image); setModified(false); connect(d->image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection); d->image->initialRefreshGraph(); } +void KisDocument::hackPreliminarySetImage(KisImageSP image) +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(!d->image); + + d->setImageAndInitIdleWatcher(image); + d->shapeController->setImage(image); +} + void KisDocument::setImageModified() { setModified(true); } KisUndoStore* KisDocument::createUndoStore() { return new KisDocumentUndoStore(this); } bool KisDocument::isAutosaving() const { return d->isAutosaving; } QString KisDocument::exportErrorToUserMessage(KisImportExportFilter::ConversionStatus status, const QString &errorMessage) { return errorMessage.isEmpty() ? KisImportExportFilter::conversionStatusString(status) : errorMessage; } diff --git a/libs/ui/KisDocument.h b/libs/ui/KisDocument.h index 9199443f16..0b8040304d 100644 --- a/libs/ui/KisDocument.h +++ b/libs/ui/KisDocument.h @@ -1,604 +1,615 @@ /* This file is part of the Krita project * * Copyright (C) 2014 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KISDOCUMENT_H #define KISDOCUMENT_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kritaui_export.h" class QString; class KUndo2Command; class KoUnit; class KoColor; class KoColorSpace; class KoShapeBasedDocumentBase; class KoShapeLayer; class KoStore; class KoOdfReadStore; class KoDocumentInfo; class KoDocumentInfoDlg; class KisImportExportManager; class KisUndoStore; class KisPart; class KisGridConfig; class KisGuidesConfig; class QDomDocument; class KisReferenceImagesLayer; #define KIS_MIME_TYPE "application/x-krita" /** * The %Calligra document class * * This class provides some functionality each %Calligra document should have. * * @short The %Calligra document class */ class KRITAUI_EXPORT KisDocument : public QObject, public KoDocumentBase { Q_OBJECT protected: explicit KisDocument(); /** * @brief KisDocument makes a deep copy of the document \p rhs. * The caller *must* ensure that the image is properly * locked and is in consistent state before asking for * cloning. * @param rhs the source document to copy from */ explicit KisDocument(const KisDocument &rhs); public: enum OpenFlag { None = 0, DontAddToRecent = 0x1, RecoveryFile = 0x2 }; Q_DECLARE_FLAGS(OpenFlags, OpenFlag) /** * Destructor. * * The destructor does not delete any attached KisView objects and it does not * delete the attached widget as returned by widget(). */ ~KisDocument() override; /** * @brief reload Reloads the document from the original url * @return the result of loading the document */ bool reload(); /** * @brief creates a clone of the document and returns it. Please make sure that you * hold all the necessary locks on the image before asking for a clone! */ KisDocument* clone(); /** * @brief openUrl Open an URL * @param url The URL to open * @param flags Control specific behavior * @return success status */ bool openUrl(const QUrl &url, OpenFlags flags = None); /** * Opens the document given by @p url, without storing the URL * in the KisDocument. * Call this instead of openUrl() to implement KisMainWindow's * File --> Import feature. * * @note This will call openUrl(). To differentiate this from an ordinary * Open operation (in any reimplementation of openUrl() or openFile()) * call isImporting(). */ bool importDocument(const QUrl &url); /** * Saves the document as @p url without changing the state of the * KisDocument (URL, modified flag etc.). Call this instead of * KisParts::ReadWritePart::saveAs() to implement KisMainWindow's * File --> Export feature. */ bool exportDocument(const QUrl &url, const QByteArray &mimeType, bool showWarnings = false, KisPropertiesConfigurationSP exportConfiguration = 0); bool exportDocumentSync(const QUrl &url, const QByteArray &mimeType, KisPropertiesConfigurationSP exportConfiguration = 0); private: bool exportDocumentImpl(const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration); public: /** * @brief Sets whether the document can be edited or is read only. * * This recursively applied to all child documents and * KisView::updateReadWrite is called for every attached * view. */ void setReadWrite(bool readwrite = true); /** * To be preferred when a document exists. It is fast when calling * it multiple times since it caches the result that readNativeFormatMimeType() * delivers. * This comes from the X-KDE-NativeMimeType key in the .desktop file. */ static QByteArray nativeFormatMimeType() { return KIS_MIME_TYPE; } /// Checks whether a given mimetype can be handled natively. bool isNativeFormat(const QByteArray& mimetype) const; /// Returns a list of the mimetypes considered "native", i.e. which can /// be saved by KisDocument without a filter, in *addition* to the main one static QStringList extraNativeMimeTypes() { return QStringList() << KIS_MIME_TYPE; } /** * Returns the actual mimetype of the document */ QByteArray mimeType() const override; /** * @brief Sets the mime type for the document. * * When choosing "save as" this is also the mime type * selected by default. */ void setMimeType(const QByteArray & mimeType) override; /** * @return true if file operations should inhibit the option dialog */ bool fileBatchMode() const; /** * @param batchMode if true, do not show the option dialog for file operations. */ void setFileBatchMode(const bool batchMode); /** * Sets the error message to be shown to the user (use i18n()!) * when loading or saving fails. * If you asked the user about something and they chose "Cancel", */ void setErrorMessage(const QString& errMsg); /** * Return the last error message. Usually KisDocument takes care of * showing it; this method is mostly provided for non-interactive use. */ QString errorMessage() const; /** * Sets the warning message to be shown to the user (use i18n()!) * when loading or saving fails. */ void setWarningMessage(const QString& warningMsg); /** * Return the last warning message set by loading or saving. Warnings * mean that the document could not be completely loaded, but the errors * were not absolutely fatal. */ QString warningMessage() const; /** * @brief Generates a preview picture of the document * @note The preview is used in the File Dialog and also to create the Thumbnail */ QPixmap generatePreview(const QSize& size); /** * Tells the document that its title has been modified, either because * the modified status changes (this is done by setModified() ) or * because the URL or the document-info's title changed. */ void setTitleModified(); /** * @brief Sets the document to empty. * * Used after loading a template * (which is not empty, but not the user's input). * * @see isEmpty() */ void setEmpty(bool empty = true); /** * Return a correctly created QDomDocument for this KisDocument, * including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element. * @param tagName the name of the tag for the root element * @param version the DTD version (usually the application's version). */ QDomDocument createDomDocument(const QString& tagName, const QString& version) const; /** * Return a correctly created QDomDocument for an old (1.3-style) %Calligra document, * including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element. * This static method can be used e.g. by filters. * @param appName the app's instance name, e.g. words, kspread, kpresenter etc. * @param tagName the name of the tag for the root element, e.g. DOC for words/kpresenter. * @param version the DTD version (usually the application's version). */ static QDomDocument createDomDocument(const QString& appName, const QString& tagName, const QString& version); /** * Loads a document in the native format from a given URL. * Reimplement if your native format isn't XML. * * @param file the file to load - usually KReadOnlyPart::m_file or the result of a filter */ bool loadNativeFormat(const QString & file); /** * Activate/deactivate/configure the autosave feature. * @param delay in seconds, 0 to disable */ void setAutoSaveDelay(int delay); /** * @return the information concerning this document. * @see KoDocumentInfo */ KoDocumentInfo *documentInfo() const; /** * Performs a cleanup of unneeded backup files */ void removeAutoSaveFiles(); /** * Returns true if this document or any of its internal child documents are modified. */ bool isModified() const override; /** * @return caption of the document * * Caption is of the form "[title] - [url]", * built out of the document info (title) and pretty-printed * document URL. * If the title is not present, only the URL it returned. */ QString caption() const; /** * Sets the document URL to empty URL * KParts doesn't allow this, but %Calligra apps have e.g. templates * After using loadNativeFormat on a template, one wants * to set the url to QUrl() */ void resetURL(); /** * @internal (public for KisMainWindow) */ void setMimeTypeAfterLoading(const QString& mimeType); /** * Returns the unit used to display all measures/distances. */ KoUnit unit() const; /** * Sets the unit used to display all measures/distances. */ void setUnit(const KoUnit &unit); KisGridConfig gridConfig() const; void setGridConfig(const KisGridConfig &config); /// returns the guides data for this document. const KisGuidesConfig& guidesConfig() const; void setGuidesConfig(const KisGuidesConfig &data); void clearUndoHistory(); /** * Sets the modified flag on the document. This means that it has * to be saved or not before deleting it. */ void setModified(bool _mod); void setRecovered(bool value); bool isRecovered() const; void updateEditingTime(bool forceStoreElapsed); /** * Returns the global undo stack */ KUndo2Stack *undoStack(); /** * @brief importExportManager gives access to the internal import/export manager * @return the document's import/export manager */ KisImportExportManager *importExportManager() const; /** * @brief serializeToNativeByteArray daves the document into a .kra file wtitten * to a memory-based byte-array * @return a byte array containing the .kra file */ QByteArray serializeToNativeByteArray(); /** * @brief isInSaving shown if the document has any (background) saving process or not * @return true if there is some saving in action */ bool isInSaving() const; public Q_SLOTS: /** * Adds a command to the undo stack and executes it by calling the redo() function. * @param command command to add to the undo stack */ void addCommand(KUndo2Command *command); /** * Begins recording of a macro command. At the end endMacro needs to be called. * @param text command description */ void beginMacro(const KUndo2MagicString &text); /** * Ends the recording of a macro command. */ void endMacro(); Q_SIGNALS: /** * This signal is emitted when the unit is changed by setUnit(). * It is common to connect views to it, in order to change the displayed units * (e.g. in the rulers) */ void unitChanged(const KoUnit &unit); /** * Emitted e.g. at the beginning of a save operation * This is emitted by KisDocument and used by KisView to display a statusbar message */ void statusBarMessage(const QString& text, int timeout = 0); /** * Emitted e.g. at the end of a save operation * This is emitted by KisDocument and used by KisView to clear the statusbar message */ void clearStatusBarMessage(); /** * Emitted when the document is modified */ void modified(bool); void titleModified(const QString &caption, bool isModified); void sigLoadingFinished(); void sigSavingFinished(); void sigGuidesConfigChanged(const KisGuidesConfig &config); void sigBackgroundSavingFinished(KisImportExportFilter::ConversionStatus status, const QString &errorMessage); void sigCompleteBackgroundSaving(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage); private Q_SLOTS: void finishExportInBackground(); void slotChildCompletedSavingInBackground(KisImportExportFilter::ConversionStatus status, const QString &errorMessage); void slotCompleteAutoSaving(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage); void slotCompleteSavingDocument(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage); private: friend class KisPart; friend class SafeSavingLocker; bool initiateSavingInBackground(const QString actionName, const QObject *receiverObject, const char *receiverMethod, const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration); bool startExportInBackground(const QString &actionName, const QString &location, const QString &realLocation, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration); /** * Generate a name for the document. */ QString newObjectName(); QString generateAutoSaveFileName(const QString & path) const; /** * Loads a document * * Applies a filter if necessary, and calls loadNativeFormat in any case * You should not have to reimplement, except for very special cases. * * NOTE: this method also creates a new KisView instance! * * This method is called from the KReadOnlyPart::openUrl method. */ bool openFile(); /** @internal */ void setModified(); public: bool isAutosaving() const override; public: QString localFilePath() const override; void setLocalFilePath( const QString &localFilePath ); KoDocumentInfoDlg* createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const; bool isReadWrite() const; QUrl url() const override; void setUrl(const QUrl &url) override; bool closeUrl(bool promptToSave = true); bool saveAs(const QUrl &url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfigration = 0); /** * Create a new image that has this document as a parent and * replace the current image with this image. */ bool newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, bool backgroundAsLayer, int numberOfLayers, const QString &imageDescription, const double imageResolution); bool isSaving() const; void waitForSavingToComplete(); KisImageWSP image() const; /** * @brief savingImage provides a detached, shallow copy of the original image that must be used when saving. * Any strokes in progress will not be applied to this image, so the result might be missing some data. On * the other hand, it won't block. * * @return a shallow copy of the original image, or 0 is saving is not in progress */ KisImageSP savingImage() const; /** * Set the current image to the specified image and turn undo on. */ void setCurrentImage(KisImageSP image); + /** + * Set the image of the document preliminary, before the document + * has completed loading. Some of the document items (shapes) may want + * to access image properties (bounds and resolution), so we should provide + * it to them even before the entire image is loaded. + * + * Right now, the only use by KoShapeRegistry::createShapeFromOdf(), remove + * after it is deprecated. + */ + void hackPreliminarySetImage(KisImageSP image); + KisUndoStore* createUndoStore(); /** * The shape controller matches internal krita image layers with * the flake shape hierarchy. */ KoShapeBasedDocumentBase * shapeController() const; KoShapeLayer* shapeForNode(KisNodeSP layer) const; /** * Set the list of nodes that was marked as currently active. Used *only* * for saving loading. Never use it for tools or processing. */ void setPreActivatedNode(KisNodeSP activatedNode); /** * @return the node that was set as active during loading. Used *only* * for saving loading. Never use it for tools or processing. */ KisNodeSP preActivatedNode() const; /// @return the list of assistants associated with this document QList assistants() const; /// @replace the current list of assistants with @param value void setAssistants(const QList &value); /** * Get existing reference images layer or create new if none exists. */ KisSharedPtr createReferenceImagesLayer(KisImageSP targetImage = KisImageSP()); /** * Get existing reference images layer or null if none exists. */ KisReferenceImagesLayer *referenceImagesLayer() const; bool save(bool showWarnings, KisPropertiesConfigurationSP exportConfiguration); Q_SIGNALS: void completed(); void canceled(const QString &); private Q_SLOTS: void setImageModified(); void slotAutoSave(); void slotUndoStackCleanChanged(bool value); void slotConfigChanged(); private: /** * @brief try to clone the image. This method handles all the locking for you. If locking * has failed, no cloning happens * @return cloned document on success, null otherwise */ KisDocument *lockAndCloneForSaving(); QString exportErrorToUserMessage(KisImportExportFilter::ConversionStatus status, const QString &errorMessage); QString prettyPathOrUrl() const; bool openUrlInternal(const QUrl &url); class Private; Private *const d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KisDocument::OpenFlags) Q_DECLARE_METATYPE(KisDocument*) #endif diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp index c9fabf5fd2..5c6cb22e42 100644 --- a/libs/ui/KisMainWindow.cpp +++ b/libs/ui/KisMainWindow.cpp @@ -1,2543 +1,2543 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2000-2006 David Faure Copyright (C) 2007, 2009 Thomas zander Copyright (C) 2010 Benjamin Port This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMainWindow.h" #include // qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_KIO #include #endif #include #include #include #include #include #include #include #include #include #include #include "KoDockFactoryBase.h" #include "KoDockWidgetTitleBar.h" #include "KoDocumentInfoDlg.h" #include "KoDocumentInfo.h" #include "KoFileDialog.h" #include #include #include #include #include #include "KoToolDocker.h" #include #include #include #include #include #include #include #include #include "dialogs/kis_about_application.h" #include "dialogs/kis_delayed_save_dialog.h" #include "dialogs/kis_dlg_preferences.h" #include "kis_action.h" #include "kis_action_manager.h" #include "KisApplication.h" #include "kis_canvas2.h" #include "kis_canvas_controller.h" #include "kis_canvas_resource_provider.h" #include "kis_clipboard.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_custom_image_widget.h" #include #include "KisDocument.h" #include "KisDocument.h" #include "kis_group_layer.h" #include "kis_icon_utils.h" #include "kis_image_from_clipboard_widget.h" #include "kis_image.h" #include #include "KisImportExportManager.h" #include "kis_mainwindow_observer.h" #include "kis_memory_statistics_server.h" #include "kis_node.h" #include "KisOpenPane.h" #include "kis_paintop_box.h" #include "KisPart.h" #include "KisPrintJob.h" #include "kis_resource_server_provider.h" #include "kis_signal_compressor_with_param.h" #include "kis_statusbar.h" #include "KisView.h" #include "KisViewManager.h" #include "thememanager.h" #include "kis_animation_importer.h" #include "dialogs/kis_dlg_import_image_sequence.h" #include #include #ifdef Q_OS_WIN #include #endif class ToolDockerFactory : public KoDockFactoryBase { public: ToolDockerFactory() : KoDockFactoryBase() { } QString id() const override { return "sharedtooldocker"; } QDockWidget* createDockWidget() override { KoToolDocker* dockWidget = new KoToolDocker(); dockWidget->setTabEnabled(false); return dockWidget; } DockPosition defaultDockPosition() const override { return DockRight; } }; class Q_DECL_HIDDEN KisMainWindow::Private { public: Private(KisMainWindow *parent, 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)) , mdiArea(new QMdiArea(parent)) , windowMapper(new QSignalMapper(parent)) , documentMapper(new QSignalMapper(parent)) { if (id.isNull()) this->id = QUuid::createUuid(); mdiArea->setTabsMovable(true); } ~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}; KoResourceModel *workspacemodel {0}; QString lastExportLocation; QMap dockWidgetsMap; QByteArray dockerStateBeforeHiding; KoToolDocker *toolOptionsDocker {0}; QCloseEvent *deferredClosingEvent {0}; Digikam::ThemeManager *themeManager {0}; QMdiArea *mdiArea; QMdiSubWindow *activeSubWindow {0}; QSignalMapper *windowMapper; QSignalMapper *documentMapper; QByteArray lastExportedFormat; QScopedPointer > tabSwitchCompressor; QMutex savingEntryMutex; KConfigGroup windowStateConfig; 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(false); QSharedPointer adapter(new KoResourceServerAdapter(rserver)); d->workspacemodel = new KoResourceModel(adapter, this); connect(d->workspacemodel, &KoResourceModel::afterResourcesLayoutReset, this, [&]() { updateWindowMenu(); }); KisConfig cfg; d->viewManager = new KisViewManager(this, actionCollection()); KConfigGroup group( KSharedConfig::openConfig(), "theme"); d->themeManager = new Digikam::ThemeManager(group.readEntry("Theme", "Krita dark"), this); 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); KoToolBoxFactory toolBoxFactory; QDockWidget *toolbox = createDockWidget(&toolBoxFactory); toolbox->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable); if (cfg.toolOptionsInDocker()) { ToolDockerFactory toolDockerFactory; d->toolOptionsDocker = qobject_cast(createDockWidget(&toolDockerFactory)); d->toolOptionsDocker->toggleViewAction()->setEnabled(true); } QMap dockwidgetActions; dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction(); Q_FOREACH (const QString & docker, KoDockRegistry::instance()->keys()) { KoDockFactoryBase *factory = KoDockRegistry::instance()->value(docker); QDockWidget *dw = createDockWidget(factory); dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction(); } if (d->toolOptionsDocker) { dockwidgetActions[d->toolOptionsDocker->toggleViewAction()->text()] = d->toolOptionsDocker->toggleViewAction(); } connect(KoToolManager::instance(), SIGNAL(toolOptionWidgetsChanged(KoCanvasController*, QList >)), this, SLOT(newOptionWidgets(KoCanvasController*, QList >))); Q_FOREACH (QString title, dockwidgetActions.keys()) { d->dockWidgetMenu->addAction(dockwidgetActions[title]); } Q_FOREACH (QDockWidget *wdg, dockWidgets()) { if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) { wdg->setVisible(true); } } Q_FOREACH (KoCanvasObserverBase* observer, canvasObservers()) { observer->setObservedCanvas(0); KisMainwindowObserver* mainwindowObserver = dynamic_cast(observer); if (mainwindowObserver) { mainwindowObserver->setMainWindow(d->viewManager); } } d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setTabPosition(QTabWidget::North); d->mdiArea->setTabsClosable(true); setCentralWidget(d->mdiArea); connect(d->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(subWindowActivated())); connect(d->windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); connect(d->documentMapper, SIGNAL(mapped(QObject*)), this, SLOT(newView(QObject*))); createActions(); setAutoSaveSettings(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); if (toolBar) { if (toolBar->objectName() == "BrushesAndStuff") { toolBar->setEnabled(false); } KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this); actionCollection()->addAction(toolBar->objectName().toUtf8(), act); act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle()))); connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool))); act->setChecked(!toolBar->isHidden()); toolbarList.append(act); } else { warnUI << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!"; } } plugActionList("toolbarlist", toolbarList); d->toolbarList = toolbarList; applyToolBarLayout(); d->viewManager->updateGUI(); d->viewManager->updateIcons(); #ifdef Q_OS_WIN auto w = qApp->activeWindow(); if (w) QWindowsWindowFunctions::setHasBorderInFullScreen(w->windowHandle(), true); #endif QTimer::singleShot(1000, this, SLOT(checkSanity())); { using namespace std::placeholders; // For _1 placeholder std::function callback( std::bind(&KisMainWindow::switchTab, this, _1)); d->tabSwitchCompressor.reset( new KisSignalCompressorWithParam(500, callback, KisSignalCompressor::FIRST_INACTIVE)); } } KisMainWindow::~KisMainWindow() { // Q_FOREACH (QAction *ac, actionCollection()->actions()) { // QAction *action = qobject_cast(ac); // if (action) { // dbgKrita << "", "").replace("", "") // << "iconText=" << action->iconText().replace("&", "&") // << "shortcut=" << action->shortcut(QAction::ActiveShortcut).toString() // << "defaultShortcut=" << action->shortcut(QAction::DefaultShortcut).toString() // << "isCheckable=" << QString((action->isChecked() ? "true" : "false")) // << "statusTip=" << action->statusTip() // << "/>" ; // } // else { // dbgKrita << "Got a QAction:" << ac->objectName(); // } // } // The doc and view might still exist (this is the case when closing the window) KisPart::instance()->removeMainWindow(this); 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; 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(); KisUpdateSchedulerConfigNotifier::instance()->notifyConfigChanged(); // XXX: should this be changed for the views in other windows as well? Q_FOREACH (QPointer koview, KisPart::instance()->views()) { KisViewManager *view = qobject_cast(koview); if (view) { // Update the settings for all nodes -- they don't query // KisConfig directly because they need the settings during // compositing, and they don't connect to the config notifier // because nodes are not QObjects (because only one base class // can be a QObject). KisNode* node = dynamic_cast(view->image()->rootLayer().data()); node->updateSettings(); } } d->viewManager->showHideScrollbars(); } } void KisMainWindow::slotThemeChanged() { // save theme changes instantly KConfigGroup group( KSharedConfig::openConfig(), "theme"); group.writeEntry("Theme", d->themeManager->currentThemeName()); // reload action icons! Q_FOREACH (QAction *action, actionCollection()->actions()) { KisIconUtils::updateIcon(action); } emit themeChanged(); } void KisMainWindow::updateReloadFileAction(KisDocument *doc) { Q_UNUSED(doc); // d->reloadFile->setEnabled(doc && !doc->url().isEmpty()); } void KisMainWindow::setReadWrite(bool readwrite) { d->saveAction->setEnabled(readwrite); d->importFile->setEnabled(readwrite); d->readOnly = !readwrite; updateCaption(); } void KisMainWindow::addRecentURL(const QUrl &url) { // Add entry to recent documents list // (call coming from KisDocument because it must work with cmd line, template dlg, file/open, etc.) if (!url.isEmpty()) { bool ok = true; if (url.isLocalFile()) { QString path = url.adjusted(QUrl::StripTrailingSlash).toLocalFile(); const QStringList tmpDirs = KoResourcePaths::resourceDirs("tmp"); for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it) { if (path.contains(*it)) { ok = false; // it's in the tmp resource } } #ifdef HAVE_KIO if (ok) { KRecentDocument::add(QUrl::fromLocalFile(path)); } #endif } #ifdef HAVE_KIO else { KRecentDocument::add(url.adjusted(QUrl::StripTrailingSlash)); } #endif if (ok) { d->recentFiles->addUrl(url); } saveRecentFiles(); } } void KisMainWindow::saveRecentFiles() { // Save list of recent files KSharedConfigPtr config = KSharedConfig::openConfig(); d->recentFiles->saveEntries(config->group("RecentFiles")); config->sync(); // Tell all windows to reload their list, after saving // Doesn't work multi-process, but it's a start Q_FOREACH (KisMainWindow *mw, KisPart::instance()->mainWindows()) { if (mw != this) { mw->reloadRecentFileList(); } } } void KisMainWindow::reloadRecentFileList() { d->recentFiles->loadEntries(KSharedConfig::openConfig()->group("RecentFiles")); } void KisMainWindow::updateCaption() { if (!d->mdiArea->activeSubWindow()) { updateCaption(QString(), false); } else if (d->activeView && d->activeView->document() && d->activeView->image()){ KisDocument *doc = d->activeView->document(); QString caption(doc->caption()); if (d->readOnly) { caption += " [" + i18n("Write Protected") + "] "; } if (doc->isRecovered()) { caption += " [" + i18n("Recovered") + "] "; } // new documents aren't saved yet, so we don't need to say it is modified // new files don't have a URL, so we are using that for the check if (!doc->url().isEmpty()) { if ( doc->isModified()) { caption += " [" + i18n("Modified") + "] "; } } // show the file size for the document KisMemoryStatisticsServer::Statistics m_fileSizeStats = KisMemoryStatisticsServer::instance()->fetchMemoryStatistics(d->activeView ? d->activeView->image() : 0); if (m_fileSizeStats.imageSize) { caption += QString(" (").append( KisStatusBar::formatSize(m_fileSizeStats.imageSize)).append( ")"); } d->activeView->setWindowTitle(caption); d->activeView->setWindowModified(doc->isModified()); updateCaption(caption, doc->isModified()); if (!doc->url().fileName().isEmpty()) d->saveAction->setToolTip(i18n("Save as %1", doc->url().fileName())); else d->saveAction->setToolTip(i18n("Save")); } } void KisMainWindow::updateCaption(const QString & caption, bool mod) { dbgUI << "KisMainWindow::updateCaption(" << caption << "," << mod << ")"; #ifdef KRITA_ALPHA setCaption(QString("ALPHA %1: %2").arg(KRITA_ALPHA).arg(caption), mod); return; #endif #ifdef KRITA_BETA setCaption(QString("BETA %1: %2").arg(KRITA_BETA).arg(caption), mod); return; #endif #ifdef KRITA_RC setCaption(QString("RELEASE CANDIDATE %1: %2").arg(KRITA_RC).arg(caption), mod); return; #endif setCaption(caption, mod); } KisView *KisMainWindow::activeView() const { if (d->activeView) { return d->activeView; } return 0; } bool KisMainWindow::openDocument(const QUrl &url, OpenFlags flags) { if (!QFile(url.toLocalFile()).exists()) { if (!flags && BatchMode) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("The file %1 does not exist.", url.url())); } d->recentFiles->removeUrl(url); //remove the file from the recent-opened-file-list saveRecentFiles(); return false; } return openDocumentInternal(url, flags); } bool KisMainWindow::openDocumentInternal(const QUrl &url, OpenFlags flags) { if (!url.isLocalFile()) { qWarning() << "KisMainWindow::openDocumentInternal. Not a local file:" << url; return false; } KisDocument *newdoc = KisPart::instance()->createDocument(); if (flags & BatchMode) { newdoc->setFileBatchMode(true); } d->firstTime = true; connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); connect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); KisDocument::OpenFlags openFlags = KisDocument::None; if (flags & RecoveryFile) { openFlags |= KisDocument::RecoveryFile; } bool openRet = !(flags & Import) ? newdoc->openUrl(url, openFlags) : newdoc->importDocument(url); if (!openRet) { delete newdoc; return false; } KisPart::instance()->addDocument(newdoc); updateReloadFileAction(newdoc); if (!QFileInfo(url.toLocalFile()).isWritable()) { setReadWrite(false); } return true; } 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) { 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::mimeFilter(KisImportExportManager::Import)); dialog.setCaption(isImporting ? i18n("Import Images") : i18n("Open Images")); return dialog.filenames(); } // Separate from openDocument to handle async loading (remote URLs) void KisMainWindow::slotLoadCompleted() { KisDocument *newdoc = qobject_cast(sender()); if (newdoc && newdoc->image()) { addViewAndNotifyLoadingCompleted(newdoc); disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); emit loadCompleted(); } } void KisMainWindow::slotLoadCanceled(const QString & errMsg) { dbgUI << "KisMainWindow::slotLoadCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg); // ... can't delete the document, it's the one who emitted the signal... KisDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); } void KisMainWindow::slotSaveCanceled(const QString &errMsg) { dbgUI << "KisMainWindow::slotSaveCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg); slotSaveCompleted(); } void KisMainWindow::slotSaveCompleted() { dbgUI << "KisMainWindow::slotSaveCompleted"; KisDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); if (d->deferredClosingEvent) { KXmlGuiWindow::closeEvent(d->deferredClosingEvent); } } bool KisMainWindow::hackIsSaving() const { StdLockableWrapper wrapper(&d->savingEntryMutex); std::unique_lock> l(wrapper, std::try_to_lock); return !l.owns_lock(); } bool KisMainWindow::saveDocument(KisDocument *document, bool saveas, bool isExporting) { if (!document) { return true; } /** * Make sure that we cannot enter this method twice! * * The lower level functions may call processEvents() so * double-entry is quite possible to achieve. Here we try to lock * the mutex, and if it is failed, just cancel saving. */ StdLockableWrapper wrapper(&d->savingEntryMutex); std::unique_lock> l(wrapper, std::try_to_lock); if (!l.owns_lock()) return false; // no busy wait for saving because it is dangerous! KisDelayedSaveDialog dlg(document->image(), KisDelayedSaveDialog::SaveDialog, 0, this); dlg.blockIfImageIsBusy(); if (dlg.result() == KisDelayedSaveDialog::Rejected) { return false; } else if (dlg.result() == KisDelayedSaveDialog::Ignored) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("You are saving a file while the image is " "still rendering. The saved file may be " "incomplete or corrupted.\n\n" "Please select a location where the original " "file will not be overridden!")); saveas = true; } if (document->isRecovered()) { saveas = true; } bool reset_url; if (document->url().isEmpty()) { reset_url = true; saveas = true; } else { reset_url = false; } connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); connect(document, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); QUrl oldURL = document->url(); QString oldFile = document->localFilePath(); QByteArray nativeFormat = document->nativeFormatMimeType(); QByteArray oldMimeFormat = document->mimeType(); QUrl suggestedURL = document->url(); QStringList mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export); mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export); if (!mimeFilter.contains(oldMimeFormat)) { dbgUI << "KisMainWindow::saveDocument no export filter for" << oldMimeFormat; // --- don't setOutputMimeType in case the user cancels the Save As // dialog and then tries to just plain Save --- // suggest a different filename extension (yes, we fortunately don't all live in a world of magic :)) QString suggestedFilename = QFileInfo(suggestedURL.toLocalFile()).baseName(); if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name suggestedFilename = suggestedFilename + "." + KisMimeDatabase::suffixesForMimeType(KIS_MIME_TYPE).first(); suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename); suggestedURL.setPath(suggestedURL.path() + suggestedFilename); } // force the user to choose outputMimeType saveas = true; } bool ret = false; if (document->url().isEmpty() || isExporting || saveas) { // if you're just File/Save As'ing to change filter options you // don't want to be reminded about overwriting files etc. bool justChangingFilterOptions = false; KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveAs"); dialog.setCaption(isExporting ? i18n("Exporting") : i18n("Saving As")); //qDebug() << ">>>>>" << isExporting << d->lastExportLocation << d->lastExportedFormat << QString::fromLatin1(document->mimeType()); if (isExporting && !d->lastExportLocation.isEmpty()) { // Use the location where we last exported to, if it's set, as the opening location for the file dialog QString proposedPath = QFileInfo(d->lastExportLocation).absolutePath(); // If the document doesn't have a filename yet, use the title QString proposedFileName = suggestedURL.isEmpty() ? document->documentInfo()->aboutInfo("title") : QFileInfo(suggestedURL.toLocalFile()).baseName(); // Use the last mimetype we exported to by default QString proposedMimeType = d->lastExportedFormat.isEmpty() ? "" : d->lastExportedFormat; QString proposedExtension = KisMimeDatabase::suffixesForMimeType(proposedMimeType).first().remove("*,"); // Set the default dir: this overrides the one loaded from the config file, since we're exporting and the lastExportLocation is not empty dialog.setDefaultDir(proposedPath + "/" + proposedFileName + "." + proposedExtension, true); dialog.setMimeTypeFilters(mimeFilter, proposedMimeType); } else { // Get the last used location for saving KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString proposedPath = group.readEntry("SaveAs", ""); // if that is empty, get the last used location for loading if (proposedPath.isEmpty()) { proposedPath = group.readEntry("OpenDocument", ""); } // If that is empty, too, use the Pictures location. if (proposedPath.isEmpty()) { proposedPath = 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!"; document->setUrl(oldURL); document->setLocalFilePath(oldFile); } } else { // Export ret = document->exportDocument(newURL, outputFormat); if (ret) { d->lastExportLocation = newURL.toLocalFile(); d->lastExportedFormat = outputFormat; } } } // if (wantToSave) { else ret = false; } // if (bOk) { else ret = false; } else { // saving // We cannot "export" into the currently // opened document. We are not Gimp. KIS_ASSERT_RECOVER_NOOP(!isExporting); // be sure document has the correct outputMimeType! if (document->isModified()) { ret = document->save(true, 0); } if (!ret) { dbgUI << "Failed Save!"; document->setUrl(oldURL); document->setLocalFilePath(oldFile); } } if (ret && !isExporting) { document->setRecovered(false); } if (!ret && reset_url) document->resetURL(); //clean the suggested filename as the save dialog was rejected updateReloadFileAction(document); updateCaption(); return ret; } void KisMainWindow::undo() { if (activeView()) { activeView()->undoAction()->trigger(); d->undo->setText(activeView()->undoAction()->text()); } } void KisMainWindow::redo() { if (activeView()) { activeView()->redoAction()->trigger(); d->redo->setText(activeView()->redoAction()->text()); } } void KisMainWindow::closeEvent(QCloseEvent *e) { 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(); actionCollection()->action("edit_undo")->setText(activeView()->undoAction()->text()); actionCollection()->action("edit_redo")->setText(activeView()->redoAction()->text()); d->viewManager->setCurrentView(view); auto *kisPart = KisPart::instance(); if (kisPart->isShowImageInAllWindowsEnabled()) { Q_FOREACH(QPointer window, kisPart->mainWindows()) { if (window == this) continue; window->showDocument(view->document()); } } } void KisMainWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/x-krita-node") || event->mimeData()->hasFormat("application/x-qt-image")) { event->accept(); } } void KisMainWindow::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) { Q_FOREACH (const QUrl &url, event->mimeData()->urls()) { openDocument(url, None); } } } void KisMainWindow::dragMoveEvent(QDragMoveEvent * event) { QTabBar *tabBar = d->findTabBarHACK(); if (!tabBar && d->mdiArea->viewMode() == QMdiArea::TabbedView) { qWarning() << "WARNING!!! Cannot find QTabBar in the main window! Looks like Qt has changed behavior. Drag & Drop between multiple tabs might not work properly (tabs will not switch automatically)!"; } if (tabBar && tabBar->isVisible()) { QPoint pos = tabBar->mapFromGlobal(mapToGlobal(event->pos())); if (tabBar->rect().contains(pos)) { const int tabIndex = tabBar->tabAt(pos); if (tabIndex >= 0 && tabBar->currentIndex() != tabIndex) { d->tabSwitchCompressor->start(tabIndex); } } else if (d->tabSwitchCompressor->isActive()) { d->tabSwitchCompressor->stop(); } } } void KisMainWindow::dragLeaveEvent(QDragLeaveEvent * /*event*/) { if (d->tabSwitchCompressor->isActive()) { d->tabSwitchCompressor->stop(); } } void KisMainWindow::mouseReleaseEvent(QMouseEvent *event) { /** * This ensures people who do not understand that you * need to make a canvas first, will find the new image * dialog on click. */ if (centralWidget()->geometry().contains(event->pos()) - && KisPart::instance()->documents().size()==0) { + && KisPart::instance()->documents().size()==0 && event->button() == Qt::LeftButton) { this->slotFileNew(); event->accept(); } else { event->ignore(); } } void KisMainWindow::switchTab(int index) { QTabBar *tabBar = d->findTabBarHACK(); if (!tabBar) return; tabBar->setCurrentIndex(index); } void KisMainWindow::slotFileNew() { const QStringList mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Import); KisOpenPane *startupWidget = new KisOpenPane(this, mimeFilter, QStringLiteral("templates/")); startupWidget->setWindowModality(Qt::WindowModal); startupWidget->setWindowTitle(i18n("Create new document")); KisConfig cfg; int w = cfg.defImageWidth(); int h = cfg.defImageHeight(); const double resolution = cfg.defImageResolution(); const QString colorModel = cfg.defColorModel(); const QString colorDepth = cfg.defaultColorDepth(); const QString colorProfile = cfg.defColorProfile(); CustomDocumentWidgetItem item; item.widget = new KisCustomImageWidget(startupWidget, w, h, resolution, colorModel, colorDepth, colorProfile, i18n("Unnamed")); item.icon = "document-new"; startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon); QSize sz = KisClipboard::instance()->clipSize(); if (sz.isValid() && sz.width() != 0 && sz.height() != 0) { w = sz.width(); h = sz.height(); } item.widget = new KisImageFromClipboard(startupWidget, w, h, resolution, colorModel, colorDepth, colorProfile, i18n("Unnamed")); item.title = i18n("Create from Clipboard"); item.icon = "tab-new"; startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon); // calls deleteLater connect(startupWidget, SIGNAL(documentSelected(KisDocument*)), KisPart::instance(), SLOT(startCustomDocument(KisDocument*))); // calls deleteLater connect(startupWidget, SIGNAL(openTemplate(const QUrl&)), KisPart::instance(), SLOT(openTemplate(const QUrl&))); startupWidget->exec(); // Cancel calls deleteLater... } void KisMainWindow::slotImportFile() { dbgUI << "slotImportFile()"; slotFileOpen(true); } void KisMainWindow::slotFileOpen(bool isImporting) { QStringList urls = showOpenFileDialog(isImporting); if (urls.isEmpty()) return; Q_FOREACH (const QString& url, urls) { if (!url.isEmpty()) { OpenFlags flags = isImporting ? Import : None; bool res = openDocument(QUrl::fromLocalFile(url), flags); if (!res) { warnKrita << "Loading" << url << "failed"; } } } } void KisMainWindow::slotFileOpenRecent(const QUrl &url) { (void) openDocument(QUrl::fromLocalFile(url.toLocalFile()), None); } void KisMainWindow::slotFileSave() { if (saveDocument(d->activeView->document(), false, false)) { emit documentSaved(); } } void KisMainWindow::slotFileSaveAs() { if (saveDocument(d->activeView->document(), true, false)) { emit documentSaved(); } } void KisMainWindow::slotExportFile() { if (saveDocument(d->activeView->document(), true, true)) { emit documentSaved(); } } void KisMainWindow::slotShowSessionManager() { KisPart::instance()->showSessionManager(); } KoCanvasResourceManager *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::restoreWorkspace(const QByteArray &state) { QByteArray oldState = saveState(); const bool showTitlebars = KisConfig().showDockerTitleBars(); // needed because otherwise the layout isn't correctly restored in some situations Q_FOREACH (QDockWidget *dock, dockWidgets()) { dock->hide(); dock->titleBarWidget()->setVisible(showTitlebars); } bool success = KXmlGuiWindow::restoreState(state); if (!success) { KXmlGuiWindow::restoreState(oldState); Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating()); } } return false; } Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget(); dock->titleBarWidget()->setVisible(showTitlebars || (dock->isFloating() && isCollapsed)); } } return success; } KisViewManager *KisMainWindow::viewManager() const { return d->viewManager; } void KisMainWindow::slotDocumentInfo() { if (!d->activeView->document()) return; KoDocumentInfo *docInfo = d->activeView->document()->documentInfo(); if (!docInfo) return; KoDocumentInfoDlg *dlg = d->activeView->document()->createDocumentInfoDialog(this, docInfo); if (dlg->exec()) { if (dlg->isDocumentSaved()) { d->activeView->document()->setModified(false); } else { d->activeView->document()->setModified(true); } d->activeView->document()->setTitleModified(); } delete dlg; } bool KisMainWindow::slotFileCloseAll() { Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { if (subwin) { if(!subwin->close()) return false; } } updateCaption(); return true; } void KisMainWindow::slotFileQuit() { 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; cfg.setFullscreenMode(fullScreen); if (fullScreen) { setWindowState(windowState() | Qt::WindowFullScreen); // set } else { setWindowState(windowState() & ~Qt::WindowFullScreen); // reset } } void KisMainWindow::setMaxRecentItems(uint _number) { d->recentFiles->setMaxItems(_number); } void KisMainWindow::slotReloadFile() { KisDocument* document = d->activeView->document(); if (!document || document->url().isEmpty()) return; if (document->isModified()) { bool ok = QMessageBox::question(this, i18nc("@title:window", "Krita"), i18n("You will lose all changes made since your last save\n" "Do you want to continue?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes; if (!ok) return; } QUrl url = document->url(); saveWindowSettings(); if (!document->reload()) { QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Error: Could not reload this document")); } return; } QDockWidget* KisMainWindow::createDockWidget(KoDockFactoryBase* factory) { QDockWidget* dockWidget = 0; if (!d->dockWidgetsMap.contains(factory->id())) { dockWidget = factory->createDockWidget(); // It is quite possible that a dock factory cannot create the dock; don't // do anything in that case. if (!dockWidget) { warnKrita << "Could not create docker for" << factory->id(); return 0; } KoDockWidgetTitleBar *titleBar = dynamic_cast(dockWidget->titleBarWidget()); // Check if the dock widget is supposed to be collapsible if (!dockWidget->titleBarWidget()) { titleBar = new KoDockWidgetTitleBar(dockWidget); dockWidget->setTitleBarWidget(titleBar); titleBar->setCollapsable(factory->isCollapsable()); } titleBar->setFont(KoDockRegistry::dockFont()); dockWidget->setObjectName(factory->id()); dockWidget->setParent(this); if (dockWidget->widget() && dockWidget->widget()->layout()) dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1); Qt::DockWidgetArea side = Qt::RightDockWidgetArea; bool visible = true; switch (factory->defaultDockPosition()) { case KoDockFactoryBase::DockTornOff: dockWidget->setFloating(true); // position nicely? break; case KoDockFactoryBase::DockTop: side = Qt::TopDockWidgetArea; break; case KoDockFactoryBase::DockLeft: side = Qt::LeftDockWidgetArea; break; case KoDockFactoryBase::DockBottom: side = Qt::BottomDockWidgetArea; break; case KoDockFactoryBase::DockRight: side = Qt::RightDockWidgetArea; break; case KoDockFactoryBase::DockMinimized: default: side = Qt::RightDockWidgetArea; visible = false; } KConfigGroup group = 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(); } bool collapsed = factory->defaultCollapsed(); bool locked = false; group = d->windowStateConfig.group("DockWidget " + factory->id()); collapsed = group.readEntry("Collapsed", collapsed); locked = group.readEntry("Locked", locked); //dbgKrita << "docker" << factory->id() << dockWidget << "collapsed" << collapsed << "locked" << locked << "titlebar" << titleBar; if (titleBar && collapsed) titleBar->setCollapsed(true); if (titleBar && locked) titleBar->setLocked(true); d->dockWidgetsMap.insert(factory->id(), dockWidget); } else { dockWidget = d->dockWidgetsMap[factory->id()]; } #ifdef Q_OS_OSX dockWidget->setAttribute(Qt::WA_MacSmallSize, true); #endif dockWidget->setFont(KoDockRegistry::dockFont()); connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts())); return dockWidget; } void KisMainWindow::forceDockTabFonts() { Q_FOREACH (QObject *child, children()) { if (child->inherits("QTabBar")) { ((QTabBar *)child)->setFont(KoDockRegistry::dockFont()); } } } QList KisMainWindow::dockWidgets() const { return d->dockWidgetsMap.values(); } QDockWidget* KisMainWindow::dockWidget(const QString &id) { if (!d->dockWidgetsMap.contains(id)) return 0; return d->dockWidgetsMap[id]; } QList KisMainWindow::canvasObservers() const { QList observers; Q_FOREACH (QDockWidget *docker, dockWidgets()) { KoCanvasObserverBase *observer = dynamic_cast(docker); if (observer) { observers << observer; } else { warnKrita << docker << "is not a canvas observer"; } } return observers; } void KisMainWindow::toggleDockersVisibility(bool visible) { if (!visible) { d->dockerStateBeforeHiding = saveState(); Q_FOREACH (QObject* widget, children()) { if (widget->inherits("QDockWidget")) { QDockWidget* dw = static_cast(widget); if (dw->isVisible()) { dw->hide(); } } } } else { restoreState(d->dockerStateBeforeHiding); } } void KisMainWindow::slotDocumentTitleModified() { updateCaption(); updateReloadFileAction(d->activeView ? d->activeView->document() : 0); } void KisMainWindow::subWindowActivated() { bool enabled = (activeKisView() != 0); d->mdiCascade->setEnabled(enabled); d->mdiNextWindow->setEnabled(enabled); d->mdiPreviousWindow->setEnabled(enabled); d->mdiTile->setEnabled(enabled); d->close->setEnabled(enabled); d->closeAll->setEnabled(enabled); setActiveSubWindow(d->mdiArea->activeSubWindow()); Q_FOREACH (QToolBar *tb, toolBars()) { if (tb->objectName() == "BrushesAndStuff") { tb->setEnabled(enabled); } } updateCaption(); d->actionManager()->updateGUI(); } void KisMainWindow::updateWindowMenu() { QMenu *menu = d->windowMenu->menu(); menu->clear(); menu->addAction(d->newWindow); menu->addAction(d->documentMenu); QMenu *docMenu = d->documentMenu->menu(); docMenu->clear(); Q_FOREACH (QPointer doc, KisPart::instance()->documents()) { if (doc) { QString title = doc->url().toDisplayString(); if (title.isEmpty() && doc->image()) { title = doc->image()->objectName(); } QAction *action = docMenu->addAction(title); action->setIcon(qApp->windowIcon()); connect(action, SIGNAL(triggered()), d->documentMapper, SLOT(map())); d->documentMapper->setMapping(action, doc); } } menu->addAction(d->workspaceMenu); QMenu *workspaceMenu = d->workspaceMenu->menu(); workspaceMenu->clear(); auto workspaces = KisResourceServerProvider::instance()->workspaceServer(false)->resources(); auto m_this = this; for (auto &w : workspaces) { auto action = workspaceMenu->addAction(w->name()); auto ds = w->dockerState(); connect(action, &QAction::triggered, this, [=]() { m_this->restoreWorkspace(ds); }); } workspaceMenu->addSeparator(); connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&Import Workspace...")), &QAction::triggered, this, [&]() { QString extensions = d->workspacemodel->extensions(); QStringList mimeTypes; for(const QString &suffix : extensions.split(":")) { mimeTypes << KisMimeDatabase::mimeTypeForSuffix(suffix); } KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); dialog.setMimeTypeFilters(mimeTypes); dialog.setCaption(i18nc("@title:window", "Choose File to Add")); QString filename = dialog.filename(); d->workspacemodel->importResourceFile(filename); }); connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&New Workspace...")), &QAction::triggered, [=]() { QString name = QInputDialog::getText(this, i18nc("@title:window", "New Workspace..."), i18nc("@label:textbox", "Name:")); if (name.isEmpty()) return; auto rserver = KisResourceServerProvider::instance()->workspaceServer(); KisWorkspaceResource* workspace = new KisWorkspaceResource(""); workspace->setDockerState(m_this->saveState()); d->viewManager->resourceProvider()->notifySavingWorkspace(workspace); workspace->setValid(true); QString saveLocation = rserver->saveLocation(); bool newName = false; if(name.isEmpty()) { newName = true; name = i18n("Workspace"); } QFileInfo fileInfo(saveLocation + name + workspace->defaultFileExtension()); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(saveLocation + name + QString("%1").arg(i) + workspace->defaultFileExtension()); i++; } workspace->setFilename(fileInfo.filePath()); if(newName) { name = i18n("Workspace %1", i); } workspace->setName(name); rserver->addResource(workspace); }); // TODO: What to do about delete? // workspaceMenu->addAction(i18nc("@action:inmenu", "&Delete Workspace...")); menu->addSeparator(); menu->addAction(d->close); menu->addAction(d->closeAll); if (d->mdiArea->viewMode() == QMdiArea::SubWindowView) { menu->addSeparator(); menu->addAction(d->mdiTile); menu->addAction(d->mdiCascade); } menu->addSeparator(); menu->addAction(d->mdiNextWindow); menu->addAction(d->mdiPreviousWindow); menu->addSeparator(); QList windows = d->mdiArea->subWindowList(); for (int i = 0; i < windows.size(); ++i) { QPointerchild = qobject_cast(windows.at(i)->widget()); if (child && child->document()) { QString text; if (i < 9) { text = i18n("&%1 %2", i + 1, child->document()->url().toDisplayString()); } else { text = i18n("%1 %2", i + 1, child->document()->url().toDisplayString()); } QAction *action = menu->addAction(text); action->setIcon(qApp->windowIcon()); action->setCheckable(true); action->setChecked(child == activeKisView()); connect(action, SIGNAL(triggered()), d->windowMapper, SLOT(map())); d->windowMapper->setMapping(action, windows.at(i)); } } updateCaption(); } void KisMainWindow::setActiveSubWindow(QWidget *window) { if (!window) return; QMdiSubWindow *subwin = qobject_cast(window); //dbgKrita << "setActiveSubWindow();" << subwin << d->activeSubWindow; if (subwin && subwin != d->activeSubWindow) { KisView *view = qobject_cast(subwin->widget()); //dbgKrita << "\t" << view << activeView(); if (view && view != activeView()) { d->mdiArea->setActiveSubWindow(subwin); setActiveView(view); } d->activeSubWindow = subwin; } updateWindowMenu(); d->actionManager()->updateGUI(); } void KisMainWindow::configChanged() { KisConfig cfg; QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView); d->mdiArea->setViewMode(viewMode); Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); /** * Dirty workaround for a bug in Qt (checked on Qt 5.6.1): * * If you make a window "Show on top" and then switch to the tabbed mode * the window will contiue to be painted in its initial "mid-screen" * position. It will persist here until you explicitly switch to its tab. */ if (viewMode == QMdiArea::TabbedView) { Qt::WindowFlags oldFlags = subwin->windowFlags(); Qt::WindowFlags flags = oldFlags; flags &= ~Qt::WindowStaysOnTopHint; flags &= ~Qt::WindowStaysOnBottomHint; if (flags != oldFlags) { subwin->setWindowFlags(flags); subwin->showMaximized(); } } } KConfigGroup group( KSharedConfig::openConfig(), "theme"); d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark")); d->actionManager()->updateGUI(); QBrush brush(cfg.getMDIBackgroundColor()); d->mdiArea->setBackground(brush); QString backgroundImage = cfg.getMDIBackgroundImage(); if (backgroundImage != "") { QImage image(backgroundImage); QBrush brush(image); d->mdiArea->setBackground(brush); } d->mdiArea->update(); } KisView* KisMainWindow::newView(QObject *document) { KisDocument *doc = qobject_cast(document); KisView *view = addViewAndNotifyLoadingCompleted(doc); d->actionManager()->updateGUI(); return view; } void KisMainWindow::newWindow() { KisPart::instance()->createMainWindow()->show(); } void KisMainWindow::closeCurrentWindow() { d->mdiArea->currentSubWindow()->close(); d->actionManager()->updateGUI(); } void KisMainWindow::checkSanity() { // print error if the lcms engine is not available if (!KoColorSpaceEngineRegistry::instance()->contains("icc")) { // need to wait 1 event since exiting here would not work. m_errorMessage = i18n("The Krita LittleCMS color management plugin is not installed. Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); if (rserver->resources().isEmpty()) { m_errorMessage = i18n("Krita cannot find any brush presets! Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } } void KisMainWindow::showErrorAndDie() { QMessageBox::critical(0, i18nc("@title:window", "Installation error"), m_errorMessage); if (m_dieOnError) { exit(10); } } void KisMainWindow::showAboutApplication() { KisAboutApplication dlg(this); dlg.exec(); } QPointerKisMainWindow::activeKisView() { if (!d->mdiArea) return 0; QMdiSubWindow *activeSubWindow = d->mdiArea->activeSubWindow(); //dbgKrita << "activeKisView" << activeSubWindow; if (!activeSubWindow) return 0; return qobject_cast(activeSubWindow->widget()); } void KisMainWindow::newOptionWidgets(KoCanvasController *controller, const QList > &optionWidgetList) { KIS_ASSERT_RECOVER_NOOP(controller == KoToolManager::instance()->activeCanvasController()); bool isOurOwnView = false; Q_FOREACH (QPointer view, KisPart::instance()->views()) { if (view && view->canvasController() == controller) { isOurOwnView = view->mainWindow() == this; } } if (!isOurOwnView) return; Q_FOREACH (QWidget *w, optionWidgetList) { #ifdef Q_OS_OSX w->setAttribute(Qt::WA_MacSmallSize, true); #endif w->setFont(KoDockRegistry::dockFont()); } if (d->toolOptionsDocker) { d->toolOptionsDocker->setOptionWidgets(optionWidgetList); } else { d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList); } } void KisMainWindow::applyDefaultSettings(QPrinter &printer) { if (!d->activeView) return; QString title = d->activeView->document()->documentInfo()->aboutInfo("title"); if (title.isEmpty()) { QFileInfo info(d->activeView->document()->url().fileName()); title = info.baseName(); } if (title.isEmpty()) { // #139905 title = i18n("%1 unsaved document (%2)", qApp->applicationDisplayName(), QLocale().toString(QDate::currentDate(), QLocale::ShortFormat)); } printer.setDocName(title); } void KisMainWindow::createActions() { KisActionManager *actionManager = d->actionManager(); KisConfig cfg; actionManager->createStandardAction(KStandardAction::New, this, SLOT(slotFileNew())); actionManager->createStandardAction(KStandardAction::Open, this, SLOT(slotFileOpen())); actionManager->createStandardAction(KStandardAction::Quit, this, SLOT(slotFileQuit())); actionManager->createStandardAction(KStandardAction::ConfigureToolbars, this, SLOT(slotConfigureToolbars())); 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->exportPdf = actionManager->createAction("file_export_pdf"); // connect(d->exportPdf, SIGNAL(triggered()), this, SLOT(exportToPdf())); d->importAnimation = actionManager->createAction("file_import_animation"); connect(d->importAnimation, SIGNAL(triggered()), this, SLOT(importAnimation())); d->closeAll = actionManager->createAction("file_close_all"); connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll())); // d->reloadFile = actionManager->createAction("file_reload_file"); // d->reloadFile->setActivationFlags(KisAction::CURRENT_IMAGE_MODIFIED); // connect(d->reloadFile, SIGNAL(triggered(bool)), this, SLOT(slotReloadFile())); d->importFile = actionManager->createAction("file_import_file"); connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile())); d->exportFile = actionManager->createAction("file_export_file"); connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile())); /* The following entry opens the document information dialog. Since the action is named so it intends to show data this entry should not have a trailing ellipses (...). */ d->showDocumentInfo = actionManager->createAction("file_documentinfo"); connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo())); d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this)); d->themeManager->registerThemeActions(actionCollection()); connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); d->toggleDockers = actionManager->createAction("view_toggledockers"); cfg.showDockers(true); d->toggleDockers->setChecked(true); connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool))); d->toggleDockerTitleBars = actionManager->createAction("view_toggledockertitlebars"); d->toggleDockerTitleBars->setChecked(cfg.showDockerTitleBars()); connect(d->toggleDockerTitleBars, SIGNAL(toggled(bool)), SLOT(showDockerTitleBars(bool))); actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu); actionCollection()->addAction("window", d->windowMenu); d->mdiCascade = actionManager->createAction("windows_cascade"); connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows())); d->mdiTile = actionManager->createAction("windows_tile"); connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows())); d->mdiNextWindow = actionManager->createAction("windows_next"); connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow())); d->mdiPreviousWindow = actionManager->createAction("windows_previous"); connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow())); d->newWindow = actionManager->createAction("view_newwindow"); connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow())); d->close = actionManager->createAction("file_close"); connect(d->close, SIGNAL(triggered()), SLOT(closeCurrentWindow())); 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 componensate for the window decs w = (deskWidth / 3) * 2; h = (desk.height() / 3) * 2; } else { w = desk.width(); h = desk.height(); } x += (desk.width() - w) / 2; y += (desk.height() - h) / 2; move(x,y); setGeometry(geometry().x(), geometry().y(), w, h); } restoreWorkspace(QByteArray::fromBase64(cfg.readEntry("State", QByteArray()))); d->fullScreenMode->setChecked(isFullScreen()); } void KisMainWindow::showManual() { QDesktopServices::openUrl(QUrl("https://docs.krita.org")); } void KisMainWindow::showDockerTitleBars(bool show) { Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget(); dock->titleBarWidget()->setVisible(show || (dock->isFloating() && isCollapsed)); } } KisConfig cfg; cfg.setShowDockerTitleBars(show); } void KisMainWindow::moveEvent(QMoveEvent *e) { if (qApp->desktop()->screenNumber(this) != qApp->desktop()->screenNumber(e->oldPos())) { KisConfigNotifier::instance()->notifyConfigChanged(); } } #include diff --git a/libs/ui/flake/kis_shape_layer.cc b/libs/ui/flake/kis_shape_layer.cc index bdd6cd651b..801816cd8d 100644 --- a/libs/ui/flake/kis_shape_layer.cc +++ b/libs/ui/flake/kis_shape_layer.cc @@ -1,675 +1,704 @@ /* * Copyright (c) 2006-2008 Boudewijn Rempt * Copyright (c) 2007 Thomas Zander * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2011 Jan Hambrecht * * 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_shape_layer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "SvgWriter.h" #include "SvgParser.h" #include #include #include "kis_default_bounds.h" #include #include "kis_shape_layer_canvas.h" #include "kis_image_view_converter.h" #include #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_effect_mask.h" +#include "commands/KoShapeReorderCommand.h" #include class ShapeLayerContainerModel : public SimpleShapeContainerModel { public: ShapeLayerContainerModel(KisShapeLayer *parent) : q(parent) {} void add(KoShape *child) override { SimpleShapeContainerModel::add(child); /** * The shape is always added with the absolute transformation set appropriately. * Here we should just squeeze it into the layer's transformation. */ KIS_SAFE_ASSERT_RECOVER_NOOP(inheritsTransform(child)); if (inheritsTransform(child)) { QTransform parentTransform = q->absoluteTransformation(0); child->applyAbsoluteTransformation(parentTransform.inverted()); } } void remove(KoShape *child) override { KIS_SAFE_ASSERT_RECOVER_NOOP(inheritsTransform(child)); if (inheritsTransform(child)) { QTransform parentTransform = q->absoluteTransformation(0); child->applyAbsoluteTransformation(parentTransform); } SimpleShapeContainerModel::remove(child); } void shapeHasBeenAddedToHierarchy(KoShape *shape, KoShapeContainer *addedToSubtree) override { q->shapeManager()->addShape(shape); SimpleShapeContainerModel::shapeHasBeenAddedToHierarchy(shape, addedToSubtree); } void shapeToBeRemovedFromHierarchy(KoShape *shape, KoShapeContainer *removedFromSubtree) override { q->shapeManager()->remove(shape); SimpleShapeContainerModel::shapeToBeRemovedFromHierarchy(shape, removedFromSubtree); } private: KisShapeLayer *q; }; struct KisShapeLayer::Private { public: Private() : canvas(0) , controller(0) , x(0) , y(0) {} KisPaintDeviceSP paintDevice; KisShapeLayerCanvasBase * canvas; KoShapeBasedDocumentBase* controller; int x; int y; }; KisShapeLayer::KisShapeLayer(KoShapeBasedDocumentBase* controller, KisImageWSP image, const QString &name, quint8 opacity) : KisExternalLayer(image, name, opacity), KoShapeLayer(new ShapeLayerContainerModel(this)), m_d(new Private()) { initShapeLayer(controller); } KisShapeLayer::KisShapeLayer(const KisShapeLayer& rhs) : KisShapeLayer(rhs, rhs.m_d->controller) { } KisShapeLayer::KisShapeLayer(const KisShapeLayer& _rhs, KoShapeBasedDocumentBase* controller) : KisExternalLayer(_rhs) , KoShapeLayer(new ShapeLayerContainerModel(this)) //no _rhs here otherwise both layer have the same KoShapeContainerModel , m_d(new Private()) { // copy the projection to avoid extra round of updates! initShapeLayer(controller, _rhs.m_d->paintDevice); + /** + * The transformaitons of the added shapes are automatically merged into the transformation + * of the layer, so we should apply this extra transform separately + */ + const QTransform thisInvertedTransform = this->absoluteTransformation(0).inverted(); + Q_FOREACH (KoShape *shape, _rhs.shapes()) { KoShape *clonedShape = shape->cloneShape(); KIS_SAFE_ASSERT_RECOVER(clonedShape) { continue; } + clonedShape->setTransformation(shape->absoluteTransformation(0) * thisInvertedTransform); addShape(clonedShape); } } KisShapeLayer::KisShapeLayer(const KisShapeLayer& _rhs, const KisShapeLayer &_addShapes) : KisExternalLayer(_rhs) , KoShapeLayer(new ShapeLayerContainerModel(this)) //no _merge here otherwise both layer have the same KoShapeContainerModel , m_d(new Private()) { // Make sure our new layer is visible otherwise the shapes cannot be painted. setVisible(true); initShapeLayer(_rhs.m_d->controller); + /** + * With current implementation this matrix will always be an identity, because + * we do not copy the transformation from any of the source layers. But we should + * handle this anyway, to not be caught by this in the future. + */ + const QTransform thisInvertedTransform = this->absoluteTransformation(0).inverted(); + + QList shapesAbove; + QList shapesBelow; + // copy in _rhs's shapes Q_FOREACH (KoShape *shape, _rhs.shapes()) { KoShape *clonedShape = shape->cloneShape(); KIS_SAFE_ASSERT_RECOVER(clonedShape) { continue; } - addShape(clonedShape); + clonedShape->setTransformation(shape->absoluteTransformation(0) * thisInvertedTransform); + shapesBelow.append(clonedShape); } // copy in _addShapes's shapes Q_FOREACH (KoShape *shape, _addShapes.shapes()) { KoShape *clonedShape = shape->cloneShape(); KIS_SAFE_ASSERT_RECOVER(clonedShape) { continue; } - addShape(clonedShape); + clonedShape->setTransformation(shape->absoluteTransformation(0) * thisInvertedTransform); + shapesAbove.append(clonedShape); + } + + QList shapes = + KoShapeReorderCommand::mergeDownShapes(shapesBelow, shapesAbove); + KoShapeReorderCommand cmd(shapes); + cmd.redo(); + + Q_FOREACH (KoShape *shape, shapesBelow + shapesAbove) { + addShape(shape); } } KisShapeLayer::KisShapeLayer(KoShapeBasedDocumentBase* controller, KisImageWSP image, const QString &name, quint8 opacity, KisShapeLayerCanvasBase *canvas) : KisExternalLayer(image, name, opacity) , KoShapeLayer(new ShapeLayerContainerModel(this)) , m_d(new Private()) { initShapeLayer(controller, nullptr, canvas); } KisShapeLayer::~KisShapeLayer() { /** * Small hack alert: we should avoid updates on shape deletion */ m_d->canvas->prepareForDestroying(); Q_FOREACH (KoShape *shape, shapes()) { shape->setParent(0); delete shape; } delete m_d->canvas; delete m_d; } void KisShapeLayer::initShapeLayer(KoShapeBasedDocumentBase* controller, KisPaintDeviceSP copyFromProjection, KisShapeLayerCanvasBase *canvas) { setSupportsLodMoves(false); setShapeId(KIS_SHAPE_LAYER_ID); KIS_ASSERT_RECOVER_NOOP(this->image()); if (!copyFromProjection) { m_d->paintDevice = new KisPaintDevice(image()->colorSpace()); m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(this->image())); m_d->paintDevice->setParentNode(this); } else { m_d->paintDevice = new KisPaintDevice(*copyFromProjection); } if (!canvas) { auto *slCanvas = new KisShapeLayerCanvas(this, image()); slCanvas->setProjection(m_d->paintDevice); canvas = slCanvas; } m_d->canvas = canvas; m_d->canvas->moveToThread(this->thread()); m_d->controller = controller; m_d->canvas->shapeManager()->selection()->disconnect(this); connect(m_d->canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged())); connect(m_d->canvas->selectedShapesProxy(), SIGNAL(currentLayerChanged(const KoShapeLayer*)), this, SIGNAL(currentLayerChanged(const KoShapeLayer*))); connect(this, SIGNAL(sigMoveShapes(const QPointF&)), SLOT(slotMoveShapes(const QPointF&))); } bool KisShapeLayer::allowAsChild(KisNodeSP node) const { return node->inherits("KisMask"); } void KisShapeLayer::setImage(KisImageWSP _image) { KisLayer::setImage(_image); m_d->canvas->setImage(_image); m_d->paintDevice->convertTo(_image->colorSpace()); m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(_image)); } KisLayerSP KisShapeLayer::createMergedLayerTemplate(KisLayerSP prevLayer) { KisShapeLayer *prevShape = dynamic_cast(prevLayer.data()); if (prevShape) return new KisShapeLayer(*prevShape, *this); else return KisExternalLayer::createMergedLayerTemplate(prevLayer); } void KisShapeLayer::fillMergedLayerTemplate(KisLayerSP dstLayer, KisLayerSP prevLayer) { if (!dynamic_cast(dstLayer.data())) { KisLayer::fillMergedLayerTemplate(dstLayer, prevLayer); } } void KisShapeLayer::setParent(KoShapeContainer *parent) { Q_UNUSED(parent) KIS_ASSERT_RECOVER_RETURN(0) } QIcon KisShapeLayer::icon() const { return KisIconUtils::loadIcon("vectorLayer"); } KisPaintDeviceSP KisShapeLayer::original() const { return m_d->paintDevice; } KisPaintDeviceSP KisShapeLayer::paintDevice() const { return 0; } qint32 KisShapeLayer::x() const { return m_d->x; } qint32 KisShapeLayer::y() const { return m_d->y; } void KisShapeLayer::setX(qint32 x) { qint32 delta = x - this->x(); QPointF diff = QPointF(m_d->canvas->viewConverter()->viewToDocumentX(delta), 0); emit sigMoveShapes(diff); // Save new value to satisfy LSP m_d->x = x; } void KisShapeLayer::setY(qint32 y) { qint32 delta = y - this->y(); QPointF diff = QPointF(0, m_d->canvas->viewConverter()->viewToDocumentY(delta)); emit sigMoveShapes(diff); // Save new value to satisfy LSP m_d->y = y; } namespace { void filterTransformableShapes(QList &shapes) { auto it = shapes.begin(); while (it != shapes.end()) { if (shapes.size() == 1) break; if ((*it)->inheritsTransformFromAny(shapes)) { it = shapes.erase(it); } else { ++it; } } } } QList KisShapeLayer::shapesToBeTransformed() { QList shapes = shapeManager()->shapes(); // We expect that **all** the shapes inherit the transform from its parent // SANITY_CHECK: we expect all the shapes inside the // shape layer to inherit transform! Q_FOREACH (KoShape *shape, shapes) { if (shape->parent()) { KIS_SAFE_ASSERT_RECOVER(shape->parent()->inheritsTransform(shape)) { break; } } } shapes << this; filterTransformableShapes(shapes); return shapes; } void KisShapeLayer::slotMoveShapes(const QPointF &diff) { QList shapes = shapesToBeTransformed(); if (shapes.isEmpty()) return; KoShapeMoveCommand cmd(shapes, diff); cmd.redo(); } bool KisShapeLayer::accept(KisNodeVisitor& visitor) { return visitor.visit(this); } void KisShapeLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } KoShapeManager* KisShapeLayer::shapeManager() const { return m_d->canvas->shapeManager(); } KoViewConverter* KisShapeLayer::converter() const { return m_d->canvas->viewConverter(); } bool KisShapeLayer::visible(bool recursive) const { return KisExternalLayer::visible(recursive); } void KisShapeLayer::setVisible(bool visible, bool isLoading) { KisExternalLayer::setVisible(visible, isLoading); } void KisShapeLayer::forceUpdateTimedNode() { m_d->canvas->forceRepaint(); } #include "SvgWriter.h" #include "SvgParser.h" bool KisShapeLayer::saveShapesToStore(KoStore *store, QList shapes, const QSizeF &sizeInPt) { if (!store->open("content.svg")) { return false; } KoStoreDevice storeDev(store); storeDev.open(QIODevice::WriteOnly); std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex); SvgWriter writer(shapes); writer.save(storeDev, sizeInPt); if (!store->close()) { return false; } return true; } QList KisShapeLayer::createShapesFromSvg(QIODevice *device, const QString &baseXmlDir, const QRectF &rectInPixels, qreal resolutionPPI, KoDocumentResourceManager *resourceManager, QSizeF *fragmentSize) { QString errorMsg; int errorLine = 0; int errorColumn; KoXmlDocument doc; bool ok = doc.setContent(device, false, &errorMsg, &errorLine, &errorColumn); if (!ok) { errKrita << "Parsing error in " << "contents.svg" << "! Aborting!" << endl << " In line: " << errorLine << ", column: " << errorColumn << endl << " Error message: " << errorMsg << endl; errKrita << i18n("Parsing error in the main document at line %1, column %2\nError message: %3" , errorLine , errorColumn , errorMsg); } SvgParser parser(resourceManager); parser.setXmlBaseDir(baseXmlDir); parser.setResolution(rectInPixels /* px */, resolutionPPI /* ppi */); return parser.parseSvg(doc.documentElement(), fragmentSize); } bool KisShapeLayer::saveLayer(KoStore * store) const { // FIXME: we handle xRes() only! const QSizeF sizeInPx = image()->bounds().size(); const QSizeF sizeInPt(sizeInPx.width() / image()->xRes(), sizeInPx.height() / image()->yRes()); return saveShapesToStore(store, this->shapes(), sizeInPt); } bool KisShapeLayer::loadSvg(QIODevice *device, const QString &baseXmlDir) { QSizeF fragmentSize; // unused! KisImageSP image = this->image(); // FIXME: we handle xRes() only! KIS_SAFE_ASSERT_RECOVER_NOOP(qFuzzyCompare(image->xRes(), image->yRes())); const qreal resolutionPPI = 72.0 * image->xRes(); QList shapes = createShapesFromSvg(device, baseXmlDir, image->bounds(), resolutionPPI, m_d->controller->resourceManager(), &fragmentSize); Q_FOREACH (KoShape *shape, shapes) { addShape(shape); } return true; } bool KisShapeLayer::loadLayer(KoStore* store) { if (!store) { warnKrita << i18n("No store backend"); return false; } if (store->open("content.svg")) { KoStoreDevice storeDev(store); storeDev.open(QIODevice::ReadOnly); loadSvg(&storeDev, ""); store->close(); return true; } KoOdfReadStore odfStore(store); QString errorMessage; odfStore.loadAndParse(errorMessage); if (!errorMessage.isEmpty()) { warnKrita << errorMessage; return false; } KoXmlElement contents = odfStore.contentDoc().documentElement(); // dbgKrita <<"Start loading OASIS document..." << contents.text(); // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().localName(); // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().namespaceURI(); // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().isElement(); KoXmlElement body(KoXml::namedItemNS(contents, KoXmlNS::office, "body")); if (body.isNull()) { //setErrorMessage( i18n( "Invalid OASIS document. No office:body tag found." ) ); return false; } body = KoXml::namedItemNS(body, KoXmlNS::office, "drawing"); if (body.isNull()) { //setErrorMessage( i18n( "Invalid OASIS document. No office:drawing tag found." ) ); return false; } KoXmlElement page(KoXml::namedItemNS(body, KoXmlNS::draw, "page")); if (page.isNull()) { //setErrorMessage( i18n( "Invalid OASIS document. No draw:page tag found." ) ); return false; } KoXmlElement * master = 0; if (odfStore.styles().masterPages().contains("Standard")) master = odfStore.styles().masterPages().value("Standard"); else if (odfStore.styles().masterPages().contains("Default")) master = odfStore.styles().masterPages().value("Default"); else if (! odfStore.styles().masterPages().empty()) master = odfStore.styles().masterPages().begin().value(); if (master) { const KoXmlElement *style = odfStore.styles().findStyle( master->attributeNS(KoXmlNS::style, "page-layout-name", QString())); KoPageLayout pageLayout; pageLayout.loadOdf(*style); setSize(QSizeF(pageLayout.width, pageLayout.height)); } // We work fine without a master page KoOdfLoadingContext context(odfStore.styles(), odfStore.store()); context.setManifestFile(QString("tar:/") + odfStore.store()->currentPath() + "META-INF/manifest.xml"); KoShapeLoadingContext shapeContext(context, m_d->controller->resourceManager()); KoXmlElement layerElement; forEachElement(layerElement, context.stylesReader().layerSet()) { // FIXME: investigate what is this // KoShapeLayer * l = new KoShapeLayer(); if (!loadOdf(layerElement, shapeContext)) { dbgKrita << "Could not load vector layer!"; return false; } } KoXmlElement child; forEachElement(child, page) { KoShape * shape = KoShapeRegistry::instance()->createShapeFromOdf(child, shapeContext); if (shape) { addShape(shape); } } return true; } void KisShapeLayer::resetCache() { m_d->paintDevice->clear(); QList shapes = m_d->canvas->shapeManager()->shapes(); Q_FOREACH (const KoShape* shape, shapes) { shape->update(); } } KUndo2Command* KisShapeLayer::crop(const QRect & rect) { QPoint oldPos(x(), y()); QPoint newPos = oldPos - rect.topLeft(); return new KisNodeMoveCommand2(this, oldPos, newPos); } KUndo2Command* KisShapeLayer::transform(const QTransform &transform) { QList shapes = shapesToBeTransformed(); if (shapes.isEmpty()) return 0; KisImageViewConverter *converter = dynamic_cast(this->converter()); QTransform realTransform = converter->documentToView() * transform * converter->viewToDocument(); QList oldTransformations; QList newTransformations; QList newShadows; const qreal transformBaseScale = KoUnit::approxTransformScale(transform); Q_FOREACH (const KoShape* shape, shapes) { QTransform oldTransform = shape->transformation(); oldTransformations.append(oldTransform); QTransform globalTransform = shape->absoluteTransformation(0); QTransform localTransform = globalTransform * realTransform * globalTransform.inverted(); newTransformations.append(localTransform * oldTransform); KoShapeShadow *shadow = 0; if (shape->shadow()) { shadow = new KoShapeShadow(*shape->shadow()); shadow->setOffset(transformBaseScale * shadow->offset()); shadow->setBlur(transformBaseScale * shadow->blur()); } newShadows.append(shadow); } KUndo2Command *parentCommand = new KUndo2Command(); new KoShapeTransformCommand(shapes, oldTransformations, newTransformations, parentCommand); new KoShapeShadowCommand(shapes, newShadows, parentCommand); return parentCommand; } KoShapeBasedDocumentBase *KisShapeLayer::shapeController() const { return m_d->controller; } diff --git a/libs/ui/flake/kis_shape_layer_canvas.cpp b/libs/ui/flake/kis_shape_layer_canvas.cpp index bf3e6eed14..ab7f3500b8 100644 --- a/libs/ui/flake/kis_shape_layer_canvas.cpp +++ b/libs/ui/flake/kis_shape_layer_canvas.cpp @@ -1,264 +1,319 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_shape_layer_canvas.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_image_view_converter.h" #include #include #include #include #include "kis_image.h" +#include "kis_global.h" //#define DEBUG_REPAINT KisShapeLayerCanvasBase::KisShapeLayerCanvasBase(KisShapeLayer *parent, KisImageWSP image) : KoCanvasBase(0) , m_isDestroying(false) , m_viewConverter(new KisImageViewConverter(image)) , m_shapeManager(new KoShapeManager(this)) , m_selectedShapesProxy(new KoSelectedShapesProxySimple(m_shapeManager.data())) { m_shapeManager->selection()->setActiveLayer(parent); } KoShapeManager *KisShapeLayerCanvasBase::shapeManager() const { return m_shapeManager.data(); } KoSelectedShapesProxy *KisShapeLayerCanvasBase::selectedShapesProxy() const { return m_selectedShapesProxy.data(); } KoViewConverter* KisShapeLayerCanvasBase::viewConverter() const { return m_viewConverter.data(); } void KisShapeLayerCanvasBase::gridSize(QPointF *offset, QSizeF *spacing) const { KIS_SAFE_ASSERT_RECOVER_NOOP(false); // This should never be called as this canvas should have no tools. Q_UNUSED(offset); Q_UNUSED(spacing); } bool KisShapeLayerCanvasBase::snapToGrid() const { KIS_SAFE_ASSERT_RECOVER_NOOP(false); // This should never be called as this canvas should have no tools. return false; } void KisShapeLayerCanvasBase::addCommand(KUndo2Command *) { KIS_SAFE_ASSERT_RECOVER_NOOP(false); // This should never be called as this canvas should have no tools. } KoToolProxy * KisShapeLayerCanvasBase::toolProxy() const { // KIS_SAFE_ASSERT_RECOVER_NOOP(false); // This should never be called as this canvas should have no tools. return 0; } QWidget* KisShapeLayerCanvasBase::canvasWidget() { return 0; } const QWidget* KisShapeLayerCanvasBase::canvasWidget() const { return 0; } KoUnit KisShapeLayerCanvasBase::unit() const { KIS_SAFE_ASSERT_RECOVER_NOOP(false); // This should never be called as this canvas should have no tools. return KoUnit(KoUnit::Point); } void KisShapeLayerCanvasBase::prepareForDestroying() { m_isDestroying = true; } KisShapeLayerCanvas::KisShapeLayerCanvas(KisShapeLayer *parent, KisImageWSP image) : KisShapeLayerCanvasBase(parent, image) , m_projection(0) , m_parentLayer(parent) + , m_asyncUpdateSignalCompressor(100, KisSignalCompressor::FIRST_INACTIVE) , m_image(image) { + /** + * The layour should also add itself to its own shape manager, so that the canvas + * would track its changes/transformations + */ + m_shapeManager->addShape(parent, KoShapeManager::AddWithoutRepaint); + m_shapeManager->selection()->setActiveLayer(parent); + connect(this, SIGNAL(forwardRepaint()), SLOT(repaint()), Qt::QueuedConnection); + connect(&m_asyncUpdateSignalCompressor, SIGNAL(timeout()), SLOT(slotStartAsyncRepaint())); + + connect(m_image, SIGNAL(sigSizeChanged(const QPointF &, const QPointF &)), SLOT(slotImageSizeChanged())); + m_cachedImageRect = m_image->bounds(); } KisShapeLayerCanvas::~KisShapeLayerCanvas() { + m_shapeManager->remove(m_parentLayer); } void KisShapeLayerCanvas::setImage(KisImageWSP image) { m_viewConverter->setImage(image); } #ifdef DEBUG_REPAINT # include #endif class KisRepaintShapeLayerLayerJob : public KisSpontaneousJob { public: - KisRepaintShapeLayerLayerJob(KisShapeLayerSP layer) : m_layer(layer) {} + KisRepaintShapeLayerLayerJob(KisShapeLayerSP layer, KisShapeLayerCanvas *canvas) + : m_layer(layer), + m_canvas(canvas) + { + } bool overrides(const KisSpontaneousJob *_otherJob) override { const KisRepaintShapeLayerLayerJob *otherJob = dynamic_cast(_otherJob); - return otherJob && otherJob->m_layer == m_layer; + return otherJob && otherJob->m_canvas == m_canvas; } void run() override { - m_layer->forceUpdateTimedNode(); + m_canvas->repaint(); } int levelOfDetail() const override { return 0; } private: + + // we store a pointer to the layer just + // to keep the lifetime of the canvas! KisShapeLayerSP m_layer; + + KisShapeLayerCanvas *m_canvas; }; -void KisShapeLayerCanvas::updateCanvas(const QRectF& rc) +void KisShapeLayerCanvas::updateCanvas(const QVector ®ion) { - dbgUI << "KisShapeLayerCanvas::updateCanvas()" << rc; - //image is 0, if parentLayer is being deleted so don't update if (!m_parentLayer->image() || m_isDestroying) { return; } - QRect r = m_viewConverter->documentToView(rc).toRect(); - r.adjust(-2, -2, 2, 2); // for antialias - { QMutexLocker locker(&m_dirtyRegionMutex); - m_dirtyRegion += r; - qreal x, y; - m_viewConverter->zoom(&x, &y); + Q_FOREACH (const QRectF &rc, region) { + // grow for antialiasing + const QRect imageRect = kisGrowRect(m_viewConverter->documentToView(rc).toAlignedRect(), 2); + m_dirtyRegion += imageRect; + } } /** * HACK ALERT! * * The shapes may be accessed from both, GUI and worker threads! And we have no real * guard against this until the vector tools will be ported to the strokes framework. * * Here we just avoid the most obvious conflict of threads: * * 1) If the layer if modified by a non-gui (worker) thread, use a spontaneous jobs * to rerender the canvas. The job will be executed (almost) exclusively and it is * the responsibility of the worker thread to add a barrier to wait until this job is * completed, and not try to access the shapes concurrently. * * 2) If the layer is modified by a gui thread, it means that we are being accessed by * a legacy vector tool. It this case just emit a queued signal to make sure the updates * are compressed a little bit (TODO: add a compressor?) */ if (qApp->thread() == QThread::currentThread()) { emit forwardRepaint(); } else { - m_image->addSpontaneousJob(new KisRepaintShapeLayerLayerJob(m_parentLayer)); + m_asyncUpdateSignalCompressor.start(); + m_hasUpdateInCompressor = true; + } +} + + +void KisShapeLayerCanvas::updateCanvas(const QRectF& rc) +{ + updateCanvas(QVector({rc})); +} + +void KisShapeLayerCanvas::slotStartAsyncRepaint() +{ + m_hasUpdateInCompressor = false; + m_image->addSpontaneousJob(new KisRepaintShapeLayerLayerJob(m_parentLayer, this)); +} + +void KisShapeLayerCanvas::slotImageSizeChanged() +{ + QRegion dirtyCacheRegion; + dirtyCacheRegion += m_image->bounds(); + dirtyCacheRegion += m_cachedImageRect; + dirtyCacheRegion -= m_image->bounds() & m_cachedImageRect; + + QVector dirtyRects; + Q_FOREACH (const QRect &rc, dirtyCacheRegion.rects()) { + dirtyRects.append(m_viewConverter->viewToDocument(rc)); } + updateCanvas(dirtyRects); + + m_cachedImageRect = m_image->bounds(); } void KisShapeLayerCanvas::repaint() { QRect r; { QMutexLocker locker(&m_dirtyRegionMutex); r = m_dirtyRegion.boundingRect(); m_dirtyRegion = QRegion(); } if (r.isEmpty()) return; + // Crop the update rect by the image bounds. We keep the cache consistent + // by tracking the size of the image in slotImageSizeChanged() r = r.intersected(m_parentLayer->image()->bounds()); + QImage image(r.width(), r.height(), QImage::Format_ARGB32); image.fill(0); QPainter p(&image); p.setRenderHint(QPainter::Antialiasing); p.setRenderHint(QPainter::TextAntialiasing); p.translate(-r.x(), -r.y()); p.setClipRect(r); #ifdef DEBUG_REPAINT QColor color = QColor(random() % 255, random() % 255, random() % 255); p.fillRect(r, color); #endif m_shapeManager->paint(p, *m_viewConverter, false); p.end(); KisPaintDeviceSP dev = new KisPaintDevice(m_projection->colorSpace()); dev->convertFromQImage(image, 0); KisPainter::copyAreaOptimized(r.topLeft(), dev, m_projection, QRect(QPoint(), r.size())); m_parentLayer->setDirty(r); } void KisShapeLayerCanvas::forceRepaint() { /** * WARNING! Although forceRepaint() may be called from different threads, it is * not entirely safe. If the user plays with shapes at the same time (vector tools are * not ported to strokes yet), the shapes my be accessed from two different places at * the same time, which will cause a crash. * * The only real solution to this is to port vector tools to strokes framework. */ - repaint(); + if (m_hasUpdateInCompressor) { + m_asyncUpdateSignalCompressor.stop(); + slotStartAsyncRepaint(); + } } diff --git a/libs/ui/flake/kis_shape_layer_canvas.h b/libs/ui/flake/kis_shape_layer_canvas.h index 4c232b0744..7a44628b6d 100644 --- a/libs/ui/flake/kis_shape_layer_canvas.h +++ b/libs/ui/flake/kis_shape_layer_canvas.h @@ -1,110 +1,120 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_SHAPE_LAYER_CANVAS_H #define KIS_SHAPE_LAYER_CANVAS_H #include #include #include #include +#include "kis_thread_safe_signal_compressor.h" #include #include #include class KoShapeManager; class KoToolProxy; class KoViewConverter; class KUndo2Command; class QWidget; class KoUnit; class KisImageViewConverter; class KisShapeLayerCanvasBase : public KoCanvasBase { public: KisShapeLayerCanvasBase(KisShapeLayer *parent, KisImageWSP image); virtual void setImage(KisImageWSP image) = 0; void prepareForDestroying(); virtual void forceRepaint() = 0; KoShapeManager *shapeManager() const override; KoViewConverter *viewConverter() const override; void gridSize(QPointF *offset, QSizeF *spacing) const override; bool snapToGrid() const override; void addCommand(KUndo2Command *command) override; KoSelectedShapesProxy *selectedShapesProxy() const override; KoToolProxy * toolProxy() const override; QWidget* canvasWidget() override; const QWidget* canvasWidget() const override; KoUnit unit() const override; void updateInputMethodInfo() override {} void setCursor(const QCursor &) override {} protected: QScopedPointer m_viewConverter; QScopedPointer m_shapeManager; QScopedPointer m_selectedShapesProxy; bool m_isDestroying = false; }; /** * KisShapeLayerCanvas is a special canvas implementation that Krita * uses for non-krita shapes to request updates on. * * Do NOT give this canvas to tools or to the KoCanvasController, it's * not made for that. */ class KisShapeLayerCanvas : public KisShapeLayerCanvasBase { Q_OBJECT public: KisShapeLayerCanvas(KisShapeLayer *parent, KisImageWSP image); ~KisShapeLayerCanvas() override; /// This canvas won't render onto a widget, but a projection void setProjection(KisPaintDeviceSP projection) { m_projection = projection; } void setImage(KisImageWSP image) override; void updateCanvas(const QRectF& rc) override; + void updateCanvas(const QVector ®ion); void forceRepaint() override; private Q_SLOTS: + friend class KisRepaintShapeLayerLayerJob; void repaint(); + void slotStartAsyncRepaint(); + void slotImageSizeChanged(); Q_SIGNALS: void forwardRepaint(); private: KisPaintDeviceSP m_projection; KisShapeLayer *m_parentLayer; + KisThreadSafeSignalCompressor m_asyncUpdateSignalCompressor; + volatile bool m_hasUpdateInCompressor = false; + QRegion m_dirtyRegion; QMutex m_dirtyRegionMutex; - KisImageSP m_image; + QRect m_cachedImageRect; + + KisImageWSP m_image; }; #endif diff --git a/libs/ui/kis_import_catcher.cc b/libs/ui/kis_import_catcher.cc index e529f44ab8..acdaaada8d 100644 --- a/libs/ui/kis_import_catcher.cc +++ b/libs/ui/kis_import_catcher.cc @@ -1,141 +1,140 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_import_catcher.h" #include #include #include #include #include #include "kis_node_manager.h" #include "kis_count_visitor.h" #include "KisViewManager.h" #include "KisDocument.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_painter.h" #include "kis_selection.h" #include "kis_node_commands_adapter.h" #include "kis_group_layer.h" #include "kis_statusbar.h" #include "kis_progress_widget.h" #include "kis_config.h" #include "KisPart.h" struct KisImportCatcher::Private { public: KisDocument* doc; KisViewManager* view; QUrl url; QString layerType; QString prettyLayerName() const; void importAsPaintLayer(KisPaintDeviceSP device); void importAsTransparencyMask(KisPaintDeviceSP device); }; QString KisImportCatcher::Private::prettyLayerName() const { QString name = url.fileName(); return !name.isEmpty() ? name : url.toDisplayString(); } void KisImportCatcher::Private::importAsPaintLayer(KisPaintDeviceSP device) { KisLayerSP newLayer = new KisPaintLayer(view->image(), prettyLayerName(), OPACITY_OPAQUE_U8, device); KisNodeSP parent = 0; KisLayerSP currentActiveLayer = view->activeLayer(); if (currentActiveLayer) { parent = currentActiveLayer->parent(); } if (parent.isNull()) { parent = view->image()->rootLayer(); } KisNodeCommandsAdapter adapter(view); adapter.addNode(newLayer, parent, currentActiveLayer); } KisImportCatcher::KisImportCatcher(const QUrl &url, KisViewManager *view, const QString &layerType) : m_d(new Private) { m_d->doc = KisPart::instance()->createDocument(); - m_d->view = view; m_d->url = url; m_d->layerType = layerType; connect(m_d->doc, SIGNAL(sigLoadingFinished()), this, SLOT(slotLoadingFinished())); - bool result = m_d->doc->openUrl(url); + bool result = m_d->doc->openUrl(url, KisDocument::DontAddToRecent); if (!result) { deleteMyself(); } } void KisImportCatcher::slotLoadingFinished() { KisImageWSP importedImage = m_d->doc->image(); importedImage->waitForDone(); if (importedImage && importedImage->projection()->exactBounds().isValid()) { if (m_d->layerType != "KisPaintLayer") { m_d->view->nodeManager()->createNode(m_d->layerType, false, importedImage->projection()); } else { KisPaintDeviceSP dev = importedImage->projection(); adaptClipToImageColorSpace(dev, m_d->view->image()); m_d->importAsPaintLayer(dev); } } deleteMyself(); } void KisImportCatcher::deleteMyself() { m_d->doc->deleteLater(); deleteLater(); } KisImportCatcher::~KisImportCatcher() { delete m_d; } void KisImportCatcher::adaptClipToImageColorSpace(KisPaintDeviceSP dev, KisImageSP image) { KisConfig cfg; qDebug() << "dev" << dev->colorSpace() << "image" << image->colorSpace() << "cfg" << cfg.convertToImageColorspaceOnImport(); if (cfg.convertToImageColorspaceOnImport() && *dev->colorSpace() != *image->colorSpace()) { /// XXX: do we need intent here? KUndo2Command* cmd = dev->convertTo(image->colorSpace()); delete cmd; } } diff --git a/libs/ui/kis_png_converter.cpp b/libs/ui/kis_png_converter.cpp index c39ddefc94..ea76c3aa7b 100644 --- a/libs/ui/kis_png_converter.cpp +++ b/libs/ui/kis_png_converter.cpp @@ -1,1293 +1,1295 @@ /* * Copyright (c) 2005-2007 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_png_converter.h" // A big thank to Glenn Randers-Pehrson for his wonderful // documentation of libpng available at // http://www.libpng.org/pub/png/libpng-1.2.5-manual.html #ifndef PNG_MAX_UINT // Removed in libpng 1.4 #define PNG_MAX_UINT PNG_UINT_31_MAX #endif #include // WORDS_BIGENDIAN #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dialogs/kis_dlg_png_import.h" #include "kis_clipboard.h" namespace { int getColorTypeforColorSpace(const KoColorSpace * cs , bool alpha) { QString id = cs->id(); if (id == "GRAYA" || id == "GRAYAU16" || id == "GRAYA16") { return alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY; } if (id == "RGBA" || id == "RGBA16") { return alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; } return -1; } bool colorSpaceIdSupported(const QString &id) { return id == "RGBA" || id == "RGBA16" || id == "GRAYA" || id == "GRAYAU16" || id == "GRAYA16"; } QPair getColorSpaceForColorType(int color_type, int color_nb_bits) { QPair r; if (color_type == PNG_COLOR_TYPE_PALETTE) { r.first = RGBAColorModelID.id(); r.second = Integer8BitsColorDepthID.id(); } else { if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { r.first = GrayAColorModelID.id(); } else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_RGB) { r.first = RGBAColorModelID.id(); } if (color_nb_bits == 16) { r.second = Integer16BitsColorDepthID.id(); } else if (color_nb_bits <= 8) { r.second = Integer8BitsColorDepthID.id(); } } return r; } void fillText(png_text* p_text, const char* key, QString& text) { p_text->compression = PNG_TEXT_COMPRESSION_zTXt; p_text->key = const_cast(key); char* textc = new char[text.length()+1]; strcpy(textc, text.toLatin1()); p_text->text = textc; p_text->text_length = text.length() + 1; } long formatStringList(char *string, const size_t length, const char *format, va_list operands) { int n = vsnprintf(string, length, format, operands); if (n < 0) string[length-1] = '\0'; return((long) n); } long formatString(char *string, const size_t length, const char *format, ...) { long n; va_list operands; va_start(operands, format); n = (long) formatStringList(string, length, format, operands); va_end(operands); return(n); } void writeRawProfile(png_struct *ping, png_info *ping_info, QString profile_type, QByteArray profile_data) { png_textp text; png_uint_32 allocated_length, description_length; const uchar hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; dbgFile << "Writing Raw profile: type=" << profile_type << ", length=" << profile_data.length() << endl; text = (png_textp) png_malloc(ping, (png_uint_32) sizeof(png_text)); description_length = profile_type.length(); allocated_length = (png_uint_32)(profile_data.length() * 2 + (profile_data.length() >> 5) + 20 + description_length); text[0].text = (png_charp) png_malloc(ping, allocated_length); QString key = QLatin1Literal("Raw profile type ") + profile_type.toLatin1(); QByteArray keyData = key.toLatin1(); text[0].key = keyData.data(); uchar* sp = (uchar*)profile_data.data(); png_charp dp = text[0].text; *dp++ = '\n'; memcpy(dp, profile_type.toLatin1().constData(), profile_type.length()); dp += description_length; *dp++ = '\n'; formatString(dp, allocated_length - strlen(text[0].text), "%8lu ", profile_data.length()); dp += 8; for (long i = 0; i < (long) profile_data.length(); i++) { if (i % 36 == 0) *dp++ = '\n'; *(dp++) = (char) hex[((*sp >> 4) & 0x0f)]; *(dp++) = (char) hex[((*sp++) & 0x0f)]; } *dp++ = '\n'; *dp = '\0'; text[0].text_length = (png_size_t)(dp - text[0].text); text[0].compression = -1; if (text[0].text_length <= allocated_length) png_set_text(ping, ping_info, text, 1); png_free(ping, text[0].text); png_free(ping, text); } QByteArray png_read_raw_profile(png_textp text) { QByteArray profile; static const unsigned char unhex[103] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15 }; png_charp sp = text[0].text + 1; /* look for newline */ while (*sp != '\n') sp++; /* look for length */ while (*sp == '\0' || *sp == ' ' || *sp == '\n') sp++; png_uint_32 length = (png_uint_32) atol(sp); while (*sp != ' ' && *sp != '\n') sp++; if (length == 0) { return profile; } profile.resize(length); /* copy profile, skipping white space and column 1 "=" signs */ unsigned char *dp = (unsigned char*)profile.data(); png_uint_32 nibbles = length * 2; for (png_uint_32 i = 0; i < nibbles; i++) { while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f') { if (*sp == '\0') { return QByteArray(); } sp++; } if (i % 2 == 0) *dp = (unsigned char)(16 * unhex[(int) *sp++]); else (*dp++) += unhex[(int) *sp++]; } return profile; } void decode_meta_data(png_textp text, KisMetaData::Store* store, QString type, int headerSize) { dbgFile << "Decoding " << type << " " << text[0].key; KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value(type); Q_ASSERT(exifIO); QByteArray rawProfile = png_read_raw_profile(text); if (headerSize > 0) { rawProfile.remove(0, headerSize); } if (rawProfile.size() > 0) { QBuffer buffer; buffer.setData(rawProfile); exifIO->loadFrom(store, &buffer); } else { dbgFile << "Decoding failed"; } } } KisPNGConverter::KisPNGConverter(KisDocument *doc, bool batchMode) { // Q_ASSERT(doc); // Q_ASSERT(adapter); m_doc = doc; m_stop = false; m_max_row = 0; m_image = 0; m_batchMode = batchMode; } KisPNGConverter::~KisPNGConverter() { } class KisPNGReadStream { public: KisPNGReadStream(quint8* buf, quint32 depth) : m_posinc(8), m_depth(depth), m_buf(buf) { } int nextValue() { if (m_posinc == 0) { m_posinc = 8; m_buf++; } m_posinc -= m_depth; return (((*m_buf) >> (m_posinc)) & ((1 << m_depth) - 1)); } private: quint32 m_posinc, m_depth; quint8* m_buf; }; class KisPNGWriteStream { public: KisPNGWriteStream(quint8* buf, quint32 depth) : m_posinc(8), m_depth(depth), m_buf(buf) { *m_buf = 0; } void setNextValue(int v) { if (m_posinc == 0) { m_posinc = 8; m_buf++; *m_buf = 0; } m_posinc -= m_depth; *m_buf = (v << m_posinc) | *m_buf; } private: quint32 m_posinc, m_depth; quint8* m_buf; }; class KisPNGReaderAbstract { public: KisPNGReaderAbstract(png_structp _png_ptr, int _width, int _height) : png_ptr(_png_ptr), width(_width), height(_height) {} virtual ~KisPNGReaderAbstract() {} virtual png_bytep readLine() = 0; protected: png_structp png_ptr; int width, height; }; class KisPNGReaderLineByLine : public KisPNGReaderAbstract { public: KisPNGReaderLineByLine(png_structp _png_ptr, png_infop info_ptr, int _width, int _height) : KisPNGReaderAbstract(_png_ptr, _width, _height) { png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr); row_pointer = new png_byte[rowbytes]; } ~KisPNGReaderLineByLine() override { delete[] row_pointer; } png_bytep readLine() override { png_read_row(png_ptr, row_pointer, 0); return row_pointer; } private: png_bytep row_pointer; }; class KisPNGReaderFullImage : public KisPNGReaderAbstract { public: KisPNGReaderFullImage(png_structp _png_ptr, png_infop info_ptr, int _width, int _height) : KisPNGReaderAbstract(_png_ptr, _width, _height), y(0) { row_pointers = new png_bytep[height]; png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr); for (int i = 0; i < height; i++) { row_pointers[i] = new png_byte[rowbytes]; } png_read_image(png_ptr, row_pointers); } ~KisPNGReaderFullImage() override { for (int i = 0; i < height; i++) { delete[] row_pointers[i]; } delete[] row_pointers; } png_bytep readLine() override { return row_pointers[y++]; } private: png_bytepp row_pointers; int y; }; static void _read_fn(png_structp png_ptr, png_bytep data, png_size_t length) { QIODevice *in = (QIODevice *)png_get_io_ptr(png_ptr); while (length) { int nr = in->read((char*)data, length); if (nr <= 0) { png_error(png_ptr, "Read Error"); return; } length -= nr; } } static void _write_fn(png_structp png_ptr, png_bytep data, png_size_t length) { QIODevice* out = (QIODevice*)png_get_io_ptr(png_ptr); uint nr = out->write((char*)data, length); if (nr != length) { png_error(png_ptr, "Write Error"); return; } } static void _flush_fn(png_structp png_ptr) { Q_UNUSED(png_ptr); } KisImageBuilder_Result KisPNGConverter::buildImage(QIODevice* iod) { dbgFile << "Start decoding PNG File"; png_byte signature[8]; iod->peek((char*)signature, 8); #if PNG_LIBPNG_VER < 10400 if (!png_check_sig(signature, 8)) { #else if (png_sig_cmp(signature, 0, 8) != 0) { #endif iod->close(); return (KisImageBuilder_RESULT_BAD_FETCH); } // Initialize the internal structures png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (!png_ptr) { iod->close(); } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0); iod->close(); return (KisImageBuilder_RESULT_FAILURE); } png_infop end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)0); iod->close(); return (KisImageBuilder_RESULT_FAILURE); } // Catch errors if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); iod->close(); return (KisImageBuilder_RESULT_FAILURE); } // Initialize the special png_set_read_fn(png_ptr, iod, _read_fn); #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED) png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON); #endif // read all PNG info up to image data png_read_info(png_ptr, info_ptr); if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY && png_get_bit_depth(png_ptr, info_ptr) < 8) { png_set_expand(png_ptr); } if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE && png_get_bit_depth(png_ptr, info_ptr) < 8) { png_set_packing(png_ptr); } if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_PALETTE && (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) { png_set_expand(png_ptr); } png_read_update_info(png_ptr, info_ptr); // Read information about the png png_uint_32 width, height; int color_nb_bits, color_type, interlace_type; png_get_IHDR(png_ptr, info_ptr, &width, &height, &color_nb_bits, &color_type, &interlace_type, 0, 0); dbgFile << "width = " << width << " height = " << height << " color_nb_bits = " << color_nb_bits << " color_type = " << color_type << " interlace_type = " << interlace_type << endl; // swap byteorder on little endian machines. #ifndef WORDS_BIGENDIAN if (color_nb_bits > 8) png_set_swap(png_ptr); #endif // Determine the colorspace QPair csName = getColorSpaceForColorType(color_type, color_nb_bits); if (csName.first.isEmpty()) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); iod->close(); return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } bool hasalpha = (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY_ALPHA); // Read image profile png_charp profile_name; #if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5 png_bytep profile_data; #else png_charp profile_data; #endif int compression_type; png_uint_32 proflen; // Get the various optional chunks // https://www.w3.org/TR/PNG/#11cHRM #if defined(PNG_cHRM_SUPPORTED) double whitePointX, whitePointY; double redX, redY; double greenX, greenY; double blueX, blueY; png_get_cHRM(png_ptr,info_ptr, &whitePointX, &whitePointY, &redX, &redY, &greenX, &greenY, &blueX, &blueY); qDebug() << "cHRM:" << whitePointX << whitePointY << redX << redY << greenX << greenY << blueX << blueY; #endif // https://www.w3.org/TR/PNG/#11gAMA #if defined(PNG_GAMMA_SUPPORTED) double gamma; png_get_gAMA(png_ptr, info_ptr, &gamma); qDebug() << "gAMA" << gamma; #endif // https://www.w3.org/TR/PNG/#11sRGB #if defined(PNG_sRGB_SUPPORTED) int sRGBIntent; png_get_sRGB(png_ptr, info_ptr, &sRGBIntent); qDebug() << "sRGB" << sRGBIntent; #endif bool fromBlender = false; png_text* text_ptr; int num_comments; png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments); for (int i = 0; i < num_comments; i++) { QString key = QString(text_ptr[i].key).toLower(); if (key == "file") { QString relatedFile = text_ptr[i].text; if (relatedFile.contains(".blend", Qt::CaseInsensitive)){ fromBlender=true; } } } const KoColorProfile* profile = 0; if (png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen)) { QByteArray profile_rawdata; // XXX: Hardcoded for icc type -- is that correct for us? profile_rawdata.resize(proflen); memcpy(profile_rawdata.data(), profile_data, proflen); profile = KoColorSpaceRegistry::instance()->createColorProfile(csName.first, csName.second, profile_rawdata); Q_CHECK_PTR(profile); if (profile) { // dbgFile << "profile name: " << profile->productName() << " profile description: " << profile->productDescription() << " information sur le produit: " << profile->productInfo(); if (!profile->isSuitableForOutput()) { dbgFile << "the profile is not suitable for output and therefore cannot be used in krita, we need to convert the image to a standard profile"; // TODO: in ko2 popup a selection menu to inform the user } } } else { dbgFile << "no embedded profile, will use the default profile"; if (color_nb_bits == 16 && !fromBlender && !qAppName().toLower().contains("test") && !m_batchMode) { KisConfig cfg; quint32 behaviour = cfg.pasteBehaviour(); if (behaviour == PASTE_ASK) { KisDlgPngImport dlg(m_path, csName.first, csName.second); QApplication::restoreOverrideCursor(); dlg.exec(); if (!dlg.profile().isEmpty()) { profile = KoColorSpaceRegistry::instance()->profileByName(dlg.profile()); } QApplication::setOverrideCursor(Qt::WaitCursor); } } dbgFile << "no embedded profile, will use the default profile"; } const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(csName.first, csName.second); // Check that the profile is used by the color space if (profile && !KoColorSpaceRegistry::instance()->profileIsCompatible(profile, colorSpaceId)) { warnFile << "The profile " << profile->name() << " is not compatible with the color space model " << csName.first << " " << csName.second; profile = 0; } // Retrieve a pointer to the colorspace const KoColorSpace* cs; if (profile && profile->isSuitableForOutput()) { dbgFile << "image has embedded profile: " << profile->name() << "\n"; cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile); } else { if (csName.first == RGBAColorModelID.id()) { cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, "sRGB-elle-V2-srgbtrc.icc"); } else { cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, 0); } } if (cs == 0) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } //TODO: two fixes : one tell the user about the problem and ask for a solution, and two once the kocolorspace include KoColorTransformation, use that instead of hacking a lcms transformation // Create the cmsTransform if needed KoColorTransformation* transform = 0; if (profile && !profile->isSuitableForOutput()) { transform = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile)->createColorConverter(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } // Creating the KisImageSP if (m_image == 0) { m_image = new KisImage(m_doc->createUndoStore(), width, height, cs, "built image"); Q_CHECK_PTR(m_image); } // Read resolution int unit_type; png_uint_32 x_resolution, y_resolution; png_get_pHYs(png_ptr, info_ptr, &x_resolution, &y_resolution, &unit_type); if (x_resolution > 0 && y_resolution > 0 && unit_type == PNG_RESOLUTION_METER) { m_image->setResolution((double) POINT_TO_CM(x_resolution) / 100.0, (double) POINT_TO_CM(y_resolution) / 100.0); // It is the "invert" macro because we convert from pointer-per-inchs to points } double coeff = quint8_MAX / (double)(pow((double)2, color_nb_bits) - 1); KisPaintLayerSP layer = new KisPaintLayer(m_image.data(), m_image -> nextLayerName(), UCHAR_MAX); // Read comments/texts... png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments); if (m_doc) { KoDocumentInfo * info = m_doc->documentInfo(); dbgFile << "There are " << num_comments << " comments in the text"; for (int i = 0; i < num_comments; i++) { QString key = QString(text_ptr[i].key).toLower(); dbgFile << "key is |" << text_ptr[i].key << "| containing " << text_ptr[i].text << " " << (key == "Raw profile type exif "); if (key == "title") { info->setAboutInfo("title", text_ptr[i].text); } else if (key == "description") { info->setAboutInfo("comment", text_ptr[i].text); } else if (key == "author") { info->setAuthorInfo("creator", text_ptr[i].text); } else if (key.contains("Raw profile type exif")) { decode_meta_data(text_ptr + i, layer->metaData(), "exif", 6); } else if (key.contains("Raw profile type iptc")) { decode_meta_data(text_ptr + i, layer->metaData(), "iptc", 14); } else if (key.contains("Raw profile type xmp")) { decode_meta_data(text_ptr + i, layer->metaData(), "xmp", 0); } else if (key == "version") { m_image->addAnnotation(new KisAnnotation("kpp_version", "version", QByteArray(text_ptr[i].text))); } else if (key == "preset") { m_image->addAnnotation(new KisAnnotation("kpp_preset", "preset", QByteArray(text_ptr[i].text))); } } } // Read image data KisPNGReaderAbstract* reader = 0; try { if (interlace_type == PNG_INTERLACE_ADAM7) { reader = new KisPNGReaderFullImage(png_ptr, info_ptr, width, height); } else { reader = new KisPNGReaderLineByLine(png_ptr, info_ptr, width, height); } } catch (std::bad_alloc& e) { // new png_byte[] may raise such an exception if the image // is invalid / to large. dbgFile << "bad alloc: " << e.what(); // Free only the already allocated png_byte instances. png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return (KisImageBuilder_RESULT_FAILURE); } // Read the palette if the file is indexed png_colorp palette ; int num_palette; if (color_type == PNG_COLOR_TYPE_PALETTE) { png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); } // Read the transparency palette quint8 palette_alpha[256]; memset(palette_alpha, 255, 256); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { if (color_type == PNG_COLOR_TYPE_PALETTE) { png_bytep alpha_ptr; int num_alpha; png_get_tRNS(png_ptr, info_ptr, &alpha_ptr, &num_alpha, 0); for (int i = 0; i < num_alpha; ++i) { palette_alpha[i] = alpha_ptr[i]; } } } for (png_uint_32 y = 0; y < height; y++) { KisHLineIteratorSP it = layer -> paintDevice() -> createHLineIteratorNG(0, y, width); png_bytep row_pointer = reader->readLine(); switch (color_type) { case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: if (color_nb_bits == 16) { quint16 *src = reinterpret_cast(row_pointer); do { quint16 *d = reinterpret_cast(it->rawData()); d[0] = *(src++); if (transform) transform->transform(reinterpret_cast(d), reinterpret_cast(d), 1); if (hasalpha) { d[1] = *(src++); } else { d[1] = quint16_MAX; } } while (it->nextPixel()); } else { KisPNGReadStream stream(row_pointer, color_nb_bits); do { quint8 *d = it->rawData(); d[0] = (quint8)(stream.nextValue() * coeff); if (transform) transform->transform(d, d, 1); if (hasalpha) { d[1] = (quint8)(stream.nextValue() * coeff); } else { d[1] = UCHAR_MAX; } } while (it->nextPixel()); } // FIXME:should be able to read 1 and 4 bits depth and scale them to 8 bits" break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: if (color_nb_bits == 16) { quint16 *src = reinterpret_cast(row_pointer); do { quint16 *d = reinterpret_cast(it->rawData()); d[2] = *(src++); d[1] = *(src++); d[0] = *(src++); if (transform) transform->transform(reinterpret_cast(d), reinterpret_cast(d), 1); if (hasalpha) d[3] = *(src++); else d[3] = quint16_MAX; } while (it->nextPixel()); } else { KisPNGReadStream stream(row_pointer, color_nb_bits); do { quint8 *d = it->rawData(); d[2] = (quint8)(stream.nextValue() * coeff); d[1] = (quint8)(stream.nextValue() * coeff); d[0] = (quint8)(stream.nextValue() * coeff); if (transform) transform->transform(d, d, 1); if (hasalpha) d[3] = (quint8)(stream.nextValue() * coeff); else d[3] = UCHAR_MAX; } while (it->nextPixel()); } break; case PNG_COLOR_TYPE_PALETTE: { KisPNGReadStream stream(row_pointer, color_nb_bits); do { quint8 *d = it->rawData(); quint8 index = stream.nextValue(); quint8 alpha = palette_alpha[ index ]; if (alpha == 0) { memset(d, 0, 4); } else { png_color c = palette[ index ]; d[2] = c.red; d[1] = c.green; d[0] = c.blue; d[3] = alpha; } } while (it->nextPixel()); } break; default: return KisImageBuilder_RESULT_UNSUPPORTED; } } m_image->addNode(layer.data(), m_image->rootLayer().data()); png_read_end(png_ptr, end_info); iod->close(); // Freeing memory png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); delete reader; return KisImageBuilder_RESULT_OK; } KisImageBuilder_Result KisPNGConverter::buildImage(const QString &filename) { m_path = filename; QFile fp(filename); if (fp.exists()) { if (!fp.open(QIODevice::ReadOnly)) { dbgFile << "Failed to open PNG File"; return (KisImageBuilder_RESULT_FAILURE); } return buildImage(&fp); } return (KisImageBuilder_RESULT_NOT_EXIST); } KisImageSP KisPNGConverter::image() { return m_image; } bool KisPNGConverter::saveDeviceToStore(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP dev, KoStore *store, KisMetaData::Store* metaData) { if (store->open(filename)) { KoStoreDevice io(store); if (!io.open(QIODevice::WriteOnly)) { dbgFile << "Could not open for writing:" << filename; return false; } KisPNGConverter pngconv(0); vKisAnnotationSP_it annotIt = 0; KisMetaData::Store* metaDataStore = 0; if (metaData) { metaDataStore = new KisMetaData::Store(*metaData); } KisPNGOptions options; options.compression = 0; options.interlace = false; options.tryToSaveAsIndexed = false; options.alpha = true; options.saveSRGBProfile = false; if (dev->colorSpace()->id() != "RGBA") { dev = new KisPaintDevice(*dev.data()); KUndo2Command *cmd = dev->convertTo(KoColorSpaceRegistry::instance()->rgb8()); delete cmd; } bool success = pngconv.buildFile(&io, imageRect, xRes, yRes, dev, annotIt, annotIt, options, metaDataStore); if (success != KisImageBuilder_RESULT_OK) { dbgFile << "Saving PNG failed:" << filename; delete metaDataStore; return false; } delete metaDataStore; io.close(); if (!store->close()) { return false; } } else { dbgFile << "Opening of data file failed :" << filename; return false; } return true; } KisImageBuilder_Result KisPNGConverter::buildFile(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData) { dbgFile << "Start writing PNG File " << filename; // Open a QIODevice for writing QFile fp (filename); if (!fp.open(QIODevice::WriteOnly)) { dbgFile << "Failed to open PNG File for writing"; return (KisImageBuilder_RESULT_FAILURE); } KisImageBuilder_Result result = buildFile(&fp, imageRect, xRes, yRes, device, annotationsStart, annotationsEnd, options, metaData); return result; } KisImageBuilder_Result KisPNGConverter::buildFile(QIODevice* iodevice, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData) { if (!device) return KisImageBuilder_RESULT_INVALID_ARG; if (!options.alpha) { KisPaintDeviceSP tmp = new KisPaintDevice(device->colorSpace()); KoColor c(options.transparencyFillColor, device->colorSpace()); tmp->fill(imageRect, c); KisPainter gc(tmp); gc.bitBlt(imageRect.topLeft(), device, imageRect); gc.end(); device = tmp; } if (device->colorSpace()->colorDepthId() == Float16BitsColorDepthID || device->colorSpace()->colorDepthId() == Float32BitsColorDepthID || device->colorSpace()->colorDepthId() == Float64BitsColorDepthID) { const KoColorSpace *dstcs = KoColorSpaceRegistry::instance()->colorSpace(device->colorSpace()->colorModelId().id(), Integer16BitsColorDepthID.id(), device->colorSpace()->profile()); KisPaintDeviceSP tmp = new KisPaintDevice(dstcs); KisPainter gc(tmp); gc.bitBlt(imageRect.topLeft(), device, imageRect); gc.end(); device = tmp; } if (options.forceSRGB) { const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), device->colorSpace()->colorDepthId().id(), "sRGB built-in - (lcms internal)"); device = new KisPaintDevice(*device); KUndo2Command *cmd = device->convertTo(cs); delete cmd; } // Initialize structures png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (!png_ptr) { return (KisImageBuilder_RESULT_FAILURE); } #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED) png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON); #endif #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED png_set_check_for_invalid_index(png_ptr, 0); #endif png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)0); return (KisImageBuilder_RESULT_FAILURE); } // If an error occurs during writing, libpng will jump here if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); return (KisImageBuilder_RESULT_FAILURE); } // Initialize the writing // png_init_io(png_ptr, fp); // Setup the progress function // XXX: Implement progress updating -- png_set_write_status_fn(png_ptr, progress);" // setProgressTotalSteps(100/*height*/); /* set the zlib compression level */ png_set_compression_level(png_ptr, options.compression); png_set_write_fn(png_ptr, (void*)iodevice, _write_fn, _flush_fn); /* set other zlib parameters */ png_set_compression_mem_level(png_ptr, 8); png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); png_set_compression_window_bits(png_ptr, 15); png_set_compression_method(png_ptr, 8); png_set_compression_buffer_size(png_ptr, 8192); int color_nb_bits = 8 * device->pixelSize() / device->channelCount(); int color_type = getColorTypeforColorSpace(device->colorSpace(), options.alpha); Q_ASSERT(color_type > -1); // Try to compute a table of color if the colorspace is RGB8f png_colorp palette = 0; int num_palette = 0; if (!options.alpha && options.tryToSaveAsIndexed && KoID(device->colorSpace()->id()) == KoID("RGBA")) { // png doesn't handle indexed images and alpha, and only have indexed for RGB8 palette = new png_color[255]; KisSequentialIterator it(device, imageRect); bool toomuchcolor = false; while (it.nextPixel()) { const quint8* c = it.oldRawData(); bool findit = false; for (int i = 0; i < num_palette; i++) { if (palette[i].red == c[2] && palette[i].green == c[1] && palette[i].blue == c[0]) { findit = true; break; } } if (!findit) { if (num_palette == 255) { toomuchcolor = true; break; } palette[num_palette].red = c[2]; palette[num_palette].green = c[1]; palette[num_palette].blue = c[0]; num_palette++; } } if (!toomuchcolor) { dbgFile << "Found a palette of " << num_palette << " colors"; color_type = PNG_COLOR_TYPE_PALETTE; if (num_palette <= 2) { color_nb_bits = 1; } else if (num_palette <= 4) { color_nb_bits = 2; } else if (num_palette <= 16) { color_nb_bits = 4; } else { color_nb_bits = 8; } } else { delete [] palette; } } int interlacetype = options.interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; png_set_IHDR(png_ptr, info_ptr, imageRect.width(), imageRect.height(), color_nb_bits, color_type, interlacetype, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // set sRGB only if the profile is sRGB -- http://www.w3.org/TR/PNG/#11sRGB says sRGB and iCCP should not both be present bool sRGB = device->colorSpace()->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive); /* * This automatically writes the correct gamma and chroma chunks along with the sRGB chunk, but firefox's * color management is bugged, so once you give it any incentive to start color managing an sRGB image it * will turn, for example, a nice desaturated rusty red into bright poppy red. So this is disabled for now. */ /*if (!options.saveSRGBProfile && sRGB) { png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL); }*/ // set the palette if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_PLTE(png_ptr, info_ptr, palette, num_palette); } // Save annotation vKisAnnotationSP_it it = annotationsStart; while (it != annotationsEnd) { if (!(*it) || (*it)->type().isEmpty()) { dbgFile << "Warning: empty annotation"; it++; continue; } dbgFile << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size(); if ((*it) -> type().startsWith(QString("krita_attribute:"))) { // // Attribute // XXX: it should be possible to save krita_attributes in the \"CHUNKs\"" dbgFile << "cannot save this annotation : " << (*it) -> type(); } else if ((*it)->type() == "kpp_version" || (*it)->type() == "kpp_preset" ) { dbgFile << "Saving preset information " << (*it)->description(); png_textp text = (png_textp) png_malloc(png_ptr, (png_uint_32) sizeof(png_text)); QByteArray keyData = (*it)->description().toLatin1(); text[0].key = keyData.data(); text[0].text = (char*)(*it)->annotation().data(); text[0].text_length = (*it)->annotation().size(); text[0].compression = -1; png_set_text(png_ptr, info_ptr, text, 1); png_free(png_ptr, text); } it++; } // Save the color profile const KoColorProfile* colorProfile = device->colorSpace()->profile(); QByteArray colorProfileData = colorProfile->rawData(); if (!sRGB || options.saveSRGBProfile) { #if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5 png_set_iCCP(png_ptr, info_ptr, (char*)"icc", PNG_COMPRESSION_TYPE_BASE, (const png_bytep)colorProfileData.constData(), colorProfileData . size()); #else png_set_iCCP(png_ptr, info_ptr, (char*)"icc", PNG_COMPRESSION_TYPE_BASE, (char*)colorProfileData.constData(), colorProfileData . size()); #endif } // save comments from the document information // warning: according to the official png spec, the keys need to be capitalised! if (m_doc) { png_text texts[4]; int nbtexts = 0; KoDocumentInfo * info = m_doc->documentInfo(); QString title = info->aboutInfo("title"); if (!title.isEmpty() && options.storeMetaData) { fillText(texts + nbtexts, "Title", title); nbtexts++; } QString abstract = info->aboutInfo("subject"); if (abstract.isEmpty()) { abstract = info->aboutInfo("abstract"); } if (!abstract.isEmpty() && options.storeMetaData) { QString keywords = info->aboutInfo("keywords"); if (!keywords.isEmpty()) { abstract = abstract + "keywords: " + keywords; } fillText(texts + nbtexts, "Description", abstract); nbtexts++; } QString license = info->aboutInfo("license"); if (!license.isEmpty() && options.storeMetaData) { fillText(texts + nbtexts, "Copyright", license); nbtexts++; } QString author = info->authorInfo("creator"); if (!author.isEmpty() && options.storeAuthor) { - QString contact = info->authorContactInfo().at(0); - if (!contact.isEmpty()) { - author = author+"("+contact+")"; + if (!info->authorContactInfo().isEmpty()) { + QString contact = info->authorContactInfo().at(0); + if (!contact.isEmpty()) { + author = author+"("+contact+")"; + } } fillText(texts + nbtexts, "Author", author); nbtexts++; } png_set_text(png_ptr, info_ptr, texts, nbtexts); } // Save metadata following imagemagick way // Save exif if (metaData && !metaData->empty()) { if (options.exif) { dbgFile << "Trying to save exif information"; KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value("exif"); Q_ASSERT(exifIO); QBuffer buffer; exifIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader); writeRawProfile(png_ptr, info_ptr, "exif", buffer.data()); } // Save IPTC if (options.iptc) { dbgFile << "Trying to save exif information"; KisMetaData::IOBackend* iptcIO = KisMetaData::IOBackendRegistry::instance()->value("iptc"); Q_ASSERT(iptcIO); QBuffer buffer; iptcIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader); dbgFile << "IPTC information size is" << buffer.data().size(); writeRawProfile(png_ptr, info_ptr, "iptc", buffer.data()); } // Save XMP if (options.xmp) { dbgFile << "Trying to save XMP information"; KisMetaData::IOBackend* xmpIO = KisMetaData::IOBackendRegistry::instance()->value("xmp"); Q_ASSERT(xmpIO); QBuffer buffer; xmpIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::NoHeader); dbgFile << "XMP information size is" << buffer.data().size(); writeRawProfile(png_ptr, info_ptr, "xmp", buffer.data()); } } #if 0 // Unimplemented? // Save resolution int unit_type; png_uint_32 x_resolution, y_resolution; #endif png_set_pHYs(png_ptr, info_ptr, CM_TO_POINT(xRes) * 100.0, CM_TO_POINT(yRes) * 100.0, PNG_RESOLUTION_METER); // It is the "invert" macro because we convert from pointer-per-inchs to points // Save the information to the file png_write_info(png_ptr, info_ptr); png_write_flush(png_ptr); // swap byteorder on little endian machines. #ifndef WORDS_BIGENDIAN if (color_nb_bits > 8) png_set_swap(png_ptr); #endif // Write the PNG // png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, 0); // Fill the data structure png_byte** row_pointers = new png_byte*[imageRect.height()]; int row = 0; for (int y = imageRect.y(); y < imageRect.y() + imageRect.height(); y++, row++) { KisHLineConstIteratorSP it = device->createHLineConstIteratorNG(imageRect.x(), y, imageRect.width()); row_pointers[row] = new png_byte[imageRect.width() * device->pixelSize()]; switch (color_type) { case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: if (color_nb_bits == 16) { quint16 *dst = reinterpret_cast(row_pointers[row]); do { const quint16 *d = reinterpret_cast(it->oldRawData()); *(dst++) = d[0]; if (options.alpha) *(dst++) = d[1]; } while (it->nextPixel()); } else { quint8 *dst = row_pointers[row]; do { const quint8 *d = it->oldRawData(); *(dst++) = d[0]; if (options.alpha) *(dst++) = d[1]; } while (it->nextPixel()); } break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: if (color_nb_bits == 16) { quint16 *dst = reinterpret_cast(row_pointers[row]); do { const quint16 *d = reinterpret_cast(it->oldRawData()); *(dst++) = d[2]; *(dst++) = d[1]; *(dst++) = d[0]; if (options.alpha) *(dst++) = d[3]; } while (it->nextPixel()); } else { quint8 *dst = row_pointers[row]; do { const quint8 *d = it->oldRawData(); *(dst++) = d[2]; *(dst++) = d[1]; *(dst++) = d[0]; if (options.alpha) *(dst++) = d[3]; } while (it->nextPixel()); } break; case PNG_COLOR_TYPE_PALETTE: { quint8 *dst = row_pointers[row]; KisPNGWriteStream writestream(dst, color_nb_bits); do { const quint8 *d = it->oldRawData(); int i; for (i = 0; i < num_palette; i++) { if (palette[i].red == d[2] && palette[i].green == d[1] && palette[i].blue == d[0]) { break; } } writestream.setNextValue(i); } while (it->nextPixel()); } break; default: delete[] row_pointers; return KisImageBuilder_RESULT_UNSUPPORTED; } } png_write_image(png_ptr, row_pointers); // Writing is over png_write_end(png_ptr, info_ptr); // Free memory png_destroy_write_struct(&png_ptr, &info_ptr); for (int y = 0; y < imageRect.height(); y++) { delete[] row_pointers[y]; } delete[] row_pointers; if (color_type == PNG_COLOR_TYPE_PALETTE) { delete [] palette; } return KisImageBuilder_RESULT_OK; } void KisPNGConverter::cancel() { m_stop = true; } void KisPNGConverter::progress(png_structp png_ptr, png_uint_32 row_number, int pass) { if (png_ptr == 0 || row_number > PNG_MAX_UINT || pass > 7) return; // setProgress(row_number); } bool KisPNGConverter::isColorSpaceSupported(const KoColorSpace *cs) { return colorSpaceIdSupported(cs->id()); } diff --git a/libs/ui/tests/CMakeLists.txt b/libs/ui/tests/CMakeLists.txt index e1a7567439..a9c60cc191 100644 --- a/libs/ui/tests/CMakeLists.txt +++ b/libs/ui/tests/CMakeLists.txt @@ -1,181 +1,182 @@ set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) #add_subdirectory(scratchpad) include_directories(${CMAKE_SOURCE_DIR}/libs/image/metadata ${CMAKE_SOURCE_DIR}/sdk/tests ) include(ECMAddTests) macro_add_unittest_definitions() ecm_add_tests( kis_image_view_converter_test.cpp kis_shape_selection_test.cpp kis_recorded_action_editor_test.cpp kis_doc2_test.cpp kis_coordinates_converter_test.cpp kis_grid_config_test.cpp kis_stabilized_events_sampler_test.cpp kis_derived_resources_test.cpp kis_brush_hud_properties_config_test.cpp kis_shape_commands_test.cpp + kis_shape_layer_test.cpp kis_stop_gradient_editor_test.cpp NAME_PREFIX "krita-ui-" LINK_LIBRARIES kritaui Qt5::Test ) ecm_add_tests( kis_file_layer_test.cpp kis_multinode_property_test.cpp NAME_PREFIX "krita-ui-" LINK_LIBRARIES kritaui kritaimage Qt5::Test ) ecm_add_test( kis_selection_decoration_test.cpp ../../../sdk/tests/stroke_testing_utils.cpp TEST_NAME krita-ui-KisSelectionDecorationTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) ecm_add_test( kis_node_dummies_graph_test.cpp ../../../sdk/tests/testutil.cpp TEST_NAME krita-ui-KisNodeDummiesGraphTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) ecm_add_test( kis_node_shapes_graph_test.cpp ../../../sdk/tests/testutil.cpp TEST_NAME krita-ui-KisNodeShapesGraphTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) ecm_add_test( kis_model_index_converter_test.cpp ../../../sdk/tests/testutil.cpp TEST_NAME krita-ui-KisModelIndexConverterTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) ecm_add_test( kis_categorized_list_model_test.cpp modeltest.cpp TEST_NAME krita-ui-KisCategorizedListModelTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) ecm_add_test( kis_resource_server_provider_test.cpp modeltest.cpp TEST_NAME krita-ui-KisResourceServerProviderTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) ecm_add_test( kis_node_juggler_compressed_test.cpp ../../../sdk/tests/testutil.cpp TEST_NAME krita-image-BaseNodeTest LINK_LIBRARIES kritaimage kritaui Qt5::Test) ecm_add_test( kis_animation_exporter_test.cpp TEST_NAME kritaui-animation_exporter_test LINK_LIBRARIES kritaui kritaimage Qt5::Test) set(kis_node_view_test_SRCS kis_node_view_test.cpp ../../../sdk/tests/testutil.cpp) qt5_add_resources(kis_node_view_test_SRCS ${krita_QRCS}) ecm_add_test(${kis_node_view_test_SRCS} TEST_NAME krita-image-kis_node_view_test LINK_LIBRARIES kritaimage kritaui Qt5::Test) ##### Tests that currently fail and should be fixed ##### include(KritaAddBrokenUnitTest) krita_add_broken_unit_test( kis_node_model_test.cpp modeltest.cpp TEST_NAME krita-ui-kis_node_model_test LINK_LIBRARIES kritaui Qt5::Test) krita_add_broken_unit_test( kis_shape_controller_test.cpp kis_dummies_facade_base_test.cpp TEST_NAME krita-ui-kis_shape_controller_test LINK_LIBRARIES kritaimage kritaui Qt5::Test) krita_add_broken_unit_test( kis_prescaled_projection_test.cpp TEST_NAME krita-ui-kis_prescaled_projection_test LINK_LIBRARIES kritaui Qt5::Test) krita_add_broken_unit_test( kis_exiv2_test.cpp TEST_NAME krita-ui-KisExiv2Test LINK_LIBRARIES kritaimage kritaui Qt5::Test) krita_add_broken_unit_test( kis_clipboard_test.cpp TEST_NAME krita-ui-KisClipboardTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) krita_add_broken_unit_test( freehand_stroke_test.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp TEST_NAME krita-ui-FreehandStrokeTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) krita_add_broken_unit_test( FreehandStrokeBenchmark.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp TEST_NAME krita-ui-FreehandStrokeBenchmark LINK_LIBRARIES kritaui kritaimage Qt5::Test) krita_add_broken_unit_test( fill_processing_visitor_test.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp TEST_NAME krita-ui-FillProcessingVisitorTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) krita_add_broken_unit_test( filter_stroke_test.cpp ../../../sdk/tests/stroke_testing_utils.cpp TEST_NAME krita-ui-FilterStrokeTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) krita_add_broken_unit_test( kis_selection_manager_test.cpp TEST_NAME krita-ui-KisSelectionManagerTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) #set_tests_properties(krita-ui-KisSelectionManagerTest PROPERTIES TIMEOUT 300) krita_add_broken_unit_test( kis_node_manager_test.cpp TEST_NAME krita-ui-KisNodeManagerTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) krita_add_broken_unit_test( kis_dummies_facade_test.cpp kis_dummies_facade_base_test.cpp ../../../sdk/tests/testutil.cpp TEST_NAME krita-ui-KisDummiesFacadeTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) krita_add_broken_unit_test( kis_zoom_and_pan_test.cpp ../../../sdk/tests/testutil.cpp TEST_NAME krita-ui-KisZoomAndPanTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) #set_tests_properties(krita-ui-KisZoomAndPanTest PROPERTIES TIMEOUT 300) krita_add_broken_unit_test( kis_action_manager_test.cpp TEST_NAME krita-ui-KisActionManagerTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) krita_add_broken_unit_test( kis_categories_mapper_test.cpp testing_categories_mapper.cpp TEST_NAME krita-ui-KisCategoriesMapperTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) krita_add_broken_unit_test( kis_asl_layer_style_serializer_test.cpp TEST_NAME krita-ui-KisAslLayerStyleSerializerTest LINK_LIBRARIES kritaui kritaimage Qt5::Test) krita_add_broken_unit_test( kis_animation_importer_test.cpp TEST_NAME kritaui-animation_importer_test LINK_LIBRARIES kritaui kritaimage Qt5::Test) krita_add_broken_unit_test( kis_animation_frame_cache_test.cpp TEST_NAME kritaui-animation_frame_cache_test LINK_LIBRARIES kritaui kritaimage Qt5::Test) krita_add_broken_unit_test( ResourceBundleTest.cpp TEST_NAME krita-resourcemanager-ResourceBundleTest LINK_LIBRARIES kritaui kritalibbrush kritalibpaintop Qt5::Test) # FIXME this test doesn't compile #ecm_add_test( # kis_input_manager_test.cpp ../../../sdk/tests/testutil.cpp # TEST_NAME krita-ui-KisInputManagerTest # LINK_LIBRARIES kritaui kritaimage Qt5::Test) diff --git a/libs/ui/tests/data/shape_layer_test/merge_down_00_initial_layer_update.png b/libs/ui/tests/data/shape_layer_test/merge_down_00_initial_layer_update.png new file mode 100644 index 0000000000..b7a268edc2 Binary files /dev/null and b/libs/ui/tests/data/shape_layer_test/merge_down_00_initial_layer_update.png differ diff --git a/libs/ui/tests/data/shape_layer_test/merge_down_02_after_merge_down.png b/libs/ui/tests/data/shape_layer_test/merge_down_02_after_merge_down.png new file mode 100644 index 0000000000..8f96d17e1f Binary files /dev/null and b/libs/ui/tests/data/shape_layer_test/merge_down_02_after_merge_down.png differ diff --git a/libs/ui/tests/data/shape_layer_test/scale_and_clone_00_initial_layer_update.png b/libs/ui/tests/data/shape_layer_test/scale_and_clone_00_initial_layer_update.png new file mode 100644 index 0000000000..acafafd0b4 Binary files /dev/null and b/libs/ui/tests/data/shape_layer_test/scale_and_clone_00_initial_layer_update.png differ diff --git a/libs/ui/tests/data/shape_layer_test/scale_and_clone_01_after_scale_down.png b/libs/ui/tests/data/shape_layer_test/scale_and_clone_01_after_scale_down.png new file mode 100644 index 0000000000..b2b32cd88d Binary files /dev/null and b/libs/ui/tests/data/shape_layer_test/scale_and_clone_01_after_scale_down.png differ diff --git a/libs/ui/tests/data/shape_layer_test/scale_and_merge_down_00_initial_layer_update.png b/libs/ui/tests/data/shape_layer_test/scale_and_merge_down_00_initial_layer_update.png new file mode 100644 index 0000000000..b7a268edc2 Binary files /dev/null and b/libs/ui/tests/data/shape_layer_test/scale_and_merge_down_00_initial_layer_update.png differ diff --git a/libs/ui/tests/data/shape_layer_test/scale_and_merge_down_01_after_scale_down.png b/libs/ui/tests/data/shape_layer_test/scale_and_merge_down_01_after_scale_down.png new file mode 100644 index 0000000000..2a6eae2d52 Binary files /dev/null and b/libs/ui/tests/data/shape_layer_test/scale_and_merge_down_01_after_scale_down.png differ diff --git a/libs/ui/tests/data/shape_layer_test/scale_and_merge_down_02_after_merge_down.png b/libs/ui/tests/data/shape_layer_test/scale_and_merge_down_02_after_merge_down.png new file mode 100644 index 0000000000..833afaea24 Binary files /dev/null and b/libs/ui/tests/data/shape_layer_test/scale_and_merge_down_02_after_merge_down.png differ diff --git a/libs/ui/tests/kis_file_layer_test.cpp b/libs/ui/tests/kis_file_layer_test.cpp index 038e2657d7..0607008b2e 100644 --- a/libs/ui/tests/kis_file_layer_test.cpp +++ b/libs/ui/tests/kis_file_layer_test.cpp @@ -1,146 +1,146 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_file_layer_test.h" #include #include #include #include #include #include void KisFileLayerTest::testFileLayerPlusTransformMaskOffImage() { - TestUtil::ExternalImageChecker chk("flayer_tmask_offimage", "file_layer"); + TestUtil::ReferenceImageChecker chk("flayer_tmask_offimage", "file_layer"); QRect refRect(0,0,640,441); QRect fillRect(400,400,100,100); TestUtil::MaskParent p(refRect); QString refName(TestUtil::fetchDataFileLazy("hakonepa.png")); KisLayerSP flayer = new KisFileLayer(p.image, "", refName, KisFileLayer::None, "flayer", OPACITY_OPAQUE_U8); p.image->addNode(flayer, p.image->root(), KisNodeSP()); QTest::qWait(2000); p.image->waitForDone(); KisTransformMaskSP mask1 = new KisTransformMask(); p.image->addNode(mask1, flayer); mask1->setName("mask1"); flayer->setDirty(refRect); p.image->waitForDone(); chk.checkImage(p.image, "00_initial_layer_update"); QTest::qWait(4000); p.image->waitForDone(); chk.checkImage(p.image, "00X_initial_layer_update"); flayer->setX(580); flayer->setY(400); flayer->setDirty(refRect); p.image->waitForDone(); chk.checkImage(p.image, "01_file_layer_moved"); QTest::qWait(4000); p.image->waitForDone(); chk.checkImage(p.image, "01X_file_layer_moved"); QTransform transform = QTransform::fromTranslate(-580, -400); mask1->setTransformParams(KisTransformMaskParamsInterfaceSP( new KisDumbTransformMaskParams(transform))); /** * NOTE: here we see our image cropped by 1.5 image size rect! * That is expected and controlled by * KisImageConfig::transformMaskOffBoundsReadArea() * parameter */ mask1->setDirty(refRect); p.image->waitForDone(); chk.checkImage(p.image, "02_mask1_moved_mask_update"); QTest::qWait(4000); p.image->waitForDone(); chk.checkImage(p.image, "02X_mask1_moved_mask_update"); QVERIFY(chk.testPassed()); } void KisFileLayerTest::testFileLayerPlusTransformMaskSmallFileBigOffset() { - TestUtil::ExternalImageChecker chk("flayer_tmask_huge_offset", "file_layer"); + TestUtil::ReferenceImageChecker chk("flayer_tmask_huge_offset", "file_layer"); QRect refRect(0,0,2000,1500); QRect fillRect(400,400,100,100); TestUtil::MaskParent p(refRect); QString refName(TestUtil::fetchDataFileLazy("file_layer_source.png")); KisLayerSP flayer = new KisFileLayer(p.image, "", refName, KisFileLayer::None, "flayer", OPACITY_OPAQUE_U8); p.image->addNode(flayer, p.image->root(), KisNodeSP()); QTest::qWait(2000); p.image->waitForDone(); // check whether the default bounds of the file layer are // initialized properly QCOMPARE(flayer->original()->defaultBounds()->bounds(), p.image->bounds()); KisTransformMaskSP mask1 = new KisTransformMask(); p.image->addNode(mask1, flayer); mask1->setName("mask1"); flayer->setDirty(refRect); p.image->waitForDone(); chk.checkImage(p.image, "00_initial_layer_update"); QTest::qWait(4000); p.image->waitForDone(); chk.checkImage(p.image, "00X_initial_layer_update"); QTransform transform; transform = QTransform::fromTranslate(1200, 300); mask1->setTransformParams(KisTransformMaskParamsInterfaceSP( new KisDumbTransformMaskParams(transform))); mask1->setDirty(refRect); p.image->waitForDone(); chk.checkImage(p.image, "01_mask1_moved_mask_update"); QTest::qWait(4000); p.image->waitForDone(); chk.checkImage(p.image, "01X_mask1_moved_mask_update"); QVERIFY(chk.testPassed()); } QTEST_MAIN(KisFileLayerTest) diff --git a/libs/ui/tests/kis_node_juggler_compressed_test.cpp b/libs/ui/tests/kis_node_juggler_compressed_test.cpp index 1c2690eb65..78c3d12bb6 100644 --- a/libs/ui/tests/kis_node_juggler_compressed_test.cpp +++ b/libs/ui/tests/kis_node_juggler_compressed_test.cpp @@ -1,190 +1,190 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_node_juggler_compressed_test.h" #include #include "kis_node_juggler_compressed.h" #include #include void KisNodeJugglerCompressedTest::init() { p.reset(new TestUtil::MaskParent); QRect rect1(100, 100, 100, 100); QRect rect2(150, 150, 150, 150); QRect rect3(50, 50, 100, 100); layer1 = p->layer; layer1->paintDevice()->fill(rect1, KoColor(Qt::red, layer1->colorSpace())); layer2 = new KisPaintLayer(p->image, "paint2", OPACITY_OPAQUE_U8); layer2->paintDevice()->fill(rect2, KoColor(Qt::blue, layer2->colorSpace())); layer3 = new KisPaintLayer(p->image, "paint3", OPACITY_OPAQUE_U8); group4 = new KisGroupLayer(p->image, "group4", OPACITY_OPAQUE_U8); layer5 = new KisPaintLayer(p->image, "paint5", OPACITY_OPAQUE_U8); layer6 = new KisPaintLayer(p->image, "paint6", OPACITY_OPAQUE_U8); p->image->addNode(layer2); p->image->addNode(layer3); p->image->addNode(group4); p->image->addNode(layer5, group4); p->image->addNode(layer6); p->image->initialRefreshGraph(); } void KisNodeJugglerCompressedTest::cleanup() { p.reset(); layer1.clear(); layer2.clear(); } void KisNodeJugglerCompressedTest::testMove(int delayBeforeEnd) { - TestUtil::ExternalImageChecker chk("node_juggler", "move_test"); + TestUtil::ReferenceImageChecker chk("node_juggler", "move_test"); chk.setMaxFailingPixels(0); KisNodeJugglerCompressed juggler(kundo2_i18n("Move Layer"), p->image, 0, 600); QVERIFY(chk.checkImage(p->image, "initial")); juggler.moveNode(layer1, p->image->root(), layer2); QTest::qWait(100); QVERIFY(chk.checkImage(p->image, "initial")); if (delayBeforeEnd) { QTest::qWait(delayBeforeEnd); QVERIFY(chk.checkImage(p->image, "moved")); } juggler.end(); p->image->waitForDone(); QVERIFY(chk.checkImage(p->image, "moved")); p->undoStore->undo(); p->image->waitForDone(); QVERIFY(chk.checkImage(p->image, "initial")); } void KisNodeJugglerCompressedTest::testApplyUndo() { testMove(1000); } void KisNodeJugglerCompressedTest::testEndBeforeUpdate() { testMove(0); } void KisNodeJugglerCompressedTest::testDuplicateImpl(bool externalParent, bool useMove) { - TestUtil::ExternalImageChecker chk("node_juggler", "move_test"); + TestUtil::ReferenceImageChecker chk("node_juggler", "move_test"); chk.setMaxFailingPixels(0); QStringList initialRef; initialRef << "paint1"; initialRef << "paint2"; initialRef << "paint3"; initialRef << "group4"; initialRef << "+paint5"; initialRef << "paint6"; QVERIFY(TestUtil::checkHierarchy(p->image->root(), initialRef)); KisNodeList selectedNodes; selectedNodes << layer2; selectedNodes << layer3; selectedNodes << layer5; KisNodeJugglerCompressed juggler(kundo2_i18n("Duplicate Layers"), p->image, 0, 600); if (!externalParent) { juggler.duplicateNode(selectedNodes); } else { if (useMove) { juggler.moveNode(selectedNodes, p->image->root(), layer6); } else { juggler.copyNode(selectedNodes, p->image->root(), layer6); } } QTest::qWait(1000); juggler.end(); p->image->waitForDone(); QStringList ref; if (!externalParent) { ref << "paint1"; ref << "paint2"; ref << "paint3"; ref << "group4"; ref << "+paint5"; ref << "+Copy of paint2"; ref << "+Copy of paint3"; ref << "+Copy of paint5"; ref << "paint6"; } else if (!useMove) { ref << "paint1"; ref << "paint2"; ref << "paint3"; ref << "group4"; ref << "+paint5"; ref << "paint6"; ref << "Copy of paint2"; ref << "Copy of paint3"; ref << "Copy of paint5"; } else { ref << "paint1"; ref << "group4"; ref << "paint6"; ref << "paint2"; ref << "paint3"; ref << "paint5"; } QVERIFY(TestUtil::checkHierarchy(p->image->root(), ref)); p->undoStore->undo(); p->image->waitForDone(); QVERIFY(TestUtil::checkHierarchy(p->image->root(), initialRef)); } void KisNodeJugglerCompressedTest::testDuplicate() { testDuplicateImpl(false, false); } void KisNodeJugglerCompressedTest::testCopyLayers() { testDuplicateImpl(true, false); } void KisNodeJugglerCompressedTest::testMoveLayers() { testDuplicateImpl(true, true); } QTEST_MAIN(KisNodeJugglerCompressedTest) diff --git a/libs/ui/tests/kis_shape_commands_test.cpp b/libs/ui/tests/kis_shape_commands_test.cpp index 03ca57c682..f93fa5727b 100644 --- a/libs/ui/tests/kis_shape_commands_test.cpp +++ b/libs/ui/tests/kis_shape_commands_test.cpp @@ -1,229 +1,229 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_shape_commands_test.h" #include #include "kis_global.h" #include "kis_shape_layer.h" #include #include #include "testutil.h" #include #include #include #include void KisShapeCommandsTest::testGrouping() { - TestUtil::ExternalImageChecker chk("grouping", "shape_commands_test"); + TestUtil::ReferenceImageChecker chk("grouping", "shape_commands_test"); QRect refRect(0,0,64,64); QScopedPointer doc(KisPart::instance()->createDocument()); TestUtil::MaskParent p(refRect); const qreal resolution = 72.0 / 72.0; p.image->setResolution(resolution, resolution); doc->setCurrentImage(p.image); KisShapeLayerSP shapeLayer = new KisShapeLayer(doc->shapeController(), p.image, "shapeLayer1", 75); { KoPathShape* path = new KoPathShape(); path->setShapeId(KoPathShapeId); path->moveTo(QPointF(5, 5)); path->lineTo(QPointF(5, 55)); path->lineTo(QPointF(55, 55)); path->lineTo(QPointF(55, 5)); path->close(); path->normalize(); path->setBackground(toQShared(new KoColorBackground(Qt::red))); path->setName("shape1"); path->setZIndex(1); shapeLayer->addShape(path); } { KoPathShape* path = new KoPathShape(); path->setShapeId(KoPathShapeId); path->moveTo(QPointF(30, 30)); path->lineTo(QPointF(30, 60)); path->lineTo(QPointF(60, 60)); path->lineTo(QPointF(60, 30)); path->close(); path->normalize(); path->setBackground(toQShared(new KoColorBackground(Qt::green))); path->setName("shape2"); path->setZIndex(2); shapeLayer->addShape(path); } p.image->addNode(shapeLayer); shapeLayer->setDirty(); qApp->processEvents(); p.image->waitForDone(); chk.checkImage(p.image, "00_initial_layer_update"); QList shapes = shapeLayer->shapes(); KoShapeGroup *group = new KoShapeGroup(); group->setName("group_shape"); shapeLayer->addShape(group); QScopedPointer cmd( new KoShapeGroupCommand(group, shapes, false, true, true)); cmd->redo(); shapeLayer->setDirty(); qApp->processEvents(); p.image->waitForDone(); chk.checkImage(p.image, "00_initial_layer_update"); cmd->undo(); shapeLayer->setDirty(); qApp->processEvents(); p.image->waitForDone(); chk.checkImage(p.image, "00_initial_layer_update"); QVERIFY(chk.testPassed()); } void KisShapeCommandsTest::testResizeShape(bool normalizeGroup) { - TestUtil::ExternalImageChecker chk("resize_shape", "shape_commands_test"); + TestUtil::ReferenceImageChecker chk("resize_shape", "shape_commands_test"); QRect refRect(0,0,64,64); QScopedPointer doc(KisPart::instance()->createDocument()); TestUtil::MaskParent p(refRect); const qreal resolution = 72.0 / 72.0; p.image->setResolution(resolution, resolution); doc->setCurrentImage(p.image); KisShapeLayerSP shapeLayer = new KisShapeLayer(doc->shapeController(), p.image, "shapeLayer1", 75); { KoPathShape* path = new KoPathShape(); path->setShapeId(KoPathShapeId); path->moveTo(QPointF(5, 5)); path->lineTo(QPointF(5, 55)); path->lineTo(QPointF(55, 55)); path->lineTo(QPointF(55, 5)); path->close(); path->normalize(); path->setBackground(toQShared(new KoColorBackground(Qt::red))); path->setName("shape1"); path->setZIndex(1); shapeLayer->addShape(path); } { KoPathShape* path = new KoPathShape(); path->setShapeId(KoPathShapeId); path->moveTo(QPointF(30, 30)); path->lineTo(QPointF(30, 60)); path->lineTo(QPointF(60, 60)); path->lineTo(QPointF(60, 30)); path->close(); path->normalize(); path->setBackground(toQShared(new KoColorBackground(Qt::green))); path->setName("shape2"); path->setZIndex(2); shapeLayer->addShape(path); } p.image->addNode(shapeLayer); shapeLayer->setDirty(); qApp->processEvents(); p.image->waitForDone(); chk.checkImage(p.image, "00_initial_layer_update"); QList shapes = shapeLayer->shapes(); KoShapeGroup *group = new KoShapeGroup(); group->setName("group_shape"); shapeLayer->addShape(group); QScopedPointer cmd( new KoShapeGroupCommand(group, shapes, false, true, normalizeGroup)); cmd->redo(); shapeLayer->setDirty(); qApp->processEvents(); p.image->waitForDone(); chk.checkImage(p.image, "00_initial_layer_update"); qDebug() << "Before:"; qDebug() << ppVar(group->absolutePosition(KoFlake::TopLeft)); qDebug() << ppVar(group->absolutePosition(KoFlake::BottomRight)); qDebug() << ppVar(group->outlineRect()); qDebug() << ppVar(group->transformation()); QCOMPARE(group->absolutePosition(KoFlake::TopLeft), QPointF(5,5)); QCOMPARE(group->absolutePosition(KoFlake::BottomRight), QPointF(60,60)); const QPointF stillPoint = group->absolutePosition(KoFlake::BottomRight); KoFlake::resizeShape(group, 1.2, 1.4, stillPoint, false, true, QTransform()); qDebug() << "After:"; qDebug() << ppVar(group->absolutePosition(KoFlake::TopLeft)); qDebug() << ppVar(group->absolutePosition(KoFlake::BottomRight)); qDebug() << ppVar(group->outlineRect()); qDebug() << ppVar(group->transformation()); QCOMPARE(group->absolutePosition(KoFlake::TopLeft), QPointF(-6,-17)); QCOMPARE(group->absolutePosition(KoFlake::BottomRight), QPointF(60,60)); } void KisShapeCommandsTest::testResizeShape() { testResizeShape(false); } void KisShapeCommandsTest::testResizeShapeNormalized() { testResizeShape(true); } QTEST_MAIN(KisShapeCommandsTest) diff --git a/libs/ui/tests/kis_shape_layer_test.cpp b/libs/ui/tests/kis_shape_layer_test.cpp new file mode 100644 index 0000000000..01d60f8617 --- /dev/null +++ b/libs/ui/tests/kis_shape_layer_test.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2018 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_shape_layer_test.h" + +#include + +#include "kis_global.h" + +#include "kis_shape_layer.h" +#include +#include +#include "testutil.h" + +#include +#include + +#include +#include + +#include "kis_filter_strategy.h" + + +void testMergeDownImpl(bool useImageTransformations) +{ + const QString testName = useImageTransformations ? "scale_and_merge_down" : "merge_down"; + + using namespace TestUtil; + + ReferenceImageChecker chk(testName, "shape_layer_test", ReferenceImageChecker::InternalStorage); + chk.setMaxFailingPixels(10); + + QScopedPointer doc(KisPart::instance()->createDocument()); + + const QRect refRect(0,0,64,64); + MaskParent p(refRect); + + const qreal resolution = 72.0 / 72.0; + p.image->setResolution(resolution, resolution); + + doc->setCurrentImage(p.image); + + + KisShapeLayerSP shapeLayer1 = new KisShapeLayer(doc->shapeController(), p.image, "shapeLayer1", 150); + + { + KoPathShape* path = new KoPathShape(); + path->setShapeId(KoPathShapeId); + path->moveTo(QPointF(5, 5)); + path->lineTo(QPointF(5, 55)); + path->lineTo(QPointF(20, 55)); + path->lineTo(QPointF(20, 5)); + path->close(); + path->normalize(); + path->setBackground(toQShared(new KoColorBackground(Qt::red))); + + path->setName("shape1"); + path->setZIndex(1); + shapeLayer1->addShape(path); + } + + KisShapeLayerSP shapeLayer2 = new KisShapeLayer(doc->shapeController(), p.image, "shapeLayer2", 255); + + { + KoPathShape* path = new KoPathShape(); + path->setShapeId(KoPathShapeId); + path->moveTo(QPointF(15, 10)); + path->lineTo(QPointF(15, 55)); + path->lineTo(QPointF(50, 55)); + path->lineTo(QPointF(50, 10)); + path->close(); + path->normalize(); + path->setBackground(toQShared(new KoColorBackground(Qt::green))); + + path->setName("shape2"); + path->setZIndex(1); + shapeLayer2->addShape(path); + } + + p.image->addNode(shapeLayer1); + p.image->addNode(shapeLayer2); + shapeLayer1->setDirty(); + shapeLayer2->setDirty(); + + qApp->processEvents(); + p.image->waitForDone(); + + QCOMPARE(int(p.image->root()->childCount()), 3); + + chk.checkImage(p.image, "00_initial_layer_update"); + + if (useImageTransformations) { + + KisFilterStrategy *strategy = new KisBilinearFilterStrategy(); + p.image->scaleImage(QSize(32, 32), p.image->xRes(), p.image->yRes(), strategy); + + qApp->processEvents(); + p.image->waitForDone(); + qApp->processEvents(); + + chk.checkImage(p.image, "01_after_scale_down"); + } + + p.image->mergeDown(shapeLayer2, KisMetaData::MergeStrategyRegistry::instance()->get("Drop")); + qApp->processEvents(); + p.image->waitForDone(); + qApp->processEvents(); + + QCOMPARE(int(p.image->root()->childCount()), 2); + + KisShapeLayer *newShapeLayer = dynamic_cast(p.image->root()->lastChild().data()); + QVERIFY(newShapeLayer); + + QVERIFY(newShapeLayer != shapeLayer1.data()); + QVERIFY(newShapeLayer != shapeLayer2.data()); + + chk.checkImage(p.image, "02_after_merge_down"); +} + +void KisShapeLayerTest::testMergeDown() +{ + testMergeDownImpl(false); +} + +void KisShapeLayerTest::testScaleAndMergeDown() +{ + testMergeDownImpl(true); +} + +namespace { +KoPathShape* createSimpleShape(int zIndex) +{ + KoPathShape* path = new KoPathShape(); + path->setShapeId(KoPathShapeId); + path->moveTo(QPointF(15, 10)); + path->lineTo(QPointF(15, 55)); + path->lineTo(QPointF(50, 55)); + path->lineTo(QPointF(50, 10)); + path->close(); + path->normalize(); + path->setBackground(toQShared(new KoColorBackground(Qt::green))); + + path->setName(QString("shape_%1").arg(zIndex)); + path->setZIndex(zIndex); + + return path; +} +} + +#include "commands/KoShapeReorderCommand.h" +#include + +void testMergingShapeZIndexesImpl(int firstIndexStart, + int firstIndexStep, + int firstIndexSize, + int secondIndexStart, + int secondIndexStep, + int secondIndexSize) +{ + QList shapesBelow; + QList shapesAbove; + + qDebug() << "Test zIndex merge:"; + qDebug() << " " << ppVar(firstIndexStart) << ppVar(firstIndexStep) << ppVar(firstIndexSize); + qDebug() << " " << ppVar(secondIndexStart) << ppVar(secondIndexStep) << ppVar(secondIndexSize); + + + for (int i = 0; i < firstIndexSize; i++) { + shapesBelow.append(createSimpleShape(firstIndexStart + firstIndexStep * i)); + } + + for (int i = 0; i < secondIndexSize; i++) { + shapesAbove.append(createSimpleShape(secondIndexStart + secondIndexStep * i)); + } + + QList shapes = + KoShapeReorderCommand::mergeDownShapes(shapesBelow, shapesAbove); + + KoShapeReorderCommand cmd(shapes); + cmd.redo(); + + for (int i = 0; i < shapesBelow.size(); i++) { + if (i > 0 && shapesBelow[i - 1]->zIndex() >= shapesBelow[i]->zIndex()) { + qDebug() << ppVar(i); + qDebug() << ppVar(shapesBelow[i - 1]->zIndex()) << ppVar(shapesBelow[i]->zIndex()); + QFAIL("Shapes have wrong ordering after merge!"); + } + } + + if (!shapesBelow.isEmpty() && !shapesAbove.isEmpty()) { + if (shapesBelow.last()->zIndex() >= shapesAbove.first()->zIndex()) { + qDebug() << ppVar(shapesBelow.last()->zIndex()) << ppVar(shapesAbove.first()->zIndex()); + QFAIL("Two shape groups have intersections after merge!"); + } + } + + for (int i = 0; i < shapesAbove.size(); i++) { + if (i > 0 && shapesAbove[i - 1]->zIndex() >= shapesAbove[i]->zIndex()) { + qDebug() << ppVar(i); + qDebug() << ppVar(shapesAbove[i - 1]->zIndex()) << ppVar(shapesAbove[i]->zIndex()); + QFAIL("Shapes have wrong ordering after merge!"); + } + } + +} + +void KisShapeLayerTest::testMergingShapeZIndexes() +{ + testMergingShapeZIndexesImpl(0, 1, 10, + 5, 1, 10); + + testMergingShapeZIndexesImpl(0, 1, 0, + 5, 1, 10); + + testMergingShapeZIndexesImpl(0, 1, 10, + 5, 1, 0); + + testMergingShapeZIndexesImpl(std::numeric_limits::max() - 10, 1, 10, + 5, 1, 10); + + testMergingShapeZIndexesImpl(-32768, 1024, 64, + 0, 1024, 31); + + testMergingShapeZIndexesImpl(-32768+1023, 1024, 64, + 0, 1, 1024); +} + +void KisShapeLayerTest::testCloneScaledLayer() +{ + using namespace TestUtil; + + ReferenceImageChecker chk("scale_and_clone", "shape_layer_test", ReferenceImageChecker::InternalStorage); + chk.setMaxFailingPixels(10); + + QScopedPointer doc(KisPart::instance()->createDocument()); + + const QRect refRect(0,0,64,64); + MaskParent p(refRect); + + const qreal resolution = 72.0 / 72.0; + p.image->setResolution(resolution, resolution); + + doc->setCurrentImage(p.image); + + + KisShapeLayerSP shapeLayer1 = new KisShapeLayer(doc->shapeController(), p.image, "shapeLayer1", 150); + + { + KoPathShape* path = new KoPathShape(); + path->setShapeId(KoPathShapeId); + path->moveTo(QPointF(5, 5)); + path->lineTo(QPointF(5, 55)); + path->lineTo(QPointF(20, 55)); + path->lineTo(QPointF(20, 5)); + path->close(); + path->normalize(); + path->setBackground(toQShared(new KoColorBackground(Qt::red))); + + path->setName("shape1"); + path->setZIndex(1); + shapeLayer1->addShape(path); + } + + p.image->addNode(shapeLayer1); + shapeLayer1->setDirty(); + + qApp->processEvents(); + p.image->waitForDone(); + + QCOMPARE(int(p.image->root()->childCount()), 2); + + chk.checkImage(p.image, "00_initial_layer_update"); + + { + + KisFilterStrategy *strategy = new KisBilinearFilterStrategy(); + p.image->scaleImage(QSize(32, 32), p.image->xRes(), p.image->yRes(), strategy); + + qApp->processEvents(); + p.image->waitForDone(); + + chk.checkImage(p.image, "01_after_scale_down"); + } + + KisNodeSP clonedLayer = shapeLayer1->clone(); + + p.image->removeNode(shapeLayer1); + p.image->addNode(clonedLayer); + clonedLayer->setDirty(); + + qApp->processEvents(); + p.image->waitForDone(); + + QCOMPARE(int(p.image->root()->childCount()), 2); + chk.checkImage(p.image, "01_after_scale_down"); +} + +QTEST_MAIN(KisShapeLayerTest) diff --git a/plugins/dockers/lut/lut_export.h b/libs/ui/tests/kis_shape_layer_test.h similarity index 66% rename from plugins/dockers/lut/lut_export.h rename to libs/ui/tests/kis_shape_layer_test.h index 0275676efe..7d15dd57ab 100644 --- a/plugins/dockers/lut/lut_export.h +++ b/libs/ui/tests/kis_shape_layer_test.h @@ -1,30 +1,36 @@ /* - * Copyright (c) 2015 Dmitry Kazakov + * Copyright (c) 2018 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 __LUT_EXPORT_H -#define __LUT_EXPORT_H +#ifndef KISSHAPELAYERTEST_H +#define KISSHAPELAYERTEST_H -#ifndef LUT_EXPORT -# ifdef MAKE_KRITALUTDOCKER_LIB -# define LUT_EXPORT KDE_EXPORT -# else -# define LUT_EXPORT KDE_IMPORT -# endif -#endif +#include -#endif /* __LUT_EXPORT_H */ +class KisShapeLayerTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + + void testMergeDown(); + void testScaleAndMergeDown(); + void testMergingShapeZIndexes(); + + void testCloneScaledLayer(); +}; + +#endif // KISSHAPELAYERTEST_H diff --git a/libs/ui/widgets/kis_color_label_selector_widget.cpp b/libs/ui/widgets/kis_color_label_selector_widget.cpp index d697a974df..0bc30f4f71 100644 --- a/libs/ui/widgets/kis_color_label_selector_widget.cpp +++ b/libs/ui/widgets/kis_color_label_selector_widget.cpp @@ -1,340 +1,341 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_label_selector_widget.h" #include "kis_debug.h" #include "kis_global.h" #include #include #include #include #include #include #include #include #include #include #include "kis_node_view_color_scheme.h" struct KisColorLabelSelectorWidget::Private { Private(KisColorLabelSelectorWidget *_q) : q(_q), xMenuOffset(0), yCenteringOffset(0), realItemSize(0), realItemSpacing(0), hoveringItem(-1), selectedItem(0) { } KisColorLabelSelectorWidget *q; QVector colors; const int minHeight = 12 + 4; const int minSpacing = 1; const int maxSpacing = 3; const int border = 2; int xMenuOffset; int yCenteringOffset; int realItemSize; int realItemSpacing; int hoveringItem; int selectedItem; QRect itemRect(int index) const; int indexFromPos(const QPoint &pos); void updateItem(int index); int widthForHeight(int height, int spacing) const; int heightForWidth(int width, int spacing) const; void updateItemSizes(const QSize &widgetSize); }; KisColorLabelSelectorWidget::KisColorLabelSelectorWidget(QWidget *parent) : QWidget(parent), m_d(new Private(this)) { KisNodeViewColorScheme scm; m_d->colors = scm.allColorLabels(); setMouseTracking(true); } KisColorLabelSelectorWidget::~KisColorLabelSelectorWidget() { } int KisColorLabelSelectorWidget::currentIndex() const { return m_d->selectedItem; } void KisColorLabelSelectorWidget::setCurrentIndex(int index) { if (index == m_d->selectedItem) return; const int oldItem = m_d->selectedItem; m_d->selectedItem = index; m_d->updateItem(oldItem); m_d->updateItem(m_d->selectedItem); + m_d->hoveringItem = index; emit currentIndexChanged(m_d->selectedItem); } QSize KisColorLabelSelectorWidget::minimumSizeHint() const { return QSize(m_d->widthForHeight(m_d->minHeight, m_d->minSpacing), m_d->minHeight); } QSize KisColorLabelSelectorWidget::sizeHint() const { const int preferredHeight = 22 + 2 * m_d->border; return QSize(m_d->widthForHeight(preferredHeight, m_d->maxSpacing), preferredHeight); } void KisColorLabelSelectorWidget::resizeEvent(QResizeEvent *e) { m_d->xMenuOffset = 0; bool hasWideItems = false; QMenu *menu = qobject_cast(parent()); if (menu) { Q_FOREACH(QAction *action, menu->actions()) { if (action->isCheckable() || !action->icon().isNull()) { hasWideItems = true; break; } } } if (hasWideItems) { QStyleOption opt; opt.init(this); // some copy-pasted code from QFusionStyle style const int hmargin = style()->pixelMetric(QStyle::PM_MenuHMargin, &opt, this); const int icone = style()->pixelMetric(QStyle::PM_SmallIconSize, &opt, this); m_d->xMenuOffset = hmargin + icone + 6; } m_d->updateItemSizes(e->size()); QWidget::resizeEvent(e); } int KisColorLabelSelectorWidget::Private::widthForHeight(int height, int spacing) const { return height * colors.size() + spacing * (colors.size() - 1) + 2 * border + xMenuOffset; } int KisColorLabelSelectorWidget::Private::heightForWidth(int width, int spacing) const { const int numItems = colors.size(); return qRound(qreal(width - spacing * (numItems - 1) - 2 * border - xMenuOffset) / numItems); } void KisColorLabelSelectorWidget::Private::updateItemSizes(const QSize &widgetSize) { const int height = qBound(minHeight, heightForWidth(widgetSize.width(), minSpacing), widgetSize.height()); const int size = height - 2 * border; const int numItems = colors.size(); const int rest = widgetSize.width() - size * numItems - 2 * border - xMenuOffset; const int spacing = qBound(minSpacing, rest / (numItems - 1), maxSpacing); realItemSize = size; realItemSpacing = spacing; yCenteringOffset = qMax(0, (widgetSize.height() - height) / 2); } QRect KisColorLabelSelectorWidget::Private::itemRect(int index) const { const int x = xMenuOffset + border + index * realItemSize + index * realItemSpacing; const int y = border + yCenteringOffset; return QRect(x, y, realItemSize, realItemSize); } int KisColorLabelSelectorWidget::Private::indexFromPos(const QPoint &pos) { const int x = pos.x() - border - xMenuOffset; const int y = pos.y() - border - yCenteringOffset; if (y < 0 || y >= realItemSize) return -1; int idx = (x + realItemSpacing) / (realItemSize + realItemSpacing); if (idx < 0 || idx >= colors.size()) { idx = -1; } return idx; } void KisColorLabelSelectorWidget::Private::updateItem(int index) { if (index >= 0 && index < colors.size()) { q->update(kisGrowRect(itemRect(index), border)); } } enum State { NORMAL = 0, HOVER, CHECKED, DISABLED }; void drawToolButton(QWidget *widget, const QRect &rc, State state, const QColor &color, int border) { QStylePainter p(widget); QStyleOption opt; opt.initFrom(widget); opt.rect = kisGrowRect(rc, border); switch (state) { case DISABLED: case NORMAL: opt.state &= ~QStyle::State_Raised; break; case HOVER: opt.state |= QStyle::State_Raised; break; case CHECKED: opt.state |= QStyle::State_On; break; }; if (opt.state & (QStyle::State_Sunken | QStyle::State_On | QStyle::State_Raised)) { const QRect borderRect = kisGrowRect(rc, 1); p.setPen(QPen(opt.palette.text().color(), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p.drawRect(borderRect); } const int offset = qMax(1, rc.height() / 10); const QRect colorBlobRect = kisGrowRect(rc, -offset); if (color.alpha() > 0) { QColor fillColor = color; if (state == DISABLED) { fillColor.setHsl(0, 0, color.lightness()); } p.fillRect(colorBlobRect, fillColor); } else { // draw an X for no color for the first item QRect crossRect = kisGrowRect(colorBlobRect, -offset); QColor shade = opt.palette.text().color(); p.setPen(QPen(shade, 2)); p.drawLine(crossRect.topLeft(), crossRect.bottomRight()); p.drawLine(crossRect.bottomLeft(), crossRect.topRight()); } } void KisColorLabelSelectorWidget::paintEvent(QPaintEvent *e) { QWidget::paintEvent(e); if (isEnabled()) { for (int i = 0; i < m_d->colors.size(); i++) { if (i == m_d->selectedItem || i == m_d->hoveringItem) { continue; } drawToolButton(this, m_d->itemRect(i), NORMAL, m_d->colors[i], m_d->border); } if (m_d->selectedItem >= 0) { drawToolButton(this, m_d->itemRect(m_d->selectedItem), CHECKED, m_d->colors[m_d->selectedItem], m_d->border); } if (m_d->hoveringItem >= 0 && m_d->hoveringItem != m_d->selectedItem) { drawToolButton(this, m_d->itemRect(m_d->hoveringItem), HOVER, m_d->colors[m_d->hoveringItem], m_d->border); } } else { for (int i = 0; i < m_d->colors.size(); i++) { drawToolButton(this, m_d->itemRect(i), DISABLED, m_d->colors[i], m_d->border); } } } void KisColorLabelSelectorWidget::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Right || e->key() == Qt::Key_Up) { int newItem = (m_d->selectedItem + 1) % m_d->colors.size(); setCurrentIndex(newItem); } else if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Down) { int newItem = m_d->selectedItem < 0 ? m_d->colors.size() - 1 : (m_d->selectedItem + m_d->colors.size() - 1) % m_d->colors.size(); setCurrentIndex(newItem); } QWidget::keyPressEvent(e); } void KisColorLabelSelectorWidget::mousePressEvent(QMouseEvent *e) { const int newItem = m_d->indexFromPos(e->pos()); if (newItem >= 0) { setCurrentIndex(newItem); } QWidget::mousePressEvent(e); } void KisColorLabelSelectorWidget::mouseReleaseEvent(QMouseEvent *e) { QWidget::mouseReleaseEvent(e); } void KisColorLabelSelectorWidget::mouseMoveEvent(QMouseEvent *e) { const int newItem = m_d->indexFromPos(e->pos()); if (newItem >= 0 && e->buttons()) { setCurrentIndex(newItem); } const int oldItem = m_d->hoveringItem; m_d->hoveringItem = m_d->indexFromPos(e->pos()); m_d->updateItem(oldItem); m_d->updateItem(m_d->hoveringItem); update(); QWidget::mouseMoveEvent(e); } void KisColorLabelSelectorWidget::leaveEvent(QEvent *e) { const int oldItem = m_d->hoveringItem; m_d->hoveringItem = -1; m_d->updateItem(oldItem); QWidget::leaveEvent(e); } diff --git a/libs/widgets/KoDockWidgetTitleBar.cpp b/libs/widgets/KoDockWidgetTitleBar.cpp index b34e60f74a..c5ce2ead36 100644 --- a/libs/widgets/KoDockWidgetTitleBar.cpp +++ b/libs/widgets/KoDockWidgetTitleBar.cpp @@ -1,379 +1,379 @@ /* This file is part of the KDE project Copyright (c) 2007 Marijn Kruisselbrink Copyright (C) 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 "KoDockWidgetTitleBar.h" #include "KoDockWidgetTitleBar_p.h" #include "KoDockWidgetTitleBarButton.h" #include #include #include #include #include #include #include #include #include #include #include static inline bool hasFeature(const QDockWidget *dockwidget, QDockWidget::DockWidgetFeature feature) { return (dockwidget->features() & feature) == feature; } KoDockWidgetTitleBar::KoDockWidgetTitleBar(QDockWidget* dockWidget) : QWidget(dockWidget), d(new Private(this)) { QDockWidget *q = dockWidget; d->floatIcon = kisIcon("docker_float"); d->floatButton = new KoDockWidgetTitleBarButton(this); d->floatButton->setIcon(d->floatIcon); connect(d->floatButton, SIGNAL(clicked()), SLOT(toggleFloating())); d->floatButton->setVisible(true); d->floatButton->setToolTip(i18nc("@info:tooltip", "Float Docker")); d->floatButton->setStyleSheet("border: 0"); d->removeIcon = kisIcon("docker_close"); d->closeButton = new KoDockWidgetTitleBarButton(this); d->closeButton->setIcon(d->removeIcon); connect(d->closeButton, SIGNAL(clicked()), q, SLOT(close())); d->closeButton->setVisible(true); d->closeButton->setToolTip(i18nc("@info:tooltip", "Close Docker")); d->closeButton->setStyleSheet("border: 0"); // border makes the header busy looking (appears on some OSs) d->openIcon = kisIcon("docker_collapse_a"); d->closeIcon = kisIcon("docker_collapse_b"); d->collapseButton = new KoDockWidgetTitleBarButton(this); d->collapseButton->setIcon(d->openIcon); connect(d->collapseButton, SIGNAL(clicked()), SLOT(toggleCollapsed())); d->collapseButton->setVisible(true); d->collapsable = true; d->collapseButton->setToolTip(i18nc("@info:tooltip", "Collapse Docker")); d->collapseButton->setStyleSheet("border: 0"); d->lockIcon = kisIcon("docker_lock_a"); d->lockButton = new KoDockWidgetTitleBarButton(this); d->lockButton->setCheckable(true); d->lockButton->setIcon(d->lockIcon); connect(d->lockButton, SIGNAL(toggled(bool)), SLOT(setLocked(bool))); d->lockButton->setVisible(true); d->lockable = true; d->lockButton->setToolTip(i18nc("@info:tooltip", "Lock Docker")); d->lockButton->setStyleSheet("border: 0"); connect(dockWidget, SIGNAL(featuresChanged(QDockWidget::DockWidgetFeatures)), SLOT(featuresChanged(QDockWidget::DockWidgetFeatures))); connect(dockWidget, SIGNAL(topLevelChanged(bool)), SLOT(topLevelChanged(bool))); d->featuresChanged(0); } KoDockWidgetTitleBar::~KoDockWidgetTitleBar() { delete d; } QSize KoDockWidgetTitleBar::minimumSizeHint() const { return sizeHint(); } QSize KoDockWidgetTitleBar::sizeHint() const { if (isHidden()) { return QSize(0, 0); } QDockWidget *q = qobject_cast(parentWidget()); int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q); int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q); // get size of buttons... QSize closeSize(0, 0); if (d->closeButton && hasFeature(q, QDockWidget::DockWidgetClosable)) { closeSize = d->closeButton->sizeHint(); } QSize floatSize(0, 0); if (d->floatButton && hasFeature(q, QDockWidget::DockWidgetFloatable)) { floatSize = d->floatButton->sizeHint(); } QSize hideSize(0, 0); if (d->collapseButton && d->collapsable) { hideSize = d->collapseButton->sizeHint(); } QSize lockSize(0, 0); if (d->lockButton && d->lockable) { lockSize = d->lockButton->sizeHint(); } int buttonHeight = qMax(qMax(qMax(closeSize.height(), floatSize.height()), hideSize.height()), lockSize.height()) + 2; int buttonWidth = closeSize.width() + floatSize.width() + hideSize.width() + lockSize.width(); int height = buttonHeight; if (d->textVisibilityMode == FullTextAlwaysVisible) { // get font size QFontMetrics titleFontMetrics = q->fontMetrics(); int fontHeight = titleFontMetrics.lineSpacing() + 2 * mw; height = qMax(height, fontHeight); } /* * Calculate the width of title and add to the total width of the docker window when collapsed. */ const int titleWidth = (d->textVisibilityMode == FullTextAlwaysVisible) ? (q->fontMetrics().width(q->windowTitle()) + 2*mw) : 0; if (d->preCollapsedWidth > 0) { return QSize(d->preCollapsedWidth, height); } else { if (d->textVisibilityMode == FullTextAlwaysVisible) { return QSize(buttonWidth /*+ height*/ + 2*mw + 2*fw + titleWidth, height); } else { if (q->widget()) { - return QSize(qMin(q->widget()->sizeHint().width(), buttonWidth), height); + return QSize(qMin(q->widget()->minimumSizeHint().width(), buttonWidth), height); } else { return QSize(buttonWidth, height); } } } } void KoDockWidgetTitleBar::paintEvent(QPaintEvent*) { QStylePainter p(this); QDockWidget *q = qobject_cast(parentWidget()); int fw = q->isFloating() ? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q) : 0; int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q); QStyleOptionDockWidget titleOpt; titleOpt.initFrom(q); QSize collapseButtonSize(0,0); if (d->collapsable && d->collapseButton->isVisible()) { collapseButtonSize = d->collapseButton->size(); } QSize lockButtonSize(0,0); if (d->lockable && d->lockButton->isVisible()) { lockButtonSize = d->lockButton->size(); } titleOpt.rect = QRect(QPoint(fw + mw + collapseButtonSize.width() + lockButtonSize.width(), 0), QSize(geometry().width() - (fw * 2) - mw - collapseButtonSize.width() - lockButtonSize.width(), geometry().height())); titleOpt.title = q->windowTitle(); titleOpt.closable = hasFeature(q, QDockWidget::DockWidgetClosable); titleOpt.floatable = hasFeature(q, QDockWidget::DockWidgetFloatable); p.drawControl(QStyle::CE_DockWidgetTitle, titleOpt); } void KoDockWidgetTitleBar::resizeEvent(QResizeEvent*) { QDockWidget *q = qobject_cast(parentWidget()); int fw = q->isFloating() ? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q) : 0; QStyleOptionDockWidget opt; opt.initFrom(q); opt.rect = QRect(QPoint(fw, fw), QSize(geometry().width() - (fw * 2), geometry().height() - (fw * 2))); opt.title = q->windowTitle(); opt.closable = hasFeature(q, QDockWidget::DockWidgetClosable); opt.floatable = hasFeature(q, QDockWidget::DockWidgetFloatable); QRect floatRect = q->style()->subElementRect(QStyle::SE_DockWidgetFloatButton, &opt, q); if (!floatRect.isNull()) d->floatButton->setGeometry(floatRect); QRect closeRect = q->style()->subElementRect(QStyle::SE_DockWidgetCloseButton, &opt, q); if (!closeRect.isNull()) d->closeButton->setGeometry(closeRect); int top = fw; if (!floatRect.isNull()) top = floatRect.y(); else if (!closeRect.isNull()) top = closeRect.y(); QSize size = d->collapseButton->size(); if (!closeRect.isNull()) { size = d->closeButton->size(); } else if (!floatRect.isNull()) { size = d->floatButton->size(); } QRect collapseRect = QRect(QPoint(fw, top), size); d->collapseButton->setGeometry(collapseRect); size = d->lockButton->size(); if (!closeRect.isNull()) { size = d->closeButton->size(); } else if (!floatRect.isNull()) { size = d->floatButton->size(); } const QSize lockRectSize = size; if (q->isFloating() || (width() < (closeRect.width() + lockRectSize.width()) + 50)) { d->collapsable = false; d->collapseButton->setVisible(false); d->lockButton->setVisible(false); d->lockable = false; } else { d->collapsable = d->collapsableSet; d->collapseButton->setVisible(d->collapsableSet); d->lockButton->setVisible(true); d->lockable = true; } int offset = 0; if (d->collapsable) { offset = collapseRect.width(); } QRect lockRect = QRect(QPoint(fw + 2 + offset, top), lockRectSize); d->lockButton->setGeometry(lockRect); } void KoDockWidgetTitleBar::setCollapsed(bool collapsed) { QDockWidget *q = qobject_cast(parentWidget()); if (q && q->widget() && q->widget()->isHidden() != collapsed) d->toggleCollapsed(); } void KoDockWidgetTitleBar::setLocked(bool locked) { QDockWidget *q = qobject_cast(parentWidget()); d->locked = locked; d->lockButton->blockSignals(true); d->lockButton->setChecked(locked); d->lockButton->blockSignals(false); //qDebug() << "setlocked" << q << d->locked << locked; if (locked) { d->features = q->features(); q->setFeatures(QDockWidget::NoDockWidgetFeatures); } else { q->setFeatures(d->features); } q->toggleViewAction()->setEnabled(!locked); d->closeButton->setEnabled(!locked); d->floatButton->setEnabled(!locked); d->collapseButton->setEnabled(!locked); d->updateIcons(); q->setProperty("Locked", locked); resizeEvent(0); } void KoDockWidgetTitleBar::setCollapsable(bool collapsable) { d->collapsableSet = collapsable; d->collapsable = collapsable; d->collapseButton->setVisible(collapsable); } void KoDockWidgetTitleBar::setTextVisibilityMode(TextVisibilityMode textVisibilityMode) { d->textVisibilityMode = textVisibilityMode; } void KoDockWidgetTitleBar::updateIcons() { d->updateIcons(); } void KoDockWidgetTitleBar::Private::toggleFloating() { QDockWidget *q = qobject_cast(thePublic->parentWidget()); q->setFloating(!q->isFloating()); updateIcons(); } void KoDockWidgetTitleBar::Private::topLevelChanged(bool topLevel) { lockButton->setEnabled(!topLevel); updateIcons(); } void KoDockWidgetTitleBar::Private::toggleCollapsed() { QDockWidget *q = qobject_cast(thePublic->parentWidget()); if (q == 0) // there does not *have* to be anything on the dockwidget. return; preCollapsedWidth = q->widget()->isHidden() ? -1 : thePublic->width(); q->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); // will be overwritten again next if (q->widget()) { q->widget()->setVisible(q->widget()->isHidden()); collapseButton->setIcon(q->widget()->isHidden() ? kisIcon("docker_collapse_b") : kisIcon("docker_collapse_a")); } } void KoDockWidgetTitleBar::Private::featuresChanged(QDockWidget::DockWidgetFeatures) { QDockWidget *q = qobject_cast(thePublic->parentWidget()); closeButton->setVisible(hasFeature(q, QDockWidget::DockWidgetClosable)); floatButton->setVisible(hasFeature(q, QDockWidget::DockWidgetFloatable)); thePublic->resizeEvent(0); } void KoDockWidgetTitleBar::Private::updateIcons() { QDockWidget *q = qobject_cast(thePublic->parentWidget()); lockIcon = (!locked) ? kisIcon("docker_lock_a") : kisIcon("docker_lock_b"); lockButton->setIcon(lockIcon); // this method gets called when switching themes, so update all of the themed icons now floatButton->setIcon(kisIcon("docker_float")); closeButton->setIcon(kisIcon("docker_close")); if (q->widget()) { collapseButton->setIcon(q->widget()->isHidden() ? kisIcon("docker_collapse_b") : kisIcon("docker_collapse_a")); } thePublic->resizeEvent(0); } //have to include this because of Q_PRIVATE_SLOT #include "moc_KoDockWidgetTitleBar.cpp" diff --git a/libs/widgets/KoToolBox.cpp b/libs/widgets/KoToolBox.cpp index 74cad37b72..521404f845 100644 --- a/libs/widgets/KoToolBox.cpp +++ b/libs/widgets/KoToolBox.cpp @@ -1,372 +1,342 @@ /* * Copyright (c) 2005-2009 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * * 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 "KoToolBox_p.h" #include "KoToolBoxLayout_p.h" #include "KoToolBoxButton_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BUTTON_MARGIN 10 static int buttonSize(int screen) { QRect rc = qApp->desktop()->screenGeometry(screen); if (rc.width() <= 1024) { return 12; } else if (rc.width() <= 1377) { return 14; } else if (rc.width() <= 1920 ) { return 16; } else { return 22; } } class KoToolBox::Private { public: Private() : layout(0) , buttonGroup(0) , floating(false) , contextSize(0) { } void addSection(Section *section, const QString &name); QList buttons; QMap sections; KoToolBoxLayout *layout; QButtonGroup *buttonGroup; QHash visibilityCodes; bool floating; QMap contextIconSizes; QMenu* contextSize; Qt::Orientation orientation; }; void KoToolBox::Private::addSection(Section *section, const QString &name) { section->setName(name); layout->addSection(section); sections.insert(name, section); } KoToolBox::KoToolBox() : d(new Private) { d->layout = new KoToolBoxLayout(this); // add defaults d->addSection(new Section(this), "main"); d->addSection(new Section(this), "dynamic"); d->buttonGroup = new QButtonGroup(this); setLayout(d->layout); Q_FOREACH (KoToolAction *toolAction, KoToolManager::instance()->toolActionList()) { addButton(toolAction); } // Update visibility of buttons setButtonsVisible(QList()); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*, int)), this, SLOT(setActiveTool(KoCanvasController*, int))); connect(KoToolManager::instance(), SIGNAL(currentLayerChanged(const KoCanvasController*,const KoShapeLayer*)), this, SLOT(setCurrentLayer(const KoCanvasController*,const KoShapeLayer*))); connect(KoToolManager::instance(), SIGNAL(toolCodesSelected(QList)), this, SLOT(setButtonsVisible(QList))); connect(KoToolManager::instance(), SIGNAL(addedTool(KoToolAction*,KoCanvasController*)), this, SLOT(toolAdded(KoToolAction*,KoCanvasController*))); QTimer::singleShot(0, this, SLOT(adjustToFit())); } KoToolBox::~KoToolBox() { delete d; } void KoToolBox::addButton(KoToolAction *toolAction) { KoToolBoxButton *button = new KoToolBoxButton(toolAction, this); d->buttons << button; int toolbuttonSize = buttonSize(qApp->desktop()->screenNumber(this)); KConfigGroup cfg = KSharedConfig::openConfig()->group("KoToolBox"); int iconSize = cfg.readEntry("iconSize", toolbuttonSize); button->setIconSize(QSize(iconSize, iconSize)); foreach (Section *section, d->sections.values()) { section->setButtonSize(QSize(iconSize + BUTTON_MARGIN, iconSize + BUTTON_MARGIN)); } QString sectionToBeAddedTo; const QString section = toolAction->section(); if (section.contains(qApp->applicationName())) { sectionToBeAddedTo = "main"; } else if (section.contains("main")) { sectionToBeAddedTo = "main"; } else if (section.contains("dynamic")) { sectionToBeAddedTo = "dynamic"; } else { sectionToBeAddedTo = section; } Section *sectionWidget = d->sections.value(sectionToBeAddedTo); if (sectionWidget == 0) { sectionWidget = new Section(this); d->addSection(sectionWidget, sectionToBeAddedTo); } sectionWidget->addButton(button, toolAction->priority()); d->buttonGroup->addButton(button, toolAction->buttonGroupId()); d->visibilityCodes.insert(button, toolAction->visibilityCode()); } void KoToolBox::setActiveTool(KoCanvasController *canvas, int id) { Q_UNUSED(canvas); QAbstractButton *button = d->buttonGroup->button(id); if (button) { button->setChecked(true); (qobject_cast(button))->setHighlightColor(); } else { warnWidgets << "KoToolBox::setActiveTool(" << id << "): no such button found"; } } void KoToolBox::setButtonsVisible(const QList &codes) { Q_FOREACH (QToolButton *button, d->visibilityCodes.keys()) { QString code = d->visibilityCodes.value(button); if (code.startsWith(QLatin1String("flake/"))) { continue; } if (code.endsWith( QLatin1String( "/always"))) { button->setVisible(true); button->setEnabled(true); } else if (code.isEmpty()) { button->setVisible(true); button->setEnabled( codes.count() != 0 ); } else { button->setVisible( codes.contains(code) ); } } layout()->invalidate(); update(); } void KoToolBox::setCurrentLayer(const KoCanvasController *canvas, const KoShapeLayer *layer) { Q_UNUSED(canvas); const bool enabled = layer == 0 || (layer->isEditable() && layer->isVisible()); foreach (QToolButton *button, d->visibilityCodes.keys()) { if (d->visibilityCodes[button].endsWith( QLatin1String( "/always") ) ) { continue; } button->setEnabled(enabled); } } void KoToolBox::paintEvent(QPaintEvent *) { QPainter painter(this); const QList sections = d->sections.values(); QList::const_iterator iterator = sections.begin(); int halfSpacing = layout()->spacing(); if (halfSpacing > 0) { halfSpacing /= 2; } while(iterator != sections.end()) { Section *section = *iterator; QStyleOption styleoption; styleoption.palette = palette(); if (section->separators() & Section::SeparatorTop) { int y = section->y() - halfSpacing; styleoption.state = QStyle::State_None; styleoption.rect = QRect(section->x(), y-1, section->width(), 2); style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &styleoption, &painter); } if (section->separators() & Section::SeparatorLeft) { int x = section->x() - halfSpacing; styleoption.state = QStyle::State_Horizontal; styleoption.rect = QRect(x-1, section->y(), 2, section->height()); style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &styleoption, &painter); } ++iterator; } painter.end(); } -void KoToolBox::resizeEvent(QResizeEvent* event) -{ - QRect availableRc = qApp->desktop()->availableGeometry(this); - QSize layoutSize = layout()->minimumSize(); - - QWidget::resizeEvent(event); - - if (layoutSize.height() > availableRc.height()) { - setMinimumWidth(layout()->minimumSize().width() * 2); // This enforces the minimum size on the widget - adjustToFit(); - } - else if (layoutSize.width() > availableRc.width()) { - setMinimumHeight(layout()->minimumSize().height() * 2); // This enforces the minimum size on the widget - adjustToFit(); - } - -} - void KoToolBox::setOrientation(Qt::Orientation orientation) { d->orientation = orientation; d->layout->setOrientation(orientation); QTimer::singleShot(0, this, SLOT(update())); Q_FOREACH (Section* section, d->sections) { section->setOrientation(orientation); } } void KoToolBox::setFloating(bool v) { - setMinimumSize(QSize(1,1)); d->floating = v; } void KoToolBox::toolAdded(KoToolAction *toolAction, KoCanvasController *canvas) { Q_UNUSED(canvas); addButton(toolAction); setButtonsVisible(QList()); } -void KoToolBox::adjustToFit() -{ - int newWidth = width() - (width() % layout()->minimumSize().width()); - - if (newWidth != width() && newWidth >= layout()->minimumSize().width()) { - setMaximumWidth(newWidth); - QTimer::singleShot(0, this, SLOT(resizeUnlock())); - } -} - -void KoToolBox::resizeUnlock() -{ - setMaximumWidth(QWIDGETSIZE_MAX); -} - void KoToolBox::slotContextIconSize() { QAction* action = qobject_cast(sender()); if (action && d->contextIconSizes.contains(action)) { const int iconSize = d->contextIconSizes.value(action); KConfigGroup cfg = KSharedConfig::openConfig()->group("KoToolBox"); cfg.writeEntry("iconSize", iconSize); Q_FOREACH (QToolButton *button, d->buttons) { button->setIconSize(QSize(iconSize, iconSize)); } Q_FOREACH (Section *section, d->sections.values()) { section->setButtonSize(QSize(iconSize + BUTTON_MARGIN, iconSize + BUTTON_MARGIN)); } } - - adjustToFit(); } void KoToolBox::contextMenuEvent(QContextMenuEvent *event) { int toolbuttonSize = buttonSize(qApp->desktop()->screenNumber(this)); if (!d->contextSize) { d->contextSize = new QMenu(i18n("Icon Size"), this); d->contextIconSizes.insert(d->contextSize->addAction(i18nc("@item:inmenu Icon size", "Default"), this, SLOT(slotContextIconSize())), toolbuttonSize); QList sizes; sizes << 12 << 14 << 16 << 22 << 32 << 48 << 64; //<< 96 << 128 << 192 << 256; Q_FOREACH (int i, sizes) { d->contextIconSizes.insert(d->contextSize->addAction(i18n("%1x%2", i, i), this, SLOT(slotContextIconSize())), i); } QActionGroup *sizeGroup = new QActionGroup(d->contextSize); foreach (QAction *action, d->contextSize->actions()) { action->setActionGroup(sizeGroup); action->setCheckable(true); } } KConfigGroup cfg = KSharedConfig::openConfig()->group("KoToolBox"); toolbuttonSize = cfg.readEntry("iconSize", toolbuttonSize); QMapIterator< QAction*, int > it = d->contextIconSizes; while (it.hasNext()) { it.next(); if (it.value() == toolbuttonSize) { it.key()->setChecked(true); break; } } d->contextSize->exec(event->globalPos()); } +KoToolBoxLayout *KoToolBox::toolBoxLayout() const +{ + return d->layout; +} + +#include "moc_KoToolBoxScrollArea_p.cpp" diff --git a/libs/widgets/KoToolBoxDocker.cpp b/libs/widgets/KoToolBoxDocker.cpp index 0494c2c37a..6bce92143e 100644 --- a/libs/widgets/KoToolBoxDocker.cpp +++ b/libs/widgets/KoToolBoxDocker.cpp @@ -1,67 +1,80 @@ /* * Copyright (c) 2005-2009 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * * 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 "KoToolBoxDocker_p.h" #include "KoToolBox_p.h" +#include "KoToolBoxScrollArea_p.h" #include #include KoToolBoxDocker::KoToolBoxDocker(KoToolBox *toolBox) : QDockWidget(i18n("Toolbox")) , m_toolBox(toolBox) + , m_scrollArea(new KoToolBoxScrollArea(toolBox, this)) { setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); - setWidget(toolBox); + setWidget(m_scrollArea); connect(this, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(updateToolBoxOrientation(Qt::DockWidgetArea))); connect(this, SIGNAL(topLevelChanged(bool)), this, SLOT(updateFloating(bool))); KoDockWidgetTitleBar* titleBar = new KoDockWidgetTitleBar(this); titleBar->setTextVisibilityMode(KoDockWidgetTitleBar::TextCanBeInvisible); titleBar->setToolTip(i18n("Tools")); setTitleBarWidget(titleBar); } void KoToolBoxDocker::setCanvas(KoCanvasBase *canvas) { Q_UNUSED(canvas); } void KoToolBoxDocker::unsetCanvas() { } +void KoToolBoxDocker::resizeEvent(QResizeEvent *event) +{ + QDockWidget::resizeEvent(event); + if (isFloating()) { + if (m_scrollArea->width() > m_scrollArea->height()) { + m_scrollArea->setOrientation(Qt::Horizontal); + } else { + m_scrollArea->setOrientation(Qt::Vertical); + } + } +} + void KoToolBoxDocker::updateToolBoxOrientation(Qt::DockWidgetArea area) { if (area == Qt::TopDockWidgetArea || area == Qt::BottomDockWidgetArea) { - m_toolBox->setOrientation(Qt::Horizontal); + m_scrollArea->setOrientation(Qt::Horizontal); } else { - m_toolBox->setOrientation(Qt::Vertical); + m_scrollArea->setOrientation(Qt::Vertical); } - m_toolBox->setFloating(area == Qt::NoDockWidgetArea); } void KoToolBoxDocker::updateFloating(bool v) { m_toolBox->setFloating(v); } diff --git a/libs/widgets/KoToolBoxDocker_p.h b/libs/widgets/KoToolBoxDocker_p.h index a302cda128..02287cb004 100644 --- a/libs/widgets/KoToolBoxDocker_p.h +++ b/libs/widgets/KoToolBoxDocker_p.h @@ -1,52 +1,56 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (c) 2005-2008 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * * 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_TOOLBOX_DOCKER_H_ #define _KO_TOOLBOX_DOCKER_H_ #include #include class KoCanvasBase; class KoToolBox; +class KoToolBoxScrollArea; class KoToolBoxDocker : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: explicit KoToolBoxDocker(KoToolBox *toolBox); /// reimplemented from KoCanvasObserverBase void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; QString observerName() override { return "KoToolBoxDocker"; } +protected: + void resizeEvent(QResizeEvent *event); protected Q_SLOTS: void updateToolBoxOrientation(Qt::DockWidgetArea area); void updateFloating(bool); private: KoToolBox *m_toolBox; + KoToolBoxScrollArea *m_scrollArea; }; #endif // _KO_TOOLBOX_DOCKER_H_ diff --git a/libs/widgets/KoToolBoxLayout_p.h b/libs/widgets/KoToolBoxLayout_p.h index bb8740e088..4616808711 100644 --- a/libs/widgets/KoToolBoxLayout_p.h +++ b/libs/widgets/KoToolBoxLayout_p.h @@ -1,404 +1,441 @@ /* * Copyright (c) 2005-2009 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * * 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_TOOLBOX_LAYOUT_H_ #define _KO_TOOLBOX_LAYOUT_H_ #include #include #include #include #include #include #include #include class SectionLayout : public QLayout { public: explicit SectionLayout(QWidget *parent) : QLayout(parent), m_orientation(Qt::Vertical) { } ~SectionLayout() override { qDeleteAll( m_items ); m_items.clear(); } void addButton(QAbstractButton *button, int priority) { addChildWidget(button); if (m_priorities.values().contains(priority)) { qWarning() << "Button" << button << "has a conflicting priority"; } m_priorities.insert(button, priority); int index = 1; Q_FOREACH (QWidgetItem *item, m_items) { if (m_priorities.value(static_cast(item->widget())) > priority) break; index++; } m_items.insert(index-1, new QWidgetItem(button)); } QSize sizeHint() const override { Q_ASSERT(0); return QSize(); } void addItem(QLayoutItem*) override { Q_ASSERT(0); } QLayoutItem* itemAt(int i) const override { if (m_items.count() <= i) return 0; return m_items.at(i); } QLayoutItem* takeAt(int i) override { return m_items.takeAt(i); } int count() const override { return m_items.count(); } void setGeometry (const QRect &rect) override { int x = 0; int y = 0; const QSize &size = buttonSize(); if (m_orientation == Qt::Vertical) { foreach (QWidgetItem* w, m_items) { if (w->isEmpty()) continue; w->widget()->setGeometry(QRect(x, y, size.width(), size.height())); x += size.width(); if (x + size.width() > rect.width()) { x = 0; y += size.height(); } } } else { foreach (QWidgetItem* w, m_items) { if (w->isEmpty()) continue; w->widget()->setGeometry(QRect(x, y, size.width(), size.height())); y += size.height(); if (y + size.height() > rect.height()) { x += size.width(); y = 0; } } } } void setButtonSize(const QSize size) { m_buttonSize = size; } const QSize &buttonSize() const { return m_buttonSize; } void setOrientation (Qt::Orientation orientation) { m_orientation = orientation; } private: QSize m_buttonSize; QMap m_priorities; QList m_items; Qt::Orientation m_orientation; }; class Section : public QWidget { public: enum SeparatorFlag { SeparatorTop = 0x0001,/* SeparatorBottom = 0x0002, SeparatorRight = 0x0004,*/ SeparatorLeft = 0x0008 }; Q_DECLARE_FLAGS(Separators, SeparatorFlag) explicit Section(QWidget *parent = 0) : QWidget(parent), m_layout(new SectionLayout(this)) { setLayout(m_layout); // Re-enable this when we need to debug the section layout again. // setAutoFillBackground(true); // static int i = 0; // switch(i) { // case 0: // setStyleSheet("background-color:red"); // break; // case 1: // setStyleSheet("background-color:blue"); // break; // case 2: // setStyleSheet("background-color:green"); // break; // case 3: // setStyleSheet("background-color:yellow"); // break; // case 4: // setStyleSheet("background-color:white"); // break; // case 5: // setStyleSheet("background-color:gray"); // break; // case 6: // setStyleSheet("background-color:lime"); // break; // case 7: // setStyleSheet("background-color:silver"); // break; // case 8: // setStyleSheet("background-color:purple"); // break; // default: // setStyleSheet("background-color:maroon"); // break; // } // i++; } void addButton(QAbstractButton *button, int priority) { m_layout->addButton(button, priority); } void setName(const QString &name) { setObjectName(name); m_name = name; } QString name() const { return m_name; } void setButtonSize(QSize size) { m_layout->setButtonSize(size); } QSize iconSize() const { return m_layout->buttonSize(); } int visibleButtonCount() const { int count = 0; for(int i = m_layout->count()-1; i >= 0; --i) { if (! static_cast (m_layout->itemAt(i))->isEmpty()) ++count; } return count; } void setSeparator(Separators separators) { m_separators = separators; } Separators separators() const { return m_separators; } void setOrientation (Qt::Orientation orientation) { m_layout->setOrientation(orientation); } protected: private: SectionLayout *m_layout; QString m_name; Separators m_separators; }; Q_DECLARE_OPERATORS_FOR_FLAGS(Section::Separators) class KoToolBoxLayout : public QLayout { public: explicit KoToolBoxLayout(QWidget *parent) - : QLayout(parent), m_orientation(Qt::Vertical), m_currentHeight(0) + : QLayout(parent) + , m_orientation(Qt::Vertical) { setSpacing(6); } ~KoToolBoxLayout() override { qDeleteAll( m_sections ); m_sections.clear(); } QSize sizeHint() const override { - if (m_sections.isEmpty()) - return QSize(); - QSize oneIcon = static_cast (m_sections[0]->widget())->iconSize(); - return oneIcon; + // Prefer showing two rows/columns by default + QSize twoIcons = static_cast (m_sections[0]->widget())->iconSize() * 2; + const int length = doLayout(QRect(QPoint(), twoIcons), false); + if (m_orientation == Qt::Vertical) { + return QSize(twoIcons.width(), length); + } else { + return QSize(length, twoIcons.height()); + } } QSize minimumSize() const override { - QSize s = sizeHint(); - if (m_orientation == Qt::Vertical) { - s.setHeight(m_currentHeight); - } else { - s.setWidth(m_currentHeight); - } - return s; + if (m_sections.isEmpty()) + return QSize(); + QSize oneIcon = static_cast (m_sections[0]->widget())->iconSize(); + return oneIcon; } void addSection(Section *section) { addChildWidget(section); QList::iterator iterator = m_sections.begin(); int defaults = 2; // skip the first two as they are the 'main' and 'dynamic' sections. while (iterator != m_sections.end()) { if (--defaults < 0 && static_cast ((*iterator)->widget())->name() > section->name()) break; ++iterator; } m_sections.insert(iterator, new QWidgetItem(section)); } void addItem(QLayoutItem*) override { Q_ASSERT(0); // don't let anything else be added. (code depends on this!) } QLayoutItem* itemAt(int i) const override { - if (m_sections.count() >= i) - return 0; - return m_sections.at(i); + return m_sections.value(i); } QLayoutItem* takeAt(int i) override { return m_sections.takeAt(i); } int count() const override { return m_sections.count(); } void setGeometry (const QRect &rect) override + { + QLayout::setGeometry(rect); + doLayout(rect, true); + } + + bool hasHeightForWidth() const override + { + // return true; + return m_orientation == Qt::Vertical; + } + + int heightForWidth(int width) const override + { + if (m_orientation == Qt::Vertical) { + const int height = doLayout(QRect(0, 0, width, 0), false); + return height; + } else { + #if 0 + const int iconHeight = static_cast (m_sections[0]->widget())->iconSize().height(); + for (int i = 1; i <= 10; i++) { + const int testWidth = doLayout(QRect(0, 0, 0, iconHeight * i), false); + if (testWidth <= width) { + return iconHeight * i; + } + } + // Return a huge height + return 65535; + #endif + return -1; + } + } + + /** + * For calculating the width from height by KoToolBoxScrollArea. + * QWidget doesn't actually support trading width for height, so it needs to + * be handled specificly. + */ + int widthForHeight(int height) const + { + if (m_orientation == Qt::Horizontal) { + const int width = doLayout(QRect(0, 0, 0, height), false); + return width; + } else { + return -1; + } + } + + void setOrientation (Qt::Orientation orientation) + { + m_orientation = orientation; + invalidate(); + } + +private: + int doLayout(const QRect &rect, bool notDryRun) const { // nothing to do? - if (m_sections.isEmpty()) - { - m_currentHeight = 0; - return; + if (m_sections.isEmpty()) { + return 0; } // the names of the variables assume a vertical orientation, // but all calculations are done based on the real orientation + const bool isVertical = m_orientation == Qt::Vertical; const QSize iconSize = static_cast (m_sections.first()->widget())->iconSize(); - const int maxWidth = (m_orientation == Qt::Vertical) ? rect.width() : rect.height(); + const int maxWidth = isVertical ? rect.width() : rect.height(); // using min 1 as width to e.g. protect against div by 0 below - const int iconWidth = qMax(1, (m_orientation == Qt::Vertical) ? iconSize.width() : iconSize.height()); - const int iconHeight = qMax(1, (m_orientation == Qt::Vertical) ? iconSize.height() : iconSize.width()); + const int iconWidth = qMax(1, isVertical ? iconSize.width() : iconSize.height()); + const int iconHeight = qMax(1, isVertical ? iconSize.height() : iconSize.width()); const int maxColumns = qMax(1, (maxWidth / iconWidth)); int x = 0; int y = 0; bool firstSection = true; foreach (QWidgetItem *wi, m_sections) { Section *section = static_cast (wi->widget()); - // Since sections can overlap (if a section occupies two rows, and there - // is space on the second row for all of the next section, the next section - // will be placed overlapping with the previous section), it's important that - // later sections will be higher in the widget z-order than previous - // sections, so raise it. - section->raise(); const int buttonCount = section->visibleButtonCount(); if (buttonCount == 0) { // move out of view, not perfect TODO: better solution - section->setGeometry(1000, 1000, 0, 0); + if (notDryRun) { + section->setGeometry(1000, 1000, 0, 0); + } continue; } // rows needed for the buttons (calculation gets the ceiling value of the plain div) const int neededRowCount = ((buttonCount-1) / maxColumns) + 1; - const int availableButtonCount = (maxWidth - x + 1) / iconWidth; if (firstSection) { firstSection = false; - } else if (buttonCount > availableButtonCount) { + } else { // start on a new row, set separator x = 0; y += iconHeight + spacing(); - const Section::Separators separator = - (m_orientation == Qt::Vertical) ? Section::SeparatorTop : Section::SeparatorLeft; - section->setSeparator( separator ); - } else { - // append to last row, set separators (on first row only to the left side) - const bool isFirstRow = (y == 0); - const Section::Separators separators = - isFirstRow ? - ((m_orientation == Qt::Vertical) ? Section::SeparatorLeft : Section::SeparatorTop) : - (Section::SeparatorTop | Section::SeparatorLeft); - section->setSeparator( separators ); + if (notDryRun){ + const Section::Separators separator = + isVertical ? Section::SeparatorTop : Section::SeparatorLeft; + section->setSeparator( separator ); + } } - const int usedColumns = qMin(buttonCount, maxColumns); - if (m_orientation == Qt::Vertical) { - section->setGeometry(x, y, - usedColumns * iconWidth, neededRowCount * iconHeight); - } else { - section->setGeometry(y, x, - neededRowCount * iconHeight, usedColumns * iconWidth); + if (notDryRun) { + const int usedColumns = qMin(buttonCount, maxColumns); + if (isVertical) { + section->setGeometry(x, y, + usedColumns * iconWidth, neededRowCount * iconHeight); + } else { + section->setGeometry(y, x, + neededRowCount * iconHeight, usedColumns * iconWidth); + } } // advance by the icons in the last row const int lastRowColumnCount = buttonCount - ((neededRowCount-1) * maxColumns); x += (lastRowColumnCount * iconWidth) + spacing(); // advance by all but the last used row y += (neededRowCount - 1) * iconHeight; } // cache total height (or width), adding the iconHeight for the current row - m_currentHeight = y + iconHeight; + return y + iconHeight; } - void setOrientation (Qt::Orientation orientation) - { - m_orientation = orientation; - invalidate(); - } - -private: QList m_sections; Qt::Orientation m_orientation; - mutable int m_currentHeight; }; #endif diff --git a/libs/widgets/KoToolBoxScrollArea_p.h b/libs/widgets/KoToolBoxScrollArea_p.h new file mode 100644 index 0000000000..f99914081f --- /dev/null +++ b/libs/widgets/KoToolBoxScrollArea_p.h @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2018 Alvin Wong + * + * 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_TOOLBOX_SCROLL_AREA_H +#define KO_TOOLBOX_SCROLL_AREA_H + +#include "KoToolBox_p.h" +#include "KoToolBoxLayout_p.h" + +#include +#include +#include +#include +#include + +class KoToolBoxScrollArea : public QScrollArea +{ + Q_OBJECT +public: + KoToolBoxScrollArea(KoToolBox *toolBox, QWidget *parent) + : QScrollArea(parent) + , m_toolBox(toolBox) + , m_orientation(Qt::Vertical) + , m_scrollPrev(new QToolButton(this)) + , m_scrollNext(new QToolButton(this)) + { + setFrameShape(QFrame::NoFrame); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + m_toolBox->setOrientation(m_orientation); + setWidget(m_toolBox); + + m_scrollPrev->setAutoRepeat(true); + m_scrollPrev->setAutoFillBackground(true); + m_scrollPrev->setFocusPolicy(Qt::NoFocus); + connect(m_scrollPrev, &QToolButton::clicked, this, &KoToolBoxScrollArea::doScrollPrev); + m_scrollNext->setAutoRepeat(true); + m_scrollNext->setAutoFillBackground(true); + m_scrollNext->setFocusPolicy(Qt::NoFocus); + connect(m_scrollNext, &QToolButton::clicked, this, &KoToolBoxScrollArea::doScrollNext); + + QScroller::grabGesture(viewport(), QScroller::LeftMouseButtonGesture); + QScroller *scroller = QScroller::scroller(viewport()); + QScrollerProperties sp = scroller->scrollerProperties(); + + sp.setScrollMetric(QScrollerProperties::MaximumVelocity, 0.0); + sp.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor, 0.1); + sp.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 0.1); + sp.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.0); + sp.setScrollMetric(QScrollerProperties::OvershootScrollTime, 0.4); + + scroller->setScrollerProperties(sp); + } + + void setOrientation(Qt::Orientation orientation) + { + if (orientation == m_orientation) { + return; + } + m_orientation = orientation; + m_toolBox->setOrientation(orientation); + layoutItems(); + } + + Qt::Orientation orientation() const + { + return m_orientation; + } + + QSize minimumSizeHint() const override + { + return m_toolBox->minimumSizeHint(); + } + + QSize sizeHint() const override + { + return m_toolBox->sizeHint(); + } + +protected: + bool event(QEvent *event) override + { + if (event->type() == QEvent::LayoutRequest) { + // LayoutRequest can be triggered by icon changes, so resize the toolbox + layoutItems(); + // The toolbox might have changed the sizeHint and minimumSizeHint + updateGeometry(); + } + return QScrollArea::event(event); + } + + void resizeEvent(QResizeEvent *event) override + { + layoutItems(); + QScrollArea::resizeEvent(event); + updateScrollButtons(); + } + + void wheelEvent(QWheelEvent *event) override + { + if (m_orientation == Qt::Vertical) { + QApplication::sendEvent(verticalScrollBar(), event); + } else { + QApplication::sendEvent(horizontalScrollBar(), event); + } + } + + void scrollContentsBy(int dx, int dy) override + { + QScrollArea::scrollContentsBy(dx, dy); + updateScrollButtons(); + } + +private Q_SLOTS: + void doScrollPrev() + { + if (m_orientation == Qt::Vertical) { + verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub); + } else { + horizontalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub); + } + } + + void doScrollNext() + { + if (m_orientation == Qt::Vertical) { + verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd); + } else { + horizontalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd); + } + } + +private: + int scrollButtonWidth() const + { + QStyleOption opt; + opt.init(this); + return style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, &opt, this); + } + + void layoutItems() + { + const KoToolBoxLayout *l = m_toolBox->toolBoxLayout(); + QSize newSize = viewport()->size(); + if (m_orientation == Qt::Vertical) { + newSize.setHeight(l->heightForWidth(newSize.width())); + } else { + newSize.setWidth(l->widthForHeight(newSize.height())); + } + m_toolBox->resize(newSize); + + updateScrollButtons(); + } + + void updateScrollButtons() + { + // We move the scroll buttons outside the widget rect instead of setting + // the visibility, because setting the visibility triggers a relayout + // of QAbstractScrollArea, which occasionally causes an offset bug when + // QScroller performs the overshoot animation. (Overshoot is done by + // moving the viewport widget, but the viewport position is reset during + // a relayout.) + const int scrollButtonWidth = this->scrollButtonWidth(); + if (m_orientation == Qt::Vertical) { + m_scrollPrev->setArrowType(Qt::UpArrow); + m_scrollPrev->setEnabled(verticalScrollBar()->value() != verticalScrollBar()->minimum()); + if (m_scrollPrev->isEnabled()) { + m_scrollPrev->setGeometry(0, 0, width(), scrollButtonWidth); + } else { + m_scrollPrev->setGeometry(-width(), 0, width(), scrollButtonWidth); + } + m_scrollNext->setArrowType(Qt::DownArrow); + m_scrollNext->setEnabled(verticalScrollBar()->value() != verticalScrollBar()->maximum()); + if (m_scrollNext->isEnabled()) { + m_scrollNext->setGeometry(0, height() - scrollButtonWidth, width(), scrollButtonWidth); + } else { + m_scrollNext->setGeometry(-width(), height() - scrollButtonWidth, width(), scrollButtonWidth); + } + } else { + m_scrollPrev->setArrowType(Qt::LeftArrow); + m_scrollPrev->setEnabled(horizontalScrollBar()->value() != horizontalScrollBar()->minimum()); + if (m_scrollPrev->isEnabled()) { + m_scrollPrev->setGeometry(0, 0, scrollButtonWidth, height()); + } else { + m_scrollPrev->setGeometry(0, -height(), scrollButtonWidth, height()); + } + m_scrollNext->setArrowType(Qt::RightArrow); + m_scrollNext->setEnabled(horizontalScrollBar()->value() != horizontalScrollBar()->maximum()); + if (m_scrollNext->isEnabled()) { + m_scrollNext->setGeometry(width() - scrollButtonWidth, 0, scrollButtonWidth, height()); + } else { + m_scrollNext->setGeometry(width() - scrollButtonWidth, -height(), scrollButtonWidth, height()); + } + } + } + + KoToolBox *m_toolBox; + Qt::Orientation m_orientation; + + QToolButton *m_scrollPrev; + QToolButton *m_scrollNext; +}; + +#endif // KO_TOOLBOX_SCROLL_AREA_H diff --git a/libs/widgets/KoToolBox_p.h b/libs/widgets/KoToolBox_p.h index bebf1a3508..7f0527ff3e 100644 --- a/libs/widgets/KoToolBox_p.h +++ b/libs/widgets/KoToolBox_p.h @@ -1,117 +1,113 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (c) 2005-2008 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * * 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_TOOLBOX_H_ #define _KO_TOOLBOX_H_ #include #include #include #include class KoCanvasController; class KoShapeLayer; +class KoToolBoxLayout; /** * KoToolBox is a dock widget that can order tools according to type and * priority. * * The ToolBox is a container for tool buttons which are themselves * divided into sections. * * Adding buttons using addButton() will allow you to show those buttons. You should connect * the button to your handling method yourself. * * The unique property of this toolbox is that it can be shown horizontal as well as vertical, * rotating in a smart way to show the buttons optimally. * @see KoToolManager */ class KoToolBox : public QWidget { Q_OBJECT public: /// constructor explicit KoToolBox(); ~KoToolBox() override; public Q_SLOTS: /** * Using the buttongroup id passed in addButton() you can set the new active button. * If the id does not resolve to a visible button, this call is ignored. * @param canvas the currently active canvas. * @param id an id to identify the button to activate. */ void setActiveTool(KoCanvasController *canvas, int id); /** * Show only the dynamic buttons that have a code from parameter codes. * The toolbox allows buttons to be optionally registered with a visibilityCode. This code * can be passed here and all buttons that have that code are shown. All buttons that * have another visibility code registered are hidden. * @param canvas the currently active canvas. * @param codes a list of all the codes to show. */ void setButtonsVisible(const QList &codes); /// Set the orientation of the layout to @p orientation void setOrientation(Qt::Orientation orientation); void setFloating(bool v); + KoToolBoxLayout *toolBoxLayout() const; + private: /** * Add a button to the toolbox. * The buttons should all be added before the first showing since adding will not really add * them to the UI until setup() is called. * * @param toolAction the action of the tool * @see setup() */ void addButton(KoToolAction *toolAction); private Q_SLOTS: void setCurrentLayer(const KoCanvasController *canvas, const KoShapeLayer* newLayer); /// add a tool post-initialization. The tool will also be activated. void toolAdded(KoToolAction *toolAction, KoCanvasController *canvas); - /// resize the toolbox to show the icons without any gap at the edge - void adjustToFit(); - - /// unlocks the with after adjustToFit - void resizeUnlock(); - /// set the icon size for all the buttons void slotContextIconSize(); protected: void paintEvent(QPaintEvent *event) override; - void resizeEvent(QResizeEvent* event) override; void contextMenuEvent(QContextMenuEvent *event) override; private: class Private; Private * const d; }; #endif // _KO_TOOLBOX_H_ diff --git a/packaging/linux/snap/setup/gui/krita.desktop b/packaging/linux/snap/setup/gui/krita.desktop index 79ec66398b..a0385abada 100755 --- a/packaging/linux/snap/setup/gui/krita.desktop +++ b/packaging/linux/snap/setup/gui/krita.desktop @@ -1,133 +1,136 @@ [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 %U GenericName=Digital Painting +GenericName[ar]=رسم رقميّ GenericName[bs]=Digitalno Bojenje GenericName[ca]=Dibuix digital GenericName[ca@valencia]=Dibuix digital GenericName[cs]=Digitální malování GenericName[da]=Digital tegning GenericName[de]=Digitales Malen GenericName[el]=Ψηφιακή ζωγραφική GenericName[en_GB]=Digital Painting GenericName[es]=Pintura digital GenericName[et]=Digitaalne joonistamine GenericName[eu]=Pintura digitala GenericName[fi]=Digitaalimaalaus GenericName[fr]=Peinture numérique GenericName[gl]=Debuxo dixital GenericName[hu]=Digitális festészet GenericName[ia]=Pintura Digital GenericName[is]=Stafræn málun GenericName[it]=Pittura digitale GenericName[ja]=デジタルペインティング GenericName[kk]=Цифрлық сурет салу GenericName[lt]=Skaitmeninis piešimas GenericName[mr]=डिजिटल पेंटिंग GenericName[nb]=Digital maling GenericName[nl]=Digitaal schilderen GenericName[pl]=Cyfrowe malowanie GenericName[pt]=Pintura Digital GenericName[pt_BR]=Pintura digital GenericName[ru]=Цифровая живопись GenericName[sk]=Digitálne maľovanie GenericName[sl]=Digitalno slikanje GenericName[sv]=Digital målning GenericName[tr]=Sayısal Boyama GenericName[ug]=سىفىرلىق رەسىم سىزغۇ GenericName[uk]=Цифрове малювання GenericName[x-test]=xxDigital Paintingxx GenericName[zh_CN]=数字绘画 GenericName[zh_TW]=數位繪畫 MimeType=application/x-krita;image/openraster;application/x-krita-paintoppreset; Comment=Pixel-based image manipulation program for the Calligra Suite +Comment[ar]=برنامج لتعديل الصّور البكسليّة لطقم «كاليغرا» Comment[ca]=Programa de manipulació d'imatges basades en píxels per a la Suite Calligra Comment[ca@valencia]=Programa de manipulació d'imatges basades en píxels per a la Suite Calligra Comment[de]=Pixelbasiertes Bildbearbeitungsprogramm für die Calligra-Suite Comment[el]=Πρόγραμμα επεξεργασίας εικόνας με βάση εικονοστοιχεία για το Calligra Stage Comment[en_GB]=Pixel-based image manipulation program for the Calligra Suite Comment[es]=Programa de manipulación de imágenes basado en píxeles para la suite Calligra Comment[et]=Calligra pikslipõhine pilditöötluse rakendus Comment[gl]=Programa da colección de Calligra para a manipulación de imaxes baseadas en píxeles. Comment[is]=Myndvinnsluforrit fyrir Calligra-forritavöndulinn Comment[it]=Programma di manipolazione delle immagini basato su pixel per Calligra Suite Comment[nl]=Afbeeldingsbewerkingsprogramma gebaseerd op pixels voor de Calligra Suite Comment[pl]=Program do obróbki obrazów na poziomie pikseli dla Pakietu Calligra Comment[pt]='Plugin' de manipulação de imagens em pixels para o Calligra Stage Comment[pt_BR]=Programa de manipulação de imagens baseado em pixels para o Calligra Suite Comment[ru]=Программа редактирования пиксельной анимации для the Calligra Suite Comment[sk]=Program na manipuláciu s pixelmi pre Calligra Suite Comment[sv]=Bildpunktsbaserat bildbehandlingsprogram för Calligra-sviten Comment[tr]=Calligra Suite için Pixel tabanlı görüntü düzenleme programı Comment[uk]=Програма для роботи із растровими зображеннями для комплексу програм Calligra Comment[x-test]=xxPixel-based image manipulation program for the Calligra Suitexx Comment[zh_CN]=Calligra 套件的位图图像处理程序 Comment[zh_TW]=Calligra 套件中基於像素的影像處理程式 Type=Application Icon=${SNAP}/meta/gui/calligrakrita.png Categories=Qt;KDE;Graphics; X-KDE-NativeMimeType=application/x-krita X-KDE-ExtraNativeMimeTypes= StartupNotify=true X-Krita-Version=28 diff --git a/packaging/windows/build.bat b/packaging/windows/build.bat deleted file mode 100644 index 68fc4649e4..0000000000 --- a/packaging/windows/build.bat +++ /dev/null @@ -1,34 +0,0 @@ -rem -rem Follow the instructions in 3rdparty/README.md to setup Qt and the -rem build dirs and checkout krita -rem - -set PATH=c:\dev\i\bin\;c:\dev\i\lib;%PATH% -cd c:\dev\b -cmake ..\krita\3rdparty -DEXTERNALS_DOWNLOAD_DIR=/dev/d -DINSTALL_ROOT=/dev/i -G "Visual Studio 14 Win64" -cmake --build . --config RelWithDebInfo --target ext_patch -cmake --build . --config RelWithDebInfo --target ext_png2ico -cmake --build . --config RelWithDebInfo --target ext_pthreads -cmake --build . --config RelWithDebInfo --target ext_boost -cmake --build . --config RelWithDebInfo --target ext_eigen3 -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_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 -rem cmake --build . --config RelWithDebInfo --target ext_freetype -cmake --build . --config RelWithDebInfo --target ext_ocio -cmake --build . --config RelWithDebInfo --target ext_openexr -cmake --build . --config RelWithDebInfo --target ext_exiv2 -cmake --build . --config RelWithDebInfo --target ext_kwindowsystem - -REM cmake --build . --config RelWithDebInfo --target ext_poppler -cd c:\dev\build -cmake ..\krita -G"Visual Studio 14 Win64" -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 - -copy c:\dev\i\lib\*.dll c:\dev\i\bin -copy "C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x64\*.dll" c:\dev\i\bin diff --git a/packaging/windows/build_krita.bat b/packaging/windows/build_krita.bat deleted file mode 100644 index de52681230..0000000000 --- a/packaging/windows/build_krita.bat +++ /dev/null @@ -1,20 +0,0 @@ -set DLLTOOL_EXE=c:\TDM-GCC-64\x86_64-w64-mingw32\bin\dlltool.exe -set MINGW_GCC_BIN=c:\TDM-GCC-64\bin\ -set BUILDROOT=c:\dev -set BUILDDIR_INSTALL=%BUILDROOT%\i -set PATH=c:\dev\i\bin;c:\dev\i\lib;c:\python35;%MINGW_GCC_BIN%;%PATH% -cd c:\dev\build2 - -set /P pkg_root=Insert krita source location: - -if [%pkg_root%] == [] ( -echo You entered an empty name! -pause -exit /B -) - -cmake ..\%pkg_root% -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 -DFOUNDATION_BUILD=1 - -mingw32-make -j4 install - -cd .. diff --git a/plugins/dockers/CMakeLists.txt b/plugins/dockers/CMakeLists.txt index 6d0ab83441..0840f5c6be 100644 --- a/plugins/dockers/CMakeLists.txt +++ b/plugins/dockers/CMakeLists.txt @@ -1,32 +1,33 @@ add_subdirectory(defaultdockers) add_subdirectory(smallcolorselector) add_subdirectory(specificcolorselector) add_subdirectory(digitalmixer) add_subdirectory(advancedcolorselector) add_subdirectory(presetdocker) add_subdirectory(historydocker) add_subdirectory(channeldocker) add_subdirectory(imagedocker) add_subdirectory(artisticcolorselector) add_subdirectory(tasksetdocker) add_subdirectory(compositiondocker) add_subdirectory(patterndocker) add_subdirectory(griddocker) add_subdirectory(arrangedocker) if(HAVE_OCIO) add_subdirectory(lut) endif() add_subdirectory(overview) add_subdirectory(palettedocker) add_subdirectory(colorslider) add_subdirectory(animation) add_subdirectory(presethistory) +add_subdirectory(svgcollectiondocker) add_subdirectory(histogram) if(HAVE_QT_QUICK) add_subdirectory(touchdocker) option(ENABLE_CPU_THROTTLE "Build the CPU Throttle Docker" OFF) if (ENABLE_CPU_THROTTLE) add_subdirectory(throttle) endif() endif() diff --git a/plugins/dockers/lut/CMakeLists.txt b/plugins/dockers/lut/CMakeLists.txt index 4b00e46112..01844be146 100644 --- a/plugins/dockers/lut/CMakeLists.txt +++ b/plugins/dockers/lut/CMakeLists.txt @@ -1,24 +1,21 @@ -if (NOT WIN32 AND NOT APPLE) - add_subdirectory(tests) -endif() +add_subdirectory(tests) include_directories(SYSTEM ${OCIO_INCLUDE_DIR} ) set(KRITA_LUTDOCKER_SOURCES lutdocker.cpp lutdocker_dock.cpp ocio_display_filter.cpp black_white_point_chooser.cpp ) ki18n_wrap_ui(KRITA_LUTDOCKER_SOURCES wdglut.ui ) add_library(kritalutdocker MODULE ${KRITA_LUTDOCKER_SOURCES}) -generate_export_header(kritalutdocker BASE_NAME kritalutdocker) target_link_libraries(kritalutdocker kritaui ${Boost_SYSTEM_LIBRARY} ${OCIO_LIBRARIES}) install(TARGETS kritalutdocker DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/dockers/lut/ocio_display_filter.h b/plugins/dockers/lut/ocio_display_filter.h index 96c2c2e9d6..48d896a4c0 100644 --- a/plugins/dockers/lut/ocio_display_filter.h +++ b/plugins/dockers/lut/ocio_display_filter.h @@ -1,96 +1,95 @@ /* * Copyright (c) 2012 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef OCIO_DISPLAY_FILTER_H #define OCIO_DISPLAY_FILTER_H -#include "kritalutdocker_export.h" #include #include #include #include #include "kis_exposure_gamma_correction_interface.h" namespace OCIO = OCIO_NAMESPACE; enum OCIO_CHANNEL_SWIZZLE { LUMINANCE, RGBA, R, G, B, A }; -class KRITALUTDOCKER_EXPORT OcioDisplayFilter : public KisDisplayFilter +class OcioDisplayFilter : public KisDisplayFilter { Q_OBJECT public: explicit OcioDisplayFilter(KisExposureGammaCorrectionInterface *interface, QObject *parent = 0); ~OcioDisplayFilter(); void filter(quint8 *pixels, quint32 numPixels); void approximateInverseTransformation(quint8 *pixels, quint32 numPixels); void approximateForwardTransformation(quint8 *pixels, quint32 numPixels); bool useInternalColorManagement() const; bool lockCurrentColorVisualRepresentation() const; void setLockCurrentColorVisualRepresentation(bool value); bool updateShader(); template bool updateShaderImpl(F *f); KisExposureGammaCorrectionInterface *correctionInterface() const; virtual QString program() const; GLuint lutTexture() const; void updateProcessor(); OCIO::ConstConfigRcPtr config; const char *inputColorSpaceName; const char *displayDevice; const char *view; const char *look; OCIO_CHANNEL_SWIZZLE swizzle; float exposure; float gamma; float blackPoint; float whitePoint; bool forceInternalColorManagement; private: OCIO::ConstProcessorRcPtr m_processor; OCIO::ConstProcessorRcPtr m_revereseApproximationProcessor; OCIO::ConstProcessorRcPtr m_forwardApproximationProcessor; KisExposureGammaCorrectionInterface *m_interface; bool m_lockCurrentColorVisualRepresentation; QString m_program; GLuint m_lut3dTexID; QVector m_lut3d; QString m_lut3dcacheid; QString m_shadercacheid; bool m_shaderDirty; }; #endif // OCIO_DISPLAY_FILTER_H diff --git a/plugins/dockers/lut/tests/CMakeLists.txt b/plugins/dockers/lut/tests/CMakeLists.txt index bc68b39399..4fecc309b1 100644 --- a/plugins/dockers/lut/tests/CMakeLists.txt +++ b/plugins/dockers/lut/tests/CMakeLists.txt @@ -1,11 +1,14 @@ macro_add_unittest_definitions() -include_directories(${CMAKE_SOURCE_DIR}/sdk/tests) +include_directories(${CMAKE_SOURCE_DIR}/sdk/tests ../) include_directories(SYSTEM ${OCIO_INCLUDE_DIR} ) ########### next target ############### -krita_add_broken_unit_test(kis_ocio_display_filter_test.cpp ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp +krita_add_broken_unit_test(kis_ocio_display_filter_test.cpp + ../black_white_point_chooser.cpp + ../ocio_display_filter.cpp + ${CMAKE_SOURCE_DIR}/sdk/tests/stroke_testing_utils.cpp TEST_NAME krita-ocio-KisOcioDisplayFilterTest - LINK_LIBRARIES kritaimage kritaui kritalutdocker ${OCIO_LIBRARIES} KF5::I18n Qt5::Test) + LINK_LIBRARIES kritaimage kritaui ${OCIO_LIBRARIES} KF5::I18n Qt5::Test) diff --git a/plugins/dockers/svgcollectiondocker/CMakeLists.txt b/plugins/dockers/svgcollectiondocker/CMakeLists.txt new file mode 100644 index 0000000000..28acd4ce0f --- /dev/null +++ b/plugins/dockers/svgcollectiondocker/CMakeLists.txt @@ -0,0 +1,17 @@ +project(calligradockers) + +set(calligradockers_SRCS + SvgSymbolCollectionDocker.cpp + Plugin.cpp +) + +ki18n_wrap_ui(calligradockers_SRCS + WdgSvgCollection.ui +) + + +add_library(kritasvgcollectiondocker MODULE ${calligradockers_SRCS}) + +target_link_libraries(kritasvgcollectiondocker kritawidgets kritaui) + +install(TARGETS kritasvgcollectiondocker DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/flake/textshape/ReferencesToolFactory.h b/plugins/dockers/svgcollectiondocker/Plugin.cpp similarity index 64% rename from plugins/flake/textshape/ReferencesToolFactory.h rename to plugins/dockers/svgcollectiondocker/Plugin.cpp index 893513ab5c..b7fc829f46 100644 --- a/plugins/flake/textshape/ReferencesToolFactory.h +++ b/plugins/dockers/svgcollectiondocker/Plugin.cpp @@ -1,34 +1,36 @@ /* This file is part of the KDE project - * Copyright (C) 2011 C. Boemann + * + * Copyright (C) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ +#include "Plugin.h" +#include "SvgSymbolCollectionDocker.h" + +#include -#ifndef REFERENCESTOOLFACTORY_H -#define REFERENCESTOOLFACTORY_H +#include -#include +K_PLUGIN_FACTORY_WITH_JSON(PluginFactory, "svgcollectiondocker.json", registerPlugin();) -class ReferencesToolFactory : public KoToolFactoryBase +Plugin::Plugin(QObject *parent, const QVariantList &) + : QObject(parent) { -public: - ReferencesToolFactory(); - ~ReferencesToolFactory() override; + KoDockRegistry::instance()->add(new SvgSymbolCollectionDockerFactory()); +} - KoToolBase *createTool(KoCanvasBase *canvas) override; -}; +#include -#endif diff --git a/plugins/flake/vectorshape/VectorShapePlugin.h b/plugins/dockers/svgcollectiondocker/Plugin.h similarity index 77% rename from plugins/flake/vectorshape/VectorShapePlugin.h rename to plugins/dockers/svgcollectiondocker/Plugin.h index dcc1258748..96fc6177cf 100644 --- a/plugins/flake/vectorshape/VectorShapePlugin.h +++ b/plugins/dockers/svgcollectiondocker/Plugin.h @@ -1,37 +1,34 @@ /* This file is part of the KDE project - * - * Copyright (C) 2009 Inge Wallin + * Copyright (C) 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 PLUGIN_H +#define PLUGIN_H -#ifndef VECTORSHAPE_PLUGIN_H -#define VECTORSHAPE_PLUGIN_H - -// Qt #include #include -class VectorShapePlugin : public QObject +class Plugin : public QObject { Q_OBJECT public: - VectorShapePlugin(QObject *parent, const QVariantList &); - ~VectorShapePlugin() override {} + Plugin(QObject *parent, const QVariantList &); + ~Plugin() override {} }; #endif diff --git a/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.cpp b/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.cpp new file mode 100644 index 0000000000..ef199f0284 --- /dev/null +++ b/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.cpp @@ -0,0 +1,249 @@ +/* This file is part of the KDE project + * Copyright (C) 2008 Peter Simonsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "SvgSymbolCollectionDocker.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "kis_icon_utils.h" +#include +#include + +#include "ui_WdgSvgCollection.h" + +#include + +// +// SvgCollectionModel +// +SvgCollectionModel::SvgCollectionModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +QVariant SvgCollectionModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() > m_symbolCollection->symbols().count()) { + return QVariant(); + } + + switch (role) { + case Qt::ToolTipRole: + return m_symbolCollection->symbols()[index.row()]->title; + + case Qt::DecorationRole: + { + QPixmap px = QPixmap::fromImage(m_symbolCollection->symbols()[index.row()]->icon()); + QIcon icon(px); + return icon; + } + case Qt::UserRole: + return m_symbolCollection->symbols()[index.row()]->id; + + case Qt::DisplayRole: + return m_symbolCollection->symbols()[index.row()]->title; + + default: + return QVariant(); + } + + return QVariant(); +} + +int SvgCollectionModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_symbolCollection->symbols().count(); +} + +QMimeData *SvgCollectionModel::mimeData(const QModelIndexList &indexes) const +{ + if (indexes.isEmpty()) { + return 0; + } + + QModelIndex index = indexes.first(); + + if (!index.isValid()) { + return 0; + } + + if (m_symbolCollection->symbols().isEmpty()) { + return 0; + } + + QList shapes; + shapes.append(m_symbolCollection->symbols()[index.row()]->shape); + KoDrag drag; + drag.setSvg(shapes); + QMimeData *mimeData = drag.mimeData(); + + return mimeData; +} + +QStringList SvgCollectionModel::mimeTypes() const +{ + return QStringList() << SHAPETEMPLATE_MIMETYPE << "image/svg+xml"; +} + +Qt::ItemFlags SvgCollectionModel::flags(const QModelIndex &index) const +{ + if (index.isValid()) { + return QAbstractListModel::flags(index) | Qt::ItemIsDragEnabled; + } + return QAbstractListModel::flags(index); +} + +Qt::DropActions SvgCollectionModel::supportedDragActions() const +{ + return Qt::CopyAction; +} + +void SvgCollectionModel::setSvgSymbolCollectionResource(KoSvgSymbolCollectionResource *resource) +{ + m_symbolCollection = resource; +} + + + +// +// SvgSymbolCollectionDockerFactory +// + +SvgSymbolCollectionDockerFactory::SvgSymbolCollectionDockerFactory() + : KoDockFactoryBase() +{ +} + +QString SvgSymbolCollectionDockerFactory::id() const +{ + return QString("SvgSymbolCollectionDocker"); +} + +QDockWidget *SvgSymbolCollectionDockerFactory::createDockWidget() +{ + SvgSymbolCollectionDocker *docker = new SvgSymbolCollectionDocker(); + + return docker; +} + +// +// SvgSymbolCollectionDocker +// + +SvgSymbolCollectionDocker::SvgSymbolCollectionDocker(QWidget *parent) + : QDockWidget(parent) + , m_wdgSvgCollection(new Ui_WdgSvgCollection()) +{ + setWindowTitle(i18n("Vector Libraries")); + QWidget* mainWidget = new QWidget(this); + setWidget(mainWidget); + m_wdgSvgCollection->setupUi(mainWidget); + + connect(m_wdgSvgCollection->cmbCollections, SIGNAL(activated(int)), SLOT(collectionActivated(int))); + + KoResourceServer *svgCollectionProvider = KoResourceServerProvider::instance()->svgSymbolCollectionServer(); + Q_FOREACH(KoSvgSymbolCollectionResource *r, svgCollectionProvider->resources()) { + m_wdgSvgCollection->cmbCollections->addSqueezedItem(r->name()); + SvgCollectionModel *model = new SvgCollectionModel(); + model->setSvgSymbolCollectionResource(r); + m_models.append(model); + } + + m_wdgSvgCollection->listCollection->setDragEnabled(true); + m_wdgSvgCollection->listCollection->setDragDropMode(QAbstractItemView::DragOnly); + m_wdgSvgCollection->listCollection->setSelectionMode(QListView::SingleSelection); + + + // thumbnail icon changer + QMenu* configureMenu = new QMenu(this); + configureMenu->setStyleSheet("margin: 6px"); + m_wdgSvgCollection->vectorPresetsConfigureButton->setIcon(KisIconUtils::loadIcon("configure")); + m_wdgSvgCollection->vectorPresetsConfigureButton->setPopupMode(QToolButton::InstantPopup); + + + + // add horizontal slider for changing the icon size + m_iconSizeSlider = new QSlider(this); + m_iconSizeSlider->setOrientation(Qt::Horizontal); + m_iconSizeSlider->setRange(20, 80); + m_iconSizeSlider->setValue(20); // defaults to small icon size + m_iconSizeSlider->setMinimumHeight(20); + m_iconSizeSlider->setMinimumWidth(40); + m_iconSizeSlider->setTickInterval(10); + + + QWidgetAction *sliderAction= new QWidgetAction(this); + sliderAction->setDefaultWidget(m_iconSizeSlider); + + configureMenu->addSection(i18n("Icon Size")); + configureMenu->addAction(sliderAction); + + m_wdgSvgCollection->vectorPresetsConfigureButton->setMenu(configureMenu); + connect(m_iconSizeSlider, SIGNAL(sliderReleased()), this, SLOT(slotSetIconSize())); // resizing while sliding is too heavy of an operation + + + KConfigGroup cfg = KSharedConfig::openConfig()->group("SvgSymbolCollection"); + int i = cfg.readEntry("currentCollection", 0); + if (i > m_wdgSvgCollection->cmbCollections->count()) { + i = 0; + } + m_wdgSvgCollection->cmbCollections->setCurrentIndex(i); + collectionActivated(i); +} + +void SvgSymbolCollectionDocker::slotSetIconSize() +{ + m_wdgSvgCollection->listCollection->setIconSize(QSize(m_iconSizeSlider->value(),m_iconSizeSlider->value())); +} + +void SvgSymbolCollectionDocker::setCanvas(KoCanvasBase *canvas) +{ + setEnabled(canvas != 0); +} + +void SvgSymbolCollectionDocker::unsetCanvas() +{ + setEnabled(false); +} + +void SvgSymbolCollectionDocker::collectionActivated(int index) +{ + if (index < m_models.size()) { + KConfigGroup cfg = KSharedConfig::openConfig()->group("SvgSymbolCollection"); + cfg.writeEntry("currentCollection", index); + m_wdgSvgCollection->listCollection->setModel(m_models[index]); + } + +} diff --git a/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.h b/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.h new file mode 100644 index 0000000000..b148088dbb --- /dev/null +++ b/plugins/dockers/svgcollectiondocker/SvgSymbolCollectionDocker.h @@ -0,0 +1,88 @@ +/* This file is part of the KDE project + * Copyright (C) 2017 Boudewijn Rempt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef SVGSYMBOLCOLLECTIONDOCKER_H +#define SVGSYMBOLCOLLECTIONDOCKER_H + +#include +#include +#include +#include +#include + +#include +#include + +#include "ui_WdgSvgCollection.h" + +class KoSvgSymbolCollectionResource; + +class SvgCollectionModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit SvgCollectionModel(QObject *parent = 0); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QMimeData *mimeData(const QModelIndexList &indexes) const override; + QStringList mimeTypes() const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + Qt::DropActions supportedDragActions() const; +public: + void setSvgSymbolCollectionResource(KoSvgSymbolCollectionResource *resource); +private: + KoSvgSymbolCollectionResource *m_symbolCollection; +}; + + +class SvgSymbolCollectionDockerFactory : public KoDockFactoryBase +{ +public: + SvgSymbolCollectionDockerFactory(); + + QString id() const override; + QDockWidget *createDockWidget() override; + DockPosition defaultDockPosition() const override + { + return DockRight; + } +}; + +class SvgSymbolCollectionDocker : public QDockWidget, public KoCanvasObserverBase +{ + Q_OBJECT +public: + + explicit SvgSymbolCollectionDocker(QWidget *parent = 0); + + /// reimplemented + void setCanvas(KoCanvasBase *canvas) override; + void unsetCanvas() override; + +private Q_SLOTS: + + void collectionActivated(int index); + void slotSetIconSize(); +private: + + Ui_WdgSvgCollection *m_wdgSvgCollection; + QVector m_models; + QSlider* m_iconSizeSlider; +}; + +#endif //KOSHAPECOLLECTIONDOCKER_H diff --git a/plugins/dockers/svgcollectiondocker/WdgSvgCollection.ui b/plugins/dockers/svgcollectiondocker/WdgSvgCollection.ui new file mode 100644 index 0000000000..9cd5b9f4a7 --- /dev/null +++ b/plugins/dockers/svgcollectiondocker/WdgSvgCollection.ui @@ -0,0 +1,42 @@ + + + WdgSvgCollection + + + + 0 + 0 + 291 + 288 + + + + + + + + + + + + ... + + + + + + + + + + + + + SqueezedComboBox + QComboBox +
squeezedcombobox.h
+
+
+ + +
diff --git a/plugins/dockers/svgcollectiondocker/svgcollectiondocker.json b/plugins/dockers/svgcollectiondocker/svgcollectiondocker.json new file mode 100644 index 0000000000..f7ee406724 --- /dev/null +++ b/plugins/dockers/svgcollectiondocker/svgcollectiondocker.json @@ -0,0 +1,10 @@ +{ + "Id": "SVG Collection Docker", + "Type": "Service", + "X-Flake-MinVersion": "28", + "X-Flake-PluginVersion": "28", + "X-KDE-Library": "kritasvgcollectiondocker", + "X-KDE-ServiceTypes": [ + "Calligra/Dock" + ] +} diff --git a/plugins/extensions/pykrita/plugin/plugins/assignprofiledialog/kritapykrita_assignprofiledialog.desktop b/plugins/extensions/pykrita/plugin/plugins/assignprofiledialog/kritapykrita_assignprofiledialog.desktop index 877a2b6d5e..fc53026742 100644 --- a/plugins/extensions/pykrita/plugin/plugins/assignprofiledialog/kritapykrita_assignprofiledialog.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/assignprofiledialog/kritapykrita_assignprofiledialog.desktop @@ -1,44 +1,46 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=assignprofiledialog X-Python-2-Compatible=false Name=Assign Profile to Image +Name[ar]=إسناد اللاحات إلى الصّور Name[ca]=Assigna un perfil a una imatge Name[ca@valencia]=Assigna un perfil a una imatge Name[cs]=Přiřadit obrázku profil Name[el]=Αντιστοίχιση προφίλ σε εικόνα Name[en_GB]=Assign Profile to Image Name[es]=Asignar perfil a imagen Name[gl]=Asignar un perfil á imaxe Name[is]=Úthluta litasniði á myndina Name[it]=Assegna profilo a immagine Name[nl]=Profiel aan afbeelding toekennen Name[pl]=Przypisz profil do obrazu Name[pt]=Atribuir um Perfil à Imagem Name[pt_BR]=Atribuir perfil a imagem Name[sv]=Tilldela profil till bild Name[tr]=Görüntüye Profil Ata Name[uk]=Призначити профіль до зображення Name[x-test]=xxAssign Profile to Imagexx Name[zh_CN]=应用配置到图像 Name[zh_TW]=指定設定檔到圖像 Comment=Assign a profile to an image without converting it. +Comment[ar]=أسنِد لاحة إلى صورة دون تحويلها. Comment[ca]=Assigna un perfil a una imatge sense convertir-la. Comment[ca@valencia]=Assigna un perfil a una imatge sense convertir-la. Comment[el]=Αντιστοιχίζει ένα προφίλ σε μια εικόνα χωρίς μετατροπή. Comment[en_GB]=Assign a profile to an image without converting it. Comment[es]=Asignar un perfil a una imagen sin convertirla. Comment[gl]=Asignar un perfil a unha imaxe sen convertela. Comment[is]=Úthluta litasniði á myndina án þess að umbreyta henni. Comment[it]=Assegna un profilo a un'immagine senza convertirla. Comment[nl]=Een profiel aan een afbeelding toekennen zonder het te converteren. Comment[pl]=Przypisz profil do obrazu bez jego przekształcania. Comment[pt]=Atribui um perfil à imagem sem a converter. Comment[pt_BR]=Atribui um perfil para uma imagem sem convertê-la. Comment[sv]=Tilldela en profil till en bild utan att konvertera den. Comment[tr]=Bir görüntüye, görüntüyü değiştirmeden bir profil ata. Comment[uk]=Призначити профіль до зображення без його перетворення. Comment[x-test]=xxAssign a profile to an image without converting it.xx Comment[zh_CN]=为图像设定配置而无需转换它。 Comment[zh_TW]=將設定檔指定給圖像,而不進行轉換。 diff --git a/plugins/extensions/pykrita/plugin/plugins/colorspace/kritapykrita_colorspace.desktop b/plugins/extensions/pykrita/plugin/plugins/colorspace/kritapykrita_colorspace.desktop index 5bab7cf2c9..84696ae7bf 100644 --- a/plugins/extensions/pykrita/plugin/plugins/colorspace/kritapykrita_colorspace.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/colorspace/kritapykrita_colorspace.desktop @@ -1,46 +1,48 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=colorspace X-Python-2-Compatible=false Name=Color Space +Name[ar]=الفضاء اللونيّ Name[ca]=Espai de color Name[ca@valencia]=Espai de color Name[cs]=Barevný prostor Name[de]=Farbraum Name[el]=Χρωματικός χώρος Name[en_GB]=Colour Space Name[es]=Espacio de color Name[gl]=Espazo de cores Name[is]=Litrýmd Name[it]=Spazio dei colori Name[nl]=Kleurruimte Name[pl]=Przestrzeń barw Name[pt]=Espaço de Cores Name[pt_BR]=Espaço de cores Name[sk]=Farebný priestor Name[sv]=Färgrymd Name[tr]=Renk Aralığı Name[uk]=Простір кольорів Name[x-test]=xxColor Spacexx Name[zh_CN]=色彩空间 Name[zh_TW]=色彩空間 Comment=Plugin to change color space to selected documents +Comment[ar]=ملحقة لتغيير الفضاء اللونيّ في المستندات المحدّدة Comment[ca]=Un connector per canviar l'espai de color dels documents seleccionats Comment[ca@valencia]=Un connector per canviar l'espai de color dels documents seleccionats Comment[cs]=Modul pro změnu rozsahu barvy pro vybrané dokumenty Comment[el]=Πρόσθετο αλλαγής χρωματικού χώρου σε επιλεγμένα έγγραφα Comment[en_GB]=Plugin to change colour space to selected documents Comment[es]=Complemento para cambiar el espacio de color de los documentos seleccionados Comment[gl]=Complemento para cambiar o espazo de cores dos documentos seleccionados. Comment[it]=Estensione per cambiare lo spazio dei colori ai documenti selezionati Comment[nl]=Plug-in om kleurruimte in geselecteerde documenten te wijzigen Comment[pl]=Wtyczka do zmiany przestrzeni barw wybranych dokumentów Comment[pt]='Plugin' para mudar o espaço de cores do documento seleccionado Comment[pt_BR]=Plug-in para alterar o espaço de cores em documentos selecionados Comment[sv]=Insticksprogram för att ändra färgrymd för valda dokument Comment[tr]=Seçili belgede renk aralığını değiştirmek için eklenti Comment[uk]=Додаток для зміни простору кольорів у позначених документах Comment[x-test]=xxPlugin to change color space to selected documentsxx Comment[zh_CN]=将颜色空间改为所选文档的插件 Comment[zh_TW]=用於變更色彩空間為選定文件的外掛程式 diff --git a/plugins/extensions/pykrita/plugin/plugins/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop b/plugins/extensions/pykrita/plugin/plugins/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop index 77f43ee2a3..a8a9fc4d98 100644 --- a/plugins/extensions/pykrita/plugin/plugins/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop @@ -1,41 +1,43 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=comics_project_management_tools X-Python-2-Compatible=false Name=Comics Project Management Tools +Name[ar]=أدوات إدارة المشاريع الهزليّة Name[ca]=Eines per a la gestió dels projectes de còmics Name[ca@valencia]=Eines per a la gestió dels projectes de còmics Name[cs]=Nástroje pro správu projektů komixů Name[el]=Εργαλεία διαχείρισης έργων ιστοριών σε εικόνες Name[en_GB]=Comics Project Management Tools Name[es]=Herramientas de gestión de proyectos de cómics Name[is]=Verkefnisstjórn teiknimyndasögu Name[it]=Strumenti per la gestione dei progetti di fumetti Name[nl]=Hulpmiddelen voor projectbeheer van strips Name[pl]=Narzędzia do zarządzania projektami komiksów Name[pt]=Ferramentas de Gestão de Projectos de Banda Desenhada Name[sv]=Projekthanteringsverktyg för tecknade serier Name[tr]=Çizgi Roman Projesi Yönetimi Araçları Name[uk]=Інструменти для керування проектами коміксів Name[x-test]=xxComics Project Management Toolsxx Name[zh_CN]=漫画项目管理工具 Name[zh_TW]=漫畫專案管理工具 Comment=Tools for managing comics. +Comment[ar]=أدوات لإدارة الهزليّات. Comment[ca]=Eines per a gestionar els còmics. Comment[ca@valencia]=Eines per a gestionar els còmics. Comment[cs]=Nástroje pro správu komixů. Comment[el]=Εργαλεία για τη διαχείριση ιστοριών σε εικόνες. Comment[en_GB]=Tools for managing comics. Comment[es]=Herramientas para gestionar cómics. Comment[is]=Verkfæri til að stýra gerð teiknimyndasögu. Comment[it]=Strumenti per la gestione dei fumetti. Comment[nl]=Hulpmiddelen voor beheer van strips. Comment[pl]=Narzędzie do zarządzania komiksami. Comment[pt]=Ferramentas para gerir bandas desenhadas. Comment[sv]=Verktyg för att hantera tecknade serier. Comment[tr]=Çizgi romanları yönetmek için araçlar. Comment[uk]=Інструменти для керування коміксами Comment[x-test]=xxTools for managing comics.xx Comment[zh_CN]=管理漫画的工具。 Comment[zh_TW]=管理漫畫的工具。 diff --git a/plugins/extensions/pykrita/plugin/plugins/documenttools/kritapykrita_documenttools.desktop b/plugins/extensions/pykrita/plugin/plugins/documenttools/kritapykrita_documenttools.desktop index 81f5e75a9d..2ecc914e77 100644 --- a/plugins/extensions/pykrita/plugin/plugins/documenttools/kritapykrita_documenttools.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/documenttools/kritapykrita_documenttools.desktop @@ -1,43 +1,45 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=documenttools X-Python-2-Compatible=false Name=Document Tools +Name[ar]=أدوات المستندات Name[ca]=Eines de document Name[ca@valencia]=Eines de document Name[cs]=Dokumentové nástroje Name[el]=Εργαλεία για έγγραφα Name[en_GB]=Document Tools Name[es]=Herramientas de documentos Name[gl]=Ferramentas de documentos Name[it]=Strumenti per i documenti Name[nl]=Documenthulpmiddelen Name[pl]=Narzędzia dokumentu Name[pt]=Ferramentas de Documentos Name[pt_BR]=Ferramentas de documento Name[sv]=Dokumentverktyg Name[tr]=Belge Araçları Name[uk]=Засоби документа Name[x-test]=xxDocument Toolsxx Name[zh_CN]=文档工具 Name[zh_TW]=文件工具 Comment=Plugin to manipulate properties of selected documents +Comment[ar]=ملحقة لتعديل خصائص المستندات المحدّدة Comment[ca]=Un connector per manipular propietats dels documents seleccionats Comment[ca@valencia]=Un connector per manipular propietats dels documents seleccionats Comment[cs]=Modul pro správu vlastností vybraných dokumentů Comment[el]=Πρόσθετο χειρισμού ιδιοτήτων σε επιλεγμένα έγγραφα Comment[en_GB]=Plugin to manipulate properties of selected documents Comment[es]=Complemento para manipular las propiedades de los documentos seleccionados Comment[gl]=Complemento para manipular as propiedades dos documentos seleccionados. Comment[it]=Estensione per manipolare le proprietà dei documenti selezionati Comment[nl]=Plug-in om eigenschappen van geselecteerde documenten te manipuleren Comment[pl]=Wtyczka do zmiany właściwości wybranych dokumentów Comment[pt]='Plugin' para manipular as propriedades dos documentos seleccionados Comment[pt_BR]=Plug-in para manipular as propriedades de documentos selecionados Comment[sv]=Insticksprogram för att ändra egenskaper för valda dokument Comment[tr]=Seçili belgelerin özelliklerini değiştirmek için eklenti Comment[uk]=Додаток для керування властивостями позначених документів Comment[x-test]=xxPlugin to manipulate properties of selected documentsxx Comment[zh_CN]=操纵选中文档的属性的插件 Comment[zh_TW]=用於修改所選文件屬性的外掛程式 diff --git a/plugins/extensions/pykrita/plugin/plugins/exportlayers/kritapykrita_exportlayers.desktop b/plugins/extensions/pykrita/plugin/plugins/exportlayers/kritapykrita_exportlayers.desktop index ca09a2730e..c240ab246c 100644 --- a/plugins/extensions/pykrita/plugin/plugins/exportlayers/kritapykrita_exportlayers.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/exportlayers/kritapykrita_exportlayers.desktop @@ -1,45 +1,47 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=exportlayers X-Python-2-Compatible=false Name=Export Layers +Name[ar]=تصدير الطّبقات Name[ca]=Exportació de capes Name[ca@valencia]=Exportació de capes Name[cs]=Exportovat vrstvy Name[de]=Ebenen exportieren Name[el]=Εξαγωγή επιπέδων Name[en_GB]=Export Layers Name[es]=Exportar capas Name[gl]=Exportar as capas Name[is]=Flytja út lög Name[it]=Esporta livelli Name[nl]=Lagen exporteren Name[pl]=Eksportuj warstwy Name[pt]=Exportar as Camadas Name[pt_BR]=Exportar camadas Name[sv]=Exportera lager Name[tr]=Katmanları Dışa Aktar Name[uk]=Експортувати шари Name[x-test]=xxExport Layersxx Name[zh_CN]=导出图层 Name[zh_TW]=匯出圖層 Comment=Plugin to export layers from a document +Comment[ar]=ملحقة لتصدير الطّبقات من مستند Comment[ca]=Un connector per exportar capes d'un document Comment[ca@valencia]=Un connector per exportar capes d'un document Comment[cs]=Modul pro export vrstev z dokumentu Comment[el]=Πρόσθετο εξαγωγής επιπέδων από έγγραφο Comment[en_GB]=Plugin to export layers from a document Comment[es]=Complemento para exportar las capas de un documento Comment[gl]=Complemento para exportar as capas dun documento. Comment[it]=Estensione per esportare i livelli da un documento Comment[nl]=Plug-in om lagen uit een document te exporteren Comment[pl]=Wtyczka do eksportowania warstw z dokumentu Comment[pt]='Plugin' para exportar as camadas de um documento Comment[pt_BR]=Plug-in para exportar as camadas de um documento Comment[sv]=Insticksprogram för att exportera lager från ett dokument Comment[tr]=Belgenin katmanlarını dışa aktarmak için eklenti Comment[uk]=Додаток для експортування шарів з документа Comment[x-test]=xxPlugin to export layers from a documentxx Comment[zh_CN]=从文档导出图层的插件 Comment[zh_TW]=用於從文件匯出圖層的外掛程式 diff --git a/plugins/extensions/pykrita/plugin/plugins/filtermanager/kritapykrita_filtermanager.desktop b/plugins/extensions/pykrita/plugin/plugins/filtermanager/kritapykrita_filtermanager.desktop index 553efe6335..032a074c67 100644 --- a/plugins/extensions/pykrita/plugin/plugins/filtermanager/kritapykrita_filtermanager.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/filtermanager/kritapykrita_filtermanager.desktop @@ -1,45 +1,47 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=filtermanager X-Python-2-Compatible=false Name=Filter Manager +Name[ar]=مدير المرشّحات Name[ca]=Gestor de filtres Name[ca@valencia]=Gestor de filtres Name[cs]=Správce filtrů Name[de]=Filterverwaltung Name[el]=Διαχειριστής φίλτρων Name[en_GB]=Filter Manager Name[es]=Gestor de filtros Name[gl]=Xestor de filtros Name[it]=Gestore dei filtri Name[nl]=Beheerder van filters Name[pl]=Zarządzanie filtrami Name[pt]=Gestor de Filtros Name[pt_BR]=Gerenciador de filtros Name[sv]=Filterhantering Name[tr]=Süzgeç Yöneticisi Name[uk]=Керування фільтрами Name[x-test]=xxFilter Managerxx Name[zh_CN]=滤镜管理器 Name[zh_TW]=濾鏡管理器 Comment=Plugin to filters management +Comment[ar]=ملحقة إدارة المرشّحات Comment[ca]=Un connector per gestionar filtres Comment[ca@valencia]=Un connector per gestionar filtres Comment[cs]=Modul pro správu filtrů Comment[de]=Modul zum Verwalten von Filtern Comment[el]=Πρόσθετο για τη διαχείριση φίλτρων Comment[en_GB]=Plugin to filters management Comment[es]=Complemento para la gestión de filtros Comment[gl]=Complemento para a xestión de filtros. Comment[it]=Estensione per la gestione dei filtri Comment[nl]=Plug-in voor beheer van filters Comment[pl]=Wtyczka do zarządzania filtrami Comment[pt]='Plugin' para a gestão de filtros Comment[pt_BR]=Plug-in para gerenciamento de filtros Comment[sv]=Insticksprogram för filterhantering Comment[tr]=Süzgeç yönetimi için eklenti Comment[uk]=Додаток для керування фільтрами Comment[x-test]=xxPlugin to filters managementxx Comment[zh_CN]=滤镜管理插件 Comment[zh_TW]=用於濾鏡管理的外掛程式 diff --git a/plugins/extensions/pykrita/plugin/plugins/hello/kritapykrita_hello.desktop b/plugins/extensions/pykrita/plugin/plugins/hello/kritapykrita_hello.desktop index f16d59988e..1087f774f0 100644 --- a/plugins/extensions/pykrita/plugin/plugins/hello/kritapykrita_hello.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/hello/kritapykrita_hello.desktop @@ -1,47 +1,49 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=hello X-Python-2-Compatible=false Name=Hello World +Name[ar]=مرحبًا يا عالم Name[ca]=Hola món Name[ca@valencia]=Hola món Name[cs]=Hello World Name[de]=Hallo Welt Name[el]=Hello World Name[en_GB]=Hello World Name[es]=Hola mundo Name[fr]=Bonjour tout le monde Name[gl]=Ola mundo Name[is]=Halló Heimur Name[it]=Ciao mondo Name[nl]=Hallo wereld Name[pl]=Witaj świecie Name[pt]=Olá Mundo Name[pt_BR]=Olá mundo Name[sk]=Ahoj svet Name[sv]=Hello World Name[tr]=Merhaba Dünya Name[uk]=Привіт, світе Name[x-test]=xxHello Worldxx Name[zh_CN]=Hello World Name[zh_TW]=你好,世界 Comment=Basic plugin to test PyKrita +Comment[ar]=ملحقة أساسيّة لاختبار PyKrita Comment[ca]=Connector bàsic per provar el PyKrita Comment[ca@valencia]=Connector bàsic per provar el PyKrita Comment[cs]=Základní modul pro testování PyKrita Comment[el]=Βασικό πρόσθετο δοκιμής PyKrita Comment[en_GB]=Basic plugin to test PyKrita Comment[es]=Complemento básico para probar PyKrita Comment[gl]=Complemento básico para probar PyKrita. Comment[it]=Estensione di base per provare PyKrita Comment[nl]=Basisplug-in om PyKrita te testen Comment[pl]=Podstawowa wtyczka do wypróbowania PyKrity Comment[pt]='Plugin' básico para testar o PyKrita Comment[pt_BR]=Plug-in básico para testar o PyKrita Comment[sv]=Enkelt insticksprogram för att utprova PyKrita Comment[tr]=PyKrita'yı test etmek için temel eklenti Comment[uk]=Базовий додаток для тестування PyKrita Comment[x-test]=xxBasic plugin to test PyKritaxx Comment[zh_CN]=测试 PyKrita 的基本插件 Comment[zh_TW]=測試 PyKrita 的基本外掛程式 diff --git a/plugins/extensions/pykrita/plugin/plugins/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop b/plugins/extensions/pykrita/plugin/plugins/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop index 94cc436a9a..bcde1265ae 100644 --- a/plugins/extensions/pykrita/plugin/plugins/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop @@ -1,39 +1,41 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=lastdocumentsdocker X-Python-2-Compatible=false Name=Last Documents Docker +Name[ar]=رصيف بآخر المستندات Name[ca]=Acoblador dels darrers documents Name[ca@valencia]=Acoblador dels darrers documents Name[el]=Προσάρτηση τελευταίων εγγράφοων Name[en_GB]=Last Documents Docker Name[es]=Panel de últimos documentos Name[gl]=Doca dos últimos documentos Name[it]=Area di aggancio Ultimi documenti Name[nl]=Vastzetter van laatste documenten Name[pl]=Dok ostatnich dokumentów Name[pt]=Área dos Últimos Documentos Name[sv]=Dockningsfönster för senaste dokument Name[tr]=Son Belgeler Doku Name[uk]=Бічна панель останніх документів Name[x-test]=xxLast Documents Dockerxx Name[zh_CN]=最近文档坞窗 Name[zh_TW]=「最後文件」面板 Comment=A Python-based docker for show thumbnails to last ten documents +Comment[ar]=رصيف بِ‍«پيثون» لعرض مصغّرات آخر ١٠ مستندات مفتوحة Comment[ca]=Un acoblador basant en el Python per mostrar miniatures dels darrers deu documents Comment[ca@valencia]=Un acoblador basant en el Python per mostrar miniatures dels darrers deu documents Comment[el]=Ένα εργαλείο προσάρτησης σε Python για την εμφάνιση επισκοπήσεων των δέκα τελευταίων εγγράφων Comment[en_GB]=A Python-based docker for show thumbnails to last ten documents Comment[es]=Un panel basado en Python para mostrar miniaturas de los últimos diez documentos Comment[gl]=Unha doca baseada en Python para mostrar as miniaturas dos últimos dez documentos. Comment[it]=Un'area di aggancio basata su Python per mostrare miniature degli ultimi dieci documenti. Comment[nl]=Een op Python gebaseerde vastzetter om miniaturen te tonen naar de laatste tien documenten. Comment[pl]=Dok oparty na pythonie do wyświetlania miniatur ostatnich dziesięciu dokumentów Comment[pt]=Uma área acoplável, feita em Python, para mostrar as miniaturas dos últimos dez documentos Comment[sv]=Ett Python-baserat dockningsfönster för att visa miniatyrbilder för de tio senaste dokumenten Comment[tr]=Son on belgenin küçük resmini göstermek için Python-tabanlı bir dok Comment[uk]=Бічна панель на основі Python для показу мініатюр останніх десяти документів Comment[x-test]=xxA Python-based docker for show thumbnails to last ten documentsxx Comment[zh_CN]=基于 Python 的坞窗,展示最近十个文档的缩略图 Comment[zh_TW]=基於 Python 的面板,用於顯示最後 10 個文件縮圖 diff --git a/plugins/extensions/pykrita/plugin/plugins/palette_docker/kritapykrita_palette_docker.desktop b/plugins/extensions/pykrita/plugin/plugins/palette_docker/kritapykrita_palette_docker.desktop index 9ebce52433..16b37485e3 100644 --- a/plugins/extensions/pykrita/plugin/plugins/palette_docker/kritapykrita_palette_docker.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/palette_docker/kritapykrita_palette_docker.desktop @@ -1,42 +1,44 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=palette_docker X-Python-2-Compatible=false Name=Palette docker +Name[ar]=رصيف اللوحات Name[ca]=Acoblador de paletes Name[ca@valencia]=Acoblador de paletes Name[cs]=Dok palet Name[de]=Paletten-Docker Name[el]=Προσάρτηση παλέτας Name[en_GB]=Palette docker Name[es]=Panel de paleta Name[gl]=Doca de paleta Name[is]=Tengikví fyrir litaspjald Name[it]=Area di aggancio della tavolozza Name[nl]=Vastzetter van palet Name[pl]=Dok palety Name[pt]=Área acoplável da paleta Name[sv]=Dockningsfönster för palett Name[tr]=Palet doku Name[uk]=Панель палітри Name[x-test]=xxPalette dockerxx Name[zh_CN]=调色板坞窗 Name[zh_TW]=「調色盤」面板 Comment=A Python-based docker to edit color palettes. +Comment[ar]=رصيف بِ‍«پيثون» لتحرير لوحات الألوان. Comment[ca]=Un acoblador basant en el Python per editar paletes de colors. Comment[ca@valencia]=Un acoblador basant en el Python per editar paletes de colors. Comment[el]=Ένα εργαλείο προσάρτησης σε Python για την επεξεργασία παλετών χρώματος. Comment[en_GB]=A Python-based docker to edit colour palettes. Comment[es]=Un panel basado en Python para editar paletas de colores. Comment[gl]=Unha doca baseada en Python para editar paletas de cores. Comment[it]=Un'area di aggancio per modificare le tavolozze di colori basata su Python. Comment[nl]=Een op Python gebaseerde vastzetter om kleurpaletten te bewerken. Comment[pl]=Dok oparty na pythonie do edytowania palet barw. Comment[pt]=Uma área acoplável, feita em Python, para editar paletas de cores. Comment[sv]=Ett Python-baserat dockningsfönster för att redigera färgpaletter. Comment[tr]=Renk paletlerini düzenlemek için Python-tabanlı bir dok. Comment[uk]=Бічна панель для редагування палітр кольорів на основі Python. Comment[x-test]=xxA Python-based docker to edit color palettes.xx Comment[zh_CN]=基于 Python 的调色板编辑坞窗 Comment[zh_TW]=基於 Python 的面板,用於編輯調色盤。 diff --git a/plugins/extensions/pykrita/plugin/plugins/quick_settings_docker/kritapykrita_quick_settings_docker.desktop b/plugins/extensions/pykrita/plugin/plugins/quick_settings_docker/kritapykrita_quick_settings_docker.desktop index b75374cb31..7ab9920e41 100644 --- a/plugins/extensions/pykrita/plugin/plugins/quick_settings_docker/kritapykrita_quick_settings_docker.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/quick_settings_docker/kritapykrita_quick_settings_docker.desktop @@ -1,40 +1,42 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=quick_settings_docker X-Python-2-Compatible=false Name=Quick Settings Docker +Name[ar]=رصيف بإعدادات سريعة Name[ca]=Acoblador d'arranjament ràpid Name[ca@valencia]=Acoblador d'arranjament ràpid Name[cs]=Dok pro rychlé nastavení Name[el]=Προσάρτηση γρήγορων ρυθμίσεων Name[en_GB]=Quick Settings Docker Name[es]=Panel de ajustes rápidos Name[gl]=Doca de configuración rápida Name[it]=Area di aggancio delle impostazioni rapide Name[nl]=Docker voor snelle instellingen Name[pl]=Dok szybkich ustawień Name[pt]=Área de Configuração Rápida Name[sv]=Dockningspanel med snabbinställningar Name[tr]=Hızlı Ayarlar Doku Name[uk]=Панель швидких параметрів Name[x-test]=xxQuick Settings Dockerxx Name[zh_CN]=快速设置坞窗 Name[zh_TW]=「快速設定」面板 Comment=A Python-based docker for quickly changing brush size and opacity. +Comment[ar]=رصيف بِ‍«پيثون» لتغيير حجم الفرشاة وشفافيّتها بسرعة. Comment[ca]=Un acoblador basant en el Python per canviar ràpidament la mida del pinzell i l'opacitat. Comment[ca@valencia]=Un acoblador basant en el Python per canviar ràpidament la mida del pinzell i l'opacitat. Comment[el]=Ένα εργαλείο προσάρτησης σε Python για γρήγορη αλλαγή του μεγέθους και της αδιαφάνειας του πινέλου. Comment[en_GB]=A Python-based docker for quickly changing brush size and opacity. Comment[es]=Un panel basado en Python para cambiar rápidamente el tamaño y la opacidad del pincel. Comment[gl]=Unha doca baseada en Python para cambiar rapidamente a opacidade e o tamaño dos pinceis. Comment[it]=Un'area di aggancio basata su Python per cambiare rapidamente la dimensione del pennello e l'opacità. Comment[nl]=Een op Python gebaseerde docker voor snel wijzigen van penseelgrootte en dekking. Comment[pl]=Dok oparty na pythonie do szybkiej zmiany rozmiaru i nieprzezroczystości pędzla. Comment[pt]=Uma área acoplável em Python para mudar rapidamente o tamanho e opacidade do pincel. Comment[sv]=En Python-baserad dockningspanel för att snabbt ändra penselstorlek och ogenomskinlighet. Comment[tr]=Fırça boyutunu ve matlığını hızlıca değiştirmek için Python-tabanlı bir dok. Comment[uk]=Панель на основі мови програмування Python для швидкої зміни розміру та непрозорості пензля. Comment[x-test]=xxA Python-based docker for quickly changing brush size and opacity.xx Comment[zh_CN]=基于 Python 的坞窗,用于快速更改笔刷尺寸和透明度。 Comment[zh_TW]=基於 Python 的面板,用於快速變更筆刷尺寸和不透明度。 diff --git a/plugins/extensions/pykrita/plugin/plugins/scriptdocker/kritapykrita_scriptdocker.desktop b/plugins/extensions/pykrita/plugin/plugins/scriptdocker/kritapykrita_scriptdocker.desktop index 58516040d4..90617de68d 100644 --- a/plugins/extensions/pykrita/plugin/plugins/scriptdocker/kritapykrita_scriptdocker.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/scriptdocker/kritapykrita_scriptdocker.desktop @@ -1,40 +1,42 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=scriptdocker X-Python-2-Compatible=false Name=Script Docker +Name[ar]=رصيف سكربتات Name[ca]=Acoblador de scripts Name[ca@valencia]=Acoblador de scripts Name[cs]=Dok skriptu Name[el]=Προσάρτηση σεναρίων Name[en_GB]=Script Docker Name[es]=Panel de guiones Name[gl]=Doca de scripts Name[it]=Area di aggancio degli script Name[nl]=Vastzetten van scripts Name[pl]=Dok skryptów Name[pt]=Área de Programas Name[sv]=Dockningsfönster för skript Name[tr]=Betik Doku Name[uk]=Бічна панель скриптів Name[x-test]=xxScript Dockerxx Name[zh_CN]=脚本坞窗 Name[zh_TW]=「腳本」面板 Comment=A Python-based docker for create actions and point to Python scripts +Comment[ar]=رصيف بِ‍«پيثون» لإنشاء الإجراءات والإشارة إلى سكربتات «پيثون» Comment[ca]=Un acoblador basant en el Python per crear accions i apuntar a scripts del Python Comment[ca@valencia]=Un acoblador basant en el Python per crear accions i apuntar a scripts del Python Comment[el]=Ένα εργαλείο προσάρτησης σε Python για τη δημιουργία ενεργειών και τη δεικτοδότηση σεναρίων σε Python Comment[en_GB]=A Python-based docker for create actions and point to Python scripts Comment[es]=Un panel basado en Python para crear y apuntar a guiones de Python Comment[gl]=Unha doca escrita en Python para crear accións e apuntar a scripts escritos en Python. Comment[it]=Un'area di aggancio basata su Python per creare azioni e scegliere script Python Comment[nl]=Een op Python gebaseerde vastzetter voor aanmaakacties en wijzen naar Python-scripts Comment[pl]=Dok oparty na pythonie do tworzenia działań i punktów dla skryptów pythona Comment[pt]=Uma área acoplável, feita em Python, para criar acções e apontar para programas em Python Comment[sv]=Ett Python-baserat dockningsfönster för att skapa åtgärder och peka ut Python-skript Comment[tr]=Eylemler oluşturmak ve Python betiklerine yönlendirmek için Python-tabanlı bir dok Comment[uk]=Бічна панель для створення дій і керування скриптами на основі Python. Comment[x-test]=xxA Python-based docker for create actions and point to Python scriptsxx Comment[zh_CN]=基于 Python 的坞窗,用于创建动作并指向 Python 脚本 Comment[zh_TW]=基於 Python 的面板,用於建立動作並指向 Python 腳本 diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/scripterdialog.py b/plugins/extensions/pykrita/plugin/plugins/scripter/scripterdialog.py index b467cfd548..5db6647944 100644 --- a/plugins/extensions/pykrita/plugin/plugins/scripter/scripterdialog.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/scripterdialog.py @@ -1,14 +1,14 @@ from PyQt5.QtWidgets import QDialog - +from PyQt5 import QtCore class ScripterDialog(QDialog): def __init__(self, uicontroller, parent=None): super(ScripterDialog, self).__init__(parent) - + self.setWindowFlags(QtCore.Qt.Tool | QtCore.Qt.WindowStaysOnTopHint) self.uicontroller = uicontroller def closeEvent(self, event): self.uicontroller._writeSettings() self.uicontroller._saveSettings() event.accept() diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/runaction/runaction.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/runaction/runaction.py index d5ee09ecf6..22d419bf4d 100644 --- a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/runaction/runaction.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/runaction/runaction.py @@ -1,114 +1,115 @@ from PyQt5.QtWidgets import QAction from PyQt5.QtGui import QIcon, QKeySequence from PyQt5.QtCore import Qt import sys from . import docwrapper import importlib from importlib.machinery import SourceFileLoader import traceback PYTHON33 = sys.version_info.major==3 and sys.version_info.minor==3 PYTHON34 = sys.version_info.major==3 and sys.version_info.minor==4 -EXEC_NAMESPACE = "users_script" # namespace that user scripts will run in +EXEC_NAMESPACE = "__main__" # namespace that user scripts will run in class RunAction(QAction): def __init__(self, scripter, parent=None): super(RunAction, self).__init__(parent) self.scripter = scripter self.editor = self.scripter.uicontroller.editor self.output = self.scripter.uicontroller.findTabWidget('OutPut', 'OutPutTextEdit') self.triggered.connect(self.run) self.setText('Run') self.setToolTip('Run Ctrl+R') self.setIcon(QIcon(':/icons/run.svg')) self.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_R)) @property def parent(self): return 'toolBar' def run(self): """ This method execute python code from an activeDocument (file) or direct from editor (ui_scripter/editor/pythoneditor.py). When executing code from a file, we use importlib to load this module/file and with "users_script" name. That's implementation seeks for a "main()" function in the script. When executing code from editor without creating a file, we compile this script to bytecode and we execute this in an empty scope. That's faster than use exec directly and cleaner, because we are using an empty scope. """ self.scripter.uicontroller.setActiveWidget('OutPut') stdout = sys.stdout stderr = sys.stderr output = docwrapper.DocWrapper(self.output.document()) if (self.editor._documentModified is True): output.write("==== Warning: Script not saved! ====\n") else: output.write("======================================\n") sys.stdout = output sys.stderr = output script = self.editor.document().toPlainText() document = self.scripter.documentcontroller.activeDocument try: if document and self.editor._documentModified is False: spec = importlib.util.spec_from_file_location(EXEC_NAMESPACE, document.filePath) try: users_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(users_module) except AttributeError as e: # no module from spec if PYTHON34 or PYTHON33: loader = SourceFileLoader(EXEC_NAMESPACE, document.filePath) users_module = loader.load_module() else: raise e try: # maybe script is to be execed, maybe main needs to be invoked # if there is a main() then execute it, otherwise don't worry... users_module.main() except AttributeError: pass else: code = compile(script, '', 'exec') - exec(code, {}) + globals_dict = {"__name__":EXEC_NAMESPACE} + exec(code, globals_dict) except Exception as e: """Provide context (line number and text) for an error that is caught. Ordinarily, syntax and Indent errors are caught during initial compilation in exec(), and the traceback traces back to this file. So these need to be treated separately. Other errors trace back to the file/script being run. """ type_, value_, traceback_ = sys.exc_info() if type_ == SyntaxError: errorMessage = "%s\n%s"%(value_.text.rstrip(), " "*(value_.offset-1)+"^") # rstrip to remove trailing \n, output needs to be fixed width font for the ^ to align correctly errorText = "Syntax Error on line %s"%value_.lineno elif type_ == IndentationError: # (no offset is provided for an IndentationError errorMessage = value_.text.rstrip() errorText = "Unexpected Indent on line %s"%value_.lineno else: errorText = traceback.format_exception_only(type_, value_)[0] format_string = "In file: {0}\nIn function: {2} at line: {1}. Line with error:\n{3}" tbList = traceback.extract_tb(traceback_) tb = tbList[-1] errorMessage = format_string.format(*tb) m = "\n**********************\n%s\n%s\n**********************\n"%(errorText, errorMessage) output.write(m) sys.stdout = stdout sys.stderr = stderr # scroll to bottom of output bottom = self.output.verticalScrollBar().maximum() self.output.verticalScrollBar().setValue(bottom) diff --git a/plugins/extensions/pykrita/plugin/plugins/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop b/plugins/extensions/pykrita/plugin/plugins/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop index df18a66fd6..b00ac6d3fd 100644 --- a/plugins/extensions/pykrita/plugin/plugins/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop @@ -1,40 +1,42 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=selectionsbagdocker X-Python-2-Compatible=false Name=Selections Bag Docker +Name[ar]=رصيف سلّة التّحديدات Name[ca]=Acoblador de bossa de seleccions Name[ca@valencia]=Acoblador de bossa de seleccions Name[el]=Προσάρτηση σάκου επιλογών Name[en_GB]=Selections Bag Docker Name[es]=Panel de selecciones Name[gl]=Doca de bolsa das seleccións Name[it]=Area di raccolta selezioni Name[nl]=Docker van zak met selecties Name[pl]=Dok worka zaznaczeń Name[pt]=Área de Selecções Name[sv]=Dockningspanel med markeringspåse Name[tr]=Seçim Çantası Doku Name[uk]=Бічна панель позначеного Name[x-test]=xxSelections Bag Dockerxx Name[zh_CN]=选区包坞窗 Name[zh_TW]=「選取範圍收藏」面板 Comment=A docker that allow to store a list of selections +Comment[ar]=رصيف يتيح تخزين قائمة تحديدات Comment[ca]=Un acoblador que permet emmagatzemar una llista de seleccions Comment[ca@valencia]=Un acoblador que permet emmagatzemar una llista de seleccions Comment[cs]=Dok umožňující uložit seznam výběrů Comment[el]=Ένα εργαλείο προσάρτησης που επιτρέπει την αποθήκευση μιας λίστας επιλογών Comment[en_GB]=A docker that allow to store a list of selections Comment[es]=Un panel que permite guardar una lista de selecciones Comment[gl]=Unha doca que permite almacenar unha lista de seleccións. Comment[it]=Un'area di aggancio che consente di memorizzare un elenco di selezioni Comment[nl]=Een docker die een lijst met selecties kan opslaan Comment[pl]=Dok, który umożliwia przechowywanie listy zaznaczeń Comment[pt]=Uma área acoplável que permite guardar uma lista de selecções Comment[sv]=En dockningspanel som gör det möjligt att lagra en lista över markeringar Comment[tr]=Seçimlerin bir listesini saklamayı sağlayan bir dok Comment[uk]=Бічна панель, на якій можна зберігати список позначеного Comment[x-test]=xxA docker that allow to store a list of selectionsxx Comment[zh_CN]=存储选区列表的坞窗 Comment[zh_TW]=允許儲存選取範圍列表的面板 diff --git a/plugins/extensions/pykrita/plugin/plugins/tenbrushes/kritapykrita_tenbrushes.desktop b/plugins/extensions/pykrita/plugin/plugins/tenbrushes/kritapykrita_tenbrushes.desktop index 6afeb2d370..3a6ec05df8 100644 --- a/plugins/extensions/pykrita/plugin/plugins/tenbrushes/kritapykrita_tenbrushes.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/tenbrushes/kritapykrita_tenbrushes.desktop @@ -1,41 +1,42 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=tenbrushes X-Python-2-Compatible=false Name=Ten Brushes +Name[ar]=عشرُ فُرش Name[ca]=Deu pinzells Name[ca@valencia]=Deu pinzells Name[cs]=Deset štětců Name[el]=Δέκα πινέλα Name[en_GB]=Ten Brushes Name[es]=Diez pinceles Name[gl]=Dez pinceis Name[is]=Tíu penslar Name[it]=Dieci pennelli Name[nl]=Tien penselen Name[pl]=Dziesięć pędzli Name[pt]=Dez Pincéis Name[sv]=Tio penslar Name[tr]=On Fırça Name[uk]=Десять пензлів Name[x-test]=xxTen Brushesxx Name[zh_CN]=十笔刷 Name[zh_TW]=10 個筆刷 Comment=Assign a preset to ctrl-1 to ctrl-0 Comment[ca]=Assigna una predefinició des de Ctrl-1 a Ctrl-0 Comment[ca@valencia]=Assigna una predefinició des de Ctrl-1 a Ctrl-0 Comment[el]=Αντιστοίχιση προκαθορισμένου από ctrl-1 στο ctrl-0 Comment[en_GB]=Assign a preset to ctrl-1 to ctrl-0 Comment[es]=Asignar una preselección a Ctrl-1 hasta Ctrl-0 Comment[gl]=Asigne unha predefinición do Ctrl+1 ao Ctrl+0. Comment[it]=Assegna una preimpostazione per ctrl-1 a ctrl-0 Comment[nl]=Een voorinstelling toekennen aan Ctrl-1 tot Ctrl-0 Comment[pl]=Przypisz nastawę do ctrl-1 lub ctrl-0 Comment[pt]=Atribuir uma predefinição de Ctrl-1 a Ctrl-0 Comment[sv]=Tilldela en förinställning för Ctrl+1 till Ctrl+0 Comment[tr]= ctrl-1 ve ctrl-0 için ayar atama Comment[uk]=Прив’язування наборів налаштувань до скорочень від ctrl-1 до ctrl-0 Comment[x-test]=xxAssign a preset to ctrl-1 to ctrl-0xx Comment[zh_CN]=将预设分配给 Ctrl+1 到 Ctrl+0 Comment[zh_TW]=將預設指定給 ctrl-1 至 ctrl-0 diff --git a/plugins/extensions/pykrita/plugin/plugins/tenscripts/kritapykrita_tenscripts.desktop b/plugins/extensions/pykrita/plugin/plugins/tenscripts/kritapykrita_tenscripts.desktop index 394439f7c2..35dad9cbb3 100644 --- a/plugins/extensions/pykrita/plugin/plugins/tenscripts/kritapykrita_tenscripts.desktop +++ b/plugins/extensions/pykrita/plugin/plugins/tenscripts/kritapykrita_tenscripts.desktop @@ -1,34 +1,36 @@ [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=tenscripts X-Python-2-Compatible=false Name=Ten Scripts +Name[ar]=عشرُ سكربتات Name[ca]=Deu scripts Name[ca@valencia]=Deu scripts Name[en_GB]=Ten Scripts Name[es]=Diez guiones Name[is]=Tíu skriftur Name[it]=Dieci script Name[nl]=Tien scripts Name[pl]=Skrypty Ten Name[pt]=Dez Programas Name[sv]=Tio skript Name[uk]=Десять скриптів Name[x-test]=xxTen Scriptsxx Name[zh_CN]=十脚本 Name[zh_TW]=10 個腳本 Comment=A Python-based plugin for creating ten actions and assign them to Python scripts +Comment[ar]=ملحقة بِ‍«پيثون» لإنشاء ١٠ إجراءات وإسنادها إلى سكربتات «پيثون» Comment[ca]=Un connector basant en el Python per crear deu accions i assignar-les a scripts del Python Comment[ca@valencia]=Un connector basant en el Python per crear deu accions i assignar-les a scripts del Python Comment[en_GB]=A Python-based plugin for creating ten actions and assign them to Python scripts Comment[es]=Un complemento basado en Python para crear diez acciones y asignarlas a guiones de Python Comment[it]=Un'estensione basata su Python per creare dieci azioni e assegnarle a script Python Comment[nl]=Een op Python gebaseerde plug-in voor aanmaken van tien acties en ze dan toewijzen aan Python-scripts Comment[pl]=Wtyczka oparta na Pythonie do tworzenia działań i przypisywanie ich skryptom Pythona Comment[pt]=Um 'plugin' feito em Python para criar dez acções e atribuí-las a programas em Python Comment[sv]=Ett Python-baserat insticksprogram för att skapa tio åtgärder och tilldela dem till Python-skript Comment[uk]=Скрипт на основі Python для створення десяти дій і прив'язування до них скриптів Python Comment[x-test]=xxA Python-based plugin for creating ten actions and assign them to Python scriptsxx Comment[zh_CN]=用于创建十个操作并将它们分配给 Python 脚本的插件 Comment[zh_TW]=基於 Python 的外掛程式,用於建立 10 個動作並且將它們指定給 Python 腳本 diff --git a/plugins/flake/CMakeLists.txt b/plugins/flake/CMakeLists.txt index 9710fe718f..5dd1c0c5cb 100644 --- a/plugins/flake/CMakeLists.txt +++ b/plugins/flake/CMakeLists.txt @@ -1,5 +1,4 @@ add_subdirectory( imageshape ) add_subdirectory( artistictextshape ) add_subdirectory( pathshapes ) add_subdirectory( textshape ) -add_subdirectory( vectorshape ) diff --git a/plugins/flake/textshape/AnnotationTextShape.cpp b/plugins/flake/textshape/AnnotationTextShape.cpp deleted file mode 100644 index 4c201c6ce0..0000000000 --- a/plugins/flake/textshape/AnnotationTextShape.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2013 Mojtaba Shahi Senobari - * - * 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 "AnnotationTextShape.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -AnnotationTextShape::AnnotationTextShape(KoInlineTextObjectManager *inlineTextObjectManager, KoTextRangeManager *textRangeManager) - : TextShape(inlineTextObjectManager, textRangeManager) - , m_creator() - , m_date() - , m_dateString() -{ - setBackground(QSharedPointer(new KoColorBackground(Qt::yellow))); - setGeometryProtected(true); -} - -AnnotationTextShape::~AnnotationTextShape() -{ -} - -void AnnotationTextShape::setAnnotaionTextData(KoTextShapeData *textShapeData) -{ - m_textShapeData = textShapeData; - m_textShapeData->setTopPadding(HeaderSpace); - m_textShapeData->setLeftPadding(qreal(4.0)); // Make it a little nicer - m_textShapeData->setRightPadding(qreal(4.0)); - m_textShapeData->setBottomPadding(qreal(4.0)); - m_textShapeData->setResizeMethod(KoTextShapeData::AutoGrowHeight); -} - -void AnnotationTextShape::paintComponent(QPainter &painter, const KoViewConverter &converter, - KoShapePaintingContext &paintcontext) -{ - if (paintcontext.showAnnotations) { - TextShape::paintComponent(painter, converter, paintcontext); - QRectF clipRect = outlineRect(); - - // Paint creator and of creation of the annotation. - QPen peninfo(Qt::darkYellow); - QFont serifFont("Times", HeaderFontSize, QFont::Bold); - painter.setPen(peninfo); - painter.setFont(serifFont); - - QDate date = QDate::fromString(m_date, Qt::ISODate); - QString info = " " + m_creator + "\n " + date.toString(Qt::LocalDate); - painter.drawText(clipRect, Qt::AlignTop, info); - } -} - -bool AnnotationTextShape::loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) -{ - //qDebug() << "****** Start Load odf ******"; - - KoTextLoader textLoader(context); - QTextCursor cursor(textShapeData()->document()); - - const QString localName(element.localName()); - - if (localName == "annotation") { - - // FIXME: Load more attributes here - - // Load the metadata (author, date) and contents here. - KoXmlElement el; - forEachElement(el, element) { - if (el.localName() == "creator" && el.namespaceURI() == KoXmlNS::dc) { - m_creator = el.text(); - if (m_creator.isEmpty()) { - m_creator = "Unknown"; - } - } else if (el.localName() == "date" && el.namespaceURI() == KoXmlNS::dc) { - m_date = el.text(); - } else if (el.localName() == "datestring" && el.namespaceURI() == KoXmlNS::meta) { - m_dateString = el.text(); - } - } - textLoader.loadBody(element, cursor); - //qDebug() << "****** End Load ******"; - } else { - // something pretty weird going on... - return false; - } - return true; -} - -void AnnotationTextShape::saveOdf(KoShapeSavingContext &context) const -{ - //qDebug() << " ****** Start saving annotation shape **********"; - KoXmlWriter *writer = &context.xmlWriter(); - - writer->startElement("dc:creator", false); - writer->addTextNode(m_creator); - writer->endElement(); // dc:creator - writer->startElement("dc:date", false); - writer->addTextNode(m_date); - writer->endElement(); // dc:date - - if (!m_dateString.isEmpty()) { - writer->startElement("meta:date-string", false); - writer->addTextNode(m_dateString); - writer->endElement(); // meta:date-string - } - - m_textShapeData->saveOdf(context, 0, 0, -1); -} - -void AnnotationTextShape::setCreator(const QString &creator) -{ - m_creator = creator; -} - -QString AnnotationTextShape::creator() const -{ - return m_creator; -} - -void AnnotationTextShape::setDate(const QString &date) -{ - m_date = date; -} - -QString AnnotationTextShape::date() const -{ - return m_date; -} - -void AnnotationTextShape::setDateString(const QString &dateString) -{ - m_dateString = dateString; -} - -QString AnnotationTextShape::dateString() const -{ - return m_dateString; -} - -const qreal AnnotationTextShape::HeaderSpace = 25.0; // The space needed for the annotation header. -const qreal AnnotationTextShape::HeaderFontSize = 6.0; diff --git a/plugins/flake/textshape/AnnotationTextShape.h b/plugins/flake/textshape/AnnotationTextShape.h deleted file mode 100644 index 74bd033be7..0000000000 --- a/plugins/flake/textshape/AnnotationTextShape.h +++ /dev/null @@ -1,88 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2013 Mojtaba Shahi Senobari - * - * 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 ANNOTATIONTEXTSHAPE_H -#define ANNOTATIONTEXTSHAPE_H - -#include "TextShape.h" -#include -#include - -#include -#include - -class KoInlineTextObjectManager; -class KoTextRangeManager; - -#define AnnotationShape_SHAPEID "AnnotationTextShapeID" - -class AnnotationTextShape : public TextShape -{ -public: - // Some constants - static const qreal HeaderSpace; // The space needed for the annotation header. - static const qreal HeaderFontSize; - - // For now we should give these parameters for TextShape. - AnnotationTextShape(KoInlineTextObjectManager *inlineTextObjectManager, - KoTextRangeManager *textRangeManager); - ~AnnotationTextShape() override; - - void setAnnotaionTextData(KoTextShapeData *textShape); - - // reimplemented - void paintComponent(QPainter &painter, const KoViewConverter &converter, - KoShapePaintingContext &paintcontext) override; - - /** - * From KoShape reimplemented method to load the TextShape from ODF. - * - * This method redirects the call to the KoTextShapeData::loadOdf() method which - * in turn will call the KoTextLoader::loadBody() method that reads the element - * into a QTextCursor. - * - * @param element element which represents the shape in odf. - * @param context the KoShapeLoadingContext used for loading. - * @return false if loading failed. - */ - bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) override; - - /** - * From KoShape reimplemented method to store the TextShape data as ODF. - * - * @param context the KoShapeSavingContext used for saving. - */ - void saveOdf(KoShapeSavingContext &context) const override; - - void setCreator(const QString &creator); - QString creator() const; - void setDate(const QString &date); - QString date() const; - void setDateString(const QString &date); - QString dateString() const; - -private: - KoTextShapeData *m_textShapeData; - - QString m_creator; - QString m_date; - QString m_dateString; // another wayof storing the date. Not sure when it is used. -}; - -#endif // ANNOTATIONTEXTSHAPE_H diff --git a/plugins/flake/textshape/AnnotationTextShapeFactory.cpp b/plugins/flake/textshape/AnnotationTextShapeFactory.cpp deleted file mode 100644 index 1fa42fdc79..0000000000 --- a/plugins/flake/textshape/AnnotationTextShapeFactory.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2013 Mojtaba Shahi Senobari - * - * 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 "AnnotationTextShapeFactory.h" -#include "AnnotationTextShape.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -AnnotationTextShapeFactory::AnnotationTextShapeFactory() - : KoShapeFactoryBase(AnnotationShape_SHAPEID, i18n("Annotation")) -{ - setToolTip(i18n("Annotation shape to show annotation content")); - QList > odfElements; - odfElements.append(QPair(KoXmlNS::office, QStringList("annotation"))); - setXmlElements(odfElements); - - KoShapeTemplate t; - t.name = i18n("Annotation"); - t.iconName = koIconName("x-shape-text"); // Any icon for now :) - t.toolTip = i18n("Annotation Shape"); - KoProperties *props = new KoProperties(); - t.properties = props; - props->setProperty("demo", true); - addTemplate(t); -} - -KoShape *AnnotationTextShapeFactory::createDefaultShape(KoDocumentResourceManager *documentResources) const -{ - KoInlineTextObjectManager *manager = 0; - KoTextRangeManager *locationManager = 0; - if (documentResources && documentResources->hasResource(KoText::InlineTextObjectManager)) { - QVariant variant = documentResources->resource(KoText::InlineTextObjectManager); - if (variant.isValid()) { - manager = variant.value(); - } - } - if (documentResources && documentResources->hasResource(KoText::TextRangeManager)) { - QVariant variant = documentResources->resource(KoText::TextRangeManager); - if (variant.isValid()) { - locationManager = variant.value(); - } - } - if (!manager) { - manager = new KoInlineTextObjectManager(); - } - if (!locationManager) { - locationManager = new KoTextRangeManager(); - } - AnnotationTextShape *annotation = new AnnotationTextShape(manager, locationManager); - if (documentResources) { - KoTextDocument document(annotation->textShapeData()->document()); - - if (documentResources->hasResource(KoText::StyleManager)) { - KoStyleManager *styleManager = documentResources->resource(KoText::StyleManager).value(); - document.setStyleManager(styleManager); - } - - // this is needed so the shape can reinitialize itself with the stylemanager - annotation->textShapeData()->setDocument(annotation->textShapeData()->document()); - - document.setUndoStack(documentResources->undoStack()); - - if (documentResources->hasResource(KoText::PageProvider)) { - KoPageProvider *pp = static_cast(documentResources->resource(KoText::PageProvider).value()); - annotation->setPageProvider(pp); - } - if (documentResources->hasResource(KoText::ChangeTracker)) { - KoChangeTracker *changeTracker = documentResources->resource(KoText::ChangeTracker).value(); - document.setChangeTracker(changeTracker); - } - - //update the resources of the document - annotation->updateDocumentData(); - annotation->setImageCollection(documentResources->imageCollection()); - } - - // Should set if we don't set id it will set to TextShapeID. - annotation->setShapeId(AnnotationShape_SHAPEID); - - annotation->setAnnotaionTextData(annotation->textShapeData()); - return annotation; -} - -KoShape *AnnotationTextShapeFactory::createShape(const KoProperties *params, KoDocumentResourceManager *documentResources) const -{ - Q_UNUSED(params); - AnnotationTextShape *shape = static_cast(createDefaultShape(documentResources)); - shape->textShapeData()->document()->setUndoRedoEnabled(false); - - if (documentResources) { - shape->setImageCollection(documentResources->imageCollection()); - } - shape->textShapeData()->document()->setUndoRedoEnabled(true); - return shape; -} - -bool AnnotationTextShapeFactory::supports(const KoXmlElement &e, KoShapeLoadingContext &context) const -{ - Q_UNUSED(context); - return (e.localName() == "annotation" && e.namespaceURI() == KoXmlNS::office); -} diff --git a/plugins/flake/textshape/AnnotationTextShapeFactory.h b/plugins/flake/textshape/AnnotationTextShapeFactory.h deleted file mode 100644 index 75f6591252..0000000000 --- a/plugins/flake/textshape/AnnotationTextShapeFactory.h +++ /dev/null @@ -1,39 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2013 Mojtaba Shahi Senobari - * - * 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 ANNOTATIONTEXTSHAPEFACTORY_H -#define ANNOTATIONTEXTSHAPEFACTORY_H - -#include - -class KoShape; - -class AnnotationTextShapeFactory : public KoShapeFactoryBase -{ -public: - AnnotationTextShapeFactory(); - ~AnnotationTextShapeFactory() override {} - - KoShape *createDefaultShape(KoDocumentResourceManager *documentResources) const override; - KoShape *createShape(const KoProperties *params, KoDocumentResourceManager *documentResources) const override; - bool supports(const KoXmlElement &e, KoShapeLoadingContext &context) const override; - -}; - -#endif // ANNOTATIONTEXTSHAPEFACTORY_H diff --git a/plugins/flake/textshape/CMakeLists.txt b/plugins/flake/textshape/CMakeLists.txt index bd4eca24d8..0f37e71d71 100644 --- a/plugins/flake/textshape/CMakeLists.txt +++ b/plugins/flake/textshape/CMakeLists.txt @@ -1,177 +1,140 @@ project( textPlugin) if (${Qt5_VERSION} VERSION_GREATER "5.8.0" ) remove_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50900) elseif(${Qt5_VERSION} VERSION_GREATER "5.7.0" ) remove_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50800) elseif(${Qt5_VERSION} VERSION_GREATER "5.6.0" ) remove_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50700) else() remove_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50600) endif() add_definitions( -DQT_DISABLE_DEPRECATED_BEFORE=0 ) add_subdirectory( textlayout ) add_subdirectory( kotext ) set ( textshape_SRCS TextPlugin.cpp TextShape.cpp TextShapeFactory.cpp TextTool.cpp TextEditingPluginContainer.cpp TextToolFactory.cpp ShrinkToFitShapeContainer.cpp SimpleRootAreaProvider.cpp - AnnotationTextShape.cpp - AnnotationTextShapeFactory.cpp - - ChangeTracker.cpp - ReviewTool.cpp - ReviewToolFactory.cpp - TextChanges.cpp - TextChange.cpp + FontSizeAction.cpp FontFamilyAction.cpp - ReferencesTool.cpp - ReferencesToolFactory.cpp - -# dialogs/StylesWidget.cpp -# dialogs/SpecialButton.cpp dialogs/StylesCombo.cpp dialogs/StylesComboPreview.cpp dialogs/DockerStylesComboModel.cpp dialogs/SimpleCharacterWidget.cpp dialogs/SimpleParagraphWidget.cpp dialogs/SimpleTableWidget.cpp dialogs/SimpleInsertWidget.cpp - dialogs/LinkInsertionDialog.cpp - dialogs/SimpleTableOfContentsWidget.cpp - dialogs/SimpleCitationBibliographyWidget.cpp - dialogs/SimpleLinksWidget.cpp - dialogs/SimpleSpellCheckingWidget.cpp - dialogs/CitationInsertionDialog.cpp - dialogs/InsertBibliographyDialog.cpp - dialogs/SimpleFootEndNotesWidget.cpp - dialogs/NotesConfigurationDialog.cpp - dialogs/SimpleCaptionsWidget.cpp dialogs/ParagraphLayout.cpp dialogs/ParagraphIndentSpacing.cpp dialogs/ParagraphDecorations.cpp dialogs/ParagraphBulletsNumbers.cpp dialogs/ParagraphSettingsDialog.cpp dialogs/ParagraphDropCaps.cpp dialogs/ListsSpinBox.cpp dialogs/StylesModel.cpp dialogs/StylesManagerModel.cpp dialogs/StylesSortFilterProxyModel.cpp dialogs/AbstractStylesModel.cpp dialogs/StylesFilteredModelBase.cpp dialogs/ValidParentStylesProxyModel.cpp dialogs/StylesDelegate.cpp dialogs/StyleManager.cpp dialogs/StyleManagerDialog.cpp dialogs/ParagraphGeneral.cpp dialogs/CharacterGeneral.cpp dialogs/CharacterHighlighting.cpp dialogs/InsertCharacter.cpp dialogs/FontDia.cpp dialogs/FontDecorations.cpp dialogs/LanguageTab.cpp dialogs/FormattingPreview.cpp dialogs/StyleManagerWelcome.cpp dialogs/TableDialog.cpp dialogs/QuickTableButton.cpp dialogs/FormattingButton.cpp dialogs/ChangeConfigureDialog.cpp dialogs/AcceptRejectChangeDialog.cpp dialogs/TrackedChangeModel.cpp dialogs/TrackedChangeManager.cpp dialogs/BibliographyConfigureDialog.cpp dialogs/TableOfContentsConfigure.cpp dialogs/TableOfContentsStyleConfigure.cpp dialogs/TableOfContentsStyleModel.cpp dialogs/TableOfContentsStyleDelegate.cpp dialogs/TableOfContentsPreview.cpp dialogs/TableOfContentsEntryDelegate.cpp dialogs/TableOfContentsEntryModel.cpp dialogs/TableOfContentsTemplate.cpp dialogs/BibliographyTemplate.cpp dialogs/BibliographyPreview.cpp dialogs/ListLevelChooser.cpp - dialogs/SimpleAnnotationWidget.cpp - dialogs/ManageBookmarkDialog.cpp dialogs/SectionFormatDialog.cpp dialogs/SectionsSplitDialog.cpp commands/ChangeListLevelCommand.cpp commands/ShowChangesCommand.cpp commands/AcceptChangeCommand.cpp commands/RejectChangeCommand.cpp commands/AutoResizeCommand.cpp ) ki18n_wrap_ui(textshape_SRCS dialogs/SimpleCharacterWidget.ui dialogs/SimpleParagraphWidget.ui dialogs/SimpleTableWidget.ui dialogs/SimpleInsertWidget.ui - dialogs/SimpleTableOfContentsWidget.ui - dialogs/SimpleCitationBibliographyWidget.ui - dialogs/SimpleSpellCheckingWidget.ui - dialogs/CitationInsertionDialog.ui - dialogs/InsertBibliographyDialog.ui - dialogs/SimpleFootEndNotesWidget.ui - dialogs/NotesConfigurationDialog.ui - dialogs/SimpleCaptionsWidget.ui - dialogs/StylesWidget.ui dialogs/ParagraphLayout.ui dialogs/ParagraphIndentSpacing.ui dialogs/ParagraphDecorations.ui dialogs/ParagraphBulletsNumbers.ui dialogs/ParagraphDropCaps.ui dialogs/StyleManager.ui dialogs/CharacterGeneral.ui dialogs/CharacterHighlighting.ui dialogs/StyleManagerWelcome.ui dialogs/TableDialog.ui dialogs/BibliographyConfigureDialog.ui dialogs/TableOfContentsConfigure.ui - dialogs/SimpleLinksWidget.ui dialogs/TableOfContentsStyleConfigure.ui - dialogs/SimpleAnnotationWidget.ui dialogs/FontDecorations.ui dialogs/LanguageTab.ui dialogs/ChangeConfigureDialog.ui dialogs/AcceptRejectChangeDialog.ui dialogs/TrackedChangeManager.ui - dialogs/LinkInsertionDialog.ui - dialogs/ManageBookmark.ui dialogs/SectionFormatDialog.ui dialogs/SectionsSplitDialog.ui ) qt5_add_resources(textshape_SRCS textshape.qrc) add_library(krita_shape_text MODULE ${textshape_SRCS}) target_link_libraries(krita_shape_text kritatext kritatextlayout kritawidgetutils kritawidgets Qt5::Network KF5::Completion KF5::ItemViews KF5::WidgetsAddons ) if( SHOULD_BUILD_FEATURE_RDF ) target_link_libraries(krita_shape_text ${SOPRANO_LIBRARIES}) endif() install(TARGETS krita_shape_text DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/flake/textshape/ChangeTracker.cpp b/plugins/flake/textshape/ChangeTracker.cpp deleted file mode 100644 index 7a36fd4143..0000000000 --- a/plugins/flake/textshape/ChangeTracker.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2007 Thomas Zander - * Copyright (C) 2008 Pierre Stirnweiss \pierre.stirnweiss_calligra@gadz.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "ChangeTracker.h" -#include "TextTool.h" - -#include - -ChangeTracker::ChangeTracker(TextTool *parent) - : QObject(parent) - , m_document(0) - , m_tool(parent) - , m_enableSignals(true) - , m_reverseUndo(false) -{ -} - -void ChangeTracker::setDocument(QTextDocument *document) -{ - m_reverseUndo = false; - if (m_document) { - disconnect(m_document, SIGNAL(contentsChange(int,int,int)), this, SLOT(contentsChange(int,int,int))); - } - m_document = document; - if (m_document) { - connect(m_document, SIGNAL(contentsChange(int,int,int)), this, SLOT(contentsChange(int,int,int))); - } -} - -int ChangeTracker::getChangeId(QString title, int existingChangeId) -{ - Q_UNUSED(title); - Q_UNUSED(existingChangeId); - qDebug() << "ChangeTracker::changeId :" << m_changeId; - return m_changeId++; -} - -void ChangeTracker::contentsChange(int from, int charsRemoves, int charsAdded) -{ - Q_UNUSED(from); - Q_UNUSED(charsRemoves); - Q_UNUSED(charsAdded); - /* - if (!m_enableSignals) return; - m_enableSignals = false; - qDebug() << "ChangeTracker::contentsChange" << from << "," << charsRemoves << "," << charsAdded; - - if (charsRemoves == 0 && charsAdded == 0) { - // I think we can quietly ignore this. - } else if (charsRemoves == 0) { // easy - QTextCursor cursor(m_document); - cursor.setPosition(from); - cursor.setPosition(from + charsAdded, QTextCursor::KeepAnchor); - qDebug() << " added text:" << cursor.selectedText(); - } else { - bool prev = m_tool->m_allowAddUndoCommand; - m_tool->m_allowAddUndoCommand = false; - m_reverseUndo ? m_document->redo() : m_document->undo(); - QTextCursor cursor(m_document); - cursor.setPosition(from); - cursor.setPosition(from + charsRemoves, QTextCursor::KeepAnchor); - QString previousText = cursor.selectedText(); - m_reverseUndo ? m_document->undo() : m_document->redo(); - m_tool->m_allowAddUndoCommand = prev; - - cursor.setPosition(from); - cursor.setPosition(from + charsAdded, QTextCursor::KeepAnchor); - - qDebug() << " - " << previousText; - qDebug() << " + " << cursor.selectedText(); - } - - m_enableSignals = true; - m_reverseUndo = false; - */ -} - -void ChangeTracker::notifyForUndo() -{ - m_reverseUndo = true; -} diff --git a/plugins/flake/textshape/ChangeTracker.h b/plugins/flake/textshape/ChangeTracker.h deleted file mode 100644 index 980daf7ed7..0000000000 --- a/plugins/flake/textshape/ChangeTracker.h +++ /dev/null @@ -1,50 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2007 Thomas Zander - * Copyright (C) 2008 Pierre Stirnweiss \pierre.stirnweiss_calligra@gadz.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -#ifndef CHANGETRACKER_H -#define CHANGETRACKER_H - -#include - -class QTextDocument; -class TextTool; - -class ChangeTracker : public QObject -{ - Q_OBJECT -public: - explicit ChangeTracker(TextTool *parent); - - void setDocument(QTextDocument *document); - - int getChangeId(QString title, int existingChangeId); - - void notifyForUndo(); - -private Q_SLOTS: - void contentsChange(int from, int charsRemoves, int charsAdded); - -private: - QTextDocument *m_document; - TextTool *m_tool; - bool m_enableSignals, m_reverseUndo; - int m_changeId; -}; - -#endif diff --git a/plugins/flake/textshape/ReferencesTool.cpp b/plugins/flake/textshape/ReferencesTool.cpp deleted file mode 100644 index 02088f58de..0000000000 --- a/plugins/flake/textshape/ReferencesTool.cpp +++ /dev/null @@ -1,394 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2011 C. Boemann - * Copyright (C) 2013 Aman Madaan - * - * 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 "ReferencesTool.h" -#include "TextShape.h" -#include "dialogs/SimpleTableOfContentsWidget.h" -#include "dialogs/SimpleCitationBibliographyWidget.h" -#include "dialogs/SimpleFootEndNotesWidget.h" -#include "dialogs/SimpleCaptionsWidget.h" -#include "dialogs/SimpleLinksWidget.h" -#include "dialogs/TableOfContentsConfigure.h" -#include "dialogs/NotesConfigurationDialog.h" -#include "dialogs/CitationInsertionDialog.h" -#include "dialogs/InsertBibliographyDialog.h" -#include "dialogs/BibliographyConfigureDialog.h" -#include "dialogs/LinkInsertionDialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "kis_action_registry.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -LabeledWidget::LabeledWidget(QAction *action, const QString &label, LabelPosition lb, bool warningLabelRequired) - : QWidget() - , m_action(action) -{ - setMouseTracking(true); - QBoxLayout *layout; - QLabel *l = new QLabel(label); - l->setWordWrap(true); - m_lineEdit = new QLineEdit(); - if (lb == LabeledWidget::INLINE) { // label followed by line edit - layout = new QHBoxLayout(); - l->setIndent(l->style()->pixelMetric(QStyle::PM_SmallIconSize) - + l->style()->pixelMetric(QStyle::PM_MenuPanelWidth) + 4); - } else { //Label goes above the text edit - layout = new QVBoxLayout(); - m_lineEdit->setFixedWidth(300); //TODO : assuming a reasonable width, is there a better way? - } - layout->addWidget(l); - layout->addWidget(m_lineEdit); - if (warningLabelRequired) { - m_warningLabel[0] = new QLabel(); - m_warningLabel[1] = new QLabel(); - m_warningLabel[0]->setWordWrap(true); - m_warningLabel[1]->setWordWrap(true); - layout->addWidget(m_warningLabel[0]); - layout->addWidget(m_warningLabel[1]); - } - layout->setMargin(0); - setLayout(layout); - connect(m_lineEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed())); - connect(m_lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(lineEditChanged(QString))); -} - -void LabeledWidget::returnPressed() -{ - emit triggered(m_lineEdit->text()); -} - -void LabeledWidget::enterEvent(QEvent *event) -{ - m_action->activate(QAction::Hover); - QWidget::enterEvent(event); -} - -void LabeledWidget::setWarningText(int pos, const QString &warning) -{ - if (m_warningLabel[pos] == 0) { - return; - } - m_warningLabel[pos]->setText(warning); -} - -void LabeledWidget::clearLineEdit() -{ - m_lineEdit->setText(QString()); -} - -ReferencesTool::ReferencesTool(KoCanvasBase *canvas): TextTool(canvas), - m_configure(0), - m_stocw(0), - m_canvas(canvas) -{ - createActions(); -} - -ReferencesTool::~ReferencesTool() -{ -} - -void ReferencesTool::createActions() -{ - KisActionRegistry *actionRegistry = KisActionRegistry::instance(); - - QAction *action = actionRegistry->makeQAction("insert_tableofcontents", this); - addAction("insert_tableofcontents", action); - - action = actionRegistry->makeQAction("insert_configure_tableofcontents", this); - addAction("insert_configure_tableofcontents", action); - - action = actionRegistry->makeQAction("format_tableofcontents", this); - addAction("format_tableofcontents", action); - connect(action, SIGNAL(triggered()), this, SLOT(formatTableOfContents())); - - action = actionRegistry->makeQAction("insert_autofootnote", this); - addAction("insert_autofootnote", action); - connect(action, SIGNAL(triggered()), this, SLOT(insertAutoFootNote())); - - QWidgetAction *wAction = new QWidgetAction(this); - wAction->setText(i18n("Insert Labeled Footnote")); - QWidget *w = new LabeledWidget(wAction, i18n("Insert with label:"), LabeledWidget::INLINE, false); - wAction->setDefaultWidget(w); - addAction("insert_labeledfootnote", wAction); - connect(w, SIGNAL(triggered(QString)), this, SLOT(insertLabeledFootNote(QString))); - - action = actionRegistry->makeQAction("insert_autoendnote", this); - addAction("insert_autoendnote", action); - connect(action, SIGNAL(triggered()), this, SLOT(insertAutoEndNote())); - - wAction = new QWidgetAction(this); - wAction->setText(i18n("Insert Labeled Endnote")); - w = new LabeledWidget(wAction, i18n("Insert with label:"), LabeledWidget::INLINE, false); - wAction->setDefaultWidget(w); - addAction("insert_labeledendnote", wAction); connect(w, SIGNAL(triggered(QString)), this, SLOT(insertLabeledEndNote(QString))); - - action = actionRegistry->makeQAction("format_footnotes", this); - addAction("format_footnotes", action); - connect(action, SIGNAL(triggered()), this, SLOT(showFootnotesConfigureDialog())); - - action = actionRegistry->makeQAction("format_endnotes", this); - addAction("format_endnotes", action); - connect(action, SIGNAL(triggered()), this, SLOT(showEndnotesConfigureDialog())); - - action = actionRegistry->makeQAction("insert_citation", this); - addAction("insert_citation", action); - connect(action, SIGNAL(triggered()), this, SLOT(insertCitation())); - - action = actionRegistry->makeQAction("insert_bibliography", this); - addAction("insert_bibliography", action); - - action = actionRegistry->makeQAction("insert_custom_bibliography", this); - addAction("insert_custom_bibliography", action); - - action = actionRegistry->makeQAction("configure_bibliography", this); - addAction("configure_bibliography", action); - connect(action, SIGNAL(triggered()), this, SLOT(configureBibliography())); - - action = actionRegistry->makeQAction("insert_link", this); - addAction("insert_link", action); - connect(action, SIGNAL(triggered()), this, SLOT(insertLink())); - - wAction = new QWidgetAction(this); - wAction->setText(i18n("Add Bookmark")); - m_bmark = new LabeledWidget(wAction, i18n("Add Bookmark :"), LabeledWidget::ABOVE, true); - connect(m_bmark, SIGNAL(lineEditChanged(QString)), this, SLOT(validateBookmark(QString))); - wAction->setDefaultWidget(m_bmark); - addAction("insert_bookmark", wAction); - connect(m_bmark, SIGNAL(triggered(QString)), this, SLOT(insertBookmark(QString))); - wAction->setToolTip(i18n("Insert a Bookmark. This is useful to create links that point to areas within the document")); - - action = actionRegistry->makeQAction("invoke_bookmark_handler", this); - addAction("invoke_bookmark_handler", action); - - action = actionRegistry->makeQAction("manage_bookmarks", this); - addAction("manage_bookmarks", action); -} - -void ReferencesTool::activate(ToolActivation toolActivation, const QSet &shapes) -{ - TextTool::activate(toolActivation, shapes); -} - -void ReferencesTool::deactivate() -{ - TextTool::deactivate(); - canvas()->canvasWidget()->setFocus(); -} - -QList > ReferencesTool::createOptionWidgets() -{ - QList > widgets; - m_stocw = new SimpleTableOfContentsWidget(this, 0); - - m_sfenw = new SimpleFootEndNotesWidget(this, 0); - - m_scbw = new SimpleCitationBibliographyWidget(this, 0); - - m_slw = new SimpleLinksWidget(this, 0); - // Connect to/with simple table of contents option widget - connect(m_stocw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas())); - - // Connect to/with simple citation index option widget - //connect(scw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas())); - - // Connect to/with simple citation index option widget - connect(m_sfenw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas())); - - connect(m_slw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas())); - - m_stocw->setWindowTitle(i18nc("as in table of contents, list of pictures, index", "Tables, Lists & Indexes")); - widgets.append(m_stocw); - - m_sfenw->setWindowTitle(i18n("Footnotes and Endnotes")); - widgets.append(m_sfenw); - - m_scbw->setWindowTitle(i18n("Citations and Bibliography")); - widgets.append(m_scbw); - - m_slw->setWindowTitle(i18n("Links and Bookmarks")); - widgets.append(m_slw); - //widgets.insert(i18n("Captions"), scapw); - connect(textEditor(), SIGNAL(cursorPositionChanged()), this, SLOT(updateButtons())); - return widgets; -} - -void ReferencesTool::insertCitation() -{ - new CitationInsertionDialog(textEditor(), m_scbw); -} - -void ReferencesTool::insertCustomBibliography(KoBibliographyInfo *defaultTemplate) -{ - Q_UNUSED(defaultTemplate); - new InsertBibliographyDialog(textEditor(), m_scbw); -} - -void ReferencesTool::configureBibliography() -{ - new BibliographyConfigureDialog(textEditor()->document(), m_scbw); -} - -void ReferencesTool::formatTableOfContents() -{ - if (textEditor()->block().blockFormat().hasProperty(KoParagraphStyle::TableOfContentsData)) { - m_configure = new TableOfContentsConfigure(textEditor(), textEditor()->block(), m_stocw); - connect(m_configure, SIGNAL(finished(int)), this, SLOT(hideCofigureDialog())); - } -} - -void ReferencesTool::showConfigureDialog(QAction *action) -{ - m_configure = new TableOfContentsConfigure(textEditor(), action->data().value(), m_stocw); - connect(m_configure, SIGNAL(finished(int)), this, SLOT(hideCofigureDialog())); -} - -void ReferencesTool::hideCofigureDialog() -{ - disconnect(m_configure, SIGNAL(finished(int)), this, SLOT(hideCofigureDialog())); - m_configure->deleteLater(); -} - -void ReferencesTool::insertAutoFootNote() -{ - m_note = textEditor()->insertFootNote(); - m_note->setAutoNumbering(true); -} - -void ReferencesTool::insertLabeledFootNote(const QString &label) -{ - m_note = textEditor()->insertFootNote(); - m_note->setAutoNumbering(false); - m_note->setLabel(label); -} - -void ReferencesTool::insertAutoEndNote() -{ - m_note = textEditor()->insertEndNote(); - m_note->setAutoNumbering(true); -} - -void ReferencesTool::insertLabeledEndNote(const QString &label) -{ - m_note = textEditor()->insertEndNote(); - m_note->setAutoNumbering(false); - m_note->setLabel(label); -} - -void ReferencesTool::showFootnotesConfigureDialog() -{ - NotesConfigurationDialog *dialog = new NotesConfigurationDialog((QTextDocument *)textEditor()->document(), true); - dialog->exec(); -} - -void ReferencesTool::showEndnotesConfigureDialog() -{ - NotesConfigurationDialog *dialog = new NotesConfigurationDialog((QTextDocument *)textEditor()->document(), false); - dialog->exec(); -} - -void ReferencesTool::updateButtons() -{ - if (textEditor()->currentFrame()->format().intProperty(KoText::SubFrameType) == KoText::NoteFrameType) { - m_sfenw->widget.addFootnote->setEnabled(false); - m_sfenw->widget.addEndnote->setEnabled(false); - } else { - m_sfenw->widget.addFootnote->setEnabled(true); - m_sfenw->widget.addEndnote->setEnabled(true); - } - if (textEditor()->block().blockFormat().hasProperty(KoParagraphStyle::TableOfContentsData)) { - action("format_tableofcontents")->setEnabled(true); - } else { - action("format_tableofcontents")->setEnabled(false); - } - -} - -KoTextEditor *ReferencesTool::editor() -{ - return textEditor(); -} - -void ReferencesTool::insertCustomToC(KoTableOfContentsGeneratorInfo *defaultTemplate) -{ - m_configure = new TableOfContentsConfigure(textEditor(), defaultTemplate, m_stocw); - connect(m_configure, SIGNAL(accepted()), this, SLOT(customToCGenerated())); - connect(m_configure, SIGNAL(finished(int)), this, SLOT(hideCofigureDialog())); -} - -void ReferencesTool::customToCGenerated() -{ - if (m_configure) { - textEditor()->insertTableOfContents(m_configure->currentToCData()); - } -} - -void ReferencesTool::insertLink() -{ - new LinkInsertionDialog(textEditor(), m_slw); -} - -bool ReferencesTool::validateBookmark(QString bookmarkName) -{ - bookmarkName = bookmarkName.trimmed(); - if (bookmarkName.isEmpty()) { - m_bmark->setWarningText(0, i18n("Bookmark cannot be empty")); - return false; - } - const KoBookmarkManager *manager = KoTextDocument(editor()->document()).textRangeManager()->bookmarkManager(); - QStringList existingBookmarks = manager->bookmarkNameList(); - int position = existingBookmarks.indexOf(bookmarkName); - if (position != -1) { - m_bmark->setWarningText(0, i18n("Duplicate Name. Click \"Manage Bookmarks\"")); - m_bmark->setWarningText(1, i18n("to Rename or Delete Bookmarks")); - return false; - } else { - m_bmark->setWarningText(0, ""); - m_bmark->setWarningText(1, ""); - return true; - } -} - -void ReferencesTool::insertBookmark(QString bookMarkName) -{ - bookMarkName = bookMarkName.trimmed(); - m_bmark->setWarningText(0, ""); - m_bmark->setWarningText(1, ""); - if (validateBookmark(bookMarkName)) { - editor()->addBookmark(bookMarkName); - m_bmark->clearLineEdit(); - } -} diff --git a/plugins/flake/textshape/ReferencesTool.h b/plugins/flake/textshape/ReferencesTool.h deleted file mode 100644 index dfb382baed..0000000000 --- a/plugins/flake/textshape/ReferencesTool.h +++ /dev/null @@ -1,136 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2011 C. Boemann - * Copyright (C) 2013 Aman Madaan - * - * 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 REFERENCESTOOL_H -#define REFERENCESTOOL_H - -#include "TextTool.h" -#include -#include - -#include - -class TableOfContentsConfigure; -class SimpleTableOfContentsWidget; -class SimpleFootEndNotesWidget; -class SimpleCitationBibliographyWidget; -class KoInlineNote; -class KoTextEditor; -class KoBibliographyInfo; -class KoTableOfContentsGeneratorInfo; -class SimpleLinksWidget; -class LabeledWidget; -/// This tool is the ui for inserting Table of Contents, Citations/bibliography, footnotes, endnotes, index, table of illustrations etc - -class ReferencesTool : public TextTool -{ - Q_OBJECT -public: - explicit ReferencesTool(KoCanvasBase *canvas); - - ~ReferencesTool() override; - - void activate(ToolActivation toolActivation, const QSet &shapes) override; - void deactivate() override; - - void createActions() override; - - KoTextEditor *editor(); - /// inserts a ToC and open a configure dialog for customization - void insertCustomToC(KoTableOfContentsGeneratorInfo *defaultTemplate); - /// insert a bibliography and open a configure dialog for customization - void insertCustomBibliography(KoBibliographyInfo *defaultTemplate); - -protected: - /// reimplemented from superclass - QList > createOptionWidgets() override; - -private Q_SLOTS: - /// insert a citation - void insertCitation(); - /// configure a bibliography - void configureBibliography(); - /// format the table of contents template - void formatTableOfContents(); - /// shows the configuration dialog for a ToC - void showConfigureDialog(QAction *action); - /// hides the configuration dialog for ToC - void hideCofigureDialog(); - /// insert an autonumbered footnote - void insertAutoFootNote(); - /// insert a labeled footnote - void insertLabeledFootNote(const QString &label); - /// insert an autonumbered endnote - void insertAutoEndNote(); - /// insert a labeled endnote - void insertLabeledEndNote(const QString &label); - /// show the configuration dialog for footnotes - void showFootnotesConfigureDialog(); - /// show the configuration dialog for endnotes - void showEndnotesConfigureDialog(); - /// enable/disable buttons if cursor in notes' body or not - void updateButtons(); - - void customToCGenerated(); - /// insert a Link - void insertLink(); - ///insert a bookmark - void insertBookmark(QString bookmarkName); - /// validate a bookmark - bool validateBookmark(QString bookmarkName); -private: - TableOfContentsConfigure *m_configure; - SimpleTableOfContentsWidget *m_stocw; - SimpleFootEndNotesWidget *m_sfenw; - KoInlineNote *m_note; - SimpleCitationBibliographyWidget *m_scbw; - SimpleLinksWidget *m_slw; - LabeledWidget *m_bmark; - QPointer m_canvas; -}; - -class QAction; -class QLineEdit; -class QLabel; -class LabeledWidget : public QWidget -{ - Q_OBJECT -public: - enum LabelPosition {INLINE, ABOVE}; - LabeledWidget(QAction *action, const QString &label, LabelPosition pos, bool warningLabelRequired); - void setWarningText(int pos, const QString &warning); - void clearLineEdit(); -Q_SIGNALS: - void triggered(const QString &label); - void lineEditChanged(const QString &); - -private Q_SLOTS: - void returnPressed(); - -protected: - void enterEvent(QEvent *event) override; - -private : - QLineEdit *m_lineEdit; - QLabel *m_warningLabel[2]; - QAction *m_action; -}; - -#endif // REFERENCESTOOL_H diff --git a/plugins/flake/textshape/ReferencesToolFactory.cpp b/plugins/flake/textshape/ReferencesToolFactory.cpp deleted file mode 100644 index 9a5340b5fa..0000000000 --- a/plugins/flake/textshape/ReferencesToolFactory.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2008 Pierre Stirnweiss \pierre.stirnweiss_calligra@gadz.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "ReferencesToolFactory.h" -#include "ReferencesTool.h" -#include "TextShape.h" - -#include -#include - -#include - -ReferencesToolFactory::ReferencesToolFactory() - : KoToolFactoryBase("ReferencesTool") -{ - setToolTip(i18n("References")); - setSection("calligrawords,calligraauthor"); - setIconName(koIconNameCStr("tool_references")); - setPriority(20); - setActivationShapeId(TextShape_SHAPEID); -} - -ReferencesToolFactory::~ReferencesToolFactory() -{ -} - -KoToolBase *ReferencesToolFactory::createTool(KoCanvasBase *canvas) -{ - return new ReferencesTool(canvas); -} diff --git a/plugins/flake/textshape/ReviewTool.cpp b/plugins/flake/textshape/ReviewTool.cpp deleted file mode 100644 index 95672ae1b4..0000000000 --- a/plugins/flake/textshape/ReviewTool.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2009-2010 Pierre Stirnweiss - * Copyright (C) 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. - */ - -#include "ReviewTool.h" -#include "AnnotationTextShape.h" - -#include -#include -#include -#include -#include -#include "KoShapeBasedDocumentBase.h" -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include - -//#include "TextShape.h" -#define AnnotationShape_SHAPEID "AnnotationTextShapeID" - -ReviewTool::ReviewTool(KoCanvasBase *canvas) - : TextTool(canvas) - , m_textEditor(0) - , m_textShapeData(0) - , m_canvas(canvas) - , m_textShape(0) -{ - createActions(); -} - -ReviewTool::~ReviewTool() -{ -} - -void ReviewTool::createActions() -{ - m_removeAnnotationAction = new QAction(i18n("Remove Comment"), this); - m_removeAnnotationAction->setToolTip(i18n("Remove Comment")); - addAction("remove_annotation", m_removeAnnotationAction); - connect(m_removeAnnotationAction, SIGNAL(triggered()), this, SLOT(removeAnnotation())); -} - -void ReviewTool::mouseReleaseEvent(KoPointerEvent *event) -{ - TextTool::mouseReleaseEvent(event); - event->accept(); -} -void ReviewTool::activate(KoToolBase::ToolActivation toolActivation, const QSet< KoShape * > &shapes) -{ - TextTool::activate(toolActivation, shapes); -} -void ReviewTool::deactivate() -{ - TextTool::deactivate(); -} -void ReviewTool::mouseMoveEvent(KoPointerEvent *event) -{ - TextTool::mouseMoveEvent(event); -} -void ReviewTool::mousePressEvent(KoPointerEvent *event) -{ - TextTool::mousePressEvent(event); - m_currentAnnotationShape = dynamic_cast(textShape()); -} -void ReviewTool::keyPressEvent(QKeyEvent *event) -{ - TextTool::keyPressEvent(event); -} -void ReviewTool::paint(QPainter &painter, const KoViewConverter &converter) -{ - TextTool::paint(painter, converter); -} - -QList > ReviewTool::createOptionWidgets() -{ - QList > widgets; - SimpleSpellCheckingWidget *sscw = new SimpleSpellCheckingWidget(this, 0); - SimpleAnnotationWidget *saw = new SimpleAnnotationWidget(this, 0); - - connect(saw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas())); - - sscw->setWindowTitle(i18n("Spell check")); - widgets.append(sscw); - - saw->setWindowTitle(i18n("Comments")); - widgets.append(saw); - - return widgets; -} - -void ReviewTool::removeAnnotation() -{ - if (m_currentAnnotationShape) { - QList shapes; - shapes << m_currentAnnotationShape; - canvas()->addCommand(canvas()->shapeController()->removeShapes(shapes)); - m_currentAnnotationShape = 0; - } -} diff --git a/plugins/flake/textshape/ReviewTool.h b/plugins/flake/textshape/ReviewTool.h deleted file mode 100644 index 42d00db62b..0000000000 --- a/plugins/flake/textshape/ReviewTool.h +++ /dev/null @@ -1,72 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2009 Pierre Stirnweiss - * - * 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 REVIEWTOOL_H -#define REVIEWTOOL_H - -#include -#include - -class KoPointerEvent; -class KoTextEditor; -class KoTextShapeData; -class KoViewConverter; -class TextShape; - -class QAction; - -class QPainter; -class QKeyEvent; -template class QVector; -/// This tool allows to manipulate the tracked changes of a document. You can accept or reject changes. - -#include - -class ReviewTool : public TextTool -{ - Q_OBJECT -public: - explicit ReviewTool(KoCanvasBase *canvas); - ~ReviewTool() override; - - void mouseReleaseEvent(KoPointerEvent *event) override; - void mouseMoveEvent(KoPointerEvent *event) override; - void mousePressEvent(KoPointerEvent *event) override; - void paint(QPainter &painter, const KoViewConverter &converter) override; - void keyPressEvent(QKeyEvent *event) override; - void activate(ToolActivation toolActivation, const QSet &shapes) override; - void deactivate() override; - void createActions() override; - - QList > createOptionWidgets() override; - -public Q_SLOTS: - void removeAnnotation(); - -private: - - KoTextEditor *m_textEditor; - KoTextShapeData *m_textShapeData; - QPointer m_canvas; - TextShape *m_textShape; - QAction *m_removeAnnotationAction; - KoShape *m_currentAnnotationShape; -}; - -#endif // REVIEWTOOL_H diff --git a/plugins/flake/textshape/ReviewToolFactory.cpp b/plugins/flake/textshape/ReviewToolFactory.cpp deleted file mode 100644 index ddf213c816..0000000000 --- a/plugins/flake/textshape/ReviewToolFactory.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2008 Pierre Stirnweiss \pierre.stirnweiss_calligra@gadz.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "ReviewToolFactory.h" -#include "ReviewTool.h" -#include "TextShape.h" -#include "AnnotationTextShape.h" - -#include -#include -#include - -#include -#include - -#include - -ReviewToolFactory::ReviewToolFactory() - : KoToolFactoryBase("ReviewTool") -{ - setToolTip(i18n("Review")); - setSection(dynamicToolType() + ",calligrawords,calligraauthor"); - setIconName(koIconNameCStr("tool_review")); - setPriority(30); - setActivationShapeId(TextShape_SHAPEID "," AnnotationShape_SHAPEID); -} - -ReviewToolFactory::~ReviewToolFactory() -{ -} - -KoToolBase *ReviewToolFactory::createTool(KoCanvasBase *canvas) -{ - return new ReviewTool(canvas); -} diff --git a/plugins/flake/textshape/ReviewToolFactory.h b/plugins/flake/textshape/ReviewToolFactory.h deleted file mode 100644 index ac793f6c79..0000000000 --- a/plugins/flake/textshape/ReviewToolFactory.h +++ /dev/null @@ -1,34 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2008 Pierre Stirnweiss \pierre.stirnweiss_calligra@gadz.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef REVIEWTOOLFACTORY_H -#define REVIEWTOOLFACTORY_H - -#include - -class ReviewToolFactory : public KoToolFactoryBase -{ -public: - ReviewToolFactory(); - ~ReviewToolFactory() override; - - KoToolBase *createTool(KoCanvasBase *canvas) override; -}; - -#endif diff --git a/plugins/flake/textshape/TextPlugin.cpp b/plugins/flake/textshape/TextPlugin.cpp index 01f5d31fd8..b77f545b37 100644 --- a/plugins/flake/textshape/TextPlugin.cpp +++ b/plugins/flake/textshape/TextPlugin.cpp @@ -1,51 +1,45 @@ /* 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 "TextPlugin.h" #include "TextToolFactory.h" -#include "ReferencesToolFactory.h" -#include "ReviewToolFactory.h" #ifdef CREATE_TEXTDOCUMENT_INSPECTOR #include "TextDocumentInspectionPlugin.h" #endif #include "TextShapeFactory.h" -#include "AnnotationTextShapeFactory.h" #include #include #include #include #ifdef CREATE_TEXTDOCUMENT_INSPECTOR K_PLUGIN_FACTORY_WITH_JSON(TextPluginFactory, "calligra_shape_text.json", registerPlugin(); registerPlugin(QLatin1String("TextDocumentInspection"));) #else K_PLUGIN_FACTORY_WITH_JSON(TextPluginFactory, "calligra_shape_text.json", registerPlugin();) #endif TextPlugin::TextPlugin(QObject *parent, const QVariantList &) : QObject(parent) { //KoToolRegistry::instance()->add(new TextToolFactory()); - //KoToolRegistry::instance()->add(new ReviewToolFactory()); - //KoToolRegistry::instance()->add(new ReferencesToolFactory()); KoShapeRegistry::instance()->add(new TextShapeFactory()); - //KoShapeRegistry::instance()->add(new AnnotationTextShapeFactory()); } #include diff --git a/plugins/flake/textshape/TextShape.cpp b/plugins/flake/textshape/TextShape.cpp index 712f7f5e33..6058d837e9 100644 --- a/plugins/flake/textshape/TextShape.cpp +++ b/plugins/flake/textshape/TextShape.cpp @@ -1,437 +1,467 @@ /* This file is part of the KDE project * Copyright (C) 2006-2010 Thomas Zander * Copyright (C) 2008-2010 Thorsten Zachmann * Copyright (C) 2008 Pierre Stirnweiss \pierre.stirnweiss_calligra@gadz.org> * 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. */ #include "TextShape.h" #include "ShrinkToFitShapeContainer.h" #include #include "SimpleRootAreaProvider.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 "kis_painting_tweaks.h" - TextShape::TextShape(KoInlineTextObjectManager *inlineTextObjectManager, KoTextRangeManager *textRangeManager) : KoShapeContainer(new KoTextShapeContainerModel()) , KoFrameShape(KoXmlNS::draw, "text-box") , m_pageProvider(0) , m_imageCollection(0) , m_clip(true) { setShapeId(TextShape_SHAPEID); m_textShapeData = new KoTextShapeData(); setUserData(m_textShapeData); SimpleRootAreaProvider *provider = new SimpleRootAreaProvider(m_textShapeData, this); KoTextDocument(m_textShapeData->document()).setInlineTextObjectManager(inlineTextObjectManager); KoTextDocument(m_textShapeData->document()).setTextRangeManager(textRangeManager); m_layout = new KoTextDocumentLayout(m_textShapeData->document(), provider); m_textShapeData->document()->setDocumentLayout(m_layout); setCollisionDetection(true); QObject::connect(m_layout, SIGNAL(layoutIsDirty()), m_layout, SLOT(scheduleLayout())); } +TextShape::TextShape(const TextShape &rhs) + : KoShapeContainer(new KoShapeContainerPrivate(*reinterpret_cast(rhs.d_ptr), this)), + KoFrameShape(rhs), + m_textShapeData(dynamic_cast(rhs.m_textShapeData->clone())), + m_pageProvider(0), + m_imageCollection(0), + m_clip(rhs.m_clip) +{ + reinterpret_cast(rhs.d_ptr)->model = new KoTextShapeContainerModel(); + + setShapeId(TextShape_SHAPEID); + setUserData(m_textShapeData); + + SimpleRootAreaProvider *provider = new SimpleRootAreaProvider(m_textShapeData, this); + m_layout = new KoTextDocumentLayout(m_textShapeData->document(), provider); + m_textShapeData->document()->setDocumentLayout(m_layout); + + setCollisionDetection(true); + QObject::connect(m_layout, SIGNAL(layoutIsDirty()), m_layout, SLOT(scheduleLayout())); + + updateDocumentData(); + m_layout->scheduleLayout(); +} + + TextShape::~TextShape() { } +KoShape *TextShape::cloneShape() const +{ + return new TextShape(*this); +} + void TextShape::paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) { painter.save(); applyConversion(painter, converter); KoBorder *border = this->border(); if (border) { paintBorder(painter, converter); } else if (paintContext.showTextShapeOutlines) { // No need to paint the outlines if there is a real border. if (qAbs(rotation()) > 1) { painter.setRenderHint(QPainter::Antialiasing); } QPen pen(QColor(210, 210, 210)); // use cosmetic pen QPointF onePixel = converter.viewToDocument(QPointF(1.0, 1.0)); QRectF rect(QPointF(0.0, 0.0), size() - QSizeF(onePixel.x(), onePixel.y())); painter.setPen(pen); painter.drawRect(rect); } painter.restore(); if (m_textShapeData->isDirty()) { // not layouted yet. return; } QTextDocument *doc = m_textShapeData->document(); Q_ASSERT(doc); KoTextDocumentLayout *lay = qobject_cast(doc->documentLayout()); Q_ASSERT(lay); lay->showInlineObjectVisualization(paintContext.showInlineObjectVisualization); applyConversion(painter, converter); if (background()) { QPainterPath p; p.addRect(QRectF(QPointF(), size())); background()->paint(painter, converter, paintContext, p); } // this enables to use the same shapes on different pages showing different page numbers if (m_pageProvider) { KoTextPage *page = m_pageProvider->page(this); if (page) { // this is used to not trigger repaints if layout during the painting is done m_paintRegion = KisPaintingTweaks::safeClipRegion(painter); if (!m_textShapeData->rootArea()->page() || page->pageNumber() != m_textShapeData->rootArea()->page()->pageNumber()) { m_textShapeData->rootArea()->setPage(page); // takes over ownership of the page } else { delete page; } } } KoTextDocumentLayout::PaintContext pc; QAbstractTextDocumentLayout::Selection selection; KoTextEditor *textEditor = KoTextDocument(m_textShapeData->document()).textEditor(); selection.cursor = *(textEditor->cursor()); QPalette palette = pc.textContext.palette; selection.format.setBackground(palette.brush(QPalette::Highlight)); selection.format.setForeground(palette.brush(QPalette::HighlightedText)); pc.textContext.selections.append(selection); pc.textContext.selections += KoTextDocument(doc).selections(); pc.viewConverter = &converter; pc.imageCollection = m_imageCollection; pc.showFormattingCharacters = paintContext.showFormattingCharacters; pc.showTableBorders = paintContext.showTableBorders; pc.showSectionBounds = paintContext.showSectionBounds; pc.showSpellChecking = paintContext.showSpellChecking; pc.showSelections = paintContext.showSelections; // When clipping the painter we need to make sure not to cutoff cosmetic pens which // may used to draw e.g. table-borders for user convenience when on screen (but not // on e.g. printing). Such cosmetic pens are special cause they will always have the // same pen-width (1 pixel) independent of zoom-factor or painter transformations and // are not taken into account in any border-calculations. QRectF clipRect = outlineRect(); qreal cosmeticPenX = 1 * 72. / painter.device()->logicalDpiX(); qreal cosmeticPenY = 1 * 72. / painter.device()->logicalDpiY(); painter.setClipRect(clipRect.adjusted(-cosmeticPenX, -cosmeticPenY, cosmeticPenX, cosmeticPenY), Qt::IntersectClip); painter.save(); painter.translate(0, -m_textShapeData->documentOffset()); m_textShapeData->rootArea()->paint(&painter, pc); // only need to draw ourselves painter.restore(); m_paintRegion = QRegion(); } QPointF TextShape::convertScreenPos(const QPointF &point) const { QPointF p = absoluteTransformation(0).inverted().map(point); return p + QPointF(0.0, m_textShapeData->documentOffset()); } QPainterPath TextShape::outline() const { QPainterPath path; path.addRect(QRectF(QPointF(0, 0), size())); return path; } QRectF TextShape::outlineRect() const { if (m_textShapeData->rootArea()) { QRectF rect = m_textShapeData->rootArea()->boundingRect(); rect.moveTop(rect.top() - m_textShapeData->rootArea()->top()); if (m_clip) { rect.setHeight(size().height()); } return rect | QRectF(QPointF(0, 0), size()); } return QRectF(QPointF(0, 0), size()); } void TextShape::shapeChanged(ChangeType type, KoShape *shape) { Q_UNUSED(shape); KoShapeContainer::shapeChanged(type, shape); if (type == PositionChanged || type == SizeChanged || type == CollisionDetected) { m_textShapeData->setDirty(); } } void TextShape::saveOdf(KoShapeSavingContext &context) const { KoXmlWriter &writer = context.xmlWriter(); QString textHeight = additionalAttribute("fo:min-height"); const_cast(this)->removeAdditionalAttribute("fo:min-height"); writer.startElement("draw:frame"); // if the TextShape is wrapped in a shrink to fit container we need to save the geometry of the container as // the geomerty of the shape might have been changed. if (ShrinkToFitShapeContainer *stf = dynamic_cast(this->parent())) { stf->saveOdfAttributes(context, OdfSize | OdfPosition | OdfTransformation); saveOdfAttributes(context, OdfAdditionalAttributes | OdfMandatories | OdfCommonChildElements); } else { saveOdfAttributes(context, OdfAllAttributes); } writer.startElement("draw:text-box"); if (!textHeight.isEmpty()) { writer.addAttribute("fo:min-height", textHeight); } KoTextDocumentLayout *lay = qobject_cast(m_textShapeData->document()->documentLayout()); int index = -1; if (lay) { int i = 0; foreach (KoShape *shape, lay->shapes()) { if (shape == this) { index = i; } else if (index >= 0) { writer.addAttribute("draw:chain-next-name", shape->name()); break; } ++i; } } const bool saveMyText = index == 0; // only save the text once. m_textShapeData->saveOdf(context, 0, 0, saveMyText ? -1 : 0); writer.endElement(); // draw:text-box saveOdfCommonChildElements(context); writer.endElement(); // draw:frame } QString TextShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const { Qt::Alignment vAlign(m_textShapeData->verticalAlignment()); QString verticalAlign = "top"; if (vAlign == Qt::AlignBottom) { verticalAlign = "bottom"; } else if (vAlign == Qt::AlignVCenter) { verticalAlign = "middle"; } style.addProperty("draw:textarea-vertical-align", verticalAlign); KoTextShapeData::ResizeMethod resize = m_textShapeData->resizeMethod(); if (resize == KoTextShapeData::AutoGrowWidth || resize == KoTextShapeData::AutoGrowWidthAndHeight) { style.addProperty("draw:auto-grow-width", "true"); } if (resize != KoTextShapeData::AutoGrowHeight && resize != KoTextShapeData::AutoGrowWidthAndHeight) { style.addProperty("draw:auto-grow-height", "false"); } if (resize == KoTextShapeData::ShrinkToFitResize) { style.addProperty("draw:fit-to-size", "true"); } m_textShapeData->saveStyle(style, context); return KoShape::saveStyle(style, context); } void TextShape::loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context) { KoShape::loadStyle(element, context); KoStyleStack &styleStack = context.odfLoadingContext().styleStack(); styleStack.setTypeProperties("graphic"); QString verticalAlign(styleStack.property(KoXmlNS::draw, "textarea-vertical-align")); Qt::Alignment alignment(Qt::AlignTop); if (verticalAlign == "bottom") { alignment = Qt::AlignBottom; } else if (verticalAlign == "justify") { // not yet supported alignment = Qt::AlignVCenter; } else if (verticalAlign == "middle") { alignment = Qt::AlignVCenter; } m_textShapeData->setVerticalAlignment(alignment); const QString fitToSize = styleStack.property(KoXmlNS::draw, "fit-to-size"); KoTextShapeData::ResizeMethod resize = KoTextShapeData::NoResize; if (fitToSize == "true" || fitToSize == "shrink-to-fit") { // second is buggy value from impress resize = KoTextShapeData::ShrinkToFitResize; } else { // An explicit svg:width or svg:height defined do change the default value (means those value // used if not explicit defined otherwise) for auto-grow-height and auto-grow-height. So // they are mutable exclusive. // It is not clear (means we did not test and took care of it) what happens if both are // defined and are in conflict with each other or how the fit-to-size is related to this. QString autoGrowWidth = styleStack.property(KoXmlNS::draw, "auto-grow-width"); if (autoGrowWidth.isEmpty()) { autoGrowWidth = element.hasAttributeNS(KoXmlNS::svg, "width") ? "false" : "true"; } QString autoGrowHeight = styleStack.property(KoXmlNS::draw, "auto-grow-height"); if (autoGrowHeight.isEmpty()) { autoGrowHeight = element.hasAttributeNS(KoXmlNS::svg, "height") ? "false" : "true"; } if (autoGrowWidth == "true") { resize = autoGrowHeight == "true" ? KoTextShapeData::AutoGrowWidthAndHeight : KoTextShapeData::AutoGrowWidth; } else if (autoGrowHeight == "true") { resize = KoTextShapeData::AutoGrowHeight; } } m_textShapeData->setResizeMethod(resize); } bool TextShape::loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) { m_textShapeData->document()->setUndoRedoEnabled(false); loadOdfAttributes(element, context, OdfAllAttributes); // this cannot be done in loadStyle as that fills the style stack incorrectly and therefore // it results in wrong data being loaded. m_textShapeData->loadStyle(element, context); #ifndef NWORKAROUND_ODF_BUGS KoTextShapeData::ResizeMethod method = m_textShapeData->resizeMethod(); if (KoOdfWorkaround::fixAutoGrow(method, context)) { KoTextDocumentLayout *lay = qobject_cast(m_textShapeData->document()->documentLayout()); Q_ASSERT(lay); if (lay) { SimpleRootAreaProvider *provider = dynamic_cast(lay->provider()); if (provider) { provider->m_fixAutogrow = true; } } } #endif bool answer = loadOdfFrame(element, context); m_textShapeData->document()->setUndoRedoEnabled(true); return answer; } bool TextShape::loadOdfFrame(const KoXmlElement &element, KoShapeLoadingContext &context) { // If the loadOdfFrame from the base class for draw:text-box fails, check // for table:table, because that is a legal child of draw:frame in ODF 1.2. if (!KoFrameShape::loadOdfFrame(element, context)) { const KoXmlElement &possibleTableElement(KoXml::namedItemNS(element, KoXmlNS::table, "table")); if (possibleTableElement.isNull()) { return false; } else { return loadOdfFrameElement(possibleTableElement, context); } } return true; } bool TextShape::loadOdfFrameElement(const KoXmlElement &element, KoShapeLoadingContext &context) { bool ok = m_textShapeData->loadOdf(element, context, 0, this); if (ok) { ShrinkToFitShapeContainer::tryWrapShape(this, element, context); } return ok; } void TextShape::update() const { KoShapeContainer::update(); } void TextShape::updateAbsolute(const QRectF &shape) const { // this is done to avoid updates which are called during the paint event and not needed. //if (!m_paintRegion.contains(shape.toRect())) { //KoShape::update(shape); //} // FIXME: just a hack to remove outdated call from the deprecated shape KoShape::updateAbsolute(shape); } void TextShape::waitUntilReady(const KoViewConverter &, bool asynchronous) const { Q_UNUSED(asynchronous); KoTextDocumentLayout *lay = qobject_cast(m_textShapeData->document()->documentLayout()); Q_ASSERT(lay); if (m_textShapeData->isDirty()) { // Do a simple layout-call which will make sure to relayout till things are done. If more // layouts are scheduled then we don't need to wait for them here but can just continue. lay->layout(); } } KoImageCollection *TextShape::imageCollection() { return m_imageCollection; } void TextShape::updateDocumentData() { if (m_layout) { KoTextDocument document(m_textShapeData->document()); m_layout->setStyleManager(document.styleManager()); m_layout->setInlineTextObjectManager(document.inlineTextObjectManager()); m_layout->setTextRangeManager(document.textRangeManager()); m_layout->setChangeTracker(document.changeTracker()); } } diff --git a/plugins/flake/textshape/TextShape.h b/plugins/flake/textshape/TextShape.h index 9fff5ce45a..411b91c8d5 100644 --- a/plugins/flake/textshape/TextShape.h +++ b/plugins/flake/textshape/TextShape.h @@ -1,143 +1,147 @@ /* This file is part of the KDE project * Copyright (C) 2006-2010 Thomas Zander * Copyright (C) 2008 Thorsten Zachmann * Copyright (C) 2008 Pierre Stirnweiss \pierre.stirnweiss_calligra@gadz.org> * 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 KOTEXTSHAPE_H #define KOTEXTSHAPE_H #include #include #include #include #include #include #define TextShape_SHAPEID "TextShapeID" class KoInlineTextObjectManager; class KoTextRangeManager; class KoPageProvider; class KoImageCollection; class KoTextDocument; class TextShape; class KoTextDocumentLayout; class KoParagraphStyle; /** * A text shape. * The Text shape is capable of drawing structured text. * @see KoTextShapeData */ class TextShape : public KoShapeContainer, public KoFrameShape { public: TextShape(KoInlineTextObjectManager *inlineTextObjectManager, KoTextRangeManager *textRangeManager); ~TextShape() override; + KoShape* cloneShape() const override; + /// reimplemented void paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext) override; /// reimplemented void waitUntilReady(const KoViewConverter &converter, bool asynchronous) const override; /// helper method. QPointF convertScreenPos(const QPointF &point) const; /// reimplemented QPainterPath outline() const override; /// reimplemented QRectF outlineRect() const override; ///reimplemented ChildZOrderPolicy childZOrderPolicy() override { return KoShape::ChildZPassThrough; } /// set the image collection which is needed to draw bullet from images void setImageCollection(KoImageCollection *collection) { m_imageCollection = collection; } KoImageCollection *imageCollection(); /** * From KoShape reimplemented method to load the TextShape from ODF. * * This method redirects the call to the KoTextShapeData::loadOdf() method which * in turn will call the KoTextLoader::loadBody() method that reads the element * into a QTextCursor. * * @param context the KoShapeLoadingContext used for loading. * @param element element which represents the shape in odf. * @return false if loading failed. */ bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) override; /** * From KoShape reimplemented method to store the TextShape data as ODF. * * @param context the KoShapeSavingContext used for saving. */ void saveOdf(KoShapeSavingContext &context) const override; KoTextShapeData *textShapeData() { return m_textShapeData; } void updateDocumentData(); void update() const override; void updateAbsolute(const QRectF &shape) const override; // required for kpresenter hack void setPageProvider(KoPageProvider *provider) { m_pageProvider = provider; } /// reimplemented bool loadOdfFrame(const KoXmlElement &element, KoShapeLoadingContext &context) override; protected: bool loadOdfFrameElement(const KoXmlElement &element, KoShapeLoadingContext &context) override; /// reimplemented void loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context) override; /// reimplemented QString saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const override; + TextShape(const TextShape &rhs); + private: void shapeChanged(ChangeType type, KoShape *shape = 0) override; KoTextShapeData *m_textShapeData; KoPageProvider *m_pageProvider; KoImageCollection *m_imageCollection; QRegion m_paintRegion; bool m_clip; KoTextDocumentLayout *m_layout; }; #endif diff --git a/plugins/flake/textshape/TextTool.cpp b/plugins/flake/textshape/TextTool.cpp index 7f1097536b..5eaff5164a 100644 --- a/plugins/flake/textshape/TextTool.cpp +++ b/plugins/flake/textshape/TextTool.cpp @@ -1,3164 +1,3137 @@ /* 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 "dialogs/SimpleTableWidget.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 "AnnotationTextShape.h" -#define AnnotationShape_SHAPEID "AnnotationTextShapeID" #include "KoShapeBasedDocumentBase.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(KoCanvasResourceManager::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()); ++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); } } 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(KoCanvasResourceManager::ApplicationSpeciality) & KoCanvasResourceManager::NoAdvancedText); KisActionRegistry *actionRegistry = KisActionRegistry::instance(); // FIXME: find new icons for these m_actionConfigureSection = actionRegistry->makeQAction("configure_section", this); 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); connect(m_actionInsertSection, SIGNAL(triggered(bool)), this, SLOT(insertNewSection())); m_actionSplitSections = actionRegistry->makeQAction("split_sections", this); 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); connect(m_actionPasteAsText, SIGNAL(triggered(bool)), this, SLOT(pasteAsText())); m_actionFormatBold = actionRegistry->makeQAction("format_bold", this); 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); 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); 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); 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); 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); 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); 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); 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); connect(m_actionChangeDirection, SIGNAL(triggered()), this, SLOT(textDirectionChanged())); m_actionFormatSuper = actionRegistry->makeQAction("format_super", this); m_actionFormatSuper->setCheckable(true); 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); 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); connect(m_actionFormatIncreaseIndent, SIGNAL(triggered()), this, SLOT(increaseIndent())); m_actionFormatDecreaseIndent = actionRegistry->makeQAction("format_decreaseindent", this); 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); action = actionRegistry->makeQAction("format_numberlist", this); addAction("format_numberlist", action); action = actionRegistry->makeQAction("fontsizeup", this); addAction("fontsizeup", action); connect(action, SIGNAL(triggered()), this, SLOT(increaseFontSize())); action = actionRegistry->makeQAction("fontsizedown", this); 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); connect(m_actionFormatFontFamily, SIGNAL(triggered(QString)), this, SLOT(setFontFamily(QString))); m_variableMenu = new KActionMenu(i18n("Variable"), this); addAction("insert_variable", m_variableMenu); // ------------------- Actions with a key binding and no GUI item action = actionRegistry->makeQAction("nonbreaking_space", this); addAction("nonbreaking_space", action); connect(action, SIGNAL(triggered()), this, SLOT(nonbreakingSpace())); action = actionRegistry->makeQAction("nonbreaking_hyphen", this); addAction("nonbreaking_hyphen", action); connect(action, SIGNAL(triggered()), this, SLOT(nonbreakingHyphen())); action = actionRegistry->makeQAction("insert_index", this); 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); connect(action, SIGNAL(triggered()), this, SLOT(softHyphen())); if (useAdvancedText) { action = actionRegistry->makeQAction("line_break", this); addAction("line_break", action); connect(action, SIGNAL(triggered()), this, SLOT(lineBreak())); action = actionRegistry->makeQAction("insert_framebreak", this); addAction("insert_framebreak", action); connect(action, SIGNAL(triggered()), this, SLOT(insertFrameBreak())); } action = actionRegistry->makeQAction("format_font", this); addAction("format_font", action); connect(action, SIGNAL(triggered()), this, SLOT(selectFont())); m_actionFormatFontSize = new FontSizeAction(i18n("Font Size"), this); 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); connect(m_actionFormatTextColor, SIGNAL(colorChanged(KoColor)), this, SLOT(setTextColor(KoColor))); m_actionFormatBackgroundColor = new KoColorPopupAction(this); 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); 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); connect(m_growHeightAction, SIGNAL(triggered(bool)), this, SLOT(setGrowHeightToFit(bool))); m_shrinkToFitAction = actionRegistry->makeQAction("shrink_to_fit", this); 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); connect(action, SIGNAL(triggered()), this, SLOT(insertTable())); action = actionRegistry->makeQAction("insert_tablerow_above", this); 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); connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableRowBelow())); action = actionRegistry->makeQAction("insert_tablecolumn_left", this); 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); connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableColumnRight())); action = actionRegistry->makeQAction("delete_tablecolumn", this); addAction("delete_tablecolumn", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteTableColumn())); action = actionRegistry->makeQAction("delete_tablerow", this); addAction("delete_tablerow", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteTableRow())); action = actionRegistry->makeQAction("merge_tablecells", this); addAction("merge_tablecells", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(mergeTableCells())); action = actionRegistry->makeQAction("split_tablecells", this); addAction("split_tablecells", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(splitTableCells())); action = actionRegistry->makeQAction("activate_borderpainter", this); addAction("activate_borderpainter", action); } action = actionRegistry->makeQAction("format_paragraph", this); addAction("format_paragraph", action); connect(action, SIGNAL(triggered()), this, SLOT(formatParagraph())); action = actionRegistry->makeQAction("format_stylist", this); addAction("format_stylist", action); connect(action, SIGNAL(triggered()), this, SLOT(showStyleManager())); action = KStandardAction::selectAll(this, SLOT(selectAll()), this); addAction("edit_select_all", action); action = actionRegistry->makeQAction("insert_specialchar", this); addAction("insert_specialchar", action); connect(action, SIGNAL(triggered()), this, SLOT(insertSpecialCharacter())); action = actionRegistry->makeQAction("repaint", this); addAction("repaint", action); connect(action, SIGNAL(triggered()), this, SLOT(relayoutContent())); action = actionRegistry->makeQAction("insert_annotation", this); 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); connect(action, SIGNAL(triggered()), this, SLOT(debugTextDocument())); action = actionRegistry->makeQAction("detailed_debug_styles", this); 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; } 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(KoCanvasResourceManager::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); } } } KoCanvasResourceManager *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()); ++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 = !dynamic_cast(m_textShape); + 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(KoCanvasResourceManager::ApplicationSpeciality) & KoCanvasResourceManager::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(KoCanvasResourceManager::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(KoCanvasResourceManager::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(KoCanvasResourceManager::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(KoCanvasResourceManager::ApplicationSpeciality) & KoCanvasResourceManager::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 genearted 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 == KoCanvasResourceManager::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) { KoTextDocument document(m_textShapeData->document()); KoStyleManager *styleManager = document.styleManager(); KoCharacterStyle *originalCharStyle = styleManager->characterStyle(m_textEditor.data()->charFormat().intProperty(KoCharacterStyle::StyleId)); KoCharacterStyle *autoStyle; if (!originalCharStyle) { KoCharacterStyle blankStyle; 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); // elipses } 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() { - AnnotationTextShape *shape = (AnnotationTextShape *)KoShapeRegistry::instance()->value(AnnotationShape_SHAPEID)->createDefaultShape(canvas()->shapeController()->resourceManager()); - textEditor()->addAnnotation(shape); - - // Set annotation creator. - KConfig cfg("kritarc"); - cfg.reparseConfiguration(); - KConfigGroup authorGroup(&cfg, "Author"); - QStringList profiles = authorGroup.readEntry("profile-names", QStringList()); - KSharedConfig::openConfig()->reparseConfiguration(); - KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author"); - QString profile = appAuthorGroup.readEntry("active-profile", ""); - KConfigGroup cgs(&authorGroup, "Author-" + profile); - - if (profiles.contains(profile)) { - KConfigGroup cgs(&authorGroup, "Author-" + profile); - shape->setCreator(cgs.readEntry("creator")); - } else { - if (profile == "anonymous") { - shape->setCreator("Anonymous"); - } else { - KUser user(KUser::UseRealUserID); - shape->setCreator(user.property(KUser::FullName).toString()); - } - } - // Set Annotation creation date. - shape->setDate(QDate::currentDate().toString(Qt::ISODate)); + // no annotations anymore, sorry :( } diff --git a/plugins/flake/textshape/TextToolFactory.cpp b/plugins/flake/textshape/TextToolFactory.cpp index d032de3f64..eebc537f5f 100644 --- a/plugins/flake/textshape/TextToolFactory.cpp +++ b/plugins/flake/textshape/TextToolFactory.cpp @@ -1,45 +1,44 @@ /* 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 "TextToolFactory.h" #include "TextTool.h" #include "TextShape.h" -#include "AnnotationTextShape.h" #include #include TextToolFactory::TextToolFactory() : KoToolFactoryBase("TextTool") { setToolTip(i18n("Text editing")); setSection(dynamicToolType() + ",calligrawords,calligraauthor"); setIconName(koIconNameCStr("tool-text")); setPriority(2); - setActivationShapeId(TextShape_SHAPEID "," AnnotationShape_SHAPEID); + setActivationShapeId(TextShape_SHAPEID); } TextToolFactory::~TextToolFactory() { } KoToolBase *TextToolFactory::createTool(KoCanvasBase *canvas) { return new TextTool(canvas); } diff --git a/plugins/flake/textshape/dialogs/CitationInsertionDialog.cpp b/plugins/flake/textshape/dialogs/CitationInsertionDialog.cpp deleted file mode 100644 index 31f074a0aa..0000000000 --- a/plugins/flake/textshape/dialogs/CitationInsertionDialog.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2011 Smit Patel - * - * 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 "CitationInsertionDialog.h" - -#include -#include -#include -#include - -#include - -CitationInsertionDialog::CitationInsertionDialog(KoTextEditor *editor, QWidget *parent) - : QDialog(parent) - , m_blockSignals(false) - , m_editor(editor) -{ - dialog.setupUi(this); - connect(dialog.buttonBox, SIGNAL(accepted()), this, SLOT(insert())); - connect(dialog.existingCites, SIGNAL(currentIndexChanged(QString)), this, SLOT(selectionChangedFromExistingCites())); - - QStringList existingCites(i18n("Select")); - foreach (KoInlineCite *cite, KoTextDocument(m_editor->document()).inlineTextObjectManager()->citations().values()) { - existingCites << cite->identifier(); - m_cites[cite->identifier()] = cite; - } - existingCites.removeDuplicates(); - dialog.existingCites->addItems(existingCites); - - show(); -} - -void CitationInsertionDialog::insert() -{ - if (m_cites.contains(dialog.shortName->text())) { - if (*m_cites.value(dialog.shortName->text()) != *toCite()) { //prompts if values are changed - int ret = QMessageBox::warning(this, i18n("Warning"), i18n("The document already contains the bibliography entry with different data.\n" - "Do you want to adjust existing entries?"), QMessageBox::Yes | QMessageBox::No); - if (ret == QMessageBox::Yes) { - Q_FOREACH (KoInlineCite *existingCite, m_cites.values(dialog.shortName->text())) { - *existingCite = *toCite(); //update all cites with new values - existingCite->setType(KoInlineCite::ClonedCitation); //change type to ClonedCitation - } - emit accept(); - } else { - return; - } - } - } - KoInlineCite *cite = m_editor->insertCitation(); - if (dialog.shortName->text().isEmpty()) { - const int number = - KoTextDocument(m_editor->document()).inlineTextObjectManager()->citations().count(); - dialog.shortName->setText(i18n("Short name%1", number)); - - dialog.shortName->setSelection(dialog.shortName->text().length(), 0); - } - *cite = *toCite(); - emit accept(); -} - -void CitationInsertionDialog::selectionChangedFromExistingCites() -{ - if (dialog.existingCites->currentIndex() != 0) { - KoInlineCite *cite = m_cites[dialog.existingCites->currentText()]; - this->fillValuesFrom(cite); - } else if (dialog.existingCites->currentIndex() == 0) { - KoInlineCite *blankCite = new KoInlineCite(KoInlineCite::Citation); - blankCite->setBibliographyType("Article"); //default bibliography type - const int number = - KoTextDocument(m_editor->document()).inlineTextObjectManager()->citations().count() + 1; - blankCite->setIdentifier(i18n("Short name%1", number)); - fillValuesFrom(blankCite); - } -} - -KoInlineCite *CitationInsertionDialog::toCite() -{ - KoInlineCite *cite = new KoInlineCite(KoInlineCite::Citation); - cite->setAddress(dialog.address->text()); - cite->setAnnotation(dialog.annotation->text()); - cite->setAuthor(dialog.author->text()); - cite->setBibliographyType(dialog.sourceType->currentText().remove(QLatin1Char(' ')).toLower()); //removing spaces and lowering case for exact tag attribute of bibliography-type - cite->setBookTitle(dialog.booktitle->text()); - cite->setChapter(dialog.chapter->text()); - cite->setCustom1(dialog.ud1->text()); - cite->setCustom2(dialog.ud2->text()); - cite->setCustom3(dialog.ud3->text()); - cite->setCustom4(dialog.ud4->text()); - cite->setCustom5(dialog.ud5->text()); - cite->setEdition(dialog.edition->text()); - cite->setEditor(dialog.editor->text()); - cite->setIdentifier(dialog.shortName->text()); - cite->setInstitution(dialog.institution->text()); - cite->setISBN(dialog.isbn->text()); - cite->setISSN(dialog.issn->text()); - cite->setJournal(dialog.journal->text()); - cite->setMonth(dialog.month->text()); - cite->setNote(dialog.note->text()); - cite->setNumber(dialog.number->text()); - cite->setOrganisation(dialog.organisation->text()); - cite->setPages(dialog.pages->text()); - cite->setPublicationType(dialog.publication->text()); - cite->setPublisher(dialog.publisher->text()); - cite->setReportType(dialog.reporttype->text()); - cite->setSchool(dialog.school->text()); - cite->setSeries(dialog.series->text()); - cite->setTitle(dialog.title->text()); - cite->setURL(dialog.url->text()); - cite->setVolume(dialog.volume->text()); - cite->setYear(dialog.year->text()); - return cite; -} - -void CitationInsertionDialog::fillValuesFrom(KoInlineCite *cite) -{ - dialog.address->setText(cite->address()); - dialog.annotation->setText(cite->annotation()); - dialog.author->setText(cite->author()); - dialog.sourceType->setCurrentIndex(dialog.sourceType->findText(cite->bibliographyType(), Qt::MatchFixedString)); - dialog.booktitle->setText(cite->bookTitle()); - dialog.chapter->setText(cite->chapter()); - dialog.ud1->setText(cite->custom1()); - dialog.ud2->setText(cite->custom2()); - dialog.ud3->setText(cite->custom3()); - dialog.ud4->setText(cite->custom4()); - dialog.ud5->setText(cite->custom5()); - dialog.edition->setText(cite->edition()); - dialog.editor->setText(cite->editor()); - dialog.institution->setText(cite->institution()); - dialog.shortName->setText(cite->identifier()); - dialog.isbn->setText(cite->isbn()); - dialog.issn->setText(cite->issn()); - dialog.journal->setText(cite->journal()); - dialog.month->setText(cite->month()); - dialog.note->setText(cite->note()); - dialog.number->setText(cite->number()); - dialog.organisation->setText(cite->organisations()); - dialog.pages->setText(cite->pages()); - dialog.publication->setText(cite->publicationType()); - dialog.publisher->setText(cite->publisher()); - dialog.school->setText(cite->school()); - dialog.series->setText(cite->series()); - dialog.title->setText(cite->title()); - dialog.reporttype->setText(cite->reportType()); - dialog.volume->setText(cite->volume()); - dialog.year->setText(cite->year()); - dialog.url->setText(cite->url()); -} diff --git a/plugins/flake/textshape/dialogs/CitationInsertionDialog.h b/plugins/flake/textshape/dialogs/CitationInsertionDialog.h deleted file mode 100644 index ef4cee73d9..0000000000 --- a/plugins/flake/textshape/dialogs/CitationInsertionDialog.h +++ /dev/null @@ -1,47 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2011 Smit Patel - * - * 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 CITATIONINSERTIONDIALOG_H -#define CITATIONINSERTIONDIALOG_H - -#include "ui_CitationInsertionDialog.h" -#include -#include - -class KoInlineCite; - -class CitationInsertionDialog : public QDialog -{ - Q_OBJECT -public: - explicit CitationInsertionDialog(KoTextEditor *editor, QWidget *parent = 0); - KoInlineCite *toCite(); //returns cite with values filled in form - void fillValuesFrom(KoInlineCite *cite); //fills form with values in cite - -public Q_SLOTS: - void insert(); - void selectionChangedFromExistingCites(); - -private: - Ui::CitationInsertionDialog dialog; - bool m_blockSignals; - KoTextEditor *m_editor; - QMap m_cites; -}; - -#endif // CITATIONBIBLIOGRAPHYDIALOG_H diff --git a/plugins/flake/textshape/dialogs/CitationInsertionDialog.ui b/plugins/flake/textshape/dialogs/CitationInsertionDialog.ui deleted file mode 100644 index 8f10f64f6a..0000000000 --- a/plugins/flake/textshape/dialogs/CitationInsertionDialog.ui +++ /dev/null @@ -1,609 +0,0 @@ - - - CitationInsertionDialog - - - Qt::ApplicationModal - - - - 0 - 0 - 744 - 614 - - - - Qt::NoFocus - - - Insert Bibliography entry - - - false - - - true - - - - - - Insert Bibliography Entry Details - - - - - - Short Name: - - - - - - - - - - - - - - Source: - - - - - - - - Article - - - - - Book - - - - - Booklet - - - - - Conference - - - - - Email - - - - - Inbook - - - - - Incollection - - - - - Inproceedings - - - - - Journal - - - - - Manual - - - - - Masters thesis - - - - - Misc - - - - - PhD thesis - - - - - Proceedings - - - - - Tech Report - - - - - Unpublished - - - - - WWW - - - - - custom 1 - - - - - custom 2 - - - - - custom 3 - - - - - custom 4 - - - - - custom 5 - - - - - - - - Author: - - - - - - - - - - Title: - - - - - - - - - - Year: - - - - - - - - - - Publisher: - - - - - - - - - - Address: - - - - - - - - - - ISBN: - - - - - - - - - - Chapter: - - - - - - - - - - ISSN: - - - - - - - - - - Editor: - - - - - - - - - - Page(s): - - - - - - - - - - Book title: - - - - - - - - - - Edition: - - - - - - - - - - Publication: - - - - - - - - - - Volume: - - - - - - - - - - Institution: - - - - - - - - - - Organisation: - - - - - - - - - - Type of Report: - - - - - - - - - - University: - - - - - - - - - - Journal: - - - - - - - - - - Month: - - - - - - - - - - Series: - - - - - - - - - - Number: - - - - - - - - - - Note: - - - - - - - - - - Annotation: - - - - - - - - - - URL: - - - - - - - - - - User-defined 1: - - - - - - - - - - User-defined 3: - - - - - - - User-defined 2: - - - - - - - - - - - - - User-defined 4: - - - - - - - - - - User-defined 5: - - - - - - - - - - - - - - - - 12 - 75 - true - - - - OR - - - - - - - Qt::Horizontal - - - - 668 - 20 - - - - - - - - - - - - From the document: - - - - - - - - - - - - - - Qt::Horizontal - - - - 528 - 20 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - - KComboBox - QComboBox -
kcombobox.h
-
-
- - shortName - sourceType - author - title - year - publisher - address - isbn - chapter - issn - editor - pages - booktitle - edition - publication - volume - institution - organisation - reporttype - school - journal - month - series - number - note - annotation - url - ud1 - ud2 - ud3 - ud4 - ud5 - existingCites - buttonBox - - - - - buttonBox - rejected() - CitationInsertionDialog - reject() - - - 684 - 589 - - - 497 - 586 - - - - -
diff --git a/plugins/flake/textshape/dialogs/InsertBibliographyDialog.cpp b/plugins/flake/textshape/dialogs/InsertBibliographyDialog.cpp deleted file mode 100644 index 80ed4f0dae..0000000000 --- a/plugins/flake/textshape/dialogs/InsertBibliographyDialog.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2011 Smit Patel - * - * 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 "InsertBibliographyDialog.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -InsertBibliographyDialog::InsertBibliographyDialog(KoTextEditor *editor, QWidget *parent) - : QDialog(parent) - , m_editor(editor) - , m_bibInfo(new KoBibliographyInfo()) -{ - dialog.setupUi(this); - - connect(dialog.bibTypes, SIGNAL(currentTextChanged(QString)), this, SLOT(updateFields())); - connect(dialog.buttonBox, SIGNAL(accepted()), this, SLOT(insert())); - connect(dialog.add, SIGNAL(clicked()), this, SLOT(addField())); - connect(dialog.remove, SIGNAL(clicked()), this, SLOT(removeField())); - connect(dialog.span, SIGNAL(clicked()), this, SLOT(addSpan())); - connect(dialog.addedFields, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(spanChanged(QListWidgetItem*))); - - /* To do : handle tab stops - */ - //connect(dialog.addTabStop,SIGNAL(clicked()),this,SLOT(insertTabStop())); - //connect(dialog.removeTabStop,SIGNAL(clicked()),this,SLOT(removeTabStop())); - - dialog.addedFields->clear(); - dialog.availableFields->clear(); - m_bibInfo->m_entryTemplate = BibliographyGenerator::defaultBibliographyEntryTemplates(); - dialog.bibTypes->setCurrentRow(0, QItemSelectionModel::Select); - show(); -} - -QString InsertBibliographyDialog::bibliographyType() -{ - return dialog.bibTypes->currentItem()->text().remove(' ').toLower(); -} - -void InsertBibliographyDialog::insert() -{ - m_bibInfo->m_indexTitleTemplate.text = dialog.title->text(); - m_editor->insertBibliography(m_bibInfo); -} - -void InsertBibliographyDialog::updateFields() -{ - dialog.availableFields->clear(); - dialog.addedFields->clear(); - - QSet addedFields; - Q_FOREACH (IndexEntry *entry, m_bibInfo->m_entryTemplate[bibliographyType()].indexEntries) { - if (entry->name == IndexEntry::BIBLIOGRAPHY) { - IndexEntryBibliography *bibEntry = static_cast(entry); - QListWidgetItem *bibItem = new QListWidgetItem(bibEntry->dataField, dialog.addedFields); - addedFields.insert(bibEntry->dataField); - bibItem->setData(Qt::UserRole, QVariant::fromValue(IndexEntry::BIBLIOGRAPHY)); - } else if (entry->name == IndexEntry::SPAN) { - IndexEntrySpan *span = static_cast(entry); - QListWidgetItem *spanField = new QListWidgetItem(span->text, dialog.addedFields); - addedFields.insert(span->text); - spanField->setData(Qt::UserRole, QVariant::fromValue(IndexEntry::SPAN)); - spanField->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); - } - } - QSet availableFields = QSet::fromList(KoOdfBibliographyConfiguration::bibDataFields) - addedFields; - - foreach (const QString &field, availableFields) { - new QListWidgetItem(field, dialog.availableFields); - } - dialog.availableFields->sortItems(); -} - -void InsertBibliographyDialog::addField() -{ - int row = dialog.availableFields->currentRow(); - - if (row != -1) { - - disconnect(dialog.addedFields, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(spanChanged(QListWidgetItem*))); - - QString newDataField = dialog.availableFields->takeItem(row)->text(); - QListWidgetItem *bibField = new QListWidgetItem(newDataField, dialog.addedFields); - bibField->setData(Qt::UserRole, QVariant::fromValue(IndexEntry::BIBLIOGRAPHY)); - - IndexEntryBibliography *newEntry = new IndexEntryBibliography(QString()); - newEntry->dataField = newDataField; - - m_bibInfo->m_entryTemplate[bibliographyType()].indexEntries.append(static_cast(newEntry)); - connect(dialog.addedFields, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(spanChanged(QListWidgetItem*))); - } -} - -void InsertBibliographyDialog::removeField() -{ - int row = dialog.addedFields->currentRow(); - - if (row != -1) { - if (dialog.addedFields->currentItem()->data(Qt::UserRole).value() == IndexEntry::BIBLIOGRAPHY) { - new QListWidgetItem(dialog.addedFields->takeItem(row)->text(), dialog.availableFields); - dialog.availableFields->sortItems(); - } else { - dialog.availableFields->removeItemWidget(dialog.addedFields->takeItem(row)); - } - - m_bibInfo->m_entryTemplate[bibliographyType()].indexEntries.removeAt(row); - } -} - -void InsertBibliographyDialog::addSpan() -{ - QString spanText = (dialog.addedFields->count() == 1) ? QString(":") : QString(","); - QListWidgetItem *spanField = new QListWidgetItem(spanText, dialog.addedFields); - spanField->setData(Qt::UserRole, QVariant::fromValue(IndexEntry::SPAN)); - spanField->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - IndexEntrySpan *span = new IndexEntrySpan(QString()); - span->text = spanText; - - m_bibInfo->m_entryTemplate[bibliographyType()].indexEntries.append(static_cast(span)); -} - -void InsertBibliographyDialog::spanChanged(QListWidgetItem *item) -{ - int row = dialog.addedFields->currentRow(); - - if (row != -1) { - IndexEntrySpan *span = static_cast(m_bibInfo->m_entryTemplate[bibliographyType()].indexEntries.at(row)); - span->text = item->text(); - } -} - -void InsertBibliographyDialog::insertTabStop() -{ - /*QListWidgetItem *tabStopItem = new QListWidgetItem(QString("Tab stop"),dialog.availableFields); - - IndexEntryTabStop *tabStop = new IndexEntryTabStop(QString());*/ -} - -void InsertBibliographyDialog::removeTabStop() -{ - /*int row = dialog.addedFields->row(dialog.addedFields->currentItem()); - - if (row != -1 && dialog.addedFields->takeItem(row)->text() == "Tab stop") { - dialog.addedFields->removeItemWidget(dialog.addedFields->takeItem(row)); - }*/ -} diff --git a/plugins/flake/textshape/dialogs/InsertBibliographyDialog.h b/plugins/flake/textshape/dialogs/InsertBibliographyDialog.h deleted file mode 100644 index beea4215c4..0000000000 --- a/plugins/flake/textshape/dialogs/InsertBibliographyDialog.h +++ /dev/null @@ -1,56 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2011 Smit Patel - * - * 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 INSERTBIBLIOGRAPHYDIALOG_H -#define INSERTBIBLIOGRAPHYDIALOG_H - -#include "ui_InsertBibliographyDialog.h" - -#include -#include - -#include - -class KoBibliographyInfo; -class QListWidgetItem; - -class InsertBibliographyDialog : public QDialog -{ - Q_OBJECT -public: - explicit InsertBibliographyDialog(KoTextEditor *editor, QWidget *parent = 0); - QString bibliographyType(); - -public Q_SLOTS: - void insert(); - void updateFields(); - void addField(); - void removeField(); - void addSpan(); - void insertTabStop(); - void removeTabStop(); - void spanChanged(QListWidgetItem *); - -private: - Ui::InsertBibliographyDialog dialog; - bool m_blockSignals; - KoTextEditor *m_editor; - KoBibliographyInfo *m_bibInfo; -}; - -#endif // INSERTBIBLIOGRAPHYDIALOG_H diff --git a/plugins/flake/textshape/dialogs/InsertBibliographyDialog.ui b/plugins/flake/textshape/dialogs/InsertBibliographyDialog.ui deleted file mode 100644 index 276310a772..0000000000 --- a/plugins/flake/textshape/dialogs/InsertBibliographyDialog.ui +++ /dev/null @@ -1,276 +0,0 @@ - - - InsertBibliographyDialog - - - Qt::NonModal - - - - 0 - 0 - 620 - 523 - - - - Insert Bibliography - - - true - - - - - - Bibliography title - - - - - - Title: - - - - - - - - - - - - - Bibliography entries - - - - - - - - Bibliography type: - - - - - - - - Article - - - - - Book - - - - - Booklet - - - - - Conference - - - - - Email - - - - - In book - - - - - In collection - - - - - In proceedings - - - - - Journal - - - - - Manual - - - - - Masters thesis - - - - - Misc - - - - - PhD thesis - - - - - Proceedings - - - - - Tech report - - - - - Unpublished - - - - - WWW - - - - - custom1 - - - - - custom2 - - - - - custom3 - - - - - custom4 - - - - - custom5 - - - - - - - - - - - - Available fields - - - - - - - - - - - - - - Add >> - - - - - - - Span - - - - - - - <<Remove - - - - - - - - - - - Fields added - - - - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - rejected() - InsertBibliographyDialog - reject() - - - 536 - 440 - - - 286 - 274 - - - - - buttonBox - accepted() - InsertBibliographyDialog - accept() - - - 468 - 434 - - - 157 - 274 - - - - - diff --git a/plugins/flake/textshape/dialogs/LinkInsertionDialog.cpp b/plugins/flake/textshape/dialogs/LinkInsertionDialog.cpp deleted file mode 100644 index 9c32d602f9..0000000000 --- a/plugins/flake/textshape/dialogs/LinkInsertionDialog.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2013 Aman Madaan - * - * 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.q - */ -#include "LinkInsertionDialog.h" -#include "SimpleTableOfContentsWidget.h" - -#include - -#include -#include -#include -#include -#include - -LinkInsertionDialog::LinkInsertionDialog(KoTextEditor *editor, QWidget *parent) - : QDialog(parent) - , m_editor(editor) - , m_bookmarkManager(0) - , m_bookmarkList(0) - , m_reply(0) - , m_networkAccessManager(0) - , m_linkURL(0) - , m_timeoutTimer(0) -{ - dlg.setupUi(this); - setUpdatesEnabled(false); - // set up the tabs with selected text - QString suggestedLinkText; - if (m_editor->hasSelection()) { - suggestedLinkText = m_editor->selectedText(); - dlg.hyperlinkText->setText(suggestedLinkText); - dlg.bookmarkLinkText->setText(suggestedLinkText); - } - connect(dlg.buttonBox, SIGNAL(accepted()), this, SLOT(insertLink())); - connect(dlg.buttonBox, SIGNAL(rejected()), this, SLOT(close())); - dlg.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - - ///setting up the web link insertion tab - m_networkAccessManager = new QNetworkAccessManager(this); - connect(dlg.fetchTitleButton, SIGNAL(clicked()), this, SLOT(fetchTitleFromURL())); - dlg.fetchTitleButton->setEnabled(false); - setUpdatesEnabled(true); - - ///setting up the bookmark link insertion tab - //connect(dlg.bookmarkListWidget, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(slotItemClicked(QListWidgetItem*))); - m_bookmarkManager = KoTextDocument(editor->document()).textRangeManager()->bookmarkManager(); - m_bookmarkList = m_bookmarkManager->bookmarkNameList(); - QCompleter *bookmarkAutoCompleter = new QCompleter(m_bookmarkList, this); - dlg.bookmarkLinkURL->setCompleter(bookmarkAutoCompleter); - dlg.bookmarkLinkURL->addItems(m_bookmarkList); - dlg.bookmarkLinkURL->clearEditText(); - connect(dlg.hyperlinkURL, SIGNAL(textChanged(QString)), this, SLOT(enableDisableButtons(QString))); - connect(dlg.hyperlinkText, SIGNAL(textChanged(QString)), this, SLOT(enableDisableButtons(QString))); - connect(dlg.bookmarkLinkURL, SIGNAL(editTextChanged(QString)), this, SLOT(enableDisableButtons(QString))); - connect(dlg.bookmarkLinkText, SIGNAL(textChanged(QString)), this, SLOT(enableDisableButtons(QString))); - - connect(dlg.linkTypesTab, SIGNAL(currentChanged(int)), this, SLOT(checkInsertEnableValidity(int))); - show(); -} -void LinkInsertionDialog::enableDisableButtons(QString text) -{ - text = text.trimmed(); - QObject *signalSender = (sender()); - if (qobject_cast< QLineEdit * >(signalSender) == dlg.hyperlinkURL) { //deal with fetch button - if (!text.isEmpty()) { // is empty? - if (!QUrl(text).isValid()) { //not empty, is it valid? - dlg.fetchTitleButton->setEnabled(false); - dlg.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);//not valid too, time to get out - displayInlineWarning(i18n("The URL is invalid"), dlg.weblinkStatusLabel); - return; - } else { //valid but non empty, can fetch but not sure about the others so can't OK - displayInlineWarning("", dlg.weblinkStatusLabel); - dlg.fetchTitleButton->setEnabled(true); - } - } else { //field is empty, no other choice - dlg.fetchTitleButton->setEnabled(false); - dlg.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - return; - } - } else if (qobject_cast< QComboBox * >(signalSender) == dlg.bookmarkLinkURL) { //need to check existence - if (dlg.bookmarkLinkURL->currentText().isEmpty()) { - displayInlineWarning("", dlg.bookmarkLinkStatusLabel); - dlg.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - return; - } else if (!exists(dlg.bookmarkLinkURL->currentText())) { //definitely can't go in - displayInlineWarning(i18n("Bookmark does not exist"), dlg.bookmarkLinkStatusLabel); - dlg.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - return; - } else { //non empty and exits - displayInlineWarning("", dlg.bookmarkLinkStatusLabel); //can clear the label but cannot be sure about OK - } - } else if (text.isEmpty()) { //for others, empty is definitely incorrect - dlg.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - return; - } - switch (dlg.linkTypesTab->currentIndex()) { //handle cases that reach here, only doubt is completeness - case 0 : - if (!dlg.hyperlinkText->text().isEmpty() && QUrl(dlg.hyperlinkURL->text()).isValid() && !dlg.hyperlinkURL->text().isEmpty()) { - dlg.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); - } - break; - case 1: - if (!dlg.bookmarkLinkText->text().isEmpty() && !dlg.bookmarkLinkURL->currentText().isEmpty() && exists(dlg.bookmarkLinkURL->currentText())) { - dlg.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); - } - break; - } -} - -void LinkInsertionDialog::checkInsertEnableValidity(int currentTab) -{ - dlg.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - switch (currentTab) { - case 0 : - if (!dlg.hyperlinkText->text().isEmpty() && QUrl(dlg.hyperlinkURL->text()).isValid() && !dlg.hyperlinkURL->text().isEmpty()) { - dlg.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); - } - break; - case 1: - if (!dlg.bookmarkLinkText->text().isEmpty() && !dlg.bookmarkLinkURL->currentText().isEmpty() && exists(dlg.bookmarkLinkURL->currentText())) { - dlg.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); - } - break; - } -} - -void LinkInsertionDialog::insertLink() -{ - if (dlg.linkTypesTab->currentIndex() == 0) { - QString linkText = dlg.hyperlinkText->text(); - QString linkURL = dlg.hyperlinkURL->text(); - insertHyperlink(linkURL, linkText); - } else { - QString linkName = dlg.bookmarkLinkURL->currentText(); - QString linkText = dlg.bookmarkLinkText->text(); - insertBookmarkLink(linkName, linkText); - } -} - -void LinkInsertionDialog::displayInlineWarning(const QString &warning, QLabel *label) const -{ - label->setText(warning); -} - -void LinkInsertionDialog::insertHyperlink(QString &linkURLString, const QString &linkText) -{ - QString linkhtml; - QUrl linkURL = QUrl(linkURLString); - dlg.weblinkStatusLabel->setText(QString()); - if (!linkURL.isValid()) { - displayInlineWarning(i18n("The URL is invalid"), dlg.weblinkStatusLabel); - } else { - if ((linkURL.scheme()).isEmpty()) { //prepend a scheme if not present - linkURLString.prepend("http://"); - } - m_editor->insertText(linkText, linkURLString); - this->close(); - } -} - -void LinkInsertionDialog::insertBookmarkLink(const QString &linkURL, const QString &linkText) -{ - dlg.bookmarkLinkStatusLabel->setText(QString()); - m_editor->insertText(linkText, linkURL); - this->close(); -} - -bool LinkInsertionDialog::exists(const QString &bookmarkName) const -{ - return m_bookmarkList.contains(bookmarkName); -} - -void LinkInsertionDialog::fetchTitleFromURL() -{ - QString linkURLString = dlg.hyperlinkURL->text(); - m_linkURL = QUrl(linkURLString); - if (m_linkURL.isValid()) { - if ((m_linkURL.scheme()).isEmpty()) { //prepend a scheme if not present - linkURLString.prepend("http://"); - dlg.hyperlinkURL->setText(linkURLString); - m_linkURL.setUrl(linkURLString); - } - sendRequest(); - } else { - displayInlineWarning(i18n("The URL is invalid"), dlg.weblinkStatusLabel); - return; - } - //xgettext: no-c-format - dlg.weblinkStatusLabel->setText(i18n("Fetching the title: 0% complete")); -} -void LinkInsertionDialog::sendRequest() -{ - QNetworkRequest request; - request.setUrl(m_linkURL); - m_reply = m_networkAccessManager->get(request); - //start a timer to notify user when it takes too long to get the title - if (m_timeoutTimer.isActive()) { //a timer for every redirection - m_timeoutTimer.stop(); - } - m_timeoutTimer.setInterval(FETCH_TIMEOUT); - m_timeoutTimer.setSingleShot(true); - m_timeoutTimer.start(); - connect(&m_timeoutTimer, SIGNAL(timeout()), this, SLOT(fetchTitleTimeout())); - connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished())); - connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(fetchTitleError(QNetworkReply::NetworkError))); - connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateTitleDownloadProgress(qint64,qint64))); -} - -void LinkInsertionDialog::fetchTitleTimeout() -{ - if (!m_reply->isFinished()) { - displayInlineWarning(i18n("Fetch timed out"), dlg.weblinkStatusLabel); - m_reply->abort(); - } -} - -void LinkInsertionDialog::replyFinished() -{ - //check for redirections - QVariant possibleRedirectVariant = - m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute); - QUrl possibleRedirectUrl = possibleRedirectVariant.toUrl(); - if (!possibleRedirectUrl.isEmpty() && m_linkURL != possibleRedirectUrl) { //redirect - if (possibleRedirectUrl.toString().at(0) == '/') { //redirection to a relative url - if (m_linkURL.toString().at(m_linkURL.toString().length() - 1) == '/') { //initially of the form http:xyz.com/ - possibleRedirectUrl.setUrl(m_linkURL.toString() + possibleRedirectUrl.toString().remove(0, 1)); - } else { - possibleRedirectUrl.setUrl(m_linkURL.toString() + possibleRedirectUrl.toString()); - } - } - m_linkURL = possibleRedirectUrl; - sendRequest(); - return; - } - const QString res = m_reply->readAll(); - static QRegExp titleStart(" - static QRegExp titleEnd(""); - int start = titleStart.indexIn(res); - if (start == -1) { //perhaps TITLE?, rare but possible - start = QRegExp("')) { - start++; - } - start++; //eat the '>' - int end = titleEnd.indexIn(res); - if (end == -1) { - end = QRegExp("").indexIn(res); - if (end == -1) { - displayInlineWarning("Error fetching title", dlg.weblinkStatusLabel); - return; - } - } - dlg.hyperlinkText->setText(QStringRef(&res, start, end - start).toString()); - dlg.weblinkStatusLabel->setText(QString()); -} - -void LinkInsertionDialog::updateTitleDownloadProgress(qint64 received, qint64 total) -{ - float percentComplete = (static_cast(received) / total) * 100; - //xgettext: no-c-format - dlg.weblinkStatusLabel->setText(i18n("Fetching the title: %1% complete", QString::number(percentComplete))); -} - -LinkInsertionDialog::~LinkInsertionDialog() -{ - m_networkAccessManager->deleteLater(); -} - -void LinkInsertionDialog::fetchTitleError(QNetworkReply::NetworkError) -{ - m_timeoutTimer.stop(); - displayInlineWarning(i18n("The URL is invalid"), dlg.weblinkStatusLabel); -} - -void LinkInsertionDialog::accept() -{ - //Overloaded to prevent the dialog from closing -} diff --git a/plugins/flake/textshape/dialogs/LinkInsertionDialog.h b/plugins/flake/textshape/dialogs/LinkInsertionDialog.h deleted file mode 100644 index 3d7ec2d355..0000000000 --- a/plugins/flake/textshape/dialogs/LinkInsertionDialog.h +++ /dev/null @@ -1,84 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2013 Aman Madaan - * - * 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 LINKINSERTDIALOG -#define LINKINSERTDIALOG - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#define FETCH_TIMEOUT 5000 - -class LinkInsertionDialog : public QDialog -{ - Q_OBJECT -public : - explicit LinkInsertionDialog(KoTextEditor *editor, QWidget *parent = 0); - ~LinkInsertionDialog() override; - -private Q_SLOTS: - void insertLink(); - -public Q_SLOTS: - - void fetchTitleFromURL(); - void replyFinished(); - void fetchTitleError(QNetworkReply::NetworkError); - void updateTitleDownloadProgress(qint64, qint64); - void fetchTitleTimeout(); - /** - * Verifies the text entered in the four line edits : Weblink URL, Weblink text, - * Bookmark name and Bookmark text. The "Ok" button is enabled only if the input - * is valid. - * @param text is the text to be verified. - */ - void enableDisableButtons(QString text); - - /** - * Once all the line edits for a tab have been verified, the OK button is enabled. - * If the tab is switched, the validity of OK should be recalculated for the new tab. - * @param text is the current active tab. - */ - void checkInsertEnableValidity(int); - -private : - Ui::LinkInsertionDialog dlg; - KoTextEditor *m_editor; - const KoBookmarkManager *m_bookmarkManager; - QStringList m_bookmarkList; - QNetworkReply *m_reply; - QNetworkAccessManager *m_networkAccessManager; - QUrl m_linkURL; - QTimer m_timeoutTimer; - void accept() override; - void sendRequest(); - void insertBookmarkLink(const QString &URL, const QString &text); - void insertHyperlink(QString &linkURL, const QString &linkText); - void displayInlineWarning(const QString &title, QLabel *label) const; - bool exists(const QString &) const; -}; -#endif diff --git a/plugins/flake/textshape/dialogs/LinkInsertionDialog.ui b/plugins/flake/textshape/dialogs/LinkInsertionDialog.ui deleted file mode 100644 index ed03a76cb4..0000000000 --- a/plugins/flake/textshape/dialogs/LinkInsertionDialog.ui +++ /dev/null @@ -1,218 +0,0 @@ - - - LinkInsertionDialog - - - - 0 - 0 - 567 - 241 - - - - Link - - - - - - - - - 0 - - - true - - - - Web Link - - - Insert links to web documents. You can provide both URL and a label for the link, - or just enter the URL and hit "Fetch the title from URL" - - - - - - - - The address of your document (Uniform Resource Locator) - - - URL: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - Hit this button to get the title from the URL. Redirections are also handled. - - - Fetch Title From URL - - - - - - - Qt::Horizontal - - - - 168 - 20 - - - - - - - - The text that will be displayed for your link - - - Text: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - 2 - - - - - - true - - - - - - - - Link To Bookmark - - - Insert links to Bookmarks. To create bookmarks, -click "Bookmarks" in the Links and Bookmarks section - - - - - 5 - 5 - 531 - 78 - - - - - - - The name of the bookmark to where the link has to point to - - - Name: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - true - - - - - - - Qt::Vertical - - - - 20 - 18 - - - - - - - - The text that will be displayed for your link - - - Text: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - 10 - 140 - 531 - 17 - - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - linkTypesTab - hyperlinkURL - fetchTitleButton - hyperlinkText - buttonBox - bookmarkLinkText - - - - diff --git a/plugins/flake/textshape/dialogs/ManageBookmark.ui b/plugins/flake/textshape/dialogs/ManageBookmark.ui deleted file mode 100644 index 7927dc626b..0000000000 --- a/plugins/flake/textshape/dialogs/ManageBookmark.ui +++ /dev/null @@ -1,55 +0,0 @@ - - - ManageBookmark - - - - 0 - 0 - 332 - 219 - - - - - - - - - - &Rename - - - - - - - &Delete - - - - - - - Insert - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - diff --git a/plugins/flake/textshape/dialogs/ManageBookmarkDialog.cpp b/plugins/flake/textshape/dialogs/ManageBookmarkDialog.cpp deleted file mode 100644 index 057e15fbdd..0000000000 --- a/plugins/flake/textshape/dialogs/ManageBookmarkDialog.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2007-2008 Fredy Yanardi - * Copyright (C) 2013 Aman Madaan - * - * 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 "ManageBookmarkDialog.h" - -#include -#include - -static QString lastBookMarkItem; - -ManageBookmark::ManageBookmark(const QList &nameList, KoTextEditor *editor, QWidget *parent) - : QWidget(parent) - , m_editor(editor) -{ - widget.setupUi(this); - widget.bookmarkList->addItems(nameList); - widget.bookmarkList->setFocus(Qt::ActiveWindowFocusReason); - const int count = widget.bookmarkList->count(); - if (count > 0) { - int row = 0; - if (!lastBookMarkItem.isNull()) { - QList items = widget.bookmarkList->findItems(lastBookMarkItem, Qt::MatchExactly); - if (items.count() > 0) { - row = widget.bookmarkList->row(items[0]); - } - } - widget.bookmarkList->setCurrentRow(row); - } - - connect(widget.bookmarkList, SIGNAL(currentRowChanged(int)), this, SLOT(selectionChanged(int))); - connect(widget.buttonRename, SIGNAL(clicked()), this, SLOT(slotBookmarkRename())); - connect(widget.buttonDelete, SIGNAL(clicked()), this, SLOT(slotBookmarkDelete())); - connect(widget.buttonInsert, SIGNAL(clicked()), this, SLOT(slotBookmarkInsert())); - connect(widget.bookmarkList, SIGNAL(itemActivated(QListWidgetItem*)), - this, SLOT(slotBookmarkItemActivated(QListWidgetItem*))); - selectionChanged(bookmarkRow()); -} - -QString ManageBookmark::bookmarkName() const -{ - const QListWidgetItem *item = widget.bookmarkList->currentItem(); - return item ? item->text() : QString(); -} - -int ManageBookmark::bookmarkRow() const -{ - return widget.bookmarkList->currentRow(); -} - -void ManageBookmark::selectionChanged(int currentRow) -{ - widget.buttonRename->setEnabled(currentRow != -1); - widget.buttonDelete->setEnabled(currentRow != -1); - emit bookmarkSelectionChanged(currentRow); -} - -void ManageBookmark::slotBookmarkRename() -{ - bool ok = 0; - QListWidgetItem *item = widget.bookmarkList->currentItem(); - Q_ASSERT(item); - QString curName = item->text(); - QString newName = item->text(); - while (true) { - newName = QInputDialog::getText(parentWidget(), - i18n("Rename Bookmark"), - i18n("Please provide a new name for the bookmark"), - QLineEdit::Normal, - newName, - &ok); - if (curName != newName && ok) { - QList items = widget.bookmarkList->findItems(newName, Qt::MatchExactly); - if (items.count() > 0) { - KMessageBox::error(parentWidget(), i18n("A bookmark with the name \"%1\" already exists.", newName)); - continue; - } - item->setText(newName); - emit bookmarkNameChanged(curName, newName); - } - break; - } -} - -void ManageBookmark::slotBookmarkDelete() -{ - int currentRow = widget.bookmarkList->currentRow(); - Q_ASSERT(currentRow >= 0); - QListWidgetItem *deletedItem = widget.bookmarkList->takeItem(currentRow); - QString deletedName = deletedItem->text(); - emit bookmarkItemDeleted(deletedName); - delete deletedItem; -} - -void ManageBookmark::slotBookmarkItemActivated(QListWidgetItem *item) -{ - Q_ASSERT(item); - lastBookMarkItem = item->text(); - emit bookmarkItemDoubleClicked(item); -} - -void ManageBookmark::slotBookmarkInsert() -{ - QString bookmarkName; - bool ok = 0; - while (true) { - bookmarkName = QInputDialog::getText(parentWidget(), - i18n("Insert Bookmark"), - i18n("Please provide a name for the bookmark"), - QLineEdit::Normal, - bookmarkName, - &ok); - if (ok) { - QList items = widget.bookmarkList->findItems(bookmarkName, Qt::MatchExactly); - if (items.count() > 0) { - KMessageBox::error(parentWidget(), i18n("A bookmark with the name \"%1\" already exists.", bookmarkName)); - continue; - } else { - m_editor->addBookmark(bookmarkName); - widget.bookmarkList->insertItem(widget.bookmarkList->count(), bookmarkName); - } - } - break; - } -} - -ManageBookmarkDialog::ManageBookmarkDialog(const QList &nameList, KoTextEditor *editor, QWidget *parent) - : KoDialog(parent) -{ - ui = new ManageBookmark(nameList, editor, this); - setMainWidget(ui); - setCaption(i18n("Manage Bookmarks")); - setModal(true); - setButtons(Ok | Cancel); - setDefaultButton(Ok); - showButtonSeparator(true); - connect(ui, SIGNAL(bookmarkSelectionChanged(int)), this, SLOT(selectionChanged(int))); - connect(ui, SIGNAL(bookmarkNameChanged(QString,QString)), - this, SIGNAL(nameChanged(QString,QString))); - connect(ui, SIGNAL(bookmarkItemDeleted(QString)), - this, SIGNAL(bookmarkDeleted(QString))); - connect(ui, SIGNAL(bookmarkItemDoubleClicked(QListWidgetItem*)), - this, SLOT(bookmarkDoubleClicked(QListWidgetItem*))); - selectionChanged(ui->bookmarkRow()); -} - -QString ManageBookmarkDialog::selectedBookmarkName() -{ - return ui->bookmarkName(); -} - -void ManageBookmarkDialog::selectionChanged(int currentRow) -{ - enableButtonOk(currentRow != -1); -} - -void ManageBookmarkDialog::bookmarkDoubleClicked(QListWidgetItem *item) -{ - Q_UNUSED(item); - accept(); -} diff --git a/plugins/flake/textshape/dialogs/ManageBookmarkDialog.h b/plugins/flake/textshape/dialogs/ManageBookmarkDialog.h deleted file mode 100644 index 38a1daf6e0..0000000000 --- a/plugins/flake/textshape/dialogs/ManageBookmarkDialog.h +++ /dev/null @@ -1,76 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2007-2008 Fredy Yanardi - * Copyright (C) 2013 Aman Madaan - * - * 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 MANAGEBOOKMARKDIALOG_H -#define MANAGEBOOKMARKDIALOG_H -#include -#include - -#include -#include - -class ManageBookmark : public QWidget -{ - Q_OBJECT -public: - explicit ManageBookmark(const QList &nameList, KoTextEditor *editor, QWidget *parent = 0); - QString bookmarkName() const; - int bookmarkRow() const; - -Q_SIGNALS: - void bookmarkSelectionChanged(int currentRow); - void bookmarkNameChanged(const QString &oldName, const QString &newName); - void bookmarkItemDeleted(const QString &deletedName); - void bookmarkItemDoubleClicked(QListWidgetItem *item); - -private Q_SLOTS: - void selectionChanged(int currentRow); - void slotBookmarkRename(); - void slotBookmarkDelete(); - void slotBookmarkInsert(); - void slotBookmarkItemActivated(QListWidgetItem *item); - -private: - Ui::ManageBookmark widget; - KoTextEditor *m_editor; -}; - -class ManageBookmarkDialog : public KoDialog -{ - Q_OBJECT -public: - explicit ManageBookmarkDialog(const QList &nameList, KoTextEditor *editor, QWidget *parent = 0); - QString selectedBookmarkName(); - -Q_SIGNALS: - void nameChanged(const QString &oldName, const QString &newName); - void bookmarkDeleted(const QString &deletedName); - -private Q_SLOTS: - void selectionChanged(int currentRow); - void bookmarkDoubleClicked(QListWidgetItem *item); - -private: - ManageBookmark *ui; - -}; - -#endif - diff --git a/plugins/flake/textshape/dialogs/NotesConfigurationDialog.cpp b/plugins/flake/textshape/dialogs/NotesConfigurationDialog.cpp deleted file mode 100644 index a7f804d96e..0000000000 --- a/plugins/flake/textshape/dialogs/NotesConfigurationDialog.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2011 Brijesh Patel - * - * 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 "NotesConfigurationDialog.h" -#include "KoTextDocument.h" -#include "KoStyleManager.h" - -#include - -#include - -#include -#include - -NotesConfigurationDialog::NotesConfigurationDialog(QTextDocument *doc, bool footnoteMode, QWidget *parent) - : QDialog(parent) - , m_document(doc) -{ - widget.setupUi(this); - if (footnoteMode) { - setWindowTitle(i18n("Footnote Settings")); - footnoteSetup(); - } else { - setWindowTitle(i18n("Endnote Settings")); - endnoteSetup(); - } - connect(widget.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(apply(QAbstractButton*))); -} - -void NotesConfigurationDialog::setStyleManager(KoStyleManager *sm) -{ - m_styleManager = sm; -} - -void NotesConfigurationDialog::footnoteSetup() -{ - m_notesConfig = KoTextDocument(m_document).styleManager() - ->notesConfiguration(KoOdfNotesConfiguration::Footnote); - if (!m_notesConfig) { - m_notesConfig = new KoOdfNotesConfiguration(KoOdfNotesConfiguration::Footnote); - } - widget.prefixLineEdit->setText(m_notesConfig->numberFormat().prefix()); - widget.suffixLineEdit->setText(m_notesConfig->numberFormat().suffix()); - widget.startAtSpinBox->setValue(m_notesConfig->startValue()); - widget.endlineEdit->setText(m_notesConfig->footnoteContinuationForward()); - widget.startlineEdit->setText(m_notesConfig->footnoteContinuationBackward()); - - switch (m_notesConfig->numberFormat().formatSpecification()) { - default: - case KoOdfNumberDefinition::Numeric: - widget.numStyleCombo->setCurrentIndex(0); - break; - case KoOdfNumberDefinition::AlphabeticLowerCase: - if (m_notesConfig->numberFormat().letterSynchronization()) { - widget.numStyleCombo->setCurrentIndex(3); - } else { - widget.numStyleCombo->setCurrentIndex(1); - } - break; - case KoOdfNumberDefinition::AlphabeticUpperCase: - if (m_notesConfig->numberFormat().letterSynchronization()) { - widget.numStyleCombo->setCurrentIndex(4); - } else { - widget.numStyleCombo->setCurrentIndex(2); - } - break; - case KoOdfNumberDefinition::RomanLowerCase: - widget.numStyleCombo->setCurrentIndex(5); - break; - case KoOdfNumberDefinition::RomanUpperCase: - widget.numStyleCombo->setCurrentIndex(6); - break; - } - - switch (m_notesConfig->numberingScheme()) { - case KoOdfNotesConfiguration::BeginAtPage: - widget.beginAtCombo->setCurrentIndex(0); - break; - case KoOdfNotesConfiguration::BeginAtChapter: - widget.beginAtCombo->setCurrentIndex(1); - break; - case KoOdfNotesConfiguration::BeginAtDocument: - widget.beginAtCombo->setCurrentIndex(2); - break; - } -} - -void NotesConfigurationDialog::endnoteSetup() -{ - widget.continuationBox->hide(); - widget.beginAtCombo->hide(); - m_notesConfig = KoTextDocument(m_document).styleManager() - ->notesConfiguration(KoOdfNotesConfiguration::Endnote); - if (!m_notesConfig) { - m_notesConfig = new KoOdfNotesConfiguration(KoOdfNotesConfiguration::Endnote); - } - widget.prefixLineEdit->setText(m_notesConfig->numberFormat().prefix()); - widget.suffixLineEdit->setText(m_notesConfig->numberFormat().suffix()); - widget.startAtSpinBox->setValue(m_notesConfig->startValue()); - - switch (m_notesConfig->numberFormat().formatSpecification()) { - case KoOdfNumberDefinition::Numeric: - widget.numStyleCombo->setCurrentIndex(0); - break; - case KoOdfNumberDefinition::AlphabeticLowerCase: - if (m_notesConfig->numberFormat().letterSynchronization()) { - widget.numStyleCombo->setCurrentIndex(3); - } else { - widget.numStyleCombo->setCurrentIndex(1); - } - break; - case KoOdfNumberDefinition::AlphabeticUpperCase: - if (m_notesConfig->numberFormat().letterSynchronization()) { - widget.numStyleCombo->setCurrentIndex(4); - } else { - widget.numStyleCombo->setCurrentIndex(2); - } - break; - default: - case KoOdfNumberDefinition::RomanLowerCase: - widget.numStyleCombo->setCurrentIndex(5); - break; - case KoOdfNumberDefinition::RomanUpperCase: - widget.numStyleCombo->setCurrentIndex(6); - break; - } -} - -void NotesConfigurationDialog::apply(QAbstractButton *button) -{ - if (widget.buttonBox->standardButton(button) == widget.buttonBox->Apply) { - //set Number Format - KoOdfNumberDefinition *numFormat = new KoOdfNumberDefinition(); - //set prefix - numFormat->setPrefix(widget.prefixLineEdit->text()); - //set suffix - numFormat->setSuffix(widget.suffixLineEdit->text()); - - switch (widget.numStyleCombo->currentIndex()) { - case 0: - numFormat->setFormatSpecification(KoOdfNumberDefinition::Numeric); - m_notesConfig->setNumberFormat(*numFormat); - break; - case 1: - numFormat->setFormatSpecification(KoOdfNumberDefinition::AlphabeticLowerCase); - numFormat->setLetterSynchronization(false); - m_notesConfig->setNumberFormat(*numFormat); - break; - case 2: - numFormat->setFormatSpecification(KoOdfNumberDefinition::AlphabeticUpperCase); - numFormat->setLetterSynchronization(false); - m_notesConfig->setNumberFormat(*numFormat); - break; - case 3: - numFormat->setFormatSpecification(KoOdfNumberDefinition::AlphabeticLowerCase); - numFormat->setLetterSynchronization(true); - m_notesConfig->setNumberFormat(*numFormat); - break; - case 4: - numFormat->setFormatSpecification(KoOdfNumberDefinition::AlphabeticUpperCase); - numFormat->setLetterSynchronization(true); - m_notesConfig->setNumberFormat(*numFormat); - break; - case 5: - numFormat->setFormatSpecification(KoOdfNumberDefinition::RomanLowerCase); - m_notesConfig->setNumberFormat(*numFormat); - break; - case 6: - numFormat->setFormatSpecification(KoOdfNumberDefinition::RomanUpperCase); - m_notesConfig->setNumberFormat(*numFormat); - break; - }; - //set Foot notes Position - /*if(m_notesConfig->noteClass() == KoOdfNotesConfiguration::Footnote) { - switch(widget.location_footnote->currentIndex()) { - case 0: - m_notesConfig->setFootnotesPosition(KoOdfNotesConfiguration::Page); - case 1: - m_notesConfig->setFootnotesPosition(KoOdfNotesConfiguration::Text); - case 2: - m_notesConfig->setFootnotesPosition(KoOdfNotesConfiguration::Section); - case 3: - m_notesConfig->setFootnotesPosition(KoOdfNotesConfiguration::Document); - - } - }*/ - //set start value - m_notesConfig->setStartValue(widget.startAtSpinBox->value()); - //set Numbering Scheme - switch (widget.beginAtCombo->currentIndex()) { - case 0: - m_notesConfig->setNumberingScheme(KoOdfNotesConfiguration::BeginAtPage); - break; - case 1: - m_notesConfig->setNumberingScheme(KoOdfNotesConfiguration::BeginAtChapter); - break; - case 2: - m_notesConfig->setNumberingScheme(KoOdfNotesConfiguration::BeginAtDocument); - break; - } - - //set footnote continuation forward - m_notesConfig->setFootnoteContinuationForward(widget.endlineEdit->text()); - //set footnote continuation backward - m_notesConfig->setFootnoteContinuationBackward(widget.startlineEdit->text()); - - //TODO - //set citation text style - - //set citation body text style - - //set master page - - //set note paragraph style - - this->close(); - } else if (widget.buttonBox->standardButton(button) == widget.buttonBox->Discard) { - this->close(); - } -} diff --git a/plugins/flake/textshape/dialogs/NotesConfigurationDialog.h b/plugins/flake/textshape/dialogs/NotesConfigurationDialog.h deleted file mode 100644 index 35fd78bef9..0000000000 --- a/plugins/flake/textshape/dialogs/NotesConfigurationDialog.h +++ /dev/null @@ -1,60 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2011 Brijesh Patel - * - * 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 NOTESCONFIGURATIONDIALOG_H -#define NOTESCONFIGURATIONDIALOG_H - -#include -#include -#include - -#include -#include -#include - -class KoStyleManager; - -class NotesConfigurationDialog : public QDialog -{ - Q_OBJECT -public: - explicit NotesConfigurationDialog(QTextDocument *doc, bool footnoteMode, QWidget *parent = 0); - Ui::NotesConfigurationDialog widget; - -public Q_SLOTS: - void setStyleManager(KoStyleManager *sm); - /** - * sets up the footnote's default configuration in the dialog box - */ - void footnoteSetup(); - /** - * sets up the endnote's default configuration in the dialog box - */ - void endnoteSetup(); - /** - * stores the applied notes' configuration as globalnotesconfiguration of the document - */ - void apply(QAbstractButton *); - -private: - KoOdfNotesConfiguration *m_notesConfig; - KoStyleManager *m_styleManager; - QTextDocument *m_document; -}; - -#endif diff --git a/plugins/flake/textshape/dialogs/NotesConfigurationDialog.ui b/plugins/flake/textshape/dialogs/NotesConfigurationDialog.ui deleted file mode 100644 index d923394f4c..0000000000 --- a/plugins/flake/textshape/dialogs/NotesConfigurationDialog.ui +++ /dev/null @@ -1,352 +0,0 @@ - - - NotesConfigurationDialog - - - - 0 - 0 - 557 - 295 - - - - - 400 - 200 - - - - - 9 - - - - false - - - false - - - - QLayout::SetMinimumSize - - - - - QLayout::SetDefaultConstraint - - - 4 - - - 12 - - - 4 - - - 4 - - - - - Automatic Numbering: - - - true - - - - - - - 0 - 0 - - - - - 54 - 0 - - - - Prefix: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Start at: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Format: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - Suffix: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - Starts over at: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - Page - - - - - Chapter - - - - - Document - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - - 100 - 20 - - - - - - - - - 0 - 0 - - - - 1 - - - 9999 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 94 - 0 - - - - - 1, 2, 3, ... - - - - - a, b, c, ..., aa, ab, ..., ba, bb, ... - - - - - A, B, C, ..., AA, AB, ..., BA, BB, ... - - - - - a, b, c, ..., aa, bb, ..., aaa, bbb, ... - - - - - A, B, C, ..., AA, BB, ..., AAA, BBB, ... - - - - - i, ii, iii, ... - - - - - I,II,III, ... - - - - - - - - - 0 - 0 - - - - - 102 - 0 - - - - - - - - - - - Continuation Notice: - - - true - - - - - - - - - - - - Text saying it is a continuation: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Text saying it will continue: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - QDialogButtonBox::Apply|QDialogButtonBox::Discard|QDialogButtonBox::Help - - - - - - - - - - - diff --git a/plugins/flake/textshape/dialogs/SimpleAnnotationWidget.cpp b/plugins/flake/textshape/dialogs/SimpleAnnotationWidget.cpp deleted file mode 100644 index 47efdb4f84..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleAnnotationWidget.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "SimpleAnnotationWidget.h" - -#include "../ReviewTool.h" -#include -#include -SimpleAnnotationWidget::SimpleAnnotationWidget(ReviewTool *tool, QWidget *parent) - : QWidget(parent) - , m_tool(tool) -{ - widget.setupUi(this); - widget.insertAnnotation->setDefaultAction(m_tool->action("insert_annotation")); - widget.removeAnnotation->setDefaultAction(m_tool->action("remove_annotation")); -} diff --git a/plugins/flake/textshape/dialogs/SimpleAnnotationWidget.h b/plugins/flake/textshape/dialogs/SimpleAnnotationWidget.h deleted file mode 100644 index 57f8a5c61c..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleAnnotationWidget.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef SIMPLEANNOTATIONWIDGET_H -#define SIMPLEANNOTATIONWIDGET_H - -#include "ui_SimpleAnnotationWidget.h" -#include - -class ReviewTool; - -class SimpleAnnotationWidget : public QWidget -{ - Q_OBJECT - -public: - explicit SimpleAnnotationWidget(ReviewTool *tool, QWidget *parent = 0); - //virtual ~SimpleAnnotationWidget(); - -Q_SIGNALS: - void doneWithFocus(); - -private: - Ui::SimpleAnnotationWidget widget; - ReviewTool *m_tool; - -}; - -#endif // SIMPLEANNOTATIONWIDGET_H diff --git a/plugins/flake/textshape/dialogs/SimpleAnnotationWidget.ui b/plugins/flake/textshape/dialogs/SimpleAnnotationWidget.ui deleted file mode 100644 index b503c9f117..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleAnnotationWidget.ui +++ /dev/null @@ -1,64 +0,0 @@ - - - SimpleAnnotationWidget - - - - 0 - 0 - 400 - 300 - - - - - 0 - 0 - - - - - - - Insert Note - - - - - - - true - - - Remove Note - - - - - - - Qt::Horizontal - - - - 138 - 20 - - - - - - - - - 0 - 0 - - - - - - - - - diff --git a/plugins/flake/textshape/dialogs/SimpleCaptionsWidget.cpp b/plugins/flake/textshape/dialogs/SimpleCaptionsWidget.cpp deleted file mode 100644 index 26ee2616ab..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleCaptionsWidget.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* This file is part of the KDE project -* Copyright (C) 2010 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 "SimpleCaptionsWidget.h" -#include "TextTool.h" - -#include -#include - -#include - -SimpleCaptionsWidget::SimpleCaptionsWidget(QWidget *parent) - : QWidget(parent) - , m_blockSignals(false) -{ - widget.setupUi(this); -// widget.splitCells->setDefaultAction(tool->action("split_tablecells")); - -// connect(widget.addRowAbove, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); -} - -void SimpleCaptionsWidget::setStyleManager(KoStyleManager *sm) -{ - m_styleManager = sm; -} diff --git a/plugins/flake/textshape/dialogs/SimpleCaptionsWidget.h b/plugins/flake/textshape/dialogs/SimpleCaptionsWidget.h deleted file mode 100644 index f6076ec293..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleCaptionsWidget.h +++ /dev/null @@ -1,52 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2010 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. - */ -#ifndef SIMPLECAPTIONSWIDGET_H -#define SIMPLECAPTIONSWIDGET_H - -#include -#include - -#include -#include - -class TextTool; -class KoStyleManager; - -class SimpleCaptionsWidget : public QWidget -{ - Q_OBJECT -public: - explicit SimpleCaptionsWidget(QWidget *parent = 0); - -public Q_SLOTS: - void setStyleManager(KoStyleManager *sm); - -Q_SIGNALS: - void doneWithFocus(); - -private: - Ui::SimpleCaptionsWidget widget; - KoStyleManager *m_styleManager; - bool m_blockSignals; - bool m_comboboxHasBidiItems; - QTextBlock m_currentBlock; - TextTool *m_tool; -}; - -#endif diff --git a/plugins/flake/textshape/dialogs/SimpleCaptionsWidget.ui b/plugins/flake/textshape/dialogs/SimpleCaptionsWidget.ui deleted file mode 100644 index 7fc454bb29..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleCaptionsWidget.ui +++ /dev/null @@ -1,82 +0,0 @@ - - - SimpleCaptionsWidget - - - - 0 - 0 - 173 - 67 - - - - - 0 - 0 - - - - - 0 - - - 0 - - - - - 2 - - - - - Add caption - - - false - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 0 - 20 - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 0 - 0 - - - - - - - - addCitation - - - - diff --git a/plugins/flake/textshape/dialogs/SimpleCitationBibliographyWidget.cpp b/plugins/flake/textshape/dialogs/SimpleCitationBibliographyWidget.cpp deleted file mode 100644 index a917f7762e..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleCitationBibliographyWidget.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2010 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 "SimpleCitationBibliographyWidget.h" -#include "ReferencesTool.h" -#include "BibliographyPreview.h" -#include "BibliographyTemplate.h" -#include -#include - -#include -#include - -#include -#include - -SimpleCitationBibliographyWidget::SimpleCitationBibliographyWidget(ReferencesTool *tool, QWidget *parent) - : QWidget(parent) - , m_blockSignals(false) - , m_referenceTool(tool) - , m_signalMapper(0) -{ - widget.setupUi(this); - Q_ASSERT(tool); - - m_templateGenerator = new BibliographyTemplate(KoTextDocument(m_referenceTool->editor()->document()).styleManager()); - - widget.addCitation->setDefaultAction(tool->action("insert_citation")); - connect(widget.addCitation, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); - - widget.addBibliography->setDefaultAction(tool->action("insert_bibliography")); - widget.addBibliography->setNumColumns(1); - connect(widget.addBibliography, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); - connect(widget.addBibliography, SIGNAL(aboutToShowMenu()), this, SLOT(prepareTemplateMenu())); - connect(widget.addBibliography, SIGNAL(itemTriggered(int)), this, SLOT(applyTemplate(int))); - - widget.configureBibliography->setDefaultAction(tool->action("configure_bibliography")); - connect(widget.configureBibliography, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); -} - -SimpleCitationBibliographyWidget::~SimpleCitationBibliographyWidget() -{ - delete m_templateGenerator; -} - -void SimpleCitationBibliographyWidget::setStyleManager(KoStyleManager *sm) -{ - m_styleManager = sm; -} - -void SimpleCitationBibliographyWidget::prepareTemplateMenu() -{ - m_previewGenerator.clear(); - if (m_signalMapper) { - delete m_signalMapper; - m_signalMapper = 0; - } - qDeleteAll(m_templateList.begin(), m_templateList.end()); - m_templateList.clear(); - - m_signalMapper = new QSignalMapper(); - - m_templateList = m_templateGenerator->templates(); - - connect(m_signalMapper, SIGNAL(mapped(int)), this, SLOT(pixmapReady(int))); - - int index = 0; - foreach (KoBibliographyInfo *info, m_templateList) { - BibliographyPreview *preview = new BibliographyPreview(); - preview->setStyleManager(KoTextDocument(m_referenceTool->editor()->document()).styleManager()); - preview->setPreviewSize(QSize(200, 120)); - preview->updatePreview(info); - connect(preview, SIGNAL(pixmapGenerated()), m_signalMapper, SLOT(map())); - m_signalMapper->setMapping(preview, index); - m_previewGenerator.append(preview); - ++index; - - //put dummy pixmaps until the actual pixmap previews are generated and added in pixmapReady() - if (!widget.addBibliography->hasItemId(index)) { - QPixmap pmm(QSize(200, 120)); - pmm.fill(Qt::white); - widget.addBibliography->addItem(pmm, index); - } - } - if (widget.addBibliography->isFirstTimeMenuShown()) { - widget.addBibliography->addSeparator(); - widget.addBibliography->addAction(m_referenceTool->action("insert_custom_bibliography")); - connect(m_referenceTool->action("insert_custom_bibliography"), - SIGNAL(triggered()), this, SLOT(insertCustomBibliography()), Qt::UniqueConnection); - } -} - -void SimpleCitationBibliographyWidget::insertCustomBibliography() -{ - m_templateGenerator->moveTemplateToUsed(m_templateList.at(0)); - m_referenceTool->insertCustomBibliography(m_templateList.at(0)); -} - -void SimpleCitationBibliographyWidget::pixmapReady(int templateId) -{ - // +1 to the templateId is because formattingButton does not allow id = 0 - widget.addBibliography->addItem(m_previewGenerator.at(templateId)->previewPixmap(), templateId + 1); - disconnect(m_previewGenerator.at(templateId), SIGNAL(pixmapGenerated()), m_signalMapper, SLOT(map())); - m_previewGenerator.at(templateId)->deleteLater(); -} - -void SimpleCitationBibliographyWidget::applyTemplate(int templateId) -{ - m_templateGenerator->moveTemplateToUsed(m_templateList.at(templateId - 1)); - m_referenceTool->editor()->insertBibliography(m_templateList.at(templateId - 1)); -} diff --git a/plugins/flake/textshape/dialogs/SimpleCitationBibliographyWidget.h b/plugins/flake/textshape/dialogs/SimpleCitationBibliographyWidget.h deleted file mode 100644 index b82009aa7e..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleCitationBibliographyWidget.h +++ /dev/null @@ -1,68 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2010 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. - */ -#ifndef SIMPLECITATIONINDEXWIDGET_H -#define SIMPLECITATIONINDEXWIDGET_H - -#include -#include -#include "FormattingButton.h" - -#include -#include - -class ReferencesTool; -class KoStyleManager; -class KoBibliographyInfo; -class BibliographyPreview; -class BibliographyTemplate; -class QSignalMapper; - -class SimpleCitationBibliographyWidget : public QWidget -{ - Q_OBJECT -public: - explicit SimpleCitationBibliographyWidget(ReferencesTool *tool, QWidget *parent = 0); - ~SimpleCitationBibliographyWidget() override; - -public Q_SLOTS: - void setStyleManager(KoStyleManager *sm); - void prepareTemplateMenu(); - void pixmapReady(int templateId); - -private Q_SLOTS: - void applyTemplate(int templateId); - void insertCustomBibliography(); - -Q_SIGNALS: - void doneWithFocus(); - -private: - Ui::SimpleCitationBibliographyWidget widget; - KoStyleManager *m_styleManager; - bool m_blockSignals; - QTextBlock m_currentBlock; - ReferencesTool *m_referenceTool; - QList m_templateList; - //each template in the template list will have have a previewGenerator that will be deleted after preview is generated - QList m_previewGenerator; - QSignalMapper *m_signalMapper; - BibliographyTemplate *m_templateGenerator; -}; - -#endif diff --git a/plugins/flake/textshape/dialogs/SimpleCitationBibliographyWidget.ui b/plugins/flake/textshape/dialogs/SimpleCitationBibliographyWidget.ui deleted file mode 100644 index a84c0f2045..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleCitationBibliographyWidget.ui +++ /dev/null @@ -1,80 +0,0 @@ - - - SimpleCitationBibliographyWidget - - - - 0 - 0 - 174 - 102 - - - - - 0 - 0 - - - - - - - Insert citation at current position - - - Insert Citation - - - false - - - - - - - Insert Bibliography - - - QToolButton::InstantPopup - - - Qt::ToolButtonTextOnly - - - false - - - - - - - Configure bibliography - - - Configure Bibliography - - - - - - - - 0 - 0 - - - - - - - - - FormattingButton - QToolButton -
dialogs/FormattingButton.h
-
-
- - -
diff --git a/plugins/flake/textshape/dialogs/SimpleFootEndNotesWidget.cpp b/plugins/flake/textshape/dialogs/SimpleFootEndNotesWidget.cpp deleted file mode 100644 index 46a08ebfd9..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleFootEndNotesWidget.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2010 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 "SimpleFootEndNotesWidget.h" -#include "TextTool.h" -#include "FormattingButton.h" - -#include -#include - -#include -#include - -#include - -SimpleFootEndNotesWidget::SimpleFootEndNotesWidget(TextTool *tool, QWidget *parent) - : QWidget(parent) -{ - widget.setupUi(this); - widget.addFootnote->addAction(tool->action("insert_autofootnote")); - widget.addFootnote->addAction(tool->action("insert_labeledfootnote")); - widget.addFootnote->addAction(tool->action("format_footnotes")); - widget.addFootnote->setIcon(koIcon("insert-footnote")); - widget.addFootnote->setToolTip(i18n("Inserts a footnote at the current cursor position")); - widget.addEndnote->addAction(tool->action("insert_autoendnote")); - widget.addEndnote->addAction(tool->action("insert_labeledendnote")); - widget.addEndnote->addAction(tool->action("format_endnotes")); - widget.addEndnote->setIcon(koIcon("insert-endnote")); - widget.addEndnote->setToolTip(i18n("Inserts an endnote at the current cursor position")); - - connect(widget.addFootnote, SIGNAL(doneWithFocus()), this, SIGNAL(doneWithFocus())); - connect(widget.addEndnote, SIGNAL(doneWithFocus()), this, SIGNAL(doneWithFocus())); -} diff --git a/plugins/flake/textshape/dialogs/SimpleFootEndNotesWidget.h b/plugins/flake/textshape/dialogs/SimpleFootEndNotesWidget.h deleted file mode 100644 index 082229366d..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleFootEndNotesWidget.h +++ /dev/null @@ -1,46 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2010 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. - */ -#ifndef SIMPLEFOOTENDNOTESWIDGET_H -#define SIMPLEFOOTENDNOTESWIDGET_H - -#include -#include - -#include -#include -#include - -class TextTool; - -class SimpleFootEndNotesWidget : public QWidget -{ - Q_OBJECT -public: - explicit SimpleFootEndNotesWidget(TextTool *tool, QWidget *parent = 0); - Ui::SimpleFootEndNotesWidget widget; - -Q_SIGNALS: - void doneWithFocus(); - -private: - QTextBlock m_currentBlock; - TextTool *m_tool; -}; - -#endif diff --git a/plugins/flake/textshape/dialogs/SimpleFootEndNotesWidget.ui b/plugins/flake/textshape/dialogs/SimpleFootEndNotesWidget.ui deleted file mode 100644 index 76b78718f4..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleFootEndNotesWidget.ui +++ /dev/null @@ -1,88 +0,0 @@ - - - SimpleFootEndNotesWidget - - - - 0 - 0 - 193 - 48 - - - - - 0 - 0 - - - - - QLayout::SetMinAndMaxSize - - - 0 - - - - - Endnote - - - - 32 - 32 - - - - QToolButton::InstantPopup - - - Qt::ToolButtonTextUnderIcon - - - - - - - Footnote - - - - 32 - 32 - - - - QToolButton::InstantPopup - - - Qt::ToolButtonTextUnderIcon - - - false - - - - - - - - 0 - 0 - - - - - - - - - FormattingButton - QToolButton -
dialogs/FormattingButton.h
-
-
- - -
diff --git a/plugins/flake/textshape/dialogs/SimpleLinksWidget.cpp b/plugins/flake/textshape/dialogs/SimpleLinksWidget.cpp deleted file mode 100644 index 331ca4751a..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleLinksWidget.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2001 David Faure - * Copyright (C) 2005-2007, 2009, 2010 Thomas Zander - * Copyright (C) 2010-2011 Boudewijn Rempt - * Copyright (C) 2013 Aman Madaan - * - * 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 "SimpleLinksWidget.h" - -#include "ReferencesTool.h" -#include -#include -#include "ManageBookmarkDialog.h" -#include -#include -#include -#include -#include -#include - -SimpleLinksWidget::SimpleLinksWidget(ReferencesTool *tool, QWidget *parent) - : QWidget(parent) - , m_referenceTool(tool) -{ - widget.setupUi(this); - Q_ASSERT(tool); - widget.insertLink->setDefaultAction(tool->action("insert_link")); - widget.invokeBookmarkHandler->setDefaultAction(tool->action("invoke_bookmark_handler")); - widget.invokeBookmarkHandler->setNumColumns(1); - connect(widget.insertLink, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); - connect(widget.invokeBookmarkHandler, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); - connect(widget.invokeBookmarkHandler, SIGNAL(aboutToShowMenu()), this, SLOT(preparePopUpMenu())); -} - -void SimpleLinksWidget::preparePopUpMenu() -{ - if (widget.invokeBookmarkHandler->isFirstTimeMenuShown()) { - widget.invokeBookmarkHandler->addAction(m_referenceTool->action("insert_bookmark")); - widget.invokeBookmarkHandler->addSeparator(); - widget.invokeBookmarkHandler->addAction(m_referenceTool->action("manage_bookmarks")); - connect(m_referenceTool->action("manage_bookmarks"), - SIGNAL(triggered()), this, SLOT(manageBookmarks()), Qt::UniqueConnection); - } -} - -void SimpleLinksWidget::manageBookmarks() -{ - QString name; - const KoBookmarkManager *manager = KoTextDocument(m_referenceTool->editor()->document()).textRangeManager()->bookmarkManager(); - QPointer dia = new ManageBookmarkDialog(manager->bookmarkNameList(), m_referenceTool->editor(), m_referenceTool->canvas()->canvasWidget()); - connect(dia, SIGNAL(nameChanged(QString,QString)), manager, SLOT(rename(QString,QString))); - connect(dia, SIGNAL(bookmarkDeleted(QString)), manager, SLOT(remove(QString))); - if (dia->exec() == QDialog::Accepted) { - name = dia->selectedBookmarkName(); - } else { - delete dia; - return; - } - delete dia; - KoBookmark *bookmark = manager->bookmark(name); - - KoCanvasResourceManager *rm = m_referenceTool->canvas()->resourceManager(); - if ((bookmark->positionOnlyMode() == false) && bookmark->hasRange()) { - rm->clearResource(KoText::SelectedTextPosition); - rm->clearResource(KoText::SelectedTextAnchor); - } - if (bookmark->positionOnlyMode()) { - rm->setResource(KoText::CurrentTextPosition, bookmark->rangeStart()); - rm->setResource(KoText::CurrentTextAnchor, bookmark->rangeStart()); - } else { - rm->setResource(KoText::CurrentTextPosition, bookmark->rangeStart()); - rm->setResource(KoText::CurrentTextAnchor, bookmark->rangeEnd()); - } -} - -SimpleLinksWidget::~SimpleLinksWidget() -{ - -} diff --git a/plugins/flake/textshape/dialogs/SimpleLinksWidget.h b/plugins/flake/textshape/dialogs/SimpleLinksWidget.h deleted file mode 100644 index 296939db76..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleLinksWidget.h +++ /dev/null @@ -1,53 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2001 David Faure - * Copyright (C) 2005-2007, 2009, 2010 Thomas Zander - * Copyright (C) 2010-2011 Boudewijn Rempt - * Copyright (C) 2013 Aman Madaan - * - * 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 SIMPLELINKSWIDGET_H -#define SIMPLELINKSWIDGET_H - -#include -#include "FormattingButton.h" -#include -#include - -class ReferencesTool; - -class SimpleLinksWidget : public QWidget -{ - Q_OBJECT -public: - explicit SimpleLinksWidget(ReferencesTool *tool, QWidget *parent = 0); - ~SimpleLinksWidget() override; - -Q_SIGNALS: - void doneWithFocus(); - -public Q_SLOTS: - void preparePopUpMenu(); - -private Q_SLOTS: - void manageBookmarks(); - -private: - Ui::SimpleLinksWidget widget; - ReferencesTool *m_referenceTool; -}; - -#endif diff --git a/plugins/flake/textshape/dialogs/SimpleLinksWidget.ui b/plugins/flake/textshape/dialogs/SimpleLinksWidget.ui deleted file mode 100644 index 48d933c8aa..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleLinksWidget.ui +++ /dev/null @@ -1,90 +0,0 @@ - - - SimpleLinksWidget - - - - 0 - 0 - 180 - 38 - - - - - 0 - 0 - - - - - 0 - - - 0 - - - - - 2 - - - - - Hyperlinks - - - false - - - - - - - Bookmarks - - - QToolButton::InstantPopup - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 0 - 20 - - - - - - - - - 0 - 0 - - - - - - - - - FormattingButton - QToolButton -
dialogs/FormattingButton.h
-
-
- - -
diff --git a/plugins/flake/textshape/dialogs/SimpleSpellCheckingWidget.cpp b/plugins/flake/textshape/dialogs/SimpleSpellCheckingWidget.cpp deleted file mode 100644 index f9aaf8dddb..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleSpellCheckingWidget.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2013 Luke De Mouy - * - * 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 "SimpleSpellCheckingWidget.h" - -#include "../ReviewTool.h" -#include "TextTool.h" - -SimpleSpellCheckingWidget::SimpleSpellCheckingWidget(ReviewTool *tool, QWidget *parent) - : QWidget(parent) - , ui(new Ui::SimpleSpellCheckingWidget) -{ - ui->setupUi(this); - ui->toolAutoSpellCheck->setDefaultAction((QAction *)tool->action("tool_auto_spellcheck")); - -} - -SimpleSpellCheckingWidget::~SimpleSpellCheckingWidget() -{ - - delete ui; -} diff --git a/plugins/flake/textshape/dialogs/SimpleSpellCheckingWidget.h b/plugins/flake/textshape/dialogs/SimpleSpellCheckingWidget.h deleted file mode 100644 index cbac44a8b2..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleSpellCheckingWidget.h +++ /dev/null @@ -1,43 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2013 Luke De Mouy - * - * 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 SIMPLESPELLCHECKINGWIDGET_H -#define SIMPLESPELLCHECKINGWIDGET_H - -#include - -#include "ui_SimpleSpellCheckingWidget.h" -namespace Ui -{ -class SimpleSpellCheckingWidget; -} -class ReviewTool; - -class SimpleSpellCheckingWidget : public QWidget -{ - Q_OBJECT - -public: - explicit SimpleSpellCheckingWidget(ReviewTool *tool, QWidget *parent = 0); - ~SimpleSpellCheckingWidget() override; - -private: - Ui::SimpleSpellCheckingWidget *ui; -}; - -#endif // SIMPLESPELLCHECKINGWIDGET_H diff --git a/plugins/flake/textshape/dialogs/SimpleSpellCheckingWidget.ui b/plugins/flake/textshape/dialogs/SimpleSpellCheckingWidget.ui deleted file mode 100644 index 95524159e2..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleSpellCheckingWidget.ui +++ /dev/null @@ -1,54 +0,0 @@ - - - SimpleSpellCheckingWidget - - - - 0 - 0 - 273 - 234 - - - - - 0 - 0 - - - - - - - ... - - - - - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - - 40 - 0 - - - - - - - - - diff --git a/plugins/flake/textshape/dialogs/SimpleTableOfContentsWidget.cpp b/plugins/flake/textshape/dialogs/SimpleTableOfContentsWidget.cpp deleted file mode 100644 index c8d5a3fa32..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleTableOfContentsWidget.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2010 C. Boemann - * Copyright (C) 2011 Gopalakrishna Bhat A - * - * 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 "SimpleTableOfContentsWidget.h" -#include "ReferencesTool.h" -#include "TableOfContentsConfigure.h" -#include "TableOfContentsTemplate.h" -#include "TableOfContentsPreview.h" - -#include -#include -#include - -#include -#include - -#include -#include -#include - -SimpleTableOfContentsWidget::SimpleTableOfContentsWidget(ReferencesTool *tool, QWidget *parent) - : QWidget(parent) - , m_blockSignals(false) - , m_referenceTool(tool) - , m_signalMapper(0) -{ - widget.setupUi(this); - Q_ASSERT(tool); - - m_templateGenerator = new TableOfContentsTemplate(KoTextDocument(m_referenceTool->editor()->document()).styleManager()); - - widget.addToC->setIcon(koIcon("insert-tableofcontents")); - widget.addToC->setNumColumns(1); - connect(widget.addToC, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); - connect(widget.addToC, SIGNAL(aboutToShowMenu()), this, SLOT(prepareTemplateMenu())); - connect(widget.addToC, SIGNAL(itemTriggered(int)), this, SLOT(applyTemplate(int))); -} - -SimpleTableOfContentsWidget::~SimpleTableOfContentsWidget() -{ - delete m_templateGenerator; -} - -void SimpleTableOfContentsWidget::setStyleManager(KoStyleManager *sm) -{ - m_styleManager = sm; -} - -void SimpleTableOfContentsWidget::prepareTemplateMenu() -{ - m_previewGenerator.clear(); - if (m_signalMapper) { - delete m_signalMapper; - m_signalMapper = 0; - } - qDeleteAll(m_templateList.begin(), m_templateList.end()); - m_templateList.clear(); - - m_signalMapper = new QSignalMapper(); - - m_templateList = m_templateGenerator->templates(); - - connect(m_signalMapper, SIGNAL(mapped(int)), this, SLOT(pixmapReady(int))); - - int index = 0; - foreach (KoTableOfContentsGeneratorInfo *info, m_templateList) { - TableOfContentsPreview *preview = new TableOfContentsPreview(); - preview->setStyleManager(KoTextDocument(m_referenceTool->editor()->document()).styleManager()); - preview->setPreviewSize(QSize(200, 120)); - preview->updatePreview(info); - connect(preview, SIGNAL(pixmapGenerated()), m_signalMapper, SLOT(map())); - m_signalMapper->setMapping(preview, index); - m_previewGenerator.append(preview); - ++index; - - //put dummy pixmaps until the actual pixmap previews are generated and added in pixmapReady() - if (!widget.addToC->hasItemId(index)) { - QPixmap pmm(QSize(200, 120)); - pmm.fill(Qt::white); - widget.addToC->addItem(pmm, index); - } - } - if (widget.addToC->isFirstTimeMenuShown()) { - widget.addToC->addSeparator(); - widget.addToC->addAction(m_referenceTool->action("insert_configure_tableofcontents")); - connect(m_referenceTool->action("insert_configure_tableofcontents"), SIGNAL(triggered()), this, SLOT(insertCustomToC()), Qt::UniqueConnection); - widget.addToC->addAction(m_referenceTool->action("format_tableofcontents")); - } -} - -void SimpleTableOfContentsWidget::pixmapReady(int templateId) -{ - // +1 to the templateId is because formattingButton does not allow id = 0 - widget.addToC->addItem(m_previewGenerator.at(templateId)->previewPixmap(), templateId + 1); - disconnect(m_previewGenerator.at(templateId), SIGNAL(pixmapGenerated()), m_signalMapper, SLOT(map())); - m_previewGenerator.at(templateId)->deleteLater(); -} - -void SimpleTableOfContentsWidget::applyTemplate(int templateId) -{ - m_templateGenerator->moveTemplateToUsed(m_templateList.at(templateId - 1)); - m_referenceTool->editor()->insertTableOfContents(m_templateList.at(templateId - 1)); -} - -void SimpleTableOfContentsWidget::insertCustomToC() -{ - m_templateGenerator->moveTemplateToUsed(m_templateList.at(0)); - m_referenceTool->insertCustomToC(m_templateList.at(0)); -} diff --git a/plugins/flake/textshape/dialogs/SimpleTableOfContentsWidget.h b/plugins/flake/textshape/dialogs/SimpleTableOfContentsWidget.h deleted file mode 100644 index 6495267129..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleTableOfContentsWidget.h +++ /dev/null @@ -1,68 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2010 C. Boemann - * Copyright (C) 2011 Gopalakrishna Bhat A - * - * 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 SIMPLETABLEOFCONTENTSWIDGET_H -#define SIMPLETABLEOFCONTENTSWIDGET_H - -#include - -#include -#include -#include - -class ReferencesTool; -class KoStyleManager; -class KoTableOfContentsGeneratorInfo; -class TableOfContentsPreview; -class QSignalMapper; -class TableOfContentsTemplate; - -class SimpleTableOfContentsWidget : public QWidget -{ - Q_OBJECT -public: - explicit SimpleTableOfContentsWidget(ReferencesTool *tool, QWidget *parent = 0); - ~SimpleTableOfContentsWidget() override; - -public Q_SLOTS: - void setStyleManager(KoStyleManager *sm); - void prepareTemplateMenu(); - void pixmapReady(int templateId); - -Q_SIGNALS: - void doneWithFocus(); - -private Q_SLOTS: - void applyTemplate(int templateId); - void insertCustomToC(); - -private: - Ui::SimpleTableOfContentsWidget widget; - KoStyleManager *m_styleManager; - bool m_blockSignals; - QTextBlock m_currentBlock; - QList m_templateList; - //each template in the template list will have have a previewGenerator that will be deleted after preview is generated - QList m_previewGenerator; - ReferencesTool *m_referenceTool; - QSignalMapper *m_signalMapper; - TableOfContentsTemplate *m_templateGenerator; -}; - -#endif diff --git a/plugins/flake/textshape/dialogs/SimpleTableOfContentsWidget.ui b/plugins/flake/textshape/dialogs/SimpleTableOfContentsWidget.ui deleted file mode 100644 index d4cf0f1db7..0000000000 --- a/plugins/flake/textshape/dialogs/SimpleTableOfContentsWidget.ui +++ /dev/null @@ -1,95 +0,0 @@ - - - SimpleTableOfContentsWidget - - - - 0 - 0 - 86 - 37 - - - - - 0 - 0 - - - - - 0 - - - 0 - - - - - 2 - - - - - Contents - - - - 32 - 32 - - - - QToolButton::InstantPopup - - - Qt::ToolButtonTextUnderIcon - - - false - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 0 - 20 - - - - - - - - - 0 - 0 - - - - - - - - - FormattingButton - QToolButton -
dialogs/FormattingButton.h
-
-
- - addToC - - - -
diff --git a/plugins/flake/textshape/dialogs/SpecialButton.cpp b/plugins/flake/textshape/dialogs/SpecialButton.cpp deleted file mode 100644 index 2091bebc44..0000000000 --- a/plugins/flake/textshape/dialogs/SpecialButton.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2010 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 "SpecialButton.h" -#include "StylesWidget.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -SpecialButton::SpecialButton(QWidget *parent) - : QFrame(parent) - , m_stylesWidget(0) - , m_preview(0) -{ - setFrameShape(QFrame::StyledPanel); - setFrameShadow(QFrame::Sunken); - - setMinimumSize(50, 32); - setMaximumHeight(25); - - m_preview = new QLabel(); - m_preview->setAutoFillBackground(true); - m_preview->setBackgroundRole(QPalette::Base); - m_preview->setMinimumWidth(50); - m_preview->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - QHBoxLayout *l = new QHBoxLayout(this); - l->addWidget(m_preview); - l->setMargin(0); - setLayout(l); - - isPopupVisible = false; -} - -SpecialButton::~SpecialButton() -{ - delete m_preview; -} - -void SpecialButton::setStylePreview(const QPixmap &pm) -{ - m_preview->setPixmap(pm); -} - -void SpecialButton::showPopup() -{ - if (!m_stylesWidget) { - return; - } - - QRect popupRect(mapToGlobal(QPoint(0, height())), m_stylesWidget->sizeHint()); - // Make sure the popup is not drawn outside the screen area - QRect screenRect = QApplication::desktop()->availableGeometry(this); - if (popupRect.right() > screenRect.right()) { - popupRect.translate(screenRect.right() - popupRect.right(), 0); - } - if (popupRect.left() < screenRect.left()) { - popupRect.translate(screenRect.left() - popupRect.left(), 0); - } - if (popupRect.bottom() > screenRect.bottom()) { - popupRect.translate(0, -(height() + m_stylesWidget->height())); - } - - m_stylesWidget->setGeometry(popupRect); - m_stylesWidget->raise(); - m_stylesWidget->show(); - isPopupVisible = true; -} - -void SpecialButton::hidePopup() -{ - m_stylesWidget->hide(); - isPopupVisible = false; -} - -void SpecialButton::setStylesWidget(StylesWidget *stylesWidget) -{ - m_stylesWidget = stylesWidget; -} - -void SpecialButton::mousePressEvent(QMouseEvent *) -{ - if (!isPopupVisible) { - showPopup(); - } else { - hidePopup(); - } -} diff --git a/plugins/flake/textshape/dialogs/SpecialButton.h b/plugins/flake/textshape/dialogs/SpecialButton.h deleted file mode 100644 index 560edeff39..0000000000 --- a/plugins/flake/textshape/dialogs/SpecialButton.h +++ /dev/null @@ -1,49 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2010 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. - */ -#ifndef SPECIALBUTTON_H -#define SPECIALBUTTON_H - -#include - -class QPixmap; -class QLabel; -class StylesWidget; - -class SpecialButton : public QFrame -{ - Q_OBJECT -public: - explicit SpecialButton(QWidget *parent); - ~SpecialButton(); - - void setStylesWidget(StylesWidget *stylesWidget); - void setStylePreview(const QPixmap &pm); - - void showPopup(); - void hidePopup(); - -protected: - virtual void mousePressEvent(QMouseEvent *event); - - StylesWidget *m_stylesWidget; - QLabel *m_preview; - bool isPopupVisible; -}; - -#endif //SPECIALBUTTON_H diff --git a/plugins/flake/textshape/dialogs/StylesWidget.cpp b/plugins/flake/textshape/dialogs/StylesWidget.cpp deleted file mode 100644 index 09e9cf8041..0000000000 --- a/plugins/flake/textshape/dialogs/StylesWidget.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2007-2009 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 "StylesWidget.h" -#include "StylesModel.h" -#include "StylesDelegate.h" -#include "ParagraphGeneral.h" -#include "CharacterGeneral.h" -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -StylesWidget::StylesWidget(QWidget *parent, bool paragraphMode, Qt::WindowFlags f) - : QFrame(parent, f) - , m_styleManager(0) - , m_styleThumbnailer(0) - , m_stylesModel(new StylesModel(0, StylesModel::ParagraphStyle)) - , m_stylesDelegate(new StylesDelegate()) - , m_blockSignals(false) - , m_isHovered(false) -{ - m_styleThumbnailer = new KoStyleThumbnailer(); - m_styleThumbnailer->setThumbnailSize(QSize(250, 48)); - m_stylesModel->setStyleThumbnailer(m_styleThumbnailer); - widget.setupUi(this); - widget.stylesView->setModel(m_stylesModel); - - if (paragraphMode) { - connect(widget.stylesView, SIGNAL(clicked(QModelIndex)), this, SLOT(applyParagraphStyle())); - } else { - connect(widget.stylesView, SIGNAL(clicked(QModelIndex)), this, SLOT(applyCharacterStyle())); - } -} - -StylesWidget::~StylesWidget() -{ - delete m_stylesDelegate; - delete m_stylesModel; - delete m_styleThumbnailer; -} - -QSize StylesWidget::sizeHint() const -{ - return QSize(widget.stylesView->sizeHint().width() + 2 * widget.stylesView->verticalScrollBar()->width(), - widget.stylesView->sizeHint().height()); -} - -void StylesWidget::setStyleManager(KoStyleManager *sm) -{ - m_styleManager = sm; - m_stylesModel->setStyleManager(sm); -} - -void StylesWidget::setCurrentFormat(const QTextBlockFormat &format) -{ - if (format == m_currentBlockFormat) { - return; - } - m_currentBlockFormat = format; - int id = m_currentBlockFormat.intProperty(KoParagraphStyle::StyleId); - KoParagraphStyle *usedStyle = 0; - if (m_styleManager) { - usedStyle = m_styleManager->paragraphStyle(id); - } - if (usedStyle) { - Q_FOREACH (int property, m_currentBlockFormat.properties().keys()) { - if (property == QTextFormat::ObjectIndex) { - continue; - } - if (property == KoParagraphStyle::ListStyleId) { - continue; - } - if (m_currentBlockFormat.property(property) != usedStyle->value(property)) { - break; - } - } - } - - widget.stylesView->setCurrentIndex(m_stylesModel->indexOf(*usedStyle)); -} - -void StylesWidget::setCurrentFormat(const QTextCharFormat &format) -{ - if (format == m_currentCharFormat) { - return; - } - m_currentCharFormat = format; - - int id = m_currentCharFormat.intProperty(KoCharacterStyle::StyleId); - KoCharacterStyle *usedStyle = 0; - if (m_styleManager) { - usedStyle = m_styleManager->characterStyle(id); - } - if (usedStyle) { - QTextCharFormat defaultFormat; - usedStyle->unapplyStyle(defaultFormat); // sets the default properties. - Q_FOREACH (int property, m_currentCharFormat.properties().keys()) { - if (property == QTextFormat::ObjectIndex) { - continue; - } - if (m_currentCharFormat.property(property) != usedStyle->value(property) - && m_currentCharFormat.property(property) != defaultFormat.property(property)) { - break; - } - } - } - - widget.stylesView->setCurrentIndex(m_stylesModel->indexOf(*usedStyle)); -} - -void StylesWidget::applyParagraphStyle() -{ - QModelIndex index = widget.stylesView->currentIndex(); - Q_ASSERT(index.isValid()); - KoParagraphStyle *paragraphStyle = m_stylesModel->paragraphStyleForIndex(index); - if (paragraphStyle) { - emit paragraphStyleSelected(paragraphStyle); - emit doneWithFocus(); - return; - } -} - -void StylesWidget::applyCharacterStyle() -{ - QModelIndex index = widget.stylesView->currentIndex(); - Q_ASSERT(index.isValid()); - KoCharacterStyle *characterStyle = m_stylesModel->characterStyleForIndex(index); - if (characterStyle) { - emit characterStyleSelected(characterStyle); - emit doneWithFocus(); - return; - } -} diff --git a/plugins/flake/textshape/dialogs/StylesWidget.h b/plugins/flake/textshape/dialogs/StylesWidget.h deleted file mode 100644 index f86bef45a8..0000000000 --- a/plugins/flake/textshape/dialogs/StylesWidget.h +++ /dev/null @@ -1,77 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2007-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 STYLESWIDGET_H -#define STYLESWIDGET_H - -#include -#include -#include -#include - -#include - -class KoStyleManager; -class KoStyleThumbnailer; -class KoParagraphStyle; -class KoCharacterStyle; -class StylesModel; -class StylesDelegate; - -class StylesWidget : public QFrame -{ - Q_OBJECT -public: - explicit StylesWidget(QWidget *parent = 0, bool paragraphMode = true, Qt::WindowFlags f = 0); - virtual ~StylesWidget(); - - virtual QSize sizeHint() const; - -public Q_SLOTS: - void setStyleManager(KoStyleManager *sm); - void setCurrentFormat(const QTextBlockFormat &format); - void setCurrentFormat(const QTextCharFormat &format); - -Q_SIGNALS: - void doneWithFocus(); - void paragraphStyleSelected(KoParagraphStyle *paragraphStyle, bool canDelete); - void characterStyleSelected(KoCharacterStyle *characterStyle, bool canDelete); - -private Q_SLOTS: - void applyParagraphStyle(); - void applyCharacterStyle(); - -Q_SIGNALS: - void paragraphStyleSelected(KoParagraphStyle *style); - void characterStyleSelected(KoCharacterStyle *style); - -private: - Ui::StylesWidget widget; - KoStyleManager *m_styleManager; - KoStyleThumbnailer *m_styleThumbnailer; - - QTextBlockFormat m_currentBlockFormat; - QTextCharFormat m_currentCharFormat; - StylesModel *m_stylesModel; - StylesDelegate *m_stylesDelegate; - bool m_blockSignals; - bool m_isEmbedded; - bool m_isHovered; -}; - -#endif diff --git a/plugins/flake/textshape/dialogs/StylesWidget.ui b/plugins/flake/textshape/dialogs/StylesWidget.ui deleted file mode 100644 index 80bc358a26..0000000000 --- a/plugins/flake/textshape/dialogs/StylesWidget.ui +++ /dev/null @@ -1,28 +0,0 @@ - - - StylesWidget - - - - 0 - 0 - 400 - 300 - - - - - 0 - - - - - Qt::ScrollBarAlwaysOff - - - - - - - - diff --git a/plugins/flake/textshape/textlayout/KoTextShapeData.cpp b/plugins/flake/textshape/textlayout/KoTextShapeData.cpp index 9cbbb243ae..83858fc6b7 100644 --- a/plugins/flake/textshape/textlayout/KoTextShapeData.cpp +++ b/plugins/flake/textshape/textlayout/KoTextShapeData.cpp @@ -1,402 +1,406 @@ /* This file is part of the KDE project * Copyright (C) 2006, 2009-2010 Thomas Zander * Copyright (C) 2008 Thorsten Zachmann * Copyright (C) 2008 Girish Ramakrishnan * Copyright (C) 2011 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 "KoTextShapeData.h" #include #include #include "KoTextDocument.h" #include #include "styles/KoStyleManager.h" #include "styles/KoParagraphStyle.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoTextPage.h" #include "opendocument/KoTextLoader.h" #include "opendocument/KoTextWriter.h" #include class KoTextShapeDataPrivate : public KoTextShapeDataBasePrivate { public: KoTextShapeDataPrivate() : topPadding(0) , leftPadding(0) , rightPadding(0) , bottomPadding(0) , direction(KoText::AutoDirection) , rootArea(0) { } KoTextShapeDataPrivate(const KoTextShapeDataPrivate &rhs) : KoTextShapeDataBasePrivate(rhs), topPadding(rhs.topPadding), leftPadding(rhs.leftPadding), rightPadding(rhs.rightPadding), bottomPadding(rhs.bottomPadding), direction(rhs.direction), - rootArea(rhs.rootArea), // WARNING: root area becomes shared via raw pointer! + rootArea(0), // root area will be reset manually by the provider paragraphStyle(rhs.paragraphStyle->clone()) { } ~KoTextShapeDataPrivate() override { } qreal topPadding; qreal leftPadding; qreal rightPadding; qreal bottomPadding; KoText::Direction direction; KoTextLayoutRootArea *rootArea; QScopedPointer paragraphStyle; // the paragraph style of the shape (part of the graphic style) }; KoTextShapeData::KoTextShapeData() : KoTextShapeDataBase(new KoTextShapeDataPrivate()) { setDocument(new QTextDocument); } KoTextShapeData::KoTextShapeData(KoTextShapeDataPrivate *dd) : KoTextShapeDataBase(dd) { + if (dd->document) { + setDocument(dd->document.data()); + } + } KoTextShapeData::~KoTextShapeData() { } KoShapeUserData *KoTextShapeData::clone() const { Q_D(const KoTextShapeData); return new KoTextShapeData(new KoTextShapeDataPrivate(*d)); } void KoTextShapeData::setDocument(QTextDocument *document) { Q_D(KoTextShapeData); Q_ASSERT(document); // The following avoids the normal case where the glyph metrices are rounded to integers and // hinted to the screen by freetype, which you of course don't want for WYSIWYG if (! document->useDesignMetrics()) document->setUseDesignMetrics(true); KoTextDocument kodoc(document); if (document->isEmpty() && !document->firstBlock().blockFormat().hasProperty(KoParagraphStyle::StyleId)) { // apply app default style for first parag KoStyleManager *sm = kodoc.styleManager(); if (sm) { KoParagraphStyle *defaultStyle = sm->defaultParagraphStyle(); if (defaultStyle) { QTextBlock block = document->begin(); defaultStyle->applyStyle(block); } } } // After setting the document (even if not changing it) we need to explicitly set the root area // to 0. Otherwise crashes may occur when inserting textshape in words (or resetting document) d->rootArea = 0; if (d->document.data() != document) { d->document.reset(document); if (kodoc.textEditor() == 0) { kodoc.setTextEditor(new KoTextEditor(d->document.data())); } } } qreal KoTextShapeData::documentOffset() const { Q_D(const KoTextShapeData); if (d->rootArea) { KoBorder *border = d->rootArea->associatedShape()->border(); if (border) { return d->rootArea->top() - topPadding() - border->borderWidth(KoBorder::TopBorder); } else { return d->rootArea->top() - topPadding(); } } else { return 0.0; } } void KoTextShapeData::setDirty() { Q_D(KoTextShapeData); if (d->rootArea) { d->rootArea->setDirty(); } } bool KoTextShapeData::isDirty() const { Q_D(const KoTextShapeData); if (d->rootArea) { return d->rootArea->isDirty(); } return true; } void KoTextShapeData::setPageDirection(KoText::Direction direction) { Q_D(KoTextShapeData); d->direction = direction; } KoText::Direction KoTextShapeData::pageDirection() const { Q_D(const KoTextShapeData); return d->direction; } void KoTextShapeData::setRootArea(KoTextLayoutRootArea *rootArea) { Q_D(KoTextShapeData); d->rootArea = rootArea; } KoTextLayoutRootArea *KoTextShapeData::rootArea() { Q_D(const KoTextShapeData); return d->rootArea; } void KoTextShapeData::setLeftPadding(qreal padding) { Q_D(KoTextShapeData); d->leftPadding = padding; } qreal KoTextShapeData::leftPadding() const { Q_D(const KoTextShapeData); return d->leftPadding; } void KoTextShapeData::setTopPadding(qreal padding) { Q_D(KoTextShapeData); d->topPadding = padding; } qreal KoTextShapeData::topPadding() const { Q_D(const KoTextShapeData); return d->topPadding; } void KoTextShapeData::setRightPadding(qreal padding) { Q_D(KoTextShapeData); d->rightPadding = padding; } qreal KoTextShapeData::rightPadding() const { Q_D(const KoTextShapeData); return d->rightPadding; } void KoTextShapeData::setBottomPadding(qreal padding) { Q_D(KoTextShapeData); d->bottomPadding = padding; } qreal KoTextShapeData::bottomPadding() const { Q_D(const KoTextShapeData); return d->bottomPadding; } void KoTextShapeData::setPadding(qreal padding) { setLeftPadding(padding); setTopPadding(padding); setRightPadding(padding); setBottomPadding(padding); } bool KoTextShapeData::loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context, KoDocumentRdfBase *rdfData, KoShape *shape) { Q_UNUSED(rdfData); KoTextLoader loader(context, shape); QTextCursor cursor(document()); loader.loadBody(element, cursor); // now let's load the body from the ODF KoXmlElement. KoTextEditor *editor = KoTextDocument(document()).textEditor(); if (editor) { // at one point we have to get the position from the odf doc instead. editor->setPosition(0); } return true; } void KoTextShapeData::saveOdf(KoShapeSavingContext &context, KoDocumentRdfBase *rdfData, int from, int to) const { Q_D(const KoTextShapeData); KoTextWriter::saveOdf(context, rdfData, d->document.data(), from, to); } void KoTextShapeData::loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context) { Q_D(KoTextShapeData); // load the (text) style of the frame const KoXmlElement *style = 0; if (element.hasAttributeNS(KoXmlNS::draw, "style-name")) { style = context.odfLoadingContext().stylesReader().findStyle( element.attributeNS(KoXmlNS::draw, "style-name"), "graphic", context.odfLoadingContext().useStylesAutoStyles()); if (!style) { warnTextLayout << "graphic style not found:" << element.attributeNS(KoXmlNS::draw, "style-name"); } } if (element.hasAttributeNS(KoXmlNS::presentation, "style-name")) { style = context.odfLoadingContext().stylesReader().findStyle( element.attributeNS(KoXmlNS::presentation, "style-name"), "presentation", context.odfLoadingContext().useStylesAutoStyles()); if (!style) { warnTextLayout << "presentation style not found:" << element.attributeNS(KoXmlNS::presentation, "style-name"); } } if (style) { KoStyleStack &styleStack = context.odfLoadingContext().styleStack(); styleStack.save(); context.odfLoadingContext().addStyles(style, style->attributeNS(KoXmlNS::style, "family", "graphic").toLocal8Bit().constData()); // Load all parents styleStack.setTypeProperties("graphic"); // Spacing (padding) const QString paddingLeft(styleStack.property(KoXmlNS::fo, "padding-left" )); if (!paddingLeft.isEmpty()) { setLeftPadding(KoUnit::parseValue(paddingLeft)); } const QString paddingRight(styleStack.property(KoXmlNS::fo, "padding-right" )); if (!paddingRight.isEmpty()) { setRightPadding(KoUnit::parseValue(paddingRight)); } const QString paddingTop(styleStack.property(KoXmlNS::fo, "padding-top" )); if (!paddingTop.isEmpty()) { setTopPadding(KoUnit::parseValue(paddingTop)); } const QString paddingBottom(styleStack.property(KoXmlNS::fo, "padding-bottom" )); if (!paddingBottom.isEmpty()) { setBottomPadding(KoUnit::parseValue(paddingBottom)); } const QString padding(styleStack.property(KoXmlNS::fo, "padding")); if (!padding.isEmpty()) { setPadding(KoUnit::parseValue(padding)); } styleStack.restore(); QString family = style->attributeNS(KoXmlNS::style, "family", "graphic"); KoParagraphStyle *defaultStyle = 0; const KoXmlElement *dstyle = context.odfLoadingContext().stylesReader().defaultStyle(family); if (dstyle) { defaultStyle = new KoParagraphStyle(); defaultStyle->loadOdf(dstyle, context); } // graphic styles don't support inheritance yet therefore some additional work is needed here. QList paragraphStyles; while (style) { KoParagraphStyle *pStyle = new KoParagraphStyle(); pStyle->loadOdf(style, context); if (!paragraphStyles.isEmpty()) { paragraphStyles.last()->setParentStyle(pStyle); } paragraphStyles.append(pStyle); QString family = style->attributeNS(KoXmlNS::style, "family", "graphic"); style = context.odfLoadingContext().stylesReader().findStyle( style->attributeNS(KoXmlNS::style, "parent-style-name"), family.toLocal8Bit().constData(), context.odfLoadingContext().useStylesAutoStyles()); } // rather than setting default style and apply to block we just set a final parent paragraphStyles.last()->setParentStyle(defaultStyle); QTextDocument *document = this->document(); QTextCursor cursor(document); QTextBlockFormat format; paragraphStyles.first()->applyStyle(format); cursor.setBlockFormat(format); QTextCharFormat cformat; paragraphStyles.first()->KoCharacterStyle::applyStyle(cformat); cursor.setCharFormat(cformat); cursor.setBlockCharFormat(cformat); d->paragraphStyle.reset(new KoParagraphStyle(format, cformat)); qDeleteAll(paragraphStyles); delete defaultStyle; } } void KoTextShapeData::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const { if ((leftPadding() == rightPadding()) && (topPadding() == bottomPadding()) && (rightPadding() == topPadding())) { style.addPropertyPt("fo:padding", leftPadding(), KoGenStyle::GraphicType); } else { if (leftPadding()) { style.addPropertyPt("fo:padding-left", leftPadding(), KoGenStyle::GraphicType); } if (rightPadding()) { style.addPropertyPt("fo:padding-right", rightPadding(), KoGenStyle::GraphicType); } if (topPadding()) { style.addPropertyPt("fo:padding-top", topPadding(), KoGenStyle::GraphicType); } if (bottomPadding()) { style.addPropertyPt("fo:padding-bottom", bottomPadding(), KoGenStyle::GraphicType); } } Q_D(const KoTextShapeData); if (d->paragraphStyle) { d->paragraphStyle->saveOdf(style, context); } } diff --git a/plugins/flake/vectorshape/CMakeLists.txt b/plugins/flake/vectorshape/CMakeLists.txt deleted file mode 100644 index 2d1f9f5995..0000000000 --- a/plugins/flake/vectorshape/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -project(vectorshape) - -set ( VectorShape_SRCS - VectorShapePlugin.cpp - VectorShape.cpp - VectorShapeConfigWidget.cpp - VectorShapeFactory.cpp - VectorTool.cpp - VectorToolFactory.cpp - ChangeVectorDataCommand.cpp -) - -add_library(krita_shape_vector MODULE ${VectorShape_SRCS}) - -target_link_libraries(krita_shape_vector kritaflake kritawidgets kritavectorimage KF5::I18n Qt5::Svg) - -install(TARGETS krita_shape_vector DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) - diff --git a/plugins/flake/vectorshape/ChangeVectorDataCommand.cpp b/plugins/flake/vectorshape/ChangeVectorDataCommand.cpp deleted file mode 100644 index 13d5cf4248..0000000000 --- a/plugins/flake/vectorshape/ChangeVectorDataCommand.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* This file is part of the KDE project - Copyright 2009 Thorsten Zachmann - Copyright 2011 Boudewijn Rempt - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -#include "ChangeVectorDataCommand.h" - -#include -#include -#include - -#include "VectorShape.h" - -ChangeVectorDataCommand::ChangeVectorDataCommand(VectorShape *shape, const QByteArray &newImageData, VectorShape::VectorType newVectorType, KUndo2Command *parent) - : KUndo2Command(parent) - , m_shape(shape) -{ - Q_ASSERT(shape); - m_oldImageData = m_shape->compressedContents(); - m_oldVectorType = m_shape->vectorType(); - m_newImageData = newImageData; - m_newVectorType = newVectorType; - setText(kundo2_i18n("Change Vector Data")); -} - -ChangeVectorDataCommand::~ChangeVectorDataCommand() -{ -} - -void ChangeVectorDataCommand::redo() -{ - m_shape->update(); - m_shape->setCompressedContents(m_newImageData, m_newVectorType); - m_shape->update(); -} - -void ChangeVectorDataCommand::undo() -{ - m_shape->update(); - m_shape->setCompressedContents(m_oldImageData, m_oldVectorType); - m_shape->update(); -} diff --git a/plugins/flake/vectorshape/ChangeVectorDataCommand.h b/plugins/flake/vectorshape/ChangeVectorDataCommand.h deleted file mode 100644 index 9614e6588c..0000000000 --- a/plugins/flake/vectorshape/ChangeVectorDataCommand.h +++ /dev/null @@ -1,48 +0,0 @@ -/* This file is part of the KDE project - Copyright 2009 Thorsten Zachmann - Copyright 2011 Boudewijn Rempt - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -#ifndef CHANGEVECTORDATACOMMAND_H -#define CHANGEVECTORDATACOMMAND_H - -#include -#include - -#include "VectorShape.h" - -class ChangeVectorDataCommand : public KUndo2Command -{ -public: - ChangeVectorDataCommand(VectorShape *shape, const QByteArray &newImageData, VectorShape::VectorType newVectorType, - KUndo2Command *parent = 0); - ~ChangeVectorDataCommand() override; - - /// redo the command - void redo() override; - /// revert the actions done in redo - void undo() override; - -private: - VectorShape *m_shape; - QByteArray m_oldImageData; - VectorShape::VectorType m_oldVectorType; - QByteArray m_newImageData; - VectorShape::VectorType m_newVectorType; -}; - -#endif /* CHANGEVECTORDATACOMMAND_H */ diff --git a/plugins/flake/vectorshape/VectorShape.cpp b/plugins/flake/vectorshape/VectorShape.cpp deleted file mode 100644 index 0d847d54ed..0000000000 --- a/plugins/flake/vectorshape/VectorShape.cpp +++ /dev/null @@ -1,490 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2009 - 2011 Inge Wallin - * Copyright (C) 2011 Boudewijn Rempt - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -// Own -#include "VectorShape.h" - -// Posix -#include - -// Qt -#include -#include -#include -#include -#include -#include -#include -#include - -// Calligra -#include "KoUnit.h" -#include "KoStore.h" -#include "KoXmlNS.h" -#include "KoXmlReader.h" -#include "KoXmlWriter.h" -#include -#include -#include -#include -#include - -// Wmf support -#include "WmfPainterBackend.h" - -// Vector shape -#include "EmfParser.h" -#include "EmfOutputPainterStrategy.h" -#include "EmfOutputDebugStrategy.h" -#include "SvmParser.h" -#include "SvmPainterBackend.h" - -#include "kis_painting_tweaks.h" - -// Comment out to get uncached painting, which is good for debugging -//#define VECTORSHAPE_PAINT_UNCACHED - -// Comment out to get unthreaded painting, which is good for debugging -//#define VECTORSHAPE_PAINT_UNTHREADED - -VectorShape::VectorShape() - : KoFrameShape(KoXmlNS::draw, "image") - , m_type(VectorTypeNone) - , m_isRendering(false) -{ - setShapeId(VectorShape_SHAPEID); - // Default size of the shape. - KoShape::setSize(QSizeF(CM_TO_POINT(8), CM_TO_POINT(5))); - m_cache.setMaxCost(3); -} - -VectorShape::~VectorShape() -{ - // Wait for the render-thread to finish before the shape is allowed to be - // destroyed so we can make sure to prevent crashes or unwanted - // side-effects. Maybe as alternate we could just kill the render-thread... - QMutexLocker locker(&m_mutex); -} - -// Methods specific to the vector shape. -QByteArray VectorShape::compressedContents() const -{ - return m_contents; -} - -VectorShape::VectorType VectorShape::vectorType() const -{ - return m_type; -} - -void VectorShape::setCompressedContents(const QByteArray &newContents, VectorType vectorType) -{ - QMutexLocker locker(&m_mutex); - - m_contents = newContents; - m_type = vectorType; - m_cache.clear(); - update(); -} - -RenderThread::RenderThread(const QByteArray &contents, VectorShape::VectorType type, - const QSizeF &size, const QSize &boundingSize, qreal zoomX, qreal zoomY) - : QObject(), QRunnable(), - m_contents(contents), m_type(type), - m_size(size), m_boundingSize(boundingSize), m_zoomX(zoomX), m_zoomY(zoomY) -{ - setAutoDelete(true); -} - -RenderThread::~RenderThread() -{ -} - -void RenderThread::run() -{ - QImage *image = new QImage(m_boundingSize, QImage::Format_ARGB32); - image->fill(0); - QPainter painter; - if (!painter.begin(image)) { - //kWarning(31000) << "Failed to create image-cache"; - delete image; - image = 0; - } else { - painter.scale(m_zoomX, m_zoomY); - draw(painter); - painter.end(); - } - emit finished(m_boundingSize, image); -} - -void RenderThread::draw(QPainter &painter) -{ - // If the data is uninitialized, e.g. because loading failed, draw the null shape. - if (m_contents.isEmpty()) { - drawNull(painter); - return; - } - - // Actually draw the contents - switch (m_type) { - case VectorShape::VectorTypeWmf: - drawWmf(painter); - break; - case VectorShape::VectorTypeEmf: - drawEmf(painter); - break; - case VectorShape::VectorTypeSvm: - drawSvm(painter); - break; - case VectorShape::VectorTypeSvg: - drawSvg(painter); - break; - case VectorShape::VectorTypeNone: - default: - drawNull(painter); - } -} - -void RenderThread::drawNull(QPainter &painter) const -{ - QRectF rect(QPointF(0, 0), m_size); - painter.save(); - - // Draw a simple cross in a rectangle just to indicate that there is something here. - painter.setPen(QPen(QColor(172, 196, 206))); - painter.drawRect(rect); - painter.drawLine(rect.topLeft(), rect.bottomRight()); - painter.drawLine(rect.bottomLeft(), rect.topRight()); - - painter.restore(); -} - -void RenderThread::drawWmf(QPainter &painter) const -{ - Libwmf::WmfPainterBackend wmfPainter(&painter, m_size); - if (!wmfPainter.load(m_contents)) { - drawNull(painter); - return; - } - painter.save(); - // Actually paint the WMF. - wmfPainter.play(); - painter.restore(); -} - -void RenderThread::drawEmf(QPainter &painter) const -{ - // FIXME: Make emfOutput use QSizeF - QSize shapeSizeInt(m_size.width(), m_size.height()); - //kDebug(31000) << "-------------------------------------------"; - //kDebug(31000) << "size: " << shapeSizeInt << m_size; - //kDebug(31000) << "position: " << position(); - //kDebug(31000) << "-------------------------------------------"; - - Libemf::Parser emfParser; - -#if 1 // Set to 0 to get debug output - // Create a new painter output strategy. Last param = true means keep aspect ratio. - Libemf::OutputPainterStrategy emfPaintOutput(painter, shapeSizeInt, true); - emfParser.setOutput(&emfPaintOutput); -#else - Libemf::OutputDebugStrategy emfDebugOutput; - emfParser.setOutput(&emfDebugOutput); -#endif - emfParser.load(m_contents); -} - -void RenderThread::drawSvm(QPainter &painter) const -{ - QSize shapeSizeInt(m_size.width(), m_size.height()); - - Libsvm::SvmParser svmParser; - - // Create a new painter backend. - Libsvm::SvmPainterBackend svmPaintOutput(&painter, shapeSizeInt); - svmParser.setBackend(&svmPaintOutput); - svmParser.parse(m_contents); -} - -void RenderThread::drawSvg(QPainter &painter) const -{ - QSvgRenderer renderer(m_contents); - renderer.render(&painter, QRectF(0, 0, m_size.width(), m_size.height())); -} - -void VectorShape::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &) -{ -#ifdef VECTORSHAPE_PAINT_UNCACHED - bool useCache = false; -#else - bool useCache = true; -#endif - - QImage *cache = render(converter, true, useCache); - if (cache) { // paint cached image - Q_ASSERT(!cache->isNull()); - QVector clipRects = KisPaintingTweaks::safeClipRegion(painter).rects(); - foreach (const QRect &rc, clipRects) { - painter.drawImage(rc.topLeft(), *cache, rc); - } - } -} - -void VectorShape::renderFinished(QSize boundingSize, QImage *image) -{ - if (image) { - m_cache.insert(boundingSize.height(), image); - update(); - } - m_isRendering = false; -} - -// ---------------------------------------------------------------- -// Loading and Saving - -void VectorShape::saveOdf(KoShapeSavingContext &context) const -{ - QMutexLocker locker(&m_mutex); - - KoEmbeddedDocumentSaver &fileSaver = context.embeddedSaver(); - KoXmlWriter &xmlWriter = context.xmlWriter(); - - QString fileName = fileSaver.getFilename("VectorImages/Image"); - QByteArray mimeType; - - switch (m_type) { - case VectorTypeWmf: - mimeType = "image/x-wmf"; - break; - case VectorTypeEmf: - mimeType = "image/x-emf"; - break; - case VectorTypeSvm: - mimeType = "image/x-svm"; // mimetype as used inside LO/AOO - break; - case VectorTypeSvg: - mimeType = "image/svg+xml"; - default: - // FIXME: What here? - mimeType = "application/x-what"; - break; - } - - xmlWriter.startElement("draw:frame"); - saveOdfAttributes(context, OdfAllAttributes); - fileSaver.embedFile(xmlWriter, "draw:image", fileName, mimeType, qUncompress(m_contents)); - xmlWriter.endElement(); // draw:frame -} - -bool VectorShape::loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) -{ - //kDebug(31000) <<"Loading ODF frame in the vector shape. Element = " << element.tagName(); - loadOdfAttributes(element, context, OdfAllAttributes); - return loadOdfFrame(element, context); -} - -inline static int read32(const char *buffer, const int offset) -{ - // little endian - int result = (int) buffer[offset]; - result |= (int) buffer[offset + 1] << 8; - result |= (int) buffer[offset + 2] << 16; - result |= (int) buffer[offset + 3] << 24; - - return result; -} - -// Load the actual contents within the vector shape. -bool VectorShape::loadOdfFrameElement(const KoXmlElement &element, - KoShapeLoadingContext &context) -{ - //kDebug(31000) <<"Loading ODF element: " << element.tagName(); - QMutexLocker locker(&m_mutex); - - // Get the reference to the vector file. If there is no href, then just return. - const QString href = element.attribute("href"); - if (href.isEmpty()) { - return false; - } - - // Try to open the embedded file. - KoStore *store = context.odfLoadingContext().store(); - bool result = store->open(href); - - if (!result) { - return false; - } - - int size = store->size(); - if (size < 88) { - store->close(); - return false; - } - - m_contents = store->read(size); - store->close(); - if (m_contents.count() < size) { - //kDebug(31000) << "Too few bytes read: " << m_contents.count() << " instead of " << size; - return false; - } - - // Try to recognize the type. We should do this before the - // compression below, because that's a semi-expensive operation. - m_type = vectorType(m_contents); - - // Return false if we didn't manage to identify the type. - if (m_type == VectorTypeNone) { - return false; - } - - // Compress for biiiig memory savings. - m_contents = qCompress(m_contents); - - return true; -} - -void VectorShape::waitUntilReady(const KoViewConverter &converter, bool asynchronous) const -{ - render(converter, asynchronous, true); -} - -QImage *VectorShape::render(const KoViewConverter &converter, bool asynchronous, bool useCache) const -{ - QRectF rect = converter.documentToView(boundingRect()); - int id = rect.size().toSize().height(); - QImage *cache = useCache ? m_cache[id] : 0; - - if (!cache || cache->isNull()) { // recreate the cached image - cache = 0; - if (!m_isRendering) { - m_isRendering = true; - qreal zoomX, zoomY; - converter.zoom(&zoomX, &zoomY); - QMutexLocker locker(&m_mutex); - const QByteArray uncompressedContents = - m_type != VectorShape::VectorTypeNone ? qUncompress(m_contents) : QByteArray(); - RenderThread *t = new RenderThread(uncompressedContents, m_type, size(), rect.size().toSize(), zoomX, zoomY); - connect(t, SIGNAL(finished(QSize,QImage*)), this, SLOT(renderFinished(QSize,QImage*))); - if (asynchronous) { // render and paint the image threaded - QThreadPool::globalInstance()->start(t); - } else { // non-threaded rendering and painting of the image - t->run(); - cache = m_cache[id]; - } - } - } - - return cache; -} - -VectorShape::VectorType VectorShape::vectorType(const QByteArray &newContents) -{ - VectorType vectorType; - - if (isWmf(newContents)) { - vectorType = VectorShape::VectorTypeWmf; - } else if (isEmf(newContents)) { - vectorType = VectorShape::VectorTypeEmf; - } else if (isSvm(newContents)) { - vectorType = VectorShape::VectorTypeSvm; - } else if (isSvg(newContents)) { - vectorType = VectorShape::VectorTypeSvg; - } else { - vectorType = VectorShape::VectorTypeNone; - } - - return vectorType; -} - -bool VectorShape::isWmf(const QByteArray &bytes) -{ - //kDebug(31000) << "Check for WMF"; - - const char *data = bytes.constData(); - const int size = bytes.count(); - - if (size < 10) { - return false; - } - - // This is how the 'file' command identifies a WMF. - if (data[0] == '\327' && data[1] == '\315' && data[2] == '\306' && data[3] == '\232') { - // FIXME: Is this a compressed wmf? Check it up. - //kDebug(31000) << "WMF identified: header 1"; - return true; - } - - if (data[0] == '\002' && data[1] == '\000' && data[2] == '\011' && data[3] == '\000') { - //kDebug(31000) << "WMF identified: header 2"; - return true; - } - - if (data[0] == '\001' && data[1] == '\000' && data[2] == '\011' && data[3] == '\000') { - //kDebug(31000) << "WMF identified: header 3"; - return true; - } - - return false; -} - -bool VectorShape::isEmf(const QByteArray &bytes) -{ - //kDebug(31000) << "Check for EMF"; - - const char *data = bytes.constData(); - const int size = bytes.count(); - - // This is how the 'file' command identifies an EMF. - // 1. Check type - qint32 mark = read32(data, 0); - if (mark != 0x00000001) { - //kDebug(31000) << "Not an EMF: mark = " << mark << " instead of 0x00000001"; - return false; - } - - // 2. An EMF has the string " EMF" at the start + offset 40. - if (size > 44 - && data[40] == ' ' && data[41] == 'E' && data[42] == 'M' && data[43] == 'F') { - //kDebug(31000) << "EMF identified"; - return true; - } - - return false; -} - -bool VectorShape::isSvm(const QByteArray &bytes) -{ - //kDebug(31000) << "Check for SVM"; - - // Check the SVM signature. - if (bytes.startsWith("VCLMTF")) { - //kDebug(31000) << "SVM identified"; - return true; - } - - return false; -} - -bool VectorShape::isSvg(const QByteArray &bytes) -{ - //kDebug(31000) << "Check for SVG"; - return (bytes.contains("svg")); -} diff --git a/plugins/flake/vectorshape/VectorShape.h b/plugins/flake/vectorshape/VectorShape.h deleted file mode 100644 index 9f0d5e6d18..0000000000 --- a/plugins/flake/vectorshape/VectorShape.h +++ /dev/null @@ -1,125 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2009-2011 Inge Wallin - * Copyright (C) 2011 Boudewijn Rempt - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef VECTORSHAPE_H -#define VECTORSHAPE_H - -// Qt -#include -#include -#include -#include -#include - -// Calligra -#include -#include - -#define DEBUG_VECTORSHAPE 0 - -class QPainter; -class VectorShape; - -#define VectorShape_SHAPEID "VectorShapeID" - -class VectorShape : public QObject, public KoShape, public KoFrameShape -{ - Q_OBJECT -public: - // Type of vector file. Add here when we get support for more. - enum VectorType { - VectorTypeNone, // Uninitialized - VectorTypeWmf, // Windows MetaFile - VectorTypeEmf, // Extended MetaFile - VectorTypeSvm, // StarView Metafile - VectorTypeSvg // Scalable Vector Graphics - // ... more here later - }; - - VectorShape(); - ~VectorShape() override; - - // reimplemented methods. - - /// reimplemented from KoShape - void paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext) override; - /// reimplemented from KoShape - void saveOdf(KoShapeSavingContext &context) const override; - /// reimplemented from KoShape - bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) override; - /// Load the real contents of the frame shape. reimplemented from KoFrameShape - bool loadOdfFrameElement(const KoXmlElement &frameElement, - KoShapeLoadingContext &context) override; - /// reimplemented from KoShape - void waitUntilReady(const KoViewConverter &converter, bool asynchronous = true) const override; - - // Methods specific to the vector shape. - QByteArray compressedContents() const; - VectorType vectorType() const; - void setCompressedContents(const QByteArray &newContents, VectorType vectorType); - - static VectorShape::VectorType vectorType(const QByteArray &contents); - -private Q_SLOTS: - void renderFinished(QSize boundingSize, QImage *image); - -private: - static bool isWmf(const QByteArray &bytes); - static bool isEmf(const QByteArray &bytes); - static bool isSvm(const QByteArray &bytes); - static bool isSvg(const QByteArray &bytes); - - // Member variables - mutable VectorType m_type; - mutable QByteArray m_contents; - mutable bool m_isRendering; - mutable QMutex m_mutex; - QCache m_cache; - - QImage *render(const KoViewConverter &converter, bool asynchronous, bool useCache) const; -}; - -class RenderThread : public QObject, public QRunnable -{ - Q_OBJECT -public: - RenderThread(const QByteArray &contents, VectorShape::VectorType type, - const QSizeF &size, const QSize &boundingSize, qreal zoomX, qreal zoomY); - ~RenderThread() override; - void run() override; -Q_SIGNALS: - void finished(QSize boundingSize, QImage *image); -private: - void draw(QPainter &painter); - void drawNull(QPainter &painter) const; - void drawWmf(QPainter &painter) const; - void drawEmf(QPainter &painter) const; - void drawSvm(QPainter &painter) const; - void drawSvg(QPainter &painter) const; -private: - const QByteArray m_contents; - VectorShape::VectorType m_type; - QSizeF m_size; - QSize m_boundingSize; - qreal m_zoomX, m_zoomY; -}; - -#endif diff --git a/plugins/flake/vectorshape/VectorShapeConfigWidget.cpp b/plugins/flake/vectorshape/VectorShapeConfigWidget.cpp deleted file mode 100644 index 3da840e787..0000000000 --- a/plugins/flake/vectorshape/VectorShapeConfigWidget.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2008 Jan Hambrecht - * Copyright 2012 Friedrich W. H. Kossebau - * - * 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 "VectorShapeConfigWidget.h" - -#include "VectorShape.h" -// Qt -#include -#include -#include - -#include - -#include - -VectorShapeConfigWidget::VectorShapeConfigWidget() - : m_shape(0) - , m_fileWidget(0) -{ -} - -VectorShapeConfigWidget::~VectorShapeConfigWidget() -{ - delete m_fileWidget; -} - -void VectorShapeConfigWidget::open(KoShape *shape) -{ - m_shape = dynamic_cast(shape); - Q_ASSERT(m_shape); - delete m_fileWidget; - QVBoxLayout *layout = new QVBoxLayout(this); - m_fileWidget = new KisFileNameRequester(this); - m_fileWidget->setMode(KoFileDialog::OpenFile); - const QStringList mimetypes = QStringList() - << QLatin1String("image/x-wmf") - << QLatin1String("image/x-emf") - << QLatin1String("image/x-svm") - << QLatin1String("image/svg+xml"); - m_fileWidget->setMimeTypeFilters(mimetypes); - layout->addWidget(m_fileWidget); - setLayout(layout); - QPushButton *bn = new QPushButton(this); - bn->setText(i18n("Replace Image")); - layout->addWidget(bn); - connect(bn, SIGNAL(clicked()), this, SIGNAL(accept())); -} - -void VectorShapeConfigWidget::save() -{ - if (!m_shape) { - return; - } - QString fn = m_fileWidget->fileName(); - if (!fn.isEmpty()) { - QFile f(fn); - if (f.exists()) { - f.open(QFile::ReadOnly); - QByteArray ba = f.readAll(); - f.close(); - if (!ba.isEmpty()) { - const VectorShape::VectorType vectorType = VectorShape::vectorType(ba); - m_shape->setCompressedContents(qCompress(ba), vectorType); - } - } - } -} - -bool VectorShapeConfigWidget::showOnShapeCreate() -{ - return true; -} - -bool VectorShapeConfigWidget::showOnShapeSelect() -{ - return false; -} diff --git a/plugins/flake/vectorshape/VectorShapeConfigWidget.h b/plugins/flake/vectorshape/VectorShapeConfigWidget.h deleted file mode 100644 index b01571d949..0000000000 --- a/plugins/flake/vectorshape/VectorShapeConfigWidget.h +++ /dev/null @@ -1,51 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2008 Jan Hambrecht - * Copyright 2012 Friedrich W. H. Kossebau - * - * 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 VECTORSHAPECONFIGWIDGET_H -#define VECTORSHAPECONFIGWIDGET_H - -#include -#include - -class VectorShape; -class KisFileNameRequester; - -class VectorShapeConfigWidget : public KoShapeConfigWidgetBase -{ - Q_OBJECT -public: - VectorShapeConfigWidget(); - ~VectorShapeConfigWidget() override; - - /// reimplemented from KoShapeConfigWidgetBase - void open(KoShape *shape) override; - /// reimplemented from KoShapeConfigWidgetBase - void save() override; - /// reimplemented from KoShapeConfigWidgetBase - bool showOnShapeCreate() override; - /// reimplemented from KoShapeConfigWidgetBase - bool showOnShapeSelect() override; - -private: - VectorShape *m_shape; - KisFileNameRequester *m_fileWidget; -}; - -#endif //VECTORSHAPECONFIGWIDGET_H diff --git a/plugins/flake/vectorshape/VectorShapeFactory.cpp b/plugins/flake/vectorshape/VectorShapeFactory.cpp deleted file mode 100644 index 58875f76a6..0000000000 --- a/plugins/flake/vectorshape/VectorShapeFactory.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2009 Inge Wallin - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -// Own -#include "VectorShapeFactory.h" - -// VectorShape -#include "VectorShape.h" -#include "VectorShapeConfigWidget.h" - -// Calligra -#include -#include -#include -#include - -// KDE -#include - -VectorShapeFactory::VectorShapeFactory() - : KoShapeFactoryBase(VectorShape_SHAPEID, i18n("Vector image")) -{ - setToolTip(i18n("A shape that shows a vector image (EMF/WMF/SVM)")); - setIconName(koIconNameCStrNeededWithSubs("a generic vector image icon", "x-shape-vectorimage", "application-x-wmf")); - setXmlElementNames(KoXmlNS::draw, QStringList("image")); - setLoadingPriority(5); -} - -KoShape *VectorShapeFactory::createDefaultShape(KoDocumentResourceManager */*documentResources*/) const -{ - VectorShape *shape = new VectorShape(); - shape->setShapeId(VectorShape_SHAPEID); - - return shape; -} - -bool VectorShapeFactory::supports(const KoXmlElement &e, KoShapeLoadingContext &context) const -{ - if (e.localName() == "image" && e.namespaceURI() == KoXmlNS::draw) { - QString href = e.attribute("href"); - if (!href.isEmpty()) { - // check the mimetype - if (href.startsWith(QLatin1String("./"))) { - href.remove(0, 2); - } - // LO 3.5 does not write a mimetype for embedded wmf files, so guess also from content - const QString mimetype = context.odfLoadingContext().mimeTypeForPath(href, true); - - return - mimetype == QLatin1String("image/x-svm") || - mimetype == QLatin1String("image/x-emf") || - mimetype == QLatin1String("image/x-wmf") || - // Note: the Vector Shape supports SVG, but _NOT_ in this method, otherwise it will stomp all over loading the artistic text shape's svg - //mimetype == QLatin1String("image/svg+xml") || - // next three for backward compatibility with Calligra - mimetype == QLatin1String("application/x-svm") || - mimetype == QLatin1String("application/x-emf") || - mimetype == QLatin1String("application/x-wmf") || - // seems like MSO does not always write a mimetype - // see jeffcoweb.jeffco.k12.co.us%2Fhigh%2Fchatfield%2Fdepartments%2Fbusiness%2Fbanking_finance%2Funit_Plan_Budget.odp - mimetype.isEmpty() || - // next for compatibility with OO/LO and our filters - // see drwho.virtadpt.net%2Ffiles%2FNOVALUG-Tor.odp - mimetype.startsWith("application/x-openoffice"); - } - return true; - } - - return false; -} - -QList VectorShapeFactory::createShapeOptionPanels() -{ - QList result; - result.append(new VectorShapeConfigWidget()); - return result; -} diff --git a/plugins/flake/vectorshape/VectorShapeFactory.h b/plugins/flake/vectorshape/VectorShapeFactory.h deleted file mode 100644 index 0e3930643e..0000000000 --- a/plugins/flake/vectorshape/VectorShapeFactory.h +++ /dev/null @@ -1,45 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2009 Inge Wallin - * - * 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 VECTORSHAPE_FACTORY_H -#define VECTORSHAPE_FACTORY_H - -// Calligra -#include - -class KoShape; - -class VectorShapeFactory : public KoShapeFactoryBase -{ - -public: - /// constructor - VectorShapeFactory(); - ~VectorShapeFactory() override {} - - KoShape *createDefaultShape(KoDocumentResourceManager *documentResources = 0) const override; - - /// Reimplemented - bool supports(const KoXmlElement &e, KoShapeLoadingContext &context) const override; - - QList createShapeOptionPanels() override; -}; - -#endif diff --git a/plugins/flake/vectorshape/VectorShapePlugin.cpp b/plugins/flake/vectorshape/VectorShapePlugin.cpp deleted file mode 100644 index bb510a3e72..0000000000 --- a/plugins/flake/vectorshape/VectorShapePlugin.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2009 Inge Wallin - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -// Own -#include "VectorShapePlugin.h" - -// KDE -#include - -// Calligra libs -#include -#include - -// VectorShape -#include "VectorToolFactory.h" -#include "VectorShapeFactory.h" - -K_PLUGIN_FACTORY_WITH_JSON(VectorShapePluginFactory, "calligra_shape_vector.json", registerPlugin();) - -VectorShapePlugin::VectorShapePlugin(QObject *parent, const QVariantList &) - : QObject(parent) -{ - KoToolRegistry::instance()->add(new VectorToolFactory()); - KoShapeRegistry::instance()->add(new VectorShapeFactory()); -} - -#include diff --git a/plugins/flake/vectorshape/VectorTool.cpp b/plugins/flake/vectorshape/VectorTool.cpp deleted file mode 100644 index 8b3aff4cdb..0000000000 --- a/plugins/flake/vectorshape/VectorTool.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* This file is part of the KDE project - Copyright 2007 Montel Laurent - Copyright 2011 Boudewijn Rempt - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include "VectorTool.h" -#include "VectorShape.h" -#include "ChangeVectorDataCommand.h" - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -VectorTool::VectorTool(KoCanvasBase *canvas) - : KoToolBase(canvas) - , m_shape(0) -{ -} - -void VectorTool::activate(ToolActivation activation, const QSet &shapes) -{ - KoToolBase::activate(activation, shapes); - - foreach (KoShape *shape, shapes) { - m_shape = dynamic_cast(shape); - if (m_shape) { - break; - } - } - if (!m_shape) { - emit done(); - return; - } - useCursor(Qt::ArrowCursor); -} - -void VectorTool::deactivate() -{ - m_shape = 0; - KoToolBase::deactivate(); -} - -QWidget *VectorTool::createOptionWidget() -{ - QWidget *optionWidget = new QWidget(); - QGridLayout *layout = new QGridLayout(optionWidget); - - QToolButton *button = 0; - - button = new QToolButton(optionWidget); - button->setIcon(koIcon("document-open")); - button->setToolTip(i18n("Open Vector Image (EMF/WMF/SVM)")); - layout->addWidget(button, 0, 0); - connect(button, SIGNAL(clicked(bool)), this, SLOT(changeUrlPressed())); - - return optionWidget; -} - -void VectorTool::changeUrlPressed() -{ - if (m_shape == 0) { - return; - } - KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); - dialog.setCaption(i18n("Select a Vector Image")); - dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); - dialog.setMimeTypeFilters(QString("image/x-emf,image/x-wmf,image/x-svm,image/svg+xml").split(',')); - QString fn = dialog.filename(); - if (!fn.isEmpty()) { - QFile f(fn); - if (f.exists()) { - f.open(QFile::ReadOnly); - QByteArray ba = f.readAll(); - f.close(); - if (!ba.isEmpty()) { - const VectorShape::VectorType vectorType = VectorShape::vectorType(ba); - ChangeVectorDataCommand *cmd = new ChangeVectorDataCommand(m_shape, qCompress(ba), vectorType); - canvas()->addCommand(cmd); - } - } - } -} - -void VectorTool::mouseDoubleClickEvent(KoPointerEvent *event) -{ - if (canvas()->shapeManager()->shapeAt(event->point) != m_shape) { - event->ignore(); // allow the event to be used by another - return; - } - changeUrlPressed(); -} - diff --git a/plugins/flake/vectorshape/VectorTool.h b/plugins/flake/vectorshape/VectorTool.h deleted file mode 100644 index b1ff16b2f4..0000000000 --- a/plugins/flake/vectorshape/VectorTool.h +++ /dev/null @@ -1,60 +0,0 @@ -/* This file is part of the KDE project - Copyright 2011 Boudewijn Rempt - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef VECTOR_TOOL -#define VECTOR_TOOL - -#include - -class VectorShape; - -class VectorTool : public KoToolBase -{ - Q_OBJECT -public: - explicit VectorTool(KoCanvasBase *canvas); - - /// reimplemented from KoToolBase - void paint(QPainter &, const KoViewConverter &) override {} - /// reimplemented from KoToolBase - void mousePressEvent(KoPointerEvent *) override {} - /// reimplemented from superclass - void mouseDoubleClickEvent(KoPointerEvent *event) override; - /// reimplemented from KoToolBase - void mouseMoveEvent(KoPointerEvent *) override {} - /// reimplemented from KoToolBase - void mouseReleaseEvent(KoPointerEvent *) override {} - - /// reimplemented from KoToolBase - void activate(ToolActivation activation, const QSet &shapes) override; - /// reimplemented from KoToolBase - void deactivate() override; - -protected: - /// reimplemented from KoToolBase - QWidget *createOptionWidget() override; - -private Q_SLOTS: - void changeUrlPressed(); - -private: - VectorShape *m_shape; -}; - -#endif diff --git a/plugins/flake/vectorshape/VectorToolFactory.cpp b/plugins/flake/vectorshape/VectorToolFactory.cpp deleted file mode 100644 index 9e15a8aa43..0000000000 --- a/plugins/flake/vectorshape/VectorToolFactory.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* This file is part of the KDE project - - Copyright 2011 Boudewijn Rempt - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include "VectorToolFactory.h" - -#include "VectorShape.h" -#include "VectorTool.h" - -#include -#include - -VectorToolFactory::VectorToolFactory() - : KoToolFactoryBase("VectorTool") -{ - setToolTip(i18n("Vector Image (EMF/WMF/SVM/SVG) tool")); - setIconName(koIconNameCStrNeededWithSubs("a generic vector image icon", "x-shape-vectorimage", "application-x-wmf")); - setSection(dynamicToolType()); - setPriority(2); - setActivationShapeId(VectorShape_SHAPEID); -} - -VectorToolFactory::~VectorToolFactory() -{ -} - -KoToolBase *VectorToolFactory::createTool(KoCanvasBase *canvas) -{ - return new VectorTool(canvas); -} - diff --git a/plugins/flake/vectorshape/VectorToolFactory.h b/plugins/flake/vectorshape/VectorToolFactory.h deleted file mode 100644 index b54a4cbc15..0000000000 --- a/plugins/flake/vectorshape/VectorToolFactory.h +++ /dev/null @@ -1,34 +0,0 @@ -/* This file is part of the KDE project - Copyright 2007 Montel Laurent - - 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 VECTOR_TOOL_FACTORY -#define VECTOR_TOOL_FACTORY - -#include - -class VectorToolFactory : public KoToolFactoryBase -{ -public: - VectorToolFactory(); - ~VectorToolFactory() override; - - KoToolBase *createTool(KoCanvasBase *canvas) override; -}; - -#endif diff --git a/plugins/flake/vectorshape/calligra_shape_vector.json b/plugins/flake/vectorshape/calligra_shape_vector.json deleted file mode 100644 index 94a83b16a8..0000000000 --- a/plugins/flake/vectorshape/calligra_shape_vector.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "Id": "Static Vector Shape", - "Type": "Service", - "X-Flake-MinVersion": "28", - "X-Flake-PluginVersion": "28", - "X-KDE-Library": "krita_shape_vector", - "X-KDE-PluginInfo-Name": "vectorshape", - "X-KDE-ServiceTypes": [ - "Calligra/Flake" - ] -} diff --git a/plugins/impex/bmp/krita_bmp.desktop b/plugins/impex/bmp/krita_bmp.desktop index 3a3a159f64..2ff49909eb 100644 --- a/plugins/impex/bmp/krita_bmp.desktop +++ b/plugins/impex/bmp/krita_bmp.desktop @@ -1,70 +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 %u MimeType=image/bmp; Type=Application Icon=calligrakrita Categories=Qt;KDE;Office;Graphics; StartupNotify=true NoDisplay=true diff --git a/plugins/impex/brush/krita_brush.desktop b/plugins/impex/brush/krita_brush.desktop index df8e9f95a7..a0eb1ba611 100644 --- a/plugins/impex/brush/krita_brush.desktop +++ b/plugins/impex/brush/krita_brush.desktop @@ -1,72 +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 %u 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 b411bba3ad..3e0b42747c 100644 --- a/plugins/impex/csv/krita_csv.desktop +++ b/plugins/impex/csv/krita_csv.desktop @@ -1,70 +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 %u 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 6968848ee3..b6148effeb 100644 --- a/plugins/impex/exr/krita_exr.desktop +++ b/plugins/impex/exr/krita_exr.desktop @@ -1,122 +1,124 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Exec=krita %u 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/heightmap/krita_heightmap.desktop b/plugins/impex/heightmap/krita_heightmap.desktop index 248af1f70c..27a3972576 100644 --- a/plugins/impex/heightmap/krita_heightmap.desktop +++ b/plugins/impex/heightmap/krita_heightmap.desktop @@ -1,74 +1,75 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Exec=krita %u 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/kis_jpeg_export.cc b/plugins/impex/jpeg/kis_jpeg_export.cc index b5f40f0aec..ff71e58c98 100644 --- a/plugins/impex/jpeg/kis_jpeg_export.cc +++ b/plugins/impex/jpeg/kis_jpeg_export.cc @@ -1,300 +1,302 @@ /* * Copyright (c) 2005 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_jpeg_export.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 "kis_jpeg_converter.h" class KisExternalLayer; K_PLUGIN_FACTORY_WITH_JSON(KisJPEGExportFactory, "krita_jpeg_export.json", registerPlugin();) KisJPEGExport::KisJPEGExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } KisJPEGExport::~KisJPEGExport() { } KisImportExportFilter::ConversionStatus KisJPEGExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration) { KisImageSP image = document->savingImage(); Q_CHECK_PTR(image); // An extra option to pass to the config widget to set the state correctly, this isn't saved const KoColorSpace* cs = image->projection()->colorSpace(); bool sRGB = cs->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive); configuration->setProperty("is_sRGB", sRGB); KisJPEGOptions options; options.progressive = configuration->getBool("progressive", false); options.quality = configuration->getInt("quality", 80); options.forceSRGB = configuration->getBool("forceSRGB", false); options.saveProfile = configuration->getBool("saveProfile", true); options.optimize = configuration->getBool("optimize", true); options.smooth = configuration->getInt("smoothing", 0); options.baseLineJPEG = configuration->getBool("baseline", true); options.subsampling = configuration->getInt("subsampling", 0); options.exif = configuration->getBool("exif", true); options.iptc = configuration->getBool("iptc", true); options.xmp = configuration->getBool("xmp", true); KoColor c(KoColorSpaceRegistry::instance()->rgb8()); c.fromQColor(Qt::white); options.transparencyFillColor = configuration->getColor("transparencyFillcolor", c).toQColor(); KisMetaData::FilterRegistryModel m; m.setEnabledFilters(configuration->getString("filters").split(",")); options.filters = m.enabledFilters(); options.storeAuthor = configuration->getBool("storeAuthor", false); options.storeDocumentMetaData = configuration->getBool("storeMetaData", false); KisPaintDeviceSP pd = new KisPaintDevice(*image->projection()); KisJPEGConverter kpc(document, batchMode()); KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd); KisExifInfoVisitor eIV; eIV.visit(image->rootLayer().data()); KisMetaData::Store* eI = 0; if (eIV.countPaintLayer() == 1) { eI = eIV.exifInfo(); } if (eI) { KisMetaData::Store* copy = new KisMetaData::Store(*eI); eI = copy; } if (!eI) { eI = new KisMetaData::Store(); } //add extra meta-data here const KisMetaData::Schema* dcSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::DublinCoreSchemaUri); Q_ASSERT(dcSchema); if (options.storeDocumentMetaData) { QString title = document->documentInfo()->aboutInfo("title"); if (!title.isEmpty()) { if (eI->containsEntry("title")) { eI->removeEntry("title"); } eI->addEntry(KisMetaData::Entry(dcSchema, "title", KisMetaData::Value(QVariant(title)))); } QString description = document->documentInfo()->aboutInfo("subject"); if (description.isEmpty()) { description = document->documentInfo()->aboutInfo("abstract"); } if (!description.isEmpty()) { QString keywords = document->documentInfo()->aboutInfo("keywords"); if (!keywords.isEmpty()) { description = description + "keywords: " + keywords; } if (eI->containsEntry("description")) { eI->removeEntry("description"); } eI->addEntry(KisMetaData::Entry(dcSchema, "description", KisMetaData::Value(QVariant(description)))); } QString license = document->documentInfo()->aboutInfo("license"); if (!title.isEmpty()) { if (eI->containsEntry("rights")) { eI->removeEntry("rights"); } eI->addEntry(KisMetaData::Entry(dcSchema, "rights", KisMetaData::Value(QVariant(license)))); } QString date = document->documentInfo()->aboutInfo("date"); if (!date.isEmpty() && !eI->containsEntry("rights")) { eI->addEntry(KisMetaData::Entry(dcSchema, "date", KisMetaData::Value(QVariant(date)))); } } if (options.storeAuthor) { QString author = document->documentInfo()->authorInfo("creator"); if (!author.isEmpty()) { - QString contact = document->documentInfo()->authorContactInfo().at(0); - if (!contact.isEmpty()) { - author = author+"("+contact+")"; + if (!document->documentInfo()->authorContactInfo().isEmpty()) { + QString contact = document->documentInfo()->authorContactInfo().at(0); + if (!contact.isEmpty()) { + author = author+"("+contact+")"; + } } if (eI->containsEntry("creator")) { eI->removeEntry("creator"); } eI->addEntry(KisMetaData::Entry(dcSchema, "creator", KisMetaData::Value(QVariant(author)))); } } KisImageBuilder_Result res = kpc.buildFile(io, l, options, eI); if (res == KisImageBuilder_RESULT_OK) { delete eI; return KisImportExportFilter::OK; } delete eI; dbgFile << " Result =" << res; return KisImportExportFilter::InternalError; } KisPropertiesConfigurationSP KisJPEGExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const { KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); cfg->setProperty("progressive", false); cfg->setProperty("quality", 80); cfg->setProperty("forceSRGB", false); cfg->setProperty("saveProfile", true); cfg->setProperty("optimize", true); cfg->setProperty("smoothing", 0); cfg->setProperty("baseline", true); cfg->setProperty("subsampling", 0); cfg->setProperty("exif", true); cfg->setProperty("iptc", true); cfg->setProperty("xmp", true); cfg->setProperty("storeAuthor", false); cfg->setProperty("storeMetaData", false); KoColor fill_color(KoColorSpaceRegistry::instance()->rgb8()); fill_color = KoColor(); fill_color.fromQColor(Qt::white); QVariant v; v.setValue(fill_color); cfg->setProperty("transparencyFillcolor", v); cfg->setProperty("filters", ""); return cfg; } KisConfigWidget *KisJPEGExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const { return new KisWdgOptionsJPEG(parent); } void KisJPEGExport::initializeCapabilities() { addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED)); addCapability(KisExportCheckRegistry::instance()->get("ExifCheck")->create(KisExportCheckBase::SUPPORTED)); QList > supportedColorModels; supportedColorModels << QPair() << QPair(RGBAColorModelID, Integer8BitsColorDepthID) << QPair(GrayAColorModelID, Integer8BitsColorDepthID) << QPair(CMYKAColorModelID, Integer8BitsColorDepthID); addSupportedColorModels(supportedColorModels, "JPEG"); } KisWdgOptionsJPEG::KisWdgOptionsJPEG(QWidget *parent) : KisConfigWidget(parent) { setupUi(this); metaDataFilters->setModel(&m_filterRegistryModel); qualityLevel->setRange(0, 100, 0); qualityLevel->setSuffix("%"); smoothLevel->setRange(0, 100, 0); smoothLevel->setSuffix("%"); } void KisWdgOptionsJPEG::setConfiguration(const KisPropertiesConfigurationSP cfg) { progressive->setChecked(cfg->getBool("progressive", false)); qualityLevel->setValue(cfg->getInt("quality", 80)); optimize->setChecked(cfg->getBool("optimize", true)); smoothLevel->setValue(cfg->getInt("smoothing", 0)); baseLineJPEG->setChecked(cfg->getBool("baseline", true)); subsampling->setCurrentIndex(cfg->getInt("subsampling", 0)); exif->setChecked(cfg->getBool("exif", true)); iptc->setChecked(cfg->getBool("iptc", true)); xmp->setChecked(cfg->getBool("xmp", true)); chkForceSRGB->setVisible(cfg->getBool("is_sRGB")); chkForceSRGB->setChecked(cfg->getBool("forceSRGB", false)); chkSaveProfile->setChecked(cfg->getBool("saveProfile", true)); KoColor background(KoColorSpaceRegistry::instance()->rgb8()); background.fromQColor(Qt::white); bnTransparencyFillColor->setDefaultColor(background); bnTransparencyFillColor->setColor(cfg->getColor("transparencyFillcolor", background)); chkAuthor->setChecked(cfg->getBool("storeAuthor", false)); chkMetaData->setChecked(cfg->getBool("storeMetaData", false)); m_filterRegistryModel.setEnabledFilters(cfg->getString("filters").split(',')); } KisPropertiesConfigurationSP KisWdgOptionsJPEG::configuration() const { KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); QVariant transparencyFillcolor; transparencyFillcolor.setValue(bnTransparencyFillColor->color()); cfg->setProperty("progressive", progressive->isChecked()); cfg->setProperty("quality", (int)qualityLevel->value()); cfg->setProperty("forceSRGB", chkForceSRGB->isChecked()); cfg->setProperty("saveProfile", chkSaveProfile->isChecked()); cfg->setProperty("optimize", optimize->isChecked()); cfg->setProperty("smoothing", (int)smoothLevel->value()); cfg->setProperty("baseline", baseLineJPEG->isChecked()); cfg->setProperty("subsampling", subsampling->currentIndex()); cfg->setProperty("exif", exif->isChecked()); cfg->setProperty("iptc", iptc->isChecked()); cfg->setProperty("xmp", xmp->isChecked()); cfg->setProperty("transparencyFillcolor", transparencyFillcolor); cfg->setProperty("storeAuthor", chkAuthor->isChecked()); cfg->setProperty("storeMetaData", chkMetaData->isChecked()); QString enabledFilters; Q_FOREACH (const KisMetaData::Filter* filter, m_filterRegistryModel.enabledFilters()) { enabledFilters = enabledFilters + filter->id() + ','; } cfg->setProperty("filters", enabledFilters); return cfg; } #include diff --git a/plugins/impex/jpeg/krita_jpeg.desktop b/plugins/impex/jpeg/krita_jpeg.desktop index 421995c747..bfe9cc616c 100644 --- a/plugins/impex/jpeg/krita_jpeg.desktop +++ b/plugins/impex/jpeg/krita_jpeg.desktop @@ -1,73 +1,74 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Exec=krita %u 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/kra_converter.cpp b/plugins/impex/kra/kra_converter.cpp index 8724b36ae2..b8d1b44130 100644 --- a/plugins/impex/kra/kra_converter.cpp +++ b/plugins/impex/kra/kra_converter.cpp @@ -1,352 +1,356 @@ /* * Copyright (C) 2016 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kra_converter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char CURRENT_DTD_VERSION[] = "2.0"; KraConverter::KraConverter(KisDocument *doc) : m_doc(doc) , m_image(doc->savingImage()) { } KraConverter::~KraConverter() { delete m_store; delete m_kraSaver; delete m_kraLoader; } KisImageBuilder_Result KraConverter::buildImage(QIODevice *io) { m_store = KoStore::createStore(io, KoStore::Read, "", KoStore::Zip); if (m_store->bad()) { m_doc->setErrorMessage(i18n("Not a valid Krita file")); return KisImageBuilder_RESULT_FAILURE; } bool success; { if (m_store->hasFile("root") || m_store->hasFile("maindoc.xml")) { // Fallback to "old" file format (maindoc.xml) KoXmlDocument doc; bool ok = oldLoadAndParse(m_store, "root", doc); if (ok) ok = loadXML(doc, m_store); if (!ok) { return KisImageBuilder_RESULT_FAILURE; } } else { errUI << "ERROR: No maindoc.xml" << endl; m_doc->setErrorMessage(i18n("Invalid document: no file 'maindoc.xml'.")); return KisImageBuilder_RESULT_FAILURE; } if (m_store->hasFile("documentinfo.xml")) { KoXmlDocument doc; if (oldLoadAndParse(m_store, "documentinfo.xml", doc)) { m_doc->documentInfo()->load(doc); } } success = completeLoading(m_store); } return success ? KisImageBuilder_RESULT_OK : KisImageBuilder_RESULT_FAILURE; } KisImageSP KraConverter::image() { return m_image; } vKisNodeSP KraConverter::activeNodes() { return m_activeNodes; } QList KraConverter::assistants() { return m_assistants; } KisImageBuilder_Result KraConverter::buildFile(QIODevice *io) { m_store = KoStore::createStore(io, KoStore::Write, m_doc->nativeFormatMimeType(), KoStore::Zip); if (m_store->bad()) { m_doc->setErrorMessage(i18n("Could not create the file for saving")); return KisImageBuilder_RESULT_FAILURE; } bool result = false; m_kraSaver = new KisKraSaver(m_doc); result = saveRootDocuments(m_store); if (!result) { return KisImageBuilder_RESULT_FAILURE; } result = m_kraSaver->saveKeyframes(m_store, m_doc->url().toLocalFile(), true); if (!result) { qWarning() << "saving key frames failed"; } result = m_kraSaver->saveBinaryData(m_store, m_image, m_doc->url().toLocalFile(), true, m_doc->isAutosaving()); if (!result) { qWarning() << "saving binary data failed"; } if (!m_store->finalize()) { return KisImageBuilder_RESULT_FAILURE; } if (!m_kraSaver->errorMessages().isEmpty()) { m_doc->setErrorMessage(m_kraSaver->errorMessages().join(".\n")); return KisImageBuilder_RESULT_FAILURE; } return KisImageBuilder_RESULT_OK; } bool KraConverter::saveRootDocuments(KoStore *store) { dbgFile << "Saving root"; if (store->open("root")) { KoStoreDevice dev(store); if (!saveToStream(&dev) || !store->close()) { dbgUI << "saveToStream failed"; return false; } } else { m_doc->setErrorMessage(i18n("Not able to write '%1'. Partition full?", QString("maindoc.xml"))); return false; } bool success = false; if (store->open("documentinfo.xml")) { QDomDocument doc = KisDocument::createDomDocument("document-info" /*DTD name*/, "document-info" /*tag name*/, "1.1"); doc = m_doc->documentInfo()->save(doc); KoStoreDevice dev(store); QByteArray s = doc.toByteArray(); // this is already Utf8! success = dev.write(s.data(), s.size()); store->close(); } if (store->open("preview.png")) { // ### TODO: missing error checking (The partition could be full!) savePreview(store); (void)store->close(); } dbgUI << "Saving done of url:" << m_doc->url().toLocalFile(); // Success return success; } bool KraConverter::saveToStream(QIODevice *dev) { QDomDocument doc = createDomDocument(); // Save to buffer QByteArray s = doc.toByteArray(); // utf8 already dev->open(QIODevice::WriteOnly); int nwritten = dev->write(s.data(), s.size()); if (nwritten != (int)s.size()) { warnUI << "wrote " << nwritten << "- expected" << s.size(); } return nwritten == (int)s.size(); } QDomDocument KraConverter::createDomDocument() { QDomDocument doc = m_doc->createDomDocument("DOC", CURRENT_DTD_VERSION); QDomElement root = doc.documentElement(); root.setAttribute("editor", "Krita"); root.setAttribute("syntaxVersion", "2"); root.setAttribute("kritaVersion", KritaVersionWrapper::versionString(false)); root.appendChild(m_kraSaver->saveXML(doc, m_image)); if (!m_kraSaver->errorMessages().isEmpty()) { m_doc->setErrorMessage(m_kraSaver->errorMessages().join(".\n")); } return doc; } bool KraConverter::savePreview(KoStore *store) { QPixmap pix = m_doc->generatePreview(QSize(256, 256)); QImage preview(pix.toImage().convertToFormat(QImage::Format_ARGB32, Qt::ColorOnly)); if (preview.size() == QSize(0,0)) { QSize newSize = m_doc->savingImage()->bounds().size(); newSize.scale(QSize(256, 256), Qt::KeepAspectRatio); preview = QImage(newSize, QImage::Format_ARGB32); preview.fill(QColor(0, 0, 0, 0)); } KoStoreDevice io(store); if (!io.open(QIODevice::WriteOnly)) { return false; } bool ret = preview.save(&io, "PNG"); io.close(); return ret; } bool KraConverter::oldLoadAndParse(KoStore *store, const QString &filename, KoXmlDocument &xmldoc) { //dbgUI <<"Trying to open" << filename; if (!store->open(filename)) { warnUI << "Entry " << filename << " not found!"; m_doc->setErrorMessage(i18n("Could not find %1", filename)); return false; } // Error variables for QDomDocument::setContent QString errorMsg; int errorLine, errorColumn; bool ok = xmldoc.setContent(store->device(), &errorMsg, &errorLine, &errorColumn); store->close(); if (!ok) { errUI << "Parsing error in " << filename << "! Aborting!" << endl << " In line: " << errorLine << ", column: " << errorColumn << endl << " Error message: " << errorMsg << endl; m_doc->setErrorMessage(i18n("Parsing error in %1 at line %2, column %3\nError message: %4" , filename , errorLine, errorColumn , QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0))); return false; } dbgUI << "File" << filename << " loaded and parsed"; return true; } bool KraConverter::loadXML(const KoXmlDocument &doc, KoStore *store) { Q_UNUSED(store); KoXmlElement root; KoXmlNode node; if (doc.doctype().name() != "DOC") { m_doc->setErrorMessage(i18n("The format is not supported or the file is corrupted")); return false; } root = doc.documentElement(); int syntaxVersion = root.attribute("syntaxVersion", "3").toInt(); if (syntaxVersion > 2) { m_doc->setErrorMessage(i18n("The file is too new for this version of Krita (%1).", syntaxVersion)); return false; } if (!root.hasChildNodes()) { m_doc->setErrorMessage(i18n("The file has no layers.")); return false; } m_kraLoader = new KisKraLoader(m_doc, syntaxVersion); // Legacy from the multi-image .kra file period. for (node = root.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isElement()) { if (node.nodeName() == "IMAGE") { KoXmlElement elem = node.toElement(); if (!(m_image = m_kraLoader->loadXML(elem))) { if (m_kraLoader->errorMessages().isEmpty()) { m_doc->setErrorMessage(i18n("Unknown error.")); } else { m_doc->setErrorMessage(m_kraLoader->errorMessages().join("\n")); } return false; } + + // HACK ALERT! + m_doc->hackPreliminarySetImage(m_image); + return true; } else { if (m_kraLoader->errorMessages().isEmpty()) { m_doc->setErrorMessage(i18n("The file does not contain an image.")); } return false; } } } return false; } bool KraConverter::completeLoading(KoStore* store) { if (!m_image) { if (m_kraLoader->errorMessages().isEmpty()) { m_doc->setErrorMessage(i18n("Unknown error.")); } else { m_doc->setErrorMessage(m_kraLoader->errorMessages().join("\n")); } return false; } m_image->blockUpdates(); m_kraLoader->loadBinaryData(store, m_image, m_doc->localFilePath(), true); m_image->unblockUpdates(); bool retval = true; if (!m_kraLoader->warningMessages().isEmpty()) { m_doc->setWarningMessage(m_kraLoader->warningMessages().join("\n")); retval = true; } if (retval) { m_activeNodes = m_kraLoader->selectedNodes(); m_assistants = m_kraLoader->assistants(); } return retval; } void KraConverter::cancel() { m_stop = true; } diff --git a/plugins/impex/kra/krita_kra.desktop b/plugins/impex/kra/krita_kra.desktop index 1b509e3619..f206e61b00 100644 --- a/plugins/impex/kra/krita_kra.desktop +++ b/plugins/impex/kra/krita_kra.desktop @@ -1,74 +1,75 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Comment= Exec=krita %u 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/libkra/kis_kra_load_visitor.cpp b/plugins/impex/libkra/kis_kra_load_visitor.cpp index dfb5fdeb12..3fa4f83809 100644 --- a/plugins/impex/libkra/kis_kra_load_visitor.cpp +++ b/plugins/impex/libkra/kis_kra_load_visitor.cpp @@ -1,707 +1,703 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2005 C. Boemann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_kra_load_visitor.h" #include "kis_kra_tags.h" #include "flake/kis_shape_layer.h" #include "flake/KisReferenceImagesLayer.h" #include "KisReferenceImage.h" #include #include #include #include #include #include #include // kritaimage #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_transform_mask_params_factory_registry.h" #include #include #include #include #include "kis_shape_selection.h" #include "kis_colorize_dom_utils.h" #include "kis_dom_utils.h" #include "kis_raster_keyframe_channel.h" #include "kis_paint_device_frames_interface.h" using namespace KRA; QString expandEncodedDirectory(const QString& _intern) { QString intern = _intern; QString result; int pos; while ((pos = intern.indexOf('/')) != -1) { if (QChar(intern.at(0)).isDigit()) result += "part"; result += intern.left(pos + 1); // copy numbers (or "pictures") + "/" intern = intern.mid(pos + 1); // remove the dir we just processed } if (!intern.isEmpty() && QChar(intern.at(0)).isDigit()) result += "part"; result += intern; return result; } KisKraLoadVisitor::KisKraLoadVisitor(KisImageSP image, KoStore *store, QMap &layerFilenames, QMap &keyframeFilenames, const QString & name, int syntaxVersion) : KisNodeVisitor(), m_layerFilenames(layerFilenames), m_keyframeFilenames(keyframeFilenames) { m_external = false; m_image = image; m_store = store; m_name = name; m_store->pushDirectory(); if (m_name.startsWith("/")) { m_name.remove(0, 1); } if (!m_store->enterDirectory(m_name)) { QStringList directories = m_store->directoryList(); dbgKrita << directories; if (directories.size() > 0) { dbgFile << "Could not locate the directory, maybe some encoding issue? Grab the first directory, that'll be the image one." << m_name << directories; m_name = directories.first(); } else { dbgFile << "Could not enter directory" << m_name << ", probably an old-style file with 'part' added."; m_name = expandEncodedDirectory(m_name); } } else { m_store->popDirectory(); } m_syntaxVersion = syntaxVersion; } void KisKraLoadVisitor::setExternalUri(const QString &uri) { m_external = true; m_uri = uri; } bool KisKraLoadVisitor::visit(KisExternalLayer * layer) { bool result = false; if (auto *referencesLayer = dynamic_cast(layer)) { Q_FOREACH(KoShape *shape, referencesLayer->shapes()) { auto *reference = dynamic_cast(shape); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(reference, false); reference->loadImage(m_store); } } else if (KisShapeLayer *shapeLayer = dynamic_cast(layer)) { if (!loadMetaData(layer)) { return false; } m_store->pushDirectory(); m_store->enterDirectory(getLocation(layer, DOT_SHAPE_LAYER)) ; result = shapeLayer->loadLayer(m_store); m_store->popDirectory(); } result = visitAll(layer) && result; return result; } bool KisKraLoadVisitor::visit(KisPaintLayer *layer) { loadNodeKeyframes(layer); dbgFile << "Visit: " << layer->name() << " colorSpace: " << layer->colorSpace()->id(); if (!loadPaintDevice(layer->paintDevice(), getLocation(layer))) { return false; } if (!loadProfile(layer->paintDevice(), getLocation(layer, DOT_ICC))) { return false; } if (!loadMetaData(layer)) { return false; } if (m_syntaxVersion == 1) { // Check whether there is a file with a .mask extension in the // layer directory, if so, it's an old-style transparency mask // that should be converted. QString location = getLocation(layer, ".mask"); if (m_store->open(location)) { KisSelectionSP selection = KisSelectionSP(new KisSelection()); KisPixelSelectionSP pixelSelection = selection->pixelSelection(); if (!pixelSelection->read(m_store->device())) { pixelSelection->disconnect(); } else { KisTransparencyMask* mask = new KisTransparencyMask(); mask->setSelection(selection); m_image->addNode(mask, layer, layer->firstChild()); } m_store->close(); } } bool result = visitAll(layer); return result; } bool KisKraLoadVisitor::visit(KisGroupLayer *layer) { if (*layer->colorSpace() != *m_image->colorSpace()) { layer->resetCache(m_image->colorSpace()); } if (!loadMetaData(layer)) { return false; } bool result = visitAll(layer); return result; } bool KisKraLoadVisitor::visit(KisAdjustmentLayer* layer) { loadNodeKeyframes(layer); // Adjustmentlayers are tricky: there's the 1.x style and the 2.x // style, which has selections with selection components bool result = true; if (m_syntaxVersion == 1) { KisSelectionSP selection = new KisSelection(); KisPixelSelectionSP pixelSelection = selection->pixelSelection(); result = loadPaintDevice(pixelSelection, getLocation(layer, ".selection")); layer->setInternalSelection(selection); } else if (m_syntaxVersion == 2) { result = loadSelection(getLocation(layer), layer->internalSelection()); } else { // We use the default, empty selection } if (!loadMetaData(layer)) { return false; } loadFilterConfiguration(layer->filter().data(), getLocation(layer, DOT_FILTERCONFIG)); result = visitAll(layer); return result; } -bool KisKraLoadVisitor::visit(KisGeneratorLayer* layer) +bool KisKraLoadVisitor::visit(KisGeneratorLayer *layer) { if (!loadMetaData(layer)) { return false; } bool result = true; loadNodeKeyframes(layer); - result = loadSelection(getLocation(layer), layer->internalSelection()); - result = loadFilterConfiguration(layer->filter().data(), getLocation(layer, DOT_FILTERCONFIG)); layer->update(); - result = visitAll(layer); return result; } bool KisKraLoadVisitor::visit(KisCloneLayer *layer) { if (!loadMetaData(layer)) { return false; } // the layer might have already been lazily initialized // from the mask loading code if (layer->copyFrom()) { return true; } KisNodeSP srcNode = layer->copyFromInfo().findNode(m_image->rootLayer()); KisLayerSP srcLayer = qobject_cast(srcNode.data()); Q_ASSERT(srcLayer); layer->setCopyFrom(srcLayer); // Clone layers have no data except for their masks bool result = visitAll(layer); return result; } void KisKraLoadVisitor::initSelectionForMask(KisMask *mask) { KisLayer *cloneLayer = dynamic_cast(mask->parent().data()); if (cloneLayer) { // the clone layers should be initialized out of order // and lazily, because their original() is still not // initialized cloneLayer->accept(*this); } KisLayer *parentLayer = qobject_cast(mask->parent().data()); // the KisKraLoader must have already set the parent for us Q_ASSERT(parentLayer); mask->initSelection(parentLayer); } bool KisKraLoadVisitor::visit(KisFilterMask *mask) { initSelectionForMask(mask); loadNodeKeyframes(mask); bool result = true; result = loadSelection(getLocation(mask), mask->selection()); result = loadFilterConfiguration(mask->filter().data(), getLocation(mask, DOT_FILTERCONFIG)); return result; } bool KisKraLoadVisitor::visit(KisTransformMask *mask) { QString location = getLocation(mask, DOT_TRANSFORMCONFIG); if (m_store->hasFile(location)) { QByteArray data; m_store->open(location); data = m_store->read(m_store->size()); m_store->close(); if (!data.isEmpty()) { QDomDocument doc; doc.setContent(data); QDomElement rootElement = doc.documentElement(); QDomElement main; if (!KisDomUtils::findOnlyElement(rootElement, "main", &main/*, &m_errorMessages*/)) { return false; } QString id = main.attribute("id", "not-valid"); if (id == "not-valid") { m_errorMessages << i18n("Could not load \"id\" of the transform mask"); return false; } QDomElement data; if (!KisDomUtils::findOnlyElement(rootElement, "data", &data, &m_errorMessages)) { return false; } KisTransformMaskParamsInterfaceSP params = KisTransformMaskParamsFactoryRegistry::instance()->createParams(id, data); if (!params) { m_errorMessages << i18n("Could not create transform mask params"); return false; } mask->setTransformParams(params); loadNodeKeyframes(mask); params->clearChangedFlag(); return true; } } return false; } bool KisKraLoadVisitor::visit(KisTransparencyMask *mask) { initSelectionForMask(mask); loadNodeKeyframes(mask); return loadSelection(getLocation(mask), mask->selection()); } bool KisKraLoadVisitor::visit(KisSelectionMask *mask) { initSelectionForMask(mask); return loadSelection(getLocation(mask), mask->selection()); } bool KisKraLoadVisitor::visit(KisColorizeMask *mask) { m_store->pushDirectory(); QString location = getLocation(mask, DOT_COLORIZE_MASK); m_store->enterDirectory(location) ; QByteArray data; if (!m_store->extractFile("content.xml", data)) return false; QDomDocument doc; if (!doc.setContent(data)) return false; QVector strokes; if (!KisDomUtils::loadValue(doc.documentElement(), COLORIZE_KEYSTROKES_SECTION, &strokes, mask->colorSpace())) return false; int i = 0; Q_FOREACH (const KisLazyFillTools::KeyStroke &stroke, strokes) { const QString fileName = QString("%1_%2").arg(COLORIZE_KEYSTROKE).arg(i++); loadPaintDevice(stroke.dev, fileName); } mask->setKeyStrokesDirect(QList::fromVector(strokes)); loadPaintDevice(mask->coloringProjection(), COLORIZE_COLORING_DEVICE); mask->resetCache(); m_store->popDirectory(); return true; } QStringList KisKraLoadVisitor::errorMessages() const { return m_errorMessages; } QStringList KisKraLoadVisitor::warningMessages() const { return m_warningMessages; } struct SimpleDevicePolicy { bool read(KisPaintDeviceSP dev, QIODevice *stream) { return dev->read(stream); } void setDefaultPixel(KisPaintDeviceSP dev, const KoColor &defaultPixel) const { return dev->setDefaultPixel(defaultPixel); } }; struct FramedDevicePolicy { FramedDevicePolicy(int frameId) : m_frameId(frameId) {} bool read(KisPaintDeviceSP dev, QIODevice *stream) { return dev->framesInterface()->readFrame(stream, m_frameId); } void setDefaultPixel(KisPaintDeviceSP dev, const KoColor &defaultPixel) const { return dev->framesInterface()->setFrameDefaultPixel(defaultPixel, m_frameId); } int m_frameId; }; bool KisKraLoadVisitor::loadPaintDevice(KisPaintDeviceSP device, const QString& location) { // Layer data KisPaintDeviceFramesInterface *frameInterface = device->framesInterface(); QList frames; if (frameInterface) { frames = device->framesInterface()->frames(); } if (!frameInterface || frames.count() <= 1) { return loadPaintDeviceFrame(device, location, SimpleDevicePolicy()); } else { KisRasterKeyframeChannel *keyframeChannel = device->keyframeChannel(); for (int i = 0; i < frames.count(); i++) { int id = frames[i]; if (keyframeChannel->frameFilename(id).isEmpty()) { m_warningMessages << i18n("Could not find keyframe pixel data for frame %1 in %2.").arg(id).arg(location); } else { Q_ASSERT(!keyframeChannel->frameFilename(id).isEmpty()); QString frameFilename = getLocation(keyframeChannel->frameFilename(id)); Q_ASSERT(!frameFilename.isEmpty()); if (!loadPaintDeviceFrame(device, frameFilename, FramedDevicePolicy(id))) { m_warningMessages << i18n("Could not load keyframe pixel data for frame %1 in %2.").arg(id).arg(location); } } } } return true; } template bool KisKraLoadVisitor::loadPaintDeviceFrame(KisPaintDeviceSP device, const QString &location, DevicePolicy policy) { if (m_store->open(location)) { if (!policy.read(device, m_store->device())) { m_warningMessages << i18n("Could not read pixel data: %1.", location); device->disconnect(); m_store->close(); return true; } m_store->close(); } else { m_warningMessages << i18n("Could not load pixel data: %1.", location); return true; } if (m_store->open(location + ".defaultpixel")) { int pixelSize = device->colorSpace()->pixelSize(); if (m_store->size() == pixelSize) { KoColor color(Qt::transparent, device->colorSpace()); m_store->read((char*)color.data(), pixelSize); policy.setDefaultPixel(device, color); } m_store->close(); } return true; } bool KisKraLoadVisitor::loadProfile(KisPaintDeviceSP device, const QString& location) { if (m_store->hasFile(location)) { m_store->open(location); QByteArray data; data.resize(m_store->size()); dbgFile << "Data to load: " << m_store->size() << " from " << location << " with color space " << device->colorSpace()->id(); int read = m_store->read(data.data(), m_store->size()); dbgFile << "Profile size: " << data.size() << " " << m_store->atEnd() << " " << m_store->device()->bytesAvailable() << " " << read; m_store->close(); // Create a colorspace with the embedded profile const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(device->colorSpace()->colorModelId().id(), device->colorSpace()->colorDepthId().id(), data); if (device->setProfile(profile)) { return true; } } m_warningMessages << i18n("Could not load profile: %1.", location); return true; } bool KisKraLoadVisitor::loadFilterConfiguration(KisFilterConfigurationSP kfc, const QString& location) { if (m_store->hasFile(location)) { QByteArray data; m_store->open(location); data = m_store->read(m_store->size()); m_store->close(); if (!data.isEmpty()) { QDomDocument doc; doc.setContent(data); QDomElement e = doc.documentElement(); if (e.tagName() == "filterconfig") { kfc->fromLegacyXML(e); } else { kfc->fromXML(e); } kfc = loadDeprecatedFilter(kfc); return true; } } m_warningMessages << i18n("Could not filter configuration %1.", location); return true; } bool KisKraLoadVisitor::loadMetaData(KisNode* node) { - dbgFile << "Load metadata for " << node->name(); KisLayer* layer = qobject_cast(node); if (!layer) return true; KisMetaData::IOBackend* backend = KisMetaData::IOBackendRegistry::instance()->get("xmp"); if (!backend || !backend->supportLoading()) { if (backend) dbgFile << "Backend " << backend->id() << " does not support loading."; else - dbgFile << "Could not load the XMP backenda t all"; + dbgFile << "Could not load the XMP backend at all"; return true; } QString location = getLocation(node, QString(".") + backend->id() + DOT_METADATA); dbgFile << "going to load " << backend->id() << ", " << backend->name() << " from " << location; if (m_store->hasFile(location)) { QByteArray data; m_store->open(location); data = m_store->read(m_store->size()); m_store->close(); QBuffer buffer(&data); if (!backend->loadFrom(layer->metaData(), &buffer)) { m_warningMessages << i18n("Could not load metadata for layer %1.", layer->name()); } } return true; } bool KisKraLoadVisitor::loadSelection(const QString& location, KisSelectionSP dstSelection) { // Pixel selection bool result = true; QString pixelSelectionLocation = location + DOT_PIXEL_SELECTION; if (m_store->hasFile(pixelSelectionLocation)) { KisPixelSelectionSP pixelSelection = dstSelection->pixelSelection(); result = loadPaintDevice(pixelSelection, pixelSelectionLocation); if (!result) { m_warningMessages << i18n("Could not load raster selection %1.", location); } pixelSelection->invalidateOutlineCache(); } // Shape selection QString shapeSelectionLocation = location + DOT_SHAPE_SELECTION; if (m_store->hasFile(shapeSelectionLocation + "/content.svg") || m_store->hasFile(shapeSelectionLocation + "/content.xml")) { m_store->pushDirectory(); m_store->enterDirectory(shapeSelectionLocation) ; KisShapeSelection* shapeSelection = new KisShapeSelection(m_image, dstSelection); dstSelection->setShapeSelection(shapeSelection); result = shapeSelection->loadSelection(m_store); m_store->popDirectory(); if (!result) { m_warningMessages << i18n("Could not load vector selection %1.", location); } } return true; } QString KisKraLoadVisitor::getLocation(KisNode* node, const QString& suffix) { return getLocation(m_layerFilenames[node], suffix); } QString KisKraLoadVisitor::getLocation(const QString &filename, const QString& suffix) { QString location = m_external ? QString() : m_uri; location += m_name + LAYER_PATH + filename + suffix; return location; } void KisKraLoadVisitor::loadNodeKeyframes(KisNode *node) { if (!m_keyframeFilenames.contains(node)) return; node->enableAnimation(); const QString &location = getLocation(m_keyframeFilenames[node]); if (!m_store->open(location)) { m_errorMessages << i18n("Could not load keyframes from %1.", location); return; } QString errorMsg; int errorLine; int errorColumn; QDomDocument dom; bool ok = dom.setContent(m_store->device(), &errorMsg, &errorLine, &errorColumn); m_store->close(); if (!ok) { m_errorMessages << i18n("parsing error in the keyframe file %1 at line %2, column %3\nError message: %4", location, errorLine, errorColumn, i18n(errorMsg.toUtf8())); return; } QDomElement root = dom.firstChildElement(); for (QDomElement child = root.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { if (child.nodeName().toUpper() == "CHANNEL") { QString id = child.attribute("name"); KisKeyframeChannel *channel = node->getKeyframeChannel(id, true); if (!channel) { m_warningMessages << i18n("unknown keyframe channel type: %1 in %2", id, location); continue; } channel->loadXML(child); } } } KisFilterConfigurationSP KisKraLoadVisitor::loadDeprecatedFilter(KisFilterConfigurationSP cfg) { if (cfg->getString("legacy") == "left edge detections") { cfg->setProperty("horizRadius", 1); cfg->setProperty("vertRadius", 1); cfg->setProperty("type", "prewitt"); cfg->setProperty("output", "yFall"); cfg->setProperty("lockAspect", true); cfg->setProperty("transparency", false); } else if (cfg->getString("legacy") == "right edge detections") { cfg->setProperty("horizRadius", 1); cfg->setProperty("vertRadius", 1); cfg->setProperty("type", "prewitt"); cfg->setProperty("output", "yGrowth"); cfg->setProperty("lockAspect", true); cfg->setProperty("transparency", false); } else if (cfg->getString("legacy") == "top edge detections") { cfg->setProperty("horizRadius", 1); cfg->setProperty("vertRadius", 1); cfg->setProperty("type", "prewitt"); cfg->setProperty("output", "xGrowth"); cfg->setProperty("lockAspect", true); cfg->setProperty("transparency", false); } else if (cfg->getString("legacy") == "bottom edge detections") { cfg->setProperty("horizRadius", 1); cfg->setProperty("vertRadius", 1); cfg->setProperty("type", "prewitt"); cfg->setProperty("output", "xFall"); cfg->setProperty("lockAspect", true); cfg->setProperty("transparency", false); } return cfg; } diff --git a/plugins/impex/libkra/tests/kis_kra_saver_test.cpp b/plugins/impex/libkra/tests/kis_kra_saver_test.cpp index 3fdc7c998e..0ab3645cab 100644 --- a/plugins/impex/libkra/tests/kis_kra_saver_test.cpp +++ b/plugins/impex/libkra/tests/kis_kra_saver_test.cpp @@ -1,549 +1,549 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_kra_saver_test.h" #include #include #include #include #include #include #include "filter/kis_filter_registry.h" #include "filter/kis_filter_configuration.h" #include "filter/kis_filter.h" #include "KisDocument.h" #include "kis_image.h" #include "kis_pixel_selection.h" #include "kis_group_layer.h" #include "kis_paint_layer.h" #include "kis_clone_layer.h" #include "kis_adjustment_layer.h" #include "kis_shape_layer.h" #include "kis_filter_mask.h" #include "kis_transparency_mask.h" #include "kis_selection_mask.h" #include "kis_selection.h" #include "kis_fill_painter.h" #include "kis_shape_selection.h" #include "util.h" #include "testutil.h" #include "kis_keyframe_channel.h" #include "kis_image_animation_interface.h" #include "kis_layer_properties_icons.h" #include "kis_transform_mask_params_interface.h" #include #include #include void KisKraSaverTest::initTestCase() { KoResourcePaths::addResourceDir("ko_patterns", QString(SYSTEM_RESOURCES_DATA_DIR) + "/patterns"); KisFilterRegistry::instance(); KisGeneratorRegistry::instance(); } void KisKraSaverTest::testCrashyShapeLayer() { /** * KisShapeLayer used to call setImage from its destructor and * therefore causing an infinite recursion (when at least one transparency * mask was preset. This testcase just checks that. */ //QScopedPointer doc(createCompleteDocument(true)); //Q_UNUSED(doc); } void KisKraSaverTest::testRoundTrip() { KisDocument* doc = createCompleteDocument(); KoColor bgColor(Qt::red, doc->image()->colorSpace()); doc->image()->setDefaultProjectionColor(bgColor); doc->exportDocumentSync(QUrl::fromLocalFile("roundtriptest.kra"), doc->mimeType()); QStringList list; KisCountVisitor cv1(list, KoProperties()); doc->image()->rootLayer()->accept(cv1); KisDocument *doc2 = KisPart::instance()->createDocument(); bool result = doc2->loadNativeFormat("roundtriptest.kra"); QVERIFY(result); KisCountVisitor cv2(list, KoProperties()); doc2->image()->rootLayer()->accept(cv2); QCOMPARE(cv1.count(), cv2.count()); // check whether the BG color is saved correctly QCOMPARE(doc2->image()->defaultProjectionColor(), bgColor); // test round trip of a transform mask KisNode* tnode = TestUtil::findNode(doc2->image()->rootLayer(), "testTransformMask").data(); QVERIFY(tnode); KisTransformMask *tmask = dynamic_cast(tnode); QVERIFY(tmask); KisDumbTransformMaskParams *params = dynamic_cast(tmask->transformParams().data()); QVERIFY(params); QTransform t = params->testingGetTransform(); QCOMPARE(t, createTestingTransform()); delete doc2; delete doc; } void KisKraSaverTest::testSaveEmpty() { KisDocument* doc = createEmptyDocument(); doc->exportDocumentSync(QUrl::fromLocalFile("emptytest.kra"), doc->mimeType()); QStringList list; KisCountVisitor cv1(list, KoProperties()); doc->image()->rootLayer()->accept(cv1); KisDocument *doc2 = KisPart::instance()->createDocument(); doc2->loadNativeFormat("emptytest.kra"); KisCountVisitor cv2(list, KoProperties()); doc2->image()->rootLayer()->accept(cv2); QCOMPARE(cv1.count(), cv2.count()); delete doc2; delete doc; } #include #include "generator/kis_generator_registry.h" #include void testRoundTripFillLayerImpl(const QString &testName, KisFilterConfigurationSP config) { - TestUtil::ExternalImageChecker chk(testName, "fill_layer"); + TestUtil::ReferenceImageChecker chk(testName, "fill_layer"); chk.setFuzzy(2); QScopedPointer doc(KisPart::instance()->createDocument()); // mask parent should be destructed before the document! QRect refRect(0,0,512,512); TestUtil::MaskParent p(refRect); doc->setCurrentImage(p.image); doc->documentInfo()->setAboutInfo("title", p.image->objectName()); KisSelectionSP selection; KisGeneratorLayerSP glayer = new KisGeneratorLayer(p.image, "glayer", config, selection); p.image->addNode(glayer, p.image->root(), KisNodeSP()); glayer->setDirty(); p.image->waitForDone(); chk.checkImage(p.image, "00_initial_layer_update"); doc->exportDocumentSync(QUrl::fromLocalFile("roundtrip_fill_layer_test.kra"), doc->mimeType()); QScopedPointer doc2(KisPart::instance()->createDocument()); doc2->loadNativeFormat("roundtrip_fill_layer_test.kra"); doc2->image()->waitForDone(); chk.checkImage(doc2->image(), "01_fill_layer_round_trip"); QVERIFY(chk.testPassed()); } void KisKraSaverTest::testRoundTripFillLayerColor() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisGeneratorSP generator = KisGeneratorRegistry::instance()->get("color"); Q_ASSERT(generator); // warning: we pass null paint device to the default constructed value KisFilterConfigurationSP config = generator->factoryConfiguration(); Q_ASSERT(config); QVariant v; v.setValue(KoColor(Qt::red, cs)); config->setProperty("color", v); testRoundTripFillLayerImpl("fill_layer_color", config); } void KisKraSaverTest::testRoundTripFillLayerPattern() { KisGeneratorSP generator = KisGeneratorRegistry::instance()->get("pattern"); QVERIFY(generator); // warning: we pass null paint device to the default constructed value KisFilterConfigurationSP config = generator->factoryConfiguration(); QVERIFY(config); QVariant v; v.setValue(QString("11_drawed_furry.png")); config->setProperty("pattern", v); testRoundTripFillLayerImpl("fill_layer_pattern", config); } #include "kis_psd_layer_style.h" void KisKraSaverTest::testRoundTripLayerStyles() { - TestUtil::ExternalImageChecker chk("kra_saver_test", "layer_styles"); + TestUtil::ReferenceImageChecker chk("kra_saver_test", "layer_styles"); QRect imageRect(0,0,512,512); // the document should be created before the image! QScopedPointer doc(KisPart::instance()->createDocument()); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisImageSP image = new KisImage(new KisSurrogateUndoStore(), imageRect.width(), imageRect.height(), cs, "test image"); KisPaintLayerSP layer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8); KisPaintLayerSP layer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8); KisPaintLayerSP layer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8); image->addNode(layer1); image->addNode(layer2); image->addNode(layer3); doc->setCurrentImage(image); doc->documentInfo()->setAboutInfo("title", image->objectName()); layer1->paintDevice()->fill(QRect(100, 100, 100, 100), KoColor(Qt::red, cs)); layer2->paintDevice()->fill(QRect(200, 200, 100, 100), KoColor(Qt::green, cs)); layer3->paintDevice()->fill(QRect(300, 300, 100, 100), KoColor(Qt::blue, cs)); KisPSDLayerStyleSP style(new KisPSDLayerStyle()); style->dropShadow()->setEffectEnabled(true); style->dropShadow()->setAngle(-90); style->dropShadow()->setUseGlobalLight(false); layer1->setLayerStyle(style->clone()); style->dropShadow()->setAngle(180); style->dropShadow()->setUseGlobalLight(true); layer2->setLayerStyle(style->clone()); style->dropShadow()->setAngle(90); style->dropShadow()->setUseGlobalLight(false); layer3->setLayerStyle(style->clone()); image->initialRefreshGraph(); chk.checkImage(image, "00_initial_layers"); doc->exportDocumentSync(QUrl::fromLocalFile("roundtrip_layer_styles.kra"), doc->mimeType()); QScopedPointer doc2(KisPart::instance()->createDocument()); doc2->loadNativeFormat("roundtrip_layer_styles.kra"); doc2->image()->waitForDone(); chk.checkImage(doc2->image(), "00_initial_layers"); QVERIFY(chk.testPassed()); } void KisKraSaverTest::testRoundTripAnimation() { QScopedPointer doc(KisPart::instance()->createDocument()); QRect imageRect(0,0,512,512); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisImageSP image = new KisImage(new KisSurrogateUndoStore(), imageRect.width(), imageRect.height(), cs, "test image"); KisPaintLayerSP layer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8); image->addNode(layer1); layer1->paintDevice()->fill(QRect(100, 100, 50, 50), KoColor(Qt::black, cs)); layer1->paintDevice()->setDefaultPixel(KoColor(Qt::red, cs)); KUndo2Command parentCommand; layer1->enableAnimation(); KisKeyframeChannel *rasterChannel = layer1->getKeyframeChannel(KisKeyframeChannel::Content.id(), true); QVERIFY(rasterChannel); rasterChannel->addKeyframe(10, &parentCommand); image->animationInterface()->switchCurrentTimeAsync(10); image->waitForDone(); layer1->paintDevice()->fill(QRect(200, 50, 10, 10), KoColor(Qt::black, cs)); layer1->paintDevice()->moveTo(25, 15); layer1->paintDevice()->setDefaultPixel(KoColor(Qt::green, cs)); rasterChannel->addKeyframe(20, &parentCommand); image->animationInterface()->switchCurrentTimeAsync(20); image->waitForDone(); layer1->paintDevice()->fill(QRect(150, 200, 30, 30), KoColor(Qt::black, cs)); layer1->paintDevice()->moveTo(100, 50); layer1->paintDevice()->setDefaultPixel(KoColor(Qt::blue, cs)); QVERIFY(!layer1->useInTimeline()); layer1->setUseInTimeline(true); doc->setCurrentImage(image); doc->exportDocumentSync(QUrl::fromLocalFile("roundtrip_animation.kra"), doc->mimeType()); QScopedPointer doc2(KisPart::instance()->createDocument()); doc2->loadNativeFormat("roundtrip_animation.kra"); KisImageSP image2 = doc2->image(); KisNodeSP node = image2->root()->firstChild(); QVERIFY(node->inherits("KisPaintLayer")); KisPaintLayerSP layer2 = qobject_cast(node.data()); cs = layer2->paintDevice()->colorSpace(); QCOMPARE(image2->animationInterface()->currentTime(), 20); KisKeyframeChannel *channel = layer2->getKeyframeChannel(KisKeyframeChannel::Content.id()); QVERIFY(channel); QCOMPARE(channel->keyframeCount(), 3); image2->animationInterface()->switchCurrentTimeAsync(0); image2->waitForDone(); QCOMPARE(layer2->paintDevice()->nonDefaultPixelArea(), QRect(64, 64, 128, 128)); QCOMPARE(layer2->paintDevice()->x(), 0); QCOMPARE(layer2->paintDevice()->y(), 0); QCOMPARE(layer2->paintDevice()->defaultPixel(), KoColor(Qt::red, cs)); image2->animationInterface()->switchCurrentTimeAsync(10); image2->waitForDone(); QCOMPARE(layer2->paintDevice()->nonDefaultPixelArea(), QRect(217, 15, 64, 64)); QCOMPARE(layer2->paintDevice()->x(), 25); QCOMPARE(layer2->paintDevice()->y(), 15); QCOMPARE(layer2->paintDevice()->defaultPixel(), KoColor(Qt::green, cs)); image2->animationInterface()->switchCurrentTimeAsync(20); image2->waitForDone(); QCOMPARE(layer2->paintDevice()->nonDefaultPixelArea(), QRect(228, 242, 64, 64)); QCOMPARE(layer2->paintDevice()->x(), 100); QCOMPARE(layer2->paintDevice()->y(), 50); QCOMPARE(layer2->paintDevice()->defaultPixel(), KoColor(Qt::blue, cs)); QVERIFY(layer2->useInTimeline()); } #include "lazybrush/kis_lazy_fill_tools.h" void KisKraSaverTest::testRoundTripColorizeMask() { QRect imageRect(0,0,512,512); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); const KoColorSpace * weirdCS = KoColorSpaceRegistry::instance()->rgb16(); QScopedPointer doc(KisPart::instance()->createDocument()); KisImageSP image = new KisImage(new KisSurrogateUndoStore(), imageRect.width(), imageRect.height(), cs, "test image"); doc->setCurrentImage(image); KisPaintLayerSP layer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, weirdCS); image->addNode(layer1); KisColorizeMaskSP mask = new KisColorizeMask(); image->addNode(mask, layer1); mask->initializeCompositeOp(); delete mask->setColorSpace(layer1->colorSpace()); { KisPaintDeviceSP key1 = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); key1->fill(QRect(50,50,10,20), KoColor(Qt::black, key1->colorSpace())); mask->testingAddKeyStroke(key1, KoColor(Qt::green, layer1->colorSpace())); // KIS_DUMP_DEVICE_2(key1, refRect, "key1", "dd"); } { KisPaintDeviceSP key2 = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); key2->fill(QRect(150,50,10,20), KoColor(Qt::black, key2->colorSpace())); mask->testingAddKeyStroke(key2, KoColor(Qt::red, layer1->colorSpace())); // KIS_DUMP_DEVICE_2(key2, refRect, "key2", "dd"); } { KisPaintDeviceSP key3 = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); key3->fill(QRect(0,0,10,10), KoColor(Qt::black, key3->colorSpace())); mask->testingAddKeyStroke(key3, KoColor(Qt::blue, layer1->colorSpace()), true); // KIS_DUMP_DEVICE_2(key3, refRect, "key3", "dd"); } KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, false, image); KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, false, image); doc->exportDocumentSync(QUrl::fromLocalFile("roundtrip_colorize.kra"), doc->mimeType()); QScopedPointer doc2(KisPart::instance()->createDocument()); doc2->loadNativeFormat("roundtrip_colorize.kra"); KisImageSP image2 = doc2->image(); KisNodeSP node = image2->root()->firstChild()->firstChild(); KisColorizeMaskSP mask2 = dynamic_cast(node.data()); QVERIFY(mask2); QCOMPARE(mask2->compositeOpId(), mask->compositeOpId()); QCOMPARE(mask2->colorSpace(), mask->colorSpace()); QCOMPARE(KisLayerPropertiesIcons::nodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, true).toBool(), false); QCOMPARE(KisLayerPropertiesIcons::nodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, true).toBool(), false); QList strokes = mask->fetchKeyStrokesDirect(); qDebug() << ppVar(strokes.size()); QCOMPARE(strokes[0].dev->exactBounds(), QRect(50,50,10,20)); QCOMPARE(strokes[0].isTransparent, false); QCOMPARE(strokes[0].color.colorSpace(), weirdCS); QCOMPARE(strokes[1].dev->exactBounds(), QRect(150,50,10,20)); QCOMPARE(strokes[1].isTransparent, false); QCOMPARE(strokes[1].color.colorSpace(), weirdCS); QCOMPARE(strokes[2].dev->exactBounds(), QRect(0,0,10,10)); QCOMPARE(strokes[2].isTransparent, true); QCOMPARE(strokes[2].color.colorSpace(), weirdCS); } #include "kis_shape_layer.h" #include #include void KisKraSaverTest::testRoundTripShapeLayer() { - TestUtil::ExternalImageChecker chk("kra_saver_test", "shape_layer"); + TestUtil::ReferenceImageChecker chk("kra_saver_test", "shape_layer"); QRect refRect(0,0,512,512); QScopedPointer doc(KisPart::instance()->createDocument()); TestUtil::MaskParent p(refRect); const qreal resolution = 144.0 / 72.0; p.image->setResolution(resolution, resolution); doc->setCurrentImage(p.image); doc->documentInfo()->setAboutInfo("title", p.image->objectName()); KoPathShape* path = new KoPathShape(); path->setShapeId(KoPathShapeId); path->moveTo(QPointF(10, 10)); path->lineTo(QPointF( 10, 110)); path->lineTo(QPointF(110, 110)); path->lineTo(QPointF(110, 10)); path->close(); path->normalize(); path->setBackground(toQShared(new KoColorBackground(Qt::red))); path->setName("my_precious_shape"); KisShapeLayerSP shapeLayer = new KisShapeLayer(doc->shapeController(), p.image, "shapeLayer1", 75); shapeLayer->addShape(path); p.image->addNode(shapeLayer); shapeLayer->setDirty(); qApp->processEvents(); p.image->waitForDone(); chk.checkImage(p.image, "00_initial_layer_update"); doc->exportDocumentSync(QUrl::fromLocalFile("roundtrip_shapelayer_test.kra"), doc->mimeType()); QScopedPointer doc2(KisPart::instance()->createDocument()); doc2->loadNativeFormat("roundtrip_shapelayer_test.kra"); qApp->processEvents(); doc2->image()->waitForDone(); QCOMPARE(doc2->image()->xRes(), resolution); QCOMPARE(doc2->image()->yRes(), resolution); chk.checkImage(doc2->image(), "01_shape_layer_round_trip"); QVERIFY(chk.testPassed()); } void KisKraSaverTest::testRoundTripShapeSelection() { - TestUtil::ExternalImageChecker chk("kra_saver_test", "shape_selection"); + TestUtil::ReferenceImageChecker chk("kra_saver_test", "shape_selection"); QRect refRect(0,0,512,512); QScopedPointer doc(KisPart::instance()->createDocument()); TestUtil::MaskParent p(refRect); const qreal resolution = 144.0 / 72.0; p.image->setResolution(resolution, resolution); doc->setCurrentImage(p.image); doc->documentInfo()->setAboutInfo("title", p.image->objectName()); p.layer->paintDevice()->setDefaultPixel(KoColor(Qt::green, p.layer->colorSpace())); KisSelectionSP selection = new KisSelection(p.layer->paintDevice()->defaultBounds()); KisShapeSelection *shapeSelection = new KisShapeSelection(p.image, selection); selection->setShapeSelection(shapeSelection); KoPathShape* path = new KoPathShape(); path->setShapeId(KoPathShapeId); path->moveTo(QPointF(10, 10)); path->lineTo(QPointF( 10, 110)); path->lineTo(QPointF(110, 110)); path->lineTo(QPointF(110, 10)); path->close(); path->normalize(); path->setBackground(toQShared(new KoColorBackground(Qt::red))); path->setName("my_precious_shape"); shapeSelection->addShape(path); KisTransparencyMaskSP tmask = new KisTransparencyMask(); tmask->setSelection(selection); p.image->addNode(tmask, p.layer); tmask->setDirty(p.image->bounds()); qApp->processEvents(); p.image->waitForDone(); chk.checkImage(p.image, "00_initial_shape_selection"); doc->exportDocumentSync(QUrl::fromLocalFile("roundtrip_shapeselection_test.kra"), doc->mimeType()); QScopedPointer doc2(KisPart::instance()->createDocument()); doc2->loadNativeFormat("roundtrip_shapeselection_test.kra"); qApp->processEvents(); doc2->image()->waitForDone(); QCOMPARE(doc2->image()->xRes(), resolution); QCOMPARE(doc2->image()->yRes(), resolution); chk.checkImage(doc2->image(), "00_initial_shape_selection"); KisNodeSP node = doc2->image()->root()->firstChild()->firstChild(); KisTransparencyMask *newMask = dynamic_cast(node.data()); QVERIFY(newMask); QVERIFY(newMask->selection()->hasShapeSelection()); QVERIFY(chk.testPassed()); } QTEST_MAIN(KisKraSaverTest) diff --git a/plugins/impex/ora/krita_ora.desktop b/plugins/impex/ora/krita_ora.desktop index 342e6956e3..6d235ad19f 100644 --- a/plugins/impex/ora/krita_ora.desktop +++ b/plugins/impex/ora/krita_ora.desktop @@ -1,74 +1,75 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Comment= Exec=krita %u 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 5de2e372a7..6362a0b6bc 100644 --- a/plugins/impex/pdf/krita_pdf.desktop +++ b/plugins/impex/pdf/krita_pdf.desktop @@ -1,73 +1,74 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Exec=krita %u 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 2647368c5e..69e3b3037d 100644 --- a/plugins/impex/png/krita_png.desktop +++ b/plugins/impex/png/krita_png.desktop @@ -1,70 +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 %u 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 953a128075..64fdea171a 100644 --- a/plugins/impex/ppm/krita_ppm.desktop +++ b/plugins/impex/ppm/krita_ppm.desktop @@ -1,74 +1,75 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Exec=krita %u 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 08c3c1eeb2..0f67842cc5 100644 --- a/plugins/impex/psd/krita_psd.desktop +++ b/plugins/impex/psd/krita_psd.desktop @@ -1,73 +1,74 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Exec=krita %u Icon=calligrakrita MimeType=image/x-psd;image/photoshop;image/x-photoshop;image/x-vnd.adobe.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/raw/krita_raw.desktop b/plugins/impex/raw/krita_raw.desktop index 43eecc5ff2..25ae68957c 100644 --- a/plugins/impex/raw/krita_raw.desktop +++ b/plugins/impex/raw/krita_raw.desktop @@ -1,70 +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 %u 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 530527f19f..82ac722acf 100644 --- a/plugins/impex/spriter/krita_spriter.desktop +++ b/plugins/impex/spriter/krita_spriter.desktop @@ -1,70 +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 %u 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 b2854eb8e1..188cdc1f38 100644 --- a/plugins/impex/svg/krita_svg.desktop +++ b/plugins/impex/svg/krita_svg.desktop @@ -1,70 +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 %u 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 97c53e27ff..8adf143a0f 100644 --- a/plugins/impex/tga/krita_tga.desktop +++ b/plugins/impex/tga/krita_tga.desktop @@ -1,70 +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 %u 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 7d130be79f..3a758b632e 100644 --- a/plugins/impex/tiff/krita_tiff.desktop +++ b/plugins/impex/tiff/krita_tiff.desktop @@ -1,73 +1,74 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Exec=krita %u 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 b4cf4ba6e6..8f2ab1fa43 100644 --- a/plugins/impex/xcf/krita_xcf.desktop +++ b/plugins/impex/xcf/krita_xcf.desktop @@ -1,73 +1,74 @@ [Desktop Entry] Categories=Qt;KDE;Office;Graphics; Exec=krita %u 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/paintops/hatching/CMakeLists.txt b/plugins/paintops/hatching/CMakeLists.txt index 417a3375a7..66aec6ddaa 100644 --- a/plugins/paintops/hatching/CMakeLists.txt +++ b/plugins/paintops/hatching/CMakeLists.txt @@ -1,22 +1,23 @@ set(kritahatchingpaintop_SOURCES hatching_paintop_plugin.cpp kis_hatching_paintop.cpp kis_hatching_options.cpp kis_hatching_preferences.cpp kis_hatching_paintop_settings.cpp kis_hatching_paintop_settings_widget.cpp + kis_hatching_pressure_angle_option.cpp kis_hatching_pressure_crosshatching_option.cpp kis_hatching_pressure_separation_option.cpp kis_hatching_pressure_thickness_option.cpp hatching_brush.cpp ) ki18n_wrap_ui(kritahatchingpaintop_SOURCES wdghatchingpreferences.ui wdghatchingoptions.ui) add_library(kritahatchingpaintop MODULE ${kritahatchingpaintop_SOURCES}) target_link_libraries(kritahatchingpaintop kritalibpaintop) install(TARGETS kritahatchingpaintop DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) install( FILES krita-hatching.png DESTINATION ${DATA_INSTALL_DIR}/krita/images) diff --git a/plugins/paintops/hatching/kis_hatching_paintop.cpp b/plugins/paintops/hatching/kis_hatching_paintop.cpp index 8d95873b32..96df6a45f9 100644 --- a/plugins/paintops/hatching/kis_hatching_paintop.cpp +++ b/plugins/paintops/hatching/kis_hatching_paintop.cpp @@ -1,210 +1,214 @@ /* * Copyright (c) 2008,2009 Lukáš Tvrdý * Copyright (c) 2010 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_hatching_paintop.h" #include "kis_hatching_paintop_settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisHatchingPaintOp::KisHatchingPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) : KisBrushBasedPaintOp(settings, painter) , m_image(image) { Q_UNUSED(node); m_settings = new KisHatchingPaintOpSettings(); static_cast(settings.data())->initializeTwin(m_settings); m_hatchingBrush = new HatchingBrush(m_settings); + m_angleOption.readOptionSetting(settings); m_crosshatchingOption.readOptionSetting(settings); m_separationOption.readOptionSetting(settings); m_thicknessOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); + m_angleOption.resetAllSensors(); m_crosshatchingOption.resetAllSensors(); m_separationOption.resetAllSensors(); m_thicknessOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); } KisHatchingPaintOp::~KisHatchingPaintOp() { delete m_hatchingBrush; } KisSpacingInformation KisHatchingPaintOp::paintAt(const KisPaintInformation& info) { //------START SIMPLE ERROR CATCHING------- if (!painter()->device()) return KisSpacingInformation(1.0); if (!m_hatchedDab) m_hatchedDab = source()->createCompositionSourceDevice(); else m_hatchedDab->clear(); //Simple convenience renaming, I'm thinking of removing these inherited quirks KisBrushSP brush = m_brush; KisPaintDeviceSP device = painter()->device(); //Macro to catch errors Q_ASSERT(brush); //----------SIMPLE error catching code, maybe it's not even needed------ if (!brush) return KisSpacingInformation(1.0); if (!brush->canPaintFor(info)) return KisSpacingInformation(1.0); //SENSOR-depending settings + m_settings->anglesensorvalue = m_angleOption.apply(info); m_settings->crosshatchingsensorvalue = m_crosshatchingOption.apply(info); m_settings->separationsensorvalue = m_separationOption.apply(info); m_settings->thicknesssensorvalue = m_thicknessOption.apply(info); const qreal additionalScale = KisLodTransform::lodToScale(painter()->device()); const double scale = additionalScale * m_sizeOption.apply(info); if ((scale * brush->width()) <= 0.01 || (scale * brush->height()) <= 0.01) return KisSpacingInformation(1.0); KisDabShape shape(scale, 1.0, 0.0); quint8 origOpacity = m_opacityOption.apply(painter(), info); /*----Fetch the Dab----*/ static const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); static KoColor color(Qt::black, cs); QRect dstRect; KisFixedPaintDeviceSP maskDab = m_dabCache->fetchDab(cs, color, info.pos(), shape, info, 1.0, &dstRect); // sanity check KIS_ASSERT_RECOVER_NOOP(dstRect.size() == maskDab->bounds().size()); /*-----Convenient renaming for the limits of the maskDab, this will be used to hatch a dab of just the right size------*/ qint32 x, y, sw, sh; dstRect.getRect(&x, &y, &sw, &sh); //------This If_block pre-fills the future m_hatchedDab with a pretty backgroundColor if (m_settings->opaquebackground) { KoColor aersh = painter()->backgroundColor(); m_hatchedDab->fill(0, 0, (sw - 1), (sh - 1), aersh.data()); //this plus yellow background = french fry brush } - // Trick for moire pattern to look better - bool donotbasehatch = false; - /* If block describing how to stack hatching passes to generate crosshatching according to user specifications */ if (m_settings->enabledcurvecrosshatching) { if (m_settings->perpendicular) { if (m_settings->crosshatchingsensorvalue > 0.5) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(90), painter()->paintColor(), additionalScale); } else if (m_settings->minusthenplus) { if (m_settings->crosshatchingsensorvalue > 0.33) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); if (m_settings->crosshatchingsensorvalue > 0.67) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); } else if (m_settings->plusthenminus) { if (m_settings->crosshatchingsensorvalue > 0.33) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); if (m_settings->crosshatchingsensorvalue > 0.67) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); } else if (m_settings->moirepattern) { - m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle((m_settings->crosshatchingsensorvalue) * 180), painter()->paintColor(), additionalScale); - donotbasehatch = true; + m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle((m_settings->crosshatchingsensorvalue) * 360), painter()->paintColor(), additionalScale); } } else { if (m_settings->perpendicular) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(90), painter()->paintColor(), additionalScale); } else if (m_settings->minusthenplus) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); } else if (m_settings->plusthenminus) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(45), painter()->paintColor(), additionalScale); m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-45), painter()->paintColor(), additionalScale); } else if (m_settings->moirepattern) { m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle(-10), painter()->paintColor(), additionalScale); } } - if (!donotbasehatch) + if (m_settings->enabledcurveangle) + m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, spinAngle((m_settings->anglesensorvalue)*360+m_settings->angle), painter()->paintColor(), additionalScale); + + // The base hatch... unless moiré or angle + if (!m_settings->moirepattern && !m_settings->enabledcurveangle) m_hatchingBrush->hatch(m_hatchedDab, x, y, sw, sh, m_settings->angle, painter()->paintColor(), additionalScale); + // The most important line, the one that paints to the screen. painter()->bitBltWithFixedSelection(x, y, m_hatchedDab, maskDab, sw, sh); painter()->renderMirrorMaskSafe(QRect(QPoint(x, y), QSize(sw, sh)), m_hatchedDab, 0, 0, maskDab, !m_dabCache->needSeparateOriginal()); painter()->setOpacity(origOpacity); return effectiveSpacing(scale); } KisSpacingInformation KisHatchingPaintOp::updateSpacingImpl(const KisPaintInformation &info) const { const qreal scale = KisLodTransform::lodToScale(painter()->device()) * m_sizeOption.apply(info); return effectiveSpacing(scale); } double KisHatchingPaintOp::spinAngle(double spin) { double tempangle = m_settings->angle + spin; qint8 factor = 1; if (tempangle < 0) factor = -1; tempangle = fabs(fmod(tempangle, 180)); if ((tempangle >= 0) && (tempangle <= 90)) return factor * tempangle; else if ((tempangle > 90) && (tempangle <= 180)) return factor * -(180 - tempangle); return 0; // this should never be executed except if NAN } diff --git a/plugins/paintops/hatching/kis_hatching_paintop.h b/plugins/paintops/hatching/kis_hatching_paintop.h index 4a513116b5..ef5fd28016 100644 --- a/plugins/paintops/hatching/kis_hatching_paintop.h +++ b/plugins/paintops/hatching/kis_hatching_paintop.h @@ -1,107 +1,114 @@ /* * Copyright (c) 2008 Boudewijn Rempt * Copyright (c) 2008, 2009 Lukáš Tvrdý * Copyright (c) 2010 José Luis Vergara Toloza * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_HATCHING_PAINTOP_H_ #define KIS_HATCHING_PAINTOP_H_ #include #include #include #include "hatching_brush.h" #include "kis_hatching_paintop_settings.h" +#include #include #include #include #include #include class KisPainter; class KisHatchingPaintOp : public KisBrushBasedPaintOp { public: KisHatchingPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image); ~KisHatchingPaintOp() override; /** * Returns a number between -90 and 90, and corresponds to the * angle that results from adding angle 'spin' to 'm_settings->angle', * corrected to coincide with the way the GUI operates. */ double spinAngle(double spin); protected: /** * Paint a hatched dab around the mouse cursor according to * sensor settings and user preferences. */ KisSpacingInformation paintAt(const KisPaintInformation& info) override; KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override; private: KisHatchingPaintOpSettingsSP m_settings; KisImageWSP m_image; HatchingBrush *m_hatchingBrush; /** * PaintDevice that will be filled with a single pass of * hatching by HatchingBrush::hatch */ KisPaintDeviceSP m_hatchedDab; + /** + * Curve to control the hatching angle + * according to user preferences set in the GUI + */ + KisHatchingPressureAngleOption m_angleOption; + /** * Curve to control the intensity of crosshatching * according to user preferences set in the GUI */ KisHatchingPressureCrosshatchingOption m_crosshatchingOption; /** * Curve to control the dynamics of separation with * device input */ KisHatchingPressureSeparationOption m_separationOption; /** * Curve to control the thickness of the hatching lines * with device input */ KisHatchingPressureThicknessOption m_thicknessOption; /** * Curve to control the opacity of the entire dab * with device input */ KisPressureOpacityOption m_opacityOption; /** * Curve to control the size of the entire dab * with device input */ KisPressureSizeOption m_sizeOption; }; #endif // KIS_HATCHING_PAINTOP_H_ diff --git a/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp b/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp index 95081b8325..362ae1acb9 100644 --- a/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp +++ b/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp @@ -1,219 +1,220 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2010 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_hatching_paintop_settings.h" #include #include #include const QString HATCHING_VERSION = "Hatching/Version"; struct KisHatchingPaintOpSettings::Private { QList uniformProperties; }; KisHatchingPaintOpSettings::KisHatchingPaintOpSettings() : m_d(new Private) { setProperty(HATCHING_VERSION, "2"); } KisHatchingPaintOpSettings::~KisHatchingPaintOpSettings() { } void KisHatchingPaintOpSettings::initializeTwin(KisPaintOpSettingsSP settings) const { // XXX: this is a nice way to reinvent the copy constructor? /*--------DO NOT REMOVE please, use this to review the XML config tree QMap rofl = QMap(getProperties()); QMap::const_iterator i; for (i = rofl.constBegin(); i != rofl.constEnd(); ++i) dbgKrita << i.key() << ":" << i.value(); /----------DO NOT REMOVE----------------*/ KisHatchingPaintOpSettings *convenienttwin = static_cast(settings.data()); + convenienttwin->enabledcurveangle = getBool("PressureAngle"); convenienttwin->enabledcurvecrosshatching = getBool("PressureCrosshatching"); convenienttwin->enabledcurveopacity = getBool("PressureOpacity"); convenienttwin->enabledcurveseparation = getBool("PressureSeparation"); convenienttwin->enabledcurvesize = getBool("PressureSize"); convenienttwin->enabledcurvethickness = getBool("PressureThickness"); convenienttwin->angle = getDouble("Hatching/angle"); convenienttwin->separation = getDouble("Hatching/separation"); convenienttwin->thickness = getDouble("Hatching/thickness"); convenienttwin->origin_x = getDouble("Hatching/origin_x"); convenienttwin->origin_y = getDouble("Hatching/origin_y"); convenienttwin->nocrosshatching = getBool("Hatching/bool_nocrosshatching"); convenienttwin->perpendicular = getBool("Hatching/bool_perpendicular"); convenienttwin->minusthenplus = getBool("Hatching/bool_minusthenplus"); convenienttwin->plusthenminus = getBool("Hatching/bool_plusthenminus"); convenienttwin->moirepattern = getBool("Hatching/bool_moirepattern"); convenienttwin->separationintervals = getInt("Hatching/separationintervals"); //convenienttwin->trigonometryalgebra = getBool("Hatching/bool_trigonometryalgebra"); //convenienttwin->scratchoff = getBool("Hatching/bool_scratchoff"); convenienttwin->antialias = getBool("Hatching/bool_antialias"); convenienttwin->opaquebackground = getBool("Hatching/bool_opaquebackground"); convenienttwin->subpixelprecision = getBool("Hatching/bool_subpixelprecision"); if (getBool("Hatching/bool_nocrosshatching")) convenienttwin->crosshatchingstyle = 0; else if (getBool("Hatching/bool_perpendicular")) convenienttwin->crosshatchingstyle = 1; else if (getBool("Hatching/bool_minusthenplus")) convenienttwin->crosshatchingstyle = 2; else if (getBool("Hatching/bool_plusthenminus")) convenienttwin->crosshatchingstyle = 3; if (getBool("Hatching/bool_moirepattern")) convenienttwin->crosshatchingstyle = 4; } void KisHatchingPaintOpSettings::fromXML(const QDomElement& elt) { setProperty(HATCHING_VERSION, "1"); // This make sure that fromXML will override HAIRY_VERSION with 2, or will default to 1 KisBrushBasedPaintOpSettings::fromXML(elt); QVariant v; if (!getProperty(HATCHING_VERSION, v) || v == "1") { setProperty("Hatching/thickness", 2.0 * getDouble("Hatching/thickness")); } setProperty(HATCHING_VERSION, "2"); // make sure it's saved as version 2 next time } #include #include "kis_paintop_preset.h" #include "kis_paintop_settings_update_proxy.h" #include "kis_hatching_options.h" QList KisHatchingPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(m_d->uniformProperties); if (props.isEmpty()) { { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "hatching_angle", i18n("Hatching Angle"), settings, 0); const QString degree = QChar(Qt::Key_degree); prop->setRange(-90, 90); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setSuffix(degree); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { HatchingOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.angle); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { HatchingOption option; option.readOptionSetting(prop->settings().data()); option.angle = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "hatching_separation", i18n("Separation"), settings, 0); prop->setRange(1.0, 30); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setSuffix(i18n(" px")); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { HatchingOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.separation); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { HatchingOption option; option.readOptionSetting(prop->settings().data()); option.separation = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } { KisDoubleSliderBasedPaintOpPropertyCallback *prop = new KisDoubleSliderBasedPaintOpPropertyCallback( KisDoubleSliderBasedPaintOpPropertyCallback::Double, "hatching_thickness", i18n("Thickness"), settings, 0); prop->setRange(1.0, 30); prop->setSingleStep(0.01); prop->setDecimals(2); prop->setSuffix(i18n(" px")); prop->setReadCallback( [](KisUniformPaintOpProperty *prop) { HatchingOption option; option.readOptionSetting(prop->settings().data()); prop->setValue(option.thickness); }); prop->setWriteCallback( [](KisUniformPaintOpProperty *prop) { HatchingOption option; option.readOptionSetting(prop->settings().data()); option.thickness = prop->value().toReal(); option.writeOptionSetting(prop->settings().data()); }); QObject::connect(preset()->updateProxy(), SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue())); prop->requestReadValue(); props << toQShared(prop); } } return KisPaintOpSettings::uniformProperties(settings) + props; } diff --git a/plugins/paintops/hatching/kis_hatching_paintop_settings.h b/plugins/paintops/hatching/kis_hatching_paintop_settings.h index b154931b90..089b3ed3bc 100644 --- a/plugins/paintops/hatching/kis_hatching_paintop_settings.h +++ b/plugins/paintops/hatching/kis_hatching_paintop_settings.h @@ -1,87 +1,89 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2010 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_HATCHING_PAINTOP_SETTINGS_H_ #define KIS_HATCHING_PAINTOP_SETTINGS_H_ #include #include #include "kis_hatching_paintop_settings_widget.h" #include class KisHatchingPaintOpSettings : public KisBrushBasedPaintOpSettings { public: KisHatchingPaintOpSettings(); ~KisHatchingPaintOpSettings() override; //Dialogs enabled + bool enabledcurveangle; bool enabledcurvecrosshatching; bool enabledcurveopacity; bool enabledcurveseparation; bool enabledcurvesize; bool enabledcurvethickness; //Hatching Options double angle; double separation; double thickness; double origin_x; double origin_y; bool nocrosshatching; bool perpendicular; bool minusthenplus; bool plusthenminus; bool moirepattern; int crosshatchingstyle; int separationintervals; //Hatching Preferences //bool trigonometryalgebra; //bool scratchoff; bool antialias; bool subpixelprecision; bool opaquebackground; - //Crosshatching, Separation and Thickness curves + //Angle, Crosshatching, Separation and Thickness curves + double anglesensorvalue; double crosshatchingsensorvalue; double separationsensorvalue; double thicknesssensorvalue; void initializeTwin(KisPaintOpSettingsSP convenienttwin) const; using KisPropertiesConfiguration::fromXML; void fromXML(const QDomElement&) override; QList uniformProperties(KisPaintOpSettingsSP settings) override; private: Q_DISABLE_COPY(KisHatchingPaintOpSettings) struct Private; const QScopedPointer m_d; }; typedef KisSharedPtr KisHatchingPaintOpSettingsSP; #endif diff --git a/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp b/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp index c5f51457f0..85119605e4 100644 --- a/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp +++ b/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp @@ -1,137 +1,139 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2010 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_hatching_paintop_settings_widget.h" #include "kis_hatching_options.h" #include "kis_hatching_preferences.h" #include "kis_hatching_paintop_settings.h" +#include "kis_hatching_pressure_angle_option.h" #include "kis_hatching_pressure_crosshatching_option.h" #include "kis_hatching_pressure_separation_option.h" #include "kis_hatching_pressure_thickness_option.h" #include #include #include #include #include #include #include #include "kis_texture_option.h" #include "kis_curve_option_widget.h" #include #include "kis_pressure_texture_strength_option.h" #include #include KisHatchingPaintOpSettingsWidget:: KisHatchingPaintOpSettingsWidget(QWidget* parent) : KisBrushBasedPaintopOptionWidget(parent) { setPrecisionEnabled(true); //-------Adding widgets to the screen------------ addPaintOpOption(new KisHatchingOptions(), i18n("Hatching options")); addPaintOpOption(new KisHatchingPreferences(), i18n("Hatching preferences")); addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode")); addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureSeparationOption(), i18n("0.0"), i18n("1.0")), i18n("Separation")); addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureThicknessOption(), i18n("0.0"), i18n("1.0")), i18n("Thickness")); + addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureAngleOption(), i18n("0.0"), i18n("1.0")), i18n("Angle")); addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureCrosshatchingOption(), i18n("0.0"), i18n("1.0")), i18n("Crosshatching")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size")); addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror")); addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode")); addPaintOpOption(new KisTextureOption(), i18n("Pattern")); addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength")); //-----Useful to read first:------ /* Below you will encounter a reasonably correct solution to the problem of changing the default presets of the "BrushTip" popup configuration dialogue. In my (Pentalis) opinion, the best solution is code refactoring (simpler ways to change the defaults). On the meanwhile, copypasting this code won't give your class a charisma penalty. In kis_hatching_paintop_settings.cpp you will find a snippet of code to discover the structure of your XML config tree if you need to edit it at build time like here. */ //---------START ALTERING DEFAULT VALUES----------- //As the name implies, reconfigurationCourier is the KisPropertiesConfigurationSP //we'll use as an intermediary to edit the default settings KisPropertiesConfigurationSP reconfigurationCourier = configuration(); /*xMLAnalyzer is an empty document we'll use to analyze and edit the config string part by part I know the important string is "brush_definition" because I read the tree with the snippet in kis_hatching_paintop_settings.cpp */ QDomDocument xMLAnalyzer; xMLAnalyzer.setContent(reconfigurationCourier->getString("brush_definition")); /*More things I know by reading the XML tree. At this point you can just read it with: dbgKrita << xMLAnalyzer.toString() ; those QDomElements are the way to navigate the XML tree, read http://doc.qt.nokia.com/latest/qdomdocument.html for more information */ QDomElement firstTag = xMLAnalyzer.documentElement(); QDomElement firstTagsChild = firstTag.elementsByTagName("MaskGenerator").item(0).toElement(); // SET THE DEFAULT VALUES firstTag.attributeNode("spacing").setValue("0.4"); firstTagsChild.attributeNode("diameter").setValue("30"); //Write them into the intermediary config file reconfigurationCourier->setProperty("brush_definition", xMLAnalyzer.toString()); KisCubicCurve CurveSize; CurveSize.fromString("0,1;1,0.1;"); //dbgKrita << "\n\n\n" << CurveSize.toString() << "\n\n\n"; QVariant QVCurveSize = QVariant::fromValue(CurveSize); reconfigurationCourier->setProperty("CurveSize", QVCurveSize); setConfiguration(reconfigurationCourier); // Finished. /* Debugging block QMap rofl = QMap(reconfigurationCourier->getProperties()); QMap::const_iterator i; for (i = rofl.constBegin(); i != rofl.constEnd(); ++i) dbgKrita << i.key() << ":" << i.value(); */ } KisHatchingPaintOpSettingsWidget::~ KisHatchingPaintOpSettingsWidget() { } KisPropertiesConfigurationSP KisHatchingPaintOpSettingsWidget::configuration() const { KisHatchingPaintOpSettingsSP config = new KisHatchingPaintOpSettings(); config->setOptionsWidget(const_cast(this)); config->setProperty("paintop", "hatchingbrush"); // XXX: make this a const id string writeConfiguration(config); return config; } diff --git a/libs/image/tests/kis_algebra_2d_test.h b/plugins/paintops/hatching/kis_hatching_pressure_angle_option.cpp similarity index 55% copy from libs/image/tests/kis_algebra_2d_test.h copy to plugins/paintops/hatching/kis_hatching_pressure_angle_option.cpp index 6edb0ff330..9fd38b6b13 100644 --- a/libs/image/tests/kis_algebra_2d_test.h +++ b/plugins/paintops/hatching/kis_hatching_pressure_angle_option.cpp @@ -1,39 +1,36 @@ -/* - * Copyright (c) 2016 Dmitry Kazakov +/* By Idiomdrottning 2018, after a file that + * was Copyright (c) 2010 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef __KIS_ALGEBRA_2D_TEST_H -#define __KIS_ALGEBRA_2D_TEST_H +#include "kis_hatching_pressure_angle_option.h" +#include -#include +#include +#include +#include -class KisAlgebra2DTest : public QObject -{ - Q_OBJECT -private Q_SLOTS: - void testHalfPlane(); - void testOuterCircle(); - - void testQuadraticEquation(); - void testIntersections(); - void testWeirdIntersections(); - void testMatrixDecomposition1(); - void testMatrixDecomposition2(); -}; +KisHatchingPressureAngleOption::KisHatchingPressureAngleOption() + : KisCurveOption("Angle", KisPaintOpOption::GENERAL, false) +{ +} -#endif /* __KIS_ALGEBRA_2D_TEST_H */ +double KisHatchingPressureAngleOption::apply(const KisPaintInformation & info) const +{ + if (!isChecked()) return 0.5; + return computeSizeLikeValue(info); +} diff --git a/plugins/paintops/hatching/kis_hatching_pressure_angle_option.h b/plugins/paintops/hatching/kis_hatching_pressure_angle_option.h new file mode 100644 index 0000000000..1e07db3dd4 --- /dev/null +++ b/plugins/paintops/hatching/kis_hatching_pressure_angle_option.h @@ -0,0 +1,37 @@ +/* By Idiomdrottning 2018, after a file that + * was Copyright (c) 2010 José Luis Vergara +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef KIS_HATCHING_PRESSURE_ANGLE_OPTION_H +#define KIS_HATCHING_PRESSURE_ANGLE_OPTION_H + +#include "kis_curve_option.h" +#include + +/** + * The pressure angle option defines a curve that is used to + * calculate the effect of pressure (or other parameters) on + * angle in the hatching brush + */ +class KisHatchingPressureAngleOption : public KisCurveOption +{ +public: + KisHatchingPressureAngleOption(); + double apply(const KisPaintInformation & info) const; +}; + +#endif diff --git a/plugins/paintops/libpaintop/kis_curve_option.cpp b/plugins/paintops/libpaintop/kis_curve_option.cpp index de28f62d07..2abd1c1b31 100644 --- a/plugins/paintops/libpaintop/kis_curve_option.cpp +++ b/plugins/paintops/libpaintop/kis_curve_option.cpp @@ -1,434 +1,434 @@ /* This file is part of the KDE project * Copyright (C) 2008 Boudewijn Rempt * Copyright (C) 2011 Silvio Heinrich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_curve_option.h" #include KisCurveOption::KisCurveOption(const QString& name, KisPaintOpOption::PaintopCategory category, bool checked, qreal value, qreal min, qreal max) : m_name(name) , m_category(category) , m_checkable(true) , m_checked(checked) , m_useCurve(true) , m_useSameCurve(true) , m_separateCurveValue(false) , m_curveMode(0) { Q_FOREACH (const DynamicSensorType sensorType, KisDynamicSensor::sensorsTypes()) { KisDynamicSensorSP sensor = KisDynamicSensor::type2Sensor(sensorType, m_name); sensor->setActive(false); replaceSensor(sensor); } m_sensorMap[PRESSURE]->setActive(true); setValueRange(min, max); setValue(value); } KisCurveOption::~KisCurveOption() { } const QString& KisCurveOption::name() const { return m_name; } KisPaintOpOption::PaintopCategory KisCurveOption::category() const { return m_category; } qreal KisCurveOption::minValue() const { return m_minValue; } qreal KisCurveOption::maxValue() const { return m_maxValue; } qreal KisCurveOption::value() const { return m_value; } void KisCurveOption::resetAllSensors() { Q_FOREACH (KisDynamicSensorSP sensor, m_sensorMap.values()) { if (sensor->isActive()) { sensor->reset(); } } } void KisCurveOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { if (m_checkable) { setting->setProperty("Pressure" + m_name, isChecked()); } if (activeSensors().size() == 1) { setting->setProperty(m_name + "Sensor", activeSensors().first()->toXML()); } else { QDomDocument doc = QDomDocument("params"); QDomElement root = doc.createElement("params"); doc.appendChild(root); root.setAttribute("id", "sensorslist"); Q_FOREACH (KisDynamicSensorSP sensor, activeSensors()) { QDomElement childelt = doc.createElement("ChildSensor"); sensor->toXML(doc, childelt); root.appendChild(childelt); } setting->setProperty(m_name + "Sensor", doc.toString()); } setting->setProperty(m_name + "UseCurve", m_useCurve); setting->setProperty(m_name + "UseSameCurve", m_useSameCurve); setting->setProperty(m_name + "Value", m_value); setting->setProperty(m_name + "curveMode", m_curveMode); } void KisCurveOption::readOptionSetting(KisPropertiesConfigurationSP setting) { m_curveCache.clear(); readNamedOptionSetting(m_name, setting); } void KisCurveOption::lodLimitations(KisPaintopLodLimitations *l) const { Q_UNUSED(l); } void KisCurveOption::readNamedOptionSetting(const QString& prefix, const KisPropertiesConfigurationSP setting) { if (!setting) return; //dbgKrita << "readNamedOptionSetting" << prefix; - setting->dump(); + // setting->dump(); if (m_checkable) { setChecked(setting->getBool("Pressure" + prefix, false)); } //dbgKrita << "\tPressure" + prefix << isChecked(); m_sensorMap.clear(); // Replace all sensors with the inactive defaults Q_FOREACH (const DynamicSensorType sensorType, KisDynamicSensor::sensorsTypes()) { replaceSensor(KisDynamicSensor::type2Sensor(sensorType, m_name)); } QString sensorDefinition = setting->getString(prefix + "Sensor"); if (!sensorDefinition.contains("sensorslist")) { KisDynamicSensorSP s = KisDynamicSensor::createFromXML(sensorDefinition, m_name); if (s) { replaceSensor(s); s->setActive(true); //dbgKrita << "\tsingle sensor" << s::id(s->sensorType()) << s->isActive() << "added"; } } else { QDomDocument doc; doc.setContent(sensorDefinition); QDomElement elt = doc.documentElement(); QDomNode node = elt.firstChild(); while (!node.isNull()) { if (node.isElement()) { QDomElement childelt = node.toElement(); if (childelt.tagName() == "ChildSensor") { KisDynamicSensorSP s = KisDynamicSensor::createFromXML(childelt, m_name); if (s) { replaceSensor(s); s->setActive(true); //dbgKrita << "\tchild sensor" << s::id(s->sensorType()) << s->isActive() << "added"; } } } node = node.nextSibling(); } } // Only load the old curve format if the curve wasn't saved by the sensor // This will give every sensor the same curve. //dbgKrita << ">>>>>>>>>>>" << prefix + "Sensor" << setting->getString(prefix + "Sensor"); if (!setting->getString(prefix + "Sensor").contains("curve")) { //dbgKrita << "\told format"; if (setting->getBool("Custom" + prefix, false)) { Q_FOREACH (KisDynamicSensorSP s, m_sensorMap.values()) { s->setCurve(setting->getCubicCurve("Curve" + prefix)); } } } // At least one sensor needs to be active if (activeSensors().size() == 0) { m_sensorMap[PRESSURE]->setActive(true); } m_value = setting->getDouble(m_name + "Value", m_maxValue); //dbgKrita << "\t" + m_name + "Value" << m_value; m_useCurve = setting->getBool(m_name + "UseCurve", true); //dbgKrita << "\t" + m_name + "UseCurve" << m_useSameCurve; m_useSameCurve = setting->getBool(m_name + "UseSameCurve", true); //dbgKrita << "\t" + m_name + "UseSameCurve" << m_useSameCurve; m_curveMode = setting->getInt(m_name + "curveMode"); //dbgKrita << "-----------------"; } void KisCurveOption::replaceSensor(KisDynamicSensorSP s) { Q_ASSERT(s); m_sensorMap[s->sensorType()] = s; } KisDynamicSensorSP KisCurveOption::sensor(DynamicSensorType sensorType, bool active) const { if (m_sensorMap.contains(sensorType)) { if (!active) { return m_sensorMap[sensorType]; } else { if (m_sensorMap[sensorType]->isActive()) { return m_sensorMap[sensorType]; } } } return 0; } bool KisCurveOption::isRandom() const { return bool(sensor(FUZZY_PER_DAB, true)) || bool(sensor(FUZZY_PER_STROKE, true)); } bool KisCurveOption::isCurveUsed() const { return m_useCurve; } bool KisCurveOption::isSameCurveUsed() const { return m_useSameCurve; } int KisCurveOption::getCurveMode() const { return m_curveMode; } void KisCurveOption::setSeparateCurveValue(bool separateCurveValue) { m_separateCurveValue = separateCurveValue; } bool KisCurveOption::isCheckable() { return m_checkable; } bool KisCurveOption::isChecked() const { return m_checked; } void KisCurveOption::setChecked(bool checked) { m_checked = checked; } void KisCurveOption::setCurveUsed(bool useCurve) { m_useCurve = useCurve; } void KisCurveOption::setCurveMode(int mode) { m_curveMode = mode; } void KisCurveOption::setCurve(DynamicSensorType sensorType, bool useSameCurve, const KisCubicCurve &curve) { // No switch in state, don't mess with the cache if (useSameCurve == m_useSameCurve) { if (useSameCurve) { Q_FOREACH (KisDynamicSensorSP s, m_sensorMap.values()) { s->setCurve(curve); } } else { KisDynamicSensorSP s = sensor(sensorType, false); if (s) { s->setCurve(curve); } } } else { // moving from not use same curve to use same curve: backup the custom curves if (!m_useSameCurve && useSameCurve) { // Copy the custom curves to the cache and set the new curve on all sensors, active or not m_curveCache.clear(); Q_FOREACH (KisDynamicSensorSP s, m_sensorMap.values()) { m_curveCache[s->sensorType()] = s->curve(); s->setCurve(curve); } } else { //if (m_useSameCurve && !useSameCurve) // Restore the cached curves KisDynamicSensorSP s = 0; Q_FOREACH (DynamicSensorType sensorType, m_curveCache.keys()) { if (m_sensorMap.contains(sensorType)) { s = m_sensorMap[sensorType]; } else { s = KisDynamicSensor::type2Sensor(sensorType, m_name); } s->setCurve(m_curveCache[sensorType]); m_sensorMap[sensorType] = s; } s = 0; // And set the current sensor to the current curve if (!m_sensorMap.contains(sensorType)) { s = KisDynamicSensor::type2Sensor(sensorType, m_name); } if (s) { s->setCurve(curve); s->setCurve(m_curveCache[sensorType]); } } m_useSameCurve = useSameCurve; } } void KisCurveOption::setValueRange(qreal min, qreal max) { m_minValue = qMin(min, max); m_maxValue = qMax(min, max); } void KisCurveOption::setValue(qreal value) { m_value = qBound(m_minValue, value, m_maxValue); } KisCurveOption::ValueComponents KisCurveOption::computeValueComponents(const KisPaintInformation& info) const { ValueComponents components; if (m_useCurve) { QMap::const_iterator i; QList sensorValues; for (i = m_sensorMap.constBegin(); i != m_sensorMap.constEnd(); ++i) { KisDynamicSensorSP s(i.value()); if (s->isActive()) { if (s->isAdditive()) { components.additive += s->parameter(info); components.hasAdditive = true; } else if (s->isAbsoluteRotation()) { components.absoluteOffset = s->parameter(info); components.hasAbsoluteOffset =true; } else { sensorValues << s->parameter(info); components.hasScaling = true; } } } if (sensorValues.count() == 1) { components.scaling = sensorValues.first(); } else { if (m_curveMode == 1){ // add components.scaling = 0; double i; foreach (i, sensorValues) { components.scaling += i; } } else if (m_curveMode == 2){ //max components.scaling = *std::max_element(sensorValues.begin(), sensorValues.end()); } else if (m_curveMode == 3){ //min components.scaling = *std::min_element(sensorValues.begin(), sensorValues.end()); } else if (m_curveMode == 4){ //difference double max = *std::max_element(sensorValues.begin(), sensorValues.end()); double min = *std::min_element(sensorValues.begin(), sensorValues.end()); components.scaling = max-min; } else { //multuply - default double i; foreach (i, sensorValues) { components.scaling *= i; } } } } if (!m_separateCurveValue) { components.constant = m_value; } components.minSizeLikeValue = m_minValue; components.maxSizeLikeValue = m_maxValue; return components; } qreal KisCurveOption::computeSizeLikeValue(const KisPaintInformation& info) const { const ValueComponents components = computeValueComponents(info); return components.sizeLikeValue(); } qreal KisCurveOption::computeRotationLikeValue(const KisPaintInformation& info, qreal baseValue, bool absoluteAxesFlipped) const { const ValueComponents components = computeValueComponents(info); return components.rotationLikeValue(baseValue, absoluteAxesFlipped); } QList KisCurveOption::sensors() { //dbgKrita << "ID" << name() << "has" << m_sensorMap.count() << "Sensors of which" << sensorList.count() << "are active."; return m_sensorMap.values(); } QList KisCurveOption::activeSensors() const { QList sensorList; Q_FOREACH (KisDynamicSensorSP sensor, m_sensorMap.values()) { if (sensor->isActive()) { sensorList << sensor; } } //dbgKrita << "ID" << name() << "has" << m_sensorMap.count() << "Sensors of which" << sensorList.count() << "are active."; return sensorList; } diff --git a/plugins/paintops/libpaintop/kis_curve_option_widget.cpp b/plugins/paintops/libpaintop/kis_curve_option_widget.cpp index 0b6e2e5824..3fe1a5383a 100644 --- a/plugins/paintops/libpaintop/kis_curve_option_widget.cpp +++ b/plugins/paintops/libpaintop/kis_curve_option_widget.cpp @@ -1,301 +1,301 @@ /* This file is part of the KDE project * Copyright (C) 2008 Boudewijn Rempt * Copyright (C) 2009 Sven Langkamp * Copyright (C) 2011 Silvio Heinrich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_curve_option_widget.h" #include "ui_wdgcurveoption.h" #include "widgets/kis_curve_widget.h" #include "kis_dynamic_sensor.h" #include "kis_global.h" #include "kis_curve_option.h" #include "kis_signals_blocker.h" #include "kis_icon_utils.h" inline void setLabel(QLabel* label, const KisCurveLabel& curve_label) { if (curve_label.icon().isNull()) { label->setText(curve_label.name()); } else { label->setPixmap(QPixmap::fromImage(curve_label.icon())); } } KisCurveOptionWidget::KisCurveOptionWidget(KisCurveOption* curveOption, const QString &minLabel, const QString &maxLabel, bool hideSlider) : KisPaintOpOption(curveOption->category(), curveOption->isChecked()) , m_widget(new QWidget) , m_curveOptionWidget(new Ui_WdgCurveOption()) , m_curveOption(curveOption) { setObjectName("KisCurveOptionWidget"); m_curveOptionWidget->setupUi(m_widget); setConfigurationPage(m_widget); m_curveOptionWidget->sensorSelector->setCurveOption(curveOption); updateSensorCurveLabels(m_curveOptionWidget->sensorSelector->currentHighlighted()); updateCurve(m_curveOptionWidget->sensorSelector->currentHighlighted()); connect(m_curveOptionWidget->curveWidget, SIGNAL(modified()), this, SLOT(transferCurve())); connect(m_curveOptionWidget->sensorSelector, SIGNAL(parametersChanged()), SLOT(emitSettingChanged())); connect(m_curveOptionWidget->sensorSelector, SIGNAL(parametersChanged()), SLOT(updateLabelsOfCurrentSensor())); connect(m_curveOptionWidget->sensorSelector, SIGNAL(highlightedSensorChanged(KisDynamicSensorSP )), SLOT(updateSensorCurveLabels(KisDynamicSensorSP ))); connect(m_curveOptionWidget->sensorSelector, SIGNAL(highlightedSensorChanged(KisDynamicSensorSP )), SLOT(updateCurve(KisDynamicSensorSP ))); connect(m_curveOptionWidget->checkBoxUseSameCurve, SIGNAL(stateChanged(int)), SLOT(transferCurve())); // set all the icons for the curve preset shapes updateThemedIcons(); // various curve preset buttons with predefined curves connect(m_curveOptionWidget->linearCurveButton, SIGNAL(clicked(bool)), this, SLOT(changeCurveLinear())); connect(m_curveOptionWidget->revLinearButton, SIGNAL(clicked(bool)), this, SLOT(changeCurveReverseLinear())); connect(m_curveOptionWidget->jCurveButton, SIGNAL(clicked(bool)), this, SLOT(changeCurveJShape())); connect(m_curveOptionWidget->lCurveButton, SIGNAL(clicked(bool)), this, SLOT(changeCurveLShape())); connect(m_curveOptionWidget->sCurveButton, SIGNAL(clicked(bool)), this, SLOT(changeCurveSShape())); connect(m_curveOptionWidget->reverseSCurveButton, SIGNAL(clicked(bool)), this, SLOT(changeCurveReverseSShape())); connect(m_curveOptionWidget->uCurveButton, SIGNAL(clicked(bool)), this, SLOT(changeCurveUShape())); connect(m_curveOptionWidget->revUCurveButton, SIGNAL(clicked(bool)), this, SLOT(changeCurveArchShape())); m_curveOptionWidget->label_ymin->setText(minLabel); m_curveOptionWidget->label_ymax->setText(maxLabel); m_curveOptionWidget->slider->setRange(curveOption->minValue(), curveOption->maxValue(), 2); m_curveOptionWidget->slider->setValue(curveOption->value()); if (hideSlider) { m_curveOptionWidget->slider->hide(); m_curveOptionWidget->strengthLabel->hide(); } connect(m_curveOptionWidget->checkBoxUseCurve, SIGNAL(stateChanged(int)) , SLOT(updateValues())); connect(m_curveOptionWidget->curveMode, SIGNAL(currentIndexChanged(int)), SLOT(updateMode())); connect(m_curveOptionWidget->slider, SIGNAL(valueChanged(qreal)), SLOT(updateValues())); } KisCurveOptionWidget::~KisCurveOptionWidget() { delete m_curveOption; delete m_curveOptionWidget; } void KisCurveOptionWidget::writeOptionSetting(KisPropertiesConfigurationSP setting) const { m_curveOption->writeOptionSetting(setting); } void KisCurveOptionWidget::readOptionSetting(const KisPropertiesConfigurationSP setting) { - setting->dump(); + //setting->dump(); m_curveOption->readOptionSetting(setting); m_curveOptionWidget->checkBoxUseCurve->setChecked(m_curveOption->isCurveUsed()); m_curveOptionWidget->slider->setValue(m_curveOption->value()); m_curveOptionWidget->checkBoxUseSameCurve->setChecked(m_curveOption->isSameCurveUsed()); m_curveOptionWidget->curveMode->setCurrentIndex(m_curveOption->getCurveMode()); disableWidgets(!m_curveOption->isCurveUsed()); m_curveOptionWidget->sensorSelector->reload(); m_curveOptionWidget->sensorSelector->setCurrent(m_curveOption->activeSensors().first()); updateSensorCurveLabels(m_curveOptionWidget->sensorSelector->currentHighlighted()); updateCurve(m_curveOptionWidget->sensorSelector->currentHighlighted()); } void KisCurveOptionWidget::lodLimitations(KisPaintopLodLimitations *l) const { m_curveOption->lodLimitations(l); } bool KisCurveOptionWidget::isCheckable() const { return m_curveOption->isCheckable(); } bool KisCurveOptionWidget::isChecked() const { return m_curveOption->isChecked(); } void KisCurveOptionWidget::setChecked(bool checked) { m_curveOption->setChecked(checked); } KisCurveOption* KisCurveOptionWidget::curveOption() { return m_curveOption; } QWidget* KisCurveOptionWidget::curveWidget() { return m_widget; } void KisCurveOptionWidget::transferCurve() { m_curveOptionWidget->sensorSelector->setCurrentCurve(m_curveOptionWidget->curveWidget->curve(), m_curveOptionWidget->checkBoxUseSameCurve->isChecked()); emitSettingChanged(); } void KisCurveOptionWidget::updateSensorCurveLabels(KisDynamicSensorSP sensor) { if (sensor) { m_curveOptionWidget->label_xmin->setText(KisDynamicSensor::minimumLabel(sensor->sensorType())); m_curveOptionWidget->label_xmax->setText(KisDynamicSensor::maximumLabel(sensor->sensorType(), sensor->length())); } } void KisCurveOptionWidget::updateCurve(KisDynamicSensorSP sensor) { if (sensor) { bool blockSignal = m_curveOptionWidget->curveWidget->blockSignals(true); m_curveOptionWidget->curveWidget->setCurve(sensor->curve()); m_curveOptionWidget->curveWidget->blockSignals(blockSignal); } } void KisCurveOptionWidget::updateLabelsOfCurrentSensor() { updateSensorCurveLabels(m_curveOptionWidget->sensorSelector->currentHighlighted()); updateCurve(m_curveOptionWidget->sensorSelector->currentHighlighted()); } void KisCurveOptionWidget::updateValues() { m_curveOption->setValue(m_curveOptionWidget->slider->value()); m_curveOption->setCurveUsed(m_curveOptionWidget->checkBoxUseCurve->isChecked()); disableWidgets(!m_curveOptionWidget->checkBoxUseCurve->isChecked()); emitSettingChanged(); } void KisCurveOptionWidget::updateMode() { m_curveOption->setCurveMode(m_curveOptionWidget->curveMode->currentIndex()); emitSettingChanged(); } void KisCurveOptionWidget::changeCurveLinear() { QList points; points.push_back(QPointF(0,0)); points.push_back(QPointF(1,1)); m_curveOptionWidget->curveWidget->setCurve(KisCubicCurve(points)); } void KisCurveOptionWidget::changeCurveReverseLinear() { QList points; points.push_back(QPointF(0,1)); points.push_back(QPointF(1,0)); m_curveOptionWidget->curveWidget->setCurve(KisCubicCurve(points)); } void KisCurveOptionWidget::changeCurveSShape() { QList points; points.push_back(QPointF(0,0)); points.push_back(QPointF(0.25,0.1)); points.push_back(QPointF(0.75,0.9)); points.push_back(QPointF(1, 1)); m_curveOptionWidget->curveWidget->setCurve(KisCubicCurve(points)); } void KisCurveOptionWidget::changeCurveReverseSShape() { QList points; points.push_back(QPointF(0,1)); points.push_back(QPointF(0.25,0.9)); points.push_back(QPointF(0.75,0.1)); points.push_back(QPointF(1,0)); m_curveOptionWidget->curveWidget->setCurve(KisCubicCurve(points)); } void KisCurveOptionWidget::changeCurveJShape() { QList points; points.push_back(QPointF(0,0)); points.push_back(QPointF(0.35,0.1)); points.push_back(QPointF(1,1)); m_curveOptionWidget->curveWidget->setCurve(KisCubicCurve(points)); } void KisCurveOptionWidget::changeCurveLShape() { QList points; points.push_back(QPointF(0,1)); points.push_back(QPointF(0.25,0.48)); points.push_back(QPointF(1,0)); m_curveOptionWidget->curveWidget->setCurve(KisCubicCurve(points)); } void KisCurveOptionWidget::changeCurveUShape() { QList points; points.push_back(QPointF(0,1)); points.push_back(QPointF(0.5,0)); points.push_back(QPointF(1,1)); m_curveOptionWidget->curveWidget->setCurve(KisCubicCurve(points)); } void KisCurveOptionWidget::changeCurveArchShape() { QList points; points.push_back(QPointF(0,0)); points.push_back(QPointF(0.5,1)); points.push_back(QPointF(1,0)); m_curveOptionWidget->curveWidget->setCurve(KisCubicCurve(points)); } void KisCurveOptionWidget::disableWidgets(bool disable) { m_curveOptionWidget->checkBoxUseSameCurve->setDisabled(disable); m_curveOptionWidget->curveWidget->setDisabled(disable); m_curveOptionWidget->sensorSelector->setDisabled(disable); m_curveOptionWidget->label_xmax->setDisabled(disable); m_curveOptionWidget->label_xmin->setDisabled(disable); m_curveOptionWidget->label_ymax->setDisabled(disable); m_curveOptionWidget->label_ymin->setDisabled(disable); } void KisCurveOptionWidget::updateThemedIcons() { // set all the icons for the curve preset shapes m_curveOptionWidget->linearCurveButton->setIcon(KisIconUtils::loadIcon("curve-preset-linear")); m_curveOptionWidget->revLinearButton->setIcon(KisIconUtils::loadIcon("curve-preset-linear-reverse")); m_curveOptionWidget->jCurveButton->setIcon(KisIconUtils::loadIcon("curve-preset-j")); m_curveOptionWidget->lCurveButton->setIcon(KisIconUtils::loadIcon("curve-preset-l")); m_curveOptionWidget->sCurveButton->setIcon(KisIconUtils::loadIcon("curve-preset-s")); m_curveOptionWidget->reverseSCurveButton->setIcon(KisIconUtils::loadIcon("curve-preset-s-reverse")); m_curveOptionWidget->uCurveButton->setIcon(KisIconUtils::loadIcon("curve-preset-u")); m_curveOptionWidget->revUCurveButton->setIcon(KisIconUtils::loadIcon("curve-preset-arch")); } diff --git a/sdk/tests/testutil.h b/sdk/tests/testutil.h index 3970c73949..42df66135e 100644 --- a/sdk/tests/testutil.h +++ b/sdk/tests/testutil.h @@ -1,492 +1,509 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TEST_UTIL #define TEST_UTIL #include #include #include #include #include #include #include #include #include #include #include #include "kis_node_graph_listener.h" #include "kis_iterator_ng.h" #include "kis_image.h" #include "testing_nodes.h" #ifndef FILES_DATA_DIR #define FILES_DATA_DIR "." #endif #ifndef FILES_DEFAULT_DATA_DIR #define FILES_DEFAULT_DATA_DIR "." #endif #include "qimage_test_util.h" /** * Routines that are useful for writing efficient tests */ namespace TestUtil { inline KisNodeSP findNode(KisNodeSP root, const QString &name) { if(root->name() == name) return root; KisNodeSP child = root->firstChild(); while (child) { if((root = findNode(child, name))) return root; child = child->nextSibling(); } return KisNodeSP(); } inline void dumpNodeStack(KisNodeSP node, QString prefix = QString("\t")) { qDebug() << node->name(); KisNodeSP child = node->firstChild(); while (child) { if (child->childCount() > 0) { dumpNodeStack(child, prefix + "\t"); } else { qDebug() << prefix << child->name(); } child = child->nextSibling(); } } class TestProgressBar : public KoProgressProxy { public: TestProgressBar() : m_min(0), m_max(0), m_value(0) {} int maximum() const override { return m_max; } void setValue(int value) override { m_value = value; } void setRange(int min, int max) override { m_min = min; m_max = max; } void setFormat(const QString &format) override { m_format = format; } void setAutoNestedName(const QString &name) { m_autoNestedName = name; KoProgressProxy::setAutoNestedName(name); } int min() { return m_min; } int max() { return m_max; } int value() { return m_value; } QString format() { return m_format; } QString autoNestedName() { return m_autoNestedName; } private: int m_min; int m_max; int m_value; QString m_format; QString m_autoNestedName; }; inline bool comparePaintDevices(QPoint & pt, const KisPaintDeviceSP dev1, const KisPaintDeviceSP dev2) { // QTime t; // t.start(); QRect rc1 = dev1->exactBounds(); QRect rc2 = dev2->exactBounds(); if (rc1 != rc2) { pt.setX(-1); pt.setY(-1); } KisHLineConstIteratorSP iter1 = dev1->createHLineConstIteratorNG(0, 0, rc1.width()); KisHLineConstIteratorSP iter2 = dev2->createHLineConstIteratorNG(0, 0, rc1.width()); int pixelSize = dev1->pixelSize(); for (int y = 0; y < rc1.height(); ++y) { do { if (memcmp(iter1->oldRawData(), iter2->oldRawData(), pixelSize) != 0) return false; } while (iter1->nextPixel() && iter2->nextPixel()); iter1->nextRow(); iter2->nextRow(); } // qDebug() << "comparePaintDevices time elapsed:" << t.elapsed(); return true; } template inline bool comparePaintDevicesClever(const KisPaintDeviceSP dev1, const KisPaintDeviceSP dev2, channel_type alphaThreshold = 0) { QRect rc1 = dev1->exactBounds(); QRect rc2 = dev2->exactBounds(); if (rc1 != rc2) { qDebug() << "Devices have different size" << ppVar(rc1) << ppVar(rc2); return false; } KisHLineConstIteratorSP iter1 = dev1->createHLineConstIteratorNG(0, 0, rc1.width()); KisHLineConstIteratorSP iter2 = dev2->createHLineConstIteratorNG(0, 0, rc1.width()); int pixelSize = dev1->pixelSize(); for (int y = 0; y < rc1.height(); ++y) { do { if (memcmp(iter1->oldRawData(), iter2->oldRawData(), pixelSize) != 0) { const channel_type* p1 = reinterpret_cast(iter1->oldRawData()); const channel_type* p2 = reinterpret_cast(iter2->oldRawData()); if (p1[3] < alphaThreshold && p2[3] < alphaThreshold) continue; qDebug() << "Failed compare paint devices:" << iter1->x() << iter1->y(); qDebug() << "src:" << p1[0] << p1[1] << p1[2] << p1[3]; qDebug() << "dst:" << p2[0] << p2[1] << p2[2] << p2[3]; return false; } } while (iter1->nextPixel() && iter2->nextPixel()); iter1->nextRow(); iter2->nextRow(); } return true; } #ifdef FILES_OUTPUT_DIR -struct ExternalImageChecker +struct ReferenceImageChecker { - ExternalImageChecker(const QString &prefix, const QString &testName) - : m_prefix(prefix), + enum StorageType { + InternalStorage = 0, + ExternalStorage + }; + + ReferenceImageChecker(const QString &prefix, const QString &testName, StorageType storageType = ExternalStorage) + : m_storageType(storageType), + m_prefix(prefix), m_testName(testName), m_success(true), m_maxFailingPixels(100), m_fuzzy(1) { } void setMaxFailingPixels(int value) { m_maxFailingPixels = value; } void setFuzzy(int fuzzy){ m_fuzzy = fuzzy; } bool testPassed() const { return m_success; } inline bool checkDevice(KisPaintDeviceSP device, KisImageSP image, const QString &caseName) { - bool result = - checkQImageExternal(device->convertToQImage(0, image->bounds()), - m_testName, - m_prefix, - caseName, m_fuzzy, m_fuzzy, m_maxFailingPixels); + bool result = false; + + + if (m_storageType == ExternalStorage) { + result = checkQImageExternal(device->convertToQImage(0, image->bounds()), + m_testName, + m_prefix, + caseName, m_fuzzy, m_fuzzy, m_maxFailingPixels); + } else { + result = checkQImage(device->convertToQImage(0, image->bounds()), + m_testName, + m_prefix, + caseName, m_fuzzy, m_fuzzy, m_maxFailingPixels); + } m_success &= result; return result; } inline bool checkImage(KisImageSP image, const QString &testName) { bool result = checkDevice(image->projection(), image, testName); m_success &= result; return result; } private: + bool m_storageType; + QString m_prefix; QString m_testName; bool m_success; int m_maxFailingPixels; int m_fuzzy; }; #endif inline quint8 alphaDevicePixel(KisPaintDeviceSP dev, qint32 x, qint32 y) { KisHLineConstIteratorSP iter = dev->createHLineConstIteratorNG(x, y, 1); const quint8 *pix = iter->oldRawData(); return *pix; } inline void alphaDeviceSetPixel(KisPaintDeviceSP dev, qint32 x, qint32 y, quint8 s) { KisHLineIteratorSP iter = dev->createHLineIteratorNG(x, y, 1); quint8 *pix = iter->rawData(); *pix = s; } inline bool checkAlphaDeviceFilledWithPixel(KisPaintDeviceSP dev, const QRect &rc, quint8 expected) { KisHLineIteratorSP it = dev->createHLineIteratorNG(rc.x(), rc.y(), rc.width()); for (int y = rc.y(); y < rc.y() + rc.height(); y++) { for (int x = rc.x(); x < rc.x() + rc.width(); x++) { if(*((quint8*)it->rawData()) != expected) { errKrita << "At point:" << x << y; errKrita << "Expected pixel:" << expected; errKrita << "Actual pixel: " << *((quint8*)it->rawData()); return false; } it->nextPixel(); } it->nextRow(); } return true; } class TestNode : public DefaultNode { Q_OBJECT public: KisNodeSP clone() const override { return KisNodeSP(new TestNode(*this)); } }; class TestGraphListener : public KisNodeGraphListener { public: void aboutToAddANode(KisNode *parent, int index) override { KisNodeGraphListener::aboutToAddANode(parent, index); beforeInsertRow = true; } void nodeHasBeenAdded(KisNode *parent, int index) override { KisNodeGraphListener::nodeHasBeenAdded(parent, index); afterInsertRow = true; } void aboutToRemoveANode(KisNode *parent, int index) override { KisNodeGraphListener::aboutToRemoveANode(parent, index); beforeRemoveRow = true; } void nodeHasBeenRemoved(KisNode *parent, int index) override { KisNodeGraphListener::nodeHasBeenRemoved(parent, index); afterRemoveRow = true; } void aboutToMoveNode(KisNode *parent, int oldIndex, int newIndex) override { KisNodeGraphListener::aboutToMoveNode(parent, oldIndex, newIndex); beforeMove = true; } void nodeHasBeenMoved(KisNode *parent, int oldIndex, int newIndex) override { KisNodeGraphListener::nodeHasBeenMoved(parent, oldIndex, newIndex); afterMove = true; } bool beforeInsertRow; bool afterInsertRow; bool beforeRemoveRow; bool afterRemoveRow; bool beforeMove; bool afterMove; void resetBools() { beforeRemoveRow = false; afterRemoveRow = false; beforeInsertRow = false; afterInsertRow = false; beforeMove = false; afterMove = false; } }; } #include #include #include "kis_undo_stores.h" namespace TestUtil { struct MaskParent { MaskParent(const QRect &_imageRect = QRect(0,0,512,512)) : imageRect(_imageRect) { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); undoStore = new KisSurrogateUndoStore(); image = new KisImage(undoStore, imageRect.width(), imageRect.height(), cs, "test image"); layer = KisPaintLayerSP(new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8)); image->addNode(KisNodeSP(layer.data())); } KisSurrogateUndoStore *undoStore; const QRect imageRect; KisImageSP image; KisPaintLayerSP layer; }; } namespace TestUtil { class MeasureAvgPortion { public: MeasureAvgPortion(int period) : m_period(period), m_val(0), m_total(0), m_cycles(0) { } ~MeasureAvgPortion() { printValues(true); } void addVal(int x) { m_val += x; } void addTotal(int x) { m_total += x; m_cycles++; printValues(); } private: void printValues(bool force = false) { if (m_cycles > m_period || force) { qDebug() << "Val / Total:" << qreal(m_val) / qreal(m_total); qDebug() << "Avg. Val: " << qreal(m_val) / m_cycles; qDebug() << "Avg. Total: " << qreal(m_total) / m_cycles; qDebug() << ppVar(m_val) << ppVar(m_total) << ppVar(m_cycles); m_val = 0; m_total = 0; m_cycles = 0; } } private: int m_period; qint64 m_val; qint64 m_total; qint64 m_cycles; }; struct MeasureDistributionStats { MeasureDistributionStats(int numBins, const QString &name = QString()) : m_numBins(numBins), m_name(name) { reset(); } void reset() { m_values.clear(); m_values.resize(m_numBins); } void addValue(int value) { addValue(value, 1); } void addValue(int value, int increment) { KIS_SAFE_ASSERT_RECOVER_RETURN(value >= 0); if (value >= m_numBins) { m_values[m_numBins - 1] += increment; } else { m_values[value] += increment; } } void print() { qCritical() << "============= Stats =============="; if (!m_name.isEmpty()) { qCritical() << "Name:" << m_name; } int total = 0; for (int i = 0; i < m_numBins; i++) { total += m_values[i]; } for (int i = 0; i < m_numBins; i++) { if (!m_values[i]) continue; const QString lastMarker = i == m_numBins - 1 ? "> " : " "; const QString line = QString(" %1%2: %3 (%4%)") .arg(lastMarker) .arg(i, 3) .arg(m_values[i], 5) .arg(qreal(m_values[i]) / total * 100.0, 7, 'g', 2); qCritical() << qPrintable(line); } qCritical() << "---- ----"; qCritical() << qPrintable(QString("Total: %1").arg(total)); qCritical() << "=================================="; } private: QVector m_values; int m_numBins = 0; QString m_name; }; QStringList getHierarchy(KisNodeSP root, const QString &prefix = ""); bool checkHierarchy(KisNodeSP root, const QStringList &expected); } #endif