diff --git a/CMakeLists.txt b/CMakeLists.txt
index 61ff6c2e1..bc1a2cac6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,367 +1,369 @@
# The CMake version we require
cmake_minimum_required(VERSION 3.1)
# Setting the name of the main project
project(KMyMoney VERSION "5.0.80")
# 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)
list(APPEND CMAKE_AUTOMOC_MACRO_NAMES "K_PLUGIN_FACTORY" "K_PLUGIN_FACTORY_WITH_JSON")
if (POLICY CMP0063)
cmake_policy(SET CMP0063 NEW) # Policy introduced in CMake version 3.3
endif()
if (POLICY CMP0071)
# We do not require the old behaviour. It is only set to old, to prevent accidential use of
# the new behavour. If the new behaviour becomes important, cmake_minimum_required has to be
# set to "3.10".
cmake_policy(SET CMP0071 OLD) # Policy introduced in CMake version 3.10
endif()
######################### General Requirements ##########################
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(CMakeDependentOption)
include(GenerateExportHeader)
include(KMyMoneyMacros)
set(GPG_ENCRYPTION "no")
set (OPT_KF5_COMPONENTS DocTools Holidays Contacts Akonadi IdentityManagement Activities)
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
OPTIONAL_COMPONENTS Concurrent)
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(LibAlkimia5 7.0.0 REQUIRED)
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
include_directories(${GMP_INCLUDE_DIR})
endif()
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)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# use DBus only on Linux
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
set(KMM_DBUS 1)
endif()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR})
# check for Doxygen
find_package(Doxygen)
if(DOXYGEN_FOUND)
set(APIDOC_DIR ${CMAKE_CURRENT_BINARY_DIR}/apidocs)
make_directory("${APIDOC_DIR}")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kmymoney.doxygen.in ${CMAKE_CURRENT_BINARY_DIR}/kmymoney.doxygen IMMEDIATE)
add_custom_target(apidoc "${DOXYGEN}" "${CMAKE_CURRENT_BINARY_DIR}/kmymoney.doxygen")
endif(DOXYGEN_FOUND)
# check some include files exists
set(CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE=500 -D_BSD_SOURCE)
include (CheckIncludeFile)
check_include_file("unistd.h" HAVE_UNISTD_H)
check_include_file("pwd.h" HAVE_PWD_H)
check_include_file("windows.h" HAVE_WINDOWS_H)
check_include_file("lmcons.h" HAVE_LMCONS_H)
check_include_file("process.h" HAVE_PROCESS_H)
# include check for members in structs
include (CheckStructHasMember)
######################### Special Requirements ##########################
# This is needed for QtSqlite and QtDesigner
# (they'll install files to ${QT_INSTALL_DIR}/plugins/)
get_filename_component(QT_BIN_DIR "${QT_MOC_EXECUTABLE}" PATH)
get_filename_component(QT_DIR ${QT_BIN_DIR} PATH)
set(QT_INSTALL_DIR ${QT_DIR} CACHE PATH
"Qt install prefix defaults to the Qt prefix: ${QT_DIR}")
if(KF5IdentityManagement_FOUND AND KF5Akonadi_FOUND AND KF5Contacts_FOUND)
set(KMM_ADDRESSBOOK_FOUND true)
endif()
+option(ENABLE_FORECASTVIEW "Enable forecast view" ON)
+
CMAKE_DEPENDENT_OPTION(ENABLE_SQLSTORAGE "Enable SQL storage support." ON
"Qt5Sql_FOUND" OFF)
# check for optional QWebEngine
option(ENABLE_WEBENGINE "Enable QWebEngine" OFF)
if(ENABLE_WEBENGINE)
find_package(Qt5WebEngineWidgets 5.8 REQUIRED)
if(Qt5WebEngineWidgets_VERSION VERSION_GREATER 5.8.99 AND Qt5WebEngineWidgets_VERSION VERSION_LESS 5.9.3)
message(WARNING "QWebEngine version ${Qt5WebEngineWidgets_VERSION} is known to be unstable with KMyMoney")
endif()
else(ENABLE_WEBENGINE)
find_package(KF5WebKit REQUIRED)
endif(ENABLE_WEBENGINE)
# check for optional LibOFX support
find_package(LibOfx)
if(LIBOFX_FOUND)
option(ENABLE_OFXIMPORTER "Enable OFX Importer" ON)
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
set(PATH_TO_LIBOFX_HEADER "${LIBOFX_INCLUDE_DIR}/libofx/libofx.h") # Windows doesn't even see the header if it's not full path
else()
set(PATH_TO_LIBOFX_HEADER "libofx/libofx.h")
endif()
unset(LIBOFX_HAVE_CLIENTUID)
unset(LIBOFX_HAVE_CLIENTUID CACHE) #not doing this will prevent updating below check
check_struct_has_member("struct OfxFiLogin" clientuid ${PATH_TO_LIBOFX_HEADER} LIBOFX_HAVE_CLIENTUID)
if(NOT ENABLE_OFXIMPORTER)
unset(LIBOFX_FOUND CACHE)
unset(LIBOFX_HAVE_CLIENTUID CACHE)
endif(NOT ENABLE_OFXIMPORTER)
else(LIBOFX_FOUND)
option(ENABLE_OFXIMPORTER "Enable OFX Importer" OFF)
if(ENABLE_OFXIMPORTER)
unset(LIBOFX_FOUND CACHE)
unset(LIBOFX_HAVE_CLIENTUID CACHE)
endif(ENABLE_OFXIMPORTER)
endif(LIBOFX_FOUND)
# 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)
set(Python_ADDITIONAL_VERSIONS 2.7 2.6)
find_package(PythonInterp 2.6)
find_package(PythonLibs ${PYTHON_VERSION_STRING})
if(PYTHONINTERP_FOUND AND PYTHONLIBS_FOUND)
if(PYTHON_VERSION_MAJOR GREATER_EQUAL 3)
unset(PYTHONLIBS_FOUND)
unset(PYTHONINTERP_FOUND)
message(WARNING "Python 2 required, but Python 3 found.")
else()
include(FindPythonModule)
find_python_module(weboob REQUIRED)
endif()
endif()
CMAKE_DEPENDENT_OPTION(ENABLE_WEBOOB "Enable Weboob plugin" ON
"PYTHONLIBS_FOUND;PYTHONINTERP_FOUND;PY_WEBOOB;Qt5Concurrent_FOUND" OFF)
# 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)
option(ENABLE_QIFIMPORTER "Enable QIF Importer" ON)
option(ENABLE_QIFEXPORTER "Enable QIF Exporter" ON)
option(ENABLE_GNCIMPORTER "Enable GNC Importer" ON)
option(ENABLE_CSVIMPORTER "Enable CSV Importer" ON)
option(ENABLE_CSVEXPORTER "Enable CSV Exporter" ON)
option(ENABLE_UNFINISHEDFEATURES "For devs only" OFF)
# TODO: this should be removed
enable_testing()
######################### Settings ##########################
# If no build type is set, use "Release with Debug Info"
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING
"Choose the type of build.
Possible values are: 'Release' 'RelWithDebInfo' 'Debug' 'DebugKMM' 'Debugfull' 'Profile'
The default value is: 'RelWithDebInfo'" FORCE)
# tells gcc to enable exception handling
include(KDECompilerSettings)
kde_enable_exceptions()
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_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# be more pedantic about common symbols
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-common -Wextra")
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(IS_GNU 1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wlogical-op")
endif()
# DebugKMM, Debugfull, Profile
set(CMAKE_CXX_FLAGS_DEBUGKMM
"-g -O2 -fno-reorder-blocks -fno-schedule-insns -fno-inline")
set(CMAKE_CXX_FLAGS_DEBUGFULL
"-g3 -fno-inline")
set(CMAKE_CXX_FLAGS_PROFILE
"-g3 -fno-inline -ftest-coverage -fprofile-arcs")
# 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()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /FORCE:Multiple")
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_DEBUGKMM "${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("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}
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/kmymoney/CMakeLists.txt b/kmymoney/CMakeLists.txt
index a7853e0e3..9b23515ae 100644
--- a/kmymoney/CMakeLists.txt
+++ b/kmymoney/CMakeLists.txt
@@ -1,189 +1,190 @@
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 )
if(BUILD_TESTING)
add_subdirectory( tests )
endif()
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 )
+kconfig_add_kcfg_files( kmm_config_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/plugins/forecast/forecastviewsettings.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
kmm_utils_webconnect
kmm_utils_platformtools
)
# must build kmymoney/transactionsortoption.h
# from transactionsortoption.ui first
add_dependencies(kmymoney_common generate_base_ui_srcs kmm_config)
add_dependencies(wizardpages widgets)
add_dependencies(dialogs 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
KF5::Archive
KF5::ConfigGui
KF5::WidgetsAddons
KF5::KIOCore
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/dialogs/settings/CMakeLists.txt b/kmymoney/dialogs/settings/CMakeLists.txt
index 9d8256353..56a078047 100644
--- a/kmymoney/dialogs/settings/CMakeLists.txt
+++ b/kmymoney/dialogs/settings/CMakeLists.txt
@@ -1,54 +1,52 @@
set (libsettings_a_SOURCES
ksettingscolors.cpp
ksettingsfonts.cpp
ksettingsicons.cpp
- ksettingsforecast.cpp
ksettingsgeneral.cpp
ksettingsgpg.cpp
ksettingshome.cpp
ksettingsonlinequotes.cpp
ksettingsregister.cpp
ksettingsschedules.cpp
ksettingsreports.cpp
ksettingskmymoney.cpp
ksettingsplugins.cpp
)
set (libsettings_a_UI
ksettingscolors.ui
ksettingsfonts.ui
ksettingsicons.ui
- ksettingsforecast.ui
ksettingsgeneral.ui
ksettingsgpg.ui
ksettingshome.ui
ksettingsonlinequotes.ui
ksettingsregister.ui
ksettingsschedules.ui
ksettingsreports.ui
)
ki18n_wrap_ui(libsettings_a_SOURCES ${libsettings_a_UI} )
add_library(settings STATIC ${libsettings_a_SOURCES})
# TODO: cleanup dependencies
target_link_libraries(settings PUBLIC
KF5::Completion
KF5::KIOWidgets
KF5::TextWidgets
KF5::I18n
KF5::IconThemes
KF5::Completion
KF5::KCMUtils
KF5::ItemViews
Alkimia::alkimia
Qt5::Sql
)
if (KF5Holidays_FOUND)
target_link_libraries(settings PUBLIC KF5::Holidays)
endif()
add_dependencies(settings widgets kmm_config)
diff --git a/kmymoney/dialogs/settings/ksettingskmymoney.cpp b/kmymoney/dialogs/settings/ksettingskmymoney.cpp
index e97c3bb04..882ae4164 100644
--- a/kmymoney/dialogs/settings/ksettingskmymoney.cpp
+++ b/kmymoney/dialogs/settings/ksettingskmymoney.cpp
@@ -1,92 +1,89 @@
/*
* This file is part of KMyMoney, A Personal Finance Manager by KDE
* Copyright (C) 2016 Christian Dávid
* (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.
*
* 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 "ksettingskmymoney.h"
#include
#include
#include "ksettingsgeneral.h"
#include "ksettingsregister.h"
#include "ksettingsgpg.h"
#include "ksettingscolors.h"
#include "ksettingsfonts.h"
#include "ksettingsicons.h"
#include "ksettingsschedules.h"
#include "ksettingsonlinequotes.h"
#include "ksettingshome.h"
-#include "ksettingsforecast.h"
#include "ksettingsreports.h"
#include "ksettingsplugins.h"
#include "icons.h"
using namespace Icons;
KSettingsKMyMoney::KSettingsKMyMoney(QWidget *parent, const QString &name, KCoreConfigSkeleton *config)
: KConfigDialog(parent, name, config)
{
// create the pages ...
const auto generalPage = new KSettingsGeneral();
const auto registerPage = new KSettingsRegister();
const auto homePage = new KSettingsHome();
const auto schedulesPage = new KSettingsSchedules();
const auto encryptionPage = new KSettingsGpg();
const auto colorsPage = new KSettingsColors();
const auto fontsPage = new KSettingsFonts();
const auto iconsPage = new KSettingsIcons();
const auto onlineQuotesPage = new KSettingsOnlineQuotes();
- const auto forecastPage = new KSettingsForecast();
const auto reportsPage = new KSettingsReports();
const auto pluginsPage = new KSettingsPlugins();
addPage(generalPage, i18nc("General settings", "General"), Icons::get(Icon::SystemRun).name());
addPage(homePage, i18n("Home"), Icons::get(Icon::ViewHome).name());
addPage(registerPage, i18nc("Ledger view settings", "Ledger"), Icons::get(Icon::ViewFinancialList).name());
addPage(schedulesPage, i18n("Scheduled transactions"), Icons::get(Icon::ViewSchedules).name());
addPage(onlineQuotesPage, i18n("Online Quotes"), Icons::get(Icon::PreferencesNetwork).name());
addPage(reportsPage, i18nc("Report settings", "Reports"), Icons::get(Icon::ViewReports).name());
- addPage(forecastPage, i18nc("Forecast settings", "Forecast"), Icons::get(Icon::ViewForecast).name());
addPage(encryptionPage, i18n("Encryption"), Icons::get(Icon::Kgpg).name());
addPage(colorsPage, i18n("Colors"), Icons::get(Icon::PreferencesColor).name());
addPage(fontsPage, i18n("Fonts"), Icons::get(Icon::PreferencesFont).name());
addPage(iconsPage, i18n("Icons"), Icons::get(Icon::PreferencesIcon).name());
addPage(pluginsPage, i18n("Plugins"), Icons::get(Icon::NetworkDisconect).name(), QString(), false);
setHelp("details.settings", "kmymoney");
connect(this, &KConfigDialog::rejected, schedulesPage, &KSettingsSchedules::slotResetRegion);
connect(this, &KConfigDialog::rejected, iconsPage, &KSettingsIcons::slotResetTheme);
connect(this, &KConfigDialog::settingsChanged, generalPage, &KSettingsGeneral::slotUpdateEquitiesVisibility);
auto defaultButton = button(QDialogButtonBox::RestoreDefaults);
auto applyButton = button(QDialogButtonBox::Apply);
connect(this, &KConfigDialog::accepted, pluginsPage, &KSettingsPlugins::slotSavePluginConfiguration);
connect(applyButton, &QPushButton::clicked, pluginsPage, &KSettingsPlugins::slotSavePluginConfiguration);
connect(defaultButton, &QPushButton::clicked, pluginsPage, &KSettingsPlugins::slotResetToDefaults);
connect(pluginsPage, &KSettingsPlugins::changed, this, &KSettingsKMyMoney::slotPluginsChanged);
connect(pluginsPage, &KSettingsPlugins::settingsChanged, this, &KConfigDialog::settingsChanged);
}
void KSettingsKMyMoney::slotPluginsChanged(bool changed)
{
auto applyButton = button(QDialogButtonBox::Apply);
applyButton->setEnabled(changed);
}
diff --git a/kmymoney/kmymoney.kcfg b/kmymoney/kmymoney.kcfg
index 1cd41e752..083ae66e2 100644
--- a/kmymoney/kmymoney.kcfg
+++ b/kmymoney/kmymoney.kcfg
@@ -1,441 +1,395 @@
false110truetruefalsefalse1002010060falsefalse00999(None)00999falsefalsefalsefalsetruetrue00falsefalsefalsetruetrue0truetruefalseaccount,cashflow,payee,category,tag,memo,number,date,amount,statenumber,date,payee,category,tag,memo,payment,deposit,state8,1,2,3,4,5,6,7,-9,101.00truetruefalsefalsefalsefalsefalse255,255,0152,251,152255,0,00,0,255255,242,155255,255,221falsefalsefalsefalsefalsefalsefalsefalsetruetruetrue00111128QDateTime::fromString("1900-01-01T00:00:00", Qt::ISODate)falsesystem1,-9,-41,-41,-9,-404false
-
-
-
- 0
-
-
-
- 90
- 1
- 999
-
-
-
- 30
- 1
- 999
-
-
-
- 3
- 1
- 999
-
-
-
- 0
- 0
- 31
-
-
-
- 1
-
-
-
- true
-
-
-
- true
-
-
-
- true
-
- BarBanca,BanqueKreditkarteAnd. Kto.EröffnungssaldoRechnungfalse10010020256202
diff --git a/kmymoney/kmymoneyglobalsettings.cpp b/kmymoney/kmymoneyglobalsettings.cpp
index 4aadda286..0b93c5767 100644
--- a/kmymoney/kmymoneyglobalsettings.cpp
+++ b/kmymoney/kmymoneyglobalsettings.cpp
@@ -1,174 +1,185 @@
/***************************************************************************
kmymoneyglobalsettings.cpp
-------------------
copyright : (C) 2006 by Thomas Baumgart
email : ipwizard@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* *
* 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 "kmymoneyglobalsettings.h"
// ----------------------------------------------------------------------------
// QT Includes
#include
#include
// ----------------------------------------------------------------------------
// KDE Includes
#include
// ----------------------------------------------------------------------------
// Project Includes
#include "mymoneyforecast.h"
// include kmymoneysettings.cpp here to gain access to s_globalKMyMoneySettings
#include "kmymoneysettings.cpp"
+#include "forecastviewsettings.h"
void KMyMoneyGlobalSettings::injectExternalSettings(KMyMoneySettings* p)
{
s_globalKMyMoneySettings()->q = p;
}
QFont KMyMoneyGlobalSettings::listCellFont()
{
if (useSystemFont()) {
return QFontDatabase::systemFont(QFontDatabase::GeneralFont);
} else {
return KMyMoneySettings::listCellFont();
}
}
QFont KMyMoneyGlobalSettings::listHeaderFont()
{
if (useSystemFont()) {
QFont font = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
font.setBold(true);
return font;
} else {
return KMyMoneySettings::listHeaderFont();
}
}
QColor KMyMoneyGlobalSettings::schemeColor(const SchemeColor color)
{
switch(color) {
case SchemeColor::ListBackground1:
return KColorScheme (QPalette::Active, KColorScheme::View).background(KColorScheme::NormalBackground).color();
case SchemeColor::ListBackground2:
return KColorScheme (QPalette::Active, KColorScheme::View).background(KColorScheme::AlternateBackground).color();
case SchemeColor::ListGrid:
return KColorScheme (QPalette::Active, KColorScheme::View).foreground(KColorScheme::InactiveText).color();
case SchemeColor::ListHighlightText:
return KColorScheme (QPalette::Active, KColorScheme::Selection).foreground(KColorScheme::NormalText).color();
case SchemeColor::ListHighlight:
return KColorScheme (QPalette::Active, KColorScheme::Selection).background(KColorScheme::NormalBackground).color();
case SchemeColor::WindowText:
return KColorScheme (QPalette::Active, KColorScheme::Window).foreground(KColorScheme::NormalText).color();
case SchemeColor::WindowBackground:
return KColorScheme (QPalette::Active, KColorScheme::Window).background(KColorScheme::NormalBackground).color();
case SchemeColor::Positive:
return KColorScheme (QPalette::Active, KColorScheme::View).foreground(KColorScheme::PositiveText).color();
case SchemeColor::Negative:
return KColorScheme (QPalette::Active, KColorScheme::View).foreground(KColorScheme::NegativeText).color();
case SchemeColor::TransactionImported:
if (useCustomColors())
return KMyMoneySettings::transactionImportedColor();
else
return KColorScheme (QPalette::Active, KColorScheme::Complementary).background(KColorScheme::NeutralBackground).color();
case SchemeColor::TransactionMatched:
if (useCustomColors())
return KMyMoneySettings::transactionMatchedColor();
else
return KColorScheme (QPalette::Active, KColorScheme::Complementary).background(KColorScheme::PositiveBackground).color();
case SchemeColor::TransactionErroneous:
if (useCustomColors())
return KMyMoneySettings::transactionErroneousColor();
else
return KColorScheme (QPalette::Active, KColorScheme::View).foreground(KColorScheme::NegativeText).color();
case SchemeColor::FieldRequired:
if (useCustomColors())
return KMyMoneySettings::fieldRequiredColor();
else
return KColorScheme (QPalette::Active, KColorScheme::View).background(KColorScheme::NeutralBackground).color();
case SchemeColor::GroupMarker:
if (useCustomColors())
return KMyMoneySettings::groupMarkerColor();
else
return KColorScheme (QPalette::Active, KColorScheme::Selection).background(KColorScheme::LinkBackground).color();
case SchemeColor::MissingConversionRate:
if (useCustomColors())
return KMyMoneySettings::missingConversionRateColor();
else
return KColorScheme (QPalette::Active, KColorScheme::Complementary).foreground(KColorScheme::LinkText).color();
default:
return QColor();
}
}
QStringList KMyMoneyGlobalSettings::itemList()
{
bool prevValue = self()->useDefaults(true);
QStringList all = KMyMoneySettings::itemList().split(',', QString::SkipEmptyParts);
self()->useDefaults(prevValue);
QStringList list = KMyMoneySettings::itemList().split(',', QString::SkipEmptyParts);
// now add all from 'all' that are missing in 'list'
QRegExp exp("-?(\\d+)");
QStringList::iterator it_s;
for (it_s = all.begin(); it_s != all.end(); ++it_s) {
exp.indexIn(*it_s);
if (!list.contains(exp.cap(1)) && !list.contains(QString("-%1").arg(exp.cap(1)))) {
list << *it_s;
}
}
return list;
}
int KMyMoneyGlobalSettings::firstFiscalMonth()
{
return KMyMoneySettings::fiscalYearBegin() + 1;
}
int KMyMoneyGlobalSettings::firstFiscalDay()
{
return KMyMoneySettings::fiscalYearBeginDay();
}
QDate KMyMoneyGlobalSettings::firstFiscalDate()
{
QDate date = QDate(QDate::currentDate().year(), firstFiscalMonth(), firstFiscalDay());
if (date > QDate::currentDate())
date = date.addYears(-1);
return date;
}
MyMoneyForecast KMyMoneyGlobalSettings::forecast()
{
MyMoneyForecast forecast;
// override object defaults with those of the application
- forecast.setForecastCycles(KMyMoneyGlobalSettings::forecastCycles());
- forecast.setAccountsCycle(KMyMoneyGlobalSettings::forecastAccountCycle());
+ forecast.setForecastCycles(ForecastViewSettings::forecastCycles());
+ forecast.setAccountsCycle(ForecastViewSettings::forecastAccountCycle());
forecast.setHistoryStartDate(QDate::currentDate().addDays(-forecast.forecastCycles()*forecast.accountsCycle()));
forecast.setHistoryEndDate(QDate::currentDate().addDays(-1));
- forecast.setForecastDays(KMyMoneyGlobalSettings::forecastDays());
- forecast.setBeginForecastDay(KMyMoneyGlobalSettings::beginForecastDay());
- forecast.setForecastMethod(KMyMoneyGlobalSettings::forecastMethod());
- forecast.setHistoryMethod(KMyMoneyGlobalSettings::historyMethod());
- forecast.setIncludeFutureTransactions(KMyMoneyGlobalSettings::includeFutureTransactions());
- forecast.setIncludeScheduledTransactions(KMyMoneyGlobalSettings::includeScheduledTransactions());
+ forecast.setForecastDays(ForecastViewSettings::forecastDays());
+ forecast.setBeginForecastDay(ForecastViewSettings::beginForecastDay());
+ forecast.setForecastMethod(ForecastViewSettings::forecastMethod());
+ forecast.setHistoryMethod(ForecastViewSettings::historyMethod());
+ forecast.setIncludeFutureTransactions(ForecastViewSettings::includeFutureTransactions());
+ forecast.setIncludeScheduledTransactions(ForecastViewSettings::includeScheduledTransactions());
return forecast;
}
+
+int KMyMoneyGlobalSettings::forecastDays()
+{
+ return ForecastViewSettings::forecastDays();
+}
+
+int KMyMoneyGlobalSettings::forecastAccountCycle()
+{
+ return ForecastViewSettings::forecastAccountCycle();
+}
diff --git a/kmymoney/kmymoneyglobalsettings.h b/kmymoney/kmymoneyglobalsettings.h
index 03574c2b7..5bf0bc76d 100644
--- a/kmymoney/kmymoneyglobalsettings.h
+++ b/kmymoney/kmymoneyglobalsettings.h
@@ -1,82 +1,84 @@
/***************************************************************************
kmymoneyglobalsettings.h
-------------------
copyright : (C) 2006 by Thomas Baumgart
email : ipwizard@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* *
* 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 KMYMONEYGLOBALSETTINGS_H
#define KMYMONEYGLOBALSETTINGS_H
// ----------------------------------------------------------------------------
// QT Includes
// ----------------------------------------------------------------------------
// KDE Includes
// ----------------------------------------------------------------------------
// Project Includes
#include "kmymoneysettings.h"
class MyMoneyForecast;
enum class SchemeColor {
ListBackground1,
ListBackground2,
ListGrid,
ListHighlightText,
ListHighlight,
WindowText,
WindowBackground,
Positive,
Negative,
TransactionImported,
TransactionMatched,
TransactionErroneous,
FieldRequired,
GroupMarker,
MissingConversionRate
};
class KMyMoneyGlobalSettings : public KMyMoneySettings
{
public:
static void injectExternalSettings(KMyMoneySettings* p);
static QColor schemeColor(const SchemeColor color);
static QFont listCellFont();
static QFont listHeaderFont();
static QStringList itemList();
/**
* returns the number of the first month in the fiscal year
*/
static int firstFiscalMonth();
/**
* returns the number of the first day of the fiscal year
*/
static int firstFiscalDay();
/**
* returns the date of the first day in the current fiscal year
*/
static QDate firstFiscalDate();
/**
* Construct a MyMoneyForecast object setup with all KMyMoneySettings
*/
static MyMoneyForecast forecast();
+ static int forecastDays();
+ static int forecastAccountCycle();
};
#endif
diff --git a/kmymoney/plugins/CMakeLists.txt b/kmymoney/plugins/CMakeLists.txt
index fe0c6c9fe..db07e5d86 100644
--- a/kmymoney/plugins/CMakeLists.txt
+++ b/kmymoney/plugins/CMakeLists.txt
@@ -1,82 +1,86 @@
add_subdirectory( onlinetasks )
add_subdirectory( ibanbicdata )
add_subdirectory( interfaces )
add_subdirectory( csv )
add_subdirectory( qif )
add_subdirectory( gnc )
add_subdirectory( ofx )
add_subdirectory( icalendar )
add_subdirectory( reconciliationreport )
add_subdirectory( checkprinting )
+if(ENABLE_FORECASTVIEW)
+ add_subdirectory(forecast)
+endif()
+
if(ENABLE_SQLSTORAGE)
add_subdirectory(sql)
endif()
if (KBANKING_FOUND)
add_subdirectory( kbanking )
endif (KBANKING_FOUND)
option(ENABLE_SQLCIPHER "Enable SQLCipher plugin" OFF)
if (ENABLE_SQLCIPHER)
add_subdirectory(sqlcipher)
endif(ENABLE_SQLCIPHER)
option(ENABLE_ONLINEJOBPLUGINMOCKUP "Enable onlineJob-plugin mockup (only for developers)" OFF)
if (ENABLE_ONLINEJOBPLUGINMOCKUP)
add_subdirectory(onlinejobpluginmockup)
endif()
if (ENABLE_WEBOOB)
add_subdirectory( weboob )
endif ()
########### next target ###############
set(kmm_plugin_LIB_SRCS
appinterface.cpp
importinterface.cpp
kmymoneyplugin.cpp
statementinterface.cpp
viewinterface.cpp
onlinepluginextended.cpp
interfaceloader.cpp
)
set(plugins_HEADERS
appinterface.h
importinterface.h
kmymoneyplugin.h
statementinterface.h
viewinterface.h
${CMAKE_CURRENT_BINARY_DIR}/kmm_plugin_export.h
onlinepluginextended.h
)
add_library(kmm_plugin SHARED ${kmm_plugin_LIB_SRCS})
generate_export_header(kmm_plugin BASE_NAME kmm_plugin)
target_link_libraries(kmm_plugin
PUBLIC
KF5::XmlGui
KF5::KCMUtils
KF5::KIOWidgets
Qt5::Gui
Qt5::Widgets
kmm_mymoney
)
set_target_properties(kmm_plugin PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR})
########### install files ###############
install(TARGETS kmm_plugin
${INSTALL_TARGETS_DEFAULT_ARGS} )
install(FILES ${plugins_HEADERS}
DESTINATION ${INCLUDE_INSTALL_DIR}/kmymoney COMPONENT Devel)
install(FILES
kmymoney-payeeidentifierdelegate.desktop
kmymoney-importerplugin.desktop
DESTINATION ${SERVICETYPES_INSTALL_DIR}
)
diff --git a/kmymoney/plugins/forecast/CMakeLists.txt b/kmymoney/plugins/forecast/CMakeLists.txt
new file mode 100644
index 000000000..f8e69b9af
--- /dev/null
+++ b/kmymoney/plugins/forecast/CMakeLists.txt
@@ -0,0 +1,51 @@
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/forecastview.json.in ${CMAKE_CURRENT_BINARY_DIR}/forecastview.json @ONLY)
+
+set(forecastview_SOURCES
+ forecastview.cpp
+ kforecastview.cpp
+ fixedcolumntreeview.cpp
+ )
+
+ki18n_wrap_ui(forecastview_SOURCES kforecastview.ui)
+kconfig_add_kcfg_files(forecastview_SOURCES forecastviewsettings.kcfgc)
+
+add_library(forecastview MODULE ${forecastview_SOURCES} )
+
+target_link_libraries(forecastview
+ PUBLIC
+ kmm_plugin
+ KF5::TextWidgets
+ reports
+ viewbase
+)
+
+install(TARGETS forecastview
+ DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney")
+
+# the KCM module
+
+set(kcm_forecastview_PART_SRCS
+ kcm_forecastview.cpp
+ )
+
+kconfig_add_kcfg_files(kcm_forecastview_PART_SRCS forecastviewsettings.kcfgc)
+
+ki18n_wrap_ui(kcm_forecastview_PART_SRCS forecastviewsettings.ui)
+
+add_library(kcm_forecastview MODULE ${kcm_forecastview_PART_SRCS})
+
+kcoreaddons_desktop_to_json(kcm_forecastview kcm_forecastview.desktop)
+
+target_link_libraries(kcm_forecastview
+ KF5::I18n
+ KF5::ConfigWidgets
+ KF5::Completion
+ KF5::KIOWidgets
+ KF5::CoreAddons
+ )
+
+install(TARGETS kcm_forecastview
+ DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney")
+
+install(FILES kcm_forecastview.desktop
+ DESTINATION "${SERVICES_INSTALL_DIR}")
diff --git a/kmymoney/widgets/fixedcolumntreeview.cpp b/kmymoney/plugins/forecast/fixedcolumntreeview.cpp
similarity index 100%
rename from kmymoney/widgets/fixedcolumntreeview.cpp
rename to kmymoney/plugins/forecast/fixedcolumntreeview.cpp
diff --git a/kmymoney/widgets/fixedcolumntreeview.h b/kmymoney/plugins/forecast/fixedcolumntreeview.h
similarity index 100%
rename from kmymoney/widgets/fixedcolumntreeview.h
rename to kmymoney/plugins/forecast/fixedcolumntreeview.h
diff --git a/kmymoney/plugins/forecast/forecastview.cpp b/kmymoney/plugins/forecast/forecastview.cpp
new file mode 100644
index 000000000..29262255e
--- /dev/null
+++ b/kmymoney/plugins/forecast/forecastview.cpp
@@ -0,0 +1,68 @@
+/***************************************************************************
+ forecastview.cpp
+ -------------------
+
+ copyright : (C) 2018 by Łukasz Wojniłowicz
+ email : lukasz.wojnilowicz@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) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "forecastview.h"
+
+// ----------------------------------------------------------------------------
+// QT Includes
+
+// ----------------------------------------------------------------------------
+// KDE Includes
+
+#include
+#include
+
+// ----------------------------------------------------------------------------
+// Project Includes
+
+#include "viewinterface.h"
+#include "kforecastview.h"
+#include "kmymoneyglobalsettings.h"
+
+ForecastView::ForecastView(QObject *parent, const QVariantList &args) :
+ KMyMoneyPlugin::Plugin(parent, "forecastview"/*must be the same as X-KDE-PluginInfo-Name*/)
+{
+ Q_UNUSED(args)
+ setComponentName("forecastview", i18n("Forecast view"));
+ // For information, announce that we have been loaded.
+ qDebug("Plugins: forecastview loaded");
+}
+
+ForecastView::~ForecastView()
+{
+ qDebug("Plugins: forecastview unloaded");
+}
+
+void ForecastView::injectExternalSettings(KMyMoneySettings* p)
+{
+ KMyMoneyGlobalSettings::injectExternalSettings(p);
+}
+
+void ForecastView::plug()
+{
+ m_view = new KForecastView;
+ viewInterface()->addView(m_view, i18n("Forecast"), View::Forecast);
+}
+
+void ForecastView::unplug()
+{
+ viewInterface()->removeView(View::Forecast);
+}
+
+K_PLUGIN_FACTORY_WITH_JSON(ForecastViewFactory, "forecastview.json", registerPlugin();)
+
+#include "forecastview.moc"
diff --git a/kmymoney/dialogs/settings/ksettingsforecast.h b/kmymoney/plugins/forecast/forecastview.h
similarity index 63%
copy from kmymoney/dialogs/settings/ksettingsforecast.h
copy to kmymoney/plugins/forecast/forecastview.h
index fd96f9594..6a5e486d0 100644
--- a/kmymoney/dialogs/settings/ksettingsforecast.h
+++ b/kmymoney/plugins/forecast/forecastview.h
@@ -1,47 +1,49 @@
/***************************************************************************
- ksettingsforecast.h
+ forecastview.h
-------------------
- copyright : (C) 2007 by Alvaro Soliverez
- email : asoliverez@gmail.com
- (C) 2017 by Łukasz Wojniłowicz
+ copyright : (C) 2018 by Łukasz Wojniłowicz
+ email : lukasz.wojnilowicz@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) any later version. *
* *
***************************************************************************/
-#ifndef KSETTINGSFORECAST_H
-#define KSETTINGSFORECAST_H
-
-// ----------------------------------------------------------------------------
-// QT Includes
-
-#include
+#ifndef FORECASTVIEW_H
+#define FORECASTVIEW_H
// ----------------------------------------------------------------------------
// KDE Includes
// ----------------------------------------------------------------------------
+// QT Includes
+
// Project Includes
-namespace Ui { class KSettingsForecast; }
+#include "kmymoneyplugin.h"
-class KSettingsForecast : public QWidget
+class KForecastView;
+
+class ForecastView : public KMyMoneyPlugin::Plugin
{
Q_OBJECT
- Q_DISABLE_COPY(KSettingsForecast)
public:
- explicit KSettingsForecast(QWidget* parent = nullptr);
- ~KSettingsForecast();
+ explicit ForecastView(QObject *parent, const QVariantList &args);
+ ~ForecastView() override;
+
+ void plug() override;
+ void unplug() override;
+
+ void injectExternalSettings(KMyMoneySettings* p) override;
private:
- Ui::KSettingsForecast *ui;
+ KForecastView* m_view;
};
-#endif
+#endif
diff --git a/kmymoney/plugins/forecast/forecastview.json.in b/kmymoney/plugins/forecast/forecastview.json.in
new file mode 100644
index 000000000..bb0df8a17
--- /dev/null
+++ b/kmymoney/plugins/forecast/forecastview.json.in
@@ -0,0 +1,21 @@
+{
+ "KPlugin": {
+ "Authors": [
+ {
+ "Email": "asoliverez@gmail.com,lukasz.wojnilowicz@gmail.com",
+ "Name": "Alvaro Soliverez,Łukasz Wojniłowicz"
+ }
+ ],
+ "Description": "Adds forecast view to KMyMoney",
+ "EnabledByDefault": true,
+ "Icon": "forecast",
+ "Id": "forecastview",
+ "License": "GPL",
+ "Name": "Forecast view",
+ "ServiceTypes": [
+ "KMyMoney/Plugin"
+ ],
+ "Version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@",
+ "Website": "https://kmymoney.org/plugins.html"
+ }
+}
diff --git a/kmymoney/plugins/forecast/forecastviewsettings.kcfg b/kmymoney/plugins/forecast/forecastviewsettings.kcfg
new file mode 100644
index 000000000..c925dbe5d
--- /dev/null
+++ b/kmymoney/plugins/forecast/forecastviewsettings.kcfg
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+ 0
+
+
+
+ 90
+ 1
+ 999
+
+
+
+ 30
+ 1
+ 999
+
+
+
+ 3
+ 1
+ 999
+
+
+
+ 0
+ 0
+ 31
+
+
+
+ 1
+
+
+
+ true
+
+
+
+ true
+
+
+
+ true
+
+
+
diff --git a/kmymoney/plugins/forecast/forecastviewsettings.kcfgc b/kmymoney/plugins/forecast/forecastviewsettings.kcfgc
new file mode 100644
index 000000000..cfcd67f63
--- /dev/null
+++ b/kmymoney/plugins/forecast/forecastviewsettings.kcfgc
@@ -0,0 +1,6 @@
+# Code generation options for kconfig_compiler
+File=forecastviewsettings.kcfg
+ClassName=ForecastViewSettings
+Singleton=true
+Mutators=true
+# will create the necessary code for setting those variables
diff --git a/kmymoney/dialogs/settings/ksettingsforecast.ui b/kmymoney/plugins/forecast/forecastviewsettings.ui
similarity index 98%
rename from kmymoney/dialogs/settings/ksettingsforecast.ui
rename to kmymoney/plugins/forecast/forecastviewsettings.ui
index c71b62f22..de6ab6b68 100644
--- a/kmymoney/dialogs/settings/ksettingsforecast.ui
+++ b/kmymoney/plugins/forecast/forecastviewsettings.ui
@@ -1,214 +1,214 @@
- KSettingsForecast
-
+ ForecastViewSettings
+ 00497379Forecast Settings5500Number of Days of Account Cycle:falseDay of Month to start Forecast:falseNumber of Days to Forecast:false13321QSizePolicy::ExpandingQt::HorizontalForecast MethodScheduled and Future TransactionsHistory-basedHistory-based settingsNumber of Cycles to use in Forecast:false13021QSizePolicy::ExpandingQt::HorizontalHistory Forecast MethodSimple Moving AverageWeighted Moving AverageLinear Regression2040QSizePolicy::ExpandingQt::Vertical
diff --git a/kmymoney/plugins/forecast/kcm_forecastview.cpp b/kmymoney/plugins/forecast/kcm_forecastview.cpp
new file mode 100644
index 000000000..ebd3b9d58
--- /dev/null
+++ b/kmymoney/plugins/forecast/kcm_forecastview.cpp
@@ -0,0 +1,53 @@
+/***************************************************************************
+ * Copyright 2018 Łukasz Wojniłowicz lukasz.wojnilowicz@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_forecastview.h"
+#include
+
+// KDE includes
+#include
+#include
+#include "forecastviewsettings.h"
+
+ForecastViewSettingsWidget::ForecastViewSettingsWidget(QWidget* parent) :
+ QWidget(parent)
+{
+ setupUi(this);
+}
+
+KCMForecastView::KCMForecastView(QWidget *parent, const QVariantList& args)
+ : KCModule(parent, args)
+{
+ ForecastViewSettingsWidget* w = new ForecastViewSettingsWidget(this);
+ addConfig(ForecastViewSettings::self(), w);
+ QVBoxLayout *layout = new QVBoxLayout;
+ setLayout(layout);
+ layout->addWidget(w);
+ setButtons(NoAdditionalButton);
+ load();
+}
+
+KCMForecastView::~KCMForecastView()
+{
+}
+
+K_PLUGIN_FACTORY_WITH_JSON(KCMForecastViewFactory, "kcm_forecastview.json", registerPlugin();)
+
+#include "kcm_forecastview.moc"
diff --git a/kmymoney/plugins/forecast/kcm_forecastview.desktop b/kmymoney/plugins/forecast/kcm_forecastview.desktop
new file mode 100644
index 000000000..1618846cb
--- /dev/null
+++ b/kmymoney/plugins/forecast/kcm_forecastview.desktop
@@ -0,0 +1,15 @@
+[Desktop Entry]
+Name=Forecast view configuration
+Name[x-test]=xxForecast view configurationxx
+Icon=forecast
+Type=Service
+ServiceTypes=KCModule
+Exec=kcmshell5 forecastview
+Categories=Qt;KDE;kmymoney;
+Keywords=forecastview;kmymoney;
+Keywords[x-test]=xxforecastviewxx;xxkmymoneyxx;
+X-KDE-Library=kmymoney/kcm_forecastview
+X-KDE-ParentApp=forecastview
+X-KDE-ParentComponents=forecastview
+Comment=Configuration for forecast view plugin
+Comment[x-test]=xxConfiguration for forecast view pluginxx
diff --git a/kmymoney/plugins/forecast/kcm_forecastview.h b/kmymoney/plugins/forecast/kcm_forecastview.h
new file mode 100644
index 000000000..024ff59a4
--- /dev/null
+++ b/kmymoney/plugins/forecast/kcm_forecastview.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * Copyright 2018 Łukasz Wojniłowicz lukasz.wojnilowicz@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_FORECASTVIEW_H
+#define KCM_FORECASTVIEW_H
+
+#include
+#include
+#include "ui_forecastviewsettings.h"
+
+class ForecastViewSettingsWidget : public QWidget, public Ui::ForecastViewSettings
+{
+ Q_OBJECT
+public:
+ explicit ForecastViewSettingsWidget(QWidget* parent = nullptr);
+};
+
+class KCMForecastView : public KCModule
+{
+public:
+ explicit KCMForecastView(QWidget* parent, const QVariantList& args);
+ ~KCMForecastView();
+};
+
+#endif
+
diff --git a/kmymoney/views/kforecastview.cpp b/kmymoney/plugins/forecast/kforecastview.cpp
similarity index 100%
rename from kmymoney/views/kforecastview.cpp
rename to kmymoney/plugins/forecast/kforecastview.cpp
diff --git a/kmymoney/views/kforecastview.h b/kmymoney/plugins/forecast/kforecastview.h
similarity index 97%
rename from kmymoney/views/kforecastview.h
rename to kmymoney/plugins/forecast/kforecastview.h
index 08300ae2c..514195e4d 100644
--- a/kmymoney/views/kforecastview.h
+++ b/kmymoney/plugins/forecast/kforecastview.h
@@ -1,71 +1,71 @@
/***************************************************************************
kforecastview.h
-------------------
copyright : (C) 2007 by Alvaro Soliverez
email : asoliverez@gmail.com
(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. *
* *
***************************************************************************/
#ifndef KFORECASTVIEW_H
#define KFORECASTVIEW_H
// ----------------------------------------------------------------------------
// QT Includes
// ----------------------------------------------------------------------------
// KDE Includes
// ----------------------------------------------------------------------------
// Project Includes
#include "kmymoneyviewbase.h"
class QTreeWidgetItem;
namespace reports { class KReportChartView; }
class FixedColumnTreeView;
class MyMoneyAccount;
class MyMoneySecurity;
class MyMoneyForecast;
class MyMoneyPrice;
/**
* @author Alvaro Soliverez
*
* This class implements the forecast 'view'.
*/
class KForecastViewPrivate;
class KForecastView : public KMyMoneyViewBase
{
Q_OBJECT
public:
- explicit KForecastView(QWidget *parent = 0);
+ explicit KForecastView(QWidget *parent = nullptr);
~KForecastView() override;
void setDefaultFocus() override;
void refresh() override;
protected:
void showEvent(QShowEvent* event) override;
private:
Q_DECLARE_PRIVATE(KForecastView)
private Q_SLOTS:
void slotTabChanged(int index);
void slotManualForecast();
void itemExpanded(QTreeWidgetItem *item);
void itemCollapsed(QTreeWidgetItem *item);
};
#endif
diff --git a/kmymoney/views/kforecastview.ui b/kmymoney/plugins/forecast/kforecastview.ui
similarity index 100%
rename from kmymoney/views/kforecastview.ui
rename to kmymoney/plugins/forecast/kforecastview.ui
diff --git a/kmymoney/views/kforecastview_p.h b/kmymoney/plugins/forecast/kforecastview_p.h
similarity index 98%
rename from kmymoney/views/kforecastview_p.h
rename to kmymoney/plugins/forecast/kforecastview_p.h
index 313f160d5..c9b7ae357 100644
--- a/kmymoney/views/kforecastview_p.h
+++ b/kmymoney/plugins/forecast/kforecastview_p.h
@@ -1,1048 +1,1049 @@
/***************************************************************************
kforecastview.cpp
-------------------
copyright : (C) 2007 by Alvaro Soliverez
email : asoliverez@gmail.com
(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. *
* *
***************************************************************************/
#ifndef KFORECASTVIEW_P_H
#define KFORECASTVIEW_P_H
#include "kforecastview.h"
// ----------------------------------------------------------------------------
// QT Includes
#include
#include
#include
#include
#include
#include
#include
// ----------------------------------------------------------------------------
// KDE Includes
#include
#include
#include
// ----------------------------------------------------------------------------
// Project Includes
#include "ui_kforecastview.h"
+#include "forecastviewsettings.h"
#include "kmymoneyviewbase_p.h"
#include "mymoneymoney.h"
#include "mymoneyforecast.h"
#include "mymoneyprice.h"
#include "mymoneyutils.h"
#include "mymoneyfile.h"
#include "mymoneyaccount.h"
#include "mymoneyexception.h"
#include "mymoneysecurity.h"
#include "kmymoneyglobalsettings.h"
#include "mymoneybudget.h"
#include "pivottable.h"
#include "fixedcolumntreeview.h"
#include "kreportchartview.h"
#include "reportaccount.h"
#include "icons.h"
#include "mymoneyenums.h"
using namespace reports;
using namespace Icons;
typedef enum {
SummaryView = 0,
ListView,
AdvancedView,
BudgetView,
ChartView,
// insert new values above this line
MaxViewTabs
} ForecastViewTab;
enum ForecastViewRoles {
ForecastRole = Qt::UserRole, /**< The forecast is held in this role.*/
AccountRole = Qt::UserRole + 1, /**< The MyMoneyAccount is stored in this role in column 0.*/
AmountRole = Qt::UserRole + 2, /**< The amount.*/
ValueRole = Qt::UserRole + 3, /**< The value.*/
};
enum EForecastViewType { eSummary = 0, eDetailed, eAdvanced, eBudget, eUndefined };
class KForecastViewPrivate : public KMyMoneyViewBasePrivate
{
Q_DECLARE_PUBLIC(KForecastView)
public:
explicit KForecastViewPrivate(KForecastView *qq) :
KMyMoneyViewBasePrivate(),
q_ptr(qq),
ui(new Ui::KForecastView),
m_needLoad(true),
m_totalItem(0),
m_assetItem(0),
m_liabilityItem(0),
m_incomeItem(0),
m_expenseItem(0),
m_chartLayout(0),
m_forecastChart(0)
{
}
~KForecastViewPrivate()
{
delete ui;
}
void init()
{
Q_Q(KForecastView);
m_needLoad = false;
ui->setupUi(q);
m_forecastChart = new KReportChartView(ui->m_tabChart);
for (int i = 0; i < MaxViewTabs; ++i)
m_needReload[i] = false;
KSharedConfigPtr config = KSharedConfig::openConfig();
KConfigGroup grp = config->group("Last Use Settings");
ui->m_tab->setCurrentIndex(grp.readEntry("KForecastView_LastType", 0));
ui->m_forecastButton->setIcon(Icons::get(Icon::ViewForecast));
q->connect(ui->m_tab, &QTabWidget::currentChanged, q, &KForecastView::slotTabChanged);
q->connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KForecastView::refresh);
q->connect(ui->m_forecastButton, &QAbstractButton::clicked, q, &KForecastView::slotManualForecast);
ui->m_forecastList->setUniformRowHeights(true);
ui->m_forecastList->setAllColumnsShowFocus(true);
ui->m_summaryList->setAllColumnsShowFocus(true);
ui->m_budgetList->setAllColumnsShowFocus(true);
ui->m_advancedList->setAlternatingRowColors(true);
q->connect(ui->m_forecastList, &QTreeWidget::itemExpanded, q, &KForecastView::itemExpanded);
q->connect(ui->m_forecastList, &QTreeWidget::itemCollapsed, q, &KForecastView::itemCollapsed);
q->connect(ui->m_summaryList, &QTreeWidget::itemExpanded, q, &KForecastView::itemExpanded);
q->connect(ui->m_summaryList, &QTreeWidget::itemCollapsed, q, &KForecastView::itemCollapsed);
q->connect(ui->m_budgetList, &QTreeWidget::itemExpanded, q, &KForecastView::itemExpanded);
q->connect(ui->m_budgetList, &QTreeWidget::itemCollapsed, q, &KForecastView::itemCollapsed);
m_forecastChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_chartLayout = ui->m_tabChart->layout();
m_chartLayout->setSpacing(6);
m_chartLayout->addWidget(m_forecastChart);
loadForecastSettings();
}
void loadForecast(ForecastViewTab tab)
{
if (m_needReload[tab]) {
switch (tab) {
case ListView:
loadListView();
break;
case SummaryView:
loadSummaryView();
break;
case AdvancedView:
loadAdvancedView();
break;
case BudgetView:
loadBudgetView();
break;
case ChartView:
loadChartView();
break;
default:
break;
}
m_needReload[tab] = false;
}
}
void loadListView()
{
MyMoneyForecast forecast = KMyMoneyGlobalSettings::forecast();
const auto file = MyMoneyFile::instance();
//get the settings from current page
forecast.setForecastDays(ui->m_forecastDays->value());
forecast.setAccountsCycle(ui->m_accountsCycle->value());
forecast.setBeginForecastDay(ui->m_beginDay->value());
forecast.setForecastCycles(ui->m_forecastCycles->value());
forecast.setHistoryMethod(ui->m_historyMethod->checkedId());
forecast.doForecast();
ui->m_forecastList->clear();
ui->m_forecastList->setColumnCount(0);
ui->m_forecastList->setIconSize(QSize(22, 22));
ui->m_forecastList->setSortingEnabled(true);
ui->m_forecastList->sortByColumn(0, Qt::AscendingOrder);
//add columns
QStringList headerLabels;
headerLabels << i18n("Account");
//add cycle interval columns
headerLabels << i18nc("Today's forecast", "Current");
for (int i = 1; i <= forecast.forecastDays(); ++i) {
QDate forecastDate = QDate::currentDate().addDays(i);
headerLabels << QLocale().toString(forecastDate, QLocale::LongFormat);
}
//add variation columns
headerLabels << i18n("Total variation");
//set the columns
ui->m_forecastList->setHeaderLabels(headerLabels);
//add default rows
addTotalRow(ui->m_forecastList, forecast);
addAssetLiabilityRows(forecast);
//load asset and liability forecast accounts
loadAccounts(forecast, file->asset(), m_assetItem, eDetailed);
loadAccounts(forecast, file->liability(), m_liabilityItem, eDetailed);
adjustHeadersAndResizeToContents(ui->m_forecastList);
// add the fixed column only if the horizontal scroll bar is visible
m_fixedColumnView.reset(ui->m_forecastList->horizontalScrollBar()->isVisible() ? new FixedColumnTreeView(ui->m_forecastList) : 0);
}
void loadAccounts(MyMoneyForecast& forecast, const MyMoneyAccount& account, QTreeWidgetItem* parentItem, int forecastType)
{
QMap nameIdx;
const auto file = MyMoneyFile::instance();
QTreeWidgetItem *forecastItem = 0;
//Get all accounts of the right type to calculate forecast
const auto accList = account.accountList();
if (accList.isEmpty())
return;
foreach (const auto sAccount, accList) {
auto subAccount = file->account(sAccount);
//only add the account if it is a forecast account or the parent of a forecast account
if (includeAccount(forecast, subAccount)) {
nameIdx[subAccount.id()] = subAccount.id();
}
}
QMap::ConstIterator it_nc;
for (it_nc = nameIdx.constBegin(); it_nc != nameIdx.constEnd(); ++it_nc) {
const MyMoneyAccount subAccount = file->account(*it_nc);
MyMoneySecurity currency;
if (subAccount.isInvest()) {
MyMoneySecurity underSecurity = file->security(subAccount.currencyId());
currency = file->security(underSecurity.tradingCurrency());
} else {
currency = file->security(subAccount.currencyId());
}
forecastItem = new QTreeWidgetItem(parentItem);
forecastItem->setText(0, subAccount.name());
forecastItem->setIcon(0, subAccount.accountPixmap());
forecastItem->setData(0, ForecastRole, QVariant::fromValue(forecast));
forecastItem->setData(0, AccountRole, QVariant::fromValue(subAccount));
forecastItem->setExpanded(true);
switch (forecastType) {
case eSummary:
updateSummary(forecastItem);
break;
case eDetailed:
updateDetailed(forecastItem);
break;
case EForecastViewType::eBudget:
updateBudget(forecastItem);
break;
default:
break;
}
loadAccounts(forecast, subAccount, forecastItem, forecastType);
}
}
void loadSummaryView()
{
MyMoneyForecast forecast = KMyMoneyGlobalSettings::forecast();
QList accList;
int dropMinimum;
int dropZero;
const auto file = MyMoneyFile::instance();
//get the settings from current page
forecast.setForecastDays(ui->m_forecastDays->value());
forecast.setAccountsCycle(ui->m_accountsCycle->value());
forecast.setBeginForecastDay(ui->m_beginDay->value());
forecast.setForecastCycles(ui->m_forecastCycles->value());
forecast.setHistoryMethod(ui->m_historyMethod->checkedId());
forecast.doForecast();
//add columns
QStringList headerLabels;
headerLabels << i18n("Account");
headerLabels << i18nc("Today's forecast", "Current");
//if beginning of forecast is today, set the begin day to next cycle to avoid repeating the first cycle
int daysToBeginDay;
if (QDate::currentDate() < forecast.beginForecastDate()) {
daysToBeginDay = QDate::currentDate().daysTo(forecast.beginForecastDate());
} else {
daysToBeginDay = forecast.accountsCycle();
}
for (int i = 0; ((i*forecast.accountsCycle()) + daysToBeginDay) <= forecast.forecastDays(); ++i) {
int intervalDays = ((i * forecast.accountsCycle()) + daysToBeginDay);
headerLabels << i18np("1 day", "%1 days", intervalDays);
}
//add variation columns
headerLabels << i18n("Total variation");
ui->m_summaryList->clear();
//set the columns
ui->m_summaryList->setHeaderLabels(headerLabels);
ui->m_summaryList->setIconSize(QSize(22, 22));
ui->m_summaryList->setSortingEnabled(true);
ui->m_summaryList->sortByColumn(0, Qt::AscendingOrder);
//add default rows
addTotalRow(ui->m_summaryList, forecast);
addAssetLiabilityRows(forecast);
loadAccounts(forecast, file->asset(), m_assetItem, eSummary);
loadAccounts(forecast, file->liability(), m_liabilityItem, eSummary);
adjustHeadersAndResizeToContents(ui->m_summaryList);
//Add comments to the advice list
ui->m_adviceText->clear();
//Get all accounts of the right type to calculate forecast
m_nameIdx.clear();
accList = forecast.accountList();
QList::const_iterator accList_t = accList.constBegin();
for (; accList_t != accList.constEnd(); ++accList_t) {
MyMoneyAccount acc = *accList_t;
if (m_nameIdx[acc.id()] != acc.id()) { //Check if the account is there
m_nameIdx[acc.id()] = acc.id();
}
}
QMap::ConstIterator it_nc;
for (it_nc = m_nameIdx.constBegin(); it_nc != m_nameIdx.constEnd(); ++it_nc) {
const MyMoneyAccount& acc = file->account(*it_nc);
MyMoneySecurity currency;
//change currency to deep currency if account is an investment
if (acc.isInvest()) {
MyMoneySecurity underSecurity = file->security(acc.currencyId());
currency = file->security(underSecurity.tradingCurrency());
} else {
currency = file->security(acc.currencyId());
}
//Check if the account is going to be below zero or below the minimal balance in the forecast period
QString minimumBalance = acc.value("minimumBalance");
MyMoneyMoney minBalance = MyMoneyMoney(minimumBalance);
//Check if the account is going to be below minimal balance
dropMinimum = forecast.daysToMinimumBalance(acc);
//Check if the account is going to be below zero in the future
dropZero = forecast.daysToZeroBalance(acc);
// 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 = QString("").arg(KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative).name());
msg += i18n("The balance of %1 is below the minimum balance %2 today.", acc.name(), MyMoneyUtils::formatMoney(minBalance, acc, currency));
msg += QString("");
break;
default:
msg = QString("").arg(KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative).name());
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, acc.name(), MyMoneyUtils::formatMoney(minBalance, acc, currency));
msg += QString("");
}
if (!msg.isEmpty()) {
ui->m_adviceText->append(msg);
}
}
// a drop below zero is always shown
msg.clear();
switch (dropZero) {
case -1:
break;
case 0:
if (acc.accountGroup() == eMyMoney::Account::Type::Asset) {
msg = QString("").arg(KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative).name());
msg += i18n("The balance of %1 is below %2 today.", acc.name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), acc, currency));
msg += QString("");
break;
}
if (acc.accountGroup() == eMyMoney::Account::Type::Liability) {
msg = i18n("The balance of %1 is above %2 today.", acc.name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), acc, currency));
break;
}
break;
default:
if (acc.accountGroup() == eMyMoney::Account::Type::Asset) {
msg = QString("").arg(KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative).name());
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, acc.name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), acc, currency));
msg += QString("");
break;
}
if (acc.accountGroup() == eMyMoney::Account::Type::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, acc.name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), acc, currency));
break;
}
}
if (!msg.isEmpty()) {
ui->m_adviceText->append(msg);
}
//advice about trends
msg.clear();
MyMoneyMoney accCycleVariation = forecast.accountCycleVariation(acc);
if (accCycleVariation < MyMoneyMoney()) {
msg = QString("").arg(KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative).name());
msg += i18n("The account %1 is decreasing %2 per cycle.", acc.name(), MyMoneyUtils::formatMoney(accCycleVariation, acc, currency));
msg += QString("");
}
if (!msg.isEmpty()) {
ui->m_adviceText->append(msg);
}
}
ui->m_adviceText->show();
}
void loadAdvancedView()
{
const auto file = MyMoneyFile::instance();
QList accList;
MyMoneySecurity baseCurrency = file->baseCurrency();
MyMoneyForecast forecast = KMyMoneyGlobalSettings::forecast();
int daysToBeginDay;
//get the settings from current page
forecast.setForecastDays(ui->m_forecastDays->value());
forecast.setAccountsCycle(ui->m_accountsCycle->value());
forecast.setBeginForecastDay(ui->m_beginDay->value());
forecast.setForecastCycles(ui->m_forecastCycles->value());
forecast.setHistoryMethod(ui->m_historyMethod->checkedId());
forecast.doForecast();
//Get all accounts of the right type to calculate forecast
m_nameIdx.clear();
accList = forecast.accountList();
QList::const_iterator accList_t = accList.constBegin();
for (; accList_t != accList.constEnd(); ++accList_t) {
MyMoneyAccount acc = *accList_t;
if (m_nameIdx[acc.id()] != acc.id()) { //Check if the account is there
m_nameIdx[acc.id()] = acc.id();
}
}
//clear the list, including columns
ui->m_advancedList->clear();
ui->m_advancedList->setColumnCount(0);
ui->m_advancedList->setIconSize(QSize(22, 22));
QStringList headerLabels;
//add first column of both lists
headerLabels << i18n("Account");
//if beginning of forecast is today, set the begin day to next cycle to avoid repeating the first cycle
if (QDate::currentDate() < forecast.beginForecastDate()) {
daysToBeginDay = QDate::currentDate().daysTo(forecast.beginForecastDate());
} else {
daysToBeginDay = forecast.accountsCycle();
}
//add columns
for (int i = 1; ((i * forecast.accountsCycle()) + daysToBeginDay) <= forecast.forecastDays(); ++i) {
headerLabels << i18n("Min Bal %1", i);
headerLabels << i18n("Min Date %1", i);
}
for (int i = 1; ((i * forecast.accountsCycle()) + daysToBeginDay) <= forecast.forecastDays(); ++i) {
headerLabels << i18n("Max Bal %1", i);
headerLabels << i18n("Max Date %1", i);
}
headerLabels << i18nc("Average balance", "Average");
ui->m_advancedList->setHeaderLabels(headerLabels);
QTreeWidgetItem *advancedItem = 0;
QMap::ConstIterator it_nc;
for (it_nc = m_nameIdx.constBegin(); it_nc != m_nameIdx.constEnd(); ++it_nc) {
const MyMoneyAccount& acc = file->account(*it_nc);
QString amount;
MyMoneyMoney amountMM;
MyMoneySecurity currency;
//change currency to deep currency if account is an investment
if (acc.isInvest()) {
MyMoneySecurity underSecurity = file->security(acc.currencyId());
currency = file->security(underSecurity.tradingCurrency());
} else {
currency = file->security(acc.currencyId());
}
advancedItem = new QTreeWidgetItem(ui->m_advancedList, advancedItem, false);
advancedItem->setText(0, acc.name());
advancedItem->setIcon(0, acc.accountPixmap());
int it_c = 1; // iterator for the columns of the listview
//get minimum balance list
QList minBalanceList = forecast.accountMinimumBalanceDateList(acc);
QList::Iterator t_min;
for (t_min = minBalanceList.begin(); t_min != minBalanceList.end() ; ++t_min) {
QDate minDate = *t_min;
amountMM = forecast.forecastBalance(acc, minDate);
amount = MyMoneyUtils::formatMoney(amountMM, acc, currency);
advancedItem->setText(it_c, amount);
advancedItem->setTextAlignment(it_c, Qt::AlignRight | Qt::AlignVCenter);
if (amountMM.isNegative()) {
advancedItem->setForeground(it_c, KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative));
}
it_c++;
QString dateString = QLocale().toString(minDate, QLocale::ShortFormat);
advancedItem->setText(it_c, dateString);
advancedItem->setTextAlignment(it_c, Qt::AlignRight | Qt::AlignVCenter);
if (amountMM.isNegative()) {
advancedItem->setForeground(it_c, KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative));
}
it_c++;
}
//get maximum balance list
QList maxBalanceList = forecast.accountMaximumBalanceDateList(acc);
QList::Iterator t_max;
for (t_max = maxBalanceList.begin(); t_max != maxBalanceList.end() ; ++t_max) {
QDate maxDate = *t_max;
amountMM = forecast.forecastBalance(acc, maxDate);
amount = MyMoneyUtils::formatMoney(amountMM, acc, currency);
advancedItem->setText(it_c, amount);
advancedItem->setTextAlignment(it_c, Qt::AlignRight | Qt::AlignVCenter);
if (amountMM.isNegative()) {
advancedItem->setForeground(it_c, KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative));
}
it_c++;
QString dateString = QLocale().toString(maxDate, QLocale::ShortFormat);
advancedItem->setText(it_c, dateString);
advancedItem->setTextAlignment(it_c, Qt::AlignRight | Qt::AlignVCenter);
if (amountMM.isNegative()) {
advancedItem->setForeground(it_c, KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative));
}
it_c++;
}
//get average balance
amountMM = forecast.accountAverageBalance(acc);
amount = MyMoneyUtils::formatMoney(amountMM, acc, currency);
advancedItem->setText(it_c, amount);
advancedItem->setTextAlignment(it_c, Qt::AlignRight | Qt::AlignVCenter);
if (amountMM.isNegative()) {
advancedItem->setForeground(it_c, KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative));
}
it_c++;
}
// make sure all data is shown
adjustHeadersAndResizeToContents(ui->m_advancedList);
ui->m_advancedList->show();
}
void loadBudgetView()
{
const auto file = MyMoneyFile::instance();
MyMoneyForecast forecast = KMyMoneyGlobalSettings::forecast();
//get the settings from current page and calculate this year based on last year
QDate historyEndDate = QDate(QDate::currentDate().year() - 1, 12, 31);
QDate historyStartDate = historyEndDate.addDays(-ui->m_accountsCycle->value() * ui->m_forecastCycles->value());
QDate forecastStartDate = QDate(QDate::currentDate().year(), 1, 1);
QDate forecastEndDate = QDate::currentDate().addDays(ui->m_forecastDays->value());
forecast.setHistoryMethod(ui->m_historyMethod->checkedId());
MyMoneyBudget budget;
forecast.createBudget(budget, historyStartDate, historyEndDate, forecastStartDate, forecastEndDate, false);
ui->m_budgetList->clear();
ui->m_budgetList->setIconSize(QSize(22, 22));
ui->m_budgetList->setSortingEnabled(true);
ui->m_budgetList->sortByColumn(0, Qt::AscendingOrder);
//add columns
QStringList headerLabels;
headerLabels << i18n("Account");
{
QDate forecastStartDate = forecast.forecastStartDate();
QDate forecastEndDate = forecast.forecastEndDate();
//add cycle interval columns
QDate f_date = forecastStartDate;
for (; f_date <= forecastEndDate; f_date = f_date.addMonths(1)) {
headerLabels << QDate::longMonthName(f_date.month());
}
}
//add total column
headerLabels << i18nc("Total balance", "Total");
//set the columns
ui->m_budgetList->setHeaderLabels(headerLabels);
//add default rows
addTotalRow(ui->m_budgetList, forecast);
addIncomeExpenseRows(forecast);
//load income and expense budget accounts
loadAccounts(forecast, file->income(), m_incomeItem, EForecastViewType::eBudget);
loadAccounts(forecast, file->expense(), m_expenseItem, EForecastViewType::eBudget);
adjustHeadersAndResizeToContents(ui->m_budgetList);
}
void loadChartView()
{
MyMoneyReport::EDetailLevel detailLevel[4] = { MyMoneyReport::eDetailAll, MyMoneyReport::eDetailTop, MyMoneyReport::eDetailGroup, MyMoneyReport::eDetailTotal };
MyMoneyReport reportCfg = MyMoneyReport(
MyMoneyReport::eAssetLiability,
MyMoneyReport::eMonths,
eMyMoney::TransactionFilter::Date::UserDefined, // overridden by the setDateFilter() call below
detailLevel[ui->m_comboDetail->currentIndex()],
i18n("Net Worth Forecast"),
i18n("Generated Report"));
reportCfg.setChartByDefault(true);
reportCfg.setChartCHGridLines(false);
reportCfg.setChartSVGridLines(false);
reportCfg.setChartType(MyMoneyReport::eChartLine);
reportCfg.setIncludingSchedules(false);
// FIXME: this causes a crash
//reportCfg.setColumnsAreDays( true );
reportCfg.setChartDataLabels(false);
reportCfg.setConvertCurrency(true);
reportCfg.setIncludingForecast(true);
reportCfg.setDateFilter(QDate::currentDate(), QDate::currentDate().addDays(ui->m_forecastDays->value()));
reports::PivotTable table(reportCfg);
table.drawChart(*m_forecastChart);
// Adjust the size
m_forecastChart->resize(ui->m_tab->width() - 10, ui->m_tab->height());
//m_forecastChart->show();
m_forecastChart->update();
}
void loadForecastSettings()
{
//fill the settings controls
- ui->m_forecastDays->setValue(KMyMoneyGlobalSettings::forecastDays());
- ui->m_accountsCycle->setValue(KMyMoneyGlobalSettings::forecastAccountCycle());
- ui->m_beginDay->setValue(KMyMoneyGlobalSettings::beginForecastDay());
- ui->m_forecastCycles->setValue(KMyMoneyGlobalSettings::forecastCycles());
+ ui->m_forecastDays->setValue(ForecastViewSettings::forecastDays());
+ ui->m_accountsCycle->setValue(ForecastViewSettings::forecastAccountCycle());
+ ui->m_beginDay->setValue(ForecastViewSettings::beginForecastDay());
+ ui->m_forecastCycles->setValue(ForecastViewSettings::forecastCycles());
ui->m_historyMethod->setId(ui->radioButton11, 0); // simple moving avg
ui->m_historyMethod->setId(ui->radioButton12, 1); // weighted moving avg
ui->m_historyMethod->setId(ui->radioButton13, 2); // linear regression
- ui->m_historyMethod->button(KMyMoneyGlobalSettings::historyMethod())->setChecked(true);
- switch (KMyMoneyGlobalSettings::forecastMethod()) {
+ ui->m_historyMethod->button(ForecastViewSettings::historyMethod())->setChecked(true);
+ switch (ForecastViewSettings::forecastMethod()) {
case 0:
ui->m_forecastMethod->setText(i18nc("Scheduled method", "Scheduled"));
ui->m_forecastCycles->setDisabled(true);
ui->m_historyMethodGroupBox->setDisabled(true);
break;
case 1:
ui->m_forecastMethod->setText(i18nc("History-based method", "History"));
ui->m_forecastCycles->setEnabled(true);
ui->m_historyMethodGroupBox->setEnabled(true);
break;
default:
ui->m_forecastMethod->setText(i18nc("Unknown forecast method", "Unknown"));
break;
}
}
void addAssetLiabilityRows(const MyMoneyForecast& forecast)
{
const auto file = MyMoneyFile::instance();
m_assetItem = new QTreeWidgetItem(m_totalItem);
m_assetItem->setText(0, file->asset().name());
m_assetItem->setIcon(0, file->asset().accountPixmap());
m_assetItem->setData(0, ForecastRole, QVariant::fromValue(forecast));
m_assetItem->setData(0, AccountRole, QVariant::fromValue(file->asset()));
m_assetItem->setExpanded(true);
m_liabilityItem = new QTreeWidgetItem(m_totalItem);
m_liabilityItem->setText(0, file->liability().name());
m_liabilityItem->setIcon(0, file->liability().accountPixmap());
m_liabilityItem->setData(0, ForecastRole, QVariant::fromValue(forecast));
m_liabilityItem->setData(0, AccountRole, QVariant::fromValue(file->liability()));
m_liabilityItem->setExpanded(true);
}
void addIncomeExpenseRows(const MyMoneyForecast& forecast)
{
const auto file = MyMoneyFile::instance();
m_incomeItem = new QTreeWidgetItem(m_totalItem);
m_incomeItem->setText(0, file->income().name());
m_incomeItem->setIcon(0, file->income().accountPixmap());
m_incomeItem->setData(0, ForecastRole, QVariant::fromValue(forecast));
m_incomeItem->setData(0, AccountRole, QVariant::fromValue(file->income()));
m_incomeItem->setExpanded(true);
m_expenseItem = new QTreeWidgetItem(m_totalItem);
m_expenseItem->setText(0, file->expense().name());
m_expenseItem->setIcon(0, file->expense().accountPixmap());
m_expenseItem->setData(0, ForecastRole, QVariant::fromValue(forecast));
m_expenseItem->setData(0, AccountRole, QVariant::fromValue(file->expense()));
m_expenseItem->setExpanded(true);
}
void addTotalRow(QTreeWidget* forecastList, const MyMoneyForecast& forecast)
{
const auto file = MyMoneyFile::instance();
m_totalItem = new QTreeWidgetItem(forecastList);
QFont font;
font.setBold(true);
m_totalItem->setFont(0, font);
m_totalItem->setText(0, i18nc("Total balance", "Total"));
m_totalItem->setIcon(0, file->asset().accountPixmap());
m_totalItem->setData(0, ForecastRole, QVariant::fromValue(forecast));
m_totalItem->setData(0, AccountRole, QVariant::fromValue(file->asset()));
m_totalItem->setExpanded(true);
}
bool includeAccount(MyMoneyForecast& forecast, const MyMoneyAccount& acc)
{
const auto file = MyMoneyFile::instance();
if (forecast.isForecastAccount(acc))
return true;
foreach (const auto sAccount, acc.accountList()) {
auto account = file->account(sAccount);
if (includeAccount(forecast, account))
return true;
}
return false;
}
void adjustHeadersAndResizeToContents(QTreeWidget *widget)
{
QSize sizeHint(0, widget->sizeHintForRow(0));
QTreeWidgetItem *header = widget->headerItem();
for (int i = 0; i < header->columnCount(); ++i) {
if (i > 0) {
header->setData(i, Qt::TextAlignmentRole, Qt::AlignRight);
// make sure that the row height stays the same even when the column that has icons is not visible
if (m_totalItem) {
m_totalItem->setSizeHint(i, sizeHint);
}
}
widget->resizeColumnToContents(i);
}
}
void setNegative(QTreeWidgetItem *item, bool isNegative)
{
if (isNegative) {
for (int i = 0; i < item->columnCount(); ++i) {
item->setForeground(i, KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative));
}
}
}
void showAmount(QTreeWidgetItem* item, int column, const MyMoneyMoney& amount, const MyMoneySecurity& security)
{
item->setText(column, MyMoneyUtils::formatMoney(amount, security));
item->setTextAlignment(column, Qt::AlignRight | Qt::AlignVCenter);
item->setFont(column, item->font(0));
if (amount.isNegative()) {
item->setForeground(column, KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative));
}
}
void adjustParentValue(QTreeWidgetItem *item, int column, const MyMoneyMoney& value)
{
if (!item)
return;
item->setData(column, ValueRole, QVariant::fromValue(item->data(column, ValueRole).value() + value));
item->setData(column, ValueRole, QVariant::fromValue(item->data(column, ValueRole).value().convert(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())));
// if the entry has no children,
// or it is the top entry
// or it is currently not open
// we need to display the value of it
if (item->childCount() == 0 || !item->parent() || (!item->isExpanded() && item->childCount() > 0) || (item->parent() && !item->parent()->parent())) {
if (item->childCount() > 0)
item->setText(column, " ");
MyMoneyMoney amount = item->data(column, ValueRole).value();
showAmount(item, column, amount, MyMoneyFile::instance()->baseCurrency());
}
// now make sure, the upstream accounts also get notified about the value change
adjustParentValue(item->parent(), column, value);
}
void setValue(QTreeWidgetItem* item, int column, const MyMoneyMoney& amount, const QDate& forecastDate)
{
MyMoneyAccount account = item->data(0, AccountRole).value();
//calculate the balance in base currency for the total row
if (account.currencyId() != MyMoneyFile::instance()->baseCurrency().id()) {
ReportAccount repAcc = ReportAccount(account.id());
MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(forecastDate);
MyMoneyMoney baseAmountMM = amount * curPrice;
MyMoneyMoney value = baseAmountMM.convert(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction());
item->setData(column, ValueRole, QVariant::fromValue(value));
adjustParentValue(item->parent(), column, value);
} else {
item->setData(column, ValueRole, QVariant::fromValue(item->data(column, ValueRole).value() + amount));
adjustParentValue(item->parent(), column, amount);
}
}
void setAmount(QTreeWidgetItem* item, int column, const MyMoneyMoney& amount)
{
item->setData(column, AmountRole, QVariant::fromValue(amount));
item->setTextAlignment(column, Qt::AlignRight | Qt::AlignVCenter);
}
void updateSummary(QTreeWidgetItem *item)
{
MyMoneyMoney amountMM;
int it_c = 1; // iterator for the columns of the listview
const auto file = MyMoneyFile::instance();
int daysToBeginDay;
MyMoneyForecast forecast = item->data(0, ForecastRole).value();
if (QDate::currentDate() < forecast.beginForecastDate()) {
daysToBeginDay = QDate::currentDate().daysTo(forecast.beginForecastDate());
} else {
daysToBeginDay = forecast.accountsCycle();
}
MyMoneyAccount account = item->data(0, AccountRole).value();
MyMoneySecurity currency;
if (account.isInvest()) {
MyMoneySecurity underSecurity = file->security(account.currencyId());
currency = file->security(underSecurity.tradingCurrency());
} else {
currency = file->security(account.currencyId());
}
//add current balance column
QDate summaryDate = QDate::currentDate();
amountMM = forecast.forecastBalance(account, summaryDate);
//calculate the balance in base currency for the total row
setAmount(item, it_c, amountMM);
setValue(item, it_c, amountMM, summaryDate);
showAmount(item, it_c, amountMM, currency);
it_c++;
//iterate through all other columns
for (QDate summaryDate = QDate::currentDate().addDays(daysToBeginDay); summaryDate <= forecast.forecastEndDate(); summaryDate = summaryDate.addDays(forecast.accountsCycle()), ++it_c) {
amountMM = forecast.forecastBalance(account, summaryDate);
//calculate the balance in base currency for the total row
setAmount(item, it_c, amountMM);
setValue(item, it_c, amountMM, summaryDate);
showAmount(item, it_c, amountMM, currency);
}
//calculate and add variation per cycle
setNegative(item, forecast.accountTotalVariation(account).isNegative());
setAmount(item, it_c, forecast.accountTotalVariation(account));
setValue(item, it_c, forecast.accountTotalVariation(account), forecast.forecastEndDate());
showAmount(item, it_c, forecast.accountTotalVariation(account), currency);
}
void updateDetailed(QTreeWidgetItem *item)
{
QString amount;
QString vAmount;
MyMoneyMoney vAmountMM;
const auto file = MyMoneyFile::instance();
MyMoneyAccount account = item->data(0, AccountRole).value();
MyMoneySecurity currency;
if (account.isInvest()) {
MyMoneySecurity underSecurity = file->security(account.currencyId());
currency = file->security(underSecurity.tradingCurrency());
} else {
currency = file->security(account.currencyId());
}
int it_c = 1; // iterator for the columns of the listview
MyMoneyForecast forecast = item->data(0, ForecastRole).value();
for (QDate forecastDate = QDate::currentDate(); forecastDate <= forecast.forecastEndDate(); ++it_c, forecastDate = forecastDate.addDays(1)) {
MyMoneyMoney amountMM = forecast.forecastBalance(account, forecastDate);
//calculate the balance in base currency for the total row
setAmount(item, it_c, amountMM);
setValue(item, it_c, amountMM, forecastDate);
showAmount(item, it_c, amountMM, currency);
}
//calculate and add variation per cycle
vAmountMM = forecast.accountTotalVariation(account);
setAmount(item, it_c, vAmountMM);
setValue(item, it_c, vAmountMM, forecast.forecastEndDate());
showAmount(item, it_c, vAmountMM, currency);
}
void updateBudget(QTreeWidgetItem *item)
{
MyMoneySecurity currency;
MyMoneyMoney tAmountMM;
MyMoneyForecast forecast = item->data(0, ForecastRole).value();
const auto file = MyMoneyFile::instance();
int it_c = 1; // iterator for the columns of the listview
QDate forecastDate = forecast.forecastStartDate();
MyMoneyAccount account = item->data(0, AccountRole).value();
if (account.isInvest()) {
MyMoneySecurity underSecurity = file->security(account.currencyId());
currency = file->security(underSecurity.tradingCurrency());
} else {
currency = file->security(account.currencyId());
}
//iterate columns
for (; forecastDate <= forecast.forecastEndDate(); forecastDate = forecastDate.addMonths(1), ++it_c) {
MyMoneyMoney amountMM;
amountMM = forecast.forecastBalance(account, forecastDate);
if (account.accountType() == eMyMoney::Account::Type::Expense)
amountMM = -amountMM;
tAmountMM += amountMM;
setAmount(item, it_c, amountMM);
setValue(item, it_c, amountMM, forecastDate);
showAmount(item, it_c, amountMM, currency);
}
//set total column
setAmount(item, it_c, tAmountMM);
setValue(item, it_c, tAmountMM, forecast.forecastEndDate());
showAmount(item, it_c, tAmountMM, currency);
}
/**
* Get the list of prices for an account
* This is used later to create an instance of KMyMoneyAccountTreeForecastItem
*
*/
// QList getAccountPrices(const MyMoneyAccount& acc)
// {
// const auto file = MyMoneyFile::instance();
// QList prices;
// MyMoneySecurity security = file->baseCurrency();
// try {
// if (acc.isInvest()) {
// security = file->security(acc.currencyId());
// if (security.tradingCurrency() != file->baseCurrency().id()) {
// MyMoneySecurity sec = file->security(security.tradingCurrency());
// prices += file->price(sec.id(), file->baseCurrency().id());
// }
// } else if (acc.currencyId() != file->baseCurrency().id()) {
// if (acc.currencyId() != file->baseCurrency().id()) {
// security = file->security(acc.currencyId());
// prices += file->price(acc.currencyId(), file->baseCurrency().id());
// }
// }
// } catch (const MyMoneyException &e) {
// qDebug() << Q_FUNC_INFO << " caught exception while adding " << acc.name() << "[" << acc.id() << "]: " << e.what();
// }
// return prices;
// }
KForecastView *q_ptr;
Ui::KForecastView *ui;
bool m_needReload[MaxViewTabs];
/**
* This member holds the load state of page
*/
bool m_needLoad;
QTreeWidgetItem* m_totalItem;
QTreeWidgetItem* m_assetItem;
QTreeWidgetItem* m_liabilityItem;
QTreeWidgetItem* m_incomeItem;
QTreeWidgetItem* m_expenseItem;
QLayout* m_chartLayout;
reports::KReportChartView* m_forecastChart;
QScopedPointer m_fixedColumnView;
QMap m_nameIdx;
};
#endif
diff --git a/kmymoney/dialogs/settings/ksettingsforecast.cpp b/kmymoney/plugins/forecast/ksettingsforecast.cpp
similarity index 100%
rename from kmymoney/dialogs/settings/ksettingsforecast.cpp
rename to kmymoney/plugins/forecast/ksettingsforecast.cpp
diff --git a/kmymoney/dialogs/settings/ksettingsforecast.h b/kmymoney/plugins/forecast/ksettingsforecast.h
similarity index 100%
rename from kmymoney/dialogs/settings/ksettingsforecast.h
rename to kmymoney/plugins/forecast/ksettingsforecast.h
diff --git a/kmymoney/plugins/interfaces/kmmviewinterface.cpp b/kmymoney/plugins/interfaces/kmmviewinterface.cpp
index 542cf38da..4d113f645 100644
--- a/kmymoney/plugins/interfaces/kmmviewinterface.cpp
+++ b/kmymoney/plugins/interfaces/kmmviewinterface.cpp
@@ -1,80 +1,90 @@
/***************************************************************************
viewinterface.cpp
-------------------
begin : Wed Jan 5 2005
copyright : (C) 2005 Thomas Baumgart
email : ipwizard@users.sourceforge.net
(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 "kmmviewinterface.h"
// ----------------------------------------------------------------------------
// QT Includes
#include
// ----------------------------------------------------------------------------
// KDE Includes
// ----------------------------------------------------------------------------
// Project Includes
#include "kmymoneyview.h"
#include "selectedtransactions.h"
KMyMoneyPlugin::KMMViewInterface::KMMViewInterface(KMyMoneyView* view, QObject* parent, const char* name) :
ViewInterface(parent, name),
m_view(view)
{
connect(m_view, &KMyMoneyView::accountSelected, this, &ViewInterface::accountSelected);
connect(m_view, &KMyMoneyView::transactionsSelected, this, &ViewInterface::transactionsSelected);
connect(m_view, &KMyMoneyView::accountReconciled,
this, &ViewInterface::accountReconciled);
// connect(app, &KMyMoneyApp::institutionSelected, this, &ViewInterface::institutionSelected);
connect(m_view, &KMyMoneyView::viewStateChanged, this, &ViewInterface::viewStateChanged);
connect(m_view, &KMyMoneyView::kmmFilePlugin, this, &ViewInterface::kmmFilePlugin);
}
bool KMyMoneyPlugin::KMMViewInterface::readFile(const QUrl &url, IMyMoneyOperationsFormat *pExtReader)
{
return m_view->readFile(url, pExtReader);
}
void KMyMoneyPlugin::KMMViewInterface::slotRefreshViews()
{
m_view->slotRefreshViews();
}
+void KMyMoneyPlugin::KMMViewInterface::addView(KMyMoneyViewBase* view, const QString& name, View idView)
+{
+ m_view->addView(view, name, idView);
+}
+
+void KMyMoneyPlugin::KMMViewInterface::removeView(View idView)
+{
+ m_view->removeView(idView);
+}
+
bool KMyMoneyPlugin::KMMViewInterface::fileOpen()
{
return m_view->fileOpen();
}
bool KMyMoneyPlugin::KMMViewInterface::isDatabase()
{
return m_view->isDatabase();
}
//KMyMoneyViewBase* KMyMoneyPlugin::KMMViewInterface::addPage(const QString& item, const QString& icon)
//{
// return m_view->addBasePage(item, icon);
//}
//void KMyMoneyPlugin::KMMViewInterface::addWidget(KMyMoneyViewBase* view, QWidget* w)
//{
// if (view && w)
// view->addWidget(w);
//}
diff --git a/kmymoney/plugins/interfaces/kmmviewinterface.h b/kmymoney/plugins/interfaces/kmmviewinterface.h
index 26e52291f..7c44eca9f 100644
--- a/kmymoney/plugins/interfaces/kmmviewinterface.h
+++ b/kmymoney/plugins/interfaces/kmmviewinterface.h
@@ -1,106 +1,108 @@
/***************************************************************************
kmmviewinterface.h
-------------------
begin : Wed Jan 5 2005
copyright : (C) 2005 Thomas Baumgart
email : ipwizard@users.sourceforge.net
(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 KMMVIEWINTERFACE_H
#define KMMVIEWINTERFACE_H
// ----------------------------------------------------------------------------
// QT Includes
// ----------------------------------------------------------------------------
// KDE Includes
class KMyMoneyView;
// ----------------------------------------------------------------------------
// Project Includes
#include "viewinterface.h"
namespace KMyMoneyPlugin
{
/**
* This class represents the implementation of the
* ViewInterface.
*/
class KMMViewInterface : public ViewInterface
{
Q_OBJECT
public:
KMMViewInterface(KMyMoneyView* view, QObject* parent, const char* name = 0);
~KMMViewInterface() {}
/**
* This method returns a pointer to a newly created view
* with title @p item and icon @p pixmap.
*
* @param item Name of view
* @param icon name for the icon to be used for the view
*
* @return pointer to KMyMoneyViewBase object
*/
// KMyMoneyViewBase* addPage(const QString& item, const QString& icon);
/**
* This method allows to add a widget to the view
* created with addPage()
*
* @param view pointer to view object
* @param w pointer to widget
*/
// void addWidget(KMyMoneyViewBase* view, QWidget* w);
/**
* 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, IMyMoneyOperationsFormat *pExtReader = nullptr) override;
/**
* Makes sure that a MyMoneyFile is open and has been created successfully.
*
* @return Whether the file is open and initialised
*/
bool fileOpen() override;
bool isDatabase() override;
/**
* 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() override;
+ void addView(KMyMoneyViewBase* view, const QString& name, View idView) override;
+ void removeView(View idView) override;
private:
KMyMoneyView* m_view;
};
} // namespace
#endif
diff --git a/kmymoney/plugins/viewinterface.h b/kmymoney/plugins/viewinterface.h
index ef2306e5a..dbf7fa2f0 100644
--- a/kmymoney/plugins/viewinterface.h
+++ b/kmymoney/plugins/viewinterface.h
@@ -1,156 +1,162 @@
/***************************************************************************
viewinterface.h
-------------------
begin : Wed Jan 5 2005
copyright : (C) 2005 Thomas Baumgart
email : ipwizard@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
// krazy:excludeall=dpointer
#ifndef VIEWINTERFACE_H
#define VIEWINTERFACE_H
// ----------------------------------------------------------------------------
// QT Includes
#include
// ----------------------------------------------------------------------------
// KDE Includes
// ----------------------------------------------------------------------------
// Project Includes
#include "mymoneymoney.h"
#include "mymoneytransaction.h"
#include "mymoneysplit.h"
#include
namespace KMyMoneyRegister
{
class SelectedTransactions;
}
+enum class View;
+
class MyMoneyInstitution;
class MyMoneyAccount;
class MyMoneySplit;
class MyMoneyTransaction;
class IMyMoneyOperationsFormat;
+class KMyMoneyViewBase;
namespace KMyMoneyPlugin
{
/**
* This abstract class represents the ViewInterface to
* add new view pages to the JanusWidget of KMyMoney. It
* also gives access to the account context menu.
*/
class KMM_PLUGIN_EXPORT ViewInterface : public QObject
{
Q_OBJECT
public:
explicit ViewInterface(QObject* parent, const char* name = 0);
virtual ~ViewInterface();
/**
* 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.
*/
virtual bool readFile(const QUrl &url, IMyMoneyOperationsFormat *pExtReader = nullptr) = 0;
/**
* Makes sure that a MyMoneyFile is open and has been created successfully.
*
* @return Whether the file is open and initialised
*/
virtual bool fileOpen() = 0;
virtual bool isDatabase() = 0;
/**
* 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).
**/
virtual void slotRefreshViews() = 0;
/**
* This method creates a new page in the application.
* See KPageWidget::addPage() for details.
*/
// virtual KMyMoneyViewBase* addPage(const QString& item, const QString& icon) = 0;
/**
* This method adds a widget to the layout of the view
* created with addPage()
*
* @param view pointer to view widget
* @param w widget to be added to @p page
*/
// virtual void addWidget(KMyMoneyViewBase* view, QWidget* w) = 0;
+ virtual void addView(KMyMoneyViewBase* view, const QString& name, View idView) = 0;
+ virtual void removeView(View idView) = 0;
+
Q_SIGNALS:
/**
* This signal is emitted when a new account has been selected by
* the GUI. If no account is selected or the selection is removed,
* @a account is identical to MyMoneyAccount(). This signal is used
* by plugins to get information about changes.
*/
void accountSelected(const MyMoneyAccount& acc);
/**
* This signal is emitted when a transaction/list of transactions has been selected by
* the GUI. If no transaction is selected or the selection is removed,
* @p transactions is identical to an empty QList. This signal is used
* by plugins to get information about changes.
*/
void transactionsSelected(const KMyMoneyRegister::SelectedTransactions& transactions);
/**
* This signal is emitted when a new institution has been selected by
* the GUI. If no institution is selected or the selection is removed,
* @a institution is identical to MyMoneyInstitution(). This signal is used
* by plugins to get information about changes.
*/
// void institutionSelected(const MyMoneyInstitution& institution);
/**
* This signal is emitted when an account has been successfully reconciled
* and all transactions are updated in the engine. It can be used by plugins
* to create reconciliation reports.
*
* @param account the account data
* @param date the reconciliation date as provided through the dialog
* @param startingBalance the starting balance as provided through the dialog
* @param endingBalance the ending balance as provided through the dialog
* @param transactionList reference to QList of QPair containing all
* transaction/split pairs processed by the reconciliation.
*/
void accountReconciled(const MyMoneyAccount& account, const QDate& date, const MyMoneyMoney& startingBalance, const MyMoneyMoney& endingBalance, const QList >& transactionList);
void viewStateChanged(bool);
void kmmFilePlugin(unsigned int);
};
} // namespace
#endif
diff --git a/kmymoney/views/CMakeLists.txt b/kmymoney/views/CMakeLists.txt
index a268511ae..c0e26024b 100644
--- a/kmymoney/views/CMakeLists.txt
+++ b/kmymoney/views/CMakeLists.txt
@@ -1,98 +1,104 @@
+set(viewbase_SOURCES
+ kmymoneyviewbase.cpp
+ )
+
+add_library(viewbase STATIC ${viewbase_SOURCES})
+
+target_link_libraries(viewbase PUBLIC KF5::TextWidgets)
+
############# next target (views) STATIC ###################
set(libviews_a_SOURCES
kaccountsview.cpp
kbudgetview.cpp
kcategoriesview.cpp
- kforecastview.cpp
kgloballedgerview.cpp
kwelcomepage.cpp
khomeview.cpp
kinstitutionsview.cpp
kinvestmentview.cpp
kmymoneyfile.cpp
- kmymoneyview.cpp
- kmymoneyviewbase.cpp
kmymoneyaccountsviewbase.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
kmymoneywebpage.cpp
)
if(ENABLE_UNFINISHEDFEATURES)
list(APPEND libviews_a_SOURCES
simpleledgerview.cpp
ledgerviewpage.cpp
ledgerview.cpp
ledgerdelegate.cpp
newspliteditor.cpp
newtransactioneditor.cpp
newtransactionform.cpp
splitdialog.cpp
splitdelegate.cpp
widgethintframe.cpp
)
endif()
set(libviews_a_UI
kaccountsview.ui
kbudgetview.ui
kcategoriesview.ui
- kforecastview.ui
kinstitutionsview.ui
kinvestmentview.ui
kpayeesview.ui
kscheduledview.ui
ktagsview.ui
konlinejoboutbox.ui
kpayeeidentifierview.ui
)
if(ENABLE_UNFINISHEDFEATURES)
list(APPEND libviews_a_UI
simpleledgerview.ui
ledgerview.ui
ledgerviewpage.ui
splitdialog.ui
newspliteditor.ui
newtransactioneditor.ui
newtransactionform.ui
)
endif()
# 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 newaccountwizard KChart KF5::KIOFileWidgets KF5::Notifications KF5::Archive KF5::TextWidgets Qt5::Sql Qt5::PrintSupport Alkimia::alkimia)
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)
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
+ viewbase
)
# we rely on some of the dialogs to be generated
add_dependencies(views dialogs newinvestmentwizard newaccountwizard newloanwizard endingbalancedlg)
diff --git a/kmymoney/views/kmymoneyview.cpp b/kmymoney/views/kmymoneyview.cpp
index 4cf06e9b8..14ef12ec3 100644
--- a/kmymoney/views/kmymoneyview.cpp
+++ b/kmymoney/views/kmymoneyview.cpp
@@ -1,2231 +1,2239 @@
/***************************************************************************
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
#include "kmymoneyview.h"
// ----------------------------------------------------------------------------
// Std Includes
#include
// ----------------------------------------------------------------------------
// QT Includes
#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
#ifdef ENABLE_UNFINISHEDFEATURES
#include "simpleledgerview.h"
#endif
#include "kmymoneyglobalsettings.h"
#include "kmymoneytitlelabel.h"
#include
#include "kcurrencyeditdlg.h"
#include "mymoneystoragemgr.h"
#include "mymoneystoragebin.h"
#include "mymoneyexception.h"
#include "mymoneystoragexml.h"
#include "mymoneystorageanon.h"
#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 "kinvestmentview.h"
#include "kreportsview.h"
#include "kbudgetview.h"
-#include "kforecastview.h"
#include "konlinejoboutbox.h"
#include "kmymoney.h"
#include "models.h"
#include "accountsmodel.h"
#include "equitiesmodel.h"
#include "securitiesmodel.h"
#include "icons.h"
#include "amountedit.h"
#include "kmymoneyaccounttreeview.h"
#include "accountsviewproxymodel.h"
#include "mymoneyprice.h"
#include "mymoneyschedule.h"
#include "mymoneysplit.h"
#include "mymoneyaccount.h"
#include "mymoneyinstitution.h"
#include "kmymoneyedit.h"
#include "mymoneyfile.h"
#include "mymoneysecurity.h"
#include "mymoneyreport.h"
#include "kmymoneyplugin.h"
#include "mymoneyenums.h"
using namespace Icons;
using namespace eMyMoney;
static constexpr KCompressionDevice::CompressionType const& COMPRESSION_TYPE = KCompressionDevice::GZip;
static constexpr char recoveryKeyId[] = "0xD2B08440";
typedef void(KMyMoneyView::*KMyMoneyViewFunc)();
KMyMoneyView::KMyMoneyView(KMyMoneyApp *kmymoney)
: KPageWidget(nullptr),
m_header(0),
m_inConstructor(true),
m_fileOpen(false),
m_fmode(QFileDevice::ReadUser | QFileDevice::WriteUser),
m_lastViewSelected(0),
m_storagePlugins(nullptr)
#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, &KMyMoneyApp::fileLoaded, this, &KMyMoneyView::slotRefreshViews);
// Page 0
m_homeView = new KHomeView;
viewFrames[View::Home] = m_model->addPage(m_homeView, i18n("Home"));
viewFrames[View::Home]->setIcon(Icons::get(Icon::ViewHome));
connect(m_homeView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_homeView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection);
// Page 1
m_institutionsView = new KInstitutionsView;
viewFrames[View::Institutions] = m_model->addPage(m_institutionsView, i18n("Institutions"));
viewFrames[View::Institutions]->setIcon(Icons::get(Icon::ViewInstitutions));
connect(m_institutionsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_institutionsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection);
// Page 2
m_accountsView = new KAccountsView;
viewFrames[View::Accounts] = m_model->addPage(m_accountsView, i18n("Accounts"));
viewFrames[View::Accounts]->setIcon(Icons::get(Icon::ViewAccounts));
connect(m_accountsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_accountsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection);
// 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(Icons::get(Icon::ViewSchedules));
connect(m_scheduledView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_scheduledView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection);
// Page 4
m_categoriesView = new KCategoriesView;
viewFrames[View::Categories] = m_model->addPage(m_categoriesView, i18n("Categories"));
viewFrames[View::Categories]->setIcon(Icons::get(Icon::ViewCategories));
connect(m_categoriesView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_categoriesView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection);
// Page 5
m_tagsView = new KTagsView;
viewFrames[View::Tags] = m_model->addPage(m_tagsView, i18n("Tags"));
viewFrames[View::Tags]->setIcon(Icons::get(Icon::ViewTags));
connect(m_tagsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_tagsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection);
// Page 6
m_payeesView = new KPayeesView;
viewFrames[View::Payees] = m_model->addPage(m_payeesView, i18n("Payees"));
viewFrames[View::Payees]->setIcon(Icons::get(Icon::ViewPayees));
connect(m_payeesView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_payeesView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection);
// Page 7
m_ledgerView = new KGlobalLedgerView;
viewFrames[View::Ledgers] = m_model->addPage(m_ledgerView, i18n("Ledgers"));
viewFrames[View::Ledgers]->setIcon(Icons::get(Icon::ViewLedgers));
connect(m_ledgerView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_ledgerView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection);
// Page 8
m_investmentView = new KInvestmentView;
viewFrames[View::Investments] = m_model->addPage(m_investmentView, i18n("Investments"));
viewFrames[View::Investments]->setIcon(Icons::get(Icon::ViewInvestment));
connect(m_investmentView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_investmentView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection);
// Page 9
m_reportsView = new KReportsView;
viewFrames[View::Reports] = m_model->addPage(m_reportsView, i18n("Reports"));
viewFrames[View::Reports]->setIcon(Icons::get(Icon::ViewReports));
connect(m_reportsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_reportsView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection);
// Page 10
m_budgetView = new KBudgetView;
viewFrames[View::Budget] = m_model->addPage(m_budgetView, i18n("Budgets"));
viewFrames[View::Budget]->setIcon(Icons::get(Icon::ViewBudgets));
connect(m_budgetView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_budgetView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection);
// Page 11
- m_forecastView = new KForecastView;
- viewFrames[View::Forecast] = m_model->addPage(m_forecastView, i18n("Forecast"));
- viewFrames[View::Forecast]->setIcon(Icons::get(Icon::ViewForecast));
- connect(m_forecastView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView);
- connect(m_forecastView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection);
+ // KForecastView
// Page 12
m_onlineJobOutboxView = new KOnlineJobOutbox;
viewFrames[View::OnlineJobOutbox] = m_model->addPage(m_onlineJobOutboxView, i18n("Outbox"));
viewFrames[View::OnlineJobOutbox]->setIcon(Icons::get(Icon::ViewOutbox));
connect(m_onlineJobOutboxView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_onlineJobOutboxView, &KMyMoneyViewBase::aboutToShow, this, &KMyMoneyView::resetViewSelection);
connect(m_reportsView, &KReportsView::switchViewRequested, this, &KMyMoneyView::slotSwitchView);
connect(m_ledgerView, &KGlobalLedgerView::switchViewRequested, this, &KMyMoneyView::slotSwitchView);
connect(m_homeView, &KHomeView::ledgerSelected, m_ledgerView, &KGlobalLedgerView::slotLedgerSelected);
#ifdef ENABLE_UNFINISHEDFEATURES
SimpleLedgerView* view = new SimpleLedgerView(kmymoney, this);
KPageWidgetItem* frame = m_model->addPage(view, i18n("New ledger"));
frame->setIcon(Icons::get(Icon::DocumentProperties));
#endif
//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;
// add fast switching of main views through Ctrl + NUM_X
struct pageInfo {
View view;
KMyMoneyViewFunc callback;
QString text;
QKeySequence shortcut = QKeySequence();
};
const QVector pageInfos {
{View::Home, &KMyMoneyView::slotShowHomePage, i18n("Show home page"), Qt::CTRL + Qt::Key_1},
{View::Institutions, &KMyMoneyView::slotShowInstitutionsPage, i18n("Show institutions page"), Qt::CTRL + Qt::Key_2},
{View::Accounts, &KMyMoneyView::slotShowAccountsPage, i18n("Show accounts page"), Qt::CTRL + Qt::Key_3},
{View::Schedules, &KMyMoneyView::slotShowSchedulesPage, i18n("Show scheduled transactions page"), Qt::CTRL + Qt::Key_4},
{View::Categories, &KMyMoneyView::slotShowCategoriesPage, i18n("Show categories page"), Qt::CTRL + Qt::Key_5},
{View::Tags, &KMyMoneyView::slotShowTagsPage, i18n("Show tags page"), },
{View::Payees, &KMyMoneyView::slotShowPayeesPage, i18n("Show payees page"), Qt::CTRL + Qt::Key_6},
{View::Ledgers, &KMyMoneyView::slotShowLedgersPage, i18n("Show ledgers page"), Qt::CTRL + Qt::Key_7},
{View::Investments, &KMyMoneyView::slotShowInvestmentsPage, i18n("Show investments page"), Qt::CTRL + Qt::Key_8},
{View::Reports, &KMyMoneyView::slotShowReportsPage, i18n("Show reports page"), Qt::CTRL + Qt::Key_9},
{View::Budget, &KMyMoneyView::slotShowBudgetPage, i18n("Show budget page"), },
{View::Forecast, &KMyMoneyView::slotShowForecastPage, i18n("Show forecast page"), },
{View::OnlineJobOutbox, &KMyMoneyView::slotShowOutboxPage, i18n("Show outbox page") }
};
QHash lutActions;
auto aC = kmymoney->actionCollection();
auto pageCount = 0;
foreach (const pageInfo info, pageInfos) {
auto a = new QAction(this);
// KActionCollection::addAction by name sets object name anyways,
// so, as better alternative, set it here right from the start
a->setObjectName(QLatin1String("ShowPage") + QString::number(pageCount++));
a->setText(info.text);
connect(a, &QAction::triggered, this, info.callback);
lutActions.insert(info.view, a); // store QAction's pointer for later processing
if (!info.shortcut.isEmpty())
aC->setDefaultShortcut(a, info.shortcut);
}
aC->addActions(lutActions.values());
// 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);
#ifdef KF5Activities_FOUND
delete m_activityResourceInstance;
#endif
removeStorage();
}
void KMyMoneyView::slotShowHomePage()
{
showPage(viewFrames[View::Home]);
}
void KMyMoneyView::slotShowInstitutionsPage()
{
showPage(viewFrames[View::Institutions]);
m_institutionsView->setDefaultFocus();
}
void KMyMoneyView::slotShowAccountsPage()
{
showPage(viewFrames[View::Accounts]);
m_accountsView->setDefaultFocus();
}
void KMyMoneyView::slotShowSchedulesPage()
{
showPage(viewFrames[View::Schedules]);
m_scheduledView->setDefaultFocus();
}
void KMyMoneyView::slotShowCategoriesPage()
{
showPage(viewFrames[View::Categories]);
m_categoriesView->setDefaultFocus();
}
void KMyMoneyView::slotShowTagsPage()
{
showPage(viewFrames[View::Tags]);
m_tagsView->setDefaultFocus();
}
void KMyMoneyView::slotShowPayeesPage()
{
showPage(viewFrames[View::Payees]);
m_payeesView->setDefaultFocus();
}
void KMyMoneyView::slotShowLedgersPage()
{
showPage(viewFrames[View::Ledgers]);
m_ledgerView->setDefaultFocus();
}
void KMyMoneyView::slotShowInvestmentsPage()
{
showPage(viewFrames[View::Investments]);
m_investmentView->setDefaultFocus();
}
void KMyMoneyView::slotShowReportsPage()
{
showPage(viewFrames[View::Reports]);
m_reportsView->setDefaultFocus();
}
void KMyMoneyView::slotShowBudgetPage()
{
showPage(viewFrames[View::Budget]);
m_budgetView->setDefaultFocus();
}
void KMyMoneyView::slotShowForecastPage()
{
- showPage(viewFrames[View::Forecast]);
- m_forecastView->setDefaultFocus();
+ if (viewFrames.contains(View::Forecast)) {
+ showPage(viewFrames[View::Forecast]);
+ viewBases[View::Forecast]->setDefaultFocus();
+ }
}
void KMyMoneyView::slotShowOutboxPage()
{
showPage(viewFrames[View::OnlineJobOutbox]);
m_onlineJobOutboxView->setDefaultFocus();
}
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 eAccountsModel::Column column, const bool show)
{
QVector proxyModels {m_institutionsView->getProxyModel(), m_accountsView->getProxyModel(),
m_categoriesView->getProxyModel(), m_budgetView->getProxyModel()};
for (auto i = proxyModels.count() - 1; i >= 0; --i) { // weed out unloaded views
if (!proxyModels.at(i))
proxyModels.removeAt(i);
}
QString question;
if (show)
question = i18n("Do you want to show %1 column on every loaded view?", AccountsModel::getHeaderName(column));
else
question = i18n("Do you want to hide %1 column on every loaded view?", AccountsModel::getHeaderName(column));
if (proxyModels.count() == 1 || // no need to ask what to do with other views because they aren't loaded
KMessageBox::questionYesNo(this,
question,
QString(),
KStandardGuiItem::yes(), KStandardGuiItem::no(),
QStringLiteral("ShowColumnOnEveryView")) == KMessageBox::Yes) {
Models::instance()->accountsModel()->setColumnVisibility(column, show);
Models::instance()->institutionsModel()->setColumnVisibility(column, show);
foreach(AccountsViewProxyModel *proxyModel, proxyModels) {
if (!proxyModel)
continue;
proxyModel->setColumnVisibility(column, show);
proxyModel->invalidate();
}
} else if(show) {
// in case we need to show it, we have to make sure to set the visibility
// in the base model as well. Otherwise, we don't see the column through the proxy model
Models::instance()->accountsModel()->setColumnVisibility(column, show);
Models::instance()->institutionsModel()->setColumnVisibility(column, show);
}
}
void KMyMoneyView::setOnlinePlugins(QMap& plugins)
{
m_accountsView->setOnlinePlugins(plugins);
m_onlineJobOutboxView->setOnlinePlugins(plugins);
}
void KMyMoneyView::setStoragePlugins(QMap& plugins)
{
m_storagePlugins = &plugins;
}
eDialogs::ScheduleResultCode KMyMoneyView::enterSchedule(MyMoneySchedule& schedule, bool autoEnter, bool extendedKeys)
{
return m_scheduledView->enterSchedule(schedule, autoEnter, extendedKeys);
}
+void KMyMoneyView::addView(KMyMoneyViewBase* view, const QString& name, View idView)
+{
+ auto isViewInserted = false;
+ for (auto i = (int)idView; i < (int)View::None; ++i) {
+ if (viewFrames.contains((View)i)) {
+ viewFrames[idView] = m_model->insertPage(viewFrames[(View)i],view, name);
+ viewBases[idView] = view;
+ isViewInserted = true;
+ break;
+ }
+ }
+
+ if (!isViewInserted)
+ viewFrames[idView] = m_model->addPage(view, name);
+
+ auto icon = Icon::ViewForecast;
+ switch (idView) {
+ case View::Forecast:
+ icon = Icon::ViewForecast;
+ break;
+ default:
+ break;
+ }
+ viewFrames[idView]->setIcon(Icons::get(icon));
+}
+
+void KMyMoneyView::removeView(View idView)
+{
+ m_model->removePage(viewFrames[idView]);
+ viewFrames.remove(idView);
+ viewBases.remove(idView);
+}
+
bool KMyMoneyView::showPageHeader() const
{
return false;
}
void KMyMoneyView::slotSwitchView(View view)
{
showPage(viewFrames[view]);
}
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;
}
void KMyMoneyView::newStorage()
{
removeStorage();
auto file = MyMoneyFile::instance();
file->attachStorage(new MyMoneyStorageMgr);
}
void KMyMoneyView::removeStorage()
{
auto file = MyMoneyFile::instance();
auto p = file->storage();
if (p) {
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);
+ for (auto i = (int)View::Home; i < (int)View::None; ++i)
+ if (viewFrames.contains(View(i)))
+ if (viewFrames[View(i)]->isEnabled() != m_fileOpen)
+ viewFrames[View(i)]->setEnabled(m_fileOpen);
emit viewStateChanged(m_fileOpen);
}
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);
}
bool KMyMoneyView::fileOpen()
{
return m_fileOpen;
}
void KMyMoneyView::closeFile()
{
if (m_reportsView)
m_reportsView->slotCloseAll();
// disconnect the signals
disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded,
Models::instance()->accountsModel(), &AccountsModel::slotObjectAdded);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectModified,
Models::instance()->accountsModel(), &AccountsModel::slotObjectModified);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved,
Models::instance()->accountsModel(), &AccountsModel::slotObjectRemoved);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::balanceChanged,
Models::instance()->accountsModel(), &AccountsModel::slotBalanceOrValueChanged);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::valueChanged,
Models::instance()->accountsModel(), &AccountsModel::slotBalanceOrValueChanged);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded,
Models::instance()->institutionsModel(), &InstitutionsModel::slotObjectAdded);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectModified,
Models::instance()->institutionsModel(), &InstitutionsModel::slotObjectModified);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved,
Models::instance()->institutionsModel(), &InstitutionsModel::slotObjectRemoved);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::balanceChanged,
Models::instance()->institutionsModel(), &AccountsModel::slotBalanceOrValueChanged);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::valueChanged,
Models::instance()->institutionsModel(), &AccountsModel::slotBalanceOrValueChanged);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded,
Models::instance()->equitiesModel(), &EquitiesModel::slotObjectAdded);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectModified,
Models::instance()->equitiesModel(), &EquitiesModel::slotObjectModified);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved,
Models::instance()->equitiesModel(), &EquitiesModel::slotObjectRemoved);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::balanceChanged,
Models::instance()->equitiesModel(), &EquitiesModel::slotBalanceOrValueChanged);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::valueChanged,
Models::instance()->equitiesModel(), &EquitiesModel::slotBalanceOrValueChanged);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded,
Models::instance()->securitiesModel(), &SecuritiesModel::slotObjectAdded);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectModified,
Models::instance()->securitiesModel(), &SecuritiesModel::slotObjectModified);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved,
Models::instance()->securitiesModel(), &SecuritiesModel::slotObjectRemoved);
disconnect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, m_homeView, &KHomeView::refresh);
// 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, IMyMoneyOperationsFormat* pExtReader)
{
QString filename;
bool downloadedFile = false;
m_fileOpen = false;
bool isEncrypted = false;
IMyMoneyOperationsFormat* 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() == QLatin1String("sql")) { // handle reading of database
m_fileType = KmmDb;
// get rid of the mode parameter which is now redundant
QUrl newUrl(url);
QUrlQuery query(url);
query.removeQueryItem("mode");
newUrl.setQuery(query);
auto rc = openDatabase(newUrl); // on error, any message will have been displayed
if (!rc)
MyMoneyFile::instance()->attachStorage(new MyMoneyStorageMgr);
return rc;
}
auto storage = new MyMoneyStorageMgr;
if (url.isLocalFile()) {
filename = url.toLocalFile();
} else {
downloadedFile = true;
KIO::StoredTransferJob *transferjob = KIO::storedGet (url);
KJobWidgets::setWindow(transferjob, this);
if (! transferjob->exec()) {
KMessageBox::detailedError(this,
i18n("Error while loading file '%1'.", url.url()),
transferjob->errorString(),
i18n("File access error"));
return false;
}
QTemporaryFile file;
file.setAutoRemove(false);
file.open();
file.write(transferjob->data());
filename = file.fileName();
file.close();
}
// 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 = QFileDevice::ReadUser | QFileDevice::WriteUser;
m_fmode |= info.permissions();
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 = pExtReader;
m_fileType = GncXML;
}
}
}
}
if (pReader) {
pReader->setProgressCallback(&KMyMoneyView::progressCallback);
pReader->readFile(qfile, 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 downloaded, then it will be removed
// with the next call. Otherwise, it stays untouched on the local
// filesystem.
if (downloadedFile) {
QFile::remove(filename);
}
return initializeStorage();
}
void KMyMoneyView::checkAccountName(const MyMoneyAccount& _acc, const QString& name) const
{
auto 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
auto pStorage = MyMoneyFile::instance()->storage();
if (!pStorage)
pStorage = new MyMoneyStorageMgr;
auto rc = false;
auto pluginFound = false;
if (m_storagePlugins) {
for (const auto& plugin : *m_storagePlugins) {
if (plugin->formatName().compare(QLatin1String("SQL")) == 0) {
rc = plugin->open(pStorage, url);
pluginFound = true;
break;
}
}
}
if(!pluginFound)
KMessageBox::error(this, i18n("Couldn't find suitable plugin to read your storage."));
if(!rc) {
removeStorage();
delete pStorage;
return false;
}
if (pStorage) {
removeStorage();
MyMoneyFile::instance()->attachStorage(pStorage);
}
m_fileOpen = true;
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() << 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() << 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();
}
// setup the standard precision
AmountEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction()));
KMyMoneyEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction()));
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
auto 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(), &MyMoneyFile::objectAdded,
Models::instance()->accountsModel(), &AccountsModel::slotObjectAdded);
connect(MyMoneyFile::instance(), &MyMoneyFile::objectModified,
Models::instance()->accountsModel(), &AccountsModel::slotObjectModified);
connect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved,
Models::instance()->accountsModel(), &AccountsModel::slotObjectRemoved);
connect(MyMoneyFile::instance(), &MyMoneyFile::balanceChanged,
Models::instance()->accountsModel(), &AccountsModel::slotBalanceOrValueChanged);
connect(MyMoneyFile::instance(), &MyMoneyFile::valueChanged,
Models::instance()->accountsModel(), &AccountsModel::slotBalanceOrValueChanged);
connect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded,
Models::instance()->institutionsModel(), &InstitutionsModel::slotObjectAdded);
connect(MyMoneyFile::instance(), &MyMoneyFile::objectModified,
Models::instance()->institutionsModel(), &InstitutionsModel::slotObjectModified);
connect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved,
Models::instance()->institutionsModel(), &InstitutionsModel::slotObjectRemoved);
connect(MyMoneyFile::instance(), &MyMoneyFile::balanceChanged,
Models::instance()->institutionsModel(), &AccountsModel::slotBalanceOrValueChanged);
connect(MyMoneyFile::instance(), &MyMoneyFile::valueChanged,
Models::instance()->institutionsModel(), &AccountsModel::slotBalanceOrValueChanged);
connect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded,
Models::instance()->equitiesModel(), &EquitiesModel::slotObjectAdded);
connect(MyMoneyFile::instance(), &MyMoneyFile::objectModified,
Models::instance()->equitiesModel(), &EquitiesModel::slotObjectModified);
connect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved,
Models::instance()->equitiesModel(), &EquitiesModel::slotObjectRemoved);
connect(MyMoneyFile::instance(), &MyMoneyFile::balanceChanged,
Models::instance()->equitiesModel(), &EquitiesModel::slotBalanceOrValueChanged);
connect(MyMoneyFile::instance(), &MyMoneyFile::valueChanged,
Models::instance()->equitiesModel(), &EquitiesModel::slotBalanceOrValueChanged);
connect(MyMoneyFile::instance(), &MyMoneyFile::objectAdded,
Models::instance()->securitiesModel(), &SecuritiesModel::slotObjectAdded);
connect(MyMoneyFile::instance(), &MyMoneyFile::objectModified,
Models::instance()->securitiesModel(), &SecuritiesModel::slotObjectModified);
connect(MyMoneyFile::instance(), &MyMoneyFile::objectRemoved,
Models::instance()->securitiesModel(), &SecuritiesModel::slotObjectRemoved);
// 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(), &MyMoneyFile::dataChanged, m_homeView, &KHomeView::refresh);
// 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, IMyMoneyOperationsFormat* 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.
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.
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()
: m_signalsWereBlocked{MyMoneyFile::instance()->signalsBlocked()}
{
MyMoneyFile::instance()->blockSignals(true);
}
~restorePreviousSettingsHelper()
{
MyMoneyFile::instance()->blockSignals(m_signalsWereBlocked);
}
const bool m_signalsWereBlocked;
} restoreHelper;
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(), 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));
}
QFile::setPermissions(localFile, m_fmode);
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);
Q_CONSTEXPR int permission = -1;
QFile file(tmpfile.fileName());
file.open(QIODevice::ReadOnly);
KIO::StoredTransferJob *putjob = KIO::storedPut(file.readAll(), url, permission, KIO::JobFlag::Overwrite);
if (!putjob->exec()) {
throw MYMONEYEXCEPTION(i18n("Unable to upload to '%1'. %2", url.toDisplayString(), putjob->errorString()));
}
file.close();
}
m_fileType = KmmXML;
} catch (const MyMoneyException &e) {
KMessageBox::error(this, e.what());
MyMoneyFile::instance()->setDirty();
rc = false;
}
emit kmmFilePlugin(postSave);
return rc;
}
bool KMyMoneyView::dirty()
{
if (!fileOpen())
return false;
return MyMoneyFile::instance()->dirty();
}
void KMyMoneyView::finishReconciliation(const MyMoneyAccount& /* account */)
{
Models::instance()->accountsModel()->slotReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney());
m_ledgerView->slotSetReconcileAccount(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"));
}
}
AmountEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction()));
KMyMoneyEdit::setStandardPrecision(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction()));
}
}
void KMyMoneyView::selectBaseCurrency()
{
auto 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()
{
auto 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()
{
auto 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, &KInvestmentView::accountSelected, m_ledgerView, static_cast(&KGlobalLedgerView::slotSelectAccount));
disconnect(m_ledgerView, &KGlobalLedgerView::objectSelected, m_investmentView, static_cast(&KInvestmentView::slotSelectAccount));
// TODO turn sync between ledger and investment view if selected by user
if (KMyMoneyGlobalSettings::syncLedgerInvestment()) {
connect(m_investmentView, &KInvestmentView::accountSelected, m_ledgerView, static_cast(&KGlobalLedgerView::slotSelectAccount));
connect(m_ledgerView, &KGlobalLedgerView::objectSelected, m_investmentView, static_cast(&KInvestmentView::slotSelectAccount));
}
showTitleBar(KMyMoneyGlobalSettings::showTitleBar());
m_accountsView->refresh();
m_institutionsView->refresh();
m_categoriesView->refresh();
m_payeesView->refresh();
m_tagsView->refresh();
m_ledgerView->refresh();
m_budgetView->refresh();
m_homeView->refresh();
m_investmentView->refresh();
m_reportsView->refresh();
- m_forecastView->refresh();
m_scheduledView->refresh();
+ for (auto i = (int)View::Home; i < (int)View::None; ++i)
+ if (viewBases.contains(View(i)))
+ viewBases[View(i)]->refresh();
m_payeesView->slotClosePayeeIdentifierSource();
}
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()
{
auto 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.
auto count = 0;
foreach (const auto transaction, transactionList) {
if (transaction.splitCount() == 2) {
QString accountId;
QString categoryId;
QString accountMemo;
QString categoryMemo;
foreach (const auto split, transaction.splits()) {
auto acc = file->account(split.accountId());
if (acc.isIncomeExpense()) {
categoryId = split.id();
categoryMemo = split.memo();
} else {
accountId = split.id();
accountMemo = split.memo();
}
}
if (!accountId.isEmpty() && !categoryId.isEmpty()
&& accountMemo != categoryMemo) {
MyMoneyTransaction t(transaction);
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) {
auto acc = MyMoneyFile::instance()->account(*it_a);
if (acc.accountType() == Account::Type::Investment) {
foreach (const auto accountID, acc.accountList()) {
if (!list.contains(accountID)) {
missing.append(accountID);
}
}
}
}
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) {
auto acc = MyMoneyFile::instance()->account(*it_a);
if (acc.accountType() == Account::Type::Investment) {
foreach (const auto accountID, acc.accountList()) {
if (!list.contains(accountID)) {
missing.append(accountID);
}
}
}
}
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.
*/
auto 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() == Account::Type::Loan
|| (*it_a).accountType() == Account::Type::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() == Account::Type::Equity) {
if ((*it_a).parentAccountId() == equity.id()) {
auto 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 {
auto 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() != eMyMoney::Split::State::NotReconciled) {
qDebug() << Q_FUNC_INFO << " " << sched.id() << " " << (*it_s).id() << " should be 'not reconciled'";
MyMoneySplit split = *it_s;
split.setReconcileDate(QDate());
split.setReconcileFlag(eMyMoney::Split::State::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 ....
foreach (const auto split, t.splits()) {
if (split.accountId().isEmpty()) {
MyMoneySplit s = split;
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()
{
auto 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;
foreach (const auto split, t.splits()) {
if (accounts.contains(split.accountId())) {
hasDuplicateAccounts = true;
qDebug() << Q_FUNC_INFO << " " << t.id() << " has multiple splits with account " << split.accountId();
} else {
accounts << split.accountId();
}
if (split.action() == MyMoneySplit::actionName(eMyMoney::Split::Action::Interest)) {
if (interestAccounts.contains(split.accountId()) == 0) {
interestAccounts << split.accountId();
}
}
}
if (hasDuplicateAccounts) {
fixDuplicateAccounts_0(t);
}
++cnt;
if (!(cnt % 10))
kmymoney->slotStatusProgressBar(cnt);
}
// scan the transactions and modify loan transactions
for (auto& transaction : transactionList) {
QString defaultAction;
QList splits = transaction.splits();
QStringList accounts;
// check if base commodity is set. if not, set baseCurrency
if (transaction.commodity().isEmpty()) {
qDebug() << Q_FUNC_INFO << " " << transaction.id() << " has no base currency";
transaction.setCommodity(file->baseCurrency().id());
file->modifyTransaction(transaction);
}
bool isLoan = false;
// Determine default action
if (transaction.splitCount() == 2) {
// check for transfer
int accountCount = 0;
MyMoneyMoney val;
foreach (const auto split, splits) {
auto acc = file->account(split.accountId());
if (acc.accountGroup() == Account::Type::Asset
|| acc.accountGroup() == Account::Type::Liability) {
val = split.value();
accountCount++;
if (acc.accountType() == Account::Type::Loan
|| acc.accountType() == Account::Type::AssetLoan)
isLoan = true;
} else
break;
}
if (accountCount == 2) {
if (isLoan)
defaultAction = MyMoneySplit::actionName(eMyMoney::Split::Action::Amortization);
else
defaultAction = MyMoneySplit::actionName(eMyMoney::Split::Action::Transfer);
} else {
if (val.isNegative())
defaultAction = MyMoneySplit::actionName(eMyMoney::Split::Action::Withdrawal);
else
defaultAction = MyMoneySplit::actionName(eMyMoney::Split::Action::Deposit);
}
}
isLoan = false;
foreach (const auto split, splits) {
auto acc = file->account(split.accountId());
MyMoneyMoney val = split.value();
if (acc.accountGroup() == Account::Type::Asset
|| acc.accountGroup() == Account::Type::Liability) {
if (!val.isPositive()) {
defaultAction = MyMoneySplit::actionName(eMyMoney::Split::Action::Withdrawal);
break;
} else {
defaultAction = MyMoneySplit::actionName(eMyMoney::Split::Action::Deposit);
break;
}
}
}
#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) {
auto acc = file->account((*it_s).accountId());
MyMoneyMoney val = (*it_s).value();
if (acc.accountType() == Account::Type::CreditCard) {
if (val < 0 && (*it_s).action() != MyMoneySplit::actionName(eMyMoney::Split::Action::Withdrawal) && (*it_s).action() != MyMoneySplit::actionName(eMyMoney::Split::Action::Transfer))
needModify = true;
if (val >= 0 && (*it_s).action() != MyMoneySplit::actionName(eMyMoney::Split::Action::Deposit) && (*it_s).action() != MyMoneySplit::actionName(eMyMoney::Split::Action::Transfer))
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);
transaction.modifySplit(*it_s);
file->modifyTransaction(transaction);
}
splits = transaction.splits(); // update local copy
qDebug("Fixed credit card assignment in %s", transaction.id().data());
}
#endif
// Check for correct assignment of ActionInterest in all splits
// and check if there are any duplicates in this transactions
for (auto& split : splits) {
MyMoneyAccount splitAccount = file->account(split.accountId());
if (!accounts.contains(split.accountId())) {
accounts << split.accountId();
}
// if this split references an interest account, the action
// must be of type ActionInterest
if (interestAccounts.contains(split.accountId())) {
if (split.action() != MyMoneySplit::actionName(eMyMoney::Split::Action::Interest)) {
qDebug() << Q_FUNC_INFO << " " << transaction.id() << " contains an interest account (" << split.accountId() << ") but does not have ActionInterest";
split.setAction(MyMoneySplit::actionName(eMyMoney::Split::Action::Interest));
transaction.modifySplit(split);
file->modifyTransaction(transaction);
qDebug("Fixed interest action in %s", qPrintable(transaction.id()));
}
// if it does not reference an interest account, it must not be
// of type ActionInterest
} else {
if (split.action() == MyMoneySplit::actionName(eMyMoney::Split::Action::Interest)) {
qDebug() << Q_FUNC_INFO << " " << transaction.id() << " does not contain an interest account so it should not have ActionInterest";
split.setAction(defaultAction);
transaction.modifySplit(split);
file->modifyTransaction(transaction);
qDebug("Fixed interest action in %s", qPrintable(transaction.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 (transaction.commodity() == splitAccount.currencyId()
&& split.value() != split.shares()) {
qDebug() << Q_FUNC_INFO << " " << transaction.id() << " " << split.id() << " uses the transaction currency, but shares != value";
split.setShares(split.value());
transaction.modifySplit(split);
file->modifyTransaction(transaction);
}
// fix the shares and values to have the correct fraction
if (!splitAccount.isInvest()) {
try {
int fract = splitAccount.fraction();
if (split.shares() != split.shares().convert(fract)) {
qDebug("adjusting fraction in %s,%s", qPrintable(transaction.id()), qPrintable(split.id()));
split.setShares(split.shares().convert(fract));
split.setValue(split.value().convert(fract));
transaction.modifySplit(split);
file->modifyTransaction(transaction);
}
} 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::resetViewSelection(const View)
{
emit aboutToChangeView();
}
void KMyMoneyView::connectView(const View view)
{
KMyMoneyAccountTreeView *treeView;
switch (view) {
case View::Home:
disconnect(m_homeView, &KHomeView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_homeView, &KHomeView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(m_homeView, &KHomeView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
break;
case View::Accounts:
disconnect(m_accountsView, &KAccountsView::aboutToShow, this, &KMyMoneyView::connectView);
treeView = m_accountsView->getTreeView();
connect(treeView, &KMyMoneyAccountTreeView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
connect(treeView, &KMyMoneyAccountTreeView::contextMenuRequested, this, &KMyMoneyView::slotContextMenuRequested);
connect(treeView, &KMyMoneyAccountTreeView::columnToggled, this, &KMyMoneyView::slotAccountTreeViewChanged);
connect(m_accountsView, &KAccountsView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(Models::instance()->accountsModel(), &AccountsModel::netWorthChanged, m_accountsView, &KAccountsView::slotNetWorthChanged);
break;
case View::Schedules:
disconnect(m_scheduledView, &KScheduledView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_scheduledView, &KScheduledView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
connect(m_scheduledView, &KScheduledView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(m_scheduledView, &KScheduledView::contextMenuRequested, this, &KMyMoneyView::slotContextMenuRequested);
break;
case View::Institutions:
disconnect(m_institutionsView, &KInstitutionsView::aboutToShow, this, &KMyMoneyView::connectView);
treeView = m_institutionsView->getTreeView();
connect(treeView, &KMyMoneyAccountTreeView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
connect(treeView, &KMyMoneyAccountTreeView::contextMenuRequested, this, &KMyMoneyView::slotContextMenuRequested);
connect(treeView, &KMyMoneyAccountTreeView::columnToggled, this, &KMyMoneyView::slotAccountTreeViewChanged);
connect(m_institutionsView, &KInstitutionsView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(Models::instance()->institutionsModel(), &AccountsModel::netWorthChanged, m_institutionsView, &KInstitutionsView::slotNetWorthChanged);
break;
case View::Categories:
disconnect(m_categoriesView, &KCategoriesView::aboutToShow, this, &KMyMoneyView::connectView);
treeView = m_categoriesView->getTreeView();
connect(treeView, &KMyMoneyAccountTreeView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
connect(treeView, &KMyMoneyAccountTreeView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(treeView, &KMyMoneyAccountTreeView::contextMenuRequested, this, &KMyMoneyView::slotContextMenuRequested);
connect(treeView, &KMyMoneyAccountTreeView::columnToggled, this, &KMyMoneyView::slotAccountTreeViewChanged);
connect(m_categoriesView, &KCategoriesView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(Models::instance()->institutionsModel(), &AccountsModel::profitChanged, m_categoriesView, &KCategoriesView::slotProfitChanged);
break;
case View::Tags:
disconnect(m_tagsView, &KTagsView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_tagsView, &KTagsView::transactionSelected, m_ledgerView, &KGlobalLedgerView::slotLedgerSelected);
break;
case View::Payees:
disconnect(m_payeesView, &KTagsView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_payeesView, &KPayeesView::transactionSelected, m_ledgerView, &KGlobalLedgerView::slotLedgerSelected);
break;
case View::Ledgers:
disconnect(m_ledgerView, &KGlobalLedgerView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_ledgerView, &KGlobalLedgerView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
connect(m_ledgerView, &KGlobalLedgerView::openPayeeRequested, this, &KMyMoneyView::slotPayeeSelected);
connect(m_ledgerView, &KGlobalLedgerView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(m_ledgerView, &KGlobalLedgerView::transactionsSelected, this, &KMyMoneyView::slotTransactionsSelected);
connect(m_ledgerView, &KGlobalLedgerView::transactionsContextMenuRequested, this, &KMyMoneyView::slotTransactionsMenuRequested);
connect(m_ledgerView, &KGlobalLedgerView::statusProgress, this, &KMyMoneyView::statusProgress);
connect(m_ledgerView, &KGlobalLedgerView::statusMsg, this, &KMyMoneyView::statusMsg);
connect(m_ledgerView, &KGlobalLedgerView::accountReconciled, this, &KMyMoneyView::accountReconciled);
connect(m_ledgerView, &KGlobalLedgerView::enterOverdueSchedulesRequested, m_scheduledView, &KScheduledView::slotEnterOverdueSchedules);
connect(m_scheduledView, &KScheduledView::enterOverdueSchedulesFinished, m_ledgerView, &KGlobalLedgerView::slotContinueReconciliation);
break;
case View::Budget:
disconnect(m_budgetView, &KBudgetView::aboutToShow, this, &KMyMoneyView::connectView);
treeView = m_budgetView->getTreeView();
connect(treeView, &KMyMoneyAccountTreeView::openObjectRequested, this, &KMyMoneyView::slotOpenObjectRequested);
connect(treeView, &KMyMoneyAccountTreeView::contextMenuRequested, this, &KMyMoneyView::slotContextMenuRequested);
connect(treeView, &KMyMoneyAccountTreeView::columnToggled, this, &KMyMoneyView::slotAccountTreeViewChanged);
connect(m_budgetView, &KBudgetView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
break;
case View::Investments:
disconnect(m_investmentView, &KInvestmentView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_investmentView, &KInvestmentView::accountSelected, kmymoney, &KMyMoneyApp::slotSelectAccount);
connect(m_investmentView, &KInvestmentView::objectSelected, this, &KMyMoneyView::slotObjectSelected);
connect(m_investmentView, &KInvestmentView::contextMenuRequested, this, &KMyMoneyView::slotContextMenuRequested);
break;
case View::Reports:
disconnect(m_reportsView, &KReportsView::aboutToShow, this, &KMyMoneyView::connectView);
connect(m_reportsView, &KReportsView::transactionSelected, m_ledgerView, &KGlobalLedgerView::slotLedgerSelected);
break;
- case View::Forecast:
- disconnect(m_forecastView, &KForecastView::aboutToShow, this, &KMyMoneyView::connectView);
- break;
-
case View::OnlineJobOutbox:
disconnect(m_onlineJobOutboxView, &KOnlineJobOutbox::aboutToShow, this, &KMyMoneyView::connectView);
break;
default:
break;
}
}
void KMyMoneyView::slotOpenObjectRequested(const MyMoneyObject& obj)
{
if (typeid(obj) == typeid(MyMoneyAccount)) {
const auto& acc = static_cast(obj);
// check if we can open this account
// currently it make's sense for asset and liability accounts
if (!MyMoneyFile::instance()->isStandardAccount(acc.id()))
m_ledgerView->slotLedgerSelected(acc.id(), QString());
} else if (typeid(obj) == typeid(MyMoneyInstitution)) {
// const auto& inst = static_cast(obj);
m_institutionsView->slotEditInstitution();
} else if (typeid(obj) == typeid(MyMoneySchedule)) {
m_scheduledView->slotEditSchedule();
} else if (typeid(obj) == typeid(MyMoneyReport)) {
const auto& rep = static_cast(obj);
m_reportsView->slotOpenReport(rep);
}
}
void KMyMoneyView::slotObjectSelected(const MyMoneyObject& obj)
{
// carrying some slots over to views isn't easy for all slots...
// ...so calls to kmymoney still must be here
if (typeid(obj) == typeid(MyMoneyAccount)) {
kmymoney->slotSelectAccount(obj);
m_investmentView->updateActions(obj);
m_categoriesView->updateActions(obj);
m_accountsView->updateActions(obj);
m_ledgerView->updateActions(obj);
m_reportsView->updateActions(obj);
m_onlineJobOutboxView->updateActions(obj);
// for plugin only
const auto& acc = static_cast(obj);
if (!acc.isIncomeExpense() &&
!MyMoneyFile::instance()->isStandardAccount(acc.id()))
emit accountSelected(acc);
} else if (typeid(obj) == typeid(MyMoneyInstitution)) {
m_institutionsView->updateActions(obj);
} else if (typeid(obj) == typeid(MyMoneySchedule)) {
kmymoney->slotUpdateActions();
m_scheduledView->updateActions(obj);
}
}
void KMyMoneyView::slotContextMenuRequested(const MyMoneyObject& obj)
{
if (typeid(obj) == typeid(MyMoneyAccount)) {
const auto& acc = static_cast(obj);
if (acc.isInvest()) {
m_investmentView->slotShowInvestmentMenu(acc);
return;
} else if (acc.isIncomeExpense()) {
m_categoriesView->slotShowCategoriesMenu(acc);
} else {
m_accountsView->slotShowAccountMenu(acc);
}
} else if (typeid(obj) == typeid(MyMoneyInstitution)) {
const auto& inst = static_cast(obj);
m_institutionsView->slotShowInstitutionsMenu(inst);
} else if (typeid(obj) == typeid(MyMoneySchedule)) {
const auto& sch = static_cast(obj);
m_scheduledView->slotShowScheduleMenu(sch);
}
}
void KMyMoneyView::slotTransactionsMenuRequested(const KMyMoneyRegister::SelectedTransactions& list)
{
Q_UNUSED(list)
m_ledgerView->slotShowTransactionMenu(MyMoneySplit());
}
void KMyMoneyView::slotTransactionsSelected(const KMyMoneyRegister::SelectedTransactions& list)
{
m_ledgerView->updateLedgerActions(list);
emit transactionsSelected(list); // for plugins
}
diff --git a/kmymoney/views/kmymoneyview.h b/kmymoney/views/kmymoneyview.h
index c27f0ffc3..0ef62427c 100644
--- a/kmymoney/views/kmymoneyview.h
+++ b/kmymoney/views/kmymoneyview.h
@@ -1,556 +1,560 @@
/***************************************************************************
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
// ----------------------------------------------------------------------------
// QT Includes
#include
// ----------------------------------------------------------------------------
// KDE Includes
#include
// ----------------------------------------------------------------------------
// Project Includes
#include "selectedtransactions.h"
#ifdef KF5Activities_FOUND
namespace KActivities
{
class ResourceInstance;
}
#endif
namespace eAccountsModel { enum class Column; }
namespace eMenu { enum class Action; }
namespace KMyMoneyPlugin { class OnlinePlugin; }
namespace KMyMoneyPlugin { class StoragePlugin; }
namespace eDialogs { enum class ScheduleResultCode; }
+namespace Icons { enum class Icon; }
class KMyMoneyApp;
class KHomeView;
class KAccountsView;
class KCategoriesView;
class KInstitutionsView;
class KPayeesView;
class KTagsView;
class KBudgetView;
class KScheduledView;
class KGlobalLedgerView;
class IMyMoneyOperationsFormat;
class MyMoneyTransaction;
class KInvestmentView;
class KReportsView;
class MyMoneySchedule;
class MyMoneySecurity;
class MyMoneyReport;
class TransactionEditor;
-class KForecastView;
class KOnlineJobOutbox;
class KMyMoneyTitleLabel;
class MyMoneyAccount;
class MyMoneyMoney;
class MyMoneyObject;
class QLabel;
+class KMyMoneyViewBase;
/**
* 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.
*/
enum class View;
class KMyMoneyView : public KPageWidget
{
Q_OBJECT
public:
// file actions for plugin
enum fileActions {
preOpen, postOpen, preSave, postSave, preClose, postClose
};
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;
+ QHash viewFrames;
+ QHash viewBases;
KMyMoneyTitleLabel* m_header;
bool m_inConstructor;
bool m_fileOpen;
QFileDevice::Permissions m_fmode;
int m_lastViewSelected;
QMap* m_storagePlugins;
// 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();
/**
* 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, IMyMoneyOperationsFormat *pExtReader = nullptr);
/**
* 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());
/**
* 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();
void addWidget(QWidget* w);
void showPage(KPageWidgetItem* pageItem);
/**
* check if the current view allows to print something
*
* @retval true Yes, view allows to print
* @retval false No, view cannot print
*/
bool canPrint();
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 eAccountsModel::Column column, const bool show);
void setOnlinePlugins(QMap& plugins);
void setStoragePlugins(QMap& plugins);
// TODO: remove that function
/**
* ugly proxy function
*/
eDialogs::ScheduleResultCode enterSchedule(MyMoneySchedule& schedule, bool autoEnter, bool extendedKeys);
+ void addView(KMyMoneyViewBase* view, const QString& name, View idView);
+ void removeView(View idView);
+
protected:
/**
* Overwritten because KMyMoney has it's custom header.
*/
virtual bool showPageHeader() const;
public Q_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 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);
/**
* This slot prints the current view.
*/
void slotPrintView();
/**
* 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);
/**
* Informs respective views about selected object, so they can
* update action states and current object.
* @param obj Account, Category, Investment, Stock, Institution
*/
void slotObjectSelected(const MyMoneyObject& obj);
void slotTransactionsSelected(const KMyMoneyRegister::SelectedTransactions& list);
private Q_SLOTS:
/**
* This slots switches the view to the specific page
*/
void slotShowHomePage();
void slotShowInstitutionsPage();
void slotShowAccountsPage();
void slotShowSchedulesPage();
void slotShowCategoriesPage();
void slotShowTagsPage();
void slotShowPayeesPage();
void slotShowLedgersPage();
void slotShowInvestmentsPage();
void slotShowReportsPage();
void slotShowBudgetPage();
void slotShowForecastPage();
void slotShowOutboxPage();
/**
* Opens object in ledgers or edits in case of institution
* @param obj Account, Category, Investment, Stock, Institution
*/
void slotOpenObjectRequested(const MyMoneyObject& obj);
/**
* Opens context menu based on objects's type
* @param obj Account, Category, Investment, Stock, Institution
*/
void slotContextMenuRequested(const MyMoneyObject& obj);
void slotTransactionsMenuRequested(const KMyMoneyRegister::SelectedTransactions& list);
void slotSwitchView(View view);
protected Q_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, IMyMoneyOperationsFormat* writer, bool plaintext = false, const QString& keyList = QString());
/**
* Internal method used by slotAccountNew() and slotAccountCategory().
*/
void accountNew(const bool createCategory);
void resetViewSelection(const View);
void connectView(const View);
Q_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);
/**
* 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();
/**
* @brief proxy signal
*/
void statusMsg(const QString& txt);
/**
* @brief proxy signal
*/
void statusProgress(int cnt, int base);
void accountReconciled(const MyMoneyAccount& account, const QDate& date, const MyMoneyMoney& startingBalance, const MyMoneyMoney& endingBalance, const QList >& transactionList);
/**
* This signal is emitted when a transaction/list of transactions has been selected by
* the GUI. If no transaction is selected or the selection is removed,
* @p transactions is identical to an empty QList. This signal is used
* by plugins to get information about changes.
*/
void transactionsSelected(const KMyMoneyRegister::SelectedTransactions& transactions);
/**
* This signal is emitted when a new account has been selected by
* the GUI. If no account is selected or the selection is removed,
* @a account is identical to MyMoneyAccount(). This signal is used
* by plugins to get information about changes.
*/
void accountSelected(const MyMoneyAccount& account);
};
#endif
diff --git a/kmymoney/widgets/CMakeLists.txt b/kmymoney/widgets/CMakeLists.txt
index 0ab41f8b0..3afb98465 100644
--- a/kmymoney/widgets/CMakeLists.txt
+++ b/kmymoney/widgets/CMakeLists.txt
@@ -1,230 +1,229 @@
########### create links ###############
set(kmymoney_STAT_HEADERS
kaccounttemplateselector.h kbudgetvalues.h kguiutils.h
kmymoneyaccountcombo.h kmymoneyaccountcompletion.h
kmymoneyaccountselector.h
kmymoneycategory.h
kmymoneycombo.h kmymoneymvccombo.h kmymoneycompletion.h
kmymoneycurrencyselector.h kmymoneydateinput.h kmymoneyedit.h
kmymoneylineedit.h kmymoneyselector.h
kmymoneytitlelabel.h register.h registeritem.h groupmarker.h fancydategroupmarker.h
scheduledtransaction.h selectedtransaction.h selectedtransactions.h stdtransactiondownloaded.h
stdtransactionmatched.h transactioneditorcontainer.h
transactionform.h transaction.h investtransaction.h stdtransaction.h
transactionsortoption.h reporttabimpl.h reportcontrolimpl.h
kmymoneyvalidationfeedback.h
onlinejobmessagesview.h
kmymoneydateedit.h
amountedit.h amountvalidator.h creditdebithelper.h
)
########### Shared widget library ###########
set(kmm_widgets_sources
widgets_config.cpp
kmymoneydateinput.cpp
kmymoneyvalidationfeedback.cpp
styleditemdelegateforwarder.cpp
kmymoneyedit.cpp
kmymoneylineedit.cpp
kmymoneytextedit.cpp
kmymoneytextedithighlighter.cpp
kmymoneymvccombo.cpp
kmymoneygeneralcombo.cpp
kmymoneyactivitycombo.cpp
kmymoneycashflowcombo.cpp
kmymoneyfrequencycombo.cpp
kmymoneyoccurrencecombo.cpp
kmymoneyoccurrenceperiodcombo.cpp
kmymoneypayeecombo.cpp
kmymoneyperiodcombo.cpp
kmymoneyreconcilecombo.cpp
kmymoneytagcombo.cpp
ktagcontainer.cpp
ktaglabel.cpp
kmymoneyselector.cpp
kmymoneycalculator.cpp
ktreewidgetfilterlinewidget.cpp
kguiutils.cpp
onlinejobmessagesview.cpp
kmymoneydateedit.cpp
kmymoneymoneyvalidator.cpp
amountedit.cpp
amountvalidator.cpp
creditdebithelper.cpp
)
ki18n_wrap_ui(kmm_widgets_sources
kmymoneyvalidationfeedback.ui
onlinejobmessagesview.ui
)
add_library(kmm_widgets SHARED ${kmm_widgets_sources})
target_link_libraries(kmm_widgets PUBLIC
KF5::TextWidgets
KF5::KIOWidgets
KF5::Completion
KF5::Notifications
KF5::ItemViews
KF5::I18n
Qt5::Gui
Qt5::Sql
Qt5::Core
Alkimia::alkimia
kmm_config
kmm_mymoney
)
set_target_properties(kmm_widgets PROPERTIES
VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}
COMPILE_FLAGS "-DKMM_BUILD_WIDGETS_LIB"
)
generate_export_header(kmm_widgets)
install(TARGETS kmm_widgets ${INSTALL_TARGETS_DEFAULT_ARGS} )
########### Basic Widget Library (kmymoney_base) STATIC #################
# Common sources for libkmymoney.so and libwidgets.a that do not
# contain the KMM_DESIGNER flag
set(_uncritial_common_sources
- fixedcolumntreeview.cpp
kbudgetvalues.cpp
kmymoneyaccountcombo.cpp
kmymoneyaccountselector.cpp
kmymoneyaccounttreeview.cpp
accountsviewproxymodel.cpp
budgetviewproxymodel.cpp
kmymoneycombo.cpp
kmymoneycompletion.cpp
kmymoneytitlelabel.cpp
kmymoneydateedit.cpp
kpricetreeitem.cpp
registeritem.cpp
registerfilter.cpp
scheduledtransaction.cpp
selectedtransaction.cpp
selectedtransactions.cpp
stdtransactiondownloaded.cpp
stdtransactionmatched.cpp
transactionform.cpp
tabbar.cpp
transactionformitemdelegate.cpp
transactionsortoption.cpp
)
# sources that contain the KMM_DESIGNER flag
set (_critial_common_sources
kaccounttemplateselector.cpp
kmymoneycurrencyselector.cpp
kmymoneyaccountcompletion.cpp
kmymoneycategory.cpp
groupmarker.cpp
groupmarkers.cpp
fancydategroupmarker.cpp
fancydategroupmarkers.cpp
register.cpp
itemptrvector.cpp
qwidgetcontainer.cpp
registeritemdelegate.cpp
transaction.cpp
stdtransaction.cpp
investtransaction.cpp
transactioneditorcontainer.cpp
)
set (kmymoney_base_UI
kbudgetvalues.ui transactionsortoption.ui kaccounttemplateselector.ui
)
ki18n_wrap_ui(kmymoney_base_ui_srcs ${kmymoney_base_UI})
set(_uncritial_common_sources ${_uncritial_common_sources}
${kmymoney_base_ui_srcs})
# in order to use add_dependencies, we need to add this custom target
# for all generated header files.
# (see http://www.vtk.org/Wiki/CMake_FAQ#How_can_I_add_a_dependency_to_a_source_file_which_is_generated_in_a_subdirectory.3F )
add_custom_target(generate_base_ui_srcs DEPENDS
${kmymoney_base_ui_srcs})
# We can compile the uncritical sources without KMM_DESIGNER flags
add_library(kmymoney_base STATIC ${_uncritial_common_sources})
# TODO: fix dependencies
target_link_libraries(kmymoney_base KF5::XmlGui KF5::TextWidgets KF5::IconThemes KF5::I18n KF5::ConfigWidgets KF5::ConfigCore KF5::Completion KF5::Service Qt5::Gui Qt5::Widgets Qt5::Sql Qt5::Xml Alkimia::alkimia)
add_dependencies(kmymoney_base kmm_config)
########### QtDesigner Widget Library (kmymoneywidgets) #################
# we never link against this library,
# but it is needed for uic and QtDesigner
if( USE_QT_DESIGNER )
set(kmymoneywidgets_PART_SRCS
${CMAKE_CURRENT_BINARY_DIR}/kmymoneywidgets.cpp)
kde4_add_widget_files(kmymoneywidgets_PART_SRCS kmymoney.widgets)
set(kmymoneywidgets_PART_SRCS
${_critial_common_sources}
${kmymoneywidgets_PART_SRCS})
add_library(kmymoneywidgets MODULE ${kmymoneywidgets_PART_SRCS})
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} )
# The option -DKMM_DESIGNER will leave away any code that breaks uic.
set_target_properties(kmymoneywidgets PROPERTIES
COMPILE_FLAGS "-DKMM_DESIGNER")
# The qt-designer widget library shouldn't need to link against the
# dialogs and converter libraries. If a widget references something
# from one of these libraries it is most likely due to code that needs
# to be excluded with a KMM_DESIGNER ifndef.
target_link_libraries(kmymoneywidgets kmymoney_base kmm_mymoney kmymoney_common kmm_config models)
install(TARGETS kmymoneywidgets DESTINATION ${QT_PLUGINS_DIR}/designer )
endif( USE_QT_DESIGNER )
########### Widget Library (widgets) STATIC #################
set(libwidgets_a_SOURCES
${_critial_common_sources}
kmymoneybriefschedule.cpp
registersearchline.cpp
transactioneditorcontainer.cpp
reporttabimpl.cpp
reportcontrolimpl.cpp
daterangedlg.cpp
)
set(libwidgets_a_UI
kmymoneybriefschedule.ui reportcontrol.ui
reporttabgeneral.ui
reporttabrowcolquery.ui reporttabrowcolpivot.ui
reporttabrange.ui reporttabchart.ui
reporttabcapitalgain.ui reporttabperformance.ui
daterangedlg.ui
)
# using uic on the above UI files DEPENDS on libkmymoney.so. If uic
# does not find libkmymoney.so, gcc will fail compiling
# kmymoneyreportconfigtab2decl.cpp and throw errors like "invalid use
# of undefined type `struct KMyMoneyGeneralCombo'"
ki18n_wrap_ui(widgets_ui_srcs ${libwidgets_a_UI})
add_custom_target(generate_widgets_ui_srcs DEPENDS ${widgets_ui_srcs})
add_library(widgets STATIC
${libwidgets_a_SOURCES}
${widgets_ui_srcs}
)
target_link_libraries(widgets KF5::XmlGui kmymoney_base)
add_dependencies(widgets kmm_config)
########### install files ###############
install(FILES ${kmymoney_STAT_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kmm_widgets_export.h
DESTINATION ${INCLUDE_INSTALL_DIR}/kmymoney COMPONENT Devel)