diff --git a/CMakeLists.txt b/CMakeLists.txt index bedcf9c47..c6ce521c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,414 +1,427 @@ # The CMake version we require cmake_minimum_required(VERSION 3.1) # Setting the name of the main project project(KMyMoney VERSION "5.0.80" LANGUAGES CXX) # Determine the GIT reference (if we're based on GIT) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") execute_process(COMMAND git rev-parse --short HEAD WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE VERSION_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE) set(VERSION_SUFFIX "-${VERSION_SUFFIX}") # Add variables which are similar to the build in names of cmake set(PROJECT_VERSION_SUFFIX "${VERSION_SUFFIX}") set(${PROJECT_NAME}_VERSION_SUFFIX "${VERSION_SUFFIX}") elseif(DEFINED VERSION_SUFFIX) string(REGEX REPLACE "-*(.+)" "\\1" DASHFREE_SUFFIX "${VERSION_SUFFIX}") set(PROJECT_VERSION_SUFFIX "-${DASHFREE_SUFFIX}") set(${PROJECT_NAME}_VERSION_SUFFIX "-${DASHFREE_SUFFIX}") endif() # Automoc all sources set(CMAKE_AUTOMOC TRUE) list(APPEND CMAKE_AUTOMOC_MACRO_NAMES "K_PLUGIN_FACTORY" "K_PLUGIN_FACTORY_WITH_JSON") if (POLICY CMP0063) cmake_policy(SET CMP0063 NEW) # Policy introduced in CMake version 3.3 endif() if (POLICY CMP0071) # We do not require the old behaviour. It is only set to old, to prevent accidential use of # the new behavour. If the new behaviour becomes important, cmake_minimum_required has to be # set to "3.10". cmake_policy(SET CMP0071 OLD) # Policy introduced in CMake version 3.10 endif() ######################### General Requirements ########################## if (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0.0) message(FATAL_ERROR "This version of KMyMoney requires at least gcc 6.0.0 to be built successfully") endif() find_package(ECM 5.42 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) include(KDEInstallDirs) include(KDECMakeSettings) +# reimplementation because kmymoney app image is bound to ECM 5.36 +if("${ECM_GLOBAL_FIND_VERSION}" VERSION_LESS "5.38.0") + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + if(WIN32) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + else() + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") + endif() +elseif(NOT WIN32) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") +endif() + include(FeatureSummary) include(CMakeDependentOption) include(GenerateExportHeader) include(KMyMoneyMacros) find_package(PkgConfig) set (OPT_KF5_COMPONENTS DocTools Holidays Contacts Akonadi IdentityManagement Activities) find_package(Gpgmepp) if (Gpgmepp_FOUND) set(GPG_FOUND TRUE) else() set(GPG_FOUND FALSE) list(APPEND OPT_KF5_COMPONENTS Gpgmepp) endif() if (PkgConfig_FOUND) pkg_check_modules(SQLCIPHER sqlcipher) endif() find_package(Qt5 5.6 REQUIRED COMPONENTS Core DBus Widgets Svg Xml Test PrintSupport OPTIONAL_COMPONENTS Sql Concurrent QuickWidgets) find_package(KF5 5.2 REQUIRED COMPONENTS Archive CoreAddons Config ConfigWidgets I18n Completion KCMUtils ItemModels ItemViews Service Wallet IconThemes XmlGui TextWidgets Notifications KIO OPTIONAL_COMPONENTS ${OPT_KF5_COMPONENTS} ) if(LibAlkimia5_DIR) set(_LibAlkimia5_DIR ${LibAlkimia5_DIR}) endif() find_package(LibAlkimia5 8.0) if (NOT LibAlkimia5_FOUND) # restore LibAlkimia5_DIR set to NOTFOUND by first find_package call if(_LibAlkimia5_DIR) set(LibAlkimia5_DIR ${_LibAlkimia5_DIR}) endif() find_package(LibAlkimia5 7.0 REQUIRED) endif() # Recent changes to LibAlkimia should allow us to remove this construct #if(CMAKE_SYSTEM_NAME MATCHES "Windows") # include_directories(${GMP_INCLUDE_DIR}) #endif() find_package(KChart 2.6.0) if(KF5Gpgmepp_FOUND) set(GPG_FOUND TRUE) endif() cmake_dependent_option(ENABLE_GPG "Enable GPG support." ON "GPG_FOUND" OFF) add_feature_info("Encryption" ENABLE_GPG "Allows to store your financial data using strong GPG encryption.") add_definitions(-DQT_USE_QSTRINGBUILDER -DQT_NO_CAST_TO_ASCII -DQT_NO_URL_CAST_FROM_STRING) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # use DBus only on Linux if(CMAKE_SYSTEM_NAME MATCHES "Linux") set(KMM_DBUS 1) endif() set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}) # check for Doxygen find_package(Doxygen) if(DOXYGEN_FOUND) set(APIDOC_DIR ${CMAKE_CURRENT_BINARY_DIR}/apidocs) make_directory("${APIDOC_DIR}") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kmymoney.doxygen.in ${CMAKE_CURRENT_BINARY_DIR}/kmymoney.doxygen IMMEDIATE) add_custom_target(apidoc "${DOXYGEN}" "${CMAKE_CURRENT_BINARY_DIR}/kmymoney.doxygen") endif(DOXYGEN_FOUND) add_feature_info("Doxygen" DOXYGEN_FOUND "Generate API documentation with Doxygen (for devs only).") # check some include files exists set(CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE=500 -D_BSD_SOURCE) include (CheckIncludeFileCXX) check_include_file_cxx("unistd.h" HAVE_UNISTD_H) check_include_file_cxx("pwd.h" HAVE_PWD_H) check_include_file_cxx("windows.h" HAVE_WINDOWS_H) check_include_file_cxx("lmcons.h" HAVE_LMCONS_H) check_include_file_cxx("process.h" HAVE_PROCESS_H) # include check for members in structs include (CheckStructHasMember) ######################### Special Requirements ########################## # This is needed for QtSqlite and QtDesigner # (they'll install files to ${QT_INSTALL_DIR}/plugins/) get_filename_component(QT_BIN_DIR "${QT_MOC_EXECUTABLE}" PATH) get_filename_component(QT_DIR ${QT_BIN_DIR} PATH) set(QT_INSTALL_DIR ${QT_DIR} CACHE PATH "Qt install prefix defaults to the Qt prefix: ${QT_DIR}") cmake_dependent_option(ENABLE_ADDRESSBOOK "Enable addressbook support." ON "KF5IdentityManagement_FOUND;KF5Akonadi_FOUND;KF5Contacts_FOUND" OFF) add_feature_info("Address book" ENABLE_ADDRESSBOOK "Allows fetching payee information from KDE PIM system.") cmake_dependent_option(ENABLE_HOLIDAYS "Enable addressbook support." ON "KF5Holidays_FOUND" OFF) add_feature_info("Holidays" ENABLE_HOLIDAYS "Allows fetching holidays from KDE PIM system.") cmake_dependent_option(ENABLE_ACTIVITIES "Enable activities support." ON "KF5Activities_FOUND" OFF) option(ENABLE_FORECASTVIEW "Enable forecast view" ON) add_feature_info("Forecast view" ENABLE_FORECASTVIEW "Adds possibility to calculate forecasts.") cmake_dependent_option(ENABLE_REPORTSVIEW "Enable reports view." ON "KChart_FOUND" OFF) add_feature_info("Reports view" ENABLE_REPORTSVIEW "Adds possibility to display chart and table reports.") option(ENABLE_BUDGETVIEW "Enable budget view" ON) add_feature_info("Budget view" ENABLE_BUDGETVIEW "Adds possibility to plan a budget.") option(ENABLE_ONLINEJOBOUTBOXVIEW "Enable online job outbox view" ON) add_feature_info("Online job outbox view" ENABLE_ONLINEJOBOUTBOXVIEW "Adds outbox for sending online jobs.") cmake_dependent_option(ENABLE_SQLSTORAGE "Enable SQL storage support." ON "Qt5Sql_FOUND" OFF) add_feature_info("SQL Storage" ENABLE_SQLSTORAGE "Allows storing your financial data in SQL database.") cmake_dependent_option(ENABLE_SQLCIPHER "Enable SQLCipher support." ON "SQLCIPHER_FOUND" OFF) # Otherwise compilers halt on something like that: # ld: library not found for -lsqlcipher # on MS Windows, FreeBSD, macOS, and Ubuntu 14.04 (Ubuntu has CMake 3.5.1 but I'm not sure if it's the one to blame). if(ENABLE_SQLCIPHER AND (NOT CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_VERSION VERSION_LESS 3.5.2)) link_directories(${SQLCIPHER_LIBRARY_DIRS}) endif() add_feature_info("SQLCipher" ENABLE_SQLCIPHER "Allows encrypting your SQLite3 database.") cmake_dependent_option(ENABLE_IBANBICDATA "Enable IBAN/BIC data support." OFF "Qt5Sql_FOUND" OFF) add_feature_info("IBAN/BIC data" ENABLE_IBANBICDATA "Adds predefined IBAN/BIC numbers to KMyMoney (note: doesn't work yet).") # check for optional QWebEngine option(ENABLE_WEBENGINE "Enable QWebEngine" OFF) if(ENABLE_WEBENGINE) find_package(Qt5WebEngineWidgets 5.8 REQUIRED) if(Qt5WebEngineWidgets_VERSION VERSION_GREATER 5.8.99 AND Qt5WebEngineWidgets_VERSION VERSION_LESS 5.9.3) message(WARNING "QWebEngine version ${Qt5WebEngineWidgets_VERSION} is known to be unstable with KMyMoney") endif() else(ENABLE_WEBENGINE) find_package(KF5WebKit REQUIRED) endif(ENABLE_WEBENGINE) # check for optional LibOFX support find_package(LibOfx) cmake_dependent_option(ENABLE_OFXIMPORTER "Enable OFX Importer" ON "LIBOFX_FOUND" OFF) if(ENABLE_OFXIMPORTER) if(NOT LIBOFX_HAVE_CLIENTUID) set(PATH_TO_LIBOFX_HEADER "${LIBOFX_INCLUDE_DIR}/libofx/libofx.h") unset(LIBOFX_HAVE_CLIENTUID) unset(LIBOFX_HAVE_CLIENTUID CACHE) #not doing this will prevent updating below check check_struct_has_member("struct OfxFiLogin" clientuid ${PATH_TO_LIBOFX_HEADER} LIBOFX_HAVE_CLIENTUID LANGUAGE CXX) endif() if (LIBOFX_HAVE_CLIENTUID) set (nice_LIBOFX_HAVE_CLIENTUID "yes") else() set (nice_LIBOFX_HAVE_CLIENTUID "no") endif() else() set (nice_LIBOFX_HAVE_CLIENTUID "unknown") unset(LIBOFX_HAVE_CLIENTUID) unset(LIBOFX_HAVE_CLIENTUID CACHE) endif(ENABLE_OFXIMPORTER) add_feature_info("OFX Importer" ENABLE_OFXIMPORTER "Allows importing OFX files (have client uid version: ${nice_LIBOFX_HAVE_CLIENTUID})" ) # check for optional KBanking support find_package(AQBANKING 5.8.0) find_package(gwenhywfar 4.20.0) find_package(gwengui-cpp) find_package(gwengui-qt5) cmake_dependent_option(ENABLE_KBANKING "Enable KBanking plugin" ON "AQBANKING_FOUND;gwengui-cpp_FOUND;gwengui-qt5_FOUND;Qt5QuickWidgets_FOUND" OFF) add_feature_info(KBanking ENABLE_KBANKING "Interface for the following online banking protocols: HBCI, EBICS, OFX Direct Connect, Paypal") # check for optional Weboob support set(Python_ADDITIONAL_VERSIONS 2.7 2.6) find_package(PythonInterp 2.6) find_package(PythonLibs ${PYTHON_VERSION_STRING}) if(PYTHONINTERP_FOUND AND PYTHONLIBS_FOUND) if(NOT PYTHON_VERSION_MAJOR VERSION_LESS 3) unset(PYTHONLIBS_FOUND) unset(PYTHONINTERP_FOUND) message(WARNING "Python 2 required, but Python 3 found.") else() include(FindPythonModule) find_python_module(weboob REQUIRED) endif() endif() cmake_dependent_option(ENABLE_WEBOOB "Enable Weboob plugin" ON "PYTHONLIBS_FOUND;PYTHONINTERP_FOUND;PY_WEBOOB;Qt5Concurrent_FOUND" OFF) add_feature_info(Weboob ENABLE_WEBOOB "Online banking interface using Weboob.") # check for optional ical support find_package(Libical) cmake_dependent_option(ENABLE_LIBICAL "Enable Calendar plugin" ON "LIBICAL_FOUND" OFF) add_feature_info(iCalendar ENABLE_LIBICAL "iCalendar integration.") option(ENABLE_QIFIMPORTER "Enable QIF Importer" ON) option(ENABLE_QIFEXPORTER "Enable QIF Exporter" ON) add_feature_info("QIF Importer" ENABLE_QIFIMPORTER "Allows importing QIF files.") add_feature_info("QIF Exporter" ENABLE_QIFEXPORTER "Allows exporting QIF files.") option(ENABLE_GNCIMPORTER "Enable GNC Importer" ON) add_feature_info("GNC Importer" ENABLE_GNCIMPORTER "Allows importing GNUCash files.") option(ENABLE_CSVIMPORTER "Enable CSV Importer" ON) option(ENABLE_CSVEXPORTER "Enable CSV Exporter" ON) add_feature_info("CSV Importer" ENABLE_CSVIMPORTER "Allows importing CSV files.") add_feature_info("CSV Exporter" ENABLE_CSVEXPORTER "Allows exporting CSV files.") option(ENABLE_UNFINISHEDFEATURES "For devs only" OFF) add_feature_info("New features" ENABLE_CSVEXPORTER "Compiles unfinished features for testing.") add_feature_info("prefix.sh" KDE_INSTALL_PREFIX_SCRIPT "create prefix.sh script to support local execution") # TODO: this should be removed enable_testing() ######################### Settings ########################## # If no build type is set, use "Release with Debug Info" if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo) endif(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Choose the type of build. Possible values are: 'Release' 'RelWithDebInfo' 'Debug' 'DebugKMM' 'Debugfull' 'Profile' The default value is: 'RelWithDebInfo'" FORCE) # tells gcc to enable exception handling include(KDECompilerSettings) kde_enable_exceptions() # Set linker flags # There is no way to detect linker in cmake (see https://gitlab.kitware.com/cmake/cmake/issues/17596) # and linkers aren't compatible with each other, so we need to assume something if(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-dead_strip -Wl,-undefined,error") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-dead_strip -Wl,-undefined,error -Wl,-mark_dead_strippable_dylib") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-dead_strip -Wl,-undefined,error") elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--as-needed") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed") # TODO: remove multiple definitions of payeeIdentifierLoader::createPayeeIdentifierFromSqlDatabase set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed -Wl,--allow-multiple-definition") # CI builds are crashing on FreeBSD with --no-undefined. Probably because -DECM_ENABLE_SANITIZERS='address' # more can be read on the following site https://bugs.freedesktop.org/show_bug.cgi?id=100120 if(NOT CMAKE_SYSTEM_NAME MATCHES "FreeBSD") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-undefined") endif() # TODO: remove multiple definitions of payeeIdentifierLoader::hasItemEditDelegate if(CMAKE_SYSTEM_NAME MATCHES "Windows") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--allow-multiple-definition") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition") endif() elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /FORCE:Multiple") endif() # Set compiler flags if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wlogical-op -Wno-null-dereference -Wshadow -Wunused -Wno-misleading-indentation -Wsuggest-override -Wcast-qual -Wformat=2 -fno-common") elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "AppleClang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wcast-qual -Wformat=2 -Wunreachable-code -fno-common") elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /experimental:external /external:anglebrackets /external:W0 /W3") endif() # IDEA: Set on a per target base set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "AppleClang") # DebugKMM, Debugfull, Profile set(CMAKE_CXX_FLAGS_DEBUGKMM "-g -O2 -fno-reorder-blocks -fno-schedule-insns -fno-inline") set(CMAKE_CXX_FLAGS_DEBUGFULL "-g3 -fno-inline") set(CMAKE_CXX_FLAGS_PROFILE "-g3 -fno-inline -ftest-coverage -fprofile-arcs") # preprocessor definitions in case this is a debug build set(CMAKE_CXX_FLAGS_DEBUGFULL "${CMAKE_CXX_FLAGS_DEBUGFULL} -DQT_STRICT_ITERATORS -DKMM_DEBUG -DQT_FORCE_ASSERTS") set(CMAKE_CXX_FLAGS_DEBUGKMM "${CMAKE_CXX_FLAGS_DEBUGFULL} -DKMM_DEBUG -DQT_FORCE_ASSERTS") endif() option(USE_MODELTEST "Compile with ModelTest code (default=OFF)" OFF) add_feature_info("Model test" USE_MODELTEST "Generate modeltest code (for devs only).") option(USE_QT_DESIGNER "Install KMyMoney specific widget library for Qt-Designer (default=OFF)" OFF) add_feature_info("QtDesigner" USE_QT_DESIGNER "Qt-Designer library support (for devs only).") ######################### The Actual Targets ########################## set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") if(WIN32) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") else() set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") endif() add_subdirectory( tools ) add_subdirectory( kmymoney ) if(KF5DocTools_FOUND) add_subdirectory( doc ) endif() ######################### Output Results ############################# # create the config.h file out of the config.h.cmake configure_file("config-kmymoney.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config-kmymoney.h") configure_file("config-kmymoney-version.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config-kmymoney-version.h") message(" Build type: ${CMAKE_BUILD_TYPE}") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND DESCRIPTION "The following REQUIRED packages have not been found:") feature_summary(WHAT OPTIONAL_PACKAGES_NOT_FOUND DESCRIPTION "The following OPTIONAL packages have not been found:") feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "The following features have been enabled:") feature_summary(WHAT DISABLED_FEATURES DESCRIPTION "The following features have been disabled:") diff --git a/README.cmake b/README.cmake index 06524829c..af4be2181 100644 --- a/README.cmake +++ b/README.cmake @@ -1,147 +1,168 @@ KMyMoney README.cmake Author: Joerg Rodehueser and Holger Date : Feb 7 2009 This README briefly describes how to build KMyMoney with cmake. ----------------------------------------------------- Quick-start 0: Precondition ----------------------------------------------------- Check that cmake is installed on your machine and is in your PATH. To do so, just type $ cmake --version on your command line. For debian/ubuntu user the following command will check for required packages to build KMyMoney: sudo apt-get build-dep kmymoney ----------------------------------------------------- Quick-start 1: Build KMyMoney ----------------------------------------------------- cmake is designed so that the build process can be done in a separate directory. This is highly recommended for users and required for packagers. Go to the top level of the git working directory. To build KMyMoney in the subdirectory ./build/ type $ mkdir build $ cd build $ cmake .. -DCMAKE_INSTALL_PREFIX=/usr to generate the Makefiles. $ ccmake . to change the configuration of the build process. (optional) Congratulations, your Makefiles were generated! Now you could just type $ make to build the project in the build/ directory. Note that 'make' automatically checks whether any CMakeLists.txt file has changed and reruns cmake if necessary. $ make install to install the project. You have to install KMyMoney to run it. Use sudo or switch to a superuser to run this command. Congratulations, you will never have a chaos of generated files between the important source files again! ----------------------------------------------------- Quick-start 2: How to compile Debug-Builds ----------------------------------------------------- As an example configuration option, you would like to configure a debug build just as './configure --enable-debug=full' did before. For this, you can conveniently create a new out-of-source build directory: $ mkdir Debug $ cd Debug $ cmake -D CMAKE_BUILD_TYPE=Debugfull .. Instead of the last command, you could also call CMake without command line arguments and use the GUI to switch the build type. $ cmake .. $ ccmake . and change the option CMAKE_BUILD_TYPE to 'Debugfull'. Selecting an option and pressing 'h' will show you its allowed values. In any case, your choices are safely stored in the file CMakeCache.txt which will never be completely overwritten. If you want to reset your changes, you will have to delete this file. ----------------------------------------------------- Quick-start 3: More options ----------------------------------------------------- -DCMAKE_INSTALL_PREFIX= This option tells cmake where to install KMyMoney to. During development, this should be a directory in your development environment, such that you can debug the program and test the installation. The default is ${KDE3PREFIX}, which is usually "/opt/kde3/". -DCMAKE_BUILD_TYPE= Choose the type of build. Possible values are: 'Release' 'RelWithDebInfo' 'Debug' 'Debugfull' 'Profile' The default value is: 'RelWithDebInfo' -DKDE4_BUILD_TESTS=ON To also build the unit tests. ----------------------------------------------------- Quick-start 4: Makefile targets ----------------------------------------------------- After cmake has finished, you have a set of ordinary Makefiles in your directory. You can type $ make help to see all available make targets in the current directory. $ make to reconfigure the Makefiles and build the project. $ make install to install KMyMoney to the directory CMAKE_INSTALL_PREFIX. $ make DESTDIR=/tmp install to install KMyMoney to the directory /tmp/CMAKE_INSTALL_PREFIX. $ make uninstall to uninstall a previous installation. $ make package to create a binary tarball. $ make package_source to create a source tarball. (Warning: must have a clean source directory and build out-of-source) $ make kmymoney-unstable_rpm $ make kmymoney_rpm to create binary rpm packages. (they only differ in the package name) $ make kmymoney_srpm $ make kmymoney-unstable_srpm to create source rpm packages. $ make messages to extract and merge translations. (Warning: This will change the source files) $ make developer-doc to create the developer handbook. Will also create HTML-version. $ make test to process all unit tests. + +----------------------------------------------------- +Quick-start 5: Run kmymoney from build dir +----------------------------------------------------- + +Instead of installing kmymoney in a system location to be able to +run it with any newly compiled plugins, you can use the following +recipe to run kmymoney from a build directory. + +After compiling kmymoney (see Quickstart 1:), the following commands +must be executed once to see icons in the application + +$ cd build +$ make install DESTDIR=$PWD/tmp + +After each build you can start kmymoney with + +$ cd build +$ XDG_DATA_DIRS=$PWD/tmp/usr/local/share:$XDG_DATA_DIRS \ + QT_PLUGIN_PATH=$PWD/lib:$QT_PLUGIN_PATH \ + LD_LIBRARY_PATH=$PWD/lib bin/kmymoney diff --git a/kmymoney/CMakeLists.txt b/kmymoney/CMakeLists.txt index 5ca45c76b..4e475eb87 100644 --- a/kmymoney/CMakeLists.txt +++ b/kmymoney/CMakeLists.txt @@ -1,208 +1,219 @@ include(ECMAddAppIcon) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/settings/ ${CMAKE_CURRENT_BINARY_DIR}/settings/ ${CMAKE_CURRENT_BINARY_DIR}/dialogs/ ${CMAKE_CURRENT_SOURCE_DIR}/widgets/ ${CMAKE_CURRENT_BINARY_DIR}/widgets/ ${CMAKE_CURRENT_SOURCE_DIR}/mymoney/ ${CMAKE_CURRENT_SOURCE_DIR}/mymoney/storage/ ${CMAKE_CURRENT_SOURCE_DIR}/plugins/ ${CMAKE_CURRENT_BINARY_DIR}/plugins/ ${CMAKE_CURRENT_SOURCE_DIR}/views/ ${CMAKE_CURRENT_SOURCE_DIR}/dialogs/ ${CMAKE_CURRENT_SOURCE_DIR}/converter/ ${CMAKE_CURRENT_BINARY_DIR}/dialogs/settings/ ${CMAKE_CURRENT_BINARY_DIR}/mymoney/storage/ ${CMAKE_CURRENT_BINARY_DIR}/mymoney/ ${CMAKE_CURRENT_SOURCE_DIR}/wizards/endingbalancedlg/ ${CMAKE_CURRENT_BINARY_DIR}/wizards/endingbalancedlg/ ${CMAKE_CURRENT_SOURCE_DIR}/wizards/newinvestmentwizard/ ${CMAKE_CURRENT_BINARY_DIR}/wizards/newinvestmentwizard/ ${CMAKE_CURRENT_SOURCE_DIR}/wizards/newloanwizard/ ${CMAKE_CURRENT_BINARY_DIR}/wizards/newloanwizard/ ${CMAKE_CURRENT_SOURCE_DIR}/wizards/wizardpages/ ${CMAKE_CURRENT_SOURCE_DIR}/models/ ${CMAKE_CURRENT_BINARY_DIR}/models/ ${CMAKE_CURRENT_SOURCE_DIR}/icons/ ${CMAKE_CURRENT_BINARY_DIR}/icons/ ${CMAKE_CURRENT_SOURCE_DIR}/menus/ ${CMAKE_CURRENT_BINARY_DIR}/menus/ ${CMAKE_CURRENT_SOURCE_DIR}/misc/ ${CMAKE_CURRENT_BINARY_DIR}/misc/ ) add_subdirectory( mymoney ) add_subdirectory( settings ) add_subdirectory( models ) add_subdirectory( plugins ) add_subdirectory( widgets ) add_subdirectory( dialogs ) add_subdirectory( views ) add_subdirectory( converter ) add_subdirectory( wizards ) add_subdirectory( pics ) add_subdirectory( html ) add_subdirectory( templates ) add_subdirectory( misc ) add_subdirectory( icons ) add_subdirectory( menus ) if(BUILD_TESTING) add_subdirectory( tests ) endif() set( _HEADERS kmymoneyutils.h ) ########### common code (kmymoney_common) STATIC ############### # will be linked into kmymoney, kmymoneytest, and libkmymoney.so set( kmymoney_common_SRCS kmymoneyutils.cpp kstartuplogo.cpp kcreditswindow.cpp ) add_library(kmymoney_common STATIC ${kmymoney_common_SRCS}) target_link_libraries(kmymoney_common PUBLIC Qt5::Core KF5::ConfigGui KF5::WidgetsAddons KF5::KIOCore KF5::KIOFileWidgets KF5::KIOWidgets KF5::KIONTLM Alkimia::alkimia kmm_mymoney kmm_utils_webconnect kmm_utils_platformtools PRIVATE KF5::I18n kmm_settings ) # must build kmymoney/transactionsortoption.h # from transactionsortoption.ui first add_dependencies(kmymoney_common generate_base_ui_srcs kmm_settings) add_dependencies(wizardpages widgets) add_dependencies(dialogs widgets) if(USE_MODELTEST) set( kmymoney_common_LIBS ${kmymoney_common_LIBS} ${QT_QTTEST_LIBRARY}) endif(USE_MODELTEST) # remove these generated files, they are in the way and leftovers from 5.0 if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/kmymoneysettings.h) file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/kmymoneysettings.h) endif() if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/kmymoneysettings.cpp) file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/kmymoneysettings.cpp) endif() ########### kmymoney executable ############### set( kmymoney_SRCS main.cpp kmymoney.cpp pluginloader.cpp ) qt5_add_dbus_adaptor(kmymoney_SRCS org.kde.kmymoney.xml kmymoney.h KMyMoneyApp) qt5_add_resources(kmymoney_SRCS kmymoney.qrc) # collect application icons file(GLOB_RECURSE KMYMONEY_APP_ICONS "${CMAKE_CURRENT_SOURCE_DIR}/icons/kmymoney/apps/*.png") # add icons to application sources, to have them bundled ecm_add_app_icon(kmymoney_SRCS ICONS ${KMYMONEY_APP_ICONS}) add_executable( kmymoney ${kmymoney_SRCS} ) if (ENABLE_SQLCIPHER) message( STATUS " SQLCIPHER INCLUDE : " ${SQLCIPHER_INCLUDE_DIRS}) #just for testing it on MS Windows target_compile_definitions(kmymoney PRIVATE SQLITE_HAS_CODEC SQLITE_TEMP_STORE=2) #otherwise "fatal error: 'sqlite3.h' file not found" if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "Windows") target_include_directories(kmymoney PRIVATE ${SQLCIPHER_INCLUDE_DIRS}) endif() endif() target_link_libraries(kmymoney PUBLIC views kmymoney_base kmymoney_common newuserwizard newaccountwizard newinvestmentwizard newloanwizard endingbalancedlg wizardpages dialogs widgets settings converter kmm_models kmm_settings kmm_menus kmm_widgets kmm_mymoney interfaces kmm_plugin Qt5::Core KF5::ConfigGui KF5::WidgetsAddons KF5::CoreAddons $<$:Qt5::Test> $<$:KF5::Holidays> $<$:KF5::Activities> PRIVATE $<$:sqlcipher> ) +# create symlink to let kcoreaddons find plugins +if(WIN32) + add_custom_command(TARGET kmymoney POST_BUILD + COMMAND mklink /J kmymoney . + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +else() + add_custom_command(TARGET kmymoney POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink . kmymoney + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +endif() + # own plist magic for mac os if(APPLE) # own plist template set_target_properties(kmymoney PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/MacOSXBundleInfo.plist.in) # the MacOSX bundle display name property (CFBundleDisplayName) is not currently supported by cmake, # so has to be set for all targets in this cmake file set(MACOSX_BUNDLE_DISPLAY_NAME KMyMoney) set_target_properties(kmymoney PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.KMyMoney") set_target_properties(kmymoney PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KMyMoney") set_target_properties(kmymoney PROPERTIES MACOSX_BUNDLE_DISPLAY_NAME "KMyMoney") set_target_properties(kmymoney PROPERTIES MACOSX_BUNDLE_INFO_STRING "KMyMoney - Personal Finances Manager") set_target_properties(kmymoney PROPERTIES MACOSX_BUNDLE_LONG_VERSION_STRING "KMyMoney ${KDE_APPLICATIONS_VERSION}") set_target_properties(kmymoney PROPERTIES MACOSX_BUNDLE_SHORT_VERSION_STRING "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}") set_target_properties(kmymoney PROPERTIES MACOSX_BUNDLE_BUNDLE_VERSION "${KDE_APPLICATIONS_VERSION}") set_target_properties(kmymoney PROPERTIES MACOSX_BUNDLE_COPYRIGHT "2000-2016 The KMyMoney Authors") endif() ########### install files ############### install(TARGETS kmymoney ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/kmymoney ) install(FILES org.kde.kmymoney.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) install(FILES org.kde.kmymoney.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR} ) install(FILES x-kmymoney.xml DESTINATION ${XDG_MIME_INSTALL_DIR}) install(FILES tips DESTINATION ${CMAKE_INSTALL_DATADIR}/kmymoney) #UPDATE_XDG_MIMETYPES(${XDG_MIME_INSTALL_DIR}) diff --git a/kmymoney/dialogs/settings/ksettingsregister.ui b/kmymoney/dialogs/settings/ksettingsregister.ui index 358c81d4b..da3a1cea4 100644 --- a/kmymoney/dialogs/settings/ksettingsregister.ui +++ b/kmymoney/dialogs/settings/ksettingsregister.ui @@ -1,554 +1,596 @@ KSettingsRegister 0 0 632 - 500 + 512 Register settings - 2 + 0 Display Show a grid in the register Show all register entries in full detail Using the ledger lens shows the details for the transaction that has focus in the ledger. Usually, when using the transaction form, only a one line summary is displayed for each transaction as the details are shown in the form. Use the ledger lens Show transaction form Always show a No. field Draws a larger header above each group of transactions. The grouping depends on the current sort order. Show group header between transactions Show header for the previous and current fiscal year If a planned transaction is overdue, today's date is used by default as the posting date. With this option, the originally planned date is used instead. Display overdue schedules with planned date Qt::Vertical QSizePolicy::Expanding 20 40 Sorting Normal view Reconciliation view Search view Use the <i>left</i> and <i>right</i> buttons to add and remove sort options. Use the <i>up</i> and <i>down</i> buttons to modify the sort order. Double-Click a selected entry to toggle the sort order between <i>ascending</i> and <i>descending</i>. true Data entry Insert transaction type into No. field for new transactions Auto increment check number Keep changes when selecting a different transaction/split Use Enter to move between fields Mark this option, if you always want to match names e.g. for payees from the start. If unset, any substring is matched. Match names from start After entering the reconciliation data automatically detect the transactions that match that data (in some cases it might be not possible to do that). Automatic reconciliation - - + + + + + Initial cursor position within the date field + + + + + + + + Day + + + + + Month + + + + + Year + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 31 + 20 + + + + + Default reconciliation state false - + Default reconciliation state for transactions entered during reconciliation of an account Not reconciled Cleared Reconciled - + Qt::Horizontal QSizePolicy::Expanding 31 20 Autofill -1 Do not auto-fill transaction data at all. No Autofill Collect all transactions for the given payee. Treat all transactions that refer to the same category and have an amount with +/- X % as identical. If more than one transaction is found, a list of them is presented to the user. Selecting 0% will list all transactions. Same transaction if amount differs less than percentage set below true The data of the last transaction assigned to the category used most often for this payee is autofilled into the transaction editor. With previously most often used transaction for the payee 0 0 Same transaction if amount differs less than Two transactions are usually treated identical for autofill, if they refer the same accounts. They are treated as different transactions though, when their amount varies by more than the percentage given here. % 100 10 Qt::Horizontal 40 20 If this option is checked the memos from the previous transaction will be used otherwise the memos will not be considered when the transaction is autofilled. Use memos from previous transaction Qt::Vertical QSizePolicy::Expanding 20 20 Import Match transactions within days false Search for matching transactions within the range of the posting date of the imported transaction +/- the number of given days. QAbstractSpinBox::UpDownArrows 0 99 4 Qt::Horizontal QSizePolicy::Expanding 61 20 Whenever a new payee is detected during import of a statement, the user will be asked to assign a default category for this user when this option is selected. Ask for a new payee's default category Qt::Vertical QSizePolicy::Expanding 20 60 KComboBox QComboBox
kcombobox.h
KLineEdit QLineEdit
klineedit.h
TransactionSortOption QWidget
transactionsortoption.h
radioButton2 toggled(bool) percentageWidget setEnabled(bool) 147 323 171 378
diff --git a/kmymoney/settings/kmymoney.kcfg b/kmymoney/settings/kmymoney.kcfg index dddb670b3..3662c5add 100644 --- a/kmymoney/settings/kmymoney.kcfg +++ b/kmymoney/settings/kmymoney.kcfg @@ -1,455 +1,464 @@ false 1 10 true true false false 10 0 20 10 0 60 false false 0 0 999 (None) 0 0 999 false false false false true true 0 0 false false false true true 0 true true false account,cashflow,payee,category,tag,memo,number,date,amount,state number,date,payee,category,tag,memo,payment,deposit,state 8,1,2,3,4,5,6,7,-9,10 1.00 true true false false false false false false false 255,255,0 152,251,152 255,0,0 0,0,255 255,242,155 255,255,221 false false false false false false false false false true true true 0 0 11 1 1 28 QDateTime::fromString("1900-01-01T00:00:00", Qt::ISODate) false system 1,-9,-4 1,-4 1,-9,-4 + + + Day + + + + + + 0 4 false Bar Banca,Banque Kreditkarte And. Kto. Eröffnungssaldo Rechnung false 10 0 100 2 0 256 2 0 2 0 90 1 999 30 1 999 3 1 999 0 0 31 1 true true true diff --git a/kmymoney/settings/kmymoneysettings.kcfgc b/kmymoney/settings/kmymoneysettings.kcfgc index ffcf13884..ea5eef567 100644 --- a/kmymoney/settings/kmymoneysettings.kcfgc +++ b/kmymoney/settings/kmymoneysettings.kcfgc @@ -1,9 +1,10 @@ File=kmymoney.kcfg ClassName=KMyMoneySettings Singleton=true Mutators=true MemberVariables=private IncludeFiles=\"kmm_settings_export.h\",\"kmymoneysettings_addon.h\" SourceIncludeFiles=\"kmymoneysettings_addons.cpp\" Visibility=KMM_SETTINGS_EXPORT CustomAdditions=true +UseEnumTypes=true diff --git a/kmymoney/widgets/kmymoneydateinput.cpp b/kmymoney/widgets/kmymoneydateinput.cpp index 6ecd2679d..d34e926e5 100644 --- a/kmymoney/widgets/kmymoneydateinput.cpp +++ b/kmymoney/widgets/kmymoneydateinput.cpp @@ -1,389 +1,402 @@ /* * Copyright 2000-2003 Michael Edwardes * Copyright 2001 Felix Rodriguez * Copyright 2017-2018 Łukasz Wojniłowicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kmymoneydateinput.h" +#include "kmymoneysettings.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "icons/icons.h" using namespace Icons; namespace { const int DATE_POPUP_TIMEOUT = 1500; const QDate INVALID_DATE = QDate(1800, 1, 1); } -KMyMoney::OldDateEdit::OldDateEdit(const QDate& date, QWidget* parent) : - QDateEdit(date, parent) +KMyMoney::OldDateEdit::OldDateEdit(const QDate& date, QWidget* parent) + : QDateEdit(date, parent) + , m_initialSection(QDateTimeEdit::DaySection) { } void KMyMoney::OldDateEdit::keyPressEvent(QKeyEvent* k) { if ((lineEdit()->text().isEmpty() || lineEdit()->selectedText() == lineEdit()->text()) && QChar(k->key()).isDigit()) { // the line edit is empty which means that the date was cleared // or the whole text is selected and a digit character was entered // (the same meaning as clearing the date) - in this case set the date // to the current date and let the editor do the actual work setDate(QDate::currentDate()); - setSelectedSection(QDateTimeEdit::DaySection); // start as when focused in if the date was cleared + setSelectedSection(m_initialSection); // start as when focused in if the date was cleared } QDateEdit::keyPressEvent(k); } void KMyMoney::OldDateEdit::focusInEvent(QFocusEvent * event) { QDateEdit::focusInEvent(event); - setSelectedSection(QDateTimeEdit::DaySection); + setSelectedSection(m_initialSection); } bool KMyMoney::OldDateEdit::event(QEvent* e) { // make sure that we keep the current date setting of a KMyMoneyDateInput object // across the QDateEdit::event(FocusOutEvent) bool rc; KMyMoneyDateInput* p = dynamic_cast(parentWidget()); if (e->type() == QEvent::FocusOut && p) { QDate d = p->date(); rc = QDateEdit::event(e); if (d.isValid()) d = p->date(); p->loadDate(d); } else { rc = QDateEdit::event(e); } return rc; } bool KMyMoney::OldDateEdit::focusNextPrevChild(bool next) { Q_UNUSED(next) return true; } struct KMyMoneyDateInput::Private { - QDateEdit *m_dateEdit; + KMyMoney::OldDateEdit *m_dateEdit; KDatePicker *m_datePicker; QDate m_date; QDate m_prevDate; Qt::AlignmentFlag m_qtalignment; QWidget *m_dateFrame; QPushButton *m_dateButton; KPassivePopup *m_datePopup; }; KMyMoneyDateInput::KMyMoneyDateInput(QWidget *parent, Qt::AlignmentFlag flags) : QWidget(parent), d(new Private) { d->m_qtalignment = flags; d->m_date = QDate::currentDate(); QHBoxLayout *dateInputLayout = new QHBoxLayout(this); dateInputLayout->setSpacing(0); dateInputLayout->setContentsMargins(0, 0, 0, 0); d->m_dateEdit = new KMyMoney::OldDateEdit(d->m_date, this); dateInputLayout->addWidget(d->m_dateEdit, 3); setFocusProxy(d->m_dateEdit); d->m_dateEdit->installEventFilter(this); // To get d->m_dateEdit's FocusIn/Out and some KeyPress events // we use INVALID_DATE as a special value for multi transaction editing d->m_dateEdit->setMinimumDate(INVALID_DATE); d->m_dateEdit->setSpecialValueText(QLatin1String(" ")); d->m_datePopup = new KPassivePopup(d->m_dateEdit); d->m_datePopup->setObjectName("datePopup"); d->m_datePopup->setTimeout(DATE_POPUP_TIMEOUT); d->m_datePopup->setView(new QLabel(QLocale().toString(d->m_date), d->m_datePopup)); d->m_dateFrame = new QWidget(this); dateInputLayout->addWidget(d->m_dateFrame); QVBoxLayout *dateFrameVBoxLayout = new QVBoxLayout(d->m_dateFrame); dateFrameVBoxLayout->setMargin(0); dateFrameVBoxLayout->setContentsMargins(0, 0, 0, 0); d->m_dateFrame->setWindowFlags(Qt::Popup); d->m_dateFrame->hide(); d->m_dateEdit->setDisplayFormat(QLocale().dateFormat(QLocale::ShortFormat)); + switch(KMyMoneySettings::initialDateFieldCursorPosition()) { + case KMyMoneySettings::Day: + d->m_dateEdit->setInitialSection(QDateTimeEdit::DaySection); + break; + case KMyMoneySettings::Month: + d->m_dateEdit->setInitialSection(QDateTimeEdit::MonthSection); + break; + case KMyMoneySettings::Year: + d->m_dateEdit->setInitialSection(QDateTimeEdit::YearSection); + break; + } d->m_datePicker = new KDatePicker(d->m_date, d->m_dateFrame); dateFrameVBoxLayout->addWidget(d->m_datePicker); // Let the date picker have a close button (Added in 3.1) d->m_datePicker->setCloseButton(true); // the next line is a try to add an icon to the button d->m_dateButton = new QPushButton(Icons::get(Icon::ViewCalendarDay), QString(), this); dateInputLayout->addWidget(d->m_dateButton); connect(d->m_dateButton, &QAbstractButton::clicked, this, &KMyMoneyDateInput::toggleDatePicker); connect(d->m_dateEdit, &QDateTimeEdit::dateChanged, this, &KMyMoneyDateInput::slotDateChosenRef); connect(d->m_datePicker, &KDatePicker::dateSelected, this, &KMyMoneyDateInput::slotDateChosen); connect(d->m_datePicker, &KDatePicker::dateEntered, this, &KMyMoneyDateInput::slotDateChosen); connect(d->m_datePicker, &KDatePicker::dateSelected, d->m_dateFrame, &QWidget::hide); } void KMyMoneyDateInput::markAsBadDate(bool bad, const QColor& color) { // the next line knows a bit about the internals of QAbstractSpinBox QLineEdit* le = d->m_dateEdit->findChild(); //krazy:exclude=qclasses if (le) { QPalette palette = this->palette(); le->setPalette(palette); if (bad) { palette.setColor(foregroundRole(), color); le->setPalette(palette); } } } void KMyMoneyDateInput::showEvent(QShowEvent* event) { // don't forget the standard behaviour ;-) QWidget::showEvent(event); // If the widget is shown, the size must be fixed a little later // to be appropriate. I saw this in some other places and the only // way to solve this problem is to postpone the setup of the size // to the time when the widget is on the screen. QTimer::singleShot(50, this, SLOT(fixSize())); } void KMyMoneyDateInput::fixSize() { // According to a hint in the documentation of KDatePicker::sizeHint() // 28 pixels should be added in each direction to obtain a better // display of the month button. I decided, (22,14) is good // enough and save some space on the screen (ipwizard) d->m_dateFrame->setFixedSize(d->m_datePicker->sizeHint() + QSize(22, 14)); } KMyMoneyDateInput::~KMyMoneyDateInput() { delete d->m_dateFrame; delete d->m_datePopup; delete d; } void KMyMoneyDateInput::toggleDatePicker() { int w = d->m_dateFrame->width(); int h = d->m_dateFrame->height(); if (d->m_dateFrame->isVisible()) { d->m_dateFrame->hide(); } else { QPoint tmpPoint = mapToGlobal(d->m_dateButton->geometry().bottomRight()); // usually, the datepicker widget is shown underneath the d->m_dateEdit widget // if it does not fit on the screen, we show it above this widget if (tmpPoint.y() + h > QApplication::desktop()->height()) { tmpPoint.setY(tmpPoint.y() - h - d->m_dateButton->height()); } if ((d->m_qtalignment == Qt::AlignRight && tmpPoint.x() + w <= QApplication::desktop()->width()) || (tmpPoint.x() - w < 0)) { d->m_dateFrame->setGeometry(tmpPoint.x() - width(), tmpPoint.y(), w, h); } else { tmpPoint.setX(tmpPoint.x() - w); d->m_dateFrame->setGeometry(tmpPoint.x(), tmpPoint.y(), w, h); } if (d->m_date.isValid() && d->m_date != INVALID_DATE) { d->m_datePicker->setDate(d->m_date); } else { d->m_datePicker->setDate(QDate::currentDate()); } d->m_dateFrame->show(); } } /** Overriding QWidget::keyPressEvent * * increments/decrements the date upon +/- or Up/Down key input * sets the date to current date when the 'T' key is pressed */ void KMyMoneyDateInput::keyPressEvent(QKeyEvent * k) { QKeySequence today(i18nc("Enter todays date into date input widget", "T")); auto adjustDateSection = [&](int offset) { switch(d->m_dateEdit->currentSection()) { case QDateTimeEdit::DaySection: slotDateChosen(d->m_date.addDays(offset)); break; case QDateTimeEdit::MonthSection: slotDateChosen(d->m_date.addMonths(offset)); break; case QDateTimeEdit::YearSection: slotDateChosen(d->m_date.addYears(offset)); break; default: break; } }; switch (k->key()) { case Qt::Key_Equal: case Qt::Key_Plus: adjustDateSection(1); k->accept(); break; case Qt::Key_Minus: adjustDateSection(-1); k->accept(); break; default: if (today == QKeySequence(k->key()) || k->key() == Qt::Key_T) { slotDateChosen(QDate::currentDate()); k->accept(); } break; } k->ignore(); // signal that the key event was not handled } /** * This function receives all events that are sent to focusWidget(). * Some KeyPress events are intercepted and passed to keyPressEvent. * Otherwise they would be consumed by QDateEdit. */ bool KMyMoneyDateInput::eventFilter(QObject *, QEvent *e) { if (e->type() == QEvent::FocusIn) { #ifndef Q_OS_MAC d->m_datePopup->show(mapToGlobal(QPoint(0, height()))); #endif // select the date section, but we need to delay it a bit } else if (e->type() == QEvent::FocusOut) { #ifndef Q_OS_MAC d->m_datePopup->hide(); #endif } else if (e->type() == QEvent::KeyPress) { if (QKeyEvent *k = dynamic_cast(e)) { keyPressEvent(k); if (k->isAccepted()) return true; // signal that the key event was handled } } return false; // Don't filter the event } void KMyMoneyDateInput::slotDateChosenRef(const QDate& date) { if (date.isValid()) { emit dateChanged(date); d->m_date = date; #ifndef Q_OS_MAC QLabel *lbl = static_cast(d->m_datePopup->view()); lbl->setText(QLocale().toString(date)); lbl->adjustSize(); if (d->m_datePopup->isVisible() || hasFocus()) d->m_datePopup->show(mapToGlobal(QPoint(0, height()))); // Repaint #endif } } void KMyMoneyDateInput::slotDateChosen(QDate date) { if (date.isValid()) { // the next line implies a call to slotDateChosenRef() above d->m_dateEdit->setDate(date); } else { d->m_dateEdit->setDate(INVALID_DATE); } } QDate KMyMoneyDateInput::date() const { QDate rc = d->m_dateEdit->date(); if (rc == INVALID_DATE) rc = QDate(); return rc; } void KMyMoneyDateInput::setDate(QDate date) { slotDateChosen(date); } void KMyMoneyDateInput::loadDate(const QDate& date) { d->m_date = d->m_prevDate = date; blockSignals(true); slotDateChosen(date); blockSignals(false); } void KMyMoneyDateInput::resetDate() { setDate(d->m_prevDate); } void KMyMoneyDateInput::setMaximumDate(const QDate& max) { d->m_dateEdit->setMaximumDate(max); } QWidget* KMyMoneyDateInput::focusWidget() const { QWidget* w = d->m_dateEdit; while (w->focusProxy()) w = w->focusProxy(); return w; } /* void KMyMoneyDateInput::setRange(const QDate & min, const QDate & max) { d->m_dateEdit->setDateRange(min, max); } */ diff --git a/kmymoney/widgets/kmymoneydateinput.h b/kmymoney/widgets/kmymoneydateinput.h index 5f5fd751d..e1bfa37e6 100644 --- a/kmymoney/widgets/kmymoneydateinput.h +++ b/kmymoney/widgets/kmymoneydateinput.h @@ -1,153 +1,156 @@ /* * Copyright 2000-2003 Michael Edwardes * Copyright 2001 Felix Rodriguez * Copyright 2017-2018 Łukasz Wojniłowicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KMYMONEYDATEINPUT_H #define KMYMONEYDATEINPUT_H // ---------------------------------------------------------------------------- // QT Includes #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmm_widgets_export.h" // Ideas neatly taken from korganizer // Respective authors are credited. // Some ideas/code have been borrowed from Calendar-0.13 (phoenix.bmedesign.com/~qt) namespace KMyMoney { /** * Provided to be able to catch the focusOut events before the contents gets changed */ class OldDateEdit : public QDateEdit { Q_OBJECT public: explicit OldDateEdit(const QDate& date, QWidget* parent = nullptr); + void setInitialSection(Section section) { m_initialSection = section; } protected: /** if the date was cleared (a state which is not supported by QDateEdit) * make sure that a date can be entered again */ void keyPressEvent(QKeyEvent* k) final override; /** reimplemented for internal reasons */ bool event(QEvent* e) final override; /** reimplemented for internal reasons */ bool focusNextPrevChild(bool next) final override; /** reimplemented for internal reasons */ void focusInEvent(QFocusEvent *event) final override; + private: + QDateEdit::Section m_initialSection; }; }; // namespace /** * This class provides the general widget used for date selection * throughout the KMyMoney project. It provides an QDateEdit widget * which is based on an edit field with spin boxes and adds a QPushButton * to open a KDatePicker. */ class KMM_WIDGETS_EXPORT KMyMoneyDateInput : public QWidget { Q_OBJECT Q_PROPERTY(QDate date READ date WRITE setDate STORED false) public: explicit KMyMoneyDateInput(QWidget* parent = nullptr, Qt::AlignmentFlag flags = Qt::AlignLeft); ~KMyMoneyDateInput(); /** * Returns the selected date in the widget. If the widget is not * showing a date, a QDate() object is returned which has an invalid date. */ QDate date() const; /** * Set the date shown in the widget to @a date. If @a date is invalid, * no text will be shown. The internal widget will use 1.1.1800 for this * special case, as the standard QDateEdit widget does not support an * invalid date as of Qt4 anymore, but we need it anyway for multi transaction * edit. */ void setDate(QDate date); void setMaximumDate(const QDate& max); /** * Setup the widget with @a date. This date is stored internally and * can be reloaded using resetDate(). * * @sa setDate, resetDate */ void loadDate(const QDate& date); /** * Setup the widget with the date loaded using loadDate(). * * @sa loadDate */ void resetDate(); QWidget* focusWidget() const; void setRange(const QDate & min, const QDate & max); void markAsBadDate(bool bad = false, const QColor& = QColor()); Q_SIGNALS: void dateChanged(const QDate& date); protected: /** * - increments/decrements the date upon +/- key input * - increments/decrements the date upon Up/Down key input * - sets the date to current date when the 'T' key is pressed. * The actual key for this to happen might be overridden through * an i18n package. The 'T'-key is always possible. */ void keyPressEvent(QKeyEvent* k) override; void showEvent(QShowEvent* event) override; /** To intercept events sent to focusWidget() */ bool eventFilter(QObject *o, QEvent *e) override; protected Q_SLOTS: void slotDateChosen(QDate date); void toggleDatePicker(); private Q_SLOTS: void slotDateChosenRef(const QDate& date); void fixSize(); private: struct Private; Private * const d; }; #endif