diff --git a/CMakeLists.txt b/CMakeLists.txt index db3c23c8faa..ca1e752f855 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1185 +1,1185 @@ cmake_minimum_required(VERSION 2.8.12) project(calligra) message(STATUS "Using CMake version: ${CMAKE_VERSION}") if (POLICY CMP0002) cmake_policy(SET CMP0002 OLD) endif() if (POLICY CMP0017) cmake_policy(SET CMP0017 NEW) endif () if (POLICY CMP0022) cmake_policy(SET CMP0022 OLD) endif () if (POLICY CMP0026) cmake_policy(SET CMP0026 OLD) endif() if (POLICY CMP0046) cmake_policy(SET CMP0046 OLD) endif () if (POLICY CMP0059) cmake_policy(SET CMP0059 OLD) endif() if (POLICY CMP0063) cmake_policy(SET CMP0063 NEW) endif() # ensure out-of-source build string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" isBuildInSource) if(isBuildInSource) message(FATAL_ERROR "Compiling Calligra inside the source folder is not possible.\nPlease refer to the build instruction: https://community.kde.org/Calligra/Building/3\nYou need to clean up the source folder from all build artifacts just created, otherwise further building attempts will fail again: With a git repo, you can use \"git clean -df\" in the toplevel source folder (attention! will remove also uncommited changes to the source code). With sources from a file bundle (like a zip file), delete the source folder and unbundle the sources again.") endif() ###################### ####################### ## Constants defines ## ####################### ###################### # define common versions of Calligra applications, used to generate calligraversion.h # update these version for every release: set(CALLIGRA_VERSION_STRING "3.0 Alpha") set(CALLIGRA_STABLE_VERSION_MAJOR 3) # 3 for 3.x, 4 for 4.x, etc. set(CALLIGRA_STABLE_VERSION_MINOR 0) # 0 for 3.0, 1 for 3.1, etc. set(CALLIGRA_VERSION_RELEASE 89) # 89 for Alpha, increase for next test releases, set 0 for first Stable, etc. set(CALLIGRA_ALPHA 1) # uncomment only for Alpha #set(CALLIGRA_BETA 1) # uncomment only for Beta #set(CALLIGRA_RC 1) # uncomment only for RC set(CALLIGRA_YEAR 2016) # update every year if(NOT DEFINED CALLIGRA_ALPHA AND NOT DEFINED CALLIGRA_BETA AND NOT DEFINED CALLIGRA_RC) set(CALLIGRA_STABLE 1) # do not edit endif() message(STATUS "Calligra version: ${CALLIGRA_VERSION_STRING}") # Define the generic version of the Calligra libraries here # This makes it easy to advance it when the next Calligra release comes. # 14 was the last GENERIC_CALLIGRA_LIB_VERSION_MAJOR of the previous Calligra series # (2.x) so we're starting with 15 in 3.x series. if(CALLIGRA_STABLE_VERSION_MAJOR EQUAL 3) math(EXPR GENERIC_CALLIGRA_LIB_VERSION_MAJOR "${CALLIGRA_STABLE_VERSION_MINOR} + 15") else() # let's make sure we won't forget to update the "15" message(FATAL_ERROR "Reminder: please update offset == 15 used to compute GENERIC_CALLIGRA_LIB_VERSION_MAJOR to something bigger") endif() set(GENERIC_CALLIGRA_LIB_VERSION "${GENERIC_CALLIGRA_LIB_VERSION_MAJOR}.0.0") set(GENERIC_CALLIGRA_LIB_SOVERSION "${GENERIC_CALLIGRA_LIB_VERSION_MAJOR}") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules") message("Module path:" ${CMAKE_MODULE_PATH}) # fetch git revision for the current build set(CALLIGRA_GIT_SHA1_STRING "") set(CALLIGRA_GIT_BRANCH_STRING "") include(GetGitRevisionDescription) get_git_head_revision(GIT_REFSPEC GIT_SHA1) get_git_branch(GIT_BRANCH) if(GIT_SHA1 AND GIT_BRANCH) string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1) set(CALLIGRA_GIT_SHA1_STRING ${GIT_SHA1}) set(CALLIGRA_GIT_BRANCH_STRING ${GIT_BRANCH}) endif() if(NOT DEFINED RELEASE_BUILD) # estimate mode by CMAKE_BUILD_TYPE content if not set on cmdline string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_TOLOWER) set(RELEASE_BUILD_TYPES "release" "relwithdebinfo" "minsizerel") list(FIND RELEASE_BUILD_TYPES "${CMAKE_BUILD_TYPE_TOLOWER}" INDEX) if (INDEX EQUAL -1) set(RELEASE_BUILD FALSE) else() set(RELEASE_BUILD TRUE) endif() endif() message(STATUS "Release build: ${RELEASE_BUILD}") ############ ############# ## Options ## ############# ############ option(GHNS "support Get Hot New Stuff" OFF) option(PACKAGERS_BUILD "Build support of multiple CPU architectures in one binary. Should be used by packagers only." ON) ####################### ######################## ## Productset setting ## ######################## ####################### # For predefined productsets see the definitions in CalligraProducts.cmake and # in the files in the folder cmake/productsets. # Finding out the products & features to build is done in 5 steps: # 1. have the user define the products/features wanted, by giving a productset # 2. estimate all additional required products/features # 3. estimate which of the products/features can be build by external deps # 4. find which products/features have been temporarily disabled due to problems # 5. estimate which of the products/features can be build by internal deps # get the special macros include(CalligraProductSetMacros) # get the definitions of products, features and product sets include(CalligraProducts.cmake) set(PRODUCTSET_DEFAULT "ALL") if(NOT PRODUCTSET) set(PRODUCTSET ${PRODUCTSET_DEFAULT} CACHE STRING "Set of products/features to build" FORCE) endif() if (RELEASE_BUILD) set(CALLIGRA_SHOULD_BUILD_STAGING FALSE) else () set(CALLIGRA_SHOULD_BUILD_STAGING TRUE) endif () # finally choose products/features to build calligra_set_productset(${PRODUCTSET}) ########################## ########################### ## Look for ECM, Qt, KF5 ## ########################### ########################## find_package(ECM 1.7.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) # ECM KDE macros (include first, to have their policies and settings effect all other macros) include(KDEInstallDirs) include(KDECMakeSettings NO_POLICY_SCOPE) include(KDECompilerSettings NO_POLICY_SCOPE) # CMake macros include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(CheckTypeSize) include(CheckIncludeFile) include(GenerateExportHeader) include(FeatureSummary) # ECM macros include(ECMOptionalAddSubdirectory) include(ECMInstallIcons) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMAddTests) include(ECMMarkAsTest) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) # own macros include(MacroBoolTo01) include(MacroOptionalFindPackage) include(MacroEnsureVersion) include(MacroJPEG) include(MacroDesktopToJson) set(REQUIRED_KF5_VERSION "5.7.0") find_package(KF5 ${REQUIRED_KF5_VERSION} REQUIRED COMPONENTS Archive Codecs Completion Config ConfigWidgets CoreAddons DBusAddons DocTools GuiAddons I18n IconThemes ItemViews KDELibs4Support KIO Kross Parts Sonnet TextEditor TextWidgets ThreadWeaver Wallet WidgetsAddons WindowSystem XmlGui NotifyConfig Notifications KCMUtils ) find_package(KF5 ${REQUIRED_KF5_VERSION} QUIET OPTIONAL_COMPONENTS Activities KHtml ) set_package_properties(KF5Activities PROPERTIES TYPE OPTIONAL ) set_package_properties(KF5KHtml PROPERTIES PURPOSE "Required for HTML2ODS import filter" TYPE OPTIONAL ) if(KF5Activities_FOUND) set(HAVE_KACTIVITIES TRUE) endif() if(${KF5_VERSION} VERSION_LESS "5.16.0") - set(OLD_PLUGIN_MIMETYPE_DATA TRUE) + set(CALLIGRA_OLD_PLUGIN_METADATA TRUE) endif() set(REQUIRED_QT_VERSION "5.3.0") find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS Core Gui Network PrintSupport Svg Test Widgets Xml ) find_package(Qt5 ${REQUIRED_QT_VERSION} QUIET COMPONENTS DBus Declarative OpenGL Quick Sql WebKit WebKitWidgets ) set_package_properties(Qt5DBus PROPERTIES TYPE RECOMMENDED ) set_package_properties(Qt5Declarative PROPERTIES PURPOSE "Required for QtQuick1 components" TYPE RECOMMENDED ) set_package_properties(Qt5OpenGL PROPERTIES PURPOSE "Required for QtQuick1 components" TYPE RECOMMENDED ) set_package_properties(Qt5Quick PROPERTIES PURPOSE "Required for QtQuick2 components" TYPE RECOMMENDED ) set_package_properties(Qt5Sql PROPERTIES PURPOSE "Optional for Sheets' database connection" TYPE OPTIONAL ) set_package_properties(Qt5WebKit PROPERTIES PURPOSE "Required for Braindump's Web shape" TYPE OPTIONAL ) set_package_properties(Qt5WebKitWidget PROPERTIES PURPOSE "Required for Stage" TYPE RECOMMENDED ) if(Qt5WebKit_FOUND) add_definitions( -DCAN_USE_QTWEBKIT ) endif() set(HAVE_OPENGL ${Qt5OpenGL_FOUND}) if (GHNS) find_package(Attica 3.0) find_package(NewStuff) set_package_properties(Attica PROPERTIES DESCRIPTION "Attica is used for Get Hot New Stuff." URL "https://projects.kde.org/projects/kdesupport/attica" TYPE OPTIONAL ) if (NOT LIBATTICA_FOUND) set(GHNS FALSE) else () message(STATUS "WARNING: You are compiling with Get Hot New Stuff enabled. Do not do that when building distribution packages. GHNS is unusable these days until someone starts maintaining it again.") endif () endif () find_package(X11) if(X11_FOUND) find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS X11Extras ) set(HAVE_X11 TRUE) add_definitions(-DHAVE_X11) else() set(HAVE_X11 FALSE) endif() # use sane compile flags add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_STRICT_ITERATORS -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_USE_FAST_OPERATOR_PLUS -DQT_USE_FAST_CONCATENATION -DQT_NO_URL_CAST_FROM_STRING -DQT_NO_CAST_TO_ASCII ) # only with this definition will all the FOO_TEST_EXPORT macro do something # TODO: check if this can be moved to only those places which make use of it, # to reduce global compiler definitions that would trigger a recompile of # everything on a change (like adding/removing tests to/from the build) if(BUILD_TESTING) add_definitions(-DCOMPILING_TESTS) endif() # overcome some platform incompatibilities if(WIN32) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif() ########################### ############################ ## Required dependencies ## ############################ ########################### find_package(Perl REQUIRED) find_package(ZLIB REQUIRED) add_definitions(-DBOOST_ALL_NO_LIB) find_package(Boost REQUIRED COMPONENTS system) # for pigment and stage if (NOT Boost_FOUND) message(FATAL_ERROR "Did not find Boost. Boost is required for the core libraries, stage, sheets.") endif () ########################### ############################ ## Optional dependencies ## ############################ ########################### ## ## Check for OpenEXR ## macro_optional_find_package(OpenEXR) macro_bool_to_01(OPENEXR_FOUND HAVE_OPENEXR) ## ## Test for GNU Scientific Library ## macro_optional_find_package(GSL 1.7) set_package_properties(GSL_FOUND PROPERTIES DESCRIPTION "GNU Scientific Library" URL "https://www.gnu.org/software/gsl" PURPOSE "Required by Sheets' solver plugin" TYPE OPTIONAL ) macro_bool_to_01(GSL_FOUND HAVE_GSL) configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h ) ## ## Test for Phonon4Qt5 ## find_package(Phonon4Qt5 QUIET) set_package_properties(Phonon4Qt5 PROPERTIES DESCRIPTION "Abstraction lib for multimedia applications" URL "https://www.kde.org/" PURPOSE "Required by Stage event actions and Videoshape plugin" TYPE OPTIONAL ) ## ## Test for KF5CalendarCore ## find_package(KF5CalendarCore CONFIG QUIET) set_package_properties(KF5CalendarCore PROPERTIES DESCRIPTION "KDE Calendar Library" URL "https://www.kde.org/" PURPOSE "Required by Plan Ical export and optionally used by semantic item Event" TYPE OPTIONAL ) ## ## Test for KF5Contacts ## find_package(KF5Contacts CONFIG QUIET) set_package_properties(KF5Contacts PROPERTIES DESCRIPTION "KDE Address book Library" URL "https://www.kde.org/" PURPOSE "Optionally used by semantic item Contact" TYPE OPTIONAL ) ## ## Test for KF5AkonadiContact ## find_package(KF5AkonadiContact CONFIG QUIET) set_package_properties(KF5AkonadiContact PROPERTIES DESCRIPTION "Library for Accessing Contacts stored in Akonadi" URL "https://www.kde.org/" PURPOSE "Optionally used by Plan" TYPE OPTIONAL ) ## ## Test for KF5AkonadiCore ## find_package(KF5Akonadi CONFIG QUIET) set_package_properties(KF5Akonadi PROPERTIES DESCRIPTION "Library for general Access to Akonadi" URL "https://www.kde.org/" PURPOSE "Optionally used by semantic items Event and Contact" TYPE OPTIONAL ) ## ## Test for KGantt ## macro_optional_find_package(KGantt 2.6.0 QUIET) set_package_properties(KGantt PROPERTIES DESCRIPTION "Library for creating Gantt diagrams (part of KDiagram)" URL "https://www.kde.org/" PURPOSE "Required by Plan" TYPE RECOMMENDED ) ## ## Test for KChart ## macro_optional_find_package(KChart 2.6.0 QUIET) set_package_properties(KChart PROPERTIES DESCRIPTION "Library for creating business charts (part of KDiagram)" URL "https://www.kde.org/" PURPOSE "Required by Chart shape and Plan" TYPE RECOMMENDED ) ## ## Test for KReport ## macro_optional_find_package(KReport 2.96.1 QUIET) set_package_properties(KReport PROPERTIES DESCRIPTION "A framework for the creation and generation of reports in multiple formats" URL "https://community.kde.org/KReport" PURPOSE "Required by Plan" TYPE RECOMMENDED ) ## ## Test for KPropertyWidgets ## macro_optional_find_package(KPropertyWidgets 2.98.0 QUIET) set_package_properties(KPropertyWidgets PROPERTIES DESCRIPTION "A property editing framework with editor widget" URL "https://community.kde.org/KProperty" PURPOSE "Required by Plan" TYPE RECOMMENDED ) ## ## Test for eigen3 ## macro_optional_find_package(Eigen3) set_package_properties(Eigen3 PROPERTIES DESCRIPTION "C++ template library for linear algebra" URL "http://eigen.tuxfamily.org" PURPOSE "Required by Calligra Sheets" TYPE RECOMMENDED ) ## ## Test for QCA2 ## macro_optional_find_package(Qca-qt5 2.1.0 QUIET) set_package_properties(Qca-qt5 PROPERTIES DESCRIPTION "Qt Cryptographic Architecture" URL "http:/download.kde.org/stable/qca-qt5" PURPOSE "Required for encrypted OpenDocument files and encrypted xls files support (available as a module in kdesupport)" TYPE OPTIONAL ) ## ## Test for soprano ## # QT5TODO: push for released (and maintained) Qt5 version of Soprano, T462, T461 # macro_optional_find_package(Soprano) set(Soprano_FOUND FALSE) set_package_properties(Soprano PROPERTIES DESCRIPTION "RDF handling library" URL "http://soprano.sourceforge.net/" PURPOSE "Required to handle RDF metadata in ODF" TYPE OPTIONAL ) if(NOT Soprano_FOUND) set(SOPRANO_INCLUDE_DIR "") endif() ## ## Test for marble ## macro_optional_find_package(Marble CONFIG) set_package_properties(Marble PROPERTIES DESCRIPTION "World Globe Widget library" URL "https://marble.kde.org/" PURPOSE "Required by RDF to show locations on a map" TYPE OPTIONAL ) ## ## Test for lcms ## macro_optional_find_package(LCMS2) set_package_properties(LCMS2 PROPERTIES DESCRIPTION "LittleCMS, a color management engine" URL "http://www.littlecms.com" PURPOSE "Will be used for color management" TYPE OPTIONAL ) if(LCMS2_FOUND) if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 ) set(HAVE_LCMS24 TRUE) endif() set(HAVE_REQUIRED_LCMS_VERSION TRUE) set(HAVE_LCMS2 TRUE) endif() ## ## Test for Vc ## set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ) set(HAVE_VC FALSE) if( NOT MSVC) macro_optional_find_package(Vc 1.1.0) set_package_properties(Vc PROPERTIES DESCRIPTION "Portable, zero-overhead SIMD library for C++" URL "https://github.com/VcDevel/Vc" PURPOSE "Required by the pigment for vectorization" TYPE OPTIONAL ) macro_bool_to_01(Vc_FOUND HAVE_VC) macro_bool_to_01(PACKAGERS_BUILD DO_PACKAGERS_BUILD) endif() configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h ) if(HAVE_VC) message(STATUS "Vc found!") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/vc") include (VcMacros) if(Vc_COMPILER_IS_CLANG) set(ADDITIONAL_VC_FLAGS "-Wabi -ffp-contract=fast -fPIC") elseif (NOT MSVC) set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast -fPIC") endif() #Handle Vc master if(Vc_COMPILER_IS_GCC OR Vc_COMPILER_IS_CLANG) AddCompilerFlag("-std=c++11" _ok) if(NOT _ok) AddCompilerFlag("-std=c++0x" _ok) endif() endif() macro(ko_compile_for_all_implementations_no_scalar _objs _src) if(PACKAGERS_BUILD) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) else() set(${_objs} ${_src}) endif() endmacro() macro(ko_compile_for_all_implementations _objs _src) if(PACKAGERS_BUILD) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) else() set(${_objs} ${_src}) endif() endmacro() if (NOT PACKAGERS_BUILD) # Optimize the whole Calligra for current architecture set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Vc_DEFINITIONS}") endif () endif() set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} ) if(WIN32) set(LIB_INSTALL_DIR ${LIB_INSTALL_DIR} RUNTIME DESTINATION ${BIN_INSTALL_DIR} LIBRARY ${INSTALL_TARGETS_DEFAULT_ARGS} ARCHIVE ${INSTALL_TARGETS_DEFAULT_ARGS} ) endif() ## ## Test for Fontconfig ## ## Only test if on non-Windows system if(NOT WIN32 AND NOT APPLE) macro_optional_find_package(Fontconfig) set_package_properties(Fontconfig PROPERTIES DESCRIPTION "Library for configuring and customizing font access" URL "http://fontconfig.org" PURPOSE "Required to handle exact font size" TYPE RECOMMENDED ) endif() ## ## Test for Freetype ## ## Only test if on non-Windows system if(NOT WIN32 AND NOT APPLE) macro_optional_find_package(Freetype) set_package_properties(Freetype PROPERTIES DESCRIPTION "A Free, High-Quality, and Portable Font Engine" URL "http://www.freetype.org/" PURPOSE "Required to handle exact font size" TYPE RECOMMENDED ) endif() if(NOT FONTCONFIG_FOUND OR NOT FREETYPE_FOUND) set(FONTCONFIG_INCLUDE_DIR "") set(FREETYPE_INCLUDE_DIRS "") else() add_definitions( -DSHOULD_BUILD_FONT_CONVERSION ) endif() ## ## Test endianess ## include (TestBigEndian) test_big_endian(CMAKE_WORDS_BIGENDIAN) ## ## Test SharedMimeInfo ## macro_optional_find_package(SharedMimeInfo) set_package_properties(SharedMimeInfo PROPERTIES DESCRIPTION "Shared Mime Info" URL "http://freedesktop.org/wiki/Software/shared-mime-info" PURPOSE "Required to determine file types SVM or all of MSOOXML." TYPE RECOMMENDED ) ## ## Test for Okular ## macro_optional_find_package(Okular5 1.0.0 QUIET) set_package_properties(Okular5 PROPERTIES DESCRIPTION "A unified document viewer" URL "https://okular.kde.org/" PURPOSE "Required to build the plugins for Okular" TYPE OPTIONAL ) ## ## Test for librevenge ## macro_optional_find_package(LibRevenge) set_package_properties(LibRevenge PROPERTIES DESCRIPTION "A base library for writing document import filters" URL "http://sf.net/p/libwpd/librevenge/" PURPOSE "Required by various import filters" TYPE OPTIONAL ) ## ## Test for libodfgen ## macro_optional_find_package(LibOdfGen) set_package_properties(LibOdfGen PROPERTIES DESCRIPTION "Open Document Format Generation Library" URL "http://sf.net/p/libwpd/libodfgen/" PURPOSE "Required by various import filters" TYPE OPTIONAL ) ## ## Test for WordPerfect Document Library ## macro_optional_find_package(LibWpd) set_package_properties(LibWpd PROPERTIES DESCRIPTION "WordPerfect Document Library" URL "http://libwpd.sourceforge.net/" PURPOSE "Required by the Words WPD import filter" TYPE OPTIONAL ) ## ## Test for WordPerfect Graphics Library ## macro_optional_find_package(LibWpg) set_package_properties(LibWpg PROPERTIES DESCRIPTION "WordPerfect Graphics Library" URL "http://libwpg.sourceforge.net/" PURPOSE "Required by the Karbon WPG import filter" TYPE OPTIONAL ) ## ## Test for Microsoft Works Document Library ## macro_optional_find_package(LibWps) set_package_properties(LibWps PROPERTIES DESCRIPTION "Microsoft Works Document Library" URL "http://libwps.sourceforge.net/" PURPOSE "Required by the Words WPS import filter" TYPE OPTIONAL ) ## ## Test for Microsoft Visio Document Library ## macro_optional_find_package(LibVisio) set_package_properties(LibVisio PROPERTIES DESCRIPTION "Visio Import Filter Library" URL "https://wiki.documentfoundation.org/DLP/Libraries/libvisio" PURPOSE "Required by the Flow visio import filter" TYPE OPTIONAL ) ## ## Test for Apple Keynote Document Library ## macro_optional_find_package(LibEtonyek) set_package_properties(LibEtonyek PROPERTIES DESCRIPTION "Apple Keynote Document Library" URL "https://wiki.documentfoundation.org/DLP/Libraries/libetonyek" PURPOSE "Required by the Stage keynote import filter" TYPE OPTIONAL ) ## ## Test for qt-poppler ## macro_optional_find_package(Poppler) set_package_properties(Poppler PROPERTIES DESCRIPTION "A PDF rendering library" URL "http://poppler.freedesktop.org" PURPOSE "Required by the Karbon PDF import filter and CSTester PDF feature" TYPE OPTIONAL ) ## ## Test for qt-poppler not-officially-supported XPDF Headers ## Installing these is off by default in poppler sources, so lets make ## sure they're really there before trying to build the pdf import ## macro_optional_find_package(PopplerXPDFHeaders) set_package_properties(PopplerXPDFHeaders PROPERTIES DESCRIPTION "XPDF headers in the Poppler Qt5 interface library" URL "http://poppler.freedesktop.org" PURPOSE "Required by the Karbon PDF import filter" TYPE OPTIONAL ) ## ## Test for libgit2 and Libqgit2 ## macro_optional_find_package(Libgit2) macro_optional_find_package(Libqgit2) ## ## Generate a file for prefix information ## ############################### ################################ ## Add Calligra helper macros ## ################################ ############################### include(MacroCalligraAddBenchmark) #################### ##################### ## Define includes ## ##################### #################### # WARNING: make sure that QT_INCLUDES is the first directory to be added to include_directory before # any other include directory # for config.h and includes (if any?) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/interfaces ) set(KOVERSION_INCLUDES ${CMAKE_SOURCE_DIR}/libs/version ${CMAKE_BINARY_DIR}/libs/version ) include_directories(${KOVERSION_INCLUDES}) # koplugin is at the bottom of the stack set(KOPLUGIN_INCLUDES ${CMAKE_SOURCE_DIR}/libs/plugin) set(KUNDO2_INCLUDES ${CMAKE_SOURCE_DIR}/libs/kundo2 ${CMAKE_BINARY_DIR}/libs/kundo2) # koodf is at the bottom of the stack set(KOODF_INCLUDES ${CMAKE_SOURCE_DIR}/libs/odf ${CMAKE_SOURCE_DIR}/libs/store ${CMAKE_BINARY_DIR}/libs/odf ${CMAKE_BINARY_DIR}/libs/store ${KOVERSION_INCLUDES} ) # pigment depends on koplugin and lcms set(PIGMENT_INCLUDES ${KOPLUGIN_INCLUDES} ${KOVERSION_INCLUDES} ${CMAKE_SOURCE_DIR}/libs/pigment ${CMAKE_BINARY_DIR}/libs/pigment ${CMAKE_SOURCE_DIR}/libs/pigment/compositeops ${CMAKE_SOURCE_DIR}/libs/pigment/resources ${Boost_INCLUDE_DIRS} ) # flake depends on koodf and pigment set(FLAKE_INCLUDES ${CMAKE_SOURCE_DIR}/libs/flake ${KOODF_INCLUDES} ${PIGMENT_INCLUDES} ${KUNDO2_INCLUDES} ${CMAKE_SOURCE_DIR}/libs/widgetutils ${CMAKE_SOURCE_DIR}/libs/flake/commands ${CMAKE_SOURCE_DIR}/libs/flake/tools ${CMAKE_SOURCE_DIR}/libs/flake/svg ${CMAKE_BINARY_DIR}/libs/flake) # vectorimage set(VECTORIMAGE_INCLUDES ${CMAKE_SOURCE_DIR}/libs/vectorimage ${CMAKE_SOURCE_DIR}/libs/vectorimage/libemf ${CMAKE_SOURCE_DIR}/libs/vectorimage/libsvm ${CMAKE_SOURCE_DIR}/libs/vectorimage/libwmf) # KoText depends on koplugin, odf set(KOTEXT_INCLUDES ${CMAKE_SOURCE_DIR}/libs/text ${CMAKE_BINARY_DIR}/libs/text ${CMAKE_SOURCE_DIR}/libs/text/changetracker ${CMAKE_SOURCE_DIR}/libs/text/styles ${CMAKE_SOURCE_DIR}/libs/text/opendocument ${SOPRANO_INCLUDE_DIR} ${FLAKE_INCLUDES} ${KOODF_INCLUDES}) # TextLayout depends on kotext set(TEXTLAYOUT_INCLUDES ${KOTEXT_INCLUDES} ${CMAKE_SOURCE_DIR}/libs/textlayout ${CMAKE_BINARY_DIR}/libs/textlayout) # Widgets depends on kotext and flake set(KOWIDGETS_INCLUDES ${KOTEXT_INCLUDES} ${CMAKE_SOURCE_DIR}/libs/widgetutils ${CMAKE_BINARY_DIR}/libs/widgetutils ${CMAKE_SOURCE_DIR}/libs/widgets ${CMAKE_BINARY_DIR}/libs/widgets) # BasicFlakes depends on flake, widgets set(BASICFLAKES_INCLUDES ${KOWIDGETS_INCLUDES} ${CMAKE_SOURCE_DIR}/libs/basicflakes ${CMAKE_SOURCE_DIR}/libs/basicflakes/tools) # komain depends on kotext & flake set(KOMAIN_INCLUDES ${KOWIDGETS_INCLUDES} ${TEXTLAYOUT_INCLUDES} ${CMAKE_SOURCE_DIR}/libs/main ${CMAKE_BINARY_DIR}/libs/main ${CMAKE_SOURCE_DIR}/libs/main/config) set(KORDF_INCLUDES ${KOMAIN_INCLUDES} ${CMAKE_SOURCE_DIR}/libs/rdf ) set(KORDF_LIBS kordf) if(SHOULD_BUILD_FEATURE_SCRIPTING) set(KOKROSS_INCLUDES ${CMAKE_SOURCE_DIR}/libs/kross ${CMAKE_BINARY_DIR}/libs/kross) endif() # kopageapp set(KOPAGEAPP_INCLUDES ${TEXTLAYOUT_INCLUDES} ${PIGMENT_INCLUDES} ${KOMAIN_INCLUDES} ${CMAKE_SOURCE_DIR}/libs/widgets ${CMAKE_SOURCE_DIR}/libs/pageapp ${CMAKE_SOURCE_DIR}/libs/pageapp/commands ${CMAKE_BINARY_DIR}/libs/pageapp ) ############################################# #### filter libraries #### ############################################# # libodf2 set(KOODF2_INCLUDES ${CMAKE_SOURCE_DIR}/filters/libodf2 ${CMAKE_SOURCE_DIR}/filters/libodf2/chart ) # libodfreader set(KOODFREADER_INCLUDES ${CMAKE_SOURCE_DIR}/filters/libodfreader ) ################################################### #################################################### ## Detect which products/features can be compiled ## #################################################### ################################################### if (NOT WIN32) set(NOT_WIN TRUE) endif() if (NOT QT_MAC_USE_COCOA) set(NOT_COCOA TRUE) endif() if (KReport_FOUND AND KREPORT_SCRIPTING) set(KReport_WithScripting_FOUND TRUE) endif() calligra_drop_product_on_bad_condition( FEATURE_RDF Soprano_FOUND "Soprano not found" ) calligra_drop_product_on_bad_condition( PART_STAGE Qt5WebKitWidgets_FOUND "Qt5WebKitWidgets devel not found" ) calligra_drop_product_on_bad_condition( PART_SHEETS EIGEN3_FOUND "Eigen devel not found" ) calligra_drop_product_on_bad_condition( OKULAR_GENERATOR_ODP Okular5_FOUND "Okular devel not found" ) calligra_drop_product_on_bad_condition( OKULAR_GENERATOR_ODT Okular5_FOUND "Okular devel not found" ) calligra_drop_product_on_bad_condition( PLUGIN_CHARTSHAPE KChart_FOUND "KChart devel not found" ) calligra_drop_product_on_bad_condition( PLUGIN_VIDEOSHAPE Phonon4Qt5_FOUND "Phonon4Qt5 devel not found" ) calligra_drop_product_on_bad_condition( FILTER_KEY_TO_ODP LIBODFGEN_FOUND "libodfgen devel not found" LIBETONYEK_FOUND "libetonyek devel not found" LIBREVENGE_FOUND "librevenge devel not found" ) calligra_drop_product_on_bad_condition( FILTER_VISIO_TO_ODG LIBODFGEN_FOUND "libodfgen devel not found" LIBVISIO_FOUND "libvisio devel not found" LIBREVENGE_FOUND "librevenge devel not found" ) calligra_drop_product_on_bad_condition( FILTER_WORDPERFECT_TO_ODT LIBODFGEN_FOUND "libodfgen devel not found" LIBWPD_FOUND "libwpd devel not found" LIBWPG_FOUND "libwpg devel not found" LIBREVENGE_FOUND "librevenge devel not found" ) calligra_drop_product_on_bad_condition( FILTER_WORKS_TO_ODT LIBODFGEN_FOUND "libodfgen devel not found" LIBWPS_FOUND "libwps devel not found" LIBREVENGE_FOUND "librevenge devel not found" ) calligra_drop_product_on_bad_condition( FILTER_WPG_TO_SVG LIBWPG_FOUND "libwpg devel not found" LIBREVENGE_FOUND "librevenge devel not found" ) calligra_drop_product_on_bad_condition( FILTER_WPG_TO_ODG LIBODFGEN_FOUND "libodfgen devel not found" LIBWPG_FOUND "libwpg devel not found" LIBREVENGE_FOUND "librevenge devel not found" ) calligra_drop_product_on_bad_condition( FILTER_PDF_TO_SVG NOT_WIN "not supported on Windows" PopplerXPDFHeaders_FOUND "poppler xpdf headers not found" ) calligra_drop_product_on_bad_condition( FILTER_HTML_TO_ODS NOT_WIN "not supported on Windows" NOT_COCOA "not supported with Qt Cocoa" KF5KHtml_FOUND "KF5KHtml devel not found" ) calligra_drop_product_on_bad_condition( FILTER_SHEETS_TO_HTML NOT_WIN "not supported on Windows" NOT_COCOA "not supported with Qt Cocoa" ) calligra_drop_product_on_bad_condition( FILTER_KSPREAD_TO_LATEX NOT_WIN "not supported on Windows" NOT_COCOA "not supported with Qt Cocoa" ) calligra_drop_product_on_bad_condition( APP_BRAINDUMP NOT_WIN "unmaintained on Windows" ) calligra_drop_product_on_bad_condition( APP_PLAN KGantt_FOUND "KGantt devel not found" KChart_FOUND "KChart devel not found" KReport_WithScripting_FOUND "KReport with scripting support not found" KPropertyWidgets_FOUND "KPropertyWidgets not found" ) calligra_drop_product_on_bad_condition( PLUGIN_CALLIGRAGEMINI_GIT LIBGIT2_FOUND "libgit2 devel not found" LIBQGIT2_FOUND "libqgit2 devel not found" ) calligra_drop_product_on_bad_condition( PART_QTQUICK Qt5OpenGL_FOUND "Qt OpenGL not found" Qt5Declarative_FOUND "QtDeclarative not found" ) calligra_drop_product_on_bad_condition( PART_COMPONENTS Qt5Quick_FOUND "QtQuick not found" ) calligra_drop_product_on_bad_condition( APP_SLIDECOMPARE Qt5OpenGL_FOUND "Qt OpenGL not found" ) ############################################# #### Backward compatibility BUILD_x=off #### ############################################# # workaround: disable directly all products which might be activated by internal # dependencies, but belong to scope of old flag calligra_drop_products_on_old_flag(braindump APP_BRAINDUMP) calligra_drop_products_on_old_flag(flow APP_FLOW) calligra_drop_products_on_old_flag(karbon APP_KARBON) calligra_drop_products_on_old_flag(plan APP_PLAN) calligra_drop_products_on_old_flag(sheets PART_SHEETS APP_SHEETS) calligra_drop_products_on_old_flag(stage PART_STAGE APP_STAGE) calligra_drop_products_on_old_flag(words PART_WORDS APP_WORDS) ############################################# #### Temporarily broken products #### ############################################# # If a product does not build due to some temporary brokeness disable it here, # by calling calligra_disable_product with the product id and the reason, # e.g.: # calligra_disable_product(APP_FOO "isn't buildable at the moment") ############################################# #### Calculate buildable products #### ############################################# calligra_drop_unbuildable_products() ############################################# #### Setup product-depending vars #### ############################################# if(SHOULD_BUILD_FEATURE_RDF) add_definitions( -DSHOULD_BUILD_RDF ) endif() ################### #################### ## Subdirectories ## #################### ################### add_subdirectory(words) if (SHOULD_BUILD_APP_FLOW) add_subdirectory(flow) endif () add_subdirectory(stage) if(SHOULD_BUILD_APP_PLAN) add_subdirectory(plan) endif() add_subdirectory(sheets) if(SHOULD_BUILD_APP_KARBON) add_subdirectory(karbon) endif() if(SHOULD_BUILD_APP_BRAINDUMP) add_subdirectory(braindump) endif() if(SHOULD_BUILD_DOC) add_subdirectory(doc) endif() if(SHOULD_BUILD_PART_QTQUICK) add_subdirectory(qtquick) endif() if(SHOULD_BUILD_PART_COMPONENTS) add_subdirectory(components) endif() if(SHOULD_BUILD_GEMINI) add_subdirectory(gemini) endif() # non-app directories are moved here because they can depend on SHOULD_BUILD_{appname} variables set above add_subdirectory(libs) add_subdirectory(interfaces) add_subdirectory(pics) add_subdirectory(plugins) add_subdirectory(servicetypes) add_subdirectory(devtools) add_subdirectory(extras) add_subdirectory(filters) add_subdirectory(data) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) calligra_product_deps_report("product_deps") calligra_log_should_build() add_custom_target(apidox doc/api/gendocs.pl WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) configure_file(KoConfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h ) if (SHOULD_BUILD_DEVEL_HEADERS) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligra COMPONENT Devel) endif() diff --git a/KoConfig.h.cmake b/KoConfig.h.cmake index 76c57dfc939..735e2e314fd 100644 --- a/KoConfig.h.cmake +++ b/KoConfig.h.cmake @@ -1,58 +1,58 @@ // Check windows #ifdef Q_OS_WIN #ifdef _WIN64 #define ENV64BIT #else #define ENV32BIT #endif #endif // Check GCC #if __GNUC__ #if defined (__x86_64__) || defined (__ppc64__) #define ENV64BIT #else #define ENV32BIT #endif #endif #ifdef __APPLE__ # ifdef __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # else # undef WORDS_BIGENDIAN # endif #else /* Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ #cmakedefine WORDS_BIGENDIAN ${CMAKE_WORDS_BIGENDIAN} #endif /* Defines if you Get Hot New Stuff support */ #cmakedefine GHNS 1 /* Number of bits in a file offset, on hosts where this is settable. */ #define _FILE_OFFSET_BITS 64 /* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ /* #undef _LARGEFILE_SOURCE */ /* Define for large files, on AIX-style hosts. */ /* #undef _LARGE_FILES */ /* Defines if your system has the OpenEXR library */ #cmakedefine HAVE_OPENEXR 1 /* Defines if you have GL (Mesa, OpenGL, ...) and Qt GL support */ #cmakedefine HAVE_OPENGL 1 /* Defines if we use lcms2 */ #cmakedefine HAVE_LCMS2 1 /* Defines if we use lcms2.4 */ #cmakedefine HAVE_LCMS24 1 /* Defines if we use KActivities */ #cmakedefine HAVE_KACTIVITIES 1 /* Defines if the old plugin metadata for mimetypes is used */ -#cmakedefine OLD_PLUGIN_MIMETYPE_DATA 1 +#cmakedefine CALLIGRA_OLD_PLUGIN_METADATA 1 diff --git a/gemini/MainWindow.cpp b/gemini/MainWindow.cpp index e7831a05bc2..b2b1c3e64a7 100644 --- a/gemini/MainWindow.cpp +++ b/gemini/MainWindow.cpp @@ -1,861 +1,870 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * Copyright (C) 2012 KO GmbH. Contact: Boudewijn Rempt * Copyright (C) 2013 Dan Leinir Turthra Jensen * * 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 "MainWindow.h" #include #include "desktopviewproxy.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 // CALLIGRA_OLD_PLUGIN_METADATA #include "PropertyContainer.h" #include "RecentFileManager.h" #include "DocumentManager.h" #include "QmlGlobalEngine.h" #include "Settings.h" #include "Theme.h" #include "DocumentListModel.h" #include "Constants.h" #include "SimpleTouchArea.h" #include "ToolManager.h" #include "ParagraphStylesModel.h" #include "KeyboardModel.h" #include "ScribbleArea.h" #include "RecentImageImageProvider.h" #include "RecentFilesModel.h" #include "TemplatesModel.h" #include "CloudAccountsModel.h" #ifdef Q_OS_WIN // Slate mode/docked detection stuff #include #define SM_CONVERTIBLESLATEMODE 0x2003 #define SM_SYSTEMDOCKED 0x2004 #endif class MainWindow::Private { public: Private(MainWindow* qq) : q(qq) , allowClose(true) , touchView(0) , desktopView(0) , currentView(0) , settings(0) , slateMode(true) , docked(false) , touchKoView(0) , touchEventReceiver(0) , desktopKoView(0) , desktopViewProxy(0) , forceDesktop(false) , forceTouch(false) , temporaryFile(false) , syncObject(0) , toDesktop(0) , toTouch(0) , switcher(0) , alternativeSaveAction(0) { #ifdef Q_OS_WIN // slateMode = (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0); // docked = (GetSystemMetrics(SM_SYSTEMDOCKED) != 0); #endif fullScreenThrottle = new QTimer(qq); fullScreenThrottle->setInterval(500); fullScreenThrottle->setSingleShot(true); } MainWindow* q; bool allowClose; QWidget* touchWidget; QQuickView* touchView; QPointer desktopView; QObject* currentView; Settings *settings; bool slateMode; bool docked; QString currentTouchPage; KoView* touchKoView; QObject* touchEventReceiver; KoView* desktopKoView; DesktopViewProxy* desktopViewProxy; bool forceDesktop; bool forceTouch; bool temporaryFile; ViewModeSynchronisationObject* syncObject; QAction* toDesktop; QAction* toTouch; QToolButton* switcher; QAction* alternativeSaveAction; QTimer* fullScreenThrottle; void initTouchView(QObject* parent) { touchView = new QQuickView(); QmlGlobalEngine::instance()->setEngine(touchView->engine()); touchView->engine()->addImageProvider(QLatin1String("recentimage"), new RecentImageImageProvider); touchView->engine()->rootContext()->setContextProperty("mainWindow", parent); settings = new Settings( q ); DocumentManager::instance()->setSettingsManager( settings ); touchView->engine()->rootContext()->setContextProperty("DocumentManager", DocumentManager::instance()); touchView->engine()->rootContext()->setContextProperty("Settings", settings); touchView->engine()->rootContext()->setContextProperty("Constants", new Constants( q )); touchView->engine()->rootContext()->setContextProperty("RecentFileManager", DocumentManager::instance()->recentFileManager()); touchView->engine()->rootContext()->setContextProperty("WORDS_MIME_TYPE", QString(WORDS_MIME_TYPE)); touchView->engine()->rootContext()->setContextProperty("STAGE_MIME_TYPE", QString(STAGE_MIME_TYPE)); #ifdef Q_OS_WIN QDir appdir(qApp->applicationDirPath()); // Corrects for mismatched case errors in path (qtdeclarative fails to load) wchar_t buffer[1024]; QString absolute = appdir.absolutePath(); DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024); rv = ::GetLongPathName(buffer, buffer, 1024); QString correctedPath((QChar *)buffer); appdir.setPath(correctedPath); // for now, the app in bin/ and we still use the env.bat script appdir.cdUp(); // QT5TODO: adapt to QML_IMPORT_PATH usage and install to ${QML_INSTALL_DIR} touchView->engine()->addImportPath(appdir.canonicalPath() + "/imports"); touchView->engine()->addImportPath(appdir.canonicalPath() + "/lib/calligra/imports"); touchView->engine()->addImportPath(appdir.canonicalPath() + "/lib64/calligra/imports"); QString mainqml = appdir.canonicalPath() + "/share/apps/calligragemini/calligragemini.qml"; #else QString mainqml = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("calligragemini/calligragemini.qml")); #endif Q_ASSERT(QFile::exists(mainqml)); if (!QFile::exists(mainqml)) { QMessageBox::warning(0, "No QML found", mainqml + " doesn't exist."); } QFileInfo fi(mainqml); touchView->setSource(QUrl::fromLocalFile(fi.canonicalFilePath())); touchView->setResizeMode( QQuickView::SizeRootObjectToView ); toDesktop = new QAction(q); toDesktop->setEnabled(true); toDesktop->setText(tr("Switch to Desktop")); // useful for monkey-testing to crash... //toDesktop->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_D); //q->addAction(toDesktop); //connect(toDesktop, SIGNAL(triggered(bool)), q, SLOT(switchDesktopForced())); connect(toDesktop, SIGNAL(triggered(bool)), q, SLOT(switchToDesktop())); touchView->engine()->rootContext()->setContextProperty("switchToDesktopAction", toDesktop); } void initDesktopView() { if(settings->currentFile().isEmpty()) { return; } // Tell the iconloader about share/apps/calligra/icons KIconLoader::global()->addAppDir("calligra"); // Initialize all Calligra directories etc. KoGlobal::initialize(); // The default theme is not what we want for Gemini KConfigGroup group(KSharedConfig::openConfig(), "theme"); if(group.readEntry("Theme", "no-theme-is-set") == QLatin1String("no-theme-is-set")) { group.writeEntry("Theme", "Krita-dark"); } if(settings->currentFileClass() == WORDS_MIME_TYPE) { qApp->setApplicationName("calligrawords"); desktopView = new KoMainWindow(WORDS_MIME_TYPE, KWFactory::componentData()); } else if(settings->currentFileClass() == STAGE_MIME_TYPE) { qApp->setApplicationName("calligrastage"); desktopView = new KoMainWindow(STAGE_MIME_TYPE, KPrFactory::componentData()); } else { desktopView = 0; qDebug() << "Big trouble, things gonna break. desktopView is not created." << settings->currentFileClass(); return; } toTouch = new QAction(desktopView); toTouch->setEnabled(false); toTouch->setText(tr("Switch to Touch")); toTouch->setIcon(koIcon("system-reboot")); toTouch->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_S); //connect(toTouch, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(switchTouchForced())); connect(toTouch, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(switchToTouch())); desktopView->actionCollection()->addAction("SwitchToTouchView", toTouch); switcher = new QToolButton(); switcher->setEnabled(false); switcher->setText(tr("Switch to Touch")); switcher->setIcon(koIcon("system-reboot")); switcher->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); //connect(switcher, SIGNAL(clicked(bool)), q, SLOT(switchDesktopForced())); connect(switcher, SIGNAL(clicked(bool)), q, SLOT(switchToTouch())); desktopView->menuBar()->setCornerWidget(switcher); // DesktopViewProxy connects itself up to everything appropriate on construction, // and destroys itself again when the view is removed desktopViewProxy = new DesktopViewProxy(q, desktopView); connect(desktopViewProxy, SIGNAL(documentSaved()), q, SIGNAL(documentSaved())); connect(desktopViewProxy, SIGNAL(documentSaved()), q, SLOT(resetWindowTitle())); connect(desktopViewProxy, SIGNAL(documentSaved()), q, SLOT(enableAltSaveAction())); } void notifySlateModeChange(); void notifyDockingModeChange(); bool queryClose(); void altSaveQuery(); }; MainWindow::MainWindow(QStringList fileNames, QWidget* parent, Qt::WindowFlags flags ) : QMainWindow( parent, flags ), d( new Private(this) ) { qmlRegisterUncreatableType("org.calligra", 1, 0, "PropertyContainer", "Contains properties and naively extends QML to support dynamic properties"); qmlRegisterType("org.calligra", 1, 0, "Theme"); qmlRegisterType("org.calligra", 1, 0, "DocumentListModel"); qmlRegisterType("org.calligra", 1, 0, "SimpleTouchArea"); qmlRegisterType("org.calligra", 1, 0, "ToolManager"); qmlRegisterType("org.calligra", 1, 0, "ParagraphStylesModel"); qmlRegisterType("org.calligra", 1, 0, "KeyboardModel"); qmlRegisterType("org.calligra", 1, 0, "ScribbleArea"); qmlRegisterType("org.calligra", 1, 0, "RecentFilesModel"); qmlRegisterType("org.calligra", 1, 0, "TemplatesModel"); qmlRegisterType("org.calligra", 1, 0, "CloudAccountsModel"); qmlRegisterType(); qApp->setActiveWindow( this ); setWindowTitle(i18n("Calligra Gemini")); setWindowIcon(koIcon("calligragemini"));//gemini")); foreach(const QString &fileName, fileNames) { DocumentManager::instance()->recentFileManager()->addRecent( QDir::current().absoluteFilePath( fileName ) ); } connect(DocumentManager::instance(), SIGNAL(documentChanged()), SLOT(documentChanged())); connect(DocumentManager::instance(), SIGNAL(documentChanged()), SLOT(resetWindowTitle())); connect(DocumentManager::instance(), SIGNAL(documentSaved()), SLOT(resetWindowTitle())); connect(DocumentManager::instance(), SIGNAL(documentSaved()), SLOT(enableAltSaveAction())); d->initTouchView(this); // Set the initial view to touch... because reasons. // Really, this allows us to show the pleasant welcome screen from Touch switchToTouch(); if(!fileNames.isEmpty()) { //It feels a little hacky, but call a QML function to open files. //This saves a lot of hassle required to change state for loading dialogs etc. QMetaObject::invokeMethod(d->touchView->rootObject(), "openFile", Q_ARG(QVariant, fileNames.at(0))); } } void MainWindow::resetWindowTitle() { QUrl url(DocumentManager::instance()->settingsManager()->currentFile()); QString fileName = url.fileName(); if(url.scheme() == "temp") fileName = i18n("Untitled"); KoDialog::CaptionFlags flags = KoDialog::HIGCompliantCaption; KoDocument* document = DocumentManager::instance()->document(); if (document && document->isModified() ) { flags |= KoDialog::ModifiedCaption; } setWindowTitle( KoDialog::makeStandardCaption(fileName, this, flags) ); } void MainWindow::switchDesktopForced() { if (d->slateMode) d->forceDesktop = true; d->forceTouch = false; } void MainWindow::switchTouchForced() { if (!d->slateMode) d->forceTouch = true; d->forceDesktop = false; } void MainWindow::switchToTouch() { QTime timer; timer.start(); if (d->toTouch) { d->toTouch->setEnabled(false); d->switcher->setEnabled(false); } d->syncObject = new ViewModeSynchronisationObject; KoView* view = 0; if (d->desktopView && centralWidget() == d->desktopView) { view = d->desktopView->rootView(); //Notify the view we are switching away from that we are about to switch away from it //giving it the possibility to set up the synchronisation object. ViewModeSwitchEvent aboutToSwitchEvent(ViewModeSwitchEvent::AboutToSwitchViewModeEvent, view, d->touchView, d->syncObject); QApplication::sendEvent(view, &aboutToSwitchEvent); d->desktopView->setParent(0); } QWidget* container = QWidget::createWindowContainer(d->touchView); d->touchWidget = new QWidget(); d->touchWidget->setLayout(new QVBoxLayout()); d->touchWidget->layout()->setContentsMargins(0,0,0,0); d->touchWidget->layout()->setSpacing(0); d->touchWidget->layout()->addWidget(container); setCentralWidget(d->touchWidget); qApp->processEvents(); d->touchView->setVisible(true); resize(size()); emit switchedToTouch(); if (d->slateMode) { if (d->syncObject->initialized) QTimer::singleShot(50, this, SLOT(touchChange())); } else QTimer::singleShot(50, this, SLOT(touchChange())); //qDebug() << "milliseconds to switch to touch:" << timer.elapsed(); } void MainWindow::touchChange() { if (centralWidget() != d->touchWidget || !d->syncObject) return; if (d->desktopView) { //if (/*!d->touchKoView ||*/ !d->touchView->canvasWidget()) //{ // QTimer::singleShot(100, this, SLOT(touchChange())); // return; //} qApp->processEvents(); KoView* view = d->desktopView->rootView(); //Notify the new view that we just switched to it, passing our synchronisation object //so it can use those values to sync with the old view. ViewModeSwitchEvent switchedEvent(ViewModeSwitchEvent::SwitchedToTouchModeEvent, view, d->touchView, d->syncObject); QApplication::sendEvent(d->touchEventReceiver, &switchedEvent); d->syncObject = 0; qApp->processEvents(); } if (d->toDesktop) { qApp->processEvents(); d->toDesktop->setEnabled(true); } } void MainWindow::switchToDesktop() { QTime timer; timer.start(); if (d->toDesktop) d->toDesktop->setEnabled(false); ViewModeSynchronisationObject* syncObject = new ViewModeSynchronisationObject; KoView* view = 0; if (d->desktopView) { view = d->desktopView->rootView(); } //Notify the view we are switching away from that we are about to switch away from it //giving it the possibility to set up the synchronisation object. ViewModeSwitchEvent aboutToSwitchEvent(ViewModeSwitchEvent::AboutToSwitchViewModeEvent, d->touchView, view, syncObject); QApplication::sendEvent(d->touchEventReceiver, &aboutToSwitchEvent); qApp->processEvents(); if (d->currentTouchPage == "MainPage") { d->touchWidget->setParent(0); d->touchView->setParent(0); d->touchView->setVisible(false); setCentralWidget(d->desktopView); if(d->touchWidget) { d->touchWidget = 0; } } if (view) { //Notify the new view that we just switched to it, passing our synchronisation object //so it can use those values to sync with the old view. ViewModeSwitchEvent switchedEvent(ViewModeSwitchEvent::SwitchedToDesktopModeEvent, d->touchView, view, syncObject); QApplication::sendEvent(view, &switchedEvent); } qApp->processEvents(); d->toTouch->setEnabled(true); d->switcher->setEnabled(true); //qDebug() << "milliseconds to switch to desktop:" << timer.elapsed(); } void MainWindow::setDocAndPart(QObject* document, QObject* part) { if(DocumentManager::instance()->document()) { disconnect(DocumentManager::instance()->document(), SIGNAL(modified(bool)), this, SLOT(resetWindowTitle())); } DocumentManager::instance()->setDocAndPart(qobject_cast(document), qobject_cast(part)); d->touchEventReceiver = d->touchView->rootObject()->findChild("controllerItem"); if(DocumentManager::instance()->document()) { connect(DocumentManager::instance()->document(), SIGNAL(modified(bool)), this, SLOT(resetWindowTitle())); } if(document && part && !d->settings->currentFile().isEmpty()) { QAction* undo = qobject_cast(part)->views().at(0)->action("edit_undo"); d->touchView->rootContext()->setContextProperty("undoaction", undo); QAction* redo = qobject_cast(part)->views().at(0)->action("edit_redo"); d->touchView->rootContext()->setContextProperty("redoaction", redo); } } void MainWindow::documentChanged() { if (d->desktopView) { d->desktopView->deleteLater(); d->desktopView = 0; qApp->processEvents(); } d->initDesktopView(); if(d->desktopView) { d->desktopView->setRootDocument(DocumentManager::instance()->document(), DocumentManager::instance()->part(), false); qApp->processEvents(); d->desktopKoView = d->desktopView->rootView(); emit desktopKoViewChanged(); // d->desktopKoView->setQtMainWindow(d->desktopView); // connect(d->desktopKoView, SIGNAL(sigLoadingFinished()), d->centerer, SLOT(start())); // connect(d->desktopKoView, SIGNAL(sigSavingFinished()), this, SLOT(resetWindowTitle())); // KWView* wordsview = qobject_cast(d->desktopView->rootView()); // if(wordsview) { // connect(wordsview->canvasBase()->resourceManager(), SIGNAL(canvasResourceChanged(int, const QVariant&)), // this, SLOT(resourceChanged(int, const QVariant&))); // } if (!d->forceTouch && !d->slateMode) switchToDesktop(); } } bool MainWindow::allowClose() const { return d->allowClose; } void MainWindow::setAllowClose(bool allow) { d->allowClose = allow; } bool MainWindow::slateMode() const { return d->slateMode; } QString MainWindow::currentTouchPage() const { return d->currentTouchPage; } void MainWindow::setCurrentTouchPage(QString newPage) { d->currentTouchPage = newPage; emit currentTouchPageChanged(); if (newPage == "MainPage") { if (!d->forceTouch && !d->slateMode) { // Just loaded to desktop, do nothing } else { //QTimer::singleShot(3000, this, SLOT(adjustZoomOnDocumentChangedAndStuff())); } } } void MainWindow::setAlternativeSaveAction(QAction* altAction) { // if mainwindow exists, and alt action exists, remove alt action from current mainwindow if(d->desktopView && d->alternativeSaveAction) { d->desktopView->actionCollection()->removeAction(d->alternativeSaveAction); d->desktopView->actionCollection()->action("file_save")->disconnect(d->alternativeSaveAction); } d->alternativeSaveAction = altAction; // if mainwindow exists, set alt action into current mainwindow if(d->desktopView && d->alternativeSaveAction) { QAction* cloudSave = d->desktopView->actionCollection()->addAction("cloud_save", d->alternativeSaveAction); KToolBar* tb = d->desktopView->toolBar("mainToolBar"); if(tb) { tb->removeAction(cloudSave); // find the action /after/ the save action (because we want the alt save there, not before it) QAction* saveAction = d->desktopView->actionCollection()->action("file_save"); QAction* afterSave = 0; bool useNext = false; Q_FOREACH(QAction* action, tb->actions()) { if(useNext) { afterSave = action; break; } if(action == saveAction) { useNext = true; } } if(afterSave) { tb->insertAction(afterSave, cloudSave); } else { tb->addAction(cloudSave); } } } if(d->alternativeSaveAction) { // disabled for a start - this is called on load completion, so let's just assume we're not ready to reupload yet d->alternativeSaveAction->setEnabled(false); } } void MainWindow::enableAltSaveAction() { if(d->alternativeSaveAction) { d->alternativeSaveAction->setEnabled(true); } } void MainWindow::openFile() { QStringList mimeFilter; KoDocumentEntry entry = KoDocumentEntry::queryByMimeType(WORDS_MIME_TYPE); if (!entry.isEmpty()) { QJsonObject json = entry.metaData(); +#ifdef CALLIGRA_OLD_PLUGIN_METADATA QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); +#else + QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); +#endif mimeFilter << KoFilterManager::mimeFilter(WORDS_MIME_TYPE, KoFilterManager::Import, mimeTypes); } entry = KoDocumentEntry::queryByMimeType(STAGE_MIME_TYPE); if (!entry.isEmpty()) { QJsonObject json = entry.metaData(); +#ifdef CALLIGRA_OLD_PLUGIN_METADATA QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); +#else + QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); +#endif mimeFilter << KoFilterManager::mimeFilter(STAGE_MIME_TYPE, KoFilterManager::Import, mimeTypes); } KoFileDialog dialog(d->desktopView, KoFileDialog::OpenFile, "OpenDocument"); dialog.setCaption(i18n("Open Document")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); dialog.setMimeTypeFilters(mimeFilter); QString filename = dialog.filename(); if(!filename.isEmpty()) { QMetaObject::invokeMethod(d->touchView->rootObject(), "openFile", Q_ARG(QVariant, filename)); } } bool MainWindow::temporaryFile() const { return d->temporaryFile; } void MainWindow::setTemporaryFile(bool newValue) { d->temporaryFile = newValue; emit temporaryFileChanged(); } bool MainWindow::fullScreen() const { return Qt::WindowFullScreen == (windowState() & Qt::WindowFullScreen); } void MainWindow::setFullScreen(bool newValue) { if(newValue) { if(d->fullScreenThrottle->isActive()) { // not a good thing... you need to avoid this happening. This exists to avoid a death-loop, // such as what might happen if readermode is enabled when the window is not maximised // as this causes a resize loop which makes readermode switch between enabled and disabled, // which in turn makes fullScreen be set and reset all the time... very bad, so let's try // and avoid that. } else { setWindowState(windowState() | Qt::WindowFullScreen); } } else { // this is really unpleasant... however, fullscreen is very twitchy, and exiting it as below // will cause an inconsistent state, so we simply assume exiting fullscreen leaves you maximised. // It isn't optimal, but it is the best state for now, this has taken too long to work out. // setWindowState(windowState() & ~Qt::WindowFullScreen); // should really do it, but... it doesn't. So, we end up with what we have next: showMaximized(); } d->fullScreenThrottle->start(); emit fullScreenChanged(); } QObject* MainWindow::desktopKoView() const { return d->desktopKoView; } int MainWindow::lastScreen() const { QDesktopWidget desktop; return desktop.screenCount() - 1; } void MainWindow::resourceChanged(int key, const QVariant& v) { Q_UNUSED(key) Q_UNUSED(v) if(centralWidget() == d->touchWidget) return; } void MainWindow::resourceChangedTouch(int key, const QVariant& v) { Q_UNUSED(key) Q_UNUSED(v) if(centralWidget() == d->desktopView) return; } void MainWindow::minimize() { setWindowState(windowState() ^ Qt::WindowMinimized); } void MainWindow::closeWindow() { d->desktopView->setNoCleanup(true); //For some reason, close() does not work even if setAllowClose(true) was called just before this method. //So instead just completely quit the application, since we are using a single window anyway. DocumentManager::instance()->closeDocument(); DocumentManager::instance()->part()->deleteLater(); qApp->processEvents(); QApplication::instance()->quit(); } bool MainWindow::Private::queryClose() { desktopView->setNoCleanup(true); if (DocumentManager::instance()->document() == 0) return true; // main doc + internally stored child documents if (DocumentManager::instance()->document()->isModified()) { QString name; if (DocumentManager::instance()->document()->documentInfo()) { name = DocumentManager::instance()->document()->documentInfo()->aboutInfo("title"); } if (name.isEmpty()) name = DocumentManager::instance()->document()->url().fileName(); if (name.isEmpty()) name = i18n("Untitled"); int res = KMessageBox::warningYesNoCancel(q, i18n("

The document '%1' has been modified.

Do you want to save it?

", name), QString(), KStandardGuiItem::save(), KStandardGuiItem::discard()); switch (res) { case KMessageBox::Yes : { if (DocumentManager::instance()->isTemporaryFile() && !desktopViewProxy->fileSaveAs()) return false; if (!DocumentManager::instance()->save()) return false; break; } case KMessageBox::No : DocumentManager::instance()->document()->removeAutoSaveFiles(); DocumentManager::instance()->document()->setModified(false); // Now when queryClose() is called by closeEvent it won't do anything. break; default : // case KMessageBox::Cancel : return false; } } return true; } void MainWindow::Private::altSaveQuery() { qApp->processEvents(); if(alternativeSaveAction && alternativeSaveAction->isEnabled()) { int res = KMessageBox::warningYesNo(q, i18n("

The cloud copy of the document is out of date. Do you want to upload a new copy?

")); switch (res) { case KMessageBox::Yes : { alternativeSaveAction->trigger(); while(alternativeSaveAction->isEnabled()) { qApp->processEvents(); } break; } case KMessageBox::No : default: break; } } } void MainWindow::closeEvent(QCloseEvent* event) { if (centralWidget() == d->desktopView) { if (DocumentManager::instance()->document()->isLoading()) { event->ignore(); return; } d->allowClose = d->queryClose(); } if (d->allowClose) { d->altSaveQuery(); d->settings->setCurrentFile(""); qApp->processEvents(); if (d->desktopView) { d->desktopView->setNoCleanup(true); } event->accept(); } else { event->ignore(); emit closeRequested(); } } MainWindow::~MainWindow() { delete d; } #ifdef Q_OS_WIN bool MainWindow::winEvent( MSG * message, long * result ) { if (message && message->message == WM_SETTINGCHANGE && message->lParam) { if (wcscmp(TEXT("ConvertibleSlateMode"), (TCHAR *) message->lParam) == 0) d->notifySlateModeChange(); else if (wcscmp(TEXT("SystemDockMode"), (TCHAR *) message->lParam) == 0) d->notifyDockingModeChange(); *result = 0; return true; } return false; } #endif void MainWindow::Private::notifySlateModeChange() { #ifdef Q_OS_WIN bool bSlateMode = (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0); if (slateMode != bSlateMode) { slateMode = bSlateMode; emit q->slateModeChanged(); if (forceTouch || (slateMode && !forceDesktop)) { if (!toTouch || (toTouch && toTouch->isEnabled())) q->switchToTouch(); } else { q->switchToDesktop(); } //qDebug() << "Slate mode is now" << slateMode; } #endif } void MainWindow::Private::notifyDockingModeChange() { #ifdef Q_OS_WIN bool bDocked = (GetSystemMetrics(SM_SYSTEMDOCKED) != 0); if (docked != bDocked) { docked = bDocked; //qDebug() << "Docking mode is now" << docked; } #endif } diff --git a/gemini/desktopviewproxy.cpp b/gemini/desktopviewproxy.cpp index af3fe362d27..4a78440e439 100644 --- a/gemini/desktopviewproxy.cpp +++ b/gemini/desktopviewproxy.cpp @@ -1,180 +1,185 @@ /* * * Copyright 2013 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "desktopviewproxy.h" #include #include #include #include #include #include #include #include #include #include #include #include #include +#include // CALLIGRA_OLD_PLUGIN_METADATA #include "MainWindow.h" #include #include #include #include #include class DesktopViewProxy::Private { public: Private(MainWindow* mainWindow, KoMainWindow* desktopView) : mainWindow(mainWindow) , desktopView(desktopView) , isImporting(false) {} MainWindow* mainWindow; KoMainWindow* desktopView; bool isImporting; }; DesktopViewProxy::DesktopViewProxy(MainWindow* mainWindow, KoMainWindow* parent) : QObject(parent) , d(new Private(mainWindow, parent)) { Q_ASSERT(parent); // "There MUST be a KoMainWindow assigned, otherwise everything will blow up"); // Hide this one... as it doesn't work at all well and release happens :P QAction* closeAction = d->desktopView->actionCollection()->action("file_close"); closeAction->setVisible(false); // Concept is simple - simply steal all the actions we require to work differently, and reconnect them to local functions QAction* newAction = d->desktopView->actionCollection()->action("file_new"); newAction->disconnect(d->desktopView); connect(newAction, SIGNAL(triggered(bool)), this, SLOT(fileNew())); QAction* openAction = d->desktopView->actionCollection()->action("file_open"); openAction->disconnect(d->desktopView); connect(openAction, SIGNAL(triggered(bool)), this, SLOT(fileOpen())); QAction* saveAction = d->desktopView->actionCollection()->action("file_save"); saveAction->disconnect(d->desktopView); connect(saveAction, SIGNAL(triggered(bool)), this, SLOT(fileSave())); QAction* saveasAction = d->desktopView->actionCollection()->action("file_save_as"); saveasAction->disconnect(d->desktopView); connect(saveasAction, SIGNAL(triggered(bool)), this, SLOT(fileSaveAs())); QAction* reloadAction = d->desktopView->actionCollection()->action("file_reload_file"); reloadAction->disconnect(d->desktopView); connect(reloadAction, SIGNAL(triggered(bool)), this, SLOT(reload())); QAction* loadExistingAsNewAction = d->desktopView->actionCollection()->action("file_import_file"); loadExistingAsNewAction->disconnect(d->desktopView); connect(loadExistingAsNewAction, SIGNAL(triggered(bool)), this, SLOT(loadExistingAsNew())); // Recent files need a touch more work, as they aren't simply an action. KRecentFilesAction* recent = qobject_cast(d->desktopView->actionCollection()->action("file_open_recent")); recent->disconnect(d->desktopView); connect(recent, SIGNAL(urlSelected(QUrl)), this, SLOT(slotFileOpenRecent(QUrl))); recent->clear(); recent->loadEntries(KSharedConfig::openConfig()->group("RecentFiles")); connect(d->desktopView, SIGNAL(documentSaved()), this, SIGNAL(documentSaved())); } DesktopViewProxy::~DesktopViewProxy() { delete d; } void DesktopViewProxy::fileNew() { QProcess::startDetached(qApp->applicationFilePath(), QStringList(), QDir::currentPath()); } void DesktopViewProxy::fileOpen() { QStringList mimeFilter; KoDocumentEntry entry = KoDocumentEntry::queryByMimeType(DocumentManager::instance()->settingsManager()->currentFileClass().toLatin1()); if (!entry.isEmpty()) { QJsonObject json = entry.metaData(); +#ifdef CALLIGRA_OLD_PLUGIN_METADATA QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); +#else + QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); +#endif mimeFilter << KoFilterManager::mimeFilter(DocumentManager::instance()->settingsManager()->currentFileClass().toLatin1(), KoFilterManager::Import, mimeTypes); } KoFileDialog dialog(d->desktopView, KoFileDialog::OpenFile, "OpenDocument"); dialog.setCaption(i18n("Open Document")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); dialog.setMimeTypeFilters(mimeFilter); QString filename = dialog.filename(); if (filename.isEmpty()) return; DocumentManager::instance()->recentFileManager()->addRecent(filename); QProcess::startDetached(qApp->applicationFilePath(), QStringList() << filename, QDir::currentPath()); } void DesktopViewProxy::fileSave() { if(DocumentManager::instance()->isTemporaryFile()) { if(d->desktopView->saveDocument(true)) { DocumentManager::instance()->recentFileManager()->addRecent(DocumentManager::instance()->document()->url().toLocalFile()); DocumentManager::instance()->settingsManager()->setCurrentFile(DocumentManager::instance()->document()->url().toLocalFile()); DocumentManager::instance()->setTemporaryFile(false); emit documentSaved(); } } else { DocumentManager::instance()->save(); emit documentSaved(); } } bool DesktopViewProxy::fileSaveAs() { if(d->desktopView->saveDocument(true)) { DocumentManager::instance()->recentFileManager()->addRecent(DocumentManager::instance()->document()->url().toLocalFile()); DocumentManager::instance()->settingsManager()->setCurrentFile(DocumentManager::instance()->document()->url().toLocalFile()); DocumentManager::instance()->setTemporaryFile(false); emit documentSaved(); return true; } DocumentManager::instance()->settingsManager()->setCurrentFile(DocumentManager::instance()->document()->url().toLocalFile()); return false; } void DesktopViewProxy::reload() { DocumentManager::instance()->reload(); } void DesktopViewProxy::loadExistingAsNew() { d->isImporting = true; fileOpen(); d->isImporting = false; } void DesktopViewProxy::slotFileOpenRecent(const QUrl& url) { QProcess::startDetached(qApp->applicationFilePath(), QStringList() << url.toLocalFile(), QDir::currentPath()); } diff --git a/libs/main/KoApplication.cpp b/libs/main/KoApplication.cpp index b90c6cc5ce6..caf184be330 100644 --- a/libs/main/KoApplication.cpp +++ b/libs/main/KoApplication.cpp @@ -1,647 +1,651 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2009 Thomas Zander Copyright (C) 2012 Boudewijn Rempt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoApplication.h" #include "KoGlobal.h" #ifndef QT_NO_DBUS #include "KoApplicationAdaptor.h" #include #include #include #endif #include "KoPrintJob.h" #include "KoDocumentEntry.h" #include "KoDocument.h" #include "KoMainWindow.h" #include "KoAutoSaveRecoveryDialog.h" #include #include "KoPart.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 #ifdef Q_OS_WIN #include #include #endif #include KoApplication* KoApplication::KoApp = 0; namespace { const QTime appStartTime(QTime::currentTime()); } class KoApplicationPrivate { public: KoApplicationPrivate() : splashScreen(0) {} QByteArray nativeMimeType; QWidget *splashScreen; QList partList; }; class KoApplication::ResetStarting { public: ResetStarting(QWidget *splash = 0) : m_splash(splash) { } ~ResetStarting() { if (m_splash) { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false); if (hideSplash) { m_splash->hide(); } else { m_splash->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); QRect r(QPoint(), m_splash->size()); m_splash->move(QApplication::desktop()->screenGeometry().center() - r.center()); m_splash->setWindowTitle(qAppName()); foreach(QObject *o, m_splash->children()) { QWidget *w = qobject_cast(o); if (w && w->isHidden()) { w->setVisible(true); } } m_splash->show(); } } } QWidget *m_splash; }; KoApplication::KoApplication(const QByteArray &nativeMimeType, const QString &windowIconName, const KAboutData &aboutData, int &argc, char **argv) : QApplication(argc, argv) , d(new KoApplicationPrivate()) { KAboutData::setApplicationData( aboutData ); setWindowIcon(QIcon::fromTheme(windowIconName)); KoApplication::KoApp = this; d->nativeMimeType = nativeMimeType; // Tell the iconloader about share/apps/calligra/icons KIconLoader::global()->addAppDir("calligra"); // Initialize all Calligra directories etc. KoGlobal::initialize(); #ifndef QT_NO_DBUS KDBusService service(KDBusService::Multiple); new KoApplicationAdaptor(this); QDBusConnection::sessionBus().registerObject("/application", this); #endif #ifdef Q_OS_MACX if ( QSysInfo::MacintoshVersion > QSysInfo::MV_10_8 ) { // fix Mac OS X 10.9 (mavericks) font issue // https://bugreports.qt-project.org/browse/QTBUG-32789 QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande"); } setAttribute(Qt::AA_DontShowIconsInMenus, true); #endif } #if defined(Q_OS_WIN) && defined(ENV32BIT) typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process; BOOL isWow64() { BOOL bIsWow64 = FALSE; //IsWow64Process is not available on all supported versions of Windows. //Use GetModuleHandle to get a handle to the DLL that contains the function //and GetProcAddress to get a pointer to the function if available. fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress( GetModuleHandle(TEXT("kernel32")),"IsWow64Process"); if(0 != fnIsWow64Process) { if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64)) { //handle error } } return bIsWow64; } #endif bool KoApplication::start() { KAboutData aboutData = KAboutData::applicationData(); // process commandline parameters QCommandLineParser parser; aboutData.setupCommandLine(&parser); parser.addHelpOption(); parser.addVersionOption(); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("print"), i18n("Only print and exit"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("template"), i18n("Open a new document with a template"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("dpi"), i18n("Override display DPI"), QStringLiteral("dpiX,dpiY"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("export-pdf"), i18n("Only export to PDF and exit"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("export-filename"), i18n("Filename for export-pdf"), QStringLiteral("filename"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("benchmark-loading"), i18n("just load the file and then exit"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("benchmark-loading-show-window"), i18n("load the file, show the window and progressbar and then exit"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("profile-filename"), i18n("Filename to write profiling information into."), QStringLiteral("filename"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("roundtrip-filename"), i18n("Load a file and save it as an ODF file. Meant for debugging."), QStringLiteral("filename"))); parser.addPositionalArgument(QStringLiteral("[file(s)]"), i18n("File(s) or URL(s) to open")); parser.process(*this); aboutData.processCommandLine(&parser); #if defined(Q_OS_WIN) || defined (Q_OS_MACX) #ifdef ENV32BIT if (isWow64()) { KMessageBox::information(0, i18n("You are running a 32 bits build on a 64 bits Windows.\n" "This is not recommended.\n" "Please download and install the x64 build instead."), qApp->applicationName(), "calligra_32_on_64_warning"); } #endif QDir appdir(applicationDirPath()); appdir.cdUp(); QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); if (!env.contains("XDG_DATA_DIRS")) { qputenv("XDG_DATA_DIRS", QFile::encodeName(appdir.absolutePath() + "/share")); } qputenv("PATH", QFile::encodeName(appdir.absolutePath() + "/bin" + ";" + appdir.absolutePath() + "/lib" + ";" + appdir.absolutePath() + "/lib/kde4" + ";" + appdir.absolutePath() + "/Frameworks" + ";" + appdir.absolutePath())); #endif if (d->splashScreen) { d->splashScreen->show(); d->splashScreen->repaint(); processEvents(); } ResetStarting resetStarting(d->splashScreen); // remove the splash when done Q_UNUSED(resetStarting); // Find the part component file corresponding to the application instance name KoDocumentEntry entry; QList pluginLoaders = KoPluginLoader::pluginLoaders("calligra/parts", d->nativeMimeType); Q_FOREACH (QPluginLoader *loader, pluginLoaders) { if (loader->fileName().contains(applicationName()+QString("part"))) { entry = KoDocumentEntry(loader); pluginLoaders.removeOne(loader); break; } } qDeleteAll(pluginLoaders); if (entry.isEmpty()) { QMessageBox::critical(0, i18n("%1: Critical Error", applicationName()), i18n("Essential application components could not be found.\n" "This might be an installation issue.\n" "Try restarting or reinstalling.")); return false; } // Get the command line arguments which we have to parse QString dpiValues = parser.value("dpi"); if (!dpiValues.isEmpty()) { int sep = dpiValues.indexOf(QRegExp("[x, ]")); int dpiX; int dpiY = 0; bool ok = true; if (sep != -1) { dpiY = dpiValues.mid(sep + 1).toInt(&ok); dpiValues.truncate(sep); } if (ok) { dpiX = dpiValues.toInt(&ok); if (ok) { if (!dpiY) dpiY = dpiX; KoDpi::setDPI(dpiX, dpiY); } } } // No argument -> create an empty document const QStringList fileUrls = parser.positionalArguments(); if (fileUrls.isEmpty()) { // if there's no document, add the current working directory // to the recent dirs so the open dialog and open pane show // the directory from where the app was started, instead of // the last directory from where we opened a file KRecentDirs::add(":OpenDialog", QDir::currentPath()); QString errorMsg; KoPart *part = entry.createKoPart(&errorMsg); d->partList << part; if (!part) { if (!errorMsg.isEmpty()) KMessageBox::error(0, errorMsg); return false; } // XXX: the document should be separate plugin KoDocument *doc = part->document(); KoMainWindow *mainWindow = part->createMainWindow(); mainWindow->show(); QObject::connect(doc, SIGNAL(sigProgress(int)), mainWindow, SLOT(slotProgress(int))); // for initDoc to fill in the recent docs list // and for KoDocument::slotStarted part->addMainWindow(mainWindow); // Check for autosave files from a previous run. There can be several, and // we want to offer a restore for every one. Including a nice thumbnail! QStringList autoSaveFiles; // get all possible autosave files in the home dir, this is for unsaved document autosave files // Using the extension allows to avoid relying on the mime magic when opening QMimeType mimeType = QMimeDatabase().mimeTypeForName(doc->nativeFormatMimeType()); if (!mimeType.isValid()) { qFatal("It seems your installation is broken/incomplete because we failed to load the native mimetype \"%s\".", doc->nativeFormatMimeType().constData()); } const QString extension = mimeType.preferredSuffix(); QStringList filters; filters << QString(".%1-%2-%3-autosave%4").arg(part->componentData().componentName()).arg("*").arg("*").arg(extension); #ifdef Q_OS_WIN QDir autosaveDir = QDir::tempPath(); #else QDir autosaveDir = QDir::home(); #endif // all autosave files for our application autoSaveFiles = autosaveDir.entryList(filters, QDir::Files | QDir::Hidden); QStringList pids; QString ourPid; ourPid.setNum(applicationPid()); #ifndef QT_NO_DBUS // all running instances of our application -- bit hackish, but we cannot get at the dbus name here, for some reason QDBusReply reply = QDBusConnection::sessionBus().interface()->registeredServiceNames(); foreach (const QString &name, reply.value()) { if (name.contains(part->componentData().componentName())) { // we got another instance of ourselves running, let's get the pid QString pid = name.split('-').last(); if (pid != ourPid) { pids << pid; } } } #endif // remove the autosave files that are saved for other, open instances of ourselves foreach(const QString &autoSaveFileName, autoSaveFiles) { if (!QFile::exists(autosaveDir.absolutePath() + QDir::separator() + autoSaveFileName)) { autoSaveFiles.removeAll(autoSaveFileName); continue; } QStringList split = autoSaveFileName.split('-'); if (split.size() == 4) { if (pids.contains(split[1])) { // We've got an active, owned autosave file. Remove. autoSaveFiles.removeAll(autoSaveFileName); } } } // Allow the user to make their selection if (autoSaveFiles.size() > 0) { KoAutoSaveRecoveryDialog dlg(autoSaveFiles); if (dlg.exec() == QDialog::Accepted) { QStringList filesToRecover = dlg.recoverableFiles(); foreach (const QString &autoSaveFileName, autoSaveFiles) { if (!filesToRecover.contains(autoSaveFileName)) { // remove the files the user didn't want to recover QFile::remove(autosaveDir.absolutePath() + QDir::separator() + autoSaveFileName); } } autoSaveFiles = filesToRecover; } else { // don't recover any of the files, but don't delete them either autoSaveFiles.clear(); } } if (autoSaveFiles.size() > 0) { short int numberOfOpenDocuments = 0; // number of documents open // bah, we need to re-use the document that was already created QUrl url = QUrl::fromLocalFile(autosaveDir.absolutePath() + QDir::separator() + autoSaveFiles.takeFirst()); if (mainWindow->openDocument(part, url)) { doc->resetURL(); doc->setModified(true); // TODO: what if the app crashes immediately, before another autosave was made? better keep & rename QFile::remove(url.toLocalFile()); numberOfOpenDocuments++; } // And then for the other autosave files, we copy & paste the code // and loop through them. foreach(const QString &autoSaveFile, autoSaveFiles) { // For now create an empty document QString errorMsg; KoPart *part = entry.createKoPart(&errorMsg); d->partList << part; if (part) { url = QUrl::fromLocalFile(autosaveDir.absolutePath() + QDir::separator() + autoSaveFile); KoMainWindow *mainWindow = part->createMainWindow(); mainWindow->show(); if (mainWindow->openDocument(part, url)) { doc->resetURL(); doc->setModified(true); // TODO: what if the app crashes immediately, before another autosave was made? better keep & rename QFile::remove(url.toLocalFile()); numberOfOpenDocuments++; } } } return (numberOfOpenDocuments > 0); } else { part->showStartUpWidget(mainWindow); } } else { const bool print = parser.isSet("print"); const bool exportAsPdf = parser.isSet("export-pdf"); const QString pdfFileName = parser.value("export-filename"); const QString roundtripFileName = parser.value("roundtrip-filename"); const bool doTemplate = parser.isSet("template"); const bool benchmarkLoading = parser.isSet("benchmark-loading") || parser.isSet("benchmark-loading-show-window") || !roundtripFileName.isEmpty(); // only show the mainWindow when no command-line mode option is passed const bool showmainWindow = parser.isSet("benchmark-loading-show-window") || ( !parser.isSet("export-pdf") && !parser.isSet("benchmark-loading") && !parser.isSet("roundtrip-filename") && roundtripFileName.isEmpty()); const QString profileFileName = parser.value("profile-filename"); QTextStream profileoutput; QFile profileFile(profileFileName); if (!profileFileName.isEmpty() && profileFile.open(QFile::WriteOnly | QFile::Truncate)) { profileoutput.setDevice(&profileFile); } // Loop through arguments short int numberOfOpenDocuments = 0; // number of documents open short int nPrinted = 0; // TODO: remove once Qt has proper handling itself const QRegExp withProtocolChecker( QStringLiteral("^[a-zA-Z]+:") ); for (int argNumber = 0; argNumber < fileUrls.size(); ++argNumber) { const QString fileUrl = fileUrls.at(argNumber); // convert to an url const bool startsWithProtocol = (withProtocolChecker.indexIn(fileUrl) == 0); const QUrl url = startsWithProtocol ? QUrl::fromUserInput(fileUrl) : QUrl::fromLocalFile(QDir::current().absoluteFilePath(fileUrl)); // For now create an empty document QString errorMsg; KoPart *part = entry.createKoPart(&errorMsg); d->partList << part; if (part) { KoDocument *doc = part->document(); // show a mainWindow asap KoMainWindow *mainWindow = part->createMainWindow(); if (showmainWindow) { mainWindow->show(); } if (benchmarkLoading) { doc->setReadWrite(false); } if (profileoutput.device()) { doc->setProfileStream(&profileoutput); profileoutput << "KoApplication::start\t" << appStartTime.msecsTo(QTime::currentTime()) <<"\t0" << endl; doc->setAutoErrorHandlingEnabled(false); } doc->setProfileReferenceTime(appStartTime); // are we just trying to open a template? if (doTemplate) { QString templatePath; if (url.isLocalFile() && QFile::exists(url.toLocalFile())) { templatePath = url.toLocalFile(); debugMain << "using full path..."; } else { QString desktopName(fileUrls.at(argNumber)); const QString templatesResourcePath = part->templatesResourcePath(); QStringList paths = KoResourcePaths::findAllResources("data", templatesResourcePath + "*/" + desktopName); if (paths.isEmpty()) { paths = KoResourcePaths::findAllResources("data", templatesResourcePath + desktopName); } if (paths.isEmpty()) { KMessageBox::error(0, i18n("No template found for: %1", desktopName)); delete mainWindow; } else if (paths.count() > 1) { KMessageBox::error(0, i18n("Too many templates found for: %1", desktopName)); delete mainWindow; } else { templatePath = paths.at(0); } } if (!templatePath.isEmpty()) { QUrl templateBase; templateBase.setPath(templatePath); KDesktopFile templateInfo(templatePath); QString templateName = templateInfo.readUrl(); QUrl templateURL; templateURL.setPath(templateBase.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() + '/' + templateName); if (mainWindow->openDocument(part, templateURL)) { doc->resetURL(); doc->setEmpty(); doc->setTitleModified(); debugMain << "Template loaded..."; numberOfOpenDocuments++; } else { KMessageBox::error(0, i18n("Template %1 failed to load.", templateURL.toDisplayString())); delete mainWindow; } } // now try to load } else if (mainWindow->openDocument(part, url)) { if (benchmarkLoading) { if (profileoutput.device()) { profileoutput << "KoApplication::start\t" << appStartTime.msecsTo(QTime::currentTime()) <<"\t100" << endl; } if (!roundtripFileName.isEmpty()) { part->document()->saveAs(QUrl("file:"+roundtripFileName)); } // close the document mainWindow->slotFileQuit(); return true; // only load one document! } else if (print) { mainWindow->slotFilePrint(); // delete mainWindow; done by ~KoDocument nPrinted++; } else if (exportAsPdf) { KoPrintJob *job = mainWindow->exportToPdf(pdfFileName); if (job) connect (job, SIGNAL(destroyed(QObject*)), mainWindow, SLOT(slotFileQuit()), Qt::QueuedConnection); nPrinted++; } else { // Normal case, success numberOfOpenDocuments++; } } else { // .... if failed // delete doc; done by openDocument // delete mainWindow; done by ~KoDocument } if (profileoutput.device()) { profileoutput << "KoApplication::start\t" << appStartTime.msecsTo(QTime::currentTime()) <<"\t100" << endl; } } } if (benchmarkLoading) { return false; // no valid urls found. } if (print || exportAsPdf) return nPrinted > 0; if (numberOfOpenDocuments == 0) // no doc, e.g. all URLs were malformed return false; } // not calling this before since the program will quit there. return true; } KoApplication::~KoApplication() { delete d; } void KoApplication::setSplashScreen(QWidget *splashScreen) { d->splashScreen = splashScreen; } QList KoApplication::partList() const { return d->partList; } QStringList KoApplication::mimeFilter(KoFilterManager::Direction direction) const { KoDocumentEntry entry = KoDocumentEntry::queryByMimeType(d->nativeMimeType); QJsonObject json = entry.metaData(); +#ifdef CALLIGRA_OLD_PLUGIN_METADATA QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); +#else + QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); +#endif return KoFilterManager::mimeFilter(d->nativeMimeType, direction, mimeTypes); } bool KoApplication::notify(QObject *receiver, QEvent *event) { try { return QApplication::notify(receiver, event); } catch (std::exception &e) { qWarning("Error %s sending event %i to object %s", e.what(), event->type(), qPrintable(receiver->objectName())); } catch (...) { qWarning("Error sending event %i to object %s", event->type(), qPrintable(receiver->objectName())); } return false; } KoApplication *KoApplication::koApplication() { return KoApp; } diff --git a/libs/main/KoDocumentEntry.cpp b/libs/main/KoDocumentEntry.cpp index 4c5b57930a7..b998a44e6a8 100644 --- a/libs/main/KoDocumentEntry.cpp +++ b/libs/main/KoDocumentEntry.cpp @@ -1,169 +1,169 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright 2007 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoDocumentEntry.h" #include "KoPart.h" #include "KoDocument.h" #include "KoFilter.h" #include #include -#include // OLD_PLUGIN_MIMETYPE_DATA +#include // CALLIGRA_OLD_PLUGIN_METADATA #include #include #include #include // UINT_MAX KoDocumentEntry::KoDocumentEntry() : m_loader(0) { } KoDocumentEntry::KoDocumentEntry(QPluginLoader *loader) : m_loader(loader) { } KoDocumentEntry::~KoDocumentEntry() { } QJsonObject KoDocumentEntry::metaData() const { return m_loader ? m_loader->metaData().value("MetaData").toObject() : QJsonObject(); } QString KoDocumentEntry::fileName() const { return m_loader ? m_loader->fileName() : QString(); } /** * @return TRUE if the service pointer is null */ bool KoDocumentEntry::isEmpty() const { return (m_loader == 0); } /** * @return name of the associated service */ QString KoDocumentEntry::name() const { QJsonObject json = metaData(); json = json.value("KPlugin").toObject(); return json.value("Name").toString(); } /** * Mimetypes (and other service types) which this document can handle. */ QStringList KoDocumentEntry::mimeTypes() const { QJsonObject json = metaData(); -#ifdef OLD_PLUGIN_MIMETYPE_DATA +#ifdef CALLIGRA_OLD_PLUGIN_METADATA return json.value("MimeType").toString().split(';', QString::SkipEmptyParts); #else QJsonObject pluginData = json.value("KPlugin").toObject(); return pluginData.value("MimeTypes").toVariant().toStringList(); #endif } /** * @return TRUE if the document can handle the requested mimetype. */ bool KoDocumentEntry::supportsMimeType(const QString & _mimetype) const { return mimeTypes().contains(_mimetype); } KoPart *KoDocumentEntry::createKoPart(QString* errorMsg) const { if (!m_loader) { return 0; } QObject *obj = m_loader->instance(); KPluginFactory *factory = qobject_cast(obj); KoPart *part = factory->create(0, QVariantList()); if (!part) { if (errorMsg) *errorMsg = m_loader->errorString(); return 0; } return part; } KoDocumentEntry KoDocumentEntry::queryByMimeType(const QString & mimetype) { QList vec = query(mimetype); if (vec.isEmpty()) { warnMain << "Got no results with " << mimetype; // Fallback to the old way (which was probably wrong, but better be safe) vec = query(mimetype); if (vec.isEmpty()) { // Still no match. Either the mimetype itself is unknown, or we have no service for it. // Help the user debugging stuff by providing some more diagnostics if (!KServiceType::serviceType(mimetype)) { errorMain << "Unknown Calligra MimeType " << mimetype << "." << endl; errorMain << "Check your installation (for instance, run 'kde4-config --path mime' and check the result)." << endl; } else { errorMain << "Found no Calligra part able to handle " << mimetype << "!" << endl; errorMain << "Check your installation (does the desktop file have X-KDE-NativeMimeType and Calligra/Part, did you install Calligra in a different prefix than KDE, without adding the prefix to /etc/kderc ?)" << endl; } return KoDocumentEntry(); } } // Filthy hack alert -- this'll be properly fixed in the mvc branch. if (qApp->applicationName() == "flow" && vec.size() == 2) { return KoDocumentEntry(vec[1]); } return KoDocumentEntry(vec[0]); } QList KoDocumentEntry::query(const QString & mimetype) { QList lst; // Query the trader const QList offers = KoPluginLoader::pluginLoaders(QStringLiteral("calligra/parts"), mimetype); foreach(QPluginLoader *pluginLoader, offers) { lst.append(KoDocumentEntry(pluginLoader)); } if (lst.count() > 1 && !mimetype.isEmpty()) { warnMain << "KoDocumentEntry::query " << mimetype << " got " << lst.count() << " offers!"; foreach(const KoDocumentEntry &entry, lst) { warnMain << entry.name(); } } return lst; } diff --git a/libs/main/KoFilterEntry.cpp b/libs/main/KoFilterEntry.cpp index 41404a01608..65a60b033c5 100644 --- a/libs/main/KoFilterEntry.cpp +++ b/libs/main/KoFilterEntry.cpp @@ -1,94 +1,103 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright 2007 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KoFilterEntry.h" #include "KoDocument.h" #include "KoFilter.h" #include #include +#include // CALLIGRA_OLD_PLUGIN_METADATA + #include #include #include // UINT_MAX KoFilterEntry::KoFilterEntry(QPluginLoader *loader) : m_loader(loader) { - import = loader->metaData().value("MetaData").toObject().value("X-KDE-Import").toString().split(','); - export_ = loader->metaData().value("MetaData").toObject().value("X-KDE-Export").toString().split(','); - int w = loader->metaData().value("MetaData").toObject().value("X-KDE-Weight").toString().toInt(); + QJsonObject metadata = loader->metaData().value("MetaData").toObject(); +#ifdef CALLIGRA_OLD_PLUGIN_METADATA + import = metadata.value("X-KDE-Import").toString().split(','); + export_ = metadata.value("X-KDE-Export").toString().split(','); + int w = metadata.value("X-KDE-Weight").toString().toInt(); +#else + import = metadata.value("X-KDE-Import").toVariant().toStringList(); + export_ = metadata.value("X-KDE-Export").toVariant().toStringList(); + int w = metadata.value("X-KDE-Weight").toInt(); +#endif weight = w < 0 ? UINT_MAX : static_cast(w); - available = loader->metaData().value("MetaData").toObject().value("X-KDE-Available").toString(); + available = metadata.value("X-KDE-Available").toString(); } KoFilterEntry::~KoFilterEntry() { delete m_loader; } QString KoFilterEntry::fileName() const { return m_loader->fileName(); } QList KoFilterEntry::query() { QList lst; QList offers = KoPluginLoader::pluginLoaders(QStringLiteral("calligra/formatfilters")); QList::ConstIterator it = offers.constBegin(); unsigned int max = offers.count(); //debugFilter <<"Query returned" << max <<" offers"; for (unsigned int i = 0; i < max; i++) { //debugFilter <<" desktopEntryPath=" << (*it)->entryPath() // << " library=" << (*it)->library() << endl; // Append converted offer lst.append(KoFilterEntry::Ptr(new KoFilterEntry(*it))); // Next service it++; } return lst; } KoFilter* KoFilterEntry::createFilter(KoFilterChain* chain, QObject* parent) { KLibFactory *factory = qobject_cast(m_loader->instance()); if (!factory) { warnMain << m_loader->errorString(); return 0; } QObject* obj = factory->create(parent); if (!obj || !obj->inherits("KoFilter")) { delete obj; return 0; } KoFilter* filter = static_cast(obj); filter->m_chain = chain; return filter; } diff --git a/libs/main/KoFilterGraph.cpp b/libs/main/KoFilterGraph.cpp index c8b54a0a32b..88b604db66d 100644 --- a/libs/main/KoFilterGraph.cpp +++ b/libs/main/KoFilterGraph.cpp @@ -1,247 +1,260 @@ /* This file is part of the Calligra libraries Copyright (C) 2001 Werner Trobin 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 "KoFilterGraph.h" #include "KoFilterManager.h" // KoFilterManager::filterAvailable, private API #include "KoDocumentEntry.h" #include "KoFilterEntry.h" #include "KoDocument.h" +#include // CALLIGRA_OLD_PLUGIN_METADATA #include "PriorityQueue_p.h" #include "KoFilterEdge.h" #include "KoFilterChainLink.h" #include "KoFilterVertex.h" #include #include #include #include // UINT_MAX namespace CalligraFilter { Graph::Graph(const QByteArray& from) : m_from(from) , m_graphValid(false) , d(0) { buildGraph(); shortestPaths(); // Will return after a single lookup if "from" is invalid (->no check here) } Graph::~Graph() { foreach(Vertex* vertex, m_vertices) { delete vertex; } m_vertices.clear(); } void Graph::setSourceMimeType(const QByteArray& from) { if (from == m_from) return; m_from = from; m_graphValid = false; // Initialize with "infinity" ... foreach(Vertex* vertex, m_vertices) { vertex->reset(); } // ...and re-run the shortest path search for the new source mime shortestPaths(); } KoFilterChain::Ptr Graph::chain(const KoFilterManager* manager, QByteArray& to) const { if (!isValid() || !manager) return KoFilterChain::Ptr(); if (to.isEmpty()) { // if the destination is empty we search the closest Calligra part to = findCalligraPart(); if (to.isEmpty()) // still empty? strange stuff... return KoFilterChain::Ptr(); } const Vertex* vertex = m_vertices.value(to); if (!vertex || vertex->key() == UINT_MAX) return KoFilterChain::Ptr(); KoFilterChain::Ptr ret(new KoFilterChain(manager)); // Fill the filter chain with all filters on the path const Vertex* tmp = vertex->predecessor(); while (tmp) { const Edge* const edge = tmp->findEdge(vertex); Q_ASSERT(edge); ret->prependChainLink(edge->filterEntry(), tmp->mimeType(), vertex->mimeType()); vertex = tmp; tmp = tmp->predecessor(); } return ret; } void Graph::dump() const { #ifndef NDEBUG debugFilter << "+++++++++ Graph::dump +++++++++"; debugFilter << "From:" << m_from; foreach(Vertex *vertex, m_vertices) { vertex->dump(" "); } debugFilter << "+++++++++ Graph::dump (done) +++++++++"; #endif } // Query the trader and create the vertices and edges representing // available mime types and filters. void Graph::buildGraph() { // Make sure that all available parts are added to the graph const QList parts(KoDocumentEntry::query()); foreach(const KoDocumentEntry& part, parts) { QJsonObject metaData = part.metaData(); +#ifdef CALLIGRA_OLD_PLUGIN_METADATA QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); +#else + QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); +#endif nativeMimeTypes += metaData.value("X-KDE-NativeMimeType").toString(); foreach(const QString& nativeMimeType, nativeMimeTypes) { const QByteArray key = nativeMimeType.toLatin1(); if (!m_vertices.contains(key)) m_vertices[key] = new Vertex(key); } } // no constraint here - we want *all* :) const QList filters(KoFilterEntry::query()); foreach(KoFilterEntry::Ptr filter, filters) { // First add the "starting points" to the dict foreach (const QString& import, filter->import) { const QByteArray key = import.toLatin1(); // latin1 is okay here (werner) // already there? if (!m_vertices.contains(key)) m_vertices.insert(key, new Vertex(key)); } // Are we allowed to use this filter at all? if (KoFilterManager::filterAvailable(filter)) { foreach(const QString& exportIt, filter->export_) { // First make sure the export vertex is in place const QByteArray key = exportIt.toLatin1(); // latin1 is okay here Vertex* exp = m_vertices.value(key); if (!exp) { exp = new Vertex(key); m_vertices.insert(key, exp); } // Then create the appropriate edges foreach(const QString& import, filter->import) { m_vertices[import.toLatin1()]->addEdge(new Edge(exp, filter)); } } } else debugFilter << "Filter:" << filter->fileName() << " doesn't apply."; } } // As all edges (=filters) are required to have a positive weight // we can use Dijkstra's shortest path algorithm from Cormen's // "Introduction to Algorithms" (p. 527) // Note: I did some adaptions as our data structures are slightly // different from the ones used in the book. Further we simply stop // the algorithm is we don't find any node with a weight != Infinity // (==UINT_MAX), as this means that the remaining nodes in the queue // aren't connected anyway. void Graph::shortestPaths() { // Is the requested start mime type valid? Vertex* from = m_vertices.value(m_from); if (!from) return; // Inititalize start vertex from->setKey(0); // Fill the priority queue with all the vertices PriorityQueue queue(m_vertices); while (!queue.isEmpty()) { Vertex *min = queue.extractMinimum(); // Did we already relax all connected vertices? if (min->key() == UINT_MAX) break; min->relaxVertices(queue); } m_graphValid = true; } QByteArray Graph::findCalligraPart() const { // Here we simply try to find the closest Calligra mimetype const QList parts(KoDocumentEntry::query()); QList::ConstIterator partIt(parts.constBegin()); QList::ConstIterator partEnd(parts.constEnd()); const Vertex *v = 0; // Be sure that v gets initialized correctly while (!v && partIt != partEnd) { QJsonObject metaData = (*partIt).metaData(); +#ifdef CALLIGRA_OLD_PLUGIN_METADATA QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); +#else + QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); +#endif nativeMimeTypes += metaData.value("X-KDE-NativeMimeType").toString(); QStringList::ConstIterator it = nativeMimeTypes.constBegin(); QStringList::ConstIterator end = nativeMimeTypes.constEnd(); for (; !v && it != end; ++it) if (!(*it).isEmpty()) v = m_vertices.value((*it).toLatin1()); ++partIt; } if (!v) return ""; // Now we try to find the "cheapest" Calligra vertex while (partIt != partEnd) { QJsonObject metaData = (*partIt).metaData(); +#ifdef CALLIGRA_OLD_PLUGIN_METADATA QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); +#else + QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); +#endif nativeMimeTypes += metaData.value("X-KDE-NativeMimeType").toString(); QStringList::ConstIterator it = nativeMimeTypes.constBegin(); QStringList::ConstIterator end = nativeMimeTypes.constEnd(); for (; !v && it != end; ++it) { QString key = *it; if (!key.isEmpty()) { Vertex* tmp = m_vertices.value(key.toLatin1()); if (!v || (tmp && tmp->key() < v->key())) v = tmp; } } ++partIt; } // It seems it already is a Calligra part if (v->key() == 0) return ""; return v->mimeType(); } } diff --git a/libs/main/KoFilterManager.cpp b/libs/main/KoFilterManager.cpp index 5e981113c8f..d6f1639cdc7 100644 --- a/libs/main/KoFilterManager.cpp +++ b/libs/main/KoFilterManager.cpp @@ -1,553 +1,562 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis 2000, 2001 Werner Trobin Copyright (C) 2004 Nicolas Goutte Copyright (C) 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 "KoFilterManager.h" #include "KoFilterManager_p.h" #include "KoDocument.h" #include "KoDocumentEntry.h" #include "KoProgressUpdater.h" +#include // CALLIGRA_OLD_PLUGIN_METADATA #include #include #include #include #include #include #include #include #include #include #include #include #include // static cache for filter availability QMap KoFilterManager::m_filterAvailable; KoFilterManager::KoFilterManager(KoDocument* document, KoProgressUpdater* progressUpdater) : m_document(document), m_parentChain(0), m_graph(""), d(new Private(progressUpdater)) { d->batch = false; } KoFilterManager::KoFilterManager(const QString& url, const QByteArray& mimetypeHint, KoFilterChain* const parentChain) : m_document(0), m_parentChain(parentChain), m_importUrl(url), m_importUrlMimetypeHint(mimetypeHint), m_graph(""), d(new Private) { d->batch = false; } KoFilterManager::KoFilterManager(const QByteArray& mimeType) : m_document(0), m_parentChain(0), m_graph(""), d(new Private) { d->batch = false; d->importMimeType = mimeType; } KoFilterManager::~KoFilterManager() { delete d; } QString KoFilterManager::importDocument(const QString& url, const QString& documentMimeType, KoFilter::ConversionStatus& status) { // Find the mime type for the file to be imported. QString typeName(documentMimeType); QUrl u(url); if (documentMimeType.isEmpty()) { QMimeType t = QMimeDatabase().mimeTypeForUrl(u); if (t.isValid()) { typeName = t.name(); } } m_graph.setSourceMimeType(typeName.toLatin1()); // .latin1() is okay here (Werner) if (!m_graph.isValid()) { bool userCancelled = false; warnFilter << "Can't open " << typeName << ", trying filter chooser"; if (m_document) { if (!m_document->isAutoErrorHandlingEnabled()) { status = KoFilter::BadConversionGraph; return QString(); } QByteArray nativeFormat = m_document->nativeFormatMimeType(); QApplication::setOverrideCursor(Qt::ArrowCursor); KoFilterChooser chooser(0, KoFilterManager::mimeFilter(nativeFormat, KoFilterManager::Import, m_document->extraNativeMimeTypes()), nativeFormat, u); if (chooser.exec()) { QByteArray f = chooser.filterSelected().toLatin1(); if (f == nativeFormat) { status = KoFilter::OK; QApplication::restoreOverrideCursor(); return url; } m_graph.setSourceMimeType(f); } else userCancelled = true; QApplication::restoreOverrideCursor(); } if (!m_graph.isValid()) { errorFilter << "Couldn't create a valid graph for this source mimetype: " << typeName; importErrorHelper(typeName, userCancelled); status = KoFilter::BadConversionGraph; return QString(); } } KoFilterChain::Ptr chain(0); // Are we owned by a KoDocument? if (m_document) { QByteArray mimeType = m_document->nativeFormatMimeType(); QStringList extraMimes = m_document->extraNativeMimeTypes(); int i = 0; int n = extraMimes.count(); chain = m_graph.chain(this, mimeType); while (i < n) { QByteArray extraMime = extraMimes[i].toUtf8(); // TODO check if its the same target mime then continue KoFilterChain::Ptr newChain(0); newChain = m_graph.chain(this, extraMime); if (!chain || (newChain && newChain->weight() < chain->weight())) chain = newChain; ++i; } } else if (!d->importMimeType.isEmpty()) { chain = m_graph.chain(this, d->importMimeType); } else { errorFilter << "You aren't supposed to use import() from a filter!" << endl; status = KoFilter::UsageError; return QString(); } if (!chain) { errorFilter << "Couldn't create a valid filter chain!" << endl; importErrorHelper(typeName); status = KoFilter::BadConversionGraph; return QString(); } // Okay, let's invoke the filters one after the other m_direction = Import; // vital information! m_importUrl = url; // We want to load that file m_exportUrl.clear(); // This is null for sure, as embedded stuff isn't // allowed to use that method status = chain->invokeChain(); m_importUrl.clear(); // Reset the import URL if (status == KoFilter::OK) return chain->chainOutput(); return QString(); } KoFilter::ConversionStatus KoFilterManager::exportDocument(const QString& url, QByteArray& mimeType) { bool userCancelled = false; // The import url should already be set correctly (null if we have a KoDocument // file manager and to the correct URL if we have an embedded manager) m_direction = Export; // vital information! m_exportUrl = url; KoFilterChain::Ptr chain; if (m_document) { // We have to pick the right native mimetype as source. QStringList nativeMimeTypes; nativeMimeTypes.append(m_document->nativeFormatMimeType()); nativeMimeTypes += m_document->extraNativeMimeTypes(); QStringList::ConstIterator it = nativeMimeTypes.constBegin(); const QStringList::ConstIterator end = nativeMimeTypes.constEnd(); for (; !chain && it != end; ++it) { m_graph.setSourceMimeType((*it).toLatin1()); if (m_graph.isValid()) chain = m_graph.chain(this, mimeType); } } else if (!m_importUrlMimetypeHint.isEmpty()) { debugFilter << "Using the mimetype hint:" << m_importUrlMimetypeHint; m_graph.setSourceMimeType(m_importUrlMimetypeHint); } else { QUrl u; u.setPath(m_importUrl); QMimeType t = QMimeDatabase().mimeTypeForUrl(u); if (!t.isValid() || t.isDefault()) { errorFilter << "No mimetype found for" << m_importUrl; return KoFilter::BadMimeType; } m_graph.setSourceMimeType(t.name().toLatin1()); if (!m_graph.isValid()) { warnFilter << "Can't open" << t.name() << ", trying filter chooser"; QApplication::setOverrideCursor(Qt::ArrowCursor); KoFilterChooser chooser(0, KoFilterManager::mimeFilter(), QString(), u); if (chooser.exec()) m_graph.setSourceMimeType(chooser.filterSelected().toLatin1()); else userCancelled = true; QApplication::restoreOverrideCursor(); } } if (!m_graph.isValid()) { errorFilter << "Couldn't create a valid graph for this source mimetype."; if (!d->batch && !userCancelled) KMessageBox::error(0, i18n("Could not export file."), i18n("Missing Export Filter")); return KoFilter::BadConversionGraph; } if (!chain) // already set when coming from the m_document case chain = m_graph.chain(this, mimeType); if (!chain) { errorFilter << "Couldn't create a valid filter chain to " << mimeType << " !" << endl; if (!d->batch) KMessageBox::error(0, i18n("Could not export file."), i18n("Missing Export Filter")); return KoFilter::BadConversionGraph; } return chain->invokeChain(); } namespace // in order not to mess with the global namespace ;) { // This class is needed only for the static mimeFilter method class Vertex { public: Vertex(const QByteArray& mimeType) : m_color(White), m_mimeType(mimeType) {} enum Color { White, Gray, Black }; Color color() const { return m_color; } void setColor(Color color) { m_color = color; } QByteArray mimeType() const { return m_mimeType; } void addEdge(Vertex* vertex) { if (vertex) m_edges.append(vertex); } QList edges() const { return m_edges; } private: Color m_color; QByteArray m_mimeType; QList m_edges; }; // Some helper methods for the static stuff // This method builds up the graph in the passed ascii dict void buildGraph(QHash& vertices, KoFilterManager::Direction direction) { QStringList stopList; // Lists of mimetypes that are considered end of chains stopList << "text/plain"; stopList << "text/csv"; stopList << "text/x-tex"; stopList << "text/html"; // partly copied from build graph, but I don't see any other // way without crude hacks, as we have to obey the direction here QList parts(KoDocumentEntry::query(QString())); QList::ConstIterator partIt(parts.constBegin()); QList::ConstIterator partEnd(parts.constEnd()); while (partIt != partEnd) { QJsonObject metaData = (*partIt).metaData(); +#ifdef CALLIGRA_OLD_PLUGIN_METADATA QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); +#else + QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); +#endif nativeMimeTypes += metaData.value("X-KDE-NativeMimeType").toString(); QStringList::ConstIterator it = nativeMimeTypes.constBegin(); const QStringList::ConstIterator end = nativeMimeTypes.constEnd(); for (; it != end; ++it) if (!(*it).isEmpty()) { const QByteArray key = (*it).toLatin1(); if (!vertices.contains(key)) vertices.insert(key, new Vertex(key)); } ++partIt; } QList filters = KoFilterEntry::query(); // no constraint here - we want *all* :) QList::ConstIterator it = filters.constBegin(); QList::ConstIterator end = filters.constEnd(); foreach(KoFilterEntry::Ptr filterEntry, filters) for (; it != end; ++it) { QStringList impList; // Import list QStringList expList; // Export list // Now we have to exclude the "stop" mimetypes (in the right direction!) if (direction == KoFilterManager::Import) { // Import: "stop" mime type should not appear in export foreach(const QString & testIt, (*it)->export_) { if (!stopList.contains(testIt)) expList.append(testIt); } impList = (*it)->import; } else { // Export: "stop" mime type should not appear in import foreach(const QString & testIt, (*it)->import) { if (!stopList.contains(testIt)) impList.append(testIt); } expList = (*it)->export_; } if (impList.empty() || expList.empty()) { // This filter cannot be used under these conditions debugFilter << "Filter:" << (*it)->fileName() << " ruled out"; continue; } // First add the "starting points" to the dict QStringList::ConstIterator importIt = impList.constBegin(); const QStringList::ConstIterator importEnd = impList.constEnd(); for (; importIt != importEnd; ++importIt) { const QByteArray key = (*importIt).toLatin1(); // latin1 is okay here (werner) // already there? if (!vertices[ key ]) vertices.insert(key, new Vertex(key)); } // Are we allowed to use this filter at all? if (KoFilterManager::filterAvailable(*it)) { QStringList::ConstIterator exportIt = expList.constBegin(); const QStringList::ConstIterator exportEnd = expList.constEnd(); for (; exportIt != exportEnd; ++exportIt) { // First make sure the export vertex is in place const QByteArray key = (*exportIt).toLatin1(); // latin1 is okay here Vertex* exp = vertices[ key ]; if (!exp) { exp = new Vertex(key); vertices.insert(key, exp); } // Then create the appropriate edges depending on the // direction (import/export) // This is the chunk of code which actually differs from the // graph stuff (apart from the different vertex class) importIt = impList.constBegin(); // ### TODO: why only the first one? if (direction == KoFilterManager::Import) { for (; importIt != importEnd; ++importIt) exp->addEdge(vertices[(*importIt).toLatin1()]); } else { for (; importIt != importEnd; ++importIt) vertices[(*importIt).toLatin1()]->addEdge(exp); } } } else { debugFilter << "Filter:" << (*it)->fileName() << " does not apply."; } } } // This method runs a BFS on the graph to determine the connected // nodes. Make sure that the graph is "cleared" (the colors of the // nodes are all white) QStringList connected(const QHash& vertices, const QByteArray& mimetype) { if (mimetype.isEmpty()) return QStringList(); Vertex *v = vertices[ mimetype ]; if (!v) return QStringList(); v->setColor(Vertex::Gray); std::queue queue; queue.push(v); QStringList connected; while (!queue.empty()) { v = queue.front(); queue.pop(); QList edges = v->edges(); foreach(Vertex* current, edges) { if (current->color() == Vertex::White) { current->setColor(Vertex::Gray); queue.push(current); } } v->setColor(Vertex::Black); connected.append(v->mimeType()); } return connected; } } // anon namespace // The static method to figure out to which parts of the // graph this mimetype has a connection to. QStringList KoFilterManager::mimeFilter(const QByteArray &mimetype, Direction direction, const QStringList &extraNativeMimeTypes) { //debugFilter <<"mimetype=" << mimetype <<" extraNativeMimeTypes=" << extraNativeMimeTypes; QHash vertices; buildGraph(vertices, direction); // TODO maybe use the fake vertex trick from the method below, to make the search faster? QStringList nativeMimeTypes; nativeMimeTypes.append(QString::fromLatin1(mimetype)); nativeMimeTypes += extraNativeMimeTypes; // Add the native mimetypes first so that they are on top. QStringList lst = nativeMimeTypes; // Now look for filters which output each of those natives mimetypes foreach(const QString &natit, nativeMimeTypes) { const QStringList outMimes = connected(vertices, natit.toLatin1()); //debugFilter <<"output formats connected to mime" << natit <<" :" << outMimes; foreach(const QString &mit, outMimes) { if (!lst.contains(mit)) // append only if not there already. Qt4: QSet? lst.append(mit); } } foreach(Vertex* vertex, vertices) { delete vertex; } vertices.clear(); return lst; } QStringList KoFilterManager::mimeFilter() { QHash vertices; buildGraph(vertices, KoFilterManager::Import); QList parts(KoDocumentEntry::query( QString())); QList::ConstIterator partIt(parts.constBegin()); QList::ConstIterator partEnd(parts.constEnd()); if (partIt == partEnd) return QStringList(); // To find *all* reachable mimetypes, we have to resort to // a small hat trick, in order to avoid multiple searches: // We introduce a fake vertex, which is connected to every // single Calligra mimetype. Due to that one BFS is enough :) // Now we just need an... ehrm.. unique name for our fake mimetype Vertex *v = new Vertex("supercalifragilistic/x-pialadocious"); vertices.insert("supercalifragilistic/x-pialadocious", v); while (partIt != partEnd) { QJsonObject metaData = (*partIt).metaData(); +#ifdef CALLIGRA_OLD_PLUGIN_METADATA QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); +#else + QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); +#endif nativeMimeTypes += metaData.value("X-KDE-NativeMimeType").toString(); QStringList::ConstIterator it = nativeMimeTypes.constBegin(); const QStringList::ConstIterator end = nativeMimeTypes.constEnd(); for (; it != end; ++it) if (!(*it).isEmpty()) v->addEdge(vertices[(*it).toLatin1()]); ++partIt; } QStringList result = connected(vertices, "supercalifragilistic/x-pialadocious"); // Finally we have to get rid of our fake mimetype again result.removeAll("supercalifragilistic/x-pialadocious"); return result; } // Here we check whether the filter is available. This stuff is quite slow, // but I don't see any other convenient (for the user) way out :} bool KoFilterManager::filterAvailable(KoFilterEntry::Ptr entry) { if (!entry) return false; if (entry->available != "check") return true; // QT5TODO #if 1 return true; #else //kDebug( 30500 ) <<"Checking whether" << entry->service()->name() <<" applies."; // generate some "unique" key QString key = entry->service()->name() + " - " + entry->service()->library(); if (!m_filterAvailable.contains(key)) { //kDebug( 30500 ) <<"Not cached, checking..."; KLibrary library(QFile::encodeName(entry->service()->library())); if (library.fileName().isEmpty()) { warnFilter << "Huh?? Couldn't load the lib: " << entry->service()->library(); m_filterAvailable[ key ] = false; return false; } // This code is "borrowed" from klibloader ;) QByteArray symname = "check_" + QString(library.objectName()).toLatin1(); KLibrary::void_function_ptr sym = library.resolveFunction(symname); if (!sym) { // warnFilter << "The library " << library.objectName() // << " does not offer a check_" << library.objectName() // << " function." << endl; m_filterAvailable[key] = false; } else { typedef int (*t_func)(); t_func check = (t_func)sym; m_filterAvailable[ key ] = check() == 1; } } return m_filterAvailable[key]; #endif } void KoFilterManager::importErrorHelper(const QString& mimeType, const bool suppressDialog) { QString tmp = i18n("Could not import file of type\n%1", mimeType); // ###### FIXME: use KLibLoader::lastErrorMessage() here if (!suppressDialog) KMessageBox::error(0, tmp, i18n("Missing Import Filter")); } void KoFilterManager::setBatchMode(const bool batch) { d->batch = batch; } bool KoFilterManager::getBatchMode(void) const { return d->batch; } KoProgressUpdater* KoFilterManager::progressUpdater() const { if (d->progressUpdater.isNull()) { // somebody, probably its parent, deleted our progress updater for us return 0; } return d->progressUpdater.data(); } diff --git a/libs/plugin/KoPluginLoader.cpp b/libs/plugin/KoPluginLoader.cpp index dfa1263abc7..78d3515ec41 100644 --- a/libs/plugin/KoPluginLoader.cpp +++ b/libs/plugin/KoPluginLoader.cpp @@ -1,205 +1,208 @@ /* This file is part of the KDE project * Copyright (c) 2006 Boudewijn Rempt * Copyright (c) 2007 Thomas Zander * Copyright (c) 2016 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 "KoPluginLoader.h" +#include // CALLIGRA_OLD_PLUGIN_METADATA + #include #include #include #include #include #include #include #include #include #include const QLoggingCategory &PLUGIN_LOG() { static const QLoggingCategory category("calligra.lib.plugin"); return category; } #define debugPlugin qCDebug(PLUGIN_LOG) #define warnPlugin qCWarning(PLUGIN_LOG) class KoPluginLoaderImpl : public QObject { public: QStringList loadedDirectories; }; Q_GLOBAL_STATIC(KoPluginLoaderImpl, pluginLoaderInstance) void KoPluginLoader::load(const QString & directory, const PluginsConfig &config, QObject* owner) { // Don't load the same plugins again if (pluginLoaderInstance->loadedDirectories.contains(directory)) { return; } pluginLoaderInstance->loadedDirectories << directory; QList offers = KoPluginLoader::pluginLoaders(directory); QList plugins; bool configChanged = false; QList blacklist; // what we will save out afterwards if (config.whiteList && config.blacklist && config.group) { debugPlugin << "Loading" << directory << "with checking the config"; KConfigGroup configGroup(KSharedConfig::openConfig(), config.group); QList whiteList = configGroup.readEntry(config.whiteList, config.defaults); QList knownList; // if there was no list of defaults; all plugins are loaded. const bool firstStart = !config.defaults.isEmpty() && !configGroup.hasKey(config.whiteList); knownList = configGroup.readEntry(config.blacklist, knownList); if (firstStart) { configChanged = true; } foreach(QPluginLoader *loader, offers) { QJsonObject json = loader->metaData().value("MetaData").toObject(); json = json.value("KPlugin").toObject(); const QString pluginName = json.value("Id").toString(); if (pluginName.isEmpty()) { warnPlugin << "Loading plugin" << loader->fileName() << "failed, has no X-KDE-PluginInfo-Name."; continue; } if (whiteList.contains(pluginName)) { plugins.append(loader); } else if (!firstStart && !knownList.contains(pluginName)) { // also load newly installed plugins. plugins.append(loader); configChanged = true; } else { blacklist << pluginName; } } } else { plugins = offers; } QMap serviceNames; foreach(QPluginLoader *loader, plugins) { if (serviceNames.contains(loader->fileName())) { // duplicate QJsonObject json2 = loader->metaData().value("MetaData").toObject(); QVariant pluginVersion2 = json2.value("X-Flake-PluginVersion").toVariant(); if (pluginVersion2.isNull()) { // just take the first one found... continue; } QPluginLoader *currentLoader = serviceNames.value(loader->fileName()); QJsonObject json = currentLoader->metaData().value("MetaData").toObject(); QVariant pluginVersion = json.value("X-Flake-PluginVersion").toVariant(); if (!(pluginVersion.isNull() || pluginVersion.toInt() < pluginVersion2.toInt())) { continue; // replace the old one with this one, since its newer. } } serviceNames.insert(loader->fileName(), loader); } QList whiteList; foreach(QPluginLoader *loader, serviceNames) { KPluginFactory *factory = qobject_cast(loader->instance()); QObject *plugin = factory->create(owner ? owner : pluginLoaderInstance, QVariantList()); if (plugin) { QJsonObject json = loader->metaData().value("MetaData").toObject(); json = json.value("KPlugin").toObject(); const QString pluginName = json.value("Id").toString(); whiteList << pluginName; debugPlugin << "Loaded plugin" << loader->fileName() << owner; if (!owner) { delete plugin; } } else { warnPlugin << "Loading plugin" << loader->fileName() << "failed, " << loader->errorString(); } } if (configChanged && config.whiteList && config.blacklist && config.group) { KConfigGroup configGroup(KSharedConfig::openConfig(), config.group); configGroup.writeEntry(config.whiteList, whiteList); configGroup.writeEntry(config.blacklist, blacklist); } qDeleteAll(offers); } QList KoPluginLoader::instantiatePluginFactories(const QString & directory) { QList pluginFactories; const QList offers = KoPluginLoader::pluginLoaders(directory); foreach(QPluginLoader *pluginLoader, offers) { QObject* pluginInstance = pluginLoader->instance(); if (!pluginInstance) { warnPlugin << "Loading plugin" << pluginLoader->fileName() << "failed, " << pluginLoader->errorString(); continue; } KPluginFactory *factory = qobject_cast(pluginInstance); if (factory == 0) { warnPlugin << "Expected a KPluginFactory, got a" << pluginInstance->metaObject()->className(); delete pluginInstance; continue; } pluginFactories.append(factory); } qDeleteAll(offers); return pluginFactories; } QList KoPluginLoader::pluginLoaders(const QString &directory, const QString &mimeType) { QListlist; KPluginLoader::forEachPlugin(directory, [&](const QString &pluginPath) { debugPlugin << "Trying to load" << pluginPath; QPluginLoader *loader = new QPluginLoader(pluginPath); QJsonObject metaData = loader->metaData().value("MetaData").toObject(); if (metaData.isEmpty()) { debugPlugin << pluginPath << "has no MetaData!"; return; } if (!mimeType.isEmpty()) { -#ifdef OLD_PLUGIN_MIMETYPE_DATA +#ifdef CALLIGRA_OLD_PLUGIN_METADATA QStringList mimeTypes = metaData.value("MimeType").toString().split(';'); + mimeTypes += metaData.value("X-KDE-ExtraNativeMimeTypes").toString().split(QLatin1Char(',')); #else QJsonObject pluginData = metaData.value("KPlugin").toObject(); QStringList mimeTypes = pluginData.value("MimeTypes").toVariant().toStringList(); + mimeTypes += metaData.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); #endif - mimeTypes += metaData.value("X-KDE-ExtraNativeMimeTypes").toString().split(QLatin1Char(',')); mimeTypes += metaData.value("X-KDE-NativeMimeType").toString(); if (! mimeTypes.contains(mimeType)) { return; } } list.append(loader); }); return list; }