diff --git a/CMakeLists.txt b/CMakeLists.txt index c4fe580b1..0768bfda6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,247 +1,246 @@ cmake_minimum_required(VERSION 3.0 FATAL_ERROR) find_package(ECM 1.8.0 REQUIRED NOMODULE) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(SetKexiCMakePolicies NO_POLICY_SCOPE) include(SetKexiVersionInfo) project(Kexi VERSION ${PROJECT_VERSION}) include(ECMInstallIcons) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMAddTests) include(ECMMarkAsTest) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(ECMPoQmTools) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) include(KexiMacros) include(KexiAddIconsRccFile) ensure_out_of_source_build("Please refer to the build instruction https://community.kde.org/Kexi/Building") get_git_revision_and_branch() detect_release_build() -set_coinstallable_project_base_name() ####################### ######################## ## Productset setting ## ######################## ####################### # For predefined productsets see the definitions in KexiProducts.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(KexiProducts.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(KEXI_SHOULD_BUILD_STAGING FALSE) else () set(KEXI_SHOULD_BUILD_STAGING TRUE) endif () # finally choose products/features to build calligra_set_productset(${PRODUCTSET}) ########################## ########################### ## Look for Qt, KF5 ## ########################### ########################## set(REQUIRED_KF5_VERSION 5.16.0) find_package(KF5 ${REQUIRED_KF5_VERSION} REQUIRED COMPONENTS Archive Codecs Completion Config ConfigWidgets CoreAddons GuiAddons I18n IconThemes ItemViews KIO TextEditor TextWidgets WidgetsAddons XmlGui ) find_package(KF5 ${REQUIRED_KF5_VERSION} QUIET OPTIONAL_COMPONENTS Crash) macro_bool_to_01(KF5Crash_FOUND HAVE_KCRASH) macro_log_feature(${KF5Crash_FOUND} "KCrash" "KDE's Crash Handler" "https://api.kde.org/frameworks/kcrash/html" FALSE "" "Optionally used to provide crash reporting on Linux") set(REQUIRED_QT_VERSION 5.4.0) find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS Core Gui Widgets Xml Network PrintSupport Test) find_package(Qt5 ${REQUIRED_QT_VERSION} COMPONENTS UiTools WebKit WebKitWidgets) # use sane compile flags add_definitions( -DQT_NO_CAST_TO_ASCII -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_NO_URL_CAST_FROM_STRING -DQT_STRICT_ITERATORS -DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS -DQT_USE_QSTRINGBUILDER ) # only with COMPILING_TESTS definition will all the FOO_TEST_EXPORT macros 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) macro_bool_to_01(BUILD_TESTING COMPILING_TESTS) # overcome some platform incompatibilities if(WIN32) find_package(KDEWin REQUIRED) endif() # set custom Kexi plugin installdir -set(KEXI_PLUGIN_INSTALL_DIR ${PLUGIN_INSTALL_DIR}/${PROJECT_NAME_LOWER}${PROJECT_STABLE_VERSION_MAJOR}) +set(KEXI_PLUGIN_INSTALL_DIR ${PLUGIN_INSTALL_DIR}/${KEXI_BASE_PATH}) # TEMPORARY: for initial Qt5/KF5 build porting phase deprecation warnings are only annoying noise # remove once code porting phase starts, perhaps first locally in product subdirs #if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUC) # add_definitions(-Wno-deprecated -Wno-deprecated-declarations) #endif () ########################### ############################ ## Required dependencies ## ############################ ########################### set(KEXI_LIBS_MIN_VERSION 3.0.90) ## ## Test for KDb ## option(KEXI_DEBUG_GUI "Debugging GUI for Kexi (requires KDB_DEBUG_GUI to be set too)" OFF) if(KEXI_DEBUG_GUI) set(KDB_REQUIRED_COMPONENTS DEBUG_GUI) endif() find_package(KDb ${KEXI_LIBS_MIN_VERSION} REQUIRED COMPONENTS ${KDB_REQUIRED_COMPONENTS}) macro_log_feature(KDb_FOUND "KDb" "A database connectivity and creation framework" "http://community.kde.org/KDb" FALSE "" "Required by Kexi for data handling") ## ## Test for KReport ## find_package(KReport ${KEXI_LIBS_MIN_VERSION} REQUIRED) if (KReport_FOUND) if(NOT KREPORT_SCRIPTING) message(FATAL_ERROR "Kexi requires KReport package with scripting support enabled (KREPORT_SCRIPTING)") endif() endif() ## ## Test for KPropertyWidgets ## find_package(KPropertyWidgets ${KEXI_LIBS_MIN_VERSION} REQUIRED COMPONENTS KF) macro_log_feature(KPropertyWidgets_FOUND "KPropertyWidgets" "A property editing framework with editor widget" "http://community.kde.org/KProperty" FALSE "" "Required by Kexi") include(CheckIfQtGuiCanBeExecuted) include(CheckGlobalBreezeIcons) ########################### ############################ ## Optional dependencies ## ############################ ########################### ## ## Test for marble ## set(MARBLE_MIN_VERSION "0.19.2") find_package(KexiMarble) if(NOT MARBLE_FOUND) set(MARBLE_INCLUDE_DIR "") else() set(HAVE_MARBLE TRUE) endif() macro_log_feature(MARBLE_FOUND "Marble" "KDE World Globe Widget library" "https://marble.kde.org/" FALSE "${MARBLE_MIN_VERSION}" "Required by Kexi form map widget") ## ## Test for Qt WebKitWidgets ## #TODO switch to Qt WebEngine macro_bool_to_01(Qt5WebKitWidgets_FOUND HAVE_QTWEBKITWIDGETS) macro_log_feature(Qt5WebKitWidgets_FOUND "Qt WebkitWidgets" "QWidgets module for Webkit, the HTML engine." "http://qt.io" FALSE "" "Required by Kexi web form widget") ################## ################### ## Helper macros ## ################### ################## include(MacroCalligraAddBenchmark) include(MacroCalligraAddTest) ############################################# #### 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_KEXI "isn't buildable at the moment") ############################################# #### Calculate buildable products #### ############################################# calligra_drop_unbuildable_products() ############################################# #### Setup product-depending vars #### ############################################# ################### #################### ## Subdirectories ## #################### ################### add_subdirectory(src) if(SHOULD_BUILD_DOC) add_subdirectory(doc) endif() # non-app directories are moved here because they can depend on SHOULD_BUILD_{appname} variables set above add_subdirectory(cmake) if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ki18n_install(po) endif() macro_display_feature_log() calligra_product_deps_report("product_deps") calligra_log_should_build() diff --git a/cmake/modules/KexiMacros.cmake b/cmake/modules/KexiMacros.cmake index 2f1faff49..f721e473c 100644 --- a/cmake/modules/KexiMacros.cmake +++ b/cmake/modules/KexiMacros.cmake @@ -1,223 +1,243 @@ # Additional CMake macros # # Copyright (C) 2015-2016 Jarosław Staniek # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. #unused: include(KDbCreateSharedDataClasses) # from KDb include(CheckFunctionExists) include(GenerateExportHeader) include(MacroLogFeature) include(GetGitRevisionDescription) include(MacroBoolTo01) string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPER) string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER) string(COMPARE EQUAL "${CMAKE_CXX_COMPILER_ID}" "Clang" CMAKE_COMPILER_IS_CLANG) macro(ensure_out_of_source_build _extra_message) string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" _isBuildInSource) if(isBuildInSource) message(FATAL_ERROR "Compiling ${PROJECT_NAME} inside the source directory is not possible. " "${_extra_message}") endif() unset(_isBuildInSource) endmacro() # Sets RELEASE_BUILD to TRUE for release builds macro(detect_release_build) 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() unset(_RELEASE_BUILD_TYPES) unset(_CMAKE_BUILD_TYPE_TOLOWER) endif() endmacro() if(WIN32) set(LIB_INSTALL_DIR ${LIB_INSTALL_DIR} RUNTIME DESTINATION ${BIN_INSTALL_DIR} LIBRARY ${INSTALL_TARGETS_DEFAULT_ARGS} ARCHIVE ${INSTALL_TARGETS_DEFAULT_ARGS} ) set(DATA_INSTALL_DIR "$ENV{APPDATA}") STRING(REGEX REPLACE "\\\\" "/" DATA_INSTALL_DIR ${DATA_INSTALL_DIR}) # Install own icons to CMAKE_INSTALL_FULL_ICONDIR (relative to bin/data/ on Windows) on Windows. # We're consistent because icons from breeze-icons.git are installed there as well. set(ICONS_INSTALL_DIR ${CMAKE_INSTALL_FULL_ICONDIR}) else() # On other OSes install own icons in app's data dir set(ICONS_INSTALL_DIR - "${DATA_INSTALL_DIR}/${PROJECT_NAME_LOWER}${PROJECT_STABLE_VERSION_MAJOR}/icons") + "${DATA_INSTALL_DIR}/${KEXI_BASE_PATH}/icons") endif() # Adds a feature info using add_feature_info() with _NAME and _DESCRIPTION. # If _NAME is equal to _DEFAULT, shows this fact. macro(add_simple_feature_info _NAME _DESCRIPTION _DEFAULT) if("${_DEFAULT}" STREQUAL "${${_NAME}}") set(_STATUS " (default value)") else() set(_STATUS "") endif() add_feature_info(${_NAME} ${_NAME} ${_DESCRIPTION}${_STATUS}) endmacro() # Adds a simple option using option() with _NAME and _DESCRIPTION and a feature # info for it using add_simple_feature_info(). If _NAME is equal to _DEFAULT, shows this fact. macro(simple_option _NAME _DESCRIPTION _DEFAULT) option(${_NAME} ${_DESCRIPTION} ${_DEFAULT}) add_simple_feature_info(${_NAME} ${_DESCRIPTION} ${_DEFAULT}) endmacro() # Fetches git revision and branch from the source dir of the current build if possible. # Sets ${PROJECT_NAME_UPPER}_GIT_SHA1_STRING and ${PROJECT_NAME_UPPER}_GIT_BRANCH_STRING variables. # If git information is not available but ${CMAKE_SOURCE_DIR}/GIT_VERSION file exists, # it is parsed. This file can be created by scripts while preparing tarballs and is # supposed to contain two lines: hash and branch. macro(get_git_revision_and_branch) set(${PROJECT_NAME_UPPER}_GIT_SHA1_STRING "") set(${PROJECT_NAME_UPPER}_GIT_BRANCH_STRING "") get_git_head_revision(GIT_REFSPEC ${PROJECT_NAME_UPPER}_GIT_SHA1_STRING) get_git_branch(${PROJECT_NAME_UPPER}_GIT_BRANCH_STRING) if(NOT ${PROJECT_NAME_UPPER}_GIT_SHA1_STRING OR NOT ${PROJECT_NAME_UPPER}_GIT_BRANCH_STRING) if(EXISTS "${CMAKE_SOURCE_DIR}/GIT_VERSION") file(READ "${CMAKE_SOURCE_DIR}/GIT_VERSION" _ver) string(REGEX REPLACE "\n" ";" _ver "${_ver}") list(GET _ver 0 ${PROJECT_NAME_UPPER}_GIT_SHA1_STRING) list(GET _ver 1 ${PROJECT_NAME_UPPER}_GIT_BRANCH_STRING) endif() endif() if(${PROJECT_NAME_UPPER}_GIT_SHA1_STRING OR ${PROJECT_NAME_UPPER}_GIT_BRANCH_STRING) string(SUBSTRING ${${PROJECT_NAME_UPPER}_GIT_SHA1_STRING} 0 7 ${PROJECT_NAME_UPPER}_GIT_SHA1_STRING) else() set(${PROJECT_NAME_UPPER}_GIT_SHA1_STRING "") set(${PROJECT_NAME_UPPER}_GIT_BRANCH_STRING "") endif() endmacro() # Adds BUILD_TESTING option to enable all kinds of tests. If enabled, build in autotests/ # and tests/ subdirectory is enabled. IF optional argument ARG1 is ON, building tests will # be ON by default. Otherwise building tests will be OFF. ARG1 is OFF by default. # If tests are OFF, BUILD_COVERAGE is set to OFF. # If tests are on BUILD_TESTING macro is defined. macro(add_tests) if (NOT "${ARG1}" STREQUAL "ON") set(_SET OFF) endif() simple_option(BUILD_TESTING "Build tests" ${_SET}) # override default from CTest.cmake if(BUILD_TESTING) add_definitions(-DBUILD_TESTING) include(CTest) if (EXISTS ${CMAKE_SOURCE_DIR}/autotests) add_subdirectory(autotests) endif() if (EXISTS ${CMAKE_SOURCE_DIR}/tests) add_subdirectory(tests) endif() else() set(BUILD_COVERAGE OFF) simple_option(BUILD_COVERAGE "Build test coverage (disabled because BUILD_TESTING is OFF)" OFF) endif() endmacro() # Adds BUILD_EXAMPLES option to enable examples. If enabled, build in examples/ subdirectory # is enabled. If optional argument ARG1 is ON, building examples will be ON by default. # Otherwise building examples will be OFF. ARG1 is OFF by default. macro(add_examples) set(_SET ${ARGV0}) if (NOT "${_SET}" STREQUAL ON) set(_SET OFF) endif() simple_option(BUILD_EXAMPLES "Build example applications" ${_SET}) if (BUILD_EXAMPLES AND EXISTS ${CMAKE_SOURCE_DIR}/examples) add_subdirectory(examples) endif() endmacro() # Adds ${PROJECT_NAME_UPPER}_UNFINISHED option. If it is ON, unfinished features # (useful for testing but may confuse end-user) are compiled-in. # This option is OFF by default. macro(add_unfinished_features_option) simple_option(${PROJECT_NAME_UPPER}_UNFINISHED "Include unfinished features (useful for testing but may confuse end-user)" OFF) endmacro() -# Adds commands that generate ${_filename}${PROJECT_STABLE_VERSION_MAJOR}.pc file +# Adds commands that generate ${_filename}${KEXI_DISTRIBUTION_VERSION}.pc file # out of ${_filename}.pc.cmake file and installs the .pc file to ${LIB_INSTALL_DIR}/pkgconfig. # These commands are not executed for WIN32. # ${CMAKE_SOURCE_DIR}/${_filename}.pc.cmake should exist. macro(add_pc_file _filename) if (NOT WIN32) - set(_name ${_filename}${PROJECT_STABLE_VERSION_MAJOR}) + set(_name ${_filename}${KEXI_DISTRIBUTION_VERSION}) configure_file(${CMAKE_SOURCE_DIR}/${_filename}.pc.cmake ${CMAKE_BINARY_DIR}/${_name}.pc @ONLY) install(FILES ${CMAKE_BINARY_DIR}/${_name}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) endif() endmacro() # Sets detailed version information for library co-installability. # - adds PROJECT_VERSION_MAJOR to the lib name # - sets VERSION and SOVERSION to PROJECT_VERSION_MAJOR.PROJECT_VERSION_MINOR +# - sets OUTPUT_NAME to ${_target}${KEXI_DISTRIBUTION_VERSION} # - sets ${_target_upper}_BASE_NAME variable to the final lib name # - sets ${_target_upper}_BASE_NAME_LOWER variable to the final lib name, lowercase # - sets ${_target_upper}_INCLUDE_INSTALL_DIR to include dir for library headers # - (where _target_upper is uppercase ${_target} macro(set_coinstallable_lib_version _target) - set(_name ${_target}${PROJECT_STABLE_VERSION_MAJOR}) + set(_name ${_target}${KEXI_DISTRIBUTION_VERSION}) + #message(FATAL_ERROR ${PROJECT_VERSION_MAJOR}) set_target_properties(${_target} - PROPERTIES VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + PROPERTIES VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_RELEASE} SOVERSION ${PROJECT_VERSION_MAJOR} EXPORT_NAME ${_target} OUTPUT_NAME ${_name} ) string(TOUPPER ${_target} _target_upper) string(TOUPPER ${_target_upper}_BASE_NAME _var) set(${_var} ${_name}) string(TOLOWER ${_name} ${_var}_LOWER) set(${_target_upper}_INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_DIR}/${_name}) unset(_target_upper) unset(_var) endmacro() -# Sets detailed base name for project's co-installability. -# - PROJECT_BASE_NAME == "${PROJECT_NAME}${PROJECT_STABLE_VERSION_MAJOR}" -# - PROJECT_BASE_NAME_LOWER == "${PROJECT_NAME_LOWER}${PROJECT_STABLE_VERSION_MAJOR}" -macro(set_coinstallable_project_base_name) - set(PROJECT_BASE_NAME ${PROJECT_NAME}${PROJECT_STABLE_VERSION_MAJOR}) - string(TOLOWER ${PROJECT_BASE_NAME} PROJECT_BASE_NAME_LOWER) +# Combines add_library() with set_coinstallable_lib_version() +macro(kexi_add_library) + set(args ${ARGV}) + list(GET args 0 _target) + add_library(${args}) + set_coinstallable_lib_version(${_target}) + unset(_target) +endmacro() + +# Sets detailed version information for executable co-installability. +# - sets OUTPUT_NAME to ${_target}-${KEXI_DISTRIBUTION_VERSION} +macro(set_coinstallable_executable_version _target) + set_target_properties(${_target} + PROPERTIES OUTPUT_NAME ${_target}-${KEXI_DISTRIBUTION_VERSION} + ) +endmacro() + +# Combines add_executable() with set_coinstallable_executable_version() +macro(kexi_add_executable) + set(args ${ARGV}) + list(GET args 0 _target) + add_executable(${args}) + set_coinstallable_executable_version(${_target}) + unset(_target) endmacro() # Adds custom target that updates given file in the current working dir using specified # command and adds its source files to the project files. # The target is not executed by default, if dependencies to/from other targets are needed # they can be set separately using add_dependencies(). # Execution of the command shows status "Updating ". # Options: # TARGET - name of the new custom target # FILE - - name of the file that is updated using the command # COMMAND [args...] - command to be executed # SOURCES [files...] - source files that are used by the command function(add_update_file_target) set(options) set(oneValueArgs TARGET FILE) set(multiValueArgs COMMAND SOURCES) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) add_custom_target(${ARG_TARGET} COMMAND ${ARG_COMMAND} SOURCES ${ARG_SOURCES} DEPENDS ${ARG_SOURCES} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMENT "Updating ${ARG_FILE}" VERBATIM ) endfunction() add_custom_target(update_all_rcc COMMENT "Updating all file lists for rcc files" ) diff --git a/cmake/modules/SetKexiVersionInfo.cmake b/cmake/modules/SetKexiVersionInfo.cmake index afd828098..509aca5e3 100644 --- a/cmake/modules/SetKexiVersionInfo.cmake +++ b/cmake/modules/SetKexiVersionInfo.cmake @@ -1,65 +1,77 @@ # Copyright (C) 2003-2016 Jarosław Staniek # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # Define common versions of Kexi components used to generate KexiVersion.h # update these version for every release: set(PROJECT_VERSION_STRING "3.1 Alpha") # Custom name such as "3.1 Alpha" set(PROJECT_STABLE_VERSION_MAJOR 3) # 3 for 3.x, 4 for 4.x, etc. set(PROJECT_STABLE_VERSION_MINOR 1) # 0 for 3.0, 1 for 3.1, etc. set(PROJECT_VERSION_RELEASE 90) # 90 for Alpha, increase for next test releases, set 0 for first Stable, etc. set(KEXI_ALPHA 1) # uncomment only for Alpha #set(KEXI_BETA 1) # uncomment only for Beta #set(KEXI_RC 1) # uncomment only for RC set(KEXI_YEAR 2016) # update every year # -- do not edit below this line -- -if(NOT DEFINED PROJECT_ALPHA AND NOT DEFINED PROJECT_BETA AND NOT DEFINED PROJECT_RC) - set(PROJECT_STABLE 1) +set(KEXI_CUSTOM_DISTRIBUTION_VERSION "" CACHE STRING + "Custom name of Kexi version useful to construct co-installabile releases. Any nonempty directory name is accepted. If specified it will be used in KEXI_DISTRIBUTION_VERSION. If not specified, KEXI_DISTRIBUTION_VERSION will be set to PROJECT_STABLE_VERSION_MAJOR.PROJECT_STABLE_VERSION_MINOR.") + +if(KEXI_CUSTOM_DISTRIBUTION_VERSION STREQUAL "") + set(KEXI_DISTRIBUTION_VERSION "${PROJECT_STABLE_VERSION_MAJOR}.${PROJECT_STABLE_VERSION_MINOR}") +else() + set(KEXI_DISTRIBUTION_VERSION "${KEXI_CUSTOM_DISTRIBUTION_VERSION}") +endif() + +# Relative path name useful to construct co-installabile file names and paths +set(KEXI_BASE_PATH "kexi/${KEXI_DISTRIBUTION_VERSION}") + +if(NOT DEFINED KEXI_ALPHA AND NOT DEFINED KEXI_BETA AND NOT DEFINED KEXI_RC) + set(KEXI_STABLE 1) endif() # PROJECT_VERSION_MAJOR is the same as PROJECT_STABLE_VERSION_MAJOR but for unstable x.0 # x is decreased by one, e.g. 3.0 Beta is 2.99. -if(NOT DEFINED PROJECT_STABLE AND PROJECT_STABLE_VERSION_MINOR EQUAL 0) +if(NOT DEFINED KEXI_STABLE AND PROJECT_STABLE_VERSION_MINOR EQUAL 0) math(EXPR PROJECT_VERSION_MAJOR "${PROJECT_STABLE_VERSION_MAJOR} - 1") else() math(EXPR PROJECT_VERSION_MAJOR ${PROJECT_STABLE_VERSION_MAJOR}) endif() # PROJECT_VERSION_MINOR is equal to PROJECT_STABLE_VERSION_MINOR for stable releases, # equal to 99 for x.0 unstable releases (e.g. it's 3.0 Beta has minor version 99), # and equal to PROJECT_STABLE_VERSION_MINOR-1 for unstable releases other than x.0. -if(DEFINED PROJECT_STABLE) +if(DEFINED KEXI_STABLE) set(PROJECT_VERSION_MINOR ${PROJECT_STABLE_VERSION_MINOR}) elseif(PROJECT_STABLE_VERSION_MINOR EQUAL 0) set(PROJECT_VERSION_MINOR 99) else() math(EXPR PROJECT_VERSION_MINOR "${PROJECT_STABLE_VERSION_MINOR} - 1") endif() # PROJECT_STABLE_VERSION_RELEASE is equal to PROJECT_VERSION_RELEASE for stable releases # and 0 for unstable ones. -if(DEFINED PROJECT_STABLE) +if(DEFINED KEXI_STABLE) set(PROJECT_STABLE_VERSION_RELEASE ${PROJECT_VERSION_RELEASE}) else() set(PROJECT_STABLE_VERSION_RELEASE 0) endif() set(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_RELEASE}) -message(STATUS "Kexi version: ${PROJECT_VERSION_STRING} (${PROJECT_VERSION})") +message(STATUS "Kexi version \"${PROJECT_VERSION_STRING}\" (${PROJECT_VERSION}), distribution version \"${KEXI_DISTRIBUTION_VERSION}\"") # Define the generic version of the Kexi libraries here # This makes it easy to advance it when the next Kexi release comes. # 14 was the last GENERIC_PROJECT_LIB_VERSION_MAJOR of the previous Kexi series # (2.x) so we're starting with 15 in 3.x series. if(PROJECT_STABLE_VERSION_MAJOR EQUAL 3) math(EXPR GENERIC_PROJECT_LIB_VERSION_MAJOR "${PROJECT_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_PROJECT_LIB_VERSION_MAJOR to something bigger") endif() set(GENERIC_PROJECT_LIB_VERSION "${GENERIC_PROJECT_LIB_VERSION_MAJOR}.0.0") set(GENERIC_PROJECT_LIB_SOVERSION "${GENERIC_PROJECT_LIB_VERSION_MAJOR}") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9d5429b1e..d64b274ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,95 +1,95 @@ set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true) option(KEXI_MOBILE "Compile a mobile version of Kexi" OFF) check_function_exists("uname" HAVE_UNAME) option(KEXI_SHOW_UNFINISHED "Show unfinished features in Kexi. Thus is useful for testing but may confuse end-user." OFF) option(KEXI_SHOW_UNIMPLEMENTED "Forces to show menu entries and dialogs just to give impression about development plans for Kexi. Only recommended for test/development versions." OFF) # Extra GUI features option(KEXI_AUTORISE_TABBED_TOOLBAR "Experimental: Autorise the main tabbed toolbar in Kexi" OFF) # Experimental: option(KEXI_SCRIPTS_SUPPORT "Experimental: Enable scripting in Kexi" ON) # Broken: option(KEXI_FORM_CURSOR_PROPERTY_SUPPORT "Broken: Enable \"cursor\" property in the form designer" OFF) option(KEXI_SHOW_CONTEXT_HELP "Broken: Enable context help in Kexi main window" OFF) option(KEXI_QUICK_PRINTING_SUPPORT "Broken: Enable print/print preview/print setup for tables/queries in the project navigator" OFF) option(KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT "Broken: Enable \"auto field\" form widget in the form designer" OFF) # OFF because we need to replace it with QTreeWidget which uses very different API compared to Q3ListView. Re-add QTreeWidget? option(KEXI_LIST_FORM_WIDGET_SUPPORT "Broken: Enable \"list\" form widget in the form designer" OFF) option(KEXI_PIXMAP_COLLECTIONS_SUPPORT "Broken: Enable support for pixmap collections" OFF) # Not available: option(KEXI_MACROS_SUPPORT "Experimental: Enable macros in Kexi" OFF) if(KEXI_MACROS_SUPPORT) # temp. message(FATAL_ERROR "Macros are not yet available.") endif() option(KEXI_TABLE_PRINT_SUPPORT "Experimental: Enable printing of tabular view in Kexi" OFF) # broken since Kexi 2 if(KEXI_TABLE_PRINT_SUPPORT) # temp. message(FATAL_ERROR "Table printing is not yet available.") endif() option(KEXI_PROJECT_TEMPLATES "Experimental: Enable support for project templates in Kexi" OFF) # broken since Kexi 2 if(KEXI_PROJECT_TEMPLATES) # temp. message(FATAL_ERROR "Project templates are not yet available.") endif() #See commit 1e433a54cd9, left here for reference #option(KEXI_SQLITE_MIGRATION "If defined, SQLite3 migration to some newer format is possible. Users can see a suitable question on app's startup." OFF) add_definitions(-DTRANSLATION_DOMAIN=\"kexi\") #no default: add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44010) configure_file(config-kexi.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kexi.h ) configure_file(KexiVersion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KexiVersion.h) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/core ) add_subdirectory( kexiutils ) add_subdirectory( core ) add_subdirectory( widget ) add_subdirectory( data ) add_subdirectory( plugins ) if (BUILD_TESTING) #TODO KEXI3 add_subdirectory( tests ) endif() if(KEXI_MOBILE) else() add_subdirectory( main ) add_subdirectory( formeditor ) add_subdirectory( migration ) endif() ########### next target ############### if(KEXI_MOBILE) add_subdirectory( mobile ) else() set(kexi_SRCS main.cpp Messages.sh # non-source: ${CMAKE_SOURCE_DIR}/kexi_xgettext.sh Mainpage.dox Messages.sh ) kexi_add_app_icons(kexi_SRCS) - add_executable(kexi ${kexi_SRCS}) + kexi_add_executable(kexi ${kexi_SRCS}) target_link_libraries(kexi PRIVATE keximain ) install(TARGETS kexi ${INSTALL_TARGETS_DEFAULT_ARGS}) endif() add_subdirectory( pics ) diff --git a/src/KexiVersion.h.cmake b/src/KexiVersion.h.cmake index 664b0a088..204212d40 100644 --- a/src/KexiVersion.h.cmake +++ b/src/KexiVersion.h.cmake @@ -1,302 +1,312 @@ /* This file is part of the KDE project Copyright (c) 2003-2016 Kexi Team Version information based on calligraversion.h, Copyright (c) 2003 David Faure Copyright (c) 2003 Lukas Tinkl Copyright (c) 2004 Nicolas Goutte This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXIVERSION_H #define KEXIVERSION_H #include // -- WARNING: do not edit values below, instead edit constants in SetKexiVersionInfo.cmake class QString; #define KEXI_APP_NAME "Kexi" -//! @def KEXI_BASE_NAME -//! @brief Base name for Kexi libraries, based on major stable version. -//! Useful to construct co-installabile file names and paths. -#define KEXI_BASE_NAME "@PROJECT_BASE_NAME@" - -//! @def KEXI_BASE_NAME_LOWER -//! @brief Like KEXI_BASE_NAME but lowercase. -#define KEXI_BASE_NAME_LOWER "@PROJECT_BASE_NAME_LOWER@" - /** * @def KEXI_VERSION_STRING * @ingroup KexiMacros * @brief Version of Kexi as string, at compile time * * This macro contains the Kexi version in string form. As it is a macro, * it contains the version at compile time. See Kexi::versionString() if you need * a version used at runtime. * * @note The version string might contain spaces and special characters, * especially for development versions of Kexi. * If you use that macro directly for a file format (e.g. OASIS Open Document) * or for a protocol (e.g. http) be careful that it is appropriate. * (Fictional) example: "3.0 Alpha" */ #define KEXI_VERSION_STRING "@PROJECT_VERSION_STRING@" /** * @def KEXI_VERSION_MAJOR_MINOR_RELEASE * @ingroup KexiMacros * @brief Version string containing "major.minor.release" * @brief Version of Kexi as string, at compile time * * This macro contains the Kexi version in string form. As it is a macro, * it contains the version at compile time. * * @note The version string never contains spaces or special characters. */ #define KEXI_VERSION_MAJOR_MINOR_RELEASE "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_RELEASE@" /** * @def KEXI_STABLE_VERSION_MAJOR * @ingroup KexiMacros * @brief Major version of stable Kexi, at compile time * KEXI_VERSION_MAJOR is computed based on this value. */ #define KEXI_STABLE_VERSION_MAJOR @PROJECT_STABLE_VERSION_MAJOR@ /** * @def KEXI_VERSION_MAJOR * @ingroup KexiMacros * @brief Major version of Kexi, at compile time * * Generally it's the same as KEXI_STABLE_VERSION_MAJOR but for unstable x.0 * x is decreased by one, e.g. 3.0 Beta is 2.99. */ #define KEXI_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ /** * @def KEXI_STABLE_VERSION_MINOR * @ingroup KexiMacros * @brief Minor version of stable Kexi, at compile time * KEXI_VERSION_MINOR is computed based on this value. */ #define KEXI_STABLE_VERSION_MINOR @PROJECT_STABLE_VERSION_MINOR@ /** * @def KEXI_VERSION_MINOR * @ingroup KexiMacros * @brief Minor version of Kexi, at compile time * * Generally it's equal to KEXI_STABLE_VERSION_MINOR for stable releases, * equal to 99 for x.0 unstable releases (e.g. it's 3.0 Beta has minor version 99), * and equal to KEXI_STABLE_VERSION_MINOR-1 for unstable releases other than x.0. */ #define KEXI_VERSION_MINOR @PROJECT_VERSION_MINOR@ /** * @def KEXI_VERSION_RELEASE * @ingroup KexiMacros * @brief Release version of Kexi, at compile time. * 89 for Alpha. */ #define KEXI_VERSION_RELEASE @PROJECT_VERSION_RELEASE@ /** * @def KEXI_STABLE_VERSION_RELEASE * @ingroup KexiMacros * @brief Release version of Kexi, at compile time. * * Equal to KEXI_VERSION_RELEASE for stable releases and 0 for unstable ones. */ #define KEXI_STABLE_VERSION_RELEASE @PROJECT_STABLE_VERSION_RELEASE@ /** * @def KEXI_ALPHA * @ingroup KexiMacros * @brief If defined (1..9), indicates at compile time that Kexi is in alpha stage */ #cmakedefine KEXI_ALPHA @KEXI_ALPHA@ /** * @def KEXI_BETA * @ingroup KexiMacros * @brief If defined (1..9), indicates at compile time that Kexi is in beta stage */ #cmakedefine KEXI_BETA @KEXI_BETA@ /** * @def KEXI_RC * @ingroup KexiMacros * @brief If defined (1..9), indicates at compile time that Kexi is in "release candidate" stage */ #cmakedefine KEXI_RC @KEXI_RC@ /** * @def KEXI_STABLE * @ingroup KexiMacros * @brief If defined, indicates at compile time that Kexi is in stable stage */ #cmakedefine KEXI_STABLE @KEXI_STABLE@ /** * @ingroup KexiMacros * @brief Make a number from the major, minor and release number of a Kexi version * * This function can be used for preprocessing when KEXI_IS_VERSION is not * appropriate. */ #define KEXI_MAKE_VERSION( a,b,c ) (((a) << 16) | ((b) << 8) | (c)) /** * @ingroup KexiMacros * @brief Version of Kexi as number, at compile time * * This macro contains the Kexi version in number form. As it is a macro, * it contains the version at compile time. See version() if you need * the Kexi version used at runtime. */ #define KEXI_VERSION \ KEXI_MAKE_VERSION(KEXI_VERSION_MAJOR,KEXI_VERSION_MINOR,KEXI_VERSION_RELEASE) /** * @ingroup KexiMacros * @brief Check if the Kexi version matches a certain version or is higher * * This macro is typically used to compile conditionally a part of code: * @code * #if KEXI_IS_VERSION(2,3,0) * // Code for Kexi 2.3.0 * #else * // Code for older Kexi * #endif * @endcode * * @warning Especially during development phases of Kexi, be careful * when choosing the version number that you are checking against. * Otherwise you might risk to break the next Kexi release. * Therefore be careful that development version have a * version number lower than the released version, so do not check * e.g. for Kexi 4.3 with KEXI_IS_VERSION(4,3,0) * but with the actual version number at a time a needed feature was introduced, e.g. 4.3.2. */ #define KEXI_IS_VERSION(a,b,c) ( KEXI_VERSION >= KEXI_MAKE_VERSION(a,b,c) ) /** * @def KEXI_YEAR * @ingroup KexiMacros * @brief Year of the Kexi release, set at compile time * * This macro is used in "About application" dialog for strings such as "© 2012-..., The Author Team". */ -#define KEXI_YEAR "@KEXI_YEAR@" +#cmakedefine KEXI_YEAR "@KEXI_YEAR@" /** * @def KEXI_GIT_SHA1_STRING * @ingroup CalligraMacros * @brief Indicates the git sha1 commit which was used for compilation of Calligra */ #cmakedefine KEXI_GIT_SHA1_STRING "@KEXI_GIT_SHA1_STRING@" /** * @def KEXI_GIT_BRANCH_STRING * @ingroup CalligraMacros * @brief Indicates the git branch name which was used for compilation of Calligra */ #cmakedefine KEXI_GIT_BRANCH_STRING "@KEXI_GIT_BRANCH_STRING@" +/** + * @def KEXI_DISTRIBUTION_VERSION + * @ingroup CalligraMacros + * @brief Name of Kexi version useful to construct co-installabile releases + * By default is it equal to KEXI_STABLE_VERSION_MAJOR.KEXI_STABLE_VERSION_MINOR. + * It can be changed at configure stage by setting the KEXI_CUSTOM_DISTRIBUTION_VERSION + * CMake variable. + * @see KEXI_BASE_PATH + */ +#cmakedefine KEXI_DISTRIBUTION_VERSION "@KEXI_DISTRIBUTION_VERSION@" + +/** + * @def KEXI_BASE_PATH + * @ingroup CalligraMacros + * @brief Relative path name useful to construct co-installabile file names and paths + * It is equal to "kexi/N" where N is KEXI_DISTRIBUTION_VERSION. + */ +#cmakedefine KEXI_BASE_PATH "@KEXI_BASE_PATH@" + /** * Namespace for general Kexi functions. */ namespace Kexi { /** * Returns the encoded number of Kexi's version, see the KEXI_VERSION macro. * In contrary to that macro this function returns the number of the actually * installed Kexi version, not the number of the Kexi version that was * installed when the program was compiled. * @return the version number, encoded in a single int */ KEXICORE_EXPORT unsigned int version(); /** * Returns the major number of Kexi's version, e.g. * 1 for Kexi 1.2.3. * @return the major version number */ KEXICORE_EXPORT unsigned int versionMajor(); /** * Returns the minor number of Kexi's version, e.g. * 2 for Kexi 1.2.3. * @return the minor version number */ KEXICORE_EXPORT unsigned int versionMinor(); /** * Returns the release of Kexi's version, e.g. * 3 for Kexi 1.2.3. * @return the release number */ KEXICORE_EXPORT unsigned int versionRelease(); /** * Returns the Kexi version as string, e.g. "1.2.3" * Sometimes it may be even something like "1.2.3 beta 2" * @return the Kexi version. You can keep the string forever */ KEXICORE_EXPORT const char *versionString(); /** * @return the Kexi version string (versionString()) but appends extra information * such as "(git 4e06281 master)" if available. */ KEXICORE_EXPORT const char *fullVersionString(); /** * Returns the encoded number of stable Kexi's version. * For 2.3.1 it returns 2.3.1, for 2.5.70 returns 2.6.0, for 2.9.70 returns 3.0.0. * In contrary to KEXI_STABLE_VERSION macro this function returns the number * of the actually installed Kexi version, not the number of the Kexi version that was * installed when the program was compiled. * @return the version number, encoded in a single int * @see Kexi::version() * @see KEXI_STABLE_VERSION */ KEXICORE_EXPORT unsigned int stableVersion(); /** * Returns the major number of stable Kexi's version, e.g. * 1 for Kexi 1.2.3. * @return the major stable version number */ KEXICORE_EXPORT unsigned int stableVersionMajor(); /** * Returns the minor number of stable Kexi's version, e.g. * 2 for Kexi 1.2.3. * @return the minor stable version number */ KEXICORE_EXPORT unsigned int stableVersionMinor(); /** * Returns the release of stable Kexi's version, e.g. * 3 for Kexi 1.2.3. * @return the release stable version number */ KEXICORE_EXPORT unsigned int stableVersionRelease(); /** * Returns the stable Kexi version as string, e.g. "1.2.3" * It never contains alpha, beta or rc part. * @return the stable Kexi version. */ KEXICORE_EXPORT QString stableVersionString(); } #endif diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e0f942a10..036c801df 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,73 +1,70 @@ add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44020) set(kexicore_LIB_SRCS KexiGlobal.cpp kexi.cpp kexiaboutdata.cpp KexiMainWindowIface.cpp KexiStandardAction.cpp kexidbconnectionset.cpp kexiprojectset.cpp kexiactionproxy.cpp kexisharedactionhost.cpp kexiactioncategories.cpp kexiproject.cpp KexiWindow.cpp KexiWindowData.cpp KexiView.cpp kexipartmanager.cpp kexipartinfo.cpp kexipartitem.cpp kexipartbase.cpp kexipart.cpp kexipartguiclient.cpp kexiprojectdata.cpp KexiRecentProjects.cpp kexiinternalpart.cpp #TODO KEXI3 kexidragobjects.cpp kexistartupdata.cpp KexiCommandLineOptions.cpp kexiguimsghandler.cpp kexitextmsghandler.cpp kexidataiteminterface.cpp kexidbshortcutfile.cpp kexiblobbuffer.cpp #TODO KEXI3 kexistaticpart.cpp kexitabledesignerinterface.cpp kexisearchandreplaceiface.cpp kexitemplateloader.cpp KexiRecordNavigatorHandler.cpp KexiRecordNavigatorIface.cpp KexiSearchableModel.cpp KexiGroupButton.cpp #TODO belongs to widget/? ) -add_library(kexicore SHARED ${kexicore_LIB_SRCS}) +kexi_add_library(kexicore SHARED ${kexicore_LIB_SRCS}) generate_export_header(kexicore) target_link_libraries(kexicore PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets KF5::CoreAddons KF5::XmlGui kexiutils KDb KPropertyWidgets ) if(WIN32) target_include_directories(kexicore PUBLIC "${KDEWIN_INCLUDES}") target_link_libraries(kexicore PUBLIC ${KDEWIN_LIBRARIES}) endif() -set_target_properties(kexicore PROPERTIES - VERSION ${GENERIC_PROJECT_LIB_VERSION} SOVERSION ${GENERIC_PROJECT_LIB_SOVERSION} -) install(TARGETS kexicore ${INSTALL_TARGETS_DEFAULT_ARGS}) #install(FILES kexihandler.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) diff --git a/src/core/kexidbconnectionset.cpp b/src/core/kexidbconnectionset.cpp index be41f6f03..7ac9bb3e9 100644 --- a/src/core/kexidbconnectionset.cpp +++ b/src/core/kexidbconnectionset.cpp @@ -1,236 +1,236 @@ /* This file is part of the KDE project Copyright (C) 2003-2014 Jarosław Staniek This program 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 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kexidbconnectionset.h" #include "kexidbshortcutfile.h" #include #include #include #include #include #include #include //! @internal class KexiDBConnectionSetPrivate { public: KexiDBConnectionSetPrivate() : maxid(-1) { } QList list; QHash filenamesForData; QHash dataForFilenames; int maxid; }; KexiDBConnectionSet::KexiDBConnectionSet() : QObject() , d(new KexiDBConnectionSetPrivate()) { } KexiDBConnectionSet::~KexiDBConnectionSet() { delete d; } bool KexiDBConnectionSet::addConnectionData(KDbConnectionData *data, const QString& _filename) { if (!data) return false; /*! @todo KEXI3 if (data->id < 0) data->id = d->maxid + 1; @todo check for id-duplicates d->maxid = qMax(d->maxid, data->id); */ d->maxid++; QString filename(_filename); bool generateUniqueFilename = filename.isEmpty() || (!filename.isEmpty() && data == d->dataForFilenames.value(filename)); if (generateUniqueFilename) { QString dir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kexi/connections/"; if (dir.isEmpty()) { m_result.setMessage(xi18n("Could not find location to save connection data file.")); return false; } QString baseFilename(dir + (data->hostName().isEmpty() ? "localhost" : data->hostName())); int i = 0; while (QFile::exists(baseFilename + (i > 0 ? QString::number(i) : QString()) + ".kexic")) { i++; } if (!QDir(dir).exists()) { //make 'connections' dir and protect it //! @todo KEXI3 set permission of the created dirs to 0700 if (!QDir().mkpath(dir)) { m_result.setMessage(xi18n("Could not create folder %1 for connection data file.", dir)); return false; } //! @todo change permission of every created subdir, see KStandardDirs::makeDir() create QFile(dir).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); } filename = baseFilename + (i > 0 ? QString::number(i) : QString()) + ".kexic"; } addConnectionDataInternal(data, filename); bool result = saveConnectionData(data, *data); if (!result) removeConnectionDataInternal(data); return result; } void KexiDBConnectionSet::addConnectionDataInternal(KDbConnectionData *data, const QString& filename) { d->filenamesForData.insert(key(*data), filename); d->dataForFilenames.insert(filename, data); d->list.append(data); } bool KexiDBConnectionSet::saveConnectionData(KDbConnectionData *oldData, const KDbConnectionData &newData) { if (!oldData) return false; const QString oldDataKey = key(*oldData); const QString filename(d->filenamesForData.value(oldDataKey)); if (filename.isEmpty()) { m_result.setCode(ERR_OTHER); return false; } KexiDBConnShortcutFile shortcutFile(filename); if (!shortcutFile.saveConnectionData(newData, newData.savePassword())) { m_result = shortcutFile.result(); return false; } if (oldData != &newData) { *oldData = newData; } const QString newDataKey = key(newData); if (oldDataKey != newDataKey) { // update file info: key changed, filename is untouched d->filenamesForData.remove(oldDataKey); d->filenamesForData.insert(newDataKey, filename); } return true; } void KexiDBConnectionSet::removeConnectionDataInternal(KDbConnectionData *data) { const QString filename(d->filenamesForData.value(key(*data))); d->filenamesForData.remove(key(*data)); d->dataForFilenames.remove(filename); d->list.removeAt(d->list.indexOf(data)); } bool KexiDBConnectionSet::removeConnectionData(KDbConnectionData *data) { if (!data) return false; const QString filename(d->filenamesForData.value(key(*data))); if (filename.isEmpty()) { m_result.setCode(ERR_OTHER); return false; } QFile file(filename); if (!file.remove()) { m_result.setMessage(xi18n("Could not remove connection file %1.", filename)); return false; } removeConnectionDataInternal(data); return true; } QList KexiDBConnectionSet::list() const { return d->list; } void KexiDBConnectionSet::clear() { d->list.clear(); d->filenamesForData.clear(); d->dataForFilenames.clear(); } void KexiDBConnectionSet::load() { clear(); const QStringList dirs(QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, - // not KEXI_BASE_NAME_LOWER because this version-independent path in user dir + // not KEXI_BASE_PATH because this version-independent path in user dir "kexi/connections", QStandardPaths::LocateDirectory)); QSet foundDirs; foreach(const QString &dir, dirs) { const QString realDir(QDir(dir).canonicalPath()); // make sure we don't visit the same dir twice to avoid duplication of connection data: // - follow symlinks // - QStandardPaths::locateAll sometimes returns duplicated entries (e.g. on Windows) if (foundDirs.contains(realDir)) { continue; } foundDirs.insert(realDir); QFileInfo realDirInfo(realDir); if (!realDirInfo.exists()) { continue; } QDirIterator it(dir, QStringList() << "*.kexic", QDir::Files | QDir::Readable, QDirIterator::FollowSymlinks); while (it.hasNext()) { KDbConnectionData *data = new KDbConnectionData(); KexiDBConnShortcutFile shortcutFile(it.next()); if (!shortcutFile.loadConnectionData(data)) { delete data; continue; } addConnectionDataInternal(data, it.filePath()); //qDebug() << file << "added."; } } } QString KexiDBConnectionSet::fileNameForConnectionData(const KDbConnectionData &data) const { return d->filenamesForData.value(key(data)); } KDbConnectionData* KexiDBConnectionSet::connectionDataForFileName(const QString& fileName) const { return d->dataForFilenames.value(fileName); } // static QString KexiDBConnectionSet::key(const KDbConnectionData &data) { return data.driverId().toLower() + ',' + data.userName().toLower() + ',' + data.hostName().toLower() + ',' + QString::number(data.port()) + ',' + QString::number(data.useLocalSocketFile()) + ',' + data.localSocketFileName() + ',' + data.databaseName().replace(',', "\\,") + ',' + QString::number(data.savePassword() ? 1 : 0) + ',' + data.caption().replace(',', "\\,") + ',' + data.description().replace(',', "\\,"); } diff --git a/src/core/kexipartmanager.cpp b/src/core/kexipartmanager.cpp index 2392572db..0fe9ed9da 100644 --- a/src/core/kexipartmanager.cpp +++ b/src/core/kexipartmanager.cpp @@ -1,310 +1,310 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2003-2016 Jarosław Staniek 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 "kexipartmanager.h" #include "kexipart.h" #include "kexiinternalpart.h" #include "kexipartinfo.h" //! @todo KEXI3 #include "kexistaticpart.h" #include "KexiVersion.h" #include "KexiJsonTrader.h" #include #include #include #include #include #include #include #include #include using namespace KexiPart; typedef QHash KexiInternalPartDict; -Q_GLOBAL_STATIC_WITH_ARGS(KexiJsonTrader, KexiPartTrader_instance, (KEXI_BASE_NAME_LOWER)) +Q_GLOBAL_STATIC_WITH_ARGS(KexiJsonTrader, KexiPartTrader_instance, (KEXI_BASE_PATH)) class Q_DECL_HIDDEN Manager::Private { public: explicit Private(Manager *manager_); ~Private(); Manager *manager; PartDict parts; KexiInternalPartDict internalParts; PartInfoList partlist; PartInfoDict partsByPluginId; bool lookupDone; bool lookupResult; }; Manager::Private::Private(Manager *manager_) : manager(manager_) , lookupDone(false) , lookupResult(false) { } Manager::Private::~Private() { qDeleteAll(partlist); partlist.clear(); } //--- Manager::Manager(QObject *parent) : QObject(parent), KDbResultable(), d(new Private(this)) { } Manager::~Manager() { delete d; } template PartClass* Manager::part(Info *info, QHash *partDict) { if (!info) { return 0; } clearResult(); KDbMessageGuard mg(this); if (!lookup()) { return 0; } if (!info->isValid()) { m_result = KDbResult(info->errorMessage()); return 0; } PartClass *p = partDict->value(info->pluginId()); if (p) { return p; } // actual loading KPluginFactory *factory = qobject_cast(info->instantiate()); if (!factory) { m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, xi18nc("@info", "Could not load Kexi plugin file %1.", info->fileName())); QPluginLoader loader(info->fileName()); // use this to get the message (void)loader.load(); m_result.setServerMessage(loader.errorString()); info->setErrorMessage(m_result.message()); qWarning() << m_result.message() << m_result.serverMessage(); return 0; } p = factory->create(this); if (!p) { m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, xi18nc("@info", "Could not open Kexi plugin %1.").arg(info->fileName())); qWarning() << m_result.message(); return 0; } p->setInfo(info); p->setObjectName(QString("%1 plugin").arg(info->id())); partDict->insert(info->pluginId(), p); return p; } //! @return a string list @a list with removed whitespace from the beginning and end of each string. //! Empty strings are also removed. static QStringList cleanupStringList(const QStringList &list) { QStringList result; foreach(const QString &item, list) { QString cleanedItem = item.trimmed(); if (!cleanedItem.isEmpty()) { result.append(cleanedItem); } } return result; } bool Manager::lookup() { if (d->lookupDone) { return d->lookupResult; } d->lookupDone = true; d->lookupResult = false; d->partlist.clear(); d->partsByPluginId.clear(); d->parts.clear(); // load visual order of plugins KConfigGroup cg(KSharedConfig::openConfig()->group("Parts")); const QStringList orderedPluginIds = cleanupStringList( cg.readEntry("Order", "org.kexi-project.table," "org.kexi-project.query," "org.kexi-project.form," "org.kexi-project.report," "org.kexi-project.macro," "org.kexi-project.script").split(',')); QVector orderedInfos(orderedPluginIds.count()); QStringList serviceTypes; serviceTypes << "Kexi/Viewer" << "Kexi/Designer" << "Kexi/Editor" << "Kexi/Internal"; QList offers = KexiPartTrader_instance->query(serviceTypes); foreach(const QPluginLoader *loader, offers) { QScopedPointer info(new Info(*loader)); if (info->id().isEmpty()) { qWarning() << "No plugin ID specified for Kexi Part" << info->fileName() << "-- skipping!"; continue; } // check version const QString expectedVersion = KexiPart::version(); if (info->version() != expectedVersion) { qWarning() << "Kexi plugin" << info->id() << "has version" << info->version() << "but version required by Kexi is" << expectedVersion << "-- skipping this plugin!"; continue; } // skip experimental types if ( (!Kexi::tempShowMacros() && info->id() == "org.kexi-project.macro") || (!Kexi::tempShowScripts() && info->id() == "org.kexi-project.script") ) { continue; } // skip duplicates if (d->partsByPluginId.contains(info->id())) { qWarning() << "More than one Kexi plugin with ID" << info->id() << info->fileName() << "-- skipping this one"; continue; } // find correct place for plugins visible in Navigator if (info->isVisibleInNavigator()) { const int index = orderedPluginIds.indexOf(info->id()); if (index != -1) { orderedInfos[index] = info.data(); } else { orderedInfos.append(info.data()); } // append later when we know order } else { // append now d->partlist.append(info.data()); } d->partsByPluginId.insert(info->pluginId(), info.data()); info.take(); } qDeleteAll(offers); offers.clear(); // fill the final list using computed order for (int i = 0; i < orderedInfos.size(); i++) { Info *info = orderedInfos[i]; if (!info) { continue; } //qDebug() << "adding Kexi part info" << info->pluginId(); d->partlist.insert(i, info); } // now the d->partlist is: [ordered plugins visible in Navigator] [other plugins in unspecified order] d->lookupResult = true; return true; } Part* Manager::part(Info *info) { KDbMessageGuard mg(this); Part *p = part(info, &d->parts); if (p) { emit partLoaded(p); } return p; } static QString realPluginId(const QString &pluginId) { if (pluginId.contains('.')) { return pluginId; } else { // not like "org.kexi-project.table" - construct return QString::fromLatin1("org.kexi-project.") + QString(pluginId).remove("kexi/"); } } Part* Manager::partForPluginId(const QString &pluginId) { Info* info = infoForPluginId(pluginId); return part(info); } Info* Manager::infoForPluginId(const QString &pluginId) { KDbMessageGuard mg(this); if (!lookup()) return 0; const QString realId = realPluginId(pluginId); Info *i = realId.isEmpty() ? 0 : d->partsByPluginId.value(realId); if (i) return i; m_result = KDbResult(xi18nc("@info", "No plugin for ID %1", realId)); return 0; } /*! @todo KEXI3 void Manager::insertStaticPart(StaticPart* part) { if (!part) return; KDbMessageGuard mg(this); if (!lookup()) return; d->partlist.append(part->info()); if (!part->info()->pluginId().isEmpty()) d->partsByPluginId.insert(part->info()->pluginId(), part->info()); d->parts.insert(part->info()->pluginId(), part); } */ KexiInternalPart* Manager::internalPartForPluginId(const QString& pluginId) { Info* info = infoForPluginId(pluginId); if (!info || !info->serviceTypes().contains("Kexi/Internal")) { return nullptr; } return part(info, &d->internalParts); } PartInfoList* Manager::infoList() { KDbMessageGuard mg(this); if (!lookup()) { return 0; } return &d->partlist; } diff --git a/src/formeditor/CMakeLists.txt b/src/formeditor/CMakeLists.txt index 65459b51b..9b8b0159a 100644 --- a/src/formeditor/CMakeLists.txt +++ b/src/formeditor/CMakeLists.txt @@ -1,69 +1,68 @@ add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44010) include_directories( ${CMAKE_SOURCE_DIR}/src/widget ${CMAKE_SOURCE_DIR}/src/widget/utils ${CMAKE_SOURCE_DIR}/src/widget/tableview ${CMAKE_SOURCE_DIR}/src/widget/properties ${CMAKE_SOURCE_DIR}/src/core ${CMAKE_SOURCE_DIR}/src/kexiutils/style ) # enable to add signal/slot connections # set(KFD_SIGSLOTS true) ########### next target ############### set(kformdesigner_LIB_SRCS container.cpp resizehandle.cpp widgetfactory.cpp widgetlibrary.cpp KexiFormWidgetsPluginMetaData.cpp WidgetInfo.cpp libactionwidget.cpp form.cpp form_p.cpp objecttree.cpp formIO.cpp FormWidget.cpp FormWidgetInterface.cpp WidgetTreeWidget.cpp commands.cpp events.cpp richtextdialog.cpp tabstopdialog.cpp #KEXI_LIST_FORM_WIDGET_SUPPORT: editlistviewdialog.cpp utils.cpp #todo kfdpixmapedit.cpp widgetwithsubpropertiesinterface.cpp kexiformeventhandler.cpp # from libkexiformutils kexiactionselectiondialog.cpp # from libkexiformutils ) set(kformdesigner_LIBS kexiutils kexicore kexiextendedwidgets kexiundo KDb KPropertyWidgets ) if(KFD_SIGSLOTS) add_definitions( -DKFD_SIGSLOTS=1 ) list(APPEND kformdesigner_LIB_SRCS connectiondialog.cpp) list(APPEND kformdesigner_LIBS kexiextendedwidgets kexidatatable) endif() -add_library(kformdesigner SHARED ${kformdesigner_LIB_SRCS}) +kexi_add_library(kformdesigner SHARED ${kformdesigner_LIB_SRCS}) generate_export_header(kformdesigner) target_link_libraries(kformdesigner ${kformdesigner_LIBS}) -set_target_properties(kformdesigner PROPERTIES VERSION ${GENERIC_PROJECT_LIB_VERSION} SOVERSION ${GENERIC_PROJECT_LIB_SOVERSION} ) install(TARGETS kformdesigner ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/formeditor/widgetlibrary.cpp b/src/formeditor/widgetlibrary.cpp index b647c1195..de0c41a8d 100644 --- a/src/formeditor/widgetlibrary.cpp +++ b/src/formeditor/widgetlibrary.cpp @@ -1,784 +1,784 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004-2015 Jarosław Staniek 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 "widgetlibrary.h" #include #include "WidgetInfo.h" #include "widgetfactory.h" #include "libactionwidget.h" #include "container.h" #include "form.h" #include "formIO.h" #include "FormWidgetInterface.h" #include "objecttree.h" #include "KexiJsonTrader.h" #include "KexiFormWidgetsPluginMetaData.h" #include "KexiVersion.h" #include #define KEXI_SKIP_SETUPBREEZEICONTHEME #define KEXI_SKIP_REGISTERRESOURCE #include
#include #include #include #include #include namespace KFormDesigner { -Q_GLOBAL_STATIC_WITH_ARGS(KexiJsonTrader, KexiFormWidgetsPluginTrader_instance, (KEXI_BASE_NAME_LOWER "/forms/widgets")) +Q_GLOBAL_STATIC_WITH_ARGS(KexiJsonTrader, KexiFormWidgetsPluginTrader_instance, (KEXI_BASE_PATH "/forms/widgets")) //! @internal class Q_DECL_HIDDEN WidgetLibrary::Private { public: Private(WidgetLibrary *library, const QStringList& supportedFactoryGroups) : showAdvancedProperties(true) , q(library) , m_couldNotFindAnyFormWidgetPluginsErrorDisplayed(false) , m_supportedFactoryGroups(supportedFactoryGroups.toSet()) , m_lookupDone(false) , m_loadFactoriesDone(false) { q->setMessageHandler(&messageHandler); m_advancedProperties.insert("acceptDrops"); m_advancedProperties.insert("accessibleDescription"); m_advancedProperties.insert("accessibleName"); m_advancedProperties.insert("autoMask"); m_advancedProperties.insert("backgroundOrigin"); m_advancedProperties.insert("backgroundMode");//this is rather useless m_advancedProperties.insert("baseSize"); m_advancedProperties.insert("contextMenuEnabled"); m_advancedProperties.insert("contextMenuPolicy"); m_advancedProperties.insert("cursorPosition"); m_advancedProperties.insert("cursorMoveStyle"); m_advancedProperties.insert("dragEnabled"); m_advancedProperties.insert("enableSqueezedText"); m_advancedProperties.insert("layout");// too large risk to break things m_advancedProperties.insert("layoutDirection"); m_advancedProperties.insert("locale"); m_advancedProperties.insert("mouseTracking"); /*! @todo: reenable */ m_advancedProperties.insert("palette"); m_advancedProperties.insert("sizeAdjustPolicy"); //QAbstractScrollArea m_advancedProperties.insert("sizeIncrement"); m_advancedProperties.insert("sizePolicy"); m_advancedProperties.insert("statusTip"); m_advancedProperties.insert("toolTipDuration"); m_advancedProperties.insert("trapEnterKeyEvent"); m_advancedProperties.insert("windowModality"); m_advancedProperties.insert("autoExclusive"); // by providing this in propeditor m_advancedProperties.insert("minimumSize"); m_advancedProperties.insert("maximumSize"); m_advancedProperties.insert("clickMessage"); // for backward compatibility Kexi projects created with Qt < 4.7 m_advancedProperties.insert("showClearButton"); // for backward compatibility Kexi projects created with Qt 4 #ifndef KEXI_SHOW_UNFINISHED /*! @todo reenable */ m_advancedProperties.insert("accel"); m_advancedProperties.insert("icon"); m_advancedProperties.insert("paletteBackgroundPixmap"); m_advancedProperties.insert("pixmap"); m_advancedProperties.insert("shortcut"); // renamed from "accel" in Qt 4 m_advancedProperties.insert("windowIcon"); // renamed from "icon" in Qt 4 #endif } ~Private() { qDeleteAll(m_factories); m_factories.clear(); qDeleteAll(m_pluginsMetaData); m_pluginsMetaData.clear(); } QHash widgets() { KDbMessageGuard mg(q); (void)loadFactories(); return m_widgets; } QHash factories() { KDbMessageGuard mg(q); (void)loadFactories(); return m_factories; } bool isAdvancedProperty(const QByteArray &property) const { return m_advancedProperties.contains(property); } bool showAdvancedProperties; private: //! Performs a form widget plugins lookup. @return true on success. //! @todo This method generates a few warnings, maybe we want to optionally display them somewhere (via the message handler)? bool lookup() { //! @todo Allow refreshing if (m_lookupDone) { return m_lookupResult; } m_lookupDone = true; m_lookupResult = false; q->clearResult(); QStringList serviceTypes; serviceTypes << "Kexi/FormWidget"; QList offers = KexiFormWidgetsPluginTrader_instance->query(serviceTypes); foreach(const QPluginLoader *loader, offers) { QScopedPointer metaData(new KexiFormWidgetsPluginMetaData(*loader)); if (metaData->id().isEmpty()) { qWarning() << "No plugin ID specified for Kexi Form Widgets plugin" << metaData->fileName() << "-- skipping!"; continue; } // check version const QString expectedVersion = KFormDesigner::version(); if (metaData->version() != expectedVersion) { qWarning() << "Kexi Form Widgets plugin" << metaData->id() << "has version" << metaData->majorVersion() << "but required version is" << KFormDesigner::version() << "-- skipping!"; continue; } // skip duplicates if (m_pluginsMetaData.contains(metaData->id())) { qWarning() << "More than one Kexi Form Widgets plugin with ID" << metaData->id() << metaData->fileName() << "-- skipping this one"; continue; } //qDebug() << "found factory:" << ptr->name(); if (!metaData->group().isEmpty() && !m_supportedFactoryGroups.contains(metaData->group())) { qDebug() << "Factory group" << metaData->group() << "for Form Widgets plugin" << metaData->id() << metaData->fileName() << "is not supported -- skipping!"; continue; } if (!setupPrivateIconsResourceWithMessage( - QLatin1String(KEXI_BASE_NAME_LOWER), + QLatin1String(KEXI_BASE_PATH), QString::fromLatin1("icons/%1_%2.rcc") .arg(metaData->id()).arg(supportedIconTheme), QtWarningMsg, QString::fromLatin1(":/icons/%1").arg(metaData->id()))) { continue; } m_pluginsMetaData.insert(metaData->id(), metaData.data()); metaData.take(); } qDeleteAll(offers); offers.clear(); if (m_pluginsMetaData.isEmpty()) { q->m_result = KDbResult(i18n("Could not find any form widget plugins.")); m_couldNotFindAnyFormWidgetPluginsErrorDisplayed = true; return false; } m_lookupResult = true; return true; } //! Loads all factory plugins bool loadFactories() { if (m_loadFactoriesDone) { if (m_couldNotFindAnyFormWidgetPluginsErrorDisplayed) { q->clearResult(); // show the warning only once } return m_loadFactoriesResult; } m_loadFactoriesDone = true; m_loadFactoriesResult = false; if (!lookup()) { return false; } foreach (KexiFormWidgetsPluginMetaData *pluginMetaData, m_pluginsMetaData) { WidgetFactory *factory = loadFactory(pluginMetaData); if (!factory) { continue; } //collect information about classes to be hidden if (factory->hiddenClasses()) { foreach (const QByteArray &c, *factory->hiddenClasses()) { m_hiddenClasses.insert(c); } } } //now we have factories instantiated: load widgets QList loadLater; foreach (WidgetFactory *factory, m_factories) { //ONE LEVEL, FLAT INHERITANCE, but works! //if this factory inherits from something, load its witgets later //! @todo improve if (factory->inheritsFactories()) loadLater.append(factory); else loadFactoryWidgets(factory); } //load now the rest foreach (WidgetFactory* f, loadLater) { loadFactoryWidgets(f); } m_loadFactoriesResult = true; return true; } //! Loads of a single factory. @return true on success WidgetFactory *loadFactory(KexiFormWidgetsPluginMetaData *pluginMetaData) { KPluginFactory *factory = qobject_cast(pluginMetaData->instantiate()); if (!factory) { q->m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, xi18nc("@info", "Could not load Kexi Form Widgets plugin file %1.", pluginMetaData->fileName())); q->setErrorMessage(pluginMetaData, q->result().message()); qWarning() << q->result().message(); return 0; } WidgetFactory *widgetFactory = factory->create(q); if (!widgetFactory) { q->m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, xi18nc("@info", "Could not open Kexi Form Widgets plugin %1.", pluginMetaData->fileName())); qWarning() << q->m_result.message(); return 0; } widgetFactory->setLibrary(q); widgetFactory->setObjectName(pluginMetaData->id()); widgetFactory->setAdvancedPropertiesVisible(showAdvancedProperties); //inherit this flag from the library m_factories.insert(pluginMetaData->id().toLatin1(), widgetFactory); return widgetFactory; } //! Loads widgets for factory @a f void loadFactoryWidgets(WidgetFactory *f) { QHash widgetsForFactory(f->classes()); foreach (WidgetInfo *w, widgetsForFactory) { if (m_hiddenClasses.contains( w->className() )) continue; //this class is hidden // check if we want to inherit a widget from a different factory if (!w->parentFactoryName().isEmpty() && !w->inheritedClassName().isEmpty()) { WidgetFactory *parentFactory = m_factories.value(w->parentFactoryName().toLower()); if (!parentFactory) { qWarning() << "class" << w->className() << ": no such parent factory" << w->parentFactoryName(); continue; } WidgetInfo* inheritedClass = parentFactory->widgetInfoForClassName(w->inheritedClassName()); if (!inheritedClass) { qWarning() << "class" << w->inheritedClassName() << " - no such class to inherit in factory" << w->parentFactoryName(); continue; } //ok: inherit properties: w->setInheritedClass( inheritedClass ); if (w->iconName().isEmpty()) w->setIconName(inheritedClass->iconName()); //ok? foreach(const QByteArray& alternateName, inheritedClass->alternateClassNames()) { w->addAlternateClassName( alternateName, inheritedClass->isOverriddenClassName(alternateName)); } if (w->includeFileName().isEmpty()) w->setIncludeFileName(inheritedClass->includeFileName()); if (w->name().isEmpty()) w->setName(inheritedClass->name()); if (w->namePrefix().isEmpty()) w->setNamePrefix(inheritedClass->namePrefix()); if (w->description().isEmpty()) w->setDescription(inheritedClass->description()); } QList cnames( w->alternateClassNames() ); cnames.prepend(w->className()); foreach (const QByteArray &wname, cnames) { WidgetInfo *widgetForClass = widgetsForFactory.value(wname); if (!widgetForClass || (widgetForClass && !widgetForClass->isOverriddenClassName(wname))) { //insert a widgetinfo, if: //1) this class has no alternate class assigned yet, or //2) this class has alternate class assigned but without 'override' flag m_widgets.insert(wname, w); } } } } WidgetLibrary *q; KexiGUIMessageHandler messageHandler; //! A map which associates a class name with a Widget class QHash m_pluginsMetaData; //!< owner bool m_couldNotFindAnyFormWidgetPluginsErrorDisplayed; QSet m_supportedFactoryGroups; QHash m_factories; //!< owner QHash m_widgets; //!< owner QSet m_advancedProperties; QSet m_hiddenClasses; bool m_lookupDone; bool m_lookupResult; bool m_loadFactoriesDone; bool m_loadFactoriesResult; }; } using namespace KFormDesigner; //------------------------------------------- WidgetLibrary::WidgetLibrary(QObject *parent, const QStringList& supportedFactoryGroups) : QObject(parent) , KDbResultable() , d(new Private(this, supportedFactoryGroups)) { } WidgetLibrary::~WidgetLibrary() { delete d; } void WidgetLibrary::createWidgetActions(ActionGroup *group) { foreach (WidgetInfo *winfo, d->widgets()) { LibActionWidget *a = new LibActionWidget(group, winfo); connect(a, SIGNAL(toggled(QByteArray)), this, SIGNAL(widgetActionToggled(QByteArray))); } } void WidgetLibrary::addCustomWidgetActions(KActionCollection *col) { if (!col) return; foreach (WidgetFactory *factory, d->factories()) { factory->createCustomActions(col); } } QWidget* WidgetLibrary::createWidget(const QByteArray &classname, QWidget *parent, const char *name, Container *c, WidgetFactory::CreateWidgetOptions options) { WidgetInfo *wclass = d->widgets().value(classname); if (!wclass) return 0; QWidget *widget = wclass->factory()->createWidget(wclass->className(), parent, name, c, options); if (!widget) { //try to instantiate from inherited class if (wclass->inheritedClass()) widget = wclass->inheritedClass()->factory()->createWidget( wclass->className(), parent, name, c, options); if (!widget) return 0; } widget->setAcceptDrops(true); if (options & WidgetFactory::DesignViewMode) { FormWidgetInterface* fwiface = dynamic_cast(widget); if (fwiface) fwiface->setDesignMode(true); } emit widgetCreated(widget); return widget; } bool WidgetLibrary::createMenuActions(const QByteArray &c, QWidget *w, QMenu *menu, KFormDesigner::Container *container) { WidgetInfo *wclass = d->widgets().value(c); if (!wclass) return false; if (wclass->factory()->createMenuActions(c, w, menu, container)) { return true; } //try from inherited class if (wclass->inheritedClass()) { return wclass->inheritedClass()->factory()->createMenuActions( wclass->className(), w, menu, container); } return false; } bool WidgetLibrary::startInlineEditing(const QByteArray &classname, QWidget *w, Container *container) { WidgetInfo *wclass = d->widgets().value(classname); if (!wclass) return false; FormWidgetInterface* fwiface = dynamic_cast(w); { KFormDesigner::WidgetFactory::InlineEditorCreationArguments args(classname, w, container); if (wclass->factory()->startInlineEditing(args)) { args.container->form()->createInlineEditor(args); if (fwiface) fwiface->setEditingMode(true); return true; } } if (wclass->inheritedClass()) { //try from inherited class KFormDesigner::WidgetFactory::InlineEditorCreationArguments args(wclass->className(), w, container); if (wclass->inheritedClass()->factory()->startInlineEditing(args)) { args.container->form()->createInlineEditor(args); if (fwiface) fwiface->setEditingMode(true); return true; } } return false; } bool WidgetLibrary::previewWidget(const QByteArray &classname, QWidget *widget, Container *container) { WidgetInfo *wclass = d->widgets().value(classname); if (!wclass) return false; FormWidgetInterface* fwiface = dynamic_cast(widget); if (fwiface) fwiface->setDesignMode(false); if (wclass->factory()->previewWidget(classname, widget, container)) return true; //try from inherited class if (wclass->inheritedClass()) return wclass->inheritedClass()->factory()->previewWidget(wclass->className(), widget, container); return false; } bool WidgetLibrary::clearWidgetContent(const QByteArray &classname, QWidget *w) { WidgetInfo *wclass = d->widgets().value(classname); if (!wclass) return false; if (wclass->factory()->clearWidgetContent(classname, w)) return true; //try from inherited class if (wclass->inheritedClass()) return wclass->inheritedClass()->factory()->clearWidgetContent(wclass->className(), w); return false; } QString WidgetLibrary::displayName(const QByteArray &classname) { WidgetInfo *wi = d->widgets().value(classname); if (wi) return wi->name(); return classname; } QString WidgetLibrary::savingName(const QByteArray &classname) { WidgetInfo *wi = d->widgets().value(classname); if (wi && !wi->savingName().isEmpty()) return wi->savingName(); return classname; } QString WidgetLibrary::namePrefix(const QByteArray &classname) { WidgetInfo *wi = d->widgets().value(classname); if (wi) return wi->namePrefix(); return classname; } QString WidgetLibrary::textForWidgetName(const QByteArray &name, const QByteArray &className) { WidgetInfo *widget = d->widgets().value(className); if (!widget) return QString(); QString newName = name; newName.remove(widget->namePrefix()); newName = widget->name() + (newName.isEmpty() ? QString() : (QLatin1String(" ") + newName)); return newName; } QByteArray WidgetLibrary::classNameForAlternate(const QByteArray &classname) { if (d->widgets().value(classname)) return classname; WidgetInfo *wi = d->widgets().value(classname); if (wi) { return wi->className(); } // widget not supported return "CustomWidget"; } QString WidgetLibrary::includeFileName(const QByteArray &classname) { WidgetInfo *wi = d->widgets().value(classname); if (wi) return wi->includeFileName(); return QString(); } QString WidgetLibrary::iconName(const QByteArray &classname) { WidgetInfo *wi = d->widgets().value(classname); if (wi) return wi->iconName(); return KexiIconName("unknown-widget"); } bool WidgetLibrary::saveSpecialProperty(const QByteArray &classname, const QString &name, const QVariant &value, QWidget *w, QDomElement &parentNode, QDomDocument &parent) { WidgetInfo *wi = d->widgets().value(classname); if (!wi) return false; if (wi->factory()->saveSpecialProperty(classname, name, value, w, parentNode, parent)) return true; //try from inherited class if (wi->inheritedClass()) return wi->inheritedClass()->factory()->saveSpecialProperty(wi->className(), name, value, w, parentNode, parent); return false; } bool WidgetLibrary::readSpecialProperty(const QByteArray &classname, QDomElement &node, QWidget *w, ObjectTreeItem *item) { WidgetInfo *wi = d->widgets().value(classname); if (!wi) return false; if (wi->factory()->readSpecialProperty(classname, node, w, item)) return true; //try from inherited class if (wi->inheritedClass()) return wi->inheritedClass()->factory()->readSpecialProperty(wi->className(), node, w, item); return false; } void WidgetLibrary::setAdvancedPropertiesVisible(bool set) { d->showAdvancedProperties = set; } bool WidgetLibrary::advancedPropertiesVisible() const { return d->showAdvancedProperties; } bool WidgetLibrary::isPropertyVisible(const QByteArray &classname, QWidget *w, const QByteArray &property, bool multiple, bool isTopLevel) { if (isTopLevel) { // no focus policy for top-level form widget... if (!d->showAdvancedProperties && property == "focusPolicy") return false; } WidgetInfo *wi = d->widgets().value(classname); if (!wi) return false; if (!d->showAdvancedProperties && d->isAdvancedProperty(property)) { //this is advanced property, should we hide it? if (!wi->internalProperty("forceShowAdvancedProperty:" + property).toBool() && (!wi->inheritedClass() || !wi->inheritedClass()->internalProperty("forceShowAdvancedProperty:" + property).toBool())) { return false; //hide it } } if (!wi->factory()->isPropertyVisible(classname, w, property, multiple, isTopLevel)) return false; //try from inherited class if (wi->inheritedClass() && !wi->inheritedClass()->factory()->isPropertyVisible(wi->className(), w, property, multiple, isTopLevel)) return false; return true; } QList WidgetLibrary::autoSaveProperties(const QByteArray &classname) { WidgetInfo *wi = d->widgets().value(classname); if (!wi) return QList(); return wi->autoSaveProperties(); } WidgetInfo* WidgetLibrary::widgetInfoForClassName(const char* classname) { return d->widgets().value(classname); } WidgetFactory* WidgetLibrary::factoryForClassName(const char* classname) { WidgetInfo *wi = widgetInfoForClassName(classname); return wi ? wi->factory() : 0; } QString WidgetLibrary::propertyDescForName(WidgetInfo *winfo, const QByteArray& propertyName) { if (!winfo || !winfo->factory()) return QString(); QString desc(winfo->factory()->propertyDescription(propertyName)); if (!desc.isEmpty()) return desc; if (winfo->parentFactoryName().isEmpty()) return QString(); //try in parent factory, if exists WidgetFactory *parentFactory = d->factories().value(winfo->parentFactoryName()); if (!parentFactory) return QString(); return parentFactory->propertyDescription(propertyName); } QString WidgetLibrary::propertyDescForValue(WidgetInfo *winfo, const QByteArray& name) { if (!winfo->factory()) return QString(); QString desc(winfo->factory()->valueDescription(name)); if (!desc.isEmpty()) return desc; if (winfo->parentFactoryName().isEmpty()) return QString(); //try in parent factory, if exists WidgetFactory *parentFactory = d->factories().value(winfo->parentFactoryName()); if (!parentFactory) return QString(); return parentFactory->valueDescription(name); } void WidgetLibrary::setPropertyOptions(KPropertySet& set, const WidgetInfo& winfo, QWidget* w) { if (!winfo.factory()) return; winfo.factory()->setPropertyOptions(set, winfo, w); if (winfo.parentFactoryName().isEmpty()) return; WidgetFactory *parentFactory = d->factories().value(winfo.parentFactoryName()); if (!parentFactory) return; parentFactory->setPropertyOptions(set, winfo, w); } WidgetFactory* WidgetLibrary::factory(const char* factoryName) const { return d->factories().value(factoryName); } QVariant WidgetLibrary::internalProperty(const QByteArray& classname, const QByteArray& property) { WidgetInfo *wclass = d->widgets().value(classname); if (!wclass) return QString(); QVariant value(wclass->internalProperty(property)); if (value.isNull() && wclass->inheritedClass()) return wclass->inheritedClass()->internalProperty(property); return value; } WidgetFactory::CreateWidgetOption WidgetLibrary::showOrientationSelectionPopup( const QByteArray &classname, QWidget* parent, const QPoint& pos) { WidgetInfo *wclass = d->widgets().value(classname); if (!wclass) return WidgetFactory::AnyOrientation; //get custom icons and strings QIcon iconHorizontal, iconVertical; QString iconName(wclass->internalProperty("orientationSelectionPopup:horizontalIcon").toString()); if (iconName.isEmpty() && wclass->inheritedClass()) iconName = wclass->inheritedClass()->internalProperty("orientationSelectionPopup:horizontalIcon").toString(); if (!iconName.isEmpty()) iconHorizontal = QIcon::fromTheme(iconName); iconName = wclass->internalProperty("orientationSelectionPopup:verticalIcon").toString(); if (iconName.isEmpty() && wclass->inheritedClass()) iconName = wclass->inheritedClass()->internalProperty("orientationSelectionPopup:verticalIcon").toString(); if (!iconName.isEmpty()) iconVertical = QIcon::fromTheme(iconName); QString textHorizontal = wclass->internalProperty("orientationSelectionPopup:horizontalText").toString(); if (textHorizontal.isEmpty() && wclass->inheritedClass()) iconName = wclass->inheritedClass()->internalProperty("orientationSelectionPopup:horizontalText").toString(); if (textHorizontal.isEmpty()) //default textHorizontal = xi18nc("Insert Horizontal Widget", "Insert Horizontal"); QString textVertical = wclass->internalProperty("orientationSelectionPopup:verticalText").toString(); if (textVertical.isEmpty() && wclass->inheritedClass()) iconName = wclass->inheritedClass()->internalProperty("orientationSelectionPopup:verticalText").toString(); if (textVertical.isEmpty()) //default textVertical = xi18nc("Insert Vertical Widget", "Insert Vertical"); QMenu popup(parent); popup.setObjectName("orientationSelectionPopup"); popup.addSection(QIcon::fromTheme(wclass->iconName()), xi18n("Insert Widget: %1", wclass->name())); QAction* horizAction = popup.addAction(iconHorizontal, textHorizontal); QAction* vertAction = popup.addAction(iconVertical, textVertical); popup.addSeparator(); popup.addAction(koIcon("dialog-cancel"), xi18n("Cancel")); QAction *a = popup.exec(pos); if (a == horizAction) return WidgetFactory::HorizontalOrientation; else if (a == vertAction) return WidgetFactory::VerticalOrientation; return WidgetFactory::AnyOrientation; //means "cancelled" } bool WidgetLibrary::propertySetShouldBeReloadedAfterPropertyChange( const QByteArray& classname, QWidget *w, const QByteArray& property) { WidgetInfo *winfo = widgetInfoForClassName(classname); if (!winfo) return false; return winfo->factory()->propertySetShouldBeReloadedAfterPropertyChange(classname, w, property); } ObjectTreeItem* WidgetLibrary::selectableItem(ObjectTreeItem* item) { //qDebug() << item->widget()->metaObject()->className(); WidgetInfo *wi = d->widgets().value(item->widget()->metaObject()->className()); if (!wi) return item; return wi->factory()->selectableItem(item); } void WidgetLibrary::setErrorMessage(KexiFormWidgetsPluginMetaData *pluginMetaData, const QString& errorMessage) { pluginMetaData->setErrorMessage(errorMessage); } diff --git a/src/kexiutils/CMakeLists.txt b/src/kexiutils/CMakeLists.txt index 23633faaa..f91c712d3 100644 --- a/src/kexiutils/CMakeLists.txt +++ b/src/kexiutils/CMakeLists.txt @@ -1,83 +1,79 @@ add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44024) include_directories(completer) set(kexiutils_LIB_SRCS utils.cpp FontSettings_p.cpp InternalPropertyMap.cpp SmallToolButton.cpp KexiCommandLinkButton.cpp FlowLayout.cpp transliteration_table.cpp kmessagewidget.cpp KexiContextMessage.cpp KexiTitleLabel.cpp KexiLinkWidget.cpp KexiLinkButton.cpp KexiCloseButton.cpp KexiAssistantPage.cpp KexiAssistantWidget.cpp KexiAnimatedLayout.cpp KexiCategorizedView.cpp KexiTester.cpp KexiJsonTrader.cpp KexiPushButton.cpp KexiFadeWidgetEffect.cpp KexiPluginMetaData.cpp completer/KexiCompleter.cpp ) if (KEXI_MOBILE) else () if (KEXI_DEBUG_GUI) list(APPEND kexiutils_LIB_SRCS debuggui.cpp ) endif () endif () -add_library(kexiutils SHARED ${kexiutils_LIB_SRCS}) +kexi_add_library(kexiutils SHARED ${kexiutils_LIB_SRCS}) set(kexiutils_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ) target_include_directories(kexiutils PUBLIC "$" ) target_link_libraries(kexiutils PUBLIC KF5::KIOWidgets #for KRun... KF5::IconThemes KF5::WidgetsAddons KF5::ConfigWidgets # KStandardAction KColorScheme KF5::I18n KF5::ItemViews # KCategorizedView KCategoryDrawer KDb ) #target_link_libraries(kexiutils LINK_INTERFACE_LIBRARIES KF5::KIOWidgets) generate_export_header(kexiutils) -set_target_properties(kexiutils PROPERTIES - VERSION ${GENERIC_PROJECT_LIB_VERSION} SOVERSION ${GENERIC_PROJECT_LIB_SOVERSION} -) - install(TARGETS kexiutils ${INSTALL_TARGETS_DEFAULT_ARGS}) if(FALSE) # TODO: install when we move to independent place install( FILES tristate.h utils.h kexiutils_export.h kexiutils_global.h InternalPropertyMap.h SmallToolButton.h FlowLayout.h kmessagewidget.h KexiContextMessage.h KexiTitleLabel.h KexiAssistantPage.h KexiAssistantWidget.h KexiAnimatedLayout.h DESTINATION ${INCLUDE_INSTALL_DIR}/kexiutils COMPONENT Devel) endif() if(BUILD_TESTING) add_subdirectory(tests) endif() diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt index 07c7f8c6e..d58b2b18a 100644 --- a/src/main/CMakeLists.txt +++ b/src/main/CMakeLists.txt @@ -1,75 +1,74 @@ #TODO add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44019) include_directories(${CMAKE_SOURCE_DIR}/src/core ${CMAKE_SOURCE_DIR}/src/kexiutils ${CMAKE_SOURCE_DIR}/src/main ${CMAKE_SOURCE_DIR}/src/main/startup ${CMAKE_SOURCE_DIR}/src/widget ${CMAKE_BINARY_DIR}/src/widget) set(QT_USE_QTUITOOLS true) set(keximain_LIB_SRCS KexiMainWindow.cpp KexiMainWindow_p.cpp KexiMenuWidget.cpp kexifinddialog.cpp KexiSearchLineEdit.cpp KexiUserFeedbackAgent.cpp KexiBugReportDialog.cpp startup/KexiNewProjectAssistant.cpp startup/KexiOpenProjectAssistant.cpp startup/KexiWelcomeAssistant.cpp startup/KexiWelcomeStatusBar.cpp startup/KexiImportExportAssistant.cpp startup/KexiStartupDialog.cpp startup/KexiStartup.cpp startup/KexiTemplatesModel.cpp startup/KexiRecentProjectsModel.cpp startup/KexiAssistantMessageHandler.cpp startup/KexiPasswordPage.cpp #todo printing/kexisimpleprintingengine.cpp #todo printing/kexisimpleprintingpagesetup.cpp #todo printing/kexisimpleprintingpart.cpp #todo printing/kexisimpleprintpreviewwindow.cpp ) ki18n_wrap_ui(keximain_LIB_SRCS kexifinddialog.ui startup/KexiProjectStorageTypeSelectionPage.ui startup/KexiServerDBNamePage.ui startup/KexiMainImportExportPage.ui ) -add_library(keximain SHARED ${keximain_LIB_SRCS}) +kexi_add_library(keximain SHARED ${keximain_LIB_SRCS}) generate_export_header(keximain) target_link_libraries(keximain PUBLIC kexicore PRIVATE kexiextendedwidgets kexiguiutils KF5::GuiAddons Qt5::UiTools ) if(HAVE_KCRASH) target_link_libraries(keximain PRIVATE KF5::Crash ) endif() target_compile_definitions(keximain PRIVATE CMAKE_CURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") -set_target_properties(keximain PROPERTIES VERSION ${GENERIC_PROJECT_LIB_VERSION} SOVERSION ${GENERIC_PROJECT_LIB_SOVERSION} ) install(TARGETS keximain ${INSTALL_TARGETS_DEFAULT_ARGS}) add_subdirectory(status) if (BUILD_TESTING) add_subdirectory(autotests) endif() diff --git a/src/main/KexiRegisterResource_p.h b/src/main/KexiRegisterResource_p.h index 119544d34..c4919082a 100644 --- a/src/main/KexiRegisterResource_p.h +++ b/src/main/KexiRegisterResource_p.h @@ -1,349 +1,349 @@ /* This file is part of the KDE project Copyright (C) 2016 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #ifdef QT_ONLY #define KLocalizedString QString #else #include #include #include #endif //! @todo Support other themes const QString supportedIconTheme = QLatin1String("breeze"); //! @return true if @a path is readable static bool fileReadable(const QString &path) { return !path.isEmpty() && QFileInfo(path).isReadable(); } #ifdef Q_OS_WIN #define KPATH_SEPARATOR ';' #else #define KPATH_SEPARATOR ':' #endif //! @brief Used for a workaround: locations for QStandardPaths::AppDataLocation end with app name. //! If this is not an expected app but for example a test app, replace //! the subdir name with app name so we can find resource file(s). static QStringList correctStandardLocations(const QString &privateName, QStandardPaths::StandardLocation location, const QString &extraLocation) { QStringList result; if (!privateName.isEmpty()) { QRegularExpression re(QLatin1Char('/') + QCoreApplication::applicationName() + QLatin1Char('$')); QStringList standardLocations(QStandardPaths::standardLocations(location)); if (!extraLocation.isEmpty()) { standardLocations.append(extraLocation); } for(const QString &dir : standardLocations) { if (dir.indexOf(re) != -1) { QString realDir(dir); realDir.replace(re, QLatin1Char('/') + privateName); result.append(realDir); } } } return result; } static QString locateFile(const QString &privateName, const QString& path, QStandardPaths::StandardLocation location, const QString &extraLocation) { // let QStandardPaths handle this, it will look for app local stuff QString fullPath = QFileInfo( QStandardPaths::locate(location, path)).canonicalFilePath(); if (fileReadable(fullPath)) { return fullPath; } // Try extra locations if (!extraLocation.isEmpty()) { fullPath = QFileInfo(extraLocation + '/' + path).canonicalFilePath(); if (fileReadable(fullPath)) { return fullPath; } } #ifdef Q_OS_WIN // This makes the app portable and working without from the build dir const QString dataDir = QFileInfo(QCoreApplication::applicationDirPath() + QStringLiteral("/data/") + path).canonicalFilePath(); if (fileReadable(dataDir)) { return dataDir; } #endif // Try in PATH subdirs, useful for running apps from the build dir, without installing for(const QByteArray &pathDir : qgetenv("PATH").split(KPATH_SEPARATOR)) { const QString dataDirFromPath = QFileInfo(QFile::decodeName(pathDir) + QStringLiteral("/data/") + path).canonicalFilePath(); if (fileReadable(dataDirFromPath)) { return dataDirFromPath; } } const QStringList correctedStandardLocations(correctStandardLocations(privateName, location, extraLocation)); for(const QString &dir : correctedStandardLocations) { fullPath = QFileInfo(dir + QLatin1Char('/') + path).canonicalFilePath(); if (fileReadable(fullPath)) { return fullPath; } } return fullPath; } #ifndef KEXI_SKIP_REGISTERRESOURCE -#ifdef KEXI_BASE_NAME_LOWER -#define BASE_NAME KEXI_BASE_NAME_LOWER +#ifdef KEXI_BASE_PATH +#define BASE_PATH KEXI_BASE_PATH #else -#define BASE_NAME QCoreApplication::applicationName() +#define BASE_PATH QCoreApplication::applicationName() #endif static bool registerResource(const QString& path, QStandardPaths::StandardLocation location, const QString &resourceRoot, const QString &extraLocation, KLocalizedString *errorMessage, KLocalizedString *detailsErrorMessage) { - const QString fullPath = locateFile(BASE_NAME, + const QString fullPath = locateFile(BASE_PATH, path, location, extraLocation); if (fullPath.isEmpty() || !QResource::registerResource(fullPath, resourceRoot)) { QStringList triedLocations(QStandardPaths::standardLocations(location)); if (!extraLocation.isEmpty()) { triedLocations.append(extraLocation); } const QString triedLocationsString = QLocale().createSeparatedList(triedLocations); #ifdef QT_ONLY *errorMessage = QString("Could not open icon resource file %1.").arg(path); *detailsErrorMessage = QString("Tried to find in %1.").arg(triedLocationsString); #else *errorMessage = kxi18nc("@info", "Could not open icon resource file %1." "Kexi will not start. " "Please check if Kexi is properly installed.") .subs(QFileInfo(path).fileName()); *detailsErrorMessage = kxi18nc("@info Tried to find files in ", "Tried to find in %1.").subs(triedLocationsString); #endif return false; } *errorMessage = KLocalizedString(); *detailsErrorMessage = KLocalizedString(); return true; } #endif // !KEXI_SKIP_REGISTERRESOURCE #ifndef KEXI_SKIP_SETUPBREEZEICONTHEME static bool registerGlobalBreezeIconsResource(KLocalizedString *errorMessage, KLocalizedString *detailsErrorMessage) { QString extraLocation; #ifdef CMAKE_INSTALL_FULL_ICONDIR extraLocation = QDir::fromNativeSeparators(QFile::decodeName(CMAKE_INSTALL_FULL_ICONDIR)); if (extraLocation.endsWith("/icons")) { extraLocation.chop(QLatin1String("/icons").size()); } #endif return registerResource("icons/breeze/breeze-icons.rcc", QStandardPaths::GenericDataLocation, QStringLiteral("/icons/breeze"), extraLocation, errorMessage, detailsErrorMessage); } //! Tell Qt about the theme static void setupBreezeIconTheme() { #ifdef QT_GUI_LIB QIcon::setThemeSearchPaths(QStringList() << QStringLiteral(":/icons")); QIcon::setThemeName(QStringLiteral("breeze")); #endif } #endif // !KEXI_SETUPBREEZEICONTHEME #ifndef KEXI_SKIP_REGISTERICONSRESOURCE /*! @brief Registers icons resource file * @param privateName Name to be used instead of application name for resource lookup * @param path Relative path to the resource file * @param location Standard file location to use for file lookup * @param resourceRoot A resource root for QResource::registerResource() * @param errorMessage On failure it is set to a brief error message. * @param errorDescription On failure it is set to a detailed error message. * other for warning */ static bool registerIconsResource(const QString &privateName, const QString& path, QStandardPaths::StandardLocation location, const QString &resourceRoot, const QString &extraLocation, QString *errorMessage, QString *detailedErrorMessage) { const QString fullPath = locateFile(privateName, path, location, extraLocation); if (fullPath.isEmpty() || !QFileInfo(fullPath).isReadable() || !QResource::registerResource(fullPath, resourceRoot)) { QStringList triedLocations(QStandardPaths::standardLocations(location)); if (!extraLocation.isEmpty()) { triedLocations.append(extraLocation); } triedLocations.append(correctStandardLocations(privateName, location, extraLocation)); const QString triedLocationsString = QLocale().createSeparatedList(triedLocations); #ifdef QT_ONLY *errorMessage = QString("Could not open icon resource file %1.").arg(path); *detailedErrorMessage = QString("Tried to find in %1.").arg(triedLocationsString); #else //! @todo 3.1 Re-add translation *errorMessage = /*QObject::tr*/ QString::fromLatin1( "Could not open icon resource file \"%1\". " "Application will not start. " "Please check if it is properly installed.") .arg(QFileInfo(path).fileName()); //! @todo 3.1 Re-add translation *detailedErrorMessage = QString::fromLatin1("Tried to find in %1.").arg(triedLocationsString); #endif return false; } *errorMessage = QString(); *detailedErrorMessage = QString(); return true; } #endif // !KEXI_SKIP_SETUPBREEZEICONTHEME #if !defined QT_ONLY && !defined KEXI_SKIP_SETUPPRIVATEICONSRESOURCE /*! @brief Sets up a private icon resource file * @return @c false on failure and sets error message. Does not warn or exit on failure. * @param privateName Name to be used instead of application name for resource lookup * @param path Relative path to the resource file * @param themeName Icon theme to use. It affects filename. * @param errorMessage On failure it is set to a brief error message * @param errorDescription On failure it is set to a detailed error message * other for warning * @param prefix Resource path prefix. The default is useful for library-global resource, * other values is useful for plugins. */ static bool setupPrivateIconsResource(const QString &privateName, const QString& path, const QString &themeName, QString *errorMessage, QString *detailedErrorMessage, const QString &prefix = QLatin1String(":/icons")) { // Register application's resource first to have priority over the theme. // Some icons may exists in both resources. if (!registerIconsResource(privateName, path, QStandardPaths::AppDataLocation, QString(), QString(), errorMessage, detailedErrorMessage)) { return false; } bool changeTheme = false; #ifdef QT_GUI_LIB QIcon::setThemeSearchPaths(QStringList() << prefix << QIcon::themeSearchPaths()); changeTheme = 0 != QIcon::themeName().compare(themeName, Qt::CaseInsensitive); if (changeTheme) { QIcon::setThemeName(themeName); } #endif KConfigGroup cg(KSharedConfig::openConfig(), "Icons"); changeTheme = changeTheme || 0 != cg.readEntry("Theme", QString()).compare(themeName, Qt::CaseInsensitive); // tell KIconLoader an co. about the theme if (changeTheme) { cg.writeEntry("Theme", themeName); cg.sync(); } return true; } /*! @brief Sets up a private icon resource file * @return @c false on failure and sets error message. * @param privateName Name to be used instead of application name for resource lookup * @param path Relative path to the resource file * @param themeName Icon theme to use. It affects filename. * @param errorMessage On failure it is set to a brief error message. * @param errorDescription On failure it is set to a detailed error message. * other for warning * @param prefix Resource path prefix. The default is useful for library-global resource, * other values is useful for plugins. */ static bool setupPrivateIconsResourceWithMessage(const QString &privateName, const QString& path, const QString &themeName, QString *errorMessage, QString *detailedErrorMessage, const QString &prefix = QLatin1String(":/icons")) { if (!setupPrivateIconsResource(privateName, path, themeName, errorMessage, detailedErrorMessage, prefix)) { if (detailedErrorMessage->isEmpty()) { KMessageBox::error(nullptr, *errorMessage); } else { KMessageBox::detailedError(nullptr, *errorMessage, *detailedErrorMessage); } return false; } return true; } /*! @overload setupPrivateIconsResourceWithMessage(QString &privateName, const QString& path, const QString &themeName, QString *errorMessage, QString *detailedErrorMessage, const QString &prefix = QLatin1String(":/icons")) Uses default theme name. */ static bool setupPrivateIconsResourceWithMessage(const QString &privateName, const QString& path, QString *errorMessage, QString *detailedErrorMessage, const QString &prefix = QLatin1String(":/icons")) { return setupPrivateIconsResourceWithMessage(privateName, path, supportedIconTheme, errorMessage, detailedErrorMessage, prefix); } /*! @brief Sets up a private icon resource file * Warns on failure and returns @c false. * @param privateName Name to be used instead of application name for resource lookup * @param path Relative path to the resource file * @param messageType Type of message to use on error, QtFatalMsg for fatal exit and any * other for warning * @param prefix Resource path prefix. The default is useful for library-global resource, * other values is useful for plugins. */ static bool setupPrivateIconsResourceWithMessage(const QString &privateName, const QString& path, QtMsgType messageType, const QString &prefix = QLatin1String(":/icons")) { QString errorMessage; QString detailedErrorMessage; if (!setupPrivateIconsResourceWithMessage(privateName, path, &errorMessage, &detailedErrorMessage, prefix)) { if (messageType == QtFatalMsg) { qFatal("%s %s", qPrintable(errorMessage), qPrintable(detailedErrorMessage)); } else { qWarning() << qPrintable(errorMessage) << qPrintable(detailedErrorMessage); } return false; } return true; } #endif // !QT_ONLY && !KEXI_SKIP_SETUPPRIVATEICONSRESOURCE diff --git a/src/main/startup/KexiWelcomeStatusBar.cpp b/src/main/startup/KexiWelcomeStatusBar.cpp index 0316f2b61..c920f52a2 100644 --- a/src/main/startup/KexiWelcomeStatusBar.cpp +++ b/src/main/startup/KexiWelcomeStatusBar.cpp @@ -1,1215 +1,1215 @@ /* This file is part of the KDE project Copyright (C) 2011-2012 Jarosław Staniek This program 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 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiWelcomeStatusBar.h" #include "KexiWelcomeStatusBar_p.h" #include #include #include #include #include #include "KexiUserFeedbackAgent.h" #define KEXI_SKIP_SETUPPRIVATEICONSRESOURCE #define KEXI_SKIP_SETUPBREEZEICONTHEME #define KEXI_SKIP_REGISTERICONSRESOURCE #define KEXI_SKIP_REGISTERRESOURCE #include "KexiRegisterResource_p.h" #include #include #include #include #include #include #include #include #if defined(Q_OS_WIN) || defined(Q_OS_MAC) # include # include # include # include #else # define USE_KIO_COPY # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const int GUI_UPDATE_INTERVAL = 60; // update interval for GUI, in minutes static const int DONATION_INTERVAL = 10; // donation interval, in days static const int UPDATE_FILES_LIST_SIZE_LIMIT = 1024 * 128; static const int UPDATE_FILES_COUNT_LIMIT = 128; //! @return x.y.0 static QString stableVersionStringDot0() { return QString::number(Kexi::stableVersionMajor()) + '.' + QString::number(Kexi::stableVersionMinor()) + ".0"; } static QString uiPath(const QString &fname) { KexiUserFeedbackAgent *f = KexiMainWindowIface::global()->userFeedbackAgent(); return f->serviceUrl() + QString("/ui/%1/").arg(stableVersionStringDot0()) + fname; } static QString basePath() { - return QString(KEXI_BASE_NAME_LOWER "/status/") + stableVersionStringDot0(); + return QString(KEXI_BASE_PATH "/status"); } static QString findFilename(const QString &guiFileName) { QString result = locateFile(QString(), basePath() + '/' + guiFileName, QStandardPaths::GenericDataLocation, QString()); if (result.isEmpty()) { // last chance: file from the source tree result = QFileInfo(QFile::decodeName(CMAKE_CURRENT_SOURCE_DIR "/status/") + guiFileName) .canonicalFilePath(); } //qDebug() << result; return result; } // --- class Q_DECL_HIDDEN KexiWelcomeStatusBarGuiUpdater::Private : public QObject { Q_OBJECT public: Private() : configGroup(KConfigGroup(KSharedConfig::openConfig()->group("User Feedback"))) { } KConfigGroup configGroup; public Q_SLOTS: void sendRequestListFilesFinished(KJob* job) { if (job->error()) { qWarning() << "Error while receiving .list file - no files will be updated"; //! @todo error... return; } KIO::StoredTransferJob* sendJob = qobject_cast(job); QString result = sendJob->data(); if (result.length() > UPDATE_FILES_LIST_SIZE_LIMIT) { // anti-DOS protection qWarning() << "Too large .list file (" << result.length() << "); the limit is" << UPDATE_FILES_LIST_SIZE_LIMIT << "- no files will be updated"; return; } //qDebug() << result; QStringList data = result.split('\n', QString::SkipEmptyParts); result.clear(); m_fileNamesToUpdate.clear(); if (data.count() > UPDATE_FILES_COUNT_LIMIT) { // anti-DOS protection qWarning() << "Too many files to update (" << data.count() << "); the limit is" << UPDATE_FILES_COUNT_LIMIT << "- no files will be updated"; return; } // OK, try to update (stage 1: check, stage 2: checking) for (int stage = 1; stage <= 2; stage++) { int i = 0; for (QStringList::ConstIterator it(data.constBegin()); it!=data.constEnd(); ++it, i++) { const QByteArray hash((*it).left(32).toLatin1()); const QString remoteFname((*it).mid(32 + 2)); if (stage == 1) { if (hash.length() != 32) { qWarning() << "Invalid hash" << hash << "in line" << i+1 << "- no files will be updated"; return; } if ((*it).mid(32, 2) != " ") { qWarning() << "Two spaces expected but found" << (*it).mid(32, 2) << "in line" << i+1 << "- no files will be updated"; return; } if (remoteFname.contains(QRegularExpression("\\s"))) { qWarning() << "Filename expected without whitespace but found" << remoteFname << "in line" << i+1 << "- no files will be updated"; return; } } else if (stage == 2) { checkFile(hash, remoteFname, &m_fileNamesToUpdate); } } } if (m_fileNamesToUpdate.isEmpty()) { qDebug() << "No files to update."; return; } // update files QList sourceFiles; foreach (const QString &fname, m_fileNamesToUpdate) { sourceFiles.append(QUrl(uiPath(fname))); } m_tempDir.reset(new QTemporaryDir(QDir::tempPath() + "/kexi-status")); //qDebug() << m_tempDir->path(); #ifdef USE_KIO_COPY KIO::CopyJob *copyJob = KIO::copy(sourceFiles, QUrl::fromLocalFile(m_tempDir->path()), KIO::HideProgressInfo | KIO::Overwrite); connect(copyJob, &KIO::CopyJob::result, this, &Private::filesCopyFinished); #else if (!m_downloadManager) { m_downloadManager = new QNetworkAccessManager(this); connect(m_downloadManager.data(), &QNetworkAccessManager::finished, this, &Private::fileDownloadFinished); } m_sourceFilesToDownload = sourceFiles; downloadNextFile(); #endif //qDebug() << "copying from" << QUrl(uiPath(fname)) << "to" // << (dir + fname); } private Q_SLOTS: #ifdef USE_KIO_COPY void filesCopyFinished(KJob* job) { if (job->error()) { //! @todo error... qDebug() << "ERROR:" << job->errorString(); m_tempDir.reset(); return; } KIO::CopyJob* copyJob = qobject_cast(job); Q_UNUSED(copyJob) //qDebug() << "DONE" << copyJob->destUrl(); (void)copyFilesToDestinationDir(); } #else #define DOWNLOAD_BUFFER_SIZE 1024 * 50 void fileDownloadFinished(QNetworkReply* reply) { const bool ok = copyFile(reply); reply->deleteLater(); if (!ok) { qWarning() << "Error downloading file" << m_sourceFilesToDownload.first(); delete m_downloadManager; m_sourceFilesToDownload.clear(); m_tempDir.reset(); } m_sourceFilesToDownload.removeFirst(); downloadNextFile(); } bool copyFile(QNetworkReply* reply) { if (reply->error() != QNetworkReply::NoError) { return false; } const QString filename(m_sourceFilesToDownload.first().fileName()); QString path(m_tempDir->path() + '/' + filename); QSaveFile f(path); if (!f.open(QIODevice::WriteOnly)) { return false; } QByteArray buf(DOWNLOAD_BUFFER_SIZE, Qt::Uninitialized); while (!reply->atEnd()) { const qint64 size = reply->read(buf.data(), buf.size()); if (size < 0) { return false; } if (f.write(buf.data(), size) != size) { return false; } } if (!f.commit()) { return false; } return true; } private: void downloadNextFile() { if (m_sourceFilesToDownload.isEmpty()) { // success (void)copyFilesToDestinationDir(); return; } m_downloadManager->get(QNetworkRequest(m_sourceFilesToDownload.first())); } #endif private: bool copyFilesToDestinationDir() { const QString dir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + basePath() + '/'); bool ok = true; if (!QDir(dir).exists()) { if (!QDir().mkpath(dir)) { ok = false; qWarning() << "Could not create" << dir; } } if (ok) { foreach (const QString &fname, m_fileNamesToUpdate) { const QByteArray oldName(QFile::encodeName(m_tempDir->path() + '/' + fname)), newName(QFile::encodeName(dir + fname)); if (0 != ::rename(oldName.constData(), newName.constData())) { qWarning() << "cannot move" << (m_tempDir->path() + '/' + fname) << "to" << (dir + fname); } } } QDir(m_tempDir->path()).removeRecursively(); m_tempDir.reset(); m_fileNamesToUpdate.clear(); return ok; } void checkFile(const QByteArray &hash, const QString &remoteFname, QStringList *fileNamesToUpdate) { QString localFname = findFilename(remoteFname); if (localFname.isEmpty()) { fileNamesToUpdate->append(remoteFname); qDebug() << "missing filename" << remoteFname << "- download it"; return; } QFile file(localFname); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "could not open file" << localFname << "- update it"; fileNamesToUpdate->append(remoteFname); return; } QCryptographicHash md5(QCryptographicHash::Md5); if (!md5.addData(&file)) { qWarning() << "could not check MD5 for file" << localFname << "- update it"; fileNamesToUpdate->append(remoteFname); return; } if (md5.result().toHex() != hash) { qDebug() << "not matching file" << localFname << "- update it"; fileNamesToUpdate->append(remoteFname); } } QStringList m_fileNamesToUpdate; QScopedPointer m_tempDir; #ifndef USE_KIO_COPY QList m_sourceFilesToDownload; QPointer m_downloadManager; #endif }; KexiWelcomeStatusBarGuiUpdater::KexiWelcomeStatusBarGuiUpdater() : QObject() , d(new Private) { } KexiWelcomeStatusBarGuiUpdater::~KexiWelcomeStatusBarGuiUpdater() { delete d; } void KexiWelcomeStatusBarGuiUpdater::update() { QDateTime lastStatusBarUpdate = d->configGroup.readEntry("LastStatusBarUpdate", QDateTime()); if (lastStatusBarUpdate.isValid()) { int minutes = lastStatusBarUpdate.secsTo(QDateTime::currentDateTime()) / 60; if (minutes < GUI_UPDATE_INTERVAL) { qDebug() << "gui updated" << minutes << "min. ago, next auto-update in" << (GUI_UPDATE_INTERVAL - minutes) << "min."; return; } } d->configGroup.writeEntry("LastStatusBarUpdate", QDateTime::currentDateTime()); KexiUserFeedbackAgent *f = KexiMainWindowIface::global()->userFeedbackAgent(); f->waitForRedirect(this, SLOT(slotRedirectLoaded())); } void KexiWelcomeStatusBarGuiUpdater::slotRedirectLoaded() { QByteArray postData = stableVersionStringDot0().toLatin1(); KIO::Job* sendJob = KIO::storedHttpPost(postData, QUrl(uiPath(".list")), KIO::HideProgressInfo); connect(sendJob, SIGNAL(result(KJob*)), d, SLOT(sendRequestListFilesFinished(KJob*))); sendJob->addMetaData("content-type", "Content-Type: application/x-www-form-urlencoded"); } // --- //! @internal class ScrollArea : public QScrollArea { Q_OBJECT public: explicit ScrollArea(QWidget *parent = 0) : QScrollArea(parent) { setFrameShape(QFrame::NoFrame); setBackgroundRole(QPalette::Base); setWidgetResizable(true); } void setEnabled(bool set) { if (set != isEnabled()) { QScrollArea::setEnabled(set); updateColors(); } } protected: virtual void changeEvent(QEvent* event) { switch (event->type()) { case QEvent::EnabledChange: case QEvent::PaletteChange: updateColors(); break; default:; } QScrollArea::changeEvent(event); } void updateColors() { if (!widget()) return; KColorScheme scheme(palette().currentColorGroup()); QColor linkColor = scheme.foreground(KColorScheme::LinkText).color(); //qDebug() << "_____________" << isEnabled(); foreach(QLabel* lbl, widget()->findChildren()) { QString t = lbl->text(); QRegularExpression re("", QRegularExpression::InvertedGreedinessOption); int pos = 0; int oldPos = 0; QString newText; QRegularExpressionMatch match = re.match(t); //qDebug() << "t:" << t; while ((pos = match.capturedStart(pos)) != -1) { //qDebug() << "pos:" << pos; //qDebug() << "newText += t.mid(oldPos, pos - oldPos)" // << t.mid(oldPos, pos - oldPos); newText += t.midRef(oldPos, pos - oldPos); //qDebug() << "newText1:" << newText; //qDebug() << lbl->objectName() << "~~~~" << t.mid(pos, re.matchedLength()); QString a = t.mid(pos, match.capturedLength()); //qDebug() << "a:" << a; int colPos = a.indexOf("color:"); if (colPos == -1) { // add color a.insert(a.length() - 1, " style=\"color:" + linkColor.name() + ";\""); } else { // replace color colPos += qstrlen("color:"); for (;colPos < a.length() && a[colPos] == ' '; colPos++) { } if (colPos < a.length() && a[colPos] == '#') { colPos++; int i = colPos; for (;i < a.length(); i++) { if (a[i] == ';' || a[i] == ' ' || a[i] == '"' || a[i] == '\'') break; } //qDebug() << "******" << a.mid(colPos, i - colPos); a.replace(colPos, i - colPos, linkColor.name().mid(1)); } } //qDebug() << "a2:" << a; newText += a; //qDebug() << "newText2:" << newText; pos += match.capturedLength(); oldPos = pos; //qDebug() << "pos2:" << pos; } //qDebug() << "oldPos:" << oldPos; newText += t.midRef(oldPos); //qDebug() << "newText3:" << newText; lbl->setText(newText); } #if 0 QString text; text = QString("%3") .arg(link).arg(linkColor.name()).arg(linkText); if (!format.isEmpty()) { text = QString(format).replace("%L", text); } q->setText(text); #endif } }; // --- class Q_DECL_HIDDEN KexiWelcomeStatusBar::Private { public: explicit Private(KexiWelcomeStatusBar* _q) : statusWidget(0), helpAction(0), shareAction(0), cancelAction(0), q(_q) { rccFname = findFilename("status.rcc"); if (!rccFname.isEmpty()) { QResource::registerResource(rccFname); } scores.insert(KexiUserFeedbackAgent::BasicArea, 4); scores.insert(KexiUserFeedbackAgent::SystemInfoArea, 4); scores.insert(KexiUserFeedbackAgent::ScreenInfoArea, 2); scores.insert(KexiUserFeedbackAgent::RegionalSettingsArea, 2); totalFeedbackScore = 0; foreach (int s, scores.values()) { totalFeedbackScore += s; } donationScore = 20; donated = false; //qDebug() << "totalFeedbackScore:" << totalFeedbackScore; } ~Private() { delete msgWidget; if (!rccFname.isEmpty()) { QResource::unregisterResource(rccFname); } } int currentFeedbackScore() const { int score = 0; KexiUserFeedbackAgent *f = KexiMainWindowIface::global()->userFeedbackAgent(); KexiUserFeedbackAgent::Areas areas = f->enabledAreas(); for (QMap::ConstIterator it(scores.constBegin()); it!=scores.constEnd(); ++it) { if (areas & it.key()) { score += it.value(); } } //qDebug() << score; return score; } template T widgetOfClass(T parent, const char *widgetName) const { T w = parent->template findChild(widgetName); if (!w) { qWarning() << "NO SUCH widget" << widgetName << "in" << parent; } return w; } QWidget* widget(QWidget *parent, const char *widgetName) const { return widgetOfClass(parent, widgetName); } QObject* object(QObject *parent, const char *objectName) const { QObject *o = parent->findChild(objectName); if (!o) { qWarning() << "NO SUCH object" << objectName << "in" << parent; } return o; } void setProperty(QWidget *parent, const char *widgetName, const char *propertyName, const QVariant &value) { QWidget *w = widget(parent, widgetName); if (w) { w->setProperty(propertyName, value); } } QVariant property(QWidget *parent, const char *widgetName, const char *propertyName) const { QWidget *w = widget(parent, widgetName); return w ? w->property(propertyName) : QVariant(); } void connect(QWidget *parent, const char *widgetName, const char *signalName, QObject *receiver, const char *slotName) { QWidget *w = widget(parent, widgetName); if (w) { QObject::connect(w, signalName, receiver, slotName); } } void animatedHide(QWidget *parent, const char *widgetName) { QWidget *w = widget(parent, widgetName); if (!w) return; KexiFadeWidgetEffect *animation = new KexiFadeWidgetEffect(w); QObject::connect(animation, SIGNAL(destroyed()), w, SLOT(hide())); animation->start(); } QWidget* loadGui(const QString &guiFileName, QWidget *parentWidget = 0) { QString fname = findFilename(guiFileName); if (fname.isEmpty()) { qWarning() << "filename" << fname << "not found"; return 0; } QFile file(fname); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "could not open file" << fname; return 0; } QUiLoader loader; QWidget* widget = loader.load(&file, parentWidget); if (!widget) { qWarning() << "could load ui from file" << fname; } file.close(); return widget; } void updateStatusWidget() { QWidget *widget = loadGui("status.ui", statusScrollArea); if (!widget) { return; } int smallFontSize = qFloor((QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont).pointSizeF() + q->font().pointSizeF()) / 2.0); smallFont = q->font(); smallFont.setPointSizeF(smallFontSize); widget->setFont(smallFont); //delete statusWidget; statusWidget = widget; statusScrollArea->setWidget(statusWidget); setProperty(statusWidget, "contribution_progress", "minimumHeight", q->fontMetrics().height()); setProperty(statusWidget, "contribution_progress", "maximumHeight", q->fontMetrics().height()); label_involved_text_mask = property(statusWidget, "label_involved", "text").toString(); setProperty(statusWidget, "link_share_usage_info", "text", property(statusWidget, "link_share_usage_info", "text").toString().arg(totalFeedbackScore)); link_share_more_usage_info_mask = property(statusWidget, "link_share_more_usage_info", "text").toString(); setProperty(statusWidget, "link_donate", "text", property(statusWidget, "link_donate", "text").toString().arg(donationScore)); updateDonationInfo(); updateUserProgress(); updateContributionLinksVisibility(); // do not alter background palette QPalette pal(widget->palette()); pal.setColor(QPalette::Disabled, QPalette::Base, pal.color(QPalette::Normal, QPalette::Base)); widget->setPalette(pal); connect(statusWidget, "link_contribute_show_help", SIGNAL(linkActivated(QString)), q, SLOT(showContributionHelp())); connect(statusWidget, "link_share_usage_info", SIGNAL(linkActivated(QString)), q, SLOT(showShareUsageInfo())); connect(statusWidget, "link_share_more_usage_info", SIGNAL(linkActivated(QString)), q, SLOT(showShareUsageInfo())); connect(statusWidget, "link_show_contribution_details", SIGNAL(linkActivated(QString)), q, SLOT(showContributionDetails())); setProperty(statusWidget, "donation_url", "visible", false); connect(statusWidget, "link_donate", SIGNAL(linkActivated(QString)), q, SLOT(showDonation())); } void setUserProgress(int progress) { setProperty(statusWidget, "contribution_progress", "value", progress); setProperty(statusWidget, "label_involved", "text", label_involved_text_mask.arg(progress)); } void updateUserProgress() { int progress = 0; progress += currentFeedbackScore(); if (donated) { progress += donationScore; } setUserProgress(progress); } void updateContributionLinksVisibility() { KexiUserFeedbackAgent *f = KexiMainWindowIface::global()->userFeedbackAgent(); int availableLinks = 0; bool noneEnabled = f->enabledAreas() == KexiUserFeedbackAgent::NoAreas; bool allEnabled = f->enabledAreas() == KexiUserFeedbackAgent::AllAreas; setProperty(statusWidget, "share_usage_info", "visible", noneEnabled); if (noneEnabled) { availableLinks++; } setProperty(statusWidget, "share_more_usage_info", "visible", !noneEnabled && !allEnabled); if (!noneEnabled && !allEnabled) { availableLinks++; } setProperty(statusWidget, "link_share_more_usage_info", "text", link_share_more_usage_info_mask.arg(totalFeedbackScore - currentFeedbackScore())); setProperty(statusWidget, "lbl_contribute", "visible", availableLinks > 0); } void updateDonationInfo() { KConfigGroup configGroup(KSharedConfig::openConfig()->group("User Feedback")); QDateTime lastDonation = configGroup.readEntry("LastDonation", QDateTime()); if (lastDonation.isValid()) { int days = lastDonation.secsTo(QDateTime::currentDateTime()) / 60 / 60 / 24; if (days >= DONATION_INTERVAL) { donated = false; qDebug() << "last donation declared" << days << "days ago, next in" << (DONATION_INTERVAL - days) << "days."; } else if (days >= 0) { donated = true; } } //show always: setProperty(statusWidget, "donate", "visible", !donated); } enum CalloutAlignment { AlignToBar, AlignToWidget }; //! Aligns callout pointer position of msgWidget to widget named @a alignToWidgetName void setMessageWidgetCalloutPointerPosition( const QString& alignToWidgetName, CalloutAlignment calloutAlignment = AlignToBar) { //qDebug() << q->pos() << q->mapToGlobal(QPoint(0, 100)); QPoint p(q->mapToGlobal(QPoint(0, 100))); QWidget *alignToWidget = this->widget(statusWidget, alignToWidgetName.toLatin1()); if (alignToWidget) { p.setY( alignToWidget->mapToGlobal( QPoint(-5, alignToWidget->height() / 2)).y()); if (calloutAlignment == AlignToWidget) { p.setX(alignToWidget->mapToGlobal(QPoint(-5, 0)).x()); //qDebug() << p; } } else { qWarning() << alignToWidgetName << "not found!"; } msgWidget->setCalloutPointerPosition(p, alignToWidget); } //! Shows message widget taking maximum space within the welcome page //! Returns created layout for further use into @a layout. //! Created widge is assigned to msgWidget. //! Calls slot @a slotToCallAfterShow after animated showing, if provided. //! Call msgWidget->animatedShow() afterwards. void showMaximizedMessageWidget(const QString &alignToWidgetName, QPointer *layout, const char* slotToCallAfterShow, CalloutAlignment calloutAlignment = AlignToBar) { QWidget *alignToWidget = this->widget(statusWidget, alignToWidgetName.toLatin1()); int msgWidth; if (alignToWidget && calloutAlignment == AlignToWidget) { msgWidth = q->parentWidget()->width() - alignToWidget->width() - 10; } else { msgWidth = q->parentWidget()->width() - q->width(); } QWidget *widget = new QWidget; *layout = new QGridLayout(widget); if (msgWidth > 100) { // nice text margin (*layout)->setColumnMinimumWidth(0, 50); } //qDebug() << (q->parentWidget()->width() - q->width()) << "***"; KexiContextMessage msg(widget); if (msgWidget) { delete static_cast(msgWidget); } msgWidget = new KexiContextMessageWidget(q->parentWidget()->parentWidget(), 0, 0, msg); msgWidget->setCalloutPointerDirection(KMessageWidget::Right); msgWidget->setMessageType(KMessageWidget::Information); msgWidget->setCloseButtonVisible(true); int offset_y = 0; if (alignToWidget) { offset_y = alignToWidget->mapToGlobal(QPoint(0, 0)).y() - q->parentWidget()->mapToGlobal(QPoint(0, 0)).y(); } else { qWarning() << alignToWidgetName << "not found!"; } msgWidget->resize(msgWidth, q->parentWidget()->height() - offset_y); setMessageWidgetCalloutPointerPosition(alignToWidgetName, calloutAlignment); msgWidget->setResizeTrackingPolicy(Qt::Horizontal | Qt::Vertical); statusScrollArea->setEnabled(false); // async show to for speed up if (slotToCallAfterShow) { QObject::connect(msgWidget, SIGNAL(animatedShowFinished()), q, slotToCallAfterShow); } QObject::connect(msgWidget, SIGNAL(animatedHideFinished()), q, SLOT(slotMessageWidgetClosed())); } ScrollArea *statusScrollArea; QWidget *statusWidget; QVBoxLayout *lyr; QPointer msgWidget; QFont smallFont; QAction *helpAction; QAction *shareAction; QAction *cancelAction; QString label_involved_text_mask; QString link_share_more_usage_info_mask; QPointer contributionHelpLayout; QPointer contributionDetailsLayout; QPointer contributionDetailsWidget; QMap scores; QString countryMask; QString languageMask; bool detailsDataVisible; int totalFeedbackScore; int donationScore; bool donated; KexiWelcomeStatusBarGuiUpdater guiUpdater; private: QString rccFname; KexiWelcomeStatusBar *q; QMap dict; }; KexiWelcomeStatusBar::KexiWelcomeStatusBar(QWidget* parent) : QWidget(parent), d(new Private(this)) { d->lyr = new QVBoxLayout(this); init(); } KexiWelcomeStatusBar::~KexiWelcomeStatusBar() { delete d; } void KexiWelcomeStatusBar::init() { d->statusScrollArea = new ScrollArea(this); d->lyr->addWidget(d->statusScrollArea); d->updateStatusWidget(); QTimer::singleShot(10, &d->guiUpdater, SLOT(update())); } void KexiWelcomeStatusBar::showContributionHelp() { d->showMaximizedMessageWidget("link_contribute_show_help", &d->contributionHelpLayout, SLOT(slotShowContributionHelpContents())); d->msgWidget->animatedShow(); } void KexiWelcomeStatusBar::slotShowContributionHelpContents() { QWidget *helpWidget = d->loadGui("contribution_help.ui"); d->contributionHelpLayout->addWidget(helpWidget, 1, 1); d->msgWidget->setPaletteInherited(); } void KexiWelcomeStatusBar::slotMessageWidgetClosed() { d->statusScrollArea->setEnabled(true); d->updateDonationInfo(); d->updateUserProgress(); d->updateContributionLinksVisibility(); } void KexiWelcomeStatusBar::showShareUsageInfo() { if (!sender()) { return; } QWidget *widget = d->loadGui("status_strings.ui"); if (!widget) { return; } QLabel *lbl = widget->findChild("question"); if (!lbl) { return; } KexiContextMessage msg(lbl->text()); delete widget; if (!d->helpAction) { d->helpAction = new QAction(KStandardGuiItem::help().icon(), KStandardGuiItem::help().text(), this); connect(d->helpAction, SIGNAL(triggered()), this, SLOT(showContributionHelp())); } if (!d->shareAction) { d->shareAction = new QAction(KStandardGuiItem::yes().icon(), xi18n("Share"), this); connect(d->shareAction, SIGNAL(triggered()), this, SLOT(slotShareFeedback())); } if (!d->cancelAction) { d->cancelAction = new QAction(KStandardGuiItem::cancel().icon(), KStandardGuiItem::cancel().text(), this); QObject::connect(d->cancelAction, SIGNAL(triggered()), this, SLOT(slotCancelled())); } msg.addAction(d->helpAction, KexiContextMessage::AlignLeft); msg.addAction(d->shareAction); msg.addAction(d->cancelAction); if (d->msgWidget) { delete static_cast(d->msgWidget); } d->msgWidget = new KexiContextMessageWidget(parentWidget(), 0, 0, msg); d->msgWidget->setMessageType(KMessageWidget::Information); d->msgWidget->setCalloutPointerDirection(KMessageWidget::Right); d->setMessageWidgetCalloutPointerPosition(sender()->objectName()); d->statusScrollArea->setEnabled(false); d->msgWidget->setMaximumWidth(parentWidget()->width() - width()); d->msgWidget->setResizeTrackingPolicy(Qt::Horizontal); d->msgWidget->animatedShow(); } void KexiWelcomeStatusBar::showDonation() { if (!sender()) { return; } if (KMessageBox::Yes != KMessageBox::questionYesNo(this, xi18nc("@info donate to the project", "Kexi may be totally free, but its development is costly." "Power, hardware, office space, internet access, traveling for meetings - everything costs." "Direct donation is the easiest and fastest way to efficiently support the Kexi Project. " "Everyone, regardless of any degree of involvement can do so." "What do you receive for your donation? Kexi will become more feature-full and stable as " "contributors will be able to devote more time to Kexi. Not only you can " "expect new features, but you can also have an influence on what features are added!" "Currently we are accepting donations through BountySource (a funding platform " "for open-source software) using secure PayPal, Bitcoin and Google Wallet transfers." "Contact us at https://community.kde.org/Kexi/Contact " "for more information." "Thanks for your support!"), xi18n("Donate to the Project"), KGuiItem(xi18nc("@action:button Go to Donation", "Proceed to the Donation Web Page"), QIcon(":/icons/heart.png")), KGuiItem(xi18nc("Do not donate now", "Not Now")), QString(), KMessageBox::Notify | KMessageBox::AllowLink)) { return; } QUrl donationUrl(d->property(this, "donation_url", "text").toString()); if (donationUrl.isValid()) { QDesktopServices::openUrl(donationUrl); d->donated = true; d->updateStatusWidget(); KConfigGroup configGroup(KSharedConfig::openConfig()->group("User Feedback")); int donationsCount = configGroup.readEntry("DonationsCount", 0); configGroup.writeEntry("LastDonation", QDateTime::currentDateTime()); configGroup.writeEntry("DonationsCount", donationsCount + 1); } else { qWarning() << "Invalid donation URL" << donationUrl; } } void KexiWelcomeStatusBar::slotShareFeedback() { d->statusScrollArea->setEnabled(true); d->msgWidget->animatedHide(); KexiMainWindowIface::global()->userFeedbackAgent() ->setEnabledAreas(KexiUserFeedbackAgent::AllAreas); d->animatedHide(d->statusWidget, "share_usage_info"); d->animatedHide(d->statusWidget, "share_more_usage_info"); d->animatedHide(d->statusWidget, "lbl_contribute"); d->updateUserProgress(); } void KexiWelcomeStatusBar::slotCancelled() { d->statusScrollArea->setEnabled(true); } // Contribution Details BEGIN void KexiWelcomeStatusBar::showContributionDetails() { d->showMaximizedMessageWidget("link_show_contribution_details", &d->contributionDetailsLayout, 0, KexiWelcomeStatusBar::Private::AlignToWidget); d->contributionDetailsLayout->setColumnMinimumWidth(0, 6); // smaller d->contributionDetailsWidget = d->loadGui("contribution_details.ui"); KexiUserFeedbackAgent *f = KexiMainWindowIface::global()->userFeedbackAgent(); d->setProperty(d->contributionDetailsWidget, "group_share", "checked", f->enabledAreas() != KexiUserFeedbackAgent::NoAreas); d->setProperty(d->contributionDetailsWidget, "group_basic", "title", d->property(d->contributionDetailsWidget, "group_basic", "title") .toString().arg(d->scores.value(KexiUserFeedbackAgent::BasicArea))); updateContributionGroupCheckboxes(); d->setProperty(d->contributionDetailsWidget, "group_system", "title", d->property(d->contributionDetailsWidget, "group_system", "title") .toString().arg(d->scores.value(KexiUserFeedbackAgent::SystemInfoArea))); d->connect(d->contributionDetailsWidget, "group_system", SIGNAL(toggled(bool)), this, SLOT(slotShareContributionDetailsGroupToggled(bool))); d->setProperty(d->contributionDetailsWidget, "group_screen", "title", d->property(d->contributionDetailsWidget, "group_screen", "title") .toString().arg(d->scores.value(KexiUserFeedbackAgent::ScreenInfoArea))); d->connect(d->contributionDetailsWidget, "group_screen", SIGNAL(toggled(bool)), this, SLOT(slotShareContributionDetailsGroupToggled(bool))); d->setProperty(d->contributionDetailsWidget, "group_regional_settings", "title", d->property(d->contributionDetailsWidget, "group_regional_settings", "title") .toString().arg(d->scores.value(KexiUserFeedbackAgent::RegionalSettingsArea))); d->connect(d->contributionDetailsWidget, "group_regional_settings", SIGNAL(toggled(bool)), this, SLOT(slotShareContributionDetailsGroupToggled(bool))); d->detailsDataVisible = false; slotShareContributionDetailsToggled( d->property(d->contributionDetailsWidget, "group_share", "checked").toBool()); d->detailsDataVisible = true; // to switch off slotToggleContributionDetailsDataVisibility(); d->connect(d->contributionDetailsWidget, "group_share", SIGNAL(toggled(bool)), this, SLOT(slotShareContributionDetailsToggled(bool))); d->connect(d->contributionDetailsWidget, "link_show_shared_info", SIGNAL(linkActivated(QString)), this, SLOT(slotToggleContributionDetailsDataVisibility())); d->setProperty(d->contributionDetailsWidget, "label_where_is_info_sent", "visible", false); ScrollArea *contributionDetailsArea = new ScrollArea(d->msgWidget); d->contributionDetailsLayout->addWidget(contributionDetailsArea, 1, 1); contributionDetailsArea->setWidget(d->contributionDetailsWidget); d->msgWidget->animatedShow(); d->msgWidget->setPaletteInherited(); } void KexiWelcomeStatusBar::updateContributionGroupCheckboxes() { KexiUserFeedbackAgent *f = KexiMainWindowIface::global()->userFeedbackAgent(); d->setProperty(d->contributionDetailsWidget, "group_system", "checked", bool(f->enabledAreas() & KexiUserFeedbackAgent::SystemInfoArea)); d->setProperty(d->contributionDetailsWidget, "group_screen", "checked", bool(f->enabledAreas() & KexiUserFeedbackAgent::ScreenInfoArea)); d->setProperty(d->contributionDetailsWidget, "group_regional_settings", "checked", bool(f->enabledAreas() & KexiUserFeedbackAgent::RegionalSettingsArea)); } void KexiWelcomeStatusBar::slotShareContributionDetailsToggled(bool on) { //qDebug() << sender(); QWidget* group_share = d->widget(d->contributionDetailsWidget, "group_share"); KexiUserFeedbackAgent *f = KexiMainWindowIface::global()->userFeedbackAgent(); if (sender() == group_share) { f->setEnabledAreas(on ? KexiUserFeedbackAgent::AllAreas : KexiUserFeedbackAgent::NoAreas); updateContributionGroupCheckboxes(); } if (!group_share) { return; } for (int i=0; i < group_share->layout()->count(); i++) { QWidget *w = group_share->layout()->itemAt(i)->widget(); if (w) { w->setVisible(on); } } if (d->detailsDataVisible) { slotToggleContributionDetailsDataVisibility(); } // fill shared values QLocale locale; foreach(QLabel* lbl, d->contributionDetailsWidget->findChildren()) { if (lbl->objectName().startsWith(QLatin1String("value_"))) { QString name = lbl->objectName().mid(6); // cut "value_" QVariant value; if (name == QLatin1String("screen_size")) { value = QString("%1 x %2").arg(f->value("screen_width").toString()) .arg(f->value("screen_height").toString()); } else if (name == QLatin1String("country")) { if (d->countryMask.isEmpty()) { d->countryMask = lbl->text(); } value = d->countryMask .arg(f->value(name).toString() /*!< @todo KEXI3 port KLocale::global()->countryCodeToName(f->value(name).toString()) */) .arg(f->value(name).toString()); } else if (name == QLatin1String("language")) { if (d->languageMask.isEmpty()) { d->languageMask = lbl->text(); } value = d->languageMask .arg(f->value(name).toString() /*!< @todo KEXI3 port KLocale::global()->languageCodeToName(f->value(name).toString()) */) .arg(f->value(name).toString()); } else { value = f->value(name); } if (value.type() == QVariant::Bool) { value = value.toBool() ? KStandardGuiItem::yes().plainText() : KStandardGuiItem::no().plainText(); } if (!value.isNull()) { lbl->setText(value.toString()); } } else if (lbl->objectName().startsWith(QLatin1String("desc_"))) { lbl->setFont(d->smallFont); } } QLabel* lbl; KConfigGroup configGroup(KSharedConfig::openConfig()->group("User Feedback")); if ((lbl = d->contributionDetailsWidget->findChild("value_recent_donation"))) { QDateTime lastDonation = configGroup.readEntry("LastDonation", QDateTime()); QString recentDonation = "-"; if (lastDonation.isValid()) { int days = lastDonation.secsTo(QDateTime::currentDateTime()) / 60 / 60 / 24; if (days == 0) { recentDonation = xi18nc("Donation today", "today"); } else if (days > 0) { recentDonation = xi18ncp("Recent donation date (xx days)", "%1 (1 day)", "%1 (%2 days)", locale.toString(lastDonation), days); } } lbl->setText(recentDonation); } if ((lbl = d->contributionDetailsWidget->findChild("value_donations_count"))) { int donationsCount = configGroup.readEntry("DonationsCount", 0); if (donationsCount == 0) { lbl->setText(QString::number(donationsCount)); } else { lbl->setText(xi18nc("donations count", "%1 (thanks!)", donationsCount)); } } } static void setArea(KexiUserFeedbackAgent::Areas *areas, KexiUserFeedbackAgent::Area area, bool on) { *areas |= area; if (!on) { *areas ^= area; } } void KexiWelcomeStatusBar::slotShareContributionDetailsGroupToggled(bool on) { if (!sender()) { return; } const QString name = sender()->objectName(); KexiUserFeedbackAgent *f = KexiMainWindowIface::global()->userFeedbackAgent(); KexiUserFeedbackAgent::Areas areas = f->enabledAreas(); //qDebug() << areas; if (name == "group_system") { setArea(&areas, KexiUserFeedbackAgent::SystemInfoArea, on); } else if (name == "group_screen") { setArea(&areas, KexiUserFeedbackAgent::ScreenInfoArea, on); } else if (name == "group_regional_settings") { setArea(&areas, KexiUserFeedbackAgent::RegionalSettingsArea, on); } if (areas) { areas |= KexiUserFeedbackAgent::AnonymousIdentificationArea; } f->setEnabledAreas(areas); //qDebug() << f->enabledAreas(); } void KexiWelcomeStatusBar::slotToggleContributionDetailsDataVisibility() { QWidget* value_app_ver = d->widget(d->contributionDetailsWidget, "value_app_ver"); if (!value_app_ver) { return; } d->detailsDataVisible = !d->detailsDataVisible; if (d->detailsDataVisible) { d->setProperty(d->contributionDetailsWidget, "link_show_shared_info", "visible", false); d->setProperty(d->contributionDetailsWidget, "label_where_is_info_sent", "visible", true); } bool show = d->contributionDetailsWidget->isVisible(); QList list; d->contributionDetailsWidget->hide(); QWidget* group_basic = d->widget(d->contributionDetailsWidget, "group_basic"); if (group_basic) { list += group_basic->findChildren(); } QWidget* group_system = d->widget(d->contributionDetailsWidget, "group_system"); if (group_system) { list += group_system->findChildren(); } QWidget* group_screen = d->widget(d->contributionDetailsWidget, "group_screen"); if (group_screen) { list += group_screen->findChildren(); } QWidget* group_regional_settings = d->widget(d->contributionDetailsWidget, "group_regional_settings"); if (group_regional_settings) { list += group_regional_settings->findChildren(); } foreach (QWidget* w, list) { if (qobject_cast(w) && !w->objectName().startsWith(QLatin1String("desc_"))) { //qDebug() << "+++" << w; w->setVisible(d->detailsDataVisible); } } if (show) { d->contributionDetailsWidget->show(); } } // Contribution Details END #include "KexiWelcomeStatusBar.moc" diff --git a/src/main/status/CMakeLists.txt b/src/main/status/CMakeLists.txt index ddf98b93e..e09e2af95 100644 --- a/src/main/status/CMakeLists.txt +++ b/src/main/status/CMakeLists.txt @@ -1,31 +1,31 @@ set(status_qrc "${CMAKE_CURRENT_SOURCE_DIR}/status.qrc") -set(status_prefix "${PROJECT_BASE_NAME_LOWER}/status/${PROJECT_STABLE_VERSION_MAJOR}.${PROJECT_STABLE_VERSION_MINOR}.0") +set(status_prefix "${KEXI_BASE_PATH}/status") set(status_rcc_dir "${CMAKE_BINARY_DIR}/bin/data/${status_prefix}") set(status_rcc "${status_rcc_dir}/status.rcc") add_custom_target(makedir_status_rcc COMMAND ${CMAKE_COMMAND} -E make_directory ${status_rcc_dir} ) if(${Qt5Core_VERSION} VERSION_LESS 5.5.0) add_custom_command(OUTPUT "${status_rcc}" COMMAND "${Qt5Core_RCC_EXECUTABLE}" --binary --verbose --output "${status_rcc}" "${status_qrc}" DEPENDS "${status_qrc}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMENT "Building external Qt resource ${status_rcc}" VERBATIM) add_custom_target(build_status_rcc DEPENDS "${status_rcc}" SOURCES "${status_qrc}") else() qt5_add_binary_resources(build_status_rcc "${status_qrc}" OPTIONS -binary DESTINATION "${status_rcc}") endif() add_dependencies(build_status_rcc makedir_status_rcc) add_dependencies(keximain build_status_rcc) install(FILES status.ui contribution_details.ui contribution_help.ui status_strings.ui ${status_rcc} DESTINATION "${CMAKE_INSTALL_FULL_DATADIR}/${status_prefix}") diff --git a/src/migration/CMakeLists.txt b/src/migration/CMakeLists.txt index 10a1f979b..590c259b5 100644 --- a/src/migration/CMakeLists.txt +++ b/src/migration/CMakeLists.txt @@ -1,112 +1,111 @@ include_directories(${CMAKE_SOURCE_DIR}/src/core ${CMAKE_SOURCE_DIR}/src/widget ${CMAKE_BINARY_DIR}/src/widget ) add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44000) option(KEXI_MIGRATEMANAGER_DEBUG "Enable debugging for the migrate driver manager" OFF) set(KEXI_MIGRATE_PLUGIN_INSTALL_DIR ${KEXI_PLUGIN_INSTALL_DIR}/migrate) # ----------------------- function(build_and_install_kexi_migrate_driver _name _srcs _extra_libs _includes _defines) set(_target keximigrate_${_name}) ecm_create_qm_loader(_srcs ${_target}_qt) add_library(${_target} MODULE ${_srcs}) target_link_libraries(${_target} PUBLIC keximigrate ${_extra_libs} ) target_include_directories(${_target} PRIVATE ${_includes}) target_compile_definitions(${_target} PRIVATE ${_defines}) # Needed for examples and autotests: set_target_properties(${_target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/plugins/migrate") install(TARGETS ${_target} DESTINATION ${KEXI_MIGRATE_PLUGIN_INSTALL_DIR}) endfunction() # ----------------------- add_subdirectory(tsv) find_package(MySQL) macro_log_feature(MYSQL_FOUND "libmysqlclient" "MySQL Client Library" "http://www.mysql.com" FALSE "" "Required by Kexi MySQL migration driver") if(MYSQL_FOUND) add_subdirectory(mysql) endif() find_package(KexiPostgreSQL) macro_log_feature(POSTGRESQL_FOUND "libpq" "C application programmer's interface to PostgreSQL" "http://www.postgresql.org" FALSE "" "Required by Kexi PostgreSQL migration driver") if(POSTGRESQL_FOUND) add_subdirectory(postgresql) endif() if(false) # TODO KEXI3 find_package(FreeTDS) macro_log_feature(FREETDS_FOUND "FreeTDS" "Open source implementation of the TDS (Tabular Data Stream) protocol" "http://www.freetds.org" FALSE "" "Required by Kexi Sybase migration driver") if(FREETDS_FOUND) add_subdirectory(sybase) endif() find_package(XBase) macro_log_feature(XBASE_FOUND "XBase" "XBase compatible C++ class library" "http://linux.techass.com/projects/xdb" FALSE "" "Required by Kexi XBase migration driver") if(XBASE_FOUND) add_subdirectory(xbase) endif() endif() # KEXI3 find_package(GLIB2) set(_REQUIRED_BY_MDB "Required by Kexi MS Access migration driver") macro_log_feature(GLIB2_FOUND "GLIB2" "Common C routines used by GTK+ and other libs" "http://www.gtk.org" FALSE "" ${_REQUIRED_BY_MDB}) include(FindIconv) macro_log_feature(ICONV_FOUND "Iconv" "A library to convert between different character encodings" "https://www.gnu.org/software/libiconv" FALSE "" ${_REQUIRED_BY_MDB}) if(GLIB2_FOUND AND ICONV_FOUND) add_subdirectory(mdb) endif() add_subdirectory(tests) ########### next target ############### set(keximigrate_LIB_SRCS AlterSchemaTableModel.cpp KexiMigratePluginMetaData.cpp keximigrate.cpp keximigratedata.cpp KexiSqlMigrate.cpp migratemanager.cpp importwizard.cpp importtablewizard.cpp importoptionsdlg.cpp AlterSchemaWidget.cpp) -add_library(keximigrate SHARED ${keximigrate_LIB_SRCS}) +kexi_add_library(keximigrate SHARED ${keximigrate_LIB_SRCS}) target_link_libraries(keximigrate PUBLIC kexicore kexiextendedwidgets kexiutils KDb Qt5::Gui ) generate_export_header(keximigrate) -set_target_properties(keximigrate PROPERTIES VERSION ${GENERIC_PROJECT_LIB_VERSION} SOVERSION ${GENERIC_PROJECT_LIB_SOVERSION}) install(TARGETS keximigrate ${INSTALL_TARGETS_DEFAULT_ARGS}) ########### install files ############### if(FALSE) # TODO: install when we move to independent place install(FILES KexiMigratePluginMetaData.h keximigrate.h keximigratedata.h KexiSqlMigrate.h migratemanager.h DESTINATION ${INCLUDE_INSTALL_DIR}/kexidb COMPONENT Devel ) endif() diff --git a/src/migration/migratemanager.cpp b/src/migration/migratemanager.cpp index dbb79b099..205a00a9c 100644 --- a/src/migration/migratemanager.cpp +++ b/src/migration/migratemanager.cpp @@ -1,317 +1,317 @@ /* This file is part of the KDE project Daniel Molkentin Joseph Wenninger Copyright (C) 2003-2016 Jarosław Staniek This program 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 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "migratemanager.h" #include "migratemanager_p.h" #include "keximigrate.h" #include "KexiMigratePluginMetaData.h" #include #include #include #include #include #include #include #include #ifdef KEXI_MIGRATEMANAGER_DEBUG #define KexiMigrateManagerDebug qDebug() #else #define KexiMigrateManagerDebug while (0) qDebug() #endif using namespace KexiMigration; Q_GLOBAL_STATIC(MigrateManagerInternal, s_self) -Q_GLOBAL_STATIC_WITH_ARGS(KexiJsonTrader, KexiMigrateTrader_instance, (KEXI_BASE_NAME_LOWER "/migrate")) +Q_GLOBAL_STATIC_WITH_ARGS(KexiJsonTrader, KexiMigrateTrader_instance, (KEXI_BASE_PATH "/migrate")) MigrateManagerInternal::MigrateManagerInternal() : m_lookupDriversNeeded(true) { } MigrateManagerInternal::~MigrateManagerInternal() { KexiMigrateManagerDebug; clear(); KexiMigrateManagerDebug << "ok"; } void MigrateManagerInternal::clear() { KexiMigrateManagerDebug << "Clearing drivers..."; qDeleteAll(m_drivers); m_drivers.clear(); qDeleteAll(m_driversMetaData); m_driversMetaData.clear(); } void MigrateManagerInternal::slotAppQuits() { if (qApp && !qApp->topLevelWidgets().isEmpty() && qApp->topLevelWidgets().first()->isVisible()) { return; //what a hack! - we give up when app is still there } clear(); } bool MigrateManagerInternal::lookupDrivers() { if (!m_lookupDriversNeeded) return true; if (qApp) { connect(qApp, &QApplication::aboutToQuit, this, &MigrateManagerInternal::slotAppQuits); } m_lookupDriversNeeded = false; clearResult(); QList offers = KexiMigrateTrader_instance->query(QLatin1String("Kexi/MigrationDriver")); const QString expectedVersion = QString::fromLatin1("%1.%2") .arg(KexiMigration::version().major()).arg(KexiMigration::version().minor()); for(const QPluginLoader *loader : offers) { QScopedPointer metaData(new KexiMigratePluginMetaData(*loader)); if (m_driversMetaData.contains(metaData->id())) { qWarning() << "Migration driver with ID" << metaData->id() << "already found at" << m_driversMetaData.value(metaData->id())->fileName() << "-- skipping another at" << metaData->fileName(); continue; } //! @todo Similar version check could be merged with KDbDriverManager if (metaData->version() != expectedVersion) { qWarning() << QString("Migration driver '%1' (%2) has version '%3' but " "KexiMigration library requires version '%4'\n" " -- skipping this driver!") .arg(metaData->id()).arg(metaData->fileName()) .arg(metaData->version()) .arg(expectedVersion); m_possibleProblems += QString("Migration driver \"%1\" (%2) has version \"%3\" " "but required version is \"%4\"") .arg(metaData->id()).arg(metaData->fileName()) .arg(metaData->version()) .arg(expectedVersion); continue; } foreach (const QString& mimeType, metaData->mimeTypes()) { m_metadata_by_mimetype.insertMulti(mimeType, metaData.data()); } foreach (const QString& sourceDriverId, metaData->supportedSourceDrivers()) { m_metadataBySourceDrivers.insertMulti(sourceDriverId, metaData.data()); } m_driversMetaData.insert(metaData->id(), metaData.data()); KexiMigrateManagerDebug << "registered driver" << metaData->id() << '(' << metaData->fileName() << ")"; metaData.take(); } if (m_driversMetaData.isEmpty()) { m_result = KDbResult(ERR_DRIVERMANAGER, xi18n("Could not find any migration database drivers.")); return false; } return true; } QStringList MigrateManagerInternal::driverIds() { if (!lookupDrivers()) { return QStringList(); } if (m_driversMetaData.isEmpty() && result().isError()) { return QStringList(); } return m_driversMetaData.keys(); } const KexiMigratePluginMetaData* MigrateManagerInternal::driverMetaData(const QString &id) { if (!lookupDrivers()) { return 0; } const KexiMigratePluginMetaData *metaData = m_driversMetaData.value(id.toLower()); if (!metaData || m_result.isError()) { m_result = KDbResult(ERR_DRIVERMANAGER, tr("Could not find migration driver \"%1\".").arg(id)); } return metaData; } QStringList MigrateManagerInternal::driverIdsForMimeType(const QString &mimeType) { if (!lookupDrivers()) { return QStringList(); } const QList metaDatas(m_metadata_by_mimetype.values(mimeType.toLower())); QStringList result; for (const KexiMigratePluginMetaData* metaData : metaDatas) { result.append(metaData->id()); } return result; } QStringList MigrateManagerInternal::driverIdsForSourceDriver(const QString &sourceDriverId) { if (!lookupDrivers()) { return QStringList(); } QStringList result; for (const KexiMigratePluginMetaData* metaData : m_metadataBySourceDrivers.values(sourceDriverId.toLower())) { result.append(metaData->id()); } return result; } KexiMigrate* MigrateManagerInternal::driver(const QString& id) { if (!lookupDrivers()) { qWarning() << "lookupDrivers failed"; return 0; } clearResult(); KexiMigrateManagerDebug << "loading" << id; KexiMigrate *driver = m_drivers.value(id.toLatin1().toLower()); if (driver) { return driver; //cached } if (!m_driversMetaData.contains(id.toLower())) { m_result = KDbResult(ERR_OBJECT_NOT_FOUND, tr("Could not find migration driver \"%1\".").arg(id)); return 0; } const KexiMigratePluginMetaData *metaData = m_driversMetaData.value(id.toLower()); KPluginFactory *factory = qobject_cast(metaData->instantiate()); if (!factory) { m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, tr("Could not load migration driver's plugin file \"%1\".") .arg(metaData->fileName())); QPluginLoader loader(metaData->fileName()); // use this to get the message (void)loader.load(); m_result.setServerMessage(loader.errorString()); qWarning() << m_result.message() << m_result.serverMessage(); return 0; } driver = factory->create(); if (!driver) { m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, tr("Could not open migration driver \"%1\" from plugin file \"%2\".") .arg(metaData->id()) .arg(metaData->fileName())); qWarning() << m_result.message(); return 0; } driver->setMetaData(metaData); m_drivers.insert(id.toLower(), driver); return driver; } QStringList MigrateManagerInternal::possibleProblemsMessage() const { return m_possibleProblems; } QStringList MigrateManagerInternal::supportedFileMimeTypes() { if (!lookupDrivers()) { qWarning() << "lookupDrivers failed"; return QStringList(); } return m_metadata_by_mimetype.uniqueKeys(); } QStringList MigrateManagerInternal::supportedSourceDriverIds() { if (!lookupDrivers()) { qWarning() << "lookupDrivers failed"; return QStringList(); } return m_metadataBySourceDrivers.uniqueKeys(); } // --------------------------- // --- DriverManager impl. --- // --------------------------- MigrateManager::MigrateManager() : QObject(0) { setObjectName("KexiMigrate::MigrateManager"); } MigrateManager::~MigrateManager() { KexiMigrateManagerDebug; } QStringList MigrateManager::driverIdsForMimeType(const QString &mimeType) { return s_self->driverIdsForMimeType(mimeType); } QStringList MigrateManager::driverIdsForSourceDriver(const QString &sourceDriverId) { return s_self->driverIdsForSourceDriver(sourceDriverId); } KexiMigrate* MigrateManager::driver(const QString &id) { return s_self->driver(id); } QString MigrateManager::possibleProblemsMessage() const { if (s_self->possibleProblemsMessage().isEmpty()) return QString(); QString str = "
    "; for(const QString &message : s_self->possibleProblemsMessage()) { str += (QString::fromLatin1("
  • ") + message + QString::fromLatin1("
  • ")); } str += "
"; return str; } QStringList MigrateManager::supportedFileMimeTypes() { return s_self->supportedFileMimeTypes(); } QStringList MigrateManager::supportedSourceDriverIds() { return s_self->supportedSourceDriverIds(); } KDbResult MigrateManager::result() const { return s_self->result(); } const KDbResultable* MigrateManager::resultable() const { return s_self; } diff --git a/src/mobile/CMakeLists.txt b/src/mobile/CMakeLists.txt index ae42aefed..49b332244 100644 --- a/src/mobile/CMakeLists.txt +++ b/src/mobile/CMakeLists.txt @@ -1,29 +1,29 @@ cmake_minimum_required(VERSION 2.8.0) find_package(Qt4 REQUIRED) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src/core ${CMAKE_SOURCE_DIR}/src/widget ) set(KexiMobile_SRCS main.cpp KexiMobileMainWindow.cpp KexiMobileWidget.cpp KexiMobileNavigator.cpp KexiMobileToolbar.cpp ) qt4_automoc(${KexiMobile_SRCS}) -add_executable(keximobile ${KexiMobile_SRCS}) +kexi_add_executable(keximobile ${KexiMobile_SRCS}) target_link_libraries(keximobile Qt5::Core Qt5::Gui kexiextendedwidgets kexicore kdeui KDb ) install(TARGETS keximobile ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/plugins/forms/CMakeLists.txt b/src/plugins/forms/CMakeLists.txt index 0523f8d14..db691d0b7 100644 --- a/src/plugins/forms/CMakeLists.txt +++ b/src/plugins/forms/CMakeLists.txt @@ -1,89 +1,87 @@ include_directories( ${CMAKE_SOURCE_DIR}/src/core ${CMAKE_SOURCE_DIR}/src/widget/utils ${CMAKE_SOURCE_DIR}/src/widget ${CMAKE_SOURCE_DIR}/src/formeditor) # the form plugin set(kexi_formplugin_SRCS kexiforms.cpp) add_library(kexi_formplugin MODULE ${kexi_formplugin_SRCS}) kcoreaddons_desktop_to_json(kexi_formplugin kexi_formplugin.desktop) target_link_libraries(kexi_formplugin kexicore kexiguiutils kexidatatable kexiextendedwidgets kformdesigner kexiformutils KPropertyWidgets ) install(TARGETS kexi_formplugin DESTINATION ${KEXI_PLUGIN_INSTALL_DIR}) # the form utils lib set(kexiformutils_LIB_SRCS # kexiformdataiteminterface.cpp kexidataawarewidgetinfo.cpp KexiFormScrollAreaWidget.cpp kexiformscrollview.cpp kexidbtextwidgetinterface.cpp kexiformmanager.cpp kexidatasourcepage.cpp kexiformpart.cpp kexiformview.cpp kexidbfactorybase.cpp widgets/kexidbutils.cpp widgets/kexidbautofield.cpp widgets/kexidbform.cpp widgets/kexidblabel.cpp widgets/kexidbimagebox.cpp widgets/KexiDBPushButton.cpp widgets/kexiframe.cpp widgets/kexidblineedit.cpp widgets/kexidbcheckbox.cpp widgets/kexidbtextedit.cpp widgets/kexidbcombobox.cpp widgets/kexidbcommandlinkbutton.cpp widgets/kexidbslider.cpp widgets/kexidbprogressbar.cpp widgets/kexidbdatepicker.cpp ) #obsolete widgets/kexidbdoublespinbox.cpp #obsolete widgets/kexidbintspinbox.cpp -add_library(kexiformutils SHARED ${kexiformutils_LIB_SRCS}) +kexi_add_library(kexiformutils SHARED ${kexiformutils_LIB_SRCS}) generate_export_header(kexiformutils) target_link_libraries(kexiformutils PRIVATE kexicore kexiextendedwidgets kformdesigner kexiutils kexidataviewcommon kexidatatable kexiguiutils KDb KPropertyWidgets Qt5::Gui Qt5::Xml ) set(kexiformutils_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/plugins/forms ${CMAKE_SOURCE_DIR}/src/plugins/forms/widgets ) target_include_directories(kexiformutils PUBLIC "$" ) -set_target_properties(kexiformutils PROPERTIES VERSION ${GENERIC_PROJECT_LIB_VERSION} - SOVERSION ${GENERIC_PROJECT_LIB_SOVERSION}) install(TARGETS kexiformutils ${INSTALL_TARGETS_DEFAULT_ARGS}) add_subdirectory(widgets) diff --git a/src/widget/CMakeLists.txt b/src/widget/CMakeLists.txt index 5a5e7616d..59392bf90 100644 --- a/src/widget/CMakeLists.txt +++ b/src/widget/CMakeLists.txt @@ -1,106 +1,105 @@ add_subdirectory( dataviewcommon ) add_subdirectory( relations ) add_subdirectory( undo ) add_subdirectory( utils ) if(KEXI_MOBILE) else() add_subdirectory( tableview ) endif() add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44023) include_directories(${CMAKE_SOURCE_DIR}/src/widget/tableview ${CMAKE_SOURCE_DIR}/src/core) ########### next target ############### set(kexiextendedwidgets_LIB_SRCS fields/KexiFieldComboBox.cpp fields/KexiFieldListModel.cpp fields/KexiFieldListModelItem.cpp fields/KexiFieldListView.cpp navigator/KexiProjectModel.cpp navigator/KexiProjectModelItem.cpp navigator/KexiProjectItemDelegate.cpp navigator/KexiProjectNavigator.cpp navigator/KexiProjectTreeView.cpp properties/KexiCustomPropertyFactory.cpp properties/KexiCustomPropertyFactory_p.cpp properties/KexiPropertyEditorView.cpp properties/KexiPropertyPaneViewBase.cpp kexiquerydesignersqleditor.cpp kexiqueryparameters.cpp kexisectionheader.cpp kexidbdrivercombobox.cpp kexieditor.cpp KexiDataSourceComboBox.cpp KexiObjectInfoLabel.cpp kexicharencodingcombobox.cpp KexiDBTitlePage.cpp KexiProjectSelectorWidget.cpp kexislider.cpp KexiServerDriverNotFoundMessage.cpp KexiNameWidget.cpp KexiNameDialog.cpp KexiStartupFileHandler.cpp KexiListView.cpp ) if (KEXI_MOBILE) else () list(APPEND kexiextendedwidgets_LIB_SRCS #navigator/KexiProjectListView.cpp #navigator/KexiProjectListViewItem.cpp kexidbconnectionwidget.cpp # TODO replace use of KexiProjectListView and KexiProjectListViewList (with KexiProjectNavigator) # in kexiactionselectiondialog and remove them kexiprjtypeselector.cpp KexiConnectionSelectorWidget.cpp KexiFileWidget.cpp KexiFileDialog.cpp KexiPasswordWidget.cpp KexiDBPasswordDialog.cpp ) ki18n_wrap_ui(kexiextendedwidgets_LIB_SRCS KexiConnectionSelector.ui kexidbconnectionwidget.ui kexidbconnectionwidgetdetails.ui kexiprjtypeselector.ui KexiPasswordWidget.ui ) endif () ki18n_wrap_ui(kexiextendedwidgets_LIB_SRCS KexiDBTitlePage.ui KexiProjectSelector.ui ) -add_library(kexiextendedwidgets SHARED ${kexiextendedwidgets_LIB_SRCS}) +kexi_add_library(kexiextendedwidgets SHARED ${kexiextendedwidgets_LIB_SRCS}) generate_export_header(kexiextendedwidgets BASE_NAME kexiextwidgets) target_link_libraries(kexiextendedwidgets PRIVATE kexidataviewcommon kexiutils kexiguiutils kexicore KDb KPropertyWidgets KF5::TextWidgets # KTextEdit KF5::Codecs # KCharsets PUBLIC KF5::KIOFileWidgets # KFileWidget KF5::TextEditor ) -set_target_properties(kexiextendedwidgets PROPERTIES VERSION ${GENERIC_PROJECT_LIB_VERSION} SOVERSION ${GENERIC_PROJECT_LIB_SOVERSION} ) install(TARGETS kexiextendedwidgets ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/widget/dataviewcommon/CMakeLists.txt b/src/widget/dataviewcommon/CMakeLists.txt index 66d670a7b..3e0513548 100644 --- a/src/widget/dataviewcommon/CMakeLists.txt +++ b/src/widget/dataviewcommon/CMakeLists.txt @@ -1,32 +1,31 @@ include_directories(${CMAKE_SOURCE_DIR}/src/widget ${CMAKE_SOURCE_DIR}/src/core) ########### next target ############### set(kexidataviewcommon_LIB_SRCS kexidataawareobjectiface.cpp kexidataawarepropertyset.cpp kexidataawareview.cpp kexidataprovider.cpp kexiformdataiteminterface.cpp ) -add_library(kexidataviewcommon SHARED ${kexidataviewcommon_LIB_SRCS}) +kexi_add_library(kexidataviewcommon SHARED ${kexidataviewcommon_LIB_SRCS}) generate_export_header(kexidataviewcommon) target_link_libraries( kexidataviewcommon kexicore kexiutils kexiguiutils KDb KPropertyWidgets KF5::KIOWidgets KF5::XmlGui #KActionCollection... ) -set_target_properties(kexidataviewcommon PROPERTIES VERSION ${GENERIC_PROJECT_LIB_VERSION} SOVERSION ${GENERIC_PROJECT_LIB_SOVERSION} ) install(TARGETS kexidataviewcommon ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/widget/relations/CMakeLists.txt b/src/widget/relations/CMakeLists.txt index a2f009dba..65059bddb 100644 --- a/src/widget/relations/CMakeLists.txt +++ b/src/widget/relations/CMakeLists.txt @@ -1,31 +1,29 @@ include_directories(${CMAKE_SOURCE_DIR}/src/core) ########### next target ############### set(kexirelationsview_LIB_SRCS KexiRelationsScrollArea.cpp KexiRelationsConnection.cpp KexiRelationsTableContainer.cpp KexiRelationsTableContainer_p.cpp KexiRelationsView.cpp ) - -add_library(kexirelationsview SHARED ${kexirelationsview_LIB_SRCS}) +kexi_add_library(kexirelationsview SHARED ${kexirelationsview_LIB_SRCS}) generate_export_header(kexirelationsview) target_link_libraries( kexirelationsview kexicore kexiextendedwidgets kexiutils KDb Qt5::Core Qt5::Gui ) -set_target_properties(kexirelationsview PROPERTIES VERSION ${GENERIC_PROJECT_LIB_VERSION} SOVERSION ${GENERIC_PROJECT_LIB_SOVERSION} ) install(TARGETS kexirelationsview ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/widget/tableview/CMakeLists.txt b/src/widget/tableview/CMakeLists.txt index 975647442..2ef13247a 100644 --- a/src/widget/tableview/CMakeLists.txt +++ b/src/widget/tableview/CMakeLists.txt @@ -1,45 +1,44 @@ include_directories(${CMAKE_SOURCE_DIR}/src/core) ########### next target ############### set(kexidatatable_LIB_SRCS KexiTableScrollArea.cpp KexiTableScrollArea_p.cpp KexiTableScrollAreaHeader.cpp KexiTableScrollAreaHeaderModel.cpp KexiDataTableView.cpp KexiDataTableScrollArea.cpp KexiTableScrollAreaWidget.cpp kexicelleditorfactory.cpp kexitableedit.cpp kexiinputtableedit.cpp kexiblobtableedit.cpp kexibooltableedit.cpp kexicomboboxbase.cpp kexicomboboxtableedit.cpp kexicomboboxpopup.cpp kexidatetableedit.cpp kexitimetableedit.cpp kexidatetimetableedit.cpp kexitextformatter.cpp ) -add_library(kexidatatable SHARED ${kexidatatable_LIB_SRCS}) +kexi_add_library(kexidatatable SHARED ${kexidatatable_LIB_SRCS}) generate_export_header(kexidatatable) target_link_libraries( kexidatatable kexidataviewcommon kexicore kexiutils kexiguiutils KDb KPropertyWidgets KF5::KIOWidgets ) -set_target_properties(kexidatatable PROPERTIES VERSION ${GENERIC_PROJECT_LIB_VERSION} SOVERSION ${GENERIC_PROJECT_LIB_SOVERSION} ) install(TARGETS kexidatatable ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/widget/undo/CMakeLists.txt b/src/widget/undo/CMakeLists.txt index 93ded87fc..de4d49b58 100644 --- a/src/widget/undo/CMakeLists.txt +++ b/src/widget/undo/CMakeLists.txt @@ -1,43 +1,40 @@ set(kexiundo_LIB_SRCS kundo2stack.cpp kundo2group.cpp kundo2view.cpp kundo2model.cpp kundo2magicstring.cpp kundo2commandextradata.cpp ) -add_library(kexiundo SHARED ${kexiundo_LIB_SRCS}) +kexi_add_library(kexiundo SHARED ${kexiundo_LIB_SRCS}) generate_export_header(kexiundo BASE_NAME kexiundo) target_link_libraries(kexiundo PUBLIC KF5::I18n KF5::XmlGui Qt5::Core Qt5::Widgets PRIVATE KF5::ConfigWidgets KF5::IconThemes #KIconLoader kexicore # TODO remove when KexiIcon() goes out of core/ KDb # TODO remove when KexiIcon() goes out of core/ ) -set_target_properties(kexiundo PROPERTIES - VERSION ${GENERIC_PROJECT_LIB_VERSION} SOVERSION ${GENERIC_PROJECT_LIB_SOVERSION} -) install(TARGETS kexiundo ${INSTALL_TARGETS_DEFAULT_ARGS}) if(FALSE) # no need to install install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kexiundo_export.h kundo2stack.h kundo2qstack.h kundo2group.h kundo2view.h kundo2model.h kundo2command.h kundo2magicstring.h DESTINATION ${INCLUDE_INSTALL_DIR}/kexi COMPONENT Devel ) endif() diff --git a/src/widget/utils/CMakeLists.txt b/src/widget/utils/CMakeLists.txt index 163bb20dd..6555277a0 100644 --- a/src/widget/utils/CMakeLists.txt +++ b/src/widget/utils/CMakeLists.txt @@ -1,34 +1,33 @@ include_directories(${CMAKE_SOURCE_DIR}/src/widget/utils) set(kexiguiutils_LIB_SRCS kexisharedactionclient.cpp kexidisplayutils.cpp kexitooltip.cpp kexicontextmenuutils.cpp kexidropdownbutton.cpp kexicomboboxdropdownbutton.cpp kexidatetimeformatter.cpp KexiDockableWidget.cpp ) if (KEXI_MOBILE) else () list(APPEND kexiguiutils_LIB_SRCS kexirecordnavigator.cpp ) endif () -add_library(kexiguiutils SHARED ${kexiguiutils_LIB_SRCS}) +kexi_add_library(kexiguiutils SHARED ${kexiguiutils_LIB_SRCS}) generate_export_header(kexiguiutils) target_link_libraries(kexiguiutils kexicore kexiutils KF5::KIOWidgets KF5::XmlGui #KActionCollection... ) -set_target_properties(kexiguiutils PROPERTIES VERSION ${GENERIC_PROJECT_LIB_VERSION} SOVERSION ${GENERIC_PROJECT_LIB_SOVERSION} ) install(TARGETS kexiguiutils ${INSTALL_TARGETS_DEFAULT_ARGS})