diff --git a/CMakeLists.txt b/CMakeLists.txt index 214eecfa9..ce51c778b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,320 +1,326 @@ # The CMake version we require cmake_minimum_required(VERSION 3.1) # Setting the name of the main project project(KMyMoney VERSION "4.100.0") # 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}") endif() # Automoc all sources set(CMAKE_AUTOMOC TRUE) if (POLICY CMP0063) cmake_policy(SET CMP0063 NEW) # Policy introduced in CMake version 3.3 endif() ######################### General Requirements ########################## find_package(ECM 0.0.11 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) include(FeatureSummary) include(GenerateExportHeader) include(KMyMoneyMacros) set(GPG_ENCRYPTION "no") set (OPT_KF5_COMPONENTS DocTools Holidays Contacts Akonadi IdentityManagement Activities Kross) find_package(Gpgmepp) if (Gpgmepp_FOUND) set(GPG_ENCRYPTION "yes") else() set(OPT_KF5_COMPONENTS ${OPT_KF5_COMPONENTS} Gpgmepp) endif() -find_package(Qt5 5.6 REQUIRED COMPONENTS Core DBus Widgets Svg Sql Xml Test PrintSupport WebEngineWidgets) - -#to be deleted when QWebEngine 5.8 will be better spread across distros -if(Qt5WebEngineWidgets_VERSION VERSION_LESS 5.8.0) - find_package(KF5KHtml 5.2 REQUIRED) -endif() +find_package(Qt5 5.6 REQUIRED COMPONENTS Core DBus Widgets Svg Sql Xml Test PrintSupport) 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} ) find_package(LibAlkimia 6.0.0 REQUIRED) find_package(KChart 2.6.0 REQUIRED) if(KF5Gpgmepp_FOUND) set(GPG_ENCRYPTION "yes") add_definitions(-DGpgmepp_FOUND) endif() add_definitions(-DQT_USE_QSTRINGBUILDER -DQT_NO_CAST_TO_ASCII -DQT_NO_URL_CAST_FROM_STRING) if(NOT MSVC) # MSVC has no regular standards version switches set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) endif() # 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) # check some include files exists set(CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE=500 -D_BSD_SOURCE) include (CheckIncludeFile) check_include_file("sys/types.h" HAVE_SYS_TYPES_H) check_include_file("unistd.h" HAVE_UNISTD_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}") if(KF5IdentityManagement_FOUND AND KF5Akonadi_FOUND AND KF5Contacts_FOUND) set(KMM_ADDRESSBOOK_FOUND true) endif() +# 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.2) + 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 OFX support set(LIBOFX_DEFAULT "AUTO") if(DEFINED ENABLE_LIBOFX) set(LIBOFX_DEFAULT ${ENABLE_LIBOFX}) endif(DEFINED ENABLE_LIBOFX) option(ENABLE_LIBOFX "Enable OFX plugin" ON) if(ENABLE_LIBOFX) find_package(LibOfx) if(NOT LIBOFX_FOUND) if(NOT LIBOFX_DEFAULT STREQUAL "AUTO") message(FATAL_ERROR "LibOFX not found") endif(NOT LIBOFX_DEFAULT STREQUAL "AUTO") set(ENABLE_LIBOFX OFF CACHE BOOL "Enable OFX plugin" FORCE) else(NOT LIBOFX_FOUND) check_struct_has_member("struct OfxFiLogin" "clientuid" "libofx/libofx.h" LIBOFX_HAVE_CLIENTUID) endif(NOT LIBOFX_FOUND) endif(ENABLE_LIBOFX) # check for optional KBanking support set(KBANKING_FOUND "AUTO") mark_as_advanced(KBANKING_FOUND) if(DEFINED ENABLE_KBANKING) set(KBANKING_FOUND OFF) endif(DEFINED ENABLE_KBANKING) option(ENABLE_KBANKING "Enable KBanking plugin" ON) if(ENABLE_KBANKING) find_package(Qt5QuickWidgets) # Includes Qt5Qml find_package(AQBANKING 5.6.5) find_package(gwenhywfar 4.15.3) find_package(gwengui-cpp) find_package(gwengui-qt5) if (AQBANKING_FOUND AND gwengui-cpp_FOUND AND gwengui-qt5_FOUND AND Qt5QuickWidgets_FOUND) set(KBANKING_FOUND ON) else() if(NOT KBANKING_FOUND STREQUAL "AUTO") message(FATAL_ERROR "KBanking requirements not met") endif(NOT KBANKING_FOUND STREQUAL "AUTO") set(KBANKING_FOUND OFF) set(ENABLE_KBANKING OFF CACHE BOOL "Enable KBanking plugin" FORCE) endif () endif(ENABLE_KBANKING) # check for optional Weboob support set(WEBOOB_FOUND "AUTO") mark_as_advanced(WEBOOB_FOUND) if(DEFINED ENABLE_WEBOOB) set(WEBOOB_FOUND OFF) endif(DEFINED ENABLE_WEBOOB) option(ENABLE_WEBOOB "Enable weboob plugin" ON) if(ENABLE_WEBOOB) if(KF5Kross_FOUND) set(WEBOOB_FOUND ON) else(KF5Kross_FOUND) if(NOT WEBOOB_FOUND STREQUAL "AUTO") message(FATAL_ERROR "Weboob requirements not met") endif(NOT WEBOOB_FOUND STREQUAL "AUTO") set(WEBOOB_FOUND OFF) set(ENABLE_WEBOOB OFF CACHE BOOL "Enable weboob plugin" FORCE) endif(KF5Kross_FOUND) endif(ENABLE_WEBOOB) # check for optional ical support set(LIBICAL_DEFAULT "AUTO") if(DEFINED ENABLE_LIBICAL) set(LIBICAL_DEFAULT ${ENABLE_LIBICAL}) endif(DEFINED ENABLE_LIBICAL) option(ENABLE_LIBICAL "Enable Calendar plugin" ON) if(ENABLE_LIBICAL) find_package(Libical) if(NOT LIBICAL_FOUND) if(NOT LIBICAL_DEFAULT STREQUAL "AUTO") message(FATAL_ERROR "LIBICAL not found") endif(NOT LIBICAL_DEFAULT STREQUAL "AUTO") set(ENABLE_LIBICAL OFF CACHE BOOL "Enable Calendar plugin" FORCE) endif(NOT LIBICAL_FOUND) endif(ENABLE_LIBICAL) # 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' 'Debugfull' 'Profile' The default value is: 'RelWithDebInfo'" FORCE) # tells gcc to enable exception handling include(KDECompilerSettings) kde_enable_exceptions() if(CMAKE_SYSTEM_NAME MATCHES "Linux") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--as-needed") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed") endif() # IDEA: Set on a per target base set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(CMAKE_COMPILER_IS_GNUCXX) # be more pedantic about common symbols set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-common -Wlogical-op") # Debug, Debugfull, Profile set(CMAKE_CXX_FLAGS_DEBUG "-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") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wlogical-op -Wextra") # be pedantic about undefined symbols when linking shared libraries if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "kFreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "GNU") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined -Wl,--as-needed") endif() endif() # preprocessor definitions in case this is a debug build set(CMAKE_CXX_FLAGS_DEBUGFULL "${CMAKE_CXX_FLAGS_DEBUGFULL} -DQT_STRICT_ITERATORS -DKMM_DEBUG") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUGFULL} -DKMM_DEBUG") option(USE_MODELTEST "Compile with ModelTest code (default=OFF)" OFF) option(USE_QT_DESIGNER "Install KMyMoney specific widget library for Qt-Designer (default=OFF)" OFF) ######################### The Actual Targets ########################## add_subdirectory( libkgpgfile ) 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") # this macro maps the boolean variable ${_varname} to "yes"/"no" # and writes the output to the variable nice_${_varname} macro(nice_yesno _varname) if(${_varname}) set("nice_${_varname}" "yes") else(${_varname}) set("nice_${_varname}" "no") endif(${_varname}) endmacro() nice_yesno("KF5Holidays_FOUND") nice_yesno("Gpgmepp_FOUND") nice_yesno("KMM_ADDRESSBOOK_FOUND") nice_yesno("LIBOFX_FOUND") nice_yesno("LIBOFX_HAVE_CLIENTUID") nice_yesno("KBANKING_FOUND") nice_yesno("WEBOOB_FOUND") nice_yesno("LIBICAL_FOUND") nice_yesno("ENABLE_SQLCIPHER") nice_yesno("USE_QT_DESIGNER") nice_yesno("USE_MODELTEST") nice_yesno("DOXYGEN_FOUND") -nice_yesno("KF5KHtml_FOUND") +nice_yesno("ENABLE_WEBENGINE") message(" -------- KMyMoney ${PROJECT_VERSION}${PROJECT_VERSION_SUFFIX} -------- Configure results (user options): -------------------------------------------- GpgME Encryption: ${GPG_ENCRYPTION} KDE PIM holidays: ${nice_KF5Holidays_FOUND} KDE PIM addressbook: ${nice_KMM_ADDRESSBOOK_FOUND} OFX plugin: ${nice_LIBOFX_FOUND} OFX supports CLIENTUID: ${nice_LIBOFX_HAVE_CLIENTUID} KBanking plugin: ${nice_KBANKING_FOUND} weboob plugin: ${nice_WEBOOB_FOUND} iCalendar export plugin: ${nice_LIBICAL_FOUND} SQLCipher plugin: ${nice_ENABLE_SQLCIPHER} -KHTML printing: ${nice_KF5KHtml_FOUND} +QWebEngine: ${nice_ENABLE_WEBENGINE} -------------------------------------------- Configure results (developer options): -------------------------------------------- Qt-Designer library support: ${nice_USE_QT_DESIGNER} Generate modeltest code: ${nice_USE_MODELTEST} Generate API documentation with Doxygen: ${nice_DOXYGEN_FOUND}") message(" Build type: ${CMAKE_BUILD_TYPE}") diff --git a/config-kmymoney.h.cmake b/config-kmymoney.h.cmake index 3ada1eef8..20325aac5 100644 --- a/config-kmymoney.h.cmake +++ b/config-kmymoney.h.cmake @@ -1,24 +1,24 @@ /* config-kmymoney.h. Generated from config-kmymoney.h.cmake by cmake */ /* Name of package */ #define PACKAGE "kmymoney" /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H 1 #cmakedefine KMM_DESIGNER 1 #cmakedefine KMM_DBUS 1 #cmakedefine KF5Holidays_FOUND 1 #cmakedefine Gpgmepp_FOUND 1 #cmakedefine KMM_ADDRESSBOOK_FOUND 1 #cmakedefine KF5Activities_FOUND 1 -#cmakedefine KF5KHtml_FOUND 0 +#cmakedefine ENABLE_WEBENGINE 0 diff --git a/kmymoney/CMakeLists.txt b/kmymoney/CMakeLists.txt index 19022610b..36629ad41 100644 --- a/kmymoney/CMakeLists.txt +++ b/kmymoney/CMakeLists.txt @@ -1,185 +1,184 @@ include(ECMAddAppIcon) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${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}/reports/ ${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_BINARY_DIR}/payeeidentifier/ibanandbic/widgets/ # TODO: this line should be moved to the target it belongs ${CMAKE_CURRENT_BINARY_DIR}/payeeidentifier/ibanandbic/ ${CMAKE_CURRENT_BINARY_DIR}/payeeidentifier/nationalaccount/ ${KMyMoney_SOURCE_DIR}/libkgpgfile/ ) add_subdirectory( mymoney ) add_subdirectory( models ) add_subdirectory( plugins ) add_subdirectory( reports ) 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( payeeidentifier ) add_subdirectory( icons ) add_subdirectory( tests ) set( _HEADERS kmymoneyutils.h kmymoneyglobalsettings.h ) ########### settings code (kmm_config) STATIC ############### set( kmm_config_SRCS kmymoneyglobalsettings.cpp ) kconfig_add_kcfg_files( kmm_config_SRCS kmymoneysettings.kcfgc ) add_library(kmm_config STATIC ${kmm_config_SRCS}) target_link_libraries(kmm_config KF5::WidgetsAddons KF5::ConfigWidgets Qt5::Sql Alkimia::alkimia) ########### 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 Alkimia::alkimia kmm_widgets kmm_mymoney ) # must build kmymoney/transactionsortoption.h # from transactionsortoption.ui first add_dependencies(kmymoney_common generate_base_ui_srcs kmm_config) add_dependencies(wizardpages widgets) if(USE_MODELTEST) set( kmymoney_common_LIBS ${kmymoney_common_LIBS} ${QT_QTTEST_LIBRARY}) endif(USE_MODELTEST) ########### 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} ) target_link_libraries(kmymoney views reports kmymoney_base kmymoney_common newuserwizard newaccountwizard newinvestmentwizard newloanwizard endingbalancedlg wizardpages dialogs widgets settings converter models kmm_config kmm_widgets kmm_storage kmm_mymoney kgpgfile interfaces kmm_plugin Qt5::Core Qt5::Sql - Qt5::WebEngineWidgets KF5::Archive KF5::ConfigGui KF5::WidgetsAddons KF5::CoreAddons KChart $<$:Qt5::Test> $<$:KF5::Holidays> ) # 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 kmymoney.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) install(FILES kmymoney.upd DESTINATION ${KCONF_UPDATE_INSTALL_DIR} ) 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/plugins/printcheck/CMakeLists.txt b/kmymoney/plugins/printcheck/CMakeLists.txt index 890c7a128..74c5aa3cd 100644 --- a/kmymoney/plugins/printcheck/CMakeLists.txt +++ b/kmymoney/plugins/printcheck/CMakeLists.txt @@ -1,64 +1,71 @@ # patch the version with the version defined in the build system configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kmm_printcheck.json.in ${CMAKE_CURRENT_BINARY_DIR}/kmm_printcheck.json @ONLY) set(kmm_printcheck_PART_SRCS numbertowords.cpp printcheck.cpp ) kconfig_add_kcfg_files(kmm_printcheck_PART_SRCS pluginsettings.kcfgc) add_library(kmm_printcheck MODULE ${kmm_printcheck_PART_SRCS}) target_link_libraries(kmm_printcheck + Qt5::PrintSupport KF5::I18n - Qt5::WebEngineWidgets kmm_mymoney kmm_plugin ) -#to be deleted when QWebEngine 5.8 will be better spread across distros -if(KF5KHtml_FOUND) - target_link_libraries(kmm_printcheck KF5::KHtml) -endif() +if(ENABLE_WEBENGINE) + target_link_libraries(kmm_printcheck Qt5::WebEngineWidgets) +else(ENABLE_WEBENGINE) + target_link_libraries(kmm_printcheck KF5::WebKit) +endif(ENABLE_WEBENGINE) install(TARGETS kmm_printcheck DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") install(FILES kmm_printcheck.rc DESTINATION "${KXMLGUI_INSTALL_DIR}/kmm_printcheck") install(FILES check_template.html DESTINATION "${DATA_INSTALL_DIR}/kmm_printcheck") install(FILES check_template_green_linen.html DESTINATION "${DATA_INSTALL_DIR}/kmm_printcheck") # the KCM module set(kcm_kmm_printcheck_PART_SRCS kcm_printcheck.cpp ) kconfig_add_kcfg_files(kcm_kmm_printcheck_PART_SRCS pluginsettings.kcfgc) ki18n_wrap_ui(kcm_kmm_printcheck_PART_SRCS pluginsettingsdecl.ui) add_library(kcm_kmm_printcheck MODULE ${kcm_kmm_printcheck_PART_SRCS}) kcoreaddons_desktop_to_json(kcm_kmm_printcheck kcm_kmm_printcheck.desktop) target_link_libraries(kcm_kmm_printcheck + Qt5::PrintSupport KF5::I18n KF5::ConfigWidgets - Qt5::WebEngineWidgets KF5::Completion KF5::KIOWidgets KF5::CoreAddons ) +if(ENABLE_WEBENGINE) + target_link_libraries(kcm_kmm_printcheck Qt5::WebEngineWidgets) +else(ENABLE_WEBENGINE) + target_link_libraries(kcm_kmm_printcheck KF5::WebKit) +endif(ENABLE_WEBENGINE) + install(TARGETS kcm_kmm_printcheck DESTINATION "${KDE_INSTALL_PLUGINDIR}") install(FILES kcm_kmm_printcheck.desktop DESTINATION "${SERVICES_INSTALL_DIR}") diff --git a/kmymoney/plugins/printcheck/kcm_printcheck.cpp b/kmymoney/plugins/printcheck/kcm_printcheck.cpp index aad17dad9..222852ea7 100644 --- a/kmymoney/plugins/printcheck/kcm_printcheck.cpp +++ b/kmymoney/plugins/printcheck/kcm_printcheck.cpp @@ -1,79 +1,89 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ #include "kcm_printcheck.h" // Qt includes #include #include #include #include +#ifdef ENABLE_WEBENGINE +#include +#else +#include +#endif // KDE includes #include #include "pluginsettings.h" PluginSettingsWidget::PluginSettingsWidget(QWidget* parent) : QWidget(parent) { setupUi(this); + #ifdef ENABLE_WEBENGINE m_checkTemplatePreviewHTMLPart = new QWebEngineView(m_previewFrame); + #else + m_checkTemplatePreviewHTMLPart = new KWebView(m_previewFrame); + #endif + QVBoxLayout *layout = new QVBoxLayout; m_previewFrame->setLayout(layout); layout->addWidget(m_checkTemplatePreviewHTMLPart); connect(kcfg_checkTemplateFile, SIGNAL(urlSelected(QUrl)), this, SLOT(urlSelected(QUrl))); connect(kcfg_checkTemplateFile, SIGNAL(returnPressed(QString)), this, SLOT(returnPressed(QString))); } void PluginSettingsWidget::urlSelected(const QUrl &url) { if (!url.isEmpty()) m_checkTemplatePreviewHTMLPart->load(url); } void PluginSettingsWidget::returnPressed(const QString& url) { if (!url.isEmpty()) m_checkTemplatePreviewHTMLPart->load(QUrl::fromUserInput(url)); } K_PLUGIN_FACTORY_WITH_JSON(KCMPrintCheckFactory, "kcm_kmm_printcheck.json", registerPlugin();) // TODO: port to KF5 KCMPrintCheck::KCMPrintCheck(QWidget *parent, const QVariantList& args) : KCModule(0/*KCMPrintCheckFactory::componentData()*/, parent, args) { PluginSettingsWidget* w = new PluginSettingsWidget(this); addConfig(PluginSettings::self(), w); QVBoxLayout *layout = new QVBoxLayout; setLayout(layout); layout->addWidget(w); load(); w->urlSelected(QUrl::fromUserInput(PluginSettings::checkTemplateFile())); } KCMPrintCheck::~KCMPrintCheck() { } #include "kcm_printcheck.moc" diff --git a/kmymoney/plugins/printcheck/kcm_printcheck.h b/kmymoney/plugins/printcheck/kcm_printcheck.h index d4613fcc6..9fff24f38 100644 --- a/kmymoney/plugins/printcheck/kcm_printcheck.h +++ b/kmymoney/plugins/printcheck/kcm_printcheck.h @@ -1,54 +1,63 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ #ifndef KCM_PRINTCHECK_H #define KCM_PRINTCHECK_H +#include "config-kmymoney.h" + #include #include -#include #include "ui_pluginsettingsdecl.h" class QStringList; class KPluginInfo; -class KHTMLPart; +#ifdef ENABLE_WEBENGINE +class QWebEngineView; +#else +class KWebView; +#endif class PluginSettingsWidget : public QWidget, public Ui::PluginSettingsDecl { Q_OBJECT public: PluginSettingsWidget(QWidget* parent = 0); public slots: void urlSelected(const QUrl &url); void returnPressed(const QString& url); private: - QWebEngineView* m_checkTemplatePreviewHTMLPart; + #ifdef ENABLE_WEBENGINE + QWebEngineView *m_checkTemplatePreviewHTMLPart; + #else + KWebView *m_checkTemplatePreviewHTMLPart; + #endif }; class KCMPrintCheck : public KCModule { public: KCMPrintCheck(QWidget* parent, const QVariantList& args); ~KCMPrintCheck(); }; #endif // KCM_PRINTCHECK_H diff --git a/kmymoney/plugins/printcheck/printcheck.cpp b/kmymoney/plugins/printcheck/printcheck.cpp index e3ca43fc6..1e10c6f33 100644 --- a/kmymoney/plugins/printcheck/printcheck.cpp +++ b/kmymoney/plugins/printcheck/printcheck.cpp @@ -1,226 +1,226 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ #include "printcheck.h" #include "config-kmymoney.h" // QT includes #include +#ifdef ENABLE_WEBENGINE #include +#else +#include +#endif #include // KDE includes #include #include #include #include -#ifdef KF5KHtml_FOUND -#include -#include -#endif // KMyMoney includes #include "mymoneyfile.h" #include "pluginloader.h" #include "numbertowords.h" #include "pluginsettings.h" struct KMMPrintCheckPlugin::Private { QAction* m_action; QString m_checkTemplateHTML; QStringList m_printedTransactionIdList; KMyMoneyRegister::SelectedTransactions m_transactions; }; KMMPrintCheckPlugin::KMMPrintCheckPlugin() : KMyMoneyPlugin::Plugin(nullptr, "Print check"/*must be the same as X-KDE-PluginInfo-Name*/) { // Tell the host application to load my GUI component setComponentName("kmm_printcheck", i18n("Print check")); setXMLFile("kmm_printcheck.rc"); // For ease announce that we have been loaded. qDebug("KMyMoney printcheck plugin loaded"); d = std::unique_ptr(new Private); // Create the actions of this plugin QString actionName = i18n("Print check"); d->m_action = actionCollection()->addAction("transaction_printcheck", this, SLOT(slotPrintCheck())); d->m_action->setText(actionName); // wait until a transaction is selected before enableing the action d->m_action->setEnabled(false); d->m_printedTransactionIdList = PluginSettings::printedChecks(); readCheckTemplate(); //! @todo Christian: Replace #if 0 connect(KMyMoneyPlugin::PluginLoader::instance(), SIGNAL(plug(KPluginInfo*)), this, SLOT(slotPlug(KPluginInfo*))); connect(KMyMoneyPlugin::PluginLoader::instance(), SIGNAL(configChanged(Plugin*)), this, SLOT(slotUpdateConfig())); #endif } /** * @internal Destructor is needed because destructor call of unique_ptr must be in this compile unit */ KMMPrintCheckPlugin::~KMMPrintCheckPlugin() { } void KMMPrintCheckPlugin::readCheckTemplate() { QString checkTemplateHTMLPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kmm_printcheck/check_template.html"); if (PluginSettings::checkTemplateFile().isEmpty()) { PluginSettings::setCheckTemplateFile(checkTemplateHTMLPath); PluginSettings::self()->save(); } QFile checkTemplateHTMLFile(PluginSettings::checkTemplateFile()); checkTemplateHTMLFile.open(QIODevice::ReadOnly); QTextStream stream(&checkTemplateHTMLFile); d->m_checkTemplateHTML = stream.readAll(); checkTemplateHTMLFile.close(); } bool KMMPrintCheckPlugin::canBePrinted(const KMyMoneyRegister::SelectedTransaction & selectedTransaction) const { MyMoneyFile* file = MyMoneyFile::instance(); bool isACheck = file->account(selectedTransaction.split().accountId()).accountType() == MyMoneyAccount::Checkings && selectedTransaction.split().shares().isNegative(); return isACheck && d->m_printedTransactionIdList.contains(selectedTransaction.transaction().id()) == 0; } void KMMPrintCheckPlugin::markAsPrinted(const KMyMoneyRegister::SelectedTransaction & selectedTransaction) { d->m_printedTransactionIdList.append(selectedTransaction.transaction().id()); } void KMMPrintCheckPlugin::slotPrintCheck() { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyMoneyToWordsConverter converter; - QWebEngineView *htmlPart = new QWebEngineView(); + #ifdef ENABLE_WEBENGINE + auto htmlPart = new QWebEngineView(); + #else + auto htmlPart = new KWebView(); + #endif + KMyMoneyRegister::SelectedTransactions::const_iterator it; for (it = d->m_transactions.constBegin(); it != d->m_transactions.constEnd(); ++it) { if (!canBePrinted(*it)) continue; // skip this check since it was already printed QString checkHTML = d->m_checkTemplateHTML; MyMoneySecurity currency = file->currency(file->account((*it).split().accountId()).currencyId()); MyMoneyInstitution institution = file->institution(file->account((*it).split().accountId()).institutionId()); // replace the predefined tokens // data about the user checkHTML.replace("$OWNER_NAME", file->user().name()); checkHTML.replace("$OWNER_ADDRESS", file->user().address()); checkHTML.replace("$OWNER_CITY", file->user().city()); checkHTML.replace("$OWNER_STATE", file->user().state()); // data about the account institution checkHTML.replace("$INSTITUTION_NAME", institution.name()); checkHTML.replace("$INSTITUTION_STREET", institution.street()); checkHTML.replace("$INSTITUTION_TELEPHONE", institution.telephone()); checkHTML.replace("$INSTITUTION_TOWN", institution.town()); checkHTML.replace("$INSTITUTION_CITY", institution.city()); checkHTML.replace("$INSTITUTION_POSTCODE", institution.postcode()); checkHTML.replace("$INSTITUTION_MANAGER", institution.manager()); // data about the transaction checkHTML.replace("$DATE", QLocale().toString((*it).transaction().postDate(), QLocale::ShortFormat)); checkHTML.replace("$CHECK_NUMBER", (*it).split().number()); checkHTML.replace("$PAYEE_NAME", file->payee((*it).split().payeeId()).name()); checkHTML.replace("$PAYEE_ADDRESS", file->payee((*it).split().payeeId()).address()); checkHTML.replace("$PAYEE_CITY", file->payee((*it).split().payeeId()).city()); checkHTML.replace("$PAYEE_POSTCODE", file->payee((*it).split().payeeId()).postcode()); checkHTML.replace("$PAYEE_STATE", file->payee((*it).split().payeeId()).state()); checkHTML.replace("$AMOUNT_STRING", converter.convert((*it).split().shares().abs())); checkHTML.replace("$AMOUNT_DECIMAL", MyMoneyUtils::formatMoney((*it).split().shares().abs(), currency)); checkHTML.replace("$MEMO", (*it).split().memo()); // print the check htmlPart->setHtml(checkHTML, QUrl("file://")); -#ifdef KF5KHtml_FOUND - KHTMLPart *khtml = new KHTMLPart(); - khtml->begin(); - khtml->write(checkHTML); - khtml->end(); - khtml->view()->print(); - delete khtml; -#else m_currentPrinter = new QPrinter(); QPointer dialog = new QPrintDialog(m_currentPrinter); dialog->setWindowTitle(QString()); if (dialog->exec() != QDialog::Accepted) { delete m_currentPrinter; m_currentPrinter = nullptr; continue; } else { - htmlPart->page()->print(m_currentPrinter, [=] (bool) {delete m_currentPrinter; m_currentPrinter = nullptr;}); + #ifdef ENABLE_WEBENGINE + htmlPart->page()->print(m_currentPrinter, [=] (bool) {delete m_currentPrinter; m_currentPrinter = nullptr;}); + #else + htmlPart->print(m_currentPrinter); + #endif } delete dialog; -#endif // mark the transaction as printed markAsPrinted(*it); } PluginSettings::setPrintedChecks(d->m_printedTransactionIdList); delete htmlPart; } void KMMPrintCheckPlugin::slotTransactionsSelected(const KMyMoneyRegister::SelectedTransactions& transactions) { d->m_transactions = transactions; bool actionEnabled = false; // enable/disable the action depending if there are transactions selected or not // and whether they can be printed or not KMyMoneyRegister::SelectedTransactions::const_iterator it; for (it = d->m_transactions.constBegin(); it != d->m_transactions.constEnd(); ++it) { if (canBePrinted(*it)) { actionEnabled = true; break; } } d->m_action->setEnabled(actionEnabled); } // the plugin loader plugs in a plugin void KMMPrintCheckPlugin::slotPlug(KPluginInfo *info) { if (info->name() == objectName()) { connect(viewInterface(), SIGNAL(transactionsSelected(KMyMoneyRegister::SelectedTransactions)), this, SLOT(slotTransactionsSelected(KMyMoneyRegister::SelectedTransactions))); } } // the plugin's configurations has changed void KMMPrintCheckPlugin::slotUpdateConfig() { PluginSettings::self()->load(); // re-read the data because the configuration has changed readCheckTemplate(); d->m_printedTransactionIdList = PluginSettings::printedChecks(); } diff --git a/kmymoney/plugins/reconciliationreport/CMakeLists.txt b/kmymoney/plugins/reconciliationreport/CMakeLists.txt index 75fbb9326..ed3f892ff 100644 --- a/kmymoney/plugins/reconciliationreport/CMakeLists.txt +++ b/kmymoney/plugins/reconciliationreport/CMakeLists.txt @@ -1,29 +1,29 @@ # patch the version with the version defined in the build system configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kmm_reconciliationreport.json.in ${CMAKE_CURRENT_BINARY_DIR}/kmm_reconciliationreport.json @ONLY ) set(kmm_reconciliationreport_PART_SRCS kreconciliationreportdlg.cpp reconciliationreport.cpp ) ki18n_wrap_ui(kmm_reconciliationreport_PART_SRCS kreconciliationreportdlgdecl.ui ) add_library(kmm_reconciliationreport MODULE ${kmm_reconciliationreport_PART_SRCS}) target_link_libraries(kmm_reconciliationreport - Qt5::WebEngineWidgets Qt5::PrintSupport kmm_mymoney kmm_plugin ) -#to be deleted when QWebEngine 5.8 will be better spread across distros -if(KF5KHtml_FOUND) - target_link_libraries(kmm_reconciliationreport KF5::KHtml) -endif() +if(ENABLE_WEBENGINE) + target_link_libraries(kmm_reconciliationreport Qt5::WebEngineWidgets) +else(ENABLE_WEBENGINE) + target_link_libraries(kmm_reconciliationreport KF5::WebKit) +endif(ENABLE_WEBENGINE) install(TARGETS kmm_reconciliationreport DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") diff --git a/kmymoney/plugins/reconciliationreport/kreconciliationreportdlg.cpp b/kmymoney/plugins/reconciliationreport/kreconciliationreportdlg.cpp index 56fbee6fe..209660ad6 100644 --- a/kmymoney/plugins/reconciliationreport/kreconciliationreportdlg.cpp +++ b/kmymoney/plugins/reconciliationreport/kreconciliationreportdlg.cpp @@ -1,113 +1,100 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ #include "kreconciliationreportdlg.h" // Qt includes #include #include #include #include #include #include #include // KDE includes #include -#ifdef KF5KHtml_FOUND -#include -#include +#ifdef ENABLE_WEBENGINE +#include +#else +#include #endif KReportDlg::KReportDlg(QWidget* parent, const QString& summaryReportHTML, const QString& detailsReportHTML) : QDialog(parent) { setupUi(this); + #ifdef ENABLE_WEBENGINE m_summaryHTMLPart = new QWebEngineView(m_summaryTab); - m_summaryLayout->addWidget(m_summaryHTMLPart); - m_detailsHTMLPart = new QWebEngineView(m_detailsTab); + #else + m_summaryHTMLPart = new KWebView(m_summaryTab); + m_detailsHTMLPart = new KWebView(m_detailsTab); + #endif + + m_summaryLayout->addWidget(m_summaryHTMLPart); m_detailsLayout->addWidget(m_detailsHTMLPart); m_summaryHTMLPart->setHtml(summaryReportHTML, QUrl("file://")); m_detailsHTMLPart->setHtml(detailsReportHTML, QUrl("file://")); QPushButton* printButton = m_buttonBox->addButton(QString(), QDialogButtonBox::ActionRole); KGuiItem::assign(printButton, KStandardGuiItem::print()); // signals and slots connections connect(printButton, SIGNAL(clicked()), this, SLOT(print())); } KReportDlg::~KReportDlg() { } -#ifdef KF5KHtml_FOUND -void KReportDlg::handleHTML(const QString &sHTML) -{ - KHTMLPart *khtml = new KHTMLPart(this); - khtml->begin(); - khtml->write(sHTML); - khtml->end(); - khtml->view()->print(); - delete khtml; -} -#endif - void KReportDlg::print() { -#ifdef KF5KHtml_FOUND - // do the actual painting job - connect(this, &KReportDlg::getHTML, this, &KReportDlg::handleHTML); - switch (m_tabWidget->currentIndex()) { - case 0: - m_summaryHTMLPart->page()->toHtml([this](const QString &result){emit getHTML(result);}); - break; - case 1: - m_detailsHTMLPart->page()->toHtml([this](const QString &result){emit getHTML(result);}); - break; - default: - qDebug("KReportDlg::print() current page index not handled correctly"); - } -#else m_currentPrinter = new QPrinter(); QPrintDialog *dialog = new QPrintDialog(m_currentPrinter, this); dialog->setWindowTitle(QString()); if (dialog->exec() != QDialog::Accepted) { delete m_currentPrinter; m_currentPrinter = nullptr; return; } // do the actual painting job switch (m_tabWidget->currentIndex()) { case 0: + #ifdef ENABLE_WEBENGINE m_summaryHTMLPart->page()->print(m_currentPrinter, [=] (bool) {delete m_currentPrinter; m_currentPrinter = nullptr;}); + #else + m_summaryHTMLPart->print(m_currentPrinter); + #endif break; case 1: + #ifdef ENABLE_WEBENGINE m_detailsHTMLPart->page()->print(m_currentPrinter, [=] (bool) {delete m_currentPrinter; m_currentPrinter = nullptr;}); + #else + m_detailsHTMLPart->print(m_currentPrinter); + #endif break; default: delete m_currentPrinter; m_currentPrinter = nullptr; qDebug("KReportDlg::print() current page index not handled correctly"); } - #endif } diff --git a/kmymoney/plugins/reconciliationreport/kreconciliationreportdlg.h b/kmymoney/plugins/reconciliationreport/kreconciliationreportdlg.h index dcfcfd80c..5458e2780 100644 --- a/kmymoney/plugins/reconciliationreport/kreconciliationreportdlg.h +++ b/kmymoney/plugins/reconciliationreport/kreconciliationreportdlg.h @@ -1,56 +1,57 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ #ifndef KRECONCILIATIONREPORTDLG_H #define KRECONCILIATIONREPORTDLG_H -#include #include "config-kmymoney.h" #include "ui_kreconciliationreportdlgdecl.h" -class KHTMLPart; class QPrinter; +#ifdef ENABLE_WEBENGINE +class QWebEngineView; +#else +class KWebView; +#endif class KReportDlg : public QDialog, public Ui::KReconciliationReportDlgDecl { Q_OBJECT public: KReportDlg(QWidget* parent, const QString& summaryReportHTML, const QString& detailsReportHTML); ~KReportDlg(); protected slots: void print(); - #ifdef KF5KHtml_FOUND - void handleHTML(const QString &sHTML); - #endif private: + #ifdef ENABLE_WEBENGINE QWebEngineView *m_summaryHTMLPart; QWebEngineView *m_detailsHTMLPart; - QPrinter *m_currentPrinter; -signals: - #ifdef KF5KHtml_FOUND - void getHTML(QString sHTML); + #else + KWebView *m_summaryHTMLPart; + KWebView *m_detailsHTMLPart; #endif + QPrinter *m_currentPrinter; }; #endif diff --git a/kmymoney/views/CMakeLists.txt b/kmymoney/views/CMakeLists.txt index 068c827e2..4f8f66361 100644 --- a/kmymoney/views/CMakeLists.txt +++ b/kmymoney/views/CMakeLists.txt @@ -1,93 +1,95 @@ ############# next target (views) STATIC ################### set(libviews_a_SOURCES kaccountsview.cpp kbudgetview.cpp kcategoriesview.cpp kforecastview.cpp kgloballedgerview.cpp ledgerview.cpp kwelcomepage.cpp khomeview.cpp kinstitutionsview.cpp kinvestmentview.cpp kmymoneyfile.cpp kmymoneyview.cpp kpayeesview.cpp kreportsview.cpp kscheduledview.cpp tocitem.cpp tocitemgroup.cpp tocitemreport.cpp kscheduletreeitem.cpp ktagsview.cpp konlinejoboutbox.cpp kpayeeidentifierview.cpp payeeidentifierselectiondelegate.cpp simpleledgerview.cpp ledgerviewpage.cpp ledgerview.cpp ledgerdelegate.cpp newspliteditor.cpp newtransactioneditor.cpp newtransactionform.cpp splitdialog.cpp splitdelegate.cpp widgethintframe.cpp + kmymoneywebpage.cpp ) set(libviews_a_HEADER kmymoneyview.h) set(libviews_a_UI kaccountsviewdecl.ui kbudgetviewdecl.ui kcategoriesviewdecl.ui kforecastviewdecl.ui kinstitutionsviewdecl.ui kinvestmentviewdecl.ui kpayeesviewdecl.ui kscheduledviewdecl.ui ktagsviewdecl.ui konlinejoboutbox.ui kpayeeidentifierview.ui simpleledgerview.ui ledgerview.ui ledgerviewpage.ui simpleledgerview.ui splitdialog.ui newspliteditor.ui newtransactioneditor.ui newtransactionform.ui ) # The handling of these ui files depends # on libkmymoney.so (the widgets library) ki18n_wrap_ui(libviews_a_SOURCES ${libviews_a_UI}) add_library(views STATIC ${libviews_a_SOURCES}) -target_link_libraries(views PUBLIC KChart KF5::KIOFileWidgets KF5::Notifications KF5::Archive KF5::TextWidgets Qt5::Sql Qt5::WebEngineWidgets Alkimia::alkimia) +target_link_libraries(views PUBLIC KChart KF5::KIOFileWidgets KF5::Notifications KF5::Archive KF5::TextWidgets Qt5::Sql Alkimia::alkimia) -#to be deleted when QWebEngine 5.8 will be better spread across distros -if(KF5KHtml_FOUND) - target_link_libraries(views PRIVATE KF5::KHtml) -endif() +if(ENABLE_WEBENGINE) + target_link_libraries(views PUBLIC Qt5::WebEngineWidgets) +else(ENABLE_WEBENGINE) + target_link_libraries(views PUBLIC KF5::WebKit) +endif(ENABLE_WEBENGINE) if(KF5Activities_FOUND) - target_link_libraries(views PRIVATE KF5::Activities) + target_link_libraries(views PRIVATE KF5::Activities) endif() # TODO: Remove this dependency. But it is needed as long as the payee editor uses these objects directly # This should be replaced by virtual methods in a pure abstract object. target_link_libraries( views PUBLIC payeeidentifier_iban_bic payeeidentifier_nationalAccount kmm_mymoney # needed to load payeeIdentifier ) # we rely on some of the dialogs to be generated add_dependencies(views dialogs newinvestmentwizard newloanwizard endingbalancedlg) ########### install files ############### install(FILES ${libviews_a_HEADER} DESTINATION ${INCLUDE_INSTALL_DIR}/kmymoney COMPONENT Devel) diff --git a/kmymoney/views/khomeview.cpp b/kmymoney/views/khomeview.cpp index a0bb794bc..c6a2e2a8a 100644 --- a/kmymoney/views/khomeview.cpp +++ b/kmymoney/views/khomeview.cpp @@ -1,2037 +1,2050 @@ /*************************************************************************** khomeview.cpp - description ------------------- begin : Tue Jan 22 2002 copyright : (C) 2000-2002 by Michael Edwardes Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "khomeview.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#ifdef ENABLE_WEBENGINE +#include +#else +#include +#endif // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include #include #include #include -#ifdef KF5KHtml_FOUND -#include -#include -#endif // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyutils.h" #include "kwelcomepage.h" #include "kmymoneyglobalsettings.h" #include "mymoneyfile.h" #include "mymoneyforecast.h" #include "kmymoney.h" #include "kreportchartview.h" #include "pivottable.h" #include "pivotgrid.h" #include "reportaccount.h" #include +#include #define VIEW_LEDGER "ledger" #define VIEW_SCHEDULE "schedule" #define VIEW_WELCOME "welcome" #define VIEW_HOME "home" #define VIEW_REPORTS "reports" using namespace Icons; bool accountNameLess(const MyMoneyAccount &acc1, const MyMoneyAccount &acc2) { return acc1.name().localeAwareCompare(acc2.name()) < 0; } using namespace reports; class KHomeView::Private { public: Private() : m_showAllSchedules(false), m_needReload(false), m_netWorthGraphLastValidSize(400, 300) { } /** * daily balances of an account */ typedef QMap dailyBalances; + #ifdef ENABLE_WEBENGINE QWebEngineView *m_view; + #else + KWebView *m_view; + #endif + QString m_html; bool m_showAllSchedules; bool m_needReload; MyMoneyForecast m_forecast; MyMoneyMoney m_total; /** * Hold the last valid size of the net worth graph * for the times when the needed size can't be computed. */ QSize m_netWorthGraphLastValidSize; /** * daily forecast balance of accounts */ QMap m_accountList; }; /** * @brief Converts a QPixmap to an data URI scheme * * According to RFC 2397 * * @param pixmap Source to convert * @return full data URI */ QString QPixmapToDataUri(const QPixmap& pixmap) { QImage image(pixmap.toImage()); QByteArray byteArray; QBuffer buffer(&byteArray); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "PNG"); // writes the image in PNG format inside the buffer return QLatin1String("data:image/png;base64,") + QString(byteArray.toBase64()); } KHomeView::KHomeView(QWidget *parent, const char *name) : KMyMoneyViewBase(parent, name, i18n("Home")), d(new Private) { + #ifdef ENABLE_WEBENGINE d->m_view = new QWebEngineView(this); + #else + d->m_view = new KWebView(this); + #endif d->m_view->setPage(new MyQWebEnginePage(d->m_view)); + addWidget(d->m_view); d->m_view->setHtml(KWelcomePage::welcomePage(), QUrl("file://")); + #ifdef ENABLE_WEBENGINE connect(d->m_view->page(), &QWebEnginePage::urlChanged, - this, &KHomeView::slotOpenUrl); + this, &KHomeView::slotOpenUrl); + #else + d->m_view->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); + connect(d->m_view->page(), &KWebPage::linkClicked, + this, &KHomeView::slotOpenUrl); + #endif } KHomeView::~KHomeView() { // if user wants to remember the font size, store it here if (KMyMoneyGlobalSettings::rememberZoomFactor()) { KMyMoneyGlobalSettings::setZoomFactor(d->m_view->zoomFactor()); KMyMoneyGlobalSettings::self()->save(); } delete d; } void KHomeView::wheelEvent(QWheelEvent* event) { // Zoom text on Ctrl + Scroll if (event->modifiers() & Qt::CTRL) { qreal factor = d->m_view->zoomFactor(); if (event->delta() > 0) factor += 0.1; else if (event->delta() < 0) factor -= 0.1; d->m_view->setZoomFactor(factor); event->accept(); return; } } void KHomeView::slotLoadView() { d->m_needReload = true; if (isVisible()) { loadView(); d->m_needReload = false; } } void KHomeView::showEvent(QShowEvent* event) { emit aboutToShow(); if (d->m_needReload) { loadView(); d->m_needReload = false; } QWidget::showEvent(event); } void KHomeView::slotPrintView() { if (d->m_view) { -#ifdef KF5KHtml_FOUND - KHTMLPart *khtml = new KHTMLPart(this); - khtml->begin(); - khtml->write(d->m_html); - khtml->end(); - khtml->view()->print(); - delete khtml; -#else m_currentPrinter = new QPrinter(); QPrintDialog *dialog = new QPrintDialog(m_currentPrinter, this); dialog->setWindowTitle(QString()); if (dialog->exec() != QDialog::Accepted) { delete m_currentPrinter; m_currentPrinter = nullptr; return; } + #ifdef ENABLE_WEBENGINE d->m_view->page()->print(m_currentPrinter, [=] (bool) {delete m_currentPrinter; m_currentPrinter = nullptr;}); -#endif + #else + d->m_view->print(m_currentPrinter); + #endif } } void KHomeView::loadView() { d->m_view->setZoomFactor(KMyMoneyGlobalSettings::zoomFactor()); QList list; MyMoneyFile::instance()->accountList(list); if (list.count() == 0) { d->m_view->setHtml(KWelcomePage::welcomePage(), QUrl("file://")); } else { //clear the forecast flag so it will be reloaded d->m_forecast.setForecastDone(false); const QString filename = QStandardPaths::locate(QStandardPaths::DataLocation, "html/kmymoney.css"); QString header = QString("\n\n").arg(QUrl::fromLocalFile(filename).url()); header += KMyMoneyUtils::variableCSS(); header += "\n"; QString footer = "\n"; d->m_html.clear(); d->m_html += header; d->m_html += QString("
%1
").arg(i18n("Your Financial Summary")); QStringList settings = KMyMoneyGlobalSettings::itemList(); QStringList::ConstIterator it; for (it = settings.constBegin(); it != settings.constEnd(); ++it) { int option = (*it).toInt(); if (option > 0) { switch (option) { case 1: // payments showPayments(); break; case 2: // preferred accounts showAccounts(Preferred, i18n("Preferred Accounts")); break; case 3: // payment accounts // Check if preferred accounts are shown separately if (settings.contains("2")) { showAccounts(static_cast(Payment | Preferred), i18n("Payment Accounts")); } else { showAccounts(Payment, i18n("Payment Accounts")); } break; case 4: // favorite reports showFavoriteReports(); break; case 5: // forecast showForecast(); break; case 6: // net worth graph over all accounts showNetWorthGraph(); break; case 8: // assets and liabilities showAssetsLiabilities(); break; case 9: // budget showBudget(); break; case 10: // cash flow summary showCashFlowSummary(); break; } d->m_html += "
 
\n"; } } d->m_html += "
"; d->m_html += link(VIEW_WELCOME, QString()) + i18n("Show KMyMoney welcome page") + linkend(); d->m_html += "
"; d->m_html += "
"; d->m_html += footer; d->m_view->setHtml(d->m_html, QUrl("file://")); } } void KHomeView::showNetWorthGraph() { d->m_html += QString("
%1
\n
 
\n").arg(i18n("Net Worth Forecast")); MyMoneyReport reportCfg = MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::userDefined, // overridden by the setDateFilter() call below MyMoneyReport::eDetailTotal, i18n("Net Worth Forecast"), i18n("Generated Report")); reportCfg.setChartByDefault(true); reportCfg.setChartCHGridLines(false); reportCfg.setChartSVGridLines(false); reportCfg.setChartDataLabels(false); reportCfg.setChartType(MyMoneyReport::eChartLine); reportCfg.setIncludingSchedules(false); reportCfg.addAccountGroup(MyMoneyAccount::Asset); reportCfg.addAccountGroup(MyMoneyAccount::Liability); reportCfg.setColumnsAreDays(true); reportCfg.setConvertCurrency(true); reportCfg.setIncludingForecast(true); reportCfg.setDateFilter(QDate::currentDate(), QDate::currentDate().addDays(+ 90)); reports::PivotTable table(reportCfg); reports::KReportChartView* chartWidget = new reports::KReportChartView(0); table.drawChart(*chartWidget); // Adjust the size QSize netWorthGraphSize = KHomeView::size(); netWorthGraphSize -= QSize(80, 30); // consider the computed size valid only if it's smaller on both axes that the applications size if (netWorthGraphSize.width() < kmymoney->width() || netWorthGraphSize.height() < kmymoney->height()) { d->m_netWorthGraphLastValidSize = netWorthGraphSize; } chartWidget->resize(d->m_netWorthGraphLastValidSize); //save the chart to an image QString chart = QPixmapToDataUri(QPixmap::grabWidget(chartWidget->coordinatePlane()->parent())); d->m_html += QString(""); d->m_html += QString(""); d->m_html += QString("").arg(chart); d->m_html += QString(""); d->m_html += QString("
\"Networth\"
"); //delete the widget since we no longer need it delete chartWidget; } void KHomeView::showPayments() { MyMoneyFile* file = MyMoneyFile::instance(); QList overdues; QList schedule; int i = 0; //if forecast has not been executed yet, do it. if (!d->m_forecast.isForecastDone()) doForecast(); schedule = file->scheduleList("", MyMoneySchedule::TYPE_ANY, MyMoneySchedule::OCCUR_ANY, MyMoneySchedule::STYPE_ANY, QDate::currentDate(), QDate::currentDate().addMonths(1)); overdues = file->scheduleList("", MyMoneySchedule::TYPE_ANY, MyMoneySchedule::OCCUR_ANY, MyMoneySchedule::STYPE_ANY, QDate(), QDate(), true); if (schedule.empty() && overdues.empty()) return; // HACK // Remove the finished schedules QList::Iterator d_it; //regular schedules d_it = schedule.begin(); while (d_it != schedule.end()) { if ((*d_it).isFinished()) { d_it = schedule.erase(d_it); continue; } ++d_it; } //overdue schedules d_it = overdues.begin(); while (d_it != overdues.end()) { if ((*d_it).isFinished()) { d_it = overdues.erase(d_it); continue; } ++d_it; } d->m_html += "
"; d->m_html += QString("
%1
\n").arg(i18n("Payments")); if (!overdues.isEmpty()) { d->m_html += "
 
\n"; qSort(overdues); QList::Iterator it; QList::Iterator it_f; d->m_html += ""; d->m_html += QString("\n").arg(showColoredAmount(i18n("Overdue payments"), true)); d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; for (it = overdues.begin(); it != overdues.end(); ++it) { // determine number of overdue payments int cnt = (*it).transactionsRemainingUntil(QDate::currentDate().addDays(-1)); d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); showPaymentEntry(*it, cnt); d->m_html += ""; } d->m_html += "
%1
"; d->m_html += i18n("Date"); d->m_html += ""; d->m_html += i18n("Schedule"); d->m_html += ""; d->m_html += i18n("Account"); d->m_html += ""; d->m_html += i18n("Amount"); d->m_html += ""; d->m_html += i18n("Balance after"); d->m_html += "
"; } if (!schedule.isEmpty()) { qSort(schedule); // Extract todays payments if any QList todays; QList::Iterator t_it; for (t_it = schedule.begin(); t_it != schedule.end();) { if ((*t_it).adjustedNextDueDate() == QDate::currentDate()) { todays.append(*t_it); (*t_it).setNextDueDate((*t_it).nextPayment(QDate::currentDate())); // if adjustedNextDueDate is still currentDate then remove it from // scheduled payments if ((*t_it).adjustedNextDueDate() == QDate::currentDate()) { t_it = schedule.erase(t_it); continue; } } ++t_it; } if (todays.count() > 0) { d->m_html += "
 
\n"; d->m_html += ""; d->m_html += QString("\n").arg(i18n("Today's due payments")); d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; for (t_it = todays.begin(); t_it != todays.end(); ++t_it) { d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); showPaymentEntry(*t_it); d->m_html += ""; } d->m_html += "
%1
"; d->m_html += i18n("Date"); d->m_html += ""; d->m_html += i18n("Schedule"); d->m_html += ""; d->m_html += i18n("Account"); d->m_html += ""; d->m_html += i18n("Amount"); d->m_html += ""; d->m_html += i18n("Balance after"); d->m_html += "
"; } if (!schedule.isEmpty()) { d->m_html += "
 
\n"; QList::Iterator it; d->m_html += ""; d->m_html += QString("\n").arg(i18n("Future payments")); d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; // show all or the first 6 entries int cnt; cnt = (d->m_showAllSchedules) ? -1 : 6; bool needMoreLess = d->m_showAllSchedules; QDate lastDate = QDate::currentDate().addMonths(1); qSort(schedule); do { it = schedule.begin(); if (it == schedule.end()) break; // if the next due date is invalid (schedule is finished) // we remove it from the list QDate nextDate = (*it).nextDueDate(); if (!nextDate.isValid()) { schedule.erase(it); continue; } if (nextDate > lastDate) break; if (cnt == 0) { needMoreLess = true; break; } // in case we've shown the current recurrence as overdue, // we don't show it here again, but keep the schedule // as it might show up later in the list again if (!(*it).isOverdue()) { if (cnt > 0) --cnt; d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); showPaymentEntry(*it); d->m_html += ""; // for single occurrence we have reported everything so we // better get out of here. if ((*it).occurrence() == MyMoneySchedule::OCCUR_ONCE) { schedule.erase(it); continue; } } // if nextPayment returns an invalid date, setNextDueDate will // just skip it, resulting in a loop // we check the resulting date and erase the schedule if invalid if (!((*it).nextPayment((*it).nextDueDate())).isValid()) { schedule.erase(it); continue; } (*it).setNextDueDate((*it).nextPayment((*it).nextDueDate())); qSort(schedule); } while (1); if (needMoreLess) { d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); d->m_html += ""; d->m_html += ""; } d->m_html += "
%1
"; d->m_html += i18n("Date"); d->m_html += ""; d->m_html += i18n("Schedule"); d->m_html += ""; d->m_html += i18n("Account"); d->m_html += ""; d->m_html += i18n("Amount"); d->m_html += ""; d->m_html += i18n("Balance after"); d->m_html += "
"; if (d->m_showAllSchedules) { d->m_html += link(VIEW_SCHEDULE, QString("?mode=%1").arg("reduced")) + i18nc("Less...", "Show fewer schedules on the list") + linkend(); } else { d->m_html += link(VIEW_SCHEDULE, QString("?mode=%1").arg("full")) + i18nc("More...", "Show more schedules on the list") + linkend(); } d->m_html += "
"; } } d->m_html += "
"; } void KHomeView::showPaymentEntry(const MyMoneySchedule& sched, int cnt) { QString tmp; MyMoneyFile* file = MyMoneyFile::instance(); try { MyMoneyAccount acc = sched.account(); if (!acc.id().isEmpty()) { MyMoneyTransaction t = sched.transaction(); // only show the entry, if it is still active if (!sched.isFinished()) { MyMoneySplit sp = t.splitByAccount(acc.id(), true); QString pathEnter = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::KeyEnter]).pixmap(QSize(16,16))); QString pathSkip = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::MediaSkipForward]).pixmap(QSize(16,16))); //show payment date tmp = QString("") + QLocale().toString(sched.adjustedNextDueDate(), QLocale::ShortFormat) + ""; if (!pathEnter.isEmpty()) tmp += link(VIEW_SCHEDULE, QString("?id=%1&mode=enter").arg(sched.id()), i18n("Enter schedule")) + QString("").arg(pathEnter) + linkend(); if (!pathSkip.isEmpty()) tmp += " " + link(VIEW_SCHEDULE, QString("?id=%1&mode=skip").arg(sched.id()), i18n("Skip schedule")) + QString("").arg(pathSkip) + linkend(); tmp += QString(" "); tmp += link(VIEW_SCHEDULE, QString("?id=%1&mode=edit").arg(sched.id()), i18n("Edit schedule")) + sched.name() + linkend(); //show quantity of payments overdue if any if (cnt > 1) tmp += i18np(" (%1 payment)", " (%1 payments)", cnt); //show account of the main split tmp += ""; tmp += QString(file->account(acc.id()).name()); //show amount of the schedule tmp += ""; const MyMoneySecurity& currency = MyMoneyFile::instance()->currency(acc.currencyId()); MyMoneyMoney payment = MyMoneyMoney(sp.value(t.commodity(), acc.currencyId()) * cnt); QString amount = MyMoneyUtils::formatMoney(payment, acc, currency); amount.replace(QChar(' '), " "); tmp += showColoredAmount(amount, payment.isNegative()); tmp += ""; //show balance after payments tmp += ""; QDate paymentDate = QDate(sched.adjustedNextDueDate()); MyMoneyMoney balanceAfter = forecastPaymentBalance(acc, payment, paymentDate); QString balance = MyMoneyUtils::formatMoney(balanceAfter, acc, currency); balance.replace(QChar(' '), " "); tmp += showColoredAmount(balance, balanceAfter.isNegative()); tmp += ""; // qDebug("paymentEntry = '%s'", tmp.toLatin1()); d->m_html += tmp; } } } catch (const MyMoneyException &e) { qDebug("Unable to display schedule entry: %s", qPrintable(e.what())); } } void KHomeView::showAccounts(KHomeView::paymentTypeE type, const QString& header) { MyMoneyFile* file = MyMoneyFile::instance(); int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); QList accounts; bool showClosedAccounts = kmymoney->isActionToggled(Action::ViewShowAll); // get list of all accounts file->accountList(accounts); for (QList::Iterator it = accounts.begin(); it != accounts.end();) { bool removeAccount = false; if (!(*it).isClosed() || showClosedAccounts) { switch ((*it).accountType()) { case MyMoneyAccount::Expense: case MyMoneyAccount::Income: // never show a category account // Note: This might be different in a future version when // the homepage also shows category based information removeAccount = true; break; // Asset and Liability accounts are only shown if they // have the preferred flag set case MyMoneyAccount::Asset: case MyMoneyAccount::Liability: case MyMoneyAccount::Investment: // if preferred accounts are requested, then keep in list if ((*it).value("PreferredAccount") != "Yes" || (type & Preferred) == 0) { removeAccount = true; } break; // Check payment accounts. If payment and preferred is selected, // then always show them. If only payment is selected, then // show only if preferred flag is not set. case MyMoneyAccount::Checkings: case MyMoneyAccount::Savings: case MyMoneyAccount::Cash: case MyMoneyAccount::CreditCard: switch (type & (Payment | Preferred)) { case Payment: if ((*it).value("PreferredAccount") == "Yes") removeAccount = true; break; case Preferred: if ((*it).value("PreferredAccount") != "Yes") removeAccount = true; break; case Payment | Preferred: break; default: removeAccount = true; break; } break; // filter all accounts that are not used on homepage views default: removeAccount = true; break; } } else if ((*it).isClosed() || (*it).isInvest()) { // don't show if closed or a stock account removeAccount = true; } if (removeAccount) it = accounts.erase(it); else ++it; } if (!accounts.isEmpty()) { // sort the accounts by name qStableSort(accounts.begin(), accounts.end(), accountNameLess); QString tmp; int i = 0; tmp = "
" + header + "
\n
 
\n"; d->m_html += tmp; d->m_html += ""; d->m_html += ""; if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) { QString pathStatusHeader = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::Download]).pixmap(QSize(16,16))); d->m_html += QString("").arg(pathStatusHeader); } d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions()) d->m_html += QString(""); if (KMyMoneyGlobalSettings::showCountOfClearedTransactions()) d->m_html += QString(""); if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) d->m_html += QString(""); d->m_html += ""; //only show limit info if user chose to do so if (KMyMoneyGlobalSettings::showLimitInfo()) { d->m_html += ""; } d->m_html += ""; d->m_total = 0; QList::const_iterator it_m; for (it_m = accounts.constBegin(); it_m != accounts.constEnd(); ++it_m) { d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); showAccountEntry(*it_m); d->m_html += ""; } d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); QString amount = d->m_total.formatMoney(file->baseCurrency().tradingSymbol(), prec); if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) d->m_html += ""; d->m_html += QString("").arg(i18n("Total")); if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions()) d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfClearedTransactions()) d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) d->m_html += ""; d->m_html += QString("").arg(showColoredAmount(amount, d->m_total.isNegative())); d->m_html += "
"; d->m_html += i18n("Account"); d->m_html += "!MC!R"; d->m_html += i18n("Current Balance"); d->m_html += ""; d->m_html += i18n("To Minimum Balance / Maximum Credit"); d->m_html += "
%1%1
"; } } void KHomeView::showAccountEntry(const MyMoneyAccount& acc) { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneySecurity currency = file->currency(acc.currencyId()); MyMoneyMoney value; bool showLimit = KMyMoneyGlobalSettings::showLimitInfo(); if (acc.accountType() == MyMoneyAccount::Investment) { //investment accounts show the balances of all its subaccounts value = investmentBalance(acc); //investment accounts have no minimum balance showAccountEntry(acc, value, MyMoneyMoney(), showLimit); } else { //get balance for normal accounts value = file->balance(acc.id(), QDate::currentDate()); if (acc.currencyId() != file->baseCurrency().id()) { ReportAccount repAcc = ReportAccount(acc.id()); MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); MyMoneyMoney baseValue = value * curPrice; baseValue = baseValue.convert(file->baseCurrency().smallestAccountFraction()); d->m_total += baseValue; } else { d->m_total += value; } //if credit card or checkings account, show maximum credit if (acc.accountType() == MyMoneyAccount::CreditCard || acc.accountType() == MyMoneyAccount::Checkings) { QString maximumCredit = acc.value("maxCreditAbsolute"); if (maximumCredit.isEmpty()) { maximumCredit = acc.value("minBalanceAbsolute"); } MyMoneyMoney maxCredit = MyMoneyMoney(maximumCredit); showAccountEntry(acc, value, value - maxCredit, showLimit); } else { //otherwise use minimum balance QString minimumBalance = acc.value("minBalanceAbsolute"); MyMoneyMoney minBalance = MyMoneyMoney(minimumBalance); showAccountEntry(acc, value, value - minBalance, showLimit); } } } void KHomeView::showAccountEntry(const MyMoneyAccount& acc, const MyMoneyMoney& value, const MyMoneyMoney& valueToMinBal, const bool showMinBal) { MyMoneyFile* file = MyMoneyFile::instance(); QString tmp; MyMoneySecurity currency = file->currency(acc.currencyId()); QString amount; QString amountToMinBal; //format amounts amount = MyMoneyUtils::formatMoney(value, acc, currency); amount.replace(QChar(' '), " "); if (showMinBal) { amountToMinBal = MyMoneyUtils::formatMoney(valueToMinBal, acc, currency); amountToMinBal.replace(QChar(' '), " "); } QString cellStatus, cellCounts, pathOK, pathTODO, pathNotOK; if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) { //show account's online-status pathOK = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::DialogOKApply]).pixmap(QSize(16,16))); pathTODO = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::MailReceive]).pixmap(QSize(16,16))); pathNotOK = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::DialogCancel]).pixmap(QSize(16,16))); if (acc.value("lastImportedTransactionDate").isEmpty() || acc.value("lastStatementBalance").isEmpty()) cellStatus = '-'; else if (file->hasMatchingOnlineBalance(acc)) { if (file->hasNewerTransaction(acc.id(), QDate::fromString(acc.value("lastImportedTransactionDate"), Qt::ISODate))) cellStatus = QString("").arg(pathTODO); else cellStatus = QString("").arg(pathOK); } else cellStatus = QString("").arg(pathNotOK); tmp = QString("%1").arg(cellStatus); } tmp += QString("") + link(VIEW_LEDGER, QString("?id=%1").arg(acc.id())) + acc.name() + linkend() + ""; int countNotMarked = 0, countCleared = 0, countNotReconciled = 0; QString countStr; if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions() || KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) countNotMarked = file->countTransactionsWithSpecificReconciliationState(acc.id(), MyMoneyTransactionFilter::notReconciled); if (KMyMoneyGlobalSettings::showCountOfClearedTransactions() || KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) countCleared = file->countTransactionsWithSpecificReconciliationState(acc.id(), MyMoneyTransactionFilter::cleared); if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) countNotReconciled = countNotMarked + countCleared; if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions()) { if (countNotMarked) countStr = QString("%1").arg(countNotMarked); else countStr = '-'; tmp += QString("%1").arg(countStr); } if (KMyMoneyGlobalSettings::showCountOfClearedTransactions()) { if (countCleared) countStr = QString("%1").arg(countCleared); else countStr = '-'; tmp += QString("%1").arg(countStr); } if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) { if (countNotReconciled) countStr = QString("%1").arg(countNotReconciled); else countStr = '-'; tmp += QString("%1").arg(countStr); } //show account balance tmp += QString("%1").arg(showColoredAmount(amount, value.isNegative())); //show minimum balance column if requested if (showMinBal) { //if it is an investment, show minimum balance empty if (acc.accountType() == MyMoneyAccount::Investment) { tmp += QString(" "); } else { //show minimum balance entry tmp += QString("%1").arg(showColoredAmount(amountToMinBal, valueToMinBal.isNegative())); } } // qDebug("accountEntry = '%s'", tmp.toLatin1()); d->m_html += tmp; } MyMoneyMoney KHomeView::investmentBalance(const MyMoneyAccount& acc) { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyMoney value; value = file->balance(acc.id(), QDate::currentDate()); QList::const_iterator it_a; for (it_a = acc.accountList().begin(); it_a != acc.accountList().end(); ++it_a) { MyMoneyAccount stock = file->account(*it_a); if (!stock.isClosed()) { try { MyMoneyMoney val; MyMoneyMoney balance = file->balance(stock.id(), QDate::currentDate()); MyMoneySecurity security = file->security(stock.currencyId()); const MyMoneyPrice &price = file->price(stock.currencyId(), security.tradingCurrency()); val = (balance * price.rate(security.tradingCurrency())).convertPrecision(security.pricePrecision()); // adjust value of security to the currency of the account MyMoneySecurity accountCurrency = file->currency(acc.currencyId()); val = val * file->price(security.tradingCurrency(), accountCurrency.id()).rate(accountCurrency.id()); val = val.convert(acc.fraction()); value += val; } catch (const MyMoneyException &e) { qWarning("%s", qPrintable(QString("cannot convert stock balance of %1 to base currency: %2").arg(stock.name(), e.what()))); } } } return value; } void KHomeView::showFavoriteReports() { QList reports = MyMoneyFile::instance()->reportList(); if (!reports.isEmpty()) { bool firstTime = 1; int row = 0; QList::const_iterator it_report = reports.constBegin(); while (it_report != reports.constEnd()) { if ((*it_report).isFavorite()) { if (firstTime) { d->m_html += QString("
%1
\n
 
\n").arg(i18n("Favorite Reports")); d->m_html += ""; d->m_html += ""; firstTime = false; } d->m_html += QString("") .arg(row++ & 0x01 ? "even" : "odd") .arg(link(VIEW_REPORTS, QString("?id=%1").arg((*it_report).id()))) .arg((*it_report).name()) .arg(linkend()) .arg((*it_report).comment()); } ++it_report; } if (!firstTime) d->m_html += "
"; d->m_html += i18n("Report"); d->m_html += ""; d->m_html += i18n("Comment"); d->m_html += "
%2%3%4%5
"; } } void KHomeView::showForecast() { MyMoneyFile* file = MyMoneyFile::instance(); QList accList; //if forecast has not been executed yet, do it. if (!d->m_forecast.isForecastDone()) doForecast(); accList = d->m_forecast.accountList(); if (accList.count() > 0) { // sort the accounts by name qStableSort(accList.begin(), accList.end(), accountNameLess); int i = 0; int colspan = 1; //get begin day int beginDay = QDate::currentDate().daysTo(d->m_forecast.beginForecastDate()); //if begin day is today skip to next cycle if (beginDay == 0) beginDay = d->m_forecast.accountsCycle(); // Now output header d->m_html += QString("
%1
\n
 
\n").arg(i18n("%1 Day Forecast", d->m_forecast.forecastDays())); d->m_html += ""; d->m_html += ""; int colWidth = 55 / (d->m_forecast.forecastDays() / d->m_forecast.accountsCycle()); for (i = 0; (i*d->m_forecast.accountsCycle() + beginDay) <= d->m_forecast.forecastDays(); ++i) { d->m_html += QString(""; colspan++; } d->m_html += ""; // Now output entries i = 0; QList::ConstIterator it_account; for (it_account = accList.constBegin(); it_account != accList.constEnd(); ++it_account) { //MyMoneyAccount acc = (*it_n); d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); d->m_html += QString(""; int dropZero = -1; //account dropped below zero int dropMinimum = -1; //account dropped below minimum balance QString minimumBalance = (*it_account).value("minimumBalance"); MyMoneyMoney minBalance = MyMoneyMoney(minimumBalance); MyMoneySecurity currency; MyMoneyMoney forecastBalance; //change account to deep currency if account is an investment if ((*it_account).isInvest()) { MyMoneySecurity underSecurity = file->security((*it_account).currencyId()); currency = file->security(underSecurity.tradingCurrency()); } else { currency = file->security((*it_account).currencyId()); } for (int f = beginDay; f <= d->m_forecast.forecastDays(); f += d->m_forecast.accountsCycle()) { forecastBalance = d->m_forecast.forecastBalance(*it_account, QDate::currentDate().addDays(f)); QString amount; amount = MyMoneyUtils::formatMoney(forecastBalance, *it_account, currency); amount.replace(QChar(' '), " "); d->m_html += QString("").arg(showColoredAmount(amount, forecastBalance.isNegative())); } d->m_html += ""; //Check if the account is going to be below zero or below the minimal balance in the forecast period //Check if the account is going to be below minimal balance dropMinimum = d->m_forecast.daysToMinimumBalance(*it_account); //Check if the account is going to be below zero in the future dropZero = d->m_forecast.daysToZeroBalance(*it_account); // spit out possible warnings QString msg; // if a minimum balance has been specified, an appropriate warning will // only be shown, if the drop below 0 is on a different day or not present if (dropMinimum != -1 && !minBalance.isZero() && (dropMinimum < dropZero || dropZero == -1)) { switch (dropMinimum) { case -1: break; case 0: msg = i18n("The balance of %1 is below the minimum balance %2 today.", (*it_account).name(), MyMoneyUtils::formatMoney(minBalance, *it_account, currency)); msg = showColoredAmount(msg, true); break; default: msg = i18np("The balance of %2 will drop below the minimum balance %3 in %1 day.", "The balance of %2 will drop below the minimum balance %3 in %1 days.", dropMinimum - 1, (*it_account).name(), MyMoneyUtils::formatMoney(minBalance, *it_account, currency)); msg = showColoredAmount(msg, true); break; } if (!msg.isEmpty()) { d->m_html += QString("").arg(msg).arg(colspan); } } // a drop below zero is always shown msg.clear(); switch (dropZero) { case -1: break; case 0: if ((*it_account).accountGroup() == MyMoneyAccount::Asset) { msg = i18n("The balance of %1 is below %2 today.", (*it_account).name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), *it_account, currency)); msg = showColoredAmount(msg, true); break; } if ((*it_account).accountGroup() == MyMoneyAccount::Liability) { msg = i18n("The balance of %1 is above %2 today.", (*it_account).name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), *it_account, currency)); break; } break; default: if ((*it_account).accountGroup() == MyMoneyAccount::Asset) { msg = i18np("The balance of %2 will drop below %3 in %1 day.", "The balance of %2 will drop below %3 in %1 days.", dropZero, (*it_account).name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), *it_account, currency)); msg = showColoredAmount(msg, true); break; } if ((*it_account).accountGroup() == MyMoneyAccount::Liability) { msg = i18np("The balance of %2 will raise above %3 in %1 day.", "The balance of %2 will raise above %3 in %1 days.", dropZero, (*it_account).name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), *it_account, currency)); break; } } if (!msg.isEmpty()) { d->m_html += QString("").arg(msg).arg(colspan); } } d->m_html += "
"; d->m_html += i18n("Account"); d->m_html += "").arg(colWidth); d->m_html += i18ncp("Forecast days", "%1 day", "%1 days", i * d->m_forecast.accountsCycle() + beginDay); d->m_html += "
") + link(VIEW_LEDGER, QString("?id=%1").arg((*it_account).id())) + (*it_account).name() + linkend() + "").arg(colWidth); d->m_html += QString("%1
%1
%1
"; } } QString KHomeView::link(const QString& view, const QString& query, const QString& _title) const { QString titlePart; QString title(_title); if (!title.isEmpty()) titlePart = QString(" title=\"%1\"").arg(title.replace(QLatin1Char(' '), " ")); return QString("").arg(view, query, titlePart); } QString KHomeView::linkend() const { return QStringLiteral(""); } void KHomeView::slotOpenUrl(const QUrl &url) { QString protocol = url.scheme(); QString view = url.fileName(); if (view.isEmpty()) return; QUrlQuery query(url); QString id = query.queryItemValue("id"); QString mode = query.queryItemValue("mode"); if (protocol == QLatin1String("http")) { QDesktopServices::openUrl(url); } else if (protocol == QLatin1String("mailto")) { QDesktopServices::openUrl(url); } else { KXmlGuiWindow* mw = KMyMoneyUtils::mainWindow(); Q_CHECK_PTR(mw); if (view == VIEW_LEDGER) { emit ledgerSelected(id, QString()); } else if (view == VIEW_SCHEDULE) { if (mode == QLatin1String("enter")) { emit scheduleSelected(id); QTimer::singleShot(0, mw->actionCollection()->action(kmymoney->s_Actions[Action::ScheduleEnter]), SLOT(trigger())); } else if (mode == QLatin1String("edit")) { emit scheduleSelected(id); QTimer::singleShot(0, mw->actionCollection()->action(kmymoney->s_Actions[Action::ScheduleEdit]), SLOT(trigger())); } else if (mode == QLatin1String("skip")) { emit scheduleSelected(id); QTimer::singleShot(0, mw->actionCollection()->action(kmymoney->s_Actions[Action::ScheduleSkip]), SLOT(trigger())); } else if (mode == QLatin1String("full")) { d->m_showAllSchedules = true; loadView(); } else if (mode == QLatin1String("reduced")) { d->m_showAllSchedules = false; loadView(); } } else if (view == VIEW_REPORTS) { emit reportSelected(id); } else if (view == VIEW_WELCOME) { if (mode == QLatin1String("whatsnew")) d->m_view->setHtml(KWelcomePage::whatsNewPage(), QUrl("file://")); else d->m_view->setHtml(KWelcomePage::welcomePage(), QUrl("file://")); } else if (view == QLatin1String("action")) { QTimer::singleShot(0, mw->actionCollection()->action(id), SLOT(trigger())); } else if (view == VIEW_HOME) { QList list; MyMoneyFile::instance()->accountList(list); if (list.count() == 0) { KMessageBox::information(this, i18n("Before KMyMoney can give you detailed information about your financial status, you need to create at least one account. Until then, KMyMoney shows the welcome page instead.")); } loadView(); } else { qDebug("Unknown view '%s' in KHomeView::slotOpenURL()", qPrintable(view)); } } } void KHomeView::showAssetsLiabilities() { QList accounts; QList::ConstIterator it; QList assets; QList liabilities; MyMoneyMoney netAssets; MyMoneyMoney netLiabilities; QString fontStart, fontEnd; MyMoneyFile* file = MyMoneyFile::instance(); int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); int i = 0; // get list of all accounts file->accountList(accounts); for (it = accounts.constBegin(); it != accounts.constEnd();) { if (!(*it).isClosed()) { switch ((*it).accountType()) { // group all assets into one list but make sure that investment accounts always show up case MyMoneyAccount::Investment: assets << *it; break; case MyMoneyAccount::Checkings: case MyMoneyAccount::Savings: case MyMoneyAccount::Cash: case MyMoneyAccount::Asset: case MyMoneyAccount::AssetLoan: // list account if it's the last in the hierarchy or has transactions in it if ((*it).accountList().isEmpty() || (file->transactionCount((*it).id()) > 0)) { assets << *it; } break; // group the liabilities into the other case MyMoneyAccount::CreditCard: case MyMoneyAccount::Liability: case MyMoneyAccount::Loan: // list account if it's the last in the hierarchy or has transactions in it if ((*it).accountList().isEmpty() || (file->transactionCount((*it).id()) > 0)) { liabilities << *it; } break; default: break; } } ++it; } //only do it if we have assets or liabilities account if (assets.count() > 0 || liabilities.count() > 0) { // sort the accounts by name qStableSort(assets.begin(), assets.end(), accountNameLess); qStableSort(liabilities.begin(), liabilities.end(), accountNameLess); QString statusHeader; if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) { QString pathStatusHeader; pathStatusHeader = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::ViewOutbox]).pixmap(QSize(16,16))); statusHeader = QString("").arg(pathStatusHeader); } //print header d->m_html += "
" + i18n("Assets and Liabilities Summary") + "
\n
 
\n"; d->m_html += ""; //column titles d->m_html += ""; if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) { d->m_html += ""; } d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions()) d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfClearedTransactions()) d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) d->m_html += ""; d->m_html += ""; //intermediate row to separate both columns d->m_html += ""; if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) { d->m_html += ""; } d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions()) d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfClearedTransactions()) d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) d->m_html += ""; d->m_html += ""; QString placeHolder_Status, placeHolder_Counts; if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) placeHolder_Status = ""; if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions()) placeHolder_Counts = ""; if (KMyMoneyGlobalSettings::showCountOfClearedTransactions()) placeHolder_Counts += ""; if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) placeHolder_Counts += ""; //get asset and liability accounts QList::const_iterator asset_it = assets.constBegin(); QList::const_iterator liabilities_it = liabilities.constBegin(); for (; asset_it != assets.constEnd() || liabilities_it != liabilities.constEnd();) { d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); //write an asset account if we still have any if (asset_it != assets.constEnd()) { MyMoneyMoney value; //investment accounts consolidate the balance of its subaccounts if ((*asset_it).accountType() == MyMoneyAccount::Investment) { value = investmentBalance(*asset_it); } else { value = MyMoneyFile::instance()->balance((*asset_it).id(), QDate::currentDate()); } //calculate balance for foreign currency accounts if ((*asset_it).currencyId() != file->baseCurrency().id()) { ReportAccount repAcc = ReportAccount((*asset_it).id()); MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); MyMoneyMoney baseValue = value * curPrice; baseValue = baseValue.convert(10000); netAssets += baseValue; } else { netAssets += value; } //show the account without minimum balance showAccountEntry(*asset_it, value, MyMoneyMoney(), false); ++asset_it; } else { //write a white space if we don't d->m_html += QString("%1%2").arg(placeHolder_Status).arg(placeHolder_Counts); } //leave the intermediate column empty d->m_html += ""; //write a liability account if (liabilities_it != liabilities.constEnd()) { MyMoneyMoney value; value = MyMoneyFile::instance()->balance((*liabilities_it).id(), QDate::currentDate()); //calculate balance if foreign currency if ((*liabilities_it).currencyId() != file->baseCurrency().id()) { ReportAccount repAcc = ReportAccount((*liabilities_it).id()); MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); MyMoneyMoney baseValue = value * curPrice; baseValue = baseValue.convert(10000); netLiabilities += baseValue; } else { netLiabilities += value; } //show the account without minimum balance showAccountEntry(*liabilities_it, value, MyMoneyMoney(), false); ++liabilities_it; } else { //leave the space empty if we run out of liabilities d->m_html += QString("%1%2").arg(placeHolder_Status).arg(placeHolder_Counts); } d->m_html += ""; } //calculate net worth MyMoneyMoney netWorth = netAssets + netLiabilities; //format assets, liabilities and net worth QString amountAssets = netAssets.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountLiabilities = netLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountNetWorth = netWorth.formatMoney(file->baseCurrency().tradingSymbol(), prec); amountAssets.replace(QChar(' '), " "); amountLiabilities.replace(QChar(' '), " "); amountNetWorth.replace(QChar(' '), " "); d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); //print total for assets d->m_html += QString("%1%3").arg(placeHolder_Status).arg(i18n("Total Assets")).arg(placeHolder_Counts).arg(showColoredAmount(amountAssets, netAssets.isNegative())); //leave the intermediate column empty d->m_html += ""; //print total liabilities d->m_html += QString("%1%3").arg(placeHolder_Status).arg(i18n("Total Liabilities")).arg(placeHolder_Counts).arg(showColoredAmount(amountLiabilities, netLiabilities.isNegative())); d->m_html += ""; //print net worth d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); d->m_html += QString("%1%2").arg(placeHolder_Status).arg(placeHolder_Counts); d->m_html += QString("%1%3").arg(placeHolder_Status).arg(i18n("Net Worth")).arg(placeHolder_Counts).arg(showColoredAmount(amountNetWorth, netWorth.isNegative())); d->m_html += ""; d->m_html += "
"; d->m_html += statusHeader; d->m_html += ""; d->m_html += i18n("Asset Accounts"); d->m_html += "!MC!R"; d->m_html += i18n("Current Balance"); d->m_html += ""; d->m_html += statusHeader; d->m_html += ""; d->m_html += i18n("Liability Accounts"); d->m_html += "!MC!R"; d->m_html += i18n("Current Balance"); d->m_html += "
%2%4%2%4
%2%4
"; d->m_html += "
"; } } void KHomeView::showBudget() { MyMoneyFile* file = MyMoneyFile::instance(); if (file->countBudgets()) { int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); bool isOverrun = false; int i = 0; //config report just like "Monthly Budgeted vs Actual MyMoneyReport reportCfg = MyMoneyReport( MyMoneyReport::eBudgetActual, MyMoneyReport::eMonths, MyMoneyTransactionFilter::currentMonth, MyMoneyReport::eDetailAll, i18n("Monthly Budgeted vs. Actual"), i18n("Generated Report")); reportCfg.setBudget("Any", true); reports::PivotTable table(reportCfg); PivotGrid grid = table.grid(); //div header d->m_html += "
" + i18n("Budget") + "
\n
 
\n"; //display budget summary d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += QString(""); MyMoneyMoney totalBudgetValue = grid.m_total[eBudget].m_total; MyMoneyMoney totalActualValue = grid.m_total[eActual].m_total; MyMoneyMoney totalBudgetDiffValue = grid.m_total[eBudgetDiff].m_total; QString totalBudgetAmount = totalBudgetValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString totalActualAmount = totalActualValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString totalBudgetDiffAmount = totalBudgetDiffValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); d->m_html += QString("").arg(showColoredAmount(totalBudgetAmount, totalBudgetValue.isNegative())); d->m_html += QString("").arg(showColoredAmount(totalActualAmount, totalActualValue.isNegative())); d->m_html += QString("").arg(showColoredAmount(totalBudgetDiffAmount, totalBudgetDiffValue.isNegative())); d->m_html += ""; d->m_html += "
"; d->m_html += i18n("Current Month Summary"); d->m_html += "
"; d->m_html += i18n("Budgeted"); d->m_html += ""; d->m_html += i18n("Actual"); d->m_html += ""; d->m_html += i18n("Difference"); d->m_html += "
%1%1%1
"; //budget overrun d->m_html += "
 
\n"; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; PivotGrid::iterator it_outergroup = grid.begin(); while (it_outergroup != grid.end()) { i = 0; PivotOuterGroup::iterator it_innergroup = (*it_outergroup).begin(); while (it_innergroup != (*it_outergroup).end()) { PivotInnerGroup::iterator it_row = (*it_innergroup).begin(); while (it_row != (*it_innergroup).end()) { //column number is 1 because the report includes only current month if (it_row.value()[eBudgetDiff][1].isNegative()) { //get report account to get the name later ReportAccount rowname = it_row.key(); //write the outergroup if it is the first row of outergroup being shown if (i == 0) { d->m_html += ""; d->m_html += QString("").arg(KMyMoneyUtils::accountTypeToString(rowname.accountType())); d->m_html += ""; } d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); //get values from grid MyMoneyMoney actualValue = it_row.value()[eActual][1]; MyMoneyMoney budgetValue = it_row.value()[eBudget][1]; MyMoneyMoney budgetDiffValue = it_row.value()[eBudgetDiff][1]; //format amounts QString actualAmount = actualValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString budgetAmount = budgetValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString budgetDiffAmount = budgetDiffValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); //account name d->m_html += QString(""; //show amounts d->m_html += QString("").arg(showColoredAmount(budgetAmount, budgetValue.isNegative())); d->m_html += QString("").arg(showColoredAmount(actualAmount, actualValue.isNegative())); d->m_html += QString("").arg(showColoredAmount(budgetDiffAmount, budgetDiffValue.isNegative())); d->m_html += ""; //set the flag that there are overruns isOverrun = true; } ++it_row; } ++it_innergroup; } ++it_outergroup; } //if no negative differences are found, then inform that if (!isOverrun) { d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); d->m_html += QString("").arg(i18n("No Budget Categories have been overrun")); d->m_html += ""; } d->m_html += "
"; d->m_html += i18n("Budget Overruns"); d->m_html += "
"; d->m_html += i18n("Account"); d->m_html += ""; d->m_html += i18n("Budgeted"); d->m_html += ""; d->m_html += i18n("Actual"); d->m_html += ""; d->m_html += i18n("Difference"); d->m_html += "
%1
") + link(VIEW_LEDGER, QString("?id=%1").arg(rowname.id())) + rowname.name() + linkend() + "%1%1%1
%1
"; } } QString KHomeView::showColoredAmount(const QString& amount, bool isNegative) { if (isNegative) { //if negative, get the settings for negative numbers return QString("%2").arg(KMyMoneyGlobalSettings::listNegativeValueColor().name(), amount); } //if positive, return the same string return amount; } void KHomeView::doForecast() { //clear m_accountList because forecast is about to changed d->m_accountList.clear(); //reinitialize the object d->m_forecast = KMyMoneyGlobalSettings::forecast(); //If forecastDays lower than accountsCycle, adjust to the first cycle if (d->m_forecast.accountsCycle() > d->m_forecast.forecastDays()) d->m_forecast.setForecastDays(d->m_forecast.accountsCycle()); //Get all accounts of the right type to calculate forecast d->m_forecast.doForecast(); } MyMoneyMoney KHomeView::forecastPaymentBalance(const MyMoneyAccount& acc, const MyMoneyMoney& payment, QDate& paymentDate) { //if paymentDate before or equal to currentDate set it to current date plus 1 //so we get to accumulate forecast balance correctly if (paymentDate <= QDate::currentDate()) paymentDate = QDate::currentDate().addDays(1); //check if the account is already there if (d->m_accountList.find(acc.id()) == d->m_accountList.end() || d->m_accountList[acc.id()].find(paymentDate) == d->m_accountList[acc.id()].end()) { if (paymentDate == QDate::currentDate()) { d->m_accountList[acc.id()][paymentDate] = d->m_forecast.forecastBalance(acc, paymentDate); } else { d->m_accountList[acc.id()][paymentDate] = d->m_forecast.forecastBalance(acc, paymentDate.addDays(-1)); } } d->m_accountList[acc.id()][paymentDate] = d->m_accountList[acc.id()][paymentDate] + payment; return d->m_accountList[acc.id()][paymentDate]; } void KHomeView::showCashFlowSummary() { MyMoneyTransactionFilter filter; MyMoneyMoney incomeValue; MyMoneyMoney expenseValue; MyMoneyFile* file = MyMoneyFile::instance(); int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); //set start and end of month dates QDate startOfMonth = QDate(QDate::currentDate().year(), QDate::currentDate().month(), 1); QDate endOfMonth = QDate(QDate::currentDate().year(), QDate::currentDate().month(), QDate::currentDate().daysInMonth()); //Add total income and expenses for this month //get transactions for current month filter.setDateFilter(startOfMonth, endOfMonth); filter.setReportAllSplits(false); QList transactions = file->transactionList(filter); //if no transaction then skip and print total in zero if (transactions.size() > 0) { QList::const_iterator it_transaction; //get all transactions for this month for (it_transaction = transactions.constBegin(); it_transaction != transactions.constEnd(); ++it_transaction) { //get the splits for each transaction const QList& splits = (*it_transaction).splits(); QList::const_iterator it_split; for (it_split = splits.begin(); it_split != splits.end(); ++it_split) { if (!(*it_split).shares().isZero()) { ReportAccount repSplitAcc = ReportAccount((*it_split).accountId()); //only add if it is an income or expense if (repSplitAcc.isIncomeExpense()) { MyMoneyMoney value; //convert to base currency if necessary if (repSplitAcc.currencyId() != file->baseCurrency().id()) { MyMoneyMoney curPrice = repSplitAcc.baseCurrencyPrice((*it_transaction).postDate()); value = ((*it_split).shares() * MyMoneyMoney::MINUS_ONE) * curPrice; value = value.convert(10000); } else { value = ((*it_split).shares() * MyMoneyMoney::MINUS_ONE); } //store depending on account type if (repSplitAcc.accountType() == MyMoneyAccount::Income) { incomeValue += value; } else { expenseValue += value; } } } } } } //format income and expenses QString amountIncome = incomeValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountExpense = expenseValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); amountIncome.replace(QChar(' '), " "); amountExpense.replace(QChar(' '), " "); //calculate schedules //Add all schedules for this month MyMoneyMoney scheduledIncome; MyMoneyMoney scheduledExpense; MyMoneyMoney scheduledLiquidTransfer; MyMoneyMoney scheduledOtherTransfer; //get overdues and schedules until the end of this month QList schedule = file->scheduleList("", MyMoneySchedule::TYPE_ANY, MyMoneySchedule::OCCUR_ANY, MyMoneySchedule::STYPE_ANY, QDate(), endOfMonth); //Remove the finished schedules QList::Iterator finished_it; for (finished_it = schedule.begin(); finished_it != schedule.end();) { if ((*finished_it).isFinished()) { finished_it = schedule.erase(finished_it); continue; } ++finished_it; } //add income and expenses QList::Iterator sched_it; for (sched_it = schedule.begin(); sched_it != schedule.end();) { QDate nextDate = (*sched_it).nextDueDate(); int cnt = 0; while (nextDate.isValid() && nextDate <= endOfMonth) { ++cnt; nextDate = (*sched_it).nextPayment(nextDate); // for single occurrence nextDate will not change, so we // better get out of here. if ((*sched_it).occurrence() == MyMoneySchedule::OCCUR_ONCE) break; } MyMoneyAccount acc = (*sched_it).account(); if (!acc.id().isEmpty()) { MyMoneyTransaction transaction = (*sched_it).transaction(); // only show the entry, if it is still active MyMoneySplit sp = transaction.splitByAccount(acc.id(), true); // take care of the autoCalc stuff if ((*sched_it).type() == MyMoneySchedule::TYPE_LOANPAYMENT) { QDate nextDate = (*sched_it).nextPayment((*sched_it).lastPayment()); //make sure we have all 'starting balances' so that the autocalc works QList::const_iterator it_s; QMap balanceMap; for (it_s = transaction.splits().constBegin(); it_s != transaction.splits().constEnd(); ++it_s) { MyMoneyAccount acc = file->account((*it_s).accountId()); // collect all overdues on the first day QDate schedDate = nextDate; if (QDate::currentDate() >= nextDate) schedDate = QDate::currentDate().addDays(1); balanceMap[acc.id()] += file->balance(acc.id(), QDate::currentDate()); } KMyMoneyUtils::calculateAutoLoan(*sched_it, transaction, balanceMap); } //go through the splits and assign to liquid or other transfers const QList splits = transaction.splits(); QList::const_iterator split_it; for (split_it = splits.constBegin(); split_it != splits.constEnd(); ++split_it) { if ((*split_it).accountId() != acc.id()) { ReportAccount repSplitAcc = ReportAccount((*split_it).accountId()); //get the shares and multiply by the quantity of occurrences in the period MyMoneyMoney value = (*split_it).shares() * cnt; //convert to foreign currency if needed if (repSplitAcc.currencyId() != file->baseCurrency().id()) { MyMoneyMoney curPrice = repSplitAcc.baseCurrencyPrice(QDate::currentDate()); value = value * curPrice; value = value.convert(10000); } if ((repSplitAcc.isLiquidLiability() || repSplitAcc.isLiquidAsset()) && acc.accountGroup() != repSplitAcc.accountGroup()) { scheduledLiquidTransfer += value; } else if (repSplitAcc.isAssetLiability() && !repSplitAcc.isLiquidLiability() && !repSplitAcc.isLiquidAsset()) { scheduledOtherTransfer += value; } else if (repSplitAcc.isIncomeExpense()) { //income and expenses are stored as negative values if (repSplitAcc.accountType() == MyMoneyAccount::Income) scheduledIncome -= value; if (repSplitAcc.accountType() == MyMoneyAccount::Expense) scheduledExpense -= value; } } } } ++sched_it; } //format the currency strings QString amountScheduledIncome = scheduledIncome.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountScheduledExpense = scheduledExpense.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountScheduledLiquidTransfer = scheduledLiquidTransfer.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountScheduledOtherTransfer = scheduledOtherTransfer.formatMoney(file->baseCurrency().tradingSymbol(), prec); amountScheduledIncome.replace(QChar(' '), " "); amountScheduledExpense.replace(QChar(' '), " "); amountScheduledLiquidTransfer.replace(QChar(' '), " "); amountScheduledOtherTransfer.replace(QChar(' '), " "); //get liquid assets and liabilities QList accounts; QList::const_iterator account_it; MyMoneyMoney liquidAssets; MyMoneyMoney liquidLiabilities; // get list of all accounts file->accountList(accounts); for (account_it = accounts.constBegin(); account_it != accounts.constEnd();) { if (!(*account_it).isClosed()) { switch ((*account_it).accountType()) { //group all assets into one list case MyMoneyAccount::Checkings: case MyMoneyAccount::Savings: case MyMoneyAccount::Cash: { MyMoneyMoney value = MyMoneyFile::instance()->balance((*account_it).id(), QDate::currentDate()); //calculate balance for foreign currency accounts if ((*account_it).currencyId() != file->baseCurrency().id()) { ReportAccount repAcc = ReportAccount((*account_it).id()); MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); MyMoneyMoney baseValue = value * curPrice; liquidAssets += baseValue; liquidAssets = liquidAssets.convert(10000); } else { liquidAssets += value; } break; } //group the liabilities into the other case MyMoneyAccount::CreditCard: { MyMoneyMoney value; value = MyMoneyFile::instance()->balance((*account_it).id(), QDate::currentDate()); //calculate balance if foreign currency if ((*account_it).currencyId() != file->baseCurrency().id()) { ReportAccount repAcc = ReportAccount((*account_it).id()); MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); MyMoneyMoney baseValue = value * curPrice; liquidLiabilities += baseValue; liquidLiabilities = liquidLiabilities.convert(10000); } else { liquidLiabilities += value; } break; } default: break; } } ++account_it; } //calculate net worth MyMoneyMoney liquidWorth = liquidAssets + liquidLiabilities; //format assets, liabilities and net worth QString amountLiquidAssets = liquidAssets.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountLiquidLiabilities = liquidLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountLiquidWorth = liquidWorth.formatMoney(file->baseCurrency().tradingSymbol(), prec); amountLiquidAssets.replace(QChar(' '), " "); amountLiquidLiabilities.replace(QChar(' '), " "); amountLiquidWorth.replace(QChar(' '), " "); //show the summary d->m_html += "
" + i18n("Cash Flow Summary") + "
\n
 
\n"; //print header d->m_html += ""; //income and expense title d->m_html += ""; d->m_html += ""; //column titles d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; //add row with banding d->m_html += QString(""); //print current income d->m_html += QString("").arg(showColoredAmount(amountIncome, incomeValue.isNegative())); //print the scheduled income d->m_html += QString("").arg(showColoredAmount(amountScheduledIncome, scheduledIncome.isNegative())); //print current expenses d->m_html += QString("").arg(showColoredAmount(amountExpense, expenseValue.isNegative())); //print the scheduled expenses d->m_html += QString("").arg(showColoredAmount(amountScheduledExpense, scheduledExpense.isNegative())); d->m_html += ""; d->m_html += "
"; d->m_html += i18n("Income and Expenses of Current Month"); d->m_html += "
"; d->m_html += i18n("Income"); d->m_html += ""; d->m_html += i18n("Scheduled Income"); d->m_html += ""; d->m_html += i18n("Expenses"); d->m_html += ""; d->m_html += i18n("Scheduled Expenses"); d->m_html += "
%2%2%2%2
"; //print header of assets and liabilities d->m_html += "
 
\n"; d->m_html += ""; //assets and liabilities title d->m_html += ""; d->m_html += ""; //column titles d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; //add row with banding d->m_html += QString(""); //print current liquid assets d->m_html += QString("").arg(showColoredAmount(amountLiquidAssets, liquidAssets.isNegative())); //print the scheduled transfers d->m_html += QString("").arg(showColoredAmount(amountScheduledLiquidTransfer, scheduledLiquidTransfer.isNegative())); //print current liabilities d->m_html += QString("").arg(showColoredAmount(amountLiquidLiabilities, liquidLiabilities.isNegative())); //print the scheduled transfers d->m_html += QString("").arg(showColoredAmount(amountScheduledOtherTransfer, scheduledOtherTransfer.isNegative())); d->m_html += ""; d->m_html += "
"; d->m_html += i18n("Liquid Assets and Liabilities"); d->m_html += "
"; d->m_html += i18n("Liquid Assets"); d->m_html += ""; d->m_html += i18n("Transfers to Liquid Liabilities"); d->m_html += ""; d->m_html += i18n("Liquid Liabilities"); d->m_html += ""; d->m_html += i18n("Other Transfers"); d->m_html += "
%2%2%2%2
"; //final conclusion MyMoneyMoney profitValue = incomeValue + expenseValue + scheduledIncome + scheduledExpense; MyMoneyMoney expectedAsset = liquidAssets + scheduledIncome + scheduledExpense + scheduledLiquidTransfer + scheduledOtherTransfer; MyMoneyMoney expectedLiabilities = liquidLiabilities + scheduledLiquidTransfer; QString amountExpectedAsset = expectedAsset.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountExpectedLiabilities = expectedLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountProfit = profitValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); amountProfit.replace(QChar(' '), " "); amountExpectedAsset.replace(QChar(' '), " "); amountExpectedLiabilities.replace(QChar(' '), " "); //print header of cash flow status d->m_html += "
 
\n"; d->m_html += ""; //income and expense title d->m_html += ""; d->m_html += ""; //column titles d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; //add row with banding d->m_html += QString(""); d->m_html += ""; //print expected assets d->m_html += QString("").arg(showColoredAmount(amountExpectedAsset, expectedAsset.isNegative())); //print expected liabilities d->m_html += QString("").arg(showColoredAmount(amountExpectedLiabilities, expectedLiabilities.isNegative())); //print expected profit d->m_html += QString("").arg(showColoredAmount(amountProfit, profitValue.isNegative())); d->m_html += ""; d->m_html += "
"; d->m_html += i18n("Cash Flow Status"); d->m_html += "
 "; d->m_html += i18n("Expected Liquid Assets"); d->m_html += ""; d->m_html += i18n("Expected Liquid Liabilities"); d->m_html += ""; d->m_html += i18n("Expected Profit/Loss"); d->m_html += "
 %2%2%2
"; d->m_html += "
"; } // Make sure, that these definitions are only used within this file // this does not seem to be necessary, but when building RPMs the // build option 'final' is used and all CPP files are concatenated. // So it could well be, that in another CPP file these definitions // are also used. #undef VIEW_LEDGER #undef VIEW_SCHEDULE #undef VIEW_WELCOME #undef VIEW_HOME #undef VIEW_REPORTS diff --git a/kmymoney/views/khomeview.h b/kmymoney/views/khomeview.h index 79a200699..8101650f9 100644 --- a/kmymoney/views/khomeview.h +++ b/kmymoney/views/khomeview.h @@ -1,142 +1,147 @@ /*************************************************************************** khomeview.h - description ------------------- begin : Tue Jan 22 2002 copyright : (C) 2000-2002 by Michael Edwardes Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KHOMEVIEW_H #define KHOMEVIEW_H // ---------------------------------------------------------------------------- // QT Includes #include -#include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyschedule.h" #include "mymoneyaccount.h" #include "kmymoneyview.h" +#ifdef ENABLE_WEBENGINE +class QWebEngineView; +#else +class KWebView; +#endif + /** * Displays a 'home page' for the user. Similar to concepts used in * quicken and m$-money. * * @author Michael Edwardes * * @short A view containing the home page for kmymoney. **/ class KHomeView : public KMyMoneyViewBase { Q_OBJECT public: /** * Definition of bitmap used as argument for showAccounts(). */ enum paymentTypeE { Preferred = 1, ///< show preferred accounts Payment = 2 ///< show payment accounts }; explicit KHomeView(QWidget *parent = 0, const char *name = 0); ~KHomeView(); protected: virtual void wheelEvent(QWheelEvent *event); void showPayments(); void showPaymentEntry(const MyMoneySchedule&, int cnt = 1); void showAccounts(paymentTypeE type, const QString& hdr); void showAccountEntry(const MyMoneyAccount&); void showFavoriteReports(); void showForecast(); void showNetWorthGraph(); void showSummary(); void showAssetsLiabilities(); void showIncomeExpenseSummary(); void showSchedulesSummary(); void showBudget(); void showCashFlowSummary(); QString link(const QString& view, const QString& query, const QString& title = QString()) const; QString linkend() const; void loadView(); /** * Overridden so we can emit the activated signal. * * @return Nothing. */ void showEvent(QShowEvent* event); public slots: void slotOpenUrl(const QUrl &url); void slotLoadView(); /** * Print the current view */ void slotPrintView(); signals: void ledgerSelected(const QString& id, const QString& transaction); void scheduleSelected(const QString& id); void reportSelected(const QString& id); private: /// \internal d-pointer class. class Private; /// \internal d-pointer instance. Private* const d; /** * Print an account and its balance and limit */ void showAccountEntry(const MyMoneyAccount& acc, const MyMoneyMoney& value, const MyMoneyMoney& valueToMinBal, const bool showMinBal); /** * @param acc the investment account * @return the balance in the currency of the investment account */ MyMoneyMoney investmentBalance(const MyMoneyAccount& acc); /** * Print text in the color set for negative numbers, if @p amount is negative * abd @p isNegative is true */ QString showColoredAmount(const QString& amount, bool isNegative); /** * Run the forecast */ void doForecast(); /** * Calculate the forecast balance after a payment has been made */ MyMoneyMoney forecastPaymentBalance(const MyMoneyAccount& acc, const MyMoneyMoney& payment, QDate& paymentDate); QPrinter *m_currentPrinter; }; #endif diff --git a/kmymoney/views/kmymoneyview.cpp b/kmymoney/views/kmymoneyview.cpp index 50b59c9d0..d67586caf 100644 --- a/kmymoney/views/kmymoneyview.cpp +++ b/kmymoney/views/kmymoneyview.cpp @@ -1,2221 +1,2213 @@ /*************************************************************************** kmymoneyview.cpp ------------------- copyright : (C) 2000-2001 by Michael Edwardes 2004 by Thomas Baumgart 2017 by Ł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. * * * ***************************************************************************/ #include "kmymoneyview.h" // ---------------------------------------------------------------------------- // Std Includes #include // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include #include #include #include #include #ifdef KF5Activities_FOUND #include #endif // ---------------------------------------------------------------------------- // Project Includes #include #include #include #include "kendingbalancedlg.h" #include "kchooseimportexportdlg.h" #include "kimportdlg.h" #include "kexportdlg.h" #include "knewloanwizard.h" #include "kcurrencyeditdlg.h" #include "kfindtransactiondlg.h" #include "knewbankdlg.h" #include "mymoneyseqaccessmgr.h" #include "mymoneydatabasemgr.h" #include "imymoneystorageformat.h" #include "mymoneystoragebin.h" #include "mymoneyexception.h" #include "mymoneystoragexml.h" #include "mymoneystoragesql.h" #include "mymoneygncreader.h" #include "mymoneystorageanon.h" #include #include "khomeview.h" #include "kaccountsview.h" #include "kcategoriesview.h" #include "kinstitutionsview.h" #include "kpayeesview.h" #include "ktagsview.h" #include "kscheduledview.h" #include "kgloballedgerview.h" #include "simpleledgerview.h" #include "kinvestmentview.h" #include "kreportsview.h" #include "kbudgetview.h" #include "kforecastview.h" #include "konlinejoboutbox.h" #include "kmymoney.h" #include "kmymoneyutils.h" #include "models.h" #include using namespace Icons; static constexpr KCompressionDevice::CompressionType COMPRESSION_TYPE = KCompressionDevice::GZip; static constexpr char recoveryKeyId[] = "0xD2B08440"; KMyMoneyView::KMyMoneyView(KMyMoneyApp *kmymoney) : KPageWidget(nullptr), m_header(0), m_inConstructor(true), m_fileOpen(false), m_fmode(0600), m_lastViewSelected(0) #ifdef KF5Activities_FOUND , m_activityResourceInstance(0) #endif { // this is a workaround for the bug in KPageWidget that causes the header to be shown // for a short while during page switch which causes a kind of bouncing of the page's // content and if the page's content is at it's minimum size then during a page switch // the main window's size is also increased to fit the header that is shown for a sort // period - reading the code in kpagewidget.cpp we know that the header should be at (1,1) // in a grid layout so if we find it there remove it for good to avoid the described issues QGridLayout* gridLayout = qobject_cast(layout()); if (gridLayout) { QLayoutItem* headerItem = gridLayout->itemAtPosition(1, 1); // make sure that we remove only the header - we avoid surprises if the header is not at (1,1) in the layout if (headerItem && qobject_cast(headerItem->widget()) != NULL) { gridLayout->removeItem(headerItem); // after we remove the KPageWidget standard header replace it with our own title label m_header = new KMyMoneyTitleLabel(this); m_header->setObjectName("titleLabel"); m_header->setMinimumSize(QSize(100, 30)); m_header->setRightImageFile("pics/titlelabel_background.png"); m_header->setVisible(KMyMoneyGlobalSettings::showTitleBar()); gridLayout->addWidget(m_header, 1, 1); } } newStorage(); m_model = new KPageWidgetModel(this); // cannot be parentless, otherwise segfaults at exit connect(kmymoney, SIGNAL(fileLoaded(QUrl)), this, SLOT(slotRefreshViews())); // let the accounts model know which account is being currently reconciled connect(this, SIGNAL(reconciliationStarts(MyMoneyAccount,QDate,MyMoneyMoney)), Models::instance()->accountsModel(), SLOT(slotReconcileAccount(MyMoneyAccount,QDate,MyMoneyMoney))); // Page 0 m_homeView = new KHomeView(); viewFrames[View::Home] = m_model->addPage(m_homeView, i18n("Home")); viewFrames[View::Home]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewHome])); connect(m_homeView, SIGNAL(ledgerSelected(QString,QString)), this, SLOT(slotLedgerSelected(QString,QString))); connect(m_homeView, SIGNAL(scheduleSelected(QString)), this, SLOT(slotScheduleSelected(QString))); connect(m_homeView, SIGNAL(reportSelected(QString)), this, SLOT(slotShowReport(QString))); connect(m_homeView, SIGNAL(aboutToShow()), this, SIGNAL(aboutToChangeView())); // Page 1 m_institutionsView = new KInstitutionsView(kmymoney, this); viewFrames[View::Institutions] = m_model->addPage(m_institutionsView, i18n("Institutions")); viewFrames[View::Institutions]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewInstitutions])); // Page 2 m_accountsView = new KAccountsView(kmymoney, this); viewFrames[View::Accounts] = m_model->addPage(m_accountsView, i18n("Accounts")); viewFrames[View::Accounts]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewAccounts])); // Page 3 m_scheduledView = new KScheduledView(); //this is to solve the way long strings are handled differently among versions of KPageWidget viewFrames[View::Schedules] = m_model->addPage(m_scheduledView, i18n("Scheduled transactions")); viewFrames[View::Schedules]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewSchedules])); connect(m_scheduledView, SIGNAL(scheduleSelected(MyMoneySchedule)), kmymoney, SLOT(slotSelectSchedule(MyMoneySchedule))); connect(m_scheduledView, SIGNAL(openContextMenu()), kmymoney, SLOT(slotShowScheduleContextMenu())); connect(m_scheduledView, SIGNAL(enterSchedule()), kmymoney, SLOT(slotScheduleEnter())); connect(m_scheduledView, SIGNAL(skipSchedule()), kmymoney, SLOT(slotScheduleSkip())); connect(m_scheduledView, SIGNAL(editSchedule()), kmymoney, SLOT(slotScheduleEdit())); connect(m_scheduledView, SIGNAL(aboutToShow()), this, SIGNAL(aboutToChangeView())); // Page 4 m_categoriesView = new KCategoriesView(kmymoney, this); viewFrames[View::Categories] = m_model->addPage(m_categoriesView, i18n("Categories")); viewFrames[View::Categories]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewCategories])); // Page 5 m_tagsView = new KTagsView(); viewFrames[View::Tags] = m_model->addPage(m_tagsView, i18n("Tags")); viewFrames[View::Tags]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewTags])); connect(kmymoney, SIGNAL(tagCreated(QString)), m_tagsView, SLOT(slotSelectTagAndTransaction(QString))); connect(kmymoney, SIGNAL(tagRename()), m_tagsView, SLOT(slotRenameButtonCliked())); connect(m_tagsView, SIGNAL(openContextMenu(MyMoneyObject)), kmymoney, SLOT(slotShowTagContextMenu())); connect(m_tagsView, SIGNAL(selectObjects(QList)), kmymoney, SLOT(slotSelectTags(QList))); connect(m_tagsView, SIGNAL(transactionSelected(QString,QString)), this, SLOT(slotLedgerSelected(QString,QString))); connect(m_tagsView, SIGNAL(aboutToShow()), this, SIGNAL(aboutToChangeView())); // Page 6 m_payeesView = new KPayeesView(); viewFrames[View::Payees] = m_model->addPage(m_payeesView, i18n("Payees")); viewFrames[View::Payees]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewPayees])); connect(kmymoney, SIGNAL(payeeCreated(QString)), m_payeesView, SLOT(slotSelectPayeeAndTransaction(QString))); connect(kmymoney, SIGNAL(payeeRename()), m_payeesView, SLOT(slotRenameButtonCliked())); connect(m_payeesView, SIGNAL(openContextMenu(MyMoneyObject)), kmymoney, SLOT(slotShowPayeeContextMenu())); connect(m_payeesView, SIGNAL(selectObjects(QList)), kmymoney, SLOT(slotSelectPayees(QList))); connect(m_payeesView, SIGNAL(transactionSelected(QString,QString)), this, SLOT(slotLedgerSelected(QString,QString))); connect(m_payeesView, SIGNAL(aboutToShow()), this, SIGNAL(aboutToChangeView())); // Page 7 m_ledgerView = new KGlobalLedgerView(); viewFrames[View::Ledgers] = m_model->addPage(m_ledgerView, i18n("Ledgers")); viewFrames[View::Ledgers]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewLedgers])); connect(m_ledgerView, SIGNAL(accountSelected(MyMoneyObject)), kmymoney, SLOT(slotSelectAccount(MyMoneyObject))); connect(m_ledgerView, SIGNAL(openContextMenu()), kmymoney, SLOT(slotShowTransactionContextMenu())); connect(m_ledgerView, SIGNAL(transactionsSelected(KMyMoneyRegister::SelectedTransactions)), kmymoney, SLOT(slotSelectTransactions(KMyMoneyRegister::SelectedTransactions))); connect(m_ledgerView, SIGNAL(newTransaction()), kmymoney, SLOT(slotTransactionsNew())); connect(m_ledgerView, SIGNAL(cancelOrEndEdit(bool&)), kmymoney, SLOT(slotTransactionsCancelOrEnter(bool&))); connect(m_ledgerView, SIGNAL(startEdit()), kmymoney, SLOT(slotTransactionsEdit())); connect(m_ledgerView, SIGNAL(endEdit()), kmymoney, SLOT(slotTransactionsEnter())); connect(m_ledgerView, SIGNAL(toggleReconciliationFlag()), kmymoney, SLOT(slotToggleReconciliationFlag())); connect(this, SIGNAL(reconciliationStarts(MyMoneyAccount,QDate,MyMoneyMoney)), m_ledgerView, SLOT(slotSetReconcileAccount(MyMoneyAccount,QDate,MyMoneyMoney))); connect(kmymoney, SIGNAL(selectAllTransactions()), m_ledgerView, SLOT(slotSelectAllTransactions())); connect(m_ledgerView, SIGNAL(aboutToShow()), this, SIGNAL(aboutToChangeView())); // Page 8 m_investmentView = new KInvestmentView(); viewFrames[View::Investments] = m_model->addPage(m_investmentView, i18n("Investments")); viewFrames[View::Investments]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewInvestment])); connect(m_investmentView, SIGNAL(accountSelected(QString,QString)), this, SLOT(slotLedgerSelected(QString,QString))); connect(m_investmentView, SIGNAL(accountSelected(MyMoneyObject)), kmymoney, SLOT(slotSelectAccount(MyMoneyObject))); connect(m_investmentView, SIGNAL(investmentRightMouseClick()), kmymoney, SLOT(slotShowInvestmentContextMenu())); connect(m_investmentView, SIGNAL(aboutToShow()), this, SIGNAL(aboutToChangeView())); // Page 9 m_reportsView = new KReportsView(); viewFrames[View::Reports] = m_model->addPage(m_reportsView, i18n("Reports")); viewFrames[View::Reports]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewReports])); connect(m_reportsView, SIGNAL(ledgerSelected(QString,QString)), this, SLOT(slotLedgerSelected(QString,QString))); connect(m_reportsView, SIGNAL(aboutToShow()), this, SIGNAL(aboutToChangeView())); // Page 10 m_budgetView = new KBudgetView(kmymoney, this); viewFrames[View::Budget] = m_model->addPage(m_budgetView, i18n("Budgets")); viewFrames[View::Budget]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewBudgets])); // Page 11 m_forecastView = new KForecastView(); viewFrames[View::Forecast] = m_model->addPage(m_forecastView, i18n("Forecast")); viewFrames[View::Forecast]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewForecast])); connect(m_forecastView, SIGNAL(aboutToShow()), this, SIGNAL(aboutToChangeView())); // Page 12 m_onlineJobOutboxView = new KOnlineJobOutbox(); viewFrames[View::OnlineJobOutbox] = m_model->addPage(m_onlineJobOutboxView, i18n("Outbox")); viewFrames[View::OnlineJobOutbox]->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewOutbox])); connect(m_onlineJobOutboxView, SIGNAL(sendJobs(QList)), kmymoney, SLOT(slotOnlineJobSend(QList))); connect(m_onlineJobOutboxView, SIGNAL(editJob(QString)), kmymoney, SLOT(slotEditOnlineJob(QString))); connect(m_onlineJobOutboxView, SIGNAL(newCreditTransfer()), kmymoney, SLOT(slotNewOnlineTransfer())); connect(m_onlineJobOutboxView, SIGNAL(aboutToShow()), this, SIGNAL(aboutToChangeView())); connect(m_onlineJobOutboxView, SIGNAL(showContextMenu(onlineJob)), kmymoney, SLOT(slotShowOnlineJobContextMenu())); SimpleLedgerView* view = new SimpleLedgerView; KPageWidgetItem* frame = m_model->addPage(view, i18n("New ledger")); frame->setIcon(QIcon::fromTheme(g_Icons[Icon::DocumentProperties])); connect(this, SIGNAL(fileClosed()), view, SLOT(closeLedgers())); connect(this, SIGNAL(fileOpened()), view, SLOT(openFavoriteLedgers())); //set the model setModel(m_model); setCurrentPage(viewFrames[View::Home]); connect(this, SIGNAL(currentPageChanged(QModelIndex,QModelIndex)), this, SLOT(slotCurrentPageChanged(QModelIndex,QModelIndex))); updateViewType(); m_inConstructor = false; // Initialize kactivities resource instance #ifdef KF5Activities_FOUND m_activityResourceInstance = new KActivities::ResourceInstance(window()->winId(), this); connect(kmymoney, SIGNAL(fileLoaded(QUrl)), m_activityResourceInstance, SLOT(setUri(QUrl))); #endif } KMyMoneyView::~KMyMoneyView() { KMyMoneyGlobalSettings::setLastViewSelected(m_lastViewSelected); removeStorage(); } void KMyMoneyView::showTitleBar(bool show) { if (m_header) m_header->setVisible(show); } void KMyMoneyView::updateViewType() { // set the face type KPageView::FaceType faceType = KPageView::List; switch (KMyMoneySettings::viewType()) { case 0: faceType = KPageView::List; break; case 1: faceType = KPageView::Tree; break; case 2: faceType = KPageView::Tabbed; break; } if (faceType != KMyMoneyView::faceType()) { setFaceType(faceType); if (faceType == KPageView::Tree) { QList views = findChildren(); foreach (QTreeView * view, views) { if (view && (view->parent() == this)) { view->setRootIsDecorated(false); break; } } } } } void KMyMoneyView::slotAccountTreeViewChanged(const AccountsModel::Columns column, const bool show) { struct viewInfo { KRecursiveFilterProxyModel *proxyModel; QList *proxyColumns; }; QList viewInfos; if (m_institutionsView->isLoaded()) viewInfos.append({m_institutionsView->getProxyModel(), m_institutionsView->getProxyColumns()}); if (m_accountsView->isLoaded()) viewInfos.append({m_accountsView->getProxyModel(), m_accountsView->getProxyColumns()}); if (m_categoriesView->isLoaded()) viewInfos.append({m_categoriesView->getProxyModel(), m_categoriesView->getProxyColumns()}); if (m_budgetView->isLoaded()) viewInfos.append({m_budgetView->getProxyModel(), m_budgetView->getProxyColumns()}); if (show) { Models::instance()->accountsModel()->setColumnVisibility(column, show); Models::instance()->institutionsModel()->setColumnVisibility(column, show); if (viewInfos.count() == 1 || KMessageBox::questionYesNo(this, i18n("Do you want to show %1 column on every loaded view?", AccountsModel::getHeaderName(column)), QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QStringLiteral("ShowColumnOnEveryView")) == KMessageBox::Yes) { foreach(viewInfo view, viewInfos) { if (!view.proxyColumns->contains(column)) { view.proxyColumns->append(column); view.proxyModel->invalidate(); } } } } else { if (viewInfos.count() == 1 || KMessageBox::questionYesNo(this, i18n("Do you want to hide %1 column on every loaded view?", AccountsModel::getHeaderName(column)), QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QStringLiteral("HideColumnOnEveryView")) == KMessageBox::Yes) { Models::instance()->accountsModel()->setColumnVisibility(column, show); Models::instance()->institutionsModel()->setColumnVisibility(column, show); foreach(viewInfo view, viewInfos) { if (view.proxyColumns->contains(column)) { view.proxyColumns->removeOne(column); view.proxyModel->invalidate(); } } } } } void KMyMoneyView::slotNetBalProChanged(const MyMoneyMoney &val, QLabel *label, const View view) { QString s; const auto isNegative = val.isNegative(); switch (view) { case View::Institutions: case View::Accounts: s = i18n("Net Worth: "); break; case View::Categories: if (isNegative) s = i18n("Loss: "); else s = i18n("Profit: "); break; case View::Budget: s = (i18nc("The balance of the selected budget", "Balance: ")); break; default: return; } // FIXME figure out how to deal with the approximate // if(!(file->totalValueValid(assetAccount.id()) & file->totalValueValid(liabilityAccount.id()))) // s += "~ "; s.replace(QLatin1Char(' '), QLatin1String(" ")); if (isNegative) s.append(QLatin1String("")); const auto sec = MyMoneyFile::instance()->baseCurrency(); QString v(MyMoneyUtils::formatMoney(val, sec)); s.append((v.replace(QLatin1Char(' '), QLatin1String(" ")))); if (isNegative) s.append(QLatin1String("")); label->setFont(KMyMoneyGlobalSettings::listCellFont()); label->setText(s); } bool KMyMoneyView::showPageHeader() const { return false; } void KMyMoneyView::showPage(KPageWidgetItem* pageItem) { // reset all selected items before showing the selected view // but not while we're in our own constructor if (!m_inConstructor && pageItem != currentPage()) { kmymoney->slotResetSelections(); } // pretend we're in the constructor to avoid calling the // above resets. For some reason which I don't know the details // of, KJanusWidget::showPage() calls itself recursively. This // screws up the action handling, as items could have been selected // in the meantime. We prevent this by setting the m_inConstructor // to true and reset it to the previos value when we leave this method. bool prevConstructor = m_inConstructor; m_inConstructor = true; setCurrentPage(pageItem); m_inConstructor = prevConstructor; if (!m_inConstructor) { // fixup some actions that are dependant on the view // this does not work during construction kmymoney->slotUpdateActions(); } } bool KMyMoneyView::canPrint() { bool rc = ( viewFrames[View::Reports] == currentPage() || viewFrames[View::Home] == currentPage() ); return rc; } bool KMyMoneyView::canCreateTransactions(const KMyMoneyRegister::SelectedTransactions& /* list */, QString& tooltip) const { // we can only create transactions in the ledger view so // we check that this is the active page bool rc = (viewFrames[View::Ledgers] == currentPage()); if (rc) rc = m_ledgerView->canCreateTransactions(tooltip); else tooltip = i18n("Creating transactions can only be performed in the ledger view"); return rc; } bool KMyMoneyView::canModifyTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const { // we can only modify transactions in the ledger view so // we check that this is the active page bool rc = (viewFrames[View::Ledgers] == currentPage()); if (rc) { rc = m_ledgerView->canModifyTransactions(list, tooltip); } else { tooltip = i18n("Modifying transactions can only be performed in the ledger view"); } return rc; } bool KMyMoneyView::canDuplicateTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const { // we can only duplicate transactions in the ledger view so // we check that this is the active page bool rc = (viewFrames[View::Ledgers] == currentPage()); if (rc) { rc = m_ledgerView->canDuplicateTransactions(list, tooltip); } else { tooltip = i18n("Duplicating transactions can only be performed in the ledger view"); } return rc; } bool KMyMoneyView::canEditTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const { bool rc; // we can only edit transactions in the ledger view so // we check that this is the active page if ((rc = canModifyTransactions(list, tooltip)) == true) { tooltip = i18n("Edit the current selected transactions"); rc = m_ledgerView->canEditTransactions(list, tooltip); } return rc; } bool KMyMoneyView::createNewTransaction() { bool rc = false; KMyMoneyRegister::SelectedTransactions list; QString txt; if (canCreateTransactions(list, txt)) { rc = m_ledgerView->selectEmptyTransaction(); } return rc; } TransactionEditor* KMyMoneyView::startEdit(const KMyMoneyRegister::SelectedTransactions& list) { TransactionEditor* editor = 0; QString txt; if (canEditTransactions(list, txt) || canCreateTransactions(list, txt)) { editor = m_ledgerView->startEdit(list); } return editor; } void KMyMoneyView::newStorage(storageTypeE t) { removeStorage(); MyMoneyFile* file = MyMoneyFile::instance(); if (t == Memory) file->attachStorage(new MyMoneySeqAccessMgr); else file->attachStorage(new MyMoneyDatabaseMgr); } void KMyMoneyView::removeStorage() { MyMoneyFile* file = MyMoneyFile::instance(); IMyMoneyStorage* p = file->storage(); if (p != 0) { file->detachStorage(p); delete p; } } void KMyMoneyView::enableViewsIfFileOpen() { // call set enabled only if the state differs to avoid widgets 'bouncing on the screen' while doing this if (viewFrames[View::Accounts]->isEnabled() != m_fileOpen) viewFrames[View::Accounts]->setEnabled(m_fileOpen); if (viewFrames[View::Institutions]->isEnabled() != m_fileOpen) viewFrames[View::Institutions]->setEnabled(m_fileOpen); if (viewFrames[View::Schedules]->isEnabled() != m_fileOpen) viewFrames[View::Schedules]->setEnabled(m_fileOpen); if (viewFrames[View::Categories]->isEnabled() != m_fileOpen) viewFrames[View::Categories]->setEnabled(m_fileOpen); if (viewFrames[View::Payees]->isEnabled() != m_fileOpen) viewFrames[View::Payees]->setEnabled(m_fileOpen); if (viewFrames[View::Tags]->isEnabled() != m_fileOpen) viewFrames[View::Tags]->setEnabled(m_fileOpen); if (viewFrames[View::Budget]->isEnabled() != m_fileOpen) viewFrames[View::Budget]->setEnabled(m_fileOpen); if (viewFrames[View::Ledgers]->isEnabled() != m_fileOpen) viewFrames[View::Ledgers]->setEnabled(m_fileOpen); if (viewFrames[View::Investments]->isEnabled() != m_fileOpen) viewFrames[View::Investments]->setEnabled(m_fileOpen); if (viewFrames[View::Reports]->isEnabled() != m_fileOpen) viewFrames[View::Reports]->setEnabled(m_fileOpen); if (viewFrames[View::Forecast]->isEnabled() != m_fileOpen) viewFrames[View::Forecast]->setEnabled(m_fileOpen); if (viewFrames[View::OnlineJobOutbox]->isEnabled() != m_fileOpen) viewFrames[View::OnlineJobOutbox]->setEnabled(m_fileOpen); emit viewStateChanged(m_fileOpen); } void KMyMoneyView::slotLedgerSelected(const QString& _accId, const QString& transaction) { MyMoneyAccount acc = MyMoneyFile::instance()->account(_accId); QString accId(_accId); switch (acc.accountType()) { case MyMoneyAccount::Stock: // if a stock account is selected, we show the // the corresponding parent (investment) account acc = MyMoneyFile::instance()->account(acc.parentAccountId()); accId = acc.id(); // intentional fall through case MyMoneyAccount::Checkings: case MyMoneyAccount::Savings: case MyMoneyAccount::Cash: case MyMoneyAccount::CreditCard: case MyMoneyAccount::Loan: case MyMoneyAccount::Asset: case MyMoneyAccount::Liability: case MyMoneyAccount::AssetLoan: case MyMoneyAccount::Income: case MyMoneyAccount::Expense: case MyMoneyAccount::Investment: case MyMoneyAccount::Equity: setCurrentPage(viewFrames[View::Ledgers]); m_ledgerView->slotSelectAccount(accId, transaction); break; case MyMoneyAccount::CertificateDep: case MyMoneyAccount::MoneyMarket: case MyMoneyAccount::Currency: qDebug("No ledger view available for account type %d", acc.accountType()); break; default: qDebug("Unknown account type %d in KMyMoneyView::slotLedgerSelected", acc.accountType()); break; } } void KMyMoneyView::slotPayeeSelected(const QString& payee, const QString& account, const QString& transaction) { showPage(viewFrames[View::Payees]); m_payeesView->slotSelectPayeeAndTransaction(payee, account, transaction); } void KMyMoneyView::slotTagSelected(const QString& tag, const QString& account, const QString& transaction) { showPage(viewFrames[View::Tags]); m_tagsView->slotSelectTagAndTransaction(tag, account, transaction); } void KMyMoneyView::slotScheduleSelected(const QString& scheduleId) { MyMoneySchedule sched = MyMoneyFile::instance()->schedule(scheduleId); kmymoney->slotSelectSchedule(sched); } void KMyMoneyView::slotShowReport(const QString& reportid) { showPage(viewFrames[View::Reports]); m_reportsView->slotOpenReport(reportid); } void KMyMoneyView::slotShowReport(const MyMoneyReport& report) { showPage(viewFrames[View::Reports]); m_reportsView->slotOpenReport(report); } bool KMyMoneyView::fileOpen() { return m_fileOpen; } void KMyMoneyView::closeFile() { if (m_reportsView) m_reportsView->slotCloseAll(); // disconnect the signals disconnect(MyMoneyFile::instance(), SIGNAL(objectAdded(MyMoneyFile::notificationObjectT,MyMoneyObject*const)), Models::instance()->accountsModel(), SLOT(slotObjectAdded(MyMoneyFile::notificationObjectT,MyMoneyObject*const))); disconnect(MyMoneyFile::instance(), SIGNAL(objectModified(MyMoneyFile::notificationObjectT,MyMoneyObject*const)), Models::instance()->accountsModel(), SLOT(slotObjectModified(MyMoneyFile::notificationObjectT,MyMoneyObject*const))); disconnect(MyMoneyFile::instance(), SIGNAL(objectRemoved(MyMoneyFile::notificationObjectT,QString)), Models::instance()->accountsModel(), SLOT(slotObjectRemoved(MyMoneyFile::notificationObjectT,QString))); disconnect(MyMoneyFile::instance(), SIGNAL(balanceChanged(MyMoneyAccount)), Models::instance()->accountsModel(), SLOT(slotBalanceOrValueChanged(MyMoneyAccount))); disconnect(MyMoneyFile::instance(), SIGNAL(valueChanged(MyMoneyAccount)), Models::instance()->accountsModel(), SLOT(slotBalanceOrValueChanged(MyMoneyAccount))); disconnect(MyMoneyFile::instance(), SIGNAL(objectAdded(MyMoneyFile::notificationObjectT,MyMoneyObject*const)), Models::instance()->institutionsModel(), SLOT(slotObjectAdded(MyMoneyFile::notificationObjectT,MyMoneyObject*const))); disconnect(MyMoneyFile::instance(), SIGNAL(objectModified(MyMoneyFile::notificationObjectT,MyMoneyObject*const)), Models::instance()->institutionsModel(), SLOT(slotObjectModified(MyMoneyFile::notificationObjectT,MyMoneyObject*const))); disconnect(MyMoneyFile::instance(), SIGNAL(objectRemoved(MyMoneyFile::notificationObjectT,QString)), Models::instance()->institutionsModel(), SLOT(slotObjectRemoved(MyMoneyFile::notificationObjectT,QString))); disconnect(MyMoneyFile::instance(), SIGNAL(balanceChanged(MyMoneyAccount)), Models::instance()->institutionsModel(), SLOT(slotBalanceOrValueChanged(MyMoneyAccount))); disconnect(MyMoneyFile::instance(), SIGNAL(valueChanged(MyMoneyAccount)), Models::instance()->institutionsModel(), SLOT(slotBalanceOrValueChanged(MyMoneyAccount))); disconnect(MyMoneyFile::instance(), SIGNAL(dataChanged()), m_homeView, SLOT(slotLoadView())); // notify the models that the file is going to be closed (we should have something like dataChanged that reaches the models first) Models::instance()->fileClosed(); emit kmmFilePlugin(preClose); if (isDatabase()) MyMoneyFile::instance()->storage()->close(); // to log off a database user newStorage(); slotShowHomePage(); emit kmmFilePlugin(postClose); m_fileOpen = false; emit fileClosed(); } void KMyMoneyView::ungetString(QIODevice *qfile, char *buf, int len) { buf = &buf[len-1]; while (len--) { qfile->ungetChar(*buf--); } } bool KMyMoneyView::readFile(const QUrl &url) { QString filename; m_fileOpen = false; bool isEncrypted = false; IMyMoneyStorageFormat* pReader = 0; if (!url.isValid()) { qDebug("Invalid URL '%s'", qPrintable(url.url())); return false; } // disconnect the current storga manager from the engine MyMoneyFile::instance()->detachStorage(); if (url.scheme() == "sql") { // handle reading of database m_fileType = KmmDb; // get rid of the mode parameter which is now redundant QUrl newUrl(url); // TODO: port to kf5 #if 0 if (QUrlQuery(url).hasQueryItem("mode")) { newUrl.removeQueryItem("mode"); } #endif return (openDatabase(newUrl)); // on error, any message will have been displayed } IMyMoneyStorage *storage = new MyMoneySeqAccessMgr; if (url.isLocalFile()) { filename = url.toLocalFile(); } else { // TODO: port to kf5 #if 0 if (!KIO::NetAccess::download(url, filename, 0)) { KMessageBox::detailedError(this, i18n("Error while loading file '%1'.", url.url()), KIO::NetAccess::lastErrorString(), i18n("File access error")); return false; } #endif } // let's glimps into the file to figure out, if it's one // of the old (uncompressed) or new (compressed) files. QFile file(filename); QFileInfo info(file); if (!info.isFile()) { QString msg = i18n("

%1 is not a KMyMoney file.

", filename); KMessageBox::error(this, msg, i18n("Filetype Error")); return false; } m_fmode = 0600; m_fmode |= info.permission(QFile::ReadGroup) ? 040 : 0; m_fmode |= info.permission(QFile::WriteGroup) ? 020 : 0; m_fmode |= info.permission(QFile::ReadOther) ? 004 : 0; m_fmode |= info.permission(QFile::WriteOther) ? 002 : 0; bool rc = true; // There's a problem with the KFilterDev and KGPGFile classes: // One supports the at(n) member but not ungetch() together with // read() and the other does not provide an at(n) method but // supports read() that considers the ungetch() buffer. QFile // supports everything so this is not a problem. We solve the problem // for now by keeping track of which method can be used. bool haveAt = true; emit kmmFilePlugin(preOpen); if (file.open(QIODevice::ReadOnly)) { QByteArray hdr(2, '\0'); int cnt; cnt = file.read(hdr.data(), 2); file.close(); if (cnt == 2) { QIODevice* qfile = nullptr; if (QString(hdr) == QString("\037\213")) { // gzipped? qfile = new KCompressionDevice(filename, COMPRESSION_TYPE); } else if (QString(hdr) == QString("--") // PGP ASCII armored? || QString(hdr) == QString("\205\001") // PGP binary? || QString(hdr) == QString("\205\002")) { // PGP binary? if (KGPGFile::GPGAvailable()) { qfile = new KGPGFile(filename); haveAt = false; isEncrypted = true; } else { KMessageBox::sorry(this, QString("%1"). arg(i18n("GPG is not available for decryption of file %1", filename))); qfile = new QFile(file.fileName()); } } else { // we can't use file directly, as we delete qfile later on qfile = new QFile(file.fileName()); } if (qfile->open(QIODevice::ReadOnly)) { try { hdr.resize(8); if (qfile->read(hdr.data(), 8) == 8) { if (haveAt) qfile->seek(0); else ungetString(qfile, hdr.data(), 8); // Ok, we got the first block of 8 bytes. Read in the two // unsigned long int's by preserving endianess. This is // achieved by reading them through a QDataStream object qint32 magic0, magic1; QDataStream s(&hdr, QIODevice::ReadOnly); s >> magic0; s >> magic1; // If both magic numbers match (we actually read in the // text 'KMyMoney' then we assume a binary file and // construct a reader for it. Otherwise, we construct // an XML reader object. // // The expression magic0 < 30 is only used to create // a binary reader if we assume an old binary file. This // should be removed at some point. An alternative is to // check the beginning of the file against an pattern // of the XML file (e.g. '?read(hdr.data(), 70) == 70) { if (haveAt) qfile->seek(0); else ungetString(qfile, hdr.data(), 70); QRegExp kmyexp(""); QRegExp gncexp("attachStorage(storage); loadAllCurrencies(); // currency list required for gnc MyMoneyFile::instance()->detachStorage(storage); pReader = new MyMoneyGncReader; m_fileType = GncXML; } } } if (pReader) { pReader->setProgressCallback(&KMyMoneyView::progressCallback); pReader->readFile(qfile, dynamic_cast(storage)); } else { if (m_fileType == KmmBinary) { KMessageBox::sorry(this, QString("%1"). arg(i18n("File %1 contains the old binary format used by KMyMoney. Please use an older version of KMyMoney (0.8.x) that still supports this format to convert it to the new XML based format.", filename))); } else { KMessageBox::sorry(this, QString("%1"). arg(i18n("File %1 contains an unknown file format.", filename))); } rc = false; } } else { KMessageBox::sorry(this, QString("%1"). arg(i18n("Cannot read from file %1.", filename))); rc = false; } } catch (const MyMoneyException &e) { KMessageBox::sorry(this, QString("%1"). arg(i18n("Cannot load file %1. Reason: %2", filename, e.what()))); rc = false; } if (pReader) { pReader->setProgressCallback(0); delete pReader; } qfile->close(); } else { KGPGFile *gpgFile = qobject_cast(qfile); if (gpgFile && !gpgFile->errorToString().isEmpty()) { KMessageBox::sorry(this, QString("%1"). arg(i18n("The following error was encountered while decrypting file %1: %2", filename, gpgFile->errorToString()))); } else { KMessageBox::sorry(this, QString("%1"). arg(i18n("File %1 not found.", filename))); } rc = false; } delete qfile; } } else { KMessageBox::sorry(this, QString("%1"). arg(i18n("File %1 not found.", filename))); rc = false; } // things are finished, now we connect the storage to the engine // which forces a reload of the cache in the engine with those // objects that are cached MyMoneyFile::instance()->attachStorage(storage); if (rc == false) return rc; // encapsulate transactions to the engine to be able to commit/rollback MyMoneyFileTransaction ft; // make sure we setup the encryption key correctly if (isEncrypted && MyMoneyFile::instance()->value("kmm-encryption-key").isEmpty()) { MyMoneyFile::instance()->setValue("kmm-encryption-key", KMyMoneyGlobalSettings::gpgRecipientList().join(",")); } // make sure we setup the name of the base accounts in translated form try { MyMoneyFile *file = MyMoneyFile::instance(); checkAccountName(file->asset(), i18n("Asset")); checkAccountName(file->liability(), i18n("Liability")); checkAccountName(file->income(), i18n("Income")); checkAccountName(file->expense(), i18n("Expense")); checkAccountName(file->equity(), i18n("Equity")); ft.commit(); } catch (const MyMoneyException &) { } // if a temporary file was constructed by NetAccess::download, // then it will be removed with the next call. Otherwise, it // stays untouched on the local filesystem // TODO: port to kf5 //KIO::NetAccess::removeTempFile(filename); return initializeStorage(); } void KMyMoneyView::checkAccountName(const MyMoneyAccount& _acc, const QString& name) const { MyMoneyFile* file = MyMoneyFile::instance(); if (_acc.name() != name) { MyMoneyAccount acc(_acc); acc.setName(name); file->modifyAccount(acc); } } bool KMyMoneyView::openDatabase(const QUrl &url) { m_fileOpen = false; // open the database IMyMoneySerialize* pStorage = dynamic_cast(MyMoneyFile::instance()->storage()); MyMoneyDatabaseMgr* pDBMgr = 0; if (! pStorage) { pDBMgr = new MyMoneyDatabaseMgr; pStorage = dynamic_cast(pDBMgr); } QExplicitlySharedDataPointer reader = pStorage->connectToDatabase(url); QUrl dbURL(url); bool retry = true; while (retry) { switch (reader->open(dbURL, QIODevice::ReadWrite)) { case 0: // opened okay retry = false; break; case 1: // permanent error KMessageBox::detailedError(this, i18n("Cannot open database %1\n", dbURL.toDisplayString()), reader->lastError()); if (pDBMgr) { removeStorage(); delete pDBMgr; } return false; case -1: // retryable error if (KMessageBox::warningYesNo(this, reader->lastError(), PACKAGE) == KMessageBox::No) { if (pDBMgr) { removeStorage(); delete pDBMgr; } return false; } else { // TODO: port to kf5 #if 0 QString options = QUrlQuery(dbURL).queryItemValue("options") + ",override"; dbURL.removeQueryItem("mode"); // now redundant dbURL.removeQueryItem("options"); dbURL.addQueryItem("options", options); #endif } } } if (pDBMgr) { removeStorage(); MyMoneyFile::instance()->attachStorage(pDBMgr); } // single user mode; read some of the data into memory // FIXME - readFile no longer relevant? // tried removing it but then got no indication that loading was complete // also, didn't show home page reader->setProgressCallback(&KMyMoneyView::progressCallback); if (!reader->readFile()) { KMessageBox::detailedError(0, i18n("An unrecoverable error occurred while reading the database"), reader->lastError().toLatin1(), i18n("Database malfunction")); return false; } m_fileOpen = true; reader->setProgressCallback(0); return initializeStorage(); } bool KMyMoneyView::initializeStorage() { bool blocked = MyMoneyFile::instance()->signalsBlocked(); MyMoneyFile::instance()->blockSignals(true); // we check, if we have any currency in the file. If not, we load // all the default currencies we know. MyMoneyFileTransaction ft; try { updateCurrencyNames(); ft.commit(); } catch (const MyMoneyException &) { MyMoneyFile::instance()->blockSignals(blocked); return false; } // make sure, we have a base currency and all accounts are // also assigned to a currency. QString baseId; try { baseId = MyMoneyFile::instance()->baseCurrency().id(); } catch (const MyMoneyException &e) { qDebug("%s", qPrintable(e.what())); } if (baseId.isEmpty()) { // Stay in this endless loop until we have a base currency, // as without it the application does not work anymore. while (baseId.isEmpty()) { selectBaseCurrency(); try { baseId = MyMoneyFile::instance()->baseCurrency().id(); } catch (const MyMoneyException &e) { qDebug("%s", qPrintable(e.what())); } } } else { // in some odd intermediate cases there could be files out there // that have a base currency set, but still have accounts that // do not have a base currency assigned. This call will take // care of it. We can safely remove it later. // // Another work-around for this scenario is to remove the base // currency setting from the XML file by removing the line // // // // and restart the application with this file. This will force to // run the above loop. selectBaseCurrency(); } KSharedConfigPtr config = KSharedConfig::openConfig(); KPageWidgetItem* page; KConfigGroup grp = config->group("General Options"); if (KMyMoneyGlobalSettings::startLastViewSelected() != 0) page = viewFrames.value(static_cast(KMyMoneyGlobalSettings::lastViewSelected())); else page = viewFrames[View::Home]; // For debugging purposes, we can turn off the automatic fix manually // by setting the entry in kmymoneyrc to true grp = config->group("General Options"); if (grp.readEntry("SkipFix", false) != true) { MyMoneyFileTransaction ft; try { // Check if we have to modify the file before we allow to work with it IMyMoneyStorage* s = MyMoneyFile::instance()->storage(); while (s->fileFixVersion() < s->currentFixVersion()) { qDebug("%s", qPrintable((QString("testing fileFixVersion %1 < %2").arg(s->fileFixVersion()).arg(s->currentFixVersion())))); switch (s->fileFixVersion()) { case 0: fixFile_0(); s->setFileFixVersion(1); break; case 1: fixFile_1(); s->setFileFixVersion(2); break; case 2: fixFile_2(); s->setFileFixVersion(3); break; case 3: fixFile_3(); s->setFileFixVersion(4); break; // add new levels above. Don't forget to increase currentFixVersion() for all // the storage backends this fix applies to default: throw MYMONEYEXCEPTION(i18n("Unknown fix level in input file")); } } ft.commit(); } catch (const MyMoneyException &) { MyMoneyFile::instance()->blockSignals(blocked); return false; } } else { qDebug("Skipping automatic transaction fix!"); } MyMoneyFile::instance()->blockSignals(blocked); // FIXME: we need to check, if it's necessary to have this // automatic funcitonality // if there's no asset account, then automatically start the // new account wizard // kmymoney->createInitialAccount(); m_fileOpen = true; emit kmmFilePlugin(postOpen); Models::instance()->fileOpened(); // connect the needed signals connect(MyMoneyFile::instance(), SIGNAL(objectAdded(MyMoneyFile::notificationObjectT,MyMoneyObject*const)), Models::instance()->accountsModel(), SLOT(slotObjectAdded(MyMoneyFile::notificationObjectT,MyMoneyObject*const))); connect(MyMoneyFile::instance(), SIGNAL(objectModified(MyMoneyFile::notificationObjectT,MyMoneyObject*const)), Models::instance()->accountsModel(), SLOT(slotObjectModified(MyMoneyFile::notificationObjectT,MyMoneyObject*const))); connect(MyMoneyFile::instance(), SIGNAL(objectRemoved(MyMoneyFile::notificationObjectT,QString)), Models::instance()->accountsModel(), SLOT(slotObjectRemoved(MyMoneyFile::notificationObjectT,QString))); connect(MyMoneyFile::instance(), SIGNAL(balanceChanged(MyMoneyAccount)), Models::instance()->accountsModel(), SLOT(slotBalanceOrValueChanged(MyMoneyAccount))); connect(MyMoneyFile::instance(), SIGNAL(valueChanged(MyMoneyAccount)), Models::instance()->accountsModel(), SLOT(slotBalanceOrValueChanged(MyMoneyAccount))); connect(MyMoneyFile::instance(), SIGNAL(objectAdded(MyMoneyFile::notificationObjectT,MyMoneyObject*const)), Models::instance()->institutionsModel(), SLOT(slotObjectAdded(MyMoneyFile::notificationObjectT,MyMoneyObject*const))); connect(MyMoneyFile::instance(), SIGNAL(objectModified(MyMoneyFile::notificationObjectT,MyMoneyObject*const)), Models::instance()->institutionsModel(), SLOT(slotObjectModified(MyMoneyFile::notificationObjectT,MyMoneyObject*const))); connect(MyMoneyFile::instance(), SIGNAL(objectRemoved(MyMoneyFile::notificationObjectT,QString)), Models::instance()->institutionsModel(), SLOT(slotObjectRemoved(MyMoneyFile::notificationObjectT,QString))); connect(MyMoneyFile::instance(), SIGNAL(balanceChanged(MyMoneyAccount)), Models::instance()->institutionsModel(), SLOT(slotBalanceOrValueChanged(MyMoneyAccount))); connect(MyMoneyFile::instance(), SIGNAL(valueChanged(MyMoneyAccount)), Models::instance()->institutionsModel(), SLOT(slotBalanceOrValueChanged(MyMoneyAccount))); // inform everyone about new data MyMoneyFile::instance()->preloadCache(); MyMoneyFile::instance()->forceDataChanged(); // views can wait since they are going to be refresed in slotRefreshViews connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), m_homeView, SLOT(slotLoadView())); // if we currently see a different page, then select the right one if (page != currentPage()) { showPage(page); } emit fileOpened(); return true; } void KMyMoneyView::saveToLocalFile(const QString& localFile, IMyMoneyStorageFormat* pWriter, bool plaintext, const QString& keyList) { // Check GPG encryption bool encryptFile = true; bool encryptRecover = false; if (!keyList.isEmpty()) { if (!KGPGFile::GPGAvailable()) { KMessageBox::sorry(this, i18n("GPG does not seem to be installed on your system. Please make sure that GPG can be found using the standard search path. This time, encryption is disabled."), i18n("GPG not found")); encryptFile = false; } else { if (KMyMoneyGlobalSettings::encryptRecover()) { encryptRecover = true; if (!KGPGFile::keyAvailable(QString(recoveryKeyId))) { KMessageBox::sorry(this, i18n("

You have selected to encrypt your data also with the KMyMoney recover key, but the key with id

%1

has not been found in your keyring at this time. Please make sure to import this key into your keyring. You can find it on the KMyMoney web-site. This time your data will not be encrypted with the KMyMoney recover key.

", QString(recoveryKeyId)), i18n("GPG Key not found")); encryptRecover = false; } } for(const QString& key: keyList.split(',', QString::SkipEmptyParts)) { if (!KGPGFile::keyAvailable(key)) { KMessageBox::sorry(this, i18n("

You have specified to encrypt your data for the user-id

%1.

Unfortunately, a valid key for this user-id was not found in your keyring. Please make sure to import a valid key for this user-id. This time, encryption is disabled.

", key), i18n("GPG Key not found")); encryptFile = false; break; } } if (encryptFile == true) { QString msg = i18n("

You have configured to save your data in encrypted form using GPG. Make sure you understand that you might lose all your data if you encrypt it, but cannot decrypt it later on. If unsure, answer No.

"); if (KMessageBox::questionYesNo(this, msg, i18n("Store GPG encrypted"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "StoreEncrypted") == KMessageBox::No) { encryptFile = false; } } } } // Create a temporary file if needed QString writeFile = localFile; QTemporaryFile tmpFile; if (QFile::exists(localFile)) { tmpFile.open(); writeFile = tmpFile.fileName(); tmpFile.close(); } /** * @brief Automatically restore settings when scope is left */ struct restorePreviousSettingsHelper { restorePreviousSettingsHelper(mode_t mode) : m_signalsWereBlocked{MyMoneyFile::instance()->signalsBlocked()}, m_oldMask{umask((~mode) & 0777u)} { MyMoneyFile::instance()->blockSignals(true); } ~restorePreviousSettingsHelper() { MyMoneyFile::instance()->blockSignals(m_signalsWereBlocked); umask(m_oldMask); } const bool m_signalsWereBlocked; const mode_t m_oldMask; } restoreHelper{m_fmode}; MyMoneyFileTransaction ft; MyMoneyFile::instance()->deletePair("kmm-encryption-key"); std::unique_ptr device; if (!keyList.isEmpty() && encryptFile && !plaintext) { std::unique_ptr kgpg = std::unique_ptr(new KGPGFile{writeFile}); if (kgpg) { for(const QString& key: keyList.split(',', QString::SkipEmptyParts)) { kgpg->addRecipient(key.toLatin1()); } if (encryptRecover) { kgpg->addRecipient(recoveryKeyId); } MyMoneyFile::instance()->setValue("kmm-encryption-key", keyList); device = std::unique_ptr(kgpg.release()); } } else { QFile *file = new QFile(writeFile); // The second parameter of KCompressionDevice means that KCompressionDevice will delete the QFile object device = std::unique_ptr(new KCompressionDevice{file, true, (plaintext) ? KCompressionDevice::None : COMPRESSION_TYPE}); } ft.commit(); if (!device || !device->open(QIODevice::WriteOnly)) { throw MYMONEYEXCEPTION(i18n("Unable to open file '%1' for writing.", localFile)); } pWriter->setProgressCallback(&KMyMoneyView::progressCallback); pWriter->writeFile(device.get(), dynamic_cast(MyMoneyFile::instance()->storage())); device->close(); // Check for errors if possible, only possible for KGPGFile QFileDevice *fileDevice = qobject_cast(device.get()); if (fileDevice && fileDevice->error() != QFileDevice::NoError) { throw MYMONEYEXCEPTION(i18n("Failure while writing to '%1'", localFile)); } if (writeFile != localFile) { // This simple comparison is possible because the strings are equal if no temporary file was created. // If a temporary file was created, it is made in a way that the name is definitely different. So no // symlinks etc. have to be evaluated. if (!QFile::remove(localFile) || !QFile::rename(writeFile, localFile)) throw MYMONEYEXCEPTION(i18n("Failure while writing to '%1'", localFile)); } pWriter->setProgressCallback(0); } bool KMyMoneyView::saveFile(const QUrl &url, const QString& keyList) { QString filename = url.path(); if (!fileOpen()) { KMessageBox::error(this, i18n("Tried to access a file when it has not been opened")); return false; } emit kmmFilePlugin(preSave); std::unique_ptr storageWriter; // If this file ends in ".ANON.XML" then this should be written using the // anonymous writer. bool plaintext = filename.right(4).toLower() == ".xml"; if (filename.right(9).toLower() == ".anon.xml") { //! @todo C++14: use std::make_unique, also some lines below storageWriter = std::unique_ptr(new MyMoneyStorageANON); } else { storageWriter = std::unique_ptr(new MyMoneyStorageXML); } // actually, url should be the parameter to this function // but for now, this would involve too many changes bool rc = true; try { if (! url.isValid()) { throw MYMONEYEXCEPTION(i18n("Malformed URL '%1'", url.url())); } if (url.isLocalFile()) { filename = url.toLocalFile(); try { const unsigned int nbak = KMyMoneyGlobalSettings::autoBackupCopies(); if (nbak) { KBackup::numberedBackupFile(filename, QString(), QString::fromLatin1("~"), nbak); } saveToLocalFile(filename, storageWriter.get(), plaintext, keyList); } catch (const MyMoneyException &) { throw MYMONEYEXCEPTION(i18n("Unable to write changes to '%1'", filename)); } } else { QTemporaryFile tmpfile; tmpfile.open(); // to obtain the name tmpfile.close(); saveToLocalFile(tmpfile.fileName(), storageWriter.get(), plaintext, keyList); // TODO: port to kf5 //if (!KIO::NetAccess::upload(tmpfile.fileName(), url, 0)) // throw MYMONEYEXCEPTION(i18n("Unable to upload to '%1'", url.toDisplayString())); } m_fileType = KmmXML; } catch (const MyMoneyException &e) { KMessageBox::error(this, e.what()); MyMoneyFile::instance()->setDirty(); rc = false; } emit kmmFilePlugin(postSave); return rc; } bool KMyMoneyView::saveAsDatabase(const QUrl &url) { bool rc = false; if (!fileOpen()) { KMessageBox::error(this, i18n("Tried to access a file when it has not been opened")); return (rc); } MyMoneyStorageSql *writer = new MyMoneyStorageSql(dynamic_cast(MyMoneyFile::instance()->storage()), url); bool canWrite = false; switch (writer->open(url, QIODevice::WriteOnly)) { case 0: canWrite = true; break; case -1: // dbase already has data, see if he wants to clear it out if (KMessageBox::warningContinueCancel(0, i18n("Database contains data which must be removed before using Save As.\n" "Do you wish to continue?"), "Database not empty") == KMessageBox::Continue) { if (writer->open(url, QIODevice::WriteOnly, true) == 0) canWrite = true; } else { delete writer; return false; } break; } if (canWrite) { writer->setProgressCallback(&KMyMoneyView::progressCallback); if (!writer->writeFile()) { KMessageBox::detailedError(0, i18n("An unrecoverable error occurred while writing to the database.\n" "It may well be corrupt."), writer->lastError().toLatin1(), i18n("Database malfunction")); rc = false; } writer->setProgressCallback(0); rc = true; } else { KMessageBox::detailedError(this, i18n("Cannot open or create database %1.\n" "Retry Save As Database and click Help" " for further info.", url.toDisplayString()), writer->lastError()); } delete writer; return (rc); } bool KMyMoneyView::dirty() { if (!fileOpen()) return false; return MyMoneyFile::instance()->dirty(); } bool KMyMoneyView::startReconciliation(const MyMoneyAccount& account, const QDate& reconciliationDate, const MyMoneyMoney& endingBalance) { bool ok = true; // we cannot reconcile standard accounts if (MyMoneyFile::instance()->isStandardAccount(account.id())) ok = false; // check if we can reconcile this account // it makes sense for asset and liability accounts only if (ok == true) { if (account.isAssetLiability()) { showPage(viewFrames[View::Ledgers]); // prepare reconciliation mode emit reconciliationStarts(account, reconciliationDate, endingBalance); } else { ok = false; } } return ok; } void KMyMoneyView::finishReconciliation(const MyMoneyAccount& /* account */) { emit reconciliationStarts(MyMoneyAccount(), QDate(), MyMoneyMoney()); } void KMyMoneyView::newFile() { closeFile(); m_fileType = KmmXML; // assume native type until saved m_fileOpen = true; } void KMyMoneyView::slotSetBaseCurrency(const MyMoneySecurity& baseCurrency) { if (!baseCurrency.id().isEmpty()) { QString baseId; try { baseId = MyMoneyFile::instance()->baseCurrency().id(); } catch (const MyMoneyException &e) { qDebug("%s", qPrintable(e.what())); } if (baseCurrency.id() != baseId) { MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->setBaseCurrency(baseCurrency); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("Cannot set %1 as base currency: %2", baseCurrency.name(), e.what()), i18n("Set base currency")); } } } } void KMyMoneyView::selectBaseCurrency() { MyMoneyFile* file = MyMoneyFile::instance(); // check if we have a base currency. If not, we need to select one QString baseId; try { baseId = MyMoneyFile::instance()->baseCurrency().id(); } catch (const MyMoneyException &e) { qDebug("%s", qPrintable(e.what())); } if (baseId.isEmpty()) { QPointer dlg = new KCurrencyEditDlg(this); connect(dlg, SIGNAL(selectBaseCurrency(MyMoneySecurity)), this, SLOT(slotSetBaseCurrency(MyMoneySecurity))); dlg->exec(); delete dlg; } try { baseId = MyMoneyFile::instance()->baseCurrency().id(); } catch (const MyMoneyException &e) { qDebug("%s", qPrintable(e.what())); } if (!baseId.isEmpty()) { // check that all accounts have a currency QList list; file->accountList(list); QList::Iterator it; // don't forget those standard accounts list << file->asset(); list << file->liability(); list << file->income(); list << file->expense(); list << file->equity(); for (it = list.begin(); it != list.end(); ++it) { QString cid; try { if (!(*it).currencyId().isEmpty() || (*it).currencyId().length() != 0) cid = MyMoneyFile::instance()->currency((*it).currencyId()).id(); } catch (const MyMoneyException &) { } if (cid.isEmpty()) { (*it).setCurrencyId(baseId); MyMoneyFileTransaction ft; try { file->modifyAccount(*it); ft.commit(); } catch (const MyMoneyException &e) { qDebug("Unable to setup base currency in account %s (%s): %s", qPrintable((*it).name()), qPrintable((*it).id()), qPrintable(e.what())); } } } } } void KMyMoneyView::updateCurrencyNames() { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyFileTransaction ft; QList storedCurrencies = MyMoneyFile::instance()->currencyList(); QList availableCurrencies = MyMoneyFile::instance()->availableCurrencyList(); QStringList currencyIDs; foreach (auto currency, availableCurrencies) currencyIDs.append(currency.id()); try { foreach (auto currency, storedCurrencies) { int i = currencyIDs.indexOf(currency.id()); if (i != -1 && availableCurrencies.at(i).name() != currency.name()) { currency.setName(availableCurrencies.at(i).name()); file->modifyCurrency(currency); } } ft.commit(); } catch (const MyMoneyException &e) { qDebug("Error %s updating currency names", qPrintable(e.what())); } } void KMyMoneyView::loadAllCurrencies() { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyFileTransaction ft; if (!file->currencyList().isEmpty()) return; QMap ancientCurrencies = file->ancientCurrencies(); try { foreach (auto currency, file->availableCurrencyList()) { file->addCurrency(currency); MyMoneyPrice price = ancientCurrencies.value(currency, MyMoneyPrice()); if (price != MyMoneyPrice()) file->addPrice(price); } ft.commit(); } catch (const MyMoneyException &e) { qDebug("Error %s loading currency", qPrintable(e.what())); } } void KMyMoneyView::viewAccountList(const QString& /*selectAccount*/) { if (viewFrames[View::Accounts] != currentPage()) showPage(viewFrames[View::Accounts]); m_accountsView->show(); } void KMyMoneyView::slotRefreshViews() { // turn off sync between ledger and investment view disconnect(m_investmentView, SIGNAL(accountSelected(MyMoneyObject)), m_ledgerView, SLOT(slotSelectAccount(MyMoneyObject))); disconnect(m_ledgerView, SIGNAL(accountSelected(MyMoneyObject)), m_investmentView, SLOT(slotSelectAccount(MyMoneyObject))); // TODO turn sync between ledger and investment view if selected by user if (KMyMoneyGlobalSettings::syncLedgerInvestment()) { connect(m_investmentView, SIGNAL(accountSelected(MyMoneyObject)), m_ledgerView, SLOT(slotSelectAccount(MyMoneyObject))); connect(m_ledgerView, SIGNAL(accountSelected(MyMoneyObject)), m_investmentView, SLOT(slotSelectAccount(MyMoneyObject))); } showTitleBar(KMyMoneyGlobalSettings::showTitleBar()); m_accountsView->slotLoadAccounts(); m_institutionsView->slotLoadAccounts(); m_categoriesView->slotLoadAccounts(); m_payeesView->slotLoadPayees(); m_tagsView->slotLoadTags(); m_ledgerView->slotLoadView(); m_budgetView->slotRefreshView(); m_homeView->slotLoadView(); m_investmentView->slotLoadView(); m_reportsView->slotLoadView(); m_forecastView->slotLoadForecast(); m_scheduledView->slotReloadView(); } void KMyMoneyView::slotShowTransactionDetail(bool detailed) { KMyMoneyGlobalSettings::setShowRegisterDetailed(detailed); slotRefreshViews(); } void KMyMoneyView::progressCallback(int current, int total, const QString& msg) { kmymoney->progressCallback(current, total, msg); } void KMyMoneyView::slotCurrentPageChanged(const QModelIndex current, const QModelIndex) { // remember the current page m_lastViewSelected = current.row(); // set the current page's title in the header if (m_header) m_header->setText(m_model->data(current, KPageModel::HeaderRole).toString()); } /* DO NOT ADD code to this function or any of it's called ones. Instead, create a new function, fixFile_n, and modify the initializeStorage() logic above to call it */ void KMyMoneyView::fixFile_3() { // make sure each storage object contains a (unique) id MyMoneyFile::instance()->storageId(); } void KMyMoneyView::fixFile_2() { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyTransactionFilter filter; filter.setReportAllSplits(false); QList transactionList; file->transactionList(transactionList, filter); // scan the transactions and modify transactions with two splits // which reference an account and a category to have the memo text // of the account. QList::Iterator it_t; int count = 0; for (it_t = transactionList.begin(); it_t != transactionList.end(); ++it_t) { if ((*it_t).splitCount() == 2) { QString accountId; QString categoryId; QString accountMemo; QString categoryMemo; const QList& splits = (*it_t).splits(); QList::const_iterator it_s; for (it_s = splits.constBegin(); it_s != splits.constEnd(); ++it_s) { MyMoneyAccount acc = file->account((*it_s).accountId()); if (acc.isIncomeExpense()) { categoryId = (*it_s).id(); categoryMemo = (*it_s).memo(); } else { accountId = (*it_s).id(); accountMemo = (*it_s).memo(); } } if (!accountId.isEmpty() && !categoryId.isEmpty() && accountMemo != categoryMemo) { MyMoneyTransaction t(*it_t); MyMoneySplit s(t.splitById(categoryId)); s.setMemo(accountMemo); t.modifySplit(s); file->modifyTransaction(t); ++count; } } } qDebug("%d transactions fixed in fixFile_2", count); } void KMyMoneyView::fixFile_1() { // we need to fix reports. If the account filter list contains // investment accounts, we need to add the stock accounts to the list // as well if we don't have the expert mode enabled if (!KMyMoneyGlobalSettings::expertMode()) { try { QList reports = MyMoneyFile::instance()->reportList(); QList::iterator it_r; for (it_r = reports.begin(); it_r != reports.end(); ++it_r) { QStringList list; (*it_r).accounts(list); QStringList missing; QStringList::const_iterator it_a, it_b; for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) { MyMoneyAccount acc = MyMoneyFile::instance()->account(*it_a); if (acc.accountType() == MyMoneyAccount::Investment) { for (it_b = acc.accountList().begin(); it_b != acc.accountList().end(); ++it_b) { if (!list.contains(*it_b)) { missing.append(*it_b); } } } } if (!missing.isEmpty()) { (*it_r).addAccount(missing); MyMoneyFile::instance()->modifyReport(*it_r); } } } catch (const MyMoneyException &) { } } } #if 0 if (!m_accountsView->allItemsSelected()) { // retrieve a list of selected accounts QStringList list; m_accountsView->selectedItems(list); // if we're not in expert mode, we need to make sure // that all stock accounts for the selected investment // account are also selected if (!KMyMoneyGlobalSettings::expertMode()) { QStringList missing; QStringList::const_iterator it_a, it_b; for (it_a = list.begin(); it_a != list.end(); ++it_a) { MyMoneyAccount acc = MyMoneyFile::instance()->account(*it_a); if (acc.accountType() == MyMoneyAccount::Investment) { for (it_b = acc.accountList().begin(); it_b != acc.accountList().end(); ++it_b) { if (!list.contains(*it_b)) { missing.append(*it_b); } } } } list += missing; } m_filter.addAccount(list); } #endif void KMyMoneyView::fixFile_0() { /* (Ace) I am on a crusade against file fixups. Whenever we have to fix the * file, it is really a warning. So I'm going to print a debug warning, and * then go track them down when I see them to figure out how they got saved * out needing fixing anyway. */ MyMoneyFile* file = MyMoneyFile::instance(); QList accountList; file->accountList(accountList); QList::Iterator it_a; QList scheduleList = file->scheduleList(); QList::Iterator it_s; MyMoneyAccount equity = file->equity(); MyMoneyAccount asset = file->asset(); bool equityListEmpty = equity.accountList().count() == 0; for (it_a = accountList.begin(); it_a != accountList.end(); ++it_a) { if ((*it_a).accountType() == MyMoneyAccount::Loan || (*it_a).accountType() == MyMoneyAccount::AssetLoan) { fixLoanAccount_0(*it_a); } // until early before 0.8 release, the equity account was not saved to // the file. If we have an equity account with no sub-accounts but // find and equity account that has equity() as it's parent, we reparent // this account. Need to move it to asset() first, because otherwise // MyMoneyFile::reparent would act as NOP. if (equityListEmpty && (*it_a).accountType() == MyMoneyAccount::Equity) { if ((*it_a).parentAccountId() == equity.id()) { MyMoneyAccount acc = *it_a; // tricky, force parent account to be empty so that we really // can re-parent it acc.setParentAccountId(QString()); file->reparentAccount(acc, equity); qDebug() << Q_FUNC_INFO << " fixed account " << acc.id() << " reparented to " << equity.id(); } } } for (it_s = scheduleList.begin(); it_s != scheduleList.end(); ++it_s) { fixSchedule_0(*it_s); } fixTransactions_0(); } void KMyMoneyView::fixSchedule_0(MyMoneySchedule sched) { MyMoneyTransaction t = sched.transaction(); QList splitList = t.splits(); QList::ConstIterator it_s; bool updated = false; try { // Check if the splits contain valid data and set it to // be valid. for (it_s = splitList.constBegin(); it_s != splitList.constEnd(); ++it_s) { // the first split is always the account on which this transaction operates // and if the transaction commodity is not set, we take this if (it_s == splitList.constBegin() && t.commodity().isEmpty()) { qDebug() << Q_FUNC_INFO << " " << t.id() << " has no commodity"; try { MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId()); t.setCommodity(acc.currencyId()); updated = true; } catch (const MyMoneyException &) { } } // make sure the account exists. If not, remove the split try { MyMoneyFile::instance()->account((*it_s).accountId()); } catch (const MyMoneyException &) { qDebug() << Q_FUNC_INFO << " " << sched.id() << " " << (*it_s).id() << " removed, because account '" << (*it_s).accountId() << "' does not exist."; t.removeSplit(*it_s); updated = true; } if ((*it_s).reconcileFlag() != MyMoneySplit::NotReconciled) { qDebug() << Q_FUNC_INFO << " " << sched.id() << " " << (*it_s).id() << " should be 'not reconciled'"; MyMoneySplit split = *it_s; split.setReconcileDate(QDate()); split.setReconcileFlag(MyMoneySplit::NotReconciled); t.modifySplit(split); updated = true; } // the schedule logic used to operate only on the value field. // This is now obsolete. if ((*it_s).shares().isZero() && !(*it_s).value().isZero()) { MyMoneySplit split = *it_s; split.setShares(split.value()); t.modifySplit(split); updated = true; } } // If there have been changes, update the schedule and // the engine data. if (updated) { sched.setTransaction(t); MyMoneyFile::instance()->modifySchedule(sched); } } catch (const MyMoneyException &e) { qWarning("Unable to update broken schedule: %s", qPrintable(e.what())); } } void KMyMoneyView::fixLoanAccount_0(MyMoneyAccount acc) { if (acc.value("final-payment").isEmpty() || acc.value("term").isEmpty() || acc.value("periodic-payment").isEmpty() || acc.value("loan-amount").isEmpty() || acc.value("interest-calculation").isEmpty() || acc.value("schedule").isEmpty() || acc.value("fixed-interest").isEmpty()) { KMessageBox::information(this, i18n("

The account \"%1\" was previously created as loan account but some information is missing.

The new loan wizard will be started to collect all relevant information.

Please use KMyMoney version 0.8.7 or later and earlier than version 0.9 to correct the problem.

" , acc.name()), i18n("Account problem")); throw MYMONEYEXCEPTION("Fix LoanAccount0 not supported anymore"); } } void KMyMoneyView::createSchedule(MyMoneySchedule newSchedule, MyMoneyAccount& newAccount) { // Add the schedule only if one exists // // Remember to modify the first split to reference the newly created account if (!newSchedule.name().isEmpty()) { MyMoneyFileTransaction ft; try { // We assume at least 2 splits in the transaction MyMoneyTransaction t = newSchedule.transaction(); if (t.splitCount() < 2) { throw MYMONEYEXCEPTION("Transaction for schedule has less than 2 splits!"); } // now search the split that does not have an account reference // and set it up to be the one of the account we just added // to the account pool. Note: the schedule code used to leave // this always the first split, but the loan code leaves it as // the second one. So I thought, searching is a good alternative .... QList::ConstIterator it_s; for (it_s = t.splits().constBegin(); it_s != t.splits().constEnd(); ++it_s) { if ((*it_s).accountId().isEmpty()) { MyMoneySplit s = (*it_s); s.setAccountId(newAccount.id()); t.modifySplit(s); break; } } newSchedule.setTransaction(t); MyMoneyFile::instance()->addSchedule(newSchedule); // in case of a loan account, we keep a reference to this // schedule in the account if (newAccount.isLoan()) { newAccount.setValue("schedule", newSchedule.id()); MyMoneyFile::instance()->modifyAccount(newAccount); } ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to add schedule: %1", e.what())); } } } void KMyMoneyView::fixTransactions_0() { MyMoneyFile* file = MyMoneyFile::instance(); QList scheduleList = file->scheduleList(); MyMoneyTransactionFilter filter; filter.setReportAllSplits(false); QList transactionList; file->transactionList(transactionList, filter); QList::Iterator it_x; QStringList interestAccounts; KMSTATUS(i18n("Fix transactions")); kmymoney->slotStatusProgressBar(0, scheduleList.count() + transactionList.count()); int cnt = 0; // scan the schedules to find interest accounts for (it_x = scheduleList.begin(); it_x != scheduleList.end(); ++it_x) { MyMoneyTransaction t = (*it_x).transaction(); QList::ConstIterator it_s; QStringList accounts; bool hasDuplicateAccounts = false; for (it_s = t.splits().constBegin(); it_s != t.splits().constEnd(); ++it_s) { if (accounts.contains((*it_s).accountId())) { hasDuplicateAccounts = true; qDebug() << Q_FUNC_INFO << " " << t.id() << " has multiple splits with account " << (*it_s).accountId(); } else { accounts << (*it_s).accountId(); } if ((*it_s).action() == MyMoneySplit::ActionInterest) { if (interestAccounts.contains((*it_s).accountId()) == 0) { interestAccounts << (*it_s).accountId(); } } } if (hasDuplicateAccounts) { fixDuplicateAccounts_0(t); } ++cnt; if (!(cnt % 10)) kmymoney->slotStatusProgressBar(cnt); } // scan the transactions and modify loan transactions QList::Iterator it_t; for (it_t = transactionList.begin(); it_t != transactionList.end(); ++it_t) { const char *defaultAction = 0; QList splits = (*it_t).splits(); QList::Iterator it_s; QStringList accounts; // check if base commodity is set. if not, set baseCurrency if ((*it_t).commodity().isEmpty()) { qDebug() << Q_FUNC_INFO << " " << (*it_t).id() << " has no base currency"; (*it_t).setCommodity(file->baseCurrency().id()); file->modifyTransaction(*it_t); } bool isLoan = false; // Determine default action if ((*it_t).splitCount() == 2) { // check for transfer int accountCount = 0; MyMoneyMoney val; for (it_s = splits.begin(); it_s != splits.end(); ++it_s) { MyMoneyAccount acc = file->account((*it_s).accountId()); if (acc.accountGroup() == MyMoneyAccount::Asset || acc.accountGroup() == MyMoneyAccount::Liability) { val = (*it_s).value(); accountCount++; if (acc.accountType() == MyMoneyAccount::Loan || acc.accountType() == MyMoneyAccount::AssetLoan) isLoan = true; } else break; } if (accountCount == 2) { if (isLoan) defaultAction = MyMoneySplit::ActionAmortization; else defaultAction = MyMoneySplit::ActionTransfer; } else { if (val.isNegative()) defaultAction = MyMoneySplit::ActionWithdrawal; else defaultAction = MyMoneySplit::ActionDeposit; } } isLoan = false; for (it_s = splits.begin(); defaultAction == 0 && it_s != splits.end(); ++it_s) { MyMoneyAccount acc = file->account((*it_s).accountId()); MyMoneyMoney val = (*it_s).value(); if (acc.accountGroup() == MyMoneyAccount::Asset || acc.accountGroup() == MyMoneyAccount::Liability) { if (!val.isPositive()) defaultAction = MyMoneySplit::ActionWithdrawal; else defaultAction = MyMoneySplit::ActionDeposit; } } #if 0 // Check for correct actions in transactions referencing credit cards bool needModify = false; // The action fields are actually not used anymore in the ledger view logic // so we might as well skip this whole thing here! for (it_s = splits.begin(); needModify == false && it_s != splits.end(); ++it_s) { MyMoneyAccount acc = file->account((*it_s).accountId()); MyMoneyMoney val = (*it_s).value(); if (acc.accountType() == MyMoneyAccount::CreditCard) { if (val < 0 && (*it_s).action() != MyMoneySplit::ActionWithdrawal && (*it_s).action() != MyMoneySplit::ActionTransfer) needModify = true; if (val >= 0 && (*it_s).action() != MyMoneySplit::ActionDeposit && (*it_s).action() != MyMoneySplit::ActionTransfer) needModify = true; } } // (Ace) Extended the #endif down to cover this conditional, because as-written // it will ALWAYS be skipped. if (needModify == true) { for (it_s = splits.begin(); it_s != splits.end(); ++it_s) { (*it_s).setAction(defaultAction); (*it_t).modifySplit(*it_s); file->modifyTransaction(*it_t); } splits = (*it_t).splits(); // update local copy qDebug("Fixed credit card assignment in %s", (*it_t).id().data()); } #endif // Check for correct assignment of ActionInterest in all splits // and check if there are any duplicates in this transactions for (it_s = splits.begin(); it_s != splits.end(); ++it_s) { MyMoneyAccount splitAccount = file->account((*it_s).accountId()); if (!accounts.contains((*it_s).accountId())) { accounts << (*it_s).accountId(); } // if this split references an interest account, the action // must be of type ActionInterest if (interestAccounts.contains((*it_s).accountId())) { if ((*it_s).action() != MyMoneySplit::ActionInterest) { qDebug() << Q_FUNC_INFO << " " << (*it_t).id() << " contains an interest account (" << (*it_s).accountId() << ") but does not have ActionInterest"; (*it_s).setAction(MyMoneySplit::ActionInterest); (*it_t).modifySplit(*it_s); file->modifyTransaction(*it_t); qDebug("Fixed interest action in %s", qPrintable((*it_t).id())); } // if it does not reference an interest account, it must not be // of type ActionInterest } else { if ((*it_s).action() == MyMoneySplit::ActionInterest) { qDebug() << Q_FUNC_INFO << " " << (*it_t).id() << " does not contain an interest account so it should not have ActionInterest"; (*it_s).setAction(defaultAction); (*it_t).modifySplit(*it_s); file->modifyTransaction(*it_t); qDebug("Fixed interest action in %s", qPrintable((*it_t).id())); } } // check that for splits referencing an account that has // the same currency as the transactions commodity the value // and shares field are the same. if ((*it_t).commodity() == splitAccount.currencyId() && (*it_s).value() != (*it_s).shares()) { qDebug() << Q_FUNC_INFO << " " << (*it_t).id() << " " << (*it_s).id() << " uses the transaction currency, but shares != value"; (*it_s).setShares((*it_s).value()); (*it_t).modifySplit(*it_s); file->modifyTransaction(*it_t); } // fix the shares and values to have the correct fraction if (!splitAccount.isInvest()) { try { int fract = splitAccount.fraction(); if ((*it_s).shares() != (*it_s).shares().convert(fract)) { qDebug("adjusting fraction in %s,%s", qPrintable((*it_t).id()), qPrintable((*it_s).id())); (*it_s).setShares((*it_s).shares().convert(fract)); (*it_s).setValue((*it_s).value().convert(fract)); (*it_t).modifySplit(*it_s); file->modifyTransaction(*it_t); } } catch (const MyMoneyException &) { qDebug("Missing security '%s', split not altered", qPrintable(splitAccount.currencyId())); } } } ++cnt; if (!(cnt % 10)) kmymoney->slotStatusProgressBar(cnt); } kmymoney->slotStatusProgressBar(-1, -1); } void KMyMoneyView::fixDuplicateAccounts_0(MyMoneyTransaction& t) { qDebug("Duplicate account in transaction %s", qPrintable(t.id())); } void KMyMoneyView::slotPrintView() { if (viewFrames[View::Reports] == currentPage()) m_reportsView->slotPrintView(); else if (viewFrames[View::Home] == currentPage()) m_homeView->slotPrintView(); } void KMyMoneyView::slotShowHomePage() { setCurrentPage(viewFrames[View::Home]); } KMyMoneyViewBase* KMyMoneyView::addBasePage(const QString& title, const QString& icon) { KMyMoneyViewBase* viewBase = new KMyMoneyViewBase(this, title, title); connect(viewBase, SIGNAL(aboutToShow()), this, SIGNAL(aboutToChangeView())); KPageWidgetItem* frm = m_model->addPage(viewBase, title); frm->setIcon(QIcon::fromTheme(icon)); return viewBase; } /* ------------------------------------------------------------------------ */ /* KMyMoneyViewBase */ /* ------------------------------------------------------------------------ */ // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes class KMyMoneyViewBase::Private { public: QVBoxLayout* m_viewLayout; }; KMyMoneyViewBase::KMyMoneyViewBase(QWidget* parent, const QString& name, const QString& title) : QWidget(parent), d(new Private) { setAccessibleName(name); setAccessibleDescription(title); d->m_viewLayout = new QVBoxLayout(this); d->m_viewLayout->setSpacing(6); d->m_viewLayout->setMargin(0); } KMyMoneyViewBase::~KMyMoneyViewBase() { delete d; } void KMyMoneyViewBase::addWidget(QWidget* w) { d->m_viewLayout->addWidget(w); } QVBoxLayout* KMyMoneyViewBase::layout() const { return d->m_viewLayout; } - -bool MyQWebEnginePage::acceptNavigationRequest(const QUrl &url, NavigationType type, bool) -{ - if (type == NavigationTypeLinkClicked) - emit urlChanged(url); - return false; -} diff --git a/kmymoney/views/kmymoneyview.h b/kmymoney/views/kmymoneyview.h index a8e60cd8c..c901d3e6b 100644 --- a/kmymoney/views/kmymoneyview.h +++ b/kmymoney/views/kmymoneyview.h @@ -1,683 +1,671 @@ /*************************************************************************** kmymoneyview.h ------------------- copyright : (C) 2000-2001 by Michael Edwardes 2004 by Thomas Baumgart 2017 by Ł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. * * * ***************************************************************************/ #ifndef KMYMONEYVIEW_H #define KMYMONEYVIEW_H +#include "config-kmymoney.h" + // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include -#include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes - -#include "config-kmymoney.h" #include "mymoneyaccount.h" #include "mymoneyinstitution.h" #include "mymoneytransaction.h" #include "mymoneyschedule.h" #include "mymoneysecurity.h" #include "selectedtransaction.h" #include #ifdef KF5Activities_FOUND namespace KActivities { class ResourceInstance; } #endif class KMyMoneyApp; class KHomeView; class KAccountsView; class KCategoriesView; class KInstitutionsView; class KPayeesView; class KTagsView; class KBudgetView; class KScheduledView; class KGlobalLedgerView; class IMyMoneyStorageFormat; class MyMoneyTransaction; class KInvestmentView; class KReportsView; class KMyMoneyViewBase; class MyMoneyReport; class TransactionEditor; class KForecastView; class KOnlineJobOutbox; class KMyMoneyTitleLabel; class QLabel; /** * This class represents the view of the MyMoneyFile which contains * Banks/Accounts/Transactions, Recurring transactions (or Bills & Deposits) * and scripts (yet to be implemented). Each different aspect of the file * is represented by a tab within the view. * * @author Michael Edwardes 2001 Copyright 2000-2001 * * @short Handles the view of the MyMoneyFile. */ class KMyMoneyView : public KPageWidget { Q_OBJECT public: enum class View { Home = 0, Institutions, Accounts, Schedules, Categories, Tags, Payees, Ledgers, Investments, Reports, Budget, Forecast, OnlineJobOutbox, None }; // file actions for plugin enum fileActions { preOpen, postOpen, preSave, postSave, preClose, postClose }; KOnlineJobOutbox* getOnlineJobOutbox() const { return m_onlineJobOutboxView; } private: enum menuID { AccountNew = 1, AccountOpen, AccountReconcile, AccountEdit, AccountDelete, AccountOnlineMap, AccountOnlineUpdate, AccountOfxConnect, CategoryNew }; enum storageTypeE { Memory = 0, Database } _storageType; KPageWidgetModel* m_model; KHomeView *m_homeView; KAccountsView *m_accountsView; KInstitutionsView *m_institutionsView; KCategoriesView *m_categoriesView; KPayeesView *m_payeesView; KTagsView *m_tagsView; KBudgetView *m_budgetView; KScheduledView *m_scheduledView; KGlobalLedgerView *m_ledgerView; KInvestmentView *m_investmentView; KReportsView* m_reportsView; KForecastView* m_forecastView; KOnlineJobOutbox* m_onlineJobOutboxView; QHash viewFrames; KMyMoneyTitleLabel* m_header; bool m_inConstructor; bool m_fileOpen; mode_t m_fmode; int m_lastViewSelected; // Keep a note of the file type typedef enum _fileTypeE { KmmBinary = 0, // native, binary KmmXML, // native, XML KmmDb, // SQL database /* insert new native file types above this line */ MaxNativeFileType, /* and non-native types below */ GncXML // Gnucash XML } fileTypeE; fileTypeE m_fileType; #ifdef KF5Activities_FOUND private: KActivities::ResourceInstance * m_activityResourceInstance; #endif private: void ungetString(QIODevice *qfile, char * buf, int len); /** * if no base currency is defined, start the dialog and force it to be set */ void selectBaseCurrency(); /** * This method attaches an empty storage object to the MyMoneyFile * object. It calls removeStorage() to remove a possibly attached * storage object. */ void newStorage(storageTypeE = Memory); /** * This method removes an attached storage from the MyMoneyFile * object. */ void removeStorage(); void viewAccountList(const QString& selectAccount); // Show the accounts view static void progressCallback(int current, int total, const QString&); /** */ void fixFile_0(); void fixFile_1(); void fixFile_2(); void fixFile_3(); /** */ void fixLoanAccount_0(MyMoneyAccount acc); /** */ void fixTransactions_0(); void fixSchedule_0(MyMoneySchedule sched); void fixDuplicateAccounts_0(MyMoneyTransaction& t); void createSchedule(MyMoneySchedule s, MyMoneyAccount& a); void checkAccountName(const MyMoneyAccount& acc, const QString& name) const; public: /** * The constructor for KMyMoneyView. Just creates all the tabs for the * different aspects of the MyMoneyFile. */ explicit KMyMoneyView(KMyMoneyApp *kmymoney); /** * Destructor */ ~KMyMoneyView(); /** * Makes sure that a MyMoneyFile is open and has been created successfully. * * @return Whether the file is open and initialised */ bool fileOpen(); /** * Closes the open MyMoneyFile and frees all the allocated memory, I hope ! */ void closeFile(); /** * Calls MyMoneyFile::readAllData which reads a MyMoneyFile into appropriate * data structures in memory. The return result is examined to make sure no * errors occurred whilst parsing. * * @param url The URL to read from. * If no protocol is specified, file:// is assumed. * * @return Whether the read was successful. */ bool readFile(const QUrl &url); /** * Saves the data into permanent storage using the XML format. * * @param url The URL to save into. * If no protocol is specified, file:// is assumed. * @param keyList QString containing a comma separated list of keys * to be used for encryption. If @p keyList is empty, * the file will be saved unencrypted (the default) * * @retval false save operation failed * @retval true save operation was successful */ bool saveFile(const QUrl &url, const QString& keyList = QString()); /** * Saves the data into permanent storage on a new or empty SQL database. * * @param url The pseudo of tyhe database * * @retval false save operation failed * @retval true save operation was successful */ //const bool saveDatabase(const QUrl &url); This no longer relevant /** * Saves the data into permanent storage on a new or empty SQL database. * * @param url The pseudo URL of the database * * @retval false save operation failed * @retval true save operation was successful */ bool saveAsDatabase(const QUrl &url); /** * Call this to find out if the currently open file is native KMM * * @retval true file is native * @retval false file is foreign */ bool isNativeFile() { return (m_fileOpen && (m_fileType < MaxNativeFileType)); } /** * Call this to find out if the currently open file is a sql database * * @retval true file is database * @retval false file is serial */ bool isDatabase() { return (m_fileOpen && ((m_fileType == KmmDb))); } /** * Call this to see if the MyMoneyFile contains any unsaved data. * * @retval true if any data has been modified but not saved * @retval false otherwise */ bool dirty(); /** * Close the currently opened file and create an empty new file. * * @see MyMoneyFile */ void newFile(); /** * This method enables the state of all views (except home view) according * to an open file. */ void enableViewsIfFileOpen(); KMyMoneyViewBase* addBasePage(const QString& title, const QString& icon = QString()); void addWidget(QWidget* w); void showPage(KPageWidgetItem* pageItem); /** * check if the current view allows to create a transaction * * @param list list of selected transactions * @param tooltip reference to string receiving the tooltip text * which explains why the modify function is not available (in case * of returning @c false) * * @retval true Yes, view allows to create a transaction (tooltip is not changed) * @retval false No, view cannot to create a transaction (tooltip is updated with message) */ bool canCreateTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const; /** * check if the current view allows to modify (edit/delete) the selected transactions * * @param list list of selected transactions * @param tooltip reference to string receiving the tooltip text * which explains why the modify function is not available (in case * of returning @c false) * * @retval true Yes, view allows to edit/delete transactions (tooltip is not changed) * @retval false No, view cannot edit/delete transactions (tooltip is updated with message) */ bool canModifyTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const; bool canDuplicateTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const; /** * check if the current view allows to edit the selected transactions * * @param list list of selected transactions * @param tooltip reference to string receiving the tooltip text * which explains why the edit function is not available (in case * of returning @c false) * * @retval true Yes, view allows to enter/edit transactions * @retval false No, view cannot enter/edit transactions */ bool canEditTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const; /** * check if the current view allows to print something * * @retval true Yes, view allows to print * @retval false No, view cannot print */ bool canPrint(); TransactionEditor* startEdit(const KMyMoneyRegister::SelectedTransactions&); bool createNewTransaction(); /** * Used to start reconciliation of account @a account. It switches the * ledger view into reconciliation mode and updates the view. * * @param account account which should be reconciled * @param reconciliationDate the statement date * @param endingBalance the ending balance entered for this account * * @retval true Reconciliation started * @retval false Account cannot be reconciled */ bool startReconciliation(const MyMoneyAccount& account, const QDate& reconciliationDate, const MyMoneyMoney& endingBalance); /** * Used to finish reconciliation of account @a account. It switches the * ledger view to normal mode and updates the view. * * @param account account which should be reconciled */ void finishReconciliation(const MyMoneyAccount& account); /** * This method updates names of currencies from file to localized names */ void updateCurrencyNames(); /** * This method loads all known currencies and saves them to the storage */ void loadAllCurrencies(); void showTitleBar(bool show); /** * This method changes the view type according to the settings. */ void updateViewType(); void slotAccountTreeViewChanged(const AccountsModel::Columns column, const bool show); void slotNetBalProChanged(const MyMoneyMoney &val, QLabel *label, const View view); protected: /** * Overwritten because KMyMoney has it's custom header. */ virtual bool showPageHeader() const; public slots: /** * This slot writes information about the page passed as argument @a current * in the kmymoney.rc file so that it can be selected automatically when * the application is started again. * * @param current QModelIndex of the current page item * @param previous QModelIndex of the previous page item */ void slotCurrentPageChanged(const QModelIndex current, const QModelIndex previous); /** * Brings up a dialog to change the list(s) settings and saves them into the * class KMyMoneySettings (a singleton). * * @see KListSettingsDlg * Refreshes all views. Used e.g. after settings have been changed or * data has been loaded from external sources (QIF import). **/ void slotRefreshViews(); /** * Called, whenever the ledger view should pop up and a specific * transaction in an account should be shown. If @p transaction * is empty, the last transaction should be selected * * @param acc The ID of the account to be shown * @param transaction The ID of the transaction to be selected */ void slotLedgerSelected(const QString& acc, const QString& transaction = QString()); /** * Called, whenever the payees view should pop up and a specific * transaction in an account should be shown. * * @param payeeId The ID of the payee to be shown * @param accountId The ID of the account to be shown * @param transactionId The ID of the transaction to be selected */ void slotPayeeSelected(const QString& payeeId, const QString& accountId, const QString& transactionId); /** * Called, whenever the tags view should pop up and a specific * transaction in an account should be shown. * * @param tagId The ID of the tag to be shown * @param accountId The ID of the account to be shown * @param transactionId The ID of the transaction to be selected */ void slotTagSelected(const QString& tagId, const QString& accountId, const QString& transactionId); /** * Called, whenever the schedule view should pop up and a specific * schedule should be shown. * * @param schedule The ID of the schedule to be shown */ void slotScheduleSelected(const QString& schedule); /** * Called, whenever the report view should pop up and a specific * report should be shown. * * @param reportid The ID of the report to be shown */ void slotShowReport(const QString& reportid); /** * Same as the above, but the caller passes in an actual report * definition to be shown. * * @param report The report to be shown */ void slotShowReport(const MyMoneyReport& report); /** * This slot prints the current view. */ void slotPrintView(); /** * This slot switches the view to present the home page */ void slotShowHomePage(); /** * Called when the user changes the detail * setting of the transaction register * * @param detailed if true, the register is shown with all details */ void slotShowTransactionDetail(bool detailed); protected slots: /** * eventually replace this with KMyMoneyApp::slotCurrencySetBase(). * it contains the same code * * @deprecated */ void slotSetBaseCurrency(const MyMoneySecurity& baseCurrency); private: /** * This method is called from readFile to open a database file which * is to be processed in 'proper' database mode, i.e. in-place updates * * @param dbaseURL pseudo-QUrl representation of database * * @retval true Database opened successfully * @retval false Could not open or read database */ bool openDatabase(const QUrl &dbaseURL); /** * This method is used after a file or database has been * read into storage, and performs various initialization tasks * * @retval true all went okay * @retval false an exception occurred during this process */ bool initializeStorage(); /** * This method is used by saveFile() to store the data * either directly in the destination file if it is on * the local file system or in a temporary file when * the final destination is reached over a network * protocol (e.g. FTP) * * @param localFile the name of the local file * @param writer pointer to the formatter * @param plaintext whether to override any compression & encryption settings * @param keyList QString containing a comma separated list of keys to be used for encryption * If @p keyList is empty, the file will be saved unencrypted * * @note This method will close the file when it is written. */ void saveToLocalFile(const QString& localFile, IMyMoneyStorageFormat* writer, bool plaintext = false, const QString& keyList = QString()); /** * Internal method used by slotAccountNew() and slotAccountCategory(). */ void accountNew(const bool createCategory); signals: /** * This signal is emitted whenever a view is selected. * The parameter @p view is identified as one of KMyMoneyView::viewID. */ void viewActivated(int view); /** * This signal is emitted whenever a new view is about to be selected. */ void aboutToChangeView(); void accountSelectedForContextMenu(const MyMoneyAccount& acc); void viewStateChanged(bool enabled); /** * This signal is emitted to inform the kmmFile plugin when various file actions * occur. The Action parameter distinguishes between them. */ void kmmFilePlugin(unsigned int action); /** * Signal is emitted when reconciliation starts or ends. In case of end, * @a account is MyMoneyAccount() * * @param account account for which reconciliation starts or MyMoneyAccount() * if reconciliation ends. * @param reconciliationDate the statement date * @param endingBalance collected ending balance when reconciliation starts * 0 otherwise */ void reconciliationStarts(const MyMoneyAccount& account, const QDate& reconciliationDate, const MyMoneyMoney& endingBalance); /** * This signal is emitted after a data source has been closed */ void fileClosed(); /** * This signal is emitted after a data source has been opened */ void fileOpened(); }; /** * This class is an abstract base class that all specific views * should be based on. */ class KMyMoneyViewBase : public QWidget { Q_OBJECT public: KMyMoneyViewBase(QWidget* parent, const QString& name, const QString& title); virtual ~KMyMoneyViewBase(); void setTitle(const QString& title); QVBoxLayout* layout() const; void addWidget(QWidget* w); /** * This method is used to edit the currently selected transactions * The default implementation returns @p false which signals to the caller, that * the view was not capable to edit the transactions. * * @retval false view was not capable to edit transactions * @retval true view was capable to edit the transactions and did so */ bool editTransactions(const QList& transactions) const { Q_UNUSED(transactions) return false; } signals: /** * This signal is emitted whenever the view is about to be shown. */ void aboutToShow(); private: /// \internal d-pointer class. class Private; /// \internal d-pointer instance. Private* const d; }; -class MyQWebEnginePage : public QWebEnginePage -{ - Q_OBJECT - -public: - MyQWebEnginePage(QObject* parent = nullptr) : QWebEnginePage(parent){} - -protected: - bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool); -}; - inline uint qHash(const KMyMoneyView::View key, uint seed) { return ::qHash(static_cast(key), seed); } #endif diff --git a/kmymoney/views/kmymoneywebpage.cpp b/kmymoney/views/kmymoneywebpage.cpp new file mode 100644 index 000000000..37e90bbd5 --- /dev/null +++ b/kmymoney/views/kmymoneywebpage.cpp @@ -0,0 +1,38 @@ +/*************************************************************************** + kmymoneywebpage.cpp + ------------------- + copyright : (C) 2017 by Ł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. * + * * + ***************************************************************************/ + +#include + +#include "kmymoneywebpage.h" + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +bool MyQWebEnginePage::acceptNavigationRequest(const QUrl &url, NavigationType type, bool) +{ + if (type == NavigationTypeLinkClicked) + #ifdef ENABLE_WEBENGINE + emit urlChanged(url); + #else + emit linkClicked(url); + #endif + return false; +} diff --git a/kmymoney/views/kmymoneywebpage.h b/kmymoney/views/kmymoneywebpage.h new file mode 100644 index 000000000..61acccffb --- /dev/null +++ b/kmymoney/views/kmymoneywebpage.h @@ -0,0 +1,56 @@ +/*************************************************************************** + kmymoneywebpage.h + ------------------- + copyright : (C) 2017 by Ł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. * + * * + ***************************************************************************/ + +#ifndef KMYMONEYWEBPAGE_H +#define KMYMONEYWEBPAGE_H + +#include "config-kmymoney.h" + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +#ifdef ENABLE_WEBENGINE +#include +#else +#include +#endif + +// ---------------------------------------------------------------------------- +// Project Includes + + +#ifdef ENABLE_WEBENGINE +class MyQWebEnginePage : public QWebEnginePage +#else +class MyQWebEnginePage : public KWebPage +#endif +{ + Q_OBJECT + +public: +#ifdef ENABLE_WEBENGINE + MyQWebEnginePage(QObject* parent = nullptr) : QWebEnginePage(parent){} +#else + MyQWebEnginePage(QObject* parent = nullptr) : KWebPage(parent){} +#endif + +protected: + bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool); +}; +#endif diff --git a/kmymoney/views/kreportsview.cpp b/kmymoney/views/kreportsview.cpp index 6181f3a83..4d484fdbe 100644 --- a/kmymoney/views/kreportsview.cpp +++ b/kmymoney/views/kreportsview.cpp @@ -1,1792 +1,1798 @@ /*************************************************************************** kreportsview.cpp - description ------------------- begin : Sat Mar 27 2004 copyright : (C) 2000-2004 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio Ace Jones (C) 2017 Ł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. * * * ***************************************************************************/ #include "kreportsview.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include +#ifdef ENABLE_WEBENGINE +#include +#else +#include +#endif // ---------------------------------------------------------------------------- // KDE Includes #include #include #include -#ifdef KF5KHtml_FOUND -#include -#include -#endif // ---------------------------------------------------------------------------- // Project Includes #include #include #include #include "querytable.h" #include "objectinfotable.h" #include "kreportconfigurationfilterdlg.h" #include +#include using namespace reports; using namespace Icons; #define VIEW_LEDGER "ledger" #define VIEW_SCHEDULE "schedule" #define VIEW_WELCOME "welcome" #define VIEW_HOME "home" #define VIEW_REPORTS "reports" /** * KReportsView::KReportTab Implementation */ KReportsView::KReportTab::KReportTab(QTabWidget* parent, const MyMoneyReport& report, const KReportsView* eventHandler): QWidget(parent), + #ifdef ENABLE_WEBENGINE m_tableView(new QWebEngineView(this)), + #else + m_tableView(new KWebView(this)), + #endif m_chartView(new KReportChartView(this)), m_control(new ReportControl(this)), m_layout(new QVBoxLayout(this)), m_report(report), m_deleteMe(false), m_chartEnabled(false), m_showingChart(report.isChartByDefault()), m_needReload(true), m_table(0) { m_layout->setSpacing(6); m_tableView->setPage(new MyQWebEnginePage(m_tableView)); m_tableView->setZoomFactor(KMyMoneyGlobalSettings::zoomFactor()); //set button icons m_control->ui->buttonChart->setIcon(QIcon::fromTheme(g_Icons[Icon::OfficeChartLine])); m_control->ui->buttonClose->setIcon(QIcon::fromTheme(g_Icons[Icon::DocumentClose])); m_control->ui->buttonConfigure->setIcon(QIcon::fromTheme(g_Icons[Icon::Configure])); m_control->ui->buttonCopy->setIcon(QIcon::fromTheme(g_Icons[Icon::EditCopy])); m_control->ui->buttonDelete->setIcon(QIcon::fromTheme(g_Icons[Icon::EditDelete])); m_control->ui->buttonExport->setIcon(QIcon::fromTheme(g_Icons[Icon::DocumentExport])); m_control->ui->buttonNew->setIcon(QIcon::fromTheme(g_Icons[Icon::DocumentNew])); m_chartView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); m_chartView->hide(); m_tableView->hide(); m_layout->addWidget(m_control); m_layout->addWidget(m_tableView); m_layout->addWidget(m_chartView); connect(m_control->ui->buttonChart, SIGNAL(clicked()), eventHandler, SLOT(slotToggleChart())); connect(m_control->ui->buttonConfigure, SIGNAL(clicked()), eventHandler, SLOT(slotConfigure())); connect(m_control->ui->buttonNew, SIGNAL(clicked()), eventHandler, SLOT(slotDuplicate())); connect(m_control->ui->buttonCopy, SIGNAL(clicked()), eventHandler, SLOT(slotCopyView())); connect(m_control->ui->buttonExport, SIGNAL(clicked()), eventHandler, SLOT(slotSaveView())); connect(m_control->ui->buttonDelete, SIGNAL(clicked()), eventHandler, SLOT(slotDelete())); connect(m_control->ui->buttonClose, SIGNAL(clicked()), eventHandler, SLOT(slotCloseCurrent())); + #ifdef ENABLE_WEBENGINE connect(m_tableView->page(), &QWebEnginePage::urlChanged, eventHandler, &KReportsView::slotOpenUrl); + #else + m_tableView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); + connect(m_tableView->page(), &KWebPage::linkClicked, + eventHandler, &KReportsView::slotOpenUrl); + #endif // if this is a default report, then you can't delete it! if (report.id().isEmpty()) m_control->ui->buttonDelete->setEnabled(false); int tabNr = parent->addTab(this, QIcon::fromTheme(g_Icons[Icon::Spreadsheet]), report.name()); parent->setTabEnabled(tabNr, true); parent->setCurrentIndex(tabNr); // get users character set encoding m_encoding = QTextCodec::codecForLocale()->name(); } KReportsView::KReportTab::~KReportTab() { delete m_table; } void KReportsView::KReportTab::print() { if (m_tableView) { -#ifdef KF5KHtml_FOUND - KHTMLPart *khtml = new KHTMLPart(this); - khtml->begin(); - khtml->write(m_table->renderReport(QLatin1String("html"), m_encoding, m_report.name())); - khtml->end(); - khtml->view()->print(); - delete khtml; -#else m_currentPrinter = new QPrinter(); QPrintDialog *dialog = new QPrintDialog(m_currentPrinter, this); dialog->setWindowTitle(QString()); if (dialog->exec() != QDialog::Accepted) { delete m_currentPrinter; m_currentPrinter = nullptr; return; } - m_tableView->page()->print(m_currentPrinter, [=] (bool) {delete m_currentPrinter; m_currentPrinter = nullptr;}); -#endif + #ifdef ENABLE_WEBENGINE + m_tableView->page()->print(m_currentPrinter, [=] (bool) {delete m_currentPrinter; m_currentPrinter = nullptr;}); + #else + m_tableView->print(m_currentPrinter); + #endif } } void KReportsView::KReportTab::copyToClipboard() { QMimeData* pMimeData = new QMimeData(); pMimeData->setHtml(m_table->renderReport(QLatin1String("html"), m_encoding, m_report.name(), true)); QApplication::clipboard()->setMimeData(pMimeData); } void KReportsView::KReportTab::saveAs(const QString& filename, bool includeCSS) { QFile file(filename); if (file.open(QIODevice::WriteOnly)) { if (QFileInfo(filename).suffix().toLower() == QLatin1String("csv")) { QTextStream(&file) << m_table->renderReport(QLatin1String("csv"), m_encoding, QString()); } else { QString table = m_table->renderReport(QLatin1String("html"), m_encoding, m_report.name(), includeCSS); QTextStream stream(&file); stream << table; } file.close(); } } void KReportsView::KReportTab::loadTab() { m_needReload = true; if (isVisible()) { m_needReload = false; updateReport(); } } void KReportsView::KReportTab::showEvent(QShowEvent * event) { if (m_needReload) { m_needReload = false; updateReport(); } QWidget::showEvent(event); } void KReportsView::KReportTab::updateReport() { m_isChartViewValid = false; m_isTableViewValid = false; // reload the report from the engine. It might have // been changed by the user try { // Don't try to reload default reports from the engine if (!m_report.id().isEmpty()) m_report = MyMoneyFile::instance()->report(m_report.id()); } catch (const MyMoneyException &) { } delete m_table; m_table = 0; if (m_report.reportType() == MyMoneyReport::ePivotTable) { m_table = new PivotTable(m_report); m_chartEnabled = true; } else if (m_report.reportType() == MyMoneyReport::eQueryTable) { m_table = new QueryTable(m_report); m_chartEnabled = false; } else if (m_report.reportType() == MyMoneyReport::eInfoTable) { m_table = new ObjectInfoTable(m_report); m_chartEnabled = false; } m_control->ui->buttonChart->setEnabled(m_chartEnabled); m_showingChart = !m_showingChart; toggleChart(); } void KReportsView::KReportTab::toggleChart() { // for now it will just SHOW the chart. In the future it actually has to toggle it. if (m_showingChart) { if (!m_isTableViewValid) { m_tableView->setHtml(m_table->renderReport(QLatin1String("html"), m_encoding, m_report.name()), QUrl("file://")); // workaround for access permission to css file } m_isTableViewValid = true; m_tableView->show(); m_chartView->hide(); m_control->ui->buttonChart->setText(i18n("Chart")); m_control->ui->buttonChart->setToolTip(i18n("Show the chart version of this report")); m_control->ui->buttonChart->setIcon(QIcon::fromTheme(g_Icons[Icon::OfficeChartLine])); } else { if (!m_isChartViewValid) m_table->drawChart(*m_chartView); m_isChartViewValid = true; m_tableView->hide(); m_chartView->show(); m_control->ui->buttonChart->setText(i18n("Report")); m_control->ui->buttonChart->setToolTip(i18n("Show the report version of this chart")); m_control->ui->buttonChart->setIcon(QIcon::fromTheme(g_Icons[Icon::ViewFinancialList])); } m_showingChart = ! m_showingChart; } void KReportsView::KReportTab::updateDataRange() { QList grids = m_chartView->coordinatePlane()->gridDimensionsList(); // get dimmensions of ploted graph if (grids.isEmpty()) return; QChar separator = locale().groupSeparator(); QChar decimalPoint = locale().decimalPoint(); int precision = m_report.yLabelsPrecision(); QList> dims; // create list of dimension values in string and qreal // get qreal values dims.append(qMakePair(QString(), grids.at(1).start)); dims.append(qMakePair(QString(), grids.at(1).end)); dims.append(qMakePair(QString(), grids.at(1).stepWidth)); dims.append(qMakePair(QString(), grids.at(1).subStepWidth)); // convert qreal values to string variables for (int i = 0; i < 4; ++i) { if (i > 2) ++precision; if (precision == 0) dims[i].first = locale().toString(qRound(dims.at(i).second)); else dims[i].first = locale().toString(dims.at(i).second, 'f', precision).remove(separator).remove(QRegularExpression("0+$")).remove(QRegularExpression("\\" + decimalPoint + "$")); } // save string variables in report's data m_report.setDataRangeStart(dims.at(0).first); m_report.setDataRangeEnd(dims.at(1).first); m_report.setDataMajorTick(dims.at(2).first); m_report.setDataMinorTick(dims.at(3).first); } /** * KReportsView Implementation */ KReportsView::KReportsView(QWidget *parent, const char *name) : KMyMoneyViewBase(parent, name, i18n("Reports")), m_needReload(false), m_needLoad(true), m_reportListView(0) { } void KReportsView::init() { m_needLoad = false; // build reports toc setColumnsAlreadyAdjusted(false); m_reportTabWidget = new QTabWidget(this); addWidget(m_reportTabWidget); m_reportTabWidget->setTabsClosable(true); m_listTab = new QWidget(m_reportTabWidget); m_listTabLayout = new QVBoxLayout(m_listTab); m_listTabLayout->setSpacing(6); m_tocTreeWidget = new QTreeWidget(m_listTab); // report-group items have only 1 column (name of group), // report items have 2 columns (report name and comment) m_tocTreeWidget->setColumnCount(2); // headers QStringList headers; headers << i18n("Reports") << i18n("Comment"); m_tocTreeWidget->setHeaderLabels(headers); m_tocTreeWidget->setAlternatingRowColors(true); m_tocTreeWidget->setSortingEnabled(true); m_tocTreeWidget->sortByColumn(0, Qt::AscendingOrder); // for report group items: // doubleclick toggles the expand-state, // so avoid any further action in case of doubleclick // (see slotItemDoubleClicked) m_tocTreeWidget->setExpandsOnDoubleClick(false); m_tocTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); m_tocTreeWidget->setSelectionMode(QAbstractItemView::SingleSelection); m_listTabLayout->addWidget(m_tocTreeWidget); m_reportTabWidget->addTab(m_listTab, i18n("Reports")); connect(m_reportTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(slotClose(int))); connect(m_tocTreeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotItemDoubleClicked(QTreeWidgetItem*,int))); connect(m_tocTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotListContextMenu(QPoint))); connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotLoadView())); } void KReportsView::showEvent(QShowEvent * event) { if (m_needLoad) init(); emit aboutToShow(); if (m_needReload) { loadView(); m_needReload = false; } KReportTab* tab = dynamic_cast(m_reportTabWidget->currentWidget()); if (tab) emit reportSelected(tab->report()); else emit reportSelected(MyMoneyReport()); // don't forget base class implementation KMyMoneyViewBase::showEvent(event); } void KReportsView::slotLoadView() { m_needReload = true; if (isVisible()) { loadView(); m_needReload = false; } } void KReportsView::loadView() { // remember the id of the current selected item QTreeWidgetItem* item = m_tocTreeWidget->currentItem(); QString selectedItem = (item) ? item->text(0) : QString(); // save expand states of all top-level items QMap expandStates; for (int i = 0; i < m_tocTreeWidget->topLevelItemCount(); i++) { QTreeWidgetItem* item = m_tocTreeWidget->topLevelItem(i); if (item) { QString itemLabel = item->text(0); if (item->isExpanded()) { expandStates.insert(itemLabel, true); } else { expandStates.insert(itemLabel, false); } } } // find the item visible on top QTreeWidgetItem* visibleTopItem = m_tocTreeWidget->itemAt(0, 0); // text of column 0 identifies the item visible on top QString visibleTopItemText; bool visibleTopItemFound = true; if (visibleTopItem == NULL) { visibleTopItemFound = false; } else { // this assumes, that all item-texts in column 0 are unique, // no matter, whether the item is a report- or a group-item visibleTopItemText = visibleTopItem->text(0); } // turn off updates to avoid flickering during reload //m_reportListView->setUpdatesEnabled(false); // // Rebuild the list page // m_tocTreeWidget->clear(); // Default Reports QList defaultreports; defaultReports(defaultreports); QList::const_iterator it_group = defaultreports.constBegin(); // the item to be set as current item QTreeWidgetItem* currentItem = 0L; // group number, this will be used as sort key for reportgroup items // we have: // 1st some default groups // 2nd a chart group // 3rd maybe a favorite group // 4th maybe an orphan group (for old reports) int defaultGroupNo = 1; int chartGroupNo = defaultreports.size() + 1; // group for diagrams QString groupName = I18N_NOOP("Charts"); TocItemGroup* chartTocItemGroup = new TocItemGroup(m_tocTreeWidget, chartGroupNo, i18n(groupName.toLatin1().data())); m_allTocItemGroups.insert(groupName, chartTocItemGroup); while (it_group != defaultreports.constEnd()) { QString groupName = (*it_group).name(); TocItemGroup* defaultTocItemGroup = new TocItemGroup(m_tocTreeWidget, defaultGroupNo++, i18n(groupName.toLatin1().data())); m_allTocItemGroups.insert(groupName, defaultTocItemGroup); if (groupName == selectedItem) { currentItem = defaultTocItemGroup; } QList::const_iterator it_report = (*it_group).begin(); while (it_report != (*it_group).end()) { MyMoneyReport report = *it_report; report.setGroup(groupName); TocItemReport* reportTocItemReport = new TocItemReport(defaultTocItemGroup, report); if (report.name() == selectedItem) { currentItem = reportTocItemReport; } // ALSO place it into the Charts list if it's displayed as a chart by default if (report.isChartByDefault()) { new TocItemReport(chartTocItemGroup, report); } ++it_report; } ++it_group; } // group for custom (favorite) reports int favoriteGroupNo = chartGroupNo + 1; groupName = I18N_NOOP("Favorite Reports"); TocItemGroup* favoriteTocItemGroup = new TocItemGroup(m_tocTreeWidget, favoriteGroupNo, i18n(groupName.toLatin1().data())); m_allTocItemGroups.insert(groupName, favoriteTocItemGroup); TocItemGroup* orphanTocItemGroup = 0; QList customreports = MyMoneyFile::instance()->reportList(); QList::const_iterator it_report = customreports.constBegin(); while (it_report != customreports.constEnd()) { MyMoneyReport report = *it_report; QString groupName = (*it_report).group(); // If this report is in a known group, place it there // KReportGroupListItem* groupnode = groupitems[(*it_report).group()]; TocItemGroup* groupNode = m_allTocItemGroups[groupName]; if (groupNode) { new TocItemReport(groupNode, report); } else { // otherwise, place it in the orphanage if (!orphanTocItemGroup) { // group for orphaned reports int orphanGroupNo = favoriteGroupNo + 1; QString groupName = I18N_NOOP("Old Customized Reports"); orphanTocItemGroup = new TocItemGroup(m_tocTreeWidget, orphanGroupNo, i18n(groupName.toLatin1().data())); m_allTocItemGroups.insert(groupName, orphanTocItemGroup); } new TocItemReport(orphanTocItemGroup, report); } // ALSO place it into the Favorites list if it's a favorite if ((*it_report).isFavorite()) { new TocItemReport(favoriteTocItemGroup, report); } // ALSO place it into the Charts list if it's displayed as a chart by default if ((*it_report).isChartByDefault()) { new TocItemReport(chartTocItemGroup, report); } ++it_report; } // // Go through the tabs to set their update flag or delete them if needed // int index = 1; while (index < m_reportTabWidget->count()) { // TODO: Find some way of detecting the file is closed and kill these tabs!! KReportTab* tab = dynamic_cast(m_reportTabWidget->widget(index)); if (tab->isReadyToDelete() /* || ! reports.count() */) { delete tab; --index; } else { tab->loadTab(); } ++index; } if (visibleTopItemFound) { // try to find the visibleTopItem that we had at the start of this method // intentionally not using 'Qt::MatchCaseSensitive' here // to avoid 'item not found' if someone corrected a typo only QList visibleTopItemList = m_tocTreeWidget->findItems(visibleTopItemText, Qt::MatchFixedString | Qt::MatchRecursive); if (visibleTopItemList.isEmpty()) { // the item could not be found, it was deleted or renamed visibleTopItemFound = false; } else { visibleTopItem = visibleTopItemList.at(0); if (visibleTopItem == NULL) { visibleTopItemFound = false; } } } // adjust column widths, // but only the first time when the view is loaded, // maybe the user sets other column widths later, // so don't disturb him if (columnsAlreadyAdjusted()) { // restore expand states of all top-level items restoreTocExpandState(expandStates); // restore current item m_tocTreeWidget->setCurrentItem(currentItem); // try to scroll to the item visible on top // when this method started if (visibleTopItemFound) { m_tocTreeWidget->scrollToItem(visibleTopItem); } else { m_tocTreeWidget->scrollToTop(); } return; } // avoid flickering m_tocTreeWidget->setUpdatesEnabled(false); // expand all top-level items m_tocTreeWidget->expandAll(); // resize columns m_tocTreeWidget->resizeColumnToContents(0); m_tocTreeWidget->resizeColumnToContents(1); // restore expand states of all top-level items restoreTocExpandState(expandStates); // restore current item m_tocTreeWidget->setCurrentItem(currentItem); // try to scroll to the item visible on top // when this method started if (visibleTopItemFound) { m_tocTreeWidget->scrollToItem(visibleTopItem); } else { m_tocTreeWidget->scrollToTop(); } setColumnsAlreadyAdjusted(true); m_tocTreeWidget->setUpdatesEnabled(true); } void KReportsView::slotOpenUrl(const QUrl &url) { QString view = url.fileName(); if (view.isEmpty()) return; QString command = QUrlQuery(url).queryItemValue("command"); QString id = QUrlQuery(url).queryItemValue("id"); QString tid = QUrlQuery(url).queryItemValue("tid"); if (view == VIEW_REPORTS) { if (command.isEmpty()) { // slotRefreshView(); } else if (command == QLatin1String("print")) slotPrintView(); else if (command == QLatin1String("copy")) slotCopyView(); else if (command == QLatin1String("save")) slotSaveView(); else if (command == QLatin1String("configure")) slotConfigure(); else if (command == QLatin1String("duplicate")) slotDuplicate(); else if (command == QLatin1String("close")) slotCloseCurrent(); else if (command == QLatin1String("delete")) slotDelete(); else qWarning() << i18n("Unknown command '%1' in KReportsView::slotOpenUrl()", qPrintable(command)); } else if (view == VIEW_LEDGER) { emit ledgerSelected(id, tid); } else { qWarning() << i18n("Unknown view '%1' in KReportsView::slotOpenUrl()", qPrintable(view)); } } void KReportsView::slotPrintView() { KReportTab* tab = dynamic_cast(m_reportTabWidget->currentWidget()); if (tab) tab->print(); } void KReportsView::slotCopyView() { KReportTab* tab = dynamic_cast(m_reportTabWidget->currentWidget()); if (tab) tab->copyToClipboard(); } void KReportsView::slotSaveView() { KReportTab* tab = dynamic_cast(m_reportTabWidget->currentWidget()); if (tab) { QString filterList = i18nc("CSV (Filefilter)", "CSV files") + " (*.csv)" + ";;" + i18nc("HTML (Filefilter)", "HTML files") + " (*.html)"; QUrl newURL = QFileDialog::getSaveFileUrl(this, i18n("Export as"), QUrl::fromLocalFile(KRecentDirs::dir(":kmymoney-export")), filterList, &m_selectedExportFilter); if (!newURL.isEmpty()) { KRecentDirs::add(":kmymoney-export", newURL.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path()); QString newName = newURL.toDisplayString(QUrl::PreferLocalFile); // TODO BUG:342776 port to KF5 if (newName.indexOf('.') == -1) newName.append(".html"); try { tab->saveAs(newName, true); } catch (const MyMoneyException &e) { KMessageBox::error(this, i18n("Failed to save: %1", e.what())); } } } } void KReportsView::slotConfigure() { QString cm = "KReportsView::slotConfigure"; KReportTab* tab = dynamic_cast(m_reportTabWidget->currentWidget()); if (!tab) // nothing to do return; int tabNr = m_reportTabWidget->currentIndex(); tab->updateDataRange(); // range will be needed during configuration, but cannot be obtained earlier MyMoneyReport report = tab->report(); if (report.comment() == i18n("Default Report") || report.comment() == i18n("Generated Report")) { report.setComment(i18n("Custom Report")); report.setName(i18n("%1 (Customized)", report.name())); } QPointer dlg = new KReportConfigurationFilterDlg(report); if (dlg->exec()) { MyMoneyReport newreport = dlg->getConfig(); // If this report has an ID, then MODIFY it, otherwise ADD it MyMoneyFileTransaction ft; try { if (! newreport.id().isEmpty()) { MyMoneyFile::instance()->modifyReport(newreport); ft.commit(); tab->modifyReport(newreport); m_reportTabWidget->setTabText(tabNr, newreport.name()); m_reportTabWidget->setCurrentIndex(tabNr) ; } else { MyMoneyFile::instance()->addReport(newreport); ft.commit(); QString reportGroupName = newreport.group(); // find report group TocItemGroup* tocItemGroup = m_allTocItemGroups[reportGroupName]; if (!tocItemGroup) { QString error = i18n("Could not find reportgroup \"%1\" for report \"%2\".\nPlease report this error to the developer's list: kmymoney-devel@kde.org", reportGroupName, newreport.name()); // write to messagehandler qWarning() << cm << error; // also inform user KMessageBox::error(m_reportTabWidget, error, i18n("Critical Error")); // cleanup delete dlg; return; } // do not add TocItemReport to TocItemGroup here, // this is done in loadView addReportTab(newreport); } } catch (const MyMoneyException &e) { KMessageBox::error(this, i18n("Failed to configure report: %1", e.what())); } } delete dlg; } void KReportsView::slotDuplicate() { QString cm = "KReportsView::slotDuplicate"; KReportTab* tab = dynamic_cast(m_reportTabWidget->currentWidget()); if (!tab) { // nothing to do return; } MyMoneyReport dupe = tab->report(); dupe.setName(i18n("Copy of %1", dupe.name())); if (dupe.comment() == i18n("Default Report")) dupe.setComment(i18n("Custom Report")); dupe.clearId(); QPointer dlg = new KReportConfigurationFilterDlg(dupe); if (dlg->exec()) { MyMoneyReport newReport = dlg->getConfig(); MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->addReport(newReport); ft.commit(); QString reportGroupName = newReport.group(); // find report group TocItemGroup* tocItemGroup = m_allTocItemGroups[reportGroupName]; if (!tocItemGroup) { QString error = i18n("Could not find reportgroup \"%1\" for report \"%2\".\nPlease report this error to the developer's list: kmymoney-devel@kde.org", reportGroupName, newReport.name()); // write to messagehandler qWarning() << cm << error; // also inform user KMessageBox::error(m_reportTabWidget, error, i18n("Critical Error")); // cleanup delete dlg; return; } // do not add TocItemReport to TocItemGroup here, // this is done in loadView addReportTab(newReport); } catch (const MyMoneyException &e) { QString error = i18n("Cannot add report, reason: \"%1\"", e.what()); // write to messagehandler qWarning() << cm << error; // also inform user KMessageBox::error(m_reportTabWidget, error, i18n("Critical Error")); } } delete dlg; } void KReportsView::slotDelete() { KReportTab* tab = dynamic_cast(m_reportTabWidget->currentWidget()); if (!tab) { // nothing to do return; } MyMoneyReport report = tab->report(); if (! report.id().isEmpty()) { if (KMessageBox::Continue == deleteReportDialog(report.name())) { // close the tab and then remove the report so that it is not // generated again during the following loadView() call slotClose(m_reportTabWidget->currentIndex()); MyMoneyFileTransaction ft; MyMoneyFile::instance()->removeReport(report); ft.commit(); } } else { KMessageBox::information(this, QString("") + i18n("%1 is a default report, so it cannot be deleted.", report.name()) + QString(""), i18n("Delete Report?")); } } int KReportsView::deleteReportDialog(const QString &reportName) { return KMessageBox::warningContinueCancel(this, QString("") + i18n("Are you sure you want to delete report %1? There is no way to recover it.", reportName) + QString(""), i18n("Delete Report?")); } void KReportsView::slotOpenReport(const QString& id) { if (id.isEmpty()) { // nothing to do return; } KReportTab* page = 0; // Find the tab which contains the report int index = 1; while (index < m_reportTabWidget->count()) { KReportTab* current = dynamic_cast(m_reportTabWidget->widget(index)); if (current->report().id() == id) { page = current; break; } ++index; } // Show the tab, or create a new one, as needed if (page) m_reportTabWidget->setCurrentIndex(index); else addReportTab(MyMoneyFile::instance()->report(id)); } void KReportsView::slotOpenReport(const MyMoneyReport& report) { qDebug() << Q_FUNC_INFO << " " << report.name(); KReportTab* page = 0; // Find the tab which contains the report indicated by this list item int index = 1; while (index < m_reportTabWidget->count()) { KReportTab* current = dynamic_cast(m_reportTabWidget->widget(index)); if (current->report().name() == report.name()) { page = current; break; } ++index; } // Show the tab, or create a new one, as needed if (page) m_reportTabWidget->setCurrentIndex(index); else addReportTab(report); } void KReportsView::slotItemDoubleClicked(QTreeWidgetItem* item, int) { TocItem* tocItem = dynamic_cast(item); if (!tocItem->isReport()) { // toggle the expanded-state for reportgroup-items item->setExpanded(item->isExpanded() ? false : true); // nothing else to do for reportgroup-items return; } TocItemReport* reportTocItem = dynamic_cast(tocItem); MyMoneyReport& report = reportTocItem->getReport(); KReportTab* page = 0; // Find the tab which contains the report indicated by this list item int index = 1; while (index < m_reportTabWidget->count()) { KReportTab* current = dynamic_cast(m_reportTabWidget->widget(index)); // If this report has an ID, we'll use the ID to match if (! report.id().isEmpty()) { if (current->report().id() == report.id()) { page = current; break; } } // Otherwise, use the name to match. THIS ASSUMES that no 2 default reports // have the same name...but that would be pretty a boneheaded thing to do. else { if (current->report().name() == report.name()) { page = current; break; } } ++index; } // Show the tab, or create a new one, as needed if (page) m_reportTabWidget->setCurrentIndex(index); else addReportTab(report); } void KReportsView::slotToggleChart() { KReportTab* tab = dynamic_cast(m_reportTabWidget->currentWidget()); if (tab) tab->toggleChart(); } void KReportsView::slotCloseCurrent() { slotClose(m_reportTabWidget->currentIndex()); } void KReportsView::slotClose(int index) { KReportTab* tab = dynamic_cast(m_reportTabWidget->widget(index)); if (tab) { m_reportTabWidget->removeTab(index); tab->setReadyToDelete(true); } } void KReportsView::slotCloseAll() { if(!m_needLoad) { while (true) { KReportTab* tab = dynamic_cast(m_reportTabWidget->widget(1)); if (tab) { m_reportTabWidget->removeTab(1); tab->setReadyToDelete(true); } else break; } } } void KReportsView::addReportTab(const MyMoneyReport& report) { new KReportTab(m_reportTabWidget, report, this); } void KReportsView::slotListContextMenu(const QPoint & p) { QTreeWidgetItem *item = m_tocTreeWidget->itemAt(p); if (!item) { return; } TocItem* tocItem = dynamic_cast(item); if (!tocItem->isReport()) { // currently there is no context menu for reportgroup items return; } QMenu* contextmenu = new QMenu(this); contextmenu->addAction(i18nc("To open a new report", "&Open"), this, SLOT(slotOpenFromList())); contextmenu->addAction(i18nc("Configure a report", "&Configure"), this, SLOT(slotConfigureFromList())); contextmenu->addAction(i18n("&New report"), this, SLOT(slotNewFromList())); // Only add this option if it's a custom report. Default reports cannot be deleted TocItemReport* reportTocItem = dynamic_cast(tocItem); MyMoneyReport& report = reportTocItem->getReport(); if (! report.id().isEmpty()) { contextmenu->addAction(i18n("&Delete"), this, SLOT(slotDeleteFromList())); } contextmenu->popup(m_tocTreeWidget->mapToGlobal(p)); } void KReportsView::slotOpenFromList() { TocItem* tocItem = dynamic_cast(m_tocTreeWidget->currentItem()); if (tocItem) slotItemDoubleClicked(tocItem, 0); } void KReportsView::slotConfigureFromList() { TocItem* tocItem = dynamic_cast(m_tocTreeWidget->currentItem()); if (tocItem) { slotItemDoubleClicked(tocItem, 0); slotConfigure(); } } void KReportsView::slotNewFromList() { TocItem* tocItem = dynamic_cast(m_tocTreeWidget->currentItem()); if (tocItem) { slotItemDoubleClicked(tocItem, 0); slotDuplicate(); } } void KReportsView::slotDeleteFromList() { TocItem* tocItem = dynamic_cast(m_tocTreeWidget->currentItem()); if (tocItem) { TocItemReport* reportTocItem = dynamic_cast(tocItem); MyMoneyReport& report = reportTocItem->getReport(); // If this report does not have an ID, it's a default report and cannot be deleted if (! report.id().isEmpty() && KMessageBox::Continue == deleteReportDialog(report.name())) { // check if report's tab is open; start from 1 because 0 is toc tab for (int i = 1; i < m_reportTabWidget->count(); ++i) { KReportTab* tab = dynamic_cast(m_reportTabWidget->widget(i)); if (tab->report().id() == report.id()) { slotClose(i); // if open, close it, so no crash when switching to it break; } } MyMoneyFileTransaction ft; MyMoneyFile::instance()->removeReport(report); ft.commit(); } } } void KReportsView::defaultReports(QList& groups) { { ReportGroup list("Income and Expenses", i18n("Income and Expenses")); list.push_back(MyMoneyReport( MyMoneyReport::eExpenseIncome, MyMoneyReport::eMonths, MyMoneyTransactionFilter::currentMonth, MyMoneyReport::eDetailAll, i18n("Income and Expenses This Month"), i18n("Default Report") )); list.push_back(MyMoneyReport( MyMoneyReport::eExpenseIncome, MyMoneyReport::eMonths, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Income and Expenses This Year"), i18n("Default Report") )); list.push_back(MyMoneyReport( MyMoneyReport::eExpenseIncome, MyMoneyReport::eYears, MyMoneyTransactionFilter::allDates, MyMoneyReport::eDetailAll, i18n("Income and Expenses By Year"), i18n("Default Report") )); list.push_back(MyMoneyReport( MyMoneyReport::eExpenseIncome, MyMoneyReport::eMonths, MyMoneyTransactionFilter::last12Months, MyMoneyReport::eDetailTop, i18n("Income and Expenses Graph"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartType(MyMoneyReport::eChartLine); list.back().setChartDataLabels(false); list.push_back(MyMoneyReport( MyMoneyReport::eExpenseIncome, MyMoneyReport::eMonths, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailGroup, i18n("Income and Expenses Pie Chart"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartType(MyMoneyReport::eChartPie); list.back().setShowingRowTotals(false); groups.push_back(list); } { ReportGroup list("Net Worth", i18n("Net Worth")); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailTop, i18n("Net Worth By Month"), i18n("Default Report") )); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::today, MyMoneyReport::eDetailTop, i18n("Net Worth Today"), i18n("Default Report") )); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eYears, MyMoneyTransactionFilter::allDates, MyMoneyReport::eDetailTop, i18n("Net Worth By Year"), i18n("Default Report") )); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::next7Days, MyMoneyReport::eDetailTop, i18n("7-day Cash Flow Forecast"), i18n("Default Report") )); list.back().setIncludingSchedules(true); list.back().setColumnsAreDays(true); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::last12Months, MyMoneyReport::eDetailTotal, i18n("Net Worth Graph"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(MyMoneyReport::eChartLine); list.push_back(MyMoneyReport( MyMoneyReport::eInstitution, MyMoneyReport::eQCnone, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailTop, i18n("Account Balances by Institution"), i18n("Default Report") )); list.push_back(MyMoneyReport( MyMoneyReport::eAccountType, MyMoneyReport::eQCnone, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailTop, i18n("Account Balances by Type"), i18n("Default Report") )); groups.push_back(list); } { ReportGroup list("Transactions", i18n("Transactions")); list.push_back(MyMoneyReport( MyMoneyReport::eAccount, MyMoneyReport::eQCnumber | MyMoneyReport::eQCpayee | MyMoneyReport::eQCcategory | MyMoneyReport::eQCtag | MyMoneyReport::eQCbalance, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Transactions by Account"), i18n("Default Report") )); //list.back().setConvertCurrency(false); list.push_back(MyMoneyReport( MyMoneyReport::eCategory, MyMoneyReport::eQCnumber | MyMoneyReport::eQCpayee | MyMoneyReport::eQCaccount | MyMoneyReport::eQCtag, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Transactions by Category"), i18n("Default Report") )); list.push_back(MyMoneyReport( MyMoneyReport::ePayee, MyMoneyReport::eQCnumber | MyMoneyReport::eQCcategory | MyMoneyReport::eQCtag, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Transactions by Payee"), i18n("Default Report") )); list.push_back(MyMoneyReport( MyMoneyReport::eTag, MyMoneyReport::eQCnumber | MyMoneyReport::eQCcategory, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Transactions by Tag"), i18n("Default Report") )); list.push_back(MyMoneyReport( MyMoneyReport::eMonth, MyMoneyReport::eQCnumber | MyMoneyReport::eQCpayee | MyMoneyReport::eQCcategory | MyMoneyReport::eQCtag, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Transactions by Month"), i18n("Default Report") )); list.push_back(MyMoneyReport( MyMoneyReport::eWeek, MyMoneyReport::eQCnumber | MyMoneyReport::eQCpayee | MyMoneyReport::eQCcategory | MyMoneyReport::eQCtag, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Transactions by Week"), i18n("Default Report") )); list.push_back(MyMoneyReport( MyMoneyReport::eAccount, MyMoneyReport::eQCloan, MyMoneyTransactionFilter::allDates, MyMoneyReport::eDetailAll, i18n("Loan Transactions"), i18n("Default Report") )); list.back().setLoansOnly(true); list.push_back(MyMoneyReport( MyMoneyReport::eAccountReconcile, MyMoneyReport::eQCnumber | MyMoneyReport::eQCpayee | MyMoneyReport::eQCcategory | MyMoneyReport::eQCbalance, MyMoneyTransactionFilter::last3Months, MyMoneyReport::eDetailAll, i18n("Transactions by Reconciliation Status"), i18n("Default Report") )); groups.push_back(list); } { ReportGroup list("CashFlow", i18n("Cash Flow")); list.push_back(MyMoneyReport( MyMoneyReport::eCashFlow, MyMoneyReport::eQCnumber | MyMoneyReport::eQCpayee | MyMoneyReport::eQCaccount, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Cash Flow Transactions This Month"), i18n("Default Report") )); groups.push_back(list); } { ReportGroup list("Investments", i18n("Investments")); list.push_back(MyMoneyReport( MyMoneyReport::eTopAccount, MyMoneyReport::eQCaction | MyMoneyReport::eQCshares | MyMoneyReport::eQCprice, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Investment Transactions"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( MyMoneyReport::eAccountByTopAccount, MyMoneyReport::eQCshares | MyMoneyReport::eQCprice, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Investment Holdings by Account"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( MyMoneyReport::eEquityType, MyMoneyReport::eQCshares | MyMoneyReport::eQCprice, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Investment Holdings by Type"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( MyMoneyReport::eAccountByTopAccount, MyMoneyReport::eQCperformance, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Investment Performance by Account"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( MyMoneyReport::eEquityType, MyMoneyReport::eQCperformance, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Investment Performance by Type"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( MyMoneyReport::eAccountByTopAccount, MyMoneyReport::eQCcapitalgain, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Investment Capital Gains by Account"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( MyMoneyReport::eEquityType, MyMoneyReport::eQCcapitalgain, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Investment Capital Gains by Type"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::today, MyMoneyReport::eDetailAll, i18n("Investment Holdings Pie"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(MyMoneyReport::eChartPie); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::last12Months, MyMoneyReport::eDetailAll, i18n("Investment Worth Graph"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(MyMoneyReport::eChartLine); list.back().setColumnsAreDays(true); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::last12Months, MyMoneyReport::eDetailAll, i18n("Investment Price Graph"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(MyMoneyReport::eChartLine); list.back().setColumnsAreDays(true); list.back().setInvestmentsOnly(true); list.back().setIncludingBudgetActuals(false); list.back().setIncludingPrice(true); list.back().setConvertCurrency(true); list.back().setChartDataLabels(false); list.back().setSkipZero(true); list.back().setShowingColumnTotals(false); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::last12Months, MyMoneyReport::eDetailAll, i18n("Investment Moving Average Price Graph"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(MyMoneyReport::eChartLine); list.back().setColumnsAreDays(true); list.back().setInvestmentsOnly(true); list.back().setIncludingBudgetActuals(false); list.back().setIncludingAveragePrice(true); list.back().setMovingAverageDays(10); list.back().setConvertCurrency(true); list.back().setChartDataLabels(false); list.back().setShowingColumnTotals(false); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::last30Days, MyMoneyReport::eDetailAll, i18n("Investment Moving Average"), i18n("Default Report") )); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(MyMoneyReport::eChartLine); list.back().setColumnsAreDays(true); list.back().setInvestmentsOnly(true); list.back().setIncludingBudgetActuals(false); list.back().setIncludingMovingAverage(true); list.back().setMovingAverageDays(10); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::last30Days, MyMoneyReport::eDetailAll, i18n("Investment Moving Average vs Actual"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(MyMoneyReport::eChartLine); list.back().setColumnsAreDays(true); list.back().setInvestmentsOnly(true); list.back().setIncludingBudgetActuals(true); list.back().setIncludingMovingAverage(true); list.back().setMovingAverageDays(10); groups.push_back(list); } { ReportGroup list("Taxes", i18n("Taxes")); list.push_back(MyMoneyReport( MyMoneyReport::eCategory, MyMoneyReport::eQCnumber | MyMoneyReport::eQCpayee | MyMoneyReport::eQCaccount, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Tax Transactions by Category"), i18n("Default Report") )); list.back().setTax(true); list.push_back(MyMoneyReport( MyMoneyReport::ePayee, MyMoneyReport::eQCnumber | MyMoneyReport::eQCcategory | MyMoneyReport::eQCaccount, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Tax Transactions by Payee"), i18n("Default Report") )); list.back().setTax(true); list.push_back(MyMoneyReport( MyMoneyReport::eCategory, MyMoneyReport::eQCnumber | MyMoneyReport::eQCpayee | MyMoneyReport::eQCaccount, MyMoneyTransactionFilter::lastFiscalYear, MyMoneyReport::eDetailAll, i18n("Tax Transactions by Category Last Fiscal Year"), i18n("Default Report") )); list.back().setTax(true); list.push_back(MyMoneyReport( MyMoneyReport::ePayee, MyMoneyReport::eQCnumber | MyMoneyReport::eQCcategory | MyMoneyReport::eQCaccount, MyMoneyTransactionFilter::lastFiscalYear, MyMoneyReport::eDetailAll, i18n("Tax Transactions by Payee Last Fiscal Year"), i18n("Default Report") )); list.back().setTax(true); groups.push_back(list); } { ReportGroup list("Budgeting", i18n("Budgeting")); list.push_back(MyMoneyReport( MyMoneyReport::eBudgetActual, MyMoneyReport::eMonths, MyMoneyTransactionFilter::yearToDate, MyMoneyReport::eDetailAll, i18n("Budgeted vs. Actual This Year"), i18n("Default Report") )); list.back().setShowingRowTotals(true); list.back().setBudget("Any", true); list.push_back(MyMoneyReport( MyMoneyReport::eBudgetActual, MyMoneyReport::eMonths, MyMoneyTransactionFilter::yearToMonth, MyMoneyReport::eDetailAll, i18n("Budgeted vs. Actual This Year (YTM)"), i18n("Default Report") )); list.back().setShowingRowTotals(true); list.back().setBudget("Any", true); // in case we're in January, we show the last year if (QDate::currentDate().month() == 1) { list.back().setDateFilter(MyMoneyTransactionFilter::lastYear); } list.push_back(MyMoneyReport( MyMoneyReport::eBudgetActual, MyMoneyReport::eMonths, MyMoneyTransactionFilter::currentMonth, MyMoneyReport::eDetailAll, i18n("Monthly Budgeted vs. Actual"), i18n("Default Report") )); list.back().setBudget("Any", true); list.push_back(MyMoneyReport( MyMoneyReport::eBudgetActual, MyMoneyReport::eMonths, MyMoneyTransactionFilter::currentYear, MyMoneyReport::eDetailAll, i18n("Yearly Budgeted vs. Actual"), i18n("Default Report") )); list.back().setBudget("Any", true); list.back().setShowingRowTotals(true); list.push_back(MyMoneyReport( MyMoneyReport::eBudget, MyMoneyReport::eMonths, MyMoneyTransactionFilter::currentMonth, MyMoneyReport::eDetailAll, i18n("Monthly Budget"), i18n("Default Report") )); list.back().setBudget("Any", false); list.push_back(MyMoneyReport( MyMoneyReport::eBudget, MyMoneyReport::eMonths, MyMoneyTransactionFilter::currentYear, MyMoneyReport::eDetailAll, i18n("Yearly Budget"), i18n("Default Report") )); list.back().setBudget("Any", false); list.back().setShowingRowTotals(true); list.push_back(MyMoneyReport( MyMoneyReport::eBudgetActual, MyMoneyReport::eMonths, MyMoneyTransactionFilter::currentYear, MyMoneyReport::eDetailGroup, i18n("Yearly Budgeted vs Actual Graph"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setBudget("Any", true); list.back().setChartType(MyMoneyReport::eChartLine); groups.push_back(list); } { ReportGroup list("Forecast", i18n("Forecast")); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::next12Months, MyMoneyReport::eDetailTop, i18n("Forecast By Month"), i18n("Default Report") )); list.back().setIncludingForecast(true); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::nextQuarter, MyMoneyReport::eDetailTop, i18n("Forecast Next Quarter"), i18n("Default Report") )); list.back().setColumnsAreDays(true); list.back().setIncludingForecast(true); list.push_back(MyMoneyReport( MyMoneyReport::eExpenseIncome, MyMoneyReport::eMonths, MyMoneyTransactionFilter::currentYear, MyMoneyReport::eDetailTop, i18n("Income and Expenses Forecast This Year"), i18n("Default Report") )); list.back().setIncludingForecast(true); list.push_back(MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, MyMoneyTransactionFilter::next3Months, MyMoneyReport::eDetailTotal, i18n("Net Worth Forecast Graph"), i18n("Default Report") )); list.back().setColumnsAreDays(true); list.back().setIncludingForecast(true); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(MyMoneyReport::eChartLine); groups.push_back(list); } { ReportGroup list("Information", i18n("General Information")); list.push_back(MyMoneyReport( MyMoneyReport::eSchedule, MyMoneyReport::eMonths, MyMoneyTransactionFilter::next12Months, MyMoneyReport::eDetailAll, i18n("Schedule Information"), i18n("Default Report") )); list.back().setDetailLevel(MyMoneyReport::eDetailAll); list.push_back(MyMoneyReport( MyMoneyReport::eSchedule, MyMoneyReport::eMonths, MyMoneyTransactionFilter::next12Months, MyMoneyReport::eDetailAll, i18n("Schedule Summary Information"), i18n("Default Report") )); list.back().setDetailLevel(MyMoneyReport::eDetailTop); list.push_back(MyMoneyReport( MyMoneyReport::eAccountInfo, MyMoneyReport::eMonths, MyMoneyTransactionFilter::today, MyMoneyReport::eDetailAll, i18n("Account Information"), i18n("Default Report") )); list.back().setConvertCurrency(false); list.push_back(MyMoneyReport( MyMoneyReport::eAccountLoanInfo, MyMoneyReport::eMonths, MyMoneyTransactionFilter::today, MyMoneyReport::eDetailAll, i18n("Loan Information"), i18n("Default Report") )); list.back().setConvertCurrency(false); groups.push_back(list); } } bool KReportsView::columnsAlreadyAdjusted() { return m_columnsAlreadyAdjusted; } void KReportsView::setColumnsAlreadyAdjusted(bool adjusted) { m_columnsAlreadyAdjusted = adjusted; } void KReportsView::restoreTocExpandState(QMap& expandStates) { for (int i = 0; i < m_tocTreeWidget->topLevelItemCount(); i++) { QTreeWidgetItem* item = m_tocTreeWidget->topLevelItem(i); if (item) { QString itemLabel = item->text(0); if (expandStates.contains(itemLabel)) { item->setExpanded(expandStates[itemLabel]); } else { item->setExpanded(false); } } } } // Make sure, that these definitions are only used within this file // this does not seem to be necessary, but when building RPMs the // build option 'final' is used and all CPP files are concatenated. // So it could well be, that in another CPP file these definitions // are also used. #undef VIEW_LEDGER #undef VIEW_SCHEDULE #undef VIEW_WELCOME #undef VIEW_HOME #undef VIEW_REPORTS diff --git a/kmymoney/views/kreportsview.h b/kmymoney/views/kreportsview.h index d4a731cdc..6be668fd9 100644 --- a/kmymoney/views/kreportsview.h +++ b/kmymoney/views/kreportsview.h @@ -1,257 +1,268 @@ /*************************************************************************** kreportsview.h - description ------------------- begin : Sat Mar 27 2004 copyright : (C) 2000-2004 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio Ace Jones ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KREPORTSVIEW_H #define KREPORTSVIEW_H +#include "config-kmymoney.h" + // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include -#include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #ifdef _CHECK_MEMORY #include #endif #include "mymoneyschedule.h" #include #include #include "pivottable.h" #include "querytable.h" #include "../widgets/reportcontrolimpl.h" #include "kreportchartview.h" #include "kmymoneyview.h" #include "tocitem.h" #include "tocitemgroup.h" #include "tocitemreport.h" class MyMoneyReport; class MyQWebEnginePage; +#ifdef ENABLE_WEBENGINE +class QWebEngineView; +#else +class KWebView; +#endif + /** * Displays a page where reports can be placed. * * @author Ace Jones * * @short A view for reports. **/ class KReportsView : public KMyMoneyViewBase { Q_OBJECT public: /** * Helper class for KReportView. * * This is the widget which displays a single report in the TabWidget that comprises this view. * * @author Ace Jones */ class KReportTab: public QWidget { private: + #ifdef ENABLE_WEBENGINE QWebEngineView *m_tableView; + #else + KWebView *m_tableView; + #endif reports::KReportChartView *m_chartView; ReportControl *m_control; QVBoxLayout *m_layout; QPrinter *m_currentPrinter; MyMoneyReport m_report; bool m_deleteMe; bool m_chartEnabled; bool m_showingChart; bool m_needReload; bool m_isChartViewValid; bool m_isTableViewValid; QPointer m_table; /** * Users character set encoding. */ QByteArray m_encoding; public: KReportTab(QTabWidget* parent, const MyMoneyReport& report, const KReportsView *eventHandler); ~KReportTab(); const MyMoneyReport& report() const { return m_report; } void print(); void toggleChart(); /** * Updates informations about ploted chart in report's data */ void updateDataRange(); void copyToClipboard(); void saveAs(const QString& filename, bool includeCSS = false); void updateReport(); QString createTable(const QString& links = QString()); const ReportControl* control() const { return m_control; } bool isReadyToDelete() const { return m_deleteMe; } void setReadyToDelete(bool f) { m_deleteMe = f; } void modifyReport(const MyMoneyReport& report) { m_report = report; } void showEvent(QShowEvent * event); void loadTab(); }; /** * Helper class for KReportView. * * This is a named list of reports, which will be one section * in the list of default reports * * @author Ace Jones */ class ReportGroup: public QList { private: QString m_name; ///< the title of the group in non-translated form QString m_title; ///< the title of the group in i18n-ed form public: ReportGroup() {} ReportGroup(const QString& name, const QString& title): m_name(name), m_title(title) {} const QString& name() const { return m_name; } const QString& title() const { return m_title; } }; private: bool m_needReload; /** * This member holds the load state of page */ bool m_needLoad; QListWidget* m_reportListView; QTabWidget* m_reportTabWidget; QWidget* m_listTab; QVBoxLayout* m_listTabLayout; QTreeWidget* m_tocTreeWidget; QMap m_allTocItemGroups; QString m_selectedExportFilter; bool m_columnsAlreadyAdjusted; void restoreTocExpandState(QMap& expandStates); public: /** * Standard constructor. * * @param parent The QWidget this is used in. * @param name The QT name. * * @return An object of type KReportsView * * @see ~KReportsView */ explicit KReportsView(QWidget *parent = 0, const char *name = 0); /** * Overridden so we can reload the view if necessary. * * @return Nothing. */ void showEvent(QShowEvent * event); protected: void addReportTab(const MyMoneyReport&); void loadView(); static void defaultReports(QList&); bool columnsAlreadyAdjusted(); void setColumnsAlreadyAdjusted(bool adjusted); public slots: void slotOpenUrl(const QUrl &url); void slotLoadView(); void slotPrintView(); void slotCopyView(); void slotSaveView(); void slotConfigure(); void slotDuplicate(); void slotToggleChart(); void slotItemDoubleClicked(QTreeWidgetItem* item, int); void slotOpenReport(const QString&); void slotOpenReport(const MyMoneyReport&); void slotCloseCurrent(); void slotClose(int index); void slotCloseAll(); void slotDelete(); void slotListContextMenu(const QPoint &); void slotOpenFromList(); void slotConfigureFromList(); void slotNewFromList(); void slotDeleteFromList(); signals: /** * This signal is emitted whenever a report is selected */ void reportSelected(const MyMoneyReport&); /** * This signal is emitted whenever a transaction is selected */ void ledgerSelected(const QString&, const QString&); private: /** * Display a dialog to confirm report deletion */ int deleteReportDialog(const QString&); /** Initializes page and sets its load status to initialized */ void init(); }; #endif